]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Variable/RuntimeDxe/TcgMorLockSmm.c
MdeModulePkg: Change TCG MOR variables to use VariablePolicy
[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 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 EFI_STATUS Status;
426 VARIABLE_POLICY_ENTRY *NewPolicy;
427
428 if (!mMorLockInitializationRequired) {
429 //
430 // The EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL has never been installed, thus
431 // the variable write service is unavailable. This should never happen.
432 //
433 ASSERT (FALSE);
434 return;
435 }
436
437 //
438 // Check if the MOR variable exists.
439 //
440 MorSize = 0;
441 MorStatus = VariableServiceGetVariable (
442 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
443 &gEfiMemoryOverwriteControlDataGuid,
444 NULL, // Attributes
445 &MorSize,
446 NULL // Data
447 );
448 //
449 // We provided a zero-sized buffer, so the above call can never succeed.
450 //
451 ASSERT (EFI_ERROR (MorStatus));
452
453 if (MorStatus == EFI_BUFFER_TOO_SMALL) {
454 //
455 // The MOR variable exists.
456 //
457 // Some OSes don't follow the TCG's Platform Reset Attack Mitigation spec
458 // in that the OS should never create the MOR variable, only read and write
459 // it -- these OSes (unintentionally) create MOR if the platform firmware
460 // does not produce it. Whether this is the case (from the last OS boot)
461 // can be deduced from the absence of the TCG / TCG2 protocols, as edk2's
462 // MOR implementation depends on (one of) those protocols.
463 //
464 if (VariableHaveTcgProtocols ()) {
465 //
466 // The MOR variable originates from the platform firmware; set the MOR
467 // Control Lock variable to report the locking capability to the OS.
468 //
469 SetMorLockVariable (0);
470 return;
471 }
472
473 //
474 // The MOR variable's origin is inexplicable; delete it.
475 //
476 DEBUG ((
477 DEBUG_WARN,
478 "%a: deleting unexpected / unsupported variable %g:%s\n",
479 __FUNCTION__,
480 &gEfiMemoryOverwriteControlDataGuid,
481 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME
482 ));
483
484 mMorPassThru = TRUE;
485 VariableServiceSetVariable (
486 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
487 &gEfiMemoryOverwriteControlDataGuid,
488 0, // Attributes
489 0, // DataSize
490 NULL // Data
491 );
492 mMorPassThru = FALSE;
493 }
494
495 //
496 // The MOR variable is absent; the platform firmware does not support it.
497 // Lock the variable so that no other module may create it.
498 //
499 NewPolicy = NULL;
500 Status = CreateBasicVariablePolicy( &gEfiMemoryOverwriteControlDataGuid,
501 MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME,
502 VARIABLE_POLICY_NO_MIN_SIZE,
503 VARIABLE_POLICY_NO_MAX_SIZE,
504 VARIABLE_POLICY_NO_MUST_ATTR,
505 VARIABLE_POLICY_NO_CANT_ATTR,
506 VARIABLE_POLICY_TYPE_LOCK_NOW,
507 &NewPolicy );
508 if (!EFI_ERROR( Status )) {
509 Status = RegisterVariablePolicy( NewPolicy );
510 }
511 if (EFI_ERROR( Status )) {
512 DEBUG(( DEBUG_ERROR, "%a - Failed to lock variable %s! %r\n", __FUNCTION__, MEMORY_OVERWRITE_REQUEST_VARIABLE_NAME, Status ));
513 ASSERT_EFI_ERROR( Status );
514 }
515 if (NewPolicy != NULL) {
516 FreePool( NewPolicy );
517 }
518
519 //
520 // Delete the MOR Control Lock variable too (should it exists for some
521 // reason) and prevent other modules from creating it.
522 //
523 mMorLockPassThru = TRUE;
524 VariableServiceSetVariable (
525 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
526 &gEfiMemoryOverwriteRequestControlLockGuid,
527 0, // Attributes
528 0, // DataSize
529 NULL // Data
530 );
531 mMorLockPassThru = FALSE;
532
533 NewPolicy = NULL;
534 Status = CreateBasicVariablePolicy( &gEfiMemoryOverwriteRequestControlLockGuid,
535 MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME,
536 VARIABLE_POLICY_NO_MIN_SIZE,
537 VARIABLE_POLICY_NO_MAX_SIZE,
538 VARIABLE_POLICY_NO_MUST_ATTR,
539 VARIABLE_POLICY_NO_CANT_ATTR,
540 VARIABLE_POLICY_TYPE_LOCK_NOW,
541 &NewPolicy );
542 if (!EFI_ERROR( Status )) {
543 Status = RegisterVariablePolicy( NewPolicy );
544 }
545 if (EFI_ERROR( Status )) {
546 DEBUG(( DEBUG_ERROR, "%a - Failed to lock variable %s! %r\n", __FUNCTION__, MEMORY_OVERWRITE_REQUEST_CONTROL_LOCK_NAME, Status ));
547 ASSERT_EFI_ERROR( Status );
548 }
549 if (NewPolicy != NULL) {
550 FreePool( NewPolicy );
551 }
552 }