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

Signed-off-by: Vivek Goyal <[email protected]>
---
 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
[email protected]
https://admin.fedoraproject.org/mailman/listinfo/kernel

Reply via email to