162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* PKCS#7 parser 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "PKCS7: "fmt 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/oid_registry.h> 1562306a36Sopenharmony_ci#include <crypto/public_key.h> 1662306a36Sopenharmony_ci#include "pkcs7_parser.h" 1762306a36Sopenharmony_ci#include "pkcs7.asn1.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ciMODULE_DESCRIPTION("PKCS#7 parser"); 2062306a36Sopenharmony_ciMODULE_AUTHOR("Red Hat, Inc."); 2162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct pkcs7_parse_context { 2462306a36Sopenharmony_ci struct pkcs7_message *msg; /* Message being constructed */ 2562306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */ 2662306a36Sopenharmony_ci struct pkcs7_signed_info **ppsinfo; 2762306a36Sopenharmony_ci struct x509_certificate *certs; /* Certificate cache */ 2862306a36Sopenharmony_ci struct x509_certificate **ppcerts; 2962306a36Sopenharmony_ci unsigned long data; /* Start of data */ 3062306a36Sopenharmony_ci enum OID last_oid; /* Last OID encountered */ 3162306a36Sopenharmony_ci unsigned x509_index; 3262306a36Sopenharmony_ci unsigned sinfo_index; 3362306a36Sopenharmony_ci const void *raw_serial; 3462306a36Sopenharmony_ci unsigned raw_serial_size; 3562306a36Sopenharmony_ci unsigned raw_issuer_size; 3662306a36Sopenharmony_ci const void *raw_issuer; 3762306a36Sopenharmony_ci const void *raw_skid; 3862306a36Sopenharmony_ci unsigned raw_skid_size; 3962306a36Sopenharmony_ci bool expect_skid; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * Free a signed information block. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistatic void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci if (sinfo) { 4862306a36Sopenharmony_ci public_key_signature_free(sinfo->sig); 4962306a36Sopenharmony_ci kfree(sinfo); 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * pkcs7_free_message - Free a PKCS#7 message 5562306a36Sopenharmony_ci * @pkcs7: The PKCS#7 message to free 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_civoid pkcs7_free_message(struct pkcs7_message *pkcs7) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct x509_certificate *cert; 6062306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (pkcs7) { 6362306a36Sopenharmony_ci while (pkcs7->certs) { 6462306a36Sopenharmony_ci cert = pkcs7->certs; 6562306a36Sopenharmony_ci pkcs7->certs = cert->next; 6662306a36Sopenharmony_ci x509_free_certificate(cert); 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci while (pkcs7->crl) { 6962306a36Sopenharmony_ci cert = pkcs7->crl; 7062306a36Sopenharmony_ci pkcs7->crl = cert->next; 7162306a36Sopenharmony_ci x509_free_certificate(cert); 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci while (pkcs7->signed_infos) { 7462306a36Sopenharmony_ci sinfo = pkcs7->signed_infos; 7562306a36Sopenharmony_ci pkcs7->signed_infos = sinfo->next; 7662306a36Sopenharmony_ci pkcs7_free_signed_info(sinfo); 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci kfree(pkcs7); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pkcs7_free_message); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* 8462306a36Sopenharmony_ci * Check authenticatedAttributes are provided or not provided consistently. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_cistatic int pkcs7_check_authattrs(struct pkcs7_message *msg) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo; 8962306a36Sopenharmony_ci bool want = false; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci sinfo = msg->signed_infos; 9262306a36Sopenharmony_ci if (!sinfo) 9362306a36Sopenharmony_ci goto inconsistent; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (sinfo->authattrs) { 9662306a36Sopenharmony_ci want = true; 9762306a36Sopenharmony_ci msg->have_authattrs = true; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) 10162306a36Sopenharmony_ci if (!!sinfo->authattrs != want) 10262306a36Sopenharmony_ci goto inconsistent; 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciinconsistent: 10662306a36Sopenharmony_ci pr_warn("Inconsistently supplied authAttrs\n"); 10762306a36Sopenharmony_ci return -EINVAL; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/** 11162306a36Sopenharmony_ci * pkcs7_parse_message - Parse a PKCS#7 message 11262306a36Sopenharmony_ci * @data: The raw binary ASN.1 encoded message to be parsed 11362306a36Sopenharmony_ci * @datalen: The size of the encoded message 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistruct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct pkcs7_parse_context *ctx; 11862306a36Sopenharmony_ci struct pkcs7_message *msg = ERR_PTR(-ENOMEM); 11962306a36Sopenharmony_ci int ret; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); 12262306a36Sopenharmony_ci if (!ctx) 12362306a36Sopenharmony_ci goto out_no_ctx; 12462306a36Sopenharmony_ci ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); 12562306a36Sopenharmony_ci if (!ctx->msg) 12662306a36Sopenharmony_ci goto out_no_msg; 12762306a36Sopenharmony_ci ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); 12862306a36Sopenharmony_ci if (!ctx->sinfo) 12962306a36Sopenharmony_ci goto out_no_sinfo; 13062306a36Sopenharmony_ci ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), 13162306a36Sopenharmony_ci GFP_KERNEL); 13262306a36Sopenharmony_ci if (!ctx->sinfo->sig) 13362306a36Sopenharmony_ci goto out_no_sig; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ctx->data = (unsigned long)data; 13662306a36Sopenharmony_ci ctx->ppcerts = &ctx->certs; 13762306a36Sopenharmony_ci ctx->ppsinfo = &ctx->msg->signed_infos; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Attempt to decode the signature */ 14062306a36Sopenharmony_ci ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); 14162306a36Sopenharmony_ci if (ret < 0) { 14262306a36Sopenharmony_ci msg = ERR_PTR(ret); 14362306a36Sopenharmony_ci goto out; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ret = pkcs7_check_authattrs(ctx->msg); 14762306a36Sopenharmony_ci if (ret < 0) { 14862306a36Sopenharmony_ci msg = ERR_PTR(ret); 14962306a36Sopenharmony_ci goto out; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci msg = ctx->msg; 15362306a36Sopenharmony_ci ctx->msg = NULL; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ciout: 15662306a36Sopenharmony_ci while (ctx->certs) { 15762306a36Sopenharmony_ci struct x509_certificate *cert = ctx->certs; 15862306a36Sopenharmony_ci ctx->certs = cert->next; 15962306a36Sopenharmony_ci x509_free_certificate(cert); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ciout_no_sig: 16262306a36Sopenharmony_ci pkcs7_free_signed_info(ctx->sinfo); 16362306a36Sopenharmony_ciout_no_sinfo: 16462306a36Sopenharmony_ci pkcs7_free_message(ctx->msg); 16562306a36Sopenharmony_ciout_no_msg: 16662306a36Sopenharmony_ci kfree(ctx); 16762306a36Sopenharmony_ciout_no_ctx: 16862306a36Sopenharmony_ci return msg; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pkcs7_parse_message); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/** 17362306a36Sopenharmony_ci * pkcs7_get_content_data - Get access to the PKCS#7 content 17462306a36Sopenharmony_ci * @pkcs7: The preparsed PKCS#7 message to access 17562306a36Sopenharmony_ci * @_data: Place to return a pointer to the data 17662306a36Sopenharmony_ci * @_data_len: Place to return the data length 17762306a36Sopenharmony_ci * @_headerlen: Size of ASN.1 header not included in _data 17862306a36Sopenharmony_ci * 17962306a36Sopenharmony_ci * Get access to the data content of the PKCS#7 message. The size of the 18062306a36Sopenharmony_ci * header of the ASN.1 object that contains it is also provided and can be used 18162306a36Sopenharmony_ci * to adjust *_data and *_data_len to get the entire object. 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Returns -ENODATA if the data object was missing from the message. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_ciint pkcs7_get_content_data(const struct pkcs7_message *pkcs7, 18662306a36Sopenharmony_ci const void **_data, size_t *_data_len, 18762306a36Sopenharmony_ci size_t *_headerlen) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci if (!pkcs7->data) 19062306a36Sopenharmony_ci return -ENODATA; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci *_data = pkcs7->data; 19362306a36Sopenharmony_ci *_data_len = pkcs7->data_len; 19462306a36Sopenharmony_ci if (_headerlen) 19562306a36Sopenharmony_ci *_headerlen = pkcs7->data_hdrlen; 19662306a36Sopenharmony_ci return 0; 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pkcs7_get_content_data); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* 20162306a36Sopenharmony_ci * Note an OID when we find one for later processing when we know how 20262306a36Sopenharmony_ci * to interpret it. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ciint pkcs7_note_OID(void *context, size_t hdrlen, 20562306a36Sopenharmony_ci unsigned char tag, 20662306a36Sopenharmony_ci const void *value, size_t vlen) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci ctx->last_oid = look_up_OID(value, vlen); 21162306a36Sopenharmony_ci if (ctx->last_oid == OID__NR) { 21262306a36Sopenharmony_ci char buffer[50]; 21362306a36Sopenharmony_ci sprint_oid(value, vlen, buffer, sizeof(buffer)); 21462306a36Sopenharmony_ci printk("PKCS7: Unknown OID: [%lu] %s\n", 21562306a36Sopenharmony_ci (unsigned long)value - ctx->data, buffer); 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/* 22162306a36Sopenharmony_ci * Note the digest algorithm for the signature. 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ciint pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, 22462306a36Sopenharmony_ci unsigned char tag, 22562306a36Sopenharmony_ci const void *value, size_t vlen) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci switch (ctx->last_oid) { 23062306a36Sopenharmony_ci case OID_md4: 23162306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "md4"; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case OID_md5: 23462306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "md5"; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci case OID_sha1: 23762306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "sha1"; 23862306a36Sopenharmony_ci break; 23962306a36Sopenharmony_ci case OID_sha256: 24062306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "sha256"; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci case OID_sha384: 24362306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "sha384"; 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci case OID_sha512: 24662306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "sha512"; 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci case OID_sha224: 24962306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "sha224"; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case OID_sm3: 25262306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "sm3"; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case OID_gost2012Digest256: 25562306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "streebog256"; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci case OID_gost2012Digest512: 25862306a36Sopenharmony_ci ctx->sinfo->sig->hash_algo = "streebog512"; 25962306a36Sopenharmony_ci break; 26062306a36Sopenharmony_ci default: 26162306a36Sopenharmony_ci printk("Unsupported digest algo: %u\n", ctx->last_oid); 26262306a36Sopenharmony_ci return -ENOPKG; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * Note the public key algorithm for the signature. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_ciint pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, 27162306a36Sopenharmony_ci unsigned char tag, 27262306a36Sopenharmony_ci const void *value, size_t vlen) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci switch (ctx->last_oid) { 27762306a36Sopenharmony_ci case OID_rsaEncryption: 27862306a36Sopenharmony_ci ctx->sinfo->sig->pkey_algo = "rsa"; 27962306a36Sopenharmony_ci ctx->sinfo->sig->encoding = "pkcs1"; 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci case OID_id_ecdsa_with_sha1: 28262306a36Sopenharmony_ci case OID_id_ecdsa_with_sha224: 28362306a36Sopenharmony_ci case OID_id_ecdsa_with_sha256: 28462306a36Sopenharmony_ci case OID_id_ecdsa_with_sha384: 28562306a36Sopenharmony_ci case OID_id_ecdsa_with_sha512: 28662306a36Sopenharmony_ci ctx->sinfo->sig->pkey_algo = "ecdsa"; 28762306a36Sopenharmony_ci ctx->sinfo->sig->encoding = "x962"; 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case OID_SM2_with_SM3: 29062306a36Sopenharmony_ci ctx->sinfo->sig->pkey_algo = "sm2"; 29162306a36Sopenharmony_ci ctx->sinfo->sig->encoding = "raw"; 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci case OID_gost2012PKey256: 29462306a36Sopenharmony_ci case OID_gost2012PKey512: 29562306a36Sopenharmony_ci ctx->sinfo->sig->pkey_algo = "ecrdsa"; 29662306a36Sopenharmony_ci ctx->sinfo->sig->encoding = "raw"; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci printk("Unsupported pkey algo: %u\n", ctx->last_oid); 30062306a36Sopenharmony_ci return -ENOPKG; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/* 30662306a36Sopenharmony_ci * We only support signed data [RFC2315 sec 9]. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ciint pkcs7_check_content_type(void *context, size_t hdrlen, 30962306a36Sopenharmony_ci unsigned char tag, 31062306a36Sopenharmony_ci const void *value, size_t vlen) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (ctx->last_oid != OID_signed_data) { 31562306a36Sopenharmony_ci pr_warn("Only support pkcs7_signedData type\n"); 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci/* 32362306a36Sopenharmony_ci * Note the SignedData version 32462306a36Sopenharmony_ci */ 32562306a36Sopenharmony_ciint pkcs7_note_signeddata_version(void *context, size_t hdrlen, 32662306a36Sopenharmony_ci unsigned char tag, 32762306a36Sopenharmony_ci const void *value, size_t vlen) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 33062306a36Sopenharmony_ci unsigned version; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (vlen != 1) 33362306a36Sopenharmony_ci goto unsupported; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ctx->msg->version = version = *(const u8 *)value; 33662306a36Sopenharmony_ci switch (version) { 33762306a36Sopenharmony_ci case 1: 33862306a36Sopenharmony_ci /* PKCS#7 SignedData [RFC2315 sec 9.1] 33962306a36Sopenharmony_ci * CMS ver 1 SignedData [RFC5652 sec 5.1] 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case 3: 34362306a36Sopenharmony_ci /* CMS ver 3 SignedData [RFC2315 sec 5.1] */ 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci default: 34662306a36Sopenharmony_ci goto unsupported; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ciunsupported: 35262306a36Sopenharmony_ci pr_warn("Unsupported SignedData version\n"); 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* 35762306a36Sopenharmony_ci * Note the SignerInfo version 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ciint pkcs7_note_signerinfo_version(void *context, size_t hdrlen, 36062306a36Sopenharmony_ci unsigned char tag, 36162306a36Sopenharmony_ci const void *value, size_t vlen) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 36462306a36Sopenharmony_ci unsigned version; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (vlen != 1) 36762306a36Sopenharmony_ci goto unsupported; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci version = *(const u8 *)value; 37062306a36Sopenharmony_ci switch (version) { 37162306a36Sopenharmony_ci case 1: 37262306a36Sopenharmony_ci /* PKCS#7 SignerInfo [RFC2315 sec 9.2] 37362306a36Sopenharmony_ci * CMS ver 1 SignerInfo [RFC5652 sec 5.3] 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci if (ctx->msg->version != 1) 37662306a36Sopenharmony_ci goto version_mismatch; 37762306a36Sopenharmony_ci ctx->expect_skid = false; 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci case 3: 38062306a36Sopenharmony_ci /* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */ 38162306a36Sopenharmony_ci if (ctx->msg->version == 1) 38262306a36Sopenharmony_ci goto version_mismatch; 38362306a36Sopenharmony_ci ctx->expect_skid = true; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci default: 38662306a36Sopenharmony_ci goto unsupported; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ciunsupported: 39262306a36Sopenharmony_ci pr_warn("Unsupported SignerInfo version\n"); 39362306a36Sopenharmony_ci return -EINVAL; 39462306a36Sopenharmony_civersion_mismatch: 39562306a36Sopenharmony_ci pr_warn("SignedData-SignerInfo version mismatch\n"); 39662306a36Sopenharmony_ci return -EBADMSG; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/* 40062306a36Sopenharmony_ci * Extract a certificate and store it in the context. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ciint pkcs7_extract_cert(void *context, size_t hdrlen, 40362306a36Sopenharmony_ci unsigned char tag, 40462306a36Sopenharmony_ci const void *value, size_t vlen) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 40762306a36Sopenharmony_ci struct x509_certificate *x509; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) { 41062306a36Sopenharmony_ci pr_debug("Cert began with tag %02x at %lu\n", 41162306a36Sopenharmony_ci tag, (unsigned long)ctx - ctx->data); 41262306a36Sopenharmony_ci return -EBADMSG; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* We have to correct for the header so that the X.509 parser can start 41662306a36Sopenharmony_ci * from the beginning. Note that since X.509 stipulates DER, there 41762306a36Sopenharmony_ci * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which 41862306a36Sopenharmony_ci * stipulates BER). 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci value -= hdrlen; 42162306a36Sopenharmony_ci vlen += hdrlen; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (((u8*)value)[1] == 0x80) 42462306a36Sopenharmony_ci vlen += 2; /* Indefinite length - there should be an EOC */ 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci x509 = x509_cert_parse(value, vlen); 42762306a36Sopenharmony_ci if (IS_ERR(x509)) 42862306a36Sopenharmony_ci return PTR_ERR(x509); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci x509->index = ++ctx->x509_index; 43162306a36Sopenharmony_ci pr_debug("Got cert %u for %s\n", x509->index, x509->subject); 43262306a36Sopenharmony_ci pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci *ctx->ppcerts = x509; 43562306a36Sopenharmony_ci ctx->ppcerts = &x509->next; 43662306a36Sopenharmony_ci return 0; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * Save the certificate list 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_ciint pkcs7_note_certificate_list(void *context, size_t hdrlen, 44362306a36Sopenharmony_ci unsigned char tag, 44462306a36Sopenharmony_ci const void *value, size_t vlen) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci pr_devel("Got cert list (%02x)\n", tag); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci *ctx->ppcerts = ctx->msg->certs; 45162306a36Sopenharmony_ci ctx->msg->certs = ctx->certs; 45262306a36Sopenharmony_ci ctx->certs = NULL; 45362306a36Sopenharmony_ci ctx->ppcerts = &ctx->certs; 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* 45862306a36Sopenharmony_ci * Note the content type. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_ciint pkcs7_note_content(void *context, size_t hdrlen, 46162306a36Sopenharmony_ci unsigned char tag, 46262306a36Sopenharmony_ci const void *value, size_t vlen) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (ctx->last_oid != OID_data && 46762306a36Sopenharmony_ci ctx->last_oid != OID_msIndirectData) { 46862306a36Sopenharmony_ci pr_warn("Unsupported data type %d\n", ctx->last_oid); 46962306a36Sopenharmony_ci return -EINVAL; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci ctx->msg->data_type = ctx->last_oid; 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/* 47762306a36Sopenharmony_ci * Extract the data from the message and store that and its content type OID in 47862306a36Sopenharmony_ci * the context. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ciint pkcs7_note_data(void *context, size_t hdrlen, 48162306a36Sopenharmony_ci unsigned char tag, 48262306a36Sopenharmony_ci const void *value, size_t vlen) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci pr_debug("Got data\n"); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ctx->msg->data = value; 48962306a36Sopenharmony_ci ctx->msg->data_len = vlen; 49062306a36Sopenharmony_ci ctx->msg->data_hdrlen = hdrlen; 49162306a36Sopenharmony_ci return 0; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci/* 49562306a36Sopenharmony_ci * Parse authenticated attributes. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ciint pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen, 49862306a36Sopenharmony_ci unsigned char tag, 49962306a36Sopenharmony_ci const void *value, size_t vlen) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 50262306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo = ctx->sinfo; 50362306a36Sopenharmony_ci enum OID content_type; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci switch (ctx->last_oid) { 50862306a36Sopenharmony_ci case OID_contentType: 50962306a36Sopenharmony_ci if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set)) 51062306a36Sopenharmony_ci goto repeated; 51162306a36Sopenharmony_ci content_type = look_up_OID(value, vlen); 51262306a36Sopenharmony_ci if (content_type != ctx->msg->data_type) { 51362306a36Sopenharmony_ci pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n", 51462306a36Sopenharmony_ci ctx->msg->data_type, sinfo->index, 51562306a36Sopenharmony_ci content_type); 51662306a36Sopenharmony_ci return -EBADMSG; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci case OID_signingTime: 52162306a36Sopenharmony_ci if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set)) 52262306a36Sopenharmony_ci goto repeated; 52362306a36Sopenharmony_ci /* Should we check that the signing time is consistent 52462306a36Sopenharmony_ci * with the signer's X.509 cert? 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci return x509_decode_time(&sinfo->signing_time, 52762306a36Sopenharmony_ci hdrlen, tag, value, vlen); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci case OID_messageDigest: 53062306a36Sopenharmony_ci if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set)) 53162306a36Sopenharmony_ci goto repeated; 53262306a36Sopenharmony_ci if (tag != ASN1_OTS) 53362306a36Sopenharmony_ci return -EBADMSG; 53462306a36Sopenharmony_ci sinfo->msgdigest = value; 53562306a36Sopenharmony_ci sinfo->msgdigest_len = vlen; 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci case OID_smimeCapabilites: 53962306a36Sopenharmony_ci if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set)) 54062306a36Sopenharmony_ci goto repeated; 54162306a36Sopenharmony_ci if (ctx->msg->data_type != OID_msIndirectData) { 54262306a36Sopenharmony_ci pr_warn("S/MIME Caps only allowed with Authenticode\n"); 54362306a36Sopenharmony_ci return -EKEYREJECTED; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE 54862306a36Sopenharmony_ci * char URLs and cont[1] 8-bit char URLs. 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * Microsoft StatementType seems to contain a list of OIDs that 55162306a36Sopenharmony_ci * are also used as extendedKeyUsage types in X.509 certs. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci case OID_msSpOpusInfo: 55462306a36Sopenharmony_ci if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) 55562306a36Sopenharmony_ci goto repeated; 55662306a36Sopenharmony_ci goto authenticode_check; 55762306a36Sopenharmony_ci case OID_msStatementType: 55862306a36Sopenharmony_ci if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set)) 55962306a36Sopenharmony_ci goto repeated; 56062306a36Sopenharmony_ci authenticode_check: 56162306a36Sopenharmony_ci if (ctx->msg->data_type != OID_msIndirectData) { 56262306a36Sopenharmony_ci pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n"); 56362306a36Sopenharmony_ci return -EKEYREJECTED; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci /* I'm not sure how to validate these */ 56662306a36Sopenharmony_ci return 0; 56762306a36Sopenharmony_ci default: 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cirepeated: 57262306a36Sopenharmony_ci /* We permit max one item per AuthenticatedAttribute and no repeats */ 57362306a36Sopenharmony_ci pr_warn("Repeated/multivalue AuthAttrs not permitted\n"); 57462306a36Sopenharmony_ci return -EKEYREJECTED; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* 57862306a36Sopenharmony_ci * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3] 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ciint pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, 58162306a36Sopenharmony_ci unsigned char tag, 58262306a36Sopenharmony_ci const void *value, size_t vlen) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 58562306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo = ctx->sinfo; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) || 58862306a36Sopenharmony_ci !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) { 58962306a36Sopenharmony_ci pr_warn("Missing required AuthAttr\n"); 59062306a36Sopenharmony_ci return -EBADMSG; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (ctx->msg->data_type != OID_msIndirectData && 59462306a36Sopenharmony_ci test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) { 59562306a36Sopenharmony_ci pr_warn("Unexpected Authenticode AuthAttr\n"); 59662306a36Sopenharmony_ci return -EBADMSG; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ 60062306a36Sopenharmony_ci sinfo->authattrs = value - (hdrlen - 1); 60162306a36Sopenharmony_ci sinfo->authattrs_len = vlen + (hdrlen - 1); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci/* 60662306a36Sopenharmony_ci * Note the issuing certificate serial number 60762306a36Sopenharmony_ci */ 60862306a36Sopenharmony_ciint pkcs7_sig_note_serial(void *context, size_t hdrlen, 60962306a36Sopenharmony_ci unsigned char tag, 61062306a36Sopenharmony_ci const void *value, size_t vlen) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 61362306a36Sopenharmony_ci ctx->raw_serial = value; 61462306a36Sopenharmony_ci ctx->raw_serial_size = vlen; 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci * Note the issuer's name 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ciint pkcs7_sig_note_issuer(void *context, size_t hdrlen, 62262306a36Sopenharmony_ci unsigned char tag, 62362306a36Sopenharmony_ci const void *value, size_t vlen) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 62662306a36Sopenharmony_ci ctx->raw_issuer = value; 62762306a36Sopenharmony_ci ctx->raw_issuer_size = vlen; 62862306a36Sopenharmony_ci return 0; 62962306a36Sopenharmony_ci} 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci/* 63262306a36Sopenharmony_ci * Note the issuing cert's subjectKeyIdentifier 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ciint pkcs7_sig_note_skid(void *context, size_t hdrlen, 63562306a36Sopenharmony_ci unsigned char tag, 63662306a36Sopenharmony_ci const void *value, size_t vlen) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ctx->raw_skid = value; 64362306a36Sopenharmony_ci ctx->raw_skid_size = vlen; 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci/* 64862306a36Sopenharmony_ci * Note the signature data 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_ciint pkcs7_sig_note_signature(void *context, size_t hdrlen, 65162306a36Sopenharmony_ci unsigned char tag, 65262306a36Sopenharmony_ci const void *value, size_t vlen) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL); 65762306a36Sopenharmony_ci if (!ctx->sinfo->sig->s) 65862306a36Sopenharmony_ci return -ENOMEM; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci ctx->sinfo->sig->s_size = vlen; 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* 66562306a36Sopenharmony_ci * Note a signature information block 66662306a36Sopenharmony_ci */ 66762306a36Sopenharmony_ciint pkcs7_note_signed_info(void *context, size_t hdrlen, 66862306a36Sopenharmony_ci unsigned char tag, 66962306a36Sopenharmony_ci const void *value, size_t vlen) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct pkcs7_parse_context *ctx = context; 67262306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo = ctx->sinfo; 67362306a36Sopenharmony_ci struct asymmetric_key_id *kid; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) { 67662306a36Sopenharmony_ci pr_warn("Authenticode requires AuthAttrs\n"); 67762306a36Sopenharmony_ci return -EBADMSG; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* Generate cert issuer + serial number key ID */ 68162306a36Sopenharmony_ci if (!ctx->expect_skid) { 68262306a36Sopenharmony_ci kid = asymmetric_key_generate_id(ctx->raw_serial, 68362306a36Sopenharmony_ci ctx->raw_serial_size, 68462306a36Sopenharmony_ci ctx->raw_issuer, 68562306a36Sopenharmony_ci ctx->raw_issuer_size); 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci kid = asymmetric_key_generate_id(ctx->raw_skid, 68862306a36Sopenharmony_ci ctx->raw_skid_size, 68962306a36Sopenharmony_ci "", 0); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci if (IS_ERR(kid)) 69262306a36Sopenharmony_ci return PTR_ERR(kid); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci sinfo->sig->auth_ids[0] = kid; 69762306a36Sopenharmony_ci sinfo->index = ++ctx->sinfo_index; 69862306a36Sopenharmony_ci *ctx->ppsinfo = sinfo; 69962306a36Sopenharmony_ci ctx->ppsinfo = &sinfo->next; 70062306a36Sopenharmony_ci ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); 70162306a36Sopenharmony_ci if (!ctx->sinfo) 70262306a36Sopenharmony_ci return -ENOMEM; 70362306a36Sopenharmony_ci ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), 70462306a36Sopenharmony_ci GFP_KERNEL); 70562306a36Sopenharmony_ci if (!ctx->sinfo->sig) 70662306a36Sopenharmony_ci return -ENOMEM; 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 709