]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/VarCheckPolicyLib/VarCheckPolicyLib.c
MdeModulePkg/FileExplorerLib: remove redundant null pointer check
[mirror_edk2.git] / MdeModulePkg / Library / VarCheckPolicyLib / VarCheckPolicyLib.c
CommitLineData
483449c9
BB
1/** @file -- VarCheckPolicyLib.c\r
2This is a NULL library instance that leverages the VarCheck interface\r
3and the business logic behind the VariablePolicy code to make its decisions.\r
4\r
5Copyright (c) Microsoft Corporation.\r
6SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include <Library/VarCheckLib.h>\r
11#include <Library/BaseLib.h>\r
12#include <Library/DebugLib.h>\r
13#include <Library/SafeIntLib.h>\r
14#include <Library/MmServicesTableLib.h>\r
15#include <Library/SmmMemLib.h>\r
16#include <Library/BaseMemoryLib.h>\r
17#include <Library/MemoryAllocationLib.h>\r
18\r
19#include <Protocol/MmCommunication.h>\r
20\r
21#include <Protocol/VariablePolicy.h>\r
22#include <Library/VariablePolicyLib.h>\r
23\r
24#include <Guid/VarCheckPolicyMmi.h>\r
25\r
26//================================================\r
27// As a VarCheck library, we're linked into the VariableServices\r
28// and may not be able to call them indirectly. To get around this,\r
29// use the internal GetVariable function to query the variable store.\r
30//================================================\r
31EFI_STATUS\r
32EFIAPI\r
33VariableServiceGetVariable (\r
34 IN CHAR16 *VariableName,\r
35 IN EFI_GUID *VendorGuid,\r
36 OUT UINT32 *Attributes OPTIONAL,\r
37 IN OUT UINTN *DataSize,\r
38 OUT VOID *Data\r
39 );\r
40\r
41\r
42UINT8 mSecurityEvalBuffer[VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE];\r
43\r
44// Pagination Cache Variables\r
45UINT8 *mPaginationCache = NULL;\r
46UINTN mPaginationCacheSize = 0;\r
47UINT32 mCurrentPaginationCommand = 0;\r
48\r
49\r
50/**\r
51 MM Communication Handler to recieve commands from the DXE protocol for\r
52 Variable Policies. This communication channel is used to register new policies\r
53 and poll and toggle the enforcement of variable policies.\r
54\r
55 @param[in] DispatchHandle All parameters standard to MM communications convention.\r
56 @param[in] RegisterContext All parameters standard to MM communications convention.\r
57 @param[in,out] CommBuffer All parameters standard to MM communications convention.\r
58 @param[in,out] CommBufferSize All parameters standard to MM communications convention.\r
59\r
60 @retval EFI_SUCCESS\r
61 @retval EFI_INVALID_PARAMETER CommBuffer or CommBufferSize is null pointer.\r
62 @retval EFI_INVALID_PARAMETER CommBuffer size is wrong.\r
63 @retval EFI_INVALID_PARAMETER Revision or signature don't match.\r
64\r
65**/\r
66STATIC\r
67EFI_STATUS\r
68EFIAPI\r
69VarCheckPolicyLibMmiHandler (\r
70 IN EFI_HANDLE DispatchHandle,\r
71 IN CONST VOID *RegisterContext,\r
72 IN OUT VOID *CommBuffer,\r
73 IN OUT UINTN *CommBufferSize\r
74 )\r
75{\r
76 UINTN InternalCommBufferSize;\r
77 VOID *InternalCommBuffer;\r
78 EFI_STATUS Status;\r
79 EFI_STATUS SubCommandStatus;\r
80 VAR_CHECK_POLICY_COMM_HEADER *PolicyCommmHeader;\r
81 VAR_CHECK_POLICY_COMM_HEADER *InternalPolicyCommmHeader;\r
82 VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *IsEnabledParams;\r
83 VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsIn;\r
84 VAR_CHECK_POLICY_COMM_DUMP_PARAMS *DumpParamsOut;\r
85 UINT8 *DumpInputBuffer;\r
86 UINT8 *DumpOutputBuffer;\r
87 UINTN DumpTotalPages;\r
88 VARIABLE_POLICY_ENTRY *PolicyEntry;\r
89 UINTN ExpectedSize;\r
90 UINT32 TempSize;\r
91\r
92 Status = EFI_SUCCESS;\r
93\r
94 //\r
95 // Validate some input parameters.\r
96 //\r
97 // If either of the pointers are NULL, we can't proceed.\r
98 if (CommBuffer == NULL || CommBufferSize == NULL) {\r
99 DEBUG(( DEBUG_INFO, "%a - Invalid comm buffer pointers!\n", __FUNCTION__ ));\r
100 return EFI_INVALID_PARAMETER;\r
101 }\r
102 // Make sure that the buffer does not overlap SMM.\r
103 // This should be covered by the SmiManage infrastructure, but just to be safe...\r
104 InternalCommBufferSize = *CommBufferSize;\r
105 if (InternalCommBufferSize > VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE || !SmmIsBufferOutsideSmmValid((UINTN)CommBuffer, (UINT64)InternalCommBufferSize)) {\r
106 DEBUG ((DEBUG_ERROR, "%a - Invalid CommBuffer supplied! 0x%016lX[0x%016lX]\n", __FUNCTION__, CommBuffer, InternalCommBufferSize));\r
107 return EFI_INVALID_PARAMETER;\r
108 }\r
109 // If the size does not meet a minimum threshold, we cannot proceed.\r
110 ExpectedSize = sizeof(VAR_CHECK_POLICY_COMM_HEADER);\r
111 if (InternalCommBufferSize < ExpectedSize) {\r
112 DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));\r
113 return EFI_INVALID_PARAMETER;\r
114 }\r
115\r
116 //\r
117 // Before proceeding any further, copy the buffer internally so that we can compare\r
118 // without worrying about TOCTOU.\r
119 //\r
120 InternalCommBuffer = &mSecurityEvalBuffer[0];\r
121 CopyMem(InternalCommBuffer, CommBuffer, InternalCommBufferSize);\r
122 PolicyCommmHeader = CommBuffer;\r
123 InternalPolicyCommmHeader = InternalCommBuffer;\r
124 // Check the revision and the signature of the comm header.\r
125 if (InternalPolicyCommmHeader->Signature != VAR_CHECK_POLICY_COMM_SIG ||\r
126 InternalPolicyCommmHeader->Revision != VAR_CHECK_POLICY_COMM_REVISION) {\r
127 DEBUG(( DEBUG_INFO, "%a - Signature or revision are incorrect!\n", __FUNCTION__ ));\r
128 // We have verified the buffer is not null and have enough size to hold Result field.\r
129 PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;\r
130 return EFI_SUCCESS;\r
131 }\r
132\r
133 // If we're in the middle of a paginated dump and any other command is sent,\r
134 // pagination cache must be cleared.\r
135 if (mPaginationCache != NULL && InternalPolicyCommmHeader->Command != mCurrentPaginationCommand) {\r
136 FreePool (mPaginationCache);\r
137 mPaginationCache = NULL;\r
138 mPaginationCacheSize = 0;\r
139 mCurrentPaginationCommand = 0;\r
140 }\r
141\r
142 //\r
143 // Now we can process the command as it was sent.\r
144 //\r
145 PolicyCommmHeader->Result = EFI_ABORTED; // Set a default return for incomplete commands.\r
146 switch(InternalPolicyCommmHeader->Command) {\r
147 case VAR_CHECK_POLICY_COMMAND_DISABLE:\r
148 PolicyCommmHeader->Result = DisableVariablePolicy();\r
149 break;\r
150\r
151 case VAR_CHECK_POLICY_COMMAND_IS_ENABLED:\r
152 // Make sure that we're dealing with a reasonable size.\r
153 // This add should be safe because these are fixed sizes so far.\r
154 ExpectedSize += sizeof(VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS);\r
155 if (InternalCommBufferSize < ExpectedSize) {\r
156 DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));\r
157 PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;\r
158 break;\r
159 }\r
160\r
161 // Now that we know we've got a valid size, we can fill in the rest of the data.\r
162 IsEnabledParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS*)((UINT8*)CommBuffer + sizeof(VAR_CHECK_POLICY_COMM_HEADER));\r
163 IsEnabledParams->State = IsVariablePolicyEnabled();\r
164 PolicyCommmHeader->Result = EFI_SUCCESS;\r
165 break;\r
166\r
167 case VAR_CHECK_POLICY_COMMAND_REGISTER:\r
168 // Make sure that we're dealing with a reasonable size.\r
169 // This add should be safe because these are fixed sizes so far.\r
170 ExpectedSize += sizeof(VARIABLE_POLICY_ENTRY);\r
171 if (InternalCommBufferSize < ExpectedSize) {\r
172 DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));\r
173 PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;\r
174 break;\r
175 }\r
176\r
177 // At the very least, we can assume that we're working with a valid policy entry.\r
178 // Time to compare its internal size.\r
179 PolicyEntry = (VARIABLE_POLICY_ENTRY*)((UINT8*)InternalCommBuffer + sizeof(VAR_CHECK_POLICY_COMM_HEADER));\r
180 if (PolicyEntry->Version != VARIABLE_POLICY_ENTRY_REVISION ||\r
181 PolicyEntry->Size < sizeof(VARIABLE_POLICY_ENTRY) ||\r
182 EFI_ERROR(SafeUintnAdd(sizeof(VAR_CHECK_POLICY_COMM_HEADER), PolicyEntry->Size, &ExpectedSize)) ||\r
183 InternalCommBufferSize < ExpectedSize) {\r
184 DEBUG(( DEBUG_INFO, "%a - Bad policy entry contents!\n", __FUNCTION__ ));\r
185 PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;\r
186 break;\r
187 }\r
188\r
189 PolicyCommmHeader->Result = RegisterVariablePolicy( PolicyEntry );\r
190 break;\r
191\r
192 case VAR_CHECK_POLICY_COMMAND_DUMP:\r
193 // Make sure that we're dealing with a reasonable size.\r
194 // This add should be safe because these are fixed sizes so far.\r
195 ExpectedSize += sizeof(VAR_CHECK_POLICY_COMM_DUMP_PARAMS) + VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;\r
196 if (InternalCommBufferSize < ExpectedSize) {\r
197 DEBUG(( DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __FUNCTION__, InternalCommBufferSize, ExpectedSize ));\r
198 PolicyCommmHeader->Result = EFI_INVALID_PARAMETER;\r
199 break;\r
200 }\r
201\r
202 // Now that we know we've got a valid size, we can fill in the rest of the data.\r
203 DumpParamsIn = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS*)(InternalPolicyCommmHeader + 1);\r
204 DumpParamsOut = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS*)(PolicyCommmHeader + 1);\r
205\r
206 // If we're requesting the first page, initialize the cache and get the sizes.\r
207 if (DumpParamsIn->PageRequested == 0) {\r
208 if (mPaginationCache != NULL) {\r
209 FreePool (mPaginationCache);\r
210 mPaginationCache = NULL;\r
211 }\r
212\r
213 // Determine what the required size is going to be.\r
214 DumpParamsOut->TotalSize = 0;\r
215 DumpParamsOut->PageSize = 0;\r
216 DumpParamsOut->HasMore = FALSE;\r
217 SubCommandStatus = DumpVariablePolicy (NULL, &TempSize);\r
218 if (SubCommandStatus == EFI_BUFFER_TOO_SMALL && TempSize > 0) {\r
219 mCurrentPaginationCommand = VAR_CHECK_POLICY_COMMAND_DUMP;\r
220 mPaginationCacheSize = TempSize;\r
221 DumpParamsOut->TotalSize = TempSize;\r
222 mPaginationCache = AllocatePool (mPaginationCacheSize);\r
223 if (mPaginationCache == NULL) {\r
224 SubCommandStatus = EFI_OUT_OF_RESOURCES;\r
225 }\r
226 }\r
227\r
228 // If we've allocated our pagination cache, we're good to cache.\r
229 if (mPaginationCache != NULL) {\r
230 SubCommandStatus = DumpVariablePolicy (mPaginationCache, &TempSize);\r
231 }\r
232\r
233 // Populate the remaining fields and we can boogie.\r
234 if (!EFI_ERROR (SubCommandStatus) && mPaginationCache != NULL) {\r
235 DumpParamsOut->HasMore = TRUE;\r
236 }\r
237 } else if (mPaginationCache != NULL) {\r
238 DumpParamsOut->TotalSize = (UINT32)mPaginationCacheSize;\r
239 DumpOutputBuffer = (UINT8*)(DumpParamsOut + 1);\r
240\r
241 // Make sure that we don't over-index the cache.\r
242 DumpTotalPages = mPaginationCacheSize / VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;\r
243 if (mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE != 0) {\r
244 DumpTotalPages++;\r
245 }\r
246 if (DumpParamsIn->PageRequested > DumpTotalPages) {\r
247 SubCommandStatus = EFI_INVALID_PARAMETER;\r
248 } else {\r
249 // Figure out how far into the page cache we need to go for our next page.\r
250 // We know the blind subtraction won't be bad because we already checked for page 0.\r
251 DumpInputBuffer = &mPaginationCache[VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE * (DumpParamsIn->PageRequested - 1)];\r
252 TempSize = VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;\r
253 // If we're getting the last page, adjust the PageSize.\r
254 if (DumpParamsIn->PageRequested == DumpTotalPages) {\r
255 TempSize = mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;\r
256 }\r
257 CopyMem (DumpOutputBuffer, DumpInputBuffer, TempSize);\r
258 DumpParamsOut->PageSize = TempSize;\r
259 // If we just got the last page, settle up the cache.\r
260 if (DumpParamsIn->PageRequested == DumpTotalPages) {\r
261 DumpParamsOut->HasMore = FALSE;\r
262 FreePool (mPaginationCache);\r
263 mPaginationCache = NULL;\r
264 mPaginationCacheSize = 0;\r
265 mCurrentPaginationCommand = 0;\r
266 // Otherwise, we could do more here.\r
267 } else {\r
268 DumpParamsOut->HasMore = TRUE;\r
269 }\r
270\r
271 // If we made it this far, we're basically good.\r
272 SubCommandStatus = EFI_SUCCESS;\r
273 }\r
274 // If we've requested any other page than 0 and the cache is empty, we must have timed out.\r
275 } else {\r
276 DumpParamsOut->TotalSize = 0;\r
277 DumpParamsOut->PageSize = 0;\r
278 DumpParamsOut->HasMore = FALSE;\r
279 SubCommandStatus = EFI_TIMEOUT;\r
280 }\r
281\r
282 // There's currently no use for this, but it shouldn't be hard to implement.\r
283 PolicyCommmHeader->Result = SubCommandStatus;\r
284 break;\r
285\r
286 case VAR_CHECK_POLICY_COMMAND_LOCK:\r
287 PolicyCommmHeader->Result = LockVariablePolicy();\r
288 break;\r
289\r
290 default:\r
291 // Mark unknown requested command as EFI_UNSUPPORTED.\r
292 DEBUG(( DEBUG_INFO, "%a - Invalid command requested! %d\n", __FUNCTION__, PolicyCommmHeader->Command ));\r
293 PolicyCommmHeader->Result = EFI_UNSUPPORTED;\r
294 break;\r
295 }\r
296\r
297 DEBUG(( DEBUG_VERBOSE, "%a - Command %d returning %r.\n", __FUNCTION__,\r
298 PolicyCommmHeader->Command, PolicyCommmHeader->Result ));\r
299\r
300 return Status;\r
301}\r
302\r
303\r
304/**\r
305 Constructor function of VarCheckPolicyLib to register VarCheck handler and\r
306 SW MMI handlers.\r
307\r
308 @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
309 @param[in] SystemTable A pointer to the EFI System Table.\r
310\r
311 @retval EFI_SUCCESS The constructor executed correctly.\r
312\r
313**/\r
314EFI_STATUS\r
315EFIAPI\r
316VarCheckPolicyLibConstructor (\r
317 IN EFI_HANDLE ImageHandle,\r
318 IN EFI_SYSTEM_TABLE *SystemTable\r
319 )\r
320{\r
321 EFI_STATUS Status;\r
322 EFI_HANDLE DiscardedHandle;\r
323\r
324 // Initialize the business logic with the internal GetVariable handler.\r
325 Status = InitVariablePolicyLib( VariableServiceGetVariable );\r
326\r
327 // Only proceed with init if the business logic could be initialized.\r
328 if (!EFI_ERROR( Status )) {\r
329 // Register the VarCheck handler for SetVariable filtering.\r
330 // Forward the check to the business logic of the library.\r
331 VarCheckLibRegisterSetVariableCheckHandler( ValidateSetVariable );\r
332\r
333 // Register the MMI handlers for receiving policy commands.\r
334 DiscardedHandle = NULL;\r
335 Status = gMmst->MmiHandlerRegister( VarCheckPolicyLibMmiHandler,\r
336 &gVarCheckPolicyLibMmiHandlerGuid,\r
337 &DiscardedHandle );\r
338 }\r
339 // Otherwise, there's not much we can do.\r
340 else {\r
341 DEBUG(( DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __FUNCTION__, Status ));\r
342 ASSERT_EFI_ERROR( Status );\r
343 }\r
344\r
345 return Status;\r
346}\r