2 * Copyright (c) 1988, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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 $
38 #include "namespace.h"
40 #include <sys/param.h>
51 #include "un-namespace.h"
55 extern void setnetgrent ( char * );
56 extern int getnetgrent ( char **, char **, char ** );
57 extern int innetgr ( const char *, const char *, const char *, const char * );
60 * The lookup techniques and data extraction code here must be kept
61 * in sync with that in `pwd_mkdb'.
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 */
70 #include <rpcsvc/yp_prot.h>
71 #include <rpcsvc/ypclnt.h>
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 *);
96 static int __hashpw(DBT
*);
97 static int __initdb(void);
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
115 char *user
, *host
, *domain
;
116 static int latch
= 0;
117 static struct group
*gr
= NULL
;
121 if (strlen(grp
) == 1) {
122 return(_nextyppass(&_pw_passwd
));
128 if (*gr
->gr_mem
!= NULL
) {
129 if (lookup(*gr
->gr_mem
)) {
133 rv
= _getyppass(&_pw_passwd
,
150 if (getnetgrent(&host
, &user
, &domain
) == 0) {
151 if ((gr
= getgrnam(grp
+2)) != NULL
)
159 if (_getyppass(&_pw_passwd
, user
,
168 return(_getyppass(&_pw_passwd
, grp
+1, "passwd.byname"));
174 while(getnetgrent(&host
, &user
, &domain
) != 0) {
178 if (!rv
&& (gr
= getgrnam(grp
+2)) != NULL
) {
195 char bf
[sizeof(_pw_keynum
) + 1];
198 if (!_pw_db
&& !__initdb())
199 return((struct passwd
*)NULL
);
202 if(_pw_stepping_yp
) {
203 _pw_passwd
= _pw_copy
;
204 if (unwind((char *)&_ypnam
))
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;
216 if(!rv
) return (struct passwd
*)NULL
;
218 if(_pw_passwd
.pw_name
[0] == '+' || _pw_passwd
.pw_name
[0] == '-') {
219 if (_yp_enabled
== -1)
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)
231 /* Ignore YP password file entries when YP is disabled. */
232 if(_pw_passwd
.pw_name
[0] == '+' || _pw_passwd
.pw_name
[0] == '-') {
240 getpwnam(const char *name
)
244 char bf
[UT_NAMESIZE
+ 2];
246 if (!_pw_db
&& !__initdb())
247 return((struct passwd
*)NULL
);
249 bf
[0] = _PW_KEYBYNAME
;
251 if (len
> UT_NAMESIZE
)
253 bcopy(name
, bf
+ 1, len
);
254 key
.data
= (u_char
*)bf
;
256 rval
= __hashpw(&key
);
260 if (_yp_enabled
== -1)
263 rval
= _getyppass(&_pw_passwd
, name
, "passwd.byname");
267 * Prevent login attempts when YP is not enabled but YP entries
268 * are in /etc/master.passwd.
270 if (rval
&& (_pw_passwd
.pw_name
[0] == '+'||
271 _pw_passwd
.pw_name
[0] == '-')) rval
= 0;
275 return(rval
? &_pw_passwd
: (struct passwd
*)NULL
);
283 char bf
[sizeof(keyuid
) + 1];
285 if (!_pw_db
&& !__initdb())
286 return((struct passwd
*)NULL
);
288 bf
[0] = _PW_KEYBYUID
;
290 bcopy(&keyuid
, bf
+ 1, sizeof(keyuid
));
291 key
.data
= (u_char
*)bf
;
292 key
.size
= sizeof(keyuid
) + 1;
293 rval
= __hashpw(&key
);
297 if (_yp_enabled
== -1)
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");
307 * Prevent login attempts when YP is not enabled but YP entries
308 * are in /etc/master.passwd.
310 if (rval
&& (_pw_passwd
.pw_name
[0] == '+'||
311 _pw_passwd
.pw_name
[0] == '-')) rval
= 0;
315 return(rval
? &_pw_passwd
: (struct passwd
*)NULL
);
319 setpassent(int stayopen
)
327 _pw_stayopen
= stayopen
;
345 (_pw_db
->close
)(_pw_db
);
350 (_ypcache
->close
)(_ypcache
);
351 _ypcache
= (DB
*)NULL
;
354 /* Fix for PR #12008 */
365 p
= (geteuid()) ? _PATH_MP_DB
: _PATH_SMP_DB
;
366 _pw_db
= dbopen(p
, O_RDONLY
, 0, DB_HASH
, NULL
);
370 syslog(LOG_ERR
, "%s: %m", p
);
382 if ((_pw_db
->get
)(_pw_db
, key
, &data
, 0))
384 p
= (char *)data
.data
;
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
)))
393 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
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
;
418 char buf
[] = { _PW_KEYYPENABLED
};
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. */
427 if (yp_get_default_domain(&_pw_yp_domain
))
429 _gotmaster
= _havemaster(_pw_yp_domain
);
430 } else _gotmaster
= YP_HAVE_NONE
;
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.
435 if (_ypcache
== (DB
*)NULL
)
436 _ypcache
= dbopen(NULL
, O_RDWR
, 600, DB_HASH
, NULL
);
441 * See if a user is in the blackballed list.
444 lookup(const char *name
)
451 key
.data
= (char *)name
;
452 key
.size
= strlen(name
);
454 if ((_ypcache
->get
)(_ypcache
, &key
, &empty
, 0)) {
462 * Store a blackballed user in an in-core hash database.
465 store(const char *key
)
475 lkey
.data
= (char *)key
;
476 lkey
.size
= strlen(key
);
478 (_ypcache
->put
)(_ypcache
, &lkey
, &empty
, R_NOOVERWRITE
);
482 * See if a user is a member of a particular group.
485 ingr(const char *grp
, const char *name
)
489 if ((gr
= getgrnam(grp
)) == NULL
)
493 if (!strcmp(*gr
->gr_mem
, name
))
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.
508 verf(const char *name
)
511 char bf
[sizeof(_pw_keynum
) + 1];
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
;
528 if (_pw_passwd
.pw_name
[0] != '+' && (_pw_passwd
.pw_name
[0] != '-'))
530 if (_pw_passwd
.pw_name
[0] == '+') {
531 if (strlen(_pw_passwd
.pw_name
) == 1) /* Wildcard */
533 if (_pw_passwd
.pw_name
[1] == '@') {
534 if ((innetgr(_pw_passwd
.pw_name
+2, NULL
, name
,
536 ingr(_pw_passwd
.pw_name
+2, name
)) && !lookup(name
))
541 if (!strcmp(name
, _pw_passwd
.pw_name
+1) &&
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
,
553 ingr(_pw_passwd
.pw_name
+2, name
)) {
559 if (!strcmp(name
, _pw_passwd
.pw_name
+1)) {
571 _get_adjunct_pw(const char *name
)
573 static char adjunctbuf
[YPMAXRECORD
+2];
577 char *map
= "passwd.adjunct.byname";
580 if ((rval
= yp_match(_pw_yp_domain
, map
, name
, strlen(name
),
581 &result
, &resultlen
)))
584 strncpy(adjunctbuf
, result
, resultlen
);
585 adjunctbuf
[resultlen
] = '\0';
587 result
= (char *)&adjunctbuf
;
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 */
599 _pw_breakout_yp(struct passwd
*pw
, char *res
, int resultlen
, int master
)
602 static char resbuf
[YPMAXRECORD
+2];
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
610 if (*res
== '+' || *res
== '-')
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.
619 strncpy((char *)&resbuf
, res
, resultlen
);
620 resbuf
[resultlen
] = '\0';
621 result
= (char *)&resbuf
;
624 * XXX Sanity check: make sure all fields are valid (no NULLs).
625 * If we find a badly formatted entry, we punt.
627 if ((s
= strsep(&result
, ":")) == NULL
) return 0; /* name */
629 * We don't care what pw_fields says: we _always_ want the
630 * username returned to us by NIS.
633 pw
->pw_fields
|= _PWF_NAME
;
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
) {
640 realpw
= _get_adjunct_pw(pw
->pw_name
);
644 pw
->pw_passwd
= realpw
;
648 pw
->pw_fields
|= _PWF_PASSWD
;
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
;
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
;
663 if (master
== YP_HAVE_MASTER
) {
664 if ((s
= strsep(&result
, ":")) == NULL
) return 0; /* class */
665 if(!(pw
->pw_fields
& _PWF_CLASS
)) {
667 pw
->pw_fields
|= _PWF_CLASS
;
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
;
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
;
683 if ((s
= strsep(&result
, ":")) == NULL
) return 0; /* gecos */
684 if(!(pw
->pw_fields
& _PWF_GECOS
)) {
686 pw
->pw_fields
|= _PWF_GECOS
;
689 if ((s
= strsep(&result
, ":")) == NULL
) return 0; /* dir */
690 if(!(pw
->pw_fields
& _PWF_DIR
)) {
692 pw
->pw_fields
|= _PWF_DIR
;
695 if ((s
= strsep(&result
, ":")) == NULL
) return 0; /* shell */
696 if(!(pw
->pw_fields
& _PWF_SHELL
)) {
698 pw
->pw_fields
|= _PWF_SHELL
;
702 if ((s
= strchr(pw
->pw_shell
, '\n'))) *s
= '\0';
708 _havemaster(char *_yp_domain
)
713 if (!(rval
= yp_order(_yp_domain
, "master.passwd.byname", &order
)))
714 return(YP_HAVE_MASTER
);
717 * NIS+ in YP compat mode doesn't support
718 * YPPROC_ORDER -- no point in continuing.
720 if (rval
== YPERR_YPERR
)
721 return(YP_HAVE_NONE
);
723 /* master.passwd doesn't exist -- try passwd.adjunct */
724 if (rval
== YPERR_MAP
) {
725 rval
= yp_order(_yp_domain
, "passwd.adjunct.byname", &order
);
727 return(YP_HAVE_ADJUNCT
);
730 return (YP_HAVE_NONE
);
734 _getyppass(struct passwd
*pw
, const char *name
, const char *map
)
739 char mastermap
[YPMAXRECORD
];
742 if(yp_get_default_domain(&_pw_yp_domain
))
746 if (_gotmaster
== YP_HAVE_MASTER
)
747 snprintf(mastermap
, sizeof(mastermap
), "master.%s", map
);
749 snprintf(mastermap
, sizeof(mastermap
), "%s", map
);
751 if(yp_match(_pw_yp_domain
, (char *)&mastermap
, name
, strlen(name
),
752 &result
, &resultlen
)) {
753 if (_gotmaster
!= YP_HAVE_MASTER
)
755 snprintf(mastermap
, sizeof(mastermap
), "%s", map
);
756 if (yp_match(_pw_yp_domain
, (char *)&mastermap
,
757 name
, strlen(name
), &result
, &resultlen
))
759 _gotmaster
= YP_HAVE_NONE
;
762 if (!_pw_stepping_yp
) {
763 s
= strchr(result
, ':');
767 /* Must be a malformed entry if no colons. */
778 *s
= ':'; /* Put back the colon we previously replaced with a NUL. */
781 rv
= _pw_breakout_yp(pw
, result
, resultlen
, _gotmaster
);
787 _nextyppass(struct passwd
*pw
)
791 char *lastkey
, *result
, *s
;
794 char *map
= "passwd.byname";
797 if(yp_get_default_domain(&_pw_yp_domain
))
801 if (_gotmaster
== YP_HAVE_MASTER
)
802 map
= "master.passwd.byname";
804 if(!_pw_stepping_yp
) {
806 rv
= yp_first(_pw_yp_domain
, map
,
807 &key
, &keylen
, &result
, &resultlen
);
816 rv
= yp_next(_pw_yp_domain
, map
, key
, keylen
,
817 &key
, &keylen
, &result
, &resultlen
);
825 s
= strchr(result
, ':');
829 /* Must be a malformed entry if no colons. */
834 if (lookup(result
)) {
840 *s
= ':'; /* Put back the colon we previously replaced with a NUL. */
841 if (_pw_breakout_yp(pw
, result
, resultlen
, _gotmaster
)) {