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