Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 114 additions & 83 deletions src/System.Management.Automation/security/wldpNativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
#if !UNIX

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
Expand Down Expand Up @@ -148,7 +149,7 @@ public static SystemEnforcementMode GetSystemLockdownPolicy()
{
lock (s_systemLockdownPolicyLock)
{
s_systemLockdownPolicy = GetDebugLockdownPolicy(path: null);
s_systemLockdownPolicy = GetDebugLockdownPolicy(path: null, out _);
}
}

Expand All @@ -172,93 +173,89 @@ public static SystemScriptFileEnforcement GetFilePolicyEnforcement(
System.IO.FileStream fileStream)
{
SafeHandle fileHandle = fileStream.SafeFileHandle;
var systemLockdownPolicy = SystemPolicy.GetSystemLockdownPolicy();
SystemEnforcementMode systemLockdownPolicy = GetSystemLockdownPolicy();

// First check latest WDAC APIs if available.
// Revert to legacy APIs if system policy is in AUDIT mode or debug hook is in effect.
Exception errorException = null;
if (s_wldpCanExecuteAvailable && systemLockdownPolicy == SystemEnforcementMode.Enforce)
if (systemLockdownPolicy is SystemEnforcementMode.Enforce
&& s_wldpCanExecuteAvailable
&& TryGetWldpCanExecuteFileResult(filePath, fileHandle, out SystemScriptFileEnforcement wldpFilePolicy))
{
try
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(filePath);
string auditMsg = $"PowerShell ExternalScriptInfo reading file: {fileName}";
return GetLockdownPolicy(filePath, fileHandle, wldpFilePolicy);
}

int hr = WldpNativeMethods.WldpCanExecuteFile(
host: PowerShellHost,
options: WLDP_EXECUTION_EVALUATION_OPTIONS.WLDP_EXECUTION_EVALUATION_OPTION_NONE,
fileHandle: fileHandle.DangerousGetHandle(),
auditInfo: auditMsg,
result: out WLDP_EXECUTION_POLICY canExecuteResult);
// Failed to invoke WldpCanExecuteFile, revert to legacy APIs.
if (systemLockdownPolicy is SystemEnforcementMode.None)
{
return SystemScriptFileEnforcement.None;
}

PSEtwLog.LogWDACQueryEvent("WldpCanExecuteFile", filePath, hr, (int)canExecuteResult);
// WldpCanExecuteFile was invoked successfully so we can skip running
// legacy WDAC APIs. AppLocker must still be checked in case it is more
// strict than the current WDAC policy.
return GetLockdownPolicy(filePath, fileHandle, canExecuteResult: null);
}

if (hr >= 0)
{
switch (canExecuteResult)
{
case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_ALLOWED:
return SystemScriptFileEnforcement.Allow;
private static SystemScriptFileEnforcement ConvertToModernFileEnforcement(SystemEnforcementMode legacyMode)
{
return legacyMode switch
{
SystemEnforcementMode.None => SystemScriptFileEnforcement.Allow,
SystemEnforcementMode.Audit => SystemScriptFileEnforcement.AllowConstrainedAudit,
SystemEnforcementMode.Enforce => SystemScriptFileEnforcement.AllowConstrained,
_ => SystemScriptFileEnforcement.Block,
};
}

case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_BLOCKED:
return SystemScriptFileEnforcement.Block;
private static bool TryGetWldpCanExecuteFileResult(string filePath, SafeHandle fileHandle, out SystemScriptFileEnforcement result)
{
try
{
string fileName = System.IO.Path.GetFileNameWithoutExtension(filePath);
string auditMsg = $"PowerShell ExternalScriptInfo reading file: {fileName}";

case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_REQUIRE_SANDBOX:
return SystemScriptFileEnforcement.AllowConstrained;
int hr = WldpNativeMethods.WldpCanExecuteFile(
host: PowerShellHost,
options: WLDP_EXECUTION_EVALUATION_OPTIONS.WLDP_EXECUTION_EVALUATION_OPTION_NONE,
fileHandle: fileHandle.DangerousGetHandle(),
auditInfo: auditMsg,
result: out WLDP_EXECUTION_POLICY canExecuteResult);

default:
// Fall through to legacy system policy checks.
System.Diagnostics.Debug.Assert(false, $"Unknown execution policy returned from WldCanExecute: {canExecuteResult}");
break;
}
}
PSEtwLog.LogWDACQueryEvent("WldpCanExecuteFile", filePath, hr, (int)canExecuteResult);

// If HResult is unsuccessful (such as E_NOTIMPL (0x80004001)), fall through to legacy system checks.
}
catch (DllNotFoundException ex)
{
// Fall back to legacy system policy checks.
s_wldpCanExecuteAvailable = false;
errorException = ex;
}
catch (EntryPointNotFoundException ex)
if (hr >= 0)
{
// Fall back to legacy system policy checks.
s_wldpCanExecuteAvailable = false;
errorException = ex;
switch (canExecuteResult)
{
case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_ALLOWED:
result = SystemScriptFileEnforcement.Allow;
return true;

case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_BLOCKED:
result = SystemScriptFileEnforcement.Block;
return true;

case WLDP_EXECUTION_POLICY.WLDP_CAN_EXECUTE_REQUIRE_SANDBOX:
result = SystemScriptFileEnforcement.AllowConstrained;
return true;

default:
// Fall through to legacy system policy checks.
Debug.Assert(false, $"Unknown policy result returned from WldCanExecute: {canExecuteResult}");
break;
}
}

if (errorException != null)
{
PSEtwLog.LogWDACQueryEvent("WldpCanExecuteFile_Failed", filePath, errorException.HResult, 0);
}
// If HResult is unsuccessful (such as E_NOTIMPL (0x80004001)), fall through to legacy system checks.
}

// Original (legacy) WDAC and AppLocker system checks.
if (systemLockdownPolicy == SystemEnforcementMode.None)
catch (Exception ex) when (ex is DllNotFoundException or EntryPointNotFoundException)
{
return SystemScriptFileEnforcement.None;
// Fall back to legacy system policy checks.
s_wldpCanExecuteAvailable = false;
PSEtwLog.LogWDACQueryEvent("WldpCanExecuteFile_Failed", filePath, ex.HResult, 0);
}

// Check policy for file.
switch (SystemPolicy.GetLockdownPolicy(filePath, fileHandle))
{
case SystemEnforcementMode.Enforce:
// File is not allowed by policy enforcement and must run in CL mode.
return SystemScriptFileEnforcement.AllowConstrained;

case SystemEnforcementMode.Audit:
// File is allowed but would be run in CL mode if policy was enforced and not audit.
return SystemScriptFileEnforcement.AllowConstrainedAudit;

case SystemEnforcementMode.None:
// No restrictions, file will run in FL mode.
return SystemScriptFileEnforcement.Allow;

default:
System.Diagnostics.Debug.Assert(false, "GetFilePolicyEnforcement: Unknown SystemEnforcementMode.");
return SystemScriptFileEnforcement.Block;
}
result = default;
return false;
}

/// <summary>
Expand All @@ -267,9 +264,32 @@ public static SystemScriptFileEnforcement GetFilePolicyEnforcement(
/// <returns>An EnforcementMode that describes policy.</returns>
public static SystemEnforcementMode GetLockdownPolicy(string path, SafeHandle handle)
{
SystemScriptFileEnforcement modernMode = GetLockdownPolicy(path, handle, canExecuteResult: null);
Debug.Assert(
modernMode is not SystemScriptFileEnforcement.Block,
"Block should never be converted to legacy file enforcement.");

return modernMode switch
{
SystemScriptFileEnforcement.Block => SystemEnforcementMode.Enforce,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't/shouldn't this not be the case as per the above and it would be better to throw the exception in the default branch?

SystemScriptFileEnforcement.AllowConstrained => SystemEnforcementMode.Enforce,
SystemScriptFileEnforcement.AllowConstrainedAudit => SystemEnforcementMode.Audit,
SystemScriptFileEnforcement.Allow => SystemEnforcementMode.None,
SystemScriptFileEnforcement.None => SystemEnforcementMode.None,
_ => throw new ArgumentOutOfRangeException(nameof(modernMode)),
};
}

private static SystemScriptFileEnforcement GetLockdownPolicy(
string path,
SafeHandle handle,
SystemScriptFileEnforcement? canExecuteResult)
{
SystemScriptFileEnforcement wldpFilePolicy = canExecuteResult
?? ConvertToModernFileEnforcement(GetWldpPolicy(path, handle));

// Check the WLDP File policy via API
var wldpFilePolicy = GetWldpPolicy(path, handle);
if (wldpFilePolicy == SystemEnforcementMode.Enforce)
if (wldpFilePolicy is SystemScriptFileEnforcement.Block or SystemScriptFileEnforcement.AllowConstrained)
{
return wldpFilePolicy;
}
Expand All @@ -281,29 +301,28 @@ public static SystemEnforcementMode GetLockdownPolicy(string path, SafeHandle ha
var appLockerFilePolicy = GetAppLockerPolicy(path, handle);
if (appLockerFilePolicy == SystemEnforcementMode.Enforce)
{
return appLockerFilePolicy;
return ConvertToModernFileEnforcement(appLockerFilePolicy);
}

// At this point, LockdownPolicy = Audit or Allowed.
// If there was a WLDP policy, but WLDP didn't block it,
// then it was explicitly allowed. Therefore, return the result for the file.
SystemEnforcementMode systemWldpPolicy = s_cachedWldpSystemPolicy.GetValueOrDefault(SystemEnforcementMode.None);
if ((systemWldpPolicy == SystemEnforcementMode.Audit) ||
(systemWldpPolicy == SystemEnforcementMode.Enforce))
if (s_cachedWldpSystemPolicy is SystemEnforcementMode.Audit or SystemEnforcementMode.Enforce
|| wldpFilePolicy is SystemScriptFileEnforcement.AllowConstrainedAudit)
{
return wldpFilePolicy;
}

// If there was a system-wide AppLocker policy, but AppLocker didn't block it,
// then return AppLocker's status.
if (s_cachedSaferSystemPolicy.GetValueOrDefault(SaferPolicy.Allowed) ==
SaferPolicy.Disallowed)
if (s_cachedSaferSystemPolicy is SaferPolicy.Disallowed)
{
return appLockerFilePolicy;
return ConvertToModernFileEnforcement(appLockerFilePolicy);
}

// If it's not set to 'Enforce' by the platform, allow debug overrides
return GetDebugLockdownPolicy(path);
GetDebugLockdownPolicy(path, out SystemScriptFileEnforcement debugPolicy);
return debugPolicy;
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods",
Expand Down Expand Up @@ -558,7 +577,7 @@ private static SaferPolicy TestSaferPolicy(string testPathScript, string testPat
return result;
}

private static SystemEnforcementMode GetDebugLockdownPolicy(string path)
private static SystemEnforcementMode GetDebugLockdownPolicy(string path, out SystemScriptFileEnforcement modernEnforcement)
{
s_allowDebugOverridePolicy = true;

Expand All @@ -569,10 +588,19 @@ private static SystemEnforcementMode GetDebugLockdownPolicy(string path)
// check so that we can actually put it in the filename during testing.
if (path.Contains("System32", StringComparison.OrdinalIgnoreCase))
{
modernEnforcement = SystemScriptFileEnforcement.Allow;
return SystemEnforcementMode.None;
}

// No explicit debug allowance for the file, so return the system policy if there is one.
modernEnforcement = s_systemLockdownPolicy switch
{
SystemEnforcementMode.Enforce => SystemScriptFileEnforcement.AllowConstrained,
SystemEnforcementMode.Audit => SystemScriptFileEnforcement.AllowConstrainedAudit,
SystemEnforcementMode.None => SystemScriptFileEnforcement.None,
_ => SystemScriptFileEnforcement.None,
};

return s_systemLockdownPolicy.GetValueOrDefault(SystemEnforcementMode.None);
}

Expand All @@ -582,10 +610,13 @@ private static SystemEnforcementMode GetDebugLockdownPolicy(string path)
if (result != null)
{
pdwLockdownState = LanguagePrimitives.ConvertTo<uint>(result);
return GetLockdownPolicyForResult(pdwLockdownState);
SystemEnforcementMode policy = GetLockdownPolicyForResult(pdwLockdownState);
modernEnforcement = ConvertToModernFileEnforcement(policy);
return policy;
}

// If the system-wide debug policy had no preference, then there is no enforcement.
modernEnforcement = SystemScriptFileEnforcement.None;
return SystemEnforcementMode.None;
}

Expand Down
Loading