This has been dogging me for 2 days now. I have a CLR sp that needs EXTERNAL_ACCESS. I can deploy it via VS2010 on my dev box by setting TRUSTWORTHY ON but we don't want to do that to the production server. We purchased an AuthentiCode compatible cert and I tried to sign my assembly with that but it failed due to chaining so I followed the instructions detailed here to strip out chaining from the cert.
Next I tried signing the assembly in VS but got the error "An attempt was made to reference a token that does not exist."
So went to the commandline and signed the assembly with the de-chained cert using SignTool.exe as several bloggers have recommended. The utility reports that signing succeeded.
Now to import the assembly into SQL Server (express 2008R2) on my dev box. First Set TRUSTWORTHY off as this procedure will have to be applied to the production server. Then I run
CREATE ASSEMBLY SqlClrProcedures from 'c:\<snip>\SqlClrProcedures.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
This gets the following error: *CREATE ASSEMBLY for assembly 'SqlClrProcedures' failed because assembly 'SqlClrProcedures' is not authorized for PERMISSION_SET = EXTERNAL_ACCESS. The assembly is authorized when either of the following is true: the database owner (DBO) has EXTERNAL ACCESS ASSEMBLY permission and the database has the TRUSTWORTHY database property on; or the assembly is signed with a certificate or an asymmetric key that has a corresponding login with EXTERNAL ACCESS ASSEMBLY permission.*
I was logged in as sa. Ok so I create a user, assign him ownership of the db and grant him EXTERNAL ACCESS:
GRANT EXTERNAL ACCESS Assembly to ClrLogin
Then try
CREATE ASSEMBLY SqlClrProcedures AUTHORIZATION ClrLogin from 'c:\<snip>\SqlClrProcedures.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS
which produces the same error above.
The dbo has been granted EXTERNAL ACCESS ASSEMBLY and the assembly is signed, but I don't understand the part about the corresponding login, do I need a login for the cert?
If set TRUSTWORTHY ON just to get past the CREATE ASSEMBLY the assembly is imported fine but when I run the sp I get this error:
An error occurred in the Microsoft .NET Framework while trying to load assembly id 65573. The server may be running out of resources, or the assembly may not be trusted with PERMISSION_SET = EXTERNAL_ACCESS or UNSAFE. Run the query again, or check documentation to see how to solve the assembly trust issues. For more information about this error:
System.IO.FileLoadException: Could not load file or assembly 'sqlclrprocedures, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An error relating to security occurred. (Exception from HRESULT: 0x8013150A)
System.IO.FileLoadException:
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
so it appears the cert is not getting recognized. Can someone please tell what I am doing wrong?
Kent Tegels has a step-by-step example showing the security process for signing a clr assembly with a certificate, then loading the certificate into the server so that the assembly is considered trusted.
Correct, TRUSTWORTHY should be set to OFF. For more info, please see my post: PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining
Signing an Assembly in Visual Studio means applying a Strong Name Key; it does not allow for signing with a Certificate (quite unfortunately).
Since you are going to use a signed Assembly, you do not need to worry about the database owner (dbo) being linked to a Login that has been granted either the EXTERNAL ACCESS ASSEMBLY or UNSAFE ASSEMBLY permission (the error message indicates that the dbo's permission only matter when TRUSTWORTHY is ON). When using a signed assembly, it is the Login created from the Asymmetric Key or Certificate (that was used to sign the Assembly) that will be granted either the EXTERNAL ACCESS ASSEMBLY or UNSAFE ASSEMBLY permission.
You don't need the AUTHORIZATION ClrLogin part
What you need (or needed) to do is:
Create a Certificate in the master Database. You can create the Certificate in a few different ways:
An already loaded SAFE Assembly (this would be the FROM ASSEMBLY option). HOWEVER, thanks to changes introduced in SQL Server 2017, this is no longer an option.
The .cer file (i.e. the public key) from the file system (this would be the FROM FILE option)
The .dll file from the file system (this would be the FROM EXECUTABLE FILE option)
The .cer file directly from a VARBINARY literal (this would be the FROM BINARY option). To easily convert that file into a hex bytes string (i.e. 0x12AB00003D...), you can use the open source BinaryFormatter command-line utility that I wrote that can be used in automation / Continuous Integration (by transforming it into a file to be imported / included), or used for manually scripting the CREATE CERTIFICATE statement (by transforming it directly to the clipboard to be pasted into a script).
Create a Login from that Certificate
Grant that Login either the EXTERNAL ACCESS ASSEMBLY or UNSAFE ASSEMBLY permission (starting in SQL Server 2017, just the UNSAFE ASSEMBLY permission).
Regarding SQL Server 2017
SQL Server 2017 introduced a new security feature ("CLR strict security", an advanced option) that is enabled by default and requires that ALL Assemblies, even those marked as SAFE, be signed with either an Asymmetric Key (i.e. strong name) or Certificate and have a Login (based on whatever was used to sign the Assembly) that has the UNSAFE ASSEMBLY permission. For details on how to make this work, with or without Visual Studio / SSDT, please see the following two posts of mine:
SQLCLR vs. SQL Server 2017, Part 2: “CLR strict security” – Solution 1
SQLCLR vs. SQL Server 2017, Part 3: “CLR strict security” – Solution 2
Please avoid the new Trusted Assemblies "feature" as it has many more flaws than benefits, not to mention it being entirely unnecessary in the first place given that existing functionality already handled the situation "Trusted Assemblies" was meant to address. For full details on that and a demo of the proper way to handle existing, unsigned Assemblies, please see: SQLCLR vs. SQL Server 2017, Part 4: “Trusted Assemblies” – The Disappointment.
Related
A database with a CLR dll was migrated from SQL Server 2008R2 to SQL Server 2017.
Deployment to this database is automated using DacPac in Azure DevOps.
The dll is registered with EXTERNAL_ACCESS.
Since the migration to the new server, the deployment pipeline is broken.
Here is what I've done so far to fix it.
I changed the Target platform in the Database Project from SQL Server 2008 to SQL Server 2017.
Error in the pipeline changed from
Unable to connect to master or target server. You must have a user with the same password in master or target server
to
Internal Error. The database platform service with type Microsoft.Data.Tools.Schema.Sql.Sql140DatabaseSchemaProvider is not valid. You must make sure the service is loaded, or you must provide the full type name of a valid database platform service.
From this answer, Microsoft SQL Server Data-Tier Application Framework (17.1 DacFx) was installed on the build agent.
Error in the pipeline changed to
Assembly 'system.componentmodel.dataannotations, version=3.5.0.0, culture=neutral, publickeytoken=31bf3856ad364e35.' was not found in the SQL catalog.
I then changed the target framework of the dll in the Database Project from NET Framework 3.5 to NET Framework 4.0
Error in the pipeline changed to:
Could not deploy package. Error SQL72014: .Net SqlClient Data Provider: Msg 6218, Level 16, State 2, Line 1
CREATE ASSEMBLY for assembly 'System.ComponentModel.DataAnnotations' failed because assembly 'System.ComponentModel.DataAnnotations' failed verification.
Check if the referenced assemblies are up-to-date and trusted (for external_access or unsafe) to execute in the database.
CLR Verifier error messages if any will follow this message
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider::GetTypeDescriptor][mdToken=0x6000003][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider::.ctor][mdToken=0x6000001][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider::.ctor][mdToken=0x6000002][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptor::GetAttributes][mdToken=0x6000007][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptor::GetProperties][mdToken=0x6000006][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptor::GetProperties][mdToken=0x6000005][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptor::.ctor][mdToken=0x6000004][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociationAttribute::.ctor][mdToken=0x6000008][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociationAttribute::get_Name][mdToken=0x6000009][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociationAttribute::get_ThisKey][mdToken=0x600000a][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociationAttribute::get_OtherKey][mdToken=0x600000b][offset 0x00000000] Code size is zero.
[ : System.ComponentModel.DataAnnotations.AssociationAttribute::get_IsForeignKey][mdToken=0x60000...
Error SQL72045: Script execution error. The executed script:
CREATE ASSEMBLY [System.ComponentModel.DataAnnotations]
AUTHORIZATION [dbo]
FROM 0x4D5A90000300000004000000FFFF0000B800000000000000400000000000000000000000000000000000000000000000000000000000000000000000800000000E1FBA0E00B409CD21B8014CCD21546869732070726F6772616D2063616E6E6F742062652072756E20696E20444F53206D6F64652E0D0D0A2400000000000000504500004C010300B7E0A14B0000000000000000E00022200B01080000B00000000800000000000092CF00000020000000E000000000C4600020000000020000040000000000000004000000000000000020010000020000CBF4000003004085000010000010000000001000001000000000000010000000000000000000000040CF00004F00000000E00000E0040000000000000000000000BA000088170000000001000C00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000080000000000000000000000082000004800000000000000000000002E7465787400000098AF00000020000000B0000000020000000000000000000000000000200000602E72737263000000E004000000E000000006000000B20000000000000000000000000000400000402E72656C6F6300000C0
Since steps 3, I have been unable to progress. All my attempts from there failed to do anything. Here is what I've tried.
GRANT EXTERNAL ACCESS ASSEMBLY TO [userName]
GRANT UNSAFE ASSEMBLY TO userName]
ALTER DATABASE [DatabaseName] SET TRUSTWORTHY ON
SET [userName] as db_owner
SET [userName] as sysadmin
put all dependency of the dll as UNSAFE
SET 'clr enabled' to 1
SET 'clr strict security' TO 0
Imported successfully a dummy CLR DLL
I've replicated the issue with a simple dummy project that reference System.ComponentModel.DataAnnotations with property Model Aware activated.
What can I do next to fix the DacPac deployment?
EDIT :
My next attempt is to decompile System.ComponentModel.DataAnnotationsand see if it would be smoother for the pipeline using my own version of it. I trying this because, I have read somewhere that the server verify if the dll already exists in the GAC. If it does, then dll version/signature much match. Therefore, I feel like even if I manage to load this assembly, it is bound to fail again after other server maintenance. Thus, the cost versus benefit of using this dll is bad.
Knowing, only a little part of the dll is used anyway, I might as well bring the code used from the decompiler.
It looks like you are impacted by clr strict security, to confirm it try to deploy your assembly manually (without using DacPac).
If confirmed, you may temporary disable strict security - change option clr strict security using sp_configure.
As a final solution you should start signing your assembles.
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-assembly-transact-sql?view=sql-server-ver15
CLR uses Code Access Security (CAS) in the .NET Framework, which is no
longer supported as a security boundary. A CLR assembly created with
PERMISSION_SET = SAFE may be able to access external system resources,
call unmanaged code, and acquire sysadmin privileges. Beginning with
SQL Server 2017 (14.x), an sp_configure option called clr strict
security is introduced to enhance the security of CLR assemblies. clr
strict security is enabled by default, and treats SAFE and
EXTERNAL_ACCESS assemblies as if they were marked UNSAFE. The clr
strict security option can be disabled for backward compatibility, but
this is not recommended. Microsoft recommends that all assemblies be
signed by a certificate or asymmetric key with a corresponding login
that has been granted UNSAFE ASSEMBLY permission in the master
database. For more information, see CLR strict security.
"Cannot load dynamically generated serialization assembly." error when executing a clr function that calls a web service in SQL server 2008 R2.
I have a database project in Visual Studio 2017 which includes a clr function that calls a web service.
The project properties include the following:
Project Settings Target Platform = SQL Server 2008
SQLCLR Target framework = .Net Framework 3.5
SQLCLR Build Generate serialization assembly = On
The serialization assembly is created in the database by a post-deployment script when the database is published.
When I publish the databse to a SQL Server 2008R2 instance on my Windows 10 PC, executing the clr function results in the "Cannot load dynamically generated serialization assembly" error, however, when the database is published to a SQL2016 instance on my PC, it runs OK
Post deployment script to register serialization assembly:
CREATE ASSEMBLY [CifasEdit.XmlSerializers] FROM 'C:\tfs\CIFAS\Source\Database\Hub\CifasEdit\CifasEdit\bin\Release\CifasEdit.XmlSerializers.dll'
WITH PERMISSION_SET = EXTERNAL_ACCESS;
GO
The full error is here
A .NET Framework error occurred during execution of user-defined routine or aggregate "xxx":
System.InvalidOperationException: Cannot load dynamically generated serialization assembly. In some hosting environments assembly load functionality is restricted, consider using pre-generated serializer. Please see inner exception for more information. ---> System.IO.FileLoadException: LoadFrom(), LoadFile(), Load(byte[]) and LoadModule() have been disabled by the host.
System.IO.FileLoadException:
at System.Reflection.Assembly.nLoadImage(Byte[] rawAssembly, Byte[] rawSymbolStore, Evidence evidence, StackCrawlMark& stackMark, Boolean fIntrospection)
at System.Reflection.Assembly.Load(Byte[] rawAssembly, Byte[] rawSymbolStore, Evidence securityEvidence)
at Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch(CompilerParameters options, String[] fileNames)
at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources)
at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.
...
System.InvalidOperationException:
at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies)
at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence)
at System.Xml.Serialization.XmlSerializer.GetSerializersFromCache(XmlMapping[] mappings, Type type)
at System.Xml.Serialization.XmlSerializer.FromMappings(XmlMapping[] mappings, Type type)
at System.Web.Services.Protocols.SoapClientType..ctor(Type type)
at System.Web.Services.Protocols.SoapHttpClientProtocol..ctor()
So it appears that the serialization assembly generated automatically by VS2017 does not play nice with SQL Server 2008 R2 although it works fine with SQL2016.
To get the CLR function working in 2008R2 I moved the CLR code to a separate project and generated the serialization assembly manually using sgen.exe, then registered both dlls.
Be sure to use a version of sgen for clr2.0, on my PC this was located in
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin
The command to generate the serialization assembly is
sgen.exe c:\YourDir\YourAssembly.dll
This will create an assembly called YourAssembly.XmlSerializers.dll
Register both assemblies with SQL Server:
CREATE ASSEMBLY [YourAssembly] FROM 'c:\YourDir\YourAssembly.dll' WITH PERMISSION_SET = EXTERNAL_ACCESS
CREATE ASSEMBLY [YourAssembly.XmlSerializers] FROM 'c:\YourDir\YourAssembly.XmlSerializers.dll' WITH PERMISSION_SET = SAFE
I have a database project in VS, and am trying to generate an asymmetric key in SQL Server from the dll. In SSMS, I am trying to run the following:
CREATE ASYMMETRIC KEY MyNewKey
FROM EXECUTABLE FILE = N'C:\{path to dll}\MyDBProj.dll'
The error I get is "The certificate, asymmetric key, or private key file is not valid or does not exist; or you do not have permissions for it."
The dll in question was built with SQLCLR permission level = SAFE
My assumption is that it is related to file permissions, but I don't know what security permissions I need to allow on the file to make this happen. I was informed that I should give the NETWORK SERVICE account read access, which I did.
This is on a local db with mixed mode authentication. I've tried as both Windows user and under the sa account.
Given that you are using SQL Server Express LocalDB (which runs as your Login and hence has no permissions issues), the only thing that should cause such an error is if you did not actually sign the Assembly. That can be done in Visual Studio via the "Signing..." button in the SQLCLR tab of Project properties.
Signing an Assembly (whether by giving it a strong name or adding a Certificate) places the public key of whatever was used to sign it inside the Assembly. The CREATE ASYMMETRIC KEY statement will extract a public key from an Assembly that was placed there by strong naming it. This is not the same as getting the public key of a Certificate that was used to sign the Assembly as they are stored separately.
Of course, if by "a local db" you meant that it is merely running on your dev workstation and is not specifically SQL Server Express LocalDB, then I guess it would be SQL Server Express? If you logged into SQL Server as yourself (you said you tried as a Windows user) and you are a sysadmin, then there still shouldn't be any file system permissions issues, nor any issue with creating an Asymmetric Key.
General
For more information on working with SQLCLR in general, please see the series that I am writing on this topic on SQL Server Central: Stairway to SQLCLR.
Regarding SQL Server 2017
SQL Server 2017 introduced a new security feature ("CLR strict security", an advanced option) that is enabled by default and requires that ALL Assemblies, even those marked as SAFE, be signed with either an Asymmetric Key (i.e. strong name) or Certificate and have a Login (based on whatever was used to sign the Assembly) that has the UNSAFE ASSEMBLY permission. For details on how to make this work, with or without Visual Studio / SSDT, please see the following two posts of mine:
SQLCLR vs. SQL Server 2017, Part 2: “CLR strict security” – Solution 1
SQLCLR vs. SQL Server 2017, Part 3: “CLR strict security” – Solution 2
Please avoid the new Trusted Assemblies "feature" as it has many more flaws than benefits, not to mention it being entirely unnecessary in the first place given that existing functionality already handled the situation "Trusted Assemblies" was meant to address. For full details on that and a demo of the proper way to handle existing, unsigned Assemblies, please see: SQLCLR vs. SQL Server 2017, Part 4: “Trusted Assemblies” – The Disappointment.
I'm trying to integrate a DLL into my SQL Server 2012 database, following the example from here.
The command is:
CREATE ASSEMBLY Excel_Procs_IF
FROM 'd:\MyDir\UWQ_Excel_Compute.dll'
WITH PERMISSION_SET = SAFE ;
and I get the following error message:
Msg 6215, Level 16, State 1, Line 1
CREATE ASSEMBLY failed because method 'get__Default' on type 'Microsoft.Office.Interop.Excel.Range' in safe assembly 'UWQ_Excel_Compute' has invalid attribute 0x1083.
I have no idea what this means nor how to tackle the issue.
You need to change the PERMISSION_SET in your CREATE ASSEMBLY statement to be UNSAFE. Then it should work.
As stated on the MSDN documentation page for Office Primary Interop Assemblies:
To use the features of a Microsoft Office application from an Office project, you must use the primary interop assembly (PIA) for the application. The PIA enables managed code to interact with a Microsoft Office application's COM-based object model.
And, the page for Primary Interop Assemblies states:
A primary interop assembly is a unique, vendor-supplied assembly that contains type definitions (as metadata) of types implemented with COM.
COM is unmanaged code, and that requires the UNSAFE Permission Set.
Running this command:
CREATE ASSEMBLY
[System.Web] from
'C:\Windows\Microsoft.NET\Framework\v2.0.50727\system.web.dll'
with permission_set = UNSAFE
Gives me this error:
Msg 10300, Level 16, State 2, Line 1
Assembly 'System.Web' references assembly 'system.web, version=2.0.0.0, culture=neutral, publickeytoken=b03f5f7f11d50a3a.', which is not present in the current database. SQL Server attempted to locate and automatically load the referenced assembly from the same location where referring assembly came from, but that operation has failed (reason: version, culture or public key mismatch). Please load the referenced assembly into the current database and retry your request.
... this sounds a little silly. It seems like SQL Server thinks that the System.Web assembly is referencing it's self. How can I fix this?
Try with Framework64 assemblies (64 bit sql server 2008)
CREATE ASSEMBLY
[System.Web] from
'C:\Windows\Microsoft.NET\Framework64\v2.0.50727\System.Web.dll'
with permission_set = UNSAFE
GO
It sounds like you need to install the .Net 2.0 framework on your database server.
Also, I wouldn't directly add a reference to System.Web.dll. Your other custom CLR code should reference that. (Or, if you don't have custom .Net code, you should create a custom .Net project to interface into the System.Web assembly.)
Turns out System.web.dll isn't supported for this. In fact, it turned out that loading DLLs into SQL Server like this (for CLR) was a bad idea on many levels (one of which was 64/32-bit support between deployments).