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