액세스하는 파일 공유(UNC)원격지에서,비-신뢰할 수 있는 도메인 자격

StackOverflow https://stackoverflow.com/questions/659013

  •  20-08-2019
  •  | 
  •  

문제

우리는 실행으로 흥미로운 상황이 필요로 하는 해결,그리고 내 검색 설정 nill.그러므로 호소하는 커뮤니티에 대한 도움이됩니다.

문제입니다:우리는 필요한 프로그래밍 방식으로 액세스하려는 공유하지 않은 파일에 우리의 도메인에 속하지 않는 신뢰할 수 있는 외부 도메인을 통해 원격 파일 공유/UNC.자연적으로 우리를 제공할 필요가 증명하는 원격 기계입니다.

일반적으로 하는 이러한 문제를 해결에는 두 가지 방법 중 하나:

  1. 지도로 파일 공유라이브 및 자격 증명을 제공합니다.이것은 일반적으로 사용하여 수행 NET USE 명령어나 Win32 는 기능을 중복 NET USE.
  2. 액세스 파일 UNC 경로를 원하는 경우 컴퓨터에서 도메인을 보장하는 계정에 프로그램을 실행하는 것은 복제(비밀번호 포함)원격 컴퓨터에서 로컬 사용자.기본적으로 활용한다는 사실 Windows 자동으로 공급이 현재 사용자의 자격 증명할 때 사용자에 액세스하려고 시도 공유한 파일입니다.
  3. 를 사용하지 않 원격 파일 공유합니다.사용 FTP(또는 다른 방법이)파일을 전송하기 위해,작업에 그것은 로컬로 전송합니다.

을 위해 다양하고 잡다한 이유는,우리는 보안/네트워크는 건축가들이 처음 두 가지 방법.두 번째 방법은 분명 보안 hole;원격 컴퓨터에 손상되는 로컬 컴퓨터에는 위험이 있습니다.첫 번째 방법은 만족스럽지 못하기 때문에 새로 장착된 드라이브를 공유 자원을 사용할 수 있는 다른 프로그램에는 로컬 컴퓨터로 파일에 액세스하여 프로그램입니다.지만 그것은 확실히 가능하게 만들이 임시,그것은 여전히 구멍에 자신의 의견입니다.

그들은 열려 세 번째 옵션이 있지만,원격 네트워크 관리자는 주장에 SFTP 보다 FTPS,그렇게 해야 쓰지도 않는 지원 스위트입니다.SFTP 더 많은 방화벽 친화적 인 옵션을 몇 가지가 있 라이브러리에 사용할 수 있는 방법,그러나 나아보세요를 줄이는 나의 종속성으면 내가 할 수 있습니다.

나는 검색 MSDN 중 하나에 대한 관리나 win32 의 수단을 사용하여 원격 파일 공유,하지만 나는 실패했을 가지고 올요합니다.

그래서 제가 물어:또 다른 방법은 무엇입니까?놓치지 않았다 슈퍼 비밀 win32 는 기능까 내가 원하는 무엇인가?나야구의 일부 변종 옵션 3?

도움이 되었습니까?

해결책

문제를 해결하는 방법은 Win32 API를 사용하는 것입니다. WnetUseConnection.
이 기능을 사용하여 인증을 통해 UNC 경로에 연결하고 드라이브를 매핑하지 않도록하십시오..

이렇게하면 동일한 도메인에 있지 않은 경우에도 원격 시스템에 연결할 수 있으며 사용자 이름과 비밀번호가 다른 경우에도 원격 시스템에 연결할 수 있습니다.

WnetUseConnection을 사용한 후에는 동일한 도메인에있는 것처럼 UNC 경로를 통해 파일에 액세스 할 수 있습니다. 가장 좋은 방법은 아마도 행정 내장 주식을 통한 것입니다.
예 : computername c $ program files folder file.txt

다음은 wnetuseconnection을 사용하는 샘플 C# 코드입니다..

NetResource의 경우 lplocalname 및 lpprovider의 경우 Null을 전달해야합니다. dwtype는 Resourcetype_disk 여야합니다. lpremoteame은 computername이어야합니다.

다른 팁

빠른 솔루션을 찾는 사람들을 위해 NetworkShareAccesser 나는 최근에 썼다 ( 이 답변 (정말 고마워!)):

용법:

using (NetworkShareAccesser.Access(REMOTE_COMPUTER_NAME, DOMAIN, USER_NAME, PASSWORD))
{
    File.Copy(@"C:\Some\File\To\copy.txt", @"\\REMOTE-COMPUTER\My\Shared\Target\file.txt");
}

경고: 절대적으로 확인하십시오 DisposeNetworkShareAccesser 호출 (앱 충돌이라도!), 그렇지 않으면 개방형 연결이 Windows에 남아 있습니다. 열기를 통해 모든 열린 연결을 볼 수 있습니다 cmd 프롬프트 및 입력 net use.

코드:

/// <summary>
/// Provides access to a network share.
/// </summary>
public class NetworkShareAccesser : IDisposable
{
    private string _remoteUncName;
    private string _remoteComputerName;

    public string RemoteComputerName
    {
        get
        {
            return this._remoteComputerName;
        }
        set
        {
            this._remoteComputerName = value;
            this._remoteUncName = @"\\" + this._remoteComputerName;
        }
    }

    public string UserName
    {
        get;
        set;
    }
    public string Password
    {
        get;
        set;
    }

    #region Consts

    private const int RESOURCE_CONNECTED = 0x00000001;
    private const int RESOURCE_GLOBALNET = 0x00000002;
    private const int RESOURCE_REMEMBERED = 0x00000003;

    private const int RESOURCETYPE_ANY = 0x00000000;
    private const int RESOURCETYPE_DISK = 0x00000001;
    private const int RESOURCETYPE_PRINT = 0x00000002;

    private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
    private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
    private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
    private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
    private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
    private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;

    private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
    private const int RESOURCEUSAGE_CONTAINER = 0x00000002;


    private const int CONNECT_INTERACTIVE = 0x00000008;
    private const int CONNECT_PROMPT = 0x00000010;
    private const int CONNECT_REDIRECT = 0x00000080;
    private const int CONNECT_UPDATE_PROFILE = 0x00000001;
    private const int CONNECT_COMMANDLINE = 0x00000800;
    private const int CONNECT_CMD_SAVECRED = 0x00001000;

    private const int CONNECT_LOCALDRIVE = 0x00000100;

    #endregion

    #region Errors

    private const int NO_ERROR = 0;

    private const int ERROR_ACCESS_DENIED = 5;
    private const int ERROR_ALREADY_ASSIGNED = 85;
    private const int ERROR_BAD_DEVICE = 1200;
    private const int ERROR_BAD_NET_NAME = 67;
    private const int ERROR_BAD_PROVIDER = 1204;
    private const int ERROR_CANCELLED = 1223;
    private const int ERROR_EXTENDED_ERROR = 1208;
    private const int ERROR_INVALID_ADDRESS = 487;
    private const int ERROR_INVALID_PARAMETER = 87;
    private const int ERROR_INVALID_PASSWORD = 1216;
    private const int ERROR_MORE_DATA = 234;
    private const int ERROR_NO_MORE_ITEMS = 259;
    private const int ERROR_NO_NET_OR_BAD_PATH = 1203;
    private const int ERROR_NO_NETWORK = 1222;

    private const int ERROR_BAD_PROFILE = 1206;
    private const int ERROR_CANNOT_OPEN_PROFILE = 1205;
    private const int ERROR_DEVICE_IN_USE = 2404;
    private const int ERROR_NOT_CONNECTED = 2250;
    private const int ERROR_OPEN_FILES = 2401;

    #endregion

    #region PInvoke Signatures

    [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 = "";
    }

    #endregion

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name. The user will be promted to enter credentials
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <returns></returns>
    public static NetworkShareAccesser Access(string remoteComputerName)
    {
        return new NetworkShareAccesser(remoteComputerName);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given domain/computer name, username and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="domainOrComuterName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string domainOrComuterName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName,
                                        domainOrComuterName + @"\" + userName,
                                        password);
    }

    /// <summary>
    /// Creates a NetworkShareAccesser for the given computer name using the given username (format: domainOrComputername\Username) and password
    /// </summary>
    /// <param name="remoteComputerName"></param>
    /// <param name="userName"></param>
    /// <param name="password"></param>
    public static NetworkShareAccesser Access(string remoteComputerName, string userName, string password)
    {
        return new NetworkShareAccesser(remoteComputerName, 
                                        userName,
                                        password);
    }

    private NetworkShareAccesser(string remoteComputerName)
    {
        RemoteComputerName = remoteComputerName;               

        this.ConnectToShare(this._remoteUncName, null, null, true);
    }

    private NetworkShareAccesser(string remoteComputerName, string userName, string password)
    {
        RemoteComputerName = remoteComputerName;
        UserName = userName;
        Password = password;

        this.ConnectToShare(this._remoteUncName, this.UserName, this.Password, false);
    }

    private void ConnectToShare(string remoteUnc, string username, string password, bool promptUser)
    {
        NETRESOURCE nr = new NETRESOURCE
        {
            dwType = RESOURCETYPE_DISK,
            lpRemoteName = remoteUnc
        };

        int result;
        if (promptUser)
        {
            result = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
        }
        else
        {
            result = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
        }

        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    private void DisconnectFromShare(string remoteUnc)
    {
        int result = WNetCancelConnection2(remoteUnc, CONNECT_UPDATE_PROFILE, false);
        if (result != NO_ERROR)
        {
            throw new Win32Exception(result);
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        this.DisconnectFromShare(this._remoteUncName);
    }
}

AFAIK,당신은 필요가 없 지도 UNC 경로에서 드라이브 문자를 설정하기 위해에 대한 자격 증명 서버입니다.나는 정기적으로 사용되는 배치는 스크립트는 다음과 같:

net use \\myserver /user:username password

:: do something with \\myserver\the\file\i\want.xml

net use /delete \\my.server.com

그러나,어떤에서 실행되는 프로그램은 동일한 계정으로 당신의 프로그램으로도 액세스할 수 있는 모든 것 username:password 에 액세스 할 수 있습니다.가능한 솔루션이 될 수 있을 격리시키는 프로그램에서 로컬 사용자 계정(UNC 액세스가 지역을 계정이라는 것을 NET USE).

참고: SMB 를 사용하여 전체 도메인은 매우 좋은 기술의 사용,IMO.보안 경우는 중요한 사실 SMB 부족한 암호화를 조금의 댐퍼에 의해 모든다.

WnetUseConnection 대신 권장합니다 netUSeadd. WnetUseConnection은 WnetUseConnection2 및 WnetUseConnection3에 의해 대체 된 레거시 기능이지만 이러한 모든 기능은 Windows 탐색기에서 볼 수있는 네트워크 장치를 만듭니다. NetUseadd는 원격 컴퓨터에서 인증하기 위해 DOS 프롬프트에서 NET 사용을 호출하는 것과 같습니다.

NetUseadd에 전화하면 디렉토리에 액세스하려는 후속 시도가 성공해야합니다.

나 자신을 모르지만 #2가 잘못되기를 바랍니다. Windows가 내 로그인 정보 (최소한 모든 암호 중 하나)를 자동으로 제공하지 않을 것이라고 생각하고 싶습니다. , 내 신뢰의 일부가 아닌 사람은 물론.

그럼에도 불구하고, 당신은 가장 자 건조를 탐구 했습니까? 코드는 이것과 비슷해 보일 것입니다.

using (System.Security.Principal.WindowsImpersonationContext context = System.Security.Principal.WindowsIdentity.Impersonate(token))
{
    // Do network operations here

    context.Undo();
}

이 경우 token 변수는 intptr입니다. 이 변수에 대한 값을 얻으려면 관리되지 않는 Logonuser Windows API 기능을 호출해야합니다. 빠른 여행 pinvoke.net 다음 서명을 제공합니다.

[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
    string lpszUsername,
    string lpszDomain,
    string lpszPassword,
    int dwLogonType,
    int dwLogonProvider,
    out IntPtr phToken
);

사용자 이름, 도메인 및 비밀번호는 상당히 분명해 보일 것입니다. dwlogontype 및 dwlogonprovider로 전달할 수있는 다양한 값을 살펴보고 필요에 가장 적합한 값을 결정하십시오.

이 코드는 여기에 두 번째 도메인이 없어서 테스트되지 않았지만, 이로 인해 올바른 길을 가야합니다.

대부분의 SFTP 서버는 SCP를 지원하여 라이브러리를 찾기가 훨씬 쉽습니다. 포함 된 PSCP와 같은 코드에서 기존 클라이언트를 호출 할 수도 있습니다. 퍼티.

작업중인 파일 유형이 텍스트 나 XML 파일과 같은 단순한 경우 .NET 리모 팅 또는 웹 서비스와 같은 것을 사용하여 파일을 조작하기 위해 자신의 클라이언트/서버 구현을 작성하는 것까지 갈 수도 있습니다.

옵션 3이 구현 된 것을 보았습니다 Jscape 도구 매우 간단한 방식으로. 시도해 볼 수 있습니다. 무료는 아니지만 그 일을합니다.

여기에서 모든 cruft가 제거 된 최소 POC 클래스.

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;
        }
    }
}

직접 사용할 수 있습니다 \\server\share\folder w/ WNetUseConnection, 그것을 제거 할 필요가 없습니다 \\server 미리 만 부분.

나는 내 첨부 내 vb.net 코드 기반 브라이언 참조

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

사용 방법

Dim login = PinvokeWindowsNetworking.connectToRemote("\\ComputerName", "ComputerName\UserName", "Password")

    If IsNothing(login) Then



        'do your thing on the shared folder



       PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")

    End If

나는 답을 찾기 위해 MS를 찾았다. 첫 번째 솔루션은 응용 프로그램 프로세스를 실행하는 사용자 계정이 공유 폴더 또는 드라이브 (동일한 도메인)에 액세스 할 수 있다고 가정합니다. DNS가 해결되었는지 확인하거나 IP 주소를 사용해보십시오. 간단히 다음을 수행하십시오.

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

자격 증명이있는 다른 도메인에서 .net 2.0을 원한다면이 모델을 따르십시오.

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();
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top