SQL Server 2005 allows for the integration of .NET assemblies into the databases so that they can be accessed from inside stored procedures and other database functions. Although this is a great new feature, I got hung up on a particularly cryptic error message when I tried to build an assembly and import it.
Since SQL Server makes it difficult to query active directory, and I wanted to build an AD-based authentication module for my database application, the best way to do that seemed to be to use this new feature. My assembly depended on System.DirectoryServices in order to access Active Directory, but that wouldn’t be a problem, since the .NET 2.0 framework is available from inside SQL Server 2005 (http://msdn2.microsoft.com/en-us/library/ms254506.aspx, provided you’ve enabled the feature), right? Well, sort of. As it turns out, SQL Server was rushed to RTM too quickly for all of the .NET 2.0 assemblies to be cleared as SAFE, so the ones that weren’t fully tested aren’t included by default. Fair enough – so it’s just a matter of importing System.DirectoryServices, and then importing my assembly that relies on it, right? Again, sort of.
System.DirectoryServices can be imported into SQL Server, but only as an UNSAFE assembly. This has all sorts of other security implications (which is a little ironic, since I was using it to verify user security), but I decided to use it anyway, since I figured that the UNSAFE tag was more of a formality than a real danger, and the assembly would be SAFE once more testing had been done. I imported System.DirectoryServices:
USE master
GO
CREATE ASYMMETRIC KEY asmKey_DirectoryServices
FROM EXECUTABLE FILE = 'c:\Windows\Microsoft.NET\Framework\v2.0.50727\System.DirectoryServices.dll'
GO
CREATE LOGIN asmLogin_DirectoryServices
FROM ASYMMETRIC KEY asmKey_DirectoryServices
GO
GRANT unsafe ASSEMBLY TO asmLogin_DirectoryServices
GO
That imports the System.DirectoryServices assembly as UNSAFE. Next, I imported my assembly as SAFE, since it was signed. The only problem was that when I called my assembly, which reached into System.DirectoryServices, I got an error (I’m calling clrIsMemberOfGroup in my assembly, SqlHelper):
Msg 6522, Level 16, State 2, Line 1
A .NET Framework error occurred during execution of user defined routine or aggregate 'clrIsMemberOfGroup':
System.Security.SecurityException: Request for the permission of type 'System.DirectoryServices.DirectoryServicesPermission, System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' failed.
After 4 hours in the phone troubleshooting the issue with Microsoft, it turned out that it was VERY simple, and even vaguely alluded to in a knowledgebase document. In order to reach into the UNSAFE System.DirectoryServices assembly, I had to make my assembly UNSAFE as well. Since the UNSAFE assembly was running outside the bounds of what .NET considers “SAFE”, it could potentially return suspect results, and so anything that relies directly on those results couldn’t be considered “SAFE”, and had to be tagged as “UNSAFE”. It seems like I should be able to implement proper sanitizing code in my assembly so that I don’t inherently trust the results from my “UNSAFE” assembly, but SQL Server would have none of it. In order to reach into an UNSAFE assembly, I needed to flag my assembly as UNSAFE – simply placing my assembly import into a “Create key, create login, import assembly” setup like the one I used for System.DirectoryServices fixed the problem.
I suppose the question is really “Did that fix anything?” since all I really did was disable security on those assemblies. It’s really hard to throw a security assembly when you don’t do any sort of security checks. Well, at least I alleviated the symptoms, and now I’ll just wait for SP1 to (hopefully) add System.DirectoryServices (among other missing framework assemblies) to the assemblies accessible from inside the CLR access in SQL Server 2005. I suppose we’ll have to wait and see…