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

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