224 votes

Comment fournir le nom d'utilisateur et le mot de passe lors de la connexion à un réseau partagé ?

Lors de la connexion à un partage réseau pour lequel l'utilisateur actuel (dans mon cas, un utilisateur du service réseau) n'a aucun droit, le nom et le mot de passe doivent être fournis.

Je sais comment faire cela avec les fonctions Win32 (les WNet* famille de mpr.dll ), mais j'aimerais le faire avec la fonctionnalité .Net (2.0).

Quelles sont les options disponibles ?

Peut-être qu'un complément d'information peut aider :

  • Le cas d'utilisation est un service Windows, pas une application Asp.Net.
  • Le service est exécuté sous un compte qui n'a aucun droit sur le partage.
  • Le compte utilisateur nécessaire pour le partage n'est pas connu du côté client.
  • Le client et le serveur ne sont pas membres du même domaine.

8 votes

Bien que je ne puisse pas vous donner une réponse utile, je peux vous fournir une anti-réponse L'usurpation d'identité et la création d'un processus comme le propose Marc ne fonctionneront pas lorsque le serveur et le client ne sont pas dans le même domaine, à moins qu'il n'y ait une confiance entre les deux domaines. S'il y a une confiance, je pense que cela fonctionnera. J'aurais bien répondu en tant que commentaire à celui de Marc mais je n'ai pas assez de réputation pour commenter :-/.

0 votes

362voto

Luke Quinane Points 8257

J'ai aimé Mark Brackett J'ai tellement aimé la réponse de l'auteur que j'ai fait ma propre implémentation rapide. La voici si quelqu'un en a besoin dans l'urgence :

public class NetworkConnection : IDisposable
{
    string _networkName;

    public NetworkConnection(string networkName, 
        NetworkCredential credentials)
    {
        _networkName = networkName;

        var netResource = new NetResource()
        {
            Scope = ResourceScope.GlobalNetwork,
            ResourceType = ResourceType.Disk,
            DisplayType = ResourceDisplaytype.Share,
            RemoteName = networkName
        };

        var userName = string.IsNullOrEmpty(credentials.Domain)
            ? credentials.UserName
            : string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName);

        var result = WNetAddConnection2(
            netResource, 
            credentials.Password,
            userName,
            0);

        if (result != 0)
        {
            throw new Win32Exception(result);
        }   
    }

    ~NetworkConnection()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        WNetCancelConnection2(_networkName, 0, true);
    }

    [DllImport("mpr.dll")]
    private static extern int WNetAddConnection2(NetResource netResource, 
        string password, string username, int flags);

    [DllImport("mpr.dll")]
    private static extern int WNetCancelConnection2(string name, int flags,
        bool force);
}

[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
    public ResourceScope Scope;
    public ResourceType ResourceType;
    public ResourceDisplaytype DisplayType;
    public int Usage;
    public string LocalName;
    public string RemoteName;
    public string Comment;
    public string Provider;
}

public enum ResourceScope : int
{
    Connected = 1,
    GlobalNetwork,
    Remembered,
    Recent,
    Context
};

public enum ResourceType : int
{
    Any = 0,
    Disk = 1,
    Print = 2,
    Reserved = 8,
}

public enum ResourceDisplaytype : int
{
    Generic = 0x0,
    Domain = 0x01,
    Server = 0x02,
    Share = 0x03,
    File = 0x04,
    Group = 0x05,
    Network = 0x06,
    Root = 0x07,
    Shareadmin = 0x08,
    Directory = 0x09,
    Tree = 0x0a,
    Ndscontainer = 0x0b
}

11 votes

Il devrait vraiment être throw new Win32Exception(result); puisque WNetAddConnection2 renvoie des codes d'erreur win32 ( ERROR_XXX )

0 votes

Notez que si vous vous connectez à une ressource de domaine, elle doit être string.Format(@"{0}\{1}", credentials.Domain, credentials.UserName) au lieu de simplement credentials.UserName .

0 votes

Pouvez-vous expliquer où se trouve ResourceScope.Recent y ResourceScope.Context proviennent-ils ? Puisque dwScope semble n'avoir que 3 valeurs possibles (tel qu'écrit aquí ) : RESOURCE_CONNECTED , RESOURCE_GLOBALNET , RESOURCE_REMEMBERED . De même, où se trouve ResourceType.Reserved vient ?

163voto

Mark Brackett Points 46824

Vous pouvez soit changer l'identité du thread, soit P/Invoke WNetAddConnection2. Je préfère cette dernière solution, car j'ai parfois besoin de maintenir plusieurs identifiants pour différents emplacements. Je l'emballe dans un IDisposable et appelle WNetCancelConnection2 pour supprimer les références par la suite (évitant l'erreur des noms d'utilisateur multiples) :

using (new NetworkConnection(@"\\server\read", readCredentials))
using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
   File.Copy(@"\\server\read\file", @"\\server2\write\file");
}

4 votes

Le service n'est pas membre du domaine cible - l'usurpation d'identité ne peut pas fonctionner puisque vous ne pourriez pas créer le jeton de sécurité localement et vous usurper l'identité avec celui-ci. PInvoke est le sólo manière.

0 votes

@MarkBrackett Je sais que c'est une vieille réponse, mais peut-être savez-vous encore... l'accès sera-t-il accordé au programme seulement ou également à l'utilisateur connecté via l'explorateur ?

0 votes

@Breeze - Je ne l'ai pas testé, mais je m'attendrais à ce qu'il s'authentifie pour la session de connexion ; donc si votre programme est exécuté en tant qu'utilisateur connecté, il aura également accès (au moins pour la durée de l'opération).

29voto

Hakan KOSE Points 265

J'ai cherché beaucoup de méthodes et je l'ai fait à ma façon. Vous devez ouvrir une connexion entre deux machines via l'invite de commande NET USE et après avoir terminé votre travail, effacer la connexion avec l'invite de commande NET USE "myconnection" /delete.

Vous devez utiliser le processus de l'invite de commande à partir du code derrière comme ceci :

var savePath = @"\\servername\foldername\myfilename.jpg";
var filePath = @"C:\\temp\myfileTosave.jpg";

L'utilisation est simple :

SaveACopyfileToServer(filePath, savePath);

Voici les fonctions :

using System.IO
using System.Diagnostics;

public static void SaveACopyfileToServer(string filePath, string savePath)
    {
        var directory = Path.GetDirectoryName(savePath).Trim();
        var username = "loginusername";
        var password = "loginpassword";
        var filenameToSave = Path.GetFileName(savePath);

        if (!directory.EndsWith("\\"))
            filenameToSave = "\\" + filenameToSave;

        var command = "NET USE " + directory + " /delete";
        ExecuteCommand(command, 5000);

        command = "NET USE " + directory + " /user:" + username + " " + password;
        ExecuteCommand(command, 5000);

        command = " copy \"" + filePath + "\"  \"" + directory + filenameToSave + "\"";

        ExecuteCommand(command, 5000);

        command = "NET USE " + directory + " /delete";
        ExecuteCommand(command, 5000);
    }

Et aussi la fonction ExecuteCommand est :

public static int ExecuteCommand(string command, int timeout)
    {
        var processInfo = new ProcessStartInfo("cmd.exe", "/C " + command)
                              {
                                  CreateNoWindow = true, 
                                  UseShellExecute = false, 
                                  WorkingDirectory = "C:\\",
                              };

        var process = Process.Start(processInfo);
        process.WaitForExit(timeout);
        var exitCode = process.ExitCode;
        process.Close();
        return exitCode;
    } 

Cette fonction a fonctionné très rapidement et de manière stable pour moi.

1 votes

En cas d'échec du mappage de partage, quels seraient les codes de retour ?

3voto

Craig Armstrong Points 21

OK... Je peux répondre...

Disclaimer : Je viens d'avoir une journée de plus de 18 heures (encore) Je suis vieux et distrait Je ne sais pas écrire J'ai une courte capacité d'attention, alors je ferais mieux de répondre rapidement :-)

Pregunta:

Est-il possible de changer le principal du fil de discussion pour un utilisateur sans compte sur la machine locale ?

Réponse :

Oui, vous pouvez modifier le principal d'un fil même si les informations d'identification que vous utilisez ne sont pas définies localement ou se trouvent en dehors de la "forêt".

Je viens de rencontrer ce problème en essayant de me connecter à un serveur SQL avec une authentification NTLM à partir d'un service. Cet appel utilise les informations d'identification associées au processus, ce qui signifie que vous devez soit un compte local, soit un compte de domaine pour vous authentifier avant de pouvoir vous faire passer pour un autre. Bla, bla, bla...

Mais...

L'appel de LogonUser(..) avec l'attribut ????_NEW_CREDENTIALS renvoie un jeton de sécurité sans essayer d'authentifier les informations d'identification. Merveilleux. Il n'est pas nécessaire de définir le compte dans la "forêt". Une fois que vous avez le jeton, vous pouvez appeler DuplicateToken() avec l'option d'activer l'usurpation d'identité pour obtenir un nouveau jeton. Appelez maintenant SetThreadToken( NULL, token ) ; (Cela pourrait être &token ?) Un appel à ImpersonateLoggedonUser( token ) ; pourrait être nécessaire, mais je ne le pense pas. Cherchez

Faites ce que vous devez faire

Appelez RevertToSelf() si vous avez appelé ImpersonateLoggedonUser() puis SetThreadToken( NULL, NULL ) ; (je pense... cherchez), et ensuite CloseHandle() sur les handles créés...

Je ne vous promets rien, mais cela a marché pour moi... Tout ceci n'est que le fruit de mon imagination (comme mes cheveux) et je ne sais pas épeler ! !!

3voto

Marc Gravell Points 482669

Une option qui pourrait fonctionner est d'utiliser WindowsIdentity.Impersonate (et changer le fil conducteur) pour devenir l'utilisateur souhaité, comme ça . Retour à p/invoke, cependant, j'ai bien peur que...

Une autre option effrontée (et tout aussi loin d'être idéale) pourrait être de créer un processus pour faire le travail... ProcessStartInfo accepte un .UserName , .Password y .Domain .

Enfin - peut-être exécuter le service dans un compte dédié qui a un accès ? (supprimé car vous avez précisé que ce n'était pas une option).

1 votes

Je ne pense pas que l'idée de processus soit si mauvaise. Google a publié des livres blancs sur les avantages du multiprocessing dans Chrome.

0 votes

Est-il possible de changer le principal du fil de discussion pour un utilisateur sans compte sur la machine locale ?

0 votes

Pour être honnête, je ne sais tout simplement pas... Vous devriez essayer LogonUser avec un autre domaine pour le savoir.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X