mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 08:21:36 +01:00 
			
		
		
		
	This function is used to calculate a message digest as part of authentication process in a later patch. Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
		
			
				
	
	
		
			824 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			824 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
 | |
|  * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <charset.h>
 | |
| #include <efi_loader.h>
 | |
| #include <efi_variable.h>
 | |
| #include <image.h>
 | |
| #include <hexdump.h>
 | |
| #include <malloc.h>
 | |
| #include <crypto/pkcs7.h>
 | |
| #include <crypto/pkcs7_parser.h>
 | |
| #include <crypto/public_key.h>
 | |
| #include <linux/compat.h>
 | |
| #include <linux/oid_registry.h>
 | |
| #include <u-boot/hash-checksum.h>
 | |
| #include <u-boot/rsa.h>
 | |
| #include <u-boot/sha256.h>
 | |
| 
 | |
| const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
 | |
| const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
 | |
| const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
 | |
| const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
 | |
| const efi_guid_t efi_guid_cert_x509_sha384 = EFI_CERT_X509_SHA384_GUID;
 | |
| const efi_guid_t efi_guid_cert_x509_sha512 = EFI_CERT_X509_SHA512_GUID;
 | |
| const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
 | |
| 
 | |
| static u8 pkcs7_hdr[] = {
 | |
| 	/* SEQUENCE */
 | |
| 	0x30, 0x82, 0x05, 0xc7,
 | |
| 	/* OID: pkcs7-signedData */
 | |
| 	0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
 | |
| 	/* Context Structured? */
 | |
| 	0xa0, 0x82, 0x05, 0xb8,
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * efi_parse_pkcs7_header - parse a signature in payload
 | |
|  * @buf:	Pointer to payload's value
 | |
|  * @buflen:	Length of @buf
 | |
|  * @tmpbuf:	Pointer to temporary buffer
 | |
|  *
 | |
|  * Parse a signature embedded in payload's value and instantiate
 | |
|  * a pkcs7_message structure. Since pkcs7_parse_message() accepts only
 | |
|  * pkcs7's signedData, some header needed be prepended for correctly
 | |
|  * parsing authentication data
 | |
|  * A temporary buffer will be allocated if needed, and it should be
 | |
|  * kept valid during the authentication because some data in the buffer
 | |
|  * will be referenced by efi_signature_verify().
 | |
|  *
 | |
|  * Return:	Pointer to pkcs7_message structure on success, NULL on error
 | |
|  */
 | |
| struct pkcs7_message *efi_parse_pkcs7_header(const void *buf,
 | |
| 					     size_t buflen,
 | |
| 					     u8 **tmpbuf)
 | |
| {
 | |
| 	u8 *ebuf;
 | |
| 	size_t ebuflen, len;
 | |
| 	struct pkcs7_message *msg;
 | |
| 
 | |
| 	/*
 | |
| 	 * This is the best assumption to check if the binary is
 | |
| 	 * already in a form of pkcs7's signedData.
 | |
| 	 */
 | |
| 	if (buflen > sizeof(pkcs7_hdr) &&
 | |
| 	    !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) {
 | |
| 		msg = pkcs7_parse_message(buf, buflen);
 | |
| 		if (IS_ERR(msg))
 | |
| 			return NULL;
 | |
| 		return msg;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Otherwise, we should add a dummy prefix sequence for pkcs7
 | |
| 	 * message parser to be able to process.
 | |
| 	 * NOTE: EDK2 also uses similar hack in WrapPkcs7Data()
 | |
| 	 * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c
 | |
| 	 * TODO:
 | |
| 	 * The header should be composed in a more refined manner.
 | |
| 	 */
 | |
| 	EFI_PRINT("Makeshift prefix added to authentication data\n");
 | |
| 	ebuflen = sizeof(pkcs7_hdr) + buflen;
 | |
| 	if (ebuflen <= 0x7f) {
 | |
| 		EFI_PRINT("Data is too short\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	ebuf = malloc(ebuflen);
 | |
| 	if (!ebuf) {
 | |
| 		EFI_PRINT("Out of memory\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr));
 | |
| 	memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen);
 | |
| 	len = ebuflen - 4;
 | |
| 	ebuf[2] = (len >> 8) & 0xff;
 | |
| 	ebuf[3] = len & 0xff;
 | |
| 	len = ebuflen - 0x13;
 | |
| 	ebuf[0x11] = (len >> 8) & 0xff;
 | |
| 	ebuf[0x12] = len & 0xff;
 | |
| 
 | |
| 	msg = pkcs7_parse_message(ebuf, ebuflen);
 | |
| 
 | |
| 	if (IS_ERR(msg)) {
 | |
| 		free(ebuf);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	*tmpbuf = ebuf;
 | |
| 	return msg;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_hash_regions - calculate a hash value
 | |
|  * @regs:	Array of regions
 | |
|  * @count:	Number of regions
 | |
|  * @hash:	Pointer to a pointer to buffer holding a hash value
 | |
|  * @size:	Size of buffer to be returned
 | |
|  *
 | |
|  * Calculate a sha256 value of @regs and return a value in @hash.
 | |
|  *
 | |
|  * Return:	true on success, false on error
 | |
|  */
 | |
| bool efi_hash_regions(struct image_region *regs, int count,
 | |
| 		      void **hash, const char *hash_algo, int *len)
 | |
| {
 | |
| 	int ret, hash_len;
 | |
| 
 | |
| 	if (!hash_algo)
 | |
| 		return false;
 | |
| 
 | |
| 	hash_len = algo_to_len(hash_algo);
 | |
| 	if (!hash_len)
 | |
| 		return false;
 | |
| 
 | |
| 	if (!*hash) {
 | |
| 		*hash = calloc(1, hash_len);
 | |
| 		if (!*hash) {
 | |
| 			EFI_PRINT("Out of memory\n");
 | |
| 			return false;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ret = hash_calculate(hash_algo, regs, count, *hash);
 | |
| 	if (ret)
 | |
| 		return false;
 | |
| 
 | |
| 	if (len)
 | |
| 		*len = hash_len;
 | |
| #ifdef DEBUG
 | |
| 	EFI_PRINT("hash calculated:\n");
 | |
| 	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
 | |
| 		       *hash, hash_len, false);
 | |
| #endif
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * hash_algo_supported - check if the requested hash algorithm is supported
 | |
|  * @guid: guid of the algorithm
 | |
|  *
 | |
|  * Return: true if supported false otherwise
 | |
|  */
 | |
| static bool hash_algo_supported(const efi_guid_t guid)
 | |
| {
 | |
| 	int i;
 | |
| 	const efi_guid_t unsupported_hashes[] = {
 | |
| 		 EFI_CERT_SHA1_GUID,
 | |
| 		 EFI_CERT_SHA224_GUID,
 | |
| 		 EFI_CERT_SHA384_GUID,
 | |
| 		 EFI_CERT_SHA512_GUID,
 | |
| 	};
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(unsupported_hashes); i++) {
 | |
| 		if (!guidcmp(&unsupported_hashes[i], &guid))
 | |
| 			return false;
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_signature_lookup_digest - search for an image's digest in sigdb
 | |
|  * @regs:	List of regions to be authenticated
 | |
|  * @db:		Signature database for trusted certificates
 | |
|  * @dbx		Caller needs to set this to true if he is searching dbx
 | |
|  *
 | |
|  * A message digest of image pointed to by @regs is calculated and
 | |
|  * its hash value is compared to entries in signature database pointed
 | |
|  * to by @db.
 | |
|  *
 | |
|  * Return:	true if found, false if not
 | |
|  */
 | |
| bool efi_signature_lookup_digest(struct efi_image_regions *regs,
 | |
| 				 struct efi_signature_store *db,
 | |
| 				 bool dbx)
 | |
| 
 | |
| {
 | |
| 	struct efi_signature_store *siglist;
 | |
| 	struct efi_sig_data *sig_data;
 | |
| 	void *hash = NULL;
 | |
| 	bool found = false;
 | |
| 	bool hash_done = false;
 | |
| 
 | |
| 	EFI_PRINT("%s: Enter, %p, %p\n", __func__, regs, db);
 | |
| 
 | |
| 	if (!regs || !db || !db->sig_data_list)
 | |
| 		goto out;
 | |
| 
 | |
| 	for (siglist = db; siglist; siglist = siglist->next) {
 | |
| 		int len = 0;
 | |
| 		const char *hash_algo = NULL;
 | |
| 		/*
 | |
| 		 * if the hash algorithm is unsupported and we get an entry in
 | |
| 		 * dbx reject the image
 | |
| 		 */
 | |
| 		if (dbx && !hash_algo_supported(siglist->sig_type)) {
 | |
| 			found = true;
 | |
| 			continue;
 | |
| 		};
 | |
| 		/*
 | |
| 		 * Only support sha256 for now, that's what
 | |
| 		 * hash-to-efi-sig-list produces
 | |
| 		 */
 | |
| 		if (guidcmp(&siglist->sig_type, &efi_guid_sha256))
 | |
| 			continue;
 | |
| 
 | |
| 		hash_algo = guid_to_sha_str(&efi_guid_sha256);
 | |
| 		/*
 | |
| 		 * We could check size and hash_algo but efi_hash_regions()
 | |
| 		 * will do that for us
 | |
| 		 */
 | |
| 		if (!hash_done &&
 | |
| 		    !efi_hash_regions(regs->reg, regs->num, &hash, hash_algo,
 | |
| 				      &len)) {
 | |
| 			EFI_PRINT("Digesting an image failed\n");
 | |
| 			break;
 | |
| 		}
 | |
| 		hash_done = true;
 | |
| 
 | |
| 		for (sig_data = siglist->sig_data_list; sig_data;
 | |
| 		     sig_data = sig_data->next) {
 | |
| #ifdef DEBUG
 | |
| 			EFI_PRINT("Msg digest in database:\n");
 | |
| 			print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
 | |
| 				       sig_data->data, sig_data->size, false);
 | |
| #endif
 | |
| 			if (sig_data->size == len &&
 | |
| 			    !memcmp(sig_data->data, hash, len)) {
 | |
| 				found = true;
 | |
| 				free(hash);
 | |
| 				goto out;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		free(hash);
 | |
| 		hash = NULL;
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	EFI_PRINT("%s: Exit, found: %d\n", __func__, found);
 | |
| 	return found;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_lookup_certificate - find a certificate within db
 | |
|  * @msg:	Signature
 | |
|  * @db:		Signature database
 | |
|  *
 | |
|  * Search signature database pointed to by @db and find a certificate
 | |
|  * pointed to by @cert.
 | |
|  *
 | |
|  * Return:	true if found, false otherwise.
 | |
|  */
 | |
| static bool efi_lookup_certificate(struct x509_certificate *cert,
 | |
| 				   struct efi_signature_store *db)
 | |
| {
 | |
| 	struct efi_signature_store *siglist;
 | |
| 	struct efi_sig_data *sig_data;
 | |
| 	struct image_region reg[1];
 | |
| 	void *hash = NULL, *hash_tmp = NULL;
 | |
| 	int len = 0;
 | |
| 	bool found = false;
 | |
| 	const char *hash_algo = NULL;
 | |
| 
 | |
| 	EFI_PRINT("%s: Enter, %p, %p\n", __func__, cert, db);
 | |
| 
 | |
| 	if (!cert || !db || !db->sig_data_list)
 | |
| 		goto out;
 | |
| 
 | |
| 	/*
 | |
| 	 * TODO: identify a certificate using sha256 digest
 | |
| 	 * Is there any better way?
 | |
| 	 */
 | |
| 	/* calculate hash of TBSCertificate */
 | |
| 	reg[0].data = cert->tbs;
 | |
| 	reg[0].size = cert->tbs_size;
 | |
| 
 | |
| 	/* We just need any sha256 algo to start the matching */
 | |
| 	hash_algo = guid_to_sha_str(&efi_guid_sha256);
 | |
| 	if (!efi_hash_regions(reg, 1, &hash, hash_algo, &len))
 | |
| 		goto out;
 | |
| 
 | |
| 	EFI_PRINT("%s: searching for %s\n", __func__, cert->subject);
 | |
| 	for (siglist = db; siglist; siglist = siglist->next) {
 | |
| 		/* only with x509 certificate */
 | |
| 		if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509))
 | |
| 			continue;
 | |
| 
 | |
| 		for (sig_data = siglist->sig_data_list; sig_data;
 | |
| 		     sig_data = sig_data->next) {
 | |
| 			struct x509_certificate *cert_tmp;
 | |
| 
 | |
| 			cert_tmp = x509_cert_parse(sig_data->data,
 | |
| 						   sig_data->size);
 | |
| 			if (IS_ERR_OR_NULL(cert_tmp))
 | |
| 				continue;
 | |
| 
 | |
| 			EFI_PRINT("%s: against %s\n", __func__,
 | |
| 				  cert_tmp->subject);
 | |
| 			reg[0].data = cert_tmp->tbs;
 | |
| 			reg[0].size = cert_tmp->tbs_size;
 | |
| 			if (!efi_hash_regions(reg, 1, &hash_tmp, hash_algo,
 | |
| 					      NULL))
 | |
| 				goto out;
 | |
| 
 | |
| 			x509_free_certificate(cert_tmp);
 | |
| 
 | |
| 			if (!memcmp(hash, hash_tmp, len)) {
 | |
| 				found = true;
 | |
| 				goto out;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| out:
 | |
| 	free(hash);
 | |
| 	free(hash_tmp);
 | |
| 
 | |
| 	EFI_PRINT("%s: Exit, found: %d\n", __func__, found);
 | |
| 	return found;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_verify_certificate - verify certificate's signature with database
 | |
|  * @signer:	Certificate
 | |
|  * @db:		Signature database
 | |
|  * @root:	Certificate to verify @signer
 | |
|  *
 | |
|  * Determine if certificate pointed to by @signer may be verified
 | |
|  * by one of certificates in signature database pointed to by @db.
 | |
|  *
 | |
|  * Return:	true if certificate is verified, false otherwise.
 | |
|  */
 | |
| static bool efi_verify_certificate(struct x509_certificate *signer,
 | |
| 				   struct efi_signature_store *db,
 | |
| 				   struct x509_certificate **root)
 | |
| {
 | |
| 	struct efi_signature_store *siglist;
 | |
| 	struct efi_sig_data *sig_data;
 | |
| 	struct x509_certificate *cert;
 | |
| 	bool verified = false;
 | |
| 	int ret;
 | |
| 
 | |
| 	EFI_PRINT("%s: Enter, %p, %p\n", __func__, signer, db);
 | |
| 
 | |
| 	if (!signer || !db || !db->sig_data_list)
 | |
| 		goto out;
 | |
| 
 | |
| 	for (siglist = db; siglist; siglist = siglist->next) {
 | |
| 		/* only with x509 certificate */
 | |
| 		if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509))
 | |
| 			continue;
 | |
| 
 | |
| 		for (sig_data = siglist->sig_data_list; sig_data;
 | |
| 		     sig_data = sig_data->next) {
 | |
| 			cert = x509_cert_parse(sig_data->data, sig_data->size);
 | |
| 			if (IS_ERR_OR_NULL(cert)) {
 | |
| 				EFI_PRINT("Cannot parse x509 certificate\n");
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			ret = public_key_verify_signature(cert->pub,
 | |
| 							  signer->sig);
 | |
| 			if (!ret) {
 | |
| 				verified = true;
 | |
| 				if (root)
 | |
| 					*root = cert;
 | |
| 				else
 | |
| 					x509_free_certificate(cert);
 | |
| 				goto out;
 | |
| 			}
 | |
| 			x509_free_certificate(cert);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
 | |
| 	return verified;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_signature_check_revocation - check revocation with dbx
 | |
|  * @sinfo:	Signer's info
 | |
|  * @cert:	x509 certificate
 | |
|  * @dbx:	Revocation signature database
 | |
|  *
 | |
|  * Search revocation signature database pointed to by @dbx and find
 | |
|  * an entry matching to certificate pointed to by @cert.
 | |
|  *
 | |
|  * While this entry contains revocation time, we don't support timestamp
 | |
|  * protocol at this time and any image will be unconditionally revoked
 | |
|  * when this match occurs.
 | |
|  *
 | |
|  * Return:	true if check passed (not found), false otherwise.
 | |
|  */
 | |
| static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo,
 | |
| 					   struct x509_certificate *cert,
 | |
| 					   struct efi_signature_store *dbx)
 | |
| {
 | |
| 	struct efi_signature_store *siglist;
 | |
| 	struct efi_sig_data *sig_data;
 | |
| 	struct image_region reg[1];
 | |
| 	void *hash = NULL;
 | |
| 	int len = 0;
 | |
| 	time64_t revoc_time;
 | |
| 	bool revoked = false;
 | |
| 	const char *hash_algo = NULL;
 | |
| 
 | |
| 	EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx);
 | |
| 
 | |
| 	if (!sinfo || !cert || !dbx || !dbx->sig_data_list)
 | |
| 		goto out;
 | |
| 
 | |
| 	EFI_PRINT("Checking revocation against %s\n", cert->subject);
 | |
| 	for (siglist = dbx; siglist; siglist = siglist->next) {
 | |
| 		hash_algo = guid_to_sha_str(&siglist->sig_type);
 | |
| 		if (!hash_algo)
 | |
| 			continue;
 | |
| 
 | |
| 		/* calculate hash of TBSCertificate */
 | |
| 		reg[0].data = cert->tbs;
 | |
| 		reg[0].size = cert->tbs_size;
 | |
| 		if (!efi_hash_regions(reg, 1, &hash, hash_algo, &len))
 | |
| 			goto out;
 | |
| 
 | |
| 		for (sig_data = siglist->sig_data_list; sig_data;
 | |
| 		     sig_data = sig_data->next) {
 | |
| 			/*
 | |
| 			 * struct efi_cert_x509_sha256 {
 | |
| 			 *	u8 tbs_hash[256/8];
 | |
| 			 *	time64_t revocation_time;
 | |
| 			 * };
 | |
| 			 */
 | |
| #ifdef DEBUG
 | |
| 			if (sig_data->size >= len) {
 | |
| 				EFI_PRINT("hash in db:\n");
 | |
| 				print_hex_dump("    ", DUMP_PREFIX_OFFSET,
 | |
| 					       16, 1,
 | |
| 					       sig_data->data, len, false);
 | |
| 			}
 | |
| #endif
 | |
| 			if ((sig_data->size < len + sizeof(time64_t)) ||
 | |
| 			    memcmp(sig_data->data, hash, len))
 | |
| 				continue;
 | |
| 
 | |
| 			memcpy(&revoc_time, sig_data->data + len,
 | |
| 			       sizeof(revoc_time));
 | |
| 			EFI_PRINT("revocation time: 0x%llx\n", revoc_time);
 | |
| 			/*
 | |
| 			 * TODO: compare signing timestamp in sinfo
 | |
| 			 * with revocation time
 | |
| 			 */
 | |
| 
 | |
| 			revoked = true;
 | |
| 			free(hash);
 | |
| 			goto out;
 | |
| 		}
 | |
| 		free(hash);
 | |
| 		hash = NULL;
 | |
| 	}
 | |
| out:
 | |
| 	EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked);
 | |
| 	return !revoked;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * efi_signature_verify - verify signatures with db and dbx
 | |
|  * @regs:	List of regions to be authenticated
 | |
|  * @msg:	Signature
 | |
|  * @db:		Signature database for trusted certificates
 | |
|  * @dbx:	Revocation signature database
 | |
|  *
 | |
|  * All the signature pointed to by @msg against image pointed to by @regs
 | |
|  * will be verified by signature database pointed to by @db and @dbx.
 | |
|  *
 | |
|  * Return:	true if verification for all signatures passed, false otherwise
 | |
|  */
 | |
| bool efi_signature_verify(struct efi_image_regions *regs,
 | |
| 			  struct pkcs7_message *msg,
 | |
| 			  struct efi_signature_store *db,
 | |
| 			  struct efi_signature_store *dbx)
 | |
| {
 | |
| 	struct pkcs7_signed_info *sinfo;
 | |
| 	struct x509_certificate *signer, *root;
 | |
| 	bool verified = false;
 | |
| 	int ret;
 | |
| 
 | |
| 	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx);
 | |
| 
 | |
| 	if (!regs || !msg || !db || !db->sig_data_list)
 | |
| 		goto out;
 | |
| 
 | |
| 	for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
 | |
| 		EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
 | |
| 			  sinfo->sig->hash_algo, sinfo->sig->pkey_algo);
 | |
| 
 | |
| 		/*
 | |
| 		 * only for authenticated variable.
 | |
| 		 *
 | |
| 		 * If this function is called for image,
 | |
| 		 * hash calculation will be done in
 | |
| 		 * pkcs7_verify_one().
 | |
| 		 */
 | |
| 		if (!msg->data &&
 | |
| 		    !efi_hash_regions(regs->reg, regs->num,
 | |
| 				      (void **)&sinfo->sig->digest,
 | |
| 				      guid_to_sha_str(&efi_guid_sha256),
 | |
| 				      NULL)) {
 | |
| 			EFI_PRINT("Digesting an image failed\n");
 | |
| 			goto out;
 | |
| 		}
 | |
| 
 | |
| 		EFI_PRINT("Verifying certificate chain\n");
 | |
| 		signer = NULL;
 | |
| 		ret = pkcs7_verify_one(msg, sinfo, &signer);
 | |
| 		if (ret == -ENOPKG)
 | |
| 			continue;
 | |
| 
 | |
| 		if (ret < 0 || !signer)
 | |
| 			goto out;
 | |
| 
 | |
| 		if (sinfo->blacklisted)
 | |
| 			goto out;
 | |
| 
 | |
| 		EFI_PRINT("Verifying last certificate in chain\n");
 | |
| 		if (efi_lookup_certificate(signer, db))
 | |
| 			if (efi_signature_check_revocation(sinfo, signer, dbx))
 | |
| 				break;
 | |
| 		if (!signer->self_signed &&
 | |
| 		    efi_verify_certificate(signer, db, &root)) {
 | |
| 			bool check;
 | |
| 
 | |
| 			check = efi_signature_check_revocation(sinfo, root,
 | |
| 							       dbx);
 | |
| 			x509_free_certificate(root);
 | |
| 			if (check)
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		EFI_PRINT("Certificate chain didn't reach trusted CA\n");
 | |
| 	}
 | |
| 	if (sinfo)
 | |
| 		verified = true;
 | |
| out:
 | |
| 	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
 | |
| 	return verified;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_signature_check_signers - check revocation against all signers with dbx
 | |
|  * @msg:	Signature
 | |
|  * @dbx:	Revocation signature database
 | |
|  *
 | |
|  * Determine if none of signers' certificates in @msg are revoked
 | |
|  * by signature database pointed to by @dbx.
 | |
|  *
 | |
|  * Return:	true if all signers passed, false otherwise.
 | |
|  */
 | |
| bool efi_signature_check_signers(struct pkcs7_message *msg,
 | |
| 				 struct efi_signature_store *dbx)
 | |
| {
 | |
| 	struct pkcs7_signed_info *sinfo;
 | |
| 	bool revoked = false;
 | |
| 
 | |
| 	EFI_PRINT("%s: Enter, %p, %p\n", __func__, msg, dbx);
 | |
| 
 | |
| 	if (!msg || !dbx)
 | |
| 		goto out;
 | |
| 
 | |
| 	for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
 | |
| 		if (sinfo->signer &&
 | |
| 		    !efi_signature_check_revocation(sinfo, sinfo->signer,
 | |
| 						    dbx)) {
 | |
| 			revoked = true;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| out:
 | |
| 	EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked);
 | |
| 	return !revoked;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_sigstore_free - free signature store
 | |
|  * @sigstore:	Pointer to signature store structure
 | |
|  *
 | |
|  * Feee all the memories held in signature store and itself,
 | |
|  * which were allocated by efi_sigstore_parse_sigdb().
 | |
|  */
 | |
| void efi_sigstore_free(struct efi_signature_store *sigstore)
 | |
| {
 | |
| 	struct efi_signature_store *sigstore_next;
 | |
| 	struct efi_sig_data *sig_data, *sig_data_next;
 | |
| 
 | |
| 	while (sigstore) {
 | |
| 		sigstore_next = sigstore->next;
 | |
| 
 | |
| 		sig_data = sigstore->sig_data_list;
 | |
| 		while (sig_data) {
 | |
| 			sig_data_next = sig_data->next;
 | |
| 			free(sig_data->data);
 | |
| 			free(sig_data);
 | |
| 			sig_data = sig_data_next;
 | |
| 		}
 | |
| 
 | |
| 		free(sigstore);
 | |
| 		sigstore = sigstore_next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_sigstore_parse_siglist - parse a signature list
 | |
|  * @name:	Pointer to signature list
 | |
|  *
 | |
|  * Parse signature list and instantiate a signature store structure.
 | |
|  * Signature database is a simple concatenation of one or more
 | |
|  * signature list(s).
 | |
|  *
 | |
|  * Return:	Pointer to signature store on success, NULL on error
 | |
|  */
 | |
| static struct efi_signature_store *
 | |
| efi_sigstore_parse_siglist(struct efi_signature_list *esl)
 | |
| {
 | |
| 	struct efi_signature_store *siglist = NULL;
 | |
| 	struct efi_sig_data *sig_data, *sig_data_next;
 | |
| 	struct efi_signature_data *esd;
 | |
| 	size_t left;
 | |
| 
 | |
| 	/*
 | |
| 	 * UEFI specification defines certificate types:
 | |
| 	 *   for non-signed images,
 | |
| 	 *	EFI_CERT_SHA256_GUID
 | |
| 	 *	EFI_CERT_RSA2048_GUID
 | |
| 	 *	EFI_CERT_RSA2048_SHA256_GUID
 | |
| 	 *	EFI_CERT_SHA1_GUID
 | |
| 	 *	EFI_CERT_RSA2048_SHA_GUID
 | |
| 	 *	EFI_CERT_SHA224_GUID
 | |
| 	 *	EFI_CERT_SHA384_GUID
 | |
| 	 *	EFI_CERT_SHA512_GUID
 | |
| 	 *
 | |
| 	 *   for signed images,
 | |
| 	 *	EFI_CERT_X509_GUID
 | |
| 	 *	NOTE: Each certificate will normally be in a separate
 | |
| 	 *	EFI_SIGNATURE_LIST as the size may vary depending on
 | |
| 	 *	its algo's.
 | |
| 	 *
 | |
| 	 *   for timestamp revocation of certificate,
 | |
| 	 *	EFI_CERT_X509_SHA512_GUID
 | |
| 	 *	EFI_CERT_X509_SHA256_GUID
 | |
| 	 *	EFI_CERT_X509_SHA384_GUID
 | |
| 	 */
 | |
| 
 | |
| 	if (esl->signature_list_size
 | |
| 			<= (sizeof(*esl) + esl->signature_header_size)) {
 | |
| 		EFI_PRINT("Siglist in wrong format\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* Create a head */
 | |
| 	siglist = calloc(sizeof(*siglist), 1);
 | |
| 	if (!siglist) {
 | |
| 		EFI_PRINT("Out of memory\n");
 | |
| 		goto err;
 | |
| 	}
 | |
| 	memcpy(&siglist->sig_type, &esl->signature_type, sizeof(efi_guid_t));
 | |
| 
 | |
| 	/* Go through the list */
 | |
| 	sig_data_next = NULL;
 | |
| 	left = esl->signature_list_size
 | |
| 			- (sizeof(*esl) + esl->signature_header_size);
 | |
| 	esd = (struct efi_signature_data *)
 | |
| 			((u8 *)esl + sizeof(*esl) + esl->signature_header_size);
 | |
| 
 | |
| 	while (left > 0) {
 | |
| 		/* Signature must exist if there is remaining data. */
 | |
| 		if (left < esl->signature_size) {
 | |
| 			EFI_PRINT("Certificate is too small\n");
 | |
| 			goto err;
 | |
| 		}
 | |
| 
 | |
| 		sig_data = calloc(esl->signature_size
 | |
| 					- sizeof(esd->signature_owner), 1);
 | |
| 		if (!sig_data) {
 | |
| 			EFI_PRINT("Out of memory\n");
 | |
| 			goto err;
 | |
| 		}
 | |
| 
 | |
| 		/* Append signature data */
 | |
| 		memcpy(&sig_data->owner, &esd->signature_owner,
 | |
| 		       sizeof(efi_guid_t));
 | |
| 		sig_data->size = esl->signature_size
 | |
| 					- sizeof(esd->signature_owner);
 | |
| 		sig_data->data = malloc(sig_data->size);
 | |
| 		if (!sig_data->data) {
 | |
| 			EFI_PRINT("Out of memory\n");
 | |
| 			goto err;
 | |
| 		}
 | |
| 		memcpy(sig_data->data, esd->signature_data, sig_data->size);
 | |
| 
 | |
| 		sig_data->next = sig_data_next;
 | |
| 		sig_data_next = sig_data;
 | |
| 
 | |
| 		/* Next */
 | |
| 		esd = (struct efi_signature_data *)
 | |
| 				((u8 *)esd + esl->signature_size);
 | |
| 		left -= esl->signature_size;
 | |
| 	}
 | |
| 	siglist->sig_data_list = sig_data_next;
 | |
| 
 | |
| 	return siglist;
 | |
| 
 | |
| err:
 | |
| 	efi_sigstore_free(siglist);
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_sigstore_parse_sigdb - parse the signature list and populate
 | |
|  * the signature store
 | |
|  *
 | |
|  * @sig_list:	Pointer to the signature list
 | |
|  * @size:	Size of the signature list
 | |
|  *
 | |
|  * Parse the efi signature list and instantiate a signature store
 | |
|  * structure.
 | |
|  *
 | |
|  * Return:	Pointer to signature store on success, NULL on error
 | |
|  */
 | |
| struct efi_signature_store *efi_build_signature_store(void *sig_list,
 | |
| 						      efi_uintn_t size)
 | |
| {
 | |
| 	struct efi_signature_list *esl;
 | |
| 	struct efi_signature_store *sigstore = NULL, *siglist;
 | |
| 
 | |
| 	esl = sig_list;
 | |
| 	while (size > 0) {
 | |
| 		/* List must exist if there is remaining data. */
 | |
| 		if (size < sizeof(*esl)) {
 | |
| 			EFI_PRINT("Signature list in wrong format\n");
 | |
| 			goto err;
 | |
| 		}
 | |
| 
 | |
| 		if (size < esl->signature_list_size) {
 | |
| 			EFI_PRINT("Signature list in wrong format\n");
 | |
| 			goto err;
 | |
| 		}
 | |
| 
 | |
| 		/* Parse a single siglist. */
 | |
| 		siglist = efi_sigstore_parse_siglist(esl);
 | |
| 		if (!siglist) {
 | |
| 			EFI_PRINT("Parsing of signature list of failed\n");
 | |
| 			goto err;
 | |
| 		}
 | |
| 
 | |
| 		/* Append siglist */
 | |
| 		siglist->next = sigstore;
 | |
| 		sigstore = siglist;
 | |
| 
 | |
| 		/* Next */
 | |
| 		size -= esl->signature_list_size;
 | |
| 		esl = (void *)esl + esl->signature_list_size;
 | |
| 	}
 | |
| 	free(sig_list);
 | |
| 
 | |
| 	return sigstore;
 | |
| 
 | |
| err:
 | |
| 	efi_sigstore_free(sigstore);
 | |
| 	free(sig_list);
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * efi_sigstore_parse_sigdb - parse a signature database variable
 | |
|  * @name:	Variable's name
 | |
|  *
 | |
|  * Read in a value of signature database variable pointed to by
 | |
|  * @name, parse it and instantiate a signature store structure.
 | |
|  *
 | |
|  * Return:	Pointer to signature store on success, NULL on error
 | |
|  */
 | |
| struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
 | |
| {
 | |
| 	const efi_guid_t *vendor;
 | |
| 	void *db;
 | |
| 	efi_uintn_t db_size;
 | |
| 
 | |
| 	vendor = efi_auth_var_get_guid(name);
 | |
| 	db = efi_get_var(name, vendor, &db_size);
 | |
| 	if (!db) {
 | |
| 		EFI_PRINT("variable, %ls, not found\n", name);
 | |
| 		return calloc(sizeof(struct efi_signature_store), 1);
 | |
| 	}
 | |
| 
 | |
| 	return efi_build_signature_store(db, db_size);
 | |
| }
 |