Accesso a un file condiviso (UNC) da un dominio remoto, non attendibile con credenziali


151

Ci siamo imbattuti in una situazione interessante che deve essere risolta e le mie ricerche si sono rivelate nulle. Chiedo quindi aiuto alla comunità SO.

Il problema è questo: abbiamo bisogno di accedere a livello di codice a un file condiviso che non è nel nostro dominio e non si trova all'interno di un dominio esterno di fiducia tramite la condivisione di file / UNC remoti. Naturalmente, dobbiamo fornire le credenziali alla macchina remota.

In genere, si risolve questo problema in due modi:

  1. Mappare la condivisione file come unità e fornire le credenziali in quel momento. Questo in genere viene eseguito utilizzando il NET USEcomando o le funzioni Win32 duplicate NET USE.
  2. Accedere al file con un percorso UNC come se il computer remoto fosse nel dominio e assicurarsi che l'account con cui viene eseguito il programma sia duplicato (inclusa la password) sul computer remoto come utente locale. Fondamentalmente sfrutta il fatto che Windows fornirà automaticamente le credenziali dell'utente corrente quando l'utente tenta di accedere a un file condiviso.
  3. Non utilizzare la condivisione di file remota. Utilizzare FTP (o altri mezzi) per trasferire il file, lavorarci su localmente, quindi trasferirlo nuovamente.

Per vari e vari motivi, i nostri architetti di sicurezza / rete hanno rifiutato i primi due approcci. Il secondo approccio è ovviamente un buco nella sicurezza; se il computer remoto è compromesso, il computer locale è ora a rischio. Il primo approccio non è soddisfacente perché l'unità appena montata è una risorsa condivisa disponibile per altri programmi sul computer locale durante l'accesso ai file da parte del programma. Anche se è del tutto possibile rendere questo temporaneo, è ancora un buco nella loro opinione.

Sono aperti alla terza opzione, ma gli amministratori della rete remota insistono su SFTP piuttosto che su FTPS e FtpWebRequest supporta solo FTPS. SFTP è l'opzione più amichevole per i firewall e ci sono un paio di librerie che potrei usare per questo approccio, ma preferirei ridurre le mie dipendenze se potessi.

Ho cercato in MSDN un mezzo gestito o win32 per utilizzare la condivisione file remota, ma non sono riuscito a trovare qualcosa di utile.

E quindi chiedo: c'è un altro modo? Mi sono perso una funzione win32 super segreta che fa quello che voglio? O devo perseguire una variante dell'opzione 3?


L'ho risolto con l'approccio della rappresentazione, ma è compreso tra 2 macchine esterne a un dominio. Non so se avrebbe un problema a parlare da un dominio a un computer esterno al dominio. stackoverflow.com/questions/17221476/...
Wolf5

Risposte:


174

Il modo per risolvere il problema è utilizzare un'API Win32 chiamata WNetUseConnection .
Utilizzare questa funzione per connettersi a un percorso UNC con autenticazione, NON per mappare un'unità .

Ciò ti consentirà di collegarti a un computer remoto, anche se non si trova nello stesso dominio e anche se ha un nome utente e una password diversi.

Dopo aver utilizzato WNetUseConnection, sarai in grado di accedere al file tramite un percorso UNC come se fossi sullo stesso dominio. Il modo migliore è probabilmente attraverso le condivisioni amministrative integrate.
Esempio: \\ nomecomputer \ c $ \ programmi \ Cartella \ file.txt

Ecco alcuni esempi di codice C # che utilizza WNetUseConnection.
Nota, per NetResource, è necessario passare null per lpLocalName e lpProvider. Il tipo dwT dovrebbe essere RESOURCETYPE_DISK. LpRemoteName dovrebbe essere \\ 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);
        }
    }
}

Esiste un modo per utilizzare funzioni come queste per aprire / chiudere esplicitamente le connessioni a una macchina di rete utilizzando le credenziali correnti, ovvero senza fornire nome utente e password? Sono specificamente interessato a chiudere una connessione dopo aver effettuato l'accesso a una condivisione file.
capovolgere il

Non per la connessione, a meno che il computer stesso non abbia un nome utente o una password. Per disconnetterti sicuramente puoi. Puoi anche farlo tramite riga di comando.
Brian R. Bondy,

1
Ciao Brian I documenti collegati per dire che è possibile passare NULL per il nome utente e la password per utilizzare le credenziali correnti. Farò alcuni test per vedere se funziona.
flipdoubt,

Il passaggio null per il nome utente / password mi consente di connettermi, ma come posso provare di essermi disconnesso? C'è qualcosa sul server che posso guardare? Su Server 2003, posso guardare le sessioni, ma l'elenco degli aggiornamenti delle sessioni correnti si aggiorna altrettanto velocemente quando la mia app non utilizza queste API.
flipdoubt,

Le connessioni aperte WNetUseConnectiondevono essere chiuse manualmente chiamando WNetCancelConnection2? Oppure c'è un timeout di inattività (o qualche altro meccanismo) e non dobbiamo preoccuparci?
w128,

123

Per le persone in cerca di una soluzione rapida, è possibile utilizzare la versione NetworkShareAccesserscritta di recente (sulla base di questa risposta (grazie mille!)):

Uso:

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

ATTENZIONE: Si prega di fare assolutamente sicuri, che Disposela NetworkShareAccessersi chiama (anche se si va in crash App!), Altrimenti una connessione aperta rimane su Windows. Puoi vedere tutte le connessioni aperte aprendo il cmdprompt e inviando net use.

Il codice:

/// <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
devi anche using System.Runtime.InteropServices;e using System.ComponentModel;per DllImporteWin32Exception
Kᴀτᴢ

Questa soluzione ha interrotto la mia lunga ricerca. Grazie!!! Funziona abbastanza bene come richiesto.
Venkat,

1
Sto cercando di utilizzare la tua soluzione con un account utente locale sul computer remoto, ma continuo a ricevere un errore di accesso negato. La tua soluzione funzionerà solo per gli account di rete?
M3NTA7,

1
L'account esiste sul computer remoto, ma non è un account di rete. È un account macchina locale. Ho provato a impostare il dominio sul nome della macchina. Ho anche fornito le autorizzazioni complete per l'account utente locale sulla cartella condivisa, ma mi viene negato l'accesso. Qualche idea sul perché questo potrebbe accadere? grazie.
M3NTA7,

2
Nota: l'eliminazione dell'oggetto non sembra cancellare le credenziali dal sistema (Windows 10); Sono in grado di accedere ai file sul computer remoto dopo che la connessione è stata "annullata". Riaccedere al mio account utente o riavviare il mio computer sembra cancellare questa cache interna.
Tim Cooper,

16

AFAIK, non è necessario mappare il percorso UNC a una lettera di unità per stabilire le credenziali per un server. Ho usato regolarmente script batch come:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

Tuttavia, qualsiasi programma in esecuzione sullo stesso account del programma sarebbe comunque in grado di accedere a tutto ciò a cui username:passwordha accesso. Una possibile soluzione potrebbe essere quella di isolare il programma nel proprio account utente locale (l'accesso UNC è locale all'account che ha chiamato NET USE).

Nota: l' utilizzo dei domini SMB non è un buon uso della tecnologia, IMO. Se la sicurezza è così importante, il fatto che SMB manchi di crittografia è un po 'smorzante da solo.


Se hai ragione sul fatto che l'accesso UNC sia disponibile solo per l'account che ha chiamato NET USE, potrebbe essere un approccio praticabile. Sei sicuro che dobbiamo usare un account locale, tuttavia? La NET USEchiamata non sarebbe locale per la macchina su cui era stata chiamata? Mi hai dato un buon percorso di ricerca
Randolpho,

AFAIK, e potrei sbagliarmi, l'accesso UNC sarà disponibile solo per l'entità di sicurezza specifica (account SAM, qualunque cosa) con cui è stata effettuata la chiamata a NET USE. È possibile verificarlo utilizzando RunAs per mappare il percorso e quindi tentando di accedervi da un altro account.
Jacob,

nel mio caso, ho dovuto usare net use \\ myserver / user: username @ domain password poiché l'utente si trova su un altro dominio.
StarCub,

4

Invece di WNetUseConnection, consiglierei NetUseAdd . WNetUseConnection è una funzione legacy sostituita da WNetUseConnection2 e WNetUseConnection3, ma tutte queste funzioni creano un dispositivo di rete visibile in Esplora risorse. NetUseAdd equivale a chiamare net use in un prompt DOS per l'autenticazione su un computer remoto.

Se si chiama NetUseAdd, i tentativi successivi di accesso alla directory dovrebbero avere esito positivo.


1
@Adam Robinson: questo non è vero. Non esiste WNetUseConnection2 né WNetUseConnection3. Penso che tu stia pensando a WNetAddConnection sostituito da WNetAddConnection2 e WnetAddConnection3. Anche le informazioni che ci hai fornito non sono vere.
Brian R. Bondy,

WNetUseConnection è come WNetAddConnection3, ma ha anche la possibilità opzionale di creare un'unità locale mappata. Che non devi usare.
Brian R. Bondy,

@ BrianR.Bondy Esistono davvero, ma non implementati come C #. Fonte: docs.microsoft.com/da-dk/windows/win32/api/lmuse/… Citazione: "Puoi anche utilizzare le funzioni WNetAddConnection2 e WNetAddConnection3 per reindirizzare un dispositivo locale a una risorsa di rete."
Thomas Williams,

4

Anche se non mi conosco, spero sicuramente che il numero 2 sia errato ... Mi piacerebbe pensare che Windows non fornirà AUTOMATICAMENTE le mie informazioni di accesso (soprattutto la mia password!) A qualsiasi macchina , figuriamoci uno che non fa parte della mia fiducia.

Indipendentemente da ciò, hai esplorato l'architettura della rappresentazione? Il tuo codice sarà simile a questo:

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

    context.Undo();
}

In questo caso, la tokenvariabile è IntPtr. Per ottenere un valore per questa variabile, dovrai chiamare la funzione API di Windows LogonUser non gestita. Un breve viaggio su pinvoke.net ci dà la seguente firma:

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

Nome utente, dominio e password dovrebbero sembrare abbastanza ovvi. Dai un'occhiata ai vari valori che possono essere passati a dwLogonType e dwLogonProvider per determinare quello più adatto alle tue esigenze.

Questo codice non è stato testato, in quanto non ho un secondo dominio qui in cui posso verificare, ma si spera che questo ti porti sulla strada giusta.


7
La rappresentazione non funzionerà quando si tenta di utilizzare un ID di accesso da un dominio non attendibile. L'ID utente deve essere in grado di accedere localmente.
Moose,

Sì, abbiamo provato questa strada, alla fine è stato come dice @Moose: il dominio non è attendibile e pertanto la rappresentazione non funzionerà.
Randolpho,

Sì, una volta che ho visto quel commento è per questo che ho pubblicato la risposta usando NetUseAdd (la differenza principale tra esso e le funzioni WNetUseConnection e WNetAddConnection è che NetUseAdd non rende visibile la connessione in Windows Explorer).
Adam Robinson,

La rappresentazione non funziona sullo stesso dominio, nei miei test continua a rispondermi con Accesso negato nel tentativo di leggere un file in una cartella condivisa con un account amministratore (amministratore su entrambe le macchine). Quindi, penso che questo non sia l'approccio giusto.
lidermin

4

Qui è stata rimossa una classe POC minima con tutta l'innesto rimosso

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

È possibile utilizzare direttamente \\server\share\folderw / WNetUseConnection, non è necessario rimuoverlo per \\serversepararlo solo in anticipo.


2

La maggior parte dei server SFTP supporta anche SCP, che può essere molto più facile trovare librerie per. Potresti anche semplicemente chiamare un client esistente dal tuo codice come pscp incluso con PuTTY .

Se il tipo di file con cui stai lavorando è qualcosa di semplice come un file di testo o XML, potresti persino arrivare a scrivere la tua implementazione client / server per manipolare il file usando qualcosa come .NET Remoting o servizi web.



1

allego il mio codice vb.net basato su riferimento 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

come usarlo

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

Ho cercato MS per trovare le risposte. La prima soluzione presuppone che l'account utente che esegue il processo dell'applicazione abbia accesso alla cartella o all'unità condivisa (stesso dominio). Assicurati che il tuo DNS sia stato risolto o prova a utilizzare l'indirizzo IP. Basta fare quanto segue:

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

Se si desidera su diversi domini .NET 2.0 con credenziali seguire questo modello:

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

sembra interessante
DeerSpotter
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.