One way to hack around this problem is to turn TRUSTWORTHY ON for the database that has your wrapper stored procedure in it. However, the best way to do this is to use EXECUTE AS OWNER, and sign the procedure with a Certificate. To do this requires the following steps:
First create the stored procedure.
Then create a certificate in the database with a strong password.
Backup the certificate to disk.
Restore the certificate in master and create a login from the certificate.
Grant Authenticate Server to the login.
Add a database user in msdb for the certificate login.
Then add the DatabaseMailUserRole to the msdb user for the certificate login.
Finally sign the stored procedure in your database with the certificate.
A full repro for this is below:
CREATE DATABASE TestDBMail
GO
USE [TestDBMail]
GO
CREATE PROCEDURE [dbo].[TestSendMail]
(
@To NVARCHAR(1000),
@Subject NVARCHAR(100),
@Body NVARCHAR(MAX)
)
WITH EXECUTE AS OWNER
AS
BEGIN
EXEC msdb.dbo.sp_send_dbmail
@profile_name = N'Database Mail Profile',
@recipients = @To,
@subject = @Subject,
@body = @Body
END
GO
-- This should fail
EXECUTE [dbo].[TestSendMail] 'someemail@domain.com', 'test', 'body'
-- Create a certificate to sign stored procedures with
CREATE CERTIFICATE [DBMailCertificate]
ENCRYPTION BY PASSWORD = '$tr0ngp@$$w0rd'
WITH SUBJECT = 'Certificate for signing TestSendMail Stored Procedure';
GO
-- Backup certificate so it can be create in master database
BACKUP CERTIFICATE [DBMailCertificate]
TO FILE = 'd:\Backup\DBMailCertificate.CER';
GO
-- Add Certificate to Master Database
USE [master]
GO
CREATE CERTIFICATE [DBMailCertificate]
FROM FILE = 'd:\Backup\DBMailCertificate.CER';
GO
-- Create a login from the certificate
CREATE LOGIN [DBMailLogin]
FROM CERTIFICATE [DBMailCertificate];
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 [DBMailLogin]
GO
-- Create a MSDB User for the Login
USE [msdb]
GO
CREATE USER [DBMailLogin] FROM LOGIN [DBMailLogin]
GO
-- Add msdb login/user to the DatabaseMailUserRole
EXEC msdb.dbo.sp_addrolemember @rolename = 'DatabaseMailUserRole', @membername = 'DBMailLogin';
GO
USE [TestDBMail]
GO
-- Sign the procedure with the certificate's private key
ADD SIGNATURE TO OBJECT::[TestSendMail]
BY CERTIFICATE [DBMailCertificate]
WITH PASSWORD = '$tr0ngp@$$w0rd';
GO
-- This will succeed
EXECUTE [dbo].[TestSendMail] 'someemail@domain.com', 'test', 'body'
/*
-- Cleanup
USE [msdb]
GO
DROP USER [DBMailLogin]
GO
USE [master]
GO
DROP LOGIN [DBMailLogin]
DROP CERTIFICATE [DBMailCertificate]
DROP DATABASE [TestDBMail]
-- Delete the certificate backup from disk
*/
This will allow you to use permission chaining the database that the wrapper procedure exists in while enforcing best security practices in design.
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]