Pergunta

Eu fiz uma pergunta há alguns dias (Acesso ao SQL Server 2005 de uma máquina que não seja de domínio usando a autenticação do Windows) que tiveram algumas sugestões interessantes, mas não utilizáveis. Eu gostaria de fazer a pergunta novamente, mas esclareça quais são minhas restrições:

Eu tenho um domínio Windows dentro do qual uma máquina está executando o SQL Server 2005 e que está configurado para suportar apenas a autenticação do Windows. Eu gostaria de executar um aplicativo cliente C# em uma máquina na mesma rede, mas que não está no domínio e acessar um banco de dados na instância do SQL Server 2005.

Não posso criar ou modificar os usuários do SQL Server em qualquer máquina e não posso fazer alterações nas permissões ou representação, e não posso usar o Runas.

Sei que posso escrever aplicativos Perl e Java que podem se conectar ao banco de dados do SQL Server usando apenas esses quatro parâmetros: nome do servidor, nome do banco de dados, nome de usuário (no domínio do formulário user) e senha.

Em C# eu tentei várias coisas por aí:

string connectionString = "Data Source=server;Initial Catalog=database;User Id=domain\user;Password=password";
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();

e tentei definir segurança integrada como verdadeira e falsa, mas nada parece funcionar. O que estou tentando fazer é simplesmente impossível em C#?

Obrigado por qualquer ajuda, Martin

Foi útil?

Solução

Como você diz corretamente, JDBC ou Perl em uma máquina Linux podem se conectar a um servidor SQL usando a autenticação do Windows e credenciais que diferem do usuário atualmente conectado. O mesmo vale para dispositivos Windows CE, a propósito.

Eu acho que isso é que isso não é um problema de C#, mas do driver SQL Server Ole DB. Eu acho que os métodos mencionados acima "fingem ser uma máquina Windows usando algumas credenciais específicas" no nível da rede; Um recurso, que falta do driver SQL Server Ole DB. Assim, minha sugestão seria procurar um driver alternativo (talvez comercial?) Ole DB que pode acessar bancos de dados do SQL Server. Não tenho certeza se existe tal coisa.

Outras dicas

Eu tinha um problema semelhante em que estava escrevendo uma ferramenta que precisava ser executada em uma máquina em um domínio e autenticar com um servidor SQL em outro domínio usando uma conexão confiável. Tudo o que pude encontrar sobre o assunto dizia que não poderia ser feito. Em vez disso, você deve ingressar no domínio, usar a autenticação do SQL, se envolver com algum sujeito chamado Kerberos ou fazer com que seus caras da rede configurem um relacionamento confiável, para citar algumas alternativas.

O problema é que eu sabia que poderia fazê -lo funcionar de alguma forma usando Runas, porque eu providei com o SSMS:

C:\WINDOWS\system32\runas.exe /netonly /savecred /user:megacorp\joe.bloggs "C:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\SqlWb.exe"

O sinalizador /netonly me permitiu executar o EXE com as credenciais locais e acessar a rede com as credenciais remotas, acho que, de qualquer forma, recebi o conjunto de resultados que esperava do servidor remoto. O problema era que o comando de Runas tornou muito difícil depurar o aplicativo e não cheira bem.

Eventualmente eu encontrei este artigo sobre o projeto de código O que estava falando sobre a autenticação de manipular o Active Directory, aqui está a classe principal que faz a representação:

    using System;
    using System.Runtime.InteropServices;  // DllImport
    using System.Security.Principal; // WindowsImpersonationContext

    namespace TestApp
    {
        class Impersonator
        {
            // group type enum
            enum SECURITY_IMPERSONATION_LEVEL : int
            {
                SecurityAnonymous = 0,
                SecurityIdentification = 1,
                SecurityImpersonation = 2,
                SecurityDelegation = 3
            }

            // obtains user token
            [DllImport("advapi32.dll", SetLastError = true)]
            static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword,
                int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

            // closes open handes returned by LogonUser
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            extern static bool CloseHandle(IntPtr handle);

            // creates duplicate token handle
            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
                int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);

            WindowsImpersonationContext newUser;

            /// 
            /// Attempts to impersonate a user.  If successful, returns 
            /// a WindowsImpersonationContext of the new users identity.
            /// 
            /// Username you want to impersonate
            /// Logon domain
            /// User's password to logon with
            /// 
            public Impersonator(string sUsername, string sDomain, string sPassword)
            {
                // initialize tokens
                IntPtr pExistingTokenHandle = new IntPtr(0);
                IntPtr pDuplicateTokenHandle = new IntPtr(0);
                pExistingTokenHandle = IntPtr.Zero;
                pDuplicateTokenHandle = IntPtr.Zero;

                // if domain name was blank, assume local machine
                if (sDomain == "")
                    sDomain = System.Environment.MachineName;

                try
                {
                    const int LOGON32_PROVIDER_DEFAULT = 0;

                    // create token
                    // const int LOGON32_LOGON_INTERACTIVE = 2;
                    const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
                    //const int SecurityImpersonation = 2;

                    // get handle to token
                    bool bImpersonated = LogonUser(sUsername, sDomain, sPassword,
                        LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT, ref pExistingTokenHandle);

                    // did impersonation fail?
                    if (false == bImpersonated)
                    {
                        int nErrorCode = Marshal.GetLastWin32Error();

                        // show the reason why LogonUser failed
                        throw new ApplicationException("LogonUser() failed with error code: " + nErrorCode);
                    }

                    bool bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref pDuplicateTokenHandle);

                    // did DuplicateToken fail?
                    if (false == bRetVal)
                    {
                        int nErrorCode = Marshal.GetLastWin32Error();
                        CloseHandle(pExistingTokenHandle); // close existing handle

                        // show the reason why DuplicateToken failed
                        throw new ApplicationException("DuplicateToken() failed with error code: " + nErrorCode);
                    }
                    else
                    {
                        // create new identity using new primary token
                        WindowsIdentity newId = new WindowsIdentity(pDuplicateTokenHandle);
                        WindowsImpersonationContext impersonatedUser = newId.Impersonate();

                        newUser = impersonatedUser;
                    }
                }
                finally
                {
                    // close handle(s)
                    if (pExistingTokenHandle != IntPtr.Zero)
                        CloseHandle(pExistingTokenHandle);
                    if (pDuplicateTokenHandle != IntPtr.Zero)
                        CloseHandle(pDuplicateTokenHandle);
                }
            }

            public void Undo()
            {
                newUser.Undo();
            }
        }
    }

Para usá -lo apenas:

Impersonator impersonator = new Impersonator("username", "domain", "password");

//Connect to and use SQL server

impersonator.Undo();

Eu adicionei no método de desfazer, caso contrário, o objeto do imitador tendia a receber o lixo. Também alterei o código para usar logon32_logon_new_credentials, mas isso foi um cutucho e correr para fazê -lo funcionar; Ainda preciso entender completamente o que faz, tenho a sensação de que é o mesmo que a bandeira /Netonly em Runas. Também vou quebrar um pouco o construtor.

É inútil especificar o nome de usuário e a senha na string de conexão porque eles implicam a autenticação SQL e você já especificou que o SQL Server aceita apenas a autenticação do Windows.

Se o servidor não permitir a autenticação SQL, então o A possibilidade de conectar é usar a autenticação do Windows, ou seja. IntegratedSecurity=true. O que significa que seu cliente se autenticará como qualquer credencial executando o processo (ou está sendo representado no momento).

Para que a autenticação do Windows funcione, você deve escolher um dos seguintes:

  • Junte-se à máquina que não é do domínio em um domínio (pode ser o seu próprio domínio!) Que confia no domínio do servidor e execute o processo do cliente como uma credencial do usuário do Domínio Usuário.
  • Use contas espelhadas do NTLM: um par de usuários locais no cliente e o servidor com nome e senhas idênticos.
  • Grant como acesso anônimo ao servidor SQL.

Se você não puder fazer com que o host do cliente confie no domínio do servidor, nem poderá adicionar contas espelhadas NTLM, e o administrador do servidor SQL é o suficiente para não permitir que o Anonymous, não poderá se conectar.

Você tem que Configure o SQL Server Para permitir a autenticação do SQL Server, ou seja, a autenticação usando nome de usuário e senha.

Você não pode se autenticar por nome de usuário/senha do domínio 'como' autenticação do servidor, ou seja, especifique o nome de usuário/senha do domínio diretamente.

Eu posso estar errado, é claro, mas tenho certeza de que isso não é um problema de C# ou .NET. Como você pode fazer login no SQL Server no seu aplicativo Perl ou Java ??

Vou dar a você a resposta Java com a qual estou mais familiarizado: uso o driver JTDS JDBC com os quatro parâmetros mencionados acima. O aplicativo PERL que eu conheço menos, mas está em execução em uma caixa Linux e é capaz de se conectar com os mesmos parâmetros. Não posso alterar o SQL Server para oferecer suporte à autenticação SQL.

Para responder as sugestões de Remus, não posso fazer nenhuma dessas três coisas que ele sugere e, no entanto, os aplicativos Java e Perl podem se conectar. Alguma outra idéia?

Obrigado, Martin

É uma opção Prompt para credenciais?

Aqui está o código de amostra que eu uso para conectar a partir de uma máquina que não seja de domínio usando o driver JTDS JDBC:

Class.ForName ("net.sourceforge.jtds.jdbc.driver"). NewInstance (); String url = "jdbc: jtds: sqlserver: // server/database; domínio = domínio"; Conn = DriverManager.getConnection (URL, "Usuário", "Senha");

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top