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