]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - usr.sbin/makefs/compat/pwcache.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / usr.sbin / makefs / compat / pwcache.c
1 /*      $NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc 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 /*-
37  * Copyright (c) 2002 The NetBSD Foundation, Inc.
38  * All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
50  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
51  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
53  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59  * POSSIBILITY OF SUCH DAMAGE.
60  */
61
62 #include <sys/cdefs.h>
63 __FBSDID("$FreeBSD$");
64
65 #include <sys/types.h>
66 #include <sys/param.h>
67
68 #include <assert.h>
69 #include <grp.h>
70 #include <pwd.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
75
76 #ifdef __weak_alias
77 __weak_alias(user_from_uid,_user_from_uid)
78 __weak_alias(group_from_gid,_group_from_gid)
79 __weak_alias(pwcache_userdb,_pwcache_userdb)
80 __weak_alias(pwcache_groupdb,_pwcache_groupdb)
81 #endif
82
83 #include "pwcache.h"
84
85 /*
86  * routines that control user, group, uid and gid caches (for the archive
87  * member print routine).
88  * IMPORTANT:
89  * these routines cache BOTH hits and misses, a major performance improvement
90  */
91
92 /*
93  * function pointers to various name lookup routines.
94  * these may be changed as necessary.
95  */
96 static  int             (*_pwcache_setgroupent)(int)            = setgroupent;
97 static  void            (*_pwcache_endgrent)(void)              = endgrent;
98 static  struct group *  (*_pwcache_getgrnam)(const char *)      = getgrnam;
99 static  struct group *  (*_pwcache_getgrgid)(gid_t)             = getgrgid;
100 static  int             (*_pwcache_setpassent)(int)             = setpassent;
101 static  void            (*_pwcache_endpwent)(void)              = endpwent;
102 static  struct passwd * (*_pwcache_getpwnam)(const char *)      = getpwnam;
103 static  struct passwd * (*_pwcache_getpwuid)(uid_t)             = getpwuid;
104
105 /*
106  * internal state
107  */
108 static  int     pwopn;          /* is password file open */
109 static  int     gropn;          /* is group file open */
110 static  UIDC    **uidtb;        /* uid to name cache */
111 static  GIDC    **gidtb;        /* gid to name cache */
112 static  UIDC    **usrtb;        /* user name to uid cache */
113 static  GIDC    **grptb;        /* group name to gid cache */
114
115 static  int     uidtb_fail;     /* uidtb_start() failed ? */
116 static  int     gidtb_fail;     /* gidtb_start() failed ? */
117 static  int     usrtb_fail;     /* usrtb_start() failed ? */
118 static  int     grptb_fail;     /* grptb_start() failed ? */
119
120
121 static  u_int   st_hash(const char *, size_t, int);
122 static  int     uidtb_start(void);
123 static  int     gidtb_start(void);
124 static  int     usrtb_start(void);
125 static  int     grptb_start(void);
126
127
128 static u_int
129 st_hash(const char *name, size_t len, int tabsz)
130 {
131         u_int key = 0;
132
133         while (len--) {
134                 key += *name++;
135                 key = (key << 8) | (key >> 24);
136         }
137
138         return (key % tabsz);
139 }
140
141 /*
142  * uidtb_start
143  *      creates an an empty uidtb
144  * Return:
145  *      0 if ok, -1 otherwise
146  */
147 static int
148 uidtb_start(void)
149 {
150
151         if (uidtb != NULL)
152                 return (0);
153         if (uidtb_fail)
154                 return (-1);
155         if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
156                 ++uidtb_fail;
157                 return (-1);
158         }
159         return (0);
160 }
161
162 /*
163  * gidtb_start
164  *      creates an an empty gidtb
165  * Return:
166  *      0 if ok, -1 otherwise
167  */
168 static int
169 gidtb_start(void)
170 {
171
172         if (gidtb != NULL)
173                 return (0);
174         if (gidtb_fail)
175                 return (-1);
176         if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
177                 ++gidtb_fail;
178                 return (-1);
179         }
180         return (0);
181 }
182
183 /*
184  * usrtb_start
185  *      creates an an empty usrtb
186  * Return:
187  *      0 if ok, -1 otherwise
188  */
189 static int
190 usrtb_start(void)
191 {
192
193         if (usrtb != NULL)
194                 return (0);
195         if (usrtb_fail)
196                 return (-1);
197         if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
198                 ++usrtb_fail;
199                 return (-1);
200         }
201         return (0);
202 }
203
204 /*
205  * grptb_start
206  *      creates an an empty grptb
207  * Return:
208  *      0 if ok, -1 otherwise
209  */
210 static int
211 grptb_start(void)
212 {
213
214         if (grptb != NULL)
215                 return (0);
216         if (grptb_fail)
217                 return (-1);
218         if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
219                 ++grptb_fail;
220                 return (-1);
221         }
222         return (0);
223 }
224
225 /*
226  * user_from_uid()
227  *      caches the name (if any) for the uid. If noname clear, we always
228  *      return the stored name (if valid or invalid match).
229  *      We use a simple hash table.
230  * Return
231  *      Pointer to stored name (or a empty string)
232  */
233 const char *
234 user_from_uid(uid_t uid, int noname)
235 {
236         struct passwd *pw;
237         UIDC *ptr, **pptr;
238
239         if ((uidtb == NULL) && (uidtb_start() < 0))
240                 return (NULL);
241
242         /*
243          * see if we have this uid cached
244          */
245         pptr = uidtb + (uid % UID_SZ);
246         ptr = *pptr;
247
248         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
249                 /*
250                  * have an entry for this uid
251                  */
252                 if (!noname || (ptr->valid == VALID))
253                         return (ptr->name);
254                 return (NULL);
255         }
256
257         /*
258          * No entry for this uid, we will add it
259          */
260         if (!pwopn) {
261                 if (_pwcache_setpassent != NULL)
262                         (*_pwcache_setpassent)(1);
263                 ++pwopn;
264         }
265
266         if (ptr == NULL)
267                 *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
268
269         if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
270                 /*
271                  * no match for this uid in the local password file
272                  * a string that is the uid in numeric format
273                  */
274                 if (ptr == NULL)
275                         return (NULL);
276                 ptr->uid = uid;
277                 (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
278                 ptr->valid = INVALID;
279                 if (noname)
280                         return (NULL);
281         } else {
282                 /*
283                  * there is an entry for this uid in the password file
284                  */
285                 if (ptr == NULL)
286                         return (pw->pw_name);
287                 ptr->uid = uid;
288                 (void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
289                 ptr->valid = VALID;
290         }
291         return (ptr->name);
292 }
293
294 /*
295  * group_from_gid()
296  *      caches the name (if any) for the gid. If noname clear, we always
297  *      return the stored name (if valid or invalid match).
298  *      We use a simple hash table.
299  * Return
300  *      Pointer to stored name (or a empty string)
301  */
302 const char *
303 group_from_gid(gid_t gid, int noname)
304 {
305         struct group *gr;
306         GIDC *ptr, **pptr;
307
308         if ((gidtb == NULL) && (gidtb_start() < 0))
309                 return (NULL);
310
311         /*
312          * see if we have this gid cached
313          */
314         pptr = gidtb + (gid % GID_SZ);
315         ptr = *pptr;
316
317         if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
318                 /*
319                  * have an entry for this gid
320                  */
321                 if (!noname || (ptr->valid == VALID))
322                         return (ptr->name);
323                 return (NULL);
324         }
325
326         /*
327          * No entry for this gid, we will add it
328          */
329         if (!gropn) {
330                 if (_pwcache_setgroupent != NULL)
331                         (*_pwcache_setgroupent)(1);
332                 ++gropn;
333         }
334
335         if (ptr == NULL)
336                 *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
337
338         if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
339                 /*
340                  * no match for this gid in the local group file, put in
341                  * a string that is the gid in numberic format
342                  */
343                 if (ptr == NULL)
344                         return (NULL);
345                 ptr->gid = gid;
346                 (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
347                 ptr->valid = INVALID;
348                 if (noname)
349                         return (NULL);
350         } else {
351                 /*
352                  * there is an entry for this group in the group file
353                  */
354                 if (ptr == NULL)
355                         return (gr->gr_name);
356                 ptr->gid = gid;
357                 (void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
358                 ptr->valid = VALID;
359         }
360         return (ptr->name);
361 }
362
363 /*
364  * uid_from_user()
365  *      caches the uid for a given user name. We use a simple hash table.
366  * Return
367  *      the uid (if any) for a user name, or a -1 if no match can be found
368  */
369 int
370 uid_from_user(const char *name, uid_t *uid)
371 {
372         struct passwd *pw;
373         UIDC *ptr, **pptr;
374         size_t namelen;
375
376         /*
377          * return -1 for mangled names
378          */
379         if (name == NULL || ((namelen = strlen(name)) == 0))
380                 return (-1);
381         if ((usrtb == NULL) && (usrtb_start() < 0))
382                 return (-1);
383
384         /*
385          * look up in hash table, if found and valid return the uid,
386          * if found and invalid, return a -1
387          */
388         pptr = usrtb + st_hash(name, namelen, UNM_SZ);
389         ptr = *pptr;
390
391         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
392                 if (ptr->valid == INVALID)
393                         return (-1);
394                 *uid = ptr->uid;
395                 return (0);
396         }
397
398         if (!pwopn) {
399                 if (_pwcache_setpassent != NULL)
400                         (*_pwcache_setpassent)(1);
401                 ++pwopn;
402         }
403
404         if (ptr == NULL)
405                 *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
406
407         /*
408          * no match, look it up, if no match store it as an invalid entry,
409          * or store the matching uid
410          */
411         if (ptr == NULL) {
412                 if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
413                         return (-1);
414                 *uid = pw->pw_uid;
415                 return (0);
416         }
417         (void)strlcpy(ptr->name, name, UNMLEN);
418         if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
419                 ptr->valid = INVALID;
420                 return (-1);
421         }
422         ptr->valid = VALID;
423         *uid = ptr->uid = pw->pw_uid;
424         return (0);
425 }
426
427 /*
428  * gid_from_group()
429  *      caches the gid for a given group name. We use a simple hash table.
430  * Return
431  *      the gid (if any) for a group name, or a -1 if no match can be found
432  */
433 int
434 gid_from_group(const char *name, gid_t *gid)
435 {
436         struct group *gr;
437         GIDC *ptr, **pptr;
438         size_t namelen;
439
440         /*
441          * return -1 for mangled names
442          */
443         if (name == NULL || ((namelen = strlen(name)) == 0))
444                 return (-1);
445         if ((grptb == NULL) && (grptb_start() < 0))
446                 return (-1);
447
448         /*
449          * look up in hash table, if found and valid return the uid,
450          * if found and invalid, return a -1
451          */
452         pptr = grptb + st_hash(name, namelen, GID_SZ);
453         ptr = *pptr;
454
455         if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
456                 if (ptr->valid == INVALID)
457                         return (-1);
458                 *gid = ptr->gid;
459                 return (0);
460         }
461
462         if (!gropn) {
463                 if (_pwcache_setgroupent != NULL)
464                         (*_pwcache_setgroupent)(1);
465                 ++gropn;
466         }
467
468         if (ptr == NULL)
469                 *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
470
471         /*
472          * no match, look it up, if no match store it as an invalid entry,
473          * or store the matching gid
474          */
475         if (ptr == NULL) {
476                 if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
477                         return (-1);
478                 *gid = gr->gr_gid;
479                 return (0);
480         }
481
482         (void)strlcpy(ptr->name, name, GNMLEN);
483         if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
484                 ptr->valid = INVALID;
485                 return (-1);
486         }
487         ptr->valid = VALID;
488         *gid = ptr->gid = gr->gr_gid;
489         return (0);
490 }
491
492 #define FLUSHTB(arr, len, fail)                         \
493         do {                                            \
494                 if (arr != NULL) {                      \
495                         for (i = 0; i < len; i++)       \
496                                 if (arr[i] != NULL)     \
497                                         free(arr[i]);   \
498                         arr = NULL;                     \
499                 }                                       \
500                 fail = 0;                               \
501         } while (/* CONSTCOND */0);
502
503 int
504 pwcache_userdb(
505         int             (*a_setpassent)(int),
506         void            (*a_endpwent)(void),
507         struct passwd * (*a_getpwnam)(const char *),
508         struct passwd * (*a_getpwuid)(uid_t))
509 {
510         int i;
511
512                 /* a_setpassent and a_endpwent may be NULL */
513         if (a_getpwnam == NULL || a_getpwuid == NULL)
514                 return (-1);
515
516         if (_pwcache_endpwent != NULL)
517                 (*_pwcache_endpwent)();
518         FLUSHTB(uidtb, UID_SZ, uidtb_fail);
519         FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
520         pwopn = 0;
521         _pwcache_setpassent = a_setpassent;
522         _pwcache_endpwent = a_endpwent;
523         _pwcache_getpwnam = a_getpwnam;
524         _pwcache_getpwuid = a_getpwuid;
525
526         return (0);
527 }
528
529 int
530 pwcache_groupdb(
531         int             (*a_setgroupent)(int),
532         void            (*a_endgrent)(void),
533         struct group *  (*a_getgrnam)(const char *),
534         struct group *  (*a_getgrgid)(gid_t))
535 {
536         int i;
537
538                 /* a_setgroupent and a_endgrent may be NULL */
539         if (a_getgrnam == NULL || a_getgrgid == NULL)
540                 return (-1);
541
542         if (_pwcache_endgrent != NULL)
543                 (*_pwcache_endgrent)();
544         FLUSHTB(gidtb, GID_SZ, gidtb_fail);
545         FLUSHTB(grptb, GNM_SZ, grptb_fail);
546         gropn = 0;
547         _pwcache_setgroupent = a_setgroupent;
548         _pwcache_endgrent = a_endgrent;
549         _pwcache_getgrnam = a_getgrnam;
550         _pwcache_getgrgid = a_getgrgid;
551
552         return (0);
553 }
554
555
556 #ifdef TEST_PWCACHE
557
558 struct passwd *
559 test_getpwnam(const char *name)
560 {
561         static struct passwd foo;
562
563         memset(&foo, 0, sizeof(foo));
564         if (strcmp(name, "toor") == 0) {
565                 foo.pw_uid = 666;
566                 return &foo;
567         }
568         return (getpwnam(name));
569 }
570
571 int
572 main(int argc, char *argv[])
573 {
574         uid_t   u;
575         int     r, i;
576
577         printf("pass 1 (default userdb)\n");
578         for (i = 1; i < argc; i++) {
579                 printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
580                     i, pwopn, usrtb_fail, usrtb);
581                 r = uid_from_user(argv[i], &u);
582                 if (r == -1)
583                         printf("  uid_from_user %s: failed\n", argv[i]);
584                 else
585                         printf("  uid_from_user %s: %d\n", argv[i], u);
586         }
587         printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
588                     pwopn, usrtb_fail, usrtb);
589
590         puts("");
591         printf("pass 2 (replacement userdb)\n");
592         printf("pwcache_userdb returned %d\n",
593             pwcache_userdb(setpassent, test_getpwnam, getpwuid));
594         printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
595
596         for (i = 1; i < argc; i++) {
597                 printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
598                     i, pwopn, usrtb_fail, usrtb);
599                 u = -1;
600                 r = uid_from_user(argv[i], &u);
601                 if (r == -1)
602                         printf("  uid_from_user %s: failed\n", argv[i]);
603                 else
604                         printf("  uid_from_user %s: %d\n", argv[i], u);
605         }
606         printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
607                     pwopn, usrtb_fail, usrtb);
608
609         puts("");
610         printf("pass 3 (null pointers)\n");
611         printf("pwcache_userdb returned %d\n",
612             pwcache_userdb(NULL, NULL, NULL));
613
614         return (0);
615 }
616 #endif  /* TEST_PWCACHE */