Accès à un fichier partagé (UNC) à partir d'un domaine distant non approuvé avec des informations d'identification

152

Nous avons rencontré une situation intéressante qui doit être résolue, et mes recherches se sont avérées nulles. J'appelle donc la communauté SO à l'aide.

Le problème est le suivant: nous avons besoin d'accéder par programme à un fichier partagé qui n'est pas dans notre domaine et qui ne se trouve pas dans un domaine externe de confiance via le partage de fichiers à distance / UNC. Naturellement, nous devons fournir des informations d'identification à la machine distante.

En règle générale, on résout ce problème de deux manières:

  1. Mappez le partage de fichiers en tant que lecteur et fournissez les informations d'identification à ce moment-là. Cela se fait généralement à l'aide de la NET USEcommande ou des fonctions Win32 qui se dupliquent NET USE.
  2. Accédez au fichier avec un chemin UNC comme si l'ordinateur distant était sur le domaine et assurez-vous que le compte sous lequel le programme s'exécute est dupliqué (y compris le mot de passe) sur l'ordinateur distant en tant qu'utilisateur local. Exploitez essentiellement le fait que Windows fournira automatiquement les informations d'identification de l'utilisateur actuel lorsque l'utilisateur tente d'accéder à un fichier partagé.
  3. N'utilisez pas le partage de fichiers à distance. Utilisez FTP (ou un autre moyen) pour transférer le fichier, travaillez dessus localement, puis transférez-le à nouveau.

Pour des raisons diverses et diverses, nos architectes de sécurité / réseau ont rejeté les deux premières approches. La deuxième approche est évidemment une faille de sécurité; si l'ordinateur distant est compromis, l'ordinateur local est maintenant en danger. La première approche n'est pas satisfaisante car le lecteur nouvellement monté est une ressource partagée disponible pour d'autres programmes sur l'ordinateur local lors de l'accès aux fichiers par le programme. Même s'il est tout à fait possible de rendre cela temporaire, c'est toujours un trou à leur avis.

Ils sont ouverts à la troisième option, mais les administrateurs réseau distants insistent sur SFTP plutôt que sur FTPS, et FtpWebRequest ne prend en charge que FTPS. SFTP est l'option la plus compatible avec le pare-feu et il y a quelques bibliothèques que je pourrais utiliser pour cette approche, mais je préférerais réduire mes dépendances si je le peux.

J'ai recherché dans MSDN un moyen géré ou win32 d'utiliser le partage de fichiers à distance, mais je n'ai rien trouvé d'utile.

Et donc je demande: y a-t-il un autre moyen? Ai-je manqué une fonction win32 super secrète qui fait ce que je veux? Ou dois-je poursuivre une variante de l'option 3?

Randolpho
la source
Je l'ai résolu avec l'approche d'emprunt d'identité, mais c'est entre 2 machines en dehors d'un domaine. Je ne sais pas si cela aurait un problème pour parler d'un domaine à un ordinateur en dehors du domaine. stackoverflow.com/questions/17221476/…
Wolf5

Réponses:

174

La façon de résoudre votre problème consiste à utiliser une API Win32 appelée WNetUseConnection .
Utilisez cette fonction pour vous connecter à un chemin UNC avec authentification, PAS pour mapper un lecteur .

Cela vous permettra de vous connecter à une machine distante, même si elle n'est pas sur le même domaine, et même si elle a un nom d'utilisateur et un mot de passe différents.

Une fois que vous avez utilisé WNetUseConnection, vous pourrez accéder au fichier via un chemin UNC comme si vous étiez sur le même domaine. Le meilleur moyen est probablement d'utiliser les partages administratifs intégrés.
Exemple: \\ computername \ c $ \ program files \ Folder \ file.txt

Voici un exemple de code C # qui utilise WNetUseConnection.
Notez que pour NetResource, vous devez passer null pour le lpLocalName et le lpProvider. Le dwType doit être RESOURCETYPE_DISK. Le lpRemoteName doit être \\ ComputerName.

using System;
using System.Runtime.InteropServices ;
using System.Threading;

namespace ExtremeMirror
{
    public class PinvokeWindowsNetworking
    {
        #region Consts
        const int RESOURCE_CONNECTED = 0x00000001;
        const int RESOURCE_GLOBALNET = 0x00000002;
        const int RESOURCE_REMEMBERED = 0x00000003;

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

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

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


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

        const int CONNECT_LOCALDRIVE = 0x00000100;
        #endregion

        #region Errors
        const int NO_ERROR = 0;

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

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

        private struct ErrorClass 
        {
            public int num;
            public string message;
            public ErrorClass(int num, string message) 
            {
                this.num = num;
                this.message = message;
            }
        }


        // Created with excel formula:
        // ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
        private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
            new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"), 
            new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"), 
            new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"), 
            new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"), 
            new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"), 
            new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"), 
            new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"), 
            new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"), 
            new ErrorClass(ERROR_MORE_DATA, "Error: More Data"), 
            new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"), 
            new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"), 
            new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"), 
            new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"), 
            new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"), 
            new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"), 
            new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"), 
            new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"), 
            new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"), 
        };

        private static string getErrorForNumber(int errNum) 
        {
            foreach (ErrorClass er in ERROR_LIST) 
            {
                if (er.num == errNum) return er.message;
            }
            return "Error: Unknown, " + errNum;
        }
        #endregion

        [DllImport("Mpr.dll")] private static extern int WNetUseConnection(
            IntPtr hwndOwner,
            NETRESOURCE lpNetResource,
            string lpPassword,
            string lpUserID,
            int dwFlags,
            string lpAccessName,
            string lpBufferSize,
            string lpResult
        );

        [DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
            string lpName,
            int dwFlags,
            bool fForce
        );

        [StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
        { 
            public int dwScope = 0;
            public int dwType = 0;
            public int dwDisplayType = 0;
            public int dwUsage = 0;
            public string lpLocalName = "";
            public string lpRemoteName = "";
            public string lpComment = "";
            public string lpProvider = "";
        }


        public static string connectToRemote(string remoteUNC, string username, string password) 
        {
            return connectToRemote(remoteUNC, username, password, false);
        }

        public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser) 
        {
            NETRESOURCE nr = new NETRESOURCE();
            nr.dwType = RESOURCETYPE_DISK;
            nr.lpRemoteName = remoteUNC;
            //          nr.lpLocalName = "F:";

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

            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }

        public static string disconnectRemote(string remoteUNC) 
        {
            int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
            if (ret == NO_ERROR) return null;
            return getErrorForNumber(ret);
        }
    }
}
Brian R. Bondy
la source
Existe-t-il un moyen d'utiliser des fonctions comme celles-ci pour ouvrir / fermer explicitement les connexions à une machine du réseau en utilisant les informations d'identification actuelles, c'est-à-dire sans fournir le nom d'utilisateur et le mot de passe? Je suis particulièrement intéressé par la fermeture d'une connexion après avoir accédé à un partage de fichiers.
flipdoubt
Pas pour la connexion, sauf si l'ordinateur lui-même n'a pas de nom d'utilisateur ou de mot de passe. Pour vous déconnecter, vous pouvez. Vous pouvez même le faire via la ligne de commande à la place.
Brian R. Bondy
1
Salut Brian. Les documents que vous liez indiquent que vous pouvez transmettre NULL pour le nom d'utilisateur et le mot de passe afin d'utiliser les informations d'identification actuelles. Je vais faire quelques tests pour voir si cela fonctionne.
flipdoubt
Passer null pour le nom d'utilisateur / mot de passe me permet de me connecter, mais comment puis-je prouver que je me suis déconnecté? Y a-t-il quelque chose sur le serveur que je peux consulter? Sur Server 2003, je peux regarder les sessions, mais la liste des sessions en cours se met à jour tout aussi rapidement lorsque mon application n'utilise pas ces API.
flipdoubt
Les connexions ouvertes avec WNetUseConnectiondoivent-elles être fermées manuellement en appelant WNetCancelConnection2? Ou y a-t-il un délai d'inactivité (ou un autre mécanisme) et nous n'avons pas à nous soucier?
w128 du
123

Pour les personnes à la recherche d'une solution rapide, vous pouvez utiliser le que NetworkShareAccesserj'ai écrit récemment (basé sur cette réponse (merci beaucoup!)):

Usage:

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

AVERTISSEMENT: assurez-vous absolument que celui Disposede NetworkShareAccesserest appelé (même si votre application plante!), Sinon une connexion ouverte restera sous Windows. Vous pouvez voir toutes les connexions ouvertes en ouvrant l' cmdinvite et en entrant net use.

Le 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);
    }
}
GameScripting
la source
2
vous avez besoin aussi using System.Runtime.InteropServices;et using System.ComponentModel;pour DllImportetWin32Exception
Kᴀτᴢ
Cette solution a arrêté ma longue journée de recherche. Merci!!! Fonctionne assez bien selon les besoins.
Venkat
1
J'essaie d'utiliser votre solution avec un compte d'utilisateur local sur la machine distante, mais j'obtiens toujours une erreur d'accès refusé. Votre solution fonctionnera-t-elle uniquement pour les comptes réseau?
M3NTA7
1
Le compte existe sur la machine distante, mais ce n'est pas un compte réseau. C'est un compte d'ordinateur local. J'ai essayé de définir le domaine sur le nom de la machine. J'ai également donné toutes les autorisations au compte d'utilisateur local sur le dossier partagé, mais l'accès est refusé. Des idées sur pourquoi cela peut se produire? THX.
M3NTA7
2
Remarque: la suppression de l'objet ne semble pas effacer les informations d'identification du système (Windows 10); Je peux accéder aux fichiers sur l'ordinateur distant une fois la connexion "annulée". La reconnexion à mon compte utilisateur ou le redémarrage de mon ordinateur semble vider ce cache interne.
Tim Cooper
16

AFAIK, vous n'avez pas besoin de mapper le chemin UNC à une lettre de lecteur afin d'établir les informations d'identification pour un serveur. J'utilisais régulièrement des scripts batch comme:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

Cependant, tout programme exécuté sur le même compte que votre programme pourra toujours accéder à tout ce qui username:passworda accès. Une solution possible pourrait être d'isoler votre programme dans son propre compte d'utilisateur local (l'accès UNC est local sur le compte qui a appelé NET USE).

Remarque: utilisation de SMB dans tous les domaines n'est pas une bonne utilisation de la technologie, IMO. Si la sécurité est si importante, le fait que SMB manque de cryptage est un peu un frein en soi.

Jacob
la source
Si vous avez raison de dire que l'accès UNC n'est disponible que pour le compte qui a appelé NET USE, cela pourrait être une approche viable. Êtes-vous certain que nous devons utiliser un compte local? L' NET USEappel ne serait-il pas local sur la machine sur laquelle il a été appelé? Vous m'avez donné un bon chemin de recherche
Randolpho
AFAIK, et je me trompe peut-être, l'accès UNC ne sera disponible que pour le principal de sécurité spécifique (compte SAM, peu importe) sous lequel l'appel à NET USE a été effectué. Vous pouvez le vérifier en utilisant RunAs pour mapper le chemin, puis en essayant d'y accéder à partir d'un autre compte.
Jacob
dans mon cas, j'ai dû utiliser net use \\ myserver / user: username @ domain password car l'utilisateur est sur un domaine différent.
StarCub du
4

Plutôt que WNetUseConnection, je recommanderais NetUseAdd . WNetUseConnection est une fonction héritée qui a été remplacée par WNetUseConnection2 et WNetUseConnection3, mais toutes ces fonctions créent un périphérique réseau visible dans l'Explorateur Windows. NetUseAdd est l'équivalent de l'appel net use dans une invite DOS pour s'authentifier sur un ordinateur distant.

Si vous appelez NetUseAdd, les tentatives suivantes pour accéder au répertoire devraient réussir.

Adam Robinson
la source
1
@Adam Robinson: Ce n'est pas vrai. Il n'existe pas de tels WNetUseConnection2 ni WNetUseConnection3. Je pense que vous pensez que WNetAddConnection est remplacé par WNetAddConnection2 et WnetAddConnection3. De plus, les informations que vous avez données à ce sujet ne sont pas vraies.
Brian R. Bondy
WNetUseConnection est comme WNetAddConnection3, mais il a également une capacité facultative de créer un lecteur local mappé. Ce que vous n'avez pas à utiliser.
Brian R. Bondy
@ BrianR.Bondy Ils existent bel et bien, mais pas implémentés en C #. Source: docs.microsoft.com/da-dk/windows/win32/api/lmuse/… Citation: "Vous pouvez également utiliser les fonctions WNetAddConnection2 et WNetAddConnection3 pour rediriger un périphérique local vers une ressource réseau."
Thomas Williams
4

Bien que je ne me connaisse pas, j'espère certainement que le n ° 2 est incorrect ... J'aimerais penser que Windows ne va pas donner AUTOMATIQUEMENT mes informations de connexion (surtout mon mot de passe!) À aucune machine , encore moins une qui ne fait pas partie de ma confiance.

Quoi qu'il en soit, avez-vous exploré l'architecture de l'usurpation d'identité? Votre code va ressembler à ceci:

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

    context.Undo();
}

Dans ce cas, la tokenvariable est un IntPtr. Afin d'obtenir une valeur pour cette variable, vous devrez appeler la fonction API Windows LogonUser non gérée. Un petit tour sur pinvoke.net nous donne la signature suivante:

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

Le nom d'utilisateur, le domaine et le mot de passe devraient sembler assez évidents. Jetez un œil aux différentes valeurs qui peuvent être transmises à dwLogonType et dwLogonProvider pour déterminer celle qui répond le mieux à vos besoins.

Ce code n'a pas été testé, car je n'ai pas de deuxième domaine ici où je puisse le vérifier, mais cela devrait, espérons-le, vous mettre sur la bonne voie.

Adam Robinson
la source
7
L'emprunt d'identité ne fonctionnera pas lorsque vous essayez d'utiliser un identifiant de connexion d'un domaine non approuvé. L'ID utilisateur doit pouvoir se connecter localement.
Moose
Oui, nous avons essayé cette route, elle a fini par être comme le dit @Moose: Le domaine n'est pas approuvé et donc l'emprunt d'identité ne fonctionnera pas.
Randolpho
Oui, une fois que j'ai vu ce commentaire, c'est pourquoi j'ai publié la réponse en utilisant NetUseAdd (la principale différence entre celui-ci et les fonctions WNetUseConnection et WNetAddConnection étant que NetUseAdd ne rend pas la connexion visible dans l'Explorateur Windows).
Adam Robinson
L'emprunt d'identité ne fonctionne pas non plus sur le même domaine, lors de mes tests, il continue de me répondre avec Accès refusé en essayant de lire un fichier sur un dossier partagé avec un compte administrateur (administrateur sur les deux machines). Donc, je pense que ce n'est pas la bonne approche.
lidermin
4

Ici une classe POC minimale avec toute la cruft supprimée

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

Vous pouvez directement utiliser \\server\share\folderw / WNetUseConnection, pas besoin de le déshabiller \\serveruniquement au préalable.

wqw
la source
2

La plupart des serveurs SFTP prennent également en charge SCP, ce qui peut être beaucoup plus facile à trouver des bibliothèques. Vous pouvez même simplement appeler un client existant à partir de votre code comme pscp inclus avec PuTTY .

Si le type de fichier avec lequel vous travaillez est quelque chose de simple comme un fichier texte ou XML, vous pouvez même aller jusqu'à écrire votre propre implémentation client / serveur pour manipuler le fichier en utilisant quelque chose comme .NET Remoting ou des services Web.

Ryan Bolger
la source
1

J'ai vu l'option 3 implémentée avec les outils JScape de manière assez simple. Vous pourriez essayer. Ce n'est pas gratuit, mais cela fait son travail.

DreamSonic
la source
1

Je joins mon code vb.net basé sur la référence 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

comment l'utiliser

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

    If IsNothing(login) Then



        'do your thing on the shared folder



       PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")

    End If
roy.d
la source
-1

J'ai regardé MS pour trouver les réponses. La première solution suppose que le compte d'utilisateur exécutant le processus d'application a accès au dossier ou au lecteur partagé (même domaine). Assurez-vous que votre DNS est résolu ou essayez d'utiliser l'adresse IP. Procédez simplement comme suit:

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

Si vous voulez sur différents domaines .NET 2.0 avec des informations d'identification, suivez ce modèle:

WebRequest req = FileWebRequest.Create(new Uri(@"\\<server Name>\Dir\test.txt"));

        req.Credentials = new NetworkCredential(@"<Domain>\<User>", "<Password>");
        req.PreAuthenticate = true;

        WebResponse d = req.GetResponse();
        FileStream fs = File.Create("test.txt");

        // here you can check that the cast was successful if you want. 
        fs = d.GetResponseStream() as FileStream;
        fs.Close();
Kentonbmax
la source
semble intéressant
DeerSpotter