1 /*	$OpenBSD: pwcache.c,v 1.15 2018/09/22 02:47:23 millert 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. 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.
22  *
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
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/types.h>
37 
38 #include <assert.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 /*
47  * Constants and data structures used to implement group and password file
48  * caches.  Name lengths have been chosen to be as large as those supported
49  * by the passwd and group files as well as the standard archive formats.
50  * CACHE SIZES MUST BE PRIME
51  */
52 #define UNMLEN		32	/* >= user name found in any protocol */
53 #define GNMLEN		32	/* >= group name found in any protocol */
54 #define UID_SZ		317	/* size of uid to user_name cache */
55 #define UNM_SZ		317	/* size of user_name to uid cache */
56 #define GID_SZ		251	/* size of gid to group_name cache */
57 #define GNM_SZ		251	/* size of group_name to gid cache */
58 #define VALID		1	/* entry and name are valid */
59 #define INVALID		2	/* entry valid, name NOT valid */
60 
61 /*
62  * Node structures used in the user, group, uid, and gid caches.
63  */
64 
65 typedef struct uidc {
66 	int valid;		/* is this a valid or a miss entry */
67 	char name[UNMLEN];	/* uid name */
68 	uid_t uid;		/* cached uid */
69 } UIDC;
70 
71 typedef struct gidc {
72 	int valid;		/* is this a valid or a miss entry */
73 	char name[GNMLEN];	/* gid name */
74 	gid_t gid;		/* cached gid */
75 } GIDC;
76 
77 /*
78  * Routines that control user, group, uid and gid caches.
79  * Traditional passwd/group cache routines perform quite poorly with
80  * archives. The chances of hitting a valid lookup with an archive is quite a
81  * bit worse than with files already resident on the file system. These misses
82  * create a MAJOR performance cost. To adress this problem, these routines
83  * cache both hits and misses.
84  */
85 
86 static UIDC **uidtb;	/* uid to name cache */
87 static GIDC **gidtb;	/* gid to name cache */
88 static UIDC **usrtb;	/* user name to uid cache */
89 static GIDC **grptb;	/* group name to gid cache */
90 
91 static u_int
st_hash(const char * name,size_t len,int tabsz)92 st_hash(const char *name, size_t len, int tabsz)
93 {
94 	u_int key = 0;
95 
96 	assert(name != NULL);
97 
98 	while (len--) {
99 		key += *name++;
100 		key = (key << 8) | (key >> 24);
101 	}
102 
103 	return key % tabsz;
104 }
105 
106 /*
107  * uidtb_start
108  *	creates an an empty uidtb
109  * Return:
110  *	0 if ok, -1 otherwise
111  */
112 static int
uidtb_start(void)113 uidtb_start(void)
114 {
115 	static int fail = 0;
116 
117 	if (uidtb != NULL)
118 		return 0;
119 	if (fail)
120 		return -1;
121 	if ((uidtb = calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
122 		++fail;
123 		return -1;
124 	}
125 	return 0;
126 }
127 
128 /*
129  * gidtb_start
130  *	creates an an empty gidtb
131  * Return:
132  *	0 if ok, -1 otherwise
133  */
134 static int
gidtb_start(void)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 = 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 static int
usrtb_start(void)157 usrtb_start(void)
158 {
159 	static int fail = 0;
160 
161 	if (usrtb != NULL)
162 		return 0;
163 	if (fail)
164 		return -1;
165 	if ((usrtb = calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
166 		++fail;
167 		return -1;
168 	}
169 	return 0;
170 }
171 
172 /*
173  * grptb_start
174  *	creates an an empty grptb
175  * Return:
176  *	0 if ok, -1 otherwise
177  */
178 static int
grptb_start(void)179 grptb_start(void)
180 {
181 	static int fail = 0;
182 
183 	if (grptb != NULL)
184 		return 0;
185 	if (fail)
186 		return -1;
187 	if ((grptb = calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
188 		++fail;
189 		return -1;
190 	}
191 	return 0;
192 }
193 
194 /*
195  * user_from_uid()
196  *	caches the name (if any) for the uid. If noname clear, we always
197  *	return the stored name (if valid or invalid match).
198  *	We use a simple hash table.
199  * Return:
200  *	Pointer to stored name (or a empty string)
201  */
202 const char *
user_from_uid(uid_t uid,int noname)203 user_from_uid(uid_t uid, int noname)
204 {
205 	struct passwd pwstore, *pw = NULL;
206 	char pwbuf[_PW_BUF_LEN];
207 	UIDC **pptr, *ptr = NULL;
208 
209 	if ((uidtb != NULL) || (uidtb_start() == 0)) {
210 		/*
211 		 * see if we have this uid cached
212 		 */
213 		pptr = uidtb + (uid % UID_SZ);
214 		ptr = *pptr;
215 
216 		if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
217 			/*
218 			 * have an entry for this uid
219 			 */
220 			if (!noname || (ptr->valid == VALID))
221 				return ptr->name;
222 			return NULL;
223 		}
224 
225 		if (ptr == NULL)
226 			*pptr = ptr = malloc(sizeof(UIDC));
227 	}
228 
229 	getpwuid_r(uid, &pwstore, pwbuf, sizeof(pwbuf), &pw);
230 	if (pw == NULL) {
231 		/*
232 		 * no match for this uid in the local password file
233 		 * a string that is the uid in numeric format
234 		 */
235 		if (ptr == NULL)
236 			return NULL;
237 		ptr->uid = uid;
238 		(void)snprintf(ptr->name, UNMLEN, "%u", uid);
239 		ptr->valid = INVALID;
240 		if (noname)
241 			return NULL;
242 	} else {
243 		/*
244 		 * there is an entry for this uid in the password file
245 		 */
246 		if (ptr == NULL)
247 			return pw->pw_name;
248 		ptr->uid = uid;
249 		(void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name));
250 		ptr->valid = VALID;
251 	}
252 	return ptr->name;
253 }
254 
255 /*
256  * group_from_gid()
257  *	caches the name (if any) for the gid. If noname clear, we always
258  *	return the stored name (if valid or invalid match).
259  *	We use a simple hash table.
260  * Return:
261  *	Pointer to stored name (or a empty string)
262  */
263 const char *
group_from_gid(gid_t gid,int noname)264 group_from_gid(gid_t gid, int noname)
265 {
266 	struct group grstore, *gr = NULL;
267 	char grbuf[_GR_BUF_LEN];
268 	GIDC **pptr, *ptr = NULL;
269 
270 	if ((gidtb != NULL) || (gidtb_start() == 0)) {
271 		/*
272 		 * see if we have this gid cached
273 		 */
274 		pptr = gidtb + (gid % GID_SZ);
275 		ptr = *pptr;
276 
277 		if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
278 			/*
279 			 * have an entry for this gid
280 			 */
281 			if (!noname || (ptr->valid == VALID))
282 				return ptr->name;
283 			return NULL;
284 		}
285 
286 		if (ptr == NULL)
287 			*pptr = ptr = malloc(sizeof(GIDC));
288 	}
289 
290 	getgrgid_r(gid, &grstore, grbuf, sizeof(grbuf), &gr);
291 	if (gr == NULL) {
292 		/*
293 		 * no match for this gid in the local group file, put in
294 		 * a string that is the gid in numeric format
295 		 */
296 		if (ptr == NULL)
297 			return NULL;
298 		ptr->gid = gid;
299 		(void)snprintf(ptr->name, GNMLEN, "%u", gid);
300 		ptr->valid = INVALID;
301 		if (noname)
302 			return NULL;
303 	} else {
304 		/*
305 		 * there is an entry for this group in the group file
306 		 */
307 		if (ptr == NULL)
308 			return gr->gr_name;
309 		ptr->gid = gid;
310 		(void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name));
311 		ptr->valid = VALID;
312 	}
313 	return ptr->name;
314 }
315 
316 /*
317  * uid_from_user()
318  *	caches the uid for a given user name. We use a simple hash table.
319  * Return:
320  *	0 if the user name is found (filling in uid), -1 otherwise
321  */
322 int
uid_from_user(const char * name,uid_t * uid)323 uid_from_user(const char *name, uid_t *uid)
324 {
325 	struct passwd pwstore, *pw = NULL;
326 	char pwbuf[_PW_BUF_LEN];
327 	UIDC **pptr, *ptr = NULL;
328 	size_t namelen;
329 
330 	/*
331 	 * return -1 for mangled names
332 	 */
333 	if (name == NULL || ((namelen = strlen(name)) == 0))
334 		return -1;
335 
336 	if ((usrtb != NULL) || (usrtb_start() == 0)) {
337 		/*
338 		 * look up in hash table, if found and valid return the uid,
339 		 * if found and invalid, return a -1
340 		 */
341 		pptr = usrtb + st_hash(name, namelen, UNM_SZ);
342 		ptr = *pptr;
343 
344 		if ((ptr != NULL) && (ptr->valid > 0) &&
345 		    strcmp(name, ptr->name) == 0) {
346 			if (ptr->valid == INVALID)
347 				return -1;
348 			*uid = ptr->uid;
349 			return 0;
350 		}
351 
352 		if (ptr == NULL)
353 			*pptr = ptr = malloc(sizeof(UIDC));
354 	}
355 
356 	/*
357 	 * no match, look it up, if no match store it as an invalid entry,
358 	 * or store the matching uid
359 	 */
360 	getpwnam_r(name, &pwstore, pwbuf, sizeof(pwbuf), &pw);
361 	if (ptr == NULL) {
362 		if (pw == NULL)
363 			return -1;
364 		*uid = pw->pw_uid;
365 		return 0;
366 	}
367 	(void)strlcpy(ptr->name, name, sizeof(ptr->name));
368 	if (pw == NULL) {
369 		ptr->valid = INVALID;
370 		return -1;
371 	}
372 	ptr->valid = VALID;
373 	*uid = ptr->uid = pw->pw_uid;
374 	return 0;
375 }
376 
377 /*
378  * gid_from_group()
379  *	caches the gid for a given group name. We use a simple hash table.
380  * Return:
381  *	0 if the group name is found (filling in gid), -1 otherwise
382  */
383 int
gid_from_group(const char * name,gid_t * gid)384 gid_from_group(const char *name, gid_t *gid)
385 {
386 	struct group grstore, *gr = NULL;
387 	char grbuf[_GR_BUF_LEN];
388 	GIDC **pptr, *ptr = NULL;
389 	size_t namelen;
390 
391 	/*
392 	 * return -1 for mangled names
393 	 */
394 	if (name == NULL || ((namelen = strlen(name)) == 0))
395 		return -1;
396 
397 	if ((grptb != NULL) || (grptb_start() == 0)) {
398 		/*
399 		 * look up in hash table, if found and valid return the uid,
400 		 * if found and invalid, return a -1
401 		 */
402 		pptr = grptb + st_hash(name, namelen, GID_SZ);
403 		ptr = *pptr;
404 
405 		if ((ptr != NULL) && (ptr->valid > 0) &&
406 		    strcmp(name, ptr->name) == 0) {
407 			if (ptr->valid == INVALID)
408 				return -1;
409 			*gid = ptr->gid;
410 			return 0;
411 		}
412 
413 		if (ptr == NULL)
414 			*pptr = ptr = malloc(sizeof(GIDC));
415 	}
416 
417 	/*
418 	 * no match, look it up, if no match store it as an invalid entry,
419 	 * or store the matching gid
420 	 */
421 	getgrnam_r(name, &grstore, grbuf, sizeof(grbuf), &gr);
422 	if (ptr == NULL) {
423 		if (gr == NULL)
424 			return -1;
425 		*gid = gr->gr_gid;
426 		return 0;
427 	}
428 
429 	(void)strlcpy(ptr->name, name, sizeof(ptr->name));
430 	if (gr == NULL) {
431 		ptr->valid = INVALID;
432 		return -1;
433 	}
434 	ptr->valid = VALID;
435 	*gid = ptr->gid = gr->gr_gid;
436 	return 0;
437 }
438