8

How do I check if the user has permission to start/stop a specific windows service, without actually stopping or starting it?

The question is not about granting the right to restart the service to the user (with subinacl.exe for example) : I want to check if the user is administrator or if he has already been granted the right to restart the service.

Govert
  • 16,387
  • 4
  • 60
  • 70
olorin
  • 2,214
  • 3
  • 19
  • 22
  • 2
    did you ever find an answer to your question? I'm looking for something similar. Thanks – Adam Plocher Apr 23 '13 at 18:59
  • Adam: unfortunately no, I did not find an answer – olorin Apr 25 '13 at 07:01
  • I know this is an old one but would there be any useful information here: http://stackoverflow.com/questions/4436558/start-stop-a-windows-service-from-a-non-administrator-user-account ? in particular using the subinacl utility to query user permissions? http://ss64.com/nt/subinacl.html – Richard Oct 20 '15 at 08:57
  • I edited the question to show what I have found : I'm now looking for the last piece of the puzzle – olorin Nov 26 '15 at 09:36

1 Answers1

2

I finally found a way, I'm answering my own question for future reference.

public static ServiceAccessFlags GetServiceAcces(ServiceController serviceController)
{
  WindowsIdentity winId = WindowsIdentity.GetCurrent(TokenAccessLevels.Duplicate | TokenAccessLevels.Query);
  return GetServiceAcces(serviceController, winId);
}

private static ServiceAccessFlags GetServiceAcces(ServiceController serviceController, WindowsIdentity windowsIdentity)
{
  // see http://www.pinvoke.net/default.aspx/advapi32/QueryServiceObjectSecurity.html?DelayRedirect=1If
  byte[] buffer = new byte[0];
  uint bufferSizeNeeded;
  bool ok = QueryServiceObjectSecurity(serviceController.ServiceHandle, SecurityInfos.DiscretionaryAcl, buffer, 0, out bufferSizeNeeded);
  if (!ok)
  {
    int err = Marshal.GetLastWin32Error();
    if (err == 122) // ERROR_INSUFFICIENT_BUFFER
    {
      // expected; now we know bufsize
      buffer = new byte[bufferSizeNeeded];
      ok = QueryServiceObjectSecurity(serviceController.ServiceHandle, SecurityInfos.DiscretionaryAcl, buffer, bufferSizeNeeded, out bufferSizeNeeded);
    }
    else
    {
      throw new InvalidOperationException("error calling QueryServiceObjectSecurity() to get DACL for Service: error code=" + err);
    }
  }
  if (!ok)
    throw new InvalidOperationException("error calling QueryServiceObjectSecurity(2) to get DACL for Service: error code=" + Marshal.GetLastWin32Error());

  RawSecurityDescriptor rsd = new RawSecurityDescriptor(buffer, 0);
  RawAcl racl = rsd.DiscretionaryAcl;
  DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, racl);

  byte[] daclBuffer = new byte[dacl.BinaryLength];
  dacl.GetBinaryForm(daclBuffer, 0);

  SecurityIdentifier sid = windowsIdentity.User;
  byte[] sidBuffer = new byte[sid.BinaryLength];
  sid.GetBinaryForm(sidBuffer, 0);

  TRUSTEE t = new TRUSTEE();
  BuildTrusteeWithSid(ref t, sidBuffer);

  uint access = 0;
  uint hr = GetEffectiveRightsFromAcl(daclBuffer, ref t, ref access);

  ServiceAccessFlags serviceAccess = (ServiceAccessFlags)access;

  int i = Marshal.Release(t.ptstrName);

  return serviceAccess;
}

[DllImport("advapi32.dll")]
private static extern uint GetEffectiveRightsFromAcl(byte[] pacl, ref TRUSTEE pTrustee, ref uint pAccessRights);

private enum MULTIPLE_TRUSTEE_OPERATION
{
  NO_MULTIPLE_TRUSTEE,
  TRUSTEE_IS_IMPERSONATE
}

private enum TRUSTEE_FORM
{
  TRUSTEE_IS_SID,
  TRUSTEE_IS_NAME,
  TRUSTEE_BAD_FORM,
  TRUSTEE_IS_OBJECTS_AND_SID,
  TRUSTEE_IS_OBJECTS_AND_NAME
}

private enum TRUSTEE_TYPE
{
  TRUSTEE_IS_UNKNOWN,
  TRUSTEE_IS_USER,
  TRUSTEE_IS_GROUP,
  TRUSTEE_IS_DOMAIN,
  TRUSTEE_IS_ALIAS,
  TRUSTEE_IS_WELL_KNOWN_GROUP,
  TRUSTEE_IS_DELETED,
  TRUSTEE_IS_INVALID,
  TRUSTEE_IS_COMPUTER
}

private struct TRUSTEE
{
  public IntPtr pMultipleTrustee;
  public MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation;
  public TRUSTEE_FORM TrusteeForm;
  public TRUSTEE_TYPE TrusteeType;
  public IntPtr ptstrName;
}

[DllImport("advapi32.dll", SetLastError = true)]
private static extern void BuildTrusteeWithSid(
    ref TRUSTEE pTrustee,
    byte[] sid
);

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool QueryServiceObjectSecurity(SafeHandle serviceHandle, System.Security.AccessControl.SecurityInfos secInfo, byte[] lpSecDesrBuf, uint bufSize, out uint bufSizeNeeded);

[System.FlagsAttribute]
public enum ServiceAccessFlags : uint
{
  QueryConfig = 1,
  ChangeConfig = 2,
  QueryStatus = 4,
  EnumerateDependents = 8,
  Start = 16,
  Stop = 32,
  PauseContinue = 64,
  Interrogate = 128,
  UserDefinedControl = 256,
  Delete = 65536,
  ReadControl = 131072,
  WriteDac = 262144,
  WriteOwner = 524288,
  Synchronize = 1048576,
  AccessSystemSecurity = 16777216,
  GenericAll = 268435456,
  GenericExecute = 536870912,
  GenericWrite = 1073741824,
  GenericRead = 2147483648
}
olorin
  • 2,214
  • 3
  • 19
  • 22
  • Thank you very much! Just note for future readers, in order to check if you have given permission you need to have a permission to check if you have permission -- https://msdn.microsoft.com/en-us/library/windows/desktop/aa379312%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 see section "Remarks". – astrowalker Dec 21 '17 at 09:05