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