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