]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/gen/getpwent.c
acpica: Create merge commit against vendor branch
[FreeBSD/FreeBSD.git] / lib / libc / gen / getpwent.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2003 Networks Associates Technology, Inc.
5  * All rights reserved.
6  *
7  * This software was developed for the FreeBSD Project by
8  * Jacques A. Vidrine, Safeport Network Services, and Network
9  * Associates Laboratories, the Security Research Division of Network
10  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
11  * ("CBOSS"), as part of the DARPA CHATS research program.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  */
35 #include "namespace.h"
36 #include <sys/param.h>
37 #ifdef YP
38 #include <rpc/rpc.h>
39 #include <rpcsvc/yp_prot.h>
40 #include <rpcsvc/ypclnt.h>
41 #endif
42 #include <arpa/inet.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #ifdef HESIOD
46 #include <hesiod.h>
47 #endif
48 #include <netdb.h>
49 #include <nsswitch.h>
50 #include <pthread.h>
51 #include <pthread_np.h>
52 #include <pwd.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <string.h>
56 #include <syslog.h>
57 #include <unistd.h>
58 #include "un-namespace.h"
59 #include <db.h>
60 #include "libc_private.h"
61 #include "pw_scan.h"
62 #include "nss_tls.h"
63 #ifdef NS_CACHING
64 #include "nscache.h"
65 #endif
66
67 #ifndef CTASSERT
68 #define CTASSERT(x)             _CTASSERT(x, __LINE__)
69 #define _CTASSERT(x, y)         __CTASSERT(x, y)
70 #define __CTASSERT(x, y)        typedef char __assert_ ## y [(x) ? 1 : -1]
71 #endif
72
73 /* Counter as stored in /etc/pwd.db */
74 typedef int             pwkeynum;
75
76 CTASSERT(MAXLOGNAME > sizeof(uid_t));
77 CTASSERT(MAXLOGNAME > sizeof(pwkeynum));
78
79 enum constants {
80         PWD_STORAGE_INITIAL     = 1 << 10, /* 1 KByte */
81         PWD_STORAGE_MAX         = 1 << 20, /* 1 MByte */
82         SETPWENT                = 1,
83         ENDPWENT                = 2,
84         HESIOD_NAME_MAX         = 256
85 };
86
87 static const ns_src defaultsrc[] = {
88         { NSSRC_COMPAT, NS_SUCCESS },
89         { NULL, 0 }
90 };
91
92 int     __pw_match_entry(const char *, size_t, enum nss_lookup_type,
93             const char *, uid_t);
94 int     __pw_parse_entry(char *, size_t, struct passwd *, int, int *errnop);
95
96 union key {
97         const char      *name;
98         uid_t            uid;
99 };
100
101 static  struct passwd *getpw(int (*fn)(union key, struct passwd *, char *,
102                     size_t, struct passwd **), union key);
103 static  int      wrap_getpwnam_r(union key, struct passwd *, char *,
104                     size_t, struct passwd **);
105 static  int      wrap_getpwuid_r(union key, struct passwd *, char *, size_t,
106                     struct passwd **);
107 static  int      wrap_getpwent_r(union key, struct passwd *, char *, size_t,
108                     struct passwd **);
109
110 static  int      pwdb_match_entry_v3(char *, size_t, enum nss_lookup_type,
111                     const char *, uid_t);
112 static  int      pwdb_parse_entry_v3(char *, size_t, struct passwd *, int *);
113 static  int      pwdb_match_entry_v4(char *, size_t, enum nss_lookup_type,
114                     const char *, uid_t);
115 static  int      pwdb_parse_entry_v4(char *, size_t, struct passwd *, int *);
116
117
118 struct {
119         int     (*match)(char *, size_t, enum nss_lookup_type, const char *,
120                     uid_t);
121         int     (*parse)(char *, size_t, struct passwd *, int *);
122 } pwdb_versions[] = {
123         { NULL, NULL },                                 /* version 0 */
124         { NULL, NULL },                                 /* version 1 */
125         { NULL, NULL },                                 /* version 2 */
126         { pwdb_match_entry_v3, pwdb_parse_entry_v3 },   /* version 3 */
127         { pwdb_match_entry_v4, pwdb_parse_entry_v4 },   /* version 4 */
128 };
129
130
131 struct files_state {
132         DB              *db;
133         pwkeynum         keynum;
134         int              stayopen;
135         int              version;
136 };
137 static  void    files_endstate(void *);
138 NSS_TLS_HANDLING(files);
139 static  DB      *pwdbopen(int *);
140 static  void     files_endstate(void *);
141 static  int      files_setpwent(void *, void *, va_list);
142 static  int      files_passwd(void *, void *, va_list);
143
144
145 #ifdef HESIOD
146 struct dns_state {
147         long    counter;
148 };
149 static  void    dns_endstate(void *);
150 NSS_TLS_HANDLING(dns);
151 static  int      dns_setpwent(void *, void *, va_list);
152 static  int      dns_passwd(void *, void *, va_list);
153 #endif
154
155
156 #ifdef YP
157 struct nis_state {
158         char     domain[MAXHOSTNAMELEN];
159         int      done;
160         char    *key;
161         int      keylen;
162 };
163 static  void     nis_endstate(void *);
164 NSS_TLS_HANDLING(nis);
165 static  int      nis_setpwent(void *, void *, va_list);
166 static  int      nis_passwd(void *, void *, va_list);
167 static  int      nis_map(char *, enum nss_lookup_type, char *, size_t, int *);
168 static  int      nis_adjunct(char *, const char *, char *, size_t);
169 #endif
170
171
172 struct compat_state {
173         DB              *db;
174         pwkeynum         keynum;
175         int              stayopen;
176         int              version;
177         DB              *exclude;
178         struct passwd    template;
179         char            *name;
180         enum _compat {
181                 COMPAT_MODE_OFF = 0,
182                 COMPAT_MODE_ALL,
183                 COMPAT_MODE_NAME,
184                 COMPAT_MODE_NETGROUP
185         }                compat;
186 };
187 static  void     compat_endstate(void *);
188 NSS_TLS_HANDLING(compat);
189 static  int      compat_setpwent(void *, void *, va_list);
190 static  int      compat_passwd(void *, void *, va_list);
191 static  void     compat_clear_template(struct passwd *);
192 static  int      compat_set_template(struct passwd *, struct passwd *);
193 static  int      compat_use_template(struct passwd *, struct passwd *, char *,
194                     size_t);
195 static  int      compat_redispatch(struct compat_state *, enum nss_lookup_type,
196                     enum nss_lookup_type, const char *, const char *, uid_t,
197                     struct passwd *, char *, size_t, int *);
198
199 #ifdef NS_CACHING
200 static  int      pwd_id_func(char *, size_t *, va_list ap, void *);
201 static  int      pwd_marshal_func(char *, size_t *, void *, va_list, void *);
202 static  int      pwd_unmarshal_func(char *, size_t, void *, va_list, void *);
203
204 static int
205 pwd_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
206 {
207         char    *name;
208         uid_t   uid;
209         size_t  size, desired_size;
210         int     res = NS_UNAVAIL;
211         enum nss_lookup_type lookup_type;
212
213         lookup_type = (enum nss_lookup_type)(uintptr_t)cache_mdata;
214         switch (lookup_type) {
215         case nss_lt_name:
216                 name = va_arg(ap, char *);
217                 size = strlen(name);
218                 desired_size = sizeof(enum nss_lookup_type) + size + 1;
219                 if (desired_size > *buffer_size) {
220                         res = NS_RETURN;
221                         goto fin;
222                 }
223
224                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
225                 memcpy(buffer + sizeof(enum nss_lookup_type), name, size + 1);
226
227                 res = NS_SUCCESS;
228                 break;
229         case nss_lt_id:
230                 uid = va_arg(ap, uid_t);
231                 desired_size = sizeof(enum nss_lookup_type) + sizeof(uid_t);
232                 if (desired_size > *buffer_size) {
233                         res = NS_RETURN;
234                         goto fin;
235                 }
236
237                 memcpy(buffer, &lookup_type, sizeof(enum nss_lookup_type));
238                 memcpy(buffer + sizeof(enum nss_lookup_type), &uid,
239                     sizeof(uid_t));
240
241                 res = NS_SUCCESS;
242                 break;
243         default:
244                 /* should be unreachable */
245                 return (NS_UNAVAIL);
246         }
247
248 fin:
249         *buffer_size = desired_size;
250         return (res);
251 }
252
253 static int
254 pwd_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
255     void *cache_mdata)
256 {
257         char *name __unused;
258         uid_t uid __unused;
259         struct passwd *pwd;
260         char *orig_buf __unused;
261         size_t orig_buf_size __unused;
262
263         struct passwd new_pwd;
264         size_t desired_size, size;
265         char *p;
266
267         switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) {
268         case nss_lt_name:
269                 name = va_arg(ap, char *);
270                 break;
271         case nss_lt_id:
272                 uid = va_arg(ap, uid_t);
273                 break;
274         case nss_lt_all:
275                 break;
276         default:
277                 /* should be unreachable */
278                 return (NS_UNAVAIL);
279         }
280
281         pwd = va_arg(ap, struct passwd *);
282         orig_buf = va_arg(ap, char *);
283         orig_buf_size = va_arg(ap, size_t);
284
285         desired_size = sizeof(struct passwd) + sizeof(char *) +
286             strlen(pwd->pw_name) + 1;
287         if (pwd->pw_passwd != NULL)
288                 desired_size += strlen(pwd->pw_passwd) + 1;
289         if (pwd->pw_class != NULL)
290                 desired_size += strlen(pwd->pw_class) + 1;
291         if (pwd->pw_gecos != NULL)
292                 desired_size += strlen(pwd->pw_gecos) + 1;
293         if (pwd->pw_dir != NULL)
294                 desired_size += strlen(pwd->pw_dir) + 1;
295         if (pwd->pw_shell != NULL)
296                 desired_size += strlen(pwd->pw_shell) + 1;
297
298         if (*buffer_size < desired_size) {
299                 /* this assignment is here for future use */
300                 *buffer_size = desired_size;
301                 return (NS_RETURN);
302         }
303
304         memcpy(&new_pwd, pwd, sizeof(struct passwd));
305         memset(buffer, 0, desired_size);
306
307         *buffer_size = desired_size;
308         p = buffer + sizeof(struct passwd) + sizeof(char *);
309         memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
310
311         if (new_pwd.pw_name != NULL) {
312                 size = strlen(new_pwd.pw_name);
313                 memcpy(p, new_pwd.pw_name, size);
314                 new_pwd.pw_name = p;
315                 p += size + 1;
316         }
317
318         if (new_pwd.pw_passwd != NULL) {
319                 size = strlen(new_pwd.pw_passwd);
320                 memcpy(p, new_pwd.pw_passwd, size);
321                 new_pwd.pw_passwd = p;
322                 p += size + 1;
323         }
324
325         if (new_pwd.pw_class != NULL) {
326                 size = strlen(new_pwd.pw_class);
327                 memcpy(p, new_pwd.pw_class, size);
328                 new_pwd.pw_class = p;
329                 p += size + 1;
330         }
331
332         if (new_pwd.pw_gecos != NULL) {
333                 size = strlen(new_pwd.pw_gecos);
334                 memcpy(p, new_pwd.pw_gecos, size);
335                 new_pwd.pw_gecos = p;
336                 p += size + 1;
337         }
338
339         if (new_pwd.pw_dir != NULL) {
340                 size = strlen(new_pwd.pw_dir);
341                 memcpy(p, new_pwd.pw_dir, size);
342                 new_pwd.pw_dir = p;
343                 p += size + 1;
344         }
345
346         if (new_pwd.pw_shell != NULL) {
347                 size = strlen(new_pwd.pw_shell);
348                 memcpy(p, new_pwd.pw_shell, size);
349                 new_pwd.pw_shell = p;
350                 p += size + 1;
351         }
352
353         memcpy(buffer, &new_pwd, sizeof(struct passwd));
354         return (NS_SUCCESS);
355 }
356
357 static int
358 pwd_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
359     void *cache_mdata)
360 {
361         char *name __unused;
362         uid_t uid __unused;
363         struct passwd *pwd;
364         char *orig_buf;
365         size_t orig_buf_size;
366         int *ret_errno;
367
368         char *p;
369
370         switch ((enum nss_lookup_type)(uintptr_t)cache_mdata) {
371         case nss_lt_name:
372                 name = va_arg(ap, char *);
373                 break;
374         case nss_lt_id:
375                 uid = va_arg(ap, uid_t);
376                 break;
377         case nss_lt_all:
378                 break;
379         default:
380                 /* should be unreachable */
381                 return (NS_UNAVAIL);
382         }
383
384         pwd = va_arg(ap, struct passwd *);
385         orig_buf = va_arg(ap, char *);
386         orig_buf_size = va_arg(ap, size_t);
387         ret_errno = va_arg(ap, int *);
388
389         if (orig_buf_size + sizeof(struct passwd) + sizeof(char *) <
390             buffer_size) {
391                 *ret_errno = ERANGE;
392                 return (NS_RETURN);
393         } else if (buffer_size < sizeof(struct passwd) + sizeof(char *)) {
394                 /*
395                  * nscd(8) sometimes returns buffer_size=1 for nonexistent
396                  * entries.
397                  */
398                 *ret_errno = 0;
399                 return (NS_NOTFOUND);
400         }
401
402         memcpy(pwd, buffer, sizeof(struct passwd));
403         memcpy(&p, buffer + sizeof(struct passwd), sizeof(char *));
404         memcpy(orig_buf, buffer + sizeof(struct passwd) + sizeof(char *),
405             buffer_size - sizeof(struct passwd) - sizeof(char *));
406
407         NS_APPLY_OFFSET(pwd->pw_name, orig_buf, p, char *);
408         NS_APPLY_OFFSET(pwd->pw_passwd, orig_buf, p, char *);
409         NS_APPLY_OFFSET(pwd->pw_class, orig_buf, p, char *);
410         NS_APPLY_OFFSET(pwd->pw_gecos, orig_buf, p, char *);
411         NS_APPLY_OFFSET(pwd->pw_dir, orig_buf, p, char *);
412         NS_APPLY_OFFSET(pwd->pw_shell, orig_buf, p, char *);
413
414         if (retval != NULL)
415                 *((struct passwd **)retval) = pwd;
416
417         return (NS_SUCCESS);
418 }
419
420 NSS_MP_CACHE_HANDLING(passwd);
421 #endif /* NS_CACHING */
422
423 void
424 setpwent(void)
425 {
426 #ifdef NS_CACHING
427         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
428                 passwd, (void *)nss_lt_all,
429                 NULL, NULL);
430 #endif
431
432         static const ns_dtab dtab[] = {
433                 { NSSRC_FILES, files_setpwent, (void *)SETPWENT },
434 #ifdef HESIOD
435                 { NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
436 #endif
437 #ifdef YP
438                 { NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
439 #endif
440                 { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
441 #ifdef NS_CACHING
442                 NS_CACHE_CB(&cache_info)
443 #endif
444                 { NULL, NULL, NULL }
445         };
446         (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc, 0);
447 }
448
449
450 int
451 setpassent(int stayopen)
452 {
453 #ifdef NS_CACHING
454         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
455                 passwd, (void *)nss_lt_all,
456                 NULL, NULL);
457 #endif
458
459         static const ns_dtab dtab[] = {
460                 { NSSRC_FILES, files_setpwent, (void *)SETPWENT },
461 #ifdef HESIOD
462                 { NSSRC_DNS, dns_setpwent, (void *)SETPWENT },
463 #endif
464 #ifdef YP
465                 { NSSRC_NIS, nis_setpwent, (void *)SETPWENT },
466 #endif
467                 { NSSRC_COMPAT, compat_setpwent, (void *)SETPWENT },
468 #ifdef NS_CACHING
469                 NS_CACHE_CB(&cache_info)
470 #endif
471                 { NULL, NULL, NULL }
472         };
473         (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "setpwent", defaultsrc,
474             stayopen);
475         return (1);
476 }
477
478
479 void
480 endpwent(void)
481 {
482 #ifdef NS_CACHING
483         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
484                 passwd, (void *)nss_lt_all,
485                 NULL, NULL);
486 #endif
487
488         static const ns_dtab dtab[] = {
489                 { NSSRC_FILES, files_setpwent, (void *)ENDPWENT },
490 #ifdef HESIOD
491                 { NSSRC_DNS, dns_setpwent, (void *)ENDPWENT },
492 #endif
493 #ifdef YP
494                 { NSSRC_NIS, nis_setpwent, (void *)ENDPWENT },
495 #endif
496                 { NSSRC_COMPAT, compat_setpwent, (void *)ENDPWENT },
497 #ifdef NS_CACHING
498                 NS_CACHE_CB(&cache_info)
499 #endif
500                 { NULL, NULL, NULL }
501         };
502         (void)_nsdispatch(NULL, dtab, NSDB_PASSWD, "endpwent", defaultsrc);
503 }
504
505
506 int
507 getpwent_r(struct passwd *pwd, char *buffer, size_t bufsize,
508     struct passwd **result)
509 {
510 #ifdef NS_CACHING
511         static const nss_cache_info cache_info = NS_MP_CACHE_INFO_INITIALIZER(
512                 passwd, (void *)nss_lt_all,
513                 pwd_marshal_func, pwd_unmarshal_func);
514 #endif
515
516         static const ns_dtab dtab[] = {
517                 { NSSRC_FILES, files_passwd, (void *)nss_lt_all },
518 #ifdef HESIOD
519                 { NSSRC_DNS, dns_passwd, (void *)nss_lt_all },
520 #endif
521 #ifdef YP
522                 { NSSRC_NIS, nis_passwd, (void *)nss_lt_all },
523 #endif
524                 { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_all },
525 #ifdef NS_CACHING
526                 NS_CACHE_CB(&cache_info)
527 #endif
528                 { NULL, NULL, NULL }
529         };
530         int     rv, ret_errno;
531
532         __pw_initpwd(pwd);
533         ret_errno = 0;
534         *result = NULL;
535         rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwent_r", defaultsrc,
536             pwd, buffer, bufsize, &ret_errno);
537         if (rv == NS_SUCCESS)
538                 return (0);
539         else
540                 return (ret_errno);
541 }
542
543
544 int
545 getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize,
546     struct passwd **result)
547 {
548 #ifdef NS_CACHING
549         static const nss_cache_info cache_info =
550                 NS_COMMON_CACHE_INFO_INITIALIZER(
551                 passwd, (void *)nss_lt_name,
552                 pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
553 #endif
554
555         static const ns_dtab dtab[] = {
556                 { NSSRC_FILES, files_passwd, (void *)nss_lt_name },
557 #ifdef HESIOD
558                 { NSSRC_DNS, dns_passwd, (void *)nss_lt_name },
559 #endif
560 #ifdef YP
561                 { NSSRC_NIS, nis_passwd, (void *)nss_lt_name },
562 #endif
563                 { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_name },
564 #ifdef NS_CACHING
565                 NS_CACHE_CB(&cache_info)
566 #endif
567                 { NULL, NULL, NULL }
568         };
569         int     rv, ret_errno;
570
571         __pw_initpwd(pwd);
572         ret_errno = 0;
573         *result = NULL;
574         rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwnam_r", defaultsrc,
575             name, pwd, buffer, bufsize, &ret_errno);
576         if (rv == NS_SUCCESS)
577                 return (0);
578         else
579                 return (ret_errno);
580 }
581
582
583 int
584 getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
585     struct passwd **result)
586 {
587 #ifdef NS_CACHING
588         static const nss_cache_info cache_info =
589                 NS_COMMON_CACHE_INFO_INITIALIZER(
590                 passwd, (void *)nss_lt_id,
591                 pwd_id_func, pwd_marshal_func, pwd_unmarshal_func);
592 #endif
593
594         static const ns_dtab dtab[] = {
595                 { NSSRC_FILES, files_passwd, (void *)nss_lt_id },
596 #ifdef HESIOD
597                 { NSSRC_DNS, dns_passwd, (void *)nss_lt_id },
598 #endif
599 #ifdef YP
600                 { NSSRC_NIS, nis_passwd, (void *)nss_lt_id },
601 #endif
602                 { NSSRC_COMPAT, compat_passwd, (void *)nss_lt_id },
603 #ifdef NS_CACHING
604                 NS_CACHE_CB(&cache_info)
605 #endif
606                 { NULL, NULL, NULL }
607         };
608         int     rv, ret_errno;
609
610         __pw_initpwd(pwd);
611         ret_errno = 0;
612         *result = NULL;
613         rv = _nsdispatch(result, dtab, NSDB_PASSWD, "getpwuid_r", defaultsrc,
614             uid, pwd, buffer, bufsize, &ret_errno);
615         if (rv == NS_SUCCESS)
616                 return (0);
617         else
618                 return (ret_errno);
619 }
620
621
622 static struct passwd     pwd;
623 static char             *pwd_storage;
624 static size_t            pwd_storage_size;
625
626
627 static struct passwd *
628 getpw(int (*fn)(union key, struct passwd *, char *, size_t, struct passwd **),
629     union key key)
630 {
631         int              rv;
632         struct passwd   *res;
633
634         if (pwd_storage == NULL) {
635                 pwd_storage = malloc(PWD_STORAGE_INITIAL);
636                 if (pwd_storage == NULL)
637                         return (NULL);
638                 pwd_storage_size = PWD_STORAGE_INITIAL;
639         }
640         do {
641                 rv = fn(key, &pwd, pwd_storage, pwd_storage_size, &res);
642                 if (res == NULL && rv == ERANGE) {
643                         free(pwd_storage);
644                         if ((pwd_storage_size << 1) > PWD_STORAGE_MAX) {
645                                 pwd_storage = NULL;
646                                 errno = ERANGE;
647                                 return (NULL);
648                         }
649                         pwd_storage_size <<= 1;
650                         pwd_storage = malloc(pwd_storage_size);
651                         if (pwd_storage == NULL)
652                                 return (NULL);
653                 }
654         } while (res == NULL && rv == ERANGE);
655         if (rv != 0)
656                 errno = rv;
657         return (res);
658 }
659
660
661 static int
662 wrap_getpwnam_r(union key key, struct passwd *pwd, char *buffer,
663     size_t bufsize, struct passwd **res)
664 {
665         return (getpwnam_r(key.name, pwd, buffer, bufsize, res));
666 }
667
668
669 static int
670 wrap_getpwuid_r(union key key, struct passwd *pwd, char *buffer,
671     size_t bufsize, struct passwd **res)
672 {
673         return (getpwuid_r(key.uid, pwd, buffer, bufsize, res));
674 }
675
676
677 static int
678 wrap_getpwent_r(union key key __unused, struct passwd *pwd, char *buffer,
679     size_t bufsize, struct passwd **res)
680 {
681         return (getpwent_r(pwd, buffer, bufsize, res));
682 }
683
684
685 struct passwd *
686 getpwnam(const char *name)
687 {
688         union key key;
689
690         key.name = name;
691         return (getpw(wrap_getpwnam_r, key));
692 }
693
694
695 struct passwd *
696 getpwuid(uid_t uid)
697 {
698         union key key;
699
700         key.uid = uid;
701         return (getpw(wrap_getpwuid_r, key));
702 }
703
704
705 struct passwd *
706 getpwent(void)
707 {
708         union key key;
709
710         key.uid = 0; /* not used */
711         return (getpw(wrap_getpwent_r, key));
712 }
713
714
715 /*
716  * files backend
717  */
718 static DB *
719 pwdbopen(int *version)
720 {
721         DB      *res;
722         DBT      key, entry;
723         int      rv;
724
725         if (geteuid() != 0 ||
726             (res = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL)) == NULL)
727                 res = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
728         if (res == NULL)
729                 return (NULL);
730         key.data = _PWD_VERSION_KEY;
731         key.size = strlen(_PWD_VERSION_KEY);
732         rv = res->get(res, &key, &entry, 0);
733         if (rv == 0)
734                 *version = *(unsigned char *)entry.data;
735         else
736                 *version = 3;
737         if (*version < 3 ||
738             *version >= nitems(pwdb_versions)) {
739                 syslog(LOG_CRIT, "Unsupported password database version %d",
740                     *version);
741                 res->close(res);
742                 res = NULL;
743         }
744         return (res);
745 }
746
747
748 static void
749 files_endstate(void *p)
750 {
751         DB      *db;
752
753         if (p == NULL)
754                 return;
755         db = ((struct files_state *)p)->db;
756         if (db != NULL)
757                 db->close(db);
758         free(p);
759 }
760
761
762 static int
763 files_setpwent(void *retval, void *mdata, va_list ap)
764 {
765         struct files_state      *st;
766         int                      rv, stayopen;
767
768         rv = files_getstate(&st);
769         if (rv != 0)
770                 return (NS_UNAVAIL);
771         switch ((enum constants)(uintptr_t)mdata) {
772         case SETPWENT:
773                 stayopen = va_arg(ap, int);
774                 st->keynum = 0;
775                 if (stayopen)
776                         st->db = pwdbopen(&st->version);
777                 st->stayopen = stayopen;
778                 break;
779         case ENDPWENT:
780                 if (st->db != NULL) {
781                         (void)st->db->close(st->db);
782                         st->db = NULL;
783                 }
784                 break;
785         default:
786                 break;
787         }
788         return (NS_UNAVAIL);
789 }
790
791
792 static int
793 files_passwd(void *retval, void *mdata, va_list ap)
794 {
795         char                     keybuf[MAXLOGNAME + 1];
796         DBT                      key, entry;
797         struct files_state      *st;
798         enum nss_lookup_type     how;
799         const char              *name;
800         struct passwd           *pwd;
801         char                    *buffer;
802         size_t                   bufsize, namesize;
803         uid_t                    uid;
804         uint32_t                 store;
805         int                      rv, stayopen = 0, *errnop;
806
807         name = NULL;
808         uid = (uid_t)-1;
809         how = (enum nss_lookup_type)(uintptr_t)mdata;
810         switch (how) {
811         case nss_lt_name:
812                 name = va_arg(ap, const char *);
813                 keybuf[0] = _PW_KEYBYNAME;
814                 break;
815         case nss_lt_id:
816                 uid = va_arg(ap, uid_t);
817                 keybuf[0] = _PW_KEYBYUID;
818                 break;
819         case nss_lt_all:
820                 keybuf[0] = _PW_KEYBYNUM;
821                 break;
822         default:
823                 rv = NS_NOTFOUND;
824                 goto fin;
825         }
826         pwd = va_arg(ap, struct passwd *);
827         buffer = va_arg(ap, char *);
828         bufsize = va_arg(ap, size_t);
829         errnop = va_arg(ap, int *);
830         *errnop = files_getstate(&st);
831         if (*errnop != 0)
832                 return (NS_UNAVAIL);
833         if (how == nss_lt_all && st->keynum < 0) {
834                 rv = NS_NOTFOUND;
835                 goto fin;
836         }
837         if (st->db == NULL &&
838             (st->db = pwdbopen(&st->version)) == NULL) {
839                 *errnop = errno;
840                 rv = NS_UNAVAIL;
841                 goto fin;
842         }
843         if (how == nss_lt_all)
844                 stayopen = 1;
845         else
846                 stayopen = st->stayopen;
847         key.data = keybuf;
848         do {
849                 switch (how) {
850                 case nss_lt_name:
851                         /* MAXLOGNAME includes NUL byte, but we do not
852                          * include the NUL byte in the key.
853                          */
854                         namesize = strlcpy(&keybuf[1], name, sizeof(keybuf)-1);
855                         if (namesize >= sizeof(keybuf)-1) {
856                                 *errnop = EINVAL;
857                                 rv = NS_NOTFOUND;
858                                 goto fin;
859                         }
860                         key.size = namesize + 1;
861                         break;
862                 case nss_lt_id:
863                         if (st->version < _PWD_CURRENT_VERSION) {
864                                 memcpy(&keybuf[1], &uid, sizeof(uid));
865                                 key.size = sizeof(uid) + 1;
866                         } else {
867                                 store = htonl(uid);
868                                 memcpy(&keybuf[1], &store, sizeof(store));
869                                 key.size = sizeof(store) + 1;
870                         }
871                         break;
872                 case nss_lt_all:
873                         st->keynum++;
874                         if (st->version < _PWD_CURRENT_VERSION) {
875                                 memcpy(&keybuf[1], &st->keynum,
876                                     sizeof(st->keynum));
877                                 key.size = sizeof(st->keynum) + 1;
878                         } else {
879                                 store = htonl(st->keynum);
880                                 memcpy(&keybuf[1], &store, sizeof(store));
881                                 key.size = sizeof(store) + 1;
882                         }
883                         break;
884                 }
885                 keybuf[0] = _PW_VERSIONED(keybuf[0], st->version);
886                 rv = st->db->get(st->db, &key, &entry, 0);
887                 if (rv < 0 || rv > 1) { /* should never return > 1 */
888                         *errnop = errno;
889                         rv = NS_UNAVAIL;
890                         goto fin;
891                 } else if (rv == 1) {
892                         if (how == nss_lt_all)
893                                 st->keynum = -1;
894                         rv = NS_NOTFOUND;
895                         goto fin;
896                 }
897                 rv = pwdb_versions[st->version].match(entry.data, entry.size,
898                     how, name, uid);
899                 if (rv != NS_SUCCESS)
900                         continue;
901                 if (entry.size > bufsize) {
902                         *errnop = ERANGE;
903                         rv = NS_RETURN;
904                         break;
905                 }
906                 memcpy(buffer, entry.data, entry.size);
907                 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
908                     errnop);
909         } while (how == nss_lt_all && !(rv & NS_TERMINATE));
910 fin:
911         if (st->db != NULL && !stayopen) {
912                 (void)st->db->close(st->db);
913                 st->db = NULL;
914         }
915         if (rv == NS_SUCCESS) {
916                 pwd->pw_fields &= ~_PWF_SOURCE;
917                 pwd->pw_fields |= _PWF_FILES;
918                 if (retval != NULL)
919                         *(struct passwd **)retval = pwd;
920         }
921         return (rv);
922 }
923
924
925 static int
926 pwdb_match_entry_v3(char *entry, size_t entrysize, enum nss_lookup_type how,
927     const char *name, uid_t uid)
928 {
929         const char      *p, *eom;
930         uid_t            uid2;
931
932         eom = &entry[entrysize];
933         for (p = entry; p < eom; p++)
934                 if (*p == '\0')
935                         break;
936         if (*p != '\0')
937                 return (NS_NOTFOUND);
938         if (how == nss_lt_all)
939                 return (NS_SUCCESS);
940         if (how == nss_lt_name)
941                 return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND);
942         for (p++; p < eom; p++)
943                 if (*p == '\0')
944                         break;
945         if (*p != '\0' || (++p) + sizeof(uid) >= eom)
946                 return (NS_NOTFOUND);
947         memcpy(&uid2, p, sizeof(uid2));
948         return (uid == uid2 ? NS_SUCCESS : NS_NOTFOUND);
949 }
950
951
952 static int
953 pwdb_parse_entry_v3(char *buffer, size_t bufsize, struct passwd *pwd,
954     int *errnop)
955 {
956         char            *p, *eom;
957         int32_t          pw_change, pw_expire;
958
959         /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
960         p = buffer;
961         eom = &buffer[bufsize];
962 #define STRING(field)   do {                    \
963                 (field) = p;                    \
964                 while (p < eom && *p != '\0')   \
965                         p++;                    \
966                 if (p >= eom)                   \
967                         return (NS_NOTFOUND);   \
968                 p++;                            \
969         } while (0)
970 #define SCALAR(field)   do {                            \
971                 if (p + sizeof(field) > eom)            \
972                         return (NS_NOTFOUND);           \
973                 memcpy(&(field), p, sizeof(field));     \
974                 p += sizeof(field);                     \
975         } while (0)
976         STRING(pwd->pw_name);
977         STRING(pwd->pw_passwd);
978         SCALAR(pwd->pw_uid);
979         SCALAR(pwd->pw_gid);
980         SCALAR(pw_change);
981         STRING(pwd->pw_class);
982         STRING(pwd->pw_gecos);
983         STRING(pwd->pw_dir);
984         STRING(pwd->pw_shell);
985         SCALAR(pw_expire);
986         SCALAR(pwd->pw_fields);
987 #undef STRING
988 #undef SCALAR
989         pwd->pw_change = pw_change;
990         pwd->pw_expire = pw_expire;
991         return (NS_SUCCESS);
992 }
993
994
995 static int
996 pwdb_match_entry_v4(char *entry, size_t entrysize, enum nss_lookup_type how,
997     const char *name, uid_t uid)
998 {
999         const char      *p, *eom;
1000         uint32_t         uid2;
1001
1002         eom = &entry[entrysize];
1003         for (p = entry; p < eom; p++)
1004                 if (*p == '\0')
1005                         break;
1006         if (*p != '\0')
1007                 return (NS_NOTFOUND);
1008         if (how == nss_lt_all)
1009                 return (NS_SUCCESS);
1010         if (how == nss_lt_name)
1011                 return (strcmp(name, entry) == 0 ? NS_SUCCESS : NS_NOTFOUND);
1012         for (p++; p < eom; p++)
1013                 if (*p == '\0')
1014                         break;
1015         if (*p != '\0' || (++p) + sizeof(uid) >= eom)
1016                 return (NS_NOTFOUND);
1017         memcpy(&uid2, p, sizeof(uid2));
1018         uid2 = ntohl(uid2);
1019         return (uid == (uid_t)uid2 ? NS_SUCCESS : NS_NOTFOUND);
1020 }
1021
1022
1023 static int
1024 pwdb_parse_entry_v4(char *buffer, size_t bufsize, struct passwd *pwd,
1025     int *errnop)
1026 {
1027         char            *p, *eom;
1028         uint32_t         n;
1029
1030         /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1031         p = buffer;
1032         eom = &buffer[bufsize];
1033 #define STRING(field)   do {                    \
1034                 (field) = p;                    \
1035                 while (p < eom && *p != '\0')   \
1036                         p++;                    \
1037                 if (p >= eom)                   \
1038                         return (NS_NOTFOUND);   \
1039                 p++;                            \
1040         } while (0)
1041 #define SCALAR(field)   do {                            \
1042                 if (p + sizeof(n) > eom)                \
1043                         return (NS_NOTFOUND);           \
1044                 memcpy(&n, p, sizeof(n));               \
1045                 (field) = ntohl(n);                     \
1046                 p += sizeof(n);                         \
1047         } while (0)
1048         STRING(pwd->pw_name);
1049         STRING(pwd->pw_passwd);
1050         SCALAR(pwd->pw_uid);
1051         SCALAR(pwd->pw_gid);
1052         SCALAR(pwd->pw_change);
1053         STRING(pwd->pw_class);
1054         STRING(pwd->pw_gecos);
1055         STRING(pwd->pw_dir);
1056         STRING(pwd->pw_shell);
1057         SCALAR(pwd->pw_expire);
1058         SCALAR(pwd->pw_fields);
1059 #undef STRING
1060 #undef SCALAR
1061         return (NS_SUCCESS);
1062 }
1063
1064
1065 #ifdef HESIOD
1066 /*
1067  * dns backend
1068  */
1069 static void
1070 dns_endstate(void *p)
1071 {
1072         free(p);
1073 }
1074
1075
1076 static int
1077 dns_setpwent(void *retval, void *mdata, va_list ap)
1078 {
1079         struct dns_state        *st;
1080         int                      rv;
1081
1082         rv = dns_getstate(&st);
1083         if (rv != 0)
1084                 return (NS_UNAVAIL);
1085         st->counter = 0;
1086         return (NS_UNAVAIL);
1087 }
1088
1089
1090 static int
1091 dns_passwd(void *retval, void *mdata, va_list ap)
1092 {
1093         char                     buf[HESIOD_NAME_MAX];
1094         struct dns_state        *st;
1095         struct passwd           *pwd;
1096         const char              *name, *label;
1097         void                    *ctx;
1098         char                    *buffer, **hes;
1099         size_t                   bufsize, linesize;
1100         uid_t                    uid;
1101         enum nss_lookup_type     how;
1102         int                      rv, *errnop;
1103
1104         ctx = NULL;
1105         hes = NULL;
1106         name = NULL;
1107         uid = (uid_t)-1;
1108         how = (enum nss_lookup_type)(uintptr_t)mdata;
1109         switch (how) {
1110         case nss_lt_name:
1111                 name = va_arg(ap, const char *);
1112                 break;
1113         case nss_lt_id:
1114                 uid = va_arg(ap, uid_t);
1115                 break;
1116         case nss_lt_all:
1117                 break;
1118         }
1119         pwd     = va_arg(ap, struct passwd *);
1120         buffer  = va_arg(ap, char *);
1121         bufsize = va_arg(ap, size_t);
1122         errnop  = va_arg(ap, int *);
1123         *errnop = dns_getstate(&st);
1124         if (*errnop != 0)
1125                 return (NS_UNAVAIL);
1126         if (hesiod_init(&ctx) != 0) {
1127                 *errnop = errno;
1128                 rv = NS_UNAVAIL;
1129                 goto fin;
1130         }
1131         do {
1132                 rv = NS_NOTFOUND;
1133                 switch (how) {
1134                 case nss_lt_name:
1135                         label = name;
1136                         break;
1137                 case nss_lt_id:
1138                         if (snprintf(buf, sizeof(buf), "%lu",
1139                             (unsigned long)uid) >= sizeof(buf))
1140                                 goto fin;
1141                         label = buf;
1142                         break;
1143                 case nss_lt_all:
1144                         if (st->counter < 0)
1145                                 goto fin;
1146                         if (snprintf(buf, sizeof(buf), "passwd-%ld",
1147                             st->counter++) >= sizeof(buf))
1148                                 goto fin;
1149                         label = buf;
1150                         break;
1151                 }
1152                 hes = hesiod_resolve(ctx, label,
1153                     how == nss_lt_id ? "uid" : "passwd");
1154                 if (hes == NULL) {
1155                         if (how == nss_lt_all)
1156                                 st->counter = -1;
1157                         if (errno != ENOENT)
1158                                 *errnop = errno;
1159                         goto fin;
1160                 }
1161                 rv = __pw_match_entry(hes[0], strlen(hes[0]), how, name, uid);
1162                 if (rv != NS_SUCCESS) {
1163                         hesiod_free_list(ctx, hes);
1164                         hes = NULL;
1165                         continue;
1166                 }
1167                 linesize = strlcpy(buffer, hes[0], bufsize);
1168                 if (linesize >= bufsize) {
1169                         *errnop = ERANGE;
1170                         rv = NS_RETURN;
1171                         continue;
1172                 }
1173                 hesiod_free_list(ctx, hes);
1174                 hes = NULL;
1175                 rv = __pw_parse_entry(buffer, bufsize, pwd, 0, errnop);
1176         } while (how == nss_lt_all && !(rv & NS_TERMINATE));
1177 fin:
1178         if (hes != NULL)
1179                 hesiod_free_list(ctx, hes);
1180         if (ctx != NULL)
1181                 hesiod_end(ctx);
1182         if (rv == NS_SUCCESS) {
1183                 pwd->pw_fields &= ~_PWF_SOURCE;
1184                 pwd->pw_fields |= _PWF_HESIOD;
1185                 if (retval != NULL)
1186                         *(struct passwd **)retval = pwd;
1187         }
1188         return (rv);
1189 }
1190 #endif /* HESIOD */
1191
1192
1193 #ifdef YP
1194 /*
1195  * nis backend
1196  */
1197 static void
1198 nis_endstate(void *p)
1199 {
1200         free(((struct nis_state *)p)->key);
1201         free(p);
1202 }
1203
1204 /*
1205  * Test for the presence of special FreeBSD-specific master.passwd.by*
1206  * maps. We do this using yp_order(). If it fails, then either the server
1207  * doesn't have the map, or the YPPROC_ORDER procedure isn't supported by
1208  * the server (Sun NIS+ servers in YP compat mode behave this way). If
1209  * the master.passwd.by* maps don't exist, then let the lookup routine try
1210  * the regular passwd.by* maps instead. If the lookup routine fails, it
1211  * can return an error as needed.
1212  */
1213 static int
1214 nis_map(char *domain, enum nss_lookup_type how, char *buffer, size_t bufsize,
1215     int *master)
1216 {
1217         int     rv, order;
1218
1219         *master = 0;
1220         if (geteuid() == 0) {
1221                 if (snprintf(buffer, bufsize, "master.passwd.by%s",
1222                     (how == nss_lt_id) ? "uid" : "name") >= bufsize)
1223                         return (NS_UNAVAIL);
1224                 rv = yp_order(domain, buffer, &order);
1225                 if (rv == 0) {
1226                         *master = 1;
1227                         return (NS_SUCCESS);
1228                 }
1229         }
1230
1231         if (snprintf(buffer, bufsize, "passwd.by%s",
1232             (how == nss_lt_id) ? "uid" : "name") >= bufsize)
1233                 return (NS_UNAVAIL);
1234
1235         return (NS_SUCCESS);
1236 }
1237
1238
1239 static int
1240 nis_adjunct(char *domain, const char *name, char *buffer, size_t bufsize)
1241 {
1242         int      rv;
1243         char    *result, *p, *q, *eor;
1244         int      resultlen;
1245
1246         result = NULL;
1247         rv = yp_match(domain, "passwd.adjunct.byname", name, strlen(name),
1248             &result, &resultlen);
1249         if (rv != 0)
1250                 rv = 1;
1251         else {
1252                 eor = &result[resultlen];
1253                 p = memchr(result, ':', eor - result);
1254                 if (p != NULL && ++p < eor &&
1255                     (q = memchr(p, ':', eor - p)) != NULL) {
1256                         if (q - p >= bufsize)
1257                                 rv = -1;
1258                         else {
1259                                 memcpy(buffer, p, q - p);
1260                                 buffer[q - p] ='\0';
1261                         }
1262                 } else
1263                         rv = 1;
1264         }
1265         free(result);
1266         return (rv);
1267 }
1268
1269
1270 static int
1271 nis_setpwent(void *retval, void *mdata, va_list ap)
1272 {
1273         struct nis_state        *st;
1274         int                      rv;
1275
1276         rv = nis_getstate(&st);
1277         if (rv != 0)
1278                 return (NS_UNAVAIL);
1279         st->done = 0;
1280         free(st->key);
1281         st->key = NULL;
1282         return (NS_UNAVAIL);
1283 }
1284
1285
1286 static int
1287 nis_passwd(void *retval, void *mdata, va_list ap)
1288 {
1289         char             map[YPMAXMAP];
1290         struct nis_state *st;
1291         struct passwd   *pwd;
1292         const char      *name;
1293         char            *buffer, *key, *result;
1294         size_t           bufsize;
1295         uid_t            uid;
1296         enum nss_lookup_type how;
1297         int             *errnop, keylen, resultlen, rv, master;
1298
1299         name = NULL;
1300         uid = (uid_t)-1;
1301         how = (enum nss_lookup_type)(uintptr_t)mdata;
1302         switch (how) {
1303         case nss_lt_name:
1304                 name = va_arg(ap, const char *);
1305                 break;
1306         case nss_lt_id:
1307                 uid = va_arg(ap, uid_t);
1308                 break;
1309         case nss_lt_all:
1310                 break;
1311         }
1312         pwd     = va_arg(ap, struct passwd *);
1313         buffer  = va_arg(ap, char *);
1314         bufsize = va_arg(ap, size_t);
1315         errnop  = va_arg(ap, int *);
1316         *errnop = nis_getstate(&st);
1317         if (*errnop != 0)
1318                 return (NS_UNAVAIL);
1319         if (st->domain[0] == '\0') {
1320                 if (getdomainname(st->domain, sizeof(st->domain)) != 0) {
1321                         *errnop = errno;
1322                         return (NS_UNAVAIL);
1323                 }
1324         }
1325         rv = nis_map(st->domain, how, map, sizeof(map), &master);
1326         if (rv != NS_SUCCESS)
1327                 return (rv);
1328         result = NULL;
1329         do {
1330                 rv = NS_NOTFOUND;
1331                 switch (how) {
1332                 case nss_lt_name:
1333                         if (strlcpy(buffer, name, bufsize) >= bufsize)
1334                                 goto erange;
1335                         break;
1336                 case nss_lt_id:
1337                         if (snprintf(buffer, bufsize, "%lu",
1338                             (unsigned long)uid) >= bufsize)
1339                                 goto erange;
1340                         break;
1341                 case nss_lt_all:
1342                         if (st->done)
1343                                 goto fin;
1344                         break;
1345                 }
1346                 result = NULL;
1347                 if (how == nss_lt_all) {
1348                         if (st->key == NULL)
1349                                 rv = yp_first(st->domain, map, &st->key,
1350                                     &st->keylen, &result, &resultlen);
1351                         else {
1352                                 key = st->key;
1353                                 keylen = st->keylen;
1354                                 st->key = NULL;
1355                                 rv = yp_next(st->domain, map, key, keylen,
1356                                     &st->key, &st->keylen, &result,
1357                                     &resultlen);
1358                                 free(key);
1359                         }
1360                         if (rv != 0) {
1361                                 free(result);
1362                                 free(st->key);
1363                                 st->key = NULL;
1364                                 if (rv == YPERR_NOMORE)
1365                                         st->done = 1;
1366                                 else
1367                                         rv = NS_UNAVAIL;
1368                                 goto fin;
1369                         }
1370                 } else {
1371                         rv = yp_match(st->domain, map, buffer, strlen(buffer),
1372                             &result, &resultlen);
1373                         if (rv == YPERR_KEY) {
1374                                 rv = NS_NOTFOUND;
1375                                 continue;
1376                         } else if (rv != 0) {
1377                                 free(result);
1378                                 rv = NS_UNAVAIL;
1379                                 continue;
1380                         }
1381                 }
1382                 if (resultlen >= bufsize) {
1383                         free(result);
1384                         goto erange;
1385                 }
1386                 memcpy(buffer, result, resultlen);
1387                 buffer[resultlen] = '\0';
1388                 free(result);
1389                 rv = __pw_match_entry(buffer, resultlen, how, name, uid);
1390                 if (rv == NS_SUCCESS)
1391                         rv = __pw_parse_entry(buffer, resultlen, pwd, master,
1392                             errnop);
1393         } while (how == nss_lt_all && !(rv & NS_TERMINATE));
1394 fin:
1395         if (rv == NS_SUCCESS) {
1396                 if (strstr(pwd->pw_passwd, "##") != NULL) {
1397                         rv = nis_adjunct(st->domain, pwd->pw_name,
1398                             &buffer[resultlen+1], bufsize-resultlen-1);
1399                         if (rv < 0)
1400                                 goto erange;
1401                         else if (rv == 0)
1402                                 pwd->pw_passwd = &buffer[resultlen+1];
1403                 }
1404                 pwd->pw_fields &= ~_PWF_SOURCE;
1405                 pwd->pw_fields |= _PWF_NIS;
1406                 if (retval != NULL)
1407                         *(struct passwd **)retval = pwd;
1408                 rv = NS_SUCCESS;
1409         }
1410         return (rv);
1411 erange:
1412         *errnop = ERANGE;
1413         return (NS_RETURN);
1414 }
1415 #endif /* YP */
1416
1417
1418 /*
1419  * compat backend
1420  */
1421 static void
1422 compat_clear_template(struct passwd *template)
1423 {
1424
1425         free(template->pw_passwd);
1426         free(template->pw_gecos);
1427         free(template->pw_dir);
1428         free(template->pw_shell);
1429         memset(template, 0, sizeof(*template));
1430 }
1431
1432
1433 static int
1434 compat_set_template(struct passwd *src, struct passwd *template)
1435 {
1436
1437         compat_clear_template(template);
1438 #ifdef PW_OVERRIDE_PASSWD
1439         if ((src->pw_fields & _PWF_PASSWD) &&
1440             (template->pw_passwd = strdup(src->pw_passwd)) == NULL)
1441                 goto enomem;
1442 #endif
1443         if (src->pw_fields & _PWF_UID)
1444                 template->pw_uid = src->pw_uid;
1445         if (src->pw_fields & _PWF_GID)
1446                 template->pw_gid = src->pw_gid;
1447         if ((src->pw_fields & _PWF_GECOS) &&
1448             (template->pw_gecos = strdup(src->pw_gecos)) == NULL)
1449                 goto enomem;
1450         if ((src->pw_fields & _PWF_DIR) &&
1451             (template->pw_dir = strdup(src->pw_dir)) == NULL)
1452                 goto enomem;
1453         if ((src->pw_fields & _PWF_SHELL) &&
1454             (template->pw_shell = strdup(src->pw_shell)) == NULL)
1455                 goto enomem;
1456         template->pw_fields = src->pw_fields;
1457         return (0);
1458 enomem:
1459         syslog(LOG_ERR, "getpwent memory allocation failure");
1460         return (-1);
1461 }
1462
1463
1464 static int
1465 compat_use_template(struct passwd *pwd, struct passwd *template, char *buffer,
1466     size_t bufsize)
1467 {
1468         struct passwd hold;
1469         char    *copy, *p, *q, *eob;
1470         size_t   n;
1471
1472         /* We cannot know the layout of the password fields in `buffer',
1473          * so we have to copy everything.
1474          */
1475         if (template->pw_fields == 0) /* nothing to fill-in */
1476                 return (0);
1477         n = 0;
1478         n += pwd->pw_name != NULL ? strlen(pwd->pw_name) + 1 : 0;
1479         n += pwd->pw_passwd != NULL ? strlen(pwd->pw_passwd) + 1 : 0;
1480         n += pwd->pw_class != NULL ? strlen(pwd->pw_class) + 1 : 0;
1481         n += pwd->pw_gecos != NULL ? strlen(pwd->pw_gecos) + 1 : 0;
1482         n += pwd->pw_dir != NULL ? strlen(pwd->pw_dir) + 1 : 0;
1483         n += pwd->pw_shell != NULL ? strlen(pwd->pw_shell) + 1 : 0;
1484         copy = malloc(n);
1485         if (copy == NULL) {
1486                 syslog(LOG_ERR, "getpwent memory allocation failure");
1487                 return (ENOMEM);
1488         }
1489         p = copy;
1490         eob = &copy[n];
1491 #define COPY(field) do {                                \
1492         if (pwd->field == NULL)                         \
1493                 hold.field = NULL;                      \
1494         else {                                          \
1495                 hold.field = p;                         \
1496                 p += strlcpy(p, pwd->field, eob-p) + 1; \
1497         }                                               \
1498 } while (0)
1499         COPY(pw_name);
1500         COPY(pw_passwd);
1501         COPY(pw_class);
1502         COPY(pw_gecos);
1503         COPY(pw_dir);
1504         COPY(pw_shell);
1505 #undef COPY
1506         p = buffer;
1507         eob = &buffer[bufsize];
1508 #define COPY(field, flag) do {                                           \
1509         q = (template->pw_fields & flag) ? template->field : hold.field; \
1510         if (q == NULL)                                                   \
1511                 pwd->field = NULL;                                       \
1512         else {                                                           \
1513                 pwd->field = p;                                          \
1514                 if ((n = strlcpy(p, q, eob-p)) >= eob-p) {               \
1515                         free(copy);                                      \
1516                         return (ERANGE);                                 \
1517                 }                                                        \
1518                 p += n + 1;                                              \
1519         }                                                                \
1520 } while (0)
1521         COPY(pw_name, 0);
1522 #ifdef PW_OVERRIDE_PASSWD
1523         COPY(pw_passwd, _PWF_PASSWD);
1524 #else
1525         COPY(pw_passwd, 0);
1526 #endif
1527         COPY(pw_class, 0);
1528         COPY(pw_gecos, _PWF_GECOS);
1529         COPY(pw_dir, _PWF_DIR);
1530         COPY(pw_shell, _PWF_SHELL);
1531 #undef COPY
1532 #define COPY(field, flag) do {                  \
1533         if (template->pw_fields & flag)         \
1534                 pwd->field = template->field;   \
1535 } while (0)
1536         COPY(pw_uid, _PWF_UID);
1537         COPY(pw_gid, _PWF_GID);
1538 #undef COPY
1539         free(copy);
1540         return (0);
1541 }
1542
1543
1544 static int
1545 compat_exclude(const char *name, DB **db)
1546 {
1547         DBT     key, data;
1548
1549         if (*db == NULL &&
1550             (*db = dbopen(NULL, O_RDWR, 600, DB_HASH, 0)) == NULL)
1551                 return (errno);
1552         key.size = strlen(name);
1553         key.data = (char *)name;
1554         data.size = 0;
1555         data.data = NULL;
1556
1557         if ((*db)->put(*db, &key, &data, 0) == -1)
1558                 return (errno);
1559         return (0);
1560 }
1561
1562
1563 static int
1564 compat_is_excluded(const char *name, DB *db)
1565 {
1566         DBT     key, data;
1567
1568         if (db == NULL)
1569                 return (0);
1570         key.size = strlen(name);
1571         key.data = (char *)name;
1572         return (db->get(db, &key, &data, 0) == 0);
1573 }
1574
1575
1576 static int
1577 compat_redispatch(struct compat_state *st, enum nss_lookup_type how,
1578     enum nss_lookup_type lookup_how, const char *name, const char *lookup_name,
1579     uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, int *errnop)
1580 {
1581         static const ns_src compatsrc[] = {
1582 #ifdef YP
1583                 { NSSRC_NIS, NS_SUCCESS },
1584 #endif
1585                 { NULL, 0 }
1586         };
1587         ns_dtab dtab[] = {
1588 #ifdef YP
1589                 { NSSRC_NIS, nis_passwd, NULL },
1590 #endif
1591 #ifdef HESIOD
1592                 { NSSRC_DNS, dns_passwd, NULL },
1593 #endif
1594                 { NULL, NULL, NULL }
1595         };
1596         void            *discard;
1597         int              e, i, rv;
1598
1599         for (i = 0; i < (int)(nitems(dtab) - 1); i++)
1600                 dtab[i].mdata = (void *)lookup_how;
1601 more:
1602         __pw_initpwd(pwd);
1603         switch (lookup_how) {
1604         case nss_lt_all:
1605                 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
1606                     "getpwent_r", compatsrc, pwd, buffer, bufsize,
1607                     errnop);
1608                 break;
1609         case nss_lt_id:
1610                 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
1611                     "getpwuid_r", compatsrc, uid, pwd, buffer,
1612                     bufsize, errnop);
1613                 break;
1614         case nss_lt_name:
1615                 rv = _nsdispatch(&discard, dtab, NSDB_PASSWD_COMPAT,
1616                     "getpwnam_r", compatsrc, lookup_name, pwd, buffer,
1617                     bufsize, errnop);
1618                 break;
1619         default:
1620                 return (NS_UNAVAIL);
1621         }
1622         if (rv != NS_SUCCESS)
1623                 return (rv);
1624         if (compat_is_excluded(pwd->pw_name, st->exclude)) {
1625                 if (how == nss_lt_all)
1626                         goto more;
1627                 return (NS_NOTFOUND);
1628         }
1629         e = compat_use_template(pwd, &st->template, buffer, bufsize);
1630         if (e != 0) {
1631                 *errnop = e;
1632                 if (e == ERANGE)
1633                         return (NS_RETURN);
1634                 else
1635                         return (NS_UNAVAIL);
1636         }
1637         switch (how) {
1638         case nss_lt_name:
1639                 if (strcmp(name, pwd->pw_name) != 0)
1640                         return (NS_NOTFOUND);
1641                 break;
1642         case nss_lt_id:
1643                 if (uid != pwd->pw_uid)
1644                         return (NS_NOTFOUND);
1645                 break;
1646         default:
1647                 break;
1648         }
1649         return (NS_SUCCESS);
1650 }
1651
1652
1653 static void
1654 compat_endstate(void *p)
1655 {
1656         struct compat_state *st;
1657
1658         if (p == NULL)
1659                 return;
1660         st = (struct compat_state *)p;
1661         if (st->db != NULL)
1662                 st->db->close(st->db);
1663         if (st->exclude != NULL)
1664                 st->exclude->close(st->exclude);
1665         compat_clear_template(&st->template);
1666         free(p);
1667 }
1668
1669
1670 static int
1671 compat_setpwent(void *retval, void *mdata, va_list ap)
1672 {
1673         static const ns_src compatsrc[] = {
1674 #ifdef YP
1675                 { NSSRC_NIS, NS_SUCCESS },
1676 #endif
1677                 { NULL, 0 }
1678         };
1679         ns_dtab dtab[] = {
1680 #ifdef YP
1681                 { NSSRC_NIS, nis_setpwent, NULL },
1682 #endif
1683 #ifdef HESIOD
1684                 { NSSRC_DNS, dns_setpwent, NULL },
1685 #endif
1686                 { NULL, NULL, NULL }
1687         };
1688         struct compat_state     *st;
1689         int                      rv, stayopen;
1690
1691 #define set_setent(x, y) do {                                   \
1692         int i;                                                  \
1693         for (i = 0; i < (int)(nitems(x) - 1); i++)              \
1694                 x[i].mdata = (void *)y;                         \
1695 } while (0)
1696
1697         rv = compat_getstate(&st);
1698         if (rv != 0)
1699                 return (NS_UNAVAIL);
1700         switch ((enum constants)(uintptr_t)mdata) {
1701         case SETPWENT:
1702                 stayopen = va_arg(ap, int);
1703                 st->keynum = 0;
1704                 if (stayopen)
1705                         st->db = pwdbopen(&st->version);
1706                 st->stayopen = stayopen;
1707                 set_setent(dtab, mdata);
1708                 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "setpwent",
1709                     compatsrc, 0);
1710                 break;
1711         case ENDPWENT:
1712                 if (st->db != NULL) {
1713                         (void)st->db->close(st->db);
1714                         st->db = NULL;
1715                 }
1716                 set_setent(dtab, mdata);
1717                 (void)_nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "endpwent",
1718                     compatsrc, 0);
1719                 break;
1720         default:
1721                 break;
1722         }
1723         return (NS_UNAVAIL);
1724 #undef set_setent
1725 }
1726
1727
1728 static int
1729 compat_passwd(void *retval, void *mdata, va_list ap)
1730 {
1731         char                     keybuf[MAXLOGNAME + 1];
1732         DBT                      key, entry;
1733         pwkeynum                 keynum;
1734         struct compat_state     *st;
1735         enum nss_lookup_type     how;
1736         const char              *name;
1737         struct passwd           *pwd;
1738         char                    *buffer, *pw_name;
1739         char                    *host, *user, *domain;
1740         size_t                   bufsize;
1741         uid_t                    uid;
1742         uint32_t                 store;
1743         int                      rv, from_compat, stayopen, *errnop;
1744
1745         from_compat = 0;
1746         name = NULL;
1747         uid = (uid_t)-1;
1748         how = (enum nss_lookup_type)(uintptr_t)mdata;
1749         switch (how) {
1750         case nss_lt_name:
1751                 name = va_arg(ap, const char *);
1752                 break;
1753         case nss_lt_id:
1754                 uid = va_arg(ap, uid_t);
1755                 break;
1756         case nss_lt_all:
1757                 break;
1758         default:
1759                 rv = NS_NOTFOUND;
1760                 goto fin;
1761         }
1762         pwd = va_arg(ap, struct passwd *);
1763         buffer = va_arg(ap, char *);
1764         bufsize = va_arg(ap, size_t);
1765         errnop = va_arg(ap, int *);
1766         *errnop = compat_getstate(&st);
1767         if (*errnop != 0)
1768                 return (NS_UNAVAIL);
1769         if (how == nss_lt_all && st->keynum < 0) {
1770                 rv = NS_NOTFOUND;
1771                 goto fin;
1772         }
1773         if (st->db == NULL &&
1774             (st->db = pwdbopen(&st->version)) == NULL) {
1775                 *errnop = errno;
1776                 rv = NS_UNAVAIL;
1777                 goto fin;
1778         }
1779         if (how == nss_lt_all) {
1780                 if (st->keynum < 0) {
1781                         rv = NS_NOTFOUND;
1782                         goto fin;
1783                 }
1784                 keynum = st->keynum;
1785                 stayopen = 1;
1786         } else {
1787                 keynum = 0;
1788                 stayopen = st->stayopen;
1789         }
1790 docompat:
1791         rv = NS_NOTFOUND;
1792         switch (st->compat) {
1793         case COMPAT_MODE_ALL:
1794                 rv = compat_redispatch(st, how, how, name, name, uid, pwd,
1795                     buffer, bufsize, errnop);
1796                 if (rv != NS_SUCCESS)
1797                         st->compat = COMPAT_MODE_OFF;
1798                 break;
1799         case COMPAT_MODE_NETGROUP:
1800                 /* XXX getnetgrent is not thread-safe. */
1801                 do {
1802                         rv = getnetgrent(&host, &user, &domain);
1803                         if (rv == 0) {
1804                                 endnetgrent();
1805                                 st->compat = COMPAT_MODE_OFF;
1806                                 rv = NS_NOTFOUND;
1807                                 continue;
1808                         } else if (user == NULL || user[0] == '\0')
1809                                 continue;
1810                         rv = compat_redispatch(st, how, nss_lt_name, name,
1811                             user, uid, pwd, buffer, bufsize, errnop);
1812                 } while (st->compat == COMPAT_MODE_NETGROUP &&
1813                     !(rv & NS_TERMINATE));
1814                 break;
1815         case COMPAT_MODE_NAME:
1816                 rv = compat_redispatch(st, how, nss_lt_name, name, st->name,
1817                     uid, pwd, buffer, bufsize, errnop);
1818                 free(st->name);
1819                 st->name = NULL;
1820                 st->compat = COMPAT_MODE_OFF;
1821                 break;
1822         default:
1823                 break;
1824         }
1825         if (rv & NS_TERMINATE) {
1826                 from_compat = 1;
1827                 goto fin;
1828         }
1829         key.data = keybuf;
1830         rv = NS_NOTFOUND;
1831         while (keynum >= 0) {
1832                 keynum++;
1833                 if (st->version < _PWD_CURRENT_VERSION) {
1834                         memcpy(&keybuf[1], &keynum, sizeof(keynum));
1835                         key.size = sizeof(keynum) + 1;
1836                 } else {
1837                         store = htonl(keynum);
1838                         memcpy(&keybuf[1], &store, sizeof(store));
1839                         key.size = sizeof(store) + 1;
1840                 }
1841                 keybuf[0] = _PW_VERSIONED(_PW_KEYBYNUM, st->version);
1842                 rv = st->db->get(st->db, &key, &entry, 0);
1843                 if (rv < 0 || rv > 1) { /* should never return > 1 */
1844                         *errnop = errno;
1845                         rv = NS_UNAVAIL;
1846                         goto fin;
1847                 } else if (rv == 1) {
1848                         keynum = -1;
1849                         rv = NS_NOTFOUND;
1850                         goto fin;
1851                 }
1852                 pw_name = (char *)entry.data;
1853                 switch (pw_name[0]) {
1854                 case '+':
1855                         switch (pw_name[1]) {
1856                         case '\0':
1857                                 st->compat = COMPAT_MODE_ALL;
1858                                 break;
1859                         case '@':
1860                                 setnetgrent(&pw_name[2]);
1861                                 st->compat = COMPAT_MODE_NETGROUP;
1862                                 break;
1863                         default:
1864                                 st->name = strdup(&pw_name[1]);
1865                                 if (st->name == NULL) {
1866                                         syslog(LOG_ERR,
1867                                          "getpwent memory allocation failure");
1868                                         *errnop = ENOMEM;
1869                                         rv = NS_UNAVAIL;
1870                                         break;
1871                                 }
1872                                 st->compat = COMPAT_MODE_NAME;
1873                         }
1874                         if (entry.size > bufsize) {
1875                                 *errnop = ERANGE;
1876                                 rv = NS_RETURN;
1877                                 goto fin;
1878                         }
1879                         memcpy(buffer, entry.data, entry.size);
1880                         rv = pwdb_versions[st->version].parse(buffer,
1881                             entry.size, pwd, errnop);
1882                         if (rv != NS_SUCCESS)
1883                                 ;
1884                         else if (compat_set_template(pwd, &st->template) < 0) {
1885                                 *errnop = ENOMEM;
1886                                 rv = NS_UNAVAIL;
1887                                 goto fin;
1888                         }
1889                         goto docompat;
1890                 case '-':
1891                         switch (pw_name[1]) {
1892                         case '\0':
1893                                 /* XXX Maybe syslog warning */
1894                                 continue;
1895                         case '@':
1896                                 setnetgrent(&pw_name[2]);
1897                                 while (getnetgrent(&host, &user, &domain) !=
1898                                     0) {
1899                                         if (user != NULL && user[0] != '\0')
1900                                                 compat_exclude(user,
1901                                                     &st->exclude);
1902                                 }
1903                                 endnetgrent();
1904                                 continue;
1905                         default:
1906                                 compat_exclude(&pw_name[1], &st->exclude);
1907                                 continue;
1908                         }
1909                         break;
1910                 default:
1911                         break;
1912                 }
1913                 if (compat_is_excluded((char *)entry.data, st->exclude))
1914                         continue;
1915                 rv = pwdb_versions[st->version].match(entry.data, entry.size,
1916                     how, name, uid);
1917                 if (rv == NS_RETURN)
1918                         break;
1919                 else if (rv != NS_SUCCESS)
1920                         continue;
1921                 if (entry.size > bufsize) {
1922                         *errnop = ERANGE;
1923                         rv = NS_RETURN;
1924                         break;
1925                 }
1926                 memcpy(buffer, entry.data, entry.size);
1927                 rv = pwdb_versions[st->version].parse(buffer, entry.size, pwd,
1928                     errnop);
1929                 if (rv & NS_TERMINATE)
1930                         break;
1931         }
1932 fin:
1933         if (how == nss_lt_all)
1934                 st->keynum = keynum;
1935         if (st->db != NULL && !stayopen) {
1936                 (void)st->db->close(st->db);
1937                 st->db = NULL;
1938         }
1939         if (rv == NS_SUCCESS) {
1940                 if (!from_compat) {
1941                         pwd->pw_fields &= ~_PWF_SOURCE;
1942                         pwd->pw_fields |= _PWF_FILES;
1943                 }
1944                 if (retval != NULL)
1945                         *(struct passwd **)retval = pwd;
1946         }
1947         return (rv);
1948 }
1949
1950
1951 /*
1952  * common passwd line matching and parsing
1953  */
1954 int
1955 __pw_match_entry(const char *entry, size_t entrysize, enum nss_lookup_type how,
1956     const char *name, uid_t uid)
1957 {
1958         const char      *p, *eom;
1959         char            *q;
1960         size_t           len;
1961         unsigned long    m;
1962
1963         eom = entry + entrysize;
1964         for (p = entry; p < eom; p++)
1965                 if (*p == ':')
1966                         break;
1967         if (*p != ':')
1968                 return (NS_NOTFOUND);
1969         if (how == nss_lt_all)
1970                 return (NS_SUCCESS);
1971         if (how == nss_lt_name) {
1972                 len = strlen(name);
1973                 if (len == (p - entry) && memcmp(name, entry, len) == 0)
1974                         return (NS_SUCCESS);
1975                 else
1976                         return (NS_NOTFOUND);
1977         }
1978         for (p++; p < eom; p++)
1979                 if (*p == ':')
1980                         break;
1981         if (*p != ':')
1982                 return (NS_NOTFOUND);
1983         m = strtoul(++p, &q, 10);
1984         if (q[0] != ':' || (uid_t)m != uid)
1985                 return (NS_NOTFOUND);
1986         else
1987                 return (NS_SUCCESS);
1988 }
1989
1990
1991 /* XXX buffer must be NUL-terminated.  errnop is not set correctly. */
1992 int
1993 __pw_parse_entry(char *buffer, size_t bufsize __unused, struct passwd *pwd,
1994     int master, int *errnop __unused)
1995 {
1996
1997         if (__pw_scan(buffer, pwd, master ? _PWSCAN_MASTER : 0) == 0)
1998                 return (NS_NOTFOUND);
1999         else
2000                 return (NS_SUCCESS);
2001 }