1c26fd69fSDavid Howells /* X.509 certificate parser 2c26fd69fSDavid Howells * 3c26fd69fSDavid Howells * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. 4c26fd69fSDavid Howells * Written by David Howells (dhowells@redhat.com) 5c26fd69fSDavid Howells * 6c26fd69fSDavid Howells * This program is free software; you can redistribute it and/or 7c26fd69fSDavid Howells * modify it under the terms of the GNU General Public Licence 8c26fd69fSDavid Howells * as published by the Free Software Foundation; either version 9c26fd69fSDavid Howells * 2 of the Licence, or (at your option) any later version. 10c26fd69fSDavid Howells */ 11c26fd69fSDavid Howells 12c26fd69fSDavid Howells #define pr_fmt(fmt) "X.509: "fmt 13c26fd69fSDavid Howells #include <linux/kernel.h> 14ace0107aSDavid Howells #include <linux/export.h> 15c26fd69fSDavid Howells #include <linux/slab.h> 16c26fd69fSDavid Howells #include <linux/err.h> 17c26fd69fSDavid Howells #include <linux/oid_registry.h> 18c26fd69fSDavid Howells #include "public_key.h" 19c26fd69fSDavid Howells #include "x509_parser.h" 20c26fd69fSDavid Howells #include "x509-asn1.h" 21c26fd69fSDavid Howells #include "x509_rsakey-asn1.h" 22c26fd69fSDavid Howells 23c26fd69fSDavid Howells struct x509_parse_context { 24c26fd69fSDavid Howells struct x509_certificate *cert; /* Certificate being constructed */ 25c26fd69fSDavid Howells unsigned long data; /* Start of data */ 26c26fd69fSDavid Howells const void *cert_start; /* Start of cert content */ 27c26fd69fSDavid Howells const void *key; /* Key data */ 28c26fd69fSDavid Howells size_t key_size; /* Size of key data */ 29c26fd69fSDavid Howells enum OID last_oid; /* Last OID encountered */ 30c26fd69fSDavid Howells enum OID algo_oid; /* Algorithm OID */ 31c26fd69fSDavid Howells unsigned char nr_mpi; /* Number of MPIs stored */ 32c26fd69fSDavid Howells u8 o_size; /* Size of organizationName (O) */ 33c26fd69fSDavid Howells u8 cn_size; /* Size of commonName (CN) */ 34c26fd69fSDavid Howells u8 email_size; /* Size of emailAddress */ 35c26fd69fSDavid Howells u16 o_offset; /* Offset of organizationName (O) */ 36c26fd69fSDavid Howells u16 cn_offset; /* Offset of commonName (CN) */ 37c26fd69fSDavid Howells u16 email_offset; /* Offset of emailAddress */ 38c26fd69fSDavid Howells }; 39c26fd69fSDavid Howells 40c26fd69fSDavid Howells /* 41c26fd69fSDavid Howells * Free an X.509 certificate 42c26fd69fSDavid Howells */ 43c26fd69fSDavid Howells void x509_free_certificate(struct x509_certificate *cert) 44c26fd69fSDavid Howells { 45c26fd69fSDavid Howells if (cert) { 46c26fd69fSDavid Howells public_key_destroy(cert->pub); 47c26fd69fSDavid Howells kfree(cert->issuer); 48c26fd69fSDavid Howells kfree(cert->subject); 4946963b77SDavid Howells kfree(cert->id); 5046963b77SDavid Howells kfree(cert->skid); 51c26fd69fSDavid Howells kfree(cert->authority); 52b426beb6SDavid Howells kfree(cert->sig.digest); 53b426beb6SDavid Howells mpi_free(cert->sig.rsa.s); 54c26fd69fSDavid Howells kfree(cert); 55c26fd69fSDavid Howells } 56c26fd69fSDavid Howells } 57ace0107aSDavid Howells EXPORT_SYMBOL_GPL(x509_free_certificate); 58c26fd69fSDavid Howells 59c26fd69fSDavid Howells /* 60c26fd69fSDavid Howells * Parse an X.509 certificate 61c26fd69fSDavid Howells */ 62c26fd69fSDavid Howells struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) 63c26fd69fSDavid Howells { 64c26fd69fSDavid Howells struct x509_certificate *cert; 65c26fd69fSDavid Howells struct x509_parse_context *ctx; 6646963b77SDavid Howells struct asymmetric_key_id *kid; 67c26fd69fSDavid Howells long ret; 68c26fd69fSDavid Howells 69c26fd69fSDavid Howells ret = -ENOMEM; 70c26fd69fSDavid Howells cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL); 71c26fd69fSDavid Howells if (!cert) 72c26fd69fSDavid Howells goto error_no_cert; 73c26fd69fSDavid Howells cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); 74c26fd69fSDavid Howells if (!cert->pub) 75c26fd69fSDavid Howells goto error_no_ctx; 76c26fd69fSDavid Howells ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); 77c26fd69fSDavid Howells if (!ctx) 78c26fd69fSDavid Howells goto error_no_ctx; 79c26fd69fSDavid Howells 80c26fd69fSDavid Howells ctx->cert = cert; 81c26fd69fSDavid Howells ctx->data = (unsigned long)data; 82c26fd69fSDavid Howells 83c26fd69fSDavid Howells /* Attempt to decode the certificate */ 84c26fd69fSDavid Howells ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen); 85c26fd69fSDavid Howells if (ret < 0) 86c26fd69fSDavid Howells goto error_decode; 87c26fd69fSDavid Howells 88c26fd69fSDavid Howells /* Decode the public key */ 89c26fd69fSDavid Howells ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx, 90c26fd69fSDavid Howells ctx->key, ctx->key_size); 91c26fd69fSDavid Howells if (ret < 0) 92c26fd69fSDavid Howells goto error_decode; 93c26fd69fSDavid Howells 9446963b77SDavid Howells /* Generate cert issuer + serial number key ID */ 9546963b77SDavid Howells kid = asymmetric_key_generate_id(cert->raw_serial, 9646963b77SDavid Howells cert->raw_serial_size, 9746963b77SDavid Howells cert->raw_issuer, 9846963b77SDavid Howells cert->raw_issuer_size); 9946963b77SDavid Howells if (IS_ERR(kid)) { 10046963b77SDavid Howells ret = PTR_ERR(kid); 10146963b77SDavid Howells goto error_decode; 10246963b77SDavid Howells } 10346963b77SDavid Howells cert->id = kid; 10446963b77SDavid Howells 105c26fd69fSDavid Howells kfree(ctx); 106c26fd69fSDavid Howells return cert; 107c26fd69fSDavid Howells 108c26fd69fSDavid Howells error_decode: 109c26fd69fSDavid Howells kfree(ctx); 110c26fd69fSDavid Howells error_no_ctx: 111c26fd69fSDavid Howells x509_free_certificate(cert); 112c26fd69fSDavid Howells error_no_cert: 113c26fd69fSDavid Howells return ERR_PTR(ret); 114c26fd69fSDavid Howells } 115ace0107aSDavid Howells EXPORT_SYMBOL_GPL(x509_cert_parse); 116c26fd69fSDavid Howells 117c26fd69fSDavid Howells /* 118c26fd69fSDavid Howells * Note an OID when we find one for later processing when we know how 119c26fd69fSDavid Howells * to interpret it. 120c26fd69fSDavid Howells */ 121c26fd69fSDavid Howells int x509_note_OID(void *context, size_t hdrlen, 122c26fd69fSDavid Howells unsigned char tag, 123c26fd69fSDavid Howells const void *value, size_t vlen) 124c26fd69fSDavid Howells { 125c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 126c26fd69fSDavid Howells 127c26fd69fSDavid Howells ctx->last_oid = look_up_OID(value, vlen); 128c26fd69fSDavid Howells if (ctx->last_oid == OID__NR) { 129c26fd69fSDavid Howells char buffer[50]; 130c26fd69fSDavid Howells sprint_oid(value, vlen, buffer, sizeof(buffer)); 131cf75446eSRandy Dunlap pr_debug("Unknown OID: [%lu] %s\n", 132c26fd69fSDavid Howells (unsigned long)value - ctx->data, buffer); 133c26fd69fSDavid Howells } 134c26fd69fSDavid Howells return 0; 135c26fd69fSDavid Howells } 136c26fd69fSDavid Howells 137c26fd69fSDavid Howells /* 138c26fd69fSDavid Howells * Save the position of the TBS data so that we can check the signature over it 139c26fd69fSDavid Howells * later. 140c26fd69fSDavid Howells */ 141c26fd69fSDavid Howells int x509_note_tbs_certificate(void *context, size_t hdrlen, 142c26fd69fSDavid Howells unsigned char tag, 143c26fd69fSDavid Howells const void *value, size_t vlen) 144c26fd69fSDavid Howells { 145c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 146c26fd69fSDavid Howells 147c26fd69fSDavid Howells pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n", 148c26fd69fSDavid Howells hdrlen, tag, (unsigned long)value - ctx->data, vlen); 149c26fd69fSDavid Howells 150c26fd69fSDavid Howells ctx->cert->tbs = value - hdrlen; 151c26fd69fSDavid Howells ctx->cert->tbs_size = vlen + hdrlen; 152c26fd69fSDavid Howells return 0; 153c26fd69fSDavid Howells } 154c26fd69fSDavid Howells 155c26fd69fSDavid Howells /* 156c26fd69fSDavid Howells * Record the public key algorithm 157c26fd69fSDavid Howells */ 158c26fd69fSDavid Howells int x509_note_pkey_algo(void *context, size_t hdrlen, 159c26fd69fSDavid Howells unsigned char tag, 160c26fd69fSDavid Howells const void *value, size_t vlen) 161c26fd69fSDavid Howells { 162c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 163c26fd69fSDavid Howells 164c26fd69fSDavid Howells pr_debug("PubKey Algo: %u\n", ctx->last_oid); 165c26fd69fSDavid Howells 166c26fd69fSDavid Howells switch (ctx->last_oid) { 167c26fd69fSDavid Howells case OID_md2WithRSAEncryption: 168c26fd69fSDavid Howells case OID_md3WithRSAEncryption: 169c26fd69fSDavid Howells default: 170c26fd69fSDavid Howells return -ENOPKG; /* Unsupported combination */ 171c26fd69fSDavid Howells 172c26fd69fSDavid Howells case OID_md4WithRSAEncryption: 1733fe78ca2SDmitry Kasatkin ctx->cert->sig.pkey_hash_algo = HASH_ALGO_MD5; 174b426beb6SDavid Howells ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; 175c26fd69fSDavid Howells break; 176c26fd69fSDavid Howells 177c26fd69fSDavid Howells case OID_sha1WithRSAEncryption: 1783fe78ca2SDmitry Kasatkin ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA1; 179b426beb6SDavid Howells ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; 180c26fd69fSDavid Howells break; 181c26fd69fSDavid Howells 182c26fd69fSDavid Howells case OID_sha256WithRSAEncryption: 1833fe78ca2SDmitry Kasatkin ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA256; 184b426beb6SDavid Howells ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; 185c26fd69fSDavid Howells break; 186c26fd69fSDavid Howells 187c26fd69fSDavid Howells case OID_sha384WithRSAEncryption: 1883fe78ca2SDmitry Kasatkin ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA384; 189b426beb6SDavid Howells ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; 190c26fd69fSDavid Howells break; 191c26fd69fSDavid Howells 192c26fd69fSDavid Howells case OID_sha512WithRSAEncryption: 1933fe78ca2SDmitry Kasatkin ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA512; 194b426beb6SDavid Howells ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; 195c26fd69fSDavid Howells break; 196c26fd69fSDavid Howells 197c26fd69fSDavid Howells case OID_sha224WithRSAEncryption: 1983fe78ca2SDmitry Kasatkin ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA224; 199b426beb6SDavid Howells ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; 200c26fd69fSDavid Howells break; 201c26fd69fSDavid Howells } 202c26fd69fSDavid Howells 203c26fd69fSDavid Howells ctx->algo_oid = ctx->last_oid; 204c26fd69fSDavid Howells return 0; 205c26fd69fSDavid Howells } 206c26fd69fSDavid Howells 207c26fd69fSDavid Howells /* 208c26fd69fSDavid Howells * Note the whereabouts and type of the signature. 209c26fd69fSDavid Howells */ 210c26fd69fSDavid Howells int x509_note_signature(void *context, size_t hdrlen, 211c26fd69fSDavid Howells unsigned char tag, 212c26fd69fSDavid Howells const void *value, size_t vlen) 213c26fd69fSDavid Howells { 214c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 215c26fd69fSDavid Howells 216c26fd69fSDavid Howells pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen); 217c26fd69fSDavid Howells 218c26fd69fSDavid Howells if (ctx->last_oid != ctx->algo_oid) { 219c26fd69fSDavid Howells pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n", 220c26fd69fSDavid Howells ctx->algo_oid, ctx->last_oid); 221c26fd69fSDavid Howells return -EINVAL; 222c26fd69fSDavid Howells } 223c26fd69fSDavid Howells 224b426beb6SDavid Howells ctx->cert->raw_sig = value; 225b426beb6SDavid Howells ctx->cert->raw_sig_size = vlen; 226c26fd69fSDavid Howells return 0; 227c26fd69fSDavid Howells } 228c26fd69fSDavid Howells 229c26fd69fSDavid Howells /* 23084aabd46SDavid Howells * Note the certificate serial number 23184aabd46SDavid Howells */ 23284aabd46SDavid Howells int x509_note_serial(void *context, size_t hdrlen, 23384aabd46SDavid Howells unsigned char tag, 23484aabd46SDavid Howells const void *value, size_t vlen) 23584aabd46SDavid Howells { 23684aabd46SDavid Howells struct x509_parse_context *ctx = context; 23784aabd46SDavid Howells ctx->cert->raw_serial = value; 23884aabd46SDavid Howells ctx->cert->raw_serial_size = vlen; 23984aabd46SDavid Howells return 0; 24084aabd46SDavid Howells } 24184aabd46SDavid Howells 24284aabd46SDavid Howells /* 243c26fd69fSDavid Howells * Note some of the name segments from which we'll fabricate a name. 244c26fd69fSDavid Howells */ 245c26fd69fSDavid Howells int x509_extract_name_segment(void *context, size_t hdrlen, 246c26fd69fSDavid Howells unsigned char tag, 247c26fd69fSDavid Howells const void *value, size_t vlen) 248c26fd69fSDavid Howells { 249c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 250c26fd69fSDavid Howells 251c26fd69fSDavid Howells switch (ctx->last_oid) { 252c26fd69fSDavid Howells case OID_commonName: 253c26fd69fSDavid Howells ctx->cn_size = vlen; 254c26fd69fSDavid Howells ctx->cn_offset = (unsigned long)value - ctx->data; 255c26fd69fSDavid Howells break; 256c26fd69fSDavid Howells case OID_organizationName: 257c26fd69fSDavid Howells ctx->o_size = vlen; 258c26fd69fSDavid Howells ctx->o_offset = (unsigned long)value - ctx->data; 259c26fd69fSDavid Howells break; 260c26fd69fSDavid Howells case OID_email_address: 261c26fd69fSDavid Howells ctx->email_size = vlen; 262c26fd69fSDavid Howells ctx->email_offset = (unsigned long)value - ctx->data; 263c26fd69fSDavid Howells break; 264c26fd69fSDavid Howells default: 265c26fd69fSDavid Howells break; 266c26fd69fSDavid Howells } 267c26fd69fSDavid Howells 268c26fd69fSDavid Howells return 0; 269c26fd69fSDavid Howells } 270c26fd69fSDavid Howells 271c26fd69fSDavid Howells /* 272c26fd69fSDavid Howells * Fabricate and save the issuer and subject names 273c26fd69fSDavid Howells */ 274c26fd69fSDavid Howells static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen, 275c26fd69fSDavid Howells unsigned char tag, 276c26fd69fSDavid Howells char **_name, size_t vlen) 277c26fd69fSDavid Howells { 278c26fd69fSDavid Howells const void *name, *data = (const void *)ctx->data; 279c26fd69fSDavid Howells size_t namesize; 280c26fd69fSDavid Howells char *buffer; 281c26fd69fSDavid Howells 282c26fd69fSDavid Howells if (*_name) 283c26fd69fSDavid Howells return -EINVAL; 284c26fd69fSDavid Howells 285c26fd69fSDavid Howells /* Empty name string if no material */ 286c26fd69fSDavid Howells if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) { 287c26fd69fSDavid Howells buffer = kmalloc(1, GFP_KERNEL); 288c26fd69fSDavid Howells if (!buffer) 289c26fd69fSDavid Howells return -ENOMEM; 290c26fd69fSDavid Howells buffer[0] = 0; 291c26fd69fSDavid Howells goto done; 292c26fd69fSDavid Howells } 293c26fd69fSDavid Howells 294c26fd69fSDavid Howells if (ctx->cn_size && ctx->o_size) { 295c26fd69fSDavid Howells /* Consider combining O and CN, but use only the CN if it is 296c26fd69fSDavid Howells * prefixed by the O, or a significant portion thereof. 297c26fd69fSDavid Howells */ 298c26fd69fSDavid Howells namesize = ctx->cn_size; 299c26fd69fSDavid Howells name = data + ctx->cn_offset; 300c26fd69fSDavid Howells if (ctx->cn_size >= ctx->o_size && 301c26fd69fSDavid Howells memcmp(data + ctx->cn_offset, data + ctx->o_offset, 302c26fd69fSDavid Howells ctx->o_size) == 0) 303c26fd69fSDavid Howells goto single_component; 304c26fd69fSDavid Howells if (ctx->cn_size >= 7 && 305c26fd69fSDavid Howells ctx->o_size >= 7 && 306c26fd69fSDavid Howells memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0) 307c26fd69fSDavid Howells goto single_component; 308c26fd69fSDavid Howells 309c26fd69fSDavid Howells buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1, 310c26fd69fSDavid Howells GFP_KERNEL); 311c26fd69fSDavid Howells if (!buffer) 312c26fd69fSDavid Howells return -ENOMEM; 313c26fd69fSDavid Howells 314c26fd69fSDavid Howells memcpy(buffer, 315c26fd69fSDavid Howells data + ctx->o_offset, ctx->o_size); 316c26fd69fSDavid Howells buffer[ctx->o_size + 0] = ':'; 317c26fd69fSDavid Howells buffer[ctx->o_size + 1] = ' '; 318c26fd69fSDavid Howells memcpy(buffer + ctx->o_size + 2, 319c26fd69fSDavid Howells data + ctx->cn_offset, ctx->cn_size); 320c26fd69fSDavid Howells buffer[ctx->o_size + 2 + ctx->cn_size] = 0; 321c26fd69fSDavid Howells goto done; 322c26fd69fSDavid Howells 323c26fd69fSDavid Howells } else if (ctx->cn_size) { 324c26fd69fSDavid Howells namesize = ctx->cn_size; 325c26fd69fSDavid Howells name = data + ctx->cn_offset; 326c26fd69fSDavid Howells } else if (ctx->o_size) { 327c26fd69fSDavid Howells namesize = ctx->o_size; 328c26fd69fSDavid Howells name = data + ctx->o_offset; 329c26fd69fSDavid Howells } else { 330c26fd69fSDavid Howells namesize = ctx->email_size; 331c26fd69fSDavid Howells name = data + ctx->email_offset; 332c26fd69fSDavid Howells } 333c26fd69fSDavid Howells 334c26fd69fSDavid Howells single_component: 335c26fd69fSDavid Howells buffer = kmalloc(namesize + 1, GFP_KERNEL); 336c26fd69fSDavid Howells if (!buffer) 337c26fd69fSDavid Howells return -ENOMEM; 338c26fd69fSDavid Howells memcpy(buffer, name, namesize); 339c26fd69fSDavid Howells buffer[namesize] = 0; 340c26fd69fSDavid Howells 341c26fd69fSDavid Howells done: 342c26fd69fSDavid Howells *_name = buffer; 343c26fd69fSDavid Howells ctx->cn_size = 0; 344c26fd69fSDavid Howells ctx->o_size = 0; 345c26fd69fSDavid Howells ctx->email_size = 0; 346c26fd69fSDavid Howells return 0; 347c26fd69fSDavid Howells } 348c26fd69fSDavid Howells 349c26fd69fSDavid Howells int x509_note_issuer(void *context, size_t hdrlen, 350c26fd69fSDavid Howells unsigned char tag, 351c26fd69fSDavid Howells const void *value, size_t vlen) 352c26fd69fSDavid Howells { 353c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 35484aabd46SDavid Howells ctx->cert->raw_issuer = value; 35584aabd46SDavid Howells ctx->cert->raw_issuer_size = vlen; 356c26fd69fSDavid Howells return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); 357c26fd69fSDavid Howells } 358c26fd69fSDavid Howells 359c26fd69fSDavid Howells int x509_note_subject(void *context, size_t hdrlen, 360c26fd69fSDavid Howells unsigned char tag, 361c26fd69fSDavid Howells const void *value, size_t vlen) 362c26fd69fSDavid Howells { 363c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 36484aabd46SDavid Howells ctx->cert->raw_subject = value; 36584aabd46SDavid Howells ctx->cert->raw_subject_size = vlen; 366c26fd69fSDavid Howells return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); 367c26fd69fSDavid Howells } 368c26fd69fSDavid Howells 369c26fd69fSDavid Howells /* 370c26fd69fSDavid Howells * Extract the data for the public key algorithm 371c26fd69fSDavid Howells */ 372c26fd69fSDavid Howells int x509_extract_key_data(void *context, size_t hdrlen, 373c26fd69fSDavid Howells unsigned char tag, 374c26fd69fSDavid Howells const void *value, size_t vlen) 375c26fd69fSDavid Howells { 376c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 377c26fd69fSDavid Howells 378c26fd69fSDavid Howells if (ctx->last_oid != OID_rsaEncryption) 379c26fd69fSDavid Howells return -ENOPKG; 380c26fd69fSDavid Howells 38167f7d60bSDavid Howells ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA; 38267f7d60bSDavid Howells 38367f7d60bSDavid Howells /* Discard the BIT STRING metadata */ 384c26fd69fSDavid Howells ctx->key = value + 1; 385c26fd69fSDavid Howells ctx->key_size = vlen - 1; 386c26fd69fSDavid Howells return 0; 387c26fd69fSDavid Howells } 388c26fd69fSDavid Howells 389c26fd69fSDavid Howells /* 390c26fd69fSDavid Howells * Extract a RSA public key value 391c26fd69fSDavid Howells */ 392c26fd69fSDavid Howells int rsa_extract_mpi(void *context, size_t hdrlen, 393c26fd69fSDavid Howells unsigned char tag, 394c26fd69fSDavid Howells const void *value, size_t vlen) 395c26fd69fSDavid Howells { 396c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 397c26fd69fSDavid Howells MPI mpi; 398c26fd69fSDavid Howells 399c26fd69fSDavid Howells if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) { 400c26fd69fSDavid Howells pr_err("Too many public key MPIs in certificate\n"); 401c26fd69fSDavid Howells return -EBADMSG; 402c26fd69fSDavid Howells } 403c26fd69fSDavid Howells 404c26fd69fSDavid Howells mpi = mpi_read_raw_data(value, vlen); 405c26fd69fSDavid Howells if (!mpi) 406c26fd69fSDavid Howells return -ENOMEM; 407c26fd69fSDavid Howells 408c26fd69fSDavid Howells ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi; 409c26fd69fSDavid Howells return 0; 410c26fd69fSDavid Howells } 411c26fd69fSDavid Howells 41204b00bdbSChun-Yi Lee /* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */ 41304b00bdbSChun-Yi Lee #define SEQ_TAG_KEYID (ASN1_CONT << 6) 41404b00bdbSChun-Yi Lee 415c26fd69fSDavid Howells /* 416c26fd69fSDavid Howells * Process certificate extensions that are used to qualify the certificate. 417c26fd69fSDavid Howells */ 418c26fd69fSDavid Howells int x509_process_extension(void *context, size_t hdrlen, 419c26fd69fSDavid Howells unsigned char tag, 420c26fd69fSDavid Howells const void *value, size_t vlen) 421c26fd69fSDavid Howells { 422c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 42346963b77SDavid Howells struct asymmetric_key_id *kid; 424c26fd69fSDavid Howells const unsigned char *v = value; 425c26fd69fSDavid Howells int i; 426c26fd69fSDavid Howells 427c26fd69fSDavid Howells pr_debug("Extension: %u\n", ctx->last_oid); 428c26fd69fSDavid Howells 429c26fd69fSDavid Howells if (ctx->last_oid == OID_subjectKeyIdentifier) { 430c26fd69fSDavid Howells /* Get hold of the key fingerprint */ 43146963b77SDavid Howells if (ctx->cert->skid || vlen < 3) 432c26fd69fSDavid Howells return -EBADMSG; 433c26fd69fSDavid Howells if (v[0] != ASN1_OTS || v[1] != vlen - 2) 434c26fd69fSDavid Howells return -EBADMSG; 435c26fd69fSDavid Howells v += 2; 436c26fd69fSDavid Howells vlen -= 2; 437c26fd69fSDavid Howells 43846963b77SDavid Howells kid = asymmetric_key_generate_id(v, vlen, 43946963b77SDavid Howells ctx->cert->raw_subject, 44046963b77SDavid Howells ctx->cert->raw_subject_size); 44146963b77SDavid Howells if (IS_ERR(kid)) 44246963b77SDavid Howells return PTR_ERR(kid); 44346963b77SDavid Howells ctx->cert->skid = kid; 44446963b77SDavid Howells pr_debug("subjkeyid %*phN\n", kid->len, kid->data); 445c26fd69fSDavid Howells return 0; 446c26fd69fSDavid Howells } 447c26fd69fSDavid Howells 448c26fd69fSDavid Howells if (ctx->last_oid == OID_authorityKeyIdentifier) { 449c26fd69fSDavid Howells /* Get hold of the CA key fingerprint */ 45046963b77SDavid Howells if (ctx->cert->authority || vlen < 5) 451c26fd69fSDavid Howells return -EBADMSG; 452c26fd69fSDavid Howells 45304b00bdbSChun-Yi Lee /* Authority Key Identifier must be a Constructed SEQUENCE */ 45404b00bdbSChun-Yi Lee if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5))) 45504b00bdbSChun-Yi Lee return -EBADMSG; 45604b00bdbSChun-Yi Lee 45704b00bdbSChun-Yi Lee /* Authority Key Identifier is not indefinite length */ 45804b00bdbSChun-Yi Lee if (unlikely(vlen == ASN1_INDEFINITE_LENGTH)) 45904b00bdbSChun-Yi Lee return -EBADMSG; 46004b00bdbSChun-Yi Lee 46104b00bdbSChun-Yi Lee if (vlen < ASN1_INDEFINITE_LENGTH) { 46204b00bdbSChun-Yi Lee /* Short Form length */ 46304b00bdbSChun-Yi Lee if (v[1] != vlen - 2 || 46404b00bdbSChun-Yi Lee v[2] != SEQ_TAG_KEYID || 46504b00bdbSChun-Yi Lee v[3] > vlen - 4) 46604b00bdbSChun-Yi Lee return -EBADMSG; 46704b00bdbSChun-Yi Lee 46846963b77SDavid Howells vlen = v[3]; 46904b00bdbSChun-Yi Lee v += 4; 47004b00bdbSChun-Yi Lee } else { 47104b00bdbSChun-Yi Lee /* Long Form length */ 47204b00bdbSChun-Yi Lee size_t seq_len = 0; 47304b00bdbSChun-Yi Lee size_t sub = v[1] - ASN1_INDEFINITE_LENGTH; 47404b00bdbSChun-Yi Lee 47504b00bdbSChun-Yi Lee if (sub > 2) 47604b00bdbSChun-Yi Lee return -EBADMSG; 47704b00bdbSChun-Yi Lee 47804b00bdbSChun-Yi Lee /* calculate the length from subsequent octets */ 47904b00bdbSChun-Yi Lee v += 2; 48004b00bdbSChun-Yi Lee for (i = 0; i < sub; i++) { 48104b00bdbSChun-Yi Lee seq_len <<= 8; 48204b00bdbSChun-Yi Lee seq_len |= v[i]; 48304b00bdbSChun-Yi Lee } 48404b00bdbSChun-Yi Lee 48504b00bdbSChun-Yi Lee if (seq_len != vlen - 2 - sub || 48604b00bdbSChun-Yi Lee v[sub] != SEQ_TAG_KEYID || 48704b00bdbSChun-Yi Lee v[sub + 1] > vlen - 4 - sub) 48804b00bdbSChun-Yi Lee return -EBADMSG; 48904b00bdbSChun-Yi Lee 49046963b77SDavid Howells vlen = v[sub + 1]; 49104b00bdbSChun-Yi Lee v += (sub + 2); 49204b00bdbSChun-Yi Lee } 49304b00bdbSChun-Yi Lee 49446963b77SDavid Howells kid = asymmetric_key_generate_id(v, vlen, 49546963b77SDavid Howells ctx->cert->raw_issuer, 49646963b77SDavid Howells ctx->cert->raw_issuer_size); 49746963b77SDavid Howells if (IS_ERR(kid)) 49846963b77SDavid Howells return PTR_ERR(kid); 49946963b77SDavid Howells pr_debug("authkeyid %*phN\n", kid->len, kid->data); 50046963b77SDavid Howells ctx->cert->authority = kid; 501c26fd69fSDavid Howells return 0; 502c26fd69fSDavid Howells } 503c26fd69fSDavid Howells 504c26fd69fSDavid Howells return 0; 505c26fd69fSDavid Howells } 506c26fd69fSDavid Howells 507c26fd69fSDavid Howells /* 508c26fd69fSDavid Howells * Record a certificate time. 509c26fd69fSDavid Howells */ 510a5752d11SDavid Howells static int x509_note_time(struct tm *tm, size_t hdrlen, 511c26fd69fSDavid Howells unsigned char tag, 512c26fd69fSDavid Howells const unsigned char *value, size_t vlen) 513c26fd69fSDavid Howells { 514c26fd69fSDavid Howells const unsigned char *p = value; 515c26fd69fSDavid Howells 516c26fd69fSDavid Howells #define dec2bin(X) ((X) - '0') 517c26fd69fSDavid Howells #define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; }) 518c26fd69fSDavid Howells 519c26fd69fSDavid Howells if (tag == ASN1_UNITIM) { 520c26fd69fSDavid Howells /* UTCTime: YYMMDDHHMMSSZ */ 521c26fd69fSDavid Howells if (vlen != 13) 522c26fd69fSDavid Howells goto unsupported_time; 523a5752d11SDavid Howells tm->tm_year = DD2bin(p); 524a5752d11SDavid Howells if (tm->tm_year >= 50) 525a5752d11SDavid Howells tm->tm_year += 1900; 526c26fd69fSDavid Howells else 527a5752d11SDavid Howells tm->tm_year += 2000; 528c26fd69fSDavid Howells } else if (tag == ASN1_GENTIM) { 529c26fd69fSDavid Howells /* GenTime: YYYYMMDDHHMMSSZ */ 530c26fd69fSDavid Howells if (vlen != 15) 531c26fd69fSDavid Howells goto unsupported_time; 532a5752d11SDavid Howells tm->tm_year = DD2bin(p) * 100 + DD2bin(p); 533c26fd69fSDavid Howells } else { 534c26fd69fSDavid Howells goto unsupported_time; 535c26fd69fSDavid Howells } 536c26fd69fSDavid Howells 537a5752d11SDavid Howells tm->tm_year -= 1900; 538a5752d11SDavid Howells tm->tm_mon = DD2bin(p) - 1; 539a5752d11SDavid Howells tm->tm_mday = DD2bin(p); 540a5752d11SDavid Howells tm->tm_hour = DD2bin(p); 541a5752d11SDavid Howells tm->tm_min = DD2bin(p); 542a5752d11SDavid Howells tm->tm_sec = DD2bin(p); 543c26fd69fSDavid Howells 544c26fd69fSDavid Howells if (*p != 'Z') 545c26fd69fSDavid Howells goto unsupported_time; 546c26fd69fSDavid Howells 547c26fd69fSDavid Howells return 0; 548c26fd69fSDavid Howells 549c26fd69fSDavid Howells unsupported_time: 550c26fd69fSDavid Howells pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n", 551c26fd69fSDavid Howells tag, (int)vlen, (int)vlen, value); 552c26fd69fSDavid Howells return -EBADMSG; 553c26fd69fSDavid Howells } 554c26fd69fSDavid Howells 555c26fd69fSDavid Howells int x509_note_not_before(void *context, size_t hdrlen, 556c26fd69fSDavid Howells unsigned char tag, 557c26fd69fSDavid Howells const void *value, size_t vlen) 558c26fd69fSDavid Howells { 559c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 560c26fd69fSDavid Howells return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen); 561c26fd69fSDavid Howells } 562c26fd69fSDavid Howells 563c26fd69fSDavid Howells int x509_note_not_after(void *context, size_t hdrlen, 564c26fd69fSDavid Howells unsigned char tag, 565c26fd69fSDavid Howells const void *value, size_t vlen) 566c26fd69fSDavid Howells { 567c26fd69fSDavid Howells struct x509_parse_context *ctx = context; 568c26fd69fSDavid Howells return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen); 569c26fd69fSDavid Howells } 570