711 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* PKCS#7 parser3 *4 * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.5 * Written by David Howells (dhowells@redhat.com)6 */7 8#define pr_fmt(fmt) "PKCS7: "fmt9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/export.h>12#include <linux/slab.h>13#include <linux/err.h>14#include <linux/oid_registry.h>15#include <crypto/public_key.h>16#include "pkcs7_parser.h"17#include "pkcs7.asn1.h"18 19MODULE_DESCRIPTION("PKCS#7 parser");20MODULE_AUTHOR("Red Hat, Inc.");21MODULE_LICENSE("GPL");22 23struct pkcs7_parse_context {24 struct pkcs7_message *msg; /* Message being constructed */25 struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */26 struct pkcs7_signed_info **ppsinfo;27 struct x509_certificate *certs; /* Certificate cache */28 struct x509_certificate **ppcerts;29 unsigned long data; /* Start of data */30 enum OID last_oid; /* Last OID encountered */31 unsigned x509_index;32 unsigned sinfo_index;33 const void *raw_serial;34 unsigned raw_serial_size;35 unsigned raw_issuer_size;36 const void *raw_issuer;37 const void *raw_skid;38 unsigned raw_skid_size;39 bool expect_skid;40};41 42/*43 * Free a signed information block.44 */45static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)46{47 if (sinfo) {48 public_key_signature_free(sinfo->sig);49 kfree(sinfo);50 }51}52 53/**54 * pkcs7_free_message - Free a PKCS#7 message55 * @pkcs7: The PKCS#7 message to free56 */57void pkcs7_free_message(struct pkcs7_message *pkcs7)58{59 struct x509_certificate *cert;60 struct pkcs7_signed_info *sinfo;61 62 if (pkcs7) {63 while (pkcs7->certs) {64 cert = pkcs7->certs;65 pkcs7->certs = cert->next;66 x509_free_certificate(cert);67 }68 while (pkcs7->crl) {69 cert = pkcs7->crl;70 pkcs7->crl = cert->next;71 x509_free_certificate(cert);72 }73 while (pkcs7->signed_infos) {74 sinfo = pkcs7->signed_infos;75 pkcs7->signed_infos = sinfo->next;76 pkcs7_free_signed_info(sinfo);77 }78 kfree(pkcs7);79 }80}81EXPORT_SYMBOL_GPL(pkcs7_free_message);82 83/*84 * Check authenticatedAttributes are provided or not provided consistently.85 */86static int pkcs7_check_authattrs(struct pkcs7_message *msg)87{88 struct pkcs7_signed_info *sinfo;89 bool want = false;90 91 sinfo = msg->signed_infos;92 if (!sinfo)93 goto inconsistent;94 95 if (sinfo->authattrs) {96 want = true;97 msg->have_authattrs = true;98 }99 100 for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)101 if (!!sinfo->authattrs != want)102 goto inconsistent;103 return 0;104 105inconsistent:106 pr_warn("Inconsistently supplied authAttrs\n");107 return -EINVAL;108}109 110/**111 * pkcs7_parse_message - Parse a PKCS#7 message112 * @data: The raw binary ASN.1 encoded message to be parsed113 * @datalen: The size of the encoded message114 */115struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)116{117 struct pkcs7_parse_context *ctx;118 struct pkcs7_message *msg = ERR_PTR(-ENOMEM);119 int ret;120 121 ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);122 if (!ctx)123 goto out_no_ctx;124 ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);125 if (!ctx->msg)126 goto out_no_msg;127 ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);128 if (!ctx->sinfo)129 goto out_no_sinfo;130 ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),131 GFP_KERNEL);132 if (!ctx->sinfo->sig)133 goto out_no_sig;134 135 ctx->data = (unsigned long)data;136 ctx->ppcerts = &ctx->certs;137 ctx->ppsinfo = &ctx->msg->signed_infos;138 139 /* Attempt to decode the signature */140 ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);141 if (ret < 0) {142 msg = ERR_PTR(ret);143 goto out;144 }145 146 ret = pkcs7_check_authattrs(ctx->msg);147 if (ret < 0) {148 msg = ERR_PTR(ret);149 goto out;150 }151 152 msg = ctx->msg;153 ctx->msg = NULL;154 155out:156 while (ctx->certs) {157 struct x509_certificate *cert = ctx->certs;158 ctx->certs = cert->next;159 x509_free_certificate(cert);160 }161out_no_sig:162 pkcs7_free_signed_info(ctx->sinfo);163out_no_sinfo:164 pkcs7_free_message(ctx->msg);165out_no_msg:166 kfree(ctx);167out_no_ctx:168 return msg;169}170EXPORT_SYMBOL_GPL(pkcs7_parse_message);171 172/**173 * pkcs7_get_content_data - Get access to the PKCS#7 content174 * @pkcs7: The preparsed PKCS#7 message to access175 * @_data: Place to return a pointer to the data176 * @_data_len: Place to return the data length177 * @_headerlen: Size of ASN.1 header not included in _data178 *179 * Get access to the data content of the PKCS#7 message. The size of the180 * header of the ASN.1 object that contains it is also provided and can be used181 * to adjust *_data and *_data_len to get the entire object.182 *183 * Returns -ENODATA if the data object was missing from the message.184 */185int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,186 const void **_data, size_t *_data_len,187 size_t *_headerlen)188{189 if (!pkcs7->data)190 return -ENODATA;191 192 *_data = pkcs7->data;193 *_data_len = pkcs7->data_len;194 if (_headerlen)195 *_headerlen = pkcs7->data_hdrlen;196 return 0;197}198EXPORT_SYMBOL_GPL(pkcs7_get_content_data);199 200/*201 * Note an OID when we find one for later processing when we know how202 * to interpret it.203 */204int pkcs7_note_OID(void *context, size_t hdrlen,205 unsigned char tag,206 const void *value, size_t vlen)207{208 struct pkcs7_parse_context *ctx = context;209 210 ctx->last_oid = look_up_OID(value, vlen);211 if (ctx->last_oid == OID__NR) {212 char buffer[50];213 sprint_oid(value, vlen, buffer, sizeof(buffer));214 printk("PKCS7: Unknown OID: [%lu] %s\n",215 (unsigned long)value - ctx->data, buffer);216 }217 return 0;218}219 220/*221 * Note the digest algorithm for the signature.222 */223int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,224 unsigned char tag,225 const void *value, size_t vlen)226{227 struct pkcs7_parse_context *ctx = context;228 229 switch (ctx->last_oid) {230 case OID_sha1:231 ctx->sinfo->sig->hash_algo = "sha1";232 break;233 case OID_sha256:234 ctx->sinfo->sig->hash_algo = "sha256";235 break;236 case OID_sha384:237 ctx->sinfo->sig->hash_algo = "sha384";238 break;239 case OID_sha512:240 ctx->sinfo->sig->hash_algo = "sha512";241 break;242 case OID_sha224:243 ctx->sinfo->sig->hash_algo = "sha224";244 break;245 case OID_sm3:246 ctx->sinfo->sig->hash_algo = "sm3";247 break;248 case OID_gost2012Digest256:249 ctx->sinfo->sig->hash_algo = "streebog256";250 break;251 case OID_gost2012Digest512:252 ctx->sinfo->sig->hash_algo = "streebog512";253 break;254 case OID_sha3_256:255 ctx->sinfo->sig->hash_algo = "sha3-256";256 break;257 case OID_sha3_384:258 ctx->sinfo->sig->hash_algo = "sha3-384";259 break;260 case OID_sha3_512:261 ctx->sinfo->sig->hash_algo = "sha3-512";262 break;263 default:264 printk("Unsupported digest algo: %u\n", ctx->last_oid);265 return -ENOPKG;266 }267 return 0;268}269 270/*271 * Note the public key algorithm for the signature.272 */273int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,274 unsigned char tag,275 const void *value, size_t vlen)276{277 struct pkcs7_parse_context *ctx = context;278 279 switch (ctx->last_oid) {280 case OID_rsaEncryption:281 ctx->sinfo->sig->pkey_algo = "rsa";282 ctx->sinfo->sig->encoding = "pkcs1";283 break;284 case OID_id_ecdsa_with_sha1:285 case OID_id_ecdsa_with_sha224:286 case OID_id_ecdsa_with_sha256:287 case OID_id_ecdsa_with_sha384:288 case OID_id_ecdsa_with_sha512:289 case OID_id_ecdsa_with_sha3_256:290 case OID_id_ecdsa_with_sha3_384:291 case OID_id_ecdsa_with_sha3_512:292 ctx->sinfo->sig->pkey_algo = "ecdsa";293 ctx->sinfo->sig->encoding = "x962";294 break;295 case OID_gost2012PKey256:296 case OID_gost2012PKey512:297 ctx->sinfo->sig->pkey_algo = "ecrdsa";298 ctx->sinfo->sig->encoding = "raw";299 break;300 default:301 printk("Unsupported pkey algo: %u\n", ctx->last_oid);302 return -ENOPKG;303 }304 return 0;305}306 307/*308 * We only support signed data [RFC2315 sec 9].309 */310int pkcs7_check_content_type(void *context, size_t hdrlen,311 unsigned char tag,312 const void *value, size_t vlen)313{314 struct pkcs7_parse_context *ctx = context;315 316 if (ctx->last_oid != OID_signed_data) {317 pr_warn("Only support pkcs7_signedData type\n");318 return -EINVAL;319 }320 321 return 0;322}323 324/*325 * Note the SignedData version326 */327int pkcs7_note_signeddata_version(void *context, size_t hdrlen,328 unsigned char tag,329 const void *value, size_t vlen)330{331 struct pkcs7_parse_context *ctx = context;332 unsigned version;333 334 if (vlen != 1)335 goto unsupported;336 337 ctx->msg->version = version = *(const u8 *)value;338 switch (version) {339 case 1:340 /* PKCS#7 SignedData [RFC2315 sec 9.1]341 * CMS ver 1 SignedData [RFC5652 sec 5.1]342 */343 break;344 case 3:345 /* CMS ver 3 SignedData [RFC2315 sec 5.1] */346 break;347 default:348 goto unsupported;349 }350 351 return 0;352 353unsupported:354 pr_warn("Unsupported SignedData version\n");355 return -EINVAL;356}357 358/*359 * Note the SignerInfo version360 */361int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,362 unsigned char tag,363 const void *value, size_t vlen)364{365 struct pkcs7_parse_context *ctx = context;366 unsigned version;367 368 if (vlen != 1)369 goto unsupported;370 371 version = *(const u8 *)value;372 switch (version) {373 case 1:374 /* PKCS#7 SignerInfo [RFC2315 sec 9.2]375 * CMS ver 1 SignerInfo [RFC5652 sec 5.3]376 */377 if (ctx->msg->version != 1)378 goto version_mismatch;379 ctx->expect_skid = false;380 break;381 case 3:382 /* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */383 if (ctx->msg->version == 1)384 goto version_mismatch;385 ctx->expect_skid = true;386 break;387 default:388 goto unsupported;389 }390 391 return 0;392 393unsupported:394 pr_warn("Unsupported SignerInfo version\n");395 return -EINVAL;396version_mismatch:397 pr_warn("SignedData-SignerInfo version mismatch\n");398 return -EBADMSG;399}400 401/*402 * Extract a certificate and store it in the context.403 */404int pkcs7_extract_cert(void *context, size_t hdrlen,405 unsigned char tag,406 const void *value, size_t vlen)407{408 struct pkcs7_parse_context *ctx = context;409 struct x509_certificate *x509;410 411 if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {412 pr_debug("Cert began with tag %02x at %lu\n",413 tag, (unsigned long)ctx - ctx->data);414 return -EBADMSG;415 }416 417 /* We have to correct for the header so that the X.509 parser can start418 * from the beginning. Note that since X.509 stipulates DER, there419 * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which420 * stipulates BER).421 */422 value -= hdrlen;423 vlen += hdrlen;424 425 if (((u8*)value)[1] == 0x80)426 vlen += 2; /* Indefinite length - there should be an EOC */427 428 x509 = x509_cert_parse(value, vlen);429 if (IS_ERR(x509))430 return PTR_ERR(x509);431 432 x509->index = ++ctx->x509_index;433 pr_debug("Got cert %u for %s\n", x509->index, x509->subject);434 pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);435 436 *ctx->ppcerts = x509;437 ctx->ppcerts = &x509->next;438 return 0;439}440 441/*442 * Save the certificate list443 */444int pkcs7_note_certificate_list(void *context, size_t hdrlen,445 unsigned char tag,446 const void *value, size_t vlen)447{448 struct pkcs7_parse_context *ctx = context;449 450 pr_devel("Got cert list (%02x)\n", tag);451 452 *ctx->ppcerts = ctx->msg->certs;453 ctx->msg->certs = ctx->certs;454 ctx->certs = NULL;455 ctx->ppcerts = &ctx->certs;456 return 0;457}458 459/*460 * Note the content type.461 */462int pkcs7_note_content(void *context, size_t hdrlen,463 unsigned char tag,464 const void *value, size_t vlen)465{466 struct pkcs7_parse_context *ctx = context;467 468 if (ctx->last_oid != OID_data &&469 ctx->last_oid != OID_msIndirectData) {470 pr_warn("Unsupported data type %d\n", ctx->last_oid);471 return -EINVAL;472 }473 474 ctx->msg->data_type = ctx->last_oid;475 return 0;476}477 478/*479 * Extract the data from the message and store that and its content type OID in480 * the context.481 */482int pkcs7_note_data(void *context, size_t hdrlen,483 unsigned char tag,484 const void *value, size_t vlen)485{486 struct pkcs7_parse_context *ctx = context;487 488 pr_debug("Got data\n");489 490 ctx->msg->data = value;491 ctx->msg->data_len = vlen;492 ctx->msg->data_hdrlen = hdrlen;493 return 0;494}495 496/*497 * Parse authenticated attributes.498 */499int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,500 unsigned char tag,501 const void *value, size_t vlen)502{503 struct pkcs7_parse_context *ctx = context;504 struct pkcs7_signed_info *sinfo = ctx->sinfo;505 enum OID content_type;506 507 pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);508 509 switch (ctx->last_oid) {510 case OID_contentType:511 if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))512 goto repeated;513 content_type = look_up_OID(value, vlen);514 if (content_type != ctx->msg->data_type) {515 pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",516 ctx->msg->data_type, sinfo->index,517 content_type);518 return -EBADMSG;519 }520 return 0;521 522 case OID_signingTime:523 if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))524 goto repeated;525 /* Should we check that the signing time is consistent526 * with the signer's X.509 cert?527 */528 return x509_decode_time(&sinfo->signing_time,529 hdrlen, tag, value, vlen);530 531 case OID_messageDigest:532 if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))533 goto repeated;534 if (tag != ASN1_OTS)535 return -EBADMSG;536 sinfo->msgdigest = value;537 sinfo->msgdigest_len = vlen;538 return 0;539 540 case OID_smimeCapabilites:541 if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))542 goto repeated;543 if (ctx->msg->data_type != OID_msIndirectData) {544 pr_warn("S/MIME Caps only allowed with Authenticode\n");545 return -EKEYREJECTED;546 }547 return 0;548 549 /* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE550 * char URLs and cont[1] 8-bit char URLs.551 *552 * Microsoft StatementType seems to contain a list of OIDs that553 * are also used as extendedKeyUsage types in X.509 certs.554 */555 case OID_msSpOpusInfo:556 if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))557 goto repeated;558 goto authenticode_check;559 case OID_msStatementType:560 if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))561 goto repeated;562 authenticode_check:563 if (ctx->msg->data_type != OID_msIndirectData) {564 pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");565 return -EKEYREJECTED;566 }567 /* I'm not sure how to validate these */568 return 0;569 default:570 return 0;571 }572 573repeated:574 /* We permit max one item per AuthenticatedAttribute and no repeats */575 pr_warn("Repeated/multivalue AuthAttrs not permitted\n");576 return -EKEYREJECTED;577}578 579/*580 * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]581 */582int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,583 unsigned char tag,584 const void *value, size_t vlen)585{586 struct pkcs7_parse_context *ctx = context;587 struct pkcs7_signed_info *sinfo = ctx->sinfo;588 589 if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||590 !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {591 pr_warn("Missing required AuthAttr\n");592 return -EBADMSG;593 }594 595 if (ctx->msg->data_type != OID_msIndirectData &&596 test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {597 pr_warn("Unexpected Authenticode AuthAttr\n");598 return -EBADMSG;599 }600 601 /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */602 sinfo->authattrs = value - (hdrlen - 1);603 sinfo->authattrs_len = vlen + (hdrlen - 1);604 return 0;605}606 607/*608 * Note the issuing certificate serial number609 */610int pkcs7_sig_note_serial(void *context, size_t hdrlen,611 unsigned char tag,612 const void *value, size_t vlen)613{614 struct pkcs7_parse_context *ctx = context;615 ctx->raw_serial = value;616 ctx->raw_serial_size = vlen;617 return 0;618}619 620/*621 * Note the issuer's name622 */623int pkcs7_sig_note_issuer(void *context, size_t hdrlen,624 unsigned char tag,625 const void *value, size_t vlen)626{627 struct pkcs7_parse_context *ctx = context;628 ctx->raw_issuer = value;629 ctx->raw_issuer_size = vlen;630 return 0;631}632 633/*634 * Note the issuing cert's subjectKeyIdentifier635 */636int pkcs7_sig_note_skid(void *context, size_t hdrlen,637 unsigned char tag,638 const void *value, size_t vlen)639{640 struct pkcs7_parse_context *ctx = context;641 642 pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);643 644 ctx->raw_skid = value;645 ctx->raw_skid_size = vlen;646 return 0;647}648 649/*650 * Note the signature data651 */652int pkcs7_sig_note_signature(void *context, size_t hdrlen,653 unsigned char tag,654 const void *value, size_t vlen)655{656 struct pkcs7_parse_context *ctx = context;657 658 ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);659 if (!ctx->sinfo->sig->s)660 return -ENOMEM;661 662 ctx->sinfo->sig->s_size = vlen;663 return 0;664}665 666/*667 * Note a signature information block668 */669int pkcs7_note_signed_info(void *context, size_t hdrlen,670 unsigned char tag,671 const void *value, size_t vlen)672{673 struct pkcs7_parse_context *ctx = context;674 struct pkcs7_signed_info *sinfo = ctx->sinfo;675 struct asymmetric_key_id *kid;676 677 if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {678 pr_warn("Authenticode requires AuthAttrs\n");679 return -EBADMSG;680 }681 682 /* Generate cert issuer + serial number key ID */683 if (!ctx->expect_skid) {684 kid = asymmetric_key_generate_id(ctx->raw_serial,685 ctx->raw_serial_size,686 ctx->raw_issuer,687 ctx->raw_issuer_size);688 } else {689 kid = asymmetric_key_generate_id(ctx->raw_skid,690 ctx->raw_skid_size,691 "", 0);692 }693 if (IS_ERR(kid))694 return PTR_ERR(kid);695 696 pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);697 698 sinfo->sig->auth_ids[0] = kid;699 sinfo->index = ++ctx->sinfo_index;700 *ctx->ppsinfo = sinfo;701 ctx->ppsinfo = &sinfo->next;702 ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);703 if (!ctx->sinfo)704 return -ENOMEM;705 ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),706 GFP_KERNEL);707 if (!ctx->sinfo->sig)708 return -ENOMEM;709 return 0;710}711