r25868: nss_wrapper: add solaris versions of getpwent_r and getgrent_r
[jelmer/openchange-samba.git] / source / 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 "lib/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 = 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 **destp)
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         return 0;
571 }
572
573 /*
574  * the caller has to call nwrap_unload() on failure
575  */
576 static bool nwrap_gr_parse_line(struct nwrap_cache *nwrap, char *line)
577 {
578         struct nwrap_gr *nwrap_gr;
579         char *c;
580         char *p;
581         char *e;
582         struct group *gr;
583         size_t list_size;
584         unsigned nummem;
585
586         nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
587
588         list_size = sizeof(*nwrap_gr->list) * (nwrap_gr->num+1);
589         gr = (struct group *)realloc(nwrap_gr->list, list_size);
590         if (!gr) {
591                 NWRAP_ERROR(("%s:realloc failed\n",__location__));
592                 return false;
593         }
594         nwrap_gr->list = gr;
595
596         gr = &nwrap_gr->list[nwrap_gr->num];
597
598         c = line;
599
600         /* name */
601         p = strchr(c, ':');
602         if (!p) {
603                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
604                              __location__, line, c));
605                 return false;
606         }
607         *p = '\0';
608         p++;
609         gr->gr_name = c;
610         c = p;
611
612         NWRAP_VERBOSE(("name[%s]\n", gr->gr_name));
613
614         /* password */
615         p = strchr(c, ':');
616         if (!p) {
617                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
618                              __location__, line, c));
619                 return false;
620         }
621         *p = '\0';
622         p++;
623         gr->gr_passwd = c;
624         c = p;
625
626         NWRAP_VERBOSE(("password[%s]\n", gr->gr_passwd));
627
628         /* gid */
629         p = strchr(c, ':');
630         if (!p) {
631                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
632                              __location__, line, c));
633                 return false;
634         }
635         *p = '\0';
636         p++;
637         e = NULL;
638         gr->gr_gid = (gid_t)strtoul(c, &e, 10);
639         if (c == e) {
640                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
641                              __location__, line, c, strerror(errno)));
642                 return false;
643         }
644         if (e == NULL) {
645                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
646                              __location__, line, c, strerror(errno)));
647                 return false;
648         }
649         if (e[0] != '\0') {
650                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
651                              __location__, line, c, strerror(errno)));
652                 return false;
653         }
654         c = p;
655
656         NWRAP_VERBOSE(("gid[%u]\n", gr->gr_gid));
657
658         /* members */
659         gr->gr_mem = (char **)malloc(sizeof(char *));
660         if (!gr->gr_mem) {
661                 NWRAP_ERROR(("%s:calloc failed\n",__location__));
662                 return false;
663         }
664         gr->gr_mem[0] = NULL;
665
666         for(nummem=0; p; nummem++) {
667                 char **m;
668                 size_t m_size;
669                 c = p;
670                 p = strchr(c, ',');
671                 if (p) {
672                         *p = '\0';
673                         p++;
674                 }
675
676                 if (strlen(c) == 0) {
677                         break;
678                 }
679
680                 m_size = sizeof(char *) * (nummem+2);
681                 m = (char **)realloc(gr->gr_mem, m_size);
682                 if (!m) {
683                         NWRAP_ERROR(("%s:realloc(%u) failed\n",
684                                       __location__, m_size));
685                         return false;
686                 }
687                 gr->gr_mem = m;
688                 gr->gr_mem[nummem] = c;
689                 gr->gr_mem[nummem+1] = NULL;
690
691                 NWRAP_VERBOSE(("member[%u]: '%s'\n", nummem, gr->gr_mem[nummem]));
692         }
693
694         NWRAP_DEBUG(("add group[%s:%s:%u:] with %u members\n",
695                      gr->gr_name, gr->gr_passwd, gr->gr_gid, nummem));
696
697         nwrap_gr->num++;
698         return true;
699 }
700
701 static void nwrap_gr_unload(struct nwrap_cache *nwrap)
702 {
703         int i;
704         struct nwrap_gr *nwrap_gr;
705         nwrap_gr = (struct nwrap_gr *)nwrap->private_data;
706
707         if (nwrap_gr->list) {
708                 for (i=0; i < nwrap_gr->num; i++) {
709                         if (nwrap_gr->list[i].gr_mem) {
710                                 free(nwrap_gr->list[i].gr_mem);
711                         }
712                 }
713                 free(nwrap_gr->list);
714         }
715
716         nwrap_gr->list = NULL;
717         nwrap_gr->num = 0;
718         nwrap_gr->idx = 0;
719 }
720
721 static int nwrap_gr_copy_r(const struct group *src, struct group *dst,
722                            char *buf, size_t buflen, struct group **destp)
723 {
724         char *first;
725         char **lastm;
726         char *last;
727         off_t ofsb;
728         off_t ofsm;
729         off_t ofs;
730         unsigned i;
731
732         first = src->gr_name;
733
734         lastm = src->gr_mem;
735         while (*lastm) lastm++;
736
737         last = *lastm;
738         while (*last) last++;
739
740         ofsb = PTR_DIFF(last + 1, first);
741         ofsm = PTR_DIFF(lastm + 1, src->gr_mem);
742
743         if ((ofsb + ofsm) > buflen) {
744                 return ERANGE;
745         }
746
747         memcpy(buf, first, ofsb);
748         memcpy(buf + ofsb, src->gr_mem, ofsm);
749
750         ofs = PTR_DIFF(src->gr_name, first);
751         dst->gr_name = buf + ofs;
752         ofs = PTR_DIFF(src->gr_passwd, first);
753         dst->gr_passwd = buf + ofs;
754         dst->gr_gid = src->gr_gid;
755
756         dst->gr_mem = (char **)(buf + ofsb);
757         for (i=0; src->gr_mem[i]; i++) {
758                 ofs = PTR_DIFF(src->gr_mem[i], first);
759                 dst->gr_mem[i] = buf + ofs;
760         }
761
762         return 0;
763 }
764
765 /* user functions */
766 _PUBLIC_ struct passwd *nwrap_getpwnam(const char *name)
767 {
768         int i;
769
770         if (!nwrap_enabled()) {
771                 return real_getpwnam(name);
772         }
773
774         nwrap_cache_reload(nwrap_pw_global.cache);
775
776         for (i=0; i<nwrap_pw_global.num; i++) {
777                 if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
778                         NWRAP_DEBUG(("%s: user[%s] found\n",
779                                      __location__, name));
780                         return &nwrap_pw_global.list[i];
781                 }
782                 NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n",
783                                __location__, name,
784                                nwrap_pw_global.list[i].pw_name));
785         }
786
787         NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name));
788
789         errno = ENOENT;
790         return NULL;
791 }
792
793 _PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
794                               char *buf, size_t buflen, struct passwd **pwdstp)
795 {
796         struct passwd *pw;
797
798         if (!nwrap_enabled()) {
799                 return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
800         }
801
802         pw = nwrap_getpwnam(name);
803         if (!pw) {
804                 if (errno == 0) {
805                         return ENOENT;
806                 }
807                 return errno;
808         }
809
810         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
811 }
812
813 _PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid)
814 {
815         int i;
816
817         if (!nwrap_enabled()) {
818                 return real_getpwuid(uid);
819         }
820
821         nwrap_cache_reload(nwrap_pw_global.cache);
822
823         for (i=0; i<nwrap_pw_global.num; i++) {
824                 if (nwrap_pw_global.list[i].pw_uid == uid) {
825                         NWRAP_DEBUG(("%s: uid[%u] found\n",
826                                      __location__, uid));
827                         return &nwrap_pw_global.list[i];
828                 }
829                 NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n",
830                                __location__, uid,
831                                nwrap_pw_global.list[i].pw_uid));
832         }
833
834         NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid));
835
836         errno = ENOENT;
837         return NULL;
838 }
839
840 _PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
841                               char *buf, size_t buflen, struct passwd **pwdstp)
842 {
843         struct passwd *pw;
844
845         if (!nwrap_enabled()) {
846                 return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
847         }
848
849         pw = nwrap_getpwuid(uid);
850         if (!pw) {
851                 if (errno == 0) {
852                         return ENOENT;
853                 }
854                 return errno;
855         }
856
857         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
858 }
859
860 /* user enum functions */
861 _PUBLIC_ void nwrap_setpwent(void)
862 {
863         if (!nwrap_enabled()) {
864                 real_setpwent();
865         }
866
867         nwrap_pw_global.idx = 0;
868 }
869
870 _PUBLIC_ struct passwd *nwrap_getpwent(void)
871 {
872         struct passwd *pw;
873
874         if (!nwrap_enabled()) {
875                 return real_getpwent();
876         }
877
878         if (nwrap_pw_global.idx == 0) {
879                 nwrap_cache_reload(nwrap_pw_global.cache);
880         }
881
882         if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
883                 errno = ENOENT;
884                 return NULL;
885         }
886
887         pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
888
889         NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
890                        __location__, pw->pw_name, pw->pw_uid));
891
892         return pw;
893 }
894
895 #ifdef SOLARIS_GETPWENT_R
896 _PUBLIC_ struct passwd *nwrap_getpwent_r(struct passwd *pwdst,
897                                          char *buf,
898                                          int buflen)
899 {
900         struct passwd *pw;
901         struct passwd *pwdstp = NULL;
902         int ret;
903
904         if (!nwrap_enabled()) {
905                 return real_getpwent_r(pwdst, buf, buflen);
906         }
907
908         pw = nwrap_getpwent();
909         if (!pw) {
910                 if (errno == 0) {
911                         errno = ENOENT;
912                 }
913                 return NULL;
914         }
915
916         ret = nwrap_pw_copy_r(pw, pwdst, buf, buflen, &pwdstp);
917         if (ret != 0) {
918                 errno = ret;
919                 return NULL;
920         }
921
922         return pwdstp;
923 }
924 #else
925 _PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
926                               size_t buflen, struct passwd **pwdstp)
927 {
928         struct passwd *pw;
929
930         if (!nwrap_enabled()) {
931                 return real_getpwent_r(pwdst, buf, buflen, pwdstp);
932         }
933
934         pw = nwrap_getpwent();
935         if (!pw) {
936                 if (errno == 0) {
937                         return ENOENT;
938                 }
939                 return errno;
940         }
941
942         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
943 }
944 #endif
945
946 _PUBLIC_ void nwrap_endpwent(void)
947 {
948         if (!nwrap_enabled()) {
949                 real_endpwent();
950         }
951
952         nwrap_pw_global.idx = 0;
953 }
954
955 /* misc functions */
956 _PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
957 {
958         if (!nwrap_enabled()) {
959                 return real_initgroups(user, group);
960         }
961
962         /* TODO: maybe we should also fake this... */
963         return EPERM;
964 }
965
966 /* group functions */
967 _PUBLIC_ struct group *nwrap_getgrnam(const char *name)
968 {
969         int i;
970
971         if (!nwrap_enabled()) {
972                 return real_getgrnam(name);
973         }
974
975         nwrap_cache_reload(nwrap_gr_global.cache);
976
977         for (i=0; i<nwrap_gr_global.num; i++) {
978                 if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
979                         NWRAP_DEBUG(("%s: group[%s] found\n",
980                                      __location__, name));
981                         return &nwrap_gr_global.list[i];
982                 }
983                 NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n",
984                                __location__, name,
985                                nwrap_gr_global.list[i].gr_name));
986         }
987
988         NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name));
989
990         errno = ENOENT;
991         return NULL;
992 }
993
994 _PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst,
995                               char *buf, size_t buflen, struct group **grdstp)
996 {
997         struct group *gr;
998
999         if (!nwrap_enabled()) {
1000                 return real_getgrnam_r(name, grdst, buf, buflen, grdstp);
1001         }
1002
1003         gr = nwrap_getgrnam(name);
1004         if (!gr) {
1005                 if (errno == 0) {
1006                         return ENOENT;
1007                 }
1008                 return errno;
1009         }
1010
1011         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1012 }
1013
1014 _PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
1015 {
1016         int i;
1017
1018         if (!nwrap_enabled()) {
1019                 return real_getgrgid(gid);
1020         }
1021
1022         nwrap_cache_reload(nwrap_gr_global.cache);
1023
1024         for (i=0; i<nwrap_gr_global.num; i++) {
1025                 if (nwrap_gr_global.list[i].gr_gid == gid) {
1026                         NWRAP_DEBUG(("%s: gid[%u] found\n",
1027                                      __location__, gid));
1028                         return &nwrap_gr_global.list[i];
1029                 }
1030                 NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n",
1031                                __location__, gid,
1032                                nwrap_gr_global.list[i].gr_gid));
1033         }
1034
1035         NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid));
1036
1037         errno = ENOENT;
1038         return NULL;
1039 }
1040
1041 _PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
1042                               char *buf, size_t buflen, struct group **grdstp)
1043 {
1044         struct group *gr;
1045
1046         if (!nwrap_enabled()) {
1047                 return real_getgrgid_r(gid, grdst, buf, buflen, grdstp);
1048         }
1049
1050         gr = nwrap_getgrgid(gid);
1051         if (!gr) {
1052                 if (errno == 0) {
1053                         return ENOENT;
1054                 }
1055                 return errno;
1056         }
1057
1058         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1059
1060         return ENOENT;
1061 }
1062
1063 /* group enum functions */
1064 _PUBLIC_ void nwrap_setgrent(void)
1065 {
1066         if (!nwrap_enabled()) {
1067                 real_setgrent();
1068         }
1069
1070         nwrap_gr_global.idx = 0;
1071 }
1072
1073 _PUBLIC_ struct group *nwrap_getgrent(void)
1074 {
1075         struct group *gr;
1076
1077         if (!nwrap_enabled()) {
1078                 return real_getgrent();
1079         }
1080
1081         if (nwrap_gr_global.idx == 0) {
1082                 nwrap_cache_reload(nwrap_gr_global.cache);
1083         }
1084
1085         if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
1086                 errno = ENOENT;
1087                 return NULL;
1088         }
1089
1090         gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
1091
1092         NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n",
1093                        __location__, gr->gr_name, gr->gr_gid));
1094
1095         return gr;
1096 }
1097
1098 #ifdef SOLARIS_GETGRENT_R
1099 _PUBLIC_ struct group *nwrap_getgrent_r(struct group *grdst,
1100                                         char *buf,
1101                                         int buflen)
1102 {
1103         struct group *gr;
1104         struct group *grdstp = NULL;
1105         int ret;
1106
1107         if (!nwrap_enabled()) {
1108                 return real_getgrent_r(grdst, buf, buflen);
1109         }
1110
1111         gr = nwrap_getgrent();
1112         if (!gr) {
1113                 if (errno == 0) {
1114                         errno = ENOENT;
1115                 }
1116                 return NULL;
1117         }
1118
1119         ret = nwrap_gr_copy_r(gr, grdst, buf, buflen, &grdstp);
1120         if (ret != 0) {
1121                 errno = ret;
1122                 return NULL;
1123         }
1124
1125         return grdstp;
1126 }
1127 #else
1128 _PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf,
1129                               size_t buflen, struct group **grdstp)
1130 {
1131         struct group *gr;
1132
1133         if (!nwrap_enabled()) {
1134                 return real_getgrent_r(grdst, buf, buflen, grdstp);
1135         }
1136
1137         gr = nwrap_getgrent();
1138         if (!gr) {
1139                 if (errno == 0) {
1140                         return ENOENT;
1141                 }
1142                 return errno;
1143         }
1144
1145         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1146 }
1147 #endif
1148
1149 _PUBLIC_ void nwrap_endgrent(void)
1150 {
1151         if (!nwrap_enabled()) {
1152                 real_endgrent();
1153         }
1154
1155         nwrap_gr_global.idx = 0;
1156 }