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

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

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