From 17514b55ea3a8c1a3d10f194e56e68133e20597d Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Fri, 12 May 2017 10:02:17 -0600 Subject: [PATCH] Add support for multiple '*' in env_keep, env_check and env_delete entries. --- MANIFEST | 3 + doc/sudoers.cat | 6 +- doc/sudoers.man.in | 12 ++- doc/sudoers.mdoc.in | 12 ++- plugins/sudoers/Makefile.in | 43 +++++++-- plugins/sudoers/env.c | 23 +---- plugins/sudoers/env_pattern.c | 92 +++++++++++++++++++ .../regress/env_match/check_env_pattern.c | 78 ++++++++++++++++ plugins/sudoers/regress/env_match/data | 19 ++++ plugins/sudoers/sudoers.h | 3 + 10 files changed, 261 insertions(+), 30 deletions(-) create mode 100644 plugins/sudoers/env_pattern.c create mode 100644 plugins/sudoers/regress/env_match/check_env_pattern.c create mode 100644 plugins/sudoers/regress/env_match/data diff --git a/MANIFEST b/MANIFEST index 25ceea8ce..3d123d046 100644 --- a/MANIFEST +++ b/MANIFEST @@ -264,6 +264,7 @@ plugins/sudoers/defaults.h plugins/sudoers/digestname.c plugins/sudoers/editor.c plugins/sudoers/env.c +plugins/sudoers/env_pattern.c plugins/sudoers/filedigest.c plugins/sudoers/filedigest_gcrypt.c plugins/sudoers/filedigest_openssl.c @@ -370,6 +371,8 @@ plugins/sudoers/rcstr.c plugins/sudoers/redblack.c plugins/sudoers/redblack.h plugins/sudoers/regress/check_symbols/check_symbols.c +plugins/sudoers/regress/env_match/check_env_pattern.c +plugins/sudoers/regress/env_match/data plugins/sudoers/regress/iolog_path/check_iolog_path.c plugins/sudoers/regress/iolog_path/data plugins/sudoers/regress/logging/check_wrap.c diff --git a/doc/sudoers.cat b/doc/sudoers.cat index a647432e1..a2f04de40 100644 --- a/doc/sudoers.cat +++ b/doc/sudoers.cat @@ -143,6 +143,10 @@ DDEESSCCRRIIPPTTIIOONN environment variables, use of the default _e_n_v___r_e_s_e_t behavior is encouraged. + Environment variables specified by _e_n_v___c_h_e_c_k, _e_n_v___d_e_l_e_t_e, or _e_n_v___k_e_e_p may + include one or more `*' characters which will match zero or more + characters. No other wildcard characters are supported. + By default, environment variables are matched by name. However, if the pattern includes an equal sign (`='), both the variables name and value must match. For example, an old-style (pre-shellshock) bbaasshh shell @@ -2813,4 +2817,4 @@ DDIISSCCLLAAIIMMEERR file distributed with ssuuddoo or https://www.sudo.ws/license.html for complete details. -Sudo 1.8.20 May 8, 2017 Sudo 1.8.20 +Sudo 1.8.20 May 10, 2017 Sudo 1.8.20 diff --git a/doc/sudoers.man.in b/doc/sudoers.man.in index 18de06b83..97d71c60d 100644 --- a/doc/sudoers.man.in +++ b/doc/sudoers.man.in @@ -21,7 +21,7 @@ .\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" -.TH "SUDOERS" "5" "May 8, 2017" "Sudo @PACKAGE_VERSION@" "File Formats Manual" +.TH "SUDOERS" "5" "May 10, 2017" "Sudo @PACKAGE_VERSION@" "File Formats Manual" .nh .if n .ad l .SH "NAME" @@ -354,6 +354,16 @@ of the default \fIenv_reset\fR behavior is encouraged. .PP +Environment variables specified by +\fIenv_check\fR, +\fIenv_delete\fR, +or +\fIenv_keep\fR +may include one or more +\(oq*\(cq +characters which will match zero or more characters. +No other wildcard characters are supported. +.PP By default, environment variables are matched by name. However, if the pattern includes an equal sign (\(oq=\&\(cq), diff --git a/doc/sudoers.mdoc.in b/doc/sudoers.mdoc.in index a69a7acc5..8dbd4db81 100644 --- a/doc/sudoers.mdoc.in +++ b/doc/sudoers.mdoc.in @@ -19,7 +19,7 @@ .\" Agency (DARPA) and Air Force Research Laboratory, Air Force .\" Materiel Command, USAF, under agreement number F39502-99-1-0512. .\" -.Dd May 8, 2017 +.Dd May 10, 2017 .Dt SUDOERS @mansectform@ .Os Sudo @PACKAGE_VERSION@ .Sh NAME @@ -343,6 +343,16 @@ of the default .Em env_reset behavior is encouraged. .Pp +Environment variables specified by +.Em env_check , +.Em env_delete , +or +.Em env_keep +may include one or more +.Ql * +characters which will match zero or more characters. +No other wildcard characters are supported. +.Pp By default, environment variables are matched by name. However, if the pattern includes an equal sign .Pq Ql =\& , diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 2ac252f2a..dedc08bf6 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -145,8 +145,9 @@ SHELL = @SHELL@ PROGS = sudoers.la visudo sudoreplay testsudoers -TEST_PROGS = check_iolog_path check_fill check_wrap check_addr check_base64 \ - check_gentime check_hexchar check_digest @SUDOERS_TEST_PROGS@ +TEST_PROGS = check_addr check_base64 check_digest check_env_pattern \ + check_fill check_gentime check_hexchar check_iolog_path \ + check_wrap @SUDOERS_TEST_PROGS@ AUTH_OBJS = sudo_auth.lo @AUTH_OBJS@ @@ -156,11 +157,11 @@ LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo digestname.lo \ rcstr.lo redblack.lo sudoers_debug.lo timeout.lo \ timestr.lo toke.lo toke_util.lo -SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo find_path.lo \ - gc.lo goodpath.lo group_plugin.lo interfaces.lo iolog.lo \ - iolog_path.lo locale.lo logging.lo logwrap.lo mkdir_parents.lo \ - parse.lo policy.lo prompt.lo set_perms.lo sudo_nss.lo \ - sudoers.lo timestamp.lo @SUDOERS_OBJS@ +SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo \ + env_pattern.lo find_path.lo gc.lo goodpath.lo group_plugin.lo \ + interfaces.lo iolog.lo iolog_path.lo locale.lo logging.lo \ + logwrap.lo mkdir_parents.lo parse.lo policy.lo prompt.lo \ + set_perms.lo sudo_nss.lo sudoers.lo timestamp.lo @SUDOERS_OBJS@ VISUDO_OBJS = editor.o find_path.o goodpath.o locale.o sudo_printf.o visudo.o \ visudo_json.o @@ -177,6 +178,8 @@ CHECK_BASE64_OBJS = check_base64.o base64.o sudoers_debug.o CHECK_DIGEST_OBJS = check_digest.o @FILEDIGEST@ digestname.o sudoers_debug.o +CHECK_ENV_MATCH_OBJS = check_env_pattern.o env_pattern.o sudoers_debug.o + CHECK_FILL_OBJS = check_fill.o hexchar.o toke_util.o sudoers_debug.o CHECK_GENTIME_OBJS = check_gentime.o gentime.o gmtoff.o sudoers_debug.o @@ -248,6 +251,9 @@ check_base64: $(CHECK_BASE64_OBJS) $(LT_LIBS) check_digest: $(CHECK_DIGEST_OBJS) $(LT_LIBS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_DIGEST_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) @LIBMD@ +check_env_pattern: $(CHECK_ENV_MATCH_OBJS) $(LT_LIBS) + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_ENV_MATCH_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) + check_fill: $(CHECK_FILL_OBJS) $(LT_LIBS) $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(CHECK_FILL_OBJS) $(LDFLAGS) $(ASAN_LDFLAGS) $(PIE_LDFLAGS) $(SSP_LDFLAGS) $(LIBS) @@ -383,6 +389,7 @@ check: $(TEST_PROGS) visudo testsudoers ./check_digest > regress/parser/check_digest.out; \ diff regress/parser/check_digest.out $(srcdir)/regress/parser/check_digest.out.ok || rval=`expr $$rval + $$?`; \ fi; \ + ./check_env_pattern $(srcdir)/regress/env_match/data || rval=`expr $$rval + $$?`; \ ./check_fill || rval=`expr $$rval + $$?`; \ ./check_gentime || rval=`expr $$rval + $$?`; \ ./check_hexchar || rval=`expr $$rval + $$?`; \ @@ -607,6 +614,17 @@ check_digest.o: $(srcdir)/regress/parser/check_digest.c \ $(incdir)/sudo_fatal.h $(incdir)/sudo_queue.h \ $(srcdir)/parse.h $(top_builddir)/config.h $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/regress/parser/check_digest.c +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 \ + $(incdir)/sudo_debug.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)/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/env_match/check_env_pattern.c check_fill.o: $(srcdir)/regress/parser/check_fill.c $(devdir)/gram.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_queue.h \ @@ -685,6 +703,17 @@ env.lo: $(srcdir)/env.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \ $(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ $(top_builddir)/pathnames.h $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/env.c +env_pattern.lo: $(srcdir)/env_pattern.c $(devdir)/def_data.h \ + $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ + $(incdir)/sudo_conf.h $(incdir)/sudo_debug.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)/sudo_nss.h $(srcdir)/sudoers.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \ + $(top_builddir)/pathnames.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/env_pattern.c +env_pattern.o: env_pattern.lo filedigest.lo: $(srcdir)/filedigest.c $(devdir)/def_data.h \ $(incdir)/compat/sha2.h $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \ diff --git a/plugins/sudoers/env.c b/plugins/sudoers/env.c index 2cf5e7787..547639e06 100644 --- a/plugins/sudoers/env.c +++ b/plugins/sudoers/env.c @@ -570,30 +570,13 @@ static bool matches_env_list(const char *var, struct list_members *list, bool *full_match) { struct list_member *cur; - bool match = false; debug_decl(matches_env_list, SUDOERS_DEBUG_ENV) SLIST_FOREACH(cur, list, entries) { - size_t sep_pos, len = strlen(cur->value); - bool iswild = false; - - /* Locate position of the '=' separator in var=value. */ - sep_pos = strcspn(var, "="); - - /* Deal with '*' wildcard at the end of the pattern. */ - if (cur->value[len - 1] == '*') { - len--; - iswild = true; - } - if (strncmp(cur->value, var, len) == 0 && - (iswild || len == sep_pos || var[len] == '\0')) { - /* If we matched past the '=', count as a full match. */ - *full_match = len > sep_pos + 1; - match = true; - break; - } + if (matches_env_pattern(cur->value, var, full_match)) + debug_return_bool(true); } - debug_return_bool(match); + debug_return_bool(false); } /* diff --git a/plugins/sudoers/env_pattern.c b/plugins/sudoers/env_pattern.c new file mode 100644 index 000000000..e00214cd4 --- /dev/null +++ b/plugins/sudoers/env_pattern.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 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 +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ + +#include "sudoers.h" + +/* extern for regress tests */ +bool +matches_env_pattern(const char *pattern, const char *var, bool *full_match) +{ + size_t len, sep_pos; + bool iswild = false, match = false; + bool saw_sep = false; + const char *cp; + debug_decl(matches_env_pattern, SUDOERS_DEBUG_ENV) + + /* Locate position of the '=' separator in var=value. */ + sep_pos = strcspn(var, "="); + + /* Locate '*' wildcard and compute len. */ + for (cp = pattern; *cp != '\0'; cp++) { + if (*cp == '*') { + iswild = true; + break; + } + } + len = (size_t)(cp - pattern); + + if (iswild) { + /* Match up to the '*' wildcard. */ + if (strncmp(pattern, var, len) == 0) { + while (*cp != '\0') { + if (*cp == '*') { + /* Collapse sequential '*'s */ + do { + cp++; + } while (*cp == '*'); + /* A '*' at the end of a pattern matches anything. */ + if (*cp == '\0') { + match = true; + break; + } + /* Keep track of whether we matched an equal sign. */ + if (*cp == '=') + saw_sep = true; + /* Look for first match of text after the '*' */ + while ((saw_sep || len != sep_pos) && + var[len] != '\0' && var[len] != *cp) + len++; + } + if (var[len] != *cp) + break; + cp++; + len++; + } + if (*cp == '\0' && (len == sep_pos || var[len] == '\0')) + match = true; + } + } else { + if (strncmp(pattern, var, len) == 0 && + (len == sep_pos || var[len] == '\0')) { + match = true; + } + } + if (match) + *full_match = len > sep_pos + 1; + debug_return_bool(match); +} diff --git a/plugins/sudoers/regress/env_match/check_env_pattern.c b/plugins/sudoers/regress/env_match/check_env_pattern.c new file mode 100644 index 000000000..76917a6a2 --- /dev/null +++ b/plugins/sudoers/regress/env_match/check_env_pattern.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 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 +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ + +#include "sudo_compat.h" +#include "sudoers.h" + +__dso_public int main(int argc, char *argv[]); + +int +main(int argc, char *argv[]) +{ + FILE *fp = stdin; + char pattern[1024], string[1024]; + int errors = 0, tests = 0, got, want; + + if (argc > 1) { + if ((fp = fopen(argv[1], "r")) == NULL) { + perror(argv[1]); + exit(1); + } + } + + /* + * Read in test file, which is formatted thusly: + * + * pattern string 1/0 + * + */ + for (;;) { + bool full_match = false; + + got = fscanf(fp, "%s %s %d\n", pattern, string, &want); + if (got == EOF) + break; + if (got == 3) { + got = matches_env_pattern(pattern, string, &full_match); + if (full_match) + got++; + if (got != want) { + fprintf(stderr, + "%s: %s %s: want %d, got %d\n", + getprogname(), pattern, string, want, got); + errors++; + } + tests++; + } + } + if (tests != 0) { + printf("%s: %d test%s run, %d errors, %d%% success rate\n", + getprogname(), tests, tests == 1 ? "" : "s", errors, + (tests - errors) * 100 / tests); + } + exit(errors); +} diff --git a/plugins/sudoers/regress/env_match/data b/plugins/sudoers/regress/env_match/data new file mode 100644 index 000000000..a8e92ef79 --- /dev/null +++ b/plugins/sudoers/regress/env_match/data @@ -0,0 +1,19 @@ +foo=(){false;} foo=(){false;} 2 +foo foo=(){false;} 1 +foo= foo=(){false;} 0 +foo=* foo=(){false;} 1 +foo=(* foo=(){false;} 2 +foo=()* foo=(){false;} 2 +foo=*()* foo=(){false;} 2 +foo() foo()=a 1 +foo*() foo()=b 1 +foo*()* foo()= 1 +foo()* foo()= 1 +foo* foo()= 1 +fo*o*() foo()= 1 +fo*o*() fooo()== 1 +fo*o*() foooo()= 1 +fo*o*() foooo 0 +MYPATH=*:/mydir:* MYPATH=/dir1/subdir1:/mydir:/dir2:/dir3/subdir2 2 +MYPATH=*:/mydir:** MYPATH=/dir1/subdir1:/mydir:/dir2:/dir3/subdir2 2 +MYPATH=*:/mdir:* MYPATH=/dir1/subdir1:/mydir:/dir2:/dir3/subdir2 0 diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index d4c01ddb0..4caa857ea 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -359,6 +359,9 @@ int sudoers_hook_putenv(char *string, void *closure); int sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure); int sudoers_hook_unsetenv(const char *name, void *closure); +/* env_pattern.c */ +bool matches_env_pattern(const char *pattern, const char *var, bool *full_match); + /* sudoers.c */ FILE *open_sudoers(const char *, bool, bool *); int sudoers_policy_init(void *info, char * const envp[]); -- 2.40.0