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>