Starting a process with credentials from a Windows Service

后端 未结 8 1579
情话喂你
情话喂你 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:06

    Based on the answer by @Stephen Martin and Martin Prikryl.

    This code helps you to run a process with different user credentials from a service.
    I have now optimized the source code.
    The removal and setting of rights is now also possible.

    namespace QlikConnectorPSExecute
    {
        #region Usings
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Runtime.InteropServices;
        using System.Security.AccessControl;
        using System.Security.Principal;
        #endregion
    
        //inspired by: http://stackoverflow.com/questions/677874/starting-a-process-with-credentials-from-a-windows-service
        public class WindowsGrandAccess : IDisposable
        {
            #region DLL-Import
            // 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.
    
            [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();
            #endregion
    
            #region Variables && Properties
            public static int WindowStationAllAccess { get; private set; } = 0x000f037f;
            public static int DesktopRightsAllAccess { get; private set; } = 0x000f01ff;
    
            private GenericSecurity WindowStationSecurity {get; set;}
            private GenericSecurity DesktopSecurity { get; set; }
            private int? OldWindowStationMask { get; set; }
            private int? OldDesktopMask { get; set; }
            private NTAccount AccountInfo { get; set; }
            private SafeHandle WsSafeHandle { get; set; }
            private SafeHandle DSafeHandle { get; set; }
            #endregion
    
            #region Constructor & Dispose
            public WindowsGrandAccess(NTAccount accountInfo, int windowStationMask, int desktopMask)
            {
                if (accountInfo != null)
                    Init(accountInfo, windowStationMask, desktopMask);
            }
    
            public void Dispose()
            {
                try
                {
                    if (AccountInfo == null)
                        return;
    
                    RestAccessMask(OldWindowStationMask, WindowStationAllAccess, WindowStationSecurity, WsSafeHandle);
                    RestAccessMask(OldDesktopMask, DesktopRightsAllAccess, DesktopSecurity, DSafeHandle);
                }
                catch (Exception ex)
                {
                    throw new Exception($"The object \"{nameof(WindowsGrandAccess)}\" could not be dispose.", ex);
                }
            }
            #endregion
    
            #region Methods
            private void Init(NTAccount accountInfo, int windowStationMask, int desktopMask)
            {
                AccountInfo = accountInfo;
    
                WsSafeHandle = new NoopSafeHandle(GetProcessWindowStation());
                WindowStationSecurity = new GenericSecurity(false, ResourceType.WindowObject, WsSafeHandle, AccessControlSections.Access);
    
                DSafeHandle = new NoopSafeHandle(GetThreadDesktop(GetCurrentThreadId()));
                DesktopSecurity = new GenericSecurity(false, ResourceType.WindowObject, DSafeHandle, AccessControlSections.Access);
    
                OldWindowStationMask = ReadAccessMask(WindowStationSecurity, WsSafeHandle, windowStationMask);
                OldDesktopMask = ReadAccessMask(DesktopSecurity, DSafeHandle, desktopMask);
            }
    
            private AuthorizationRuleCollection GetAccessRules(GenericSecurity security)
            {
                return security.GetAccessRules(true, false, typeof(NTAccount));
            }
    
            private int? ReadAccessMask(GenericSecurity security, SafeHandle safeHandle, int accessMask)
            {
                var ruels = GetAccessRules(security);
    
                var username = AccountInfo.Value;
                if (!username.Contains("\\"))
                    username = $"{Environment.MachineName}\\{username}";
    
                var userResult = ruels.Cast().SingleOrDefault(r => r.IdentityReference.Value.ToLower() == username.ToLower() && accessMask == r.PublicAccessMask);
                if (userResult == null)
                {
                    AddGrandAccess(security, accessMask, safeHandle);
                    userResult = ruels.Cast().SingleOrDefault(r => r.IdentityReference.Value.ToLower() == username.ToLower());
                    if (userResult != null)
                        return userResult.PublicAccessMask;
                }
                else
                  return userResult.PublicAccessMask;
    
                return null;
            }
    
            private void AddGrandAccess(GenericSecurity security, int accessMask, SafeHandle safeHandle)
            {
                var rule = new GrantAccessRule(AccountInfo, accessMask, AccessControlType.Allow);
                security.AddAccessRule(rule);
                security.Persist(safeHandle, AccessControlSections.Access);
            }
    
            private void RemoveGrantAccess(GenericSecurity security, int accessMask, SafeHandle safeHandle)
            {
                var rule = new GrantAccessRule(AccountInfo, accessMask, AccessControlType.Allow);
                security.RemoveAccessRule(rule);
                security.Persist(safeHandle, AccessControlSections.Access);
            }
    
            private void SetGrandAccess(GenericSecurity security, int accessMask, SafeHandle safeHandle)
            {
                var rule = new GrantAccessRule(AccountInfo, accessMask, AccessControlType.Allow);
                security.SetAccessRule(rule);
                security.Persist(safeHandle, AccessControlSections.Access);
            }
    
            private void RestAccessMask(int? oldAccessMask, int fullAccessMask, GenericSecurity security, SafeHandle safeHandle)
            {
                if (oldAccessMask == null)
                    RemoveGrantAccess(security, fullAccessMask, safeHandle);
                else if (oldAccessMask != fullAccessMask)
                {
                    SetGrandAccess(security, oldAccessMask.Value, safeHandle);
                }
            }
            #endregion
    
            #region private classes
            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);
                }
    
                new public bool RemoveAccessRule(AccessRule rule)
                {
                    return base.RemoveAccessRule(rule);
                }
    
                new public void SetAccessRule(AccessRule rule)
                {
                    base.SetAccessRule(rule);
                }
    
                new public AuthorizationRuleCollection GetAccessRules(bool includeExplicit, bool includeInherited, Type targetType)
                {
                    return base.GetAccessRules(includeExplicit, includeInherited, targetType);
                }
    
                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)
                {
                    return new GrantAccessRule(identityReference, accessMask, isInherited, inheritanceFlags, propagationFlags, type);
                }
    
                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); }
                }
            }
    
            private class GrantAccessRule : AccessRule
            {
                public GrantAccessRule(IdentityReference identity, int accessMask, bool isInherited,
                                         InheritanceFlags inheritanceFlags, PropagationFlags propagationFlags,
                                         AccessControlType type) :
                                         base(identity, accessMask, isInherited,
                                              inheritanceFlags, propagationFlags, type) { }
    
                public GrantAccessRule(IdentityReference identity, int accessMask, AccessControlType type) :
                    base(identity, accessMask, false, InheritanceFlags.None,
                         PropagationFlags.None, type) { }
    
                public int PublicAccessMask
                {
                    get { return base.AccessMask; }
                }
            }
    
            // 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;
                }
            }
            #endregion
        }
    }
    

    Using Sample

    using (var windowsAccess = new WindowsGrandAccess(accountInfo, WindowsGrandAccess.WindowStationAllAccess, WindowsGrandAccess.DesktopRightsAllAccess))
    {
       ...
    }
    

    Thank you.

提交回复
热议问题