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