Annotation of embedaddon/sudo/plugins/sudoers/match.c, revision 1.1.1.3
1.1 misho 1: /*
1.1.1.2 misho 2: * Copyright (c) 1996, 1998-2005, 2007-2012
1.1 misho 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 */
1.1.1.2 misho 50: #ifdef HAVE_GLOB
1.1 misho 51: # include <glob.h>
1.1.1.2 misho 52: #endif /* HAVE_GLOB */
1.1 misho 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 */
1.1.1.2 misho 84: #ifndef HAVE_GLOB
1.1 misho 85: # include "compat/glob.h"
1.1.1.2 misho 86: #endif /* HAVE_GLOB */
1.1 misho 87:
88: static struct member_list empty;
89:
1.1.1.2 misho 90: static bool command_matches_dir(char *, size_t);
91: static bool command_matches_glob(char *, char *);
92: static bool command_matches_fnmatch(char *, char *);
93: static bool command_matches_normal(char *, char *);
1.1 misho 94:
95: /*
1.1.1.2 misho 96: * Returns true if string 's' contains meta characters.
1.1 misho 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;
1.1.1.2 misho 110: debug_decl(_userlist_matches, SUDO_DEBUG_MATCH)
1.1 misho 111:
112: tq_foreach_rev(list, m) {
113: switch (m->type) {
114: case ALL:
115: matched = !m->negated;
116: break;
117: case NETGROUP:
118: if (netgr_matches(m->name, NULL, NULL, pw->pw_name))
119: matched = !m->negated;
120: break;
121: case USERGROUP:
122: if (usergr_matches(m->name, pw->pw_name, pw))
123: matched = !m->negated;
124: break;
125: case ALIAS:
126: if ((a = alias_find(m->name, USERALIAS)) != NULL) {
127: rval = _userlist_matches(pw, &a->members);
128: if (rval != UNSPEC)
129: matched = m->negated ? !rval : rval;
130: break;
131: }
132: /* FALLTHROUGH */
133: case WORD:
134: if (userpw_matches(m->name, pw->pw_name, pw))
135: matched = !m->negated;
136: break;
137: }
138: if (matched != UNSPEC)
139: break;
140: }
1.1.1.2 misho 141: debug_return_bool(matched);
1.1 misho 142: }
143:
144: int
145: userlist_matches(struct passwd *pw, struct member_list *list)
146: {
147: alias_seqno++;
148: return _userlist_matches(pw, list);
149: }
150:
151: /*
152: * Check for user described by pw in a list of members.
153: * If both lists are empty compare against def_runas_default.
154: * Returns ALLOW, DENY or UNSPEC.
155: */
156: static int
1.1.1.3 ! misho 157: _runaslist_matches(struct member_list *user_list,
! 158: struct member_list *group_list, struct member **matching_user,
! 159: struct member **matching_group)
1.1 misho 160: {
161: struct member *m;
162: struct alias *a;
163: int rval;
164: int user_matched = UNSPEC;
165: int group_matched = UNSPEC;
1.1.1.2 misho 166: debug_decl(_runaslist_matches, SUDO_DEBUG_MATCH)
1.1 misho 167:
168: if (runas_pw != NULL) {
169: /* If no runas user or runas group listed in sudoers, use default. */
170: if (tq_empty(user_list) && tq_empty(group_list))
1.1.1.2 misho 171: debug_return_int(userpw_matches(def_runas_default, runas_pw->pw_name, runas_pw));
1.1 misho 172:
173: tq_foreach_rev(user_list, m) {
174: switch (m->type) {
175: case ALL:
176: user_matched = !m->negated;
177: break;
178: case NETGROUP:
179: if (netgr_matches(m->name, NULL, NULL, runas_pw->pw_name))
180: user_matched = !m->negated;
181: break;
182: case USERGROUP:
183: if (usergr_matches(m->name, runas_pw->pw_name, runas_pw))
184: user_matched = !m->negated;
185: break;
186: case ALIAS:
187: if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
1.1.1.3 ! misho 188: rval = _runaslist_matches(&a->members, &empty,
! 189: matching_user, NULL);
1.1 misho 190: if (rval != UNSPEC)
191: user_matched = m->negated ? !rval : rval;
192: break;
193: }
194: /* FALLTHROUGH */
195: case WORD:
196: if (userpw_matches(m->name, runas_pw->pw_name, runas_pw))
197: user_matched = !m->negated;
198: break;
1.1.1.3 ! misho 199: case MYSELF:
! 200: if (!ISSET(sudo_user.flags, RUNAS_USER_SPECIFIED) ||
! 201: strcmp(user_name, runas_pw->pw_name) == 0)
! 202: user_matched = !m->negated;
! 203: break;
1.1 misho 204: }
1.1.1.3 ! misho 205: if (user_matched != UNSPEC) {
! 206: if (matching_user != NULL && m->type != ALIAS)
! 207: *matching_user = m;
1.1 misho 208: break;
1.1.1.3 ! misho 209: }
1.1 misho 210: }
211: }
212:
213: if (runas_gr != NULL) {
214: if (user_matched == UNSPEC) {
215: if (runas_pw == NULL || strcmp(runas_pw->pw_name, user_name) == 0)
216: user_matched = ALLOW; /* only changing group */
217: }
218: tq_foreach_rev(group_list, m) {
219: switch (m->type) {
220: case ALL:
221: group_matched = !m->negated;
222: break;
223: case ALIAS:
224: if ((a = alias_find(m->name, RUNASALIAS)) != NULL) {
1.1.1.3 ! misho 225: rval = _runaslist_matches(&empty, &a->members,
! 226: NULL, matching_group);
1.1 misho 227: if (rval != UNSPEC)
228: group_matched = m->negated ? !rval : rval;
229: break;
230: }
231: /* FALLTHROUGH */
232: case WORD:
233: if (group_matches(m->name, runas_gr))
234: group_matched = !m->negated;
235: break;
236: }
1.1.1.3 ! misho 237: if (group_matched != UNSPEC) {
! 238: if (matching_group != NULL && m->type != ALIAS)
! 239: *matching_group = m;
1.1 misho 240: break;
1.1.1.3 ! misho 241: }
1.1 misho 242: }
243: if (group_matched == UNSPEC) {
244: if (runas_pw != NULL && runas_pw->pw_gid == runas_gr->gr_gid)
245: group_matched = ALLOW; /* runas group matches passwd db */
246: }
247: }
248:
249: if (user_matched == DENY || group_matched == DENY)
1.1.1.2 misho 250: debug_return_int(DENY);
1.1 misho 251: if (user_matched == group_matched || runas_gr == NULL)
1.1.1.2 misho 252: debug_return_int(user_matched);
253: debug_return_int(UNSPEC);
1.1 misho 254: }
255:
256: int
1.1.1.3 ! misho 257: runaslist_matches(struct member_list *user_list,
! 258: struct member_list *group_list, struct member **matching_user,
! 259: struct member **matching_group)
1.1 misho 260: {
261: alias_seqno++;
262: return _runaslist_matches(user_list ? user_list : &empty,
1.1.1.3 ! misho 263: group_list ? group_list : &empty, matching_user, matching_group);
1.1 misho 264: }
265:
266: /*
267: * Check for host and shost in a list of members.
268: * Returns ALLOW, DENY or UNSPEC.
269: */
270: static int
271: _hostlist_matches(struct member_list *list)
272: {
273: struct member *m;
274: struct alias *a;
275: int rval, matched = UNSPEC;
1.1.1.2 misho 276: debug_decl(_hostlist_matches, SUDO_DEBUG_MATCH)
1.1 misho 277:
278: tq_foreach_rev(list, m) {
279: switch (m->type) {
280: case ALL:
281: matched = !m->negated;
282: break;
283: case NETGROUP:
284: if (netgr_matches(m->name, user_host, user_shost, NULL))
285: matched = !m->negated;
286: break;
287: case NTWKADDR:
288: if (addr_matches(m->name))
289: matched = !m->negated;
290: break;
291: case ALIAS:
292: if ((a = alias_find(m->name, HOSTALIAS)) != NULL) {
293: rval = _hostlist_matches(&a->members);
294: if (rval != UNSPEC)
295: matched = m->negated ? !rval : rval;
296: break;
297: }
298: /* FALLTHROUGH */
299: case WORD:
300: if (hostname_matches(user_shost, user_host, m->name))
301: matched = !m->negated;
302: break;
303: }
304: if (matched != UNSPEC)
305: break;
306: }
1.1.1.2 misho 307: debug_return_bool(matched);
1.1 misho 308: }
309:
310: int
311: hostlist_matches(struct member_list *list)
312: {
313: alias_seqno++;
314: return _hostlist_matches(list);
315: }
316:
317: /*
318: * Check for cmnd and args in a list of members.
319: * Returns ALLOW, DENY or UNSPEC.
320: */
321: static int
322: _cmndlist_matches(struct member_list *list)
323: {
324: struct member *m;
325: int matched = UNSPEC;
1.1.1.2 misho 326: debug_decl(_cmndlist_matches, SUDO_DEBUG_MATCH)
1.1 misho 327:
328: tq_foreach_rev(list, m) {
329: matched = cmnd_matches(m);
330: if (matched != UNSPEC)
331: break;
332: }
1.1.1.2 misho 333: debug_return_bool(matched);
1.1 misho 334: }
335:
336: int
337: cmndlist_matches(struct member_list *list)
338: {
339: alias_seqno++;
340: return _cmndlist_matches(list);
341: }
342:
343: /*
344: * Check cmnd and args.
345: * Returns ALLOW, DENY or UNSPEC.
346: */
347: int
348: cmnd_matches(struct member *m)
349: {
350: struct alias *a;
351: struct sudo_command *c;
352: int rval, matched = UNSPEC;
1.1.1.2 misho 353: debug_decl(cmnd_matches, SUDO_DEBUG_MATCH)
1.1 misho 354:
355: switch (m->type) {
356: case ALL:
357: matched = !m->negated;
358: break;
359: case ALIAS:
360: alias_seqno++;
361: if ((a = alias_find(m->name, CMNDALIAS)) != NULL) {
362: rval = _cmndlist_matches(&a->members);
363: if (rval != UNSPEC)
364: matched = m->negated ? !rval : rval;
365: }
366: break;
367: case COMMAND:
368: c = (struct sudo_command *)m->name;
369: if (command_matches(c->cmnd, c->args))
370: matched = !m->negated;
371: break;
372: }
1.1.1.2 misho 373: debug_return_bool(matched);
1.1 misho 374: }
375:
1.1.1.2 misho 376: static bool
1.1 misho 377: command_args_match(sudoers_cmnd, sudoers_args)
378: char *sudoers_cmnd;
379: char *sudoers_args;
380: {
381: int flags = 0;
1.1.1.2 misho 382: debug_decl(command_args_match, SUDO_DEBUG_MATCH)
1.1 misho 383:
384: /*
385: * If no args specified in sudoers, any user args are allowed.
386: * If the empty string is specified in sudoers, no user args are allowed.
387: */
388: if (!sudoers_args ||
389: (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)))
1.1.1.2 misho 390: debug_return_bool(true);
1.1 misho 391: /*
392: * If args are specified in sudoers, they must match the user args.
393: * If running as sudoedit, all args are assumed to be paths.
394: */
395: if (sudoers_args) {
396: /* For sudoedit, all args are assumed to be pathnames. */
397: if (strcmp(sudoers_cmnd, "sudoedit") == 0)
398: flags = FNM_PATHNAME;
399: if (fnmatch(sudoers_args, user_args ? user_args : "", flags) == 0)
1.1.1.2 misho 400: debug_return_bool(true);
1.1 misho 401: }
1.1.1.2 misho 402: debug_return_bool(false);
1.1 misho 403: }
404:
405: /*
1.1.1.2 misho 406: * If path doesn't end in /, return true iff cmnd & path name the same inode;
407: * otherwise, return true if user_cmnd names one of the inodes in path.
1.1 misho 408: */
1.1.1.2 misho 409: bool
1.1 misho 410: command_matches(char *sudoers_cmnd, char *sudoers_args)
411: {
1.1.1.2 misho 412: debug_decl(command_matches, SUDO_DEBUG_MATCH)
413:
1.1 misho 414: /* Check for pseudo-commands */
415: if (sudoers_cmnd[0] != '/') {
416: /*
417: * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND
418: * a) there are no args in sudoers OR
419: * b) there are no args on command line and none req by sudoers OR
420: * c) there are args in sudoers and on command line and they match
421: */
422: if (strcmp(sudoers_cmnd, "sudoedit") != 0 ||
423: strcmp(user_cmnd, "sudoedit") != 0)
1.1.1.2 misho 424: debug_return_bool(false);
1.1 misho 425: if (command_args_match(sudoers_cmnd, sudoers_args)) {
426: efree(safe_cmnd);
427: safe_cmnd = estrdup(sudoers_cmnd);
1.1.1.2 misho 428: debug_return_bool(true);
1.1 misho 429: } else
1.1.1.2 misho 430: debug_return_bool(false);
1.1 misho 431: }
432:
433: if (has_meta(sudoers_cmnd)) {
434: /*
435: * If sudoers_cmnd has meta characters in it, we need to
436: * use glob(3) and/or fnmatch(3) to do the matching.
437: */
438: if (def_fast_glob)
1.1.1.2 misho 439: debug_return_bool(command_matches_fnmatch(sudoers_cmnd, sudoers_args));
440: debug_return_bool(command_matches_glob(sudoers_cmnd, sudoers_args));
1.1 misho 441: }
1.1.1.2 misho 442: debug_return_bool(command_matches_normal(sudoers_cmnd, sudoers_args));
1.1 misho 443: }
444:
1.1.1.2 misho 445: static bool
1.1 misho 446: command_matches_fnmatch(char *sudoers_cmnd, char *sudoers_args)
447: {
1.1.1.2 misho 448: debug_decl(command_matches_fnmatch, SUDO_DEBUG_MATCH)
449:
1.1 misho 450: /*
451: * Return true if fnmatch(3) succeeds AND
452: * a) there are no args in sudoers OR
453: * b) there are no args on command line and none required by sudoers OR
454: * c) there are args in sudoers and on command line and they match
455: * else return false.
456: */
457: if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
1.1.1.2 misho 458: debug_return_bool(false);
1.1 misho 459: if (command_args_match(sudoers_cmnd, sudoers_args)) {
460: if (safe_cmnd)
461: free(safe_cmnd);
462: safe_cmnd = estrdup(user_cmnd);
1.1.1.2 misho 463: debug_return_bool(true);
464: }
465: debug_return_bool(false);
1.1 misho 466: }
467:
1.1.1.2 misho 468: static bool
1.1 misho 469: command_matches_glob(char *sudoers_cmnd, char *sudoers_args)
470: {
471: struct stat sudoers_stat;
472: size_t dlen;
473: char **ap, *base, *cp;
474: glob_t gl;
1.1.1.2 misho 475: debug_decl(command_matches_glob, SUDO_DEBUG_MATCH)
1.1 misho 476:
477: /*
478: * First check to see if we can avoid the call to glob(3).
479: * Short circuit if there are no meta chars in the command itself
480: * and user_base and basename(sudoers_cmnd) don't match.
481: */
482: dlen = strlen(sudoers_cmnd);
483: if (sudoers_cmnd[dlen - 1] != '/') {
484: if ((base = strrchr(sudoers_cmnd, '/')) != NULL) {
485: base++;
486: if (!has_meta(base) && strcmp(user_base, base) != 0)
1.1.1.2 misho 487: debug_return_bool(false);
1.1 misho 488: }
489: }
490: /*
491: * Return true if we find a match in the glob(3) results AND
492: * a) there are no args in sudoers OR
493: * b) there are no args on command line and none required by sudoers OR
494: * c) there are args in sudoers and on command line and they match
495: * else return false.
496: */
1.1.1.2 misho 497: if (glob(sudoers_cmnd, GLOB_NOSORT, NULL, &gl) != 0 || gl.gl_pathc == 0) {
1.1 misho 498: globfree(&gl);
1.1.1.2 misho 499: debug_return_bool(false);
1.1 misho 500: }
501: /* For each glob match, compare basename, st_dev and st_ino. */
502: for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
503: /* If it ends in '/' it is a directory spec. */
504: dlen = strlen(cp);
505: if (cp[dlen - 1] == '/') {
506: if (command_matches_dir(cp, dlen))
1.1.1.2 misho 507: debug_return_bool(true);
1.1 misho 508: continue;
509: }
510:
511: /* Only proceed if user_base and basename(cp) match */
512: if ((base = strrchr(cp, '/')) != NULL)
513: base++;
514: else
515: base = cp;
516: if (strcmp(user_base, base) != 0 ||
517: stat(cp, &sudoers_stat) == -1)
518: continue;
519: if (user_stat == NULL ||
520: (user_stat->st_dev == sudoers_stat.st_dev &&
521: user_stat->st_ino == sudoers_stat.st_ino)) {
522: efree(safe_cmnd);
523: safe_cmnd = estrdup(cp);
524: break;
525: }
526: }
527: globfree(&gl);
528: if (cp == NULL)
1.1.1.2 misho 529: debug_return_bool(false);
1.1 misho 530:
531: if (command_args_match(sudoers_cmnd, sudoers_args)) {
532: efree(safe_cmnd);
533: safe_cmnd = estrdup(user_cmnd);
1.1.1.2 misho 534: debug_return_bool(true);
1.1 misho 535: }
1.1.1.2 misho 536: debug_return_bool(false);
1.1 misho 537: }
538:
1.1.1.2 misho 539: static bool
1.1 misho 540: command_matches_normal(char *sudoers_cmnd, char *sudoers_args)
541: {
542: struct stat sudoers_stat;
543: char *base;
544: size_t dlen;
1.1.1.2 misho 545: debug_decl(command_matches_normal, SUDO_DEBUG_MATCH)
1.1 misho 546:
547: /* If it ends in '/' it is a directory spec. */
548: dlen = strlen(sudoers_cmnd);
549: if (sudoers_cmnd[dlen - 1] == '/')
1.1.1.2 misho 550: debug_return_bool(command_matches_dir(sudoers_cmnd, dlen));
1.1 misho 551:
552: /* Only proceed if user_base and basename(sudoers_cmnd) match */
553: if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
554: base = sudoers_cmnd;
555: else
556: base++;
557: if (strcmp(user_base, base) != 0 ||
558: stat(sudoers_cmnd, &sudoers_stat) == -1)
1.1.1.2 misho 559: debug_return_bool(false);
1.1 misho 560:
561: /*
562: * Return true if inode/device matches AND
563: * a) there are no args in sudoers OR
564: * b) there are no args on command line and none req by sudoers OR
565: * c) there are args in sudoers and on command line and they match
566: */
567: if (user_stat != NULL &&
568: (user_stat->st_dev != sudoers_stat.st_dev ||
569: user_stat->st_ino != sudoers_stat.st_ino))
1.1.1.2 misho 570: debug_return_bool(false);
1.1 misho 571: if (command_args_match(sudoers_cmnd, sudoers_args)) {
572: efree(safe_cmnd);
573: safe_cmnd = estrdup(sudoers_cmnd);
1.1.1.2 misho 574: debug_return_bool(true);
1.1 misho 575: }
1.1.1.2 misho 576: debug_return_bool(false);
1.1 misho 577: }
578:
579: /*
1.1.1.2 misho 580: * Return true if user_cmnd names one of the inodes in dir, else false.
1.1 misho 581: */
1.1.1.2 misho 582: static bool
1.1 misho 583: command_matches_dir(char *sudoers_dir, size_t dlen)
584: {
585: struct stat sudoers_stat;
586: struct dirent *dent;
587: char buf[PATH_MAX];
588: DIR *dirp;
1.1.1.2 misho 589: debug_decl(command_matches_dir, SUDO_DEBUG_MATCH)
1.1 misho 590:
591: /*
592: * Grot through directory entries, looking for user_base.
593: */
594: dirp = opendir(sudoers_dir);
595: if (dirp == NULL)
1.1.1.2 misho 596: debug_return_bool(false);
1.1 misho 597:
598: if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
599: closedir(dirp);
1.1.1.2 misho 600: debug_return_bool(false);
1.1 misho 601: }
602: while ((dent = readdir(dirp)) != NULL) {
603: /* ignore paths > PATH_MAX (XXX - log) */
604: buf[dlen] = '\0';
605: if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
606: continue;
607:
608: /* only stat if basenames are the same */
609: if (strcmp(user_base, dent->d_name) != 0 ||
610: stat(buf, &sudoers_stat) == -1)
611: continue;
612: if (user_stat == NULL ||
613: (user_stat->st_dev == sudoers_stat.st_dev &&
614: user_stat->st_ino == sudoers_stat.st_ino)) {
615: efree(safe_cmnd);
616: safe_cmnd = estrdup(buf);
617: break;
618: }
619: }
620:
621: closedir(dirp);
1.1.1.2 misho 622: debug_return_bool(dent != NULL);
1.1 misho 623: }
624:
625: /*
1.1.1.2 misho 626: * Returns true if the hostname matches the pattern, else false
1.1 misho 627: */
1.1.1.2 misho 628: bool
1.1 misho 629: hostname_matches(char *shost, char *lhost, char *pattern)
630: {
1.1.1.2 misho 631: debug_decl(hostname_matches, SUDO_DEBUG_MATCH)
632:
1.1 misho 633: if (has_meta(pattern)) {
634: if (strchr(pattern, '.'))
1.1.1.2 misho 635: debug_return_bool(!fnmatch(pattern, lhost, FNM_CASEFOLD));
1.1 misho 636: else
1.1.1.2 misho 637: debug_return_bool(!fnmatch(pattern, shost, FNM_CASEFOLD));
1.1 misho 638: } else {
639: if (strchr(pattern, '.'))
1.1.1.2 misho 640: debug_return_bool(!strcasecmp(lhost, pattern));
1.1 misho 641: else
1.1.1.2 misho 642: debug_return_bool(!strcasecmp(shost, pattern));
1.1 misho 643: }
644: }
645:
646: /*
1.1.1.2 misho 647: * Returns true if the user/uid from sudoers matches the specified user/uid,
648: * else returns false.
1.1 misho 649: */
1.1.1.2 misho 650: bool
1.1 misho 651: userpw_matches(char *sudoers_user, char *user, struct passwd *pw)
652: {
1.1.1.2 misho 653: debug_decl(userpw_matches, SUDO_DEBUG_MATCH)
654:
1.1 misho 655: if (pw != NULL && *sudoers_user == '#') {
656: uid_t uid = (uid_t) atoi(sudoers_user + 1);
657: if (uid == pw->pw_uid)
1.1.1.2 misho 658: debug_return_bool(true);
1.1 misho 659: }
1.1.1.2 misho 660: debug_return_bool(strcmp(sudoers_user, user) == 0);
1.1 misho 661: }
662:
663: /*
1.1.1.2 misho 664: * Returns true if the group/gid from sudoers matches the specified group/gid,
665: * else returns false.
1.1 misho 666: */
1.1.1.2 misho 667: bool
1.1 misho 668: group_matches(char *sudoers_group, struct group *gr)
669: {
1.1.1.2 misho 670: debug_decl(group_matches, SUDO_DEBUG_MATCH)
671:
1.1 misho 672: if (*sudoers_group == '#') {
673: gid_t gid = (gid_t) atoi(sudoers_group + 1);
674: if (gid == gr->gr_gid)
1.1.1.2 misho 675: debug_return_bool(true);
1.1 misho 676: }
1.1.1.2 misho 677: debug_return_bool(strcmp(gr->gr_name, sudoers_group) == 0);
1.1 misho 678: }
679:
680: /*
1.1.1.2 misho 681: * Returns true if the given user belongs to the named group,
682: * else returns false.
1.1 misho 683: */
1.1.1.2 misho 684: bool
1.1 misho 685: usergr_matches(char *group, char *user, struct passwd *pw)
686: {
1.1.1.2 misho 687: int matched = false;
1.1 misho 688: struct passwd *pw0 = NULL;
1.1.1.2 misho 689: debug_decl(usergr_matches, SUDO_DEBUG_MATCH)
1.1 misho 690:
691: /* make sure we have a valid usergroup, sudo style */
692: if (*group++ != '%')
693: goto done;
694:
695: if (*group == ':' && def_group_plugin) {
696: matched = group_plugin_query(user, group + 1, pw);
697: goto done;
698: }
699:
700: /* look up user's primary gid in the passwd file */
701: if (pw == NULL) {
702: if ((pw0 = sudo_getpwnam(user)) == NULL)
703: goto done;
704: pw = pw0;
705: }
706:
707: if (user_in_group(pw, group)) {
1.1.1.2 misho 708: matched = true;
1.1 misho 709: goto done;
710: }
711:
712: /* not a Unix group, could be an external group */
713: if (def_group_plugin && group_plugin_query(user, group, pw)) {
1.1.1.2 misho 714: matched = true;
1.1 misho 715: goto done;
716: }
717:
718: done:
719: if (pw0 != NULL)
1.1.1.3 ! misho 720: sudo_pw_delref(pw0);
1.1 misho 721:
1.1.1.2 misho 722: debug_return_bool(matched);
1.1 misho 723: }
724:
725: /*
1.1.1.2 misho 726: * Returns true if "host" and "user" belong to the netgroup "netgr",
727: * else return false. Either of "host", "shost" or "user" may be NULL
1.1 misho 728: * in which case that argument is not checked...
729: *
730: * XXX - swap order of host & shost
731: */
1.1.1.2 misho 732: bool
1.1 misho 733: netgr_matches(char *netgr, char *lhost, char *shost, char *user)
734: {
735: static char *domain;
736: #ifdef HAVE_GETDOMAINNAME
737: static int initialized;
738: #endif
1.1.1.2 misho 739: debug_decl(netgr_matches, SUDO_DEBUG_MATCH)
1.1 misho 740:
741: /* make sure we have a valid netgroup, sudo style */
742: if (*netgr++ != '+')
1.1.1.2 misho 743: debug_return_bool(false);
1.1 misho 744:
745: #ifdef HAVE_GETDOMAINNAME
746: /* get the domain name (if any) */
747: if (!initialized) {
748: domain = (char *) emalloc(MAXHOSTNAMELEN + 1);
749: if (getdomainname(domain, MAXHOSTNAMELEN + 1) == -1 || *domain == '\0') {
750: efree(domain);
751: domain = NULL;
752: }
753: initialized = 1;
754: }
755: #endif /* HAVE_GETDOMAINNAME */
756:
757: #ifdef HAVE_INNETGR
758: if (innetgr(netgr, lhost, user, domain))
1.1.1.2 misho 759: debug_return_bool(true);
1.1 misho 760: else if (lhost != shost && innetgr(netgr, shost, user, domain))
1.1.1.2 misho 761: debug_return_bool(true);
1.1 misho 762: #endif /* HAVE_INNETGR */
763:
1.1.1.2 misho 764: debug_return_bool(false);
1.1 misho 765: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>