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

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

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