getpwent.c revision 1.49 1 /* $NetBSD: getpwent.c,v 1.49 2002/02/12 18:58:04 mycroft Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 * Portions Copyright (c) 1994, 1995, Jason Downs. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
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 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)getpwent.c 8.2 (Berkeley) 4/27/95";
41 #else
42 __RCSID("$NetBSD: getpwent.c,v 1.49 2002/02/12 18:58:04 mycroft Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45
46 #include "namespace.h"
47 #include <sys/param.h>
48
49 #include <assert.h>
50 #include <db.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <limits.h>
54 #include <netgroup.h>
55 #include <nsswitch.h>
56 #include <pwd.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <unistd.h>
61 #include <utmp.h>
62
63 #ifdef HESIOD
64 #include <hesiod.h>
65 #endif
66 #ifdef YP
67 #include <machine/param.h>
68 #include <stdio.h>
69 #include <rpc/rpc.h>
70 #include <rpcsvc/yp_prot.h>
71 #include <rpcsvc/ypclnt.h>
72 #endif
73
74 #ifdef __STDC__
75 #include <stdarg.h>
76 #else
77 #include <varargs.h>
78 #endif
79
80 #include "pw_private.h"
81
82 #if defined(YP) || defined(HESIOD)
83 #define _PASSWD_COMPAT
84 #endif
85
86 #ifdef __weak_alias
87 __weak_alias(endpwent,_endpwent)
88 __weak_alias(getpwent,_getpwent)
89 __weak_alias(getpwnam,_getpwnam)
90 __weak_alias(getpwuid,_getpwuid)
91 __weak_alias(setpassent,_setpassent)
92 __weak_alias(setpwent,_setpwent)
93 #endif
94
95
96 /*
97 * The lookup techniques and data extraction code here must be kept
98 * in sync with that in `pwd_mkdb'.
99 */
100
101 static struct passwd _pw_passwd; /* password structure */
102 static DB *_pw_db; /* password database */
103 static int _pw_keynum; /* key counter. no more records if -1 */
104 static int _pw_stayopen; /* keep fd's open */
105 static int _pw_flags; /* password flags */
106
107 static int __hashpw __P((DBT *));
108 static int __initdb __P((void));
109
110 const char __yp_token[] = "__YP!"; /* Let pwd_mkdb pull this in. */
111 static const ns_src compatsrc[] = {
112 { NSSRC_COMPAT, NS_SUCCESS },
113 { 0 }
114 };
115
116 #ifdef YP
117 static char *__ypcurrent, *__ypdomain;
118 static int __ypcurrentlen;
119 static int _pw_ypdone; /* non-zero if no more yp records */
120 #endif
121
122 #ifdef HESIOD
123 static int _pw_hesnum; /* hes counter. no more records if -1 */
124 #endif
125
126 #ifdef _PASSWD_COMPAT
127 enum _pwmode { PWMODE_NONE, PWMODE_FULL, PWMODE_USER, PWMODE_NETGRP };
128 static enum _pwmode __pwmode;
129
130 enum _ypmap { YPMAP_NONE, YPMAP_ADJUNCT, YPMAP_MASTER };
131
132 static struct passwd *__pwproto = (struct passwd *)NULL;
133 static int __pwproto_flags;
134 static char line[1024];
135 static long prbuf[1024 / sizeof(long)];
136 static DB *__pwexclude = (DB *)NULL;
137
138 static int __pwexclude_add __P((const char *));
139 static int __pwexclude_is __P((const char *));
140 static void __pwproto_set __P((void));
141 static int __ypmaptype __P((void));
142 static int __pwparse __P((struct passwd *, char *));
143
144 /* macros for deciding which YP maps to use. */
145 #define PASSWD_BYNAME (__ypmaptype() == YPMAP_MASTER \
146 ? "master.passwd.byname" : "passwd.byname")
147 #define PASSWD_BYUID (__ypmaptype() == YPMAP_MASTER \
148 ? "master.passwd.byuid" : "passwd.byuid")
149
150 /*
151 * add a name to the compat mode exclude list
152 */
153 static int
154 __pwexclude_add(name)
155 const char *name;
156 {
157 DBT key;
158 DBT data;
159
160 _DIAGASSERT(name != NULL);
161
162 /* initialize the exclusion table if needed. */
163 if(__pwexclude == (DB *)NULL) {
164 __pwexclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
165 if(__pwexclude == (DB *)NULL)
166 return 1;
167 }
168
169 /* set up the key */
170 key.size = strlen(name);
171 /* LINTED key does not get modified */
172 key.data = (char *)name;
173
174 /* data is nothing. */
175 data.data = NULL;
176 data.size = 0;
177
178 /* store it */
179 if((__pwexclude->put)(__pwexclude, &key, &data, 0) == -1)
180 return 1;
181
182 return 0;
183 }
184
185 /*
186 * test if a name is on the compat mode exclude list
187 */
188 static int
189 __pwexclude_is(name)
190 const char *name;
191 {
192 DBT key;
193 DBT data;
194
195 _DIAGASSERT(name != NULL);
196
197 if(__pwexclude == (DB *)NULL)
198 return 0; /* nothing excluded */
199
200 /* set up the key */
201 key.size = strlen(name);
202 /* LINTED key does not get modified */
203 key.data = (char *)name;
204
205 if((__pwexclude->get)(__pwexclude, &key, &data, 0) == 0)
206 return 1; /* excluded */
207
208 return 0;
209 }
210
211 /*
212 * setup the compat mode prototype template
213 */
214 static void
215 __pwproto_set()
216 {
217 char *ptr;
218 struct passwd *pw = &_pw_passwd;
219
220 /* make this the new prototype */
221 ptr = (char *)(void *)prbuf;
222
223 /* first allocate the struct. */
224 __pwproto = (struct passwd *)(void *)ptr;
225 ptr += sizeof(struct passwd);
226
227 /* name */
228 if(pw->pw_name && (pw->pw_name)[0]) {
229 ptr = (char *)ALIGN((u_long)ptr);
230 memmove(ptr, pw->pw_name, strlen(pw->pw_name) + 1);
231 __pwproto->pw_name = ptr;
232 ptr += (strlen(pw->pw_name) + 1);
233 } else
234 __pwproto->pw_name = (char *)NULL;
235
236 /* password */
237 if(pw->pw_passwd && (pw->pw_passwd)[0]) {
238 ptr = (char *)ALIGN((u_long)ptr);
239 memmove(ptr, pw->pw_passwd, strlen(pw->pw_passwd) + 1);
240 __pwproto->pw_passwd = ptr;
241 ptr += (strlen(pw->pw_passwd) + 1);
242 } else
243 __pwproto->pw_passwd = (char *)NULL;
244
245 /* uid */
246 __pwproto->pw_uid = pw->pw_uid;
247
248 /* gid */
249 __pwproto->pw_gid = pw->pw_gid;
250
251 /* change (ignored anyway) */
252 __pwproto->pw_change = pw->pw_change;
253
254 /* class (ignored anyway) */
255 __pwproto->pw_class = "";
256
257 /* gecos */
258 if(pw->pw_gecos && (pw->pw_gecos)[0]) {
259 ptr = (char *)ALIGN((u_long)ptr);
260 memmove(ptr, pw->pw_gecos, strlen(pw->pw_gecos) + 1);
261 __pwproto->pw_gecos = ptr;
262 ptr += (strlen(pw->pw_gecos) + 1);
263 } else
264 __pwproto->pw_gecos = (char *)NULL;
265
266 /* dir */
267 if(pw->pw_dir && (pw->pw_dir)[0]) {
268 ptr = (char *)ALIGN((u_long)ptr);
269 memmove(ptr, pw->pw_dir, strlen(pw->pw_dir) + 1);
270 __pwproto->pw_dir = ptr;
271 ptr += (strlen(pw->pw_dir) + 1);
272 } else
273 __pwproto->pw_dir = (char *)NULL;
274
275 /* shell */
276 if(pw->pw_shell && (pw->pw_shell)[0]) {
277 ptr = (char *)ALIGN((u_long)ptr);
278 memmove(ptr, pw->pw_shell, strlen(pw->pw_shell) + 1);
279 __pwproto->pw_shell = ptr;
280 ptr += (strlen(pw->pw_shell) + 1);
281 } else
282 __pwproto->pw_shell = (char *)NULL;
283
284 /* expire (ignored anyway) */
285 __pwproto->pw_expire = pw->pw_expire;
286
287 /* flags */
288 __pwproto_flags = _pw_flags;
289 }
290
291 static int
292 __ypmaptype()
293 {
294 static int maptype = -1;
295 int order, r;
296
297 if (maptype != -1)
298 return (maptype);
299
300 maptype = YPMAP_NONE;
301 if (geteuid() != 0)
302 return (maptype);
303
304 if (!__ypdomain) {
305 if( _yp_check(&__ypdomain) == 0)
306 return (maptype);
307 }
308
309 r = yp_order(__ypdomain, "master.passwd.byname", &order);
310 if (r == 0) {
311 maptype = YPMAP_MASTER;
312 return (maptype);
313 }
314
315 /*
316 * NIS+ in YP compat mode doesn't support
317 * YPPROC_ORDER -- no point in continuing.
318 */
319 if (r == YPERR_YPERR)
320 return (maptype);
321
322 /* master.passwd doesn't exist -- try passwd.adjunct */
323 if (r == YPERR_MAP) {
324 r = yp_order(__ypdomain, "passwd.adjunct.byname", &order);
325 if (r == 0)
326 maptype = YPMAP_ADJUNCT;
327 return (maptype);
328 }
329
330 return (maptype);
331 }
332
333 /*
334 * parse a passwd file line (from NIS or HESIOD).
335 * assumed to be `old-style' if maptype != YPMAP_MASTER.
336 */
337 static int
338 __pwparse(pw, s)
339 struct passwd *pw;
340 char *s;
341 {
342 static char adjunctpw[YPMAXRECORD + 2];
343 int flags, maptype;
344
345 _DIAGASSERT(pw != NULL);
346 _DIAGASSERT(s != NULL);
347
348 maptype = __ypmaptype();
349 flags = _PASSWORD_NOWARN;
350 if (maptype != YPMAP_MASTER)
351 flags |= _PASSWORD_OLDFMT;
352 if (! __pw_scan(s, pw, &flags))
353 return 1;
354
355 /* now let the prototype override, if set. */
356 if(__pwproto != (struct passwd *)NULL) {
357 #ifdef PW_OVERRIDE_PASSWD
358 if(__pwproto->pw_passwd != (char *)NULL)
359 pw->pw_passwd = __pwproto->pw_passwd;
360 #endif
361 if(!(__pwproto_flags & _PASSWORD_NOUID))
362 pw->pw_uid = __pwproto->pw_uid;
363 if(!(__pwproto_flags & _PASSWORD_NOGID))
364 pw->pw_gid = __pwproto->pw_gid;
365 if(__pwproto->pw_gecos != (char *)NULL)
366 pw->pw_gecos = __pwproto->pw_gecos;
367 if(__pwproto->pw_dir != (char *)NULL)
368 pw->pw_dir = __pwproto->pw_dir;
369 if(__pwproto->pw_shell != (char *)NULL)
370 pw->pw_shell = __pwproto->pw_shell;
371 }
372 if ((maptype == YPMAP_ADJUNCT) &&
373 (strstr(pw->pw_passwd, "##") != NULL)) {
374 char *data, *bp;
375 int datalen;
376
377 if (yp_match(__ypdomain, "passwd.adjunct.byname", pw->pw_name,
378 (int)strlen(pw->pw_name), &data, &datalen) == 0) {
379 if (datalen > sizeof(adjunctpw) - 1)
380 datalen = sizeof(adjunctpw) - 1;
381 strncpy(adjunctpw, data, (size_t)datalen);
382
383 /* skip name to get password */
384 if ((bp = strsep(&data, ":")) != NULL &&
385 (bp = strsep(&data, ":")) != NULL)
386 pw->pw_passwd = bp;
387 }
388 }
389 return 0;
390 }
391 #endif /* _PASSWD_COMPAT */
392
393 /*
394 * local files implementation of getpw*()
395 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
396 */
397 static int _local_getpw __P((void *, void *, va_list));
398
399 /*ARGSUSED*/
400 static int
401 _local_getpw(rv, cb_data, ap)
402 void *rv;
403 void *cb_data;
404 va_list ap;
405 {
406 DBT key;
407 char bf[/*CONSTCOND*/ MAX(MAXLOGNAME, sizeof(_pw_keynum)) + 1];
408 uid_t uid;
409 size_t len;
410 int search, rval;
411 const char *name;
412
413 if (!_pw_db && !__initdb())
414 return NS_UNAVAIL;
415
416 search = va_arg(ap, int);
417 bf[0] = search;
418 switch (search) {
419 case _PW_KEYBYNUM:
420 if (_pw_keynum == -1)
421 return NS_NOTFOUND; /* no more local records */
422 ++_pw_keynum;
423 memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
424 key.size = sizeof(_pw_keynum) + 1;
425 break;
426 case _PW_KEYBYNAME:
427 name = va_arg(ap, const char *);
428 len = strlen(name);
429 if (len > MAXLOGNAME)
430 return NS_NOTFOUND;
431 memmove(bf + 1, name, len);
432 key.size = len + 1;
433 break;
434 case _PW_KEYBYUID:
435 uid = va_arg(ap, uid_t);
436 memmove(bf + 1, &uid, sizeof(uid));
437 key.size = sizeof(uid) + 1;
438 break;
439 default:
440 abort();
441 }
442
443 key.data = (u_char *)bf;
444 rval = __hashpw(&key);
445 if (rval == NS_NOTFOUND && search == _PW_KEYBYNUM)
446 _pw_keynum = -1; /* flag `no more local records' */
447
448 if (!_pw_stayopen && (search != _PW_KEYBYNUM)) {
449 (void)(_pw_db->close)(_pw_db);
450 _pw_db = (DB *)NULL;
451 }
452 return (rval);
453 }
454
455 #ifdef HESIOD
456 /*
457 * hesiod implementation of getpw*()
458 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
459 */
460 static int _dns_getpw __P((void *, void *, va_list));
461
462 /*ARGSUSED*/
463 static int
464 _dns_getpw(rv, cb_data, ap)
465 void *rv;
466 void *cb_data;
467 va_list ap;
468 {
469 const char *name;
470 uid_t uid;
471 int search;
472
473 const char *map;
474 char **hp;
475 void *context;
476 int r;
477
478 search = va_arg(ap, int);
479 nextdnsbynum:
480 switch (search) {
481 case _PW_KEYBYNUM:
482 if (_pw_hesnum == -1)
483 return NS_NOTFOUND; /* no more hesiod records */
484 snprintf(line, sizeof(line) - 1, "passwd-%u", _pw_hesnum);
485 _pw_hesnum++;
486 map = "passwd";
487 break;
488 case _PW_KEYBYNAME:
489 name = va_arg(ap, const char *);
490 strncpy(line, name, sizeof(line));
491 map = "passwd";
492 break;
493 case _PW_KEYBYUID:
494 uid = va_arg(ap, uid_t);
495 snprintf(line, sizeof(line), "%u", (unsigned int)uid);
496 map = "uid"; /* XXX this is `passwd' on ultrix */
497 break;
498 default:
499 abort();
500 }
501 line[sizeof(line) - 1] = '\0';
502
503 r = NS_UNAVAIL;
504 if (hesiod_init(&context) == -1)
505 return (r);
506
507 hp = hesiod_resolve(context, line, map);
508 if (hp == NULL) {
509 if (errno == ENOENT) {
510 /* flag `no more hesiod records' */
511 if (search == _PW_KEYBYNUM)
512 _pw_hesnum = -1;
513 r = NS_NOTFOUND;
514 }
515 goto cleanup_dns_getpw;
516 }
517
518 strncpy(line, hp[0], sizeof(line)); /* only check first elem */
519 line[sizeof(line) - 1] = '\0';
520 hesiod_free_list(context, hp);
521 if (__pwparse(&_pw_passwd, line)) {
522 if (search == _PW_KEYBYNUM)
523 goto nextdnsbynum; /* skip dogdy entries */
524 r = NS_UNAVAIL;
525 } else
526 r = NS_SUCCESS;
527 cleanup_dns_getpw:
528 hesiod_end(context);
529 return (r);
530 }
531 #endif
532
533 #ifdef YP
534 /*
535 * nis implementation of getpw*()
536 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
537 */
538 static int _nis_getpw __P((void *, void *, va_list));
539
540 /*ARGSUSED*/
541 static int
542 _nis_getpw(rv, cb_data, ap)
543 void *rv;
544 void *cb_data;
545 va_list ap;
546 {
547 const char *name;
548 uid_t uid;
549 int search;
550 char *key, *data;
551 const char *map;
552 int keylen, datalen, r, rval;
553
554 if(__ypdomain == NULL) {
555 if(_yp_check(&__ypdomain) == 0)
556 return NS_UNAVAIL;
557 }
558
559 map = PASSWD_BYNAME;
560 search = va_arg(ap, int);
561 switch (search) {
562 case _PW_KEYBYNUM:
563 break;
564 case _PW_KEYBYNAME:
565 name = va_arg(ap, const char *);
566 strncpy(line, name, sizeof(line));
567 break;
568 case _PW_KEYBYUID:
569 uid = va_arg(ap, uid_t);
570 snprintf(line, sizeof(line), "%u", (unsigned int)uid);
571 map = PASSWD_BYUID;
572 break;
573 default:
574 abort();
575 }
576 line[sizeof(line) - 1] = '\0';
577 rval = NS_UNAVAIL;
578 if (search != _PW_KEYBYNUM) {
579 data = NULL;
580 r = yp_match(__ypdomain, map, line, (int)strlen(line),
581 &data, &datalen);
582 if (r == YPERR_KEY)
583 rval = NS_NOTFOUND;
584 if (r != 0) {
585 if (data)
586 free(data);
587 return (rval);
588 }
589 data[datalen] = '\0'; /* clear trailing \n */
590 strncpy(line, data, sizeof(line));
591 line[sizeof(line) - 1] = '\0';
592 free(data);
593 if (__pwparse(&_pw_passwd, line))
594 return NS_UNAVAIL;
595 return NS_SUCCESS;
596 }
597
598 if (_pw_ypdone)
599 return NS_NOTFOUND;
600 for (;;) {
601 data = key = NULL;
602 if (__ypcurrent) {
603 r = yp_next(__ypdomain, map,
604 __ypcurrent, __ypcurrentlen,
605 &key, &keylen, &data, &datalen);
606 free(__ypcurrent);
607 switch (r) {
608 case 0:
609 __ypcurrent = key;
610 __ypcurrentlen = keylen;
611 break;
612 case YPERR_NOMORE:
613 __ypcurrent = NULL;
614 /* flag `no more yp records' */
615 _pw_ypdone = 1;
616 rval = NS_NOTFOUND;
617 }
618 } else {
619 r = yp_first(__ypdomain, map, &__ypcurrent,
620 &__ypcurrentlen, &data, &datalen);
621 }
622 if (r != 0) {
623 if (key)
624 free(key);
625 if (data)
626 free(data);
627 return (rval);
628 }
629 data[datalen] = '\0'; /* clear trailing \n */
630 strncpy(line, data, sizeof(line));
631 line[sizeof(line) - 1] = '\0';
632 free(data);
633 if (! __pwparse(&_pw_passwd, line))
634 return NS_SUCCESS;
635 }
636 /* NOTREACHED */
637 } /* _nis_getpw */
638 #endif
639
640 #ifdef _PASSWD_COMPAT
641 /*
642 * See if the compat token is in the database. Only works if pwd_mkdb knows
643 * about the token.
644 */
645 static int __has_compatpw __P((void));
646
647 static int
648 __has_compatpw()
649 {
650 DBT key, data;
651 DBT pkey, pdata;
652 char bf[MAXLOGNAME];
653
654 /*LINTED*/
655 key.data = (u_char *)__yp_token;
656 key.size = strlen(__yp_token);
657
658 /* Pre-token database support. */
659 bf[0] = _PW_KEYBYNAME;
660 bf[1] = '+';
661 pkey.data = (u_char *)bf;
662 pkey.size = 2;
663
664 if ((_pw_db->get)(_pw_db, &key, &data, 0)
665 && (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
666 return 0; /* No compat token */
667 return 1;
668 }
669
670 /*
671 * log an error if "files" or "compat" is specified in passwd_compat database
672 */
673 static int _bad_getpw __P((void *, void *, va_list));
674
675 /*ARGSUSED*/
676 static int
677 _bad_getpw(rv, cb_data, ap)
678 void *rv;
679 void *cb_data;
680 va_list ap;
681 {
682 static int warned;
683
684 _DIAGASSERT(cb_data != NULL);
685
686 if (!warned) {
687 syslog(LOG_ERR,
688 "nsswitch.conf passwd_compat database can't use '%s'",
689 (char *)cb_data);
690 }
691 warned = 1;
692 return NS_UNAVAIL;
693 }
694
695 /*
696 * when a name lookup in compat mode is required (e.g., '+name', or a name in
697 * '+@netgroup'), look it up in the 'passwd_compat' nsswitch database.
698 * only Hesiod and NIS is supported - it doesn't make sense to lookup
699 * compat names from 'files' or 'compat'.
700 */
701 static int __getpwcompat __P((int, uid_t, const char *));
702
703 static int
704 __getpwcompat(type, uid, name)
705 int type;
706 uid_t uid;
707 const char *name;
708 {
709 static const ns_dtab dtab[] = {
710 NS_FILES_CB(_bad_getpw, "files")
711 NS_DNS_CB(_dns_getpw, NULL)
712 NS_NIS_CB(_nis_getpw, NULL)
713 NS_COMPAT_CB(_bad_getpw, "compat")
714 { 0 }
715 };
716 static const ns_src defaultnis[] = {
717 { NSSRC_NIS, NS_SUCCESS },
718 { 0 }
719 };
720
721 switch (type) {
722 case _PW_KEYBYNUM:
723 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
724 defaultnis, type);
725 case _PW_KEYBYNAME:
726 _DIAGASSERT(name != NULL);
727 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
728 defaultnis, type, name);
729 case _PW_KEYBYUID:
730 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
731 defaultnis, type, uid);
732 default:
733 abort();
734 /*NOTREACHED*/
735 }
736 }
737 #endif /* _PASSWD_COMPAT */
738
739 /*
740 * compat implementation of getpwent()
741 * varargs (ignored):
742 * type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
743 */
744 static int _compat_getpwent __P((void *, void *, va_list));
745
746 /*ARGSUSED*/
747 static int
748 _compat_getpwent(rv, cb_data, ap)
749 void *rv;
750 void *cb_data;
751 va_list ap;
752 {
753 DBT key;
754 int rval;
755 char bf[sizeof(_pw_keynum) + 1];
756 #ifdef _PASSWD_COMPAT
757 static char *name = NULL;
758 const char *user, *host, *dom;
759 int has_compatpw;
760 #endif
761
762 if (!_pw_db && !__initdb())
763 return NS_UNAVAIL;
764
765 #ifdef _PASSWD_COMPAT
766 has_compatpw = __has_compatpw();
767
768 again:
769 if (has_compatpw && (__pwmode != PWMODE_NONE)) {
770 int r;
771
772 switch (__pwmode) {
773 case PWMODE_FULL:
774 r = __getpwcompat(_PW_KEYBYNUM, 0, NULL);
775 if (r == NS_SUCCESS)
776 return r;
777 __pwmode = PWMODE_NONE;
778 break;
779
780 case PWMODE_NETGRP:
781 r = getnetgrent(&host, &user, &dom);
782 if (r == 0) { /* end of group */
783 endnetgrent();
784 __pwmode = PWMODE_NONE;
785 break;
786 }
787 if (!user || !*user)
788 break;
789 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
790 if (r == NS_SUCCESS)
791 return r;
792 break;
793
794 case PWMODE_USER:
795 if (name == NULL) {
796 __pwmode = PWMODE_NONE;
797 break;
798 }
799 r = __getpwcompat(_PW_KEYBYNAME, 0, name);
800 free(name);
801 name = NULL;
802 if (r == NS_SUCCESS)
803 return r;
804 break;
805
806 case PWMODE_NONE:
807 abort();
808 }
809 goto again;
810 }
811 #endif
812
813 if (_pw_keynum == -1)
814 return NS_NOTFOUND; /* no more local records */
815 ++_pw_keynum;
816 bf[0] = _PW_KEYBYNUM;
817 memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
818 key.data = (u_char *)bf;
819 key.size = sizeof(_pw_keynum) + 1;
820 rval = __hashpw(&key);
821 if (rval == NS_NOTFOUND)
822 _pw_keynum = -1; /* flag `no more local records' */
823 else if (rval == NS_SUCCESS) {
824 #ifdef _PASSWD_COMPAT
825 /* if we don't have YP at all, don't bother. */
826 if (has_compatpw) {
827 if(_pw_passwd.pw_name[0] == '+') {
828 /* set the mode */
829 switch(_pw_passwd.pw_name[1]) {
830 case '\0':
831 __pwmode = PWMODE_FULL;
832 break;
833 case '@':
834 __pwmode = PWMODE_NETGRP;
835 setnetgrent(_pw_passwd.pw_name + 2);
836 break;
837 default:
838 __pwmode = PWMODE_USER;
839 name = strdup(_pw_passwd.pw_name + 1);
840 break;
841 }
842
843 /* save the prototype */
844 __pwproto_set();
845 goto again;
846 } else if(_pw_passwd.pw_name[0] == '-') {
847 /* an attempted exclusion */
848 switch(_pw_passwd.pw_name[1]) {
849 case '\0':
850 break;
851 case '@':
852 setnetgrent(_pw_passwd.pw_name + 2);
853 while(getnetgrent(&host, &user, &dom)) {
854 if(user && *user)
855 __pwexclude_add(user);
856 }
857 endnetgrent();
858 break;
859 default:
860 __pwexclude_add(_pw_passwd.pw_name + 1);
861 break;
862 }
863 goto again;
864 }
865 }
866 #endif
867 }
868 return (rval);
869 }
870
871 /*
872 * compat implementation of getpwnam() and getpwuid()
873 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
874 */
875 static int _compat_getpw __P((void *, void *, va_list));
876
877 static int
878 _compat_getpw(rv, cb_data, ap)
879 void *rv;
880 void *cb_data;
881 va_list ap;
882 {
883 #ifdef _PASSWD_COMPAT
884 DBT key;
885 int search, rval, r, s, keynum;
886 uid_t uid;
887 char bf[sizeof(keynum) + 1];
888 const char *name, *host, *user, *dom;
889 #endif
890
891 if (!_pw_db && !__initdb())
892 return NS_UNAVAIL;
893
894 /*
895 * If there isn't a compat token in the database, use files.
896 */
897 #ifdef _PASSWD_COMPAT
898 if (! __has_compatpw())
899 #endif
900 return (_local_getpw(rv, cb_data, ap));
901
902 #ifdef _PASSWD_COMPAT
903 search = va_arg(ap, int);
904 uid = 0;
905 name = NULL;
906 rval = NS_NOTFOUND;
907 switch (search) {
908 case _PW_KEYBYNAME:
909 name = va_arg(ap, const char *);
910 break;
911 case _PW_KEYBYUID:
912 uid = va_arg(ap, uid_t);
913 break;
914 default:
915 abort();
916 }
917
918 for (s = -1, keynum = 1 ; ; keynum++) {
919 bf[0] = _PW_KEYBYNUM;
920 memmove(bf + 1, &keynum, sizeof(keynum));
921 key.data = (u_char *)bf;
922 key.size = sizeof(keynum) + 1;
923 if(__hashpw(&key) != NS_SUCCESS)
924 break;
925 switch(_pw_passwd.pw_name[0]) {
926 case '+':
927 /* save the prototype */
928 __pwproto_set();
929
930 switch(_pw_passwd.pw_name[1]) {
931 case '\0':
932 r = __getpwcompat(search, uid, name);
933 if (r != NS_SUCCESS)
934 continue;
935 break;
936 case '@':
937 pwnam_netgrp:
938 #if 0 /* XXX: is this a hangover from pre-nsswitch? */
939 if(__ypcurrent) {
940 free(__ypcurrent);
941 __ypcurrent = NULL;
942 }
943 #endif
944 if (s == -1) /* first time */
945 setnetgrent(_pw_passwd.pw_name + 2);
946 s = getnetgrent(&host, &user, &dom);
947 if (s == 0) { /* end of group */
948 endnetgrent();
949 s = -1;
950 continue;
951 }
952 if (!user || !*user)
953 goto pwnam_netgrp;
954
955 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
956
957 if (r == NS_UNAVAIL)
958 return r;
959 if (r == NS_NOTFOUND) {
960 /*
961 * just because this user is bad
962 * it doesn't mean they all are.
963 */
964 goto pwnam_netgrp;
965 }
966 break;
967 default:
968 user = _pw_passwd.pw_name + 1;
969 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
970
971 if (r == NS_UNAVAIL)
972 return r;
973 if (r == NS_NOTFOUND)
974 continue;
975 break;
976 }
977 if(__pwexclude_is(_pw_passwd.pw_name)) {
978 if(s == 1) /* inside netgroup */
979 goto pwnam_netgrp;
980 continue;
981 }
982 break;
983 case '-':
984 /* attempted exclusion */
985 switch(_pw_passwd.pw_name[1]) {
986 case '\0':
987 break;
988 case '@':
989 setnetgrent(_pw_passwd.pw_name + 2);
990 while(getnetgrent(&host, &user, &dom)) {
991 if(user && *user)
992 __pwexclude_add(user);
993 }
994 endnetgrent();
995 break;
996 default:
997 __pwexclude_add(_pw_passwd.pw_name + 1);
998 break;
999 }
1000 break;
1001 }
1002 if ((search == _PW_KEYBYNAME &&
1003 strcmp(_pw_passwd.pw_name, name) == 0)
1004 || (search == _PW_KEYBYUID && _pw_passwd.pw_uid == uid)) {
1005 rval = NS_SUCCESS;
1006 break;
1007 }
1008 if(s == 1) /* inside netgroup */
1009 goto pwnam_netgrp;
1010 continue;
1011 }
1012 __pwproto = (struct passwd *)NULL;
1013
1014 if (!_pw_stayopen) {
1015 (void)(_pw_db->close)(_pw_db);
1016 _pw_db = (DB *)NULL;
1017 }
1018 if(__pwexclude != (DB *)NULL) {
1019 (void)(__pwexclude->close)(__pwexclude);
1020 __pwexclude = (DB *)NULL;
1021 }
1022 return rval;
1023 #endif /* _PASSWD_COMPAT */
1024 }
1025
1026 struct passwd *
1027 getpwent()
1028 {
1029 int r;
1030 static const ns_dtab dtab[] = {
1031 NS_FILES_CB(_local_getpw, NULL)
1032 NS_DNS_CB(_dns_getpw, NULL)
1033 NS_NIS_CB(_nis_getpw, NULL)
1034 NS_COMPAT_CB(_compat_getpwent, NULL)
1035 { 0 }
1036 };
1037
1038 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", compatsrc,
1039 _PW_KEYBYNUM);
1040 if (r != NS_SUCCESS)
1041 return (struct passwd *)NULL;
1042 return &_pw_passwd;
1043 }
1044
1045 struct passwd *
1046 getpwnam(name)
1047 const char *name;
1048 {
1049 int r;
1050 static const ns_dtab dtab[] = {
1051 NS_FILES_CB(_local_getpw, NULL)
1052 NS_DNS_CB(_dns_getpw, NULL)
1053 NS_NIS_CB(_nis_getpw, NULL)
1054 NS_COMPAT_CB(_compat_getpw, NULL)
1055 { 0 }
1056 };
1057
1058 if (name == NULL || name[0] == '\0')
1059 return (struct passwd *)NULL;
1060
1061 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", compatsrc,
1062 _PW_KEYBYNAME, name);
1063 return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1064 }
1065
1066 struct passwd *
1067 getpwuid(uid)
1068 uid_t uid;
1069 {
1070 int r;
1071 static const ns_dtab dtab[] = {
1072 NS_FILES_CB(_local_getpw, NULL)
1073 NS_DNS_CB(_dns_getpw, NULL)
1074 NS_NIS_CB(_nis_getpw, NULL)
1075 NS_COMPAT_CB(_compat_getpw, NULL)
1076 { 0 }
1077 };
1078
1079 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", compatsrc,
1080 _PW_KEYBYUID, uid);
1081 return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1082 }
1083
1084 int
1085 setpassent(stayopen)
1086 int stayopen;
1087 {
1088 _pw_keynum = 0;
1089 _pw_stayopen = stayopen;
1090 #ifdef YP
1091 __pwmode = PWMODE_NONE;
1092 if(__ypcurrent)
1093 free(__ypcurrent);
1094 __ypcurrent = NULL;
1095 _pw_ypdone = 0;
1096 #endif
1097 #ifdef HESIOD
1098 _pw_hesnum = 0;
1099 #endif
1100 #ifdef _PASSWD_COMPAT
1101 if(__pwexclude != (DB *)NULL) {
1102 (void)(__pwexclude->close)(__pwexclude);
1103 __pwexclude = (DB *)NULL;
1104 }
1105 __pwproto = (struct passwd *)NULL;
1106 #endif
1107 return 1;
1108 }
1109
1110 void
1111 setpwent()
1112 {
1113 (void) setpassent(0);
1114 }
1115
1116 void
1117 endpwent()
1118 {
1119 _pw_keynum = 0;
1120 if (_pw_db) {
1121 (void)(_pw_db->close)(_pw_db);
1122 _pw_db = (DB *)NULL;
1123 }
1124 #ifdef _PASSWD_COMPAT
1125 __pwmode = PWMODE_NONE;
1126 #endif
1127 #ifdef YP
1128 if(__ypcurrent)
1129 free(__ypcurrent);
1130 __ypcurrent = NULL;
1131 _pw_ypdone = 0;
1132 #endif
1133 #ifdef HESIOD
1134 _pw_hesnum = 0;
1135 #endif
1136 #ifdef _PASSWD_COMPAT
1137 if(__pwexclude != (DB *)NULL) {
1138 (void)(__pwexclude->close)(__pwexclude);
1139 __pwexclude = (DB *)NULL;
1140 }
1141 __pwproto = (struct passwd *)NULL;
1142 #endif
1143 }
1144
1145 static int
1146 __initdb()
1147 {
1148 static int warned;
1149 char *p;
1150
1151 #ifdef _PASSWD_COMPAT
1152 __pwmode = PWMODE_NONE;
1153 #endif
1154 if (geteuid() == 0) {
1155 _pw_db = dbopen((p = _PATH_SMP_DB), O_RDONLY, 0, DB_HASH, NULL);
1156 if (_pw_db)
1157 return(1);
1158 }
1159 _pw_db = dbopen((p = _PATH_MP_DB), O_RDONLY, 0, DB_HASH, NULL);
1160 if (_pw_db)
1161 return 1;
1162 if (!warned)
1163 syslog(LOG_ERR, "%s: %m", p);
1164 warned = 1;
1165 return 0;
1166 }
1167
1168 static int
1169 __hashpw(key)
1170 DBT *key;
1171 {
1172 char *p, *t, *oldbuf;
1173 static u_int max;
1174 static char *buf;
1175 DBT data;
1176
1177 _DIAGASSERT(key != NULL);
1178
1179 switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
1180 case 0:
1181 break; /* found */
1182 case 1:
1183 return NS_NOTFOUND;
1184 case -1:
1185 return NS_UNAVAIL; /* error in db routines */
1186 default:
1187 abort();
1188 }
1189
1190 p = (char *)data.data;
1191 if (data.size > max) {
1192 max = roundup(data.size, 1024);
1193 oldbuf = buf;
1194 if ((buf = realloc(buf, max)) == NULL) {
1195 if (oldbuf != NULL)
1196 free(oldbuf);
1197 max = 0;
1198 return NS_UNAVAIL;
1199 }
1200 }
1201
1202 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1203 t = buf;
1204 #define EXPAND(e) e = t; while ((*t++ = *p++));
1205 #define SCALAR(v) memmove(&(v), p, sizeof v); p += sizeof v
1206 EXPAND(_pw_passwd.pw_name);
1207 EXPAND(_pw_passwd.pw_passwd);
1208 SCALAR(_pw_passwd.pw_uid);
1209 SCALAR(_pw_passwd.pw_gid);
1210 SCALAR(_pw_passwd.pw_change);
1211 EXPAND(_pw_passwd.pw_class);
1212 EXPAND(_pw_passwd.pw_gecos);
1213 EXPAND(_pw_passwd.pw_dir);
1214 EXPAND(_pw_passwd.pw_shell);
1215 SCALAR(_pw_passwd.pw_expire);
1216
1217 /* See if there's any data left. If so, read in flags. */
1218 if (data.size > (p - (char *)data.data)) {
1219 SCALAR(_pw_flags);
1220 } else
1221 _pw_flags = _PASSWORD_NOUID|_PASSWORD_NOGID; /* default */
1222
1223 return NS_SUCCESS;
1224 }
1225