使用登录用户长时间运行的 Windows 服务

     2023-02-16     239

关键词:

【中文标题】使用登录用户长时间运行的 Windows 服务【英文标题】:Long Running Windows Service using LogOn User 【发布时间】:2015-01-26 19:32:07 【问题描述】:

我有一个长期运行的 Windows 服务。在其部署中,我们经常使用自定义域用户登录凭据来为服务提供附加权限。当服务正在运行时,服务正在运行的用户的凭据可能会更改或过期。我正在尝试编写一个功能来通知用户服务凭据已过期并且必须采取手动干预来更新登录凭据。

我的问题是,从在过期上下文下运行的服务中检测其上下文已过期的最佳方法是什么?

谢谢

【问题讨论】:

您目前如何检查context 的用户是否有效..?为什么你不能在该部分添加一些额外的代码来检查域用户的密码/登录是否有效.. 你在使用PrincipalContext along with AD 在某些情况下,我们使用自定义登录来访问网络驱动器。如果上下文已过期,则与网络位置的连接将失败。我希望有一种更通用的方法来测试服务上下文是否已过期。我无权访问用户名并通过,因为这是由 windows 处理的。 我认为这取决于...取决于您的服务是如何设置的,如果它查看您确实有权访问的域用户名\密码,但我不熟悉您当前的结构.. 你是熟悉 `PrincipalContext.. 或者您可以在不暴露您的实际域的情况下显示代码 sn-p 您的服务当前如何检查..? 我仍然找不到解决这个问题的方法。由此...msdn.microsoft.com/en-us/library/cc875826.aspx ..(管理服务帐户密码更改部分)“分配密码后,SCM 不会验证存储在该数据库中的密码,分配给 Active Directory 中用户帐户的密码将继续匹配。”似乎一旦进程运行,AD 就会正常工作,并且无法知道上下文已过期。这很令人沮丧,因为如果系统重新启动,服务将无法启动,并且不会向用户发出警告。 【参考方案1】:

我会采取不同的方法,将服务留给受限用户,例如网络服务。 然后从服务中模拟当前登录的用户,这很棘手,因为可能没有或不止一个用户登录。这个answer 提供sample code 来复制令牌。

最后使用那个重复的令牌来访问网络共享,比如here。

编辑: 以下代码在作为本地系统帐户运行时有效:

class AccessShareAsLoggedOnUser

    public static bool CopyAsLocalUser(String sourceFile, string targetFile)
    
        uint dwSessionId, winlogonPid = 0;
        IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;

        Debug.Print("CreateProcessInConsoleSession");
        // Log the client on to the local computer.
        dwSessionId = WTSGetActiveConsoleSessionId();

        // Find the winlogon process
        var procEntry = new PROCESSENTRY32();

        uint hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnap == INVALID_HANDLE_VALUE)
        
            return false;
        

        procEntry.dwSize = (uint)Marshal.SizeOf(procEntry); //sizeof(PROCESSENTRY32);

        if (Process32First(hSnap, ref procEntry) == 0)
        
            return false;
        

        String strCmp = "explorer.exe";
        do
        
            if (strCmp.IndexOf(procEntry.szExeFile) == 0)
            
                // We found a winlogon process...make sure it's running in the console session
                uint winlogonSessId = 0;
                if (ProcessIdToSessionId(procEntry.th32ProcessID, ref winlogonSessId) &&
                    winlogonSessId == dwSessionId)
                
                    winlogonPid = procEntry.th32ProcessID;
                    break;
                
            
        
        while (Process32Next(hSnap, ref procEntry) != 0);

        //Get the user token used by DuplicateTokenEx
        WTSQueryUserToken(dwSessionId, ref hUserToken);

        hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

        if (
            !OpenProcessToken(hProcess,
                TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY
                | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE, ref hPToken))
        
            Debug.Print(String.Format("CreateProcessInConsoleSession OpenProcessToken error: 0",
                Marshal.GetLastWin32Error()));
        

        var sa = new SECURITY_ATTRIBUTES();
        sa.Length = Marshal.SizeOf(sa);

        if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa,
                (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary,
                ref hUserTokenDup))
        
            Debug.Print(
                String.Format(
                    "CreateProcessInConsoleSession DuplicateTokenEx error: 0 Token does not have the privilege.",
                    Marshal.GetLastWin32Error()));
            CloseHandle(hProcess);
            CloseHandle(hUserToken);
            CloseHandle(hPToken);
            return false;
        

        try
        
            using (WindowsImpersonationContext impersonationContext =
                new WindowsIdentity(hUserTokenDup).Impersonate())
            
            // Put your network Code here.
                File.WriteAllText(targetFile // Somewhere with right permissions like @"C:\Users\xxx\output.txt"
                      , File.ReadAllText(sourceFile)); // like @"\\server\share\existingfile.txt"

                impersonationContext.Undo();
            
            return true;
        
        finally
        
            if (hUserTokenDup != IntPtr.Zero)
            
                if (!CloseHandle(hUserTokenDup))
                
                    // Uncomment if you need to know this case.
                    ////throw new Win32Exception();
                
            

            //Close handles task
            CloseHandle(hProcess);
            CloseHandle(hUserToken);
            CloseHandle(hUserTokenDup);
            CloseHandle(hPToken);
        
    

    [DllImport("kernel32.dll")]
    private static extern int Process32First(uint hSnapshot, ref PROCESSENTRY32 lppe);

    [DllImport("kernel32.dll")]
    private static extern int Process32Next(uint hSnapshot, ref PROCESSENTRY32 lppe);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern uint CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hSnapshot);

    [DllImport("kernel32.dll")]
    private static extern uint WTSGetActiveConsoleSessionId();

    [DllImport("Wtsapi32.dll")]
    private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);

    [DllImport("kernel32.dll")]
    private static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);

    [DllImport("kernel32.dll")]
    private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

    [DllImport("advapi32", SetLastError = true)]
    [SuppressUnmanagedCodeSecurity]
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, // handle to process
        int DesiredAccess, // desired access to process
        ref IntPtr TokenHandle);

    [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
    public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
        ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
        int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);


    #region Nested type: PROCESSENTRY32

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESSENTRY32
    
        public uint dwSize;
        public readonly uint cntUsage;
        public readonly uint th32ProcessID;
        public readonly IntPtr th32DefaultHeapID;
        public readonly uint th32ModuleID;
        public readonly uint cntThreads;
        public readonly uint th32ParentProcessID;
        public readonly int pcPriClassBase;
        public readonly uint dwFlags;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public readonly string szExeFile;
    

    #endregion


    #region Nested type: SECURITY_ATTRIBUTES

    [StructLayout(LayoutKind.Sequential)]
    public struct SECURITY_ATTRIBUTES
    
        public int Length;
        public IntPtr lpSecurityDescriptor;
        public bool bInheritHandle;
    

    #endregion

    #region Nested type: SECURITY_IMPERSONATION_LEVEL

    private enum SECURITY_IMPERSONATION_LEVEL
    
        SecurityAnonymous = 0,
        SecurityIdentification = 1,
        SecurityImpersonation = 2,
        SecurityDelegation = 3,
    

    #endregion


    #region Nested type: TOKEN_TYPE

    private enum TOKEN_TYPE
    
        TokenPrimary = 1,
        TokenImpersonation = 2
    

    #endregion

    public const int READ_CONTROL = 0x00020000;

    public const int STANDARD_RIGHTS_REQUIRED = 0x000F0000;

    public const int STANDARD_RIGHTS_READ = READ_CONTROL;
    public const int STANDARD_RIGHTS_WRITE = READ_CONTROL;
    public const int STANDARD_RIGHTS_EXECUTE = READ_CONTROL;

    public const int STANDARD_RIGHTS_ALL = 0x001F0000;

    public const int SPECIFIC_RIGHTS_ALL = 0x0000FFFF;

    public const int TOKEN_ASSIGN_PRIMARY = 0x0001;
    public const int TOKEN_DUPLICATE = 0x0002;
    public const int TOKEN_IMPERSONATE = 0x0004;
    public const int TOKEN_QUERY = 0x0008;
    public const int TOKEN_QUERY_SOURCE = 0x0010;
    public const int TOKEN_ADJUST_PRIVILEGES = 0x0020;
    public const int TOKEN_ADJUST_GROUPS = 0x0040;
    public const int TOKEN_ADJUST_DEFAULT = 0x0080;
    public const int TOKEN_ADJUST_SESSIONID = 0x0100;

    public const int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY;

    public const int TOKEN_WRITE = STANDARD_RIGHTS_WRITE |
                                   TOKEN_ADJUST_PRIVILEGES |
                                   TOKEN_ADJUST_GROUPS |
                                   TOKEN_ADJUST_DEFAULT;

    public const uint MAXIMUM_ALLOWED = 0x2000000;

    private const uint TH32CS_SNAPPROCESS = 0x00000002;

    public static int INVALID_HANDLE_VALUE = -1;

    // handle to open access token

【讨论】:

我不太喜欢这个,因为它要求服务能够访问密码才能进行提升。这个密码需要存储在网络服务可以访问的地方,因此我觉得这会引入一个安全漏洞。如果您通过 kerberos 之类的方式进行提升,则需要将该控制委托给网络服务,这又会导致一些安全问题 不,它没有。第一个链接在不知道密码的情况下复制登录用户的令牌。第二个链接使用该令牌访问网络共享。无需密码。 此链接有一种更简洁的方式来获取登录用户的令牌。它使用 2 个 API 调用,您可以轻松地将它们从 c++ 转换为 c#。 ***.com/a/19796825/97471 不幸的是,这种方法对我不起作用。我的 Windows 服务经常在服务器上运行,并且没有用户登录。这就是我们使用 Windows 服务 - Windows 提供的运行方式功能的原因。

c#创建一个window服务(代码片段)

Window服务介绍MicrosoftWindows服务能够创建在它们自己的Windows会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。这使服务非常适合在服务器上使用,或... 查看详情

windowsservice编程

1基本概念1.1windows服务简介创建在它们自己的Windows会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。 1.2适用情形适合在服务器上使用且不需要界... 查看详情

在后台IOS实现长时间运行的任务

...:02【问题描述】:我一直在开发一个应用程序,用户可以使用AVFoundation录制视频并发送到服务器,视频的最大大小为15M,具体取决于互联网速度和类型,大约需要1到5分钟将视频传输到服务器。我正在后台线程中将录制的视频传... 查看详情

当用户关闭浏览器等待长时间运行的 Web 服务调用时会发生啥?

】当用户关闭浏览器等待长时间运行的Web服务调用时会发生啥?【英文标题】:Whathappenswhenauserclosestheirbrowserwaitingforalongrunningwebservicecall?当用户关闭浏览器等待长时间运行的Web服务调用时会发生什么?【发布时间】:2010-09-2901:34... 查看详情

Redshift:检测并取消每个用户连接的长时间运行的查询

...询。我想为特定用户检测并取消长时间运行的查询。如何使用脚本自动执行此过程?【问题讨论】:您最终是否使用了WLM队列规则?我注意到M 查看详情

是否可以使用 iBATIS 中止长时间运行的查询?

】是否可以使用iBATIS中止长时间运行的查询?【英文标题】:IsitpossibletoabortalongrunningqueryusingiBATIS?【发布时间】:2013-02-0407:03:06【问题描述】:我有一个GUI,它允许用户运行长时间运行的查询。有时,用户会后悔运行查询并想取... 查看详情

以与长时间运行的 Python 进程不同的用户身份运行子进程

...长时间运行的守护程序Python进程,当某些事件发生时,它使用子进程生成新的子进程。长时间运行的进程由具有超级用户权限的用户启动。我需要它生成的子进程作为不同的用户(例如“n 查看详情

如何在长时间运行的服务器操作期间与用户交互(例如确认对话框)?

】如何在长时间运行的服务器操作期间与用户交互(例如确认对话框)?【英文标题】:Howtointeractwithuser(forexampleaconfirmdialog)duringalongrunningserveraction?【发布时间】:2015-12-1809:56:07【问题描述】:我有一个MVC5应用程序。有一个处... 查看详情

接收来自 emqx 推送消息的长时间运行服务

...自emqx服务器的推送消息(即使程序没有运行)。如果我使用androidService或JobIntentService,则有必要显示一个非预期的通知。如果我使用WorkManager,可以 查看详情

使用异步服务器的长时间运行任务

】使用异步服务器的长时间运行任务【英文标题】:Long-runningtaskswithasyncserver【发布时间】:2016-05-2304:53:58【问题描述】:我想每个人都知道如何处理django中长时间运行的任务:使用celery并放松。但是如果我想通过aiohttp(或tornad... 查看详情

使用 SignalR 从长时间运行的进程推送到客户端

...在数据库中。然后将响应发送回客户端。目前,我有一个Windows服务轮询 查看详情

取消长时间运行的下载

...时间】:2015-01-0921:59:29【问题描述】:我在NGINX上有一个使用PHP服务器端语言的Web应用程序。通过JS,用户可以从非常慢的数据库中下载由PHP动态构建的文件。因此,接收第一个字节进行打印大约需要20秒。然后,更多的数据在之... 查看详情

如何使用 Ninject 在长时间运行的进程中最好地处理 Sql 连接?

...4:16:42【问题描述】:我们有一个AzureWorker角色(基本上与Windows服务相同),我们将Ninject用于IoC,并将IDbConnection注入到我们的worker中。最佳实践 查看详情

在长时间运行期间泵送 Windows 消息?

】在长时间运行期间泵送Windows消息?【英文标题】:PumpingWindowsMessagesDuringLongOperation?【发布时间】:2011-06-1522:59:52【问题描述】:我正在运行的大型操作中收到以下消息:CLR无法转换从COM上下文0x1fe458到COM上下文0x1fe5c8持续60秒... 查看详情

对长时间运行的通知通道使用 ServerStreaming rpc 调用

】对长时间运行的通知通道使用ServerStreamingrpc调用【英文标题】:UsingaServerStreamingrpccallforlongrunningnotificationschannel【发布时间】:2019-07-0315:30:24【问题描述】:我正在考虑使用gRPC服务来促进两个服务之间的通知。(顺便说一句,... 查看详情

长时间运行任务的 Grails 状态更新

...我有一个长时间运行的任务(>1.5分钟),我希望能够使用ajax向用户提供完成状态。这是我目前正在做的事情:我正在使用ajax处理上传(使用ajax上传插件),并且在任务运行时显示繁忙标签。我希望能够显示完成百分比(不... 查看详情

具有长时间运行任务的单独线程中的计时器

...ask【发布时间】:2014-05-2710:15:12【问题描述】:我有一个Windows服务,它在start方法中创建一个计时器并触发计时器立即执行。计时器是一项长时间运行的任务,因此services.msc中的启动服务会被检测为错误。它认为下面的计时器在... 查看详情

在 Windows 窗体应用程序中查找长时间运行代码的最佳方法是啥

】在Windows窗体应用程序中查找长时间运行代码的最佳方法是啥【英文标题】:What\'sthebestwaytofindlong-runningcodeinaWindowsFormsApplication在Windows窗体应用程序中查找长时间运行代码的最佳方法是什么【发布时间】:2010-09-0511:01:52【问题... 查看详情