1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22e3fadbfSDavid Howells /* PKCS#7 parser
32e3fadbfSDavid Howells *
42e3fadbfSDavid Howells * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
52e3fadbfSDavid Howells * Written by David Howells (dhowells@redhat.com)
62e3fadbfSDavid Howells */
72e3fadbfSDavid Howells
82e3fadbfSDavid Howells #define pr_fmt(fmt) "PKCS7: "fmt
92e3fadbfSDavid Howells #include <linux/kernel.h>
101e684d38SDavid Howells #include <linux/module.h>
112e3fadbfSDavid Howells #include <linux/export.h>
122e3fadbfSDavid Howells #include <linux/slab.h>
132e3fadbfSDavid Howells #include <linux/err.h>
142e3fadbfSDavid Howells #include <linux/oid_registry.h>
15db6c43bdSTadeusz Struk #include <crypto/public_key.h>
162e3fadbfSDavid Howells #include "pkcs7_parser.h"
174fa8bc94SMasahiro Yamada #include "pkcs7.asn1.h"
182e3fadbfSDavid Howells
191e684d38SDavid Howells MODULE_DESCRIPTION("PKCS#7 parser");
201e684d38SDavid Howells MODULE_AUTHOR("Red Hat, Inc.");
211e684d38SDavid Howells MODULE_LICENSE("GPL");
221e684d38SDavid Howells
232e3fadbfSDavid Howells struct pkcs7_parse_context {
242e3fadbfSDavid Howells struct pkcs7_message *msg; /* Message being constructed */
252e3fadbfSDavid Howells struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */
262e3fadbfSDavid Howells struct pkcs7_signed_info **ppsinfo;
272e3fadbfSDavid Howells struct x509_certificate *certs; /* Certificate cache */
282e3fadbfSDavid Howells struct x509_certificate **ppcerts;
292e3fadbfSDavid Howells unsigned long data; /* Start of data */
302e3fadbfSDavid Howells enum OID last_oid; /* Last OID encountered */
312e3fadbfSDavid Howells unsigned x509_index;
322e3fadbfSDavid Howells unsigned sinfo_index;
3346963b77SDavid Howells const void *raw_serial;
3446963b77SDavid Howells unsigned raw_serial_size;
3546963b77SDavid Howells unsigned raw_issuer_size;
3646963b77SDavid Howells const void *raw_issuer;
3760d65cacSDavid Howells const void *raw_skid;
3860d65cacSDavid Howells unsigned raw_skid_size;
3960d65cacSDavid Howells bool expect_skid;
402e3fadbfSDavid Howells };
412e3fadbfSDavid Howells
423cd0920cSDavid Howells /*
433cd0920cSDavid Howells * Free a signed information block.
443cd0920cSDavid Howells */
pkcs7_free_signed_info(struct pkcs7_signed_info * sinfo)453cd0920cSDavid Howells static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
463cd0920cSDavid Howells {
473cd0920cSDavid Howells if (sinfo) {
48566a117aSDavid Howells public_key_signature_free(sinfo->sig);
493cd0920cSDavid Howells kfree(sinfo);
503cd0920cSDavid Howells }
513cd0920cSDavid Howells }
523cd0920cSDavid Howells
532e3fadbfSDavid Howells /**
542e3fadbfSDavid Howells * pkcs7_free_message - Free a PKCS#7 message
552e3fadbfSDavid Howells * @pkcs7: The PKCS#7 message to free
562e3fadbfSDavid Howells */
pkcs7_free_message(struct pkcs7_message * pkcs7)572e3fadbfSDavid Howells void pkcs7_free_message(struct pkcs7_message *pkcs7)
582e3fadbfSDavid Howells {
592e3fadbfSDavid Howells struct x509_certificate *cert;
602e3fadbfSDavid Howells struct pkcs7_signed_info *sinfo;
612e3fadbfSDavid Howells
622e3fadbfSDavid Howells if (pkcs7) {
632e3fadbfSDavid Howells while (pkcs7->certs) {
642e3fadbfSDavid Howells cert = pkcs7->certs;
652e3fadbfSDavid Howells pkcs7->certs = cert->next;
662e3fadbfSDavid Howells x509_free_certificate(cert);
672e3fadbfSDavid Howells }
682e3fadbfSDavid Howells while (pkcs7->crl) {
692e3fadbfSDavid Howells cert = pkcs7->crl;
702e3fadbfSDavid Howells pkcs7->crl = cert->next;
712e3fadbfSDavid Howells x509_free_certificate(cert);
722e3fadbfSDavid Howells }
732e3fadbfSDavid Howells while (pkcs7->signed_infos) {
742e3fadbfSDavid Howells sinfo = pkcs7->signed_infos;
752e3fadbfSDavid Howells pkcs7->signed_infos = sinfo->next;
763cd0920cSDavid Howells pkcs7_free_signed_info(sinfo);
772e3fadbfSDavid Howells }
782e3fadbfSDavid Howells kfree(pkcs7);
792e3fadbfSDavid Howells }
802e3fadbfSDavid Howells }
812e3fadbfSDavid Howells EXPORT_SYMBOL_GPL(pkcs7_free_message);
822e3fadbfSDavid Howells
8399db4435SDavid Howells /*
8499db4435SDavid Howells * Check authenticatedAttributes are provided or not provided consistently.
8599db4435SDavid Howells */
pkcs7_check_authattrs(struct pkcs7_message * msg)8699db4435SDavid Howells static int pkcs7_check_authattrs(struct pkcs7_message *msg)
8799db4435SDavid Howells {
8899db4435SDavid Howells struct pkcs7_signed_info *sinfo;
8906aae592SColin Ian King bool want = false;
9099db4435SDavid Howells
9199db4435SDavid Howells sinfo = msg->signed_infos;
9268a1fdbbSEric Sesterhenn if (!sinfo)
9368a1fdbbSEric Sesterhenn goto inconsistent;
9468a1fdbbSEric Sesterhenn
9599db4435SDavid Howells if (sinfo->authattrs) {
9699db4435SDavid Howells want = true;
9799db4435SDavid Howells msg->have_authattrs = true;
9899db4435SDavid Howells }
9999db4435SDavid Howells
10099db4435SDavid Howells for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
10199db4435SDavid Howells if (!!sinfo->authattrs != want)
10299db4435SDavid Howells goto inconsistent;
10399db4435SDavid Howells return 0;
10499db4435SDavid Howells
10599db4435SDavid Howells inconsistent:
10699db4435SDavid Howells pr_warn("Inconsistently supplied authAttrs\n");
10799db4435SDavid Howells return -EINVAL;
10899db4435SDavid Howells }
10999db4435SDavid Howells
1102e3fadbfSDavid Howells /**
1112e3fadbfSDavid Howells * pkcs7_parse_message - Parse a PKCS#7 message
1122e3fadbfSDavid Howells * @data: The raw binary ASN.1 encoded message to be parsed
1132e3fadbfSDavid Howells * @datalen: The size of the encoded message
1142e3fadbfSDavid Howells */
pkcs7_parse_message(const void * data,size_t datalen)1152e3fadbfSDavid Howells struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
1162e3fadbfSDavid Howells {
1172e3fadbfSDavid Howells struct pkcs7_parse_context *ctx;
118cecf5d2eSDavid Howells struct pkcs7_message *msg = ERR_PTR(-ENOMEM);
119cecf5d2eSDavid Howells int ret;
1202e3fadbfSDavid Howells
1212e3fadbfSDavid Howells ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
1222e3fadbfSDavid Howells if (!ctx)
123cecf5d2eSDavid Howells goto out_no_ctx;
124cecf5d2eSDavid Howells ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
125cecf5d2eSDavid Howells if (!ctx->msg)
126cecf5d2eSDavid Howells goto out_no_msg;
1272e3fadbfSDavid Howells ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
1282e3fadbfSDavid Howells if (!ctx->sinfo)
129cecf5d2eSDavid Howells goto out_no_sinfo;
130566a117aSDavid Howells ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
131566a117aSDavid Howells GFP_KERNEL);
132566a117aSDavid Howells if (!ctx->sinfo->sig)
133566a117aSDavid Howells goto out_no_sig;
1342e3fadbfSDavid Howells
1352e3fadbfSDavid Howells ctx->data = (unsigned long)data;
1362e3fadbfSDavid Howells ctx->ppcerts = &ctx->certs;
1372e3fadbfSDavid Howells ctx->ppsinfo = &ctx->msg->signed_infos;
1382e3fadbfSDavid Howells
1392e3fadbfSDavid Howells /* Attempt to decode the signature */
1402e3fadbfSDavid Howells ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
141cecf5d2eSDavid Howells if (ret < 0) {
142cecf5d2eSDavid Howells msg = ERR_PTR(ret);
143cecf5d2eSDavid Howells goto out;
144cecf5d2eSDavid Howells }
1452e3fadbfSDavid Howells
14699db4435SDavid Howells ret = pkcs7_check_authattrs(ctx->msg);
1478ecb506dSEric Biggers if (ret < 0) {
1488ecb506dSEric Biggers msg = ERR_PTR(ret);
14999db4435SDavid Howells goto out;
1508ecb506dSEric Biggers }
15199db4435SDavid Howells
152cecf5d2eSDavid Howells msg = ctx->msg;
153cecf5d2eSDavid Howells ctx->msg = NULL;
154cecf5d2eSDavid Howells
155cecf5d2eSDavid Howells out:
1562e3fadbfSDavid Howells while (ctx->certs) {
1572e3fadbfSDavid Howells struct x509_certificate *cert = ctx->certs;
1582e3fadbfSDavid Howells ctx->certs = cert->next;
1592e3fadbfSDavid Howells x509_free_certificate(cert);
1602e3fadbfSDavid Howells }
161566a117aSDavid Howells out_no_sig:
1623cd0920cSDavid Howells pkcs7_free_signed_info(ctx->sinfo);
163cecf5d2eSDavid Howells out_no_sinfo:
164cecf5d2eSDavid Howells pkcs7_free_message(ctx->msg);
165cecf5d2eSDavid Howells out_no_msg:
1662e3fadbfSDavid Howells kfree(ctx);
167cecf5d2eSDavid Howells out_no_ctx:
1682e3fadbfSDavid Howells return msg;
1692e3fadbfSDavid Howells }
1702e3fadbfSDavid Howells EXPORT_SYMBOL_GPL(pkcs7_parse_message);
1712e3fadbfSDavid Howells
1722e3fadbfSDavid Howells /**
1732e3fadbfSDavid Howells * pkcs7_get_content_data - Get access to the PKCS#7 content
1742e3fadbfSDavid Howells * @pkcs7: The preparsed PKCS#7 message to access
1752e3fadbfSDavid Howells * @_data: Place to return a pointer to the data
1762e3fadbfSDavid Howells * @_data_len: Place to return the data length
177e68503bdSDavid Howells * @_headerlen: Size of ASN.1 header not included in _data
1782e3fadbfSDavid Howells *
179e68503bdSDavid Howells * Get access to the data content of the PKCS#7 message. The size of the
180e68503bdSDavid Howells * header of the ASN.1 object that contains it is also provided and can be used
181e68503bdSDavid Howells * to adjust *_data and *_data_len to get the entire object.
182e68503bdSDavid Howells *
183e68503bdSDavid Howells * Returns -ENODATA if the data object was missing from the message.
1842e3fadbfSDavid Howells */
pkcs7_get_content_data(const struct pkcs7_message * pkcs7,const void ** _data,size_t * _data_len,size_t * _headerlen)1852e3fadbfSDavid Howells int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
1862e3fadbfSDavid Howells const void **_data, size_t *_data_len,
187e68503bdSDavid Howells size_t *_headerlen)
1882e3fadbfSDavid Howells {
1892e3fadbfSDavid Howells if (!pkcs7->data)
1902e3fadbfSDavid Howells return -ENODATA;
1912e3fadbfSDavid Howells
192e68503bdSDavid Howells *_data = pkcs7->data;
193e68503bdSDavid Howells *_data_len = pkcs7->data_len;
194e68503bdSDavid Howells if (_headerlen)
195e68503bdSDavid Howells *_headerlen = pkcs7->data_hdrlen;
1962e3fadbfSDavid Howells return 0;
1972e3fadbfSDavid Howells }
1982e3fadbfSDavid Howells EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
1992e3fadbfSDavid Howells
2002e3fadbfSDavid Howells /*
2012e3fadbfSDavid Howells * Note an OID when we find one for later processing when we know how
2022e3fadbfSDavid Howells * to interpret it.
2032e3fadbfSDavid Howells */
pkcs7_note_OID(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)2042e3fadbfSDavid Howells int pkcs7_note_OID(void *context, size_t hdrlen,
2052e3fadbfSDavid Howells unsigned char tag,
2062e3fadbfSDavid Howells const void *value, size_t vlen)
2072e3fadbfSDavid Howells {
2082e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
2092e3fadbfSDavid Howells
2102e3fadbfSDavid Howells ctx->last_oid = look_up_OID(value, vlen);
2112e3fadbfSDavid Howells if (ctx->last_oid == OID__NR) {
2122e3fadbfSDavid Howells char buffer[50];
2132e3fadbfSDavid Howells sprint_oid(value, vlen, buffer, sizeof(buffer));
2142e3fadbfSDavid Howells printk("PKCS7: Unknown OID: [%lu] %s\n",
2152e3fadbfSDavid Howells (unsigned long)value - ctx->data, buffer);
2162e3fadbfSDavid Howells }
2172e3fadbfSDavid Howells return 0;
2182e3fadbfSDavid Howells }
2192e3fadbfSDavid Howells
2202e3fadbfSDavid Howells /*
2212e3fadbfSDavid Howells * Note the digest algorithm for the signature.
2222e3fadbfSDavid Howells */
pkcs7_sig_note_digest_algo(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)2232e3fadbfSDavid Howells int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
2242e3fadbfSDavid Howells unsigned char tag,
2252e3fadbfSDavid Howells const void *value, size_t vlen)
2262e3fadbfSDavid Howells {
2272e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
2282e3fadbfSDavid Howells
2292e3fadbfSDavid Howells switch (ctx->last_oid) {
2302e3fadbfSDavid Howells case OID_md4:
231566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "md4";
2322e3fadbfSDavid Howells break;
2332e3fadbfSDavid Howells case OID_md5:
234566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "md5";
2352e3fadbfSDavid Howells break;
2362e3fadbfSDavid Howells case OID_sha1:
237566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "sha1";
2382e3fadbfSDavid Howells break;
2392e3fadbfSDavid Howells case OID_sha256:
240566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "sha256";
2412e3fadbfSDavid Howells break;
24207f081fbSDavid Howells case OID_sha384:
243566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "sha384";
24407f081fbSDavid Howells break;
24507f081fbSDavid Howells case OID_sha512:
246566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "sha512";
24707f081fbSDavid Howells break;
24807f081fbSDavid Howells case OID_sha224:
249566a117aSDavid Howells ctx->sinfo->sig->hash_algo = "sha224";
250566a117aSDavid Howells break;
2513fb8e3f5STianjia Zhang case OID_sm3:
2523fb8e3f5STianjia Zhang ctx->sinfo->sig->hash_algo = "sm3";
2533fb8e3f5STianjia Zhang break;
254*d35f42caSElvira Khabirova case OID_gost2012Digest256:
255*d35f42caSElvira Khabirova ctx->sinfo->sig->hash_algo = "streebog256";
256*d35f42caSElvira Khabirova break;
257*d35f42caSElvira Khabirova case OID_gost2012Digest512:
258*d35f42caSElvira Khabirova ctx->sinfo->sig->hash_algo = "streebog512";
259*d35f42caSElvira Khabirova break;
2602e3fadbfSDavid Howells default:
2612e3fadbfSDavid Howells printk("Unsupported digest algo: %u\n", ctx->last_oid);
2622e3fadbfSDavid Howells return -ENOPKG;
2632e3fadbfSDavid Howells }
2642e3fadbfSDavid Howells return 0;
2652e3fadbfSDavid Howells }
2662e3fadbfSDavid Howells
2672e3fadbfSDavid Howells /*
2682e3fadbfSDavid Howells * Note the public key algorithm for the signature.
2692e3fadbfSDavid Howells */
pkcs7_sig_note_pkey_algo(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)2702e3fadbfSDavid Howells int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
2712e3fadbfSDavid Howells unsigned char tag,
2722e3fadbfSDavid Howells const void *value, size_t vlen)
2732e3fadbfSDavid Howells {
2742e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
2752e3fadbfSDavid Howells
2762e3fadbfSDavid Howells switch (ctx->last_oid) {
2772e3fadbfSDavid Howells case OID_rsaEncryption:
278566a117aSDavid Howells ctx->sinfo->sig->pkey_algo = "rsa";
27903988490SDavid Howells ctx->sinfo->sig->encoding = "pkcs1";
2802e3fadbfSDavid Howells break;
281a4aed36eSStefan Berger case OID_id_ecdsa_with_sha1:
282a4aed36eSStefan Berger case OID_id_ecdsa_with_sha224:
283a4aed36eSStefan Berger case OID_id_ecdsa_with_sha256:
284a4aed36eSStefan Berger case OID_id_ecdsa_with_sha384:
285a4aed36eSStefan Berger case OID_id_ecdsa_with_sha512:
286a4aed36eSStefan Berger ctx->sinfo->sig->pkey_algo = "ecdsa";
287a4aed36eSStefan Berger ctx->sinfo->sig->encoding = "x962";
288a4aed36eSStefan Berger break;
2893fb8e3f5STianjia Zhang case OID_SM2_with_SM3:
2903fb8e3f5STianjia Zhang ctx->sinfo->sig->pkey_algo = "sm2";
2913fb8e3f5STianjia Zhang ctx->sinfo->sig->encoding = "raw";
2923fb8e3f5STianjia Zhang break;
293*d35f42caSElvira Khabirova case OID_gost2012PKey256:
294*d35f42caSElvira Khabirova case OID_gost2012PKey512:
295*d35f42caSElvira Khabirova ctx->sinfo->sig->pkey_algo = "ecrdsa";
296*d35f42caSElvira Khabirova ctx->sinfo->sig->encoding = "raw";
297*d35f42caSElvira Khabirova break;
2982e3fadbfSDavid Howells default:
2992e3fadbfSDavid Howells printk("Unsupported pkey algo: %u\n", ctx->last_oid);
3002e3fadbfSDavid Howells return -ENOPKG;
3012e3fadbfSDavid Howells }
3022e3fadbfSDavid Howells return 0;
3032e3fadbfSDavid Howells }
3042e3fadbfSDavid Howells
3052e3fadbfSDavid Howells /*
3062c7fd367SDavid Howells * We only support signed data [RFC2315 sec 9].
3072c7fd367SDavid Howells */
pkcs7_check_content_type(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)3082c7fd367SDavid Howells int pkcs7_check_content_type(void *context, size_t hdrlen,
3092c7fd367SDavid Howells unsigned char tag,
3102c7fd367SDavid Howells const void *value, size_t vlen)
3112c7fd367SDavid Howells {
3122c7fd367SDavid Howells struct pkcs7_parse_context *ctx = context;
3132c7fd367SDavid Howells
3142c7fd367SDavid Howells if (ctx->last_oid != OID_signed_data) {
3152c7fd367SDavid Howells pr_warn("Only support pkcs7_signedData type\n");
3162c7fd367SDavid Howells return -EINVAL;
3172c7fd367SDavid Howells }
3182c7fd367SDavid Howells
3192c7fd367SDavid Howells return 0;
3202c7fd367SDavid Howells }
3212c7fd367SDavid Howells
3222c7fd367SDavid Howells /*
3232c7fd367SDavid Howells * Note the SignedData version
3242c7fd367SDavid Howells */
pkcs7_note_signeddata_version(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)3252c7fd367SDavid Howells int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
3262c7fd367SDavid Howells unsigned char tag,
3272c7fd367SDavid Howells const void *value, size_t vlen)
3282c7fd367SDavid Howells {
32960d65cacSDavid Howells struct pkcs7_parse_context *ctx = context;
3302c7fd367SDavid Howells unsigned version;
3312c7fd367SDavid Howells
3322c7fd367SDavid Howells if (vlen != 1)
3332c7fd367SDavid Howells goto unsupported;
3342c7fd367SDavid Howells
33560d65cacSDavid Howells ctx->msg->version = version = *(const u8 *)value;
3362c7fd367SDavid Howells switch (version) {
3372c7fd367SDavid Howells case 1:
33860d65cacSDavid Howells /* PKCS#7 SignedData [RFC2315 sec 9.1]
33960d65cacSDavid Howells * CMS ver 1 SignedData [RFC5652 sec 5.1]
34060d65cacSDavid Howells */
34160d65cacSDavid Howells break;
34260d65cacSDavid Howells case 3:
34360d65cacSDavid Howells /* CMS ver 3 SignedData [RFC2315 sec 5.1] */
3442c7fd367SDavid Howells break;
3452c7fd367SDavid Howells default:
3462c7fd367SDavid Howells goto unsupported;
3472c7fd367SDavid Howells }
3482c7fd367SDavid Howells
3492c7fd367SDavid Howells return 0;
3502c7fd367SDavid Howells
3512c7fd367SDavid Howells unsupported:
3522c7fd367SDavid Howells pr_warn("Unsupported SignedData version\n");
3532c7fd367SDavid Howells return -EINVAL;
3542c7fd367SDavid Howells }
3552c7fd367SDavid Howells
3562c7fd367SDavid Howells /*
3572c7fd367SDavid Howells * Note the SignerInfo version
3582c7fd367SDavid Howells */
pkcs7_note_signerinfo_version(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)3592c7fd367SDavid Howells int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
3602c7fd367SDavid Howells unsigned char tag,
3612c7fd367SDavid Howells const void *value, size_t vlen)
3622c7fd367SDavid Howells {
36360d65cacSDavid Howells struct pkcs7_parse_context *ctx = context;
3642c7fd367SDavid Howells unsigned version;
3652c7fd367SDavid Howells
3662c7fd367SDavid Howells if (vlen != 1)
3672c7fd367SDavid Howells goto unsupported;
3682c7fd367SDavid Howells
3692c7fd367SDavid Howells version = *(const u8 *)value;
3702c7fd367SDavid Howells switch (version) {
3712c7fd367SDavid Howells case 1:
37260d65cacSDavid Howells /* PKCS#7 SignerInfo [RFC2315 sec 9.2]
37360d65cacSDavid Howells * CMS ver 1 SignerInfo [RFC5652 sec 5.3]
37460d65cacSDavid Howells */
37560d65cacSDavid Howells if (ctx->msg->version != 1)
37660d65cacSDavid Howells goto version_mismatch;
37760d65cacSDavid Howells ctx->expect_skid = false;
37860d65cacSDavid Howells break;
37960d65cacSDavid Howells case 3:
38060d65cacSDavid Howells /* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
38160d65cacSDavid Howells if (ctx->msg->version == 1)
38260d65cacSDavid Howells goto version_mismatch;
38360d65cacSDavid Howells ctx->expect_skid = true;
3842c7fd367SDavid Howells break;
3852c7fd367SDavid Howells default:
3862c7fd367SDavid Howells goto unsupported;
3872c7fd367SDavid Howells }
3882c7fd367SDavid Howells
3892c7fd367SDavid Howells return 0;
3902c7fd367SDavid Howells
3912c7fd367SDavid Howells unsupported:
3922c7fd367SDavid Howells pr_warn("Unsupported SignerInfo version\n");
3932c7fd367SDavid Howells return -EINVAL;
39460d65cacSDavid Howells version_mismatch:
39560d65cacSDavid Howells pr_warn("SignedData-SignerInfo version mismatch\n");
39660d65cacSDavid Howells return -EBADMSG;
3972c7fd367SDavid Howells }
3982c7fd367SDavid Howells
3992c7fd367SDavid Howells /*
4002e3fadbfSDavid Howells * Extract a certificate and store it in the context.
4012e3fadbfSDavid Howells */
pkcs7_extract_cert(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4022e3fadbfSDavid Howells int pkcs7_extract_cert(void *context, size_t hdrlen,
4032e3fadbfSDavid Howells unsigned char tag,
4042e3fadbfSDavid Howells const void *value, size_t vlen)
4052e3fadbfSDavid Howells {
4062e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
4072e3fadbfSDavid Howells struct x509_certificate *x509;
4082e3fadbfSDavid Howells
4092e3fadbfSDavid Howells if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
4102e3fadbfSDavid Howells pr_debug("Cert began with tag %02x at %lu\n",
4112e3fadbfSDavid Howells tag, (unsigned long)ctx - ctx->data);
4122e3fadbfSDavid Howells return -EBADMSG;
4132e3fadbfSDavid Howells }
4142e3fadbfSDavid Howells
4152e3fadbfSDavid Howells /* We have to correct for the header so that the X.509 parser can start
4162e3fadbfSDavid Howells * from the beginning. Note that since X.509 stipulates DER, there
4172e3fadbfSDavid Howells * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
4182e3fadbfSDavid Howells * stipulates BER).
4192e3fadbfSDavid Howells */
4202e3fadbfSDavid Howells value -= hdrlen;
4212e3fadbfSDavid Howells vlen += hdrlen;
4222e3fadbfSDavid Howells
4232e3fadbfSDavid Howells if (((u8*)value)[1] == 0x80)
4242e3fadbfSDavid Howells vlen += 2; /* Indefinite length - there should be an EOC */
4252e3fadbfSDavid Howells
4262e3fadbfSDavid Howells x509 = x509_cert_parse(value, vlen);
4272e3fadbfSDavid Howells if (IS_ERR(x509))
4282e3fadbfSDavid Howells return PTR_ERR(x509);
4292e3fadbfSDavid Howells
4302e3fadbfSDavid Howells x509->index = ++ctx->x509_index;
43146963b77SDavid Howells pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
43246963b77SDavid Howells pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
43346963b77SDavid Howells
4342e3fadbfSDavid Howells *ctx->ppcerts = x509;
4352e3fadbfSDavid Howells ctx->ppcerts = &x509->next;
4362e3fadbfSDavid Howells return 0;
4372e3fadbfSDavid Howells }
4382e3fadbfSDavid Howells
4392e3fadbfSDavid Howells /*
4402e3fadbfSDavid Howells * Save the certificate list
4412e3fadbfSDavid Howells */
pkcs7_note_certificate_list(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4422e3fadbfSDavid Howells int pkcs7_note_certificate_list(void *context, size_t hdrlen,
4432e3fadbfSDavid Howells unsigned char tag,
4442e3fadbfSDavid Howells const void *value, size_t vlen)
4452e3fadbfSDavid Howells {
4462e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
4472e3fadbfSDavid Howells
4482e3fadbfSDavid Howells pr_devel("Got cert list (%02x)\n", tag);
4492e3fadbfSDavid Howells
4502e3fadbfSDavid Howells *ctx->ppcerts = ctx->msg->certs;
4512e3fadbfSDavid Howells ctx->msg->certs = ctx->certs;
4522e3fadbfSDavid Howells ctx->certs = NULL;
4532e3fadbfSDavid Howells ctx->ppcerts = &ctx->certs;
4542e3fadbfSDavid Howells return 0;
4552e3fadbfSDavid Howells }
4562e3fadbfSDavid Howells
4572e3fadbfSDavid Howells /*
45899db4435SDavid Howells * Note the content type.
45999db4435SDavid Howells */
pkcs7_note_content(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)46099db4435SDavid Howells int pkcs7_note_content(void *context, size_t hdrlen,
46199db4435SDavid Howells unsigned char tag,
46299db4435SDavid Howells const void *value, size_t vlen)
46399db4435SDavid Howells {
46499db4435SDavid Howells struct pkcs7_parse_context *ctx = context;
46599db4435SDavid Howells
46699db4435SDavid Howells if (ctx->last_oid != OID_data &&
46799db4435SDavid Howells ctx->last_oid != OID_msIndirectData) {
46899db4435SDavid Howells pr_warn("Unsupported data type %d\n", ctx->last_oid);
46999db4435SDavid Howells return -EINVAL;
47099db4435SDavid Howells }
47199db4435SDavid Howells
47299db4435SDavid Howells ctx->msg->data_type = ctx->last_oid;
47399db4435SDavid Howells return 0;
47499db4435SDavid Howells }
47599db4435SDavid Howells
47699db4435SDavid Howells /*
4772e3fadbfSDavid Howells * Extract the data from the message and store that and its content type OID in
4782e3fadbfSDavid Howells * the context.
4792e3fadbfSDavid Howells */
pkcs7_note_data(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4802e3fadbfSDavid Howells int pkcs7_note_data(void *context, size_t hdrlen,
4812e3fadbfSDavid Howells unsigned char tag,
4822e3fadbfSDavid Howells const void *value, size_t vlen)
4832e3fadbfSDavid Howells {
4842e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
4852e3fadbfSDavid Howells
4862e3fadbfSDavid Howells pr_debug("Got data\n");
4872e3fadbfSDavid Howells
4882e3fadbfSDavid Howells ctx->msg->data = value;
4892e3fadbfSDavid Howells ctx->msg->data_len = vlen;
4902e3fadbfSDavid Howells ctx->msg->data_hdrlen = hdrlen;
4912e3fadbfSDavid Howells return 0;
4922e3fadbfSDavid Howells }
4932e3fadbfSDavid Howells
4942e3fadbfSDavid Howells /*
49599db4435SDavid Howells * Parse authenticated attributes.
4962e3fadbfSDavid Howells */
pkcs7_sig_note_authenticated_attr(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4972e3fadbfSDavid Howells int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
4982e3fadbfSDavid Howells unsigned char tag,
4992e3fadbfSDavid Howells const void *value, size_t vlen)
5002e3fadbfSDavid Howells {
5012e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
50299db4435SDavid Howells struct pkcs7_signed_info *sinfo = ctx->sinfo;
50399db4435SDavid Howells enum OID content_type;
5042e3fadbfSDavid Howells
5052e3fadbfSDavid Howells pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
5062e3fadbfSDavid Howells
5072e3fadbfSDavid Howells switch (ctx->last_oid) {
50899db4435SDavid Howells case OID_contentType:
50999db4435SDavid Howells if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
51099db4435SDavid Howells goto repeated;
51199db4435SDavid Howells content_type = look_up_OID(value, vlen);
51299db4435SDavid Howells if (content_type != ctx->msg->data_type) {
51399db4435SDavid Howells pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
51499db4435SDavid Howells ctx->msg->data_type, sinfo->index,
51599db4435SDavid Howells content_type);
51699db4435SDavid Howells return -EBADMSG;
51799db4435SDavid Howells }
51899db4435SDavid Howells return 0;
51999db4435SDavid Howells
52099db4435SDavid Howells case OID_signingTime:
52199db4435SDavid Howells if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
52299db4435SDavid Howells goto repeated;
52399db4435SDavid Howells /* Should we check that the signing time is consistent
52499db4435SDavid Howells * with the signer's X.509 cert?
52599db4435SDavid Howells */
52699db4435SDavid Howells return x509_decode_time(&sinfo->signing_time,
52799db4435SDavid Howells hdrlen, tag, value, vlen);
52899db4435SDavid Howells
5292e3fadbfSDavid Howells case OID_messageDigest:
53099db4435SDavid Howells if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
53199db4435SDavid Howells goto repeated;
5322e3fadbfSDavid Howells if (tag != ASN1_OTS)
5332e3fadbfSDavid Howells return -EBADMSG;
53499db4435SDavid Howells sinfo->msgdigest = value;
53599db4435SDavid Howells sinfo->msgdigest_len = vlen;
53699db4435SDavid Howells return 0;
53799db4435SDavid Howells
53899db4435SDavid Howells case OID_smimeCapabilites:
53999db4435SDavid Howells if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
54099db4435SDavid Howells goto repeated;
54199db4435SDavid Howells if (ctx->msg->data_type != OID_msIndirectData) {
54299db4435SDavid Howells pr_warn("S/MIME Caps only allowed with Authenticode\n");
54399db4435SDavid Howells return -EKEYREJECTED;
54499db4435SDavid Howells }
54599db4435SDavid Howells return 0;
54699db4435SDavid Howells
54799db4435SDavid Howells /* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
54899db4435SDavid Howells * char URLs and cont[1] 8-bit char URLs.
54999db4435SDavid Howells *
55099db4435SDavid Howells * Microsoft StatementType seems to contain a list of OIDs that
55199db4435SDavid Howells * are also used as extendedKeyUsage types in X.509 certs.
55299db4435SDavid Howells */
55399db4435SDavid Howells case OID_msSpOpusInfo:
55499db4435SDavid Howells if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
55599db4435SDavid Howells goto repeated;
55699db4435SDavid Howells goto authenticode_check;
55799db4435SDavid Howells case OID_msStatementType:
55899db4435SDavid Howells if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
55999db4435SDavid Howells goto repeated;
56099db4435SDavid Howells authenticode_check:
56199db4435SDavid Howells if (ctx->msg->data_type != OID_msIndirectData) {
56299db4435SDavid Howells pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
56399db4435SDavid Howells return -EKEYREJECTED;
56499db4435SDavid Howells }
56599db4435SDavid Howells /* I'm not sure how to validate these */
5662e3fadbfSDavid Howells return 0;
5672e3fadbfSDavid Howells default:
5682e3fadbfSDavid Howells return 0;
5692e3fadbfSDavid Howells }
57099db4435SDavid Howells
57199db4435SDavid Howells repeated:
57299db4435SDavid Howells /* We permit max one item per AuthenticatedAttribute and no repeats */
57399db4435SDavid Howells pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
57499db4435SDavid Howells return -EKEYREJECTED;
5752e3fadbfSDavid Howells }
5762e3fadbfSDavid Howells
5772e3fadbfSDavid Howells /*
5782c7fd367SDavid Howells * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]
5792e3fadbfSDavid Howells */
pkcs7_sig_note_set_of_authattrs(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)5802e3fadbfSDavid Howells int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
5812e3fadbfSDavid Howells unsigned char tag,
5822e3fadbfSDavid Howells const void *value, size_t vlen)
5832e3fadbfSDavid Howells {
5842e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
58599db4435SDavid Howells struct pkcs7_signed_info *sinfo = ctx->sinfo;
58699db4435SDavid Howells
58799db4435SDavid Howells if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
5887ee7014dSPeter Jones !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {
58999db4435SDavid Howells pr_warn("Missing required AuthAttr\n");
59099db4435SDavid Howells return -EBADMSG;
59199db4435SDavid Howells }
59299db4435SDavid Howells
59399db4435SDavid Howells if (ctx->msg->data_type != OID_msIndirectData &&
59499db4435SDavid Howells test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
59599db4435SDavid Howells pr_warn("Unexpected Authenticode AuthAttr\n");
59699db4435SDavid Howells return -EBADMSG;
59799db4435SDavid Howells }
5982e3fadbfSDavid Howells
5992e3fadbfSDavid Howells /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
60099db4435SDavid Howells sinfo->authattrs = value - (hdrlen - 1);
60199db4435SDavid Howells sinfo->authattrs_len = vlen + (hdrlen - 1);
6022e3fadbfSDavid Howells return 0;
6032e3fadbfSDavid Howells }
6042e3fadbfSDavid Howells
6052e3fadbfSDavid Howells /*
6062e3fadbfSDavid Howells * Note the issuing certificate serial number
6072e3fadbfSDavid Howells */
pkcs7_sig_note_serial(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6082e3fadbfSDavid Howells int pkcs7_sig_note_serial(void *context, size_t hdrlen,
6092e3fadbfSDavid Howells unsigned char tag,
6102e3fadbfSDavid Howells const void *value, size_t vlen)
6112e3fadbfSDavid Howells {
6122e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
61346963b77SDavid Howells ctx->raw_serial = value;
61446963b77SDavid Howells ctx->raw_serial_size = vlen;
6152e3fadbfSDavid Howells return 0;
6162e3fadbfSDavid Howells }
6172e3fadbfSDavid Howells
6182e3fadbfSDavid Howells /*
6192e3fadbfSDavid Howells * Note the issuer's name
6202e3fadbfSDavid Howells */
pkcs7_sig_note_issuer(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6212e3fadbfSDavid Howells int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
6222e3fadbfSDavid Howells unsigned char tag,
6232e3fadbfSDavid Howells const void *value, size_t vlen)
6242e3fadbfSDavid Howells {
6252e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
62646963b77SDavid Howells ctx->raw_issuer = value;
62746963b77SDavid Howells ctx->raw_issuer_size = vlen;
6282e3fadbfSDavid Howells return 0;
6292e3fadbfSDavid Howells }
6302e3fadbfSDavid Howells
6312e3fadbfSDavid Howells /*
63260d65cacSDavid Howells * Note the issuing cert's subjectKeyIdentifier
63360d65cacSDavid Howells */
pkcs7_sig_note_skid(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)63460d65cacSDavid Howells int pkcs7_sig_note_skid(void *context, size_t hdrlen,
63560d65cacSDavid Howells unsigned char tag,
63660d65cacSDavid Howells const void *value, size_t vlen)
63760d65cacSDavid Howells {
63860d65cacSDavid Howells struct pkcs7_parse_context *ctx = context;
63960d65cacSDavid Howells
64060d65cacSDavid Howells pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
64160d65cacSDavid Howells
64260d65cacSDavid Howells ctx->raw_skid = value;
64360d65cacSDavid Howells ctx->raw_skid_size = vlen;
64460d65cacSDavid Howells return 0;
64560d65cacSDavid Howells }
64660d65cacSDavid Howells
64760d65cacSDavid Howells /*
6482e3fadbfSDavid Howells * Note the signature data
6492e3fadbfSDavid Howells */
pkcs7_sig_note_signature(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6502e3fadbfSDavid Howells int pkcs7_sig_note_signature(void *context, size_t hdrlen,
6512e3fadbfSDavid Howells unsigned char tag,
6522e3fadbfSDavid Howells const void *value, size_t vlen)
6532e3fadbfSDavid Howells {
6542e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
6552e3fadbfSDavid Howells
656566a117aSDavid Howells ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
657566a117aSDavid Howells if (!ctx->sinfo->sig->s)
6582e3fadbfSDavid Howells return -ENOMEM;
6592e3fadbfSDavid Howells
660566a117aSDavid Howells ctx->sinfo->sig->s_size = vlen;
6612e3fadbfSDavid Howells return 0;
6622e3fadbfSDavid Howells }
6632e3fadbfSDavid Howells
6642e3fadbfSDavid Howells /*
6652e3fadbfSDavid Howells * Note a signature information block
6662e3fadbfSDavid Howells */
pkcs7_note_signed_info(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6672e3fadbfSDavid Howells int pkcs7_note_signed_info(void *context, size_t hdrlen,
6682e3fadbfSDavid Howells unsigned char tag,
6692e3fadbfSDavid Howells const void *value, size_t vlen)
6702e3fadbfSDavid Howells {
6712e3fadbfSDavid Howells struct pkcs7_parse_context *ctx = context;
67246963b77SDavid Howells struct pkcs7_signed_info *sinfo = ctx->sinfo;
67346963b77SDavid Howells struct asymmetric_key_id *kid;
6742e3fadbfSDavid Howells
67599db4435SDavid Howells if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
67699db4435SDavid Howells pr_warn("Authenticode requires AuthAttrs\n");
67799db4435SDavid Howells return -EBADMSG;
67899db4435SDavid Howells }
67999db4435SDavid Howells
68046963b77SDavid Howells /* Generate cert issuer + serial number key ID */
68160d65cacSDavid Howells if (!ctx->expect_skid) {
68246963b77SDavid Howells kid = asymmetric_key_generate_id(ctx->raw_serial,
68346963b77SDavid Howells ctx->raw_serial_size,
68446963b77SDavid Howells ctx->raw_issuer,
68546963b77SDavid Howells ctx->raw_issuer_size);
68660d65cacSDavid Howells } else {
68760d65cacSDavid Howells kid = asymmetric_key_generate_id(ctx->raw_skid,
68860d65cacSDavid Howells ctx->raw_skid_size,
68960d65cacSDavid Howells "", 0);
69060d65cacSDavid Howells }
69146963b77SDavid Howells if (IS_ERR(kid))
69246963b77SDavid Howells return PTR_ERR(kid);
69346963b77SDavid Howells
69460d65cacSDavid Howells pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
69560d65cacSDavid Howells
696566a117aSDavid Howells sinfo->sig->auth_ids[0] = kid;
69746963b77SDavid Howells sinfo->index = ++ctx->sinfo_index;
69846963b77SDavid Howells *ctx->ppsinfo = sinfo;
69946963b77SDavid Howells ctx->ppsinfo = &sinfo->next;
7002e3fadbfSDavid Howells ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
7012e3fadbfSDavid Howells if (!ctx->sinfo)
7022e3fadbfSDavid Howells return -ENOMEM;
703566a117aSDavid Howells ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
704566a117aSDavid Howells GFP_KERNEL);
705566a117aSDavid Howells if (!ctx->sinfo->sig)
706566a117aSDavid Howells return -ENOMEM;
7072e3fadbfSDavid Howells return 0;
7082e3fadbfSDavid Howells }
709