Mengakses File Bersama (UNC) Dari Domain Jarak Jauh, Tidak Tepercaya dengan Kredensial

151

Kami mengalami situasi yang menarik yang perlu diselesaikan, dan pencarian saya telah muncul sampai nol. Karena itu saya meminta bantuan komunitas SO.

Masalahnya adalah ini: kami memiliki kebutuhan untuk secara terprogram mengakses file bersama yang tidak ada dalam domain kami, dan tidak berada dalam domain eksternal tepercaya melalui file sharing jarak jauh / UNC. Secara alami, kita perlu memasok kredensial ke mesin jarak jauh.

Biasanya, seseorang memecahkan masalah ini dengan satu dari dua cara:

  1. Petakan berbagi file sebagai drive dan berikan kredensial pada saat itu. Ini biasanya dilakukan dengan menggunakan NET USEperintah atau fungsi Win32 yang menduplikasi NET USE.
  2. Akses file dengan jalur UNC seolah-olah komputer jarak jauh berada di domain dan pastikan bahwa akun yang menjalankan program digandakan (termasuk kata sandi) pada mesin jarak jauh sebagai pengguna lokal. Pada dasarnya memanfaatkan fakta bahwa Windows akan secara otomatis menyediakan kredensial pengguna saat ini ketika pengguna mencoba mengakses file bersama.
  3. Jangan gunakan berbagi file jarak jauh. Gunakan FTP (atau cara lain) untuk mentransfer file, mengerjakannya secara lokal, lalu mentransfernya kembali.

Karena berbagai alasan, arsitek keamanan / jaringan kami telah menolak dua pendekatan pertama. Pendekatan kedua jelas merupakan lubang keamanan; jika komputer jarak jauh terganggu, komputer lokal sekarang berisiko. Pendekatan pertama tidak memuaskan karena drive yang baru dipasang adalah sumber daya bersama yang tersedia untuk program lain di komputer lokal selama akses file oleh program. Meskipun sangat mungkin untuk membuat ini sementara, itu masih menjadi lubang dalam pendapat mereka.

Mereka terbuka untuk opsi ketiga, tetapi admin jaringan jauh bersikeras SFTP daripada FTPS, dan FtpWebRequest hanya mendukung FTPS. SFTP adalah opsi yang lebih ramah-firewall dan ada beberapa perpustakaan yang bisa saya gunakan untuk pendekatan itu, tetapi saya lebih suka mengurangi ketergantungan saya jika saya bisa.

Saya telah mencari MSDN untuk dikelola atau win32 cara menggunakan berbagi file jarak jauh, tapi saya gagal menemukan sesuatu yang berguna.

Jadi saya bertanya: Apakah ada cara lain? Apakah saya melewatkan fungsi win32 rahasia super yang melakukan apa yang saya inginkan? Atau haruskah saya mengejar beberapa varian opsi 3?

Randolpho
sumber
Saya telah menyelesaikannya dengan pendekatan peniruan, tetapi itu adalah antara 2 mesin di luar domain. Saya tidak tahu apakah itu akan memiliki masalah berbicara dari domain ke komputer di luar domain. stackoverflow.com/questions/17221476/...
Wolf5

Jawaban:

174

Cara untuk memecahkan masalah Anda adalah dengan menggunakan Win32 API yang disebut WNetUseConnection .
Gunakan fungsi ini untuk terhubung ke jalur UNC dengan otentikasi, BUKAN untuk memetakan drive .

Ini akan memungkinkan Anda untuk terhubung ke mesin jarak jauh, bahkan jika itu tidak pada domain yang sama, dan bahkan jika itu memiliki nama pengguna dan kata sandi yang berbeda.

Setelah Anda menggunakan WNetUseConnection Anda akan dapat mengakses file melalui jalur UNC seolah-olah Anda berada di domain yang sama. Cara terbaik mungkin melalui administrasi yang dibangun di saham.
Contoh: \\ computername \ c $ \ program file \ Folder \ file.txt

Berikut adalah beberapa contoh kode C # yang menggunakan WNetUseConnection.
Catatan, untuk NetResource, Anda harus memberikan null untuk lpLocalName dan lpProvider. Tipe dwType harus RESOURCETYPE_DISK. LpRemoteName harus \\ 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);
        }
    }
}
Brian R. Bondy
sumber
Apakah ada cara untuk menggunakan fungsi seperti ini untuk secara eksplisit membuka / menutup koneksi ke mesin jaringan menggunakan kredensial saat ini, yaitu, tanpa memberikan nama pengguna dan kata sandi? Saya secara khusus tertarik untuk menutup koneksi setelah mengakses file sharing.
flipdoubt
Bukan untuk terhubung, kecuali komputer itu sendiri tidak memiliki nama pengguna atau kata sandi. Untuk memutuskan hubungan pasti Anda bisa. Anda bahkan dapat melakukannya melalui baris perintah saja.
Brian R. Bondy
1
Hai Brian. Dokumen yang Anda tautkan untuk mengatakan Anda dapat melewati NULL untuk nama pengguna dan kata sandi untuk menggunakan kredensial saat ini. Saya akan melakukan beberapa pengujian untuk melihat apakah ini berhasil.
flipdoubt
Melewati nol untuk nama pengguna / kata sandi memungkinkan saya terhubung, tetapi bagaimana saya bisa membuktikan bahwa saya telah terputus? Apakah ada sesuatu di server yang bisa saya lihat? Pada Server 2003, saya bisa menonton sesi, tetapi daftar sesi saat ini sama cepatnya saat aplikasi saya tidak menggunakan API ini.
flipdoubt
Haruskah koneksi dibuka dengan WNetUseConnectionditutup secara manual dengan menelepon WNetCancelConnection2? Atau apakah ada batas waktu idle (atau mekanisme lain) dan kita tidak perlu repot?
w128
123

Untuk orang yang mencari solusi cepat, Anda dapat menggunakan NetworkShareAccessersaya menulis baru-baru ini (berdasarkan jawaban ini (terima kasih banyak!)):

Pemakaian:

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

PERINGATAN: Silakan membuat benar-benar yakin, bahwa Disposedari NetworkShareAccesserdisebut (bahkan jika Anda app crash!), Jika tidak sambungan terbuka akan tetap pada Windows. Anda dapat melihat semua koneksi terbuka dengan membuka cmdprompt dan masuk net use.

Kode:

/// <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);
    }
}
Script Game
sumber
2
Anda perlu juga using System.Runtime.InteropServices;dan using System.ComponentModel;untuk DllImportdanWin32Exception
Kᴀτᴢ
Solusi ini telah menghentikan pencarian saya sepanjang hari. Terima kasih!!! Bekerja cukup baik sesuai kebutuhan.
Venkat
1
Saya mencoba menggunakan solusi Anda dengan akun pengguna lokal di mesin jarak jauh, tapi saya tetap mendapatkan kesalahan akses ditolak. Apakah solusi Anda hanya berfungsi untuk akun jaringan?
M3NTA7
1
Akun itu ada di mesin jarak jauh, tetapi itu bukan akun jaringan. Ini akun mesin lokal. Saya telah mencoba mengatur domain ke nama mesin. Saya juga telah memberikan izin penuh ke akun pengguna lokal di folder bersama, tetapi saya mendapat akses ditolak. Adakah ide mengapa ini bisa terjadi? Terima kasih.
M3NTA7
2
Catatan: membuang objek sepertinya tidak menghapus kredensial dari sistem (Windows 10); Saya dapat mengakses file di komputer jarak jauh setelah koneksi telah "dibatalkan". Masuk kembali ke akun pengguna saya atau me-restart komputer saya tampaknya menghapus cache internal ini.
Tim Cooper
16

AFAIK, Anda tidak perlu memetakan jalur UNC ke huruf drive untuk membuat kredensial untuk server. Saya secara teratur menggunakan skrip batch seperti:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

Namun, semua program yang berjalan pada akun yang sama dengan program Anda masih dapat mengakses semua yang username:passwordmemiliki akses. Solusi yang mungkin bisa dilakukan dengan mengisolasi program Anda di akun pengguna lokalnya sendiri (akses UNC bersifat lokal ke akun yang dipanggil NET USE).

Catatan: Menggunakan domain lintas SMB tidak cukup baik menggunakan teknologi, IMO. Jika keamanan itu penting, fakta bahwa SMB tidak memiliki enkripsi sedikit mereda dengan sendirinya.

Yakub
sumber
Jika Anda benar tentang akses UNC yang hanya tersedia untuk akun yang menelepon NET USE, itu mungkin pendekatan yang layak. Apakah Anda yakin kami perlu menggunakan akun lokal? Bukankah NET USEpanggilan itu bersifat lokal ke mesin yang dipanggil? Anda telah memberi saya jalur penelitian yang bagus
Randolpho
AFAIK, dan saya mungkin salah, akses UNC hanya akan tersedia untuk kepala keamanan tertentu (akun SAM, apa pun) yang menjadi dasar panggilan untuk PENGGUNAAN NET. Anda dapat memverifikasi ini dengan menggunakan RunAs untuk memetakan jalur dan kemudian mencoba mengaksesnya dari akun lain.
Jacob
dalam kasus saya, saya harus menggunakan net use \\ myserver / user: username @ domain password karena pengguna berada di domain yang berbeda.
StarCub
4

Daripada WNetUseConnection, saya akan merekomendasikan NetUseAdd . WNetUseConnection adalah fungsi lawas yang telah digantikan oleh WNetUseConnection2 dan WNetUseConnection3, tetapi semua fungsi tersebut menciptakan perangkat jaringan yang terlihat di Windows Explorer. NetUseAdd adalah setara dengan memanggil penggunaan bersih dalam prompt DOS untuk mengotentikasi pada komputer jarak jauh.

Jika Anda memanggil NetUseAdd maka upaya selanjutnya untuk mengakses direktori harus berhasil.

Adam Robinson
sumber
1
@Adam Robinson: Ini tidak benar. Tidak ada WNetUseConnection2 atau WNetUseConnection3. Saya pikir Anda berpikir tentang WNetAddConnection yang digantikan oleh WNetAddConnection2 dan WnetAddConnection3. Juga informasi yang Anda berikan tentang itu tidak benar.
Brian R. Bondy
WNetUseConnection seperti WNetAddConnection3, tetapi juga memiliki kemampuan opsional untuk membuat drive lokal yang dipetakan. Yang tidak harus Anda gunakan.
Brian R. Bondy
@ BrianR.Bondy Mereka memang ada, hanya saja tidak diimplementasikan sebagai C #. Sumber: docs.microsoft.com/da-dk/windows/win32/api/lmuse/… Quote: "Anda juga dapat menggunakan fungsi WNetAddConnection2 dan WNetAddConnection3 untuk mengarahkan perangkat lokal ke sumber daya jaringan."
Thomas Williams
4

Walaupun saya sendiri tidak tahu, saya tentu berharap bahwa # 2 tidak benar ... Saya ingin berpikir bahwa Windows tidak akan secara OTOMATIS memberikan informasi login saya (apalagi kata sandi saya!) Ke mesin apa pun , apalagi yang bukan bagian dari kepercayaan saya.

Bagaimanapun juga, sudahkah Anda menjelajahi arsitektur peniruan? Kode Anda akan terlihat mirip dengan ini:

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

    context.Undo();
}

Dalam hal ini, tokenvariabelnya adalah IntPtr. Untuk mendapatkan nilai untuk variabel ini, Anda harus memanggil fungsi LogonUser Windows API yang tidak dikelola. Perjalanan singkat ke pinvoke.net memberi kami tanda tangan berikut:

[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
);

Nama pengguna, domain, dan kata sandi seharusnya terlihat cukup jelas. Lihat berbagai nilai yang dapat dikirimkan ke dwLogonType dan dwLogonProvider untuk menentukan nilai yang paling sesuai dengan kebutuhan Anda.

Kode ini belum diuji, karena saya tidak memiliki domain kedua di sini tempat saya dapat memverifikasi, tetapi semoga ini menempatkan Anda di jalur yang benar.

Adam Robinson
sumber
7
Peniruan identitas tidak akan berfungsi ketika Anda mencoba menggunakan id login dari domain yang tidak dipercaya. Id pengguna harus dapat masuk secara lokal.
Moose
Ya, kami mencoba rute ini, akhirnya seperti yang dikatakan @Moose: Domain tidak dipercaya dan karenanya peniruan tidak akan berfungsi.
Randolpho
Ya, begitu saya melihat komentar itu sebabnya saya memposting jawaban menggunakan NetUseAdd (perbedaan utama antara itu dan fungsi WNetUseConnection dan WNetAddConnection adalah bahwa NetUseAdd tidak membuat koneksi terlihat di Windows Explorer).
Adam Robinson
Peniruan identitas tidak berfungsi pada domain yang sama, pada pengujian saya tetap merespons saya dengan Akses Ditolak mencoba membaca file pada folder bersama dengan akun administrator (admin di kedua mesin). Jadi, saya pikir ini bukan pendekatan yang tepat.
lidermin
4

Di sini kelas POC minimal dg semua cruft dihapus

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

Anda dapat langsung menggunakan \\server\share\folderdg WNetUseConnection, tidak perlu melepasnya hanya untuk \\serverbagian sebelumnya.

wqw
sumber
2

Sebagian besar server SFTP mendukung SCP juga yang bisa menjadi jauh lebih mudah untuk menemukan perpustakaan. Anda bahkan bisa memanggil klien yang sudah ada dari kode Anda seperti pscp yang disertakan dengan Putty .

Jika jenis file yang Anda kerjakan adalah sesuatu yang sederhana seperti teks atau file XML, Anda bahkan dapat menulis implementasi klien / server Anda sendiri untuk memanipulasi file menggunakan sesuatu seperti .NET Remoting atau layanan web.

Ryan Bolger
sumber
1

Saya telah melihat opsi 3 diimplementasikan dengan alat JScape dengan cara yang sangat mudah. Anda mungkin mencobanya. Ini tidak gratis, tetapi ini berfungsi dengan baik.

DreamSonic
sumber
1

saya lampirkan kode vb.net saya berdasarkan referensi brian

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

bagaimana cara menggunakannya

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

    If IsNothing(login) Then



        'do your thing on the shared folder



       PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")

    End If
roy.d
sumber
-1

Saya mencari MS untuk menemukan jawabannya. Solusi pertama mengasumsikan akun pengguna yang menjalankan proses aplikasi memiliki akses ke folder atau drive bersama (Domain yang sama). Pastikan DNS Anda teratasi atau coba gunakan alamat IP. Cukup lakukan hal berikut:

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

Jika Anda ingin melintasi berbagai domain .NET 2.0 dengan kredensial, ikuti model ini:

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();
Kentonbmax
sumber
terlihat menarik
DeerSpotter