2009-03-24 34 views
9

Tôi có dịch vụ Windows chạy dưới dạng mydomain \ userA. Tôi muốn có thể chạy các tùy chọn .exes từ dịch vụ. Thông thường, tôi sử dụng Process.Start() và nó hoạt động tốt, nhưng trong một số trường hợp, tôi muốn chạy tệp thực thi như một người dùng khác (mydomain \ userB).Bắt đầu quy trình có thông tin xác thực từ Dịch vụ Windows

Nếu tôi thay đổi ProcessStartInfo tôi sử dụng để bắt đầu quy trình bao gồm thông tin đăng nhập, tôi bắt đầu nhận được lỗi - hộp thoại báo lỗi cho biết "Ứng dụng không khởi chạy được đúng (0xc0000142). Nhấp vào OK để chấm dứt ứng dụng. ", hoặc" Truy cập bị từ chối "Win32Exception. Nếu tôi chạy mã bắt đầu quá trình từ dòng lệnh thay vì chạy nó trong dịch vụ, quá trình bắt đầu sử dụng thông tin xác thực chính xác (tôi đã xác minh điều này bằng cách đặt ProcessStartInfo để chạy whoami.exe và bắt đầu dòng lệnh).

Tôi cũng đã thử mạo danh bằng WindowsIdentity.Impersonate(), nhưng điều này không hiệu quả - như tôi hiểu, mạo danh chỉ ảnh hưởng đến chuỗi hiện tại và bắt đầu một quy trình mới kế thừa bộ mô tả bảo mật của quá trình chứ không phải chủ đề hiện tại.

Tôi đang chạy điều này trong miền thử nghiệm bị cô lập, do đó cả userA và userB là quản trị viên tên miền và cả hai đều có Đăng nhập với tư cách là Dịch vụ ngay trên toàn miền.

+0

Có phải hệ điều hành Vista không? Nó có xảy ra khi bạn khởi chạy Notepad.exe không? –

+0

Bạn có thể đặt mã mẫu đối số của mình không? –

+0

Rất tiếc, tôi không thể liên hệ lại với bạn sớm hơn. Nếu bạn vẫn cần một số mã mẫu, tôi đã chỉnh sửa câu trả lời của mình để thêm liên kết vào một bài viết tôi vừa mới đăng với mã nguồn. –

Trả lời

16

Khi bạn khởi chạy một quy trình mới bằng cách sử dụng ProcessStartInfo, quá trình được bắt đầu trong cùng một cửa sổ và màn hình nền làm quá trình khởi chạy. Nếu bạn đang sử dụng các thông tin đăng nhập khác nhau thì người dùng nói chung sẽ không có đủ quyền để chạy trên màn hình đó. Lỗi khởi tạo không thành công khi user32.dll cố gắng khởi tạo trong tiến trình mới và không thể.

Để giải quyết vấn đề này, trước tiên bạn phải truy xuất các mô tả bảo mật liên quan đến trạm cửa sổ và màn hình nền và thêm các quyền thích hợp vào DACL cho người dùng của bạn, sau đó khởi chạy tiến trình của bạn theo thông tin đăng nhập mới.

CHỈNH SỬA: Mô tả chi tiết về cách thực hiện mã mẫu và mã mẫu này hơi dài ở đây nên tôi đặt cùng một mã article.

 //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)) 
     { 
     } 
+0

Cảm ơn; có vẻ như đây là nguyên nhân của vấn đề. Bạn có bất cứ đề xuất nào về cách tốt nhất để lấy DACL và thêm quyền cho người dùng thứ 2 không? –

+0

Bạn không cần phải đặt Window Station và Desktop DACL nếu bạn tạo dịch vụ tương tác. –

+0

Có giống như khi bắt đầu quy trình với thông tin đăng nhập từ "Dịch vụ WCF" không? – Kiquenet

0

Bạn đặt tên miền, người dùng và mật khẩu bằng cách nào? Bạn có thiết lập tên miền đúng cách cũng như mật khẩu không (nó phải sử dụng SecureString).

Ngoài ra, bạn có đang đặt thuộc tính WorkingDirectory không? Khi sử dụng một tên người dùng và mật khẩu, tài liệu nói rằng bạn phải thiết lập thuộc tính WorkingDirectory.

+0

Tôi đang đặt tên miền, tên người dùng và mật khẩu (như một SecureString) một cách chính xác - nếu tôi chạy mã này bên ngoài dịch vụ Windows, nó hoạt động như mong đợi. Đặt WorkingDirectory dường như không có bất kỳ ảnh hưởng nào. –

2

Đây là triệu chứng của:
- không đủ quyền;
- tải không thành công của thư viện;

Sử dụng Filemon để phát hiện một số truy cập bị từ chối hoặc
WinDbg để chạy ứng dụng trong trình gỡ rối và xem bất kỳ sự cố nào.

+0

* bây giờ * tôi đã đọc câu trả lời của bạn ... 1 cho tham chiếu Filemon, tôi nghĩ điều đó sẽ chỉ ra vấn đề. – Moose

0

Có thể là bất kỳ quá trình nào được khởi động bởi dịch vụ cũng phải có đặc quyền "Đăng nhập với tư cách Dịch vụ".

Nếu id người dùng mà bạn đang sử dụng để bắt đầu quá trình thứ hai không có quyền quản trị đối với hộp, điều này có thể xảy ra.

Một thử nghiệm dễ dàng là thay đổi chính sách bảo mật cục bộ để cung cấp cho userid "Đăng nhập dưới dạng dịch vụ" và thử lại.

Edit: Sau khi các thông tin bổ sung ..

Chăn thả trên Google trên thế này, có vẻ như 0xc0000142 liên quan đến việc không thể khởi tạo một DLL cần thiết. Có điều gì đó mà dịch vụ đã mở mà quá trình sinh sản cần không? Trong mọi trường hợp, có vẻ như nó phải liên quan đến quá trình được khởi động và không phải cách bạn đang thực hiện nó.

+0

Cả hai người dùng đều đã bật Đăng nhập dưới dạng Dịch vụ. –

+0

Đã thêm một số câu trả lời khác cho tôi. Dường như bạn có thể có một số xung đột giữa dịch vụ và quá trình sinh ra dựa trên lỗi 0xc0000142. – Moose

0

Tôi đã có vấn đề này ngày hôm nay và tôi đã dành khá nhiều thời gian để tìm hiểu. Những gì tôi đã kết thúc là tạo dịch vụ tương tác (bằng cách sử dụng hộp kiểm Cho phép dịch vụ tương tác với máy tính để bàn trong services.msc). Ngay sau khi tôi đã làm điều đó các lỗi 0xc0000142 đã biến mất.

7

Dựa trên số answer by @StephenMartin.

Quy trình mới được khởi chạy sử dụng lớp Process chạy trong cùng một cửa sổ và máy tính để bàn làm quá trình khởi chạy. Nếu bạn đang chạy quy trình mới bằng các thông tin đăng nhập khác nhau, thì quy trình mới sẽ không có quyền truy cập vào cửa sổ và máy tính để bàn. Điều gì gây ra lỗi như 0xC0000142.

Sau đây là mã độc lập "nhỏ gọn" để cấp cho người dùng quyền truy cập vào cửa sổ hiện tại và máy tính để bàn. Nó không yêu cầu thư viện 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; 
    } 
} 
+0

Điều này đã giúp tôi giải quyết một vấn đề lớn.Cảm ơn rất nhiều. Hãy tiếp tục công việc tốt –

+1

Tôi đã làm tuần trước :). –

1

Dựa trên câu trả lời của @Stephen Martin và Martin Prikryl.

Mã này giúp bạn chạy quy trình với thông tin xác thực người dùng khác nhau từ một dịch vụ.
Tôi hiện đã tối ưu hóa mã nguồn.
Việc xóa và thiết lập quyền hiện cũng có thể thực hiện được.

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<GrantAccessRule>().SingleOrDefault(r => r.IdentityReference.Value.ToLower() == username.ToLower() && accessMask == r.PublicAccessMask); 
      if (userResult == null) 
      { 
       AddGrandAccess(security, accessMask, safeHandle); 
       userResult = ruels.Cast<GrantAccessRule>().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 
    } 
} 

Sử dụng mẫu

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

Cảm ơn bạn.

Các vấn đề liên quan