Annotation of embedaddon/sudo/plugins/sudoers/pwutil.c, revision 1.1.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>