xref: /netbsd-src/lib/libc/gen/pwcache.c (revision bada23909e740596d0a3785a73bd3583a9807fb8)
1 /*	$NetBSD: pwcache.c,v 1.12 1999/01/19 08:32:34 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.12 1999/01/19 08:32:34 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 		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
265 
266 	if ((pw = getpwuid(uid)) == NULL) {
267 		/*
268 		 * no match for this uid in the local password file
269 		 * a string that is the uid in numberic format
270 		 */
271 		if (ptr == NULL)
272 			return (NULL);
273 		ptr->uid = uid;
274 #		ifdef NET2_STAT
275 		(void)snprintf(ptr->name, UNMLEN, "%u", uid);
276 #		else
277 		(void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
278 #		endif
279 		ptr->valid = INVALID;
280 		if (noname)
281 			return (NULL);
282 	} else {
283 		/*
284 		 * there is an entry for this uid in the password file
285 		 */
286 		if (ptr == NULL)
287 			return (pw->pw_name);
288 		ptr->uid = uid;
289 		(void)strncpy(ptr->name, pw->pw_name, UNMLEN);
290 		ptr->name[UNMLEN-1] = '\0';
291 		ptr->valid = VALID;
292 	}
293 	return (ptr->name);
294 }
295 
296 /*
297  * group_from_gid()
298  *	caches the name (if any) for the gid. If noname clear, we always return the
299  *	the stored name (if valid or invalid match). We use a simple hash table.
300  * Return
301  *	Pointer to stored name (or a empty string)
302  */
303 
304 #if __STDC__
305 const char *
306 group_from_gid(gid_t gid, int noname)
307 #else
308 const char *
309 group_from_gid(gid, noname)
310 	gid_t gid;
311 	int noname;
312 #endif
313 {
314 	struct group *gr;
315 	GIDC *ptr, **pptr;
316 
317 	if ((gidtb == NULL) && (gidtb_start() < 0))
318 		return (NULL);
319 
320 	/*
321 	 * see if we have this gid cached
322 	 */
323 	pptr = gidtb + (gid % GID_SZ);
324 	ptr = *pptr;
325 
326 	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
327 		/*
328 		 * have an entry for this gid
329 		 */
330 		if (!noname || (ptr->valid == VALID))
331 			return (ptr->name);
332 		return (NULL);
333 	}
334 
335 	/*
336 	 * No entry for this gid, we will add it
337 	 */
338 	if (!gropn) {
339 		setgroupent(1);
340 		++gropn;
341 	}
342 
343 	if (ptr == NULL)
344 		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
345 
346 	if ((gr = getgrgid(gid)) == NULL) {
347 		/*
348 		 * no match for this gid in the local group file, put in
349 		 * a string that is the gid in numberic format
350 		 */
351 		if (ptr == NULL)
352 			return (NULL);
353 		ptr->gid = gid;
354 #		ifdef NET2_STAT
355 		(void)snprintf(ptr->name, GNMLEN, "%u", gid);
356 #		else
357 		(void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
358 #		endif
359 		ptr->valid = INVALID;
360 		if (noname)
361 			return (NULL);
362 	} else {
363 		/*
364 		 * there is an entry for this group in the group file
365 		 */
366 		if (ptr == NULL)
367 			return (gr->gr_name);
368 		ptr->gid = gid;
369 		(void)strncpy(ptr->name, gr->gr_name, GNMLEN);
370 		ptr->name[GNMLEN-1] = '\0';
371 		ptr->valid = VALID;
372 	}
373 	return (ptr->name);
374 }
375 
376 /*
377  * uid_from_user()
378  *	caches the uid for a given user name. We use a simple hash table.
379  * Return
380  *	the uid (if any) for a user name, or a -1 if no match can be found
381  */
382 
383 #if __STDC__
384 int
385 uid_from_user(const char *name, uid_t *uid)
386 #else
387 int
388 uid_from_user(name, uid)
389 	const char *name;
390 	uid_t *uid;
391 #endif
392 {
393 	struct passwd *pw;
394 	UIDC *ptr, **pptr;
395 	size_t namelen;
396 
397 	/*
398 	 * return -1 for mangled names
399 	 */
400 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
401 		return (-1);
402 	if ((usrtb == NULL) && (usrtb_start() < 0))
403 		return (-1);
404 
405 	/*
406 	 * look up in hash table, if found and valid return the uid,
407 	 * if found and invalid, return a -1
408 	 */
409 	pptr = usrtb + st_hash(name, namelen, UNM_SZ);
410 	ptr = *pptr;
411 
412 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
413 		if (ptr->valid == INVALID)
414 			return (-1);
415 		*uid = ptr->uid;
416 		return (0);
417 	}
418 
419 	if (!pwopn) {
420 		setpassent(1);
421 		++pwopn;
422 	}
423 
424 	if (ptr == NULL)
425 		*pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
426 
427 	/*
428 	 * no match, look it up, if no match store it as an invalid entry,
429 	 * or store the matching uid
430 	 */
431 	if (ptr == NULL) {
432 		if ((pw = getpwnam(name)) == NULL)
433 			return (-1);
434 		*uid = pw->pw_uid;
435 		return (0);
436 	}
437 	(void)strncpy(ptr->name, name, UNMLEN);
438 	ptr->name[UNMLEN-1] = '\0';
439 	if ((pw = getpwnam(name)) == NULL) {
440 		ptr->valid = INVALID;
441 		return (-1);
442 	}
443 	ptr->valid = VALID;
444 	*uid = ptr->uid = pw->pw_uid;
445 	return (0);
446 }
447 
448 /*
449  * gid_from_group()
450  *	caches the gid for a given group name. We use a simple hash table.
451  * Return
452  *	the gid (if any) for a group name, or a -1 if no match can be found
453  */
454 
455 #if __STDC__
456 int
457 gid_from_group(const char *name, gid_t *gid)
458 #else
459 int
460 gid_from_group(name, gid)
461 	const char *name;
462 	gid_t *gid;
463 #endif
464 {
465 	struct group *gr;
466 	GIDC *ptr, **pptr;
467 	size_t namelen;
468 
469 	/*
470 	 * return -1 for mangled names
471 	 */
472 	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
473 		return (-1);
474 	if ((grptb == NULL) && (grptb_start() < 0))
475 		return (-1);
476 
477 	/*
478 	 * look up in hash table, if found and valid return the uid,
479 	 * if found and invalid, return a -1
480 	 */
481 	pptr = grptb + st_hash(name, namelen, GID_SZ);
482 	ptr = *pptr;
483 
484 	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
485 		if (ptr->valid == INVALID)
486 			return (-1);
487 		*gid = ptr->gid;
488 		return (0);
489 	}
490 
491 	if (!gropn) {
492 		setgroupent(1);
493 		++gropn;
494 	}
495 
496 	if (ptr == NULL)
497 		*pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
498 
499 	/*
500 	 * no match, look it up, if no match store it as an invalid entry,
501 	 * or store the matching gid
502 	 */
503 	if (ptr == NULL) {
504 		if ((gr = getgrnam(name)) == NULL)
505 			return (-1);
506 		*gid = gr->gr_gid;
507 		return (0);
508 	}
509 
510 	(void)strncpy(ptr->name, name, GNMLEN);
511 	ptr->name[GNMLEN-1] = '\0';
512 	if ((gr = getgrnam(name)) == NULL) {
513 		ptr->valid = INVALID;
514 		return (-1);
515 	}
516 	ptr->valid = VALID;
517 	*gid = ptr->gid = gr->gr_gid;
518 	return (0);
519 }
520