diff options
-rw-r--r-- | crypto/asymmetric_keys/Kconfig | 22 | ||||
-rw-r--r-- | crypto/asymmetric_keys/Makefile | 22 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7.asn1 | 127 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_key_type.c | 99 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.c | 396 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.h | 61 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_trust.c | 219 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_verify.c | 323 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509.asn1 | 2 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 20 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 13 | ||||
-rw-r--r-- | include/crypto/pkcs7.h | 36 | ||||
-rw-r--r-- | include/linux/oid_registry.h | 1 |
13 files changed, 1339 insertions, 2 deletions
diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb95ab50..b6df198d1b6f 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig | |||
@@ -37,4 +37,26 @@ config X509_CERTIFICATE_PARSER | |||
37 | data and provides the ability to instantiate a crypto key from a | 37 | data and provides the ability to instantiate a crypto key from a |
38 | public key packet found inside the certificate. | 38 | public key packet found inside the certificate. |
39 | 39 | ||
40 | config PKCS7_MESSAGE_PARSER | ||
41 | tristate "PKCS#7 message parser" | ||
42 | depends on X509_CERTIFICATE_PARSER | ||
43 | select ASN1 | ||
44 | select OID_REGISTRY | ||
45 | help | ||
46 | This option provides support for parsing PKCS#7 format messages for | ||
47 | signature data and provides the ability to verify the signature. | ||
48 | |||
49 | config PKCS7_TEST_KEY | ||
50 | tristate "PKCS#7 testing key type" | ||
51 | depends on PKCS7_MESSAGE_PARSER | ||
52 | select SYSTEM_TRUSTED_KEYRING | ||
53 | help | ||
54 | This option provides a type of key that can be loaded up from a | ||
55 | PKCS#7 message - provided the message is signed by a trusted key. If | ||
56 | it is, the PKCS#7 wrapper is discarded and reading the key returns | ||
57 | just the payload. If it isn't, adding the key will fail with an | ||
58 | error. | ||
59 | |||
60 | This is intended for testing the PKCS#7 parser. | ||
61 | |||
40 | endif # ASYMMETRIC_KEY_TYPE | 62 | endif # ASYMMETRIC_KEY_TYPE |
diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 0727204aab68..92d0e9af24d5 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile | |||
@@ -25,3 +25,25 @@ $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h | |||
25 | 25 | ||
26 | clean-files += x509-asn1.c x509-asn1.h | 26 | clean-files += x509-asn1.c x509-asn1.h |
27 | clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h | 27 | clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h |
28 | |||
29 | # | ||
30 | # PKCS#7 message handling | ||
31 | # | ||
32 | obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o | ||
33 | pkcs7_message-y := \ | ||
34 | pkcs7-asn1.o \ | ||
35 | pkcs7_parser.o \ | ||
36 | pkcs7_trust.o \ | ||
37 | pkcs7_verify.o | ||
38 | |||
39 | $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h | ||
40 | $(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h | ||
41 | |||
42 | clean-files += pkcs7-asn1.c pkcs7-asn1.h | ||
43 | |||
44 | # | ||
45 | # PKCS#7 parser testing key | ||
46 | # | ||
47 | obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o | ||
48 | pkcs7_test_key-y := \ | ||
49 | pkcs7_key_type.o | ||
diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1 new file mode 100644 index 000000000000..a5a14ef28c86 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7.asn1 | |||
@@ -0,0 +1,127 @@ | |||
1 | PKCS7ContentInfo ::= SEQUENCE { | ||
2 | contentType ContentType, | ||
3 | content [0] EXPLICIT SignedData OPTIONAL | ||
4 | } | ||
5 | |||
6 | ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID }) | ||
7 | |||
8 | SignedData ::= SEQUENCE { | ||
9 | version INTEGER, | ||
10 | digestAlgorithms DigestAlgorithmIdentifiers, | ||
11 | contentInfo ContentInfo, | ||
12 | certificates CHOICE { | ||
13 | certSet [0] IMPLICIT ExtendedCertificatesAndCertificates, | ||
14 | certSequence [2] IMPLICIT Certificates | ||
15 | } OPTIONAL ({ pkcs7_note_certificate_list }), | ||
16 | crls CHOICE { | ||
17 | crlSet [1] IMPLICIT CertificateRevocationLists, | ||
18 | crlSequence [3] IMPLICIT CRLSequence | ||
19 | } OPTIONAL, | ||
20 | signerInfos SignerInfos | ||
21 | } | ||
22 | |||
23 | ContentInfo ::= SEQUENCE { | ||
24 | contentType ContentType, | ||
25 | content [0] EXPLICIT Data OPTIONAL | ||
26 | } | ||
27 | |||
28 | Data ::= ANY ({ pkcs7_note_data }) | ||
29 | |||
30 | DigestAlgorithmIdentifiers ::= CHOICE { | ||
31 | daSet SET OF DigestAlgorithmIdentifier, | ||
32 | daSequence SEQUENCE OF DigestAlgorithmIdentifier | ||
33 | } | ||
34 | |||
35 | DigestAlgorithmIdentifier ::= SEQUENCE { | ||
36 | algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), | ||
37 | parameters ANY OPTIONAL | ||
38 | } | ||
39 | |||
40 | -- | ||
41 | -- Certificates and certificate lists | ||
42 | -- | ||
43 | ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate | ||
44 | |||
45 | ExtendedCertificateOrCertificate ::= CHOICE { | ||
46 | certificate Certificate, -- X.509 | ||
47 | extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6 | ||
48 | } | ||
49 | |||
50 | ExtendedCertificate ::= Certificate -- cheating | ||
51 | |||
52 | Certificates ::= SEQUENCE OF Certificate | ||
53 | |||
54 | CertificateRevocationLists ::= SET OF CertificateList | ||
55 | |||
56 | CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly | ||
57 | |||
58 | CRLSequence ::= SEQUENCE OF CertificateList | ||
59 | |||
60 | Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509 | ||
61 | |||
62 | -- | ||
63 | -- Signer information | ||
64 | -- | ||
65 | SignerInfos ::= CHOICE { | ||
66 | siSet SET OF SignerInfo, | ||
67 | siSequence SEQUENCE OF SignerInfo | ||
68 | } | ||
69 | |||
70 | SignerInfo ::= SEQUENCE { | ||
71 | version INTEGER, | ||
72 | issuerAndSerialNumber IssuerAndSerialNumber, | ||
73 | digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }), | ||
74 | authenticatedAttributes CHOICE { | ||
75 | aaSet [0] IMPLICIT SetOfAuthenticatedAttribute | ||
76 | ({ pkcs7_sig_note_set_of_authattrs }), | ||
77 | aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute | ||
78 | -- Explicit because easier to compute digest on | ||
79 | -- sequence of attributes and then reuse encoded | ||
80 | -- sequence in aaSequence. | ||
81 | } OPTIONAL, | ||
82 | digestEncryptionAlgorithm | ||
83 | DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }), | ||
84 | encryptedDigest EncryptedDigest, | ||
85 | unauthenticatedAttributes CHOICE { | ||
86 | uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute, | ||
87 | uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute | ||
88 | } OPTIONAL | ||
89 | } ({ pkcs7_note_signed_info }) | ||
90 | |||
91 | IssuerAndSerialNumber ::= SEQUENCE { | ||
92 | issuer Name ({ pkcs7_sig_note_issuer }), | ||
93 | serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial }) | ||
94 | } | ||
95 | |||
96 | CertificateSerialNumber ::= INTEGER | ||
97 | |||
98 | SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute | ||
99 | |||
100 | AuthenticatedAttribute ::= SEQUENCE { | ||
101 | type OBJECT IDENTIFIER ({ pkcs7_note_OID }), | ||
102 | values SET OF ANY ({ pkcs7_sig_note_authenticated_attr }) | ||
103 | } | ||
104 | |||
105 | UnauthenticatedAttribute ::= SEQUENCE { | ||
106 | type OBJECT IDENTIFIER ({ pkcs7_note_OID }), | ||
107 | values SET OF ANY | ||
108 | } | ||
109 | |||
110 | DigestEncryptionAlgorithmIdentifier ::= SEQUENCE { | ||
111 | algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), | ||
112 | parameters ANY OPTIONAL | ||
113 | } | ||
114 | |||
115 | EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature }) | ||
116 | |||
117 | --- | ||
118 | --- X.500 Name | ||
119 | --- | ||
120 | Name ::= SEQUENCE OF RelativeDistinguishedName | ||
121 | |||
122 | RelativeDistinguishedName ::= SET OF AttributeValueAssertion | ||
123 | |||
124 | AttributeValueAssertion ::= SEQUENCE { | ||
125 | attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }), | ||
126 | attributeValue ANY | ||
127 | } | ||
diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c new file mode 100644 index 000000000000..c2091f7bd15d --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_key_type.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* Testing module to load key from trusted PKCS#7 message | ||
2 | * | ||
3 | * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "PKCS7key: "fmt | ||
13 | #include <linux/key.h> | ||
14 | #include <linux/key-type.h> | ||
15 | #include <crypto/pkcs7.h> | ||
16 | #include <keys/user-type.h> | ||
17 | #include <keys/system_keyring.h> | ||
18 | #include "pkcs7_parser.h" | ||
19 | |||
20 | /* | ||
21 | * Preparse a PKCS#7 wrapped and validated data blob. | ||
22 | */ | ||
23 | static int pkcs7_preparse(struct key_preparsed_payload *prep) | ||
24 | { | ||
25 | struct pkcs7_message *pkcs7; | ||
26 | const void *data, *saved_prep_data; | ||
27 | size_t datalen, saved_prep_datalen; | ||
28 | bool trusted; | ||
29 | int ret; | ||
30 | |||
31 | kenter(""); | ||
32 | |||
33 | saved_prep_data = prep->data; | ||
34 | saved_prep_datalen = prep->datalen; | ||
35 | pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen); | ||
36 | if (IS_ERR(pkcs7)) { | ||
37 | ret = PTR_ERR(pkcs7); | ||
38 | goto error; | ||
39 | } | ||
40 | |||
41 | ret = pkcs7_verify(pkcs7); | ||
42 | if (ret < 0) | ||
43 | goto error_free; | ||
44 | |||
45 | ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted); | ||
46 | if (ret < 0) | ||
47 | goto error_free; | ||
48 | if (!trusted) | ||
49 | pr_warn("PKCS#7 message doesn't chain back to a trusted key\n"); | ||
50 | |||
51 | ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false); | ||
52 | if (ret < 0) | ||
53 | goto error_free; | ||
54 | |||
55 | prep->data = data; | ||
56 | prep->datalen = datalen; | ||
57 | ret = user_preparse(prep); | ||
58 | prep->data = saved_prep_data; | ||
59 | prep->datalen = saved_prep_datalen; | ||
60 | |||
61 | error_free: | ||
62 | pkcs7_free_message(pkcs7); | ||
63 | error: | ||
64 | kleave(" = %d", ret); | ||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * user defined keys take an arbitrary string as the description and an | ||
70 | * arbitrary blob of data as the payload | ||
71 | */ | ||
72 | struct key_type key_type_pkcs7 = { | ||
73 | .name = "pkcs7_test", | ||
74 | .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, | ||
75 | .preparse = pkcs7_preparse, | ||
76 | .free_preparse = user_free_preparse, | ||
77 | .instantiate = generic_key_instantiate, | ||
78 | .match = user_match, | ||
79 | .revoke = user_revoke, | ||
80 | .destroy = user_destroy, | ||
81 | .describe = user_describe, | ||
82 | .read = user_read, | ||
83 | }; | ||
84 | |||
85 | /* | ||
86 | * Module stuff | ||
87 | */ | ||
88 | static int __init pkcs7_key_init(void) | ||
89 | { | ||
90 | return register_key_type(&key_type_pkcs7); | ||
91 | } | ||
92 | |||
93 | static void __exit pkcs7_key_cleanup(void) | ||
94 | { | ||
95 | unregister_key_type(&key_type_pkcs7); | ||
96 | } | ||
97 | |||
98 | module_init(pkcs7_key_init); | ||
99 | module_exit(pkcs7_key_cleanup); | ||
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c new file mode 100644 index 000000000000..42e56aa7d277 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_parser.c | |||
@@ -0,0 +1,396 @@ | |||
1 | /* PKCS#7 parser | ||
2 | * | ||
3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "PKCS7: "fmt | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/oid_registry.h> | ||
18 | #include "public_key.h" | ||
19 | #include "pkcs7_parser.h" | ||
20 | #include "pkcs7-asn1.h" | ||
21 | |||
22 | struct pkcs7_parse_context { | ||
23 | struct pkcs7_message *msg; /* Message being constructed */ | ||
24 | struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */ | ||
25 | struct pkcs7_signed_info **ppsinfo; | ||
26 | struct x509_certificate *certs; /* Certificate cache */ | ||
27 | struct x509_certificate **ppcerts; | ||
28 | unsigned long data; /* Start of data */ | ||
29 | enum OID last_oid; /* Last OID encountered */ | ||
30 | unsigned x509_index; | ||
31 | unsigned sinfo_index; | ||
32 | }; | ||
33 | |||
34 | /** | ||
35 | * pkcs7_free_message - Free a PKCS#7 message | ||
36 | * @pkcs7: The PKCS#7 message to free | ||
37 | */ | ||
38 | void pkcs7_free_message(struct pkcs7_message *pkcs7) | ||
39 | { | ||
40 | struct x509_certificate *cert; | ||
41 | struct pkcs7_signed_info *sinfo; | ||
42 | |||
43 | if (pkcs7) { | ||
44 | while (pkcs7->certs) { | ||
45 | cert = pkcs7->certs; | ||
46 | pkcs7->certs = cert->next; | ||
47 | x509_free_certificate(cert); | ||
48 | } | ||
49 | while (pkcs7->crl) { | ||
50 | cert = pkcs7->crl; | ||
51 | pkcs7->crl = cert->next; | ||
52 | x509_free_certificate(cert); | ||
53 | } | ||
54 | while (pkcs7->signed_infos) { | ||
55 | sinfo = pkcs7->signed_infos; | ||
56 | pkcs7->signed_infos = sinfo->next; | ||
57 | mpi_free(sinfo->sig.mpi[0]); | ||
58 | kfree(sinfo->sig.digest); | ||
59 | kfree(sinfo); | ||
60 | } | ||
61 | kfree(pkcs7); | ||
62 | } | ||
63 | } | ||
64 | EXPORT_SYMBOL_GPL(pkcs7_free_message); | ||
65 | |||
66 | /** | ||
67 | * pkcs7_parse_message - Parse a PKCS#7 message | ||
68 | * @data: The raw binary ASN.1 encoded message to be parsed | ||
69 | * @datalen: The size of the encoded message | ||
70 | */ | ||
71 | struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) | ||
72 | { | ||
73 | struct pkcs7_parse_context *ctx; | ||
74 | struct pkcs7_message *msg; | ||
75 | long ret; | ||
76 | |||
77 | ret = -ENOMEM; | ||
78 | msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); | ||
79 | if (!msg) | ||
80 | goto error_no_sig; | ||
81 | ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); | ||
82 | if (!ctx) | ||
83 | goto error_no_ctx; | ||
84 | ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); | ||
85 | if (!ctx->sinfo) | ||
86 | goto error_no_sinfo; | ||
87 | |||
88 | ctx->msg = msg; | ||
89 | ctx->data = (unsigned long)data; | ||
90 | ctx->ppcerts = &ctx->certs; | ||
91 | ctx->ppsinfo = &ctx->msg->signed_infos; | ||
92 | |||
93 | /* Attempt to decode the signature */ | ||
94 | ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); | ||
95 | if (ret < 0) | ||
96 | goto error_decode; | ||
97 | |||
98 | while (ctx->certs) { | ||
99 | struct x509_certificate *cert = ctx->certs; | ||
100 | ctx->certs = cert->next; | ||
101 | x509_free_certificate(cert); | ||
102 | } | ||
103 | mpi_free(ctx->sinfo->sig.mpi[0]); | ||
104 | kfree(ctx->sinfo->sig.digest); | ||
105 | kfree(ctx->sinfo); | ||
106 | kfree(ctx); | ||
107 | return msg; | ||
108 | |||
109 | error_decode: | ||
110 | mpi_free(ctx->sinfo->sig.mpi[0]); | ||
111 | kfree(ctx->sinfo->sig.digest); | ||
112 | kfree(ctx->sinfo); | ||
113 | error_no_sinfo: | ||
114 | kfree(ctx); | ||
115 | error_no_ctx: | ||
116 | pkcs7_free_message(msg); | ||
117 | error_no_sig: | ||
118 | return ERR_PTR(ret); | ||
119 | } | ||
120 | EXPORT_SYMBOL_GPL(pkcs7_parse_message); | ||
121 | |||
122 | /** | ||
123 | * pkcs7_get_content_data - Get access to the PKCS#7 content | ||
124 | * @pkcs7: The preparsed PKCS#7 message to access | ||
125 | * @_data: Place to return a pointer to the data | ||
126 | * @_data_len: Place to return the data length | ||
127 | * @want_wrapper: True if the ASN.1 object header should be included in the data | ||
128 | * | ||
129 | * Get access to the data content of the PKCS#7 message, including, optionally, | ||
130 | * the header of the ASN.1 object that contains it. Returns -ENODATA if the | ||
131 | * data object was missing from the message. | ||
132 | */ | ||
133 | int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, | ||
134 | const void **_data, size_t *_data_len, | ||
135 | bool want_wrapper) | ||
136 | { | ||
137 | size_t wrapper; | ||
138 | |||
139 | if (!pkcs7->data) | ||
140 | return -ENODATA; | ||
141 | |||
142 | wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; | ||
143 | *_data = pkcs7->data - wrapper; | ||
144 | *_data_len = pkcs7->data_len + wrapper; | ||
145 | return 0; | ||
146 | } | ||
147 | EXPORT_SYMBOL_GPL(pkcs7_get_content_data); | ||
148 | |||
149 | /* | ||
150 | * Note an OID when we find one for later processing when we know how | ||
151 | * to interpret it. | ||
152 | */ | ||
153 | int pkcs7_note_OID(void *context, size_t hdrlen, | ||
154 | unsigned char tag, | ||
155 | const void *value, size_t vlen) | ||
156 | { | ||
157 | struct pkcs7_parse_context *ctx = context; | ||
158 | |||
159 | ctx->last_oid = look_up_OID(value, vlen); | ||
160 | if (ctx->last_oid == OID__NR) { | ||
161 | char buffer[50]; | ||
162 | sprint_oid(value, vlen, buffer, sizeof(buffer)); | ||
163 | printk("PKCS7: Unknown OID: [%lu] %s\n", | ||
164 | (unsigned long)value - ctx->data, buffer); | ||
165 | } | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | /* | ||
170 | * Note the digest algorithm for the signature. | ||
171 | */ | ||
172 | int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, | ||
173 | unsigned char tag, | ||
174 | const void *value, size_t vlen) | ||
175 | { | ||
176 | struct pkcs7_parse_context *ctx = context; | ||
177 | |||
178 | switch (ctx->last_oid) { | ||
179 | case OID_md4: | ||
180 | ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4; | ||
181 | break; | ||
182 | case OID_md5: | ||
183 | ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5; | ||
184 | break; | ||
185 | case OID_sha1: | ||
186 | ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1; | ||
187 | break; | ||
188 | case OID_sha256: | ||
189 | ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256; | ||
190 | break; | ||
191 | default: | ||
192 | printk("Unsupported digest algo: %u\n", ctx->last_oid); | ||
193 | return -ENOPKG; | ||
194 | } | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * Note the public key algorithm for the signature. | ||
200 | */ | ||
201 | int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, | ||
202 | unsigned char tag, | ||
203 | const void *value, size_t vlen) | ||
204 | { | ||
205 | struct pkcs7_parse_context *ctx = context; | ||
206 | |||
207 | switch (ctx->last_oid) { | ||
208 | case OID_rsaEncryption: | ||
209 | ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA; | ||
210 | break; | ||
211 | default: | ||
212 | printk("Unsupported pkey algo: %u\n", ctx->last_oid); | ||
213 | return -ENOPKG; | ||
214 | } | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * Extract a certificate and store it in the context. | ||
220 | */ | ||
221 | int pkcs7_extract_cert(void *context, size_t hdrlen, | ||
222 | unsigned char tag, | ||
223 | const void *value, size_t vlen) | ||
224 | { | ||
225 | struct pkcs7_parse_context *ctx = context; | ||
226 | struct x509_certificate *x509; | ||
227 | |||
228 | if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) { | ||
229 | pr_debug("Cert began with tag %02x at %lu\n", | ||
230 | tag, (unsigned long)ctx - ctx->data); | ||
231 | return -EBADMSG; | ||
232 | } | ||
233 | |||
234 | /* We have to correct for the header so that the X.509 parser can start | ||
235 | * from the beginning. Note that since X.509 stipulates DER, there | ||
236 | * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which | ||
237 | * stipulates BER). | ||
238 | */ | ||
239 | value -= hdrlen; | ||
240 | vlen += hdrlen; | ||
241 | |||
242 | if (((u8*)value)[1] == 0x80) | ||
243 | vlen += 2; /* Indefinite length - there should be an EOC */ | ||
244 | |||
245 | x509 = x509_cert_parse(value, vlen); | ||
246 | if (IS_ERR(x509)) | ||
247 | return PTR_ERR(x509); | ||
248 | |||
249 | pr_debug("Got cert for %s\n", x509->subject); | ||
250 | pr_debug("- fingerprint %s\n", x509->fingerprint); | ||
251 | |||
252 | x509->index = ++ctx->x509_index; | ||
253 | *ctx->ppcerts = x509; | ||
254 | ctx->ppcerts = &x509->next; | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | /* | ||
259 | * Save the certificate list | ||
260 | */ | ||
261 | int pkcs7_note_certificate_list(void *context, size_t hdrlen, | ||
262 | unsigned char tag, | ||
263 | const void *value, size_t vlen) | ||
264 | { | ||
265 | struct pkcs7_parse_context *ctx = context; | ||
266 | |||
267 | pr_devel("Got cert list (%02x)\n", tag); | ||
268 | |||
269 | *ctx->ppcerts = ctx->msg->certs; | ||
270 | ctx->msg->certs = ctx->certs; | ||
271 | ctx->certs = NULL; | ||
272 | ctx->ppcerts = &ctx->certs; | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Extract the data from the message and store that and its content type OID in | ||
278 | * the context. | ||
279 | */ | ||
280 | int pkcs7_note_data(void *context, size_t hdrlen, | ||
281 | unsigned char tag, | ||
282 | const void *value, size_t vlen) | ||
283 | { | ||
284 | struct pkcs7_parse_context *ctx = context; | ||
285 | |||
286 | pr_debug("Got data\n"); | ||
287 | |||
288 | ctx->msg->data = value; | ||
289 | ctx->msg->data_len = vlen; | ||
290 | ctx->msg->data_hdrlen = hdrlen; | ||
291 | ctx->msg->data_type = ctx->last_oid; | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * Parse authenticated attributes | ||
297 | */ | ||
298 | int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen, | ||
299 | unsigned char tag, | ||
300 | const void *value, size_t vlen) | ||
301 | { | ||
302 | struct pkcs7_parse_context *ctx = context; | ||
303 | |||
304 | pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); | ||
305 | |||
306 | switch (ctx->last_oid) { | ||
307 | case OID_messageDigest: | ||
308 | if (tag != ASN1_OTS) | ||
309 | return -EBADMSG; | ||
310 | ctx->sinfo->msgdigest = value; | ||
311 | ctx->sinfo->msgdigest_len = vlen; | ||
312 | return 0; | ||
313 | default: | ||
314 | return 0; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /* | ||
319 | * Note the set of auth attributes for digestion purposes [RFC2315 9.3] | ||
320 | */ | ||
321 | int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, | ||
322 | unsigned char tag, | ||
323 | const void *value, size_t vlen) | ||
324 | { | ||
325 | struct pkcs7_parse_context *ctx = context; | ||
326 | |||
327 | /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ | ||
328 | ctx->sinfo->authattrs = value - (hdrlen - 1); | ||
329 | ctx->sinfo->authattrs_len = vlen + (hdrlen - 1); | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * Note the issuing certificate serial number | ||
335 | */ | ||
336 | int pkcs7_sig_note_serial(void *context, size_t hdrlen, | ||
337 | unsigned char tag, | ||
338 | const void *value, size_t vlen) | ||
339 | { | ||
340 | struct pkcs7_parse_context *ctx = context; | ||
341 | ctx->sinfo->raw_serial = value; | ||
342 | ctx->sinfo->raw_serial_size = vlen; | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Note the issuer's name | ||
348 | */ | ||
349 | int pkcs7_sig_note_issuer(void *context, size_t hdrlen, | ||
350 | unsigned char tag, | ||
351 | const void *value, size_t vlen) | ||
352 | { | ||
353 | struct pkcs7_parse_context *ctx = context; | ||
354 | ctx->sinfo->raw_issuer = value; | ||
355 | ctx->sinfo->raw_issuer_size = vlen; | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Note the signature data | ||
361 | */ | ||
362 | int pkcs7_sig_note_signature(void *context, size_t hdrlen, | ||
363 | unsigned char tag, | ||
364 | const void *value, size_t vlen) | ||
365 | { | ||
366 | struct pkcs7_parse_context *ctx = context; | ||
367 | MPI mpi; | ||
368 | |||
369 | BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA); | ||
370 | |||
371 | mpi = mpi_read_raw_data(value, vlen); | ||
372 | if (!mpi) | ||
373 | return -ENOMEM; | ||
374 | |||
375 | ctx->sinfo->sig.mpi[0] = mpi; | ||
376 | ctx->sinfo->sig.nr_mpi = 1; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | /* | ||
381 | * Note a signature information block | ||
382 | */ | ||
383 | int pkcs7_note_signed_info(void *context, size_t hdrlen, | ||
384 | unsigned char tag, | ||
385 | const void *value, size_t vlen) | ||
386 | { | ||
387 | struct pkcs7_parse_context *ctx = context; | ||
388 | |||
389 | ctx->sinfo->index = ++ctx->sinfo_index; | ||
390 | *ctx->ppsinfo = ctx->sinfo; | ||
391 | ctx->ppsinfo = &ctx->sinfo->next; | ||
392 | ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); | ||
393 | if (!ctx->sinfo) | ||
394 | return -ENOMEM; | ||
395 | return 0; | ||
396 | } | ||
diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h new file mode 100644 index 000000000000..d25f4d15370f --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_parser.h | |||
@@ -0,0 +1,61 @@ | |||
1 | /* PKCS#7 crypto data parser internal definitions | ||
2 | * | ||
3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/oid_registry.h> | ||
13 | #include <crypto/pkcs7.h> | ||
14 | #include "x509_parser.h" | ||
15 | |||
16 | #define kenter(FMT, ...) \ | ||
17 | pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) | ||
18 | #define kleave(FMT, ...) \ | ||
19 | pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) | ||
20 | |||
21 | struct pkcs7_signed_info { | ||
22 | struct pkcs7_signed_info *next; | ||
23 | struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ | ||
24 | unsigned index; | ||
25 | bool trusted; | ||
26 | |||
27 | /* Message digest - the digest of the Content Data (or NULL) */ | ||
28 | const void *msgdigest; | ||
29 | unsigned msgdigest_len; | ||
30 | |||
31 | /* Authenticated Attribute data (or NULL) */ | ||
32 | unsigned authattrs_len; | ||
33 | const void *authattrs; | ||
34 | |||
35 | /* Issuing cert serial number and issuer's name */ | ||
36 | const void *raw_serial; | ||
37 | unsigned raw_serial_size; | ||
38 | unsigned raw_issuer_size; | ||
39 | const void *raw_issuer; | ||
40 | |||
41 | /* Message signature. | ||
42 | * | ||
43 | * This contains the generated digest of _either_ the Content Data or | ||
44 | * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of | ||
45 | * the attributes contains the digest of the the Content Data within | ||
46 | * it. | ||
47 | */ | ||
48 | struct public_key_signature sig; | ||
49 | }; | ||
50 | |||
51 | struct pkcs7_message { | ||
52 | struct x509_certificate *certs; /* Certificate list */ | ||
53 | struct x509_certificate *crl; /* Revocation list */ | ||
54 | struct pkcs7_signed_info *signed_infos; | ||
55 | |||
56 | /* Content Data (or NULL) */ | ||
57 | enum OID data_type; /* Type of Data */ | ||
58 | size_t data_len; /* Length of Data */ | ||
59 | size_t data_hdrlen; /* Length of Data ASN.1 header */ | ||
60 | const void *data; /* Content Data (or 0) */ | ||
61 | }; | ||
diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c new file mode 100644 index 000000000000..b6b045131403 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_trust.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* Validate the trust chain of a PKCS#7 message. | ||
2 | * | ||
3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "PKCS7: "fmt | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/asn1.h> | ||
18 | #include <linux/key.h> | ||
19 | #include <keys/asymmetric-type.h> | ||
20 | #include "public_key.h" | ||
21 | #include "pkcs7_parser.h" | ||
22 | |||
23 | /* | ||
24 | * Request an asymmetric key. | ||
25 | */ | ||
26 | static struct key *pkcs7_request_asymmetric_key( | ||
27 | struct key *keyring, | ||
28 | const char *signer, size_t signer_len, | ||
29 | const char *authority, size_t auth_len) | ||
30 | { | ||
31 | key_ref_t key; | ||
32 | char *id; | ||
33 | |||
34 | kenter(",%zu,,%zu", signer_len, auth_len); | ||
35 | |||
36 | /* Construct an identifier. */ | ||
37 | id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); | ||
38 | if (!id) | ||
39 | return ERR_PTR(-ENOMEM); | ||
40 | |||
41 | memcpy(id, signer, signer_len); | ||
42 | id[signer_len + 0] = ':'; | ||
43 | id[signer_len + 1] = ' '; | ||
44 | memcpy(id + signer_len + 2, authority, auth_len); | ||
45 | id[signer_len + 2 + auth_len] = 0; | ||
46 | |||
47 | pr_debug("Look up: \"%s\"\n", id); | ||
48 | |||
49 | key = keyring_search(make_key_ref(keyring, 1), | ||
50 | &key_type_asymmetric, id); | ||
51 | if (IS_ERR(key)) | ||
52 | pr_debug("Request for module key '%s' err %ld\n", | ||
53 | id, PTR_ERR(key)); | ||
54 | kfree(id); | ||
55 | |||
56 | if (IS_ERR(key)) { | ||
57 | switch (PTR_ERR(key)) { | ||
58 | /* Hide some search errors */ | ||
59 | case -EACCES: | ||
60 | case -ENOTDIR: | ||
61 | case -EAGAIN: | ||
62 | return ERR_PTR(-ENOKEY); | ||
63 | default: | ||
64 | return ERR_CAST(key); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); | ||
69 | return key_ref_to_ptr(key); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * Check the trust on one PKCS#7 SignedInfo block. | ||
74 | */ | ||
75 | int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, | ||
76 | struct pkcs7_signed_info *sinfo, | ||
77 | struct key *trust_keyring) | ||
78 | { | ||
79 | struct public_key_signature *sig = &sinfo->sig; | ||
80 | struct x509_certificate *x509, *last = NULL, *p; | ||
81 | struct key *key; | ||
82 | bool trusted; | ||
83 | int ret; | ||
84 | |||
85 | kenter(",%u,", sinfo->index); | ||
86 | |||
87 | for (x509 = sinfo->signer; x509; x509 = x509->signer) { | ||
88 | if (x509->seen) { | ||
89 | if (x509->verified) { | ||
90 | trusted = x509->trusted; | ||
91 | goto verified; | ||
92 | } | ||
93 | kleave(" = -ENOKEY [cached]"); | ||
94 | return -ENOKEY; | ||
95 | } | ||
96 | x509->seen = true; | ||
97 | |||
98 | /* Look to see if this certificate is present in the trusted | ||
99 | * keys. | ||
100 | */ | ||
101 | key = pkcs7_request_asymmetric_key( | ||
102 | trust_keyring, | ||
103 | x509->subject, strlen(x509->subject), | ||
104 | x509->fingerprint, strlen(x509->fingerprint)); | ||
105 | if (!IS_ERR(key)) | ||
106 | /* One of the X.509 certificates in the PKCS#7 message | ||
107 | * is apparently the same as one we already trust. | ||
108 | * Verify that the trusted variant can also validate | ||
109 | * the signature on the descendant. | ||
110 | */ | ||
111 | goto matched; | ||
112 | if (key == ERR_PTR(-ENOMEM)) | ||
113 | return -ENOMEM; | ||
114 | |||
115 | /* Self-signed certificates form roots of their own, and if we | ||
116 | * don't know them, then we can't accept them. | ||
117 | */ | ||
118 | if (x509->next == x509) { | ||
119 | kleave(" = -ENOKEY [unknown self-signed]"); | ||
120 | return -ENOKEY; | ||
121 | } | ||
122 | |||
123 | might_sleep(); | ||
124 | last = x509; | ||
125 | sig = &last->sig; | ||
126 | } | ||
127 | |||
128 | /* No match - see if the root certificate has a signer amongst the | ||
129 | * trusted keys. | ||
130 | */ | ||
131 | if (!last || !last->issuer || !last->authority) { | ||
132 | kleave(" = -ENOKEY [no backref]"); | ||
133 | return -ENOKEY; | ||
134 | } | ||
135 | |||
136 | key = pkcs7_request_asymmetric_key( | ||
137 | trust_keyring, | ||
138 | last->issuer, strlen(last->issuer), | ||
139 | last->authority, strlen(last->authority)); | ||
140 | if (IS_ERR(key)) | ||
141 | return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; | ||
142 | x509 = last; | ||
143 | |||
144 | matched: | ||
145 | ret = verify_signature(key, sig); | ||
146 | trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); | ||
147 | key_put(key); | ||
148 | if (ret < 0) { | ||
149 | if (ret == -ENOMEM) | ||
150 | return ret; | ||
151 | kleave(" = -EKEYREJECTED [verify %d]", ret); | ||
152 | return -EKEYREJECTED; | ||
153 | } | ||
154 | |||
155 | verified: | ||
156 | x509->verified = true; | ||
157 | for (p = sinfo->signer; p != x509; p = p->signer) { | ||
158 | p->verified = true; | ||
159 | p->trusted = trusted; | ||
160 | } | ||
161 | sinfo->trusted = trusted; | ||
162 | kleave(" = 0"); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | /** | ||
167 | * pkcs7_validate_trust - Validate PKCS#7 trust chain | ||
168 | * @pkcs7: The PKCS#7 certificate to validate | ||
169 | * @trust_keyring: Signing certificates to use as starting points | ||
170 | * @_trusted: Set to true if trustworth, false otherwise | ||
171 | * | ||
172 | * Validate that the certificate chain inside the PKCS#7 message intersects | ||
173 | * keys we already know and trust. | ||
174 | * | ||
175 | * Returns, in order of descending priority: | ||
176 | * | ||
177 | * (*) -EKEYREJECTED if a signature failed to match for which we have a valid | ||
178 | * key, or: | ||
179 | * | ||
180 | * (*) 0 if at least one signature chain intersects with the keys in the trust | ||
181 | * keyring, or: | ||
182 | * | ||
183 | * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a | ||
184 | * chain. | ||
185 | * | ||
186 | * (*) -ENOKEY if we couldn't find a match for any of the signature chains in | ||
187 | * the message. | ||
188 | * | ||
189 | * May also return -ENOMEM. | ||
190 | */ | ||
191 | int pkcs7_validate_trust(struct pkcs7_message *pkcs7, | ||
192 | struct key *trust_keyring, | ||
193 | bool *_trusted) | ||
194 | { | ||
195 | struct pkcs7_signed_info *sinfo; | ||
196 | struct x509_certificate *p; | ||
197 | int cached_ret = 0, ret; | ||
198 | |||
199 | for (p = pkcs7->certs; p; p = p->next) | ||
200 | p->seen = false; | ||
201 | |||
202 | for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { | ||
203 | ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); | ||
204 | if (ret < 0) { | ||
205 | if (ret == -ENOPKG) { | ||
206 | cached_ret = -ENOPKG; | ||
207 | } else if (ret == -ENOKEY) { | ||
208 | if (cached_ret == 0) | ||
209 | cached_ret = -ENOKEY; | ||
210 | } else { | ||
211 | return ret; | ||
212 | } | ||
213 | } | ||
214 | *_trusted |= sinfo->trusted; | ||
215 | } | ||
216 | |||
217 | return cached_ret; | ||
218 | } | ||
219 | EXPORT_SYMBOL_GPL(pkcs7_validate_trust); | ||
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c new file mode 100644 index 000000000000..51ff36f3a913 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_verify.c | |||
@@ -0,0 +1,323 @@ | |||
1 | /* Verify the signature on a PKCS#7 message. | ||
2 | * | ||
3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #define pr_fmt(fmt) "PKCS7: "fmt | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/export.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/asn1.h> | ||
18 | #include <crypto/hash.h> | ||
19 | #include "public_key.h" | ||
20 | #include "pkcs7_parser.h" | ||
21 | |||
22 | /* | ||
23 | * Digest the relevant parts of the PKCS#7 data | ||
24 | */ | ||
25 | static int pkcs7_digest(struct pkcs7_message *pkcs7, | ||
26 | struct pkcs7_signed_info *sinfo) | ||
27 | { | ||
28 | struct crypto_shash *tfm; | ||
29 | struct shash_desc *desc; | ||
30 | size_t digest_size, desc_size; | ||
31 | void *digest; | ||
32 | int ret; | ||
33 | |||
34 | kenter(",%u,%u", sinfo->index, sinfo->sig.pkey_hash_algo); | ||
35 | |||
36 | if (sinfo->sig.pkey_hash_algo >= PKEY_HASH__LAST || | ||
37 | !hash_algo_name[sinfo->sig.pkey_hash_algo]) | ||
38 | return -ENOPKG; | ||
39 | |||
40 | /* Allocate the hashing algorithm we're going to need and find out how | ||
41 | * big the hash operational data will be. | ||
42 | */ | ||
43 | tfm = crypto_alloc_shash(hash_algo_name[sinfo->sig.pkey_hash_algo], | ||
44 | 0, 0); | ||
45 | if (IS_ERR(tfm)) | ||
46 | return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); | ||
47 | |||
48 | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); | ||
49 | sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm); | ||
50 | |||
51 | ret = -ENOMEM; | ||
52 | digest = kzalloc(digest_size + desc_size, GFP_KERNEL); | ||
53 | if (!digest) | ||
54 | goto error_no_desc; | ||
55 | |||
56 | desc = digest + digest_size; | ||
57 | desc->tfm = tfm; | ||
58 | desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; | ||
59 | |||
60 | /* Digest the message [RFC2315 9.3] */ | ||
61 | ret = crypto_shash_init(desc); | ||
62 | if (ret < 0) | ||
63 | goto error; | ||
64 | ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest); | ||
65 | if (ret < 0) | ||
66 | goto error; | ||
67 | pr_devel("MsgDigest = [%*ph]\n", 8, digest); | ||
68 | |||
69 | /* However, if there are authenticated attributes, there must be a | ||
70 | * message digest attribute amongst them which corresponds to the | ||
71 | * digest we just calculated. | ||
72 | */ | ||
73 | if (sinfo->msgdigest) { | ||
74 | u8 tag; | ||
75 | |||
76 | if (sinfo->msgdigest_len != sinfo->sig.digest_size) { | ||
77 | pr_debug("Sig %u: Invalid digest size (%u)\n", | ||
78 | sinfo->index, sinfo->msgdigest_len); | ||
79 | ret = -EBADMSG; | ||
80 | goto error; | ||
81 | } | ||
82 | |||
83 | if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) { | ||
84 | pr_debug("Sig %u: Message digest doesn't match\n", | ||
85 | sinfo->index); | ||
86 | ret = -EKEYREJECTED; | ||
87 | goto error; | ||
88 | } | ||
89 | |||
90 | /* We then calculate anew, using the authenticated attributes | ||
91 | * as the contents of the digest instead. Note that we need to | ||
92 | * convert the attributes from a CONT.0 into a SET before we | ||
93 | * hash it. | ||
94 | */ | ||
95 | memset(digest, 0, sinfo->sig.digest_size); | ||
96 | |||
97 | ret = crypto_shash_init(desc); | ||
98 | if (ret < 0) | ||
99 | goto error; | ||
100 | tag = ASN1_CONS_BIT | ASN1_SET; | ||
101 | ret = crypto_shash_update(desc, &tag, 1); | ||
102 | if (ret < 0) | ||
103 | goto error; | ||
104 | ret = crypto_shash_finup(desc, sinfo->authattrs, | ||
105 | sinfo->authattrs_len, digest); | ||
106 | if (ret < 0) | ||
107 | goto error; | ||
108 | pr_devel("AADigest = [%*ph]\n", 8, digest); | ||
109 | } | ||
110 | |||
111 | sinfo->sig.digest = digest; | ||
112 | digest = NULL; | ||
113 | |||
114 | error: | ||
115 | kfree(digest); | ||
116 | error_no_desc: | ||
117 | crypto_free_shash(tfm); | ||
118 | kleave(" = %d", ret); | ||
119 | return ret; | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 | ||
124 | * uses the issuer's name and the issuing certificate serial number for | ||
125 | * matching purposes. These must match the certificate issuer's name (not | ||
126 | * subject's name) and the certificate serial number [RFC 2315 6.7]. | ||
127 | */ | ||
128 | static int pkcs7_find_key(struct pkcs7_message *pkcs7, | ||
129 | struct pkcs7_signed_info *sinfo) | ||
130 | { | ||
131 | struct x509_certificate *x509; | ||
132 | unsigned certix = 1; | ||
133 | |||
134 | kenter("%u,%u,%u", | ||
135 | sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size); | ||
136 | |||
137 | for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { | ||
138 | /* I'm _assuming_ that the generator of the PKCS#7 message will | ||
139 | * encode the fields from the X.509 cert in the same way in the | ||
140 | * PKCS#7 message - but I can't be 100% sure of that. It's | ||
141 | * possible this will need element-by-element comparison. | ||
142 | */ | ||
143 | if (x509->raw_serial_size != sinfo->raw_serial_size || | ||
144 | memcmp(x509->raw_serial, sinfo->raw_serial, | ||
145 | sinfo->raw_serial_size) != 0) | ||
146 | continue; | ||
147 | pr_devel("Sig %u: Found cert serial match X.509[%u]\n", | ||
148 | sinfo->index, certix); | ||
149 | |||
150 | if (x509->raw_issuer_size != sinfo->raw_issuer_size || | ||
151 | memcmp(x509->raw_issuer, sinfo->raw_issuer, | ||
152 | sinfo->raw_issuer_size) != 0) { | ||
153 | pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n", | ||
154 | sinfo->index); | ||
155 | continue; | ||
156 | } | ||
157 | |||
158 | if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { | ||
159 | pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", | ||
160 | sinfo->index); | ||
161 | continue; | ||
162 | } | ||
163 | |||
164 | sinfo->signer = x509; | ||
165 | return 0; | ||
166 | } | ||
167 | pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", | ||
168 | sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial); | ||
169 | return -ENOKEY; | ||
170 | } | ||
171 | |||
172 | /* | ||
173 | * Verify the internal certificate chain as best we can. | ||
174 | */ | ||
175 | static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, | ||
176 | struct pkcs7_signed_info *sinfo) | ||
177 | { | ||
178 | struct x509_certificate *x509 = sinfo->signer, *p; | ||
179 | int ret; | ||
180 | |||
181 | kenter(""); | ||
182 | |||
183 | for (p = pkcs7->certs; p; p = p->next) | ||
184 | p->seen = false; | ||
185 | |||
186 | for (;;) { | ||
187 | pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint); | ||
188 | x509->seen = true; | ||
189 | ret = x509_get_sig_params(x509); | ||
190 | if (ret < 0) | ||
191 | return ret; | ||
192 | |||
193 | if (x509->issuer) | ||
194 | pr_debug("- issuer %s\n", x509->issuer); | ||
195 | if (x509->authority) | ||
196 | pr_debug("- authkeyid %s\n", x509->authority); | ||
197 | |||
198 | if (!x509->authority || | ||
199 | (x509->subject && | ||
200 | strcmp(x509->subject, x509->issuer) == 0)) { | ||
201 | /* If there's no authority certificate specified, then | ||
202 | * the certificate must be self-signed and is the root | ||
203 | * of the chain. Likewise if the cert is its own | ||
204 | * authority. | ||
205 | */ | ||
206 | pr_debug("- no auth?\n"); | ||
207 | if (x509->raw_subject_size != x509->raw_issuer_size || | ||
208 | memcmp(x509->raw_subject, x509->raw_issuer, | ||
209 | x509->raw_issuer_size) != 0) | ||
210 | return 0; | ||
211 | |||
212 | ret = x509_check_signature(x509->pub, x509); | ||
213 | if (ret < 0) | ||
214 | return ret; | ||
215 | x509->signer = x509; | ||
216 | pr_debug("- self-signed\n"); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | /* Look through the X.509 certificates in the PKCS#7 message's | ||
221 | * list to see if the next one is there. | ||
222 | */ | ||
223 | pr_debug("- want %s\n", x509->authority); | ||
224 | for (p = pkcs7->certs; p; p = p->next) { | ||
225 | pr_debug("- cmp [%u] %s\n", p->index, p->fingerprint); | ||
226 | if (p->raw_subject_size == x509->raw_issuer_size && | ||
227 | strcmp(p->fingerprint, x509->authority) == 0 && | ||
228 | memcmp(p->raw_subject, x509->raw_issuer, | ||
229 | x509->raw_issuer_size) == 0) | ||
230 | goto found_issuer; | ||
231 | } | ||
232 | |||
233 | /* We didn't find the root of this chain */ | ||
234 | pr_debug("- top\n"); | ||
235 | return 0; | ||
236 | |||
237 | found_issuer: | ||
238 | pr_debug("- issuer %s\n", p->subject); | ||
239 | if (p->seen) { | ||
240 | pr_warn("Sig %u: X.509 chain contains loop\n", | ||
241 | sinfo->index); | ||
242 | return 0; | ||
243 | } | ||
244 | ret = x509_check_signature(p->pub, x509); | ||
245 | if (ret < 0) | ||
246 | return ret; | ||
247 | x509->signer = p; | ||
248 | if (x509 == p) { | ||
249 | pr_debug("- self-signed\n"); | ||
250 | return 0; | ||
251 | } | ||
252 | x509 = p; | ||
253 | might_sleep(); | ||
254 | } | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Verify one signed information block from a PKCS#7 message. | ||
259 | */ | ||
260 | static int pkcs7_verify_one(struct pkcs7_message *pkcs7, | ||
261 | struct pkcs7_signed_info *sinfo) | ||
262 | { | ||
263 | int ret; | ||
264 | |||
265 | kenter(",%u", sinfo->index); | ||
266 | |||
267 | /* First of all, digest the data in the PKCS#7 message and the | ||
268 | * signed information block | ||
269 | */ | ||
270 | ret = pkcs7_digest(pkcs7, sinfo); | ||
271 | if (ret < 0) | ||
272 | return ret; | ||
273 | |||
274 | /* Find the key for the signature */ | ||
275 | ret = pkcs7_find_key(pkcs7, sinfo); | ||
276 | if (ret < 0) | ||
277 | return ret; | ||
278 | |||
279 | pr_devel("Using X.509[%u] for sig %u\n", | ||
280 | sinfo->signer->index, sinfo->index); | ||
281 | |||
282 | /* Verify the PKCS#7 binary against the key */ | ||
283 | ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); | ||
284 | if (ret < 0) | ||
285 | return ret; | ||
286 | |||
287 | pr_devel("Verified signature %u\n", sinfo->index); | ||
288 | |||
289 | /* Verify the internal certificate chain */ | ||
290 | return pkcs7_verify_sig_chain(pkcs7, sinfo); | ||
291 | } | ||
292 | |||
293 | /** | ||
294 | * pkcs7_verify - Verify a PKCS#7 message | ||
295 | * @pkcs7: The PKCS#7 message to be verified | ||
296 | */ | ||
297 | int pkcs7_verify(struct pkcs7_message *pkcs7) | ||
298 | { | ||
299 | struct pkcs7_signed_info *sinfo; | ||
300 | struct x509_certificate *x509; | ||
301 | int ret, n; | ||
302 | |||
303 | kenter(""); | ||
304 | |||
305 | for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) { | ||
306 | ret = x509_get_sig_params(x509); | ||
307 | if (ret < 0) | ||
308 | return ret; | ||
309 | pr_debug("X.509[%u] %s\n", n, x509->authority); | ||
310 | } | ||
311 | |||
312 | for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { | ||
313 | ret = pkcs7_verify_one(pkcs7, sinfo); | ||
314 | if (ret < 0) { | ||
315 | kleave(" = %d", ret); | ||
316 | return ret; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | kleave(" = 0"); | ||
321 | return 0; | ||
322 | } | ||
323 | EXPORT_SYMBOL_GPL(pkcs7_verify); | ||
diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1 index bf32b3dff088..aae0cde414e2 100644 --- a/crypto/asymmetric_keys/x509.asn1 +++ b/crypto/asymmetric_keys/x509.asn1 | |||
@@ -6,7 +6,7 @@ Certificate ::= SEQUENCE { | |||
6 | 6 | ||
7 | TBSCertificate ::= SEQUENCE { | 7 | TBSCertificate ::= SEQUENCE { |
8 | version [ 0 ] Version DEFAULT, | 8 | version [ 0 ] Version DEFAULT, |
9 | serialNumber CertificateSerialNumber, | 9 | serialNumber CertificateSerialNumber ({ x509_note_serial }), |
10 | signature AlgorithmIdentifier ({ x509_note_pkey_algo }), | 10 | signature AlgorithmIdentifier ({ x509_note_pkey_algo }), |
11 | issuer Name ({ x509_note_issuer }), | 11 | issuer Name ({ x509_note_issuer }), |
12 | validity Validity, | 12 | validity Validity, |
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 29893162497c..ac72348c186a 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | #define pr_fmt(fmt) "X.509: "fmt | 12 | #define pr_fmt(fmt) "X.509: "fmt |
13 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
14 | #include <linux/export.h> | ||
14 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
15 | #include <linux/err.h> | 16 | #include <linux/err.h> |
16 | #include <linux/oid_registry.h> | 17 | #include <linux/oid_registry.h> |
@@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert) | |||
52 | kfree(cert); | 53 | kfree(cert); |
53 | } | 54 | } |
54 | } | 55 | } |
56 | EXPORT_SYMBOL_GPL(x509_free_certificate); | ||
55 | 57 | ||
56 | /* | 58 | /* |
57 | * Parse an X.509 certificate | 59 | * Parse an X.509 certificate |
@@ -97,6 +99,7 @@ error_no_ctx: | |||
97 | error_no_cert: | 99 | error_no_cert: |
98 | return ERR_PTR(ret); | 100 | return ERR_PTR(ret); |
99 | } | 101 | } |
102 | EXPORT_SYMBOL_GPL(x509_cert_parse); | ||
100 | 103 | ||
101 | /* | 104 | /* |
102 | * Note an OID when we find one for later processing when we know how | 105 | * Note an OID when we find one for later processing when we know how |
@@ -211,6 +214,19 @@ int x509_note_signature(void *context, size_t hdrlen, | |||
211 | } | 214 | } |
212 | 215 | ||
213 | /* | 216 | /* |
217 | * Note the certificate serial number | ||
218 | */ | ||
219 | int x509_note_serial(void *context, size_t hdrlen, | ||
220 | unsigned char tag, | ||
221 | const void *value, size_t vlen) | ||
222 | { | ||
223 | struct x509_parse_context *ctx = context; | ||
224 | ctx->cert->raw_serial = value; | ||
225 | ctx->cert->raw_serial_size = vlen; | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | /* | ||
214 | * Note some of the name segments from which we'll fabricate a name. | 230 | * Note some of the name segments from which we'll fabricate a name. |
215 | */ | 231 | */ |
216 | int x509_extract_name_segment(void *context, size_t hdrlen, | 232 | int x509_extract_name_segment(void *context, size_t hdrlen, |
@@ -322,6 +338,8 @@ int x509_note_issuer(void *context, size_t hdrlen, | |||
322 | const void *value, size_t vlen) | 338 | const void *value, size_t vlen) |
323 | { | 339 | { |
324 | struct x509_parse_context *ctx = context; | 340 | struct x509_parse_context *ctx = context; |
341 | ctx->cert->raw_issuer = value; | ||
342 | ctx->cert->raw_issuer_size = vlen; | ||
325 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); | 343 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); |
326 | } | 344 | } |
327 | 345 | ||
@@ -330,6 +348,8 @@ int x509_note_subject(void *context, size_t hdrlen, | |||
330 | const void *value, size_t vlen) | 348 | const void *value, size_t vlen) |
331 | { | 349 | { |
332 | struct x509_parse_context *ctx = context; | 350 | struct x509_parse_context *ctx = context; |
351 | ctx->cert->raw_subject = value; | ||
352 | ctx->cert->raw_subject_size = vlen; | ||
333 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); | 353 | return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); |
334 | } | 354 | } |
335 | 355 | ||
diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 87d9cc26f630..1b76f207c1f3 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h | |||
@@ -14,7 +14,9 @@ | |||
14 | 14 | ||
15 | struct x509_certificate { | 15 | struct x509_certificate { |
16 | struct x509_certificate *next; | 16 | struct x509_certificate *next; |
17 | struct x509_certificate *signer; /* Certificate that signed this one */ | ||
17 | struct public_key *pub; /* Public key details */ | 18 | struct public_key *pub; /* Public key details */ |
19 | struct public_key_signature sig; /* Signature parameters */ | ||
18 | char *issuer; /* Name of certificate issuer */ | 20 | char *issuer; /* Name of certificate issuer */ |
19 | char *subject; /* Name of certificate subject */ | 21 | char *subject; /* Name of certificate subject */ |
20 | char *fingerprint; /* Key fingerprint as hex */ | 22 | char *fingerprint; /* Key fingerprint as hex */ |
@@ -25,7 +27,16 @@ struct x509_certificate { | |||
25 | unsigned tbs_size; /* Size of signed data */ | 27 | unsigned tbs_size; /* Size of signed data */ |
26 | unsigned raw_sig_size; /* Size of sigature */ | 28 | unsigned raw_sig_size; /* Size of sigature */ |
27 | const void *raw_sig; /* Signature data */ | 29 | const void *raw_sig; /* Signature data */ |
28 | struct public_key_signature sig; /* Signature parameters */ | 30 | const void *raw_serial; /* Raw serial number in ASN.1 */ |
31 | unsigned raw_serial_size; | ||
32 | unsigned raw_issuer_size; | ||
33 | const void *raw_issuer; /* Raw issuer name in ASN.1 */ | ||
34 | const void *raw_subject; /* Raw subject name in ASN.1 */ | ||
35 | unsigned raw_subject_size; | ||
36 | unsigned index; | ||
37 | bool seen; /* Infinite recursion prevention */ | ||
38 | bool verified; | ||
39 | bool trusted; | ||
29 | }; | 40 | }; |
30 | 41 | ||
31 | /* | 42 | /* |
diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h new file mode 100644 index 000000000000..691c79172a26 --- /dev/null +++ b/include/crypto/pkcs7.h | |||
@@ -0,0 +1,36 @@ | |||
1 | /* PKCS#7 crypto data parser | ||
2 | * | ||
3 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | struct key; | ||
13 | struct pkcs7_message; | ||
14 | |||
15 | /* | ||
16 | * pkcs7_parser.c | ||
17 | */ | ||
18 | extern struct pkcs7_message *pkcs7_parse_message(const void *data, | ||
19 | size_t datalen); | ||
20 | extern void pkcs7_free_message(struct pkcs7_message *pkcs7); | ||
21 | |||
22 | extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, | ||
23 | const void **_data, size_t *_datalen, | ||
24 | bool want_wrapper); | ||
25 | |||
26 | /* | ||
27 | * pkcs7_trust.c | ||
28 | */ | ||
29 | extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, | ||
30 | struct key *trust_keyring, | ||
31 | bool *_trusted); | ||
32 | |||
33 | /* | ||
34 | * pkcs7_verify.c | ||
35 | */ | ||
36 | extern int pkcs7_verify(struct pkcs7_message *pkcs7); | ||
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 6926db724258..edeff85cb1e8 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h | |||
@@ -55,6 +55,7 @@ enum OID { | |||
55 | OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ | 55 | OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ |
56 | OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ | 56 | OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ |
57 | OID_sha1, /* 1.3.14.3.2.26 */ | 57 | OID_sha1, /* 1.3.14.3.2.26 */ |
58 | OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ | ||
58 | 59 | ||
59 | /* Distinguished Name attribute IDs [RFC 2256] */ | 60 | /* Distinguished Name attribute IDs [RFC 2256] */ |
60 | OID_commonName, /* 2.5.4.3 */ | 61 | OID_commonName, /* 2.5.4.3 */ |