pkcs7_trust.c 5.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* Validate the trust chain of a PKCS#7 message.
 *
 * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#define pr_fmt(fmt) "PKCS7: "fmt
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/asn1.h>
#include <linux/key.h>
#include <keys/asymmetric-type.h>
20
#include <crypto/public_key.h>
21 22 23 24 25
#include "pkcs7_parser.h"

/**
 * Check the trust on one PKCS#7 SignedInfo block.
 */
D
David Howells 已提交
26 27 28
static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7,
				    struct pkcs7_signed_info *sinfo,
				    struct key *trust_keyring)
29 30 31 32 33 34 35 36 37
{
	struct public_key_signature *sig = &sinfo->sig;
	struct x509_certificate *x509, *last = NULL, *p;
	struct key *key;
	bool trusted;
	int ret;

	kenter(",%u,", sinfo->index);

38 39 40 41 42
	if (sinfo->unsupported_crypto) {
		kleave(" = -ENOPKG [cached]");
		return -ENOPKG;
	}

43 44 45 46 47 48 49 50 51 52 53 54 55 56
	for (x509 = sinfo->signer; x509; x509 = x509->signer) {
		if (x509->seen) {
			if (x509->verified) {
				trusted = x509->trusted;
				goto verified;
			}
			kleave(" = -ENOKEY [cached]");
			return -ENOKEY;
		}
		x509->seen = true;

		/* Look to see if this certificate is present in the trusted
		 * keys.
		 */
57 58
		key = x509_request_asymmetric_key(trust_keyring,
						  x509->id, x509->skid,
59
						  false);
60
		if (!IS_ERR(key)) {
61 62 63 64 65
			/* One of the X.509 certificates in the PKCS#7 message
			 * is apparently the same as one we already trust.
			 * Verify that the trusted variant can also validate
			 * the signature on the descendant.
			 */
66 67
			pr_devel("sinfo %u: Cert %u as key %x\n",
				 sinfo->index, x509->index, key_serial(key));
68
			goto matched;
69
		}
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
		if (key == ERR_PTR(-ENOMEM))
			return -ENOMEM;

		 /* Self-signed certificates form roots of their own, and if we
		  * don't know them, then we can't accept them.
		  */
		if (x509->next == x509) {
			kleave(" = -ENOKEY [unknown self-signed]");
			return -ENOKEY;
		}

		might_sleep();
		last = x509;
		sig = &last->sig;
	}

	/* No match - see if the root certificate has a signer amongst the
	 * trusted keys.
	 */
89 90 91 92
	if (last && (last->akid_id || last->akid_skid)) {
		key = x509_request_asymmetric_key(trust_keyring,
						  last->akid_id,
						  last->akid_skid,
93
						  false);
94 95 96 97 98 99 100 101
		if (!IS_ERR(key)) {
			x509 = last;
			pr_devel("sinfo %u: Root cert %u signer is key %x\n",
				 sinfo->index, x509->index, key_serial(key));
			goto matched;
		}
		if (PTR_ERR(key) != -ENOKEY)
			return PTR_ERR(key);
102 103
	}

104 105 106 107
	/* As a last resort, see if we have a trusted public key that matches
	 * the signed info directly.
	 */
	key = x509_request_asymmetric_key(trust_keyring,
108
					  sinfo->signing_cert_id,
109
					  NULL,
110
					  false);
111 112 113 114 115 116 117 118 119 120 121
	if (!IS_ERR(key)) {
		pr_devel("sinfo %u: Direct signer is key %x\n",
			 sinfo->index, key_serial(key));
		x509 = NULL;
		goto matched;
	}
	if (PTR_ERR(key) != -ENOKEY)
		return PTR_ERR(key);

	kleave(" = -ENOKEY [no backref]");
	return -ENOKEY;
122 123 124 125 126 127 128 129 130 131 132 133 134

matched:
	ret = verify_signature(key, sig);
	trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags);
	key_put(key);
	if (ret < 0) {
		if (ret == -ENOMEM)
			return ret;
		kleave(" = -EKEYREJECTED [verify %d]", ret);
		return -EKEYREJECTED;
	}

verified:
135 136 137 138 139 140
	if (x509) {
		x509->verified = true;
		for (p = sinfo->signer; p != x509; p = p->signer) {
			p->verified = true;
			p->trusted = trusted;
		}
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	}
	sinfo->trusted = trusted;
	kleave(" = 0");
	return 0;
}

/**
 * pkcs7_validate_trust - Validate PKCS#7 trust chain
 * @pkcs7: The PKCS#7 certificate to validate
 * @trust_keyring: Signing certificates to use as starting points
 * @_trusted: Set to true if trustworth, false otherwise
 *
 * Validate that the certificate chain inside the PKCS#7 message intersects
 * keys we already know and trust.
 *
 * Returns, in order of descending priority:
 *
 *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid
 *	key, or:
 *
 *  (*) 0 if at least one signature chain intersects with the keys in the trust
 *	keyring, or:
 *
 *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
 *	chain.
 *
 *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in
 *	the message.
 *
 * May also return -ENOMEM.
 */
int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
			 struct key *trust_keyring,
			 bool *_trusted)
{
	struct pkcs7_signed_info *sinfo;
	struct x509_certificate *p;
178 179
	int cached_ret = -ENOKEY;
	int ret;
180

181 182
	*_trusted = false;

183 184 185 186 187
	for (p = pkcs7->certs; p; p = p->next)
		p->seen = false;

	for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) {
		ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring);
188 189 190 191 192
		switch (ret) {
		case -ENOKEY:
			continue;
		case -ENOPKG:
			if (cached_ret == -ENOKEY)
193
				cached_ret = -ENOPKG;
194 195 196 197 198 199 200
			continue;
		case 0:
			*_trusted |= sinfo->trusted;
			cached_ret = 0;
			continue;
		default:
			return ret;
201 202 203 204 205 206
		}
	}

	return cached_ret;
}
EXPORT_SYMBOL_GPL(pkcs7_validate_trust);