Annotation of embedaddon/sudo/plugins/sudoers/pwutil.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>