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