Starting a process with credentials from a Windows Service

后端 未结 8 1512
情话喂你
情话喂你 2020-11-27 05:42

I have a Windows service that runs as mydomain\\userA. I want to be able to run arbitrary .exes from the service. Normally, I use Process.Start() and it works fine, but in s

相关标签:
8条回答
  • 2020-11-27 06:26

    Based on the answer by @StephenMartin.

    A new process launched using the Process class runs in the same window station and desktop as the launching process. If you are running the new process using different credentials, then the new process won't have permissions to access the window station and desktop. What results in errors like 0xC0000142.

    The following is a "compact" standalone code to grant a user an access to the current window station and desktop. It does not require the AsproLock library.

    Call the GrantAccessToWindowStationAndDesktop method with the username you use to run the Process (Process.StartInfo.UserName), before calling Process.Start.

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-27 06:30

    I had this problem today, and I spent quite some time trying to figure it out. What I ended up doing was to create the service as interactive (using the Allow service to interact with desktop checkbox in services.msc). As soon as I did that the 0xc0000142 errors went away.

    0 讨论(0)
提交回复
热议问题