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