xref: /netbsd-src/lib/libc/gen/pwcache.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: pwcache.c,v 1.11 1998/07/28 18:13:53 mycroft 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 #ifndef 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.11 1998/07/28 18:13:53 mycroft Exp $");
46 #endif
47 #endif /* not lint */
48 
49 #include "namespace.h"
50 
51 #include <sys/types.h>
52 #include <sys/param.h>
53 
54 #include <grp.h>
55 #include <pwd.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 
61 #include "pwcache.h"
62 
63 #ifdef __weak_alias
64 __weak_alias(user_from_uid,_user_from_uid);
65 __weak_alias(group_from_gid,_group_from_gid);
66 #endif
67 
68 /*
69  * routines that control user, group, uid and gid caches (for the archive
70  * member print routine).
71  * IMPORTANT:
72  * these routines cache BOTH hits and misses, a major performance improvement
73  */
74 
75 static	int pwopn = 0;		/* is password file open */
76 static	int gropn = 0;		/* is group file open */
77 static UIDC **uidtb = NULL;	/* uid to name cache */
78 static GIDC **gidtb = NULL;	/* gid to name cache */
79 static UIDC **usrtb = NULL;	/* user name to uid cache */
80 static GIDC **grptb = NULL;	/* group name to gid cache */
81 
82 static u_int st_hash __P((const char *, size_t, int));
83 static int uidtb_start __P((void));
84 static int gidtb_start __P((void));
85 static int usrtb_start __P((void));
86 static int grptb_start __P((void));
87 
88 static u_int
89 st_hash(name, len, tabsz)
90 	const char *name;
91 	size_t len;
92 	int tabsz;
93 {
94 	u_int key = 0;
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 #if __STDC__
112 static int
113 uidtb_start(void)
114 #else
115 static int
116 uidtb_start()
117 #endif
118 {
119 	static int fail = 0;
120 
121 	if (uidtb != NULL)
122 		return (0);
123 	if (fail)
124 		return (-1);
125 	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
126 		++fail;
127 		return (-1);
128 	}
129 	return (0);
130 }
131 
132 /*
133  * gidtb_start
134  *	creates an an empty gidtb
135  * Return:
136  *	0 if ok, -1 otherwise
137  */
138 
139 #if __STDC__
140 int
141 gidtb_start(void)
142 #else
143 int
144 gidtb_start()
145 #endif
146 {
147 	static int fail = 0;
148 
149 	if (gidtb != NULL)
150 		return (0);
151 	if (fail)
152 		return (-1);
153 	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
154 		++fail;
155 		return (-1);
156 	}
157 	return (0);
158 }
159 
160 /*
161  * usrtb_start
162  *	creates an an empty usrtb
163  * Return:
164  *	0 if ok, -1 otherwise
165  */
166 
167 #if __STDC__
168 int
169 usrtb_start(void)
170 #else
171 int
172 usrtb_start()
173 #endif
174 {
175 	static int fail = 0;
176 
177 	if (usrtb != NULL)
178 		return (0);
179 	if (fail)
180 		return (-1);
181 	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
182 		++fail;
183 		return (-1);
184 	}
185 	return (0);
186 }
187 
188 /*
189  * grptb_start
190  *	creates an an empty grptb
191  * Return:
192  *	0 if ok, -1 otherwise
193  */
194 
195 #if __STDC__
196 int
197 grptb_start(void)
198 #else
199 int
200 grptb_start()
201 #endif
202 {
203 	static int fail = 0;
204 
205 	if (grptb != NULL)
206 		return (0);
207 	if (fail)
208 		return (-1);
209 	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
210 		++fail;
211 		return (-1);
212 	}
213 	return (0);
214 }
215 
216 /*
217  * user_from_uid()
218  *	caches the name (if any) for the uid. If noname clear, we always return the
219  *	the stored name (if valid or invalid match). We use a simple hash table.
220  * Return
221  *	Pointer to stored name (or a empty string)
222  */
223 
224 #if __STDC__
225 const char *
226 user_from_uid(uid_t uid, int noname)
227 #else
228 const char *
229 user_from_uid(uid, noname)
230 	uid_t uid;
231 	int noname;
232 #endif
233 {
234 	struct passwd *pw;
235 	UIDC *ptr, **pptr;
236 
237 	if ((uidtb == NULL) && (uidtb_start() < 0))
238 		return (NULL);
239 
240 	/*
241 	 * see if we have this uid cached
242 	 */
243 	pptr = uidtb + (uid % UID_SZ);
244 	ptr = *pptr;
245 
246 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
247 		/*
248 		 * have an entry for this uid
249 		 */
250 		if (!noname || (ptr->valid == VALID))
251 			return (ptr->name);
252 		return (NULL);
253 	}
254 
255 	/*
256 	 * No entry for this uid, we will add it
257 	 */
258 	if (!pwopn) {
259 		setpassent(1);
260 		++pwopn;
261 	}
262 
263 	if (ptr == NULL)
264 		ptr = (UIDC *)malloc(sizeof(UIDC));
265 	*pptr = ptr;
266 
267 	if ((pw = getpwuid(uid)) == NULL) {
268 		/*
269 		 * no match for this uid in the local password file
270 		 * a string that is the uid in numberic format
271 		 */
272 		if (ptr == NULL)
273 			return (NULL);
274 		ptr->uid = uid;
275 #		ifdef NET2_STAT
276 		(void)snprintf(ptr->name, UNMLEN, "%u", uid);
277 #		else
278 		(void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
279 #		endif
280 		ptr->valid = INVALID;
281 		if (noname)
282 			return (NULL);
283 	} else {
284 		/*
285 		 * there is an entry for this uid in the password file
286 		 */
287 		if (ptr == NULL)
288 			return (pw->pw_name);
289 		ptr->uid = uid;
290 		(void)strncpy(ptr->name, pw->pw_name, UNMLEN);
291 		ptr->name[UNMLEN-1] = '\0';
292 		ptr->valid = VALID;
293 	}
294 	return (ptr->name);
295 }
296 
297 /*
298  * group_from_gid()
299  *	caches the name (if any) for the gid. If noname clear, we always return the
300  *	the stored name (if valid or invalid match). We use a simple hash table.
301  * Return
302  *	Pointer to stored name (or a empty string)
303  */
304 
305 #if __STDC__
306 const char *
307 group_from_gid(gid_t gid, int noname)
308 #else
309 const char *
310 group_from_gid(gid, noname)
311 	gid_t gid;
312 	int noname;
313 #endif
314 {
315 	struct group *gr;
316 	GIDC *ptr, **pptr;
317 
318 	if ((gidtb == NULL) && (gidtb_start() < 0))
319 		return (NULL);
320 
321 	/*
322 	 * see if we have this gid cached
323 	 */
324 	pptr = gidtb + (gid % GID_SZ);
325 	ptr = *pptr;
326 
327 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
328 		/*
329 		 * have an entry for this gid
330 		 */
331 		if (!noname || (ptr->valid == VALID))
332 			return (ptr->name);
333 		return (NULL);
334 	}
335 
336 	/*
337 	 * No entry for this gid, we will add it
338 	 */
339 	if (!gropn) {
340 		setgroupent(1);
341 		++gropn;
342 	}
343 
344 	if (ptr == NULL)
345 		ptr = (GIDC *)malloc(sizeof(GIDC));
346 	*pptr = ptr;
347 
348 	if ((gr = getgrgid(gid)) == NULL) {
349 		/*
350 		 * no match for this gid in the local group file, put in
351 		 * a string that is the gid in numberic format
352 		 */
353 		if (ptr == NULL)
354 			return (NULL);
355 		ptr->gid = gid;
356 #		ifdef NET2_STAT
357 		(void)snprintf(ptr->name, GNMLEN, "%u", gid);
358 #		else
359 		(void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
360 #		endif
361 		ptr->valid = INVALID;
362 		if (noname)
363 			return (NULL);
364 	} else {
365 		/*
366 		 * there is an entry for this group in the group file
367 		 */
368 		if (ptr == NULL)
369 			return (gr->gr_name);
370 		ptr->gid = gid;
371 		(void)strncpy(ptr->name, gr->gr_name, GNMLEN);
372 		ptr->name[GNMLEN-1] = '\0';
373 		ptr->valid = VALID;
374 	}
375 	return (ptr->name);
376 }
377 
378 /*
379  * uid_from_user()
380  *	caches the uid for a given user name. We use a simple hash table.
381  * Return
382  *	the uid (if any) for a user name, or a -1 if no match can be found
383  */
384 
385 #if __STDC__
386 int
387 uid_from_user(const char *name, uid_t *uid)
388 #else
389 int
390 uid_from_user(name, uid)
391 	const char *name;
392 	uid_t *uid;
393 #endif
394 {
395 	struct passwd *pw;
396 	UIDC *ptr, **pptr;
397 	size_t namelen;
398 
399 	/*
400 	 * return -1 for mangled names
401 	 */
402 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
403 		return (-1);
404 	if ((usrtb == NULL) && (usrtb_start() < 0))
405 		return (-1);
406 
407 	/*
408 	 * look up in hash table, if found and valid return the uid,
409 	 * if found and invalid, return a -1
410 	 */
411 	pptr = usrtb + st_hash(name, namelen, UNM_SZ);
412 	ptr = *pptr;
413 
414 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
415 		if (ptr->valid == INVALID)
416 			return (-1);
417 		*uid = ptr->uid;
418 		return (0);
419 	}
420 
421 	if (!pwopn) {
422 		setpassent(1);
423 		++pwopn;
424 	}
425 
426 	if (ptr == NULL)
427 		ptr = (UIDC *)malloc(sizeof(UIDC));
428 	*pptr = ptr;
429 
430 	/*
431 	 * no match, look it up, if no match store it as an invalid entry,
432 	 * or store the matching uid
433 	 */
434 	if (ptr == NULL) {
435 		if ((pw = getpwnam(name)) == NULL)
436 			return (-1);
437 		*uid = pw->pw_uid;
438 		return (0);
439 	}
440 	(void)strncpy(ptr->name, name, UNMLEN);
441 	ptr->name[UNMLEN-1] = '\0';
442 	if ((pw = getpwnam(name)) == NULL) {
443 		ptr->valid = INVALID;
444 		return (-1);
445 	}
446 	ptr->valid = VALID;
447 	*uid = ptr->uid = pw->pw_uid;
448 	return (0);
449 }
450 
451 /*
452  * gid_from_group()
453  *	caches the gid for a given group name. We use a simple hash table.
454  * Return
455  *	the gid (if any) for a group name, or a -1 if no match can be found
456  */
457 
458 #if __STDC__
459 int
460 gid_from_group(const char *name, gid_t *gid)
461 #else
462 int
463 gid_from_group(name, gid)
464 	const char *name;
465 	gid_t *gid;
466 #endif
467 {
468 	struct group *gr;
469 	GIDC *ptr, **pptr;
470 	size_t namelen;
471 
472 	/*
473 	 * return -1 for mangled names
474 	 */
475 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
476 		return (-1);
477 	if ((grptb == NULL) && (grptb_start() < 0))
478 		return (-1);
479 
480 	/*
481 	 * look up in hash table, if found and valid return the uid,
482 	 * if found and invalid, return a -1
483 	 */
484 	pptr = grptb + st_hash(name, namelen, GID_SZ);
485 	ptr = *pptr;
486 
487 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
488 		if (ptr->valid == INVALID)
489 			return (-1);
490 		*gid = ptr->gid;
491 		return (0);
492 	}
493 
494 	if (!gropn) {
495 		setgroupent(1);
496 		++gropn;
497 	}
498 
499 	if (ptr == NULL)
500 		ptr = (GIDC *)malloc(sizeof(GIDC));
501 	*pptr = ptr;
502 
503 	/*
504 	 * no match, look it up, if no match store it as an invalid entry,
505 	 * or store the matching gid
506 	 */
507 	if (ptr == NULL) {
508 		if ((gr = getgrnam(name)) == NULL)
509 			return (-1);
510 		*gid = gr->gr_gid;
511 		return (0);
512 	}
513 
514 	(void)strncpy(ptr->name, name, GNMLEN);
515 	ptr->name[GNMLEN-1] = '\0';
516 	if ((gr = getgrnam(name)) == NULL) {
517 		ptr->valid = INVALID;
518 		return (-1);
519 	}
520 	ptr->valid = VALID;
521 	*gid = ptr->gid = gr->gr_gid;
522 	return (0);
523 }
524