This is the logic to verify bzImage signature. Signature verification
happens only if secureboot is enabled.
Signed-off-by: Vivek Goyal <vgoyal(a)redhat.com>
---
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