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