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