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"
21b92e6570SDavid Howells #include "x509_akid-asn1.h"
22c26fd69fSDavid Howells #include "x509_rsakey-asn1.h"
23c26fd69fSDavid Howells 
24c26fd69fSDavid Howells struct x509_parse_context {
25c26fd69fSDavid Howells 	struct x509_certificate	*cert;		/* Certificate being constructed */
26c26fd69fSDavid Howells 	unsigned long	data;			/* Start of data */
27c26fd69fSDavid Howells 	const void	*cert_start;		/* Start of cert content */
28c26fd69fSDavid Howells 	const void	*key;			/* Key data */
29c26fd69fSDavid Howells 	size_t		key_size;		/* Size of key data */
30c26fd69fSDavid Howells 	enum OID	last_oid;		/* Last OID encountered */
31c26fd69fSDavid Howells 	enum OID	algo_oid;		/* Algorithm OID */
32c26fd69fSDavid Howells 	unsigned char	nr_mpi;			/* Number of MPIs stored */
33c26fd69fSDavid Howells 	u8		o_size;			/* Size of organizationName (O) */
34c26fd69fSDavid Howells 	u8		cn_size;		/* Size of commonName (CN) */
35c26fd69fSDavid Howells 	u8		email_size;		/* Size of emailAddress */
36c26fd69fSDavid Howells 	u16		o_offset;		/* Offset of organizationName (O) */
37c26fd69fSDavid Howells 	u16		cn_offset;		/* Offset of commonName (CN) */
38c26fd69fSDavid Howells 	u16		email_offset;		/* Offset of emailAddress */
39b92e6570SDavid Howells 	unsigned	raw_akid_size;
40b92e6570SDavid Howells 	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
41b92e6570SDavid Howells 	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
42b92e6570SDavid Howells 	unsigned	akid_raw_issuer_size;
43c26fd69fSDavid Howells };
44c26fd69fSDavid Howells 
45c26fd69fSDavid Howells /*
46c26fd69fSDavid Howells  * Free an X.509 certificate
47c26fd69fSDavid Howells  */
48c26fd69fSDavid Howells void x509_free_certificate(struct x509_certificate *cert)
49c26fd69fSDavid Howells {
50c26fd69fSDavid Howells 	if (cert) {
51c26fd69fSDavid Howells 		public_key_destroy(cert->pub);
52c26fd69fSDavid Howells 		kfree(cert->issuer);
53c26fd69fSDavid Howells 		kfree(cert->subject);
5446963b77SDavid Howells 		kfree(cert->id);
5546963b77SDavid Howells 		kfree(cert->skid);
56b92e6570SDavid Howells 		kfree(cert->akid_id);
57b92e6570SDavid Howells 		kfree(cert->akid_skid);
58b426beb6SDavid Howells 		kfree(cert->sig.digest);
59b426beb6SDavid Howells 		mpi_free(cert->sig.rsa.s);
60c26fd69fSDavid Howells 		kfree(cert);
61c26fd69fSDavid Howells 	}
62c26fd69fSDavid Howells }
63ace0107aSDavid Howells EXPORT_SYMBOL_GPL(x509_free_certificate);
64c26fd69fSDavid Howells 
65c26fd69fSDavid Howells /*
66c26fd69fSDavid Howells  * Parse an X.509 certificate
67c26fd69fSDavid Howells  */
68c26fd69fSDavid Howells struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
69c26fd69fSDavid Howells {
70c26fd69fSDavid Howells 	struct x509_certificate *cert;
71c26fd69fSDavid Howells 	struct x509_parse_context *ctx;
7246963b77SDavid Howells 	struct asymmetric_key_id *kid;
73c26fd69fSDavid Howells 	long ret;
74c26fd69fSDavid Howells 
75c26fd69fSDavid Howells 	ret = -ENOMEM;
76c26fd69fSDavid Howells 	cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
77c26fd69fSDavid Howells 	if (!cert)
78c26fd69fSDavid Howells 		goto error_no_cert;
79c26fd69fSDavid Howells 	cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
80c26fd69fSDavid Howells 	if (!cert->pub)
81c26fd69fSDavid Howells 		goto error_no_ctx;
82c26fd69fSDavid Howells 	ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
83c26fd69fSDavid Howells 	if (!ctx)
84c26fd69fSDavid Howells 		goto error_no_ctx;
85c26fd69fSDavid Howells 
86c26fd69fSDavid Howells 	ctx->cert = cert;
87c26fd69fSDavid Howells 	ctx->data = (unsigned long)data;
88c26fd69fSDavid Howells 
89c26fd69fSDavid Howells 	/* Attempt to decode the certificate */
90c26fd69fSDavid Howells 	ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen);
91c26fd69fSDavid Howells 	if (ret < 0)
92c26fd69fSDavid Howells 		goto error_decode;
93c26fd69fSDavid Howells 
94b92e6570SDavid Howells 	/* Decode the AuthorityKeyIdentifier */
95b92e6570SDavid Howells 	if (ctx->raw_akid) {
96b92e6570SDavid Howells 		pr_devel("AKID: %u %*phN\n",
97b92e6570SDavid Howells 			 ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
98b92e6570SDavid Howells 		ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
99b92e6570SDavid Howells 				       ctx->raw_akid, ctx->raw_akid_size);
100b92e6570SDavid Howells 		if (ret < 0) {
101b92e6570SDavid Howells 			pr_warn("Couldn't decode AuthKeyIdentifier\n");
102b92e6570SDavid Howells 			goto error_decode;
103b92e6570SDavid Howells 		}
104b92e6570SDavid Howells 	}
105b92e6570SDavid Howells 
106c26fd69fSDavid Howells 	/* Decode the public key */
107c26fd69fSDavid Howells 	ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx,
108c26fd69fSDavid Howells 			       ctx->key, ctx->key_size);
109c26fd69fSDavid Howells 	if (ret < 0)
110c26fd69fSDavid Howells 		goto error_decode;
111c26fd69fSDavid Howells 
11246963b77SDavid Howells 	/* Generate cert issuer + serial number key ID */
11346963b77SDavid Howells 	kid = asymmetric_key_generate_id(cert->raw_serial,
11446963b77SDavid Howells 					 cert->raw_serial_size,
11546963b77SDavid Howells 					 cert->raw_issuer,
11646963b77SDavid Howells 					 cert->raw_issuer_size);
11746963b77SDavid Howells 	if (IS_ERR(kid)) {
11846963b77SDavid Howells 		ret = PTR_ERR(kid);
11946963b77SDavid Howells 		goto error_decode;
12046963b77SDavid Howells 	}
12146963b77SDavid Howells 	cert->id = kid;
12246963b77SDavid Howells 
123c26fd69fSDavid Howells 	kfree(ctx);
124c26fd69fSDavid Howells 	return cert;
125c26fd69fSDavid Howells 
126c26fd69fSDavid Howells error_decode:
127c26fd69fSDavid Howells 	kfree(ctx);
128c26fd69fSDavid Howells error_no_ctx:
129c26fd69fSDavid Howells 	x509_free_certificate(cert);
130c26fd69fSDavid Howells error_no_cert:
131c26fd69fSDavid Howells 	return ERR_PTR(ret);
132c26fd69fSDavid Howells }
133ace0107aSDavid Howells EXPORT_SYMBOL_GPL(x509_cert_parse);
134c26fd69fSDavid Howells 
135c26fd69fSDavid Howells /*
136c26fd69fSDavid Howells  * Note an OID when we find one for later processing when we know how
137c26fd69fSDavid Howells  * to interpret it.
138c26fd69fSDavid Howells  */
139c26fd69fSDavid Howells int x509_note_OID(void *context, size_t hdrlen,
140c26fd69fSDavid Howells 	     unsigned char tag,
141c26fd69fSDavid Howells 	     const void *value, size_t vlen)
142c26fd69fSDavid Howells {
143c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
144c26fd69fSDavid Howells 
145c26fd69fSDavid Howells 	ctx->last_oid = look_up_OID(value, vlen);
146c26fd69fSDavid Howells 	if (ctx->last_oid == OID__NR) {
147c26fd69fSDavid Howells 		char buffer[50];
148c26fd69fSDavid Howells 		sprint_oid(value, vlen, buffer, sizeof(buffer));
149cf75446eSRandy Dunlap 		pr_debug("Unknown OID: [%lu] %s\n",
150c26fd69fSDavid Howells 			 (unsigned long)value - ctx->data, buffer);
151c26fd69fSDavid Howells 	}
152c26fd69fSDavid Howells 	return 0;
153c26fd69fSDavid Howells }
154c26fd69fSDavid Howells 
155c26fd69fSDavid Howells /*
156c26fd69fSDavid Howells  * Save the position of the TBS data so that we can check the signature over it
157c26fd69fSDavid Howells  * later.
158c26fd69fSDavid Howells  */
159c26fd69fSDavid Howells int x509_note_tbs_certificate(void *context, size_t hdrlen,
160c26fd69fSDavid Howells 			      unsigned char tag,
161c26fd69fSDavid Howells 			      const void *value, size_t vlen)
162c26fd69fSDavid Howells {
163c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
164c26fd69fSDavid Howells 
165c26fd69fSDavid Howells 	pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n",
166c26fd69fSDavid Howells 		 hdrlen, tag, (unsigned long)value - ctx->data, vlen);
167c26fd69fSDavid Howells 
168c26fd69fSDavid Howells 	ctx->cert->tbs = value - hdrlen;
169c26fd69fSDavid Howells 	ctx->cert->tbs_size = vlen + hdrlen;
170c26fd69fSDavid Howells 	return 0;
171c26fd69fSDavid Howells }
172c26fd69fSDavid Howells 
173c26fd69fSDavid Howells /*
174c26fd69fSDavid Howells  * Record the public key algorithm
175c26fd69fSDavid Howells  */
176c26fd69fSDavid Howells int x509_note_pkey_algo(void *context, size_t hdrlen,
177c26fd69fSDavid Howells 			unsigned char tag,
178c26fd69fSDavid Howells 			const void *value, size_t vlen)
179c26fd69fSDavid Howells {
180c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
181c26fd69fSDavid Howells 
182c26fd69fSDavid Howells 	pr_debug("PubKey Algo: %u\n", ctx->last_oid);
183c26fd69fSDavid Howells 
184c26fd69fSDavid Howells 	switch (ctx->last_oid) {
185c26fd69fSDavid Howells 	case OID_md2WithRSAEncryption:
186c26fd69fSDavid Howells 	case OID_md3WithRSAEncryption:
187c26fd69fSDavid Howells 	default:
188c26fd69fSDavid Howells 		return -ENOPKG; /* Unsupported combination */
189c26fd69fSDavid Howells 
190c26fd69fSDavid Howells 	case OID_md4WithRSAEncryption:
1913fe78ca2SDmitry Kasatkin 		ctx->cert->sig.pkey_hash_algo = HASH_ALGO_MD5;
192b426beb6SDavid Howells 		ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
193c26fd69fSDavid Howells 		break;
194c26fd69fSDavid Howells 
195c26fd69fSDavid Howells 	case OID_sha1WithRSAEncryption:
1963fe78ca2SDmitry Kasatkin 		ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA1;
197b426beb6SDavid Howells 		ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
198c26fd69fSDavid Howells 		break;
199c26fd69fSDavid Howells 
200c26fd69fSDavid Howells 	case OID_sha256WithRSAEncryption:
2013fe78ca2SDmitry Kasatkin 		ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA256;
202b426beb6SDavid Howells 		ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
203c26fd69fSDavid Howells 		break;
204c26fd69fSDavid Howells 
205c26fd69fSDavid Howells 	case OID_sha384WithRSAEncryption:
2063fe78ca2SDmitry Kasatkin 		ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA384;
207b426beb6SDavid Howells 		ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
208c26fd69fSDavid Howells 		break;
209c26fd69fSDavid Howells 
210c26fd69fSDavid Howells 	case OID_sha512WithRSAEncryption:
2113fe78ca2SDmitry Kasatkin 		ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA512;
212b426beb6SDavid Howells 		ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
213c26fd69fSDavid Howells 		break;
214c26fd69fSDavid Howells 
215c26fd69fSDavid Howells 	case OID_sha224WithRSAEncryption:
2163fe78ca2SDmitry Kasatkin 		ctx->cert->sig.pkey_hash_algo = HASH_ALGO_SHA224;
217b426beb6SDavid Howells 		ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA;
218c26fd69fSDavid Howells 		break;
219c26fd69fSDavid Howells 	}
220c26fd69fSDavid Howells 
221c26fd69fSDavid Howells 	ctx->algo_oid = ctx->last_oid;
222c26fd69fSDavid Howells 	return 0;
223c26fd69fSDavid Howells }
224c26fd69fSDavid Howells 
225c26fd69fSDavid Howells /*
226c26fd69fSDavid Howells  * Note the whereabouts and type of the signature.
227c26fd69fSDavid Howells  */
228c26fd69fSDavid Howells int x509_note_signature(void *context, size_t hdrlen,
229c26fd69fSDavid Howells 			unsigned char tag,
230c26fd69fSDavid Howells 			const void *value, size_t vlen)
231c26fd69fSDavid Howells {
232c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
233c26fd69fSDavid Howells 
234c26fd69fSDavid Howells 	pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen);
235c26fd69fSDavid Howells 
236c26fd69fSDavid Howells 	if (ctx->last_oid != ctx->algo_oid) {
237c26fd69fSDavid Howells 		pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
238c26fd69fSDavid Howells 			ctx->algo_oid, ctx->last_oid);
239c26fd69fSDavid Howells 		return -EINVAL;
240c26fd69fSDavid Howells 	}
241c26fd69fSDavid Howells 
242b426beb6SDavid Howells 	ctx->cert->raw_sig = value;
243b426beb6SDavid Howells 	ctx->cert->raw_sig_size = vlen;
244c26fd69fSDavid Howells 	return 0;
245c26fd69fSDavid Howells }
246c26fd69fSDavid Howells 
247c26fd69fSDavid Howells /*
24884aabd46SDavid Howells  * Note the certificate serial number
24984aabd46SDavid Howells  */
25084aabd46SDavid Howells int x509_note_serial(void *context, size_t hdrlen,
25184aabd46SDavid Howells 		     unsigned char tag,
25284aabd46SDavid Howells 		     const void *value, size_t vlen)
25384aabd46SDavid Howells {
25484aabd46SDavid Howells 	struct x509_parse_context *ctx = context;
25584aabd46SDavid Howells 	ctx->cert->raw_serial = value;
25684aabd46SDavid Howells 	ctx->cert->raw_serial_size = vlen;
25784aabd46SDavid Howells 	return 0;
25884aabd46SDavid Howells }
25984aabd46SDavid Howells 
26084aabd46SDavid Howells /*
261c26fd69fSDavid Howells  * Note some of the name segments from which we'll fabricate a name.
262c26fd69fSDavid Howells  */
263c26fd69fSDavid Howells int x509_extract_name_segment(void *context, size_t hdrlen,
264c26fd69fSDavid Howells 			      unsigned char tag,
265c26fd69fSDavid Howells 			      const void *value, size_t vlen)
266c26fd69fSDavid Howells {
267c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
268c26fd69fSDavid Howells 
269c26fd69fSDavid Howells 	switch (ctx->last_oid) {
270c26fd69fSDavid Howells 	case OID_commonName:
271c26fd69fSDavid Howells 		ctx->cn_size = vlen;
272c26fd69fSDavid Howells 		ctx->cn_offset = (unsigned long)value - ctx->data;
273c26fd69fSDavid Howells 		break;
274c26fd69fSDavid Howells 	case OID_organizationName:
275c26fd69fSDavid Howells 		ctx->o_size = vlen;
276c26fd69fSDavid Howells 		ctx->o_offset = (unsigned long)value - ctx->data;
277c26fd69fSDavid Howells 		break;
278c26fd69fSDavid Howells 	case OID_email_address:
279c26fd69fSDavid Howells 		ctx->email_size = vlen;
280c26fd69fSDavid Howells 		ctx->email_offset = (unsigned long)value - ctx->data;
281c26fd69fSDavid Howells 		break;
282c26fd69fSDavid Howells 	default:
283c26fd69fSDavid Howells 		break;
284c26fd69fSDavid Howells 	}
285c26fd69fSDavid Howells 
286c26fd69fSDavid Howells 	return 0;
287c26fd69fSDavid Howells }
288c26fd69fSDavid Howells 
289c26fd69fSDavid Howells /*
290c26fd69fSDavid Howells  * Fabricate and save the issuer and subject names
291c26fd69fSDavid Howells  */
292c26fd69fSDavid Howells static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
293c26fd69fSDavid Howells 			       unsigned char tag,
294c26fd69fSDavid Howells 			       char **_name, size_t vlen)
295c26fd69fSDavid Howells {
296c26fd69fSDavid Howells 	const void *name, *data = (const void *)ctx->data;
297c26fd69fSDavid Howells 	size_t namesize;
298c26fd69fSDavid Howells 	char *buffer;
299c26fd69fSDavid Howells 
300c26fd69fSDavid Howells 	if (*_name)
301c26fd69fSDavid Howells 		return -EINVAL;
302c26fd69fSDavid Howells 
303c26fd69fSDavid Howells 	/* Empty name string if no material */
304c26fd69fSDavid Howells 	if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
305c26fd69fSDavid Howells 		buffer = kmalloc(1, GFP_KERNEL);
306c26fd69fSDavid Howells 		if (!buffer)
307c26fd69fSDavid Howells 			return -ENOMEM;
308c26fd69fSDavid Howells 		buffer[0] = 0;
309c26fd69fSDavid Howells 		goto done;
310c26fd69fSDavid Howells 	}
311c26fd69fSDavid Howells 
312c26fd69fSDavid Howells 	if (ctx->cn_size && ctx->o_size) {
313c26fd69fSDavid Howells 		/* Consider combining O and CN, but use only the CN if it is
314c26fd69fSDavid Howells 		 * prefixed by the O, or a significant portion thereof.
315c26fd69fSDavid Howells 		 */
316c26fd69fSDavid Howells 		namesize = ctx->cn_size;
317c26fd69fSDavid Howells 		name = data + ctx->cn_offset;
318c26fd69fSDavid Howells 		if (ctx->cn_size >= ctx->o_size &&
319c26fd69fSDavid Howells 		    memcmp(data + ctx->cn_offset, data + ctx->o_offset,
320c26fd69fSDavid Howells 			   ctx->o_size) == 0)
321c26fd69fSDavid Howells 			goto single_component;
322c26fd69fSDavid Howells 		if (ctx->cn_size >= 7 &&
323c26fd69fSDavid Howells 		    ctx->o_size >= 7 &&
324c26fd69fSDavid Howells 		    memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0)
325c26fd69fSDavid Howells 			goto single_component;
326c26fd69fSDavid Howells 
327c26fd69fSDavid Howells 		buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1,
328c26fd69fSDavid Howells 				 GFP_KERNEL);
329c26fd69fSDavid Howells 		if (!buffer)
330c26fd69fSDavid Howells 			return -ENOMEM;
331c26fd69fSDavid Howells 
332c26fd69fSDavid Howells 		memcpy(buffer,
333c26fd69fSDavid Howells 		       data + ctx->o_offset, ctx->o_size);
334c26fd69fSDavid Howells 		buffer[ctx->o_size + 0] = ':';
335c26fd69fSDavid Howells 		buffer[ctx->o_size + 1] = ' ';
336c26fd69fSDavid Howells 		memcpy(buffer + ctx->o_size + 2,
337c26fd69fSDavid Howells 		       data + ctx->cn_offset, ctx->cn_size);
338c26fd69fSDavid Howells 		buffer[ctx->o_size + 2 + ctx->cn_size] = 0;
339c26fd69fSDavid Howells 		goto done;
340c26fd69fSDavid Howells 
341c26fd69fSDavid Howells 	} else if (ctx->cn_size) {
342c26fd69fSDavid Howells 		namesize = ctx->cn_size;
343c26fd69fSDavid Howells 		name = data + ctx->cn_offset;
344c26fd69fSDavid Howells 	} else if (ctx->o_size) {
345c26fd69fSDavid Howells 		namesize = ctx->o_size;
346c26fd69fSDavid Howells 		name = data + ctx->o_offset;
347c26fd69fSDavid Howells 	} else {
348c26fd69fSDavid Howells 		namesize = ctx->email_size;
349c26fd69fSDavid Howells 		name = data + ctx->email_offset;
350c26fd69fSDavid Howells 	}
351c26fd69fSDavid Howells 
352c26fd69fSDavid Howells single_component:
353c26fd69fSDavid Howells 	buffer = kmalloc(namesize + 1, GFP_KERNEL);
354c26fd69fSDavid Howells 	if (!buffer)
355c26fd69fSDavid Howells 		return -ENOMEM;
356c26fd69fSDavid Howells 	memcpy(buffer, name, namesize);
357c26fd69fSDavid Howells 	buffer[namesize] = 0;
358c26fd69fSDavid Howells 
359c26fd69fSDavid Howells done:
360c26fd69fSDavid Howells 	*_name = buffer;
361c26fd69fSDavid Howells 	ctx->cn_size = 0;
362c26fd69fSDavid Howells 	ctx->o_size = 0;
363c26fd69fSDavid Howells 	ctx->email_size = 0;
364c26fd69fSDavid Howells 	return 0;
365c26fd69fSDavid Howells }
366c26fd69fSDavid Howells 
367c26fd69fSDavid Howells int x509_note_issuer(void *context, size_t hdrlen,
368c26fd69fSDavid Howells 		     unsigned char tag,
369c26fd69fSDavid Howells 		     const void *value, size_t vlen)
370c26fd69fSDavid Howells {
371c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
37284aabd46SDavid Howells 	ctx->cert->raw_issuer = value;
37384aabd46SDavid Howells 	ctx->cert->raw_issuer_size = vlen;
374c26fd69fSDavid Howells 	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
375c26fd69fSDavid Howells }
376c26fd69fSDavid Howells 
377c26fd69fSDavid Howells int x509_note_subject(void *context, size_t hdrlen,
378c26fd69fSDavid Howells 		      unsigned char tag,
379c26fd69fSDavid Howells 		      const void *value, size_t vlen)
380c26fd69fSDavid Howells {
381c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
38284aabd46SDavid Howells 	ctx->cert->raw_subject = value;
38384aabd46SDavid Howells 	ctx->cert->raw_subject_size = vlen;
384c26fd69fSDavid Howells 	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
385c26fd69fSDavid Howells }
386c26fd69fSDavid Howells 
387c26fd69fSDavid Howells /*
388c26fd69fSDavid Howells  * Extract the data for the public key algorithm
389c26fd69fSDavid Howells  */
390c26fd69fSDavid Howells int x509_extract_key_data(void *context, size_t hdrlen,
391c26fd69fSDavid Howells 			  unsigned char tag,
392c26fd69fSDavid Howells 			  const void *value, size_t vlen)
393c26fd69fSDavid Howells {
394c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
395c26fd69fSDavid Howells 
396c26fd69fSDavid Howells 	if (ctx->last_oid != OID_rsaEncryption)
397c26fd69fSDavid Howells 		return -ENOPKG;
398c26fd69fSDavid Howells 
39967f7d60bSDavid Howells 	ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA;
40067f7d60bSDavid Howells 
40167f7d60bSDavid Howells 	/* Discard the BIT STRING metadata */
402c26fd69fSDavid Howells 	ctx->key = value + 1;
403c26fd69fSDavid Howells 	ctx->key_size = vlen - 1;
404c26fd69fSDavid Howells 	return 0;
405c26fd69fSDavid Howells }
406c26fd69fSDavid Howells 
407c26fd69fSDavid Howells /*
408c26fd69fSDavid Howells  * Extract a RSA public key value
409c26fd69fSDavid Howells  */
410c26fd69fSDavid Howells int rsa_extract_mpi(void *context, size_t hdrlen,
411c26fd69fSDavid Howells 		    unsigned char tag,
412c26fd69fSDavid Howells 		    const void *value, size_t vlen)
413c26fd69fSDavid Howells {
414c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
415c26fd69fSDavid Howells 	MPI mpi;
416c26fd69fSDavid Howells 
417c26fd69fSDavid Howells 	if (ctx->nr_mpi >= ARRAY_SIZE(ctx->cert->pub->mpi)) {
418c26fd69fSDavid Howells 		pr_err("Too many public key MPIs in certificate\n");
419c26fd69fSDavid Howells 		return -EBADMSG;
420c26fd69fSDavid Howells 	}
421c26fd69fSDavid Howells 
422c26fd69fSDavid Howells 	mpi = mpi_read_raw_data(value, vlen);
423c26fd69fSDavid Howells 	if (!mpi)
424c26fd69fSDavid Howells 		return -ENOMEM;
425c26fd69fSDavid Howells 
426c26fd69fSDavid Howells 	ctx->cert->pub->mpi[ctx->nr_mpi++] = mpi;
427c26fd69fSDavid Howells 	return 0;
428c26fd69fSDavid Howells }
429c26fd69fSDavid Howells 
43004b00bdbSChun-Yi Lee /* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */
43104b00bdbSChun-Yi Lee #define SEQ_TAG_KEYID (ASN1_CONT << 6)
43204b00bdbSChun-Yi Lee 
433c26fd69fSDavid Howells /*
434c26fd69fSDavid Howells  * Process certificate extensions that are used to qualify the certificate.
435c26fd69fSDavid Howells  */
436c26fd69fSDavid Howells int x509_process_extension(void *context, size_t hdrlen,
437c26fd69fSDavid Howells 			   unsigned char tag,
438c26fd69fSDavid Howells 			   const void *value, size_t vlen)
439c26fd69fSDavid Howells {
440c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
44146963b77SDavid Howells 	struct asymmetric_key_id *kid;
442c26fd69fSDavid Howells 	const unsigned char *v = value;
443c26fd69fSDavid Howells 
444c26fd69fSDavid Howells 	pr_debug("Extension: %u\n", ctx->last_oid);
445c26fd69fSDavid Howells 
446c26fd69fSDavid Howells 	if (ctx->last_oid == OID_subjectKeyIdentifier) {
447c26fd69fSDavid Howells 		/* Get hold of the key fingerprint */
44846963b77SDavid Howells 		if (ctx->cert->skid || vlen < 3)
449c26fd69fSDavid Howells 			return -EBADMSG;
450c26fd69fSDavid Howells 		if (v[0] != ASN1_OTS || v[1] != vlen - 2)
451c26fd69fSDavid Howells 			return -EBADMSG;
452c26fd69fSDavid Howells 		v += 2;
453c26fd69fSDavid Howells 		vlen -= 2;
454c26fd69fSDavid Howells 
455dd2f6c44SDavid Howells 		ctx->cert->raw_skid_size = vlen;
456dd2f6c44SDavid Howells 		ctx->cert->raw_skid = v;
457a4c6e57fSDavid Howells 		kid = asymmetric_key_generate_id(v, vlen, "", 0);
45846963b77SDavid Howells 		if (IS_ERR(kid))
45946963b77SDavid Howells 			return PTR_ERR(kid);
46046963b77SDavid Howells 		ctx->cert->skid = kid;
46146963b77SDavid Howells 		pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
462c26fd69fSDavid Howells 		return 0;
463c26fd69fSDavid Howells 	}
464c26fd69fSDavid Howells 
465c26fd69fSDavid Howells 	if (ctx->last_oid == OID_authorityKeyIdentifier) {
466c26fd69fSDavid Howells 		/* Get hold of the CA key fingerprint */
467b92e6570SDavid Howells 		ctx->raw_akid = v;
468b92e6570SDavid Howells 		ctx->raw_akid_size = vlen;
469c26fd69fSDavid Howells 		return 0;
470c26fd69fSDavid Howells 	}
471c26fd69fSDavid Howells 
472c26fd69fSDavid Howells 	return 0;
473c26fd69fSDavid Howells }
474c26fd69fSDavid Howells 
475fd19a3d1SDavid Howells /**
476fd19a3d1SDavid Howells  * x509_decode_time - Decode an X.509 time ASN.1 object
477fd19a3d1SDavid Howells  * @_t: The time to fill in
478fd19a3d1SDavid Howells  * @hdrlen: The length of the object header
479fd19a3d1SDavid Howells  * @tag: The object tag
480fd19a3d1SDavid Howells  * @value: The object value
481fd19a3d1SDavid Howells  * @vlen: The size of the object value
482fd19a3d1SDavid Howells  *
483fd19a3d1SDavid Howells  * Decode an ASN.1 universal time or generalised time field into a struct the
484fd19a3d1SDavid Howells  * kernel can handle and check it for validity.  The time is decoded thus:
485fd19a3d1SDavid Howells  *
486fd19a3d1SDavid Howells  *	[RFC5280 §4.1.2.5]
487fd19a3d1SDavid Howells  *	CAs conforming to this profile MUST always encode certificate validity
488fd19a3d1SDavid Howells  *	dates through the year 2049 as UTCTime; certificate validity dates in
489fd19a3d1SDavid Howells  *	2050 or later MUST be encoded as GeneralizedTime.  Conforming
490fd19a3d1SDavid Howells  *	applications MUST be able to process validity dates that are encoded in
491fd19a3d1SDavid Howells  *	either UTCTime or GeneralizedTime.
492c26fd69fSDavid Howells  */
493fd19a3d1SDavid Howells int x509_decode_time(time64_t *_t,  size_t hdrlen,
494c26fd69fSDavid Howells 		     unsigned char tag,
495c26fd69fSDavid Howells 		     const unsigned char *value, size_t vlen)
496c26fd69fSDavid Howells {
497fd19a3d1SDavid Howells 	static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30,
498fd19a3d1SDavid Howells 						       31, 31, 30, 31, 30, 31 };
499c26fd69fSDavid Howells 	const unsigned char *p = value;
500fd19a3d1SDavid Howells 	unsigned year, mon, day, hour, min, sec, mon_len;
501c26fd69fSDavid Howells 
502fd19a3d1SDavid Howells #define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
503c26fd69fSDavid Howells #define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
504c26fd69fSDavid Howells 
505c26fd69fSDavid Howells 	if (tag == ASN1_UNITIM) {
506c26fd69fSDavid Howells 		/* UTCTime: YYMMDDHHMMSSZ */
507c26fd69fSDavid Howells 		if (vlen != 13)
508c26fd69fSDavid Howells 			goto unsupported_time;
509fd19a3d1SDavid Howells 		year = DD2bin(p);
510fd19a3d1SDavid Howells 		if (year >= 50)
511fd19a3d1SDavid Howells 			year += 1900;
512c26fd69fSDavid Howells 		else
513fd19a3d1SDavid Howells 			year += 2000;
514c26fd69fSDavid Howells 	} else if (tag == ASN1_GENTIM) {
515c26fd69fSDavid Howells 		/* GenTime: YYYYMMDDHHMMSSZ */
516c26fd69fSDavid Howells 		if (vlen != 15)
517c26fd69fSDavid Howells 			goto unsupported_time;
518fd19a3d1SDavid Howells 		year = DD2bin(p) * 100 + DD2bin(p);
519fd19a3d1SDavid Howells 		if (year >= 1950 && year <= 2049)
520fd19a3d1SDavid Howells 			goto invalid_time;
521c26fd69fSDavid Howells 	} else {
522c26fd69fSDavid Howells 		goto unsupported_time;
523c26fd69fSDavid Howells 	}
524c26fd69fSDavid Howells 
525fd19a3d1SDavid Howells 	mon  = DD2bin(p);
526fd19a3d1SDavid Howells 	day = DD2bin(p);
527fd19a3d1SDavid Howells 	hour = DD2bin(p);
528fd19a3d1SDavid Howells 	min  = DD2bin(p);
529fd19a3d1SDavid Howells 	sec  = DD2bin(p);
530c26fd69fSDavid Howells 
531c26fd69fSDavid Howells 	if (*p != 'Z')
532c26fd69fSDavid Howells 		goto unsupported_time;
533c26fd69fSDavid Howells 
534cc25b994SDavid Howells 	if (year < 1970 ||
535cc25b994SDavid Howells 	    mon < 1 || mon > 12)
536cc25b994SDavid Howells 		goto invalid_time;
537cc25b994SDavid Howells 
538cc25b994SDavid Howells 	mon_len = month_lengths[mon - 1];
539fd19a3d1SDavid Howells 	if (mon == 2) {
540fd19a3d1SDavid Howells 		if (year % 4 == 0) {
541fd19a3d1SDavid Howells 			mon_len = 29;
542fd19a3d1SDavid Howells 			if (year % 100 == 0) {
543fd19a3d1SDavid Howells 				year /= 100;
544fd19a3d1SDavid Howells 				if (year % 4 != 0)
545fd19a3d1SDavid Howells 					mon_len = 28;
546fd19a3d1SDavid Howells 			}
547fd19a3d1SDavid Howells 		}
548fd19a3d1SDavid Howells 	}
549fd19a3d1SDavid Howells 
550cc25b994SDavid Howells 	if (day < 1 || day > mon_len ||
5514dd17c9cSsudip 	    hour > 23 ||
5524dd17c9cSsudip 	    min > 59 ||
5534dd17c9cSsudip 	    sec > 59)
554fd19a3d1SDavid Howells 		goto invalid_time;
555fd19a3d1SDavid Howells 
556fd19a3d1SDavid Howells 	*_t = mktime64(year, mon, day, hour, min, sec);
557c26fd69fSDavid Howells 	return 0;
558c26fd69fSDavid Howells 
559c26fd69fSDavid Howells unsupported_time:
560fd19a3d1SDavid Howells 	pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
561fd19a3d1SDavid Howells 		 tag, (int)vlen, value);
562fd19a3d1SDavid Howells 	return -EBADMSG;
563fd19a3d1SDavid Howells invalid_time:
564fd19a3d1SDavid Howells 	pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
565fd19a3d1SDavid Howells 		 tag, (int)vlen, value);
566c26fd69fSDavid Howells 	return -EBADMSG;
567c26fd69fSDavid Howells }
568fd19a3d1SDavid Howells EXPORT_SYMBOL_GPL(x509_decode_time);
569c26fd69fSDavid Howells 
570c26fd69fSDavid Howells int x509_note_not_before(void *context, size_t hdrlen,
571c26fd69fSDavid Howells 			 unsigned char tag,
572c26fd69fSDavid Howells 			 const void *value, size_t vlen)
573c26fd69fSDavid Howells {
574c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
575fd19a3d1SDavid Howells 	return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
576c26fd69fSDavid Howells }
577c26fd69fSDavid Howells 
578c26fd69fSDavid Howells int x509_note_not_after(void *context, size_t hdrlen,
579c26fd69fSDavid Howells 			unsigned char tag,
580c26fd69fSDavid Howells 			const void *value, size_t vlen)
581c26fd69fSDavid Howells {
582c26fd69fSDavid Howells 	struct x509_parse_context *ctx = context;
583fd19a3d1SDavid Howells 	return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
584c26fd69fSDavid Howells }
585b92e6570SDavid Howells 
586b92e6570SDavid Howells /*
587b92e6570SDavid Howells  * Note a key identifier-based AuthorityKeyIdentifier
588b92e6570SDavid Howells  */
589b92e6570SDavid Howells int x509_akid_note_kid(void *context, size_t hdrlen,
590b92e6570SDavid Howells 		       unsigned char tag,
591b92e6570SDavid Howells 		       const void *value, size_t vlen)
592b92e6570SDavid Howells {
593b92e6570SDavid Howells 	struct x509_parse_context *ctx = context;
594b92e6570SDavid Howells 	struct asymmetric_key_id *kid;
595b92e6570SDavid Howells 
596b92e6570SDavid Howells 	pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
597b92e6570SDavid Howells 
598b92e6570SDavid Howells 	if (ctx->cert->akid_skid)
599b92e6570SDavid Howells 		return 0;
600b92e6570SDavid Howells 
601a4c6e57fSDavid Howells 	kid = asymmetric_key_generate_id(value, vlen, "", 0);
602b92e6570SDavid Howells 	if (IS_ERR(kid))
603b92e6570SDavid Howells 		return PTR_ERR(kid);
604b92e6570SDavid Howells 	pr_debug("authkeyid %*phN\n", kid->len, kid->data);
605b92e6570SDavid Howells 	ctx->cert->akid_skid = kid;
606b92e6570SDavid Howells 	return 0;
607b92e6570SDavid Howells }
608b92e6570SDavid Howells 
609b92e6570SDavid Howells /*
610b92e6570SDavid Howells  * Note a directoryName in an AuthorityKeyIdentifier
611b92e6570SDavid Howells  */
612b92e6570SDavid Howells int x509_akid_note_name(void *context, size_t hdrlen,
613b92e6570SDavid Howells 			unsigned char tag,
614b92e6570SDavid Howells 			const void *value, size_t vlen)
615b92e6570SDavid Howells {
616b92e6570SDavid Howells 	struct x509_parse_context *ctx = context;
617b92e6570SDavid Howells 
618b92e6570SDavid Howells 	pr_debug("AKID: name: %*phN\n", (int)vlen, value);
619b92e6570SDavid Howells 
620b92e6570SDavid Howells 	ctx->akid_raw_issuer = value;
621b92e6570SDavid Howells 	ctx->akid_raw_issuer_size = vlen;
622b92e6570SDavid Howells 	return 0;
623b92e6570SDavid Howells }
624b92e6570SDavid Howells 
625b92e6570SDavid Howells /*
626b92e6570SDavid Howells  * Note a serial number in an AuthorityKeyIdentifier
627b92e6570SDavid Howells  */
628b92e6570SDavid Howells int x509_akid_note_serial(void *context, size_t hdrlen,
629b92e6570SDavid Howells 			  unsigned char tag,
630b92e6570SDavid Howells 			  const void *value, size_t vlen)
631b92e6570SDavid Howells {
632b92e6570SDavid Howells 	struct x509_parse_context *ctx = context;
633b92e6570SDavid Howells 	struct asymmetric_key_id *kid;
634b92e6570SDavid Howells 
635b92e6570SDavid Howells 	pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
636b92e6570SDavid Howells 
637b92e6570SDavid Howells 	if (!ctx->akid_raw_issuer || ctx->cert->akid_id)
638b92e6570SDavid Howells 		return 0;
639b92e6570SDavid Howells 
640b92e6570SDavid Howells 	kid = asymmetric_key_generate_id(value,
641b92e6570SDavid Howells 					 vlen,
642b92e6570SDavid Howells 					 ctx->akid_raw_issuer,
643b92e6570SDavid Howells 					 ctx->akid_raw_issuer_size);
644b92e6570SDavid Howells 	if (IS_ERR(kid))
645b92e6570SDavid Howells 		return PTR_ERR(kid);
646b92e6570SDavid Howells 
647b92e6570SDavid Howells 	pr_debug("authkeyid %*phN\n", kid->len, kid->data);
648b92e6570SDavid Howells 	ctx->cert->akid_id = kid;
649b92e6570SDavid Howells 	return 0;
650b92e6570SDavid Howells }
651