3 * Copyright (c) 1996, 1998-2005, 2007-2010
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 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
19 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21 * Sponsored in part by the Defense Advanced Research Projects
22 * Agency (DARPA) and Air Force Research Laboratory, Air Force
23 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
28 #include <sys/types.h>
29 #include <sys/param.h>
39 #endif /* STDC_HEADERS */
42 #endif /* HAVE_STRING_H */
45 #endif /* HAVE_STRINGS_H */
48 #endif /* HAVE_UNISTD_H */
49 #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
51 #endif /* HAVE_MALLOC_H && !STDC_HEADERS */
54 # define NAMLEN(dirent) strlen((dirent)->d_name)
56 # define dirent direct
57 # define NAMLEN(dirent) (dirent)->d_namlen
58 # ifdef HAVE_SYS_NDIR_H
59 # include <sys/ndir.h>
61 # ifdef HAVE_SYS_DIR_H
73 extern YYSTYPE yylval;
74 extern int parse_error;
77 static int sawspace = 0;
78 static int arg_len = 0;
79 static int arg_size = 0;
81 static int append __P((char *, int));
82 static int _fill __P((char *, int, int));
83 static int fill_cmnd __P((char *, int));
84 static int fill_args __P((char *, int, int));
85 static int _push_include __P((char *, int));
86 static int pop_include __P((void));
87 static int ipv6_valid __P((const char *s));
88 static char *parse_include __P((char *));
89 extern void yyerror __P((const char *));
91 #define fill(a, b) _fill(a, b, 0)
93 #define push_include(_p) (_push_include((_p), FALSE))
94 #define push_includedir(_p) (_push_include((_p), TRUE))
96 /* realloc() to size + COMMANDARGINC to make room for command args */
97 #define COMMANDARGINC 64
100 #define LEXTRACE(msg) fputs(msg, stderr)
102 #define LEXTRACE(msg)
106 HEX16 [0-9A-Fa-f]{1,4}
107 OCTET (1?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5])
108 IPV4ADDR {OCTET}(\.{OCTET}){3}
109 IPV6ADDR ({HEX16}?:){2,7}{HEX16}?|({HEX16}?:){2,6}:{IPV4ADDR}
111 HOSTNAME [[:alnum:]_-]+
112 WORD ([^#>!=:,\(\) \t\n\\]|\\[^\n])+
114 PATH \/(\\[\,:= \t#]|[^\,:=\\ \t\n#])+
115 ENVAR ([^#!=, \t\n\\\"]|\\[^\n])([^#=, \t\n\\\"]|\\[^\n])*
128 <GOTDEFS>[[:blank:]]+ BEGIN STARTDEFS;
130 <STARTDEFS>{DEFVAR} {
133 if (!fill(yytext, yyleng))
161 LEXTRACE("BEGINSTR ");
162 yylval.string = NULL;
167 LEXTRACE("WORD(2) ");
168 if (!fill(yytext, yyleng))
175 \\[[:blank:]]*\n[[:blank:]]* {
176 /* Line continuation char followed by newline. */
188 LEXTRACE("BACKSLASH ");
189 if (!append(yytext, yyleng))
194 LEXTRACE("STRBODY ");
195 if (!append(yytext, yyleng))
202 /* quoted fnmatch glob char, pass verbatim */
203 LEXTRACE("QUOTEDCHAR ");
204 if (!fill_args(yytext, 2, sawspace))
210 /* quoted sudoers special char, strip backslash */
211 LEXTRACE("QUOTEDCHAR ");
212 if (!fill_args(yytext + 1, 1, sawspace))
221 } /* end of command line args */
225 if (!fill_args(yytext, yyleng, sawspace))
228 } /* a command line arg */
231 <INITIAL>^#include[[:blank:]]+\/.*\n {
234 if ((path = parse_include(yytext)) == NULL)
237 LEXTRACE("INCLUDE\n");
239 /* Push current buffer and switch to include file */
240 if (!push_include(path))
244 <INITIAL>^#includedir[[:blank:]]+\/.*\n {
247 if ((path = parse_include(yytext)) == NULL)
250 LEXTRACE("INCLUDEDIR\n");
253 * Push current buffer and switch to include file.
254 * We simply ignore empty directories.
256 if (!push_includedir(path) && parse_error)
260 <INITIAL>^[[:blank:]]*Defaults([:@>\!]\!?{WORD})? {
262 for (n = 0; isblank((unsigned char)yytext[n]); n++)
266 switch (yytext[n++]) {
269 LEXTRACE("DEFAULTS_USER ");
270 return(DEFAULTS_USER);
273 LEXTRACE("DEFAULTS_RUNAS ");
274 return(DEFAULTS_RUNAS);
277 LEXTRACE("DEFAULTS_HOST ");
278 return(DEFAULTS_HOST);
281 LEXTRACE("DEFAULTS_CMND ");
282 return(DEFAULTS_CMND);
284 LEXTRACE("DEFAULTS ");
289 <INITIAL>^[[:blank:]]*(Host|Cmnd|User|Runas)_Alias {
291 for (n = 0; isblank((unsigned char)yytext[n]); n++)
295 LEXTRACE("HOSTALIAS ");
298 LEXTRACE("CMNDALIAS ");
301 LEXTRACE("USERALIAS ");
304 LEXTRACE("RUNASALIAS ");
309 NOPASSWD[[:blank:]]*: {
310 /* cmnd does not require passwd for this user */
311 LEXTRACE("NOPASSWD ");
315 PASSWD[[:blank:]]*: {
316 /* cmnd requires passwd for this user */
321 NOEXEC[[:blank:]]*: {
331 SETENV[[:blank:]]*: {
336 NOSETENV[[:blank:]]*: {
337 LEXTRACE("NOSETENV ");
341 LOG_OUTPUT[[:blank:]]*: {
342 LEXTRACE("LOG_OUTPUT ");
346 NOLOG_OUTPUT[[:blank:]]*: {
347 LEXTRACE("NOLOG_OUTPUT ");
348 return(NOLOG_OUTPUT);
351 LOG_INPUT[[:blank:]]*: {
352 LEXTRACE("LOG_INPUT ");
356 NOLOG_INPUT[[:blank:]]*: {
357 LEXTRACE("NOLOG_INPUT ");
363 if (!fill(yytext, yyleng))
365 LEXTRACE("NETGROUP ");
371 if (!fill(yytext, yyleng))
373 LEXTRACE("USERGROUP ");
377 {IPV4ADDR}(\/{IPV4ADDR})? {
378 if (!fill(yytext, yyleng))
380 LEXTRACE("NTWKADDR ");
384 {IPV4ADDR}\/([12][0-9]*|3[0-2]*) {
385 if (!fill(yytext, yyleng))
387 LEXTRACE("NTWKADDR ");
391 {IPV6ADDR}(\/{IPV6ADDR})? {
392 if (!ipv6_valid(yytext)) {
396 if (!fill(yytext, yyleng))
398 LEXTRACE("NTWKADDR ");
402 {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) {
403 if (!ipv6_valid(yytext)) {
407 if (!fill(yytext, yyleng))
409 LEXTRACE("NTWKADDR ");
413 [[:upper:]][[:upper:][:digit:]_]* {
414 if (strcmp(yytext, "ALL") == 0) {
419 /* XXX - restrict type/role to initial state */
420 if (strcmp(yytext, "TYPE") == 0) {
424 if (strcmp(yytext, "ROLE") == 0) {
428 #endif /* HAVE_SELINUX */
429 if (!fill(yytext, yyleng))
435 <GOTDEFS>({PATH}|sudoedit) {
436 /* no command args allowed for Defaults!/path */
437 if (!fill_cmnd(yytext, yyleng))
439 LEXTRACE("COMMAND ");
445 LEXTRACE("COMMAND ");
446 if (!fill_cmnd(yytext, yyleng))
451 /* directories can't have args... */
452 if (yytext[yyleng - 1] == '/') {
453 LEXTRACE("COMMAND ");
454 if (!fill_cmnd(yytext, yyleng))
459 LEXTRACE("COMMAND ");
460 if (!fill_cmnd(yytext, yyleng))
465 <INITIAL,GOTDEFS>\"[^"\n]+\" {
466 /* a quoted user/group name */
467 if (!fill(yytext + 1, yyleng - 2))
471 LEXTRACE("USERGROUP ");
474 LEXTRACE("NETGROUP ");
477 LEXTRACE("WORD(4) ");
482 <INITIAL,GOTDEFS>({ID}|{WORD}) {
484 if (!fill(yytext, yyleng))
486 LEXTRACE("WORD(5) ");
517 return('!'); /* return '!' */
525 } /* return newline */
527 <*>[[:blank:]]+ { /* throw away space/tabs */
528 sawspace = TRUE; /* but remember for fill_args */
531 <*>\\[[:blank:]]*\n {
532 sawspace = TRUE; /* remember for fill_args */
535 } /* throw away EOL after \ */
537 <INITIAL,STARTDEFS,INDEFS>#(-[^\n0-9].*|[^\n0-9-].*)?\n {
542 } /* comment, not uid/gid */
550 if (YY_START != INITIAL) {
567 s += 2; /* skip \\x */
568 for (i = 0; i < 2; i++) {
603 return((unsigned char)result);
607 _fill(src, len, olen)
613 dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1);
615 yyerror("unable to allocate memory");
620 /* Copy the string and collapse any escaped characters. */
623 if (*src == '\\' && len) {
624 if (src[1] == 'x' && len >= 3 &&
625 isxdigit((unsigned char) src[2]) &&
626 isxdigit((unsigned char) src[3])) {
627 *dst++ = hexchar(src);
650 if (yylval.string != NULL)
651 olen = strlen(yylval.string);
653 return(_fill(src, len, olen));
657 ((c) == ',' || (c) == ':' || (c) == '=' || (c) == ' ' || (c) == '\t' || (c) == '#')
667 arg_len = arg_size = 0;
669 dst = yylval.command.cmnd = (char *) malloc(len + 1);
670 if (yylval.command.cmnd == NULL) {
671 yyerror("unable to allocate memory");
675 /* Copy the string and collapse any escaped sudo-specific characters. */
676 for (i = 0; i < len; i++) {
677 if (src[i] == '\\' && i != len - 1 && SPECIAL(src[i + 1]))
684 yylval.command.args = NULL;
689 fill_args(s, len, addspace)
697 if (yylval.command.args == NULL) {
701 new_len = arg_len + len + addspace;
703 if (new_len >= arg_size) {
704 /* Allocate more space than we need for subsequent args */
705 while (new_len >= (arg_size += COMMANDARGINC))
708 p = yylval.command.args ?
709 (char *) realloc(yylval.command.args, arg_size) :
710 (char *) malloc(arg_size);
712 efree(yylval.command.args);
713 yyerror("unable to allocate memory");
716 yylval.command.args = p;
719 /* Efficiently append the arg (with a leading space if needed). */
720 p = yylval.command.args + arg_len;
723 if (strlcpy(p, s, arg_size - (p - yylval.command.args)) != len) {
724 yyerror("fill_args: buffer overflow"); /* paranoia */
733 struct path_list *next;
736 struct include_stack {
739 struct path_list *more; /* more files in case of includedir */
749 const struct path_list * const *p1 = v1;
750 const struct path_list * const *p2 = v2;
752 return(strcmp((*p1)->path, (*p2)->path));
756 switch_dir(stack, dirpath)
757 struct include_stack *stack;
765 struct path_list *pl, *first = NULL;
766 struct path_list **sorted = NULL;
768 if (!(dir = opendir(dirpath))) {
772 while ((dent = readdir(dir))) {
773 /* Ignore files that end in '~' or have a '.' in them. */
774 if (dent->d_name[0] == '\0' || dent->d_name[NAMLEN(dent) - 1] == '~'
775 || strchr(dent->d_name, '.') != NULL) {
778 if (asprintf(&path, "%s/%s", dirpath, dent->d_name) == -1) {
782 if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) {
786 pl = malloc(sizeof(*pl));
799 /* Sort the list as an array. */
800 sorted = malloc(sizeof(*sorted) * count);
804 for (i = 0; i < count; i++) {
808 qsort(sorted, count, sizeof(*sorted), pl_compare);
810 /* Apply sorting to the list. */
812 sorted[count - 1]->next = NULL;
813 for (i = 1; i < count; i++)
814 sorted[i - 1]->next = sorted[i];
817 /* Pull out the first element for parsing, leave the rest for later. */
830 while (first != NULL) {
842 #define MAX_SUDOERS_DEPTH 128
843 #define SUDOERS_STACK_INCREMENT 16
845 static size_t istacksize, idepth;
846 static struct include_stack *istack;
852 struct path_list *pl;
856 while ((pl = istack[idepth].more) != NULL) {
857 istack[idepth].more = pl->next;
861 efree(istack[idepth].path);
862 if (idepth && !istack[idepth].keepopen)
863 fclose(istack[idepth].bs->yy_input_file);
864 yy_delete_buffer(istack[idepth].bs);
868 istacksize = idepth = 0;
873 _push_include(path, isdir)
877 struct path_list *pl;
880 /* push current state onto stack */
881 if (idepth >= istacksize) {
882 if (idepth > MAX_SUDOERS_DEPTH) {
883 yyerror("too many levels of includes");
886 istacksize += SUDOERS_STACK_INCREMENT;
887 istack = (struct include_stack *) realloc(istack,
888 sizeof(*istack) * istacksize);
889 if (istack == NULL) {
890 yyerror("unable to allocate memory");
895 if (!(path = switch_dir(&istack[idepth], path))) {
896 /* switch_dir() called yyerror() for us */
899 while ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) {
900 /* Unable to open path in includedir, go to next one, if any. */
902 if ((pl = istack[idepth].more) == NULL)
905 istack[idepth].more = pl->next;
909 if ((fp = open_sudoers(path, TRUE, &keepopen)) == NULL) {
913 istack[idepth].more = NULL;
915 /* Push the old (current) file and open the new one. */
916 istack[idepth].path = sudoers; /* push old path */
917 istack[idepth].bs = YY_CURRENT_BUFFER;
918 istack[idepth].lineno = sudolineno;
919 istack[idepth].keepopen = keepopen;
923 yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
931 struct path_list *pl;
938 fclose(YY_CURRENT_BUFFER->yy_input_file);
939 yy_delete_buffer(YY_CURRENT_BUFFER);
940 /* If we are in an include dir, move to the next file. */
941 while ((pl = istack[idepth - 1].more) != NULL) {
942 fp = open_sudoers(pl->path, FALSE, &keepopen);
944 istack[idepth - 1].more = pl->next;
948 yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
952 /* Unable to open path in include dir, go to next one. */
953 istack[idepth - 1].more = pl->next;
957 /* If no path list, just pop the last dir on the stack. */
960 yy_switch_to_buffer(istack[idepth].bs);
962 sudoers = istack[idepth].path;
963 sudolineno = istack[idepth].lineno;
964 keepopen = istack[idepth].keepopen;
973 char *cp, *ep, *path;
974 int len = 0, subst = 0;
975 size_t shost_len = 0;
977 /* Pull out path from #include line. */
978 cp = base + sizeof("#include");
980 cp += 3; /* includedir */
981 while (isblank((unsigned char) *cp))
984 while (*ep != '\0' && !isspace((unsigned char) *ep)) {
985 if (ep[0] == '%' && ep[1] == 'h') {
986 shost_len = strlen(user_shost);
987 len += shost_len - 2;
993 /* Make a copy of path and return it. */
994 len += (int)(ep - cp);
995 if ((path = malloc(len + 1)) == NULL)
996 yyerror("unable to allocate memory");
998 /* substitute for %h */
1001 if (cp[0] == '%' && cp[1] == 'h') {
1002 memcpy(pp, user_shost, shost_len);
1011 memcpy(path, cp, len);
1015 /* Push any excess characters (e.g. comment, newline) back to the lexer */
1017 yyless((int)(ep - base));
1023 * Check to make sure an IPv6 address does not contain multiple instances
1024 * of the string "::". Assumes strlen(s) >= 1.
1025 * Returns TRUE if address is valid else FALSE.
1033 for (; *s != '\0'; s++) {
1034 if (s[0] == ':' && s[1] == ':') {
1039 nmatch = 0; /* reset if we hit netmask */
1042 return (nmatch <= 1);