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