xref: /freebsd/lib/libc/gen/getpwent.c (revision b5acd0014ee3e0962ded83e7869d1ca0f1275612)
1 /*
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char sccsid[] = "@(#)getpwent.c	8.2 (Berkeley) 4/27/95";
36 #endif /* LIBC_SCCS and not lint */
37 
38 #include <stdio.h>
39 #include <sys/param.h>
40 #include <fcntl.h>
41 #include <db.h>
42 #include <syslog.h>
43 #include <pwd.h>
44 #include <utmp.h>
45 #include <errno.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <limits.h>
50 #include <grp.h>
51 
52 extern void setnetgrent __P(( char * ));
53 extern int getnetgrent __P(( char **, char **, char ** ));
54 extern int innetgr __P(( const char *, const char *, const char *, const char * ));
55 
56 /*
57  * The lookup techniques and data extraction code here must be kept
58  * in sync with that in `pwd_mkdb'.
59  */
60 
61 static struct passwd _pw_passwd;	/* password structure */
62 static DB *_pw_db;			/* password database */
63 static int _pw_keynum;			/* key counter */
64 static int _pw_stayopen;		/* keep fd's open */
65 #ifdef YP
66 #include <rpc/rpc.h>
67 #include <rpcsvc/yp_prot.h>
68 #include <rpcsvc/ypclnt.h>
69 
70 static struct passwd _pw_copy;
71 static DBT empty = { NULL, 0 };
72 static DB *_ypcache = (DB *)NULL;
73 static int _yp_exclusions = 0;
74 static int _yp_enabled = -1;
75 static int _pw_stepping_yp;		/* set true when stepping thru map */
76 static char _ypnam[YPMAXRECORD];
77 #define YP_HAVE_MASTER 2
78 #define YP_HAVE_ADJUNCT 1
79 #define YP_HAVE_NONE 0
80 static int _gotmaster;
81 static char *_pw_yp_domain;
82 static inline int unwind __P(( char * ));
83 static void _ypinitdb __P(( void ));
84 static int _havemaster __P((char *));
85 static int _getyppass __P((struct passwd *, const char *, const char * ));
86 static int _nextyppass __P((struct passwd *));
87 static inline int lookup __P((const char *));
88 static inline void store __P((const char *));
89 static inline int ingr __P((const char *, const char*));
90 static inline int verf __P((const char *));
91 static char * _get_adjunct_pw __P((const char *));
92 #endif
93 static int __hashpw(DBT *);
94 static int __initdb(void);
95 
96 struct passwd *
97 getpwent()
98 {
99 	DBT key;
100 	char bf[sizeof(_pw_keynum) + 1];
101 	int rv;
102 
103 	if (!_pw_db && !__initdb())
104 		return((struct passwd *)NULL);
105 
106 #ifdef YP
107 	if(_pw_stepping_yp) {
108 		_pw_passwd = _pw_copy;
109 		if (unwind((char *)&_ypnam))
110 			return(&_pw_passwd);
111 	}
112 #endif
113 tryagain:
114 
115 	++_pw_keynum;
116 	bf[0] = _PW_KEYBYNUM;
117 	bcopy((char *)&_pw_keynum, bf + 1, sizeof(_pw_keynum));
118 	key.data = (u_char *)bf;
119 	key.size = sizeof(_pw_keynum) + 1;
120 	rv = __hashpw(&key);
121 	if(!rv) return (struct passwd *)NULL;
122 #ifdef YP
123 	if(_pw_passwd.pw_name[0] == '+' || _pw_passwd.pw_name[0] == '-') {
124 		if (_yp_enabled == -1)
125 			_ypinitdb();
126 		bzero((char *)&_ypnam, sizeof(_ypnam));
127 		bcopy(_pw_passwd.pw_name, _ypnam,
128 			strlen(_pw_passwd.pw_name));
129 		_pw_copy = _pw_passwd;
130 		if (unwind((char *)&_ypnam) == 0)
131 			goto tryagain;
132 		else
133 			return(&_pw_passwd);
134 	}
135 #else
136 	/* Ignore YP password file entries when YP is disabled. */
137 	if(_pw_passwd.pw_name[0] == '+' || _pw_passwd.pw_name[0] == '-') {
138 		goto tryagain;
139 	}
140 #endif
141 	return(&_pw_passwd);
142 }
143 
144 struct passwd *
145 getpwnam(name)
146 	const char *name;
147 {
148 	DBT key;
149 	int len, rval;
150 	char bf[UT_NAMESIZE + 2];
151 
152 	if (!_pw_db && !__initdb())
153 		return((struct passwd *)NULL);
154 
155 	bf[0] = _PW_KEYBYNAME;
156 	len = strlen(name);
157 	bcopy(name, bf + 1, MIN(len, UT_NAMESIZE));
158 	key.data = (u_char *)bf;
159 	key.size = len + 1;
160 	rval = __hashpw(&key);
161 
162 #ifdef YP
163 	if (!rval) {
164 		if (_yp_enabled == -1)
165 			_ypinitdb();
166 		if (_yp_enabled)
167 			rval = _getyppass(&_pw_passwd, name, "passwd.byname");
168 	}
169 #endif
170 	/*
171 	 * Prevent login attempts when YP is not enabled but YP entries
172 	 * are in /etc/master.passwd.
173 	 */
174 	if (rval && (_pw_passwd.pw_name[0] == '+'||
175 			_pw_passwd.pw_name[0] == '-')) rval = 0;
176 
177 	endpwent();
178 	return(rval ? &_pw_passwd : (struct passwd *)NULL);
179 }
180 
181 struct passwd *
182 getpwuid(uid)
183 	uid_t uid;
184 {
185 	DBT key;
186 	int keyuid, rval;
187 	char bf[sizeof(keyuid) + 1];
188 
189 	if (!_pw_db && !__initdb())
190 		return((struct passwd *)NULL);
191 
192 	bf[0] = _PW_KEYBYUID;
193 	keyuid = uid;
194 	bcopy(&keyuid, bf + 1, sizeof(keyuid));
195 	key.data = (u_char *)bf;
196 	key.size = sizeof(keyuid) + 1;
197 	rval = __hashpw(&key);
198 
199 #ifdef YP
200 	if (!rval) {
201 		if (_yp_enabled == -1)
202 			_ypinitdb();
203 		if (_yp_enabled) {
204 			char ypbuf[16];	/* big enough for 32-bit uids */
205 			snprintf(ypbuf, sizeof ypbuf, "%u", (unsigned)uid);
206 			rval = _getyppass(&_pw_passwd, ypbuf, "passwd.byuid");
207 		}
208 	}
209 #endif
210 	/*
211 	 * Prevent login attempts when YP is not enabled but YP entries
212 	 * are in /etc/master.passwd.
213 	 */
214 	if (rval && (_pw_passwd.pw_name[0] == '+'||
215 			_pw_passwd.pw_name[0] == '-')) rval = 0;
216 
217 	endpwent();
218 	return(rval ? &_pw_passwd : (struct passwd *)NULL);
219 }
220 
221 int
222 setpassent(stayopen)
223 	int stayopen;
224 {
225 	_pw_keynum = 0;
226 #ifdef YP
227 	_pw_stepping_yp = 0;
228 #endif
229 	_pw_stayopen = stayopen;
230 	return(1);
231 }
232 
233 void
234 setpwent()
235 {
236 	(void)setpassent(0);
237 }
238 
239 void
240 endpwent()
241 {
242 	_pw_keynum = 0;
243 #ifdef YP
244 	_pw_stepping_yp = 0;
245 #endif
246 	if (_pw_db) {
247 		(void)(_pw_db->close)(_pw_db);
248 		_pw_db = (DB *)NULL;
249 	}
250 #ifdef YP
251 	if (_ypcache) {
252 		(void)(_ypcache->close)(_ypcache);
253 		_ypcache = (DB *)NULL;
254 		_yp_exclusions = 0;
255 	}
256 #endif
257 }
258 
259 static int
260 __initdb()
261 {
262 	static int warned;
263 	char *p;
264 
265 	p = (geteuid()) ? _PATH_MP_DB : _PATH_SMP_DB;
266 	_pw_db = dbopen(p, O_RDONLY, 0, DB_HASH, NULL);
267 	if (_pw_db)
268 		return(1);
269 	if (!warned++)
270 		syslog(LOG_ERR, "%s: %m", p);
271 	return(0);
272 }
273 
274 static int
275 __hashpw(key)
276 	DBT *key;
277 {
278 	register char *p, *t;
279 	static u_int max;
280 	static char *line;
281 	DBT data;
282 
283 	if ((_pw_db->get)(_pw_db, key, &data, 0))
284 		return(0);
285 	p = (char *)data.data;
286 
287 	/* Increase buffer size for long lines if necessary. */
288 	if (data.size > max) {
289 		max = data.size + 1024;
290 		if (!(line = realloc(line, max)))
291 			return(0);
292 	}
293 
294 	/* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
295 	t = line;
296 #define	EXPAND(e)	e = t; while ( (*t++ = *p++) );
297 #define	SCALAR(v)	memmove(&(v), p, sizeof v); p += sizeof v
298 	EXPAND(_pw_passwd.pw_name);
299 	EXPAND(_pw_passwd.pw_passwd);
300 	SCALAR(_pw_passwd.pw_uid);
301 	SCALAR(_pw_passwd.pw_gid);
302 	SCALAR(_pw_passwd.pw_change);
303 	EXPAND(_pw_passwd.pw_class);
304 	EXPAND(_pw_passwd.pw_gecos);
305 	EXPAND(_pw_passwd.pw_dir);
306 	EXPAND(_pw_passwd.pw_shell);
307 	SCALAR(_pw_passwd.pw_expire);
308 	bcopy(p, (char *)&_pw_passwd.pw_fields, sizeof _pw_passwd.pw_fields);
309 	p += sizeof _pw_passwd.pw_fields;
310 	return(1);
311 }
312 
313 #ifdef YP
314 
315 static void
316 _ypinitdb()
317 {
318 	DBT key, data;
319 	char buf[] = { _PW_KEYYPENABLED };
320 	key.data = buf;
321 	key.size = 1;
322 	_yp_enabled = 0;
323 	if ((_pw_db->get)(_pw_db, &key, &data, 0) == 0) {
324 		_yp_enabled = (int)*((char *)data.data) - 2;
325 		/* Don't even bother with this if we aren't root. */
326 		if (!geteuid()) {
327 			if (!_pw_yp_domain)
328 				if (yp_get_default_domain(&_pw_yp_domain))
329 					return;
330 			_gotmaster = _havemaster(_pw_yp_domain);
331 		} else _gotmaster = YP_HAVE_NONE;
332 		/*
333 		 * Create a DB hash database in memory. Bet you didn't know you
334 		 * could do a dbopen() with a NULL filename, did you.
335 		 */
336 		if (_ypcache == (DB *)NULL)
337 			_ypcache = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
338 	}
339 }
340 
341 /*
342  * See if a user is in the blackballed list.
343  */
344 static inline int
345 lookup(name)
346 	const char *name;
347 {
348 	DBT key;
349 
350 	if (!_yp_exclusions)
351 		return(0);
352 
353 	key.data = (char *)name;
354 	key.size = strlen(name);
355 
356 	if ((_ypcache->get)(_ypcache, &key, &empty, 0)) {
357 		return(0);
358 	}
359 
360 	return(1);
361 }
362 
363 /*
364  * Store a blackballed user in an in-core hash database.
365  */
366 static inline void
367 store(key)
368 	const char *key;
369 {
370 	DBT lkey;
371 /*
372 	if (lookup(key))
373 		return;
374 */
375 
376 	_yp_exclusions = 1;
377 
378 	lkey.data = (char *)key;
379 	lkey.size = strlen(key);
380 
381 	(void)(_ypcache->put)(_ypcache, &lkey, &empty, R_NOOVERWRITE);
382 }
383 
384 /*
385  * Parse the + entries in the password database and do appropriate
386  * NIS lookups. While ugly to look at, this is optimized to do only
387  * as many lookups as are absolutely necessary in any given case.
388  * Basically, the getpwent() function will feed us + and - lines
389  * as they appear in the database. For + lines, we do netgroup/group
390  * and user lookups to find all usernames that match the rule and
391  * extract them from the NIS passwd maps. For - lines, we save the
392  * matching names in a database and a) exlude them, and b) make sure
393  * we don't consider them when processing other + lines that appear
394  * later.
395  */
396 static inline int
397 unwind(grp)
398 	char *grp;
399 {
400 	char *user, *host, *domain;
401 	static int latch = 0;
402 	static struct group *gr = NULL;
403 	int rv = 0;
404 
405 	if (grp[0] == '+') {
406 		if (strlen(grp) == 1) {
407 			return(_nextyppass(&_pw_passwd));
408 		}
409 		if (grp[1] == '@') {
410 			_pw_stepping_yp = 1;
411 grpagain:
412 			if (gr != NULL) {
413 				if (*gr->gr_mem != NULL) {
414 					if (lookup(*gr->gr_mem)) {
415 						gr->gr_mem++;
416 						goto grpagain;
417 					}
418 					rv = _getyppass(&_pw_passwd,
419 							*gr->gr_mem,
420 							"passwd.byname");
421 					gr->gr_mem++;
422 					return(rv);
423 				} else {
424 					endgrent();
425 					latch = 0;
426 					gr = NULL;
427 					return(0);
428 				}
429 			}
430 			if (!latch) {
431 				setnetgrent(grp+2);
432 				latch++;
433 			}
434 again:
435 			if (getnetgrent(&host, &user, &domain) == 0) {
436 				if ((gr = getgrnam(grp+2)) != NULL)
437 					goto grpagain;
438 				latch = 0;
439 				_pw_stepping_yp = 0;
440 				return(0);
441 			} else {
442 				if (lookup(user))
443 					goto again;
444 				if (_getyppass(&_pw_passwd, user,
445 							"passwd.byname"))
446 					return(1);
447 				else
448 					goto again;
449 			}
450 		} else {
451 			if (lookup(grp+1))
452 				return(0);
453 			return(_getyppass(&_pw_passwd, grp+1, "passwd.byname"));
454 		}
455 	} else {
456 		if (grp[1] == '@') {
457 			setnetgrent(grp+2);
458 			rv = 0;
459 			while(getnetgrent(&host, &user, &domain) != 0) {
460 				store(user);
461 				rv++;
462 			}
463 			if (!rv && (gr = getgrnam(grp+2)) != NULL) {
464 				while(*gr->gr_mem) {
465 					store(*gr->gr_mem);
466 					gr->gr_mem++;
467 				}
468 			}
469 		} else {
470 			store(grp+1);
471 		}
472 	}
473 	return(0);
474 }
475 
476 /*
477  * See if a user is a member of a particular group.
478  */
479 static inline int
480 ingr(grp, name)
481 	const char *grp;
482 	const char *name;
483 {
484 	register struct group *gr;
485 
486 	if ((gr = getgrnam(grp)) == NULL)
487 		return(0);
488 
489 	while(*gr->gr_mem) {
490 		if (!strcmp(*gr->gr_mem, name)) {
491 			endgrent();
492 			return(1);
493 		}
494 		gr->gr_mem++;
495 	}
496 
497 	endgrent();
498 	return(0);
499 }
500 
501 /*
502  * Check a user against the +@netgroup/-@netgroup lines listed in
503  * the local password database. Also checks +user/-user lines.
504  * If no netgroup exists that matches +@netgroup/-@netgroup,
505  * try searching regular groups with the same name.
506  */
507 static inline int
508 verf(name)
509 	const char *name;
510 {
511 	DBT key;
512 	char bf[sizeof(_pw_keynum) + 1];
513 	int keynum = 0;
514 
515 again:
516 	++keynum;
517 	bf[0] = _PW_KEYYPBYNUM;
518 	bcopy((char *)&keynum, bf + 1, sizeof(keynum));
519 	key.data = (u_char *)bf;
520 	key.size = sizeof(keynum) + 1;
521 	if (!__hashpw(&key)) {
522 		/* Try again using old format */
523 		bf[0] = _PW_KEYBYNUM;
524 		bcopy((char *)&keynum, bf + 1, sizeof(keynum));
525 		key.data = (u_char *)bf;
526 		if (!__hashpw(&key))
527 			return(0);
528 	}
529 	if (_pw_passwd.pw_name[0] != '+' && (_pw_passwd.pw_name[0] != '-'))
530 		goto again;
531 	if (_pw_passwd.pw_name[0] == '+') {
532 		if (strlen(_pw_passwd.pw_name) == 1) /* Wildcard */
533 			return(1);
534 		if (_pw_passwd.pw_name[1] == '@') {
535 			if ((innetgr(_pw_passwd.pw_name+2, NULL, name,
536 							_pw_yp_domain) ||
537 			    ingr(_pw_passwd.pw_name+2, name)) && !lookup(name))
538 				return(1);
539 			else
540 				goto again;
541 		} else {
542 			if (!strcmp(name, _pw_passwd.pw_name+1) &&
543 								!lookup(name))
544 				return(1);
545 			else
546 				goto again;
547 		}
548 	}
549 	if (_pw_passwd.pw_name[0] == '-') {
550 		/* Note that a minus wildcard is a no-op. */
551 		if (_pw_passwd.pw_name[1] == '@') {
552 			if (innetgr(_pw_passwd.pw_name+2, NULL, name,
553 							_pw_yp_domain) ||
554 			    ingr(_pw_passwd.pw_name+2, name)) {
555 				store(name);
556 				return(0);
557 			} else
558 				goto again;
559 		} else {
560 			if (!strcmp(name, _pw_passwd.pw_name+1)) {
561 				store(name);
562 				return(0);
563 			} else
564 				goto again;
565 		}
566 
567 	}
568 	return(0);
569 }
570 
571 static char *
572 _get_adjunct_pw(name)
573 	const char *name;
574 {
575 	static char adjunctbuf[YPMAXRECORD+2];
576 	int rval;
577 	char *result;
578 	int resultlen;
579 	char *map = "passwd.adjunct.byname";
580 	char *s;
581 
582 	if ((rval = yp_match(_pw_yp_domain, map, name, strlen(name),
583 		    &result, &resultlen)))
584 		return(NULL);
585 
586 	strncpy(adjunctbuf, result, resultlen);
587 	adjunctbuf[resultlen] = '\0';
588 	free(result);
589 	result = (char *)&adjunctbuf;
590 
591 	/* Don't care about the name. */
592 	if ((s = strsep(&result, ":")) == NULL)
593 		return (NULL); /* name */
594 	if ((s = strsep(&result, ":")) == NULL)
595 		return (NULL); /* password */
596 
597 	return(s);
598 }
599 
600 static int
601 _pw_breakout_yp(struct passwd *pw, char *res, int resultlen, int master)
602 {
603 	char *s, *result;
604 	static char resbuf[YPMAXRECORD+2];
605 
606 	/*
607 	 * Be triple, ultra super-duper paranoid: reject entries
608 	 * that start with a + or -. yp_mkdb and /var/yp/Makefile
609 	 * are _both_ supposed to strip these out, but you never
610 	 * know.
611 	 */
612 	if (*res == '+' || *res == '-')
613 		return 0;
614 
615 	/*
616 	 * The NIS protocol definition limits the size of an NIS
617 	 * record to YPMAXRECORD bytes. We need to do a copy to
618 	 * a static buffer here since the memory pointed to by
619 	 * res will be free()ed when this function returns.
620 	 */
621 	strncpy((char *)&resbuf, res, resultlen);
622 	resbuf[resultlen] = '\0';
623 	result = (char *)&resbuf;
624 
625 	/*
626 	 * XXX Sanity check: make sure all fields are valid (no NULLs).
627 	 * If we find a badly formatted entry, we punt.
628 	 */
629 	if ((s = strsep(&result, ":")) == NULL) return 0; /* name */
630 	/*
631 	 * We don't care what pw_fields says: we _always_ want the
632 	 * username returned to us by NIS.
633 	 */
634 	pw->pw_name = s;
635 	pw->pw_fields |= _PWF_NAME;
636 
637 	if ((s = strsep(&result, ":")) == NULL) return 0; /* password */
638 	if(!(pw->pw_fields & _PWF_PASSWD)) {
639 		/* SunOS passwd.adjunct hack */
640 		if (master == YP_HAVE_ADJUNCT && strstr(s, "##") != NULL) {
641 			char *realpw;
642 			realpw = _get_adjunct_pw(pw->pw_name);
643 			if (realpw == NULL)
644 				pw->pw_passwd = s;
645 			else
646 				pw->pw_passwd = realpw;
647 		} else {
648 			pw->pw_passwd = s;
649 		}
650 		pw->pw_fields |= _PWF_PASSWD;
651 	}
652 
653 	if ((s = strsep(&result, ":")) == NULL) return 0; /* uid */
654 	if(!(pw->pw_fields & _PWF_UID)) {
655 		pw->pw_uid = atoi(s);
656 		pw->pw_fields |= _PWF_UID;
657 	}
658 
659 	if ((s = strsep(&result, ":")) == NULL) return 0; /* gid */
660 	if(!(pw->pw_fields & _PWF_GID))  {
661 		pw->pw_gid = atoi(s);
662 		pw->pw_fields |= _PWF_GID;
663 	}
664 
665 	if (master == YP_HAVE_MASTER) {
666 		if ((s = strsep(&result, ":")) == NULL) return 0; /* class */
667 		if(!(pw->pw_fields & _PWF_CLASS))  {
668 			pw->pw_class = s;
669 			pw->pw_fields |= _PWF_CLASS;
670 		}
671 
672 		if ((s = strsep(&result, ":")) == NULL) return 0; /* change */
673 		if(!(pw->pw_fields & _PWF_CHANGE))  {
674 			pw->pw_change = atol(s);
675 			pw->pw_fields |= _PWF_CHANGE;
676 		}
677 
678 		if ((s = strsep(&result, ":")) == NULL) return 0; /* expire */
679 		if(!(pw->pw_fields & _PWF_EXPIRE))  {
680 			pw->pw_expire = atol(s);
681 			pw->pw_fields |= _PWF_EXPIRE;
682 		}
683 	}
684 
685 	if ((s = strsep(&result, ":")) == NULL) return 0; /* gecos */
686 	if(!(pw->pw_fields & _PWF_GECOS)) {
687 		pw->pw_gecos = s;
688 		pw->pw_fields |= _PWF_GECOS;
689 	}
690 
691 	if ((s = strsep(&result, ":")) == NULL) return 0; /* dir */
692 	if(!(pw->pw_fields & _PWF_DIR)) {
693 		pw->pw_dir = s;
694 		pw->pw_fields |= _PWF_DIR;
695 	}
696 
697 	if ((s = strsep(&result, ":")) == NULL) return 0; /* shell */
698 	if(!(pw->pw_fields & _PWF_SHELL)) {
699 		pw->pw_shell = s;
700 		pw->pw_fields |= _PWF_SHELL;
701 	}
702 
703 	/* Be consistent. */
704 	if ((s = strchr(pw->pw_shell, '\n'))) *s = '\0';
705 
706 	return 1;
707 }
708 
709 static int
710 _havemaster(char *_yp_domain)
711 {
712 	int order;
713 	int rval;
714 
715 	if (!(rval = yp_order(_yp_domain, "master.passwd.byname", &order)))
716 		return(YP_HAVE_MASTER);
717 
718 	/*
719 	 * NIS+ in YP compat mode doesn't support
720 	 * YPPROC_ORDER -- no point in continuing.
721 	 */
722 	if (rval == YPERR_YPERR)
723 		return(YP_HAVE_NONE);
724 
725 	/* master.passwd doesn't exist -- try passwd.adjunct */
726 	if (rval == YPERR_MAP) {
727 		rval = yp_order(_yp_domain, "passwd.adjunct.byname", &order);
728 		if (!rval)
729 			return(YP_HAVE_ADJUNCT);
730 	}
731 
732 	return (YP_HAVE_NONE);
733 }
734 
735 static int
736 _getyppass(struct passwd *pw, const char *name, const char *map)
737 {
738 	char *result, *s;
739 	int resultlen;
740 	int rv;
741 	char mastermap[YPMAXRECORD];
742 
743 	if(!_pw_yp_domain) {
744 		if(yp_get_default_domain(&_pw_yp_domain))
745 		  return 0;
746 	}
747 
748 	sprintf(mastermap,"%s",map);
749 
750 	if (_gotmaster == YP_HAVE_MASTER)
751 		sprintf(mastermap,"master.%s", map);
752 
753 	if(yp_match(_pw_yp_domain, (char *)&mastermap, name, strlen(name),
754 		    &result, &resultlen))
755 		return 0;
756 
757 	if (!_pw_stepping_yp) {
758 		s = strchr(result, ':');
759 		if (s) {
760 			*s = '\0';
761 		} else {
762 			/* Must be a malformed entry if no colons. */
763 			free(result);
764 			return(0);
765 		}
766 
767 		if (!verf(result)) {
768 			*s = ':';
769 			free(result);
770 			return(0);
771 		}
772 
773 		*s = ':'; /* Put back the colon we previously replaced with a NUL. */
774 	}
775 
776 	rv = _pw_breakout_yp(pw, result, resultlen, _gotmaster);
777 	free(result);
778 	return(rv);
779 }
780 
781 static int
782 _nextyppass(struct passwd *pw)
783 {
784 	static char *key;
785 	static int keylen;
786 	char *lastkey, *result, *s;
787 	int resultlen;
788 	int rv;
789 	char *map = "passwd.byname";
790 
791 	if(!_pw_yp_domain) {
792 		if(yp_get_default_domain(&_pw_yp_domain))
793 		  return 0;
794 	}
795 
796 	if (_gotmaster == YP_HAVE_MASTER)
797 		map = "master.passwd.byname";
798 
799 	if(!_pw_stepping_yp) {
800 		if(key) free(key);
801 			rv = yp_first(_pw_yp_domain, map,
802 				      &key, &keylen, &result, &resultlen);
803 		if(rv) {
804 			return 0;
805 		}
806 		_pw_stepping_yp = 1;
807 		goto unpack;
808 	} else {
809 tryagain:
810 		lastkey = key;
811 			rv = yp_next(_pw_yp_domain, map, key, keylen,
812 			     &key, &keylen, &result, &resultlen);
813 		free(lastkey);
814 unpack:
815 		if(rv) {
816 			_pw_stepping_yp = 0;
817 			return 0;
818 		}
819 
820 		s = strchr(result, ':');
821 		if (s) {
822 			*s = '\0';
823 		} else {
824 			/* Must be a malformed entry if no colons. */
825 			free(result);
826 			goto tryagain;
827 		}
828 
829 		if (lookup(result)) {
830 			*s = ':';
831 			free(result);
832 			goto tryagain;
833 		}
834 
835 		*s = ':'; /* Put back the colon we previously replaced with a NUL. */
836 		if (_pw_breakout_yp(pw, result, resultlen, _gotmaster)) {
837 			free(result);
838 			return(1);
839 		} else {
840 			free(result);
841 			goto tryagain;
842 		}
843 	}
844 }
845 
846 #endif /* YP */
847