VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c

Last change on this file was 105670, checked in by vboxsync, 8 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 40.1 KB
Line 
1/** @file -- VariablePolicyLib.c
2Business logic for Variable Policy enforcement.
3
4Copyright (c) Microsoft Corporation.
5SPDX-License-Identifier: BSD-2-Clause-Patent
6
7**/
8
9#include <Uefi.h>
10
11#include <Library/BaseLib.h>
12#include <Library/SafeIntLib.h>
13#include <Library/MemoryAllocationLib.h>
14#include <Library/BaseMemoryLib.h>
15#include <Library/DebugLib.h>
16#include <Library/PcdLib.h>
17
18#include <Protocol/VariablePolicy.h>
19#include <Library/VariablePolicyLib.h>
20
21// IMPORTANT NOTE: This library is currently rife with multiple return statements
22// for error handling. A refactor should remove these at some point.
23
24//
25// This library was designed with advanced unit-test features.
26// This define handles the configuration.
27#ifdef INTERNAL_UNIT_TEST
28 #undef STATIC
29#define STATIC // Nothing...
30#endif
31
32// An abstracted GetVariable interface that enables configuration regardless of the environment.
33EFI_GET_VARIABLE mGetVariableHelper = NULL;
34
35// Master switch to lock this entire interface. Does not stop enforcement,
36// just prevents the configuration from being changed for the rest of the boot.
37STATIC BOOLEAN mInterfaceLocked = FALSE;
38
39// Master switch to disable the entire interface for a single boot.
40// This will disable all policy enforcement for the duration of the boot.
41STATIC BOOLEAN mProtectionDisabled = FALSE;
42
43// Table to hold all the current policies.
44UINT8 *mPolicyTable = NULL;
45STATIC UINT32 mCurrentTableSize = 0;
46STATIC UINT32 mCurrentTableUsage = 0;
47STATIC UINT32 mCurrentTableCount = 0;
48
49#define POLICY_TABLE_STEP_SIZE 0x1000
50
51// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.
52// Current table data has already been sanitized.
53#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)
54#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)
55
56#define MATCH_PRIORITY_EXACT 0
57#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT
58#define MATCH_PRIORITY_MIN MAX_UINT8
59
60/**
61 An extra init hook that enables the RuntimeDxe library instance to
62 register VirtualAddress change callbacks. Among other things.
63
64 @retval EFI_SUCCESS Everything is good. Continue with init.
65 @retval Others Uh... don't continue.
66
67**/
68EFI_STATUS
69VariablePolicyExtraInit (
70 VOID
71 );
72
73/**
74 An extra deinit hook that enables the RuntimeDxe library instance to
75 register VirtualAddress change callbacks. Among other things.
76
77 @retval EFI_SUCCESS Everything is good. Continue with deinit.
78 @retval Others Uh... don't continue.
79
80**/
81EFI_STATUS
82VariablePolicyExtraDeinit (
83 VOID
84 );
85
86/**
87 This helper function determines whether the structure of an incoming policy
88 is valid and internally consistent.
89
90 @param[in] NewPolicy Pointer to the incoming policy structure.
91
92 @retval TRUE
93 @retval FALSE Pointer is NULL, size is wrong, strings are empty, or
94 substructures overlap.
95
96**/
97STATIC
98BOOLEAN
99IsValidVariablePolicyStructure (
100 IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
101 )
102{
103 EFI_STATUS Status;
104 UINTN EntryEnd;
105 CHAR16 *CheckChar;
106 UINTN WildcardCount;
107
108 // Sanitize some quick values.
109 if ((NewPolicy == NULL) || (NewPolicy->Size == 0) ||
110 // Structure size should be at least as long as the minumum structure and a NULL string.
111 (NewPolicy->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||
112 // Check for the known revision.
113 (NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION))
114 {
115 return FALSE;
116 }
117
118 // Calculate the theoretical end of the structure and make sure
119 // that the structure can fit in memory.
120 Status = SafeUintnAdd ((UINTN)NewPolicy, NewPolicy->Size, &EntryEnd);
121 if (EFI_ERROR (Status)) {
122 return FALSE;
123 }
124
125 // Check for a valid Max Size.
126 if (NewPolicy->MaxSize == 0) {
127 return FALSE;
128 }
129
130 // Check for the valid list of lock policies.
131 if ((NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK) &&
132 (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW) &&
133 (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) &&
134 (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE))
135 {
136 return FALSE;
137 }
138
139 // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name
140 // terminates before the OffsetToName for the matching policy variable Name.
141 if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
142 // Adjust CheckChar to the offset of the LockPolicy->Name.
143 Status = SafeUintnAdd (
144 (UINTN)NewPolicy + sizeof (VARIABLE_POLICY_ENTRY),
145 sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),
146 (UINTN *)&CheckChar
147 );
148 if (EFI_ERROR (Status) || (EntryEnd <= (UINTN)CheckChar)) {
149 return FALSE;
150 }
151
152 while (*CheckChar != CHAR_NULL) {
153 if (EntryEnd <= (UINTN)CheckChar) {
154 return FALSE;
155 }
156
157 CheckChar++;
158 }
159
160 // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.
161 // We should check to make sure that the policy Name comes immediately after this charcter.
162 if ((UINTN)++ CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {
163 return FALSE;
164 }
165
166 // If the policy type is any other value, make sure that the LockPolicy structure has a zero length.
167 } else {
168 if (NewPolicy->OffsetToName != sizeof (VARIABLE_POLICY_ENTRY)) {
169 return FALSE;
170 }
171 }
172
173 // Check to make sure that the name has a terminating character
174 // before the end of the structure.
175 // We've already checked that the name is within the bounds of the structure.
176 if (NewPolicy->Size != NewPolicy->OffsetToName) {
177 CheckChar = (CHAR16 *)((UINTN)NewPolicy + NewPolicy->OffsetToName);
178 WildcardCount = 0;
179 while (*CheckChar != CHAR_NULL) {
180 // Make sure there aren't excessive wildcards.
181 if (*CheckChar == '#') {
182 WildcardCount++;
183 if (WildcardCount > MATCH_PRIORITY_MIN) {
184 return FALSE;
185 }
186 }
187
188 // Make sure you're still within the bounds of the policy structure.
189 if (EntryEnd <= (UINTN)CheckChar) {
190 return FALSE;
191 }
192
193 CheckChar++;
194 }
195
196 // Finally, we should be pointed at the very last character in Name, so we should be right
197 // up against the end of the structure.
198 if ((UINTN)++ CheckChar != EntryEnd) {
199 return FALSE;
200 }
201 }
202
203 return TRUE;
204}
205
206/**
207 This helper function evaluates a policy and determines whether it matches the target
208 variable. If matched, will also return a value corresponding to the priority of the match.
209
210 The rules for "best match" are listed in the Variable Policy Spec.
211 Perfect name matches will return 0.
212 Single wildcard characters will return the number of wildcard characters.
213 Full namespaces will return MAX_UINT8.
214
215 @param[in] EvalEntry Pointer to the policy entry being evaluated.
216 @param[in] VariableName Same as EFI_SET_VARIABLE.
217 @param[in] VendorGuid Same as EFI_SET_VARIABLE.
218 @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.
219 Lower number == higher priority. Only valid if a match found.
220
221 @retval TRUE Current entry matches the target variable.
222 @retval FALSE Current entry does not match at all.
223
224**/
225STATIC
226BOOLEAN
227EvaluatePolicyMatch (
228 IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,
229 IN CONST CHAR16 *VariableName,
230 IN CONST EFI_GUID *VendorGuid,
231 OUT UINT8 *MatchPriority OPTIONAL
232 )
233{
234 BOOLEAN Result;
235 CHAR16 *PolicyName;
236 UINT8 CalculatedPriority;
237 UINTN Index;
238
239 Result = FALSE;
240 CalculatedPriority = MATCH_PRIORITY_EXACT;
241
242 // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.
243 if (!CompareGuid (&EvalEntry->Namespace, VendorGuid)) {
244 goto Exit;
245 }
246
247 // If the GUID matches, check to see whether there is a Name associated
248 // with the policy. If not, this policy matches the entire namespace.
249 // Missing Name is indicated by size being equal to name.
250 if (EvalEntry->Size == EvalEntry->OffsetToName) {
251 CalculatedPriority = MATCH_PRIORITY_MIN;
252 Result = TRUE;
253 goto Exit;
254 }
255
256 // Now that we know the name exists, get it.
257 PolicyName = GET_POLICY_NAME (EvalEntry);
258
259 // Evaluate the name against the policy name and check for a match.
260 // Account for any wildcards.
261 Index = 0;
262 Result = TRUE;
263 // Keep going until the end of both strings.
264 while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {
265 // If we don't have a match...
266 if ((PolicyName[Index] != VariableName[Index]) || (PolicyName[Index] == '#')) {
267 // If this is a numerical wildcard, we can consider
268 // it a match if we alter the priority.
269 if ((PolicyName[Index] == L'#') &&
270 (((L'0' <= VariableName[Index]) && (VariableName[Index] <= L'9')) ||
271 ((L'A' <= VariableName[Index]) && (VariableName[Index] <= L'F')) ||
272 ((L'a' <= VariableName[Index]) && (VariableName[Index] <= L'f'))))
273 {
274 if (CalculatedPriority < MATCH_PRIORITY_MIN) {
275 CalculatedPriority++;
276 }
277
278 // Otherwise, not a match.
279 } else {
280 Result = FALSE;
281 goto Exit;
282 }
283 }
284
285 Index++;
286 }
287
288Exit:
289 if (Result && (MatchPriority != NULL)) {
290 *MatchPriority = CalculatedPriority;
291 }
292
293 return Result;
294}
295
296/**
297 This helper function walks the current policy table and returns a pointer
298 to the best match, if any are found. Leverages EvaluatePolicyMatch() to
299 determine "best".
300
301 @param[in] VariableName Same as EFI_SET_VARIABLE.
302 @param[in] VendorGuid Same as EFI_SET_VARIABLE.
303 @param[out] ReturnPriority [Optional] If pointer is provided, return the
304 priority of the match. Same as EvaluatePolicyMatch().
305 Only valid if a match is returned.
306
307 @retval VARIABLE_POLICY_ENTRY* Best match that was found.
308 @retval NULL No match was found.
309
310**/
311STATIC
312VARIABLE_POLICY_ENTRY *
313GetBestPolicyMatch (
314 IN CONST CHAR16 *VariableName,
315 IN CONST EFI_GUID *VendorGuid,
316 OUT UINT8 *ReturnPriority OPTIONAL
317 )
318{
319 VARIABLE_POLICY_ENTRY *BestResult;
320 VARIABLE_POLICY_ENTRY *CurrentEntry;
321 UINT8 MatchPriority;
322 UINT8 CurrentPriority;
323 UINTN Index;
324
325 BestResult = NULL;
326 MatchPriority = MATCH_PRIORITY_EXACT;
327
328 // Walk all entries in the table, looking for matches.
329 CurrentEntry = (VARIABLE_POLICY_ENTRY *)mPolicyTable;
330 for (Index = 0; Index < mCurrentTableCount; Index++) {
331 // Check for a match.
332 if (EvaluatePolicyMatch (CurrentEntry, VariableName, VendorGuid, &CurrentPriority)) {
333 // If match is better, take it.
334 if ((BestResult == NULL) || (CurrentPriority < MatchPriority)) {
335 BestResult = CurrentEntry;
336 MatchPriority = CurrentPriority;
337 }
338
339 // If you've hit the highest-priority match, can exit now.
340 if (MatchPriority == 0) {
341 break;
342 }
343 }
344
345 // If we're still in the loop, move to the next entry.
346 CurrentEntry = GET_NEXT_POLICY (CurrentEntry);
347 }
348
349 // If a return priority was requested, return it.
350 if (ReturnPriority != NULL) {
351 *ReturnPriority = MatchPriority;
352 }
353
354 return BestResult;
355}
356
357/**
358 This API function validates and registers a new policy with
359 the policy enforcement engine.
360
361 @param[in] NewPolicy Pointer to the incoming policy structure.
362
363 @retval EFI_SUCCESS
364 @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
365 @retval EFI_ALREADY_STARTED An identical matching policy already exists.
366 @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
367 @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
368 @retval EFI_ABORTED A calculation error has prevented this function from completing.
369 @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
370 @retval EFI_NOT_READY Library has not yet been initialized.
371
372**/
373EFI_STATUS
374EFIAPI
375RegisterVariablePolicy (
376 IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
377 )
378{
379 EFI_STATUS Status;
380 VARIABLE_POLICY_ENTRY *MatchPolicy;
381 UINT8 MatchPriority;
382 UINT32 NewSize;
383 UINT8 *NewTable;
384
385 if (!IsVariablePolicyLibInitialized ()) {
386 return EFI_NOT_READY;
387 }
388
389 if (mInterfaceLocked) {
390 return EFI_WRITE_PROTECTED;
391 }
392
393 if (!IsValidVariablePolicyStructure (NewPolicy)) {
394 return EFI_INVALID_PARAMETER;
395 }
396
397 // Check to see whether an exact matching policy already exists.
398 MatchPolicy = GetBestPolicyMatch (
399 GET_POLICY_NAME (NewPolicy),
400 &NewPolicy->Namespace,
401 &MatchPriority
402 );
403 if ((MatchPolicy != NULL) && (MatchPriority == MATCH_PRIORITY_EXACT)) {
404 return EFI_ALREADY_STARTED;
405 }
406
407 // If none exists, create it.
408 // If we need more space, allocate that now.
409 Status = SafeUint32Add (mCurrentTableUsage, NewPolicy->Size, &NewSize);
410 if (EFI_ERROR (Status)) {
411 return EFI_ABORTED;
412 }
413
414 if (NewSize > mCurrentTableSize) {
415 // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.
416 NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?
417 (NewSize / POLICY_TABLE_STEP_SIZE) + 1 :
418 (NewSize / POLICY_TABLE_STEP_SIZE);
419 // Calculate the new table size in absolute bytes.
420 Status = SafeUint32Mult (NewSize, POLICY_TABLE_STEP_SIZE, &NewSize);
421 if (EFI_ERROR (Status)) {
422 return EFI_ABORTED;
423 }
424
425 // Reallocate and copy the table.
426 NewTable = AllocateRuntimePool (NewSize);
427 if (NewTable == NULL) {
428 return EFI_OUT_OF_RESOURCES;
429 }
430
431 CopyMem (NewTable, mPolicyTable, mCurrentTableUsage);
432 mCurrentTableSize = NewSize;
433 if (mPolicyTable != NULL) {
434 FreePool (mPolicyTable);
435 }
436
437 mPolicyTable = NewTable;
438 }
439
440 // Copy the policy into the table.
441 CopyMem (mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size);
442 mCurrentTableUsage += NewPolicy->Size;
443 mCurrentTableCount += 1;
444
445 // We're done here.
446
447 return EFI_SUCCESS;
448}
449
450/**
451 This API function checks to see whether the parameters to SetVariable would
452 be allowed according to the current variable policies.
453
454 @param[in] VariableName Same as EFI_SET_VARIABLE.
455 @param[in] VendorGuid Same as EFI_SET_VARIABLE.
456 @param[in] Attributes Same as EFI_SET_VARIABLE.
457 @param[in] DataSize Same as EFI_SET_VARIABLE.
458 @param[in] Data Same as EFI_SET_VARIABLE.
459
460 @retval EFI_SUCCESS A matching policy allows this update.
461 @retval EFI_SUCCESS There are currently no policies that restrict this update.
462 @retval EFI_SUCCESS The protections have been disable until the next reboot.
463 @retval EFI_WRITE_PROTECTED Variable is currently locked.
464 @retval EFI_INVALID_PARAMETER Attributes or size are invalid.
465 @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.
466 @retval EFI_NOT_READY Library has not been initialized.
467
468**/
469EFI_STATUS
470EFIAPI
471ValidateSetVariable (
472 IN CHAR16 *VariableName,
473 IN EFI_GUID *VendorGuid,
474 IN UINT32 Attributes,
475 IN UINTN DataSize,
476 IN VOID *Data
477 )
478{
479 BOOLEAN IsDel;
480 VARIABLE_POLICY_ENTRY *ActivePolicy;
481 EFI_STATUS Status;
482 EFI_STATUS ReturnStatus;
483 VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;
484 CHAR16 *StateVarName;
485 UINTN StateVarSize;
486 UINT8 StateVar;
487
488 ReturnStatus = EFI_SUCCESS;
489
490 if (!IsVariablePolicyLibInitialized ()) {
491 ReturnStatus = EFI_NOT_READY;
492 goto Exit;
493 }
494
495 // Bail if the protections are currently disabled.
496 if (mProtectionDisabled) {
497 ReturnStatus = EFI_SUCCESS;
498 goto Exit;
499 }
500
501 // Determine whether this is a delete operation.
502 // If so, it will affect which tests are applied.
503 if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
504 IsDel = TRUE;
505 } else {
506 IsDel = FALSE;
507 }
508
509 // Find an active policy if one exists.
510 ActivePolicy = GetBestPolicyMatch (VariableName, VendorGuid, NULL);
511
512 // If we have an active policy, check it against the incoming data.
513 if (ActivePolicy != NULL) {
514 //
515 // Only enforce size and attribute constraints when updating data, not deleting.
516 if (!IsDel) {
517 // Check for size constraints.
518 if (((ActivePolicy->MinSize > 0) && (DataSize < ActivePolicy->MinSize)) ||
519 ((ActivePolicy->MaxSize > 0) && (DataSize > ActivePolicy->MaxSize)))
520 {
521 ReturnStatus = EFI_INVALID_PARAMETER;
522 DEBUG ((
523 DEBUG_VERBOSE,
524 "%a - Bad Size. 0x%X <> 0x%X-0x%X\n",
525 __func__,
526 DataSize,
527 ActivePolicy->MinSize,
528 ActivePolicy->MaxSize
529 ));
530 goto Exit;
531 }
532
533 // Check for attribute constraints.
534 if (((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave) ||
535 ((ActivePolicy->AttributesCantHave & Attributes) != 0))
536 {
537 ReturnStatus = EFI_INVALID_PARAMETER;
538 DEBUG ((
539 DEBUG_VERBOSE,
540 "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n",
541 __func__,
542 Attributes,
543 ActivePolicy->AttributesMustHave,
544 ActivePolicy->AttributesCantHave
545 ));
546 goto Exit;
547 }
548 }
549
550 //
551 // Lock policy check.
552 //
553 // Check for immediate lock.
554 if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {
555 ReturnStatus = EFI_WRITE_PROTECTED;
556 goto Exit;
557 // Check for lock on create.
558 } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {
559 StateVarSize = 0;
560 Status = mGetVariableHelper (
561 VariableName,
562 VendorGuid,
563 NULL,
564 &StateVarSize,
565 NULL
566 );
567 if (Status == EFI_BUFFER_TOO_SMALL) {
568 ReturnStatus = EFI_WRITE_PROTECTED;
569 goto Exit;
570 }
571
572 // Check for lock on state variable.
573 } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
574 StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)((UINT8 *)ActivePolicy + sizeof (VARIABLE_POLICY_ENTRY));
575 StateVarName = (CHAR16 *)((UINT8 *)StateVarPolicy + sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY));
576 StateVarSize = sizeof (StateVar);
577 Status = mGetVariableHelper (
578 StateVarName,
579 &StateVarPolicy->Namespace,
580 NULL,
581 &StateVarSize,
582 &StateVar
583 );
584
585 // If the variable was found, check the state. If matched, this variable is locked.
586 if (!EFI_ERROR (Status)) {
587 if (StateVar == StateVarPolicy->Value) {
588 ReturnStatus = EFI_WRITE_PROTECTED;
589 goto Exit;
590 }
591
592 // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.
593 } else if ((Status != EFI_NOT_FOUND) && (Status != EFI_BUFFER_TOO_SMALL)) {
594 // We don't know what happened, but it isn't good.
595 ReturnStatus = EFI_ABORTED;
596 goto Exit;
597 }
598 }
599 }
600
601Exit:
602 DEBUG ((DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __func__, VendorGuid, VariableName, ReturnStatus));
603 return ReturnStatus;
604}
605
606/**
607 This API function disables the variable policy enforcement. If it's
608 already been called once, will return EFI_ALREADY_STARTED.
609
610 @retval EFI_SUCCESS
611 @retval EFI_ALREADY_STARTED Has already been called once this boot.
612 @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
613 @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
614 @retval EFI_NOT_READY Library has not yet been initialized.
615
616**/
617EFI_STATUS
618EFIAPI
619DisableVariablePolicy (
620 VOID
621 )
622{
623 if (!IsVariablePolicyLibInitialized ()) {
624 return EFI_NOT_READY;
625 }
626
627 if (mProtectionDisabled) {
628 return EFI_ALREADY_STARTED;
629 }
630
631 if (mInterfaceLocked) {
632 return EFI_WRITE_PROTECTED;
633 }
634
635 if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
636 return EFI_WRITE_PROTECTED;
637 }
638
639 mProtectionDisabled = TRUE;
640 return EFI_SUCCESS;
641}
642
643/**
644 This API function will dump the entire contents of the variable policy table.
645
646 Similar to GetVariable, the first call can be made with a 0 size and it will return
647 the size of the buffer required to hold the entire table.
648
649 @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
650 @param[in,out] Size On input, the size of the output buffer. On output, the size
651 of the data returned.
652
653 @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
654 @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
655 @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
656 @retval EFI_NOT_READY Library has not yet been initialized.
657
658**/
659EFI_STATUS
660EFIAPI
661DumpVariablePolicy (
662 OUT UINT8 *Policy,
663 IN OUT UINT32 *Size
664 )
665{
666 if (!IsVariablePolicyLibInitialized ()) {
667 return EFI_NOT_READY;
668 }
669
670 // Check the parameters.
671 if ((Size == NULL) || ((*Size > 0) && (Policy == NULL))) {
672 return EFI_INVALID_PARAMETER;
673 }
674
675 // Make sure the size is sufficient to hold the policy table.
676 if (*Size < mCurrentTableUsage) {
677 *Size = mCurrentTableUsage;
678 return EFI_BUFFER_TOO_SMALL;
679 }
680
681 // If we're still here, copy the table and bounce.
682 CopyMem (Policy, mPolicyTable, mCurrentTableUsage);
683 *Size = mCurrentTableUsage;
684
685 return EFI_SUCCESS;
686}
687
688/**
689 This function will return variable policy information for a UEFI variable with a
690 registered variable policy.
691
692 @param[in] VariableName The name of the variable to use for the policy search.
693 @param[in] VendorGuid The vendor GUID of the variable to use for the policy search.
694 @param[in,out] VariablePolicyVariableNameBufferSize On input, the size, in bytes, of the VariablePolicyVariableName
695 buffer.
696
697 On output, the size, in bytes, needed to store the variable
698 policy variable name.
699
700 If testing for the VariablePolicyVariableName buffer size
701 needed, set this value to zero so EFI_BUFFER_TOO_SMALL is
702 guaranteed to be returned if the variable policy variable name
703 is found.
704 @param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
705 if found.
706 @param[out] VariablePolicyVariableName Pointer to a buffer where the variable name used for the
707 variable policy will be written if a variable name is
708 registered.
709
710 If the variable policy is not associated with a variable name
711 (e.g. applied to variable vendor namespace) and this parameter
712 is given, this parameter will not be modified and
713 VariablePolicyVariableNameBufferSize will be set to zero to
714 indicate a name was not present.
715
716 If the pointer given is not NULL,
717 VariablePolicyVariableNameBufferSize must be non-NULL.
718
719 @retval EFI_SUCCESS A variable policy entry was found and returned successfully.
720 @retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
721 @retval EFI_BUFFER_TOO_SMALL The VariablePolicyVariableName buffer value is too small for the size needed.
722 The buffer should now point to the size needed.
723 @retval EFI_NOT_READY Variable policy has not yet been initialized.
724 @retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
725 VariablePolicyVariableName is non-NULL and VariablePolicyVariableNameBufferSize
726 is NULL.
727 @retval EFI_NOT_FOUND A variable policy was not found for the given UEFI variable name and vendor GUID.
728
729**/
730EFI_STATUS
731EFIAPI
732GetVariablePolicyInfo (
733 IN CONST CHAR16 *VariableName,
734 IN CONST EFI_GUID *VendorGuid,
735 IN OUT UINTN *VariablePolicyVariableNameBufferSize OPTIONAL,
736 OUT VARIABLE_POLICY_ENTRY *VariablePolicy,
737 OUT CHAR16 *VariablePolicyVariableName OPTIONAL
738 )
739{
740 EFI_STATUS Status;
741 UINT8 MatchPriority;
742 UINTN LocalVariablePolicyVariableNameBufferSize;
743 UINTN RequiredVariablePolicyVariableNameBufferSize;
744 VARIABLE_POLICY_ENTRY *MatchPolicy;
745
746 Status = EFI_SUCCESS;
747
748 if (!IsVariablePolicyLibInitialized ()) {
749 return EFI_NOT_READY;
750 }
751
752 if ((VariableName == NULL) || (VendorGuid == NULL) || (VariablePolicy == NULL)) {
753 return EFI_INVALID_PARAMETER;
754 }
755
756 MatchPolicy = GetBestPolicyMatch (
757 VariableName,
758 VendorGuid,
759 &MatchPriority
760 );
761 if (MatchPolicy != NULL) {
762 CopyMem (VariablePolicy, MatchPolicy, sizeof (*VariablePolicy));
763
764 if (VariablePolicyVariableNameBufferSize == NULL) {
765 if (VariablePolicyVariableName != NULL) {
766 return EFI_INVALID_PARAMETER;
767 }
768
769 return Status;
770 }
771
772 if (MatchPolicy->Size != MatchPolicy->OffsetToName) {
773 if (MatchPolicy->Size < MatchPolicy->OffsetToName) {
774 ASSERT (MatchPolicy->Size > MatchPolicy->OffsetToName);
775 return EFI_BAD_BUFFER_SIZE;
776 }
777
778 RequiredVariablePolicyVariableNameBufferSize = (UINTN)(MatchPolicy->Size - MatchPolicy->OffsetToName);
779 ASSERT (RequiredVariablePolicyVariableNameBufferSize > 0);
780
781 if (*VariablePolicyVariableNameBufferSize < RequiredVariablePolicyVariableNameBufferSize) {
782 // Let the caller get the size needed to hold the policy variable name
783 *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
784 return EFI_BUFFER_TOO_SMALL;
785 }
786
787 if (VariablePolicyVariableName == NULL) {
788 // If the policy variable name size given is valid, then a valid policy variable name buffer should be provided
789 *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
790 return EFI_INVALID_PARAMETER;
791 }
792
793 LocalVariablePolicyVariableNameBufferSize = *VariablePolicyVariableNameBufferSize;
794
795 // Actual string size should match expected string size
796 if (
797 ((StrnLenS (GET_POLICY_NAME (MatchPolicy), RequiredVariablePolicyVariableNameBufferSize) + 1) * sizeof (CHAR16))
798 != RequiredVariablePolicyVariableNameBufferSize)
799 {
800 ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
801 return EFI_BAD_BUFFER_SIZE;
802 }
803
804 *VariablePolicyVariableNameBufferSize = RequiredVariablePolicyVariableNameBufferSize;
805
806 Status = StrnCpyS (
807 VariablePolicyVariableName,
808 LocalVariablePolicyVariableNameBufferSize / sizeof (CHAR16),
809 GET_POLICY_NAME (MatchPolicy),
810 RequiredVariablePolicyVariableNameBufferSize / sizeof (CHAR16)
811 );
812 ASSERT_EFI_ERROR (Status);
813 } else {
814 // A variable policy variable name is not present. Return values according to interface.
815 *VariablePolicyVariableNameBufferSize = 0;
816 }
817
818 return Status;
819 }
820
821 return EFI_NOT_FOUND;
822}
823
824/**
825 This function will return the Lock on Variable State policy information for the policy
826 associated with the given UEFI variable.
827
828 @param[in] VariableName The name of the variable to use for the policy search.
829 @param[in] VendorGuid The vendor GUID of the variable to use for the policy
830 search.
831 @param[in,out] VariableLockPolicyVariableNameBufferSize On input, the size, in bytes, of the
832 VariableLockPolicyVariableName buffer.
833
834 On output, the size, in bytes, needed to store the variable
835 policy variable name.
836
837 If testing for the VariableLockPolicyVariableName buffer
838 size needed, set this value to zero so EFI_BUFFER_TOO_SMALL
839 is guaranteed to be returned if the variable policy variable
840 name is found.
841 @param[out] VariablePolicy Pointer to a buffer where the policy entry will be written
842 if found.
843 @param[out] VariableLockPolicyVariableName Pointer to a buffer where the variable name used for the
844 variable lock on variable state policy will be written if
845 a variable name is registered.
846
847 If the lock on variable policy is not associated with a
848 variable name (e.g. applied to variable vendor namespace)
849 and this parameter is given, this parameter will not be
850 modified and VariableLockPolicyVariableNameBufferSize will
851 be set to zero to indicate a name was not present.
852
853 If the pointer given is not NULL,
854 VariableLockPolicyVariableNameBufferSize must be non-NULL.
855
856 @retval EFI_SUCCESS A Lock on Variable State variable policy entry was found and returned
857 successfully.
858 @retval EFI_BAD_BUFFER_SIZE An internal buffer size caused a calculation error.
859 @retval EFI_BUFFER_TOO_SMALL The VariableLockPolicyVariableName buffer is too small for the size needed.
860 The buffer should now point to the size needed.
861 @retval EFI_NOT_READY Variable policy has not yet been initialized.
862 @retval EFI_INVALID_PARAMETER A required pointer argument passed is NULL. This will be returned if
863 VariableLockPolicyVariableName is non-NULL and
864 VariableLockPolicyVariableNameBufferSize is NULL.
865 @retval EFI_NOT_FOUND A Lock on Variable State variable policy was not found for the given UEFI
866 variable name and vendor GUID.
867
868**/
869EFI_STATUS
870EFIAPI
871GetLockOnVariableStateVariablePolicyInfo (
872 IN CONST CHAR16 *VariableName,
873 IN CONST EFI_GUID *VendorGuid,
874 IN OUT UINTN *VariableLockPolicyVariableNameBufferSize OPTIONAL,
875 OUT VARIABLE_LOCK_ON_VAR_STATE_POLICY *VariablePolicy,
876 OUT CHAR16 *VariableLockPolicyVariableName OPTIONAL
877 )
878{
879 EFI_STATUS Status;
880 UINT8 MatchPriority;
881 UINTN RequiredVariablePolicyVariableNameBufferSize;
882 UINTN RequiredVariableLockPolicyVariableNameBufferSize;
883 UINTN LocalVariablePolicyLockVariableNameBufferSize;
884 UINTN LockOnVarStatePolicyEndOffset;
885 CHAR16 *LocalVariableLockPolicyVariableName;
886 VARIABLE_LOCK_ON_VAR_STATE_POLICY *LocalLockOnVarStatePolicy;
887 VARIABLE_POLICY_ENTRY *MatchPolicy;
888
889 Status = EFI_SUCCESS;
890
891 if (!IsVariablePolicyLibInitialized ()) {
892 return EFI_NOT_READY;
893 }
894
895 if ((VariableName == NULL) || (VendorGuid == NULL) || (VariablePolicy == NULL)) {
896 return EFI_INVALID_PARAMETER;
897 }
898
899 MatchPolicy = GetBestPolicyMatch (
900 VariableName,
901 VendorGuid,
902 &MatchPriority
903 );
904 if (MatchPolicy != NULL) {
905 if (MatchPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
906 return EFI_NOT_FOUND;
907 }
908
909 Status = SafeUintnAdd (
910 sizeof (VARIABLE_POLICY_ENTRY),
911 sizeof (VARIABLE_LOCK_ON_VAR_STATE_POLICY),
912 &LockOnVarStatePolicyEndOffset
913 );
914 if (EFI_ERROR (Status) || (LockOnVarStatePolicyEndOffset > (UINTN)MatchPolicy->Size)) {
915 return EFI_BAD_BUFFER_SIZE;
916 }
917
918 LocalLockOnVarStatePolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY *)(MatchPolicy + 1);
919 CopyMem (VariablePolicy, LocalLockOnVarStatePolicy, sizeof (*LocalLockOnVarStatePolicy));
920
921 if ((VariableLockPolicyVariableNameBufferSize == NULL)) {
922 if (VariableLockPolicyVariableName != NULL) {
923 return EFI_INVALID_PARAMETER;
924 }
925
926 return Status;
927 }
928
929 // The name offset should be less than or equal to the total policy size.
930 if (MatchPolicy->Size < MatchPolicy->OffsetToName) {
931 return EFI_BAD_BUFFER_SIZE;
932 }
933
934 RequiredVariablePolicyVariableNameBufferSize = (UINTN)(MatchPolicy->Size - MatchPolicy->OffsetToName);
935 RequiredVariableLockPolicyVariableNameBufferSize = MatchPolicy->Size -
936 (LockOnVarStatePolicyEndOffset + RequiredVariablePolicyVariableNameBufferSize);
937
938 LocalVariablePolicyLockVariableNameBufferSize = *VariableLockPolicyVariableNameBufferSize;
939 *VariableLockPolicyVariableNameBufferSize = RequiredVariableLockPolicyVariableNameBufferSize;
940
941 if (LocalVariablePolicyLockVariableNameBufferSize < RequiredVariableLockPolicyVariableNameBufferSize) {
942 // Let the caller get the size needed to hold the policy variable name
943 return EFI_BUFFER_TOO_SMALL;
944 }
945
946 if (VariableLockPolicyVariableName == NULL) {
947 // If the policy variable name size given is valid, then a valid policy variable name buffer should be provided
948 return EFI_INVALID_PARAMETER;
949 }
950
951 if (RequiredVariableLockPolicyVariableNameBufferSize == 0) {
952 return Status;
953 }
954
955 LocalVariableLockPolicyVariableName = (CHAR16 *)((UINT8 *)LocalLockOnVarStatePolicy + sizeof (*LocalLockOnVarStatePolicy));
956 *VariableLockPolicyVariableNameBufferSize = RequiredVariableLockPolicyVariableNameBufferSize;
957
958 // Actual string size should match expected string size (if a variable name is present)
959 if (
960 (RequiredVariablePolicyVariableNameBufferSize > 0) &&
961 (((StrnLenS (GET_POLICY_NAME (MatchPolicy), RequiredVariablePolicyVariableNameBufferSize) + 1) * sizeof (CHAR16)) !=
962 RequiredVariablePolicyVariableNameBufferSize))
963 {
964 ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
965 return EFI_BAD_BUFFER_SIZE;
966 }
967
968 // Actual string size should match expected string size (if here, variable lock variable name is present)
969 if (
970 ((StrnLenS (LocalVariableLockPolicyVariableName, RequiredVariableLockPolicyVariableNameBufferSize) + 1) * sizeof (CHAR16)) !=
971 RequiredVariableLockPolicyVariableNameBufferSize)
972 {
973 ASSERT_EFI_ERROR (EFI_BAD_BUFFER_SIZE);
974 return EFI_BAD_BUFFER_SIZE;
975 }
976
977 Status = StrnCpyS (
978 VariableLockPolicyVariableName,
979 LocalVariablePolicyLockVariableNameBufferSize / sizeof (CHAR16),
980 LocalVariableLockPolicyVariableName,
981 RequiredVariableLockPolicyVariableNameBufferSize / sizeof (CHAR16)
982 );
983 ASSERT_EFI_ERROR (Status);
984
985 return Status;
986 }
987
988 return EFI_NOT_FOUND;
989}
990
991/**
992 This API function returns whether or not the policy engine is
993 currently being enforced.
994
995 @retval TRUE
996 @retval FALSE
997 @retval FALSE Library has not yet been initialized.
998
999**/
1000BOOLEAN
1001EFIAPI
1002IsVariablePolicyEnabled (
1003 VOID
1004 )
1005{
1006 if (!IsVariablePolicyLibInitialized ()) {
1007 return FALSE;
1008 }
1009
1010 return !mProtectionDisabled;
1011}
1012
1013/**
1014 This API function locks the interface so that no more policy updates
1015 can be performed or changes made to the enforcement until the next boot.
1016
1017 @retval EFI_SUCCESS
1018 @retval EFI_NOT_READY Library has not yet been initialized.
1019
1020**/
1021EFI_STATUS
1022EFIAPI
1023LockVariablePolicy (
1024 VOID
1025 )
1026{
1027 if (!IsVariablePolicyLibInitialized ()) {
1028 return EFI_NOT_READY;
1029 }
1030
1031 if (mInterfaceLocked) {
1032 return EFI_WRITE_PROTECTED;
1033 }
1034
1035 mInterfaceLocked = TRUE;
1036 return EFI_SUCCESS;
1037}
1038
1039/**
1040 This API function returns whether or not the policy interface is locked
1041 for the remainder of the boot.
1042
1043 @retval TRUE
1044 @retval FALSE
1045 @retval FALSE Library has not yet been initialized.
1046
1047**/
1048BOOLEAN
1049EFIAPI
1050IsVariablePolicyInterfaceLocked (
1051 VOID
1052 )
1053{
1054 if (!IsVariablePolicyLibInitialized ()) {
1055 return FALSE;
1056 }
1057
1058 return mInterfaceLocked;
1059}
1060
1061/**
1062 This helper function initializes the library and sets
1063 up any required internal structures or handlers.
1064
1065 Also registers the internal pointer for the GetVariable helper.
1066
1067 @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to
1068 check policy criteria that involve the existence of other variables.
1069
1070 @retval EFI_SUCCESS
1071 @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to
1072 deinitialize.
1073
1074**/
1075EFI_STATUS
1076EFIAPI
1077InitVariablePolicyLib (
1078 IN EFI_GET_VARIABLE GetVariableHelper
1079 )
1080{
1081 EFI_STATUS Status;
1082
1083 Status = EFI_SUCCESS;
1084
1085 if (mGetVariableHelper != NULL) {
1086 return EFI_ALREADY_STARTED;
1087 }
1088
1089 if (!EFI_ERROR (Status)) {
1090 Status = VariablePolicyExtraInit ();
1091 }
1092
1093 if (!EFI_ERROR (Status)) {
1094 // Save an internal pointer to the GetVariableHelper.
1095 mGetVariableHelper = GetVariableHelper;
1096
1097 // Initialize the global state.
1098 mInterfaceLocked = FALSE;
1099 mProtectionDisabled = FALSE;
1100 mPolicyTable = NULL;
1101 mCurrentTableSize = 0;
1102 mCurrentTableUsage = 0;
1103 mCurrentTableCount = 0;
1104 }
1105
1106 return Status;
1107}
1108
1109/**
1110 This helper function returns whether or not the library is currently initialized.
1111
1112 @retval TRUE
1113 @retval FALSE
1114
1115**/
1116BOOLEAN
1117EFIAPI
1118IsVariablePolicyLibInitialized (
1119 VOID
1120 )
1121{
1122 return (mGetVariableHelper != NULL);
1123}
1124
1125/**
1126 This helper function tears down the library.
1127
1128 Should generally only be used for test harnesses.
1129
1130 @retval EFI_SUCCESS
1131 @retval EFI_NOT_READY Deinitialize was called without first calling initialize.
1132
1133**/
1134EFI_STATUS
1135EFIAPI
1136DeinitVariablePolicyLib (
1137 VOID
1138 )
1139{
1140 EFI_STATUS Status;
1141
1142 Status = EFI_SUCCESS;
1143
1144 if (mGetVariableHelper == NULL) {
1145 return EFI_NOT_READY;
1146 }
1147
1148 if (!EFI_ERROR (Status)) {
1149 Status = VariablePolicyExtraDeinit ();
1150 }
1151
1152 if (!EFI_ERROR (Status)) {
1153 mGetVariableHelper = NULL;
1154 mInterfaceLocked = FALSE;
1155 mProtectionDisabled = FALSE;
1156 mCurrentTableSize = 0;
1157 mCurrentTableUsage = 0;
1158 mCurrentTableCount = 0;
1159
1160 if (mPolicyTable != NULL) {
1161 FreePool (mPolicyTable);
1162 mPolicyTable = NULL;
1163 }
1164 }
1165
1166 return Status;
1167}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette