Specifying Global Assembly Cache Permissions

I came across this problem today.  I am using SQL Reporting Services to send reports to a SharePoint document library on a scheduled basis by using the Copy webservice that SharePoint provides.  It was working as expected during initial testing.  The report would be generated and then uploaded to the SharePoint document library when the subscription ran.  Life was good...

Until the next day.  When the reports ran the following day, the SharePoint web service would return a 500 error, even though nothing had been changed.  After looking at the Application event log for the SharePoint server, I found a series of warnings as shown below which coincided with the scheduled run times of the report subscriptions:

Type: Warning
Date: 7/14/2008
Time: 9:32:28 AM
Event: 1309
Source: ASP.NET 2.0.50727.0
Category: Web Event
User: N/A
Computer: SHAREPOINT-SERVER
Description:
Event code: 3005
Event message: An unhandled exception has occurred.
Event time: 7/14/2008 9:32:28 AM
Event time (UTC): 7/14/2008 2:32:28 PM
Event ID: 7b51c82d6e8c4f41919b25cbf2a2f82d
Event sequence: 4
Event occurrence: 3
Event detail code: 0

Application information:
    Application domain: /LM/W3SVC/1/Root-1-458652365596426441
    Trust level: WSS_Minimal
    Application Virtual Path: /
    Application Path: D:\Intetpub\wwwroot\wss\virtualdirectories\
sharepoint-test
Machine name: SHAREPOINT-SERVER

Process information:
    Process ID: 1768
    Process name: w3wp.exe
Account name: DOMAIN\sharepoint-service

Exception information:
Exception type: FileLoadException
Exception message: Could not load file or assembly 'Microsoft.SharePoint.intl, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' or one of its dependencies. Access is denied.

Request information:
Request URL: https://sharepoint-test/
Request path: /
    User host address: 192.168.1.1
    User:
    Is authenticated: False
    Authentication Type:
Thread account name: DOMAIN\sharepoint-service

Thread information:
    Thread ID: 7
    Thread account name: DOMAIN\sharepoint-service
    Is impersonating: False
    Stack trace: at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
at Microsoft.SharePoint.CoreResource..cctor()

I highlighted the SharePoint exception in red above.  All of the sudden, SharePoint was not able to load the "Microsoft.SharePoint.intl" assembly from the GAC (with a permission denied error).  My first thought was "Why could it do this yesterday, but not today?".

To make a long debugging story short, the SQL Reporting Services Account  (SSRSSvcAccount) that was invoking the SharePoint webservice did not have READ permissions to the GAC on the local SharePoint server.  It seems that during debugging  when I logged into the SharePoint site to verify the reports I used a privileged account, which loaded the needed assembly into the app domain.  Now that it was loaded, the service account was able to call the webservice without any problem since the library was already loaded.  But when the app pool was recycled, the needed assembly load was not attempted until the service account invoked the web service, which led to this error because SharePoint was trying to load the assembly by the context of the invoking service account.

OK, so how do we give the service account permissions to the local GAC on the SharePoint server.  Unfortunately, you can't just right-click on the C:\Windows\Assembly folder and set the security permissions.  But we can use the cacls.exe command line utility:

cacls.exe %windir%/assembly /e /t /p DOMAIN\SSRSSvcAccount:R

The command above will give the specified user READ permissions on the entire GAC.  You could tailor it to apply only to specified assembly paths if you prefer. 

You can also view the current permissions for the GAC by running:

cacls.exe %windir%/assembly