【问题标题】:CLR Stored procedure to execute commandCLR 存储过程执行命令
【发布时间】:2016-12-05 06:03:28
【问题描述】:

我编写了一个 CLR 存储过程来执行作为参数传递给它的命令。这是 CLR 存储过程的代码;我已在 SQL Server 中将程序集注册为“不安全”,并从中创建了存储过程。

[SqlProcedure]
public static int ExecuteCommand(string cmd)
{
    int success = 0;

    try
    {
        EventLog.WriteEntry("MyAppName", "Starting execution " + cmd + " Username: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name + " OR " + Environment.UserName, EventLogEntryType.Information);

        Process p = new Process();
        p.StartInfo = new ProcessStartInfo() { FileName = "cmd.exe", Arguments = cmd, WindowStyle = ProcessWindowStyle.Hidden};
        p.Start();
    }
    catch (Exception ex)
    {
        EventLog.WriteEntry("MyAppName", "Executed command : " + cmd + "  with error : " + ex.Message, EventLogEntryType.Error);
        success = 1;
    }

    return success;
}

此代码能够写入事件查看器并将用户名打印为NT AUTHORITY\SYSTEM。然后它启动cmd.exe,但它不执行传递的命令(例如mkdir)并且cmd.exe 保持在运行状态。在任务管理器中,每次运行存储过程时,我都可以看到多个 cmd.exe 实例,但用户名列中没有值。 SQL Server 服务正在使用本地系统帐户运行。

这里出了什么问题?如果与权限有关,那么有没有办法在调用存储过程的用户的上下文下执行CLR存储过程?

【问题讨论】:

    标签: .net sql-server sqlclr


    【解决方案1】:

    虽然权限可能是个问题,但您肯定似乎在错误地调用 cmd.exe。如果你简单地称它为:

    cmd.exe mkdir folder_name
    

    然后你会得到你所看到的行为,即文件夹没有被创建并且进程继续而不退出。

    您需要调用 cmd.exe 传递命令以使用 /C 命令行开关来运行,该开关指示 CMD执行提供的命令,然后退出。因此,调用以下内容:

    cmd.exe /C mkdir folder_name
    

    会按您的预期工作。所以也许你需要使用:

    Arguments = "/C " + cmd
    

    此外,您可能需要另一个“WindowStyle”和/或其他ProcessStartInfo 属性。如果上述方法不起作用,我会检查我过去使用的东西,因为我已经让它起作用了。

    附:您应该将Sql* 类型用于SQLCLR 方法的参数和返回值。所以用SqlString代替stringSqlInt32代替int。所有Sql* 类型都有一个.Value 属性,该属性返回预期的.NET 本机类型。因此,您可以按如下方式使用它:

    Arguments = "/C " + cmd.Value
    

    【讨论】:

      【解决方案2】:

      听起来像是假冒的问题,或者更确切地说是缺乏假冒的问题。 SQL CLR 不会将您的令牌(凭据)转发给其他应用程序,除非您明确告诉它这样做。您可以尝试以下方法:

      [SqlProcedure]
      public static int ExecuteCommand(string cmd)
      {
          int success = 0;
          Impersonate impersonatedUser = new Impersonate();
          try
          {
              EventLog.WriteEntry("MyAppName", "Starting execution " + cmd + " Username: " + System.Security.Principal.WindowsIdentity.GetCurrent().Name + " OR " + Environment.UserName, EventLogEntryType.Information);
              Process p = new Process();
              p.StartInfo = new ProcessStartInfo() { FileName = "cmd.exe", Arguments = cmd, WindowStyle = ProcessWindowStyle.Hidden };
              p.Start();
      
          }
          catch (Exception ex)
          {
              EventLog.WriteEntry("MyAppName", "Executed command : " + cmd + "  with error : " + ex.Message, EventLogEntryType.Error);
              success = 1;
          }
          finally
          {
              impersonatedUser.Undo();
          }
          return success;
      }
      

      请注意,只有几行不同。

      Impersonate impersonatedUser = new Impersonate();
      

      这个告诉 SQL 模拟正在执行存储过程的用户。这将因 SQL 身份验证而失败,因为 SQL 身份验证实际上不是 Windows 用户。

      finally
      {
           impersonatedUser.Undo();
      }
      

      如果您未能还原模拟,则可能会导致其他问题,因此将其放在 finally 块中将使模拟无论如何都还原。

      这是一篇关于该主题的 Microsoft 文章,其中包含更多内容:

      https://msdn.microsoft.com/en-us/library/ms345105.aspx

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多