|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
#if !RUNTIME_TYPE_NETCORE
using System.Security;
using System.Security.Permissions;
#endif
using System.Xml;
using Microsoft.Build.Shared.FileSystem;
#nullable disable
namespace Microsoft.Build.Tasks.Deployment.ManifestUtilities
{
/// <summary>
/// Describes the application security trust information.
/// </summary>
[ComVisible(false)]
public sealed class TrustInfo
{
#if !RUNTIME_TYPE_NETCORE
// Partial trust and permission sets are not supported by .NET Core.
// SameSite evaluation is conditioned on .NET FX but always done in .NET Core code.
private PermissionSet _inputPermissionSet;
private PermissionSet _outputPermissionSet;
private bool _sameSiteChanged;
#endif
private XmlDocument _inputTrustInfoDocument;
private bool _isFullTrust = true;
private string _sameSiteSetting = "site";
private void AddSameSiteAttribute(XmlElement permissionSetElement)
{
XmlAttribute sameSiteAttribute = (XmlAttribute)permissionSetElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.sameSiteAttribute));
if (sameSiteAttribute == null)
{
sameSiteAttribute = permissionSetElement.OwnerDocument.CreateAttribute(XmlUtil.TrimPrefix(XPaths.sameSiteAttribute));
permissionSetElement.Attributes.Append(sameSiteAttribute);
}
sameSiteAttribute.Value = _sameSiteSetting;
}
/// <summary>
/// Resets the object to its default state.
/// </summary>
public void Clear()
{
#if !RUNTIME_TYPE_NETCORE
_inputPermissionSet = null;
_outputPermissionSet = null;
#endif
_inputTrustInfoDocument = null;
_isFullTrust = true;
}
private void FixupPermissionSetElement(XmlElement permissionSetElement)
{
XmlDocument document = permissionSetElement.OwnerDocument;
XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(document.NameTable);
if (PreserveFullTrustPermissionSet)
{
var unrestrictedAttribute = (XmlAttribute)permissionSetElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
if (_isFullTrust)
{
if (unrestrictedAttribute == null)
{
unrestrictedAttribute = document.CreateAttribute(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
permissionSetElement.Attributes.Append(unrestrictedAttribute);
}
unrestrictedAttribute.Value = "true";
}
else
{
if (unrestrictedAttribute != null)
{
permissionSetElement.Attributes.RemoveNamedItem(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
}
}
}
else
{
if (_isFullTrust)
{
var unrestrictedAttribute = (XmlAttribute)permissionSetElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
if (unrestrictedAttribute == null)
{
unrestrictedAttribute = document.CreateAttribute(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
permissionSetElement.Attributes.Append(unrestrictedAttribute);
}
unrestrictedAttribute.Value = "true";
while (permissionSetElement.FirstChild != null)
{
permissionSetElement.RemoveChild(permissionSetElement.FirstChild);
}
}
}
// Add ID="Custom" attribute if there's not one already
var idAttribute = (XmlAttribute)permissionSetElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.idAttribute));
if (idAttribute == null)
{
idAttribute = document.CreateAttribute(XmlUtil.TrimPrefix(XPaths.idAttribute));
permissionSetElement.Attributes.Append(idAttribute);
}
if (String.IsNullOrEmpty(idAttribute.Value))
{
idAttribute.Value = "Custom";
}
AddSameSiteAttribute(permissionSetElement);
if (permissionSetElement.ParentNode == null ||
permissionSetElement.ParentNode.NodeType == XmlNodeType.Document)
{
return;
}
XmlElement defaultAssemblyRequestElement = (XmlElement)permissionSetElement.ParentNode.SelectSingleNode(XPaths.defaultAssemblyRequestElement, nsmgr);
if (defaultAssemblyRequestElement == null)
{
defaultAssemblyRequestElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.defaultAssemblyRequestElement), XmlNamespaces.asmv2);
permissionSetElement.ParentNode.AppendChild(defaultAssemblyRequestElement);
}
XmlAttribute idrefAttribute = (XmlAttribute)permissionSetElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.permissionSetReferenceAttribute));
if (idrefAttribute == null)
{
idrefAttribute = document.CreateAttribute(XmlUtil.TrimPrefix(XPaths.permissionSetReferenceAttribute));
defaultAssemblyRequestElement.Attributes.Append(idrefAttribute);
}
if (!String.Equals(idAttribute.Value, idrefAttribute.Value, StringComparison.Ordinal))
{
idrefAttribute.Value = idAttribute.Value;
}
}
#if !RUNTIME_TYPE_NETCORE
private PermissionSet GetInputPermissionSet()
{
if (_inputPermissionSet == null)
{
XmlElement psElement = GetInputPermissionSetElement();
if (PreserveFullTrustPermissionSet)
{
XmlAttribute unrestrictedAttribute = (XmlAttribute)psElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
_isFullTrust = unrestrictedAttribute != null && Boolean.Parse(unrestrictedAttribute.Value);
if (_isFullTrust)
{
XmlDocument document = new XmlDocument();
document.AppendChild(document.ImportNode(psElement, true));
document.DocumentElement.Attributes.RemoveNamedItem(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
psElement = document.DocumentElement;
}
_inputPermissionSet = SecurityUtilities.XmlToPermissionSet(psElement);
}
else
{
_inputPermissionSet = SecurityUtilities.XmlToPermissionSet(psElement);
_isFullTrust = _inputPermissionSet.IsUnrestricted();
}
}
return _inputPermissionSet;
}
#endif
private XmlElement GetInputPermissionSetElement()
{
if (_inputTrustInfoDocument == null)
{
_inputTrustInfoDocument = new XmlDocument();
XmlElement trustInfoElement = _inputTrustInfoDocument.CreateElement(XmlUtil.TrimPrefix(XPaths.trustInfoElement), XmlNamespaces.asmv2);
_inputTrustInfoDocument.AppendChild(trustInfoElement);
}
return GetPermissionSetElement(_inputTrustInfoDocument);
}
private XmlElement GetInputRequestedPrivilegeElement()
{
if (_inputTrustInfoDocument == null)
{
return null;
}
XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(_inputTrustInfoDocument.NameTable);
XmlElement trustInfoElement = _inputTrustInfoDocument.DocumentElement;
XmlElement securityElement = (XmlElement)trustInfoElement?.SelectSingleNode(XPaths.securityElement, nsmgr);
XmlElement requestedPrivilegeElement = (XmlElement)securityElement?.SelectSingleNode(XPaths.requestedPrivilegeElement, nsmgr);
return requestedPrivilegeElement;
}
private static XmlElement GetRequestedPrivilegeElement(XmlElement inputRequestedPrivilegeElement, XmlDocument document)
{
// <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
// <!--
// UAC Manifest Options
// If you want to change the Windows User Account Control level replace the
// requestedExecutionLevel node with one of the following .
// <requestedExecutionLevel level="asInvoker" />
// <requestedExecutionLevel level="requireAdministrator" />
// <requestedExecutionLevel level="highestAvailable" />
// If you want to utilize File and Registry Virtualization for backward compatibility
// delete the requestedExecutionLevel node.
// -->
// <requestedExecutionLevel level="asInvoker" />
// </requestedPrivileges>
// we always create a requestedPrivilege node to put into the generated TrustInfo document
//
XmlElement requestedPrivilegeElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.requestedPrivilegeElement), XmlNamespaces.asmv3);
document.AppendChild(requestedPrivilegeElement);
// our three cases we need to handle are:
// (a) no requestedPrivilege node (and therefore no requestedExecutionLevel node as well) - use default values
// (b) requestedPrivilege node and no requestedExecutionLevel node - omit the requestedExecutionLevel node
// (c) requestedPrivilege node and requestedExecutionLevel node - use the incoming requestedExecutionLevel node values
//
// using null for both values is case (b) above -- do not output values
//
string executionLevelString = null;
string executionUIAccessString = null;
string commentString = null;
// case (a) above -- load default values
//
if (inputRequestedPrivilegeElement == null)
{
// If UAC requestedPrivilege node is missing (possibly due to upgraded project) then automatically
// add a default UAC requestedPrivilege node with a default requestedExecutionLevel node set to
// the expected ClickOnce level (asInvoker) with uiAccess as false
//
executionLevelString = Constants.UACAsInvoker;
executionUIAccessString = Constants.UACUIAccess;
// load up a default comment string that we put in front of the requestedExecutionLevel node
// here so we can allow the passed-in node to override it if there is a comment present
//
System.Resources.ResourceManager resources = new System.Resources.ResourceManager("Microsoft.Build.Tasks.Core.Strings.ManifestUtilities", typeof(SecurityUtilities).Module.Assembly);
commentString = resources.GetString("TrustInfo.RequestedExecutionLevelComment");
}
else
{
// we need to see if the requestedExecutionLevel node is present to decide whether or not to create one.
//
XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(document.NameTable);
XmlElement inputRequestedExecutionLevel = (XmlElement)inputRequestedPrivilegeElement.SelectSingleNode(XPaths.requestedExecutionLevelElement, nsmgr);
// case (c) above -- use incoming values [note that we should do nothing for case (b) above
// because the default values will make us not emit the requestedExecutionLevel and comment]
//
if (inputRequestedExecutionLevel != null)
{
XmlNode previousNode = inputRequestedExecutionLevel.PreviousSibling;
// fetch the current comment node if there is one (if there is not one, simply
// keep the default null value which means we will not create one in the
// output document)
//
if (previousNode?.NodeType == XmlNodeType.Comment)
{
commentString = ((XmlComment)previousNode).Data;
}
// fetch the current requestedExecutionLevel node's level attribute if there is one
//
if (inputRequestedExecutionLevel.HasAttribute("level"))
{
executionLevelString = inputRequestedExecutionLevel.GetAttribute("level");
}
// fetch the current requestedExecutionLevel node's uiAccess attribute if there is one
//
if (inputRequestedExecutionLevel.HasAttribute("uiAccess"))
{
executionUIAccessString = inputRequestedExecutionLevel.GetAttribute("uiAccess");
}
}
}
if (commentString != null)
{
XmlComment requestedPrivilegeComment = document.CreateComment(commentString);
requestedPrivilegeElement.AppendChild(requestedPrivilegeComment);
}
if (executionLevelString != null)
{
XmlElement requestedExecutionLevelElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.requestedExecutionLevelElement), XmlNamespaces.asmv3);
requestedPrivilegeElement.AppendChild(requestedExecutionLevelElement);
XmlAttribute levelAttribute = document.CreateAttribute("level");
levelAttribute.Value = executionLevelString;
requestedExecutionLevelElement.Attributes.Append(levelAttribute);
if (executionUIAccessString != null)
{
XmlAttribute uiAccessAttribute = document.CreateAttribute("uiAccess");
uiAccessAttribute.Value = executionUIAccessString;
requestedExecutionLevelElement.Attributes.Append(uiAccessAttribute);
}
}
return requestedPrivilegeElement;
}
// Returns permission set sub-element, creating a full-trust permission-set if one doesn't exist
private XmlElement GetPermissionSetElement(XmlDocument document)
{
Debug.Assert(document != null, "GetPermissionSetElement was passed a null document");
XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(document.NameTable);
XmlElement trustInfoElement = document.DocumentElement;
var securityElement = (XmlElement)trustInfoElement.SelectSingleNode(XPaths.securityElement, nsmgr);
if (securityElement == null)
{
securityElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.securityElement), XmlNamespaces.asmv2);
trustInfoElement.AppendChild(securityElement);
}
XmlElement applicationRequestMinimumElement = (XmlElement)securityElement.SelectSingleNode(XPaths.applicationRequestMinimumElement, nsmgr);
if (applicationRequestMinimumElement == null)
{
applicationRequestMinimumElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.applicationRequestMinimumElement), XmlNamespaces.asmv2);
securityElement.AppendChild(applicationRequestMinimumElement);
}
XmlElement permissionSetElement = (XmlElement)applicationRequestMinimumElement.SelectSingleNode(XPaths.permissionSetElement, nsmgr);
if (permissionSetElement == null)
{
permissionSetElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.permissionSetElement), XmlNamespaces.asmv2);
applicationRequestMinimumElement.AppendChild(permissionSetElement);
XmlAttribute unrestrictedAttribute = document.CreateAttribute(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute), XmlNamespaces.asmv2);
unrestrictedAttribute.Value = _isFullTrust.ToString().ToLowerInvariant();
permissionSetElement.Attributes.Append(unrestrictedAttribute);
}
return permissionSetElement;
}
#if !RUNTIME_TYPE_NETCORE
private PermissionSet GetOutputPermissionSet()
{
if (_outputPermissionSet == null)
{
// NOTE: outputPermissionSet is now the same as the inputPermissionSet
// we used to maintain a list of loadable vs. non-loadable permissions
// this is now cut. If we get more time we should simplify this further
// so there is only one permission set.
_outputPermissionSet = GetInputPermissionSet();
}
return _outputPermissionSet;
}
// Computes permission set from _outputPermissionSet and _unknownPermissions and returns new document
private XmlDocument GetOutputPermissionSetDocument()
{
PermissionSet outputPermissionSet = GetOutputPermissionSet();
XmlDocument outputDocument = SecurityUtilities.PermissionSetToXml(outputPermissionSet);
return outputDocument;
}
#endif
/// <summary>
/// Determines whether the application has permission to call unmanaged code.
/// </summary>
public bool HasUnmanagedCodePermission
{
get
{
#if RUNTIME_TYPE_NETCORE
// Always use full-trust on .NET Core.
return true;
#else
PermissionSet ps = GetOutputPermissionSet();
if (ps == null)
{
return false;
}
var ups = new PermissionSet(PermissionState.None);
ups.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
return ps.Intersect(ups) != null;
#endif
}
}
/// <summary>
/// Determines whether the application is full trust or partial trust.
/// </summary>
public bool IsFullTrust
{
get
{
#if RUNTIME_TYPE_NETCORE
// Always use full-trust on .NET Core.
return true;
#else
GetInputPermissionSet();
return _isFullTrust;
#endif
}
#if !RUNTIME_TYPE_NETCORE
set => _isFullTrust = value;
#endif
}
#if !RUNTIME_TYPE_NETCORE
/// <summary>
/// Gets or sets the permission set object for the application trust.
/// </summary>
public PermissionSet PermissionSet
{
get => GetOutputPermissionSet();
set => _outputPermissionSet = value ?? throw new ArgumentNullException("PermissionSet cannot be set to null.");
}
#endif
/// <summary>
/// Determines whether to preserve partial trust permission when the full trust flag is set.
/// If this option is false with full trust specified, then any permissions defined in the permission set object will be dropped on save.
/// </summary>
public bool PreserveFullTrustPermissionSet { get; set; }
/// <summary>
/// Reads the application trust from an XML file.
/// </summary>
/// <param name="path">The name of the input file.</param>
public void Read(string path)
{
using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
Read(s);
}
}
/// <summary>
/// Reads the application trust from an XML file.
/// </summary>
/// <param name="input">Specifies an input stream.</param>
public void Read(Stream input)
{
Read(input, XPaths.trustInfoPath);
}
private void Read(Stream s, string xpath)
{
Clear();
var document = new XmlDocument();
var xrSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore };
using (XmlReader xr = XmlReader.Create(s, xrSettings))
{
document.Load(xr);
}
XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(document.NameTable);
var trustInfoElement = (XmlElement)document.SelectSingleNode(xpath, nsmgr);
if (trustInfoElement == null)
{
return; // no trustInfo element is okay
}
ReadTrustInfo(trustInfoElement.OuterXml);
}
/// <summary>
/// Reads the application trust from a ClickOnce application manifest.
/// </summary>
/// <param name="path">The name of the input file.</param>
public void ReadManifest(string path)
{
using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
ReadManifest(s);
}
}
/// <summary>
/// Reads the application trust from a ClickOnce application manifest.
/// </summary>
/// <param name="input">Specifies an input stream.</param>
public void ReadManifest(Stream input)
{
Read(input, XPaths.manifestTrustInfoPath);
}
[SuppressMessage("Microsoft.Security.Xml", "CA3057: DoNotUseLoadXml.")]
private void ReadTrustInfo(string xml)
{
_inputTrustInfoDocument = new XmlDocument();
// CA3057: DoNotUseLoadXml. Suppressed since the suggested fix is to use XmlReader.
// XmlReader.Create(string) requires an URI. Whereas the input parameter 'xml' is file content and not a path.
_inputTrustInfoDocument.LoadXml(xml);
XmlElement psElement = GetInputPermissionSetElement();
XmlAttribute unrestrictedAttribute = (XmlAttribute)psElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute));
#if RUNTIME_TYPE_NETCORE
// Partial trust is not supported on .NET Core.
// Fail if loaded manifest does not specify full-trust.
// It can happen if manifest is manually modifed.
if (unrestrictedAttribute == null || (!Boolean.Parse(unrestrictedAttribute.Value)))
{
throw new ArgumentException("Partial trust is not supported.");
}
#else
_isFullTrust = unrestrictedAttribute != null && Boolean.Parse(unrestrictedAttribute.Value);
#endif
XmlAttribute sameSiteAttribute = (XmlAttribute)psElement.Attributes.GetNamedItem(XmlUtil.TrimPrefix(XPaths.sameSiteAttribute));
if (sameSiteAttribute != null)
{
_sameSiteSetting = sameSiteAttribute.Value;
}
}
/// <summary>
/// Describes the level of "same site" access permitted, specifying whether the application has permission to communicate with the server from which it was deployed.
/// </summary>
public string SameSiteAccess
{
get => _sameSiteSetting;
set
{
_sameSiteSetting = value;
#if !RUNTIME_TYPE_NETCORE
_sameSiteChanged = true;
#endif
}
}
public override string ToString()
{
var m = new MemoryStream();
Write(m);
m.Position = 0;
using var r = new StreamReader(m);
return r.ReadToEnd();
}
/// <summary>
/// Writes the application trust to an XML file.
/// </summary>
/// <param name="path">The name of the output file.</param>
public void Write(string path)
{
using (Stream s = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None))
{
Write(s);
s.Flush();
}
}
/// <summary>
/// Writes the application trust to an XML file.
/// </summary>
/// <param name="output"></param>
public void Write(Stream output)
{
var outputDocument = new XmlDocument();
XmlElement inputPermissionSetElement = GetInputPermissionSetElement();
// NOTE: XmlDocument.ImportNode munges "xmlns:asmv2" to "xmlns:d1p1" for some reason, use XmlUtil.CloneElementToDocument instead
XmlElement outputPermissionSetElement = XmlUtil.CloneElementToDocument(inputPermissionSetElement, outputDocument, XmlNamespaces.asmv2);
outputDocument.AppendChild(outputPermissionSetElement);
string tempPrivilegeDocument = null;
var privilegeDocument = new XmlDocument();
XmlElement inputRequestedPrivilegeElement = GetInputRequestedPrivilegeElement();
XmlElement requestedPrivilegeElement = GetRequestedPrivilegeElement(inputRequestedPrivilegeElement, privilegeDocument);
if (requestedPrivilegeElement != null)
{
privilegeDocument.AppendChild(requestedPrivilegeElement);
var p = new MemoryStream();
privilegeDocument.Save(p);
p.Position = 0;
tempPrivilegeDocument = Util.WriteTempFile(p);
}
try
{
string trustInfoResource2 = "trustinfo2.xsl";
// If permission set was not altered, just write out what was read in...
MemoryStream m = new MemoryStream();
#if RUNTIME_TYPE_NETCORE
// Simpler code on .NET Core - due to no support for custom permission sets.
XmlElement permissionSetElement = outputDocument.DocumentElement;
FixupPermissionSetElement(permissionSetElement);
outputDocument.Save(m);
m.Position = 0;
#else
if (_outputPermissionSet == null && !_sameSiteChanged)
{
XmlElement permissionSetElement = outputDocument.DocumentElement;
FixupPermissionSetElement(permissionSetElement);
outputDocument.Save(m);
m.Position = 0;
}
else
{
XmlDocument permissionSetDocument = GetOutputPermissionSetDocument();
XmlElement permissionSetElement = permissionSetDocument.DocumentElement;
FixupPermissionSetElement(permissionSetElement);
if (outputDocument.DocumentElement == null)
{
permissionSetDocument.Save(m);
m.Position = 0;
}
else
{
XmlElement oldPermissionSetElement = outputDocument.DocumentElement;
XmlElement newPermissionSetElement = (XmlElement)outputDocument.ImportNode(permissionSetElement, true);
oldPermissionSetElement.ParentNode.ReplaceChild(newPermissionSetElement, oldPermissionSetElement);
outputDocument.Save(m);
m.Position = 0;
}
}
#endif
// Wrap <PermissionSet> in a <TrustInfo> section
Stream s = tempPrivilegeDocument != null ? XmlUtil.XslTransform(trustInfoResource2, m, new DictionaryEntry("defaultRequestedPrivileges", tempPrivilegeDocument)) : XmlUtil.XslTransform(trustInfoResource2, m);
Util.CopyStream(s, output);
}
finally
{
if (tempPrivilegeDocument != null)
{
File.Delete(tempPrivilegeDocument);
}
}
}
/// <summary>
/// Writes the application trust to a ClickOnce application manifest.
/// If the file exists, the trust section will be updated.
/// If the file does not exist, a new template manifest with the specified trust will be created.
/// </summary>
/// <param name="path">The name of the output file.</param>
public void WriteManifest(string path)
{
Stream s = null;
try
{
if (FileSystems.Default.FileExists(path))
{
s = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
}
else
{
s = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
}
if (s.Length > 0)
{
// If the file is not empty then we assume that it is already a
// valid manifest and that we are just modifying the trust info
WriteManifest(s, s);
}
else
{
// If the file is empty we need to start with a manifest template.
WriteManifest(s);
}
}
finally
{
if (s != null)
{
s.Flush();
s.Close();
}
}
}
/// <summary>
/// Writes the application trust to a new template ClickOnce application manifest.
/// </summary>
/// <param name="output">Specifies an output stream.</param>
public void WriteManifest(Stream output)
{
string r = "manifest.xml";
Stream input = Util.GetEmbeddedResourceStream(r);
WriteManifest(input, output);
}
/// <summary>
/// Updates an existing ClickOnce application manifest with the specified trust.
/// </summary>
/// <param name="input">Specifies an input stream.</param>
/// <param name="output">Specifies an output stream.</param>
public void WriteManifest(Stream input, Stream output)
{
int t1 = Environment.TickCount;
var document = new XmlDocument();
var xrSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore };
using (XmlReader xr = XmlReader.Create(input, xrSettings))
{
document.Load(xr);
}
XmlNamespaceManager nsmgr = XmlNamespaces.GetNamespaceManager(document.NameTable);
XmlElement assemblyElement = (XmlElement)document.SelectSingleNode(XPaths.assemblyElement, nsmgr);
if (assemblyElement == null)
{
throw new BadImageFormatException();
}
var trustInfoElement = (XmlElement)assemblyElement.SelectSingleNode(XPaths.trustInfoElement, nsmgr);
if (trustInfoElement == null)
{
trustInfoElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.trustInfoElement), XmlNamespaces.asmv2);
assemblyElement.AppendChild(trustInfoElement);
}
#if RUNTIME_TYPE_NETCORE
// Partial trust and permission sets are not supported on .NET Core.
// Consequently we do not support updating permissionSet element.
XmlElement securityElement = (XmlElement)trustInfoElement.SelectSingleNode(XPaths.securityElement, nsmgr);
if (securityElement == null)
{
securityElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.securityElement), XmlNamespaces.asmv2);
trustInfoElement.AppendChild(securityElement);
}
XmlElement applicationRequestMinimumElement = (XmlElement)securityElement.SelectSingleNode(XPaths.applicationRequestMinimumElement, nsmgr);
if (applicationRequestMinimumElement == null)
{
applicationRequestMinimumElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.applicationRequestMinimumElement), XmlNamespaces.asmv2);
securityElement.AppendChild(applicationRequestMinimumElement);
}
XmlNodeList permissionSetNodes = applicationRequestMinimumElement.SelectNodes(XPaths.permissionSetElement, nsmgr);
foreach (XmlNode permissionSetNode in permissionSetNodes)
{
applicationRequestMinimumElement.RemoveChild(permissionSetNode);
}
XmlElement fullTrustPermissionSetElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.permissionSetElement), XmlNamespaces.asmv2);
XmlAttribute unrestrictedAttribute = document.CreateAttribute(XmlUtil.TrimPrefix(XPaths.unrestrictedAttribute), XmlNamespaces.asmv2);
unrestrictedAttribute.Value = "true";
applicationRequestMinimumElement.AppendChild(fullTrustPermissionSetElement);
FixupPermissionSetElement(fullTrustPermissionSetElement);
#else
// If we have an input trustinfo document and no output specified then just copy the input to the output
if (_inputTrustInfoDocument != null && _outputPermissionSet == null && !_sameSiteChanged)
{
XmlElement newTrustInfoElement = (XmlElement)document.ImportNode(_inputTrustInfoDocument.DocumentElement, true);
trustInfoElement.ParentNode.ReplaceChild(newTrustInfoElement, trustInfoElement);
}
else
{
var securityElement = (XmlElement)trustInfoElement.SelectSingleNode(XPaths.securityElement, nsmgr);
if (securityElement == null)
{
securityElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.securityElement), XmlNamespaces.asmv2);
trustInfoElement.AppendChild(securityElement);
}
var applicationRequestMinimumElement = (XmlElement)securityElement.SelectSingleNode(XPaths.applicationRequestMinimumElement, nsmgr);
if (applicationRequestMinimumElement == null)
{
applicationRequestMinimumElement = document.CreateElement(XmlUtil.TrimPrefix(XPaths.applicationRequestMinimumElement), XmlNamespaces.asmv2);
securityElement.AppendChild(applicationRequestMinimumElement);
}
XmlNodeList permissionSetNodes = applicationRequestMinimumElement.SelectNodes(XPaths.permissionSetElement, nsmgr);
foreach (XmlNode permissionSetNode in permissionSetNodes)
{
applicationRequestMinimumElement.RemoveChild(permissionSetNode);
}
XmlDocument permissionSetDocument = GetOutputPermissionSetDocument();
var permissionSetElement = (XmlElement)document.ImportNode(permissionSetDocument.DocumentElement, true);
applicationRequestMinimumElement.AppendChild(permissionSetElement);
FixupPermissionSetElement(permissionSetElement);
}
#endif
// Truncate any contents that may be in the file
if (output.Length > 0)
{
output.SetLength(0);
output.Flush();
}
document.Save(output);
Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "ManifestWriter.WriteTrustInfo t={0}", Environment.TickCount - t1));
}
}
}
|