#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