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