Using a Certificate Signed Stored Procedure to Execute sp_send_dbmail

  • FreeHansje

    SSChampion

    Points: 11751

    I thought I had found the solution for my problem, but alas...

    I need to call master.sys.dm_exec_sql_text(to find the last used sql-statement) from a trigger, or sp, in another database, by ordinary users without SA-rights; please don't ask why.

    I changed reference in your article from msdb into master, used GRANT VIEW SERVER STATE as authorization, but alas, no succes. For days now I am looking for a way to allow non-SA users to view DMV's. Any1 here can help me out?!

    Greetz,
    Hans Brouwer

  • Jonathan Kehayias

    One Orange Chip

    Points: 26778

    FreeHansje (1/11/2010)


    I thought I had found the solution for my problem, but alas...

    I need to call master.sys.dm_exec_sql_text(to find the last used sql-statement) from a trigger, or sp, in another database, by ordinary users without SA-rights; please don't ask why.

    I changed reference in your article from msdb into master, used GRANT VIEW SERVER STATE as authorization, but alas, no succes. For days now I am looking for a way to allow non-SA users to view DMV's. Any1 here can help me out?!

    You actually don't need any kind of special permissions to solve your problem. First sys.dm_exec_sql_text isn't going to give you the information that you probably want. If you run that for a session_id in a trigger, it is going to return the CREATE TRIGGER statement of the trigger firing the request. As long as the need to get the SQL Statement that was executed to cause the trigger to fire is self session scoped, you can get it without added permissions using DBCC INPUTBUFFER() like follows:

    CREATE TABLE PermsTest

    (RowID int identity primary key)

    GO

    CREATE TRIGGER Audit_PermsTest

    ON PermsTest

    WITH EXECUTE AS OWNER

    FOR INSERT, UPDATE, DELETE

    AS

    BEGIN

    DECLARE @TEMP TABLE

    (EventType nvarchar(30), Parameters int, EventInfo nvarchar(4000))

    INSERT INTO @TEMP EXEC('DBCC INPUTBUFFER(@@SPID)')

    SELECT EventInfo FROM @TEMP

    END

    GO

    If you want to get information from another session that is executing that would require the additional permissions. As long as it stays self scoped it can be called without GRANT VIEW SERVER STATE.

    Jonathan Kehayias | Principal Consultant | MCM: SQL Server 2008
    My Blog | Twitter | MVP Profile
    Training | Consulting | Become a SQLskills Insider
    Troubleshooting SQL Server: A Guide for Accidental DBAs[/url]

  • Jonathan Kehayias

    One Orange Chip

    Points: 26778

    For those interested, you can use the same method to give SA rights or access like VIEW SERVER STATE to users with no access. For example to call sys.dm_exec_sql_text() as originally requested:

    USE [master]

    GO

    CREATE DATABASE SAPermsTest

    GO

    CREATE LOGIN [PermsTestLogin]

    WITH PASSWORD=N'c0mpl3xp@$$'

    GO

    USE [SAPermsTest]

    GO

    CREATE USER [PermsTestLogin] FROM LOGIN [PermsTestLogin]

    GO

    CREATE TABLE PermsTest

    (RowID int identity primary key)

    GO

    GRANT INSERT ON PermsTest TO PermsTestLogin

    GO

    CREATE TRIGGER Audit_PermsTest

    ON PermsTest

    WITH EXECUTE AS OWNER

    FOR INSERT, UPDATE, DELETE

    AS

    BEGIN

    DECLARE @TEMP TABLE

    (EventType nvarchar(30), Parameters int, EventInfo nvarchar(4000))

    INSERT INTO @TEMP EXEC('DBCC INPUTBUFFER(@@SPID)')

    SELECT EventInfo FROM @TEMP

    SELECT SYSTEM_USER

    SELECT text

    FROM sys.dm_exec_requests

    CROSS APPLY sys.dm_exec_sql_text(sql_handle)

    WHERE session_id = @@SPID

    END

    GO

    -- Test the trigger will fail. Make sure to revert

    EXECUTE AS LOGIN='PermsTestLogin'

    INSERT INTO PermsTest default values;

    REVERT

    GO

    -- Revert Fails when trigger execution fails so do it now.

    REVERT

    GO

    -- Create a certificate to sign stored procedures with

    CREATE CERTIFICATE [SAPermsCertificate]

    ENCRYPTION BY PASSWORD = '$tr0ngp@$$w0rd'

    WITH SUBJECT = 'Certificate for signing Audit Triggers';

    GO

    -- Backup certificate so it can be create in master database

    BACKUP CERTIFICATE [SAPermsCertificate]

    TO FILE = 'D:\SQLBackups\SAPermsCertificate.CER';

    GO

    -- Add Certificate to Master Database

    USE [master]

    GO

    CREATE CERTIFICATE [SAPermsCertificate]

    FROM FILE = 'D:\SQLBackups\SAPermsCertificate.CER';

    GO

    -- Create a login from the certificate

    CREATE LOGIN [SAPermsLogin]

    FROM CERTIFICATE [SAPermsCertificate];

    GO

    -- The Login must have Authenticate Sever to access server scoped system tables

    -- per http://msdn.microsoft.com/en-us/library/ms190785.aspx

    GRANT AUTHENTICATE SERVER TO [SAPermsLogin]

    GO

    -- Add the VIEW Server State permission to the Certificate login.

    GRANT VIEW SERVER STATE TO [SAPermsLogin]

    GO

    USE [SAPermsTest]

    GO

    -- Sign the procedure with the certificate's private key

    ADD SIGNATURE TO OBJECT::Audit_PermsTest

    BY CERTIFICATE [SAPermsCertificate]

    WITH PASSWORD = '$tr0ngp@$$w0rd';

    GO

    -- Retest the Trigger function and it will work returning

    -- CREATE TRIGGER statement for the sys.dm_exec_sql_text()

    EXECUTE AS LOGIN='PermsTestLogin'

    INSERT INTO PermsTest default values;

    REVERT

    GO

    -- Cleanup

    /*

    USE [master]

    GO

    DROP LOGIN [SAPermsLogin]

    DROP CERTIFICATE [SAPermsCertificate]

    DROP DATABASE [SAPermsTest]

    -- Delete the certificate backup from disk

    */

    If you run the above, you will note that as I stated in my last response the sys.dm_exec_sql_text() DMF will not output the last statement called by the client it will output the last statement executed by the session which happens to be the execution of the trigger itself. Kind of self defeating code, at least in my opinion which is why I still fall back on good ole' trusty DBCC INPUTBUFFER to get the last input statement from the client.

    Jonathan Kehayias | Principal Consultant | MCM: SQL Server 2008
    My Blog | Twitter | MVP Profile
    Training | Consulting | Become a SQLskills Insider
    Troubleshooting SQL Server: A Guide for Accidental DBAs[/url]

  • FreeHansje

    SSChampion

    Points: 11751

    Tnx Jonathan, this is really hel;pful, in several ways.

    Greetz,
    Hans Brouwer

  • FreeHansje

    SSChampion

    Points: 11751

    Jonathan,

    I wish to say thanks again. I must have done something wrong, different then you describe in your solution. I could have sworn I used the same construction to test permission on the DMV's, but could not get it working. But as I said, I must have donw something different, and since there is no1 at my current position with the knowledge to talk this over I could not pinpoint the error.

    Also tnx for showing that my initial 'solution' did not work. With your suggestion I can get things working.

    Tnx again, I have learned a lot today.

    Greetz,
    Hans Brouwer

  • ChiragNS

    One Orange Chip

    Points: 26137

    Great article and here comes another dumb question!

    After signing the procedure with certificate can the procedure be executed only by the login/user created from the certificate? Can other users execute the procedure.

    "Keep Trying"

  • ChiragNS

    One Orange Chip

    Points: 26137

    Hi anyone there!!

    "Keep Trying"

  • Jonathan Kehayias

    One Orange Chip

    Points: 26778

    ChiragNS (1/18/2010)


    Great article and here comes another dumb question!

    After signing the procedure with certificate can the procedure be executed only by the login/user created from the certificate? Can other users execute the procedure.

    No, you can GRANT EXECUTE on the stored procedure that is signed by the certificate to any database user. That's the point behind using the certificate signed stored procedure to grant access. The users don't have to have elevated rights, they only need the ability to EXECUTE the signed stored procedure, or a parent procedure in the ownership chain.

    Jonathan Kehayias | Principal Consultant | MCM: SQL Server 2008
    My Blog | Twitter | MVP Profile
    Training | Consulting | Become a SQLskills Insider
    Troubleshooting SQL Server: A Guide for Accidental DBAs[/url]

  • Jonathan Kehayias

    One Orange Chip

    Points: 26778

    FreeHansje (1/18/2010)


    Jonathan,

    I wish to say thanks again. I must have done something wrong, different then you describe in your solution. I could have sworn I used the same construction to test permission on the DMV's, but could not get it working. But as I said, I must have donw something different, and since there is no1 at my current position with the knowledge to talk this over I could not pinpoint the error.

    Also tnx for showing that my initial 'solution' did not work. With your suggestion I can get things working.

    Tnx again, I have learned a lot today.

    Did you figure your problem out? If not, DM me or shoot me a contact through my blog on SQLBlog and I'll see if I can help you troubleshoot the issue further.

    Jonathan Kehayias | Principal Consultant | MCM: SQL Server 2008
    My Blog | Twitter | MVP Profile
    Training | Consulting | Become a SQLskills Insider
    Troubleshooting SQL Server: A Guide for Accidental DBAs[/url]

  • ChiragNS

    One Orange Chip

    Points: 26137

    Jonathan Kehayias (1/22/2010)


    ChiragNS (1/18/2010)


    Great article and here comes another dumb question!

    After signing the procedure with certificate can the procedure be executed only by the login/user created from the certificate? Can other users execute the procedure.

    No, you can GRANT EXECUTE on the stored procedure that is signed by the certificate to any database user. That's the point behind using the certificate signed stored procedure to grant access. The users don't have to have elevated rights, they only need the ability to EXECUTE the signed stored procedure, or a parent procedure in the ownership chain.

    Thank you.

    "Keep Trying"

  • Rachel Byford

    Hall of Fame

    Points: 3437

    Brilliant article.

    Only one thing which is a bit of a shame - this only works if the SP is execute as owner, not if it's execute as caller.

    As a result, if you look in msdb.dbo.sysmail_allitems, all items sent with this method say they were sent by sa. Also it's allowed to send using private profiles to which nobody has been granted access.

    If you send the mail by adding the user to the msdb role instead, then msdb.dbo.sysmail_allitems details the correct sender.

    DatabaseMail is great, but the permissions are a real struggle. In addition to the sp_send_dbmail permission problem, it's hard to get the permissions sorted out for private profiles. All the documentation says that you can grant permissions on private profiles to database roles in msdb, but in actual fact it won't let you do this.

    Rachel.

  • FreeHansje

    SSChampion

    Points: 11751

    Another question, I'm stupified by the following:

    I wish to allow non-sa users to run a bcp string with xp_cmdshell. The option to use this XP is by default turned off. I can get these non-sa users to run the xp_cmdshell, it's the turning on and off of the configuration which I cannot resolve.

    What I'm looking at is a SP like this:

    CREATE PROCEDURE testSP1

    BEGIN

    EXEC SP_CONFIGURE 'xp_cmdshell', 1;

    RECONFIGURE WITH OVERRIDE;

    EXEC XP_CMDSHELL 'DIR c:\' -- some VBCP command, but for a simple example...

    EXEC SP_CONFIGURE 'xp_cmdshell', 0;

    RECONFIGURE WITH OVERRIDE;

    END

    GO

    I have tried constructions with EXECUTE AS and Certificates, but sofar nada. Scoured the Internet, no example specifically on permissions on SP_CONFIGURE.

    Any suggestions?

    Greetz,
    Hans Brouwer

  • sam bryant

    Right there with Babe

    Points: 786

    I have created a stored procedure in the msdb that will basically start a job and wait for completion. I have implemented the certificate signed stored procedure in one database successfully, but would now like to incorporate it into a second database. I was thinking I would be able to use the same certificate to make this happen (create in second database from file).

    This does not seem to be the case though. Doing a select on sys.certificates in the second database shows NA for pvt_key_encryption_type and NO_PRIVATE_KEY for pvt_key_encryption_type_desc. I receive the following error when trying to sign the stored procedure in the second database from this certificate

    Msg 15556, Level 16, State 1, Line 1

    Cannot decrypt or encrypt using the specified certificate, either because it has no private key or because the password provided for the private key is incorrect.

    I was wondering if this is possible. And if so if there are any reasons not to do this.

    I could see this expanding to other databases in the future and would like to use one certificate (since everything will be targeting the same stored procedure in the msdb database) instead of having to create numerous certificates, logins, users, etc.

    Any advise/information is much appreciated.

    Thanks,

    Sam

  • Jonathan Kehayias

    One Orange Chip

    Points: 26778

    ssb-402814 (3/30/2011)


    I have created a stored procedure in the msdb that will basically start a job and wait for completion. I have implemented the certificate signed stored procedure in one database successfully, but would now like to incorporate it into a second database. I was thinking I would be able to use the same certificate to make this happen (create in second database from file).

    This does not seem to be the case though. Doing a select on sys.certificates in the second database shows NA for pvt_key_encryption_type and NO_PRIVATE_KEY for pvt_key_encryption_type_desc. I receive the following error when trying to sign the stored procedure in the second database from this certificate

    Msg 15556, Level 16, State 1, Line 1

    Cannot decrypt or encrypt using the specified certificate, either because it has no private key or because the password provided for the private key is incorrect.

    I was wondering if this is possible. And if so if there are any reasons not to do this.

    I could see this expanding to other databases in the future and would like to use one certificate (since everything will be targeting the same stored procedure in the msdb database) instead of having to create numerous certificates, logins, users, etc.

    Any advise/information is much appreciated.

    Thanks,

    Sam

    Sam,

    While you and I have passed emails to resolve the problem, I wanted to post an update here in the comments as well so that anyone else encountering this issue has the reference to how to resolve it. I posted a blog post that details the problem with a repro and then shows how to fix the issue by backing up the private key for the certificate when the certificate is backed up so that the private key can be created with the certificate from the backup files in the new database.

    Certificate Signing Stored Procedures in Multiple Databases

    Jonathan Kehayias | Principal Consultant | MCM: SQL Server 2008
    My Blog | Twitter | MVP Profile
    Training | Consulting | Become a SQLskills Insider
    Troubleshooting SQL Server: A Guide for Accidental DBAs[/url]

  • Bob hopw

    SSC Journeyman

    Points: 90

    Excellent article.

    I was having a permission denied on db_mail when sending a email from a sp that was being executed by Service Broker, and this was despite the user who was was executing existing in msdb.DatabaseMailUserRole and set trustworthy was on.

    Ran through the article, created the certificate and assigned it .... success.

    ty

    Murtagh

Viewing 15 posts - 16 through 30 (of 31 total)

You must be logged in to reply to this topic. Login to reply