[BACK]Return to pwutil.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

Annotation of src/usr.bin/sudo/pwutil.c, Revision 1.4

1.1       millert     1: /*
1.2       millert     2:  * Copyright (c) 1996, 1998-2005, 2007-2009
1.1       millert     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: #else
                     42: # ifdef HAVE_STRINGS_H
                     43: #  include <strings.h>
                     44: # endif
                     45: #endif /* HAVE_STRING_H */
                     46: #ifdef HAVE_UNISTD_H
                     47: # include <unistd.h>
                     48: #endif /* HAVE_UNISTD_H */
                     49: #include <pwd.h>
                     50: #include <grp.h>
                     51:
                     52: #include "sudo.h"
                     53: #include "redblack.h"
                     54:
                     55: #ifdef MYPW
                     56: extern void (*my_setgrent) __P((void));
                     57: extern void (*my_endgrent) __P((void));
                     58: extern struct group *(*my_getgrnam) __P((const char *));
                     59: extern struct group *(*my_getgrgid) __P((gid_t));
                     60: #define setgrent()     my_setgrent()
                     61: #define endgrent()     my_endgrent()
                     62: #define getgrnam(n)    my_getgrnam(n)
                     63: #define getgrgid(g)    my_getgrgid(g)
                     64:
                     65: extern void (*my_setpwent) __P((void));
                     66: extern void (*my_endpwent) __P((void));
                     67: extern struct passwd *(*my_getpwnam) __P((const char *));
                     68: extern struct passwd *(*my_getpwuid) __P((uid_t));
                     69: #define setpwent()     my_setpwent()
                     70: #define endpwent()     my_endpwent()
                     71: #define getpwnam(n)    my_getpwnam(n)
                     72: #define getpwuid(u)    my_getpwuid(u)
                     73: #endif
                     74:
                     75: /*
                     76:  * The passwd and group caches.
                     77:  */
                     78: static struct rbtree *pwcache_byuid, *pwcache_byname;
                     79: static struct rbtree *grcache_bygid, *grcache_byname;
                     80:
                     81: static int  cmp_pwuid  __P((const void *, const void *));
                     82: static int  cmp_pwnam  __P((const void *, const void *));
                     83: static int  cmp_grgid  __P((const void *, const void *));
                     84: static int  cmp_grnam  __P((const void *, const void *));
                     85:
                     86: /*
                     87:  * Compare by uid.
                     88:  */
                     89: static int
                     90: cmp_pwuid(v1, v2)
                     91:     const void *v1;
                     92:     const void *v2;
                     93: {
                     94:     const struct passwd *pw1 = (const struct passwd *) v1;
                     95:     const struct passwd *pw2 = (const struct passwd *) v2;
                     96:     return(pw1->pw_uid - pw2->pw_uid);
                     97: }
                     98:
                     99: /*
                    100:  * Compare by user name.
                    101:  */
                    102: static int
                    103: cmp_pwnam(v1, v2)
                    104:     const void *v1;
                    105:     const void *v2;
                    106: {
                    107:     const struct passwd *pw1 = (const struct passwd *) v1;
                    108:     const struct passwd *pw2 = (const struct passwd *) v2;
1.4     ! millert   109:     return(strcasecmp(pw1->pw_name, pw2->pw_name));
1.1       millert   110: }
                    111:
                    112: #define FIELD_SIZE(src, name, size)                    \
                    113: do {                                                   \
                    114:        if (src->name) {                                \
                    115:                size = strlen(src->name) + 1;           \
                    116:                total += size;                          \
                    117:        }                                               \
                    118: } while (0)
                    119:
                    120: #define FIELD_COPY(src, dst, name, size)               \
                    121: do {                                                   \
                    122:        if (src->name) {                                \
                    123:                memcpy(cp, src->name, size);            \
                    124:                dst->name = cp;                         \
                    125:                cp += size;                             \
                    126:        }                                               \
                    127: } while (0)
                    128:
                    129: /*
                    130:  * Dynamically allocate space for a struct password and the constituent parts
                    131:  * that we care about.  Fills in pw_passwd from shadow file.
                    132:  */
                    133: static struct passwd *
                    134: sudo_pwdup(pw)
                    135:     const struct passwd *pw;
                    136: {
                    137:     char *cp;
                    138:     const char *pw_shell;
                    139:     size_t nsize, psize, csize, gsize, dsize, ssize, total;
                    140:     struct passwd *newpw;
                    141:
                    142:     /* If shell field is empty, expand to _PATH_BSHELL. */
                    143:     pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
                    144:        ? _PATH_BSHELL : pw->pw_shell;
                    145:
                    146:     /* Allocate in one big chunk for easy freeing. */
                    147:     nsize = psize = csize = gsize = dsize = ssize = 0;
                    148:     total = sizeof(struct passwd);
                    149:     FIELD_SIZE(pw, pw_name, nsize);
                    150:     FIELD_SIZE(pw, pw_passwd, psize);
                    151: #ifdef HAVE_LOGIN_CAP_H
                    152:     FIELD_SIZE(pw, pw_class, csize);
                    153: #endif
                    154:     FIELD_SIZE(pw, pw_gecos, gsize);
                    155:     FIELD_SIZE(pw, pw_dir, dsize);
                    156:     FIELD_SIZE(pw, pw_shell, ssize);
                    157:
                    158:     if ((cp = malloc(total)) == NULL)
                    159:            return(NULL);
                    160:     newpw = (struct passwd *) cp;
                    161:
                    162:     /*
                    163:      * Copy in passwd contents and make strings relative to space
                    164:      * at the end of the buffer.
                    165:      */
                    166:     memcpy(newpw, pw, sizeof(struct passwd));
                    167:     cp += sizeof(struct passwd);
                    168:     FIELD_COPY(pw, newpw, pw_name, nsize);
                    169:     FIELD_COPY(pw, newpw, pw_passwd, psize);
                    170: #ifdef HAVE_LOGIN_CAP_H
                    171:     FIELD_COPY(pw, newpw, pw_class, csize);
                    172: #endif
                    173:     FIELD_COPY(pw, newpw, pw_gecos, gsize);
                    174:     FIELD_COPY(pw, newpw, pw_dir, dsize);
                    175:     FIELD_COPY(pw, newpw, pw_shell, ssize);
                    176:
                    177:     return(newpw);
                    178: }
                    179:
                    180: /*
                    181:  * Get a password entry by uid and allocate space for it.
                    182:  * Fills in pw_passwd from shadow file if necessary.
                    183:  */
                    184: struct passwd *
                    185: sudo_getpwuid(uid)
                    186:     uid_t uid;
                    187: {
                    188:     struct passwd key, *pw;
                    189:     struct rbnode *node;
                    190:     char *cp;
                    191:
                    192:     key.pw_uid = uid;
                    193:     if ((node = rbfind(pwcache_byuid, &key)) != NULL) {
                    194:        pw = (struct passwd *) node->data;
                    195:        return(pw->pw_name != NULL ? pw : NULL);
                    196:     }
                    197:     /*
                    198:      * Cache passwd db entry if it exists or a negative response if not.
                    199:      */
                    200:     if ((pw = getpwuid(uid)) != NULL) {
                    201:        pw = sudo_pwdup(pw);
                    202:        cp = sudo_getepw(pw);           /* get shadow password */
                    203:        if (pw->pw_passwd != NULL)
                    204:            zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
                    205:        pw->pw_passwd = cp;
                    206:        if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
1.2       millert   207:            errorx(1, "unable to cache uid %lu (%s), already exists",
                    208:                uid, pw->pw_name);
1.1       millert   209:        return(pw);
                    210:     } else {
                    211:        pw = emalloc(sizeof(*pw));
                    212:        zero_bytes(pw, sizeof(*pw));
                    213:        pw->pw_uid = uid;
                    214:        if (rbinsert(pwcache_byuid, (void *) pw) != NULL)
1.2       millert   215:            errorx(1, "unable to cache uid %lu, already exists", uid);
1.1       millert   216:        return(NULL);
                    217:     }
                    218: }
                    219:
                    220: /*
                    221:  * Get a password entry by name and allocate space for it.
                    222:  * Fills in pw_passwd from shadow file if necessary.
                    223:  */
                    224: struct passwd *
                    225: sudo_getpwnam(name)
                    226:     const char *name;
                    227: {
                    228:     struct passwd key, *pw;
                    229:     struct rbnode *node;
                    230:     size_t len;
                    231:     char *cp;
                    232:
                    233:     key.pw_name = (char *) name;
                    234:     if ((node = rbfind(pwcache_byname, &key)) != NULL) {
                    235:        pw = (struct passwd *) node->data;
                    236:        return(pw->pw_uid != (uid_t) -1 ? pw : NULL);
                    237:     }
                    238:     /*
                    239:      * Cache passwd db entry if it exists or a negative response if not.
                    240:      */
                    241:     if ((pw = getpwnam(name)) != NULL) {
                    242:        pw = sudo_pwdup(pw);
                    243:        cp = sudo_getepw(pw);           /* get shadow password */
                    244:        if (pw->pw_passwd != NULL)
                    245:            zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
                    246:        pw->pw_passwd = cp;
                    247:        if (rbinsert(pwcache_byname, (void *) pw) != NULL)
1.2       millert   248:            errorx(1, "unable to cache user %s, already exists", name);
1.1       millert   249:        return(pw);
                    250:     } else {
                    251:        len = strlen(name) + 1;
                    252:        cp = emalloc(sizeof(*pw) + len);
                    253:        zero_bytes(cp, sizeof(*pw));
                    254:        pw = (struct passwd *) cp;
                    255:        cp += sizeof(*pw);
                    256:        memcpy(cp, name, len);
                    257:        pw->pw_name = cp;
                    258:        pw->pw_uid = (uid_t) -1;
                    259:        if (rbinsert(pwcache_byname, (void *) pw) != NULL)
1.2       millert   260:            errorx(1, "unable to cache user %s, already exists", name);
1.1       millert   261:        return(NULL);
                    262:     }
                    263: }
                    264:
                    265: /*
                    266:  * Take a uid in string form "#123" and return a faked up passwd struct.
                    267:  */
                    268: struct passwd *
                    269: sudo_fakepwnam(user, gid)
                    270:     const char *user;
                    271:     gid_t gid;
                    272: {
                    273:     struct passwd *pw;
                    274:     struct rbnode *node;
                    275:     size_t len;
                    276:
                    277:     len = strlen(user);
                    278:     pw = emalloc(sizeof(struct passwd) + len + 1 /* pw_name */ +
                    279:        sizeof("*") /* pw_passwd */ + sizeof("") /* pw_gecos */ +
                    280:        sizeof("/") /* pw_dir */ + sizeof(_PATH_BSHELL));
                    281:     zero_bytes(pw, sizeof(struct passwd));
                    282:     pw->pw_uid = (uid_t) atoi(user + 1);
                    283:     pw->pw_gid = gid;
                    284:     pw->pw_name = (char *)pw + sizeof(struct passwd);
                    285:     memcpy(pw->pw_name, user, len + 1);
                    286:     pw->pw_passwd = pw->pw_name + len + 1;
                    287:     memcpy(pw->pw_passwd, "*", 2);
                    288:     pw->pw_gecos = pw->pw_passwd + 2;
                    289:     pw->pw_gecos[0] = '\0';
                    290:     pw->pw_dir = pw->pw_gecos + 1;
                    291:     memcpy(pw->pw_dir, "/", 2);
                    292:     pw->pw_shell = pw->pw_dir + 2;
                    293:     memcpy(pw->pw_shell, _PATH_BSHELL, sizeof(_PATH_BSHELL));
                    294:
                    295:     /* Store by uid and by name, overwriting cached version. */
                    296:     if ((node = rbinsert(pwcache_byuid, pw)) != NULL) {
                    297:        efree(node->data);
                    298:        node->data = (void *) pw;
                    299:     }
                    300:     if ((node = rbinsert(pwcache_byname, pw)) != NULL) {
                    301:        efree(node->data);
                    302:        node->data = (void *) pw;
                    303:     }
                    304:     return(pw);
                    305: }
                    306:
                    307: /*
                    308:  * Take a gid in string form "#123" and return a faked up group struct.
                    309:  */
                    310: struct group *
                    311: sudo_fakegrnam(group)
                    312:     const char *group;
                    313: {
                    314:     struct group *gr;
                    315:     struct rbnode *node;
                    316:     size_t len;
                    317:
                    318:     len = strlen(group);
                    319:     gr = emalloc(sizeof(struct group) + len + 1);
                    320:     zero_bytes(gr, sizeof(struct group));
                    321:     gr->gr_gid = (gid_t) atoi(group + 1);
                    322:     gr->gr_name = (char *)gr + sizeof(struct group);
                    323:     strlcpy(gr->gr_name, group, len + 1);
                    324:
                    325:     /* Store by gid and by name, overwriting cached version. */
                    326:     if ((node = rbinsert(grcache_bygid, gr)) != NULL) {
                    327:        efree(node->data);
                    328:        node->data = (void *) gr;
                    329:     }
                    330:     if ((node = rbinsert(grcache_byname, gr)) != NULL) {
                    331:        efree(node->data);
                    332:        node->data = (void *) gr;
                    333:     }
                    334:     return(gr);
                    335: }
                    336:
                    337: void
                    338: sudo_setpwent()
                    339: {
                    340:     setpwent();
                    341:     sudo_setspent();
                    342:     if (pwcache_byuid == NULL)
                    343:        pwcache_byuid = rbcreate(cmp_pwuid);
                    344:     if (pwcache_byname == NULL)
                    345:        pwcache_byname = rbcreate(cmp_pwnam);
                    346: }
                    347:
                    348: #ifdef PURIFY
                    349: static void pw_free    __P((void *));
                    350:
                    351: void
                    352: sudo_freepwcache()
                    353: {
                    354:     if (pwcache_byuid != NULL) {
                    355:        rbdestroy(pwcache_byuid, pw_free);
                    356:        pwcache_byuid = NULL;
                    357:     }
                    358:     if (pwcache_byname != NULL) {
                    359:        rbdestroy(pwcache_byname, NULL);
                    360:        pwcache_byname = NULL;
                    361:     }
                    362: }
                    363:
                    364: static void
                    365: pw_free(v)
                    366:     void *v;
                    367: {
                    368:     struct passwd *pw = (struct passwd *) v;
                    369:
                    370:     if (pw->pw_passwd != NULL) {
                    371:        zero_bytes(pw->pw_passwd, strlen(pw->pw_passwd));
                    372:        efree(pw->pw_passwd);
                    373:     }
                    374:     efree(pw);
                    375: }
                    376: #endif /* PURIFY */
                    377:
                    378: void
                    379: sudo_endpwent()
                    380: {
                    381:     endpwent();
                    382:     sudo_endspent();
                    383: #ifdef PURIFY
                    384:     sudo_freepwcache();
                    385: #endif
                    386: }
                    387:
                    388: /*
                    389:  * Compare by gid.
                    390:  */
                    391: static int
                    392: cmp_grgid(v1, v2)
                    393:     const void *v1;
                    394:     const void *v2;
                    395: {
                    396:     const struct group *grp1 = (const struct group *) v1;
                    397:     const struct group *grp2 = (const struct group *) v2;
                    398:     return(grp1->gr_gid - grp2->gr_gid);
                    399: }
                    400:
                    401: /*
                    402:  * Compare by group name.
                    403:  */
                    404: static int
                    405: cmp_grnam(v1, v2)
                    406:     const void *v1;
                    407:     const void *v2;
                    408: {
                    409:     const struct group *grp1 = (const struct group *) v1;
                    410:     const struct group *grp2 = (const struct group *) v2;
1.4     ! millert   411:     return(strcasecmp(grp1->gr_name, grp2->gr_name));
1.1       millert   412: }
                    413:
                    414: struct group *
                    415: sudo_grdup(gr)
                    416:     const struct group *gr;
                    417: {
                    418:     char *cp;
                    419:     size_t nsize, psize, nmem, total, len;
                    420:     struct group *newgr;
                    421:
                    422:     /* Allocate in one big chunk for easy freeing. */
                    423:     nsize = psize = nmem = 0;
                    424:     total = sizeof(struct group);
                    425:     FIELD_SIZE(gr, gr_name, nsize);
                    426:     FIELD_SIZE(gr, gr_passwd, psize);
                    427:     if (gr->gr_mem) {
                    428:        for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
                    429:            total += strlen(gr->gr_mem[nmem]) + 1;
                    430:        nmem++;
                    431:        total += sizeof(char *) * nmem;
                    432:     }
                    433:     if ((cp = malloc(total)) == NULL)
                    434:            return(NULL);
                    435:     newgr = (struct group *)cp;
                    436:
                    437:     /*
                    438:      * Copy in group contents and make strings relative to space
                    439:      * at the end of the buffer.  Note that gr_mem must come
                    440:      * immediately after struct group to guarantee proper alignment.
                    441:      */
                    442:     (void)memcpy(newgr, gr, sizeof(struct group));
                    443:     cp += sizeof(struct group);
                    444:     if (gr->gr_mem) {
                    445:        newgr->gr_mem = (char **)cp;
                    446:        cp += sizeof(char *) * nmem;
                    447:        for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
                    448:            len = strlen(gr->gr_mem[nmem]) + 1;
                    449:            memcpy(cp, gr->gr_mem[nmem], len);
                    450:            newgr->gr_mem[nmem] = cp;
                    451:            cp += len;
                    452:        }
                    453:        newgr->gr_mem[nmem] = NULL;
                    454:     }
                    455:     FIELD_COPY(gr, newgr, gr_passwd, psize);
                    456:     FIELD_COPY(gr, newgr, gr_name, nsize);
                    457:
                    458:     return(newgr);
                    459: }
                    460:
                    461: /*
                    462:  * Get a group entry by gid and allocate space for it.
                    463:  */
                    464: struct group *
                    465: sudo_getgrgid(gid)
                    466:     gid_t gid;
                    467: {
                    468:     struct group key, *gr;
                    469:     struct rbnode *node;
                    470:
                    471:     key.gr_gid = gid;
                    472:     if ((node = rbfind(grcache_bygid, &key)) != NULL) {
                    473:        gr = (struct group *) node->data;
                    474:        return(gr->gr_name != NULL ? gr : NULL);
                    475:     }
                    476:     /*
                    477:      * Cache group db entry if it exists or a negative response if not.
                    478:      */
                    479:     if ((gr = getgrgid(gid)) != NULL) {
                    480:        gr = sudo_grdup(gr);
                    481:        if (rbinsert(grcache_bygid, (void *) gr) != NULL)
1.2       millert   482:            errorx(1, "unable to cache gid %lu (%s), already exists",
                    483:                gid, gr->gr_name);
1.1       millert   484:        return(gr);
                    485:     } else {
                    486:        gr = emalloc(sizeof(*gr));
                    487:        zero_bytes(gr, sizeof(*gr));
                    488:        gr->gr_gid = gid;
                    489:        if (rbinsert(grcache_bygid, (void *) gr) != NULL)
1.2       millert   490:            errorx(1, "unable to cache gid %lu, already exists, gid");
1.1       millert   491:        return(NULL);
                    492:     }
                    493: }
                    494:
                    495: /*
                    496:  * Get a group entry by name and allocate space for it.
                    497:  */
                    498: struct group *
                    499: sudo_getgrnam(name)
                    500:     const char *name;
                    501: {
                    502:     struct group key, *gr;
                    503:     struct rbnode *node;
                    504:     size_t len;
                    505:     char *cp;
                    506:
                    507:     key.gr_name = (char *) name;
                    508:     if ((node = rbfind(grcache_byname, &key)) != NULL) {
                    509:        gr = (struct group *) node->data;
                    510:        return(gr->gr_gid != (gid_t) -1 ? gr : NULL);
                    511:     }
                    512:     /*
                    513:      * Cache group db entry if it exists or a negative response if not.
                    514:      */
                    515:     if ((gr = getgrnam(name)) != NULL) {
                    516:        gr = sudo_grdup(gr);
                    517:        if (rbinsert(grcache_byname, (void *) gr) != NULL)
1.2       millert   518:            errorx(1, "unable to cache group %s, already exists", name);
1.1       millert   519:        return(gr);
                    520:     } else {
                    521:        len = strlen(name) + 1;
                    522:        cp = emalloc(sizeof(*gr) + len);
                    523:        zero_bytes(cp, sizeof(*gr));
                    524:        gr = (struct group *) cp;
                    525:        cp += sizeof(*gr);
                    526:        memcpy(cp, name, len);
                    527:        gr->gr_name = cp;
                    528:        gr->gr_gid = (gid_t) -1;
                    529:        if (rbinsert(grcache_byname, (void *) gr) != NULL)
1.2       millert   530:            errorx(1, "unable to cache group %s, already exists", name);
1.1       millert   531:        return(NULL);
                    532:     }
                    533: }
                    534:
                    535: void
                    536: sudo_setgrent()
                    537: {
                    538:     setgrent();
                    539:     if (grcache_bygid == NULL)
                    540:        grcache_bygid = rbcreate(cmp_grgid);
                    541:     if (grcache_byname == NULL)
                    542:        grcache_byname = rbcreate(cmp_grnam);
                    543: }
                    544:
                    545: #ifdef PURIFY
                    546: void
                    547: sudo_freegrcache()
                    548: {
                    549:     if (grcache_bygid != NULL) {
                    550:        rbdestroy(grcache_bygid, free);
                    551:        grcache_bygid = NULL;
                    552:     }
                    553:     if (grcache_byname != NULL) {
                    554:        rbdestroy(grcache_byname, NULL);
                    555:        grcache_byname = NULL;
                    556:     }
                    557: }
                    558: #endif /* PURIFY */
                    559:
                    560: void
                    561: sudo_endgrent()
                    562: {
                    563:     endgrent();
                    564: #ifdef PURIFY
                    565:     sudo_freegrcache();
                    566: #endif
                    567: }