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