[Fedora kexec-tools 3/7] kexec: Verifiy kernel signature if secureboot/secure modules is enabled

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is the logic to verify bzImage signature. Signature verification
happens only if secureboot is enabled.

Signed-off-by: Vivek Goyal <vgoyal@xxxxxxxxxx>
---
 kexec/integrity-digsig.h |  26 ++++++
 kexec/kexec.c            | 231 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 257 insertions(+)
 create mode 100644 kexec/integrity-digsig.h

diff --git a/kexec/integrity-digsig.h b/kexec/integrity-digsig.h
new file mode 100644
index 0000000..c1fa306
--- /dev/null
+++ b/kexec/integrity-digsig.h
@@ -0,0 +1,26 @@
+#ifndef INTEGRITY_DIGSIG_H
+#define INTEGRITY_DIGSIG_H
+
+struct signature_hdr {
+	uint8_t version;	/* signature format version */
+	uint32_t timestamp;	/* signature made */
+	uint8_t algo;
+	uint8_t hash;
+	uint8_t keyid[8];
+	uint8_t nmpi;
+	char mpi[0];
+} __attribute__ ((packed));
+
+
+/*
+ * signature format v2 - for using with asymmetric keys
+ */
+struct signature_v2_hdr {
+	uint8_t version;	/* signature format version */
+	uint8_t hash_algo;	/* Digest algorithm [enum pkey_hash_algo] */
+	uint32_t keyid;		/* IMA key identifier - not X509/PGP specific*/
+	uint16_t sig_size;	/* signature size */
+	uint8_t sig[0];		/* signature payload */
+} __attribute__ ((packed));
+
+#endif /* INTEGRITY_DIGSIG_H */
diff --git a/kexec/kexec.c b/kexec/kexec.c
index 9187fb8..14c5d16 100644
--- a/kexec/kexec.c
+++ b/kexec/kexec.c
@@ -33,11 +33,14 @@
 #include <fcntl.h>
 #include <sys/mount.h>
 #include <sched.h>
+#include <stdbool.h>
+#include <linux/keyctl.h>
 #ifndef _O_BINARY
 #define _O_BINARY 0
 #endif
 #include <getopt.h>
 #include <ctype.h>
+#include <attr/xattr.h>
 
 #include "config.h"
 
@@ -48,6 +51,7 @@
 #include "kexec-sha256.h"
 #include "kexec-zlib.h"
 #include "kexec-lzma.h"
+#include "integrity-digsig.h"
 #include <arch/options.h>
 
 unsigned long long mem_min = 0;
@@ -640,6 +644,197 @@ static void update_purgatory(struct kexec_info *info)
 			   sizeof(digest));
 }
 
+static char *get_file_signature(char *filename, off_t *r_size)
+{
+	int fd;
+	char *buf = NULL, *sig;
+	int sig_sz;
+
+	fd = open(filename, O_RDONLY);
+	if (fd == -1) {
+		fprintf(stderr, "Open of file %s failed:%s\n", filename,
+				strerror(errno));
+		return NULL;
+	}
+
+	/* Get signature of file */
+	sig_sz = fgetxattr(fd, "security.ima", NULL, 0);
+	if (sig_sz == -1) {
+		fprintf(stderr, "fgetattr() failed:%s\n", strerror(errno));
+		goto out_close_fd;
+        }
+
+	sig = malloc(sig_sz);
+	if (sig == NULL) {
+		fprintf(stderr, "malloc(%d) failed:%s\n", sig_sz,
+					strerror(errno));
+		goto out_close_fd;
+        }
+
+	sig_sz = fgetxattr(fd, "security.ima", sig, sig_sz);
+	if (sig_sz == -1) {
+		fprintf(stderr, "fgetattr() failed:%s\n", strerror(errno));
+		free(sig);
+		goto out_close_fd;
+        }
+
+	buf = sig;
+	*r_size = sig_sz;
+
+out_close_fd:
+	close(fd);
+	return buf;
+}
+
+/*
+ * It is assumed signatures are stored in security.ima xattr. buf and size
+ * contain the contents of file whose signature need to be verified
+ */
+static int verify_signature(unsigned long keyring_id, char *data, off_t dlen,
+				char *sig, off_t slen)
+{
+	int ret = 0;
+	struct keyctl_sig_data sig_data;
+
+	sig_data.data = data;
+	sig_data.datalen = dlen;
+	sig_data.sig = sig;
+	sig_data.siglen = slen,
+	sig_data.sig_type = 1;
+	sig_data.keyring_id = keyring_id;
+	sig_data.flags = 0;
+
+	ret = syscall(__NR_keyctl, KEYCTL_VERIFY_SIGNATURE, &sig_data);
+
+	if (ret) {
+		fprintf(stderr, "keyctl() failed. ret= %d:%s\n", ret,
+				strerror(errno));
+	}
+
+	return ret;
+}
+
+/*
+ * Ask running kernel to see if it needs /sbin/kexec to verify new kernel's
+ * signature.
+ */
+static bool is_secureboot_enabled(void) {
+	int fd, ret;
+	char value = 0;
+
+	fd = open("/sys/kernel/secureboot_enabled", O_RDONLY);
+	if (fd == -1) {
+		/* For backward compatibility with old kernels */
+		return false;
+	}
+
+	ret = read(fd, &value, sizeof(value));
+	if (ret < 0) {
+		die("Failed to read /sys/kernel/secureboot_enabled");
+	}
+
+	if (value == '1')
+		return true;
+	else
+		return false;
+}
+
+static bool is_secure_modules_enabled(void) {
+	int fd, ret;
+	char value = 0;
+
+	fd = open("/sys/kernel/secure_modules_enabled", O_RDONLY);
+	if (fd == -1) {
+		/* For backward compatibility with old kernels */
+		return false;
+	}
+
+	ret = read(fd, &value, sizeof(value));
+	if (ret < 0) {
+		die("Failed to read /sys/kernel/secure_modules_enabled");
+	}
+
+	if (value == '1')
+		return true;
+	else
+		return false;
+}
+
+static bool should_verify_kernel_sig(void) {
+
+	return is_secureboot_enabled() || is_secure_modules_enabled();
+}
+
+int get_system_keyring_id(unsigned long *keyring_id)
+{
+	FILE *fp;
+	char buf[256];
+
+	fp = fopen("/proc/keys", "r");
+	if (!fp) {
+		fprintf(stderr, "Can not open /proc/keys:%s", strerror(errno));
+		return 1;
+	}
+
+	while(fgets(buf, sizeof(buf), fp)) {
+		int field_idx = 0;
+		char *token, *str;
+		str = buf;
+		unsigned long keyid = 0;
+
+		while ((token = strtok(str, " "))) {
+			field_idx++;
+			str = NULL;
+
+			if (field_idx == 1)
+				keyid = strtol(token, NULL, 16);
+
+			/* second field is key flags. If "Q" is set, ignore
+			 * key. We are looking for kernel keyring which
+			 * does not contribute towards quota
+			 */
+			if (field_idx == 2) {
+				if (strchr(token, 'Q'))
+					break;
+			}
+
+			/* 9 the field contains the name of keyring */
+			if (field_idx == 9) {
+				if (strstr(token, ".system_keyring")) {
+					*keyring_id = keyid;
+					return 0;
+				}
+			}
+		}
+	}
+
+	return 1;
+}
+
+/* TODO: Move signature format specific handling into a separate file */
+static int get_integrity_digsig_len(char *sig)
+{
+	uint16_t sz;
+
+	if (sig[0] == 1) {
+		sz = *(uint16_t *)(sig + sizeof(struct signature_hdr));
+		sz = be16_to_cpu(sz) >> 3;
+		return sizeof(struct signature_hdr) + 2 + sz;
+	} else if (sig[0] == 2) {
+		sz = ((struct signature_v2_hdr *)sig)->sig_size;
+		sz = be16_to_cpu(sz);
+		return sizeof(struct signature_v2_hdr) + sz;
+	}
+
+	return -1;
+}
+
+static char *get_integrity_signature(char *xattr)
+{
+	/* ima/evm uses first byte to store data about type of signature */
+	return ++xattr;
+}
+
 /*
  *	Load the new kernel
  */
@@ -654,6 +849,9 @@ static int my_load(const char *type, int fileind, int argc, char **argv,
 	struct kexec_info info;
 	long native_arch;
 	int guess_only = 0;
+	char *xattr_buf, *sig_ptr;
+	off_t xattr_sz, sig_sz;
+	unsigned long keyring_id;
 
 	memset(&info, 0, sizeof(info));
 	info.segment = NULL;
@@ -675,6 +873,39 @@ static int my_load(const char *type, int fileind, int argc, char **argv,
 	dbgprintf("kernel: %p kernel_size: %lx\n",
 		  kernel_buf, kernel_size);
 
+	if (should_verify_kernel_sig()) {
+		/* Verify kernel signature */
+		xattr_buf = get_file_signature(kernel, &xattr_sz);
+
+		if (!xattr_buf) {
+			fprintf(stderr, "Could not find signature of file %s\n",
+					kernel);
+			return -1;
+		}
+
+		sig_ptr = get_integrity_signature(xattr_buf);
+		sig_sz = get_integrity_digsig_len(sig_ptr);
+		if (sig_sz < 0) {
+			fprintf(stderr, "Can not get digital signature size\n");
+			return -1;
+		}
+
+		result = get_system_keyring_id(&keyring_id);
+		if (result) {
+			fprintf(stderr, "Can not get id for system keyring\n");
+			return -1;
+		}
+
+		result = verify_signature(keyring_id, kernel_buf, kernel_size,
+					sig_ptr, sig_sz);
+
+		if (result) {
+			fprintf(stderr, "Signature verification failed for %s\n",
+					kernel);
+			return -1;
+		}
+	}
+
 	if (get_memory_ranges(&info.memory_range, &info.memory_ranges,
 		info.kexec_flags) < 0 || info.memory_ranges == 0) {
 		fprintf(stderr, "Could not get memory layout\n");
-- 
1.8.3.1

_______________________________________________
kernel mailing list
kernel@xxxxxxxxxxxxxxxxxxxxxxx
https://admin.fedoraproject.org/mailman/listinfo/kernel





[Index of Archives]     [Fedora General Discussion]     [Older Fedora Users Archive]     [Fedora Advisory Board]     [Fedora Security]     [Fedora Devel Java]     [Fedora Legacy]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Mentors]     [Fedora Package Announce]     [Fedora Package Review]     [Fedora Music]     [Fedora Packaging]     [Centos]     [Fedora SELinux]     [Coolkey]     [Yum Users]     [Tux]     [Yosemite News]     [KDE Users]     [Fedora Art]     [Fedora Docs]     [USB]     [Asterisk PBX]

  Powered by Linux