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