TianoCore EDK2 master
Loading...
Searching...
No Matches
VarCheckPolicyLib.c
1
11#include <Library/VarCheckLib.h>
12#include <Library/BaseLib.h>
13#include <Library/DebugLib.h>
14#include <Library/SafeIntLib.h>
18
20
21#include <Protocol/VariablePolicy.h>
22#include <Library/VariablePolicyLib.h>
23
24#include <Guid/VarCheckPolicyMmi.h>
25
26#include "VarCheckPolicyLib.h"
27
28// ================================================
29// As a VarCheck library, we're linked into the VariableServices
30// and may not be able to call them indirectly. To get around this,
31// use the internal GetVariable function to query the variable store.
32// ================================================
34EFIAPI
35VariableServiceGetVariable (
36 IN CHAR16 *VariableName,
37 IN EFI_GUID *VendorGuid,
38 OUT UINT32 *Attributes OPTIONAL,
39 IN OUT UINTN *DataSize,
40 OUT VOID *Data
41 );
42
43UINT8 mSecurityEvalBuffer[VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE];
44
45// Pagination Cache Variables
46UINT8 *mPaginationCache = NULL;
47UINTN mPaginationCacheSize = 0;
48UINT32 mCurrentPaginationCommand = 0;
49
68EFIAPI
69VarCheckPolicyLibMmiHandler (
70 IN EFI_HANDLE DispatchHandle,
71 IN CONST VOID *RegisterContext,
72 IN OUT VOID *CommBuffer,
73 IN OUT UINTN *CommBufferSize
74 )
75{
76 UINTN InternalCommBufferSize;
77 VOID *InternalCommBuffer;
78 EFI_STATUS Status;
79 EFI_STATUS SubCommandStatus;
80 VAR_CHECK_POLICY_COMM_HEADER *PolicyCommHeader;
81 VAR_CHECK_POLICY_COMM_HEADER *InternalPolicyCommHeader;
85 VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *GetInfoParamsInternal;
86 VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *GetInfoParamsExternal;
87 CHAR16 *InternalCopyOfOutputVariableName;
88 CHAR16 *ExternalCopyOfOutputVariableName;
89 UINT8 *DumpInputBuffer;
90 UINT8 *DumpOutputBuffer;
91 UINTN AllowedOutputVariableNameSize;
92 UINTN DumpTotalPages;
93 UINTN LocalSize;
94 VARIABLE_POLICY_ENTRY *PolicyEntry;
95 UINTN ExpectedSize;
96 UINT32 TempSize;
97
98 Status = EFI_SUCCESS;
99
100 //
101 // Validate some input parameters.
102 //
103 // If either of the pointers are NULL, we can't proceed.
104 if ((CommBuffer == NULL) || (CommBufferSize == NULL)) {
105 DEBUG ((DEBUG_INFO, "%a - Invalid comm buffer pointers!\n", __func__));
106 return EFI_INVALID_PARAMETER;
107 }
108
109 //
110 // Make sure that the buffer is valid.
111 // This should be covered by the SmiManage infrastructure, but just to be safe...
112 //
113 InternalCommBufferSize = *CommBufferSize;
114 if ((InternalCommBufferSize > VAR_CHECK_POLICY_MM_COMM_BUFFER_SIZE) ||
115 !VarCheckPolicyIsPrimaryBufferValid ((UINTN)CommBuffer, (UINT64)InternalCommBufferSize))
116 {
117 DEBUG ((DEBUG_ERROR, "%a - Invalid Primary Buffer (CommBuffer) supplied! 0x%016lX[0x%016lX]\n", __func__, CommBuffer, InternalCommBufferSize));
118 return EFI_INVALID_PARAMETER;
119 }
120
121 // If the size does not meet a minimum threshold, we cannot proceed.
122 ExpectedSize = sizeof (VAR_CHECK_POLICY_COMM_HEADER);
123 if (InternalCommBufferSize < ExpectedSize) {
124 DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
125 return EFI_INVALID_PARAMETER;
126 }
127
128 //
129 // Before proceeding any further, copy the buffer internally so that we can compare
130 // without worrying about TOCTOU.
131 //
132 InternalCommBuffer = &mSecurityEvalBuffer[0];
133 CopyMem (InternalCommBuffer, CommBuffer, InternalCommBufferSize);
134 PolicyCommHeader = CommBuffer;
135 InternalPolicyCommHeader = InternalCommBuffer;
136 // Check the revision and the signature of the comm header.
137 if ((InternalPolicyCommHeader->Signature != VAR_CHECK_POLICY_COMM_SIG) ||
138 (InternalPolicyCommHeader->Revision != VAR_CHECK_POLICY_COMM_REVISION))
139 {
140 DEBUG ((DEBUG_INFO, "%a - Signature or revision are incorrect!\n", __func__));
141 // We have verified the buffer is not null and have enough size to hold Result field.
142 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
143 return EFI_SUCCESS;
144 }
145
146 // If we're in the middle of a paginated dump and any other command is sent,
147 // pagination cache must be cleared.
148 if ((mPaginationCache != NULL) && (InternalPolicyCommHeader->Command != mCurrentPaginationCommand)) {
149 FreePool (mPaginationCache);
150 mPaginationCache = NULL;
151 mPaginationCacheSize = 0;
152 mCurrentPaginationCommand = 0;
153 }
154
155 //
156 // Now we can process the command as it was sent.
157 //
158 PolicyCommHeader->Result = EFI_ABORTED; // Set a default return for incomplete commands.
159 switch (InternalPolicyCommHeader->Command) {
160 case VAR_CHECK_POLICY_COMMAND_DISABLE:
161 PolicyCommHeader->Result = DisableVariablePolicy ();
162 break;
163
164 case VAR_CHECK_POLICY_COMMAND_IS_ENABLED:
165 // Make sure that we're dealing with a reasonable size.
166 // This add should be safe because these are fixed sizes so far.
167 ExpectedSize += sizeof (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS);
168 if (InternalCommBufferSize < ExpectedSize) {
169 DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
170 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
171 break;
172 }
173
174 // Now that we know we've got a valid size, we can fill in the rest of the data.
175 IsEnabledParams = (VAR_CHECK_POLICY_COMM_IS_ENABLED_PARAMS *)((UINT8 *)CommBuffer + sizeof (VAR_CHECK_POLICY_COMM_HEADER));
176 IsEnabledParams->State = IsVariablePolicyEnabled ();
177 PolicyCommHeader->Result = EFI_SUCCESS;
178 break;
179
180 case VAR_CHECK_POLICY_COMMAND_REGISTER:
181 // Make sure that we're dealing with a reasonable size.
182 // This add should be safe because these are fixed sizes so far.
183 ExpectedSize += sizeof (VARIABLE_POLICY_ENTRY);
184 if (InternalCommBufferSize < ExpectedSize) {
185 DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
186 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
187 break;
188 }
189
190 // At the very least, we can assume that we're working with a valid policy entry.
191 // Time to compare its internal size.
192 PolicyEntry = (VARIABLE_POLICY_ENTRY *)((UINT8 *)InternalCommBuffer + sizeof (VAR_CHECK_POLICY_COMM_HEADER));
193 if ((PolicyEntry->Version != VARIABLE_POLICY_ENTRY_REVISION) ||
194 (PolicyEntry->Size < sizeof (VARIABLE_POLICY_ENTRY)) ||
195 EFI_ERROR (SafeUintnAdd (sizeof (VAR_CHECK_POLICY_COMM_HEADER), PolicyEntry->Size, &ExpectedSize)) ||
196 (InternalCommBufferSize < ExpectedSize))
197 {
198 DEBUG ((DEBUG_INFO, "%a - Bad policy entry contents!\n", __func__));
199 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
200 break;
201 }
202
203 PolicyCommHeader->Result = RegisterVariablePolicy (PolicyEntry);
204 break;
205
206 case VAR_CHECK_POLICY_COMMAND_DUMP:
207 // Make sure that we're dealing with a reasonable size.
208 // This add should be safe because these are fixed sizes so far.
209 ExpectedSize += sizeof (VAR_CHECK_POLICY_COMM_DUMP_PARAMS) + VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
210 if (InternalCommBufferSize < ExpectedSize) {
211 DEBUG ((DEBUG_INFO, "%a - Bad comm buffer size! %d < %d\n", __func__, InternalCommBufferSize, ExpectedSize));
212 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
213 break;
214 }
215
216 // Now that we know we've got a valid size, we can fill in the rest of the data.
217 DumpParamsIn = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(InternalPolicyCommHeader + 1);
218 DumpParamsOut = (VAR_CHECK_POLICY_COMM_DUMP_PARAMS *)(PolicyCommHeader + 1);
219
220 // If we're requesting the first page, initialize the cache and get the sizes.
221 if (DumpParamsIn->PageRequested == 0) {
222 if (mPaginationCache != NULL) {
223 FreePool (mPaginationCache);
224 mPaginationCache = NULL;
225 }
226
227 // Determine what the required size is going to be.
228 DumpParamsOut->TotalSize = 0;
229 DumpParamsOut->PageSize = 0;
230 DumpParamsOut->HasMore = FALSE;
231 TempSize = 0;
232 SubCommandStatus = DumpVariablePolicy (NULL, &TempSize);
233 if ((SubCommandStatus == EFI_BUFFER_TOO_SMALL) && (TempSize > 0)) {
234 mCurrentPaginationCommand = VAR_CHECK_POLICY_COMMAND_DUMP;
235 mPaginationCacheSize = TempSize;
236 DumpParamsOut->TotalSize = TempSize;
237 mPaginationCache = AllocatePool (mPaginationCacheSize);
238 if (mPaginationCache == NULL) {
239 SubCommandStatus = EFI_OUT_OF_RESOURCES;
240 }
241 }
242
243 // If we've allocated our pagination cache, we're good to cache.
244 if (mPaginationCache != NULL) {
245 SubCommandStatus = DumpVariablePolicy (mPaginationCache, &TempSize);
246 }
247
248 // Populate the remaining fields and we can boogie.
249 if (!EFI_ERROR (SubCommandStatus) && (mPaginationCache != NULL)) {
250 DumpParamsOut->HasMore = TRUE;
251 }
252 } else if (mPaginationCache != NULL) {
253 DumpParamsOut->TotalSize = (UINT32)mPaginationCacheSize;
254 DumpOutputBuffer = (UINT8 *)(DumpParamsOut + 1);
255
256 // Make sure that we don't over-index the cache.
257 DumpTotalPages = mPaginationCacheSize / VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
258 if (mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE != 0) {
259 DumpTotalPages++;
260 }
261
262 if (DumpParamsIn->PageRequested > DumpTotalPages) {
263 SubCommandStatus = EFI_INVALID_PARAMETER;
264 } else {
265 // Figure out how far into the page cache we need to go for our next page.
266 // We know the blind subtraction won't be bad because we already checked for page 0.
267 DumpInputBuffer = &mPaginationCache[VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE * (DumpParamsIn->PageRequested - 1)];
268 TempSize = VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
269 // If we're getting the last page, adjust the PageSize.
270 if (DumpParamsIn->PageRequested == DumpTotalPages) {
271 TempSize = mPaginationCacheSize % VAR_CHECK_POLICY_MM_DUMP_BUFFER_SIZE;
272 }
273
274 CopyMem (DumpOutputBuffer, DumpInputBuffer, TempSize);
275 DumpParamsOut->PageSize = TempSize;
276 // If we just got the last page, settle up the cache.
277 if (DumpParamsIn->PageRequested == DumpTotalPages) {
278 DumpParamsOut->HasMore = FALSE;
279 FreePool (mPaginationCache);
280 mPaginationCache = NULL;
281 mPaginationCacheSize = 0;
282 mCurrentPaginationCommand = 0;
283 // Otherwise, we could do more here.
284 } else {
285 DumpParamsOut->HasMore = TRUE;
286 }
287
288 // If we made it this far, we're basically good.
289 SubCommandStatus = EFI_SUCCESS;
290 }
291
292 // If we've requested any other page than 0 and the cache is empty, we must have timed out.
293 } else {
294 DumpParamsOut->TotalSize = 0;
295 DumpParamsOut->PageSize = 0;
296 DumpParamsOut->HasMore = FALSE;
297 SubCommandStatus = EFI_TIMEOUT;
298 }
299
300 // There's currently no use for this, but it shouldn't be hard to implement.
301 PolicyCommHeader->Result = SubCommandStatus;
302 break;
303
304 case VAR_CHECK_POLICY_COMMAND_LOCK:
305 PolicyCommHeader->Result = LockVariablePolicy ();
306 break;
307
308 case VAR_CHECK_POLICY_COMMAND_GET_INFO:
309 case VAR_CHECK_POLICY_COMMAND_GET_LOCK_VAR_STATE_INFO:
310 ExpectedSize += VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END + VAR_CHECK_POLICY_MM_GET_INFO_BUFFER_SIZE;
311
312 if (InternalCommBufferSize < ExpectedSize) {
313 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
314 break;
315 }
316
317 GetInfoParamsInternal = (VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *)(InternalPolicyCommHeader + 1);
318 GetInfoParamsExternal = (VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS *)(PolicyCommHeader + 1);
319
320 SubCommandStatus = SafeUintnSub (
321 VAR_CHECK_POLICY_MM_GET_INFO_BUFFER_SIZE,
322 GetInfoParamsInternal->InputVariableNameSize,
323 &AllowedOutputVariableNameSize
324 );
325 if (EFI_ERROR (SubCommandStatus)) {
326 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
327 break;
328 }
329
330 if (GetInfoParamsInternal->OutputVariableNameSize > 0) {
331 SubCommandStatus = SafeUintnAdd (
332 ((UINTN)GetInfoParamsInternal + VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END),
333 (UINTN)GetInfoParamsInternal->InputVariableNameSize,
334 (UINTN *)&InternalCopyOfOutputVariableName
335 );
336 if (EFI_ERROR (SubCommandStatus)) {
337 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
338 break;
339 }
340 } else {
341 InternalCopyOfOutputVariableName = NULL;
342 }
343
344 ZeroMem (&GetInfoParamsInternal->OutputPolicyEntry, sizeof (GetInfoParamsInternal->OutputPolicyEntry));
345 ZeroMem (&GetInfoParamsExternal->OutputPolicyEntry, sizeof (GetInfoParamsExternal->OutputPolicyEntry));
346
347 LocalSize = (UINTN)GetInfoParamsInternal->OutputVariableNameSize;
348
349 if (InternalPolicyCommHeader->Command == VAR_CHECK_POLICY_COMMAND_GET_INFO) {
350 SubCommandStatus = GetVariablePolicyInfo (
351 GetInfoParamsInternal->InputVariableName,
352 &GetInfoParamsInternal->InputVendorGuid,
353 &LocalSize,
354 &GetInfoParamsInternal->OutputPolicyEntry.VariablePolicy,
355 InternalCopyOfOutputVariableName
356 );
357 } else if (InternalPolicyCommHeader->Command == VAR_CHECK_POLICY_COMMAND_GET_LOCK_VAR_STATE_INFO) {
358 SubCommandStatus = GetLockOnVariableStateVariablePolicyInfo (
359 GetInfoParamsInternal->InputVariableName,
360 &GetInfoParamsInternal->InputVendorGuid,
361 &LocalSize,
362 &GetInfoParamsInternal->OutputPolicyEntry.LockOnVarStatePolicy,
363 InternalCopyOfOutputVariableName
364 );
365 } else {
366 PolicyCommHeader->Result = EFI_INVALID_PARAMETER;
367 break;
368 }
369
370 if (EFI_ERROR (SubCommandStatus) && (SubCommandStatus != EFI_BUFFER_TOO_SMALL)) {
371 PolicyCommHeader->Result = SubCommandStatus;
372 break;
373 }
374
375 if (EFI_ERROR (SafeUintnToUint32 (LocalSize, &GetInfoParamsInternal->OutputVariableNameSize))) {
376 PolicyCommHeader->Result = EFI_BAD_BUFFER_SIZE;
377 break;
378 }
379
380 ASSERT (sizeof (GetInfoParamsInternal->OutputPolicyEntry) == sizeof (GetInfoParamsExternal->OutputPolicyEntry));
381 CopyMem (
382 &GetInfoParamsExternal->OutputPolicyEntry,
383 &GetInfoParamsInternal->OutputPolicyEntry,
384 sizeof (GetInfoParamsExternal->OutputPolicyEntry)
385 );
386
387 GetInfoParamsExternal->OutputVariableNameSize = GetInfoParamsInternal->OutputVariableNameSize;
388 if (SubCommandStatus == EFI_BUFFER_TOO_SMALL) {
389 PolicyCommHeader->Result = EFI_BUFFER_TOO_SMALL;
390 break;
391 }
392
393 SubCommandStatus = SafeUintnAdd (
394 ((UINTN)GetInfoParamsExternal + VAR_CHECK_POLICY_COMM_GET_INFO_PARAMS_END),
395 (UINTN)GetInfoParamsInternal->InputVariableNameSize,
396 (UINTN *)&ExternalCopyOfOutputVariableName
397 );
398 if (EFI_ERROR (SubCommandStatus)) {
399 PolicyCommHeader->Result = EFI_BAD_BUFFER_SIZE;
400 break;
401 }
402
403 if (GetInfoParamsInternal->OutputVariableNameSize > 0) {
404 SubCommandStatus = StrnCpyS (
405 ExternalCopyOfOutputVariableName,
406 AllowedOutputVariableNameSize,
407 InternalCopyOfOutputVariableName,
408 (UINTN)GetInfoParamsInternal->OutputVariableNameSize
409 );
410 ASSERT_EFI_ERROR (SubCommandStatus);
411 } else {
412 // The comm buffer should always have the space for the variable policy output
413 // variable name. Fill it with NULL chars if a variable name is not present so
414 // it has a consistent value in the case of variable name absence.
415 SetMem (ExternalCopyOfOutputVariableName, AllowedOutputVariableNameSize, CHAR_NULL);
416 }
417
418 PolicyCommHeader->Result = SubCommandStatus;
419
420 break;
421
422 default:
423 // Mark unknown requested command as EFI_UNSUPPORTED.
424 DEBUG ((DEBUG_INFO, "%a - Invalid command requested! %d\n", __func__, PolicyCommHeader->Command));
425 PolicyCommHeader->Result = EFI_UNSUPPORTED;
426 break;
427 }
428
429 DEBUG ((
430 DEBUG_VERBOSE,
431 "%a - Command %d returning %r.\n",
432 __func__,
433 PolicyCommHeader->Command,
434 PolicyCommHeader->Result
435 ));
436
437 return Status;
438}
439
448EFIAPI
449VarCheckPolicyLibCommonConstructor (
450 VOID
451 )
452{
453 EFI_STATUS Status;
454 EFI_HANDLE DiscardedHandle;
455
456 // Initialize the business logic with the internal GetVariable handler.
457 Status = InitVariablePolicyLib (VariableServiceGetVariable);
458
459 // Only proceed with init if the business logic could be initialized.
460 if (!EFI_ERROR (Status)) {
461 // Register the VarCheck handler for SetVariable filtering.
462 // Forward the check to the business logic of the library.
463 VarCheckLibRegisterSetVariableCheckHandler (ValidateSetVariable);
464
465 // Register the MMI handlers for receiving policy commands.
466 DiscardedHandle = NULL;
467 Status = gMmst->MmiHandlerRegister (
468 VarCheckPolicyLibMmiHandler,
469 &gVarCheckPolicyLibMmiHandlerGuid,
470 &DiscardedHandle
471 );
472 }
473 // Otherwise, there's not much we can do.
474 else {
475 DEBUG ((DEBUG_ERROR, "%a - Cannot Initialize VariablePolicyLib! %r\n", __func__, Status));
476 ASSERT_EFI_ERROR (Status);
477 }
478
479 return Status;
480}
UINT64 UINTN
RETURN_STATUS EFIAPI StrnCpyS(OUT CHAR16 *Destination, IN UINTN DestMax, IN CONST CHAR16 *Source, IN UINTN Length)
Definition: SafeString.c:310
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI SetMem(OUT VOID *Buffer, IN UINTN Length, IN UINT8 Value)
Definition: SetMemWrapper.c:38
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define CONST
Definition: Base.h:259
#define STATIC
Definition: Base.h:264
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
RETURN_STATUS EFIAPI SafeUintnAdd(IN UINTN Augend, IN UINTN Addend, OUT UINTN *Result)
Definition: SafeIntLib32.c:338
RETURN_STATUS EFIAPI SafeUintnToUint32(IN UINTN Operand, OUT UINT32 *Result)
Definition: SafeIntLib32.c:177
RETURN_STATUS EFIAPI SafeUintnSub(IN UINTN Minuend, IN UINTN Subtrahend, OUT UINTN *Result)
Definition: SafeIntLib32.c:384
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_STATUS EFIAPI VarCheckLibRegisterSetVariableCheckHandler(IN VAR_CHECK_SET_VARIABLE_CHECK_HANDLER Handler)
Definition: VarCheckLib.c:440
Definition: Base.h:213