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