How to deploy certificate and signed assembly to other database? - sql-server

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 :-).

Related

How does one Register a C# CLR Assembly with SQL Server without using Security Hacks?

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

Attempting to Distribute an Access Application with SQL Server Backend

I am attempting to use a Packaged Solution for my Access 2010 application that has its backend linked to SQL Server. At the moment, I'm using the .accdb file as the frontend, and I would like to distribute my application to some other Windows computers, but the Packaged Solution does not work. I had the package include Access Runtime, so their version of the frontend is running on Runtime and not full Access. However, once the application makes a request to the backend, the application does nothing, as I am not even prompted for the SQL Password as per usual with the full version. I've read on about including a .dsn file in the package can secure the SQL connection (see here), but going through steps of other tutorials to create .dsn files hasn't led to any results. Would anyone know how to correctly generate the .dsn file or if I've done something else wrong at this point?
(And yes, I understanding using Access 2010 in the year 2019 is almost a joke at this point, but I'm doing this for testing purposes. I plan to completely remake the frontend in Angular in the future.)
One other unrelated note... would it be a better idea to have the frontend hosted as a .html file like through the "Publish to Access Services" process? I did read that Access Services was discontinued last year, so would that not be possible?
Edit: This is not a duplicate of "DSN Less Connection (MS Access to SQL2016)" because A) I want to utilize a DSN Connection, not DSN-less and B) I am not using connection strings in my code to hook up with SQL.
You should be able to just create FILE dsn, link your tables, and then distribute the compiled accDE to each desktop.
However, what SQL odbc source provider did you use? If you use the SQL server ODBC provider, then that is by default installed on each computer.
However, if you linked using Native 11 (or later), then that driver is NOT installed on each workstation by default. So, I HIGH recommend you create a FILE dsn (not a user or system DSN), and link the table using that. (Access will create DSN-less links for you)
And you should NOT be seeing a logon prompt with your application. This suggests you forgot or missed the save password option.
So, I would re-link your tables, creating a new FILE DSN. And if you using the linked table manager, then make sure you check the prompt for new location to force creating of a NEW DSN. If you just re-fresh, then you DO NOT get a chance to click on the save password option during the linking process.
So, what odbc driver are you using? The native 11 or later are better, but they are not installed by default on each workstation. However, CAUTION is required here, since the older sql driver does NOT support the newer datetime2 formats. If you used these newer sql column types, they will be returned as string data types in Access and create a mess of issues.
So, first, I would re-link using a FILE dsn.
Make sure you check the save password during the re-link.
You then compile your accDB into an accDE, and then distribute that. You don’t really need to use the package wizard, since once each workstation has the runtime installed, then a simple copy of the accDE to each person’s computer will thus work fine. There is NO special connection between your accDE and the package wizard. Once the runtime is installed, then any and all mdb, accDB, and your accDE can simply be clicked on to launch + run. So for testing, you can skip the package wizard, and just copy the accDE to the target machine, click on it, and see if it works.
Edit
The prompt and check box during this process is this:
So you have to check that box to save the password. Note that you ONLY get this dialog WHEN you create a new FILE dsn.

SQL Server CLR project version number increment

Hope some one can help me figure this out.
Problem: I have a sql CLR project in visual studio 2013. Every thing seems to work as I can install the project on SQLServer and call its stored procedures etc. However I do not know how to control the version number of this assembly when its installed. When I run following command
select * from sys.assemblies
It results in following
name principal_id assembly_id clr_name
MyTestSqlClrProject 1 65612 mytestsqlclrproject, ***version=0.0.0.0***, culture=neutral, publickeytoken=null, processorarchitecture=msil
What I want to know is what do I need to do to control the version number
version=0.0.0.0
So that I can manually increment it or auto increment it whenever a build is successful..
I have tried to change project>Properties>Project Settings> On presented screen clicked on Advance which shows another dialogue box on(Data Tier-Application) this there is a field Version, but apparently changing this value does not have any impact on the version attribute when I get this result from the database.
Thanks
The VersionNumber is an Assembly property. It is the [assembly: AssemblyVersion("x.x.x.x")] attribute that is typically stored in the \Properties\AssemblyInfo.cs file of the project.
You can either edit that file directly, or you can go to:
Project (menu) -> Project Properties (menu item) -> SQLCLR (tab) -> Assembly Information... (button) -> Assembly Version (fields)
If you do not have an \Properties\AssemblyInfo.cs file yet, then entering any info into the Assembly Information popup will create that file upon clicking the OK button.
Please note that there is a bug in SQL Server that prevents the version number from being displayed in the sys.assemblies system catalog view IF the Assembly is not signed:
"version" incorrectly showing as 0.0.0.0 in sys.assemblies and ASSEMBLYPROPERTY(name, 'CLRName') for unsigned SQLCLR Assemblies
Signed Assemblies will show the correct version number in sys.assemblies. When using Object Explorer in either SQL Server Management Studio (SSMS) or Visual Studio to look at the Assembly properties, the correct version number is displayed even if the Assembly has not been signed. Still, it usually best to sign Assemblies anyway, so just sign them and you won't have any issues here (especially because MS might never fix it; I did report that bug on 2016-01-28).
In terms of auto-incrementing the version number, you can use an * in the last position to do that. For example:
[assembly: AssemblyVersion("1.2.3.*")]
or:
[assembly: AssemblyVersion("1.2.*")]
Beyond signing the Assembly, you should be aware of complications in loading the Assembly into SQL Server starting in 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.

F# with sqlclr in a reasonably safe way and scripted assembly

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.

How to override or workaround compiled DB connection info

I've faced a bit strange problem. There is a site client would like to duplicate on another domain name. Site is built on ASP(yes, old v1 ASP :( ) with SQLServer. Problem is that all the database operations, including connection information is compiled into a DLL library.
Is there a way to some how intercept, override or workaround this?
Platform:
Windows 2000 Server
SQLServer 7
ASP v1 (VisualBasic)
What a nightmare...
If you have control over the new database server, and the connection string references the database server by name, you could add a line in the hosts file which points the name of the old server to a new ip address. Then you still have to create a user with the same password on the new database server.
Yes! However, your new database name MUST be smaller or equal to the length of your old one. Simply open up the vb6 .dll in any Hex Editor and search and replace. Make SURE you do not change the length of the DLL or shift any bytes around.
Failing that, add a hosts entry to windows to redirect the connection.
Could it be that the application uses a DSN definition for the connection?
In Control Panel -> Administrative Tools open ODBC Data Source Administrator. Have a look at the System DSN tab. Does there seem to be anything listed there that is related to the application?
Before you hack your DNS or rename the server, be aware that the SQL Server client supports aliases specifically for this scenario: How to: Create a Server Alias for Use by a Client.
The SQL 2000 tool for configuring an alias is the Client Network Utility.
Do you have any configuration options at all from asp? Even dbname or the such? If so you might be able to use two separate servers for IIS and use a single db server. I'm afraid you might need to find the source or the guy who wrote it.
If you could figure out the connection string or DNS you might be able to do something.
You could write your own passthrough ODBC provider.

Resources