diff options
-rw-r--r-- | security/integrity/Kconfig | 12 | ||||
-rw-r--r-- | security/integrity/Makefile | 1 | ||||
-rw-r--r-- | security/integrity/digsig.c | 11 | ||||
-rw-r--r-- | security/integrity/digsig_asymmetric.c | 115 | ||||
-rw-r--r-- | security/integrity/integrity.h | 12 |
5 files changed, 150 insertions, 1 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 5bd1cc1b4a54..4bb3a775a996 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -17,5 +17,17 @@ config INTEGRITY_SIGNATURE This is useful for evm and module keyrings, when keys are usually only added from initramfs. +config INTEGRITY_ASYMMETRIC_KEYS + boolean "Enable asymmetric keys support" + depends on INTEGRITY_SIGNATURE + default n + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PUBLIC_KEY_ALGO_RSA + select X509_CERTIFICATE_PARSER + help + This option enables digital signature verification using + asymmetric keys. + source security/integrity/ima/Kconfig source security/integrity/evm/Kconfig diff --git a/security/integrity/Makefile b/security/integrity/Makefile index d43799cc14f6..ebb6409b3fcb 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INTEGRITY) += integrity.o obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o +obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o integrity-y := iint.o diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 2dc167d7cde9..0b759e17a131 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -44,5 +44,14 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, } } - return digsig_verify(keyring[id], sig, siglen, digest, digestlen); + switch (sig[0]) { + case 1: + return digsig_verify(keyring[id], sig, siglen, + digest, digestlen); + case 2: + return asymmetric_verify(keyring[id], sig, siglen, + digest, digestlen); + } + + return -EOPNOTSUPP; } diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c new file mode 100644 index 000000000000..b4754667659d --- /dev/null +++ b/security/integrity/digsig_asymmetric.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Author: + * Dmitry Kasatkin <dmitry.kasatkin@intel.com> + * + * 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, version 2 of the License. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/err.h> +#include <linux/key-type.h> +#include <crypto/public_key.h> +#include <keys/asymmetric-type.h> + +#include "integrity.h" + +/* + * 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 */ +} __packed; + +/* + * Request an asymmetric key. + */ +static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) +{ + struct key *key; + char name[12]; + + sprintf(name, "id:%x", keyid); + + pr_debug("key search: \"%s\"\n", name); + + if (keyring) { + /* search in specific keyring */ + key_ref_t kref; + kref = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, name); + if (IS_ERR(kref)) + key = ERR_CAST(kref); + else + key = key_ref_to_ptr(kref); + } else { + key = request_key(&key_type_asymmetric, name, NULL); + } + + if (IS_ERR(key)) { + pr_warn("Request for unknown key '%s' err %ld\n", + name, PTR_ERR(key)); + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return key; + } + } + + pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key)); + + return key; +} + +int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen) +{ + struct public_key_signature pks; + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + struct key *key; + int ret = -ENOMEM; + + if (siglen <= sizeof(*hdr)) + return -EBADMSG; + + siglen -= sizeof(*hdr); + + if (siglen != __be16_to_cpu(hdr->sig_size)) + return -EBADMSG; + + if (hdr->hash_algo >= PKEY_HASH__LAST) + return -ENOPKG; + + key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid)); + if (IS_ERR(key)) + return PTR_ERR(key); + + memset(&pks, 0, sizeof(pks)); + + pks.pkey_hash_algo = hdr->hash_algo; + pks.digest = (u8 *)data; + pks.digest_size = datalen; + pks.nr_mpi = 1; + pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen); + + if (pks.rsa.s) + ret = verify_signature(key, &pks); + + mpi_free(pks.rsa.s); + key_put(key); + pr_debug("%s() = %d\n", __func__, ret); + return ret; +} diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 0ae08fc88585..84c37c4db914 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -14,6 +14,7 @@ #include <linux/types.h> #include <linux/integrity.h> #include <crypto/sha.h> +#include <linux/key.h> /* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -101,5 +102,16 @@ static inline int integrity_digsig_verify(const unsigned int id, #endif /* CONFIG_INTEGRITY_SIGNATURE */ +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS +int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen); +#else +static inline int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen) +{ + return -EOPNOTSUPP; +} +#endif + /* set during initialization */ extern int iint_initialized; |