Accessing a Shared File (UNC) From a Remote, Non-Trusted Domain With Credentials

后端 未结 10 1028
走了就别回头了
走了就别回头了 2020-11-22 05:12

We\'ve run into an interesting situation that needs solving, and my searches have turned up nill. I therefore appeal to the SO community for help.

The issue is this:

相关标签:
10条回答
  • 2020-11-22 05:24

    The way to solve your problem is to use a Win32 API called WNetUseConnection.
    Use this function to connect to a UNC path with authentication, NOT to map a drive.

    This will allow you to connect to a remote machine, even if it is not on the same domain, and even if it has a different username and password.

    Once you have used WNetUseConnection you will be able to access the file via a UNC path as if you were on the same domain. The best way is probably through the administrative built in shares.
    Example: \\computername\c$\program files\Folder\file.txt

    Here is some sample C# code that uses WNetUseConnection.
    Note, for the NetResource, you should pass null for the lpLocalName and lpProvider. The dwType should be RESOURCETYPE_DISK. The lpRemoteName should be \\ComputerName.

    using System;
    using System.Runtime.InteropServices ;
    using System.Threading;
    
    namespace ExtremeMirror
    {
        public class PinvokeWindowsNetworking
        {
            #region Consts
            const int RESOURCE_CONNECTED = 0x00000001;
            const int RESOURCE_GLOBALNET = 0x00000002;
            const int RESOURCE_REMEMBERED = 0x00000003;
    
            const int RESOURCETYPE_ANY = 0x00000000;
            const int RESOURCETYPE_DISK = 0x00000001;
            const int RESOURCETYPE_PRINT = 0x00000002;
    
            const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
            const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
            const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
            const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
            const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
            const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;
    
            const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
            const int RESOURCEUSAGE_CONTAINER = 0x00000002;
    
    
            const int CONNECT_INTERACTIVE = 0x00000008;
            const int CONNECT_PROMPT = 0x00000010;
            const int CONNECT_REDIRECT = 0x00000080;
            const int CONNECT_UPDATE_PROFILE = 0x00000001;
            const int CONNECT_COMMANDLINE = 0x00000800;
            const int CONNECT_CMD_SAVECRED = 0x00001000;
    
            const int CONNECT_LOCALDRIVE = 0x00000100;
            #endregion
    
            #region Errors
            const int NO_ERROR = 0;
    
            const int ERROR_ACCESS_DENIED = 5;
            const int ERROR_ALREADY_ASSIGNED = 85;
            const int ERROR_BAD_DEVICE = 1200;
            const int ERROR_BAD_NET_NAME = 67;
            const int ERROR_BAD_PROVIDER = 1204;
            const int ERROR_CANCELLED = 1223;
            const int ERROR_EXTENDED_ERROR = 1208;
            const int ERROR_INVALID_ADDRESS = 487;
            const int ERROR_INVALID_PARAMETER = 87;
            const int ERROR_INVALID_PASSWORD = 1216;
            const int ERROR_MORE_DATA = 234;
            const int ERROR_NO_MORE_ITEMS = 259;
            const int ERROR_NO_NET_OR_BAD_PATH = 1203;
            const int ERROR_NO_NETWORK = 1222;
    
            const int ERROR_BAD_PROFILE = 1206;
            const int ERROR_CANNOT_OPEN_PROFILE = 1205;
            const int ERROR_DEVICE_IN_USE = 2404;
            const int ERROR_NOT_CONNECTED = 2250;
            const int ERROR_OPEN_FILES  = 2401;
    
            private struct ErrorClass 
            {
                public int num;
                public string message;
                public ErrorClass(int num, string message) 
                {
                    this.num = num;
                    this.message = message;
                }
            }
    
    
            // Created with excel formula:
            // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
            private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
                new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), 
                new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), 
                new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), 
                new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), 
                new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), 
                new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), 
                new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
                new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), 
                new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), 
                new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), 
                new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), 
                new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), 
                new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), 
                new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), 
                new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), 
                new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), 
                new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), 
                new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
                new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), 
                new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), 
            };
    
            private static string getErrorForNumber(int errNum) 
            {
                foreach (ErrorClass er in ERROR_LIST) 
                {
                    if (er.num == errNum) return er.message;
                }
                return "Error: Unknown, " + errNum;
            }
            #endregion
    
            [DllImport("Mpr.dll")] private static extern int WNetUseConnection(
                IntPtr hwndOwner,
                NETRESOURCE lpNetResource,
                string lpPassword,
                string lpUserID,
                int dwFlags,
                string lpAccessName,
                string lpBufferSize,
                string lpResult
            );
    
            [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
                string lpName,
                int dwFlags,
                bool fForce
            );
    
            [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
            { 
                public int dwScope = 0;
                public int dwType = 0;
                public int dwDisplayType = 0;
                public int dwUsage = 0;
                public string lpLocalName = "";
                public string lpRemoteName = "";
                public string lpComment = "";
                public string lpProvider = "";
            }
    
    
            public static string connectToRemote(string remoteUNC, string username, string password) 
            {
                return connectToRemote(remoteUNC, username, password, false);
            }
    
            public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) 
            {
                NETRESOURCE nr = new NETRESOURCE();
                nr.dwType = RESOURCETYPE_DISK;
                nr.lpRemoteName = remoteUNC;
                //          nr.lpLocalName = "F:";
    
                int ret;
                if (promptUser) 
                    ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
                else 
                    ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
    
                if (ret == NO_ERROR) return null;
                return getErrorForNumber(ret);
            }
    
            public static string disconnectRemote(string remoteUNC) 
            {
                int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
                if (ret == NO_ERROR) return null;
                return getErrorForNumber(ret);
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 05:28

    Most SFTP servers support SCP as well which can be a lot easier to find libraries for. You could even just call an existing client from your code like pscp included with PuTTY.

    If the type of file you're working with is something simple like a text or XML file, you could even go so far as to write your own client/server implementation to manipulate the file using something like .NET Remoting or web services.

    0 讨论(0)
  • 2020-11-22 05:28

    I looked to MS to find the answers. The first solution assumes the user account running the application process has access to the shared folder or drive (Same domain). Make sure your DNS is resolved or try using IP address. Simply do the following:

     DirectoryInfo di = new DirectoryInfo(PATH);
     var files = di.EnumerateFiles("*.*", SearchOption.AllDirectories);
    

    If you want across different domains .NET 2.0 with credentials follow this model:

    WebRequest req = FileWebRequest.Create(new Uri(@"\\<server Name>\Dir\test.txt"));
    
            req.Credentials = new NetworkCredential(@"<Domain>\<User>", "<Password>");
            req.PreAuthenticate = true;
    
            WebResponse d = req.GetResponse();
            FileStream fs = File.Create("test.txt");
    
            // here you can check that the cast was successful if you want. 
            fs = d.GetResponseStream() as FileStream;
            fs.Close();
    
    0 讨论(0)
  • 2020-11-22 05:39

    Here a minimal POC class w/ all the cruft removed

    using System;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    
    public class UncShareWithCredentials : IDisposable
    {
        private string _uncShare;
    
        public UncShareWithCredentials(string uncShare, string userName, string password)
        {
            var nr = new Native.NETRESOURCE
            {
                dwType = Native.RESOURCETYPE_DISK,
                lpRemoteName = uncShare
            };
    
            int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
            if (result != Native.NO_ERROR)
            {
                throw new Win32Exception(result);
            }
            _uncShare = uncShare;
        }
    
        public void Dispose()
        {
            if (!string.IsNullOrEmpty(_uncShare))
            {
                Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
                _uncShare = null;
            }
        }
    
        private class Native
        {
            public const int RESOURCETYPE_DISK = 0x00000001;
            public const int CONNECT_UPDATE_PROFILE = 0x00000001;
            public const int NO_ERROR = 0;
    
            [DllImport("mpr.dll")]
            public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
                int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);
    
            [DllImport("mpr.dll")]
            public static extern int WNetCancelConnection2(string lpName, int dwFlags, bool fForce);
    
            [StructLayout(LayoutKind.Sequential)]
            public class NETRESOURCE
            {
                public int dwScope;
                public int dwType;
                public int dwDisplayType;
                public int dwUsage;
                public string lpLocalName;
                public string lpRemoteName;
                public string lpComment;
                public string lpProvider;
            }
        }
    }
    

    You can directly use \\server\share\folder w/ WNetUseConnection, no need to strip it to \\server part only beforehand.

    0 讨论(0)
  • 2020-11-22 05:39

    I've seen option 3 implemented with JScape tools in a pretty straightforward fashion. You might give it a try. It's not free, but it does its job.

    0 讨论(0)
  • 2020-11-22 05:41

    im attach my vb.net code based on brian reference

    Imports System.ComponentModel
    
    Imports System.Runtime.InteropServices
    
    Public Class PinvokeWindowsNetworking
    
    Const NO_ERROR As Integer = 0
    
    
    
    Private Structure ErrorClass
    
        Public num As Integer
    
        Public message As String
    
    
    
        Public Sub New(ByVal num As Integer, ByVal message As String)
    
            Me.num = num
    
            Me.message = message
    
        End Sub
    
    End Structure
    
    
    
    Private Shared ERROR_LIST As ErrorClass() = New ErrorClass() {
    
        New ErrorClass(5, "Error: Access Denied"),
    
        New ErrorClass(85, "Error: Already Assigned"),
    
        New ErrorClass(1200, "Error: Bad Device"),
    
        New ErrorClass(67, "Error: Bad Net Name"),
    
        New ErrorClass(1204, "Error: Bad Provider"),
    
        New ErrorClass(1223, "Error: Cancelled"),
    
        New ErrorClass(1208, "Error: Extended Error"),
    
        New ErrorClass(487, "Error: Invalid Address"),
    
        New ErrorClass(87, "Error: Invalid Parameter"),
    
        New ErrorClass(1216, "Error: Invalid Password"),
    
        New ErrorClass(234, "Error: More Data"),
    
        New ErrorClass(259, "Error: No More Items"),
    
        New ErrorClass(1203, "Error: No Net Or Bad Path"),
    
        New ErrorClass(1222, "Error: No Network"),
    
        New ErrorClass(1206, "Error: Bad Profile"),
    
        New ErrorClass(1205, "Error: Cannot Open Profile"),
    
        New ErrorClass(2404, "Error: Device In Use"),
    
        New ErrorClass(2250, "Error: Not Connected"),
    
        New ErrorClass(2401, "Error: Open Files")}
    
    
    
    Private Shared Function getErrorForNumber(ByVal errNum As Integer) As String
    
        For Each er As ErrorClass In ERROR_LIST
    
            If er.num = errNum Then Return er.message
    
        Next
    
    
    
        Try
    
            Throw New Win32Exception(errNum)
    
        Catch ex As Exception
    
            Return "Error: Unknown, " & errNum & " " & ex.Message
    
        End Try
    
    
    
        Return "Error: Unknown, " & errNum
    
    End Function
    
    
    
    <DllImport("Mpr.dll")>
    
    Private Shared Function WNetUseConnection(ByVal hwndOwner As IntPtr, ByVal lpNetResource As NETRESOURCE, ByVal lpPassword As String, ByVal lpUserID As String, ByVal dwFlags As Integer, ByVal lpAccessName As String, ByVal lpBufferSize As String, ByVal lpResult As String) As Integer
    
    End Function
    
    
    
    <DllImport("Mpr.dll")>
    
    Private Shared Function WNetCancelConnection2(ByVal lpName As String, ByVal dwFlags As Integer, ByVal fForce As Boolean) As Integer
    
    End Function
    
    
    
    <StructLayout(LayoutKind.Sequential)>
    
    Private Class NETRESOURCE
    
        Public dwScope As Integer = 0
    
        Public dwType As Integer = 0
    
        Public dwDisplayType As Integer = 0
    
        Public dwUsage As Integer = 0
    
        Public lpLocalName As String = ""
    
        Public lpRemoteName As String = ""
    
        Public lpComment As String = ""
    
        Public lpProvider As String = ""
    
    End Class
    
    
    
    Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String) As String
    
        Return connectToRemote(remoteUNC, username, password, False)
    
    End Function
    
    
    
    Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String, ByVal promptUser As Boolean) As String
    
        Dim nr As NETRESOURCE = New NETRESOURCE()
    
        nr.dwType = ResourceTypes.Disk
    
        nr.lpRemoteName = remoteUNC
    
        Dim ret As Integer
    
    
    
        If promptUser Then
    
            ret = WNetUseConnection(IntPtr.Zero, nr, "", "", Connects.Interactive Or Connects.Prompt, Nothing, Nothing, Nothing)
    
        Else
    
            ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, Nothing, Nothing, Nothing)
    
        End If
    
    
    
        If ret = NO_ERROR Then Return Nothing
    
        Return getErrorForNumber(ret)
    
    End Function
    
    
    
    Public Shared Function disconnectRemote(ByVal remoteUNC As String) As String
    
        Dim ret As Integer = WNetCancelConnection2(remoteUNC, Connects.UpdateProfile, False)
    
        If ret = NO_ERROR Then Return Nothing
    
        Return getErrorForNumber(ret)
    
    End Function
    
    
    Enum Resources As Integer
    
        Connected = &H1
    
        GlobalNet = &H2
    
        Remembered = &H3
    
    End Enum
    
    
    Enum ResourceTypes As Integer
    
        Any = &H0
    
        Disk = &H1
    
        Print = &H2
    
    End Enum
    
    
    Enum ResourceDisplayTypes As Integer
    
        Generic = &H0
    
        Domain = &H1
    
        Server = &H2
    
        Share = &H3
    
        File = &H4
    
        Group = &H5
    
    End Enum
    
    
    Enum ResourceUsages As Integer
    
        Connectable = &H1
    
        Container = &H2
    
    End Enum
    
    
    Enum Connects As Integer
    
        Interactive = &H8
    
        Prompt = &H10
    
        Redirect = &H80
    
        UpdateProfile = &H1
    
        CommandLine = &H800
    
        CmdSaveCred = &H1000
    
        LocalDrive = &H100
    
    End Enum
    
    
    End Class
    

    how to use it

    Dim login = PinvokeWindowsNetworking.connectToRemote("\\ComputerName", "ComputerName\UserName", "Password")
    
        If IsNothing(login) Then
    
    
    
            'do your thing on the shared folder
    
    
    
           PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")
    
        End If
    
    0 讨论(0)
提交回复
热议问题