Dostęp do udostępnionego pliku (UNC) ze zdalnej, niezaufanej domeny z poświadczeniami


151

Natknęliśmy się na interesującą sytuację, która wymaga rozwiązania, a moje wyszukiwania zakończyły się niepowodzeniem. Dlatego apeluję do społeczności SO o pomoc.

Problem jest następujący: musimy programowo uzyskać dostęp do udostępnionego pliku, który nie znajduje się w naszej domenie i nie znajduje się w zaufanej domenie zewnętrznej za pośrednictwem zdalnego udostępniania plików / UNC. Oczywiście musimy dostarczyć poświadczenia do zdalnego komputera.

Zwykle można rozwiązać ten problem na jeden z dwóch sposobów:

  1. Zamapuj udział plików jako dysk i podaj w tym momencie poświadczenia. Odbywa się to zwykle za pomocą NET USEpolecenia lub funkcji Win32, które się powielają NET USE.
  2. Uzyskaj dostęp do pliku ze ścieżką UNC, tak jakby komputer zdalny znajdował się w domenie, i upewnij się, że konto, na którym działa program, jest zduplikowane (łącznie z hasłem) na komputerze zdalnym jako użytkownik lokalny. Zasadniczo należy wykorzystać fakt, że system Windows automatycznie dostarczy poświadczenia bieżącego użytkownika, gdy użytkownik spróbuje uzyskać dostęp do udostępnionego pliku.
  3. Nie używaj zdalnego udostępniania plików. Użyj FTP (lub innego sposobu), aby przesłać plik, pracuj nad nim lokalnie, a następnie prześlij go z powrotem.

Z różnych i różnych powodów nasi architekci bezpieczeństwa / sieci odrzucili pierwsze dwa podejścia. Drugie podejście to oczywiście luka w zabezpieczeniach; jeśli zdalny komputer jest zagrożony, lokalny komputer jest teraz zagrożony. Pierwsze podejście jest niezadowalające, ponieważ nowo zamontowany dysk jest współdzielonym zasobem dostępnym dla innych programów na komputerze lokalnym podczas uzyskiwania dostępu do plików przez program. Mimo że całkiem możliwe jest uczynienie tego tymczasowym, ich zdaniem nadal jest to dziura.

Są otwarte na trzecią opcję, ale zdalni administratorzy sieci nalegają na SFTP zamiast FTPS, a FtpWebRequest obsługuje tylko FTPS. SFTP jest opcją bardziej przyjazną dla zapory ogniowej i istnieje kilka bibliotek, których mógłbym użyć do tego podejścia, ale wolałbym zmniejszyć moje zależności, jeśli mogę.

Przeszukałem MSDN pod kątem zarządzanego lub win32 sposobu korzystania ze zdalnego udostępniania plików, ale nie udało mi się znaleźć niczego użytecznego.

Pytam więc: czy jest inny sposób? Czy przegapiłem super tajną funkcję win32, która robi to, czego chcę? A może muszę skorzystać z opcji 3?


Rozwiązałem to z podejściem personifikacji, ale dotyczy to 2 komputerów spoza domeny. Nie wiem, czy wystąpiłby problem z komunikacją z domeny do komputera spoza domeny. stackoverflow.com/questions/17221476/…
Wolf5

Odpowiedzi:


174

Sposobem rozwiązania problemu jest użycie interfejsu API Win32 o nazwie WNetUseConnection .
Użyj tej funkcji, aby połączyć się ze ścieżką UNC z uwierzytelnianiem, a NIE do mapowania dysku .

Umożliwi to połączenie się ze zdalnym komputerem, nawet jeśli nie znajduje się on w tej samej domenie, a nawet jeśli ma inną nazwę użytkownika i hasło.

Po użyciu WNetUseConnection będziesz mógł uzyskać dostęp do pliku za pośrednictwem ścieżki UNC, tak jakbyś był w tej samej domenie. Najlepszym sposobem jest prawdopodobnie użycie wbudowanych udziałów administracyjnych.
Przykład: \\ nazwa_komputera \ c $ \ program files \ Folder \ file.txt

Oto przykładowy kod C #, który używa WNetUseConnection.
Uwaga: w przypadku NetResource należy przekazać null dla lpLocalName i lpProvider. Typ dwType powinien mieć wartość RESOURCETYPE_DISK. Nazwa lpRemoteName powinna mieć wartość \\ nazwa_komputera.

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

Czy istnieje sposób na użycie takich funkcji do jawnego otwierania / zamykania połączeń z maszyną sieciową przy użyciu bieżących poświadczeń, tj. Bez podawania nazwy użytkownika i hasła? Jestem szczególnie zainteresowany zamknięciem połączenia po uzyskaniu dostępu do udziału plików.
flipdoubt

Nie do łączenia, chyba że sam komputer nie ma nazwy użytkownika ani hasła. Do rozłączenia z pewnością możesz. Możesz nawet zrobić to za pomocą wiersza poleceń.
Brian R. Bondy

1
Cześć Brian. Dokumenty, do których łączysz się, informują, że możesz przekazać NULL jako nazwę użytkownika i hasło, aby użyć bieżących poświadczeń. Zrobię kilka testów, aby sprawdzić, czy to działa.
flipdoubt

Podanie wartości null w nazwie użytkownika / haśle umożliwia mi połączenie, ale jak mogę udowodnić, że się rozłączyłem? Czy jest coś na serwerze, na co mogę spojrzeć? Na serwerze 2003 mogę oglądać sesje, ale lista bieżących sesji jest aktualizowana równie szybko, gdy moja aplikacja nie korzysta z tych interfejsów API.
flipdoubt

Czy połączenia otwierane za pomocą WNetUseConnectionnależy zamykać ręcznie, dzwoniąc WNetCancelConnection2? A może istnieje limit czasu bezczynności (lub jakiś inny mechanizm) i nie musimy się tym przejmować?
w128

123

Dla osób szukających szybkiego rozwiązania możesz skorzystać z NetworkShareAccessernapisanego przeze mnie niedawno (na podstawie tej odpowiedzi (wielkie dzięki!)):

Stosowanie:

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

UWAGA: Należy bezwzględnie, że Disposez NetworkShareAccessernazywany jest (nawet jeśli app awarii!), W przeciwnym razie otwarte połączenie pozostanie w systemie Windows. Możesz zobaczyć wszystkie otwarte połączenia, otwierając cmdmonit i wpisując net use.

Kod:

/// <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
potrzebujesz również using System.Runtime.InteropServices;i using System.ComponentModel;dla DllImportiWin32Exception
Kᴀτᴢ

To rozwiązanie zatrzymało moje długie poszukiwania. Dzięki!!! Działa całkiem nieźle, zgodnie z wymaganiami.
Venkat

1
Próbuję użyć twojego rozwiązania z lokalnym kontem użytkownika na zdalnym komputerze, ale ciągle otrzymuję błąd odmowy dostępu. Czy Twoje rozwiązanie będzie działać tylko dla kont sieciowych?
M3NTA7

1
Konto istnieje na zdalnym komputerze, ale nie jest kontem sieciowym. To lokalne konto komputera. Próbowałem ustawić domenę na nazwę komputera. Udzieliłem również pełnych uprawnień lokalnemu kontu użytkownika w folderze współdzielonym, ale odmowa dostępu. Jakieś pomysły, dlaczego tak się dzieje? dzięki.
M3NTA7

2
Uwaga: usunięcie obiektu nie powoduje usunięcia poświadczeń z systemu (Windows 10); Jestem w stanie uzyskać dostęp do plików na zdalnym komputerze po „anulowaniu” połączenia. Ponowne zalogowanie się na konto użytkownika lub ponowne uruchomienie komputera powoduje wyczyszczenie wewnętrznej pamięci podręcznej.
Tim Cooper

16

AFAIK, nie musisz mapować ścieżki UNC na literę dysku, aby ustanowić poświadczenia dla serwera. Regularnie korzystałem ze skryptów wsadowych, takich jak:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

Jednak każdy program działający na tym samym koncie co Twój program nadal miałby dostęp do wszystkiego, do czego username:passwordma dostęp. Możliwym rozwiązaniem może być odizolowanie programu na jego własnym lokalnym koncie użytkownika (dostęp UNC jest lokalny dla konta, które wywołało NET USE).

Uwaga: korzystanie z SMB w różnych domenach nie jest dobrym zastosowaniem technologii IMO. Jeśli bezpieczeństwo jest tak ważne, fakt, że SMB nie ma szyfrowania, sam w sobie trochę utrudnia.


Jeśli masz rację co do tego, że dostęp UNC jest dostępny tylko dla konta, które wywołało NET USE, może to być opłacalne podejście. Czy na pewno jednak musimy korzystać z konta lokalnego? Czy NET USEpołączenie nie byłoby lokalne dla komputera, na którym zostało wywołane? Dałeś mi dobrą ścieżkę badawczą
Randolpho

AFAIK, a mogę się mylić, dostęp do UNC będzie dostępny tylko dla określonego podmiotu zabezpieczeń (konta SAM, cokolwiek), w ramach którego wykonano wywołanie NET USE. Możesz to sprawdzić, używając RunAs do mapowania ścieżki, a następnie próbując uzyskać do niej dostęp z innego konta.
Jacob,

w moim przypadku musiałem użyć net use \\ myserver / user: username @ domain password, ponieważ użytkownik znajduje się w innej domenie.
StarCub

4

Zamiast WNetUseConnection poleciłbym NetUseAdd . WNetUseConnection to starsza funkcja, która została zastąpiona przez WNetUseConnection2 i WNetUseConnection3, ale wszystkie te funkcje tworzą urządzenie sieciowe, które jest widoczne w Eksploratorze Windows. NetUseAdd jest odpowiednikiem wywołania net use w monicie DOS w celu uwierzytelnienia na zdalnym komputerze.

Jeśli wywołasz NetUseAdd, kolejne próby uzyskania dostępu do katalogu powinny się powieść.


1
@Adam Robinson: To nie jest prawda. Nie ma takiego WNetUseConnection2 ani WNetUseConnection3. Myślę, że myślisz o zastąpieniu WNetAddConnection przez WNetAddConnection2 i WnetAddConnection3. Również podane przez Ciebie informacje nie są prawdziwe.
Brian R. Bondy

WNetUseConnection jest podobne do WNetAddConnection3, ale ma również opcjonalną możliwość tworzenia mapowanego dysku lokalnego. Którego nie musisz używać.
Brian R. Bondy,

@ BrianR.Bondy Rzeczywiście istnieją, tylko nie są zaimplementowane jako C #. Źródło: docs.microsoft.com/da-dk/windows/win32/api/lmuse/ ... Cytat: „Możesz także użyć funkcji WNetAddConnection2 i WNetAddConnection3, aby przekierować urządzenie lokalne do zasobu sieciowego”.
Thomas Williams,

4

Chociaż sam nie wiem, z pewnością mam nadzieję, że numer 2 jest nieprawidłowy ... Chciałbym pomyśleć, że system Windows nie będzie AUTOMATYCZNIE podawać moich danych logowania (a najmniej mojego hasła!) Do żadnego komputera , a co dopiero takiego, który nie jest częścią mojego zaufania.

Niezależnie od tego, czy poznałeś architekturę personifikacji? Twój kod będzie wyglądał podobnie do tego:

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

    context.Undo();
}

W tym przypadku tokenzmienną jest IntPtr. Aby uzyskać wartość tej zmiennej, musisz wywołać niezarządzaną funkcję LogonUser Windows API. Szybka wycieczka do pinvoke.net daje nam następujący podpis:

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

Nazwa użytkownika, domena i hasło powinny wydawać się dość oczywiste. Przyjrzyj się różnym wartościom, które można przekazać do dwLogonType i dwLogonProvider, aby określić, która najlepiej odpowiada Twoim potrzebom.

Ten kod nie został przetestowany, ponieważ nie mam tutaj drugiej domeny, w której mógłbym zweryfikować, ale mam nadzieję, że to powinno skierować Cię na właściwą ścieżkę.


7
Podszywanie się pod inne osoby nie działa, gdy próbujesz użyć identyfikatora logowania z niezaufanej domeny. Identyfikator użytkownika musi mieć możliwość logowania się lokalnie.
Łoś

Tak, wypróbowaliśmy tę trasę, ale skończyło się tak, jak mówi @Moose: domena jest niezaufana i dlatego podszywanie się nie zadziała.
Randolpho

Tak, kiedy zobaczyłem ten komentarz, dlatego opublikowałem odpowiedź za pomocą NetUseAdd (podstawowa różnica między nim a funkcjami WNetUseConnection i WNetAddConnection polega na tym, że NetUseAdd nie sprawia, że ​​połączenie jest widoczne w Eksploratorze Windows).
Adam Robinson

Podszywanie się nie działa w tej samej domenie, w moich testach ciągle odpowiada mi odmowa dostępu, próbując odczytać plik w folderze udostępnionym za pomocą konta administratora (administratora na obu komputerach). Więc myślę, że to nie jest właściwe podejście.
lidermina

4

Tutaj minimalna klasa POC ze wszystkimi usuniętymi okruchami

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

Możesz bezpośrednio użyć \\server\share\folderw / WNetUseConnection, nie ma potrzeby rozbierania go \\servertylko wcześniej.


2

Większość serwerów SFTP obsługuje również SCP, co może być o wiele łatwiejsze do znalezienia bibliotek. Możesz nawet zadzwonić do istniejącego klienta z kodu, takiego jak pscp dołączony do PuTTY .

Jeśli typ pliku, z którym pracujesz, jest czymś prostym, jak plik tekstowy lub XML, możesz nawet posunąć się do napisania własnej implementacji klient / serwer w celu manipulowania plikiem za pomocą czegoś takiego jak .NET Remoting lub usługi sieciowe.



1

im dołączyć mój kod vb.net na podstawie odniesienia 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

Jak tego użyć

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

Szukałem do stwardnienia rozsianego, aby znaleźć odpowiedzi. Pierwsze rozwiązanie zakłada, że ​​konto użytkownika uruchamiającego proces aplikacji ma dostęp do udostępnionego folderu lub dysku (ta sama domena). Upewnij się, że Twój DNS został rozwiązany lub spróbuj użyć adresu IP. Po prostu wykonaj następujące czynności:

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

Jeśli chcesz korzystać z różnych domen .NET 2.0 z poświadczeniami, postępuj zgodnie z tym modelem:

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

wygląda ciekawie
DeerSpotter
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.