nss_wrapper: add support for passwd accounts
[kai/samba-autobuild/.git] / source / lib / nss_wrapper / nss_wrapper.c
1 /*
2  * Copyright (C) Stefan Metzmacher 2007 <metze@samba.org>
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the author nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #ifdef _SAMBA_BUILD_
35
36 #define NSS_WRAPPER_NOT_REPLACE
37 #include "lib/replace/replace.h"
38 #include "system/passwd.h"
39 #include "system/filesys.h"
40
41 #else /* _SAMBA_BUILD_ */
42
43 #error nss_wrapper_only_supported_in_samba_yet
44
45 #endif
46
47 #ifndef _PUBLIC_
48 #define _PUBLIC_
49 #endif
50
51 /* LD_PRELOAD doesn't work yet, so REWRITE_CALLS is all we support
52  * for now */
53 #define REWRITE_CALLS
54
55 #ifdef REWRITE_CALLS
56
57 #define real_getpwnam           getpwnam
58 #define real_getpwnam_r         getpwnam_r
59 #define real_getpwuid           getpwuid
60 #define real_getpwuid_r         getpwuid_r
61
62 #define real_setpwent           setpwent
63 #define real_getpwent           getpwent
64 #define real_getpwent_r         getpwent_r
65 #define real_endpwent           endpwent
66
67 /*
68 #define real_getgrlst           getgrlst
69 #define real_getgrlst_r         getgrlst_r
70 #define real_initgroups_dyn     initgroups_dyn
71 */
72 #define real_initgroups         initgroups
73
74 #define real_getgrnam           getgrnam
75 #define real_getgrnam_r         getgrnam_r
76 #define real_getgrgid           getgrgid
77 #define real_getgrgid_r         getgrgid_r
78
79 #define real_setgrent           setgrent
80 #define real_getgrent           getgrent
81 #define real_getgrent_r         getgrent_r
82 #define real_endgrent           endgrent
83
84 #endif
85
86 #if 0
87 # ifdef DEBUG
88 # define NWRAP_ERROR(args)      DEBUG(0, args)
89 # else
90 # define NWRAP_ERROR(args)      printf args
91 # endif
92 #else
93 #define NWRAP_ERROR(args)
94 #endif
95
96 #if 0
97 # ifdef DEBUG
98 # define NWRAP_DEBUG(args)      DEBUG(0, args)
99 # else
100 # define NWRAP_DEBUG(args)      printf args
101 # endif
102 #else
103 #define NWRAP_DEBUG(args)
104 #endif
105
106 #if 0
107 # ifdef DEBUG
108 # define NWRAP_VERBOSE(args)    DEBUG(0, args)
109 # else
110 # define NWRAP_VERBOSE(args)    printf args
111 # endif
112 #else
113 #define NWRAP_VERBOSE(args)
114 #endif
115
116 struct nwrap_cache {
117         const char *path;
118         int fd;
119         struct stat st;
120         uint8_t *buf;
121         void *private_data;
122         bool (*parse_line)(struct nwrap_cache *, char *line);
123         void (*unload)(struct nwrap_cache *);
124 };
125
126 struct nwrap_pw {
127         struct nwrap_cache *cache;
128
129         struct passwd *list;
130         int num;
131         int idx;
132 };
133
134 struct nwrap_cache __nwrap_cache_pw;
135 struct nwrap_pw nwrap_pw_global;
136
137 static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line);
138 static void nwrap_pw_unload(struct nwrap_cache *nwrap);
139
140 static void nwrap_init(void)
141 {
142         static bool initialized;
143
144         if (initialized) return;
145         initialized = true;
146
147         nwrap_pw_global.cache = &__nwrap_cache_pw;
148
149         nwrap_pw_global.cache->path = getenv("NSS_WRAPPER_PASSWD");
150         nwrap_pw_global.cache->fd = -1;
151         nwrap_pw_global.cache->private_data = &nwrap_pw_global;
152         nwrap_pw_global.cache->parse_line = nwrap_pw_parse_line;
153         nwrap_pw_global.cache->unload = nwrap_pw_unload;
154 }
155
156 static bool nwrap_enabled(void)
157 {
158         nwrap_init();
159
160         if (!nwrap_pw_global.cache->path) {
161                 return false;
162         }
163         if (nwrap_pw_global.cache->path[0] == '\0') {
164                 return false;
165         }
166
167         return true;
168 }
169
170 static bool nwrap_parse_file(struct nwrap_cache *nwrap)
171 {
172         int ret;
173         uint8_t *buf = NULL;
174         char *nline;
175
176         if (nwrap->st.st_size == 0) {
177                 NWRAP_DEBUG(("%s: size == 0\n",
178                              __location__));
179                 goto done;
180         }
181
182         if (nwrap->st.st_size > INT32_MAX) {
183                 NWRAP_ERROR(("%s: size[%u] larger than INT32_MAX\n",
184                              __location__, (unsigned)nwrap->st.st_size));
185                 goto failed;
186         }
187
188         ret = lseek(nwrap->fd, 0, SEEK_SET);
189         if (ret != 0) {
190                 NWRAP_ERROR(("%s: lseek - %d\n",__location__,ret));
191                 goto failed;
192         }
193
194         buf = malloc(nwrap->st.st_size + 1);
195         if (!buf) {
196                 NWRAP_ERROR(("%s: malloc failed\n",__location__));
197                 goto failed;
198         }
199
200         ret = read(nwrap->fd, buf, nwrap->st.st_size);
201         if (ret != nwrap->st.st_size) {
202                 NWRAP_ERROR(("%s: read(%u) gave %d\n",
203                              __location__, (unsigned)nwrap->st.st_size, ret));
204                 goto failed;
205         }
206
207         buf[nwrap->st.st_size] = '\0';
208
209         nline = (char *)buf;
210         while (nline && nline[0]) {
211                 char *line;
212                 char *e;
213                 bool ok;
214
215                 line = nline;
216                 nline = NULL;
217
218                 e = strchr(line, '\n');
219                 if (e) {
220                         e[0] = '\0';
221                         e++;
222                         if (e[0] == '\r') {
223                                 e[0] = '\0';
224                                 e++;
225                         }
226                         nline = e;
227                 }
228
229                 NWRAP_VERBOSE(("%s:'%s'\n",__location__, line));
230
231                 if (strlen(line) == 0) {
232                         continue;
233                 }
234
235                 ok = nwrap->parse_line(nwrap, line);
236                 if (!ok) {
237                         goto failed;
238                 }
239         }
240
241 done:
242         nwrap->buf = buf;
243         return true;
244
245 failed:
246         if (buf) free(buf);
247         return false;
248 }
249
250 static void nwrap_cache_unload(struct nwrap_cache *nwrap)
251 {
252         nwrap->unload(nwrap);
253
254         if (nwrap->buf) free(nwrap->buf);
255
256         nwrap->buf = NULL;
257 }
258
259 static void nwrap_cache_reload(struct nwrap_cache *nwrap)
260 {
261         struct stat st;
262         int ret;
263         bool ok;
264         bool retried = false;
265
266 reopen:
267         if (nwrap->fd < 0) {
268                 nwrap->fd = open(nwrap->path, O_RDONLY);
269                 if (nwrap->fd < 0) {
270                         NWRAP_ERROR(("%s: unable to open '%s' readonly %d:%s\n",
271                                      __location__,
272                                      nwrap->path, nwrap->fd,
273                                      strerror(errno)));
274                         return;
275                 }
276                 NWRAP_VERBOSE(("%s: open '%s'\n", __location__, nwrap->path));
277         }
278
279         ret = fstat(nwrap->fd, &st);
280         if (ret != 0) {
281                 NWRAP_ERROR(("%s: fstat(%s) - %d:%s\n",
282                              __location__,
283                              nwrap->path,
284                              ret, strerror(errno)));
285                 return;
286         }
287
288         if (retried == false && st.st_nlink == 0) {
289                 /* maybe someone has replaced the file... */
290                 NWRAP_DEBUG(("%s: st_nlink == 0, reopen %s\n",
291                              __location__, nwrap->path));
292                 retried = true;
293                 memset(&nwrap->st, 0, sizeof(nwrap->st));
294                 close(nwrap->fd);
295                 nwrap->fd = -1;
296                 goto reopen;
297         }
298
299         if (st.st_mtime == nwrap->st.st_mtime) {
300                 NWRAP_VERBOSE(("%s: st_mtime[%u] hasn't changed, skip reload\n",
301                                __location__, (unsigned)st.st_mtime));
302                 return;
303         }
304         NWRAP_DEBUG(("%s: st_mtime has changed [%u] => [%u], start reload\n",
305                      __location__, (unsigned)st.st_mtime,
306                      (unsigned)nwrap->st.st_mtime));
307
308         nwrap->st = st;
309
310         nwrap_cache_unload(nwrap);
311
312         ok = nwrap_parse_file(nwrap);
313         if (!ok) {
314                 NWRAP_ERROR(("%s: failed to reload %s\n",
315                              __location__, nwrap->path));
316                 nwrap_cache_unload(nwrap);
317         }
318         NWRAP_DEBUG(("%s: reloaded %s\n",
319                      __location__, nwrap->path));
320 }
321
322 /*
323  * the caller has to call nwrap_unload() on failure
324  */
325 static bool nwrap_pw_parse_line(struct nwrap_cache *nwrap, char *line)
326 {
327         struct nwrap_pw *nwrap_pw;
328         char *c;
329         char *p;
330         char *e;
331         struct passwd *pw;
332         size_t list_size;
333
334         nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
335
336         list_size = sizeof(*nwrap_pw->list) * (nwrap_pw->num+1);
337         pw = (struct passwd *)realloc(nwrap_pw->list, list_size);
338         if (!pw) {
339                 NWRAP_ERROR(("%s:realloc(%u) failed\n",
340                              __location__, list_size));
341                 return false;
342         }
343         nwrap_pw->list = pw;
344
345         pw = &nwrap_pw->list[nwrap_pw->num];
346
347         c = line;
348
349         /* name */
350         p = strchr(c, ':');
351         if (!p) {
352                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
353                              __location__, line, c));
354                 return false;
355         }
356         *p = '\0';
357         p++;
358         pw->pw_name = c;
359         c = p;
360
361         NWRAP_VERBOSE(("name[%s]\n", pw->pw_name));
362
363         /* password */
364         p = strchr(c, ':');
365         if (!p) {
366                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
367                              __location__, line, c));
368                 return false;
369         }
370         *p = '\0';
371         p++;
372         pw->pw_passwd = c;
373         c = p;
374
375         NWRAP_VERBOSE(("password[%s]\n", pw->pw_passwd));
376
377         /* uid */
378         p = strchr(c, ':');
379         if (!p) {
380                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
381                              __location__, line, c));
382                 return false;
383         }
384         *p = '\0';
385         p++;
386         e = NULL;
387         pw->pw_uid = (uid_t)strtoul(c, &e, 10);
388         if (c == e) {
389                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
390                              __location__, line, c, strerror(errno)));
391                 return false;
392         }
393         if (e == NULL) {
394                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
395                              __location__, line, c, strerror(errno)));
396                 return false;
397         }
398         if (e[0] != '\0') {
399                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
400                              __location__, line, c, strerror(errno)));
401                 return false;
402         }
403         c = p;
404
405         NWRAP_VERBOSE(("uid[%u]\n", pw->pw_uid));
406
407         /* gid */
408         p = strchr(c, ':');
409         if (!p) {
410                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
411                              __location__, line, c));
412                 return false;
413         }
414         *p = '\0';
415         p++;
416         e = NULL;
417         pw->pw_gid = (gid_t)strtoul(c, &e, 10);
418         if (c == e) {
419                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
420                              __location__, line, c, strerror(errno)));
421                 return false;
422         }
423         if (e == NULL) {
424                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
425                              __location__, line, c, strerror(errno)));
426                 return false;
427         }
428         if (e[0] != '\0') {
429                 NWRAP_ERROR(("%s:invalid line[%s]: '%s' - %s\n",
430                              __location__, line, c, strerror(errno)));
431                 return false;
432         }
433         c = p;
434
435         NWRAP_VERBOSE(("gid[%u]\n", pw->pw_gid));
436
437         /* gecos */
438         p = strchr(c, ':');
439         if (!p) {
440                 NWRAP_ERROR(("%s:invalid line[%s]: '%s'\n",
441                              __location__, line, c));
442                 return false;
443         }
444         *p = '\0';
445         p++;
446         pw->pw_gecos = c;
447         c = p;
448
449         NWRAP_VERBOSE(("gecos[%s]\n", pw->pw_gecos));
450
451         /* dir */
452         p = strchr(c, ':');
453         if (!p) {
454                 NWRAP_ERROR(("%s:'%s'\n",__location__,c));
455                 return false;
456         }
457         *p = '\0';
458         p++;
459         pw->pw_dir = c;
460         c = p;
461
462         NWRAP_VERBOSE(("dir[%s]\n", pw->pw_dir));
463
464         /* shell */
465         pw->pw_shell = c;
466         NWRAP_VERBOSE(("shell[%s]\n", pw->pw_shell));
467
468         NWRAP_DEBUG(("add user[%s:%s:%u:%u:%s:%s:%s]\n",
469                      pw->pw_name, pw->pw_passwd,
470                      pw->pw_uid, pw->pw_gid,
471                      pw->pw_gecos, pw->pw_dir, pw->pw_shell));
472
473         nwrap_pw->num++;
474         return true;
475 }
476
477 static void nwrap_pw_unload(struct nwrap_cache *nwrap)
478 {
479         struct nwrap_pw *nwrap_pw;
480         nwrap_pw = (struct nwrap_pw *)nwrap->private_data;
481
482         if (nwrap_pw->list) free(nwrap_pw->list);
483
484         nwrap_pw->list = NULL;
485         nwrap_pw->num = 0;
486         nwrap_pw->idx = 0;
487 }
488
489 static int nwrap_pw_copy_r(const struct passwd *src, struct passwd *dst,
490                            char *buf, size_t buflen, struct passwd **destp)
491 {
492         char *first;
493         char *last;
494         off_t ofs;
495
496         first = src->pw_name;
497
498         last = src->pw_shell;
499         while (*last) last++;
500
501         ofs = PTR_DIFF(last + 1, first);
502
503         if (ofs > buflen) {
504                 return ERANGE;
505         }
506
507         memcpy(buf, first, ofs);
508
509         ofs = PTR_DIFF(src->pw_name, first);
510         dst->pw_name = buf + ofs;
511         ofs = PTR_DIFF(src->pw_passwd, first);
512         dst->pw_passwd = buf + ofs;
513         dst->pw_uid = src->pw_uid;
514         dst->pw_gid = src->pw_gid;
515         ofs = PTR_DIFF(src->pw_gecos, first);
516         dst->pw_gecos = buf + ofs;
517         ofs = PTR_DIFF(src->pw_dir, first);
518         dst->pw_dir = buf + ofs;
519         ofs = PTR_DIFF(src->pw_shell, first);
520         dst->pw_shell = buf + ofs;
521
522         return 0;
523 }
524
525 /* user functions */
526 _PUBLIC_ struct passwd *nwrap_getpwnam(const char *name)
527 {
528         int i;
529
530         if (!nwrap_enabled()) {
531                 return real_getpwnam(name);
532         }
533
534         nwrap_cache_reload(nwrap_pw_global.cache);
535
536         for (i=0; i<nwrap_pw_global.num; i++) {
537                 if (strcmp(nwrap_pw_global.list[i].pw_name, name) == 0) {
538                         NWRAP_DEBUG(("%s: user[%s] found\n",
539                                      __location__, name));
540                         return &nwrap_pw_global.list[i];
541                 }
542                 NWRAP_VERBOSE(("%s: user[%s] does not match [%s]\n",
543                                __location__, name,
544                                nwrap_pw_global.list[i].pw_name));
545         }
546
547         NWRAP_DEBUG(("%s: user[%s] not found\n", __location__, name));
548
549         errno = ENOENT;
550         return NULL;
551 }
552
553 _PUBLIC_ int nwrap_getpwnam_r(const char *name, struct passwd *pwdst,
554                               char *buf, size_t buflen, struct passwd **pwdstp)
555 {
556         struct passwd *pw;
557
558         if (!nwrap_enabled()) {
559                 return real_getpwnam_r(name, pwdst, buf, buflen, pwdstp);
560         }
561
562         pw = nwrap_getpwnam(name);
563         if (!pw) {
564                 if (errno == 0) {
565                         return ENOENT;
566                 }
567                 return errno;
568         }
569
570         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
571 }
572
573 _PUBLIC_ struct passwd *nwrap_getpwuid(uid_t uid)
574 {
575         int i;
576
577         if (!nwrap_enabled()) {
578                 return real_getpwuid(uid);
579         }
580
581         nwrap_cache_reload(nwrap_pw_global.cache);
582
583         for (i=0; i<nwrap_pw_global.num; i++) {
584                 if (nwrap_pw_global.list[i].pw_uid == uid) {
585                         NWRAP_DEBUG(("%s: uid[%u] found\n",
586                                      __location__, uid));
587                         return &nwrap_pw_global.list[i];
588                 }
589                 NWRAP_VERBOSE(("%s: uid[%u] does not match [%u]\n",
590                                __location__, uid,
591                                nwrap_pw_global.list[i].pw_uid));
592         }
593
594         NWRAP_DEBUG(("%s: uid[%u] not found\n", __location__, uid));
595
596         errno = ENOENT;
597         return NULL;
598 }
599
600 _PUBLIC_ int nwrap_getpwuid_r(uid_t uid, struct passwd *pwdst,
601                               char *buf, size_t buflen, struct passwd **pwdstp)
602 {
603         struct passwd *pw;
604
605         if (!nwrap_enabled()) {
606                 return real_getpwuid_r(uid, pwdst, buf, buflen, pwdstp);
607         }
608
609         pw = nwrap_getpwuid(uid);
610         if (!pw) {
611                 if (errno == 0) {
612                         return ENOENT;
613                 }
614                 return errno;
615         }
616
617         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
618 }
619
620 /* user enum functions */
621 _PUBLIC_ void nwrap_setpwent(void)
622 {
623         if (!nwrap_enabled()) {
624                 real_setpwent();
625         }
626
627         nwrap_pw_global.idx = 0;
628 }
629
630 _PUBLIC_ struct passwd *nwrap_getpwent(void)
631 {
632         struct passwd *pw;
633
634         if (!nwrap_enabled()) {
635                 return real_getpwent();
636         }
637
638         if (nwrap_pw_global.idx == 0) {
639                 nwrap_cache_reload(nwrap_pw_global.cache);
640         }
641
642         if (nwrap_pw_global.idx >= nwrap_pw_global.num) {
643                 errno = ENOENT;
644                 return NULL;
645         }
646
647         pw = &nwrap_pw_global.list[nwrap_pw_global.idx++];
648
649         NWRAP_VERBOSE(("%s: return user[%s] uid[%u]\n",
650                        __location__, pw->pw_name, pw->pw_uid));
651
652         return pw;
653 }
654
655 _PUBLIC_ int nwrap_getpwent_r(struct passwd *pwdst, char *buf,
656                               size_t buflen, struct passwd **pwdstp)
657 {
658         struct passwd *pw;
659
660         if (!nwrap_enabled()) {
661                 return real_getpwent_r(pwdst, buf, buflen, pwdstp);
662         }
663
664         pw = nwrap_getpwent();
665         if (!pw) {
666                 if (errno == 0) {
667                         return ENOENT;
668                 }
669                 return errno;
670         }
671
672         return nwrap_pw_copy_r(pw, pwdst, buf, buflen, pwdstp);
673 }
674
675 _PUBLIC_ void nwrap_endpwent(void)
676 {
677         if (!nwrap_enabled()) {
678                 real_endpwent();
679         }
680
681         nwrap_pw_global.idx = 0;
682 }
683
684 /* misc functions */
685 _PUBLIC_ int nwrap_initgroups(const char *user, gid_t group)
686 {
687         return real_initgroups(user, group);
688 }
689
690 /* group functions */
691 _PUBLIC_ struct group *nwrap_getgrnam(const char *name)
692 {
693         return real_getgrnam(name);
694 }
695
696 _PUBLIC_ int nwrap_getgrnam_r(const char *name, struct group *gbuf,
697                               char *buf, size_t buflen, struct group **gbufp)
698 {
699         return real_getgrnam_r(name, gbuf, buf, buflen, gbufp);
700 }
701
702 _PUBLIC_ struct group *nwrap_getgrgid(gid_t gid)
703 {
704         return real_getgrgid(gid);
705 }
706
707 _PUBLIC_ int nwrap_getgrgid_r(gid_t gid, struct group *gbuf,
708                               char *buf, size_t buflen, struct group **gbufp)
709 {
710         return real_getgrgid_r(gid, gbuf, buf, buflen, gbufp);
711 }
712
713 /* group enum functions */
714 _PUBLIC_ void nwrap_setgrent(void)
715 {
716         real_setgrent();
717 }
718
719 _PUBLIC_ struct group *nwrap_getgrent(void)
720 {
721         return real_getgrent();
722 }
723
724 _PUBLIC_ int nwrap_getgrent_r(struct group *gbuf, char *buf,
725                               size_t buflen, struct group **gbufp)
726 {
727         return real_getgrent_r(gbuf, buf, buflen, gbufp);
728 }
729
730 _PUBLIC_ void nwrap_endgrent(void)
731 {
732         real_endgrent();
733 }