Issue with SPFile.SendToOfficialFile() while using with custom access policy (copy of WSS_Minimal)
Recently I had worked with an another interesting case with one of my customers. She has developed a custom webpart to iterate all the files in a SharePoint document library and send that file to a record center, also she want to implement partial trust and use least permission to the custom component. For that, she copied the WSS_Minimal.config file and added basic permissions. The below lines of code was using inside the custom webpart.
SPList oList = oElevWeb.Lists["Shared Documents"];
SPFile oFile = oList.Items[0].File;
string strOutPut;
oFile.SendToOfficialFile(out strOutPut);
But the webpart failed to accomplish the functionality whenever she deploy the webpart dll to the webapplication’s bin direcoty and use her custom CAS policy based upon WSS_Minimal trust. From the call stack we came to know that the operation of sending the file to a record center need some high level permission (Full trust or GAC deployment) and the issue get resolved whenever we deploy the dll to the GAC or gave FullTrust as the Trust, but customer don’t want to go ahead with that resolution and want to create a custom WSS_Minimal file with exact permission set to execute her code.
Description: The application attempted to perform an operation not allowed by the security policy. To grant this application the required permission please contact your system administrator or change the application's trust level in the configuration file.
Exception Details: System.Security.SecurityException: Request for the permission of type 'Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' failed.
Source Error:
The source code that generated this unhandled exception can only be shown when compiled in debug mode. To enable this, please follow one of the below steps, then request the URL: 1. Add a "Debug=true" directive at the top of the file that generated the error. Example: <%@ Page Language="C#" Debug="true" %> or: 2) Add the following section to the configuration file of your application: <configuration> <system.web> <compilation debug="true"/> </system.web> </configuration> Note that this second technique will cause all files within a given application to be compiled in debug mode. The first technique will cause only that particular file to be compiled in debug mode. Important: Running applications in debug mode does incur a memory/performance overhead. You should make sure that an application has debugging disabled before deploying into production scenario. |
Stack Trace:
[SecurityException: Request for the permission of type 'Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' failed.] Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) +40 Microsoft.SharePoint.OfficialFile.RgofpFromListItem(SPListItem item, RecordsRepositoryProperty[]& rgofp) +1400 Microsoft.SharePoint.OfficialFile.SubmitFile(SPFile file, String strRecordSeries, String& strAdditionalInfo) +2122 Microsoft.SharePoint.OfficialFile.SubmitFile(SPFile file, String& strAdditionalInfo) +120 Microsoft.SharePoint.SPFile.SendToOfficialFile(String& additionalInformation) +44 AugTestLast.WebPart1.oButton_Click(Object sender, EventArgs e) +158 System.Web.UI.WebControls.Button.OnClick(EventArgs e) +111 System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +110 System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10 System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13 System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +36 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +6785 System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +242 System.Web.UI.Page.ProcessRequest() +80 System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context) +21 System.Web.UI.Page.ProcessRequest(HttpContext context) +49 ASP.DEFAULT_ASPX__82859748.ProcessRequest(HttpContext context) +4 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously |
Resolution:
==========
After my research found the specific permission sets that we need to provide to execute the code to send file to record center. To understand better about the CAS setting in WSS please take look at the below excerpt that I have taken from the below MSDN article.
https://msdn.microsoft.com/en-us/library/dd583158(office.11).aspx.
Windows SharePoint Services defines two trust levels of its own:
- WSS_Minimal
- WSS_Medium
The trust levels extend the Minimal and Medium trust levels of ASP.NET for Windows SharePoint Services. The trust levels are defined in security policy files, wss_minimaltrust.config and wss_mediumtrust.config. By default, Windows SharePoint Services 3.0 stores these files in the following location:
local_drive:\Program Files\Common Files\Microsoft Shared\web server extensions\12\config
By default, when you extend a virtual server with Windows SharePoint Services, Windows SharePoint Services sets the trust level to WSS_Minimal. This helps provide a secure trust level in which assemblies operate with the smallest set of permissions required for code to execute.
The following table outlines the specific permissions granted with the custom security policy files included with Windows SharePoint Services.
Permission |
WSS_Medium trust level |
WSS_Minimal trust level |
AspNetHostingPermission |
Medium |
Minimal |
Environment |
Read: TEMP, TMP, OS, USERNAME, COMPUTERNAME |
|
FileIO |
Read, Write, Append, PathDiscovery:Application Directory |
|
IsolatedStorage |
AssemblyIsolationByUser, Unrestricted UserQuota |
|
Reflection |
|
|
Registry |
|
|
Security |
Execution, Assertion, ControlPrincipal, ControlThread, RemotingConfiguration |
Execution |
Socket |
|
|
WebPermission |
Connect to origin host (if configured) |
|
DNS |
Unrestricted |
|
Printing |
Default printing |
|
OleDBPermission |
|
|
SqlClientPermission |
AllowBlankPassword=false |
|
EventLog |
|
|
Message Queue |
|
|
Service Controller |
|
|
Performance Counters |
|
|
Directory Service |
|
|
SharePointPermission |
ObjectModel = true |
|
WebPartPermission |
Connections = true |
Connections = true |
Note By default, Windows SharePoint Services does not grant access to the Microsoft SharePoint object model. To grant access, you must raise the associated trust level by one of several methods. The next section discusses these methods.
So, if we take a look at the WSS_Minimaltrust.config file we can see that it will not even allow us to execute SharePoint object model code because we must need to enable it by setting ObjectModel=true in Microsoft.SharePoint.Security.SharePointPermission in the custom CAS policy file. Once we do that then we can execute the code but once we use oFile.SendToOfficialFile() then internally it is using a webservice _vti_bin/officialfile.asmx to send the file to the record center. We must need to be configure this in Central Administration > Application Management > Configure Connection to Records Center if we want to send the document through UI. By default, assemblies in the BIN directory do not have the required permission, System.Net.WebPermission to access Web services. To grant this permission, we must need to add the following to the corresponding IPermission element in the appropriate policy file:
<IPermission
class="WebPermission"
version="1"
Unrestricted="true"
/>
Once we do that then we can execute the SendToOfficialFile() method. Also here we have to enable to the following IPermission to send it successfully. If you are using a copy of WSS_MediumTrust.config then no need to add this one because it will be there by default.
<IPermission
class="EnvironmentPermission"
version="1"
Read="TEMP;TMP;USERNAME;OS;COMPUTERNAME"
/>
Below xml is a modified copy of WSS_MinimalTrust.config to execute the SendToOfficialFile() method. Also we can use Permission Calculator Tool to find out the exact permission levels that needed for a particular assembly https://msdn.microsoft.com/en-us/library/ms165077(VS.80).aspx, I have used this tool to find out the all permission sets of Microsoft.SharePoint.dll.
<configuration>
<mscorlib>
<security>
<policy>
<PolicyLevel version="1">
<SecurityClasses>
<SecurityClass Name="AllMembershipCondition" Description="System.Security.Policy.AllMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="AspNetHostingPermission" Description="System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="FirstMatchCodeGroup" Description="System.Security.Policy.FirstMatchCodeGroup, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="NamedPermissionSet" Description="System.Security.NamedPermissionSet"/>
<SecurityClass Name="SecurityPermission" Description="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="StrongNameMembershipCondition" Description="System.Security.Policy.StrongNameMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="UnionCodeGroup" Description="System.Security.Policy.UnionCodeGroup, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="UrlMembershipCondition" Description="System.Security.Policy.UrlMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="WebPartPermission" Description="Microsoft.SharePoint.Security.WebPartPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"/>
<SecurityClass Name="ZoneMembershipCondition" Description="System.Security.Policy.ZoneMembershipCondition, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<!-- add the following 3 security class information to refer those later-->
<SecurityClass Name="SharePointPermission" Description="Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<SecurityClass Name="WebPermission" Description="System.Net.WebPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
<SecurityClass Name="EnvironmentPermission" Description="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</SecurityClasses>
<NamedPermissionSets>
<PermissionSet
class="NamedPermissionSet"
version="1"
Unrestricted="true"
Name="FullTrust"
Description="Allows full access to all resources"
/>
<PermissionSet
class="NamedPermissionSet"
version="1"
Name="Nothing"
Description="Denies all resources, including the right to execute"
/>
<PermissionSet
class="NamedPermissionSet"
version="1"
Name="SPRestricted">
<IPermission
class="AspNetHostingPermission"
version="1"
Level="Minimal"
/>
<IPermission
class="SecurityPermission"
version="1"
Flags="Execution"
/>
<IPermission class="WebPartPermission"
version="1"
Connections="True"
/>
<!--add the following IPermissions to allow to execute the object model code and webservices-->
<IPermission
class="EnvironmentPermission"
version="1"
Read="TEMP;TMP;USERNAME;OS;COMPUTERNAME"
/>
<IPermission class="SharePointPermission"
version="1"
ObjectModel="True"
Unrestricted ="True"
/>
<IPermission
class="WebPermission"
version="1"
Unrestricted="true"
/>
</PermissionSet>
</NamedPermissionSets>
<CodeGroup
class="FirstMatchCodeGroup"
version="1"
PermissionSetName="Nothing">
<IMembershipCondition
class="AllMembershipCondition"
version="1"
/>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust">
<IMembershipCondition
class="UrlMembershipCondition"
version="1"
Url="$AppDirUrl$/_app_bin/*"
/>
</CodeGroup>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="SPRestricted">
<IMembershipCondition
class="UrlMembershipCondition"
version="1"
Url="$AppDirUrl$/*"
/>
</CodeGroup>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust">
<IMembershipCondition
class="UrlMembershipCondition"
version="1"
Url="$CodeGen$/*"
/>
</CodeGroup>
<CodeGroup class="UnionCodeGroup" version="1" PermissionSetName="Nothing">
<IMembershipCondition
class="ZoneMembershipCondition"
version="1"
Zone="MyComputer" />
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="Microsoft_Strong_Name"
Description="This code group grants code signed with the Microsoft strong name full trust. ">
<IMembershipCondition
class="StrongNameMembershipCondition"
version="1"
PublicKeyBlob="002400000480000094000000060200000024000052534131000400000100010007D1FA57C4AED9F0A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C834C99921EB23BE79AD9D5DCC1DD9AD236132102900B723CF980957FC4E177108FC607774F29E8320E92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB99285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF0FC4963D261C8A12436518206DC093344D5AD293"
/>
</CodeGroup>
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="FullTrust"
Name="Ecma_Strong_Name"
Description="This code group grants code signed with the ECMA strong name full trust. ">
<IMembershipCondition
class="StrongNameMembershipCondition"
version="1"
PublicKeyBlob="00000000000000000400000000000000"
/>
</CodeGroup>
</CodeGroup>
</CodeGroup>
</PolicyLevel>
</policy>
</security>
</mscorlib>
</configuration>