Commit e0751257a64ea10cca96ccb06522bfb10e36cb5b

Authored by Dmitry Kasatkin
Committed by Mimi Zohar
1 parent 50af554466

ima: digital signature verification using asymmetric keys

Asymmetric keys were introduced in linux-3.7 to verify the signature on
signed kernel modules. The asymmetric keys infrastructure abstracts the
signature verification from the crypto details. This patch adds IMA/EVM
signature verification using asymmetric keys. Support for additional
signature verification methods can now be delegated to the asymmetric
key infrastructure.

Although the module signature header and the IMA/EVM signature header
could use the same format, to minimize the signature length and save
space in the extended attribute, this patch defines a new IMA/EVM
header format.  The main difference is that the key identifier is a
sha1[12 - 19] hash of the key modulus and exponent, similar to the
current implementation.  The only purpose of the key identifier is to
identify the corresponding key in the kernel keyring.  ima-evm-utils
was updated to support the new signature format.

While asymmetric signature verification functionality supports many
different hash algorithms, the hash used in this patch is calculated
during the IMA collection phase, based on the configured algorithm.
The default algorithm is sha1, but for backwards compatibility md5
is supported.  Due to this current limitation, signatures should be
generated using a sha1 hash algorithm.

Changes in this patch:
- Functionality has been moved to separate source file in order to get rid of
  in source #ifdefs.
- keyid is derived according to the RFC 3280. It does not require to assign
  IMA/EVM specific "description" when loading X509 certificate. Kernel
  asymmetric key subsystem automatically generate the description. Also
  loading a certificate does not require using of ima-evm-utils and can be
  done using keyctl only.
- keyid size is reduced to 32 bits to save xattr space.  Key search is done
  using partial match functionality of asymmetric_key_match().
- Kconfig option title was changed

Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@intel.com>
Acked-by: David Howells <dhowells@redhat.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>

Showing 5 changed files with 150 additions and 1 deletions Side-by-side Diff

security/integrity/Kconfig
... ... @@ -17,6 +17,18 @@
17 17 This is useful for evm and module keyrings, when keys are
18 18 usually only added from initramfs.
19 19  
  20 +config INTEGRITY_ASYMMETRIC_KEYS
  21 + boolean "Enable asymmetric keys support"
  22 + depends on INTEGRITY_SIGNATURE
  23 + default n
  24 + select ASYMMETRIC_KEY_TYPE
  25 + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
  26 + select PUBLIC_KEY_ALGO_RSA
  27 + select X509_CERTIFICATE_PARSER
  28 + help
  29 + This option enables digital signature verification using
  30 + asymmetric keys.
  31 +
20 32 source security/integrity/ima/Kconfig
21 33 source security/integrity/evm/Kconfig
security/integrity/Makefile
... ... @@ -4,6 +4,7 @@
4 4  
5 5 obj-$(CONFIG_INTEGRITY) += integrity.o
6 6 obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
  7 +obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
7 8  
8 9 integrity-y := iint.o
9 10  
security/integrity/digsig.c
... ... @@ -44,6 +44,15 @@
44 44 }
45 45 }
46 46  
47   - return digsig_verify(keyring[id], sig, siglen, digest, digestlen);
  47 + switch (sig[0]) {
  48 + case 1:
  49 + return digsig_verify(keyring[id], sig, siglen,
  50 + digest, digestlen);
  51 + case 2:
  52 + return asymmetric_verify(keyring[id], sig, siglen,
  53 + digest, digestlen);
  54 + }
  55 +
  56 + return -EOPNOTSUPP;
48 57 }
security/integrity/digsig_asymmetric.c
  1 +/*
  2 + * Copyright (C) 2013 Intel Corporation
  3 + *
  4 + * Author:
  5 + * Dmitry Kasatkin <dmitry.kasatkin@intel.com>
  6 + *
  7 + * This program is free software; you can redistribute it and/or modify
  8 + * it under the terms of the GNU General Public License as published by
  9 + * the Free Software Foundation, version 2 of the License.
  10 + *
  11 + */
  12 +
  13 +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14 +
  15 +#include <linux/err.h>
  16 +#include <linux/key-type.h>
  17 +#include <crypto/public_key.h>
  18 +#include <keys/asymmetric-type.h>
  19 +
  20 +#include "integrity.h"
  21 +
  22 +/*
  23 + * signature format v2 - for using with asymmetric keys
  24 + */
  25 +struct signature_v2_hdr {
  26 + uint8_t version; /* signature format version */
  27 + uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */
  28 + uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/
  29 + uint16_t sig_size; /* signature size */
  30 + uint8_t sig[0]; /* signature payload */
  31 +} __packed;
  32 +
  33 +/*
  34 + * Request an asymmetric key.
  35 + */
  36 +static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
  37 +{
  38 + struct key *key;
  39 + char name[12];
  40 +
  41 + sprintf(name, "id:%x", keyid);
  42 +
  43 + pr_debug("key search: \"%s\"\n", name);
  44 +
  45 + if (keyring) {
  46 + /* search in specific keyring */
  47 + key_ref_t kref;
  48 + kref = keyring_search(make_key_ref(keyring, 1),
  49 + &key_type_asymmetric, name);
  50 + if (IS_ERR(kref))
  51 + key = ERR_CAST(kref);
  52 + else
  53 + key = key_ref_to_ptr(kref);
  54 + } else {
  55 + key = request_key(&key_type_asymmetric, name, NULL);
  56 + }
  57 +
  58 + if (IS_ERR(key)) {
  59 + pr_warn("Request for unknown key '%s' err %ld\n",
  60 + name, PTR_ERR(key));
  61 + switch (PTR_ERR(key)) {
  62 + /* Hide some search errors */
  63 + case -EACCES:
  64 + case -ENOTDIR:
  65 + case -EAGAIN:
  66 + return ERR_PTR(-ENOKEY);
  67 + default:
  68 + return key;
  69 + }
  70 + }
  71 +
  72 + pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key));
  73 +
  74 + return key;
  75 +}
  76 +
  77 +int asymmetric_verify(struct key *keyring, const char *sig,
  78 + int siglen, const char *data, int datalen)
  79 +{
  80 + struct public_key_signature pks;
  81 + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
  82 + struct key *key;
  83 + int ret = -ENOMEM;
  84 +
  85 + if (siglen <= sizeof(*hdr))
  86 + return -EBADMSG;
  87 +
  88 + siglen -= sizeof(*hdr);
  89 +
  90 + if (siglen != __be16_to_cpu(hdr->sig_size))
  91 + return -EBADMSG;
  92 +
  93 + if (hdr->hash_algo >= PKEY_HASH__LAST)
  94 + return -ENOPKG;
  95 +
  96 + key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid));
  97 + if (IS_ERR(key))
  98 + return PTR_ERR(key);
  99 +
  100 + memset(&pks, 0, sizeof(pks));
  101 +
  102 + pks.pkey_hash_algo = hdr->hash_algo;
  103 + pks.digest = (u8 *)data;
  104 + pks.digest_size = datalen;
  105 + pks.nr_mpi = 1;
  106 + pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen);
  107 +
  108 + if (pks.rsa.s)
  109 + ret = verify_signature(key, &pks);
  110 +
  111 + mpi_free(pks.rsa.s);
  112 + key_put(key);
  113 + pr_debug("%s() = %d\n", __func__, ret);
  114 + return ret;
  115 +}
security/integrity/integrity.h
... ... @@ -14,6 +14,7 @@
14 14 #include <linux/types.h>
15 15 #include <linux/integrity.h>
16 16 #include <crypto/sha.h>
  17 +#include <linux/key.h>
17 18  
18 19 /* iint action cache flags */
19 20 #define IMA_MEASURE 0x00000001
... ... @@ -100,6 +101,17 @@
100 101 }
101 102  
102 103 #endif /* CONFIG_INTEGRITY_SIGNATURE */
  104 +
  105 +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
  106 +int asymmetric_verify(struct key *keyring, const char *sig,
  107 + int siglen, const char *data, int datalen);
  108 +#else
  109 +static inline int asymmetric_verify(struct key *keyring, const char *sig,
  110 + int siglen, const char *data, int datalen)
  111 +{
  112 + return -EOPNOTSUPP;
  113 +}
  114 +#endif
103 115  
104 116 /* set during initialization */
105 117 extern int iint_initialized;