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.
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
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 \
$(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 \
$(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 \
--- /dev/null
+/*
+ * Copyright (c) 2017 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <config.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#if defined(HAVE_STDINT_H)
+# include <stdint.h>
+#elif defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "sudoers.h"
+#include "parse.h"
+
+#ifdef HAVE_SHA224UPDATE
+# include <sha2.h>
+#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);
+}
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);
}
}
#else
# include "compat/fnmatch.h"
#endif /* HAVE_FNMATCH */
-#ifdef HAVE_SHA224UPDATE
-# include <sha2.h>
-#else
-# include "compat/sha2.h"
-#endif
#if !defined(O_SEARCH) && defined(O_PATH)
# define O_SEARCH O_PATH
}
#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
/* 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 */
*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);
}
}
/* 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);