getpwent.c revision 1.50 1 /* $NetBSD: getpwent.c,v 1.50 2002/04/16 19:10:07 groo Exp $ */
2
3 /*
4 * Copyright (c) 1988, 1993
5 * The Regents of the University of California. All rights reserved.
6 * Portions Copyright (c) 1994, 1995, Jason Downs. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #include <sys/cdefs.h>
38 #if defined(LIBC_SCCS) && !defined(lint)
39 #if 0
40 static char sccsid[] = "@(#)getpwent.c 8.2 (Berkeley) 4/27/95";
41 #else
42 __RCSID("$NetBSD: getpwent.c,v 1.50 2002/04/16 19:10:07 groo Exp $");
43 #endif
44 #endif /* LIBC_SCCS and not lint */
45
46 #include "namespace.h"
47 #include <sys/param.h>
48
49 #include <assert.h>
50 #include <db.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <limits.h>
54 #include <netgroup.h>
55 #include <nsswitch.h>
56 #include <pwd.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <syslog.h>
60 #include <unistd.h>
61 #include <utmp.h>
62
63 #ifdef HESIOD
64 #include <hesiod.h>
65 #endif
66 #ifdef YP
67 #include <machine/param.h>
68 #include <stdio.h>
69 #include <rpc/rpc.h>
70 #include <rpcsvc/yp_prot.h>
71 #include <rpcsvc/ypclnt.h>
72 #endif
73
74 #ifdef __STDC__
75 #include <stdarg.h>
76 #else
77 #include <varargs.h>
78 #endif
79
80 #include "pw_private.h"
81
82 #if defined(YP) || defined(HESIOD)
83 #define _PASSWD_COMPAT
84 #endif
85
86 #ifdef __weak_alias
87 __weak_alias(endpwent,_endpwent)
88 __weak_alias(getpwent,_getpwent)
89 __weak_alias(getpwnam,_getpwnam)
90 __weak_alias(getpwuid,_getpwuid)
91 __weak_alias(setpassent,_setpassent)
92 __weak_alias(setpwent,_setpwent)
93 #endif
94
95
96 /*
97 * The lookup techniques and data extraction code here must be kept
98 * in sync with that in `pwd_mkdb'.
99 */
100
101 static struct passwd _pw_passwd; /* password structure */
102 static DB *_pw_db; /* password database */
103 static int _pw_keynum; /* key counter. no more records if -1 */
104 static int _pw_stayopen; /* keep fd's open */
105 static int _pw_flags; /* password flags */
106
107 static int __hashpw __P((DBT *));
108 static int __initdb __P((void));
109
110 const char __yp_token[] = "__YP!"; /* Let pwd_mkdb pull this in. */
111 static const ns_src compatsrc[] = {
112 { NSSRC_COMPAT, NS_SUCCESS },
113 { 0 }
114 };
115
116 #ifdef YP
117 static char *__ypcurrent, *__ypdomain;
118 static int __ypcurrentlen;
119 static int _pw_ypdone; /* non-zero if no more yp records */
120 #endif
121
122 #ifdef HESIOD
123 static int _pw_hesnum; /* hes counter. no more records if -1 */
124 #endif
125
126 #ifdef _PASSWD_COMPAT
127 enum _pwmode { PWMODE_NONE, PWMODE_FULL, PWMODE_USER, PWMODE_NETGRP };
128 static enum _pwmode __pwmode;
129
130 enum _ypmap { YPMAP_NONE, YPMAP_ADJUNCT, YPMAP_MASTER };
131
132 static struct passwd *__pwproto = (struct passwd *)NULL;
133 static int __pwproto_flags;
134 static char line[1024];
135 static long prbuf[1024 / sizeof(long)];
136 static DB *__pwexclude = (DB *)NULL;
137
138 static int __pwexclude_add __P((const char *));
139 static int __pwexclude_is __P((const char *));
140 static void __pwproto_set __P((void));
141 static int __ypmaptype __P((void));
142 static int __pwparse __P((struct passwd *, char *));
143
144 /* macros for deciding which YP maps to use. */
145 #define PASSWD_BYNAME (__ypmaptype() == YPMAP_MASTER \
146 ? "master.passwd.byname" : "passwd.byname")
147 #define PASSWD_BYUID (__ypmaptype() == YPMAP_MASTER \
148 ? "master.passwd.byuid" : "passwd.byuid")
149
150 /*
151 * add a name to the compat mode exclude list
152 */
153 static int
154 __pwexclude_add(name)
155 const char *name;
156 {
157 DBT key;
158 DBT data;
159
160 _DIAGASSERT(name != NULL);
161
162 /* initialize the exclusion table if needed. */
163 if(__pwexclude == (DB *)NULL) {
164 __pwexclude = dbopen(NULL, O_RDWR, 600, DB_HASH, NULL);
165 if(__pwexclude == (DB *)NULL)
166 return 1;
167 }
168
169 /* set up the key */
170 key.size = strlen(name);
171 /* LINTED key does not get modified */
172 key.data = (char *)name;
173
174 /* data is nothing. */
175 data.data = NULL;
176 data.size = 0;
177
178 /* store it */
179 if((__pwexclude->put)(__pwexclude, &key, &data, 0) == -1)
180 return 1;
181
182 return 0;
183 }
184
185 /*
186 * test if a name is on the compat mode exclude list
187 */
188 static int
189 __pwexclude_is(name)
190 const char *name;
191 {
192 DBT key;
193 DBT data;
194
195 _DIAGASSERT(name != NULL);
196
197 if(__pwexclude == (DB *)NULL)
198 return 0; /* nothing excluded */
199
200 /* set up the key */
201 key.size = strlen(name);
202 /* LINTED key does not get modified */
203 key.data = (char *)name;
204
205 if((__pwexclude->get)(__pwexclude, &key, &data, 0) == 0)
206 return 1; /* excluded */
207
208 return 0;
209 }
210
211 /*
212 * setup the compat mode prototype template
213 */
214 static void
215 __pwproto_set()
216 {
217 char *ptr;
218 struct passwd *pw = &_pw_passwd;
219
220 /* make this the new prototype */
221 ptr = (char *)(void *)prbuf;
222
223 /* first allocate the struct. */
224 __pwproto = (struct passwd *)(void *)ptr;
225 ptr += sizeof(struct passwd);
226
227 /* name */
228 if(pw->pw_name && (pw->pw_name)[0]) {
229 ptr = (char *)ALIGN((u_long)ptr);
230 memmove(ptr, pw->pw_name, strlen(pw->pw_name) + 1);
231 __pwproto->pw_name = ptr;
232 ptr += (strlen(pw->pw_name) + 1);
233 } else
234 __pwproto->pw_name = (char *)NULL;
235
236 /* password */
237 if(pw->pw_passwd && (pw->pw_passwd)[0]) {
238 ptr = (char *)ALIGN((u_long)ptr);
239 memmove(ptr, pw->pw_passwd, strlen(pw->pw_passwd) + 1);
240 __pwproto->pw_passwd = ptr;
241 ptr += (strlen(pw->pw_passwd) + 1);
242 } else
243 __pwproto->pw_passwd = (char *)NULL;
244
245 /* uid */
246 __pwproto->pw_uid = pw->pw_uid;
247
248 /* gid */
249 __pwproto->pw_gid = pw->pw_gid;
250
251 /* change (ignored anyway) */
252 __pwproto->pw_change = pw->pw_change;
253
254 /* class (ignored anyway) */
255 __pwproto->pw_class = "";
256
257 /* gecos */
258 if(pw->pw_gecos && (pw->pw_gecos)[0]) {
259 ptr = (char *)ALIGN((u_long)ptr);
260 memmove(ptr, pw->pw_gecos, strlen(pw->pw_gecos) + 1);
261 __pwproto->pw_gecos = ptr;
262 ptr += (strlen(pw->pw_gecos) + 1);
263 } else
264 __pwproto->pw_gecos = (char *)NULL;
265
266 /* dir */
267 if(pw->pw_dir && (pw->pw_dir)[0]) {
268 ptr = (char *)ALIGN((u_long)ptr);
269 memmove(ptr, pw->pw_dir, strlen(pw->pw_dir) + 1);
270 __pwproto->pw_dir = ptr;
271 ptr += (strlen(pw->pw_dir) + 1);
272 } else
273 __pwproto->pw_dir = (char *)NULL;
274
275 /* shell */
276 if(pw->pw_shell && (pw->pw_shell)[0]) {
277 ptr = (char *)ALIGN((u_long)ptr);
278 memmove(ptr, pw->pw_shell, strlen(pw->pw_shell) + 1);
279 __pwproto->pw_shell = ptr;
280 ptr += (strlen(pw->pw_shell) + 1);
281 } else
282 __pwproto->pw_shell = (char *)NULL;
283
284 /* expire (ignored anyway) */
285 __pwproto->pw_expire = pw->pw_expire;
286
287 /* flags */
288 __pwproto_flags = _pw_flags;
289 }
290
291 static int
292 __ypmaptype()
293 {
294 static int maptype = -1;
295 int order, r;
296
297 if (maptype != -1)
298 return (maptype);
299
300 maptype = YPMAP_NONE;
301 if (geteuid() != 0)
302 return (maptype);
303
304 if (!__ypdomain) {
305 if( _yp_check(&__ypdomain) == 0)
306 return (maptype);
307 }
308
309 r = yp_order(__ypdomain, "master.passwd.byname", &order);
310 if (r == 0) {
311 maptype = YPMAP_MASTER;
312 return (maptype);
313 }
314
315 /*
316 * NIS+ in YP compat mode doesn't support
317 * YPPROC_ORDER -- no point in continuing.
318 */
319 if (r == YPERR_YPERR)
320 return (maptype);
321
322 /* master.passwd doesn't exist -- try passwd.adjunct */
323 if (r == YPERR_MAP) {
324 r = yp_order(__ypdomain, "passwd.adjunct.byname", &order);
325 if (r == 0)
326 maptype = YPMAP_ADJUNCT;
327 return (maptype);
328 }
329
330 return (maptype);
331 }
332
333 /*
334 * parse a passwd file line (from NIS or HESIOD).
335 * assumed to be `old-style' if maptype != YPMAP_MASTER.
336 */
337 static int
338 __pwparse(pw, s)
339 struct passwd *pw;
340 char *s;
341 {
342 static char adjunctpw[YPMAXRECORD + 2];
343 int flags, maptype;
344
345 _DIAGASSERT(pw != NULL);
346 _DIAGASSERT(s != NULL);
347
348 maptype = __ypmaptype();
349 flags = _PASSWORD_NOWARN;
350 if (maptype != YPMAP_MASTER)
351 flags |= _PASSWORD_OLDFMT;
352 if (! __pw_scan(s, pw, &flags))
353 return 1;
354
355 /* now let the prototype override, if set. */
356 if(__pwproto != (struct passwd *)NULL) {
357 #ifdef PW_OVERRIDE_PASSWD
358 if(__pwproto->pw_passwd != (char *)NULL)
359 pw->pw_passwd = __pwproto->pw_passwd;
360 #endif
361 if(!(__pwproto_flags & _PASSWORD_NOUID))
362 pw->pw_uid = __pwproto->pw_uid;
363 if(!(__pwproto_flags & _PASSWORD_NOGID))
364 pw->pw_gid = __pwproto->pw_gid;
365 if(__pwproto->pw_gecos != (char *)NULL)
366 pw->pw_gecos = __pwproto->pw_gecos;
367 if(__pwproto->pw_dir != (char *)NULL)
368 pw->pw_dir = __pwproto->pw_dir;
369 if(__pwproto->pw_shell != (char *)NULL)
370 pw->pw_shell = __pwproto->pw_shell;
371 }
372 if ((maptype == YPMAP_ADJUNCT) &&
373 (strstr(pw->pw_passwd, "##") != NULL)) {
374 char *data, *bp;
375 int datalen;
376
377 if (yp_match(__ypdomain, "passwd.adjunct.byname", pw->pw_name,
378 (int)strlen(pw->pw_name), &data, &datalen) == 0) {
379 strlcpy(adjunctpw, data, MIN((size_t)datalen,
380 sizeof(adjunctpw)));
381
382 /* skip name to get password */
383 if ((bp = strsep(&data, ":")) != NULL &&
384 (bp = strsep(&data, ":")) != NULL)
385 pw->pw_passwd = bp;
386 }
387 }
388 return 0;
389 }
390 #endif /* _PASSWD_COMPAT */
391
392 /*
393 * local files implementation of getpw*()
394 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
395 */
396 static int _local_getpw __P((void *, void *, va_list));
397
398 /*ARGSUSED*/
399 static int
400 _local_getpw(rv, cb_data, ap)
401 void *rv;
402 void *cb_data;
403 va_list ap;
404 {
405 DBT key;
406 char bf[/*CONSTCOND*/ MAX(MAXLOGNAME, sizeof(_pw_keynum)) + 1];
407 uid_t uid;
408 size_t len;
409 int search, rval;
410 const char *name;
411
412 if (!_pw_db && !__initdb())
413 return NS_UNAVAIL;
414
415 search = va_arg(ap, int);
416 bf[0] = search;
417 switch (search) {
418 case _PW_KEYBYNUM:
419 if (_pw_keynum == -1)
420 return NS_NOTFOUND; /* no more local records */
421 ++_pw_keynum;
422 memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
423 key.size = sizeof(_pw_keynum) + 1;
424 break;
425 case _PW_KEYBYNAME:
426 name = va_arg(ap, const char *);
427 len = strlen(name);
428 if (len > MAXLOGNAME)
429 return NS_NOTFOUND;
430 memmove(bf + 1, name, len);
431 key.size = len + 1;
432 break;
433 case _PW_KEYBYUID:
434 uid = va_arg(ap, uid_t);
435 memmove(bf + 1, &uid, sizeof(uid));
436 key.size = sizeof(uid) + 1;
437 break;
438 default:
439 abort();
440 }
441
442 key.data = (u_char *)bf;
443 rval = __hashpw(&key);
444 if (rval == NS_NOTFOUND && search == _PW_KEYBYNUM)
445 _pw_keynum = -1; /* flag `no more local records' */
446
447 if (!_pw_stayopen && (search != _PW_KEYBYNUM)) {
448 (void)(_pw_db->close)(_pw_db);
449 _pw_db = (DB *)NULL;
450 }
451 return (rval);
452 }
453
454 #ifdef HESIOD
455 /*
456 * hesiod implementation of getpw*()
457 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
458 */
459 static int _dns_getpw __P((void *, void *, va_list));
460
461 /*ARGSUSED*/
462 static int
463 _dns_getpw(rv, cb_data, ap)
464 void *rv;
465 void *cb_data;
466 va_list ap;
467 {
468 const char *name;
469 uid_t uid;
470 int search;
471
472 const char *map;
473 char **hp;
474 void *context;
475 int r;
476
477 search = va_arg(ap, int);
478 nextdnsbynum:
479 switch (search) {
480 case _PW_KEYBYNUM:
481 if (_pw_hesnum == -1)
482 return NS_NOTFOUND; /* no more hesiod records */
483 snprintf(line, sizeof(line) - 1, "passwd-%u", _pw_hesnum);
484 _pw_hesnum++;
485 map = "passwd";
486 break;
487 case _PW_KEYBYNAME:
488 name = va_arg(ap, const char *);
489 strncpy(line, name, sizeof(line));
490 map = "passwd";
491 break;
492 case _PW_KEYBYUID:
493 uid = va_arg(ap, uid_t);
494 snprintf(line, sizeof(line), "%u", (unsigned int)uid);
495 map = "uid"; /* XXX this is `passwd' on ultrix */
496 break;
497 default:
498 abort();
499 }
500 line[sizeof(line) - 1] = '\0';
501
502 r = NS_UNAVAIL;
503 if (hesiod_init(&context) == -1)
504 return (r);
505
506 hp = hesiod_resolve(context, line, map);
507 if (hp == NULL) {
508 if (errno == ENOENT) {
509 /* flag `no more hesiod records' */
510 if (search == _PW_KEYBYNUM)
511 _pw_hesnum = -1;
512 r = NS_NOTFOUND;
513 }
514 goto cleanup_dns_getpw;
515 }
516
517 strncpy(line, hp[0], sizeof(line)); /* only check first elem */
518 line[sizeof(line) - 1] = '\0';
519 hesiod_free_list(context, hp);
520 if (__pwparse(&_pw_passwd, line)) {
521 if (search == _PW_KEYBYNUM)
522 goto nextdnsbynum; /* skip dogdy entries */
523 r = NS_UNAVAIL;
524 } else
525 r = NS_SUCCESS;
526 cleanup_dns_getpw:
527 hesiod_end(context);
528 return (r);
529 }
530 #endif
531
532 #ifdef YP
533 /*
534 * nis implementation of getpw*()
535 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
536 */
537 static int _nis_getpw __P((void *, void *, va_list));
538
539 /*ARGSUSED*/
540 static int
541 _nis_getpw(rv, cb_data, ap)
542 void *rv;
543 void *cb_data;
544 va_list ap;
545 {
546 const char *name;
547 uid_t uid;
548 int search;
549 char *key, *data;
550 const char *map;
551 int keylen, datalen, r, rval;
552
553 if(__ypdomain == NULL) {
554 if(_yp_check(&__ypdomain) == 0)
555 return NS_UNAVAIL;
556 }
557
558 map = PASSWD_BYNAME;
559 search = va_arg(ap, int);
560 switch (search) {
561 case _PW_KEYBYNUM:
562 break;
563 case _PW_KEYBYNAME:
564 name = va_arg(ap, const char *);
565 strncpy(line, name, sizeof(line));
566 break;
567 case _PW_KEYBYUID:
568 uid = va_arg(ap, uid_t);
569 snprintf(line, sizeof(line), "%u", (unsigned int)uid);
570 map = PASSWD_BYUID;
571 break;
572 default:
573 abort();
574 }
575 line[sizeof(line) - 1] = '\0';
576 rval = NS_UNAVAIL;
577 if (search != _PW_KEYBYNUM) {
578 data = NULL;
579 r = yp_match(__ypdomain, map, line, (int)strlen(line),
580 &data, &datalen);
581 if (r == YPERR_KEY)
582 rval = NS_NOTFOUND;
583 if (r != 0) {
584 if (data)
585 free(data);
586 return (rval);
587 }
588 data[datalen] = '\0'; /* clear trailing \n */
589 strncpy(line, data, sizeof(line));
590 line[sizeof(line) - 1] = '\0';
591 free(data);
592 if (__pwparse(&_pw_passwd, line))
593 return NS_UNAVAIL;
594 return NS_SUCCESS;
595 }
596
597 if (_pw_ypdone)
598 return NS_NOTFOUND;
599 for (;;) {
600 data = key = NULL;
601 if (__ypcurrent) {
602 r = yp_next(__ypdomain, map,
603 __ypcurrent, __ypcurrentlen,
604 &key, &keylen, &data, &datalen);
605 free(__ypcurrent);
606 switch (r) {
607 case 0:
608 __ypcurrent = key;
609 __ypcurrentlen = keylen;
610 break;
611 case YPERR_NOMORE:
612 __ypcurrent = NULL;
613 /* flag `no more yp records' */
614 _pw_ypdone = 1;
615 rval = NS_NOTFOUND;
616 }
617 } else {
618 r = yp_first(__ypdomain, map, &__ypcurrent,
619 &__ypcurrentlen, &data, &datalen);
620 }
621 if (r != 0) {
622 if (key)
623 free(key);
624 if (data)
625 free(data);
626 return (rval);
627 }
628 data[datalen] = '\0'; /* clear trailing \n */
629 strncpy(line, data, sizeof(line));
630 line[sizeof(line) - 1] = '\0';
631 free(data);
632 if (! __pwparse(&_pw_passwd, line))
633 return NS_SUCCESS;
634 }
635 /* NOTREACHED */
636 } /* _nis_getpw */
637 #endif
638
639 #ifdef _PASSWD_COMPAT
640 /*
641 * See if the compat token is in the database. Only works if pwd_mkdb knows
642 * about the token.
643 */
644 static int __has_compatpw __P((void));
645
646 static int
647 __has_compatpw()
648 {
649 DBT key, data;
650 DBT pkey, pdata;
651 char bf[MAXLOGNAME];
652
653 /*LINTED*/
654 key.data = (u_char *)__yp_token;
655 key.size = strlen(__yp_token);
656
657 /* Pre-token database support. */
658 bf[0] = _PW_KEYBYNAME;
659 bf[1] = '+';
660 pkey.data = (u_char *)bf;
661 pkey.size = 2;
662
663 if ((_pw_db->get)(_pw_db, &key, &data, 0)
664 && (_pw_db->get)(_pw_db, &pkey, &pdata, 0))
665 return 0; /* No compat token */
666 return 1;
667 }
668
669 /*
670 * log an error if "files" or "compat" is specified in passwd_compat database
671 */
672 static int _bad_getpw __P((void *, void *, va_list));
673
674 /*ARGSUSED*/
675 static int
676 _bad_getpw(rv, cb_data, ap)
677 void *rv;
678 void *cb_data;
679 va_list ap;
680 {
681 static int warned;
682
683 _DIAGASSERT(cb_data != NULL);
684
685 if (!warned) {
686 syslog(LOG_ERR,
687 "nsswitch.conf passwd_compat database can't use '%s'",
688 (char *)cb_data);
689 }
690 warned = 1;
691 return NS_UNAVAIL;
692 }
693
694 /*
695 * when a name lookup in compat mode is required (e.g., '+name', or a name in
696 * '+@netgroup'), look it up in the 'passwd_compat' nsswitch database.
697 * only Hesiod and NIS is supported - it doesn't make sense to lookup
698 * compat names from 'files' or 'compat'.
699 */
700 static int __getpwcompat __P((int, uid_t, const char *));
701
702 static int
703 __getpwcompat(type, uid, name)
704 int type;
705 uid_t uid;
706 const char *name;
707 {
708 static const ns_dtab dtab[] = {
709 NS_FILES_CB(_bad_getpw, "files")
710 NS_DNS_CB(_dns_getpw, NULL)
711 NS_NIS_CB(_nis_getpw, NULL)
712 NS_COMPAT_CB(_bad_getpw, "compat")
713 { 0 }
714 };
715 static const ns_src defaultnis[] = {
716 { NSSRC_NIS, NS_SUCCESS },
717 { 0 }
718 };
719
720 switch (type) {
721 case _PW_KEYBYNUM:
722 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
723 defaultnis, type);
724 case _PW_KEYBYNAME:
725 _DIAGASSERT(name != NULL);
726 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
727 defaultnis, type, name);
728 case _PW_KEYBYUID:
729 return nsdispatch(NULL, dtab, NSDB_PASSWD_COMPAT, "getpwcompat",
730 defaultnis, type, uid);
731 default:
732 abort();
733 /*NOTREACHED*/
734 }
735 }
736 #endif /* _PASSWD_COMPAT */
737
738 /*
739 * compat implementation of getpwent()
740 * varargs (ignored):
741 * type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
742 */
743 static int _compat_getpwent __P((void *, void *, va_list));
744
745 /*ARGSUSED*/
746 static int
747 _compat_getpwent(rv, cb_data, ap)
748 void *rv;
749 void *cb_data;
750 va_list ap;
751 {
752 DBT key;
753 int rval;
754 char bf[sizeof(_pw_keynum) + 1];
755 #ifdef _PASSWD_COMPAT
756 static char *name = NULL;
757 const char *user, *host, *dom;
758 int has_compatpw;
759 #endif
760
761 if (!_pw_db && !__initdb())
762 return NS_UNAVAIL;
763
764 #ifdef _PASSWD_COMPAT
765 has_compatpw = __has_compatpw();
766
767 again:
768 if (has_compatpw && (__pwmode != PWMODE_NONE)) {
769 int r;
770
771 switch (__pwmode) {
772 case PWMODE_FULL:
773 r = __getpwcompat(_PW_KEYBYNUM, 0, NULL);
774 if (r == NS_SUCCESS)
775 return r;
776 __pwmode = PWMODE_NONE;
777 break;
778
779 case PWMODE_NETGRP:
780 r = getnetgrent(&host, &user, &dom);
781 if (r == 0) { /* end of group */
782 endnetgrent();
783 __pwmode = PWMODE_NONE;
784 break;
785 }
786 if (!user || !*user)
787 break;
788 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
789 if (r == NS_SUCCESS)
790 return r;
791 break;
792
793 case PWMODE_USER:
794 if (name == NULL) {
795 __pwmode = PWMODE_NONE;
796 break;
797 }
798 r = __getpwcompat(_PW_KEYBYNAME, 0, name);
799 free(name);
800 name = NULL;
801 if (r == NS_SUCCESS)
802 return r;
803 break;
804
805 case PWMODE_NONE:
806 abort();
807 }
808 goto again;
809 }
810 #endif
811
812 if (_pw_keynum == -1)
813 return NS_NOTFOUND; /* no more local records */
814 ++_pw_keynum;
815 bf[0] = _PW_KEYBYNUM;
816 memmove(bf + 1, &_pw_keynum, sizeof(_pw_keynum));
817 key.data = (u_char *)bf;
818 key.size = sizeof(_pw_keynum) + 1;
819 rval = __hashpw(&key);
820 if (rval == NS_NOTFOUND)
821 _pw_keynum = -1; /* flag `no more local records' */
822 else if (rval == NS_SUCCESS) {
823 #ifdef _PASSWD_COMPAT
824 /* if we don't have YP at all, don't bother. */
825 if (has_compatpw) {
826 if(_pw_passwd.pw_name[0] == '+') {
827 /* set the mode */
828 switch(_pw_passwd.pw_name[1]) {
829 case '\0':
830 __pwmode = PWMODE_FULL;
831 break;
832 case '@':
833 __pwmode = PWMODE_NETGRP;
834 setnetgrent(_pw_passwd.pw_name + 2);
835 break;
836 default:
837 __pwmode = PWMODE_USER;
838 name = strdup(_pw_passwd.pw_name + 1);
839 break;
840 }
841
842 /* save the prototype */
843 __pwproto_set();
844 goto again;
845 } else if(_pw_passwd.pw_name[0] == '-') {
846 /* an attempted exclusion */
847 switch(_pw_passwd.pw_name[1]) {
848 case '\0':
849 break;
850 case '@':
851 setnetgrent(_pw_passwd.pw_name + 2);
852 while(getnetgrent(&host, &user, &dom)) {
853 if(user && *user)
854 __pwexclude_add(user);
855 }
856 endnetgrent();
857 break;
858 default:
859 __pwexclude_add(_pw_passwd.pw_name + 1);
860 break;
861 }
862 goto again;
863 }
864 }
865 #endif
866 }
867 return (rval);
868 }
869
870 /*
871 * compat implementation of getpwnam() and getpwuid()
872 * varargs: type, [ uid (type == _PW_KEYBYUID) | name (type == _PW_KEYBYNAME) ]
873 */
874 static int _compat_getpw __P((void *, void *, va_list));
875
876 static int
877 _compat_getpw(rv, cb_data, ap)
878 void *rv;
879 void *cb_data;
880 va_list ap;
881 {
882 #ifdef _PASSWD_COMPAT
883 DBT key;
884 int search, rval, r, s, keynum;
885 uid_t uid;
886 char bf[sizeof(keynum) + 1];
887 const char *name, *host, *user, *dom;
888 #endif
889
890 if (!_pw_db && !__initdb())
891 return NS_UNAVAIL;
892
893 /*
894 * If there isn't a compat token in the database, use files.
895 */
896 #ifdef _PASSWD_COMPAT
897 if (! __has_compatpw())
898 #endif
899 return (_local_getpw(rv, cb_data, ap));
900
901 #ifdef _PASSWD_COMPAT
902 search = va_arg(ap, int);
903 uid = 0;
904 name = NULL;
905 rval = NS_NOTFOUND;
906 switch (search) {
907 case _PW_KEYBYNAME:
908 name = va_arg(ap, const char *);
909 break;
910 case _PW_KEYBYUID:
911 uid = va_arg(ap, uid_t);
912 break;
913 default:
914 abort();
915 }
916
917 for (s = -1, keynum = 1 ; ; keynum++) {
918 bf[0] = _PW_KEYBYNUM;
919 memmove(bf + 1, &keynum, sizeof(keynum));
920 key.data = (u_char *)bf;
921 key.size = sizeof(keynum) + 1;
922 if(__hashpw(&key) != NS_SUCCESS)
923 break;
924 switch(_pw_passwd.pw_name[0]) {
925 case '+':
926 /* save the prototype */
927 __pwproto_set();
928
929 switch(_pw_passwd.pw_name[1]) {
930 case '\0':
931 r = __getpwcompat(search, uid, name);
932 if (r != NS_SUCCESS)
933 continue;
934 break;
935 case '@':
936 pwnam_netgrp:
937 #if 0 /* XXX: is this a hangover from pre-nsswitch? */
938 if(__ypcurrent) {
939 free(__ypcurrent);
940 __ypcurrent = NULL;
941 }
942 #endif
943 if (s == -1) /* first time */
944 setnetgrent(_pw_passwd.pw_name + 2);
945 s = getnetgrent(&host, &user, &dom);
946 if (s == 0) { /* end of group */
947 endnetgrent();
948 s = -1;
949 continue;
950 }
951 if (!user || !*user)
952 goto pwnam_netgrp;
953
954 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
955
956 if (r == NS_UNAVAIL)
957 return r;
958 if (r == NS_NOTFOUND) {
959 /*
960 * just because this user is bad
961 * it doesn't mean they all are.
962 */
963 goto pwnam_netgrp;
964 }
965 break;
966 default:
967 user = _pw_passwd.pw_name + 1;
968 r = __getpwcompat(_PW_KEYBYNAME, 0, user);
969
970 if (r == NS_UNAVAIL)
971 return r;
972 if (r == NS_NOTFOUND)
973 continue;
974 break;
975 }
976 if(__pwexclude_is(_pw_passwd.pw_name)) {
977 if(s == 1) /* inside netgroup */
978 goto pwnam_netgrp;
979 continue;
980 }
981 break;
982 case '-':
983 /* attempted exclusion */
984 switch(_pw_passwd.pw_name[1]) {
985 case '\0':
986 break;
987 case '@':
988 setnetgrent(_pw_passwd.pw_name + 2);
989 while(getnetgrent(&host, &user, &dom)) {
990 if(user && *user)
991 __pwexclude_add(user);
992 }
993 endnetgrent();
994 break;
995 default:
996 __pwexclude_add(_pw_passwd.pw_name + 1);
997 break;
998 }
999 break;
1000 }
1001 if ((search == _PW_KEYBYNAME &&
1002 strcmp(_pw_passwd.pw_name, name) == 0)
1003 || (search == _PW_KEYBYUID && _pw_passwd.pw_uid == uid)) {
1004 rval = NS_SUCCESS;
1005 break;
1006 }
1007 if(s == 1) /* inside netgroup */
1008 goto pwnam_netgrp;
1009 continue;
1010 }
1011 __pwproto = (struct passwd *)NULL;
1012
1013 if (!_pw_stayopen) {
1014 (void)(_pw_db->close)(_pw_db);
1015 _pw_db = (DB *)NULL;
1016 }
1017 if(__pwexclude != (DB *)NULL) {
1018 (void)(__pwexclude->close)(__pwexclude);
1019 __pwexclude = (DB *)NULL;
1020 }
1021 return rval;
1022 #endif /* _PASSWD_COMPAT */
1023 }
1024
1025 struct passwd *
1026 getpwent()
1027 {
1028 int r;
1029 static const ns_dtab dtab[] = {
1030 NS_FILES_CB(_local_getpw, NULL)
1031 NS_DNS_CB(_dns_getpw, NULL)
1032 NS_NIS_CB(_nis_getpw, NULL)
1033 NS_COMPAT_CB(_compat_getpwent, NULL)
1034 { 0 }
1035 };
1036
1037 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwent", compatsrc,
1038 _PW_KEYBYNUM);
1039 if (r != NS_SUCCESS)
1040 return (struct passwd *)NULL;
1041 return &_pw_passwd;
1042 }
1043
1044 struct passwd *
1045 getpwnam(name)
1046 const char *name;
1047 {
1048 int r;
1049 static const ns_dtab dtab[] = {
1050 NS_FILES_CB(_local_getpw, NULL)
1051 NS_DNS_CB(_dns_getpw, NULL)
1052 NS_NIS_CB(_nis_getpw, NULL)
1053 NS_COMPAT_CB(_compat_getpw, NULL)
1054 { 0 }
1055 };
1056
1057 if (name == NULL || name[0] == '\0')
1058 return (struct passwd *)NULL;
1059
1060 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwnam", compatsrc,
1061 _PW_KEYBYNAME, name);
1062 return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1063 }
1064
1065 struct passwd *
1066 getpwuid(uid)
1067 uid_t uid;
1068 {
1069 int r;
1070 static const ns_dtab dtab[] = {
1071 NS_FILES_CB(_local_getpw, NULL)
1072 NS_DNS_CB(_dns_getpw, NULL)
1073 NS_NIS_CB(_nis_getpw, NULL)
1074 NS_COMPAT_CB(_compat_getpw, NULL)
1075 { 0 }
1076 };
1077
1078 r = nsdispatch(NULL, dtab, NSDB_PASSWD, "getpwuid", compatsrc,
1079 _PW_KEYBYUID, uid);
1080 return (r == NS_SUCCESS ? &_pw_passwd : (struct passwd *)NULL);
1081 }
1082
1083 int
1084 setpassent(stayopen)
1085 int stayopen;
1086 {
1087 _pw_keynum = 0;
1088 _pw_stayopen = stayopen;
1089 #ifdef YP
1090 __pwmode = PWMODE_NONE;
1091 if(__ypcurrent)
1092 free(__ypcurrent);
1093 __ypcurrent = NULL;
1094 _pw_ypdone = 0;
1095 #endif
1096 #ifdef HESIOD
1097 _pw_hesnum = 0;
1098 #endif
1099 #ifdef _PASSWD_COMPAT
1100 if(__pwexclude != (DB *)NULL) {
1101 (void)(__pwexclude->close)(__pwexclude);
1102 __pwexclude = (DB *)NULL;
1103 }
1104 __pwproto = (struct passwd *)NULL;
1105 #endif
1106 return 1;
1107 }
1108
1109 void
1110 setpwent()
1111 {
1112 (void) setpassent(0);
1113 }
1114
1115 void
1116 endpwent()
1117 {
1118 _pw_keynum = 0;
1119 if (_pw_db) {
1120 (void)(_pw_db->close)(_pw_db);
1121 _pw_db = (DB *)NULL;
1122 }
1123 #ifdef _PASSWD_COMPAT
1124 __pwmode = PWMODE_NONE;
1125 #endif
1126 #ifdef YP
1127 if(__ypcurrent)
1128 free(__ypcurrent);
1129 __ypcurrent = NULL;
1130 _pw_ypdone = 0;
1131 #endif
1132 #ifdef HESIOD
1133 _pw_hesnum = 0;
1134 #endif
1135 #ifdef _PASSWD_COMPAT
1136 if(__pwexclude != (DB *)NULL) {
1137 (void)(__pwexclude->close)(__pwexclude);
1138 __pwexclude = (DB *)NULL;
1139 }
1140 __pwproto = (struct passwd *)NULL;
1141 #endif
1142 }
1143
1144 static int
1145 __initdb()
1146 {
1147 static int warned;
1148 char *p;
1149
1150 #ifdef _PASSWD_COMPAT
1151 __pwmode = PWMODE_NONE;
1152 #endif
1153 if (geteuid() == 0) {
1154 _pw_db = dbopen((p = _PATH_SMP_DB), O_RDONLY, 0, DB_HASH, NULL);
1155 if (_pw_db)
1156 return(1);
1157 }
1158 _pw_db = dbopen((p = _PATH_MP_DB), O_RDONLY, 0, DB_HASH, NULL);
1159 if (_pw_db)
1160 return 1;
1161 if (!warned)
1162 syslog(LOG_ERR, "%s: %m", p);
1163 warned = 1;
1164 return 0;
1165 }
1166
1167 static int
1168 __hashpw(key)
1169 DBT *key;
1170 {
1171 char *p, *t, *oldbuf;
1172 static u_int max;
1173 static char *buf;
1174 DBT data;
1175
1176 _DIAGASSERT(key != NULL);
1177
1178 switch ((_pw_db->get)(_pw_db, key, &data, 0)) {
1179 case 0:
1180 break; /* found */
1181 case 1:
1182 return NS_NOTFOUND;
1183 case -1:
1184 return NS_UNAVAIL; /* error in db routines */
1185 default:
1186 abort();
1187 }
1188
1189 p = (char *)data.data;
1190 if (data.size > max) {
1191 max = roundup(data.size, 1024);
1192 oldbuf = buf;
1193 if ((buf = realloc(buf, max)) == NULL) {
1194 if (oldbuf != NULL)
1195 free(oldbuf);
1196 max = 0;
1197 return NS_UNAVAIL;
1198 }
1199 }
1200
1201 /* THIS CODE MUST MATCH THAT IN pwd_mkdb. */
1202 t = buf;
1203 #define EXPAND(e) e = t; while ((*t++ = *p++));
1204 #define SCALAR(v) memmove(&(v), p, sizeof v); p += sizeof v
1205 EXPAND(_pw_passwd.pw_name);
1206 EXPAND(_pw_passwd.pw_passwd);
1207 SCALAR(_pw_passwd.pw_uid);
1208 SCALAR(_pw_passwd.pw_gid);
1209 SCALAR(_pw_passwd.pw_change);
1210 EXPAND(_pw_passwd.pw_class);
1211 EXPAND(_pw_passwd.pw_gecos);
1212 EXPAND(_pw_passwd.pw_dir);
1213 EXPAND(_pw_passwd.pw_shell);
1214 SCALAR(_pw_passwd.pw_expire);
1215
1216 /* See if there's any data left. If so, read in flags. */
1217 if (data.size > (p - (char *)data.data)) {
1218 SCALAR(_pw_flags);
1219 } else
1220 _pw_flags = _PASSWORD_NOUID|_PASSWORD_NOGID; /* default */
1221
1222 return NS_SUCCESS;
1223 }
1224