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