diff --git a/src/Lithnet.CredentialProvider.Management/AssemblyContextResourceLoader.cs b/src/Lithnet.CredentialProvider.Management/AssemblyContextResourceLoader.cs new file mode 100644 index 0000000..63a7b8a --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/AssemblyContextResourceLoader.cs @@ -0,0 +1,24 @@ +using System.IO; +using System.Reflection; +using System.Runtime.Loader; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + internal static class AssemblyContextResourceLoader + { + private static readonly DependencyAssemblyLoadContext dependencyLoadContext = new DependencyAssemblyLoadContext(); + + public static Assembly LoadIntoAlc(Stream stream) + { + return dependencyLoadContext.LoadFromStream(stream); + } + + private class DependencyAssemblyLoadContext : AssemblyLoadContext + { + protected override Assembly Load(AssemblyName assemblyName) + { + return null; + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/CredentialProviderRegistrationData.cs b/src/Lithnet.CredentialProvider.Management/CredentialProviderRegistrationData.cs new file mode 100644 index 0000000..2025928 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/CredentialProviderRegistrationData.cs @@ -0,0 +1,23 @@ +using System; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + public class CredentialProviderRegistrationData + { + public bool IsComRegistered { get; set; } + + public bool IsCredentialProviderRegistered { get; set; } + + public bool IsCredentalProviderEnabled { get; set; } + + public string CredentialProviderName { get; set; } + + public Guid Clsid { get; set; } + + public string ProgId { get; set; } + + public string DllPath { get; set; } + + public DllType DllType { get; set; } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/CredentialProviderRegistrationServices.cs b/src/Lithnet.CredentialProvider.Management/CredentialProviderRegistrationServices.cs new file mode 100644 index 0000000..721ae95 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/CredentialProviderRegistrationServices.cs @@ -0,0 +1,443 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Runtime.InteropServices; +using Microsoft.Win32; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + public static class CredentialProviderRegistrationServices + { + public static bool IsManagedAssembly(string path) + { + using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (var peReader = new PEReader(fs)) + { + if (!peReader.HasMetadata) + { + return false; + } + + MetadataReader reader = peReader.GetMetadataReader(); + return reader.IsAssembly; + } + } + } + + public static IEnumerable GetCredentalProviders() + { + var cpKeys = Registry.LocalMachine.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers"); + foreach (var clsid in cpKeys.GetSubKeyNames()) + { + if (Guid.TryParse(clsid, out Guid result)) + { + yield return GetCredentialProvider(result); + } + } + } + + public static CredentialProviderRegistrationData GetCredentialProvider(Type type) + { + var comGuid = GetComGuid(type); + return GetCredentialProvider(comGuid); + } + + public static CredentialProviderRegistrationData GetCredentialProvider(string progId) + { + var clsid = GetClsidFromProgId(progId); + return GetCredentialProvider(clsid); + } + + public static CredentialProviderRegistrationData GetCredentialProvider(Guid clsid) + { + CredentialProviderRegistrationData data = new CredentialProviderRegistrationData(); + + data.Clsid = clsid; + + var clsidKey = Registry.ClassesRoot.OpenSubKey($@"CLSID\{clsid:B}"); + if (clsidKey != null) + { + var inprocKey = clsidKey.OpenSubKey("InprocServer32"); + data.IsComRegistered = inprocKey != null; + + if (data.IsComRegistered) + { + var coreLib = inprocKey.GetValue(string.Empty) as string; + + if (string.IsNullOrWhiteSpace(coreLib)) + { + data.IsComRegistered = false; + } + else + { + if (string.Equals(coreLib, "mscoree.dll", StringComparison.OrdinalIgnoreCase)) + { + data.DllType = DllType.NetFramework; + data.DllPath = inprocKey.GetValue("CodeBase") as string; + } + else if (coreLib.EndsWith(".comhost.dll", StringComparison.OrdinalIgnoreCase)) + { + data.DllType = DllType.NetCore; + var i = coreLib.IndexOf(".comhost.dll", StringComparison.OrdinalIgnoreCase); + data.DllPath = coreLib.Substring(0, i) + ".dll"; + } + else + { + data.DllType = DllType.Native; + data.DllPath = coreLib; + } + } + } + } + + data.ProgId = Registry.ClassesRoot.OpenSubKey($@"CLSID\{clsid:B}\ProgId")?.GetValue(string.Empty) as string; + + var cpkey = Registry.LocalMachine.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{clsid:B}"); + data.IsCredentialProviderRegistered = cpkey != null; + + if (data.IsCredentialProviderRegistered) + { + int? disabled = cpkey.GetValue("Disabled", 0) as int?; + data.IsCredentalProviderEnabled = disabled == null || disabled == 0; + data.CredentialProviderName = cpkey.GetValue(string.Empty) as string; + } + + return data; + } + + public static void UnregisterCredentialProvider(Type type, bool unregisterCom) + { + DeleteCredentialProviderRegistration(type); + + if (unregisterCom) + { + if (IsFrameworkType(type)) + { + UnregisterFrameworkAssembly(type); + } + else + { + UnregisterNetCoreAssembly(type); + } + } + } + + public static void UnregisterCredentialProvider(Guid clsid, bool unregisterCom) + { + DeleteCredentialProviderRegistration(clsid); + + if (unregisterCom) + { + UnregisterClass(clsid); + + } + } + + 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(string progId) + { + var clsid = GetClsidFromProgId(progId); + EnableCredentialProvider(clsid); + } + + public static void DisableCredentialProvider(string progId) + { + var clsid = GetClsidFromProgId(progId); + DisableCredentialProvider(clsid); + } + + 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); + DeleteCredentialProviderRegistration(comGuid); + } + + private static void DeleteCredentialProviderRegistration(Guid clsid) + { + Registry.LocalMachine.DeleteSubKeyTree($@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\{clsid: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); + + UnregisterClass(comGuid, progId); + } + + private static void UnregisterClass(Guid? clsid, string progId) + { + if (clsid != null) + { + Registry.LocalMachine.DeleteSubKeyTree($@"Software\Classes\CLSID\{clsid}", false); + } + + if (!string.IsNullOrWhiteSpace(progId)) + { + 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); + + UnregisterClass(comGuid, progId); + } + + public static void UnregisterClass(Guid clsid) + { + string progid = null; + + try + { + progid = GetProgIdFromClasid(clsid); + } + catch (NotFoundException) { } + + UnregisterClass(clsid, progid); + } + + public static void UnregisterClass(string progId) + { + Guid? clsid = null; + try + { + clsid = GetClsidFromProgId(progId); + } + catch (NotFoundException) { } + + UnregisterClass(clsid, progId); + } + + public static Guid GetClsidFromProgId(string progId) + { + var value = Registry.ClassesRoot.OpenSubKey($@"{progId}\CLSID")?.GetValue(string.Empty) as string; + + if (string.IsNullOrWhiteSpace(value)) + { + throw new ClsidNotFoundException($"The clsid for ProgId was not found {progId}"); + } + + return Guid.Parse(value); + } + + public static string GetProgIdFromClasid(Guid clsid) + { + var value = Registry.ClassesRoot.OpenSubKey($@"CLSID\{clsid:B}\ProgId")?.GetValue(string.Empty) as string; + + if (string.IsNullOrWhiteSpace(value)) + { + throw new ProgIdNotFoundException($"The ProgId for clsid was not found {clsid}"); + } + + return value; + } + + 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) + { + return IsFrameworkAssembly(type.Assembly); + } + + private static bool IsFrameworkAssembly(Assembly assembly) + { + var framework = 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; + } + + public static IEnumerable GetCredentialProviders(Assembly assembly) + { + return assembly.GetExportedTypes().Where(t => t.GetInterfaces().Any(ifn => ifn.Name == "ICredentialProvider") && !t.IsAbstract && !t.IsInterface); + } + + public static Assembly LoadAssembly(string assemblyPath) + { + string assemblyBasePath = Path.GetDirectoryName(assemblyPath); + + List paths = new List(); + paths.AddRange(Directory.GetFiles(assemblyBasePath)); + paths.Add(typeof(object).Assembly.Location); + + // needs to be the .net fx or net core fx location + paths.AddRange(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll")); + + var resolver = new PathAssemblyResolver(paths); + MetadataLoadContext mlc = new MetadataLoadContext(resolver); + return mlc.LoadFromAssemblyPath(assemblyPath); + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/DisableCredentialProviderCmdlet.cs b/src/Lithnet.CredentialProvider.Management/DisableCredentialProviderCmdlet.cs new file mode 100644 index 0000000..90f0c17 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/DisableCredentialProviderCmdlet.cs @@ -0,0 +1,45 @@ +using System; +using System.Management.Automation; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + [Cmdlet(VerbsLifecycle.Disable, "CredentialProvider", DefaultParameterSetName = "DisableByFileName")] + public class DisableCredentialProviderCmdlet : PSCmdlet + { + [Parameter(ParameterSetName = "DisableByFileName")] + public string File { get; set; } + + [Parameter(ParameterSetName = "DisableByClsid")] + public Guid Clsid { get; set; } + + [Parameter(ParameterSetName = "DisableByProgId")] + public string ProgId { get; set; } + + protected override void ProcessRecord() + { + if (this.ParameterSetName == "DisableByFileName") + { + if (!CredentialProviderRegistrationServices.IsManagedAssembly(this.File)) + { + throw new System.Exception("This tool cannot disable managed assemblies by file name. You can disable native assemblies using the CLSID or ProgID"); + } + var assembly = CredentialProviderRegistrationServices.LoadAssembly(this.File); + + foreach (var type in CredentialProviderRegistrationServices.GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.DisableCredentialProvider(type); + this.WriteVerbose($"Disabled credential provider {type.FullName}"); + } + } + else if (this.ParameterSetName == "DisableByClsid") + { + CredentialProviderRegistrationServices.DisableCredentialProvider(this.Clsid); + } + else if (this.ParameterSetName == "DisableByProgId") + { + var clsid = CredentialProviderRegistrationServices.GetClsidFromProgId(this.ProgId); + CredentialProviderRegistrationServices.DisableCredentialProvider(clsid); + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/DllType.cs b/src/Lithnet.CredentialProvider.Management/DllType.cs new file mode 100644 index 0000000..16f421a --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/DllType.cs @@ -0,0 +1,10 @@ +namespace Lithnet.CredentialProvider.RegistrationTool +{ + public enum DllType + { + Unknown, + NetFramework, + NetCore, + Native + } +} diff --git a/src/Lithnet.CredentialProvider.Management/EnableCredentialProviderCmdlet.cs b/src/Lithnet.CredentialProvider.Management/EnableCredentialProviderCmdlet.cs new file mode 100644 index 0000000..98987e7 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/EnableCredentialProviderCmdlet.cs @@ -0,0 +1,46 @@ +using System; +using System.Management.Automation; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + [Cmdlet(VerbsLifecycle.Enable, "CredentialProvider", DefaultParameterSetName = "EnableByFileName")] + public class EnableCredentialProviderCmdlet : PSCmdlet + { + [Parameter(ParameterSetName = "EnableByFileName")] + public string File { get; set; } + + [Parameter(ParameterSetName = "EnableByClsid")] + public Guid Clsid { get; set; } + + [Parameter(ParameterSetName = "EnableByProgId")] + public string ProgId { get; set; } + + protected override void ProcessRecord() + { + if (this.ParameterSetName == "EnableByFileName") + { + if (!CredentialProviderRegistrationServices.IsManagedAssembly(this.File)) + { + throw new System.Exception("This tool cannot enable managed assemblies by file name. You can enable native assemblies using the CLSID or ProgID"); + } + + var assembly = CredentialProviderRegistrationServices.LoadAssembly(this.File); + + foreach (var type in CredentialProviderRegistrationServices.GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.EnableCredentialProvider(type); + this.WriteVerbose($"Enabled credential provider {type.FullName}"); + } + } + else if (this.ParameterSetName == "EnableByClsid") + { + CredentialProviderRegistrationServices.EnableCredentialProvider(this.Clsid); + } + else if (this.ParameterSetName == "EnableByProgId") + { + var clsid = CredentialProviderRegistrationServices.GetClsidFromProgId(this.ProgId); + CredentialProviderRegistrationServices.EnableCredentialProvider(clsid); + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/Exceptions/ClsidNotFoundException.cs b/src/Lithnet.CredentialProvider.Management/Exceptions/ClsidNotFoundException.cs new file mode 100644 index 0000000..40c1656 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/Exceptions/ClsidNotFoundException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Lithnet.CredentialProvider +{ + public class ClsidNotFoundException : NotFoundException + { + public ClsidNotFoundException() : base() + { + } + + public ClsidNotFoundException(string message) : base(message) + { + } + + public ClsidNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/Exceptions/NotFoundException.cs b/src/Lithnet.CredentialProvider.Management/Exceptions/NotFoundException.cs new file mode 100644 index 0000000..4b818d6 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/Exceptions/NotFoundException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Lithnet.CredentialProvider +{ + public class NotFoundException : Exception + { + public NotFoundException() : base() + { + } + + public NotFoundException(string message) : base(message) + { + } + + public NotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/Exceptions/ProgIdNotFoundException.cs b/src/Lithnet.CredentialProvider.Management/Exceptions/ProgIdNotFoundException.cs new file mode 100644 index 0000000..7a6f65a --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/Exceptions/ProgIdNotFoundException.cs @@ -0,0 +1,19 @@ +using System; + +namespace Lithnet.CredentialProvider +{ + public class ProgIdNotFoundException : NotFoundException + { + public ProgIdNotFoundException() : base() + { + } + + public ProgIdNotFoundException(string message) : base(message) + { + } + + public ProgIdNotFoundException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/GetCredentialProviderCmdlet.cs b/src/Lithnet.CredentialProvider.Management/GetCredentialProviderCmdlet.cs new file mode 100644 index 0000000..8c424cc --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/GetCredentialProviderCmdlet.cs @@ -0,0 +1,48 @@ +using System; +using System.Management.Automation; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + [Cmdlet(VerbsCommon.Get, "CredentialProvider", DefaultParameterSetName = "None")] + public class GetCredentialProviderCmdlet : PSCmdlet + { + [Parameter(ParameterSetName = "GetByFileName")] + public string File { get; set; } + + [Parameter(ParameterSetName = "GetByClsid")] + public Guid Clsid { get; set; } + + [Parameter(ParameterSetName = "GetByProgId")] + public string ProgId { get; set; } + + protected override void ProcessRecord() + { + if (this.ParameterSetName == "None") + { + foreach (var item in CredentialProviderRegistrationServices.GetCredentalProviders()) + { + this.WriteObject(item); + } + } + else if (this.ParameterSetName == "GetByFileName") + { + var assembly = CredentialProviderRegistrationServices.LoadAssembly(this.File); + + foreach (var type in CredentialProviderRegistrationServices.GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.GetCredentialProvider(type); + this.WriteVerbose($"Got credential provider {type.FullName}"); + } + } + else if (this.ParameterSetName == "GetByClsid") + { + CredentialProviderRegistrationServices.GetCredentialProvider(this.Clsid); + } + else if (this.ParameterSetName == "GetByProgId") + { + var clsid = CredentialProviderRegistrationServices.GetClsidFromProgId(this.ProgId); + CredentialProviderRegistrationServices.GetCredentialProvider(clsid); + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/Lithnet.CredentialProvider.Management.csproj b/src/Lithnet.CredentialProvider.Management/Lithnet.CredentialProvider.Management.csproj new file mode 100644 index 0000000..1774b8a --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/Lithnet.CredentialProvider.Management.csproj @@ -0,0 +1,35 @@ + + + + net461 + AnyCPU + true + + + + + + + + + + + + + + + + Always + + + + + + + + %(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension) + + + + + diff --git a/src/Lithnet.CredentialProvider.Management/Lithnet.CredentialProvider.Management.psd1 b/src/Lithnet.CredentialProvider.Management/Lithnet.CredentialProvider.Management.psd1 new file mode 100644 index 0000000..7c58205 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/Lithnet.CredentialProvider.Management.psd1 @@ -0,0 +1,124 @@ +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'Lithnet.CredentialProvider.Management.dll' + +# Version number of this module. +ModuleVersion = '1.0.0' + +# Supported PSEditions +CompatiblePSEditions = @('Desktop' ,'Core') + +# ID used to uniquely identify this module +GUID = '7cbafd6a-cfe0-4380-b3f3-a161a9b96792' + +# Author of this module +Author = 'Lithnet Pty Ltd' + +# Company or vendor of this module +CompanyName = 'Lithnet Pty Ltd' + +# Copyright statement for this module +Copyright = '(c) Lithnet Pty Ltd 2023. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'This module provides cmdlets fot the management of Windows Credential providers' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '5.1' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +DotNetFrameworkVersion = '4.6.1' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +ClrVersion = '4.0' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = @() + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = @('Register-CredentialProvider', 'Unregister-CredentialProvider', 'Enable-CredentialProvider', 'Disable-CredentialProvider', 'Get-CredentialProvider') + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @("Windows" ,"PSEdition_Desktop", "PSEdition_Core") + + # A URL to the license for this module. + LicenseUri = 'https://github.com/lithnet/windows-credential-provider/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/lithnet/windows-credential-provider' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = 'https://github.com/lithnet/windows-credential-provider' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/src/Lithnet.CredentialProvider.Management/ModuleInitializer.cs b/src/Lithnet.CredentialProvider.Management/ModuleInitializer.cs new file mode 100644 index 0000000..852e6fa --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/ModuleInitializer.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Management.Automation; +using System.Reflection; +using System.Runtime.Loader; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + public class ModuleInitializer : IModuleAssemblyInitializer, IModuleAssemblyCleanup + { + public static bool IsFullFramework => typeof(object).Assembly.FullName.StartsWith("mscorlib", StringComparison.OrdinalIgnoreCase); + private Dictionary assemblies = new Dictionary(); + + public void OnImport() + { + Trace.WriteLine($"Initializing PowerShell module loaded in {(IsFullFramework ? "netfx" : "netcore")}"); + + this.PreloadAssemblies(); + this.HookResolvers(); + } + + public void OnRemove(PSModuleInfo psModuleInfo) + { + this.UnhookResolvers(); + } + + private void PreloadAssemblies() + { + var executingAssembly = Assembly.GetExecutingAssembly(); + + foreach (string resource in executingAssembly.GetManifestResourceNames().Where(n => n.EndsWith(".dll"))) + { + using (var stream = executingAssembly.GetManifestResourceStream(resource)) + { + if (stream == null) + { + continue; + } + + try + { + Trace.WriteLine("Preloading assembly: " + resource); + + if (IsFullFramework) + { + var bytes = new byte[stream.Length]; + stream.Read(bytes, 0, bytes.Length); + this.assemblies.Add(resource, Assembly.Load(bytes)); + } + else + { + this.assemblies.Add(resource, AssemblyContextResourceLoader.LoadIntoAlc(stream)); + } + } + catch (Exception ex) + { + Trace.TraceError("Failed to load: {0}\r\n", resource, ex.ToString()); + } + } + } + } + + private void HookResolvers() + { + AppDomain.CurrentDomain.AssemblyResolve += this.ResolveAssembly; + + if (!IsFullFramework) + { + this.HookAssemblyLoadContextResolver(); + } + } + + private void UnhookResolvers() + { + AppDomain.CurrentDomain.AssemblyResolve -= this.ResolveAssembly; + + if (!IsFullFramework) + { + this.UnhookAssemblyLoadContextResolver(); + } + } + + private void HookAssemblyLoadContextResolver() + { + AssemblyLoadContext.Default.Resolving += this.ResolveAssembly; + } + + private void UnhookAssemblyLoadContextResolver() + { + AssemblyLoadContext.Default.Resolving += this.ResolveAssembly; + } + + private Assembly ResolveAssembly(object s, ResolveEventArgs e) + { + var assemblyName = new AssemblyName(e.Name); + return this.ResolveAssemblyFromCache(assemblyName); + } + + private Assembly ResolveAssembly(AssemblyLoadContext defaultAlc, AssemblyName assemblyName) + { + return this.ResolveAssemblyFromCache(assemblyName); + } + + private Assembly ResolveAssemblyFromCache(AssemblyName assemblyName) + { + var path = string.Format("{0}.dll", assemblyName.Name); + + if (this.assemblies.ContainsKey(path)) + { + return this.assemblies[path]; + } + + return null; + } + } +} diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/Properties/launchSettings.json b/src/Lithnet.CredentialProvider.Management/Properties/launchSettings.json similarity index 100% rename from src/Lithnet.CredentialProvider.RegistrationTool/Properties/launchSettings.json rename to src/Lithnet.CredentialProvider.Management/Properties/launchSettings.json diff --git a/src/Lithnet.CredentialProvider.Management/RegisterCredentialProviderCmdlet.cs b/src/Lithnet.CredentialProvider.Management/RegisterCredentialProviderCmdlet.cs new file mode 100644 index 0000000..b599391 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/RegisterCredentialProviderCmdlet.cs @@ -0,0 +1,27 @@ +using System.Management.Automation; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + [Cmdlet(VerbsLifecycle.Register, "CredentialProvider")] + public class RegisterCredentialProviderCmdlet : PSCmdlet + { + [Parameter] + public string File { get; set; } + + protected override void ProcessRecord() + { + if (!CredentialProviderRegistrationServices.IsManagedAssembly(this.File)) + { + throw new System.Exception("This tool can only register managed assemblies"); + } + + var assembly = CredentialProviderRegistrationServices.LoadAssembly(this.File); + + foreach (var type in CredentialProviderRegistrationServices.GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.RegisterCredentialProvider(type); + this.WriteVerbose($"Registered credential provider {type.FullName}"); + } + } + } +} diff --git a/src/Lithnet.CredentialProvider.Management/UnregisterCredentialProviderCmdlet.cs b/src/Lithnet.CredentialProvider.Management/UnregisterCredentialProviderCmdlet.cs new file mode 100644 index 0000000..589b720 --- /dev/null +++ b/src/Lithnet.CredentialProvider.Management/UnregisterCredentialProviderCmdlet.cs @@ -0,0 +1,59 @@ +using System; +using System.Management.Automation; + +namespace Lithnet.CredentialProvider.RegistrationTool +{ + [Cmdlet(VerbsLifecycle.Unregister, "CredentialProvider", DefaultParameterSetName = "UnregisterByFileName")] + public class UnregisterCredentialProviderCmdlet : PSCmdlet + { + [Parameter(ParameterSetName = "UnregisterByFileName")] + public string File { get; set; } + + [Parameter(ParameterSetName = "UnregisterByClsid")] + public Guid Clsid { get; set; } + + [Parameter(ParameterSetName = "UnregisterByProgId")] + public string ProgId { get; set; } + + [Parameter] + public SwitchParameter UnregisterCom { get; set; } + + protected override void ProcessRecord() + { + if (this.ParameterSetName == "UnregisterByFileName") + { + if (!CredentialProviderRegistrationServices.IsManagedAssembly(this.File)) + { + throw new Exception("This tool cannot unregister managed assemblies by file name. You can unregister native assemblies using the CLSID or ProgID"); + } + + var assembly = CredentialProviderRegistrationServices.LoadAssembly(this.File); + + foreach (var type in CredentialProviderRegistrationServices.GetCredentialProviders(assembly)) + { + CredentialProviderRegistrationServices.UnregisterCredentialProvider(type, this.GetSwitchValue(this.UnregisterCom, nameof(this.UnregisterCom))); + this.WriteVerbose($"Unregistered credential provider {type.FullName}"); + } + } + else if (this.ParameterSetName == "UnregisterByClsid") + { + CredentialProviderRegistrationServices.UnregisterCredentialProvider(this.Clsid, this.GetSwitchValue(this.UnregisterCom, nameof(this.UnregisterCom))); + } + else if (this.ParameterSetName == "UnregisterByProgId") + { + var clsid = CredentialProviderRegistrationServices.GetClsidFromProgId(this.ProgId); + CredentialProviderRegistrationServices.UnregisterCredentialProvider(clsid, this.GetSwitchValue(this.UnregisterCom, nameof(this.UnregisterCom))); + } + } + + protected bool GetSwitchValue(SwitchParameter parameter, string name) + { + if (this.MyInvocation.BoundParameters.ContainsKey(name)) + { + return parameter.ToBool(); + } + + return false; + } + } +} diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/CredentialProviderRegistrationServices.cs b/src/Lithnet.CredentialProvider.RegistrationTool/CredentialProviderRegistrationServices.cs deleted file mode 100644 index ca8b1e8..0000000 --- a/src/Lithnet.CredentialProvider.RegistrationTool/CredentialProviderRegistrationServices.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using Microsoft.Win32; - -namespace Lithnet.CredentialProvider -{ - public static class CredentialProviderRegistrationServices - { - 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.RegistrationTool/GlobalSuppressions.cs b/src/Lithnet.CredentialProvider.RegistrationTool/GlobalSuppressions.cs deleted file mode 100644 index 1178f4c..0000000 --- a/src/Lithnet.CredentialProvider.RegistrationTool/GlobalSuppressions.cs +++ /dev/null @@ -1,8 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")] diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj b/src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj deleted file mode 100644 index bf978a9..0000000 --- a/src/Lithnet.CredentialProvider.RegistrationTool/Lithnet.CredentialProvider.RegistrationTool.csproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - Exe - net6.0-windows - - - - - - - diff --git a/src/Lithnet.CredentialProvider.RegistrationTool/Program.cs b/src/Lithnet.CredentialProvider.RegistrationTool/Program.cs deleted file mode 100644 index 51aab60..0000000 --- a/src/Lithnet.CredentialProvider.RegistrationTool/Program.cs +++ /dev/null @@ -1,187 +0,0 @@ -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.sln b/src/Lithnet.CredentialProvider.sln index ecfaa85..520c592 100644 --- a/src/Lithnet.CredentialProvider.sln +++ b/src/Lithnet.CredentialProvider.sln @@ -8,6 +8,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9E97082E-4E8D-4E8B-A320-40E2A1494C74}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + ..\azure-pipelines.yml = ..\azure-pipelines.yml 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}" @@ -22,7 +23,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider. 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}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lithnet.CredentialProvider.Management", "Lithnet.CredentialProvider.Management\Lithnet.CredentialProvider.Management.csproj", "{6A9A22A9-A2BB-4329-A0A5-2539E464CE5F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution