1/* PKCS#7 parser 2 * 3 * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public Licence 8 * as published by the Free Software Foundation; either version 9 * 2 of the Licence, or (at your option) any later version. 10 */ 11 12#define pr_fmt(fmt) "PKCS7: "fmt 13#include <linux/kernel.h> 14#include <linux/export.h> 15#include <linux/slab.h> 16#include <linux/err.h> 17#include <linux/oid_registry.h> 18#include "public_key.h" 19#include "pkcs7_parser.h" 20#include "pkcs7-asn1.h" 21 22struct pkcs7_parse_context { 23 struct pkcs7_message *msg; /* Message being constructed */ 24 struct pkcs7_signed_info *sinfo; /* SignedInfo being constructed */ 25 struct pkcs7_signed_info **ppsinfo; 26 struct x509_certificate *certs; /* Certificate cache */ 27 struct x509_certificate **ppcerts; 28 unsigned long data; /* Start of data */ 29 enum OID last_oid; /* Last OID encountered */ 30 unsigned x509_index; 31 unsigned sinfo_index; 32 const void *raw_serial; 33 unsigned raw_serial_size; 34 unsigned raw_issuer_size; 35 const void *raw_issuer; 36}; 37 38/* 39 * Free a signed information block. 40 */ 41static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) 42{ 43 if (sinfo) { 44 mpi_free(sinfo->sig.mpi[0]); 45 kfree(sinfo->sig.digest); 46 kfree(sinfo->signing_cert_id); 47 kfree(sinfo); 48 } 49} 50 51/** 52 * pkcs7_free_message - Free a PKCS#7 message 53 * @pkcs7: The PKCS#7 message to free 54 */ 55void pkcs7_free_message(struct pkcs7_message *pkcs7) 56{ 57 struct x509_certificate *cert; 58 struct pkcs7_signed_info *sinfo; 59 60 if (pkcs7) { 61 while (pkcs7->certs) { 62 cert = pkcs7->certs; 63 pkcs7->certs = cert->next; 64 x509_free_certificate(cert); 65 } 66 while (pkcs7->crl) { 67 cert = pkcs7->crl; 68 pkcs7->crl = cert->next; 69 x509_free_certificate(cert); 70 } 71 while (pkcs7->signed_infos) { 72 sinfo = pkcs7->signed_infos; 73 pkcs7->signed_infos = sinfo->next; 74 pkcs7_free_signed_info(sinfo); 75 } 76 kfree(pkcs7); 77 } 78} 79EXPORT_SYMBOL_GPL(pkcs7_free_message); 80 81/** 82 * pkcs7_parse_message - Parse a PKCS#7 message 83 * @data: The raw binary ASN.1 encoded message to be parsed 84 * @datalen: The size of the encoded message 85 */ 86struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) 87{ 88 struct pkcs7_parse_context *ctx; 89 struct pkcs7_message *msg = ERR_PTR(-ENOMEM); 90 int ret; 91 92 ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); 93 if (!ctx) 94 goto out_no_ctx; 95 ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); 96 if (!ctx->msg) 97 goto out_no_msg; 98 ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); 99 if (!ctx->sinfo) 100 goto out_no_sinfo; 101 102 ctx->data = (unsigned long)data; 103 ctx->ppcerts = &ctx->certs; 104 ctx->ppsinfo = &ctx->msg->signed_infos; 105 106 /* Attempt to decode the signature */ 107 ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); 108 if (ret < 0) { 109 msg = ERR_PTR(ret); 110 goto out; 111 } 112 113 msg = ctx->msg; 114 ctx->msg = NULL; 115 116out: 117 while (ctx->certs) { 118 struct x509_certificate *cert = ctx->certs; 119 ctx->certs = cert->next; 120 x509_free_certificate(cert); 121 } 122 pkcs7_free_signed_info(ctx->sinfo); 123out_no_sinfo: 124 pkcs7_free_message(ctx->msg); 125out_no_msg: 126 kfree(ctx); 127out_no_ctx: 128 return msg; 129} 130EXPORT_SYMBOL_GPL(pkcs7_parse_message); 131 132/** 133 * pkcs7_get_content_data - Get access to the PKCS#7 content 134 * @pkcs7: The preparsed PKCS#7 message to access 135 * @_data: Place to return a pointer to the data 136 * @_data_len: Place to return the data length 137 * @want_wrapper: True if the ASN.1 object header should be included in the data 138 * 139 * Get access to the data content of the PKCS#7 message, including, optionally, 140 * the header of the ASN.1 object that contains it. Returns -ENODATA if the 141 * data object was missing from the message. 142 */ 143int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, 144 const void **_data, size_t *_data_len, 145 bool want_wrapper) 146{ 147 size_t wrapper; 148 149 if (!pkcs7->data) 150 return -ENODATA; 151 152 wrapper = want_wrapper ? pkcs7->data_hdrlen : 0; 153 *_data = pkcs7->data - wrapper; 154 *_data_len = pkcs7->data_len + wrapper; 155 return 0; 156} 157EXPORT_SYMBOL_GPL(pkcs7_get_content_data); 158 159/* 160 * Note an OID when we find one for later processing when we know how 161 * to interpret it. 162 */ 163int pkcs7_note_OID(void *context, size_t hdrlen, 164 unsigned char tag, 165 const void *value, size_t vlen) 166{ 167 struct pkcs7_parse_context *ctx = context; 168 169 ctx->last_oid = look_up_OID(value, vlen); 170 if (ctx->last_oid == OID__NR) { 171 char buffer[50]; 172 sprint_oid(value, vlen, buffer, sizeof(buffer)); 173 printk("PKCS7: Unknown OID: [%lu] %s\n", 174 (unsigned long)value - ctx->data, buffer); 175 } 176 return 0; 177} 178 179/* 180 * Note the digest algorithm for the signature. 181 */ 182int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, 183 unsigned char tag, 184 const void *value, size_t vlen) 185{ 186 struct pkcs7_parse_context *ctx = context; 187 188 switch (ctx->last_oid) { 189 case OID_md4: 190 ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD4; 191 break; 192 case OID_md5: 193 ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_MD5; 194 break; 195 case OID_sha1: 196 ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA1; 197 break; 198 case OID_sha256: 199 ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256; 200 break; 201 default: 202 printk("Unsupported digest algo: %u\n", ctx->last_oid); 203 return -ENOPKG; 204 } 205 return 0; 206} 207 208/* 209 * Note the public key algorithm for the signature. 210 */ 211int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, 212 unsigned char tag, 213 const void *value, size_t vlen) 214{ 215 struct pkcs7_parse_context *ctx = context; 216 217 switch (ctx->last_oid) { 218 case OID_rsaEncryption: 219 ctx->sinfo->sig.pkey_algo = PKEY_ALGO_RSA; 220 break; 221 default: 222 printk("Unsupported pkey algo: %u\n", ctx->last_oid); 223 return -ENOPKG; 224 } 225 return 0; 226} 227 228/* 229 * Extract a certificate and store it in the context. 230 */ 231int pkcs7_extract_cert(void *context, size_t hdrlen, 232 unsigned char tag, 233 const void *value, size_t vlen) 234{ 235 struct pkcs7_parse_context *ctx = context; 236 struct x509_certificate *x509; 237 238 if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) { 239 pr_debug("Cert began with tag %02x at %lu\n", 240 tag, (unsigned long)ctx - ctx->data); 241 return -EBADMSG; 242 } 243 244 /* We have to correct for the header so that the X.509 parser can start 245 * from the beginning. Note that since X.509 stipulates DER, there 246 * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which 247 * stipulates BER). 248 */ 249 value -= hdrlen; 250 vlen += hdrlen; 251 252 if (((u8*)value)[1] == 0x80) 253 vlen += 2; /* Indefinite length - there should be an EOC */ 254 255 x509 = x509_cert_parse(value, vlen); 256 if (IS_ERR(x509)) 257 return PTR_ERR(x509); 258 259 x509->index = ++ctx->x509_index; 260 pr_debug("Got cert %u for %s\n", x509->index, x509->subject); 261 pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data); 262 263 *ctx->ppcerts = x509; 264 ctx->ppcerts = &x509->next; 265 return 0; 266} 267 268/* 269 * Save the certificate list 270 */ 271int pkcs7_note_certificate_list(void *context, size_t hdrlen, 272 unsigned char tag, 273 const void *value, size_t vlen) 274{ 275 struct pkcs7_parse_context *ctx = context; 276 277 pr_devel("Got cert list (%02x)\n", tag); 278 279 *ctx->ppcerts = ctx->msg->certs; 280 ctx->msg->certs = ctx->certs; 281 ctx->certs = NULL; 282 ctx->ppcerts = &ctx->certs; 283 return 0; 284} 285 286/* 287 * Extract the data from the message and store that and its content type OID in 288 * the context. 289 */ 290int pkcs7_note_data(void *context, size_t hdrlen, 291 unsigned char tag, 292 const void *value, size_t vlen) 293{ 294 struct pkcs7_parse_context *ctx = context; 295 296 pr_debug("Got data\n"); 297 298 ctx->msg->data = value; 299 ctx->msg->data_len = vlen; 300 ctx->msg->data_hdrlen = hdrlen; 301 ctx->msg->data_type = ctx->last_oid; 302 return 0; 303} 304 305/* 306 * Parse authenticated attributes 307 */ 308int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen, 309 unsigned char tag, 310 const void *value, size_t vlen) 311{ 312 struct pkcs7_parse_context *ctx = context; 313 314 pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); 315 316 switch (ctx->last_oid) { 317 case OID_messageDigest: 318 if (tag != ASN1_OTS) 319 return -EBADMSG; 320 ctx->sinfo->msgdigest = value; 321 ctx->sinfo->msgdigest_len = vlen; 322 return 0; 323 default: 324 return 0; 325 } 326} 327 328/* 329 * Note the set of auth attributes for digestion purposes [RFC2315 9.3] 330 */ 331int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, 332 unsigned char tag, 333 const void *value, size_t vlen) 334{ 335 struct pkcs7_parse_context *ctx = context; 336 337 /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ 338 ctx->sinfo->authattrs = value - (hdrlen - 1); 339 ctx->sinfo->authattrs_len = vlen + (hdrlen - 1); 340 return 0; 341} 342 343/* 344 * Note the issuing certificate serial number 345 */ 346int pkcs7_sig_note_serial(void *context, size_t hdrlen, 347 unsigned char tag, 348 const void *value, size_t vlen) 349{ 350 struct pkcs7_parse_context *ctx = context; 351 ctx->raw_serial = value; 352 ctx->raw_serial_size = vlen; 353 return 0; 354} 355 356/* 357 * Note the issuer's name 358 */ 359int pkcs7_sig_note_issuer(void *context, size_t hdrlen, 360 unsigned char tag, 361 const void *value, size_t vlen) 362{ 363 struct pkcs7_parse_context *ctx = context; 364 ctx->raw_issuer = value; 365 ctx->raw_issuer_size = vlen; 366 return 0; 367} 368 369/* 370 * Note the signature data 371 */ 372int pkcs7_sig_note_signature(void *context, size_t hdrlen, 373 unsigned char tag, 374 const void *value, size_t vlen) 375{ 376 struct pkcs7_parse_context *ctx = context; 377 MPI mpi; 378 379 BUG_ON(ctx->sinfo->sig.pkey_algo != PKEY_ALGO_RSA); 380 381 mpi = mpi_read_raw_data(value, vlen); 382 if (!mpi) 383 return -ENOMEM; 384 385 ctx->sinfo->sig.mpi[0] = mpi; 386 ctx->sinfo->sig.nr_mpi = 1; 387 return 0; 388} 389 390/* 391 * Note a signature information block 392 */ 393int pkcs7_note_signed_info(void *context, size_t hdrlen, 394 unsigned char tag, 395 const void *value, size_t vlen) 396{ 397 struct pkcs7_parse_context *ctx = context; 398 struct pkcs7_signed_info *sinfo = ctx->sinfo; 399 struct asymmetric_key_id *kid; 400 401 /* Generate cert issuer + serial number key ID */ 402 kid = asymmetric_key_generate_id(ctx->raw_serial, 403 ctx->raw_serial_size, 404 ctx->raw_issuer, 405 ctx->raw_issuer_size); 406 if (IS_ERR(kid)) 407 return PTR_ERR(kid); 408 409 sinfo->signing_cert_id = kid; 410 sinfo->index = ++ctx->sinfo_index; 411 *ctx->ppsinfo = sinfo; 412 ctx->ppsinfo = &sinfo->next; 413 ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); 414 if (!ctx->sinfo) 415 return -ENOMEM; 416 return 0; 417} 418