3c061f0de84b478c1f58d090ab1fd84229006a60
[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         if (!nwrap_enabled()) {
859                 return real_getpwuid(uid);
860         }
861
862         return nwrap_files_getpwuid(uid);
863 }
864
865 static int nwrap_files_getpwuid_r(uid_t uid, struct passwd *pwdst,
866                                   char *buf, size_t buflen, struct passwd **pwdstp)
867 {
868         struct passwd *pw;
869
870         pw = nwrap_getpwuid(uid);
871         if (!pw) {
872                 if (errno == 0) {
873                         return ENOENT;
874                 }
875                 return errno;
876         }
877
878         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
879 }
880
881 _PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
882                               char *buf, size_t buflen, struct passwd **pwdstp)
883 {
884         if (!nwrap_enabled()) {
885                 return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
886         }
887
888         return nwrap_files_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
889 }
890
891 /* user enum functions */
892 static void nwrap_files_setpwent(void)
893 {
894         nwrap_pw_global.idx = 0;
895 }
896
897 _PUBLIC_ void nwrap_setpwent(void)
898 {
899         if (!nwrap_enabled()) {
900                 real_setpwent();
901         }
902
903         nwrap_files_setpwent();
904 }
905
906 static struct passwd *nwrap_files_getpwent(void)
907 {
908         struct passwd *pw;
909
910         if (nwrap_pw_global.idx == 0) {
911                 nwrap_cache_reload(nwrap_pw_global.cache);
912         }
913
914         if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
915                 errno = ENOENT;
916                 return NULL;
917         }
918
919         pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
920
921         NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
922                        __location__, pw->pw_name, pw->pw_uid));
923
924         return pw;
925 }
926
927 _PUBLIC_ struct passwd *nwrap_getpwent(void)
928 {
929         if (!nwrap_enabled()) {
930                 return real_getpwent();
931         }
932
933         return nwrap_files_getpwent();
934 }
935
936 static int nwrap_files_getpwent_r(struct passwd *pwdst, char *buf,
937                                   size_t buflen, struct passwd **pwdstp)
938 {
939         struct passwd *pw;
940
941         pw = nwrap_getpwent();
942         if (!pw) {
943                 if (errno == 0) {
944                         return ENOENT;
945                 }
946                 return errno;
947         }
948
949         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
950 }
951
952 _PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
953                               size_t buflen, struct passwd **pwdstp)
954 {
955         if (!nwrap_enabled()) {
956 #ifdef SOLARIS_GETPWENT_R
957                 pw = real_getpwent_r(pwdst, buf, buflen);
958                 if (!pw) {
959                         if (errno == 0) {
960                                 return ENOENT;
961                         }
962                         return errno;
963                 }
964                 if (pwdstp) {
965                         *pwdstp = pw;
966                 }
967                 return 0;
968 #else
969                 return real_getpwent_r(pwdst, buf, buflen, pwdstp);
970 #endif
971         }
972
973         return nwrap_files_getpwent_r(pwdst, buf, buflen, pwdstp);
974 }
975
976 static void nwrap_files_endpwent(void)
977 {
978         nwrap_pw_global.idx = 0;
979 }
980
981 _PUBLIC_ void nwrap_endpwent(void)
982 {
983         if (!nwrap_enabled()) {
984                 real_endpwent();
985         }
986
987         nwrap_files_endpwent();
988 }
989
990 /* misc functions */
991 static int nwrap_files_initgroups(const char *user, gid_t group)
992 {
993         /* TODO: maybe we should also fake this... */
994         return EPERM;
995 }
996
997 _PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
998 {
999         if (!nwrap_enabled()) {
1000                 return real_initgroups(user, group);
1001         }
1002
1003         return nwrap_files_initgroups(user, group);
1004 }
1005
1006 /* group functions */
1007 static struct group *nwrap_files_getgrnam(const char *name)
1008 {
1009         int i;
1010
1011         nwrap_cache_reload(nwrap_gr_global.cache);
1012
1013         for (i=0; i<nwrap_gr_global.num; i++) {
1014                 if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
1015                         NWRAP_DEBUG(("%s: group[%s] found\n",
1016                                      __location__, name));
1017                         return &nwrap_gr_global.list[i];
1018                 }
1019                 NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n",
1020                                __location__, name,
1021                                nwrap_gr_global.list[i].gr_name));
1022         }
1023
1024         NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name));
1025
1026         errno = ENOENT;
1027         return NULL;
1028 }
1029
1030 _PUBLIC_ struct group *nwrap_getgrnam(const char *name)
1031 {
1032         if (!nwrap_enabled()) {
1033                 return real_getgrnam(name);
1034         }
1035
1036         return nwrap_files_getgrnam(name);
1037 }
1038
1039 static int nwrap_files_getgrnam_r(const char *name, struct group *grdst,
1040                                   char *buf, size_t buflen, struct group **grdstp)
1041 {
1042         struct group *gr;
1043
1044         gr = nwrap_getgrnam(name);
1045         if (!gr) {
1046                 if (errno == 0) {
1047                         return ENOENT;
1048                 }
1049                 return errno;
1050         }
1051
1052         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1053 }
1054
1055 _PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst,
1056                               char *buf, size_t buflen, struct group **grdstp)
1057 {
1058         if (!nwrap_enabled()) {
1059                 return real_getgrnam_r(name, grdst, buf, buflen, grdstp);
1060         }
1061
1062         return nwrap_files_getgrnam_r(name, grdst, buf, buflen, grdstp);
1063 }
1064
1065 static struct group *nwrap_files_getgrgid(gid_t gid)
1066 {
1067         int i;
1068
1069         nwrap_cache_reload(nwrap_gr_global.cache);
1070
1071         for (i=0; i<nwrap_gr_global.num; i++) {
1072                 if (nwrap_gr_global.list[i].gr_gid == gid) {
1073                         NWRAP_DEBUG(("%s: gid[%u] found\n",
1074                                      __location__, gid));
1075                         return &nwrap_gr_global.list[i];
1076                 }
1077                 NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n",
1078                                __location__, gid,
1079                                nwrap_gr_global.list[i].gr_gid));
1080         }
1081
1082         NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid));
1083
1084         errno = ENOENT;
1085         return NULL;
1086 }
1087
1088 _PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
1089 {
1090         if (!nwrap_enabled()) {
1091                 return real_getgrgid(gid);
1092         }
1093
1094         return nwrap_files_getgrgid(gid);
1095 }
1096
1097 static int nwrap_files_getgrgid_r(gid_t gid, struct group *grdst,
1098                                   char *buf, size_t buflen, struct group **grdstp)
1099 {
1100         struct group *gr;
1101
1102         gr = nwrap_getgrgid(gid);
1103         if (!gr) {
1104                 if (errno == 0) {
1105                         return ENOENT;
1106                 }
1107                 return errno;
1108         }
1109
1110         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1111
1112         return ENOENT;
1113 }
1114
1115 _PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
1116                               char *buf, size_t buflen, struct group **grdstp)
1117 {
1118         if (!nwrap_enabled()) {
1119                 return real_getgrgid_r(gid, grdst, buf, buflen, grdstp);
1120         }
1121
1122         return nwrap_files_getgrgid_r(gid, grdst, buf, buflen, grdstp);
1123 }
1124
1125 /* group enum functions */
1126 static void nwrap_files_setgrent(void)
1127 {
1128         nwrap_gr_global.idx = 0;
1129 }
1130
1131 _PUBLIC_ void nwrap_setgrent(void)
1132 {
1133         if (!nwrap_enabled()) {
1134                 real_setgrent();
1135         }
1136
1137         nwrap_files_setgrent();
1138 }
1139
1140 static struct group *nwrap_files_getgrent(void)
1141 {
1142         struct group *gr;
1143
1144         if (nwrap_gr_global.idx == 0) {
1145                 nwrap_cache_reload(nwrap_gr_global.cache);
1146         }
1147
1148         if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
1149                 errno = ENOENT;
1150                 return NULL;
1151         }
1152
1153         gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
1154
1155         NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n",
1156                        __location__, gr->gr_name, gr->gr_gid));
1157
1158         return gr;
1159 }
1160
1161 _PUBLIC_ struct group *nwrap_getgrent(void)
1162 {
1163         if (!nwrap_enabled()) {
1164                 return real_getgrent();
1165         }
1166
1167         return nwrap_files_getgrent();
1168 }
1169
1170 static int nwrap_files_getgrent_r(struct group *grdst, char *buf,
1171                                   size_t buflen, struct group **grdstp)
1172 {
1173         struct group *gr;
1174
1175         gr = nwrap_getgrent();
1176         if (!gr) {
1177                 if (errno == 0) {
1178                         return ENOENT;
1179                 }
1180                 return errno;
1181         }
1182
1183         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1184 }
1185
1186 _PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf,
1187                               size_t buflen, struct group **grdstp)
1188 {
1189         if (!nwrap_enabled()) {
1190 #ifdef SOLARIS_GETGRENT_R
1191                 gr = real_getgrent_r(grdst, buf, buflen);
1192                 if (!gr) {
1193                         if (errno == 0) {
1194                                 return ENOENT;
1195                         }
1196                         return errno;
1197                 }
1198                 if (grdstp) {
1199                         *grdstp = gr;
1200                 }
1201                 return 0;
1202 #else
1203                 return real_getgrent_r(grdst, buf, buflen, grdstp);
1204 #endif
1205         }
1206
1207         return nwrap_files_getgrent_r(grdst, buf, buflen, grdstp);
1208 }
1209
1210 static void nwrap_files_endgrent(void)
1211 {
1212         nwrap_gr_global.idx = 0;
1213 }
1214
1215 _PUBLIC_ void nwrap_endgrent(void)
1216 {
1217         if (!nwrap_enabled()) {
1218                 real_endgrent();
1219         }
1220
1221         nwrap_files_endgrent();
1222 }