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