Annotation of embedaddon/sudo/plugins/sudoers/parse.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 2004-2005, 2007-2011 Todd C. Miller <Todd.Miller@courtesan.com>
3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
16: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17: */
18:
19: #include <config.h>
20:
21: #include <sys/types.h>
22: #include <sys/param.h>
23: #include <stdio.h>
24: #ifdef STDC_HEADERS
25: # include <stdlib.h>
26: # include <stddef.h>
27: #else
28: # ifdef HAVE_STDLIB_H
29: # include <stdlib.h>
30: # endif
31: #endif /* STDC_HEADERS */
32: #ifdef HAVE_STRING_H
33: # include <string.h>
34: #endif /* HAVE_STRING_H */
35: #ifdef HAVE_STRINGS_H
36: # include <strings.h>
37: #endif /* HAVE_STRINGS_H */
38: #ifdef HAVE_UNISTD_H
39: # include <unistd.h>
40: #endif /* HAVE_UNISTD_H */
41: #include <ctype.h>
42: #include <pwd.h>
43: #include <grp.h>
44:
45: #include "sudoers.h"
46: #include "parse.h"
47: #include "lbuf.h"
48: #include <gram.h>
49:
50: /* Characters that must be quoted in sudoers */
51: #define SUDOERS_QUOTED ":\\,=#\""
52:
53: /* sudoers nsswitch routines */
54: struct sudo_nss sudo_nss_file = {
55: &sudo_nss_file,
56: NULL,
57: sudo_file_open,
58: sudo_file_close,
59: sudo_file_parse,
60: sudo_file_setdefs,
61: sudo_file_lookup,
62: sudo_file_display_cmnd,
63: sudo_file_display_defaults,
64: sudo_file_display_bound_defaults,
65: sudo_file_display_privs
66: };
67:
68: /*
69: * Parser externs.
70: */
71: extern FILE *yyin;
72: extern char *errorfile;
73: extern int errorlineno, parse_error;
74:
75: /*
76: * Local prototypes.
77: */
78: static void print_member(struct lbuf *, char *, int, int, int);
79: static int display_bound_defaults(int, struct lbuf *);
80:
81: int
82: sudo_file_open(struct sudo_nss *nss)
83: {
84: if (def_ignore_local_sudoers)
85: return -1;
86: nss->handle = open_sudoers(sudoers_file, FALSE, NULL);
87: return nss->handle ? 0 : -1;
88: }
89:
90: int
91: sudo_file_close(struct sudo_nss *nss)
92: {
93: /* Free parser data structures and close sudoers file. */
94: init_parser(NULL, 0);
95: if (nss->handle != NULL) {
96: fclose(nss->handle);
97: nss->handle = NULL;
98: yyin = NULL;
99: }
100: return 0;
101: }
102:
103: /*
104: * Parse the specified sudoers file.
105: */
106: int
107: sudo_file_parse(struct sudo_nss *nss)
108: {
109: if (nss->handle == NULL)
110: return -1;
111:
112: init_parser(sudoers_file, 0);
113: yyin = nss->handle;
114: if (yyparse() != 0 || parse_error) {
115: log_error(NO_EXIT, _("parse error in %s near line %d"),
116: errorfile, errorlineno);
117: return -1;
118: }
119: return 0;
120: }
121:
122: /*
123: * Wrapper around update_defaults() for nsswitch code.
124: */
125: int
126: sudo_file_setdefs(struct sudo_nss *nss)
127: {
128: if (nss->handle == NULL)
129: return -1;
130:
131: if (!update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER))
132: return -1;
133: return 0;
134: }
135:
136: /*
137: * Look up the user in the parsed sudoers file and check to see if they are
138: * allowed to run the specified command on this host as the target user.
139: */
140: int
141: sudo_file_lookup(struct sudo_nss *nss, int validated, int pwflag)
142: {
143: int match, host_match, runas_match, cmnd_match;
144: struct cmndspec *cs;
145: struct cmndtag *tags = NULL;
146: struct privilege *priv;
147: struct userspec *us;
148:
149: if (nss->handle == NULL)
150: return validated;
151:
152: /*
153: * Only check the actual command if pwflag is not set.
154: * It is set for the "validate", "list" and "kill" pseudo-commands.
155: * Always check the host and user.
156: */
157: if (pwflag) {
158: int nopass;
159: enum def_tuple pwcheck;
160:
161: pwcheck = (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple;
162: nopass = (pwcheck == all) ? TRUE : FALSE;
163:
164: if (list_pw == NULL)
165: SET(validated, FLAG_NO_CHECK);
166: CLR(validated, FLAG_NO_USER);
167: CLR(validated, FLAG_NO_HOST);
168: match = DENY;
169: tq_foreach_fwd(&userspecs, us) {
170: if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
171: continue;
172: tq_foreach_fwd(&us->privileges, priv) {
173: if (hostlist_matches(&priv->hostlist) != ALLOW)
174: continue;
175: tq_foreach_fwd(&priv->cmndlist, cs) {
176: /* Only check the command when listing another user. */
177: if (user_uid == 0 || list_pw == NULL ||
178: user_uid == list_pw->pw_uid ||
179: cmnd_matches(cs->cmnd) == ALLOW)
180: match = ALLOW;
181: if ((pwcheck == any && cs->tags.nopasswd == TRUE) ||
182: (pwcheck == all && cs->tags.nopasswd != TRUE))
183: nopass = cs->tags.nopasswd;
184: }
185: }
186: }
187: if (match == ALLOW || user_uid == 0) {
188: /* User has an entry for this host. */
189: SET(validated, VALIDATE_OK);
190: } else if (match == DENY)
191: SET(validated, VALIDATE_NOT_OK);
192: if (pwcheck == always && def_authenticate)
193: SET(validated, FLAG_CHECK_USER);
194: else if (pwcheck == never || nopass == TRUE)
195: def_authenticate = FALSE;
196: return validated;
197: }
198:
199: /* Need to be runas user while stat'ing things. */
200: set_perms(PERM_RUNAS);
201:
202: match = UNSPEC;
203: tq_foreach_rev(&userspecs, us) {
204: if (userlist_matches(sudo_user.pw, &us->users) != ALLOW)
205: continue;
206: CLR(validated, FLAG_NO_USER);
207: tq_foreach_rev(&us->privileges, priv) {
208: host_match = hostlist_matches(&priv->hostlist);
209: if (host_match == ALLOW)
210: CLR(validated, FLAG_NO_HOST);
211: else
212: continue;
213: tq_foreach_rev(&priv->cmndlist, cs) {
214: runas_match = runaslist_matches(&cs->runasuserlist,
215: &cs->runasgrouplist);
216: if (runas_match == ALLOW) {
217: cmnd_match = cmnd_matches(cs->cmnd);
218: if (cmnd_match != UNSPEC) {
219: match = cmnd_match;
220: tags = &cs->tags;
221: #ifdef HAVE_SELINUX
222: /* Set role and type if not specified on command line. */
223: if (user_role == NULL)
224: user_role = cs->role ? estrdup(cs->role) : def_role;
225: if (user_type == NULL)
226: user_type = cs->type ? estrdup(cs->type) : def_type;
227: #endif /* HAVE_SELINUX */
228: goto matched2;
229: }
230: }
231: }
232: }
233: }
234: matched2:
235: if (match == ALLOW) {
236: SET(validated, VALIDATE_OK);
237: CLR(validated, VALIDATE_NOT_OK);
238: if (tags != NULL) {
239: if (tags->nopasswd != UNSPEC)
240: def_authenticate = !tags->nopasswd;
241: if (tags->noexec != UNSPEC)
242: def_noexec = tags->noexec;
243: if (tags->setenv != UNSPEC)
244: def_setenv = tags->setenv;
245: if (tags->log_input != UNSPEC)
246: def_log_input = tags->log_input;
247: if (tags->log_output != UNSPEC)
248: def_log_output = tags->log_output;
249: }
250: } else if (match == DENY) {
251: SET(validated, VALIDATE_NOT_OK);
252: CLR(validated, VALIDATE_OK);
253: if (tags != NULL && tags->nopasswd != UNSPEC)
254: def_authenticate = !tags->nopasswd;
255: }
256: restore_perms();
257: return validated;
258: }
259:
260: #define TAG_CHANGED(t) \
261: (cs->tags.t != UNSPEC && cs->tags.t != IMPLIED && cs->tags.t != tags->t)
262:
263: static void
264: sudo_file_append_cmnd(struct cmndspec *cs, struct cmndtag *tags,
265: struct lbuf *lbuf)
266: {
267: struct member *m;
268:
269: #ifdef HAVE_SELINUX
270: if (cs->role)
271: lbuf_append(lbuf, "ROLE=%s ", cs->role);
272: if (cs->type)
273: lbuf_append(lbuf, "TYPE=%s ", cs->type);
274: #endif /* HAVE_SELINUX */
275: if (TAG_CHANGED(setenv)) {
276: lbuf_append(lbuf, cs->tags.setenv ? "SETENV: " : "NOSETENV: ");
277: tags->setenv = cs->tags.setenv;
278: }
279: if (TAG_CHANGED(noexec)) {
280: lbuf_append(lbuf, cs->tags.noexec ? "NOEXEC: " : "EXEC: ");
281: tags->noexec = cs->tags.noexec;
282: }
283: if (TAG_CHANGED(nopasswd)) {
284: lbuf_append(lbuf, cs->tags.nopasswd ? "NOPASSWD: " : "PASSWD: ");
285: tags->nopasswd = cs->tags.nopasswd;
286: }
287: if (TAG_CHANGED(log_input)) {
288: lbuf_append(lbuf, cs->tags.log_input ? "LOG_INPUT: " : "NOLOG_INPUT: ");
289: tags->log_input = cs->tags.log_input;
290: }
291: if (TAG_CHANGED(log_output)) {
292: lbuf_append(lbuf, cs->tags.log_output ? "LOG_OUTPUT: " : "NOLOG_OUTPUT: ");
293: tags->log_output = cs->tags.log_output;
294: }
295: m = cs->cmnd;
296: print_member(lbuf, m->name, m->type, m->negated,
297: CMNDALIAS);
298: }
299:
300: static int
301: sudo_file_display_priv_short(struct passwd *pw, struct userspec *us,
302: struct lbuf *lbuf)
303: {
304: struct cmndspec *cs;
305: struct member *m;
306: struct privilege *priv;
307: struct cmndtag tags;
308: int nfound = 0;
309:
310: tq_foreach_fwd(&us->privileges, priv) {
311: if (hostlist_matches(&priv->hostlist) != ALLOW)
312: continue;
313: tags.noexec = UNSPEC;
314: tags.setenv = UNSPEC;
315: tags.nopasswd = UNSPEC;
316: tags.log_input = UNSPEC;
317: tags.log_output = UNSPEC;
318: lbuf_append(lbuf, " ");
319: tq_foreach_fwd(&priv->cmndlist, cs) {
320: if (cs != tq_first(&priv->cmndlist))
321: lbuf_append(lbuf, ", ");
322: lbuf_append(lbuf, "(");
323: if (!tq_empty(&cs->runasuserlist)) {
324: tq_foreach_fwd(&cs->runasuserlist, m) {
325: if (m != tq_first(&cs->runasuserlist))
326: lbuf_append(lbuf, ", ");
327: print_member(lbuf, m->name, m->type, m->negated,
328: RUNASALIAS);
329: }
330: } else if (tq_empty(&cs->runasgrouplist)) {
331: lbuf_append(lbuf, "%s", def_runas_default);
332: } else {
333: lbuf_append(lbuf, "%s", pw->pw_name);
334: }
335: if (!tq_empty(&cs->runasgrouplist)) {
336: lbuf_append(lbuf, " : ");
337: tq_foreach_fwd(&cs->runasgrouplist, m) {
338: if (m != tq_first(&cs->runasgrouplist))
339: lbuf_append(lbuf, ", ");
340: print_member(lbuf, m->name, m->type, m->negated,
341: RUNASALIAS);
342: }
343: }
344: lbuf_append(lbuf, ") ");
345: sudo_file_append_cmnd(cs, &tags, lbuf);
346: nfound++;
347: }
348: lbuf_append(lbuf, "\n");
349: }
350: return nfound;
351: }
352:
353: static int
354: sudo_file_display_priv_long(struct passwd *pw, struct userspec *us,
355: struct lbuf *lbuf)
356: {
357: struct cmndspec *cs;
358: struct member *m;
359: struct privilege *priv;
360: struct cmndtag tags;
361: int nfound = 0;
362:
363: tq_foreach_fwd(&us->privileges, priv) {
364: if (hostlist_matches(&priv->hostlist) != ALLOW)
365: continue;
366: tags.noexec = UNSPEC;
367: tags.setenv = UNSPEC;
368: tags.nopasswd = UNSPEC;
369: tags.log_input = UNSPEC;
370: tags.log_output = UNSPEC;
371: lbuf_append(lbuf, _("\nSudoers entry:\n"));
372: tq_foreach_fwd(&priv->cmndlist, cs) {
373: lbuf_append(lbuf, _(" RunAsUsers: "));
374: if (!tq_empty(&cs->runasuserlist)) {
375: tq_foreach_fwd(&cs->runasuserlist, m) {
376: if (m != tq_first(&cs->runasuserlist))
377: lbuf_append(lbuf, ", ");
378: print_member(lbuf, m->name, m->type, m->negated,
379: RUNASALIAS);
380: }
381: } else if (tq_empty(&cs->runasgrouplist)) {
382: lbuf_append(lbuf, "%s", def_runas_default);
383: } else {
384: lbuf_append(lbuf, "%s", pw->pw_name);
385: }
386: lbuf_append(lbuf, "\n");
387: if (!tq_empty(&cs->runasgrouplist)) {
388: lbuf_append(lbuf, _(" RunAsGroups: "));
389: tq_foreach_fwd(&cs->runasgrouplist, m) {
390: if (m != tq_first(&cs->runasgrouplist))
391: lbuf_append(lbuf, ", ");
392: print_member(lbuf, m->name, m->type, m->negated,
393: RUNASALIAS);
394: }
395: lbuf_append(lbuf, "\n");
396: }
397: lbuf_append(lbuf, _(" Commands:\n\t"));
398: sudo_file_append_cmnd(cs, &tags, lbuf);
399: lbuf_append(lbuf, "\n");
400: nfound++;
401: }
402: }
403: return nfound;
404: }
405:
406: int
407: sudo_file_display_privs(struct sudo_nss *nss, struct passwd *pw,
408: struct lbuf *lbuf)
409: {
410: struct userspec *us;
411: int nfound = 0;
412:
413: if (nss->handle == NULL)
414: goto done;
415:
416: tq_foreach_fwd(&userspecs, us) {
417: if (userlist_matches(pw, &us->users) != ALLOW)
418: continue;
419:
420: if (long_list)
421: nfound += sudo_file_display_priv_long(pw, us, lbuf);
422: else
423: nfound += sudo_file_display_priv_short(pw, us, lbuf);
424: }
425: done:
426: return nfound;
427: }
428:
429: /*
430: * Display matching Defaults entries for the given user on this host.
431: */
432: int
433: sudo_file_display_defaults(struct sudo_nss *nss, struct passwd *pw,
434: struct lbuf *lbuf)
435: {
436: struct defaults *d;
437: char *prefix;
438: int nfound = 0;
439:
440: if (nss->handle == NULL)
441: goto done;
442:
443: if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1]))
444: prefix = " ";
445: else
446: prefix = ", ";
447:
448: tq_foreach_fwd(&defaults, d) {
449: switch (d->type) {
450: case DEFAULTS_HOST:
451: if (hostlist_matches(&d->binding) != ALLOW)
452: continue;
453: break;
454: case DEFAULTS_USER:
455: if (userlist_matches(pw, &d->binding) != ALLOW)
456: continue;
457: break;
458: case DEFAULTS_RUNAS:
459: case DEFAULTS_CMND:
460: continue;
461: }
462: if (d->val != NULL) {
463: lbuf_append(lbuf, "%s%s%s", prefix, d->var,
464: d->op == '+' ? "+=" : d->op == '-' ? "-=" : "=");
465: if (strpbrk(d->val, " \t") != NULL) {
466: lbuf_append(lbuf, "\"");
467: lbuf_append_quoted(lbuf, "\"", "%s", d->val);
468: lbuf_append(lbuf, "\"");
469: } else
470: lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", d->val);
471: } else
472: lbuf_append(lbuf, "%s%s%s", prefix,
473: d->op == FALSE ? "!" : "", d->var);
474: prefix = ", ";
475: nfound++;
476: }
477: done:
478: return nfound;
479: }
480:
481: /*
482: * Display Defaults entries that are per-runas or per-command
483: */
484: int
485: sudo_file_display_bound_defaults(struct sudo_nss *nss, struct passwd *pw,
486: struct lbuf *lbuf)
487: {
488: int nfound = 0;
489:
490: /* XXX - should only print ones that match what the user can do. */
491: nfound += display_bound_defaults(DEFAULTS_RUNAS, lbuf);
492: nfound += display_bound_defaults(DEFAULTS_CMND, lbuf);
493:
494: return nfound;
495: }
496:
497: /*
498: * Display Defaults entries of the given type.
499: */
500: static int
501: display_bound_defaults(int dtype, struct lbuf *lbuf)
502: {
503: struct defaults *d;
504: struct member *m, *binding = NULL;
505: char *dsep;
506: int atype, nfound = 0;
507:
508: switch (dtype) {
509: case DEFAULTS_HOST:
510: atype = HOSTALIAS;
511: dsep = "@";
512: break;
513: case DEFAULTS_USER:
514: atype = USERALIAS;
515: dsep = ":";
516: break;
517: case DEFAULTS_RUNAS:
518: atype = RUNASALIAS;
519: dsep = ">";
520: break;
521: case DEFAULTS_CMND:
522: atype = CMNDALIAS;
523: dsep = "!";
524: break;
525: default:
526: return -1;
527: }
528: tq_foreach_fwd(&defaults, d) {
529: if (d->type != dtype)
530: continue;
531:
532: nfound++;
533: if (binding != tq_first(&d->binding)) {
534: binding = tq_first(&d->binding);
535: if (nfound != 1)
536: lbuf_append(lbuf, "\n");
537: lbuf_append(lbuf, " Defaults%s", dsep);
538: for (m = binding; m != NULL; m = m->next) {
539: if (m != binding)
540: lbuf_append(lbuf, ",");
541: print_member(lbuf, m->name, m->type, m->negated, atype);
542: lbuf_append(lbuf, " ");
543: }
544: } else
545: lbuf_append(lbuf, ", ");
546: if (d->val != NULL) {
547: lbuf_append(lbuf, "%s%s%s", d->var, d->op == '+' ? "+=" :
548: d->op == '-' ? "-=" : "=", d->val);
549: } else
550: lbuf_append(lbuf, "%s%s", d->op == FALSE ? "!" : "", d->var);
551: }
552:
553: return nfound;
554: }
555:
556: int
557: sudo_file_display_cmnd(struct sudo_nss *nss, struct passwd *pw)
558: {
559: struct cmndspec *cs;
560: struct member *match;
561: struct privilege *priv;
562: struct userspec *us;
563: int rval = 1;
564: int host_match, runas_match, cmnd_match;
565:
566: if (nss->handle == NULL)
567: goto done;
568:
569: match = NULL;
570: tq_foreach_rev(&userspecs, us) {
571: if (userlist_matches(pw, &us->users) != ALLOW)
572: continue;
573:
574: tq_foreach_rev(&us->privileges, priv) {
575: host_match = hostlist_matches(&priv->hostlist);
576: if (host_match != ALLOW)
577: continue;
578: tq_foreach_rev(&priv->cmndlist, cs) {
579: runas_match = runaslist_matches(&cs->runasuserlist,
580: &cs->runasgrouplist);
581: if (runas_match == ALLOW) {
582: cmnd_match = cmnd_matches(cs->cmnd);
583: if (cmnd_match != UNSPEC) {
584: match = host_match && runas_match ? cs->cmnd : NULL;
585: goto matched;
586: }
587: }
588: }
589: }
590: }
591: matched:
592: if (match != NULL && !match->negated) {
593: sudo_printf(SUDO_CONV_INFO_MSG, "%s%s%s\n",
594: safe_cmnd, user_args ? " " : "", user_args ? user_args : "");
595: rval = 0;
596: }
597: done:
598: return rval;
599: }
600:
601: /*
602: * Print the contents of a struct member to stdout
603: */
604: static void
605: _print_member(struct lbuf *lbuf, char *name, int type, int negated,
606: int alias_type)
607: {
608: struct alias *a;
609: struct member *m;
610: struct sudo_command *c;
611:
612: switch (type) {
613: case ALL:
614: lbuf_append(lbuf, "%sALL", negated ? "!" : "");
615: break;
616: case COMMAND:
617: c = (struct sudo_command *) name;
618: if (negated)
619: lbuf_append(lbuf, "!");
620: lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->cmnd);
621: if (c->args) {
622: lbuf_append(lbuf, " ");
623: lbuf_append_quoted(lbuf, SUDOERS_QUOTED, "%s", c->args);
624: }
625: break;
626: case ALIAS:
627: if ((a = alias_find(name, alias_type)) != NULL) {
628: tq_foreach_fwd(&a->members, m) {
629: if (m != tq_first(&a->members))
630: lbuf_append(lbuf, ", ");
631: _print_member(lbuf, m->name, m->type,
632: negated ? !m->negated : m->negated, alias_type);
633: }
634: break;
635: }
636: /* FALLTHROUGH */
637: default:
638: lbuf_append(lbuf, "%s%s", negated ? "!" : "", name);
639: break;
640: }
641: }
642:
643: static void
644: print_member(struct lbuf *lbuf, char *name, int type, int negated,
645: int alias_type)
646: {
647: alias_seqno++;
648: _print_member(lbuf, name, type, negated, alias_type);
649: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>