Возможно ли, чтобы функция CLR в SQL принимала конкретную c windows идентичность? - PullRequest
2 голосов
/ 27 февраля 2020

Я хочу, чтобы функция CLR выполнялась с указанием c windows идентификатора при вызове из SQL Заданий агента сервера. С этой целью оператор SQL, вызывающий функцию, имеет вид:

execute as user='domain\username'
select database.schema.function()
revert

Сама функция, ради аргументов, может просто выглядеть примерно так:

        [SqlFunction(DataAccess = DataAccessKind.Read)]
        [return: SqlFacet(MaxSize = -1)]
        public static SqlChars GetIdentityInformationImpersonated()
        {
            WindowsIdentity clientId;
            WindowsImpersonationContext impersonatedUser;
            string currentUser;

            clientId = SqlContext.WindowsIdentity;
            impersonatedUser = clientId.Impersonate();

            currentUser = WindowsIdentity.GetCurrent().Name;

            impersonatedUser.Undo();

            return new SqlChars(currentUser);
        }

Это бросает исключение:

A. NET Ошибка структуры произошла во время выполнения пользовательской подпрограммы или агрегата «GetIdentityInformationImpersonated»: System.NullReferenceException: ссылка на объект не установлена ​​для экземпляра объекта. System.NullReferenceException: at WinIdentityDebugging.IdentityResults.GetIdentityInformationImpersonated ()

Что означает, что SqlContext.WindowsIdentity равно нулю. Теперь в inte rnet есть ссылки, утверждающие, что такая вещь невозможна, например ...

... здесь ...

Имейте в виду, что олицетворение SQLCLR не работает с олицетворенными контекстами (EXECUTE AS)

... и здесь ...

попытка получить объект SqlContext.WindowsIdentity является олицетворенным контекстом безопасности, в настоящее время возвращает ноль и для этого делает c 'd.

... и здесь ...

Если выполнение происходит в контексте логина «Выполнение от имени» (оператор непосредственно или некоторый модуль, помеченный им), SqlContext.WindowsIdentity будет иметь значение null, если логин не является sysadmin. В этом случае SqlContext.WindowsIdentity возвращает идентификатор, под которым выполняется процесс сервера.

Но все эти ссылки довольно старые, и я не могу найти таких ограничений в реальной документации. Например, страница SqlContext.WindowsIdentity указывает только на то, что она является нулевой для SQL вызывающих абонентов:

Экземпляр WindowsIdentity, представляющий Windows идентификатор вызывающего абонента, или нулевой если клиент был аутентифицирован с использованием SQL аутентификации сервера.

Так что я надеюсь, что есть способ достичь этого.

Возможно ли такое? Или мне не повезло?

Ответы [ 2 ]

0 голосов
/ 27 февраля 2020

Вы можете переключить контекст безопасности на «домен \ имя пользователя» из задания, используя прокси-сервер и шаг powershell или с помощью команды «Выполнить вход в систему» ​​и нового соединения с помощью OPENROWSET:

powershell:

--create credential
--#!!!!!!!!!!!!!!!!  set domain account and password !!!!!!!!!!!!!!!!!!
create credential credentialUserxyz with identity = '**domain\user**', secret = '**password_here**';
go

exec msdb.dbo.sp_add_proxy @proxy_name=N'proxyUserxyz',@credential_name=N'credentialUserxyz', @enabled=1;
go
--enable proxy for powershell
exec msdb.dbo.sp_grant_proxy_to_subsystem @proxy_name=N'proxyUserxyz', @subsystem_id=12;
go

--create a job with a powershell step
declare @jobid binary(16);
exec msdb.dbo.sp_add_job @job_name=N'thestclrwithproxy', @enabled=1, @category_name=N'[Uncategorized (Local)]', @job_id = @jobId OUTPUT;
exec msdb.dbo.sp_add_jobserver @job_id = @jobId, @server_name = N'(local)';
exec msdb.dbo.sp_add_jobstep @job_id=@jobId, @step_name=N'step 1', 
        @step_id=1, 
        @subsystem=N'PowerShell', 
        @command=N'
$conn = New-Object System.Data.Odbc.OdbcConnection("Driver={ODBC Driver 17 for SQL Server};Server=$(ESCAPE_DQUOTE(SRVR));Trusted_Connection=yes;");
$conn.open();

$cmd = New-Object data.Odbc.OdbcCommand;
$cmd.Connection = $conn;

#!!!!!!!!!!!!!!!!  change the clr function call !!!!!!!!!!!!!!!!!!
$cmd.CommandText = "select database.schema.function();";
$scalar = $cmd.ExecuteScalar();

#echo $scalar;
Write-Output "clr identity: $scalar";

$cmd.Dispose();
$conn.Dispose();', 
        @database_name=N'master', 
        @proxy_name=N'proxyUserxyz';

go

--execute the job
exec msdb.dbo.sp_start_job @job_name = 'thestclrwithproxy' 
go

waitfor delay '00:00:10';
go

--message, output of clr identity
select message, *
from msdb.dbo.sysjobhistory as h
join msdb.dbo.sysjobs as j on h.job_id = j.job_id
where j.name = 'thestclrwithproxy';
go

--delete the job
exec msdb.dbo.sp_delete_job @job_name = 'thestclrwithproxy';
go

--drop proxy
exec msdb.dbo.sp_delete_proxy @proxy_name = N'proxyUserxyz';

--drop credential
drop credential credentialUserxyz;
go

openrowset:

execute as login = 'domain\username';

select *
    from OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;', 
    'select database.schema.function() as functioncall;'
    ) ;
0 голосов
/ 27 февраля 2020

Ссылки, которые вы нашли, даже если они старые, по-прежнему верны. Мало того, что я никогда не видел пример этой работы, но я только что проверил на SQL Server 2017, что даже при олицетворении Windows имени входа через EXECUTE AS LOGIN = 'machine_or_domain\account_mame'; свойство SqlContext.WindowsIdentity по-прежнему возвращает ноль.

Если вам необходимо выполнять внешние команды как учетную запись c Windows, вам необходимо авторизоваться как эта учетная запись в коде. NET. Для этого требуется установить сборку на UNSAFE.

Я помню, как пробовал годы а ​​go и посмотрю, есть ли у меня этот тестовый код.

ИЛИ, возможно, это может быть достаточно просто, учитывая, что вы уже используете SQL Агент сервера. Как уже упоминали другие прокси, вы можете просто изменить тип шага задания на «Операционная система (CmdExe c)» и обрабатывать это одним из двух способов, в зависимости от того, нужен ли вам вывод в контексте T- SQL или нет.

  1. Создайте учетные данные для учетной записи Windows, для которой функция должна выполняться как
  2. Создайте прокси-сервер для этого удостоверения, которое будет использоваться для этапов задания этого типа ( в SSMS под «SQL агент сервера» -> «Прокси» -> «Операционная система (CmdExe c)»)
  3. Измените / создайте шаг задания агента SQL на «Операционная система ( CmdExe c) "
  4. В раскрывающемся списке« Запуск от имени »выберите только что созданный прокси
  5. В зависимости от того, нужен ли вам вывод из. NET метод, возвращенный в контекст T- SQL:
    1. Если вы не нуждаетесь в каком-либо выводе этого метода, тогда вместо того, чтобы делать это в SQLCLR, создайте консольное приложение и выполните его как SQL Шаг задания агента. И, я полагаю, даже если вам нужно отправить данные в эту функцию из T- SQL или сделать что-то с выводом, во многих / большинстве случаев вы можете просто подключиться к SQL Серверу из кода. NET и выполните требуемые запросы из консольного приложения.
    2. Если вам do нужен вывод в контексте T- SQL или необходимо передать данные из контекста T- SQL и это не может быть легко выполнено с помощью запросов, выполняемых в коде. NET, тогда вы можете просто выполнить SQLCMD.exe с шага задания агента SQL, так как он будет подключаться как требуется Windows Счет. Из SQLCMD вы выполняете запрос, содержащий функцию / хранимую процедуру SQLCLR, и олицетворение. NET будет использовать эту учетную запись-посредник (через учетные данные), поскольку внешний процесс подключен к SQL Server в качестве этой учетной записи. Я проверил это, и оно работает.
...