summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <Todd.Miller@sudo.ws>2023-05-02 10:37:38 -0600
committerTodd C. Miller <Todd.Miller@sudo.ws>2023-05-02 10:37:38 -0600
commit96ac9744fa4391a373b5960ad37ac1443118e87b (patch)
treece8075421176449243984ce73ab0d975b9f2e897
parentd4c33b71898c9f4b122815c6826ad5cc8a69ff60 (diff)
downloadsudo-96ac9744fa4391a373b5960ad37ac1443118e87b.tar.gz
Support sudoers_file being a colon-separated path of files.
The first file found is used.
-rw-r--r--plugins/sudoers/cvtsudoers.c2
-rw-r--r--plugins/sudoers/file.c8
-rw-r--r--plugins/sudoers/regress/fuzz/fuzz_sudoers.c2
-rw-r--r--plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c2
-rw-r--r--plugins/sudoers/sudoers.c18
-rw-r--r--plugins/sudoers/sudoers.h2
-rw-r--r--plugins/sudoers/testsudoers.c2
-rw-r--r--plugins/sudoers/toke.c6
-rw-r--r--plugins/sudoers/toke.l6
-rw-r--r--plugins/sudoers/visudo.c138
10 files changed, 131 insertions, 55 deletions
diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c
index d45803aea..f083f92cb 100644
--- a/plugins/sudoers/cvtsudoers.c
+++ b/plugins/sudoers/cvtsudoers.c
@@ -779,7 +779,7 @@ parse_sudoers(const char *input_file, struct cvtsudoers_config *conf)
}
FILE *
-open_sudoers(const char *file, bool doedit, bool *keepopen)
+open_sudoers(const char *file, char **outfile, bool doedit, bool *keepopen)
{
return fopen(file, "r");
}
diff --git a/plugins/sudoers/file.c b/plugins/sudoers/file.c
index 185617950..cbb1f4f2a 100644
--- a/plugins/sudoers/file.c
+++ b/plugins/sudoers/file.c
@@ -59,6 +59,7 @@ sudo_file_open(struct sudo_nss *nss)
{
debug_decl(sudo_file_open, SUDOERS_DEBUG_NSS);
struct sudo_file_handle *handle;
+ char *outfile = NULL;
/* Note: relies on defaults being initialized early. */
if (def_ignore_local_sudoers)
@@ -72,9 +73,14 @@ sudo_file_open(struct sudo_nss *nss)
handle = malloc(sizeof(*handle));
if (handle != NULL) {
- handle->fp = open_sudoers(sudoers_file, false, NULL);
+ handle->fp = open_sudoers(sudoers_file, &outfile, false, NULL);
if (handle->fp != NULL) {
init_parse_tree(&handle->parse_tree, NULL, NULL, nss);
+ if (outfile != NULL) {
+ /* Update path to open sudoers file. */
+ sudo_rcstr_delref(sudoers);
+ sudoers = outfile;
+ }
} else {
free(handle);
handle = NULL;
diff --git a/plugins/sudoers/regress/fuzz/fuzz_sudoers.c b/plugins/sudoers/regress/fuzz/fuzz_sudoers.c
index 63ad8cca2..d2b89ff23 100644
--- a/plugins/sudoers/regress/fuzz/fuzz_sudoers.c
+++ b/plugins/sudoers/regress/fuzz/fuzz_sudoers.c
@@ -56,7 +56,7 @@ sudo_printf_t sudo_printf = fuzz_printf;
int sudo_mode;
FILE *
-open_sudoers(const char *file, bool doedit, bool *keepopen)
+open_sudoers(const char *file, char **outfile, bool doedit, bool *keepopen)
{
/*
* If we allow the fuzzer to choose include paths it will
diff --git a/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c b/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c
index 941ce94aa..5d86bff08 100644
--- a/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c
+++ b/plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c
@@ -37,7 +37,7 @@ struct passwd *list_pw;
sudo_printf_t sudo_printf = fuzz_printf;
FILE *
-open_sudoers(const char *file, bool doedit, bool *keepopen)
+open_sudoers(const char *file, char **outfile, bool doedit, bool *keepopen)
{
/*
* If we allow the fuzzer to choose include paths it will
diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c
index 103080e47..6aea73dd6 100644
--- a/plugins/sudoers/sudoers.c
+++ b/plugins/sudoers/sudoers.c
@@ -251,7 +251,7 @@ sudoers_init(void *info, sudoers_logger_t logger, char * const envp[])
}
/* Open and parse sudoers, set global defaults. */
- init_parser(sudoers_file);
+ init_parser(NULL);
TAILQ_FOREACH_SAFE(nss, snl, entries, nss_next) {
if (nss->open(nss) == -1 || (nss->parse_tree = nss->parse(nss)) == NULL) {
TAILQ_REMOVE(snl, nss, entries);
@@ -1293,7 +1293,7 @@ open_file(const char *path, int flags)
* Returns a handle to the sudoers file or NULL on error.
*/
FILE *
-open_sudoers(const char *path, bool doedit, bool *keepopen)
+open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
{
char fname[PATH_MAX];
FILE *fp = NULL;
@@ -1311,22 +1311,30 @@ open_sudoers(const char *path, bool doedit, bool *keepopen)
*/
if ((fp = fdopen(fd, "r")) == NULL) {
log_warning(SLOG_PARSE_ERROR, N_("unable to open %s"), fname);
- close(fd);
} else {
+ fd = -1;
if (sb.st_size != 0 && fgetc(fp) == EOF) {
log_warning(SLOG_PARSE_ERROR, N_("unable to read %s"), fname);
fclose(fp);
fp = NULL;
- fd = -1;
} else {
/* Rewind fp and set close on exec flag. */
rewind(fp);
(void)fcntl(fileno(fp), F_SETFD, 1);
+ if (outfile != NULL) {
+ *outfile = sudo_rcstr_dup(fname);
+ if (*outfile == NULL) {
+ sudo_warnx(U_("%s: %s"), __func__,
+ U_("unable to allocate memory"));
+ fclose(fp);
+ fp = NULL;
+ }
+ }
}
}
break;
case SUDO_PATH_MISSING:
- log_warning(SLOG_PARSE_ERROR, N_("unable to open %s"), fname);
+ log_warning(SLOG_PARSE_ERROR, N_("unable to open %s"), path);
break;
case SUDO_PATH_BAD_TYPE:
log_warningx(SLOG_PARSE_ERROR, N_("%s is not a regular file"), fname);
diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h
index d69fd8584..8858f3e3f 100644
--- a/plugins/sudoers/sudoers.h
+++ b/plugins/sudoers/sudoers.h
@@ -421,7 +421,7 @@ void register_env_file(void * (*ef_open)(const char *), void (*ef_close)(void *)
bool matches_env_pattern(const char *pattern, const char *var, bool *full_match);
/* sudoers.c */
-FILE *open_sudoers(const char *, bool, bool *);
+FILE *open_sudoers(const char *, char **, bool, bool *);
bool cb_log_input(const char *file, int line, int column, const union sudo_defs_val *sd_un, int op);
bool cb_log_output(const char *file, int line, int column, const union sudo_defs_val *sd_un, int op);
int set_cmnd_path(const char *runchroot);
diff --git a/plugins/sudoers/testsudoers.c b/plugins/sudoers/testsudoers.c
index 59b082d11..c3121daf0 100644
--- a/plugins/sudoers/testsudoers.c
+++ b/plugins/sudoers/testsudoers.c
@@ -442,7 +442,7 @@ sudo_endspent(void)
}
FILE *
-open_sudoers(const char *file, bool doedit, bool *keepopen)
+open_sudoers(const char *file, char **outfile, bool doedit, bool *keepopen)
{
struct stat sb;
FILE *fp = NULL;
diff --git a/plugins/sudoers/toke.c b/plugins/sudoers/toke.c
index 4779a503a..76585d0e4 100644
--- a/plugins/sudoers/toke.c
+++ b/plugins/sudoers/toke.c
@@ -5841,9 +5841,9 @@ 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, false, &keepopen)) == NULL);
+ } while ((fp = open_sudoers(path, NULL, false, &keepopen)) == NULL);
} else {
- if ((fp = open_sudoers(path, true, &keepopen)) == NULL) {
+ if ((fp = open_sudoers(path, NULL, true, &keepopen)) == NULL) {
/* The error was already printed by open_sudoers() */
sudoerserror(NULL);
sudo_rcstr_delref(path);
@@ -5898,7 +5898,7 @@ pop_include(void)
/* If we are in an include dir, move to the next file. */
while ((pl = SLIST_FIRST(&istack[idepth - 1].more)) != NULL) {
SLIST_REMOVE_HEAD(&istack[idepth - 1].more, entries);
- fp = open_sudoers(pl->path, false, &keepopen);
+ fp = open_sudoers(pl->path, NULL, false, &keepopen);
if (fp != NULL) {
sudolinebuf.len = sudolinebuf.off = 0;
sudolinebuf.toke_start = sudolinebuf.toke_end = 0;
diff --git a/plugins/sudoers/toke.l b/plugins/sudoers/toke.l
index 550b6b9e1..a9111a2f6 100644
--- a/plugins/sudoers/toke.l
+++ b/plugins/sudoers/toke.l
@@ -1294,9 +1294,9 @@ 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, false, &keepopen)) == NULL);
+ } while ((fp = open_sudoers(path, NULL, false, &keepopen)) == NULL);
} else {
- if ((fp = open_sudoers(path, true, &keepopen)) == NULL) {
+ if ((fp = open_sudoers(path, NULL, true, &keepopen)) == NULL) {
/* The error was already printed by open_sudoers() */
sudoerserror(NULL);
sudo_rcstr_delref(path);
@@ -1351,7 +1351,7 @@ pop_include(void)
/* If we are in an include dir, move to the next file. */
while ((pl = SLIST_FIRST(&istack[idepth - 1].more)) != NULL) {
SLIST_REMOVE_HEAD(&istack[idepth - 1].more, entries);
- fp = open_sudoers(pl->path, false, &keepopen);
+ fp = open_sudoers(pl->path, NULL, false, &keepopen);
if (fp != NULL) {
sudolinebuf.len = sudolinebuf.off = 0;
sudolinebuf.toke_start = sudolinebuf.toke_end = 0;
diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c
index 79bdfc31b..13b017144 100644
--- a/plugins/sudoers/visudo.c
+++ b/plugins/sudoers/visudo.c
@@ -97,6 +97,7 @@ static int run_command(const char *, char *const *, bool);
static void parse_sudoers_options(void);
static void setup_signals(void);
static void visudo_cleanup(void);
+sudo_noreturn static void export_sudoers(const char *infile, const char *outfile);
sudo_noreturn static void help(void);
sudo_noreturn static void usage(void);
@@ -250,13 +251,7 @@ main(int argc, char *argv[])
if (export_path != NULL) {
/* Backward compatibility for the time being. */
- sudo_warnx("%s",
- U_("the -x option will be removed in a future release"));
- sudo_warnx("%s",
- U_("please consider using the cvtsudoers utility instead"));
- execlp("cvtsudoers", "cvtsudoers", "-f", "json", "-o", export_path,
- sudoers_file, (char *)0);
- sudo_fatal(U_("unable to execute %s"), "cvtsudoers");
+ export_sudoers(sudoers_file, export_path);
}
/* Mock up a fake sudo_user struct. */
@@ -291,9 +286,9 @@ 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.
*/
- if ((sudoersin = open_sudoers(sudoers_file, true, NULL)) == NULL)
+ init_parser_ext(NULL, true, quiet ? 0 : 2);
+ if ((sudoersin = open_sudoers(sudoers_file, &sudoers, true, NULL)) == NULL)
exit(EXIT_FAILURE);
- init_parser_ext(sudoers_file, true, quiet ? 0 : 2);
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
(void) sudoersparse();
(void) update_defaults(&parsed_policy, NULL,
@@ -1049,26 +1044,32 @@ check_file(const char *path, bool quiet, bool check_owner, bool check_mode)
}
static bool
-check_syntax(const char *file, bool quiet, bool strict, bool check_owner,
+check_syntax(const char *path, bool quiet, bool strict, bool check_owner,
bool check_mode)
{
bool ok = false;
- int oldlocale;
+ int fd, oldlocale;
+ char fname[PATH_MAX];
debug_decl(check_syntax, SUDOERS_DEBUG_UTIL);
- if (strcmp(file, "-") == 0) {
+ if (strcmp(path, "-") == 0) {
sudoersin = stdin;
- file = "stdin";
- } else if ((sudoersin = fopen(file, "r")) == NULL) {
- if (!quiet)
- sudo_warn(U_("unable to open %s"), file);
- goto done;
+ (void)strlcpy(fname, "stdin", sizeof(fname));
+ } else {
+ fd = sudo_open_conf_path(path, fname, sizeof(fname), NULL);
+ if (fd == -1 || (sudoersin = fdopen(fd, "r")) == NULL) {
+ if (!quiet)
+ sudo_warn(U_("unable to open %s"), fname);
+ if (fd != -1
+ close(fd);
+ goto done;
+ }
}
- init_parser_ext(file, true, quiet ? 0 : 2);
+ init_parser_ext(fname, true, quiet ? 0 : 2);
sudoers_setlocale(SUDOERS_LOCALE_SUDOERS, &oldlocale);
if (sudoersparse() && !parse_error) {
if (!quiet)
- sudo_warnx(U_("failed to parse %s file, unknown error"), file);
+ sudo_warnx(U_("failed to parse %s file, unknown error"), fname);
parse_error = true;
}
if (!parse_error) {
@@ -1083,9 +1084,9 @@ check_syntax(const char *file, bool quiet, bool strict, bool check_owner,
struct sudoersfile *sp;
/* Parsed OK, check mode and owner. */
- if (check_file(file, quiet, check_owner, check_mode)) {
+ if (check_file(fname, quiet, check_owner, check_mode)) {
if (!quiet)
- (void) printf(_("%s: parsed OK\n"), file);
+ (void) printf(_("%s: parsed OK\n"), fname);
} else {
ok = false;
}
@@ -1130,24 +1131,45 @@ lock_sudoers(struct sudoersfile *entry)
static struct sudoersfile *
new_sudoers(const char *path, bool doedit)
{
+ const char *cp, *ep, *path_end;
struct sudoersfile *entry;
struct stat sb;
- int open_flags;
+ size_t len;
+ int fd = -1;
debug_decl(new_sudoersfile, SUDOERS_DEBUG_UTIL);
- if (checkonly)
- open_flags = O_RDONLY;
- else
- open_flags = O_RDWR | O_CREAT;
+ /* Open the first file found in the colon-separated path. */
+ path_end = path + strlen(path);
+ for (cp = sudo_strsplit(path, path_end, ":", &ep);
+ cp != NULL; cp = sudo_strsplit(NULL, path_end, ":", &ep)) {
+
+ char fname[PATH_MAX];
+ len = ep - cp;
+ if (len >= sizeof(fname)) {
+ errno = ENAMETOOLONG;
+ break;
+ }
+ memcpy(fname, cp, len);
+ fname[len] = '\0';
+
+ /* Open in write mode for file locking. */
+ fd = open(fname, checkonly ? O_RDONLY : O_RDWR);
+ if (fd != -1 || errno != ENOENT)
+ break;
+ }
+ /* If more than one file is specified, we always write to the first one. */
+ len = strcspn(path, ":");
entry = calloc(1, sizeof(*entry));
- if (entry == NULL || (entry->path = strdup(path)) == NULL)
+ if (entry == NULL || (entry->path = strndup(path, len)) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- /* entry->tpath = NULL; */
- /* entry->modified = false; */
- entry->doedit = doedit;
- entry->fd = open(entry->path, open_flags, sudoers_mode);
- if (entry->fd == -1 || fstat(entry->fd, &sb) == -1) {
+ if (fd == -1 && errno == ENOENT) {
+ if (!checkonly) {
+ /* Create the first file in the path. */
+ fd = open(entry->path, O_RDWR|O_CREAT, sudoers_mode);
+ }
+ }
+ if (fd == -1 || fstat(fd, &sb) == -1) {
sudo_warn("%s", entry->path);
goto bad;
}
@@ -1155,12 +1177,16 @@ new_sudoers(const char *path, bool doedit)
sudo_warnx(U_("%s is not a regular file"), entry->path);
goto bad;
}
+ entry->fd = fd;
+ /* entry->tpath = NULL; */
+ /* entry->modified = false; */
+ entry->doedit = doedit;
if (!checkonly && !lock_sudoers(entry))
goto bad;
debug_return_ptr(entry);
bad:
- if (entry->fd != -1)
- close(entry->fd);
+ if (fd != -1)
+ close(fd);
free(entry->path);
free(entry);
debug_return_ptr(NULL);
@@ -1171,21 +1197,24 @@ bad:
* any subsequent files #included via a callback from the parser.
*/
FILE *
-open_sudoers(const char *path, bool doedit, bool *keepopen)
+open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
{
struct sudoersfile *entry;
+ size_t len;
FILE *fp;
debug_decl(open_sudoers, SUDOERS_DEBUG_UTIL);
- /* Check for existing entry */
+ /* Check for existing entry using the first file in path. */
+ len = strcspn(path, ":");
TAILQ_FOREACH(entry, &sudoerslist, entries) {
- if (strcmp(path, entry->path) == 0)
+ if (strncmp(path, entry->path, len) == 0 && entry->path[len] == '\0')
break;
}
if (entry == NULL) {
if (doedit && !edit_includes) {
/* Only edit the main sudoers file. */
- if (strcmp(path, sudoers_file) != 0)
+ if (strncmp(path, sudoers_file, len) != 0 ||
+ (sudoers_file[len] != '\0' && sudoers_file[len] != ':'))
doedit = false;
}
if ((entry = new_sudoers(path, doedit)) == NULL)
@@ -1206,9 +1235,42 @@ open_sudoers(const char *path, bool doedit, bool *keepopen)
}
if (keepopen != NULL)
*keepopen = true;
+ if (outfile != NULL) {
+ /* XXX - if path is a list, entry->path may not exist yet. */
+ *outfile = sudo_rcstr_dup(entry->path);
+ if (*outfile == NULL)
+ sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+ }
debug_return_ptr(fp);
}
+/*
+ * Support "visudo -x" for backwards compatibility.
+ * To be removed in a future version of sudo.
+ */
+static void
+export_sudoers(const char *infile, const char *outfile)
+{
+ char pathbuf[PATH_MAX];
+ int fd;
+ debug_decl(export_sudoers, SUDOERS_DEBUG_UTIL);
+
+ sudo_warnx("%s",
+ U_("the -x option will be removed in a future release"));
+ sudo_warnx("%s",
+ U_("please consider using the cvtsudoers utility instead"));
+
+ /* Export the first sudoers file that exists in the path. */
+ fd = sudo_open_conf_path(infile, pathbuf, sizeof(pathbuf), NULL);
+ if (fd != -1) {
+ close(fd);
+ infile = pathbuf;
+ }
+ execlp("cvtsudoers", "cvtsudoers", "-f", "json", "-o", outfile,
+ infile, (char *)0);
+ sudo_fatal(U_("unable to execute %s"), "cvtsudoers");
+}
+
/* Display unused aliases from check_aliases(). */
static int
print_unused(struct sudoers_parse_tree *parse_tree, struct alias *a, void *v)