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