Acessando um arquivo compartilhado (UNC) de um domínio remoto e não confiável com credenciais

151

Nós encontramos uma situação interessante que precisa ser resolvida, e minhas pesquisas acabaram. Por isso, apelo à comunidade SO para obter ajuda.

O problema é o seguinte: precisamos acessar programaticamente um arquivo compartilhado que não esteja em nosso domínio e não esteja em um domínio externo confiável via compartilhamento remoto de arquivos / UNC. Naturalmente, precisamos fornecer credenciais para a máquina remota.

Normalmente, o problema é resolvido de duas maneiras:

  1. Mapeie o compartilhamento de arquivos como uma unidade e forneça as credenciais naquele momento. Isso geralmente é feito usando o NET USEcomando ou as funções do Win32 duplicadas NET USE.
  2. Acesse o arquivo com um caminho UNC como se o computador remoto estivesse no domínio e verifique se a conta sob a qual o programa é executado está duplicada (incluindo senha) na máquina remota como usuário local. Basicamente, aproveite o fato de o Windows fornecer automaticamente as credenciais do usuário atual quando o usuário tentar acessar um arquivo compartilhado.
  3. Não use o compartilhamento remoto de arquivos. Use FTP (ou algum outro meio) para transferir o arquivo, trabalhe localmente e depois transfira-o novamente.

Por várias e diversas razões, nossos arquitetos de segurança / rede rejeitaram as duas primeiras abordagens. A segunda abordagem é obviamente uma falha de segurança; se o computador remoto estiver comprometido, o computador local está agora em risco. A primeira abordagem é insatisfatória porque a unidade recém-montada é um recurso compartilhado disponível para outros programas no computador local durante o acesso ao arquivo pelo programa. Embora seja possível tornar isso temporário, ainda é um buraco na opinião deles.

Eles estão abertos à terceira opção, mas os administradores de rede remota insistem no SFTP em vez do FTPS, e o FtpWebRequest suporta apenas o FTPS. O SFTP é a opção mais compatível com o firewall e há algumas bibliotecas que eu poderia usar para essa abordagem, mas eu prefiro reduzir minhas dependências, se puder.

Procurei no MSDN um meio gerenciado ou um win32 de usar o compartilhamento remoto de arquivos, mas não consegui encontrar nada útil.

E então pergunto: existe outro caminho? Perdi uma função super-secreta do win32 que faz o que eu quero? Ou devo buscar alguma variante da opção 3?

Randolpho
fonte
Eu o resolvi com a abordagem de representação, mas isso é entre duas máquinas fora de um domínio. Não sei se haveria um problema ao falar de um domínio para um computador fora do domínio. stackoverflow.com/questions/17221476/...
Wolf5

Respostas:

174

A maneira de resolver seu problema é usar uma API do Win32 chamada WNetUseConnection .
Use esta função para conectar-se a um caminho UNC com autenticação, NÃO para mapear uma unidade .

Isso permitirá que você se conecte a uma máquina remota, mesmo que não esteja no mesmo domínio e mesmo que tenha um nome de usuário e senha diferentes.

Depois de usar o WNetUseConnection, você poderá acessar o arquivo por um caminho UNC como se estivesse no mesmo domínio. A melhor maneira é provavelmente através dos compartilhamentos administrativos construídos.
Exemplo: \\ nome_do_computador \ c $ \ arquivos de programas \ Pasta \ arquivo.txt

Aqui está um exemplo de código C # que usa WNetUseConnection.
Observe que, para o NetResource, você deve passar nulo para lpLocalName e lpProvider. O dwType deve ser RESOURCETYPE_DISK. O lpRemoteName deve ser \\ 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
fonte
Existe alguma maneira de usar funções como essas para abrir / fechar explicitamente conexões a uma máquina de rede usando as credenciais atuais, ou seja, sem fornecer o nome de usuário e a senha? Estou especificamente interessado em fechar uma conexão depois de acessar um compartilhamento de arquivo.
Flipdoubt 23/10/09
Não para conexão, a menos que o próprio computador não tenha um nome de usuário ou senha. Para desconectar, você pode. Você pode até fazê-lo via linha de comando.
Brian R. Bondy
1
Oi Brian. Os documentos aos quais você vincula dizem que pode passar NULL para o nome de usuário e a senha para usar as credenciais atuais. Vou fazer alguns testes para ver se isso funciona.
flipdoubt
Passar nulo para o nome de usuário / senha permite a conexão, mas como posso provar que desconectei? Existe algo no servidor que eu possa ver? No Server 2003, eu posso assistir às sessões, mas a lista de sessões atuais é atualizada com a mesma rapidez quando meu aplicativo não usa essas APIs.
31
As conexões abertas devem WNetUseConnectionser fechadas manualmente chamando WNetCancelConnection2? Ou existe um tempo limite inativo (ou algum outro mecanismo) e não precisamos nos preocupar?
W128 26/11/2013
123

Para quem procura uma solução rápida, você pode usar o NetworkShareAccesserque escrevi recentemente (com base nesta resposta (muito obrigado!)):

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

AVISO: Certifique-se de que Disposeo NetworkShareAccessernome é chamado (mesmo se o aplicativo travar!); Caso contrário, uma conexão aberta permanecerá no Windows. Você pode ver todas as conexões abertas, abrindo o cmdprompt e insira net use.

O código:

/// <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
fonte
2
você também precisa using System.Runtime.InteropServices;e using System.ComponentModel;para DllImporteWin32Exception
#
Esta solução interrompeu minha pesquisa de longo dia. Obrigado!!! Funciona muito bem, conforme necessário.
Venkat
1
Estou tentando usar sua solução com uma conta de usuário local na máquina remota, mas continuo recebendo um erro de acesso negado. Sua solução funcionará apenas para contas de rede?
M3NTA7 /
1
A conta existe na máquina remota, mas não é uma conta de rede. É uma conta de máquina local. Eu tentei definir o domínio com o nome da máquina. Também dei permissões totais para a conta de usuário local na pasta compartilhada, mas o acesso foi negado. Alguma idéia de por que isso pode estar acontecendo? THX.
M3NTA7 4/17
2
Nota: o descarte do objeto parece não apagar as credenciais do sistema (Windows 10); Consigo acessar arquivos no computador remoto depois que a conexão foi "cancelada". Voltar a fazer login na minha conta de usuário ou reiniciar o computador parece limpar esse cache interno.
Tim Cooper
16

AFAIK, você não precisa mapear o caminho UNC para uma letra de unidade para estabelecer credenciais para um servidor. Eu costumava usar scripts em lote como:

net use \\myserver /user:username password

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

net use /delete \\my.server.com

No entanto, qualquer programa em execução na mesma conta do seu programa ainda poderá acessar tudo o que username:passwordtem acesso. Uma solução possível poderia ser isolar seu programa em sua própria conta de usuário local (o acesso UNC é local para a conta chamada NET USE).

Nota: O uso de SMB entre domínios não é um bom uso da tecnologia, IMO. Se a segurança é tão importante, o fato de o SMB não ter criptografia é um amortecedor por si só.

Jacob
fonte
Se você estiver certo sobre o acesso UNC estar disponível apenas para a conta que chamou NET USE, isso pode ser uma abordagem viável. Você tem certeza de que precisamos usar uma conta local? A NET USEchamada não seria local para a máquina na qual foi chamada? Você me deu um bom caminho de pesquisa
Randolpho
AFAIK, e eu posso estar errado, o acesso UNC estará disponível apenas para a entidade de segurança específica (conta SAM, qualquer que seja) sob a qual a chamada para NET USE foi feita. Você pode verificar isso usando RunAs para mapear o caminho e tentando acessá-lo de outra conta.
Jacob
no meu caso, eu tive que usar net use \\ myserver / user: nome de usuário @ senha do domínio, pois o usuário está em um domínio diferente.
StarCub 28/09
4

Em vez de WNetUseConnection, eu recomendaria NetUseAdd . WNetUseConnection é uma função herdada que foi substituída por WNetUseConnection2 e WNetUseConnection3, mas todas essas funções criam um dispositivo de rede visível no Windows Explorer. NetUseAdd é o equivalente a chamar o uso da rede em um prompt do DOS para autenticar em um computador remoto.

Se você ligar para o NetUseAdd, as tentativas subseqüentes de acessar o diretório deverão ter êxito.

Adam Robinson
fonte
1
@ Adam Robinson: Isso não é verdade. Não existe esse WNetUseConnection2 nem WNetUseConnection3. Eu acho que você está pensando em WNetAddConnection sendo substituído por WNetAddConnection2 e WnetAddConnection3. Além disso, as informações que você forneceu sobre isso não são verdadeiras.
Brian R. Bondy
WNetUseConnection é como WNetAddConnection3, mas também possui a capacidade opcional de criar uma unidade local mapeada. Que você não precisa usar.
Brian R. Bondy
@ BrianR.Bondy Eles realmente existem, apenas não implementados como C #. Fonte: docs.microsoft.com/da-dk/windows/win32/api/lmuse/… Citação: "Você também pode usar as funções WNetAddConnection2 e WNetAddConnection3 para redirecionar um dispositivo local para um recurso de rede".
Thomas Williams
4

Embora eu não me conheça, certamente espero que o número 2 esteja incorreto ... Gostaria de pensar que o Windows não fornecerá automaticamente minhas informações de login (menos a minha senha!) A qualquer máquina , muito menos um que não faz parte da minha confiança.

Independentemente disso, você explorou a arquitetura de representação? Seu código será semelhante a este:

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

    context.Undo();
}

Nesse caso, a tokenvariável é um IntPtr. Para obter um valor para essa variável, você precisará chamar a função não gerenciada da API do Windows do LogonUser. Uma rápida viagem ao pinvoke.net nos dá a seguinte assinatura:

[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 de usuário, domínio e senha devem parecer bastante óbvios. Veja os vários valores que podem ser passados ​​para dwLogonType e dwLogonProvider para determinar o que melhor se adapta às suas necessidades.

Esse código não foi testado, pois não tenho um segundo domínio aqui onde possa verificar, mas espero que isso o coloque no caminho certo.

Adam Robinson
fonte
7
A representação não funcionará quando você estiver tentando usar um ID de logon de um domínio não confiável. O ID do usuário deve poder fazer logon localmente.
24510 Moose
Sim, tentamos essa rota, e acabou sendo como o @Moose diz: O domínio não é confiável e, portanto, a representação não funciona.
31909 Randolpho
Sim, quando vi esse comentário, foi por isso que postei a resposta usando o NetUseAdd (a principal diferença entre ele e as funções WNetUseConnection e WNetAddConnection é que o NetUseAdd não torna a conexão visível no Windows Explorer).
23139 Adam Robinson
A representação não funciona no mesmo domínio; nos meus testes, ela continua me respondendo com o Acesso Negado tentando ler um arquivo em uma pasta compartilhada com uma conta de administrador (administrador nas duas máquinas). Então, acho que essa não é a abordagem correta.
lidermin
4

Aqui, uma classe POC mínima com todo o cruft removido

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

Você pode usar diretamente \\server\share\folderw / WNetUseConnection, não há necessidade de removê-lo para \\serverseparar apenas antecipadamente.

wqw
fonte
2

A maioria dos servidores SFTP também suporta SCP, o que pode ser muito mais fácil de encontrar bibliotecas. Você pode até chamar um cliente existente do seu código, como o pscp incluído no PuTTY .

Se o tipo de arquivo com o qual você está trabalhando for algo simples como um arquivo de texto ou XML, você pode até escrever sua própria implementação de cliente / servidor para manipular o arquivo usando algo como o .NET Remoting ou serviços da Web.

Ryan Bolger
fonte
1

Eu vi a opção 3 implementada com as ferramentas JScape de uma maneira bastante direta. Você pode tentar. Não é grátis, mas faz o seu trabalho.

DreamSonic
fonte
1

im anexar meu código vb.net com base na referência 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

como usá-lo

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
fonte
-1

Eu olhei para a MS para encontrar as respostas. A primeira solução supõe que a conta do usuário que está executando o processo do aplicativo tenha acesso à pasta ou unidade compartilhada (Mesmo domínio). Verifique se o seu DNS está resolvido ou tente usar o endereço IP. Simplesmente faça o seguinte:

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

Se você desejar em diferentes domínios, o .NET 2.0 com credenciais siga este modelo:

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
fonte
parece interessante
DeerSpotter