3 * Copyright (c) 1996, 1998-2005, 2007-2012
4 * Todd C. Miller <Todd.Miller@courtesan.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
18 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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.
27 #include <sys/types.h>
28 #include <sys/param.h>
37 #endif /* STDC_HEADERS */
40 #endif /* HAVE_STRING_H */
43 #endif /* HAVE_STRINGS_H */
46 #endif /* HAVE_UNISTD_H */
47 #ifdef HAVE_INTTYPES_H
48 # include <inttypes.h>
50 #if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
52 #endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
55 #include "sudoers.h" /* XXX */
61 * We must define SIZE_MAX for yacc's skeleton.c.
62 * If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
63 * could be signed (as it is on SunOS 4.x).
67 # define SIZE_MAX SIZE_T_MAX
69 # define SIZE_MAX INT_MAX
70 # endif /* SIZE_T_MAX */
76 extern int sudolineno;
77 extern int last_token;
79 bool sudoers_warnings = true;
80 bool parse_error = false;
82 char *errorfile = NULL;
84 struct defaults_list defaults;
85 struct userspec_list userspecs;
90 static void add_defaults(int, struct member *, struct defaults *);
91 static void add_userspec(struct member *, struct privilege *);
92 static struct defaults *new_default(char *, char *, int);
93 static struct member *new_member(char *, int);
94 void yyerror(const char *);
97 yyerror(const char *s)
99 debug_decl(yyerror, SUDO_DEBUG_PARSER)
101 /* If we last saw a newline the error is on the preceding line. */
102 if (last_token == COMMENT)
105 /* Save the line the first error occurred on. */
106 if (errorlineno == -1) {
107 errorlineno = sudolineno;
108 errorfile = estrdup(sudoers);
110 if (sudoers_warnings && s != NULL) {
113 if (trace_print == NULL || trace_print == sudoers_trace_print)
114 warningx(_(">>> %s: %s near line %d <<<"), sudoers, s, sudolineno);
123 struct cmndspec *cmndspec;
124 struct defaults *defaults;
125 struct member *member;
126 struct runascontainer *runas;
127 struct privilege *privilege;
128 struct sudo_command command;
130 struct selinux_info seinfo;
131 struct solaris_privs_info privinfo;
136 %start file /* special start symbol */
137 %token <command> COMMAND /* absolute pathname w/ optional args */
138 %token <string> ALIAS /* an UPPERCASE alias name */
139 %token <string> DEFVAR /* a Defaults variable name */
140 %token <string> NTWKADDR /* ipv4 or ipv6 address */
141 %token <string> NETGROUP /* a netgroup (+NAME) */
142 %token <string> USERGROUP /* a usergroup (%NAME) */
143 %token <string> WORD /* a word */
144 %token <tok> DEFAULTS /* Defaults entry */
145 %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
146 %token <tok> DEFAULTS_USER /* User-specific defaults entry */
147 %token <tok> DEFAULTS_RUNAS /* Runas-specific defaults entry */
148 %token <tok> DEFAULTS_CMND /* Command-specific defaults entry */
149 %token <tok> NOPASSWD /* no passwd req for command */
150 %token <tok> PASSWD /* passwd req for command (default) */
151 %token <tok> NOEXEC /* preload dummy execve() for cmnd */
152 %token <tok> EXEC /* don't preload dummy execve() */
153 %token <tok> SETENV /* user may set environment for cmnd */
154 %token <tok> NOSETENV /* user may not set environment */
155 %token <tok> LOG_INPUT /* log user's cmnd input */
156 %token <tok> NOLOG_INPUT /* don't log user's cmnd input */
157 %token <tok> LOG_OUTPUT /* log cmnd output */
158 %token <tok> NOLOG_OUTPUT /* don't log cmnd output */
159 %token <tok> ALL /* ALL keyword */
160 %token <tok> COMMENT /* comment and/or carriage return */
161 %token <tok> HOSTALIAS /* Host_Alias keyword */
162 %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
163 %token <tok> USERALIAS /* User_Alias keyword */
164 %token <tok> RUNASALIAS /* Runas_Alias keyword */
165 %token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
166 %token <tok> '(' ')' /* runas tokens */
168 %token <tok> TYPE /* SELinux type */
169 %token <tok> ROLE /* SELinux role */
170 %token <tok> PRIVS /* Solaris privileges */
171 %token <tok> LIMITPRIVS /* Solaris limit privileges */
172 %token <tok> MYSELF /* run as myself, not another user */
174 %type <cmndspec> cmndspec
175 %type <cmndspec> cmndspeclist
176 %type <defaults> defaults_entry
177 %type <defaults> defaults_list
179 %type <member> opcmnd
180 %type <member> cmndlist
182 %type <member> hostlist
183 %type <member> ophost
184 %type <member> opuser
186 %type <member> userlist
187 %type <member> opgroup
189 %type <member> grouplist
190 %type <runas> runasspec
191 %type <runas> runaslist
192 %type <privilege> privilege
193 %type <privilege> privileges
195 %type <seinfo> selinux
196 %type <string> rolespec
197 %type <string> typespec
198 %type <privinfo> solarisprivs
199 %type <string> privsspec
200 %type <string> limitprivsspec
218 | userlist privileges {
219 add_userspec($1, $2);
221 | USERALIAS useraliases {
224 | HOSTALIAS hostaliases {
227 | CMNDALIAS cmndaliases {
230 | RUNASALIAS runasaliases {
233 | DEFAULTS defaults_list {
234 add_defaults(DEFAULTS, NULL, $2);
236 | DEFAULTS_USER userlist defaults_list {
237 add_defaults(DEFAULTS_USER, $2, $3);
239 | DEFAULTS_RUNAS userlist defaults_list {
240 add_defaults(DEFAULTS_RUNAS, $2, $3);
242 | DEFAULTS_HOST hostlist defaults_list {
243 add_defaults(DEFAULTS_HOST, $2, $3);
245 | DEFAULTS_CMND cmndlist defaults_list {
246 add_defaults(DEFAULTS_CMND, $2, $3);
250 defaults_list : defaults_entry
251 | defaults_list ',' defaults_entry {
257 defaults_entry : DEFVAR {
258 $$ = new_default($1, NULL, true);
261 $$ = new_default($2, NULL, false);
264 $$ = new_default($1, $3, true);
267 $$ = new_default($1, $3, '+');
270 $$ = new_default($1, $3, '-');
274 privileges : privilege
275 | privileges ':' privilege {
281 privilege : hostlist '=' cmndspeclist {
282 struct privilege *p = ecalloc(1, sizeof(*p));
283 list2tq(&p->hostlist, $1);
284 list2tq(&p->cmndlist, $3);
286 /* p->next = NULL; */
302 $$ = new_member($1, ALIAS);
305 $$ = new_member(NULL, ALL);
308 $$ = new_member($1, NETGROUP);
311 $$ = new_member($1, NTWKADDR);
314 $$ = new_member($1, WORD);
318 cmndspeclist : cmndspec
319 | cmndspeclist ',' cmndspec {
322 /* propagate role and type */
323 if ($3->role == NULL)
324 $3->role = $3->prev->role;
325 if ($3->type == NULL)
326 $3->type = $3->prev->type;
327 #endif /* HAVE_SELINUX */
329 /* propagate privs & limitprivs */
330 if ($3->privs == NULL)
331 $3->privs = $3->prev->privs;
332 if ($3->limitprivs == NULL)
333 $3->limitprivs = $3->prev->limitprivs;
334 #endif /* HAVE_PRIV_SET */
335 /* propagate tags and runas list */
336 if ($3->tags.nopasswd == UNSPEC)
337 $3->tags.nopasswd = $3->prev->tags.nopasswd;
338 if ($3->tags.noexec == UNSPEC)
339 $3->tags.noexec = $3->prev->tags.noexec;
340 if ($3->tags.setenv == UNSPEC &&
341 $3->prev->tags.setenv != IMPLIED)
342 $3->tags.setenv = $3->prev->tags.setenv;
343 if ($3->tags.log_input == UNSPEC)
344 $3->tags.log_input = $3->prev->tags.log_input;
345 if ($3->tags.log_output == UNSPEC)
346 $3->tags.log_output = $3->prev->tags.log_output;
347 if ((tq_empty(&$3->runasuserlist) &&
348 tq_empty(&$3->runasgrouplist)) &&
349 (!tq_empty(&$3->prev->runasuserlist) ||
350 !tq_empty(&$3->prev->runasgrouplist))) {
351 $3->runasuserlist = $3->prev->runasuserlist;
352 $3->runasgrouplist = $3->prev->runasgrouplist;
358 cmndspec : runasspec selinux solarisprivs cmndtag opcmnd {
359 struct cmndspec *cs = ecalloc(1, sizeof(*cs));
361 list2tq(&cs->runasuserlist, $1->runasusers);
362 list2tq(&cs->runasgrouplist, $1->runasgroups);
365 tq_init(&cs->runasuserlist);
366 tq_init(&cs->runasgrouplist);
373 cs->privs = $3.privs;
374 cs->limitprivs = $3.limitprivs;
380 /* sudo "ALL" implies the SETENV tag */
381 if (cs->cmnd->type == ALL && !cs->cmnd->negated &&
382 cs->tags.setenv == UNSPEC)
383 cs->tags.setenv = IMPLIED;
398 rolespec : ROLE '=' WORD {
403 typespec : TYPE '=' WORD {
408 selinux : /* empty */ {
420 | rolespec typespec {
424 | typespec rolespec {
430 privsspec : PRIVS '=' WORD {
434 limitprivsspec : LIMITPRIVS '=' WORD {
439 solarisprivs : /* empty */ {
441 $$.limitprivs = NULL;
445 $$.limitprivs = NULL;
451 | privsspec limitprivsspec {
455 | limitprivsspec privsspec {
460 runasspec : /* empty */ {
463 | '(' runaslist ')' {
468 runaslist : /* empty */ {
469 $$ = ecalloc(1, sizeof(struct runascontainer));
470 $$->runasusers = new_member(NULL, MYSELF);
471 /* $$->runasgroups = NULL; */
474 $$ = ecalloc(1, sizeof(struct runascontainer));
476 /* $$->runasgroups = NULL; */
478 | userlist ':' grouplist {
479 $$ = ecalloc(1, sizeof(struct runascontainer));
481 $$->runasgroups = $3;
484 $$ = ecalloc(1, sizeof(struct runascontainer));
485 /* $$->runasusers = NULL; */
486 $$->runasgroups = $2;
489 $$ = ecalloc(1, sizeof(struct runascontainer));
490 $$->runasusers = new_member(NULL, MYSELF);
491 /* $$->runasgroups = NULL; */
495 cmndtag : /* empty */ {
496 $$.nopasswd = $$.noexec = $$.setenv =
497 $$.log_input = $$.log_output = UNSPEC;
517 | cmndtag LOG_INPUT {
520 | cmndtag NOLOG_INPUT {
521 $$.log_input = false;
523 | cmndtag LOG_OUTPUT {
524 $$.log_output = true;
526 | cmndtag NOLOG_OUTPUT {
527 $$.log_output = false;
532 $$ = new_member(NULL, ALL);
535 $$ = new_member($1, ALIAS);
538 struct sudo_command *c = ecalloc(1, sizeof(*c));
541 $$ = new_member((char *)c, COMMAND);
545 hostaliases : hostalias
546 | hostaliases ':' hostalias
549 hostalias : ALIAS '=' hostlist {
551 if ((s = alias_add($1, HOSTALIAS, $3)) != NULL) {
559 | hostlist ',' ophost {
565 cmndaliases : cmndalias
566 | cmndaliases ':' cmndalias
569 cmndalias : ALIAS '=' cmndlist {
571 if ((s = alias_add($1, CMNDALIAS, $3)) != NULL) {
579 | cmndlist ',' opcmnd {
585 runasaliases : runasalias
586 | runasaliases ':' runasalias
589 runasalias : ALIAS '=' userlist {
591 if ((s = alias_add($1, RUNASALIAS, $3)) != NULL) {
598 useraliases : useralias
599 | useraliases ':' useralias
602 useralias : ALIAS '=' userlist {
604 if ((s = alias_add($1, USERALIAS, $3)) != NULL) {
612 | userlist ',' opuser {
629 $$ = new_member($1, ALIAS);
632 $$ = new_member(NULL, ALL);
635 $$ = new_member($1, NETGROUP);
638 $$ = new_member($1, USERGROUP);
641 $$ = new_member($1, WORD);
646 | grouplist ',' opgroup {
663 $$ = new_member($1, ALIAS);
666 $$ = new_member(NULL, ALL);
669 $$ = new_member($1, WORD);
674 static struct defaults *
675 new_default(char *var, char *val, int op)
678 debug_decl(new_default, SUDO_DEBUG_PARSER)
680 d = ecalloc(1, sizeof(struct defaults));
683 tq_init(&d->binding);
687 /* d->next = NULL; */
692 static struct member *
693 new_member(char *name, int type)
696 debug_decl(new_member, SUDO_DEBUG_PARSER)
698 m = ecalloc(1, sizeof(struct member));
702 /* m->next = NULL; */
708 * Add a list of defaults structures to the defaults list.
709 * The binding, if non-NULL, specifies a list of hosts, users, or
710 * runas users the entries apply to (specified by the type).
713 add_defaults(int type, struct member *bmem, struct defaults *defs)
716 struct member_list binding;
717 debug_decl(add_defaults, SUDO_DEBUG_PARSER)
720 * We can only call list2tq once on bmem as it will zero
721 * out the prev pointer when it consumes bmem.
723 list2tq(&binding, bmem);
726 * Set type and binding (who it applies to) for new entries.
728 for (d = defs; d != NULL; d = d->next) {
730 d->binding = binding;
732 tq_append(&defaults, defs);
738 * Allocate a new struct userspec, populate it, and insert it at the
739 * and of the userspecs list.
742 add_userspec(struct member *members, struct privilege *privs)
745 debug_decl(add_userspec, SUDO_DEBUG_PARSER)
747 u = ecalloc(1, sizeof(*u));
748 list2tq(&u->users, members);
749 list2tq(&u->privileges, privs);
751 /* u->next = NULL; */
752 tq_append(&userspecs, u);
758 * Free up space used by data structures from a previous parser run and sets
759 * the current sudoers file to path.
762 init_parser(const char *path, bool quiet)
765 struct member *m, *binding;
767 struct privilege *priv;
769 struct sudo_command *c;
770 debug_decl(init_parser, SUDO_DEBUG_PARSER)
772 while ((us = tq_pop(&userspecs)) != NULL) {
773 while ((m = tq_pop(&us->users)) != NULL) {
777 while ((priv = tq_pop(&us->privileges)) != NULL) {
778 struct member *runasuser = NULL, *runasgroup = NULL;
780 char *role = NULL, *type = NULL;
781 #endif /* HAVE_SELINUX */
783 char *privs = NULL, *limitprivs = NULL;
784 #endif /* HAVE_PRIV_SET */
786 while ((m = tq_pop(&priv->hostlist)) != NULL) {
790 while ((cs = tq_pop(&priv->cmndlist)) != NULL) {
792 /* Only free the first instance of a role/type. */
793 if (cs->role != role) {
797 if (cs->type != type) {
801 #endif /* HAVE_SELINUX */
803 /* Only free the first instance of privs/limitprivs. */
804 if (cs->privs != privs) {
808 if (cs->limitprivs != limitprivs) {
809 limitprivs = cs->limitprivs;
810 efree(cs->limitprivs);
812 #endif /* HAVE_PRIV_SET */
813 if (tq_last(&cs->runasuserlist) != runasuser) {
814 runasuser = tq_last(&cs->runasuserlist);
815 while ((m = tq_pop(&cs->runasuserlist)) != NULL) {
820 if (tq_last(&cs->runasgrouplist) != runasgroup) {
821 runasgroup = tq_last(&cs->runasgrouplist);
822 while ((m = tq_pop(&cs->runasgrouplist)) != NULL) {
827 if (cs->cmnd->type == COMMAND) {
828 c = (struct sudo_command *) cs->cmnd->name;
832 efree(cs->cmnd->name);
843 while ((d = tq_pop(&defaults)) != NULL) {
844 if (tq_last(&d->binding) != binding) {
845 binding = tq_last(&d->binding);
846 while ((m = tq_pop(&d->binding)) != NULL) {
847 if (m->type == COMMAND) {
848 c = (struct sudo_command *) m->name;
867 sudoers = path ? estrdup(path) : NULL;
872 sudoers_warnings = !quiet;