| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 2 | /* Validate the trust chain of a PKCS#7 message. | 
|---|
| 3 | * | 
|---|
| 4 | * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. | 
|---|
| 5 | * Written by David Howells (dhowells@redhat.com) | 
|---|
| 6 | */ | 
|---|
| 7 |  | 
|---|
| 8 | #define pr_fmt(fmt) "PKCS7: "fmt | 
|---|
| 9 | #include <linux/kernel.h> | 
|---|
| 10 | #include <linux/export.h> | 
|---|
| 11 | #include <linux/slab.h> | 
|---|
| 12 | #include <linux/err.h> | 
|---|
| 13 | #include <linux/asn1.h> | 
|---|
| 14 | #include <linux/key.h> | 
|---|
| 15 | #include <keys/asymmetric-type.h> | 
|---|
| 16 | #include <crypto/public_key.h> | 
|---|
| 17 | #include "pkcs7_parser.h" | 
|---|
| 18 |  | 
|---|
| 19 | /* | 
|---|
| 20 | * Check the trust on one PKCS#7 SignedInfo block. | 
|---|
| 21 | */ | 
|---|
| 22 | static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, | 
|---|
| 23 | struct pkcs7_signed_info *sinfo, | 
|---|
| 24 | struct key *trust_keyring) | 
|---|
| 25 | { | 
|---|
| 26 | struct public_key_signature *sig = sinfo->sig; | 
|---|
| 27 | struct x509_certificate *x509, *last = NULL, *p; | 
|---|
| 28 | struct key *key; | 
|---|
| 29 | int ret; | 
|---|
| 30 |  | 
|---|
| 31 | kenter( ",%u,", sinfo->index); | 
|---|
| 32 |  | 
|---|
| 33 | if (sinfo->unsupported_crypto) { | 
|---|
| 34 | kleave( " = -ENOPKG [cached]"); | 
|---|
| 35 | return -ENOPKG; | 
|---|
| 36 | } | 
|---|
| 37 |  | 
|---|
| 38 | for (x509 = sinfo->signer; x509; x509 = x509->signer) { | 
|---|
| 39 | if (x509->seen) { | 
|---|
| 40 | if (x509->verified) | 
|---|
| 41 | goto verified; | 
|---|
| 42 | kleave( " = -ENOKEY [cached]"); | 
|---|
| 43 | return -ENOKEY; | 
|---|
| 44 | } | 
|---|
| 45 | x509->seen = true; | 
|---|
| 46 |  | 
|---|
| 47 | /* Look to see if this certificate is present in the trusted | 
|---|
| 48 | * keys. | 
|---|
| 49 | */ | 
|---|
| 50 | key = find_asymmetric_key(keyring: trust_keyring, | 
|---|
| 51 | id_0: x509->id, id_1: x509->skid, NULL, partial: false); | 
|---|
| 52 | if (!IS_ERR(ptr: key)) { | 
|---|
| 53 | /* One of the X.509 certificates in the PKCS#7 message | 
|---|
| 54 | * is apparently the same as one we already trust. | 
|---|
| 55 | * Verify that the trusted variant can also validate | 
|---|
| 56 | * the signature on the descendant. | 
|---|
| 57 | */ | 
|---|
| 58 | pr_devel( "sinfo %u: Cert %u as key %x\n", | 
|---|
| 59 | sinfo->index, x509->index, key_serial(key)); | 
|---|
| 60 | goto matched; | 
|---|
| 61 | } | 
|---|
| 62 | if (key == ERR_PTR(error: -ENOMEM)) | 
|---|
| 63 | return -ENOMEM; | 
|---|
| 64 |  | 
|---|
| 65 | /* Self-signed certificates form roots of their own, and if we | 
|---|
| 66 | * don't know them, then we can't accept them. | 
|---|
| 67 | */ | 
|---|
| 68 | if (x509->signer == x509) { | 
|---|
| 69 | kleave( " = -ENOKEY [unknown self-signed]"); | 
|---|
| 70 | return -ENOKEY; | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | might_sleep(); | 
|---|
| 74 | last = x509; | 
|---|
| 75 | sig = last->sig; | 
|---|
| 76 | } | 
|---|
| 77 |  | 
|---|
| 78 | /* No match - see if the root certificate has a signer amongst the | 
|---|
| 79 | * trusted keys. | 
|---|
| 80 | */ | 
|---|
| 81 | if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) { | 
|---|
| 82 | key = find_asymmetric_key(keyring: trust_keyring, | 
|---|
| 83 | id_0: last->sig->auth_ids[0], | 
|---|
| 84 | id_1: last->sig->auth_ids[1], | 
|---|
| 85 | NULL, partial: false); | 
|---|
| 86 | if (!IS_ERR(ptr: key)) { | 
|---|
| 87 | x509 = last; | 
|---|
| 88 | pr_devel( "sinfo %u: Root cert %u signer is key %x\n", | 
|---|
| 89 | sinfo->index, x509->index, key_serial(key)); | 
|---|
| 90 | goto matched; | 
|---|
| 91 | } | 
|---|
| 92 | if (PTR_ERR(ptr: key) != -ENOKEY) | 
|---|
| 93 | return PTR_ERR(ptr: key); | 
|---|
| 94 | } | 
|---|
| 95 |  | 
|---|
| 96 | /* As a last resort, see if we have a trusted public key that matches | 
|---|
| 97 | * the signed info directly. | 
|---|
| 98 | */ | 
|---|
| 99 | key = find_asymmetric_key(keyring: trust_keyring, | 
|---|
| 100 | id_0: sinfo->sig->auth_ids[0], NULL, NULL, partial: false); | 
|---|
| 101 | if (!IS_ERR(ptr: key)) { | 
|---|
| 102 | pr_devel( "sinfo %u: Direct signer is key %x\n", | 
|---|
| 103 | sinfo->index, key_serial(key)); | 
|---|
| 104 | x509 = NULL; | 
|---|
| 105 | sig = sinfo->sig; | 
|---|
| 106 | goto matched; | 
|---|
| 107 | } | 
|---|
| 108 | if (PTR_ERR(ptr: key) != -ENOKEY) | 
|---|
| 109 | return PTR_ERR(ptr: key); | 
|---|
| 110 |  | 
|---|
| 111 | kleave( " = -ENOKEY [no backref]"); | 
|---|
| 112 | return -ENOKEY; | 
|---|
| 113 |  | 
|---|
| 114 | matched: | 
|---|
| 115 | ret = verify_signature(key, sig); | 
|---|
| 116 | key_put(key); | 
|---|
| 117 | if (ret < 0) { | 
|---|
| 118 | if (ret == -ENOMEM) | 
|---|
| 119 | return ret; | 
|---|
| 120 | kleave( " = -EKEYREJECTED [verify %d]", ret); | 
|---|
| 121 | return -EKEYREJECTED; | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | verified: | 
|---|
| 125 | if (x509) { | 
|---|
| 126 | x509->verified = true; | 
|---|
| 127 | for (p = sinfo->signer; p != x509; p = p->signer) | 
|---|
| 128 | p->verified = true; | 
|---|
| 129 | } | 
|---|
| 130 | kleave( " = 0"); | 
|---|
| 131 | return 0; | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | /** | 
|---|
| 135 | * pkcs7_validate_trust - Validate PKCS#7 trust chain | 
|---|
| 136 | * @pkcs7: The PKCS#7 certificate to validate | 
|---|
| 137 | * @trust_keyring: Signing certificates to use as starting points | 
|---|
| 138 | * | 
|---|
| 139 | * Validate that the certificate chain inside the PKCS#7 message intersects | 
|---|
| 140 | * keys we already know and trust. | 
|---|
| 141 | * | 
|---|
| 142 | * Returns, in order of descending priority: | 
|---|
| 143 | * | 
|---|
| 144 | *  (*) -EKEYREJECTED if a signature failed to match for which we have a valid | 
|---|
| 145 | *	key, or: | 
|---|
| 146 | * | 
|---|
| 147 | *  (*) 0 if at least one signature chain intersects with the keys in the trust | 
|---|
| 148 | *	keyring, or: | 
|---|
| 149 | * | 
|---|
| 150 | *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a | 
|---|
| 151 | *	chain. | 
|---|
| 152 | * | 
|---|
| 153 | *  (*) -ENOKEY if we couldn't find a match for any of the signature chains in | 
|---|
| 154 | *	the message. | 
|---|
| 155 | * | 
|---|
| 156 | * May also return -ENOMEM. | 
|---|
| 157 | */ | 
|---|
| 158 | int pkcs7_validate_trust(struct pkcs7_message *pkcs7, | 
|---|
| 159 | struct key *trust_keyring) | 
|---|
| 160 | { | 
|---|
| 161 | struct pkcs7_signed_info *sinfo; | 
|---|
| 162 | struct x509_certificate *p; | 
|---|
| 163 | int cached_ret = -ENOKEY; | 
|---|
| 164 | int ret; | 
|---|
| 165 |  | 
|---|
| 166 | for (p = pkcs7->certs; p; p = p->next) | 
|---|
| 167 | p->seen = false; | 
|---|
| 168 |  | 
|---|
| 169 | for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { | 
|---|
| 170 | ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); | 
|---|
| 171 | switch (ret) { | 
|---|
| 172 | case -ENOKEY: | 
|---|
| 173 | continue; | 
|---|
| 174 | case -ENOPKG: | 
|---|
| 175 | if (cached_ret == -ENOKEY) | 
|---|
| 176 | cached_ret = -ENOPKG; | 
|---|
| 177 | continue; | 
|---|
| 178 | case 0: | 
|---|
| 179 | cached_ret = 0; | 
|---|
| 180 | continue; | 
|---|
| 181 | default: | 
|---|
| 182 | return ret; | 
|---|
| 183 | } | 
|---|
| 184 | } | 
|---|
| 185 |  | 
|---|
| 186 | return cached_ret; | 
|---|
| 187 | } | 
|---|
| 188 | EXPORT_SYMBOL_GPL(pkcs7_validate_trust); | 
|---|
| 189 |  | 
|---|