我有一个运行为mydomain\userA的Windows服务.我希望能够从服务中运行任意.exes.通常,我使用Process.Start()并且它工作正常,但在某些情况下我想以不同的用户(mydomain\userB)运行可执行文件.
如果我更改ProcessStartInfo,我用来启动进程以包含凭据,我开始收到错误 - 或者是一个错误对话框,显示"应用程序无法正确初始化(0xc0000142).单击确定以终止应用程序."或者"访问被拒绝"Win32Exception.如果我从命令行运行进程启动代码而不是在服务中运行它,则进程将使用正确的凭据开始(我已通过设置ProcessStartInfo来运行whoami.exe并捕获命令行输出来验证这一点) ).
我也尝试使用WindowsIdentity.Impersonate()进行模拟,但这没有用 - 据我所知,模拟只会影响当前线程,启动新进程会继承进程的安全描述符,而不是当前线程.
我在一个独立的测试域中运行它,因此userA和userB都是域管理员,并且两者都在域范围内具有"登录即服务"权限.
使用ProcessStartInfo启动新进程时,该进程将在启动过程的同一窗口站和桌面中启动.如果您使用不同的凭据,则用户通常没有足够的权限在该桌面上运行.如果user32.dll尝试在新进程中初始化而无法初始化错误,则会导致无法初始化错误.
要解决此问题,您必须首先检索与窗口站和桌面关联的安全描述符,并为您的用户添加适当的DACL权限,然后在新凭据下启动您的进程.
编辑:有关如何执行此操作和示例代码的详细说明在这里有点长,所以我将一篇文章与代码放在一起.
//The following security adjustments are necessary to give the new //process sufficient permission to run in the service's window station //and desktop. This uses classes from the AsproLock library also from //Asprosys. IntPtr hWinSta = GetProcessWindowStation(); WindowStationSecurity ws = new WindowStationSecurity(hWinSta, System.Security.AccessControl.AccessControlSections.Access); ws.AddAccessRule(new WindowStationAccessRule("LaunchProcessUser", WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ws.AcceptChanges(); IntPtr hDesk = GetThreadDesktop(GetCurrentThreadId()); DesktopSecurity ds = new DesktopSecurity(hDesk, System.Security.AccessControl.AccessControlSections.Access); ds.AddAccessRule(new DesktopAccessRule("LaunchProcessUser", DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow)); ds.AcceptChanges(); EventLog.WriteEntry("Launching application.", EventLogEntryType.Information); using (Process process = Process.Start(psi)) { }
根据@StephenMartin的回答.
使用Process
该类启动的新进程在启动过程中在相同的窗口站和桌面中运行.如果使用不同的凭据运行新进程,则新进程将无权访问窗口站和桌面.什么导致错误,如0xC0000142.
以下是一个"紧凑"独立代码,用于授予用户访问当前窗口站和桌面的权限.它不需要AsproLock库.
public static void GrantAccessToWindowStationAndDesktop(string username) { IntPtr handle; const int WindowStationAllAccess = 0x000f037f; handle = GetProcessWindowStation(); GrantAccess(username, handle, WindowStationAllAccess); const int DesktopRightsAllAccess = 0x000f01ff; handle = GetThreadDesktop(GetCurrentThreadId()); GrantAccess(username, handle, DesktopRightsAllAccess); } private static void GrantAccess(string username, IntPtr handle, int accessMask) { SafeHandle safeHandle = new NoopSafeHandle(handle); GenericSecurity security = new GenericSecurity( false, ResourceType.WindowObject, safeHandle, AccessControlSections.Access); security.AddAccessRule( new GenericAccessRule( new NTAccount(username), accessMask, AccessControlType.Allow)); security.Persist(safeHandle, AccessControlSections.Access); } [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetProcessWindowStation(); [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr GetThreadDesktop(int dwThreadId); [DllImport("kernel32.dll", SetLastError = true)] private static extern int GetCurrentThreadId(); // All the code to manipulate a security object is available in .NET framework, // but its API tries to be type-safe and handle-safe, enforcing a special implementation // (to an otherwise generic WinAPI) for each handle type. This is to make sure // only a correct set of permissions can be set for corresponding object types and // mainly that handles do not leak. // Hence the AccessRule and the NativeObjectSecurity classes are abstract. // This is the simplest possible implementation that yet allows us to make use // of the existing .NET implementation, sparing necessity to // P/Invoke the underlying WinAPI. private class GenericAccessRule : AccessRule { public GenericAccessRule( IdentityReference identity, int accessMask, AccessControlType type) : base(identity, accessMask, false, InheritanceFlags.None, PropagationFlags.None, type) { } } private class GenericSecurity : NativeObjectSecurity { public GenericSecurity( bool isContainer, ResourceType resType, SafeHandle objectHandle, AccessControlSections sectionsRequested) : base(isContainer, resType, objectHandle, sectionsRequested) { } new public void Persist(SafeHandle handle, AccessControlSections includeSections) { base.Persist(handle, includeSections); } new public void AddAccessRule(AccessRule rule) { base.AddAccessRule(rule); } #region NativeObjectSecurity Abstract Method Overrides public override Type AccessRightType { get { throw new NotImplementedException(); } } public override AccessRule AccessRuleFactory( System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AccessControlType type) { throw new NotImplementedException(); } public override Type AccessRuleType { get { return typeof(AccessRule); } } public override AuditRule AuditRuleFactory( System.Security.Principal.IdentityReference identityReference, int accessMask, bool isInherited, InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags, AuditFlags flags) { throw new NotImplementedException(); } public override Type AuditRuleType { get { return typeof(AuditRule); } } #endregion } // Handles returned by GetProcessWindowStation and GetThreadDesktop should not be closed private class NoopSafeHandle : SafeHandle { public NoopSafeHandle(IntPtr handle) : base(handle, false) { } public override bool IsInvalid { get { return false; } } protected override bool ReleaseHandle() { return true; } }