Merge pull request #12 from lithnet/v2-consent-header
Adds support for reading 'v2' consent ui headers
This commit is contained in:
@@ -23,8 +23,12 @@ namespace Lithnet.CredentialProvider
|
||||
{
|
||||
private static bool? isConsentUI;
|
||||
private static ConsentUICommandLineArgs commandLineArgs;
|
||||
private protected ConsentUIStructureHeader header;
|
||||
private protected ConsentUIStructureHeaderBase header;
|
||||
private readonly byte[] rawData;
|
||||
private static readonly Version v2HeaderOsVersion = new Version(10, 0, 26100, 0);
|
||||
internal static int v2HeaderSize = Marshal.SizeOf<ConsentUIStructureHeaderV2>();
|
||||
internal static int v1HeaderSize = Marshal.SizeOf<ConsentUIStructureHeaderV1>();
|
||||
internal static int HeaderSize { get; set; } = Environment.OSVersion.Version >= v2HeaderOsVersion ? v2HeaderSize : v1HeaderSize;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the type of ConsentUI data structure
|
||||
@@ -32,7 +36,8 @@ namespace Lithnet.CredentialProvider
|
||||
public ConsentUIType Type => this.header.Type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating the consent prompt type
|
||||
/// Gets a value indicating how UAC has been told to fetch approval.
|
||||
/// In the case where a Credential Provider is initialised, this should always be `Credentials`.
|
||||
/// </summary>
|
||||
public ConsentUIPromptType PromptType => this.header.PromptType;
|
||||
|
||||
@@ -42,13 +47,13 @@ namespace Lithnet.CredentialProvider
|
||||
public IntPtr HWnd => this.header.hWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method that ConsentUI has been told to fetch approval.
|
||||
/// In the case where a Credential Provider is initialised, this should always be `Credentials`.
|
||||
/// Gets the reason why `consent.exe` was started in the first place. In other words,
|
||||
/// the type of action that led to an elevation request.
|
||||
/// </summary>
|
||||
public ConsentUIElevationType ElevationType => this.header.ElevationType;
|
||||
public ConsentUIElevationReason ElevationReason => this.header.ElevationReason;
|
||||
|
||||
/// <summary>
|
||||
/// A series of flags that AppInfo passes to ConsentUI to signifiy actions that need to
|
||||
/// A series of flags that AppInfo passes to ConsentUI to signify actions that need to
|
||||
/// take place on the UI side.
|
||||
/// This includes specifics around the UI that should be presented & signature verification settings.
|
||||
/// </summary>
|
||||
@@ -62,12 +67,18 @@ namespace Lithnet.CredentialProvider
|
||||
private protected ConsentUIData(IntPtr pData, int expectedSize)
|
||||
{
|
||||
this.rawData = GetRawBytes(pData, expectedSize);
|
||||
this.header = Marshal.PtrToStructure<ConsentUIStructureHeader>(pData);
|
||||
|
||||
this.header = Marshal.PtrToStructure<ConsentUIStructureHeaderBase>(pData);
|
||||
|
||||
if (this.header.Size != expectedSize)
|
||||
{
|
||||
throw new InvalidDataException($"The size of the data structure {this.header.Size} does not match the expected size {expectedSize}");
|
||||
}
|
||||
|
||||
if (HeaderSize > this.header.Size)
|
||||
{
|
||||
throw new InvalidDataException($"The size of the data structure {this.header.Size} is less than the expected header size {HeaderSize}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -330,7 +341,7 @@ namespace Lithnet.CredentialProvider
|
||||
/// <param name="handle">The handle to duplicate</param>
|
||||
/// <returns>A duplicated reference to the handle</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when the handle could not be duplicated</exception>
|
||||
protected private static SafeHandle DuplicateHandleInternal(IntPtr handle)
|
||||
private protected static SafeHandle DuplicateHandleInternal(IntPtr handle)
|
||||
{
|
||||
commandLineArgs ??= GetConsentUICommandLineArgs();
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Lithnet.CredentialProvider
|
||||
throw new InvalidOperationException("The data structure is not of type COM");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureCom>(pData);
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureCom>(pData + HeaderSize);
|
||||
|
||||
this.ComComponentPath = this.GetStringValueIfValid(pData, (int)s.oComComponentPath);
|
||||
this.ImageResourcePath = this.GetStringValueIfValid(pData, (int)s.oImageResourcePath);
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Lithnet.CredentialProvider
|
||||
throw new InvalidOperationException("The data structure is not of type EXE");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureExe>(pData);
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureExe>(pData + HeaderSize);
|
||||
|
||||
this.hFile = s.hFile;
|
||||
this.ExecutablePath = this.GetStringValueIfValid(pData, (int)s.oExecutablePath1);
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Lithnet.CredentialProvider
|
||||
throw new InvalidOperationException("The data structure is not of type MSI");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureMsi>(pData);
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureMsi>(pData + HeaderSize);
|
||||
|
||||
this.Action = s.MsiAction;
|
||||
this.ProductName = this.GetStringValueIfValid(pData, (int)s.oProductName);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Lithnet.CredentialProvider
|
||||
throw new InvalidOperationException("The data structure is not of type MSIX");
|
||||
}
|
||||
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureMsix>(pData);
|
||||
var s = Marshal.PtrToStructure<ConsentUIStructureMsix>(pData + HeaderSize);
|
||||
|
||||
this.ExecutablePath = this.GetStringValueIfValid(pData, (int)s.oExecutablePath);
|
||||
this.PackageName = this.GetStringValueIfValid(pData, (int)s.oPackageName);
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,89 @@
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
public enum ConsentUIElevationReason
|
||||
{
|
||||
/// <summary>
|
||||
/// Application Compatibility
|
||||
/// e.g. "Run this program as an Administrator" explicitly configured
|
||||
///
|
||||
/// > "The AppCompat database stores information in the application
|
||||
/// > compatibility fix entries for an application."
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_APPCOMPAT_EXPLICIT (ole32.dll)`
|
||||
/// </summary>
|
||||
AppCompatExplicit = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Application Compatibility
|
||||
/// e.g. "Run this program as an Administrator" set via Windows heuristics
|
||||
///
|
||||
/// > "The AppCompat database stores information in the application
|
||||
/// > compatibility fix entries for an application."
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_APPCOMPAT_HEURISTIC (ole32.dll)`
|
||||
/// </summary>
|
||||
AppCompatHeuristic = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Application manifest
|
||||
/// > "The Fusion database stores information from application
|
||||
/// > manifests that describe the applications. The manifest schema
|
||||
/// > is updated to add a new requested execution level field."
|
||||
/// See also: https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests#trustinfo
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_FUSION (ole32.dll)`
|
||||
/// </summary>
|
||||
Fusion = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Automatically detected Windows Installer package (e.g. an EXE installer)
|
||||
/// > "Installer detection detects setup files, which helps prevent installations
|
||||
/// > from being run without the user's knowledge and consent."
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_INSTALLER (ole32.dll)`
|
||||
/// </summary>
|
||||
Installer = 3,
|
||||
|
||||
/// <summary>
|
||||
/// COM elevation action
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_CLSID (ole32.dll)`
|
||||
/// </summary>
|
||||
Clsid = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Windows Installer package installation (MSI)
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_MSI (ole32.dll)`
|
||||
/// </summary>
|
||||
Msi = 5,
|
||||
|
||||
/// <summary>
|
||||
/// "Run as Administrator..." (e.g. manual UAC)
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_REQUEST (ole32.dll)`
|
||||
/// </summary>
|
||||
Request = 6,
|
||||
|
||||
/// <summary>
|
||||
/// ActiveX Installer Service (AXIS)
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_AXIS (ole32.dll)`
|
||||
/// </summary>
|
||||
Axis = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Packaged Applications (MSIX / APPX)
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_PACKAGED_APP (ole32.dll)`
|
||||
/// </summary>
|
||||
Msix = 8,
|
||||
|
||||
/// <summary>
|
||||
/// Unknown
|
||||
///
|
||||
/// `ELEVATION_REASON.ELEVATION_REASON_NUM_REASONS (ole32.dll)`
|
||||
/// </summary>
|
||||
NumReasons = 9,
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace Lithnet.CredentialProvider
|
||||
{
|
||||
public enum ConsentUIElevationType
|
||||
{
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Automatic Admin Mode.
|
||||
/// This seems to be an instance where UAC creates a local, secondary
|
||||
/// account called '%username%_admin' which is used to elevate a process.
|
||||
/// </summary>
|
||||
AutomaticAdmin = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Prompt the user for consent (i.e. Yes or No)
|
||||
/// </summary>
|
||||
Consent = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Prompt the user for credentials
|
||||
/// </summary>
|
||||
Credentials = 3
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace Lithnet.CredentialProvider
|
||||
BlockElevation = 0x1000,
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to `ConsentUIElevationType.AutomaticAdmin`
|
||||
/// Corresponds to `ConsentUIPromptType.AutomaticAdmin`
|
||||
/// This seems to be an instance where UAC creates a local, secondary
|
||||
/// account called '%username%_admin' which is used to elevate a process.
|
||||
/// </summary>
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
/// </summary>
|
||||
IntPtr pointer;
|
||||
|
||||
int size;
|
||||
|
||||
SafeHGlobalHandle()
|
||||
{
|
||||
this.pointer = IntPtr.Zero;
|
||||
@@ -25,6 +27,8 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
this.Dispose();
|
||||
}
|
||||
|
||||
public int Size => size;
|
||||
|
||||
public static SafeHGlobalHandle InvalidHandle => new SafeHGlobalHandle(IntPtr.Zero);
|
||||
|
||||
/// <summary>
|
||||
@@ -58,6 +62,7 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
|
||||
SafeHGlobalHandle result = new SafeHGlobalHandle();
|
||||
result.pointer = Marshal.AllocHGlobal(cb);
|
||||
result.size = cb;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureCom
|
||||
{
|
||||
public ConsentUIStructureHeader Header;
|
||||
|
||||
// 64
|
||||
|
||||
public IntPtr oOperationType; // 8
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureExe
|
||||
{
|
||||
public ConsentUIStructureHeader Header;
|
||||
|
||||
// 64
|
||||
|
||||
public IntPtr hFile; // 8
|
||||
|
||||
+2
-5
@@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
|
||||
namespace Lithnet.CredentialProvider.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureHeader
|
||||
internal struct ConsentUIStructureHeaderBase
|
||||
{
|
||||
public int Size; // 4
|
||||
public ConsentUIType Type; // 4
|
||||
@@ -18,7 +18,7 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
|
||||
// 32
|
||||
|
||||
public ConsentUIElevationType ElevationType; // 4
|
||||
public ConsentUIElevationReason ElevationReason; // 4
|
||||
public int sessionId; // 4
|
||||
public IntPtr hMutex; // 8
|
||||
|
||||
@@ -26,8 +26,5 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
|
||||
public ConsentUIFlags Flags; // 4
|
||||
public int unknown0; // 4
|
||||
public IntPtr pReturnAddress; // 8
|
||||
|
||||
// 64
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Lithnet.CredentialProvider.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureHeaderV1
|
||||
{
|
||||
public ConsentUIStructureHeaderBase BaseHeader;
|
||||
|
||||
public IntPtr pReturnAddress; // 8
|
||||
|
||||
// 64
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Lithnet.CredentialProvider.Interop
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureHeaderV2
|
||||
{
|
||||
public ConsentUIStructureHeaderBase BaseHeader;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x81)]
|
||||
public byte[] unknown1;
|
||||
|
||||
public IntPtr pReturnAddress; // 8
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,6 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureMsi
|
||||
{
|
||||
public ConsentUIStructureHeader Header;
|
||||
|
||||
// 64
|
||||
|
||||
public ConsentUIMsiAction MsiAction; // 8
|
||||
|
||||
@@ -6,8 +6,6 @@ namespace Lithnet.CredentialProvider.Interop
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct ConsentUIStructureMsix
|
||||
{
|
||||
public ConsentUIStructureHeader Header;
|
||||
|
||||
// 64
|
||||
|
||||
public IntPtr oExecutablePath; // 8
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0-windows;net7.0-windows;net461</TargetFrameworks>
|
||||
<TargetFrameworks>net6.0-windows;net7.0-windows;net8.0-windows;net461</TargetFrameworks>
|
||||
<RegisterForComInterop>false</RegisterForComInterop>
|
||||
<OutputType>Library</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -38,6 +38,15 @@
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
|
||||
<_Parameter1>Lithnet.CredentialProvider.UnitTests.x64</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
|
||||
<_Parameter1>Lithnet.CredentialProvider.UnitTests.x86</_Parameter1>
|
||||
</AssemblyAttribute>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"$schema": "https://aka.ms/CsWin32.schema.json"
|
||||
}
|
||||
Reference in New Issue
Block a user