1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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#include <sys/stat.h>
38#include <string.h>
39#include <stdio.h>
40#include <pwd.h>
41#include <grp.h>
42#include <stdlib.h>
43#include "pax.h"
44#include "cache.h"
45#include "extern.h"
46
47/*
48 * routines that control user, group, uid and gid caches (for the archive
49 * member print routine).
50 * IMPORTANT:
51 * these routines cache BOTH hits and misses, a major performance improvement
52 */
53
54static	int pwopn = 0;		/* is password file open */
55static	int gropn = 0;		/* is group file open */
56static UIDC **uidtb = NULL;	/* uid to name cache */
57static GIDC **gidtb = NULL;	/* gid to name cache */
58static UIDC **usrtb = NULL;	/* user name to uid cache */
59static GIDC **grptb = NULL;	/* group name to gid cache */
60
61/*
62 * uidtb_start
63 *	creates an empty uidtb
64 * Return:
65 *	0 if ok, -1 otherwise
66 */
67
68int
69uidtb_start(void)
70{
71	static int fail = 0;
72
73	if (uidtb != NULL)
74		return(0);
75	if (fail)
76		return(-1);
77	if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
78		++fail;
79		paxwarn(1, "Unable to allocate memory for user id cache table");
80		return(-1);
81	}
82	return(0);
83}
84
85/*
86 * gidtb_start
87 *	creates an empty gidtb
88 * Return:
89 *	0 if ok, -1 otherwise
90 */
91
92int
93gidtb_start(void)
94{
95	static int fail = 0;
96
97	if (gidtb != NULL)
98		return(0);
99	if (fail)
100		return(-1);
101	if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
102		++fail;
103		paxwarn(1, "Unable to allocate memory for group id cache table");
104		return(-1);
105	}
106	return(0);
107}
108
109/*
110 * usrtb_start
111 *	creates an empty usrtb
112 * Return:
113 *	0 if ok, -1 otherwise
114 */
115
116int
117usrtb_start(void)
118{
119	static int fail = 0;
120
121	if (usrtb != NULL)
122		return(0);
123	if (fail)
124		return(-1);
125	if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
126		++fail;
127		paxwarn(1, "Unable to allocate memory for user name cache table");
128		return(-1);
129	}
130	return(0);
131}
132
133/*
134 * grptb_start
135 *	creates an empty grptb
136 * Return:
137 *	0 if ok, -1 otherwise
138 */
139
140int
141grptb_start(void)
142{
143	static int fail = 0;
144
145	if (grptb != NULL)
146		return(0);
147	if (fail)
148		return(-1);
149	if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
150		++fail;
151		paxwarn(1,"Unable to allocate memory for group name cache table");
152		return(-1);
153	}
154	return(0);
155}
156
157/*
158 * name_uid()
159 *	caches the name (if any) for the uid. If frc set, we always return the
160 *	the stored name (if valid or invalid match). We use a simple hash table.
161 * Return
162 *	Pointer to stored name (or an empty string).
163 */
164
165const char *
166name_uid(uid_t uid, int frc)
167{
168	struct passwd *pw;
169	UIDC *ptr;
170
171	if ((uidtb == NULL) && (uidtb_start() < 0))
172		return("");
173
174	/*
175	 * see if we have this uid cached
176	 */
177	ptr = uidtb[uid % UID_SZ];
178	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
179		/*
180		 * have an entry for this uid
181		 */
182		if (frc || (ptr->valid == VALID))
183			return(ptr->name);
184		return("");
185	}
186
187	/*
188	 * No entry for this uid, we will add it
189	 */
190	if (!pwopn) {
191		setpassent(1);
192		++pwopn;
193	}
194	if (ptr == NULL)
195		ptr = uidtb[uid % UID_SZ] = (UIDC *)malloc(sizeof(UIDC));
196
197	if ((pw = getpwuid(uid)) == NULL) {
198		/*
199		 * no match for this uid in the local password file
200		 * a string that is the uid in numeric format
201		 */
202		if (ptr == NULL)
203			return("");
204		ptr->uid = uid;
205		ptr->valid = INVALID;
206		(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
207			       (unsigned long)uid);
208		if (frc == 0)
209			return("");
210	} else {
211		/*
212		 * there is an entry for this uid in the password file
213		 */
214		if (ptr == NULL)
215			return(pw->pw_name);
216		ptr->uid = uid;
217		(void)strncpy(ptr->name, pw->pw_name, UNMLEN - 1);
218		ptr->name[UNMLEN-1] = '\0';
219		ptr->valid = VALID;
220	}
221	return(ptr->name);
222}
223
224/*
225 * name_gid()
226 *	caches the name (if any) for the gid. If frc set, we always return the
227 *	the stored name (if valid or invalid match). We use a simple hash table.
228 * Return
229 *	Pointer to stored name (or an empty string).
230 */
231
232const char *
233name_gid(gid_t gid, int frc)
234{
235	struct group *gr;
236	GIDC *ptr;
237
238	if ((gidtb == NULL) && (gidtb_start() < 0))
239		return("");
240
241	/*
242	 * see if we have this gid cached
243	 */
244	ptr = gidtb[gid % GID_SZ];
245	if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
246		/*
247		 * have an entry for this gid
248		 */
249		if (frc || (ptr->valid == VALID))
250			return(ptr->name);
251		return("");
252	}
253
254	/*
255	 * No entry for this gid, we will add it
256	 */
257	if (!gropn) {
258		setgroupent(1);
259		++gropn;
260	}
261	if (ptr == NULL)
262		ptr = gidtb[gid % GID_SZ] = (GIDC *)malloc(sizeof(GIDC));
263
264	if ((gr = getgrgid(gid)) == NULL) {
265		/*
266		 * no match for this gid in the local group file, put in
267		 * a string that is the gid in numeric format
268		 */
269		if (ptr == NULL)
270			return("");
271		ptr->gid = gid;
272		ptr->valid = INVALID;
273		(void)snprintf(ptr->name, sizeof(ptr->name), "%lu",
274			       (unsigned long)gid);
275		if (frc == 0)
276			return("");
277	} else {
278		/*
279		 * there is an entry for this group in the group file
280		 */
281		if (ptr == NULL)
282			return(gr->gr_name);
283		ptr->gid = gid;
284		(void)strncpy(ptr->name, gr->gr_name, GNMLEN - 1);
285		ptr->name[GNMLEN-1] = '\0';
286		ptr->valid = VALID;
287	}
288	return(ptr->name);
289}
290
291/*
292 * uid_name()
293 *	caches the uid for a given user name. We use a simple hash table.
294 * Return
295 *	the uid (if any) for a user name, or a -1 if no match can be found
296 */
297
298int
299uid_name(char *name, uid_t *uid)
300{
301	struct passwd *pw;
302	UIDC *ptr;
303	int namelen;
304
305	/*
306	 * return -1 for mangled names
307	 */
308	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
309		return(-1);
310	if ((usrtb == NULL) && (usrtb_start() < 0))
311		return(-1);
312
313	/*
314	 * look up in hash table, if found and valid return the uid,
315	 * if found and invalid, return a -1
316	 */
317	ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
318	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
319		if (ptr->valid == INVALID)
320			return(-1);
321		*uid = ptr->uid;
322		return(0);
323	}
324
325	if (!pwopn) {
326		setpassent(1);
327		++pwopn;
328	}
329
330	if (ptr == NULL)
331		ptr = usrtb[st_hash(name, namelen, UNM_SZ)] =
332		  (UIDC *)malloc(sizeof(UIDC));
333
334	/*
335	 * no match, look it up, if no match store it as an invalid entry,
336	 * or store the matching uid
337	 */
338	if (ptr == NULL) {
339		if ((pw = getpwnam(name)) == NULL)
340			return(-1);
341		*uid = pw->pw_uid;
342		return(0);
343	}
344	(void)strncpy(ptr->name, name, UNMLEN - 1);
345	ptr->name[UNMLEN-1] = '\0';
346	if ((pw = getpwnam(name)) == NULL) {
347		ptr->valid = INVALID;
348		return(-1);
349	}
350	ptr->valid = VALID;
351	*uid = ptr->uid = pw->pw_uid;
352	return(0);
353}
354
355/*
356 * gid_name()
357 *	caches the gid for a given group name. We use a simple hash table.
358 * Return
359 *	the gid (if any) for a group name, or a -1 if no match can be found
360 */
361
362int
363gid_name(char *name, gid_t *gid)
364{
365	struct group *gr;
366	GIDC *ptr;
367	int namelen;
368
369	/*
370	 * return -1 for mangled names
371	 */
372	if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
373		return(-1);
374	if ((grptb == NULL) && (grptb_start() < 0))
375		return(-1);
376
377	/*
378	 * look up in hash table, if found and valid return the uid,
379	 * if found and invalid, return a -1
380	 */
381	ptr = grptb[st_hash(name, namelen, GID_SZ)];
382	if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
383		if (ptr->valid == INVALID)
384			return(-1);
385		*gid = ptr->gid;
386		return(0);
387	}
388
389	if (!gropn) {
390		setgroupent(1);
391		++gropn;
392	}
393	if (ptr == NULL)
394		ptr = grptb[st_hash(name, namelen, GID_SZ)] =
395		  (GIDC *)malloc(sizeof(GIDC));
396
397	/*
398	 * no match, look it up, if no match store it as an invalid entry,
399	 * or store the matching gid
400	 */
401	if (ptr == NULL) {
402		if ((gr = getgrnam(name)) == NULL)
403			return(-1);
404		*gid = gr->gr_gid;
405		return(0);
406	}
407
408	(void)strncpy(ptr->name, name, GNMLEN - 1);
409	ptr->name[GNMLEN-1] = '\0';
410	if ((gr = getgrnam(name)) == NULL) {
411		ptr->valid = INVALID;
412		return(-1);
413	}
414	ptr->valid = VALID;
415	*gid = ptr->gid = gr->gr_gid;
416	return(0);
417}
418