From 1205f217b30c084551c25bf77865e448fd8a3a31 Mon Sep 17 00:00:00 2001 From: Ryan Newington Date: Sun, 29 Jan 2023 17:30:11 +1100 Subject: [PATCH] Cleans up static loggers Adds test projects for different frameworks and architectures Adds registration tool assembly --- ...CredentialProvider.RegistrationTool.csproj | 13 + .../Program.cs | 187 ++++++++++++++ .../Properties/launchSettings.json | 9 + .../Lithnet.CredentialProvider.Samples.csproj | 35 --- .../Resources/TileIcon.png | Bin 889 -> 0 bytes .../TestCredentialProvider.cs | 99 -------- src/Lithnet.CredentialProvider.sln | 48 +++- .../Controls/ControlBase.cs | 8 +- .../CredentialProviderBase.cs | 19 +- .../CredentialProviderCredential1Tile.cs | 9 +- .../CredentialProviderRegistrationServices.cs | 233 ++++++++++++++++++ .../CredentialProviderUser.cs | 4 +- .../Interop/CredentialSerializer.cs | 34 +-- .../Interop/Structs/FieldDescriptor.cs | 2 +- .../Lithnet.CredentialProvider.csproj | 7 +- .../ModuleInit/AssemblyResolver.cs | 37 +++ .../ModuleInit/ModuleInitializerAttribute.cs | 7 + .../ControlKeys.cs} | 2 +- .../Installing the sample.md | 50 ++++ .../InternalLogger.cs | 36 +++ ...redentialProvider.Sample.net472.x64.csproj | 14 ++ .../Resources.Designer.cs | 2 +- .../Resources.resx | 0 .../Resources/TileIcon.png | Bin 0 -> 1662 bytes .../TestCredentialProviderNet472x64.cs | 97 ++++++++ .../TestCredentialProviderTile.cs | 26 +- .../Installing the sample.md | 14 +- ...redentialProvider.Sample.net472.x86.csproj | 20 ++ .../Resources.Designer.cs | 73 ++++++ .../Resources.resx | 124 ++++++++++ .../Resources/TileIcon.png | Bin 0 -> 1878 bytes .../TestCredentialProviderNet472x86.cs | 100 ++++++++ .../Installing the sample.md | 36 +++ ...redentialProvider.Sample.net6.0.x64.csproj | 31 +++ .../Resources/TileIcon.png | Bin 0 -> 1510 bytes .../TestCredentialProviderNet60x64.cs | 105 ++++++++ .../Installing the sample.md | 36 +++ ...redentialProvider.Sample.net6.0.x86.csproj | 31 +++ .../Resources/TileIcon.png | Bin 0 -> 1708 bytes .../TestCredentialProviderNet60x86.cs | 104 ++++++++ .../CredUI.cs | 0 ...hnet.CredentialProvider.TestApp.x64.csproj | 17 ++ .../Program.cs | 0 .../CredUI.cs | 60 +++++ ...hnet.CredentialProvider.TestApp.x86.csproj | 16 ++ .../Program.cs | 44 ++++ 46 files changed, 1589 insertions(+), 200 deletions(-) create mode 100644 src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj create mode 100644 src/Lithnet.CredentialProvider.RegistrationTool/Program.cs create mode 100644 src/Lithnet.CredentialProvider.RegistrationTool/Properties/launchSettings.json delete mode 100644 src/Lithnet.CredentialProvider.Samples/Lithnet.CredentialProvider.Samples.csproj delete mode 100644 src/Lithnet.CredentialProvider.Samples/Resources/TileIcon.png delete mode 100644 src/Lithnet.CredentialProvider.Samples/TestCredentialProvider.cs create mode 100644 src/Lithnet.CredentialProvider/CredentialProviderRegistrationServices.cs create mode 100644 src/Lithnet.CredentialProvider/ModuleInit/AssemblyResolver.cs create mode 100644 src/Lithnet.CredentialProvider/ModuleInit/ModuleInitializerAttribute.cs rename src/{Lithnet.CredentialProvider.Samples/TestCredentialProviderControlKeys.cs => samples/Lithnet.CredentialProvider.Sample.net472.x64/ControlKeys.cs} (95%) create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x64/InternalLogger.cs create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj rename src/{Lithnet.CredentialProvider.Samples => samples/Lithnet.CredentialProvider.Sample.net472.x64}/Resources.Designer.cs (97%) rename src/{Lithnet.CredentialProvider.Samples => samples/Lithnet.CredentialProvider.Sample.net472.x64}/Resources.resx (100%) create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources/TileIcon.png create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs rename src/{Lithnet.CredentialProvider.Samples => samples/Lithnet.CredentialProvider.Sample.net472.x64}/TestCredentialProviderTile.cs (84%) rename src/{Lithnet.CredentialProvider.Samples => samples/Lithnet.CredentialProvider.Sample.net472.x86}/Installing the sample.md (61%) create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.Designer.cs create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.resx create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources/TileIcon.png create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Resources/TileIcon.png create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Resources/TileIcon.png create mode 100644 src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs rename src/{Lithnet.CredentialProvider.Samples => samples/Lithnet.CredentialProvider.TestApp.x64}/CredUI.cs (100%) create mode 100644 src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj rename src/{Lithnet.CredentialProvider.Samples => samples/Lithnet.CredentialProvider.TestApp.x64}/Program.cs (100%) create mode 100644 src/samples/Lithnet.CredentialProvider.TestApp.x86/CredUI.cs create mode 100644 src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj create mode 100644 src/samples/Lithnet.CredentialProvider.TestApp.x86/Program.cs diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj b/src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj new file mode 100644 index 0000000..bbebc64 --- /dev/null +++ b/src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj @@ -0,0 +1,13 @@ + + + + Exe + net6.0 + + + + + + + + diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/Program.cs b/src/Lithnet.CredentialProvider.RegistrationTool/Program.cs new file mode 100644 index 0000000..51aab60 --- /dev/null +++ b/src/Lithnet.CredentialProvider.RegistrationTool/Program.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + internal static class Program + { + static async Task Main(string[] args) + { + var rootCommand = new RootCommand("Lithnet credential provider registration tool"); + + var registerCommand = new Command("--register", "Registers the specified credential provider on this system"); + registerCommand.AddAlias("-r"); + var registerArgument = new Argument("file name", "The name of the file to register"); + registerCommand.AddArgument(registerArgument); + registerCommand.SetHandler((file) => RegisterAssembly(file), registerArgument); + rootCommand.Add(registerCommand); + + + var unregisterCommand = new Command("--unregister", "Unregisters the specified credential provider on this system"); + unregisterCommand.AddAlias("-u"); + var unregisterArgument = new Argument("file name", "The name of the file to register"); + unregisterCommand.AddArgument(unregisterArgument); + unregisterCommand.SetHandler((file) => UnregisterAssembly(file), unregisterArgument); + rootCommand.Add(unregisterCommand); + + + + var enableCommand = new Command("--enable", "Enables the specified credential provider on this system"); + enableCommand.AddAlias("-e"); + var enableFileOption = new Option("file", "The path to the credential provider to enable"); + + var enableGuidOption = new Option("providerid", "The GUID of the provider to enable"); + + enableCommand.AddOption(enableFileOption); + enableCommand.AddOption(enableGuidOption); + enableCommand.SetHandler((file) => EnableProviders(file), enableFileOption); + enableCommand.SetHandler((id) => EnableProviders(id), enableGuidOption); + rootCommand.Add(enableCommand); + + + //var unregisterOption = new Option( + // name: "--unregister", + // description: "Unregisters the specified file as a credential provider on this system"); + + //var enableOption = new Option( + // name: "--enable", + // description: "Enables a credential provider on this system"); + + //var disableOption = new Option( + // name: "--disable", + // description: "Disables a credential provider on this system"); + + + //rootCommand.AddOption(registerOptions); + //rootCommand.AddOption(unregisterOption); + //rootCommand.AddOption(enableOption); + //rootCommand.AddOption(disableOption); + + + //rootCommand.SetHandler((file) => RegisterAssembly(file), registerOptions); + //rootCommand.SetHandler((file) => UnregisterAssembly(file), unregisterOption); + //rootCommand.SetHandler((file) => EnableProviders(file), enableOption); + //rootCommand.SetHandler((file) => DisableProviders(file), disableOption); + + return await rootCommand.InvokeAsync(args); + } + + private static void UnregisterAssembly(FileInfo file) + { + var assembly = LoadAssembly(file.FullName); + + foreach (var type in GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.UnregisterCredentialProvider(type); + Console.WriteLine($"Unregistered credential provider {type.Name}"); + } + } + + private static void EnableProviders(FileInfo file) + { + var assembly = LoadAssembly(file.FullName); + + foreach (var type in GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.EnableCredentialProvider(type); + Console.WriteLine($"Enabled credential provider {type.FullName}"); + } + } + + private static void EnableProviders(Guid id) + { + CredentialProviderRegistrationServices.EnableCredentialProvider(id); + Console.WriteLine($"Enabled credential provider {id}"); + } + + private static void DisableProviders(FileInfo file) + { + var assembly = LoadAssembly(file.FullName); + + foreach (var type in GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.DisableCredentialProvider(type); + Console.WriteLine($"Disabled credential provider {type.FullName}"); + } + } + private static void DisableProviders(string id) + { + if (Guid.TryParse(id, out var providerId)) + { + CredentialProviderRegistrationServices.DisableCredentialProvider(providerId); + Console.WriteLine($"Disabled credential provider {providerId}"); + } + + var assembly = LoadAssembly(Path.GetFullPath(id)); + + foreach (var type in GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.DisableCredentialProvider(type); + Console.WriteLine($"Disabled credential provider {type.FullName}"); + } + } + + private static void RegisterAssembly(FileInfo file) + { + var assembly = LoadAssembly(file.FullName); + + foreach (var type in GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.RegisterCredentialProvider(type); + Console.WriteLine($"Registered credential provider {type.FullName}"); + } + } + + private static IEnumerable GetCredentialProviders(Assembly assembly) + { + return assembly.GetExportedTypes().Where(t => t.GetInterfaces().Any(ifn => ifn.Name == "ICredentialProvider") && !t.IsAbstract && !t.IsInterface); + } + + private static Assembly LoadAssembly(string assemblyPath) + { + string assemblyBasePath = Path.GetDirectoryName(assemblyPath); + + List paths = new List(); + paths.AddRange(Directory.GetFiles(Path.GetDirectoryName(assemblyPath))); + paths.Add(typeof(object).Assembly.Location); + paths.AddRange(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll")); + + var resolver = new PathAssemblyResolver(paths); + MetadataLoadContext mlc = new MetadataLoadContext(resolver); + return mlc.LoadFromAssemblyPath(assemblyPath); + + //AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, args) => + //{ + // var name = new AssemblyName(args.Name); + // string assyPath = Path.Combine(assemblyBasePath, $"{name.Name}.dll"); + // Trace.WriteLine($"Request for {args.Name}"); + + // if (File.Exists(assyPath)) + // { + // Trace.WriteLine($"Found at {assyPath}"); + // return Assembly.ReflectionOnlyLoadFrom(assyPath); + // } + + // assyPath = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), $"{name.Name}.dll"); + + // if (File.Exists(assyPath)) + // { + // Trace.WriteLine($"Found at {assyPath}"); + // return Assembly.ReflectionOnlyLoadFrom(assyPath); + // } + + // Trace.WriteLine($"Assembly {args.Name} not found"); + // return null; + + //}; + + //var assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath); + } + } +} \ No newline at end of file diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/Properties/launchSettings.json b/src/Lithnet.CredentialProvider.RegistrationTool/Properties/launchSettings.json new file mode 100644 index 0000000..2987def --- /dev/null +++ b/src/Lithnet.CredentialProvider.RegistrationTool/Properties/launchSettings.json @@ -0,0 +1,9 @@ +{ + "profiles": { + "Lithnet.CredentialProvider.RegistrationTool": { + "commandName": "Project", + "commandLineArgs": "--unregister \"D:\\dev\\git\\ryannewington\\CredProvider.NET\\CredProvider.NET\\bin\\Debug\\CredProvider.NET.dll\"" + //"commandLineArgs": "--register \"D:\\dev\\git\\lithnet\\windows-credential-provider\\src\\samples\\Lithnet.CredentialProvider.Sample.net6.0.x64\\bin\\Debug\\net6.0-windows\\Lithnet.CredentialProvider.Sample.net6.0.x64.dll\"" + } + } +} \ No newline at end of file diff --git a/src/Lithnet.CredentialProvider.Samples/Lithnet.CredentialProvider.Samples.csproj b/src/Lithnet.CredentialProvider.Samples/Lithnet.CredentialProvider.Samples.csproj deleted file mode 100644 index 879c78c..0000000 --- a/src/Lithnet.CredentialProvider.Samples/Lithnet.CredentialProvider.Samples.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - Exe - net472 - false - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - diff --git a/src/Lithnet.CredentialProvider.Samples/Resources/TileIcon.png b/src/Lithnet.CredentialProvider.Samples/Resources/TileIcon.png deleted file mode 100644 index 85fb8afa0a397f6b5b27cae2d15022a2fe76eb09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 889 zcmV-<1BU#GP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&0}e?@J@12Zvnoa5t?g7STU+KrpG3*>eY5wJM;xX>Z+kF%3iOztWvtXxE#7_>WeCz z9m2lyzSaVBZD_TZkt$ji$C`RCF!dPNiah0Iub;5-uT{K(Z*`@{4GpnGYk`?FCL4#V z2+}iZnZaUO2&T`2to;y-|AcUrqsb6UG!G^W{BXeJWa~*?1a^HCje2m5@0H0A`1)u1 z91PktdPd=jR|~rk4EiaW(Omb+446N}5_w?fQ3gv*87wtru+)^nQqv}2zhq7Ri)O)` zj~$L}ztAzjFy?dY%Lw8SVEoZN-b;khtCv0)et>)N7)nH#OUz^%j3Pp56dg{(AFv(4oP3KX2D8h8B^++agbYm(Ry_;iKQx$$8l!m&tTKV) z{%4Fl2%oW3!R*-(w;~*Ub#f+P?5-^_I^0$S@uE343l>Yr9PRQz-D=LIfQ?;)zRrND z)d(eI=q>>ptFqRdyMvuV87wtru+)^nQd0&?O@{$n`$O3S^S|*r_XiB$D&qadVA|IE z#@ii?;xQWR>nznf+7yf*?f;tnWR8lp5FU)<0Iw5vTD^iHg7H3<@Cl@eX5E-k4D3?U zis?u6iB0zyipRWv`-`79NH7}9H3AE#iDuluQ9QkI#^`14(M%p~9*(tn->O+~_7(Pt zft&y{ug^r;*oO(zEjbM{i`vDdrEyd}V#_U=~XckNhK8-V%yRvTX!POV*40XF+ORYlT7 zTY!PgPiVm8fmAX#8}CDq1cQ}{Bz!_?qJ?1RQ3gv*87wtru+(%?u(); - - public TestCredentialProvider() - { - } - - public override ILoggerFactory GetLoggerFactory() - { - return Program.LoggerFactory; - } - - public override IEnumerable GetControls(UsageScenario cpus) - { - - var password = new SecurePasswordTextboxControl(TestCredentialProviderControlKeys.Password, "Password"); - - if (cpus == UsageScenario.ChangePassword) - { - var confirmPassword = new SecurePasswordTextboxControl(TestCredentialProviderControlKeys.ConfirmPassword, "Confirm password"); - yield return new TextboxControl(TestCredentialProviderControlKeys.Username, "Username"); - yield return password; - yield return confirmPassword; - yield return new SubmitButtonControl(TestCredentialProviderControlKeys.ButtonSubmit, "Submit", confirmPassword); - } - else - { - yield return new CredentialProviderLabelControl(TestCredentialProviderControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); - yield return new CredentialProviderLogoControl(TestCredentialProviderControlKeys.ImageCredentialProvider, "Credential provider logo", Resources.TileIcon); - yield return new CredentialProviderLogoControl(TestCredentialProviderControlKeys.ImageUserTile, "User tile image", Resources.TileIcon); - - yield return new LargeLabelControl(TestCredentialProviderControlKeys.LabelLargeHeading, "The is our showcase credential provider"); - yield return new SmallLabelControl(TestCredentialProviderControlKeys.LabelSmallHeading, "Let's see what we can do"); - - yield return new CheckboxControl(TestCredentialProviderControlKeys.Checkbox, "A checkbox"); - yield return new SmallLabelControl(TestCredentialProviderControlKeys.LabelCheckboxValue, "The check box is currently unchecked"); - yield return new CommandLinkControl(TestCredentialProviderControlKeys.CommandLinkCheckboxValue, "Click this link to change the check box value in code behind"); - - yield return new ComboboxControl(TestCredentialProviderControlKeys.Combobox, "Items to choose from:"); - yield return new SmallLabelControl(TestCredentialProviderControlKeys.LabelComboboxSelectedItem, "This is the currently selected item: "); - yield return new CommandLinkControl(TestCredentialProviderControlKeys.CommandLinkComboboxAdd, "Add a random item to the combo box"); - yield return new CommandLinkControl(TestCredentialProviderControlKeys.CommandLinkComboboxRemove, "Remove the last item from the combo box"); - - yield return new TextboxControl(TestCredentialProviderControlKeys.Username, "Username"); - yield return new CommandLinkControl(TestCredentialProviderControlKeys.CommandLinkUsername, "Click this link to generate a random username"); - - yield return password; - yield return new SubmitButtonControl(TestCredentialProviderControlKeys.ButtonSubmit, "Submit", password); - } - } - - public override bool IsUsageScenarioSupported(UsageScenario cpus, CredUIWinFlags dwFlags) - { - switch (cpus) - { - case UsageScenario.Logon: - case UsageScenario.UnlockWorkstation: - case UsageScenario.CredUI: - case UsageScenario.ChangePassword: - return true; - - default: - return false; - } - } - - public override bool ShouldIncludeUserTile(CredentialProviderUser user) - { - return true; - } - - public override bool ShouldIncludeGenericTile() - { - return true; - } - - public override CredentialProviderCredential1Tile CreateGenericTile() - { - return new TestCredentialProviderTile(this); - } - - public override CredentialProviderCredential1Tile CreateUserTile(CredentialProviderUser user) - { - return new TestCredentialProviderTile(this, user); - } - } -} diff --git a/src/Lithnet.CredentialProvider.sln b/src/Lithnet.CredentialProvider.sln index a7aa86a..ecfaa85 100644 --- a/src/Lithnet.CredentialProvider.sln +++ b/src/Lithnet.CredentialProvider.sln @@ -5,13 +5,25 @@ VisualStudioVersion = 17.4.33205.214 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider", "Lithnet.CredentialProvider\Lithnet.CredentialProvider.csproj", "{9C07477C-ABB8-4DE5-B69C-3F8719D952C6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Samples", "Lithnet.CredentialProvider.Samples\Lithnet.CredentialProvider.Samples.csproj", "{967DF0EB-F85A-4006-A3BF-0C558753A9DC}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9E97082E-4E8D-4E8B-A320-40E2A1494C74}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net6.0.x64", "samples\Lithnet.CredentialProvider.Sample.net6.0.x64\Lithnet.CredentialProvider.Sample.net6.0.x64.csproj", "{163E16D0-9FF3-40D3-AE96-3F221C922AA3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net6.0.x86", "samples\Lithnet.CredentialProvider.Sample.net6.0.x86\Lithnet.CredentialProvider.Sample.net6.0.x86.csproj", "{7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net472.x64", "samples\Lithnet.CredentialProvider.Sample.net472.x64\Lithnet.CredentialProvider.Sample.net472.x64.csproj", "{F60AC10A-337C-46D9-A2DE-1ED48B4AB301}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Sample.net472.x86", "samples\Lithnet.CredentialProvider.Sample.net472.x86\Lithnet.CredentialProvider.Sample.net472.x86.csproj", "{57F2780E-58F1-4A7B-BCB4-A218733273EA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.TestApp.x64", "samples\Lithnet.CredentialProvider.TestApp.x64\Lithnet.CredentialProvider.TestApp.x64.csproj", "{5019CCEB-AD78-4688-8C11-89A85C4289CD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.TestApp.x86", "samples\Lithnet.CredentialProvider.TestApp.x86\Lithnet.CredentialProvider.TestApp.x86.csproj", "{F39C84F2-60C2-45EB-BC87-0ED095D41C96}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lithnet.CredentialProvider.RegistrationTool", "Lithnet.CredentialProvider.RegistrationTool\Lithnet.CredentialProvider.RegistrationTool.csproj", "{6A9A22A9-A2BB-4329-A0A5-2539E464CE5F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -22,10 +34,34 @@ Global {9C07477C-ABB8-4DE5-B69C-3F8719D952C6}.Debug|Any CPU.Build.0 = Debug|Any CPU {9C07477C-ABB8-4DE5-B69C-3F8719D952C6}.Release|Any CPU.ActiveCfg = Release|Any CPU {9C07477C-ABB8-4DE5-B69C-3F8719D952C6}.Release|Any CPU.Build.0 = Release|Any CPU - {967DF0EB-F85A-4006-A3BF-0C558753A9DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {967DF0EB-F85A-4006-A3BF-0C558753A9DC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {967DF0EB-F85A-4006-A3BF-0C558753A9DC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {967DF0EB-F85A-4006-A3BF-0C558753A9DC}.Release|Any CPU.Build.0 = Release|Any CPU + {163E16D0-9FF3-40D3-AE96-3F221C922AA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {163E16D0-9FF3-40D3-AE96-3F221C922AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {163E16D0-9FF3-40D3-AE96-3F221C922AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {163E16D0-9FF3-40D3-AE96-3F221C922AA3}.Release|Any CPU.Build.0 = Release|Any CPU + {7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A8A10F9-7D79-4755-BBDD-77C80FDC58ED}.Release|Any CPU.Build.0 = Release|Any CPU + {F60AC10A-337C-46D9-A2DE-1ED48B4AB301}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F60AC10A-337C-46D9-A2DE-1ED48B4AB301}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F60AC10A-337C-46D9-A2DE-1ED48B4AB301}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F60AC10A-337C-46D9-A2DE-1ED48B4AB301}.Release|Any CPU.Build.0 = Release|Any CPU + {57F2780E-58F1-4A7B-BCB4-A218733273EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57F2780E-58F1-4A7B-BCB4-A218733273EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57F2780E-58F1-4A7B-BCB4-A218733273EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57F2780E-58F1-4A7B-BCB4-A218733273EA}.Release|Any CPU.Build.0 = Release|Any CPU + {5019CCEB-AD78-4688-8C11-89A85C4289CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5019CCEB-AD78-4688-8C11-89A85C4289CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5019CCEB-AD78-4688-8C11-89A85C4289CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5019CCEB-AD78-4688-8C11-89A85C4289CD}.Release|Any CPU.Build.0 = Release|Any CPU + {F39C84F2-60C2-45EB-BC87-0ED095D41C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F39C84F2-60C2-45EB-BC87-0ED095D41C96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F39C84F2-60C2-45EB-BC87-0ED095D41C96}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F39C84F2-60C2-45EB-BC87-0ED095D41C96}.Release|Any CPU.Build.0 = Release|Any CPU + {6A9A22A9-A2BB-4329-A0A5-2539E464CE5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A9A22A9-A2BB-4329-A0A5-2539E464CE5F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A9A22A9-A2BB-4329-A0A5-2539E464CE5F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A9A22A9-A2BB-4329-A0A5-2539E464CE5F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Lithnet.CredentialProvider/Controls/ControlBase.cs b/src/Lithnet.CredentialProvider/Controls/ControlBase.cs index 5887f87..2bd45f7 100644 --- a/src/Lithnet.CredentialProvider/Controls/ControlBase.cs +++ b/src/Lithnet.CredentialProvider/Controls/ControlBase.cs @@ -29,7 +29,7 @@ namespace Lithnet.CredentialProvider this.Key = source.Key; this.Type = source.Type; this.FieldTypeGuid = source.FieldTypeGuid; - + this.logger = source.logger; this.state = source.State; this.interactiveState = source.InteractiveState; this.label = source.Label; @@ -54,7 +54,6 @@ namespace Lithnet.CredentialProvider this.options = FieldOptions.None; this.Key = key; - this.logger = CredentialProviderBase.LoggerFactory.CreateLogger(this.GetType()); } internal void AssignCredential(ICredentialProviderCredential credential) @@ -76,6 +75,11 @@ namespace Lithnet.CredentialProvider internal ICredentialProviderCredential Credential { get; private set; } + internal void SetLogger(ILoggerFactory loggerFactory) + { + this.logger = loggerFactory.CreateLogger(this.GetType()); + } + /// /// Gets the unique ID associated with this control /// diff --git a/src/Lithnet.CredentialProvider/CredentialProviderBase.cs b/src/Lithnet.CredentialProvider/CredentialProviderBase.cs index 248d2a2..ee5f84a 100644 --- a/src/Lithnet.CredentialProvider/CredentialProviderBase.cs +++ b/src/Lithnet.CredentialProvider/CredentialProviderBase.cs @@ -13,11 +13,7 @@ namespace Lithnet.CredentialProvider /// public abstract partial class CredentialProviderBase { - internal static ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance; - private readonly ILogger logger; - private readonly ILoggerFactory loggerFactory; - private ICredentialProviderEvents CredentialProviderEvents; private ICredentialProviderUserArray credentialProviderUsers; @@ -25,6 +21,8 @@ namespace Lithnet.CredentialProvider private bool notifyOnTileCollectionChange; private List tiles; + internal ILoggerFactory LoggerFactory { get; } + /// /// Gets the GUID of the credential provider /// @@ -57,14 +55,10 @@ namespace Lithnet.CredentialProvider protected CredentialProviderBase() { - this.loggerFactory = this.GetLoggerFactory(); + this.LoggerFactory = this.GetLoggerFactory(); - if (CredentialProviderBase.LoggerFactory != this.loggerFactory) - { - CredentialProviderBase.LoggerFactory = this.loggerFactory; - } - this.logger = this.loggerFactory.CreateLogger(this.GetType()); + this.logger = this.LoggerFactory.CreateLogger(this.GetType()); var guidAttribute = (GuidAttribute)(this.GetType().GetCustomAttribute(typeof(GuidAttribute))); if (guidAttribute == null) @@ -79,7 +73,7 @@ namespace Lithnet.CredentialProvider /// Gets a logger factory. Override this method and provide an implementation of to enable credential provider logging /// /// An ILoggerFactory instance - public virtual ILoggerFactory GetLoggerFactory() { return NullLoggerFactory.Instance; } + protected virtual ILoggerFactory GetLoggerFactory() { return NullLoggerFactory.Instance; } /// /// Gets a value indicating if the credential provider supports the provided by LogonUI or CredUI @@ -175,6 +169,7 @@ namespace Lithnet.CredentialProvider foreach (var control in this.GetControls(this.UsageScenario)) { + control.SetLogger(this.LoggerFactory); this.Controls.Add(control); } @@ -205,7 +200,7 @@ namespace Lithnet.CredentialProvider this.logger.LogTrace($"Got supplied user {i}: with name {user.GetQualifiedUserName()} and SID {sid}"); - var credentialProviderUser = new CredentialProviderUser(user); + var credentialProviderUser = new CredentialProviderUser(this.LoggerFactory, user); users.Add(credentialProviderUser); if (this.ShouldIncludeUserTile(credentialProviderUser)) diff --git a/src/Lithnet.CredentialProvider/CredentialProviderCredential1Tile.cs b/src/Lithnet.CredentialProvider/CredentialProviderCredential1Tile.cs index 281d7a2..0ebb64e 100644 --- a/src/Lithnet.CredentialProvider/CredentialProviderCredential1Tile.cs +++ b/src/Lithnet.CredentialProvider/CredentialProviderCredential1Tile.cs @@ -12,7 +12,6 @@ namespace Lithnet.CredentialProvider public abstract partial class CredentialProviderCredential1Tile { private protected readonly ILogger logger; - private protected ICredentialProviderCredentialEvents events; private protected ICredentialProviderCredentialEvents2 events2; @@ -21,7 +20,7 @@ namespace Lithnet.CredentialProvider protected CredentialProviderCredential1Tile(CredentialProviderBase credentialProvider) { this.CredentialProvider = credentialProvider; - this.logger = CredentialProviderBase.LoggerFactory.CreateLogger(this.GetType()); + this.logger = credentialProvider.LoggerFactory.CreateLogger(this.GetType()); } internal CredentialProviderBase CredentialProvider { get; } @@ -206,15 +205,15 @@ namespace Lithnet.CredentialProvider return response; } - CredentialSerializer.Logger = this.logger; + var serializer = new CredentialSerializer(this.CredentialProvider.LoggerFactory); if (credentials is CredentialResponseSecure s) { - response.SerializedCredentials = CredentialSerializer.GenerateCredentialSerialization(credentials.Domain, credentials.Username, s.Password, this.UsageScenario == UsageScenario.UnlockWorkstation, this.CredentialProvider.CredentialProviderId); + response.SerializedCredentials = serializer.GenerateCredentialSerialization(credentials.Domain, credentials.Username, s.Password, this.UsageScenario == UsageScenario.UnlockWorkstation, this.CredentialProvider.CredentialProviderId); } else if (credentials is CredentialResponseInsecure i) { - response.SerializedCredentials = CredentialSerializer.GenerateCredentialSerialization(credentials.Domain, credentials.Username, i.Password, this.UsageScenario == UsageScenario.UnlockWorkstation, this.CredentialProvider.CredentialProviderId); + response.SerializedCredentials = serializer.GenerateCredentialSerialization(credentials.Domain, credentials.Username, i.Password, this.UsageScenario == UsageScenario.UnlockWorkstation, this.CredentialProvider.CredentialProviderId); } else { diff --git a/src/Lithnet.CredentialProvider/CredentialProviderRegistrationServices.cs b/src/Lithnet.CredentialProvider/CredentialProviderRegistrationServices.cs new file mode 100644 index 0000000..ee6796a --- /dev/null +++ b/src/Lithnet.CredentialProvider/CredentialProviderRegistrationServices.cs @@ -0,0 +1,233 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.Win32; + +namespace Lithnet.CredentialProvider +{ + public static class CredentialProviderRegistrationServices + { + public static void DisableCredentialProvider() where T : CredentialProviderBase => DisableCredentialProvider(typeof(T)); + + public static void UnregisterCredentialProvider() where T : CredentialProviderBase => UnregisterCredentialProvider(typeof(T)); + + public static void RegisterCredentialProvider() where T : CredentialProviderBase => RegisterCredentialProvider(typeof(T)); + + public static void EnableCredentialProvider() where T : CredentialProviderBase => EnableCredentialProvider(typeof(T)); + + public static void UnregisterCredentialProvider(Type type) + { + DeleteCredentialProviderRegistration(type); + + if (IsFrameworkType(type)) + { + UnregisterFrameworkAssembly(type); + } + else + { + UnregisterNetCoreAssembly(type); + } + } + + public static void RegisterCredentialProvider(Type type) + { + CreateCredentialProviderRegistration(type); + + if (IsFrameworkType(type)) + { + RegisterFrameworkAssembly(type); + } + else + { + RegisterNetCoreAssembly(type); + } + } + + public static void DisableCredentialProvider(Type type) + { + var comGuid = GetComGuid(type); + DisableCredentialProvider(comGuid); + } + + public static void DisableCredentialProvider(Guid comGuid) + { + var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{comGuid:B}", true); + key?.SetValue("Disabled", 1); + } + + + public static void EnableCredentialProvider(Guid comGuid) + { + var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{comGuid:B}", true); + key?.SetValue("Disabled", 0); + } + + public static void EnableCredentialProvider(Type type) + { + var comGuid = GetComGuid(type); + EnableCredentialProvider(comGuid); + } + + private static void CreateCredentialProviderRegistration(Type t) + { + var comGuid = GetComGuid(t); + var typeName = GetTypeFullName(t); + + var key = Registry.LocalMachine.CreateSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{comGuid:B}", true); + key.SetValue(null, typeName); + } + + private static void DeleteCredentialProviderRegistration(Type t) + { + var comGuid = GetComGuid(t); + + Registry.LocalMachine.DeleteSubKeyTree($@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{comGuid:B}", false); + } + + private static void RegisterNetCoreAssembly(Type t) + { + var comGuid = GetComGuid(t); + var typeName = GetTypeFullName(t); + var progId = GetComProgId(t); + var assemblyLocation = GetTypeAssemblyLocation(t); + + var dir = Path.GetDirectoryName(assemblyLocation); + var assemblyFile = Path.GetFileNameWithoutExtension(assemblyLocation); + var comHostLocation = Path.Combine(dir, assemblyFile + ".comhost.dll"); + + var rootClsid = Registry.LocalMachine.CreateSubKey($@"Software\Classes\CLSID\{comGuid:B}", true); + rootClsid.SetValue(null, "CoreCLR COMHost Server"); + + var inprocKey = rootClsid.CreateSubKey("InprocServer32", true); + inprocKey.SetValue(null, comHostLocation); + inprocKey.SetValue("ThreadingModel", "Both"); + + var progIdKey = rootClsid.CreateSubKey("ProgId", true); + progIdKey.SetValue(null, progId); + + var progIdRoot = Registry.LocalMachine.CreateSubKey($@"Software\Classes\{progId}", true); + progIdRoot.SetValue(null, typeName); + var progIdSubKey = progIdRoot.CreateSubKey("CLSID"); + progIdSubKey.SetValue(null, comGuid.ToString("B")); + } + + private static void UnregisterNetCoreAssembly(Type t) + { + var comGuid = GetComGuid(t); + var progId = GetComProgId(t); + + Registry.LocalMachine.DeleteSubKeyTree($@"Software\Classes\CLSID\{comGuid:B}", false); + Registry.LocalMachine.DeleteSubKeyTree($@"Software\Classes\{progId}", false); + } + + private static void RegisterFrameworkAssembly(Type t) + { + var comGuid = GetComGuid(t); + var typeName = GetTypeFullName(t); + var progId = GetComProgId(t); + + var rootClsid = Registry.LocalMachine.CreateSubKey($@"Software\Classes\CLSID\{comGuid:B}", true); + rootClsid.SetValue(null, typeName); + + rootClsid.CreateSubKey("Implemented Categories"); + rootClsid.CreateSubKey(@"Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}"); + + var inprocKey = rootClsid.CreateSubKey("InprocServer32", true); + inprocKey.SetValue(null, "mscoree.dll"); + inprocKey.SetValue("ThreadingModel", "Both"); + inprocKey.SetValue("Class", typeName); + inprocKey.SetValue("RuntimeVersion", "v4.0.30319"); + inprocKey.SetValue("Assembly", GetTypeAssemblyName(t)); + inprocKey.SetValue("CodeBase", GetTypeAssemblyLocation(t)); + + var progIdKey = rootClsid.CreateSubKey("ProgId", true); + progIdKey.SetValue(null, progId); + + var progIdRoot = Registry.LocalMachine.CreateSubKey($@"Software\Classes\{progId}", true); + progIdRoot.SetValue(null, typeName); + var progIdSubKey = progIdRoot.CreateSubKey("CLSID"); + progIdSubKey.SetValue(null, comGuid.ToString("B")); + } + + private static void UnregisterFrameworkAssembly(Type t) + { + var comGuid = GetComGuid(t); + var progId = GetComProgId(t); + + Registry.LocalMachine.DeleteSubKeyTree($@"Software\Classes\CLSID\{comGuid:B}", false); + Registry.LocalMachine.DeleteSubKeyTree($@"Software\Classes\{progId}", false); + } + + private static string GetTypeAssemblyLocation(Type type) + { + return type.Assembly.Location; + } + + private static string GetTypeAssemblyName(Type type) + { + return type.Assembly.FullName; + } + + private static string GetTypeClassName(Type type) + { + return type.Name; + } + + private static string GetTypeFullName(Type type) + { + return type.FullName; + } + + private static Guid GetComGuid(Type type) + { + var typeId = type.GetCustomAttributeValue("GuidAttribute"); + + if (typeId == null) + { + throw new ArgumentException($"The type {type.Name} does not have the Guid attribute present"); + } + + return new Guid(typeId); + } + + private static string GetComProgId(Type type) + { + var typeId = type.GetCustomAttributeValue("ProgIdAttribute"); + + if (typeId == null) + { + throw new ArgumentException($"The type {type.Name} does not have the ProgId attribute present"); + } + + return typeId; + } + + private static bool IsFrameworkType(Type type) + { + var framework = type.Assembly.GetCustomAttributeValue("TargetFrameworkAttribute"); + return framework.StartsWith(".NETFramework"); + } + + private static string GetCustomAttributeValue(this Type type, string attributeName) + { + var cads = type.GetCustomAttributesData(); + foreach (CustomAttributeData cad in cads.Where(a => a.AttributeType.Name == attributeName)) + { + return cad.ConstructorArguments.FirstOrDefault().Value as string; + } + + return String.Empty; + } + + private static string GetCustomAttributeValue(this Assembly assembly, string attributeName) + { + foreach (CustomAttributeData cad in assembly.GetCustomAttributesData().Where(a => a.AttributeType.Name == attributeName)) + { + return cad.ConstructorArguments.FirstOrDefault().Value as string; + } + + return String.Empty; + } + } +} diff --git a/src/Lithnet.CredentialProvider/CredentialProviderUser.cs b/src/Lithnet.CredentialProvider/CredentialProviderUser.cs index eef8b72..d02a9e2 100644 --- a/src/Lithnet.CredentialProvider/CredentialProviderUser.cs +++ b/src/Lithnet.CredentialProvider/CredentialProviderUser.cs @@ -19,10 +19,10 @@ namespace Lithnet.CredentialProvider private string logonStatus; private string providerId; - internal CredentialProviderUser(ICredentialProviderUser user) + internal CredentialProviderUser(ILoggerFactory loggerFactory, ICredentialProviderUser user) { this.User = user; - this.logger = CredentialProviderBase.LoggerFactory.CreateLogger(); + this.logger = loggerFactory.CreateLogger(); } /// diff --git a/src/Lithnet.CredentialProvider/Interop/CredentialSerializer.cs b/src/Lithnet.CredentialProvider/Interop/CredentialSerializer.cs index 19237c6..613b8fa 100644 --- a/src/Lithnet.CredentialProvider/Interop/CredentialSerializer.cs +++ b/src/Lithnet.CredentialProvider/Interop/CredentialSerializer.cs @@ -3,20 +3,24 @@ using System.Runtime.InteropServices; using System.Security; using System.Text; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Lithnet.CredentialProvider.Interop { - internal static class CredentialSerializer + internal class CredentialSerializer { - internal static ILogger Logger { get; set; } = NullLogger.Instance; + private readonly ILogger logger; - public static CredentialSerialization GenerateCredentialSerialization(string domain, string username, SecureString password, bool isWorkstationUnlock, Guid providerId) + public CredentialSerializer(ILoggerFactory loggerFactory) + { + this.logger = loggerFactory.CreateLogger(); + } + + public CredentialSerialization GenerateCredentialSerialization(string domain, string username, SecureString password, bool isWorkstationUnlock, Guid providerId) { var authPackage = PInvoke.LookupAuthenticationPackage(CredProviderConstants.NEGOSSP_NAME_A); - var pData = SerializeKerbLogon(domain, username, password, isWorkstationUnlock ? KerbLogonSubmitType.WorkstationUnlockLogon : KerbLogonSubmitType.InteractiveLogon, out int size); + var pData = this.SerializeKerbLogon(domain, username, password, isWorkstationUnlock ? KerbLogonSubmitType.WorkstationUnlockLogon : KerbLogonSubmitType.InteractiveLogon, out int size); - Logger.LogTrace($"0x{pData.ToString("X16")} - Serializer: Password got packed into "); + this.logger.LogTrace($"0x{pData.ToString("X16")} - Serializer: Password got packed into "); return new CredentialSerialization() { @@ -27,12 +31,12 @@ namespace Lithnet.CredentialProvider.Interop }; } - public static CredentialSerialization GenerateCredentialSerialization(string domain, string username, string password, bool isWorkstationUnlock, Guid providerId) + public CredentialSerialization GenerateCredentialSerialization(string domain, string username, string password, bool isWorkstationUnlock, Guid providerId) { var authPackage = PInvoke.LookupAuthenticationPackage(CredProviderConstants.NEGOSSP_NAME_A); - var pData = SerializeKerbLogon(domain, username, password, isWorkstationUnlock ? KerbLogonSubmitType.WorkstationUnlockLogon : KerbLogonSubmitType.InteractiveLogon, out int size); + var pData = this.SerializeKerbLogon(domain, username, password, isWorkstationUnlock ? KerbLogonSubmitType.WorkstationUnlockLogon : KerbLogonSubmitType.InteractiveLogon, out int size); - Logger.LogTrace($"0x{pData.ToString("X16")}: Password got packed"); + this.logger.LogTrace($"0x{pData.ToString("X16")}: Password got packed"); return new CredentialSerialization() { @@ -43,7 +47,7 @@ namespace Lithnet.CredentialProvider.Interop }; } - private static unsafe IntPtr SerializeKerbLogon(string domain, string username, string password, KerbLogonSubmitType type, out int size) + private unsafe IntPtr SerializeKerbLogon(string domain, string username, string password, KerbLogonSubmitType type, out int size) { size = sizeof(KerberosInteractiveUnlockLogon) + Encoding.Unicode.GetMaxByteCount(domain.Length) + @@ -86,7 +90,7 @@ namespace Lithnet.CredentialProvider.Interop return pBuffer; } - private static unsafe IntPtr SerializeKerbLogon(string domain, string username, SecureString password, KerbLogonSubmitType type, out int size) + private unsafe IntPtr SerializeKerbLogon(string domain, string username, SecureString password, KerbLogonSubmitType type, out int size) { size = sizeof(KerberosInteractiveUnlockLogon) + Encoding.Unicode.GetMaxByteCount(domain.Length) + @@ -127,24 +131,24 @@ namespace Lithnet.CredentialProvider.Interop try { buff = Marshal.SecureStringToCoTaskMemUnicode(password); - Logger.LogTrace($"0x{buff.ToString("X16")} - Serializer: Unprotected password"); + this.logger.LogTrace($"0x{buff.ToString("X16")} - Serializer: Unprotected password"); IntPtr targetPositionToCopyTo = (IntPtr)(buffer + logon->Password.Buffer.ToInt64()); Buffer.MemoryCopy(buff.ToPointer(), targetPositionToCopyTo.ToPointer(), logon->Password.Length, password.Length * sizeof(char)); - Logger.LogTrace($"0x{targetPositionToCopyTo.ToString("X16")} - Serializer: Copied unprotected password into LSA string buffer"); + this.logger.LogTrace($"0x{targetPositionToCopyTo.ToString("X16")} - Serializer: Copied unprotected password into LSA string buffer"); } finally { if (buff != IntPtr.Zero) { Marshal.ZeroFreeCoTaskMemUnicode(buff); - Logger.LogTrace($"0x{buff.ToString("X16")} - Serializer: Freed Unprotected password"); + this.logger.LogTrace($"0x{buff.ToString("X16")} - Serializer: Freed Unprotected password"); } } - Logger.LogTrace($"0x{((IntPtr)buffer).ToString("X16")} - Serializer: Put password"); + this.logger.LogTrace($"0x{((IntPtr)buffer).ToString("X16")} - Serializer: Put password"); return pBuffer; } diff --git a/src/Lithnet.CredentialProvider/Interop/Structs/FieldDescriptor.cs b/src/Lithnet.CredentialProvider/Interop/Structs/FieldDescriptor.cs index 0380e34..6986bbf 100644 --- a/src/Lithnet.CredentialProvider/Interop/Structs/FieldDescriptor.cs +++ b/src/Lithnet.CredentialProvider/Interop/Structs/FieldDescriptor.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; namespace Lithnet.CredentialProvider.Interop { - [StructLayout(LayoutKind.Sequential, Pack = 8)] + [StructLayout(LayoutKind.Sequential, Pack = 4)] internal struct FieldDescriptor { public uint FieldID; diff --git a/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj b/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj index 0f93076..69d48b9 100644 --- a/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj +++ b/src/Lithnet.CredentialProvider/Lithnet.CredentialProvider.csproj @@ -6,6 +6,9 @@ true AnyCPU true + true + true + 9 @@ -14,7 +17,7 @@ Copyright 2023 Lithnet Pty Ltd Lithnet Windows Credential Provider 1.0.0 - beta.3 + beta.26 Lithnet true true @@ -23,10 +26,12 @@ MIT https://github.com/lithnet/windows-credential-provider https://github.com/lithnet/windows-credential-provider + D:\dev\nuget\packages + diff --git a/src/Lithnet.CredentialProvider/ModuleInit/AssemblyResolver.cs b/src/Lithnet.CredentialProvider/ModuleInit/AssemblyResolver.cs new file mode 100644 index 0000000..8734bff --- /dev/null +++ b/src/Lithnet.CredentialProvider/ModuleInit/AssemblyResolver.cs @@ -0,0 +1,37 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Lithnet.CredentialProvider.ModuleInit +{ + internal static class AssemblyResolver + { + private static string basePath; + + [ModuleInitializer] + public static void AttachResolver() + { + Trace.WriteLine("Attaching assembly resolver"); + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + basePath = Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + } + + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + var name = new AssemblyName(args.Name); + string assyPath = Path.Combine(basePath, $"{name.Name}.dll"); + Trace.WriteLine($"Request for {args.Name}"); + + if (File.Exists(assyPath)) + { + Trace.WriteLine($"Found at {assyPath}"); + return Assembly.Load(assyPath); + } + + Trace.WriteLine($"Assembly {args.Name} not found"); + return null; + } + } +} \ No newline at end of file diff --git a/src/Lithnet.CredentialProvider/ModuleInit/ModuleInitializerAttribute.cs b/src/Lithnet.CredentialProvider/ModuleInit/ModuleInitializerAttribute.cs new file mode 100644 index 0000000..f3af805 --- /dev/null +++ b/src/Lithnet.CredentialProvider/ModuleInit/ModuleInitializerAttribute.cs @@ -0,0 +1,7 @@ +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(System.AttributeTargets.Method, Inherited = false)] + internal sealed class ModuleInitializerAttribute : Attribute + { + } +} diff --git a/src/Lithnet.CredentialProvider.Samples/TestCredentialProviderControlKeys.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/ControlKeys.cs similarity index 95% rename from src/Lithnet.CredentialProvider.Samples/TestCredentialProviderControlKeys.cs rename to src/samples/Lithnet.CredentialProvider.Sample.net472.x64/ControlKeys.cs index aebc473..30b1cca 100644 --- a/src/Lithnet.CredentialProvider.Samples/TestCredentialProviderControlKeys.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/ControlKeys.cs @@ -1,6 +1,6 @@ namespace Lithnet.CredentialProvider.Samples { - internal static class TestCredentialProviderControlKeys + internal static class ControlKeys { internal const string LabelCredentialProvider = "LabelCredentialProvider"; internal const string ImageCredentialProvider = "ImageCredentialProvider"; diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md new file mode 100644 index 0000000..dd4a3e1 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Installing the sample.md @@ -0,0 +1,50 @@ +# Using the sample credential provider + +## Installing the sample + +In order to install and run the sample app, you have to register the COM component + +Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands + +``` +SETLOCAL +SET CLSID={4EB911FA-CA18-40EA-86DF-19AFF5D1DA58} +SET BinaryPath=D:\dev\git\lithnet\windows-credential-provider\src\samples\Lithnet.CredentialProvider.Sample.net472.x64\bin\Debug\net472\Lithnet.CredentialProvider.Sample.net472.x64.dll +REM %windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Sample.net472.x64.dll" + +REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Samples.TestCredentialProvider" +REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" +REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /ve /t /REG_SZ /f /d "mscoree.dll" +REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "ThreadingModel" /t REG_SZ /f /d "Both" +REM REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "Class" /t REG_SZ /f /d "Both" +REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "RuntimeVersion" /t REG_SZ /f /d "v4.0.30319" +REG ADD "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\InprocServer32" /v "CodeBase" /t REG_SZ /f /d "%BinaryPath%" + +REG ADD "HKLM\SOFTWARE\Classes\Lithnet.CredentialProvider.Sample.net472.x64" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Samples.TestCredentialProvider" +REG ADD "HKLM\SOFTWARE\Classes\Lithnet.CredentialProvider.Sample.net472.x64\CLSID" /ve /t REG_SZ /f /d "%CLSID%" + + +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\%CLSID%" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net472.x64" +``` + +## Disable the sample + +To disable the credential provider, run the following command. + +``` +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4eb911fa-ca18-40ea-86df-19aff5d1da58"}" /v "Disabled" /t REG_DWORD /f /d 1 +``` + +## Re-enable the sample +To enable the provider again after disabling it, run the following command. +``` +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4eb911fa-ca18-40ea-86df-19aff5d1da58"}" /v "Disabled" /t REG_DWORD /f /d 0 +``` + +## Uninstalling the sample +To remove the credential provider, run the following command. + +``` +%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Sample.net472.x64.dll" +REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4eb911fa-ca18-40ea-86df-19aff5d1da58"}" /f +``` \ No newline at end of file diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/InternalLogger.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/InternalLogger.cs new file mode 100644 index 0000000..427ece4 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/InternalLogger.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using NLog; +using NLog.Extensions.Logging; + +namespace Lithnet.CredentialProvider.Samples +{ + internal static class InternalLogger + { + internal static ILoggerFactory LoggerFactory { get; } + + static InternalLogger() + { + /* + This sample uses NLog to capture trace events from the provider, but you can use any + logging system compatible with Microsoft.Extensions.Logging; + */ + + var config = new NLog.Config.LoggingConfiguration(); + var logconsole = new NLog.Targets.ConsoleTarget("logconsole"); + + /* + + Add file based logging if required + + */ + + var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "c:\\file.txt" }; + config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logfile); + + config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logconsole); + + LogManager.Configuration = config; + InternalLogger.LoggerFactory = new NLogLoggerFactory(new NLogLoggerProvider(new NLogProviderOptions() { ReplaceLoggerFactory = true }, LogManager.LogFactory)); + } + } +} diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj new file mode 100644 index 0000000..7dbe35d --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Lithnet.CredentialProvider.Sample.net472.x64.csproj @@ -0,0 +1,14 @@ + + + + net472 + false + x64 + + + + + + + + diff --git a/src/Lithnet.CredentialProvider.Samples/Resources.Designer.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources.Designer.cs similarity index 97% rename from src/Lithnet.CredentialProvider.Samples/Resources.Designer.cs rename to src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources.Designer.cs index de0a7f4..326a150 100644 --- a/src/Lithnet.CredentialProvider.Samples/Resources.Designer.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources.Designer.cs @@ -39,7 +39,7 @@ namespace Lithnet.CredentialProvider.Samples { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lithnet.CredentialProvider.Samples.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lithnet.CredentialProvider.Sample.Net472.x64.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; diff --git a/src/Lithnet.CredentialProvider.Samples/Resources.resx b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources.resx similarity index 100% rename from src/Lithnet.CredentialProvider.Samples/Resources.resx rename to src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources.resx diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/Resources/TileIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..a3cb8a5238a3e0c1510dde4c8f7096b03ddb97b7 GIT binary patch literal 1662 zcmV-^27&pBP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&1`|m{K~!i%?V8VP z;#w5P^B!+C*#(WoRm9>(9g1LZ+l95{KagSpM3k<5g6t- z0>k`9V3^<7zG0!*`N{s(Nh-tpzlg;JTFw~BgSZ@Fong20tvVX8_J)9AMJ*zYZ?2zP zl58C5d4(SVhSTwV?YTAKT@RV_m|8y4OO{nYB%VCZ$-J`#B1*Va+=??DhJmlrIGkp^ z);PU>IK6J%bWJ+sE$tdy{NePjKg3@6U<+gS7vCc4UK}^=TBG{Hc1UKc+Gt@zEuSRQ z)Ba6YKFEe6)Bfdc58^YLOC3X(o$3r)@H`)*cJqyPxiA_1m}*vJy52(0j4JryB%EF`PA89#BW7ErX3Wd;z<>1RS0g7(Z{7r+~nRoG4o=gbjJ zs9)J>qp1}a&Rb**y<}MhMB>`nQ7kH%Pb`koyL3A#x{E& zS70~Z1jltSG7nf}QpgP`5aQ~gTO0!7tY}ti{ja!~%K3txw{xI7;tdfD46PIoLn!9? zE_f6+FY&n!qjYv?16;4u?c>-1{Ad_cU?C0xXO2yJ7k8cdZ0&*^=tC;=o)cIJV3lKj zn*#=@(`L?xcDvT8^Y*%!)!T;iVQ^mIJu4c4#W(~4&BJd5hWU-aFuxHP<~IVv{KkrJ z9<~T$6<`Rjd8yQ$Q)z%peth$+lgdKui1RK_s6RE30m>m+C6@G@UTufS40WuUK2+m^^65JZn$) zgU5bbk|NodtJ5qo>s=(Nvh zKABz2oJdw@aZ3{;N&w;%NA6mT)r_H^Wo0fy@O>^UGYr-xOcmG3l$@5%YFgwDqz`k&r@^BWC~8x(t<{7BPyZ8X5+#o>5*3TK}Z`~qaBL@ch{UsgVwU# zRXq4|MZ-XRck{6=7y-v|uz8-ZbdBQVTw z{E|11IR&QKsW{#t7xWx2_yrzYP+;)Idd8Z1&Z=zPF#g<7XjuQJz7#7FThHNr&+x`D zC}~BVgSzJ^AJBXH_-0l!>=?gTVG0cY+ULujLB%o{ zuN!vpx{98&DPD!*s8SolCS6FC3s@tzYhv*yr|le5@RJ0CeJQbup0hFDY z#wGs2W9O!6FH?)3yKRd=%<&t6VSXbp%x?sS`HjFZe<4Z#1GHQgUp8sh-2eap07*qo IM6N<$f@VDs>;M1& literal 0 HcmV?d00001 diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs new file mode 100644 index 0000000..ce9651b --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderNet472x64.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + +namespace Lithnet.CredentialProvider.Samples +{ + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + [ProgId("Lithnet.CredentialProvider.Sample.net472.x64")] + [Guid("4eb911fa-ca18-40ea-86df-19aff5d1da58")] + public class TestCredentialProviderNet472x64 : CredentialProviderBase + { + public static void SelfRegister() + { + CredentialProviderRegistrationServices.RegisterCredentialProvider(); + } + + protected override ILoggerFactory GetLoggerFactory() + { + return InternalLogger.LoggerFactory; + } + + public override IEnumerable GetControls(UsageScenario cpus) + { + var password = new SecurePasswordTextboxControl(ControlKeys.Password, "Password"); + + if (cpus == UsageScenario.ChangePassword) + { + var confirmPassword = new SecurePasswordTextboxControl(ControlKeys.ConfirmPassword, "Confirm password"); + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return password; + yield return confirmPassword; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", confirmPassword); + } + else + { + yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); + yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", Resources.TileIcon); + yield return new CredentialProviderLogoControl(ControlKeys.ImageUserTile, "User tile image", Resources.TileIcon); + + yield return new LargeLabelControl(ControlKeys.LabelLargeHeading, "The is our showcase credential provider"); + yield return new SmallLabelControl(ControlKeys.LabelSmallHeading, "Let's see what we can do"); + + yield return new CheckboxControl(ControlKeys.Checkbox, "A checkbox"); + yield return new SmallLabelControl(ControlKeys.LabelCheckboxValue, "The check box is currently unchecked"); + yield return new CommandLinkControl(ControlKeys.CommandLinkCheckboxValue, "Click this link to change the check box value in code behind"); + + yield return new ComboboxControl(ControlKeys.Combobox, "Items to choose from:"); + yield return new SmallLabelControl(ControlKeys.LabelComboboxSelectedItem, "This is the currently selected item: "); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxAdd, "Add a random item to the combo box"); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxRemove, "Remove the last item from the combo box"); + + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return new CommandLinkControl(ControlKeys.CommandLinkUsername, "Click this link to generate a random username"); + + yield return password; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", password); + } + } + + public override bool IsUsageScenarioSupported(UsageScenario cpus, CredUIWinFlags dwFlags) + { + switch (cpus) + { + case UsageScenario.Logon: + case UsageScenario.UnlockWorkstation: + case UsageScenario.CredUI: + case UsageScenario.ChangePassword: + return true; + + default: + return false; + } + } + + public override bool ShouldIncludeUserTile(CredentialProviderUser user) + { + return true; + } + + public override bool ShouldIncludeGenericTile() + { + return true; + } + + public override CredentialProviderCredential1Tile CreateGenericTile() + { + return new TestCredentialProviderTile(this); + } + + public override CredentialProviderCredential1Tile CreateUserTile(CredentialProviderUser user) + { + return new TestCredentialProviderTile(this, user); + } + } +} diff --git a/src/Lithnet.CredentialProvider.Samples/TestCredentialProviderTile.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderTile.cs similarity index 84% rename from src/Lithnet.CredentialProvider.Samples/TestCredentialProviderTile.cs rename to src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderTile.cs index 6449553..b060d58 100644 --- a/src/Lithnet.CredentialProvider.Samples/TestCredentialProviderTile.cs +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x64/TestCredentialProviderTile.cs @@ -15,7 +15,7 @@ namespace Lithnet.CredentialProvider.Samples private SmallLabelControl CheckboxStateControl; private SmallLabelControl ComboboxStateControl; - private ILogger logger = Program.LoggerFactory.CreateLogger(); + private ILogger logger = InternalLogger.LoggerFactory.CreateLogger(); public TestCredentialProviderTile(CredentialProviderBase credentialProvider) : base(credentialProvider) { @@ -65,29 +65,29 @@ namespace Lithnet.CredentialProvider.Samples { if (UsageScenario == UsageScenario.ChangePassword) { - PasswordConfirmControl = Controls.GetControl(TestCredentialProviderControlKeys.ConfirmPassword); + PasswordConfirmControl = Controls.GetControl(ControlKeys.ConfirmPassword); } - PasswordControl = Controls.GetControl(TestCredentialProviderControlKeys.Password); - CheckboxControl = Controls.GetControl(TestCredentialProviderControlKeys.Checkbox); - CheckboxStateControl = Controls.GetControl(TestCredentialProviderControlKeys.LabelCheckboxValue); + PasswordControl = Controls.GetControl(ControlKeys.Password); + CheckboxControl = Controls.GetControl(ControlKeys.Checkbox); + CheckboxStateControl = Controls.GetControl(ControlKeys.LabelCheckboxValue); - Controls.GetControl(TestCredentialProviderControlKeys.CommandLinkCheckboxValue).OnClick = () => + Controls.GetControl(ControlKeys.CommandLinkCheckboxValue).OnClick = () => { CheckboxControl.IsChecked = !CheckboxControl.IsChecked; }; CheckboxControl.PropertyChanged += CheckboxControl_PropertyChanged; - UsernameControl = Controls.GetControl(TestCredentialProviderControlKeys.Username); - Controls.GetControl(TestCredentialProviderControlKeys.CommandLinkUsername).OnClick = () => + UsernameControl = Controls.GetControl(ControlKeys.Username); + Controls.GetControl(ControlKeys.CommandLinkUsername).OnClick = () => { Username = Guid.NewGuid().ToString(); }; - ComboboxStateControl = Controls.GetControl(TestCredentialProviderControlKeys.LabelComboboxSelectedItem); + ComboboxStateControl = Controls.GetControl(ControlKeys.LabelComboboxSelectedItem); - ComboboxControl = Controls.GetControl(TestCredentialProviderControlKeys.Combobox); + ComboboxControl = Controls.GetControl(ControlKeys.Combobox); ComboboxControl.PropertyChanged += ComboboxControl_PropertyChanged; ComboboxControl.ComboBoxItems.Add("Item 1"); ComboboxControl.ComboBoxItems.Add("Item 2"); @@ -95,12 +95,12 @@ namespace Lithnet.CredentialProvider.Samples ComboboxControl.SelectedItemIndex = 0; int count = 3; - Controls.GetControl(TestCredentialProviderControlKeys.CommandLinkComboboxAdd).OnClick = () => + Controls.GetControl(ControlKeys.CommandLinkComboboxAdd).OnClick = () => { ComboboxControl.ComboBoxItems.Add($"Item {++count}"); }; - Controls.GetControl(TestCredentialProviderControlKeys.CommandLinkComboboxRemove).OnClick = () => + Controls.GetControl(ControlKeys.CommandLinkComboboxRemove).OnClick = () => { if (ComboboxControl.ComboBoxItems.Count > 0) { @@ -140,7 +140,7 @@ namespace Lithnet.CredentialProvider.Samples domain = Environment.MachineName; } - var spassword = Controls.GetControl(TestCredentialProviderControlKeys.Password).Password; + var spassword = Controls.GetControl(ControlKeys.Password).Password; if (!IsChecked) { diff --git a/src/Lithnet.CredentialProvider.Samples/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Installing the sample.md similarity index 61% rename from src/Lithnet.CredentialProvider.Samples/Installing the sample.md rename to src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Installing the sample.md index 0afea96..ae0b944 100644 --- a/src/Lithnet.CredentialProvider.Samples/Installing the sample.md +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Installing the sample.md @@ -7,10 +7,10 @@ In order to install and run the sample app, you have to register the COM compone Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands ``` -%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Samples.exe" +%windir%\Microsoft.NET\Framework\v4.0.30319\regasm /codebase "Lithnet.CredentialProvider.Sample.net472.x86.dll" -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{1A3993B6-EB2B-44BB-A788-7AB1711DFF16}" /f -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{1A3993B6-EB2B-44BB-A788-7AB1711DFF16}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Samples.TestCredentialProvider" +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /f +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net472.x86" ``` ## Disable the sample @@ -18,19 +18,19 @@ REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credentia To disable the credential provider, run the following command. ``` -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{1A3993B6-EB2B-44BB-A788-7AB1711DFF16}" /v "Disabled" /t REG_DWORD /f /d 1 +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /v "Disabled" /t REG_DWORD /f /d 1 ``` ## Re-enable the sample To enable the provider again after disabling it, run the following command. ``` -REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{1A3993B6-EB2B-44BB-A788-7AB1711DFF16}" /v "Disabled" /t REG_DWORD /f /d 0 +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /v "Disabled" /t REG_DWORD /f /d 0 ``` ## Uninstalling the sample To remove the credential provider, run the following command. ``` -%windir%\Microsoft.NET\Framework64\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Samples.exe" -REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{1A3993B6-EB2B-44BB-A788-7AB1711DFF16}" /f +%windir%\Microsoft.NET\Framework\v4.0.30319\regasm /u "Lithnet.CredentialProvider.Sample.net472.x86.dll" +REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{c9055c88-03f9-4a12-8e33-1ee75826a4a6}" /f ``` \ No newline at end of file diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj new file mode 100644 index 0000000..5594a90 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Lithnet.CredentialProvider.Sample.net472.x86.csproj @@ -0,0 +1,20 @@ + + + + net472 + false + x86 + + + + + + + + + + + + + + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.Designer.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.Designer.cs new file mode 100644 index 0000000..326a150 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Lithnet.CredentialProvider.Samples { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Lithnet.CredentialProvider.Sample.Net472.x64.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap TileIcon { + get { + object obj = ResourceManager.GetObject("TileIcon", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.resx b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.resx new file mode 100644 index 0000000..e6bb7a2 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\TileIcon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/Resources/TileIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2cfefdc17d558c246aa63a82bf757dfd72c103 GIT binary patch literal 1878 zcmV-c2dVgpP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&2J1;gK~!i%?V8PN z8%GqtH%VJaR}>?pRgij+DpwbKQ%4|0&{FV0maAJSJs2YwbS;MZV91{k$iYzeSagUm zhEm#_8d*?>LcvI&gvh!$QM89fjkK;323cJ?((G#GL|=t9`#X4NKfF6{-p9;) znoHk&`>Svfxy+6i4o5J%;Rt3o9Kr0y>;?0LZf!2_Y%WIG`TxY=1T99>3oBvKM^4&} z`rSgOMcQKmf~^I8f|}nwt_os$J)U&4J3zau9u$wOr|fi!m^3D3*W(#oD!>^ZL5VoxLX5r$4c{0jK0qqtSxq*%;L-XY`T< zr=3ULpl+F#pAe&og_T&(FO<6lyh!aCMkh!6Nv&P)6@}B7N0jiOK4el2;0|VVF#vxe zzRjJ2(Qc>73-H5KDs0Q#Gba2I={k9=)79<5_=#vbp3$WOeBt2sddM$m4~ZSg^Yn9q ze;)s(-7v%gPGj!=D~8gj-(hNzvN5n9t+Zb*fa`mtY-&LcOHrXy_0aCpTR7y^mBr^6vm!BdSdXJk;1)J zD(j;Xkz_oG$u$V&%R6gSMekno=mXSMA7ZT2s?Z61(h>~}n71FpCwlJzsesewK?9GH zTO>9NP47|!#}vnreXb8DSXZO2j8D%P7Cdu1I`qpS^hX5xS8wRkGcsfFj2MlTP z7V{hm}A66|JyWHg*Q%3Om!S5g4>X zz^QrI4M#A$;Rt3o9Kq~{BbeQI!<9#`2%{%p2+MhCs%xs!0PTv>i3Le^V}cfTDC8yB z(BB+c+(~)KEuMbjmyQRMxbVe`uwKj2q-KCoH6|wGR*U^{!72-Dz6nxxZ9$g`5FruC z4oR~3)%|PH+Fi(7gK1AkSDKlA9fESy#J{Wp$7`EFGZ9En_~iZMc#D4nQ{dTi`j1_; zRB6(LI=hDEfO)52p|p`Sc6Lxvr>L}OoJd<#tkVZXOvlhH+m$9U-A-=r7^MPEJI~Ke z8b&n5lP2aMSZuHciy2t=UEaZ}%eh1rGoY?seq{+OpQr~%gE|qf5tr^4f{cvnV>Anz zyb?%NffWvR!3v_%p1KS$=WIampcl-H9PV33N=*=av5=+G^kT;0mZ3y5KqLeqv|Xv6 zt1d$=fXAPRqqZR+4@rW7kp%aa~CaL*E=h1U~u{g0?8W^zzz=t@hu0dOkrsLZ& zk(D9XH4BCb2J;fSie)gRN|R&3X$F21y1GoZiX~JC_pg%*7#8lCYYqvfptPXO)o6S0 zmC$yjeA~k5k!so2Tr6s=Y4sLG-$t8fF?)wbWm_~!?u$a?28oNaE zCg{H+R9>T&b8w0|cEb_OZa9M34M#A$abX3U-2`pi2xHo)Vk%u!>bW6{ud$ht#;~?y z*a+kHW>US>bD(sAE7B=*CGpu;tT{zI?|%`WbK$!7_wVj2#iy5U$aAi%Z=XGV_V^4} z=vdE!75euNek?1+vRZrb^40L&AmcHS#vtM2s{r)$79`iFnkfM!r?enL7r1J^pLb|2 zW5zWVCeqleKmHBelzdm`-g~J$xD@(2CAkFk=bxVaDQNA>(#HWYEQfA*T%UXz_yD!{ z;5Sfd-@iq<=VNaCEk8}S2278Mg9oi!3G~%YmG1?+GV7i!1t*o)Arzho$OcGhw)wzWh8us=4(aTCUZ3(G{4x0x|t> z5#zfcek6x5JjfE6(A0xpI(l4r#}x>WTXT2|bbJ5hD|}{ZOr$YdwPfW%=4_+@jvHZ= z8m9zUdF1zdaiyNG^me`s?h~61PR+w^ID**?M=-nL2xd1N!R*Eb6-*HR10n}gI$b>v Q8UO$Q07*qoM6N<$g0k;>*8l(j literal 0 HcmV?d00001 diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs new file mode 100644 index 0000000..aa66a35 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net472.x86/TestCredentialProviderNet472x86.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + +namespace Lithnet.CredentialProvider.Samples +{ + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + [ProgId("Lithnet.CredentialProvider.Sample.net472.x86")] + [Guid("c9055c88-03f9-4a12-8e33-1ee75826a4a6")] + public class TestCredentialProviderNet472x86 : CredentialProviderBase + { + private static readonly ILogger logger = InternalLogger.LoggerFactory.CreateLogger(); + + public static void SelfRegister() + { + CredentialProviderRegistrationServices.RegisterCredentialProvider(); + } + + protected override ILoggerFactory GetLoggerFactory() + { + return InternalLogger.LoggerFactory; + } + + public override IEnumerable GetControls(UsageScenario cpus) + { + + var password = new SecurePasswordTextboxControl(ControlKeys.Password, "Password"); + + if (cpus == UsageScenario.ChangePassword) + { + var confirmPassword = new SecurePasswordTextboxControl(ControlKeys.ConfirmPassword, "Confirm password"); + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return password; + yield return confirmPassword; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", confirmPassword); + } + else + { + yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); + yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", Resources.TileIcon); + yield return new CredentialProviderLogoControl(ControlKeys.ImageUserTile, "User tile image", Resources.TileIcon); + + yield return new LargeLabelControl(ControlKeys.LabelLargeHeading, "The is our showcase credential provider"); + yield return new SmallLabelControl(ControlKeys.LabelSmallHeading, "Let's see what we can do"); + + yield return new CheckboxControl(ControlKeys.Checkbox, "A checkbox"); + yield return new SmallLabelControl(ControlKeys.LabelCheckboxValue, "The check box is currently unchecked"); + yield return new CommandLinkControl(ControlKeys.CommandLinkCheckboxValue, "Click this link to change the check box value in code behind"); + + yield return new ComboboxControl(ControlKeys.Combobox, "Items to choose from:"); + yield return new SmallLabelControl(ControlKeys.LabelComboboxSelectedItem, "This is the currently selected item: "); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxAdd, "Add a random item to the combo box"); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxRemove, "Remove the last item from the combo box"); + + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return new CommandLinkControl(ControlKeys.CommandLinkUsername, "Click this link to generate a random username"); + + yield return password; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", password); + } + } + + public override bool IsUsageScenarioSupported(UsageScenario cpus, CredUIWinFlags dwFlags) + { + switch (cpus) + { + case UsageScenario.Logon: + case UsageScenario.UnlockWorkstation: + case UsageScenario.CredUI: + case UsageScenario.ChangePassword: + return true; + + default: + return false; + } + } + + public override bool ShouldIncludeUserTile(CredentialProviderUser user) + { + return true; + } + + public override bool ShouldIncludeGenericTile() + { + return true; + } + + public override CredentialProviderCredential1Tile CreateGenericTile() + { + return new TestCredentialProviderTile(this); + } + + public override CredentialProviderCredential1Tile CreateUserTile(CredentialProviderUser user) + { + return new TestCredentialProviderTile(this, user); + } + } +} diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md new file mode 100644 index 0000000..0930d98 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Installing the sample.md @@ -0,0 +1,36 @@ +# Using the sample credential provider + +## Installing the sample + +In order to install and run the sample app, you have to register the COM component + +Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands + +``` +regsvr32 "Lithnet.CredentialProvider.Sample.net6.0.x64.comhost.dll" + +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /f +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net6.0.x64" +``` + +## Disable the sample + +To disable the credential provider, run the following command. + +``` +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /v "Disabled" /t REG_DWORD /f /d 1 +``` + +## Re-enable the sample +To enable the provider again after disabling it, run the following command. +``` +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /v "Disabled" /t REG_DWORD /f /d 0 +``` + +## Uninstalling the sample +To remove the credential provider, run the following command. + +``` +regsvr32 /u "Lithnet.CredentialProvider.Sample.net6.0.x64.comhost.dll" +REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{4cd12d80-9259-4f38-94dc-1828080ad9ff}" /f +``` \ No newline at end of file diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj new file mode 100644 index 0000000..0e60bff --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Lithnet.CredentialProvider.Sample.net6.0.x64.csproj @@ -0,0 +1,31 @@ + + + + net6.0-windows + false + x64 + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/Resources/TileIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..55414d4871358dd3e7fb1fec2e7c4b403a11e86a GIT binary patch literal 1510 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&1$#+EK~!i%?V8VP z;#w5P^B%LB>_lU6rC8i(1`!NyU8sZqffT!NUwB;<`X>x@A-wLp7#aE>$XMZZTNI>> z`A4wd^$MkkW;u!yBF}3Cbr^h1JCAKo1Trsyu zts=ya-oG7mA@!qDtCp41QbAabqO`m85DqzX4*$c3Tr0P0tSzk7ulAudZxKzMVgx~6 zOuA1xb4w)oF*CdQp)J5*dOJbPr?DsBepp$v?{u&C2gaymBpEkir2$iUx!s^HdVdr( z257B4;#{YXpc8neCprYM89xNuEzIkHRlsU?H^r?K5lIX~GnUWbnBq9H+cBJmt#z8p zc=j)bO&-^SPd?WYD;hWB5O5mCh5_P4sb^^LKp^EVS>`xlqoDO~X%|^XF)Zn!NGuQD zgDa35`@wM?jLa&DOd4`?8VG6W<3=0;u31s<_S6r!n2ME}QZaL2x?~MI7#PWBItHOw zVLw4v$h^enI*d~5kPkesGgWcy0CqHnDd3DlU?ay?vro5^zO{Bi4ycgIY|ja-1hC2x zzqx<`nv99_p*`+R`mDV#diTq~*%_Qy*q#xMfGZAxQ1b{Hp{4%l#ZX-qlcef#K?NVIMCwYif={;q)Zmkw2gft+PB?l@tSDYZ zfFGn*BWqX%h%yvld*XV!_l>?-Lp__UN0)}v+*tYq!j6P|_%!t(17oyJ28dUgbQgAc=CUBb|BdG($;O)e(=&mk+Fl>SCY zM^jXswiMO3;m0r<6KpK3nEp(ZnQcQj zfAB5cFyIQEp{WsVDynZH6mx<`Xjsq)4GS8fVL>A_ENC3+$zz8&L&P6!I5dKv;V>TC zAf?~w)wuC9~?t*dDGKiBiC}R8aP21_lTy;@Dyjn zdNGC*hCMPl!8OOI0w>V#6I?Z~mpGG2RU*P-IN|OtfbZmC(~=_M5o=LA#F@e|xXX|L z*mRGdba<*M`@++(M2YAVQ<7|^8pBDP*%yurG+kuL3LD9_Yb1HdYD>QUGZKtq`>@c! z_V53z1)45aEMZu(F)5#mII}OIm=iQY!-7U=SkMR!3mTze!9o!J0{}G41^6lpwg3PC M07*qoM6N<$g0hsmEC2ui literal 0 HcmV?d00001 diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs new file mode 100644 index 0000000..ff75725 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x64/TestCredentialProviderNet60x64.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + +namespace Lithnet.CredentialProvider.Samples +{ + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + [ProgId("Lithnet.CredentialProvider.Sample.net6.0.x64")] + [Guid("4cd12d80-9259-4f38-94dc-1828080ad9ff")] + public class TestCredentialProviderNet60x64 : CredentialProviderBase + { + private static readonly ILogger logger = InternalLogger.LoggerFactory.CreateLogger(); + + public static void SelfRegister() + { + CredentialProviderRegistrationServices.RegisterCredentialProvider(); + } + + protected override ILoggerFactory GetLoggerFactory() + { + return InternalLogger.LoggerFactory; + } + + public override IEnumerable GetControls(UsageScenario cpus) + { + + var password = new SecurePasswordTextboxControl(ControlKeys.Password, "Password"); + + if (cpus == UsageScenario.ChangePassword) + { + var confirmPassword = new SecurePasswordTextboxControl(ControlKeys.ConfirmPassword, "Confirm password"); + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return password; + yield return confirmPassword; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", confirmPassword); + } + else + { + yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); + + var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.net6.0.x64.Resources.TileIcon.png")); + + yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", image); + yield return new CredentialProviderLogoControl(ControlKeys.ImageUserTile, "User tile image", image); + + yield return new LargeLabelControl(ControlKeys.LabelLargeHeading, "The is our showcase credential provider"); + yield return new SmallLabelControl(ControlKeys.LabelSmallHeading, "Let's see what we can do"); + + yield return new CheckboxControl(ControlKeys.Checkbox, "A checkbox"); + yield return new SmallLabelControl(ControlKeys.LabelCheckboxValue, "The check box is currently unchecked"); + yield return new CommandLinkControl(ControlKeys.CommandLinkCheckboxValue, "Click this link to change the check box value in code behind"); + + yield return new ComboboxControl(ControlKeys.Combobox, "Items to choose from:"); + yield return new SmallLabelControl(ControlKeys.LabelComboboxSelectedItem, "This is the currently selected item: "); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxAdd, "Add a random item to the combo box"); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxRemove, "Remove the last item from the combo box"); + + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return new CommandLinkControl(ControlKeys.CommandLinkUsername, "Click this link to generate a random username"); + + yield return password; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", password); + } + } + + public override bool IsUsageScenarioSupported(UsageScenario cpus, CredUIWinFlags dwFlags) + { + switch (cpus) + { + case UsageScenario.Logon: + case UsageScenario.UnlockWorkstation: + case UsageScenario.CredUI: + case UsageScenario.ChangePassword: + return true; + + default: + return false; + } + } + + public override bool ShouldIncludeUserTile(CredentialProviderUser user) + { + return true; + } + + public override bool ShouldIncludeGenericTile() + { + return true; + } + + public override CredentialProviderCredential1Tile CreateGenericTile() + { + return new TestCredentialProviderTile(this); + } + + public override CredentialProviderCredential1Tile CreateUserTile(CredentialProviderUser user) + { + return new TestCredentialProviderTile(this, user); + } + } +} diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md new file mode 100644 index 0000000..5e2ab19 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Installing the sample.md @@ -0,0 +1,36 @@ +# Using the sample credential provider + +## Installing the sample + +In order to install and run the sample app, you have to register the COM component + +Build the EXE, and from an elevated command prompt, change to the bin folder, and run the following commands + +``` +regsvr32 "Lithnet.CredentialProvider.Sample.net6.0.x86.comhost.dll" + +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /f +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /ve /t REG_SZ /f /d "Lithnet.CredentialProvider.Sample.net6.0.x86" +``` + +## Disable the sample + +To disable the credential provider, run the following command. + +``` +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /v "Disabled" /t REG_DWORD /f /d 1 +``` + +## Re-enable the sample +To enable the provider again after disabling it, run the following command. +``` +REG ADD "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /v "Disabled" /t REG_DWORD /f /d 0 +``` + +## Uninstalling the sample +To remove the credential provider, run the following command. + +``` +regsvr32 /u "Lithnet.CredentialProvider.Sample.net6.0.x86.comhost.dll" +REG DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{90592593-f4d3-4f62-aa83-9cf1f7b590e0}" /f +``` \ No newline at end of file diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj new file mode 100644 index 0000000..b14f59e --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Lithnet.CredentialProvider.Sample.net6.0.x86.csproj @@ -0,0 +1,31 @@ + + + + net6.0-windows + false + x86 + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Resources/TileIcon.png b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/Resources/TileIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..970f61401efcd2f743e3cc4458d721eac6179b88 GIT binary patch literal 1708 zcmV;d22=ToP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGmbN~PnbOGLGA9w%&20=+gK~!i%?V7<$ zBUc>9$F(i&PAC#46G?hV8k7~Eldv7wCIN8xp+bNT6j+)I`UGvC#E~*a zA1C<6@L%dxBNv!G<~DB8lWJv!xkb)QLHuajn}r-qeM!zFBT7h~6dEOoc9)*QA&1W4 ze{`TE%+ zn(npjf<7u?S;38%n1xgxU!A2cx;HB61Jr8nW3JOm&~RdUVJ(eLWxV>DVaa2y@Tkvsi4lz#;}9?##)bjnB$^7- z;DJCIUuHAM3>$^!_9pEj>nMgLJ>~XHfcM}IJ?aay1$VA$jIP90FZg zQOo7k`}i^y6Uk_zJqM}{w$lj)Mj##XKqw~IA?OOpODxx6lp3e{!16v-6~_)>XFWIt z%y9@@m}4i0k2WjY#=8sVfC^KYwambq0N!%MZ(YCuRm#NquwTwsw%Ptw(Q+FFb7wHG zu$CT;Kvx_Bj+kRN9K-B}W0>7=46_@KVRmEiFORl3qZeiftA%L^+@;uh6BALZBA_4S%xpxr3J?`2z^+Kw*L~k;^%m5# zft1wHoz{(^Pf&Ib@h?;6I=xKJYUl+{|T$6&ny zexZ19h_wld5Z0i`fqHO!71fyDFhS|wL@KIZ4^=NYbrm?yFUP z(U*p2y07i3WkK*x1udPXiyS+PCh4XZFze{`v`7_4&3W~$@H9*i8c&MoG#*;e`7xVU zjpNt2L1pSH^kEphmoW4*R=uZAQ>Fs_=a3VPl>T~1OF=458;a`X=*KV`6+MQKIL%6U zHIm0deECiDkh&d0nk0`|@*eL}R4+jvhH1w{ndvozwcWm@8wT7VTWo7Y7Zue@;fOhQ z!!gWmIEL8`$1uC$7-lza=F7vp)Te7H&cM2jB{$$PRH;l$ecYaOvRy=T&}}Rkj(z?$ z+r1{F55A0!xUg{i)Y>p9*Te&@8_`J?p*R zIN-hJFr&(lA;^i^E?a5l4bcS|s`(fGL3JBTxF!#(zzKAHf>HBMrs9x1 zz@98wN`OnQ<~rB$4XWE%!Z#~+^vNxb(w*>l8YWT;SFM!}8tWSc?7h2F@EKIMv4p2# zz8KLbs;K953yfeVTV2y(JSW}85{~I!{^|=qs_^;Bk2TlKnCSA43`0);N5uRth#xCK zOb?1oUTDp&UmJQ}dC%qdlT#yj3Vi$Y^bD^V2yPyuza-R)75r_1O*SIs<5C~B+~EZH z^2lzs^9l#u#u7)&u^WzIcEd5uZa9Y7jazD%Ap8$4?g;O CI4?B- literal 0 HcmV?d00001 diff --git a/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs new file mode 100644 index 0000000..5645d6c --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.Sample.net6.0.x86/TestCredentialProviderNet60x86.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; + +namespace Lithnet.CredentialProvider.Samples +{ + [ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + [ProgId("Lithnet.CredentialProvider.Sample.net6.0.x86")] + [Guid("90592593-f4d3-4f62-aa83-9cf1f7b590e0")] + public class TestCredentialProviderNet60x86 : CredentialProviderBase + { + private static readonly ILogger logger = InternalLogger.LoggerFactory.CreateLogger(); + + public static void SelfRegister() + { + CredentialProviderRegistrationServices.RegisterCredentialProvider(); + } + + protected override ILoggerFactory GetLoggerFactory() + { + return InternalLogger.LoggerFactory; + } + + public override IEnumerable GetControls(UsageScenario cpus) + { + + var password = new SecurePasswordTextboxControl(ControlKeys.Password, "Password"); + + if (cpus == UsageScenario.ChangePassword) + { + var confirmPassword = new SecurePasswordTextboxControl(ControlKeys.ConfirmPassword, "Confirm password"); + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return password; + yield return confirmPassword; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", confirmPassword); + } + else + { + var image = new Bitmap(Assembly.GetExecutingAssembly().GetManifestResourceStream("Lithnet.CredentialProvider.Sample.net6.0.x86.Resources.TileIcon.png")); + + yield return new CredentialProviderLabelControl(ControlKeys.LabelCredentialProvider, "Login with showcase credential provider"); + yield return new CredentialProviderLogoControl(ControlKeys.ImageCredentialProvider, "Credential provider logo", image); + yield return new CredentialProviderLogoControl(ControlKeys.ImageUserTile, "User tile image", image); + + yield return new LargeLabelControl(ControlKeys.LabelLargeHeading, "The is our showcase credential provider"); + yield return new SmallLabelControl(ControlKeys.LabelSmallHeading, "Let's see what we can do"); + + yield return new CheckboxControl(ControlKeys.Checkbox, "A checkbox"); + yield return new SmallLabelControl(ControlKeys.LabelCheckboxValue, "The check box is currently unchecked"); + yield return new CommandLinkControl(ControlKeys.CommandLinkCheckboxValue, "Click this link to change the check box value in code behind"); + + yield return new ComboboxControl(ControlKeys.Combobox, "Items to choose from:"); + yield return new SmallLabelControl(ControlKeys.LabelComboboxSelectedItem, "This is the currently selected item: "); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxAdd, "Add a random item to the combo box"); + yield return new CommandLinkControl(ControlKeys.CommandLinkComboboxRemove, "Remove the last item from the combo box"); + + yield return new TextboxControl(ControlKeys.Username, "Username"); + yield return new CommandLinkControl(ControlKeys.CommandLinkUsername, "Click this link to generate a random username"); + + yield return password; + yield return new SubmitButtonControl(ControlKeys.ButtonSubmit, "Submit", password); + } + } + + public override bool IsUsageScenarioSupported(UsageScenario cpus, CredUIWinFlags dwFlags) + { + switch (cpus) + { + case UsageScenario.Logon: + case UsageScenario.UnlockWorkstation: + case UsageScenario.CredUI: + case UsageScenario.ChangePassword: + return true; + + default: + return false; + } + } + + public override bool ShouldIncludeUserTile(CredentialProviderUser user) + { + return true; + } + + public override bool ShouldIncludeGenericTile() + { + return true; + } + + public override CredentialProviderCredential1Tile CreateGenericTile() + { + return new TestCredentialProviderTile(this); + } + + public override CredentialProviderCredential1Tile CreateUserTile(CredentialProviderUser user) + { + return new TestCredentialProviderTile(this, user); + } + } +} diff --git a/src/Lithnet.CredentialProvider.Samples/CredUI.cs b/src/samples/Lithnet.CredentialProvider.TestApp.x64/CredUI.cs similarity index 100% rename from src/Lithnet.CredentialProvider.Samples/CredUI.cs rename to src/samples/Lithnet.CredentialProvider.TestApp.x64/CredUI.cs diff --git a/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj b/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj new file mode 100644 index 0000000..5fd9820 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.TestApp.x64/Lithnet.CredentialProvider.TestApp.x64.csproj @@ -0,0 +1,17 @@ + + + + Exe + net6.0-windows + false + x64 + + + + + + + + + + diff --git a/src/Lithnet.CredentialProvider.Samples/Program.cs b/src/samples/Lithnet.CredentialProvider.TestApp.x64/Program.cs similarity index 100% rename from src/Lithnet.CredentialProvider.Samples/Program.cs rename to src/samples/Lithnet.CredentialProvider.TestApp.x64/Program.cs diff --git a/src/samples/Lithnet.CredentialProvider.TestApp.x86/CredUI.cs b/src/samples/Lithnet.CredentialProvider.TestApp.x86/CredUI.cs new file mode 100644 index 0000000..60024b5 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.TestApp.x86/CredUI.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.InteropServices; + +namespace Lithnet.CredentialProvider.Samples +{ + public static class CredUI + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct CREDUI_INFO + { + public int cbSize; + public IntPtr hwndParent; + public string pszMessageText; + public string pszCaptionText; + public IntPtr hbmBanner; + } + + [DllImport("credui.dll", CharSet = CharSet.Auto)] + public static extern int CredUIPromptForWindowsCredentials( + ref CREDUI_INFO uiInfo, + int authError, + ref uint authPackage, + IntPtr InAuthBuffer, + uint InAuthBufferSize, + out IntPtr refOutAuthBuffer, + out uint refOutAuthBufferSize, + ref bool fSave, + uint flags + ); + + public static void Prompt(string caption, string message) + { + var uiInfo = new CREDUI_INFO() + { + pszCaptionText = caption, + pszMessageText = message + }; + + uiInfo.cbSize = Marshal.SizeOf(uiInfo); + + uint authPackage = 0; + + var save = false; + + CredUIPromptForWindowsCredentials( + ref uiInfo, + 0, + ref authPackage, + IntPtr.Zero, + 0, + out IntPtr outCredBuffer, + out uint outCredSize, + ref save, + 0 + ); + + Marshal.FreeCoTaskMem(outCredBuffer); + } + } +} diff --git a/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj b/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj new file mode 100644 index 0000000..84ddc34 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.TestApp.x86/Lithnet.CredentialProvider.TestApp.x86.csproj @@ -0,0 +1,16 @@ + + + + Exe + net6.0-windows + false + x86 + + + + + + + + + diff --git a/src/samples/Lithnet.CredentialProvider.TestApp.x86/Program.cs b/src/samples/Lithnet.CredentialProvider.TestApp.x86/Program.cs new file mode 100644 index 0000000..3bbdb12 --- /dev/null +++ b/src/samples/Lithnet.CredentialProvider.TestApp.x86/Program.cs @@ -0,0 +1,44 @@ +using System; +using Microsoft.Extensions.Logging; +using NLog; +using NLog.Extensions.Logging; + +namespace Lithnet.CredentialProvider.Samples +{ + internal static class Program + { + internal static ILoggerFactory LoggerFactory { get; } + + static Program() + { + /* + This sample uses NLog to capture trace events from the provider, but you can use any + logging system compatible with Microsoft.Extensions.Logging; + */ + + var config = new NLog.Config.LoggingConfiguration(); + var logconsole = new NLog.Targets.ConsoleTarget("logconsole"); + + /* + + Add file based logging if required + + */ + + // var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "c:\\file.txt" }; + //config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logfile); + + config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logconsole); + + LogManager.Configuration = config; + Program.LoggerFactory = new NLogLoggerFactory(new NLogLoggerProvider(new NLogProviderOptions() { ReplaceLoggerFactory = true }, LogManager.LogFactory)); + } + + static void Main(string[] args) + { + CredUI.Prompt("Login with Cred UI", "Select your favorite credential provider"); + Console.WriteLine("Done! Press any key to exit"); + Console.ReadKey(); + } + } +} \ No newline at end of file