1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22e3fadbfSDavid Howells /* PKCS#7 parser
32e3fadbfSDavid Howells  *
42e3fadbfSDavid Howells  * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
52e3fadbfSDavid Howells  * Written by David Howells (dhowells@redhat.com)
62e3fadbfSDavid Howells  */
72e3fadbfSDavid Howells 
82e3fadbfSDavid Howells #define pr_fmt(fmt) "PKCS7: "fmt
92e3fadbfSDavid Howells #include <linux/kernel.h>
101e684d38SDavid Howells #include <linux/module.h>
112e3fadbfSDavid Howells #include <linux/export.h>
122e3fadbfSDavid Howells #include <linux/slab.h>
132e3fadbfSDavid Howells #include <linux/err.h>
142e3fadbfSDavid Howells #include <linux/oid_registry.h>
15db6c43bdSTadeusz Struk #include <crypto/public_key.h>
162e3fadbfSDavid Howells #include "pkcs7_parser.h"
174fa8bc94SMasahiro Yamada #include "pkcs7.asn1.h"
182e3fadbfSDavid Howells 
191e684d38SDavid Howells MODULE_DESCRIPTION("PKCS#7 parser");
201e684d38SDavid Howells MODULE_AUTHOR("Red Hat, Inc.");
211e684d38SDavid Howells MODULE_LICENSE("GPL");
221e684d38SDavid Howells 
232e3fadbfSDavid Howells struct pkcs7_parse_context {
242e3fadbfSDavid Howells 	struct pkcs7_message	*msg;		/* Message being constructed */
252e3fadbfSDavid Howells 	struct pkcs7_signed_info *sinfo;	/* SignedInfo being constructed */
262e3fadbfSDavid Howells 	struct pkcs7_signed_info **ppsinfo;
272e3fadbfSDavid Howells 	struct x509_certificate *certs;		/* Certificate cache */
282e3fadbfSDavid Howells 	struct x509_certificate **ppcerts;
292e3fadbfSDavid Howells 	unsigned long	data;			/* Start of data */
302e3fadbfSDavid Howells 	enum OID	last_oid;		/* Last OID encountered */
312e3fadbfSDavid Howells 	unsigned	x509_index;
322e3fadbfSDavid Howells 	unsigned	sinfo_index;
3346963b77SDavid Howells 	const void	*raw_serial;
3446963b77SDavid Howells 	unsigned	raw_serial_size;
3546963b77SDavid Howells 	unsigned	raw_issuer_size;
3646963b77SDavid Howells 	const void	*raw_issuer;
3760d65cacSDavid Howells 	const void	*raw_skid;
3860d65cacSDavid Howells 	unsigned	raw_skid_size;
3960d65cacSDavid Howells 	bool		expect_skid;
402e3fadbfSDavid Howells };
412e3fadbfSDavid Howells 
423cd0920cSDavid Howells /*
433cd0920cSDavid Howells  * Free a signed information block.
443cd0920cSDavid Howells  */
pkcs7_free_signed_info(struct pkcs7_signed_info * sinfo)453cd0920cSDavid Howells static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
463cd0920cSDavid Howells {
473cd0920cSDavid Howells 	if (sinfo) {
48566a117aSDavid Howells 		public_key_signature_free(sinfo->sig);
493cd0920cSDavid Howells 		kfree(sinfo);
503cd0920cSDavid Howells 	}
513cd0920cSDavid Howells }
523cd0920cSDavid Howells 
532e3fadbfSDavid Howells /**
542e3fadbfSDavid Howells  * pkcs7_free_message - Free a PKCS#7 message
552e3fadbfSDavid Howells  * @pkcs7: The PKCS#7 message to free
562e3fadbfSDavid Howells  */
pkcs7_free_message(struct pkcs7_message * pkcs7)572e3fadbfSDavid Howells void pkcs7_free_message(struct pkcs7_message *pkcs7)
582e3fadbfSDavid Howells {
592e3fadbfSDavid Howells 	struct x509_certificate *cert;
602e3fadbfSDavid Howells 	struct pkcs7_signed_info *sinfo;
612e3fadbfSDavid Howells 
622e3fadbfSDavid Howells 	if (pkcs7) {
632e3fadbfSDavid Howells 		while (pkcs7->certs) {
642e3fadbfSDavid Howells 			cert = pkcs7->certs;
652e3fadbfSDavid Howells 			pkcs7->certs = cert->next;
662e3fadbfSDavid Howells 			x509_free_certificate(cert);
672e3fadbfSDavid Howells 		}
682e3fadbfSDavid Howells 		while (pkcs7->crl) {
692e3fadbfSDavid Howells 			cert = pkcs7->crl;
702e3fadbfSDavid Howells 			pkcs7->crl = cert->next;
712e3fadbfSDavid Howells 			x509_free_certificate(cert);
722e3fadbfSDavid Howells 		}
732e3fadbfSDavid Howells 		while (pkcs7->signed_infos) {
742e3fadbfSDavid Howells 			sinfo = pkcs7->signed_infos;
752e3fadbfSDavid Howells 			pkcs7->signed_infos = sinfo->next;
763cd0920cSDavid Howells 			pkcs7_free_signed_info(sinfo);
772e3fadbfSDavid Howells 		}
782e3fadbfSDavid Howells 		kfree(pkcs7);
792e3fadbfSDavid Howells 	}
802e3fadbfSDavid Howells }
812e3fadbfSDavid Howells EXPORT_SYMBOL_GPL(pkcs7_free_message);
822e3fadbfSDavid Howells 
8399db4435SDavid Howells /*
8499db4435SDavid Howells  * Check authenticatedAttributes are provided or not provided consistently.
8599db4435SDavid Howells  */
pkcs7_check_authattrs(struct pkcs7_message * msg)8699db4435SDavid Howells static int pkcs7_check_authattrs(struct pkcs7_message *msg)
8799db4435SDavid Howells {
8899db4435SDavid Howells 	struct pkcs7_signed_info *sinfo;
8906aae592SColin Ian King 	bool want = false;
9099db4435SDavid Howells 
9199db4435SDavid Howells 	sinfo = msg->signed_infos;
9268a1fdbbSEric Sesterhenn 	if (!sinfo)
9368a1fdbbSEric Sesterhenn 		goto inconsistent;
9468a1fdbbSEric Sesterhenn 
9599db4435SDavid Howells 	if (sinfo->authattrs) {
9699db4435SDavid Howells 		want = true;
9799db4435SDavid Howells 		msg->have_authattrs = true;
9899db4435SDavid Howells 	}
9999db4435SDavid Howells 
10099db4435SDavid Howells 	for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
10199db4435SDavid Howells 		if (!!sinfo->authattrs != want)
10299db4435SDavid Howells 			goto inconsistent;
10399db4435SDavid Howells 	return 0;
10499db4435SDavid Howells 
10599db4435SDavid Howells inconsistent:
10699db4435SDavid Howells 	pr_warn("Inconsistently supplied authAttrs\n");
10799db4435SDavid Howells 	return -EINVAL;
10899db4435SDavid Howells }
10999db4435SDavid Howells 
1102e3fadbfSDavid Howells /**
1112e3fadbfSDavid Howells  * pkcs7_parse_message - Parse a PKCS#7 message
1122e3fadbfSDavid Howells  * @data: The raw binary ASN.1 encoded message to be parsed
1132e3fadbfSDavid Howells  * @datalen: The size of the encoded message
1142e3fadbfSDavid Howells  */
pkcs7_parse_message(const void * data,size_t datalen)1152e3fadbfSDavid Howells struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
1162e3fadbfSDavid Howells {
1172e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx;
118cecf5d2eSDavid Howells 	struct pkcs7_message *msg = ERR_PTR(-ENOMEM);
119cecf5d2eSDavid Howells 	int ret;
1202e3fadbfSDavid Howells 
1212e3fadbfSDavid Howells 	ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
1222e3fadbfSDavid Howells 	if (!ctx)
123cecf5d2eSDavid Howells 		goto out_no_ctx;
124cecf5d2eSDavid Howells 	ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
125cecf5d2eSDavid Howells 	if (!ctx->msg)
126cecf5d2eSDavid Howells 		goto out_no_msg;
1272e3fadbfSDavid Howells 	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
1282e3fadbfSDavid Howells 	if (!ctx->sinfo)
129cecf5d2eSDavid Howells 		goto out_no_sinfo;
130566a117aSDavid Howells 	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
131566a117aSDavid Howells 				  GFP_KERNEL);
132566a117aSDavid Howells 	if (!ctx->sinfo->sig)
133566a117aSDavid Howells 		goto out_no_sig;
1342e3fadbfSDavid Howells 
1352e3fadbfSDavid Howells 	ctx->data = (unsigned long)data;
1362e3fadbfSDavid Howells 	ctx->ppcerts = &ctx->certs;
1372e3fadbfSDavid Howells 	ctx->ppsinfo = &ctx->msg->signed_infos;
1382e3fadbfSDavid Howells 
1392e3fadbfSDavid Howells 	/* Attempt to decode the signature */
1402e3fadbfSDavid Howells 	ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
141cecf5d2eSDavid Howells 	if (ret < 0) {
142cecf5d2eSDavid Howells 		msg = ERR_PTR(ret);
143cecf5d2eSDavid Howells 		goto out;
144cecf5d2eSDavid Howells 	}
1452e3fadbfSDavid Howells 
14699db4435SDavid Howells 	ret = pkcs7_check_authattrs(ctx->msg);
1478ecb506dSEric Biggers 	if (ret < 0) {
1488ecb506dSEric Biggers 		msg = ERR_PTR(ret);
14999db4435SDavid Howells 		goto out;
1508ecb506dSEric Biggers 	}
15199db4435SDavid Howells 
152cecf5d2eSDavid Howells 	msg = ctx->msg;
153cecf5d2eSDavid Howells 	ctx->msg = NULL;
154cecf5d2eSDavid Howells 
155cecf5d2eSDavid Howells out:
1562e3fadbfSDavid Howells 	while (ctx->certs) {
1572e3fadbfSDavid Howells 		struct x509_certificate *cert = ctx->certs;
1582e3fadbfSDavid Howells 		ctx->certs = cert->next;
1592e3fadbfSDavid Howells 		x509_free_certificate(cert);
1602e3fadbfSDavid Howells 	}
161566a117aSDavid Howells out_no_sig:
1623cd0920cSDavid Howells 	pkcs7_free_signed_info(ctx->sinfo);
163cecf5d2eSDavid Howells out_no_sinfo:
164cecf5d2eSDavid Howells 	pkcs7_free_message(ctx->msg);
165cecf5d2eSDavid Howells out_no_msg:
1662e3fadbfSDavid Howells 	kfree(ctx);
167cecf5d2eSDavid Howells out_no_ctx:
1682e3fadbfSDavid Howells 	return msg;
1692e3fadbfSDavid Howells }
1702e3fadbfSDavid Howells EXPORT_SYMBOL_GPL(pkcs7_parse_message);
1712e3fadbfSDavid Howells 
1722e3fadbfSDavid Howells /**
1732e3fadbfSDavid Howells  * pkcs7_get_content_data - Get access to the PKCS#7 content
1742e3fadbfSDavid Howells  * @pkcs7: The preparsed PKCS#7 message to access
1752e3fadbfSDavid Howells  * @_data: Place to return a pointer to the data
1762e3fadbfSDavid Howells  * @_data_len: Place to return the data length
177e68503bdSDavid Howells  * @_headerlen: Size of ASN.1 header not included in _data
1782e3fadbfSDavid Howells  *
179e68503bdSDavid Howells  * Get access to the data content of the PKCS#7 message.  The size of the
180e68503bdSDavid Howells  * header of the ASN.1 object that contains it is also provided and can be used
181e68503bdSDavid Howells  * to adjust *_data and *_data_len to get the entire object.
182e68503bdSDavid Howells  *
183e68503bdSDavid Howells  * Returns -ENODATA if the data object was missing from the message.
1842e3fadbfSDavid Howells  */
pkcs7_get_content_data(const struct pkcs7_message * pkcs7,const void ** _data,size_t * _data_len,size_t * _headerlen)1852e3fadbfSDavid Howells int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
1862e3fadbfSDavid Howells 			   const void **_data, size_t *_data_len,
187e68503bdSDavid Howells 			   size_t *_headerlen)
1882e3fadbfSDavid Howells {
1892e3fadbfSDavid Howells 	if (!pkcs7->data)
1902e3fadbfSDavid Howells 		return -ENODATA;
1912e3fadbfSDavid Howells 
192e68503bdSDavid Howells 	*_data = pkcs7->data;
193e68503bdSDavid Howells 	*_data_len = pkcs7->data_len;
194e68503bdSDavid Howells 	if (_headerlen)
195e68503bdSDavid Howells 		*_headerlen = pkcs7->data_hdrlen;
1962e3fadbfSDavid Howells 	return 0;
1972e3fadbfSDavid Howells }
1982e3fadbfSDavid Howells EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
1992e3fadbfSDavid Howells 
2002e3fadbfSDavid Howells /*
2012e3fadbfSDavid Howells  * Note an OID when we find one for later processing when we know how
2022e3fadbfSDavid Howells  * to interpret it.
2032e3fadbfSDavid Howells  */
pkcs7_note_OID(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)2042e3fadbfSDavid Howells int pkcs7_note_OID(void *context, size_t hdrlen,
2052e3fadbfSDavid Howells 		   unsigned char tag,
2062e3fadbfSDavid Howells 		   const void *value, size_t vlen)
2072e3fadbfSDavid Howells {
2082e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
2092e3fadbfSDavid Howells 
2102e3fadbfSDavid Howells 	ctx->last_oid = look_up_OID(value, vlen);
2112e3fadbfSDavid Howells 	if (ctx->last_oid == OID__NR) {
2122e3fadbfSDavid Howells 		char buffer[50];
2132e3fadbfSDavid Howells 		sprint_oid(value, vlen, buffer, sizeof(buffer));
2142e3fadbfSDavid Howells 		printk("PKCS7: Unknown OID: [%lu] %s\n",
2152e3fadbfSDavid Howells 		       (unsigned long)value - ctx->data, buffer);
2162e3fadbfSDavid Howells 	}
2172e3fadbfSDavid Howells 	return 0;
2182e3fadbfSDavid Howells }
2192e3fadbfSDavid Howells 
2202e3fadbfSDavid Howells /*
2212e3fadbfSDavid Howells  * Note the digest algorithm for the signature.
2222e3fadbfSDavid Howells  */
pkcs7_sig_note_digest_algo(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)2232e3fadbfSDavid Howells int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
2242e3fadbfSDavid Howells 			       unsigned char tag,
2252e3fadbfSDavid Howells 			       const void *value, size_t vlen)
2262e3fadbfSDavid Howells {
2272e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
2282e3fadbfSDavid Howells 
2292e3fadbfSDavid Howells 	switch (ctx->last_oid) {
2302e3fadbfSDavid Howells 	case OID_md4:
231566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "md4";
2322e3fadbfSDavid Howells 		break;
2332e3fadbfSDavid Howells 	case OID_md5:
234566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "md5";
2352e3fadbfSDavid Howells 		break;
2362e3fadbfSDavid Howells 	case OID_sha1:
237566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "sha1";
2382e3fadbfSDavid Howells 		break;
2392e3fadbfSDavid Howells 	case OID_sha256:
240566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "sha256";
2412e3fadbfSDavid Howells 		break;
24207f081fbSDavid Howells 	case OID_sha384:
243566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "sha384";
24407f081fbSDavid Howells 		break;
24507f081fbSDavid Howells 	case OID_sha512:
246566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "sha512";
24707f081fbSDavid Howells 		break;
24807f081fbSDavid Howells 	case OID_sha224:
249566a117aSDavid Howells 		ctx->sinfo->sig->hash_algo = "sha224";
250566a117aSDavid Howells 		break;
2513fb8e3f5STianjia Zhang 	case OID_sm3:
2523fb8e3f5STianjia Zhang 		ctx->sinfo->sig->hash_algo = "sm3";
2533fb8e3f5STianjia Zhang 		break;
254*d35f42caSElvira Khabirova 	case OID_gost2012Digest256:
255*d35f42caSElvira Khabirova 		ctx->sinfo->sig->hash_algo = "streebog256";
256*d35f42caSElvira Khabirova 		break;
257*d35f42caSElvira Khabirova 	case OID_gost2012Digest512:
258*d35f42caSElvira Khabirova 		ctx->sinfo->sig->hash_algo = "streebog512";
259*d35f42caSElvira Khabirova 		break;
2602e3fadbfSDavid Howells 	default:
2612e3fadbfSDavid Howells 		printk("Unsupported digest algo: %u\n", ctx->last_oid);
2622e3fadbfSDavid Howells 		return -ENOPKG;
2632e3fadbfSDavid Howells 	}
2642e3fadbfSDavid Howells 	return 0;
2652e3fadbfSDavid Howells }
2662e3fadbfSDavid Howells 
2672e3fadbfSDavid Howells /*
2682e3fadbfSDavid Howells  * Note the public key algorithm for the signature.
2692e3fadbfSDavid Howells  */
pkcs7_sig_note_pkey_algo(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)2702e3fadbfSDavid Howells int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
2712e3fadbfSDavid Howells 			     unsigned char tag,
2722e3fadbfSDavid Howells 			     const void *value, size_t vlen)
2732e3fadbfSDavid Howells {
2742e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
2752e3fadbfSDavid Howells 
2762e3fadbfSDavid Howells 	switch (ctx->last_oid) {
2772e3fadbfSDavid Howells 	case OID_rsaEncryption:
278566a117aSDavid Howells 		ctx->sinfo->sig->pkey_algo = "rsa";
27903988490SDavid Howells 		ctx->sinfo->sig->encoding = "pkcs1";
2802e3fadbfSDavid Howells 		break;
281a4aed36eSStefan Berger 	case OID_id_ecdsa_with_sha1:
282a4aed36eSStefan Berger 	case OID_id_ecdsa_with_sha224:
283a4aed36eSStefan Berger 	case OID_id_ecdsa_with_sha256:
284a4aed36eSStefan Berger 	case OID_id_ecdsa_with_sha384:
285a4aed36eSStefan Berger 	case OID_id_ecdsa_with_sha512:
286a4aed36eSStefan Berger 		ctx->sinfo->sig->pkey_algo = "ecdsa";
287a4aed36eSStefan Berger 		ctx->sinfo->sig->encoding = "x962";
288a4aed36eSStefan Berger 		break;
2893fb8e3f5STianjia Zhang 	case OID_SM2_with_SM3:
2903fb8e3f5STianjia Zhang 		ctx->sinfo->sig->pkey_algo = "sm2";
2913fb8e3f5STianjia Zhang 		ctx->sinfo->sig->encoding = "raw";
2923fb8e3f5STianjia Zhang 		break;
293*d35f42caSElvira Khabirova 	case OID_gost2012PKey256:
294*d35f42caSElvira Khabirova 	case OID_gost2012PKey512:
295*d35f42caSElvira Khabirova 		ctx->sinfo->sig->pkey_algo = "ecrdsa";
296*d35f42caSElvira Khabirova 		ctx->sinfo->sig->encoding = "raw";
297*d35f42caSElvira Khabirova 		break;
2982e3fadbfSDavid Howells 	default:
2992e3fadbfSDavid Howells 		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
3002e3fadbfSDavid Howells 		return -ENOPKG;
3012e3fadbfSDavid Howells 	}
3022e3fadbfSDavid Howells 	return 0;
3032e3fadbfSDavid Howells }
3042e3fadbfSDavid Howells 
3052e3fadbfSDavid Howells /*
3062c7fd367SDavid Howells  * We only support signed data [RFC2315 sec 9].
3072c7fd367SDavid Howells  */
pkcs7_check_content_type(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)3082c7fd367SDavid Howells int pkcs7_check_content_type(void *context, size_t hdrlen,
3092c7fd367SDavid Howells 			     unsigned char tag,
3102c7fd367SDavid Howells 			     const void *value, size_t vlen)
3112c7fd367SDavid Howells {
3122c7fd367SDavid Howells 	struct pkcs7_parse_context *ctx = context;
3132c7fd367SDavid Howells 
3142c7fd367SDavid Howells 	if (ctx->last_oid != OID_signed_data) {
3152c7fd367SDavid Howells 		pr_warn("Only support pkcs7_signedData type\n");
3162c7fd367SDavid Howells 		return -EINVAL;
3172c7fd367SDavid Howells 	}
3182c7fd367SDavid Howells 
3192c7fd367SDavid Howells 	return 0;
3202c7fd367SDavid Howells }
3212c7fd367SDavid Howells 
3222c7fd367SDavid Howells /*
3232c7fd367SDavid Howells  * Note the SignedData version
3242c7fd367SDavid Howells  */
pkcs7_note_signeddata_version(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)3252c7fd367SDavid Howells int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
3262c7fd367SDavid Howells 				  unsigned char tag,
3272c7fd367SDavid Howells 				  const void *value, size_t vlen)
3282c7fd367SDavid Howells {
32960d65cacSDavid Howells 	struct pkcs7_parse_context *ctx = context;
3302c7fd367SDavid Howells 	unsigned version;
3312c7fd367SDavid Howells 
3322c7fd367SDavid Howells 	if (vlen != 1)
3332c7fd367SDavid Howells 		goto unsupported;
3342c7fd367SDavid Howells 
33560d65cacSDavid Howells 	ctx->msg->version = version = *(const u8 *)value;
3362c7fd367SDavid Howells 	switch (version) {
3372c7fd367SDavid Howells 	case 1:
33860d65cacSDavid Howells 		/* PKCS#7 SignedData [RFC2315 sec 9.1]
33960d65cacSDavid Howells 		 * CMS ver 1 SignedData [RFC5652 sec 5.1]
34060d65cacSDavid Howells 		 */
34160d65cacSDavid Howells 		break;
34260d65cacSDavid Howells 	case 3:
34360d65cacSDavid Howells 		/* CMS ver 3 SignedData [RFC2315 sec 5.1] */
3442c7fd367SDavid Howells 		break;
3452c7fd367SDavid Howells 	default:
3462c7fd367SDavid Howells 		goto unsupported;
3472c7fd367SDavid Howells 	}
3482c7fd367SDavid Howells 
3492c7fd367SDavid Howells 	return 0;
3502c7fd367SDavid Howells 
3512c7fd367SDavid Howells unsupported:
3522c7fd367SDavid Howells 	pr_warn("Unsupported SignedData version\n");
3532c7fd367SDavid Howells 	return -EINVAL;
3542c7fd367SDavid Howells }
3552c7fd367SDavid Howells 
3562c7fd367SDavid Howells /*
3572c7fd367SDavid Howells  * Note the SignerInfo version
3582c7fd367SDavid Howells  */
pkcs7_note_signerinfo_version(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)3592c7fd367SDavid Howells int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
3602c7fd367SDavid Howells 				  unsigned char tag,
3612c7fd367SDavid Howells 				  const void *value, size_t vlen)
3622c7fd367SDavid Howells {
36360d65cacSDavid Howells 	struct pkcs7_parse_context *ctx = context;
3642c7fd367SDavid Howells 	unsigned version;
3652c7fd367SDavid Howells 
3662c7fd367SDavid Howells 	if (vlen != 1)
3672c7fd367SDavid Howells 		goto unsupported;
3682c7fd367SDavid Howells 
3692c7fd367SDavid Howells 	version = *(const u8 *)value;
3702c7fd367SDavid Howells 	switch (version) {
3712c7fd367SDavid Howells 	case 1:
37260d65cacSDavid Howells 		/* PKCS#7 SignerInfo [RFC2315 sec 9.2]
37360d65cacSDavid Howells 		 * CMS ver 1 SignerInfo [RFC5652 sec 5.3]
37460d65cacSDavid Howells 		 */
37560d65cacSDavid Howells 		if (ctx->msg->version != 1)
37660d65cacSDavid Howells 			goto version_mismatch;
37760d65cacSDavid Howells 		ctx->expect_skid = false;
37860d65cacSDavid Howells 		break;
37960d65cacSDavid Howells 	case 3:
38060d65cacSDavid Howells 		/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
38160d65cacSDavid Howells 		if (ctx->msg->version == 1)
38260d65cacSDavid Howells 			goto version_mismatch;
38360d65cacSDavid Howells 		ctx->expect_skid = true;
3842c7fd367SDavid Howells 		break;
3852c7fd367SDavid Howells 	default:
3862c7fd367SDavid Howells 		goto unsupported;
3872c7fd367SDavid Howells 	}
3882c7fd367SDavid Howells 
3892c7fd367SDavid Howells 	return 0;
3902c7fd367SDavid Howells 
3912c7fd367SDavid Howells unsupported:
3922c7fd367SDavid Howells 	pr_warn("Unsupported SignerInfo version\n");
3932c7fd367SDavid Howells 	return -EINVAL;
39460d65cacSDavid Howells version_mismatch:
39560d65cacSDavid Howells 	pr_warn("SignedData-SignerInfo version mismatch\n");
39660d65cacSDavid Howells 	return -EBADMSG;
3972c7fd367SDavid Howells }
3982c7fd367SDavid Howells 
3992c7fd367SDavid Howells /*
4002e3fadbfSDavid Howells  * Extract a certificate and store it in the context.
4012e3fadbfSDavid Howells  */
pkcs7_extract_cert(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4022e3fadbfSDavid Howells int pkcs7_extract_cert(void *context, size_t hdrlen,
4032e3fadbfSDavid Howells 		       unsigned char tag,
4042e3fadbfSDavid Howells 		       const void *value, size_t vlen)
4052e3fadbfSDavid Howells {
4062e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
4072e3fadbfSDavid Howells 	struct x509_certificate *x509;
4082e3fadbfSDavid Howells 
4092e3fadbfSDavid Howells 	if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
4102e3fadbfSDavid Howells 		pr_debug("Cert began with tag %02x at %lu\n",
4112e3fadbfSDavid Howells 			 tag, (unsigned long)ctx - ctx->data);
4122e3fadbfSDavid Howells 		return -EBADMSG;
4132e3fadbfSDavid Howells 	}
4142e3fadbfSDavid Howells 
4152e3fadbfSDavid Howells 	/* We have to correct for the header so that the X.509 parser can start
4162e3fadbfSDavid Howells 	 * from the beginning.  Note that since X.509 stipulates DER, there
4172e3fadbfSDavid Howells 	 * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
4182e3fadbfSDavid Howells 	 * stipulates BER).
4192e3fadbfSDavid Howells 	 */
4202e3fadbfSDavid Howells 	value -= hdrlen;
4212e3fadbfSDavid Howells 	vlen += hdrlen;
4222e3fadbfSDavid Howells 
4232e3fadbfSDavid Howells 	if (((u8*)value)[1] == 0x80)
4242e3fadbfSDavid Howells 		vlen += 2; /* Indefinite length - there should be an EOC */
4252e3fadbfSDavid Howells 
4262e3fadbfSDavid Howells 	x509 = x509_cert_parse(value, vlen);
4272e3fadbfSDavid Howells 	if (IS_ERR(x509))
4282e3fadbfSDavid Howells 		return PTR_ERR(x509);
4292e3fadbfSDavid Howells 
4302e3fadbfSDavid Howells 	x509->index = ++ctx->x509_index;
43146963b77SDavid Howells 	pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
43246963b77SDavid Howells 	pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
43346963b77SDavid Howells 
4342e3fadbfSDavid Howells 	*ctx->ppcerts = x509;
4352e3fadbfSDavid Howells 	ctx->ppcerts = &x509->next;
4362e3fadbfSDavid Howells 	return 0;
4372e3fadbfSDavid Howells }
4382e3fadbfSDavid Howells 
4392e3fadbfSDavid Howells /*
4402e3fadbfSDavid Howells  * Save the certificate list
4412e3fadbfSDavid Howells  */
pkcs7_note_certificate_list(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4422e3fadbfSDavid Howells int pkcs7_note_certificate_list(void *context, size_t hdrlen,
4432e3fadbfSDavid Howells 				unsigned char tag,
4442e3fadbfSDavid Howells 				const void *value, size_t vlen)
4452e3fadbfSDavid Howells {
4462e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
4472e3fadbfSDavid Howells 
4482e3fadbfSDavid Howells 	pr_devel("Got cert list (%02x)\n", tag);
4492e3fadbfSDavid Howells 
4502e3fadbfSDavid Howells 	*ctx->ppcerts = ctx->msg->certs;
4512e3fadbfSDavid Howells 	ctx->msg->certs = ctx->certs;
4522e3fadbfSDavid Howells 	ctx->certs = NULL;
4532e3fadbfSDavid Howells 	ctx->ppcerts = &ctx->certs;
4542e3fadbfSDavid Howells 	return 0;
4552e3fadbfSDavid Howells }
4562e3fadbfSDavid Howells 
4572e3fadbfSDavid Howells /*
45899db4435SDavid Howells  * Note the content type.
45999db4435SDavid Howells  */
pkcs7_note_content(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)46099db4435SDavid Howells int pkcs7_note_content(void *context, size_t hdrlen,
46199db4435SDavid Howells 		       unsigned char tag,
46299db4435SDavid Howells 		       const void *value, size_t vlen)
46399db4435SDavid Howells {
46499db4435SDavid Howells 	struct pkcs7_parse_context *ctx = context;
46599db4435SDavid Howells 
46699db4435SDavid Howells 	if (ctx->last_oid != OID_data &&
46799db4435SDavid Howells 	    ctx->last_oid != OID_msIndirectData) {
46899db4435SDavid Howells 		pr_warn("Unsupported data type %d\n", ctx->last_oid);
46999db4435SDavid Howells 		return -EINVAL;
47099db4435SDavid Howells 	}
47199db4435SDavid Howells 
47299db4435SDavid Howells 	ctx->msg->data_type = ctx->last_oid;
47399db4435SDavid Howells 	return 0;
47499db4435SDavid Howells }
47599db4435SDavid Howells 
47699db4435SDavid Howells /*
4772e3fadbfSDavid Howells  * Extract the data from the message and store that and its content type OID in
4782e3fadbfSDavid Howells  * the context.
4792e3fadbfSDavid Howells  */
pkcs7_note_data(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4802e3fadbfSDavid Howells int pkcs7_note_data(void *context, size_t hdrlen,
4812e3fadbfSDavid Howells 		    unsigned char tag,
4822e3fadbfSDavid Howells 		    const void *value, size_t vlen)
4832e3fadbfSDavid Howells {
4842e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
4852e3fadbfSDavid Howells 
4862e3fadbfSDavid Howells 	pr_debug("Got data\n");
4872e3fadbfSDavid Howells 
4882e3fadbfSDavid Howells 	ctx->msg->data = value;
4892e3fadbfSDavid Howells 	ctx->msg->data_len = vlen;
4902e3fadbfSDavid Howells 	ctx->msg->data_hdrlen = hdrlen;
4912e3fadbfSDavid Howells 	return 0;
4922e3fadbfSDavid Howells }
4932e3fadbfSDavid Howells 
4942e3fadbfSDavid Howells /*
49599db4435SDavid Howells  * Parse authenticated attributes.
4962e3fadbfSDavid Howells  */
pkcs7_sig_note_authenticated_attr(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)4972e3fadbfSDavid Howells int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
4982e3fadbfSDavid Howells 				      unsigned char tag,
4992e3fadbfSDavid Howells 				      const void *value, size_t vlen)
5002e3fadbfSDavid Howells {
5012e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
50299db4435SDavid Howells 	struct pkcs7_signed_info *sinfo = ctx->sinfo;
50399db4435SDavid Howells 	enum OID content_type;
5042e3fadbfSDavid Howells 
5052e3fadbfSDavid Howells 	pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
5062e3fadbfSDavid Howells 
5072e3fadbfSDavid Howells 	switch (ctx->last_oid) {
50899db4435SDavid Howells 	case OID_contentType:
50999db4435SDavid Howells 		if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
51099db4435SDavid Howells 			goto repeated;
51199db4435SDavid Howells 		content_type = look_up_OID(value, vlen);
51299db4435SDavid Howells 		if (content_type != ctx->msg->data_type) {
51399db4435SDavid Howells 			pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
51499db4435SDavid Howells 				ctx->msg->data_type, sinfo->index,
51599db4435SDavid Howells 				content_type);
51699db4435SDavid Howells 			return -EBADMSG;
51799db4435SDavid Howells 		}
51899db4435SDavid Howells 		return 0;
51999db4435SDavid Howells 
52099db4435SDavid Howells 	case OID_signingTime:
52199db4435SDavid Howells 		if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
52299db4435SDavid Howells 			goto repeated;
52399db4435SDavid Howells 		/* Should we check that the signing time is consistent
52499db4435SDavid Howells 		 * with the signer's X.509 cert?
52599db4435SDavid Howells 		 */
52699db4435SDavid Howells 		return x509_decode_time(&sinfo->signing_time,
52799db4435SDavid Howells 					hdrlen, tag, value, vlen);
52899db4435SDavid Howells 
5292e3fadbfSDavid Howells 	case OID_messageDigest:
53099db4435SDavid Howells 		if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
53199db4435SDavid Howells 			goto repeated;
5322e3fadbfSDavid Howells 		if (tag != ASN1_OTS)
5332e3fadbfSDavid Howells 			return -EBADMSG;
53499db4435SDavid Howells 		sinfo->msgdigest = value;
53599db4435SDavid Howells 		sinfo->msgdigest_len = vlen;
53699db4435SDavid Howells 		return 0;
53799db4435SDavid Howells 
53899db4435SDavid Howells 	case OID_smimeCapabilites:
53999db4435SDavid Howells 		if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
54099db4435SDavid Howells 			goto repeated;
54199db4435SDavid Howells 		if (ctx->msg->data_type != OID_msIndirectData) {
54299db4435SDavid Howells 			pr_warn("S/MIME Caps only allowed with Authenticode\n");
54399db4435SDavid Howells 			return -EKEYREJECTED;
54499db4435SDavid Howells 		}
54599db4435SDavid Howells 		return 0;
54699db4435SDavid Howells 
54799db4435SDavid Howells 		/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
54899db4435SDavid Howells 		 * char URLs and cont[1] 8-bit char URLs.
54999db4435SDavid Howells 		 *
55099db4435SDavid Howells 		 * Microsoft StatementType seems to contain a list of OIDs that
55199db4435SDavid Howells 		 * are also used as extendedKeyUsage types in X.509 certs.
55299db4435SDavid Howells 		 */
55399db4435SDavid Howells 	case OID_msSpOpusInfo:
55499db4435SDavid Howells 		if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
55599db4435SDavid Howells 			goto repeated;
55699db4435SDavid Howells 		goto authenticode_check;
55799db4435SDavid Howells 	case OID_msStatementType:
55899db4435SDavid Howells 		if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
55999db4435SDavid Howells 			goto repeated;
56099db4435SDavid Howells 	authenticode_check:
56199db4435SDavid Howells 		if (ctx->msg->data_type != OID_msIndirectData) {
56299db4435SDavid Howells 			pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
56399db4435SDavid Howells 			return -EKEYREJECTED;
56499db4435SDavid Howells 		}
56599db4435SDavid Howells 		/* I'm not sure how to validate these */
5662e3fadbfSDavid Howells 		return 0;
5672e3fadbfSDavid Howells 	default:
5682e3fadbfSDavid Howells 		return 0;
5692e3fadbfSDavid Howells 	}
57099db4435SDavid Howells 
57199db4435SDavid Howells repeated:
57299db4435SDavid Howells 	/* We permit max one item per AuthenticatedAttribute and no repeats */
57399db4435SDavid Howells 	pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
57499db4435SDavid Howells 	return -EKEYREJECTED;
5752e3fadbfSDavid Howells }
5762e3fadbfSDavid Howells 
5772e3fadbfSDavid Howells /*
5782c7fd367SDavid Howells  * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]
5792e3fadbfSDavid Howells  */
pkcs7_sig_note_set_of_authattrs(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)5802e3fadbfSDavid Howells int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
5812e3fadbfSDavid Howells 				    unsigned char tag,
5822e3fadbfSDavid Howells 				    const void *value, size_t vlen)
5832e3fadbfSDavid Howells {
5842e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
58599db4435SDavid Howells 	struct pkcs7_signed_info *sinfo = ctx->sinfo;
58699db4435SDavid Howells 
58799db4435SDavid Howells 	if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
5887ee7014dSPeter Jones 	    !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {
58999db4435SDavid Howells 		pr_warn("Missing required AuthAttr\n");
59099db4435SDavid Howells 		return -EBADMSG;
59199db4435SDavid Howells 	}
59299db4435SDavid Howells 
59399db4435SDavid Howells 	if (ctx->msg->data_type != OID_msIndirectData &&
59499db4435SDavid Howells 	    test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
59599db4435SDavid Howells 		pr_warn("Unexpected Authenticode AuthAttr\n");
59699db4435SDavid Howells 		return -EBADMSG;
59799db4435SDavid Howells 	}
5982e3fadbfSDavid Howells 
5992e3fadbfSDavid Howells 	/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
60099db4435SDavid Howells 	sinfo->authattrs = value - (hdrlen - 1);
60199db4435SDavid Howells 	sinfo->authattrs_len = vlen + (hdrlen - 1);
6022e3fadbfSDavid Howells 	return 0;
6032e3fadbfSDavid Howells }
6042e3fadbfSDavid Howells 
6052e3fadbfSDavid Howells /*
6062e3fadbfSDavid Howells  * Note the issuing certificate serial number
6072e3fadbfSDavid Howells  */
pkcs7_sig_note_serial(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6082e3fadbfSDavid Howells int pkcs7_sig_note_serial(void *context, size_t hdrlen,
6092e3fadbfSDavid Howells 			  unsigned char tag,
6102e3fadbfSDavid Howells 			  const void *value, size_t vlen)
6112e3fadbfSDavid Howells {
6122e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
61346963b77SDavid Howells 	ctx->raw_serial = value;
61446963b77SDavid Howells 	ctx->raw_serial_size = vlen;
6152e3fadbfSDavid Howells 	return 0;
6162e3fadbfSDavid Howells }
6172e3fadbfSDavid Howells 
6182e3fadbfSDavid Howells /*
6192e3fadbfSDavid Howells  * Note the issuer's name
6202e3fadbfSDavid Howells  */
pkcs7_sig_note_issuer(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6212e3fadbfSDavid Howells int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
6222e3fadbfSDavid Howells 			  unsigned char tag,
6232e3fadbfSDavid Howells 			  const void *value, size_t vlen)
6242e3fadbfSDavid Howells {
6252e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
62646963b77SDavid Howells 	ctx->raw_issuer = value;
62746963b77SDavid Howells 	ctx->raw_issuer_size = vlen;
6282e3fadbfSDavid Howells 	return 0;
6292e3fadbfSDavid Howells }
6302e3fadbfSDavid Howells 
6312e3fadbfSDavid Howells /*
63260d65cacSDavid Howells  * Note the issuing cert's subjectKeyIdentifier
63360d65cacSDavid Howells  */
pkcs7_sig_note_skid(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)63460d65cacSDavid Howells int pkcs7_sig_note_skid(void *context, size_t hdrlen,
63560d65cacSDavid Howells 			unsigned char tag,
63660d65cacSDavid Howells 			const void *value, size_t vlen)
63760d65cacSDavid Howells {
63860d65cacSDavid Howells 	struct pkcs7_parse_context *ctx = context;
63960d65cacSDavid Howells 
64060d65cacSDavid Howells 	pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
64160d65cacSDavid Howells 
64260d65cacSDavid Howells 	ctx->raw_skid = value;
64360d65cacSDavid Howells 	ctx->raw_skid_size = vlen;
64460d65cacSDavid Howells 	return 0;
64560d65cacSDavid Howells }
64660d65cacSDavid Howells 
64760d65cacSDavid Howells /*
6482e3fadbfSDavid Howells  * Note the signature data
6492e3fadbfSDavid Howells  */
pkcs7_sig_note_signature(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6502e3fadbfSDavid Howells int pkcs7_sig_note_signature(void *context, size_t hdrlen,
6512e3fadbfSDavid Howells 			     unsigned char tag,
6522e3fadbfSDavid Howells 			     const void *value, size_t vlen)
6532e3fadbfSDavid Howells {
6542e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
6552e3fadbfSDavid Howells 
656566a117aSDavid Howells 	ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
657566a117aSDavid Howells 	if (!ctx->sinfo->sig->s)
6582e3fadbfSDavid Howells 		return -ENOMEM;
6592e3fadbfSDavid Howells 
660566a117aSDavid Howells 	ctx->sinfo->sig->s_size = vlen;
6612e3fadbfSDavid Howells 	return 0;
6622e3fadbfSDavid Howells }
6632e3fadbfSDavid Howells 
6642e3fadbfSDavid Howells /*
6652e3fadbfSDavid Howells  * Note a signature information block
6662e3fadbfSDavid Howells  */
pkcs7_note_signed_info(void * context,size_t hdrlen,unsigned char tag,const void * value,size_t vlen)6672e3fadbfSDavid Howells int pkcs7_note_signed_info(void *context, size_t hdrlen,
6682e3fadbfSDavid Howells 			   unsigned char tag,
6692e3fadbfSDavid Howells 			   const void *value, size_t vlen)
6702e3fadbfSDavid Howells {
6712e3fadbfSDavid Howells 	struct pkcs7_parse_context *ctx = context;
67246963b77SDavid Howells 	struct pkcs7_signed_info *sinfo = ctx->sinfo;
67346963b77SDavid Howells 	struct asymmetric_key_id *kid;
6742e3fadbfSDavid Howells 
67599db4435SDavid Howells 	if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
67699db4435SDavid Howells 		pr_warn("Authenticode requires AuthAttrs\n");
67799db4435SDavid Howells 		return -EBADMSG;
67899db4435SDavid Howells 	}
67999db4435SDavid Howells 
68046963b77SDavid Howells 	/* Generate cert issuer + serial number key ID */
68160d65cacSDavid Howells 	if (!ctx->expect_skid) {
68246963b77SDavid Howells 		kid = asymmetric_key_generate_id(ctx->raw_serial,
68346963b77SDavid Howells 						 ctx->raw_serial_size,
68446963b77SDavid Howells 						 ctx->raw_issuer,
68546963b77SDavid Howells 						 ctx->raw_issuer_size);
68660d65cacSDavid Howells 	} else {
68760d65cacSDavid Howells 		kid = asymmetric_key_generate_id(ctx->raw_skid,
68860d65cacSDavid Howells 						 ctx->raw_skid_size,
68960d65cacSDavid Howells 						 "", 0);
69060d65cacSDavid Howells 	}
69146963b77SDavid Howells 	if (IS_ERR(kid))
69246963b77SDavid Howells 		return PTR_ERR(kid);
69346963b77SDavid Howells 
69460d65cacSDavid Howells 	pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
69560d65cacSDavid Howells 
696566a117aSDavid Howells 	sinfo->sig->auth_ids[0] = kid;
69746963b77SDavid Howells 	sinfo->index = ++ctx->sinfo_index;
69846963b77SDavid Howells 	*ctx->ppsinfo = sinfo;
69946963b77SDavid Howells 	ctx->ppsinfo = &sinfo->next;
7002e3fadbfSDavid Howells 	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
7012e3fadbfSDavid Howells 	if (!ctx->sinfo)
7022e3fadbfSDavid Howells 		return -ENOMEM;
703566a117aSDavid Howells 	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
704566a117aSDavid Howells 				  GFP_KERNEL);
705566a117aSDavid Howells 	if (!ctx->sinfo->sig)
706566a117aSDavid Howells 		return -ENOMEM;
7072e3fadbfSDavid Howells 	return 0;
7082e3fadbfSDavid Howells }
709