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