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