From 31ec634391a2fa68ee861c3d3c3efc21d3673149 Mon Sep 17 00:00:00 2001 From: Brian Scholer <1260690+briantist@users.noreply.github.com> Date: Mon, 16 May 2022 20:28:51 -0400 Subject: [PATCH] separate the CLI command implementations (#10) --- src/CcgVault/CLI/CliCommandPing.cs | 30 ++++ src/CcgVault/CLI/CliCommandRegistry.cs | 81 ++++++++++ src/CcgVault/CLI/CliCommandService.cs | 81 ++++++++++ src/CcgVault/CLI/CliCommandTest.cs | 34 +++++ src/CcgVault/CLI/ICliCommand.cs | 11 ++ src/CcgVault/CcgVault.csproj | 5 + src/CcgVault/Program.cs | 204 +------------------------ 7 files changed, 243 insertions(+), 203 deletions(-) create mode 100644 src/CcgVault/CLI/CliCommandPing.cs create mode 100644 src/CcgVault/CLI/CliCommandRegistry.cs create mode 100644 src/CcgVault/CLI/CliCommandService.cs create mode 100644 src/CcgVault/CLI/CliCommandTest.cs create mode 100644 src/CcgVault/CLI/ICliCommand.cs diff --git a/src/CcgVault/CLI/CliCommandPing.cs b/src/CcgVault/CLI/CliCommandPing.cs new file mode 100644 index 0000000..b48b796 --- /dev/null +++ b/src/CcgVault/CLI/CliCommandPing.cs @@ -0,0 +1,30 @@ +using System; + +namespace CcgVault +{ + partial class Program + { + class CliCommandPing : ICliCommand + { + public void Help(string text) + { + Console.WriteLine("ccgvault.exe ping"); + Console.WriteLine(); + Console.WriteLine("Runs a simple method against the COM+ service, implicitly registering it in the process."); + Console.WriteLine(); + Console.WriteLine("The following options are accepted:"); + Console.WriteLine(text); + Exit(-1); + } + + public void Invoke() + { + using (var ccg = new CcgPlugin()) + { + Console.WriteLine("Expecting response: 'Pong'"); + Console.WriteLine($"Got response: '{ccg.Ping()}'"); + } + } + } + } +} diff --git a/src/CcgVault/CLI/CliCommandRegistry.cs b/src/CcgVault/CLI/CliCommandRegistry.cs new file mode 100644 index 0000000..1ee5fc9 --- /dev/null +++ b/src/CcgVault/CLI/CliCommandRegistry.cs @@ -0,0 +1,81 @@ +using Microsoft.Win32; +using System; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Security.Principal; + +namespace CcgVault +{ + partial class Program + { + class CliCommandRegistry : ICliCommand + { + public bool Permission { get; set; } + public bool ComClass { get; set; } + + public void Help(string text) + { + Console.WriteLine("ccgvault.exe registry"); + Console.WriteLine(); + Console.WriteLine(@"Adds the CcgVault CLSID to the allowed CCG plugins and/or sets permission and ownership of the CCG\COMClasses key."); + Console.WriteLine(); + Console.WriteLine("The following options are accepted:"); + Console.WriteLine(text); + Exit(-1); + } + + public void Invoke() + { + var subkey = @"SYSTEM\CurrentControlSet\Control\CCG\COMClasses"; + + if (!(Permission || ComClass)) + { + Console.WriteLine("Neither --permission nor --comclass were specified. Choose one or both."); + Exit(-1); + } + + RegistryKey key; + + if (Permission) + { + using (new TokenPrivilege(TokenPrivilege.SE_TAKE_OWNERSHIP_NAME, TokenPrivilege.SE_RESTORE_NAME)) + { + key = Registry.LocalMachine.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership); + + if (key == null) + { + Console.WriteLine($"{subkey} not found or error opening key."); + Exit(-1); + } + + const string SID_ADMINISTRATORS = "S-1-5-32-544"; + var admins = new SecurityIdentifier(SID_ADMINISTRATORS).Translate(typeof(NTAccount)); + var ac = key.GetAccessControl(); + ac.SetOwner(admins); + key.SetAccessControl(ac); + key.Close(); + + key = Registry.LocalMachine.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership); + ac = key.GetAccessControl(); + ac.AddAccessRule(new RegistryAccessRule(admins, + RegistryRights.FullControl, + InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, + PropagationFlags.None, + AccessControlType.Allow)); + key.SetAccessControl(ac); + key.Close(); + } + } + + if (ComClass) + { + var pluginId = (GuidAttribute)Attribute.GetCustomAttribute(typeof(CcgPlugin), typeof(GuidAttribute), true); + + key = Registry.LocalMachine.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree); + key.CreateSubKey($"{{{pluginId.Value}}}"); + key.Close(); + } + } + } + } +} diff --git a/src/CcgVault/CLI/CliCommandService.cs b/src/CcgVault/CLI/CliCommandService.cs new file mode 100644 index 0000000..7cc3624 --- /dev/null +++ b/src/CcgVault/CLI/CliCommandService.cs @@ -0,0 +1,81 @@ +using COMAdmin; +using System; +using System.EnterpriseServices; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace CcgVault +{ + partial class Program + { + class CliCommandService : ICliCommand + { + public bool Install { get; set; } + public bool Uninstall { get; set; } + + public void Help(string text) + { + Console.WriteLine("ccgvault.exe service"); + Console.WriteLine(); + Console.WriteLine("Installs or uninstalls a Windows (NT) service to associate with the COM+ application."); + Console.WriteLine(); + Console.WriteLine("The following options are accepted:"); + Console.WriteLine(text); + Exit(-1); + } + + public void Invoke() + { + if (Install && Uninstall) + { + Console.WriteLine("Both --install and --uninstall were specified. Choose one or the other."); + Exit(-1); + } + + if (!(Install || Uninstall)) + { + Console.WriteLine("Neither --install nor --uninstall were specified. Choose one or the other."); + Exit(-1); + } + + var appId = Assembly.GetExecutingAssembly().GetCustomAttribute().Value; + var serviceName = Assembly.GetExecutingAssembly().GetCustomAttribute().Value; + + // https://docs.microsoft.com/en-us/windows/win32/api/comadmin/nf-comadmin-icomadmincatalog2-createserviceforapplication + var oCatalog = (ICOMAdminCatalog2)Activator.CreateInstance(Type.GetTypeFromProgID("ComAdmin.COMAdminCatalog")); + + if (Install) + { + try + { + oCatalog.CreateServiceForApplication( + bstrApplicationIDOrName: appId.ToString("B"), // Using an AppID, the GUID needs to be formatted with curly braces {} + bstrServiceName: serviceName, + bstrStartType: "SERVICE_DEMAND_START", + bstrErrorControl: "SERVICE_ERROR_NORMAL", + bstrDependencies: null, + bstrRunAs: null, + bstrPassword: null, + bDesktopOk: false); + } + catch (COMException e) when (e.HResult == -2147023823) // 0x80070431 ERROR_SERVICE_EXISTS + { + // ignore + } + } + + if (Uninstall) + { + try + { + oCatalog.DeleteServiceForApplication(bstrApplicationIDOrName: appId.ToString("B")); // Using an AppID, the GUID needs to be formatted with curly braces {} + } + catch (COMException e) when (e.HResult == -2146368458) // 0x80110436 COMADMIN_E_SERVICENOTINSTALLED + { + // ignore + } + } + } + } + } +} diff --git a/src/CcgVault/CLI/CliCommandTest.cs b/src/CcgVault/CLI/CliCommandTest.cs new file mode 100644 index 0000000..8a9e3a8 --- /dev/null +++ b/src/CcgVault/CLI/CliCommandTest.cs @@ -0,0 +1,34 @@ +using System; + +namespace CcgVault +{ + partial class Program + { + class CliCommandTest : ICliCommand + { + public string Input { get; set; } + + public void Invoke() + { + using (CcgPlugin ccg = new CcgPlugin()) + { + string domain, user, pass; + ccg.GetPasswordCredentials(Input, out domain, out user, out pass); + + Console.WriteLine($"Domain: {domain}\nUser: {user}\nPass: {pass}"); + } + } + + public void Help(string text) + { + Console.WriteLine("ccgvault.exe test"); + Console.WriteLine(); + Console.WriteLine("Tests the credential process with the given input string."); + Console.WriteLine(); + Console.WriteLine("The following options are accepted:"); + Console.WriteLine(text); + Exit(-1); + } + } + } +} diff --git a/src/CcgVault/CLI/ICliCommand.cs b/src/CcgVault/CLI/ICliCommand.cs new file mode 100644 index 0000000..fc42e7c --- /dev/null +++ b/src/CcgVault/CLI/ICliCommand.cs @@ -0,0 +1,11 @@ +namespace CcgVault +{ + partial class Program + { + interface ICliCommand + { + void Invoke(); + void Help(string text); + } + } +} diff --git a/src/CcgVault/CcgVault.csproj b/src/CcgVault/CcgVault.csproj index 34a7cad..359b408 100644 --- a/src/CcgVault/CcgVault.csproj +++ b/src/CcgVault/CcgVault.csproj @@ -82,6 +82,11 @@ + + + + + diff --git a/src/CcgVault/Program.cs b/src/CcgVault/Program.cs index 9e4eb7b..a848e50 100644 --- a/src/CcgVault/Program.cs +++ b/src/CcgVault/Program.cs @@ -1,215 +1,13 @@ -using COMAdmin; using Fclp; -using Microsoft.Win32; using System; using System.Diagnostics; -using System.EnterpriseServices; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; -using System.Security.AccessControl; -using System.Security.Principal; namespace CcgVault { - class Program + partial class Program { - interface ICliCommand - { - void Invoke(); - void Help(string text); - } - - class CliCommandTest : ICliCommand - { - public string Input { get; set; } - - public void Invoke() - { - using (CcgPlugin ccg = new CcgPlugin()) - { - string domain, user, pass; - ccg.GetPasswordCredentials(Input, out domain, out user, out pass); - - Console.WriteLine($"Domain: {domain}\nUser: {user}\nPass: {pass}"); - } - } - - public void Help(string text) - { - Console.WriteLine("ccgvault.exe test"); - Console.WriteLine(); - Console.WriteLine("Tests the credential process with the given input string."); - Console.WriteLine(); - Console.WriteLine("The following options are accepted:"); - Console.WriteLine(text); - Exit(-1); - } - } - - class CliCommandPing : ICliCommand - { - public void Help(string text) - { - Console.WriteLine("ccgvault.exe ping"); - Console.WriteLine(); - Console.WriteLine("Runs a simple method against the COM+ service, implicitly registering it in the process."); - Console.WriteLine(); - Console.WriteLine("The following options are accepted:"); - Console.WriteLine(text); - Exit(-1); - } - - public void Invoke() - { - using (var ccg = new CcgPlugin()) - { - Console.WriteLine("Expecting response: 'Pong'"); - Console.WriteLine($"Got response: '{ccg.Ping()}'"); - } - } - } - - class CliCommandRegistry : ICliCommand - { - public bool Permission { get; set; } - public bool ComClass { get; set; } - - public void Help(string text) - { - Console.WriteLine("ccgvault.exe registry"); - Console.WriteLine(); - Console.WriteLine(@"Adds the CcgVault CLSID to the allowed CCG plugins and/or sets permission and ownership of the CCG\COMClasses key."); - Console.WriteLine(); - Console.WriteLine("The following options are accepted:"); - Console.WriteLine(text); - Exit(-1); - } - - public void Invoke() - { - var subkey = @"SYSTEM\CurrentControlSet\Control\CCG\COMClasses"; - - if (!(Permission || ComClass)) - { - Console.WriteLine("Neither --permission nor --comclass were specified. Choose one or both."); - Exit(-1); - } - - RegistryKey key; - - if (Permission) - { - using (new TokenPrivilege(TokenPrivilege.SE_TAKE_OWNERSHIP_NAME, TokenPrivilege.SE_RESTORE_NAME)) - { - key = Registry.LocalMachine.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership); - - if (key == null) - { - Console.WriteLine($"{subkey} not found or error opening key."); - Exit(-1); - } - - const string SID_ADMINISTRATORS = "S-1-5-32-544"; - var admins = new SecurityIdentifier(SID_ADMINISTRATORS).Translate(typeof(NTAccount)); - var ac = key.GetAccessControl(); - ac.SetOwner(admins); - key.SetAccessControl(ac); - key.Close(); - - key = Registry.LocalMachine.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership); - ac = key.GetAccessControl(); - ac.AddAccessRule(new RegistryAccessRule(admins, - RegistryRights.FullControl, - InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, - PropagationFlags.None, - AccessControlType.Allow)); - key.SetAccessControl(ac); - key.Close(); - } - } - - if (ComClass) - { - var pluginId = (GuidAttribute)Attribute.GetCustomAttribute(typeof(CcgPlugin), typeof(GuidAttribute), true); - - key = Registry.LocalMachine.OpenSubKey(subkey, RegistryKeyPermissionCheck.ReadWriteSubTree); - key.CreateSubKey($"{{{pluginId.Value}}}"); - key.Close(); - } - } - } - - class CliCommandService : ICliCommand - { - public bool Install { get; set; } - public bool Uninstall { get; set; } - - public void Help(string text) - { - Console.WriteLine("ccgvault.exe service"); - Console.WriteLine(); - Console.WriteLine("Installs or uninstalls a Windows (NT) service to associate with the COM+ application."); - Console.WriteLine(); - Console.WriteLine("The following options are accepted:"); - Console.WriteLine(text); - Exit(-1); - } - - public void Invoke() - { - if (Install && Uninstall) - { - Console.WriteLine("Both --install and --uninstall were specified. Choose one or the other."); - Exit(-1); - } - - if (!(Install || Uninstall)) - { - Console.WriteLine("Neither --install nor --uninstall were specified. Choose one or the other."); - Exit(-1); - } - - var appId = Assembly.GetExecutingAssembly().GetCustomAttribute().Value; - var serviceName = Assembly.GetExecutingAssembly().GetCustomAttribute().Value; - - // https://docs.microsoft.com/en-us/windows/win32/api/comadmin/nf-comadmin-icomadmincatalog2-createserviceforapplication - var oCatalog = (ICOMAdminCatalog2)Activator.CreateInstance(Type.GetTypeFromProgID("ComAdmin.COMAdminCatalog")); - - if (Install) - { - try - { - oCatalog.CreateServiceForApplication( - bstrApplicationIDOrName: appId.ToString("B"), // Using an AppID, the GUID needs to be formatted with curly braces {} - bstrServiceName: serviceName, - bstrStartType: "SERVICE_DEMAND_START", - bstrErrorControl: "SERVICE_ERROR_NORMAL", - bstrDependencies: null, - bstrRunAs: null, - bstrPassword: null, - bDesktopOk: false); - } - catch (COMException e) when (e.HResult == -2147023823) // 0x80070431 ERROR_SERVICE_EXISTS - { - // ignore - } - } - - if (Uninstall) - { - try - { - oCatalog.DeleteServiceForApplication(bstrApplicationIDOrName: appId.ToString("B")); // Using an AppID, the GUID needs to be formatted with curly braces {} - } - catch (COMException e) when (e.HResult == -2146368458) // 0x80110436 COMADMIN_E_SERVICENOTINSTALLED - { - // ignore - } - } - } - } - public static void Main(string[] args) { string cmd = "";