How to provide user name and password when connecting to a network share

后端 未结 11 1699
不知归路
不知归路 2020-11-22 00:34

When connecting to a network share for which the current user (in my case, a network enabled service user) has no rights, name and password have to be provided.

I kn

相关标签:
11条回答
  • 2020-11-22 01:02

    You can either change the thread identity, or P/Invoke WNetAddConnection2. I prefer the latter, as I sometimes need to maintain multiple credentials for different locations. I wrap it into an IDisposable and call WNetCancelConnection2 to remove the creds afterwards (avoiding the multiple usernames error):

    using (new NetworkConnection(@"\\server\read", readCredentials))
    using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
       File.Copy(@"\\server\read\file", @"\\server2\write\file");
    }
    
    0 讨论(0)
  • 2020-11-22 01:15

    I liked Mark Brackett's answer so much that I did my own quick implementation. Here it is if anyone else needs it in a hurry:

    public class NetworkConnection : IDisposable
    {
        string _networkName;
    
        public NetworkConnection(string networkName, 
            NetworkCredential credentials)
        {
            _networkName = networkName;
    
            var netResource = new NetResource()
            {
                Scope = ResourceScope.GlobalNetwork,
                ResourceType = ResourceType.Disk,
                DisplayType = ResourceDisplaytype.Share,
                RemoteName = networkName
            };
    
            var userName = string.IsNullOrEmpty(credentials.Domain)
                ? credentials.UserName
                : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);
    
            var result = WNetAddConnection2(
                netResource, 
                credentials.Password,
                userName,
                0);
    
            if (result != 0)
            {
                throw new Win32Exception(result);
            }   
        }
    
        ~NetworkConnection()
        {
            Dispose(false);
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        protected virtual void Dispose(bool disposing)
        {
            WNetCancelConnection2(_networkName, 0, true);
        }
    
        [DllImport("mpr.dll")]
        private static extern int WNetAddConnection2(NetResource netResource, 
            string password, string username, int flags);
    
        [DllImport("mpr.dll")]
        private static extern int WNetCancelConnection2(string name, int flags,
            bool force);
    }
    
    [StructLayout(LayoutKind.Sequential)]
    public class NetResource
    {
        public ResourceScope Scope;
        public ResourceType ResourceType;
        public ResourceDisplaytype DisplayType;
        public int Usage;
        public string LocalName;
        public string RemoteName;
        public string Comment;
        public string Provider;
    }
    
    public enum ResourceScope : int
    {
        Connected = 1,
        GlobalNetwork,
        Remembered,
        Recent,
        Context
    };
    
    public enum ResourceType : int
    {
        Any = 0,
        Disk = 1,
        Print = 2,
        Reserved = 8,
    }
    
    public enum ResourceDisplaytype : int
    {
        Generic = 0x0,
        Domain = 0x01,
        Server = 0x02,
        Share = 0x03,
        File = 0x04,
        Group = 0x05,
        Network = 0x06,
        Root = 0x07,
        Shareadmin = 0x08,
        Directory = 0x09,
        Tree = 0x0a,
        Ndscontainer = 0x0b
    }
    
    0 讨论(0)
  • 2020-11-22 01:18

    You should be looking at adding a like like this:

    <identity impersonate="true" userName="domain\user" password="****" />
    

    Into your web.config.

    More Information.

    0 讨论(0)
  • 2020-11-22 01:22

    Today 7 years later I'm facing the same issue and I'd like to share my version of the solution.

    It is copy & paste ready :-) Here it is:

    Step 1

    In your code (whenever you need to do something with permissions)

    ImpersonationHelper.Impersonate(domain, userName, userPassword, delegate
                                {
                                    //Your code here 
                                    //Let's say file copy:
                                    if (!File.Exists(to))
                                    {
                                        File.Copy(from, to);
                                    }
                                });
    

    Step 2

    The Helper file which does a magic

    using System;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Permissions;
    using System.Security.Principal;    
    using Microsoft.Win32.SafeHandles;
    
    
    namespace BlaBla
    {
        public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
        {
            private SafeTokenHandle()
                : base(true)
            {
            }
    
            [DllImport("kernel32.dll")]
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            [SuppressUnmanagedCodeSecurity]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool CloseHandle(IntPtr handle);
    
            protected override bool ReleaseHandle()
            {
                return CloseHandle(handle);
            }
        }
    
        public class ImpersonationHelper
        {
            [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
    
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            private extern static bool CloseHandle(IntPtr handle);
    
            [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
            public static void Impersonate(string domainName, string userName, string userPassword, Action actionToExecute)
            {
                SafeTokenHandle safeTokenHandle;
                try
                {
    
                    const int LOGON32_PROVIDER_DEFAULT = 0;
                    //This parameter causes LogonUser to create a primary token.
                    const int LOGON32_LOGON_INTERACTIVE = 2;
    
                    // Call LogonUser to obtain a handle to an access token.
                    bool returnValue = LogonUser(userName, domainName, userPassword,
                        LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                        out safeTokenHandle);
                    //Facade.Instance.Trace("LogonUser called.");
    
                    if (returnValue == false)
                    {
                        int ret = Marshal.GetLastWin32Error();
                        //Facade.Instance.Trace($"LogonUser failed with error code : {ret}");
    
                        throw new System.ComponentModel.Win32Exception(ret);
                    }
    
                    using (safeTokenHandle)
                    {
                        //Facade.Instance.Trace($"Value of Windows NT token: {safeTokenHandle}");
                        //Facade.Instance.Trace($"Before impersonation: {WindowsIdentity.GetCurrent().Name}");
    
                        // Use the token handle returned by LogonUser.
                        using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
                        {
                            using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
                            {
                                //Facade.Instance.Trace($"After impersonation: {WindowsIdentity.GetCurrent().Name}");
                                //Facade.Instance.Trace("Start executing an action");
    
                                actionToExecute();
    
                                //Facade.Instance.Trace("Finished executing an action");
                            }
                        }
                        //Facade.Instance.Trace($"After closing the context: {WindowsIdentity.GetCurrent().Name}");
                    }
    
                }
                catch (Exception ex)
                {
                    //Facade.Instance.Trace("Oh no! Impersonate method failed.");
                    //ex.HandleException();
                    //On purpose: we want to notify a caller about the issue /Pavel Kovalev 9/16/2016 2:15:23 PM)/
                    throw;
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 01:22

    For VB.lovers the VB.NET equivalent of Luke Quinane's code (thanks Luke!)

    Imports System
    Imports System.Net
    Imports System.Runtime.InteropServices
    Imports System.ComponentModel
    
    Public Class NetworkConnection
        Implements IDisposable
    
        Private _networkName As String
    
        Public Sub New(networkName As String, credentials As NetworkCredential)
            _networkName = networkName
    
            Dim netResource = New NetResource() With {
                 .Scope = ResourceScope.GlobalNetwork,
                 .ResourceType = ResourceType.Disk,
                 .DisplayType = ResourceDisplaytype.Share,
                 .RemoteName = networkName
            }
    
            Dim userName = If(String.IsNullOrEmpty(credentials.Domain), credentials.UserName, String.Format("{0}\{1}", credentials.Domain, credentials.UserName))
    
            Dim result = WNetAddConnection2(NetResource, credentials.Password, userName, 0)
    
            If result <> 0 Then
                Throw New Win32Exception(result, "Error connecting to remote share")
            End If
        End Sub
    
        Protected Overrides Sub Finalize()
            Try
                Dispose (False)
            Finally
                MyBase.Finalize()
            End Try
        End Sub
    
        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose (True)
            GC.SuppressFinalize (Me)
        End Sub
    
        Protected Overridable Sub Dispose(disposing As Boolean)
            WNetCancelConnection2(_networkName, 0, True)
        End Sub
    
        <DllImport("mpr.dll")> _
        Private Shared Function WNetAddConnection2(netResource As NetResource, password As String, username As String, flags As Integer) As Integer
        End Function
    
        <DllImport("mpr.dll")> _
        Private Shared Function WNetCancelConnection2(name As String, flags As Integer, force As Boolean) As Integer
        End Function
    
    End Class
    
    <StructLayout(LayoutKind.Sequential)> _
    Public Class NetResource
        Public Scope As ResourceScope
        Public ResourceType As ResourceType
        Public DisplayType As ResourceDisplaytype
        Public Usage As Integer
        Public LocalName As String
        Public RemoteName As String
        Public Comment As String
        Public Provider As String
    End Class
    
    Public Enum ResourceScope As Integer
        Connected = 1
        GlobalNetwork
        Remembered
        Recent
        Context
    End Enum
    
    Public Enum ResourceType As Integer
        Any = 0
        Disk = 1
        Print = 2
        Reserved = 8
    End Enum
    
    Public Enum ResourceDisplaytype As Integer
        Generic = &H0
        Domain = &H1
        Server = &H2
        Share = &H3
        File = &H4
        Group = &H5
        Network = &H6
        Root = &H7
        Shareadmin = &H8
        Directory = &H9
        Tree = &HA
        Ndscontainer = &HB
    End Enum
    
    0 讨论(0)
  • 2020-11-22 01:23

    The Luke Quinane solution looks good, but did work only partially in my ASP.NET MVC application. Having two shares on the same server with different credentials I could use the impersonation only for the first one.

    The problem with WNetAddConnection2 is also that it behaves differently on different windows versions. That is why I looked for alternatives and found the LogonUser function. Here is my code which also works in ASP.NET:

    public sealed class WrappedImpersonationContext
    {
        public enum LogonType : int
        {
            Interactive = 2,
            Network = 3,
            Batch = 4,
            Service = 5,
            Unlock = 7,
            NetworkClearText = 8,
            NewCredentials = 9
        }
    
        public enum LogonProvider : int
        {
            Default = 0,  // LOGON32_PROVIDER_DEFAULT
            WinNT35 = 1,
            WinNT40 = 2,  // Use the NTLM logon provider.
            WinNT50 = 3   // Use the negotiate logon provider.
        }
    
        [DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain,
            String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken);
    
        [DllImport("kernel32.dll")]
        public extern static bool CloseHandle(IntPtr handle);
    
        private string _domain, _password, _username;
        private IntPtr _token;
        private WindowsImpersonationContext _context;
    
        private bool IsInContext
        {
            get { return _context != null; }
        }
    
        public WrappedImpersonationContext(string domain, string username, string password)
        {
            _domain = String.IsNullOrEmpty(domain) ? "." : domain;
            _username = username;
            _password = password;
        }
    
        // Changes the Windows identity of this thread. Make sure to always call Leave() at the end.
        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public void Enter()
        {
            if (IsInContext)
                return;
    
            _token = IntPtr.Zero;
            bool logonSuccessfull = LogonUser(_username, _domain, _password, LogonType.NewCredentials, LogonProvider.WinNT50, ref _token);
            if (!logonSuccessfull)
            {
                throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            WindowsIdentity identity = new WindowsIdentity(_token);
            _context = identity.Impersonate();
    
            Debug.WriteLine(WindowsIdentity.GetCurrent().Name);
        }
    
        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public void Leave()
        {
            if (!IsInContext)
                return;
    
            _context.Undo();
    
            if (_token != IntPtr.Zero)
            {
                CloseHandle(_token);
            }
            _context = null;
        }
    }
    

    Usage:

    var impersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
    impersonationContext.Enter();
    
    //do your stuff here
    
    impersonationContext.Leave();
    
    0 讨论(0)
提交回复
热议问题