pwcache.c revision 1.16 1 /* $NetBSD: pwcache.c,v 1.16 2002/01/04 14:50:29 lukem Exp $ */
2
3 /*-
4 * Copyright (c) 1992 Keith Muller.
5 * Copyright (c) 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Keith Muller of the University of California, San Diego.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
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. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 */
39
40 #include <sys/cdefs.h>
41 #if defined(LIBC_SCCS) && !defined(lint)
42 #if 0
43 static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93";
44 #else
45 __RCSID("$NetBSD: pwcache.c,v 1.16 2002/01/04 14:50:29 lukem Exp $");
46 #endif
47 #endif /* LIBC_SCCS and not lint */
48
49 #include "namespace.h"
50
51 #include <sys/types.h>
52 #include <sys/param.h>
53
54 #include <assert.h>
55 #include <grp.h>
56 #include <pwd.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 #include "pwcache.h"
63
64 #ifdef __weak_alias
65 __weak_alias(user_from_uid,_user_from_uid)
66 __weak_alias(group_from_gid,_group_from_gid)
67 #endif
68
69 /*
70 * routines that control user, group, uid and gid caches (for the archive
71 * member print routine).
72 * IMPORTANT:
73 * these routines cache BOTH hits and misses, a major performance improvement
74 */
75
76 static int pwopn = 0; /* is password file open */
77 static int gropn = 0; /* is group file open */
78 static UIDC **uidtb = NULL; /* uid to name cache */
79 static GIDC **gidtb = NULL; /* gid to name cache */
80 static UIDC **usrtb = NULL; /* user name to uid cache */
81 static GIDC **grptb = NULL; /* group name to gid cache */
82
83 static u_int st_hash(const char *, size_t, int);
84 static int uidtb_start(void);
85 static int gidtb_start(void);
86 static int usrtb_start(void);
87 static int grptb_start(void);
88
89 static u_int
90 st_hash(const char *name, size_t len, int tabsz)
91 {
92 u_int key = 0;
93
94 _DIAGASSERT(name != NULL);
95
96 while (len--) {
97 key += *name++;
98 key = (key << 8) | (key >> 24);
99 }
100
101 return (key % tabsz);
102 }
103
104 /*
105 * uidtb_start
106 * creates an an empty uidtb
107 * Return:
108 * 0 if ok, -1 otherwise
109 */
110
111 static int
112 uidtb_start(void)
113 {
114 static int fail = 0;
115
116 if (uidtb != NULL)
117 return (0);
118 if (fail)
119 return (-1);
120 if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
121 ++fail;
122 return (-1);
123 }
124 return (0);
125 }
126
127 /*
128 * gidtb_start
129 * creates an an empty gidtb
130 * Return:
131 * 0 if ok, -1 otherwise
132 */
133
134 int
135 gidtb_start(void)
136 {
137 static int fail = 0;
138
139 if (gidtb != NULL)
140 return (0);
141 if (fail)
142 return (-1);
143 if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
144 ++fail;
145 return (-1);
146 }
147 return (0);
148 }
149
150 /*
151 * usrtb_start
152 * creates an an empty usrtb
153 * Return:
154 * 0 if ok, -1 otherwise
155 */
156
157 int
158 usrtb_start(void)
159 {
160 static int fail = 0;
161
162 if (usrtb != NULL)
163 return (0);
164 if (fail)
165 return (-1);
166 if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
167 ++fail;
168 return (-1);
169 }
170 return (0);
171 }
172
173 /*
174 * grptb_start
175 * creates an an empty grptb
176 * Return:
177 * 0 if ok, -1 otherwise
178 */
179
180 int
181 grptb_start(void)
182 {
183 static int fail = 0;
184
185 if (grptb != NULL)
186 return (0);
187 if (fail)
188 return (-1);
189 if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
190 ++fail;
191 return (-1);
192 }
193 return (0);
194 }
195
196 /*
197 * user_from_uid()
198 * caches the name (if any) for the uid. If noname clear, we always
199 * return the the stored name (if valid or invalid match).
200 * We use a simple hash table.
201 * Return
202 * Pointer to stored name (or a empty string)
203 */
204
205 const char *
206 user_from_uid(uid_t uid, int noname)
207 {
208 struct passwd *pw;
209 UIDC *ptr, **pptr;
210
211 if ((uidtb == NULL) && (uidtb_start() < 0))
212 return (NULL);
213
214 /*
215 * see if we have this uid cached
216 */
217 pptr = uidtb + (uid % UID_SZ);
218 ptr = *pptr;
219
220 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
221 /*
222 * have an entry for this uid
223 */
224 if (!noname || (ptr->valid == VALID))
225 return (ptr->name);
226 return (NULL);
227 }
228
229 /*
230 * No entry for this uid, we will add it
231 */
232 if (!pwopn) {
233 setpassent(1);
234 ++pwopn;
235 }
236
237 if (ptr == NULL)
238 *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
239
240 if ((pw = getpwuid(uid)) == NULL) {
241 /*
242 * no match for this uid in the local password file
243 * a string that is the uid in numberic format
244 */
245 if (ptr == NULL)
246 return (NULL);
247 ptr->uid = uid;
248 (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
249 ptr->valid = INVALID;
250 if (noname)
251 return (NULL);
252 } else {
253 /*
254 * there is an entry for this uid in the password file
255 */
256 if (ptr == NULL)
257 return (pw->pw_name);
258 ptr->uid = uid;
259 (void)strncpy(ptr->name, pw->pw_name, UNMLEN);
260 ptr->name[UNMLEN-1] = '\0';
261 ptr->valid = VALID;
262 }
263 return (ptr->name);
264 }
265
266 /*
267 * group_from_gid()
268 * caches the name (if any) for the gid. If noname clear, we always
269 * return the the stored name (if valid or invalid match).
270 * We use a simple hash table.
271 * Return
272 * Pointer to stored name (or a empty string)
273 */
274
275 const char *
276 group_from_gid(gid_t gid, int noname)
277 {
278 struct group *gr;
279 GIDC *ptr, **pptr;
280
281 if ((gidtb == NULL) && (gidtb_start() < 0))
282 return (NULL);
283
284 /*
285 * see if we have this gid cached
286 */
287 pptr = gidtb + (gid % GID_SZ);
288 ptr = *pptr;
289
290 if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
291 /*
292 * have an entry for this gid
293 */
294 if (!noname || (ptr->valid == VALID))
295 return (ptr->name);
296 return (NULL);
297 }
298
299 /*
300 * No entry for this gid, we will add it
301 */
302 if (!gropn) {
303 setgroupent(1);
304 ++gropn;
305 }
306
307 if (ptr == NULL)
308 *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
309
310 if ((gr = getgrgid(gid)) == NULL) {
311 /*
312 * no match for this gid in the local group file, put in
313 * a string that is the gid in numberic format
314 */
315 if (ptr == NULL)
316 return (NULL);
317 ptr->gid = gid;
318 (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
319 ptr->valid = INVALID;
320 if (noname)
321 return (NULL);
322 } else {
323 /*
324 * there is an entry for this group in the group file
325 */
326 if (ptr == NULL)
327 return (gr->gr_name);
328 ptr->gid = gid;
329 (void)strncpy(ptr->name, gr->gr_name, GNMLEN);
330 ptr->name[GNMLEN-1] = '\0';
331 ptr->valid = VALID;
332 }
333 return (ptr->name);
334 }
335
336 /*
337 * uid_from_user()
338 * caches the uid for a given user name. We use a simple hash table.
339 * Return
340 * the uid (if any) for a user name, or a -1 if no match can be found
341 */
342
343 int
344 uid_from_user(const char *name, uid_t *uid)
345 {
346 struct passwd *pw;
347 UIDC *ptr, **pptr;
348 size_t namelen;
349
350 /*
351 * return -1 for mangled names
352 */
353 if (name == NULL || ((namelen = strlen(name)) == 0))
354 return (-1);
355 if ((usrtb == NULL) && (usrtb_start() < 0))
356 return (-1);
357
358 /*
359 * look up in hash table, if found and valid return the uid,
360 * if found and invalid, return a -1
361 */
362 pptr = usrtb + st_hash(name, namelen, UNM_SZ);
363 ptr = *pptr;
364
365 if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
366 if (ptr->valid == INVALID)
367 return (-1);
368 *uid = ptr->uid;
369 return (0);
370 }
371
372 if (!pwopn) {
373 setpassent(1);
374 ++pwopn;
375 }
376
377 if (ptr == NULL)
378 *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
379
380 /*
381 * no match, look it up, if no match store it as an invalid entry,
382 * or store the matching uid
383 */
384 if (ptr == NULL) {
385 if ((pw = getpwnam(name)) == NULL)
386 return (-1);
387 *uid = pw->pw_uid;
388 return (0);
389 }
390 (void)strncpy(ptr->name, name, UNMLEN);
391 ptr->name[UNMLEN-1] = '\0';
392 if ((pw = getpwnam(name)) == NULL) {
393 ptr->valid = INVALID;
394 return (-1);
395 }
396 ptr->valid = VALID;
397 *uid = ptr->uid = pw->pw_uid;
398 return (0);
399 }
400
401 /*
402 * gid_from_group()
403 * caches the gid for a given group name. We use a simple hash table.
404 * Return
405 * the gid (if any) for a group name, or a -1 if no match can be found
406 */
407
408 int
409 gid_from_group(const char *name, gid_t *gid)
410 {
411 struct group *gr;
412 GIDC *ptr, **pptr;
413 size_t namelen;
414
415 /*
416 * return -1 for mangled names
417 */
418 if (name == NULL || ((namelen = strlen(name)) == 0))
419 return (-1);
420 if ((grptb == NULL) && (grptb_start() < 0))
421 return (-1);
422
423 /*
424 * look up in hash table, if found and valid return the uid,
425 * if found and invalid, return a -1
426 */
427 pptr = grptb + st_hash(name, namelen, GID_SZ);
428 ptr = *pptr;
429
430 if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
431 if (ptr->valid == INVALID)
432 return (-1);
433 *gid = ptr->gid;
434 return (0);
435 }
436
437 if (!gropn) {
438 setgroupent(1);
439 ++gropn;
440 }
441
442 if (ptr == NULL)
443 *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
444
445 /*
446 * no match, look it up, if no match store it as an invalid entry,
447 * or store the matching gid
448 */
449 if (ptr == NULL) {
450 if ((gr = getgrnam(name)) == NULL)
451 return (-1);
452 *gid = gr->gr_gid;
453 return (0);
454 }
455
456 (void)strncpy(ptr->name, name, GNMLEN);
457 ptr->name[GNMLEN-1] = '\0';
458 if ((gr = getgrnam(name)) == NULL) {
459 ptr->valid = INVALID;
460 return (-1);
461 }
462 ptr->valid = VALID;
463 *gid = ptr->gid = gr->gr_gid;
464 return (0);
465 }
466