| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 2 | /* PKCS#7 parser | 
|---|
| 3 | * | 
|---|
| 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: "fmt | 
|---|
| 9 | #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 |  | 
|---|
| 19 | MODULE_DESCRIPTION( "PKCS#7 parser"); | 
|---|
| 20 | MODULE_AUTHOR( "Red Hat, Inc."); | 
|---|
| 21 | MODULE_LICENSE( "GPL"); | 
|---|
| 22 |  | 
|---|
| 23 | struct 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 | */ | 
|---|
| 45 | static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) | 
|---|
| 46 | { | 
|---|
| 47 | if (sinfo) { | 
|---|
| 48 | public_key_signature_free(sig: sinfo->sig); | 
|---|
| 49 | kfree(objp: sinfo); | 
|---|
| 50 | } | 
|---|
| 51 | } | 
|---|
| 52 |  | 
|---|
| 53 | /** | 
|---|
| 54 | * pkcs7_free_message - Free a PKCS#7 message | 
|---|
| 55 | * @pkcs7: The PKCS#7 message to free | 
|---|
| 56 | */ | 
|---|
| 57 | void 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(objp: pkcs7); | 
|---|
| 79 | } | 
|---|
| 80 | } | 
|---|
| 81 | EXPORT_SYMBOL_GPL(pkcs7_free_message); | 
|---|
| 82 |  | 
|---|
| 83 | /* | 
|---|
| 84 | * Check authenticatedAttributes are provided or not provided consistently. | 
|---|
| 85 | */ | 
|---|
| 86 | static 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 |  | 
|---|
| 105 | inconsistent: | 
|---|
| 106 | pr_warn( "Inconsistently supplied authAttrs\n"); | 
|---|
| 107 | return -EINVAL; | 
|---|
| 108 | } | 
|---|
| 109 |  | 
|---|
| 110 | /** | 
|---|
| 111 | * pkcs7_parse_message - Parse a PKCS#7 message | 
|---|
| 112 | * @data: The raw binary ASN.1 encoded message to be parsed | 
|---|
| 113 | * @datalen: The size of the encoded message | 
|---|
| 114 | */ | 
|---|
| 115 | struct 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(error: -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(decoder: &pkcs7_decoder, context: ctx, data, datalen); | 
|---|
| 141 | if (ret < 0) { | 
|---|
| 142 | msg = ERR_PTR(error: ret); | 
|---|
| 143 | goto out; | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | ret = pkcs7_check_authattrs(msg: ctx->msg); | 
|---|
| 147 | if (ret < 0) { | 
|---|
| 148 | msg = ERR_PTR(error: ret); | 
|---|
| 149 | goto out; | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | msg = ctx->msg; | 
|---|
| 153 | ctx->msg = NULL; | 
|---|
| 154 |  | 
|---|
| 155 | out: | 
|---|
| 156 | while (ctx->certs) { | 
|---|
| 157 | struct x509_certificate *cert = ctx->certs; | 
|---|
| 158 | ctx->certs = cert->next; | 
|---|
| 159 | x509_free_certificate(cert); | 
|---|
| 160 | } | 
|---|
| 161 | out_no_sig: | 
|---|
| 162 | pkcs7_free_signed_info(sinfo: ctx->sinfo); | 
|---|
| 163 | out_no_sinfo: | 
|---|
| 164 | pkcs7_free_message(ctx->msg); | 
|---|
| 165 | out_no_msg: | 
|---|
| 166 | kfree(objp: ctx); | 
|---|
| 167 | out_no_ctx: | 
|---|
| 168 | return msg; | 
|---|
| 169 | } | 
|---|
| 170 | EXPORT_SYMBOL_GPL(pkcs7_parse_message); | 
|---|
| 171 |  | 
|---|
| 172 | /** | 
|---|
| 173 | * pkcs7_get_content_data - Get access to the PKCS#7 content | 
|---|
| 174 | * @pkcs7: The preparsed PKCS#7 message to access | 
|---|
| 175 | * @_data: Place to return a pointer to the data | 
|---|
| 176 | * @_data_len: Place to return the data length | 
|---|
| 177 | * @_headerlen: Size of ASN.1 header not included in _data | 
|---|
| 178 | * | 
|---|
| 179 | * Get access to the data content of the PKCS#7 message.  The size of the | 
|---|
| 180 | * header of the ASN.1 object that contains it is also provided and can be used | 
|---|
| 181 | * 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 | */ | 
|---|
| 185 | int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, | 
|---|
| 186 | const void **_data, size_t *_data_len, | 
|---|
| 187 | size_t *) | 
|---|
| 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 | } | 
|---|
| 198 | EXPORT_SYMBOL_GPL(pkcs7_get_content_data); | 
|---|
| 199 |  | 
|---|
| 200 | /* | 
|---|
| 201 | * Note an OID when we find one for later processing when we know how | 
|---|
| 202 | * to interpret it. | 
|---|
| 203 | */ | 
|---|
| 204 | int 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(data: value, datasize: 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 | */ | 
|---|
| 223 | int 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 | */ | 
|---|
| 273 | int 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 | */ | 
|---|
| 310 | int 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 version | 
|---|
| 326 | */ | 
|---|
| 327 | int 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 |  | 
|---|
| 353 | unsupported: | 
|---|
| 354 | pr_warn( "Unsupported SignedData version\n"); | 
|---|
| 355 | return -EINVAL; | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | /* | 
|---|
| 359 | * Note the SignerInfo version | 
|---|
| 360 | */ | 
|---|
| 361 | int 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 |  | 
|---|
| 393 | unsupported: | 
|---|
| 394 | pr_warn( "Unsupported SignerInfo version\n"); | 
|---|
| 395 | return -EINVAL; | 
|---|
| 396 | version_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 | */ | 
|---|
| 404 | int (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 start | 
|---|
| 418 | * from the beginning.  Note that since X.509 stipulates DER, there | 
|---|
| 419 | * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which | 
|---|
| 420 | * 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(data: value, datalen: vlen); | 
|---|
| 429 | if (IS_ERR(ptr: x509)) | 
|---|
| 430 | return PTR_ERR(ptr: 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 list | 
|---|
| 443 | */ | 
|---|
| 444 | int 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 | */ | 
|---|
| 462 | int 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 in | 
|---|
| 480 | * the context. | 
|---|
| 481 | */ | 
|---|
| 482 | int 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 | */ | 
|---|
| 499 | int 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(data: value, datasize: 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 consistent | 
|---|
| 526 | * with the signer's X.509 cert? | 
|---|
| 527 | */ | 
|---|
| 528 | return x509_decode_time(t: &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 BE | 
|---|
| 550 | * char URLs and cont[1] 8-bit char URLs. | 
|---|
| 551 | * | 
|---|
| 552 | * Microsoft StatementType seems to contain a list of OIDs that | 
|---|
| 553 | * 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 |  | 
|---|
| 573 | repeated: | 
|---|
| 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 | */ | 
|---|
| 582 | int 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 number | 
|---|
| 609 | */ | 
|---|
| 610 | int 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 name | 
|---|
| 622 | */ | 
|---|
| 623 | int 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 subjectKeyIdentifier | 
|---|
| 635 | */ | 
|---|
| 636 | int 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 data | 
|---|
| 651 | */ | 
|---|
| 652 | int 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 block | 
|---|
| 668 | */ | 
|---|
| 669 | int 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(val_1: ctx->raw_serial, | 
|---|
| 685 | len_1: ctx->raw_serial_size, | 
|---|
| 686 | val_2: ctx->raw_issuer, | 
|---|
| 687 | len_2: ctx->raw_issuer_size); | 
|---|
| 688 | } else { | 
|---|
| 689 | kid = asymmetric_key_generate_id(val_1: ctx->raw_skid, | 
|---|
| 690 | len_1: ctx->raw_skid_size, | 
|---|
| 691 | val_2: "", len_2: 0); | 
|---|
| 692 | } | 
|---|
| 693 | if (IS_ERR(ptr: kid)) | 
|---|
| 694 | return PTR_ERR(ptr: 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 |  | 
|---|