162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Validate the trust chain of a PKCS#7 message. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. 562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) "PKCS7: "fmt 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/asn1.h> 1462306a36Sopenharmony_ci#include <linux/key.h> 1562306a36Sopenharmony_ci#include <keys/asymmetric-type.h> 1662306a36Sopenharmony_ci#include <crypto/public_key.h> 1762306a36Sopenharmony_ci#include "pkcs7_parser.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * Check the trust on one PKCS#7 SignedInfo block. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cistatic int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, 2362306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo, 2462306a36Sopenharmony_ci struct key *trust_keyring) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct public_key_signature *sig = sinfo->sig; 2762306a36Sopenharmony_ci struct x509_certificate *x509, *last = NULL, *p; 2862306a36Sopenharmony_ci struct key *key; 2962306a36Sopenharmony_ci int ret; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci kenter(",%u,", sinfo->index); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (sinfo->unsupported_crypto) { 3462306a36Sopenharmony_ci kleave(" = -ENOPKG [cached]"); 3562306a36Sopenharmony_ci return -ENOPKG; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci for (x509 = sinfo->signer; x509; x509 = x509->signer) { 3962306a36Sopenharmony_ci if (x509->seen) { 4062306a36Sopenharmony_ci if (x509->verified) 4162306a36Sopenharmony_ci goto verified; 4262306a36Sopenharmony_ci kleave(" = -ENOKEY [cached]"); 4362306a36Sopenharmony_ci return -ENOKEY; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci x509->seen = true; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* Look to see if this certificate is present in the trusted 4862306a36Sopenharmony_ci * keys. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci key = find_asymmetric_key(trust_keyring, 5162306a36Sopenharmony_ci x509->id, x509->skid, NULL, false); 5262306a36Sopenharmony_ci if (!IS_ERR(key)) { 5362306a36Sopenharmony_ci /* One of the X.509 certificates in the PKCS#7 message 5462306a36Sopenharmony_ci * is apparently the same as one we already trust. 5562306a36Sopenharmony_ci * Verify that the trusted variant can also validate 5662306a36Sopenharmony_ci * the signature on the descendant. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_ci pr_devel("sinfo %u: Cert %u as key %x\n", 5962306a36Sopenharmony_ci sinfo->index, x509->index, key_serial(key)); 6062306a36Sopenharmony_ci goto matched; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci if (key == ERR_PTR(-ENOMEM)) 6362306a36Sopenharmony_ci return -ENOMEM; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Self-signed certificates form roots of their own, and if we 6662306a36Sopenharmony_ci * don't know them, then we can't accept them. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci if (x509->signer == x509) { 6962306a36Sopenharmony_ci kleave(" = -ENOKEY [unknown self-signed]"); 7062306a36Sopenharmony_ci return -ENOKEY; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci might_sleep(); 7462306a36Sopenharmony_ci last = x509; 7562306a36Sopenharmony_ci sig = last->sig; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* No match - see if the root certificate has a signer amongst the 7962306a36Sopenharmony_ci * trusted keys. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) { 8262306a36Sopenharmony_ci key = find_asymmetric_key(trust_keyring, 8362306a36Sopenharmony_ci last->sig->auth_ids[0], 8462306a36Sopenharmony_ci last->sig->auth_ids[1], 8562306a36Sopenharmony_ci NULL, false); 8662306a36Sopenharmony_ci if (!IS_ERR(key)) { 8762306a36Sopenharmony_ci x509 = last; 8862306a36Sopenharmony_ci pr_devel("sinfo %u: Root cert %u signer is key %x\n", 8962306a36Sopenharmony_ci sinfo->index, x509->index, key_serial(key)); 9062306a36Sopenharmony_ci goto matched; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci if (PTR_ERR(key) != -ENOKEY) 9362306a36Sopenharmony_ci return PTR_ERR(key); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* As a last resort, see if we have a trusted public key that matches 9762306a36Sopenharmony_ci * the signed info directly. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_ci key = find_asymmetric_key(trust_keyring, 10062306a36Sopenharmony_ci sinfo->sig->auth_ids[0], NULL, NULL, false); 10162306a36Sopenharmony_ci if (!IS_ERR(key)) { 10262306a36Sopenharmony_ci pr_devel("sinfo %u: Direct signer is key %x\n", 10362306a36Sopenharmony_ci sinfo->index, key_serial(key)); 10462306a36Sopenharmony_ci x509 = NULL; 10562306a36Sopenharmony_ci sig = sinfo->sig; 10662306a36Sopenharmony_ci goto matched; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci if (PTR_ERR(key) != -ENOKEY) 10962306a36Sopenharmony_ci return PTR_ERR(key); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci kleave(" = -ENOKEY [no backref]"); 11262306a36Sopenharmony_ci return -ENOKEY; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cimatched: 11562306a36Sopenharmony_ci ret = verify_signature(key, sig); 11662306a36Sopenharmony_ci key_put(key); 11762306a36Sopenharmony_ci if (ret < 0) { 11862306a36Sopenharmony_ci if (ret == -ENOMEM) 11962306a36Sopenharmony_ci return ret; 12062306a36Sopenharmony_ci kleave(" = -EKEYREJECTED [verify %d]", ret); 12162306a36Sopenharmony_ci return -EKEYREJECTED; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_civerified: 12562306a36Sopenharmony_ci if (x509) { 12662306a36Sopenharmony_ci x509->verified = true; 12762306a36Sopenharmony_ci for (p = sinfo->signer; p != x509; p = p->signer) 12862306a36Sopenharmony_ci p->verified = true; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci kleave(" = 0"); 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * pkcs7_validate_trust - Validate PKCS#7 trust chain 13662306a36Sopenharmony_ci * @pkcs7: The PKCS#7 certificate to validate 13762306a36Sopenharmony_ci * @trust_keyring: Signing certificates to use as starting points 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Validate that the certificate chain inside the PKCS#7 message intersects 14062306a36Sopenharmony_ci * keys we already know and trust. 14162306a36Sopenharmony_ci * 14262306a36Sopenharmony_ci * Returns, in order of descending priority: 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * (*) -EKEYREJECTED if a signature failed to match for which we have a valid 14562306a36Sopenharmony_ci * key, or: 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * (*) 0 if at least one signature chain intersects with the keys in the trust 14862306a36Sopenharmony_ci * keyring, or: 14962306a36Sopenharmony_ci * 15062306a36Sopenharmony_ci * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a 15162306a36Sopenharmony_ci * chain. 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * (*) -ENOKEY if we couldn't find a match for any of the signature chains in 15462306a36Sopenharmony_ci * the message. 15562306a36Sopenharmony_ci * 15662306a36Sopenharmony_ci * May also return -ENOMEM. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ciint pkcs7_validate_trust(struct pkcs7_message *pkcs7, 15962306a36Sopenharmony_ci struct key *trust_keyring) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct pkcs7_signed_info *sinfo; 16262306a36Sopenharmony_ci struct x509_certificate *p; 16362306a36Sopenharmony_ci int cached_ret = -ENOKEY; 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci for (p = pkcs7->certs; p; p = p->next) 16762306a36Sopenharmony_ci p->seen = false; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { 17062306a36Sopenharmony_ci ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); 17162306a36Sopenharmony_ci switch (ret) { 17262306a36Sopenharmony_ci case -ENOKEY: 17362306a36Sopenharmony_ci continue; 17462306a36Sopenharmony_ci case -ENOPKG: 17562306a36Sopenharmony_ci if (cached_ret == -ENOKEY) 17662306a36Sopenharmony_ci cached_ret = -ENOPKG; 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci case 0: 17962306a36Sopenharmony_ci cached_ret = 0; 18062306a36Sopenharmony_ci continue; 18162306a36Sopenharmony_ci default: 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return cached_ret; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pkcs7_validate_trust); 189