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