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