Return to pwutil.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers |
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: ! 76: #define ptr_to_item(p) ((struct cache_item *)((char *)(p) - sizeof(struct cache_item))) ! 77: ! 78: struct cache_item { ! 79: unsigned int refcnt; ! 80: /* key */ ! 81: union { ! 82: uid_t uid; ! 83: gid_t gid; ! 84: char *name; ! 85: } k; ! 86: /* datum */ ! 87: union { ! 88: struct passwd *pw; ! 89: struct group *gr; ! 90: struct group_list *grlist; ! 91: } d; ! 92: }; ! 93: ! 94: /* ! 95: * Compare by uid. ! 96: */ ! 97: static int ! 98: cmp_pwuid(const void *v1, const void *v2) ! 99: { ! 100: const struct cache_item *ci1 = (const struct cache_item *) v1; ! 101: const struct cache_item *ci2 = (const struct cache_item *) v2; ! 102: return ci1->k.uid - ci2->k.uid; ! 103: } ! 104: ! 105: /* ! 106: * Compare by user name. ! 107: */ ! 108: static int ! 109: cmp_pwnam(const void *v1, const void *v2) ! 110: { ! 111: const struct cache_item *ci1 = (const struct cache_item *) v1; ! 112: const struct cache_item *ci2 = (const struct cache_item *) v2; ! 113: return strcmp(ci1->k.name, ci2->k.name); ! 114: } ! 115: ! 116: #define FIELD_SIZE(src, name, size) \ ! 117: do { \ ! 118: if (src->name) { \ ! 119: size = strlen(src->name) + 1; \ ! 120: total += size; \ ! 121: } \ ! 122: } while (0) ! 123: ! 124: #define FIELD_COPY(src, dst, name, size) \ ! 125: do { \ ! 126: if (src->name) { \ ! 127: memcpy(cp, src->name, size); \ ! 128: dst->name = cp; \ ! 129: cp += size; \ ! 130: } \ ! 131: } while (0) ! 132: ! 133: /* ! 134: * Dynamically allocate space for a struct item plus the key and data ! 135: * elements. If name is non-NULL it is used as the key, else the ! 136: * uid is the key. Fills in datum from struct password. ! 137: * ! 138: * We would like to fill in the encrypted password too but the ! 139: * call to the shadow function could overwrite the pw buffer (NIS). ! 140: */ ! 141: static struct cache_item * ! 142: make_pwitem(const struct passwd *pw, const char *name) ! 143: { ! 144: char *cp; ! 145: const char *pw_shell; ! 146: size_t nsize, psize, csize, gsize, dsize, ssize, total; ! 147: struct cache_item *item; ! 148: struct passwd *newpw; ! 149: ! 150: /* If shell field is empty, expand to _PATH_BSHELL. */ ! 151: pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0') ! 152: ? _PATH_BSHELL : pw->pw_shell; ! 153: ! 154: /* Allocate in one big chunk for easy freeing. */ ! 155: nsize = psize = csize = gsize = dsize = ssize = 0; ! 156: total = sizeof(struct cache_item) + sizeof(struct passwd); ! 157: FIELD_SIZE(pw, pw_name, nsize); ! 158: FIELD_SIZE(pw, pw_passwd, psize); ! 159: #ifdef HAVE_LOGIN_CAP_H ! 160: FIELD_SIZE(pw, pw_class, csize); ! 161: #endif ! 162: FIELD_SIZE(pw, pw_gecos, gsize); ! 163: FIELD_SIZE(pw, pw_dir, dsize); ! 164: /* Treat shell specially since we expand "" -> _PATH_BSHELL */ ! 165: ssize = strlen(pw_shell) + 1; ! 166: total += ssize; ! 167: if (name != NULL) ! 168: total += strlen(name) + 1; ! 169: ! 170: /* Allocate space for struct item, struct passwd and the strings. */ ! 171: item = emalloc(total); ! 172: cp = (char *) item + sizeof(struct cache_item); ! 173: ! 174: /* ! 175: * Copy in passwd contents and make strings relative to space ! 176: * at the end of the buffer. ! 177: */ ! 178: newpw = (struct passwd *) cp; ! 179: memcpy(newpw, pw, sizeof(struct passwd)); ! 180: cp += sizeof(struct passwd); ! 181: FIELD_COPY(pw, newpw, pw_name, nsize); ! 182: FIELD_COPY(pw, newpw, pw_passwd, psize); ! 183: #ifdef HAVE_LOGIN_CAP_H ! 184: FIELD_COPY(pw, newpw, pw_class, csize); ! 185: #endif ! 186: FIELD_COPY(pw, newpw, pw_gecos, gsize); ! 187: FIELD_COPY(pw, newpw, pw_dir, dsize); ! 188: /* Treat shell specially since we expand "" -> _PATH_BSHELL */ ! 189: memcpy(cp, pw_shell, ssize); ! 190: newpw->pw_shell = cp; ! 191: cp += ssize; ! 192: ! 193: /* Set key and datum. */ ! 194: if (name != NULL) { ! 195: memcpy(cp, name, strlen(name) + 1); ! 196: item->k.name = cp; ! 197: } else { ! 198: item->k.uid = pw->pw_uid; ! 199: } ! 200: item->d.pw = newpw; ! 201: item->refcnt = 1; ! 202: ! 203: return item; ! 204: } ! 205: ! 206: void ! 207: pw_addref(struct passwd *pw) ! 208: { ! 209: ptr_to_item(pw)->refcnt++; ! 210: } ! 211: ! 212: static void ! 213: pw_delref_item(void *v) ! 214: { ! 215: struct cache_item *item = v; ! 216: ! 217: if (--item->refcnt == 0) ! 218: efree(item); ! 219: } ! 220: ! 221: void ! 222: pw_delref(struct passwd *pw) ! 223: { ! 224: pw_delref_item(ptr_to_item(pw)); ! 225: } ! 226: ! 227: /* ! 228: * Get a password entry by uid and allocate space for it. ! 229: * Fills in pw_passwd from shadow file if necessary. ! 230: */ ! 231: struct passwd * ! 232: sudo_getpwuid(uid_t uid) ! 233: { ! 234: struct cache_item key, *item; ! 235: struct rbnode *node; ! 236: ! 237: key.k.uid = uid; ! 238: if ((node = rbfind(pwcache_byuid, &key)) != NULL) { ! 239: item = (struct cache_item *) node->data; ! 240: goto done; ! 241: } ! 242: /* ! 243: * Cache passwd db entry if it exists or a negative response if not. ! 244: */ ! 245: #ifdef HAVE_SETAUTHDB ! 246: aix_setauthdb(IDtouser(uid)); ! 247: #endif ! 248: if ((key.d.pw = getpwuid(uid)) != NULL) { ! 249: item = make_pwitem(key.d.pw, NULL); ! 250: if (rbinsert(pwcache_byuid, item) != NULL) ! 251: errorx(1, _("unable to cache uid %u (%s), already exists"), ! 252: (unsigned int) uid, item->d.pw->pw_name); ! 253: } else { ! 254: item = emalloc(sizeof(*item)); ! 255: item->refcnt = 1; ! 256: item->k.uid = uid; ! 257: item->d.pw = NULL; ! 258: if (rbinsert(pwcache_byuid, item) != NULL) ! 259: errorx(1, _("unable to cache uid %u, already exists"), ! 260: (unsigned int) uid); ! 261: } ! 262: #ifdef HAVE_SETAUTHDB ! 263: aix_restoreauthdb(); ! 264: #endif ! 265: done: ! 266: item->refcnt++; ! 267: return item->d.pw; ! 268: } ! 269: ! 270: /* ! 271: * Get a password entry by name and allocate space for it. ! 272: * Fills in pw_passwd from shadow file if necessary. ! 273: */ ! 274: struct passwd * ! 275: sudo_getpwnam(const char *name) ! 276: { ! 277: struct cache_item key, *item; ! 278: struct rbnode *node; ! 279: size_t len; ! 280: ! 281: key.k.name = (char *) name; ! 282: if ((node = rbfind(pwcache_byname, &key)) != NULL) { ! 283: item = (struct cache_item *) node->data; ! 284: goto done; ! 285: } ! 286: /* ! 287: * Cache passwd db entry if it exists or a negative response if not. ! 288: */ ! 289: #ifdef HAVE_SETAUTHDB ! 290: aix_setauthdb((char *) name); ! 291: #endif ! 292: if ((key.d.pw = getpwnam(name)) != NULL) { ! 293: item = make_pwitem(key.d.pw, name); ! 294: if (rbinsert(pwcache_byname, item) != NULL) ! 295: errorx(1, _("unable to cache user %s, already exists"), name); ! 296: } else { ! 297: len = strlen(name) + 1; ! 298: item = emalloc(sizeof(*item) + len); ! 299: item->refcnt = 1; ! 300: item->k.name = (char *) item + sizeof(*item); ! 301: memcpy(item->k.name, name, len); ! 302: item->d.pw = NULL; ! 303: if (rbinsert(pwcache_byname, item) != NULL) ! 304: errorx(1, _("unable to cache user %s, already exists"), name); ! 305: } ! 306: #ifdef HAVE_SETAUTHDB ! 307: aix_restoreauthdb(); ! 308: #endif ! 309: done: ! 310: item->refcnt++; ! 311: return item->d.pw; ! 312: } ! 313: ! 314: /* ! 315: * Take a user, uid and gid and return a faked up passwd struct. ! 316: */ ! 317: struct passwd * ! 318: sudo_fakepwnamid(const char *user, uid_t uid, gid_t gid) ! 319: { ! 320: struct cache_item *item; ! 321: struct passwd *pw; ! 322: struct rbnode *node; ! 323: size_t len, namelen; ! 324: int i; ! 325: ! 326: namelen = strlen(user); ! 327: len = sizeof(*item) + sizeof(*pw) + namelen + 1 /* pw_name */ + ! 328: sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ + ! 329: sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL); ! 330: ! 331: for (i = 0; i < 2; i++) { ! 332: item = emalloc(len); ! 333: zero_bytes(item, sizeof(*item) + sizeof(*pw)); ! 334: pw = (struct passwd *) ((char *)item + sizeof(*item)); ! 335: pw->pw_uid = uid; ! 336: pw->pw_gid = gid; ! 337: pw->pw_name = (char *)pw + sizeof(struct passwd); ! 338: memcpy(pw->pw_name, user, namelen + 1); ! 339: pw->pw_passwd = pw->pw_name + namelen + 1; ! 340: memcpy(pw->pw_passwd, "*", 2); ! 341: pw->pw_gecos = pw->pw_passwd + 2; ! 342: pw->pw_gecos[0] = '\0'; ! 343: pw->pw_dir = pw->pw_gecos + 1; ! 344: memcpy(pw->pw_dir, "/", 2); ! 345: pw->pw_shell = pw->pw_dir + 2; ! 346: memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL)); ! 347: ! 348: item->refcnt = 1; ! 349: item->d.pw = pw; ! 350: if (i == 0) { ! 351: /* Store by uid, overwriting cached version. */ ! 352: item->k.uid = pw->pw_uid; ! 353: if ((node = rbinsert(pwcache_byuid, item)) != NULL) { ! 354: pw_delref_item(node->data); ! 355: node->data = item; ! 356: } ! 357: } else { ! 358: /* Store by name, overwriting cached version. */ ! 359: item->k.name = pw->pw_name; ! 360: if ((node = rbinsert(pwcache_byname, item)) != NULL) { ! 361: pw_delref_item(node->data); ! 362: node->data = item; ! 363: } ! 364: } ! 365: } ! 366: item->refcnt++; ! 367: return pw; ! 368: } ! 369: ! 370: /* ! 371: * Take a uid in string form "#123" and return a faked up passwd struct. ! 372: */ ! 373: struct passwd * ! 374: sudo_fakepwnam(const char *user, gid_t gid) ! 375: { ! 376: uid_t uid; ! 377: ! 378: uid = (uid_t) atoi(user + 1); ! 379: return sudo_fakepwnamid(user, uid, gid); ! 380: } ! 381: ! 382: void ! 383: sudo_setpwent(void) ! 384: { ! 385: setpwent(); ! 386: if (pwcache_byuid == NULL) ! 387: pwcache_byuid = rbcreate(cmp_pwuid); ! 388: if (pwcache_byname == NULL) ! 389: pwcache_byname = rbcreate(cmp_pwnam); ! 390: } ! 391: ! 392: void ! 393: sudo_freepwcache(void) ! 394: { ! 395: if (pwcache_byuid != NULL) { ! 396: rbdestroy(pwcache_byuid, pw_delref_item); ! 397: pwcache_byuid = NULL; ! 398: } ! 399: if (pwcache_byname != NULL) { ! 400: rbdestroy(pwcache_byname, pw_delref_item); ! 401: pwcache_byname = NULL; ! 402: } ! 403: } ! 404: ! 405: void ! 406: sudo_endpwent(void) ! 407: { ! 408: endpwent(); ! 409: sudo_freepwcache(); ! 410: } ! 411: ! 412: /* ! 413: * Compare by gid. ! 414: */ ! 415: static int ! 416: cmp_grgid(const void *v1, const void *v2) ! 417: { ! 418: const struct cache_item *ci1 = (const struct cache_item *) v1; ! 419: const struct cache_item *ci2 = (const struct cache_item *) v2; ! 420: return ci1->k.gid - ci2->k.gid; ! 421: } ! 422: ! 423: /* ! 424: * Dynamically allocate space for a struct item plus the key and data ! 425: * elements. If name is non-NULL it is used as the key, else the ! 426: * gid is the key. Fills in datum from struct group. ! 427: */ ! 428: static struct cache_item * ! 429: make_gritem(const struct group *gr, const char *name) ! 430: { ! 431: char *cp; ! 432: size_t nsize, psize, nmem, total, len; ! 433: struct cache_item *item; ! 434: struct group *newgr; ! 435: ! 436: /* Allocate in one big chunk for easy freeing. */ ! 437: nsize = psize = nmem = 0; ! 438: total = sizeof(struct cache_item) + sizeof(struct group); ! 439: FIELD_SIZE(gr, gr_name, nsize); ! 440: FIELD_SIZE(gr, gr_passwd, psize); ! 441: if (gr->gr_mem) { ! 442: for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) ! 443: total += strlen(gr->gr_mem[nmem]) + 1; ! 444: nmem++; ! 445: total += sizeof(char *) * nmem; ! 446: } ! 447: if (name != NULL) ! 448: total += strlen(name) + 1; ! 449: ! 450: item = emalloc(total); ! 451: cp = (char *) item + sizeof(struct cache_item); ! 452: ! 453: /* ! 454: * Copy in group contents and make strings relative to space ! 455: * at the end of the buffer. Note that gr_mem must come ! 456: * immediately after struct group to guarantee proper alignment. ! 457: */ ! 458: newgr = (struct group *)cp; ! 459: memcpy(newgr, gr, sizeof(struct group)); ! 460: cp += sizeof(struct group); ! 461: if (gr->gr_mem) { ! 462: newgr->gr_mem = (char **)cp; ! 463: cp += sizeof(char *) * nmem; ! 464: for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) { ! 465: len = strlen(gr->gr_mem[nmem]) + 1; ! 466: memcpy(cp, gr->gr_mem[nmem], len); ! 467: newgr->gr_mem[nmem] = cp; ! 468: cp += len; ! 469: } ! 470: newgr->gr_mem[nmem] = NULL; ! 471: } ! 472: FIELD_COPY(gr, newgr, gr_passwd, psize); ! 473: FIELD_COPY(gr, newgr, gr_name, nsize); ! 474: ! 475: /* Set key and datum. */ ! 476: if (name != NULL) { ! 477: memcpy(cp, name, strlen(name) + 1); ! 478: item->k.name = cp; ! 479: } else { ! 480: item->k.gid = gr->gr_gid; ! 481: } ! 482: item->d.gr = newgr; ! 483: item->refcnt = 1; ! 484: ! 485: return item; ! 486: } ! 487: ! 488: #ifdef HAVE_UTMPX_H ! 489: # define GROUPNAME_LEN (sizeof((struct utmpx *)0)->ut_user + 1) ! 490: #else ! 491: # ifdef HAVE_STRUCT_UTMP_UT_USER ! 492: # define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_user + 1) ! 493: # else ! 494: # define GROUPNAME_LEN (sizeof((struct utmp *)0)->ut_name + 1) ! 495: # endif ! 496: #endif /* HAVE_UTMPX_H */ ! 497: ! 498: /* ! 499: * Dynamically allocate space for a struct item plus the key and data ! 500: * elements. Fills in datum from the groups and gids arrays. ! 501: */ ! 502: static struct cache_item * ! 503: make_grlist_item(const char *user, GETGROUPS_T *gids, int ngids) ! 504: { ! 505: char *cp; ! 506: size_t i, nsize, ngroups, total, len; ! 507: struct cache_item *item; ! 508: struct group_list *grlist; ! 509: struct group *grp; ! 510: ! 511: #ifdef HAVE_SETAUTHDB ! 512: aix_setauthdb((char *) user); ! 513: #endif ! 514: ! 515: /* Allocate in one big chunk for easy freeing. */ ! 516: nsize = strlen(user) + 1; ! 517: total = sizeof(struct cache_item) + sizeof(struct group_list) + nsize; ! 518: total += sizeof(char *) * ngids; ! 519: total += sizeof(gid_t *) * ngids; ! 520: total += GROUPNAME_LEN * ngids; ! 521: ! 522: again: ! 523: item = emalloc(total); ! 524: cp = (char *) item + sizeof(struct cache_item); ! 525: ! 526: /* ! 527: * Copy in group list and make pointers relative to space ! 528: * at the end of the buffer. Note that the groups array must come ! 529: * immediately after struct group to guarantee proper alignment. ! 530: */ ! 531: grlist = (struct group_list *)cp; ! 532: zero_bytes(grlist, sizeof(struct group_list)); ! 533: cp += sizeof(struct group_list); ! 534: grlist->groups = (char **)cp; ! 535: cp += sizeof(char *) * ngids; ! 536: grlist->gids = (gid_t *)cp; ! 537: cp += sizeof(gid_t) * ngids; ! 538: ! 539: /* Set key and datum. */ ! 540: memcpy(cp, user, nsize); ! 541: item->k.name = cp; ! 542: item->d.grlist = grlist; ! 543: item->refcnt = 1; ! 544: cp += nsize; ! 545: ! 546: /* ! 547: * Store group IDs. ! 548: */ ! 549: for (i = 0; i < ngids; i++) ! 550: grlist->gids[i] = gids[i]; ! 551: grlist->ngids = ngids; ! 552: ! 553: /* ! 554: * Resolve and store group names by ID. ! 555: */ ! 556: ngroups = 0; ! 557: for (i = 0; i < ngids; i++) { ! 558: if ((grp = sudo_getgrgid(gids[i])) != NULL) { ! 559: len = strlen(grp->gr_name) + 1; ! 560: if (cp - (char *)item + len > total) { ! 561: total += len + GROUPNAME_LEN; ! 562: efree(item); ! 563: gr_delref(grp); ! 564: goto again; ! 565: } ! 566: memcpy(cp, grp->gr_name, len); ! 567: grlist->groups[ngroups++] = cp; ! 568: cp += len; ! 569: gr_delref(grp); ! 570: } ! 571: } ! 572: grlist->ngroups = ngroups; ! 573: ! 574: #ifdef HAVE_SETAUTHDB ! 575: aix_restoreauthdb(); ! 576: #endif ! 577: ! 578: return item; ! 579: } ! 580: ! 581: void ! 582: gr_addref(struct group *gr) ! 583: { ! 584: ptr_to_item(gr)->refcnt++; ! 585: } ! 586: ! 587: static void ! 588: gr_delref_item(void *v) ! 589: { ! 590: struct cache_item *item = v; ! 591: ! 592: if (--item->refcnt == 0) ! 593: efree(item); ! 594: } ! 595: ! 596: void ! 597: gr_delref(struct group *gr) ! 598: { ! 599: gr_delref_item(ptr_to_item(gr)); ! 600: } ! 601: ! 602: /* ! 603: * Get a group entry by gid and allocate space for it. ! 604: */ ! 605: struct group * ! 606: sudo_getgrgid(gid_t gid) ! 607: { ! 608: struct cache_item key, *item; ! 609: struct rbnode *node; ! 610: ! 611: key.k.gid = gid; ! 612: if ((node = rbfind(grcache_bygid, &key)) != NULL) { ! 613: item = (struct cache_item *) node->data; ! 614: goto done; ! 615: } ! 616: /* ! 617: * Cache group db entry if it exists or a negative response if not. ! 618: */ ! 619: if ((key.d.gr = getgrgid(gid)) != NULL) { ! 620: item = make_gritem(key.d.gr, NULL); ! 621: if (rbinsert(grcache_bygid, item) != NULL) ! 622: errorx(1, _("unable to cache gid %u (%s), already exists"), ! 623: (unsigned int) gid, key.d.gr->gr_name); ! 624: } else { ! 625: item = emalloc(sizeof(*item)); ! 626: item->refcnt = 1; ! 627: item->k.gid = gid; ! 628: item->d.gr = NULL; ! 629: if (rbinsert(grcache_bygid, item) != NULL) ! 630: errorx(1, _("unable to cache gid %u, already exists"), ! 631: (unsigned int) gid); ! 632: } ! 633: done: ! 634: item->refcnt++; ! 635: return item->d.gr; ! 636: } ! 637: ! 638: /* ! 639: * Get a group entry by name and allocate space for it. ! 640: */ ! 641: struct group * ! 642: sudo_getgrnam(const char *name) ! 643: { ! 644: struct cache_item key, *item; ! 645: struct rbnode *node; ! 646: size_t len; ! 647: ! 648: key.k.name = (char *) name; ! 649: if ((node = rbfind(grcache_byname, &key)) != NULL) { ! 650: item = (struct cache_item *) node->data; ! 651: goto done; ! 652: } ! 653: /* ! 654: * Cache group db entry if it exists or a negative response if not. ! 655: */ ! 656: if ((key.d.gr = getgrnam(name)) != NULL) { ! 657: item = make_gritem(key.d.gr, name); ! 658: if (rbinsert(grcache_byname, item) != NULL) ! 659: errorx(1, _("unable to cache group %s, already exists"), name); ! 660: } else { ! 661: len = strlen(name) + 1; ! 662: item = emalloc(sizeof(*item) + len); ! 663: item->refcnt = 1; ! 664: item->k.name = (char *) item + sizeof(*item); ! 665: memcpy(item->k.name, name, len); ! 666: item->d.gr = NULL; ! 667: if (rbinsert(grcache_byname, item) != NULL) ! 668: errorx(1, _("unable to cache group %s, already exists"), name); ! 669: } ! 670: done: ! 671: item->refcnt++; ! 672: return item->d.gr; ! 673: } ! 674: ! 675: /* ! 676: * Take a gid in string form "#123" and return a faked up group struct. ! 677: */ ! 678: struct group * ! 679: sudo_fakegrnam(const char *group) ! 680: { ! 681: struct cache_item *item; ! 682: struct group *gr; ! 683: struct rbnode *node; ! 684: size_t len, namelen; ! 685: int i; ! 686: ! 687: namelen = strlen(group); ! 688: len = sizeof(*item) + sizeof(*gr) + namelen + 1; ! 689: ! 690: for (i = 0; i < 2; i++) { ! 691: item = emalloc(len); ! 692: zero_bytes(item, sizeof(*item) + sizeof(*gr)); ! 693: gr = (struct group *) ((char *)item + sizeof(*item)); ! 694: gr->gr_gid = (gid_t) atoi(group + 1); ! 695: gr->gr_name = (char *)gr + sizeof(struct group); ! 696: memcpy(gr->gr_name, group, namelen + 1); ! 697: ! 698: item->refcnt = 1; ! 699: item->d.gr = gr; ! 700: if (i == 0) { ! 701: /* Store by gid, overwriting cached version. */ ! 702: item->k.gid = gr->gr_gid; ! 703: if ((node = rbinsert(grcache_bygid, item)) != NULL) { ! 704: gr_delref_item(node->data); ! 705: node->data = item; ! 706: } ! 707: } else { ! 708: /* Store by name, overwriting cached version. */ ! 709: item->k.name = gr->gr_name; ! 710: if ((node = rbinsert(grcache_byname, item)) != NULL) { ! 711: gr_delref_item(node->data); ! 712: node->data = item; ! 713: } ! 714: } ! 715: } ! 716: item->refcnt++; ! 717: return gr; ! 718: } ! 719: ! 720: void ! 721: grlist_addref(struct group_list *grlist) ! 722: { ! 723: ptr_to_item(grlist)->refcnt++; ! 724: } ! 725: ! 726: static void ! 727: grlist_delref_item(void *v) ! 728: { ! 729: struct cache_item *item = v; ! 730: ! 731: if (--item->refcnt == 0) ! 732: efree(item); ! 733: } ! 734: ! 735: void ! 736: grlist_delref(struct group_list *grlist) ! 737: { ! 738: grlist_delref_item(ptr_to_item(grlist)); ! 739: } ! 740: ! 741: void ! 742: sudo_setgrent(void) ! 743: { ! 744: setgrent(); ! 745: if (grcache_bygid == NULL) ! 746: grcache_bygid = rbcreate(cmp_grgid); ! 747: if (grcache_byname == NULL) ! 748: grcache_byname = rbcreate(cmp_grnam); ! 749: if (grlist_cache == NULL) ! 750: grlist_cache = rbcreate(cmp_grnam); ! 751: } ! 752: ! 753: void ! 754: sudo_freegrcache(void) ! 755: { ! 756: if (grcache_bygid != NULL) { ! 757: rbdestroy(grcache_bygid, gr_delref_item); ! 758: grcache_bygid = NULL; ! 759: } ! 760: if (grcache_byname != NULL) { ! 761: rbdestroy(grcache_byname, gr_delref_item); ! 762: grcache_byname = NULL; ! 763: } ! 764: if (grlist_cache != NULL) { ! 765: rbdestroy(grlist_cache, grlist_delref_item); ! 766: grlist_cache = NULL; ! 767: } ! 768: } ! 769: ! 770: void ! 771: sudo_endgrent(void) ! 772: { ! 773: endgrent(); ! 774: sudo_freegrcache(); ! 775: } ! 776: ! 777: struct group_list * ! 778: get_group_list(struct passwd *pw) ! 779: { ! 780: struct cache_item key, *item; ! 781: struct rbnode *node; ! 782: size_t len; ! 783: GETGROUPS_T *gids; ! 784: int ngids; ! 785: ! 786: key.k.name = pw->pw_name; ! 787: if ((node = rbfind(grlist_cache, &key)) != NULL) { ! 788: item = (struct cache_item *) node->data; ! 789: goto done; ! 790: } ! 791: /* ! 792: * Cache group db entry if it exists or a negative response if not. ! 793: */ ! 794: #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX) ! 795: ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2; ! 796: if (ngids < 0) ! 797: #endif ! 798: ngids = NGROUPS_MAX * 2; ! 799: gids = emalloc2(ngids, sizeof(GETGROUPS_T)); ! 800: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) { ! 801: efree(gids); ! 802: gids = emalloc2(ngids, sizeof(GETGROUPS_T)); ! 803: if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) { ! 804: efree(gids); ! 805: return NULL; ! 806: } ! 807: } ! 808: if (ngids > 0) { ! 809: if ((item = make_grlist_item(pw->pw_name, gids, ngids)) == NULL) ! 810: errorx(1, "unable to parse group list for %s", pw->pw_name); ! 811: efree(gids); ! 812: if (rbinsert(grlist_cache, item) != NULL) ! 813: errorx(1, "unable to cache group list for %s, already exists", ! 814: pw->pw_name); ! 815: } else { ! 816: /* Should not happen. */ ! 817: len = strlen(pw->pw_name) + 1; ! 818: item = emalloc(sizeof(*item) + len); ! 819: item->refcnt = 1; ! 820: item->k.name = (char *) item + sizeof(*item); ! 821: memcpy(item->k.name, pw->pw_name, len); ! 822: item->d.grlist = NULL; ! 823: if (rbinsert(grlist_cache, item) != NULL) ! 824: errorx(1, "unable to cache group list for %s, already exists", ! 825: pw->pw_name); ! 826: } ! 827: done: ! 828: item->refcnt++; ! 829: return item->d.grlist; ! 830: } ! 831: ! 832: void ! 833: set_group_list(const char *user, GETGROUPS_T *gids, int ngids) ! 834: { ! 835: struct cache_item key, *item; ! 836: struct rbnode *node; ! 837: ! 838: /* ! 839: * Cache group db entry if it doesn't already exist ! 840: */ ! 841: key.k.name = (char *) user; ! 842: if ((node = rbfind(grlist_cache, &key)) == NULL) { ! 843: if ((item = make_grlist_item(user, gids, ngids)) == NULL) ! 844: errorx(1, "unable to parse group list for %s", user); ! 845: if (rbinsert(grlist_cache, item) != NULL) ! 846: errorx(1, "unable to cache group list for %s, already exists", ! 847: user); ! 848: } ! 849: } ! 850: ! 851: int ! 852: user_in_group(struct passwd *pw, const char *group) ! 853: { ! 854: struct group_list *grlist; ! 855: struct group *grp = NULL; ! 856: int i, matched = FALSE; ! 857: ! 858: if ((grlist = get_group_list(pw)) != NULL) { ! 859: /* ! 860: * If it could be a sudo-style group ID check gids first. ! 861: */ ! 862: if (group[0] == '#') { ! 863: gid_t gid = atoi(group + 1); ! 864: if (gid == pw->pw_gid) { ! 865: matched = TRUE; ! 866: goto done; ! 867: } ! 868: for (i = 0; i < grlist->ngids; i++) { ! 869: if (gid == grlist->gids[i]) { ! 870: matched = TRUE; ! 871: goto done; ! 872: } ! 873: } ! 874: } ! 875: ! 876: /* ! 877: * Next check the supplementary group vector. ! 878: * It usually includes the password db group too. ! 879: */ ! 880: for (i = 0; i < grlist->ngroups; i++) { ! 881: if (strcasecmp(group, grlist->groups[i]) == 0) { ! 882: matched = TRUE; ! 883: goto done; ! 884: } ! 885: } ! 886: ! 887: /* Finally check against user's primary (passwd file) group. */ ! 888: if ((grp = sudo_getgrgid(pw->pw_gid)) != NULL) { ! 889: if (strcasecmp(group, grp->gr_name) == 0) { ! 890: matched = TRUE; ! 891: goto done; ! 892: } ! 893: } ! 894: done: ! 895: if (grp != NULL) ! 896: gr_delref(grp); ! 897: grlist_delref(grlist); ! 898: } ! 899: return matched; ! 900: }