#if NET20_OR_GREATER using System; using System.ComponentModel; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; using System.ServiceProcess; using Vanara.PInvoke; using static Vanara.PInvoke.AdvApi32; namespace Vanara.Extensions { /// Extension methods for . public static partial class ServiceControllerExtension { /// /// Gets a object that encapsulates the specified type of access /// control list (ACL) entries for the service described by the current object. /// /// The object from which to get access control. /// /// One of the values that specifies which group of access control entries to retrieve. /// /// /// A object that encapsulates the access control rules for the /// current service. /// public static Security.AccessControl.ServiceControllerSecurity GetAccessControl(this ServiceController svc, AccessControlSections includeSections = AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group) => new Security.AccessControl.ServiceControllerSecurity(svc.ServiceHandle, includeSections); /// /// Applies access control list (ACL) entries described by a object to /// the service described by the current object. /// /// The object on which to set access control. /// /// A object that describes an access control list (ACL) entry to /// apply to the current service. /// public static void SetAccessControl(this ServiceController svc, Security.AccessControl.ServiceControllerSecurity serviceSecurity) { if (serviceSecurity is null) throw new ArgumentNullException(nameof(serviceSecurity)); serviceSecurity.Persist(svc.ServiceHandle); } /// Changes the start mode of a service. /// The instance. /// The new start mode. public static void SetStartType(this ServiceController svc, ServiceStartMode mode) { using (var serviceHandle = svc.ServiceHandle) { if (!ChangeServiceConfig(serviceHandle.DangerousGetHandle(), ServiceTypes.SERVICE_NO_CHANGE, (ServiceStartType)mode, ServiceErrorControlType.SERVICE_NO_CHANGE)) throw new ExternalException("Could not change service start type.", new Win32Exception()); } } } } namespace Vanara.Security.AccessControl { /// Defines the access rights to use when creating access and audit rules. [Flags] public enum ServiceControllerAccessRights : uint { /// Includes STANDARD_RIGHTS_REQUIRED in addition to all access rights in this table. FullControl = ServiceAccessRights.SERVICE_ALL_ACCESS | AccessSystemSecurity | ACCESS_MASK.STANDARD_RIGHTS_ALL, /// /// Required to call the ChangeServiceConfig or ChangeServiceConfig2 function to change the service configuration. Because this /// grants the caller the right to change the executable file that the system runs, it should be granted only to administrators. /// ChangeConfig = ServiceAccessRights.SERVICE_CHANGE_CONFIG, /// Required to call the EnumDependentServices function to enumerate all the services dependent on the service. EnumerateDependents = ServiceAccessRights.SERVICE_ENUMERATE_DEPENDENTS, /// Required to call the ControlService function to ask the service to report its status immediately. Interrogate = ServiceAccessRights.SERVICE_INTERROGATE, /// Required to call the ControlService function to pause or continue the service. Continue = ServiceAccessRights.SERVICE_PAUSE_CONTINUE, /// Required to call the QueryServiceConfig and QueryServiceConfig2 functions to query the service configuration. QueryConfig = ServiceAccessRights.SERVICE_QUERY_CONFIG, /// /// Required to call the QueryServiceStatus or QueryServiceStatusEx function to ask the service control manager about the status of /// the service. /// Required to call the NotifyServiceStatusChange function to receive notification when a service changes status. /// QueryStatus = ServiceAccessRights.SERVICE_QUERY_STATUS, /// Required to call the StartService function to start the service. Start = ServiceAccessRights.SERVICE_START, /// Required to call the ControlService function to stop the service. Stop = ServiceAccessRights.SERVICE_STOP, /// Required to call the ControlService function to specify a user-defined control code. UserDefinedControl = ServiceAccessRights.SERVICE_USER_DEFINED_CONTROL, /// /// Required to call the QueryServiceObjectSecurity or SetServiceObjectSecurity function to access the SACL. The proper way to obtain /// this access is to enable the SE_SECURITY_NAMEprivilege in the caller's current access token, open the handle for /// ACCESS_SYSTEM_SECURITY access, and then disable the privilege. /// AccessSystemSecurity = ACCESS_MASK.ACCESS_SYSTEM_SECURITY, /// Required to call the DeleteService function to delete the service. Delete = ACCESS_MASK.DELETE, /// Required to call the QueryServiceObjectSecurity function to query the security descriptor of the service object. ReadPermissions = ACCESS_MASK.READ_CONTROL, /// /// Required to call the SetServiceObjectSecurity function to modify the Dacl member of the service object's security descriptor. /// ChangePermissions = ACCESS_MASK.WRITE_DAC, /// /// Required to call the SetServiceObjectSecurity function to modify the Owner and Group members of the service object's security descriptor. /// TakeOwnership = ACCESS_MASK.WRITE_OWNER, /// /// Specifies the right to open and copy folders or files as read-only. This right includes the ReadData right, /// ReadExtendedAttributes right, ReadAttributes right, and ReadPermissions right. /// Read = ACCESS_MASK.STANDARD_RIGHTS_READ | ServiceAccessRights.SERVICE_QUERY_CONFIG | ServiceAccessRights.SERVICE_QUERY_STATUS | ServiceAccessRights.SERVICE_INTERROGATE | ServiceAccessRights.SERVICE_ENUMERATE_DEPENDENTS, /// /// Specifies the right to create folders and files, and to add or remove data from files. This right includes the WriteData right, /// AppendData right, WriteExtendedAttributes right, and WriteAttributes right. /// Write = ACCESS_MASK.STANDARD_RIGHTS_WRITE | ServiceAccessRights.SERVICE_CHANGE_CONFIG, /// Specifies the right to run an application file. Execute = ACCESS_MASK.STANDARD_RIGHTS_EXECUTE | ServiceAccessRights.SERVICE_START | ServiceAccessRights.SERVICE_STOP | ServiceAccessRights.SERVICE_PAUSE_CONTINUE | ServiceAccessRights.SERVICE_USER_DEFINED_CONTROL, } /// Represents an abstraction of an access control entry (ACE) that defines an access rule for a service. /// public sealed class ServiceControllerAccessRule : AccessRule { /// /// Initializes a new instance of the class with the specified identity, access rights, and /// access control type.. /// /// The name of the user account. /// /// One of the values that specifies the type of operation associated with the access rule. /// /// One of the values that specifies whether to allow or deny the operation. public ServiceControllerAccessRule(string identity, ServiceControllerAccessRights rights, AccessControlType type) : this(new NTAccount(identity), AccessMaskFromRights(rights), false, type) { } /// /// Initializes a new instance of the class with the specified identity, access rights, and /// access control type.. /// /// An object that encapsulates a reference to a user account. /// /// One of the values that specifies the type of operation associated with the access rule. /// /// One of the values that specifies whether to allow or deny the operation. public ServiceControllerAccessRule(IdentityReference identity, ServiceControllerAccessRights rights, AccessControlType type) : this(identity, AccessMaskFromRights(rights), false, type) { } internal ServiceControllerAccessRule(IdentityReference identity, int accessMask, bool isInherited, AccessControlType type) : base(identity, accessMask, isInherited, InheritanceFlags.None, PropagationFlags.None, type) { } /// /// Gets the flags that are associated with the current /// object. /// /// A bitwise combination of the values. public ServiceControllerAccessRights AccessRights => (ServiceControllerAccessRights)AccessMask; internal static int AccessMaskFromRights(ServiceControllerAccessRights rights) => rights >= 0 && rights <= ServiceControllerAccessRights.FullControl ? (int)rights : throw new ArgumentOutOfRangeException(nameof(rights)); } /// Represents an abstraction of an access control entry (ACE) that defines an audit rule for a service. /// public sealed class ServiceControllerAuditRule : AuditRule { /// /// Initializes a new instance of the class for a user account specified in a /// object. /// /// An object that encapsulates a reference to a user account. /// /// One of the values that specifies the type of operation associated with the access rule. /// /// One of the values that specifies when to perform auditing. public ServiceControllerAuditRule(IdentityReference identity, ServiceControllerAccessRights rights, AuditFlags flags) : this(identity, ServiceControllerAccessRule.AccessMaskFromRights(rights), false, flags) { } /// /// Initializes a new instance of the class for a user account specified in a /// object. /// /// The name of the user account. /// /// One of the values that specifies the type of operation associated with the access rule. /// /// One of the values that specifies when to perform auditing. public ServiceControllerAuditRule(string identity, ServiceControllerAccessRights rights, AuditFlags flags) : this(new NTAccount(identity), ServiceControllerAccessRule.AccessMaskFromRights(rights), false, flags) { } internal ServiceControllerAuditRule(IdentityReference identity, int accessMask, bool isInherited, AuditFlags flags) : base(identity, accessMask, isInherited, InheritanceFlags.None, PropagationFlags.None, flags) { } } /// Represents the access control and audit security for a service. /// public class ServiceControllerSecurity : NativeObjectSecurity { /// Initializes a new instance of the class. public ServiceControllerSecurity() : base(false, System.Security.AccessControl.ResourceType.Service) { } internal ServiceControllerSecurity(SafeHandle handle, AccessControlSections includeSections) : base(false, System.Security.AccessControl.ResourceType.Service, handle, includeSections) { } /// /// Gets the of the securable object associated with this /// object. /// public override Type AccessRightType => typeof(ServiceControllerAccessRights); /// /// Gets the of the object associated with the access rules of this /// object. The object must be an object /// that can be cast as a object. /// public override Type AccessRuleType => typeof(ServiceControllerAccessRule); /// /// Gets the object associated with the audit rules of this /// object. The object must be an object /// that can be cast as a object. /// public override Type AuditRuleType => typeof(ServiceControllerAuditRule); /// /// Initializes a new instance of the class with the specified values. /// /// The identity to which the access rule applies. It must be an object that can be cast as a . /// /// The access mask of this rule. The access mask is a 32-bit collection of anonymous bits, the meaning of which is defined by the /// individual integrators. /// /// true if this rule is inherited from a parent container. /// Specifies the inheritance properties of the access rule. /// /// Specifies whether inherited access rules are automatically propagated. The propagation flags are ignored if /// is set to . /// /// Specifies the valid access control type. /// The object that this method creates. public override AccessRule AccessRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) => new ServiceControllerAccessRule(identityReference, (ServiceControllerAccessRights)accessMask, type); /// /// Initializes a new instance of the class with the specified values. /// /// The identity to which the audit rule applies. It must be an object that can be cast as a . /// /// The access mask of this rule. The access mask is a 32-bit collection of anonymous bits, the meaning of which is defined by the /// individual integrators. /// /// true if this rule is inherited from a parent container. /// Specifies the inheritance properties of the audit rule. /// /// Specifies whether inherited audit rules are automatically propagated. The propagation flags are ignored if /// is set to . /// /// Specifies the conditions for which the rule is audited. /// The object that this method creates. public override AuditRule AuditRuleFactory(IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) => new ServiceControllerAuditRule(identityReference, (ServiceControllerAccessRights)accessMask, flags); internal void Persist(SafeHandle handle) { WriteLock(); try { var persistRules = GetAccessControlSectionsFromChanges(); if (persistRules == AccessControlSections.None) return; Persist(handle, persistRules); OwnerModified = GroupModified = AuditRulesModified = AccessRulesModified = false; } finally { WriteUnlock(); } } private AccessControlSections GetAccessControlSectionsFromChanges() { var persistRules = AccessControlSections.None; if (AccessRulesModified) persistRules = AccessControlSections.Access; if (AuditRulesModified) persistRules |= AccessControlSections.Audit; if (OwnerModified) persistRules |= AccessControlSections.Owner; if (GroupModified) persistRules |= AccessControlSections.Group; return persistRules; } } } #endif