Zugriff auf eine gemeinsam genutzte Datei (UNC) von einer entfernten, nicht vertrauenswürdigen Domäne mit Anmeldeinformationen


151

Wir sind in eine interessante Situation geraten, die gelöst werden muss, und meine Suchanfragen sind nicht aufgetaucht. Ich appelliere daher an die SO-Community um Hilfe.

Das Problem ist folgendes: Wir müssen programmgesteuert auf eine freigegebene Datei zugreifen, die sich nicht in unserer Domäne befindet und sich nicht über Remote File Sharing / UNC in einer vertrauenswürdigen externen Domäne befindet. Natürlich müssen wir dem Remote-Computer Anmeldeinformationen bereitstellen.

Normalerweise löst man dieses Problem auf zwei Arten:

  1. Ordnen Sie die Dateifreigabe als Laufwerk zu und geben Sie die Anmeldeinformationen zu diesem Zeitpunkt an. Dies erfolgt normalerweise mit dem NET USEBefehl oder den duplizierten Win32-Funktionen NET USE.
  2. Greifen Sie mit einem UNC-Pfad auf die Datei zu, als ob sich der Remotecomputer in der Domäne befindet, und stellen Sie sicher, dass das Konto, unter dem das Programm ausgeführt wird, als lokaler Benutzer auf dem Remotecomputer dupliziert wird (einschließlich Kennwort). Nutzen Sie grundsätzlich die Tatsache, dass Windows automatisch die Anmeldeinformationen des aktuellen Benutzers bereitstellt, wenn der Benutzer versucht, auf eine freigegebene Datei zuzugreifen.
  3. Verwenden Sie keine Remote-Dateifreigabe. Verwenden Sie FTP (oder ein anderes Mittel), um die Datei zu übertragen, arbeiten Sie lokal daran und übertragen Sie sie dann zurück.

Aus verschiedenen Gründen haben unsere Sicherheits- / Netzwerkarchitekten die ersten beiden Ansätze abgelehnt. Der zweite Ansatz ist offensichtlich eine Sicherheitslücke; Wenn der Remotecomputer kompromittiert wird, ist der lokale Computer jetzt gefährdet. Der erste Ansatz ist unbefriedigend, da das neu gemountete Laufwerk eine gemeinsam genutzte Ressource ist, die anderen Programmen auf dem lokalen Computer während des Dateizugriffs durch das Programm zur Verfügung steht. Auch wenn es durchaus möglich ist, dies vorübergehend zu machen, ist es dennoch ein Loch in ihrer Meinung.

Sie stehen der dritten Option offen, aber die Remote-Netzwerkadministratoren bestehen eher auf SFTP als auf FTPS, und FtpWebRequest unterstützt nur FTPS. SFTP ist die Firewall-freundlichere Option, und es gibt einige Bibliotheken, die ich für diesen Ansatz verwenden könnte, aber ich würde es vorziehen, meine Abhängigkeiten zu reduzieren, wenn ich kann.

Ich habe MSDN nach einem verwalteten oder einem Win32-Mittel für die Verwendung der Remote-Dateifreigabe durchsucht, aber ich habe nichts Nützliches gefunden.

Und so frage ich: Gibt es einen anderen Weg? Habe ich eine supergeheime win32-Funktion verpasst, die macht, was ich will? Oder muss ich eine Variante von Option 3 verfolgen?


Ich habe es mit dem Identitätswechsel-Ansatz gelöst, aber das ist zwischen 2 Computern außerhalb einer Domäne. Ich weiß nicht, ob es ein Problem geben würde, von einer Domäne zu einem Computer außerhalb der Domäne zu sprechen. stackoverflow.com/questions/17221476/…
Wolf5

Antworten:


174

Um Ihr Problem zu lösen, verwenden Sie eine Win32-API namens WNetUseConnection .
Verwenden Sie diese Funktion, um eine Verbindung zu einem UNC-Pfad mit Authentifizierung herzustellen und NICHT um ein Laufwerk zuzuordnen .

Auf diese Weise können Sie eine Verbindung zu einem Remotecomputer herstellen, auch wenn dieser sich nicht in derselben Domäne befindet und einen anderen Benutzernamen und ein anderes Kennwort hat.

Sobald Sie WNetUseConnection verwendet haben, können Sie über einen UNC-Pfad auf die Datei zugreifen, als wären Sie in derselben Domäne. Der beste Weg ist wahrscheinlich durch die administrativen eingebauten Aktien.
Beispiel: \\ Computername \ c $ \ Programme \ Ordner \ Datei.txt

Hier ist ein Beispiel für einen C # -Code, der WNetUseConnection verwendet.
Beachten Sie, dass Sie für NetResource für lpLocalName und lpProvider null übergeben sollten. Der dwType sollte RESOURCETYPE_DISK sein. Der lpRemoteName sollte \\ Computername sein.

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

Gibt es eine Möglichkeit, Funktionen wie diese zu verwenden, um Verbindungen zu einem Netzwerkcomputer unter Verwendung der aktuellen Anmeldeinformationen explizit zu öffnen / zu schließen, dh ohne den Benutzernamen und das Kennwort anzugeben? Ich bin speziell daran interessiert, eine Verbindung nach dem Zugriff auf eine Dateifreigabe zu schließen.
Flipdoubt

Nicht zum Herstellen einer Verbindung, es sei denn, der Computer selbst hat keinen Benutzernamen oder kein Kennwort. Zum Trennen sicher können Sie. Sie können dies stattdessen sogar über die Befehlszeile tun.
Brian R. Bondy

1
Hallo Brian. In den von Ihnen verlinkten Dokumenten können Sie NULL als Benutzernamen und Kennwort übergeben, um die aktuellen Anmeldeinformationen zu verwenden. Ich werde einige Tests durchführen, um zu sehen, ob dies funktioniert.
Flipdoubt

Wenn ich für den Benutzernamen / das Kennwort null übergebe, kann ich eine Verbindung herstellen. Wie kann ich jedoch nachweisen, dass ich die Verbindung getrennt habe? Gibt es etwas auf dem Server, das ich mir ansehen kann? Auf Server 2003 kann ich die Sitzungen anzeigen, aber die Liste der aktuellen Sitzungen wird genauso schnell aktualisiert, wenn meine App diese APIs nicht verwendet.
Flipdoubt

Sollten Verbindungen, mit WNetUseConnectiondenen geöffnet wurde , manuell durch Aufrufen geschlossen werden WNetCancelConnection2? Oder gibt es eine Leerlaufzeit (oder einen anderen Mechanismus) und wir müssen uns nicht darum kümmern?
W128

123

Für Leute, die nach einer schnellen Lösung suchen, können Sie das verwenden, was NetworkShareAccesserich kürzlich geschrieben habe (basierend auf dieser Antwort (vielen Dank!)):

Verwendung:

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

WARNUNG: Bitte stellen Sie unbedingt sicher, dass Disposedie NetworkShareAccesseraufgerufen wird (auch wenn Ihre App abstürzt!), Andernfalls bleibt eine offene Verbindung unter Windows bestehen. Sie können alle offenen Verbindungen cmdanzeigen, indem Sie die Eingabeaufforderung öffnen und eingeben net use.

Der Code:

/// <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
Sie brauchen auch using System.Runtime.InteropServices;und using System.ComponentModel;für DllImportundWin32Exception
Kᴀτᴢ

Diese Lösung hat meine lange Tagessuche gestoppt. Vielen Dank!!! Funktioniert nach Bedarf ziemlich gut.
Venkat

1
Ich versuche, Ihre Lösung mit einem lokalen Benutzerkonto auf dem Remotecomputer zu verwenden, erhalte jedoch weiterhin den Fehler "Zugriff verweigert". Funktioniert Ihre Lösung nur für Netzwerkkonten?
M3NTA7

1
Das Konto ist auf dem Remotecomputer vorhanden, es ist jedoch kein Netzwerkkonto. Es ist ein lokales Computerkonto. Ich habe versucht, die Domäne auf den Namen des Computers festzulegen. Ich habe auch dem lokalen Benutzerkonto für den freigegebenen Ordner die vollständigen Berechtigungen erteilt, aber mir wird der Zugriff verweigert. Irgendwelche Ideen, warum dies passieren könnte? Vielen Dank.
M3NTA7

2
Hinweis: Durch das Entsorgen des Objekts werden die Anmeldeinformationen nicht vom System gelöscht (Windows 10). Ich kann auf Dateien auf dem Remotecomputer zugreifen, nachdem die Verbindung "abgebrochen" wurde. Das erneute Anmelden bei meinem Benutzerkonto oder das Neustarten meines Computers scheint diesen internen Cache zu leeren.
Tim Cooper

16

AFAIK, die Sie nicht benötigen Karte den UNC - Pfad zu einem Laufwerksbuchstaben , um Anmeldeinformationen für einen Server herzustellen. Ich habe regelmäßig Batch-Skripte verwendet wie:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

Jedes Programm, das auf demselben Konto wie Ihr Programm ausgeführt wird, kann jedoch weiterhin auf alles zugreifen, auf username:passworddas Zugriff hat. Eine mögliche Lösung könnte darin bestehen, Ihr Programm in einem eigenen lokalen Benutzerkonto zu isolieren (der UNC-Zugriff ist lokal für das aufgerufene Konto NET USE).

Hinweis: Die Verwendung von SMB über Domänen hinweg ist für die IMO-Technologie keine gute Verwendung. Wenn Sicherheit so wichtig ist, ist die Tatsache, dass SMB nicht verschlüsselt ist, für sich genommen ein Dämpfer.


Wenn Sie richtig sind, dass der UNC-Zugriff nur für das angerufene Konto verfügbar NET USEist, ist dies möglicherweise ein praktikabler Ansatz. Sind Sie sicher, dass wir ein lokales Konto verwenden müssen? Wäre der NET USEAnruf nicht lokal für den Computer, auf dem er angerufen wurde? Sie haben mir einen guten Forschungsweg gegeben
Randolpho

AFAIK, und ich kann mich irren, der UNC-Zugriff steht nur dem spezifischen Sicherheitsprinzipal (SAM-Konto, was auch immer) zur Verfügung, unter dem der Anruf bei NET USE getätigt wurde. Sie können dies überprüfen, indem Sie den Pfad mithilfe von RunAs zuordnen und dann versuchen, von einem anderen Konto aus darauf zuzugreifen.
Jacob

In meinem Fall musste ich net use \\ myserver / user: username @ domain password verwenden, da sich der Benutzer in einer anderen Domain befindet.
StarCub

4

Anstelle von WNetUseConnection würde ich NetUseAdd empfehlen . WNetUseConnection ist eine Legacy-Funktion, die von WNetUseConnection2 und WNetUseConnection3 abgelöst wurde. Alle diese Funktionen erstellen jedoch ein Netzwerkgerät, das im Windows Explorer angezeigt wird. NetUseAdd entspricht dem Aufruf der Netznutzung in einer DOS-Eingabeaufforderung zur Authentifizierung auf einem Remotecomputer.

Wenn Sie NetUseAdd aufrufen, sollten nachfolgende Versuche, auf das Verzeichnis zuzugreifen, erfolgreich sein.


1
@ Adam Robinson: Das ist nicht wahr. Es gibt weder WNetUseConnection2 noch WNetUseConnection3. Ich denke, Sie denken darüber nach, dass WNetAddConnection von WNetAddConnection2 und WnetAddConnection3 abgelöst wird. Auch die Informationen, die Sie darüber gegeben haben, sind nicht wahr.
Brian R. Bondy

WNetUseConnection ähnelt WNetAddConnection3, bietet jedoch optional die Möglichkeit, ein zugeordnetes lokales Laufwerk zu erstellen. Was du nicht benutzen musst.
Brian R. Bondy

@ BrianR.Bondy Sie existieren tatsächlich, nur nicht als C # implementiert. Quelle: docs.microsoft.com/da-dk/windows/win32/api/lmuse/… Zitat: "Sie können auch die Funktionen WNetAddConnection2 und WNetAddConnection3 verwenden, um ein lokales Gerät auf eine Netzwerkressource umzuleiten."
Thomas Williams

4

Obwohl ich mich selbst nicht kenne, würde ich sicherlich hoffen, dass # 2 falsch ist ... Ich würde gerne glauben, dass Windows meine Anmeldeinformationen (am allerwenigsten mein Passwort!) AUTOMATISCH nicht an einen Computer weitergibt geschweige denn eine, die nicht zu meinem Vertrauen gehört.

Haben Sie die Identitätswechselarchitektur untersucht? Ihr Code wird ungefähr so ​​aussehen:

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

    context.Undo();
}

In diesem Fall ist die tokenVariable ein IntPtr. Um einen Wert für diese Variable zu erhalten, müssen Sie die nicht verwaltete LogonUser Windows-API-Funktion aufrufen. Eine kurze Reise zu pinvoke.net gibt uns die folgende Signatur:

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

Benutzername, Domain und Passwort sollten ziemlich offensichtlich sein. Schauen Sie sich die verschiedenen Werte an, die an dwLogonType und dwLogonProvider übergeben werden können, um den Wert zu ermitteln, der Ihren Anforderungen am besten entspricht.

Dieser Code wurde nicht getestet, da ich hier keine zweite Domain habe, in der ich überprüfen kann, aber dies sollte Sie hoffentlich auf den richtigen Weg bringen.


7
Der Identitätswechsel funktioniert nicht, wenn Sie versuchen, eine Anmelde-ID aus einer nicht vertrauenswürdigen Domäne zu verwenden. Die Benutzer-ID muss sich lokal anmelden können.
Elch

Ja, wir haben diese Route ausprobiert. Am Ende war es so, wie @Moose sagt: Die Domain ist nicht vertrauenswürdig und daher funktioniert der Identitätswechsel nicht.
Randolpho

Ja, als ich diesen Kommentar gesehen habe, habe ich die Antwort mit NetUseAdd gepostet (der Hauptunterschied zwischen ihm und den Funktionen WNetUseConnection und WNetAddConnection besteht darin, dass NetUseAdd die Verbindung im Windows Explorer nicht sichtbar macht).
Adam Robinson

Der Identitätswechsel funktioniert auch nicht in derselben Domäne. Bei meinen Tests antwortet er mir immer wieder mit "Zugriff verweigert", wenn versucht wird, eine Datei in einem freigegebenen Ordner mit einem Administratorkonto (Administrator auf beiden Computern) zu lesen. Ich denke, das ist nicht der richtige Ansatz.
Lidermin

4

Hier wurde eine minimale POC-Klasse mit der gesamten Kruft entfernt

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

Sie können \\server\share\folderw / direkt verwenden WNetUseConnection, ohne es \\servervorher nur abisolieren zu müssen.


2

Die meisten SFTP-Server unterstützen auch SCP, für das Bibliotheken viel einfacher zu finden sind. Sie können sogar einfach einen vorhandenen Client aus Ihrem Code wie pscp aufrufen, der in PuTTY enthalten ist .

Wenn der Dateityp, mit dem Sie arbeiten, so einfach wie eine Text- oder XML-Datei ist, können Sie sogar Ihre eigene Client / Server-Implementierung schreiben, um die Datei mithilfe von .NET Remoting oder Webdiensten zu bearbeiten.


1

Ich habe gesehen, dass Option 3 mit JScape-Tools auf ziemlich einfache Weise implementiert wurde . Sie könnten es versuchen. Es ist nicht kostenlos, aber es macht seinen Job.


1

Ich füge meinen vb.net Code basierend auf Brian Referenz hinzu

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

wie man es benutzt

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

Ich suchte bei MS nach Antworten. Bei der ersten Lösung wird davon ausgegangen, dass das Benutzerkonto, auf dem der Anwendungsprozess ausgeführt wird, Zugriff auf den freigegebenen Ordner oder das freigegebene Laufwerk (dieselbe Domäne) hat. Stellen Sie sicher, dass Ihr DNS aufgelöst ist, oder verwenden Sie die IP-Adresse. Gehen Sie einfach wie folgt vor:

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

Wenn Sie .NET 2.0 mit Anmeldeinformationen für verschiedene Domänen verwenden möchten, gehen Sie wie folgt vor:

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

sieht interessant aus
DeerSpotter
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.