version 1.3, 2009/04/11 11:48:06 |
version 1.4, 2009/06/21 14:48:42 |
|
|
%{ |
%{ |
/* |
/* |
* Copyright (c) 1996, 1998-2005, 2007-2008 |
* Copyright (c) 1996, 1998-2005, 2007-2009 |
* Todd C. Miller <Todd.Miller@courtesan.com> |
* Todd C. Miller <Todd.Miller@courtesan.com> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
|
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/param.h> |
#include <sys/param.h> |
|
#include <sys/stat.h> |
#include <stdio.h> |
#include <stdio.h> |
#ifdef STDC_HEADERS |
#ifdef STDC_HEADERS |
# include <stdlib.h> |
# include <stdlib.h> |
|
|
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) |
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) |
# include <malloc.h> |
# include <malloc.h> |
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ |
#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 <ctype.h> |
#include "sudo.h" |
#include "sudo.h" |
#include "parse.h" |
#include "parse.h" |
#include <gram.h> |
#include <gram.h> |
|
|
#ifndef lint |
#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 */ |
#endif /* lint */ |
|
|
extern YYSTYPE yylval; |
extern YYSTYPE yylval; |
|
|
static int _fill __P((char *, int, int)); |
static int _fill __P((char *, int, int)); |
static int fill_cmnd __P((char *, int)); |
static int fill_cmnd __P((char *, int)); |
static int fill_args __P((char *, int, 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 int ipv6_valid __P((const char *s)); |
static char *parse_include __P((char *)); |
static char *parse_include __P((char *)); |
extern void yyerror __P((const char *)); |
extern void yyerror __P((const char *)); |
|
|
#define fill(a, b) _fill(a, b, 0) |
#define fill(a, b) _fill(a, b, 0) |
|
|
#define push_include(_p) (switch_buffer((_p))) |
#define push_include(_p) (_push_include((_p), FALSE)) |
#define pop_include() (switch_buffer(NULL)) |
#define push_includedir(_p) (_push_include((_p), TRUE)) |
|
|
/* realloc() to size + COMMANDARGINC to make room for command args */ |
/* realloc() to size + COMMANDARGINC to make room for command args */ |
#define COMMANDARGINC 64 |
#define COMMANDARGINC 64 |
|
|
yyterminate(); |
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})? { |
<INITIAL>^[[:blank:]]*Defaults([:@>\!]{WORD})? { |
int n; |
int n; |
for (n = 0; isblank((unsigned char)yytext[n]); n++) |
for (n = 0; isblank((unsigned char)yytext[n]); n++) |
|
|
return(NETGROUP); |
return(NETGROUP); |
} |
} |
|
|
\%{WORD} { |
\%:?{WORD} { |
/* UN*X group */ |
/* UN*X group */ |
if (!fill(yytext, yyleng)) |
if (!fill(yytext, yyleng)) |
yyterminate(); |
yyterminate(); |
|
|
} |
} |
} /* a pathname */ |
} /* 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}) { |
<INITIAL,GOTDEFS>({ID}|{WORD}) { |
/* a word */ |
/* a word */ |
if (!fill(yytext, yyleng)) |
if (!fill(yytext, yyleng)) |
yyterminate(); |
yyterminate(); |
LEXTRACE("WORD(4) "); |
LEXTRACE("WORD(5) "); |
return(WORD); |
return(WORD); |
} |
} |
|
|
|
|
} |
} |
|
|
%% |
%% |
|
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 |
static int |
_fill(src, len, olen) |
_fill(src, len, olen) |
char *src; |
char *src; |
int len, olen; |
int len, olen; |
{ |
{ |
int i, j; |
|
char *dst; |
char *dst; |
|
|
dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1); |
dst = olen ? realloc(yylval.string, olen + len + 1) : malloc(len + 1); |
|
|
|
|
/* Copy the string and collapse any escaped characters. */ |
/* Copy the string and collapse any escaped characters. */ |
dst += olen; |
dst += olen; |
for (i = 0, j = 0; i < len; i++, j++) { |
while (len--) { |
if (src[i] == '\\' && i != len - 1) |
if (*src == '\\' && len) { |
dst[j] = src[++i]; |
if (src[1] == 'x' && len >= 3 && |
else |
isxdigit((unsigned char) src[2]) && |
dst[j] = src[i]; |
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); |
return(TRUE); |
} |
} |
|
|
|
|
return(TRUE); |
return(TRUE); |
} |
} |
|
|
struct sudoers_state { |
struct path_list { |
|
char *path; |
|
struct path_list *next; |
|
}; |
|
|
|
struct include_stack { |
YY_BUFFER_STATE bs; |
YY_BUFFER_STATE bs; |
char *path; |
char *path; |
|
struct path_list *more; /* more files in case of includedir */ |
int lineno; |
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 MAX_SUDOERS_DEPTH 128 |
#define SUDOERS_STACK_INCREMENT 16 |
#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 |
static int |
switch_buffer(path) |
_push_include(path, isdir) |
char *path; |
char *path; |
|
int isdir; |
{ |
{ |
static size_t stacksize, depth; |
|
static struct sudoers_state *state; |
|
static int keepopen; |
|
FILE *fp; |
FILE *fp; |
|
|
if (path != NULL) { |
/* push current state onto stack */ |
/* push current state */ |
if (idepth >= istacksize) { |
if (depth >= stacksize) { |
if (idepth > MAX_SUDOERS_DEPTH) { |
if (depth > MAX_SUDOERS_DEPTH) { |
yyerror("too many levels of includes"); |
yyerror("too many levels of includes"); |
return(FALSE); |
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); |
|
} |
|
} |
} |
if ((fp = open_sudoers(path, &keepopen)) == NULL) { |
istacksize += SUDOERS_STACK_INCREMENT; |
|
istack = (struct include_stack *) realloc(istack, |
|
sizeof(istack) * istacksize); |
|
if (istack == NULL) { |
|
yyerror("unable to allocate memory"); |
|
return(FALSE); |
|
} |
|
} |
|
if (isdir) { |
|
if (!(path = switch_dir(&istack[idepth], path))) { |
yyerror(path); |
yyerror(path); |
return(FALSE); |
return(FALSE); |
} |
} |
state[depth].bs = YY_CURRENT_BUFFER; |
if ((fp = open_sudoers(path, FALSE, &keepopen)) == NULL) { |
state[depth].path = sudoers; |
yyerror(path); |
state[depth].lineno = sudolineno; |
return(FALSE); /* XXX - just to go next one? */ |
depth++; |
} |
|
} 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; |
sudolineno = 1; |
sudoers = path; |
|
yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); |
yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); |
|
efree(pl); |
} else { |
} else { |
/* pop */ |
idepth--; |
if (depth == 0) |
yy_switch_to_buffer(istack[idepth].bs); |
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); |
|
efree(sudoers); |
efree(sudoers); |
sudoers = state[depth].path; |
sudoers = istack[idepth].path; |
sudolineno = state[depth].lineno; |
sudolineno = istack[idepth].lineno; |
keepopen = FALSE; |
|
} |
} |
return(TRUE); |
return(TRUE); |
} |
} |
|
|
|
|
/* Pull out path from #include line. */ |
/* Pull out path from #include line. */ |
cp = base + sizeof("#include"); |
cp = base + sizeof("#include"); |
|
if (*cp == 'i') |
|
cp += 3; /* includedir */ |
while (isblank((unsigned char) *cp)) |
while (isblank((unsigned char) *cp)) |
cp++; |
cp++; |
ep = cp; |
ep = cp; |