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

Annotation of src/usr.bin/sudo/match.c, Revision 1.7

1.1       millert     1: /*
1.3       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:  * 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/socket.h>
                     29: #include <sys/stat.h>
                     30: #include <stdio.h>
                     31: #ifdef STDC_HEADERS
                     32: # include <stdlib.h>
                     33: # include <stddef.h>
                     34: #else
                     35: # ifdef HAVE_STDLIB_H
                     36: #  include <stdlib.h>
                     37: # endif
                     38: #endif /* STDC_HEADERS */
                     39: #ifdef HAVE_STRING_H
                     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: #ifdef HAVE_FNMATCH
                     50: # include <fnmatch.h>
                     51: #endif /* HAVE_FNMATCH */
                     52: #ifdef HAVE_EXTENDED_GLOB
                     53: # include <glob.h>
                     54: #endif /* HAVE_EXTENDED_GLOB */
                     55: #ifdef HAVE_NETGROUP_H
                     56: # include <netgroup.h>
                     57: #endif /* HAVE_NETGROUP_H */
                     58: #include <ctype.h>
                     59: #include <pwd.h>
                     60: #include <grp.h>
                     61: #include <netinet/in.h>
                     62: #include <arpa/inet.h>
                     63: #include <netdb.h>
                     64: #ifdef HAVE_DIRENT_H
                     65: # include <dirent.h>
                     66: # define NAMLEN(dirent) strlen((dirent)->d_name)
                     67: #else
                     68: # define dirent direct
                     69: # define NAMLEN(dirent) (dirent)->d_namlen
                     70: # ifdef HAVE_SYS_NDIR_H
                     71: #  include <sys/ndir.h>
                     72: # endif
                     73: # ifdef HAVE_SYS_DIR_H
                     74: #  include <sys/dir.h>
                     75: # endif
                     76: # ifdef HAVE_NDIR_H
                     77: #  include <ndir.h>
                     78: # endif
                     79: #endif
                     80:
                     81: #include "sudo.h"
                     82: #include "interfaces.h"
                     83: #include "parse.h"
                     84: #include <gram.h>
                     85:
                     86: #ifndef HAVE_FNMATCH
                     87: # include "emul/fnmatch.h"
                     88: #endif /* HAVE_FNMATCH */
                     89: #ifndef HAVE_EXTENDED_GLOB
                     90: # include "emul/glob.h"
                     91: #endif /* HAVE_EXTENDED_GLOB */
1.3       millert    92: #ifdef USING_NONUNIX_GROUPS
                     93: # include "nonunix.h"
                     94: #endif /* USING_NONUNIX_GROUPS */
1.1       millert    95:
                     96: static struct member_list empty;
                     97:
                     98: static int command_matches_dir __P((char *, size_t));
1.2       millert    99: static int command_matches_glob __P((char *, char *));
                    100: static int command_matches_fnmatch __P((char *, char *));
                    101: static int command_matches_normal __P((char *, char *));
1.1       millert   102:
                    103: /*
                    104:  * Returns TRUE if string 's' contains meta characters.
                    105:  */
                    106: #define has_meta(s)    (strpbrk(s, "\\?*[]") != NULL)
                    107:
                    108: /*
                    109:  * Check for user described by pw in a list of members.
                    110:  * Returns ALLOW, DENY or UNSPEC.
                    111:  */
                    112: static int
                    113: _userlist_matches(pw, list)
                    114:     struct passwd *pw;
                    115:     struct member_list *list;
                    116: {
                    117:     struct member *m;
                    118:     struct alias *a;
                    119:     int rval, matched = UNSPEC;
                    120:
                    121:     tq_foreach_rev(list, m) {
                    122:        switch (m->type) {
                    123:            case ALL:
                    124:                matched = !m->negated;
                    125:                break;
                    126:            case NETGROUP:
                    127:                if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
                    128:                    matched = !m->negated;
                    129:                break;
                    130:            case USERGROUP:
                    131:                if (usergr_matches(m->name, pw->pw_name, pw))
                    132:                    matched = !m->negated;
                    133:                break;
                    134:            case ALIAS:
1.2       millert   135:                if ((a = alias_find(m->name, USERALIAS)) != NULL) {
1.1       millert   136:                    rval = _userlist_matches(pw, &a->members);
                    137:                    if (rval != UNSPEC)
                    138:                        matched = m->negated ? !rval : rval;
                    139:                    break;
                    140:                }
                    141:                /* FALLTHROUGH */
                    142:            case WORD:
                    143:                if (userpw_matches(m->name, pw->pw_name, pw))
                    144:                    matched = !m->negated;
                    145:                break;
                    146:        }
                    147:        if (matched != UNSPEC)
                    148:            break;
                    149:     }
                    150:     return(matched);
                    151: }
                    152:
                    153: int
                    154: userlist_matches(pw, list)
                    155:     struct passwd *pw;
                    156:     struct member_list *list;
                    157: {
                    158:     alias_seqno++;
                    159:     return(_userlist_matches(pw, list));
                    160: }
                    161:
                    162: /*
                    163:  * Check for user described by pw in a list of members.
                    164:  * If both lists are empty compare against def_runas_default.
                    165:  * Returns ALLOW, DENY or UNSPEC.
                    166:  */
                    167: static int
                    168: _runaslist_matches(user_list, group_list)
                    169:     struct member_list *user_list;
                    170:     struct member_list *group_list;
                    171: {
                    172:     struct member *m;
                    173:     struct alias *a;
                    174:     int rval, matched = UNSPEC;
                    175:
1.3       millert   176:     if (runas_gr != NULL) {
                    177:        if (tq_empty(group_list))
                    178:            return(DENY); /* group was specified but none in sudoers */
                    179:        if (runas_pw != NULL && strcmp(runas_pw->pw_name, user_name) &&
                    180:            tq_empty(user_list))
                    181:            return(DENY); /* user was specified but none in sudoers */
                    182:     }
1.1       millert   183:
                    184:     if (tq_empty(user_list) && tq_empty(group_list))
                    185:        return(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
                    186:
                    187:     if (runas_pw != NULL) {
                    188:        tq_foreach_rev(user_list, m) {
                    189:            switch (m->type) {
                    190:                case ALL:
                    191:                    matched = !m->negated;
                    192:                    break;
                    193:                case NETGROUP:
                    194:                    if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
                    195:                        matched = !m->negated;
                    196:                    break;
                    197:                case USERGROUP:
                    198:                    if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
                    199:                        matched = !m->negated;
                    200:                    break;
                    201:                case ALIAS:
1.2       millert   202:                    if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
1.1       millert   203:                        rval = _runaslist_matches(&a->members, &empty);
                    204:                        if (rval != UNSPEC)
                    205:                            matched = m->negated ? !rval : rval;
                    206:                        break;
                    207:                    }
                    208:                    /* FALLTHROUGH */
                    209:                case WORD:
                    210:                    if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
                    211:                        matched = !m->negated;
                    212:                    break;
                    213:            }
                    214:            if (matched != UNSPEC)
                    215:                break;
                    216:        }
                    217:     }
                    218:
                    219:     if (runas_gr != NULL) {
                    220:        tq_foreach_rev(group_list, m) {
                    221:            switch (m->type) {
                    222:                case ALL:
                    223:                    matched = !m->negated;
                    224:                    break;
                    225:                case ALIAS:
1.2       millert   226:                    if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
1.1       millert   227:                        rval = _runaslist_matches(&a->members, &empty);
                    228:                        if (rval != UNSPEC)
                    229:                            matched = m->negated ? !rval : rval;
                    230:                        break;
                    231:                    }
                    232:                    /* FALLTHROUGH */
                    233:                case WORD:
                    234:                    if (group_matches(m->name, runas_gr))
                    235:                        matched = !m->negated;
                    236:                    break;
                    237:            }
                    238:            if (matched != UNSPEC)
                    239:                break;
                    240:        }
                    241:     }
                    242:
                    243:     return(matched);
                    244: }
                    245:
                    246: int
                    247: runaslist_matches(user_list, group_list)
                    248:     struct member_list *user_list;
                    249:     struct member_list *group_list;
                    250: {
                    251:     alias_seqno++;
                    252:     return(_runaslist_matches(user_list ? user_list : &empty,
                    253:        group_list ? group_list : &empty));
                    254: }
                    255:
                    256: /*
                    257:  * Check for host and shost in a list of members.
                    258:  * Returns ALLOW, DENY or UNSPEC.
                    259:  */
                    260: static int
                    261: _hostlist_matches(list)
                    262:     struct member_list *list;
                    263: {
                    264:     struct member *m;
                    265:     struct alias *a;
                    266:     int rval, matched = UNSPEC;
                    267:
                    268:     tq_foreach_rev(list, m) {
                    269:        switch (m->type) {
                    270:            case ALL:
                    271:                matched = !m->negated;
                    272:                break;
                    273:            case NETGROUP:
                    274:                if (netgr_matches(m->name, user_host, user_shost, NULL))
                    275:                    matched = !m->negated;
                    276:                break;
                    277:            case NTWKADDR:
                    278:                if (addr_matches(m->name))
                    279:                    matched = !m->negated;
                    280:                break;
                    281:            case ALIAS:
1.2       millert   282:                if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
1.1       millert   283:                    rval = _hostlist_matches(&a->members);
                    284:                    if (rval != UNSPEC)
                    285:                        matched = m->negated ? !rval : rval;
                    286:                    break;
                    287:                }
                    288:                /* FALLTHROUGH */
                    289:            case WORD:
                    290:                if (hostname_matches(user_shost, user_host, m->name))
                    291:                    matched = !m->negated;
                    292:                break;
                    293:        }
                    294:        if (matched != UNSPEC)
                    295:            break;
                    296:     }
                    297:     return(matched);
                    298: }
                    299:
                    300: int
                    301: hostlist_matches(list)
                    302:     struct member_list *list;
                    303: {
                    304:     alias_seqno++;
                    305:     return(_hostlist_matches(list));
                    306: }
                    307:
                    308: /*
                    309:  * Check for cmnd and args in a list of members.
                    310:  * Returns ALLOW, DENY or UNSPEC.
                    311:  */
                    312: static int
                    313: _cmndlist_matches(list)
                    314:     struct member_list *list;
                    315: {
                    316:     struct member *m;
1.4       millert   317:     int matched = UNSPEC;
1.1       millert   318:
                    319:     tq_foreach_rev(list, m) {
1.4       millert   320:        matched = cmnd_matches(m);
                    321:        if (matched != UNSPEC)
1.1       millert   322:            break;
                    323:     }
                    324:     return(matched);
                    325: }
                    326:
                    327: int
                    328: cmndlist_matches(list)
                    329:     struct member_list *list;
                    330: {
                    331:     alias_seqno++;
                    332:     return(_cmndlist_matches(list));
                    333: }
                    334:
                    335: /*
                    336:  * Check cmnd and args.
                    337:  * Returns ALLOW, DENY or UNSPEC.
                    338:  */
                    339: int
                    340: cmnd_matches(m)
                    341:     struct member *m;
                    342: {
                    343:     struct alias *a;
                    344:     struct sudo_command *c;
                    345:     int rval, matched = UNSPEC;
                    346:
                    347:     switch (m->type) {
                    348:        case ALL:
                    349:            matched = !m->negated;
                    350:            break;
                    351:        case ALIAS:
                    352:            alias_seqno++;
1.2       millert   353:            if ((a = alias_find(m->name, CMNDALIAS)) != NULL) {
1.1       millert   354:                rval = _cmndlist_matches(&a->members);
                    355:                if (rval != UNSPEC)
                    356:                    matched = m->negated ? !rval : rval;
                    357:            }
                    358:            break;
                    359:        case COMMAND:
                    360:            c = (struct sudo_command *)m->name;
                    361:            if (command_matches(c->cmnd, c->args))
                    362:                matched = !m->negated;
                    363:            break;
                    364:     }
                    365:     return(matched);
                    366: }
                    367:
                    368: /*
                    369:  * If path doesn't end in /, return TRUE iff cmnd & path name the same inode;
                    370:  * otherwise, return TRUE if user_cmnd names one of the inodes in path.
                    371:  */
                    372: int
                    373: command_matches(sudoers_cmnd, sudoers_args)
                    374:     char *sudoers_cmnd;
                    375:     char *sudoers_args;
                    376: {
                    377:     /* Check for pseudo-commands */
1.5       millert   378:     if (sudoers_cmnd[0] != '/') {
1.1       millert   379:        /*
                    380:         * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
                    381:         *  a) there are no args in sudoers OR
                    382:         *  b) there are no args on command line and none req by sudoers OR
                    383:         *  c) there are args in sudoers and on command line and they match
                    384:         */
                    385:        if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
                    386:            strcmp(user_cmnd, "sudoedit") != 0)
                    387:            return(FALSE);
                    388:        if (!sudoers_args ||
                    389:            (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
                    390:            (sudoers_args &&
                    391:             fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
                    392:            efree(safe_cmnd);
                    393:            safe_cmnd = estrdup(sudoers_cmnd);
                    394:            return(TRUE);
                    395:        } else
                    396:            return(FALSE);
                    397:     }
                    398:
                    399:     if (has_meta(sudoers_cmnd)) {
                    400:        /*
1.2       millert   401:         * If sudoers_cmnd has meta characters in it, we need to
                    402:         * use glob(3) and/or fnmatch(3) to do the matching.
1.1       millert   403:         */
1.2       millert   404:        if (def_fast_glob)
                    405:            return(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
                    406:        return(command_matches_glob(sudoers_cmnd, sudoers_args));
                    407:     }
                    408:     return(command_matches_normal(sudoers_cmnd, sudoers_args));
                    409: }
                    410:
                    411: static int
                    412: command_matches_fnmatch(sudoers_cmnd, sudoers_args)
                    413:     char *sudoers_cmnd;
                    414:     char *sudoers_args;
                    415: {
                    416:     /*
                    417:      * Return true if fnmatch(3) succeeds AND
                    418:      *  a) there are no args in sudoers OR
                    419:      *  b) there are no args on command line and none required by sudoers OR
                    420:      *  c) there are args in sudoers and on command line and they match
                    421:      * else return false.
                    422:      */
                    423:     if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
                    424:        return(FALSE);
                    425:     if (!sudoers_args ||
                    426:        (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
                    427:        (sudoers_args &&
                    428:         fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
                    429:        if (safe_cmnd)
                    430:            free(safe_cmnd);
                    431:        safe_cmnd = estrdup(user_cmnd);
                    432:        return(TRUE);
                    433:     } else
                    434:        return(FALSE);
                    435: }
                    436:
                    437: static int
                    438: command_matches_glob(sudoers_cmnd, sudoers_args)
                    439:     char *sudoers_cmnd;
                    440:     char *sudoers_args;
                    441: {
                    442:     struct stat sudoers_stat;
                    443:     size_t dlen;
                    444:     char **ap, *base, *cp;
                    445:     glob_t gl;
                    446:
                    447:     /*
                    448:      * First check to see if we can avoid the call to glob(3).
                    449:      * Short circuit if there are no meta chars in the command itself
                    450:      * and user_base and basename(sudoers_cmnd) don't match.
                    451:      */
                    452:     dlen = strlen(sudoers_cmnd);
                    453:     if (sudoers_cmnd[dlen - 1] != '/') {
                    454:        if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
                    455:            base++;
                    456:            if (!has_meta(base) && strcmp(user_base, base) != 0)
                    457:                return(FALSE);
1.1       millert   458:        }
1.2       millert   459:     }
                    460:     /*
                    461:      * Return true if we find a match in the glob(3) results AND
                    462:      *  a) there are no args in sudoers OR
                    463:      *  b) there are no args on command line and none required by sudoers OR
                    464:      *  c) there are args in sudoers and on command line and they match
                    465:      * else return false.
                    466:      */
1.1       millert   467: #define GLOB_FLAGS     (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE)
1.7     ! kettenis  468:     if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0 || gl.gl_pathc == 0) {
1.1       millert   469:        globfree(&gl);
                    470:        return(FALSE);
1.2       millert   471:     }
                    472:     /* For each glob match, compare basename, st_dev and st_ino. */
                    473:     for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
1.1       millert   474:        /* If it ends in '/' it is a directory spec. */
1.2       millert   475:        dlen = strlen(cp);
                    476:        if (cp[dlen - 1] == '/') {
                    477:            if (command_matches_dir(cp, dlen))
                    478:                return(TRUE);
                    479:            continue;
                    480:        }
1.1       millert   481:
1.2       millert   482:        /* Only proceed if user_base and basename(cp) match */
                    483:        if ((base = strrchr(cp, '/')) != NULL)
                    484:            base++;
1.1       millert   485:        else
1.2       millert   486:            base = cp;
1.1       millert   487:        if (strcmp(user_base, base) != 0 ||
1.2       millert   488:            stat(cp, &sudoers_stat) == -1)
                    489:            continue;
                    490:        if (user_stat == NULL ||
                    491:            (user_stat->st_dev == sudoers_stat.st_dev &&
                    492:            user_stat->st_ino == sudoers_stat.st_ino)) {
1.1       millert   493:            efree(safe_cmnd);
1.2       millert   494:            safe_cmnd = estrdup(cp);
                    495:            break;
1.1       millert   496:        }
1.2       millert   497:     }
                    498:     globfree(&gl);
                    499:     if (cp == NULL)
                    500:        return(FALSE);
                    501:
                    502:     if (!sudoers_args ||
                    503:        (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
                    504:        (sudoers_args &&
                    505:         fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
                    506:        efree(safe_cmnd);
                    507:        safe_cmnd = estrdup(user_cmnd);
                    508:        return(TRUE);
                    509:     }
                    510:     return(FALSE);
                    511: }
                    512:
                    513: static int
                    514: command_matches_normal(sudoers_cmnd, sudoers_args)
                    515:     char *sudoers_cmnd;
                    516:     char *sudoers_args;
                    517: {
                    518:     struct stat sudoers_stat;
                    519:     char *base;
                    520:     size_t dlen;
                    521:
                    522:     /* If it ends in '/' it is a directory spec. */
                    523:     dlen = strlen(sudoers_cmnd);
                    524:     if (sudoers_cmnd[dlen - 1] == '/')
                    525:        return(command_matches_dir(sudoers_cmnd, dlen));
                    526:
                    527:     /* Only proceed if user_base and basename(sudoers_cmnd) match */
                    528:     if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
                    529:        base = sudoers_cmnd;
                    530:     else
                    531:        base++;
                    532:     if (strcmp(user_base, base) != 0 ||
                    533:        stat(sudoers_cmnd, &sudoers_stat) == -1)
                    534:        return(FALSE);
                    535:
                    536:     /*
                    537:      * Return true if inode/device matches AND
                    538:      *  a) there are no args in sudoers OR
                    539:      *  b) there are no args on command line and none req by sudoers OR
                    540:      *  c) there are args in sudoers and on command line and they match
                    541:      */
                    542:     if (user_stat != NULL &&
                    543:        (user_stat->st_dev != sudoers_stat.st_dev ||
                    544:        user_stat->st_ino != sudoers_stat.st_ino))
1.1       millert   545:        return(FALSE);
1.2       millert   546:     if (!sudoers_args ||
                    547:        (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
                    548:        (sudoers_args &&
                    549:         fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
                    550:        efree(safe_cmnd);
                    551:        safe_cmnd = estrdup(sudoers_cmnd);
                    552:        return(TRUE);
1.1       millert   553:     }
1.2       millert   554:     return(FALSE);
1.1       millert   555: }
                    556:
                    557: /*
                    558:  * Return TRUE if user_cmnd names one of the inodes in dir, else FALSE.
                    559:  */
                    560: static int
                    561: command_matches_dir(sudoers_dir, dlen)
                    562:     char *sudoers_dir;
                    563:     size_t dlen;
                    564: {
                    565:     struct stat sudoers_stat;
                    566:     struct dirent *dent;
                    567:     char buf[PATH_MAX];
                    568:     DIR *dirp;
                    569:
                    570:     /*
                    571:      * Grot through directory entries, looking for user_base.
                    572:      */
                    573:     dirp = opendir(sudoers_dir);
                    574:     if (dirp == NULL)
                    575:        return(FALSE);
                    576:
1.4       millert   577:     if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
                    578:        closedir(dirp);
1.1       millert   579:        return(FALSE);
1.4       millert   580:     }
1.1       millert   581:     while ((dent = readdir(dirp)) != NULL) {
                    582:        /* ignore paths > PATH_MAX (XXX - log) */
                    583:        buf[dlen] = '\0';
                    584:        if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
                    585:            continue;
                    586:
                    587:        /* only stat if basenames are the same */
                    588:        if (strcmp(user_base, dent->d_name) != 0 ||
                    589:            stat(buf, &sudoers_stat) == -1)
                    590:            continue;
                    591:        if (user_stat->st_dev == sudoers_stat.st_dev &&
                    592:            user_stat->st_ino == sudoers_stat.st_ino) {
                    593:            efree(safe_cmnd);
                    594:            safe_cmnd = estrdup(buf);
                    595:            break;
                    596:        }
                    597:     }
                    598:
                    599:     closedir(dirp);
                    600:     return(dent != NULL);
                    601: }
                    602:
                    603: static int
                    604: addr_matches_if(n)
                    605:     char *n;
                    606: {
                    607:     int i;
                    608:     struct in_addr addr;
                    609:     struct interface *ifp;
                    610: #ifdef HAVE_IN6_ADDR
                    611:     struct in6_addr addr6;
                    612:     int j;
                    613: #endif
                    614:     int family;
                    615:
                    616: #ifdef HAVE_IN6_ADDR
                    617:     if (inet_pton(AF_INET6, n, &addr6) > 0) {
                    618:        family = AF_INET6;
                    619:     } else
                    620: #endif
                    621:     {
                    622:        family = AF_INET;
                    623:        addr.s_addr = inet_addr(n);
                    624:     }
                    625:
                    626:     for (i = 0; i < num_interfaces; i++) {
                    627:        ifp = &interfaces[i];
                    628:        if (ifp->family != family)
                    629:            continue;
                    630:        switch(family) {
                    631:            case AF_INET:
                    632:                if (ifp->addr.ip4.s_addr == addr.s_addr ||
                    633:                    (ifp->addr.ip4.s_addr & ifp->netmask.ip4.s_addr)
                    634:                    == addr.s_addr)
                    635:                    return(TRUE);
                    636:                break;
                    637: #ifdef HAVE_IN6_ADDR
                    638:            case AF_INET6:
                    639:                if (memcmp(ifp->addr.ip6.s6_addr, addr6.s6_addr,
                    640:                    sizeof(addr6.s6_addr)) == 0)
                    641:                    return(TRUE);
                    642:                for (j = 0; j < sizeof(addr6.s6_addr); j++) {
                    643:                    if ((ifp->addr.ip6.s6_addr[j] & ifp->netmask.ip6.s6_addr[j]) != addr6.s6_addr[j])
                    644:                        break;
                    645:                }
                    646:                if (j == sizeof(addr6.s6_addr))
                    647:                    return(TRUE);
                    648: #endif
                    649:        }
                    650:     }
                    651:
                    652:     return(FALSE);
                    653: }
                    654:
                    655: static int
                    656: addr_matches_if_netmask(n, m)
                    657:     char *n;
                    658:     char *m;
                    659: {
                    660:     int i;
                    661:     struct in_addr addr, mask;
                    662:     struct interface *ifp;
                    663: #ifdef HAVE_IN6_ADDR
                    664:     struct in6_addr addr6, mask6;
                    665:     int j;
                    666: #endif
                    667:     int family;
                    668:
                    669: #ifdef HAVE_IN6_ADDR
                    670:     if (inet_pton(AF_INET6, n, &addr6) > 0)
                    671:        family = AF_INET6;
                    672:     else
                    673: #endif
                    674:     {
                    675:        family = AF_INET;
                    676:        addr.s_addr = inet_addr(n);
                    677:     }
                    678:
                    679:     if (family == AF_INET) {
                    680:        if (strchr(m, '.'))
                    681:            mask.s_addr = inet_addr(m);
                    682:        else {
                    683:            i = 32 - atoi(m);
                    684:            mask.s_addr = 0xffffffff;
                    685:            mask.s_addr >>= i;
                    686:            mask.s_addr <<= i;
                    687:            mask.s_addr = htonl(mask.s_addr);
                    688:        }
                    689:     }
                    690: #ifdef HAVE_IN6_ADDR
                    691:     else {
                    692:        if (inet_pton(AF_INET6, m, &mask6) <= 0) {
                    693:            j = atoi(m);
                    694:            for (i = 0; i < 16; i++) {
                    695:                if (j < i * 8)
                    696:                    mask6.s6_addr[i] = 0;
                    697:                else if (i * 8 + 8 <= j)
                    698:                    mask6.s6_addr[i] = 0xff;
                    699:                else
                    700:                    mask6.s6_addr[i] = 0xff00 >> (j - i * 8);
                    701:            }
                    702:        }
                    703:     }
                    704: #endif /* HAVE_IN6_ADDR */
                    705:
                    706:     for (i = 0; i < num_interfaces; i++) {
                    707:        ifp = &interfaces[i];
                    708:        if (ifp->family != family)
                    709:            continue;
                    710:        switch(family) {
                    711:            case AF_INET:
                    712:                if ((ifp->addr.ip4.s_addr & mask.s_addr) == addr.s_addr)
                    713:                    return(TRUE);
                    714: #ifdef HAVE_IN6_ADDR
                    715:            case AF_INET6:
                    716:                for (j = 0; j < sizeof(addr6.s6_addr); j++) {
                    717:                    if ((ifp->addr.ip6.s6_addr[j] & mask6.s6_addr[j]) != addr6.s6_addr[j])
                    718:                        break;
                    719:                }
                    720:                if (j == sizeof(addr6.s6_addr))
                    721:                    return(TRUE);
                    722: #endif /* HAVE_IN6_ADDR */
                    723:        }
                    724:     }
                    725:
                    726:     return(FALSE);
                    727: }
                    728:
                    729: /*
                    730:  * Returns TRUE if "n" is one of our ip addresses or if
                    731:  * "n" is a network that we are on, else returns FALSE.
                    732:  */
                    733: int
                    734: addr_matches(n)
                    735:     char *n;
                    736: {
                    737:     char *m;
                    738:     int retval;
                    739:
                    740:     /* If there's an explicit netmask, use it. */
                    741:     if ((m = strchr(n, '/'))) {
                    742:        *m++ = '\0';
                    743:        retval = addr_matches_if_netmask(n, m);
                    744:        *(m - 1) = '/';
                    745:     } else
                    746:        retval = addr_matches_if(n);
                    747:
                    748:     return(retval);
                    749: }
                    750:
                    751: /*
                    752:  * Returns TRUE if the hostname matches the pattern, else FALSE
                    753:  */
                    754: int
                    755: hostname_matches(shost, lhost, pattern)
                    756:     char *shost;
                    757:     char *lhost;
                    758:     char *pattern;
                    759: {
                    760:     if (has_meta(pattern)) {
                    761:        if (strchr(pattern, '.'))
                    762:            return(!fnmatch(pattern, lhost, FNM_CASEFOLD));
                    763:        else
                    764:            return(!fnmatch(pattern, shost, FNM_CASEFOLD));
                    765:     } else {
                    766:        if (strchr(pattern, '.'))
                    767:            return(!strcasecmp(lhost, pattern));
                    768:        else
                    769:            return(!strcasecmp(shost, pattern));
                    770:     }
                    771: }
                    772:
                    773: /*
                    774:  *  Returns TRUE if the user/uid from sudoers matches the specified user/uid,
                    775:  *  else returns FALSE.
                    776:  */
                    777: int
                    778: userpw_matches(sudoers_user, user, pw)
                    779:     char *sudoers_user;
                    780:     char *user;
                    781:     struct passwd *pw;
                    782: {
                    783:     if (pw != NULL && *sudoers_user == '#') {
                    784:        uid_t uid = (uid_t) atoi(sudoers_user + 1);
                    785:        if (uid == pw->pw_uid)
                    786:            return(TRUE);
                    787:     }
                    788:     return(strcmp(sudoers_user, user) == 0);
                    789: }
                    790:
                    791: /*
                    792:  *  Returns TRUE if the group/gid from sudoers matches the specified group/gid,
                    793:  *  else returns FALSE.
                    794:  */
                    795: int
                    796: group_matches(sudoers_group, gr)
                    797:     char *sudoers_group;
                    798:     struct group *gr;
                    799: {
                    800:     if (*sudoers_group == '#') {
                    801:        gid_t gid = (gid_t) atoi(sudoers_group + 1);
                    802:        if (gid == gr->gr_gid)
                    803:            return(TRUE);
                    804:     }
                    805:     return(strcmp(gr->gr_name, sudoers_group) == 0);
                    806: }
                    807:
                    808: /*
                    809:  *  Returns TRUE if the given user belongs to the named group,
                    810:  *  else returns FALSE.
                    811:  */
                    812: int
                    813: usergr_matches(group, user, pw)
                    814:     char *group;
                    815:     char *user;
                    816:     struct passwd *pw;
                    817: {
1.3       millert   818:     struct group *grp = NULL;
1.1       millert   819:     char **cur;
                    820:     int i;
                    821:
                    822:     /* make sure we have a valid usergroup, sudo style */
                    823:     if (*group++ != '%')
                    824:        return(FALSE);
                    825:
1.3       millert   826: #ifdef USING_NONUNIX_GROUPS
                    827:     if (*group == ':')
                    828:        return(sudo_nonunix_groupcheck(++group, user, pw));
                    829: #endif /* USING_NONUNIX_GROUPS */
                    830:
1.1       millert   831:     /* look up user's primary gid in the passwd file */
                    832:     if (pw == NULL && (pw = sudo_getpwnam(user)) == NULL)
1.3       millert   833:        goto try_supplementary;
1.1       millert   834:
1.3       millert   835:     /* check against user's primary (passwd file) gid */
1.1       millert   836:     if ((grp = sudo_getgrnam(group)) == NULL)
1.3       millert   837:        goto try_supplementary;
1.1       millert   838:     if (grp->gr_gid == pw->pw_gid)
                    839:        return(TRUE);
                    840:
                    841:     /*
                    842:      * If we are matching the invoking or list user and that user has a
                    843:      * supplementary group vector, check it first.
                    844:      */
                    845:     if (strcmp(user, list_pw ? list_pw->pw_name : user_name) == 0) {
                    846:        for (i = 0; i < user_ngroups; i++)
                    847:            if (grp->gr_gid == user_groups[i])
                    848:                return(TRUE);
                    849:     }
1.3       millert   850:
                    851: try_supplementary:
                    852:     if (grp != NULL && grp->gr_mem != NULL) {
1.1       millert   853:        for (cur = grp->gr_mem; *cur; cur++)
                    854:            if (strcmp(*cur, user) == 0)
                    855:                return(TRUE);
                    856:     }
1.3       millert   857:
                    858: #ifdef USING_NONUNIX_GROUPS
                    859:     /* not a Unix group, could be an AD group */
                    860:     if (sudo_nonunix_groupcheck_available() &&
                    861:        sudo_nonunix_groupcheck(group, user, pw))
                    862:        return(TRUE);
                    863: #endif /* USING_NONUNIX_GROUPS */
1.1       millert   864:
                    865:     return(FALSE);
                    866: }
                    867:
                    868: /*
                    869:  * Returns TRUE if "host" and "user" belong to the netgroup "netgr",
                    870:  * else return FALSE.  Either of "host", "shost" or "user" may be NULL
                    871:  * in which case that argument is not checked...
                    872:  *
                    873:  * XXX - swap order of host & shost
                    874:  */
                    875: int
                    876: netgr_matches(netgr, lhost, shost, user)
                    877:     char *netgr;
                    878:     char *lhost;
                    879:     char *shost;
                    880:     char *user;
                    881: {
                    882:     static char *domain;
                    883: #ifdef HAVE_GETDOMAINNAME
                    884:     static int initialized;
                    885: #endif
                    886:
                    887:     /* make sure we have a valid netgroup, sudo style */
                    888:     if (*netgr++ != '+')
                    889:        return(FALSE);
                    890:
                    891: #ifdef HAVE_GETDOMAINNAME
                    892:     /* get the domain name (if any) */
                    893:     if (!initialized) {
                    894:        domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
                    895:        if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
                    896:            efree(domain);
                    897:            domain = NULL;
                    898:        }
                    899:        initialized = 1;
                    900:     }
                    901: #endif /* HAVE_GETDOMAINNAME */
                    902:
                    903: #ifdef HAVE_INNETGR
                    904:     if (innetgr(netgr, lhost, user, domain))
                    905:        return(TRUE);
                    906:     else if (lhost != shost && innetgr(netgr, shost, user, domain))
                    907:        return(TRUE);
                    908: #endif /* HAVE_INNETGR */
                    909:
                    910:     return(FALSE);
                    911: }