xref: /netbsd-src/lib/libc/gen/getpwent.c (revision 0dd5877adce57db949b16ae963e5a6831cccdfb6)
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