efi_loader: image_loader: support image authentication
With this commit, image validation can be enforced, as UEFI specification
section 32.5 describes, if CONFIG_EFI_SECURE_BOOT is enabled.
Currently we support
* authentication based on db and dbx,
  so dbx-validated image will always be rejected.
* following signature types:
    EFI_CERT_SHA256_GUID (SHA256 digest for unsigned images)
    EFI_CERT_X509_GUID (x509 certificate for signed images)
Timestamp-based certificate revocation is not supported here.
Internally, authentication data is stored in one of certificates tables
of PE image (See efi_image_parse()) and will be verified by
efi_image_authenticate() before loading a given image.
It seems that UEFI specification defines the verification process
in a bit ambiguous way. I tried to implement it as closely to as
EDK2 does.
Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
			
			
This commit is contained in:
		
							parent
							
								
									8353516f4a
								
							
						
					
					
						commit
						4540dabdca
					
				|  | @ -11,6 +11,7 @@ | ||||||
| #include <common.h> | #include <common.h> | ||||||
| #include <part_efi.h> | #include <part_efi.h> | ||||||
| #include <efi_api.h> | #include <efi_api.h> | ||||||
|  | #include <pe.h> | ||||||
| 
 | 
 | ||||||
| static inline int guidcmp(const void *g1, const void *g2) | static inline int guidcmp(const void *g1, const void *g2) | ||||||
| { | { | ||||||
|  | @ -263,6 +264,11 @@ struct efi_object { | ||||||
| 	enum efi_object_type type; | 	enum efi_object_type type; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | enum efi_image_auth_status { | ||||||
|  | 	EFI_IMAGE_AUTH_FAILED = 0, | ||||||
|  | 	EFI_IMAGE_AUTH_PASSED, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * struct efi_loaded_image_obj - handle of a loaded image |  * struct efi_loaded_image_obj - handle of a loaded image | ||||||
|  * |  * | ||||||
|  | @ -282,6 +288,7 @@ struct efi_loaded_image_obj { | ||||||
| 	EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, | 	EFIAPI efi_status_t (*entry)(efi_handle_t image_handle, | ||||||
| 				     struct efi_system_table *st); | 				     struct efi_system_table *st); | ||||||
| 	u16 image_type; | 	u16 image_type; | ||||||
|  | 	enum efi_image_auth_status auth_status; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  | @ -415,7 +422,8 @@ efi_status_t efi_set_watchdog(unsigned long timeout); | ||||||
| /* Called from places to check whether a timer expired */ | /* Called from places to check whether a timer expired */ | ||||||
| void efi_timer_check(void); | void efi_timer_check(void); | ||||||
| /* PE loader implementation */ | /* PE loader implementation */ | ||||||
| efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, | ||||||
|  | 			 void *efi, size_t efi_size, | ||||||
| 			 struct efi_loaded_image *loaded_image_info); | 			 struct efi_loaded_image *loaded_image_info); | ||||||
| /* Called once to store the pristine gd pointer */ | /* Called once to store the pristine gd pointer */ | ||||||
| void efi_save_gd(void); | void efi_save_gd(void); | ||||||
|  | @ -756,6 +764,9 @@ void efi_sigstore_free(struct efi_signature_store *sigstore); | ||||||
| struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name); | struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name); | ||||||
| 
 | 
 | ||||||
| bool efi_secure_boot_enabled(void); | bool efi_secure_boot_enabled(void); | ||||||
|  | 
 | ||||||
|  | bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, | ||||||
|  | 		     WIN_CERTIFICATE **auth, size_t *auth_len); | ||||||
| #endif /* CONFIG_EFI_SECURE_BOOT */ | #endif /* CONFIG_EFI_SECURE_BOOT */ | ||||||
| 
 | 
 | ||||||
| #else /* CONFIG_IS_ENABLED(EFI_LOADER) */ | #else /* CONFIG_IS_ENABLED(EFI_LOADER) */ | ||||||
|  |  | ||||||
|  | @ -1882,12 +1882,12 @@ efi_status_t EFIAPI efi_load_image(bool boot_policy, | ||||||
| 	efi_dp_split_file_path(file_path, &dp, &fp); | 	efi_dp_split_file_path(file_path, &dp, &fp); | ||||||
| 	ret = efi_setup_loaded_image(dp, fp, image_obj, &info); | 	ret = efi_setup_loaded_image(dp, fp, image_obj, &info); | ||||||
| 	if (ret == EFI_SUCCESS) | 	if (ret == EFI_SUCCESS) | ||||||
| 		ret = efi_load_pe(*image_obj, dest_buffer, info); | 		ret = efi_load_pe(*image_obj, dest_buffer, source_size, info); | ||||||
| 	if (!source_buffer) | 	if (!source_buffer) | ||||||
| 		/* Release buffer to which file was loaded */ | 		/* Release buffer to which file was loaded */ | ||||||
| 		efi_free_pages((uintptr_t)dest_buffer, | 		efi_free_pages((uintptr_t)dest_buffer, | ||||||
| 			       efi_size_in_pages(source_size)); | 			       efi_size_in_pages(source_size)); | ||||||
| 	if (ret == EFI_SUCCESS) { | 	if (ret == EFI_SUCCESS || ret == EFI_SECURITY_VIOLATION) { | ||||||
| 		info->system_table = &systab; | 		info->system_table = &systab; | ||||||
| 		info->parent_handle = parent_image; | 		info->parent_handle = parent_image; | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -2885,10 +2885,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, | ||||||
| 
 | 
 | ||||||
| 	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); | 	EFI_ENTRY("%p, %p, %p", image_handle, exit_data_size, exit_data); | ||||||
| 
 | 
 | ||||||
|  | 	if (!efi_search_obj(image_handle)) | ||||||
|  | 		return EFI_EXIT(EFI_INVALID_PARAMETER); | ||||||
|  | 
 | ||||||
| 	/* Check parameters */ | 	/* Check parameters */ | ||||||
| 	if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE) | 	if (image_obj->header.type != EFI_OBJECT_TYPE_LOADED_IMAGE) | ||||||
| 		return EFI_EXIT(EFI_INVALID_PARAMETER); | 		return EFI_EXIT(EFI_INVALID_PARAMETER); | ||||||
| 
 | 
 | ||||||
|  | 	if (image_obj->auth_status != EFI_IMAGE_AUTH_PASSED) | ||||||
|  | 		return EFI_EXIT(EFI_SECURITY_VIOLATION); | ||||||
|  | 
 | ||||||
| 	ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, | 	ret = EFI_CALL(efi_open_protocol(image_handle, &efi_guid_loaded_image, | ||||||
| 					 &info, NULL, NULL, | 					 &info, NULL, NULL, | ||||||
| 					 EFI_OPEN_PROTOCOL_GET_PROTOCOL)); | 					 EFI_OPEN_PROTOCOL_GET_PROTOCOL)); | ||||||
|  |  | ||||||
|  | @ -10,7 +10,10 @@ | ||||||
| #include <common.h> | #include <common.h> | ||||||
| #include <cpu_func.h> | #include <cpu_func.h> | ||||||
| #include <efi_loader.h> | #include <efi_loader.h> | ||||||
|  | #include <malloc.h> | ||||||
| #include <pe.h> | #include <pe.h> | ||||||
|  | #include <sort.h> | ||||||
|  | #include "../lib/crypto/pkcs7_parser.h" | ||||||
| 
 | 
 | ||||||
| const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; | const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; | ||||||
| const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; | const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; | ||||||
|  | @ -206,6 +209,386 @@ static void efi_set_code_and_data_type( | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #ifdef CONFIG_EFI_SECURE_BOOT | ||||||
|  | /**
 | ||||||
|  |  * cmp_pe_section - compare two sections | ||||||
|  |  * @arg1:	Pointer to pointer to first section | ||||||
|  |  * @arg2:	Pointer to pointer to second section | ||||||
|  |  * | ||||||
|  |  * Compare two sections in PE image. | ||||||
|  |  * | ||||||
|  |  * Return:	-1, 0, 1 respectively if arg1 < arg2, arg1 == arg2 or | ||||||
|  |  *		arg1 > arg2 | ||||||
|  |  */ | ||||||
|  | static int cmp_pe_section(const void *arg1, const void *arg2) | ||||||
|  | { | ||||||
|  | 	const IMAGE_SECTION_HEADER *section1, *section2; | ||||||
|  | 
 | ||||||
|  | 	section1 = *((const IMAGE_SECTION_HEADER **)arg1); | ||||||
|  | 	section2 = *((const IMAGE_SECTION_HEADER **)arg2); | ||||||
|  | 
 | ||||||
|  | 	if (section1->VirtualAddress < section2->VirtualAddress) | ||||||
|  | 		return -1; | ||||||
|  | 	else if (section1->VirtualAddress == section2->VirtualAddress) | ||||||
|  | 		return 0; | ||||||
|  | 	else | ||||||
|  | 		return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * efi_image_parse - parse a PE image | ||||||
|  |  * @efi:	Pointer to image | ||||||
|  |  * @len:	Size of @efi | ||||||
|  |  * @regp:	Pointer to a list of regions | ||||||
|  |  * @auth:	Pointer to a pointer to authentication data in PE | ||||||
|  |  * @auth_len:	Size of @auth | ||||||
|  |  * | ||||||
|  |  * Parse image binary in PE32(+) format, assuming that sanity of PE image | ||||||
|  |  * has been checked by a caller. | ||||||
|  |  * On success, an address of authentication data in @efi and its size will | ||||||
|  |  * be returned in @auth and @auth_len, respectively. | ||||||
|  |  * | ||||||
|  |  * Return:	true on success, false on error | ||||||
|  |  */ | ||||||
|  | bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, | ||||||
|  | 		     WIN_CERTIFICATE **auth, size_t *auth_len) | ||||||
|  | { | ||||||
|  | 	struct efi_image_regions *regs; | ||||||
|  | 	IMAGE_DOS_HEADER *dos; | ||||||
|  | 	IMAGE_NT_HEADERS32 *nt; | ||||||
|  | 	IMAGE_SECTION_HEADER *sections, **sorted; | ||||||
|  | 	int num_regions, num_sections, i; | ||||||
|  | 	int ctidx = IMAGE_DIRECTORY_ENTRY_SECURITY; | ||||||
|  | 	u32 align, size, authsz, authoff; | ||||||
|  | 	size_t bytes_hashed; | ||||||
|  | 
 | ||||||
|  | 	dos = (void *)efi; | ||||||
|  | 	nt = (void *)(efi + dos->e_lfanew); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Count maximum number of regions to be digested. | ||||||
|  | 	 * We don't have to have an exact number here. | ||||||
|  | 	 * See efi_image_region_add()'s in parsing below. | ||||||
|  | 	 */ | ||||||
|  | 	num_regions = 3; /* for header */ | ||||||
|  | 	num_regions += nt->FileHeader.NumberOfSections; | ||||||
|  | 	num_regions++; /* for extra */ | ||||||
|  | 
 | ||||||
|  | 	regs = calloc(sizeof(*regs) + sizeof(struct image_region) * num_regions, | ||||||
|  | 		      1); | ||||||
|  | 	if (!regs) | ||||||
|  | 		goto err; | ||||||
|  | 	regs->max = num_regions; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Collect data regions for hash calculation | ||||||
|  | 	 * 1. File headers | ||||||
|  | 	 */ | ||||||
|  | 	if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | ||||||
|  | 		IMAGE_NT_HEADERS64 *nt64 = (void *)nt; | ||||||
|  | 		IMAGE_OPTIONAL_HEADER64 *opt = &nt64->OptionalHeader; | ||||||
|  | 
 | ||||||
|  | 		/* Skip CheckSum */ | ||||||
|  | 		efi_image_region_add(regs, efi, &opt->CheckSum, 0); | ||||||
|  | 		if (nt64->OptionalHeader.NumberOfRvaAndSizes <= ctidx) { | ||||||
|  | 			efi_image_region_add(regs, | ||||||
|  | 					     &opt->CheckSum + 1, | ||||||
|  | 					     efi + opt->SizeOfHeaders, 0); | ||||||
|  | 		} else { | ||||||
|  | 			/* Skip Certificates Table */ | ||||||
|  | 			efi_image_region_add(regs, | ||||||
|  | 					     &opt->CheckSum + 1, | ||||||
|  | 					     &opt->DataDirectory[ctidx], 0); | ||||||
|  | 			efi_image_region_add(regs, | ||||||
|  | 					     &opt->DataDirectory[ctidx] + 1, | ||||||
|  | 					     efi + opt->SizeOfHeaders, 0); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		bytes_hashed = opt->SizeOfHeaders; | ||||||
|  | 		align = opt->FileAlignment; | ||||||
|  | 		authoff = opt->DataDirectory[ctidx].VirtualAddress; | ||||||
|  | 		authsz = opt->DataDirectory[ctidx].Size; | ||||||
|  | 	} else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | ||||||
|  | 		IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; | ||||||
|  | 
 | ||||||
|  | 		efi_image_region_add(regs, efi, &opt->CheckSum, 0); | ||||||
|  | 		efi_image_region_add(regs, &opt->CheckSum + 1, | ||||||
|  | 				     &opt->DataDirectory[ctidx], 0); | ||||||
|  | 		efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1, | ||||||
|  | 				     efi + opt->SizeOfHeaders, 0); | ||||||
|  | 
 | ||||||
|  | 		bytes_hashed = opt->SizeOfHeaders; | ||||||
|  | 		align = opt->FileAlignment; | ||||||
|  | 		authoff = opt->DataDirectory[ctidx].VirtualAddress; | ||||||
|  | 		authsz = opt->DataDirectory[ctidx].Size; | ||||||
|  | 	} else { | ||||||
|  | 		debug("%s: Invalid optional header magic %x\n", __func__, | ||||||
|  | 		      nt->OptionalHeader.Magic); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* 2. Sections */ | ||||||
|  | 	num_sections = nt->FileHeader.NumberOfSections; | ||||||
|  | 	sections = (void *)((uint8_t *)&nt->OptionalHeader + | ||||||
|  | 			    nt->FileHeader.SizeOfOptionalHeader); | ||||||
|  | 	sorted = calloc(sizeof(IMAGE_SECTION_HEADER *), num_sections); | ||||||
|  | 	if (!sorted) { | ||||||
|  | 		debug("%s: Out of memory\n", __func__); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Make sure the section list is in ascending order. | ||||||
|  | 	 */ | ||||||
|  | 	for (i = 0; i < num_sections; i++) | ||||||
|  | 		sorted[i] = §ions[i]; | ||||||
|  | 	qsort(sorted, num_sections, sizeof(sorted[0]), cmp_pe_section); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < num_sections; i++) { | ||||||
|  | 		if (!sorted[i]->SizeOfRawData) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		size = (sorted[i]->SizeOfRawData + align - 1) & ~(align - 1); | ||||||
|  | 		efi_image_region_add(regs, efi + sorted[i]->PointerToRawData, | ||||||
|  | 				     efi + sorted[i]->PointerToRawData + size, | ||||||
|  | 				     0); | ||||||
|  | 		debug("section[%d](%s): raw: 0x%x-0x%x, virt: %x-%x\n", | ||||||
|  | 		      i, sorted[i]->Name, | ||||||
|  | 		      sorted[i]->PointerToRawData, | ||||||
|  | 		      sorted[i]->PointerToRawData + size, | ||||||
|  | 		      sorted[i]->VirtualAddress, | ||||||
|  | 		      sorted[i]->VirtualAddress | ||||||
|  | 			+ sorted[i]->Misc.VirtualSize); | ||||||
|  | 
 | ||||||
|  | 		bytes_hashed += size; | ||||||
|  | 	} | ||||||
|  | 	free(sorted); | ||||||
|  | 
 | ||||||
|  | 	/* 3. Extra data excluding Certificates Table */ | ||||||
|  | 	if (bytes_hashed + authsz < len) { | ||||||
|  | 		debug("extra data for hash: %lu\n", | ||||||
|  | 		      len - (bytes_hashed + authsz)); | ||||||
|  | 		efi_image_region_add(regs, efi + bytes_hashed, | ||||||
|  | 				     efi + len - authsz, 0); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Return Certificates Table */ | ||||||
|  | 	if (authsz) { | ||||||
|  | 		if (len < authoff + authsz) { | ||||||
|  | 			debug("%s: Size for auth too large: %u >= %zu\n", | ||||||
|  | 			      __func__, authsz, len - authoff); | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 		if (authsz < sizeof(*auth)) { | ||||||
|  | 			debug("%s: Size for auth too small: %u < %zu\n", | ||||||
|  | 			      __func__, authsz, sizeof(*auth)); | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 		*auth = efi + authoff; | ||||||
|  | 		*auth_len = authsz; | ||||||
|  | 		debug("WIN_CERTIFICATE: 0x%x, size: 0x%x\n", authoff, authsz); | ||||||
|  | 	} else { | ||||||
|  | 		*auth = NULL; | ||||||
|  | 		*auth_len = 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	*regp = regs; | ||||||
|  | 
 | ||||||
|  | 	return true; | ||||||
|  | 
 | ||||||
|  | err: | ||||||
|  | 	free(regs); | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * efi_image_unsigned_authenticate - authenticate unsigned image with | ||||||
|  |  * SHA256 hash | ||||||
|  |  * @regs:	List of regions to be verified | ||||||
|  |  * | ||||||
|  |  * If an image is not signed, it doesn't have a signature. In this case, | ||||||
|  |  * its message digest is calculated and it will be compared with one of | ||||||
|  |  * hash values stored in signature databases. | ||||||
|  |  * | ||||||
|  |  * Return:	true if authenticated, false if not | ||||||
|  |  */ | ||||||
|  | static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs) | ||||||
|  | { | ||||||
|  | 	struct efi_signature_store *db = NULL, *dbx = NULL; | ||||||
|  | 	bool ret = false; | ||||||
|  | 
 | ||||||
|  | 	dbx = efi_sigstore_parse_sigdb(L"dbx"); | ||||||
|  | 	if (!dbx) { | ||||||
|  | 		debug("Getting signature database(dbx) failed\n"); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	db = efi_sigstore_parse_sigdb(L"db"); | ||||||
|  | 	if (!db) { | ||||||
|  | 		debug("Getting signature database(db) failed\n"); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* try black-list first */ | ||||||
|  | 	if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) { | ||||||
|  | 		debug("Image is not signed and rejected by \"dbx\"\n"); | ||||||
|  | 		goto out; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* try white-list */ | ||||||
|  | 	if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL)) | ||||||
|  | 		ret = true; | ||||||
|  | 	else | ||||||
|  | 		debug("Image is not signed and not found in \"db\" or \"dbx\"\n"); | ||||||
|  | 
 | ||||||
|  | out: | ||||||
|  | 	efi_sigstore_free(db); | ||||||
|  | 	efi_sigstore_free(dbx); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * efi_image_authenticate - verify a signature of signed image | ||||||
|  |  * @efi:	Pointer to image | ||||||
|  |  * @efi_size:	Size of @efi | ||||||
|  |  * | ||||||
|  |  * A signed image should have its signature stored in a table of its PE header. | ||||||
|  |  * So if an image is signed and only if if its signature is verified using | ||||||
|  |  * signature databases, an image is authenticated. | ||||||
|  |  * If an image is not signed, its validity is checked by using | ||||||
|  |  * efi_image_unsigned_authenticated(). | ||||||
|  |  * TODO: | ||||||
|  |  * When AuditMode==0, if the image's signature is not found in | ||||||
|  |  * the authorized database, or is found in the forbidden database, | ||||||
|  |  * the image will not be started and instead, information about it | ||||||
|  |  * will be placed in this table. | ||||||
|  |  * When AuditMode==1, an EFI_IMAGE_EXECUTION_INFO element is created | ||||||
|  |  * in the EFI_IMAGE_EXECUTION_INFO_TABLE for every certificate found | ||||||
|  |  * in the certificate table of every image that is validated. | ||||||
|  |  * | ||||||
|  |  * Return:	true if authenticated, false if not | ||||||
|  |  */ | ||||||
|  | static bool efi_image_authenticate(void *efi, size_t efi_size) | ||||||
|  | { | ||||||
|  | 	struct efi_image_regions *regs = NULL; | ||||||
|  | 	WIN_CERTIFICATE *wincerts = NULL, *wincert; | ||||||
|  | 	size_t wincerts_len; | ||||||
|  | 	struct pkcs7_message *msg = NULL; | ||||||
|  | 	struct efi_signature_store *db = NULL, *dbx = NULL; | ||||||
|  | 	struct x509_certificate *cert = NULL; | ||||||
|  | 	void *new_efi = NULL; | ||||||
|  | 	size_t new_efi_size; | ||||||
|  | 	bool ret = false; | ||||||
|  | 
 | ||||||
|  | 	if (!efi_secure_boot_enabled()) | ||||||
|  | 		return true; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Size must be 8-byte aligned and the trailing bytes must be | ||||||
|  | 	 * zero'ed. Otherwise hash value may be incorrect. | ||||||
|  | 	 */ | ||||||
|  | 	if (efi_size & 0x7) { | ||||||
|  | 		new_efi_size = (efi_size + 0x7) & ~0x7ULL; | ||||||
|  | 		new_efi = calloc(new_efi_size, 1); | ||||||
|  | 		if (!new_efi) | ||||||
|  | 			return false; | ||||||
|  | 		memcpy(new_efi, efi, efi_size); | ||||||
|  | 		efi = new_efi; | ||||||
|  | 		efi_size = new_efi_size; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!efi_image_parse(efi, efi_size, ®s, &wincerts, | ||||||
|  | 			     &wincerts_len)) { | ||||||
|  | 		debug("Parsing PE executable image failed\n"); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!wincerts) { | ||||||
|  | 		/* The image is not signed */ | ||||||
|  | 		ret = efi_image_unsigned_authenticate(regs); | ||||||
|  | 
 | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * verify signature using db and dbx | ||||||
|  | 	 */ | ||||||
|  | 	db = efi_sigstore_parse_sigdb(L"db"); | ||||||
|  | 	if (!db) { | ||||||
|  | 		debug("Getting signature database(db) failed\n"); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dbx = efi_sigstore_parse_sigdb(L"dbx"); | ||||||
|  | 	if (!dbx) { | ||||||
|  | 		debug("Getting signature database(dbx) failed\n"); | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* go through WIN_CERTIFICATE list */ | ||||||
|  | 	for (wincert = wincerts; | ||||||
|  | 	     (void *)wincert < (void *)wincerts + wincerts_len; | ||||||
|  | 	     wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) { | ||||||
|  | 		if (wincert->dwLength < sizeof(*wincert)) { | ||||||
|  | 			debug("%s: dwLength too small: %u < %zu\n", | ||||||
|  | 			      __func__, wincert->dwLength, sizeof(*wincert)); | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 		msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert), | ||||||
|  | 					  wincert->dwLength - sizeof(*wincert)); | ||||||
|  | 		if (!msg) { | ||||||
|  | 			debug("Parsing image's signature failed\n"); | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* try black-list first */ | ||||||
|  | 		if (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) { | ||||||
|  | 			debug("Signature was rejected by \"dbx\"\n"); | ||||||
|  | 			goto err; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!efi_signature_verify_signers(msg, dbx)) { | ||||||
|  | 			debug("Signer was rejected by \"dbx\"\n"); | ||||||
|  | 			goto err; | ||||||
|  | 		} else { | ||||||
|  | 			ret = true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* try white-list */ | ||||||
|  | 		if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) { | ||||||
|  | 			debug("Verifying signature with \"db\" failed\n"); | ||||||
|  | 			goto err; | ||||||
|  | 		} else { | ||||||
|  | 			ret = true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!efi_signature_verify_cert(cert, dbx)) { | ||||||
|  | 			debug("Certificate was rejected by \"dbx\"\n"); | ||||||
|  | 			goto err; | ||||||
|  | 		} else { | ||||||
|  | 			ret = true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | err: | ||||||
|  | 	x509_free_certificate(cert); | ||||||
|  | 	efi_sigstore_free(db); | ||||||
|  | 	efi_sigstore_free(dbx); | ||||||
|  | 	pkcs7_free_message(msg); | ||||||
|  | 	free(regs); | ||||||
|  | 	free(new_efi); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | static bool efi_image_authenticate(void *efi, size_t efi_size) | ||||||
|  | { | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | #endif /* CONFIG_EFI_SECURE_BOOT */ | ||||||
|  | 
 | ||||||
| /**
 | /**
 | ||||||
|  * efi_load_pe() - relocate EFI binary |  * efi_load_pe() - relocate EFI binary | ||||||
|  * |  * | ||||||
|  | @ -214,10 +597,12 @@ static void efi_set_code_and_data_type( | ||||||
|  * |  * | ||||||
|  * @handle:		loaded image handle |  * @handle:		loaded image handle | ||||||
|  * @efi:		pointer to the EFI binary |  * @efi:		pointer to the EFI binary | ||||||
|  |  * @efi_size:		size of @efi binary | ||||||
|  * @loaded_image_info:	loaded image protocol |  * @loaded_image_info:	loaded image protocol | ||||||
|  * Return:		status code |  * Return:		status code | ||||||
|  */ |  */ | ||||||
| efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, | ||||||
|  | 			 void *efi, size_t efi_size, | ||||||
| 			 struct efi_loaded_image *loaded_image_info) | 			 struct efi_loaded_image *loaded_image_info) | ||||||
| { | { | ||||||
| 	IMAGE_NT_HEADERS32 *nt; | 	IMAGE_NT_HEADERS32 *nt; | ||||||
|  | @ -232,17 +617,41 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 	uint64_t image_base; | 	uint64_t image_base; | ||||||
| 	unsigned long virt_size = 0; | 	unsigned long virt_size = 0; | ||||||
| 	int supported = 0; | 	int supported = 0; | ||||||
|  | 	efi_status_t ret; | ||||||
|  | 
 | ||||||
|  | 	/* Sanity check for a file header */ | ||||||
|  | 	if (efi_size < sizeof(*dos)) { | ||||||
|  | 		printf("%s: Truncated DOS Header\n", __func__); | ||||||
|  | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	dos = efi; | 	dos = efi; | ||||||
| 	if (dos->e_magic != IMAGE_DOS_SIGNATURE) { | 	if (dos->e_magic != IMAGE_DOS_SIGNATURE) { | ||||||
| 		printf("%s: Invalid DOS Signature\n", __func__); | 		printf("%s: Invalid DOS Signature\n", __func__); | ||||||
| 		return EFI_LOAD_ERROR; | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* assume sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64) */ | ||||||
|  | 	if (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)) { | ||||||
|  | 		printf("%s: Invalid offset for Extended Header\n", __func__); | ||||||
|  | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nt = (void *) ((char *)efi + dos->e_lfanew); | 	nt = (void *) ((char *)efi + dos->e_lfanew); | ||||||
|  | 	if ((nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) && | ||||||
|  | 	    (efi_size < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64))) { | ||||||
|  | 		printf("%s: Invalid offset for Extended Header\n", __func__); | ||||||
|  | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if (nt->Signature != IMAGE_NT_SIGNATURE) { | 	if (nt->Signature != IMAGE_NT_SIGNATURE) { | ||||||
| 		printf("%s: Invalid NT Signature\n", __func__); | 		printf("%s: Invalid NT Signature\n", __func__); | ||||||
| 		return EFI_LOAD_ERROR; | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (i = 0; machines[i]; i++) | 	for (i = 0; machines[i]; i++) | ||||||
|  | @ -254,14 +663,29 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 	if (!supported) { | 	if (!supported) { | ||||||
| 		printf("%s: Machine type 0x%04x is not supported\n", | 		printf("%s: Machine type 0x%04x is not supported\n", | ||||||
| 		       __func__, nt->FileHeader.Machine); | 		       __func__, nt->FileHeader.Machine); | ||||||
| 		return EFI_LOAD_ERROR; | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Calculate upper virtual address boundary */ |  | ||||||
| 	num_sections = nt->FileHeader.NumberOfSections; | 	num_sections = nt->FileHeader.NumberOfSections; | ||||||
| 	sections = (void *)&nt->OptionalHeader + | 	sections = (void *)&nt->OptionalHeader + | ||||||
| 			    nt->FileHeader.SizeOfOptionalHeader; | 			    nt->FileHeader.SizeOfOptionalHeader; | ||||||
| 
 | 
 | ||||||
|  | 	if (efi_size < ((void *)sections + sizeof(sections[0]) * num_sections | ||||||
|  | 			- efi)) { | ||||||
|  | 		printf("%s: Invalid number of sections: %d\n", | ||||||
|  | 		       __func__, num_sections); | ||||||
|  | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Authenticate an image */ | ||||||
|  | 	if (efi_image_authenticate(efi, efi_size)) | ||||||
|  | 		handle->auth_status = EFI_IMAGE_AUTH_PASSED; | ||||||
|  | 	else | ||||||
|  | 		handle->auth_status = EFI_IMAGE_AUTH_FAILED; | ||||||
|  | 
 | ||||||
|  | 	/* Calculate upper virtual address boundary */ | ||||||
| 	for (i = num_sections - 1; i >= 0; i--) { | 	for (i = num_sections - 1; i >= 0; i--) { | ||||||
| 		IMAGE_SECTION_HEADER *sec = §ions[i]; | 		IMAGE_SECTION_HEADER *sec = §ions[i]; | ||||||
| 		virt_size = max_t(unsigned long, virt_size, | 		virt_size = max_t(unsigned long, virt_size, | ||||||
|  | @ -280,7 +704,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 		if (!efi_reloc) { | 		if (!efi_reloc) { | ||||||
| 			printf("%s: Could not allocate %lu bytes\n", | 			printf("%s: Could not allocate %lu bytes\n", | ||||||
| 			       __func__, virt_size); | 			       __func__, virt_size); | ||||||
| 			return EFI_OUT_OF_RESOURCES; | 			ret = EFI_OUT_OF_RESOURCES; | ||||||
|  | 			goto err; | ||||||
| 		} | 		} | ||||||
| 		handle->entry = efi_reloc + opt->AddressOfEntryPoint; | 		handle->entry = efi_reloc + opt->AddressOfEntryPoint; | ||||||
| 		rel_size = opt->DataDirectory[rel_idx].Size; | 		rel_size = opt->DataDirectory[rel_idx].Size; | ||||||
|  | @ -296,7 +721,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 		if (!efi_reloc) { | 		if (!efi_reloc) { | ||||||
| 			printf("%s: Could not allocate %lu bytes\n", | 			printf("%s: Could not allocate %lu bytes\n", | ||||||
| 			       __func__, virt_size); | 			       __func__, virt_size); | ||||||
| 			return EFI_OUT_OF_RESOURCES; | 			ret = EFI_OUT_OF_RESOURCES; | ||||||
|  | 			goto err; | ||||||
| 		} | 		} | ||||||
| 		handle->entry = efi_reloc + opt->AddressOfEntryPoint; | 		handle->entry = efi_reloc + opt->AddressOfEntryPoint; | ||||||
| 		rel_size = opt->DataDirectory[rel_idx].Size; | 		rel_size = opt->DataDirectory[rel_idx].Size; | ||||||
|  | @ -305,13 +731,16 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 	} else { | 	} else { | ||||||
| 		printf("%s: Invalid optional header magic %x\n", __func__, | 		printf("%s: Invalid optional header magic %x\n", __func__, | ||||||
| 		       nt->OptionalHeader.Magic); | 		       nt->OptionalHeader.Magic); | ||||||
| 		return EFI_LOAD_ERROR; | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Copy PE headers */ | 	/* Copy PE headers */ | ||||||
| 	memcpy(efi_reloc, efi, sizeof(*dos) + sizeof(*nt) | 	memcpy(efi_reloc, efi, | ||||||
| 	       + nt->FileHeader.SizeOfOptionalHeader | 	       sizeof(*dos) | ||||||
| 	       + num_sections * sizeof(IMAGE_SECTION_HEADER)); | 		 + sizeof(*nt) | ||||||
|  | 		 + nt->FileHeader.SizeOfOptionalHeader | ||||||
|  | 		 + num_sections * sizeof(IMAGE_SECTION_HEADER)); | ||||||
| 
 | 
 | ||||||
| 	/* Load sections into RAM */ | 	/* Load sections into RAM */ | ||||||
| 	for (i = num_sections - 1; i >= 0; i--) { | 	for (i = num_sections - 1; i >= 0; i--) { | ||||||
|  | @ -328,7 +757,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 				(unsigned long)image_base) != EFI_SUCCESS) { | 				(unsigned long)image_base) != EFI_SUCCESS) { | ||||||
| 		efi_free_pages((uintptr_t) efi_reloc, | 		efi_free_pages((uintptr_t) efi_reloc, | ||||||
| 			       (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT); | 			       (virt_size + EFI_PAGE_MASK) >> EFI_PAGE_SHIFT); | ||||||
| 		return EFI_LOAD_ERROR; | 		ret = EFI_LOAD_ERROR; | ||||||
|  | 		goto err; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Flush cache */ | 	/* Flush cache */ | ||||||
|  | @ -340,5 +770,11 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj *handle, void *efi, | ||||||
| 	loaded_image_info->image_base = efi_reloc; | 	loaded_image_info->image_base = efi_reloc; | ||||||
| 	loaded_image_info->image_size = virt_size; | 	loaded_image_info->image_size = virt_size; | ||||||
| 
 | 
 | ||||||
| 	return EFI_SUCCESS; | 	if (handle->auth_status == EFI_IMAGE_AUTH_PASSED) | ||||||
|  | 		return EFI_SUCCESS; | ||||||
|  | 	else | ||||||
|  | 		return EFI_SECURITY_VIOLATION; | ||||||
|  | 
 | ||||||
|  | err: | ||||||
|  | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue