我有一个网站,它启用了Windows身份验证.从网站中的页面,用户可以启动一项服务,该服务可以对数据库执行一些操作.
它适用于我启动服务,因为我是服务器上的本地管理员.但我只是有一个用户测试它,他们无法启动服务.
我的问题是:
有没有人知道如何通过名称使用与当前登录时不同的Windows帐户获取指定计算机上的服务列表?
我真的不想将需要启动服务的所有用户添加到一个Windows组中,并将它们全部设置为我的IIS服务器上的本地管理员.....
这是我得到的一些代码:
public static ServiceControllerStatus FindService() { ServiceControllerStatus status = ServiceControllerStatus.Stopped; try { string machineName = ConfigurationManager.AppSettings["ServiceMachineName"]; ServiceController[] services = ServiceController.GetServices(machineName); string serviceName = ConfigurationManager.AppSettings["ServiceName"].ToLower(); foreach (ServiceController service in services) { if (service.ServiceName.ToLower() == serviceName) { status = service.Status; break; } } } catch(Exception ex) { status = ServiceControllerStatus.Stopped; SaveError(ex, "Utilities - FindService()"); } return status; }
我的例外来自try块中的第二行.这是错误:
System.InvalidOperationException:无法在计算机'server.domain.com'上打开服务控制管理器.此操作可能需要其他权限.---> System.ComponentModel.Win32Exception:拒绝访问---内部异常堆栈跟踪的结束---在System.ServiceProcess.ServiceController.GetServicesOfType(String)处的System.ServiceProcess.ServiceController.GetDataBaseHandleWithAccess(String machineName,Int32 serviceControlManaqerAccess)处telemarketingWebSite.Utilities.StartService()上的machineName,Int32 serviceType)
感谢您的帮助/信息
注意:这并不是将服务枚举为不同的用户,但考虑到您正在做的更广泛的描述,我认为这是一个很好的答案.
如果您直接访问感兴趣的服务,我认为您可以简化这一过程,并可能避免部分安全问题.试试这个,而不是调用GetServices:
string machineName = ConfigurationManager.AppSettings["ServiceMachineName"]; string serviceName = ConfigurationManager.AppSettings["ServiceName"]; ServiceController service = new ServiceController( serviceName, machineName ); return service.Status;
这直接连接到感兴趣的服务并绕过枚举/搜索步骤.因此,它不需要调用者SC_MANAGER_ENUMERATE_SERVICE
在服务控制管理器(SCM)上拥有权限,默认情况下远程用户没有.它仍然需要SC_MANAGER_CONNECT
,但根据MSDN,应该授予远程认证用户.
一旦找到感兴趣的服务,您仍然需要能够停止并启动它,远程用户可能无权执行此操作.但是,可以修改单个服务上的安全描述符(DACL),这样您就可以授予远程用户访问权限以停止和启动服务,而无需他们成为本地管理员.这是通过SetNamedSecurityInfo API函数完成的.您需要授予的访问权限是SERVICE_START
和SERVICE_STOP
.根据这些用户所属的确切组,您可能还需要授予它们GENERIC_READ
.所有这些权利都在MSDN中描述.
这是一些将执行此设置的C++代码,假设感兴趣的用户位于"远程服务控制器"组(您将创建)中,并且服务名称为"my-service-name".请注意,如果您要授予对诸如用户(不一定是个好主意)之类的知名组的访问权限,而不是您创建的组,则需要更改TRUSTEE_IS_GROUP
为TRUSTEE_IS_WELL_KNOWN_GROUP
.
代码没有错误检查,您可以添加.可能失败的所有三个函数(Get/SetNamedSecurityInfo和SetEntriesInAcl)返回0表示成功.
另请注意:您还可以使用SC工具设置服务的安全描述符,该工具可以在%WINDIR%\ System32下找到,但不涉及任何编程.
#include "windows.h" #include "accctrl.h" #include "aclapi.h" int main() { char serviceName[] = "my-service-name"; char userGroup[] = "Remote Service Controllers"; // retrieve the security info PACL pDacl = NULL; PSECURITY_DESCRIPTOR pDescriptor = NULL; GetNamedSecurityInfo( serviceName, SE_SERVICE, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pDescriptor ); // add an entry to allow the users to start and stop the service EXPLICIT_ACCESS access; ZeroMemory( &access, sizeof(access) ); access.grfAccessMode = GRANT_ACCESS; access.grfAccessPermissions = SERVICE_START | SERVICE_STOP; access.Trustee.TrusteeForm = TRUSTEE_IS_NAME; access.Trustee.TrusteeType = TRUSTEE_IS_GROUP; access.Trustee.ptstrName = userGroup; PACL pNewDacl; SetEntriesInAcl( 1, &access, pDacl, &pNewDacl ); // write the changes back to the service SetNamedSecurityInfo( serviceName, SE_SERVICE, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDacl, NULL ); LocalFree( pNewDacl ); LocalFree( pDescriptor ); }
这也可以通过P/Invoke从C#完成,但这需要更多的工作.
如果您仍然特别希望能够以这些用户的身份枚举服务,则需要SC_MANAGER_ENUMERATE_SERVICE
在SCM上授予他们权限.不幸的是,根据MSDN,SCM的安全性只能在Windows Server 2003 sp1或更高版本上进行修改.