3 * Copyright (c) 1996, 1998-2005, 2007-2013
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>
36 #endif /* STDC_HEADERS */
39 #endif /* HAVE_STRING_H */
42 #endif /* HAVE_STRINGS_H */
45 #endif /* HAVE_UNISTD_H */
46 #ifdef HAVE_INTTYPES_H
47 # include <inttypes.h>
49 #if defined(YYBISON) && defined(HAVE_ALLOCA_H) && !defined(__GNUC__)
51 #endif /* YYBISON && HAVE_ALLOCA_H && !__GNUC__ */
54 #include "sudoers.h" /* XXX */
59 * We must define SIZE_MAX for yacc's skeleton.c.
60 * If there is no SIZE_MAX or SIZE_T_MAX we have to assume that size_t
61 * could be signed (as it is on SunOS 4.x).
65 # define SIZE_MAX SIZE_T_MAX
67 # define SIZE_MAX INT_MAX
68 # endif /* SIZE_T_MAX */
74 extern int sudolineno;
75 extern int last_token;
77 bool sudoers_warnings = true;
78 bool parse_error = false;
80 char *errorfile = NULL;
82 struct defaults_list defaults;
83 struct userspec_list userspecs;
88 static void add_defaults(int, struct member *, struct defaults *);
89 static void add_userspec(struct member *, struct privilege *);
90 static struct defaults *new_default(char *, char *, int);
91 static struct member *new_member(char *, int);
92 static struct sudo_digest *new_digest(int, const char *);
96 struct cmndspec *cmndspec;
97 struct defaults *defaults;
98 struct member *member;
99 struct runascontainer *runas;
100 struct privilege *privilege;
101 struct sudo_digest *digest;
102 struct sudo_command command;
104 struct selinux_info seinfo;
105 struct solaris_privs_info privinfo;
110 %start file /* special start symbol */
111 %token <command> COMMAND /* absolute pathname w/ optional args */
112 %token <string> ALIAS /* an UPPERCASE alias name */
113 %token <string> DEFVAR /* a Defaults variable name */
114 %token <string> NTWKADDR /* ipv4 or ipv6 address */
115 %token <string> NETGROUP /* a netgroup (+NAME) */
116 %token <string> USERGROUP /* a usergroup (%NAME) */
117 %token <string> WORD /* a word */
118 %token <string> DIGEST /* a SHA-2 digest */
119 %token <tok> DEFAULTS /* Defaults entry */
120 %token <tok> DEFAULTS_HOST /* Host-specific defaults entry */
121 %token <tok> DEFAULTS_USER /* User-specific defaults entry */
122 %token <tok> DEFAULTS_RUNAS /* Runas-specific defaults entry */
123 %token <tok> DEFAULTS_CMND /* Command-specific defaults entry */
124 %token <tok> NOPASSWD /* no passwd req for command */
125 %token <tok> PASSWD /* passwd req for command (default) */
126 %token <tok> NOEXEC /* preload dummy execve() for cmnd */
127 %token <tok> EXEC /* don't preload dummy execve() */
128 %token <tok> SETENV /* user may set environment for cmnd */
129 %token <tok> NOSETENV /* user may not set environment */
130 %token <tok> LOG_INPUT /* log user's cmnd input */
131 %token <tok> NOLOG_INPUT /* don't log user's cmnd input */
132 %token <tok> LOG_OUTPUT /* log cmnd output */
133 %token <tok> NOLOG_OUTPUT /* don't log cmnd output */
134 %token <tok> ALL /* ALL keyword */
135 %token <tok> COMMENT /* comment and/or carriage return */
136 %token <tok> HOSTALIAS /* Host_Alias keyword */
137 %token <tok> CMNDALIAS /* Cmnd_Alias keyword */
138 %token <tok> USERALIAS /* User_Alias keyword */
139 %token <tok> RUNASALIAS /* Runas_Alias keyword */
140 %token <tok> ':' '=' ',' '!' '+' '-' /* union member tokens */
141 %token <tok> '(' ')' /* runas tokens */
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> MYSELF /* run as myself, not another user */
148 %token <tok> SHA224 /* sha224 digest */
149 %token <tok> SHA256 /* sha256 digest */
150 %token <tok> SHA384 /* sha384 digest */
151 %token <tok> SHA512 /* sha512 digest */
153 %type <cmndspec> cmndspec
154 %type <cmndspec> cmndspeclist
155 %type <defaults> defaults_entry
156 %type <defaults> defaults_list
158 %type <member> opcmnd
159 %type <member> digcmnd
160 %type <member> cmndlist
162 %type <member> hostlist
163 %type <member> ophost
164 %type <member> opuser
166 %type <member> userlist
167 %type <member> opgroup
169 %type <member> grouplist
170 %type <runas> runasspec
171 %type <runas> runaslist
172 %type <privilege> privilege
173 %type <privilege> privileges
175 %type <seinfo> selinux
176 %type <string> rolespec
177 %type <string> typespec
178 %type <privinfo> solarisprivs
179 %type <string> privsspec
180 %type <string> limitprivsspec
181 %type <digest> digest
199 | userlist privileges {
200 add_userspec($1, $2);
202 | USERALIAS useraliases {
205 | HOSTALIAS hostaliases {
208 | CMNDALIAS cmndaliases {
211 | RUNASALIAS runasaliases {
214 | DEFAULTS defaults_list {
215 add_defaults(DEFAULTS, NULL, $2);
217 | DEFAULTS_USER userlist defaults_list {
218 add_defaults(DEFAULTS_USER, $2, $3);
220 | DEFAULTS_RUNAS userlist defaults_list {
221 add_defaults(DEFAULTS_RUNAS, $2, $3);
223 | DEFAULTS_HOST hostlist defaults_list {
224 add_defaults(DEFAULTS_HOST, $2, $3);
226 | DEFAULTS_CMND cmndlist defaults_list {
227 add_defaults(DEFAULTS_CMND, $2, $3);
231 defaults_list : defaults_entry
232 | defaults_list ',' defaults_entry {
238 defaults_entry : DEFVAR {
239 $$ = new_default($1, NULL, true);
242 $$ = new_default($2, NULL, false);
245 $$ = new_default($1, $3, true);
248 $$ = new_default($1, $3, '+');
251 $$ = new_default($1, $3, '-');
255 privileges : privilege
256 | privileges ':' privilege {
262 privilege : hostlist '=' cmndspeclist {
263 struct privilege *p = ecalloc(1, sizeof(*p));
264 list2tq(&p->hostlist, $1);
265 list2tq(&p->cmndlist, $3);
267 /* p->next = NULL; */
283 $$ = new_member($1, ALIAS);
286 $$ = new_member(NULL, ALL);
289 $$ = new_member($1, NETGROUP);
292 $$ = new_member($1, NTWKADDR);
295 $$ = new_member($1, WORD);
299 cmndspeclist : cmndspec
300 | cmndspeclist ',' cmndspec {
303 /* propagate role and type */
304 if ($3->role == NULL)
305 $3->role = $3->prev->role;
306 if ($3->type == NULL)
307 $3->type = $3->prev->type;
308 #endif /* HAVE_SELINUX */
310 /* propagate privs & limitprivs */
311 if ($3->privs == NULL)
312 $3->privs = $3->prev->privs;
313 if ($3->limitprivs == NULL)
314 $3->limitprivs = $3->prev->limitprivs;
315 #endif /* HAVE_PRIV_SET */
316 /* propagate tags and runas list */
317 if ($3->tags.nopasswd == UNSPEC)
318 $3->tags.nopasswd = $3->prev->tags.nopasswd;
319 if ($3->tags.noexec == UNSPEC)
320 $3->tags.noexec = $3->prev->tags.noexec;
321 if ($3->tags.setenv == UNSPEC &&
322 $3->prev->tags.setenv != IMPLIED)
323 $3->tags.setenv = $3->prev->tags.setenv;
324 if ($3->tags.log_input == UNSPEC)
325 $3->tags.log_input = $3->prev->tags.log_input;
326 if ($3->tags.log_output == UNSPEC)
327 $3->tags.log_output = $3->prev->tags.log_output;
328 if ((tq_empty(&$3->runasuserlist) &&
329 tq_empty(&$3->runasgrouplist)) &&
330 (!tq_empty(&$3->prev->runasuserlist) ||
331 !tq_empty(&$3->prev->runasgrouplist))) {
332 $3->runasuserlist = $3->prev->runasuserlist;
333 $3->runasgrouplist = $3->prev->runasgrouplist;
339 cmndspec : runasspec selinux solarisprivs cmndtag digcmnd {
340 struct cmndspec *cs = ecalloc(1, sizeof(*cs));
342 list2tq(&cs->runasuserlist, $1->runasusers);
343 list2tq(&cs->runasgrouplist, $1->runasgroups);
346 tq_init(&cs->runasuserlist);
347 tq_init(&cs->runasgrouplist);
354 cs->privs = $3.privs;
355 cs->limitprivs = $3.limitprivs;
361 /* sudo "ALL" implies the SETENV tag */
362 if (cs->cmnd->type == ALL && !cs->cmnd->negated &&
363 cs->tags.setenv == UNSPEC)
364 cs->tags.setenv = IMPLIED;
369 digest : SHA224 ':' DIGEST {
370 $$ = new_digest(SUDO_DIGEST_SHA224, $3);
372 | SHA256 ':' DIGEST {
373 $$ = new_digest(SUDO_DIGEST_SHA256, $3);
375 | SHA384 ':' DIGEST {
376 $$ = new_digest(SUDO_DIGEST_SHA384, $3);
378 | SHA512 ':' DIGEST {
379 $$ = new_digest(SUDO_DIGEST_SHA512, $3);
388 struct sudo_command *c = (struct sudo_command *)($2->name);
404 rolespec : ROLE '=' WORD {
409 typespec : TYPE '=' WORD {
414 selinux : /* empty */ {
426 | rolespec typespec {
430 | typespec rolespec {
436 privsspec : PRIVS '=' WORD {
440 limitprivsspec : LIMITPRIVS '=' WORD {
445 solarisprivs : /* empty */ {
447 $$.limitprivs = NULL;
451 $$.limitprivs = NULL;
457 | privsspec limitprivsspec {
461 | limitprivsspec privsspec {
467 runasspec : /* empty */ {
470 | '(' runaslist ')' {
475 runaslist : /* empty */ {
476 $$ = ecalloc(1, sizeof(struct runascontainer));
477 $$->runasusers = new_member(NULL, MYSELF);
478 /* $$->runasgroups = NULL; */
481 $$ = ecalloc(1, sizeof(struct runascontainer));
483 /* $$->runasgroups = NULL; */
485 | userlist ':' grouplist {
486 $$ = ecalloc(1, sizeof(struct runascontainer));
488 $$->runasgroups = $3;
491 $$ = ecalloc(1, sizeof(struct runascontainer));
492 /* $$->runasusers = NULL; */
493 $$->runasgroups = $2;
496 $$ = ecalloc(1, sizeof(struct runascontainer));
497 $$->runasusers = new_member(NULL, MYSELF);
498 /* $$->runasgroups = NULL; */
502 cmndtag : /* empty */ {
503 $$.nopasswd = $$.noexec = $$.setenv =
504 $$.log_input = $$.log_output = UNSPEC;
524 | cmndtag LOG_INPUT {
527 | cmndtag NOLOG_INPUT {
528 $$.log_input = false;
530 | cmndtag LOG_OUTPUT {
531 $$.log_output = true;
533 | cmndtag NOLOG_OUTPUT {
534 $$.log_output = false;
539 $$ = new_member(NULL, ALL);
542 $$ = new_member($1, ALIAS);
545 struct sudo_command *c = ecalloc(1, sizeof(*c));
548 $$ = new_member((char *)c, COMMAND);
552 hostaliases : hostalias
553 | hostaliases ':' hostalias
556 hostalias : ALIAS '=' hostlist {
558 if ((s = alias_add($1, HOSTALIAS, $3)) != NULL) {
566 | hostlist ',' ophost {
572 cmndaliases : cmndalias
573 | cmndaliases ':' cmndalias
576 cmndalias : ALIAS '=' cmndlist {
578 if ((s = alias_add($1, CMNDALIAS, $3)) != NULL) {
586 | cmndlist ',' digcmnd {
592 runasaliases : runasalias
593 | runasaliases ':' runasalias
596 runasalias : ALIAS '=' userlist {
598 if ((s = alias_add($1, RUNASALIAS, $3)) != NULL) {
605 useraliases : useralias
606 | useraliases ':' useralias
609 useralias : ALIAS '=' userlist {
611 if ((s = alias_add($1, USERALIAS, $3)) != NULL) {
619 | userlist ',' opuser {
636 $$ = new_member($1, ALIAS);
639 $$ = new_member(NULL, ALL);
642 $$ = new_member($1, NETGROUP);
645 $$ = new_member($1, USERGROUP);
648 $$ = new_member($1, WORD);
653 | grouplist ',' opgroup {
670 $$ = new_member($1, ALIAS);
673 $$ = new_member(NULL, ALL);
676 $$ = new_member($1, WORD);
682 sudoerserror(const char *s)
684 debug_decl(sudoerserror, SUDO_DEBUG_PARSER)
686 /* If we last saw a newline the error is on the preceding line. */
687 if (last_token == COMMENT)
690 /* Save the line the first error occurred on. */
691 if (errorlineno == -1) {
692 errorlineno = sudolineno;
693 errorfile = estrdup(sudoers);
695 if (sudoers_warnings && s != NULL) {
698 if (trace_print == NULL || trace_print == sudoers_trace_print) {
699 const char fmt[] = ">>> %s: %s near line %d <<<\n";
702 /* Warnings are displayed in the user's locale. */
703 sudoers_setlocale(SUDOERS_LOCALE_USER, &oldlocale);
704 sudo_printf(SUDO_CONV_ERROR_MSG, _(fmt), sudoers, _(s), sudolineno);
705 sudoers_setlocale(oldlocale, NULL);
713 static struct defaults *
714 new_default(char *var, char *val, int op)
717 debug_decl(new_default, SUDO_DEBUG_PARSER)
719 d = ecalloc(1, sizeof(struct defaults));
722 tq_init(&d->binding);
726 /* d->next = NULL; */
731 static struct member *
732 new_member(char *name, int type)
735 debug_decl(new_member, SUDO_DEBUG_PARSER)
737 m = ecalloc(1, sizeof(struct member));
741 /* m->next = NULL; */
747 new_digest(int digest_type, const char *digest_str)
749 struct sudo_digest *dig;
750 debug_decl(new_digest, SUDO_DEBUG_PARSER)
752 dig = emalloc(sizeof(*dig));
753 dig->digest_type = digest_type;
754 dig->digest_str = estrdup(digest_str);
756 debug_return_ptr(dig);
760 * Add a list of defaults structures to the defaults list.
761 * The binding, if non-NULL, specifies a list of hosts, users, or
762 * runas users the entries apply to (specified by the type).
765 add_defaults(int type, struct member *bmem, struct defaults *defs)
768 struct member_list binding;
769 debug_decl(add_defaults, SUDO_DEBUG_PARSER)
772 * We can only call list2tq once on bmem as it will zero
773 * out the prev pointer when it consumes bmem.
775 list2tq(&binding, bmem);
778 * Set type and binding (who it applies to) for new entries.
780 for (d = defs; d != NULL; d = d->next) {
782 d->binding = binding;
784 tq_append(&defaults, defs);
790 * Allocate a new struct userspec, populate it, and insert it at the
791 * and of the userspecs list.
794 add_userspec(struct member *members, struct privilege *privs)
797 debug_decl(add_userspec, SUDO_DEBUG_PARSER)
799 u = ecalloc(1, sizeof(*u));
800 list2tq(&u->users, members);
801 list2tq(&u->privileges, privs);
803 /* u->next = NULL; */
804 tq_append(&userspecs, u);
810 * Free up space used by data structures from a previous parser run and sets
811 * the current sudoers file to path.
814 init_parser(const char *path, bool quiet)
817 struct member *m, *binding;
819 struct privilege *priv;
821 struct sudo_command *c;
822 debug_decl(init_parser, SUDO_DEBUG_PARSER)
824 while ((us = tq_pop(&userspecs)) != NULL) {
825 while ((m = tq_pop(&us->users)) != NULL) {
829 while ((priv = tq_pop(&us->privileges)) != NULL) {
830 struct member *runasuser = NULL, *runasgroup = NULL;
832 char *role = NULL, *type = NULL;
833 #endif /* HAVE_SELINUX */
835 char *privs = NULL, *limitprivs = NULL;
836 #endif /* HAVE_PRIV_SET */
838 while ((m = tq_pop(&priv->hostlist)) != NULL) {
842 while ((cs = tq_pop(&priv->cmndlist)) != NULL) {
844 /* Only free the first instance of a role/type. */
845 if (cs->role != role) {
849 if (cs->type != type) {
853 #endif /* HAVE_SELINUX */
855 /* Only free the first instance of privs/limitprivs. */
856 if (cs->privs != privs) {
860 if (cs->limitprivs != limitprivs) {
861 limitprivs = cs->limitprivs;
862 efree(cs->limitprivs);
864 #endif /* HAVE_PRIV_SET */
865 if (tq_last(&cs->runasuserlist) != runasuser) {
866 runasuser = tq_last(&cs->runasuserlist);
867 while ((m = tq_pop(&cs->runasuserlist)) != NULL) {
872 if (tq_last(&cs->runasgrouplist) != runasgroup) {
873 runasgroup = tq_last(&cs->runasgrouplist);
874 while ((m = tq_pop(&cs->runasgrouplist)) != NULL) {
879 if (cs->cmnd->type == COMMAND) {
880 c = (struct sudo_command *) cs->cmnd->name;
884 efree(cs->cmnd->name);
895 while ((d = tq_pop(&defaults)) != NULL) {
896 if (tq_last(&d->binding) != binding) {
897 binding = tq_last(&d->binding);
898 while ((m = tq_pop(&d->binding)) != NULL) {
899 if (m->type == COMMAND) {
900 c = (struct sudo_command *) m->name;
919 sudoers = path ? estrdup(path) : NULL;
924 sudoers_warnings = !quiet;