如何使用C#代码授予用户启动和停止特定服务的权限

时间:2013-04-02 18:49:48

标签: c# service permissions

如何使用C#代码为特定用户(可能是NET SERVICE用户)授予启动和停止特定服务的权限。

我需要生成的代码才能处理从Windows XP到Windows 8的所有内容。

[编辑] 我已经有了一个可以运行的服务,以及一个设置文件夹权限,安装服务,启动它等的安装例程。

服务检查URL以查看是否有更新,如果有,则下载它,并启动更新程序以更新服务(并终止自身)。

更新程序更新服务exe(和其他所需文件),并需要重新启动服务。

我从研究中了解到,可以为服务用户(在这种情况下为NETWORK SERVICE)提供启动和停止单个服务的权限,但我不知道在代码中执行此操作的api。

1 个答案:

答案 0 :(得分:7)

我从其他地方得到了一些线索,并设法弄明白:

[StructLayoutAttribute(LayoutKind.Sequential)]
struct SECURITY_DESCRIPTOR {
    public byte revision;
    public byte size;
    public short control;
    public IntPtr owner;
    public IntPtr group;
    public IntPtr sacl;
    public IntPtr dacl;
}

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

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

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool SetServiceObjectSecurity(SafeHandle serviceHandle, 
    System.Security.AccessControl.SecurityInfos secInfos, 
    byte[] lpSecDesrBuf);

public void SetServicePermissions(string service, string username) {
    System.ServiceProcess.ServiceController sc = new System.ServiceProcess.ServiceController(service, ".");
    ServiceControllerStatus status = sc.Status;
    byte[] psd = new byte[0];
    uint bufSizeNeeded;
    bool ok = QueryServiceObjectSecurity(sc.ServiceHandle, SecurityInfos.DiscretionaryAcl, psd, 0, out bufSizeNeeded);
    if (!ok) {
        int err = Marshal.GetLastWin32Error();
        if (err == 122 || err == 0) { // ERROR_INSUFFICIENT_BUFFER
            // expected; now we know bufsize
            psd = new byte[bufSizeNeeded];
            ok = QueryServiceObjectSecurity(sc.ServiceHandle, SecurityInfos.DiscretionaryAcl, psd, bufSizeNeeded, out bufSizeNeeded);
        } else {
            throw new ApplicationException("error calling QueryServiceObjectSecurity() to get DACL for " + _name + ": error code=" + err);
        }
    }
    if (!ok)
        throw new ApplicationException("error calling QueryServiceObjectSecurity(2) to get DACL for " + _name + ": error code=" + Marshal.GetLastWin32Error());

    // get security descriptor via raw into DACL form so ACE
    // ordering checks are done for us.
    RawSecurityDescriptor rsd = new RawSecurityDescriptor(psd, 0);
    RawAcl racl = rsd.DiscretionaryAcl;
    DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, racl);

    // Add start/stop/read access
    NTAccount acct = new NTAccount(username);
    SecurityIdentifier sid = (SecurityIdentifier) acct.Translate(typeof(SecurityIdentifier));
    // 0xf7 is SERVICE_QUERY_CONFIG|SERVICE_CHANGE_CONFIG|SERVICE_QUERY_STATUS|
    // SERVICE_START|SERVICE_STOP|SERVICE_PAUSE_CONTINUE|SERVICE_INTERROGATE
    dacl.AddAccess(AccessControlType.Allow, sid, 0xf7, InheritanceFlags.None, PropagationFlags.None);

    // convert discretionary ACL back to raw form; looks like via byte[] is only way
    byte[] rawdacl = new byte[dacl.BinaryLength];
    dacl.GetBinaryForm(rawdacl, 0);
    rsd.DiscretionaryAcl = new RawAcl(rawdacl, 0);

    // set raw security descriptor on service again
    byte[] rawsd = new byte[rsd.BinaryLength];
    rsd.GetBinaryForm(rawsd, 0);
    ok = SetServiceObjectSecurity(sc.ServiceHandle, SecurityInfos.DiscretionaryAcl, rawsd);
    if (!ok) {
        throw new ApplicationException("error calling SetServiceObjectSecurity(); error code=" + Marshal.GetLastWin32Error());
    }
}