Annotation of embedaddon/sudo/plugins/sudoers/pwutil.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>