Annotation of embedaddon/sudo/plugins/sudoers/match.c, revision 1.1.1.2

1.1       misho       1: /*
1.1.1.2 ! 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:  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
                     17:  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     18:  *
                     19:  * Sponsored in part by the Defense Advanced Research Projects
                     20:  * Agency (DARPA) and Air Force Research Laboratory, Air Force
                     21:  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
                     22:  */
                     23: 
                     24: #include <config.h>
                     25: 
                     26: #include <sys/types.h>
                     27: #include <sys/param.h>
                     28: #include <sys/stat.h>
                     29: #include <stdio.h>
                     30: #ifdef STDC_HEADERS
                     31: # include <stdlib.h>
                     32: # include <stddef.h>
                     33: #else
                     34: # ifdef HAVE_STDLIB_H
                     35: #  include <stdlib.h>
                     36: # endif
                     37: #endif /* STDC_HEADERS */
                     38: #ifdef HAVE_STRING_H
                     39: # include <string.h>
                     40: #endif /* HAVE_STRING_H */
                     41: #ifdef HAVE_STRINGS_H
                     42: # include <strings.h>
                     43: #endif /* HAVE_STRINGS_H */
                     44: #ifdef HAVE_UNISTD_H
                     45: # include <unistd.h>
                     46: #endif /* HAVE_UNISTD_H */
                     47: #ifdef HAVE_FNMATCH
                     48: # include <fnmatch.h>
                     49: #endif /* HAVE_FNMATCH */
1.1.1.2 ! misho      50: #ifdef HAVE_GLOB
1.1       misho      51: # include <glob.h>
1.1.1.2 ! misho      52: #endif /* HAVE_GLOB */
1.1       misho      53: #ifdef HAVE_NETGROUP_H
                     54: # include <netgroup.h>
                     55: #endif /* HAVE_NETGROUP_H */
                     56: #include <ctype.h>
                     57: #include <pwd.h>
                     58: #include <grp.h>
                     59: #include <netdb.h>
                     60: #ifdef HAVE_DIRENT_H
                     61: # include <dirent.h>
                     62: # define NAMLEN(dirent) strlen((dirent)->d_name)
                     63: #else
                     64: # define dirent direct
                     65: # define NAMLEN(dirent) (dirent)->d_namlen
                     66: # ifdef HAVE_SYS_NDIR_H
                     67: #  include <sys/ndir.h>
                     68: # endif
                     69: # ifdef HAVE_SYS_DIR_H
                     70: #  include <sys/dir.h>
                     71: # endif
                     72: # ifdef HAVE_NDIR_H
                     73: #  include <ndir.h>
                     74: # endif
                     75: #endif
                     76: 
                     77: #include "sudoers.h"
                     78: #include "parse.h"
                     79: #include <gram.h>
                     80: 
                     81: #ifndef HAVE_FNMATCH
                     82: # include "compat/fnmatch.h"
                     83: #endif /* HAVE_FNMATCH */
1.1.1.2 ! misho      84: #ifndef HAVE_GLOB
1.1       misho      85: # include "compat/glob.h"
1.1.1.2 ! misho      86: #endif /* HAVE_GLOB */
1.1       misho      87: 
                     88: static struct member_list empty;
                     89: 
1.1.1.2 ! misho      90: static bool command_matches_dir(char *, size_t);
        !            91: static bool command_matches_glob(char *, char *);
        !            92: static bool command_matches_fnmatch(char *, char *);
        !            93: static bool command_matches_normal(char *, char *);
1.1       misho      94: 
                     95: /*
1.1.1.2 ! misho      96:  * Returns true if string 's' contains meta characters.
1.1       misho      97:  */
                     98: #define has_meta(s)    (strpbrk(s, "\\?*[]") != NULL)
                     99: 
                    100: /*
                    101:  * Check for user described by pw in a list of members.
                    102:  * Returns ALLOW, DENY or UNSPEC.
                    103:  */
                    104: static int
                    105: _userlist_matches(struct passwd *pw, struct member_list *list)
                    106: {
                    107:     struct member *m;
                    108:     struct alias *a;
                    109:     int rval, matched = UNSPEC;
1.1.1.2 ! misho     110:     debug_decl(_userlist_matches, SUDO_DEBUG_MATCH)
1.1       misho     111: 
                    112:     tq_foreach_rev(list, m) {
                    113:        switch (m->type) {
                    114:            case ALL:
                    115:                matched = !m->negated;
                    116:                break;
                    117:            case NETGROUP:
                    118:                if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
                    119:                    matched = !m->negated;
                    120:                break;
                    121:            case USERGROUP:
                    122:                if (usergr_matches(m->name, pw->pw_name, pw))
                    123:                    matched = !m->negated;
                    124:                break;
                    125:            case ALIAS:
                    126:                if ((a = alias_find(m->name, USERALIAS)) != NULL) {
                    127:                    rval = _userlist_matches(pw, &a->members);
                    128:                    if (rval != UNSPEC)
                    129:                        matched = m->negated ? !rval : rval;
                    130:                    break;
                    131:                }
                    132:                /* FALLTHROUGH */
                    133:            case WORD:
                    134:                if (userpw_matches(m->name, pw->pw_name, pw))
                    135:                    matched = !m->negated;
                    136:                break;
                    137:        }
                    138:        if (matched != UNSPEC)
                    139:            break;
                    140:     }
1.1.1.2 ! misho     141:     debug_return_bool(matched);
1.1       misho     142: }
                    143: 
                    144: int
                    145: userlist_matches(struct passwd *pw, struct member_list *list)
                    146: {
                    147:     alias_seqno++;
                    148:     return _userlist_matches(pw, list);
                    149: }
                    150: 
                    151: /*
                    152:  * Check for user described by pw in a list of members.
                    153:  * If both lists are empty compare against def_runas_default.
                    154:  * Returns ALLOW, DENY or UNSPEC.
                    155:  */
                    156: static int
                    157: _runaslist_matches(struct member_list *user_list, struct member_list *group_list)
                    158: {
                    159:     struct member *m;
                    160:     struct alias *a;
                    161:     int rval;
                    162:     int user_matched = UNSPEC;
                    163:     int group_matched = UNSPEC;
1.1.1.2 ! misho     164:     debug_decl(_runaslist_matches, SUDO_DEBUG_MATCH)
1.1       misho     165: 
                    166:     if (runas_pw != NULL) {
                    167:        /* If no runas user or runas group listed in sudoers, use default. */
                    168:        if (tq_empty(user_list) && tq_empty(group_list))
1.1.1.2 ! misho     169:            debug_return_int(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
1.1       misho     170: 
                    171:        tq_foreach_rev(user_list, m) {
                    172:            switch (m->type) {
                    173:                case ALL:
                    174:                    user_matched = !m->negated;
                    175:                    break;
                    176:                case NETGROUP:
                    177:                    if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
                    178:                        user_matched = !m->negated;
                    179:                    break;
                    180:                case USERGROUP:
                    181:                    if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
                    182:                        user_matched = !m->negated;
                    183:                    break;
                    184:                case ALIAS:
                    185:                    if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
                    186:                        rval = _runaslist_matches(&a->members, &empty);
                    187:                        if (rval != UNSPEC)
                    188:                            user_matched = m->negated ? !rval : rval;
                    189:                        break;
                    190:                    }
                    191:                    /* FALLTHROUGH */
                    192:                case WORD:
                    193:                    if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
                    194:                        user_matched = !m->negated;
                    195:                    break;
                    196:            }
                    197:            if (user_matched != UNSPEC)
                    198:                break;
                    199:        }
                    200:     }
                    201: 
                    202:     if (runas_gr != NULL) {
                    203:        if (user_matched == UNSPEC) {
                    204:            if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0)
                    205:                user_matched = ALLOW;   /* only changing group */
                    206:        }
                    207:        tq_foreach_rev(group_list, m) {
                    208:            switch (m->type) {
                    209:                case ALL:
                    210:                    group_matched = !m->negated;
                    211:                    break;
                    212:                case ALIAS:
                    213:                    if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
                    214:                        rval = _runaslist_matches(&empty, &a->members);
                    215:                        if (rval != UNSPEC)
                    216:                            group_matched = m->negated ? !rval : rval;
                    217:                        break;
                    218:                    }
                    219:                    /* FALLTHROUGH */
                    220:                case WORD:
                    221:                    if (group_matches(m->name, runas_gr))
                    222:                        group_matched = !m->negated;
                    223:                    break;
                    224:            }
                    225:            if (group_matched != UNSPEC)
                    226:                break;
                    227:        }
                    228:        if (group_matched == UNSPEC) {
                    229:            if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid)
                    230:                group_matched = ALLOW;  /* runas group matches passwd db */
                    231:        }
                    232:     }
                    233: 
                    234:     if (user_matched == DENY || group_matched == DENY)
1.1.1.2 ! misho     235:        debug_return_int(DENY);
1.1       misho     236:     if (user_matched == group_matched || runas_gr == NULL)
1.1.1.2 ! misho     237:        debug_return_int(user_matched);
        !           238:     debug_return_int(UNSPEC);
1.1       misho     239: }
                    240: 
                    241: int
                    242: runaslist_matches(struct member_list *user_list, struct member_list *group_list)
                    243: {
                    244:     alias_seqno++;
                    245:     return _runaslist_matches(user_list ? user_list : &empty,
                    246:        group_list ? group_list : &empty);
                    247: }
                    248: 
                    249: /*
                    250:  * Check for host and shost in a list of members.
                    251:  * Returns ALLOW, DENY or UNSPEC.
                    252:  */
                    253: static int
                    254: _hostlist_matches(struct member_list *list)
                    255: {
                    256:     struct member *m;
                    257:     struct alias *a;
                    258:     int rval, matched = UNSPEC;
1.1.1.2 ! misho     259:     debug_decl(_hostlist_matches, SUDO_DEBUG_MATCH)
1.1       misho     260: 
                    261:     tq_foreach_rev(list, m) {
                    262:        switch (m->type) {
                    263:            case ALL:
                    264:                matched = !m->negated;
                    265:                break;
                    266:            case NETGROUP:
                    267:                if (netgr_matches(m->name, user_host, user_shost, NULL))
                    268:                    matched = !m->negated;
                    269:                break;
                    270:            case NTWKADDR:
                    271:                if (addr_matches(m->name))
                    272:                    matched = !m->negated;
                    273:                break;
                    274:            case ALIAS:
                    275:                if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
                    276:                    rval = _hostlist_matches(&a->members);
                    277:                    if (rval != UNSPEC)
                    278:                        matched = m->negated ? !rval : rval;
                    279:                    break;
                    280:                }
                    281:                /* FALLTHROUGH */
                    282:            case WORD:
                    283:                if (hostname_matches(user_shost, user_host, m->name))
                    284:                    matched = !m->negated;
                    285:                break;
                    286:        }
                    287:        if (matched != UNSPEC)
                    288:            break;
                    289:     }
1.1.1.2 ! misho     290:     debug_return_bool(matched);
1.1       misho     291: }
                    292: 
                    293: int
                    294: hostlist_matches(struct member_list *list)
                    295: {
                    296:     alias_seqno++;
                    297:     return _hostlist_matches(list);
                    298: }
                    299: 
                    300: /*
                    301:  * Check for cmnd and args in a list of members.
                    302:  * Returns ALLOW, DENY or UNSPEC.
                    303:  */
                    304: static int
                    305: _cmndlist_matches(struct member_list *list)
                    306: {
                    307:     struct member *m;
                    308:     int matched = UNSPEC;
1.1.1.2 ! misho     309:     debug_decl(_cmndlist_matches, SUDO_DEBUG_MATCH)
1.1       misho     310: 
                    311:     tq_foreach_rev(list, m) {
                    312:        matched = cmnd_matches(m);
                    313:        if (matched != UNSPEC)
                    314:            break;
                    315:     }
1.1.1.2 ! misho     316:     debug_return_bool(matched);
1.1       misho     317: }
                    318: 
                    319: int
                    320: cmndlist_matches(struct member_list *list)
                    321: {
                    322:     alias_seqno++;
                    323:     return _cmndlist_matches(list);
                    324: }
                    325: 
                    326: /*
                    327:  * Check cmnd and args.
                    328:  * Returns ALLOW, DENY or UNSPEC.
                    329:  */
                    330: int
                    331: cmnd_matches(struct member *m)
                    332: {
                    333:     struct alias *a;
                    334:     struct sudo_command *c;
                    335:     int rval, matched = UNSPEC;
1.1.1.2 ! misho     336:     debug_decl(cmnd_matches, SUDO_DEBUG_MATCH)
1.1       misho     337: 
                    338:     switch (m->type) {
                    339:        case ALL:
                    340:            matched = !m->negated;
                    341:            break;
                    342:        case ALIAS:
                    343:            alias_seqno++;
                    344:            if ((a = alias_find(m->name, CMNDALIAS)) != NULL) {
                    345:                rval = _cmndlist_matches(&a->members);
                    346:                if (rval != UNSPEC)
                    347:                    matched = m->negated ? !rval : rval;
                    348:            }
                    349:            break;
                    350:        case COMMAND:
                    351:            c = (struct sudo_command *)m->name;
                    352:            if (command_matches(c->cmnd, c->args))
                    353:                matched = !m->negated;
                    354:            break;
                    355:     }
1.1.1.2 ! misho     356:     debug_return_bool(matched);
1.1       misho     357: }
                    358: 
1.1.1.2 ! misho     359: static bool
1.1       misho     360: command_args_match(sudoers_cmnd, sudoers_args)
                    361:     char *sudoers_cmnd;
                    362:     char *sudoers_args;
                    363: {
                    364:     int flags = 0;
1.1.1.2 ! misho     365:     debug_decl(command_args_match, SUDO_DEBUG_MATCH)
1.1       misho     366: 
                    367:     /*
                    368:      * If no args specified in sudoers, any user args are allowed.
                    369:      * If the empty string is specified in sudoers, no user args are allowed.
                    370:      */
                    371:     if (!sudoers_args ||
                    372:        (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)))
1.1.1.2 ! misho     373:        debug_return_bool(true);
1.1       misho     374:     /*
                    375:      * If args are specified in sudoers, they must match the user args.
                    376:      * If running as sudoedit, all args are assumed to be paths.
                    377:      */
                    378:     if (sudoers_args) {
                    379:        /* For sudoedit, all args are assumed to be pathnames. */
                    380:        if (strcmp(sudoers_cmnd, "sudoedit") == 0)
                    381:            flags = FNM_PATHNAME;
                    382:        if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
1.1.1.2 ! misho     383:            debug_return_bool(true);
1.1       misho     384:     }
1.1.1.2 ! misho     385:     debug_return_bool(false);
1.1       misho     386: }
                    387: 
                    388: /*
1.1.1.2 ! misho     389:  * If path doesn't end in /, return true iff cmnd & path name the same inode;
        !           390:  * otherwise, return true if user_cmnd names one of the inodes in path.
1.1       misho     391:  */
1.1.1.2 ! misho     392: bool
1.1       misho     393: command_matches(char *sudoers_cmnd, char *sudoers_args)
                    394: {
1.1.1.2 ! misho     395:     debug_decl(command_matches, SUDO_DEBUG_MATCH)
        !           396: 
1.1       misho     397:     /* Check for pseudo-commands */
                    398:     if (sudoers_cmnd[0] != '/') {
                    399:        /*
                    400:         * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
                    401:         *  a) there are no args in sudoers OR
                    402:         *  b) there are no args on command line and none req by sudoers OR
                    403:         *  c) there are args in sudoers and on command line and they match
                    404:         */
                    405:        if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
                    406:            strcmp(user_cmnd, "sudoedit") != 0)
1.1.1.2 ! misho     407:            debug_return_bool(false);
1.1       misho     408:        if (command_args_match(sudoers_cmnd, sudoers_args)) {
                    409:            efree(safe_cmnd);
                    410:            safe_cmnd = estrdup(sudoers_cmnd);
1.1.1.2 ! misho     411:            debug_return_bool(true);
1.1       misho     412:        } else
1.1.1.2 ! misho     413:            debug_return_bool(false);
1.1       misho     414:     }
                    415: 
                    416:     if (has_meta(sudoers_cmnd)) {
                    417:        /*
                    418:         * If sudoers_cmnd has meta characters in it, we need to
                    419:         * use glob(3) and/or fnmatch(3) to do the matching.
                    420:         */
                    421:        if (def_fast_glob)
1.1.1.2 ! misho     422:            debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
        !           423:        debug_return_bool(command_matches_glob(sudoers_cmnd, sudoers_args));
1.1       misho     424:     }
1.1.1.2 ! misho     425:     debug_return_bool(command_matches_normal(sudoers_cmnd, sudoers_args));
1.1       misho     426: }
                    427: 
1.1.1.2 ! misho     428: static bool
1.1       misho     429: command_matches_fnmatch(char *sudoers_cmnd, char *sudoers_args)
                    430: {
1.1.1.2 ! misho     431:     debug_decl(command_matches_fnmatch, SUDO_DEBUG_MATCH)
        !           432: 
1.1       misho     433:     /*
                    434:      * Return true if fnmatch(3) succeeds AND
                    435:      *  a) there are no args in sudoers OR
                    436:      *  b) there are no args on command line and none required by sudoers OR
                    437:      *  c) there are args in sudoers and on command line and they match
                    438:      * else return false.
                    439:      */
                    440:     if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
1.1.1.2 ! misho     441:        debug_return_bool(false);
1.1       misho     442:     if (command_args_match(sudoers_cmnd, sudoers_args)) {
                    443:        if (safe_cmnd)
                    444:            free(safe_cmnd);
                    445:        safe_cmnd = estrdup(user_cmnd);
1.1.1.2 ! misho     446:        debug_return_bool(true);
        !           447:     }
        !           448:     debug_return_bool(false);
1.1       misho     449: }
                    450: 
1.1.1.2 ! misho     451: static bool
1.1       misho     452: command_matches_glob(char *sudoers_cmnd, char *sudoers_args)
                    453: {
                    454:     struct stat sudoers_stat;
                    455:     size_t dlen;
                    456:     char **ap, *base, *cp;
                    457:     glob_t gl;
1.1.1.2 ! misho     458:     debug_decl(command_matches_glob, SUDO_DEBUG_MATCH)
1.1       misho     459: 
                    460:     /*
                    461:      * First check to see if we can avoid the call to glob(3).
                    462:      * Short circuit if there are no meta chars in the command itself
                    463:      * and user_base and basename(sudoers_cmnd) don't match.
                    464:      */
                    465:     dlen = strlen(sudoers_cmnd);
                    466:     if (sudoers_cmnd[dlen - 1] != '/') {
                    467:        if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
                    468:            base++;
                    469:            if (!has_meta(base) && strcmp(user_base, base) != 0)
1.1.1.2 ! misho     470:                debug_return_bool(false);
1.1       misho     471:        }
                    472:     }
                    473:     /*
                    474:      * Return true if we find a match in the glob(3) results AND
                    475:      *  a) there are no args in sudoers OR
                    476:      *  b) there are no args on command line and none required by sudoers OR
                    477:      *  c) there are args in sudoers and on command line and they match
                    478:      * else return false.
                    479:      */
1.1.1.2 ! misho     480:     if (glob(sudoers_cmnd, GLOB_NOSORT, NULL, &gl) != 0 || gl.gl_pathc == 0) {
1.1       misho     481:        globfree(&gl);
1.1.1.2 ! misho     482:        debug_return_bool(false);
1.1       misho     483:     }
                    484:     /* For each glob match, compare basename, st_dev and st_ino. */
                    485:     for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
                    486:        /* If it ends in '/' it is a directory spec. */
                    487:        dlen = strlen(cp);
                    488:        if (cp[dlen - 1] == '/') {
                    489:            if (command_matches_dir(cp, dlen))
1.1.1.2 ! misho     490:                debug_return_bool(true);
1.1       misho     491:            continue;
                    492:        }
                    493: 
                    494:        /* Only proceed if user_base and basename(cp) match */
                    495:        if ((base = strrchr(cp, '/')) != NULL)
                    496:            base++;
                    497:        else
                    498:            base = cp;
                    499:        if (strcmp(user_base, base) != 0 ||
                    500:            stat(cp, &sudoers_stat) == -1)
                    501:            continue;
                    502:        if (user_stat == NULL ||
                    503:            (user_stat->st_dev == sudoers_stat.st_dev &&
                    504:            user_stat->st_ino == sudoers_stat.st_ino)) {
                    505:            efree(safe_cmnd);
                    506:            safe_cmnd = estrdup(cp);
                    507:            break;
                    508:        }
                    509:     }
                    510:     globfree(&gl);
                    511:     if (cp == NULL)
1.1.1.2 ! misho     512:        debug_return_bool(false);
1.1       misho     513: 
                    514:     if (command_args_match(sudoers_cmnd, sudoers_args)) {
                    515:        efree(safe_cmnd);
                    516:        safe_cmnd = estrdup(user_cmnd);
1.1.1.2 ! misho     517:        debug_return_bool(true);
1.1       misho     518:     }
1.1.1.2 ! misho     519:     debug_return_bool(false);
1.1       misho     520: }
                    521: 
1.1.1.2 ! misho     522: static bool
1.1       misho     523: command_matches_normal(char *sudoers_cmnd, char *sudoers_args)
                    524: {
                    525:     struct stat sudoers_stat;
                    526:     char *base;
                    527:     size_t dlen;
1.1.1.2 ! misho     528:     debug_decl(command_matches_normal, SUDO_DEBUG_MATCH)
1.1       misho     529: 
                    530:     /* If it ends in '/' it is a directory spec. */
                    531:     dlen = strlen(sudoers_cmnd);
                    532:     if (sudoers_cmnd[dlen - 1] == '/')
1.1.1.2 ! misho     533:        debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
1.1       misho     534: 
                    535:     /* Only proceed if user_base and basename(sudoers_cmnd) match */
                    536:     if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
                    537:        base = sudoers_cmnd;
                    538:     else
                    539:        base++;
                    540:     if (strcmp(user_base, base) != 0 ||
                    541:        stat(sudoers_cmnd, &sudoers_stat) == -1)
1.1.1.2 ! misho     542:        debug_return_bool(false);
1.1       misho     543: 
                    544:     /*
                    545:      * Return true if inode/device matches AND
                    546:      *  a) there are no args in sudoers OR
                    547:      *  b) there are no args on command line and none req by sudoers OR
                    548:      *  c) there are args in sudoers and on command line and they match
                    549:      */
                    550:     if (user_stat != NULL &&
                    551:        (user_stat->st_dev != sudoers_stat.st_dev ||
                    552:        user_stat->st_ino != sudoers_stat.st_ino))
1.1.1.2 ! misho     553:        debug_return_bool(false);
1.1       misho     554:     if (command_args_match(sudoers_cmnd, sudoers_args)) {
                    555:        efree(safe_cmnd);
                    556:        safe_cmnd = estrdup(sudoers_cmnd);
1.1.1.2 ! misho     557:        debug_return_bool(true);
1.1       misho     558:     }
1.1.1.2 ! misho     559:     debug_return_bool(false);
1.1       misho     560: }
                    561: 
                    562: /*
1.1.1.2 ! misho     563:  * Return true if user_cmnd names one of the inodes in dir, else false.
1.1       misho     564:  */
1.1.1.2 ! misho     565: static bool
1.1       misho     566: command_matches_dir(char *sudoers_dir, size_t dlen)
                    567: {
                    568:     struct stat sudoers_stat;
                    569:     struct dirent *dent;
                    570:     char buf[PATH_MAX];
                    571:     DIR *dirp;
1.1.1.2 ! misho     572:     debug_decl(command_matches_dir, SUDO_DEBUG_MATCH)
1.1       misho     573: 
                    574:     /*
                    575:      * Grot through directory entries, looking for user_base.
                    576:      */
                    577:     dirp = opendir(sudoers_dir);
                    578:     if (dirp == NULL)
1.1.1.2 ! misho     579:        debug_return_bool(false);
1.1       misho     580: 
                    581:     if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
                    582:        closedir(dirp);
1.1.1.2 ! misho     583:        debug_return_bool(false);
1.1       misho     584:     }
                    585:     while ((dent = readdir(dirp)) != NULL) {
                    586:        /* ignore paths > PATH_MAX (XXX - log) */
                    587:        buf[dlen] = '\0';
                    588:        if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
                    589:            continue;
                    590: 
                    591:        /* only stat if basenames are the same */
                    592:        if (strcmp(user_base, dent->d_name) != 0 ||
                    593:            stat(buf, &sudoers_stat) == -1)
                    594:            continue;
                    595:        if (user_stat == NULL ||
                    596:            (user_stat->st_dev == sudoers_stat.st_dev &&
                    597:            user_stat->st_ino == sudoers_stat.st_ino)) {
                    598:            efree(safe_cmnd);
                    599:            safe_cmnd = estrdup(buf);
                    600:            break;
                    601:        }
                    602:     }
                    603: 
                    604:     closedir(dirp);
1.1.1.2 ! misho     605:     debug_return_bool(dent != NULL);
1.1       misho     606: }
                    607: 
                    608: /*
1.1.1.2 ! misho     609:  * Returns true if the hostname matches the pattern, else false
1.1       misho     610:  */
1.1.1.2 ! misho     611: bool
1.1       misho     612: hostname_matches(char *shost, char *lhost, char *pattern)
                    613: {
1.1.1.2 ! misho     614:     debug_decl(hostname_matches, SUDO_DEBUG_MATCH)
        !           615: 
1.1       misho     616:     if (has_meta(pattern)) {
                    617:        if (strchr(pattern, '.'))
1.1.1.2 ! misho     618:            debug_return_bool(!fnmatch(pattern, lhost, FNM_CASEFOLD));
1.1       misho     619:        else
1.1.1.2 ! misho     620:            debug_return_bool(!fnmatch(pattern, shost, FNM_CASEFOLD));
1.1       misho     621:     } else {
                    622:        if (strchr(pattern, '.'))
1.1.1.2 ! misho     623:            debug_return_bool(!strcasecmp(lhost, pattern));
1.1       misho     624:        else
1.1.1.2 ! misho     625:            debug_return_bool(!strcasecmp(shost, pattern));
1.1       misho     626:     }
                    627: }
                    628: 
                    629: /*
1.1.1.2 ! misho     630:  *  Returns true if the user/uid from sudoers matches the specified user/uid,
        !           631:  *  else returns false.
1.1       misho     632:  */
1.1.1.2 ! misho     633: bool
1.1       misho     634: userpw_matches(char *sudoers_user, char *user, struct passwd *pw)
                    635: {
1.1.1.2 ! misho     636:     debug_decl(userpw_matches, SUDO_DEBUG_MATCH)
        !           637: 
1.1       misho     638:     if (pw != NULL && *sudoers_user == '#') {
                    639:        uid_t uid = (uid_t) atoi(sudoers_user + 1);
                    640:        if (uid == pw->pw_uid)
1.1.1.2 ! misho     641:            debug_return_bool(true);
1.1       misho     642:     }
1.1.1.2 ! misho     643:     debug_return_bool(strcmp(sudoers_user, user) == 0);
1.1       misho     644: }
                    645: 
                    646: /*
1.1.1.2 ! misho     647:  *  Returns true if the group/gid from sudoers matches the specified group/gid,
        !           648:  *  else returns false.
1.1       misho     649:  */
1.1.1.2 ! misho     650: bool
1.1       misho     651: group_matches(char *sudoers_group, struct group *gr)
                    652: {
1.1.1.2 ! misho     653:     debug_decl(group_matches, SUDO_DEBUG_MATCH)
        !           654: 
1.1       misho     655:     if (*sudoers_group == '#') {
                    656:        gid_t gid = (gid_t) atoi(sudoers_group + 1);
                    657:        if (gid == gr->gr_gid)
1.1.1.2 ! misho     658:            debug_return_bool(true);
1.1       misho     659:     }
1.1.1.2 ! misho     660:     debug_return_bool(strcmp(gr->gr_name, sudoers_group) == 0);
1.1       misho     661: }
                    662: 
                    663: /*
1.1.1.2 ! misho     664:  *  Returns true if the given user belongs to the named group,
        !           665:  *  else returns false.
1.1       misho     666:  */
1.1.1.2 ! misho     667: bool
1.1       misho     668: usergr_matches(char *group, char *user, struct passwd *pw)
                    669: {
1.1.1.2 ! misho     670:     int matched = false;
1.1       misho     671:     struct passwd *pw0 = NULL;
1.1.1.2 ! misho     672:     debug_decl(usergr_matches, SUDO_DEBUG_MATCH)
1.1       misho     673: 
                    674:     /* make sure we have a valid usergroup, sudo style */
                    675:     if (*group++ != '%')
                    676:        goto done;
                    677: 
                    678:     if (*group == ':' && def_group_plugin) {
                    679:        matched = group_plugin_query(user, group + 1, pw);
                    680:        goto done;
                    681:     }
                    682: 
                    683:     /* look up user's primary gid in the passwd file */
                    684:     if (pw == NULL) {
                    685:        if ((pw0 = sudo_getpwnam(user)) == NULL)
                    686:            goto done;
                    687:        pw = pw0;
                    688:     }
                    689: 
                    690:     if (user_in_group(pw, group)) {
1.1.1.2 ! misho     691:        matched = true;
1.1       misho     692:        goto done;
                    693:     }
                    694: 
                    695:     /* not a Unix group, could be an external group */
                    696:     if (def_group_plugin && group_plugin_query(user, group, pw)) {
1.1.1.2 ! misho     697:        matched = true;
1.1       misho     698:        goto done;
                    699:     }
                    700: 
                    701: done:
                    702:     if (pw0 != NULL)
                    703:        pw_delref(pw0);
                    704: 
1.1.1.2 ! misho     705:     debug_return_bool(matched);
1.1       misho     706: }
                    707: 
                    708: /*
1.1.1.2 ! misho     709:  * Returns true if "host" and "user" belong to the netgroup "netgr",
        !           710:  * else return false.  Either of "host", "shost" or "user" may be NULL
1.1       misho     711:  * in which case that argument is not checked...
                    712:  *
                    713:  * XXX - swap order of host & shost
                    714:  */
1.1.1.2 ! misho     715: bool
1.1       misho     716: netgr_matches(char *netgr, char *lhost, char *shost, char *user)
                    717: {
                    718:     static char *domain;
                    719: #ifdef HAVE_GETDOMAINNAME
                    720:     static int initialized;
                    721: #endif
1.1.1.2 ! misho     722:     debug_decl(netgr_matches, SUDO_DEBUG_MATCH)
1.1       misho     723: 
                    724:     /* make sure we have a valid netgroup, sudo style */
                    725:     if (*netgr++ != '+')
1.1.1.2 ! misho     726:        debug_return_bool(false);
1.1       misho     727: 
                    728: #ifdef HAVE_GETDOMAINNAME
                    729:     /* get the domain name (if any) */
                    730:     if (!initialized) {
                    731:        domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
                    732:        if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
                    733:            efree(domain);
                    734:            domain = NULL;
                    735:        }
                    736:        initialized = 1;
                    737:     }
                    738: #endif /* HAVE_GETDOMAINNAME */
                    739: 
                    740: #ifdef HAVE_INNETGR
                    741:     if (innetgr(netgr, lhost, user, domain))
1.1.1.2 ! misho     742:        debug_return_bool(true);
1.1       misho     743:     else if (lhost != shost && innetgr(netgr, shost, user, domain))
1.1.1.2 ! misho     744:        debug_return_bool(true);
1.1       misho     745: #endif /* HAVE_INNETGR */
                    746: 
1.1.1.2 ! misho     747:     debug_return_bool(false);
1.1       misho     748: }

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