66//
77#if ! UNIX
88
9+ using System . Diagnostics ;
910using System . Diagnostics . CodeAnalysis ;
1011using System . Management . Automation . Internal ;
1112using System . Management . Automation . Runspaces ;
@@ -148,7 +149,7 @@ public static SystemEnforcementMode GetSystemLockdownPolicy()
148149 {
149150 lock ( s_systemLockdownPolicyLock )
150151 {
151- s_systemLockdownPolicy = GetDebugLockdownPolicy ( path : null ) ;
152+ s_systemLockdownPolicy = GetDebugLockdownPolicy ( path : null , out _ ) ;
152153 }
153154 }
154155
@@ -172,93 +173,89 @@ public static SystemScriptFileEnforcement GetFilePolicyEnforcement(
172173 System . IO . FileStream fileStream )
173174 {
174175 SafeHandle fileHandle = fileStream . SafeFileHandle ;
175- var systemLockdownPolicy = SystemPolicy . GetSystemLockdownPolicy ( ) ;
176+ SystemEnforcementMode systemLockdownPolicy = GetSystemLockdownPolicy ( ) ;
176177
177178 // First check latest WDAC APIs if available.
178- // Revert to legacy APIs if system policy is in AUDIT mode or debug hook is in effect.
179- Exception errorException = null ;
180- if ( s_wldpCanExecuteAvailable && systemLockdownPolicy == SystemEnforcementMode . Enforce )
179+ if ( systemLockdownPolicy is SystemEnforcementMode . Enforce
180+ && s_wldpCanExecuteAvailable
181+ && TryGetWldpCanExecuteFileResult ( filePath , fileHandle , out SystemScriptFileEnforcement wldpFilePolicy ) )
181182 {
182- try
183- {
184- string fileName = System . IO . Path . GetFileNameWithoutExtension ( filePath ) ;
185- string auditMsg = $ "PowerShell ExternalScriptInfo reading file: { fileName } ";
183+ return GetLockdownPolicy ( filePath , fileHandle , wldpFilePolicy ) ;
184+ }
186185
187- int hr = WldpNativeMethods . WldpCanExecuteFile (
188- host : PowerShellHost ,
189- options : WLDP_EXECUTION_EVALUATION_OPTIONS . WLDP_EXECUTION_EVALUATION_OPTION_NONE ,
190- fileHandle : fileHandle . DangerousGetHandle ( ) ,
191- auditInfo : auditMsg ,
192- result : out WLDP_EXECUTION_POLICY canExecuteResult ) ;
186+ // Failed to invoke WldpCanExecuteFile, revert to legacy APIs.
187+ if ( systemLockdownPolicy is SystemEnforcementMode . None )
188+ {
189+ return SystemScriptFileEnforcement . None ;
190+ }
193191
194- PSEtwLog . LogWDACQueryEvent ( "WldpCanExecuteFile" , filePath , hr , ( int ) canExecuteResult ) ;
192+ // WldpCanExecuteFile was invoked successfully so we can skip running
193+ // legacy WDAC APIs. AppLocker must still be checked in case it is more
194+ // strict than the current WDAC policy.
195+ return GetLockdownPolicy ( filePath , fileHandle , canExecuteResult : null ) ;
196+ }
195197
196- if ( hr >= 0 )
197- {
198- switch ( canExecuteResult )
199- {
200- case WLDP_EXECUTION_POLICY . WLDP_CAN_EXECUTE_ALLOWED :
201- return SystemScriptFileEnforcement . Allow ;
198+ private static SystemScriptFileEnforcement ConvertToModernFileEnforcement ( SystemEnforcementMode legacyMode )
199+ {
200+ return legacyMode switch
201+ {
202+ SystemEnforcementMode . None => SystemScriptFileEnforcement . Allow ,
203+ SystemEnforcementMode . Audit => SystemScriptFileEnforcement . AllowConstrainedAudit ,
204+ SystemEnforcementMode . Enforce => SystemScriptFileEnforcement . AllowConstrained ,
205+ _ => SystemScriptFileEnforcement . Block ,
206+ } ;
207+ }
202208
203- case WLDP_EXECUTION_POLICY . WLDP_CAN_EXECUTE_BLOCKED :
204- return SystemScriptFileEnforcement . Block ;
209+ private static bool TryGetWldpCanExecuteFileResult ( string filePath , SafeHandle fileHandle , out SystemScriptFileEnforcement result )
210+ {
211+ try
212+ {
213+ string fileName = System . IO . Path . GetFileNameWithoutExtension ( filePath ) ;
214+ string auditMsg = $ "PowerShell ExternalScriptInfo reading file: { fileName } ";
205215
206- case WLDP_EXECUTION_POLICY . WLDP_CAN_EXECUTE_REQUIRE_SANDBOX :
207- return SystemScriptFileEnforcement . AllowConstrained ;
216+ int hr = WldpNativeMethods . WldpCanExecuteFile (
217+ host : PowerShellHost ,
218+ options : WLDP_EXECUTION_EVALUATION_OPTIONS . WLDP_EXECUTION_EVALUATION_OPTION_NONE ,
219+ fileHandle : fileHandle . DangerousGetHandle ( ) ,
220+ auditInfo : auditMsg ,
221+ result : out WLDP_EXECUTION_POLICY canExecuteResult ) ;
208222
209- default :
210- // Fall through to legacy system policy checks.
211- System . Diagnostics . Debug . Assert ( false , $ "Unknown execution policy returned from WldCanExecute: { canExecuteResult } ") ;
212- break ;
213- }
214- }
223+ PSEtwLog . LogWDACQueryEvent ( "WldpCanExecuteFile" , filePath , hr , ( int ) canExecuteResult ) ;
215224
216- // If HResult is unsuccessful (such as E_NOTIMPL (0x80004001)), fall through to legacy system checks.
217- }
218- catch ( DllNotFoundException ex )
219- {
220- // Fall back to legacy system policy checks.
221- s_wldpCanExecuteAvailable = false ;
222- errorException = ex ;
223- }
224- catch ( EntryPointNotFoundException ex )
225+ if ( hr >= 0 )
225226 {
226- // Fall back to legacy system policy checks.
227- s_wldpCanExecuteAvailable = false ;
228- errorException = ex ;
227+ switch ( canExecuteResult )
228+ {
229+ case WLDP_EXECUTION_POLICY . WLDP_CAN_EXECUTE_ALLOWED :
230+ result = SystemScriptFileEnforcement . Allow ;
231+ return true ;
232+
233+ case WLDP_EXECUTION_POLICY . WLDP_CAN_EXECUTE_BLOCKED :
234+ result = SystemScriptFileEnforcement . Block ;
235+ return true ;
236+
237+ case WLDP_EXECUTION_POLICY . WLDP_CAN_EXECUTE_REQUIRE_SANDBOX :
238+ result = SystemScriptFileEnforcement . AllowConstrained ;
239+ return true ;
240+
241+ default :
242+ // Fall through to legacy system policy checks.
243+ Debug . Assert ( false , $ "Unknown policy result returned from WldCanExecute: { canExecuteResult } ") ;
244+ break ;
245+ }
229246 }
230247
231- if ( errorException != null )
232- {
233- PSEtwLog . LogWDACQueryEvent ( "WldpCanExecuteFile_Failed" , filePath , errorException . HResult , 0 ) ;
234- }
248+ // If HResult is unsuccessful (such as E_NOTIMPL (0x80004001)), fall through to legacy system checks.
235249 }
236-
237- // Original (legacy) WDAC and AppLocker system checks.
238- if ( systemLockdownPolicy == SystemEnforcementMode . None )
250+ catch ( Exception ex ) when ( ex is DllNotFoundException or EntryPointNotFoundException )
239251 {
240- return SystemScriptFileEnforcement . None ;
252+ // Fall back to legacy system policy checks.
253+ s_wldpCanExecuteAvailable = false ;
254+ PSEtwLog . LogWDACQueryEvent ( "WldpCanExecuteFile_Failed" , filePath , ex . HResult , 0 ) ;
241255 }
242256
243- // Check policy for file.
244- switch ( SystemPolicy . GetLockdownPolicy ( filePath , fileHandle ) )
245- {
246- case SystemEnforcementMode . Enforce :
247- // File is not allowed by policy enforcement and must run in CL mode.
248- return SystemScriptFileEnforcement . AllowConstrained ;
249-
250- case SystemEnforcementMode . Audit :
251- // File is allowed but would be run in CL mode if policy was enforced and not audit.
252- return SystemScriptFileEnforcement . AllowConstrainedAudit ;
253-
254- case SystemEnforcementMode . None :
255- // No restrictions, file will run in FL mode.
256- return SystemScriptFileEnforcement . Allow ;
257-
258- default :
259- System . Diagnostics . Debug . Assert ( false , "GetFilePolicyEnforcement: Unknown SystemEnforcementMode." ) ;
260- return SystemScriptFileEnforcement . Block ;
261- }
257+ result = default ;
258+ return false ;
262259 }
263260
264261 /// <summary>
@@ -267,9 +264,32 @@ public static SystemScriptFileEnforcement GetFilePolicyEnforcement(
267264 /// <returns>An EnforcementMode that describes policy.</returns>
268265 public static SystemEnforcementMode GetLockdownPolicy ( string path , SafeHandle handle )
269266 {
267+ SystemScriptFileEnforcement modernMode = GetLockdownPolicy ( path , handle , canExecuteResult : null ) ;
268+ Debug . Assert (
269+ modernMode is not SystemScriptFileEnforcement . Block ,
270+ "Block should never be converted to legacy file enforcement." ) ;
271+
272+ return modernMode switch
273+ {
274+ SystemScriptFileEnforcement . Block => SystemEnforcementMode . Enforce ,
275+ SystemScriptFileEnforcement . AllowConstrained => SystemEnforcementMode . Enforce ,
276+ SystemScriptFileEnforcement . AllowConstrainedAudit => SystemEnforcementMode . Audit ,
277+ SystemScriptFileEnforcement . Allow => SystemEnforcementMode . None ,
278+ SystemScriptFileEnforcement . None => SystemEnforcementMode . None ,
279+ _ => throw new ArgumentOutOfRangeException ( nameof ( modernMode ) ) ,
280+ } ;
281+ }
282+
283+ private static SystemScriptFileEnforcement GetLockdownPolicy (
284+ string path ,
285+ SafeHandle handle ,
286+ SystemScriptFileEnforcement ? canExecuteResult )
287+ {
288+ SystemScriptFileEnforcement wldpFilePolicy = canExecuteResult
289+ ?? ConvertToModernFileEnforcement ( GetWldpPolicy ( path , handle ) ) ;
290+
270291 // Check the WLDP File policy via API
271- var wldpFilePolicy = GetWldpPolicy ( path , handle ) ;
272- if ( wldpFilePolicy == SystemEnforcementMode . Enforce )
292+ if ( wldpFilePolicy is SystemScriptFileEnforcement . Block or SystemScriptFileEnforcement . AllowConstrained )
273293 {
274294 return wldpFilePolicy ;
275295 }
@@ -281,29 +301,28 @@ public static SystemEnforcementMode GetLockdownPolicy(string path, SafeHandle ha
281301 var appLockerFilePolicy = GetAppLockerPolicy ( path , handle ) ;
282302 if ( appLockerFilePolicy == SystemEnforcementMode . Enforce )
283303 {
284- return appLockerFilePolicy ;
304+ return ConvertToModernFileEnforcement ( appLockerFilePolicy ) ;
285305 }
286306
287307 // At this point, LockdownPolicy = Audit or Allowed.
288308 // If there was a WLDP policy, but WLDP didn't block it,
289309 // then it was explicitly allowed. Therefore, return the result for the file.
290- SystemEnforcementMode systemWldpPolicy = s_cachedWldpSystemPolicy . GetValueOrDefault ( SystemEnforcementMode . None ) ;
291- if ( ( systemWldpPolicy == SystemEnforcementMode . Audit ) ||
292- ( systemWldpPolicy == SystemEnforcementMode . Enforce ) )
310+ if ( s_cachedWldpSystemPolicy is SystemEnforcementMode . Audit or SystemEnforcementMode . Enforce
311+ || wldpFilePolicy is SystemScriptFileEnforcement . AllowConstrainedAudit )
293312 {
294313 return wldpFilePolicy ;
295314 }
296315
297316 // If there was a system-wide AppLocker policy, but AppLocker didn't block it,
298317 // then return AppLocker's status.
299- if ( s_cachedSaferSystemPolicy . GetValueOrDefault ( SaferPolicy . Allowed ) ==
300- SaferPolicy . Disallowed )
318+ if ( s_cachedSaferSystemPolicy is SaferPolicy . Disallowed )
301319 {
302- return appLockerFilePolicy ;
320+ return ConvertToModernFileEnforcement ( appLockerFilePolicy ) ;
303321 }
304322
305323 // If it's not set to 'Enforce' by the platform, allow debug overrides
306- return GetDebugLockdownPolicy ( path ) ;
324+ GetDebugLockdownPolicy ( path , out SystemScriptFileEnforcement debugPolicy ) ;
325+ return debugPolicy ;
307326 }
308327
309328 [ SuppressMessage ( "Microsoft.Reliability" , "CA2001:AvoidCallingProblematicMethods" ,
@@ -558,7 +577,7 @@ private static SaferPolicy TestSaferPolicy(string testPathScript, string testPat
558577 return result ;
559578 }
560579
561- private static SystemEnforcementMode GetDebugLockdownPolicy ( string path )
580+ private static SystemEnforcementMode GetDebugLockdownPolicy ( string path , out SystemScriptFileEnforcement modernEnforcement )
562581 {
563582 s_allowDebugOverridePolicy = true ;
564583
@@ -569,10 +588,19 @@ private static SystemEnforcementMode GetDebugLockdownPolicy(string path)
569588 // check so that we can actually put it in the filename during testing.
570589 if ( path . Contains ( "System32" , StringComparison . OrdinalIgnoreCase ) )
571590 {
591+ modernEnforcement = SystemScriptFileEnforcement . Allow ;
572592 return SystemEnforcementMode . None ;
573593 }
574594
575595 // No explicit debug allowance for the file, so return the system policy if there is one.
596+ modernEnforcement = s_systemLockdownPolicy switch
597+ {
598+ SystemEnforcementMode . Enforce => SystemScriptFileEnforcement . AllowConstrained ,
599+ SystemEnforcementMode . Audit => SystemScriptFileEnforcement . AllowConstrainedAudit ,
600+ SystemEnforcementMode . None => SystemScriptFileEnforcement . None ,
601+ _ => SystemScriptFileEnforcement . None ,
602+ } ;
603+
576604 return s_systemLockdownPolicy . GetValueOrDefault ( SystemEnforcementMode . None ) ;
577605 }
578606
@@ -582,10 +610,13 @@ private static SystemEnforcementMode GetDebugLockdownPolicy(string path)
582610 if ( result != null )
583611 {
584612 pdwLockdownState = LanguagePrimitives . ConvertTo < uint > ( result ) ;
585- return GetLockdownPolicyForResult ( pdwLockdownState ) ;
613+ SystemEnforcementMode policy = GetLockdownPolicyForResult ( pdwLockdownState ) ;
614+ modernEnforcement = ConvertToModernFileEnforcement ( policy ) ;
615+ return policy ;
586616 }
587617
588618 // If the system-wide debug policy had no preference, then there is no enforcement.
619+ modernEnforcement = SystemScriptFileEnforcement . None ;
589620 return SystemEnforcementMode . None ;
590621 }
591622
0 commit comments