1 %{
2 /*
3  * SPDX-License-Identifier: ISC
4  *
5  * Copyright (c) 1996, 1998-2005, 2007-2013, 2014-2021
6  *	Todd C. Miller <Todd.Miller@sudo.ws>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  *
20  * Sponsored in part by the Defense Advanced Research Projects
21  * Agency (DARPA) and Air Force Research Laboratory, Air Force
22  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
23  */
24 
25 #include <config.h>
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stddef.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <errno.h>
33 
34 #include "sudoers.h"
35 #include "sudo_digest.h"
36 #include "toke.h"
37 
38 #ifdef YYBISON
39 # define YYERROR_VERBOSE
40 #endif
41 
42 /* If we last saw a newline the entry is on the preceding line. */
43 #define this_lineno	(sudoerschar == '\n' ? sudolineno - 1 : sudolineno)
44 
45 // PVS Studio suppression
46 // -V::1037, 1042
47 
48 /*
49  * Globals
50  */
51 bool sudoers_warnings = true;
52 bool sudoers_strict = false;
53 bool parse_error = false;
54 int errorlineno = -1;
55 char *errorfile = NULL;
56 
57 static int alias_line, alias_column;
58 
59 #ifdef NO_LEAKS
60 static struct parser_leak_list parser_leak_list =
61     SLIST_HEAD_INITIALIZER(parser_leak_list);
62 #endif
63 
64 struct sudoers_parse_tree parsed_policy = {
65     TAILQ_HEAD_INITIALIZER(parsed_policy.userspecs),
66     TAILQ_HEAD_INITIALIZER(parsed_policy.defaults),
67     NULL, /* aliases */
68     NULL, /* lhost */
69     NULL /* shost */
70 };
71 
72 /*
73  * Local prototypes
74  */
75 static void init_options(struct command_options *opts);
76 static bool add_defaults(int, struct member *, struct defaults *);
77 static bool add_userspec(struct member *, struct privilege *);
78 static struct defaults *new_default(char *, char *, short);
79 static struct member *new_member(char *, int);
80 static struct sudo_command *new_command(char *, char *);
81 static struct command_digest *new_digest(int, char *);
82 static void alias_error(const char *name, int errnum);
83 %}
84 
85 %union {
86     struct cmndspec *cmndspec;
87     struct defaults *defaults;
88     struct member *member;
89     struct runascontainer *runas;
90     struct privilege *privilege;
91     struct command_digest *digest;
92     struct sudo_command command;
93     struct command_options options;
94     struct cmndtag tag;
95     char *string;
96     int tok;
97 }
98 
99 %start file				/* special start symbol */
100 %token <command> COMMAND		/* absolute pathname w/ optional args */
101 %token <string>  ALIAS			/* an UPPERCASE alias name */
102 %token <string>	 DEFVAR			/* a Defaults variable name */
103 %token <string>  NTWKADDR		/* ipv4 or ipv6 address */
104 %token <string>  NETGROUP		/* a netgroup (+NAME) */
105 %token <string>  USERGROUP		/* a usergroup (%NAME) */
106 %token <string>  WORD			/* a word */
107 %token <string>  DIGEST			/* a SHA-2 digest */
108 %token <tok>	 INCLUDE		/* @include */
109 %token <tok>	 INCLUDEDIR		/* @includedir */
110 %token <tok>	 DEFAULTS		/* Defaults entry */
111 %token <tok>	 DEFAULTS_HOST		/* Host-specific defaults entry */
112 %token <tok>	 DEFAULTS_USER		/* User-specific defaults entry */
113 %token <tok>	 DEFAULTS_RUNAS		/* Runas-specific defaults entry */
114 %token <tok>	 DEFAULTS_CMND		/* Command-specific defaults entry */
115 %token <tok> 	 NOPASSWD		/* no passwd req for command */
116 %token <tok> 	 PASSWD			/* passwd req for command (default) */
117 %token <tok> 	 NOEXEC			/* preload fake execve() for cmnd */
118 %token <tok> 	 EXEC			/* don't preload fake execve() */
119 %token <tok>	 SETENV			/* user may set environment for cmnd */
120 %token <tok>	 NOSETENV		/* user may not set environment */
121 %token <tok>	 LOG_INPUT		/* log user's cmnd input */
122 %token <tok>	 NOLOG_INPUT		/* don't log user's cmnd input */
123 %token <tok>	 LOG_OUTPUT		/* log cmnd output */
124 %token <tok>	 NOLOG_OUTPUT		/* don't log cmnd output */
125 %token <tok>	 MAIL			/* mail log message */
126 %token <tok>	 NOMAIL			/* don't mail log message */
127 %token <tok>	 FOLLOWLNK		/* follow symbolic links */
128 %token <tok>	 NOFOLLOWLNK		/* don't follow symbolic links */
129 %token <tok>	 INTERCEPT		/* intercept children of command */
130 %token <tok>	 NOINTERCEPT		/* disable intercepting of children */
131 %token <tok>	 ALL			/* ALL keyword */
132 %token <tok>	 HOSTALIAS		/* Host_Alias keyword */
133 %token <tok>	 CMNDALIAS		/* Cmnd_Alias keyword */
134 %token <tok>	 USERALIAS		/* User_Alias keyword */
135 %token <tok>	 RUNASALIAS		/* Runas_Alias keyword */
136 %token <tok>	 ':' '=' ',' '!' '+' '-' /* union member tokens */
137 %token <tok>	 '(' ')'		/* runas tokens */
138 %token <tok>	 '\n'			/* newline (with optional comment) */
139 %token <tok>	 ERROR			/* error from lexer */
140 %token <tok>	 NOMATCH		/* no match from lexer */
141 %token <tok>	 CHROOT			/* root directory for command */
142 %token <tok>	 CWD			/* working directory for command */
143 %token <tok>	 TYPE			/* SELinux type */
144 %token <tok>	 ROLE			/* SELinux role */
145 %token <tok>	 PRIVS			/* Solaris privileges */
146 %token <tok>	 LIMITPRIVS		/* Solaris limit privileges */
147 %token <tok>	 CMND_TIMEOUT		/* command timeout */
148 %token <tok>	 NOTBEFORE		/* time restriction */
149 %token <tok>	 NOTAFTER		/* time restriction */
150 %token <tok>	 MYSELF			/* run as myself, not another user */
151 %token <tok>	 SHA224_TOK		/* sha224 token */
152 %token <tok>	 SHA256_TOK		/* sha256 token */
153 %token <tok>	 SHA384_TOK		/* sha384 token */
154 %token <tok>	 SHA512_TOK		/* sha512 token */
155 
156 %type <cmndspec>  cmndspec
157 %type <cmndspec>  cmndspeclist
158 %type <defaults>  defaults_entry
159 %type <defaults>  defaults_list
160 %type <member>	  cmnd
161 %type <member>	  opcmnd
162 %type <member>	  digcmnd
163 %type <member>	  cmndlist
164 %type <member>	  host
165 %type <member>	  hostlist
166 %type <member>	  ophost
167 %type <member>	  opuser
168 %type <member>	  user
169 %type <member>	  userlist
170 %type <member>	  opgroup
171 %type <member>	  group
172 %type <member>	  grouplist
173 %type <runas>	  runasspec
174 %type <runas>	  runaslist
175 %type <privilege> privilege
176 %type <privilege> privileges
177 %type <tag>	  cmndtag
178 %type <options>	  options
179 %type <string>	  chdirspec
180 %type <string>	  chrootspec
181 %type <string>	  rolespec
182 %type <string>	  typespec
183 %type <string>	  privsspec
184 %type <string>	  limitprivsspec
185 %type <string>	  timeoutspec
186 %type <string>	  notbeforespec
187 %type <string>	  notafterspec
188 %type <string>	  include
189 %type <string>	  includedir
190 %type <digest>	  digestspec
191 %type <digest>	  digestlist
192 %type <string>	  reserved_word
193 
194 %%
195 
196 file		:	{
197 			    ; /* empty file */
198 			}
199 		|	line
200 		;
201 
202 line		:	entry
203 		|	line entry
204 		;
205 
206 entry		:	'\n' {
207 			    ; /* blank line */
208 			}
209                 |       error '\n' {
210 			    yyerrok;
211 			}
212 		|	include {
213 			    if (!push_include($1, false)) {
214 				parser_leak_remove(LEAK_PTR, $1);
215 				free($1);
216 				YYERROR;
217 			    }
218 			    parser_leak_remove(LEAK_PTR, $1);
219 			    free($1);
220 			}
221 		|	includedir {
222 			    if (!push_include($1, true)) {
223 				parser_leak_remove(LEAK_PTR, $1);
224 				free($1);
225 				YYERROR;
226 			    }
227 			    parser_leak_remove(LEAK_PTR, $1);
228 			    free($1);
229 			}
230 		|	userlist privileges '\n' {
231 			    if (!add_userspec($1, $2)) {
232 				sudoerserror(N_("unable to allocate memory"));
233 				YYERROR;
234 			    }
235 			}
236 		|	USERALIAS useraliases '\n' {
237 			    ;
238 			}
239 		|	HOSTALIAS hostaliases '\n' {
240 			    ;
241 			}
242 		|	CMNDALIAS cmndaliases '\n' {
243 			    ;
244 			}
245 		|	RUNASALIAS runasaliases '\n' {
246 			    ;
247 			}
248 		|	DEFAULTS defaults_list '\n' {
249 			    if (!add_defaults(DEFAULTS, NULL, $2))
250 				YYERROR;
251 			}
252 		|	DEFAULTS_USER userlist defaults_list '\n' {
253 			    if (!add_defaults(DEFAULTS_USER, $2, $3))
254 				YYERROR;
255 			}
256 		|	DEFAULTS_RUNAS userlist defaults_list '\n' {
257 			    if (!add_defaults(DEFAULTS_RUNAS, $2, $3))
258 				YYERROR;
259 			}
260 		|	DEFAULTS_HOST hostlist defaults_list '\n' {
261 			    if (!add_defaults(DEFAULTS_HOST, $2, $3))
262 				YYERROR;
263 			}
264 		|	DEFAULTS_CMND cmndlist defaults_list '\n' {
265 			    if (!add_defaults(DEFAULTS_CMND, $2, $3))
266 				YYERROR;
267 			}
268 		;
269 
270 include		:	INCLUDE WORD '\n' {
271 			    $$ = $2;
272 			}
273 		|	INCLUDE WORD error '\n' {
274 			    yyerrok;
275 			    $$ = $2;
276 			}
277 		;
278 
279 includedir	:	INCLUDEDIR WORD '\n' {
280 			    $$ = $2;
281 			}
282 		|	INCLUDEDIR WORD error '\n' {
283 			    yyerrok;
284 			    $$ = $2;
285 			}
286 		;
287 
288 defaults_list	:	defaults_entry
289 		|	defaults_list ',' defaults_entry {
290 			    parser_leak_remove(LEAK_DEFAULTS, $3);
291 			    HLTQ_CONCAT($1, $3, entries);
292 			    $$ = $1;
293 			}
294 		;
295 
296 defaults_entry	:	DEFVAR {
297 			    $$ = new_default($1, NULL, true);
298 			    if ($$ == NULL) {
299 				sudoerserror(N_("unable to allocate memory"));
300 				YYERROR;
301 			    }
302 			    parser_leak_remove(LEAK_PTR, $1);
303 			    parser_leak_add(LEAK_DEFAULTS, $$);
304 			}
305 		|	'!' DEFVAR {
306 			    $$ = new_default($2, NULL, false);
307 			    if ($$ == NULL) {
308 				sudoerserror(N_("unable to allocate memory"));
309 				YYERROR;
310 			    }
311 			    parser_leak_remove(LEAK_PTR, $2);
312 			    parser_leak_add(LEAK_DEFAULTS, $$);
313 			}
314 		|	DEFVAR '=' WORD {
315 			    $$ = new_default($1, $3, true);
316 			    if ($$ == NULL) {
317 				sudoerserror(N_("unable to allocate memory"));
318 				YYERROR;
319 			    }
320 			    parser_leak_remove(LEAK_PTR, $1);
321 			    parser_leak_remove(LEAK_PTR, $3);
322 			    parser_leak_add(LEAK_DEFAULTS, $$);
323 			}
324 		|	DEFVAR '+' WORD {
325 			    $$ = new_default($1, $3, '+');
326 			    if ($$ == NULL) {
327 				sudoerserror(N_("unable to allocate memory"));
328 				YYERROR;
329 			    }
330 			    parser_leak_remove(LEAK_PTR, $1);
331 			    parser_leak_remove(LEAK_PTR, $3);
332 			    parser_leak_add(LEAK_DEFAULTS, $$);
333 			}
334 		|	DEFVAR '-' WORD {
335 			    $$ = new_default($1, $3, '-');
336 			    if ($$ == NULL) {
337 				sudoerserror(N_("unable to allocate memory"));
338 				YYERROR;
339 			    }
340 			    parser_leak_remove(LEAK_PTR, $1);
341 			    parser_leak_remove(LEAK_PTR, $3);
342 			    parser_leak_add(LEAK_DEFAULTS, $$);
343 			}
344 		;
345 
346 privileges	:	privilege
347 		|	privileges ':' privilege {
348 			    parser_leak_remove(LEAK_PRIVILEGE, $3);
349 			    HLTQ_CONCAT($1, $3, entries);
350 			    $$ = $1;
351 			}
352 		|	privileges ':' error {
353 			    yyerrok;
354 			    $$ = $1;
355 			}
356 		;
357 
358 privilege	:	hostlist '=' cmndspeclist {
359 			    struct privilege *p = calloc(1, sizeof(*p));
360 			    if (p == NULL) {
361 				sudoerserror(N_("unable to allocate memory"));
362 				YYERROR;
363 			    }
364 			    parser_leak_add(LEAK_PRIVILEGE, p);
365 			    TAILQ_INIT(&p->defaults);
366 			    parser_leak_remove(LEAK_MEMBER, $1);
367 			    HLTQ_TO_TAILQ(&p->hostlist, $1, entries);
368 			    parser_leak_remove(LEAK_CMNDSPEC, $3);
369 			    HLTQ_TO_TAILQ(&p->cmndlist, $3, entries);
370 			    HLTQ_INIT(p, entries);
371 			    $$ = p;
372 			}
373 		;
374 
375 ophost		:	host {
376 			    $$ = $1;
377 			    $$->negated = false;
378 			}
379 		|	'!' host {
380 			    $$ = $2;
381 			    $$->negated = true;
382 			}
383 		;
384 
385 host		:	ALIAS {
386 			    $$ = new_member($1, ALIAS);
387 			    if ($$ == NULL) {
388 				sudoerserror(N_("unable to allocate memory"));
389 				YYERROR;
390 			    }
391 			    parser_leak_remove(LEAK_PTR, $1);
392 			    parser_leak_add(LEAK_MEMBER, $$);
393 			}
394 		|	ALL {
395 			    $$ = new_member(NULL, ALL);
396 			    if ($$ == NULL) {
397 				sudoerserror(N_("unable to allocate memory"));
398 				YYERROR;
399 			    }
400 			    parser_leak_add(LEAK_MEMBER, $$);
401 			}
402 		|	NETGROUP {
403 			    $$ = new_member($1, NETGROUP);
404 			    if ($$ == NULL) {
405 				sudoerserror(N_("unable to allocate memory"));
406 				YYERROR;
407 			    }
408 			    parser_leak_remove(LEAK_PTR, $1);
409 			    parser_leak_add(LEAK_MEMBER, $$);
410 			}
411 		|	NTWKADDR {
412 			    $$ = new_member($1, NTWKADDR);
413 			    if ($$ == NULL) {
414 				sudoerserror(N_("unable to allocate memory"));
415 				YYERROR;
416 			    }
417 			    parser_leak_remove(LEAK_PTR, $1);
418 			    parser_leak_add(LEAK_MEMBER, $$);
419 			}
420 		|	WORD {
421 			    $$ = new_member($1, WORD);
422 			    if ($$ == NULL) {
423 				sudoerserror(N_("unable to allocate memory"));
424 				YYERROR;
425 			    }
426 			    parser_leak_remove(LEAK_PTR, $1);
427 			    parser_leak_add(LEAK_MEMBER, $$);
428 			}
429 		;
430 
431 cmndspeclist	:	cmndspec
432 		|	cmndspeclist ',' cmndspec {
433 			    struct cmndspec *prev;
434 			    prev = HLTQ_LAST($1, cmndspec, entries);
435 			    parser_leak_remove(LEAK_CMNDSPEC, $3);
436 			    HLTQ_CONCAT($1, $3, entries);
437 
438 			    /* propagate runcwd and runchroot */
439 			    if ($3->runcwd == NULL)
440 				$3->runcwd = prev->runcwd;
441 			    if ($3->runchroot == NULL)
442 				$3->runchroot = prev->runchroot;
443 #ifdef HAVE_SELINUX
444 			    /* propagate role and type */
445 			    if ($3->role == NULL && $3->type == NULL) {
446 				$3->role = prev->role;
447 				$3->type = prev->type;
448 			    }
449 #endif /* HAVE_SELINUX */
450 #ifdef HAVE_PRIV_SET
451 			    /* propagate privs & limitprivs */
452 			    if ($3->privs == NULL && $3->limitprivs == NULL) {
453 			        $3->privs = prev->privs;
454 			        $3->limitprivs = prev->limitprivs;
455 			    }
456 #endif /* HAVE_PRIV_SET */
457 			    /* propagate command time restrictions */
458 			    if ($3->notbefore == UNSPEC)
459 				$3->notbefore = prev->notbefore;
460 			    if ($3->notafter == UNSPEC)
461 				$3->notafter = prev->notafter;
462 			    /* propagate command timeout */
463 			    if ($3->timeout == UNSPEC)
464 				$3->timeout = prev->timeout;
465 			    /* propagate tags and runas list */
466 			    if ($3->tags.nopasswd == UNSPEC)
467 				$3->tags.nopasswd = prev->tags.nopasswd;
468 			    if ($3->tags.noexec == UNSPEC)
469 				$3->tags.noexec = prev->tags.noexec;
470 			    if ($3->tags.intercept == UNSPEC)
471 				$3->tags.intercept = prev->tags.intercept;
472 			    if ($3->tags.setenv == UNSPEC &&
473 				prev->tags.setenv != IMPLIED)
474 				$3->tags.setenv = prev->tags.setenv;
475 			    if ($3->tags.log_input == UNSPEC)
476 				$3->tags.log_input = prev->tags.log_input;
477 			    if ($3->tags.log_output == UNSPEC)
478 				$3->tags.log_output = prev->tags.log_output;
479 			    if ($3->tags.send_mail == UNSPEC)
480 				$3->tags.send_mail = prev->tags.send_mail;
481 			    if ($3->tags.follow == UNSPEC)
482 				$3->tags.follow = prev->tags.follow;
483 			    if (($3->runasuserlist == NULL &&
484 				 $3->runasgrouplist == NULL) &&
485 				(prev->runasuserlist != NULL ||
486 				 prev->runasgrouplist != NULL)) {
487 				$3->runasuserlist = prev->runasuserlist;
488 				$3->runasgrouplist = prev->runasgrouplist;
489 			    }
490 			    $$ = $1;
491 			}
492 		;
493 
494 cmndspec	:	runasspec options cmndtag digcmnd {
495 			    struct cmndspec *cs = calloc(1, sizeof(*cs));
496 			    if (cs == NULL) {
497 				sudoerserror(N_("unable to allocate memory"));
498 				YYERROR;
499 			    }
500 			    parser_leak_add(LEAK_CMNDSPEC, cs);
501 			    if ($1 != NULL) {
502 				if ($1->runasusers != NULL) {
503 				    cs->runasuserlist =
504 					malloc(sizeof(*cs->runasuserlist));
505 				    if (cs->runasuserlist == NULL) {
506 					free(cs);
507 					sudoerserror(N_("unable to allocate memory"));
508 					YYERROR;
509 				    }
510 				    /* g/c done via runas container */
511 				    HLTQ_TO_TAILQ(cs->runasuserlist,
512 					$1->runasusers, entries);
513 				}
514 				if ($1->runasgroups != NULL) {
515 				    cs->runasgrouplist =
516 					malloc(sizeof(*cs->runasgrouplist));
517 				    if (cs->runasgrouplist == NULL) {
518 					free(cs);
519 					sudoerserror(N_("unable to allocate memory"));
520 					YYERROR;
521 				    }
522 				    /* g/c done via runas container */
523 				    HLTQ_TO_TAILQ(cs->runasgrouplist,
524 					$1->runasgroups, entries);
525 				}
526 				parser_leak_remove(LEAK_RUNAS, $1);
527 				free($1);
528 			    }
529 #ifdef HAVE_SELINUX
530 			    cs->role = $2.role;
531 			    parser_leak_remove(LEAK_PTR, $2.role);
532 			    cs->type = $2.type;
533 			    parser_leak_remove(LEAK_PTR, $2.type);
534 #endif
535 #ifdef HAVE_PRIV_SET
536 			    cs->privs = $2.privs;
537 			    parser_leak_remove(LEAK_PTR, $2.privs);
538 			    cs->limitprivs = $2.limitprivs;
539 			    parser_leak_remove(LEAK_PTR, $2.limitprivs);
540 #endif
541 			    cs->notbefore = $2.notbefore;
542 			    cs->notafter = $2.notafter;
543 			    cs->timeout = $2.timeout;
544 			    cs->runcwd = $2.runcwd;
545 			    parser_leak_remove(LEAK_PTR, $2.runcwd);
546 			    cs->runchroot = $2.runchroot;
547 			    parser_leak_remove(LEAK_PTR, $2.runchroot);
548 			    cs->tags = $3;
549 			    cs->cmnd = $4;
550 			    parser_leak_remove(LEAK_MEMBER, $4);
551 			    HLTQ_INIT(cs, entries);
552 			    /* sudo "ALL" implies the SETENV tag */
553 			    if (cs->cmnd->type == ALL && !cs->cmnd->negated &&
554 				cs->tags.setenv == UNSPEC)
555 				cs->tags.setenv = IMPLIED;
556 			    $$ = cs;
557 			}
558 		;
559 
560 digestspec	:	SHA224_TOK ':' DIGEST {
561 			    $$ = new_digest(SUDO_DIGEST_SHA224, $3);
562 			    if ($$ == NULL) {
563 				sudoerserror(N_("unable to allocate memory"));
564 				YYERROR;
565 			    }
566 			    parser_leak_remove(LEAK_PTR, $3);
567 			    parser_leak_add(LEAK_DIGEST, $$);
568 			}
569 		|	SHA256_TOK ':' DIGEST {
570 			    $$ = new_digest(SUDO_DIGEST_SHA256, $3);
571 			    if ($$ == NULL) {
572 				sudoerserror(N_("unable to allocate memory"));
573 				YYERROR;
574 			    }
575 			    parser_leak_remove(LEAK_PTR, $3);
576 			    parser_leak_add(LEAK_DIGEST, $$);
577 			}
578 		|	SHA384_TOK ':' DIGEST {
579 			    $$ = new_digest(SUDO_DIGEST_SHA384, $3);
580 			    if ($$ == NULL) {
581 				sudoerserror(N_("unable to allocate memory"));
582 				YYERROR;
583 			    }
584 			    parser_leak_remove(LEAK_PTR, $3);
585 			    parser_leak_add(LEAK_DIGEST, $$);
586 			}
587 		|	SHA512_TOK ':' DIGEST {
588 			    $$ = new_digest(SUDO_DIGEST_SHA512, $3);
589 			    if ($$ == NULL) {
590 				sudoerserror(N_("unable to allocate memory"));
591 				YYERROR;
592 			    }
593 			    parser_leak_remove(LEAK_PTR, $3);
594 			    parser_leak_add(LEAK_DIGEST, $$);
595 			}
596 		;
597 
598 digestlist	:	digestspec
599 		|	digestlist ',' digestspec {
600 			    parser_leak_remove(LEAK_DIGEST, $3);
601 			    HLTQ_CONCAT($1, $3, entries);
602 			    $$ = $1;
603 			}
604 		;
605 
606 digcmnd		:	opcmnd {
607 			    $$ = $1;
608 			}
609 		|	digestlist opcmnd {
610 			    struct sudo_command *c =
611 				(struct sudo_command *) $2->name;
612 
613 			    if ($2->type != COMMAND && $2->type != ALL) {
614 				sudoerserror(N_("a digest requires a path name"));
615 				YYERROR;
616 			    }
617 			    parser_leak_remove(LEAK_DIGEST, $1);
618 			    HLTQ_TO_TAILQ(&c->digests, $1, entries);
619 			    $$ = $2;
620 			}
621 		;
622 
623 opcmnd		:	cmnd {
624 			    $$ = $1;
625 			    $$->negated = false;
626 			}
627 		|	'!' cmnd {
628 			    $$ = $2;
629 			    $$->negated = true;
630 			}
631 		;
632 
633 chdirspec	:	CWD '=' WORD {
634 			    if ($3[0] != '/' && $3[0] != '~') {
635 				if (strcmp($3, "*") != 0) {
636 				    sudoerserror(N_("values for \"CWD\" must"
637 					" start with a '/', '~', or '*'"));
638 				    YYERROR;
639 				}
640 			    }
641 			    $$ = $3;
642 			}
643 		;
644 
645 chrootspec	:	CHROOT '=' WORD {
646 			    if ($3[0] != '/' && $3[0] != '~') {
647 				if (strcmp($3, "*") != 0) {
648 				    sudoerserror(N_("values for \"CHROOT\" must"
649 					" start with a '/', '~', or '*'"));
650 				    YYERROR;
651 				}
652 			    }
653 			    $$ = $3;
654 			}
655 		;
656 
657 timeoutspec	:	CMND_TIMEOUT '=' WORD {
658 			    $$ = $3;
659 			}
660 		;
661 
662 notbeforespec	:	NOTBEFORE '=' WORD {
663 			    $$ = $3;
664 			}
665 
666 notafterspec	:	NOTAFTER '=' WORD {
667 			    $$ = $3;
668 			}
669 		;
670 
671 rolespec	:	ROLE '=' WORD {
672 			    $$ = $3;
673 			}
674 		;
675 
676 typespec	:	TYPE '=' WORD {
677 			    $$ = $3;
678 			}
679 		;
680 
681 privsspec	:	PRIVS '=' WORD {
682 			    $$ = $3;
683 			}
684 		;
685 limitprivsspec	:	LIMITPRIVS '=' WORD {
686 			    $$ = $3;
687 			}
688 		;
689 
690 runasspec	:	/* empty */ {
691 			    $$ = NULL;
692 			}
693 		|	'(' runaslist ')' {
694 			    $$ = $2;
695 			}
696 		;
697 
698 runaslist	:	/* empty */ {
699 			    $$ = calloc(1, sizeof(struct runascontainer));
700 			    if ($$ != NULL) {
701 				$$->runasusers = new_member(NULL, MYSELF);
702 				/* $$->runasgroups = NULL; */
703 				if ($$->runasusers == NULL) {
704 				    free($$);
705 				    $$ = NULL;
706 				}
707 			    }
708 			    if ($$ == NULL) {
709 				sudoerserror(N_("unable to allocate memory"));
710 				YYERROR;
711 			    }
712 			    parser_leak_add(LEAK_RUNAS, $$);
713 			}
714 		|	userlist {
715 			    $$ = calloc(1, sizeof(struct runascontainer));
716 			    if ($$ == NULL) {
717 				sudoerserror(N_("unable to allocate memory"));
718 				YYERROR;
719 			    }
720 			    parser_leak_add(LEAK_RUNAS, $$);
721 			    parser_leak_remove(LEAK_MEMBER, $1);
722 			    $$->runasusers = $1;
723 			    /* $$->runasgroups = NULL; */
724 			}
725 		|	userlist ':' grouplist {
726 			    $$ = calloc(1, sizeof(struct runascontainer));
727 			    if ($$ == NULL) {
728 				sudoerserror(N_("unable to allocate memory"));
729 				YYERROR;
730 			    }
731 			    parser_leak_add(LEAK_RUNAS, $$);
732 			    parser_leak_remove(LEAK_MEMBER, $1);
733 			    parser_leak_remove(LEAK_MEMBER, $3);
734 			    $$->runasusers = $1;
735 			    $$->runasgroups = $3;
736 			}
737 		|	':' grouplist {
738 			    $$ = calloc(1, sizeof(struct runascontainer));
739 			    if ($$ == NULL) {
740 				sudoerserror(N_("unable to allocate memory"));
741 				YYERROR;
742 			    }
743 			    parser_leak_add(LEAK_RUNAS, $$);
744 			    parser_leak_remove(LEAK_MEMBER, $2);
745 			    /* $$->runasusers = NULL; */
746 			    $$->runasgroups = $2;
747 			}
748 		|	':' {
749 			    $$ = calloc(1, sizeof(struct runascontainer));
750 			    if ($$ != NULL) {
751 				$$->runasusers = new_member(NULL, MYSELF);
752 				/* $$->runasgroups = NULL; */
753 				if ($$->runasusers == NULL) {
754 				    free($$);
755 				    $$ = NULL;
756 				}
757 			    }
758 			    if ($$ == NULL) {
759 				sudoerserror(N_("unable to allocate memory"));
760 				YYERROR;
761 			    }
762 			    parser_leak_add(LEAK_RUNAS, $$);
763 			}
764 		;
765 
766 reserved_word	:	ALL		{ $$ = "ALL"; }
767 		|	CHROOT		{ $$ = "CHROOT"; }
768 		|	CWD		{ $$ = "CWD"; }
769 		|	CMND_TIMEOUT	{ $$ = "CMND_TIMEOUT"; }
770 		|	NOTBEFORE	{ $$ = "NOTBEFORE"; }
771 		|	NOTAFTER	{ $$ = "NOTAFTER"; }
772 		|	ROLE		{ $$ = "ROLE"; }
773 		|	TYPE		{ $$ = "TYPE"; }
774 		|	PRIVS		{ $$ = "PRIVS"; }
775 		|	LIMITPRIVS	{ $$ = "LIMITPRIVS"; }
776 		;
777 
778 reserved_alias	:	reserved_word {
779 			    sudoerserrorf(U_("syntax error, reserved word %s used as an alias name"), $1);
780 			    YYERROR;
781 			}
782 		;
783 
784 options		:	/* empty */ {
785 			    init_options(&$$);
786 			}
787 		|	options chdirspec {
788 			    parser_leak_remove(LEAK_PTR, $$.runcwd);
789 			    free($$.runcwd);
790 			    $$.runcwd = $2;
791 			}
792 		|	options chrootspec {
793 			    parser_leak_remove(LEAK_PTR, $$.runchroot);
794 			    free($$.runchroot);
795 			    $$.runchroot = $2;
796 			}
797 		|	options notbeforespec {
798 			    $$.notbefore = parse_gentime($2);
799 			    parser_leak_remove(LEAK_PTR, $2);
800 			    free($2);
801 			    if ($$.notbefore == -1) {
802 				sudoerserror(N_("invalid notbefore value"));
803 				YYERROR;
804 			    }
805 			}
806 		|	options notafterspec {
807 			    $$.notafter = parse_gentime($2);
808 			    parser_leak_remove(LEAK_PTR, $2);
809 			    free($2);
810 			    if ($$.notafter == -1) {
811 				sudoerserror(N_("invalid notafter value"));
812 				YYERROR;
813 			    }
814 			}
815 		|	options timeoutspec {
816 			    $$.timeout = parse_timeout($2);
817 			    parser_leak_remove(LEAK_PTR, $2);
818 			    free($2);
819 			    if ($$.timeout == -1) {
820 				if (errno == ERANGE)
821 				    sudoerserror(N_("timeout value too large"));
822 				else
823 				    sudoerserror(N_("invalid timeout value"));
824 				YYERROR;
825 			    }
826 			}
827 		|	options rolespec {
828 #ifdef HAVE_SELINUX
829 			    parser_leak_remove(LEAK_PTR, $$.role);
830 			    free($$.role);
831 			    $$.role = $2;
832 #endif
833 			}
834 		|	options typespec {
835 #ifdef HAVE_SELINUX
836 			    parser_leak_remove(LEAK_PTR, $$.type);
837 			    free($$.type);
838 			    $$.type = $2;
839 #endif
840 			}
841 		|	options privsspec {
842 #ifdef HAVE_PRIV_SET
843 			    parser_leak_remove(LEAK_PTR, $$.privs);
844 			    free($$.privs);
845 			    $$.privs = $2;
846 #endif
847 			}
848 		|	options limitprivsspec {
849 #ifdef HAVE_PRIV_SET
850 			    parser_leak_remove(LEAK_PTR, $$.limitprivs);
851 			    free($$.limitprivs);
852 			    $$.limitprivs = $2;
853 #endif
854 			}
855 		;
856 
857 cmndtag		:	/* empty */ {
858 			    TAGS_INIT(&$$);
859 			}
860 		|	cmndtag NOPASSWD {
861 			    $$.nopasswd = true;
862 			}
863 		|	cmndtag PASSWD {
864 			    $$.nopasswd = false;
865 			}
866 		|	cmndtag NOEXEC {
867 			    $$.noexec = true;
868 			}
869 		|	cmndtag EXEC {
870 			    $$.noexec = false;
871 			}
872 		|	cmndtag INTERCEPT {
873 			    $$.intercept = true;
874 			}
875 		|	cmndtag NOINTERCEPT {
876 			    $$.intercept = false;
877 			}
878 		|	cmndtag SETENV {
879 			    $$.setenv = true;
880 			}
881 		|	cmndtag NOSETENV {
882 			    $$.setenv = false;
883 			}
884 		|	cmndtag LOG_INPUT {
885 			    $$.log_input = true;
886 			}
887 		|	cmndtag NOLOG_INPUT {
888 			    $$.log_input = false;
889 			}
890 		|	cmndtag LOG_OUTPUT {
891 			    $$.log_output = true;
892 			}
893 		|	cmndtag NOLOG_OUTPUT {
894 			    $$.log_output = false;
895 			}
896 		|	cmndtag FOLLOWLNK {
897 			    $$.follow = true;
898 			}
899 		|	cmndtag NOFOLLOWLNK {
900 			    $$.follow = false;
901 			}
902 		|	cmndtag MAIL {
903 			    $$.send_mail = true;
904 			}
905 		|	cmndtag NOMAIL {
906 			    $$.send_mail = false;
907 			}
908 		;
909 
910 cmnd		:	ALL {
911 			    struct sudo_command *c;
912 
913 			    if ((c = new_command(NULL, NULL)) == NULL) {
914 				sudoerserror(N_("unable to allocate memory"));
915 				YYERROR;
916 			    }
917 			    $$ = new_member((char *)c, ALL);
918 			    if ($$ == NULL) {
919 				sudoerserror(N_("unable to allocate memory"));
920 				YYERROR;
921 			    }
922 			    parser_leak_add(LEAK_MEMBER, $$);
923 			}
924 		|	ALIAS {
925 			    $$ = new_member($1, ALIAS);
926 			    if ($$ == NULL) {
927 				sudoerserror(N_("unable to allocate memory"));
928 				YYERROR;
929 			    }
930 			    parser_leak_remove(LEAK_PTR, $1);
931 			    parser_leak_add(LEAK_MEMBER, $$);
932 			}
933 		|	COMMAND {
934 			    struct sudo_command *c;
935 
936 			    if ((c = new_command($1.cmnd, $1.args)) == NULL) {
937 				sudoerserror(N_("unable to allocate memory"));
938 				YYERROR;
939 			    }
940 			    $$ = new_member((char *)c, COMMAND);
941 			    if ($$ == NULL) {
942 				free(c);
943 				sudoerserror(N_("unable to allocate memory"));
944 				YYERROR;
945 			    }
946 			    parser_leak_remove(LEAK_PTR, $1.cmnd);
947 			    parser_leak_remove(LEAK_PTR, $1.args);
948 			    parser_leak_add(LEAK_MEMBER, $$);
949 			}
950 		;
951 
952 hostaliases	:	hostalias
953 		|	hostaliases ':' hostalias
954 		;
955 
956 hostalias	:	ALIAS {
957 			    alias_line = this_lineno;
958 			    alias_column = sudolinebuf.toke_start + 1;
959 			} '=' hostlist {
960 			    if (!alias_add(&parsed_policy, $1, HOSTALIAS,
961 				sudoers, alias_line, alias_column, $4)) {
962 				alias_error($1, errno);
963 				YYERROR;
964 			    }
965 			    parser_leak_remove(LEAK_PTR, $1);
966 			    parser_leak_remove(LEAK_MEMBER, $4);
967 			}
968 		|	reserved_alias '=' hostlist
969 		;
970 
971 hostlist	:	ophost
972 		|	hostlist ',' ophost {
973 			    parser_leak_remove(LEAK_MEMBER, $3);
974 			    HLTQ_CONCAT($1, $3, entries);
975 			    $$ = $1;
976 			}
977 		;
978 
979 cmndaliases	:	cmndalias
980 		|	cmndaliases ':' cmndalias
981 		;
982 
983 cmndalias	:	ALIAS {
984 			    alias_line = this_lineno;
985 			    alias_column = sudolinebuf.toke_start + 1;
986 			} '=' cmndlist {
987 			    if (!alias_add(&parsed_policy, $1, CMNDALIAS,
988 				sudoers, alias_line, alias_column, $4)) {
989 				alias_error($1, errno);
990 				YYERROR;
991 			    }
992 			    parser_leak_remove(LEAK_PTR, $1);
993 			    parser_leak_remove(LEAK_MEMBER, $4);
994 			}
995 		|	reserved_alias '=' cmndlist
996 		;
997 
998 cmndlist	:	digcmnd
999 		|	cmndlist ',' digcmnd {
1000 			    parser_leak_remove(LEAK_MEMBER, $3);
1001 			    HLTQ_CONCAT($1, $3, entries);
1002 			    $$ = $1;
1003 			}
1004 		;
1005 
1006 runasaliases	:	runasalias
1007 		|	runasaliases ':' runasalias
1008 		;
1009 
1010 runasalias	:	ALIAS {
1011 			    alias_line = this_lineno;
1012 			    alias_column = sudolinebuf.toke_start + 1;
1013 			} '=' userlist {
1014 			    if (!alias_add(&parsed_policy, $1, RUNASALIAS,
1015 				sudoers, alias_line, alias_column, $4)) {
1016 				alias_error($1, errno);
1017 				YYERROR;
1018 			    }
1019 			    parser_leak_remove(LEAK_PTR, $1);
1020 			    parser_leak_remove(LEAK_MEMBER, $4);
1021 			}
1022 		|	reserved_alias '=' userlist
1023 		;
1024 
1025 useraliases	:	useralias
1026 		|	useraliases ':' useralias
1027 		;
1028 
1029 useralias	:	ALIAS {
1030 			    alias_line = this_lineno;
1031 			    alias_column = sudolinebuf.toke_start + 1;
1032 			} '=' userlist {
1033 			    if (!alias_add(&parsed_policy, $1, USERALIAS,
1034 				sudoers, alias_line, alias_column, $4)) {
1035 				alias_error($1, errno);
1036 				YYERROR;
1037 			    }
1038 			    parser_leak_remove(LEAK_PTR, $1);
1039 			    parser_leak_remove(LEAK_MEMBER, $4);
1040 			}
1041 		|	reserved_alias '=' userlist
1042 		;
1043 
1044 userlist	:	opuser
1045 		|	userlist ',' opuser {
1046 			    parser_leak_remove(LEAK_MEMBER, $3);
1047 			    HLTQ_CONCAT($1, $3, entries);
1048 			    $$ = $1;
1049 			}
1050 		;
1051 
1052 opuser		:	user {
1053 			    $$ = $1;
1054 			    $$->negated = false;
1055 			}
1056 		|	'!' user {
1057 			    $$ = $2;
1058 			    $$->negated = true;
1059 			}
1060 		;
1061 
1062 user		:	ALIAS {
1063 			    $$ = new_member($1, ALIAS);
1064 			    if ($$ == NULL) {
1065 				sudoerserror(N_("unable to allocate memory"));
1066 				YYERROR;
1067 			    }
1068 			    parser_leak_remove(LEAK_PTR, $1);
1069 			    parser_leak_add(LEAK_MEMBER, $$);
1070 			}
1071 		|	ALL {
1072 			    $$ = new_member(NULL, ALL);
1073 			    if ($$ == NULL) {
1074 				sudoerserror(N_("unable to allocate memory"));
1075 				YYERROR;
1076 			    }
1077 			    parser_leak_add(LEAK_MEMBER, $$);
1078 			}
1079 		|	NETGROUP {
1080 			    $$ = new_member($1, NETGROUP);
1081 			    if ($$ == NULL) {
1082 				sudoerserror(N_("unable to allocate memory"));
1083 				YYERROR;
1084 			    }
1085 			    parser_leak_remove(LEAK_PTR, $1);
1086 			    parser_leak_add(LEAK_MEMBER, $$);
1087 			}
1088 		|	USERGROUP {
1089 			    $$ = new_member($1, USERGROUP);
1090 			    if ($$ == NULL) {
1091 				sudoerserror(N_("unable to allocate memory"));
1092 				YYERROR;
1093 			    }
1094 			    parser_leak_remove(LEAK_PTR, $1);
1095 			    parser_leak_add(LEAK_MEMBER, $$);
1096 			}
1097 		|	WORD {
1098 			    $$ = new_member($1, WORD);
1099 			    if ($$ == NULL) {
1100 				sudoerserror(N_("unable to allocate memory"));
1101 				YYERROR;
1102 			    }
1103 			    parser_leak_remove(LEAK_PTR, $1);
1104 			    parser_leak_add(LEAK_MEMBER, $$);
1105 			}
1106 		;
1107 
1108 grouplist	:	opgroup
1109 		|	grouplist ',' opgroup {
1110 			    parser_leak_remove(LEAK_MEMBER, $3);
1111 			    HLTQ_CONCAT($1, $3, entries);
1112 			    $$ = $1;
1113 			}
1114 		;
1115 
1116 opgroup		:	group {
1117 			    $$ = $1;
1118 			    $$->negated = false;
1119 			}
1120 		|	'!' group {
1121 			    $$ = $2;
1122 			    $$->negated = true;
1123 			}
1124 		;
1125 
1126 group		:	ALIAS {
1127 			    $$ = new_member($1, ALIAS);
1128 			    if ($$ == NULL) {
1129 				sudoerserror(N_("unable to allocate memory"));
1130 				YYERROR;
1131 			    }
1132 			    parser_leak_remove(LEAK_PTR, $1);
1133 			    parser_leak_add(LEAK_MEMBER, $$);
1134 			}
1135 		|	ALL {
1136 			    $$ = new_member(NULL, ALL);
1137 			    if ($$ == NULL) {
1138 				sudoerserror(N_("unable to allocate memory"));
1139 				YYERROR;
1140 			    }
1141 			    parser_leak_add(LEAK_MEMBER, $$);
1142 			}
1143 		|	WORD {
1144 			    $$ = new_member($1, WORD);
1145 			    if ($$ == NULL) {
1146 				sudoerserror(N_("unable to allocate memory"));
1147 				YYERROR;
1148 			    }
1149 			    parser_leak_remove(LEAK_PTR, $1);
1150 			    parser_leak_add(LEAK_MEMBER, $$);
1151 			}
1152 		;
1153 %%
1154 /* Like yyerror() but takes a printf-style format string. */
1155 void
1156 sudoerserrorf(const char *fmt, ...)
1157 {
1158     debug_decl(sudoerserrorf, SUDOERS_DEBUG_PARSER);
1159 
1160     /* Save the line the first error occurred on. */
1161     if (errorlineno == -1) {
1162 	errorlineno = this_lineno;
1163 	sudo_rcstr_delref(errorfile);
1164 	errorfile = sudo_rcstr_addref(sudoers);
1165     }
1166     if (sudoers_warnings && fmt != NULL) {
1167 	LEXTRACE("<*> ");
1168 #ifndef TRACELEXER
1169 	if (trace_print == NULL || trace_print == sudoers_trace_print) {
1170 	    char *s, *tofree = NULL;
1171 	    int oldlocale;
1172 	    va_list ap;
1173 
1174 	    /* Warnings are displayed in the user's locale. */
1175 	    sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
1176 
1177 	    va_start(ap, fmt);
1178 	    if (sudoerschar == ERROR) {
1179 		/* Use error string from lexer. */
1180 		s = _(sudoers_errstr);
1181 	    } else if (strcmp(fmt, "%s") == 0) {
1182 		/* Optimize common case, a single string. */
1183 		s = _(va_arg(ap, char *));
1184 	    } else {
1185 		if (vasprintf(&s, fmt, ap) != -1)
1186 		    tofree = s;
1187 		else
1188 		    s = _("syntax error");
1189 	    }
1190 	    sudo_printf(SUDO_CONV_ERROR_MSG, _("%s:%d:%d: %s\n"), sudoers,
1191 		this_lineno, (int)sudolinebuf.toke_start + 1, s);
1192 	    free(tofree);
1193 	    va_end(ap);
1194 	    sudoers_setlocale(oldlocale, NULL);
1195 
1196 	    /* Display the offending line and token if possible. */
1197 	    if (sudolinebuf.len != 0) {
1198 		char tildes[128];
1199 		size_t tlen = 0;
1200 
1201 		sudo_printf(SUDO_CONV_ERROR_MSG, "%s%s", sudolinebuf.buf,
1202 		    sudolinebuf.buf[sudolinebuf.len - 1] == '\n' ? "" : "\n");
1203 		if (sudolinebuf.toke_end > sudolinebuf.toke_start) {
1204 		    tlen = sudolinebuf.toke_end - sudolinebuf.toke_start - 1;
1205 		    if (tlen >= sizeof(tildes))
1206 			tlen = sizeof(tildes) - 1;
1207 		    memset(tildes, '~', tlen);
1208 		}
1209 		tildes[tlen] = '\0';
1210 		sudo_printf(SUDO_CONV_ERROR_MSG, "%*s^%s\n",
1211 		    (int)sudolinebuf.toke_start, "", tildes);
1212 	    }
1213 	}
1214 #endif
1215     }
1216     parse_error = true;
1217     debug_return;
1218 }
1219 
1220 void
sudoerserror(const char * s)1221 sudoerserror(const char *s)
1222 {
1223     // -V:sudoerserror:575, 618
1224     if (s == NULL)
1225 	sudoerserrorf(NULL);
1226     else
1227 	sudoerserrorf("%s", s);
1228 }
1229 
1230 static void
alias_error(const char * name,int errnum)1231 alias_error(const char *name, int errnum)
1232 {
1233     if (errnum == EEXIST)
1234 	sudoerserrorf(U_("Alias \"%s\" already defined"), name);
1235     else
1236 	sudoerserror(N_("unable to allocate memory"));
1237 }
1238 
1239 static struct defaults *
new_default(char * var,char * val,short op)1240 new_default(char *var, char *val, short op)
1241 {
1242     struct defaults *d;
1243     debug_decl(new_default, SUDOERS_DEBUG_PARSER);
1244 
1245     if ((d = calloc(1, sizeof(struct defaults))) == NULL) {
1246 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1247 	    "unable to allocate memory");
1248 	debug_return_ptr(NULL);
1249     }
1250 
1251     d->var = var;
1252     d->val = val;
1253     /* d->type = 0; */
1254     d->op = op;
1255     /* d->binding = NULL */
1256     d->line = this_lineno;
1257     d->column = sudolinebuf.toke_start + 1;
1258     d->file = sudo_rcstr_addref(sudoers);
1259     HLTQ_INIT(d, entries);
1260 
1261     debug_return_ptr(d);
1262 }
1263 
1264 static struct member *
new_member(char * name,int type)1265 new_member(char *name, int type)
1266 {
1267     struct member *m;
1268     debug_decl(new_member, SUDOERS_DEBUG_PARSER);
1269 
1270     if ((m = calloc(1, sizeof(struct member))) == NULL) {
1271 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1272 	    "unable to allocate memory");
1273 	debug_return_ptr(NULL);
1274     }
1275 
1276     m->name = name;
1277     m->type = type;
1278     HLTQ_INIT(m, entries);
1279 
1280     debug_return_ptr(m);
1281 }
1282 
1283 static struct sudo_command *
new_command(char * cmnd,char * args)1284 new_command(char *cmnd, char *args)
1285 {
1286     struct sudo_command *c;
1287     debug_decl(new_command, SUDOERS_DEBUG_PARSER);
1288 
1289     if ((c = calloc(1, sizeof(*c))) == NULL) {
1290 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1291 	    "unable to allocate memory");
1292 	debug_return_ptr(NULL);
1293     }
1294     /* garbage collected as part of struct member */
1295 
1296     c->cmnd = cmnd;
1297     c->args = args;
1298     TAILQ_INIT(&c->digests);
1299 
1300     debug_return_ptr(c);
1301 }
1302 
1303 static struct command_digest *
new_digest(int digest_type,char * digest_str)1304 new_digest(int digest_type, char *digest_str)
1305 {
1306     struct command_digest *digest;
1307     debug_decl(new_digest, SUDOERS_DEBUG_PARSER);
1308 
1309     if ((digest = malloc(sizeof(*digest))) == NULL) {
1310 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1311 	    "unable to allocate memory");
1312 	debug_return_ptr(NULL);
1313     }
1314 
1315     HLTQ_INIT(digest, entries);
1316     digest->digest_type = digest_type;
1317     digest->digest_str = digest_str;
1318     if (digest->digest_str == NULL) {
1319 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1320 	    "unable to allocate memory");
1321 	free(digest);
1322 	digest = NULL;
1323     }
1324 
1325     debug_return_ptr(digest);
1326 }
1327 
1328 /*
1329  * Add a list of defaults structures to the defaults list.
1330  * The binding, if non-NULL, specifies a list of hosts, users, or
1331  * runas users the entries apply to (specified by the type).
1332  */
1333 static bool
add_defaults(int type,struct member * bmem,struct defaults * defs)1334 add_defaults(int type, struct member *bmem, struct defaults *defs)
1335 {
1336     struct defaults *d, *next;
1337     struct member_list *binding;
1338     bool ret = true;
1339     debug_decl(add_defaults, SUDOERS_DEBUG_PARSER);
1340 
1341     if (defs != NULL) {
1342 	/*
1343 	 * We use a single binding for each entry in defs.
1344 	 */
1345 	if ((binding = malloc(sizeof(*binding))) == NULL) {
1346 	    sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1347 		"unable to allocate memory");
1348 	    sudoerserror(N_("unable to allocate memory"));
1349 	    debug_return_bool(false);
1350 	}
1351 	if (bmem != NULL) {
1352 	    parser_leak_remove(LEAK_MEMBER, bmem);
1353 	    HLTQ_TO_TAILQ(binding, bmem, entries);
1354 	} else {
1355 	    TAILQ_INIT(binding);
1356 	}
1357 
1358 	/*
1359 	 * Set type and binding (who it applies to) for new entries.
1360 	 * Then add to the global defaults list.
1361 	 */
1362 	parser_leak_remove(LEAK_DEFAULTS, defs);
1363 	HLTQ_FOREACH_SAFE(d, defs, entries, next) {
1364 	    d->type = type;
1365 	    d->binding = binding;
1366 	    TAILQ_INSERT_TAIL(&parsed_policy.defaults, d, entries);
1367 	}
1368     }
1369 
1370     debug_return_bool(ret);
1371 }
1372 
1373 /*
1374  * Allocate a new struct userspec, populate it, and insert it at the
1375  * end of the userspecs list.
1376  */
1377 static bool
add_userspec(struct member * members,struct privilege * privs)1378 add_userspec(struct member *members, struct privilege *privs)
1379 {
1380     struct userspec *u;
1381     debug_decl(add_userspec, SUDOERS_DEBUG_PARSER);
1382 
1383     if ((u = calloc(1, sizeof(*u))) == NULL) {
1384 	sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
1385 	    "unable to allocate memory");
1386 	debug_return_bool(false);
1387     }
1388     u->line = this_lineno;
1389     u->column = sudolinebuf.toke_start + 1;
1390     u->file = sudo_rcstr_addref(sudoers);
1391     parser_leak_remove(LEAK_MEMBER, members);
1392     HLTQ_TO_TAILQ(&u->users, members, entries);
1393     parser_leak_remove(LEAK_PRIVILEGE, privs);
1394     HLTQ_TO_TAILQ(&u->privileges, privs, entries);
1395     STAILQ_INIT(&u->comments);
1396     TAILQ_INSERT_TAIL(&parsed_policy.userspecs, u, entries);
1397 
1398     debug_return_bool(true);
1399 }
1400 
1401 /*
1402  * Free a member struct and its contents.
1403  */
1404 void
free_member(struct member * m)1405 free_member(struct member *m)
1406 {
1407     debug_decl(free_member, SUDOERS_DEBUG_PARSER);
1408 
1409     if (m->type == COMMAND || (m->type == ALL && m->name != NULL)) {
1410 	struct command_digest *digest;
1411 	struct sudo_command *c = (struct sudo_command *)m->name;
1412 	free(c->cmnd);
1413 	free(c->args);
1414 	while ((digest = TAILQ_FIRST(&c->digests)) != NULL) {
1415 	    TAILQ_REMOVE(&c->digests, digest, entries);
1416 	    free(digest->digest_str);
1417 	    free(digest);
1418 	}
1419     }
1420     free(m->name);
1421     free(m);
1422 
1423     debug_return;
1424 }
1425 
1426 /*
1427  * Free a tailq of members but not the struct member_list container itself.
1428  */
1429 void
free_members(struct member_list * members)1430 free_members(struct member_list *members)
1431 {
1432     struct member *m;
1433     debug_decl(free_members, SUDOERS_DEBUG_PARSER);
1434 
1435     while ((m = TAILQ_FIRST(members)) != NULL) {
1436 	TAILQ_REMOVE(members, m, entries);
1437 	free_member(m);
1438     }
1439 
1440     debug_return;
1441 }
1442 
1443 void
free_defaults(struct defaults_list * defs)1444 free_defaults(struct defaults_list *defs)
1445 {
1446     struct member_list *prev_binding = NULL;
1447     struct defaults *def;
1448     debug_decl(free_defaults, SUDOERS_DEBUG_PARSER);
1449 
1450     while ((def = TAILQ_FIRST(defs)) != NULL) {
1451 	TAILQ_REMOVE(defs, def, entries);
1452 	free_default(def, &prev_binding);
1453     }
1454 
1455     debug_return;
1456 }
1457 
1458 void
free_default(struct defaults * def,struct member_list ** binding)1459 free_default(struct defaults *def, struct member_list **binding)
1460 {
1461     debug_decl(free_default, SUDOERS_DEBUG_PARSER);
1462 
1463     if (def->binding != *binding) {
1464 	*binding = def->binding;
1465 	if (def->binding != NULL) {
1466 	    free_members(def->binding);
1467 	    free(def->binding);
1468 	}
1469     }
1470     sudo_rcstr_delref(def->file);
1471     free(def->var);
1472     free(def->val);
1473     free(def);
1474 
1475     debug_return;
1476 }
1477 
1478 void
free_cmndspecs(struct cmndspec_list * csl)1479 free_cmndspecs(struct cmndspec_list *csl)
1480 {
1481     struct member_list *runasuserlist = NULL, *runasgrouplist = NULL;
1482     char *runcwd = NULL, *runchroot = NULL;
1483 #ifdef HAVE_SELINUX
1484     char *role = NULL, *type = NULL;
1485 #endif /* HAVE_SELINUX */
1486 #ifdef HAVE_PRIV_SET
1487     char *privs = NULL, *limitprivs = NULL;
1488 #endif /* HAVE_PRIV_SET */
1489     struct cmndspec *cs;
1490     debug_decl(free_cmndspecs, SUDOERS_DEBUG_PARSER);
1491 
1492     while ((cs = TAILQ_FIRST(csl)) != NULL) {
1493 	TAILQ_REMOVE(csl, cs, entries);
1494 
1495 	/* Only free the first instance of runcwd/runchroot. */
1496 	if (cs->runcwd != runcwd) {
1497 	    runcwd = cs->runcwd;
1498 	    free(cs->runcwd);
1499 	}
1500 	if (cs->runchroot != runchroot) {
1501 	    runchroot = cs->runchroot;
1502 	    free(cs->runchroot);
1503 	}
1504 #ifdef HAVE_SELINUX
1505 	/* Only free the first instance of a role/type. */
1506 	if (cs->role != role) {
1507 	    role = cs->role;
1508 	    free(cs->role);
1509 	}
1510 	if (cs->type != type) {
1511 	    type = cs->type;
1512 	    free(cs->type);
1513 	}
1514 #endif /* HAVE_SELINUX */
1515 #ifdef HAVE_PRIV_SET
1516 	/* Only free the first instance of privs/limitprivs. */
1517 	if (cs->privs != privs) {
1518 	    privs = cs->privs;
1519 	    free(cs->privs);
1520 	}
1521 	if (cs->limitprivs != limitprivs) {
1522 	    limitprivs = cs->limitprivs;
1523 	    free(cs->limitprivs);
1524 	}
1525 #endif /* HAVE_PRIV_SET */
1526 	/* Only free the first instance of runas user/group lists. */
1527 	if (cs->runasuserlist && cs->runasuserlist != runasuserlist) {
1528 	    runasuserlist = cs->runasuserlist;
1529 	    free_members(runasuserlist);
1530 	    free(runasuserlist);
1531 	}
1532 	if (cs->runasgrouplist && cs->runasgrouplist != runasgrouplist) {
1533 	    runasgrouplist = cs->runasgrouplist;
1534 	    free_members(runasgrouplist);
1535 	    free(runasgrouplist);
1536 	}
1537 	free_member(cs->cmnd);
1538 	free(cs);
1539     }
1540 
1541     debug_return;
1542 }
1543 
1544 void
free_privilege(struct privilege * priv)1545 free_privilege(struct privilege *priv)
1546 {
1547     struct member_list *prev_binding = NULL;
1548     struct defaults *def;
1549     debug_decl(free_privilege, SUDOERS_DEBUG_PARSER);
1550 
1551     free(priv->ldap_role);
1552     free_members(&priv->hostlist);
1553     free_cmndspecs(&priv->cmndlist);
1554     while ((def = TAILQ_FIRST(&priv->defaults)) != NULL) {
1555 	TAILQ_REMOVE(&priv->defaults, def, entries);
1556 	free_default(def, &prev_binding);
1557     }
1558     free(priv);
1559 
1560     debug_return;
1561 }
1562 
1563 void
free_userspecs(struct userspec_list * usl)1564 free_userspecs(struct userspec_list *usl)
1565 {
1566     struct userspec *us;
1567     debug_decl(free_userspecs, SUDOERS_DEBUG_PARSER);
1568 
1569     while ((us = TAILQ_FIRST(usl)) != NULL) {
1570 	TAILQ_REMOVE(usl, us, entries);
1571 	free_userspec(us);
1572     }
1573 
1574     debug_return;
1575 }
1576 
1577 void
free_userspec(struct userspec * us)1578 free_userspec(struct userspec *us)
1579 {
1580     struct privilege *priv;
1581     struct sudoers_comment *comment;
1582     debug_decl(free_userspec, SUDOERS_DEBUG_PARSER);
1583 
1584     free_members(&us->users);
1585     while ((priv = TAILQ_FIRST(&us->privileges)) != NULL) {
1586 	TAILQ_REMOVE(&us->privileges, priv, entries);
1587 	free_privilege(priv);
1588     }
1589     while ((comment = STAILQ_FIRST(&us->comments)) != NULL) {
1590 	STAILQ_REMOVE_HEAD(&us->comments, entries);
1591 	free(comment->str);
1592 	free(comment);
1593     }
1594     sudo_rcstr_delref(us->file);
1595     free(us);
1596 
1597     debug_return;
1598 }
1599 
1600 /*
1601  * Initialized a sudoers parse tree.
1602  */
1603 void
init_parse_tree(struct sudoers_parse_tree * parse_tree,const char * lhost,const char * shost)1604 init_parse_tree(struct sudoers_parse_tree *parse_tree, const char *lhost,
1605     const char *shost)
1606 {
1607     TAILQ_INIT(&parse_tree->userspecs);
1608     TAILQ_INIT(&parse_tree->defaults);
1609     parse_tree->aliases = NULL;
1610     parse_tree->shost = shost;
1611     parse_tree->lhost = lhost;
1612 }
1613 
1614 /*
1615  * Move the contents of parsed_policy to new_tree.
1616  */
1617 void
reparent_parse_tree(struct sudoers_parse_tree * new_tree)1618 reparent_parse_tree(struct sudoers_parse_tree *new_tree)
1619 {
1620     TAILQ_CONCAT(&new_tree->userspecs, &parsed_policy.userspecs, entries);
1621     TAILQ_CONCAT(&new_tree->defaults, &parsed_policy.defaults, entries);
1622     new_tree->aliases = parsed_policy.aliases;
1623     parsed_policy.aliases = NULL;
1624 }
1625 
1626 /*
1627  * Free the contents of a sudoers parse tree and initialize it.
1628  */
1629 void
free_parse_tree(struct sudoers_parse_tree * parse_tree)1630 free_parse_tree(struct sudoers_parse_tree *parse_tree)
1631 {
1632     free_userspecs(&parse_tree->userspecs);
1633     free_defaults(&parse_tree->defaults);
1634     free_aliases(parse_tree->aliases);
1635     parse_tree->aliases = NULL;
1636 }
1637 
1638 /*
1639  * Free up space used by data structures from a previous parser run and sets
1640  * the current sudoers file to path.
1641  */
1642 bool
init_parser(const char * path,bool quiet,bool strict)1643 init_parser(const char *path, bool quiet, bool strict)
1644 {
1645     bool ret = true;
1646     debug_decl(init_parser, SUDOERS_DEBUG_PARSER);
1647 
1648     free_parse_tree(&parsed_policy);
1649     parser_leak_init();
1650     init_lexer();
1651 
1652     sudo_rcstr_delref(sudoers);
1653     if (path != NULL) {
1654 	if ((sudoers = sudo_rcstr_dup(path)) == NULL) {
1655 	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1656 	    ret = false;
1657 	}
1658     } else {
1659 	sudoers = NULL;
1660     }
1661 
1662     parse_error = false;
1663     errorlineno = -1;
1664     sudo_rcstr_delref(errorfile);
1665     errorfile = NULL;
1666     sudoers_warnings = !quiet;
1667     sudoers_strict = strict;
1668 
1669     debug_return_bool(ret);
1670 }
1671 
1672 /*
1673  * Initialize all options in a cmndspec.
1674  */
1675 static void
init_options(struct command_options * opts)1676 init_options(struct command_options *opts)
1677 {
1678     opts->notbefore = UNSPEC;
1679     opts->notafter = UNSPEC;
1680     opts->timeout = UNSPEC;
1681     opts->runchroot = NULL;
1682     opts->runcwd = NULL;
1683 #ifdef HAVE_SELINUX
1684     opts->role = NULL;
1685     opts->type = NULL;
1686 #endif
1687 #ifdef HAVE_PRIV_SET
1688     opts->privs = NULL;
1689     opts->limitprivs = NULL;
1690 #endif
1691 }
1692 
1693 bool
parser_leak_add(enum parser_leak_types type,void * v)1694 parser_leak_add(enum parser_leak_types type, void *v)
1695 {
1696 #ifdef NO_LEAKS
1697     struct parser_leak_entry *entry;
1698     debug_decl(parser_leak_add, SUDOERS_DEBUG_PARSER);
1699 
1700     if (v == NULL)
1701 	debug_return_bool(false);
1702 
1703     entry = calloc(1, sizeof(*entry));
1704     if (entry == NULL) {
1705 	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
1706 	debug_return_bool(false);
1707     }
1708     switch (type) {
1709     case LEAK_PRIVILEGE:
1710 	entry->u.p = v;
1711 	break;
1712     case LEAK_CMNDSPEC:
1713 	entry->u.cs = v;
1714 	break;
1715     case LEAK_DEFAULTS:
1716 	entry->u.d = v;
1717 	break;
1718     case LEAK_MEMBER:
1719 	entry->u.m = v;
1720 	break;
1721     case LEAK_DIGEST:
1722 	entry->u.dig = v;
1723 	break;
1724     case LEAK_RUNAS:
1725 	entry->u.rc = v;
1726 	break;
1727     case LEAK_PTR:
1728 	entry->u.ptr = v;
1729 	break;
1730     default:
1731 	free(entry);
1732 	sudo_warnx("unexpected leak type %d", type);
1733 	debug_return_bool(false);
1734     }
1735     entry->type = type;
1736     SLIST_INSERT_HEAD(&parser_leak_list, entry, entries);
1737     debug_return_bool(true);
1738 #else
1739     return true;
1740 #endif /* NO_LEAKS */
1741 }
1742 
1743 bool
parser_leak_remove(enum parser_leak_types type,void * v)1744 parser_leak_remove(enum parser_leak_types type, void *v)
1745 {
1746 #ifdef NO_LEAKS
1747     struct parser_leak_entry *entry, *prev = NULL;
1748     debug_decl(parser_leak_remove, SUDOERS_DEBUG_PARSER);
1749 
1750     if (v == NULL)
1751 	debug_return_bool(false);
1752 
1753     SLIST_FOREACH(entry, &parser_leak_list, entries) {
1754 	switch (entry->type) {
1755 	case LEAK_PRIVILEGE:
1756 	    if (entry->u.p == v)
1757 	    	goto found;
1758 	    break;
1759 	case LEAK_CMNDSPEC:
1760 	    if (entry->u.cs == v)
1761 	    	goto found;
1762 	    break;
1763 	case LEAK_DEFAULTS:
1764 	    if (entry->u.d == v)
1765 	    	goto found;
1766 	    break;
1767 	case LEAK_MEMBER:
1768 	    if (entry->u.m == v)
1769 	    	goto found;
1770 	    break;
1771 	case LEAK_DIGEST:
1772 	    if (entry->u.dig == v)
1773 	    	goto found;
1774 	    break;
1775 	case LEAK_RUNAS:
1776 	    if (entry->u.rc == v)
1777 	    	goto found;
1778 	    break;
1779 	case LEAK_PTR:
1780 	    if (entry->u.ptr == v)
1781 	    	goto found;
1782 	    break;
1783 	default:
1784 	    sudo_warnx("unexpected leak type %d in %p", entry->type, entry);
1785 	}
1786 	prev = entry;
1787     }
1788     /* If this happens, there is a bug in the leak tracking code. */
1789     sudo_warnx("%s: unable to find %p, type %d", __func__, v, type);
1790     debug_return_bool(false);
1791 found:
1792     if (prev == NULL)
1793 	SLIST_REMOVE_HEAD(&parser_leak_list, entries);
1794     else
1795 	SLIST_REMOVE_AFTER(prev, entries);
1796     free(entry);
1797     debug_return_bool(true);
1798 #else
1799     return true;
1800 #endif /* NO_LEAKS */
1801 }
1802 
1803 void
parser_leak_free(void)1804 parser_leak_free(void)
1805 {
1806 #ifdef NO_LEAKS
1807     struct parser_leak_entry *entry;
1808     void *next;
1809     debug_decl(parser_leak_run, SUDOERS_DEBUG_PARSER);
1810 
1811     /* Free the leaks. */
1812     while ((entry = SLIST_FIRST(&parser_leak_list))) {
1813 	SLIST_REMOVE_HEAD(&parser_leak_list, entries);
1814 	switch (entry->type) {
1815 	case LEAK_PRIVILEGE:
1816 	    {
1817 		struct privilege *priv;
1818 
1819 		HLTQ_FOREACH_SAFE(priv, entry->u.p, entries, next)
1820 		    free_privilege(priv);
1821 		free(entry);
1822 	    }
1823 	    break;
1824 	case LEAK_CMNDSPEC:
1825 	    {
1826 		struct cmndspec_list specs;
1827 
1828 		HLTQ_TO_TAILQ(&specs, entry->u.cs, entries);
1829 		free_cmndspecs(&specs);
1830 		free(entry);
1831 	    }
1832 	    break;
1833 	case LEAK_DEFAULTS:
1834 	    {
1835 		struct defaults_list defs;
1836 
1837 		HLTQ_TO_TAILQ(&defs, entry->u.d, entries);
1838 		free_defaults(&defs);
1839 		free(entry);
1840 	    }
1841 	    break;
1842 	case LEAK_MEMBER:
1843 	    {
1844 		struct member *m;
1845 
1846 		HLTQ_FOREACH_SAFE(m, entry->u.m, entries, next)
1847 		    free_member(m);
1848 		free(entry);
1849 	    }
1850 	    break;
1851 	case LEAK_DIGEST:
1852 	    {
1853 		struct command_digest *dig;
1854 
1855 		HLTQ_FOREACH_SAFE(dig, entry->u.dig, entries, next) {
1856 		    free(dig->digest_str);
1857 		    free(dig);
1858 		}
1859 		free(entry);
1860 	    }
1861 	    break;
1862 	case LEAK_RUNAS:
1863 	    {
1864 		struct member *m;
1865 
1866 		if (entry->u.rc->runasusers != NULL) {
1867 		    HLTQ_FOREACH_SAFE(m, entry->u.rc->runasusers, entries, next)
1868 			free_member(m);
1869 		}
1870 		if (entry->u.rc->runasgroups != NULL) {
1871 		    HLTQ_FOREACH_SAFE(m, entry->u.rc->runasgroups, entries, next)
1872 			free_member(m);
1873 		}
1874 		free(entry->u.rc);
1875 		free(entry);
1876 		break;
1877 	    }
1878 	case LEAK_PTR:
1879 	    free(entry->u.ptr);
1880 	    free(entry);
1881 	    break;
1882 	default:
1883 	    sudo_warnx("unexpected garbage type %d", entry->type);
1884 	}
1885     }
1886 
1887     debug_return;
1888 #endif /* NO_LEAKS */
1889 }
1890 
1891 void
parser_leak_init(void)1892 parser_leak_init(void)
1893 {
1894 #ifdef NO_LEAKS
1895     static bool initialized;
1896     debug_decl(parser_leak_init, SUDOERS_DEBUG_PARSER);
1897 
1898     if (!initialized) {
1899 	atexit(parser_leak_free);
1900 	initialized = true;
1901 	debug_return;
1902     }
1903 
1904     /* Already initialized, free existing leaks. */
1905     parser_leak_free();
1906     debug_return;
1907 #endif /* NO_LEAKS */
1908 }
1909