diff options
Diffstat (limited to 'usr.bin/sudo/toke.l')
-rw-r--r-- | usr.bin/sudo/toke.l | 371 |
1 files changed, 321 insertions, 50 deletions
diff --git a/usr.bin/sudo/toke.l b/usr.bin/sudo/toke.l index 01aa762a154..9b93563558f 100644 --- a/usr.bin/sudo/toke.l +++ b/usr.bin/sudo/toke.l @@ -1,6 +1,6 @@ %{ /* - * Copyright (c) 1996, 1998-2005, 2007-2008 + * Copyright (c) 1996, 1998-2005, 2007-2009 * Todd C. Miller <Todd.Miller@courtesan.com> * * Permission to use, copy, modify, and distribute this software for any @@ -27,6 +27,7 @@ #include <sys/types.h> #include <sys/param.h> +#include <sys/stat.h> #include <stdio.h> #ifdef STDC_HEADERS # include <stdlib.h> @@ -49,13 +50,29 @@ #if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) # include <malloc.h> #endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#ifdef HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# ifdef HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# ifdef HAVE_NDIR_H +# include <ndir.h> +# endif +#endif #include <ctype.h> #include "sudo.h" #include "parse.h" #include <gram.h> #ifndef lint -__unused static const char rcsid[] = "$Sudo: toke.l,v 1.29 2009/02/21 21:49:19 millert Exp $"; +__unused static const char rcsid[] = "$Sudo: toke.l,v 1.37 2009/05/27 00:46:51 millert Exp $"; #endif /* lint */ extern YYSTYPE yylval; @@ -69,15 +86,16 @@ static int append __P((char *, int)); static int _fill __P((char *, int, int)); static int fill_cmnd __P((char *, int)); static int fill_args __P((char *, int, int)); -static int switch_buffer __P((char *)); +static int _push_include __P((char *, int)); +static int pop_include __P((void)); static int ipv6_valid __P((const char *s)); static char *parse_include __P((char *)); extern void yyerror __P((const char *)); #define fill(a, b) _fill(a, b, 0) -#define push_include(_p) (switch_buffer((_p))) -#define pop_include() (switch_buffer(NULL)) +#define push_include(_p) (_push_include((_p), FALSE)) +#define push_includedir(_p) (_push_include((_p), TRUE)) /* realloc() to size + COMMANDARGINC to make room for command args */ #define COMMANDARGINC 64 @@ -227,6 +245,19 @@ DEFVAR [a-z_]+ yyterminate(); } +<INITIAL>^#includedir[[:blank:]]+\/.*\n { + char *path; + + if ((path = parse_include(yytext)) == NULL) + yyterminate(); + + LEXTRACE("INCLUDEDIR\n"); + + /* Push current buffer and switch to include file */ + if (!push_includedir(path)) + yyterminate(); + } + <INITIAL>^[[:blank:]]*Defaults([:@>\!]{WORD})? { int n; for (n = 0; isblank((unsigned char)yytext[n]); n++) @@ -316,7 +347,7 @@ NOSETENV[[:blank:]]*: { return(NETGROUP); } -\%{WORD} { +\%:?{WORD} { /* UN*X group */ if (!fill(yytext, yyleng)) yyterminate(); @@ -412,11 +443,28 @@ sudoedit { } } /* a pathname */ +<INITIAL,GOTDEFS>\"[^"\n]+\" { + /* a quoted user/group name */ + if (!fill(yytext + 1, yyleng - 2)) + yyterminate(); + switch (yytext[1]) { + case '%': + LEXTRACE("USERGROUP "); + return(USERGROUP); + case '+': + LEXTRACE("NETGROUP "); + return(NETGROUP); + default: + LEXTRACE("WORD(4) "); + return(WORD); + } + } + <INITIAL,GOTDEFS>({ID}|{WORD}) { /* a word */ if (!fill(yytext, yyleng)) yyterminate(); - LEXTRACE("WORD(4) "); + LEXTRACE("WORD(5) "); return(WORD); } @@ -490,12 +538,57 @@ sudoedit { } %% +static unsigned char +hexchar(s) + const char *s; +{ + int i; + int result = 0; + + s += 2; /* skip \\x */ + for (i = 0; i < 2; i++) { + switch (*s) { + case 'A': + case 'a': + result += 10; + break; + case 'B': + case 'b': + result += 11; + break; + case 'C': + case 'c': + result += 12; + break; + case 'D': + case 'd': + result += 13; + break; + case 'E': + case 'e': + result += 14; + break; + case 'F': + case 'f': + result += 15; + break; + default: + result += *s - '0'; + break; + } + if (i == 0) { + result *= 16; + s++; + } + } + return((unsigned char)result); +} + static int _fill(src, len, olen) char *src; int len, olen; { - int i, j; char *dst; dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1); @@ -507,13 +600,24 @@ _fill(src, len, olen) /* Copy the string and collapse any escaped characters. */ dst += olen; - for (i = 0, j = 0; i < len; i++, j++) { - if (src[i] == '\\' && i != len - 1) - dst[j] = src[++i]; - else - dst[j] = src[i]; + while (len--) { + if (*src == '\\' && len) { + if (src[1] == 'x' && len >= 3 && + isxdigit((unsigned char) src[2]) && + isxdigit((unsigned char) src[3])) { + *dst++ = hexchar(src); + src += 4; + len -= 3; + } else { + src++; + len--; + *dst++ = *src++; + } + } else { + *dst++ = *src++; + } } - dst[j] = '\0'; + *dst = '\0'; return(TRUE); } @@ -605,63 +709,228 @@ fill_args(s, len, addspace) return(TRUE); } -struct sudoers_state { +struct path_list { + char *path; + struct path_list *next; +}; + +struct include_stack { YY_BUFFER_STATE bs; char *path; + struct path_list *more; /* more files in case of includedir */ int lineno; + int keepopen; }; +static int +pl_compare(v1, v2) + 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)); +} + +static char * +switch_dir(stack, dirpath) + struct include_stack *stack; + char *dirpath; +{ + DIR *dir; + int i, count = 0; + char *path = NULL; + struct dirent *dent; + struct stat sb; + struct path_list *pl, *first = NULL; + struct path_list **sorted = NULL; + + if (!(dir = opendir(dirpath))) { + yyerror(dirpath); + return(FALSE); + } + 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] == '~' + || strchr(dent->d_name, '.') != NULL) { + continue; + } + if (asprintf(&path, "%s/%s", dirpath, dent->d_name) == -1) { + closedir(dir); + goto bad; + } + if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode)) { + efree(path); + continue; + } + pl = malloc(sizeof(*pl)); + if (pl == NULL) + goto bad; + pl->path = path; + pl->next = first; + first = pl; + count++; + } + 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; + for (i = 0; i < count; i++) { + sorted[i] = pl; + pl = pl->next; + } + 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; + } +done: + efree(dirpath); + return(path); +bad: + while (first != NULL) { + pl = first; + first = pl->next; + free(pl->path); + free(pl); + } + efree(sorted); + efree(dirpath); + efree(path); + return(NULL); +} + #define MAX_SUDOERS_DEPTH 128 #define SUDOERS_STACK_INCREMENT 16 +static size_t istacksize, idepth; +static struct include_stack *istack; +static int keepopen; + +void +init_lexer() +{ + struct path_list *pl; + + while (idepth) { + idepth--; + while ((pl = istack[idepth].more) != NULL) { + istack[idepth].more = pl->next; + efree(pl->path); + efree(pl); + } + efree(istack[idepth].path); + if (!istack[idepth].keepopen) + fclose(istack[idepth].bs->yy_input_file); + yy_delete_buffer(istack[idepth].bs); + } + efree(istack); + istack = NULL; + istacksize = idepth = 0; + keepopen = FALSE; +} + static int -switch_buffer(path) +_push_include(path, isdir) char *path; + int isdir; { - static size_t stacksize, depth; - static struct sudoers_state *state; - static int keepopen; FILE *fp; - if (path != NULL) { - /* push current state */ - if (depth >= stacksize) { - if (depth > MAX_SUDOERS_DEPTH) { - yyerror("too many levels of includes"); - return(FALSE); - } - stacksize += SUDOERS_STACK_INCREMENT; - state = (struct sudoers_state *) realloc(state, - sizeof(state) * stacksize); - if (state == NULL) { - yyerror("unable to allocate memory"); - return(FALSE); - } + /* push current state onto stack */ + if (idepth >= istacksize) { + if (idepth > MAX_SUDOERS_DEPTH) { + yyerror("too many levels of includes"); + return(FALSE); + } + istacksize += SUDOERS_STACK_INCREMENT; + istack = (struct include_stack *) realloc(istack, + sizeof(istack) * istacksize); + if (istack == NULL) { + yyerror("unable to allocate memory"); + return(FALSE); } - if ((fp = open_sudoers(path, &keepopen)) == NULL) { + } + if (isdir) { + if (!(path = switch_dir(&istack[idepth], path))) { yyerror(path); return(FALSE); } - state[depth].bs = YY_CURRENT_BUFFER; - state[depth].path = sudoers; - state[depth].lineno = sudolineno; - depth++; + if ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) { + yyerror(path); + return(FALSE); /* XXX - just to go next one? */ + } + } else { + if ((fp = open_sudoers(path, TRUE, &keepopen)) == NULL) { + yyerror(path); + return(FALSE); + } + istack[idepth].more = NULL; + } + /* Push the old (current) file and open the new one. */ + istack[idepth].path = sudoers; /* push old path */ + istack[idepth].bs = YY_CURRENT_BUFFER; + istack[idepth].lineno = sudolineno; + istack[idepth].keepopen = keepopen; + idepth++; + sudolineno = 1; + sudoers = path; + yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + + return(TRUE); +} + +static int +pop_include() +{ + struct path_list *pl; + FILE *fp; + + if (idepth == 0) + return(FALSE); + + if (!keepopen) + fclose(YY_CURRENT_BUFFER->yy_input_file); + yy_delete_buffer(YY_CURRENT_BUFFER); + keepopen = FALSE; + if ((pl = istack[idepth - 1].more) != NULL) { + /* Move to next file in the dir. */ + istack[idepth - 1].more = pl->next; + if ((fp = open_sudoers(pl->path, FALSE, &keepopen)) == NULL) { + yyerror(pl->path); + return(FALSE); /* XXX - just to go next one? */ + } + efree(sudoers); + sudoers = pl->path; sudolineno = 1; - sudoers = path; yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + efree(pl); } else { - /* pop */ - if (depth == 0) - return(FALSE); - depth--; - if (!keepopen) - fclose(YY_CURRENT_BUFFER->yy_input_file); - yy_delete_buffer(YY_CURRENT_BUFFER); - yy_switch_to_buffer(state[depth].bs); + idepth--; + yy_switch_to_buffer(istack[idepth].bs); efree(sudoers); - sudoers = state[depth].path; - sudolineno = state[depth].lineno; - keepopen = FALSE; + sudoers = istack[idepth].path; + sudolineno = istack[idepth].lineno; } return(TRUE); } @@ -676,6 +945,8 @@ parse_include(base) /* Pull out path from #include line. */ cp = base + sizeof("#include"); + if (*cp == 'i') + cp += 3; /* includedir */ while (isblank((unsigned char) *cp)) cp++; ep = cp; |