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