[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.2

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