diff --git a/eng/Versions.props b/eng/Versions.props
index b972545c2ba0e9..ecc5ef1c317a8b 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -136,6 +136,8 @@
5.0.0
5.0.0
7.0.0
+
+ 7.0.3
8.0.0-rc.1.23406.6
6.0.0
7.0.0
diff --git a/src/coreclr/debug/createdump/createdump.h b/src/coreclr/debug/createdump/createdump.h
index 50cf53dccfc7aa..baaa128c54f027 100644
--- a/src/coreclr/debug/createdump/createdump.h
+++ b/src/coreclr/debug/createdump/createdump.h
@@ -151,6 +151,9 @@ extern MINIDUMP_TYPE GetMiniDumpType(DumpType dumpType);
#ifdef HOST_WINDOWS
extern std::string GetLastErrorString();
+extern DWORD GetTempPathWrapper(IN DWORD nBufferLength, OUT LPSTR lpBuffer);
+#else
+#define GetTempPathWrapper GetTempPathA
#endif
extern void printf_status(const char* format, ...);
extern void printf_error(const char* format, ...);
diff --git a/src/coreclr/debug/createdump/createdumpmain.cpp b/src/coreclr/debug/createdump/createdumpmain.cpp
index e39538c4cb1c38..f2663169727fde 100644
--- a/src/coreclr/debug/createdump/createdumpmain.cpp
+++ b/src/coreclr/debug/createdump/createdumpmain.cpp
@@ -205,7 +205,7 @@ int createdump_main(const int argc, const char* argv[])
ArrayHolder tmpPath = new char[MAX_LONGPATH];
if (options.DumpPathTemplate == nullptr)
{
- if (::GetTempPathA(MAX_LONGPATH, tmpPath) == 0)
+ if (GetTempPathWrapper(MAX_LONGPATH, tmpPath) == 0)
{
printf_error("GetTempPath failed\n");
return -1;
diff --git a/src/coreclr/debug/createdump/createdumpwindows.cpp b/src/coreclr/debug/createdump/createdumpwindows.cpp
index d1b843f1a2fd2b..43e6f2c12bd521 100644
--- a/src/coreclr/debug/createdump/createdumpwindows.cpp
+++ b/src/coreclr/debug/createdump/createdumpwindows.cpp
@@ -135,3 +135,38 @@ GetLastErrorString()
return result;
}
+
+typedef DWORD(WINAPI *pfnGetTempPathA)(DWORD nBufferLength, LPSTR lpBuffer);
+
+static volatile pfnGetTempPathA
+g_pfnGetTempPathA = nullptr;
+
+
+DWORD
+GetTempPathWrapper(
+ IN DWORD nBufferLength,
+ OUT LPSTR lpBuffer)
+{
+ if (g_pfnGetTempPathA == nullptr)
+ {
+ HMODULE hKernel32 = LoadLibraryExW(L"kernel32.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+
+ pfnGetTempPathA pLocalGetTempPathA = NULL;
+ if (hKernel32 != NULL)
+ {
+ // store to thread local variable to prevent data race
+ pLocalGetTempPathA = (pfnGetTempPathA)::GetProcAddress(hKernel32, "GetTempPath2A");
+ }
+
+ if (pLocalGetTempPathA == NULL) // method is only available with Windows 10 Creators Update or later
+ {
+ g_pfnGetTempPathA = &GetTempPathA;
+ }
+ else
+ {
+ g_pfnGetTempPathA = pLocalGetTempPathA;
+ }
+ }
+
+ return g_pfnGetTempPathA(nBufferLength, lpBuffer);
+}
\ No newline at end of file
diff --git a/src/coreclr/inc/longfilepathwrappers.h b/src/coreclr/inc/longfilepathwrappers.h
index ee394aa0b77586..6407680900dc14 100644
--- a/src/coreclr/inc/longfilepathwrappers.h
+++ b/src/coreclr/inc/longfilepathwrappers.h
@@ -59,10 +59,6 @@ SearchPathWrapper(
_Out_opt_ LPWSTR * lpFilePart
);
-DWORD WINAPI GetTempPathWrapper(
- SString& lpBuffer
- );
-
DWORD
GetModuleFileNameWrapper(
_In_opt_ HMODULE hModule,
diff --git a/src/coreclr/inc/winwrap.h b/src/coreclr/inc/winwrap.h
index 652c0b80653310..98570ed3eac1d9 100644
--- a/src/coreclr/inc/winwrap.h
+++ b/src/coreclr/inc/winwrap.h
@@ -204,9 +204,6 @@
//Can not use extended syntax
#define WszGetFullPathName GetFullPathNameW
-//Long Files will not work on these till redstone
-#define WszGetTempPath GetTempPathWrapper
-
//APIS which have a buffer as an out parameter
#define WszGetEnvironmentVariable GetEnvironmentVariableWrapper
#define WszSearchPath SearchPathWrapper
diff --git a/src/coreclr/utilcode/longfilepathwrappers.cpp b/src/coreclr/utilcode/longfilepathwrappers.cpp
index 120b3c04c9322d..079fce749ae638 100644
--- a/src/coreclr/utilcode/longfilepathwrappers.cpp
+++ b/src/coreclr/utilcode/longfilepathwrappers.cpp
@@ -184,47 +184,6 @@ GetModuleFileNameWrapper(
return ret;
}
-DWORD WINAPI GetTempPathWrapper(
- SString& lpBuffer
- )
-{
- CONTRACTL
- {
- NOTHROW;
- }
- CONTRACTL_END;
-
- HRESULT hr = S_OK;
- DWORD ret = 0;
- DWORD lastError = 0;
-
- EX_TRY
- {
- //Change the behaviour in Redstone to retry
- COUNT_T size = MAX_LONGPATH;
-
- ret = GetTempPathW(
- size,
- lpBuffer.OpenUnicodeBuffer(size - 1)
- );
-
- lastError = GetLastError();
- lpBuffer.CloseBuffer(ret);
- }
- EX_CATCH_HRESULT(hr);
-
- if (hr != S_OK)
- {
- SetLastError(hr);
- }
- else if (ret == 0)
- {
- SetLastError(lastError);
- }
-
- return ret;
-}
-
DWORD WINAPI GetEnvironmentVariableWrapper(
_In_opt_ LPCTSTR lpName,
_Out_opt_ SString& lpBuffer
diff --git a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj
index de2e9ac36a7949..3eb55ac664eea2 100644
--- a/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj
+++ b/src/installer/managed/Microsoft.NET.HostModel/Microsoft.NET.HostModel.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs
index 63c7b451e142b4..57f3344c18954c 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/DSAKeyFormatHelper.cs
@@ -25,16 +25,6 @@ internal static void ReadDsaPrivateKey(
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
- DssParms parms = DssParms.Decode(algId.Parameters.Value, AsnEncodingRules.BER);
-
- ret = new DSAParameters
- {
- P = parms.P.ToByteArray(isUnsigned: true, isBigEndian: true),
- Q = parms.Q.ToByteArray(isUnsigned: true, isBigEndian: true),
- };
-
- ret.G = parms.G.ExportKeyParameter(ret.P.Length);
-
BigInteger x;
try
@@ -57,6 +47,34 @@ internal static void ReadDsaPrivateKey(
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
+ DssParms parms = DssParms.Decode(algId.Parameters.Value, AsnEncodingRules.BER);
+
+ // Sanity checks from FIPS 186-4 4.1/4.2. Since FIPS 186-5 withdrew DSA/DSS
+ // these will never change again.
+ //
+ // This technically allows a non-standard combination of 1024-bit P and 256-bit Q,
+ // but that will get filtered out by the underlying provider.
+ // These checks just prevent obviously bad data from wasting work on reinterpretation.
+
+ if (parms.P.Sign < 0 ||
+ parms.Q.Sign < 0 ||
+ !IsValidPLength(parms.P.GetBitLength()) ||
+ !IsValidQLength(parms.Q.GetBitLength()) ||
+ parms.G <= 1 ||
+ parms.G >= parms.P ||
+ x <= 1 ||
+ x >= parms.Q)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ ret = new DSAParameters
+ {
+ P = parms.P.ToByteArray(isUnsigned: true, isBigEndian: true),
+ Q = parms.Q.ToByteArray(isUnsigned: true, isBigEndian: true),
+ };
+
+ ret.G = parms.G.ExportKeyParameter(ret.P.Length);
ret.X = x.ExportKeyParameter(ret.Q.Length);
// The public key is not contained within the format, calculate it.
@@ -69,6 +87,11 @@ internal static void ReadDsaPublicKey(
in AlgorithmIdentifierAsn algId,
out DSAParameters ret)
{
+ if (!algId.Parameters.HasValue)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
BigInteger y;
try
@@ -88,13 +111,27 @@ internal static void ReadDsaPublicKey(
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
}
- if (!algId.Parameters.HasValue)
+ DssParms parms = DssParms.Decode(algId.Parameters.Value, AsnEncodingRules.BER);
+
+ // Sanity checks from FIPS 186-4 4.1/4.2. Since FIPS 186-5 withdrew DSA/DSS
+ // these will never change again.
+ //
+ // This technically allows a non-standard combination of 1024-bit P and 256-bit Q,
+ // but that will get filtered out by the underlying provider.
+ // These checks just prevent obviously bad data from wasting work on reinterpretation.
+
+ if (parms.P.Sign < 0 ||
+ parms.Q.Sign < 0 ||
+ !IsValidPLength(parms.P.GetBitLength()) ||
+ !IsValidQLength(parms.Q.GetBitLength()) ||
+ parms.G <= 1 ||
+ parms.G >= parms.P ||
+ y <= 1 ||
+ y >= parms.P)
{
throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
}
- DssParms parms = DssParms.Decode(algId.Parameters.Value, AsnEncodingRules.BER);
-
ret = new DSAParameters
{
P = parms.P.ToByteArray(isUnsigned: true, isBigEndian: true),
@@ -105,6 +142,25 @@ internal static void ReadDsaPublicKey(
ret.Y = y.ExportKeyParameter(ret.P.Length);
}
+ private static bool IsValidPLength(long pBitLength)
+ {
+ return pBitLength switch
+ {
+ // FIPS 186-3/186-4
+ 1024 or 2048 or 3072 => true,
+ // FIPS 186-1/186-2
+ >= 512 and < 1024 => pBitLength % 64 == 0,
+ _ => false,
+ };
+ }
+
+ private static bool IsValidQLength(long qBitLength)
+ {
+ // FIPS 186-1/186-2 only allows 160
+ // FIPS 186-3/186-4 allow 160/224/256
+ return qBitLength is 160 or 224 or 256;
+ }
+
internal static void ReadSubjectPublicKeyInfo(
ReadOnlySpan source,
out int bytesRead,
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
index 980a8a9194c83e..3d655f77ea96b1 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyFileTests.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Formats.Asn1;
+using System.Numerics;
using System.Security.Cryptography.Encryption.RC2.Tests;
using System.Text;
using Test.Cryptography;
@@ -302,6 +304,150 @@ public static void ReadWriteDsa2048SubjectPublicKeyInfo()
DSATestData.GetDSA2048Params());
}
+ [Fact]
+ [SkipOnPlatform(TestPlatforms.OSX, "DSASecurityTransforms goes straight to OS, has different failure mode")]
+ public static void ImportNonsensePublicParameters()
+ {
+ AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
+
+ DSAParameters validParameters = DSATestData.GetDSA2048Params();
+ BigInteger p = new BigInteger(validParameters.P, true, true);
+ BigInteger q = new BigInteger(validParameters.Q, true, true);
+ BigInteger g = new BigInteger(validParameters.G, true, true);
+ BigInteger y = new BigInteger(validParameters.Y, true, true);
+
+ using (DSA dsa = DSAFactory.Create())
+ {
+ // 1 < y < p, 1 < g < p, q is 160/224/256 bits
+ // p is 512..1024 % 64, or 1024/2048/3072 bits
+ ImportSPKI(dsa, p, q, g, p, writer);
+ ImportSPKI(dsa, p, q, g, BigInteger.One, writer);
+ ImportSPKI(dsa, p, q, g, BigInteger.MinusOne, writer);
+ ImportSPKI(dsa, p, q, p, y, writer);
+ ImportSPKI(dsa, p, q, -g, y, writer);
+ ImportSPKI(dsa, p, q, BigInteger.One, y, writer);
+ ImportSPKI(dsa, p, q, BigInteger.MinusOne, y, writer);
+ ImportSPKI(dsa, p, q << 1, g, y, writer);
+ ImportSPKI(dsa, p, q >> 1, g, y, writer);
+ ImportSPKI(dsa, p, -q, g, y, writer);
+ ImportSPKI(dsa, p >> 1, q, g, y, writer);
+ ImportSPKI(dsa, p << 1, q, g, y, writer);
+ ImportSPKI(dsa, BigInteger.One << 4095, q, 2, 97, writer);
+ }
+
+ static void ImportSPKI(
+ DSA key,
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ BigInteger y,
+ AsnWriter writer)
+ {
+ writer.Reset();
+ writer.WriteInteger(y);
+ byte[] encodedPublicKey = writer.Encode();
+ writer.Reset();
+
+ using (writer.PushSequence())
+ {
+ using (writer.PushSequence())
+ {
+ writer.WriteObjectIdentifier("1.2.840.10040.4.1");
+
+ using (writer.PushSequence())
+ {
+ writer.WriteInteger(p);
+ writer.WriteInteger(q);
+ writer.WriteInteger(g);
+ }
+ }
+
+ writer.WriteBitString(encodedPublicKey);
+ }
+
+ byte[] spki = writer.Encode();
+ writer.Reset();
+
+ AssertExtensions.ThrowsContains(
+ () => key.ImportSubjectPublicKeyInfo(spki, out _),
+ "corrupted");
+ }
+ }
+
+ [Fact]
+ public static void ImportNonsensePrivateParameters()
+ {
+ AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
+
+ DSAParameters validParameters = DSATestData.GetDSA2048Params();
+ BigInteger p = new BigInteger(validParameters.P, true, true);
+ BigInteger q = new BigInteger(validParameters.Q, true, true);
+ BigInteger g = new BigInteger(validParameters.G, true, true);
+ BigInteger x = new BigInteger(validParameters.X, true, true);
+
+ using (DSA dsa = DSAFactory.Create())
+ {
+ // 1 < x < q, 1 < g < p, q is 160/224/256 bits
+ // p is 512..1024 % 64, or 1024/2048/3072 bits
+ ImportPkcs8(dsa, p, q, g, q, writer);
+ ImportPkcs8(dsa, p, q, g, BigInteger.One, writer);
+ // x = -1 gets re-interpreted as x = 255 because of a CAPI compat issue.
+ //ImportPkcs8(dsa, p, q, g, BigInteger.MinusOne, writer);
+ ImportPkcs8(dsa, p, q, g, -x, writer);
+ ImportPkcs8(dsa, p, q, p, x, writer);
+ ImportPkcs8(dsa, p, q, -g, x, writer);
+ ImportPkcs8(dsa, p, q, BigInteger.One, x, writer);
+ ImportPkcs8(dsa, p, q, BigInteger.MinusOne, x, writer);
+ ImportPkcs8(dsa, p, q << 1, g, x, writer);
+ ImportPkcs8(dsa, p, q >> 1, g, x, writer);
+ ImportPkcs8(dsa, p >> 1, q, g, x, writer);
+ ImportPkcs8(dsa, p << 1, q, g, x, writer);
+ ImportPkcs8(dsa, -q, q, g, x, writer);
+ ImportPkcs8(dsa, BigInteger.One << 4095, q, 2, 97, writer);
+ ImportPkcs8(dsa, -p, q, g, x, writer);
+ }
+
+ static void ImportPkcs8(
+ DSA key,
+ BigInteger p,
+ BigInteger q,
+ BigInteger g,
+ BigInteger x,
+ AsnWriter writer)
+ {
+ writer.Reset();
+
+ using (writer.PushSequence())
+ {
+ writer.WriteInteger(0);
+
+ using (writer.PushSequence())
+ {
+ writer.WriteObjectIdentifier("1.2.840.10040.4.1");
+
+ using (writer.PushSequence())
+ {
+ writer.WriteInteger(p);
+ writer.WriteInteger(q);
+ writer.WriteInteger(g);
+ }
+ }
+
+ using (writer.PushOctetString())
+ {
+ writer.WriteInteger(x);
+ }
+ }
+
+ byte[] pkcs8 = writer.Encode();
+ writer.Reset();
+
+ AssertExtensions.ThrowsContains(
+ () => key.ImportPkcs8PrivateKey(pkcs8, out _),
+ "corrupted");
+ }
+ }
+
[Fact]
public static void NoFuzzySubjectPublicKeyInfo()
{
diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj
index 6cd3d26b2b6f63..9cb379a7ad24d8 100644
--- a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj
+++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj
@@ -3,6 +3,8 @@
$(NetCoreAppCurrent);$(NetCoreAppPrevious);$(NetCoreAppMinimum);netstandard2.0;$(NetFrameworkMinimum)
true
true
+ true
+ 1
Provides abstractions for reading `.deps` files. When a .NET application is compiled, the SDK generates a JSON manifest file (`<ApplicationName>.deps.json`) that contains information about application dependencies. You can use `Microsoft.Extensions.DependencyModel` to read information from this manifest at run time. This is useful when you want to dynamically compile code (for example, using Roslyn Emit API) referencing the same dependencies as your main application.
By default, the dependency manifest contains information about the application's target framework and runtime dependencies. Set the PreserveCompilationContext project property to `true` to additionally include information about reference assemblies used during compilation.
diff --git a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs
index 2e28b0cb6ac3df..de5a86a64ac8c0 100644
--- a/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs
+++ b/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/EmailAddressAttribute.cs
@@ -7,6 +7,9 @@ namespace System.ComponentModel.DataAnnotations
AllowMultiple = false)]
public sealed class EmailAddressAttribute : DataTypeAttribute
{
+ private static bool EnableFullDomainLiterals { get; } =
+ AppContext.TryGetSwitch("System.Net.AllowFullDomainLiterals", out bool enable) ? enable : false;
+
public EmailAddressAttribute()
: base(DataType.EmailAddress)
{
@@ -27,6 +30,11 @@ public override bool IsValid(object? value)
return false;
}
+ if (!EnableFullDomainLiterals && (valueAsString.Contains('\r') || valueAsString.Contains('\n')))
+ {
+ return false;
+ }
+
// only return true if there is only 1 '@' character
// and it is neither the first nor the last character
int index = valueAsString.IndexOf('@');
diff --git a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs
index a0c67ed7d2b92b..da80016608e402 100644
--- a/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs
+++ b/src/libraries/System.ComponentModel.Annotations/tests/System/ComponentModel/DataAnnotations/EmailAddressAttributeTests.cs
@@ -29,6 +29,7 @@ protected override IEnumerable InvalidValues()
yield return new TestCase(new EmailAddressAttribute(), 0);
yield return new TestCase(new EmailAddressAttribute(), "");
yield return new TestCase(new EmailAddressAttribute(), " \r \t \n" );
+ yield return new TestCase(new EmailAddressAttribute(), "someName@[\r\n\tsomeDomain]");
yield return new TestCase(new EmailAddressAttribute(), "@someDomain.com");
yield return new TestCase(new EmailAddressAttribute(), "@someDomain@abc.com");
yield return new TestCase(new EmailAddressAttribute(), "someName");
diff --git a/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx
index 3b028a6f3c90d5..c05cd4bee55a45 100644
--- a/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx
+++ b/src/libraries/System.Formats.Asn1/src/Resources/Strings.resx
@@ -144,6 +144,9 @@
The encoded named bit list value is larger than the value size of the '{0}' enum.
+
+ The encoded object identifier (OID) exceeds the limits supported by this library. Supported OIDs are limited to 64 arcs and each subidentifier is limited to a 128-bit value.
+
The encoded value uses a constructed encoding, which is invalid for '{0}' values.
diff --git a/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj
index a899353cd4a050..f8831d6ee17e0a 100644
--- a/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj
+++ b/src/libraries/System.Formats.Asn1/src/System.Formats.Asn1.csproj
@@ -4,6 +4,8 @@
true
$(DefineConstants);CP_NO_ZEROMEMORY
true
+ true
+ 1
Provides classes that can read and write the ASN.1 BER, CER, and DER data formats.
Commonly Used Types:
@@ -12,6 +14,9 @@ System.Formats.Asn1.AsnWriter
+
+ Common\System\LocalAppContextSwitches.Common.cs
+
Common\System\Security\Cryptography\CryptoPool.cs
@@ -49,6 +54,7 @@ System.Formats.Asn1.AsnWriter
+
diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs
index fac90e12aec1c2..e46f04b6f308ad 100644
--- a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs
+++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Oid.cs
@@ -90,15 +90,55 @@ private static void ReadSubIdentifier(
throw new AsnContentException();
}
+ // Set semanticBits to a value such that on the first
+ // iteration of the loop it becomes the correct value.
+ // So each entry here is [real semantic bits for this value] - 7.
+ int semanticBits = source[0] switch
+ {
+ >= 0b1100_0000 => 0,
+ >= 0b1010_0000 => -1,
+ >= 0b1001_0000 => -2,
+ >= 0b1000_1000 => -3,
+ >= 0b1000_0100 => -4,
+ >= 0b1000_0010 => -5,
+ >= 0b1000_0001 => -6,
+ _ => 0,
+ };
+
// First, see how long the segment is
int end = -1;
int idx;
+ // None of T-REC-X.660-201107, T-REC-X.680-201508, or T-REC-X.690-201508
+ // have any recommendations for a minimum (or maximum) size of a
+ // sub-identifier.
+ //
+ // T-REC-X.667-201210 (and earlier versions) discuss the no-registration-
+ // required UUID space at 2.25.{UUID}, where UUIDs are defined as 128-bit
+ // values. This gives us a minimum lower bound of 128-bit.
+ //
+ // Windows Crypt32 has historically only supported 64-bit values, and
+ // the "size limitations" FAQ on oid-info.com says that the largest arc
+ // value is a 39-digit value that corresponds to a 2.25.UUID value.
+ //
+ // So, until something argues for a bigger number, our bit-limit is 128.
+ const int MaxAllowedBits = 128;
+
for (idx = 0; idx < source.Length; idx++)
{
// If the high bit isn't set this marks the end of the sub-identifier.
bool endOfIdentifier = (source[idx] & 0x80) == 0;
+ if (!LocalAppContextSwitches.AllowAnySizeOid)
+ {
+ semanticBits += 7;
+
+ if (semanticBits > MaxAllowedBits)
+ {
+ throw new AsnContentException(SR.ContentException_OidTooBig);
+ }
+ }
+
if (endOfIdentifier)
{
end = idx;
@@ -265,8 +305,21 @@ private static string ReadObjectIdentifier(ReadOnlySpan contents)
contents = contents.Slice(bytesRead);
+ const int MaxArcs = 64;
+ int remainingArcs = MaxArcs - 2;
+
while (!contents.IsEmpty)
{
+ if (!LocalAppContextSwitches.AllowAnySizeOid)
+ {
+ if (remainingArcs <= 0)
+ {
+ throw new AsnContentException(SR.ContentException_OidTooBig);
+ }
+
+ remainingArcs--;
+ }
+
ReadSubIdentifier(contents, out bytesRead, out smallValue, out largeValue);
// Exactly one should be non-null.
Debug.Assert((smallValue == null) != (largeValue == null));
diff --git a/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/LocalAppContextSwitches.cs b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/LocalAppContextSwitches.cs
new file mode 100644
index 00000000000000..62722d7f4d0008
--- /dev/null
+++ b/src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/LocalAppContextSwitches.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.CompilerServices;
+
+namespace System
+{
+ internal static partial class LocalAppContextSwitches
+ {
+ private static int s_allowAnySizeOid;
+ public static bool AllowAnySizeOid
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetCachedSwitchValue("System.Formats.Asn1.AllowAnySizeOid", ref s_allowAnySizeOid);
+ }
+ }
+}
diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs
index c1add074220a06..6584fd47b8f6ba 100644
--- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs
+++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadObjectIdentifier.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text;
+using Microsoft.DotNet.RemoteExecutor;
using Test.Cryptography;
using Xunit;
@@ -198,70 +199,188 @@ public static void ExpectedTag_IgnoresConstructed(
[InlineData(AsnEncodingRules.BER)]
[InlineData(AsnEncodingRules.CER)]
[InlineData(AsnEncodingRules.DER)]
- public static void ReadVeryLongOid(AsnEncodingRules ruleSet)
+ public static void ReadMaximumArcOid(AsnEncodingRules ruleSet)
{
- byte[] inputData = new byte[100000];
- // 06 83 02 00 00 (OBJECT IDENTIFIER, 65536 bytes).
- inputData[0] = 0x06;
- inputData[1] = 0x83;
- inputData[2] = 0x01;
- inputData[3] = 0x00;
- inputData[4] = 0x00;
- // and the rest are all zero.
-
- // The first byte produces "0.0". Each of the remaining 65535 bytes produce
- // another ".0".
- const int ExpectedLength = 65536 * 2 + 1;
- StringBuilder builder = new StringBuilder(ExpectedLength);
- builder.Append('0');
-
- for (int i = 0; i <= ushort.MaxValue; i++)
+ const int MaxArcs = 64;
+ // MaxArcs content bytes (all 0x7F) (which includes one for failure), plus one for the tag
+ // plus one for the encoded length.
+ byte[] input = new byte[MaxArcs + 2];
+ input.AsSpan().Fill(0x7F);
+ input[0] = 0x06;
+ // The first two arcs are encoded in the first sub-identifier, so MaxArcs - 1.
+ input[1] = MaxArcs - 1;
+
+ string decoded = AsnDecoder.ReadObjectIdentifier(input, ruleSet, out int consumed);
+ Assert.Equal(input.Length - 1, consumed);
+
+ StringBuilder expected = new StringBuilder(4 * MaxArcs);
+ expected.Append("2.47");
+
+ for (int i = 2; i < MaxArcs; i++)
{
- builder.Append('.');
- builder.Append(0);
+ expected.Append(".127");
}
- AsnReader reader = new AsnReader(inputData, ruleSet);
- string oidString = reader.ReadObjectIdentifier();
+ Assert.Equal(expected.ToString(), decoded);
- Assert.Equal(ExpectedLength, oidString.Length);
- Assert.Equal(builder.ToString(), oidString);
+ input[1] = MaxArcs;
+ AsnContentException ex = Assert.Throws(
+ () => AsnDecoder.ReadObjectIdentifier(input, ruleSet, out _));
+ Assert.Contains("OID", ex.Message);
}
[Theory]
[InlineData(AsnEncodingRules.BER)]
[InlineData(AsnEncodingRules.CER)]
[InlineData(AsnEncodingRules.DER)]
- public static void ReadVeryLongOidArc(AsnEncodingRules ruleSet)
+ public static void ReadMaximumInitialSubIdentifier(AsnEncodingRules ruleSet)
{
- byte[] inputData = new byte[255];
- // 06 81 93 (OBJECT IDENTIFIER, 147 bytes).
- inputData[0] = 0x06;
- inputData[1] = 0x81;
- inputData[2] = 0x93;
-
- // With 147 bytes we get 147*7 = 1029 value bits.
- // The smallest legal number to encode would have a top byte of 0x81,
- // leaving 1022 bits remaining. If they're all zero then we have 2^1022.
- //
- // Since it's our first sub-identifier it's really encoding "2.(2^1022 - 80)".
- inputData[3] = 0x81;
- // Leave the last byte as 0.
- new Span(inputData, 4, 145).Fill(0x80);
-
- const string ExpectedOid =
- "2." +
- "449423283715578976932326297697256183404494244735576643183575" +
- "202894331689513752407831771193306018840052800284699678483394" +
- "146974422036041556232118576598685310944419733562163713190755" +
- "549003115235298632707380212514422095376705856157203684782776" +
- "352068092908376276711465745599868114846199290762088390824060" +
- "56034224";
+ // First sub-identifier is 2^128 - 1, second is 1
+ byte[] valid =
+ {
+ 0x06, 0x14, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x01,
+ };
- AsnReader reader = new AsnReader(inputData, ruleSet);
+ // First sub-identifier is 2^128, second is 1
+ byte[] invalid =
+ {
+ 0x06, 0x14, 0x84, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x00, 0x01,
+ };
+
+ string oid = AsnDecoder.ReadObjectIdentifier(valid, ruleSet, out int consumed);
+ Assert.Equal(valid.Length, consumed);
+ Assert.Equal("2.340282366920938463463374607431768211375.1", oid);
+
+ AsnContentException ex = Assert.Throws(
+ () => AsnDecoder.ReadObjectIdentifier(invalid, ruleSet, out _));
+ Assert.Contains("OID", ex.Message);
+ }
- string oidString = reader.ReadObjectIdentifier();
- Assert.Equal(ExpectedOid, oidString);
+ [Theory]
+ [InlineData(AsnEncodingRules.BER)]
+ [InlineData(AsnEncodingRules.CER)]
+ [InlineData(AsnEncodingRules.DER)]
+ public static void ReadMaximumNonInitialSubIdentifier(AsnEncodingRules ruleSet)
+ {
+ // First sub-identifier is 1, second is 2^128 - 1
+ byte[] valid =
+ {
+ 0x06, 0x14, 0x01, 0x83, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
+ };
+
+ // First sub-identifier is 1, second is 2^128
+ byte[] invalid = new byte[]
+ {
+ 0x06, 0x14, 0x01, 0x84, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+ 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
+ };
+
+ string oid = AsnDecoder.ReadObjectIdentifier(valid, ruleSet, out int consumed);
+ Assert.Equal(valid.Length, consumed);
+ Assert.Equal("0.1.340282366920938463463374607431768211455", oid);
+
+ AsnContentException ex = Assert.Throws(
+ () => AsnDecoder.ReadObjectIdentifier(invalid, ruleSet, out _));
+ Assert.Contains("OID", ex.Message);
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void ReadVeryLongOid_WithAppContext()
+ {
+ RemoteExecutor.Invoke(
+ static () =>
+ {
+ AppContext.SetSwitch("System.Formats.Asn1.AllowAnySizeOid", true);
+
+ byte[] inputData = new byte[100000];
+ // 06 83 02 00 00 (OBJECT IDENTIFIER, 65536 bytes).
+ inputData[0] = 0x06;
+ inputData[1] = 0x83;
+ inputData[2] = 0x01;
+ inputData[3] = 0x00;
+ inputData[4] = 0x00;
+ // and the rest are all zero.
+
+ // The first byte produces "0.0". Each of the remaining 65535 bytes produce
+ // another ".0".
+ const int ExpectedLength = 65536 * 2 + 1;
+ StringBuilder builder = new StringBuilder(ExpectedLength);
+ builder.Append('0');
+
+ for (int i = 0; i <= ushort.MaxValue; i++)
+ {
+ builder.Append('.');
+ builder.Append(0);
+ }
+
+ AsnEncodingRules[] ruleSets =
+ {
+ AsnEncodingRules.BER,
+ AsnEncodingRules.CER,
+ AsnEncodingRules.DER,
+ };
+
+ foreach (AsnEncodingRules ruleSet in ruleSets)
+ {
+ AsnReader reader = new AsnReader(inputData, ruleSet);
+ string oidString = reader.ReadObjectIdentifier();
+
+ Assert.Equal(ExpectedLength, oidString.Length);
+ Assert.Equal(builder.ToString(), oidString);
+ }
+ }).Dispose();
+ }
+
+ [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ public static void ReadVeryLongOidArc_WithAppContext()
+ {
+ RemoteExecutor.Invoke(
+ static () =>
+ {
+ AppContext.SetSwitch("System.Formats.Asn1.AllowAnySizeOid", true);
+
+ byte[] inputData = new byte[255];
+ // 06 81 93 (OBJECT IDENTIFIER, 147 bytes).
+ inputData[0] = 0x06;
+ inputData[1] = 0x81;
+ inputData[2] = 0x93;
+
+ // With 147 bytes we get 147*7 = 1029 value bits.
+ // The smallest legal number to encode would have a top byte of 0x81,
+ // leaving 1022 bits remaining. If they're all zero then we have 2^1022.
+ //
+ // Since it's our first sub-identifier it's really encoding "2.(2^1022 - 80)".
+ inputData[3] = 0x81;
+ // Leave the last byte as 0.
+ new Span(inputData, 4, 145).Fill(0x80);
+
+ const string ExpectedOid =
+ "2." +
+ "449423283715578976932326297697256183404494244735576643183575" +
+ "202894331689513752407831771193306018840052800284699678483394" +
+ "146974422036041556232118576598685310944419733562163713190755" +
+ "549003115235298632707380212514422095376705856157203684782776" +
+ "352068092908376276711465745599868114846199290762088390824060" +
+ "56034224";
+
+ AsnEncodingRules[] ruleSets =
+ {
+ AsnEncodingRules.BER,
+ AsnEncodingRules.CER,
+ AsnEncodingRules.DER,
+ };
+
+ foreach (AsnEncodingRules ruleSet in ruleSets)
+ {
+ AsnReader reader = new AsnReader(inputData, ruleSet);
+
+ string oidString = reader.ReadObjectIdentifier();
+ Assert.Equal(ExpectedOid, oidString);
+ }
+ }).Dispose();
}
}
}
diff --git a/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj
index 68bdc72edf895f..83d8041222f6c9 100644
--- a/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj
+++ b/src/libraries/System.Formats.Asn1/tests/System.Formats.Asn1.Tests.csproj
@@ -2,6 +2,7 @@
true
$(NetCoreAppCurrent);$(NetFrameworkMinimum)
+ true
diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs
index 47ac65dbf9b5d4..644f7502ff7763 100644
--- a/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs
+++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs
@@ -15,6 +15,9 @@ namespace System.Net.Mail
//
public partial class MailAddress
{
+ private static bool EnableFullDomainLiterals { get; } =
+ AppContext.TryGetSwitch("System.Net.AllowFullDomainLiterals", out bool enable) ? enable : false;
+
// These components form an e-mail address when assembled as follows:
// "EncodedDisplayname"
private readonly Encoding _displayNameEncoding;
@@ -216,6 +219,12 @@ private string GetHost(bool allowUnicode)
throw new SmtpException(SR.Format(SR.SmtpInvalidHostName, Address), argEx);
}
}
+
+ if (!EnableFullDomainLiterals && domain.AsSpan().IndexOfAny('\r', '\n') >= 0)
+ {
+ throw new SmtpException(SR.Format(SR.SmtpInvalidHostName, Address));
+ }
+
return domain;
}
diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs
index 306368df202183..a9cf0bca251ce8 100644
--- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs
+++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs
@@ -9,11 +9,15 @@
// (C) 2006 John Luke
//
+using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Net.NetworkInformation;
using System.Net.Sockets;
+using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.DotNet.RemoteExecutor;
using Systen.Net.Mail.Tests;
using System.Net.Test.Common;
using Xunit;
@@ -573,5 +577,60 @@ public void TestGssapiAuthentication()
Assert.Equal("GSSAPI", server.AuthMethodUsed, StringComparer.OrdinalIgnoreCase);
}
+
+ [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
+ [InlineData("foo@[\r\n bar]")]
+ [InlineData("foo@[bar\r\n ]")]
+ [InlineData("foo@[bar\r\n baz]")]
+ public void MultiLineDomainLiterals_Enabled_Success(string input)
+ {
+ RemoteExecutor.Invoke(static (string @input) =>
+ {
+ AppContext.SetSwitch("System.Net.AllowFullDomainLiterals", true);
+
+ var address = new MailAddress(@input);
+
+ // Using address with new line breaks the protocol so we cannot easily use LoopbackSmtpServer
+ // Instead we call internal method that does the extra validation.
+ string? host = (string?)typeof(MailAddress).InvokeMember("GetAddress", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, address, new object[] { true });
+ Assert.Equal(input, host);
+ }, input).Dispose();
+ }
+
+ [Theory]
+ [MemberData(nameof(SendMail_MultiLineDomainLiterals_Data))]
+ public async Task SendMail_MultiLineDomainLiterals_Disabled_Throws(string from, string to, bool asyncSend)
+ {
+ using var server = new LoopbackSmtpServer();
+
+ using SmtpClient client = server.CreateClient();
+ client.Credentials = new NetworkCredential("Foo", "Bar");
+
+ using var msg = new MailMessage(@from, @to, "subject", "body");
+
+ await Assert.ThrowsAsync(async () =>
+ {
+ if (asyncSend)
+ {
+ await client.SendMailAsync(msg).WaitAsync(TimeSpan.FromSeconds(30));
+ }
+ else
+ {
+ client.Send(msg);
+ }
+ });
+ }
+
+ public static IEnumerable