Articles

Current Articles | Categories | Search | Syndication

By Jonathan Kehayias @ Friday, August 01, 2008 6:35 PM :: 328 Views :: 0 Comments

Traditionally if you had a need to access a file or other resource outside of SQL Server, the SQL Server service account was required to have appropriate file system access to the folder or path containing the file.  With CLR integration, this is no longer an absolute requirement.  Identity Impersonation will allow you to implicitly or explicitly change the execution context inside of a SQLCLR Function, Procedure, or Trigger. 

Before you start using impersonation, there are a few considerations that need to be made.  First and foremost, you must always deploy an assembly that uses Impersonation with a minimum of EXTERNAL_ACCESS.  While the Assembly will deploy, you will receive a message similar to the following when you try to call the Impersonating object from SQL:

Msg 6522, Level 16, State 1, Procedure ImpersonationFailsUndo, Line 0
A .NET Framework error occurred during execution of user-defined routine or aggregate "ImpersonationFailsUndo":
System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
System.Security.SecurityException:
   at System.Data.SqlServer.Internal.ClrLevelContext.get_WindowsIdentity()
   at Microsoft.SqlServer.Server.SqlContext.get_WindowsIdentity()
   at StoredProcedures.ImpersonationFailsUndo()

The second thing to keep in mind is that you must revert the Impersonation Context by calling the WindowsImpersonationContext.Undo() method.  If you fail to do this, you will receive the following exception from the SQL CLR execution:

Msg 10312, Level 16, State 49, Procedure ImpersonationFailsUndo, Line 0
.NET Framework execution was aborted. The UDP/UDF/UDT did not revert thread token.

This is caused by the fact that you must always revert the ImpersonationContext explicitly in code when you call Impersonation in your SQLCLR code.  To demonstrate this, create the following Stored Procedure and execute it:

[Microsoft.SqlServer.Server.SqlProcedure]
public static void ImpersonationFailsUndo()
{
    WindowsIdentity currentIdentity = SqlContext.WindowsIdentity;
    WindowsImpersonationContext impersonatedIdentity = currentIdentity.Impersonate();
}

 

The third thing to keep in mind is that while the identity is Impersonated during the execution, the SqlPipe and other SQLCLR paradigms that rely on the Context Connection are not available in the SQL object.  This is due to the fact that the SQLCLR Context Connection functions under the defined execution context of the object definition, generally the caller.  If you attempt to use the context connection while impersonated, you will get the same error as if you didn't revert the impersonation.  To demonstrate this create and execute the following procedure:

[Microsoft.SqlServer.Server.SqlProcedure]
public static void ContextConnnectionUnavailable()
{
    WindowsIdentity currentIdentity = SqlContext.WindowsIdentity;
    WindowsImpersonationContext impersonatedIdentity = currentIdentity.Impersonate();

    string retval = string.Empty;
    using (SqlConnection conn = new SqlConnection("context connection=true"))
    {
        using (SqlCommand cmd = new SqlCommand("SELECT @@VERSION", conn))
        {
            conn.Open();
            retval = cmd.ExecuteScalar().ToString();
        }
    }
    SqlContext.Pipe.Send(retval);

    impersonatedIdentity.Undo();
}

 

Note that the error is identical to the last one:

Msg 10312, Level 16, State 49, Procedure ContextConnnectionUnavailable, Line 0
.NET Framework execution was aborted. The UDP/UDF/UDT did not revert thread token.

Both of the above examples perform what I consider to be implicit impersonation, that is, they impersonate the Windows Login account of the logged in user, if that user is logged in using Windows Authentication.  However, if you were to login with a SQL Login account instead of using Windows Authentication, you will receive an error as follows:

Msg 6522, Level 16, State 1, Procedure ContextConnnectionUnavailable, Line 0
A .NET Framework error occurred during execution of user-defined routine or aggregate "ContextConnnectionUnavailable":
System.NullReferenceException: Object reference not set to an instance of an object.
System.NullReferenceException:
   at StoredProcedures.ContextConnnectionUnavailable()

This is because the SqlContext.WindowsIdentity is null if the process executes with a SQL Login.  To get around this, you need to test the SqlContext.WindowsIdentity and determine if it is null, and then handle the value appropriately.  For instance if the WindowsIdentity is null and you don't want the procedure to run for SQL Logins, you can place the following check at the top of the Stored Procedure:

if (SqlContext.WindowsIdentity == null)
{
    SqlContext.Pipe.Send("This procedure can not be executed by a SQL Login");
    return;
}

 

The result of this execution is that the Stored Procedure will output the above message and terminate execution for all SQL Logins.  The alternative to this is to allow the Stored Procedure to continue to execute, but without calling the Impersonation code:

[Microsoft.SqlServer.Server.SqlProcedure]
public static void ContextConnnectionUnavailable()
{
    WindowsIdentity currentIdentity = null;
    WindowsImpersonationContext impersonatedIdentity = null;

    if (SqlContext.WindowsIdentity != null)
    {
        currentIdentity = SqlContext.WindowsIdentity;
        impersonatedIdentity = currentIdentity.Impersonate();
    }

    string retval = string.Empty;
    try
    {
        using (SqlConnection conn = new SqlConnection("Server=(local);Database=SQLCLR;Trusted_Connection=True;"))
        {
            using (SqlCommand cmd = new SqlCommand("SELECT @@VERSION", conn))
            {
                conn.Open();
                retval = cmd.ExecuteScalar().ToString();
            }
        }
    }
    catch
    {
        throw;
    }
    finally
    {
        if (impersonatedIdentity != null)
            impersonatedIdentity.Undo();
    }

    SqlContext.Pipe.Send(retval);
}

 

The above procedure will succeed whether the user is logged in with a SQL Login or a Windows Login since the user has access to the SQLCLR database.  However, if this were accessing a file on the file system, keep in mind that the file system access would be being made under the context of the SQL Service Account.

The second type of Impersonation is explicit Impersonation, where you explicitly specify the Windows User Account and Password to Impersonate.  This is an UNSAFE operation currently since it requires a Interop call into the unmanaged code advapi32.dll to execute the LogonUser() command necessary to create the Windows.Identity to impersonate.  This will be covered in another article since it ventures into the UNSAFE realm of SQL CLR, and has the potential to leak the token handle.  However, take note that it is possible to do.

An example on this site that uses Impersonation is:

SqlBulkCopy Sample Framework

Some Additional References for CLR Impersonation inside SQLCLR are:

Impersonation and CLR Integration Security
SQL Server 2005: CLR Integration : Impersonation in SQL CLR

SQLCLR SQL Server 2005 2007 CLR .NET Impersonation External Access Permissions

Posted @ Friday, August 01, 2008 6:35 PM by Jonathan Kehayias
Previous Page | Next Page
Comments
Currently, there are no comments. Be the first to post one!
You must be logged in to post a comment. You can login here

Survey

Which of the following CLR objects are you currently using in SQL Server?




Submit Survey  View Results

Links


Copyright 2007 by SQLCLR.net Terms Of Use Privacy Statement
Website graphics provided by Matt Green Designs
Page generated in 0.2656199 seconds.

All information and example code on this site is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from its use.

This site is in no way affiliated with Microsoft. Unless specifically stated otherwise, nothing should be construed to represent the official positions or opinions of Microsoft and/or its Employees.