Truy cập tệp được chia sẻ (UNC) từ một miền từ xa, không đáng tin cậy có thông tin xác thực


151

Chúng tôi đã gặp phải một tình huống thú vị cần giải quyết, và các tìm kiếm của tôi đã trở nên vô nghĩa. Do đó tôi kêu gọi cộng đồng SO giúp đỡ.

Vấn đề là ở đây: chúng tôi có nhu cầu truy cập theo chương trình một tệp được chia sẻ không thuộc miền của chúng tôi và không nằm trong miền bên ngoài đáng tin cậy thông qua chia sẻ tệp từ xa / UNC. Đương nhiên, chúng ta cần cung cấp thông tin đăng nhập cho máy từ xa.

Thông thường, người ta giải quyết vấn đề này theo một trong hai cách:

  1. Ánh xạ chia sẻ tệp dưới dạng một ổ đĩa và cung cấp thông tin đăng nhập tại thời điểm đó. Điều này thường được thực hiện bằng cách sử dụng NET USElệnh hoặc các chức năng Win32 trùng lặp NET USE.
  2. Truy cập tệp bằng đường dẫn UNC như thể máy tính từ xa nằm trên miền và đảm bảo rằng tài khoản mà chương trình chạy được sao chép (bao gồm cả mật khẩu) trên máy từ xa với tư cách là người dùng cục bộ. Về cơ bản, thúc đẩy thực tế là Windows sẽ tự động cung cấp thông tin đăng nhập của người dùng hiện tại khi người dùng cố gắng truy cập vào một tệp được chia sẻ.
  3. Không sử dụng chia sẻ tập tin từ xa. Sử dụng FTP (hoặc một số phương tiện khác) để truyền tệp, làm việc trên tệp cục bộ, sau đó chuyển lại.

Vì nhiều lý do và lặt vặt, các kiến ​​trúc sư an ninh / mạng của chúng tôi đã từ chối hai cách tiếp cận đầu tiên. Cách tiếp cận thứ hai rõ ràng là một lỗ hổng bảo mật; nếu máy tính từ xa bị xâm nhập, máy tính cục bộ hiện đang gặp rủi ro. Cách tiếp cận đầu tiên là không thỏa đáng vì ổ đĩa mới được gắn là một tài nguyên được chia sẻ có sẵn cho các chương trình khác trên máy tính cục bộ trong khi chương trình truy cập tệp. Mặc dù hoàn toàn có thể thực hiện điều này tạm thời, nhưng vẫn là một lỗ hổng trong quan điểm của họ.

Họ mở tùy chọn thứ ba, nhưng các quản trị viên mạng từ xa khăng khăng đòi SFTP thay vì FTPS và FtpWebRequest chỉ hỗ trợ FTPS. SFTP tùy chọn thân thiện với tường lửa hơn và có một vài thư viện tôi có thể sử dụng cho phương pháp đó, nhưng tôi muốn giảm sự phụ thuộc của mình nếu có thể.

Tôi đã tìm kiếm MSDN cho một phương tiện được quản lý hoặc win32 để sử dụng chia sẻ tệp từ xa, nhưng tôi đã không tìm thấy bất cứ điều gì hữu ích.

Và tôi hỏi: Có cách nào khác không? Tôi đã bỏ lỡ một chức năng win32 siêu bí mật làm những gì tôi muốn? Hoặc tôi phải theo đuổi một số biến thể của tùy chọn 3?


Tôi đã giải quyết nó bằng phương pháp mạo danh, nhưng đó là giữa 2 máy bên ngoài một miền. Tôi không biết liệu có vấn đề gì khi nói từ tên miền sang máy tính bên ngoài miền không. stackoverflow.com/questions/17221476/
Mạnh

Câu trả lời:


174

Cách để giải quyết vấn đề của bạn là sử dụng API Win32 có tên WNetUseConnection .
Sử dụng chức năng này để kết nối với đường dẫn UNC với xác thực, KHÔNG để ánh xạ ổ đĩa .

Điều này sẽ cho phép bạn kết nối với một máy từ xa, ngay cả khi nó không nằm trên cùng một tên miền và ngay cả khi nó có tên người dùng và mật khẩu khác.

Khi bạn đã sử dụng WNetUseConnection, bạn sẽ có thể truy cập tệp qua đường dẫn UNC như thể bạn đang ở trên cùng một tên miền. Cách tốt nhất có lẽ là thông qua các hành chính được xây dựng trong cổ phiếu.
Ví dụ: \\ computername \ c $ \ chương trình tập tin \ Thư mục \ file.txt

Dưới đây là một số mã C # mẫu sử dụng WNetUseConnection.
Lưu ý, đối với NetResource, bạn nên truyền null cho lpLocalName và lpProvider. DwType nên là RESOURCETYPE_DISK. Tên lpRemote nên là \ Tên máy tính.

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

Có cách nào để sử dụng các chức năng như thế này để mở / đóng kết nối rõ ràng với máy mạng bằng thông tin đăng nhập hiện tại, tức là không cung cấp tên người dùng và mật khẩu không? Tôi đặc biệt quan tâm đến việc đóng kết nối sau khi truy cập vào chia sẻ tệp.
flipdoubt

Không phải để kết nối, trừ khi máy tính không có tên người dùng hoặc mật khẩu. Để ngắt kết nối chắc chắn bạn có thể. Bạn thậm chí có thể làm điều đó thông qua dòng lệnh thay thế.
Brian R. Bondy

1
Chao Brian. Các tài liệu bạn liên kết để nói rằng bạn có thể chuyển NULL cho tên người dùng và mật khẩu để sử dụng thông tin đăng nhập hiện tại. Tôi sẽ làm một số thử nghiệm để xem nếu điều này làm việc.
flipdoubt

Truyền null cho tên người dùng / mật khẩu cho phép tôi kết nối, nhưng làm cách nào để chứng minh rằng tôi đã ngắt kết nối? Có cái gì trên máy chủ tôi có thể nhìn vào? Trên Server 2003, tôi có thể xem các phiên, nhưng danh sách các phiên hiện tại cập nhật nhanh như khi ứng dụng của tôi không sử dụng các API này.
flipdoubt

Có nên đóng kết nối WNetUseConnectionbằng tay bằng cách gọi WNetCancelConnection2? Hoặc có thời gian chờ không hoạt động (hoặc một số cơ chế khác) và chúng ta không phải bận tâm?
w128

123

Đối với những người đang tìm kiếm một giải pháp nhanh chóng, bạn có thể sử dụng NetworkShareAccesserTôi đã viết gần đây (dựa trên câu trả lời này (cảm ơn rất nhiều!)):

Sử dụng:

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

Chú ý: Hãy chắc hoàn toàn chắc chắn, rằng Disposetrong những NetworkShareAccesserđược gọi là (ngay cả khi bạn app crashes!), Nếu không kết nối mở sẽ vẫn còn trên Windows. Bạn có thể thấy tất cả các kết nối mở bằng cách mở cmddấu nhắc và nhập net use.

Mật mã:

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

2
bạn cũng cần using System.Runtime.InteropServices;using System.ComponentModel;cho DllImportWin32Exception
Kᴀτᴢ

Giải pháp này đã dừng tìm kiếm ngày dài của tôi. Cảm ơn!!! Hoạt động khá tốt theo yêu cầu.
Venkat

1
Tôi đang cố gắng sử dụng giải pháp của bạn với tài khoản người dùng cục bộ trên máy từ xa, nhưng tôi cứ bị lỗi truy cập từ chối. Giải pháp của bạn sẽ chỉ làm việc cho các tài khoản mạng?
M3NTA7

1
Tài khoản tồn tại trên máy từ xa, nhưng nó không phải là tài khoản mạng. Đó là một tài khoản máy cục bộ. Tôi đã thử đặt tên miền thành tên của máy. Tôi cũng đã cấp quyền đầy đủ cho tài khoản người dùng cục bộ trên thư mục dùng chung, nhưng tôi bị từ chối truy cập. Bất kỳ ý tưởng về lý do tại sao điều này có thể xảy ra? cám ơn.
M3NTA7

2
Lưu ý: việc xử lý đối tượng dường như không xóa sạch thông tin đăng nhập khỏi hệ thống (Windows 10); Tôi có thể truy cập các tệp trên máy tính từ xa sau khi kết nối bị "hủy". Đăng nhập lại vào tài khoản người dùng của tôi hoặc khởi động lại máy tính của tôi dường như xóa bộ nhớ cache nội bộ này.
Tim Cooper

16

AFAIK, bạn không cần ánh xạ đường dẫn UNC tới ký tự ổ đĩa để thiết lập thông tin đăng nhập cho máy chủ. Tôi thường xuyên sử dụng các tập lệnh bó như:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

Tuy nhiên, bất kỳ chương trình nào chạy trên cùng một tài khoản với chương trình của bạn vẫn có thể truy cập mọi thứ username:passwordcó quyền truy cập. Một giải pháp khả thi có thể là cách ly chương trình của bạn trong tài khoản người dùng cục bộ của chính nó (quyền truy cập UNC là cục bộ vào tài khoản được gọi NET USE).

Lưu ý: Sử dụng SMB trên các miền không phải là cách sử dụng tốt công nghệ, IMO. Nếu bảo mật là điều quan trọng, thì việc SMB thiếu mã hóa là một phần của một bộ điều tiết.


Nếu bạn đúng về quyền truy cập UNC chỉ khả dụng đối với tài khoản được gọi NET USE, đó có thể là một cách tiếp cận khả thi. Bạn có chắc chắn chúng tôi cần sử dụng tài khoản địa phương, tuy nhiên? Sẽ không phải là NET USEcuộc gọi được địa phương để các máy mà trên đó nó được gọi là? Bạn đã cho tôi một con đường nghiên cứu tốt
Randolpho

AFAIK và tôi có thể sai, quyền truy cập UNC sẽ chỉ khả dụng đối với hiệu trưởng bảo mật cụ thể (tài khoản SAM, bất cứ điều gì) theo đó cuộc gọi đến NET USE đã được thực hiện. Bạn có thể xác minh điều này bằng cách sử dụng RunAs để ánh xạ đường dẫn và sau đó cố gắng truy cập nó từ một tài khoản khác.
Jacob

trong trường hợp của tôi, tôi đã phải sử dụng net sử dụng \ myserver / user: username @ domain password vì người dùng ở trên một tên miền khác.
StarCub

4

Thay vì WNetUseConnection, tôi muốn giới thiệu NetUseAdd . WNetUseConnection là một chức năng cũ được WNetUseConnection2 và WNetUseConnection3 áp dụng, nhưng tất cả các chức năng đó tạo ra một thiết bị mạng có thể nhìn thấy trong Windows Explorer. NetUseAdd tương đương với việc sử dụng mạng trong dấu nhắc DOS để xác thực trên máy tính từ xa.

Nếu bạn gọi NetUseAdd thì những lần thử tiếp theo để truy cập thư mục sẽ thành công.


1
@Adam Robinson: Điều này không đúng. Không có WNetUseConnection2 hay WNetUseConnection3 như vậy. Tôi nghĩ rằng bạn đang suy nghĩ về việc WNetAddConnection được áp dụng bởi WNetAddConnection2 và WnetAddConnection3. Ngoài ra thông tin bạn đã cung cấp về nó là không đúng sự thật.
Brian R. Bondy

WNetUseConnection giống như WNetAddConnection3, nhưng nó cũng có một khả năng tùy chọn để tạo một ổ đĩa cục bộ được ánh xạ. Mà bạn không phải sử dụng.
Brian R. Bondy

@ BrianR.Bondy Họ thực sự tồn tại, chỉ không được thực hiện như C #. Nguồn: docs.microsoft.com/da-dk/windows/win32/api/lmuse/ ( Trích dẫn: "Bạn cũng có thể sử dụng các chức năng WNetAddConnection2 và WNetAddConnection3 để chuyển hướng một thiết bị cục bộ sang tài nguyên mạng."
Thomas Williams

4

Trong khi tôi không biết bản thân mình, tôi chắc chắn sẽ hy vọng rằng Số 2 không chính xác ... Tôi muốn nghĩ rằng Windows sẽ không TỰ ĐỘNG cung cấp thông tin đăng nhập của tôi (ít nhất là tất cả mật khẩu của tôi!) Cho bất kỳ máy nào , hãy để một mình không phải là một phần của niềm tin của tôi.

Bất kể, bạn đã khám phá kiến ​​trúc mạo danh? Mã của bạn sẽ trông giống như thế này:

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

    context.Undo();
}

Trong trường hợp này, tokenbiến là một IntPtr. Để có được giá trị cho biến này, bạn sẽ phải gọi hàm API API của LogonUser không được quản lý. Một chuyến đi nhanh đến pinvoke.net cung cấp cho chúng tôi chữ ký sau:

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

Tên người dùng, tên miền và mật khẩu có vẻ khá rõ ràng. Hãy xem các giá trị khác nhau có thể được chuyển cho dwLogonType và dwLogonProvider để xác định giá trị phù hợp nhất với nhu cầu của bạn.

Mã này chưa được kiểm tra, vì tôi không có tên miền thứ hai ở đây nơi tôi có thể xác minh, nhưng điều này hy vọng sẽ đưa bạn đi đúng hướng.


7
Mạo danh sẽ không hoạt động khi bạn đang cố gắng sử dụng id đăng nhập từ một tên miền không đáng tin cậy. Id người dùng phải có thể đăng nhập cục bộ.
Moose

Vâng, chúng tôi đã thử tuyến đường này, kết cục là @Moose nói: Tên miền không đáng tin cậy và do đó việc mạo danh sẽ không hoạt động.
Randolpho

Vâng, một khi tôi thấy nhận xét đó là lý do tại sao tôi đã đăng câu trả lời bằng NetUseAdd (sự khác biệt chính giữa nó và các chức năng WNetUseConnection và WNetAddConnection là NetUseAdd không hiển thị kết nối trong Windows Explorer).
Adam Robinson

Mạo danh không hoạt động trên cùng một tên miền, trong các thử nghiệm của tôi, nó tiếp tục phản hồi tôi với Access Denied khi cố đọc một tệp trên thư mục dùng chung với tài khoản quản trị viên (quản trị viên trên cả hai máy). Vì vậy, tôi nghĩ rằng đây không phải là phương pháp đúng đắn.
lidermin

4

Ở đây một lớp POC tối thiểu với tất cả các hành trình bị loại bỏ

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

Bạn có thể trực tiếp sử dụng \\server\share\folderw / WNetUseConnection, không cần phải tước nó thành \\servermột phần trước đó.


2

Hầu hết các máy chủ SFTP đều hỗ trợ SCP, điều này có thể dễ dàng tìm thấy các thư viện hơn. Bạn thậm chí có thể gọi một khách hàng hiện tại từ mã của bạn như pscp đi kèm với PuTTY .

Nếu loại tệp bạn đang làm việc là một thứ đơn giản như tệp văn bản hoặc tệp XML, bạn thậm chí có thể đi xa hơn để viết triển khai máy khách / máy chủ của riêng bạn để thao tác tệp bằng cách sử dụng .NET Remjection hoặc dịch vụ web.


1

Tôi đã thấy tùy chọn 3 được triển khai với các công cụ JScape theo cách khá đơn giản. Bạn có thể thử nó. Nó không miễn phí, nhưng nó làm công việc của nó.


1

tôi đính kèm mã vb.net của tôi dựa trên tài liệu tham khảo 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

Làm thế nào để sử dụng nó

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

    If IsNothing(login) Then



        'do your thing on the shared folder



       PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")

    End If

-1

Tôi tìm đến MS để tìm câu trả lời. Giải pháp đầu tiên giả sử tài khoản người dùng đang chạy quá trình ứng dụng có quyền truy cập vào thư mục hoặc ổ đĩa được chia sẻ (Cùng tên miền). Đảm bảo DNS của bạn được giải quyết hoặc thử sử dụng địa chỉ IP. Đơn giản chỉ cần làm như sau:

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

Nếu bạn muốn trên các miền khác nhau .NET 2.0 với thông tin đăng nhập, hãy theo mô hình này:

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

có vẻ thú vị
DeerSpotter
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.