Annotation of embedaddon/sudo/plugins/sudoers/pwutil.c, revision 1.1.1.2
1.1 misho 1: /*
2: * Copyright (c) 1996, 1998-2005, 2007-2011
3: * Todd C. Miller <Todd.Miller@courtesan.com>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: *
17: * Sponsored in part by the Defense Advanced Research Projects
18: * Agency (DARPA) and Air Force Research Laboratory, Air Force
19: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20: */
21:
22: #include <config.h>
23:
24: #include <sys/types.h>
25: #include <sys/stat.h>
26: #include <sys/param.h>
27: #include <stdio.h>
28: #ifdef STDC_HEADERS
29: # include <stdlib.h>
30: # include <stddef.h>
31: #else
32: # ifdef HAVE_STDLIB_H
33: # include <stdlib.h>
34: # endif
35: #endif /* STDC_HEADERS */
36: #ifdef HAVE_STRING_H
37: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
38: # include <memory.h>
39: # endif
40: # include <string.h>
41: #endif /* HAVE_STRING_H */
42: #ifdef HAVE_STRINGS_H
43: # include <strings.h>
44: #endif /* HAVE_STRINGS_H */
45: #ifdef HAVE_UNISTD_H
46: # include <unistd.h>
47: #endif /* HAVE_UNISTD_H */
48: #ifdef HAVE_SETAUTHDB
49: # include <usersec.h>
50: #endif /* HAVE_SETAUTHDB */
51: #ifdef HAVE_UTMPX_H
52: # include <utmpx.h>
53: #else
54: # include <utmp.h>
55: #endif /* HAVE_UTMPX_H */
56: #include <limits.h>
57: #include <pwd.h>
58: #include <grp.h>
59:
60: #include "sudoers.h"
61: #include "redblack.h"
62:
63: /*
64: * The passwd and group caches.
65: */
66: static struct rbtree *pwcache_byuid, *pwcache_byname;
67: static struct rbtree *grcache_bygid, *grcache_byname;
68: static struct rbtree *grlist_cache;
69:
70: static int cmp_pwuid(const void *, const void *);
71: static int cmp_pwnam(const void *, const void *);
72: static int cmp_grgid(const void *, const void *);
73:
74: #define cmp_grnam cmp_pwnam
75:
1.1.1.2 ! misho 76: #define ptr_to_item(p) ((struct cache_item *)((char *)p - offsetof(struct cache_item_##p, p)))
1.1 misho 77:
1.1.1.2 ! misho 78: /*
! 79: * Generic cache element.
! 80: */
1.1 misho 81: struct cache_item {
82: unsigned int refcnt;
83: /* key */
84: union {
85: uid_t uid;
86: gid_t gid;
87: char *name;
88: } k;
89: /* datum */
90: union {
91: struct passwd *pw;
92: struct group *gr;
93: struct group_list *grlist;
94: } d;
95: };
96:
97: /*
1.1.1.2 ! misho 98: * Container structs to simpify size and offset calculations and guarantee
! 99: * proper aligment of struct passwd, group and group_list.
! 100: */
! 101: struct cache_item_pw {
! 102: struct cache_item cache;
! 103: struct passwd pw;
! 104: };
! 105:
! 106: struct cache_item_gr {
! 107: struct cache_item cache;
! 108: struct group gr;
! 109: };
! 110:
! 111: struct cache_item_grlist {
! 112: struct cache_item cache;
! 113: struct group_list grlist;
! 114: /* actually bigger */
! 115: };
! 116:
! 117: /*
1.1 misho 118: * Compare by uid.
119: */
120: static int
121: cmp_pwuid(const void *v1, const void *v2)
122: {
123: const struct cache_item *ci1 = (const struct cache_item *) v1;
124: const struct cache_item *ci2 = (const struct cache_item *) v2;
125: return ci1->k.uid - ci2->k.uid;
126: }
127:
128: /*
129: * Compare by user name.
130: */
131: static int
132: cmp_pwnam(const void *v1, const void *v2)
133: {
134: const struct cache_item *ci1 = (const struct cache_item *) v1;
135: const struct cache_item *ci2 = (const struct cache_item *) v2;
136: return strcmp(ci1->k.name, ci2->k.name);
137: }
138:
139: #define FIELD_SIZE(src, name, size) \
140: do { \
141: if (src->name) { \
142: size = strlen(src->name) + 1; \
143: total += size; \
144: } \
145: } while (0)
146:
147: #define FIELD_COPY(src, dst, name, size) \
148: do { \
149: if (src->name) { \
150: memcpy(cp, src->name, size); \
151: dst->name = cp; \
152: cp += size; \
153: } \
154: } while (0)
155:
156: /*
157: * Dynamically allocate space for a struct item plus the key and data
158: * elements. If name is non-NULL it is used as the key, else the
159: * uid is the key. Fills in datum from struct password.
160: */
161: static struct cache_item *
162: make_pwitem(const struct passwd *pw, const char *name)
163: {
164: char *cp;
165: const char *pw_shell;
166: size_t nsize, psize, csize, gsize, dsize, ssize, total;
1.1.1.2 ! misho 167: struct cache_item_pw *pwitem;
1.1 misho 168: struct passwd *newpw;
1.1.1.2 ! misho 169: debug_decl(make_pwitem, SUDO_DEBUG_NSS)
1.1 misho 170:
171: /* If shell field is empty, expand to _PATH_BSHELL. */
172: pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
173: ? _PATH_BSHELL : pw->pw_shell;
174:
175: /* Allocate in one big chunk for easy freeing. */
176: nsize = psize = csize = gsize = dsize = ssize = 0;
1.1.1.2 ! misho 177: total = sizeof(*pwitem);
1.1 misho 178: FIELD_SIZE(pw, pw_name, nsize);
179: FIELD_SIZE(pw, pw_passwd, psize);
180: #ifdef HAVE_LOGIN_CAP_H
181: FIELD_SIZE(pw, pw_class, csize);
182: #endif
183: FIELD_SIZE(pw, pw_gecos, gsize);
184: FIELD_SIZE(pw, pw_dir, dsize);
185: /* Treat shell specially since we expand "" -> _PATH_BSHELL */
186: ssize = strlen(pw_shell) + 1;
187: total += ssize;
188: if (name != NULL)
189: total += strlen(name) + 1;
190:
191: /* Allocate space for struct item, struct passwd and the strings. */
1.1.1.2 ! misho 192: pwitem = ecalloc(1, total);
! 193: newpw = &pwitem->pw;
1.1 misho 194:
195: /*
196: * Copy in passwd contents and make strings relative to space
1.1.1.2 ! misho 197: * at the end of the struct.
1.1 misho 198: */
1.1.1.2 ! misho 199: memcpy(newpw, pw, sizeof(*pw));
! 200: cp = (char *)(pwitem + 1);
1.1 misho 201: FIELD_COPY(pw, newpw, pw_name, nsize);
202: FIELD_COPY(pw, newpw, pw_passwd, psize);
203: #ifdef HAVE_LOGIN_CAP_H
204: FIELD_COPY(pw, newpw, pw_class, csize);
205: #endif
206: FIELD_COPY(pw, newpw, pw_gecos, gsize);
207: FIELD_COPY(pw, newpw, pw_dir, dsize);
208: /* Treat shell specially since we expand "" -> _PATH_BSHELL */
209: memcpy(cp, pw_shell, ssize);
210: newpw->pw_shell = cp;
211: cp += ssize;
212:
213: /* Set key and datum. */
214: if (name != NULL) {
215: memcpy(cp, name, strlen(name) + 1);
1.1.1.2 ! misho 216: pwitem->cache.k.name = cp;
1.1 misho 217: } else {
1.1.1.2 ! misho 218: pwitem->cache.k.uid = pw->pw_uid;
1.1 misho 219: }
1.1.1.2 ! misho 220: pwitem->cache.d.pw = newpw;
! 221: pwitem->cache.refcnt = 1;
1.1 misho 222:
1.1.1.2 ! misho 223: debug_return_ptr(&pwitem->cache);
1.1 misho 224: }
225:
226: void
227: pw_addref(struct passwd *pw)
228: {
1.1.1.2 ! misho 229: debug_decl(pw_addref, SUDO_DEBUG_NSS)
1.1 misho 230: ptr_to_item(pw)->refcnt++;
1.1.1.2 ! misho 231: debug_return;
1.1 misho 232: }
233:
234: static void
235: pw_delref_item(void *v)
236: {
237: struct cache_item *item = v;
1.1.1.2 ! misho 238: debug_decl(pw_delref_item, SUDO_DEBUG_NSS)
1.1 misho 239:
240: if (--item->refcnt == 0)
241: efree(item);
1.1.1.2 ! misho 242:
! 243: debug_return;
1.1 misho 244: }
245:
246: void
247: pw_delref(struct passwd *pw)
248: {
1.1.1.2 ! misho 249: debug_decl(pw_delref, SUDO_DEBUG_NSS)
1.1 misho 250: pw_delref_item(ptr_to_item(pw));
1.1.1.2 ! misho 251: debug_return;
1.1 misho 252: }
253:
254: /*
255: * Get a password entry by uid and allocate space for it.
256: */
257: struct passwd *
258: sudo_getpwuid(uid_t uid)
259: {
260: struct cache_item key, *item;
261: struct rbnode *node;
1.1.1.2 ! misho 262: debug_decl(sudo_getpwuid, SUDO_DEBUG_NSS)
1.1 misho 263:
264: key.k.uid = uid;
265: if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
266: item = (struct cache_item *) node->data;
267: goto done;
268: }
269: /*
270: * Cache passwd db entry if it exists or a negative response if not.
271: */
272: #ifdef HAVE_SETAUTHDB
273: aix_setauthdb(IDtouser(uid));
274: #endif
275: if ((key.d.pw = getpwuid(uid)) != NULL) {
276: item = make_pwitem(key.d.pw, NULL);
277: if (rbinsert(pwcache_byuid, item) != NULL)
278: errorx(1, _("unable to cache uid %u (%s), already exists"),
279: (unsigned int) uid, item->d.pw->pw_name);
280: } else {
1.1.1.2 ! misho 281: item = ecalloc(1, sizeof(*item));
1.1 misho 282: item->refcnt = 1;
283: item->k.uid = uid;
1.1.1.2 ! misho 284: /* item->d.pw = NULL; */
1.1 misho 285: if (rbinsert(pwcache_byuid, item) != NULL)
286: errorx(1, _("unable to cache uid %u, already exists"),
287: (unsigned int) uid);
288: }
289: #ifdef HAVE_SETAUTHDB
290: aix_restoreauthdb();
291: #endif
292: done:
293: item->refcnt++;
1.1.1.2 ! misho 294: debug_return_ptr(item->d.pw);
1.1 misho 295: }
296:
297: /*
298: * Get a password entry by name and allocate space for it.
299: */
300: struct passwd *
301: sudo_getpwnam(const char *name)
302: {
303: struct cache_item key, *item;
304: struct rbnode *node;
305: size_t len;
1.1.1.2 ! misho 306: debug_decl(sudo_getpwnam, SUDO_DEBUG_NSS)
1.1 misho 307:
308: key.k.name = (char *) name;
309: if ((node = rbfind(pwcache_byname, &key)) != NULL) {
310: item = (struct cache_item *) node->data;
311: goto done;
312: }
313: /*
314: * Cache passwd db entry if it exists or a negative response if not.
315: */
316: #ifdef HAVE_SETAUTHDB
317: aix_setauthdb((char *) name);
318: #endif
319: if ((key.d.pw = getpwnam(name)) != NULL) {
320: item = make_pwitem(key.d.pw, name);
321: if (rbinsert(pwcache_byname, item) != NULL)
322: errorx(1, _("unable to cache user %s, already exists"), name);
323: } else {
324: len = strlen(name) + 1;
1.1.1.2 ! misho 325: item = ecalloc(1, sizeof(*item) + len);
1.1 misho 326: item->refcnt = 1;
327: item->k.name = (char *) item + sizeof(*item);
328: memcpy(item->k.name, name, len);
1.1.1.2 ! misho 329: /* item->d.pw = NULL; */
1.1 misho 330: if (rbinsert(pwcache_byname, item) != NULL)
331: errorx(1, _("unable to cache user %s, already exists"), name);
332: }
333: #ifdef HAVE_SETAUTHDB
334: aix_restoreauthdb();
335: #endif
336: done:
337: item->refcnt++;
1.1.1.2 ! misho 338: debug_return_ptr(item->d.pw);
1.1 misho 339: }
340:
341: /*
342: * Take a user, uid and gid and return a faked up passwd struct.
343: */
344: struct passwd *
345: sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid)
346: {
1.1.1.2 ! misho 347: struct cache_item_pw *pwitem;
1.1 misho 348: struct passwd *pw;
349: struct rbnode *node;
350: size_t len, namelen;
351: int i;
1.1.1.2 ! misho 352: debug_decl(sudo_fakepwnam, SUDO_DEBUG_NSS)
1.1 misho 353:
354: namelen = strlen(user);
1.1.1.2 ! misho 355: len = sizeof(*pwitem) + namelen + 1 /* pw_name */ +
1.1 misho 356: sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
357: sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL);
358:
359: for (i = 0; i < 2; i++) {
1.1.1.2 ! misho 360: pwitem = ecalloc(1, len);
! 361: pw = &pwitem->pw;
1.1 misho 362: pw->pw_uid = uid;
363: pw->pw_gid = gid;
1.1.1.2 ! misho 364: pw->pw_name = (char *)(pwitem + 1);
1.1 misho 365: memcpy(pw->pw_name, user, namelen + 1);
366: pw->pw_passwd = pw->pw_name + namelen + 1;
367: memcpy(pw->pw_passwd, "*", 2);
368: pw->pw_gecos = pw->pw_passwd + 2;
369: pw->pw_gecos[0] = '\0';
370: pw->pw_dir = pw->pw_gecos + 1;
371: memcpy(pw->pw_dir, "/", 2);
372: pw->pw_shell = pw->pw_dir + 2;
373: memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
374:
1.1.1.2 ! misho 375: pwitem->cache.refcnt = 1;
! 376: pwitem->cache.d.pw = pw;
1.1 misho 377: if (i == 0) {
378: /* Store by uid, overwriting cached version. */
1.1.1.2 ! misho 379: pwitem->cache.k.uid = pw->pw_uid;
! 380: if ((node = rbinsert(pwcache_byuid, &pwitem->cache)) != NULL) {
1.1 misho 381: pw_delref_item(node->data);
1.1.1.2 ! misho 382: node->data = &pwitem->cache;
1.1 misho 383: }
384: } else {
385: /* Store by name, overwriting cached version. */
1.1.1.2 ! misho 386: pwitem->cache.k.name = pw->pw_name;
! 387: if ((node = rbinsert(pwcache_byname, &pwitem->cache)) != NULL) {
1.1 misho 388: pw_delref_item(node->data);
1.1.1.2 ! misho 389: node->data = &pwitem->cache;
1.1 misho 390: }
391: }
392: }
1.1.1.2 ! misho 393: pwitem->cache.refcnt++;
! 394: debug_return_ptr(pw);
1.1 misho 395: }
396:
397: /*
398: * Take a uid in string form "#123" and return a faked up passwd struct.
399: */
400: struct passwd *
401: sudo_fakepwnam(const char *user, gid_t gid)
402: {
403: uid_t uid;
404:
405: uid = (uid_t) atoi(user + 1);
406: return sudo_fakepwnamid(user, uid, gid);
407: }
408:
409: void
410: sudo_setpwent(void)
411: {
1.1.1.2 ! misho 412: debug_decl(sudo_setpwent, SUDO_DEBUG_NSS)
! 413:
1.1 misho 414: setpwent();
415: if (pwcache_byuid == NULL)
416: pwcache_byuid = rbcreate(cmp_pwuid);
417: if (pwcache_byname == NULL)
418: pwcache_byname = rbcreate(cmp_pwnam);
1.1.1.2 ! misho 419:
! 420: debug_return;
1.1 misho 421: }
422:
423: void
424: sudo_freepwcache(void)
425: {
1.1.1.2 ! misho 426: debug_decl(sudo_freepwcache, SUDO_DEBUG_NSS)
! 427:
1.1 misho 428: if (pwcache_byuid != NULL) {
429: rbdestroy(pwcache_byuid, pw_delref_item);
430: pwcache_byuid = NULL;
431: }
432: if (pwcache_byname != NULL) {
433: rbdestroy(pwcache_byname, pw_delref_item);
434: pwcache_byname = NULL;
435: }
1.1.1.2 ! misho 436:
! 437: debug_return;
1.1 misho 438: }
439:
440: void
441: sudo_endpwent(void)
442: {
1.1.1.2 ! misho 443: debug_decl(sudo_endpwent, SUDO_DEBUG_NSS)
! 444:
1.1 misho 445: endpwent();
446: sudo_freepwcache();
1.1.1.2 ! misho 447:
! 448: debug_return;
1.1 misho 449: }
450:
451: /*
452: * Compare by gid.
453: */
454: static int
455: cmp_grgid(const void *v1, const void *v2)
456: {
457: const struct cache_item *ci1 = (const struct cache_item *) v1;
458: const struct cache_item *ci2 = (const struct cache_item *) v2;
459: return ci1->k.gid - ci2->k.gid;
460: }
461:
462: /*
463: * Dynamically allocate space for a struct item plus the key and data
464: * elements. If name is non-NULL it is used as the key, else the
465: * gid is the key. Fills in datum from struct group.
466: */
467: static struct cache_item *
468: make_gritem(const struct group *gr, const char *name)
469: {
470: char *cp;
471: size_t nsize, psize, nmem, total, len;
1.1.1.2 ! misho 472: struct cache_item_gr *gritem;
1.1 misho 473: struct group *newgr;
1.1.1.2 ! misho 474: debug_decl(make_gritem, SUDO_DEBUG_NSS)
1.1 misho 475:
476: /* Allocate in one big chunk for easy freeing. */
477: nsize = psize = nmem = 0;
1.1.1.2 ! misho 478: total = sizeof(*gritem);
1.1 misho 479: FIELD_SIZE(gr, gr_name, nsize);
480: FIELD_SIZE(gr, gr_passwd, psize);
481: if (gr->gr_mem) {
482: for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
483: total += strlen(gr->gr_mem[nmem]) + 1;
484: nmem++;
485: total += sizeof(char *) * nmem;
486: }
487: if (name != NULL)
488: total += strlen(name) + 1;
489:
1.1.1.2 ! misho 490: gritem = ecalloc(1, total);
1.1 misho 491:
492: /*
493: * Copy in group contents and make strings relative to space
494: * at the end of the buffer. Note that gr_mem must come
495: * immediately after struct group to guarantee proper alignment.
496: */
1.1.1.2 ! misho 497: newgr = &gritem->gr;
! 498: memcpy(newgr, gr, sizeof(*gr));
! 499: cp = (char *)(gritem + 1);
1.1 misho 500: if (gr->gr_mem) {
501: newgr->gr_mem = (char **)cp;
502: cp += sizeof(char *) * nmem;
503: for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
504: len = strlen(gr->gr_mem[nmem]) + 1;
505: memcpy(cp, gr->gr_mem[nmem], len);
506: newgr->gr_mem[nmem] = cp;
507: cp += len;
508: }
509: newgr->gr_mem[nmem] = NULL;
510: }
511: FIELD_COPY(gr, newgr, gr_passwd, psize);
512: FIELD_COPY(gr, newgr, gr_name, nsize);
513:
514: /* Set key and datum. */
515: if (name != NULL) {
516: memcpy(cp, name, strlen(name) + 1);
1.1.1.2 ! misho 517: gritem->cache.k.name = cp;
1.1 misho 518: } else {
1.1.1.2 ! misho 519: gritem->cache.k.gid = gr->gr_gid;
1.1 misho 520: }
1.1.1.2 ! misho 521: gritem->cache.d.gr = newgr;
! 522: gritem->cache.refcnt = 1;
1.1 misho 523:
1.1.1.2 ! misho 524: debug_return_ptr(&gritem->cache);
1.1 misho 525: }
526:
527: #ifdef HAVE_UTMPX_H
528: # define GROUPNAME_LEN (sizeof((struct utmpx *)0)->ut_user + 1)
529: #else
530: # ifdef HAVE_STRUCT_UTMP_UT_USER
531: # define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_user + 1)
532: # else
533: # define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_name + 1)
534: # endif
535: #endif /* HAVE_UTMPX_H */
536:
537: /*
538: * Dynamically allocate space for a struct item plus the key and data
539: * elements. Fills in datum from the groups and gids arrays.
540: */
541: static struct cache_item *
542: make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids)
543: {
544: char *cp;
545: size_t i, nsize, ngroups, total, len;
1.1.1.2 ! misho 546: struct cache_item_grlist *grlitem;
1.1 misho 547: struct group_list *grlist;
548: struct group *grp;
1.1.1.2 ! misho 549: debug_decl(make_grlist_item, SUDO_DEBUG_NSS)
1.1 misho 550:
551: #ifdef HAVE_SETAUTHDB
552: aix_setauthdb((char *) user);
553: #endif
554:
555: /* Allocate in one big chunk for easy freeing. */
556: nsize = strlen(user) + 1;
1.1.1.2 ! misho 557: total = sizeof(*grlitem) + nsize;
1.1 misho 558: total += sizeof(char *) * ngids;
559: total += sizeof(gid_t *) * ngids;
560: total += GROUPNAME_LEN * ngids;
561:
562: again:
1.1.1.2 ! misho 563: grlitem = ecalloc(1, total);
1.1 misho 564:
565: /*
566: * Copy in group list and make pointers relative to space
567: * at the end of the buffer. Note that the groups array must come
568: * immediately after struct group to guarantee proper alignment.
569: */
1.1.1.2 ! misho 570: grlist = &grlitem->grlist;
! 571: cp = (char *)(grlitem + 1);
1.1 misho 572: grlist->groups = (char **)cp;
573: cp += sizeof(char *) * ngids;
574: grlist->gids = (gid_t *)cp;
575: cp += sizeof(gid_t) * ngids;
576:
577: /* Set key and datum. */
578: memcpy(cp, user, nsize);
1.1.1.2 ! misho 579: grlitem->cache.k.name = cp;
! 580: grlitem->cache.d.grlist = grlist;
! 581: grlitem->cache.refcnt = 1;
1.1 misho 582: cp += nsize;
583:
584: /*
585: * Store group IDs.
586: */
587: for (i = 0; i < ngids; i++)
588: grlist->gids[i] = gids[i];
589: grlist->ngids = ngids;
590:
591: /*
592: * Resolve and store group names by ID.
593: */
594: ngroups = 0;
595: for (i = 0; i < ngids; i++) {
596: if ((grp = sudo_getgrgid(gids[i])) != NULL) {
597: len = strlen(grp->gr_name) + 1;
1.1.1.2 ! misho 598: if (cp - (char *)grlitem + len > total) {
1.1 misho 599: total += len + GROUPNAME_LEN;
1.1.1.2 ! misho 600: efree(grlitem);
1.1 misho 601: gr_delref(grp);
602: goto again;
603: }
604: memcpy(cp, grp->gr_name, len);
605: grlist->groups[ngroups++] = cp;
606: cp += len;
607: gr_delref(grp);
608: }
609: }
610: grlist->ngroups = ngroups;
611:
612: #ifdef HAVE_SETAUTHDB
613: aix_restoreauthdb();
614: #endif
615:
1.1.1.2 ! misho 616: debug_return_ptr(&grlitem->cache);
1.1 misho 617: }
618:
619: void
620: gr_addref(struct group *gr)
621: {
1.1.1.2 ! misho 622: debug_decl(gr_addref, SUDO_DEBUG_NSS)
1.1 misho 623: ptr_to_item(gr)->refcnt++;
1.1.1.2 ! misho 624: debug_return;
1.1 misho 625: }
626:
627: static void
628: gr_delref_item(void *v)
629: {
630: struct cache_item *item = v;
1.1.1.2 ! misho 631: debug_decl(gr_delref_item, SUDO_DEBUG_NSS)
1.1 misho 632:
633: if (--item->refcnt == 0)
634: efree(item);
1.1.1.2 ! misho 635:
! 636: debug_return;
1.1 misho 637: }
638:
639: void
640: gr_delref(struct group *gr)
641: {
1.1.1.2 ! misho 642: debug_decl(gr_delref, SUDO_DEBUG_NSS)
1.1 misho 643: gr_delref_item(ptr_to_item(gr));
1.1.1.2 ! misho 644: debug_return;
1.1 misho 645: }
646:
647: /*
648: * Get a group entry by gid and allocate space for it.
649: */
650: struct group *
651: sudo_getgrgid(gid_t gid)
652: {
653: struct cache_item key, *item;
654: struct rbnode *node;
1.1.1.2 ! misho 655: debug_decl(sudo_getgrgid, SUDO_DEBUG_NSS)
1.1 misho 656:
657: key.k.gid = gid;
658: if ((node = rbfind(grcache_bygid, &key)) != NULL) {
659: item = (struct cache_item *) node->data;
660: goto done;
661: }
662: /*
663: * Cache group db entry if it exists or a negative response if not.
664: */
665: if ((key.d.gr = getgrgid(gid)) != NULL) {
666: item = make_gritem(key.d.gr, NULL);
667: if (rbinsert(grcache_bygid, item) != NULL)
668: errorx(1, _("unable to cache gid %u (%s), already exists"),
669: (unsigned int) gid, key.d.gr->gr_name);
670: } else {
1.1.1.2 ! misho 671: item = ecalloc(1, sizeof(*item));
1.1 misho 672: item->refcnt = 1;
673: item->k.gid = gid;
1.1.1.2 ! misho 674: /* item->d.gr = NULL; */
1.1 misho 675: if (rbinsert(grcache_bygid, item) != NULL)
676: errorx(1, _("unable to cache gid %u, already exists"),
677: (unsigned int) gid);
678: }
679: done:
680: item->refcnt++;
1.1.1.2 ! misho 681: debug_return_ptr(item->d.gr);
1.1 misho 682: }
683:
684: /*
685: * Get a group entry by name and allocate space for it.
686: */
687: struct group *
688: sudo_getgrnam(const char *name)
689: {
690: struct cache_item key, *item;
691: struct rbnode *node;
692: size_t len;
1.1.1.2 ! misho 693: debug_decl(sudo_getgrnam, SUDO_DEBUG_NSS)
1.1 misho 694:
695: key.k.name = (char *) name;
696: if ((node = rbfind(grcache_byname, &key)) != NULL) {
697: item = (struct cache_item *) node->data;
698: goto done;
699: }
700: /*
701: * Cache group db entry if it exists or a negative response if not.
702: */
703: if ((key.d.gr = getgrnam(name)) != NULL) {
704: item = make_gritem(key.d.gr, name);
705: if (rbinsert(grcache_byname, item) != NULL)
706: errorx(1, _("unable to cache group %s, already exists"), name);
707: } else {
708: len = strlen(name) + 1;
1.1.1.2 ! misho 709: item = ecalloc(1, sizeof(*item) + len);
1.1 misho 710: item->refcnt = 1;
711: item->k.name = (char *) item + sizeof(*item);
712: memcpy(item->k.name, name, len);
1.1.1.2 ! misho 713: /* item->d.gr = NULL; */
1.1 misho 714: if (rbinsert(grcache_byname, item) != NULL)
715: errorx(1, _("unable to cache group %s, already exists"), name);
716: }
717: done:
718: item->refcnt++;
1.1.1.2 ! misho 719: debug_return_ptr(item->d.gr);
1.1 misho 720: }
721:
722: /*
723: * Take a gid in string form "#123" and return a faked up group struct.
724: */
725: struct group *
726: sudo_fakegrnam(const char *group)
727: {
1.1.1.2 ! misho 728: struct cache_item_gr *gritem;
1.1 misho 729: struct group *gr;
730: struct rbnode *node;
731: size_t len, namelen;
732: int i;
1.1.1.2 ! misho 733: debug_decl(sudo_fakegrnam, SUDO_DEBUG_NSS)
1.1 misho 734:
735: namelen = strlen(group);
1.1.1.2 ! misho 736: len = sizeof(*gritem) + namelen + 1;
1.1 misho 737:
738: for (i = 0; i < 2; i++) {
1.1.1.2 ! misho 739: gritem = ecalloc(1, len);
! 740: gr = &gritem->gr;
1.1 misho 741: gr->gr_gid = (gid_t) atoi(group + 1);
1.1.1.2 ! misho 742: gr->gr_name = (char *)(gritem + 1);
1.1 misho 743: memcpy(gr->gr_name, group, namelen + 1);
744:
1.1.1.2 ! misho 745: gritem->cache.refcnt = 1;
! 746: gritem->cache.d.gr = gr;
1.1 misho 747: if (i == 0) {
748: /* Store by gid, overwriting cached version. */
1.1.1.2 ! misho 749: gritem->cache.k.gid = gr->gr_gid;
! 750: if ((node = rbinsert(grcache_bygid, &gritem->cache)) != NULL) {
1.1 misho 751: gr_delref_item(node->data);
1.1.1.2 ! misho 752: node->data = &gritem->cache;
1.1 misho 753: }
754: } else {
755: /* Store by name, overwriting cached version. */
1.1.1.2 ! misho 756: gritem->cache.k.name = gr->gr_name;
! 757: if ((node = rbinsert(grcache_byname, &gritem->cache)) != NULL) {
1.1 misho 758: gr_delref_item(node->data);
1.1.1.2 ! misho 759: node->data = &gritem->cache;
1.1 misho 760: }
761: }
762: }
1.1.1.2 ! misho 763: gritem->cache.refcnt++;
! 764: debug_return_ptr(gr);
1.1 misho 765: }
766:
767: void
768: grlist_addref(struct group_list *grlist)
769: {
1.1.1.2 ! misho 770: debug_decl(gr_addref, SUDO_DEBUG_NSS)
1.1 misho 771: ptr_to_item(grlist)->refcnt++;
1.1.1.2 ! misho 772: debug_return;
1.1 misho 773: }
774:
775: static void
776: grlist_delref_item(void *v)
777: {
778: struct cache_item *item = v;
1.1.1.2 ! misho 779: debug_decl(gr_delref_item, SUDO_DEBUG_NSS)
1.1 misho 780:
781: if (--item->refcnt == 0)
782: efree(item);
1.1.1.2 ! misho 783:
! 784: debug_return;
1.1 misho 785: }
786:
787: void
788: grlist_delref(struct group_list *grlist)
789: {
1.1.1.2 ! misho 790: debug_decl(gr_delref, SUDO_DEBUG_NSS)
1.1 misho 791: grlist_delref_item(ptr_to_item(grlist));
1.1.1.2 ! misho 792: debug_return;
1.1 misho 793: }
794:
795: void
796: sudo_setgrent(void)
797: {
1.1.1.2 ! misho 798: debug_decl(sudo_setgrent, SUDO_DEBUG_NSS)
! 799:
1.1 misho 800: setgrent();
801: if (grcache_bygid == NULL)
802: grcache_bygid = rbcreate(cmp_grgid);
803: if (grcache_byname == NULL)
804: grcache_byname = rbcreate(cmp_grnam);
805: if (grlist_cache == NULL)
806: grlist_cache = rbcreate(cmp_grnam);
1.1.1.2 ! misho 807:
! 808: debug_return;
1.1 misho 809: }
810:
811: void
812: sudo_freegrcache(void)
813: {
1.1.1.2 ! misho 814: debug_decl(sudo_freegrcache, SUDO_DEBUG_NSS)
! 815:
1.1 misho 816: if (grcache_bygid != NULL) {
817: rbdestroy(grcache_bygid, gr_delref_item);
818: grcache_bygid = NULL;
819: }
820: if (grcache_byname != NULL) {
821: rbdestroy(grcache_byname, gr_delref_item);
822: grcache_byname = NULL;
823: }
824: if (grlist_cache != NULL) {
825: rbdestroy(grlist_cache, grlist_delref_item);
826: grlist_cache = NULL;
827: }
1.1.1.2 ! misho 828:
! 829: debug_return;
1.1 misho 830: }
831:
832: void
833: sudo_endgrent(void)
834: {
1.1.1.2 ! misho 835: debug_decl(sudo_endgrent, SUDO_DEBUG_NSS)
! 836:
1.1 misho 837: endgrent();
838: sudo_freegrcache();
1.1.1.2 ! misho 839:
! 840: debug_return;
1.1 misho 841: }
842:
843: struct group_list *
844: get_group_list(struct passwd *pw)
845: {
846: struct cache_item key, *item;
847: struct rbnode *node;
848: size_t len;
849: GETGROUPS_T *gids;
850: int ngids;
1.1.1.2 ! misho 851: debug_decl(get_group_list, SUDO_DEBUG_NSS)
1.1 misho 852:
853: key.k.name = pw->pw_name;
854: if ((node = rbfind(grlist_cache, &key)) != NULL) {
855: item = (struct cache_item *) node->data;
856: goto done;
857: }
858: /*
859: * Cache group db entry if it exists or a negative response if not.
860: */
861: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
862: ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2;
863: if (ngids < 0)
864: #endif
865: ngids = NGROUPS_MAX * 2;
866: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
867: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
868: efree(gids);
869: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
870: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
871: efree(gids);
1.1.1.2 ! misho 872: debug_return_ptr(NULL);
1.1 misho 873: }
874: }
875: if (ngids > 0) {
876: if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL)
877: errorx(1, "unable to parse group list for %s", pw->pw_name);
878: efree(gids);
879: if (rbinsert(grlist_cache, item) != NULL)
880: errorx(1, "unable to cache group list for %s, already exists",
881: pw->pw_name);
882: } else {
883: /* Should not happen. */
884: len = strlen(pw->pw_name) + 1;
1.1.1.2 ! misho 885: item = ecalloc(1, sizeof(*item) + len);
1.1 misho 886: item->refcnt = 1;
887: item->k.name = (char *) item + sizeof(*item);
888: memcpy(item->k.name, pw->pw_name, len);
1.1.1.2 ! misho 889: /* item->d.grlist = NULL; */
1.1 misho 890: if (rbinsert(grlist_cache, item) != NULL)
891: errorx(1, "unable to cache group list for %s, already exists",
892: pw->pw_name);
893: }
894: done:
895: item->refcnt++;
1.1.1.2 ! misho 896: debug_return_ptr(item->d.grlist);
1.1 misho 897: }
898:
899: void
900: set_group_list(const char *user, GETGROUPS_T *gids, int ngids)
901: {
902: struct cache_item key, *item;
903: struct rbnode *node;
1.1.1.2 ! misho 904: debug_decl(set_group_list, SUDO_DEBUG_NSS)
1.1 misho 905:
906: /*
907: * Cache group db entry if it doesn't already exist
908: */
909: key.k.name = (char *) user;
910: if ((node = rbfind(grlist_cache, &key)) == NULL) {
911: if ((item = make_grlist_item(user, gids, ngids)) == NULL)
912: errorx(1, "unable to parse group list for %s", user);
913: if (rbinsert(grlist_cache, item) != NULL)
914: errorx(1, "unable to cache group list for %s, already exists",
915: user);
916: }
1.1.1.2 ! misho 917: debug_return;
1.1 misho 918: }
919:
1.1.1.2 ! misho 920: bool
1.1 misho 921: user_in_group(struct passwd *pw, const char *group)
922: {
923: struct group_list *grlist;
924: struct group *grp = NULL;
1.1.1.2 ! misho 925: int i;
! 926: bool matched = false;
! 927: debug_decl(user_in_group, SUDO_DEBUG_NSS)
1.1 misho 928:
929: if ((grlist = get_group_list(pw)) != NULL) {
930: /*
931: * If it could be a sudo-style group ID check gids first.
932: */
933: if (group[0] == '#') {
934: gid_t gid = atoi(group + 1);
935: if (gid == pw->pw_gid) {
1.1.1.2 ! misho 936: matched = true;
1.1 misho 937: goto done;
938: }
939: for (i = 0; i < grlist->ngids; i++) {
940: if (gid == grlist->gids[i]) {
1.1.1.2 ! misho 941: matched = true;
1.1 misho 942: goto done;
943: }
944: }
945: }
946:
947: /*
948: * Next check the supplementary group vector.
949: * It usually includes the password db group too.
950: */
951: for (i = 0; i < grlist->ngroups; i++) {
952: if (strcasecmp(group, grlist->groups[i]) == 0) {
1.1.1.2 ! misho 953: matched = true;
1.1 misho 954: goto done;
955: }
956: }
957:
958: /* Finally check against user's primary (passwd file) group. */
959: if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
960: if (strcasecmp(group, grp->gr_name) == 0) {
1.1.1.2 ! misho 961: matched = true;
1.1 misho 962: goto done;
963: }
964: }
965: done:
966: if (grp != NULL)
967: gr_delref(grp);
968: grlist_delref(grlist);
969: }
1.1.1.2 ! misho 970: debug_return_bool(matched);
1.1 misho 971: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>