1 /* $NetBSD: pwcache.c,v 1.31 2010/03/23 20:28:59 drochner Exp $ */
4 * Copyright (c) 1992 Keith Muller.
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Keith Muller of the University of California, San Diego.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * Copyright (c) 2002 The NetBSD Foundation, Inc.
38 * All rights reserved.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
49 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59 * POSSIBILITY OF SUCH DAMAGE.
62 #if HAVE_NBTOOL_CONFIG_H
63 #include "nbtool_config.h"
65 * XXX Undefine the renames of these functions so that we don't
66 * XXX rename the versions found in the host's <pwd.h> by mistake!
72 #include <sys/cdefs.h>
73 #if defined(LIBC_SCCS) && !defined(lint)
75 static char sccsid
[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93";
77 __RCSID("$NetBSD: pwcache.c,v 1.31 2010/03/23 20:28:59 drochner Exp $");
79 #endif /* LIBC_SCCS and not lint */
81 #include "namespace.h"
83 #include <sys/types.h>
84 #include <sys/param.h>
94 #if HAVE_NBTOOL_CONFIG_H
95 /* XXX Now, re-apply the renaming that we undid above. */
96 #define group_from_gid __nbcompat_group_from_gid
97 #define user_from_uid __nbcompat_user_from_uid
101 __weak_alias(user_from_uid
,_user_from_uid
)
102 __weak_alias(group_from_gid
,_group_from_gid
)
103 __weak_alias(pwcache_groupdb
,_pwcache_groupdb
)
106 #if !HAVE_PWCACHE_USERDB || HAVE_NBTOOL_CONFIG_H
110 * routines that control user, group, uid and gid caches (for the archive
111 * member print routine).
113 * these routines cache BOTH hits and misses, a major performance improvement
117 * function pointers to various name lookup routines.
118 * these may be changed as necessary.
120 static int (*_pwcache_setgroupent
)(int) = setgroupent
;
121 static void (*_pwcache_endgrent
)(void) = endgrent
;
122 static struct group
* (*_pwcache_getgrnam
)(const char *) = getgrnam
;
123 static struct group
* (*_pwcache_getgrgid
)(gid_t
) = getgrgid
;
124 static int (*_pwcache_setpassent
)(int) = setpassent
;
125 static void (*_pwcache_endpwent
)(void) = endpwent
;
126 static struct passwd
* (*_pwcache_getpwnam
)(const char *) = getpwnam
;
127 static struct passwd
* (*_pwcache_getpwuid
)(uid_t
) = getpwuid
;
132 static int pwopn
; /* is password file open */
133 static int gropn
; /* is group file open */
134 static UIDC
**uidtb
; /* uid to name cache */
135 static GIDC
**gidtb
; /* gid to name cache */
136 static UIDC
**usrtb
; /* user name to uid cache */
137 static GIDC
**grptb
; /* group name to gid cache */
139 static int uidtb_fail
; /* uidtb_start() failed ? */
140 static int gidtb_fail
; /* gidtb_start() failed ? */
141 static int usrtb_fail
; /* usrtb_start() failed ? */
142 static int grptb_fail
; /* grptb_start() failed ? */
145 static u_int
st_hash(const char *, size_t, int);
146 static int uidtb_start(void);
147 static int gidtb_start(void);
148 static int usrtb_start(void);
149 static int grptb_start(void);
153 st_hash(const char *name
, size_t len
, int tabsz
)
157 _DIAGASSERT(name
!= NULL
);
161 key
= (key
<< 8) | (key
>> 24);
164 return (key
% tabsz
);
169 * creates an an empty uidtb
171 * 0 if ok, -1 otherwise
181 if ((uidtb
= (UIDC
**)calloc(UID_SZ
, sizeof(UIDC
*))) == NULL
) {
190 * creates an an empty gidtb
192 * 0 if ok, -1 otherwise
202 if ((gidtb
= (GIDC
**)calloc(GID_SZ
, sizeof(GIDC
*))) == NULL
) {
211 * creates an an empty usrtb
213 * 0 if ok, -1 otherwise
223 if ((usrtb
= (UIDC
**)calloc(UNM_SZ
, sizeof(UIDC
*))) == NULL
) {
232 * creates an an empty grptb
234 * 0 if ok, -1 otherwise
244 if ((grptb
= (GIDC
**)calloc(GNM_SZ
, sizeof(GIDC
*))) == NULL
) {
253 * caches the name (if any) for the uid. If noname clear, we always
254 * return the stored name (if valid or invalid match).
255 * We use a simple hash table.
257 * Pointer to stored name (or a empty string)
260 user_from_uid(uid_t uid
, int noname
)
265 if ((uidtb
== NULL
) && (uidtb_start() < 0))
269 * see if we have this uid cached
271 pptr
= uidtb
+ (uid
% UID_SZ
);
274 if ((ptr
!= NULL
) && (ptr
->valid
> 0) && (ptr
->uid
== uid
)) {
276 * have an entry for this uid
278 if (!noname
|| (ptr
->valid
== VALID
))
284 * No entry for this uid, we will add it
287 if (_pwcache_setpassent
!= NULL
)
288 (*_pwcache_setpassent
)(1);
293 *pptr
= ptr
= (UIDC
*)malloc(sizeof(UIDC
));
295 if ((pw
= (*_pwcache_getpwuid
)(uid
)) == NULL
) {
297 * no match for this uid in the local password file
298 * a string that is the uid in numeric format
303 (void)snprintf(ptr
->name
, UNMLEN
, "%lu", (long) uid
);
304 ptr
->valid
= INVALID
;
309 * there is an entry for this uid in the password file
312 return (pw
->pw_name
);
314 (void)strlcpy(ptr
->name
, pw
->pw_name
, UNMLEN
);
322 * caches the name (if any) for the gid. If noname clear, we always
323 * return the stored name (if valid or invalid match).
324 * We use a simple hash table.
326 * Pointer to stored name (or a empty string)
329 group_from_gid(gid_t gid
, int noname
)
334 if ((gidtb
== NULL
) && (gidtb_start() < 0))
338 * see if we have this gid cached
340 pptr
= gidtb
+ (gid
% GID_SZ
);
343 if ((ptr
!= NULL
) && (ptr
->valid
> 0) && (ptr
->gid
== gid
)) {
345 * have an entry for this gid
347 if (!noname
|| (ptr
->valid
== VALID
))
353 * No entry for this gid, we will add it
356 if (_pwcache_setgroupent
!= NULL
)
357 (*_pwcache_setgroupent
)(1);
362 *pptr
= ptr
= (GIDC
*)malloc(sizeof(GIDC
));
364 if ((gr
= (*_pwcache_getgrgid
)(gid
)) == NULL
) {
366 * no match for this gid in the local group file, put in
367 * a string that is the gid in numberic format
372 (void)snprintf(ptr
->name
, GNMLEN
, "%lu", (long) gid
);
373 ptr
->valid
= INVALID
;
378 * there is an entry for this group in the group file
381 return (gr
->gr_name
);
383 (void)strlcpy(ptr
->name
, gr
->gr_name
, GNMLEN
);
391 * caches the uid for a given user name. We use a simple hash table.
393 * the uid (if any) for a user name, or a -1 if no match can be found
396 uid_from_user(const char *name
, uid_t
*uid
)
403 * return -1 for mangled names
405 if (name
== NULL
|| ((namelen
= strlen(name
)) == 0))
407 if ((usrtb
== NULL
) && (usrtb_start() < 0))
411 * look up in hash table, if found and valid return the uid,
412 * if found and invalid, return a -1
414 pptr
= usrtb
+ st_hash(name
, namelen
, UNM_SZ
);
417 if ((ptr
!= NULL
) && (ptr
->valid
> 0) && !strcmp(name
, ptr
->name
)) {
418 if (ptr
->valid
== INVALID
)
425 if (_pwcache_setpassent
!= NULL
)
426 (*_pwcache_setpassent
)(1);
431 *pptr
= ptr
= (UIDC
*)malloc(sizeof(UIDC
));
434 * no match, look it up, if no match store it as an invalid entry,
435 * or store the matching uid
438 if ((pw
= (*_pwcache_getpwnam
)(name
)) == NULL
)
443 (void)strlcpy(ptr
->name
, name
, UNMLEN
);
444 if ((pw
= (*_pwcache_getpwnam
)(name
)) == NULL
) {
445 ptr
->valid
= INVALID
;
449 *uid
= ptr
->uid
= pw
->pw_uid
;
455 * caches the gid for a given group name. We use a simple hash table.
457 * the gid (if any) for a group name, or a -1 if no match can be found
460 gid_from_group(const char *name
, gid_t
*gid
)
467 * return -1 for mangled names
469 if (name
== NULL
|| ((namelen
= strlen(name
)) == 0))
471 if ((grptb
== NULL
) && (grptb_start() < 0))
475 * look up in hash table, if found and valid return the uid,
476 * if found and invalid, return a -1
478 pptr
= grptb
+ st_hash(name
, namelen
, GID_SZ
);
481 if ((ptr
!= NULL
) && (ptr
->valid
> 0) && !strcmp(name
, ptr
->name
)) {
482 if (ptr
->valid
== INVALID
)
489 if (_pwcache_setgroupent
!= NULL
)
490 (*_pwcache_setgroupent
)(1);
495 *pptr
= ptr
= (GIDC
*)malloc(sizeof(GIDC
));
498 * no match, look it up, if no match store it as an invalid entry,
499 * or store the matching gid
502 if ((gr
= (*_pwcache_getgrnam
)(name
)) == NULL
)
508 (void)strlcpy(ptr
->name
, name
, GNMLEN
);
509 if ((gr
= (*_pwcache_getgrnam
)(name
)) == NULL
) {
510 ptr
->valid
= INVALID
;
514 *gid
= ptr
->gid
= gr
->gr_gid
;
518 #define FLUSHTB(arr, len, fail) \
521 for (i = 0; i < len; i++) \
522 if (arr[i] != NULL) \
527 } while (/* CONSTCOND */0);
531 int (*a_setpassent
)(int),
532 void (*a_endpwent
)(void),
533 struct passwd
* (*a_getpwnam
)(const char *),
534 struct passwd
* (*a_getpwuid
)(uid_t
))
538 /* a_setpassent and a_endpwent may be NULL */
539 if (a_getpwnam
== NULL
|| a_getpwuid
== NULL
)
542 if (_pwcache_endpwent
!= NULL
)
543 (*_pwcache_endpwent
)();
544 FLUSHTB(uidtb
, UID_SZ
, uidtb_fail
);
545 FLUSHTB(usrtb
, UNM_SZ
, usrtb_fail
);
547 _pwcache_setpassent
= a_setpassent
;
548 _pwcache_endpwent
= a_endpwent
;
549 _pwcache_getpwnam
= a_getpwnam
;
550 _pwcache_getpwuid
= a_getpwuid
;
557 int (*a_setgroupent
)(int),
558 void (*a_endgrent
)(void),
559 struct group
* (*a_getgrnam
)(const char *),
560 struct group
* (*a_getgrgid
)(gid_t
))
564 /* a_setgroupent and a_endgrent may be NULL */
565 if (a_getgrnam
== NULL
|| a_getgrgid
== NULL
)
568 if (_pwcache_endgrent
!= NULL
)
569 (*_pwcache_endgrent
)();
570 FLUSHTB(gidtb
, GID_SZ
, gidtb_fail
);
571 FLUSHTB(grptb
, GNM_SZ
, grptb_fail
);
573 _pwcache_setgroupent
= a_setgroupent
;
574 _pwcache_endgrent
= a_endgrent
;
575 _pwcache_getgrnam
= a_getgrnam
;
576 _pwcache_getgrgid
= a_getgrgid
;
585 test_getpwnam(const char *name
)
587 static struct passwd foo
;
589 memset(&foo
, 0, sizeof(foo
));
590 if (strcmp(name
, "toor") == 0) {
594 return (getpwnam(name
));
598 main(int argc
, char *argv
[])
603 printf("pass 1 (default userdb)\n");
604 for (i
= 1; i
< argc
; i
++) {
605 printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
606 i
, pwopn
, usrtb_fail
, usrtb
);
607 r
= uid_from_user(argv
[i
], &u
);
609 printf(" uid_from_user %s: failed\n", argv
[i
]);
611 printf(" uid_from_user %s: %d\n", argv
[i
], u
);
613 printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
614 pwopn
, usrtb_fail
, usrtb
);
617 printf("pass 2 (replacement userdb)\n");
618 printf("pwcache_userdb returned %d\n",
619 pwcache_userdb(setpassent
, test_getpwnam
, getpwuid
));
620 printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn
, usrtb_fail
, usrtb
);
622 for (i
= 1; i
< argc
; i
++) {
623 printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
624 i
, pwopn
, usrtb_fail
, usrtb
);
626 r
= uid_from_user(argv
[i
], &u
);
628 printf(" uid_from_user %s: failed\n", argv
[i
]);
630 printf(" uid_from_user %s: %d\n", argv
[i
], u
);
632 printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
633 pwopn
, usrtb_fail
, usrtb
);
636 printf("pass 3 (null pointers)\n");
637 printf("pwcache_userdb returned %d\n",
638 pwcache_userdb(NULL
, NULL
, NULL
));
642 #endif /* TEST_PWCACHE */
643 #endif /* !HAVE_PWCACHE_USERDB */