Path: blob/master/crypto/asymmetric_keys/pkcs7_parser.c
50868 views
// SPDX-License-Identifier: GPL-2.0-or-later1/* PKCS#7 parser2*3* Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.4* Written by David Howells ([email protected])5*/67#define pr_fmt(fmt) "PKCS7: "fmt8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/export.h>11#include <linux/slab.h>12#include <linux/err.h>13#include <linux/oid_registry.h>14#include <crypto/public_key.h>15#include "pkcs7_parser.h"16#include "pkcs7.asn1.h"1718MODULE_DESCRIPTION("PKCS#7 parser");19MODULE_AUTHOR("Red Hat, Inc.");20MODULE_LICENSE("GPL");2122struct pkcs7_parse_context {23struct pkcs7_message *msg; /* Message being constructed */24struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */25struct pkcs7_signed_info **ppsinfo;26struct x509_certificate *certs; /* Certificate cache */27struct x509_certificate **ppcerts;28unsigned long data; /* Start of data */29enum OID last_oid; /* Last OID encountered */30unsigned x509_index;31unsigned sinfo_index;32const void *raw_serial;33unsigned raw_serial_size;34unsigned raw_issuer_size;35const void *raw_issuer;36const void *raw_skid;37unsigned raw_skid_size;38bool expect_skid;39};4041/*42* Free a signed information block.43*/44static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)45{46if (sinfo) {47public_key_signature_free(sinfo->sig);48kfree(sinfo);49}50}5152/**53* pkcs7_free_message - Free a PKCS#7 message54* @pkcs7: The PKCS#7 message to free55*/56void pkcs7_free_message(struct pkcs7_message *pkcs7)57{58struct x509_certificate *cert;59struct pkcs7_signed_info *sinfo;6061if (pkcs7) {62while (pkcs7->certs) {63cert = pkcs7->certs;64pkcs7->certs = cert->next;65x509_free_certificate(cert);66}67while (pkcs7->crl) {68cert = pkcs7->crl;69pkcs7->crl = cert->next;70x509_free_certificate(cert);71}72while (pkcs7->signed_infos) {73sinfo = pkcs7->signed_infos;74pkcs7->signed_infos = sinfo->next;75pkcs7_free_signed_info(sinfo);76}77kfree(pkcs7);78}79}80EXPORT_SYMBOL_GPL(pkcs7_free_message);8182/*83* Check authenticatedAttributes are provided or not provided consistently.84*/85static int pkcs7_check_authattrs(struct pkcs7_message *msg)86{87struct pkcs7_signed_info *sinfo;88bool want = false;8990sinfo = msg->signed_infos;91if (!sinfo)92goto inconsistent;9394#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA95msg->authattrs_rej_waivable = true;96#endif9798if (sinfo->authattrs) {99want = true;100msg->have_authattrs = true;101#ifdef CONFIG_PKCS7_WAIVE_AUTHATTRS_REJECTION_FOR_MLDSA102if (strncmp(sinfo->sig->pkey_algo, "mldsa", 5) != 0)103msg->authattrs_rej_waivable = false;104#endif105} else if (sinfo->sig->algo_takes_data) {106sinfo->sig->hash_algo = "none";107}108109for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) {110if (!!sinfo->authattrs != want)111goto inconsistent;112113if (!sinfo->authattrs &&114sinfo->sig->algo_takes_data)115sinfo->sig->hash_algo = "none";116}117return 0;118119inconsistent:120pr_warn("Inconsistently supplied authAttrs\n");121return -EINVAL;122}123124/**125* pkcs7_parse_message - Parse a PKCS#7 message126* @data: The raw binary ASN.1 encoded message to be parsed127* @datalen: The size of the encoded message128*/129struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)130{131struct pkcs7_parse_context *ctx;132struct pkcs7_message *msg = ERR_PTR(-ENOMEM);133int ret;134135ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);136if (!ctx)137goto out_no_ctx;138ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);139if (!ctx->msg)140goto out_no_msg;141ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);142if (!ctx->sinfo)143goto out_no_sinfo;144ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),145GFP_KERNEL);146if (!ctx->sinfo->sig)147goto out_no_sig;148149ctx->data = (unsigned long)data;150ctx->ppcerts = &ctx->certs;151ctx->ppsinfo = &ctx->msg->signed_infos;152153/* Attempt to decode the signature */154ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);155if (ret < 0) {156msg = ERR_PTR(ret);157goto out;158}159160ret = pkcs7_check_authattrs(ctx->msg);161if (ret < 0) {162msg = ERR_PTR(ret);163goto out;164}165166msg = ctx->msg;167ctx->msg = NULL;168169out:170while (ctx->certs) {171struct x509_certificate *cert = ctx->certs;172ctx->certs = cert->next;173x509_free_certificate(cert);174}175out_no_sig:176pkcs7_free_signed_info(ctx->sinfo);177out_no_sinfo:178pkcs7_free_message(ctx->msg);179out_no_msg:180kfree(ctx);181out_no_ctx:182return msg;183}184EXPORT_SYMBOL_GPL(pkcs7_parse_message);185186/**187* pkcs7_get_content_data - Get access to the PKCS#7 content188* @pkcs7: The preparsed PKCS#7 message to access189* @_data: Place to return a pointer to the data190* @_data_len: Place to return the data length191* @_headerlen: Size of ASN.1 header not included in _data192*193* Get access to the data content of the PKCS#7 message. The size of the194* header of the ASN.1 object that contains it is also provided and can be used195* to adjust *_data and *_data_len to get the entire object.196*197* Returns -ENODATA if the data object was missing from the message.198*/199int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,200const void **_data, size_t *_data_len,201size_t *_headerlen)202{203if (!pkcs7->data)204return -ENODATA;205206*_data = pkcs7->data;207*_data_len = pkcs7->data_len;208if (_headerlen)209*_headerlen = pkcs7->data_hdrlen;210return 0;211}212EXPORT_SYMBOL_GPL(pkcs7_get_content_data);213214/*215* Note an OID when we find one for later processing when we know how216* to interpret it.217*/218int pkcs7_note_OID(void *context, size_t hdrlen,219unsigned char tag,220const void *value, size_t vlen)221{222struct pkcs7_parse_context *ctx = context;223224ctx->last_oid = look_up_OID(value, vlen);225if (ctx->last_oid == OID__NR) {226char buffer[50];227sprint_oid(value, vlen, buffer, sizeof(buffer));228printk("PKCS7: Unknown OID: [%lu] %s\n",229(unsigned long)value - ctx->data, buffer);230}231return 0;232}233234/*235* Note the digest algorithm for the signature.236*/237int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,238unsigned char tag,239const void *value, size_t vlen)240{241struct pkcs7_parse_context *ctx = context;242243switch (ctx->last_oid) {244case OID_sha1:245ctx->sinfo->sig->hash_algo = "sha1";246break;247case OID_sha256:248ctx->sinfo->sig->hash_algo = "sha256";249break;250case OID_sha384:251ctx->sinfo->sig->hash_algo = "sha384";252break;253case OID_sha512:254ctx->sinfo->sig->hash_algo = "sha512";255break;256case OID_sha224:257ctx->sinfo->sig->hash_algo = "sha224";258break;259case OID_sm3:260ctx->sinfo->sig->hash_algo = "sm3";261break;262case OID_gost2012Digest256:263ctx->sinfo->sig->hash_algo = "streebog256";264break;265case OID_gost2012Digest512:266ctx->sinfo->sig->hash_algo = "streebog512";267break;268case OID_sha3_256:269ctx->sinfo->sig->hash_algo = "sha3-256";270break;271case OID_sha3_384:272ctx->sinfo->sig->hash_algo = "sha3-384";273break;274case OID_sha3_512:275ctx->sinfo->sig->hash_algo = "sha3-512";276break;277default:278printk("Unsupported digest algo: %u\n", ctx->last_oid);279return -ENOPKG;280}281return 0;282}283284/*285* Note the public key algorithm for the signature.286*/287int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,288unsigned char tag,289const void *value, size_t vlen)290{291struct pkcs7_parse_context *ctx = context;292293switch (ctx->last_oid) {294case OID_rsaEncryption:295ctx->sinfo->sig->pkey_algo = "rsa";296ctx->sinfo->sig->encoding = "pkcs1";297break;298case OID_id_ecdsa_with_sha1:299case OID_id_ecdsa_with_sha224:300case OID_id_ecdsa_with_sha256:301case OID_id_ecdsa_with_sha384:302case OID_id_ecdsa_with_sha512:303case OID_id_ecdsa_with_sha3_256:304case OID_id_ecdsa_with_sha3_384:305case OID_id_ecdsa_with_sha3_512:306ctx->sinfo->sig->pkey_algo = "ecdsa";307ctx->sinfo->sig->encoding = "x962";308break;309case OID_gost2012PKey256:310case OID_gost2012PKey512:311ctx->sinfo->sig->pkey_algo = "ecrdsa";312ctx->sinfo->sig->encoding = "raw";313break;314case OID_id_ml_dsa_44:315ctx->sinfo->sig->pkey_algo = "mldsa44";316ctx->sinfo->sig->encoding = "raw";317ctx->sinfo->sig->algo_takes_data = true;318break;319case OID_id_ml_dsa_65:320ctx->sinfo->sig->pkey_algo = "mldsa65";321ctx->sinfo->sig->encoding = "raw";322ctx->sinfo->sig->algo_takes_data = true;323break;324case OID_id_ml_dsa_87:325ctx->sinfo->sig->pkey_algo = "mldsa87";326ctx->sinfo->sig->encoding = "raw";327ctx->sinfo->sig->algo_takes_data = true;328break;329default:330printk("Unsupported pkey algo: %u\n", ctx->last_oid);331return -ENOPKG;332}333return 0;334}335336/*337* We only support signed data [RFC2315 sec 9].338*/339int pkcs7_check_content_type(void *context, size_t hdrlen,340unsigned char tag,341const void *value, size_t vlen)342{343struct pkcs7_parse_context *ctx = context;344345if (ctx->last_oid != OID_signed_data) {346pr_warn("Only support pkcs7_signedData type\n");347return -EINVAL;348}349350return 0;351}352353/*354* Note the SignedData version355*/356int pkcs7_note_signeddata_version(void *context, size_t hdrlen,357unsigned char tag,358const void *value, size_t vlen)359{360struct pkcs7_parse_context *ctx = context;361unsigned version;362363if (vlen != 1)364goto unsupported;365366ctx->msg->version = version = *(const u8 *)value;367switch (version) {368case 1:369/* PKCS#7 SignedData [RFC2315 sec 9.1]370* CMS ver 1 SignedData [RFC5652 sec 5.1]371*/372break;373case 3:374/* CMS ver 3 SignedData [RFC2315 sec 5.1] */375break;376default:377goto unsupported;378}379380return 0;381382unsupported:383pr_warn("Unsupported SignedData version\n");384return -EINVAL;385}386387/*388* Note the SignerInfo version389*/390int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,391unsigned char tag,392const void *value, size_t vlen)393{394struct pkcs7_parse_context *ctx = context;395unsigned version;396397if (vlen != 1)398goto unsupported;399400version = *(const u8 *)value;401switch (version) {402case 1:403/* PKCS#7 SignerInfo [RFC2315 sec 9.2]404* CMS ver 1 SignerInfo [RFC5652 sec 5.3]405*/406if (ctx->msg->version != 1)407goto version_mismatch;408ctx->expect_skid = false;409break;410case 3:411/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */412if (ctx->msg->version == 1)413goto version_mismatch;414ctx->expect_skid = true;415break;416default:417goto unsupported;418}419420return 0;421422unsupported:423pr_warn("Unsupported SignerInfo version\n");424return -EINVAL;425version_mismatch:426pr_warn("SignedData-SignerInfo version mismatch\n");427return -EBADMSG;428}429430/*431* Extract a certificate and store it in the context.432*/433int pkcs7_extract_cert(void *context, size_t hdrlen,434unsigned char tag,435const void *value, size_t vlen)436{437struct pkcs7_parse_context *ctx = context;438struct x509_certificate *x509;439440if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {441pr_debug("Cert began with tag %02x at %lu\n",442tag, (unsigned long)ctx - ctx->data);443return -EBADMSG;444}445446/* We have to correct for the header so that the X.509 parser can start447* from the beginning. Note that since X.509 stipulates DER, there448* probably shouldn't be an EOC trailer - but it is in PKCS#7 (which449* stipulates BER).450*/451value -= hdrlen;452vlen += hdrlen;453454if (((u8*)value)[1] == 0x80)455vlen += 2; /* Indefinite length - there should be an EOC */456457x509 = x509_cert_parse(value, vlen);458if (IS_ERR(x509))459return PTR_ERR(x509);460461x509->index = ++ctx->x509_index;462pr_debug("Got cert %u for %s\n", x509->index, x509->subject);463pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);464465*ctx->ppcerts = x509;466ctx->ppcerts = &x509->next;467return 0;468}469470/*471* Save the certificate list472*/473int pkcs7_note_certificate_list(void *context, size_t hdrlen,474unsigned char tag,475const void *value, size_t vlen)476{477struct pkcs7_parse_context *ctx = context;478479pr_devel("Got cert list (%02x)\n", tag);480481*ctx->ppcerts = ctx->msg->certs;482ctx->msg->certs = ctx->certs;483ctx->certs = NULL;484ctx->ppcerts = &ctx->certs;485return 0;486}487488/*489* Note the content type.490*/491int pkcs7_note_content(void *context, size_t hdrlen,492unsigned char tag,493const void *value, size_t vlen)494{495struct pkcs7_parse_context *ctx = context;496497if (ctx->last_oid != OID_data &&498ctx->last_oid != OID_msIndirectData) {499pr_warn("Unsupported data type %d\n", ctx->last_oid);500return -EINVAL;501}502503ctx->msg->data_type = ctx->last_oid;504return 0;505}506507/*508* Extract the data from the message and store that and its content type OID in509* the context.510*/511int pkcs7_note_data(void *context, size_t hdrlen,512unsigned char tag,513const void *value, size_t vlen)514{515struct pkcs7_parse_context *ctx = context;516517pr_debug("Got data\n");518519ctx->msg->data = value;520ctx->msg->data_len = vlen;521ctx->msg->data_hdrlen = hdrlen;522return 0;523}524525/*526* Parse authenticated attributes.527*/528int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,529unsigned char tag,530const void *value, size_t vlen)531{532struct pkcs7_parse_context *ctx = context;533struct pkcs7_signed_info *sinfo = ctx->sinfo;534enum OID content_type;535536pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);537538switch (ctx->last_oid) {539case OID_contentType:540if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))541goto repeated;542content_type = look_up_OID(value, vlen);543if (content_type != ctx->msg->data_type) {544pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",545ctx->msg->data_type, sinfo->index,546content_type);547return -EBADMSG;548}549return 0;550551case OID_signingTime:552if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))553goto repeated;554/* Should we check that the signing time is consistent555* with the signer's X.509 cert?556*/557return x509_decode_time(&sinfo->signing_time,558hdrlen, tag, value, vlen);559560case OID_messageDigest:561if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))562goto repeated;563if (tag != ASN1_OTS)564return -EBADMSG;565sinfo->msgdigest = value;566sinfo->msgdigest_len = vlen;567return 0;568569case OID_smimeCapabilites:570if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))571goto repeated;572if (ctx->msg->data_type != OID_msIndirectData) {573pr_warn("S/MIME Caps only allowed with Authenticode\n");574return -EKEYREJECTED;575}576return 0;577578/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE579* char URLs and cont[1] 8-bit char URLs.580*581* Microsoft StatementType seems to contain a list of OIDs that582* are also used as extendedKeyUsage types in X.509 certs.583*/584case OID_msSpOpusInfo:585if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))586goto repeated;587goto authenticode_check;588case OID_msStatementType:589if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))590goto repeated;591authenticode_check:592if (ctx->msg->data_type != OID_msIndirectData) {593pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");594return -EKEYREJECTED;595}596/* I'm not sure how to validate these */597return 0;598default:599return 0;600}601602repeated:603/* We permit max one item per AuthenticatedAttribute and no repeats */604pr_warn("Repeated/multivalue AuthAttrs not permitted\n");605return -EKEYREJECTED;606}607608/*609* Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]610*/611int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,612unsigned char tag,613const void *value, size_t vlen)614{615struct pkcs7_parse_context *ctx = context;616struct pkcs7_signed_info *sinfo = ctx->sinfo;617618if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||619!test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {620pr_warn("Missing required AuthAttr\n");621return -EBADMSG;622}623624if (ctx->msg->data_type != OID_msIndirectData &&625test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {626pr_warn("Unexpected Authenticode AuthAttr\n");627return -EBADMSG;628}629630/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */631sinfo->authattrs = value - hdrlen;632sinfo->authattrs_len = vlen + hdrlen;633return 0;634}635636/*637* Note the issuing certificate serial number638*/639int pkcs7_sig_note_serial(void *context, size_t hdrlen,640unsigned char tag,641const void *value, size_t vlen)642{643struct pkcs7_parse_context *ctx = context;644ctx->raw_serial = value;645ctx->raw_serial_size = vlen;646return 0;647}648649/*650* Note the issuer's name651*/652int pkcs7_sig_note_issuer(void *context, size_t hdrlen,653unsigned char tag,654const void *value, size_t vlen)655{656struct pkcs7_parse_context *ctx = context;657ctx->raw_issuer = value;658ctx->raw_issuer_size = vlen;659return 0;660}661662/*663* Note the issuing cert's subjectKeyIdentifier664*/665int pkcs7_sig_note_skid(void *context, size_t hdrlen,666unsigned char tag,667const void *value, size_t vlen)668{669struct pkcs7_parse_context *ctx = context;670671pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);672673ctx->raw_skid = value;674ctx->raw_skid_size = vlen;675return 0;676}677678/*679* Note the signature data680*/681int pkcs7_sig_note_signature(void *context, size_t hdrlen,682unsigned char tag,683const void *value, size_t vlen)684{685struct pkcs7_parse_context *ctx = context;686687ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);688if (!ctx->sinfo->sig->s)689return -ENOMEM;690691ctx->sinfo->sig->s_size = vlen;692return 0;693}694695/*696* Note a signature information block697*/698int pkcs7_note_signed_info(void *context, size_t hdrlen,699unsigned char tag,700const void *value, size_t vlen)701{702struct pkcs7_parse_context *ctx = context;703struct pkcs7_signed_info *sinfo = ctx->sinfo;704struct asymmetric_key_id *kid;705706if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {707pr_warn("Authenticode requires AuthAttrs\n");708return -EBADMSG;709}710711/* Generate cert issuer + serial number key ID */712if (!ctx->expect_skid) {713kid = asymmetric_key_generate_id(ctx->raw_serial,714ctx->raw_serial_size,715ctx->raw_issuer,716ctx->raw_issuer_size);717} else {718kid = asymmetric_key_generate_id(ctx->raw_skid,719ctx->raw_skid_size,720"", 0);721}722if (IS_ERR(kid))723return PTR_ERR(kid);724725pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);726727sinfo->sig->auth_ids[0] = kid;728sinfo->index = ++ctx->sinfo_index;729*ctx->ppsinfo = sinfo;730ctx->ppsinfo = &sinfo->next;731ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);732if (!ctx->sinfo)733return -ENOMEM;734ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),735GFP_KERNEL);736if (!ctx->sinfo->sig)737return -ENOMEM;738return 0;739}740741742