1From 19e79008e0fa3193b54bf6499516dc75cb10f6ec Mon Sep 17 00:00:00 2001
2From: Gabor Toth <gabor.toth2@arm.com>
3Date: Thu, 11 Apr 2024 13:42:03 +0200
4Subject: [PATCH 2/3] Isolate common uefi variable authentication steps
5
6Currently all auth variables are authenticated with the secure boot
7keys. To introduce corrent check for Private Authenticated Variables
8first separate the common steps from the secure boot related steps.
9
10Signed-off-by: Gabor Toth <gabor.toth2@arm.com>
11Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TS/trusted-services/+/27956]
12---
13 .../backend/uefi_variable_store.c             | 191 ++++++++++--------
14 1 file changed, 103 insertions(+), 88 deletions(-)
15
16diff --git a/components/service/uefi/smm_variable/backend/uefi_variable_store.c b/components/service/uefi/smm_variable/backend/uefi_variable_store.c
17index 1b624f0c9..1384d0def 100644
18--- a/components/service/uefi/smm_variable/backend/uefi_variable_store.c
19+++ b/components/service/uefi/smm_variable/backend/uefi_variable_store.c
20@@ -78,6 +78,12 @@ static efi_status_t verify_var_by_key_var(const efi_data_map *new_var,
21 static efi_status_t authenticate_variable(const struct uefi_variable_store *context,
22 					  EFI_TIME *timestamp,
23 					  SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *var);
24+
25+static efi_status_t authenticate_secure_boot_variable(const struct uefi_variable_store *context,
26+						      efi_data_map* var_map,
27+						      uint8_t* hash_buffer,
28+						      size_t hash_len,
29+						      uint64_t max_variable_size);
30 #endif
31
32 static efi_status_t store_variable_data(const struct uefi_variable_store *context,
33@@ -1118,30 +1124,109 @@ static efi_status_t authenticate_variable(const struct uefi_variable_store *cont
34 {
35 	efi_status_t status = EFI_SUCCESS;
36 	EFI_GUID pkcs7_guid = EFI_CERT_TYPE_PKCS7_GUID;
37-	EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE;
38-	EFI_GUID security_database_guid = EFI_IMAGE_SECURITY_DATABASE_GUID;
39 	SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO variable_info = { 0, 0, 0, 0 };
40-	SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *pk_variable = NULL;
41-	size_t pk_payload_size = 0;
42 	efi_data_map var_map = { NULL, NULL, NULL, 0, 0, NULL, 0, NULL };
43 	uint8_t hash_buffer[PSA_HASH_MAX_SIZE];
44 	size_t hash_len = 0;
45-	bool hash_result = false;
46
47 	/* Create a map of the fields of the new variable including the auth header */
48 	if (!init_efi_data_map(var, true, &var_map))
49 		return EFI_SECURITY_VIOLATION;
50
51-	/* database variables can be verified by either PK or KEK while images
52-	 * should be checked by db and dbx so the length of two will be enough.
53-	 */
54-	SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *allowed_key_store_variables[] = { NULL, NULL };
55-
56 	/* Find the maximal size of variables for the GetVariable operation */
57 	status = uefi_variable_store_query_variable_info(context, &variable_info);
58 	if (status != EFI_SUCCESS)
59 		return EFI_SECURITY_VIOLATION;
60
61+	/**
62+	 * UEFI: Page 246
63+	 * If the EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set in a
64+	 * SetVariable() call, and firmware does not support signature type of the certificate
65+	 * included in the EFI_VARIABLE_AUTHENTICATION_2 descriptor, then the SetVariable() call
66+	 * shall return EFI_INVALID_PARAMETER. The list of signature types supported by the
67+	 * firmware is defined by the SignatureSupport variable. Signature type of the certificate
68+	 * is defined by its digest and encryption algorithms.
69+	 */
70+	/* TODO: Should support WIN_CERT_TYPE_PKCS_SIGNED_DATA and WIN_CERT_TYPE_EFI_PKCS115 */
71+	if (var_map.efi_auth_descriptor->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID)
72+		return EFI_INVALID_PARAMETER;
73+
74+	/* Only a CertType of EFI_CERT_TYPE_PKCS7_GUID is accepted */
75+	if (!compare_guid(&var_map.efi_auth_descriptor->AuthInfo.CertType, &pkcs7_guid))
76+		return EFI_SECURITY_VIOLATION;
77+
78+	/**
79+	 * Time associated with the authentication descriptor. For the TimeStamp value,
80+	 * components Pad1, Nanosecond, TimeZone, Daylight and Pad2 shall be set to 0.
81+	 * This means that the time shall always be expressed in GMT.
82+	 *
83+	 * UEFI: Page 253
84+	 * 2. Verify that Pad1, Nanosecond, TimeZone, Daylight and Pad2 components
85+	 * of the TimeStamp value are set to zero.
86+	 */
87+	if ((var_map.efi_auth_descriptor->TimeStamp.Pad1 != 0) ||
88+	    (var_map.efi_auth_descriptor->TimeStamp.Pad2 != 0) ||
89+	    (var_map.efi_auth_descriptor->TimeStamp.Nanosecond != 0) ||
90+	    (var_map.efi_auth_descriptor->TimeStamp.TimeZone != 0) ||
91+	    (var_map.efi_auth_descriptor->TimeStamp.Daylight != 0)) {
92+		return EFI_SECURITY_VIOLATION;
93+	}
94+
95+	/**
96+	 * UEFI: Page 253
97+	 * Unless the EFI_VARIABLE_APPEND_WRITE attribute is set, verify
98+	 * that the TimeStamp value is later than the current
99+	 * timestamp value associated with the variable
100+	 */
101+	if (!(var->Attributes & EFI_VARIABLE_APPEND_WRITE)) {
102+		if (memcmp(&var_map.efi_auth_descriptor->TimeStamp, timestamp, sizeof(EFI_GUID)) <= 0) {
103+			EMSG("Timestamp violation");
104+			return EFI_SECURITY_VIOLATION;
105+		}
106+
107+		/* Save new timestamp */
108+		memcpy(timestamp, &var_map.efi_auth_descriptor->TimeStamp, sizeof(EFI_TIME));
109+	}
110+	/* Calculate hash for the variable only once */
111+	if (calc_variable_hash(&var_map, (uint8_t *)&hash_buffer, sizeof(hash_buffer), &hash_len) == 0) {
112+		status = EFI_SECURITY_VIOLATION;
113+	}
114+
115+	/* Run Secure Boot related authentication steps */
116+	status = authenticate_secure_boot_variable(context, &var_map, (uint8_t*) &hash_buffer, hash_len, variable_info.MaximumVariableSize);
117+
118+	/* Remove the authentication header from the variable if the authentication is successful */
119+	if (status == EFI_SUCCESS) {
120+		uint8_t *smm_payload =
121+			(uint8_t *)var + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var);
122+
123+		memmove(smm_payload, var_map.payload, var_map.payload_len);
124+		memset((uint8_t *)smm_payload + var_map.payload_len, 0,
125+		       var_map.efi_auth_descriptor_len);
126+
127+		var->DataSize -= var_map.efi_auth_descriptor_len;
128+	}
129+
130+	return status;
131+}
132+
133+static efi_status_t authenticate_secure_boot_variable(const struct uefi_variable_store *context,
134+						      efi_data_map* var_map,
135+						      uint8_t* hash_buffer,
136+						      size_t hash_len,
137+						      uint64_t max_variable_size)
138+{
139+	efi_status_t status = EFI_SUCCESS;
140+	EFI_GUID global_variable_guid = EFI_GLOBAL_VARIABLE;
141+	EFI_GUID security_database_guid = EFI_IMAGE_SECURITY_DATABASE_GUID;
142+	SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *pk_variable = NULL;
143+	size_t pk_payload_size = 0;
144+
145+	/* database variables can be verified by either PK or KEK while images
146+	 * should be checked by db and dbx so the length of two will be enough.
147+	 */
148+	SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *allowed_key_store_variables[] = { NULL, NULL };
149+
150 	/**
151 	 * UEFI: Page 253
152 	 * 3. If the variable SetupMode==1, and the variable is a secure
153@@ -1166,14 +1251,14 @@ static efi_status_t authenticate_variable(const struct uefi_variable_store *cont
154 	 * Platform Key is checked to enable or disable authentication.
155 	 */
156 	create_smm_variable(&pk_variable, sizeof(EFI_PLATFORM_KEY_NAME),
157-			    variable_info.MaximumVariableSize, (uint8_t *)EFI_PLATFORM_KEY_NAME,
158+			    max_variable_size, (uint8_t *)EFI_PLATFORM_KEY_NAME,
159 			    &global_variable_guid);
160
161 	if (!pk_variable)
162 		return EFI_OUT_OF_RESOURCES;
163
164 	status = uefi_variable_store_get_variable(
165-		context, pk_variable, variable_info.MaximumVariableSize, &pk_payload_size);
166+		context, pk_variable, max_variable_size, &pk_payload_size);
167
168 	/* If PK does not exist authentication is disabled */
169 	if (status != EFI_SUCCESS) {
170@@ -1207,66 +1292,8 @@ static efi_status_t authenticate_variable(const struct uefi_variable_store *cont
171 		goto end;
172 	}
173
174-	/**
175-	 * UEFI: Page 246
176-	 * If the EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set in a
177-	 * SetVariable() call, and firmware does not support signature type of the certificate
178-	 * included in the EFI_VARIABLE_AUTHENTICATION_2 descriptor, then the SetVariable() call
179-	 * shall return EFI_INVALID_PARAMETER. The list of signature types supported by the
180-	 * firmware is defined by the SignatureSupport variable. Signature type of the certificate
181-	 * is defined by its digest and encryption algorithms.
182-	 */
183-	/* TODO: Should support WIN_CERT_TYPE_PKCS_SIGNED_DATA and WIN_CERT_TYPE_EFI_PKCS115 */
184-	if (var_map.efi_auth_descriptor->AuthInfo.Hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID)
185-		return EFI_INVALID_PARAMETER;
186-
187-	/* Only a CertType of EFI_CERT_TYPE_PKCS7_GUID is accepted */
188-	if (!compare_guid(&var_map.efi_auth_descriptor->AuthInfo.CertType, &pkcs7_guid))
189-		return EFI_SECURITY_VIOLATION;
190-
191-	/**
192-	 * Time associated with the authentication descriptor. For the TimeStamp value,
193-	 * components Pad1, Nanosecond, TimeZone, Daylight and Pad2 shall be set to 0.
194-	 * This means that the time shall always be expressed in GMT.
195-	 *
196-	 * UEFI: Page 253
197-	 * 2. Verify that Pad1, Nanosecond, TimeZone, Daylight and Pad2 components
198-	 * of the TimeStamp value are set to zero.
199-	 */
200-	if ((var_map.efi_auth_descriptor->TimeStamp.Pad1 != 0) ||
201-	    (var_map.efi_auth_descriptor->TimeStamp.Pad2 != 0) ||
202-	    (var_map.efi_auth_descriptor->TimeStamp.Nanosecond != 0) ||
203-	    (var_map.efi_auth_descriptor->TimeStamp.TimeZone != 0) ||
204-	    (var_map.efi_auth_descriptor->TimeStamp.Daylight != 0)) {
205-		return EFI_SECURITY_VIOLATION;
206-	}
207-
208-	/**
209-	 * UEFI: Page 253
210-	 * Unless the EFI_VARIABLE_APPEND_WRITE attribute is set, verify
211-	 * that the TimeStamp value is later than the current
212-	 * timestamp value associated with the variable
213-	 */
214-	if (!(var->Attributes & EFI_VARIABLE_APPEND_WRITE)) {
215-		if (memcmp(&var_map.efi_auth_descriptor->TimeStamp, timestamp, sizeof(EFI_GUID)) <= 0) {
216-			EMSG("Timestamp violation");
217-			return EFI_SECURITY_VIOLATION;
218-		}
219-
220-		/* Save new timestamp */
221-		memcpy(timestamp, &var_map.efi_auth_descriptor->TimeStamp, sizeof(EFI_TIME));
222-	}
223-	/* Calculate hash for the variable only once */
224-	hash_result = calc_variable_hash(&var_map, (uint8_t *)&hash_buffer, sizeof(hash_buffer),
225-					 &hash_len);
226-
227-	if (!hash_result) {
228-		status = EFI_SECURITY_VIOLATION;
229-		goto end;
230-	}
231-
232-	status = select_verification_keys(var_map, global_variable_guid, security_database_guid,
233-					  variable_info.MaximumVariableSize,
234+	status = select_verification_keys(*var_map, global_variable_guid, security_database_guid,
235+					  max_variable_size,
236 					  &allowed_key_store_variables[0]);
237
238 	if (status != EFI_SUCCESS)
239@@ -1280,8 +1307,8 @@ static efi_status_t authenticate_variable(const struct uefi_variable_store *cont
240 			continue;
241
242 		status = uefi_variable_store_get_variable(context, allowed_key_store_variables[i],
243-							  variable_info.MaximumVariableSize,
244-							  &actual_variable_length);
245+							max_variable_size,
246+							&actual_variable_length);
247
248 		if (status) {
249 			/* When the parent does not exist it is considered verification failure */
250@@ -1297,8 +1324,8 @@ static efi_status_t authenticate_variable(const struct uefi_variable_store *cont
251 			goto end;
252 		}
253
254-		status = verify_var_by_key_var(&var_map, &allowed_key_store_var_map,
255-					       (uint8_t *)&hash_buffer, hash_len);
256+		status = verify_var_by_key_var(var_map, &allowed_key_store_var_map,
257+					hash_buffer, hash_len);
258
259 		if (status == EFI_SUCCESS)
260 			goto end;
261@@ -1311,18 +1338,6 @@ end:
262 			free(allowed_key_store_variables[i]);
263 	}
264
265-	/* Remove the authentication header from the variable if the authentication is successful */
266-	if (status == EFI_SUCCESS) {
267-		uint8_t *smm_payload =
268-			(uint8_t *)var + SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE_DATA_OFFSET(var);
269-
270-		memmove(smm_payload, var_map.payload, var_map.payload_len);
271-		memset((uint8_t *)smm_payload + var_map.payload_len, 0,
272-		       var_map.efi_auth_descriptor_len);
273-
274-		var->DataSize -= var_map.efi_auth_descriptor_len;
275-	}
276-
277 	return status;
278 }
279 #endif
280--
2812.25.1
282
283