From c8a6eecf768d8102a9a77f5fdb5b516e571d462e Mon Sep 17 00:00:00 2001 From: Radovan Sroka Date: Tue, 23 Aug 2016 13:43:08 +0200 Subject: [PATCH] Using libgcrypt Using libgcrypt and not sudo implementation of SHA... Rebased patch of digest backport. Added option --with-gcrypt Rebased from: Patch35: sudo-1.8.6p7-digest-backport.patch Resolves: rhbz#1183818 --- configure.ac | 16 +++++++ plugins/sudoers/Makefile.in | 9 +++- plugins/sudoers/filedigest.c | 104 +++++++++++++++++++++++++++++++++++++++++++ plugins/sudoers/filedigest.h | 17 +++++++ plugins/sudoers/match.c | 94 ++++++++++++++++++++++++++++++-------- 5 files changed, 219 insertions(+), 21 deletions(-) create mode 100644 plugins/sudoers/filedigest.c create mode 100644 plugins/sudoers/filedigest.h diff --git a/configure.ac b/configure.ac index 13c3c1b..54929b2 100644 --- a/configure.ac +++ b/configure.ac @@ -35,6 +35,7 @@ AC_SUBST([SUDO_OBJS]) AC_SUBST([LIBS]) AC_SUBST([SUDO_LIBS]) AC_SUBST([SUDOERS_LIBS]) +AC_SUBST([LIBPARSESUDOERS_LIBS]) AC_SUBST([STATIC_SUDOERS]) AC_SUBST([NET_LIBS]) AC_SUBST([AFS_LIBS]) @@ -1517,6 +1518,19 @@ AC_ARG_WITH(selinux, [AS_HELP_STRING([--with-selinux], [enable SELinux support]) ;; esac], [with_selinux=no]) +AC_ARG_WITH(gcrypt, [AS_HELP_STRING([--with-gcrypt], [enable libgcrypt support])], +[case $with_gcrypt in + yes) + AC_DEFINE(HAVE_LIBGCRYPT) + LIBPARSESUDOERS_LIBS="${LIBPARSESUDOERS_LIBS} -lgcrypt" + AC_CHECK_LIB([gcrypt], [gcry_md_open], + [AC_DEFINE(HAVE_GCRY_MD_OPEN)]) + ;; + no) ;; + *) AC_MSG_ERROR(["--with-gcrypt does not take an argument."]) + ;; +esac]) + dnl dnl gss_krb5_ccache_name() may not work on Heimdal so we don't use it by default dnl @@ -4344,6 +4358,8 @@ AH_TEMPLATE(HAVE_PROJECT_H, [Define to 1 if you have the header file AH_TEMPLATE(HAVE_SECURID, [Define to 1 if you use SecurID for authentication.]) AH_TEMPLATE(HAVE_SELINUX, [Define to 1 to enable SELinux RBAC support.]) AH_TEMPLATE(HAVE_SETKEYCREATECON, [Define to 1 if you have the `setkeycreatecon' function.]) +AH_TEMPLATE(HAVE_LIBGCRYPT, [Define to 1 to enable libgcrypt support.]) +AH_TEMPLATE(HAVE_GCRY_MD_OPEN, [Define to 1 if you have the `gcry_md_open' function.]) AH_TEMPLATE(HAVE_SHL_LOAD, [Define to 1 if you have the `shl_load' function.]) AH_TEMPLATE(HAVE_SKEY, [Define to 1 if you use S/Key.]) AH_TEMPLATE(HAVE_SKEYACCESS, [Define to 1 if your S/Key library has skeyaccess().]) diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index f36f9ef..32c0ed0 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -55,6 +55,7 @@ LT_LIBS = $(top_builddir)/lib/util/libsudo_util.la LIBS = $(LT_LIBS) NET_LIBS = @NET_LIBS@ SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS) @ZLIB@ @LIBMD@ +LIBPARSESUDOERS_LIBS = @LIBPARSESUDOERS_LIBS@ REPLAY_LIBS = @REPLAY_LIBS@ @ZLIB@ VISUDO_LIBS = $(NET_LIBS) @LIBMD@ TESTSUDOERS_LIBS = $(NET_LIBS) @LIBMD@ @@ -153,7 +154,7 @@ AUTH_OBJS = sudo_auth.lo @AUTH_OBJS@ LIBPARSESUDOERS_OBJS = alias.lo audit.lo base64.lo defaults.lo hexchar.lo \ gram.lo match.lo match_addr.lo pwutil.lo pwutil_impl.lo \ rcstr.lo redblack.lo sudoers_debug.lo timestr.lo \ - toke.lo toke_util.lo + toke.lo toke_util.lo filedigest.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 \ @@ -217,7 +218,7 @@ Makefile: $(srcdir)/Makefile.in (cd $(top_builddir) && ./config.status --file plugins/sudoers/Makefile) libparsesudoers.la: $(LIBPARSESUDOERS_OBJS) - $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) -o $@ $(LIBPARSESUDOERS_OBJS) -no-install + $(LIBTOOL) --mode=link $(CC) -o $@ $(LIBPARSESUDOERS_OBJS) $(LIBPARSESUDOERS_LIBS) -no-install sudoers.la: $(SUDOERS_OBJS) $(LT_LIBS) libparsesudoers.la @LT_LDDEP@ case "$(LT_LDFLAGS)" in \ @@ -656,6 +657,10 @@ 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 $(top_builddir)/config.h \ + $(incdir)/sudo_debug.h + $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/filedigest.c +filedigest.o: filedigest.lo 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/filedigest.c b/plugins/sudoers/filedigest.c new file mode 100644 index 0000000..c173741 --- /dev/null +++ b/plugins/sudoers/filedigest.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include +#include +#include "filedigest.h" +#include "sudo_compat.h" +#include "sudo_debug.h" + +#if defined(HAVE_LIBGCRYPT) +#include + +static int sudo_filedigest_gcrypt(int fd, int algo, unsigned char **dvalue, size_t *dvalue_size) +{ + char buffer[4096]; + gcry_md_hd_t ctx; + int gcry_algo; + debug_decl(sudo_filedigest_gcrypt, SUDO_DEBUG_UTIL); + + switch(algo) { + case SUDO_DIGEST_SHA224: + gcry_algo = GCRY_MD_SHA224; break; + case SUDO_DIGEST_SHA256: + gcry_algo = GCRY_MD_SHA256; break; + case SUDO_DIGEST_SHA384: + gcry_algo = GCRY_MD_SHA384; break; + case SUDO_DIGEST_SHA512: + gcry_algo = GCRY_MD_SHA512; break; + default: + debug_return_int(-1); + } + + gcry_md_open(&ctx, gcry_algo, 0); + + /* Read block of data from fd and digest them */ + while (1) { + const ssize_t read_bytes = read(fd, buffer, sizeof buffer); + + if (read_bytes < 0) { + /* Error */ + gcry_md_close(ctx); + debug_return_int(-1); + } + else if (read_bytes > 0) { + /* Some data read -- update the digest */ + gcry_md_write(ctx, buffer, (size_t)read_bytes); + } + else { + /* EOF */ + break; + } + } + + /* + * All data digested. Finalize the digest value. + */ + const unsigned char *value = gcry_md_read(ctx, gcry_algo); + + if (value == NULL) { + debug_return_int(-1); + } + + /* + * Make a copy of the digest value. The pointer + * returned from gcry_md_read cannot be used after + * gcry_md_close was called + */ + (*dvalue_size) = gcry_md_get_algo_dlen(gcry_algo); + (*dvalue) = malloc(*dvalue_size); + + if (*dvalue == NULL) { + debug_return_int(-1); + } + + memcpy(*dvalue, value, *dvalue_size); + gcry_md_close(ctx); + + debug_return_int(0); +} +#endif + +#include + +int sudo_filedigest(const char *path, int algo, unsigned char **dvalue, size_t *dvalue_size) +{ + int rc = -1; + int fd = -1; + debug_decl(sudo_filedigest, SUDO_DEBUG_UTIL); + + if ((fd = open(path, O_RDONLY)) < 0) { + debug_return_int(rc); + } + +#if defined(HAVE_LIBGCRYPT) + rc = sudo_filedigest_gcrypt(fd, algo, dvalue, dvalue_size); + close(fd); +#else + rc = -1; + errno = ENOTSUP; +#endif + debug_return_int(rc); +} diff --git a/plugins/sudoers/filedigest.h b/plugins/sudoers/filedigest.h new file mode 100644 index 0000000..437f02f --- /dev/null +++ b/plugins/sudoers/filedigest.h @@ -0,0 +1,17 @@ +#include + +#define SUDO_DIGEST_SHA224 0 +#define SUDO_DIGEST_SHA256 1 +#define SUDO_DIGEST_SHA384 2 +#define SUDO_DIGEST_SHA512 3 +#define SUDO_DIGEST_INVALID 4 + +#define SUDO_SHA224_DIGEST_LENGTH 28 +#define SUDO_SHA256_DIGEST_LENGTH 32 +#define SUDO_SHA384_DIGEST_LENGTH 48 +#define SUDO_SHA512_DIGEST_LENGTH 64 + +/* + * Compute a digest of a given file. Returns 0 on success, -1 otherwise. + */ +int sudo_filedigest(const char *path, int algo, unsigned char **dvalue, size_t *dvalue_size); diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c index 1916bde..2a9ea4b 100644 --- a/plugins/sudoers/match.c +++ b/plugins/sudoers/match.c @@ -62,6 +62,7 @@ #include "sudoers.h" #include "parse.h" +#include "filedigest.h" #include #ifdef HAVE_FNMATCH @@ -576,6 +577,7 @@ command_matches_normal(const char *sudoers_cmnd, const char *sudoers_args, const } #else /* !SUDOERS_NAME_MATCH */ +#ifndef HAVE_LIBGCRYPT /* !!! */ static struct digest_function { const char *digest_name; const unsigned int digest_len; @@ -616,24 +618,43 @@ static struct digest_function { NULL } }; +#endif /* !HAVE_LIBGCRYPT */ + +static const char *digesttype2str(int digest_type) +{ + switch(digest_type) { + case SUDO_DIGEST_SHA224: + return "SHA224"; + case SUDO_DIGEST_SHA256: + return "SHA256"; + case SUDO_DIGEST_SHA384: + return "SHA384"; + case SUDO_DIGEST_SHA512: + return "SHA512"; + } + return ""; +} static bool digest_matches(const char *file, const struct sudo_digest *sd, int *fd) { - unsigned char file_digest[SHA512_DIGEST_LENGTH]; - unsigned char sudoers_digest[SHA512_DIGEST_LENGTH]; + unsigned char * file_digest = NULL; + unsigned char * sudoers_digest = NULL; + size_t digest_size; unsigned char buf[32 * 1024]; - struct digest_function *func = NULL; #ifdef HAVE_FEXECVE bool first = true; bool is_script = false; #endif /* HAVE_FEXECVE */ size_t nread; - SHA2_CTX ctx; FILE *fp; unsigned int i; debug_decl(digest_matches, SUDOERS_DEBUG_MATCH) +#ifndef HAVE_LIBGCRYPT /* !!! */ + + SHA2_CTX ctx; + struct digest_function *func = NULL; for (i = 0; digest_functions[i].digest_name != NULL; i++) { if (sd->digest_type == i) { func = &digest_functions[i]; @@ -644,9 +665,33 @@ digest_matches(const char *file, const struct sudo_digest *sd, int *fd) sudo_warnx(U_("unsupported digest type %d for %s"), sd->digest_type, file); debug_return_bool(false); } - if (strlen(sd->digest_str) == func->digest_len * 2) { + + digest_size = func->digest_len; + + file_digest = malloc(digest_size); + if (file_digest == NULL) { + debug_return_bool(false); + } + +#elif HAVE_LIBGCRYPT + + if (sudo_filedigest(file, sd->digest_type, + &file_digest, &digest_size) != 0) { + sudo_warnx(U_("Cannot compute digest type %d for %s"), sd->digest_type, file); + goto clean_up; + } + +#endif /* !HAVE_LIBGCRYPT */ + + sudoers_digest = malloc(digest_size); + if (sudoers_digest == NULL) { + free(file_digest); + debug_return_bool(false); + } + + if (strlen(sd->digest_str) == digest_size * 2) { /* Convert the command digest from ascii hex to binary. */ - for (i = 0; i < func->digest_len; i++) { + for (i = 0; i < digest_size ; i++) { const int h = hexchar(&sd->digest_str[i + i]); if (h == -1) goto bad_format; @@ -654,11 +699,11 @@ digest_matches(const char *file, const struct sudo_digest *sd, int *fd) } } else { size_t len = base64_decode(sd->digest_str, sudoers_digest, - sizeof(sudoers_digest)); - if (len != func->digest_len) { + digest_size); + if (len != digest_size) { 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_size, len); goto bad_format; } } @@ -666,10 +711,11 @@ digest_matches(const char *file, const struct sudo_digest *sd, int *fd) if ((fp = fopen(file, "r")) == NULL) { sudo_debug_printf(SUDO_DEBUG_INFO, "unable to open %s: %s", file, strerror(errno)); - debug_return_bool(false); + goto clean_up; } - +#ifndef HAVE_LIBGCRYPT func->init(&ctx); +#endif /* !HAVE_LIBGCRYPT */ while ((nread = fread(buf, 1, sizeof(buf), fp)) != 0) { #ifdef HAVE_FEXECVE /* Check for #! cookie and set is_script. */ @@ -679,21 +725,24 @@ digest_matches(const char *file, const struct sudo_digest *sd, int *fd) is_script = true; } #endif /* HAVE_FEXECVE */ +#ifndef HAVE_LIBGCRYPT func->update(&ctx, buf, nread); +#endif /* !HAVE_LIBGCRYPT */ } if (ferror(fp)) { sudo_warnx(U_("%s: read error"), file); fclose(fp); - debug_return_bool(false); + goto clean_up; } +#ifndef HAVE_LIBGCRYPT func->final(file_digest, &ctx); - - if (memcmp(file_digest, sudoers_digest, func->digest_len) != 0) { +#endif /* !HAVE_LIBGCRYPT */ + if (memcmp(file_digest, sudoers_digest, digest_size) != 0) { fclose(fp); 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); + digesttype2str(sd->digest_type), file, sd->digest_str); + goto clean_up; } #ifdef HAVE_FEXECVE @@ -705,7 +754,7 @@ digest_matches(const char *file, const struct sudo_digest *sd, int *fd) sudo_debug_printf(SUDO_DEBUG_INFO, "unable to dup %s: %s", file, strerror(errno)); fclose(fp); - debug_return_bool(false); + goto clean_up; } /* * Shell scripts go through namei twice and so we can't set the close @@ -715,10 +764,17 @@ digest_matches(const char *file, const struct sudo_digest *sd, int *fd) (void)fcntl(*fd, F_SETFD, FD_CLOEXEC); #endif /* HAVE_FEXECVE */ fclose(fp); + free(file_digest); + free(sudoers_digest); 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); + sd->digest_str, digesttype2str(sd->digest_type)); +clean_up: + if (file_digest) + free(file_digest); + if (sudoers_digest) + free(sudoers_digest); debug_return_bool(false); } -- 2.7.4