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
54 static int pwopn = 0; /* is password file open */
55 static int gropn = 0; /* is group file open */
56 static UIDC **uidtb = NULL; /* uid to name cache */
57 static GIDC **gidtb = NULL; /* gid to name cache */
58 static UIDC **usrtb = NULL; /* user name to uid cache */
59 static 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
68 int
uidtb_start(void)69 uidtb_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
92 int
gidtb_start(void)93 gidtb_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
116 int
usrtb_start(void)117 usrtb_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
140 int
grptb_start(void)141 grptb_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
165 const char *
name_uid(uid_t uid,int frc)166 name_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
232 const char *
name_gid(gid_t gid,int frc)233 name_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
298 int
uid_name(char * name,uid_t * uid)299 uid_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
362 int
gid_name(char * name,gid_t * gid)363 gid_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