1 /** @file -- VarCheckPolicyLib.c
2 This is a NULL library instance that leverages the VarCheck interface
3 and the business logic behind the VariablePolicy code to make its decisions.
5 Copyright (c) Microsoft Corporation.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <Library/VarCheckLib.h>
11 #include <Library/BaseLib.h>
12 #include <Library/DebugLib.h>
13 #include <Library/SafeIntLib.h>
14 #include <Library/MmServicesTableLib.h>
15 #include <Library/BaseMemoryLib.h>
16 #include <Library/MemoryAllocationLib.h>
18 #include <Protocol/MmCommunication.h>
20 #include <Protocol/VariablePolicy.h>
21 #include <Library/VariablePolicyLib.h>
23 #include <Guid/VarCheckPolicyMmi.h>
25 #include "VarCheckPolicyLib.h"
27 // ================================================
28 // As a VarCheck library, we're linked into the VariableServices
29 // and may not be able to call them indirectly. To get around this,
30 // use the internal GetVariable function to query the variable store.
31 // ================================================
34 VariableServiceGetVariable (
35 IN CHAR16
*VariableName
,
36 IN EFI_GUID
*VendorGuid
,
37 OUT UINT32
*Attributes OPTIONAL
,
38 IN OUT UINTN
*DataSize
,
42 UINT8 mSecurityEvalBuffer
[VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE
];
44 // Pagination Cache Variables
45 UINT8
*mPaginationCache
= NULL
;
46 UINTN mPaginationCacheSize
= 0;
47 UINT32 mCurrentPaginationCommand
= 0;
50 MM Communication Handler to recieve commands from the DXE protocol for
51 Variable Policies. This communication channel is used to register new policies
52 and poll and toggle the enforcement of variable policies.
54 @param[in] DispatchHandle All parameters standard to MM communications convention.
55 @param[in] RegisterContext All parameters standard to MM communications convention.
56 @param[in,out] CommBuffer All parameters standard to MM communications convention.
57 @param[in,out] CommBufferSize All parameters standard to MM communications convention.
60 @retval EFI_INVALID_PARAMETER CommBuffer or CommBufferSize is null pointer.
61 @retval EFI_INVALID_PARAMETER CommBuffer size is wrong.
62 @retval EFI_INVALID_PARAMETER Revision or signature don't match.
68 VarCheckPolicyLibMmiHandler (
69 IN EFI_HANDLE DispatchHandle
,
70 IN CONST VOID
*RegisterContext
,
71 IN OUT VOID
*CommBuffer
,
72 IN OUT UINTN
*CommBufferSize
75 UINTN InternalCommBufferSize
;
76 VOID
*InternalCommBuffer
;
78 EFI_STATUS SubCommandStatus
;
79 VAR_CHECK_POLICY_COMM_HEADER
*PolicyCommmHeader
;
80 VAR_CHECK_POLICY_COMM_HEADER
*InternalPolicyCommmHeader
;
81 VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS
*IsEnabledParams
;
82 VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*DumpParamsIn
;
83 VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*DumpParamsOut
;
84 UINT8
*DumpInputBuffer
;
85 UINT8
*DumpOutputBuffer
;
87 VARIABLE_POLICY_ENTRY
*PolicyEntry
;
94 // Validate some input parameters.
96 // If either of the pointers are NULL, we can't proceed.
97 if ((CommBuffer
== NULL
) || (CommBufferSize
== NULL
)) {
98 DEBUG ((DEBUG_INFO
, "%a - Invalid comm buffer pointers!\n", __FUNCTION__
));
99 return EFI_INVALID_PARAMETER
;
102 // Make sure that the buffer does not overlap SMM.
103 // This should be covered by the SmiManage infrastructure, but just to be safe...
104 InternalCommBufferSize
= *CommBufferSize
;
105 if ((InternalCommBufferSize
> VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE
) ||
106 !VarCheckPolicyIsBufferOutsideValid ((UINTN
)CommBuffer
, (UINT64
)InternalCommBufferSize
))
108 DEBUG ((DEBUG_ERROR
, "%a - Invalid CommBuffer supplied! 0x%016lX[0x%016lX]\n", __FUNCTION__
, CommBuffer
, InternalCommBufferSize
));
109 return EFI_INVALID_PARAMETER
;
112 // If the size does not meet a minimum threshold, we cannot proceed.
113 ExpectedSize
= sizeof (VAR_CHECK_POLICY_COMM_HEADER
);
114 if (InternalCommBufferSize
< ExpectedSize
) {
115 DEBUG ((DEBUG_INFO
, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__
, InternalCommBufferSize
, ExpectedSize
));
116 return EFI_INVALID_PARAMETER
;
120 // Before proceeding any further, copy the buffer internally so that we can compare
121 // without worrying about TOCTOU.
123 InternalCommBuffer
= &mSecurityEvalBuffer
[0];
124 CopyMem (InternalCommBuffer
, CommBuffer
, InternalCommBufferSize
);
125 PolicyCommmHeader
= CommBuffer
;
126 InternalPolicyCommmHeader
= InternalCommBuffer
;
127 // Check the revision and the signature of the comm header.
128 if ((InternalPolicyCommmHeader
->Signature
!= VAR_CHECK_POLICY_COMM_SIG
) ||
129 (InternalPolicyCommmHeader
->Revision
!= VAR_CHECK_POLICY_COMM_REVISION
))
131 DEBUG ((DEBUG_INFO
, "%a - Signature or revision are incorrect!\n", __FUNCTION__
));
132 // We have verified the buffer is not null and have enough size to hold Result field.
133 PolicyCommmHeader
->Result
= EFI_INVALID_PARAMETER
;
137 // If we're in the middle of a paginated dump and any other command is sent,
138 // pagination cache must be cleared.
139 if ((mPaginationCache
!= NULL
) && (InternalPolicyCommmHeader
->Command
!= mCurrentPaginationCommand
)) {
140 FreePool (mPaginationCache
);
141 mPaginationCache
= NULL
;
142 mPaginationCacheSize
= 0;
143 mCurrentPaginationCommand
= 0;
147 // Now we can process the command as it was sent.
149 PolicyCommmHeader
->Result
= EFI_ABORTED
; // Set a default return for incomplete commands.
150 switch (InternalPolicyCommmHeader
->Command
) {
151 case VAR_CHECK_POLICY_COMMAND_DISABLE
:
152 PolicyCommmHeader
->Result
= DisableVariablePolicy ();
155 case VAR_CHECK_POLICY_COMMAND_IS_ENABLED
:
156 // Make sure that we're dealing with a reasonable size.
157 // This add should be safe because these are fixed sizes so far.
158 ExpectedSize
+= sizeof (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS
);
159 if (InternalCommBufferSize
< ExpectedSize
) {
160 DEBUG ((DEBUG_INFO
, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__
, InternalCommBufferSize
, ExpectedSize
));
161 PolicyCommmHeader
->Result
= EFI_INVALID_PARAMETER
;
165 // Now that we know we've got a valid size, we can fill in the rest of the data.
166 IsEnabledParams
= (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS
*)((UINT8
*)CommBuffer
+ sizeof (VAR_CHECK_POLICY_COMM_HEADER
));
167 IsEnabledParams
->State
= IsVariablePolicyEnabled ();
168 PolicyCommmHeader
->Result
= EFI_SUCCESS
;
171 case VAR_CHECK_POLICY_COMMAND_REGISTER
:
172 // Make sure that we're dealing with a reasonable size.
173 // This add should be safe because these are fixed sizes so far.
174 ExpectedSize
+= sizeof (VARIABLE_POLICY_ENTRY
);
175 if (InternalCommBufferSize
< ExpectedSize
) {
176 DEBUG ((DEBUG_INFO
, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__
, InternalCommBufferSize
, ExpectedSize
));
177 PolicyCommmHeader
->Result
= EFI_INVALID_PARAMETER
;
181 // At the very least, we can assume that we're working with a valid policy entry.
182 // Time to compare its internal size.
183 PolicyEntry
= (VARIABLE_POLICY_ENTRY
*)((UINT8
*)InternalCommBuffer
+ sizeof (VAR_CHECK_POLICY_COMM_HEADER
));
184 if ((PolicyEntry
->Version
!= VARIABLE_POLICY_ENTRY_REVISION
) ||
185 (PolicyEntry
->Size
< sizeof (VARIABLE_POLICY_ENTRY
)) ||
186 EFI_ERROR (SafeUintnAdd (sizeof (VAR_CHECK_POLICY_COMM_HEADER
), PolicyEntry
->Size
, &ExpectedSize
)) ||
187 (InternalCommBufferSize
< ExpectedSize
))
189 DEBUG ((DEBUG_INFO
, "%a - Bad policy entry contents!\n", __FUNCTION__
));
190 PolicyCommmHeader
->Result
= EFI_INVALID_PARAMETER
;
194 PolicyCommmHeader
->Result
= RegisterVariablePolicy (PolicyEntry
);
197 case VAR_CHECK_POLICY_COMMAND_DUMP
:
198 // Make sure that we're dealing with a reasonable size.
199 // This add should be safe because these are fixed sizes so far.
200 ExpectedSize
+= sizeof (VAR_CHECK_POLICY_COMM_DUMP_PARAMS
) + VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE
;
201 if (InternalCommBufferSize
< ExpectedSize
) {
202 DEBUG ((DEBUG_INFO
, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__
, InternalCommBufferSize
, ExpectedSize
));
203 PolicyCommmHeader
->Result
= EFI_INVALID_PARAMETER
;
207 // Now that we know we've got a valid size, we can fill in the rest of the data.
208 DumpParamsIn
= (VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*)(InternalPolicyCommmHeader
+ 1);
209 DumpParamsOut
= (VAR_CHECK_POLICY_COMM_DUMP_PARAMS
*)(PolicyCommmHeader
+ 1);
211 // If we're requesting the first page, initialize the cache and get the sizes.
212 if (DumpParamsIn
->PageRequested
== 0) {
213 if (mPaginationCache
!= NULL
) {
214 FreePool (mPaginationCache
);
215 mPaginationCache
= NULL
;
218 // Determine what the required size is going to be.
219 DumpParamsOut
->TotalSize
= 0;
220 DumpParamsOut
->PageSize
= 0;
221 DumpParamsOut
->HasMore
= FALSE
;
223 SubCommandStatus
= DumpVariablePolicy (NULL
, &TempSize
);
224 if ((SubCommandStatus
== EFI_BUFFER_TOO_SMALL
) && (TempSize
> 0)) {
225 mCurrentPaginationCommand
= VAR_CHECK_POLICY_COMMAND_DUMP
;
226 mPaginationCacheSize
= TempSize
;
227 DumpParamsOut
->TotalSize
= TempSize
;
228 mPaginationCache
= AllocatePool (mPaginationCacheSize
);
229 if (mPaginationCache
== NULL
) {
230 SubCommandStatus
= EFI_OUT_OF_RESOURCES
;
234 // If we've allocated our pagination cache, we're good to cache.
235 if (mPaginationCache
!= NULL
) {
236 SubCommandStatus
= DumpVariablePolicy (mPaginationCache
, &TempSize
);
239 // Populate the remaining fields and we can boogie.
240 if (!EFI_ERROR (SubCommandStatus
) && (mPaginationCache
!= NULL
)) {
241 DumpParamsOut
->HasMore
= TRUE
;
243 } else if (mPaginationCache
!= NULL
) {
244 DumpParamsOut
->TotalSize
= (UINT32
)mPaginationCacheSize
;
245 DumpOutputBuffer
= (UINT8
*)(DumpParamsOut
+ 1);
247 // Make sure that we don't over-index the cache.
248 DumpTotalPages
= mPaginationCacheSize
/ VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE
;
249 if (mPaginationCacheSize
% VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE
!= 0) {
253 if (DumpParamsIn
->PageRequested
> DumpTotalPages
) {
254 SubCommandStatus
= EFI_INVALID_PARAMETER
;
256 // Figure out how far into the page cache we need to go for our next page.
257 // We know the blind subtraction won't be bad because we already checked for page 0.
258 DumpInputBuffer
= &mPaginationCache
[VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE
* (DumpParamsIn
->PageRequested
- 1)];
259 TempSize
= VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE
;
260 // If we're getting the last page, adjust the PageSize.
261 if (DumpParamsIn
->PageRequested
== DumpTotalPages
) {
262 TempSize
= mPaginationCacheSize
% VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE
;
265 CopyMem (DumpOutputBuffer
, DumpInputBuffer
, TempSize
);
266 DumpParamsOut
->PageSize
= TempSize
;
267 // If we just got the last page, settle up the cache.
268 if (DumpParamsIn
->PageRequested
== DumpTotalPages
) {
269 DumpParamsOut
->HasMore
= FALSE
;
270 FreePool (mPaginationCache
);
271 mPaginationCache
= NULL
;
272 mPaginationCacheSize
= 0;
273 mCurrentPaginationCommand
= 0;
274 // Otherwise, we could do more here.
276 DumpParamsOut
->HasMore
= TRUE
;
279 // If we made it this far, we're basically good.
280 SubCommandStatus
= EFI_SUCCESS
;
283 // If we've requested any other page than 0 and the cache is empty, we must have timed out.
285 DumpParamsOut
->TotalSize
= 0;
286 DumpParamsOut
->PageSize
= 0;
287 DumpParamsOut
->HasMore
= FALSE
;
288 SubCommandStatus
= EFI_TIMEOUT
;
291 // There's currently no use for this, but it shouldn't be hard to implement.
292 PolicyCommmHeader
->Result
= SubCommandStatus
;
295 case VAR_CHECK_POLICY_COMMAND_LOCK
:
296 PolicyCommmHeader
->Result
= LockVariablePolicy ();
300 // Mark unknown requested command as EFI_UNSUPPORTED.
301 DEBUG ((DEBUG_INFO
, "%a - Invalid command requested! %d\n", __FUNCTION__
, PolicyCommmHeader
->Command
));
302 PolicyCommmHeader
->Result
= EFI_UNSUPPORTED
;
308 "%a - Command %d returning %r.\n",
310 PolicyCommmHeader
->Command
,
311 PolicyCommmHeader
->Result
318 Constructor function of VarCheckPolicyLib to register VarCheck handler and
321 @retval EFI_SUCCESS The constructor executed correctly.
326 VarCheckPolicyLibCommonConstructor (
331 EFI_HANDLE DiscardedHandle
;
333 // Initialize the business logic with the internal GetVariable handler.
334 Status
= InitVariablePolicyLib (VariableServiceGetVariable
);
336 // Only proceed with init if the business logic could be initialized.
337 if (!EFI_ERROR (Status
)) {
338 // Register the VarCheck handler for SetVariable filtering.
339 // Forward the check to the business logic of the library.
340 VarCheckLibRegisterSetVariableCheckHandler (ValidateSetVariable
);
342 // Register the MMI handlers for receiving policy commands.
343 DiscardedHandle
= NULL
;
344 Status
= gMmst
->MmiHandlerRegister (
345 VarCheckPolicyLibMmiHandler
,
346 &gVarCheckPolicyLibMmiHandlerGuid
,
350 // Otherwise, there's not much we can do.
352 DEBUG ((DEBUG_ERROR
, "%a - Cannot Initialize VariablePolicyLib! %r\n", __FUNCTION__
, Status
));
353 ASSERT_EFI_ERROR (Status
);