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