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