Merge branch 'master' of ssh://git.samba.org/data/git/samba into noejs
[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 _PUBLIC_ struct passwd *nwrap_getpwnam(const char *name)
775 {
776         int i;
777
778         if (!nwrap_enabled()) {
779                 return real_getpwnam(name);
780         }
781
782         nwrap_cache_reload(nwrap_pw_global.cache);
783
784         for (i=0; i<nwrap_pw_global.num; i++) {
785                 if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
786                         NWRAP_DEBUG(("%s: user[%s] found\n",
787                                      __location__, name));
788                         return &nwrap_pw_global.list[i];
789                 }
790                 NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n",
791                                __location__, name,
792                                nwrap_pw_global.list[i].pw_name));
793         }
794
795         NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name));
796
797         errno = ENOENT;
798         return NULL;
799 }
800
801 _PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
802                               char *buf, size_t buflen, struct passwd **pwdstp)
803 {
804         struct passwd *pw;
805
806         if (!nwrap_enabled()) {
807                 return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
808         }
809
810         pw = nwrap_getpwnam(name);
811         if (!pw) {
812                 if (errno == 0) {
813                         return ENOENT;
814                 }
815                 return errno;
816         }
817
818         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
819 }
820
821 _PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid)
822 {
823         int i;
824
825         if (!nwrap_enabled()) {
826                 return real_getpwuid(uid);
827         }
828
829         nwrap_cache_reload(nwrap_pw_global.cache);
830
831         for (i=0; i<nwrap_pw_global.num; i++) {
832                 if (nwrap_pw_global.list[i].pw_uid == uid) {
833                         NWRAP_DEBUG(("%s: uid[%u] found\n",
834                                      __location__, uid));
835                         return &nwrap_pw_global.list[i];
836                 }
837                 NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n",
838                                __location__, uid,
839                                nwrap_pw_global.list[i].pw_uid));
840         }
841
842         NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid));
843
844         errno = ENOENT;
845         return NULL;
846 }
847
848 _PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
849                               char *buf, size_t buflen, struct passwd **pwdstp)
850 {
851         struct passwd *pw;
852
853         if (!nwrap_enabled()) {
854                 return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
855         }
856
857         pw = nwrap_getpwuid(uid);
858         if (!pw) {
859                 if (errno == 0) {
860                         return ENOENT;
861                 }
862                 return errno;
863         }
864
865         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
866 }
867
868 /* user enum functions */
869 _PUBLIC_ void nwrap_setpwent(void)
870 {
871         if (!nwrap_enabled()) {
872                 real_setpwent();
873         }
874
875         nwrap_pw_global.idx = 0;
876 }
877
878 _PUBLIC_ struct passwd *nwrap_getpwent(void)
879 {
880         struct passwd *pw;
881
882         if (!nwrap_enabled()) {
883                 return real_getpwent();
884         }
885
886         if (nwrap_pw_global.idx == 0) {
887                 nwrap_cache_reload(nwrap_pw_global.cache);
888         }
889
890         if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
891                 errno = ENOENT;
892                 return NULL;
893         }
894
895         pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
896
897         NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
898                        __location__, pw->pw_name, pw->pw_uid));
899
900         return pw;
901 }
902
903 _PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
904                               size_t buflen, struct passwd **pwdstp)
905 {
906         struct passwd *pw;
907
908         if (!nwrap_enabled()) {
909 #ifdef SOLARIS_GETPWENT_R
910                 pw = real_getpwent_r(pwdst, buf, buflen);
911                 if (!pw) {
912                         if (errno == 0) {
913                                 return ENOENT;
914                         }
915                         return errno;
916                 }
917                 if (pwdstp) {
918                         *pwdstp = pw;
919                 }
920                 return 0;
921 #else
922                 return real_getpwent_r(pwdst, buf, buflen, pwdstp);
923 #endif
924         }
925
926         pw = nwrap_getpwent();
927         if (!pw) {
928                 if (errno == 0) {
929                         return ENOENT;
930                 }
931                 return errno;
932         }
933
934         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
935 }
936
937 _PUBLIC_ void nwrap_endpwent(void)
938 {
939         if (!nwrap_enabled()) {
940                 real_endpwent();
941         }
942
943         nwrap_pw_global.idx = 0;
944 }
945
946 /* misc functions */
947 _PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
948 {
949         if (!nwrap_enabled()) {
950                 return real_initgroups(user, group);
951         }
952
953         /* TODO: maybe we should also fake this... */
954         return EPERM;
955 }
956
957 /* group functions */
958 _PUBLIC_ struct group *nwrap_getgrnam(const char *name)
959 {
960         int i;
961
962         if (!nwrap_enabled()) {
963                 return real_getgrnam(name);
964         }
965
966         nwrap_cache_reload(nwrap_gr_global.cache);
967
968         for (i=0; i<nwrap_gr_global.num; i++) {
969                 if (strcmp(nwrap_gr_global.list[i].gr_name, name) == 0) {
970                         NWRAP_DEBUG(("%s: group[%s] found\n",
971                                      __location__, name));
972                         return &nwrap_gr_global.list[i];
973                 }
974                 NWRAP_VERBOSE(("%s: group[%s] does not match [%s]\n",
975                                __location__, name,
976                                nwrap_gr_global.list[i].gr_name));
977         }
978
979         NWRAP_DEBUG(("%s: group[%s] not found\n", __location__, name));
980
981         errno = ENOENT;
982         return NULL;
983 }
984
985 _PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *grdst,
986                               char *buf, size_t buflen, struct group **grdstp)
987 {
988         struct group *gr;
989
990         if (!nwrap_enabled()) {
991                 return real_getgrnam_r(name, grdst, buf, buflen, grdstp);
992         }
993
994         gr = nwrap_getgrnam(name);
995         if (!gr) {
996                 if (errno == 0) {
997                         return ENOENT;
998                 }
999                 return errno;
1000         }
1001
1002         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1003 }
1004
1005 _PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
1006 {
1007         int i;
1008
1009         if (!nwrap_enabled()) {
1010                 return real_getgrgid(gid);
1011         }
1012
1013         nwrap_cache_reload(nwrap_gr_global.cache);
1014
1015         for (i=0; i<nwrap_gr_global.num; i++) {
1016                 if (nwrap_gr_global.list[i].gr_gid == gid) {
1017                         NWRAP_DEBUG(("%s: gid[%u] found\n",
1018                                      __location__, gid));
1019                         return &nwrap_gr_global.list[i];
1020                 }
1021                 NWRAP_VERBOSE(("%s: gid[%u] does not match [%u]\n",
1022                                __location__, gid,
1023                                nwrap_gr_global.list[i].gr_gid));
1024         }
1025
1026         NWRAP_DEBUG(("%s: gid[%u] not found\n", __location__, gid));
1027
1028         errno = ENOENT;
1029         return NULL;
1030 }
1031
1032 _PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *grdst,
1033                               char *buf, size_t buflen, struct group **grdstp)
1034 {
1035         struct group *gr;
1036
1037         if (!nwrap_enabled()) {
1038                 return real_getgrgid_r(gid, grdst, buf, buflen, grdstp);
1039         }
1040
1041         gr = nwrap_getgrgid(gid);
1042         if (!gr) {
1043                 if (errno == 0) {
1044                         return ENOENT;
1045                 }
1046                 return errno;
1047         }
1048
1049         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1050
1051         return ENOENT;
1052 }
1053
1054 /* group enum functions */
1055 _PUBLIC_ void nwrap_setgrent(void)
1056 {
1057         if (!nwrap_enabled()) {
1058                 real_setgrent();
1059         }
1060
1061         nwrap_gr_global.idx = 0;
1062 }
1063
1064 _PUBLIC_ struct group *nwrap_getgrent(void)
1065 {
1066         struct group *gr;
1067
1068         if (!nwrap_enabled()) {
1069                 return real_getgrent();
1070         }
1071
1072         if (nwrap_gr_global.idx == 0) {
1073                 nwrap_cache_reload(nwrap_gr_global.cache);
1074         }
1075
1076         if (nwrap_gr_global.idx >= nwrap_gr_global.num) {
1077                 errno = ENOENT;
1078                 return NULL;
1079         }
1080
1081         gr = &nwrap_gr_global.list[nwrap_gr_global.idx++];
1082
1083         NWRAP_VERBOSE(("%s: return group[%s] gid[%u]\n",
1084                        __location__, gr->gr_name, gr->gr_gid));
1085
1086         return gr;
1087 }
1088
1089 _PUBLIC_ int nwrap_getgrent_r(struct group *grdst, char *buf,
1090                               size_t buflen, struct group **grdstp)
1091 {
1092         struct group *gr;
1093
1094         if (!nwrap_enabled()) {
1095 #ifdef SOLARIS_GETGRENT_R
1096                 gr = real_getgrent_r(grdst, buf, buflen);
1097                 if (!gr) {
1098                         if (errno == 0) {
1099                                 return ENOENT;
1100                         }
1101                         return errno;
1102                 }
1103                 if (grdstp) {
1104                         *grdstp = gr;
1105                 }
1106                 return 0;
1107 #else
1108                 return real_getgrent_r(grdst, buf, buflen, grdstp);
1109 #endif
1110         }
1111
1112         gr = nwrap_getgrent();
1113         if (!gr) {
1114                 if (errno == 0) {
1115                         return ENOENT;
1116                 }
1117                 return errno;
1118         }
1119
1120         return nwrap_gr_copy_r(gr, grdst, buf, buflen, grdstp);
1121 }
1122
1123 _PUBLIC_ void nwrap_endgrent(void)
1124 {
1125         if (!nwrap_enabled()) {
1126                 real_endgrent();
1127         }
1128
1129         nwrap_gr_global.idx = 0;
1130 }