Annotation of embedaddon/sudo/plugins/sudoers/pwutil.c, revision 1.1.1.3
1.1 misho 1: /*
1.1.1.3 ! misho 2: * Copyright (c) 1996, 1998-2005, 2007-2012
1.1 misho 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
1.1.1.3 ! misho 227: sudo_pw_addref(struct passwd *pw)
1.1 misho 228: {
1.1.1.3 ! misho 229: debug_decl(sudo_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
1.1.1.3 ! misho 235: sudo_pw_delref_item(void *v)
1.1 misho 236: {
237: struct cache_item *item = v;
1.1.1.3 ! misho 238: debug_decl(sudo_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
1.1.1.3 ! misho 247: sudo_pw_delref(struct passwd *pw)
1.1 misho 248: {
1.1.1.3 ! misho 249: debug_decl(sudo_pw_delref, SUDO_DEBUG_NSS)
! 250: sudo_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.1.3 ! misho 381: sudo_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.1.3 ! misho 388: sudo_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) {
1.1.1.3 ! misho 429: rbdestroy(pwcache_byuid, sudo_pw_delref_item);
1.1 misho 430: pwcache_byuid = NULL;
431: }
432: if (pwcache_byname != NULL) {
1.1.1.3 ! misho 433: rbdestroy(pwcache_byname, sudo_pw_delref_item);
1.1 misho 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.1.3 ! misho 601: sudo_gr_delref(grp);
1.1 misho 602: goto again;
603: }
604: memcpy(cp, grp->gr_name, len);
605: grlist->groups[ngroups++] = cp;
606: cp += len;
1.1.1.3 ! misho 607: sudo_gr_delref(grp);
1.1 misho 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
1.1.1.3 ! misho 620: sudo_gr_addref(struct group *gr)
1.1 misho 621: {
1.1.1.3 ! misho 622: debug_decl(sudo_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
1.1.1.3 ! misho 628: sudo_gr_delref_item(void *v)
1.1 misho 629: {
630: struct cache_item *item = v;
1.1.1.3 ! misho 631: debug_decl(sudo_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
1.1.1.3 ! misho 640: sudo_gr_delref(struct group *gr)
1.1 misho 641: {
1.1.1.3 ! misho 642: debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
! 643: sudo_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.1.3 ! misho 751: sudo_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.1.3 ! misho 758: sudo_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
1.1.1.3 ! misho 768: sudo_grlist_addref(struct group_list *grlist)
1.1 misho 769: {
1.1.1.3 ! misho 770: debug_decl(sudo_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
1.1.1.3 ! misho 776: sudo_grlist_delref_item(void *v)
1.1 misho 777: {
778: struct cache_item *item = v;
1.1.1.3 ! misho 779: debug_decl(sudo_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
1.1.1.3 ! misho 788: sudo_grlist_delref(struct group_list *grlist)
1.1 misho 789: {
1.1.1.3 ! misho 790: debug_decl(sudo_gr_delref, SUDO_DEBUG_NSS)
! 791: sudo_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) {
1.1.1.3 ! misho 817: rbdestroy(grcache_bygid, sudo_gr_delref_item);
1.1 misho 818: grcache_bygid = NULL;
819: }
820: if (grcache_byname != NULL) {
1.1.1.3 ! misho 821: rbdestroy(grcache_byname, sudo_gr_delref_item);
1.1 misho 822: grcache_byname = NULL;
823: }
824: if (grlist_cache != NULL) {
1.1.1.3 ! misho 825: rbdestroy(grlist_cache, sudo_grlist_delref_item);
1.1 misho 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 *
1.1.1.3 ! misho 844: sudo_get_grlist(struct passwd *pw)
1.1 misho 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.3 ! misho 851: debug_decl(sudo_get_grlist, 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.
1.1.1.3 ! misho 860: * Use gids list from front-end if possible, otherwise getgrouplist().
1.1 misho 861: */
1.1.1.3 ! misho 862: if (pw == sudo_user.pw && sudo_user.gids != NULL) {
! 863: gids = user_gids;
! 864: ngids = user_ngids;
! 865: user_gids = NULL;
! 866: user_ngids = 0;
! 867: } else {
1.1 misho 868: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
1.1.1.3 ! misho 869: ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2;
! 870: if (ngids < 0)
1.1 misho 871: #endif
1.1.1.3 ! misho 872: ngids = NGROUPS_MAX * 2;
1.1 misho 873: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
874: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
875: efree(gids);
1.1.1.3 ! misho 876: gids = emalloc2(ngids, sizeof(GETGROUPS_T));
! 877: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
! 878: efree(gids);
! 879: debug_return_ptr(NULL);
! 880: }
1.1 misho 881: }
882: }
883: if (ngids > 0) {
884: if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL)
885: errorx(1, "unable to parse group list for %s", pw->pw_name);
886: efree(gids);
887: if (rbinsert(grlist_cache, item) != NULL)
888: errorx(1, "unable to cache group list for %s, already exists",
889: pw->pw_name);
890: } else {
891: /* Should not happen. */
892: len = strlen(pw->pw_name) + 1;
1.1.1.2 misho 893: item = ecalloc(1, sizeof(*item) + len);
1.1 misho 894: item->refcnt = 1;
895: item->k.name = (char *) item + sizeof(*item);
896: memcpy(item->k.name, pw->pw_name, len);
1.1.1.2 misho 897: /* item->d.grlist = NULL; */
1.1 misho 898: if (rbinsert(grlist_cache, item) != NULL)
899: errorx(1, "unable to cache group list for %s, already exists",
900: pw->pw_name);
901: }
902: done:
903: item->refcnt++;
1.1.1.2 misho 904: debug_return_ptr(item->d.grlist);
1.1 misho 905: }
906:
1.1.1.2 misho 907: bool
1.1 misho 908: user_in_group(struct passwd *pw, const char *group)
909: {
910: struct group_list *grlist;
911: struct group *grp = NULL;
1.1.1.2 misho 912: int i;
913: bool matched = false;
914: debug_decl(user_in_group, SUDO_DEBUG_NSS)
1.1 misho 915:
1.1.1.3 ! misho 916: if ((grlist = sudo_get_grlist(pw)) != NULL) {
1.1 misho 917: /*
918: * If it could be a sudo-style group ID check gids first.
919: */
920: if (group[0] == '#') {
921: gid_t gid = atoi(group + 1);
922: if (gid == pw->pw_gid) {
1.1.1.2 misho 923: matched = true;
1.1 misho 924: goto done;
925: }
926: for (i = 0; i < grlist->ngids; i++) {
927: if (gid == grlist->gids[i]) {
1.1.1.2 misho 928: matched = true;
1.1 misho 929: goto done;
930: }
931: }
932: }
933:
934: /*
935: * Next check the supplementary group vector.
936: * It usually includes the password db group too.
937: */
938: for (i = 0; i < grlist->ngroups; i++) {
939: if (strcasecmp(group, grlist->groups[i]) == 0) {
1.1.1.2 misho 940: matched = true;
1.1 misho 941: goto done;
942: }
943: }
944:
945: /* Finally check against user's primary (passwd file) group. */
946: if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) {
947: if (strcasecmp(group, grp->gr_name) == 0) {
1.1.1.2 misho 948: matched = true;
1.1 misho 949: goto done;
950: }
951: }
952: done:
953: if (grp != NULL)
1.1.1.3 ! misho 954: sudo_gr_delref(grp);
! 955: sudo_grlist_delref(grlist);
1.1 misho 956: }
1.1.1.2 misho 957: debug_return_bool(matched);
1.1 misho 958: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>