There exist some blog posts about how to use F# with SQLCLR in SQL Server that are helpful: http://richardminerich.com/2015/12/a-safer-way-to-use-f-with-sql-clr/, http://thelastexpression.blogspot.com/2012/04/f-agents-and-sqlclr.html, https://rojepp.wordpress.com/2011/08/03/f_on_sqlclr/, Can the F# core libs be SQLCLR-approved?
and for the C# approach: http://www.sqlservercentral.com/Authors/Articles/Solomon_Rutzky/294002/
I am wondering/hoping that with the passage of time there is a blog post out there, which I haven't been able to find yet or an answer here, which addresses how to use F# with SQLCLR such that the assembly can be scripted into hex using Visual Studio (or some other tool), like is done with C# deployment (I don't have access to install code on the server except through SQL Server Management Studio), and is at least more safe than using 'trustworthy on' or 'unsafe'. I've written F# and lots of T-SQL before and the prototype I wrote (which now must live in SQL Server) in Common Lisp would map better to F# (and make me happier than using C#).
I'm skeptical of the approach shown in your first link ( http://richardminerich.com/2015/12/a-safer-way-to-use-f-with-sql-clr/ ) as it does not directly show the loading of the FSharp.Core library, hence it is not clear that the author did not have to set TRUSTWORTHY ON in order to at least get that part working. What seems highly suspicious is that in Step 5, the Asymmetric Key-based Login is granted the wrong permission:
GRANT EXTERNAL ACCESS ASSEMBLY TO FSHARP_CLR_Login
Granting EXTERNAL ACCESS ASSEMBLY does not allow for setting an Assembly to UNSAFE. That requires the UNSAFE ASSEMBLY permission. It could be a copy / paste error when writing the post, but no proof is shown (i.e. from sys.databases) that TRUSTWORTHY is currently OFF, or that the author's code wasn't working prior to creating that Login and granting that permission to it.
So, I just tried this by installing the most recent build of FSharp.Core – 4.1.2 – and here is what I found:
Confirm that TRUSTWORTHY is OFF (i.e. 0) via:
SELECT [name], is_trustworthy_on FROM sys.databases WHERE [database_id] = DB_ID();
Attempt to load FSharp.Core as SAFE, just to see if it works:
USE [TestDB];
CREATE ASSEMBLY [FSharp.Core]
FROM N'C:\path\to\project\packages\FSharp.Core.4.1.2\lib\net45\FSharp.Core.dll'
WITH PERMISSION_SET = SAFE;
That receives the following error:
Msg 6211, Level 16, State 1, Line 32
CREATE ASSEMBLY failed because type 'Microsoft.FSharp.Collections.FSharpMap`2' in safe assembly 'FSharp.Core' has a static field 'empty'. Attributes of static fields in safe assemblies must be marked readonly in Visual C#, ReadOnly in Visual Basic, or initonly in Visual C++ and intermediate language.
Attempt to load FSharp.Core again, but as UNSAFE:
USE [TestDB];
CREATE ASSEMBLY [FSharp.Core]
FROM N'C:\path\to\project\packages\FSharp.Core.4.1.2\lib\net45\FSharp.Core.dll'
WITH PERMISSION_SET = UNSAFE;
That works. But, I didn't set the Database to TRUSTWORTHY ON, nor did I create a Login and grant it the EXTERNAL ACCESS ASSEMBLY permission. Meaning: the violation is probably found via a run-time verification instead of a load-time verification. And I have no way to test beyond this part, but I would expect that an error will occur.
If an error does occur regarding the UNSAFE Permission Set for this Assembly, then you can handle that without resorting to setting TRUSTWORTHY ON, but you will need to create a Certificate in master and a Certficate-based Login:
USE [master];
CREATE CERTIFICATE [FSharpCert45]
FROM EXECUTABLE FILE =
N'C:\path\to\project\packages\FSharp.Core.4.1.2\lib\net45\FSharp.Core.dll';
CREATE LOGIN [FSharpLogin45] FROM CERTIFICATE [FSharpCert45];
GRANT UNSAFE ASSEMBLY TO [FSharpLogin45];
IF your Assembly is also required to be marked as UNSAFE, then you can create an Asymmetric Key from the DLL in master and then a Key-based Login from that Asymmetric Key, and then grant that Key-based Login the UNSAFE ASSEMBLY permission. (this assumes that your Assembly is signed -- and protected with a password)
Of course, all of the above assumes that you can get the DLL onto the server or at least onto a share that the SQL Server service account has access to, and you did mention wanting to deploy this via hex bytes. That should be possible by doing:
In Visual Studio, under "References" in the "Solution Explorer", go to the "Properties" for FSharp.Core and set Model Aware to True and Permission Set to Unsafe. This will cause the publish process to include the DLL in the build script.
If the DLL is already in your target DB, then it probably won't generate the CREATE ASSEMBLY statement for this Assembly since publish scripts are incremental changes. If this is the case, then go to the project properties, and under Project Settings, check the box for Create script (.sql file) (if not already checked). This will cause the build process to always produce a _Create.sql script, and in there will definitely be the CREATE ASSEMBLY statement for FSharp.Core.
That CREATE ASSEMBLY [FSharp.Core] FROM 0x... statement will obviously be used to load the Assembly into the target DB (i.e. where your Assembly is also getting loaded into).
That CREATE ASSEMBLY [FSharp.Core] FROM 0x... statement will also be your ticket to creating the objects in master as follows:
USE [master];
CREATE ASSEMBLY [FSharp.Core]
FROM 0x4D....
WITH PERMISSION_SET = UNSAFE;
CREATE CERTIFICATE [FSharpCert45]
FROM ASSEMBLY [FSharp.Core];
DROP ASSEMBLY [FSharp.Core];
CREATE LOGIN [FSharpLogin45] FROM CERTIFICATE [FSharpCert45];
GRANT UNSAFE ASSEMBLY TO [FSharpLogin45];
This worked for me on SQL Server 2012, the only difference being I used the file path instead of the hex bytes.
Related
I have created a certificate, signed my assembly (UNSAFE) and it works, All as described there
https://nielsberglund.com/2017/07/01/sqlclr-and-certificates/
Now I need to deploy the solution to many customers databases. I do not want to change the TRUSTWORTHY setting.
Is it possible to deploy it do not using file system? I.e. now I am using the binary to create the assembly.
But how I can create the signed assembly and deploy it to other db?
Yes, you can create everything straight from T-SQL scripts and never reference the file system. It is possible to create a Certificate from hex bytes (i.e. VARBINARY literal), and you can get the string form of the hex bytes from a binary file using a small command-line utility I wrote, BinaryFormatter (open source; on GitHub). BinaryFormatter also works for converting the compiled DLL into hex bytes for use with the CREATE ASSEMBLY statement.
Also, I should point out that if you are using certificates, then you do not need to also enable TRUSTWORTHY. This is one of the reasons to use certificates. Just make sure to load the certificate into [master], create the associated login from the certificate, and grant that login the UNSAFE ASSEMBLY permission, all before attempting to load the assembly into any database. But, once the certificate is loaded, then you can load the assembly into any database with no more configuration needed (especially not needing to enable TRUSTWORTHY anywhere).
Also, if you are looking to streamline/automate your build process while still using certificates, 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
Additional details can be found in my answer to the following S.O. question:
CLR Strict Security on SQL Server 2017
P.S. This process would be a thousand times easier if the CREATE ASYMMETRIC KEY command were enhanced to allow for being created from a binary string. I have made this suggestion on Microsoft Connect – Allow Asymmetric Key to be created from binary hex bytes string just like CREATE CERTIFICATE – so please support it :-).
When I try and register a signed C# assembly in SQL Server via the Object Explorer in SSMS by right-clicking on the "Assemblies" node and selecting "New Assembly", then my signed DLL via "Browse", I receive the following error:
CREATE or ALTER ASSEMBLY for assembly 'My Assembly' with the SAFE or EXTERNAL_ACCESS option failed because the 'clr strict security' option of sp_configure is set to 1. Microsoft recommends that you sign the assembly with a certificate or asymmetric key that has a corresponding login with UNSAFE ASSEMBLY permission. Alternatively, you can trust the assembly using sp_add_trusted_assembly.
Even though I've tried signing the assembly both through a "Signing" project-properties-generated PFX file and through an sn.exe-generated SNK file (linked through hacking the project file's AssemblyOriginatorKeyFile node), both result in the DLL still being reported as not having a "Strong Name" by SQL Server in the "New Assembly" dialog:
I've tried this with both a Visual-Studio-2019-generated Class Library project in both .NET 4.8 and .NET 4.0 and a Visual-Studio-2010-generated SQL CLR Database Project all to the same end.
I understand the error message suggests a security hack to workaround the problem (which I have also tried to no avail anyway) but I'm trying to avoid these as the database server is public-facing. How do I fix this properly?
Assuming that the DLL truly is signed, then you are likely missing the second step of the preferred method noted in the first part of the error message, specifically:
that has a corresponding login with UNSAFE ASSEMBLY permission.
That part is critical to this working properly. Prior to loading the assembly into SQL Server, you need to do the following:
create an asymmetric key in the [master] database from the DLL
create a login from that asymmetric key
grant the new login the UNSAFE ASSEMBLY instance-level permission
Then you can load any assembly into any database so long as it has been signed by that same strong-name-key / pfx file (which can be several if you have multiple projects in your solution).
Even though I've tried signing the assembly both through ..., both result in the DLL still being reported as not having a "Strong Name" by SQL Server in the "New Assembly" dialog:
Correct. This is due to either poor UI design, or a bug. When creating a new assembly, the "Additional properties:" fields do not reflect the values of the file indicated in the "Path to assembly:" field. Most likely this is just the "assembly" dialog for all assembly-related stuff, and it works just fine for assemblies that are already loaded into SQL Server, but not for what you are attempting to load. If the intention was to peek into the file to indicate the current values of whatever is found at the path specified in the "Path to assembly:" field, then it's a bug (but I suspect it's the former). I have reported this to Microsoft here:
SSMS: "Additional properties" in "New Assembly" dialog is misleading as it doesn't describe DLL being imported
For info on working with SQLCLR in general, please visit SQLCLR Info
I have problem with adding assembly dll in SQL Server 2014. I try to create assemble stored procedure like this
CREATE ASSEMBLY pdf_create FROM 'dll path\test.dll' WITH PERMISSION_SET = SAFE
SQL Server returns this error:
Assembly 'test' references assembly 'system.drawing, 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: 2(Не удается найти указанный файл.)). Please load the referenced assembly into the current database and retry your request.
Help please if you have any ideas
So the question is why you would need a dependency on system.drawing.dll in an SQLCLR assembly? I would look at the code for the test.dll, and try to get rid of that particular dependency.
What you can do, if you absolutely need to include system.drawing.dll, is to copy it and put it in the same directory as the dll you try to create the assembly from, as SQL Server automatically creates assemblies from dependent assemblies if they are in the same path.
However, if you do that, then you probably need to change the permission set for the assembly you create as (if I remember correctly) system.drawing.dll needs a lesser permission set than SAFE.
So all in all, try to get rid of the dependency, you save yourself a lot of trouble that way.
i have an assembly registered in SQL Server:
CREATE ASSEMBLY [CLRFunctions]
AUTHORIZATION [dbo]
FROM 0x4D5A90000300000...
WITH PERMISSION_SET = SAFE
How can i find the path to the this assembly?
It is not an assembly reference in SQL Server, the assembly is actually loaded in the database. Therefore there isn't a typical "path".
If you're looking for a SQLPS (PowerShell) path then it'd be:
SQLSERVER:\SQL\YourServerName\YourInstanceName\Databases\YourDatabaseName\Assemblies\
The deprecated (as of the release of SQL Server 2005 which introduced SQLCLR) extended stored procedure API / feature (i.e. XPs) did point to external DLLs. This meant that they could get deleted and were not included in backups of the database. With SQLCLR, the assemblies are imported into the database and can be accessed via sys.assembly_files. Having the assemblies contained within the database guarantees that they are included in all DB backups (among the other benefits of not magically disappearing or being replaced without your knowledge, etc).
Execute the following to see what assemblies you have loaded, their original paths (only if they were loaded from an external DLL and not from a VARBINARY literal / hex bytes), and their full contents/bytes:
SELECT asm.[name] AS [Assembly], afl.[name] AS [PathOrAltName], afl.[content]
FROM sys.assembly_files afl
INNER JOIN sys.assemblies asm
ON asm.[assembly_id] = afl.[assembly_id]
ORDER BY asm.[name];
If the assembly was loaded from a VARBINARY literal / hex bytes (i.e. 0x4D5A9000...), then the [name] column in sys.assembly_files should be the same as the [name] column in sys.assemblies (which is the name used in the CREATE ASSEMBLY statement).
For more information on working with SQLCLR in general, please visit: SQLCLR Info
I have a CLR project that references an external assembly. I created the referenced assembly first as follows:
CREATE ASSEMBLY [GraphClient]
FROM 'C:\Users\rknight\Desktop\GraphServerClientProject\GraphServerClient\GraphClient\GraphClient\bin\Debug\GraphClient.dll'
WITH PERMISSION_SET = UNSAFE
Then in my CLR project I was able to reference it just fine under the "SQL Server" tab. Built the CLR project and it compiles with no errors. But when I create the new assembly I get an error:
CREATE ASSEMBLY [GraphCLR]
FROM 'C:\Users\rknight\Desktop\GraphServerClientProject\GraphServerClient\GraphClient\GraphCLR\bin\Debug\GraphCLR.dll'
WITH PERMISSION_SET = UNSAFE
Msg 10301, Level 16, State 1, Line 1
Assembly 'GraphCLR' references assembly 'graphclient, version=0.0.0.0, culture=neutral, publickeytoken=null.', 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: 2(The system cannot find the file specified.)). Please load the referenced assembly into the current database and retry your request.
I had this problem and I eventually worked it out. I was referencing an x86 assembly on a 64 bit server. I rebuilt the referenced assembly as Any CPU, installed in on the server, re-added the reference to the project, rebuilt my project as Any CPU, and then SQL Server could resolve the reference and install the assembly.
A message along the lines of "You're referencing an assembly that's built for the wrong platform" would have been rather useful :)
This should work. Are you positive GraphClient is successfully loaded before the GraphCLR deployment is attempted? Instead of relying on the database project to deploy correctly I would first manually load the dlls in the right order, then if that works go back and mess with the project to ensure the dependencies are correct and deployment is ordered correctly.
If that doesnt help, have you tried setting trustworthy on to eliminate any obscure permissions issues?