Showing posts with label clr. Show all posts
Showing posts with label clr. Show all posts

Monday, March 26, 2012

Permissions With Dynamic SQL Within Stored Procedure

Okay, I have sort of a peculiar permissions question I am wondering if someone can help me with. Basically, here's the scenario...

I have a CLR stored procedure which does some dynamic SQL building based on values sent in via XML. It's a CLR stored procedure using XML because I want to build a parameterized statement (to guard against SQL Injection) based on a flexible number of parameters which are basically passed in the XML.

The dynamic SQL ends up reading from a table I'll call TableX and I actually discovered an (understandable) quirk with security.

Basically, the connection context is using security for a low-privilaged Windows account ("UserX") and UserX has no permission to the table referenced in the dynamic SQL but because of the dyanmic nature of the query, the stored procedure ends up adopting the security context of UserX. Naturally, this throws a security exception saying UserX has no SELECT permission on TableX.

Now, I can give UserX read permission to the table in question to get things running, but one of the points of using stored procedures is to defer security to the procedure level vs. configuration for tables or columns.

So in striving toward my ideal of security at the procedure level, my question is what is the best way to allow minimum privilege in this case?

I thought about having the internals of the CLR stored procedure run under a different (low-privalaged) security context, but I am wondering if there's an alternate configuration that may be as secure, but simpler.

PS - Please don't let this degenerate into a conversation about OR mappers. I know that happens a lot on these forums.Smile

BoulderBum:

Basically, the connection context is using security for a low-privilaged Windows account ("UserX") and UserX has no permission to the table referenced in the dynamic SQL but because of the dyanmic nature of the query, the stored procedure ends up adopting the security context of UserX. Naturally, this throws a security exception saying UserX has no SELECT permission on TableX.

Yes, this is by design. When you EXECUTE dynamic SQL statement. When a stored procedure is run that executes a string, permissions are checked in the context of the user who executes the procedure, not in the context of the user who created the procedure.

So in earlier SQL versions you have to choose: use dynamic SQL if you do have to, or not use it if not necessary. Fortunately in SQL 2005 we have another option: using EXECUTE AS clause when creating the stored procedure. For example:

create proc sp_testper WITH EXECUTE AS SELF
as
EXEC('select * from Orders')
go

For more information about this clause, please refer to:

http://msdn2.microsoft.com/en-us/library/ms188354(d=ide).aspx

|||

Thanks, lori_Jay.

That's the solution I'm probably going to end up going with, though I didn't know until a few days ago that EXECUTE AS could apply toCLRstored procedures. I was just deploying via the IDE which doesn't offer such options and the documentation I saw didn't give an example of how to use EXECUTE AS with a CLR stored procedure.

Luckily I was informed that it was indeed possible and though I haven't sat down to do it yet, and that solution suffices for my needs!

Anyway, thanks again!

|||

For future readers, something else I discovered comes in handy is that you can have a predeployscripts.sql and postdeployscripts.sql file in a database project.

With those files, I was able to rig the EXECUTE AS and change the schema for my stored procedures. It took some dropping/recreating to get everything where I wanted, but it was pretty easy and worked like a charm.

I now have one-touch deployment of my CLR stored procedures through Visual Studio again!

Permissions using CLR triggers

It seems that there is a difference between the way t-sql permissions and CLR permissions work. I don't know if this is a fault of SQL2005 or of my coding - perhaps someone can tell me.

E.g. Assume there are two tables called test1 and test2 with no permissions granted to anyone on them. Below is a simple trigger that inserts into test2 when test1 is updated

CREATE TRIGGER tr_test ON test1 FOR UPDATE AS
BEGIN
DECLARE @.v INT
SELECT @.v = MAX(ID) FROM test1
INSERT INTO test2 (audit) VALUES ('The max value is currently: ' + CONVERT(VARCHAR(10),@.v))
END
GO

Attempting to update a row from a non-DBO account will fail (because the user does not have update permissions). This is of course correct.

Now assume we have a stored proc that updates the row in test1. We can grant EXEC permission to the stored proc and, hey presto, everything works.

Now, the problem. If the above trigger were written like this instead:-

[Microsoft.SqlServer.Server.SqlTrigger (Name="tr_test", Target="test1", Event="FOR UPDATE")]
public static void Trigger1()
{
using (SqlConnection conn = new SqlConnection("Context Connection = true"))
{
conn.Open();

SqlCommand cmd = new SqlCommand("select max(id) from test1", conn);
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
int val = rdr.GetInt32(0);
rdr.Close();

string s = string.Format("The max value is currently: {0}", val);
cmd = new SqlCommand("insert into test2 (audit) values (@.p)", conn);
cmd.Parameters.AddWithValue("@.p", s);
cmd.ExecuteNonQuery();
}
}

This is functionally equivalent to our previous trigger. However, running our stored proc again fails this time because we don't have SELECT permissions on test1.

So ultimately my question is, how do I get this to work without having to grant permissions to the tables?

Thanks

swg

You should be using the 'inserted' and 'deleted' tables. What you are doing is requerying the table (therefore you need select rights).

So it should read something like: select max(id) from inserted

Cheers

Paul.

|||

The problem is that your CLR trigger breaks ownership chaining because it executes the sql code like dynamic sql. It has the same effect as using sp_executesql to make the query in your t-sql trigger, which would fail for the same reason.

Erland covers this in-depth here http://www.sommarskog.se/grantperm.html#ownershipchaining

There are two ways to work around this:

1. using the EXECUTE AS clause on your CLR trigger so that the trigger has access to the tables. Erland's article above is a good reference on how to do this and the potential pitfalls that should be avoided.

2. having your CLR trigger call a T-SQL stored proc to make the update, which shouldn't break the ownership chain. You can do this with SqlCommand.CommandType = CommandType.StoredProcedure

Steven

|||

Thanks for the responses guys.

Paul, yes I agree that would have been a better example. However, the same symptom occurs if you access the inserted/deleted trigger tables.

Steven, thanks very much. I'll give your options some consideration.

This would seem to me to be a deficiency in the CLR/SQL Server integration. Although I can see (now that you have pointed it out to me) why the CLR case is different, I would hope I'm not alone in thinking that when SQL2005 offers us CLR for use with triggers and stored procs, the developer should not expect that the CLR equivalent version to operate differently. Certainly I didn't expect this to be the case and the connection string "Context Connection = true" reinforced this. Obviously I cannot excercise your option 2 in a normal trigger because I will not have access to the inserted/deleted tables from a stored proc. However, the EXECUTE AS may offer some hope although it is still a kludge.

Many thanks

~swg

|||

Well just to put the cat among the pigeons, I've just done a small test app and, yes, sure enough everything worked fine. So I revoked the select permission on the table in my original app (the one that caused me to start this thread in the first place) and, yup, that's working fine too. So, God only knows what bug I had in there but it ain't there any more... or maybe I've written a new one in there to make the whole thing work now, who knows!

But certainly, going step-by-step and re-checking the permissions are there (or not there as appropriate) at each stage; it would seem the ownership chain is NOT broken when selecting from the inserted/deleted tables in a CLR trigger.

Cheers

~swg

Addedndum: To be clear, the ownership chain IS still broken if you follow my example (in the original post above) as explained in Steven's reply, because it does not use the inserted/deleted tables

|||

Your original example didn't use Inserted/Updated tables, CLR Triggers can access those fine as they are not real tables and don't have the same ownership concept.

However, your CLR trigger will still not be able to access your audit table test2 without doing one of the workarounds I mentioned above. This is not the case for a T-SQL trigger. Here is a full scenario based on your example demonstrating this:

public class triggers

{

public static void Trigger1()

{

using (SqlConnection conn = new SqlConnection("Context Connection = true"))

{

conn.Open();

SqlCommand cmd = new SqlCommand("select count(*) from inserted", conn);

Int32 val = (Int32)cmd.ExecuteScalar();

SqlContext.Pipe.Send("Inserted: " + val.ToString());

cmd = new SqlCommand("insert into test2 values (@.p)", conn);

cmd.Parameters.AddWithValue("@.p", val);

cmd.ExecuteNonQuery();

}

}

}

create assembly SimpleTrigger from 'c:\assemblies\SimpleTrigger.dll'

go

create table test1(c1 int)

go

create table test2(c1 int)

go

create proc InsertTest @.input int

as

begin

insert into test1 values(@.input)

end

go

create trigger Trigger1 on test1 for insert as external name SimpleTrigger.triggers.Trigger1

go

create user user1 without login

go

grant execute on InsertTest to user1

go

execute as user='user1'

go

--CLR Trigger fails to update test2 because it does not have permission

exec InsertTest 1

go

revert

go

drop trigger Trigger1

go

create trigger Trigger1 on test1 for insert

as

begin

insert into test2 select count(*) from inserted

end

go

execute as user='user1'

go

--T-SQL trigger is able to update test2

exec InsertTest 1

go

revert

go

drop user user1

drop proc InsertTest

drop table test1

drop table test2

drop assembly SimpleTrigger

|||

Sure thing, Steven

Absolutely agree. I hope you didn't take my last reply as a slur or contradiction of your answer. Most certainly wasn't intended that way. As I said in an earlier reply to Paul, my original post was a bad example; it should have contained inserted/deleted tables. That was why I couldn't understand why the CLR triggers wouldn't work. They would be next to useless if you couldn't access these tables.

My "real" situation was that the CLR trigger could not access the inserted table and granting select permission to the underlying table fixed the problem. There must have been something else at play somewhere though (and I'd like to know what) because I have now revoked select permission from this underlying table and my trigger continues to function correctly.

So, apologies for the confusion. I do appreciate your replies and it has helped me a lot in understanding ownership chains, and the differences between T-SQL and CLR triggers.

Thanks again

~swg

|||

No offense taken - I just wanted to make sure it was completely clear both to you and to those playing along at home. What's happening here isn't exactly obvious and I'm don't think it's clearly stated in BOL either.

Steven

Friday, March 9, 2012

Permission-problem on simple query inside CLR function

I have created an assembly with permission set safe, and a function inside the assembly.

The function reads data from the same SQL Server as it is running inside. For connection I use the "Context connection = true", and the function has the SystemDataAccessKind attribute set to Read.

However when I execute my CLR function I get an error saying something like:

"The request for permission of type System.Data.SqlClient.SqlClientPermission...... failed"

I do, as the login user, have been granted the necessary rights, so I don't believe this is the answer to the error.

And my .dll is also signed.

Has this something to do with writing something a config file?

I have had simular problems with reporting services but fixed them by entering a node in the rspolicy.config file. If this is the case here - which .config file should i modify...machine.config?

Where do you execute your CLR proc from, i.e. where do you get the exception?

Niels
|||

Inside Management Studio. Management Studio is running on a different machine than the SQL Server, but the user that I use when logging into Management Studio is dbowner, and the same user that I use to create the assembly and CLR function.

If I extract the SQL from the CLR function and runs it as a basic query in Management Studio, everything works fine.

|||

I have also noted than when I try to create an SqlPermission in the code and run the Asset(), I get another type of error:

System.Security.Permissions.SecurityPermissions, mscorlib....

|||What happens if you try to do a "normal" query from the SSMS you are testing from, against a table in a database on the SQL Server you have deployed your assembly to?

Niels
|||

pnp wrote:

Inside Management Studio. Management Studio is running on a different machine than the SQL Server, but the user that I use when logging into Management Studio is dbowner, and the same user that I use to create the assembly and CLR function.

If I extract the SQL from the CLR function and runs it as a basic query in Management Studio, everything works fine.


Oh, sorry - disregard my previous posting anout executing a "normal query", I didn't read your post properly. I assume, when you say that you run it as a basic query, that you are running it from the same SSMS that you fail when going against the CLR proc?

What if you try and run from the same box that SQL is on?

Niels
|||

Niels, thanks for your suggestions. I have also installed Management Studio on the same server that is running the SQL Server but the error is the same.

However, as I don't have endless time to deal with this problem I have fixed it by setting the permission of the assembly to unsafe. This fixes it. However, from the documentation I have read, safe-permissions should be enough, and I don't really understand why it isn't in my case.

Permission-problem on simple query inside CLR function

I have created an assembly with permission set safe, and a function inside the assembly.

The function reads data from the same SQL Server as it is running inside. For connection I use the "Context connection = true", and the function has the SystemDataAccessKind attribute set to Read.

However when I execute my CLR function I get an error saying something like:

"The request for permission of type System.Data.SqlClient.SqlClientPermission...... failed"

I do, as the login user, have been granted the necessary rights, so I don't believe this is the answer to the error.

And my .dll is also signed.

Has this something to do with writing something a config file?

I have had simular problems with reporting services but fixed them by entering a node in the rspolicy.config file. If this is the case here - which .config file should i modify...machine.config?

Where do you execute your CLR proc from, i.e. where do you get the exception?

Niels
|||

Inside Management Studio. Management Studio is running on a different machine than the SQL Server, but the user that I use when logging into Management Studio is dbowner, and the same user that I use to create the assembly and CLR function.

If I extract the SQL from the CLR function and runs it as a basic query in Management Studio, everything works fine.

|||

I have also noted than when I try to create an SqlPermission in the code and run the Asset(), I get another type of error:

System.Security.Permissions.SecurityPermissions, mscorlib....

|||What happens if you try to do a "normal" query from the SSMS you are testing from, against a table in a database on the SQL Server you have deployed your assembly to?

Niels
|||

pnp wrote:

Inside Management Studio. Management Studio is running on a different machine than the SQL Server, but the user that I use when logging into Management Studio is dbowner, and the same user that I use to create the assembly and CLR function.

If I extract the SQL from the CLR function and runs it as a basic query in Management Studio, everything works fine.


Oh, sorry - disregard my previous posting anout executing a "normal query", I didn't read your post properly. I assume, when you say that you run it as a basic query, that you are running it from the same SSMS that you fail when going against the CLR proc?

What if you try and run from the same box that SQL is on?

Niels
|||

Niels, thanks for your suggestions. I have also installed Management Studio on the same server that is running the SQL Server but the error is the same.

However, as I don't have endless time to deal with this problem I have fixed it by setting the permission of the assembly to unsafe. This fixes it. However, from the documentation I have read, safe-permissions should be enough, and I don't really understand why it isn't in my case.

Wednesday, March 7, 2012

Permission problems: Need help with CLR credentials

Hi everyone....

I have made a CLR stored procedure which goes to a shared folder on a remote computer. I am having permission problems when executing the stored procedure. (In case anyone is wondering, I am using the .NET SYSTEM.IO class)

I have done the following:

CREATE CREDENTIAL myuser
WITH IDENTITY = 'mydomain\myuser', SECRET = 'some56*Z';

CREATE LOGIN sam WITH PASSWORD = 'meowPw!a3';

ALTER LOGIN sam WITH CREDENTIAL = myuser;

...(other SQL so that SAM can execute the SP)

Now, I login to SQL Server using the new login, and when I try to execute the CLR stored procedure, I get "unknown username or bad password.".

I know 100% for sure that the account in the "MYUSER" credential has access. Is there something else I need to do?

You need to use Impersonation so that your CLR stored proc accesses external resources as your Windows user account, rather than the account that SQL Server is running under.

You can see an example of how to do this here: http://msdn2.microsoft.com/en-us/library/ms131068.aspx

If you are already doing this and it doesn't work, post the relevant clr code you're using.

Steven