未验证 提交 d2afae4f 编写于 作者: L Levi Broderick 提交者: GitHub

Obsolete XmlSecureResolver (#73676)

- Add XmlResolver.ThrowingResolver
- Migrate all call sites to the new API
- Improve error messages on failure

Resolves CVE-2022-34716.
上级 f44da522
......@@ -101,6 +101,7 @@ The PR that reveals the implementation of the `<IncludeInternalObsoleteAttribute
| __`SYSLIB0044`__ | AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete. Using them for loading an assembly is not supported. |
| __`SYSLIB0045`__ | Cryptographic factory methods accepting an algorithm name are obsolete. Use the parameterless Create factory method on the algorithm type instead. |
| __`SYSLIB0046`__ | ControlledExecution.Run method may corrupt the process and should not be used in production code. |
| __`SYSLIB0047`__ | XmlSecureResolver is obsolete. Use XmlResolver.ThrowingResolver instead when attempting to forbid XML external entity resolution. |
## Analyzer Warnings
......
......@@ -150,5 +150,8 @@ internal static class Obsoletions
internal const string ControlledExecutionRunMessage = "ControlledExecution.Run method may corrupt the process and should not be used in production code.";
internal const string ControlledExecutionRunDiagId = "SYSLIB0046";
internal const string XmlSecureResolverMessage = "XmlSecureResolver is obsolete. Use XmlResolver.ThrowingResolver instead when attempting to forbid XML external entity resolution.";
internal const string XmlSecureResolverDiagId = "SYSLIB0047";
}
}
......@@ -514,7 +514,7 @@
<value>Object type is not supported.</value>
</data>
<data name="Xml_NullResolver" xml:space="preserve">
<value>Resolving of external URIs was prohibited.</value>
<value>Resolving of external URIs was prohibited. Attempted access to: {0}</value>
</data>
<data name="Xml_RelativeUriNotSupported" xml:space="preserve">
<value>Relative URIs are not supported.</value>
......
......@@ -127,13 +127,11 @@
<Compile Include="System\Xml\XmlNameTable.cs" />
<Compile Include="System\Xml\XmlNodeOrder.cs" />
<Compile Include="System\Xml\XmlNodeType.cs" />
<Compile Include="System\Xml\XmlNullResolver.cs" />
<Compile Include="System\Xml\XmlQualifiedName.cs" />
<Compile Include="System\Xml\XmlReservedNs.cs" />
<Compile Include="System\Xml\XmlResolver.cs" />
<Compile Include="System\Xml\XmlResolverAsync.cs" />
<Compile Include="System\Xml\XmlResolver.ThrowingResolver.cs" />
<Compile Include="System\Xml\XmlSecureResolver.cs" />
<Compile Include="System\Xml\XmlSecureResolverAsync.cs" />
<Compile Include="System\Xml\XmlUrlResolver.cs" />
<Compile Include="System\Xml\XmlUrlResolverAsync.cs" />
<Compile Include="System\Xml\Core\CharEntityEncoderFallback.cs" />
......@@ -748,6 +746,7 @@
<Compile Include="$(CommonPath)System\CSharpHelpers.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.cs" Link="Common\System\Text\ValueStringBuilder.cs" />
<Compile Include="$(CommonPath)System\Text\ValueStringBuilder.AppendSpanFormattable.cs" Link="Common\System\Text\ValueStringBuilder.AppendSpanFormattable.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs" Link="Common\System\Obsoletions.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net;
namespace System.Xml
{
internal sealed class XmlNullResolver : XmlResolver
{
public static readonly XmlNullResolver Singleton = new XmlNullResolver();
// Private constructor ensures existing only one instance of XmlNullResolver
private XmlNullResolver() { }
public override object GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
throw new XmlException(SR.Xml_NullResolver, string.Empty);
}
public override ICredentials Credentials
{
set { /* Do nothing */ }
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net;
using System.Threading.Tasks;
namespace System.Xml
{
public abstract partial class XmlResolver
{
/// <summary>
/// Gets an XML resolver which forbids entity resolution.
/// </summary>
/// <value>An XML resolver which forbids entity resolution.</value>
/// <remarks>
/// Calling <see cref="GetEntity"/> or <see cref="GetEntityAsync"/> on the
/// <see cref="XmlResolver"/> instance returned by this property is forbidden
/// and will result in <see cref="XmlException"/> being thrown.
///
/// Use <see cref="ThrowingResolver"/> when external entity resolution must be
/// prohibited, even when DTD processing is otherwise enabled.
/// </remarks>
public static XmlResolver ThrowingResolver => XmlThrowingResolver.s_singleton;
// An XmlResolver that forbids all external entity resolution.
private sealed class XmlThrowingResolver : XmlResolver
{
internal static readonly XmlThrowingResolver s_singleton = new();
// Private constructor ensures existing only one instance of XmlThrowingResolver
private XmlThrowingResolver() { }
public override ICredentials Credentials
{
set { /* Do nothing */ }
}
public override object GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
throw new XmlException(SR.Format(SR.Xml_NullResolver, absoluteUri));
}
public override Task<object> GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
throw new XmlException(SR.Format(SR.Xml_NullResolver, absoluteUri));
}
}
}
}
......@@ -28,7 +28,12 @@ public abstract partial class XmlResolver
string? role,
Type? ofObjectToReturn);
public virtual Task<object> GetEntityAsync(Uri absoluteUri,
string? role,
Type? ofObjectToReturn)
{
throw new NotImplementedException();
}
/// <devdoc>
/// <para>[To be supplied.]</para>
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Threading.Tasks;
namespace System.Xml
{
public abstract partial class XmlResolver
{
public virtual Task<object> GetEntityAsync(Uri absoluteUri,
string? role,
Type? ofObjectToReturn)
{
throw new NotImplementedException();
}
}
}
......@@ -3,32 +3,31 @@
using System.Net;
using System.Security;
using System.Runtime.Versioning;
using System.Threading.Tasks;
namespace System.Xml
{
public partial class XmlSecureResolver : XmlResolver
[Obsolete(Obsoletions.XmlSecureResolverMessage, DiagnosticId = Obsoletions.XmlSecureResolverDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public class XmlSecureResolver : XmlResolver
{
private readonly XmlResolver _resolver;
public XmlSecureResolver(XmlResolver resolver, string? securityUrl)
{
_resolver = resolver;
// no-op
}
public override ICredentials Credentials
{
set { _resolver.Credentials = value; }
set { /* no-op */ }
}
public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
return _resolver.GetEntity(absoluteUri, role, ofObjectToReturn);
}
// Forward to ThrowingResolver to get its exception message
public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn) => XmlResolver.ThrowingResolver.GetEntity(absoluteUri, role, ofObjectToReturn);
public override Uri ResolveUri(Uri? baseUri, string? relativeUri)
{
return _resolver.ResolveUri(baseUri, relativeUri);
}
// Forward to ThrowingResolver to get its exception message
public override Task<object> GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn) => XmlResolver.ThrowingResolver.GetEntityAsync(absoluteUri, role, ofObjectToReturn);
// An earlier implementation of this type overrode this method, so we'll continue to do so
// to preserve binary compatibility.
public override Uri ResolveUri(Uri? baseUri, string? relativeUri) => base.ResolveUri(baseUri, relativeUri);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Threading.Tasks;
namespace System.Xml
{
public partial class XmlSecureResolver : XmlResolver
{
public override Task<object> GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
return _resolver.GetEntityAsync(absoluteUri, role, ofObjectToReturn);
}
}
}
......@@ -83,7 +83,7 @@ private void Execute(object defaultDocument, XmlResolver? dataSources, XsltArgum
Debug.Assert(results != null);
// Ensure that dataSources is always non-null
dataSources ??= XmlNullResolver.Singleton;
dataSources ??= XmlResolver.ThrowingResolver;
_delExec(new XmlQueryRuntime(_staticData, defaultDocument, dataSources, argumentList, results));
}
......
......@@ -70,12 +70,12 @@ public Compiler(XsltSettings settings, bool debug, string? scriptAssemblyPath)
Scripts = new Scripts(this);
}
public CompilerErrorCollection Compile(object stylesheet, XmlResolver? xmlResolver, out QilExpression qil)
public CompilerErrorCollection Compile(object stylesheet, XmlResolver? xmlResolver, XmlResolver? origResolver, out QilExpression qil)
{
Debug.Assert(stylesheet != null);
Debug.Assert(Root == null, "Compiler cannot be reused");
new XsltLoader().Load(this, stylesheet, xmlResolver);
new XsltLoader().Load(this, stylesheet, xmlResolver, origResolver);
qil = QilGenerator.CompileStylesheet(this);
SortErrors();
return CompilerErrorColl;
......
......@@ -38,11 +38,11 @@ internal sealed class XsltLoader : IErrorHelper
public static int V2Opt = 4;
public static int V2Req = 8;
public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver)
public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver, XmlResolver? origResolver)
{
Debug.Assert(compiler != null);
_compiler = compiler;
_xmlResolver = xmlResolver ?? XmlNullResolver.Singleton;
_xmlResolver = xmlResolver ?? XmlResolver.ThrowingResolver;
XmlReader? reader = stylesheet as XmlReader;
if (reader != null)
......@@ -57,10 +57,21 @@ public void Load(Compiler compiler, object stylesheet, XmlResolver? xmlResolver)
string? uri = stylesheet as string;
if (uri != null)
{
// If xmlResolver == null, then the original uri will be resolved using XmlUrlResolver
XmlResolver origResolver = xmlResolver!;
if (xmlResolver == null || xmlResolver == XmlNullResolver.Singleton)
origResolver = new XmlUrlResolver();
// If the stylesheet has been provided as a string (URI, really), then we'll bounce
// through an XmlResolver to look up its contents. There's a complication here since
// the default resolver provided by our caller is likely a throwing resolver, and
// a throwing resolver would fail even when attempting to read this URL. We need
// to at minimum allow this URL to be read, because the user after all did explicitly
// ask us to do so.
//
// In this case, we'll rely on the 'origResolver' argument, which is the XmlResolver
// which was provided *to our caller* before any default substitution took place.
// If an explicit resolver was specified, we'll honor it. Otherwise we'll substitute
// an XmlUrlResolver for this one read operation. The stored resolver (which is used
// for reads beyond the initial stylesheet read) will use a throwing resolver as its
// default, as shown at the very top of this method.
origResolver ??= new XmlUrlResolver();
Uri resolvedUri = origResolver.ResolveUri(null, uri);
if (resolvedUri == null)
{
......
......@@ -345,7 +345,7 @@ internal XsltOutput Output
_builder = null;
_actionStack = new HWStack(StackIncrement);
_output = _rootAction.Output;
_resolver = resolver ?? XmlNullResolver.Singleton;
_resolver = resolver ?? XmlResolver.ThrowingResolver;
_args = args ?? new XsltArgumentList();
_debugger = debugger;
if (_debugger != null)
......
......@@ -82,7 +82,7 @@ private void Reset()
public void Load(XmlReader stylesheet)
{
Reset();
LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver());
LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver(), originalStylesheetResolver: null);
}
// SxS: This method does not take any resource name and does not expose any resources to the caller.
......@@ -90,7 +90,7 @@ public void Load(XmlReader stylesheet)
public void Load(XmlReader stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver)
{
Reset();
LoadInternal(stylesheet, settings, stylesheetResolver);
LoadInternal(stylesheet, settings, stylesheetResolver, stylesheetResolver);
}
// SxS: This method does not take any resource name and does not expose any resources to the caller.
......@@ -98,7 +98,7 @@ public void Load(XmlReader stylesheet, XsltSettings? settings, XmlResolver? styl
public void Load(IXPathNavigable stylesheet)
{
Reset();
LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver());
LoadInternal(stylesheet, XsltSettings.Default, CreateDefaultResolver(), originalStylesheetResolver: null);
}
// SxS: This method does not take any resource name and does not expose any resources to the caller.
......@@ -106,29 +106,32 @@ public void Load(IXPathNavigable stylesheet)
public void Load(IXPathNavigable stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver)
{
Reset();
LoadInternal(stylesheet, settings, stylesheetResolver);
LoadInternal(stylesheet, settings, stylesheetResolver, stylesheetResolver);
}
public void Load(string stylesheetUri)
{
Reset();
ArgumentNullException.ThrowIfNull(stylesheetUri);
LoadInternal(stylesheetUri, XsltSettings.Default, CreateDefaultResolver());
LoadInternal(stylesheetUri, XsltSettings.Default, CreateDefaultResolver(), originalStylesheetResolver: null);
}
public void Load(string stylesheetUri, XsltSettings? settings, XmlResolver? stylesheetResolver)
{
Reset();
ArgumentNullException.ThrowIfNull(stylesheetUri);
LoadInternal(stylesheetUri, settings, stylesheetResolver);
LoadInternal(stylesheetUri, settings, stylesheetResolver, stylesheetResolver);
}
private void LoadInternal(object stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver)
// The 'originalStylesheetResolver' argument should be the original XmlResolver
// that was passed to the caller (or null), *before* any default substitutions
// were made by the caller.
private void LoadInternal(object stylesheet, XsltSettings? settings, XmlResolver? stylesheetResolver, XmlResolver? originalStylesheetResolver)
{
ArgumentNullException.ThrowIfNull(stylesheet);
settings ??= XsltSettings.Default;
CompileXsltToQil(stylesheet, settings, stylesheetResolver);
CompileXsltToQil(stylesheet, settings, stylesheetResolver, originalStylesheetResolver);
CompilerError? error = GetFirstError();
if (error != null)
{
......@@ -142,9 +145,9 @@ private void LoadInternal(object stylesheet, XsltSettings? settings, XmlResolver
[MemberNotNull(nameof(_compilerErrorColl))]
[MemberNotNull(nameof(_qil))]
private void CompileXsltToQil(object stylesheet, XsltSettings settings, XmlResolver? stylesheetResolver)
private void CompileXsltToQil(object stylesheet, XsltSettings settings, XmlResolver? stylesheetResolver, XmlResolver? originalStylesheetResolver)
{
_compilerErrorColl = new Compiler(settings, _enableDebug, null).Compile(stylesheet, stylesheetResolver, out _qil);
_compilerErrorColl = new Compiler(settings, _enableDebug, null).Compile(stylesheet, stylesheetResolver, originalStylesheetResolver, out _qil);
}
/// <summary>
......@@ -405,7 +408,7 @@ private static XmlResolver CreateDefaultResolver()
return new XmlUrlResolver();
}
return XmlNullResolver.Singleton;
return XmlResolver.ThrowingResolver;
}
}
}
......@@ -246,7 +246,7 @@ private void Compile(XPathNavigator stylesheet, XmlResolver? resolver)
Compiler compiler = new Compiler();
NavigatorInput input = new NavigatorInput(stylesheet);
compiler.Compile(input, resolver ?? XmlNullResolver.Singleton);
compiler.Compile(input, resolver ?? XmlResolver.ThrowingResolver);
Debug.Assert(compiler.CompiledStylesheet != null);
Debug.Assert(compiler.QueryStore != null);
......@@ -264,7 +264,7 @@ private static XmlResolver CreateDefaultResolver()
}
else
{
return XmlNullResolver.Singleton;
return XmlResolver.ThrowingResolver;
}
}
}
......
......@@ -4,6 +4,8 @@
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="XmlThrowingResolverTests.cs" />
<Compile Include="XmlSecureResolverTests.cs" />
<Compile Include="XmlUrlResolverTests.cs" />
</ItemGroup>
</Project>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using Xunit;
#pragma warning disable SYSLIB0047 // XmlSecureResolver is obsolete
namespace System.Xml.Tests
{
public class XmlSecureResolverTests
{
[Fact]
public void GetEntity_ThrowsXmlException()
{
PoisonedXmlResolver innerResolver = new PoisonedXmlResolver();
XmlSecureResolver outerResolver = new XmlSecureResolver(innerResolver, "some-url");
Uri absoluteUri = new Uri("https://dot.net/");
Type typeToReturn = typeof(Stream);
Assert.Throws<XmlException>(() => outerResolver.GetEntity(absoluteUri, "role", typeToReturn));
Assert.False(innerResolver.WasAnyApiInvoked);
}
[Fact]
public void GetEntityAsync_ThrowsXmlException()
{
PoisonedXmlResolver innerResolver = new PoisonedXmlResolver();
XmlSecureResolver outerResolver = new XmlSecureResolver(innerResolver, "some-url");
Uri absoluteUri = new Uri("https://dot.net/");
Type typeToReturn = typeof(Stream);
Assert.Throws<XmlException>(() => (object)outerResolver.GetEntityAsync(absoluteUri, "role", typeToReturn));
Assert.False(innerResolver.WasAnyApiInvoked);
}
[Fact]
public void Instance_HasNoState()
{
// This is a safety check to ensure we're not keeping the inner resolver in an instance field,
// since we don't want to risk invoking it.
FieldInfo[] allDeclaredInstanceFields = typeof(XmlSecureResolver).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
Assert.Empty(allDeclaredInstanceFields);
}
private sealed class PoisonedXmlResolver : XmlResolver
{
public bool WasAnyApiInvoked { get; private set; }
public override ICredentials Credentials
{
set
{
WasAnyApiInvoked = true;
throw new NotImplementedException();
}
}
public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
WasAnyApiInvoked = true;
throw new NotImplementedException();
}
public override Task<object> GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
WasAnyApiInvoked = true;
throw new NotImplementedException();
}
public override Uri ResolveUri(Uri? baseUri, string? relativeUri)
{
WasAnyApiInvoked = true;
throw new NotImplementedException();
}
public override bool SupportsType(Uri absoluteUri, Type? type)
{
WasAnyApiInvoked = true;
throw new NotImplementedException();
}
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using Xunit;
namespace System.Xml.Tests
{
public class XmlThrowingResolverTests
{
[Fact]
public void PropertyAccessor_ReturnsSingleton()
{
XmlResolver resolver1 = XmlResolver.ThrowingResolver;
Assert.NotNull(resolver1);
XmlResolver resolver2 = XmlResolver.ThrowingResolver;
Assert.Same(resolver1, resolver2);
Assert.Equal(resolver1, resolver2); // default comparer should also say they're equal
}
[Fact]
public void GetEntity_ThrowsXmlException()
{
XmlResolver resolver = XmlResolver.ThrowingResolver;
Uri absoluteUri = new Uri("https://dot.net/");
Type typeToReturn = typeof(Stream);
Assert.Throws<XmlException>(() => resolver.GetEntity(absoluteUri, "role", typeToReturn));
}
[Fact]
public void GetEntityAsync_ThrowsXmlException()
{
XmlResolver resolver = XmlResolver.ThrowingResolver;
Uri absoluteUri = new Uri("https://dot.net/");
Type typeToReturn = typeof(Stream);
Assert.Throws<XmlException>(() => (object)resolver.GetEntityAsync(absoluteUri, "role", typeToReturn));
}
}
}
......@@ -86,12 +86,6 @@ public CXmlTestResolver()
_resolver = new XmlUrlResolver();
}
public CXmlTestResolver(string securityUri)
: base()
{
_resolver = new XmlSecureResolver(new XmlUrlResolver(), securityUri);
}
// -----------------
// Events
// -----------------
......
......@@ -98,6 +98,6 @@
<!-- XML Exceptions -->
<exception res="Xml_BadStartNameChar" message="The '{0}' character, hexadecimal value {1}, cannot begin a name."/>
<exception res="Xml_ResolveUrl" message="There was an error accessing {0}."/>
<exception res="Xml_NullResolver" message="Resolving of external URIs was prohibited. "/>
<exception res="Xml_NullResolver" message="Resolving of external URIs was prohibited. Attempted access to: {0}"/>
</exceptions>
\ No newline at end of file
......@@ -101,6 +101,6 @@
<!-- XML Exceptions -->
<exception res="Xml_BadStartNameChar" message="The '{0}' character, hexadecimal value {1}, cannot begin a name."/>
<exception res="Xml_ResolveUrl" message="There was an error accessing {0}."/>
<exception res="Xml_NullResolver" message="Resolving of external URIs was prohibited. "/>
<exception res="Xml_NullResolver" message="Resolving of external URIs was prohibited. Attempted access to: {0}"/>
</exceptions>
\ No newline at end of file
......@@ -5,6 +5,7 @@
using Xunit.Abstractions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
......@@ -1486,6 +1487,35 @@ public void LoadUrlResolver3(XslInputType xslInputType, ReaderType readerType)
_output.WriteLine("Passing null stylesheet parameter should have thrown ArgumentNullException");
Assert.True(false);
}
//[Variation("Call Load with custom resolver; custom resolver should be honored.")]
[InlineData(XslInputType.URI, ReaderType.XmlValidatingReader)]
[Theory]
public void LoadUrlResolver4(XslInputType xslInputType, ReaderType readerType)
{
var auditingResolver = new XmlAuditingUrlResolver();
LoadXSL_Resolver(Path.Combine("XmlResolver", "XmlResolverTestMain.xsl"), xslInputType, readerType, auditingResolver);
HashSet<Uri> expected = new()
{
new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath(Path.Combine("XmlResolver", "XmlResolverTestMain.xsl")))),
new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath(Path.Combine("XmlResolver", "XmlResolverInclude.xsl")))),
new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath(Path.Combine("XmlResolver", "XmlResolverImport.xsl")))),
};
Assert.Equal(expected, auditingResolver.FetchedUris);
}
private sealed class XmlAuditingUrlResolver : XmlUrlResolver
{
internal readonly HashSet<Uri> FetchedUris = new();
public override object? GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
FetchedUris.Add(absoluteUri);
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
}
}
/***********************************************************/
......
......@@ -1019,7 +1019,8 @@ public void LoadGeneric4(InputType inputType, ReaderType readerType)
}
catch (System.Xml.Xsl.XsltCompileException e)
{
CheckExpectedError(e.InnerException, "System.Xml", "Xml_NullResolver", new string[] { "" });
var absoluteUri = new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath("XmlResolver_Include.xsl"))).AbsoluteUri;
CheckExpectedError(e.InnerException, "System.Xml", "Xml_NullResolver", new string[] { absoluteUri });
return;
}
_output.WriteLine("Exception not thrown for null resolver");
......@@ -1104,7 +1105,9 @@ public void TC_No_Explicit_Resolver_Prohibits_External_Url(InputType inputType,
Assert.False(isEnabled);
var e = Assert.Throws<XsltCompileException>(() => LoadXSL("XmlResolver_Main.xsl", inputType, readerType));
var xmlException = Assert.IsType<XmlException>(e.InnerException);
CheckExpectedError(xmlException, "System.Xml", "Xml_NullResolver", Array.Empty<string>());
var absoluteUri = new Uri(Path.Combine(Environment.CurrentDirectory, FullFilePath("XmlResolver_Include.xsl"))).AbsoluteUri;
CheckExpectedError(xmlException, "System.Xml", "Xml_NullResolver", new string[] { absoluteUri });
}
//[Variation("Load with resolver with credentials, then load XSL that does not need cred.")]
......
......@@ -393,4 +393,7 @@
<data name="WrongRootElement" xml:space="preserve">
<value>Root element must be {0} element in namespace {1}</value>
</data>
</root>
<data name="Cryptography_Xml_EntityResolutionNotSupported" xml:space="preserve">
<value>External entity resolution is not supported.</value>
</data>
</root>
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
......@@ -126,6 +126,7 @@ System.Security.Cryptography.Xml.XmlLicenseTransform</PackageDescription>
<Compile Include="System\Security\Cryptography\Xml\XmlDsigXPathTransform.cs" />
<Compile Include="System\Security\Cryptography\Xml\XmlDsigXsltTransform.cs" />
<Compile Include="System\Security\Cryptography\Xml\XmlLicenseTransform.cs" />
<Compile Include="System\Security\Cryptography\Xml\XmlResolverHelper.cs" />
<Compile Include="System\Security\Cryptography\Xml\CryptoHelpers.cs" />
<Compile Include="System\Security\Cryptography\Xml\RSAPKCS1SignatureDescription.cs" />
<Compile Include="System\Security\Cryptography\Xml\RSAPKCS1SHA1SignatureDescription.cs" />
......
......@@ -359,7 +359,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re
{
case ReferenceTargetType.Stream:
// This is the easiest case. We already have a stream, so just pump it through the TransformChain
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver());
hashInputStream = TransformChain.TransformToOctetStream((Stream)_refTarget, resolver, baseUri);
break;
case ReferenceTargetType.UriReference:
......@@ -369,7 +369,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re
if (_uri == null)
{
// We need to create a DocumentNavigator out of the XmlElement
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver());
// In the case of a Uri-less reference, we will simply pass null to the transform chain.
// The first transform in the chain is expected to know how to retrieve the data to hash.
hashInputStream = TransformChain.TransformToOctetStream((Stream)null, resolver, baseUri);
......@@ -382,7 +382,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re
throw new CryptographicException(SR.Format(SR.Cryptography_Xml_SelfReferenceRequiresContext, _uri));
// Normalize the containing document
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver());
XmlDocument docWithNoComments = Utils.DiscardComments(Utils.PreProcessDocumentInput(document, resolver, baseUri));
hashInputStream = TransformChain.TransformToOctetStream(docWithNoComments, resolver, baseUri);
}
......@@ -398,7 +398,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re
throw new CryptographicException(SR.Format(SR.Cryptography_Xml_SelfReferenceRequiresContext, _uri));
// We should not discard comments here!!!
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver());
hashInputStream = TransformChain.TransformToOctetStream(Utils.PreProcessDocumentInput(document, resolver, baseUri), resolver, baseUri);
break;
}
......@@ -434,7 +434,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re
// Add the propagated attributes
Utils.AddNamespaces(normDocument.DocumentElement, _namespaces);
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver());
if (discardComments)
{
// We should discard comments before going into the transform chain
......@@ -454,7 +454,7 @@ internal byte[] CalculateHashValue(XmlDocument document, CanonicalXmlNodeList re
break;
case ReferenceTargetType.XmlElement:
// We need to create a DocumentNavigator out of the XmlElement
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
resolver = (SignedXml.ResolverSet ? SignedXml._xmlResolver : XmlResolverHelper.GetThrowingResolver());
hashInputStream = TransformChain.TransformToOctetStream(Utils.PreProcessElementInput((XmlElement)_refTarget, resolver, baseUri), resolver, baseUri);
break;
default:
......
......@@ -775,7 +775,7 @@ private byte[] GetC14NDigest(HashAlgorithm hash)
if (isKeyedHashAlgorithm || !_bCacheValid || !SignedInfo.CacheValid)
{
string baseUri = _containingDocument?.BaseURI;
XmlResolver resolver = (_bResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), baseUri));
XmlResolver resolver = (_bResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
XmlDocument doc = Utils.PreProcessElementInput(SignedInfo.GetXml(), resolver, baseUri);
// Add non default namespaces in scope
......
......@@ -142,7 +142,7 @@ private void LoadStreamInput(Stream stream)
{
XmlDocument document = new XmlDocument();
document.PreserveWhitespace = true;
XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI));
XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI);
document.Load(xmlReader);
_containingDocument = document;
......
......@@ -47,7 +47,7 @@ protected override XmlNodeList GetInnerXml()
public override void LoadInput(object obj)
{
XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI));
XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
if (obj is Stream)
{
_cXml = new CanonicalXml((Stream)obj, _includeComments, resolver, BaseURI);
......
......@@ -79,7 +79,7 @@ private void LoadStreamInput(Stream stream)
{
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI));
XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
XmlReader xmlReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI);
doc.Load(xmlReader);
_containingDocument = doc;
......
......@@ -74,7 +74,7 @@ public override void LoadInnerXml(XmlNodeList nodeList)
public override void LoadInput(object obj)
{
XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI));
XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
if (obj is Stream)
{
_excCanonicalXml = new ExcCanonicalXml((Stream)obj, _includeComments, _inclusiveNamespacesPrefixList, resolver, BaseURI);
......
......@@ -133,7 +133,7 @@ public override void LoadInput(object obj)
private void LoadStreamInput(Stream stream)
{
XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI));
XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
XmlReader valReader = Utils.PreProcessStreamInput(stream, resolver, BaseURI);
_document = new XmlDocument();
_document.PreserveWhitespace = true;
......@@ -143,7 +143,7 @@ private void LoadStreamInput(Stream stream)
private void LoadXmlNodeListInput(XmlNodeList nodeList)
{
// Use C14N to get a document
XmlResolver resolver = (ResolverSet ? _xmlResolver : new XmlSecureResolver(new XmlUrlResolver(), BaseURI));
XmlResolver resolver = (ResolverSet ? _xmlResolver : XmlResolverHelper.GetThrowingResolver());
CanonicalXml c14n = new CanonicalXml((XmlNodeList)nodeList, resolver, true);
using (MemoryStream ms = new MemoryStream(c14n.GetBytes()))
{
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Net;
using System.Threading.Tasks;
using System.Xml;
namespace System.Security.Cryptography.Xml
{
internal static class XmlResolverHelper
{
internal static XmlResolver GetThrowingResolver()
{
#if NET7_0_OR_GREATER
return XmlResolver.ThrowingResolver;
#else
return XmlThrowingResolver.s_singleton;
#endif
}
#if !NET7_0_OR_GREATER
// An XmlResolver that forbids all external entity resolution.
// (Copied from XmlResolver.ThrowingResolver.cs.)
private sealed class XmlThrowingResolver : XmlResolver
{
internal static readonly XmlThrowingResolver s_singleton = new();
// Private constructor ensures existing only one instance of XmlThrowingResolver
private XmlThrowingResolver() { }
public override ICredentials Credentials
{
set { /* Do nothing */ }
}
public override object GetEntity(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
throw new XmlException(SR.Cryptography_Xml_EntityResolutionNotSupported);
}
public override Task<object> GetEntityAsync(Uri absoluteUri, string? role, Type? ofObjectToReturn)
{
throw new XmlException(SR.Cryptography_Xml_EntityResolutionNotSupported);
}
}
#endif
}
}
......@@ -11,8 +11,11 @@
using System.Globalization;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.XPath;
using Test.Cryptography;
......@@ -1587,6 +1590,127 @@ public void VerifyHMAC_HMACOutputLength_Invalid()
Assert.Throws<FormatException>(() => sign.CheckSignature(new HMACSHA1("no clue"u8.ToArray())));
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void VerifyXmlResolver(bool provideResolver)
{
HttpListener listener;
int port = 9000;
while (true)
{
listener = new HttpListener();
listener.Prefixes.Add($"http://127.0.0.1:{port}/");
listener.IgnoreWriteExceptions = true;
try
{
listener.Start();
break;
}
catch
{
}
port++;
if (port > 10000)
{
throw new InvalidOperationException("Could not find an open port");
}
}
string xml = $@"<!DOCTYPE foo [<!ENTITY xxe SYSTEM ""http://127.0.0.1:{port}/"" >]>
<ExampleDoc>Example doc to be signed.&xxe;<Signature xmlns=""http://www.w3.org/2000/09/xmldsig#"">
<SignedInfo>
<CanonicalizationMethod Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315"" />
<SignatureMethod Algorithm=""http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"" />
<Reference URI="""">
<Transforms>
<Transform Algorithm=""http://www.w3.org/2000/09/xmldsig#enveloped-signature"" />
</Transforms>
<DigestMethod Algorithm=""http://www.w3.org/2001/04/xmlenc#sha256"" />
<DigestValue>CLUSJx4H4EwydAT/CtNWYu/l6R8uZe0tO2rlM/o0iM4=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>o0IAVyovNUYKs5CCIRpZVy6noLpdJBp8LwWrqzzhKPg=</SignatureValue>
</Signature>
</ExampleDoc>";
bool listenerContacted = false;
CancellationTokenSource tokenSource = new CancellationTokenSource();
Task listenerTask = ProcessRequests(listener, req => listenerContacted = true, tokenSource.Token);
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
SignedXml signedXml = new SignedXml(doc);
signedXml.LoadXml((XmlElement)doc.GetElementsByTagName("Signature")[0]);
try
{
using (HMAC key = new HMACSHA256(Encoding.UTF8.GetBytes("sample")))
{
if (provideResolver)
{
signedXml.Resolver = new XmlUrlResolver();
Assert.True(signedXml.CheckSignature(key), "signedXml.CheckSignature(key)");
Assert.True(listenerContacted, "listenerContacted");
}
else
{
XmlException ex = Assert.Throws<XmlException>(() => signedXml.CheckSignature(key));
Assert.False(listenerContacted, "listenerContacted");
}
}
}
finally
{
tokenSource.Cancel();
try
{
listener.Stop();
}
catch
{
}
((IDisposable)listener).Dispose();
}
static async Task ProcessRequests(
HttpListener listener,
Action<HttpListenerRequest> requestReceived,
CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
HttpListenerContext ctx;
try
{
ctx = await listener.GetContextAsync();
}
catch
{
break;
}
HttpListenerRequest req = ctx.Request;
requestReceived(req);
using (HttpListenerResponse resp = ctx.Response)
{
resp.ContentType = "text/plain";
resp.ContentEncoding = Encoding.UTF8;
resp.ContentLength64 = 0;
}
}
}
}
[Fact]
public void CoreFxSignedXmlUsesSha256ByDefault()
{
......
......@@ -948,11 +948,13 @@ public abstract partial class XmlResolver
{
protected XmlResolver() { }
public virtual System.Net.ICredentials Credentials { set { } }
public static System.Xml.XmlResolver ThrowingResolver { get { throw null; } }
public abstract object? GetEntity(System.Uri absoluteUri, string? role, System.Type? ofObjectToReturn);
public virtual System.Threading.Tasks.Task<object> GetEntityAsync(System.Uri absoluteUri, string? role, System.Type? ofObjectToReturn) { throw null; }
public virtual System.Uri ResolveUri(System.Uri? baseUri, string? relativeUri) { throw null; }
public virtual bool SupportsType(System.Uri absoluteUri, System.Type? type) { throw null; }
}
[System.ObsoleteAttribute("XmlSecureResolver is obsolete. Use XmlResolver.ThrowingResolver instead when attempting to forbid XML external entity resolution.", DiagnosticId = "SYSLIB0047", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")]
public partial class XmlSecureResolver : System.Xml.XmlResolver
{
public XmlSecureResolver(System.Xml.XmlResolver resolver, string? securityUrl) { }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册