]>
Commit | Line | Data |
---|---|---|
355b181f BB |
1 | /** @file -- VariablePolicyLib.c\r |
2 | Business logic for Variable Policy enforcement.\r | |
3 | \r | |
4 | Copyright (c) Microsoft Corporation.\r | |
5 | SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
6 | \r | |
7 | **/\r | |
8 | \r | |
9 | #include <Uefi.h>\r | |
10 | \r | |
11 | #include <Library/SafeIntLib.h>\r | |
12 | #include <Library/MemoryAllocationLib.h>\r | |
13 | #include <Library/BaseMemoryLib.h>\r | |
14 | #include <Library/DebugLib.h>\r | |
15 | #include <Library/PcdLib.h>\r | |
16 | \r | |
17 | #include <Protocol/VariablePolicy.h>\r | |
18 | #include <Library/VariablePolicyLib.h>\r | |
19 | \r | |
20 | \r | |
21 | // IMPORTANT NOTE: This library is currently rife with multiple return statements\r | |
22 | // for error handling. A refactor should remove these at some point.\r | |
23 | \r | |
24 | //\r | |
25 | // This library was designed with advanced unit-test features.\r | |
26 | // This define handles the configuration.\r | |
27 | #ifdef INTERNAL_UNIT_TEST\r | |
28 | #undef STATIC\r | |
29 | #define STATIC // Nothing...\r | |
30 | #endif\r | |
31 | \r | |
32 | // An abstracted GetVariable interface that enables configuration regardless of the environment.\r | |
33 | EFI_GET_VARIABLE mGetVariableHelper = NULL;\r | |
34 | \r | |
35 | // Master switch to lock this entire interface. Does not stop enforcement,\r | |
36 | // just prevents the configuration from being changed for the rest of the boot.\r | |
37 | STATIC BOOLEAN mInterfaceLocked = FALSE;\r | |
38 | \r | |
39 | // Master switch to disable the entire interface for a single boot.\r | |
40 | // This will disable all policy enforcement for the duration of the boot.\r | |
41 | STATIC BOOLEAN mProtectionDisabled = FALSE;\r | |
42 | \r | |
43 | // Table to hold all the current policies.\r | |
44 | UINT8 *mPolicyTable = NULL;\r | |
45 | STATIC UINT32 mCurrentTableSize = 0;\r | |
46 | STATIC UINT32 mCurrentTableUsage = 0;\r | |
47 | STATIC UINT32 mCurrentTableCount = 0;\r | |
48 | \r | |
49 | #define POLICY_TABLE_STEP_SIZE 0x1000\r | |
50 | \r | |
51 | // NOTE: DO NOT USE THESE MACROS on any structure that has not been validated.\r | |
52 | // Current table data has already been sanitized.\r | |
53 | #define GET_NEXT_POLICY(CurPolicy) (VARIABLE_POLICY_ENTRY*)((UINT8*)CurPolicy + CurPolicy->Size)\r | |
54 | #define GET_POLICY_NAME(CurPolicy) (CHAR16*)((UINTN)CurPolicy + CurPolicy->OffsetToName)\r | |
55 | \r | |
56 | #define MATCH_PRIORITY_EXACT 0\r | |
57 | #define MATCH_PRIORITY_MAX MATCH_PRIORITY_EXACT\r | |
58 | #define MATCH_PRIORITY_MIN MAX_UINT8\r | |
59 | \r | |
60 | \r | |
61 | /**\r | |
62 | An extra init hook that enables the RuntimeDxe library instance to\r | |
63 | register VirtualAddress change callbacks. Among other things.\r | |
64 | \r | |
65 | @retval EFI_SUCCESS Everything is good. Continue with init.\r | |
66 | @retval Others Uh... don't continue.\r | |
67 | \r | |
68 | **/\r | |
69 | EFI_STATUS\r | |
70 | VariablePolicyExtraInit (\r | |
71 | VOID\r | |
72 | );\r | |
73 | \r | |
74 | /**\r | |
75 | An extra deinit hook that enables the RuntimeDxe library instance to\r | |
76 | register VirtualAddress change callbacks. Among other things.\r | |
77 | \r | |
78 | @retval EFI_SUCCESS Everything is good. Continue with deinit.\r | |
79 | @retval Others Uh... don't continue.\r | |
80 | \r | |
81 | **/\r | |
82 | EFI_STATUS\r | |
83 | VariablePolicyExtraDeinit (\r | |
84 | VOID\r | |
85 | );\r | |
86 | \r | |
87 | \r | |
88 | /**\r | |
89 | This helper function determines whether the structure of an incoming policy\r | |
90 | is valid and internally consistent.\r | |
91 | \r | |
92 | @param[in] NewPolicy Pointer to the incoming policy structure.\r | |
93 | \r | |
94 | @retval TRUE\r | |
95 | @retval FALSE Pointer is NULL, size is wrong, strings are empty, or\r | |
96 | substructures overlap.\r | |
97 | \r | |
98 | **/\r | |
99 | STATIC\r | |
100 | BOOLEAN\r | |
101 | IsValidVariablePolicyStructure (\r | |
102 | IN CONST VARIABLE_POLICY_ENTRY *NewPolicy\r | |
103 | )\r | |
104 | {\r | |
105 | EFI_STATUS Status;\r | |
106 | UINTN EntryEnd;\r | |
107 | CHAR16 *CheckChar;\r | |
108 | UINTN WildcardCount;\r | |
109 | \r | |
110 | // Sanitize some quick values.\r | |
111 | if (NewPolicy == NULL || NewPolicy->Size == 0 ||\r | |
112 | // Structure size should be at least as long as the minumum structure and a NULL string.\r | |
113 | NewPolicy->Size < sizeof(VARIABLE_POLICY_ENTRY) ||\r | |
114 | // Check for the known revision.\r | |
115 | NewPolicy->Version != VARIABLE_POLICY_ENTRY_REVISION) {\r | |
116 | return FALSE;\r | |
117 | }\r | |
118 | \r | |
119 | // Calculate the theoretical end of the structure and make sure\r | |
120 | // that the structure can fit in memory.\r | |
121 | Status = SafeUintnAdd( (UINTN)NewPolicy, NewPolicy->Size, &EntryEnd );\r | |
122 | if (EFI_ERROR( Status )) {\r | |
123 | return FALSE;\r | |
124 | }\r | |
125 | \r | |
126 | // Check for a valid Max Size.\r | |
127 | if (NewPolicy->MaxSize == 0) {\r | |
128 | return FALSE;\r | |
129 | }\r | |
130 | \r | |
131 | // Check for the valid list of lock policies.\r | |
132 | if (NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_NO_LOCK &&\r | |
133 | NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_NOW &&\r | |
134 | NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_CREATE &&\r | |
135 | NewPolicy->LockPolicyType != VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE)\r | |
136 | {\r | |
137 | return FALSE;\r | |
138 | }\r | |
139 | \r | |
140 | // If the policy type is VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE, make sure that the matching state variable Name\r | |
141 | // terminates before the OffsetToName for the matching policy variable Name.\r | |
142 | if (NewPolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {\r | |
143 | // Adjust CheckChar to the offset of the LockPolicy->Name.\r | |
144 | Status = SafeUintnAdd( (UINTN)NewPolicy + sizeof(VARIABLE_POLICY_ENTRY),\r | |
145 | sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY),\r | |
146 | (UINTN*)&CheckChar );\r | |
147 | if (EFI_ERROR( Status ) || EntryEnd <= (UINTN)CheckChar) {\r | |
148 | return FALSE;\r | |
149 | }\r | |
150 | while (*CheckChar != CHAR_NULL) {\r | |
151 | if (EntryEnd <= (UINTN)CheckChar) {\r | |
152 | return FALSE;\r | |
153 | }\r | |
154 | CheckChar++;\r | |
155 | }\r | |
156 | // At this point we should have either exeeded the structure or be pointing at the last char in LockPolicy->Name.\r | |
157 | // We should check to make sure that the policy Name comes immediately after this charcter.\r | |
158 | if ((UINTN)++CheckChar != (UINTN)NewPolicy + NewPolicy->OffsetToName) {\r | |
159 | return FALSE;\r | |
160 | }\r | |
161 | // If the policy type is any other value, make sure that the LockPolicy structure has a zero length.\r | |
162 | } else {\r | |
163 | if (NewPolicy->OffsetToName != sizeof(VARIABLE_POLICY_ENTRY)) {\r | |
164 | return FALSE;\r | |
165 | }\r | |
166 | }\r | |
167 | \r | |
168 | // Check to make sure that the name has a terminating character\r | |
169 | // before the end of the structure.\r | |
170 | // We've already checked that the name is within the bounds of the structure.\r | |
171 | if (NewPolicy->Size != NewPolicy->OffsetToName) {\r | |
172 | CheckChar = (CHAR16*)((UINTN)NewPolicy + NewPolicy->OffsetToName);\r | |
173 | WildcardCount = 0;\r | |
174 | while (*CheckChar != CHAR_NULL) {\r | |
175 | // Make sure there aren't excessive wildcards.\r | |
176 | if (*CheckChar == '#') {\r | |
177 | WildcardCount++;\r | |
178 | if (WildcardCount > MATCH_PRIORITY_MIN) {\r | |
179 | return FALSE;\r | |
180 | }\r | |
181 | }\r | |
182 | // Make sure you're still within the bounds of the policy structure.\r | |
183 | if (EntryEnd <= (UINTN)CheckChar) {\r | |
184 | return FALSE;\r | |
185 | }\r | |
186 | CheckChar++;\r | |
187 | }\r | |
188 | \r | |
189 | // Finally, we should be pointed at the very last character in Name, so we should be right\r | |
190 | // up against the end of the structure.\r | |
191 | if ((UINTN)++CheckChar != EntryEnd) {\r | |
192 | return FALSE;\r | |
193 | }\r | |
194 | }\r | |
195 | \r | |
196 | return TRUE;\r | |
197 | }\r | |
198 | \r | |
199 | \r | |
200 | /**\r | |
201 | This helper function evaluates a policy and determines whether it matches the target\r | |
202 | variable. If matched, will also return a value corresponding to the priority of the match.\r | |
203 | \r | |
204 | The rules for "best match" are listed in the Variable Policy Spec.\r | |
205 | Perfect name matches will return 0.\r | |
206 | Single wildcard characters will return the number of wildcard characters.\r | |
207 | Full namespaces will return MAX_UINT8.\r | |
208 | \r | |
209 | @param[in] EvalEntry Pointer to the policy entry being evaluated.\r | |
210 | @param[in] VariableName Same as EFI_SET_VARIABLE.\r | |
211 | @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r | |
212 | @param[out] MatchPriority [Optional] On finding a match, this value contains the priority of the match.\r | |
213 | Lower number == higher priority. Only valid if a match found.\r | |
214 | \r | |
215 | @retval TRUE Current entry matches the target variable.\r | |
216 | @retval FALSE Current entry does not match at all.\r | |
217 | \r | |
218 | **/\r | |
219 | STATIC\r | |
220 | BOOLEAN\r | |
221 | EvaluatePolicyMatch (\r | |
222 | IN CONST VARIABLE_POLICY_ENTRY *EvalEntry,\r | |
223 | IN CONST CHAR16 *VariableName,\r | |
224 | IN CONST EFI_GUID *VendorGuid,\r | |
225 | OUT UINT8 *MatchPriority OPTIONAL\r | |
226 | )\r | |
227 | {\r | |
228 | BOOLEAN Result;\r | |
229 | CHAR16 *PolicyName;\r | |
230 | UINT8 CalculatedPriority;\r | |
231 | UINTN Index;\r | |
232 | \r | |
233 | Result = FALSE;\r | |
234 | CalculatedPriority = MATCH_PRIORITY_EXACT;\r | |
235 | \r | |
236 | // Step 1: If the GUID doesn't match, we're done. No need to evaluate anything else.\r | |
237 | if (!CompareGuid( &EvalEntry->Namespace, VendorGuid )) {\r | |
238 | goto Exit;\r | |
239 | }\r | |
240 | \r | |
241 | // If the GUID matches, check to see whether there is a Name associated\r | |
242 | // with the policy. If not, this policy matches the entire namespace.\r | |
243 | // Missing Name is indicated by size being equal to name.\r | |
244 | if (EvalEntry->Size == EvalEntry->OffsetToName) {\r | |
245 | CalculatedPriority = MATCH_PRIORITY_MIN;\r | |
246 | Result = TRUE;\r | |
247 | goto Exit;\r | |
248 | }\r | |
249 | \r | |
250 | // Now that we know the name exists, get it.\r | |
251 | PolicyName = GET_POLICY_NAME( EvalEntry );\r | |
252 | \r | |
253 | // Evaluate the name against the policy name and check for a match.\r | |
254 | // Account for any wildcards.\r | |
255 | Index = 0;\r | |
256 | Result = TRUE;\r | |
257 | // Keep going until the end of both strings.\r | |
258 | while (PolicyName[Index] != CHAR_NULL || VariableName[Index] != CHAR_NULL) {\r | |
259 | // If we don't have a match...\r | |
260 | if (PolicyName[Index] != VariableName[Index] || PolicyName[Index] == '#') {\r | |
261 | // If this is a numerical wildcard, we can consider\r | |
262 | // it a match if we alter the priority.\r | |
263 | if (PolicyName[Index] == L'#' &&\r | |
264 | ((L'0' <= VariableName[Index] && VariableName[Index] <= L'9') ||\r | |
265 | (L'A' <= VariableName[Index] && VariableName[Index] <= L'F') ||\r | |
266 | (L'a' <= VariableName[Index] && VariableName[Index] <= L'f'))) {\r | |
267 | if (CalculatedPriority < MATCH_PRIORITY_MIN) {\r | |
268 | CalculatedPriority++;\r | |
269 | }\r | |
270 | // Otherwise, not a match.\r | |
271 | } else {\r | |
272 | Result = FALSE;\r | |
273 | goto Exit;\r | |
274 | }\r | |
275 | }\r | |
276 | Index++;\r | |
277 | }\r | |
278 | \r | |
279 | Exit:\r | |
280 | if (Result && MatchPriority != NULL) {\r | |
281 | *MatchPriority = CalculatedPriority;\r | |
282 | }\r | |
283 | return Result;\r | |
284 | }\r | |
285 | \r | |
286 | \r | |
287 | /**\r | |
288 | This helper function walks the current policy table and returns a pointer\r | |
289 | to the best match, if any are found. Leverages EvaluatePolicyMatch() to\r | |
290 | determine "best".\r | |
291 | \r | |
292 | @param[in] VariableName Same as EFI_SET_VARIABLE.\r | |
293 | @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r | |
294 | @param[out] ReturnPriority [Optional] If pointer is provided, return the\r | |
295 | priority of the match. Same as EvaluatePolicyMatch().\r | |
296 | Only valid if a match is returned.\r | |
297 | \r | |
298 | @retval VARIABLE_POLICY_ENTRY* Best match that was found.\r | |
299 | @retval NULL No match was found.\r | |
300 | \r | |
301 | **/\r | |
302 | STATIC\r | |
303 | VARIABLE_POLICY_ENTRY*\r | |
304 | GetBestPolicyMatch (\r | |
305 | IN CONST CHAR16 *VariableName,\r | |
306 | IN CONST EFI_GUID *VendorGuid,\r | |
307 | OUT UINT8 *ReturnPriority OPTIONAL\r | |
308 | )\r | |
309 | {\r | |
310 | VARIABLE_POLICY_ENTRY *BestResult;\r | |
311 | VARIABLE_POLICY_ENTRY *CurrentEntry;\r | |
312 | UINT8 MatchPriority;\r | |
313 | UINT8 CurrentPriority;\r | |
314 | UINTN Index;\r | |
315 | \r | |
316 | BestResult = NULL;\r | |
317 | MatchPriority = MATCH_PRIORITY_EXACT;\r | |
318 | \r | |
319 | // Walk all entries in the table, looking for matches.\r | |
320 | CurrentEntry = (VARIABLE_POLICY_ENTRY*)mPolicyTable;\r | |
321 | for (Index = 0; Index < mCurrentTableCount; Index++) {\r | |
322 | // Check for a match.\r | |
323 | if (EvaluatePolicyMatch( CurrentEntry, VariableName, VendorGuid, &CurrentPriority )) {\r | |
324 | // If match is better, take it.\r | |
325 | if (BestResult == NULL || CurrentPriority < MatchPriority) {\r | |
326 | BestResult = CurrentEntry;\r | |
327 | MatchPriority = CurrentPriority;\r | |
328 | }\r | |
329 | \r | |
330 | // If you've hit the highest-priority match, can exit now.\r | |
331 | if (MatchPriority == 0) {\r | |
332 | break;\r | |
333 | }\r | |
334 | }\r | |
335 | \r | |
336 | // If we're still in the loop, move to the next entry.\r | |
337 | CurrentEntry = GET_NEXT_POLICY( CurrentEntry );\r | |
338 | }\r | |
339 | \r | |
340 | // If a return priority was requested, return it.\r | |
341 | if (ReturnPriority != NULL) {\r | |
342 | *ReturnPriority = MatchPriority;\r | |
343 | }\r | |
344 | \r | |
345 | return BestResult;\r | |
346 | }\r | |
347 | \r | |
348 | \r | |
349 | /**\r | |
350 | This API function validates and registers a new policy with\r | |
351 | the policy enforcement engine.\r | |
352 | \r | |
353 | @param[in] NewPolicy Pointer to the incoming policy structure.\r | |
354 | \r | |
355 | @retval EFI_SUCCESS\r | |
356 | @retval EFI_INVALID_PARAMETER NewPolicy is NULL or is internally inconsistent.\r | |
357 | @retval EFI_ALREADY_STARTED An identical matching policy already exists.\r | |
358 | @retval EFI_WRITE_PROTECTED The interface has been locked until the next reboot.\r | |
359 | @retval EFI_UNSUPPORTED Policy enforcement has been disabled. No reason to add more policies.\r | |
360 | @retval EFI_ABORTED A calculation error has prevented this function from completing.\r | |
361 | @retval EFI_OUT_OF_RESOURCES Cannot grow the table to hold any more policies.\r | |
362 | @retval EFI_NOT_READY Library has not yet been initialized.\r | |
363 | \r | |
364 | **/\r | |
365 | EFI_STATUS\r | |
366 | EFIAPI\r | |
367 | RegisterVariablePolicy (\r | |
368 | IN CONST VARIABLE_POLICY_ENTRY *NewPolicy\r | |
369 | )\r | |
370 | {\r | |
371 | EFI_STATUS Status;\r | |
372 | VARIABLE_POLICY_ENTRY *MatchPolicy;\r | |
373 | UINT8 MatchPriority;\r | |
374 | UINT32 NewSize;\r | |
375 | UINT8 *NewTable;\r | |
376 | \r | |
377 | if (!IsVariablePolicyLibInitialized()) {\r | |
378 | return EFI_NOT_READY;\r | |
379 | }\r | |
380 | if (mInterfaceLocked) {\r | |
381 | return EFI_WRITE_PROTECTED;\r | |
382 | }\r | |
383 | \r | |
384 | if (!IsValidVariablePolicyStructure( NewPolicy )) {\r | |
385 | return EFI_INVALID_PARAMETER;\r | |
386 | }\r | |
387 | \r | |
388 | // Check to see whether an exact matching policy already exists.\r | |
389 | MatchPolicy = GetBestPolicyMatch( GET_POLICY_NAME( NewPolicy ),\r | |
390 | &NewPolicy->Namespace,\r | |
391 | &MatchPriority );\r | |
392 | if (MatchPolicy != NULL && MatchPriority == MATCH_PRIORITY_EXACT) {\r | |
393 | return EFI_ALREADY_STARTED;\r | |
394 | }\r | |
395 | \r | |
396 | // If none exists, create it.\r | |
397 | // If we need more space, allocate that now.\r | |
398 | Status = SafeUint32Add( mCurrentTableUsage, NewPolicy->Size, &NewSize );\r | |
399 | if (EFI_ERROR( Status )) {\r | |
400 | return EFI_ABORTED;\r | |
401 | }\r | |
402 | if (NewSize > mCurrentTableSize) {\r | |
403 | // Use NewSize to calculate the new table size in units of POLICY_TABLE_STEP_SIZE.\r | |
404 | NewSize = (NewSize % POLICY_TABLE_STEP_SIZE) > 0 ?\r | |
405 | (NewSize / POLICY_TABLE_STEP_SIZE) + 1 :\r | |
406 | (NewSize / POLICY_TABLE_STEP_SIZE);\r | |
407 | // Calculate the new table size in absolute bytes.\r | |
408 | Status = SafeUint32Mult( NewSize, POLICY_TABLE_STEP_SIZE, &NewSize );\r | |
409 | if (EFI_ERROR( Status )) {\r | |
410 | return EFI_ABORTED;\r | |
411 | }\r | |
412 | \r | |
413 | // Reallocate and copy the table.\r | |
872f9532 | 414 | NewTable = AllocateRuntimePool( NewSize );\r |
355b181f BB |
415 | if (NewTable == NULL) {\r |
416 | return EFI_OUT_OF_RESOURCES;\r | |
417 | }\r | |
418 | CopyMem( NewTable, mPolicyTable, mCurrentTableUsage );\r | |
419 | mCurrentTableSize = NewSize;\r | |
420 | if (mPolicyTable != NULL) {\r | |
421 | FreePool( mPolicyTable );\r | |
422 | }\r | |
423 | mPolicyTable = NewTable;\r | |
424 | }\r | |
425 | // Copy the policy into the table.\r | |
426 | CopyMem( mPolicyTable + mCurrentTableUsage, NewPolicy, NewPolicy->Size );\r | |
427 | mCurrentTableUsage += NewPolicy->Size;\r | |
428 | mCurrentTableCount += 1;\r | |
429 | \r | |
430 | // We're done here.\r | |
431 | \r | |
432 | return EFI_SUCCESS;\r | |
433 | }\r | |
434 | \r | |
435 | \r | |
436 | /**\r | |
437 | This API function checks to see whether the parameters to SetVariable would\r | |
438 | be allowed according to the current variable policies.\r | |
439 | \r | |
440 | @param[in] VariableName Same as EFI_SET_VARIABLE.\r | |
441 | @param[in] VendorGuid Same as EFI_SET_VARIABLE.\r | |
442 | @param[in] Attributes Same as EFI_SET_VARIABLE.\r | |
443 | @param[in] DataSize Same as EFI_SET_VARIABLE.\r | |
444 | @param[in] Data Same as EFI_SET_VARIABLE.\r | |
445 | \r | |
446 | @retval EFI_SUCCESS A matching policy allows this update.\r | |
447 | @retval EFI_SUCCESS There are currently no policies that restrict this update.\r | |
448 | @retval EFI_SUCCESS The protections have been disable until the next reboot.\r | |
449 | @retval EFI_WRITE_PROTECTED Variable is currently locked.\r | |
450 | @retval EFI_INVALID_PARAMETER Attributes or size are invalid.\r | |
451 | @retval EFI_ABORTED A lock policy exists, but an error prevented evaluation.\r | |
452 | @retval EFI_NOT_READY Library has not been initialized.\r | |
453 | \r | |
454 | **/\r | |
455 | EFI_STATUS\r | |
456 | EFIAPI\r | |
457 | ValidateSetVariable (\r | |
458 | IN CHAR16 *VariableName,\r | |
459 | IN EFI_GUID *VendorGuid,\r | |
460 | IN UINT32 Attributes,\r | |
461 | IN UINTN DataSize,\r | |
462 | IN VOID *Data\r | |
463 | )\r | |
464 | {\r | |
465 | BOOLEAN IsDel;\r | |
466 | VARIABLE_POLICY_ENTRY *ActivePolicy;\r | |
467 | EFI_STATUS Status;\r | |
468 | EFI_STATUS ReturnStatus;\r | |
469 | VARIABLE_LOCK_ON_VAR_STATE_POLICY *StateVarPolicy;\r | |
470 | CHAR16 *StateVarName;\r | |
471 | UINTN StateVarSize;\r | |
472 | UINT8 StateVar;\r | |
473 | \r | |
474 | ReturnStatus = EFI_SUCCESS;\r | |
475 | \r | |
476 | if (!IsVariablePolicyLibInitialized()) {\r | |
477 | ReturnStatus = EFI_NOT_READY;\r | |
478 | goto Exit;\r | |
479 | }\r | |
480 | \r | |
481 | // Bail if the protections are currently disabled.\r | |
482 | if (mProtectionDisabled) {\r | |
483 | ReturnStatus = EFI_SUCCESS;\r | |
484 | goto Exit;\r | |
485 | }\r | |
486 | \r | |
487 | // Determine whether this is a delete operation.\r | |
488 | // If so, it will affect which tests are applied.\r | |
489 | if ((DataSize == 0) && ((Attributes & EFI_VARIABLE_APPEND_WRITE) == 0)) {\r | |
490 | IsDel = TRUE;\r | |
491 | } else {\r | |
492 | IsDel = FALSE;\r | |
493 | }\r | |
494 | \r | |
495 | // Find an active policy if one exists.\r | |
496 | ActivePolicy = GetBestPolicyMatch( VariableName, VendorGuid, NULL );\r | |
497 | \r | |
498 | // If we have an active policy, check it against the incoming data.\r | |
499 | if (ActivePolicy != NULL) {\r | |
500 | //\r | |
501 | // Only enforce size and attribute constraints when updating data, not deleting.\r | |
502 | if (!IsDel) {\r | |
503 | // Check for size constraints.\r | |
504 | if ((ActivePolicy->MinSize > 0 && DataSize < ActivePolicy->MinSize) ||\r | |
505 | (ActivePolicy->MaxSize > 0 && DataSize > ActivePolicy->MaxSize)) {\r | |
506 | ReturnStatus = EFI_INVALID_PARAMETER;\r | |
507 | DEBUG(( DEBUG_VERBOSE, "%a - Bad Size. 0x%X <> 0x%X-0x%X\n", __FUNCTION__,\r | |
508 | DataSize, ActivePolicy->MinSize, ActivePolicy->MaxSize ));\r | |
509 | goto Exit;\r | |
510 | }\r | |
511 | \r | |
512 | // Check for attribute constraints.\r | |
513 | if ((ActivePolicy->AttributesMustHave & Attributes) != ActivePolicy->AttributesMustHave ||\r | |
514 | (ActivePolicy->AttributesCantHave & Attributes) != 0) {\r | |
515 | ReturnStatus = EFI_INVALID_PARAMETER;\r | |
516 | DEBUG(( DEBUG_VERBOSE, "%a - Bad Attributes. 0x%X <> 0x%X:0x%X\n", __FUNCTION__,\r | |
517 | Attributes, ActivePolicy->AttributesMustHave, ActivePolicy->AttributesCantHave ));\r | |
518 | goto Exit;\r | |
519 | }\r | |
520 | }\r | |
521 | \r | |
522 | //\r | |
523 | // Lock policy check.\r | |
524 | //\r | |
525 | // Check for immediate lock.\r | |
526 | if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_NOW) {\r | |
527 | ReturnStatus = EFI_WRITE_PROTECTED;\r | |
528 | goto Exit;\r | |
529 | // Check for lock on create.\r | |
530 | } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_CREATE) {\r | |
531 | StateVarSize = 0;\r | |
532 | Status = mGetVariableHelper( VariableName,\r | |
533 | VendorGuid,\r | |
534 | NULL,\r | |
535 | &StateVarSize,\r | |
536 | NULL );\r | |
537 | if (Status == EFI_BUFFER_TOO_SMALL) {\r | |
538 | ReturnStatus = EFI_WRITE_PROTECTED;\r | |
539 | goto Exit;\r | |
540 | }\r | |
541 | // Check for lock on state variable.\r | |
542 | } else if (ActivePolicy->LockPolicyType == VARIABLE_POLICY_TYPE_LOCK_ON_VAR_STATE) {\r | |
543 | StateVarPolicy = (VARIABLE_LOCK_ON_VAR_STATE_POLICY*)((UINT8*)ActivePolicy + sizeof(VARIABLE_POLICY_ENTRY));\r | |
544 | StateVarName = (CHAR16*)((UINT8*)StateVarPolicy + sizeof(VARIABLE_LOCK_ON_VAR_STATE_POLICY));\r | |
545 | StateVarSize = sizeof(StateVar);\r | |
546 | Status = mGetVariableHelper( StateVarName,\r | |
547 | &StateVarPolicy->Namespace,\r | |
548 | NULL,\r | |
549 | &StateVarSize,\r | |
550 | &StateVar );\r | |
551 | \r | |
552 | // If the variable was found, check the state. If matched, this variable is locked.\r | |
553 | if (!EFI_ERROR( Status )) {\r | |
554 | if (StateVar == StateVarPolicy->Value) {\r | |
555 | ReturnStatus = EFI_WRITE_PROTECTED;\r | |
556 | goto Exit;\r | |
557 | }\r | |
558 | // EFI_NOT_FOUND and EFI_BUFFER_TOO_SMALL indicate that the state doesn't match.\r | |
559 | } else if (Status != EFI_NOT_FOUND && Status != EFI_BUFFER_TOO_SMALL) {\r | |
560 | // We don't know what happened, but it isn't good.\r | |
561 | ReturnStatus = EFI_ABORTED;\r | |
562 | goto Exit;\r | |
563 | }\r | |
564 | }\r | |
565 | }\r | |
566 | \r | |
567 | Exit:\r | |
568 | DEBUG(( DEBUG_VERBOSE, "%a - Variable (%g:%s) returning %r.\n", __FUNCTION__, VendorGuid, VariableName, ReturnStatus ));\r | |
569 | return ReturnStatus;\r | |
570 | }\r | |
571 | \r | |
572 | \r | |
573 | /**\r | |
574 | This API function disables the variable policy enforcement. If it's\r | |
575 | already been called once, will return EFI_ALREADY_STARTED.\r | |
576 | \r | |
577 | @retval EFI_SUCCESS\r | |
578 | @retval EFI_ALREADY_STARTED Has already been called once this boot.\r | |
579 | @retval EFI_WRITE_PROTECTED Interface has been locked until reboot.\r | |
580 | @retval EFI_WRITE_PROTECTED Interface option is disabled by platform PCD.\r | |
581 | @retval EFI_NOT_READY Library has not yet been initialized.\r | |
582 | \r | |
583 | **/\r | |
584 | EFI_STATUS\r | |
585 | EFIAPI\r | |
586 | DisableVariablePolicy (\r | |
587 | VOID\r | |
588 | )\r | |
589 | {\r | |
590 | if (!IsVariablePolicyLibInitialized()) {\r | |
591 | return EFI_NOT_READY;\r | |
592 | }\r | |
593 | if (mProtectionDisabled) {\r | |
594 | return EFI_ALREADY_STARTED;\r | |
595 | }\r | |
596 | if (mInterfaceLocked) {\r | |
597 | return EFI_WRITE_PROTECTED;\r | |
598 | }\r | |
599 | if (!PcdGetBool (PcdAllowVariablePolicyEnforcementDisable)) {\r | |
600 | return EFI_WRITE_PROTECTED;\r | |
601 | }\r | |
602 | mProtectionDisabled = TRUE;\r | |
603 | return EFI_SUCCESS;\r | |
604 | }\r | |
605 | \r | |
606 | \r | |
607 | /**\r | |
608 | This API function will dump the entire contents of the variable policy table.\r | |
609 | \r | |
610 | Similar to GetVariable, the first call can be made with a 0 size and it will return\r | |
611 | the size of the buffer required to hold the entire table.\r | |
612 | \r | |
613 | @param[out] Policy Pointer to the policy buffer. Can be NULL if Size is 0.\r | |
614 | @param[in,out] Size On input, the size of the output buffer. On output, the size\r | |
615 | of the data returned.\r | |
616 | \r | |
617 | @retval EFI_SUCCESS Policy data is in the output buffer and Size has been updated.\r | |
618 | @retval EFI_INVALID_PARAMETER Size is NULL, or Size is non-zero and Policy is NULL.\r | |
619 | @retval EFI_BUFFER_TOO_SMALL Size is insufficient to hold policy. Size updated with required size.\r | |
620 | @retval EFI_NOT_READY Library has not yet been initialized.\r | |
621 | \r | |
622 | **/\r | |
623 | EFI_STATUS\r | |
624 | EFIAPI\r | |
625 | DumpVariablePolicy (\r | |
626 | OUT UINT8 *Policy,\r | |
627 | IN OUT UINT32 *Size\r | |
628 | )\r | |
629 | {\r | |
630 | if (!IsVariablePolicyLibInitialized()) {\r | |
631 | return EFI_NOT_READY;\r | |
632 | }\r | |
633 | \r | |
634 | // Check the parameters.\r | |
635 | if (Size == NULL || (*Size > 0 && Policy == NULL)) {\r | |
636 | return EFI_INVALID_PARAMETER;\r | |
637 | }\r | |
638 | \r | |
639 | // Make sure the size is sufficient to hold the policy table.\r | |
640 | if (*Size < mCurrentTableUsage) {\r | |
641 | *Size = mCurrentTableUsage;\r | |
642 | return EFI_BUFFER_TOO_SMALL;\r | |
643 | }\r | |
644 | \r | |
645 | // If we're still here, copy the table and bounce.\r | |
646 | CopyMem( Policy, mPolicyTable, mCurrentTableUsage );\r | |
647 | *Size = mCurrentTableUsage;\r | |
648 | \r | |
649 | return EFI_SUCCESS;\r | |
650 | }\r | |
651 | \r | |
652 | \r | |
653 | /**\r | |
654 | This API function returns whether or not the policy engine is\r | |
655 | currently being enforced.\r | |
656 | \r | |
657 | @retval TRUE\r | |
658 | @retval FALSE\r | |
659 | @retval FALSE Library has not yet been initialized.\r | |
660 | \r | |
661 | **/\r | |
662 | BOOLEAN\r | |
663 | EFIAPI\r | |
664 | IsVariablePolicyEnabled (\r | |
665 | VOID\r | |
666 | )\r | |
667 | {\r | |
668 | if (!IsVariablePolicyLibInitialized()) {\r | |
669 | return FALSE;\r | |
670 | }\r | |
671 | return !mProtectionDisabled;\r | |
672 | }\r | |
673 | \r | |
674 | \r | |
675 | /**\r | |
676 | This API function locks the interface so that no more policy updates\r | |
677 | can be performed or changes made to the enforcement until the next boot.\r | |
678 | \r | |
679 | @retval EFI_SUCCESS\r | |
680 | @retval EFI_NOT_READY Library has not yet been initialized.\r | |
681 | \r | |
682 | **/\r | |
683 | EFI_STATUS\r | |
684 | EFIAPI\r | |
685 | LockVariablePolicy (\r | |
686 | VOID\r | |
687 | )\r | |
688 | {\r | |
689 | if (!IsVariablePolicyLibInitialized()) {\r | |
690 | return EFI_NOT_READY;\r | |
691 | }\r | |
692 | if (mInterfaceLocked) {\r | |
693 | return EFI_WRITE_PROTECTED;\r | |
694 | }\r | |
695 | mInterfaceLocked = TRUE;\r | |
696 | return EFI_SUCCESS;\r | |
697 | }\r | |
698 | \r | |
699 | \r | |
700 | /**\r | |
701 | This API function returns whether or not the policy interface is locked\r | |
702 | for the remainder of the boot.\r | |
703 | \r | |
704 | @retval TRUE\r | |
705 | @retval FALSE\r | |
706 | @retval FALSE Library has not yet been initialized.\r | |
707 | \r | |
708 | **/\r | |
709 | BOOLEAN\r | |
710 | EFIAPI\r | |
711 | IsVariablePolicyInterfaceLocked (\r | |
712 | VOID\r | |
713 | )\r | |
714 | {\r | |
715 | if (!IsVariablePolicyLibInitialized()) {\r | |
716 | return FALSE;\r | |
717 | }\r | |
718 | return mInterfaceLocked;\r | |
719 | }\r | |
720 | \r | |
721 | \r | |
722 | /**\r | |
723 | This helper function initializes the library and sets\r | |
724 | up any required internal structures or handlers.\r | |
725 | \r | |
726 | Also registers the internal pointer for the GetVariable helper.\r | |
727 | \r | |
728 | @param[in] GetVariableHelper A function pointer matching the EFI_GET_VARIABLE prototype that will be used to\r | |
729 | check policy criteria that involve the existence of other variables.\r | |
730 | \r | |
731 | @retval EFI_SUCCESS\r | |
732 | @retval EFI_ALREADY_STARTED The initialize function has been called more than once without a call to\r | |
733 | deinitialize.\r | |
734 | \r | |
735 | **/\r | |
736 | EFI_STATUS\r | |
737 | EFIAPI\r | |
738 | InitVariablePolicyLib (\r | |
739 | IN EFI_GET_VARIABLE GetVariableHelper\r | |
740 | )\r | |
741 | {\r | |
742 | EFI_STATUS Status;\r | |
743 | \r | |
744 | Status = EFI_SUCCESS;\r | |
745 | \r | |
746 | if (mGetVariableHelper != NULL) {\r | |
747 | return EFI_ALREADY_STARTED;\r | |
748 | }\r | |
749 | \r | |
750 | if (!EFI_ERROR( Status )) {\r | |
751 | Status = VariablePolicyExtraInit();\r | |
752 | }\r | |
753 | \r | |
754 | if (!EFI_ERROR( Status )) {\r | |
755 | // Save an internal pointer to the GetVariableHelper.\r | |
756 | mGetVariableHelper = GetVariableHelper;\r | |
757 | \r | |
758 | // Initialize the global state.\r | |
759 | mInterfaceLocked = FALSE;\r | |
760 | mProtectionDisabled = FALSE;\r | |
761 | mPolicyTable = NULL;\r | |
762 | mCurrentTableSize = 0;\r | |
763 | mCurrentTableUsage = 0;\r | |
764 | mCurrentTableCount = 0;\r | |
765 | }\r | |
766 | \r | |
767 | return Status;\r | |
768 | }\r | |
769 | \r | |
770 | \r | |
771 | /**\r | |
772 | This helper function returns whether or not the library is currently initialized.\r | |
773 | \r | |
774 | @retval TRUE\r | |
775 | @retval FALSE\r | |
776 | \r | |
777 | **/\r | |
778 | BOOLEAN\r | |
779 | EFIAPI\r | |
780 | IsVariablePolicyLibInitialized (\r | |
781 | VOID\r | |
782 | )\r | |
783 | {\r | |
784 | return (mGetVariableHelper != NULL);\r | |
785 | }\r | |
786 | \r | |
787 | \r | |
788 | /**\r | |
789 | This helper function tears down the library.\r | |
790 | \r | |
791 | Should generally only be used for test harnesses.\r | |
792 | \r | |
793 | @retval EFI_SUCCESS\r | |
794 | @retval EFI_NOT_READY Deinitialize was called without first calling initialize.\r | |
795 | \r | |
796 | **/\r | |
797 | EFI_STATUS\r | |
798 | EFIAPI\r | |
799 | DeinitVariablePolicyLib (\r | |
800 | VOID\r | |
801 | )\r | |
802 | {\r | |
803 | EFI_STATUS Status;\r | |
804 | \r | |
805 | Status = EFI_SUCCESS;\r | |
806 | \r | |
807 | if (mGetVariableHelper == NULL) {\r | |
808 | return EFI_NOT_READY;\r | |
809 | }\r | |
810 | \r | |
811 | if (!EFI_ERROR( Status )) {\r | |
812 | Status = VariablePolicyExtraDeinit();\r | |
813 | }\r | |
814 | \r | |
815 | if (!EFI_ERROR( Status )) {\r | |
816 | mGetVariableHelper = NULL;\r | |
817 | mInterfaceLocked = FALSE;\r | |
818 | mProtectionDisabled = FALSE;\r | |
819 | mCurrentTableSize = 0;\r | |
820 | mCurrentTableUsage = 0;\r | |
821 | mCurrentTableCount = 0;\r | |
822 | \r | |
823 | if (mPolicyTable != NULL) {\r | |
824 | FreePool( mPolicyTable );\r | |
825 | mPolicyTable = NULL;\r | |
826 | }\r | |
827 | }\r | |
828 | \r | |
829 | return Status;\r | |
830 | }\r |