Annotation of src/usr.bin/sudo/parse.yacc, Revision 1.4
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.4 ! millert 82: static const char rcsid[] = "$Sudo: parse.yacc,v 1.173 2000/03/24 23:58:58 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> NETGROUP /* a netgroup (+NAME) */
219: %token <string> USERGROUP /* a usergroup (%NAME) */
220: %token <string> WORD /* a word */
221: %token <tok> DEFAULTS /* Defaults entry */
222: %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
223: %token <tok> DEFAULTS_USER /* User-specific defaults entry */
224: %token <tok> RUNAS /* ( runas_list ) */
225: %token <tok> NOPASSWD /* no passwd req for command */
226: %token <tok> PASSWD /* passwd req for command (default) */
227: %token <tok> ALL /* ALL keyword */
228: %token <tok> COMMENT /* comment and/or carriage return */
229: %token <tok> HOSTALIAS /* Host_Alias keyword */
230: %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
231: %token <tok> USERALIAS /* User_Alias keyword */
232: %token <tok> RUNASALIAS /* Runas_Alias keyword */
233: %token <tok> ':' '=' ',' '!' /* union member tokens */
234: %token <tok> ERROR
235:
236: /*
237: * NOTE: these are not true booleans as there are actually 3 possible values:
238: * 1) TRUE (positive match)
239: * 0) FALSE (negative match due to a '!' somewhere)
240: * -1) No match (don't change the value of *_matches)
241: */
242: %type <BOOLEAN> cmnd
243: %type <BOOLEAN> host
244: %type <BOOLEAN> runasuser
1.2 millert 245: %type <BOOLEAN> oprunasuser
246: %type <BOOLEAN> runaslist
1.1 millert 247: %type <BOOLEAN> user
248:
249: %%
250:
251: file : entry
252: | file entry
253: ;
254:
255: entry : COMMENT
256: { ; }
257: | error COMMENT
258: { yyerrok; }
259: | { push; } userlist privileges {
260: while (top && user_matches != TRUE)
261: pop;
262: }
263: | USERALIAS useraliases
264: { ; }
265: | HOSTALIAS hostaliases
266: { ; }
267: | CMNDALIAS cmndaliases
268: { ; }
269: | RUNASALIAS runasaliases
270: { ; }
271: | defaults_line
272: { ; }
273: ;
274:
275: defaults_line : defaults_type defaults_list
276:
277: defaults_type : DEFAULTS {
278: defaults_matches = TRUE;
279: }
280: | DEFAULTS_USER { push; } userlist {
281: defaults_matches = user_matches;
282: pop;
283: }
284: | DEFAULTS_HOST { push; } hostlist {
285: defaults_matches = host_matches;
286: pop;
287: }
288: ;
289:
290: defaults_list : defaults_entry
291: | defaults_entry ',' defaults_list
292:
293: defaults_entry : WORD {
1.4 ! millert 294: if (defaults_matches == TRUE &&
! 295: !set_default($1, NULL, 1)) {
1.1 millert 296: yyerror(NULL);
297: YYERROR;
298: }
299: free($1);
300: }
301: | '!' WORD {
1.4 ! millert 302: if (defaults_matches == TRUE &&
! 303: !set_default($2, NULL, 0)) {
1.1 millert 304: yyerror(NULL);
305: YYERROR;
306: }
307: free($2);
308: }
309: | WORD '=' WORD {
310: /* XXX - need to support quoted values */
1.4 ! millert 311: if (defaults_matches == TRUE &&
! 312: !set_default($1, $3, 1)) {
1.1 millert 313: yyerror(NULL);
314: YYERROR;
315: }
316: free($1);
317: free($3);
318: }
319:
320: privileges : privilege
321: | privileges ':' privilege
322: ;
323:
324: privilege : hostlist '=' cmndspeclist {
325: /*
326: * We already did a push if necessary in
327: * cmndspec so just reset some values so
328: * the next 'privilege' gets a clean slate.
329: */
330: host_matches = -1;
331: runas_matches = -1;
332: if (def_flag(I_AUTHENTICATE))
333: no_passwd = -1;
334: else
335: no_passwd = TRUE;
336: }
337: ;
338:
339: ophost : host {
340: if ($1 != -1)
341: host_matches = $1;
342: }
343: | '!' host {
344: if ($2 != -1)
345: host_matches = ! $2;
346: }
347:
348: host : ALL {
349: $$ = TRUE;
350: }
351: | NTWKADDR {
352: if (addr_matches($1))
353: $$ = TRUE;
354: else
355: $$ = -1;
356: free($1);
357: }
358: | NETGROUP {
1.3 millert 359: if (netgr_matches($1, user_host, user_shost, NULL))
1.1 millert 360: $$ = TRUE;
361: else
362: $$ = -1;
363: free($1);
364: }
365: | WORD {
1.4 ! millert 366: if (hostname_matches(user_shost, user_host, $1) == 0)
1.1 millert 367: $$ = TRUE;
368: else
369: $$ = -1;
370: free($1);
371: }
372: | ALIAS {
373: aliasinfo *aip = find_alias($1, HOST_ALIAS);
374:
375: /* could be an all-caps hostname */
376: if (aip)
377: $$ = aip->val;
378: else if (strcasecmp(user_shost, $1) == 0)
379: $$ = TRUE;
380: else {
381: if (pedantic) {
382: (void) fprintf(stderr,
383: "%s: undeclared Host_Alias `%s' referenced near line %d\n",
384: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
385: if (pedantic > 1) {
386: yyerror(NULL);
387: YYERROR;
388: }
389: }
390: $$ = -1;
391: }
392: free($1);
393: }
394: ;
395:
396: cmndspeclist : cmndspec
397: | cmndspeclist ',' cmndspec
398: ;
399:
400: cmndspec : runasspec nopasswd opcmnd {
401: /*
402: * Push the entry onto the stack if it is worth
403: * saving and clear cmnd_matches for next cmnd.
404: *
405: * We need to save at least one entry on
406: * the stack so sudoers_lookup() can tell that
407: * the user was listed in sudoers. Also, we
408: * need to be able to tell whether or not a
409: * user was listed for this specific host.
1.3 millert 410: *
411: * If keepall is set and the user matches then
412: * we need to keep entries around too...
1.1 millert 413: */
414: if (user_matches != -1 && host_matches != -1 &&
415: cmnd_matches != -1 && runas_matches != -1)
416: pushcp;
417: else if (user_matches != -1 && (top == 1 ||
418: (top == 2 && host_matches != -1 &&
419: match[0].host == -1)))
420: pushcp;
1.3 millert 421: else if (user_matches == TRUE && keepall)
422: pushcp;
1.1 millert 423: cmnd_matches = -1;
424: }
425: ;
426:
427: opcmnd : cmnd {
428: if ($1 != -1)
429: cmnd_matches = $1;
430: }
431: | '!' {
432: if (printmatches == TRUE) {
433: if (in_alias == TRUE)
434: append_entries("!", ", ");
435: else if (host_matches == TRUE &&
436: user_matches == TRUE)
437: append_cmnd("!", NULL);
438: }
439: } cmnd {
440: if ($3 != -1)
441: cmnd_matches = ! $3;
442: }
443: ;
444:
445: runasspec : /* empty */ {
446: if (printmatches == TRUE && host_matches == TRUE &&
447: user_matches == TRUE) {
448: if (runas_matches == -1) {
449: cm_list[cm_list_len].runas_len = 0;
450: } else {
451: /* Inherit runas data. */
452: cm_list[cm_list_len].runas =
453: estrdup(cm_list[cm_list_len-1].runas);
454: cm_list[cm_list_len].runas_len =
455: cm_list[cm_list_len-1].runas_len;
456: cm_list[cm_list_len].runas_size =
457: cm_list[cm_list_len-1].runas_size;
458: }
459: }
460: /*
461: * If this is the first entry in a command list
462: * then check against default runas user.
463: */
464: if (runas_matches == -1)
465: runas_matches = (strcmp(*user_runas,
466: def_str(I_RUNAS_DEF)) == 0);
467: }
1.2 millert 468: | RUNAS runaslist {
469: runas_matches = ($2 == TRUE ? TRUE : FALSE);
470: }
1.1 millert 471: ;
472:
1.2 millert 473: runaslist : oprunasuser { ; }
474: | runaslist ',' oprunasuser {
475: /* Later entries override earlier ones. */
476: if ($3 != -1)
477: $$ = $3;
478: else
479: $$ = $1;
480: }
1.1 millert 481: ;
482:
1.2 millert 483: oprunasuser : runasuser { ; }
1.1 millert 484: | '!' {
485: if (printmatches == TRUE) {
486: if (in_alias == TRUE)
487: append_entries("!", ", ");
488: else if (host_matches == TRUE &&
489: user_matches == TRUE)
490: append_runas("!", ", ");
491: }
492: } runasuser {
1.2 millert 493: /* Set $$ to the negation of runasuser */
494: $$ = ($3 == -1 ? -1 : ! $3);
1.1 millert 495: }
496:
497: runasuser : WORD {
498: if (printmatches == TRUE) {
499: if (in_alias == TRUE)
500: append_entries($1, ", ");
501: else if (host_matches == TRUE &&
502: user_matches == TRUE)
503: append_runas($1, ", ");
504: }
505: if (strcmp($1, *user_runas) == 0)
506: $$ = TRUE;
507: else
508: $$ = -1;
509: free($1);
510: }
511: | USERGROUP {
512: if (printmatches == TRUE) {
513: if (in_alias == TRUE)
514: append_entries($1, ", ");
515: else if (host_matches == TRUE &&
516: user_matches == TRUE)
517: append_runas($1, ", ");
518: }
519: if (usergr_matches($1, *user_runas))
520: $$ = TRUE;
521: else
522: $$ = -1;
523: free($1);
524: }
525: | NETGROUP {
526: if (printmatches == TRUE) {
527: if (in_alias == TRUE)
528: append_entries($1, ", ");
529: else if (host_matches == TRUE &&
530: user_matches == TRUE)
531: append_runas($1, ", ");
532: }
1.3 millert 533: if (netgr_matches($1, NULL, NULL, *user_runas))
1.1 millert 534: $$ = TRUE;
535: else
536: $$ = -1;
537: free($1);
538: }
539: | ALIAS {
540: aliasinfo *aip = find_alias($1, RUNAS_ALIAS);
541:
542: if (printmatches == TRUE) {
543: if (in_alias == TRUE)
544: append_entries($1, ", ");
545: else if (host_matches == TRUE &&
546: user_matches == TRUE)
547: append_runas($1, ", ");
548: }
549: /* could be an all-caps username */
550: if (aip)
551: $$ = aip->val;
552: else if (strcmp($1, *user_runas) == 0)
553: $$ = TRUE;
554: else {
555: if (pedantic) {
556: (void) fprintf(stderr,
557: "%s: undeclared Runas_Alias `%s' referenced near line %d\n",
558: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
559: if (pedantic > 1) {
560: yyerror(NULL);
561: YYERROR;
562: }
563: }
564: $$ = -1;
565: }
566: free($1);
567: }
568: | ALL {
569: if (printmatches == TRUE) {
570: if (in_alias == TRUE)
571: append_entries("ALL", ", ");
572: else if (host_matches == TRUE &&
573: user_matches == TRUE)
574: append_runas("ALL", ", ");
575: }
576: $$ = TRUE;
577: }
578: ;
579:
580: nopasswd : /* empty */ {
581: /* Inherit NOPASSWD/PASSWD status. */
582: if (printmatches == TRUE && host_matches == TRUE &&
583: user_matches == TRUE) {
584: if (no_passwd == TRUE)
585: cm_list[cm_list_len].nopasswd = TRUE;
586: else
587: cm_list[cm_list_len].nopasswd = FALSE;
588: }
589: }
590: | NOPASSWD {
591: no_passwd = TRUE;
592: if (printmatches == TRUE && host_matches == TRUE &&
593: user_matches == TRUE)
594: cm_list[cm_list_len].nopasswd = TRUE;
595: }
596: | PASSWD {
597: no_passwd = FALSE;
598: if (printmatches == TRUE && host_matches == TRUE &&
599: user_matches == TRUE)
600: cm_list[cm_list_len].nopasswd = FALSE;
601: }
602: ;
603:
604: cmnd : ALL {
605: if (printmatches == TRUE) {
606: if (in_alias == TRUE)
607: append_entries("ALL", ", ");
608: else if (host_matches == TRUE &&
609: user_matches == TRUE) {
610: append_cmnd("ALL", NULL);
611: expand_match_list();
612: }
613: }
614:
615: $$ = TRUE;
616:
617: if (safe_cmnd)
618: free(safe_cmnd);
619: safe_cmnd = estrdup(user_cmnd);
620: }
621: | ALIAS {
622: aliasinfo *aip;
623:
624: if (printmatches == TRUE) {
625: if (in_alias == TRUE)
626: append_entries($1, ", ");
627: else if (host_matches == TRUE &&
628: user_matches == TRUE) {
629: append_cmnd($1, NULL);
630: expand_match_list();
631: }
632: }
633:
634: if ((aip = find_alias($1, CMND_ALIAS)))
635: $$ = aip->val;
636: else {
637: if (pedantic) {
638: (void) fprintf(stderr,
639: "%s: undeclared Cmnd_Alias `%s' referenced near line %d\n",
640: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
641: if (pedantic > 1) {
642: yyerror(NULL);
643: YYERROR;
644: }
645: }
646: $$ = -1;
647: }
648: free($1);
649: }
650: | COMMAND {
651: if (printmatches == TRUE) {
652: if (in_alias == TRUE) {
653: append_entries($1.cmnd, ", ");
654: if ($1.args)
655: append_entries($1.args, " ");
656: }
657: if (host_matches == TRUE &&
658: user_matches == TRUE) {
659: append_cmnd($1.cmnd, NULL);
660: if ($1.args)
661: append_cmnd($1.args, " ");
662: expand_match_list();
663: }
664: }
665:
666: if (command_matches(user_cmnd, user_args,
667: $1.cmnd, $1.args))
668: $$ = TRUE;
669: else
670: $$ = -1;
671:
672: free($1.cmnd);
673: if ($1.args)
674: free($1.args);
675: }
676: ;
677:
678: hostaliases : hostalias
679: | hostaliases ':' hostalias
680: ;
681:
682: hostalias : ALIAS { push; } '=' hostlist {
683: if ((host_matches != -1 || pedantic) &&
684: !add_alias($1, HOST_ALIAS, host_matches))
685: YYERROR;
686: pop;
687: }
688: ;
689:
690: hostlist : ophost
691: | hostlist ',' ophost
692: ;
693:
694: cmndaliases : cmndalias
695: | cmndaliases ':' cmndalias
696: ;
697:
698: cmndalias : ALIAS {
699: push;
700: if (printmatches == TRUE) {
701: in_alias = TRUE;
702: /* Allocate space for ga_list if necessary. */
703: expand_ga_list();
704: ga_list[ga_list_len-1].type = CMND_ALIAS;
705: ga_list[ga_list_len-1].alias = estrdup($1);
706: }
707: } '=' cmndlist {
708: if ((cmnd_matches != -1 || pedantic) &&
709: !add_alias($1, CMND_ALIAS, cmnd_matches))
710: YYERROR;
711: pop;
712: free($1);
713:
714: if (printmatches == TRUE)
715: in_alias = FALSE;
716: }
717: ;
718:
719: cmndlist : opcmnd { ; }
720: | cmndlist ',' opcmnd
721: ;
722:
723: runasaliases : runasalias
724: | runasaliases ':' runasalias
725: ;
726:
727: runasalias : ALIAS {
728: if (printmatches == TRUE) {
729: in_alias = TRUE;
730: /* Allocate space for ga_list if necessary. */
731: expand_ga_list();
732: ga_list[ga_list_len-1].type = RUNAS_ALIAS;
733: ga_list[ga_list_len-1].alias = estrdup($1);
734: }
735: } '=' runaslist {
1.2 millert 736: if (($4 != -1 || pedantic) &&
737: !add_alias($1, RUNAS_ALIAS, $4))
1.1 millert 738: YYERROR;
739: free($1);
740:
741: if (printmatches == TRUE)
742: in_alias = FALSE;
743: }
744: ;
745:
746: useraliases : useralias
747: | useraliases ':' useralias
748: ;
749:
750: useralias : ALIAS { push; } '=' userlist {
751: if ((user_matches != -1 || pedantic) &&
752: !add_alias($1, USER_ALIAS, user_matches))
753: YYERROR;
754: pop;
755: free($1);
756: }
757: ;
758:
759: userlist : opuser
760: | userlist ',' opuser
761: ;
762:
763: opuser : user {
764: if ($1 != -1)
765: user_matches = $1;
766: }
767: | '!' user {
768: if ($2 != -1)
769: user_matches = ! $2;
770: }
771:
772: user : WORD {
773: if (strcmp($1, user_name) == 0)
774: $$ = TRUE;
775: else
776: $$ = -1;
777: free($1);
778: }
779: | USERGROUP {
780: if (usergr_matches($1, user_name))
781: $$ = TRUE;
782: else
783: $$ = -1;
784: free($1);
785: }
786: | NETGROUP {
1.3 millert 787: if (netgr_matches($1, NULL, NULL, user_name))
1.1 millert 788: $$ = TRUE;
789: else
790: $$ = -1;
791: free($1);
792: }
793: | ALIAS {
794: aliasinfo *aip = find_alias($1, USER_ALIAS);
795:
796: /* could be an all-caps username */
797: if (aip)
798: $$ = aip->val;
799: else if (strcmp($1, user_name) == 0)
800: $$ = TRUE;
801: else {
802: if (pedantic) {
803: (void) fprintf(stderr,
804: "%s: undeclared User_Alias `%s' referenced near line %d\n",
805: (pedantic == 1) ? "Warning" : "Error", $1, sudolineno);
806: if (pedantic > 1)
807: YYERROR;
808: }
809: $$ = -1;
810: }
811: free($1);
812: }
813: | ALL {
814: $$ = TRUE;
815: }
816: ;
817:
818: %%
819:
820: #define MOREALIASES (32)
821: aliasinfo *aliases = NULL;
822: size_t naliases = 0;
823: size_t nslots = 0;
824:
825:
826: /*
827: * Compare two aliasinfo structures, strcmp() style.
828: * Note that we do *not* compare their values.
829: */
830: static int
831: aliascmp(a1, a2)
832: const VOID *a1, *a2;
833: {
834: int r;
835: aliasinfo *ai1, *ai2;
836:
837: ai1 = (aliasinfo *) a1;
838: ai2 = (aliasinfo *) a2;
839: if ((r = strcmp(ai1->name, ai2->name)) == 0)
840: r = ai1->type - ai2->type;
841:
842: return(r);
843: }
844:
845: /*
846: * Compare two generic_alias structures, strcmp() style.
847: */
848: static int
849: genaliascmp(entry, key)
850: const VOID *entry, *key;
851: {
852: int r;
853: struct generic_alias *ga1, *ga2;
854:
855: ga1 = (struct generic_alias *) key;
856: ga2 = (struct generic_alias *) entry;
857: if ((r = strcmp(ga1->alias, ga2->alias)) == 0)
858: r = ga1->type - ga2->type;
859:
860: return(r);
861: }
862:
863:
864: /*
865: * Adds the named alias of the specified type to the aliases list.
866: */
867: static int
868: add_alias(alias, type, val)
869: char *alias;
870: int type;
871: int val;
872: {
873: aliasinfo ai, *aip;
874: size_t onaliases;
875: char s[512];
876:
877: if (naliases >= nslots && !more_aliases()) {
878: (void) snprintf(s, sizeof(s), "Out of memory defining alias `%s'",
879: alias);
880: yyerror(s);
881: return(FALSE);
882: }
883:
884: ai.type = type;
885: ai.val = val;
886: ai.name = estrdup(alias);
887: onaliases = naliases;
888:
889: aip = (aliasinfo *) lsearch((VOID *)&ai, (VOID *)aliases, &naliases,
890: sizeof(ai), aliascmp);
891: if (aip == NULL) {
892: (void) snprintf(s, sizeof(s), "Aliases corrupted defining alias `%s'",
893: alias);
894: yyerror(s);
895: return(FALSE);
896: }
897: if (onaliases == naliases) {
898: (void) snprintf(s, sizeof(s), "Alias `%s' already defined", alias);
899: yyerror(s);
900: return(FALSE);
901: }
902:
903: return(TRUE);
904: }
905:
906: /*
907: * Searches for the named alias of the specified type.
908: */
909: static aliasinfo *
910: find_alias(alias, type)
911: char *alias;
912: int type;
913: {
914: aliasinfo ai;
915:
916: ai.name = alias;
917: ai.type = type;
918:
919: return((aliasinfo *) lfind((VOID *)&ai, (VOID *)aliases, &naliases,
920: sizeof(ai), aliascmp));
921: }
922:
923: /*
924: * Allocates more space for the aliases list.
925: */
926: static int
927: more_aliases()
928: {
929:
930: nslots += MOREALIASES;
931: if (nslots == MOREALIASES)
932: aliases = (aliasinfo *) malloc(nslots * sizeof(aliasinfo));
933: else
934: aliases = (aliasinfo *) realloc(aliases, nslots * sizeof(aliasinfo));
935:
936: return(aliases != NULL);
937: }
938:
939: /*
940: * Lists the contents of the aliases list.
941: */
942: void
943: dumpaliases()
944: {
945: size_t n;
946:
947: for (n = 0; n < naliases; n++) {
948: if (aliases[n].val == -1)
949: continue;
950:
951: switch (aliases[n].type) {
952: case HOST_ALIAS:
953: (void) puts("HOST_ALIAS");
954: break;
955:
956: case CMND_ALIAS:
957: (void) puts("CMND_ALIAS");
958: break;
959:
960: case USER_ALIAS:
961: (void) puts("USER_ALIAS");
962: break;
963:
964: case RUNAS_ALIAS:
965: (void) puts("RUNAS_ALIAS");
966: break;
967: }
968: (void) printf("\t%s: %d\n", aliases[n].name, aliases[n].val);
969: }
970: }
971:
972: /*
973: * Lists the contents of cm_list and ga_list for `sudo -l'.
974: */
975: void
976: list_matches()
977: {
978: int i;
979: char *p;
980: struct generic_alias *ga, key;
981:
982: (void) printf("User %s may run the following commands on this host:\n",
983: user_name);
984: for (i = 0; i < cm_list_len; i++) {
985:
986: /* Print the runas list. */
987: (void) fputs(" ", stdout);
988: if (cm_list[i].runas) {
989: (void) putchar('(');
990: p = strtok(cm_list[i].runas, ", ");
991: do {
992: if (p != cm_list[i].runas)
993: (void) fputs(", ", stdout);
994:
995: key.alias = p;
996: key.type = RUNAS_ALIAS;
997: if ((ga = (struct generic_alias *) lfind((VOID *) &key,
998: (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
999: (void) fputs(ga->entries, stdout);
1000: else
1001: (void) fputs(p, stdout);
1002: } while ((p = strtok(NULL, ", ")));
1003: (void) fputs(") ", stdout);
1004: } else {
1005: (void) printf("(%s) ", def_str(I_RUNAS_DEF));
1006: }
1007:
1008: /* Is a password required? */
1009: if (cm_list[i].nopasswd == TRUE && def_flag(I_AUTHENTICATE))
1010: (void) fputs("NOPASSWD: ", stdout);
1011: else if (cm_list[i].nopasswd == FALSE && !def_flag(I_AUTHENTICATE))
1012: (void) fputs("PASSWD: ", stdout);
1013:
1014: /* Print the actual command or expanded Cmnd_Alias. */
1015: key.alias = cm_list[i].cmnd;
1016: key.type = CMND_ALIAS;
1017: if ((ga = (struct generic_alias *) lfind((VOID *) &key,
1018: (VOID *) &ga_list[0], &ga_list_len, sizeof(key), genaliascmp)))
1019: (void) puts(ga->entries);
1020: else
1021: (void) puts(cm_list[i].cmnd);
1022: }
1023:
1024: /* Be nice and free up space now that we are done. */
1025: for (i = 0; i < ga_list_len; i++) {
1026: free(ga_list[i].alias);
1027: free(ga_list[i].entries);
1028: }
1029: free(ga_list);
1030: ga_list = NULL;
1031:
1032: for (i = 0; i < cm_list_len; i++) {
1033: free(cm_list[i].runas);
1034: free(cm_list[i].cmnd);
1035: }
1036: free(cm_list);
1037: cm_list = NULL;
1038: cm_list_len = 0;
1039: cm_list_size = 0;
1040: }
1041:
1042: /*
1043: * Appends a source string to the destination, optionally prefixing a separator.
1044: */
1045: static void
1046: append(src, dstp, dst_len, dst_size, separator)
1047: char *src, **dstp;
1048: size_t *dst_len, *dst_size;
1049: char *separator;
1050: {
1051: size_t src_len = strlen(src);
1052: char *dst = *dstp;
1053:
1054: /*
1055: * Only add the separator if there is something to separate from.
1056: * If the last char is a '!', don't apply the separator (XXX).
1057: */
1058: if (separator && dst && dst[*dst_len - 1] != '!')
1059: src_len += strlen(separator);
1060: else
1061: separator = NULL;
1062:
1063: /* Assumes dst will be NULL if not set. */
1064: if (dst == NULL) {
1065: dst = (char *) emalloc(BUFSIZ);
1066: *dst_size = BUFSIZ;
1067: *dst_len = 0;
1068: *dstp = dst;
1069: }
1070:
1071: /* Allocate more space if necessary. */
1072: if (*dst_size <= *dst_len + src_len) {
1073: while (*dst_size <= *dst_len + src_len)
1074: *dst_size += BUFSIZ;
1075:
1076: dst = (char *) erealloc(dst, *dst_size);
1077: *dstp = dst;
1078: }
1079:
1080: /* Copy src -> dst adding a separator if appropriate and adjust len. */
1081: dst += *dst_len;
1082: *dst_len += src_len;
1083: *dst = '\0';
1084: if (separator)
1085: (void) strcat(dst, separator);
1086: (void) strcat(dst, src);
1087: }
1088:
1089: /*
1090: * Frees up space used by the aliases list and resets the associated counters.
1091: */
1092: void
1093: reset_aliases()
1094: {
1095: size_t n;
1096:
1097: if (aliases) {
1098: for (n = 0; n < naliases; n++)
1099: free(aliases[n].name);
1100: free(aliases);
1101: aliases = NULL;
1102: }
1103: naliases = nslots = 0;
1104: }
1105:
1106: /*
1107: * Increments ga_list_len, allocating more space as necessary.
1108: */
1109: static void
1110: expand_ga_list()
1111: {
1112:
1113: if (++ga_list_len >= ga_list_size) {
1114: while ((ga_list_size += STACKINCREMENT) < ga_list_len)
1115: ;
1116: ga_list = (struct generic_alias *)
1117: erealloc(ga_list, sizeof(struct generic_alias) * ga_list_size);
1118: }
1119:
1120: ga_list[ga_list_len - 1].entries = NULL;
1121: }
1122:
1123: /*
1124: * Increments cm_list_len, allocating more space as necessary.
1125: */
1126: static void
1127: expand_match_list()
1128: {
1129:
1130: if (++cm_list_len >= cm_list_size) {
1131: while ((cm_list_size += STACKINCREMENT) < cm_list_len)
1132: ;
1133: if (cm_list == NULL)
1134: cm_list_len = 0; /* start at 0 since it is a subscript */
1135: cm_list = (struct command_match *)
1136: erealloc(cm_list, sizeof(struct command_match) * cm_list_size);
1137: }
1138:
1139: cm_list[cm_list_len].runas = cm_list[cm_list_len].cmnd = NULL;
1140: cm_list[cm_list_len].nopasswd = FALSE;
1141: }
1142:
1143: /*
1144: * Frees up spaced used by a previous parser run and allocates new space
1145: * for various data structures.
1146: */
1147: void
1148: init_parser()
1149: {
1150:
1151: /* Free up old data structures if we run the parser more than once. */
1152: if (match) {
1153: free(match);
1154: match = NULL;
1155: top = 0;
1156: parse_error = FALSE;
1157: errorlineno = -1;
1158: sudolineno = 1;
1159: }
1160:
1161: /* Allocate space for the matching stack. */
1162: stacksize = STACKINCREMENT;
1163: match = (struct matchstack *) emalloc(sizeof(struct matchstack) * stacksize);
1164:
1165: /* Allocate space for the match list (for `sudo -l'). */
1166: if (printmatches == TRUE)
1167: expand_match_list();
1168: }