6

我们有一个场景,我们需要我们的用户能够启动 SQLServer 并使用与当前登录不同的域对其进行身份验证。因此,为了澄清这是设置的方式:

  1. 用户到达办公室并登录公司域(为简单起见,我们将其称为 LOCALDOMAIN)
  2. 他们希望连接到我们在不同域上的远程数据库(我们称之为 REMOTEDOMAIN)
  3. 首先,他们启动了 VPN 工具,该工具建立了到 REMOTEDOMAIN 的 VPN 隧道(这一切都经过测试并且效果很好)
  4. 但是,如果他们默认启动 SSMS,它将只允许通过 LOCALDOMAIN 进行 Windows 身份验证,选择 REMOTEDOMAIN 的选项甚至不可用

我们发现从命令行运行它会起作用:

RUNAS /user:REMOTEDOMAIN\AUserName /netonly "C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe

它将提示“输入 REMOTEDOMAIN\AUserName 的密码:”消息,如果您提供正确的密码,SSMS 将启动并可以连接到远程数据库。但是,当我尝试在 C# 中使用更好的界面做同样的事情时,我得到“登录失败:未知用户名或密码错误”,这是我的代码:

System.Security.SecureString password = new System.Security.SecureString();
foreach(char c in txtPassword.Text.ToCharArray()){
    password.AppendChar(c);
}
System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
procInfo.Arguments = "/netonly";
procInfo.FileName = @"C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Ssms.exe"; ;
procInfo.Domain = "REMOTEDOMAIN";
procInfo.Verb = "runas";
procInfo.UserName = txtUsername.Text;
procInfo.Password = password;
procInfo.UseShellExecute = false;
System.Diagnostics.Process.Start(procInfo);

我尝试了带有和不带有预先挂起的域的用户名,但都不起作用。有人尝试过做类似的事情吗?谢谢

4

3 回答 3

0

我做了一些可能相关的事情。我登录到一个域并尝试获取另一个域上共享文件夹的目录列表。为此,我使用 LogonUser 和 Impersonate。代码如下所示(抱歉,我没有 SQL 服务器来尝试您的确切方案)...

public class Login : IDisposable
{
    public Login(string userName, string domainName)
    {
        _userName = userName;
        _domainName = domainName;
    }

    string _userName = null;
    string _domainName = null;

    IntPtr tokenHandle = new IntPtr(0);
    IntPtr dupeTokenHandle = new IntPtr(0);
    WindowsImpersonationContext impersonatedUser = null;

    const int LOGON32_PROVIDER_DEFAULT = 0;
    const int LOGON32_LOGON_INTERACTIVE = 2;
    const int LOGON32_LOGON_NEW_CREDENTIALS = 9;

    [DllImport("advapi32.dll", SetLastError = true, EntryPoint = "LogonUser")]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("advapi32.dll", SetLastError = true, EntryPoint = "LogonUser")]
    public static extern bool LogonUserPrompt(String lpszUsername, String lpszDomain, IntPtr lpszPassword,
        int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

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

    public void AccessShare(string password)
    {
        tokenHandle = IntPtr.Zero;

        bool returnValue = LogonUser(_userName, _domainName, password,
            LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
            ref tokenHandle);

        if (false == returnValue)
        {
            int ret = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(ret);
        }

        // Use the token handle returned by LogonUser.
        WindowsIdentity newId = new WindowsIdentity(tokenHandle);
        impersonatedUser = newId.Impersonate();
    }

#region IDisposable Members
    public void  Dispose()
    {
        impersonatedUser.Undo();

        // Free the tokens.
        if (tokenHandle != IntPtr.Zero)
            CloseHandle(tokenHandle);
    }
#endregion
}

我已经将它与 Directory.GetDirectories(UNCPath) 一起使用,其中路径通向另一个域上的机器并且它在那里工作。我还没有尝试过实现“runas”。

我这样称呼它...

using(var login = new Login("myname","mydomain))
{
    login.AccessShare("mypassword");
    // do stuff
}

也许你可以让它适应你的问题。LMK

于 2012-10-25T20:02:38.960 回答
0

您应该删除以下行:

// Not passing /netonly to SMSS, it was passed to RunAs originally.
procInfo.Arguments = "/netonly";
// Again, SMSS is not getting the verb, it's being run
procInfo.Verb = "runas";

基本上,您将/netonly参数传递给 SMSS,而在命令行上,您运行的runas 不是 SMSS。与动词相同,you're not running runas

此时调用Start应该会成功,因为您将使用正确的凭据指向正确的可执行文件。

于 2012-10-23T17:57:32.327 回答
0

我尝试了所有我能找到的各种用户模拟代码示例。他们都没有工作。

最后,我想出了以下代码。它cmd.exe使用/C参数执行, which Carries out the command specified by string and then terminates。我执行的命令是runas /netonly ...

注意事项

不幸的是,密码必须手动输入。我的下一步是调查向process. 我尝试重定向标准输入并写入它,但它不起作用。我在某处读到,大多数密码提示只接受直接来自键盘的输入。

此外,当 SSMS 打开时,“连接到服务器”对话框将显示您当前的域\用户名,但它将使用您提供给runas.

最后,如果您的 AD 帐户被锁定,在您尝试连接到 SQL Server 之前,您不会收到错误消息。我忽略了复制收到的错误消息,但没有提到帐户被锁定。

代码

    public static void RunAsNetonly(string username, string domain, string exePath)
    {

        var psi = new ProcessStartInfo();

        psi.FileName = "cmd.exe";
        psi.Arguments = $"/C runas /netonly /user:{domain}\\{username} \"{exePath}\"";            
        psi.UseShellExecute = false;

        var process = Process.Start(psi);

        // not sure if this is required
        process.WaitForExit();

    }        
    
    // usage example
    public static void RunSSMS()
    {
        RunAsNetonly("walter", "domain123", @"C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\ssms.exe");
    }
于 2018-10-12T14:53:12.413 回答