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