Path: blob/master/crypto/asymmetric_keys/pkcs7_parser.c
26278 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;9394if (sinfo->authattrs) {95want = true;96msg->have_authattrs = true;97}9899for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)100if (!!sinfo->authattrs != want)101goto inconsistent;102return 0;103104inconsistent:105pr_warn("Inconsistently supplied authAttrs\n");106return -EINVAL;107}108109/**110* pkcs7_parse_message - Parse a PKCS#7 message111* @data: The raw binary ASN.1 encoded message to be parsed112* @datalen: The size of the encoded message113*/114struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)115{116struct pkcs7_parse_context *ctx;117struct pkcs7_message *msg = ERR_PTR(-ENOMEM);118int ret;119120ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);121if (!ctx)122goto out_no_ctx;123ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);124if (!ctx->msg)125goto out_no_msg;126ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);127if (!ctx->sinfo)128goto out_no_sinfo;129ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),130GFP_KERNEL);131if (!ctx->sinfo->sig)132goto out_no_sig;133134ctx->data = (unsigned long)data;135ctx->ppcerts = &ctx->certs;136ctx->ppsinfo = &ctx->msg->signed_infos;137138/* Attempt to decode the signature */139ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);140if (ret < 0) {141msg = ERR_PTR(ret);142goto out;143}144145ret = pkcs7_check_authattrs(ctx->msg);146if (ret < 0) {147msg = ERR_PTR(ret);148goto out;149}150151msg = ctx->msg;152ctx->msg = NULL;153154out:155while (ctx->certs) {156struct x509_certificate *cert = ctx->certs;157ctx->certs = cert->next;158x509_free_certificate(cert);159}160out_no_sig:161pkcs7_free_signed_info(ctx->sinfo);162out_no_sinfo:163pkcs7_free_message(ctx->msg);164out_no_msg:165kfree(ctx);166out_no_ctx:167return msg;168}169EXPORT_SYMBOL_GPL(pkcs7_parse_message);170171/**172* pkcs7_get_content_data - Get access to the PKCS#7 content173* @pkcs7: The preparsed PKCS#7 message to access174* @_data: Place to return a pointer to the data175* @_data_len: Place to return the data length176* @_headerlen: Size of ASN.1 header not included in _data177*178* Get access to the data content of the PKCS#7 message. The size of the179* header of the ASN.1 object that contains it is also provided and can be used180* to adjust *_data and *_data_len to get the entire object.181*182* Returns -ENODATA if the data object was missing from the message.183*/184int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,185const void **_data, size_t *_data_len,186size_t *_headerlen)187{188if (!pkcs7->data)189return -ENODATA;190191*_data = pkcs7->data;192*_data_len = pkcs7->data_len;193if (_headerlen)194*_headerlen = pkcs7->data_hdrlen;195return 0;196}197EXPORT_SYMBOL_GPL(pkcs7_get_content_data);198199/*200* Note an OID when we find one for later processing when we know how201* to interpret it.202*/203int pkcs7_note_OID(void *context, size_t hdrlen,204unsigned char tag,205const void *value, size_t vlen)206{207struct pkcs7_parse_context *ctx = context;208209ctx->last_oid = look_up_OID(value, vlen);210if (ctx->last_oid == OID__NR) {211char buffer[50];212sprint_oid(value, vlen, buffer, sizeof(buffer));213printk("PKCS7: Unknown OID: [%lu] %s\n",214(unsigned long)value - ctx->data, buffer);215}216return 0;217}218219/*220* Note the digest algorithm for the signature.221*/222int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,223unsigned char tag,224const void *value, size_t vlen)225{226struct pkcs7_parse_context *ctx = context;227228switch (ctx->last_oid) {229case OID_sha1:230ctx->sinfo->sig->hash_algo = "sha1";231break;232case OID_sha256:233ctx->sinfo->sig->hash_algo = "sha256";234break;235case OID_sha384:236ctx->sinfo->sig->hash_algo = "sha384";237break;238case OID_sha512:239ctx->sinfo->sig->hash_algo = "sha512";240break;241case OID_sha224:242ctx->sinfo->sig->hash_algo = "sha224";243break;244case OID_sm3:245ctx->sinfo->sig->hash_algo = "sm3";246break;247case OID_gost2012Digest256:248ctx->sinfo->sig->hash_algo = "streebog256";249break;250case OID_gost2012Digest512:251ctx->sinfo->sig->hash_algo = "streebog512";252break;253case OID_sha3_256:254ctx->sinfo->sig->hash_algo = "sha3-256";255break;256case OID_sha3_384:257ctx->sinfo->sig->hash_algo = "sha3-384";258break;259case OID_sha3_512:260ctx->sinfo->sig->hash_algo = "sha3-512";261break;262default:263printk("Unsupported digest algo: %u\n", ctx->last_oid);264return -ENOPKG;265}266return 0;267}268269/*270* Note the public key algorithm for the signature.271*/272int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,273unsigned char tag,274const void *value, size_t vlen)275{276struct pkcs7_parse_context *ctx = context;277278switch (ctx->last_oid) {279case OID_rsaEncryption:280ctx->sinfo->sig->pkey_algo = "rsa";281ctx->sinfo->sig->encoding = "pkcs1";282break;283case OID_id_ecdsa_with_sha1:284case OID_id_ecdsa_with_sha224:285case OID_id_ecdsa_with_sha256:286case OID_id_ecdsa_with_sha384:287case OID_id_ecdsa_with_sha512:288case OID_id_ecdsa_with_sha3_256:289case OID_id_ecdsa_with_sha3_384:290case OID_id_ecdsa_with_sha3_512:291ctx->sinfo->sig->pkey_algo = "ecdsa";292ctx->sinfo->sig->encoding = "x962";293break;294case OID_gost2012PKey256:295case OID_gost2012PKey512:296ctx->sinfo->sig->pkey_algo = "ecrdsa";297ctx->sinfo->sig->encoding = "raw";298break;299default:300printk("Unsupported pkey algo: %u\n", ctx->last_oid);301return -ENOPKG;302}303return 0;304}305306/*307* We only support signed data [RFC2315 sec 9].308*/309int pkcs7_check_content_type(void *context, size_t hdrlen,310unsigned char tag,311const void *value, size_t vlen)312{313struct pkcs7_parse_context *ctx = context;314315if (ctx->last_oid != OID_signed_data) {316pr_warn("Only support pkcs7_signedData type\n");317return -EINVAL;318}319320return 0;321}322323/*324* Note the SignedData version325*/326int pkcs7_note_signeddata_version(void *context, size_t hdrlen,327unsigned char tag,328const void *value, size_t vlen)329{330struct pkcs7_parse_context *ctx = context;331unsigned version;332333if (vlen != 1)334goto unsupported;335336ctx->msg->version = version = *(const u8 *)value;337switch (version) {338case 1:339/* PKCS#7 SignedData [RFC2315 sec 9.1]340* CMS ver 1 SignedData [RFC5652 sec 5.1]341*/342break;343case 3:344/* CMS ver 3 SignedData [RFC2315 sec 5.1] */345break;346default:347goto unsupported;348}349350return 0;351352unsupported:353pr_warn("Unsupported SignedData version\n");354return -EINVAL;355}356357/*358* Note the SignerInfo version359*/360int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,361unsigned char tag,362const void *value, size_t vlen)363{364struct pkcs7_parse_context *ctx = context;365unsigned version;366367if (vlen != 1)368goto unsupported;369370version = *(const u8 *)value;371switch (version) {372case 1:373/* PKCS#7 SignerInfo [RFC2315 sec 9.2]374* CMS ver 1 SignerInfo [RFC5652 sec 5.3]375*/376if (ctx->msg->version != 1)377goto version_mismatch;378ctx->expect_skid = false;379break;380case 3:381/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */382if (ctx->msg->version == 1)383goto version_mismatch;384ctx->expect_skid = true;385break;386default:387goto unsupported;388}389390return 0;391392unsupported:393pr_warn("Unsupported SignerInfo version\n");394return -EINVAL;395version_mismatch:396pr_warn("SignedData-SignerInfo version mismatch\n");397return -EBADMSG;398}399400/*401* Extract a certificate and store it in the context.402*/403int pkcs7_extract_cert(void *context, size_t hdrlen,404unsigned char tag,405const void *value, size_t vlen)406{407struct pkcs7_parse_context *ctx = context;408struct x509_certificate *x509;409410if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {411pr_debug("Cert began with tag %02x at %lu\n",412tag, (unsigned long)ctx - ctx->data);413return -EBADMSG;414}415416/* We have to correct for the header so that the X.509 parser can start417* from the beginning. Note that since X.509 stipulates DER, there418* probably shouldn't be an EOC trailer - but it is in PKCS#7 (which419* stipulates BER).420*/421value -= hdrlen;422vlen += hdrlen;423424if (((u8*)value)[1] == 0x80)425vlen += 2; /* Indefinite length - there should be an EOC */426427x509 = x509_cert_parse(value, vlen);428if (IS_ERR(x509))429return PTR_ERR(x509);430431x509->index = ++ctx->x509_index;432pr_debug("Got cert %u for %s\n", x509->index, x509->subject);433pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);434435*ctx->ppcerts = x509;436ctx->ppcerts = &x509->next;437return 0;438}439440/*441* Save the certificate list442*/443int pkcs7_note_certificate_list(void *context, size_t hdrlen,444unsigned char tag,445const void *value, size_t vlen)446{447struct pkcs7_parse_context *ctx = context;448449pr_devel("Got cert list (%02x)\n", tag);450451*ctx->ppcerts = ctx->msg->certs;452ctx->msg->certs = ctx->certs;453ctx->certs = NULL;454ctx->ppcerts = &ctx->certs;455return 0;456}457458/*459* Note the content type.460*/461int pkcs7_note_content(void *context, size_t hdrlen,462unsigned char tag,463const void *value, size_t vlen)464{465struct pkcs7_parse_context *ctx = context;466467if (ctx->last_oid != OID_data &&468ctx->last_oid != OID_msIndirectData) {469pr_warn("Unsupported data type %d\n", ctx->last_oid);470return -EINVAL;471}472473ctx->msg->data_type = ctx->last_oid;474return 0;475}476477/*478* Extract the data from the message and store that and its content type OID in479* the context.480*/481int pkcs7_note_data(void *context, size_t hdrlen,482unsigned char tag,483const void *value, size_t vlen)484{485struct pkcs7_parse_context *ctx = context;486487pr_debug("Got data\n");488489ctx->msg->data = value;490ctx->msg->data_len = vlen;491ctx->msg->data_hdrlen = hdrlen;492return 0;493}494495/*496* Parse authenticated attributes.497*/498int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,499unsigned char tag,500const void *value, size_t vlen)501{502struct pkcs7_parse_context *ctx = context;503struct pkcs7_signed_info *sinfo = ctx->sinfo;504enum OID content_type;505506pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);507508switch (ctx->last_oid) {509case OID_contentType:510if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))511goto repeated;512content_type = look_up_OID(value, vlen);513if (content_type != ctx->msg->data_type) {514pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",515ctx->msg->data_type, sinfo->index,516content_type);517return -EBADMSG;518}519return 0;520521case OID_signingTime:522if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))523goto repeated;524/* Should we check that the signing time is consistent525* with the signer's X.509 cert?526*/527return x509_decode_time(&sinfo->signing_time,528hdrlen, tag, value, vlen);529530case OID_messageDigest:531if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))532goto repeated;533if (tag != ASN1_OTS)534return -EBADMSG;535sinfo->msgdigest = value;536sinfo->msgdigest_len = vlen;537return 0;538539case OID_smimeCapabilites:540if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))541goto repeated;542if (ctx->msg->data_type != OID_msIndirectData) {543pr_warn("S/MIME Caps only allowed with Authenticode\n");544return -EKEYREJECTED;545}546return 0;547548/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE549* char URLs and cont[1] 8-bit char URLs.550*551* Microsoft StatementType seems to contain a list of OIDs that552* are also used as extendedKeyUsage types in X.509 certs.553*/554case OID_msSpOpusInfo:555if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))556goto repeated;557goto authenticode_check;558case OID_msStatementType:559if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))560goto repeated;561authenticode_check:562if (ctx->msg->data_type != OID_msIndirectData) {563pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");564return -EKEYREJECTED;565}566/* I'm not sure how to validate these */567return 0;568default:569return 0;570}571572repeated:573/* We permit max one item per AuthenticatedAttribute and no repeats */574pr_warn("Repeated/multivalue AuthAttrs not permitted\n");575return -EKEYREJECTED;576}577578/*579* Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]580*/581int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,582unsigned char tag,583const void *value, size_t vlen)584{585struct pkcs7_parse_context *ctx = context;586struct pkcs7_signed_info *sinfo = ctx->sinfo;587588if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||589!test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {590pr_warn("Missing required AuthAttr\n");591return -EBADMSG;592}593594if (ctx->msg->data_type != OID_msIndirectData &&595test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {596pr_warn("Unexpected Authenticode AuthAttr\n");597return -EBADMSG;598}599600/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */601sinfo->authattrs = value - (hdrlen - 1);602sinfo->authattrs_len = vlen + (hdrlen - 1);603return 0;604}605606/*607* Note the issuing certificate serial number608*/609int pkcs7_sig_note_serial(void *context, size_t hdrlen,610unsigned char tag,611const void *value, size_t vlen)612{613struct pkcs7_parse_context *ctx = context;614ctx->raw_serial = value;615ctx->raw_serial_size = vlen;616return 0;617}618619/*620* Note the issuer's name621*/622int pkcs7_sig_note_issuer(void *context, size_t hdrlen,623unsigned char tag,624const void *value, size_t vlen)625{626struct pkcs7_parse_context *ctx = context;627ctx->raw_issuer = value;628ctx->raw_issuer_size = vlen;629return 0;630}631632/*633* Note the issuing cert's subjectKeyIdentifier634*/635int pkcs7_sig_note_skid(void *context, size_t hdrlen,636unsigned char tag,637const void *value, size_t vlen)638{639struct pkcs7_parse_context *ctx = context;640641pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);642643ctx->raw_skid = value;644ctx->raw_skid_size = vlen;645return 0;646}647648/*649* Note the signature data650*/651int pkcs7_sig_note_signature(void *context, size_t hdrlen,652unsigned char tag,653const void *value, size_t vlen)654{655struct pkcs7_parse_context *ctx = context;656657ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);658if (!ctx->sinfo->sig->s)659return -ENOMEM;660661ctx->sinfo->sig->s_size = vlen;662return 0;663}664665/*666* Note a signature information block667*/668int pkcs7_note_signed_info(void *context, size_t hdrlen,669unsigned char tag,670const void *value, size_t vlen)671{672struct pkcs7_parse_context *ctx = context;673struct pkcs7_signed_info *sinfo = ctx->sinfo;674struct asymmetric_key_id *kid;675676if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {677pr_warn("Authenticode requires AuthAttrs\n");678return -EBADMSG;679}680681/* Generate cert issuer + serial number key ID */682if (!ctx->expect_skid) {683kid = asymmetric_key_generate_id(ctx->raw_serial,684ctx->raw_serial_size,685ctx->raw_issuer,686ctx->raw_issuer_size);687} else {688kid = asymmetric_key_generate_id(ctx->raw_skid,689ctx->raw_skid_size,690"", 0);691}692if (IS_ERR(kid))693return PTR_ERR(kid);694695pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);696697sinfo->sig->auth_ids[0] = kid;698sinfo->index = ++ctx->sinfo_index;699*ctx->ppsinfo = sinfo;700ctx->ppsinfo = &sinfo->next;701ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);702if (!ctx->sinfo)703return -ENOMEM;704ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),705GFP_KERNEL);706if (!ctx->sinfo->sig)707return -ENOMEM;708return 0;709}710711712