diff options
author | Todd C. Miller <Todd.Miller@sudo.ws> | 2023-05-02 10:37:38 -0600 |
---|---|---|
committer | Todd C. Miller <Todd.Miller@sudo.ws> | 2023-05-02 10:37:38 -0600 |
commit | 96ac9744fa4391a373b5960ad37ac1443118e87b (patch) | |
tree | ce8075421176449243984ce73ab0d975b9f2e897 | |
parent | d4c33b71898c9f4b122815c6826ad5cc8a69ff60 (diff) | |
download | sudo-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.c | 2 | ||||
-rw-r--r-- | plugins/sudoers/file.c | 8 | ||||
-rw-r--r-- | plugins/sudoers/regress/fuzz/fuzz_sudoers.c | 2 | ||||
-rw-r--r-- | plugins/sudoers/regress/fuzz/fuzz_sudoers_ldif.c | 2 | ||||
-rw-r--r-- | plugins/sudoers/sudoers.c | 18 | ||||
-rw-r--r-- | plugins/sudoers/sudoers.h | 2 | ||||
-rw-r--r-- | plugins/sudoers/testsudoers.c | 2 | ||||
-rw-r--r-- | plugins/sudoers/toke.c | 6 | ||||
-rw-r--r-- | plugins/sudoers/toke.l | 6 | ||||
-rw-r--r-- | plugins/sudoers/visudo.c | 138 |
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) |