Skip to contentSkip to conversation

ima-evm-utils: Extract digest algorithms from hash_info.h

PATCH In review Vitaly Chikunov 2 participants 5 messages Nov 29, 2018 – Dec 2, 2018 Keys-trusted

lore
Share & tools
lore
#0 lore raw

PATCH 6/7 ima-evm-utils: Extract digest algorithms from hash_info.h

If configured with "--with-kernel-headers=PATH" try to extract hash algorithms from "hash_info.h" from the kernel source tree or kernel-headers package located in the specified path. (Otherwise, it will be tried to get from the installed kernel.)

This also introduces two algorithm lists, one is built-in and another is from the kernel source. (They should never contain conflicting algorithm IDs by their append-only nature.) If the digest is not found in the built-in list it will be searched in the list from kernel's "hash_info.h".

This patch will allow evmctl to be just recompiled to work with digest algorithms introduced in the newer kernels.

---
Changes since v1:
- New patch.
Changes since v2:
- Mark PATH portion of "--with-kernel-headers=PATH" non-optional and change description to reflect correct behavior.

 configure.ac      |  6 ++++++
 src/Makefile.am   |  6 ++++++
 src/hash_info.gen | 43 +++++++++++++++++++++++++++++++++++++++++++
 src/libimaevm.c   | 44 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100755 src/hash_info.gen
configure.ac +6 -0
index a5b4288..60f3684 100644
@@ -27,12 +27,18 @@ AC_HEADER_STDC
 PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ])
 AC_SUBST(OPENSSL_CFLAGS)
 AC_SUBST(OPENSSL_LIBS)
+AC_SUBST(KERNEL_HEADERS)
 AC_CHECK_HEADER(unistd.h)
 AC_CHECK_HEADERS(openssl/conf.h)
 
 AC_CHECK_HEADERS(sys/xattr.h, , [AC_MSG_ERROR([sys/xattr.h header not found. You need the c-library development package.])])
 AC_CHECK_HEADERS(keyutils.h, , [AC_MSG_ERROR([keyutils.h header not found. You need the libkeyutils development package.])])
 
+AC_ARG_WITH(kernel_headers, [AS_HELP_STRING([--with-kernel-headers=PATH],
+	    [specifies the Linux kernel-headers package location or kernel root directory you want to use])],
+	    [KERNEL_HEADERS="$withval"],
+	    [KERNEL_HEADERS=/lib/modules/$(uname -r)/source])
+
 #debug support - yes for a while
 PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support])
 if test $pkg_cv_enable_debug = yes; then
src/Makefile.am +6 -0
index deb18fb..d74fc6f 100644
@@ -9,6 +9,11 @@ libimaevm_la_LIBADD =  $(OPENSSL_LIBS)
 
 include_HEADERS = imaevm.h
 
+nodist_libimaevm_la_SOURCES = hash_info.h
+BUILT_SOURCES = hash_info.h
+hash_info.h: Makefile
+	./hash_info.gen $(KERNEL_HEADERS) >$@
+
 bin_PROGRAMS = evmctl
 
 evmctl_SOURCES = evmctl.c
@@ -18,5 +23,6 @@ evmctl_LDADD =  $(OPENSSL_LIBS) -lkeyutils libimaevm.la
 
 INCLUDES = -I$(top_srcdir) -include config.h
 
+CLEANFILES = hash_info.h
 DISTCLEANFILES = @DISTCLEANFILES@
 
src/hash_info.gen new file +43 -0

43 lines not on this page · raw message

src/libimaevm.c +43 -1

83 lines not on this page · raw message

Signature · 1 line
2.11.0
maintainer
#1 lore raw
Quoted 14 lines · Vitaly Chikunov

If configured with "--with-kernel-headers=PATH" try to extract hash algorithms from "hash_info.h" from the kernel source tree or kernel-headers package located in the specified path. (Otherwise, it will be tried to get from the installed kernel.)

This also introduces two algorithm lists, one is built-in and another is from the kernel source. (They should never contain conflicting algorithm IDs by their append-only nature.) If the digest is not found in the built-in list it will be searched in the list from kernel's "hash_info.h".

This patch will allow evmctl to be just recompiled to work with digest algorithms introduced in the newer kernels.

Quoted 8 lines

---
Changes since v1:
- New patch.
Changes since v2:
- Mark PATH portion of "--with-kernel-headers=PATH" non-optional and change description to reflect correct behavior.

Defaulting to the currently running kernel build tree would have been nice.

Quoted 93 lines
 configure.ac      |  6 ++++++
 src/Makefile.am   |  6 ++++++
 src/hash_info.gen | 43 +++++++++++++++++++++++++++++++++++++++++++
 src/libimaevm.c   | 44 +++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100755 src/hash_info.gen
configure.ac +6 -0
index a5b4288..60f3684 100644
@@ -27,12 +27,18 @@ AC_HEADER_STDC
 PKG_CHECK_MODULES(OPENSSL, [ openssl >= 0.9.8 ])
 AC_SUBST(OPENSSL_CFLAGS)
 AC_SUBST(OPENSSL_LIBS)
+AC_SUBST(KERNEL_HEADERS)
 AC_CHECK_HEADER(unistd.h)
 AC_CHECK_HEADERS(openssl/conf.h)
 
 AC_CHECK_HEADERS(sys/xattr.h, , [AC_MSG_ERROR([sys/xattr.h header not found. You need the c-library development package.])])
 AC_CHECK_HEADERS(keyutils.h, , [AC_MSG_ERROR([keyutils.h header not found. You need the libkeyutils development package.])])
 
+AC_ARG_WITH(kernel_headers, [AS_HELP_STRING([--with-kernel-headers=PATH],
+	    [specifies the Linux kernel-headers package location or kernel root directory you want to use])],
+	    [KERNEL_HEADERS="$withval"],
+	    [KERNEL_HEADERS=/lib/modules/$(uname -r)/source])
+
 #debug support - yes for a while
 PKG_ARG_ENABLE(debug, "yes", DEBUG, [Enable Debug support])
 if test $pkg_cv_enable_debug = yes; then
src/Makefile.am +6 -0
index deb18fb..d74fc6f 100644
@@ -9,6 +9,11 @@ libimaevm_la_LIBADD =  $(OPENSSL_LIBS)
 
 include_HEADERS = imaevm.h
 
+nodist_libimaevm_la_SOURCES = hash_info.h
+BUILT_SOURCES = hash_info.h
+hash_info.h: Makefile
+	./hash_info.gen $(KERNEL_HEADERS) >$@
+
 bin_PROGRAMS = evmctl
 
 evmctl_SOURCES = evmctl.c
@@ -18,5 +23,6 @@ evmctl_LDADD =  $(OPENSSL_LIBS) -lkeyutils libimaevm.la
 
 INCLUDES = -I$(top_srcdir) -include config.h
 
+CLEANFILES = hash_info.h
 DISTCLEANFILES = @DISTCLEANFILES@
 
src/hash_info.gen new file +33 -0
new file mode 100755
index 0000000..60fc750
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Generate hash_info.h from kernel headers
+#
+# Copyright (C) 2018 <[email protected]>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+KERNEL_HEADERS=$1
+HASH_INFO_H=uapi/linux/hash_info.h
+HASH_INFO=$KERNEL_HEADERS/include/$HASH_INFO_H
+
+# Allow to specify kernel-headers past include/
+if [ ! -e $HASH_INFO ]; then
+  HASH_INFO2=$KERNEL_HEADERS/$HASH_INFO_H
+  if [ -e $HASH_INFO2 ]; then
+    HASH_INFO=$HASH_INFO2
+  fi
+fi
+
+if [ ! -e $HASH_INFO ]; then
+  echo "/* $HASH_INFO is not found */"
+  HASH_INFO=/dev/null
+else
+  echo "/* $HASH_INFO is found */"

src/hash_info.gen line 43

Quoted 10 lines
src/hash_info.gen new file +10 -0
+fi
+
+echo "enum hash_algo {"
+grep HASH_ALGO_.*, $HASH_INFO
+printf "\tHASH_ALGO__LAST\n"
+echo "};"
+
+echo "const char *const hash_algo_name[HASH_ALGO__LAST] = {"
+sed -n 's/HASH_ALGO_\(.*\),/[HASH_ALGO_\1] = "\L\1\E",/p' $HASH_INFO
+echo "};"

Almost perfectly matches crypto/hash_crypto.c!  Waiting to see if/how the next patch addresses the differences...

Quoted 68 lines
src/libimaevm.c +34 -0
index 7b2b62c..cb4721b 100644
@@ -50,6 +50,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <assert.h>
+#include <ctype.h>
 
 #include <openssl/crypto.h>
 #include <openssl/pem.h>
@@ -58,6 +59,7 @@
 #include <openssl/err.h>
 
 #include "imaevm.h"
+#include "hash_info.h"
 
 const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
 	[PKEY_HASH_MD4]		= "md4",
@@ -153,6 +155,17 @@ void dump(const void *ptr, int len)
 	do_dump(stdout, ptr, len, true);
 }
 
+const char *get_hash_algo_by_id(int algo)
+{
+	if (algo < PKEY_HASH__LAST)
+	    return pkey_hash_algo[algo];
+	if (algo < HASH_ALGO__LAST)
+	    return hash_algo_name[algo];
+
+	log_err("digest %d not found\n", algo);
+	return "unknown";
+}
+
 int get_filesize(const char *filename)
 {
 	struct stat stats;
@@ -528,15 +541,44 @@ int verify_hash_v2(const char *file, const unsigned char *hash, int size,
 	return 0;
 }
 
+/* compare algo names case insensitively and ignoring separators */
+static int algocmp(const char *a, const char *b)
+{
+	while (*a && *b) {
+		int cha, chb;
+
+		cha = tolower((unsigned char)*a++);
+		if (!isalnum(cha))
+			continue;
+		chb = tolower((unsigned char)*b++);
+		if (!isalnum(chb)) {
+			a--;
+			continue;
+		}
+		if (cha != chb)
+			return -1;
+	}
+	return *a || *b;
+}
+
 int get_hash_algo(const char *algo)
 {
 	int i;
 
+	/* first iterate over builtin algorithms */
 	for (i = 0; i < PKEY_HASH__LAST; i++)

src/libimaevm.c int verify_hash_v2(const char *file, const unsigned char *hash, int size, · line 579

Quoted 10 lines
src/libimaevm.c +6 -0
 		if (pkey_hash_algo[i] &&
 		    !strcmp(algo, pkey_hash_algo[i]))
 			return i;
 
+	/* iterate over algorithms provided by kernel-headers */
+	for (i = 0; i < HASH_ALGO__LAST; i++) {
+		if (hash_algo_name[i] &&
+		    !algocmp(algo, hash_algo_name[i]))
+			return i;
+	}

Assuming the two lists are in sync, which they should be, "i" could be set to PKEY_HASH__LAST.

Mimi

Quoted 14 lines
src/libimaevm.c +3 -1
+
+	log_info("digest %s not found, fall back to sha1\n", algo);
 	return PKEY_HASH_SHA1;
 }
 
@@ -611,7 +653,7 @@ int ima_verify_signature(const char *file, unsigned char *sig, int siglen,
 		return -1;
 	}
 	/* Use hash algorithm as retrieved from signature */
-	params.hash_algo = pkey_hash_algo[sig_hash_algo];
+	params.hash_algo = get_hash_algo_by_id(sig_hash_algo);
 
 	/*
 	 * Validate the signature based on the digest included in the
maintainer
#2 lore raw
Quoted 23 lines · Mimi Zohar
Quoted 14 lines · Vitaly Chikunov

If configured with "--with-kernel-headers=PATH" try to extract hash algorithms from "hash_info.h" from the kernel source tree or kernel-headers package located in the specified path. (Otherwise, it will be tried to get from the installed kernel.)

This also introduces two algorithm lists, one is built-in and another is from the kernel source. (They should never contain conflicting algorithm IDs by their append-only nature.) If the digest is not found in the built-in list it will be searched in the list from kernel's "hash_info.h".

This patch will allow evmctl to be just recompiled to work with digest algorithms introduced in the newer kernels.

Quoted 8 lines

---
Changes since v1:
- New patch.
Changes since v2:
- Mark PATH portion of "--with-kernel-headers=PATH" non-optional and change description to reflect correct behavior.

Quoted 2 lines

Defaulting to the currently running kernel build tree would have been nice.

Then you could extract "hash_algo_name[]" from crypto/hash_info.c directly.

Mimi

#3 lore raw
In reply to Mimi Zohar
Quoted 24 lines · Mimi Zohar
Quoted 14 lines · Vitaly Chikunov

If configured with "--with-kernel-headers=PATH" try to extract hash algorithms from "hash_info.h" from the kernel source tree or kernel-headers package located in the specified path. (Otherwise, it will be tried to get from the installed kernel.)

This also introduces two algorithm lists, one is built-in and another is from the kernel source. (They should never contain conflicting algorithm IDs by their append-only nature.) If the digest is not found in the built-in list it will be searched in the list from kernel's "hash_info.h".

This patch will allow evmctl to be just recompiled to work with digest algorithms introduced in the newer kernels.

Quoted 8 lines

---
Changes since v1:
- New patch.
Changes since v2:
- Mark PATH portion of "--with-kernel-headers=PATH" non-optional and change description to reflect correct behavior.

Quoted 2 lines

Defaulting to the currently running kernel build tree would have been nice.

It already does. As description states "(Otherwise, it will be tried to get from the installed kernel.)" Also:

Quoted 4 lines

configure.ac AC_HEADER_STDC · line 40

Quoted 4 lines
configure.ac +4 -0
+AC_ARG_WITH(kernel_headers, [AS_HELP_STRING([--with-kernel-headers=PATH],
+	    [specifies the Linux kernel-headers package location or kernel root directory you want to use])],
+	    [KERNEL_HEADERS="$withval"],
+	    [KERNEL_HEADERS=/lib/modules/$(uname -r)/source])

if "--with-kernel-headers" is not specified it will default to /lib/modules/$(uname -r)/source which points to the kernel-headers or kernel source tree root.

Quoted 2 lines

Then you could extract "hash_algo_name[]" from crypto/hash_info.c directly.

I could add this, BUT this will mean that developer with kernel tree (very small amount of users) will have slightly different algos list than a common person with a kernel-headers package. And I don't know a single distro which packs full kernel source (with crypto/hash_info.c) as easy to install package, and which would link appropriately into /lib/modules/. So this is highly doubtful that user will be inclined to install kernel source via usually complicated procedure just to compile evmctl.

I already tried to address this possible difference issue by algocmp() which would not compare _ and - chars, which is the only difference from hash_algo_name in the kernel and in ima-evm-utils and OpenSSL. So, even without this feature (of parsing crypto/hash_info.c) added, user already can specify any algo from crypto/hash_info.c and it will just work.

Quoted 11 lines

src/hash_info.gen line 43

Quoted 8 lines
src/hash_info.gen new file +8 -0
+echo "enum hash_algo {"
+grep HASH_ALGO_.*, $HASH_INFO
+printf "\tHASH_ALGO__LAST\n"
+echo "};"
+
+echo "const char *const hash_algo_name[HASH_ALGO__LAST] = {"
+sed -n 's/HASH_ALGO_\(.*\),/[HASH_ALGO_\1] = "\L\1\E",/p' $HASH_INFO
+echo "};"

Almost perfectly matches crypto/hash_crypto.c!  Waiting to see if/how the next patch addresses the differences...

Could you elaborate again on what I should do with this suggestion? I guessed crypto/hash_crypto.c is crypto/hash_info.c, but what differences you expecting?

Quoted 6 lines
Quoted 6 lines
src/libimaevm.c +1 -0
+	/* first iterate over builtin algorithms */
 	for (i = 0; i < PKEY_HASH__LAST; i++)
 		if (pkey_hash_algo[i] &&
 		    !strcmp(algo, pkey_hash_algo[i]))
 			return i;
Quoted 9 lines

src/libimaevm.c int verify_hash_v2(const char *file, const unsigned char *hash, int size, · line 579

Quoted 6 lines
src/libimaevm.c +6 -0
+	/* iterate over algorithms provided by kernel-headers */
+	for (i = 0; i < HASH_ALGO__LAST; i++) {
+		if (hash_algo_name[i] &&
+		    !algocmp(algo, hash_algo_name[i]))
+			return i;
+	}

Assuming the two lists are in sync, which they should be, "i" could be set to PKEY_HASH__LAST.

Can not do that, since pkey_hash_algo is currently sparse array and could skip entries which are present in hash_algo_name.

Note, that since "[PATCH v2 7/7] ima-evm-utils: Try to load digest by its alias" lists will have different semantics: the pkey_hash_algo list is compared algo names with strmatch which supports algo aliases, and the hash_algo_name list is compared using algocmp which does not compare non-alphanumeric chars such as _ and -, so streebog-512, streebog_512 or just streebog512 all would work (to match algo id).

Concluding, if you would not insist on parsing crypto/hash_info.c, I don't need fixing anything in this patch.

Thanks,

maintainer
#4 lore raw
Quoted 31 lines · Vitaly Chikunov
Quoted 24 lines · Mimi Zohar
Quoted 14 lines · Vitaly Chikunov

If configured with "--with-kernel-headers=PATH" try to extract hash algorithms from "hash_info.h" from the kernel source tree or kernel-headers package located in the specified path. (Otherwise, it will be tried to get from the installed kernel.)

This also introduces two algorithm lists, one is built-in and another is from the kernel source. (They should never contain conflicting algorithm IDs by their append-only nature.) If the digest is not found in the built-in list it will be searched in the list from kernel's "hash_info.h".

This patch will allow evmctl to be just recompiled to work with digest algorithms introduced in the newer kernels.

Quoted 8 lines

---
Changes since v1:
- New patch.
Changes since v2:
- Mark PATH portion of "--with-kernel-headers=PATH" non-optional and change description to reflect correct behavior.

Quoted 2 lines

Defaulting to the currently running kernel build tree would have been nice.

It already does. As description states "(Otherwise, it will be tried to get from the installed kernel.)" Also:

Quoted 8 lines
Quoted 4 lines

configure.ac AC_HEADER_STDC · line 40

Quoted 4 lines
configure.ac +4 -0
+AC_ARG_WITH(kernel_headers, [AS_HELP_STRING([--with-kernel-headers=PATH],
+	    [specifies the Linux kernel-headers package location or kernel root directory you want to use])],
+	    [KERNEL_HEADERS="$withval"],
+	    [KERNEL_HEADERS=/lib/modules/$(uname -r)/source])

if "--with-kernel-headers" is not specified it will default to /lib/modules/$(uname -r)/source which points to the kernel-headers or kernel source tree root.

I missed that.

<snip>

Quoted 16 lines
Quoted 6 lines
Quoted 6 lines
src/libimaevm.c +1 -0
+	/* first iterate over builtin algorithms */
 	for (i = 0; i < PKEY_HASH__LAST; i++)
 		if (pkey_hash_algo[i] &&
 		    !strcmp(algo, pkey_hash_algo[i]))
 			return i;
Quoted 9 lines

src/libimaevm.c int verify_hash_v2(const char *file, const unsigned char *hash, int size, · line 579

Quoted 6 lines
src/libimaevm.c +6 -0
+	/* iterate over algorithms provided by kernel-headers */
+	for (i = 0; i < HASH_ALGO__LAST; i++) {
+		if (hash_algo_name[i] &&
+		    !algocmp(algo, hash_algo_name[i]))
+			return i;
+	}

Assuming the two lists are in sync, which they should be, "i" could be set to PKEY_HASH__LAST.

Quoted 2 lines

Can not do that, since pkey_hash_algo is currently sparse array and could skip entries which are present in hash_algo_name.

Missed that too.

Quoted 10 lines

Note, that since "[PATCH v2 7/7] ima-evm-utils: Try to load digest by its alias" lists will have different semantics: the pkey_hash_algo list is compared algo names with strmatch which supports algo aliases, and the hash_algo_name list is compared using algocmp which does not compare non-alphanumeric chars such as _ and -, so streebog-512, streebog_512 or just streebog512 all would work (to match algo id).

Concluding, if you would not insist on parsing crypto/hash_info.c, I don't need fixing anything in this patch.

Agreed, the patch looks fine as is.

Mimi