* [PATCH 01/23] KEYS: Rename public key parameter name arrays
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
@ 2012-10-30 19:19 ` David Howells
2012-10-30 19:19 ` [PATCH 02/23] KEYS: Move the algorithm pointer array from x509 to public_key.c David Howells
` (22 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:19 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Rename the arrays of public key parameters (public key algorithm names, hash
algorithm names and ID type names) so that the array name ends in "_name".
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/public_key.c | 14 +++++++-------
crypto/asymmetric_keys/x509_public_key.c | 8 ++++----
include/crypto/public_key.h | 6 +++---
kernel/module_signing.c | 4 ++--
4 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index cb2e291..b313df1 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -22,13 +22,13 @@
MODULE_LICENSE("GPL");
-const char *const pkey_algo[PKEY_ALGO__LAST] = {
+const char *const pkey_algo_name[PKEY_ALGO__LAST] = {
[PKEY_ALGO_DSA] = "DSA",
[PKEY_ALGO_RSA] = "RSA",
};
-EXPORT_SYMBOL_GPL(pkey_algo);
+EXPORT_SYMBOL_GPL(pkey_algo_name);
-const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
+const char *const pkey_hash_algo_name[PKEY_HASH__LAST] = {
[PKEY_HASH_MD4] = "md4",
[PKEY_HASH_MD5] = "md5",
[PKEY_HASH_SHA1] = "sha1",
@@ -38,13 +38,13 @@ const char *const pkey_hash_algo[PKEY_HASH__LAST] = {
[PKEY_HASH_SHA512] = "sha512",
[PKEY_HASH_SHA224] = "sha224",
};
-EXPORT_SYMBOL_GPL(pkey_hash_algo);
+EXPORT_SYMBOL_GPL(pkey_hash_algo_name);
-const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = {
+const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = {
[PKEY_ID_PGP] = "PGP",
[PKEY_ID_X509] = "X509",
};
-EXPORT_SYMBOL_GPL(pkey_id_type);
+EXPORT_SYMBOL_GPL(pkey_id_type_name);
/*
* Provide a part of a description of the key for /proc/keys.
@@ -56,7 +56,7 @@ static void public_key_describe(const struct key *asymmetric_key,
if (key)
seq_printf(m, "%s.%s",
- pkey_id_type[key->id_type], key->algo->name);
+ pkey_id_type_name[key->id_type], key->algo->name);
}
/*
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 06007f0..afbbc36 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -49,7 +49,7 @@ static int x509_check_signature(const struct public_key *pub,
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be.
*/
- tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0);
+ tfm = crypto_alloc_shash(pkey_hash_algo_name[cert->sig_hash_algo], 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
@@ -117,7 +117,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
pr_devel("Cert Issuer: %s\n", cert->issuer);
pr_devel("Cert Subject: %s\n", cert->subject);
- pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]);
+ pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pkey_algo]);
pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n",
cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
cert->valid_from.tm_mday, cert->valid_from.tm_hour,
@@ -127,8 +127,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
cert->valid_to.tm_min, cert->valid_to.tm_sec);
pr_devel("Cert Signature: %s + %s\n",
- pkey_algo[cert->sig_pkey_algo],
- pkey_hash_algo[cert->sig_hash_algo]);
+ pkey_algo_name[cert->sig_pkey_algo],
+ pkey_hash_algo_name[cert->sig_hash_algo]);
if (!cert->fingerprint || !cert->authority) {
pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index f5b0224..619d570 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -22,7 +22,7 @@ enum pkey_algo {
PKEY_ALGO__LAST
};
-extern const char *const pkey_algo[PKEY_ALGO__LAST];
+extern const char *const pkey_algo_name[PKEY_ALGO__LAST];
enum pkey_hash_algo {
PKEY_HASH_MD4,
@@ -36,7 +36,7 @@ enum pkey_hash_algo {
PKEY_HASH__LAST
};
-extern const char *const pkey_hash_algo[PKEY_HASH__LAST];
+extern const char *const pkey_hash_algo_name[PKEY_HASH__LAST];
enum pkey_id_type {
PKEY_ID_PGP, /* OpenPGP generated key ID */
@@ -44,7 +44,7 @@ enum pkey_id_type {
PKEY_ID_TYPE__LAST
};
-extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST];
+extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];
/*
* Cryptographic data for the public-key subtype of the asymmetric key type.
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
index ea1b1df..015e5b7 100644
--- a/kernel/module_signing.c
+++ b/kernel/module_signing.c
@@ -54,7 +54,7 @@ static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash,
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be.
*/
- tfm = crypto_alloc_shash(pkey_hash_algo[hash], 0, 0);
+ tfm = crypto_alloc_shash(pkey_hash_algo_name[hash], 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm);
@@ -217,7 +217,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
return -ENOPKG;
if (ms.hash >= PKEY_HASH__LAST ||
- !pkey_hash_algo[ms.hash])
+ !pkey_hash_algo_name[ms.hash])
return -ENOPKG;
key = request_asymmetric_key(sig, ms.signer_len,
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 02/23] KEYS: Move the algorithm pointer array from x509 to public_key.c
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
2012-10-30 19:19 ` [PATCH 01/23] KEYS: Rename public key parameter name arrays David Howells
@ 2012-10-30 19:19 ` David Howells
2012-10-30 19:19 ` [PATCH 03/23] KEYS: Store public key algo ID in public_key struct David Howells
` (21 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:19 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Move the public-key algorithm pointer array from x509_public_key.c to
public_key.c as it isn't X.509 specific.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/public_key.c | 8 ++++++++
crypto/asymmetric_keys/x509_public_key.c | 11 +----------
include/crypto/public_key.h | 1 +
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index b313df1..796ce08 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -28,6 +28,14 @@ const char *const pkey_algo_name[PKEY_ALGO__LAST] = {
};
EXPORT_SYMBOL_GPL(pkey_algo_name);
+const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST] = {
+#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
+ defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
+ [PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
+#endif
+};
+EXPORT_SYMBOL_GPL(pkey_algo);
+
const char *const pkey_hash_algo_name[PKEY_HASH__LAST] = {
[PKEY_HASH_MD4] = "md4",
[PKEY_HASH_MD5] = "md5",
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index afbbc36..fe38628 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -23,15 +23,6 @@
#include "public_key.h"
#include "x509_parser.h"
-static const
-struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = {
- [PKEY_ALGO_DSA] = NULL,
-#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \
- defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE)
- [PKEY_ALGO_RSA] = &RSA_public_key_algorithm,
-#endif
-};
-
/*
* Check the signature on a certificate using the provided public key
*/
@@ -174,7 +165,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
goto error_free_cert;
}
- cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo];
+ cert->pub->algo = pkey_algo[cert->pkey_algo];
cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key */
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 619d570..46bde25 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -23,6 +23,7 @@ enum pkey_algo {
};
extern const char *const pkey_algo_name[PKEY_ALGO__LAST];
+extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST];
enum pkey_hash_algo {
PKEY_HASH_MD4,
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 03/23] KEYS: Store public key algo ID in public_key struct
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
2012-10-30 19:19 ` [PATCH 01/23] KEYS: Rename public key parameter name arrays David Howells
2012-10-30 19:19 ` [PATCH 02/23] KEYS: Move the algorithm pointer array from x509 to public_key.c David Howells
@ 2012-10-30 19:19 ` David Howells
2012-10-30 19:20 ` [PATCH 04/23] KEYS: Split public_key_verify_signature() and make available David Howells
` (20 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:19 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Store public key algo ID in public_key struct for reference purposes. This
allows it to be removed from the x509_certificate struct and used to find a
default in public_key_verify_signature().
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/x509_cert_parser.c | 5 +++--
crypto/asymmetric_keys/x509_parser.h | 1 -
crypto/asymmetric_keys/x509_public_key.c | 4 ++--
include/crypto/public_key.h | 1 +
4 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 7fabc4c..a583930 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -343,8 +343,9 @@ int x509_extract_key_data(void *context, size_t hdrlen,
if (ctx->last_oid != OID_rsaEncryption)
return -ENOPKG;
- /* There seems to be an extraneous 0 byte on the front of the data */
- ctx->cert->pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA;
+
+ /* Discard the BIT STRING metadata */
ctx->key = value + 1;
ctx->key_size = vlen - 1;
return 0;
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index f86dc5f..e583ad0 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -20,7 +20,6 @@ struct x509_certificate {
char *authority; /* Authority key fingerprint as hex */
struct tm valid_from;
struct tm valid_to;
- enum pkey_algo pkey_algo : 8; /* Public key algorithm */
enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */
enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
const void *tbs; /* Signed data */
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index fe38628..fac574c 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -108,7 +108,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
pr_devel("Cert Issuer: %s\n", cert->issuer);
pr_devel("Cert Subject: %s\n", cert->subject);
- pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pkey_algo]);
+ pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]);
pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n",
cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1,
cert->valid_from.tm_mday, cert->valid_from.tm_hour,
@@ -165,7 +165,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
goto error_free_cert;
}
- cert->pub->algo = pkey_algo[cert->pkey_algo];
+ cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
cert->pub->id_type = PKEY_ID_X509;
/* Check the signature on the key */
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 46bde25..05778df 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -60,6 +60,7 @@ struct public_key {
#define PKEY_CAN_DECRYPT 0x02
#define PKEY_CAN_SIGN 0x04
#define PKEY_CAN_VERIFY 0x08
+ enum pkey_algo pkey_algo : 8;
enum pkey_id_type id_type : 8;
union {
MPI mpi[5];
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 04/23] KEYS: Split public_key_verify_signature() and make available
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (2 preceding siblings ...)
2012-10-30 19:19 ` [PATCH 03/23] KEYS: Store public key algo ID in public_key struct David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:20 ` [PATCH 05/23] KEYS: Store public key algo ID in public_key_signature struct David Howells
` (19 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Modify public_key_verify_signature() so that it now takes a public_key struct
rather than a key struct and supply a wrapper that takes a key struct. The
wrapper is then used by the asymmetric key subtype and the modified function is
used by X.509 self-signature checking and can be used by PKCS#7 also.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/public_key.c | 38 ++++++++++++++++++++++++------
crypto/asymmetric_keys/public_key.h | 6 +++++
crypto/asymmetric_keys/x509_public_key.c | 2 +-
3 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c
index 796ce08..566cc50 100644
--- a/crypto/asymmetric_keys/public_key.c
+++ b/crypto/asymmetric_keys/public_key.c
@@ -86,21 +86,43 @@ EXPORT_SYMBOL_GPL(public_key_destroy);
/*
* Verify a signature using a public key.
*/
-static int public_key_verify_signature(const struct key *key,
- const struct public_key_signature *sig)
+int public_key_verify_signature(const struct public_key *pk,
+ const struct public_key_signature *sig)
{
- const struct public_key *pk = key->payload.data;
+ const struct public_key_algorithm *algo;
+
+ BUG_ON(!pk);
+ BUG_ON(!pk->mpi[0]);
+ BUG_ON(!pk->mpi[1]);
+ BUG_ON(!sig);
+ BUG_ON(!sig->digest);
+ BUG_ON(!sig->mpi[0]);
+
+ algo = pk->algo;
+ if (!algo) {
+ algo = pkey_algo[pk->pkey_algo];
+ if (!algo)
+ return -ENOPKG;
+ }
- if (!pk->algo->verify_signature)
+ if (!algo->verify_signature)
return -ENOTSUPP;
- if (sig->nr_mpi != pk->algo->n_sig_mpi) {
+ if (sig->nr_mpi != algo->n_sig_mpi) {
pr_debug("Signature has %u MPI not %u\n",
- sig->nr_mpi, pk->algo->n_sig_mpi);
+ sig->nr_mpi, algo->n_sig_mpi);
return -EINVAL;
}
- return pk->algo->verify_signature(pk, sig);
+ return algo->verify_signature(pk, sig);
+}
+EXPORT_SYMBOL_GPL(public_key_verify_signature);
+
+static int public_key_verify_signature_2(const struct key *key,
+ const struct public_key_signature *sig)
+{
+ const struct public_key *pk = key->payload.data;
+ return public_key_verify_signature(pk, sig);
}
/*
@@ -111,6 +133,6 @@ struct asymmetric_key_subtype public_key_subtype = {
.name = "public_key",
.describe = public_key_describe,
.destroy = public_key_destroy,
- .verify_signature = public_key_verify_signature,
+ .verify_signature = public_key_verify_signature_2,
};
EXPORT_SYMBOL_GPL(public_key_subtype);
diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h
index 5e5e356..5c37a22 100644
--- a/crypto/asymmetric_keys/public_key.h
+++ b/crypto/asymmetric_keys/public_key.h
@@ -28,3 +28,9 @@ struct public_key_algorithm {
};
extern const struct public_key_algorithm RSA_public_key_algorithm;
+
+/*
+ * public_key.c
+ */
+extern int public_key_verify_signature(const struct public_key *pk,
+ const struct public_key_signature *sig);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index fac574c..8cb2f70 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -76,7 +76,7 @@ static int x509_check_signature(const struct public_key *pub,
if (ret < 0)
goto error_mpi;
- ret = pub->algo->verify_signature(pub, sig);
+ ret = public_key_verify_signature(pub, sig);
pr_debug("Cert Verification: %d\n", ret);
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 05/23] KEYS: Store public key algo ID in public_key_signature struct
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (3 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 04/23] KEYS: Split public_key_verify_signature() and make available David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:20 ` [PATCH 06/23] x509: struct x509_certificate needs struct tm declaring David Howells
` (18 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Store public key algorithm ID in public_key_signature struct for reference
purposes. This allows a public_key_signature struct to be embedded in
struct x509_certificate and struct pkcs7_message more easily.
Signed-off-by: David Howells <dhowells@redhat.com>
---
include/crypto/public_key.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index 05778df..b34fda4 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -90,6 +90,7 @@ struct public_key_signature {
u8 *digest;
u8 digest_size; /* Number of bytes in digest */
u8 nr_mpi; /* Occupancy of mpi[] */
+ enum pkey_algo pkey_algo : 8;
enum pkey_hash_algo pkey_hash_algo : 8;
union {
MPI mpi[2];
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 06/23] x509: struct x509_certificate needs struct tm declaring
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (4 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 05/23] KEYS: Store public key algo ID in public_key_signature struct David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:20 ` [PATCH 07/23] X.509: Add bits needed for PKCS#7 David Howells
` (17 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
struct x509_certificate needs struct tm declaring by #inclusion of linux/time.h
prior to its definition.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/x509_parser.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index e583ad0..2d01182 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -9,6 +9,7 @@
* 2 of the Licence, or (at your option) any later version.
*/
+#include <linux/time.h>
#include <crypto/public_key.h>
struct x509_certificate {
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 07/23] X.509: Add bits needed for PKCS#7
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (5 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 06/23] x509: struct x509_certificate needs struct tm declaring David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:20 ` [PATCH 08/23] X.509: Embed public_key_signature struct and create filler function David Howells
` (16 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
PKCS#7 validation requires access to the serial number and the raw names in an
X.509 certificate.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/x509.asn1 | 2 +-
crypto/asymmetric_keys/x509_cert_parser.c | 17 +++++++++++++++++
crypto/asymmetric_keys/x509_parser.h | 10 ++++++++--
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1
index bf32b3d..aae0cde 100644
--- a/crypto/asymmetric_keys/x509.asn1
+++ b/crypto/asymmetric_keys/x509.asn1
@@ -6,7 +6,7 @@ Certificate ::= SEQUENCE {
TBSCertificate ::= SEQUENCE {
version [ 0 ] Version DEFAULT,
- serialNumber CertificateSerialNumber,
+ serialNumber CertificateSerialNumber ({ x509_note_serial }),
signature AlgorithmIdentifier ({ x509_note_pkey_algo }),
issuer Name ({ x509_note_issuer }),
validity Validity,
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index a583930..08bebf1 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -209,6 +209,19 @@ int x509_note_signature(void *context, size_t hdrlen,
}
/*
+ * Note the certificate serial number
+ */
+int x509_note_serial(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct x509_parse_context *ctx = context;
+ ctx->cert->raw_serial = value;
+ ctx->cert->raw_serial_size = vlen;
+ return 0;
+}
+
+/*
* Note some of the name segments from which we'll fabricate a name.
*/
int x509_extract_name_segment(void *context, size_t hdrlen,
@@ -320,6 +333,8 @@ int x509_note_issuer(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
+ ctx->cert->raw_issuer = value;
+ ctx->cert->raw_issuer_size = vlen;
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
}
@@ -328,6 +343,8 @@ int x509_note_subject(void *context, size_t hdrlen,
const void *value, size_t vlen)
{
struct x509_parse_context *ctx = context;
+ ctx->cert->raw_subject = value;
+ ctx->cert->raw_subject_size = vlen;
return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
}
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 2d01182..a6ce46f 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -24,9 +24,15 @@ struct x509_certificate {
enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */
enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
const void *tbs; /* Signed data */
- size_t tbs_size; /* Size of signed data */
+ unsigned tbs_size; /* Size of signed data */
+ unsigned sig_size; /* Size of sigature */
const void *sig; /* Signature data */
- size_t sig_size; /* Size of sigature */
+ const void *raw_serial; /* Raw serial number in ASN.1 */
+ unsigned raw_serial_size;
+ unsigned raw_issuer_size;
+ const void *raw_issuer; /* Raw issuer name in ASN.1 */
+ const void *raw_subject; /* Raw subject name in ASN.1 */
+ unsigned raw_subject_size;
};
/*
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 08/23] X.509: Embed public_key_signature struct and create filler function
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (6 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 07/23] X.509: Add bits needed for PKCS#7 David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:20 ` [PATCH 09/23] X.509: Handle certificates that lack an authorityKeyIdentifier field David Howells
` (15 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Embed a public_key_signature struct in struct x509_certificate, eliminating
now unnecessary fields, and split x509_check_signature() to create a filler
function for it that attaches a digest of the signed data and an MPI that
represents the signature data. x509_free_certificate() is then modified to
these.
Whilst we're at it, export both x509_check_signature() and the new
x509_get_sig_params().
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/x509_cert_parser.c | 30 ++++++----
crypto/asymmetric_keys/x509_parser.h | 14 +++--
crypto/asymmetric_keys/x509_public_key.c | 83 ++++++++++++++++-------------
3 files changed, 73 insertions(+), 54 deletions(-)
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 08bebf1..931f069 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -47,6 +47,8 @@ void x509_free_certificate(struct x509_certificate *cert)
kfree(cert->subject);
kfree(cert->fingerprint);
kfree(cert->authority);
+ kfree(cert->sig.digest);
+ mpi_free(cert->sig.rsa.s);
kfree(cert);
}
}
@@ -152,33 +154,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen,
return -ENOPKG; /* Unsupported combination */
case OID_md4WithRSAEncryption:
- ctx->cert->sig_hash_algo = PKEY_HASH_MD5;
- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_MD5;
+ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
break;
case OID_sha1WithRSAEncryption:
- ctx->cert->sig_hash_algo = PKEY_HASH_SHA1;
- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA1;
+ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
break;
case OID_sha256WithRSAEncryption:
- ctx->cert->sig_hash_algo = PKEY_HASH_SHA256;
- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA256;
+ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
break;
case OID_sha384WithRSAEncryption:
- ctx->cert->sig_hash_algo = PKEY_HASH_SHA384;
- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA384;
+ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
break;
case OID_sha512WithRSAEncryption:
- ctx->cert->sig_hash_algo = PKEY_HASH_SHA512;
- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA512;
+ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
break;
case OID_sha224WithRSAEncryption:
- ctx->cert->sig_hash_algo = PKEY_HASH_SHA224;
- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA;
+ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA224;
+ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
break;
}
@@ -203,8 +205,8 @@ int x509_note_signature(void *context, size_t hdrlen,
return -EINVAL;
}
- ctx->cert->sig = value;
- ctx->cert->sig_size = vlen;
+ ctx->cert->raw_sig = value;
+ ctx->cert->raw_sig_size = vlen;
return 0;
}
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index a6ce46f..6b1d877 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -21,18 +21,17 @@ struct x509_certificate {
char *authority; /* Authority key fingerprint as hex */
struct tm valid_from;
struct tm valid_to;
- enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */
- enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */
const void *tbs; /* Signed data */
unsigned tbs_size; /* Size of signed data */
- unsigned sig_size; /* Size of sigature */
- const void *sig; /* Signature data */
+ unsigned raw_sig_size; /* Size of sigature */
+ const void *raw_sig; /* Signature data */
const void *raw_serial; /* Raw serial number in ASN.1 */
unsigned raw_serial_size;
unsigned raw_issuer_size;
const void *raw_issuer; /* Raw issuer name in ASN.1 */
const void *raw_subject; /* Raw subject name in ASN.1 */
unsigned raw_subject_size;
+ struct public_key_signature sig; /* Signature parameters */
};
/*
@@ -40,3 +39,10 @@ struct x509_certificate {
*/
extern void x509_free_certificate(struct x509_certificate *cert);
extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+
+/*
+ * x509_public_key.c
+ */
+extern int x509_get_sig_params(struct x509_certificate *cert);
+extern int x509_check_signature(const struct public_key *pub,
+ struct x509_certificate *cert);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 8cb2f70..b7c81d8 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -24,72 +24,83 @@
#include "x509_parser.h"
/*
- * Check the signature on a certificate using the provided public key
+ * Set up the signature parameters in an X.509 certificate. This involves
+ * digesting the signed data and extracting the signature.
*/
-static int x509_check_signature(const struct public_key *pub,
- const struct x509_certificate *cert)
+int x509_get_sig_params(struct x509_certificate *cert)
{
- struct public_key_signature *sig;
struct crypto_shash *tfm;
struct shash_desc *desc;
size_t digest_size, desc_size;
+ void *digest;
int ret;
pr_devel("==>%s()\n", __func__);
-
+
+ if (cert->sig.rsa.s)
+ return 0;
+
+ cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size);
+ if (!cert->sig.rsa.s)
+ return -ENOMEM;
+ cert->sig.nr_mpi = 1;
+
/* Allocate the hashing algorithm we're going to need and find out how
* big the hash operational data will be.
*/
- tfm = crypto_alloc_shash(pkey_hash_algo_name[cert->sig_hash_algo], 0, 0);
+ tfm = crypto_alloc_shash(pkey_hash_algo_name[cert->sig.pkey_hash_algo], 0, 0);
if (IS_ERR(tfm))
return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
digest_size = crypto_shash_digestsize(tfm);
- /* We allocate the hash operational data storage on the end of our
- * context data.
+ /* We allocate the hash operational data storage on the end of the
+ * digest storage space.
*/
ret = -ENOMEM;
- sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL);
- if (!sig)
- goto error_no_sig;
+ digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+ if (!digest)
+ goto error;
- sig->pkey_hash_algo = cert->sig_hash_algo;
- sig->digest = (u8 *)sig + sizeof(*sig) + desc_size;
- sig->digest_size = digest_size;
+ cert->sig.digest = digest;
+ cert->sig.digest_size = digest_size;
- desc = (void *)sig + sizeof(*sig);
- desc->tfm = tfm;
- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ desc = digest + digest_size;
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
ret = crypto_shash_init(desc);
if (ret < 0)
goto error;
+ might_sleep();
+ ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest);
+error:
+ crypto_free_shash(tfm);
+ pr_devel("<==%s() = %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(x509_get_sig_params);
- ret = -ENOMEM;
- sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size);
- if (!sig->rsa.s)
- goto error;
+/*
+ * Check the signature on a certificate using the provided public key
+ */
+int x509_check_signature(const struct public_key *pub,
+ struct x509_certificate *cert)
+{
+ int ret;
- ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest);
- if (ret < 0)
- goto error_mpi;
+ pr_devel("==>%s()\n", __func__);
- ret = public_key_verify_signature(pub, sig);
+ ret = x509_get_sig_params(cert);
+ if (ret < 0)
+ return ret;
+ ret = public_key_verify_signature(pub, &cert->sig);
pr_debug("Cert Verification: %d\n", ret);
-
-error_mpi:
- mpi_free(sig->rsa.s);
-error:
- kfree(sig);
-error_no_sig:
- crypto_free_shash(tfm);
-
- pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
+EXPORT_SYMBOL_GPL(x509_check_signature);
/*
* Attempt to parse a data blob for a key as an X509 certificate.
@@ -118,8 +129,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->valid_to.tm_mday, cert->valid_to.tm_hour,
cert->valid_to.tm_min, cert->valid_to.tm_sec);
pr_devel("Cert Signature: %s + %s\n",
- pkey_algo_name[cert->sig_pkey_algo],
- pkey_hash_algo_name[cert->sig_hash_algo]);
+ pkey_algo_name[cert->sig.pkey_algo],
+ pkey_hash_algo_name[cert->sig.pkey_hash_algo]);
if (!cert->fingerprint || !cert->authority) {
pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 09/23] X.509: Handle certificates that lack an authorityKeyIdentifier field
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (7 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 08/23] X.509: Embed public_key_signature struct and create filler function David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:20 ` [PATCH 10/23] X.509: Export certificate parse and free functions David Howells
` (14 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Handle certificates that lack an authorityKeyIdentifier field by assuming
they're self-signed and checking their signatures against themselves.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/x509_public_key.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index b7c81d8..3a87512 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -132,8 +132,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
pkey_algo_name[cert->sig.pkey_algo],
pkey_hash_algo_name[cert->sig.pkey_hash_algo]);
- if (!cert->fingerprint || !cert->authority) {
- pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n",
+ if (!cert->fingerprint) {
+ pr_warn("Cert for '%s' must have a SubjKeyId extension\n",
cert->subject);
ret = -EKEYREJECTED;
goto error_free_cert;
@@ -179,8 +179,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep)
cert->pub->algo = pkey_algo[cert->pub->pkey_algo];
cert->pub->id_type = PKEY_ID_X509;
- /* Check the signature on the key */
- if (strcmp(cert->fingerprint, cert->authority) == 0) {
+ /* Check the signature on the key if it appears to be self-signed */
+ if (!cert->authority ||
+ strcmp(cert->fingerprint, cert->authority) == 0) {
ret = x509_check_signature(cert->pub, cert);
if (ret < 0)
goto error_free_cert;
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 10/23] X.509: Export certificate parse and free functions
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (8 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 09/23] X.509: Handle certificates that lack an authorityKeyIdentifier field David Howells
@ 2012-10-30 19:20 ` David Howells
2012-10-30 19:21 ` [PATCH 11/23] PKCS#7: Implement a parser [RFC 2315] David Howells
` (13 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:20 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Export certificate parse and free functions for use by modules.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/x509_cert_parser.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 931f069..9cf0e16 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -11,6 +11,7 @@
#define pr_fmt(fmt) "X.509: "fmt
#include <linux/kernel.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/oid_registry.h>
@@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert)
kfree(cert);
}
}
+EXPORT_SYMBOL_GPL(x509_free_certificate);
/*
* Parse an X.509 certificate
@@ -97,6 +99,7 @@ error_no_ctx:
error_no_cert:
return ERR_PTR(ret);
}
+EXPORT_SYMBOL_GPL(x509_cert_parse);
/*
* Note an OID when we find one for later processing when we know how
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 11/23] PKCS#7: Implement a parser [RFC 2315]
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (9 preceding siblings ...)
2012-10-30 19:20 ` [PATCH 10/23] X.509: Export certificate parse and free functions David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 19:21 ` [PATCH 12/23] PKCS#7: Digest the data in a signed-data message David Howells
` (12 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Implement a parser for a PKCS#7 signed-data message as described in part of
RFC 2315.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/Kconfig | 9 +
crypto/asymmetric_keys/Makefile | 13 +
crypto/asymmetric_keys/pkcs7.asn1 | 127 +++++++++++++
crypto/asymmetric_keys/pkcs7_parser.c | 326 +++++++++++++++++++++++++++++++++
crypto/asymmetric_keys/pkcs7_parser.h | 65 +++++++
include/linux/oid_registry.h | 1
6 files changed, 541 insertions(+)
create mode 100644 crypto/asymmetric_keys/pkcs7.asn1
create mode 100644 crypto/asymmetric_keys/pkcs7_parser.c
create mode 100644 crypto/asymmetric_keys/pkcs7_parser.h
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 6d2c2ea..413f3f6 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -35,4 +35,13 @@ config X509_CERTIFICATE_PARSER
data and provides the ability to instantiate a crypto key from a
public key packet found inside the certificate.
+config PKCS7_MESSAGE_PARSER
+ tristate "PKCS#7 message parser"
+ depends on X509_CERTIFICATE_PARSER
+ select ASN1
+ select OID_REGISTRY
+ help
+ This option provides support for parsing PKCS#7 format messages for
+ signature data and provides the ability to verify the signature.
+
endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 0727204..59d8cad 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -25,3 +25,16 @@ $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h
clean-files += x509-asn1.c x509-asn1.h
clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
+
+#
+# PKCS#7 message handling
+#
+obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
+pkcs7_message-y := \
+ pkcs7-asn1.o \
+ pkcs7_parser.o
+
+$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
+$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
+
+clean-files += pkcs7-asn1.c pkcs7-asn1.h
diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1
new file mode 100644
index 0000000..7bf91ed
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7.asn1
@@ -0,0 +1,127 @@
+PKCS7ContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ content [0] EXPLICIT SignedData OPTIONAL
+}
+
+ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
+
+SignedData ::= SEQUENCE {
+ version INTEGER,
+ digestAlgorithms DigestAlgorithmIdentifiers ({ pkcs7_note_digest_algo }),
+ contentInfo ContentInfo,
+ certificates CHOICE {
+ certSet [0] IMPLICIT ExtendedCertificatesAndCertificates,
+ certSequence [2] IMPLICIT Certificates
+ } OPTIONAL ({ pkcs7_note_certificate_list }),
+ crls CHOICE {
+ crlSet [1] IMPLICIT CertificateRevocationLists,
+ crlSequence [3] IMPLICIT CRLSequence
+ } OPTIONAL,
+ signerInfos SignerInfos
+}
+
+ContentInfo ::= SEQUENCE {
+ contentType ContentType,
+ content [0] EXPLICIT Data OPTIONAL
+}
+
+Data ::= ANY ({ pkcs7_note_data })
+
+DigestAlgorithmIdentifiers ::= CHOICE {
+ daSet SET OF DigestAlgorithmIdentifier,
+ daSequence SEQUENCE OF DigestAlgorithmIdentifier
+}
+
+DigestAlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ parameters ANY OPTIONAL
+}
+
+--
+-- Certificates and certificate lists
+--
+ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
+
+ExtendedCertificateOrCertificate ::= CHOICE {
+ certificate Certificate, -- X.509
+ extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6
+}
+
+ExtendedCertificate ::= Certificate -- cheating
+
+Certificates ::= SEQUENCE OF Certificate
+
+CertificateRevocationLists ::= SET OF CertificateList
+
+CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
+
+CRLSequence ::= SEQUENCE OF CertificateList
+
+Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
+
+--
+-- Signer information
+--
+SignerInfos ::= CHOICE {
+ siSet SET OF SignerInfo,
+ siSequence SEQUENCE OF SignerInfo
+}
+
+SignerInfo ::= SEQUENCE {
+ version INTEGER,
+ issuerAndSerialNumber IssuerAndSerialNumber,
+ digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_note_digest_algo }),
+ authenticatedAttributes CHOICE {
+ aaSet [0] IMPLICIT SetOfAuthenticatedAttribute
+ ({ pkcs7_note_set_of_authattrs }),
+ aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
+ -- Explicit because easier to compute digest on
+ -- sequence of attributes and then reuse encoded
+ -- sequence in aaSequence.
+ } OPTIONAL,
+ digestEncryptionAlgorithm
+ DigestEncryptionAlgorithmIdentifier ({ pkcs7_note_pkey_algo }),
+ encryptedDigest EncryptedDigest,
+ unauthenticatedAttributes CHOICE {
+ uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute,
+ uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
+ } OPTIONAL
+}
+
+IssuerAndSerialNumber ::= SEQUENCE {
+ issuer Name ({ pkcs7_note_issuer }),
+ serialNumber CertificateSerialNumber ({ pkcs7_note_serial })
+}
+
+CertificateSerialNumber ::= INTEGER
+
+SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
+
+AuthenticatedAttribute ::= SEQUENCE {
+ type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ values SET OF ANY ({ pkcs7_note_authenticated_attr })
+}
+
+UnauthenticatedAttribute ::= SEQUENCE {
+ type OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ values SET OF ANY
+}
+
+DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ parameters ANY OPTIONAL
+}
+
+EncryptedDigest ::= OCTET STRING ({ pkcs7_note_signature })
+
+---
+--- X.500 Name
+---
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+ attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+ attributeValue ANY
+}
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
new file mode 100644
index 0000000..56df2d2
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -0,0 +1,326 @@
+/* PKCS#7 parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+#include "pkcs7-asn1.h"
+
+struct pkcs7_parse_context {
+ struct pkcs7_message *msg; /* Message being constructed */
+ struct x509_certificate *certs; /* Certificate cache */
+ struct x509_certificate **ppcerts;
+ unsigned long data; /* Start of data */
+ enum OID last_oid; /* Last OID encountered */
+};
+
+/*
+ * Free a PKCS#7 message
+ */
+void pkcs7_free_message(struct pkcs7_message *pkcs7)
+{
+ struct x509_certificate *cert;
+
+ if (pkcs7) {
+ while (pkcs7->certs) {
+ cert = pkcs7->certs;
+ pkcs7->certs = cert->next;
+ x509_free_certificate(cert);
+ }
+ while (pkcs7->crl) {
+ cert = pkcs7->certs;
+ pkcs7->certs = cert->next;
+ x509_free_certificate(cert);
+ }
+ kfree(pkcs7->sig.digest);
+ mpi_free(pkcs7->sig.mpi[0]);
+ kfree(pkcs7);
+ }
+}
+EXPORT_SYMBOL_GPL(pkcs7_free_message);
+
+/*
+ * Parse a PKCS#7 message
+ */
+struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
+{
+ struct pkcs7_parse_context *ctx;
+ struct pkcs7_message *msg;
+ long ret;
+
+ ret = -ENOMEM;
+ msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
+ if (!msg)
+ goto error_no_sig;
+ ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
+ if (!ctx)
+ goto error_no_ctx;
+
+ ctx->msg = msg;
+ ctx->data = (unsigned long)data;
+ ctx->ppcerts = &ctx->certs;
+
+ /* Attempt to decode the signature */
+ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+ if (ret < 0)
+ goto error_decode;
+
+ while (ctx->certs) {
+ struct x509_certificate *cert = ctx->certs;
+ ctx->certs = cert->next;
+ x509_free_certificate(cert);
+ }
+ kfree(ctx);
+ return msg;
+
+error_decode:
+ kfree(ctx);
+error_no_ctx:
+ pkcs7_free_message(msg);
+error_no_sig:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pkcs7_parse_message);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int pkcs7_note_OID(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ ctx->last_oid = look_up_OID(value, vlen);
+ if (ctx->last_oid == OID__NR) {
+ char buffer[50];
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ printk("PKCS7: Unknown OID: [%lu] %s\n",
+ (unsigned long)value - ctx->data, buffer);
+ }
+ return 0;
+}
+
+/*
+ * Note the digest algorithm for the signature.
+ */
+int pkcs7_note_digest_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ switch (ctx->last_oid) {
+ case OID_md4:
+ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_MD4;
+ break;
+ case OID_md5:
+ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_MD5;
+ break;
+ case OID_sha1:
+ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_SHA1;
+ break;
+ case OID_sha256:
+ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_SHA256;
+ break;
+ default:
+ printk("Unsupported digest algo: %u\n", ctx->last_oid);
+ return -ENOPKG;
+ }
+ return 0;
+}
+
+/*
+ * Note the public key algorithm for the signature.
+ */
+int pkcs7_note_pkey_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ switch (ctx->last_oid) {
+ case OID_rsaEncryption:
+ ctx->msg->sig.pkey_algo = PKEY_ALGO_RSA;
+ break;
+ default:
+ printk("Unsupported pkey algo: %u\n", ctx->last_oid);
+ return -ENOPKG;
+ }
+ return 0;
+}
+
+/*
+ * Extract a certificate and store it in the context.
+ */
+int pkcs7_extract_cert(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ struct x509_certificate *cert;
+
+ if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
+ pr_debug("Cert began with tag %02x at %lu\n",
+ tag, (unsigned long)ctx - ctx->data);
+ return -EBADMSG;
+ }
+
+ /* We have to correct for the header so that the X.509 parser can start
+ * from the beginning. Note that since X.509 stipulates DER, there
+ * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
+ * stipulates BER).
+ */
+ value -= hdrlen;
+ vlen += hdrlen;
+
+ if (((u8*)value)[1] == 0x80)
+ vlen += 2; /* Indefinite length - there should be an EOC */
+
+ cert = x509_cert_parse(value, vlen);
+ if (IS_ERR(cert))
+ return PTR_ERR(cert);
+
+ pr_debug("Got cert for %s\n", cert->subject);
+ pr_debug("- fingerprint %s\n", cert->fingerprint);
+
+ *ctx->ppcerts = cert;
+ ctx->ppcerts = &cert->next;
+ return 0;
+}
+
+/*
+ * Save the certificate list
+ */
+int pkcs7_note_certificate_list(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ pr_devel("Got cert list (%02x)\n", tag);
+
+ *ctx->ppcerts = ctx->msg->certs;
+ ctx->msg->certs = ctx->certs;
+ ctx->certs = NULL;
+ ctx->ppcerts = &ctx->certs;
+ return 0;
+}
+
+/*
+ * Extract the data from the signature and store that and its content type OID
+ * in the context.
+ */
+int pkcs7_note_data(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ pr_debug("Got data\n");
+
+ ctx->msg->data = value;
+ ctx->msg->data_len = vlen;
+ ctx->msg->data_hdrlen = hdrlen;
+ ctx->msg->data_type = ctx->last_oid;
+ return 0;
+}
+
+/*
+ * Parse authenticated attributes
+ */
+int pkcs7_note_authenticated_attr(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+ switch (ctx->last_oid) {
+ case OID_messageDigest:
+ if (tag != ASN1_OTS)
+ return -EBADMSG;
+ ctx->msg->msgdigest = value;
+ ctx->msg->msgdigest_len = vlen;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Note the set of auth attributes for digestion purposes [RFC2315 9.3]
+ */
+int pkcs7_note_set_of_authattrs(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+
+ /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
+ ctx->msg->authattrs = value - (hdrlen - 1);
+ ctx->msg->authattrs_len = vlen + (hdrlen - 1);
+ return 0;
+}
+
+/*
+ * Note the issuing certificate serial number
+ */
+int pkcs7_note_serial(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ ctx->msg->raw_serial = value;
+ ctx->msg->raw_serial_size = vlen;
+ return 0;
+}
+
+/*
+ * Note the issuer's name
+ */
+int pkcs7_note_issuer(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ ctx->msg->raw_issuer = value;
+ ctx->msg->raw_issuer_size = vlen;
+ return 0;
+}
+
+/*
+ * Note the signature data
+ */
+int pkcs7_note_signature(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pkcs7_parse_context *ctx = context;
+ MPI mpi;
+
+ BUG_ON(ctx->msg->sig.pkey_algo != PKEY_ALGO_RSA);
+
+ mpi = mpi_read_raw_data(value, vlen);
+ if (!mpi)
+ return -ENOMEM;
+
+ ctx->msg->sig.mpi[0] = mpi;
+ ctx->msg->sig.nr_mpi = 1;
+ return 0;
+}
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
new file mode 100644
index 0000000..5415857
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -0,0 +1,65 @@
+/* PKCS#7 crypto data parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/oid_registry.h>
+#include "x509_parser.h"
+
+#define kenter(FMT, ...) \
+ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+struct pkcs7_message {
+ struct x509_certificate *certs; /* Certificate list */
+ struct x509_certificate *crl; /* Revocation list */
+ struct x509_certificate *signer; /* Signing certificate (in ->certs) */
+
+ /* Content Data (or NULL) */
+ enum OID data_type; /* Type of Data */
+ size_t data_len; /* Length of Data */
+ size_t data_hdrlen; /* Length of Data ASN.1 header */
+ const void *data; /* Content Data (or 0) */
+
+ /* Message digest - the digest of the Content Data (or NULL) */
+ const void *msgdigest;
+ unsigned msgdigest_len;
+
+ /* Authenticated Attribute data (or NULL) */
+ unsigned authattrs_len;
+ const void *authattrs;
+
+ /* Issuing cert serial number and issuer's name */
+ const void *raw_serial;
+ unsigned raw_serial_size;
+ unsigned raw_issuer_size;
+ const void *raw_issuer;
+
+ /* Message signature.
+ *
+ * This contains the generated digest of _either_ the Content Data or
+ * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of
+ * the attributes contains the digest of the the Content Data within
+ * it.
+ */
+ struct public_key_signature sig;
+};
+
+/*
+ * pkcs7_parser.c
+ */
+extern struct pkcs7_message *pkcs7_parse_message(const void *data,
+ size_t datalen);
+extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
+
+/*
+ * pkcs7_verify.c
+ */
+extern int pkcs7_verify(struct pkcs7_message *pkcs7);
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index 6926db7..edeff85 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -55,6 +55,7 @@ enum OID {
OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
OID_sha1, /* 1.3.14.3.2.26 */
+ OID_sha256, /* 2.16.840.1.101.3.4.2.1 */
/* Distinguished Name attribute IDs [RFC 2256] */
OID_commonName, /* 2.5.4.3 */
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 12/23] PKCS#7: Digest the data in a signed-data message
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (10 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 11/23] PKCS#7: Implement a parser [RFC 2315] David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 19:21 ` [PATCH 13/23] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature David Howells
` (11 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Digest the data in a PKCS#7 signed-data message and attach to the
public_key_signature struct contained in the pkcs7_message struct.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/Makefile | 3 +
crypto/asymmetric_keys/pkcs7_verify.c | 130 +++++++++++++++++++++++++++++++++
2 files changed, 132 insertions(+), 1 deletion(-)
create mode 100644 crypto/asymmetric_keys/pkcs7_verify.c
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 59d8cad..b6b39e7 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -32,7 +32,8 @@ clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h
obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
pkcs7_message-y := \
pkcs7-asn1.o \
- pkcs7_parser.o
+ pkcs7_parser.o \
+ pkcs7_verify.o
$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
new file mode 100644
index 0000000..7d7ac8a
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -0,0 +1,130 @@
+/* Verify the signature on a PKCS#7 message.
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <crypto/hash.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+
+/*
+ * Digest the relevant parts of the PKCS#7 data
+ */
+static int pkcs7_digest(struct pkcs7_message *pkcs7)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ void *digest;
+ int ret;
+
+ kenter(",%u", pkcs7->sig.pkey_hash_algo);
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(pkey_hash_algo_name[pkcs7->sig.pkey_hash_algo],
+ 0, 0);
+ if (IS_ERR(tfm))
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+ pkcs7->sig.digest_size = digest_size = crypto_shash_digestsize(tfm);
+
+ ret = -ENOMEM;
+ digest = kzalloc(digest_size + desc_size, GFP_KERNEL);
+ if (!digest)
+ goto error_no_desc;
+
+ desc = digest + digest_size;
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ /* Digest the message [RFC2315 9.3] */
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+ ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest);
+ if (ret < 0)
+ goto error;
+ pr_devel("MsgDigest = [%*ph]\n", 8, digest);
+
+ /* However, if there are authenticated attributes, there must be a
+ * message digest attribute amongst them which corresponds to the
+ * digest we just calculated.
+ */
+ if (pkcs7->msgdigest) {
+ u8 tag;
+
+ if (pkcs7->msgdigest_len != pkcs7->sig.digest_size) {
+ pr_debug("Invalid digest size (%u)\n",
+ pkcs7->msgdigest_len);
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ if (memcmp(digest, pkcs7->msgdigest, pkcs7->msgdigest_len) != 0) {
+ pr_debug("Message digest doesn't match\n");
+ ret = -EKEYREJECTED;
+ goto error;
+ }
+
+ /* We then calculate anew, using the authenticated attributes
+ * as the contents of the digest instead. Note that we need to
+ * convert the attributes from a CONT.0 into a SET before we
+ * hash it.
+ */
+ memset(digest, 0, pkcs7->sig.digest_size);
+
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+ tag = ASN1_CONS_BIT | ASN1_SET;
+ ret = crypto_shash_update(desc, &tag, 1);
+ if (ret < 0)
+ goto error;
+ ret = crypto_shash_finup(desc, pkcs7->authattrs,
+ pkcs7->authattrs_len, digest);
+ if (ret < 0)
+ goto error;
+ pr_devel("AADigest = [%*ph]\n", 8, digest);
+ }
+
+ pkcs7->sig.digest = digest;
+ digest = NULL;
+
+error:
+ kfree(digest);
+error_no_desc:
+ crypto_free_shash(tfm);
+ kleave(" = %d\n", ret);
+ return ret;
+}
+
+/*
+ * Verify a PKCS#7 message
+ */
+int pkcs7_verify(struct pkcs7_message *pkcs7)
+{
+ int ret;
+
+ /* First of all, digest the data in the PKCS#7 message */
+ ret = pkcs7_digest(pkcs7);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_verify);
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 13/23] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (11 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 12/23] PKCS#7: Digest the data in a signed-data message David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 19:21 ` [PATCH 14/23] PKCS#7: Verify internal certificate chain David Howells
` (10 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Find the appropriate key in the PKCS#7 key list and verify the signature with
it. There may be several keys in there forming a chain. Any link in that
chain or the root of that chain may be in our keyrings.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pkcs7_verify.c | 61 +++++++++++++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 7d7ac8a..614f2b6 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -114,6 +114,53 @@ error_no_desc:
}
/*
+ * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7
+ * uses the issuer's name and the issuing certificate serial number for
+ * matching purposes. These must match the certificate issuer's name (not
+ * subject's name) and the certificate serial number [RFC 2315 6.7].
+ */
+static int pkcs7_find_key(struct pkcs7_message *pkcs7)
+{
+ struct x509_certificate *x509;
+
+ kenter("%u,%u", pkcs7->raw_serial_size, pkcs7->raw_issuer_size);
+
+ for (x509 = pkcs7->certs; x509; x509 = x509->next) {
+ pr_devel("- x509 %u,%u\n",
+ x509->raw_serial_size, x509->raw_issuer_size);
+
+ /* I'm _assuming_ that the generator of the PKCS#7 message will
+ * encode the fields from the X.509 cert in the same way in the
+ * PKCS#7 message - but I can't be 100% sure of that. It's
+ * possible this will need element-by-element comparison.
+ */
+ if (x509->raw_serial_size != pkcs7->raw_serial_size ||
+ memcmp(x509->raw_serial, pkcs7->raw_serial,
+ pkcs7->raw_serial_size) != 0)
+ continue;
+ pr_devel("Found cert serial match\n");
+
+ if (x509->raw_issuer_size != pkcs7->raw_issuer_size ||
+ memcmp(x509->raw_issuer, pkcs7->raw_issuer,
+ pkcs7->raw_issuer_size) != 0) {
+ pr_warn("X.509 subject and PKCS#7 issuer don't match\n");
+ continue;
+ }
+
+ if (x509->pub->pkey_algo != pkcs7->sig.pkey_algo) {
+ pr_warn("X.509 algo and PKCS#7 sig algo don't match\n");
+ continue;
+ }
+
+ pkcs7->signer = x509;
+ return 0;
+ }
+ pr_warn("Issuing X.509 cert not found (#%*ph)\n",
+ pkcs7->raw_serial_size, pkcs7->raw_serial);
+ return -ENOKEY;
+}
+
+/*
* Verify a PKCS#7 message
*/
int pkcs7_verify(struct pkcs7_message *pkcs7)
@@ -125,6 +172,20 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
if (ret < 0)
return ret;
+ /* Find the key for the message signature */
+ ret = pkcs7_find_key(pkcs7);
+ if (ret < 0)
+ return ret;
+
+ pr_devel("Found X.509 cert\n");
+
+ /* Verify the PKCS#7 binary against the key */
+ ret = public_key_verify_signature(pkcs7->signer->pub, &pkcs7->sig);
+ if (ret < 0)
+ return ret;
+
+ pr_devel("Verified signature\n");
+
return 0;
}
EXPORT_SYMBOL_GPL(pkcs7_verify);
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 14/23] PKCS#7: Verify internal certificate chain
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (12 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 13/23] PKCS#7: Find the right key in the PKCS#7 key list and verify the signature David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 19:21 ` [PATCH 15/23] Provide PE binary definitions David Howells
` (9 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Verify certificate chain in the X.509 certificates contained within the PKCS#7
message as far as possible. If any signature that we should be able to verify
fails, we reject the whole lot.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pkcs7_verify.c | 67 +++++++++++++++++++++++++++++++++
crypto/asymmetric_keys/x509_parser.h | 1
2 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 614f2b6..2d729a6 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -161,6 +161,70 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7)
}
/*
+ * Verify the internal certificate chain as best we can.
+ */
+static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7)
+{
+ struct x509_certificate *x509 = pkcs7->signer, *p;
+ int ret;
+
+ kenter("");
+
+ for (;;) {
+ pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint);
+ ret = x509_get_sig_params(x509);
+ if (ret < 0)
+ return ret;
+
+ if (x509->issuer)
+ pr_debug("- issuer %s\n", x509->issuer);
+ if (x509->authority)
+ pr_debug("- authkeyid %s\n", x509->authority);
+
+ if (!x509->authority ||
+ (x509->subject &&
+ strcmp(x509->subject, x509->authority) == 0)) {
+ /* If there's no authority certificate specified, then
+ * the certificate must be self-signed and is the root
+ * of the chain. Likewise if the cert is its own
+ * authority.
+ */
+ pr_debug("- no auth?\n");
+ if (x509->raw_subject_size != x509->raw_issuer_size ||
+ memcmp(x509->raw_subject, x509->raw_issuer,
+ x509->raw_issuer_size) != 0)
+ return 0;
+
+ ret = x509_check_signature(x509->pub, x509);
+ if (ret < 0)
+ return ret;
+ x509->signer = x509;
+ pr_debug("- self-signed\n");
+ return 0;
+ }
+
+ for (p = pkcs7->certs; p; p = p->next)
+ if (!p->signer &&
+ p->raw_subject_size == x509->raw_issuer_size &&
+ strcmp(p->fingerprint, x509->authority) == 0 &&
+ memcmp(p->raw_subject, x509->raw_issuer,
+ x509->raw_issuer_size) == 0)
+ goto found_issuer;
+ pr_debug("- top\n");
+ return 0;
+
+ found_issuer:
+ pr_debug("- issuer %s\n", p->subject);
+ ret = x509_check_signature(p->pub, x509);
+ if (ret < 0)
+ return ret;
+ x509->signer = p;
+ x509 = p;
+ might_sleep();
+ }
+}
+
+/*
* Verify a PKCS#7 message
*/
int pkcs7_verify(struct pkcs7_message *pkcs7)
@@ -186,6 +250,7 @@ int pkcs7_verify(struct pkcs7_message *pkcs7)
pr_devel("Verified signature\n");
- return 0;
+ /* Verify the internal certificate chain */
+ return pkcs7_verify_sig_chain(pkcs7);
}
EXPORT_SYMBOL_GPL(pkcs7_verify);
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 6b1d877..5e35fba 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -14,6 +14,7 @@
struct x509_certificate {
struct x509_certificate *next;
+ const struct x509_certificate *signer; /* Certificate that signed this one */
struct public_key *pub; /* Public key details */
char *issuer; /* Name of certificate issuer */
char *subject; /* Name of certificate subject */
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 15/23] Provide PE binary definitions
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (13 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 14/23] PKCS#7: Verify internal certificate chain David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 19:21 ` [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein David Howells
` (8 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Provide some PE binary structural and constant definitions as taken from the
pesign package sources.
Signed-off-by: David Howells <dhowells@redhat.com>
---
include/linux/pe.h | 448 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 448 insertions(+)
create mode 100644 include/linux/pe.h
diff --git a/include/linux/pe.h b/include/linux/pe.h
new file mode 100644
index 0000000..9234aef
--- /dev/null
+++ b/include/linux/pe.h
@@ -0,0 +1,448 @@
+/*
+ * Copyright 2011 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Peter Jones <pjones@redhat.com>
+ */
+#ifndef __LINUX_PE_H
+#define __LINUX_PE_H
+
+#include <linux/types.h>
+
+#define MZ_MAGIC 0x5a4d /* "MZ" */
+
+struct mz_hdr {
+ uint16_t magic; /* MZ_MAGIC */
+ uint16_t lbsize; /* size of last used block */
+ uint16_t blocks; /* pages in file, 0x3 */
+ uint16_t relocs; /* relocations */
+ uint16_t hdrsize; /* header size in "paragraphs" */
+ uint16_t min_extra_pps; /* .bss */
+ uint16_t max_extra_pps; /* runtime limit for the arena size */
+ uint16_t ss; /* relative stack segment */
+ uint16_t sp; /* initial %sp register */
+ uint16_t checksum; /* word checksum */
+ uint16_t ip; /* initial %ip register */
+ uint16_t cs; /* initial %cs relative to load segment */
+ uint16_t reloc_table_offset; /* offset of the first relocation */
+ uint16_t overlay_num; /* overlay number. set to 0. */
+ uint16_t reserved0[4]; /* reserved */
+ uint16_t oem_id; /* oem identifier */
+ uint16_t oem_info; /* oem specific */
+ uint16_t reserved1[10]; /* reserved */
+ uint32_t peaddr; /* address of pe header */
+ char message[64]; /* message to print */
+};
+
+struct mz_reloc {
+ uint16_t offset;
+ uint16_t segment;
+};
+
+#define PE_MAGIC 0x00004550 /* "PE\0\0" */
+#define PE_OPT_MAGIC_PE32 0x010b
+#define PE_OPT_MAGIC_PE32_ROM 0x0107
+#define PE_OPT_MAGIC_PE32PLUS 0x020b
+
+/* machine type */
+#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000
+#define IMAGE_FILE_MACHINE_AM33 0x01d3
+#define IMAGE_FILE_MACHINE_AMD64 0x8664
+#define IMAGE_FILE_MACHINE_ARM 0x01c0
+#define IMAGE_FILE_MACHINE_ARMV7 0x01c4
+#define IMAGE_FILE_MACHINE_EBC 0x0ebc
+#define IMAGE_FILE_MACHINE_I386 0x014c
+#define IMAGE_FILE_MACHINE_IA64 0x0200
+#define IMAGE_FILE_MACHINE_M32R 0x9041
+#define IMAGE_FILE_MACHINE_MIPS16 0x0266
+#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366
+#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466
+#define IMAGE_FILE_MACHINE_POWERPC 0x01f0
+#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
+#define IMAGE_FILE_MACHINE_R4000 0x0166
+#define IMAGE_FILE_MACHINE_SH3 0x01a2
+#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
+#define IMAGE_FILE_MACHINE_SH3E 0x01a4
+#define IMAGE_FILE_MACHINE_SH4 0x01a6
+#define IMAGE_FILE_MACHINE_SH5 0x01a8
+#define IMAGE_FILE_MACHINE_THUMB 0x01c2
+#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169
+
+/* flags */
+#define IMAGE_FILE_RELOCS_STRIPPED 0x0001
+#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002
+#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008
+#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010
+#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020
+#define IMAGE_FILE_16BIT_MACHINE 0x0040
+#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080
+#define IMAGE_FILE_32BIT_MACHINE 0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED 0x0200
+#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400
+#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800
+#define IMAGE_FILE_SYSTEM 0x1000
+#define IMAGE_FILE_DLL 0x2000
+#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000
+#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000
+
+struct pe_hdr {
+ uint32_t magic; /* PE magic */
+ uint16_t machine; /* machine type */
+ uint16_t sections; /* number of sections */
+ uint32_t timestamp; /* time_t */
+ uint32_t symbol_table; /* symbol table offset */
+ uint32_t symbols; /* number of symbols */
+ uint16_t opt_hdr_size; /* size of optional header */
+ uint16_t flags; /* flags */
+};
+
+#define IMAGE_FILE_OPT_ROM_MAGIC 0x107
+#define IMAGE_FILE_OPT_PE32_MAGIC 0x10b
+#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC 0x20b
+
+#define IMAGE_SUBSYSTEM_UNKNOWN 0
+#define IMAGE_SUBSYSTEM_NATIVE 1
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
+#define IMAGE_SUBSYSTEM_POSIX_CUI 7
+#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9
+#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
+#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11
+#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12
+#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13
+#define IMAGE_SUBSYSTEM_XBOX 14
+
+#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040
+#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080
+#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100
+#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200
+#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400
+#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800
+#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
+#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
+
+/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't
+ * work right. vomit. */
+struct pe32_opt_hdr {
+ /* "standard" header */
+ uint16_t magic; /* file type */
+ uint8_t ld_major; /* linker major version */
+ uint8_t ld_minor; /* linker minor version */
+ uint32_t text_size; /* size of text section(s) */
+ uint32_t data_size; /* size of data section(s) */
+ uint32_t bss_size; /* size of bss section(s) */
+ uint32_t entry_point; /* file offset of entry point */
+ uint32_t code_base; /* relative code addr in ram */
+ uint32_t data_base; /* relative data addr in ram */
+ /* "windows" header */
+ uint32_t image_base; /* preferred load address */
+ uint32_t section_align; /* alignment in bytes */
+ uint32_t file_align; /* file alignment in bytes */
+ uint16_t os_major; /* major OS version */
+ uint16_t os_minor; /* minor OS version */
+ uint16_t image_major; /* major image version */
+ uint16_t image_minor; /* minor image version */
+ uint16_t subsys_major; /* major subsystem version */
+ uint16_t subsys_minor; /* minor subsystem version */
+ uint32_t win32_version; /* reserved, must be 0 */
+ uint32_t image_size; /* image size */
+ uint32_t header_size; /* header size rounded up to
+ file_align */
+ uint32_t csum; /* checksum */
+ uint16_t subsys; /* subsystem */
+ uint16_t dll_flags; /* more flags! */
+ uint32_t stack_size_req;/* amt of stack requested */
+ uint32_t stack_size; /* amt of stack required */
+ uint32_t heap_size_req; /* amt of heap requested */
+ uint32_t heap_size; /* amt of heap required */
+ uint32_t loader_flags; /* reserved, must be 0 */
+ uint32_t data_dirs; /* number of data dir entries */
+};
+
+struct pe32plus_opt_hdr {
+ uint16_t magic; /* file type */
+ uint8_t ld_major; /* linker major version */
+ uint8_t ld_minor; /* linker minor version */
+ uint32_t text_size; /* size of text section(s) */
+ uint32_t data_size; /* size of data section(s) */
+ uint32_t bss_size; /* size of bss section(s) */
+ uint32_t entry_point; /* file offset of entry point */
+ uint32_t code_base; /* relative code addr in ram */
+ /* "windows" header */
+ uint64_t image_base; /* preferred load address */
+ uint32_t section_align; /* alignment in bytes */
+ uint32_t file_align; /* file alignment in bytes */
+ uint16_t os_major; /* major OS version */
+ uint16_t os_minor; /* minor OS version */
+ uint16_t image_major; /* major image version */
+ uint16_t image_minor; /* minor image version */
+ uint16_t subsys_major; /* major subsystem version */
+ uint16_t subsys_minor; /* minor subsystem version */
+ uint32_t win32_version; /* reserved, must be 0 */
+ uint32_t image_size; /* image size */
+ uint32_t header_size; /* header size rounded up to
+ file_align */
+ uint32_t csum; /* checksum */
+ uint16_t subsys; /* subsystem */
+ uint16_t dll_flags; /* more flags! */
+ uint64_t stack_size_req;/* amt of stack requested */
+ uint64_t stack_size; /* amt of stack required */
+ uint64_t heap_size_req; /* amt of heap requested */
+ uint64_t heap_size; /* amt of heap required */
+ uint32_t loader_flags; /* reserved, must be 0 */
+ uint32_t data_dirs; /* number of data dir entries */
+};
+
+struct data_dirent {
+ uint32_t virtual_address; /* relative to load address */
+ uint32_t size;
+};
+
+struct data_directory {
+ struct data_dirent exports; /* .edata */
+ struct data_dirent imports; /* .idata */
+ struct data_dirent resources; /* .rsrc */
+ struct data_dirent exceptions; /* .pdata */
+ struct data_dirent certs; /* certs */
+ struct data_dirent base_relocations; /* .reloc */
+ struct data_dirent debug; /* .debug */
+ struct data_dirent arch; /* reservered */
+ struct data_dirent global_ptr; /* global pointer reg. Size=0 */
+ struct data_dirent tls; /* .tls */
+ struct data_dirent load_config; /* load configuration structure */
+ struct data_dirent bound_imports; /* no idea */
+ struct data_dirent import_addrs; /* import address table */
+ struct data_dirent delay_imports; /* delay-load import table */
+ struct data_dirent clr_runtime_hdr; /* .cor (object only) */
+ struct data_dirent reserved;
+};
+
+struct section_header {
+ char name[8]; /* name or "/12\0" string tbl offset */
+ uint32_t virtual_size; /* size of loaded section in ram */
+ uint32_t virtual_address; /* relative virtual address */
+ uint32_t raw_data_size; /* size of the section */
+ uint32_t data_addr; /* file pointer to first page of sec */
+ uint32_t relocs; /* file pointer to relocation entries */
+ uint32_t line_numbers; /* line numbers! */
+ uint16_t num_relocs; /* number of relocations */
+ uint16_t num_lin_numbers; /* srsly. */
+ uint32_t flags;
+};
+
+/* they actually defined 0x00000000 as well, but I think we'll skip that one. */
+#define IMAGE_SCN_RESERVED_0 0x00000001
+#define IMAGE_SCN_RESERVED_1 0x00000002
+#define IMAGE_SCN_RESERVED_2 0x00000004
+#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* don't pad - obsolete */
+#define IMAGE_SCN_RESERVED_3 0x00000010
+#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */
+#define IMAGE_SCN_LNK_OTHER 0x00000100 /* reserved */
+#define IMAGE_SCN_LNK_INFO 0x00000200 /* .drectve comments */
+#define IMAGE_SCN_RESERVED_4 0x00000400
+#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* .o only - scn to be rm'd*/
+#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* .o only - COMDAT data */
+#define IMAGE_SCN_RESERVED_5 0x00002000 /* spec omits this */
+#define IMAGE_SCN_RESERVED_6 0x00004000 /* spec omits this */
+#define IMAGE_SCN_GPREL 0x00008000 /* global pointer referenced data */
+/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */
+#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 /* reserved for "future" use */
+#define IMAGE_SCN_16BIT 0x00020000 /* reserved for "future" use */
+#define IMAGE_SCN_LOCKED 0x00040000 /* reserved for "future" use */
+#define IMAGE_SCN_PRELOAD 0x00080000 /* reserved for "future" use */
+/* and here they just stuck a 1-byte integer in the middle of a bitfield */
+#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 /* it does what it says on the box */
+#define IMAGE_SCN_ALIGN_2BYTES 0x00200000
+#define IMAGE_SCN_ALIGN_4BYTES 0x00300000
+#define IMAGE_SCN_ALIGN_8BYTES 0x00400000
+#define IMAGE_SCN_ALIGN_16BYTES 0x00500000
+#define IMAGE_SCN_ALIGN_32BYTES 0x00600000
+#define IMAGE_SCN_ALIGN_64BYTES 0x00700000
+#define IMAGE_SCN_ALIGN_128BYTES 0x00800000
+#define IMAGE_SCN_ALIGN_256BYTES 0x00900000
+#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000
+#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000
+#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000
+#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000
+#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000
+#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */
+#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */
+#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* not pageable */
+#define IMAGE_SCN_MEM_SHARED 0x10000000 /* can be shared */
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */
+#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */
+#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */
+
+enum x64_coff_reloc_type {
+ IMAGE_REL_AMD64_ABSOLUTE = 0,
+ IMAGE_REL_AMD64_ADDR64,
+ IMAGE_REL_AMD64_ADDR32,
+ IMAGE_REL_AMD64_ADDR32N,
+ IMAGE_REL_AMD64_REL32,
+ IMAGE_REL_AMD64_REL32_1,
+ IMAGE_REL_AMD64_REL32_2,
+ IMAGE_REL_AMD64_REL32_3,
+ IMAGE_REL_AMD64_REL32_4,
+ IMAGE_REL_AMD64_REL32_5,
+ IMAGE_REL_AMD64_SECTION,
+ IMAGE_REL_AMD64_SECREL,
+ IMAGE_REL_AMD64_SECREL7,
+ IMAGE_REL_AMD64_TOKEN,
+ IMAGE_REL_AMD64_SREL32,
+ IMAGE_REL_AMD64_PAIR,
+ IMAGE_REL_AMD64_SSPAN32,
+};
+
+enum arm_coff_reloc_type {
+ IMAGE_REL_ARM_ABSOLUTE,
+ IMAGE_REL_ARM_ADDR32,
+ IMAGE_REL_ARM_ADDR32N,
+ IMAGE_REL_ARM_BRANCH2,
+ IMAGE_REL_ARM_BRANCH1,
+ IMAGE_REL_ARM_SECTION,
+ IMAGE_REL_ARM_SECREL,
+};
+
+enum sh_coff_reloc_type {
+ IMAGE_REL_SH3_ABSOLUTE,
+ IMAGE_REL_SH3_DIRECT16,
+ IMAGE_REL_SH3_DIRECT32,
+ IMAGE_REL_SH3_DIRECT8,
+ IMAGE_REL_SH3_DIRECT8_WORD,
+ IMAGE_REL_SH3_DIRECT8_LONG,
+ IMAGE_REL_SH3_DIRECT4,
+ IMAGE_REL_SH3_DIRECT4_WORD,
+ IMAGE_REL_SH3_DIRECT4_LONG,
+ IMAGE_REL_SH3_PCREL8_WORD,
+ IMAGE_REL_SH3_PCREL8_LONG,
+ IMAGE_REL_SH3_PCREL12_WORD,
+ IMAGE_REL_SH3_STARTOF_SECTION,
+ IMAGE_REL_SH3_SIZEOF_SECTION,
+ IMAGE_REL_SH3_SECTION,
+ IMAGE_REL_SH3_SECREL,
+ IMAGE_REL_SH3_DIRECT32_NB,
+ IMAGE_REL_SH3_GPREL4_LONG,
+ IMAGE_REL_SH3_TOKEN,
+ IMAGE_REL_SHM_PCRELPT,
+ IMAGE_REL_SHM_REFLO,
+ IMAGE_REL_SHM_REFHALF,
+ IMAGE_REL_SHM_RELLO,
+ IMAGE_REL_SHM_RELHALF,
+ IMAGE_REL_SHM_PAIR,
+ IMAGE_REL_SHM_NOMODE,
+};
+
+enum ppc_coff_reloc_type {
+ IMAGE_REL_PPC_ABSOLUTE,
+ IMAGE_REL_PPC_ADDR64,
+ IMAGE_REL_PPC_ADDR32,
+ IMAGE_REL_PPC_ADDR24,
+ IMAGE_REL_PPC_ADDR16,
+ IMAGE_REL_PPC_ADDR14,
+ IMAGE_REL_PPC_REL24,
+ IMAGE_REL_PPC_REL14,
+ IMAGE_REL_PPC_ADDR32N,
+ IMAGE_REL_PPC_SECREL,
+ IMAGE_REL_PPC_SECTION,
+ IMAGE_REL_PPC_SECREL16,
+ IMAGE_REL_PPC_REFHI,
+ IMAGE_REL_PPC_REFLO,
+ IMAGE_REL_PPC_PAIR,
+ IMAGE_REL_PPC_SECRELLO,
+ IMAGE_REL_PPC_GPREL,
+ IMAGE_REL_PPC_TOKEN,
+};
+
+enum x86_coff_reloc_type {
+ IMAGE_REL_I386_ABSOLUTE,
+ IMAGE_REL_I386_DIR16,
+ IMAGE_REL_I386_REL16,
+ IMAGE_REL_I386_DIR32,
+ IMAGE_REL_I386_DIR32NB,
+ IMAGE_REL_I386_SEG12,
+ IMAGE_REL_I386_SECTION,
+ IMAGE_REL_I386_SECREL,
+ IMAGE_REL_I386_TOKEN,
+ IMAGE_REL_I386_SECREL7,
+ IMAGE_REL_I386_REL32,
+};
+
+enum ia64_coff_reloc_type {
+ IMAGE_REL_IA64_ABSOLUTE,
+ IMAGE_REL_IA64_IMM14,
+ IMAGE_REL_IA64_IMM22,
+ IMAGE_REL_IA64_IMM64,
+ IMAGE_REL_IA64_DIR32,
+ IMAGE_REL_IA64_DIR64,
+ IMAGE_REL_IA64_PCREL21B,
+ IMAGE_REL_IA64_PCREL21M,
+ IMAGE_REL_IA64_PCREL21F,
+ IMAGE_REL_IA64_GPREL22,
+ IMAGE_REL_IA64_LTOFF22,
+ IMAGE_REL_IA64_SECTION,
+ IMAGE_REL_IA64_SECREL22,
+ IMAGE_REL_IA64_SECREL64I,
+ IMAGE_REL_IA64_SECREL32,
+ IMAGE_REL_IA64_DIR32NB,
+ IMAGE_REL_IA64_SREL14,
+ IMAGE_REL_IA64_SREL22,
+ IMAGE_REL_IA64_SREL32,
+ IMAGE_REL_IA64_UREL32,
+ IMAGE_REL_IA64_PCREL60X,
+ IMAGE_REL_IA64_PCREL60B,
+ IMAGE_REL_IA64_PCREL60F,
+ IMAGE_REL_IA64_PCREL60I,
+ IMAGE_REL_IA64_PCREL60M,
+ IMAGE_REL_IA64_IMMGPREL6,
+ IMAGE_REL_IA64_TOKEN,
+ IMAGE_REL_IA64_GPREL32,
+ IMAGE_REL_IA64_ADDEND,
+};
+
+struct coff_reloc {
+ uint32_t virtual_address;
+ uint32_t symbol_table_index;
+ union {
+ enum x64_coff_reloc_type x64_type;
+ enum arm_coff_reloc_type arm_type;
+ enum sh_coff_reloc_type sh_type;
+ enum ppc_coff_reloc_type ppc_type;
+ enum x86_coff_reloc_type x86_type;
+ enum ia64_coff_reloc_type ia64_type;
+ uint16_t data;
+ };
+};
+
+/*
+ * Definitions for the contents of the certs data block
+ */
+#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
+#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0
+#define WIN_CERT_TYPE_EFI_GUID 0x0EF1
+
+#define WIN_CERT_REVISION_1_0 0x0100
+#define WIN_CERT_REVISION_2_0 0x0200
+
+struct win_certificate {
+ uint32_t length;
+ uint16_t revision;
+ uint16_t cert_type;
+};
+
+#endif /* __LINUX_PE_H */
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (14 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 15/23] Provide PE binary definitions David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 21:11 ` Kees Cook
2012-10-30 19:21 ` [PATCH 17/23] pefile: Strip the wrapper off of the cert data block David Howells
` (7 subsequent siblings)
23 siblings, 1 reply; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Parse a PE binary to find a key and a signature contained therein. Later
patches will check the signature and add the key if the signature checks out.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/Kconfig | 10 ++
crypto/asymmetric_keys/Makefile | 8 +
crypto/asymmetric_keys/pefile_parser.c | 178 ++++++++++++++++++++++++++++++++
crypto/asymmetric_keys/pefile_parser.h | 31 ++++++
4 files changed, 226 insertions(+), 1 deletion(-)
create mode 100644 crypto/asymmetric_keys/pefile_parser.c
create mode 100644 crypto/asymmetric_keys/pefile_parser.h
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
index 413f3f6..2e7315c 100644
--- a/crypto/asymmetric_keys/Kconfig
+++ b/crypto/asymmetric_keys/Kconfig
@@ -31,7 +31,7 @@ config X509_CERTIFICATE_PARSER
select ASN1
select OID_REGISTRY
help
- This option procides support for parsing X.509 format blobs for key
+ This option provides support for parsing X.509 format blobs for key
data and provides the ability to instantiate a crypto key from a
public key packet found inside the certificate.
@@ -44,4 +44,12 @@ config PKCS7_MESSAGE_PARSER
This option provides support for parsing PKCS#7 format messages for
signature data and provides the ability to verify the signature.
+config PE_FILE_PARSER
+ tristate "PE binary-wrapped key parser"
+ depends on X509_CERTIFICATE_PARSER
+ depends on PKCS7_MESSAGE_PARSER
+ help
+ This option provides support for parsing signed PE binaries that
+ contain an X.509 certificate in an internal section.
+
endif # ASYMMETRIC_KEY_TYPE
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index b6b39e7..61905d3 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -39,3 +39,11 @@ $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
clean-files += pkcs7-asn1.c pkcs7-asn1.h
+
+#
+# Signed PE binary-wrapped key handling
+#
+obj-$(CONFIG_PE_FILE_PARSER) += pefile_key_parser.o
+
+pefile_key_parser-y := \
+ pefile_parser.o
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
new file mode 100644
index 0000000..efae278
--- /dev/null
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -0,0 +1,178 @@
+/* Parse a signed PE binary that wraps a key.
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PEFILE: "fmt
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pe.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <crypto/hash.h>
+#include "asymmetric_keys.h"
+#include "public_key.h"
+#include "pefile_parser.h"
+
+/*
+ * Parse a PE binary.
+ */
+static int pefile_parse_binary(struct key_preparsed_payload *prep,
+ struct pefile_context *ctx)
+{
+ const struct mz_hdr *mz = prep->data;
+ const struct pe_hdr *pe;
+ const struct pe32_opt_hdr *pe32;
+ const struct pe32plus_opt_hdr *pe64;
+ const struct data_directory *ddir;
+ const struct data_dirent *dde;
+ const struct section_header *secs, *sec;
+ unsigned loop;
+ size_t cursor, datalen = prep->datalen;
+
+ kenter("");
+
+#define chkaddr(base, x, s) \
+ do { \
+ if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
+ return -ERANGE; \
+ } while(0)
+
+ chkaddr(0, 0, sizeof(*mz));
+ if (mz->magic != MZ_MAGIC)
+ return -ELIBBAD;
+ cursor = sizeof(*mz);
+
+ chkaddr(cursor, mz->peaddr, sizeof(*pe));
+ pe = prep->data + mz->peaddr;
+ if (pe->magic != PE_MAGIC)
+ return -ELIBBAD;
+ cursor = mz->peaddr + sizeof(*pe);
+
+ chkaddr(0, cursor, sizeof(pe32->magic));
+ pe32 = prep->data + cursor;
+ pe64 = prep->data + cursor;
+
+ switch (pe32->magic) {
+ case PE_OPT_MAGIC_PE32:
+ chkaddr(0, cursor, sizeof(*pe32));
+ ctx->image_checksum_offset =
+ (unsigned long)&pe32->csum - (unsigned long)prep->data;
+ ctx->header_size = pe32->header_size;
+ cursor += sizeof(*pe32);
+ ctx->n_data_dirents = pe32->data_dirs;
+ break;
+
+ case PE_OPT_MAGIC_PE32PLUS:
+ chkaddr(0, cursor, sizeof(*pe64));
+ ctx->image_checksum_offset =
+ (unsigned long)&pe64->csum - (unsigned long)prep->data;
+ ctx->header_size = pe64->header_size;
+ cursor += sizeof(*pe64);
+ ctx->n_data_dirents = pe64->data_dirs;
+ break;
+
+ default:
+ pr_devel("Unknown PEOPT magic = %04hx\n", pe32->magic);
+ return -ELIBBAD;
+ }
+
+ pr_devel("checksum @ %x\n", ctx->image_checksum_offset);
+ pr_devel("header size = %x\n", ctx->header_size);
+
+ chkaddr(0, cursor, sizeof(*ddir));
+ ddir = prep->data + cursor;
+ cursor += sizeof(*dde) * ctx->n_data_dirents;
+
+ ctx->cert_dirent_offset =
+ (unsigned long)&ddir->certs - (unsigned long)prep->data;
+ ctx->certs_size = ddir->certs.size;
+
+ if (!ddir->certs.virtual_address || !ddir->certs.size) {
+ pr_devel("Unsigned PE binary\n");
+ return -EKEYREJECTED;
+ }
+
+ chkaddr(cursor, ddir->certs.virtual_address, ddir->certs.size);
+ ctx->sig_offset = ddir->certs.virtual_address;
+ ctx->sig_len = ddir->certs.size;
+ pr_devel("cert = %x @%x [%*ph]\n",
+ ctx->sig_len, ctx->sig_offset,
+ ctx->sig_len, prep->data + ctx->sig_offset);
+
+ /* Parse the section table, checking the parameters and looking for the
+ * section containing the list of keys.
+ */
+ ctx->n_sections = pe->sections;
+ chkaddr(0, cursor, sizeof(*sec) * ctx->n_sections);
+ ctx->secs = secs = prep->data + cursor;
+ cursor += sizeof(*sec) * ctx->n_sections;
+
+ for (loop = 0; loop < ctx->n_sections; loop++) {
+ sec = &secs[loop];
+ chkaddr(cursor, sec->data_addr, sec->raw_data_size);
+ if (memcmp(sec->name, ".keylist", 8) == 0) {
+ ctx->keylist_offset = sec->data_addr;
+ ctx->keylist_len = sec->raw_data_size;
+ }
+ }
+
+ if (ctx->keylist_offset == 0) {
+ pr_devel("No .keylist section in PE binary\n");
+ return -ENOENT;
+ }
+
+ pr_devel("keylist = %x @%x [%*ph]\n",
+ ctx->keylist_len, ctx->keylist_offset,
+ ctx->keylist_len, prep->data + ctx->keylist_offset);
+
+ return 0;
+}
+
+/*
+ * Parse a PE binary.
+ */
+static int pefile_key_preparse(struct key_preparsed_payload *prep)
+{
+ struct pefile_context ctx;
+ int ret;
+
+ kenter("");
+
+ memset(&ctx, 0, sizeof(ctx));
+ ret = pefile_parse_binary(prep, &ctx);
+ if (ret < 0)
+ return ret;
+
+ return -ENOANO; // Not yet complete
+}
+
+static struct asymmetric_key_parser pefile_key_parser = {
+ .owner = THIS_MODULE,
+ .name = "pefile",
+ .parse = pefile_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init pefile_key_init(void)
+{
+ return register_asymmetric_key_parser(&pefile_key_parser);
+}
+
+static void __exit pefile_key_exit(void)
+{
+ unregister_asymmetric_key_parser(&pefile_key_parser);
+}
+
+module_init(pefile_key_init);
+module_exit(pefile_key_exit);
diff --git a/crypto/asymmetric_keys/pefile_parser.h b/crypto/asymmetric_keys/pefile_parser.h
new file mode 100644
index 0000000..82bcaf6
--- /dev/null
+++ b/crypto/asymmetric_keys/pefile_parser.h
@@ -0,0 +1,31 @@
+/* PE Binary parser bits
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+#include "pkcs7_parser.h"
+
+struct pefile_context {
+ unsigned header_size;
+ unsigned image_checksum_offset;
+ unsigned cert_dirent_offset;
+ unsigned n_data_dirents;
+ unsigned n_sections;
+ unsigned certs_size;
+ unsigned sig_offset;
+ unsigned sig_len;
+ unsigned keylist_offset;
+ unsigned keylist_len;
+ const struct section_header *secs;
+ struct pkcs7_message *pkcs7;
+
+ /* PKCS#7 MS Individual Code Signing content */
+ const void *digest; /* Digest */
+ unsigned digest_len; /* Digest length */
+ enum pkey_hash_algo digest_algo; /* Digest algorithm */
+};
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein
2012-10-30 19:21 ` [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein David Howells
@ 2012-10-30 21:11 ` Kees Cook
2012-10-31 0:59 ` David Howells
2012-10-31 12:31 ` David Howells
0 siblings, 2 replies; 33+ messages in thread
From: Kees Cook @ 2012-10-30 21:11 UTC (permalink / raw)
To: David Howells
Cc: rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keyrings,
linux-kernel
On Tue, Oct 30, 2012 at 12:21 PM, David Howells <dhowells@redhat.com> wrote:
> Parse a PE binary to find a key and a signature contained therein. Later
> patches will check the signature and add the key if the signature checks out.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>
> crypto/asymmetric_keys/Kconfig | 10 ++
> crypto/asymmetric_keys/Makefile | 8 +
> crypto/asymmetric_keys/pefile_parser.c | 178 ++++++++++++++++++++++++++++++++
> crypto/asymmetric_keys/pefile_parser.h | 31 ++++++
> 4 files changed, 226 insertions(+), 1 deletion(-)
> create mode 100644 crypto/asymmetric_keys/pefile_parser.c
> create mode 100644 crypto/asymmetric_keys/pefile_parser.h
>
>
> diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig
> index 413f3f6..2e7315c 100644
> --- a/crypto/asymmetric_keys/Kconfig
> +++ b/crypto/asymmetric_keys/Kconfig
> @@ -31,7 +31,7 @@ config X509_CERTIFICATE_PARSER
> select ASN1
> select OID_REGISTRY
> help
> - This option procides support for parsing X.509 format blobs for key
> + This option provides support for parsing X.509 format blobs for key
> data and provides the ability to instantiate a crypto key from a
> public key packet found inside the certificate.
>
> @@ -44,4 +44,12 @@ config PKCS7_MESSAGE_PARSER
> This option provides support for parsing PKCS#7 format messages for
> signature data and provides the ability to verify the signature.
>
> +config PE_FILE_PARSER
> + tristate "PE binary-wrapped key parser"
> + depends on X509_CERTIFICATE_PARSER
> + depends on PKCS7_MESSAGE_PARSER
> + help
> + This option provides support for parsing signed PE binaries that
> + contain an X.509 certificate in an internal section.
> +
> endif # ASYMMETRIC_KEY_TYPE
> diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
> index b6b39e7..61905d3 100644
> --- a/crypto/asymmetric_keys/Makefile
> +++ b/crypto/asymmetric_keys/Makefile
> @@ -39,3 +39,11 @@ $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
> $(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h
>
> clean-files += pkcs7-asn1.c pkcs7-asn1.h
> +
> +#
> +# Signed PE binary-wrapped key handling
> +#
> +obj-$(CONFIG_PE_FILE_PARSER) += pefile_key_parser.o
> +
> +pefile_key_parser-y := \
> + pefile_parser.o
> diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
> new file mode 100644
> index 0000000..efae278
> --- /dev/null
> +++ b/crypto/asymmetric_keys/pefile_parser.c
> @@ -0,0 +1,178 @@
> +/* Parse a signed PE binary that wraps a key.
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#define pr_fmt(fmt) "PEFILE: "fmt
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/pe.h>
> +#include <keys/asymmetric-subtype.h>
> +#include <keys/asymmetric-parser.h>
> +#include <crypto/hash.h>
> +#include "asymmetric_keys.h"
> +#include "public_key.h"
> +#include "pefile_parser.h"
> +
> +/*
> + * Parse a PE binary.
> + */
> +static int pefile_parse_binary(struct key_preparsed_payload *prep,
> + struct pefile_context *ctx)
> +{
> + const struct mz_hdr *mz = prep->data;
> + const struct pe_hdr *pe;
> + const struct pe32_opt_hdr *pe32;
> + const struct pe32plus_opt_hdr *pe64;
> + const struct data_directory *ddir;
> + const struct data_dirent *dde;
> + const struct section_header *secs, *sec;
> + unsigned loop;
> + size_t cursor, datalen = prep->datalen;
> +
> + kenter("");
> +
> +#define chkaddr(base, x, s) \
> + do { \
> + if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
> + return -ERANGE; \
> + } while(0)
> +
> + chkaddr(0, 0, sizeof(*mz));
> + if (mz->magic != MZ_MAGIC)
> + return -ELIBBAD;
> + cursor = sizeof(*mz);
> +
> + chkaddr(cursor, mz->peaddr, sizeof(*pe));
> + pe = prep->data + mz->peaddr;
> + if (pe->magic != PE_MAGIC)
> + return -ELIBBAD;
> + cursor = mz->peaddr + sizeof(*pe);
> +
> + chkaddr(0, cursor, sizeof(pe32->magic));
> + pe32 = prep->data + cursor;
> + pe64 = prep->data + cursor;
> +
> + switch (pe32->magic) {
> + case PE_OPT_MAGIC_PE32:
> + chkaddr(0, cursor, sizeof(*pe32));
> + ctx->image_checksum_offset =
> + (unsigned long)&pe32->csum - (unsigned long)prep->data;
> + ctx->header_size = pe32->header_size;
> + cursor += sizeof(*pe32);
> + ctx->n_data_dirents = pe32->data_dirs;
> + break;
> +
> + case PE_OPT_MAGIC_PE32PLUS:
> + chkaddr(0, cursor, sizeof(*pe64));
> + ctx->image_checksum_offset =
> + (unsigned long)&pe64->csum - (unsigned long)prep->data;
> + ctx->header_size = pe64->header_size;
> + cursor += sizeof(*pe64);
> + ctx->n_data_dirents = pe64->data_dirs;
> + break;
> +
> + default:
> + pr_devel("Unknown PEOPT magic = %04hx\n", pe32->magic);
> + return -ELIBBAD;
> + }
> +
> + pr_devel("checksum @ %x\n", ctx->image_checksum_offset);
> + pr_devel("header size = %x\n", ctx->header_size);
> +
> + chkaddr(0, cursor, sizeof(*ddir));
> + ddir = prep->data + cursor;
> + cursor += sizeof(*dde) * ctx->n_data_dirents;
This multiplication can push the cursor out of bounds. (n_data_dirents
is unverified).
> + ctx->cert_dirent_offset =
> + (unsigned long)&ddir->certs - (unsigned long)prep->data;
> + ctx->certs_size = ddir->certs.size;
> +
> + if (!ddir->certs.virtual_address || !ddir->certs.size) {
> + pr_devel("Unsigned PE binary\n");
> + return -EKEYREJECTED;
> + }
> +
> + chkaddr(cursor, ddir->certs.virtual_address, ddir->certs.size);
> + ctx->sig_offset = ddir->certs.virtual_address;
> + ctx->sig_len = ddir->certs.size;
> + pr_devel("cert = %x @%x [%*ph]\n",
> + ctx->sig_len, ctx->sig_offset,
> + ctx->sig_len, prep->data + ctx->sig_offset);
> +
> + /* Parse the section table, checking the parameters and looking for the
> + * section containing the list of keys.
> + */
> + ctx->n_sections = pe->sections;
> + chkaddr(0, cursor, sizeof(*sec) * ctx->n_sections);
> + ctx->secs = secs = prep->data + cursor;
> + cursor += sizeof(*sec) * ctx->n_sections;
Both of these cases of n_sections multiplications can wrap.
Ultimately, you can end up with cursor close to zero, but n_sections
being giant.
> + for (loop = 0; loop < ctx->n_sections; loop++) {
> + sec = &secs[loop];
> + chkaddr(cursor, sec->data_addr, sec->raw_data_size);
> + if (memcmp(sec->name, ".keylist", 8) == 0) {
> + ctx->keylist_offset = sec->data_addr;
> + ctx->keylist_len = sec->raw_data_size;
> + }
> + }
Which means this loop will walk past the end of the memory (loop is
bounded by n_sections, so secs[loop] can go past datalen). While
data_addr and raw_data_size will stay bounded, the read of sec->name
can be out of bounds. (Also, do you want a "break" in there after the
first .keylist is found, or is this intentionally "use last key
list"?)
> +
> + if (ctx->keylist_offset == 0) {
> + pr_devel("No .keylist section in PE binary\n");
> + return -ENOENT;
> + }
> +
> + pr_devel("keylist = %x @%x [%*ph]\n",
> + ctx->keylist_len, ctx->keylist_offset,
> + ctx->keylist_len, prep->data + ctx->keylist_offset);
> +
> + return 0;
> +}
> +
> +/*
> + * Parse a PE binary.
> + */
> +static int pefile_key_preparse(struct key_preparsed_payload *prep)
> +{
> + struct pefile_context ctx;
> + int ret;
> +
> + kenter("");
> +
> + memset(&ctx, 0, sizeof(ctx));
> + ret = pefile_parse_binary(prep, &ctx);
> + if (ret < 0)
> + return ret;
> +
> + return -ENOANO; // Not yet complete
> +}
> +
> +static struct asymmetric_key_parser pefile_key_parser = {
> + .owner = THIS_MODULE,
> + .name = "pefile",
> + .parse = pefile_key_preparse,
> +};
> +
> +/*
> + * Module stuff
> + */
> +static int __init pefile_key_init(void)
> +{
> + return register_asymmetric_key_parser(&pefile_key_parser);
> +}
> +
> +static void __exit pefile_key_exit(void)
> +{
> + unregister_asymmetric_key_parser(&pefile_key_parser);
> +}
> +
> +module_init(pefile_key_init);
> +module_exit(pefile_key_exit);
> diff --git a/crypto/asymmetric_keys/pefile_parser.h b/crypto/asymmetric_keys/pefile_parser.h
> new file mode 100644
> index 0000000..82bcaf6
> --- /dev/null
> +++ b/crypto/asymmetric_keys/pefile_parser.h
> @@ -0,0 +1,31 @@
> +/* PE Binary parser bits
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells@redhat.com)
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +#include "pkcs7_parser.h"
> +
> +struct pefile_context {
> + unsigned header_size;
> + unsigned image_checksum_offset;
> + unsigned cert_dirent_offset;
> + unsigned n_data_dirents;
> + unsigned n_sections;
> + unsigned certs_size;
> + unsigned sig_offset;
> + unsigned sig_len;
> + unsigned keylist_offset;
> + unsigned keylist_len;
> + const struct section_header *secs;
> + struct pkcs7_message *pkcs7;
> +
> + /* PKCS#7 MS Individual Code Signing content */
> + const void *digest; /* Digest */
> + unsigned digest_len; /* Digest length */
> + enum pkey_hash_algo digest_algo; /* Digest algorithm */
> +};
>
--
Kees Cook
Chrome OS Security
^ permalink raw reply [flat|nested] 33+ messages in thread* Re: [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein
2012-10-30 21:11 ` Kees Cook
@ 2012-10-31 0:59 ` David Howells
2012-10-31 1:06 ` Kees Cook
2012-10-31 12:31 ` David Howells
1 sibling, 1 reply; 33+ messages in thread
From: David Howells @ 2012-10-31 0:59 UTC (permalink / raw)
To: Kees Cook
Cc: dhowells, rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar,
keyrings, linux-kernel
Kees Cook <keescook@chromium.org> wrote:
> This multiplication can push the cursor out of bounds. (n_data_dirents
> is unverified).
> ...
> Both of these cases of n_sections multiplications can wrap.
> Ultimately, you can end up with cursor close to zero, but n_sections
> being giant.
Good points. I wonder if I should limit these to some low number, or just
check that they don't exceed header_size, which also needs checking as you
said.
> ... (Also, do you want a "break" in there after the first .keylist is found,
> or is this intentionally "use last key list"?)
I hadn't considered that. Inserting a break is probably best, if only to
curtail the processing time slightly.
David
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein
2012-10-31 0:59 ` David Howells
@ 2012-10-31 1:06 ` Kees Cook
0 siblings, 0 replies; 33+ messages in thread
From: Kees Cook @ 2012-10-31 1:06 UTC (permalink / raw)
To: David Howells
Cc: rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keyrings,
linux-kernel
On Tue, Oct 30, 2012 at 5:59 PM, David Howells <dhowells@redhat.com> wrote:
> Kees Cook <keescook@chromium.org> wrote:
>
>> This multiplication can push the cursor out of bounds. (n_data_dirents
>> is unverified).
>> ...
>> Both of these cases of n_sections multiplications can wrap.
>> Ultimately, you can end up with cursor close to zero, but n_sections
>> being giant.
>
> Good points. I wonder if I should limit these to some low number, or just
> check that they don't exceed header_size, which also needs checking as you
> said.
I think header_size can be bounded by datalen? I didn't investigate
that one very deeply. For the multiplications, I think you can just do
a check before chkaddr:
ctx->n_sections = pe->sections;
if (datalen / ctx->n_sections > sizeof(*sec))
return -ELIBBAD;
chkaddr(0, cursor, sizeof(*sec) * ctx->n_sections);
Or extend chkaddr to do the check for you:
chkaddr(0, cursor, sizeof(*sec), ctx->n_sections);
with other callers just using "1" for the final argument.
Either way, you won't have to choose arbitrary limits.
-Kees
--
Kees Cook
Chrome OS Security
^ permalink raw reply [flat|nested] 33+ messages in thread
* Re: [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein
2012-10-30 21:11 ` Kees Cook
2012-10-31 0:59 ` David Howells
@ 2012-10-31 12:31 ` David Howells
2012-10-31 19:48 ` Kees Cook
1 sibling, 1 reply; 33+ messages in thread
From: David Howells @ 2012-10-31 12:31 UTC (permalink / raw)
To: Kees Cook
Cc: dhowells, rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar,
keyrings, linux-kernel
Kees Cook <keescook@chromium.org> wrote:
> Which means this loop will walk past the end of the memory (loop is
> bounded by n_sections, so secs[loop] can go past datalen). While
> data_addr and raw_data_size will stay bounded, the read of sec->name
> can be out of bounds.
Assuming n_sections is checked, sec->name can't be out of bounds because it's
a char array, not a pointer.
> (Also, do you want a "break" in there after the first .keylist is found, or
> is this intentionally "use last key list"?)
Actually, no I don't. The loop also checks the limits on each section - which
I need to do since I have to individually digest the sections later.
Attached is a patch I'm applying to pefile_parse_binary() to be more rigorous
about the checking of values in the PE binary.
David
---
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index efae278..fb80cf0 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -42,8 +42,8 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep,
#define chkaddr(base, x, s) \
do { \
- if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
- return -ERANGE; \
+ if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \
+ return -ELIBBAD; \
} while(0)
chkaddr(0, 0, sizeof(*mz));
@@ -88,7 +88,13 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep,
pr_devel("checksum @ %x\n", ctx->image_checksum_offset);
pr_devel("header size = %x\n", ctx->header_size);
- chkaddr(0, cursor, sizeof(*ddir));
+ if (cursor >= ctx->header_size || ctx->header_size >= datalen)
+ return -ELIBBAD;
+
+ if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde) ||
+ ctx->n_data_dirents < sizeof(*ddir) / sizeof(*dde))
+ return -ELIBBAD;
+
ddir = prep->data + cursor;
cursor += sizeof(*dde) * ctx->n_data_dirents;
@@ -101,7 +107,7 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep,
return -EKEYREJECTED;
}
- chkaddr(cursor, ddir->certs.virtual_address, ddir->certs.size);
+ chkaddr(ctx->header_size, ddir->certs.virtual_address, ddir->certs.size);
ctx->sig_offset = ddir->certs.virtual_address;
ctx->sig_len = ddir->certs.size;
pr_devel("cert = %x @%x [%*ph]\n",
@@ -112,7 +118,8 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep,
* section containing the list of keys.
*/
ctx->n_sections = pe->sections;
- chkaddr(0, cursor, sizeof(*sec) * ctx->n_sections);
+ if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec))
+ return -ELIBBAD;
ctx->secs = secs = prep->data + cursor;
cursor += sizeof(*sec) * ctx->n_sections;
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein
2012-10-31 12:31 ` David Howells
@ 2012-10-31 19:48 ` Kees Cook
0 siblings, 0 replies; 33+ messages in thread
From: Kees Cook @ 2012-10-31 19:48 UTC (permalink / raw)
To: David Howells
Cc: rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keyrings,
linux-kernel
On Wed, Oct 31, 2012 at 5:31 AM, David Howells <dhowells@redhat.com> wrote:
> Kees Cook <keescook@chromium.org> wrote:
>
>> Which means this loop will walk past the end of the memory (loop is
>> bounded by n_sections, so secs[loop] can go past datalen). While
>> data_addr and raw_data_size will stay bounded, the read of sec->name
>> can be out of bounds.
>
> Assuming n_sections is checked, sec->name can't be out of bounds because it's
> a char array, not a pointer.
>
>> (Also, do you want a "break" in there after the first .keylist is found, or
>> is this intentionally "use last key list"?)
>
> Actually, no I don't. The loop also checks the limits on each section - which
> I need to do since I have to individually digest the sections later.
>
> Attached is a patch I'm applying to pefile_parse_binary() to be more rigorous
> about the checking of values in the PE binary.
Cool; looks good to me. When this gets into linux-next, we should
probably spend more time staring at it. :)
Thanks for the fixes!
-Kees
--
Kees Cook
Chrome OS Security
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 17/23] pefile: Strip the wrapper off of the cert data block
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (15 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 16/23] pefile: Parse a PE binary to find a key and a signature contained therein David Howells
@ 2012-10-30 19:21 ` David Howells
2012-10-30 21:14 ` Kees Cook
2012-10-30 19:22 ` [PATCH 18/23] pefile: Parse the presumed PKCS#7 content of the certificate blob David Howells
` (6 subsequent siblings)
23 siblings, 1 reply; 33+ messages in thread
From: David Howells @ 2012-10-30 19:21 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
The certificate data block in a PE binary has a wrapper around the PKCS#7
signature we actually want to get at. Strip this off and check that we've got
something that appears to be a PKCS#7 signature.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pefile_parser.c | 60 ++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index efae278..24c117e 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/pe.h>
+#include <linux/asn1.h>
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
#include <crypto/hash.h>
@@ -138,6 +139,61 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep,
}
/*
+ * Check and strip the PE wrapper from around the signature and check that the
+ * remnant looks something like PKCS#7.
+ */
+static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep,
+ struct pefile_context *ctx)
+{
+ struct win_certificate wrapper;
+ const u8 *pkcs7;
+
+ if (ctx->sig_len < 8) {
+ pr_devel("Signature wrapper too short\n");
+ return -ELIBBAD;
+ }
+
+ memcpy(&wrapper, prep->data + ctx->sig_offset, 8);
+ pr_devel("sig wrapper = { %x, %x, %x }\n",
+ wrapper.length, wrapper.revision, wrapper.cert_type);
+ if (wrapper.length != ctx->sig_len) {
+ pr_devel("Signature wrapper len wrong\n");
+ return -ELIBBAD;
+ }
+ if (wrapper.revision != WIN_CERT_REVISION_2_0) {
+ pr_devel("Signature is not revision 2.0\n");
+ return -ENOTSUPP;
+ }
+ if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ pr_devel("Signature certificate type is not PKCS\n");
+ return -ENOTSUPP;
+ }
+
+ ctx->sig_offset += 8;
+ ctx->sig_len -= 8;
+ if (ctx->sig_len == 0) {
+ pr_devel("Signature data missing\n");
+ return -EKEYREJECTED;
+ }
+
+ /* What's left should a PKCS#7 cert */
+ pkcs7 = prep->data + ctx->sig_offset;
+ if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) {
+ if (pkcs7[1] == 0x82 &&
+ pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) &&
+ pkcs7[3] == ((ctx->sig_len - 4) & 0xff))
+ return 0;
+ if (pkcs7[1] == 0x80)
+ return 0;
+ if (pkcs7[1] > 0x82)
+ return -EMSGSIZE;
+ }
+
+ pr_devel("Signature data not PKCS#7\n");
+ return -ELIBBAD;
+}
+
+/*
* Parse a PE binary.
*/
static int pefile_key_preparse(struct key_preparsed_payload *prep)
@@ -152,6 +208,10 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
if (ret < 0)
return ret;
+ ret = pefile_strip_sig_wrapper(prep, &ctx);
+ if (ret < 0)
+ return ret;
+
return -ENOANO; // Not yet complete
}
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH 17/23] pefile: Strip the wrapper off of the cert data block
2012-10-30 19:21 ` [PATCH 17/23] pefile: Strip the wrapper off of the cert data block David Howells
@ 2012-10-30 21:14 ` Kees Cook
2012-10-31 1:03 ` David Howells
0 siblings, 1 reply; 33+ messages in thread
From: Kees Cook @ 2012-10-30 21:14 UTC (permalink / raw)
To: David Howells
Cc: rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keyrings,
linux-kernel
On Tue, Oct 30, 2012 at 12:21 PM, David Howells <dhowells@redhat.com> wrote:
> The certificate data block in a PE binary has a wrapper around the PKCS#7
> signature we actually want to get at. Strip this off and check that we've got
> something that appears to be a PKCS#7 signature.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>
> crypto/asymmetric_keys/pefile_parser.c | 60 ++++++++++++++++++++++++++++++++
> 1 file changed, 60 insertions(+)
>
>
> diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
> index efae278..24c117e 100644
> --- a/crypto/asymmetric_keys/pefile_parser.c
> +++ b/crypto/asymmetric_keys/pefile_parser.c
> @@ -15,6 +15,7 @@
> #include <linux/slab.h>
> #include <linux/err.h>
> #include <linux/pe.h>
> +#include <linux/asn1.h>
> #include <keys/asymmetric-subtype.h>
> #include <keys/asymmetric-parser.h>
> #include <crypto/hash.h>
> @@ -138,6 +139,61 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep,
> }
>
> /*
> + * Check and strip the PE wrapper from around the signature and check that the
> + * remnant looks something like PKCS#7.
> + */
> +static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep,
> + struct pefile_context *ctx)
> +{
> + struct win_certificate wrapper;
> + const u8 *pkcs7;
> +
> + if (ctx->sig_len < 8) {
> + pr_devel("Signature wrapper too short\n");
> + return -ELIBBAD;
> + }
> +
> + memcpy(&wrapper, prep->data + ctx->sig_offset, 8);
Instead of the literal 8, sizeof(wrapper)?
> + pr_devel("sig wrapper = { %x, %x, %x }\n",
> + wrapper.length, wrapper.revision, wrapper.cert_type);
> + if (wrapper.length != ctx->sig_len) {
> + pr_devel("Signature wrapper len wrong\n");
> + return -ELIBBAD;
> + }
> + if (wrapper.revision != WIN_CERT_REVISION_2_0) {
> + pr_devel("Signature is not revision 2.0\n");
> + return -ENOTSUPP;
> + }
> + if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
> + pr_devel("Signature certificate type is not PKCS\n");
> + return -ENOTSUPP;
> + }
> +
> + ctx->sig_offset += 8;
> + ctx->sig_len -= 8;
Same for these?
> + if (ctx->sig_len == 0) {
> + pr_devel("Signature data missing\n");
> + return -EKEYREJECTED;
> + }
> +
> + /* What's left should a PKCS#7 cert */
> + pkcs7 = prep->data + ctx->sig_offset;
> + if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) {
> + if (pkcs7[1] == 0x82 &&
> + pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) &&
> + pkcs7[3] == ((ctx->sig_len - 4) & 0xff))
> + return 0;
> + if (pkcs7[1] == 0x80)
> + return 0;
> + if (pkcs7[1] > 0x82)
> + return -EMSGSIZE;
Can these literals be replaced with something more meaningful?
> + }
> +
> + pr_devel("Signature data not PKCS#7\n");
> + return -ELIBBAD;
> +}
> +
> +/*
> * Parse a PE binary.
> */
> static int pefile_key_preparse(struct key_preparsed_payload *prep)
> @@ -152,6 +208,10 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
> if (ret < 0)
> return ret;
>
> + ret = pefile_strip_sig_wrapper(prep, &ctx);
> + if (ret < 0)
> + return ret;
> +
> return -ENOANO; // Not yet complete
> }
>
>
--
Kees Cook
Chrome OS Security
^ permalink raw reply [flat|nested] 33+ messages in thread* Re: [PATCH 17/23] pefile: Strip the wrapper off of the cert data block
2012-10-30 21:14 ` Kees Cook
@ 2012-10-31 1:03 ` David Howells
0 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-31 1:03 UTC (permalink / raw)
To: Kees Cook
Cc: dhowells, rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar,
keyrings, linux-kernel
Kees Cook <keescook@chromium.org> wrote:
> > + memcpy(&wrapper, prep->data + ctx->sig_offset, 8);
>
> Instead of the literal 8, sizeof(wrapper)?
Reasonable. It was originally an array of bytes until I found out that it had
structure. Even so, I should probably have used sizeof() then.
> > + if (pkcs7[1] == 0x82 &&
> > + pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) &&
> > + pkcs7[3] == ((ctx->sig_len - 4) & 0xff))
> > + return 0;
> > + if (pkcs7[1] == 0x80)
> > + return 0;
> > + if (pkcs7[1] > 0x82)
> > + return -EMSGSIZE;
>
> Can these literals be replaced with something more meaningful?
Certainly the 0x80 can. I have a patch to define ASN1_INDEFINITE_LENGTH. The
question is how to define the 0x82 since it's a flag plus a value. Maybe by
using something like:
#define ASN1_LONG_LENGTH(x) (0x80 | (x))
Anyway, thanks for the reviews!
David
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 18/23] pefile: Parse the presumed PKCS#7 content of the certificate blob
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (16 preceding siblings ...)
2012-10-30 19:21 ` [PATCH 17/23] pefile: Strip the wrapper off of the cert data block David Howells
@ 2012-10-30 19:22 ` David Howells
2012-10-30 19:22 ` [PATCH 19/23] pefile: Parse the "Microsoft individual code signing" data blob David Howells
` (5 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:22 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Parse the content of the certificate blob, presuming it to be PKCS#7 format.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pefile_parser.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index 24c117e..68fc525 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -198,6 +198,7 @@ static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep,
*/
static int pefile_key_preparse(struct key_preparsed_payload *prep)
{
+ struct pkcs7_message *pkcs7;
struct pefile_context ctx;
int ret;
@@ -212,7 +213,22 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
if (ret < 0)
return ret;
- return -ENOANO; // Not yet complete
+ pkcs7 = pkcs7_parse_message(prep->data + ctx.sig_offset, ctx.sig_len);
+ if (IS_ERR(pkcs7))
+ return PTR_ERR(pkcs7);
+ ctx.pkcs7 = pkcs7;
+
+ if (!ctx.pkcs7->data || !ctx.pkcs7->data_len) {
+ pr_devel("PKCS#7 message does not contain data\n");
+ ret = -EBADMSG;
+ goto error;
+ }
+
+ ret = -ENOANO; // Not yet complete
+
+error:
+ pkcs7_free_message(ctx.pkcs7);
+ return ret;
}
static struct asymmetric_key_parser pefile_key_parser = {
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 19/23] pefile: Parse the "Microsoft individual code signing" data blob
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (17 preceding siblings ...)
2012-10-30 19:22 ` [PATCH 18/23] pefile: Parse the presumed PKCS#7 content of the certificate blob David Howells
@ 2012-10-30 19:22 ` David Howells
2012-10-30 19:22 ` [PATCH 20/23] pefile: Digest the PE binary and compare to the PKCS#7 data David Howells
` (4 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:22 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
The PKCS#7 certificate should contain a "Microsoft individual code signing"
data blob as its signed content. This blob contains a digest of the signed
content of the PE binary and the OID of the digest algorithm used (typically
SHA256).
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/Makefile | 9 ++-
crypto/asymmetric_keys/mscode.asn1 | 28 ++++++++
crypto/asymmetric_keys/mscode_parser.c | 110 ++++++++++++++++++++++++++++++++
crypto/asymmetric_keys/pefile_parser.c | 6 ++
crypto/asymmetric_keys/pefile_parser.h | 5 +
include/linux/oid_registry.h | 6 +-
6 files changed, 162 insertions(+), 2 deletions(-)
create mode 100644 crypto/asymmetric_keys/mscode.asn1
create mode 100644 crypto/asymmetric_keys/mscode_parser.c
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 61905d3..566dd45 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -46,4 +46,11 @@ clean-files += pkcs7-asn1.c pkcs7-asn1.h
obj-$(CONFIG_PE_FILE_PARSER) += pefile_key_parser.o
pefile_key_parser-y := \
- pefile_parser.o
+ pefile_parser.o \
+ mscode_parser.o \
+ mscode-asn1.o
+
+$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h
+$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h
+
+clean-files += mscode-asn1.c mscode-asn1.h
diff --git a/crypto/asymmetric_keys/mscode.asn1 b/crypto/asymmetric_keys/mscode.asn1
new file mode 100644
index 0000000..6d09ba4
--- /dev/null
+++ b/crypto/asymmetric_keys/mscode.asn1
@@ -0,0 +1,28 @@
+--- Microsoft individual code signing data blob parser
+---
+--- Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+--- Written by David Howells (dhowells@redhat.com)
+---
+--- This program is free software; you can redistribute it and/or
+--- modify it under the terms of the GNU General Public Licence
+--- as published by the Free Software Foundation; either version
+--- 2 of the Licence, or (at your option) any later version.
+---
+
+MSCode ::= SEQUENCE {
+ type SEQUENCE {
+ contentType ContentType,
+ parameters ANY
+ },
+ content SEQUENCE {
+ digestAlgorithm DigestAlgorithmIdentifier,
+ digest OCTET STRING ({ mscode_note_digest })
+ }
+}
+
+ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type })
+
+DigestAlgorithmIdentifier ::= SEQUENCE {
+ algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }),
+ parameters ANY OPTIONAL
+}
diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c
new file mode 100644
index 0000000..0bd68e0
--- /dev/null
+++ b/crypto/asymmetric_keys/mscode_parser.c
@@ -0,0 +1,110 @@
+/* Parse a Microsoft Individual Code Signing blob
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "MSCODE: "fmt
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include "pefile_parser.h"
+#include "mscode-asn1.h"
+
+/*
+ * Parse a Microsoft Individual Code Signing blob
+ */
+int mscode_parse(struct pefile_context *ctx)
+{
+ pr_devel("Data: %zu [%*ph]\n",
+ ctx->pkcs7->data_len + ctx->pkcs7->data_hdrlen,
+ (unsigned)(ctx->pkcs7->data_len + ctx->pkcs7->data_hdrlen),
+ ctx->pkcs7->data - ctx->pkcs7->data_hdrlen);
+
+ return asn1_ber_decoder(&mscode_decoder, ctx,
+ ctx->pkcs7->data - ctx->pkcs7->data_hdrlen,
+ ctx->pkcs7->data_len + ctx->pkcs7->data_hdrlen);
+}
+
+/*
+ * Check the content type OID
+ */
+int mscode_note_content_type(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ enum OID oid;
+
+ oid = look_up_OID(value, vlen);
+ if (oid == OID__NR) {
+ char buffer[50];
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ printk("MSCODE: Unknown OID: %s\n", buffer);
+ return -EBADMSG;
+ }
+
+ if (oid != OID_msIndividualSPKeyPurpose) {
+ printk("MSCODE: Unexpected content type OID %u\n", oid);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/*
+ * Note the digest algorithm OID
+ */
+int mscode_note_digest_algo(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pefile_context *ctx = context;
+ char buffer[50];
+ enum OID oid;
+
+ oid = look_up_OID(value, vlen);
+ switch (oid) {
+ case OID_md4:
+ ctx->digest_algo = PKEY_HASH_MD4;
+ break;
+ case OID_md5:
+ ctx->digest_algo = PKEY_HASH_MD5;
+ break;
+ case OID_sha1:
+ ctx->digest_algo = PKEY_HASH_SHA1;
+ break;
+ case OID_sha256:
+ ctx->digest_algo = PKEY_HASH_SHA256;
+ break;
+
+ case OID__NR:
+ sprint_oid(value, vlen, buffer, sizeof(buffer));
+ printk("MSCODE: Unknown OID: %s\n", buffer);
+ return -EBADMSG;
+
+ default:
+ printk("MSCODE: Unsupported content type: %u\n", oid);
+ return -ENOPKG;
+ }
+
+ return 0;
+}
+
+/*
+ * Note the digest we're guaranteeing with this certificate
+ */
+int mscode_note_digest(void *context, size_t hdrlen,
+ unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct pefile_context *ctx = context;
+ ctx->digest = value;
+ ctx->digest_len = vlen;
+ return 0;
+}
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index 68fc525..42b9696 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -224,6 +224,12 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
goto error;
}
+ ret = mscode_parse(&ctx);
+ if (ret < 0)
+ goto error;
+
+ pr_devel("Digest: %u [%*ph]\n", ctx.digest_len, ctx.digest_len, ctx.digest);
+
ret = -ENOANO; // Not yet complete
error:
diff --git a/crypto/asymmetric_keys/pefile_parser.h b/crypto/asymmetric_keys/pefile_parser.h
index 82bcaf6..c3462b7 100644
--- a/crypto/asymmetric_keys/pefile_parser.h
+++ b/crypto/asymmetric_keys/pefile_parser.h
@@ -29,3 +29,8 @@ struct pefile_context {
unsigned digest_len; /* Digest length */
enum pkey_hash_algo digest_algo; /* Digest algorithm */
};
+
+/*
+ * mscode_parser.c
+ */
+extern int mscode_parse(struct pefile_context *ctx);
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
index edeff85..332dcf5 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -52,8 +52,12 @@ enum OID {
OID_md4, /* 1.2.840.113549.2.4 */
OID_md5, /* 1.2.840.113549.2.5 */
- OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
+ /* Microsoft Authenticode & Software Publishing */
+ OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */
+ OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */
OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */
+
+ OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */
OID_sha1, /* 1.3.14.3.2.26 */
OID_sha256, /* 2.16.840.1.101.3.4.2.1 */
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 20/23] pefile: Digest the PE binary and compare to the PKCS#7 data
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (18 preceding siblings ...)
2012-10-30 19:22 ` [PATCH 19/23] pefile: Parse the "Microsoft individual code signing" data blob David Howells
@ 2012-10-30 19:22 ` David Howells
2012-10-30 21:44 ` Kees Cook
2012-10-30 19:22 ` [PATCH 21/23] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys David Howells
` (3 subsequent siblings)
23 siblings, 1 reply; 33+ messages in thread
From: David Howells @ 2012-10-30 19:22 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Digest the signed parts of the PE binary, canonicalising the section table
before we need it, and then compare the the resulting digest to the one in the
PKCS#7 signed content.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pefile_parser.c | 198 ++++++++++++++++++++++++++++++++
1 file changed, 198 insertions(+)
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index 42b9696..9ede195 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -194,6 +194,193 @@ static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep,
}
/*
+ * Compare two sections for canonicalisation.
+ */
+static int pefile_compare_shdrs(const void *a, const void *b)
+{
+ const struct section_header *shdra = a;
+ const struct section_header *shdrb = b;
+ int rc;
+
+ if (shdra->data_addr > shdrb->data_addr)
+ return 1;
+ if (shdrb->data_addr > shdra->data_addr)
+ return -1;
+
+ if (shdra->virtual_address > shdrb->virtual_address)
+ return 1;
+ if (shdrb->virtual_address > shdra->virtual_address)
+ return -1;
+
+ rc = strcmp(shdra->name, shdrb->name);
+ if (rc != 0)
+ return rc;
+
+ if (shdra->virtual_size > shdrb->virtual_size)
+ return 1;
+ if (shdrb->virtual_size > shdra->virtual_size)
+ return -1;
+
+ if (shdra->raw_data_size > shdrb->raw_data_size)
+ return 1;
+ if (shdrb->raw_data_size > shdra->raw_data_size)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Load the contents of the PE binary into the digest, leaving out the image
+ * checksum and the certificate data block.
+ */
+static int pefile_digest_pe_contents(struct key_preparsed_payload *prep,
+ struct pefile_context *ctx,
+ struct shash_desc *desc)
+{
+ unsigned *canon, tmp, loop, i, hashed_bytes;
+ int ret;
+
+ /* Digest the header and data directory, but leave out the image
+ * checksum and the data dirent for the signature.
+ */
+ ret = crypto_shash_update(desc, prep->data, ctx->image_checksum_offset);
+ if (ret < 0)
+ return ret;
+
+ tmp = ctx->image_checksum_offset + sizeof(uint32_t);
+ ret = crypto_shash_update(desc, prep->data + tmp,
+ ctx->cert_dirent_offset - tmp);
+ if (ret < 0)
+ return ret;
+
+ tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent);
+ ret = crypto_shash_update(desc, prep->data + tmp,
+ ctx->header_size - tmp);
+ if (ret < 0)
+ return ret;
+
+ canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL);
+ if (!canon)
+ return -ENOMEM;
+
+ /* We have to canonicalise the section table, so we perform an
+ * insertion sort.
+ */
+ canon[0] = 0;
+ for (loop = 1; loop < ctx->n_sections; loop++) {
+ for (i = 0; i < loop; i++) {
+ if (pefile_compare_shdrs(&ctx->secs[canon[i]],
+ &ctx->secs[loop]) > 0) {
+ memmove(&canon[i + 1], &canon[i],
+ (loop - i) * sizeof(canon[0]));
+ break;
+ }
+ }
+ canon[i] = loop;
+ }
+
+ hashed_bytes = ctx->header_size;
+ for (loop = 0; loop < ctx->n_sections; loop++) {
+ i = canon[loop];
+ if (ctx->secs[i].raw_data_size == 0)
+ continue;
+ ret = crypto_shash_update(desc,
+ prep->data + ctx->secs[i].data_addr,
+ ctx->secs[i].raw_data_size);
+ if (ret < 0) {
+ kfree(canon);
+ return ret;
+ }
+ hashed_bytes += ctx->secs[i].raw_data_size;
+ }
+ kfree(canon);
+
+ if (prep->datalen > hashed_bytes) {
+ tmp = hashed_bytes + ctx->certs_size;
+ ret = crypto_shash_update(desc,
+ prep->data + hashed_bytes,
+ prep->datalen - tmp);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Digest the contents of the PE binary, leaving out the image checksum and the
+ * certificate data block.
+ */
+static int pefile_digest_pe(struct key_preparsed_payload *prep,
+ struct pefile_context *ctx)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ size_t digest_size, desc_size;
+ void *digest;
+ int ret;
+
+ kenter(",%u", ctx->digest_algo);
+
+ /* Allocate the hashing algorithm we're going to need and find out how
+ * big the hash operational data will be.
+ */
+ tfm = crypto_alloc_shash(pkey_hash_algo_name[ctx->digest_algo], 0, 0);
+ if (IS_ERR(tfm))
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
+
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+ digest_size = crypto_shash_digestsize(tfm);
+
+ if (digest_size != ctx->digest_len) {
+ pr_debug("Digest size mismatch (%zx != %x)\n",
+ digest_size, ctx->digest_len);
+ ret = -EBADMSG;
+ goto error_no_desc;
+ }
+ pr_devel("Digest: desc=%zu size=%zu\n", desc_size, digest_size);
+
+ ret = -ENOMEM;
+ desc = kzalloc(desc_size + digest_size, GFP_KERNEL);
+ if (!desc)
+ goto error_no_desc;
+
+ desc->tfm = tfm;
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ ret = crypto_shash_init(desc);
+ if (ret < 0)
+ goto error;
+
+ ret = pefile_digest_pe_contents(prep, ctx, desc);
+ if (ret < 0)
+ goto error;
+
+ digest = (void *)desc + desc_size;
+ ret = crypto_shash_final(desc, digest);
+ if (ret < 0)
+ goto error;
+
+ pr_devel("Digest calc = [%*ph]\n", ctx->digest_len, digest);
+
+ /* Check that the PE file digest matches that in the MSCODE part of the
+ * PKCS#7 certificate.
+ */
+ if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) {
+ pr_debug("Digest mismatch\n");
+ ret = -EKEYREJECTED;
+ } else {
+ pr_debug("The digests match!\n");
+ }
+
+error:
+ kfree(desc);
+error_no_desc:
+ crypto_free_shash(tfm);
+ kleave(" = %d", ret);
+ return ret;
+}
+
+/*
* Parse a PE binary.
*/
static int pefile_key_preparse(struct key_preparsed_payload *prep)
@@ -230,6 +417,17 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
pr_devel("Digest: %u [%*ph]\n", ctx.digest_len, ctx.digest_len, ctx.digest);
+ /* Generate the digest and check against the PKCS7 certificate
+ * contents.
+ */
+ ret = pefile_digest_pe(prep, &ctx);
+ if (ret < 0)
+ goto error;
+
+ ret = pkcs7_verify(pkcs7);
+ if (ret < 0)
+ goto error;
+
ret = -ENOANO; // Not yet complete
error:
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [PATCH 20/23] pefile: Digest the PE binary and compare to the PKCS#7 data
2012-10-30 19:22 ` [PATCH 20/23] pefile: Digest the PE binary and compare to the PKCS#7 data David Howells
@ 2012-10-30 21:44 ` Kees Cook
0 siblings, 0 replies; 33+ messages in thread
From: Kees Cook @ 2012-10-30 21:44 UTC (permalink / raw)
To: David Howells
Cc: rusty, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keyrings,
linux-kernel
On Tue, Oct 30, 2012 at 12:22 PM, David Howells <dhowells@redhat.com> wrote:
> Digest the signed parts of the PE binary, canonicalising the section table
> before we need it, and then compare the the resulting digest to the one in the
> PKCS#7 signed content.
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> ---
>
> crypto/asymmetric_keys/pefile_parser.c | 198 ++++++++++++++++++++++++++++++++
> 1 file changed, 198 insertions(+)
>
>
> diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
> index 42b9696..9ede195 100644
> --- a/crypto/asymmetric_keys/pefile_parser.c
> +++ b/crypto/asymmetric_keys/pefile_parser.c
> @@ -194,6 +194,193 @@ static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep,
> }
>
> /*
> + * Compare two sections for canonicalisation.
> + */
> +static int pefile_compare_shdrs(const void *a, const void *b)
> +{
> + const struct section_header *shdra = a;
> + const struct section_header *shdrb = b;
> + int rc;
> +
> + if (shdra->data_addr > shdrb->data_addr)
> + return 1;
> + if (shdrb->data_addr > shdra->data_addr)
> + return -1;
> +
> + if (shdra->virtual_address > shdrb->virtual_address)
> + return 1;
> + if (shdrb->virtual_address > shdra->virtual_address)
> + return -1;
> +
> + rc = strcmp(shdra->name, shdrb->name);
> + if (rc != 0)
> + return rc;
> +
> + if (shdra->virtual_size > shdrb->virtual_size)
> + return 1;
> + if (shdrb->virtual_size > shdra->virtual_size)
> + return -1;
> +
> + if (shdra->raw_data_size > shdrb->raw_data_size)
> + return 1;
> + if (shdrb->raw_data_size > shdra->raw_data_size)
> + return -1;
> +
> + return 0;
> +}
> +
> +/*
> + * Load the contents of the PE binary into the digest, leaving out the image
> + * checksum and the certificate data block.
> + */
> +static int pefile_digest_pe_contents(struct key_preparsed_payload *prep,
> + struct pefile_context *ctx,
> + struct shash_desc *desc)
> +{
> + unsigned *canon, tmp, loop, i, hashed_bytes;
> + int ret;
> +
> + /* Digest the header and data directory, but leave out the image
> + * checksum and the data dirent for the signature.
> + */
> + ret = crypto_shash_update(desc, prep->data, ctx->image_checksum_offset);
> + if (ret < 0)
> + return ret;
> +
> + tmp = ctx->image_checksum_offset + sizeof(uint32_t);
> + ret = crypto_shash_update(desc, prep->data + tmp,
> + ctx->cert_dirent_offset - tmp);
> + if (ret < 0)
> + return ret;
> +
> + tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent);
> + ret = crypto_shash_update(desc, prep->data + tmp,
> + ctx->header_size - tmp);
header_size is not verified, so this update can walk past datalen, or
be truncated, which is probably worse here, IIUC. I think it might be
better to make sure every field that goes into ctx is sanity-checked.
> + if (ret < 0)
> + return ret;
> +
> + canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL);
n_sections is unverified too, so this and the loops after are troublesome.
> + if (!canon)
> + return -ENOMEM;
> +
> + /* We have to canonicalise the section table, so we perform an
> + * insertion sort.
> + */
> + canon[0] = 0;
> + for (loop = 1; loop < ctx->n_sections; loop++) {
> + for (i = 0; i < loop; i++) {
> + if (pefile_compare_shdrs(&ctx->secs[canon[i]],
> + &ctx->secs[loop]) > 0) {
> + memmove(&canon[i + 1], &canon[i],
> + (loop - i) * sizeof(canon[0]));
> + break;
> + }
> + }
> + canon[i] = loop;
> + }
> +
> + hashed_bytes = ctx->header_size;
> + for (loop = 0; loop < ctx->n_sections; loop++) {
> + i = canon[loop];
> + if (ctx->secs[i].raw_data_size == 0)
> + continue;
> + ret = crypto_shash_update(desc,
> + prep->data + ctx->secs[i].data_addr,
> + ctx->secs[i].raw_data_size);
> + if (ret < 0) {
> + kfree(canon);
> + return ret;
> + }
> + hashed_bytes += ctx->secs[i].raw_data_size;
> + }
> + kfree(canon);
> +
> + if (prep->datalen > hashed_bytes) {
> + tmp = hashed_bytes + ctx->certs_size;
> + ret = crypto_shash_update(desc,
> + prep->data + hashed_bytes,
> + prep->datalen - tmp);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * Digest the contents of the PE binary, leaving out the image checksum and the
> + * certificate data block.
> + */
> +static int pefile_digest_pe(struct key_preparsed_payload *prep,
> + struct pefile_context *ctx)
> +{
> + struct crypto_shash *tfm;
> + struct shash_desc *desc;
> + size_t digest_size, desc_size;
> + void *digest;
> + int ret;
> +
> + kenter(",%u", ctx->digest_algo);
> +
> + /* Allocate the hashing algorithm we're going to need and find out how
> + * big the hash operational data will be.
> + */
> + tfm = crypto_alloc_shash(pkey_hash_algo_name[ctx->digest_algo], 0, 0);
> + if (IS_ERR(tfm))
> + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
> +
> + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
> + digest_size = crypto_shash_digestsize(tfm);
> +
> + if (digest_size != ctx->digest_len) {
> + pr_debug("Digest size mismatch (%zx != %x)\n",
> + digest_size, ctx->digest_len);
> + ret = -EBADMSG;
> + goto error_no_desc;
> + }
> + pr_devel("Digest: desc=%zu size=%zu\n", desc_size, digest_size);
> +
> + ret = -ENOMEM;
> + desc = kzalloc(desc_size + digest_size, GFP_KERNEL);
> + if (!desc)
> + goto error_no_desc;
> +
> + desc->tfm = tfm;
> + desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
> + ret = crypto_shash_init(desc);
> + if (ret < 0)
> + goto error;
> +
> + ret = pefile_digest_pe_contents(prep, ctx, desc);
> + if (ret < 0)
> + goto error;
> +
> + digest = (void *)desc + desc_size;
> + ret = crypto_shash_final(desc, digest);
> + if (ret < 0)
> + goto error;
> +
> + pr_devel("Digest calc = [%*ph]\n", ctx->digest_len, digest);
> +
> + /* Check that the PE file digest matches that in the MSCODE part of the
> + * PKCS#7 certificate.
> + */
> + if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) {
> + pr_debug("Digest mismatch\n");
> + ret = -EKEYREJECTED;
> + } else {
> + pr_debug("The digests match!\n");
> + }
> +
> +error:
> + kfree(desc);
> +error_no_desc:
> + crypto_free_shash(tfm);
> + kleave(" = %d", ret);
> + return ret;
> +}
> +
> +/*
> * Parse a PE binary.
> */
> static int pefile_key_preparse(struct key_preparsed_payload *prep)
> @@ -230,6 +417,17 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
>
> pr_devel("Digest: %u [%*ph]\n", ctx.digest_len, ctx.digest_len, ctx.digest);
>
> + /* Generate the digest and check against the PKCS7 certificate
> + * contents.
> + */
> + ret = pefile_digest_pe(prep, &ctx);
> + if (ret < 0)
> + goto error;
> +
> + ret = pkcs7_verify(pkcs7);
> + if (ret < 0)
> + goto error;
> +
> ret = -ENOANO; // Not yet complete
>
> error:
>
-Kees
--
Kees Cook
Chrome OS Security
^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH 21/23] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (19 preceding siblings ...)
2012-10-30 19:22 ` [PATCH 20/23] pefile: Digest the PE binary and compare to the PKCS#7 data David Howells
@ 2012-10-30 19:22 ` David Howells
2012-10-30 19:22 ` [PATCH 22/23] PEFILE: Load the contained key if we consider the container to be validly signed David Howells
` (2 subsequent siblings)
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:22 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Find the intersection between the X.509 certificate chain contained in a PKCS#7
message and a set of keys that we already know and trust.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/Makefile | 1
crypto/asymmetric_keys/pefile_parser.c | 6 +
crypto/asymmetric_keys/pkcs7_parser.h | 6 +
crypto/asymmetric_keys/pkcs7_trust.c | 145 ++++++++++++++++++++++++++++++++
kernel/modsign_pubkey.c | 1
5 files changed, 159 insertions(+)
create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile
index 566dd45..ddc64bb 100644
--- a/crypto/asymmetric_keys/Makefile
+++ b/crypto/asymmetric_keys/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
pkcs7_message-y := \
pkcs7-asn1.o \
pkcs7_parser.o \
+ pkcs7_trust.o \
pkcs7_verify.o
$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index 9ede195..1ab1890 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -23,6 +23,8 @@
#include "public_key.h"
#include "pefile_parser.h"
+extern struct key *modsign_keyring;
+
/*
* Parse a PE binary.
*/
@@ -428,6 +430,10 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
if (ret < 0)
goto error;
+ ret = pkcs7_validate_trust(pkcs7, modsign_keyring);
+ if (ret < 0)
+ goto error;
+
ret = -ENOANO; // Not yet complete
error:
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
index 5415857..f6df500 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.h
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -60,6 +60,12 @@ extern struct pkcs7_message *pkcs7_parse_message(const void *data,
extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
/*
+ * pkcs7_trust.c
+ */
+extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+ struct key *trust_keyring);
+
+/*
* pkcs7_verify.c
*/
extern int pkcs7_verify(struct pkcs7_message *pkcs7);
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
new file mode 100644
index 0000000..9abcb39
--- /dev/null
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -0,0 +1,145 @@
+/* Validate the trust chain of a PKCS#7 message.
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/asn1.h>
+#include <linux/key.h>
+#include <keys/asymmetric-type.h>
+#include "public_key.h"
+#include "pkcs7_parser.h"
+
+/*
+ * Request an asymmetric key.
+ */
+static struct key *pkcs7_request_asymmetric_key(
+ struct key *keyring,
+ const char *signer, size_t signer_len,
+ const char *authority, size_t auth_len)
+{
+ key_ref_t key;
+ char *id;
+
+ kenter(",%zu,,%zu", signer_len, auth_len);
+
+ /* Construct an identifier. */
+ id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL);
+ if (!id)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(id, signer, signer_len);
+ id[signer_len + 0] = ':';
+ id[signer_len + 1] = ' ';
+ memcpy(id + signer_len + 2, authority, auth_len);
+ id[signer_len + 2 + auth_len] = 0;
+
+ pr_debug("Look up: \"%s\"\n", id);
+
+ key = keyring_search(make_key_ref(keyring, 1),
+ &key_type_asymmetric, id);
+ if (IS_ERR(key))
+ pr_debug("Request for module key '%s' err %ld\n",
+ id, PTR_ERR(key));
+ kfree(id);
+
+ if (IS_ERR(key)) {
+ switch (PTR_ERR(key)) {
+ /* Hide some search errors */
+ case -EACCES:
+ case -ENOTDIR:
+ case -EAGAIN:
+ return ERR_PTR(-ENOKEY);
+ default:
+ return ERR_CAST(key);
+ }
+ }
+
+ pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key)));
+ return key_ref_to_ptr(key);
+}
+
+/*
+ * Validate that the certificate chain inside the PKCS#7 message intersects
+ * keys we already know and trust.
+ */
+int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+ struct key *trust_keyring)
+{
+ struct public_key_signature *sig = &pkcs7->sig;
+ struct x509_certificate *x509, *last = NULL;
+ struct key *key;
+ int ret;
+
+ kenter("");
+
+ for (x509 = pkcs7->signer; x509; x509 = x509->next) {
+ /* Look to see if this certificate is present in the trusted
+ * keys.
+ */
+ key = pkcs7_request_asymmetric_key(
+ trust_keyring,
+ x509->subject, strlen(x509->subject),
+ x509->fingerprint, strlen(x509->fingerprint));
+ if (!IS_ERR(key))
+ /* One of the X.509 certificates in the PKCS#7 message
+ * is apparently the same as one we already trust.
+ * Verify that the trusted variant can also validate
+ * the signature on the descendent.
+ */
+ goto matched;
+ if (key == ERR_PTR(-ENOMEM))
+ return -ENOMEM;
+
+ /* Self-signed certificates form roots of their own, and if we
+ * don't know them, then we can't accept them.
+ */
+ if (x509->next == x509) {
+ kleave(" = -EKEYREJECTED [unknown self-signed]");
+ return -EKEYREJECTED;
+ }
+
+ might_sleep();
+ last = x509;
+ sig = &last->sig;
+ }
+
+ /* No match - see if the root certificate has a signer amongst the
+ * trusted keys.
+ */
+ if (!last || !last->issuer || !last->authority) {
+ kleave(" = -EKEYREJECTED [no backref]");
+ return -EKEYREJECTED;
+ }
+
+ key = pkcs7_request_asymmetric_key(
+ trust_keyring,
+ last->issuer, strlen(last->issuer),
+ last->authority, strlen(last->authority));
+ if (IS_ERR(key))
+ return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -EKEYREJECTED;
+
+matched:
+ ret = verify_signature(key, sig);
+ key_put(key);
+ if (ret < 0) {
+ if (ret == -ENOMEM)
+ return ret;
+ kleave(" = -EKEYREJECTED [verify %d]", ret);
+ return -EKEYREJECTED;
+ }
+
+ kleave(" = 0");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_validate_trust);
diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c
index 4646eb2..602be22 100644
--- a/kernel/modsign_pubkey.c
+++ b/kernel/modsign_pubkey.c
@@ -17,6 +17,7 @@
#include "module-internal.h"
struct key *modsign_keyring;
+EXPORT_SYMBOL_GPL(modsign_keyring);
extern __initdata const u8 modsign_certificate_list[];
extern __initdata const u8 modsign_certificate_list_end[];
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 22/23] PEFILE: Load the contained key if we consider the container to be validly signed
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (20 preceding siblings ...)
2012-10-30 19:22 ` [PATCH 21/23] PKCS#7: Find intersection between PKCS#7 message and known, trusted keys David Howells
@ 2012-10-30 19:22 ` David Howells
2012-10-30 19:22 ` [PATCH 23/23] KEYS: Add a 'trusted' flag and a 'trusted only' flag David Howells
2012-10-31 2:20 ` [RFC][PATCH 00/23] Load keys from signed PE binaries Rusty Russell
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:22 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Load the key contained in the PE binary if the signature on the container can
be verified by following the chain of X.509 certificates in the PKCS#7 message
to a key that we already trust. Typically, the trusted key will be acquired
from a source outside of the kernel, such as the UEFI database.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pefile_parser.c | 11 ++++++++++-
crypto/asymmetric_keys/x509_parser.h | 3 +++
crypto/asymmetric_keys/x509_public_key.c | 2 +-
3 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index 1ab1890..28be7d3 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -389,6 +389,8 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
{
struct pkcs7_message *pkcs7;
struct pefile_context ctx;
+ const void *saved_data;
+ size_t saved_datalen;
int ret;
kenter("");
@@ -434,7 +436,14 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
if (ret < 0)
goto error;
- ret = -ENOANO; // Not yet complete
+ /* We can now try to load the key */
+ saved_data = prep->data;
+ saved_datalen = prep->datalen;
+ prep->data += ctx.keylist_offset;
+ prep->datalen = ctx.keylist_len;
+ ret = x509_key_preparse(prep);
+ prep->data = saved_data;
+ prep->datalen = saved_datalen;
error:
pkcs7_free_message(ctx.pkcs7);
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h
index 5e35fba..65452c4 100644
--- a/crypto/asymmetric_keys/x509_parser.h
+++ b/crypto/asymmetric_keys/x509_parser.h
@@ -12,6 +12,8 @@
#include <linux/time.h>
#include <crypto/public_key.h>
+struct key_preparsed_payload;
+
struct x509_certificate {
struct x509_certificate *next;
const struct x509_certificate *signer; /* Certificate that signed this one */
@@ -47,3 +49,4 @@ extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen
extern int x509_get_sig_params(struct x509_certificate *cert);
extern int x509_check_signature(const struct public_key *pub,
struct x509_certificate *cert);
+extern int x509_key_preparse(struct key_preparsed_payload *prep);
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 3a87512..b37f2a6 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -105,7 +105,7 @@ EXPORT_SYMBOL_GPL(x509_check_signature);
/*
* Attempt to parse a data blob for a key as an X509 certificate.
*/
-static int x509_key_preparse(struct key_preparsed_payload *prep)
+int x509_key_preparse(struct key_preparsed_payload *prep)
{
struct x509_certificate *cert;
struct tm now;
^ permalink raw reply related [flat|nested] 33+ messages in thread* [PATCH 23/23] KEYS: Add a 'trusted' flag and a 'trusted only' flag
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (21 preceding siblings ...)
2012-10-30 19:22 ` [PATCH 22/23] PEFILE: Load the contained key if we consider the container to be validly signed David Howells
@ 2012-10-30 19:22 ` David Howells
2012-10-31 2:20 ` [RFC][PATCH 00/23] Load keys from signed PE binaries Rusty Russell
23 siblings, 0 replies; 33+ messages in thread
From: David Howells @ 2012-10-30 19:22 UTC (permalink / raw)
To: rusty
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
Add KEY_FLAG_TRUSTED to indicate that a key either comes from a trusted source
or had a cryptographic signature chain that led back to a trusted key the
kernel already possessed.
Add KEY_FLAGS_TRUSTED_ONLY to indicate that a keyring will only accept links to
keys marked with KEY_FLAGS_TRUSTED.
Signed-off-by: David Howells <dhowells@redhat.com>
---
crypto/asymmetric_keys/pefile_parser.c | 2 +-
crypto/asymmetric_keys/pkcs7_parser.h | 3 ++-
crypto/asymmetric_keys/pkcs7_trust.c | 6 +++++-
include/linux/key-type.h | 1 +
include/linux/key.h | 3 +++
kernel/modsign_pubkey.c | 6 ++++--
security/keys/key.c | 8 ++++++++
security/keys/keyring.c | 4 ++++
security/keys/proc.c | 3 ++-
9 files changed, 30 insertions(+), 6 deletions(-)
diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c
index 28be7d3..da2db58 100644
--- a/crypto/asymmetric_keys/pefile_parser.c
+++ b/crypto/asymmetric_keys/pefile_parser.c
@@ -432,7 +432,7 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep)
if (ret < 0)
goto error;
- ret = pkcs7_validate_trust(pkcs7, modsign_keyring);
+ ret = pkcs7_validate_trust(pkcs7, modsign_keyring, &prep->trusted);
if (ret < 0)
goto error;
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h
index f6df500..ffa72dc 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.h
+++ b/crypto/asymmetric_keys/pkcs7_parser.h
@@ -63,7 +63,8 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
* pkcs7_trust.c
*/
extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
- struct key *trust_keyring);
+ struct key *trust_keyring,
+ bool *_trusted);
/*
* pkcs7_verify.c
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c
index 9abcb39..cc226f5 100644
--- a/crypto/asymmetric_keys/pkcs7_trust.c
+++ b/crypto/asymmetric_keys/pkcs7_trust.c
@@ -74,11 +74,13 @@ static struct key *pkcs7_request_asymmetric_key(
* keys we already know and trust.
*/
int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
- struct key *trust_keyring)
+ struct key *trust_keyring,
+ bool *_trusted)
{
struct public_key_signature *sig = &pkcs7->sig;
struct x509_certificate *x509, *last = NULL;
struct key *key;
+ bool trusted;
int ret;
kenter("");
@@ -131,6 +133,7 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
matched:
ret = verify_signature(key, sig);
+ trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
key_put(key);
if (ret < 0) {
if (ret == -ENOMEM)
@@ -139,6 +142,7 @@ matched:
return -EKEYREJECTED;
}
+ *_trusted = trusted;
kleave(" = 0");
return 0;
}
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index 518a53a..f942b2d 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -45,6 +45,7 @@ struct key_preparsed_payload {
const void *data; /* Raw data */
size_t datalen; /* Raw datalen */
size_t quotalen; /* Quota length for proposed payload */
+ bool trusted; /* True if key is trusted */
};
typedef int (*request_key_actor_t)(struct key_construction *key,
diff --git a/include/linux/key.h b/include/linux/key.h
index 2393b1c..cd0b9e9 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -162,6 +162,8 @@ struct key {
#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */
#define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
+#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */
+#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */
/* the description string
* - this is used to match a key against search criteria
@@ -203,6 +205,7 @@ extern struct key *key_alloc(struct key_type *type,
#define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */
#define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */
#define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */
+#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */
extern void key_revoke(struct key *key);
extern void key_invalidate(struct key *key);
diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c
index 602be22..57b0a77 100644
--- a/kernel/modsign_pubkey.c
+++ b/kernel/modsign_pubkey.c
@@ -45,7 +45,7 @@ static __init int module_verify_init(void)
KUIDT_INIT(0), KGIDT_INIT(0),
current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ,
+ KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE,
KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(modsign_keyring))
panic("Can't allocate module signing keyring\n");
@@ -53,6 +53,7 @@ static __init int module_verify_init(void)
if (key_instantiate_and_link(modsign_keyring, NULL, 0, NULL, NULL) < 0)
panic("Can't instantiate module signing keyring\n");
+ set_bit(KEY_FLAG_TRUSTED_ONLY, &modsign_keyring->flags);
return 0;
}
@@ -95,7 +96,8 @@ static __init int load_module_signing_keys(void)
plen,
(KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW,
- KEY_ALLOC_NOT_IN_QUOTA);
+ KEY_ALLOC_NOT_IN_QUOTA |
+ KEY_ALLOC_TRUSTED);
if (IS_ERR(key))
pr_err("MODSIGN: Problem loading in-kernel X.509 certificate (%ld)\n",
PTR_ERR(key));
diff --git a/security/keys/key.c b/security/keys/key.c
index a15c9da..d0f35d1 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -299,6 +299,8 @@ struct key *key_alloc(struct key_type *type, const char *desc,
if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))
key->flags |= 1 << KEY_FLAG_IN_QUOTA;
+ if (flags & KEY_ALLOC_TRUSTED)
+ key->flags |= 1 << KEY_FLAG_TRUSTED;
memset(&key->type_data, 0, sizeof(key->type_data));
@@ -813,6 +815,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
prep.data = payload;
prep.datalen = plen;
prep.quotalen = ktype->def_datalen;
+ prep.trusted = flags & KEY_ALLOC_TRUSTED;
if (ktype->preparse) {
ret = ktype->preparse(&prep);
if (ret < 0) {
@@ -826,6 +829,11 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
goto error_free_prep;
}
+ key_ref = ERR_PTR(-EPERM);
+ if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags))
+ goto error_free_prep;
+ flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0;
+
ret = __key_link_begin(keyring, ktype, description, &prealloc);
if (ret < 0) {
key_ref = ERR_PTR(ret);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 6e42df1..af90786 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1008,6 +1008,10 @@ int key_link(struct key *keyring, struct key *key)
key_check(keyring);
key_check(key);
+ if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) &&
+ !test_bit(KEY_FLAG_TRUSTED, &key->flags))
+ return -EPERM;
+
ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
if (ret == 0) {
ret = __key_link_check_live_key(keyring, key);
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 217b685..d0ca948 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -242,7 +242,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
#define showflag(KEY, LETTER, FLAG) \
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
- seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
+ seq_printf(m, "%08x %c%c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
key->serial,
showflag(key, 'I', KEY_FLAG_INSTANTIATED),
showflag(key, 'R', KEY_FLAG_REVOKED),
@@ -251,6 +251,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
showflag(key, 'N', KEY_FLAG_NEGATIVE),
showflag(key, 'i', KEY_FLAG_INVALIDATED),
+ showflag(key, 'T', KEY_FLAG_TRUSTED),
atomic_read(&key->usage),
xbuf,
key->perm,
^ permalink raw reply related [flat|nested] 33+ messages in thread* Re: [RFC][PATCH 00/23] Load keys from signed PE binaries
2012-10-30 19:19 [RFC][PATCH 00/23] Load keys from signed PE binaries David Howells
` (22 preceding siblings ...)
2012-10-30 19:22 ` [PATCH 23/23] KEYS: Add a 'trusted' flag and a 'trusted only' flag David Howells
@ 2012-10-31 2:20 ` Rusty Russell
23 siblings, 0 replies; 33+ messages in thread
From: Rusty Russell @ 2012-10-31 2:20 UTC (permalink / raw)
To: David Howells
Cc: dhowells, pjones, jwboyer, mjg, dmitry.kasatkin, zohar, keescook,
keyrings, linux-kernel
David Howells <dhowells@redhat.com> writes:
> Hi Rusty,
>
> Here's a set of patches to load a key out of a signed PE format binary:
>
> http://git.kernel.org/?p=linux/kernel/git/dhowells/linux-modsign.git;a=shortlog;h=refs/heads/devel-pekey
AFAICT this is no longer a module issue, so I'm not going to take
these. Perhaps via the crypto people, or direct to Linus?
Cheers,
Rusty.
^ permalink raw reply [flat|nested] 33+ messages in thread