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