Annotation of embedaddon/sudo/plugins/sudoers/gram.y, revision 1.1.1.1
1.1 misho 1: %{
2: /*
3: * Copyright (c) 1996, 1998-2005, 2007-2011
4: * Todd C. Miller <Todd.Miller@courtesan.com>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
18: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19: *
20: * Sponsored in part by the Defense Advanced Research Projects
21: * Agency (DARPA) and Air Force Research Laboratory, Air Force
22: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
23: */
24:
25: #include <config.h>
26:
27: #include <sys/types.h>
28: #include <sys/param.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: #if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
48: # include <alloca.h>
49: #endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
50: #include <limits.h>
51:
52: #include "sudoers.h" /* XXX */
53: #include "parse.h"
54: #include "toke.h"
55:
56: /*
57: * We must define SIZE_MAX for yacc's skeleton.c.
58: * If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
59: * could be signed (as it is on SunOS 4.x).
60: */
61: #ifndef SIZE_MAX
62: # ifdef SIZE_T_MAX
63: # define SIZE_MAX SIZE_T_MAX
64: # else
65: # define SIZE_MAX INT_MAX
66: # endif /* SIZE_T_MAX */
67: #endif /* SIZE_MAX */
68:
69: /*
70: * Globals
71: */
72: extern int sudolineno;
73: extern char *sudoers;
74: static int verbose = FALSE;
75: int parse_error = FALSE;
76: int pedantic = FALSE;
77: int errorlineno = -1;
78: char *errorfile = NULL;
79:
80: struct defaults_list defaults;
81: struct userspec_list userspecs;
82:
83: /*
84: * Local protoypes
85: */
86: static void add_defaults(int, struct member *, struct defaults *);
87: static void add_userspec(struct member *, struct privilege *);
88: static struct defaults *new_default(char *, char *, int);
89: static struct member *new_member(char *, int);
90: void yyerror(const char *);
91:
92: void
93: yyerror(const char *s)
94: {
95: /* Save the line the first error occurred on. */
96: if (errorlineno == -1) {
97: errorlineno = sudolineno ? sudolineno - 1 : 0;
98: errorfile = estrdup(sudoers);
99: }
100: if (trace_print != NULL) {
101: LEXTRACE("<*> ");
102: } else if (verbose && s != NULL) {
103: warningx(_(">>> %s: %s near line %d <<<"), sudoers, s,
104: sudolineno ? sudolineno - 1 : 0);
105: }
106: parse_error = TRUE;
107: }
108: %}
109:
110: %union {
111: struct cmndspec *cmndspec;
112: struct defaults *defaults;
113: struct member *member;
114: struct runascontainer *runas;
115: struct privilege *privilege;
116: struct sudo_command command;
117: struct cmndtag tag;
118: struct selinux_info seinfo;
119: char *string;
120: int tok;
121: }
122:
123: %start file /* special start symbol */
124: %token <command> COMMAND /* absolute pathname w/ optional args */
125: %token <string> ALIAS /* an UPPERCASE alias name */
126: %token <string> DEFVAR /* a Defaults variable name */
127: %token <string> NTWKADDR /* ipv4 or ipv6 address */
128: %token <string> NETGROUP /* a netgroup (+NAME) */
129: %token <string> USERGROUP /* a usergroup (%NAME) */
130: %token <string> WORD /* a word */
131: %token <tok> DEFAULTS /* Defaults entry */
132: %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
133: %token <tok> DEFAULTS_USER /* User-specific defaults entry */
134: %token <tok> DEFAULTS_RUNAS /* Runas-specific defaults entry */
135: %token <tok> DEFAULTS_CMND /* Command-specific defaults entry */
136: %token <tok> NOPASSWD /* no passwd req for command */
137: %token <tok> PASSWD /* passwd req for command (default) */
138: %token <tok> NOEXEC /* preload dummy execve() for cmnd */
139: %token <tok> EXEC /* don't preload dummy execve() */
140: %token <tok> SETENV /* user may set environment for cmnd */
141: %token <tok> NOSETENV /* user may not set environment */
142: %token <tok> LOG_INPUT /* log user's cmnd input */
143: %token <tok> NOLOG_INPUT /* don't log user's cmnd input */
144: %token <tok> LOG_OUTPUT /* log cmnd output */
145: %token <tok> NOLOG_OUTPUT /* don't log cmnd output */
146: %token <tok> ALL /* ALL keyword */
147: %token <tok> COMMENT /* comment and/or carriage return */
148: %token <tok> HOSTALIAS /* Host_Alias keyword */
149: %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
150: %token <tok> USERALIAS /* User_Alias keyword */
151: %token <tok> RUNASALIAS /* Runas_Alias keyword */
152: %token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
153: %token <tok> '(' ')' /* runas tokens */
154: %token <tok> ERROR
155: %token <tok> TYPE /* SELinux type */
156: %token <tok> ROLE /* SELinux role */
157:
158: %type <cmndspec> cmndspec
159: %type <cmndspec> cmndspeclist
160: %type <defaults> defaults_entry
161: %type <defaults> defaults_list
162: %type <member> cmnd
163: %type <member> opcmnd
164: %type <member> cmndlist
165: %type <member> host
166: %type <member> hostlist
167: %type <member> ophost
168: %type <member> opuser
169: %type <member> user
170: %type <member> userlist
171: %type <member> opgroup
172: %type <member> group
173: %type <member> grouplist
174: %type <runas> runasspec
175: %type <runas> runaslist
176: %type <privilege> privilege
177: %type <privilege> privileges
178: %type <tag> cmndtag
179: %type <seinfo> selinux
180: %type <string> rolespec
181: %type <string> typespec
182:
183: %%
184:
185: file : { ; }
186: | line
187: ;
188:
189: line : entry
190: | line entry
191: ;
192:
193: entry : COMMENT {
194: ;
195: }
196: | error COMMENT {
197: yyerrok;
198: }
199: | userlist privileges {
200: add_userspec($1, $2);
201: }
202: | USERALIAS useraliases {
203: ;
204: }
205: | HOSTALIAS hostaliases {
206: ;
207: }
208: | CMNDALIAS cmndaliases {
209: ;
210: }
211: | RUNASALIAS runasaliases {
212: ;
213: }
214: | DEFAULTS defaults_list {
215: add_defaults(DEFAULTS, NULL, $2);
216: }
217: | DEFAULTS_USER userlist defaults_list {
218: add_defaults(DEFAULTS_USER, $2, $3);
219: }
220: | DEFAULTS_RUNAS userlist defaults_list {
221: add_defaults(DEFAULTS_RUNAS, $2, $3);
222: }
223: | DEFAULTS_HOST hostlist defaults_list {
224: add_defaults(DEFAULTS_HOST, $2, $3);
225: }
226: | DEFAULTS_CMND cmndlist defaults_list {
227: add_defaults(DEFAULTS_CMND, $2, $3);
228: }
229: ;
230:
231: defaults_list : defaults_entry
232: | defaults_list ',' defaults_entry {
233: list_append($1, $3);
234: $$ = $1;
235: }
236: ;
237:
238: defaults_entry : DEFVAR {
239: $$ = new_default($1, NULL, TRUE);
240: }
241: | '!' DEFVAR {
242: $$ = new_default($2, NULL, FALSE);
243: }
244: | DEFVAR '=' WORD {
245: $$ = new_default($1, $3, TRUE);
246: }
247: | DEFVAR '+' WORD {
248: $$ = new_default($1, $3, '+');
249: }
250: | DEFVAR '-' WORD {
251: $$ = new_default($1, $3, '-');
252: }
253: ;
254:
255: privileges : privilege
256: | privileges ':' privilege {
257: list_append($1, $3);
258: $$ = $1;
259: }
260: ;
261:
262: privilege : hostlist '=' cmndspeclist {
263: struct privilege *p = emalloc(sizeof(*p));
264: list2tq(&p->hostlist, $1);
265: list2tq(&p->cmndlist, $3);
266: p->prev = p;
267: p->next = NULL;
268: $$ = p;
269: }
270: ;
271:
272: ophost : host {
273: $$ = $1;
274: $$->negated = FALSE;
275: }
276: | '!' host {
277: $$ = $2;
278: $$->negated = TRUE;
279: }
280: ;
281:
282: host : ALIAS {
283: $$ = new_member($1, ALIAS);
284: }
285: | ALL {
286: $$ = new_member(NULL, ALL);
287: }
288: | NETGROUP {
289: $$ = new_member($1, NETGROUP);
290: }
291: | NTWKADDR {
292: $$ = new_member($1, NTWKADDR);
293: }
294: | WORD {
295: $$ = new_member($1, WORD);
296: }
297: ;
298:
299: cmndspeclist : cmndspec
300: | cmndspeclist ',' cmndspec {
301: list_append($1, $3);
302: #ifdef HAVE_SELINUX
303: /* propagate role and type */
304: if ($3->role == NULL)
305: $3->role = $3->prev->role;
306: if ($3->type == NULL)
307: $3->type = $3->prev->type;
308: #endif /* HAVE_SELINUX */
309: /* propagate tags and runas list */
310: if ($3->tags.nopasswd == UNSPEC)
311: $3->tags.nopasswd = $3->prev->tags.nopasswd;
312: if ($3->tags.noexec == UNSPEC)
313: $3->tags.noexec = $3->prev->tags.noexec;
314: if ($3->tags.setenv == UNSPEC &&
315: $3->prev->tags.setenv != IMPLIED)
316: $3->tags.setenv = $3->prev->tags.setenv;
317: if ($3->tags.log_input == UNSPEC)
318: $3->tags.log_input = $3->prev->tags.log_input;
319: if ($3->tags.log_output == UNSPEC)
320: $3->tags.log_output = $3->prev->tags.log_output;
321: if ((tq_empty(&$3->runasuserlist) &&
322: tq_empty(&$3->runasgrouplist)) &&
323: (!tq_empty(&$3->prev->runasuserlist) ||
324: !tq_empty(&$3->prev->runasgrouplist))) {
325: $3->runasuserlist = $3->prev->runasuserlist;
326: $3->runasgrouplist = $3->prev->runasgrouplist;
327: }
328: $$ = $1;
329: }
330: ;
331:
332: cmndspec : runasspec selinux cmndtag opcmnd {
333: struct cmndspec *cs = emalloc(sizeof(*cs));
334: if ($1 != NULL) {
335: list2tq(&cs->runasuserlist, $1->runasusers);
336: list2tq(&cs->runasgrouplist, $1->runasgroups);
337: efree($1);
338: } else {
339: tq_init(&cs->runasuserlist);
340: tq_init(&cs->runasgrouplist);
341: }
342: #ifdef HAVE_SELINUX
343: cs->role = $2.role;
344: cs->type = $2.type;
345: #endif
346: cs->tags = $3;
347: cs->cmnd = $4;
348: cs->prev = cs;
349: cs->next = NULL;
350: /* sudo "ALL" implies the SETENV tag */
351: if (cs->cmnd->type == ALL && !cs->cmnd->negated &&
352: cs->tags.setenv == UNSPEC)
353: cs->tags.setenv = IMPLIED;
354: $$ = cs;
355: }
356: ;
357:
358: opcmnd : cmnd {
359: $$ = $1;
360: $$->negated = FALSE;
361: }
362: | '!' cmnd {
363: $$ = $2;
364: $$->negated = TRUE;
365: }
366: ;
367:
368: rolespec : ROLE '=' WORD {
369: $$ = $3;
370: }
371: ;
372:
373: typespec : TYPE '=' WORD {
374: $$ = $3;
375: }
376: ;
377:
378: selinux : /* empty */ {
379: $$.role = NULL;
380: $$.type = NULL;
381: }
382: | rolespec {
383: $$.role = $1;
384: $$.type = NULL;
385: }
386: | typespec {
387: $$.type = $1;
388: $$.role = NULL;
389: }
390: | rolespec typespec {
391: $$.role = $1;
392: $$.type = $2;
393: }
394: | typespec rolespec {
395: $$.type = $1;
396: $$.role = $2;
397: }
398: ;
399:
400: runasspec : /* empty */ {
401: $$ = NULL;
402: }
403: | '(' runaslist ')' {
404: $$ = $2;
405: }
406: ;
407:
408: runaslist : userlist {
409: $$ = emalloc(sizeof(struct runascontainer));
410: $$->runasusers = $1;
411: $$->runasgroups = NULL;
412: }
413: | userlist ':' grouplist {
414: $$ = emalloc(sizeof(struct runascontainer));
415: $$->runasusers = $1;
416: $$->runasgroups = $3;
417: }
418: | ':' grouplist {
419: $$ = emalloc(sizeof(struct runascontainer));
420: $$->runasusers = NULL;
421: $$->runasgroups = $2;
422: }
423: ;
424:
425: cmndtag : /* empty */ {
426: $$.nopasswd = $$.noexec = $$.setenv =
427: $$.log_input = $$.log_output = UNSPEC;
428: }
429: | cmndtag NOPASSWD {
430: $$.nopasswd = TRUE;
431: }
432: | cmndtag PASSWD {
433: $$.nopasswd = FALSE;
434: }
435: | cmndtag NOEXEC {
436: $$.noexec = TRUE;
437: }
438: | cmndtag EXEC {
439: $$.noexec = FALSE;
440: }
441: | cmndtag SETENV {
442: $$.setenv = TRUE;
443: }
444: | cmndtag NOSETENV {
445: $$.setenv = FALSE;
446: }
447: | cmndtag LOG_INPUT {
448: $$.log_input = TRUE;
449: }
450: | cmndtag NOLOG_INPUT {
451: $$.log_input = FALSE;
452: }
453: | cmndtag LOG_OUTPUT {
454: $$.log_output = TRUE;
455: }
456: | cmndtag NOLOG_OUTPUT {
457: $$.log_output = FALSE;
458: }
459: ;
460:
461: cmnd : ALL {
462: $$ = new_member(NULL, ALL);
463: }
464: | ALIAS {
465: $$ = new_member($1, ALIAS);
466: }
467: | COMMAND {
468: struct sudo_command *c = emalloc(sizeof(*c));
469: c->cmnd = $1.cmnd;
470: c->args = $1.args;
471: $$ = new_member((char *)c, COMMAND);
472: }
473: ;
474:
475: hostaliases : hostalias
476: | hostaliases ':' hostalias
477: ;
478:
479: hostalias : ALIAS '=' hostlist {
480: char *s;
481: if ((s = alias_add($1, HOSTALIAS, $3)) != NULL) {
482: yyerror(s);
483: YYERROR;
484: }
485: }
486: ;
487:
488: hostlist : ophost
489: | hostlist ',' ophost {
490: list_append($1, $3);
491: $$ = $1;
492: }
493: ;
494:
495: cmndaliases : cmndalias
496: | cmndaliases ':' cmndalias
497: ;
498:
499: cmndalias : ALIAS '=' cmndlist {
500: char *s;
501: if ((s = alias_add($1, CMNDALIAS, $3)) != NULL) {
502: yyerror(s);
503: YYERROR;
504: }
505: }
506: ;
507:
508: cmndlist : opcmnd
509: | cmndlist ',' opcmnd {
510: list_append($1, $3);
511: $$ = $1;
512: }
513: ;
514:
515: runasaliases : runasalias
516: | runasaliases ':' runasalias
517: ;
518:
519: runasalias : ALIAS '=' userlist {
520: char *s;
521: if ((s = alias_add($1, RUNASALIAS, $3)) != NULL) {
522: yyerror(s);
523: YYERROR;
524: }
525: }
526: ;
527:
528: useraliases : useralias
529: | useraliases ':' useralias
530: ;
531:
532: useralias : ALIAS '=' userlist {
533: char *s;
534: if ((s = alias_add($1, USERALIAS, $3)) != NULL) {
535: yyerror(s);
536: YYERROR;
537: }
538: }
539: ;
540:
541: userlist : opuser
542: | userlist ',' opuser {
543: list_append($1, $3);
544: $$ = $1;
545: }
546: ;
547:
548: opuser : user {
549: $$ = $1;
550: $$->negated = FALSE;
551: }
552: | '!' user {
553: $$ = $2;
554: $$->negated = TRUE;
555: }
556: ;
557:
558: user : ALIAS {
559: $$ = new_member($1, ALIAS);
560: }
561: | ALL {
562: $$ = new_member(NULL, ALL);
563: }
564: | NETGROUP {
565: $$ = new_member($1, NETGROUP);
566: }
567: | USERGROUP {
568: $$ = new_member($1, USERGROUP);
569: }
570: | WORD {
571: $$ = new_member($1, WORD);
572: }
573: ;
574:
575: grouplist : opgroup
576: | grouplist ',' opgroup {
577: list_append($1, $3);
578: $$ = $1;
579: }
580: ;
581:
582: opgroup : group {
583: $$ = $1;
584: $$->negated = FALSE;
585: }
586: | '!' group {
587: $$ = $2;
588: $$->negated = TRUE;
589: }
590: ;
591:
592: group : ALIAS {
593: $$ = new_member($1, ALIAS);
594: }
595: | ALL {
596: $$ = new_member(NULL, ALL);
597: }
598: | WORD {
599: $$ = new_member($1, WORD);
600: }
601: ;
602:
603: %%
604: static struct defaults *
605: new_default(char *var, char *val, int op)
606: {
607: struct defaults *d;
608:
609: d = emalloc(sizeof(struct defaults));
610: d->var = var;
611: d->val = val;
612: tq_init(&d->binding);
613: d->type = 0;
614: d->op = op;
615: d->prev = d;
616: d->next = NULL;
617:
618: return d;
619: }
620:
621: static struct member *
622: new_member(char *name, int type)
623: {
624: struct member *m;
625:
626: m = emalloc(sizeof(struct member));
627: m->name = name;
628: m->type = type;
629: m->prev = m;
630: m->next = NULL;
631:
632: return m;
633: }
634:
635: /*
636: * Add a list of defaults structures to the defaults list.
637: * The binding, if non-NULL, specifies a list of hosts, users, or
638: * runas users the entries apply to (specified by the type).
639: */
640: static void
641: add_defaults(int type, struct member *bmem, struct defaults *defs)
642: {
643: struct defaults *d;
644: struct member_list binding;
645:
646: /*
647: * We can only call list2tq once on bmem as it will zero
648: * out the prev pointer when it consumes bmem.
649: */
650: list2tq(&binding, bmem);
651:
652: /*
653: * Set type and binding (who it applies to) for new entries.
654: */
655: for (d = defs; d != NULL; d = d->next) {
656: d->type = type;
657: d->binding = binding;
658: }
659: tq_append(&defaults, defs);
660: }
661:
662: /*
663: * Allocate a new struct userspec, populate it, and insert it at the
664: * and of the userspecs list.
665: */
666: static void
667: add_userspec(struct member *members, struct privilege *privs)
668: {
669: struct userspec *u;
670:
671: u = emalloc(sizeof(*u));
672: list2tq(&u->users, members);
673: list2tq(&u->privileges, privs);
674: u->prev = u;
675: u->next = NULL;
676: tq_append(&userspecs, u);
677: }
678:
679: /*
680: * Free up space used by data structures from a previous parser run and sets
681: * the current sudoers file to path.
682: */
683: void
684: init_parser(const char *path, int quiet)
685: {
686: struct defaults *d;
687: struct member *m, *binding;
688: struct userspec *us;
689: struct privilege *priv;
690: struct cmndspec *cs;
691: struct sudo_command *c;
692:
693: while ((us = tq_pop(&userspecs)) != NULL) {
694: while ((m = tq_pop(&us->users)) != NULL) {
695: efree(m->name);
696: efree(m);
697: }
698: while ((priv = tq_pop(&us->privileges)) != NULL) {
699: struct member *runasuser = NULL, *runasgroup = NULL;
700: #ifdef HAVE_SELINUX
701: char *role = NULL, *type = NULL;
702: #endif /* HAVE_SELINUX */
703:
704: while ((m = tq_pop(&priv->hostlist)) != NULL) {
705: efree(m->name);
706: efree(m);
707: }
708: while ((cs = tq_pop(&priv->cmndlist)) != NULL) {
709: #ifdef HAVE_SELINUX
710: /* Only free the first instance of a role/type. */
711: if (cs->role != role) {
712: role = cs->role;
713: efree(cs->role);
714: }
715: if (cs->type != type) {
716: type = cs->type;
717: efree(cs->type);
718: }
719: #endif /* HAVE_SELINUX */
720: if (tq_last(&cs->runasuserlist) != runasuser) {
721: runasuser = tq_last(&cs->runasuserlist);
722: while ((m = tq_pop(&cs->runasuserlist)) != NULL) {
723: efree(m->name);
724: efree(m);
725: }
726: }
727: if (tq_last(&cs->runasgrouplist) != runasgroup) {
728: runasgroup = tq_last(&cs->runasgrouplist);
729: while ((m = tq_pop(&cs->runasgrouplist)) != NULL) {
730: efree(m->name);
731: efree(m);
732: }
733: }
734: if (cs->cmnd->type == COMMAND) {
735: c = (struct sudo_command *) cs->cmnd->name;
736: efree(c->cmnd);
737: efree(c->args);
738: }
739: efree(cs->cmnd->name);
740: efree(cs->cmnd);
741: efree(cs);
742: }
743: efree(priv);
744: }
745: efree(us);
746: }
747: tq_init(&userspecs);
748:
749: binding = NULL;
750: while ((d = tq_pop(&defaults)) != NULL) {
751: if (tq_last(&d->binding) != binding) {
752: binding = tq_last(&d->binding);
753: while ((m = tq_pop(&d->binding)) != NULL) {
754: if (m->type == COMMAND) {
755: c = (struct sudo_command *) m->name;
756: efree(c->cmnd);
757: efree(c->args);
758: }
759: efree(m->name);
760: efree(m);
761: }
762: }
763: efree(d->var);
764: efree(d->val);
765: efree(d);
766: }
767: tq_init(&defaults);
768:
769: init_aliases();
770:
771: init_lexer();
772:
773: efree(sudoers);
774: sudoers = path ? estrdup(path) : NULL;
775:
776: parse_error = FALSE;
777: errorlineno = -1;
778: errorfile = NULL;
779: verbose = !quiet;
780: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>