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