]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c
MdeModulePkg: Apply uncrustify changes
[mirror_edk2.git] / MdeModulePkg / Library / VarCheckPolicyLib / VarCheckPolicyLib.c
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.
4
5 Copyright (c) Microsoft Corporation.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
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>
17
18 #include <Protocol/MmCommunication.h>
19
20 #include <Protocol/VariablePolicy.h>
21 #include <Library/VariablePolicyLib.h>
22
23 #include <Guid/VarCheckPolicyMmi.h>
24
25 #include "VarCheckPolicyLib.h"
26
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 // ================================================
32 EFI_STATUS
33 EFIAPI
34 VariableServiceGetVariable (
35 IN CHAR16 *VariableName,
36 IN EFI_GUID *VendorGuid,
37 OUT UINT32 *Attributes OPTIONAL,
38 IN OUT UINTN *DataSize,
39 OUT VOID *Data
40 );
41
42 UINT8 mSecurityEvalBuffer[VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE];
43
44 // Pagination Cache Variables
45 UINT8 *mPaginationCache = NULL;
46 UINTN mPaginationCacheSize = 0;
47 UINT32 mCurrentPaginationCommand = 0;
48
49 /**
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.
53
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.
58
59 @retval EFI_SUCCESS
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.
63
64 **/
65 STATIC
66 EFI_STATUS
67 EFIAPI
68 VarCheckPolicyLibMmiHandler (
69 IN EFI_HANDLE DispatchHandle,
70 IN CONST VOID *RegisterContext,
71 IN OUT VOID *CommBuffer,
72 IN OUT UINTN *CommBufferSize
73 )
74 {
75 UINTN InternalCommBufferSize;
76 VOID *InternalCommBuffer;
77 EFI_STATUS Status;
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;
86 UINTN DumpTotalPages;
87 VARIABLE_POLICY_ENTRY *PolicyEntry;
88 UINTN ExpectedSize;
89 UINT32 TempSize;
90
91 Status = EFI_SUCCESS;
92
93 //
94 // Validate some input parameters.
95 //
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;
100 }
101
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))
107 {
108 DEBUG ((DEBUG_ERROR, "%a - Invalid CommBuffer supplied! 0x%016lX[0x%016lX]\n", __FUNCTION__, CommBuffer, InternalCommBufferSize));
109 return EFI_INVALID_PARAMETER;
110 }
111
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;
117 }
118
119 //
120 // Before proceeding any further, copy the buffer internally so that we can compare
121 // without worrying about TOCTOU.
122 //
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))
130 {
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;
134 return EFI_SUCCESS;
135 }
136
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;
144 }
145
146 //
147 // Now we can process the command as it was sent.
148 //
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 ();
153 break;
154
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;
162 break;
163 }
164
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;
169 break;
170
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;
178 break;
179 }
180
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))
188 {
189 DEBUG ((DEBUG_INFO, "%a - Bad policy entry contents!\n", __FUNCTION__));
190 PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;
191 break;
192 }
193
194 PolicyCommmHeader->Result = RegisterVariablePolicy (PolicyEntry);
195 break;
196
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;
204 break;
205 }
206
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);
210
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;
216 }
217
218 // Determine what the required size is going to be.
219 DumpParamsOut->TotalSize = 0;
220 DumpParamsOut->PageSize = 0;
221 DumpParamsOut->HasMore = FALSE;
222 TempSize = 0;
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;
231 }
232 }
233
234 // If we've allocated our pagination cache, we're good to cache.
235 if (mPaginationCache != NULL) {
236 SubCommandStatus = DumpVariablePolicy (mPaginationCache, &TempSize);
237 }
238
239 // Populate the remaining fields and we can boogie.
240 if (!EFI_ERROR (SubCommandStatus) && (mPaginationCache != NULL)) {
241 DumpParamsOut->HasMore = TRUE;
242 }
243 } else if (mPaginationCache != NULL) {
244 DumpParamsOut->TotalSize = (UINT32)mPaginationCacheSize;
245 DumpOutputBuffer = (UINT8 *)(DumpParamsOut + 1);
246
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) {
250 DumpTotalPages++;
251 }
252
253 if (DumpParamsIn->PageRequested > DumpTotalPages) {
254 SubCommandStatus = EFI_INVALID_PARAMETER;
255 } else {
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;
263 }
264
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.
275 } else {
276 DumpParamsOut->HasMore = TRUE;
277 }
278
279 // If we made it this far, we're basically good.
280 SubCommandStatus = EFI_SUCCESS;
281 }
282
283 // If we've requested any other page than 0 and the cache is empty, we must have timed out.
284 } else {
285 DumpParamsOut->TotalSize = 0;
286 DumpParamsOut->PageSize = 0;
287 DumpParamsOut->HasMore = FALSE;
288 SubCommandStatus = EFI_TIMEOUT;
289 }
290
291 // There's currently no use for this, but it shouldn't be hard to implement.
292 PolicyCommmHeader->Result = SubCommandStatus;
293 break;
294
295 case VAR_CHECK_POLICY_COMMAND_LOCK:
296 PolicyCommmHeader->Result = LockVariablePolicy ();
297 break;
298
299 default:
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;
303 break;
304 }
305
306 DEBUG ((
307 DEBUG_VERBOSE,
308 "%a - Command %d returning %r.\n",
309 __FUNCTION__,
310 PolicyCommmHeader->Command,
311 PolicyCommmHeader->Result
312 ));
313
314 return Status;
315 }
316
317 /**
318 Constructor function of VarCheckPolicyLib to register VarCheck handler and
319 SW MMI handlers.
320
321 @retval EFI_SUCCESS The constructor executed correctly.
322
323 **/
324 EFI_STATUS
325 EFIAPI
326 VarCheckPolicyLibCommonConstructor (
327 VOID
328 )
329 {
330 EFI_STATUS Status;
331 EFI_HANDLE DiscardedHandle;
332
333 // Initialize the business logic with the internal GetVariable handler.
334 Status = InitVariablePolicyLib (VariableServiceGetVariable);
335
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);
341
342 // Register the MMI handlers for receiving policy commands.
343 DiscardedHandle = NULL;
344 Status = gMmst->MmiHandlerRegister (
345 VarCheckPolicyLibMmiHandler,
346 &gVarCheckPolicyLibMmiHandlerGuid,
347 &DiscardedHandle
348 );
349 }
350 // Otherwise, there's not much we can do.
351 else {
352 DEBUG ((DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __FUNCTION__, Status));
353 ASSERT_EFI_ERROR (Status);
354 }
355
356 return Status;
357 }