]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
MdeModulePkg/UefiBootManagerLib: Convert BmLoadOption to Variable Policy
[mirror_edk2.git] / MdeModulePkg / Library / VariablePolicyLib / VariablePolicyLib.c
CommitLineData
355b181f
BB
1/** @file -- VariablePolicyLib.c\r
2Business logic for Variable Policy enforcement.\r
3\r
4Copyright (c) Microsoft Corporation.\r
5SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include <Uefi.h>\r
10\r
11#include <Library/SafeIntLib.h>\r
12#include <Library/MemoryAllocationLib.h>\r
13#include <Library/BaseMemoryLib.h>\r
14#include <Library/DebugLib.h>\r
15#include <Library/PcdLib.h>\r
16\r
17#include <Protocol/VariablePolicy.h>\r
18#include <Library/VariablePolicyLib.h>\r
19\r
355b181f
BB
20// IMPORTANT NOTE: This library is currently rife with multiple return statements\r
21// for error handling. A refactor should remove these at some point.\r
22\r
23//\r
24// This library was designed with advanced unit-test features.\r
25// This define handles the configuration.\r
26#ifdef INTERNAL_UNIT_TEST\r
1436aea4 27 #undef STATIC\r
355b181f
BB
28#define STATIC // Nothing...\r
29#endif\r
30\r
31// An abstracted GetVariable interface that enables configuration regardless of the environment.\r
1436aea4 32EFI_GET_VARIABLE mGetVariableHelper = NULL;\r
355b181f
BB
33\r
34// Master switch to lock this entire interface. Does not stop enforcement,\r
35// just prevents the configuration from being changed for the rest of the boot.\r
1436aea4 36STATIC BOOLEAN mInterfaceLocked = FALSE;\r
355b181f
BB
37\r
38// Master switch to disable the entire interface for a single boot.\r
39// This will disable all policy enforcement for the duration of the boot.\r
1436aea4 40STATIC BOOLEAN mProtectionDisabled = FALSE;\r
355b181f
BB
41\r
42// Table to hold all the current policies.\r
1436aea4
MK
43UINT8 *mPolicyTable = NULL;\r
44STATIC UINT32 mCurrentTableSize = 0;\r
45STATIC UINT32 mCurrentTableUsage = 0;\r
46STATIC UINT32 mCurrentTableCount = 0;\r
355b181f 47\r
1436aea4 48#define POLICY_TABLE_STEP_SIZE 0x1000\r
355b181f
BB
49\r
50// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.\r
51// Current table data has already been sanitized.\r
1436aea4
MK
52#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)\r
53#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)\r
355b181f 54\r
1436aea4
MK
55#define MATCH_PRIORITY_EXACT 0\r
56#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT\r
57#define MATCH_PRIORITY_MIN MAX_UINT8\r
355b181f
BB
58\r
59/**\r
60 An extra init hook that enables the RuntimeDxe library instance to\r
61 register VirtualAddress change callbacks. Among other things.\r
62\r
63 @retval EFI_SUCCESS Everything is good. Continue with init.\r
64 @retval Others Uh... don't continue.\r
65\r
66**/\r
67EFI_STATUS\r
68VariablePolicyExtraInit (\r
69 VOID\r
70 );\r
71\r
72/**\r
73 An extra deinit hook that enables the RuntimeDxe library instance to\r
74 register VirtualAddress change callbacks. Among other things.\r
75\r
76 @retval EFI_SUCCESS Everything is good. Continue with deinit.\r
77 @retval Others Uh... don't continue.\r
78\r
79**/\r
80EFI_STATUS\r
81VariablePolicyExtraDeinit (\r
82 VOID\r
83 );\r
84\r
355b181f
BB
85/**\r
86 This helper function determines whether the structure of an incoming policy\r
87 is valid and internally consistent.\r
88\r
89 @param[in] NewPolicy Pointer to the incoming policy structure.\r
90\r
91 @retval TRUE\r
92 @retval FALSE Pointer is NULL, size is wrong, strings are empty, or\r
93 substructures overlap.\r
94\r
95**/\r
96STATIC\r
97BOOLEAN\r
98IsValidVariablePolicyStructure (\r
1436aea4 99 IN CONST VARIABLE_POLICY_ENTRY *NewPolicy\r
355b181f
BB
100 )\r
101{\r
1436aea4
MK
102 EFI_STATUS Status;\r
103 UINTN EntryEnd;\r
104 CHAR16 *CheckChar;\r
105 UINTN WildcardCount;\r
355b181f
BB
106\r
107 // Sanitize some quick values.\r
1436aea4 108 if ((NewPolicy == NULL) || (NewPolicy->Size == 0) ||\r
355b181f 109 // Structure size should be at least as long as the minumum structure and a NULL string.\r
1436aea4 110 (NewPolicy->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||\r
355b181f 111 // Check for the known revision.\r
1436aea4
MK
112 (NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION))\r
113 {\r
355b181f
BB
114 return FALSE;\r
115 }\r
116\r
117 // Calculate the theoretical end of the structure and make sure\r
118 // that the structure can fit in memory.\r
1436aea4
MK
119 Status = SafeUintnAdd ((UINTN)NewPolicy, NewPolicy->Size, &EntryEnd);\r
120 if (EFI_ERROR (Status)) {\r
355b181f
BB
121 return FALSE;\r
122 }\r
123\r
124 // Check for a valid Max Size.\r
125 if (NewPolicy->MaxSize == 0) {\r
126 return FALSE;\r
127 }\r
128\r
129 // Check for the valid list of lock policies.\r
1436aea4
MK
130 if ((NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK) &&\r
131 (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW) &&\r
132 (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) &&\r
133 (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE))\r
355b181f
BB
134 {\r
135 return FALSE;\r
136 }\r
137\r
138 // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name\r
139 // terminates before the OffsetToName for the matching policy variable Name.\r
140 if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {\r
141 // Adjust CheckChar to the offset of the LockPolicy->Name.\r
1436aea4
MK
142 Status = SafeUintnAdd (\r
143 (UINTN)NewPolicy + sizeof (VARIABLE_POLICY_ENTRY),\r
144 sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),\r
145 (UINTN *)&CheckChar\r
146 );\r
147 if (EFI_ERROR (Status) || (EntryEnd <= (UINTN)CheckChar)) {\r
355b181f
BB
148 return FALSE;\r
149 }\r
1436aea4 150\r
355b181f
BB
151 while (*CheckChar != CHAR_NULL) {\r
152 if (EntryEnd <= (UINTN)CheckChar) {\r
153 return FALSE;\r
154 }\r
1436aea4 155\r
355b181f
BB
156 CheckChar++;\r
157 }\r
1436aea4 158\r
355b181f
BB
159 // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.\r
160 // We should check to make sure that the policy Name comes immediately after this charcter.\r
1436aea4 161 if ((UINTN)++ CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {\r
355b181f
BB
162 return FALSE;\r
163 }\r
1436aea4
MK
164\r
165 // If the policy type is any other value, make sure that the LockPolicy structure has a zero length.\r
355b181f 166 } else {\r
1436aea4 167 if (NewPolicy->OffsetToName != sizeof (VARIABLE_POLICY_ENTRY)) {\r
355b181f
BB
168 return FALSE;\r
169 }\r
170 }\r
171\r
172 // Check to make sure that the name has a terminating character\r
173 // before the end of the structure.\r
174 // We've already checked that the name is within the bounds of the structure.\r
175 if (NewPolicy->Size != NewPolicy->OffsetToName) {\r
1436aea4 176 CheckChar = (CHAR16 *)((UINTN)NewPolicy + NewPolicy->OffsetToName);\r
355b181f
BB
177 WildcardCount = 0;\r
178 while (*CheckChar != CHAR_NULL) {\r
179 // Make sure there aren't excessive wildcards.\r
180 if (*CheckChar == '#') {\r
181 WildcardCount++;\r
182 if (WildcardCount > MATCH_PRIORITY_MIN) {\r
183 return FALSE;\r
184 }\r
185 }\r
1436aea4 186\r
355b181f
BB
187 // Make sure you're still within the bounds of the policy structure.\r
188 if (EntryEnd <= (UINTN)CheckChar) {\r
189 return FALSE;\r
190 }\r
1436aea4 191\r
355b181f
BB
192 CheckChar++;\r
193 }\r
194\r
195 // Finally, we should be pointed at the very last character in Name, so we should be right\r
196 // up against the end of the structure.\r
1436aea4 197 if ((UINTN)++ CheckChar != EntryEnd) {\r
355b181f
BB
198 return FALSE;\r
199 }\r
200 }\r
201\r
202 return TRUE;\r
203}\r
204\r
355b181f
BB
205/**\r
206 This helper function evaluates a policy and determines whether it matches the target\r
207 variable. If matched, will also return a value corresponding to the priority of the match.\r
208\r
209 The rules for "best match" are listed in the Variable Policy Spec.\r
210 Perfect name matches will return 0.\r
211 Single wildcard characters will return the number of wildcard characters.\r
212 Full namespaces will return MAX_UINT8.\r
213\r
214 @param[in] EvalEntry Pointer to the policy entry being evaluated.\r
215 @param[in] VariableName Same as EFI_SET_VARIABLE.\r
216 @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r
217 @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.\r
218 Lower number == higher priority. Only valid if a match found.\r
219\r
220 @retval TRUE Current entry matches the target variable.\r
221 @retval FALSE Current entry does not match at all.\r
222\r
223**/\r
224STATIC\r
225BOOLEAN\r
226EvaluatePolicyMatch (\r
1436aea4
MK
227 IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,\r
228 IN CONST CHAR16 *VariableName,\r
229 IN CONST EFI_GUID *VendorGuid,\r
230 OUT UINT8 *MatchPriority OPTIONAL\r
355b181f
BB
231 )\r
232{\r
1436aea4
MK
233 BOOLEAN Result;\r
234 CHAR16 *PolicyName;\r
235 UINT8 CalculatedPriority;\r
236 UINTN Index;\r
355b181f 237\r
1436aea4 238 Result = FALSE;\r
355b181f
BB
239 CalculatedPriority = MATCH_PRIORITY_EXACT;\r
240\r
241 // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.\r
1436aea4 242 if (!CompareGuid (&EvalEntry->Namespace, VendorGuid)) {\r
355b181f
BB
243 goto Exit;\r
244 }\r
245\r
246 // If the GUID matches, check to see whether there is a Name associated\r
247 // with the policy. If not, this policy matches the entire namespace.\r
248 // Missing Name is indicated by size being equal to name.\r
249 if (EvalEntry->Size == EvalEntry->OffsetToName) {\r
250 CalculatedPriority = MATCH_PRIORITY_MIN;\r
1436aea4 251 Result = TRUE;\r
355b181f
BB
252 goto Exit;\r
253 }\r
254\r
255 // Now that we know the name exists, get it.\r
1436aea4 256 PolicyName = GET_POLICY_NAME (EvalEntry);\r
355b181f
BB
257\r
258 // Evaluate the name against the policy name and check for a match.\r
259 // Account for any wildcards.\r
1436aea4 260 Index = 0;\r
355b181f
BB
261 Result = TRUE;\r
262 // Keep going until the end of both strings.\r
263 while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {\r
264 // If we don't have a match...\r
1436aea4 265 if ((PolicyName[Index] != VariableName[Index]) || (PolicyName[Index] == '#')) {\r
355b181f
BB
266 // If this is a numerical wildcard, we can consider\r
267 // it a match if we alter the priority.\r
1436aea4
MK
268 if ((PolicyName[Index] == L'#') &&\r
269 (((L'0' <= VariableName[Index]) && (VariableName[Index] <= L'9')) ||\r
270 ((L'A' <= VariableName[Index]) && (VariableName[Index] <= L'F')) ||\r
271 ((L'a' <= VariableName[Index]) && (VariableName[Index] <= L'f'))))\r
272 {\r
355b181f
BB
273 if (CalculatedPriority < MATCH_PRIORITY_MIN) {\r
274 CalculatedPriority++;\r
275 }\r
1436aea4
MK
276\r
277 // Otherwise, not a match.\r
355b181f
BB
278 } else {\r
279 Result = FALSE;\r
280 goto Exit;\r
281 }\r
282 }\r
1436aea4 283\r
355b181f
BB
284 Index++;\r
285 }\r
286\r
287Exit:\r
1436aea4 288 if (Result && (MatchPriority != NULL)) {\r
355b181f
BB
289 *MatchPriority = CalculatedPriority;\r
290 }\r
1436aea4 291\r
355b181f
BB
292 return Result;\r
293}\r
294\r
355b181f
BB
295/**\r
296 This helper function walks the current policy table and returns a pointer\r
297 to the best match, if any are found. Leverages EvaluatePolicyMatch() to\r
298 determine "best".\r
299\r
300 @param[in] VariableName Same as EFI_SET_VARIABLE.\r
301 @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r
302 @param[out] ReturnPriority [Optional] If pointer is provided, return the\r
303 priority of the match. Same as EvaluatePolicyMatch().\r
304 Only valid if a match is returned.\r
305\r
306 @retval VARIABLE_POLICY_ENTRY* Best match that was found.\r
307 @retval NULL No match was found.\r
308\r
309**/\r
310STATIC\r
1436aea4 311VARIABLE_POLICY_ENTRY *\r
355b181f 312GetBestPolicyMatch (\r
1436aea4
MK
313 IN CONST CHAR16 *VariableName,\r
314 IN CONST EFI_GUID *VendorGuid,\r
315 OUT UINT8 *ReturnPriority OPTIONAL\r
355b181f
BB
316 )\r
317{\r
1436aea4
MK
318 VARIABLE_POLICY_ENTRY *BestResult;\r
319 VARIABLE_POLICY_ENTRY *CurrentEntry;\r
320 UINT8 MatchPriority;\r
321 UINT8 CurrentPriority;\r
322 UINTN Index;\r
355b181f 323\r
1436aea4 324 BestResult = NULL;\r
355b181f
BB
325 MatchPriority = MATCH_PRIORITY_EXACT;\r
326\r
327 // Walk all entries in the table, looking for matches.\r
1436aea4 328 CurrentEntry = (VARIABLE_POLICY_ENTRY *)mPolicyTable;\r
355b181f
BB
329 for (Index = 0; Index < mCurrentTableCount; Index++) {\r
330 // Check for a match.\r
1436aea4 331 if (EvaluatePolicyMatch (CurrentEntry, VariableName, VendorGuid, &CurrentPriority)) {\r
355b181f 332 // If match is better, take it.\r
1436aea4
MK
333 if ((BestResult == NULL) || (CurrentPriority < MatchPriority)) {\r
334 BestResult = CurrentEntry;\r
355b181f
BB
335 MatchPriority = CurrentPriority;\r
336 }\r
337\r
338 // If you've hit the highest-priority match, can exit now.\r
339 if (MatchPriority == 0) {\r
340 break;\r
341 }\r
342 }\r
343\r
344 // If we're still in the loop, move to the next entry.\r
1436aea4 345 CurrentEntry = GET_NEXT_POLICY (CurrentEntry);\r
355b181f
BB
346 }\r
347\r
348 // If a return priority was requested, return it.\r
349 if (ReturnPriority != NULL) {\r
350 *ReturnPriority = MatchPriority;\r
351 }\r
352\r
353 return BestResult;\r
354}\r
355\r
355b181f
BB
356/**\r
357 This API function validates and registers a new policy with\r
358 the policy enforcement engine.\r
359\r
360 @param[in] NewPolicy Pointer to the incoming policy structure.\r
361\r
362 @retval EFI_SUCCESS\r
363 @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.\r
364 @retval EFI_ALREADY_STARTED An identical matching policy already exists.\r
365 @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.\r
366 @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.\r
367 @retval EFI_ABORTED A calculation error has prevented this function from completing.\r
368 @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.\r
369 @retval EFI_NOT_READY Library has not yet been initialized.\r
370\r
371**/\r
372EFI_STATUS\r
373EFIAPI\r
374RegisterVariablePolicy (\r
1436aea4 375 IN CONST VARIABLE_POLICY_ENTRY *NewPolicy\r
355b181f
BB
376 )\r
377{\r
1436aea4
MK
378 EFI_STATUS Status;\r
379 VARIABLE_POLICY_ENTRY *MatchPolicy;\r
380 UINT8 MatchPriority;\r
381 UINT32 NewSize;\r
382 UINT8 *NewTable;\r
355b181f 383\r
1436aea4 384 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
385 return EFI_NOT_READY;\r
386 }\r
1436aea4 387\r
355b181f
BB
388 if (mInterfaceLocked) {\r
389 return EFI_WRITE_PROTECTED;\r
390 }\r
391\r
1436aea4 392 if (!IsValidVariablePolicyStructure (NewPolicy)) {\r
355b181f
BB
393 return EFI_INVALID_PARAMETER;\r
394 }\r
395\r
396 // Check to see whether an exact matching policy already exists.\r
1436aea4
MK
397 MatchPolicy = GetBestPolicyMatch (\r
398 GET_POLICY_NAME (NewPolicy),\r
399 &NewPolicy->Namespace,\r
400 &MatchPriority\r
401 );\r
402 if ((MatchPolicy != NULL) && (MatchPriority == MATCH_PRIORITY_EXACT)) {\r
355b181f
BB
403 return EFI_ALREADY_STARTED;\r
404 }\r
405\r
406 // If none exists, create it.\r
407 // If we need more space, allocate that now.\r
1436aea4
MK
408 Status = SafeUint32Add (mCurrentTableUsage, NewPolicy->Size, &NewSize);\r
409 if (EFI_ERROR (Status)) {\r
355b181f
BB
410 return EFI_ABORTED;\r
411 }\r
1436aea4 412\r
355b181f
BB
413 if (NewSize > mCurrentTableSize) {\r
414 // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.\r
415 NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?\r
1436aea4
MK
416 (NewSize / POLICY_TABLE_STEP_SIZE) + 1 :\r
417 (NewSize / POLICY_TABLE_STEP_SIZE);\r
355b181f 418 // Calculate the new table size in absolute bytes.\r
1436aea4
MK
419 Status = SafeUint32Mult (NewSize, POLICY_TABLE_STEP_SIZE, &NewSize);\r
420 if (EFI_ERROR (Status)) {\r
355b181f
BB
421 return EFI_ABORTED;\r
422 }\r
423\r
424 // Reallocate and copy the table.\r
1436aea4 425 NewTable = AllocateRuntimePool (NewSize);\r
355b181f
BB
426 if (NewTable == NULL) {\r
427 return EFI_OUT_OF_RESOURCES;\r
428 }\r
1436aea4
MK
429\r
430 CopyMem (NewTable, mPolicyTable, mCurrentTableUsage);\r
355b181f
BB
431 mCurrentTableSize = NewSize;\r
432 if (mPolicyTable != NULL) {\r
1436aea4 433 FreePool (mPolicyTable);\r
355b181f 434 }\r
1436aea4 435\r
355b181f
BB
436 mPolicyTable = NewTable;\r
437 }\r
1436aea4 438\r
355b181f 439 // Copy the policy into the table.\r
1436aea4 440 CopyMem (mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size);\r
355b181f
BB
441 mCurrentTableUsage += NewPolicy->Size;\r
442 mCurrentTableCount += 1;\r
443\r
444 // We're done here.\r
445\r
446 return EFI_SUCCESS;\r
447}\r
448\r
355b181f
BB
449/**\r
450 This API function checks to see whether the parameters to SetVariable would\r
451 be allowed according to the current variable policies.\r
452\r
453 @param[in] VariableName Same as EFI_SET_VARIABLE.\r
454 @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r
455 @param[in] Attributes Same as EFI_SET_VARIABLE.\r
456 @param[in] DataSize Same as EFI_SET_VARIABLE.\r
457 @param[in] Data Same as EFI_SET_VARIABLE.\r
458\r
459 @retval EFI_SUCCESS A matching policy allows this update.\r
460 @retval EFI_SUCCESS There are currently no policies that restrict this update.\r
461 @retval EFI_SUCCESS The protections have been disable until the next reboot.\r
462 @retval EFI_WRITE_PROTECTED Variable is currently locked.\r
463 @retval EFI_INVALID_PARAMETER Attributes or size are invalid.\r
464 @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.\r
465 @retval EFI_NOT_READY Library has not been initialized.\r
466\r
467**/\r
468EFI_STATUS\r
469EFIAPI\r
470ValidateSetVariable (\r
1436aea4
MK
471 IN CHAR16 *VariableName,\r
472 IN EFI_GUID *VendorGuid,\r
473 IN UINT32 Attributes,\r
474 IN UINTN DataSize,\r
475 IN VOID *Data\r
355b181f
BB
476 )\r
477{\r
1436aea4
MK
478 BOOLEAN IsDel;\r
479 VARIABLE_POLICY_ENTRY *ActivePolicy;\r
480 EFI_STATUS Status;\r
481 EFI_STATUS ReturnStatus;\r
482 VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;\r
483 CHAR16 *StateVarName;\r
484 UINTN StateVarSize;\r
485 UINT8 StateVar;\r
355b181f
BB
486\r
487 ReturnStatus = EFI_SUCCESS;\r
488\r
1436aea4 489 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
490 ReturnStatus = EFI_NOT_READY;\r
491 goto Exit;\r
492 }\r
493\r
494 // Bail if the protections are currently disabled.\r
495 if (mProtectionDisabled) {\r
496 ReturnStatus = EFI_SUCCESS;\r
497 goto Exit;\r
498 }\r
499\r
500 // Determine whether this is a delete operation.\r
501 // If so, it will affect which tests are applied.\r
502 if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {\r
503 IsDel = TRUE;\r
504 } else {\r
505 IsDel = FALSE;\r
506 }\r
507\r
508 // Find an active policy if one exists.\r
1436aea4 509 ActivePolicy = GetBestPolicyMatch (VariableName, VendorGuid, NULL);\r
355b181f
BB
510\r
511 // If we have an active policy, check it against the incoming data.\r
512 if (ActivePolicy != NULL) {\r
513 //\r
514 // Only enforce size and attribute constraints when updating data, not deleting.\r
515 if (!IsDel) {\r
516 // Check for size constraints.\r
1436aea4
MK
517 if (((ActivePolicy->MinSize > 0) && (DataSize < ActivePolicy->MinSize)) ||\r
518 ((ActivePolicy->MaxSize > 0) && (DataSize > ActivePolicy->MaxSize)))\r
519 {\r
355b181f 520 ReturnStatus = EFI_INVALID_PARAMETER;\r
1436aea4
MK
521 DEBUG ((\r
522 DEBUG_VERBOSE,\r
523 "%a - Bad Size. 0x%X <> 0x%X-0x%X\n",\r
524 __FUNCTION__,\r
525 DataSize,\r
526 ActivePolicy->MinSize,\r
527 ActivePolicy->MaxSize\r
528 ));\r
355b181f
BB
529 goto Exit;\r
530 }\r
531\r
532 // Check for attribute constraints.\r
1436aea4
MK
533 if (((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave) ||\r
534 ((ActivePolicy->AttributesCantHave & Attributes) != 0))\r
535 {\r
355b181f 536 ReturnStatus = EFI_INVALID_PARAMETER;\r
1436aea4
MK
537 DEBUG ((\r
538 DEBUG_VERBOSE,\r
539 "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n",\r
540 __FUNCTION__,\r
541 Attributes,\r
542 ActivePolicy->AttributesMustHave,\r
543 ActivePolicy->AttributesCantHave\r
544 ));\r
355b181f
BB
545 goto Exit;\r
546 }\r
547 }\r
548\r
549 //\r
550 // Lock policy check.\r
551 //\r
552 // Check for immediate lock.\r
553 if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {\r
554 ReturnStatus = EFI_WRITE_PROTECTED;\r
555 goto Exit;\r
1436aea4 556 // Check for lock on create.\r
355b181f
BB
557 } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {\r
558 StateVarSize = 0;\r
1436aea4
MK
559 Status = mGetVariableHelper (\r
560 VariableName,\r
561 VendorGuid,\r
562 NULL,\r
563 &StateVarSize,\r
564 NULL\r
565 );\r
355b181f
BB
566 if (Status == EFI_BUFFER_TOO_SMALL) {\r
567 ReturnStatus = EFI_WRITE_PROTECTED;\r
568 goto Exit;\r
569 }\r
1436aea4
MK
570\r
571 // Check for lock on state variable.\r
355b181f 572 } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {\r
1436aea4
MK
573 StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)((UINT8 *)ActivePolicy + sizeof (VARIABLE_POLICY_ENTRY));\r
574 StateVarName = (CHAR16 *)((UINT8 *)StateVarPolicy + sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY));\r
575 StateVarSize = sizeof (StateVar);\r
576 Status = mGetVariableHelper (\r
577 StateVarName,\r
578 &StateVarPolicy->Namespace,\r
579 NULL,\r
580 &StateVarSize,\r
581 &StateVar\r
582 );\r
355b181f
BB
583\r
584 // If the variable was found, check the state. If matched, this variable is locked.\r
1436aea4 585 if (!EFI_ERROR (Status)) {\r
355b181f
BB
586 if (StateVar == StateVarPolicy->Value) {\r
587 ReturnStatus = EFI_WRITE_PROTECTED;\r
588 goto Exit;\r
589 }\r
1436aea4
MK
590\r
591 // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.\r
592 } else if ((Status != EFI_NOT_FOUND) && (Status != EFI_BUFFER_TOO_SMALL)) {\r
355b181f
BB
593 // We don't know what happened, but it isn't good.\r
594 ReturnStatus = EFI_ABORTED;\r
595 goto Exit;\r
596 }\r
597 }\r
598 }\r
599\r
600Exit:\r
1436aea4 601 DEBUG ((DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __FUNCTION__, VendorGuid, VariableName, ReturnStatus));\r
355b181f
BB
602 return ReturnStatus;\r
603}\r
604\r
355b181f
BB
605/**\r
606 This API function disables the variable policy enforcement. If it's\r
607 already been called once, will return EFI_ALREADY_STARTED.\r
608\r
609 @retval EFI_SUCCESS\r
610 @retval EFI_ALREADY_STARTED Has already been called once this boot.\r
611 @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.\r
612 @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.\r
613 @retval EFI_NOT_READY Library has not yet been initialized.\r
614\r
615**/\r
616EFI_STATUS\r
617EFIAPI\r
618DisableVariablePolicy (\r
619 VOID\r
620 )\r
621{\r
1436aea4 622 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
623 return EFI_NOT_READY;\r
624 }\r
1436aea4 625\r
355b181f
BB
626 if (mProtectionDisabled) {\r
627 return EFI_ALREADY_STARTED;\r
628 }\r
1436aea4 629\r
355b181f
BB
630 if (mInterfaceLocked) {\r
631 return EFI_WRITE_PROTECTED;\r
632 }\r
1436aea4 633\r
355b181f
BB
634 if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {\r
635 return EFI_WRITE_PROTECTED;\r
636 }\r
1436aea4 637\r
355b181f
BB
638 mProtectionDisabled = TRUE;\r
639 return EFI_SUCCESS;\r
640}\r
641\r
355b181f
BB
642/**\r
643 This API function will dump the entire contents of the variable policy table.\r
644\r
645 Similar to GetVariable, the first call can be made with a 0 size and it will return\r
646 the size of the buffer required to hold the entire table.\r
647\r
648 @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.\r
649 @param[in,out] Size On input, the size of the output buffer. On output, the size\r
650 of the data returned.\r
651\r
652 @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.\r
653 @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.\r
654 @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.\r
655 @retval EFI_NOT_READY Library has not yet been initialized.\r
656\r
657**/\r
658EFI_STATUS\r
659EFIAPI\r
660DumpVariablePolicy (\r
1436aea4
MK
661 OUT UINT8 *Policy,\r
662 IN OUT UINT32 *Size\r
355b181f
BB
663 )\r
664{\r
1436aea4 665 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
666 return EFI_NOT_READY;\r
667 }\r
668\r
669 // Check the parameters.\r
1436aea4 670 if ((Size == NULL) || ((*Size > 0) && (Policy == NULL))) {\r
355b181f
BB
671 return EFI_INVALID_PARAMETER;\r
672 }\r
673\r
674 // Make sure the size is sufficient to hold the policy table.\r
675 if (*Size < mCurrentTableUsage) {\r
676 *Size = mCurrentTableUsage;\r
677 return EFI_BUFFER_TOO_SMALL;\r
678 }\r
679\r
680 // If we're still here, copy the table and bounce.\r
1436aea4 681 CopyMem (Policy, mPolicyTable, mCurrentTableUsage);\r
355b181f
BB
682 *Size = mCurrentTableUsage;\r
683\r
684 return EFI_SUCCESS;\r
685}\r
686\r
355b181f
BB
687/**\r
688 This API function returns whether or not the policy engine is\r
689 currently being enforced.\r
690\r
691 @retval TRUE\r
692 @retval FALSE\r
693 @retval FALSE Library has not yet been initialized.\r
694\r
695**/\r
696BOOLEAN\r
697EFIAPI\r
698IsVariablePolicyEnabled (\r
699 VOID\r
700 )\r
701{\r
1436aea4 702 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
703 return FALSE;\r
704 }\r
1436aea4 705\r
355b181f
BB
706 return !mProtectionDisabled;\r
707}\r
708\r
355b181f
BB
709/**\r
710 This API function locks the interface so that no more policy updates\r
711 can be performed or changes made to the enforcement until the next boot.\r
712\r
713 @retval EFI_SUCCESS\r
714 @retval EFI_NOT_READY Library has not yet been initialized.\r
715\r
716**/\r
717EFI_STATUS\r
718EFIAPI\r
719LockVariablePolicy (\r
720 VOID\r
721 )\r
722{\r
1436aea4 723 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
724 return EFI_NOT_READY;\r
725 }\r
1436aea4 726\r
355b181f
BB
727 if (mInterfaceLocked) {\r
728 return EFI_WRITE_PROTECTED;\r
729 }\r
1436aea4 730\r
355b181f
BB
731 mInterfaceLocked = TRUE;\r
732 return EFI_SUCCESS;\r
733}\r
734\r
355b181f
BB
735/**\r
736 This API function returns whether or not the policy interface is locked\r
737 for the remainder of the boot.\r
738\r
739 @retval TRUE\r
740 @retval FALSE\r
741 @retval FALSE Library has not yet been initialized.\r
742\r
743**/\r
744BOOLEAN\r
745EFIAPI\r
746IsVariablePolicyInterfaceLocked (\r
747 VOID\r
748 )\r
749{\r
1436aea4 750 if (!IsVariablePolicyLibInitialized ()) {\r
355b181f
BB
751 return FALSE;\r
752 }\r
1436aea4 753\r
355b181f
BB
754 return mInterfaceLocked;\r
755}\r
756\r
355b181f
BB
757/**\r
758 This helper function initializes the library and sets\r
759 up any required internal structures or handlers.\r
760\r
761 Also registers the internal pointer for the GetVariable helper.\r
762\r
763 @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to\r
764 check policy criteria that involve the existence of other variables.\r
765\r
766 @retval EFI_SUCCESS\r
767 @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to\r
768 deinitialize.\r
769\r
770**/\r
771EFI_STATUS\r
772EFIAPI\r
773InitVariablePolicyLib (\r
1436aea4 774 IN EFI_GET_VARIABLE GetVariableHelper\r
355b181f
BB
775 )\r
776{\r
1436aea4 777 EFI_STATUS Status;\r
355b181f
BB
778\r
779 Status = EFI_SUCCESS;\r
780\r
781 if (mGetVariableHelper != NULL) {\r
782 return EFI_ALREADY_STARTED;\r
783 }\r
784\r
1436aea4
MK
785 if (!EFI_ERROR (Status)) {\r
786 Status = VariablePolicyExtraInit ();\r
355b181f
BB
787 }\r
788\r
1436aea4 789 if (!EFI_ERROR (Status)) {\r
355b181f
BB
790 // Save an internal pointer to the GetVariableHelper.\r
791 mGetVariableHelper = GetVariableHelper;\r
792\r
793 // Initialize the global state.\r
1436aea4 794 mInterfaceLocked = FALSE;\r
355b181f 795 mProtectionDisabled = FALSE;\r
1436aea4
MK
796 mPolicyTable = NULL;\r
797 mCurrentTableSize = 0;\r
798 mCurrentTableUsage = 0;\r
799 mCurrentTableCount = 0;\r
355b181f
BB
800 }\r
801\r
802 return Status;\r
803}\r
804\r
355b181f
BB
805/**\r
806 This helper function returns whether or not the library is currently initialized.\r
807\r
808 @retval TRUE\r
809 @retval FALSE\r
810\r
811**/\r
812BOOLEAN\r
813EFIAPI\r
814IsVariablePolicyLibInitialized (\r
815 VOID\r
816 )\r
817{\r
818 return (mGetVariableHelper != NULL);\r
819}\r
820\r
355b181f
BB
821/**\r
822 This helper function tears down the library.\r
823\r
824 Should generally only be used for test harnesses.\r
825\r
826 @retval EFI_SUCCESS\r
827 @retval EFI_NOT_READY Deinitialize was called without first calling initialize.\r
828\r
829**/\r
830EFI_STATUS\r
831EFIAPI\r
832DeinitVariablePolicyLib (\r
833 VOID\r
834 )\r
835{\r
1436aea4 836 EFI_STATUS Status;\r
355b181f
BB
837\r
838 Status = EFI_SUCCESS;\r
839\r
840 if (mGetVariableHelper == NULL) {\r
841 return EFI_NOT_READY;\r
842 }\r
843\r
1436aea4
MK
844 if (!EFI_ERROR (Status)) {\r
845 Status = VariablePolicyExtraDeinit ();\r
355b181f
BB
846 }\r
847\r
1436aea4
MK
848 if (!EFI_ERROR (Status)) {\r
849 mGetVariableHelper = NULL;\r
850 mInterfaceLocked = FALSE;\r
355b181f 851 mProtectionDisabled = FALSE;\r
1436aea4
MK
852 mCurrentTableSize = 0;\r
853 mCurrentTableUsage = 0;\r
854 mCurrentTableCount = 0;\r
355b181f
BB
855\r
856 if (mPolicyTable != NULL) {\r
1436aea4 857 FreePool (mPolicyTable);\r
355b181f
BB
858 mPolicyTable = NULL;\r
859 }\r
860 }\r
861\r
862 return Status;\r
863}\r