Support adminconfdir for relative include paths in sudoers.
This commit is contained in:
@@ -3965,7 +3965,7 @@ free_parse_tree(struct sudoers_parse_tree *parse_tree)
|
||||
* the current sudoers file to path.
|
||||
*/
|
||||
bool
|
||||
init_parser_ext(const char *path, bool strict, int verbose)
|
||||
init_parser_ext(const char *file, const char *path, bool strict, int verbose)
|
||||
{
|
||||
bool ret = true;
|
||||
debug_decl(init_parser, SUDOERS_DEBUG_PARSER);
|
||||
@@ -3975,8 +3975,8 @@ init_parser_ext(const char *path, bool strict, int verbose)
|
||||
init_lexer();
|
||||
|
||||
sudo_rcstr_delref(sudoers);
|
||||
if (path != NULL) {
|
||||
if ((sudoers = sudo_rcstr_dup(path)) == NULL) {
|
||||
if (file != NULL) {
|
||||
if ((sudoers = sudo_rcstr_dup(file)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
ret = false;
|
||||
}
|
||||
@@ -3984,6 +3984,16 @@ init_parser_ext(const char *path, bool strict, int verbose)
|
||||
sudoers = NULL;
|
||||
}
|
||||
|
||||
sudo_rcstr_delref(sudoers_search_path);
|
||||
if (path != NULL) {
|
||||
if ((sudoers_search_path = sudo_rcstr_dup(path)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
sudoers_search_path = NULL;
|
||||
}
|
||||
|
||||
parse_error = false;
|
||||
sudoers_strict = strict;
|
||||
sudoers_verbose = verbose;
|
||||
@@ -3992,9 +4002,9 @@ init_parser_ext(const char *path, bool strict, int verbose)
|
||||
}
|
||||
|
||||
bool
|
||||
init_parser(const char *path)
|
||||
init_parser(const char *file)
|
||||
{
|
||||
return init_parser_ext(path, false, 1);
|
||||
return init_parser_ext(file, NULL, false, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -1782,7 +1782,7 @@ free_parse_tree(struct sudoers_parse_tree *parse_tree)
|
||||
* the current sudoers file to path.
|
||||
*/
|
||||
bool
|
||||
init_parser_ext(const char *path, bool strict, int verbose)
|
||||
init_parser_ext(const char *file, const char *path, bool strict, int verbose)
|
||||
{
|
||||
bool ret = true;
|
||||
debug_decl(init_parser, SUDOERS_DEBUG_PARSER);
|
||||
@@ -1792,8 +1792,8 @@ init_parser_ext(const char *path, bool strict, int verbose)
|
||||
init_lexer();
|
||||
|
||||
sudo_rcstr_delref(sudoers);
|
||||
if (path != NULL) {
|
||||
if ((sudoers = sudo_rcstr_dup(path)) == NULL) {
|
||||
if (file != NULL) {
|
||||
if ((sudoers = sudo_rcstr_dup(file)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
ret = false;
|
||||
}
|
||||
@@ -1801,6 +1801,16 @@ init_parser_ext(const char *path, bool strict, int verbose)
|
||||
sudoers = NULL;
|
||||
}
|
||||
|
||||
sudo_rcstr_delref(sudoers_search_path);
|
||||
if (path != NULL) {
|
||||
if ((sudoers_search_path = sudo_rcstr_dup(path)) == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
ret = false;
|
||||
}
|
||||
} else {
|
||||
sudoers_search_path = NULL;
|
||||
}
|
||||
|
||||
parse_error = false;
|
||||
sudoers_strict = strict;
|
||||
sudoers_verbose = verbose;
|
||||
@@ -1809,9 +1819,9 @@ init_parser_ext(const char *path, bool strict, int verbose)
|
||||
}
|
||||
|
||||
bool
|
||||
init_parser(const char *path)
|
||||
init_parser(const char *file)
|
||||
{
|
||||
return init_parser_ext(path, false, 1);
|
||||
return init_parser_ext(file, NULL, false, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -372,8 +372,8 @@ int check_aliases(struct sudoers_parse_tree *parse_tree, bool strict, bool quiet
|
||||
/* gram.y */
|
||||
extern struct sudoers_parse_tree parsed_policy;
|
||||
extern bool (*sudoers_error_hook)(const char *file, int line, int column, const char *fmt, va_list args);
|
||||
bool init_parser(const char *path);
|
||||
bool init_parser_ext(const char *path, bool strict, int verbose);
|
||||
bool init_parser(const char *file);
|
||||
bool init_parser_ext(const char *file, const char *path, bool strict, int verbose);
|
||||
void free_member(struct member *m);
|
||||
void free_members(struct member_list *members);
|
||||
void free_cmndspec(struct cmndspec *cs, struct cmndspec_list *csl);
|
||||
|
@@ -312,7 +312,7 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
||||
|
||||
/* Initialize defaults and parse sudoers. */
|
||||
init_defaults();
|
||||
init_parser_ext("sudoers", true, 1);
|
||||
init_parser_ext("sudoers", NULL, true, 1);
|
||||
sudoersrestart(fp);
|
||||
sudoersparse();
|
||||
reparent_parse_tree(&parse_tree);
|
||||
|
@@ -335,6 +335,7 @@ void sudoersrestart(FILE *);
|
||||
extern FILE *sudoersin;
|
||||
extern const char *sudoers_file;
|
||||
extern char *sudoers;
|
||||
extern char *sudoers_search_path;
|
||||
extern mode_t sudoers_mode;
|
||||
extern uid_t sudoers_uid;
|
||||
extern gid_t sudoers_gid;
|
||||
|
@@ -274,7 +274,7 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
/* Initialize the parser and set sudoers filename to "sudoers". */
|
||||
init_parser_ext("sudoers", true, 2);
|
||||
init_parser_ext("sudoers", NULL, true, 2);
|
||||
|
||||
/*
|
||||
* Set runas passwd/group entries based on command line or sudoers.
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -55,6 +55,7 @@
|
||||
|
||||
int sudolineno; /* current sudoers line number. */
|
||||
char *sudoers; /* sudoers file being parsed. */
|
||||
char *sudoers_search_path; /* colon-separated path of sudoers files. */
|
||||
const char *sudoers_errstr; /* description of last error from lexer. */
|
||||
struct sudolinebuf sudolinebuf; /* sudoers line being parsed. */
|
||||
|
||||
@@ -922,7 +923,8 @@ SLIST_HEAD(path_list_head, path_list);
|
||||
struct include_stack {
|
||||
struct sudolinebuf line;
|
||||
YY_BUFFER_STATE bs;
|
||||
char *path;
|
||||
char *path; /* search path */
|
||||
char *file;
|
||||
struct path_list_head more; /* more files in case of includedir */
|
||||
int lineno;
|
||||
bool keepopen;
|
||||
@@ -957,6 +959,7 @@ read_dir_files(const char *dirpath, struct path_list ***pathsp, int verbose)
|
||||
const size_t dirlen = strlen(dirpath);
|
||||
debug_decl(read_dir_files, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
/* XXX - fdopendir */
|
||||
dir = opendir(dirpath);
|
||||
if (dir == NULL) {
|
||||
if (errno == ENOENT)
|
||||
@@ -1117,89 +1120,150 @@ init_lexer(void)
|
||||
debug_return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Like strlcpy() but expand %h escapes to user_shost.
|
||||
*/
|
||||
static size_t
|
||||
strlcpy_expand_host(char *dst, const char *src, size_t size)
|
||||
{
|
||||
size_t len = 0;
|
||||
char ch;
|
||||
debug_decl(strlcpy_expand_host, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
while ((ch = *src++) != '\0') {
|
||||
if (ch == '%' && *src == 'h') {
|
||||
size_t n = strlcpy(dst, user_shost, size);
|
||||
len += n;
|
||||
if (n >= size) {
|
||||
/* truncated */
|
||||
n = size ? size - 1 : 0;
|
||||
}
|
||||
dst += n;
|
||||
size -= n;
|
||||
src++;
|
||||
continue;
|
||||
}
|
||||
if (size > 1) {
|
||||
*dst++ = ch;
|
||||
size--;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
if (size > 0)
|
||||
*dst = '\0';
|
||||
|
||||
debug_return_size_t(len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expand any embedded %h (host) escapes in the given path and makes
|
||||
* a relative path fully-qualified based on the current sudoers file.
|
||||
* Returns a reference-counted string.
|
||||
* Returns a reference-counted string on success or NULL on failure.
|
||||
*/
|
||||
static char *
|
||||
expand_include(const char *opath)
|
||||
expand_include(const char *src)
|
||||
{
|
||||
const char *path = sudoers_search_path ? sudoers_search_path : sudoers;
|
||||
const char *path_end = path + strlen(path);
|
||||
const char *cp, *ep;
|
||||
char *path, *pp;
|
||||
size_t len, olen, dirlen = 0;
|
||||
bool subst = false;
|
||||
char *dst0, *dst;
|
||||
size_t dst_size, src_len;
|
||||
unsigned int nhost = 0;
|
||||
debug_decl(expand_include, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
/* Strip double quotes if present. */
|
||||
olen = strlen(opath);
|
||||
if (olen > 1 && opath[0] == '"' && opath[olen - 1] == '"') {
|
||||
opath++;
|
||||
olen -= 2;
|
||||
src_len = strlen(src);
|
||||
if (src_len > 1 && src[0] == '"' && src[src_len - 1] == '"') {
|
||||
src++;
|
||||
src_len -= 2;
|
||||
}
|
||||
if (olen == 0)
|
||||
if (src_len == 0)
|
||||
debug_return_ptr(NULL);
|
||||
|
||||
/* Relative paths are located in the same dir as the sudoers file. */
|
||||
if (*opath != '/') {
|
||||
char *dirend = strrchr(sudoers, '/');
|
||||
if (dirend != NULL)
|
||||
dirlen = (size_t)(dirend - sudoers) + 1;
|
||||
}
|
||||
|
||||
cp = opath;
|
||||
ep = opath + olen;
|
||||
len = olen;
|
||||
/* Check for %h escapes in src. */
|
||||
cp = src;
|
||||
ep = src + src_len;
|
||||
while (cp < ep) {
|
||||
if (cp[0] == '%' && cp[1] == 'h') {
|
||||
subst = true;
|
||||
len += strlen(user_shost);
|
||||
nhost++;
|
||||
cp += 2;
|
||||
continue;
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (*src == '/') {
|
||||
/* Fully-qualified path, make a copy and expand %h escapes. */
|
||||
dst_size = src_len + (nhost * strlen(user_shost)) - (nhost * 2) + 1;
|
||||
dst0 = sudo_rcstr_alloc(dst_size - 1);
|
||||
if (dst0 == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
sudoerserror(NULL);
|
||||
debug_return_str(NULL);
|
||||
}
|
||||
if (strlcpy_expand_host(dst0, src, dst_size) >= dst_size)
|
||||
goto oflow;
|
||||
debug_return_str(dst0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Relative paths are located in the same dir as the sudoers file.
|
||||
* If the current sudoers file was opened via a colon-separated path,
|
||||
* use the same path when opening src.
|
||||
*/
|
||||
dst_size = 0;
|
||||
for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
|
||||
cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
|
||||
char *dirend = memrchr(cp, '/', ep - cp);
|
||||
if (dirend != NULL) {
|
||||
dst_size += (size_t)(dirend - cp) + 1;
|
||||
}
|
||||
/* Includes space for ':' separator and NUL terminator. */
|
||||
dst_size += src_len + (nhost * strlen(user_shost)) - (nhost * 2) + 1;
|
||||
}
|
||||
|
||||
/* Make a copy of the fully-qualified path and return it. */
|
||||
path = pp = sudo_rcstr_alloc(dirlen + len);
|
||||
if (path == NULL) {
|
||||
dst = dst0 = sudo_rcstr_alloc(dst_size - 1);
|
||||
if (dst0 == NULL) {
|
||||
sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
|
||||
sudoerserror(NULL);
|
||||
debug_return_str(NULL);
|
||||
}
|
||||
if (dirlen) {
|
||||
memcpy(path, sudoers, dirlen);
|
||||
pp += dirlen;
|
||||
}
|
||||
if (subst) {
|
||||
/* substitute for %h */
|
||||
cp = opath;
|
||||
while (cp < ep) {
|
||||
if (cp[0] == '%' && cp[1] == 'h') {
|
||||
size_t n = strlcpy(pp, user_shost, len + 1);
|
||||
if (n >= len + 1)
|
||||
goto oflow;
|
||||
cp += 2;
|
||||
pp += n;
|
||||
len -= n;
|
||||
continue;
|
||||
}
|
||||
if (len < 1)
|
||||
goto oflow;
|
||||
*pp++ = *cp++;
|
||||
len--;
|
||||
}
|
||||
*pp = '\0';
|
||||
} else {
|
||||
memcpy(pp, opath, len);
|
||||
pp[len] = '\0';
|
||||
}
|
||||
for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
|
||||
cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
|
||||
size_t len;
|
||||
char *dirend;
|
||||
|
||||
debug_return_str(path);
|
||||
if (cp != path) {
|
||||
if (dst_size < 2)
|
||||
goto oflow;
|
||||
*dst++ = ':';
|
||||
dst_size--;
|
||||
}
|
||||
|
||||
dirend = memrchr(cp, '/', ep - cp);
|
||||
if (dirend != NULL) {
|
||||
len = (size_t)(dirend - cp) + 1;
|
||||
if (len >= dst_size)
|
||||
goto oflow;
|
||||
memcpy(dst, cp, len);
|
||||
dst += len;
|
||||
dst_size -= len;
|
||||
}
|
||||
|
||||
len = strlcpy_expand_host(dst, src, dst_size);
|
||||
if (len >= dst_size)
|
||||
goto oflow;
|
||||
dst += len;
|
||||
dst_size -= len;
|
||||
}
|
||||
*dst = '\0';
|
||||
|
||||
debug_return_str(dst0);
|
||||
oflow:
|
||||
sudo_warnx(U_("internal error, %s overflow"), __func__);
|
||||
sudoerserror(NULL);
|
||||
sudo_rcstr_delref(path);
|
||||
free(dst0);
|
||||
debug_return_str(NULL);
|
||||
}
|
||||
|
||||
@@ -1213,7 +1277,7 @@ static bool
|
||||
push_include_int(const char *opath, bool isdir, int verbose)
|
||||
{
|
||||
struct path_list *pl;
|
||||
char *path;
|
||||
char *file = NULL, *path;
|
||||
FILE *fp;
|
||||
debug_decl(push_include, SUDOERS_DEBUG_PARSER);
|
||||
|
||||
@@ -1226,7 +1290,8 @@ push_include_int(const char *opath, bool isdir, int verbose)
|
||||
|
||||
if (idepth > MAX_SUDOERS_DEPTH) {
|
||||
if (verbose > 0) {
|
||||
fprintf(stderr, U_("%s: %s"), path, U_("too many levels of includes"));
|
||||
fprintf(stderr, U_("%s: %s"), path,
|
||||
U_("too many levels of includes"));
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
sudoerserror(NULL);
|
||||
@@ -1246,9 +1311,12 @@ push_include_int(const char *opath, bool isdir, int verbose)
|
||||
SLIST_INIT(&istack[idepth].more);
|
||||
if (isdir) {
|
||||
struct stat sb;
|
||||
int count, status;
|
||||
char dname[PATH_MAX];
|
||||
int count, fd, status;
|
||||
|
||||
status = sudo_secure_dir(path, sudoers_uid, sudoers_gid, &sb);
|
||||
fd = sudo_open_conf_path(path, dname, sizeof(dname), NULL);
|
||||
status = sudo_secure_fd(fd, S_IFDIR, sudoers_uid, sudoers_gid, &sb);
|
||||
close(fd); /* XXX use in read_dir_files? */
|
||||
if (status != SUDO_PATH_SECURE) {
|
||||
if (verbose > 0) {
|
||||
switch (status) {
|
||||
@@ -1277,7 +1345,7 @@ push_include_int(const char *opath, bool isdir, int verbose)
|
||||
sudo_rcstr_delref(path);
|
||||
debug_return_bool(true);
|
||||
}
|
||||
count = switch_dir(&istack[idepth], path, verbose);
|
||||
count = switch_dir(&istack[idepth], dname, verbose);
|
||||
if (count <= 0) {
|
||||
/* switch_dir() called sudoerserror() for us */
|
||||
sudo_rcstr_delref(path);
|
||||
@@ -1286,6 +1354,7 @@ push_include_int(const char *opath, bool isdir, int verbose)
|
||||
|
||||
/* Parse the first dir entry we can open, leave the rest for later. */
|
||||
do {
|
||||
sudo_rcstr_delref(file);
|
||||
sudo_rcstr_delref(path);
|
||||
if ((pl = SLIST_FIRST(&istack[idepth].more)) == NULL) {
|
||||
/* Unable to open any files in include dir, not an error. */
|
||||
@@ -1294,24 +1363,32 @@ push_include_int(const char *opath, bool isdir, int verbose)
|
||||
SLIST_REMOVE_HEAD(&istack[idepth].more, entries);
|
||||
path = pl->path;
|
||||
free(pl);
|
||||
} while ((fp = open_sudoers(path, NULL, false, &keepopen)) == NULL);
|
||||
/* The file and path and the same for sudoers.d files. */
|
||||
file = path;
|
||||
sudo_rcstr_addref(file);
|
||||
} while ((fp = open_sudoers(file, NULL, false, &keepopen)) == NULL);
|
||||
} else {
|
||||
if ((fp = open_sudoers(path, NULL, true, &keepopen)) == NULL) {
|
||||
if ((fp = open_sudoers(path, &file, true, &keepopen)) == NULL) {
|
||||
/* The error was already printed by open_sudoers() */
|
||||
sudoerserror(NULL);
|
||||
sudo_rcstr_delref(path);
|
||||
debug_return_bool(false);
|
||||
}
|
||||
}
|
||||
/* Push the old (current) file and open the new one. */
|
||||
istack[idepth].path = sudoers; /* push old path (and its ref) */
|
||||
/*
|
||||
* Push the old (current) file and open the new one.
|
||||
* We use the existing refs of sudoers and sudoers_search_path.
|
||||
*/
|
||||
istack[idepth].file = sudoers;
|
||||
istack[idepth].path = sudoers_search_path;
|
||||
istack[idepth].line = sudolinebuf;
|
||||
istack[idepth].bs = YY_CURRENT_BUFFER;
|
||||
istack[idepth].lineno = sudolineno;
|
||||
istack[idepth].keepopen = keepopen;
|
||||
idepth++;
|
||||
sudolineno = 1;
|
||||
sudoers = path;
|
||||
sudoers = file;
|
||||
sudoers_search_path = path;
|
||||
sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
|
||||
memset(&sudolinebuf, 0, sizeof(sudolinebuf));
|
||||
|
||||
@@ -1356,7 +1433,10 @@ pop_include(void)
|
||||
sudolinebuf.len = sudolinebuf.off = 0;
|
||||
sudolinebuf.toke_start = sudolinebuf.toke_end = 0;
|
||||
sudo_rcstr_delref(sudoers);
|
||||
sudoers = pl->path;
|
||||
sudo_rcstr_delref(sudoers_search_path);
|
||||
sudoers_search_path = pl->path;
|
||||
sudoers = sudoers_search_path;
|
||||
sudo_rcstr_addref(sudoers);
|
||||
sudolineno = 1;
|
||||
sudoers_switch_to_buffer(sudoers_create_buffer(fp, YY_BUF_SIZE));
|
||||
free(pl);
|
||||
@@ -1373,7 +1453,9 @@ pop_include(void)
|
||||
free(sudolinebuf.buf);
|
||||
sudolinebuf = istack[idepth].line;
|
||||
sudo_rcstr_delref(sudoers);
|
||||
sudoers = istack[idepth].path;
|
||||
sudoers = istack[idepth].file;
|
||||
sudo_rcstr_delref(sudoers_search_path);
|
||||
sudoers_search_path = istack[idepth].path;
|
||||
sudolineno = istack[idepth].lineno;
|
||||
keepopen = istack[idepth].keepopen;
|
||||
}
|
||||
|
@@ -287,7 +287,7 @@ main(int argc, char *argv[])
|
||||
* Parse the existing sudoers file(s) to highlight any existing
|
||||
* errors and to pull in editor and env_editor conf values.
|
||||
*/
|
||||
init_parser_ext(NULL, true, quiet ? 0 : 2);
|
||||
init_parser_ext(NULL, sudoers_file, true, quiet ? 0 : 2);
|
||||
if ((sudoersin = open_sudoers(sudoers_file, &sudoers, true, NULL)) == NULL)
|
||||
exit(EXIT_FAILURE);
|
||||
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
|
||||
@@ -650,7 +650,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
|
||||
/* Clean slate for each parse */
|
||||
if (!init_defaults())
|
||||
sudo_fatalx("%s", U_("unable to initialize sudoers default values"));
|
||||
init_parser_ext(sp->opath, true, quiet ? 0 : 2);
|
||||
init_parser_ext(sp->opath, sudoers_file, true, quiet ? 0 : 2);
|
||||
sp->errorline = -1;
|
||||
|
||||
/* Parse the sudoers temp file(s) */
|
||||
@@ -1067,7 +1067,7 @@ check_syntax(const char *path, bool quiet, bool strict, bool check_owner,
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
init_parser_ext(fname, true, quiet ? 0 : 2);
|
||||
init_parser_ext(fname, path, true, quiet ? 0 : 2);
|
||||
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
|
||||
if (sudoersparse() && !parse_error) {
|
||||
if (!quiet)
|
||||
@@ -1228,7 +1228,7 @@ open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
|
||||
/* Check for existing entry using the first file in path. */
|
||||
len = strcspn(path, ":");
|
||||
TAILQ_FOREACH(entry, &sudoerslist, entries) {
|
||||
if (strncmp(path, entry->opath, len) == 0 && entry->opath[len] == '\0')
|
||||
if (strncmp(path, entry->dpath, len) == 0 && entry->dpath[len] == '\0')
|
||||
break;
|
||||
}
|
||||
if (entry == NULL) {
|
||||
|
Reference in New Issue
Block a user