Support adminconfdir for relative include paths in sudoers.

This commit is contained in:
Todd C. Miller
2023-05-02 10:47:11 -06:00
parent 7a6ac5d26e
commit 5446b009e3
9 changed files with 430 additions and 245 deletions

View File

@@ -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);
}
/*

View File

@@ -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);
}
/*

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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++;
}
/* Make a copy of the fully-qualified path and return it. */
path = pp = sudo_rcstr_alloc(dirlen + len);
if (path == NULL) {
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 (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)
if (strlcpy_expand_host(dst0, src, dst_size) >= dst_size)
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';
debug_return_str(dst0);
}
debug_return_str(path);
/*
* 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. */
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);
}
for (cp = sudo_strsplit(path, path_end, ":", &ep); cp != NULL;
cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
size_t len;
char *dirend;
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;
}

View File

@@ -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) {