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