getpwent.c revision 1.46 1 /* $NetBSD: getpwent.c,v 1.46 2000/01/22 22:19:11 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.46 2000/01/22 22:19:11 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 int search, len, rval;
410 const char *name;
411
412 if (!_pw_db && !__initdb())
413 return NS_UNAVAIL;
414
415 search = va_arg(ap, int);
416 bf[0] = search;
417 switch (search) {
418 case _PW_KEYBYNUM:
419 if (_pw_keynum == -1)
420 return NS_NOTFOUND; /* no more local records */
421 ++_pw_keynum;
422 memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
423 key.size = sizeof(_pw_keynum) + 1;
424 break;
425 case _PW_KEYBYNAME:
426 name = va_arg(ap, const char *);
427 len = strlen(name);
428 memmove(bf + 1, name, (size_t)MIN(len, MAXLOGNAME));
429 key.size = len + 1;
430 break;
431 case _PW_KEYBYUID:
432 uid = va_arg(ap, uid_t);
433 memmove(bf + 1, &uid, sizeof(len));
434 key.size = sizeof(uid) + 1;
435 break;
436 default:
437 abort();
438 }
439
440 key.data = (u_char *)bf;
441 rval = __hashpw(&key);
442 if (rval == NS_NOTFOUND && search == _PW_KEYBYNUM)
443 _pw_keynum = -1; /* flag `no more local records' */
444
445 if (!_pw_stayopen && (search != _PW_KEYBYNUM)) {
446 (void)(_pw_db->close)(_pw_db);
447 _pw_db = (DB *)NULL;
448 }
449 return (rval);
450 }
451
452 #ifdef HESIOD
453 /*
454 * hesiod implementation of getpw*()
455 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
456 */
457 static int _dns_getpw __P((void *, void *, va_list));
458
459 /*ARGSUSED*/
460 static int
461 _dns_getpw(rv, cb_data, ap)
462 void *rv;
463 void *cb_data;
464 va_list ap;
465 {
466 const char *name;
467 uid_t uid;
468 int search;
469
470 const char *map;
471 char **hp;
472 void *context;
473 int r;
474
475 search = va_arg(ap, int);
476 nextdnsbynum:
477 switch (search) {
478 case _PW_KEYBYNUM:
479 if (_pw_hesnum == -1)
480 return NS_NOTFOUND; /* no more hesiod records */
481 snprintf(line, sizeof(line) - 1, "passwd-%u", _pw_hesnum);
482 _pw_hesnum++;
483 map = "passwd";
484 break;
485 case _PW_KEYBYNAME:
486 name = va_arg(ap, const char *);
487 strncpy(line, name, sizeof(line));
488 map = "passwd";
489 break;
490 case _PW_KEYBYUID:
491 uid = va_arg(ap, uid_t);
492 snprintf(line, sizeof(line), "%u", (unsigned int)uid);
493 map = "uid"; /* XXX this is `passwd' on ultrix */
494 break;
495 default:
496 abort();
497 }
498 line[sizeof(line) - 1] = '\0';
499
500 r = NS_UNAVAIL;
501 if (hesiod_init(&context) == -1)
502 return (r);
503
504 hp = hesiod_resolve(context, line, map);
505 if (hp == NULL) {
506 if (errno == ENOENT) {
507 /* flag `no more hesiod records' */
508 if (search == _PW_KEYBYNUM)
509 _pw_hesnum = -1;
510 r = NS_NOTFOUND;
511 }
512 goto cleanup_dns_getpw;
513 }
514
515 strncpy(line, hp[0], sizeof(line)); /* only check first elem */
516 line[sizeof(line) - 1] = '\0';
517 hesiod_free_list(context, hp);
518 if (__pwparse(&_pw_passwd, line)) {
519 if (search == _PW_KEYBYNUM)
520 goto nextdnsbynum; /* skip dogdy entries */
521 r = NS_UNAVAIL;
522 } else
523 r = NS_SUCCESS;
524 cleanup_dns_getpw:
525 hesiod_end(context);
526 return (r);
527 }
528 #endif
529
530 #ifdef YP
531 /*
532 * nis implementation of getpw*()
533 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
534 */
535 static int _nis_getpw __P((void *, void *, va_list));
536
537 /*ARGSUSED*/
538 static int
539 _nis_getpw(rv, cb_data, ap)
540 void *rv;
541 void *cb_data;
542 va_list ap;
543 {
544 const char *name;
545 uid_t uid;
546 int search;
547 char *key, *data;
548 const char *map;
549 int keylen, datalen, r, rval;
550
551 if(__ypdomain == NULL) {
552 if(_yp_check(&__ypdomain) == 0)
553 return NS_UNAVAIL;
554 }
555
556 map = PASSWD_BYNAME;
557 search = va_arg(ap, int);
558 switch (search) {
559 case _PW_KEYBYNUM:
560 break;
561 case _PW_KEYBYNAME:
562 name = va_arg(ap, const char *);
563 strncpy(line, name, sizeof(line));
564 break;
565 case _PW_KEYBYUID:
566 uid = va_arg(ap, uid_t);
567 snprintf(line, sizeof(line), "%u", (unsigned int)uid);
568 map = PASSWD_BYUID;
569 break;
570 default:
571 abort();
572 }
573 line[sizeof(line) - 1] = '\0';
574 rval = NS_UNAVAIL;
575 if (search != _PW_KEYBYNUM) {
576 data = NULL;
577 r = yp_match(__ypdomain, map, line, (int)strlen(line),
578 &data, &datalen);
579 if (r == YPERR_KEY)
580 rval = NS_NOTFOUND;
581 if (r != 0) {
582 if (data)
583 free(data);
584 return (rval);
585 }
586 data[datalen] = '\0'; /* clear trailing \n */
587 strncpy(line, data, sizeof(line));
588 line[sizeof(line) - 1] = '\0';
589 free(data);
590 if (__pwparse(&_pw_passwd, line))
591 return NS_UNAVAIL;
592 return NS_SUCCESS;
593 }
594
595 if (_pw_ypdone)
596 return NS_NOTFOUND;
597 for (;;) {
598 data = key = NULL;
599 if (__ypcurrent) {
600 r = yp_next(__ypdomain, map,
601 __ypcurrent, __ypcurrentlen,
602 &key, &keylen, &data, &datalen);
603 free(__ypcurrent);
604 switch (r) {
605 case 0:
606 __ypcurrent = key;
607 __ypcurrentlen = keylen;
608 break;
609 case YPERR_NOMORE:
610 __ypcurrent = NULL;
611 /* flag `no more yp records' */
612 _pw_ypdone = 1;
613 rval = NS_NOTFOUND;
614 }
615 } else {
616 r = yp_first(__ypdomain, map, &__ypcurrent,
617 &__ypcurrentlen, &data, &datalen);
618 }
619 if (r != 0) {
620 if (key)
621 free(key);
622 if (data)
623 free(data);
624 return (rval);
625 }
626 data[datalen] = '\0'; /* clear trailing \n */
627 strncpy(line, data, sizeof(line));
628 line[sizeof(line) - 1] = '\0';
629 free(data);
630 if (! __pwparse(&_pw_passwd, line))
631 return NS_SUCCESS;
632 }
633 /* NOTREACHED */
634 } /* _nis_getpw */
635 #endif
636
637 #ifdef _PASSWD_COMPAT
638 /*
639 * See if the compat token is in the database. Only works if pwd_mkdb knows
640 * about the token.
641 */
642 static int __has_compatpw __P((void));
643
644 static int
645 __has_compatpw()
646 {
647 DBT key, data;
648 DBT pkey, pdata;
649 char bf[MAXLOGNAME];
650
651 /*LINTED*/
652 key.data = (u_char *)__yp_token;
653 key.size = strlen(__yp_token);
654
655 /* Pre-token database support. */
656 bf[0] = _PW_KEYBYNAME;
657 bf[1] = '+';
658 pkey.data = (u_char *)bf;
659 pkey.size = 2;
660
661 if ((_pw_db->get)(_pw_db, &key, &data, 0)
662 && (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
663 return 0; /* No compat token */
664 return 1;
665 }
666
667 /*
668 * log an error if "files" or "compat" is specified in passwd_compat database
669 */
670 static int _bad_getpw __P((void *, void *, va_list));
671
672 /*ARGSUSED*/
673 static int
674 _bad_getpw(rv, cb_data, ap)
675 void *rv;
676 void *cb_data;
677 va_list ap;
678 {
679 static int warned;
680
681 _DIAGASSERT(cb_data != NULL);
682
683 if (!warned) {
684 syslog(LOG_ERR,
685 "nsswitch.conf passwd_compat database can't use '%s'",
686 (char *)cb_data);
687 }
688 warned = 1;
689 return NS_UNAVAIL;
690 }
691
692 /*
693 * when a name lookup in compat mode is required (e.g., '+name', or a name in
694 * '+@netgroup'), look it up in the 'passwd_compat' nsswitch database.
695 * only Hesiod and NIS is supported - it doesn't make sense to lookup
696 * compat names from 'files' or 'compat'.
697 */
698 static int __getpwcompat __P((int, uid_t, const char *));
699
700 static int
701 __getpwcompat(type, uid, name)
702 int type;
703 uid_t uid;
704 const char *name;
705 {
706 static const ns_dtab dtab[] = {
707 NS_FILES_CB(_bad_getpw, "files")
708 NS_DNS_CB(_dns_getpw, NULL)
709 NS_NIS_CB(_nis_getpw, NULL)
710 NS_COMPAT_CB(_bad_getpw, "compat")
711 { 0 }
712 };
713 static const ns_src defaultnis[] = {
714 { NSSRC_NIS, NS_SUCCESS },
715 { 0 }
716 };
717
718 switch (type) {
719 case _PW_KEYBYNUM:
720 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
721 defaultnis, type);
722 case _PW_KEYBYNAME:
723 _DIAGASSERT(name != NULL);
724 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
725 defaultnis, type, name);
726 case _PW_KEYBYUID:
727 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
728 defaultnis, type, uid);
729 default:
730 abort();
731 /*NOTREACHED*/
732 }
733 }
734 #endif /* _PASSWD_COMPAT */
735
736 /*
737 * compat implementation of getpwent()
738 * varargs (ignored):
739 * type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
740 */
741 static int _compat_getpwent __P((void *, void *, va_list));
742
743 /*ARGSUSED*/
744 static int
745 _compat_getpwent(rv, cb_data, ap)
746 void *rv;
747 void *cb_data;
748 va_list ap;
749 {
750 DBT key;
751 int rval;
752 char bf[sizeof(_pw_keynum) + 1];
753 #ifdef _PASSWD_COMPAT
754 static char *name = NULL;
755 const char *user, *host, *dom;
756 int has_compatpw;
757 #endif
758
759 if (!_pw_db && !__initdb())
760 return NS_UNAVAIL;
761
762 #ifdef _PASSWD_COMPAT
763 has_compatpw = __has_compatpw();
764
765 again:
766 if (has_compatpw && (__pwmode != PWMODE_NONE)) {
767 int r;
768
769 switch (__pwmode) {
770 case PWMODE_FULL:
771 r = __getpwcompat(_PW_KEYBYNUM, 0, NULL);
772 if (r == NS_SUCCESS)
773 return r;
774 __pwmode = PWMODE_NONE;
775 break;
776
777 case PWMODE_NETGRP:
778 r = getnetgrent(&host, &user, &dom);
779 if (r == 0) { /* end of group */
780 endnetgrent();
781 __pwmode = PWMODE_NONE;
782 break;
783 }
784 if (!user || !*user)
785 break;
786 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
787 if (r == NS_SUCCESS)
788 return r;
789 break;
790
791 case PWMODE_USER:
792 if (name == NULL) {
793 __pwmode = PWMODE_NONE;
794 break;
795 }
796 r = __getpwcompat(_PW_KEYBYNAME, 0, name);
797 free(name);
798 name = NULL;
799 if (r == NS_SUCCESS)
800 return r;
801 break;
802
803 case PWMODE_NONE:
804 abort();
805 }
806 goto again;
807 }
808 #endif
809
810 if (_pw_keynum == -1)
811 return NS_NOTFOUND; /* no more local records */
812 ++_pw_keynum;
813 bf[0] = _PW_KEYBYNUM;
814 memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
815 key.data = (u_char *)bf;
816 key.size = sizeof(_pw_keynum) + 1;
817 rval = __hashpw(&key);
818 if (rval == NS_NOTFOUND)
819 _pw_keynum = -1; /* flag `no more local records' */
820 else if (rval == NS_SUCCESS) {
821 #ifdef _PASSWD_COMPAT
822 /* if we don't have YP at all, don't bother. */
823 if (has_compatpw) {
824 if(_pw_passwd.pw_name[0] == '+') {
825 /* set the mode */
826 switch(_pw_passwd.pw_name[1]) {
827 case '\0':
828 __pwmode = PWMODE_FULL;
829 break;
830 case '@':
831 __pwmode = PWMODE_NETGRP;
832 setnetgrent(_pw_passwd.pw_name + 2);
833 break;
834 default:
835 __pwmode = PWMODE_USER;
836 name = strdup(_pw_passwd.pw_name + 1);
837 break;
838 }
839
840 /* save the prototype */
841 __pwproto_set();
842 goto again;
843 } else if(_pw_passwd.pw_name[0] == '-') {
844 /* an attempted exclusion */
845 switch(_pw_passwd.pw_name[1]) {
846 case '\0':
847 break;
848 case '@':
849 setnetgrent(_pw_passwd.pw_name + 2);
850 while(getnetgrent(&host, &user, &dom)) {
851 if(user && *user)
852 __pwexclude_add(user);
853 }
854 endnetgrent();
855 break;
856 default:
857 __pwexclude_add(_pw_passwd.pw_name + 1);
858 break;
859 }
860 goto again;
861 }
862 }
863 #endif
864 }
865 return (rval);
866 }
867
868 /*
869 * compat implementation of getpwnam() and getpwuid()
870 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
871 */
872 static int _compat_getpw __P((void *, void *, va_list));
873
874 static int
875 _compat_getpw(rv, cb_data, ap)
876 void *rv;
877 void *cb_data;
878 va_list ap;
879 {
880 #ifdef _PASSWD_COMPAT
881 DBT key;
882 int search, rval, r, s, keynum;
883 uid_t uid;
884 char bf[sizeof(keynum) + 1];
885 const char *name, *host, *user, *dom;
886 #endif
887
888 if (!_pw_db && !__initdb())
889 return NS_UNAVAIL;
890
891 /*
892 * If there isn't a compat token in the database, use files.
893 */
894 #ifdef _PASSWD_COMPAT
895 if (! __has_compatpw())
896 #endif
897 return (_local_getpw(rv, cb_data, ap));
898
899 #ifdef _PASSWD_COMPAT
900 search = va_arg(ap, int);
901 uid = 0;
902 name = NULL;
903 rval = NS_NOTFOUND;
904 switch (search) {
905 case _PW_KEYBYNAME:
906 name = va_arg(ap, const char *);
907 break;
908 case _PW_KEYBYUID:
909 uid = va_arg(ap, uid_t);
910 break;
911 default:
912 abort();
913 }
914
915 for (s = -1, keynum = 1 ; ; keynum++) {
916 bf[0] = _PW_KEYBYNUM;
917 memmove(bf + 1, &keynum, sizeof(keynum));
918 key.data = (u_char *)bf;
919 key.size = sizeof(keynum) + 1;
920 if(__hashpw(&key) != NS_SUCCESS)
921 break;
922 switch(_pw_passwd.pw_name[0]) {
923 case '+':
924 /* save the prototype */
925 __pwproto_set();
926
927 switch(_pw_passwd.pw_name[1]) {
928 case '\0':
929 r = __getpwcompat(search, uid, name);
930 if (r != NS_SUCCESS)
931 continue;
932 break;
933 case '@':
934 pwnam_netgrp:
935 #if 0 /* XXX: is this a hangover from pre-nsswitch? */
936 if(__ypcurrent) {
937 free(__ypcurrent);
938 __ypcurrent = NULL;
939 }
940 #endif
941 if (s == -1) /* first time */
942 setnetgrent(_pw_passwd.pw_name + 2);
943 s = getnetgrent(&host, &user, &dom);
944 if (s == 0) { /* end of group */
945 endnetgrent();
946 s = -1;
947 continue;
948 }
949 if (!user || !*user)
950 goto pwnam_netgrp;
951
952 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
953
954 if (r == NS_UNAVAIL)
955 return r;
956 if (r == NS_NOTFOUND) {
957 /*
958 * just because this user is bad
959 * it doesn't mean they all are.
960 */
961 goto pwnam_netgrp;
962 }
963 break;
964 default:
965 user = _pw_passwd.pw_name + 1;
966 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
967
968 if (r == NS_UNAVAIL)
969 return r;
970 if (r == NS_NOTFOUND)
971 continue;
972 break;
973 }
974 if(__pwexclude_is(_pw_passwd.pw_name)) {
975 if(s == 1) /* inside netgroup */
976 goto pwnam_netgrp;
977 continue;
978 }
979 break;
980 case '-':
981 /* attempted exclusion */
982 switch(_pw_passwd.pw_name[1]) {
983 case '\0':
984 break;
985 case '@':
986 setnetgrent(_pw_passwd.pw_name + 2);
987 while(getnetgrent(&host, &user, &dom)) {
988 if(user && *user)
989 __pwexclude_add(user);
990 }
991 endnetgrent();
992 break;
993 default:
994 __pwexclude_add(_pw_passwd.pw_name + 1);
995 break;
996 }
997 break;
998 }
999 if ((search == _PW_KEYBYNAME &&
1000 strcmp(_pw_passwd.pw_name, name) == 0)
1001 || (search == _PW_KEYBYUID && _pw_passwd.pw_uid == uid)) {
1002 rval = NS_SUCCESS;
1003 break;
1004 }
1005 if(s == 1) /* inside netgroup */
1006 goto pwnam_netgrp;
1007 continue;
1008 }
1009 __pwproto = (struct passwd *)NULL;
1010
1011 if (!_pw_stayopen) {
1012 (void)(_pw_db->close)(_pw_db);
1013 _pw_db = (DB *)NULL;
1014 }
1015 if(__pwexclude != (DB *)NULL) {
1016 (void)(__pwexclude->close)(__pwexclude);
1017 __pwexclude = (DB *)NULL;
1018 }
1019 return rval;
1020 #endif /* _PASSWD_COMPAT */
1021 }
1022
1023 struct passwd *
1024 getpwent()
1025 {
1026 int r;
1027 static const ns_dtab dtab[] = {
1028 NS_FILES_CB(_local_getpw, NULL)
1029 NS_DNS_CB(_dns_getpw, NULL)
1030 NS_NIS_CB(_nis_getpw, NULL)
1031 NS_COMPAT_CB(_compat_getpwent, NULL)
1032 { 0 }
1033 };
1034
1035 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", compatsrc,
1036 _PW_KEYBYNUM);
1037 if (r != NS_SUCCESS)
1038 return (struct passwd *)NULL;
1039 return &_pw_passwd;
1040 }
1041
1042 struct passwd *
1043 getpwnam(name)
1044 const char *name;
1045 {
1046 int r;
1047 static const ns_dtab dtab[] = {
1048 NS_FILES_CB(_local_getpw, NULL)
1049 NS_DNS_CB(_dns_getpw, NULL)
1050 NS_NIS_CB(_nis_getpw, NULL)
1051 NS_COMPAT_CB(_compat_getpw, NULL)
1052 { 0 }
1053 };
1054
1055 if (name == NULL || name[0] == '\0')
1056 return (struct passwd *)NULL;
1057
1058 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", compatsrc,
1059 _PW_KEYBYNAME, name);
1060 return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1061 }
1062
1063 struct passwd *
1064 getpwuid(uid)
1065 uid_t uid;
1066 {
1067 int r;
1068 static const ns_dtab dtab[] = {
1069 NS_FILES_CB(_local_getpw, NULL)
1070 NS_DNS_CB(_dns_getpw, NULL)
1071 NS_NIS_CB(_nis_getpw, NULL)
1072 NS_COMPAT_CB(_compat_getpw, NULL)
1073 { 0 }
1074 };
1075
1076 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", compatsrc,
1077 _PW_KEYBYUID, uid);
1078 return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1079 }
1080
1081 int
1082 setpassent(stayopen)
1083 int stayopen;
1084 {
1085 _pw_keynum = 0;
1086 _pw_stayopen = stayopen;
1087 #ifdef YP
1088 __pwmode = PWMODE_NONE;
1089 if(__ypcurrent)
1090 free(__ypcurrent);
1091 __ypcurrent = NULL;
1092 _pw_ypdone = 0;
1093 #endif
1094 #ifdef HESIOD
1095 _pw_hesnum = 0;
1096 #endif
1097 #ifdef _PASSWD_COMPAT
1098 if(__pwexclude != (DB *)NULL) {
1099 (void)(__pwexclude->close)(__pwexclude);
1100 __pwexclude = (DB *)NULL;
1101 }
1102 __pwproto = (struct passwd *)NULL;
1103 #endif
1104 return 1;
1105 }
1106
1107 void
1108 setpwent()
1109 {
1110 (void) setpassent(0);
1111 }
1112
1113 void
1114 endpwent()
1115 {
1116 _pw_keynum = 0;
1117 if (_pw_db) {
1118 (void)(_pw_db->close)(_pw_db);
1119 _pw_db = (DB *)NULL;
1120 }
1121 #ifdef _PASSWD_COMPAT
1122 __pwmode = PWMODE_NONE;
1123 #endif
1124 #ifdef YP
1125 if(__ypcurrent)
1126 free(__ypcurrent);
1127 __ypcurrent = NULL;
1128 _pw_ypdone = 0;
1129 #endif
1130 #ifdef HESIOD
1131 _pw_hesnum = 0;
1132 #endif
1133 #ifdef _PASSWD_COMPAT
1134 if(__pwexclude != (DB *)NULL) {
1135 (void)(__pwexclude->close)(__pwexclude);
1136 __pwexclude = (DB *)NULL;
1137 }
1138 __pwproto = (struct passwd *)NULL;
1139 #endif
1140 }
1141
1142 static int
1143 __initdb()
1144 {
1145 static int warned;
1146 char *p;
1147
1148 #ifdef _PASSWD_COMPAT
1149 __pwmode = PWMODE_NONE;
1150 #endif
1151 if (geteuid() == 0) {
1152 _pw_db = dbopen((p = _PATH_SMP_DB), O_RDONLY, 0, DB_HASH, NULL);
1153 if (_pw_db)
1154 return(1);
1155 }
1156 _pw_db = dbopen((p = _PATH_MP_DB), O_RDONLY, 0, DB_HASH, NULL);
1157 if (_pw_db)
1158 return 1;
1159 if (!warned)
1160 syslog(LOG_ERR, "%s: %m", p);
1161 warned = 1;
1162 return 0;
1163 }
1164
1165 static int
1166 __hashpw(key)
1167 DBT *key;
1168 {
1169 char *p, *t;
1170 static u_int max;
1171 static char *buf;
1172 DBT data;
1173
1174 _DIAGASSERT(key != NULL);
1175
1176 switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
1177 case 0:
1178 break; /* found */
1179 case 1:
1180 return NS_NOTFOUND;
1181 case -1:
1182 return NS_UNAVAIL; /* error in db routines */
1183 default:
1184 abort();
1185 }
1186
1187 p = (char *)data.data;
1188 if (data.size > max && !(buf = realloc(buf, (max += 1024))))
1189 return NS_UNAVAIL;
1190
1191 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1192 t = buf;
1193 #define EXPAND(e) e = t; while ((*t++ = *p++));
1194 #define SCALAR(v) memmove(&(v), p, sizeof v); p += sizeof v
1195 EXPAND(_pw_passwd.pw_name);
1196 EXPAND(_pw_passwd.pw_passwd);
1197 SCALAR(_pw_passwd.pw_uid);
1198 SCALAR(_pw_passwd.pw_gid);
1199 SCALAR(_pw_passwd.pw_change);
1200 EXPAND(_pw_passwd.pw_class);
1201 EXPAND(_pw_passwd.pw_gecos);
1202 EXPAND(_pw_passwd.pw_dir);
1203 EXPAND(_pw_passwd.pw_shell);
1204 SCALAR(_pw_passwd.pw_expire);
1205
1206 /* See if there's any data left. If so, read in flags. */
1207 if (data.size > (p - (char *)data.data)) {
1208 SCALAR(_pw_flags);
1209 } else
1210 _pw_flags = _PASSWORD_NOUID|_PASSWORD_NOGID; /* default */
1211
1212 return NS_SUCCESS;
1213 }
1214