--- embedaddon/sudo/plugins/sudoers/toke.l 2012/02/21 16:23:02 1.1 +++ embedaddon/sudo/plugins/sudoers/toke.l 2014/06/15 16:12:54 1.1.1.5 @@ -1,6 +1,6 @@ %{ /* - * Copyright (c) 1996, 1998-2005, 2007-2011 + * Copyright (c) 1996, 1998-2005, 2007-2013 * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any @@ -26,7 +26,6 @@ #include #include -#include #include #include #ifdef STDC_HEADERS @@ -43,6 +42,11 @@ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#endif #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ @@ -71,27 +75,39 @@ #include "parse.h" #include "toke.h" #include +#include "lbuf.h" +#include "sha2.h" +#include "secure_path.h" -extern YYSTYPE yylval; -extern int parse_error; -int sudolineno; -char *sudoers; +int sudolineno; /* current sudoers line number. */ +int last_token; /* last token that was parsed. */ +char *sudoers; /* sudoers file being parsed. */ -static int continued, prev_state, sawspace; +/* Default sudoers path, mode and owner (may be set via sudo.conf) */ +const char *sudoers_file = _PATH_SUDOERS; +mode_t sudoers_mode = SUDOERS_MODE; +uid_t sudoers_uid = SUDOERS_UID; +gid_t sudoers_gid = SUDOERS_GID; -static int _push_include(char *, int); -static int pop_include(void); +static bool continued, sawspace; +static int prev_state; +static int digest_len; + +static bool _push_include(char *, bool); +static bool pop_include(void); static char *parse_include(char *); -#ifdef TRACELEXER -static int sudoers_trace_print(const char *msg); -#else -# define sudoers_trace_print NULL -#endif int (*trace_print)(const char *msg) = sudoers_trace_print; -#define push_include(_p) (_push_include((_p), FALSE)) -#define push_includedir(_p) (_push_include((_p), TRUE)) +#define LEXRETURN(n) do { \ + last_token = (n); \ + return (n); \ +} while (0) + +#define ECHO ignore_result(fwrite(sudoerstext, sudoersleng, 1, sudoersout)) + +#define push_include(_p) (_push_include((_p), false)) +#define push_includedir(_p) (_push_include((_p), true)) %} HEX16 [0-9A-Fa-f]{1,4} @@ -109,17 +125,19 @@ DEFVAR [a-z_]+ %option noinput %option nounput %option noyywrap +%option prefix="sudoers" %s GOTDEFS %x GOTCMND %x STARTDEFS %x INDEFS %x INSTR +%s WANTDIGEST %% [[:blank:]]*,[[:blank:]]* { LEXTRACE(", "); - return ','; + LEXRETURN(','); } /* return ',' */ [[:blank:]]+ BEGIN STARTDEFS; @@ -127,96 +145,96 @@ DEFVAR [a-z_]+ {DEFVAR} { BEGIN INDEFS; LEXTRACE("DEFVAR "); - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); - return DEFVAR; + LEXRETURN(DEFVAR); } { , { BEGIN STARTDEFS; LEXTRACE(", "); - return ','; + LEXRETURN(','); } /* return ',' */ = { LEXTRACE("= "); - return '='; + LEXRETURN('='); } /* return '=' */ \+= { LEXTRACE("+= "); - return '+'; + LEXRETURN('+'); } /* return '+' */ -= { LEXTRACE("-= "); - return '-'; + LEXRETURN('-'); } /* return '-' */ \" { LEXTRACE("BEGINSTR "); - yylval.string = NULL; + sudoerslval.string = NULL; prev_state = YY_START; BEGIN INSTR; } {ENVAR} { LEXTRACE("WORD(2) "); - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); - return WORD; + LEXRETURN(WORD); } } { \\[[:blank:]]*\n[[:blank:]]* { /* Line continuation char followed by newline. */ - ++sudolineno; - continued = TRUE; + sudolineno++; + continued = true; } \" { LEXTRACE("ENDSTR "); BEGIN prev_state; - if (yylval.string == NULL) { + if (sudoerslval.string == NULL) { LEXTRACE("ERROR "); /* empty string */ - return ERROR; + LEXRETURN(ERROR); } if (prev_state == INITIAL) { - switch (yylval.string[0]) { + switch (sudoerslval.string[0]) { case '%': - if (yylval.string[1] == '\0' || - (yylval.string[1] == ':' && - yylval.string[2] == '\0')) { + if (sudoerslval.string[1] == '\0' || + (sudoerslval.string[1] == ':' && + sudoerslval.string[2] == '\0')) { LEXTRACE("ERROR "); /* empty group */ - return ERROR; + LEXRETURN(ERROR); } LEXTRACE("USERGROUP "); - return USERGROUP; + LEXRETURN(USERGROUP); case '+': - if (yylval.string[1] == '\0') { + if (sudoerslval.string[1] == '\0') { LEXTRACE("ERROR "); /* empty netgroup */ - return ERROR; + LEXRETURN(ERROR); } LEXTRACE("NETGROUP "); - return NETGROUP; + LEXRETURN(NETGROUP); } } LEXTRACE("WORD(4) "); - return WORD; + LEXRETURN(WORD); } \\ { LEXTRACE("BACKSLASH "); - if (!append(yytext, yyleng)) + if (!append(sudoerstext, sudoersleng)) yyterminate(); } ([^\"\n\\]|\\\")+ { LEXTRACE("STRBODY "); - if (!append(yytext, yyleng)) + if (!append(sudoerstext, sudoersleng)) yyterminate(); } } @@ -225,42 +243,76 @@ DEFVAR [a-z_]+ \\[\*\?\[\]\!] { /* quoted fnmatch glob char, pass verbatim */ LEXTRACE("QUOTEDCHAR "); - if (!fill_args(yytext, 2, sawspace)) + if (!fill_args(sudoerstext, 2, sawspace)) yyterminate(); - sawspace = FALSE; + sawspace = false; } \\[:\\,= \t#] { /* quoted sudoers special char, strip backslash */ LEXTRACE("QUOTEDCHAR "); - if (!fill_args(yytext + 1, 1, sawspace)) + if (!fill_args(sudoerstext + 1, 1, sawspace)) yyterminate(); - sawspace = FALSE; + sawspace = false; } [#:\,=\n] { BEGIN INITIAL; yyless(0); - return COMMAND; + LEXRETURN(COMMAND); } /* end of command line args */ [^#\\:, \t\n]+ { LEXTRACE("ARG "); - if (!fill_args(yytext, yyleng, sawspace)) + if (!fill_args(sudoerstext, sudoersleng, sawspace)) yyterminate(); - sawspace = FALSE; + sawspace = false; } /* a command line arg */ } -^#include[[:blank:]]+\/.*\n { +[[:xdigit:]]+ { + /* Only return DIGEST if the length is correct. */ + if (sudoersleng == digest_len * 2) { + if (!fill(sudoerstext, sudoersleng)) + yyterminate(); + BEGIN INITIAL; + LEXTRACE("DIGEST "); + LEXRETURN(DIGEST); + } + BEGIN INITIAL; + yyless(sudoersleng); + } /* hex digest */ + +[A-Za-z0-9\+/=]+ { + /* Only return DIGEST if the length is correct. */ + int len; + if (sudoerstext[sudoersleng - 1] == '=') { + /* use padding */ + len = 4 * ((digest_len + 2) / 3); + } else { + /* no padding */ + len = (4 * digest_len + 2) / 3; + } + if (sudoersleng == len) { + if (!fill(sudoerstext, sudoersleng)) + yyterminate(); + BEGIN INITIAL; + LEXTRACE("DIGEST "); + LEXRETURN(DIGEST); + } + BEGIN INITIAL; + yyless(sudoersleng); + } /* base64 digest */ + +^#include[[:blank:]]+.*\n { char *path; if (continued) { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } - if ((path = parse_include(yytext)) == NULL) + if ((path = parse_include(sudoerstext)) == NULL) yyterminate(); LEXTRACE("INCLUDE\n"); @@ -270,15 +322,15 @@ DEFVAR [a-z_]+ yyterminate(); } -^#includedir[[:blank:]]+\/.*\n { +^#includedir[[:blank:]]+.*\n { char *path; if (continued) { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } - if ((path = parse_include(yytext)) == NULL) + if ((path = parse_include(sudoerstext)) == NULL) yyterminate(); LEXTRACE("INCLUDEDIR\n"); @@ -297,14 +349,14 @@ DEFVAR [a-z_]+ if (continued) { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } - for (n = 0; isblank((unsigned char)yytext[n]); n++) + for (n = 0; isblank((unsigned char)sudoerstext[n]); n++) continue; n += sizeof("Defaults") - 1; - if ((deftype = yytext[n++]) != '\0') { - while (isblank((unsigned char)yytext[n])) + if ((deftype = sudoerstext[n++]) != '\0') { + while (isblank((unsigned char)sudoerstext[n])) n++; } BEGIN GOTDEFS; @@ -312,22 +364,22 @@ DEFVAR [a-z_]+ case ':': yyless(n); LEXTRACE("DEFAULTS_USER "); - return DEFAULTS_USER; + LEXRETURN(DEFAULTS_USER); case '>': yyless(n); LEXTRACE("DEFAULTS_RUNAS "); - return DEFAULTS_RUNAS; + LEXRETURN(DEFAULTS_RUNAS); case '@': yyless(n); LEXTRACE("DEFAULTS_HOST "); - return DEFAULTS_HOST; + LEXRETURN(DEFAULTS_HOST); case '!': yyless(n); LEXTRACE("DEFAULTS_CMND "); - return DEFAULTS_CMND; + LEXRETURN(DEFAULTS_CMND); default: LEXTRACE("DEFAULTS "); - return DEFAULTS; + LEXRETURN(DEFAULTS); } } @@ -336,147 +388,147 @@ DEFVAR [a-z_]+ if (continued) { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } - for (n = 0; isblank((unsigned char)yytext[n]); n++) + for (n = 0; isblank((unsigned char)sudoerstext[n]); n++) continue; - switch (yytext[n]) { + switch (sudoerstext[n]) { case 'H': LEXTRACE("HOSTALIAS "); - return HOSTALIAS; + LEXRETURN(HOSTALIAS); case 'C': LEXTRACE("CMNDALIAS "); - return CMNDALIAS; + LEXRETURN(CMNDALIAS); case 'U': LEXTRACE("USERALIAS "); - return USERALIAS; + LEXRETURN(USERALIAS); case 'R': LEXTRACE("RUNASALIAS "); - return RUNASALIAS; + LEXRETURN(RUNASALIAS); } } NOPASSWD[[:blank:]]*: { /* cmnd does not require passwd for this user */ LEXTRACE("NOPASSWD "); - return NOPASSWD; + LEXRETURN(NOPASSWD); } PASSWD[[:blank:]]*: { /* cmnd requires passwd for this user */ LEXTRACE("PASSWD "); - return PASSWD; + LEXRETURN(PASSWD); } NOEXEC[[:blank:]]*: { LEXTRACE("NOEXEC "); - return NOEXEC; + LEXRETURN(NOEXEC); } EXEC[[:blank:]]*: { LEXTRACE("EXEC "); - return EXEC; + LEXRETURN(EXEC); } SETENV[[:blank:]]*: { LEXTRACE("SETENV "); - return SETENV; + LEXRETURN(SETENV); } NOSETENV[[:blank:]]*: { LEXTRACE("NOSETENV "); - return NOSETENV; + LEXRETURN(NOSETENV); } LOG_OUTPUT[[:blank:]]*: { LEXTRACE("LOG_OUTPUT "); - return LOG_OUTPUT; + LEXRETURN(LOG_OUTPUT); } NOLOG_OUTPUT[[:blank:]]*: { LEXTRACE("NOLOG_OUTPUT "); - return NOLOG_OUTPUT; + LEXRETURN(NOLOG_OUTPUT); } LOG_INPUT[[:blank:]]*: { LEXTRACE("LOG_INPUT "); - return LOG_INPUT; + LEXRETURN(LOG_INPUT); } NOLOG_INPUT[[:blank:]]*: { LEXTRACE("NOLOG_INPUT "); - return NOLOG_INPUT; + LEXRETURN(NOLOG_INPUT); } (\+|\%|\%:) { /* empty group or netgroup */ LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } \+{WORD} { /* netgroup */ - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("NETGROUP "); - return NETGROUP; + LEXRETURN(NETGROUP); } \%:?({WORD}|{ID}) { /* group */ - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("USERGROUP "); - return USERGROUP; + LEXRETURN(USERGROUP); } {IPV4ADDR}(\/{IPV4ADDR})? { - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("NTWKADDR "); - return NTWKADDR; + LEXRETURN(NTWKADDR); } {IPV4ADDR}\/([12]?[0-9]|3[0-2]) { - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("NTWKADDR "); - return NTWKADDR; + LEXRETURN(NTWKADDR); } {IPV6ADDR}(\/{IPV6ADDR})? { - if (!ipv6_valid(yytext)) { + if (!ipv6_valid(sudoerstext)) { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("NTWKADDR "); - return NTWKADDR; + LEXRETURN(NTWKADDR); } {IPV6ADDR}\/([0-9]|[1-9][0-9]|1[01][0-9]|12[0-8]) { - if (!ipv6_valid(yytext)) { + if (!ipv6_valid(sudoerstext)) { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("NTWKADDR "); - return NTWKADDR; + LEXRETURN(NTWKADDR); } ALL { LEXTRACE("ALL "); - return ALL; + LEXRETURN(ALL); } ROLE { #ifdef HAVE_SELINUX LEXTRACE("ROLE "); - return ROLE; + LEXRETURN(ROLE); #else goto got_alias; #endif @@ -485,139 +537,189 @@ ALL { TYPE { #ifdef HAVE_SELINUX LEXTRACE("TYPE "); - return TYPE; + LEXRETURN(TYPE); #else goto got_alias; #endif } +PRIVS { +#ifdef HAVE_PRIV_SET + LEXTRACE("PRIVS "); + LEXRETURN(PRIVS); +#else + goto got_alias; +#endif + } +LIMITPRIVS { +#ifdef HAVE_PRIV_SET + LEXTRACE("LIMITPRIVS "); + LEXRETURN(LIMITPRIVS); +#else + goto got_alias; +#endif + } + [[:upper:]][[:upper:][:digit:]_]* { -#ifndef HAVE_SELINUX got_alias: -#endif - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("ALIAS "); - return ALIAS; + LEXRETURN(ALIAS); } ({PATH}|sudoedit) { + /* XXX - no way to specify digest for command */ /* no command args allowed for Defaults!/path */ - if (!fill_cmnd(yytext, yyleng)) + if (!fill_cmnd(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("COMMAND "); - return COMMAND; + LEXRETURN(COMMAND); } +sha224 { + digest_len = SHA224_DIGEST_LENGTH; + BEGIN WANTDIGEST; + LEXTRACE("SHA224 "); + LEXRETURN(SHA224); + } + +sha256 { + digest_len = SHA256_DIGEST_LENGTH; + BEGIN WANTDIGEST; + LEXTRACE("SHA256 "); + LEXRETURN(SHA256); + } + +sha384 { + digest_len = SHA384_DIGEST_LENGTH; + BEGIN WANTDIGEST; + LEXTRACE("SHA384 "); + LEXRETURN(SHA384); + } + +sha512 { + digest_len = SHA512_DIGEST_LENGTH; + BEGIN WANTDIGEST; + LEXTRACE("SHA512 "); + LEXRETURN(SHA512); + } + sudoedit { BEGIN GOTCMND; LEXTRACE("COMMAND "); - if (!fill_cmnd(yytext, yyleng)) + if (!fill_cmnd(sudoerstext, sudoersleng)) yyterminate(); } /* sudo -e */ {PATH} { /* directories can't have args... */ - if (yytext[yyleng - 1] == '/') { + if (sudoerstext[sudoersleng - 1] == '/') { LEXTRACE("COMMAND "); - if (!fill_cmnd(yytext, yyleng)) + if (!fill_cmnd(sudoerstext, sudoersleng)) yyterminate(); - return COMMAND; + LEXRETURN(COMMAND); } else { BEGIN GOTCMND; LEXTRACE("COMMAND "); - if (!fill_cmnd(yytext, yyleng)) + if (!fill_cmnd(sudoerstext, sudoersleng)) yyterminate(); } } /* a pathname */ \" { LEXTRACE("BEGINSTR "); - yylval.string = NULL; + sudoerslval.string = NULL; prev_state = YY_START; BEGIN INSTR; } ({ID}|{WORD}) { /* a word */ - if (!fill(yytext, yyleng)) + if (!fill(sudoerstext, sudoersleng)) yyterminate(); LEXTRACE("WORD(5) "); - return WORD; + LEXRETURN(WORD); } \( { LEXTRACE("( "); - return '('; + LEXRETURN('('); } \) { LEXTRACE(") "); - return ')'; + LEXRETURN(')'); } , { LEXTRACE(", "); - return ','; + LEXRETURN(','); } /* return ',' */ = { LEXTRACE("= "); - return '='; + LEXRETURN('='); } /* return '=' */ : { LEXTRACE(": "); - return ':'; + LEXRETURN(':'); } /* return ':' */ <*>!+ { - if (yyleng & 1) { + if (sudoersleng & 1) { LEXTRACE("!"); - return '!'; /* return '!' */ + LEXRETURN('!'); /* return '!' */ } } <*>\n { if (YY_START == INSTR) { LEXTRACE("ERROR "); - return ERROR; /* line break in string */ + LEXRETURN(ERROR); /* line break in string */ } BEGIN INITIAL; - ++sudolineno; - continued = FALSE; + sudolineno++; + continued = false; LEXTRACE("\n"); - return COMMENT; + LEXRETURN(COMMENT); } /* return newline */ <*>[[:blank:]]+ { /* throw away space/tabs */ - sawspace = TRUE; /* but remember for fill_args */ + sawspace = true; /* but remember for fill_args */ } <*>\\[[:blank:]]*\n { - sawspace = TRUE; /* remember for fill_args */ - ++sudolineno; - continued = TRUE; + sawspace = true; /* remember for fill_args */ + sudolineno++; + continued = true; } /* throw away EOL after \ */ -#(-[^\n0-9].*|[^\n0-9-].*)?\n { - BEGIN INITIAL; - ++sudolineno; - continued = FALSE; +#(-[^\n0-9].*|[^\n0-9-].*)?\n? { + if (sudoerstext[sudoersleng - 1] == '\n') { + /* comment ending in a newline */ + BEGIN INITIAL; + sudolineno++; + continued = false; + } else if (!feof(yyin)) { + LEXTRACE("ERROR "); + LEXRETURN(ERROR); + } LEXTRACE("#\n"); - return COMMENT; + LEXRETURN(COMMENT); } /* comment, not uid/gid */ <*>. { LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } /* parse error */ <*><> { if (YY_START != INITIAL) { BEGIN INITIAL; LEXTRACE("ERROR "); - return ERROR; + LEXRETURN(ERROR); } if (!pop_include()) yyterminate(); @@ -625,50 +727,56 @@ sudoedit { %% struct path_list { + SLIST_ENTRY(path_list) entries; char *path; - struct path_list *next; }; +SLIST_HEAD(path_list_head, path_list); + struct include_stack { YY_BUFFER_STATE bs; char *path; - struct path_list *more; /* more files in case of includedir */ + struct path_list_head more; /* more files in case of includedir */ int lineno; - int keepopen; + bool keepopen; }; +/* + * Compare two struct path_list structs in reverse order. + */ static int pl_compare(const void *v1, const void *v2) { const struct path_list * const *p1 = v1; const struct path_list * const *p2 = v2; - return strcmp((*p1)->path, (*p2)->path); + return strcmp((*p2)->path, (*p1)->path); } static char * switch_dir(struct include_stack *stack, char *dirpath) { DIR *dir; - int i, count = 0; + unsigned int i, count = 0; + unsigned int max_paths = 32; char *path = NULL; struct dirent *dent; struct stat sb; - struct path_list *pl, *first = NULL; - struct path_list **sorted = NULL; + struct path_list *pl, **paths = NULL; + debug_decl(switch_dir, SUDO_DEBUG_PARSER) if (!(dir = opendir(dirpath))) { if (errno != ENOENT) { - char *errbuf; - if (asprintf(&errbuf, _("%s: %s"), dirpath, strerror(errno)) != -1) { - yyerror(errbuf); - free(errbuf); - } else { - yyerror(_("unable to allocate memory")); - } + warning("%s", dirpath); + sudoerserror(NULL); } goto done; } + paths = malloc(sizeof(*paths) * max_paths); + if (paths == NULL) { + closedir(dir); + goto bad; + } while ((dent = readdir(dir))) { /* Ignore files that end in '~' or have a '.' in them. */ if (dent->d_name[0] == '\0' || dent->d_name[NAMLEN(dent) - 1] == '~' @@ -688,56 +796,50 @@ switch_dir(struct include_stack *stack, char *dirpath) if (pl == NULL) goto bad; pl->path = path; - pl->next = first; - first = pl; - count++; + if (count >= max_paths) { + struct path_list **tmp; + max_paths <<= 1; + tmp = realloc(paths, sizeof(*paths) * max_paths); + if (tmp == NULL) { + closedir(dir); + goto bad; + } + paths = tmp; + } + paths[count++] = pl; + path = NULL; } closedir(dir); if (count == 0) goto done; - /* Sort the list as an array. */ - sorted = malloc(sizeof(*sorted) * count); - if (sorted == NULL) - goto bad; - pl = first; + /* Sort the list as an array in reverse order. */ + qsort(paths, count, sizeof(*paths), pl_compare); + + /* Build up the list in sorted order. */ for (i = 0; i < count; i++) { - sorted[i] = pl; - pl = pl->next; + SLIST_INSERT_HEAD(&stack->more, paths[i], entries); } - qsort(sorted, count, sizeof(*sorted), pl_compare); - /* Apply sorting to the list. */ - first = sorted[0]; - sorted[count - 1]->next = NULL; - for (i = 1; i < count; i++) - sorted[i - 1]->next = sorted[i]; - efree(sorted); - /* Pull out the first element for parsing, leave the rest for later. */ - if (count) { - path = first->path; - pl = first->next; - efree(first); - stack->more = pl; - } else { - path = NULL; - } + pl = SLIST_FIRST(&stack->more); + SLIST_REMOVE_HEAD(&stack->more, entries); + path = pl->path; + efree(pl); done: + efree(paths); efree(dirpath); - return path; + debug_return_str(path); bad: - while (first != NULL) { - pl = first; - first = pl->next; - free(pl->path); - free(pl); + for (i = 0; i < count; i++) { + efree(paths[i]->path); + efree(paths[i]); } - efree(sorted); + efree(paths); efree(dirpath); efree(path); - return NULL; + debug_return_str(NULL); } #define MAX_SUDOERS_DEPTH 128 @@ -745,81 +847,119 @@ bad: static size_t istacksize, idepth; static struct include_stack *istack; -static int keepopen; +static bool keepopen; void init_lexer(void) { struct path_list *pl; + debug_decl(init_lexer, SUDO_DEBUG_PARSER) while (idepth) { idepth--; - while ((pl = istack[idepth].more) != NULL) { - istack[idepth].more = pl->next; + while ((pl = SLIST_FIRST(&istack[idepth].more)) != NULL) { + SLIST_REMOVE_HEAD(&istack[idepth].more, entries); efree(pl->path); efree(pl); } efree(istack[idepth].path); if (idepth && !istack[idepth].keepopen) fclose(istack[idepth].bs->yy_input_file); - yy_delete_buffer(istack[idepth].bs); + sudoers_delete_buffer(istack[idepth].bs); } efree(istack); istack = NULL; istacksize = idepth = 0; sudolineno = 1; - keepopen = FALSE; - sawspace = FALSE; - continued = FALSE; + keepopen = false; + sawspace = false; + continued = false; prev_state = INITIAL; + + debug_return; } -static int -_push_include(char *path, int isdir) +static bool +_push_include(char *path, bool isdir) { struct path_list *pl; FILE *fp; + debug_decl(_push_include, SUDO_DEBUG_PARSER) /* push current state onto stack */ if (idepth >= istacksize) { + struct include_stack *new_istack; + if (idepth > MAX_SUDOERS_DEPTH) { - yyerror(_("too many levels of includes")); - return FALSE; + sudoerserror(N_("too many levels of includes")); + debug_return_bool(false); } istacksize += SUDOERS_STACK_INCREMENT; - istack = (struct include_stack *) realloc(istack, + new_istack = (struct include_stack *) realloc(istack, sizeof(*istack) * istacksize); - if (istack == NULL) { - yyerror(_("unable to allocate memory")); - return FALSE; + if (new_istack == NULL) { + warning(NULL); + sudoerserror(NULL); + debug_return_bool(false); } + istack = new_istack; } + SLIST_INIT(&istack[idepth].more); if (isdir) { + struct stat sb; + switch (sudo_secure_dir(path, sudoers_uid, sudoers_gid, &sb)) { + case SUDO_PATH_SECURE: + break; + case SUDO_PATH_MISSING: + debug_return_bool(false); + case SUDO_PATH_BAD_TYPE: + errno = ENOTDIR; + if (sudoers_warnings) { + warning("%s", path); + } + debug_return_bool(false); + case SUDO_PATH_WRONG_OWNER: + if (sudoers_warnings) { + warningx(U_("%s is owned by uid %u, should be %u"), + path, (unsigned int) sb.st_uid, + (unsigned int) sudoers_uid); + } + debug_return_bool(false); + case SUDO_PATH_WORLD_WRITABLE: + if (sudoers_warnings) { + warningx(U_("%s is world writable"), path); + } + debug_return_bool(false); + case SUDO_PATH_GROUP_WRITABLE: + if (sudoers_warnings) { + warningx(U_("%s is owned by gid %u, should be %u"), + path, (unsigned int) sb.st_gid, + (unsigned int) sudoers_gid); + } + debug_return_bool(false); + default: + /* NOTREACHED */ + debug_return_bool(false); + } if (!(path = switch_dir(&istack[idepth], path))) { - /* switch_dir() called yyerror() for us */ - return FALSE; + /* switch_dir() called sudoerserror() for us */ + debug_return_bool(false); } - while ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) { + while ((fp = open_sudoers(path, false, &keepopen)) == NULL) { /* Unable to open path in includedir, go to next one, if any. */ efree(path); - if ((pl = istack[idepth].more) == NULL) - return FALSE; + if ((pl = SLIST_FIRST(&istack[idepth].more)) == NULL) + debug_return_bool(false); + SLIST_REMOVE_HEAD(&istack[idepth].more, entries); path = pl->path; - istack[idepth].more = pl->next; efree(pl); } } else { - if ((fp = open_sudoers(path, TRUE, &keepopen)) == NULL) { - char *errbuf; - if (asprintf(&errbuf, _("%s: %s"), path, strerror(errno)) != -1) { - yyerror(errbuf); - free(errbuf); - } else { - yyerror(_("unable to allocate memory")); - } - return FALSE; + if ((fp = open_sudoers(path, true, &keepopen)) == NULL) { + /* The error was already printed by open_sudoers() */ + sudoerserror(NULL); + debug_return_bool(false); } - istack[idepth].more = NULL; } /* Push the old (current) file and open the new one. */ istack[idepth].path = sudoers; /* push old path */ @@ -829,58 +969,59 @@ _push_include(char *path, int isdir) idepth++; sudolineno = 1; sudoers = path; - yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE)); - return TRUE; + debug_return_bool(true); } -static int +static bool pop_include(void) { struct path_list *pl; FILE *fp; + debug_decl(pop_include, SUDO_DEBUG_PARSER) if (idepth == 0) - return FALSE; + debug_return_bool(false); if (!keepopen) fclose(YY_CURRENT_BUFFER->yy_input_file); - yy_delete_buffer(YY_CURRENT_BUFFER); + sudoers_delete_buffer(YY_CURRENT_BUFFER); /* If we are in an include dir, move to the next file. */ - while ((pl = istack[idepth - 1].more) != NULL) { - fp = open_sudoers(pl->path, FALSE, &keepopen); + while ((pl = SLIST_FIRST(&istack[idepth - 1].more)) != NULL) { + SLIST_REMOVE_HEAD(&istack[idepth - 1].more, entries); + fp = open_sudoers(pl->path, false, &keepopen); if (fp != NULL) { - istack[idepth - 1].more = pl->next; efree(sudoers); sudoers = pl->path; sudolineno = 1; - yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE)); efree(pl); break; } /* Unable to open path in include dir, go to next one. */ - istack[idepth - 1].more = pl->next; efree(pl->path); efree(pl); } /* If no path list, just pop the last dir on the stack. */ if (pl == NULL) { idepth--; - yy_switch_to_buffer(istack[idepth].bs); + sudoers_switch_to_buffer(istack[idepth].bs); efree(sudoers); sudoers = istack[idepth].path; sudolineno = istack[idepth].lineno; keepopen = istack[idepth].keepopen; } - return TRUE; + debug_return_bool(true); } static char * parse_include(char *base) { - char *cp, *ep, *path; - int len = 0, subst = 0; + char *cp, *ep, *path, *pp; + int dirlen = 0, len = 0, subst = 0; size_t shost_len = 0; + debug_decl(parse_include, SUDO_DEBUG_PARSER) /* Pull out path from #include line. */ cp = base + sizeof("#include"); @@ -898,15 +1039,27 @@ parse_include(char *base) ep++; } - /* Make a copy of path and return it. */ + /* Relative paths are located in the same dir as the sudoers file. */ + if (*cp != '/') { + char *dirend = strrchr(sudoers, '/'); + if (dirend != NULL) + dirlen = (int)(dirend - sudoers) + 1; + } + + /* Make a copy of the fully-qualified path and return it. */ len += (int)(ep - cp); - if ((path = malloc(len + 1)) == NULL) { - yyerror(_("unable to allocate memory")); - return NULL; + path = pp = malloc(len + dirlen + 1); + if (path == NULL) { + warning(NULL); + sudoerserror(NULL); + debug_return_str(NULL); } + if (dirlen) { + memcpy(path, sudoers, dirlen); + pp += dirlen; + } if (subst) { /* substitute for %h */ - char *pp = path; while (cp < ep) { if (cp[0] == '%' && cp[1] == 'h') { memcpy(pp, user_shost, shost_len); @@ -918,21 +1071,43 @@ parse_include(char *base) } *pp = '\0'; } else { - memcpy(path, cp, len); - path[len] = '\0'; + memcpy(pp, cp, len); + pp[len] = '\0'; } /* Push any excess characters (e.g. comment, newline) back to the lexer */ if (*ep != '\0') yyless((int)(ep - base)); - return path; + debug_return_str(path); } #ifdef TRACELEXER -static int +int sudoers_trace_print(const char *msg) { return fputs(msg, stderr); +} +#else +int +sudoers_trace_print(const char *msg) +{ + static bool initialized; + static struct lbuf lbuf; + + if (!initialized) { + initialized = true; + lbuf_init(&lbuf, NULL, 0, NULL, 0); + } + + lbuf_append(&lbuf, "%s", msg); + /* XXX - assumes a final newline */ + if (strchr(msg, '\n') != NULL) + { + sudo_debug_printf2(NULL, NULL, 0, SUDO_DEBUG_PARSER|SUDO_DEBUG_DEBUG, + "%s:%d %s", sudoers, sudolineno, lbuf.buf); + lbuf.len = 0; + } + return 0; } #endif /* TRACELEXER */