Annotation of src/usr.bin/sudo/parse.yacc, Revision 1.17
1.1 millert 1: %{
2: /*
1.12 millert 3: * Copyright (c) 1996, 1998-2004, 2007
4: * Todd C. Miller <Todd.Miller@courtesan.com>
1.1 millert 5: *
1.10 millert 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.
1.1 millert 9: *
1.10 millert 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.
1.1 millert 17: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
18: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.9 millert 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.
1.1 millert 23: */
24:
25: /*
26: * XXX - the whole opFOO naming thing is somewhat bogus.
27: *
28: * XXX - the way things are stored for printmatches is stupid,
29: * they should be stored as elements in an array and then
30: * list_matches() can format things the way it wants.
31: */
32:
1.12 millert 33: #include <config.h>
1.6 millert 34:
35: #include <sys/types.h>
36: #include <sys/param.h>
1.1 millert 37: #include <stdio.h>
38: #ifdef STDC_HEADERS
1.6 millert 39: # include <stdlib.h>
40: # include <stddef.h>
41: #else
42: # ifdef HAVE_STDLIB_H
43: # include <stdlib.h>
44: # endif
1.1 millert 45: #endif /* STDC_HEADERS */
1.6 millert 46: #ifdef HAVE_STRING_H
47: # include <string.h>
48: #else
49: # ifdef HAVE_STRINGS_H
50: # include <strings.h>
51: # endif
52: #endif /* HAVE_STRING_H */
1.1 millert 53: #ifdef HAVE_UNISTD_H
1.6 millert 54: # include <unistd.h>
1.1 millert 55: #endif /* HAVE_UNISTD_H */
56: #include <pwd.h>
57: #if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
1.6 millert 58: # include <alloca.h>
1.1 millert 59: #endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
60: #ifdef HAVE_LSEARCH
1.6 millert 61: # include <search.h>
1.1 millert 62: #endif /* HAVE_LSEARCH */
63:
64: #include "sudo.h"
65: #include "parse.h"
66:
67: #ifndef HAVE_LSEARCH
68: #include "emul/search.h"
69: #endif /* HAVE_LSEARCH */
70:
71: #ifndef lint
1.17 ! millert 72: __unused static const char rcsid[] = "$Sudo: parse.yacc,v 1.204.2.7 2007/09/13 23:06:51 millert Exp $";
1.1 millert 73: #endif /* lint */
74:
75: /*
76: * Globals
77: */
78: extern int sudolineno, parse_error;
79: int errorlineno = -1;
80: int clearaliases = TRUE;
81: int printmatches = FALSE;
82: int pedantic = FALSE;
1.3 millert 83: int keepall = FALSE;
1.6 millert 84: int quiet = FALSE;
1.10 millert 85: int used_runas = FALSE;
1.1 millert 86:
87: /*
88: * Alias types
89: */
90: #define HOST_ALIAS 1
91: #define CMND_ALIAS 2
92: #define USER_ALIAS 3
93: #define RUNAS_ALIAS 4
94:
1.10 millert 95: #define SETMATCH(_var, _val) do { \
96: if ((_var) == UNSPEC || (_val) != NOMATCH) \
97: (_var) = (_val); \
98: } while (0)
99:
100: #define SETNMATCH(_var, _val) do { \
101: if ((_val) != NOMATCH) \
102: (_var) = ! (_val); \
103: else if ((_var) == UNSPEC) \
104: (_var) = NOMATCH; \
105: } while (0)
106:
1.1 millert 107: /*
108: * The matching stack, initial space allocated in init_parser().
109: */
110: struct matchstack *match;
111: int top = 0, stacksize = 0;
112:
113: #define push \
114: do { \
115: if (top >= stacksize) { \
116: while ((stacksize += STACKINCREMENT) < top); \
1.8 millert 117: match = (struct matchstack *) erealloc3(match, stacksize, sizeof(struct matchstack)); \
1.1 millert 118: } \
1.10 millert 119: match[top].user = UNSPEC; \
120: match[top].cmnd = UNSPEC; \
121: match[top].host = UNSPEC; \
122: match[top].runas = UNSPEC; \
123: match[top].nopass = def_authenticate ? UNSPEC : TRUE; \
124: match[top].noexec = def_noexec ? TRUE : UNSPEC; \
1.12 millert 125: match[top].setenv = def_setenv ? TRUE : UNSPEC; \
1.1 millert 126: top++; \
127: } while (0)
128:
129: #define pushcp \
130: do { \
131: if (top >= stacksize) { \
132: while ((stacksize += STACKINCREMENT) < top); \
1.8 millert 133: match = (struct matchstack *) erealloc3(match, stacksize, sizeof(struct matchstack)); \
1.1 millert 134: } \
135: match[top].user = match[top-1].user; \
136: match[top].cmnd = match[top-1].cmnd; \
137: match[top].host = match[top-1].host; \
138: match[top].runas = match[top-1].runas; \
139: match[top].nopass = match[top-1].nopass; \
1.10 millert 140: match[top].noexec = match[top-1].noexec; \
1.12 millert 141: match[top].setenv = match[top-1].setenv; \
1.1 millert 142: top++; \
143: } while (0)
144:
145: #define pop \
1.10 millert 146: do { \
1.1 millert 147: if (top == 0) \
148: yyerror("matching stack underflow"); \
149: else \
150: top--; \
1.10 millert 151: } while (0)
152:
153:
154: /*
155: * For testing if foo_matches variable was set to TRUE or FALSE
156: */
157: #define MATCHED(_v) ((_v) >= 0)
1.1 millert 158:
159: /*
160: * Shortcuts for append()
161: */
162: #define append_cmnd(s, p) append(s, &cm_list[cm_list_len].cmnd, \
163: &cm_list[cm_list_len].cmnd_len, &cm_list[cm_list_len].cmnd_size, p)
164:
165: #define append_runas(s, p) append(s, &cm_list[cm_list_len].runas, \
166: &cm_list[cm_list_len].runas_len, &cm_list[cm_list_len].runas_size, p)
167:
168: #define append_entries(s, p) append(s, &ga_list[ga_list_len-1].entries, \
169: &ga_list[ga_list_len-1].entries_len, \
170: &ga_list[ga_list_len-1].entries_size, p)
171:
172: /*
173: * The stack for printmatches. A list of allowed commands for the user.
174: */
175: static struct command_match *cm_list = NULL;
176: static size_t cm_list_len = 0, cm_list_size = 0;
177:
178: /*
179: * List of Cmnd_Aliases and expansions for `sudo -l'
180: */
181: static int in_alias = FALSE;
182: static size_t ga_list_len = 0, ga_list_size = 0;
183: static struct generic_alias *ga_list = NULL;
184:
185: /*
186: * Does this Defaults list pertain to this user?
187: */
1.10 millert 188: static int defaults_matches = FALSE;
1.1 millert 189:
190: /*
191: * Local protoypes
192: */
193: static int add_alias __P((char *, int, int));
194: static void append __P((char *, char **, size_t *, size_t *, char *));
195: static void expand_ga_list __P((void));
196: static void expand_match_list __P((void));
197: static aliasinfo *find_alias __P((char *, int));
1.16 millert 198: static void more_aliases __P((void));
1.1 millert 199: void init_parser __P((void));
200: void yyerror __P((char *));
201:
202: void
203: yyerror(s)
204: char *s;
205: {
1.5 mpech 206: /* Save the line the first error occurred on. */
1.1 millert 207: if (errorlineno == -1)
208: errorlineno = sudolineno ? sudolineno - 1 : 0;
1.6 millert 209: if (s && !quiet) {
1.1 millert 210: #ifndef TRACELEXER
211: (void) fprintf(stderr, ">>> sudoers file: %s, line %d <<<\n", s,
212: sudolineno ? sudolineno - 1 : 0);
213: #else
214: (void) fprintf(stderr, "<*> ");
215: #endif
216: }
217: parse_error = TRUE;
218: }
219: %}
220:
221: %union {
222: char *string;
223: int BOOLEAN;
224: struct sudo_command command;
225: int tok;
226: }
227:
228: %start file /* special start symbol */
229: %token <command> COMMAND /* absolute pathname w/ optional args */
230: %token <string> ALIAS /* an UPPERCASE alias name */
1.6 millert 231: %token <string> DEFVAR /* a Defaults variable name */
1.13 millert 232: %token <string> NTWKADDR /* w.x.y.z or ipv6 address */
1.1 millert 233: %token <string> NETGROUP /* a netgroup (+NAME) */
234: %token <string> USERGROUP /* a usergroup (%NAME) */
235: %token <string> WORD /* a word */
236: %token <tok> DEFAULTS /* Defaults entry */
237: %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
238: %token <tok> DEFAULTS_USER /* User-specific defaults entry */
1.8 millert 239: %token <tok> DEFAULTS_RUNAS /* Runas-specific defaults entry */
1.1 millert 240: %token <tok> RUNAS /* ( runas_list ) */
241: %token <tok> NOPASSWD /* no passwd req for command */
242: %token <tok> PASSWD /* passwd req for command (default) */
1.10 millert 243: %token <tok> NOEXEC /* preload dummy execve() for cmnd */
244: %token <tok> EXEC /* don't preload dummy execve() */
1.12 millert 245: %token <tok> SETENV /* user may set environment for cmnd */
246: %token <tok> NOSETENV /* user may not set environment */
1.1 millert 247: %token <tok> ALL /* ALL keyword */
248: %token <tok> COMMENT /* comment and/or carriage return */
249: %token <tok> HOSTALIAS /* Host_Alias keyword */
250: %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
251: %token <tok> USERALIAS /* User_Alias keyword */
252: %token <tok> RUNASALIAS /* Runas_Alias keyword */
1.6 millert 253: %token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
1.1 millert 254: %token <tok> ERROR
255:
256: /*
1.10 millert 257: * NOTE: these are not true booleans as there are actually 4 possible values:
1.1 millert 258: * 1) TRUE (positive match)
259: * 0) FALSE (negative match due to a '!' somewhere)
1.10 millert 260: * -1) NOMATCH (don't change the value of *_matches)
261: * -2) UNSPEC (uninitialized value)
1.1 millert 262: */
263: %type <BOOLEAN> cmnd
264: %type <BOOLEAN> host
265: %type <BOOLEAN> runasuser
1.2 millert 266: %type <BOOLEAN> oprunasuser
267: %type <BOOLEAN> runaslist
1.1 millert 268: %type <BOOLEAN> user
269:
270: %%
271:
272: file : entry
273: | file entry
274: ;
275:
276: entry : COMMENT
277: { ; }
278: | error COMMENT
279: { yyerrok; }
280: | { push; } userlist privileges {
281: while (top && user_matches != TRUE)
282: pop;
283: }
284: | USERALIAS useraliases
285: { ; }
286: | HOSTALIAS hostaliases
287: { ; }
288: | CMNDALIAS cmndaliases
289: { ; }
290: | RUNASALIAS runasaliases
291: { ; }
292: | defaults_line
293: { ; }
294: ;
295:
296: defaults_line : defaults_type defaults_list
1.8 millert 297: ;
1.1 millert 298:
299: defaults_type : DEFAULTS {
300: defaults_matches = TRUE;
301: }
302: | DEFAULTS_USER { push; } userlist {
303: defaults_matches = user_matches;
304: pop;
305: }
1.8 millert 306: | DEFAULTS_RUNAS { push; } runaslist {
307: defaults_matches = $3 == TRUE;
308: pop;
309: }
1.1 millert 310: | DEFAULTS_HOST { push; } hostlist {
311: defaults_matches = host_matches;
312: pop;
313: }
314: ;
315:
316: defaults_list : defaults_entry
317: | defaults_entry ',' defaults_list
1.8 millert 318: ;
1.1 millert 319:
1.6 millert 320: defaults_entry : DEFVAR {
1.4 millert 321: if (defaults_matches == TRUE &&
1.6 millert 322: !set_default($1, NULL, TRUE)) {
1.1 millert 323: yyerror(NULL);
324: YYERROR;
325: }
1.12 millert 326: efree($1);
1.1 millert 327: }
1.6 millert 328: | '!' DEFVAR {
1.4 millert 329: if (defaults_matches == TRUE &&
1.6 millert 330: !set_default($2, NULL, FALSE)) {
1.1 millert 331: yyerror(NULL);
332: YYERROR;
333: }
1.12 millert 334: efree($2);
1.1 millert 335: }
1.6 millert 336: | DEFVAR '=' WORD {
337: if (defaults_matches == TRUE &&
338: !set_default($1, $3, TRUE)) {
339: yyerror(NULL);
340: YYERROR;
341: }
1.12 millert 342: efree($1);
343: efree($3);
1.6 millert 344: }
345: | DEFVAR '+' WORD {
346: if (defaults_matches == TRUE &&
347: !set_default($1, $3, '+')) {
348: yyerror(NULL);
349: YYERROR;
350: }
1.12 millert 351: efree($1);
352: efree($3);
1.6 millert 353: }
354: | DEFVAR '-' WORD {
1.4 millert 355: if (defaults_matches == TRUE &&
1.6 millert 356: !set_default($1, $3, '-')) {
1.1 millert 357: yyerror(NULL);
358: YYERROR;
359: }
1.12 millert 360: efree($1);
361: efree($3);
1.1 millert 362: }
1.8 millert 363: ;
1.1 millert 364:
365: privileges : privilege
366: | privileges ':' privilege
367: ;
368:
369: privilege : hostlist '=' cmndspeclist {
370: /*
371: * We already did a push if necessary in
372: * cmndspec so just reset some values so
373: * the next 'privilege' gets a clean slate.
374: */
1.10 millert 375: host_matches = UNSPEC;
376: runas_matches = UNSPEC;
377: no_passwd = def_authenticate ? UNSPEC : TRUE;
378: no_execve = def_noexec ? TRUE : UNSPEC;
1.12 millert 379: setenv_ok = def_setenv ? TRUE : UNSPEC;
1.1 millert 380: }
381: ;
382:
383: ophost : host {
1.10 millert 384: SETMATCH(host_matches, $1);
1.1 millert 385: }
386: | '!' host {
1.10 millert 387: SETNMATCH(host_matches, $2);
1.1 millert 388: }
1.8 millert 389: ;
1.1 millert 390:
391: host : ALL {
392: $$ = TRUE;
393: }
394: | NTWKADDR {
395: if (addr_matches($1))
396: $$ = TRUE;
397: else
1.10 millert 398: $$ = NOMATCH;
1.12 millert 399: efree($1);
1.1 millert 400: }
401: | NETGROUP {
1.3 millert 402: if (netgr_matches($1, user_host, user_shost, NULL))
1.1 millert 403: $$ = TRUE;
404: else
1.10 millert 405: $$ = NOMATCH;
1.12 millert 406: efree($1);
1.1 millert 407: }
408: | WORD {
1.4 millert 409: if (hostname_matches(user_shost, user_host, $1) == 0)
1.1 millert 410: $$ = TRUE;
411: else
1.10 millert 412: $$ = NOMATCH;
1.12 millert 413: efree($1);
1.1 millert 414: }
415: | ALIAS {
416: aliasinfo *aip = find_alias($1, HOST_ALIAS);
417:
418: /* could be an all-caps hostname */
419: if (aip)
420: $$ = aip->val;
421: else if (strcasecmp(user_shost, $1) == 0)
422: $$ = TRUE;
423: else {
424: if (pedantic) {
425: (void) fprintf(stderr,
426: "%s: undeclared Host_Alias `%s' referenced near line %d\n",
427: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
428: if (pedantic > 1) {
429: yyerror(NULL);
430: YYERROR;
431: }
432: }
1.10 millert 433: $$ = NOMATCH;
1.1 millert 434: }
1.12 millert 435: efree($1);
1.1 millert 436: }
437: ;
438:
439: cmndspeclist : cmndspec
440: | cmndspeclist ',' cmndspec
441: ;
442:
1.10 millert 443: cmndspec : runasspec cmndtag opcmnd {
1.1 millert 444: /*
445: * Push the entry onto the stack if it is worth
1.10 millert 446: * saving and reset cmnd_matches for next cmnd.
1.1 millert 447: *
448: * We need to save at least one entry on
449: * the stack so sudoers_lookup() can tell that
450: * the user was listed in sudoers. Also, we
451: * need to be able to tell whether or not a
452: * user was listed for this specific host.
1.3 millert 453: *
454: * If keepall is set and the user matches then
455: * we need to keep entries around too...
1.1 millert 456: */
1.10 millert 457: if (MATCHED(user_matches) &&
458: MATCHED(host_matches) &&
459: MATCHED(cmnd_matches) &&
460: MATCHED(runas_matches))
1.1 millert 461: pushcp;
1.10 millert 462: else if (MATCHED(user_matches) && (top == 1 ||
463: (top == 2 && MATCHED(host_matches) &&
464: !MATCHED(match[0].host))))
1.1 millert 465: pushcp;
1.3 millert 466: else if (user_matches == TRUE && keepall)
467: pushcp;
1.10 millert 468: cmnd_matches = UNSPEC;
1.1 millert 469: }
470: ;
471:
472: opcmnd : cmnd {
1.10 millert 473: SETMATCH(cmnd_matches, $1);
1.1 millert 474: }
475: | '!' {
476: if (printmatches == TRUE) {
477: if (in_alias == TRUE)
478: append_entries("!", ", ");
479: else if (host_matches == TRUE &&
480: user_matches == TRUE)
481: append_cmnd("!", NULL);
482: }
483: } cmnd {
1.10 millert 484: SETNMATCH(cmnd_matches, $3);
1.1 millert 485: }
486: ;
487:
488: runasspec : /* empty */ {
489: if (printmatches == TRUE && host_matches == TRUE &&
490: user_matches == TRUE) {
1.10 millert 491: if (runas_matches == UNSPEC) {
1.1 millert 492: cm_list[cm_list_len].runas_len = 0;
493: } else {
494: /* Inherit runas data. */
495: cm_list[cm_list_len].runas =
496: estrdup(cm_list[cm_list_len-1].runas);
497: cm_list[cm_list_len].runas_len =
498: cm_list[cm_list_len-1].runas_len;
499: cm_list[cm_list_len].runas_size =
500: cm_list[cm_list_len-1].runas_size;
501: }
502: }
503: /*
504: * If this is the first entry in a command list
505: * then check against default runas user.
506: */
1.10 millert 507: if (runas_matches == UNSPEC) {
508: runas_matches =
509: userpw_matches(def_runas_default,
510: *user_runas, runas_pw);
511: }
1.1 millert 512: }
1.2 millert 513: | RUNAS runaslist {
1.10 millert 514: runas_matches = $2;
1.2 millert 515: }
1.1 millert 516: ;
517:
1.2 millert 518: runaslist : oprunasuser { ; }
519: | runaslist ',' oprunasuser {
520: /* Later entries override earlier ones. */
1.10 millert 521: if ($3 != NOMATCH)
1.2 millert 522: $$ = $3;
523: else
524: $$ = $1;
525: }
1.1 millert 526: ;
527:
1.2 millert 528: oprunasuser : runasuser { ; }
1.1 millert 529: | '!' {
530: if (printmatches == TRUE) {
531: if (in_alias == TRUE)
532: append_entries("!", ", ");
533: else if (host_matches == TRUE &&
534: user_matches == TRUE)
535: append_runas("!", ", ");
536: }
537: } runasuser {
1.2 millert 538: /* Set $$ to the negation of runasuser */
1.10 millert 539: $$ = ($3 == NOMATCH ? NOMATCH : ! $3);
1.1 millert 540: }
1.8 millert 541: ;
1.1 millert 542:
543: runasuser : WORD {
544: if (printmatches == TRUE) {
545: if (in_alias == TRUE)
546: append_entries($1, ", ");
547: else if (host_matches == TRUE &&
548: user_matches == TRUE)
549: append_runas($1, ", ");
550: }
1.10 millert 551: if (userpw_matches($1, *user_runas, runas_pw))
1.1 millert 552: $$ = TRUE;
553: else
1.10 millert 554: $$ = NOMATCH;
1.12 millert 555: efree($1);
1.10 millert 556: used_runas = TRUE;
1.1 millert 557: }
558: | USERGROUP {
559: if (printmatches == TRUE) {
560: if (in_alias == TRUE)
561: append_entries($1, ", ");
562: else if (host_matches == TRUE &&
563: user_matches == TRUE)
564: append_runas($1, ", ");
565: }
1.10 millert 566: if (usergr_matches($1, *user_runas, runas_pw))
1.1 millert 567: $$ = TRUE;
568: else
1.10 millert 569: $$ = NOMATCH;
1.12 millert 570: efree($1);
1.10 millert 571: used_runas = TRUE;
1.1 millert 572: }
573: | NETGROUP {
574: if (printmatches == TRUE) {
575: if (in_alias == TRUE)
576: append_entries($1, ", ");
577: else if (host_matches == TRUE &&
578: user_matches == TRUE)
579: append_runas($1, ", ");
580: }
1.3 millert 581: if (netgr_matches($1, NULL, NULL, *user_runas))
1.1 millert 582: $$ = TRUE;
583: else
1.10 millert 584: $$ = NOMATCH;
1.12 millert 585: efree($1);
1.10 millert 586: used_runas = TRUE;
1.1 millert 587: }
588: | ALIAS {
589: aliasinfo *aip = find_alias($1, RUNAS_ALIAS);
590:
591: if (printmatches == TRUE) {
592: if (in_alias == TRUE)
593: append_entries($1, ", ");
594: else if (host_matches == TRUE &&
595: user_matches == TRUE)
596: append_runas($1, ", ");
597: }
598: /* could be an all-caps username */
599: if (aip)
600: $$ = aip->val;
601: else if (strcmp($1, *user_runas) == 0)
602: $$ = TRUE;
603: else {
604: if (pedantic) {
605: (void) fprintf(stderr,
606: "%s: undeclared Runas_Alias `%s' referenced near line %d\n",
607: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
608: if (pedantic > 1) {
609: yyerror(NULL);
610: YYERROR;
611: }
612: }
1.10 millert 613: $$ = NOMATCH;
1.1 millert 614: }
1.12 millert 615: efree($1);
1.10 millert 616: used_runas = TRUE;
1.1 millert 617: }
618: | ALL {
619: if (printmatches == TRUE) {
620: if (in_alias == TRUE)
621: append_entries("ALL", ", ");
622: else if (host_matches == TRUE &&
623: user_matches == TRUE)
624: append_runas("ALL", ", ");
625: }
626: $$ = TRUE;
627: }
628: ;
629:
1.10 millert 630: cmndtag : /* empty */ {
1.12 millert 631: /* Inherit {NO,}{PASSWD,EXEC,SETENV} status. */
1.1 millert 632: if (printmatches == TRUE && host_matches == TRUE &&
633: user_matches == TRUE) {
634: if (no_passwd == TRUE)
635: cm_list[cm_list_len].nopasswd = TRUE;
636: else
637: cm_list[cm_list_len].nopasswd = FALSE;
1.10 millert 638: if (no_execve == TRUE)
639: cm_list[cm_list_len].noexecve = TRUE;
640: else
641: cm_list[cm_list_len].noexecve = FALSE;
1.12 millert 642: if (setenv_ok == TRUE)
643: cm_list[cm_list_len].setenv = TRUE;
644: else
645: cm_list[cm_list_len].setenv = FALSE;
1.1 millert 646: }
647: }
1.10 millert 648: | cmndtag NOPASSWD {
1.1 millert 649: no_passwd = TRUE;
650: if (printmatches == TRUE && host_matches == TRUE &&
651: user_matches == TRUE)
652: cm_list[cm_list_len].nopasswd = TRUE;
653: }
1.10 millert 654: | cmndtag PASSWD {
1.1 millert 655: no_passwd = FALSE;
656: if (printmatches == TRUE && host_matches == TRUE &&
657: user_matches == TRUE)
658: cm_list[cm_list_len].nopasswd = FALSE;
659: }
1.10 millert 660: | cmndtag NOEXEC {
661: no_execve = TRUE;
662: if (printmatches == TRUE && host_matches == TRUE &&
663: user_matches == TRUE)
664: cm_list[cm_list_len].noexecve = TRUE;
665: }
666: | cmndtag EXEC {
667: no_execve = FALSE;
668: if (printmatches == TRUE && host_matches == TRUE &&
669: user_matches == TRUE)
670: cm_list[cm_list_len].noexecve = FALSE;
671: }
1.12 millert 672: | cmndtag SETENV {
673: setenv_ok = TRUE;
674: if (printmatches == TRUE && host_matches == TRUE &&
675: user_matches == TRUE)
676: cm_list[cm_list_len].setenv = TRUE;
677: }
678: | cmndtag NOSETENV {
679: setenv_ok = FALSE;
680: if (printmatches == TRUE && host_matches == TRUE &&
681: user_matches == TRUE)
682: cm_list[cm_list_len].setenv = FALSE;
683: }
1.1 millert 684: ;
685:
686: cmnd : ALL {
687: if (printmatches == TRUE) {
688: if (in_alias == TRUE)
689: append_entries("ALL", ", ");
690: else if (host_matches == TRUE &&
691: user_matches == TRUE) {
692: append_cmnd("ALL", NULL);
693: expand_match_list();
694: }
695: }
696:
1.12 millert 697: efree(safe_cmnd);
698: safe_cmnd = NULL;
1.1 millert 699: $$ = TRUE;
700: }
701: | ALIAS {
702: aliasinfo *aip;
703:
704: if (printmatches == TRUE) {
705: if (in_alias == TRUE)
706: append_entries($1, ", ");
707: else if (host_matches == TRUE &&
708: user_matches == TRUE) {
709: append_cmnd($1, NULL);
710: expand_match_list();
711: }
712: }
713:
714: if ((aip = find_alias($1, CMND_ALIAS)))
715: $$ = aip->val;
716: else {
717: if (pedantic) {
718: (void) fprintf(stderr,
719: "%s: undeclared Cmnd_Alias `%s' referenced near line %d\n",
720: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
721: if (pedantic > 1) {
722: yyerror(NULL);
723: YYERROR;
724: }
725: }
1.10 millert 726: $$ = NOMATCH;
1.1 millert 727: }
1.12 millert 728: efree($1);
1.1 millert 729: }
730: | COMMAND {
731: if (printmatches == TRUE) {
732: if (in_alias == TRUE) {
733: append_entries($1.cmnd, ", ");
734: if ($1.args)
735: append_entries($1.args, " ");
736: }
737: if (host_matches == TRUE &&
738: user_matches == TRUE) {
739: append_cmnd($1.cmnd, NULL);
740: if ($1.args)
741: append_cmnd($1.args, " ");
742: expand_match_list();
743: }
744: }
745:
1.10 millert 746: if (command_matches($1.cmnd, $1.args))
1.1 millert 747: $$ = TRUE;
748: else
1.10 millert 749: $$ = NOMATCH;
1.1 millert 750:
1.12 millert 751: efree($1.cmnd);
752: efree($1.args);
1.1 millert 753: }
754: ;
755:
756: hostaliases : hostalias
757: | hostaliases ':' hostalias
758: ;
759:
760: hostalias : ALIAS { push; } '=' hostlist {
1.10 millert 761: if ((MATCHED(host_matches) || pedantic) &&
1.8 millert 762: !add_alias($1, HOST_ALIAS, host_matches)) {
763: yyerror(NULL);
1.1 millert 764: YYERROR;
1.8 millert 765: }
1.1 millert 766: pop;
767: }
768: ;
769:
770: hostlist : ophost
771: | hostlist ',' ophost
772: ;
773:
774: cmndaliases : cmndalias
775: | cmndaliases ':' cmndalias
776: ;
777:
778: cmndalias : ALIAS {
779: push;
780: if (printmatches == TRUE) {
781: in_alias = TRUE;
782: /* Allocate space for ga_list if necessary. */
783: expand_ga_list();
784: ga_list[ga_list_len-1].type = CMND_ALIAS;
785: ga_list[ga_list_len-1].alias = estrdup($1);
786: }
787: } '=' cmndlist {
1.10 millert 788: if ((MATCHED(cmnd_matches) || pedantic) &&
1.8 millert 789: !add_alias($1, CMND_ALIAS, cmnd_matches)) {
790: yyerror(NULL);
1.1 millert 791: YYERROR;
1.8 millert 792: }
1.1 millert 793: pop;
1.12 millert 794: efree($1);
1.1 millert 795:
796: if (printmatches == TRUE)
797: in_alias = FALSE;
798: }
799: ;
800:
801: cmndlist : opcmnd { ; }
802: | cmndlist ',' opcmnd
803: ;
804:
805: runasaliases : runasalias
806: | runasaliases ':' runasalias
807: ;
808:
809: runasalias : ALIAS {
810: if (printmatches == TRUE) {
811: in_alias = TRUE;
812: /* Allocate space for ga_list if necessary. */
813: expand_ga_list();
814: ga_list[ga_list_len-1].type = RUNAS_ALIAS;
815: ga_list[ga_list_len-1].alias = estrdup($1);
816: }
817: } '=' runaslist {
1.10 millert 818: if (($4 != NOMATCH || pedantic) &&
1.8 millert 819: !add_alias($1, RUNAS_ALIAS, $4)) {
820: yyerror(NULL);
1.1 millert 821: YYERROR;
1.8 millert 822: }
1.12 millert 823: efree($1);
1.1 millert 824:
825: if (printmatches == TRUE)
826: in_alias = FALSE;
827: }
828: ;
829:
830: useraliases : useralias
831: | useraliases ':' useralias
832: ;
833:
834: useralias : ALIAS { push; } '=' userlist {
1.10 millert 835: if ((MATCHED(user_matches) || pedantic) &&
1.8 millert 836: !add_alias($1, USER_ALIAS, user_matches)) {
837: yyerror(NULL);
1.1 millert 838: YYERROR;
1.8 millert 839: }
1.1 millert 840: pop;
1.12 millert 841: efree($1);
1.1 millert 842: }
843: ;
844:
845: userlist : opuser
846: | userlist ',' opuser
847: ;
848:
849: opuser : user {
1.10 millert 850: SETMATCH(user_matches, $1);
1.1 millert 851: }
852: | '!' user {
1.10 millert 853: SETNMATCH(user_matches, $2);
1.1 millert 854: }
1.8 millert 855: ;
1.1 millert 856:
857: user : WORD {
1.10 millert 858: if (userpw_matches($1, user_name, sudo_user.pw))
1.1 millert 859: $$ = TRUE;
860: else
1.10 millert 861: $$ = NOMATCH;
1.12 millert 862: efree($1);
1.1 millert 863: }
864: | USERGROUP {
1.10 millert 865: if (usergr_matches($1, user_name, sudo_user.pw))
1.1 millert 866: $$ = TRUE;
867: else
1.10 millert 868: $$ = NOMATCH;
1.12 millert 869: efree($1);
1.1 millert 870: }
871: | NETGROUP {
1.3 millert 872: if (netgr_matches($1, NULL, NULL, user_name))
1.1 millert 873: $$ = TRUE;
874: else
1.10 millert 875: $$ = NOMATCH;
1.12 millert 876: efree($1);
1.1 millert 877: }
878: | ALIAS {
879: aliasinfo *aip = find_alias($1, USER_ALIAS);
880:
881: /* could be an all-caps username */
882: if (aip)
883: $$ = aip->val;
884: else if (strcmp($1, user_name) == 0)
885: $$ = TRUE;
886: else {
887: if (pedantic) {
888: (void) fprintf(stderr,
889: "%s: undeclared User_Alias `%s' referenced near line %d\n",
890: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
1.8 millert 891: if (pedantic > 1) {
892: yyerror(NULL);
1.1 millert 893: YYERROR;
1.8 millert 894: }
1.1 millert 895: }
1.10 millert 896: $$ = NOMATCH;
1.1 millert 897: }
1.12 millert 898: efree($1);
1.1 millert 899: }
900: | ALL {
901: $$ = TRUE;
902: }
903: ;
904:
905: %%
906:
907: #define MOREALIASES (32)
908: aliasinfo *aliases = NULL;
909: size_t naliases = 0;
910: size_t nslots = 0;
911:
912:
913: /*
914: * Compare two aliasinfo structures, strcmp() style.
915: * Note that we do *not* compare their values.
916: */
917: static int
918: aliascmp(a1, a2)
919: const VOID *a1, *a2;
920: {
921: int r;
922: aliasinfo *ai1, *ai2;
923:
924: ai1 = (aliasinfo *) a1;
925: ai2 = (aliasinfo *) a2;
926: if ((r = strcmp(ai1->name, ai2->name)) == 0)
927: r = ai1->type - ai2->type;
928:
929: return(r);
930: }
931:
932: /*
933: * Compare two generic_alias structures, strcmp() style.
934: */
935: static int
936: genaliascmp(entry, key)
937: const VOID *entry, *key;
938: {
939: int r;
940: struct generic_alias *ga1, *ga2;
941:
942: ga1 = (struct generic_alias *) key;
943: ga2 = (struct generic_alias *) entry;
944: if ((r = strcmp(ga1->alias, ga2->alias)) == 0)
945: r = ga1->type - ga2->type;
946:
947: return(r);
948: }
949:
950:
951: /*
952: * Adds the named alias of the specified type to the aliases list.
953: */
954: static int
955: add_alias(alias, type, val)
956: char *alias;
957: int type;
958: int val;
959: {
960: aliasinfo ai, *aip;
961: size_t onaliases;
962: char s[512];
963:
1.16 millert 964: if (naliases >= nslots)
965: more_aliases();
1.1 millert 966:
967: ai.type = type;
968: ai.val = val;
969: ai.name = estrdup(alias);
970: onaliases = naliases;
971:
972: aip = (aliasinfo *) lsearch((VOID *)&ai, (VOID *)aliases, &naliases,
973: sizeof(ai), aliascmp);
974: if (aip == NULL) {
975: (void) snprintf(s, sizeof(s), "Aliases corrupted defining alias `%s'",
976: alias);
977: yyerror(s);
978: return(FALSE);
979: }
980: if (onaliases == naliases) {
981: (void) snprintf(s, sizeof(s), "Alias `%s' already defined", alias);
982: yyerror(s);
983: return(FALSE);
984: }
985:
986: return(TRUE);
987: }
988:
989: /*
990: * Searches for the named alias of the specified type.
991: */
992: static aliasinfo *
993: find_alias(alias, type)
994: char *alias;
995: int type;
996: {
997: aliasinfo ai;
998:
999: ai.name = alias;
1000: ai.type = type;
1001:
1002: return((aliasinfo *) lfind((VOID *)&ai, (VOID *)aliases, &naliases,
1003: sizeof(ai), aliascmp));
1004: }
1005:
1006: /*
1007: * Allocates more space for the aliases list.
1008: */
1.16 millert 1009: static void
1.1 millert 1010: more_aliases()
1011: {
1012:
1013: nslots += MOREALIASES;
1.16 millert 1014: aliases = (aliasinfo *) erealloc3(aliases, nslots, sizeof(aliasinfo));
1.1 millert 1015: }
1016:
1017: /*
1018: * Lists the contents of the aliases list.
1019: */
1020: void
1021: dumpaliases()
1022: {
1023: size_t n;
1024:
1025: for (n = 0; n < naliases; n++) {
1026: if (aliases[n].val == -1)
1027: continue;
1028:
1029: switch (aliases[n].type) {
1030: case HOST_ALIAS:
1031: (void) puts("HOST_ALIAS");
1032: break;
1033:
1034: case CMND_ALIAS:
1035: (void) puts("CMND_ALIAS");
1036: break;
1037:
1038: case USER_ALIAS:
1039: (void) puts("USER_ALIAS");
1040: break;
1041:
1042: case RUNAS_ALIAS:
1043: (void) puts("RUNAS_ALIAS");
1044: break;
1045: }
1046: (void) printf("\t%s: %d\n", aliases[n].name, aliases[n].val);
1047: }
1048: }
1049:
1050: /*
1051: * Lists the contents of cm_list and ga_list for `sudo -l'.
1052: */
1053: void
1054: list_matches()
1055: {
1.10 millert 1056: size_t count;
1.1 millert 1057: char *p;
1058: struct generic_alias *ga, key;
1059:
1060: (void) printf("User %s may run the following commands on this host:\n",
1061: user_name);
1.8 millert 1062: for (count = 0; count < cm_list_len; count++) {
1.1 millert 1063:
1064: /* Print the runas list. */
1065: (void) fputs(" ", stdout);
1.8 millert 1066: if (cm_list[count].runas) {
1.1 millert 1067: (void) putchar('(');
1.8 millert 1068: p = strtok(cm_list[count].runas, ", ");
1.1 millert 1069: do {
1.8 millert 1070: if (p != cm_list[count].runas)
1.1 millert 1071: (void) fputs(", ", stdout);
1072:
1073: key.alias = p;
1074: key.type = RUNAS_ALIAS;
1075: if ((ga = (struct generic_alias *) lfind((VOID *) &key,
1076: (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
1077: (void) fputs(ga->entries, stdout);
1078: else
1079: (void) fputs(p, stdout);
1080: } while ((p = strtok(NULL, ", ")));
1081: (void) fputs(") ", stdout);
1082: } else {
1.10 millert 1083: (void) printf("(%s) ", def_runas_default);
1.1 millert 1084: }
1085:
1.10 millert 1086: /* Is execve(2) disabled? */
1087: if (cm_list[count].noexecve == TRUE && !def_noexec)
1088: (void) fputs("NOEXEC: ", stdout);
1089: else if (cm_list[count].noexecve == FALSE && def_noexec)
1090: (void) fputs("EXEC: ", stdout);
1091:
1.1 millert 1092: /* Is a password required? */
1.10 millert 1093: if (cm_list[count].nopasswd == TRUE && def_authenticate)
1.1 millert 1094: (void) fputs("NOPASSWD: ", stdout);
1.10 millert 1095: else if (cm_list[count].nopasswd == FALSE && !def_authenticate)
1.1 millert 1096: (void) fputs("PASSWD: ", stdout);
1097:
1.12 millert 1098: /* Is setenv enabled? */
1099: if (cm_list[count].setenv == TRUE && !def_setenv)
1100: (void) fputs("SETENV: ", stdout);
1101: else if (cm_list[count].setenv == FALSE && def_setenv)
1102: (void) fputs("NOSETENV: ", stdout);
1103:
1.1 millert 1104: /* Print the actual command or expanded Cmnd_Alias. */
1.8 millert 1105: key.alias = cm_list[count].cmnd;
1.1 millert 1106: key.type = CMND_ALIAS;
1107: if ((ga = (struct generic_alias *) lfind((VOID *) &key,
1108: (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
1109: (void) puts(ga->entries);
1110: else
1.8 millert 1111: (void) puts(cm_list[count].cmnd);
1.1 millert 1112: }
1113:
1114: /* Be nice and free up space now that we are done. */
1.8 millert 1115: for (count = 0; count < ga_list_len; count++) {
1.12 millert 1116: efree(ga_list[count].alias);
1117: efree(ga_list[count].entries);
1.1 millert 1118: }
1.12 millert 1119: efree(ga_list);
1.1 millert 1120: ga_list = NULL;
1121:
1.8 millert 1122: for (count = 0; count < cm_list_len; count++) {
1.12 millert 1123: efree(cm_list[count].runas);
1124: efree(cm_list[count].cmnd);
1.1 millert 1125: }
1.12 millert 1126: efree(cm_list);
1.1 millert 1127: cm_list = NULL;
1128: cm_list_len = 0;
1129: cm_list_size = 0;
1130: }
1131:
1132: /*
1133: * Appends a source string to the destination, optionally prefixing a separator.
1134: */
1135: static void
1136: append(src, dstp, dst_len, dst_size, separator)
1137: char *src, **dstp;
1138: size_t *dst_len, *dst_size;
1139: char *separator;
1140: {
1141: size_t src_len = strlen(src);
1142: char *dst = *dstp;
1143:
1144: /*
1145: * Only add the separator if there is something to separate from.
1146: * If the last char is a '!', don't apply the separator (XXX).
1147: */
1148: if (separator && dst && dst[*dst_len - 1] != '!')
1149: src_len += strlen(separator);
1150: else
1151: separator = NULL;
1152:
1153: /* Assumes dst will be NULL if not set. */
1154: if (dst == NULL) {
1155: dst = (char *) emalloc(BUFSIZ);
1.8 millert 1156: *dst = '\0';
1.1 millert 1157: *dst_size = BUFSIZ;
1158: *dst_len = 0;
1159: *dstp = dst;
1160: }
1161:
1162: /* Allocate more space if necessary. */
1163: if (*dst_size <= *dst_len + src_len) {
1164: while (*dst_size <= *dst_len + src_len)
1165: *dst_size += BUFSIZ;
1166:
1167: dst = (char *) erealloc(dst, *dst_size);
1168: *dstp = dst;
1169: }
1170:
1171: /* Copy src -> dst adding a separator if appropriate and adjust len. */
1.8 millert 1172: if (separator)
1173: (void) strlcat(dst, separator, *dst_size);
1174: (void) strlcat(dst, src, *dst_size);
1.1 millert 1175: *dst_len += src_len;
1176: }
1177:
1178: /*
1179: * Frees up space used by the aliases list and resets the associated counters.
1180: */
1181: void
1182: reset_aliases()
1183: {
1184: size_t n;
1185:
1186: if (aliases) {
1187: for (n = 0; n < naliases; n++)
1.12 millert 1188: efree(aliases[n].name);
1189: efree(aliases);
1.1 millert 1190: aliases = NULL;
1191: }
1192: naliases = nslots = 0;
1193: }
1194:
1195: /*
1196: * Increments ga_list_len, allocating more space as necessary.
1197: */
1198: static void
1199: expand_ga_list()
1200: {
1201:
1202: if (++ga_list_len >= ga_list_size) {
1203: while ((ga_list_size += STACKINCREMENT) < ga_list_len)
1204: ;
1205: ga_list = (struct generic_alias *)
1.8 millert 1206: erealloc3(ga_list, ga_list_size, sizeof(struct generic_alias));
1.1 millert 1207: }
1208:
1209: ga_list[ga_list_len - 1].entries = NULL;
1210: }
1211:
1212: /*
1213: * Increments cm_list_len, allocating more space as necessary.
1214: */
1215: static void
1216: expand_match_list()
1217: {
1218:
1219: if (++cm_list_len >= cm_list_size) {
1220: while ((cm_list_size += STACKINCREMENT) < cm_list_len)
1221: ;
1222: if (cm_list == NULL)
1223: cm_list_len = 0; /* start at 0 since it is a subscript */
1224: cm_list = (struct command_match *)
1.8 millert 1225: erealloc3(cm_list, cm_list_size, sizeof(struct command_match));
1.1 millert 1226: }
1227:
1228: cm_list[cm_list_len].runas = cm_list[cm_list_len].cmnd = NULL;
1229: cm_list[cm_list_len].nopasswd = FALSE;
1.10 millert 1230: cm_list[cm_list_len].noexecve = FALSE;
1.12 millert 1231: cm_list[cm_list_len].setenv = FALSE;
1.1 millert 1232: }
1233:
1234: /*
1235: * Frees up spaced used by a previous parser run and allocates new space
1236: * for various data structures.
1237: */
1238: void
1239: init_parser()
1240: {
1241:
1242: /* Free up old data structures if we run the parser more than once. */
1243: if (match) {
1.12 millert 1244: efree(match);
1.1 millert 1245: match = NULL;
1246: top = 0;
1247: parse_error = FALSE;
1.10 millert 1248: used_runas = FALSE;
1249: errorlineno = -1;
1250: sudolineno = 1;
1.1 millert 1251: }
1252:
1253: /* Allocate space for the matching stack. */
1254: stacksize = STACKINCREMENT;
1.8 millert 1255: match = (struct matchstack *) emalloc2(stacksize, sizeof(struct matchstack));
1.1 millert 1256:
1257: /* Allocate space for the match list (for `sudo -l'). */
1258: if (printmatches == TRUE)
1259: expand_match_list();
1260: }