mirror of
https://github.com/acidanthera/audk.git
synced 2025-07-23 21:54:27 +02:00
MdeModulePkg: Define the VariablePolicyLib
https://bugzilla.tianocore.org/show_bug.cgi?id=2522 VariablePolicy is an updated interface to replace VarLock and VarCheckProtocol. Add the VariablePolicyLib library that implements the portable business logic for the VariablePolicy engine. Also add host-based CI test cases for the lib. Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Liming Gao <liming.gao@intel.com> Cc: Bret Barkelew <brbarkel@microsoft.com> Signed-off-by: Bret Barkelew <brbarkel@microsoft.com> Reviewed-by: Dandan Bi <dandan.bi@intel.com> Acked-by: Jian J Wang <jian.j.wang@intel.com>
This commit is contained in:
parent
d11e235976
commit
355b181f74
207
MdeModulePkg/Include/Library/VariablePolicyLib.h
Normal file
207
MdeModulePkg/Include/Library/VariablePolicyLib.h
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
/** @file -- VariablePolicyLib.h
|
||||||
|
Business logic for Variable Policy enforcement.
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef _VARIABLE_POLICY_LIB_H_
|
||||||
|
#define _VARIABLE_POLICY_LIB_H_
|
||||||
|
|
||||||
|
#include <Protocol/VariablePolicy.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function validates and registers a new policy with
|
||||||
|
the policy enforcement engine.
|
||||||
|
|
||||||
|
@param[in] NewPolicy Pointer to the incoming policy structure.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
|
||||||
|
@retval EFI_ALREADY_STARTED An identical matching policy already exists.
|
||||||
|
@retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
|
||||||
|
@retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
|
||||||
|
@retval EFI_ABORTED A calculation error has prevented this function from completing.
|
||||||
|
@retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
RegisterVariablePolicy (
|
||||||
|
IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function checks to see whether the parameters to SetVariable would
|
||||||
|
be allowed according to the current variable policies.
|
||||||
|
|
||||||
|
@param[in] VariableName Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] VendorGuid Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] Attributes Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] DataSize Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] Data Same as EFI_SET_VARIABLE.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS A matching policy allows this update.
|
||||||
|
@retval EFI_SUCCESS There are currently no policies that restrict this update.
|
||||||
|
@retval EFI_SUCCESS The protections have been disable until the next reboot.
|
||||||
|
@retval EFI_WRITE_PROTECTED Variable is currently locked.
|
||||||
|
@retval EFI_INVALID_PARAMETER Attributes or size are invalid.
|
||||||
|
@retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.
|
||||||
|
@retval EFI_NOT_READY Library has not been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
ValidateSetVariable (
|
||||||
|
IN CHAR16 *VariableName,
|
||||||
|
IN EFI_GUID *VendorGuid,
|
||||||
|
IN UINT32 Attributes,
|
||||||
|
IN UINTN DataSize,
|
||||||
|
IN VOID *Data
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function disables the variable policy enforcement. If it's
|
||||||
|
already been called once, will return EFI_ALREADY_STARTED.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_ALREADY_STARTED Has already been called once this boot.
|
||||||
|
@retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
|
||||||
|
@retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DisableVariablePolicy (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function will dump the entire contents of the variable policy table.
|
||||||
|
|
||||||
|
Similar to GetVariable, the first call can be made with a 0 size and it will return
|
||||||
|
the size of the buffer required to hold the entire table.
|
||||||
|
|
||||||
|
@param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
|
||||||
|
@param[in,out] Size On input, the size of the output buffer. On output, the size
|
||||||
|
of the data returned.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
|
||||||
|
@retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
|
||||||
|
@retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DumpVariablePolicy (
|
||||||
|
OUT UINT8 *Policy,
|
||||||
|
IN OUT UINT32 *Size
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function returns whether or not the policy engine is
|
||||||
|
currently being enforced.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE
|
||||||
|
@retval FALSE Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
BOOLEAN
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyEnabled (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function locks the interface so that no more policy updates
|
||||||
|
can be performed or changes made to the enforcement until the next boot.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
LockVariablePolicy (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function returns whether or not the policy interface is locked
|
||||||
|
for the remainder of the boot.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE
|
||||||
|
@retval FALSE Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
BOOLEAN
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyInterfaceLocked (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function initializes the library and sets
|
||||||
|
up any required internal structures or handlers.
|
||||||
|
|
||||||
|
Also registers the internal pointer for the GetVariable helper.
|
||||||
|
|
||||||
|
@param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to
|
||||||
|
check policy criteria that involve the existence of other variables.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to
|
||||||
|
deinitialize.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
InitVariablePolicyLib (
|
||||||
|
IN EFI_GET_VARIABLE GetVariableHelper
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function returns whether or not the library is currently initialized.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE
|
||||||
|
|
||||||
|
**/
|
||||||
|
BOOLEAN
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyLibInitialized (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function tears down the library.
|
||||||
|
|
||||||
|
Should generally only be used for test harnesses.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_NOT_READY Deinitialize was called without first calling initialize.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DeinitVariablePolicyLib (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _VARIABLE_POLICY_LIB_H_
|
406
MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
Normal file
406
MdeModulePkg/Library/VariablePolicyLib/ReadMe.md
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
---
|
||||||
|
title: UEFI Variable Policy Whitepaper
|
||||||
|
version: 1.0
|
||||||
|
copyright: Copyright (c) Microsoft Corporation.
|
||||||
|
---
|
||||||
|
|
||||||
|
# UEFI Variable Policy
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
UEFI Variable Policy spec aims to describe the DXE protocol interface
|
||||||
|
which allows enforcing certain rules on certain UEFI variables. The
|
||||||
|
protocol allows communication with the Variable Policy Engine which
|
||||||
|
performs the policy enforcement.
|
||||||
|
|
||||||
|
The Variable Policy is comprised of a set of policy entries which
|
||||||
|
describe, per UEFI variable (identified by namespace GUID and variable
|
||||||
|
name) the following rules:
|
||||||
|
|
||||||
|
- Required variable attributes
|
||||||
|
- Prohibited variable attributes
|
||||||
|
- Minimum variable size
|
||||||
|
- Maximum variable size
|
||||||
|
- Locking:
|
||||||
|
- Locking "immediately"
|
||||||
|
- Locking on creation
|
||||||
|
- Locking based on a state of another variable
|
||||||
|
|
||||||
|
The spec assumes that the Variable Policy Engine runs in a trusted
|
||||||
|
enclave, potentially off the main CPU that runs UEFI. For that reason,
|
||||||
|
it is assumed that the Variable Policy Engine has no concept of UEFI
|
||||||
|
events, and that the communication from the DXE driver to the trusted
|
||||||
|
enclave is proprietary.
|
||||||
|
|
||||||
|
At power-on, the Variable Policy Engine is:
|
||||||
|
|
||||||
|
- Enabled -- present policy entries are evaluated on variable access
|
||||||
|
calls.
|
||||||
|
- Unlocked -- new policy entries can be registered.
|
||||||
|
|
||||||
|
Policy is expected to be clear on power-on. Policy is volatile and not
|
||||||
|
preserved across system reset.
|
||||||
|
|
||||||
|
## DXE Protocol
|
||||||
|
|
||||||
|
```h
|
||||||
|
typedef struct {
|
||||||
|
UINT64 Revision;
|
||||||
|
DISABLE_VARIABLE_POLICY DisableVariablePolicy;
|
||||||
|
IS_VARIABLE_POLICY_ENABLED IsVariablePolicyEnabled;
|
||||||
|
REGISTER_VARIABLE_POLICY RegisterVariablePolicy;
|
||||||
|
DUMP_VARIABLE_POLICY DumpVariablePolicy;
|
||||||
|
LOCK_VARIABLE_POLICY LockVariablePolicy;
|
||||||
|
} _VARIABLE_POLICY_PROTOCOL;
|
||||||
|
|
||||||
|
typedef _VARIABLE_POLICY_PROTOCOL VARIABLE_POLICY_PROTOCOL;
|
||||||
|
|
||||||
|
extern EFI_GUID gVariablePolicyProtocolGuid;
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
## Include/Protocol/VariablePolicy.h
|
||||||
|
gVariablePolicyProtocolGuid = { 0x81D1675C, 0x86F6, 0x48DF, { 0xBD, 0x95, 0x9A, 0x6E, 0x4F, 0x09, 0x25, 0xC3 } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### DisableVariablePolicy
|
||||||
|
|
||||||
|
Function prototype:
|
||||||
|
|
||||||
|
```c
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DisableVariablePolicy (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`DisableVariablePolicy` call disables the Variable Policy Engine, so
|
||||||
|
that the present policy entries are no longer taken into account on
|
||||||
|
variable access calls. This call effectively turns off the variable
|
||||||
|
policy verification for this boot. This also disables UEFI
|
||||||
|
Authenticated Variable protections including Secure Boot.
|
||||||
|
`DisableVariablePolicy` can only be called once during boot. If called
|
||||||
|
more than once, it will return `EFI_ALREADY_STARTED`. Note, this process
|
||||||
|
is irreversible until the next system reset -- there is no
|
||||||
|
"EnablePolicy" protocol function.
|
||||||
|
|
||||||
|
_IMPORTANT NOTE:_ It is strongly recommended that VariablePolicy *NEVER*
|
||||||
|
be disabled in "normal, production boot conditions". It is expected to always
|
||||||
|
be enforced. The most likely reasons to disable are for Manufacturing and
|
||||||
|
Refurbishing scenarios. If in doubt, leave the `gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable`
|
||||||
|
PCD set to `FALSE` and VariablePolicy will always be enabled.
|
||||||
|
|
||||||
|
### IsVariablePolicyEnabled
|
||||||
|
|
||||||
|
Function prototype:
|
||||||
|
|
||||||
|
```c
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyEnabled (
|
||||||
|
OUT BOOLEAN *State
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`IsVariablePolicyEnabled` accepts a pointer to a Boolean in which it
|
||||||
|
will store `TRUE` if Variable Policy Engine is enabled, or `FALSE` if
|
||||||
|
Variable Policy Engine is disabled. The function returns `EFI_SUCCESS`.
|
||||||
|
|
||||||
|
### RegisterVariablePolicy
|
||||||
|
|
||||||
|
Function prototype:
|
||||||
|
|
||||||
|
```c
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
RegisterVariablePolicy (
|
||||||
|
IN CONST VARIABLE_POLICY_ENTRY *PolicyEntry
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`RegisterVariablePolicy` call accepts a pointer to a policy entry
|
||||||
|
structure and returns the status of policy registration. If the
|
||||||
|
Variable Policy Engine is not locked and the policy structures are
|
||||||
|
valid, the function will return `EFI_SUCCESS`. If the Variable Policy
|
||||||
|
Engine is locked, `RegisterVariablePolicy` call will return
|
||||||
|
`EFI_WRITE_PROTECTED` and will not register the policy entry. Bulk
|
||||||
|
registration is not supported at this time due to the requirements
|
||||||
|
around error handling on each policy registration.
|
||||||
|
|
||||||
|
Upon successful registration of a policy entry, Variable Policy Engine
|
||||||
|
will then evaluate this entry on subsequent variable access calls (as
|
||||||
|
long as Variable Policy Engine hasn't been disabled).
|
||||||
|
|
||||||
|
### DumpVariablePolicy
|
||||||
|
|
||||||
|
Function prototype:
|
||||||
|
|
||||||
|
```c
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DumpVariablePolicy (
|
||||||
|
OUT UINT8 *Policy,
|
||||||
|
IN OUT UINT32 *Size
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`DumpVariablePolicy` call accepts a pointer to a buffer and a pointer to
|
||||||
|
the size of the buffer as parameters and returns the status of placing
|
||||||
|
the policy into the buffer. On first call to `DumpVariablePolicy` one
|
||||||
|
should pass `NULL` as the buffer and a pointer to 0 as the `Size` variable
|
||||||
|
and `DumpVariablePolicy` will return `EFI_BUFFER_TOO_SMALL` and will
|
||||||
|
populate the `Size` parameter with the size of the needed buffer to
|
||||||
|
store the policy. This way, the caller can allocate the buffer of
|
||||||
|
correct size and call `DumpVariablePolicy` again. The function will
|
||||||
|
populate the buffer with policy and return `EFI_SUCCESS`.
|
||||||
|
|
||||||
|
### LockVariablePolicy
|
||||||
|
|
||||||
|
Function prototype:
|
||||||
|
|
||||||
|
```c
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
LockVariablePolicy (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`LockVariablePolicy` locks the Variable Policy Engine, i.e. prevents any
|
||||||
|
new policy entries from getting registered in this boot
|
||||||
|
(`RegisterVariablePolicy` calls will fail with `EFI_WRITE_PROTECTED`
|
||||||
|
status code returned).
|
||||||
|
|
||||||
|
## Policy Structure
|
||||||
|
|
||||||
|
The structure below is meant for the DXE protocol calling interface,
|
||||||
|
when communicating to the Variable Policy Engine, thus the pragma pack
|
||||||
|
directive. How these policies are stored in memory is up to the
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#pragma pack(1)
|
||||||
|
typedef struct {
|
||||||
|
UINT32 Version;
|
||||||
|
UINT16 Size;
|
||||||
|
UINT16 OffsetToName;
|
||||||
|
EFI_GUID Namespace;
|
||||||
|
UINT32 MinSize;
|
||||||
|
UINT32 MaxSize;
|
||||||
|
UINT32 AttributesMustHave;
|
||||||
|
UINT32 AttributesCantHave;
|
||||||
|
UINT8 LockPolicyType;
|
||||||
|
UINT8 Reserved[3];
|
||||||
|
// UINT8 LockPolicy[]; // Variable Length Field
|
||||||
|
// CHAR16 Name[]; // Variable Length Field
|
||||||
|
} VARIABLE_POLICY_ENTRY;
|
||||||
|
```
|
||||||
|
|
||||||
|
The struct `VARIABLE_POLICY_ENTRY` above describes the layout for a policy
|
||||||
|
entry. The first element, `Size`, is the size of the policy entry, then
|
||||||
|
followed by `OffsetToName` -- the number of bytes from the beginning of
|
||||||
|
the struct to the name of the UEFI variable targeted by the policy
|
||||||
|
entry. The name can contain wildcards to match more than one variable,
|
||||||
|
more on this in the Wildcards section. The rest of the struct elements
|
||||||
|
are self-explanatory.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define VARIABLE_POLICY_TYPE_NO_LOCK 0
|
||||||
|
#define VARIABLE_POLICY_TYPE_LOCK_NOW 1
|
||||||
|
#define VARIABLE_POLICY_TYPE_LOCK_ON_CREATE 2
|
||||||
|
#define VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE 3
|
||||||
|
```
|
||||||
|
|
||||||
|
`LockPolicyType` can have the following values:
|
||||||
|
|
||||||
|
- `VARIABLE_POLICY_TYPE_NO_LOCK` -- means that no variable locking is performed. However,
|
||||||
|
the attribute and size constraints are still enforced. LockPolicy
|
||||||
|
field is size 0.
|
||||||
|
- `VARIABLE_POLICY_TYPE_LOCK_NOW` -- means that the variable starts being locked
|
||||||
|
immediately after policy entry registration. If the variable doesn't
|
||||||
|
exist at this point, being LockedNow means it cannot be created on
|
||||||
|
this boot. LockPolicy field is size 0.
|
||||||
|
- `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE` -- means that the variable starts being locked
|
||||||
|
after it is created. This allows for variable creation and
|
||||||
|
protection after LockVariablePolicy() function has been called. The
|
||||||
|
LockPolicy field is size 0.
|
||||||
|
- `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE` -- means that the Variable Policy Engine will
|
||||||
|
examine the state/contents of another variable to determine if the
|
||||||
|
variable referenced in the policy entry is locked.
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
EFI_GUID Namespace;
|
||||||
|
UINT8 Value;
|
||||||
|
UINT8 Reserved;
|
||||||
|
// CHAR16 Name[]; // Variable Length Field
|
||||||
|
} VARIABLE_LOCK_ON_VAR_STATE_POLICY;
|
||||||
|
```
|
||||||
|
|
||||||
|
If `LockPolicyType` is `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`, then the final element in the
|
||||||
|
policy entry struct is of type `VARIABLE_LOCK_ON_VAR_STATE_POLICY`, which
|
||||||
|
lists the namespace GUID, name (no wildcards here), and value of the
|
||||||
|
variable which state determines the locking of the variable referenced
|
||||||
|
in the policy entry. The "locking" variable must be 1 byte in terms of
|
||||||
|
payload size. If the Referenced variable contents match the Value of the
|
||||||
|
`VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure, the lock will be considered
|
||||||
|
active and the target variable will be locked. If the Reference variable
|
||||||
|
does not exist (ie. returns `EFI_NOT_FOUND`), this policy will be
|
||||||
|
considered inactive.
|
||||||
|
|
||||||
|
## Variable Name Wildcards
|
||||||
|
|
||||||
|
Two types of wildcards can be used in the UEFI variable name field in a
|
||||||
|
policy entry:
|
||||||
|
|
||||||
|
1. If the Name is a zero-length array (easily checked by comparing
|
||||||
|
fields `Size` and `OffsetToName` -- if they're the same, then the
|
||||||
|
`Name` is zero-length), then all variables in the namespace specified
|
||||||
|
by the provided GUID are targeted by the policy entry.
|
||||||
|
2. Character "#" in the `Name` corresponds to one numeric character
|
||||||
|
(0-9, A-F, a-f). For example, string "Boot####" in the `Name`
|
||||||
|
field of the policy entry will make it so that the policy entry will
|
||||||
|
target variables named "Boot0001", "Boot0002", etc.
|
||||||
|
|
||||||
|
Given the above two types of wildcards, one variable can be targeted by
|
||||||
|
more than one policy entry, thus there is a need to establish the
|
||||||
|
precedence rule: a more specific match is applied. When a variable
|
||||||
|
access operation is performed, Variable Policy Engine should first check
|
||||||
|
the variable being accessed against the policy entries without
|
||||||
|
wildcards, then with 1 wildcard, then with 2 wildcards, etc., followed
|
||||||
|
in the end by policy entries that match the whole namespace. One can
|
||||||
|
still imagine a situation where two policy entries with the same number
|
||||||
|
of wildcards match the same variable -- for example, policy entries with
|
||||||
|
Names "Boot00##" and "Boot##01" will both match variable "Boot0001".
|
||||||
|
Such situation can (and should) be avoided by designing mutually
|
||||||
|
exclusive Name strings with wildcards, however, if it occurs, then the
|
||||||
|
policy entry that was registered first will be used. After the most
|
||||||
|
specific match is selected, all other policies are ignored.
|
||||||
|
|
||||||
|
## Available Testing
|
||||||
|
|
||||||
|
This functionality is current supported by two kinds of tests: there is a host-based
|
||||||
|
unit test for the core business logic (this test accompanies the `VariablePolicyLib`
|
||||||
|
implementation that lives in `MdeModulePkg/Library`) and there is a functional test
|
||||||
|
for the protocol and its interfaces (this test lives in the `MdeModulePkg/Test/ShellTest`
|
||||||
|
directory).
|
||||||
|
|
||||||
|
### Host-Based Unit Test
|
||||||
|
|
||||||
|
There is a test that can be run as part of the Host-Based Unit Testing
|
||||||
|
infrastructure provided by EDK2 PyTools (documented elsewhere). It will test
|
||||||
|
all internal guarantees and is where you will find test cases for most of the
|
||||||
|
policy matching and security of the Variable Policy Engine.
|
||||||
|
|
||||||
|
### Shell-Based Functional Test
|
||||||
|
|
||||||
|
This test -- [Variable Policy Functional Unit Test](https://github.com/microsoft/mu_plus/tree/release/202005/UefiTestingPkg/FunctionalSystemTests/VarPolicyUnitTestApp) -- can be built as a
|
||||||
|
UEFI Shell application and run to validate that the Variable Policy Engine
|
||||||
|
is correctly installed and enforcing policies on the target system.
|
||||||
|
|
||||||
|
NOTE: This test _must_ be run prior to calling `DisableVariablePolicy` for all
|
||||||
|
test cases to pass. For this reason, it is recommended to run this on a test-built
|
||||||
|
FW for complete results, and then again on a production-built FW for release
|
||||||
|
results.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
The below examples are hypothetical scenarios based on real-world requirements
|
||||||
|
that demonstrate how Variable Policies could be constructed to solve various
|
||||||
|
problems.
|
||||||
|
|
||||||
|
### UEFI Setup Variables (Example 1)
|
||||||
|
|
||||||
|
Variables containing values of the setup options exposed via UEFI
|
||||||
|
menu (setup variables). These would be locked based on a state of
|
||||||
|
another variable, "ReadyToBoot", which would be set to 1 at the
|
||||||
|
ReadyToBoot event. Thus, the policy for the setup variables would be
|
||||||
|
of type `LockOnVarState`, with the "ReadyToBoot" listed as the name of
|
||||||
|
the variable, appropriate GUID listed as the namespace, and 1 as
|
||||||
|
value. Entry into the trusted UEFI menu app doesn't signal
|
||||||
|
ReadyToBoot, but booting to any device does, and the setup variables
|
||||||
|
are write-protected. The "ReadyToBoot" variable would need to be
|
||||||
|
locked-on-create. *(THIS IS ESSENTIALLY LOCK ON EVENT, BUT SINCE THE
|
||||||
|
POLICY ENGINE IS NOT IN THE UEFI ENVIRONMENT VARIABLES ARE USED)*
|
||||||
|
|
||||||
|
For example, "AllowPXEBoot" variable locked by "ReadyToBoot" variable.
|
||||||
|
|
||||||
|
(NOTE: In the below example, the emphasized fields ('Namespace', 'Value', and 'Name')
|
||||||
|
are members of the `VARIABLE_LOCK_ON_VAR_STATE_POLICY` structure.)
|
||||||
|
|
||||||
|
Size | ...
|
||||||
|
---- | ---
|
||||||
|
OffsetToName | ...
|
||||||
|
NameSpace | ...
|
||||||
|
MinSize | ...
|
||||||
|
MaxSize | ...
|
||||||
|
AttributesMustHave | ...
|
||||||
|
AttributesCantHave | ...
|
||||||
|
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
|
||||||
|
_Namespace_ | ...
|
||||||
|
_Value_ | 1
|
||||||
|
_Name_ | "ReadyToBoot"
|
||||||
|
//Name | "AllowPXEBoot"
|
||||||
|
|
||||||
|
### Manufacturing VPD (Example 2)
|
||||||
|
|
||||||
|
Manufacturing Variable Provisioning Data (VPD) is stored in
|
||||||
|
variables and is created while in Manufacturing (MFG) Mode. In MFG
|
||||||
|
Mode Variable Policy Engine is disabled, thus these VPD variables
|
||||||
|
can be created. These variables are locked with lock policy type
|
||||||
|
`LockNow`, so that these variables can't be tampered with in Customer
|
||||||
|
Mode. To overwrite or clear VPD, the device would need to MFG mode,
|
||||||
|
which is standard practice for refurbishing/remanufacturing
|
||||||
|
scenarios.
|
||||||
|
|
||||||
|
Example: "DisplayPanelCalibration" variable...
|
||||||
|
|
||||||
|
Size | ...
|
||||||
|
---- | ---
|
||||||
|
OffsetToName | ...
|
||||||
|
NameSpace | ...
|
||||||
|
MinSize | ...
|
||||||
|
MaxSize | ...
|
||||||
|
AttributesMustHave | ...
|
||||||
|
AttributesCantHave | ...
|
||||||
|
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_NOW`
|
||||||
|
// Name | "DisplayPanelCalibration"
|
||||||
|
|
||||||
|
### 3rd Party Calibration Data (Example 3)
|
||||||
|
|
||||||
|
Bluetooth pre-pairing variables are locked-on-create because these
|
||||||
|
get created by an OS application when Variable Policy is in effect.
|
||||||
|
|
||||||
|
Example: "KeyboardBTPairing" variable
|
||||||
|
|
||||||
|
Size | ...
|
||||||
|
---- | ---
|
||||||
|
OffsetToName | ...
|
||||||
|
NameSpace | ...
|
||||||
|
MinSize | ...
|
||||||
|
MaxSize | ...
|
||||||
|
AttributesMustHave | ...
|
||||||
|
AttributesCantHave | ...
|
||||||
|
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_CREATE`
|
||||||
|
// Name | "KeyboardBTPairing"
|
||||||
|
|
||||||
|
### Software-based Variable Policy (Example 4)
|
||||||
|
|
||||||
|
Example: "Boot####" variables (a name string with wildcards that
|
||||||
|
will match variables "Boot0000" to "BootFFFF") locked by "LockBootOrder"
|
||||||
|
variable.
|
||||||
|
|
||||||
|
Size | ...
|
||||||
|
---- | ---
|
||||||
|
OffsetToName | ...
|
||||||
|
NameSpace | ...
|
||||||
|
MinSize | ...
|
||||||
|
MaxSize | ...
|
||||||
|
AttributesMustHave | ...
|
||||||
|
AttributesCantHave | ...
|
||||||
|
LockPolicyType | `VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE`
|
||||||
|
_Namespace_ | ...
|
||||||
|
_Value_ | 1
|
||||||
|
_Name_ | "LockBootOrder"
|
||||||
|
//Name | "Boot####"
|
@ -0,0 +1,46 @@
|
|||||||
|
/** @file -- VariablePolicyExtraInitNull.c
|
||||||
|
This file contains extra init and deinit routines that don't do anything
|
||||||
|
extra.
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An extra init hook that enables the RuntimeDxe library instance to
|
||||||
|
register VirtualAddress change callbacks. Among other things.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Everything is good. Continue with init.
|
||||||
|
@retval Others Uh... don't continue.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
VariablePolicyExtraInit (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// NULL implementation.
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An extra deinit hook that enables the RuntimeDxe library instance to
|
||||||
|
register VirtualAddress change callbacks. Among other things.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Everything is good. Continue with deinit.
|
||||||
|
@retval Others Uh... don't continue.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
VariablePolicyExtraDeinit (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// NULL implementation.
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
/** @file -- VariablePolicyExtraInitRuntimeDxe.c
|
||||||
|
This file contains extra init and deinit routines that register and unregister
|
||||||
|
VariableAddressChange callbacks.
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <Library/UefiBootServicesTableLib.h>
|
||||||
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
||||||
|
|
||||||
|
extern EFI_GET_VARIABLE mGetVariableHelper;
|
||||||
|
extern UINT8 *mPolicyTable;
|
||||||
|
STATIC BOOLEAN mIsVirtualAddrConverted;
|
||||||
|
STATIC EFI_EVENT mVariablePolicyLibVirtualAddressChangeEvent = NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
For the RuntimeDxe version of this lib, convert internal pointer addresses to virtual addresses.
|
||||||
|
|
||||||
|
@param[in] Event Event whose notification function is being invoked.
|
||||||
|
@param[in] Context The pointer to the notification function's context, which
|
||||||
|
is implementation-dependent.
|
||||||
|
**/
|
||||||
|
STATIC
|
||||||
|
VOID
|
||||||
|
EFIAPI
|
||||||
|
VariablePolicyLibVirtualAddressCallback (
|
||||||
|
IN EFI_EVENT Event,
|
||||||
|
IN VOID *Context
|
||||||
|
)
|
||||||
|
{
|
||||||
|
gRT->ConvertPointer (0, (VOID **)&mPolicyTable);
|
||||||
|
gRT->ConvertPointer (0, (VOID **)&mGetVariableHelper);
|
||||||
|
mIsVirtualAddrConverted = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An extra init hook that enables the RuntimeDxe library instance to
|
||||||
|
register VirtualAddress change callbacks. Among other things.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Everything is good. Continue with init.
|
||||||
|
@retval Others Uh... don't continue.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
VariablePolicyExtraInit (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return gBS->CreateEventEx (EVT_NOTIFY_SIGNAL,
|
||||||
|
TPL_NOTIFY,
|
||||||
|
VariablePolicyLibVirtualAddressCallback,
|
||||||
|
NULL,
|
||||||
|
&gEfiEventVirtualAddressChangeGuid,
|
||||||
|
&mVariablePolicyLibVirtualAddressChangeEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An extra deinit hook that enables the RuntimeDxe library instance to
|
||||||
|
register VirtualAddress change callbacks. Among other things.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Everything is good. Continue with deinit.
|
||||||
|
@retval Others Uh... don't continue.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
VariablePolicyExtraDeinit (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
Status = EFI_SUCCESS;
|
||||||
|
if (mIsVirtualAddrConverted) {
|
||||||
|
Status = gBS->CloseEvent (mVariablePolicyLibVirtualAddressChangeEvent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Status = EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
830
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
Normal file
830
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.c
Normal file
@ -0,0 +1,830 @@
|
|||||||
|
/** @file -- VariablePolicyLib.c
|
||||||
|
Business logic for Variable Policy enforcement.
|
||||||
|
|
||||||
|
Copyright (c) Microsoft Corporation.
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <Uefi.h>
|
||||||
|
|
||||||
|
#include <Library/SafeIntLib.h>
|
||||||
|
#include <Library/MemoryAllocationLib.h>
|
||||||
|
#include <Library/BaseMemoryLib.h>
|
||||||
|
#include <Library/DebugLib.h>
|
||||||
|
#include <Library/PcdLib.h>
|
||||||
|
|
||||||
|
#include <Protocol/VariablePolicy.h>
|
||||||
|
#include <Library/VariablePolicyLib.h>
|
||||||
|
|
||||||
|
|
||||||
|
// IMPORTANT NOTE: This library is currently rife with multiple return statements
|
||||||
|
// for error handling. A refactor should remove these at some point.
|
||||||
|
|
||||||
|
//
|
||||||
|
// This library was designed with advanced unit-test features.
|
||||||
|
// This define handles the configuration.
|
||||||
|
#ifdef INTERNAL_UNIT_TEST
|
||||||
|
#undef STATIC
|
||||||
|
#define STATIC // Nothing...
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// An abstracted GetVariable interface that enables configuration regardless of the environment.
|
||||||
|
EFI_GET_VARIABLE mGetVariableHelper = NULL;
|
||||||
|
|
||||||
|
// Master switch to lock this entire interface. Does not stop enforcement,
|
||||||
|
// just prevents the configuration from being changed for the rest of the boot.
|
||||||
|
STATIC BOOLEAN mInterfaceLocked = FALSE;
|
||||||
|
|
||||||
|
// Master switch to disable the entire interface for a single boot.
|
||||||
|
// This will disable all policy enforcement for the duration of the boot.
|
||||||
|
STATIC BOOLEAN mProtectionDisabled = FALSE;
|
||||||
|
|
||||||
|
// Table to hold all the current policies.
|
||||||
|
UINT8 *mPolicyTable = NULL;
|
||||||
|
STATIC UINT32 mCurrentTableSize = 0;
|
||||||
|
STATIC UINT32 mCurrentTableUsage = 0;
|
||||||
|
STATIC UINT32 mCurrentTableCount = 0;
|
||||||
|
|
||||||
|
#define POLICY_TABLE_STEP_SIZE 0x1000
|
||||||
|
|
||||||
|
// NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.
|
||||||
|
// Current table data has already been sanitized.
|
||||||
|
#define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)
|
||||||
|
#define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)
|
||||||
|
|
||||||
|
#define MATCH_PRIORITY_EXACT 0
|
||||||
|
#define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT
|
||||||
|
#define MATCH_PRIORITY_MIN MAX_UINT8
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An extra init hook that enables the RuntimeDxe library instance to
|
||||||
|
register VirtualAddress change callbacks. Among other things.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Everything is good. Continue with init.
|
||||||
|
@retval Others Uh... don't continue.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
VariablePolicyExtraInit (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
An extra deinit hook that enables the RuntimeDxe library instance to
|
||||||
|
register VirtualAddress change callbacks. Among other things.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Everything is good. Continue with deinit.
|
||||||
|
@retval Others Uh... don't continue.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
VariablePolicyExtraDeinit (
|
||||||
|
VOID
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function determines whether the structure of an incoming policy
|
||||||
|
is valid and internally consistent.
|
||||||
|
|
||||||
|
@param[in] NewPolicy Pointer to the incoming policy structure.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE Pointer is NULL, size is wrong, strings are empty, or
|
||||||
|
substructures overlap.
|
||||||
|
|
||||||
|
**/
|
||||||
|
STATIC
|
||||||
|
BOOLEAN
|
||||||
|
IsValidVariablePolicyStructure (
|
||||||
|
IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
UINTN EntryEnd;
|
||||||
|
CHAR16 *CheckChar;
|
||||||
|
UINTN WildcardCount;
|
||||||
|
|
||||||
|
// Sanitize some quick values.
|
||||||
|
if (NewPolicy == NULL || NewPolicy->Size == 0 ||
|
||||||
|
// Structure size should be at least as long as the minumum structure and a NULL string.
|
||||||
|
NewPolicy->Size < sizeof(VARIABLE_POLICY_ENTRY) ||
|
||||||
|
// Check for the known revision.
|
||||||
|
NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the theoretical end of the structure and make sure
|
||||||
|
// that the structure can fit in memory.
|
||||||
|
Status = SafeUintnAdd( (UINTN)NewPolicy, NewPolicy->Size, &EntryEnd );
|
||||||
|
if (EFI_ERROR( Status )) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a valid Max Size.
|
||||||
|
if (NewPolicy->MaxSize == 0) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for the valid list of lock policies.
|
||||||
|
if (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK &&
|
||||||
|
NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW &&
|
||||||
|
NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE &&
|
||||||
|
NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name
|
||||||
|
// terminates before the OffsetToName for the matching policy variable Name.
|
||||||
|
if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
|
||||||
|
// Adjust CheckChar to the offset of the LockPolicy->Name.
|
||||||
|
Status = SafeUintnAdd( (UINTN)NewPolicy + sizeof(VARIABLE_POLICY_ENTRY),
|
||||||
|
sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY),
|
||||||
|
(UINTN*)&CheckChar );
|
||||||
|
if (EFI_ERROR( Status ) || EntryEnd <= (UINTN)CheckChar) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
while (*CheckChar != CHAR_NULL) {
|
||||||
|
if (EntryEnd <= (UINTN)CheckChar) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
CheckChar++;
|
||||||
|
}
|
||||||
|
// At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.
|
||||||
|
// We should check to make sure that the policy Name comes immediately after this charcter.
|
||||||
|
if ((UINTN)++CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// If the policy type is any other value, make sure that the LockPolicy structure has a zero length.
|
||||||
|
} else {
|
||||||
|
if (NewPolicy->OffsetToName != sizeof(VARIABLE_POLICY_ENTRY)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to make sure that the name has a terminating character
|
||||||
|
// before the end of the structure.
|
||||||
|
// We've already checked that the name is within the bounds of the structure.
|
||||||
|
if (NewPolicy->Size != NewPolicy->OffsetToName) {
|
||||||
|
CheckChar = (CHAR16*)((UINTN)NewPolicy + NewPolicy->OffsetToName);
|
||||||
|
WildcardCount = 0;
|
||||||
|
while (*CheckChar != CHAR_NULL) {
|
||||||
|
// Make sure there aren't excessive wildcards.
|
||||||
|
if (*CheckChar == '#') {
|
||||||
|
WildcardCount++;
|
||||||
|
if (WildcardCount > MATCH_PRIORITY_MIN) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure you're still within the bounds of the policy structure.
|
||||||
|
if (EntryEnd <= (UINTN)CheckChar) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
CheckChar++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, we should be pointed at the very last character in Name, so we should be right
|
||||||
|
// up against the end of the structure.
|
||||||
|
if ((UINTN)++CheckChar != EntryEnd) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function evaluates a policy and determines whether it matches the target
|
||||||
|
variable. If matched, will also return a value corresponding to the priority of the match.
|
||||||
|
|
||||||
|
The rules for "best match" are listed in the Variable Policy Spec.
|
||||||
|
Perfect name matches will return 0.
|
||||||
|
Single wildcard characters will return the number of wildcard characters.
|
||||||
|
Full namespaces will return MAX_UINT8.
|
||||||
|
|
||||||
|
@param[in] EvalEntry Pointer to the policy entry being evaluated.
|
||||||
|
@param[in] VariableName Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] VendorGuid Same as EFI_SET_VARIABLE.
|
||||||
|
@param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.
|
||||||
|
Lower number == higher priority. Only valid if a match found.
|
||||||
|
|
||||||
|
@retval TRUE Current entry matches the target variable.
|
||||||
|
@retval FALSE Current entry does not match at all.
|
||||||
|
|
||||||
|
**/
|
||||||
|
STATIC
|
||||||
|
BOOLEAN
|
||||||
|
EvaluatePolicyMatch (
|
||||||
|
IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,
|
||||||
|
IN CONST CHAR16 *VariableName,
|
||||||
|
IN CONST EFI_GUID *VendorGuid,
|
||||||
|
OUT UINT8 *MatchPriority OPTIONAL
|
||||||
|
)
|
||||||
|
{
|
||||||
|
BOOLEAN Result;
|
||||||
|
CHAR16 *PolicyName;
|
||||||
|
UINT8 CalculatedPriority;
|
||||||
|
UINTN Index;
|
||||||
|
|
||||||
|
Result = FALSE;
|
||||||
|
CalculatedPriority = MATCH_PRIORITY_EXACT;
|
||||||
|
|
||||||
|
// Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.
|
||||||
|
if (!CompareGuid( &EvalEntry->Namespace, VendorGuid )) {
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the GUID matches, check to see whether there is a Name associated
|
||||||
|
// with the policy. If not, this policy matches the entire namespace.
|
||||||
|
// Missing Name is indicated by size being equal to name.
|
||||||
|
if (EvalEntry->Size == EvalEntry->OffsetToName) {
|
||||||
|
CalculatedPriority = MATCH_PRIORITY_MIN;
|
||||||
|
Result = TRUE;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we know the name exists, get it.
|
||||||
|
PolicyName = GET_POLICY_NAME( EvalEntry );
|
||||||
|
|
||||||
|
// Evaluate the name against the policy name and check for a match.
|
||||||
|
// Account for any wildcards.
|
||||||
|
Index = 0;
|
||||||
|
Result = TRUE;
|
||||||
|
// Keep going until the end of both strings.
|
||||||
|
while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {
|
||||||
|
// If we don't have a match...
|
||||||
|
if (PolicyName[Index] != VariableName[Index] || PolicyName[Index] == '#') {
|
||||||
|
// If this is a numerical wildcard, we can consider
|
||||||
|
// it a match if we alter the priority.
|
||||||
|
if (PolicyName[Index] == L'#' &&
|
||||||
|
((L'0' <= VariableName[Index] && VariableName[Index] <= L'9') ||
|
||||||
|
(L'A' <= VariableName[Index] && VariableName[Index] <= L'F') ||
|
||||||
|
(L'a' <= VariableName[Index] && VariableName[Index] <= L'f'))) {
|
||||||
|
if (CalculatedPriority < MATCH_PRIORITY_MIN) {
|
||||||
|
CalculatedPriority++;
|
||||||
|
}
|
||||||
|
// Otherwise, not a match.
|
||||||
|
} else {
|
||||||
|
Result = FALSE;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit:
|
||||||
|
if (Result && MatchPriority != NULL) {
|
||||||
|
*MatchPriority = CalculatedPriority;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function walks the current policy table and returns a pointer
|
||||||
|
to the best match, if any are found. Leverages EvaluatePolicyMatch() to
|
||||||
|
determine "best".
|
||||||
|
|
||||||
|
@param[in] VariableName Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] VendorGuid Same as EFI_SET_VARIABLE.
|
||||||
|
@param[out] ReturnPriority [Optional] If pointer is provided, return the
|
||||||
|
priority of the match. Same as EvaluatePolicyMatch().
|
||||||
|
Only valid if a match is returned.
|
||||||
|
|
||||||
|
@retval VARIABLE_POLICY_ENTRY* Best match that was found.
|
||||||
|
@retval NULL No match was found.
|
||||||
|
|
||||||
|
**/
|
||||||
|
STATIC
|
||||||
|
VARIABLE_POLICY_ENTRY*
|
||||||
|
GetBestPolicyMatch (
|
||||||
|
IN CONST CHAR16 *VariableName,
|
||||||
|
IN CONST EFI_GUID *VendorGuid,
|
||||||
|
OUT UINT8 *ReturnPriority OPTIONAL
|
||||||
|
)
|
||||||
|
{
|
||||||
|
VARIABLE_POLICY_ENTRY *BestResult;
|
||||||
|
VARIABLE_POLICY_ENTRY *CurrentEntry;
|
||||||
|
UINT8 MatchPriority;
|
||||||
|
UINT8 CurrentPriority;
|
||||||
|
UINTN Index;
|
||||||
|
|
||||||
|
BestResult = NULL;
|
||||||
|
MatchPriority = MATCH_PRIORITY_EXACT;
|
||||||
|
|
||||||
|
// Walk all entries in the table, looking for matches.
|
||||||
|
CurrentEntry = (VARIABLE_POLICY_ENTRY*)mPolicyTable;
|
||||||
|
for (Index = 0; Index < mCurrentTableCount; Index++) {
|
||||||
|
// Check for a match.
|
||||||
|
if (EvaluatePolicyMatch( CurrentEntry, VariableName, VendorGuid, &CurrentPriority )) {
|
||||||
|
// If match is better, take it.
|
||||||
|
if (BestResult == NULL || CurrentPriority < MatchPriority) {
|
||||||
|
BestResult = CurrentEntry;
|
||||||
|
MatchPriority = CurrentPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you've hit the highest-priority match, can exit now.
|
||||||
|
if (MatchPriority == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're still in the loop, move to the next entry.
|
||||||
|
CurrentEntry = GET_NEXT_POLICY( CurrentEntry );
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a return priority was requested, return it.
|
||||||
|
if (ReturnPriority != NULL) {
|
||||||
|
*ReturnPriority = MatchPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BestResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function validates and registers a new policy with
|
||||||
|
the policy enforcement engine.
|
||||||
|
|
||||||
|
@param[in] NewPolicy Pointer to the incoming policy structure.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.
|
||||||
|
@retval EFI_ALREADY_STARTED An identical matching policy already exists.
|
||||||
|
@retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.
|
||||||
|
@retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.
|
||||||
|
@retval EFI_ABORTED A calculation error has prevented this function from completing.
|
||||||
|
@retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
RegisterVariablePolicy (
|
||||||
|
IN CONST VARIABLE_POLICY_ENTRY *NewPolicy
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
VARIABLE_POLICY_ENTRY *MatchPolicy;
|
||||||
|
UINT8 MatchPriority;
|
||||||
|
UINT32 NewSize;
|
||||||
|
UINT8 *NewTable;
|
||||||
|
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
return EFI_NOT_READY;
|
||||||
|
}
|
||||||
|
if (mInterfaceLocked) {
|
||||||
|
return EFI_WRITE_PROTECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidVariablePolicyStructure( NewPolicy )) {
|
||||||
|
return EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see whether an exact matching policy already exists.
|
||||||
|
MatchPolicy = GetBestPolicyMatch( GET_POLICY_NAME( NewPolicy ),
|
||||||
|
&NewPolicy->Namespace,
|
||||||
|
&MatchPriority );
|
||||||
|
if (MatchPolicy != NULL && MatchPriority == MATCH_PRIORITY_EXACT) {
|
||||||
|
return EFI_ALREADY_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none exists, create it.
|
||||||
|
// If we need more space, allocate that now.
|
||||||
|
Status = SafeUint32Add( mCurrentTableUsage, NewPolicy->Size, &NewSize );
|
||||||
|
if (EFI_ERROR( Status )) {
|
||||||
|
return EFI_ABORTED;
|
||||||
|
}
|
||||||
|
if (NewSize > mCurrentTableSize) {
|
||||||
|
// Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.
|
||||||
|
NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?
|
||||||
|
(NewSize / POLICY_TABLE_STEP_SIZE) + 1 :
|
||||||
|
(NewSize / POLICY_TABLE_STEP_SIZE);
|
||||||
|
// Calculate the new table size in absolute bytes.
|
||||||
|
Status = SafeUint32Mult( NewSize, POLICY_TABLE_STEP_SIZE, &NewSize );
|
||||||
|
if (EFI_ERROR( Status )) {
|
||||||
|
return EFI_ABORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reallocate and copy the table.
|
||||||
|
NewTable = AllocatePool( NewSize );
|
||||||
|
if (NewTable == NULL) {
|
||||||
|
return EFI_OUT_OF_RESOURCES;
|
||||||
|
}
|
||||||
|
CopyMem( NewTable, mPolicyTable, mCurrentTableUsage );
|
||||||
|
mCurrentTableSize = NewSize;
|
||||||
|
if (mPolicyTable != NULL) {
|
||||||
|
FreePool( mPolicyTable );
|
||||||
|
}
|
||||||
|
mPolicyTable = NewTable;
|
||||||
|
}
|
||||||
|
// Copy the policy into the table.
|
||||||
|
CopyMem( mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size );
|
||||||
|
mCurrentTableUsage += NewPolicy->Size;
|
||||||
|
mCurrentTableCount += 1;
|
||||||
|
|
||||||
|
// We're done here.
|
||||||
|
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function checks to see whether the parameters to SetVariable would
|
||||||
|
be allowed according to the current variable policies.
|
||||||
|
|
||||||
|
@param[in] VariableName Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] VendorGuid Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] Attributes Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] DataSize Same as EFI_SET_VARIABLE.
|
||||||
|
@param[in] Data Same as EFI_SET_VARIABLE.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS A matching policy allows this update.
|
||||||
|
@retval EFI_SUCCESS There are currently no policies that restrict this update.
|
||||||
|
@retval EFI_SUCCESS The protections have been disable until the next reboot.
|
||||||
|
@retval EFI_WRITE_PROTECTED Variable is currently locked.
|
||||||
|
@retval EFI_INVALID_PARAMETER Attributes or size are invalid.
|
||||||
|
@retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.
|
||||||
|
@retval EFI_NOT_READY Library has not been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
ValidateSetVariable (
|
||||||
|
IN CHAR16 *VariableName,
|
||||||
|
IN EFI_GUID *VendorGuid,
|
||||||
|
IN UINT32 Attributes,
|
||||||
|
IN UINTN DataSize,
|
||||||
|
IN VOID *Data
|
||||||
|
)
|
||||||
|
{
|
||||||
|
BOOLEAN IsDel;
|
||||||
|
VARIABLE_POLICY_ENTRY *ActivePolicy;
|
||||||
|
EFI_STATUS Status;
|
||||||
|
EFI_STATUS ReturnStatus;
|
||||||
|
VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;
|
||||||
|
CHAR16 *StateVarName;
|
||||||
|
UINTN StateVarSize;
|
||||||
|
UINT8 StateVar;
|
||||||
|
|
||||||
|
ReturnStatus = EFI_SUCCESS;
|
||||||
|
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
ReturnStatus = EFI_NOT_READY;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail if the protections are currently disabled.
|
||||||
|
if (mProtectionDisabled) {
|
||||||
|
ReturnStatus = EFI_SUCCESS;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine whether this is a delete operation.
|
||||||
|
// If so, it will affect which tests are applied.
|
||||||
|
if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {
|
||||||
|
IsDel = TRUE;
|
||||||
|
} else {
|
||||||
|
IsDel = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find an active policy if one exists.
|
||||||
|
ActivePolicy = GetBestPolicyMatch( VariableName, VendorGuid, NULL );
|
||||||
|
|
||||||
|
// If we have an active policy, check it against the incoming data.
|
||||||
|
if (ActivePolicy != NULL) {
|
||||||
|
//
|
||||||
|
// Only enforce size and attribute constraints when updating data, not deleting.
|
||||||
|
if (!IsDel) {
|
||||||
|
// Check for size constraints.
|
||||||
|
if ((ActivePolicy->MinSize > 0 && DataSize < ActivePolicy->MinSize) ||
|
||||||
|
(ActivePolicy->MaxSize > 0 && DataSize > ActivePolicy->MaxSize)) {
|
||||||
|
ReturnStatus = EFI_INVALID_PARAMETER;
|
||||||
|
DEBUG(( DEBUG_VERBOSE, "%a - Bad Size. 0x%X <> 0x%X-0x%X\n", __FUNCTION__,
|
||||||
|
DataSize, ActivePolicy->MinSize, ActivePolicy->MaxSize ));
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for attribute constraints.
|
||||||
|
if ((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave ||
|
||||||
|
(ActivePolicy->AttributesCantHave & Attributes) != 0) {
|
||||||
|
ReturnStatus = EFI_INVALID_PARAMETER;
|
||||||
|
DEBUG(( DEBUG_VERBOSE, "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n", __FUNCTION__,
|
||||||
|
Attributes, ActivePolicy->AttributesMustHave, ActivePolicy->AttributesCantHave ));
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Lock policy check.
|
||||||
|
//
|
||||||
|
// Check for immediate lock.
|
||||||
|
if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {
|
||||||
|
ReturnStatus = EFI_WRITE_PROTECTED;
|
||||||
|
goto Exit;
|
||||||
|
// Check for lock on create.
|
||||||
|
} else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {
|
||||||
|
StateVarSize = 0;
|
||||||
|
Status = mGetVariableHelper( VariableName,
|
||||||
|
VendorGuid,
|
||||||
|
NULL,
|
||||||
|
&StateVarSize,
|
||||||
|
NULL );
|
||||||
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
||||||
|
ReturnStatus = EFI_WRITE_PROTECTED;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
// Check for lock on state variable.
|
||||||
|
} else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {
|
||||||
|
StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)ActivePolicy + sizeof(VARIABLE_POLICY_ENTRY));
|
||||||
|
StateVarName = (CHAR16*)((UINT8*)StateVarPolicy + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY));
|
||||||
|
StateVarSize = sizeof(StateVar);
|
||||||
|
Status = mGetVariableHelper( StateVarName,
|
||||||
|
&StateVarPolicy->Namespace,
|
||||||
|
NULL,
|
||||||
|
&StateVarSize,
|
||||||
|
&StateVar );
|
||||||
|
|
||||||
|
// If the variable was found, check the state. If matched, this variable is locked.
|
||||||
|
if (!EFI_ERROR( Status )) {
|
||||||
|
if (StateVar == StateVarPolicy->Value) {
|
||||||
|
ReturnStatus = EFI_WRITE_PROTECTED;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
// EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.
|
||||||
|
} else if (Status != EFI_NOT_FOUND && Status != EFI_BUFFER_TOO_SMALL) {
|
||||||
|
// We don't know what happened, but it isn't good.
|
||||||
|
ReturnStatus = EFI_ABORTED;
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit:
|
||||||
|
DEBUG(( DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __FUNCTION__, VendorGuid, VariableName, ReturnStatus ));
|
||||||
|
return ReturnStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function disables the variable policy enforcement. If it's
|
||||||
|
already been called once, will return EFI_ALREADY_STARTED.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_ALREADY_STARTED Has already been called once this boot.
|
||||||
|
@retval EFI_WRITE_PROTECTED Interface has been locked until reboot.
|
||||||
|
@retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DisableVariablePolicy (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
return EFI_NOT_READY;
|
||||||
|
}
|
||||||
|
if (mProtectionDisabled) {
|
||||||
|
return EFI_ALREADY_STARTED;
|
||||||
|
}
|
||||||
|
if (mInterfaceLocked) {
|
||||||
|
return EFI_WRITE_PROTECTED;
|
||||||
|
}
|
||||||
|
if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {
|
||||||
|
return EFI_WRITE_PROTECTED;
|
||||||
|
}
|
||||||
|
mProtectionDisabled = TRUE;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function will dump the entire contents of the variable policy table.
|
||||||
|
|
||||||
|
Similar to GetVariable, the first call can be made with a 0 size and it will return
|
||||||
|
the size of the buffer required to hold the entire table.
|
||||||
|
|
||||||
|
@param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.
|
||||||
|
@param[in,out] Size On input, the size of the output buffer. On output, the size
|
||||||
|
of the data returned.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.
|
||||||
|
@retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.
|
||||||
|
@retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DumpVariablePolicy (
|
||||||
|
OUT UINT8 *Policy,
|
||||||
|
IN OUT UINT32 *Size
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
return EFI_NOT_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the parameters.
|
||||||
|
if (Size == NULL || (*Size > 0 && Policy == NULL)) {
|
||||||
|
return EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the size is sufficient to hold the policy table.
|
||||||
|
if (*Size < mCurrentTableUsage) {
|
||||||
|
*Size = mCurrentTableUsage;
|
||||||
|
return EFI_BUFFER_TOO_SMALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're still here, copy the table and bounce.
|
||||||
|
CopyMem( Policy, mPolicyTable, mCurrentTableUsage );
|
||||||
|
*Size = mCurrentTableUsage;
|
||||||
|
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function returns whether or not the policy engine is
|
||||||
|
currently being enforced.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE
|
||||||
|
@retval FALSE Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
BOOLEAN
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyEnabled (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return !mProtectionDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function locks the interface so that no more policy updates
|
||||||
|
can be performed or changes made to the enforcement until the next boot.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_NOT_READY Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
LockVariablePolicy (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
return EFI_NOT_READY;
|
||||||
|
}
|
||||||
|
if (mInterfaceLocked) {
|
||||||
|
return EFI_WRITE_PROTECTED;
|
||||||
|
}
|
||||||
|
mInterfaceLocked = TRUE;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This API function returns whether or not the policy interface is locked
|
||||||
|
for the remainder of the boot.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE
|
||||||
|
@retval FALSE Library has not yet been initialized.
|
||||||
|
|
||||||
|
**/
|
||||||
|
BOOLEAN
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyInterfaceLocked (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!IsVariablePolicyLibInitialized()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return mInterfaceLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function initializes the library and sets
|
||||||
|
up any required internal structures or handlers.
|
||||||
|
|
||||||
|
Also registers the internal pointer for the GetVariable helper.
|
||||||
|
|
||||||
|
@param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to
|
||||||
|
check policy criteria that involve the existence of other variables.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to
|
||||||
|
deinitialize.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
InitVariablePolicyLib (
|
||||||
|
IN EFI_GET_VARIABLE GetVariableHelper
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
Status = EFI_SUCCESS;
|
||||||
|
|
||||||
|
if (mGetVariableHelper != NULL) {
|
||||||
|
return EFI_ALREADY_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EFI_ERROR( Status )) {
|
||||||
|
Status = VariablePolicyExtraInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EFI_ERROR( Status )) {
|
||||||
|
// Save an internal pointer to the GetVariableHelper.
|
||||||
|
mGetVariableHelper = GetVariableHelper;
|
||||||
|
|
||||||
|
// Initialize the global state.
|
||||||
|
mInterfaceLocked = FALSE;
|
||||||
|
mProtectionDisabled = FALSE;
|
||||||
|
mPolicyTable = NULL;
|
||||||
|
mCurrentTableSize = 0;
|
||||||
|
mCurrentTableUsage = 0;
|
||||||
|
mCurrentTableCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function returns whether or not the library is currently initialized.
|
||||||
|
|
||||||
|
@retval TRUE
|
||||||
|
@retval FALSE
|
||||||
|
|
||||||
|
**/
|
||||||
|
BOOLEAN
|
||||||
|
EFIAPI
|
||||||
|
IsVariablePolicyLibInitialized (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return (mGetVariableHelper != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
This helper function tears down the library.
|
||||||
|
|
||||||
|
Should generally only be used for test harnesses.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS
|
||||||
|
@retval EFI_NOT_READY Deinitialize was called without first calling initialize.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
EFIAPI
|
||||||
|
DeinitVariablePolicyLib (
|
||||||
|
VOID
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
|
||||||
|
Status = EFI_SUCCESS;
|
||||||
|
|
||||||
|
if (mGetVariableHelper == NULL) {
|
||||||
|
return EFI_NOT_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EFI_ERROR( Status )) {
|
||||||
|
Status = VariablePolicyExtraDeinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EFI_ERROR( Status )) {
|
||||||
|
mGetVariableHelper = NULL;
|
||||||
|
mInterfaceLocked = FALSE;
|
||||||
|
mProtectionDisabled = FALSE;
|
||||||
|
mCurrentTableSize = 0;
|
||||||
|
mCurrentTableUsage = 0;
|
||||||
|
mCurrentTableCount = 0;
|
||||||
|
|
||||||
|
if (mPolicyTable != NULL) {
|
||||||
|
FreePool( mPolicyTable );
|
||||||
|
mPolicyTable = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
48
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
Normal file
48
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
## @file VariablePolicyLib.inf
|
||||||
|
# Business logic for Variable Policy enforcement.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
[Defines]
|
||||||
|
INF_VERSION = 0x00010017
|
||||||
|
BASE_NAME = VariablePolicyLib
|
||||||
|
FILE_GUID = E9ECD342-159A-4F24-9FDF-65724027C594
|
||||||
|
VERSION_STRING = 1.0
|
||||||
|
MODULE_TYPE = DXE_DRIVER
|
||||||
|
LIBRARY_CLASS = VariablePolicyLib|DXE_DRIVER DXE_SMM_DRIVER MM_STANDALONE
|
||||||
|
|
||||||
|
#
|
||||||
|
# The following information is for reference only and not required by the build tools.
|
||||||
|
#
|
||||||
|
# VALID_ARCHITECTURES = ANY
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[Sources]
|
||||||
|
VariablePolicyLib.c
|
||||||
|
VariablePolicyExtraInitNull.c
|
||||||
|
|
||||||
|
|
||||||
|
[Packages]
|
||||||
|
MdePkg/MdePkg.dec
|
||||||
|
MdeModulePkg/MdeModulePkg.dec
|
||||||
|
|
||||||
|
|
||||||
|
[LibraryClasses]
|
||||||
|
DebugLib
|
||||||
|
BaseMemoryLib
|
||||||
|
MemoryAllocationLib
|
||||||
|
SafeIntLib
|
||||||
|
PcdLib
|
||||||
|
|
||||||
|
|
||||||
|
[Pcd]
|
||||||
|
gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
|
||||||
|
|
||||||
|
|
||||||
|
[BuildOptions]
|
||||||
|
MSFT:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
|
||||||
|
GCC:NOOPT_*_*_CC_FLAGS = -DINTERNAL_UNIT_TEST
|
12
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni
Normal file
12
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.uni
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// /** @file
|
||||||
|
// VariablePolicyLib.uni
|
||||||
|
//
|
||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
//
|
||||||
|
// **/
|
||||||
|
|
||||||
|
|
||||||
|
#string STR_MODULE_ABSTRACT #language en-US "Library containing the business logic for the VariablePolicy engine"
|
||||||
|
|
||||||
|
#string STR_MODULE_DESCRIPTION #language en-US "Library containing the business logic for the VariablePolicy engine"
|
@ -0,0 +1,51 @@
|
|||||||
|
## @file VariablePolicyLibRuntimeDxe.inf
|
||||||
|
# Business logic for Variable Policy enforcement.
|
||||||
|
# This instance is specifically for RuntimeDxe and contains
|
||||||
|
# extra routines to register for VirtualAddressChangeEvents.
|
||||||
|
#
|
||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
[Defines]
|
||||||
|
INF_VERSION = 0x00010017
|
||||||
|
BASE_NAME = VariablePolicyLibRuntimeDxe
|
||||||
|
FILE_GUID = 205F7F0E-8EAC-4914-8390-1B90DD7E2A27
|
||||||
|
VERSION_STRING = 1.0
|
||||||
|
MODULE_TYPE = DXE_RUNTIME_DRIVER
|
||||||
|
LIBRARY_CLASS = VariablePolicyLib|DXE_RUNTIME_DRIVER
|
||||||
|
|
||||||
|
#
|
||||||
|
# The following information is for reference only and not required by the build tools.
|
||||||
|
#
|
||||||
|
# VALID_ARCHITECTURES = ANY
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
[Sources]
|
||||||
|
VariablePolicyLib.c
|
||||||
|
VariablePolicyExtraInitRuntimeDxe.c
|
||||||
|
|
||||||
|
|
||||||
|
[Packages]
|
||||||
|
MdePkg/MdePkg.dec
|
||||||
|
MdeModulePkg/MdeModulePkg.dec
|
||||||
|
|
||||||
|
|
||||||
|
[LibraryClasses]
|
||||||
|
DebugLib
|
||||||
|
BaseMemoryLib
|
||||||
|
MemoryAllocationLib
|
||||||
|
SafeIntLib
|
||||||
|
UefiBootServicesTableLib
|
||||||
|
UefiRuntimeServicesTableLib
|
||||||
|
PcdLib
|
||||||
|
|
||||||
|
|
||||||
|
[Pcd]
|
||||||
|
gEfiMdeModulePkgTokenSpaceGuid.PcdAllowVariablePolicyEnforcementDisable ## CONSUMES
|
||||||
|
|
||||||
|
|
||||||
|
[Guids]
|
||||||
|
gEfiEventVirtualAddressChangeGuid
|
@ -104,7 +104,9 @@
|
|||||||
"FVMAIN",
|
"FVMAIN",
|
||||||
"VARCHECKPCD",
|
"VARCHECKPCD",
|
||||||
"Getxx",
|
"Getxx",
|
||||||
"lzturbo"
|
"lzturbo",
|
||||||
|
"musthave",
|
||||||
|
"canthave"
|
||||||
],
|
],
|
||||||
"AdditionalIncludePaths": [] # Additional paths to spell check relative to package root (wildcards supported)
|
"AdditionalIncludePaths": [] # Additional paths to spell check relative to package root (wildcards supported)
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
## @libraryclass Defines a set of methods to reset whole system.
|
## @libraryclass Defines a set of methods to reset whole system.
|
||||||
ResetSystemLib|Include/Library/ResetSystemLib.h
|
ResetSystemLib|Include/Library/ResetSystemLib.h
|
||||||
|
|
||||||
|
## @libraryclass Business logic for storing and testing variable policies
|
||||||
|
VariablePolicyLib|Include/Library/VariablePolicyLib.h
|
||||||
|
|
||||||
## @libraryclass Defines a set of helper functions for resetting the system.
|
## @libraryclass Defines a set of helper functions for resetting the system.
|
||||||
ResetUtilityLib|Include/Library/ResetUtilityLib.h
|
ResetUtilityLib|Include/Library/ResetUtilityLib.h
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
# (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
|
# (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
|
||||||
# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
|
# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
|
||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
#
|
#
|
||||||
@ -58,6 +59,7 @@
|
|||||||
DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
|
DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
|
||||||
DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf
|
DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf
|
||||||
UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
|
UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
|
||||||
|
VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
|
||||||
#
|
#
|
||||||
# Generic Modules
|
# Generic Modules
|
||||||
#
|
#
|
||||||
@ -129,6 +131,7 @@
|
|||||||
DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
|
DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
|
||||||
LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
|
LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
|
||||||
CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
|
CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
|
||||||
|
VariablePolicyLib|MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
|
||||||
|
|
||||||
[LibraryClasses.common.SMM_CORE]
|
[LibraryClasses.common.SMM_CORE]
|
||||||
HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
|
HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
|
||||||
@ -306,6 +309,8 @@
|
|||||||
MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
|
MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
|
||||||
MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
|
MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
|
||||||
MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
|
MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
|
||||||
|
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLib.inf
|
||||||
|
MdeModulePkg/Library/VariablePolicyLib/VariablePolicyLibRuntimeDxe.inf
|
||||||
MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
|
MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
|
||||||
MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
|
MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
|
||||||
MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
|
MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
|
||||||
|
Loading…
x
Reference in New Issue
Block a user