]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c
MdeModulePkg: Allow VariablePolicy state to delete protected variables
[mirror_edk2.git] / MdeModulePkg / Universal / Variable / RuntimeDxe / TcgMorLockSmm.c
1 /** @file
2 TCG MOR (Memory Overwrite Request) Lock Control support (SMM version).
3
4 This module initilizes MemoryOverwriteRequestControlLock variable.
5 This module adds Variable Hook and check MemoryOverwriteRequestControlLock.
6
7 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
8 Copyright (c) Microsoft Corporation.
9 SPDX-License-Identifier: BSD-2-Clause-Patent
10
11 **/
12
13 #include <PiDxe.h>
14 #include <Guid/MemoryOverwriteControl.h>
15 #include <IndustryStandard/MemoryOverwriteRequestControlLock.h>
16 #include <Library/DebugLib.h>
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include "Variable.h"
20
21 #include <Protocol/VariablePolicy.h>
22
23 #include <Library/VariablePolicyLib.h>
24
25 typedef struct {
26 CHAR16 *VariableName;
27 EFI_GUID *VendorGuid;
28 } VARIABLE_TYPE;
29
30 VARIABLE_TYPE mMorVariableType[] = {
31 {MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, &gEfiMemoryOverwriteControlDataGuid},
32 {MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, &gEfiMemoryOverwriteRequestControlLockGuid},
33 };
34
35 BOOLEAN mMorPassThru = FALSE;
36
37 #define MOR_LOCK_DATA_UNLOCKED 0x0
38 #define MOR_LOCK_DATA_LOCKED_WITHOUT_KEY 0x1
39 #define MOR_LOCK_DATA_LOCKED_WITH_KEY 0x2
40
41 #define MOR_LOCK_V1_SIZE 1
42 #define MOR_LOCK_V2_KEY_SIZE 8
43
44 typedef enum {
45 MorLockStateUnlocked = 0,
46 MorLockStateLocked = 1,
47 } MOR_LOCK_STATE;
48
49 BOOLEAN mMorLockInitializationRequired = FALSE;
50 UINT8 mMorLockKey[MOR_LOCK_V2_KEY_SIZE];
51 BOOLEAN mMorLockKeyEmpty = TRUE;
52 BOOLEAN mMorLockPassThru = FALSE;
53 MOR_LOCK_STATE mMorLockState = MorLockStateUnlocked;
54
55 /**
56 Returns if this is MOR related variable.
57
58 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
59 @param VendorGuid Unify identifier for vendor.
60
61 @retval TRUE The variable is MOR related.
62 @retval FALSE The variable is NOT MOR related.
63 **/
64 BOOLEAN
65 IsAnyMorVariable (
66 IN CHAR16 *VariableName,
67 IN EFI_GUID *VendorGuid
68 )
69 {
70 UINTN Index;
71
72 for (Index = 0; Index < sizeof(mMorVariableType)/sizeof(mMorVariableType[0]); Index++) {
73 if ((StrCmp (VariableName, mMorVariableType[Index].VariableName) == 0) &&
74 (CompareGuid (VendorGuid, mMorVariableType[Index].VendorGuid))) {
75 return TRUE;
76 }
77 }
78 return FALSE;
79 }
80
81 /**
82 Returns if this is MOR lock variable.
83
84 @param VariableName the name of the vendor's variable, it's a Null-Terminated Unicode String
85 @param VendorGuid Unify identifier for vendor.
86
87 @retval TRUE The variable is MOR lock variable.
88 @retval FALSE The variable is NOT MOR lock variable.
89 **/
90 BOOLEAN
91 IsMorLockVariable (
92 IN CHAR16 *VariableName,
93 IN EFI_GUID *VendorGuid
94 )
95 {
96 if ((StrCmp (VariableName, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME) == 0) &&
97 (CompareGuid (VendorGuid, &gEfiMemoryOverwriteRequestControlLockGuid))) {
98 return TRUE;
99 }
100 return FALSE;
101 }
102
103 /**
104 Set MOR lock variable.
105
106 @param Data MOR Lock variable data.
107
108 @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
109 defined by the Attributes.
110 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied, or the
111 DataSize exceeds the maximum allowed.
112 @retval EFI_INVALID_PARAMETER VariableName is an empty Unicode string.
113 @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
114 @retval EFI_DEVICE_ERROR The variable could not be saved due to a hardware failure.
115 @retval EFI_WRITE_PROTECTED The variable in question is read-only.
116 @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
117 @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
118 set but the AuthInfo does NOT pass the validation check carried
119 out by the firmware.
120 @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
121 **/
122 EFI_STATUS
123 SetMorLockVariable (
124 IN UINT8 Data
125 )
126 {
127 EFI_STATUS Status;
128
129 mMorLockPassThru = TRUE;
130 Status = VariableServiceSetVariable (
131 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
132 &gEfiMemoryOverwriteRequestControlLockGuid,
133 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
134 sizeof(Data),
135 &Data
136 );
137 mMorLockPassThru = FALSE;
138 return Status;
139 }
140
141 /**
142 This service is an MorLock checker handler for the SetVariable().
143
144 @param VariableName the name of the vendor's variable, as a
145 Null-Terminated Unicode String
146 @param VendorGuid Unify identifier for vendor.
147 @param Attributes Point to memory location to return the attributes of variable. If the point
148 is NULL, the parameter would be ignored.
149 @param DataSize The size in bytes of Data-Buffer.
150 @param Data Point to the content of the variable.
151
152 @retval EFI_SUCCESS The MorLock check pass, and Variable driver can store the variable data.
153 @retval EFI_INVALID_PARAMETER The MorLock data or data size or attributes is not allowed.
154 @retval EFI_ACCESS_DENIED The MorLock is locked.
155 @retval EFI_WRITE_PROTECTED The MorLock deletion is not allowed.
156 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this function.
157 Variable driver can just return EFI_SUCCESS.
158 **/
159 EFI_STATUS
160 SetVariableCheckHandlerMorLock (
161 IN CHAR16 *VariableName,
162 IN EFI_GUID *VendorGuid,
163 IN UINT32 Attributes,
164 IN UINTN DataSize,
165 IN VOID *Data
166 )
167 {
168 EFI_STATUS Status;
169
170 //
171 // Basic Check
172 //
173 if (Attributes == 0 || DataSize == 0 || Data == NULL) {
174 //
175 // Permit deletion for passthru request, deny it otherwise.
176 //
177 return mMorLockPassThru ? EFI_SUCCESS : EFI_WRITE_PROTECTED;
178 }
179
180 if ((Attributes != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) ||
181 ((DataSize != MOR_LOCK_V1_SIZE) && (DataSize != MOR_LOCK_V2_KEY_SIZE))) {
182 return EFI_INVALID_PARAMETER;
183 }
184
185 //
186 // Do not check if the request is passthru.
187 //
188 if (mMorLockPassThru) {
189 return EFI_SUCCESS;
190 }
191
192 if (mMorLockState == MorLockStateUnlocked) {
193 //
194 // In Unlocked State
195 //
196 if (DataSize == MOR_LOCK_V1_SIZE) {
197 //
198 // V1 - lock permanently
199 //
200 if (*(UINT8 *)Data == MOR_LOCK_DATA_UNLOCKED) {
201 //
202 // Unlock
203 //
204 Status = SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED);
205 if (!EFI_ERROR (Status)) {
206 //
207 // return EFI_ALREADY_STARTED to skip variable set.
208 //
209 return EFI_ALREADY_STARTED;
210 } else {
211 //
212 // SetVar fail
213 //
214 return Status;
215 }
216 } else if (*(UINT8 *)Data == MOR_LOCK_DATA_LOCKED_WITHOUT_KEY) {
217 //
218 // Lock without key
219 //
220 Status = SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITHOUT_KEY);
221 if (!EFI_ERROR (Status)) {
222 //
223 // Lock success
224 //
225 mMorLockState = MorLockStateLocked;
226 //
227 // return EFI_ALREADY_STARTED to skip variable set.
228 //
229 return EFI_ALREADY_STARTED;
230 } else {
231 //
232 // SetVar fail
233 //
234 return Status;
235 }
236 } else {
237 return EFI_INVALID_PARAMETER;
238 }
239 } else if (DataSize == MOR_LOCK_V2_KEY_SIZE) {
240 //
241 // V2 lock and provision the key
242 //
243
244 //
245 // Need set here because the data value on flash is different
246 //
247 Status = SetMorLockVariable (MOR_LOCK_DATA_LOCKED_WITH_KEY);
248 if (EFI_ERROR(Status)) {
249 //
250 // SetVar fail, do not provision the key
251 //
252 return Status;
253 } else {
254 //
255 // Lock success, provision the key
256 //
257 mMorLockKeyEmpty = FALSE;
258 CopyMem (mMorLockKey, Data, MOR_LOCK_V2_KEY_SIZE);
259 mMorLockState = MorLockStateLocked;
260 //
261 // return EFI_ALREADY_STARTED to skip variable set.
262 //
263 return EFI_ALREADY_STARTED;
264 }
265 } else {
266 ASSERT (FALSE);
267 return EFI_OUT_OF_RESOURCES;
268 }
269 } else {
270 //
271 // In Locked State
272 //
273 if (mMorLockKeyEmpty || (DataSize != MOR_LOCK_V2_KEY_SIZE)) {
274 return EFI_ACCESS_DENIED;
275 }
276 if ((CompareMem (Data, mMorLockKey, MOR_LOCK_V2_KEY_SIZE) == 0)) {
277 //
278 // Key match - unlock
279 //
280
281 //
282 // Need set here because the data value on flash is different
283 //
284 Status = SetMorLockVariable (MOR_LOCK_DATA_UNLOCKED);
285 if (EFI_ERROR (Status)) {
286 //
287 // SetVar fail
288 //
289 return Status;
290 } else {
291 //
292 // Unlock Success
293 //
294 mMorLockState = MorLockStateUnlocked;
295 mMorLockKeyEmpty = TRUE;
296 ZeroMem (mMorLockKey, sizeof(mMorLockKey));
297 //
298 // return EFI_ALREADY_STARTED to skip variable set.
299 //
300 return EFI_ALREADY_STARTED;
301 }
302 } else {
303 //
304 // Key mismatch - Prevent Dictionary Attack
305 //
306 mMorLockState = MorLockStateLocked;
307 mMorLockKeyEmpty = TRUE;
308 ZeroMem (mMorLockKey, sizeof(mMorLockKey));
309 return EFI_ACCESS_DENIED;
310 }
311 }
312 }
313
314 /**
315 This service is an MOR/MorLock checker handler for the SetVariable().
316
317 @param[in] VariableName the name of the vendor's variable, as a
318 Null-Terminated Unicode String
319 @param[in] VendorGuid Unify identifier for vendor.
320 @param[in] Attributes Attributes bitmask to set for the variable.
321 @param[in] DataSize The size in bytes of Data-Buffer.
322 @param[in] Data Point to the content of the variable.
323
324 @retval EFI_SUCCESS The MOR/MorLock check pass, and Variable
325 driver can store the variable data.
326 @retval EFI_INVALID_PARAMETER The MOR/MorLock data or data size or
327 attributes is not allowed for MOR variable.
328 @retval EFI_ACCESS_DENIED The MOR/MorLock is locked.
329 @retval EFI_ALREADY_STARTED The MorLock variable is handled inside this
330 function. Variable driver can just return
331 EFI_SUCCESS.
332 **/
333 EFI_STATUS
334 SetVariableCheckHandlerMor (
335 IN CHAR16 *VariableName,
336 IN EFI_GUID *VendorGuid,
337 IN UINT32 Attributes,
338 IN UINTN DataSize,
339 IN VOID *Data
340 )
341 {
342 //
343 // do not handle non-MOR variable
344 //
345 if (!IsAnyMorVariable (VariableName, VendorGuid)) {
346 return EFI_SUCCESS;
347 }
348
349 // Permit deletion when policy is disabled.
350 if (!IsVariablePolicyEnabled() && ((Attributes == 0) || (DataSize == 0))) {
351 return EFI_SUCCESS;
352 }
353
354 //
355 // MorLock variable
356 //
357 if (IsMorLockVariable (VariableName, VendorGuid)) {
358 return SetVariableCheckHandlerMorLock (
359 VariableName,
360 VendorGuid,
361 Attributes,
362 DataSize,
363 Data
364 );
365 }
366
367 //
368 // Mor Variable
369 //
370
371 //
372 // Permit deletion for passthru request.
373 //
374 if (((Attributes == 0) || (DataSize == 0)) && mMorPassThru) {
375 return EFI_SUCCESS;
376 }
377
378 //
379 // Basic Check
380 //
381 if ((Attributes != (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS)) ||
382 (DataSize != sizeof(UINT8)) ||
383 (Data == NULL)) {
384 return EFI_INVALID_PARAMETER;
385 }
386 if (mMorLockState == MorLockStateLocked) {
387 //
388 // If lock, deny access
389 //
390 return EFI_ACCESS_DENIED;
391 }
392 //
393 // grant access
394 //
395 return EFI_SUCCESS;
396 }
397
398 /**
399 Initialization for MOR Control Lock.
400
401 @retval EFI_SUCCESS MorLock initialization success.
402 @return Others Some error occurs.
403 **/
404 EFI_STATUS
405 MorLockInit (
406 VOID
407 )
408 {
409 mMorLockInitializationRequired = TRUE;
410 return EFI_SUCCESS;
411 }
412
413 /**
414 Delayed initialization for MOR Control Lock at EndOfDxe.
415
416 This function performs any operations queued by MorLockInit().
417 **/
418 VOID
419 MorLockInitAtEndOfDxe (
420 VOID
421 )
422 {
423 UINTN MorSize;
424 EFI_STATUS MorStatus;
425
426 if (!mMorLockInitializationRequired) {
427 //
428 // The EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL has never been installed, thus
429 // the variable write service is unavailable. This should never happen.
430 //
431 ASSERT (FALSE);
432 return;
433 }
434
435 //
436 // Check if the MOR variable exists.
437 //
438 MorSize = 0;
439 MorStatus = VariableServiceGetVariable (
440 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
441 &gEfiMemoryOverwriteControlDataGuid,
442 NULL, // Attributes
443 &MorSize,
444 NULL // Data
445 );
446 //
447 // We provided a zero-sized buffer, so the above call can never succeed.
448 //
449 ASSERT (EFI_ERROR (MorStatus));
450
451 if (MorStatus == EFI_BUFFER_TOO_SMALL) {
452 //
453 // The MOR variable exists.
454 //
455 // Some OSes don't follow the TCG's Platform Reset Attack Mitigation spec
456 // in that the OS should never create the MOR variable, only read and write
457 // it -- these OSes (unintentionally) create MOR if the platform firmware
458 // does not produce it. Whether this is the case (from the last OS boot)
459 // can be deduced from the absence of the TCG / TCG2 protocols, as edk2's
460 // MOR implementation depends on (one of) those protocols.
461 //
462 if (VariableHaveTcgProtocols ()) {
463 //
464 // The MOR variable originates from the platform firmware; set the MOR
465 // Control Lock variable to report the locking capability to the OS.
466 //
467 SetMorLockVariable (0);
468 return;
469 }
470
471 //
472 // The MOR variable's origin is inexplicable; delete it.
473 //
474 DEBUG ((
475 DEBUG_WARN,
476 "%a: deleting unexpected / unsupported variable %g:%s\n",
477 __FUNCTION__,
478 &gEfiMemoryOverwriteControlDataGuid,
479 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
480 ));
481
482 mMorPassThru = TRUE;
483 VariableServiceSetVariable (
484 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
485 &gEfiMemoryOverwriteControlDataGuid,
486 0, // Attributes
487 0, // DataSize
488 NULL // Data
489 );
490 mMorPassThru = FALSE;
491 }
492
493 //
494 // The MOR variable is absent; the platform firmware does not support it.
495 // Lock the variable so that no other module may create it.
496 //
497 VariableLockRequestToLock (
498 NULL, // This
499 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
500 &gEfiMemoryOverwriteControlDataGuid
501 );
502
503 //
504 // Delete the MOR Control Lock variable too (should it exists for some
505 // reason) and prevent other modules from creating it.
506 //
507 mMorLockPassThru = TRUE;
508 VariableServiceSetVariable (
509 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
510 &gEfiMemoryOverwriteRequestControlLockGuid,
511 0, // Attributes
512 0, // DataSize
513 NULL // Data
514 );
515 mMorLockPassThru = FALSE;
516
517 VariableLockRequestToLock (
518 NULL, // This
519 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
520 &gEfiMemoryOverwriteRequestControlLockGuid
521 );
522 }