From: Todd C. Miller Date: Mon, 20 Feb 2017 23:44:12 +0000 (-0700) Subject: Move the file digest code out of match.c and into filedigest.c. X-Git-Tag: SUDO_1_8_20^2~96 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b5e7b7bd2cdd208d5905a9d305d9646503c35029;p=sudo Move the file digest code out of match.c and into filedigest.c. Inspired by RedHat changes that used libgcrypt. Also add digest_type_to_name() to map a sudo digest type (int) to a name (string) and use it. --- diff --git a/MANIFEST b/MANIFEST index bba1c5c43..f57694395 100644 --- a/MANIFEST +++ b/MANIFEST @@ -260,8 +260,10 @@ plugins/sudoers/def_data.h plugins/sudoers/def_data.in plugins/sudoers/defaults.c plugins/sudoers/defaults.h +plugins/sudoers/digestname.c plugins/sudoers/editor.c plugins/sudoers/env.c +plugins/sudoers/filedigest.c plugins/sudoers/find_path.c plugins/sudoers/gc.c plugins/sudoers/gentime.c diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index f8f90b619..f60361a37 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -150,11 +150,11 @@ TEST_PROGS = check_iolog_path check_fill check_wrap check_addr check_digest \ AUTH_OBJS = sudo_auth.lo @AUTH_OBJS@ -LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo gentime.lo \ - hexchar.lo gmtoff.lo gram.lo match.lo match_addr.lo \ - pwutil.lo pwutil_impl.lo rcstr.lo redblack.lo \ - sudoers_debug.lo timeout.lo timestr.lo toke.lo \ - toke_util.lo +LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo digestname.lo \ + filedigest.lo gentime.lo gmtoff.lo gram.lo hexchar.lo \ + match.lo match_addr.lo pwutil.lo pwutil_impl.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 \ @@ -656,6 +656,11 @@ defaults.lo: $(srcdir)/defaults.c $(devdir)/def_data.c $(devdir)/def_data.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)/defaults.c +digestname.lo: $(srcdir)/digestname.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_debug.h \ + $(incdir)/sudo_queue.h $(srcdir)/parse.h \ + $(srcdir)/sudoers_debug.h $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/digestname.c editor.lo: $(srcdir)/editor.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 \ @@ -674,6 +679,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 +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 \ + $(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)/parse.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)/filedigest.c find_path.lo: $(srcdir)/find_path.c $(devdir)/def_data.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \ diff --git a/plugins/sudoers/digestname.c b/plugins/sudoers/digestname.c new file mode 100644 index 000000000..e7b837340 --- /dev/null +++ b/plugins/sudoers/digestname.c @@ -0,0 +1,50 @@ +/* + * 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 "sudo_compat.h" +#include "sudoers_debug.h" +#include "parse.h" + +const char * +digest_type_to_name(int digest_type) +{ + const char *digest_name; + debug_decl(digest_type_to_name, SUDOERS_DEBUG_UTIL) + + switch (digest_type) { + case SUDO_DIGEST_SHA224: + digest_name = "sha224"; + break; + case SUDO_DIGEST_SHA256: + digest_name = "sha256"; + break; + case SUDO_DIGEST_SHA384: + digest_name = "sha384"; + break; + case SUDO_DIGEST_SHA512: + digest_name = "sha512"; + break; + default: + digest_name = "unknown digest"; + break; + } + debug_return_const_str(digest_name); +} diff --git a/plugins/sudoers/filedigest.c b/plugins/sudoers/filedigest.c new file mode 100644 index 000000000..d1000592c --- /dev/null +++ b/plugins/sudoers/filedigest.c @@ -0,0 +1,142 @@ +/* + * 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. + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#endif +#include +#include +#include + +#include "sudoers.h" +#include "parse.h" + +#ifdef HAVE_SHA224UPDATE +# include +#else +# include "compat/sha2.h" +#endif + +static struct digest_function { + const unsigned int digest_len; + void (*init)(SHA2_CTX *); +#ifdef SHA2_VOID_PTR + void (*update)(SHA2_CTX *, const void *, size_t); + void (*final)(void *, SHA2_CTX *); +#else + void (*update)(SHA2_CTX *, const unsigned char *, size_t); + void (*final)(unsigned char *, SHA2_CTX *); +#endif +} digest_functions[] = { + { + SHA224_DIGEST_LENGTH, + SHA224Init, + SHA224Update, + SHA224Final + }, { + SHA256_DIGEST_LENGTH, + SHA256Init, + SHA256Update, + SHA256Final + }, { + SHA384_DIGEST_LENGTH, + SHA384Init, + SHA384Update, + SHA384Final + }, { + SHA512_DIGEST_LENGTH, + SHA512Init, + SHA512Update, + SHA512Final + }, { + 0 + } +}; + +unsigned char * +sudo_filedigest(int fd, const char *file, int digest_type, size_t *digest_len) +{ + struct digest_function *func = NULL; + unsigned char *file_digest = NULL; + unsigned char buf[32 * 1024]; + size_t nread; + SHA2_CTX ctx; + int i, fd2; + FILE *fp = NULL; + debug_decl(sudo_filedigest, SUDOERS_DEBUG_UTIL) + + for (i = 0; digest_functions[i].digest_len != 0; i++) { + if (digest_type == i) { + func = &digest_functions[i]; + break; + } + } + if (func == NULL) { + sudo_warnx(U_("unsupported digest type %d for %s"), digest_type, file); + goto bad; + } + + if ((fd2 = dup(fd)) == -1) { + sudo_debug_printf(SUDO_DEBUG_INFO, "unable to dup %s: %s", + file, strerror(errno)); + goto bad; + } + if ((fp = fdopen(fd2, "r")) == NULL) { + sudo_debug_printf(SUDO_DEBUG_INFO, "unable to fdopen %s: %s", + file, strerror(errno)); + close(fd2); + goto bad; + } + if ((file_digest = malloc(func->digest_len)) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + goto bad; + } + + func->init(&ctx); + while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) { + func->update(&ctx, buf, nread); + } + if (ferror(fp)) { + sudo_warnx(U_("%s: read error"), file); + goto bad; + } + func->final(file_digest, &ctx); + fclose(fp); + + *digest_len = func->digest_len; + debug_return_ptr(file_digest); +bad: + free(file_digest); + if (fp != NULL) + fclose(fp); + debug_return_ptr(NULL); +} diff --git a/plugins/sudoers/ldap.c b/plugins/sudoers/ldap.c index 1fa74d086..9b866ad5e 100644 --- a/plugins/sudoers/ldap.c +++ b/plugins/sudoers/ldap.c @@ -968,10 +968,8 @@ sudo_ldap_extract_digest(char **cmnd, struct sudo_digest *digest) cp++; *cmnd = cp; DPRINTF1("%s digest %s for %s", - digest_type == SUDO_DIGEST_SHA224 ? "sha224" : - digest_type == SUDO_DIGEST_SHA256 ? "sha256" : - digest_type == SUDO_DIGEST_SHA384 ? "sha384" : - "sha512", digest->digest_str, cp); + digest_type_to_name(digest_type), + digest->digest_str, cp); debug_return_ptr(digest); } } diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c index b51eff4bb..bb68285ec 100644 --- a/plugins/sudoers/match.c +++ b/plugins/sudoers/match.c @@ -69,11 +69,6 @@ #else # include "compat/fnmatch.h" #endif /* HAVE_FNMATCH */ -#ifdef HAVE_SHA224UPDATE -# include -#else -# include "compat/sha2.h" -#endif #if !defined(O_SEARCH) && defined(O_PATH) # define O_SEARCH O_PATH @@ -723,127 +718,62 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const } #else /* !SUDOERS_NAME_MATCH */ -static struct digest_function { - const char *digest_name; - const unsigned int digest_len; - void (*init)(SHA2_CTX *); -#ifdef SHA2_VOID_PTR - void (*update)(SHA2_CTX *, const void *, size_t); - void (*final)(void *, SHA2_CTX *); -#else - void (*update)(SHA2_CTX *, const unsigned char *, size_t); - void (*final)(unsigned char *, SHA2_CTX *); -#endif -} digest_functions[] = { - { - "SHA224", - SHA224_DIGEST_LENGTH, - SHA224Init, - SHA224Update, - SHA224Final - }, { - "SHA256", - SHA256_DIGEST_LENGTH, - SHA256Init, - SHA256Update, - SHA256Final - }, { - "SHA384", - SHA384_DIGEST_LENGTH, - SHA384Init, - SHA384Update, - SHA384Final - }, { - "SHA512", - SHA512_DIGEST_LENGTH, - SHA512Init, - SHA512Update, - SHA512Final - }, { - NULL - } -}; - static bool digest_matches(int fd, const char *file, const struct sudo_digest *sd) { - unsigned char file_digest[SHA512_DIGEST_LENGTH]; - unsigned char sudoers_digest[SHA512_DIGEST_LENGTH]; - unsigned char buf[32 * 1024]; - struct digest_function *func = NULL; - size_t nread; - SHA2_CTX ctx; - FILE *fp; - int fd2; - unsigned int i; + unsigned char *file_digest = NULL; + unsigned char *sudoers_digest = NULL; + bool matched = false; + size_t digest_len; debug_decl(digest_matches, SUDOERS_DEBUG_MATCH) - for (i = 0; digest_functions[i].digest_name != NULL; i++) { - if (sd->digest_type == i) { - func = &digest_functions[i]; - break; - } + file_digest = sudo_filedigest(fd, file, sd->digest_type, &digest_len); + if (file_digest == NULL) { + /* Warning (if any) printed by sudo_filedigest() */ + goto done; } - if (func == NULL) { - sudo_warnx(U_("unsupported digest type %d for %s"), sd->digest_type, file); - debug_return_bool(false); + + /* Convert the command digest from ascii to binary. */ + if ((sudoers_digest = malloc(digest_len)) == NULL) { + sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + goto done; } - if (strlen(sd->digest_str) == func->digest_len * 2) { - /* Convert the command digest from ascii hex to binary. */ - for (i = 0; i < func->digest_len; i++) { + if (strlen(sd->digest_str) == digest_len * 2) { + /* Convert ascii hex to binary. */ + unsigned int i; + for (i = 0; i < digest_len; i++) { const int h = hexchar(&sd->digest_str[i + i]); if (h == -1) goto bad_format; sudoers_digest[i] = (unsigned char)h; } } else { - size_t len = base64_decode(sd->digest_str, sudoers_digest, - sizeof(sudoers_digest)); - if (len != func->digest_len) { + /* Convert base64 to binary. */ + size_t len = base64_decode(sd->digest_str, sudoers_digest, digest_len); + if (len != digest_len) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, - "incorrect length for digest, expected %u, got %zu", - func->digest_len, len); + "incorrect length for digest, expected %zu, got %zu", + digest_len, len); goto bad_format; } } - if ((fd2 = dup(fd)) == -1) { - sudo_debug_printf(SUDO_DEBUG_INFO, "unable to dup %s: %s", - file, strerror(errno)); - debug_return_bool(false); - } - if ((fp = fdopen(fd2, "r")) == NULL) { - sudo_debug_printf(SUDO_DEBUG_INFO, "unable to open %s: %s", - file, strerror(errno)); - close(fd2); - debug_return_bool(false); - } - - func->init(&ctx); - while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) { - func->update(&ctx, buf, nread); - } - if (ferror(fp)) { - sudo_warnx(U_("%s: read error"), file); - fclose(fp); - debug_return_bool(false); - } - func->final(file_digest, &ctx); - - if (memcmp(file_digest, sudoers_digest, func->digest_len) != 0) { - fclose(fp); + if (memcmp(file_digest, sudoers_digest, digest_len) == 0) { + matched = true; + } else { sudo_debug_printf(SUDO_DEBUG_DIAG|SUDO_DEBUG_LINENO, "%s digest mismatch for %s, expecting %s", - func->digest_name, file, sd->digest_str); - debug_return_bool(false); + digest_type_to_name(sd->digest_type), file, sd->digest_str); } + goto done; - fclose(fp); - debug_return_bool(true); bad_format: sudo_warnx(U_("digest for %s (%s) is not in %s form"), file, - sd->digest_str, func->digest_name); - debug_return_bool(false); + sd->digest_str, digest_type_to_name(sd->digest_type)); +done: + free(sudoers_digest); + free(file_digest); + debug_return_bool(matched); } static bool diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h index 407651c0c..b16f9ce03 100644 --- a/plugins/sudoers/parse.h +++ b/plugins/sudoers/parse.h @@ -290,4 +290,10 @@ long get_gmtoff(time_t *clock); /* gentime.c */ time_t parse_gentime(const char *expstr); +/* filedigest.c */ +unsigned char *sudo_filedigest(int fd, const char *file, int digest_type, size_t *digest_len); + +/* digestname.c */ +const char *digest_type_to_name(int digest_type); + #endif /* SUDOERS_PARSE_H */ diff --git a/plugins/sudoers/sssd.c b/plugins/sudoers/sssd.c index 93d407c7d..c6725cfa8 100644 --- a/plugins/sudoers/sssd.c +++ b/plugins/sudoers/sssd.c @@ -1059,10 +1059,8 @@ sudo_sss_extract_digest(char **cmnd, struct sudo_digest *digest) *cmnd = cp; sudo_debug_printf(SUDO_DEBUG_INFO, "%s digest %s for %s", - digest_type == SUDO_DIGEST_SHA224 ? "sha224" : - digest_type == SUDO_DIGEST_SHA256 ? "sha256" : - digest_type == SUDO_DIGEST_SHA384 ? "sha384" : - "sha512", digest->digest_str, cp); + digest_type_to_name(digest_type), + digest->digest_str, cp); debug_return_ptr(digest); } } diff --git a/plugins/sudoers/visudo_json.c b/plugins/sudoers/visudo_json.c index bbea8d891..f3c330f1d 100644 --- a/plugins/sudoers/visudo_json.c +++ b/plugins/sudoers/visudo_json.c @@ -254,23 +254,7 @@ print_command_json(FILE *fp, struct member *m, int indent, bool last_one) /* Optional digest. */ if (c->digest != NULL) { fputs(",\n", fp); - switch (c->digest->digest_type) { - case SUDO_DIGEST_SHA224: - digest_name = "sha224"; - break; - case SUDO_DIGEST_SHA256: - digest_name = "sha256"; - break; - case SUDO_DIGEST_SHA384: - digest_name = "sha384"; - break; - case SUDO_DIGEST_SHA512: - digest_name = "sha512"; - break; - default: - digest_name = "invalid digest"; - break; - } + digest_name = digest_type_to_name(c->digest->digest_type); value.type = JSON_STRING; value.u.string = c->digest->digest_str; print_pair_json(fp, NULL, digest_name, &value, NULL, indent);