diff --git a/MANIFEST b/MANIFEST index c8296e523..5c441feb4 100644 --- a/MANIFEST +++ b/MANIFEST @@ -742,6 +742,7 @@ plugins/sudoers/regress/cvtsudoers/test8.out.ok plugins/sudoers/regress/cvtsudoers/test8.sh plugins/sudoers/regress/cvtsudoers/test9.out.ok plugins/sudoers/regress/cvtsudoers/test9.sh +plugins/sudoers/regress/editor/check_editor.c plugins/sudoers/regress/env_match/check_env_pattern.c plugins/sudoers/regress/env_match/data plugins/sudoers/regress/exptilde/check_exptilde.c diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 06ca21f2e..662567dd7 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -155,9 +155,10 @@ SHELL = @SHELL@ PROGS = sudoers.la visudo sudoreplay cvtsudoers testsudoers # Regression tests -TEST_PROGS = check_addr check_base64 check_digest check_env_pattern \ - check_exptilde check_fill check_gentime check_hexchar \ - check_iolog_plugin check_starttime check_unesc @SUDOERS_TEST_PROGS@ +TEST_PROGS = check_addr check_base64 check_digest check_editor \ + check_env_pattern check_exptilde check_fill check_gentime \ + check_hexchar check_iolog_plugin check_starttime \ + check_unesc @SUDOERS_TEST_PROGS@ # Fuzzers LIB_FUZZING_ENGINE = @FUZZ_ENGINE@ @@ -224,6 +225,8 @@ CHECK_BASE64_OBJS = check_base64.o b64_decode.lo b64_encode.o sudoers_debug.lo CHECK_DIGEST_OBJS = check_digest.o filedigest.lo digestname.lo sudoers_debug.lo +CHECK_EDITOR_OBJS = check_editor.o editor.lo sudoers_debug.lo + CHECK_ENV_MATCH_OBJS = check_env_pattern.o env_pattern.lo sudoers_debug.lo CHECK_EXPTILDE_OBJS = check_exptilde.o exptilde.lo pwutil.lo pwutil_impl.lo redblack.lo sudoers_debug.lo @@ -360,6 +363,9 @@ check_base64: $(CHECK_BASE64_OBJS) $(LIBUTIL) check_digest: $(CHECK_DIGEST_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_DIGEST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) +check_editor: $(CHECK_EDITOR_OBJS) $(LIBUTIL) + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_EDITOR_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) + check_env_pattern: $(CHECK_ENV_MATCH_OBJS) $(LIBUTIL) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_ENV_MATCH_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) @@ -1118,6 +1124,30 @@ check_digest.i: $(srcdir)/regress/parser/check_digest.c \ $(CC) -E -o $@ $(CPPFLAGS) $< check_digest.plog: check_digest.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/parser/check_digest.c --i-file $< --output-file $@ +check_editor.o: $(srcdir)/regress/editor/check_editor.c $(devdir)/def_data.c \ + $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_eventlog.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/parse.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/editor/check_editor.c +check_editor.i: $(srcdir)/regress/editor/check_editor.c $(devdir)/def_data.c \ + $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ + $(incdir)/sudo_debug.h $(incdir)/sudo_eventlog.h \ + $(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ + $(incdir)/sudo_util.h $(srcdir)/defaults.h $(srcdir)/logging.h \ + $(srcdir)/parse.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(CC) -E -o $@ $(CPPFLAGS) $< +check_editor.plog: check_editor.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/editor/check_editor.c --i-file $< --output-file $@ check_env_pattern.o: $(srcdir)/regress/env_match/check_env_pattern.c \ $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ diff --git a/plugins/sudoers/editor.c b/plugins/sudoers/editor.c index a0cd63d43..70b8cdf64 100644 --- a/plugins/sudoers/editor.c +++ b/plugins/sudoers/editor.c @@ -41,7 +41,7 @@ static const char * wordsplit(const char *str, const char *endstr, const char **last) { const char *cp; - debug_decl(wordsplit, SUDO_DEBUG_UTIL); + debug_decl(wordsplit, SUDOERS_DEBUG_UTIL); /* If no str specified, use last ptr (if any). */ if (str == NULL) { @@ -72,7 +72,7 @@ wordsplit(const char *str, const char *endstr, const char **last) /* Scan str until we encounter white space. */ for (cp = str; cp < endstr; cp++) { - if (*cp == '\\') { + if (cp[0] == '\\' && cp[1] != '\0') { /* quoted char, do not interpret */ cp++; continue; @@ -96,7 +96,7 @@ copy_arg(const char *src, size_t len) if ((copy = malloc(len + 1)) != NULL) { for (dst = copy; src < src_end; ) { - if (*src == '\\') { + if (src[0] == '\\' && src[1] != '\0') { src++; continue; } diff --git a/plugins/sudoers/regress/editor/check_editor.c b/plugins/sudoers/regress/editor/check_editor.c new file mode 100644 index 000000000..7653c94eb --- /dev/null +++ b/plugins/sudoers/regress/editor/check_editor.c @@ -0,0 +1,142 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2021 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#define SUDO_ERROR_WRAP 0 + +#include "sudoers.h" +#include + +/* Note hard-coded array lengths. */ +struct test_data { + char *editor_var; + int nfiles; + char *files[4]; + char *editor_path; + int edit_argc; + char *edit_argv[10]; +} test_data[] = { + { + /* Bug #942 */ + "SUDO_EDITOR=sh -c \"vi \\$1\"", + 1, + { "/etc/motd", NULL }, + "/usr/bin/sh", + 5, + { "sh", "-c", "vi $1", "--", "/etc/motd", NULL } + }, + { + /* GitHub issue #99 */ + "EDITOR=/usr/bin/vi\\", + 1, + { "/etc/hosts", "/bogus/file", NULL }, + "/usr/bin/vi\\", + 3, + { "/usr/bin/vi\\", "--", "/etc/hosts", "/bogus/file", NULL } + }, + { NULL } +}; + +sudo_dso_public int main(int argc, char *argv[]); + +/* STUB */ +int +find_path(const char *infile, char **outfile, struct stat *sbp, + const char *path, const char *runchroot, int ignore_dot, + char * const *allowlist) +{ + if (infile[0] == '/') { + *outfile = strdup(infile); + } else { + if (asprintf(outfile, "/usr/bin/%s", infile) == -1) + *outfile = NULL; + } + if (*outfile == NULL) + return NOT_FOUND_ERROR; + return FOUND; +} + +int +main(int argc, char *argv[]) +{ + struct test_data *data; + int ntests = 0, errors = 0; + + initprogname(argc > 0 ? argv[0] : "check_editor"); + + for (data = test_data; data->editor_var != NULL; data++) { + const char *env_editor = NULL; + char *cp, *editor_path, **edit_argv = NULL; + int i, edit_argc = 0; + + /* clear existing editor environment vars */ + putenv("VISUAL="); + putenv("EDITOR="); + putenv("SUDO_EDITOR="); + + putenv(data->editor_var); + editor_path = find_editor(data->nfiles, data->files, &edit_argc, + &edit_argv, NULL, &env_editor, false); + ntests++; + if (strcmp(editor_path, data->editor_path) != 0) { + sudo_warnx("test %d: editor_path: expected \"%s\", got \"%s\"", + ntests, data->editor_path, editor_path); + errors++; + } + ntests++; + cp = strchr(data->editor_var, '=') + 1; + if (strcmp(env_editor, cp) != 0) { + sudo_warnx("test %d: env_editor: expected \"%s\", got \"%s\"", + ntests, cp, env_editor ? env_editor : "(NULL)"); + errors++; + } + ntests++; + if (edit_argc != data->edit_argc) { + sudo_warnx("test %d: edit_argc: expected %d, got %d", + ntests, data->edit_argc, edit_argc); + errors++; + } else { + ntests++; + for (i = 0; i < edit_argc; i++) { + if (strcmp(edit_argv[i], data->edit_argv[i]) != 0) { + sudo_warnx("test %d: edit_argv[%d]: expected \"%s\", got \"%s\"", + ntests, i, data->edit_argv[i], edit_argv[i]); + errors++; + break; + } + } + } + + free(editor_path); + edit_argc -= data->nfiles + 1; + for (i = 0; i < edit_argc; i++) { + free(edit_argv[i]); + } + free(edit_argv); + } + + printf("%s: %d tests run, %d errors, %d%% success rate\n", getprogname(), + ntests, errors, (ntests - errors) * 100 / ntests); + + exit(errors); +}