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