pkcs7_trust.c (4692B)
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 */ 22static 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(trust_keyring, 51 x509->id, x509->skid, NULL, false); 52 if (!IS_ERR(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(-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(trust_keyring, 83 last->sig->auth_ids[0], 84 last->sig->auth_ids[1], 85 NULL, false); 86 if (!IS_ERR(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(key) != -ENOKEY) 93 return PTR_ERR(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(trust_keyring, 100 sinfo->sig->auth_ids[0], NULL, NULL, false); 101 if (!IS_ERR(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(key) != -ENOKEY) 109 return PTR_ERR(key); 110 111 kleave(" = -ENOKEY [no backref]"); 112 return -ENOKEY; 113 114matched: 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 124verified: 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 */ 158int 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} 188EXPORT_SYMBOL_GPL(pkcs7_validate_trust);