C# 使用来自Windows服务的凭据启动进程
我有一个作为mydomain\userA运行的Windows服务。我希望能够从服务中运行任意.exe。通常,我使用Process.Start(),它工作正常,但在某些情况下,我希望以不同的用户(mydomain\userB)运行可执行文件 如果我更改用于启动进程的ProcessStartInfo以包含凭据,则会出现错误-显示“应用程序未能正确初始化(0xc0000142)。单击“确定”终止应用程序”的错误对话框,或者显示“访问被拒绝”Win32Exception。如果我从命令行运行进程启动代码而不是在服务中运行它,则进程将使用正确的凭据启动(我已通过将ProcessStartInfo设置为运行whoami.exe并捕获命令行输出来验证这一点) 我还尝试过使用WindowsIdentity.Impersonate()进行模拟,但这不起作用-据我所知,模拟只影响当前线程,启动新进程会继承进程的安全描述符,而不是当前线程C# 使用来自Windows服务的凭据启动进程,c#,.net,windows-services,C#,.net,Windows Services,我有一个作为mydomain\userA运行的Windows服务。我希望能够从服务中运行任意.exe。通常,我使用Process.Start(),它工作正常,但在某些情况下,我希望以不同的用户(mydomain\userB)运行可执行文件 如果我更改用于启动进程的ProcessStartInfo以包含凭据,则会出现错误-显示“应用程序未能正确初始化(0xc0000142)。单击“确定”终止应用程序”的错误对话框,或者显示“访问被拒绝”Win32Exception。如果我从命令行运行进程启动代码而
我在一个独立的测试域中运行这个程序,因此userA和userB都是域管理员,并且都具有作为服务登录的权限。如何设置域、用户和密码?您是否正确设置了域以及密码(它必须使用SecureString)
另外,您是否正在设置WorkingDirectory属性?当使用用户名和密码时,文档说明必须设置WorkingDirectory属性。这表明:
-权利不足
-图书馆的故障负荷
使用Filemon检测某些访问被拒绝或
WinDbg在调试器中运行应用程序并查看任何问题。可能是由服务启动的任何进程也必须具有“作为服务登录”权限 如果用于启动第二个进程的用户id没有该框的管理权限,则可能是这种情况 一个简单的测试是更改本地安全策略,给用户标识“作为服务登录”,然后重试 编辑:在附加信息之后
在这一点上浏览Google,似乎0xc0000142与无法初始化所需的DLL有关。服务是否打开了衍生流程需要的某些内容?在任何情况下,它看起来都与启动的流程有关,而与您的操作方式无关。当您使用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))
{
}
我今天遇到了这个问题,我花了相当长的时间试图解决它。我最后做的是将服务创建为交互式的(使用services.msc中的“允许服务与桌面交互”复选框)。当我这样做时,0xc0000142错误就消失了。基于 使用
进程
类启动的新进程与启动进程在同一窗口站和桌面上运行。如果使用不同的凭据运行新进程,则新进程将没有访问窗口站和桌面的权限。导致0xC0000142等错误的原因
以下是授予用户访问当前窗口站和桌面的“压缩”独立代码。它不需要AsproLock库
在调用Process.Start
之前,使用用于运行进程的用户名调用GrantAccessToWindowStationAndDesktop
方法(Process.StartInfo.username
)
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;
}
}
基于@Stephen Martin和Martin Prikryl的回答
此代码帮助您使用来自服务的不同用户凭据运行进程。
我现在已经优化了源代码。
现在也可以取消和设定权利
名称空间QLIKConnectorPSecute
{
#区域使用
使用制度;
使用System.Collections.Generic;
使用System.Linq;
使用System.Runtime.InteropServices;
使用System.Security.AccessControl;
使用System.Security.Principal;
#端区
//灵感来自:http://stackoverflow.com/questions/677874/starting-a-process-with-credentials-from-a-windows-service
公共类访问:IDisposable
{
#区域DLL导入
//操作安全对象的所有代码都可以在.NET framework中使用,
//但是它的API试图做到类型安全和处理安全,强制执行一个特殊的实现
//(对于其他通用WinAPI)的每个句柄类型。这是为了确保
//只能为相应的对象类型和类型设置正确的权限集
//主要是手柄不会泄漏。
//因此AccessRule和NativeObjectSecurity类是抽象的。
//这是允许我们使用的最简单的实现
//现有的.NET实现,无需
//P/调用底层WinAPI。
[DllImport(“user32.dll”,SetLastError=true)]
私有静态外部IntPtr getProcessWindowsStation();
[DllImport(“user32.dll”,SetLastError=true)]
私有静态外部IntPtr GetThreadDesktop(intdwthreadid);
[DllImport(“kernel32.dll”
# Import .NET objects using pythonnet
from System.Diagnostics import Process
# Use .NET API to run a subprocess using the given executable
# as the target user, in the provided working directory.
process = Process()
process.StartInfo.UseShellExecute = False
process.StartInfo.CreateNoWindow = True
process.StartInfo.LoadUserProfile = True
process.StartInfo.RedirectStandardOutput = True
process.StartInfo.RedirectStandardError = True
process.StartInfo.WorkingDirectory = working_dir
process.StartInfo.Domain = "mydomain"
process.StartInfo.UserName = username.lower().replace("mydomain\\", "")
process.StartInfo.PasswordInClearText = password
process.StartInfo.FileName = executable
process.StartInfo.Arguments = " ".join(args)
# Run the subprocess.
process.Start()
# Read subprocess console output
stdout = process.StandardOutput.ReadToEnd()
stderr = process.StandardError.ReadToEnd()
log.info(f"\n{executable} subprocess stdout:\n\n{stdout}")
log.info(f"{executable} subprocess stderr:\n\n{stderr}")
log.info(f"Done running {executable} as {username}.")
import win32api, win32process, win32service, win32security
WINDOW_STATION_ALL_ACCESS = 983935
DESKTOP_RIGHTS_ALL_ACCESS = 983551
SE_WINDOW_OBJECT = 7
DACL_SECURITY_INFORMATION = 4
def set_access(user, handle, access):
info = win32security.GetSecurityInfo(
handle, SE_WINDOW_OBJECT, DACL_SECURITY_INFORMATION
)
dacl = info.GetSecurityDescriptorDacl()
dacl.AddAccessAllowedAce(win32security.ACL_REVISION, access, user)
win32security.SetSecurityInfo(
handle, SE_WINDOW_OBJECT, DACL_SECURITY_INFORMATION, None, None, dacl, None
)
username = "mattsegal"
user, domain, user_type = win32security.LookupAccountName("", username)
thread_id = win32api.GetCurrentThreadId()
station_handle = win32process.GetProcessWindowStation()
desktop_handle = win32service.GetThreadDesktop(thread_id)
set_access(user, station_handle, WINDOW_STATION_ALL_ACCESS)
set_access(user, desktop_handle, DESKTOP_RIGHTS_ALL_ACCESS)