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