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