如何使用C#代码为特定用户(可能是NET SERVICE用户)授予启动和停止特定服务的权限。
我需要生成的代码才能处理从Windows XP到Windows 8的所有内容。
[编辑] 我已经有了一个可以运行的服务,以及一个设置文件夹权限,安装服务,启动它等的安装例程。
服务检查URL以查看是否有更新,如果有,则下载它,并启动更新程序以更新服务(并终止自身)。
更新程序更新服务exe(和其他所需文件),并需要重新启动服务。
我从研究中了解到,可以为服务用户(在这种情况下为NETWORK SERVICE)提供启动和停止单个服务的权限,但我不知道在代码中执行此操作的api。
答案 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());
}
}