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