From 97fb10713ff9e086509810f848eee797f8a30913 Mon Sep 17 00:00:00 2001 From: James Spencer Date: Sun, 1 Sep 2024 18:52:42 +1000 Subject: [PATCH] Correctly document 'ConsentUIElevationReason' based on ELEVATION_REASON enum from Windows headers. Update tests to match --- .../V1HeaderTests.cs | 8 +-- .../V2HeaderTests.cs | 8 +-- .../V1HeaderTests.cs | 8 +-- .../ConsentUI/ConsentUIData.cs | 11 +-- .../Enums/ConsentUIElevationReason.cs | 70 +++++++++++++++++++ .../Enums/ConsentUIElevationType.cs | 24 ------- .../Enums/ConsentUIFlags.cs | 2 +- .../Structs/ConsentUIStructureHeaderBase.cs | 2 +- 8 files changed, 90 insertions(+), 43 deletions(-) create mode 100644 src/Lithnet.CredentialProvider/Enums/ConsentUIElevationReason.cs delete mode 100644 src/Lithnet.CredentialProvider/Enums/ConsentUIElevationType.cs diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/V1HeaderTests.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/V1HeaderTests.cs index 6bf34d8..a8d3126 100644 --- a/src/Lithnet.CredentialProvider.UnitTests.x64/V1HeaderTests.cs +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/V1HeaderTests.cs @@ -23,7 +23,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.InWindowsDirectory | ConsentUIFlags.SecureDesktop | ConsentUIFlags.Unknown3, d.Flags); Assert.AreEqual(3, d.SessionId); Assert.AreEqual((IntPtr)0x0000000000010128, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)6, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.Request, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", d.ExecutablePath); Assert.AreEqual("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", d.Unknown1); @@ -42,7 +42,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.InWindowsDirectory | ConsentUIFlags.SecureDesktop | ConsentUIFlags.AutoElevationOther, d.Flags); Assert.AreEqual(3, d.SessionId); Assert.AreEqual((IntPtr)0x0000000000230370, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)5, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.Msi, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("7-Zip 23.01 (x64 edition)", d.ProductName); Assert.AreEqual(ConsentUIMsiAction.Install, d.Action); @@ -65,7 +65,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual( ConsentUIFlags.SecureDesktop | ConsentUIFlags.Unknown3, d.Flags); Assert.AreEqual(3, d.SessionId); Assert.AreEqual((IntPtr)0x0, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)8, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.PackagedApp, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("\"C:\\Program Files\\WindowsApps\\Microsoft.MSIXPackagingTool_1.2023.807.0_x64__8wekyb3d8bbwe\\MsixPackageTool.exe\" ", d.CommandLine); Assert.AreEqual("C:\\Program Files\\WindowsApps\\Microsoft.MSIXPackagingTool_1.2023.807.0_x64__8wekyb3d8bbwe\\MsixPackageTool.exe", d.ExecutablePath); @@ -84,7 +84,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.SecureDesktop | ConsentUIFlags.AutoElevationOther | ConsentUIFlags.InWindowsDirectory, d.Flags); Assert.AreEqual(3, d.SessionId); Assert.AreEqual((IntPtr)0x00000000000903c2, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)4, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.CLSID, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual(new Guid("{3ad05575-8857-4850-9277-11b85bdb8e09}"), d.ClsId); Assert.AreEqual("C:\\Windows\\system32\\windows.storage.dll", d.ComComponentPath); diff --git a/src/Lithnet.CredentialProvider.UnitTests.x64/V2HeaderTests.cs b/src/Lithnet.CredentialProvider.UnitTests.x64/V2HeaderTests.cs index fbd5af1..0417bd3 100644 --- a/src/Lithnet.CredentialProvider.UnitTests.x64/V2HeaderTests.cs +++ b/src/Lithnet.CredentialProvider.UnitTests.x64/V2HeaderTests.cs @@ -24,7 +24,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.InWindowsDirectory | ConsentUIFlags.SecureDesktop | ConsentUIFlags.Unknown3, d.Flags); Assert.AreEqual(3, d.SessionId); Assert.AreEqual((IntPtr)0x0, d.HWnd); // this seems odd - Assert.AreEqual((ConsentUIElevationType)6, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.Request, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", d.ExecutablePath); Assert.AreEqual("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", d.Unknown1); @@ -43,7 +43,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.InWindowsDirectory | ConsentUIFlags.SecureDesktop | ConsentUIFlags.AutoElevationOther, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x00000000001e033a, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)5, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.Msi, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("7-Zip 24.08 (x64 edition)", d.ProductName); Assert.AreEqual(ConsentUIMsiAction.Install, d.Action); @@ -66,7 +66,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.SecureDesktop | ConsentUIFlags.Unknown3, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x0, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)8, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.PackagedApp, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("\"C:\\Program Files\\WindowsApps\\Microsoft.MSIXPackagingTool_1.2024.405.0_x64__8wekyb3d8bbwe\\MsixPackageTool.exe\" ", d.CommandLine); Assert.AreEqual("C:\\Program Files\\WindowsApps\\Microsoft.MSIXPackagingTool_1.2024.405.0_x64__8wekyb3d8bbwe\\MsixPackageTool.exe", d.ExecutablePath); @@ -85,7 +85,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.SecureDesktop | ConsentUIFlags.AutoElevationOther | ConsentUIFlags.InWindowsDirectory, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x00000000000d021e, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)4, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.CLSID, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual(new Guid("{3ad05575-8857-4850-9277-11b85bdb8e09}"), d.ClsId); Assert.AreEqual("C:\\WINDOWS\\system32\\windows.storage.dll", d.ComComponentPath); diff --git a/src/Lithnet.CredentialProvider.UnitTests.x86/V1HeaderTests.cs b/src/Lithnet.CredentialProvider.UnitTests.x86/V1HeaderTests.cs index e4c6dbe..4aa43e8 100644 --- a/src/Lithnet.CredentialProvider.UnitTests.x86/V1HeaderTests.cs +++ b/src/Lithnet.CredentialProvider.UnitTests.x86/V1HeaderTests.cs @@ -23,7 +23,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.InWindowsDirectory | ConsentUIFlags.SecureDesktop | ConsentUIFlags.Unknown3, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x0006056a, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)6, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.Request, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", d.ExecutablePath); Assert.AreEqual("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", d.Unknown1); @@ -42,7 +42,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.InWindowsDirectory | ConsentUIFlags.SecureDesktop | ConsentUIFlags.AutoElevationOther, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x000705bc, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)5, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.Msi, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("7-Zip 23.01", d.ProductName); Assert.AreEqual(ConsentUIMsiAction.Install, d.Action); @@ -65,7 +65,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual( ConsentUIFlags.SecureDesktop | ConsentUIFlags.Unknown3, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x0, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)8, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.PackagedApp, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual("\"C:\\Program Files\\WindowsApps\\Microsoft.MSIXPackagingTool_1.2023.807.0_x86__8wekyb3d8bbwe\\MsixPackageTool.exe\" ", d.CommandLine); Assert.AreEqual("C:\\Program Files\\WindowsApps\\Microsoft.MSIXPackagingTool_1.2023.807.0_x86__8wekyb3d8bbwe\\MsixPackageTool.exe", d.ExecutablePath); @@ -84,7 +84,7 @@ namespace Lithnet.CredentialProvider.UnitTests Assert.AreEqual(ConsentUIFlags.SecureDesktop | ConsentUIFlags.AutoElevationOther | ConsentUIFlags.InWindowsDirectory, d.Flags); Assert.AreEqual(2, d.SessionId); Assert.AreEqual((IntPtr)0x000b0586, d.HWnd); - Assert.AreEqual((ConsentUIElevationType)4, d.ElevationType); + Assert.AreEqual(ConsentUIElevationReason.CLSID, d.ElevationReason); Assert.AreEqual(ConsentUIPromptType.Credentials, d.PromptType); Assert.AreEqual(new Guid("{3ad05575-8857-4850-9277-11b85bdb8e09}"), d.ClsId); Assert.AreEqual("C:\\Windows\\system32\\windows.storage.dll", d.ComComponentPath); diff --git a/src/Lithnet.CredentialProvider/ConsentUI/ConsentUIData.cs b/src/Lithnet.CredentialProvider/ConsentUI/ConsentUIData.cs index 55da8fb..34fe588 100644 --- a/src/Lithnet.CredentialProvider/ConsentUI/ConsentUIData.cs +++ b/src/Lithnet.CredentialProvider/ConsentUI/ConsentUIData.cs @@ -36,7 +36,8 @@ namespace Lithnet.CredentialProvider public ConsentUIType Type => this.header.Type; /// - /// 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`. /// public ConsentUIPromptType PromptType => this.header.PromptType; @@ -46,13 +47,13 @@ namespace Lithnet.CredentialProvider public IntPtr HWnd => this.header.hWindow; /// - /// 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. /// - public ConsentUIElevationType ElevationType => this.header.ElevationType; + public ConsentUIElevationReason ElevationReason => this.header.ElevationReason; /// - /// 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. /// diff --git a/src/Lithnet.CredentialProvider/Enums/ConsentUIElevationReason.cs b/src/Lithnet.CredentialProvider/Enums/ConsentUIElevationReason.cs new file mode 100644 index 0000000..76b0025 --- /dev/null +++ b/src/Lithnet.CredentialProvider/Enums/ConsentUIElevationReason.cs @@ -0,0 +1,70 @@ +namespace Lithnet.CredentialProvider +{ + public enum ConsentUIElevationReason + { + /// + /// 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." + /// + AppCompatExplicit = 0, + + /// + /// 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." + /// + AppCompatHeuristic = 1, + + /// + /// 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 + /// + Fusion = 2, + + /// + /// 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." + /// + Installer = 3, + + /// + /// COM elevation action + /// + CLSID = 4, + + /// + /// Windows Installer package installation (MSI) + /// + Msi = 5, + + /// + /// "Run as Administrator..." (e.g. manual UAC) + /// + Request = 6, + + /// + /// ActiveX Installer Service (AXIS) + /// + AxIS = 7, + + /// + /// Packaged Applications (MSIX / APPX) + /// + PackagedApp = 8, + + /// + /// Unknown + /// + NumReasons = 9, + } +} \ No newline at end of file diff --git a/src/Lithnet.CredentialProvider/Enums/ConsentUIElevationType.cs b/src/Lithnet.CredentialProvider/Enums/ConsentUIElevationType.cs deleted file mode 100644 index b01808b..0000000 --- a/src/Lithnet.CredentialProvider/Enums/ConsentUIElevationType.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Lithnet.CredentialProvider -{ - public enum ConsentUIElevationType - { - Unknown = 0, - - /// - /// 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. - /// - AutomaticAdmin = 1, - - /// - /// Prompt the user for consent (i.e. Yes or No) - /// - Consent = 2, - - /// - /// Prompt the user for credentials - /// - Credentials = 3 - } -} \ No newline at end of file diff --git a/src/Lithnet.CredentialProvider/Enums/ConsentUIFlags.cs b/src/Lithnet.CredentialProvider/Enums/ConsentUIFlags.cs index 08bda47..04a82bf 100644 --- a/src/Lithnet.CredentialProvider/Enums/ConsentUIFlags.cs +++ b/src/Lithnet.CredentialProvider/Enums/ConsentUIFlags.cs @@ -62,7 +62,7 @@ namespace Lithnet.CredentialProvider BlockElevation = 0x1000, /// - /// 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. /// diff --git a/src/Lithnet.CredentialProvider/Interop/Structs/ConsentUIStructureHeaderBase.cs b/src/Lithnet.CredentialProvider/Interop/Structs/ConsentUIStructureHeaderBase.cs index f58c6b7..2edb82c 100644 --- a/src/Lithnet.CredentialProvider/Interop/Structs/ConsentUIStructureHeaderBase.cs +++ b/src/Lithnet.CredentialProvider/Interop/Structs/ConsentUIStructureHeaderBase.cs @@ -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