nss_wrapper: remove getgrouplist from nwrap_ops table.
[metze/samba/wip.git] / lib / nss_wrapper / nss_wrapper.c
1 /*
2  * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org>
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the author nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifdef _SAMBA_BUILD_
35
36 #define NSS_WRAPPER_NOT_REPLACE
37 #include "../replace/replace.h"
38 #include "system/passwd.h"
39 #include "system/filesys.h"
40
41 #else /* _SAMBA_BUILD_ */
42
43 #error nss_wrapper_only_supported_in_samba_yet
44
45 #endif
46
47 #ifndef _PUBLIC_
48 #define _PUBLIC_
49 #endif
50
51 /* not all systems have _r functions... */
52 #ifndef HAVE_GETPWNAM_R
53 #define getpwnam_r(name, pwdst, buf, buflen, pwdstp)    ENOSYS
54 #endif
55 #ifndef HAVE_GETPWUID_R
56 #define getpwuid_r(uid, pwdst, buf, buflen, pwdstp)     ENOSYS
57 #endif
58 #ifndef HAVE_GETPWENT_R
59 #define getpwent_r(pwdst, buf, buflen, pwdstp)          ENOSYS
60 #endif
61 #ifndef HAVE_GETGRNAM_R
62 #define getgrnam_r(name, grdst, buf, buflen, grdstp)    ENOSYS
63 #endif
64 #ifndef HAVE_GETGRGID_R
65 #define getgrgid_r(gid, grdst, buf, buflen, grdstp)     ENOSYS
66 #endif
67 #ifndef HAVE_GETGRENT_R
68 #define getgrent_r(grdst, buf, buflen, grdstp)          ENOSYS
69 #endif
70
71 /* not all systems have getgrouplist */
72 #ifndef HAVE_GETGROUPLIST
73 #define getgrouplist(user, group, groups, ngroups)      0
74 #endif
75
76 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
77  * for now */
78 #define REWRITE_CALLS
79
80 #ifdef REWRITE_CALLS
81
82 #define real_getpwnam           getpwnam
83 #define real_getpwnam_r         getpwnam_r
84 #define real_getpwuid           getpwuid
85 #define real_getpwuid_r         getpwuid_r
86
87 #define real_setpwent           setpwent
88 #define real_getpwent           getpwent
89 #define real_getpwent_r         getpwent_r
90 #define real_endpwent           endpwent
91
92 /*
93 #define real_getgrlst           getgrlst
94 #define real_getgrlst_r         getgrlst_r
95 #define real_initgroups_dyn     initgroups_dyn
96 */
97 #define real_initgroups         initgroups
98 #define real_getgrouplist       getgrouplist
99
100 #define real_getgrnam           getgrnam
101 #define real_getgrnam_r         getgrnam_r
102 #define real_getgrgid           getgrgid
103 #define real_getgrgid_r         getgrgid_r
104
105 #define real_setgrent           setgrent
106 #define real_getgrent           getgrent
107 #define real_getgrent_r         getgrent_r
108 #define real_endgrent           endgrent
109
110 #endif
111
112 #if 0
113 # ifdef DEBUG
114 # define NWRAP_ERROR(args)      DEBUG(0, args)
115 # else
116 # define NWRAP_ERROR(args)      printf args
117 # endif
118 #else
119 #define NWRAP_ERROR(args)
120 #endif
121
122 #if 0
123 # ifdef DEBUG
124 # define NWRAP_DEBUG(args)      DEBUG(0, args)
125 # else
126 # define NWRAP_DEBUG(args)      printf args
127 # endif
128 #else
129 #define NWRAP_DEBUG(args)
130 #endif
131
132 #if 0
133 # ifdef DEBUG
134 # define NWRAP_VERBOSE(args)    DEBUG(0, args)
135 # else
136 # define NWRAP_VERBOSE(args)    printf args
137 # endif
138 #else
139 #define NWRAP_VERBOSE(args)
140 #endif
141
142 struct nwrap_ops {
143         const char *name;
144         struct passwd * (*nw_getpwnam)(const char *name);
145         int             (*nw_getpwnam_r)(const char *name, struct passwd *pwdst,
146                                       char *buf, size_t buflen, struct passwd **pwdstp);
147         struct passwd * (*nw_getpwuid)(uid_t uid);
148         int             (*nw_getpwuid_r)(uid_t uid, struct passwd *pwdst,
149                                       char *buf, size_t buflen, struct passwd **pwdstp);
150         void            (*nw_setpwent)(void);
151         struct passwd * (*nw_getpwent)(void);
152         int             (*nw_getpwent_r)(struct passwd *pwdst, char *buf,
153                                       size_t buflen, struct passwd **pwdstp);
154         void            (*nw_endpwent)(void);
155         int             (*nw_initgroups)(const char *user, gid_t group);
156         struct group *  (*nw_getgrnam)(const char *name);
157         int             (*nw_getgrnam_r)(const char *name, struct group *grdst,
158                                       char *buf, size_t buflen, struct group **grdstp);
159         struct group *  (*nw_getgrgid)(gid_t gid);
160         int             (*nw_getgrgid_r)(gid_t gid, struct group *grdst,
161                                       char *buf, size_t buflen, struct group **grdstp);
162         void            (*nw_setgrent)(void);
163         struct group *  (*nw_getgrent)(void);
164         int             (*nw_getgrent_r)(struct group *grdst, char *buf,
165                                       size_t buflen, struct group **grdstp);
166         void            (*nw_endgrent)(void);
167 };
168
169 static struct passwd *nwrap_files_getpwnam(const char *name);
170 static int nwrap_files_getpwnam_r(const char *name, struct passwd *pwdst,
171                                   char *buf, size_t buflen, struct passwd **pwdstp);
172 static struct passwd *nwrap_files_getpwuid(uid_t uid);
173 static int nwrap_files_getpwuid_r(uid_t uid, struct passwd *pwdst,
174                                   char *buf, size_t buflen, struct passwd **pwdstp);
175 static void nwrap_files_setpwent(void);
176 static struct passwd *nwrap_files_getpwent(void);
177 static int nwrap_files_getpwent_r(struct passwd *pwdst, char *buf,
178                                   size_t buflen, struct passwd **pwdstp);
179 static void nwrap_files_endpwent(void);
180 static int nwrap_files_initgroups(const char *user, gid_t group);
181 static struct group *nwrap_files_getgrnam(const char *name);
182 static int nwrap_files_getgrnam_r(const char *name, struct group *grdst,
183                                   char *buf, size_t buflen, struct group **grdstp);
184 static struct group *nwrap_files_getgrgid(gid_t gid);
185 static int nwrap_files_getgrgid_r(gid_t gid, struct group *grdst,
186                                   char *buf, size_t buflen, struct group **grdstp);
187 static void nwrap_files_setgrent(void);
188 static struct group *nwrap_files_getgrent(void);
189 static int nwrap_files_getgrent_r(struct group *grdst, char *buf,
190                                   size_t buflen, struct group **grdstp);
191 static void nwrap_files_endgrent(void);
192
193 struct nwrap_ops nwrap_files_ops = {
194         .name           = "files",
195         .nw_getpwnam    = nwrap_files_getpwnam,
196         .nw_getpwnam_r  = nwrap_files_getpwnam_r,
197         .nw_getpwuid    = nwrap_files_getpwuid,
198         .nw_getpwuid_r  = nwrap_files_getpwuid_r,
199         .nw_setpwent    = nwrap_files_setpwent,
200         .nw_getpwent    = nwrap_files_getpwent,
201         .nw_getpwent_r  = nwrap_files_getpwent_r,
202         .nw_endpwent    = nwrap_files_endpwent,
203         .nw_initgroups  = nwrap_files_initgroups,
204         .nw_getgrnam    = nwrap_files_getgrnam,
205         .nw_getgrnam_r  = nwrap_files_getgrnam_r,
206         .nw_getgrgid    = nwrap_files_getgrgid,
207         .nw_getgrgid_r  = nwrap_files_getgrgid_r,
208         .nw_setgrent    = nwrap_files_setgrent,
209         .nw_getgrent    = nwrap_files_getgrent,
210         .nw_getgrent_r  = nwrap_files_getgrent_r,
211         .nw_endgrent    = nwrap_files_endgrent,
212 };
213
214 struct nwrap_main {
215         struct nwrap_ops *ops;
216 };
217
218 struct nwrap_main *nwrap_main_global;
219 struct nwrap_main __nwrap_main_global;
220
221 struct nwrap_cache {
222         const char *path;
223         int fd;
224         struct stat st;
225         uint8_t *buf;
226         void *private_data;
227         bool (*parse_line)(struct nwrap_cache *, char *line);
228         void (*unload)(struct nwrap_cache *);
229 };
230
231 struct nwrap_pw {
232         struct nwrap_cache *cache;
233
234         struct passwd *list;
235         int num;
236         int idx;
237 };
238
239 struct nwrap_cache __nwrap_cache_pw;
240 struct nwrap_pw nwrap_pw_global;
241
242 static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line);
243 static void nwrap_pw_unload(struct nwrap_cache *nwrap);
244
245 struct nwrap_gr {
246         struct nwrap_cache *cache;
247
248         struct group *list;
249         int num;
250         int idx;
251 };
252
253 struct nwrap_cache __nwrap_cache_gr;
254 struct nwrap_gr nwrap_gr_global;
255
256 static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line);
257 static void nwrap_gr_unload(struct nwrap_cache *nwrap);
258
259 static void nwrap_init(void)
260 {
261         static bool initialized;
262
263         if (initialized) return;
264         initialized = true;
265
266         nwrap_main_global = &__nwrap_main_global;
267
268         nwrap_main_global->ops = &nwrap_files_ops;
269
270         nwrap_pw_global.cache = &__nwrap_cache_pw;
271
272         nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD");
273         nwrap_pw_global.cache->fd = -1;
274         nwrap_pw_global.cache->private_data = &nwrap_pw_global;
275         nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line;
276         nwrap_pw_global.cache->unload = nwrap_pw_unload;
277
278         nwrap_gr_global.cache = &__nwrap_cache_gr;
279
280         nwrap_gr_global.cache->path = getenv("NSS_WRAPPER_GROUP");
281         nwrap_gr_global.cache->fd = -1;
282         nwrap_gr_global.cache->private_data = &nwrap_gr_global;
283         nwrap_gr_global.cache->parse_line = nwrap_gr_parse_line;
284         nwrap_gr_global.cache->unload = nwrap_gr_unload;
285 }
286
287 static bool nwrap_enabled(void)
288 {
289         nwrap_init();
290
291         if (!nwrap_pw_global.cache->path) {
292                 return false;
293         }
294         if (nwrap_pw_global.cache->path[0] == '\0') {
295                 return false;
296         }
297         if (!nwrap_gr_global.cache->path) {
298                 return false;
299         }
300         if (nwrap_gr_global.cache->path[0] == '\0') {
301                 return false;
302         }
303
304         return true;
305 }
306
307 static bool nwrap_parse_file(struct nwrap_cache *nwrap)
308 {
309         int ret;
310         uint8_t *buf = NULL;
311         char *nline;
312
313         if (nwrap->st.st_size == 0) {
314                 NWRAP_DEBUG(("%s: size == 0\n",
315                              __location__));
316                 goto done;
317         }
318
319         if (nwrap->st.st_size > INT32_MAX) {
320                 NWRAP_ERROR(("%s: size[%u] larger than INT32_MAX\n",
321                              __location__, (unsigned)nwrap->st.st_size));
322                 goto failed;
323         }
324
325         ret = lseek(nwrap->fd, 0, SEEK_SET);
326         if (ret != 0) {
327                 NWRAP_ERROR(("%s: lseek - %d\n",__location__,ret));
328                 goto failed;
329         }
330
331         buf = (uint8_t *)malloc(nwrap->st.st_size + 1);
332         if (!buf) {
333                 NWRAP_ERROR(("%s: malloc failed\n",__location__));
334                 goto failed;
335         }
336
337         ret = read(nwrap->fd, buf, nwrap->st.st_size);
338         if (ret != nwrap->st.st_size) {
339                 NWRAP_ERROR(("%s: read(%u) gave %d\n",
340                              __location__, (unsigned)nwrap->st.st_size, ret));
341                 goto failed;
342         }
343
344         buf[nwrap->st.st_size] = '\0';
345
346         nline = (char *)buf;
347         while (nline && nline[0]) {
348                 char *line;
349                 char *e;
350                 bool ok;
351
352                 line = nline;
353                 nline = NULL;
354
355                 e = strchr(line, '\n');
356                 if (e) {
357                         e[0] = '\0';
358                         e++;
359                         if (e[0] == '\r') {
360                                 e[0] = '\0';
361                                 e++;
362                         }
363                         nline = e;
364                 }
365
366                 NWRAP_VERBOSE(("%s:'%s'\n",__location__, line));
367
368                 if (strlen(line) == 0) {
369                         continue;
370                 }
371
372                 ok = nwrap->parse_line(nwrap, line);
373                 if (!ok) {
374                         goto failed;
375                 }
376         }
377
378 done:
379         nwrap->buf = buf;
380         return true;
381
382 failed:
383         if (buf) free(buf);
384         return false;
385 }
386
387 static void nwrap_cache_unload(struct nwrap_cache *nwrap)
388 {
389         nwrap->unload(nwrap);
390
391         if (nwrap->buf) free(nwrap->buf);
392
393         nwrap->buf = NULL;
394 }
395
396 static void nwrap_cache_reload(struct nwrap_cache *nwrap)
397 {
398         struct stat st;
399         int ret;
400         bool ok;
401         bool retried = false;
402
403 reopen:
404         if (nwrap->fd < 0) {
405                 nwrap->fd = open(nwrap->path, O_RDONLY);
406                 if (nwrap->fd < 0) {
407                         NWRAP_ERROR(("%s: unable to open '%s' readonly %d:%s\n",
408                                      __location__,
409                                      nwrap->path, nwrap->fd,
410                                      strerror(errno)));
411                         return;
412                 }
413                 NWRAP_VERBOSE(("%s: open '%s'\n", __location__, nwrap->path));
414         }
415
416         ret = fstat(nwrap->fd, &st);
417         if (ret != 0) {
418                 NWRAP_ERROR(("%s: fstat(%s) - %d:%s\n",
419                              __location__,
420                              nwrap->path,
421                              ret, strerror(errno)));
422                 return;
423         }
424
425         if (retried == false && st.st_nlink == 0) {
426                 /* maybe someone has replaced the file... */
427                 NWRAP_DEBUG(("%s: st_nlink == 0, reopen %s\n",
428                              __location__, nwrap->path));
429                 retried = true;
430                 memset(&nwrap->st, 0, sizeof(nwrap->st));
431                 close(nwrap->fd);
432                 nwrap->fd = -1;
433                 goto reopen;
434         }
435
436         if (st.st_mtime == nwrap->st.st_mtime) {
437                 NWRAP_VERBOSE(("%s: st_mtime[%u] hasn't changed, skip reload\n",
438                                __location__, (unsigned)st.st_mtime));
439                 return;
440         }
441         NWRAP_DEBUG(("%s: st_mtime has changed [%u] => [%u], start reload\n",
442                      __location__, (unsigned)st.st_mtime,
443                      (unsigned)nwrap->st.st_mtime));
444
445         nwrap->st = st;
446
447         nwrap_cache_unload(nwrap);
448
449         ok = nwrap_parse_file(nwrap);
450         if (!ok) {
451                 NWRAP_ERROR(("%s: failed to reload %s\n",
452                              __location__, nwrap->path));
453                 nwrap_cache_unload(nwrap);
454         }
455         NWRAP_DEBUG(("%s: reloaded %s\n",
456                      __location__, nwrap->path));
457 }
458
459 /*
460  * the caller has to call nwrap_unload() on failure
461  */
462 static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line)
463 {
464         struct nwrap_pw *nwrap_pw;
465         char *c;
466         char *p;
467         char *e;
468         struct passwd *pw;
469         size_t list_size;
470
471         nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
472
473         list_size = sizeof(*nwrap_pw->list) * (nwrap_pw->num+1);
474         pw = (struct passwd *)realloc(nwrap_pw->list, list_size);
475         if (!pw) {
476                 NWRAP_ERROR(("%s:realloc(%u) failed\n",
477                              __location__, list_size));
478                 return false;
479         }
480         nwrap_pw->list = pw;
481
482         pw = &nwrap_pw->list[nwrap_pw->num];
483
484         c = line;
485
486         /* name */
487         p = strchr(c, ':');
488         if (!p) {
489                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
490                              __location__, line, c));
491                 return false;
492         }
493         *p = '\0';
494         p++;
495         pw->pw_name = c;
496         c = p;
497
498         NWRAP_VERBOSE(("name[%s]\n", pw->pw_name));
499
500         /* password */
501         p = strchr(c, ':');
502         if (!p) {
503                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
504                              __location__, line, c));
505                 return false;
506         }
507         *p = '\0';
508         p++;
509         pw->pw_passwd = c;
510         c = p;
511
512         NWRAP_VERBOSE(("password[%s]\n", pw->pw_passwd));
513
514         /* uid */
515         p = strchr(c, ':');
516         if (!p) {
517                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
518                              __location__, line, c));
519                 return false;
520         }
521         *p = '\0';
522         p++;
523         e = NULL;
524         pw->pw_uid = (uid_t)strtoul(c, &e, 10);
525         if (c == e) {
526                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
527                              __location__, line, c, strerror(errno)));
528                 return false;
529         }
530         if (e == NULL) {
531                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
532                              __location__, line, c, strerror(errno)));
533                 return false;
534         }
535         if (e[0] != '\0') {
536                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
537                              __location__, line, c, strerror(errno)));
538                 return false;
539         }
540         c = p;
541
542         NWRAP_VERBOSE(("uid[%u]\n", pw->pw_uid));
543
544         /* gid */
545         p = strchr(c, ':');
546         if (!p) {
547                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
548                              __location__, line, c));
549                 return false;
550         }
551         *p = '\0';
552         p++;
553         e = NULL;
554         pw->pw_gid = (gid_t)strtoul(c, &e, 10);
555         if (c == e) {
556                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
557                              __location__, line, c, strerror(errno)));
558                 return false;
559         }
560         if (e == NULL) {
561                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
562                              __location__, line, c, strerror(errno)));
563                 return false;
564         }
565         if (e[0] != '\0') {
566                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
567                              __location__, line, c, strerror(errno)));
568                 return false;
569         }
570         c = p;
571
572         NWRAP_VERBOSE(("gid[%u]\n", pw->pw_gid));
573
574         /* gecos */
575         p = strchr(c, ':');
576         if (!p) {
577                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
578                              __location__, line, c));
579                 return false;
580         }
581         *p = '\0';
582         p++;
583         pw->pw_gecos = c;
584         c = p;
585
586         NWRAP_VERBOSE(("gecos[%s]\n", pw->pw_gecos));
587
588         /* dir */
589         p = strchr(c, ':');
590         if (!p) {
591                 NWRAP_ERROR(("%s:'%s'\n",__location__,c));
592                 return false;
593         }
594         *p = '\0';
595         p++;
596         pw->pw_dir = c;
597         c = p;
598
599         NWRAP_VERBOSE(("dir[%s]\n", pw->pw_dir));
600
601         /* shell */
602         pw->pw_shell = c;
603         NWRAP_VERBOSE(("shell[%s]\n", pw->pw_shell));
604
605         NWRAP_DEBUG(("add user[%s:%s:%u:%u:%s:%s:%s]\n",
606                      pw->pw_name, pw->pw_passwd,
607                      pw->pw_uid, pw->pw_gid,
608                      pw->pw_gecos, pw->pw_dir, pw->pw_shell));
609
610         nwrap_pw->num++;
611         return true;
612 }
613
614 static void nwrap_pw_unload(struct nwrap_cache *nwrap)
615 {
616         struct nwrap_pw *nwrap_pw;
617         nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
618
619         if (nwrap_pw->list) free(nwrap_pw->list);
620
621         nwrap_pw->list = NULL;
622         nwrap_pw->num = 0;
623         nwrap_pw->idx = 0;
624 }
625
626 static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst,
627                            char *buf, size_t buflen, struct passwd **dstp)
628 {
629         char *first;
630         char *last;
631         off_t ofs;
632
633         first = src->pw_name;
634
635         last = src->pw_shell;
636         while (*last) last++;
637
638         ofs = PTR_DIFF(last + 1, first);
639
640         if (ofs > buflen) {
641                 return ERANGE;
642         }
643
644         memcpy(buf, first, ofs);
645
646         ofs = PTR_DIFF(src->pw_name, first);
647         dst->pw_name = buf + ofs;
648         ofs = PTR_DIFF(src->pw_passwd, first);
649         dst->pw_passwd = buf + ofs;
650         dst->pw_uid = src->pw_uid;
651         dst->pw_gid = src->pw_gid;
652         ofs = PTR_DIFF(src->pw_gecos, first);
653         dst->pw_gecos = buf + ofs;
654         ofs = PTR_DIFF(src->pw_dir, first);
655         dst->pw_dir = buf + ofs;
656         ofs = PTR_DIFF(src->pw_shell, first);
657         dst->pw_shell = buf + ofs;
658
659         if (dstp) {
660                 *dstp = dst;
661         }
662
663         return 0;
664 }
665
666 /*
667  * the caller has to call nwrap_unload() on failure
668  */
669 static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line)
670 {
671         struct nwrap_gr *nwrap_gr;
672         char *c;
673         char *p;
674         char *e;
675         struct group *gr;
676         size_t list_size;
677         unsigned nummem;
678
679         nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
680
681         list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1);
682         gr = (struct group *)realloc(nwrap_gr->list, list_size);
683         if (!gr) {
684                 NWRAP_ERROR(("%s:realloc failed\n",__location__));
685                 return false;
686         }
687         nwrap_gr->list = gr;
688
689         gr = &nwrap_gr->list[nwrap_gr->num];
690
691         c = line;
692
693         /* name */
694         p = strchr(c, ':');
695         if (!p) {
696                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
697                              __location__, line, c));
698                 return false;
699         }
700         *p = '\0';
701         p++;
702         gr->gr_name = c;
703         c = p;
704
705         NWRAP_VERBOSE(("name[%s]\n", gr->gr_name));
706
707         /* password */
708         p = strchr(c, ':');
709         if (!p) {
710                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
711                              __location__, line, c));
712                 return false;
713         }
714         *p = '\0';
715         p++;
716         gr->gr_passwd = c;
717         c = p;
718
719         NWRAP_VERBOSE(("password[%s]\n", gr->gr_passwd));
720
721         /* gid */
722         p = strchr(c, ':');
723         if (!p) {
724                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
725                              __location__, line, c));
726                 return false;
727         }
728         *p = '\0';
729         p++;
730         e = NULL;
731         gr->gr_gid = (gid_t)strtoul(c, &e, 10);
732         if (c == e) {
733                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
734                              __location__, line, c, strerror(errno)));
735                 return false;
736         }
737         if (e == NULL) {
738                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
739                              __location__, line, c, strerror(errno)));
740                 return false;
741         }
742         if (e[0] != '\0') {
743                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
744                              __location__, line, c, strerror(errno)));
745                 return false;
746         }
747         c = p;
748
749         NWRAP_VERBOSE(("gid[%u]\n", gr->gr_gid));
750
751         /* members */
752         gr->gr_mem = (char **)malloc(sizeof(char *));
753         if (!gr->gr_mem) {
754                 NWRAP_ERROR(("%s:calloc failed\n",__location__));
755                 return false;
756         }
757         gr->gr_mem[0] = NULL;
758
759         for(nummem=0; p; nummem++) {
760                 char **m;
761                 size_t m_size;
762                 c = p;
763                 p = strchr(c, ',');
764                 if (p) {
765                         *p = '\0';
766                         p++;
767                 }
768
769                 if (strlen(c) == 0) {
770                         break;
771                 }
772
773                 m_size = sizeof(char *) * (nummem+2);
774                 m = (char **)realloc(gr->gr_mem, m_size);
775                 if (!m) {
776                         NWRAP_ERROR(("%s:realloc(%u) failed\n",
777                                       __location__, m_size));
778                         return false;
779                 }
780                 gr->gr_mem = m;
781                 gr->gr_mem[nummem] = c;
782                 gr->gr_mem[nummem+1] = NULL;
783
784                 NWRAP_VERBOSE(("member[%u]: '%s'\n", nummem, gr->gr_mem[nummem]));
785         }
786
787         NWRAP_DEBUG(("add group[%s:%s:%u:] with %u members\n",
788                      gr->gr_name, gr->gr_passwd, gr->gr_gid, nummem));
789
790         nwrap_gr->num++;
791         return true;
792 }
793
794 static void nwrap_gr_unload(struct nwrap_cache *nwrap)
795 {
796         int i;
797         struct nwrap_gr *nwrap_gr;
798         nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
799
800         if (nwrap_gr->list) {
801                 for (i=0; i < nwrap_gr->num; i++) {
802                         if (nwrap_gr->list[i].gr_mem) {
803                                 free(nwrap_gr->list[i].gr_mem);
804                         }
805                 }
806                 free(nwrap_gr->list);
807         }
808
809         nwrap_gr->list = NULL;
810         nwrap_gr->num = 0;
811         nwrap_gr->idx = 0;
812 }
813
814 static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
815                            char *buf, size_t buflen, struct group **dstp)
816 {
817         char *first;
818         char **lastm;
819         char *last;
820         off_t ofsb;
821         off_t ofsm;
822         off_t ofs;
823         unsigned i;
824
825         first = src->gr_name;
826
827         lastm = src->gr_mem;
828         while (*lastm) lastm++;
829
830         last = *lastm;
831         while (*last) last++;
832
833         ofsb = PTR_DIFF(last + 1, first);
834         ofsm = PTR_DIFF(lastm + 1, src->gr_mem);
835
836         if ((ofsb + ofsm) > buflen) {
837                 return ERANGE;
838         }
839
840         memcpy(buf, first, ofsb);
841         memcpy(buf + ofsb, src->gr_mem, ofsm);
842
843         ofs = PTR_DIFF(src->gr_name, first);
844         dst->gr_name = buf + ofs;
845         ofs = PTR_DIFF(src->gr_passwd, first);
846         dst->gr_passwd = buf + ofs;
847         dst->gr_gid = src->gr_gid;
848
849         dst->gr_mem = (char **)(buf + ofsb);
850         for (i=0; src->gr_mem[i]; i++) {
851                 ofs = PTR_DIFF(src->gr_mem[i], first);
852                 dst->gr_mem[i] = buf + ofs;
853         }
854
855         if (dstp) {
856                 *dstp = dst;
857         }
858
859         return 0;
860 }
861
862 /* user functions */
863
864 static struct passwd *nwrap_files_getpwnam(const char *name)
865 {
866         int i;
867
868         nwrap_cache_reload(nwrap_pw_global.cache);
869
870         for (i=0; i<nwrap_pw_global.num; i++) {
871                 if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
872                         NWRAP_DEBUG(("%s: user[%s] found\n",
873                                      __location__, name));
874                         return &nwrap_pw_global.list[i];
875                 }
876                 NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n",
877                                __location__, name,
878                                nwrap_pw_global.list[i].pw_name));
879         }
880
881         NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name));
882
883         errno = ENOENT;
884         return NULL;
885 }
886
887 static int nwrap_files_getpwnam_r(const char *name, struct passwd *pwdst,
888                                   char *buf, size_t buflen, struct passwd **pwdstp)
889 {
890         struct passwd *pw;
891
892         pw = nwrap_files_getpwnam(name);
893         if (!pw) {
894                 if (errno == 0) {
895                         return ENOENT;
896                 }
897                 return errno;
898         }
899
900         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
901 }
902
903 static struct passwd *nwrap_files_getpwuid(uid_t uid)
904 {
905         int i;
906
907         nwrap_cache_reload(nwrap_pw_global.cache);
908
909         for (i=0; i<nwrap_pw_global.num; i++) {
910                 if (nwrap_pw_global.list[i].pw_uid == uid) {
911                         NWRAP_DEBUG(("%s: uid[%u] found\n",
912                                      __location__, uid));
913                         return &nwrap_pw_global.list[i];
914                 }
915                 NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n",
916                                __location__, uid,
917                                nwrap_pw_global.list[i].pw_uid));
918         }
919
920         NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid));
921
922         errno = ENOENT;
923         return NULL;
924 }
925
926 static int nwrap_files_getpwuid_r(uid_t uid, struct passwd *pwdst,
927                                   char *buf, size_t buflen, struct passwd **pwdstp)
928 {
929         struct passwd *pw;
930
931         pw = nwrap_files_getpwuid(uid);
932         if (!pw) {
933                 if (errno == 0) {
934                         return ENOENT;
935                 }
936                 return errno;
937         }
938
939         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
940 }
941
942 /* user enum functions */
943 static void nwrap_files_setpwent(void)
944 {
945         nwrap_pw_global.idx = 0;
946 }
947
948 static struct passwd *nwrap_files_getpwent(void)
949 {
950         struct passwd *pw;
951
952         if (nwrap_pw_global.idx == 0) {
953                 nwrap_cache_reload(nwrap_pw_global.cache);
954         }
955
956         if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
957                 errno = ENOENT;
958                 return NULL;
959         }
960
961         pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
962
963         NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
964                        __location__, pw->pw_name, pw->pw_uid));
965
966         return pw;
967 }
968
969 static int nwrap_files_getpwent_r(struct passwd *pwdst, char *buf,
970                                   size_t buflen, struct passwd **pwdstp)
971 {
972         struct passwd *pw;
973
974         pw = nwrap_files_getpwent();
975         if (!pw) {
976                 if (errno == 0) {
977                         return ENOENT;
978                 }
979                 return errno;
980         }
981
982         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
983 }
984
985 static void nwrap_files_endpwent(void)
986 {
987         nwrap_pw_global.idx = 0;
988 }
989
990 /* misc functions */
991 static int nwrap_files_initgroups(const char *user, gid_t group)
992 {
993         /* TODO: maybe we should also fake this... */
994         return EPERM;
995 }
996
997 /* group functions */
998 static struct group *nwrap_files_getgrnam(const char *name)
999 {
1000         int i;
1001
1002         nwrap_cache_reload(nwrap_gr_global.cache);
1003
1004         for (i=0; i<nwrap_gr_global.num; i++) {
1005                 if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
1006                         NWRAP_DEBUG(("%s: group[%s] found\n",
1007                                      __location__, name));
1008                         return &nwrap_gr_global.list[i];
1009                 }
1010                 NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n",
1011                                __location__, name,
1012                                nwrap_gr_global.list[i].gr_name));
1013         }
1014
1015         NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name));
1016
1017         errno = ENOENT;
1018         return NULL;
1019 }
1020
1021 static int nwrap_files_getgrnam_r(const char *name, struct group *grdst,
1022                                   char *buf, size_t buflen, struct group **grdstp)
1023 {
1024         struct group *gr;
1025
1026         gr = nwrap_files_getgrnam(name);
1027         if (!gr) {
1028                 if (errno == 0) {
1029                         return ENOENT;
1030                 }
1031                 return errno;
1032         }
1033
1034         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1035 }
1036
1037 static struct group *nwrap_files_getgrgid(gid_t gid)
1038 {
1039         int i;
1040
1041         nwrap_cache_reload(nwrap_gr_global.cache);
1042
1043         for (i=0; i<nwrap_gr_global.num; i++) {
1044                 if (nwrap_gr_global.list[i].gr_gid == gid) {
1045                         NWRAP_DEBUG(("%s: gid[%u] found\n",
1046                                      __location__, gid));
1047                         return &nwrap_gr_global.list[i];
1048                 }
1049                 NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n",
1050                                __location__, gid,
1051                                nwrap_gr_global.list[i].gr_gid));
1052         }
1053
1054         NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid));
1055
1056         errno = ENOENT;
1057         return NULL;
1058 }
1059
1060 static int nwrap_files_getgrgid_r(gid_t gid, struct group *grdst,
1061                                   char *buf, size_t buflen, struct group **grdstp)
1062 {
1063         struct group *gr;
1064
1065         gr = nwrap_files_getgrgid(gid);
1066         if (!gr) {
1067                 if (errno == 0) {
1068                         return ENOENT;
1069                 }
1070                 return errno;
1071         }
1072
1073         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1074 }
1075
1076 /* group enum functions */
1077 static void nwrap_files_setgrent(void)
1078 {
1079         nwrap_gr_global.idx = 0;
1080 }
1081
1082 static struct group *nwrap_files_getgrent(void)
1083 {
1084         struct group *gr;
1085
1086         if (nwrap_gr_global.idx == 0) {
1087                 nwrap_cache_reload(nwrap_gr_global.cache);
1088         }
1089
1090         if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
1091                 errno = ENOENT;
1092                 return NULL;
1093         }
1094
1095         gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
1096
1097         NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n",
1098                        __location__, gr->gr_name, gr->gr_gid));
1099
1100         return gr;
1101 }
1102
1103 static int nwrap_files_getgrent_r(struct group *grdst, char *buf,
1104                                   size_t buflen, struct group **grdstp)
1105 {
1106         struct group *gr;
1107
1108         gr = nwrap_files_getgrent();
1109         if (!gr) {
1110                 if (errno == 0) {
1111                         return ENOENT;
1112                 }
1113                 return errno;
1114         }
1115
1116         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1117 }
1118
1119 static void nwrap_files_endgrent(void)
1120 {
1121         nwrap_gr_global.idx = 0;
1122 }
1123
1124 /*
1125  * PUBLIC interface
1126  */
1127
1128 _PUBLIC_ struct passwd *nwrap_getpwnam(const char *name)
1129 {
1130         if (!nwrap_enabled()) {
1131                 return real_getpwnam(name);
1132         }
1133
1134         return nwrap_main_global->ops->nw_getpwnam(name);
1135 }
1136
1137 _PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
1138                               char *buf, size_t buflen, struct passwd **pwdstp)
1139 {
1140         if (!nwrap_enabled()) {
1141                 return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
1142         }
1143
1144         return nwrap_main_global->ops->nw_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
1145 }
1146
1147 _PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid)
1148 {
1149         if (!nwrap_enabled()) {
1150                 return real_getpwuid(uid);
1151         }
1152
1153         return nwrap_main_global->ops->nw_getpwuid(uid);
1154 }
1155
1156 _PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
1157                               char *buf, size_t buflen, struct passwd **pwdstp)
1158 {
1159         if (!nwrap_enabled()) {
1160                 return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
1161         }
1162
1163         return nwrap_main_global->ops->nw_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
1164 }
1165
1166 _PUBLIC_ void nwrap_setpwent(void)
1167 {
1168         if (!nwrap_enabled()) {
1169                 real_setpwent();
1170                 return;
1171         }
1172
1173         nwrap_main_global->ops->nw_setpwent();
1174 }
1175
1176 _PUBLIC_ struct passwd *nwrap_getpwent(void)
1177 {
1178         if (!nwrap_enabled()) {
1179                 return real_getpwent();
1180         }
1181
1182         return nwrap_main_global->ops->nw_getpwent();
1183 }
1184
1185 _PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
1186                               size_t buflen, struct passwd **pwdstp)
1187 {
1188         if (!nwrap_enabled()) {
1189 #ifdef SOLARIS_GETPWENT_R
1190                 struct passwd *pw;
1191                 pw = real_getpwent_r(pwdst, buf, buflen);
1192                 if (!pw) {
1193                         if (errno == 0) {
1194                                 return ENOENT;
1195                         }
1196                         return errno;
1197                 }
1198                 if (pwdstp) {
1199                         *pwdstp = pw;
1200                 }
1201                 return 0;
1202 #else
1203                 return real_getpwent_r(pwdst, buf, buflen, pwdstp);
1204 #endif
1205         }
1206
1207         return nwrap_main_global->ops->nw_getpwent_r(pwdst, buf, buflen, pwdstp);
1208 }
1209
1210 _PUBLIC_ void nwrap_endpwent(void)
1211 {
1212         if (!nwrap_enabled()) {
1213                 real_endpwent();
1214                 return;
1215         }
1216
1217         nwrap_main_global->ops->nw_endpwent();
1218 }
1219
1220 _PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
1221 {
1222         if (!nwrap_enabled()) {
1223                 return real_initgroups(user, group);
1224         }
1225
1226         return nwrap_main_global->ops->nw_initgroups(user, group);
1227 }
1228
1229 _PUBLIC_ struct group *nwrap_getgrnam(const char *name)
1230 {
1231         if (!nwrap_enabled()) {
1232                 return real_getgrnam(name);
1233         }
1234
1235         return nwrap_main_global->ops->nw_getgrnam(name);
1236 }
1237
1238 _PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst,
1239                               char *buf, size_t buflen, struct group **grdstp)
1240 {
1241         if (!nwrap_enabled()) {
1242                 return real_getgrnam_r(name, grdst, buf, buflen, grdstp);
1243         }
1244
1245         return nwrap_main_global->ops->nw_getgrnam_r(name, grdst, buf, buflen, grdstp);
1246 }
1247
1248 _PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
1249 {
1250         if (!nwrap_enabled()) {
1251                 return real_getgrgid(gid);
1252         }
1253
1254         return nwrap_main_global->ops->nw_getgrgid(gid);
1255 }
1256
1257 _PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
1258                               char *buf, size_t buflen, struct group **grdstp)
1259 {
1260         if (!nwrap_enabled()) {
1261                 return real_getgrgid_r(gid, grdst, buf, buflen, grdstp);
1262         }
1263
1264         return nwrap_main_global->ops->nw_getgrgid_r(gid, grdst, buf, buflen, grdstp);
1265 }
1266
1267 _PUBLIC_ void nwrap_setgrent(void)
1268 {
1269         if (!nwrap_enabled()) {
1270                 real_setgrent();
1271                 return;
1272         }
1273
1274         nwrap_main_global->ops->nw_setgrent();
1275 }
1276
1277 _PUBLIC_ struct group *nwrap_getgrent(void)
1278 {
1279         if (!nwrap_enabled()) {
1280                 return real_getgrent();
1281         }
1282
1283         return nwrap_main_global->ops->nw_getgrent();
1284 }
1285
1286 _PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf,
1287                               size_t buflen, struct group **grdstp)
1288 {
1289         if (!nwrap_enabled()) {
1290 #ifdef SOLARIS_GETGRENT_R
1291                 struct group *gr;
1292                 gr = real_getgrent_r(grdst, buf, buflen);
1293                 if (!gr) {
1294                         if (errno == 0) {
1295                                 return ENOENT;
1296                         }
1297                         return errno;
1298                 }
1299                 if (grdstp) {
1300                         *grdstp = gr;
1301                 }
1302                 return 0;
1303 #else
1304                 return real_getgrent_r(grdst, buf, buflen, grdstp);
1305 #endif
1306         }
1307
1308         return nwrap_main_global->ops->nw_getgrent_r(grdst, buf, buflen, grdstp);
1309 }
1310
1311 _PUBLIC_ void nwrap_endgrent(void)
1312 {
1313         if (!nwrap_enabled()) {
1314                 real_endgrent();
1315                 return;
1316         }
1317
1318         nwrap_main_global->ops->nw_endgrent();
1319 }
1320
1321 _PUBLIC_ int nwrap_getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups)
1322 {
1323         struct group *grp;
1324         gid_t *groups_tmp;
1325         int count = 1;
1326         const char *name_of_group = NULL;
1327
1328         if (!nwrap_enabled()) {
1329                 return real_getgrouplist(user, group, groups, ngroups);
1330         }
1331
1332         NWRAP_DEBUG(("%s: getgrouplist called for %s\n", __location__, user));
1333
1334         groups_tmp = (gid_t *)malloc(count * sizeof(gid_t));
1335         if (!groups_tmp) {
1336                 NWRAP_ERROR(("%s:calloc failed\n",__location__));
1337                 errno = ENOMEM;
1338                 return -1;
1339         }
1340
1341         memcpy(groups_tmp, &group, sizeof(gid_t));
1342
1343         grp = nwrap_getgrgid(group);
1344         if (grp) {
1345                 name_of_group = grp->gr_name;
1346         }
1347
1348         nwrap_setgrent();
1349         while ((grp = nwrap_getgrent()) != NULL) {
1350                 int i = 0;
1351
1352                 NWRAP_VERBOSE(("%s: inspecting %s for group membership\n",
1353                                __location__, grp->gr_name));
1354
1355                 for (i=0; grp->gr_mem && grp->gr_mem[i] != NULL; i++) {
1356
1357                         if ((strcmp(user, grp->gr_mem[i]) == 0) &&
1358                             (strcmp(name_of_group, grp->gr_name) != 0)) {
1359
1360                                 NWRAP_DEBUG(("%s: %s is member of %s\n",
1361                                         __location__, user, grp->gr_name));
1362
1363                                 groups_tmp = (gid_t *)realloc(groups_tmp, (count + 1) * sizeof(gid_t));
1364                                 if (!groups_tmp) {
1365                                         NWRAP_ERROR(("%s:calloc failed\n",__location__));
1366                                         errno = ENOMEM;
1367                                         return -1;
1368                                 }
1369
1370                                 memcpy(&groups_tmp[count], &grp->gr_gid, sizeof(gid_t));
1371                                 count++;
1372                         }
1373                 }
1374         }
1375         nwrap_endgrent();
1376
1377         NWRAP_VERBOSE(("%s: %s is member of %d groups: %d\n",
1378                        __location__, user, *ngroups));
1379
1380         if (*ngroups < count) {
1381                 *ngroups = count;
1382                 free(groups_tmp);
1383                 return -1;
1384         }
1385
1386         *ngroups = count;
1387         memcpy(groups, groups_tmp, count * sizeof(gid_t));
1388         free(groups_tmp);
1389
1390         return count;
1391 }