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