我有一个使用Delphi创建的服务应用程序,并设法使用提升的权限从另一个Delphi应用程序安装它。
该服务设置为以本地系统帐户登录(在Delphi中创建服务应用程序时是默认帐户)。
我有另一个Delphi应用程序,普通用户应该能够启动或停止上述服务。
我的问题是:Windows允许这样做吗?当我尝试使用Delphi中的代码启动服务时,它只是失败了“Code 5. Access被拒绝”。如何防止发生此错误?我希望能够让普通用户运行应用程序(不是在管理员模式下,因此没有提升权限)来启动/停止服务。有可能,如果可能,怎么样?以下是我的代码:
function ServiceStart(sMachine, sService: string): boolean;
var
schm, schs: SC_Handle;
ss: TServiceStatus;
psTemp: PChar;
dwChkP: DWord; // check point
begin
ss.dwCurrentState := 0;
// connect to the service control manager
schm := OpenSCManager(PChar(sMachine), nil, SC_MANAGER_CONNECT);
// if successful...
if (schm <> 0) then
begin
// open a handle to the specified service
// we want to start the service and query service
// status
schs := OpenService(schm, PChar(sService), SERVICE_START or SERVICE_QUERY_STATUS);
// if successful...
if (schs <> 0) then
begin
psTemp := nil;
if (StartService(schs, 0, psTemp)) then
begin
// check status
if (QueryServiceStatus(schs, ss)) then
begin
while (SERVICE_RUNNING <> ss.dwCurrentState) do
begin
// dwCheckPoint contains a value that the
// service increments periodically to
// report its progress during a
// lengthy operation. Save current value
dwChkP := ss.dwCheckPoint;
// wait a bit before checking status again
// dwWaitHint is the estimated amount of
// time the calling program should wait
// before calling QueryServiceStatus()
// again. Idle events should be
// handled here...
Sleep(ss.dwWaitHint);
if not QueryServiceStatus(schs, ss) then
begin
// couldn't check status break from the
// loop
break;
end;
if ss.dwCheckPoint < dwChkP then
begin
// QueryServiceStatus didn't increment
// dwCheckPoint as it should have.
// Avoid an infinite loop by breaking
break;
end;
end;
end;
end
else
begin
if MessageDlg('Start Service failed. Do you want remove it?',
mtWarning, [mbYes, mbNo], 0) = mrYes then
begin
InstallUninstallService(1);
end;
end;
// close service handle
CloseServiceHandle(schs);
end else RaiseLastOSError;
// close service control manager handle
CloseServiceHandle(schm);
end;
// Return TRUE if the service status is running
Result := SERVICE_RUNNING = ss.dwCurrentState;
end;
答案 0 :(得分:6)
默认情况下,您需要拥有管理员权限才能启动,停止,安装和删除服务。
您必须安排自己的服务来公开自己的Active属性,这与Windows术语的运行区别不同。安排它一直以Windows术语运行,但在Active属性为false时是惰性的。
您必须为您的用户应用实施控制机制。我已经使用命名管道完成了这项工作,但其他IPC方法也是可用的。
答案 1 :(得分:3)
服务hava ACL作为其他Windows对象。在域中,可以使用组策略向用户分配启动/停止服务的权限(在“计算机配置” - >“策略” - >“Windows设置” - >“安全设置” - >“系统服务”下)。对于本地系统,AFAIK此节点不会出现在gpedit.msc中。 sc.exe可用于设置服务安全描述符,但它需要一些SDDL知识(安全描述符定义语言,this may help you)。不知道是否有一个模板可以让gpedit处理它 - 这个用户没有管理员帐户的独立系统有点不常见。
答案 2 :(得分:2)
有几种方法可以实现这一目标:
这必须在提升模式下完成,例如在创建服务时。
请注意Windows access control model很难处理。也许JEDI Windows Security Library可以帮到你。
可以将权限授予单个用户,用户组或预定义的用户组,例如经过身份验证的用户。
要执行此操作,您需要为服务创建access control list。服务有几个access rights,为此您需要在acl中包含 SERVICE_START 和 SERVICE_STOP 。
使用SetSecurityDescriptorDacl和SetServiceObjectSecurity api函数应用acl。以下是how to use them的C中的示例。
必须使用所需Trustee填充EXPLICIT_ACCESS结构的SID变量,以指定用户或组。以下是C中显示how to do it的另一个示例。
注意: Microsoft有一个名为SubInACL的实用程序,可用于查询和设置所有类型的acls,但我猜它不是可再发行的。这是关于how to use it的小型教程。
您可以使用其他服务来响应命令并控制主服务。该监护人应该以 LocalSystem 的形式运行。 LocalSystem具有 SERVICE_START 和 SERVICE_STOP 权限,因此无需为监护人设置任何acl。
监护人还允许您通过简单地停止,更新和重新启动主服务来进行自动更新。
请注意LocalSystem是某种本地管理员,因此使用它是一种安全风险,如here所述。
作为David Heffernan says,不需要启动和停止服务,因为类似的行为可以通过暴露命令来实现,这些命令指示它在内部停用并独立于窗口的思考而激活。