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