diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln index 6cdeaa1464..7718ccf9e7 100644 --- a/KestrelHttpServer.sln +++ b/KestrelHttpServer.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27101.0 +VisualStudioVersion = 15.0.27110.0 MinimumVisualStudioVersion = 15.0.26730.03 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}" ProjectSection(SolutionItems) = preProject @@ -102,8 +102,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Protocols.Abstractions", "src\Protocols.Abstractions\Protocols.Abstractions.csproj", "{6956CF5C-3163-4398-8628-4ECA569245B5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Tls", "src\Kestrel.Tls\Kestrel.Tls.csproj", "{924AE57C-1EBA-4A1D-A039-8C100B7507A5}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{C2910A13-B2C2-46D8-81D8-7E166F4F5981}" ProjectSection(SolutionItems) = preProject build\repo.props = build\repo.props @@ -301,18 +299,6 @@ Global {6956CF5C-3163-4398-8628-4ECA569245B5}.Release|x64.Build.0 = Release|Any CPU {6956CF5C-3163-4398-8628-4ECA569245B5}.Release|x86.ActiveCfg = Release|Any CPU {6956CF5C-3163-4398-8628-4ECA569245B5}.Release|x86.Build.0 = Release|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Debug|x64.ActiveCfg = Debug|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Debug|x64.Build.0 = Debug|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Debug|x86.ActiveCfg = Debug|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Debug|x86.Build.0 = Debug|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|Any CPU.Build.0 = Release|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x64.ActiveCfg = Release|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x64.Build.0 = Release|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x86.ActiveCfg = Release|Any CPU - {924AE57C-1EBA-4A1D-A039-8C100B7507A5}.Release|x86.Build.0 = Release|Any CPU {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|Any CPU.Build.0 = Debug|Any CPU {74032D79-8EA7-4483-BD82-C38370420FFF}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -369,7 +355,6 @@ Global {D95A7EC3-48AC-4D03-B2E2-0DA3E13BD3A4} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {4F1C30F8-CCAA-48D7-9DF6-2A84021F5BCC} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {6956CF5C-3163-4398-8628-4ECA569245B5} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} - {924AE57C-1EBA-4A1D-A039-8C100B7507A5} = {2D5D5227-4DBD-499A-96B1-76A36B03B750} {B7B0EA74-528F-46B8-9BC4-909D9A67C194} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {74032D79-8EA7-4483-BD82-C38370420FFF} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} {9C7B6B5F-088A-436E-834B-6373EA36DEEE} = {D3273454-EA07-41D2-BF0B-FCC3675C2483} diff --git a/samples/Http2SampleApp/Http2SampleApp.csproj b/samples/Http2SampleApp/Http2SampleApp.csproj index 0fd184ca8d..683e6e4041 100644 --- a/samples/Http2SampleApp/Http2SampleApp.csproj +++ b/samples/Http2SampleApp/Http2SampleApp.csproj @@ -1,13 +1,13 @@ - netcoreapp2.0 + netcoreapp2.1 false + true - diff --git a/samples/Http2SampleApp/Program.cs b/samples/Http2SampleApp/Program.cs index 5ee9cbda2b..bdb49b0a50 100644 --- a/samples/Http2SampleApp/Program.cs +++ b/samples/Http2SampleApp/Program.cs @@ -30,7 +30,7 @@ namespace Http2SampleApp options.Listen(IPAddress.Any, basePort, listenOptions => { listenOptions.Protocols = HttpProtocols.Http1AndHttp2; - listenOptions.UseTls("testCert.pfx", "testPassword"); + listenOptions.UseHttps("testCert.pfx", "testPassword"); listenOptions.UseConnectionLogging(); }); }) diff --git a/src/Kestrel.Https/HttpsConnectionAdapterOptions.cs b/src/Kestrel.Https/HttpsConnectionAdapterOptions.cs index 39b9f9615a..14238658d0 100644 --- a/src/Kestrel.Https/HttpsConnectionAdapterOptions.cs +++ b/src/Kestrel.Https/HttpsConnectionAdapterOptions.cs @@ -6,6 +6,7 @@ using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Threading; +using Microsoft.AspNetCore.Server.Kestrel.Core; namespace Microsoft.AspNetCore.Server.Kestrel.Https { @@ -51,6 +52,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https /// public SslProtocols SslProtocols { get; set; } + /// + /// The protocols enabled on this endpoint. + /// + /// Defaults to HTTP/1.x only. + internal HttpProtocols HttpProtocols { get; set; } + /// /// Specifies whether the certificate revocation list is checked during authentication. /// diff --git a/src/Kestrel.Https/Internal/HttpsConnectionAdapter.cs b/src/Kestrel.Https/Internal/HttpsConnectionAdapter.cs index 290bb3beee..853f81966a 100644 --- a/src/Kestrel.Https/Internal/HttpsConnectionAdapter.cs +++ b/src/Kestrel.Https/Internal/HttpsConnectionAdapter.cs @@ -2,12 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Security; using System.Security.Cryptography.X509Certificates; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.Extensions.Logging; @@ -64,6 +67,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal { SslStream sslStream; bool certificateRequired; + var feature = new TlsConnectionFeature(); + context.Features.Set(feature); if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate) { @@ -114,8 +119,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal try { +#if NETCOREAPP2_1 + var sslOptions = new SslServerAuthenticationOptions() + { + ServerCertificate = _serverCertificate, + ClientCertificateRequired = certificateRequired, + EnabledSslProtocols = _options.SslProtocols, + CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, + ApplicationProtocols = new List() + }; + + // This is order sensitive + if ((_options.HttpProtocols & HttpProtocols.Http2) != 0) + { + sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2); + } + + if ((_options.HttpProtocols & HttpProtocols.Http1) != 0) + { + sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11); + } + + await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None); +#else await sslStream.AuthenticateAsServerAsync(_serverCertificate, certificateRequired, _options.SslProtocols, _options.CheckCertificateRevocation); +#endif } catch (OperationCanceledException) { @@ -134,11 +163,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal timeoutFeature.CancelTimeout(); } - // Always set the feature even though the cert might be null - context.Features.Set(new TlsConnectionFeature +#if NETCOREAPP2_1 + // Don't allocate in the common case, see https://github.com/dotnet/corefx/issues/25432 + if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http11) { - ClientCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate) - }); + feature.ApplicationProtocol = "http/1.1"; + } + else if (sslStream.NegotiatedApplicationProtocol == SslApplicationProtocol.Http2) + { + feature.ApplicationProtocol = "h2"; + } + else + { + feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.ToString(); + } + + context.Features.Set(feature); +#endif + feature.ClientCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate); return new HttpsAdaptedConnection(sslStream); } diff --git a/src/Kestrel.Https/Internal/TlsConnectionFeature.cs b/src/Kestrel.Https/Internal/TlsConnectionFeature.cs index 2fb85037b5..d5d7983d8e 100644 --- a/src/Kestrel.Https/Internal/TlsConnectionFeature.cs +++ b/src/Kestrel.Https/Internal/TlsConnectionFeature.cs @@ -6,13 +6,16 @@ using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core.Features; namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal { - internal class TlsConnectionFeature : ITlsConnectionFeature + internal class TlsConnectionFeature : ITlsConnectionFeature, ITlsApplicationProtocolFeature { public X509Certificate2 ClientCertificate { get; set; } + public string ApplicationProtocol { get; set; } + public Task GetClientCertificateAsync(CancellationToken cancellationToken) { return Task.FromResult(ClientCertificate); diff --git a/src/Kestrel.Https/Kestrel.Https.csproj b/src/Kestrel.Https/Kestrel.Https.csproj index de626b50b9..d9620b513e 100644 --- a/src/Kestrel.Https/Kestrel.Https.csproj +++ b/src/Kestrel.Https/Kestrel.Https.csproj @@ -4,7 +4,7 @@ Microsoft.AspNetCore.Server.Kestrel.Https Microsoft.AspNetCore.Server.Kestrel.Https HTTPS support for the ASP.NET Core Kestrel cross-platform web server. - netstandard2.0 + netstandard2.0;netcoreapp2.1 true aspnetcore;kestrel CS1591;$(NoWarn) diff --git a/src/Kestrel.Https/ListenOptionsHttpsExtensions.cs b/src/Kestrel.Https/ListenOptionsHttpsExtensions.cs index 44463ea10e..68f2e71ef1 100644 --- a/src/Kestrel.Https/ListenOptionsHttpsExtensions.cs +++ b/src/Kestrel.Https/ListenOptionsHttpsExtensions.cs @@ -87,6 +87,8 @@ namespace Microsoft.AspNetCore.Hosting public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions) { var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService(); + // Set the list of protocols from listen options + httpsOptions.HttpProtocols = listenOptions.Protocols; listenOptions.ConnectionAdapters.Add(new HttpsConnectionAdapter(httpsOptions, loggerFactory)); return listenOptions; } diff --git a/src/Kestrel.Tls/ClosedStream.cs b/src/Kestrel.Tls/ClosedStream.cs deleted file mode 100644 index 511869b29f..0000000000 --- a/src/Kestrel.Tls/ClosedStream.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - internal class ClosedStream : Stream - { - private static readonly Task ZeroResultTask = Task.FromResult(result: 0); - - public override bool CanRead => true; - public override bool CanSeek => false; - public override bool CanWrite => false; - - public override long Length - { - get - { - throw new NotSupportedException(); - } - } - - public override long Position - { - get - { - throw new NotSupportedException(); - } - set - { - throw new NotSupportedException(); - } - } - - public override void Flush() - { - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return 0; - } - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return ZeroResultTask; - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); - } - } -} diff --git a/src/Kestrel.Tls/Kestrel.Tls.csproj b/src/Kestrel.Tls/Kestrel.Tls.csproj deleted file mode 100644 index 3088fbcc46..0000000000 --- a/src/Kestrel.Tls/Kestrel.Tls.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - Microsoft.AspNetCore.Server.Kestrel.Tls - Microsoft.AspNetCore.Server.Kestrel.Tls - netstandard2.0 - true - aspnetcore;kestrel - CS1591;$(NoWarn) - false - true - false - - - - - - - - - - - diff --git a/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs b/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs deleted file mode 100644 index 695297eb56..0000000000 --- a/src/Kestrel.Tls/ListenOptionsTlsExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Tls; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Hosting -{ - public static class ListenOptionsTlsExtensions - { - public static ListenOptions UseTls(this ListenOptions listenOptions, string certificatePath, string password) - { - return listenOptions.UseTls(new TlsConnectionAdapterOptions - { - CertificatePath = certificatePath, - Password = password, - Protocols = listenOptions.Protocols - }); - } - - public static ListenOptions UseTls(this ListenOptions listenOptions, TlsConnectionAdapterOptions tlsOptions) - { - var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService(); - listenOptions.ConnectionAdapters.Add(new TlsConnectionAdapter(tlsOptions, loggerFactory)); - return listenOptions; - } - } -} diff --git a/src/Kestrel.Tls/OpenSsl.cs b/src/Kestrel.Tls/OpenSsl.cs deleted file mode 100644 index 52af7a8857..0000000000 --- a/src/Kestrel.Tls/OpenSsl.cs +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - public static class OpenSsl - { - public const int OPENSSL_NPN_NEGOTIATED = 1; - public const int SSL_TLSEXT_ERR_OK = 0; - public const int SSL_TLSEXT_ERR_NOACK = 3; - public const int SSL_CTRL_CHAIN = 88; - - private const int BIO_C_SET_BUF_MEM_EOF_RETURN = 130; - private const int SSL_CTRL_SET_ECDH_AUTO = 94; - - public static void SSL_library_init() - { - try - { - // Try OpenSSL 1.0.2 - NativeMethods.SSL_library_init(); - } - catch (EntryPointNotFoundException) - { - // It's fine, OpenSSL 1.1 doesn't need initialization - } - } - - public static void SSL_load_error_strings() - { - try - { - NativeMethods.SSL_load_error_strings(); - } - catch (EntryPointNotFoundException) - { - // Also fine, OpenSSL 1.1 doesn't need it. - } - } - - public static void OpenSSL_add_all_algorithms() - { - try - { - NativeMethods.OPENSSL_add_all_algorithms_noconf(); - } - catch (EntryPointNotFoundException) - { - // Also fine, OpenSSL 1.1 doesn't need it. - } - } - - public static IntPtr TLSv1_2_method() - { - return NativeMethods.TLSv1_2_method(); - } - - public static IntPtr SSL_CTX_new(IntPtr method) - { - return NativeMethods.SSL_CTX_new(method); - } - - public static void SSL_CTX_free(IntPtr ctx) - { - NativeMethods.SSL_CTX_free(ctx); - } - - public unsafe static int SSL_CTX_Set_Pfx(IntPtr ctx, string path, string password) - { - var pass = Marshal.StringToHGlobalAnsi(password); - var key = IntPtr.Zero; - var cert = IntPtr.Zero; - var ca = IntPtr.Zero; - - try - { - var file = System.IO.File.ReadAllBytes(path); - - fixed (void* f = file) - { - var buffer = (IntPtr)f; - var pkcs = NativeMethods.d2i_PKCS12(IntPtr.Zero, ref buffer, file.Length); - var result = NativeMethods.PKCS12_parse(pkcs, pass, ref key, ref cert, ref ca); - if (result != 1) - { - return -1; - } - if (NativeMethods.SSL_CTX_use_certificate(ctx, cert) != 1) return -1; - if (NativeMethods.SSL_CTX_use_PrivateKey(ctx, key) != 1) return -1; - if (NativeMethods.SSL_CTX_ctrl(ctx, SSL_CTRL_CHAIN, 1, ca) != 1) return -1; - return 1; - } - } - finally - { - Marshal.FreeHGlobal(pass); - if (key != IntPtr.Zero) NativeMethods.EVP_PKEY_free(key); - if (cert != IntPtr.Zero) NativeMethods.X509_free(cert); - if (ca != IntPtr.Zero) NativeMethods.sk_X509_pop_free(ca); - } - } - - public static int SSL_CTX_set_ecdh_auto(IntPtr ctx, int onoff) - { - return (int)NativeMethods.SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, IntPtr.Zero); - } - - public static int SSL_CTX_use_certificate_file(IntPtr ctx, string file, int type) - { - var ptr = Marshal.StringToHGlobalAnsi(file); - var error = NativeMethods.SSL_CTX_use_certificate_file(ctx, ptr, type); - Marshal.FreeHGlobal(ptr); - - return error; - } - - public static int SSL_CTX_use_PrivateKey_file(IntPtr ctx, string file, int type) - { - var ptr = Marshal.StringToHGlobalAnsi(file); - var error = NativeMethods.SSL_CTX_use_PrivateKey_file(ctx, ptr, type); - Marshal.FreeHGlobal(ptr); - - return error; - } - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate int alpn_select_cb_t(IntPtr ssl, out byte* @out, out byte outlen, byte* @in, uint inlen, IntPtr arg); - - public unsafe static void SSL_CTX_set_alpn_select_cb(IntPtr ctx, alpn_select_cb_t cb, IntPtr arg) - { - NativeMethods.SSL_CTX_set_alpn_select_cb(ctx, cb, arg); - } - - public static unsafe int SSL_select_next_proto(out byte* @out, out byte outlen, byte* server, uint server_len, byte* client, uint client_len) - { - return NativeMethods.SSL_select_next_proto(out @out, out outlen, server, server_len, client, client_len); - } - - public static unsafe void SSL_get0_alpn_selected(IntPtr ssl, out string protocol) - { - NativeMethods.SSL_get0_alpn_selected(ssl, out var data, out var length); - - protocol = data != null - ? Marshal.PtrToStringAnsi((IntPtr)data, length) - : null; - } - - public static IntPtr SSL_new(IntPtr ctx) - { - return NativeMethods.SSL_new(ctx); - } - - public static void SSL_free(IntPtr ssl) - { - NativeMethods.SSL_free(ssl); - } - - public static int SSL_get_error(IntPtr ssl, int ret) - { - return NativeMethods.SSL_get_error(ssl, ret); - } - - public static int ERR_get_error() - { - return NativeMethods.ERR_get_error(); - } - - public static string ERR_error_string(int error) - { - var buf = NativeMethods.ERR_error_string(error, IntPtr.Zero); - - // Don't free the buffer! It's a static buffer - return Marshal.PtrToStringAnsi(buf); - } - - public static void SSL_set_accept_state(IntPtr ssl) - { - NativeMethods.SSL_set_accept_state(ssl); - } - - public static int SSL_do_handshake(IntPtr ssl) - { - return NativeMethods.SSL_do_handshake(ssl); - } - - public static unsafe int SSL_read(IntPtr ssl, byte[] buffer, int offset, int count) - { - fixed (byte* ptr = buffer) - { - return NativeMethods.SSL_read(ssl, (IntPtr)(ptr + offset), count); - } - } - - public static unsafe int SSL_write(IntPtr ssl, byte[] buffer, int offset, int count) - { - fixed (byte* ptr = buffer) - { - return NativeMethods.SSL_write(ssl, (IntPtr)(ptr + offset), count); - } - } - - public static void SSL_set_bio(IntPtr ssl, IntPtr rbio, IntPtr wbio) - { - NativeMethods.SSL_set_bio(ssl, rbio, wbio); - } - - public static IntPtr BIO_new(IntPtr type) - { - return NativeMethods.BIO_new(type); - } - - public static unsafe int BIO_read(IntPtr b, byte[] buffer, int offset, int count) - { - fixed (byte* ptr = buffer) - { - return NativeMethods.BIO_read(b, (IntPtr)(ptr + offset), count); - } - } - - public static unsafe int BIO_write(IntPtr b, byte[] buffer, int offset, int count) - { - fixed (byte* ptr = buffer) - { - return NativeMethods.BIO_write(b, (IntPtr)(ptr + offset), count); - } - } - - public static long BIO_ctrl_pending(IntPtr b) - { - return NativeMethods.BIO_ctrl_pending(b); - } - - public static long BIO_set_mem_eof_return(IntPtr b, int v) - { - return NativeMethods.BIO_ctrl(b, BIO_C_SET_BUF_MEM_EOF_RETURN, v, IntPtr.Zero); - } - - public static IntPtr BIO_s_mem() - { - return NativeMethods.BIO_s_mem(); - } - - public static void ERR_load_BIO_strings() - { - NativeMethods.ERR_load_BIO_strings(); - } - - private class NativeMethods - { - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_library_init(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void SSL_load_error_strings(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void OPENSSL_add_all_algorithms_noconf(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr TLSv1_2_method(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr SSL_CTX_new(IntPtr method); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr SSL_CTX_free(IntPtr ctx); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern long SSL_CTX_ctrl(IntPtr ctx, int cmd, long larg, IntPtr parg); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_CTX_use_certificate_file(IntPtr ctx, IntPtr file, int type); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_CTX_use_PrivateKey_file(IntPtr ctx, IntPtr file, int type); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void SSL_CTX_set_alpn_select_cb(IntPtr ctx, alpn_select_cb_t cb, IntPtr arg); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern unsafe int SSL_select_next_proto(out byte* @out, out byte outlen, byte* server, uint server_len, byte* client, uint client_len); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern unsafe void SSL_get0_alpn_selected(IntPtr ssl, out byte* data, out int len); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr SSL_new(IntPtr ctx); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr SSL_free(IntPtr ssl); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_get_error(IntPtr ssl, int ret); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void SSL_set_accept_state(IntPtr ssl); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_do_handshake(IntPtr ssl); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_read(IntPtr ssl, IntPtr buf, int len); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_write(IntPtr ssl, IntPtr buf, int len); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void SSL_set_bio(IntPtr ssl, IntPtr rbio, IntPtr wbio); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr BIO_new(IntPtr type); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int BIO_read(IntPtr b, IntPtr buf, int len); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int BIO_write(IntPtr b, IntPtr buf, int len); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern long BIO_ctrl(IntPtr bp, int cmd, long larg, IntPtr parg); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern long BIO_ctrl_pending(IntPtr bp); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr BIO_s_mem(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void ERR_load_BIO_strings(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int ERR_get_error(); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr ERR_error_string(int error, IntPtr buf); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr d2i_PKCS12(IntPtr unsused, ref IntPtr bufferPointer, long length); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int PKCS12_parse(IntPtr p12, IntPtr pass, ref IntPtr pkey, ref IntPtr cert, ref IntPtr ca); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void PKCS12_free(IntPtr p12); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void EVP_PKEY_free(IntPtr pkey); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void X509_free(IntPtr a); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern void sk_X509_pop_free(IntPtr ca); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_CTX_ctrl(IntPtr ctx, int cmd, int larg, IntPtr parg); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_CTX_set1_chain(IntPtr ctx, IntPtr sk); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_CTX_use_certificate(IntPtr ctx, IntPtr x509); - - [DllImport("libssl", CallingConvention = CallingConvention.Cdecl)] - public static extern int SSL_CTX_use_PrivateKey(IntPtr ctx, IntPtr pkey); - } - } -} diff --git a/src/Kestrel.Tls/Properties/AssemblyInfo.cs b/src/Kestrel.Tls/Properties/AssemblyInfo.cs deleted file mode 100644 index 3bb5150c92..0000000000 --- a/src/Kestrel.Tls/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] diff --git a/src/Kestrel.Tls/README.md b/src/Kestrel.Tls/README.md deleted file mode 100644 index 9012206247..0000000000 --- a/src/Kestrel.Tls/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# NOT FOR PRODUCTION USE - -The code in this package contains the bare minimum to make Kestrel work with TLS 1.2 with ALPN support. It has not been audited nor hardened in any way. DO NOT USE THIS IN PRODUCTION. - -This package is temporary and will be removed once `SslStream` supports ALPN. diff --git a/src/Kestrel.Tls/TlsApplicationProtocolFeature.cs b/src/Kestrel.Tls/TlsApplicationProtocolFeature.cs deleted file mode 100644 index b3bcee74cc..0000000000 --- a/src/Kestrel.Tls/TlsApplicationProtocolFeature.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Server.Kestrel.Core.Features; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - internal class TlsApplicationProtocolFeature : ITlsApplicationProtocolFeature - { - public TlsApplicationProtocolFeature(string applicationProtocol) - { - ApplicationProtocol = applicationProtocol; - } - - public string ApplicationProtocol { get; } - } -} diff --git a/src/Kestrel.Tls/TlsConnectionAdapter.cs b/src/Kestrel.Tls/TlsConnectionAdapter.cs deleted file mode 100644 index 8dee80eb68..0000000000 --- a/src/Kestrel.Tls/TlsConnectionAdapter.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal; -using Microsoft.AspNetCore.Server.Kestrel.Core.Features; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - public class TlsConnectionAdapter : IConnectionAdapter - { - private static readonly ClosedAdaptedConnection _closedAdaptedConnection = new ClosedAdaptedConnection(); - private static readonly List _serverProtocols = new List(); - - private readonly TlsConnectionAdapterOptions _options; - private readonly ILogger _logger; - - private string _applicationProtocol; - - public TlsConnectionAdapter(TlsConnectionAdapterOptions options) - : this(options, loggerFactory: null) - { - } - - public TlsConnectionAdapter(TlsConnectionAdapterOptions options, ILoggerFactory loggerFactory) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (options.CertificatePath == null) - { - throw new ArgumentException("Certificate path must be non-null.", nameof(options)); - } - - if (options.Password == null) - { - throw new ArgumentException("Password must be non-null.", nameof(options)); - } - - _options = options; - _logger = loggerFactory?.CreateLogger(nameof(TlsConnectionAdapter)); - - // Order is important. If HTTP/2 is enabled, we prefer it over HTTP/1.1. So add it first. - if ((options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2) - { - _serverProtocols.Add("h2"); - } - - if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1) - { - _serverProtocols.Add("http/1.1"); - } - } - - public bool IsHttps => true; - - public Task OnConnectionAsync(ConnectionAdapterContext context) - { - // Don't trust TlsStream not to block. - return Task.Run(() => InnerOnConnectionAsync(context)); - } - - private async Task InnerOnConnectionAsync(ConnectionAdapterContext context) - { - var tlsStream = new TlsStream(context.ConnectionStream, _options.CertificatePath, _options.Password, _serverProtocols); - - try - { - await tlsStream.DoHandshakeAsync(); - _applicationProtocol = tlsStream.GetNegotiatedApplicationProtocol(); - } - catch (IOException ex) - { - _logger?.LogInformation(1, ex, "Authentication failed."); - tlsStream.Dispose(); - return _closedAdaptedConnection; - } - - // Always set the feature even though the cert might be null - context.Features.Set(new TlsConnectionFeature()); - context.Features.Set(new TlsApplicationProtocolFeature(_applicationProtocol)); - - return new TlsAdaptedConnection(tlsStream); - } - - private class TlsAdaptedConnection : IAdaptedConnection - { - private readonly TlsStream _tlsStream; - - public TlsAdaptedConnection(TlsStream tlsStream) - { - _tlsStream = tlsStream; - } - - public Stream ConnectionStream => _tlsStream; - - public void Dispose() - { - _tlsStream.Dispose(); - } - } - - private class ClosedAdaptedConnection : IAdaptedConnection - { - public Stream ConnectionStream { get; } = new ClosedStream(); - - public void Dispose() - { - } - } - } -} diff --git a/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs b/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs deleted file mode 100644 index 220bd47d9c..0000000000 --- a/src/Kestrel.Tls/TlsConnectionAdapterOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Server.Kestrel.Core; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - public class TlsConnectionAdapterOptions - { - public string CertificatePath { get; set; } = string.Empty; - - public string Password { get; set; } = string.Empty; - - public HttpProtocols Protocols { get; set; } - } -} diff --git a/src/Kestrel.Tls/TlsConnectionFeature.cs b/src/Kestrel.Tls/TlsConnectionFeature.cs deleted file mode 100644 index 007fbd0f8a..0000000000 --- a/src/Kestrel.Tls/TlsConnectionFeature.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http.Features; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - internal class TlsConnectionFeature : ITlsConnectionFeature - { - public X509Certificate2 ClientCertificate { get; set; } - - public Task GetClientCertificateAsync(CancellationToken cancellationToken) - { - return Task.FromResult(ClientCertificate); - } - } -} diff --git a/src/Kestrel.Tls/TlsStream.cs b/src/Kestrel.Tls/TlsStream.cs deleted file mode 100644 index 497317bab7..0000000000 --- a/src/Kestrel.Tls/TlsStream.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; - -namespace Microsoft.AspNetCore.Server.Kestrel.Tls -{ - public class TlsStream : Stream - { - // Error code that indicates that a handshake failed because unencrypted HTTP was sent - private const int SSL_ERROR_HTTP_REQUEST = 336130204; - - private static unsafe OpenSsl.alpn_select_cb_t _alpnSelectCallback = AlpnSelectCallback; - - private readonly Stream _innerStream; - private readonly byte[] _protocols; - private readonly GCHandle _protocolsHandle; - - private IntPtr _ctx; - private IntPtr _ssl; - private IntPtr _inputBio; - private IntPtr _outputBio; - - private readonly byte[] _inputBuffer = new byte[1024 * 1024]; - private readonly byte[] _outputBuffer = new byte[1024 * 1024]; - - static TlsStream() - { - OpenSsl.SSL_library_init(); - OpenSsl.SSL_load_error_strings(); - OpenSsl.ERR_load_BIO_strings(); - OpenSsl.OpenSSL_add_all_algorithms(); - } - - public TlsStream(Stream innerStream, string certificatePath, string password, IEnumerable protocols) - { - _innerStream = innerStream; - _protocols = ToWireFormat(protocols); - _protocolsHandle = GCHandle.Alloc(_protocols); - - _ctx = OpenSsl.SSL_CTX_new(OpenSsl.TLSv1_2_method()); - - if (_ctx == IntPtr.Zero) - { - throw new Exception("Unable to create SSL context."); - } - - if(OpenSsl.SSL_CTX_Set_Pfx(_ctx, certificatePath, password) != 1) - { - throw new InvalidOperationException("Unable to load PFX"); - } - - OpenSsl.SSL_CTX_set_ecdh_auto(_ctx, 1); - - OpenSsl.SSL_CTX_set_alpn_select_cb(_ctx, _alpnSelectCallback, GCHandle.ToIntPtr(_protocolsHandle)); - - _ssl = OpenSsl.SSL_new(_ctx); - - _inputBio = OpenSsl.BIO_new(OpenSsl.BIO_s_mem()); - OpenSsl.BIO_set_mem_eof_return(_inputBio, -1); - - _outputBio = OpenSsl.BIO_new(OpenSsl.BIO_s_mem()); - OpenSsl.BIO_set_mem_eof_return(_outputBio, -1); - - OpenSsl.SSL_set_bio(_ssl, _inputBio, _outputBio); - } - - ~TlsStream() - { - if (_ssl != IntPtr.Zero) - { - OpenSsl.SSL_free(_ssl); - } - - if (_ctx != IntPtr.Zero) - { - // This frees the BIOs. - OpenSsl.SSL_CTX_free(_ctx); - } - - if (_protocolsHandle.IsAllocated) - { - _protocolsHandle.Free(); - } - } - - public override bool CanRead => true; - public override bool CanWrite => true; - - public override bool CanSeek => false; - public override long Length => throw new NotSupportedException(); - public override long Position - { - get => throw new NotSupportedException(); - set => throw new NotSupportedException(); - } - public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); - public override void SetLength(long value) => throw new NotSupportedException(); - - public override void Flush() - { - FlushAsync(default(CancellationToken)).GetAwaiter().GetResult(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - WriteAsync(buffer, offset, count).GetAwaiter().GetResult(); - } - - public override async Task FlushAsync(CancellationToken cancellationToken) - { - var pending = OpenSsl.BIO_ctrl_pending(_outputBio); - - while (pending > 0) - { - var count = OpenSsl.BIO_read(_outputBio, _outputBuffer, 0, _outputBuffer.Length); - await _innerStream.WriteAsync(_outputBuffer, 0, count, cancellationToken); - - pending = OpenSsl.BIO_ctrl_pending(_outputBio); - } - } - - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (OpenSsl.BIO_ctrl_pending(_inputBio) == 0) - { - var bytesRead = await _innerStream.ReadAsync(_inputBuffer, 0, _inputBuffer.Length, cancellationToken); - - if (bytesRead == 0) - { - return 0; - } - - OpenSsl.BIO_write(_inputBio, _inputBuffer, 0, bytesRead); - } - - return OpenSsl.SSL_read(_ssl, buffer, offset, count); - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - OpenSsl.SSL_write(_ssl, buffer, offset, count); - - return FlushAsync(cancellationToken); - } - - public async Task DoHandshakeAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - OpenSsl.SSL_set_accept_state(_ssl); - - var count = 0; - - try - { - while ((count = await _innerStream.ReadAsync(_inputBuffer, 0, _inputBuffer.Length, cancellationToken)) > 0) - { - if (count == 0) - { - throw new IOException("TLS handshake failed: the inner stream was closed."); - } - - OpenSsl.BIO_write(_inputBio, _inputBuffer, 0, count); - - var ret = OpenSsl.SSL_do_handshake(_ssl); - - if (ret != 1) - { - var error = OpenSsl.SSL_get_error(_ssl, ret); - - if (error == 1) - { - // SSL error, get it from the OpenSSL error queue - error = OpenSsl.ERR_get_error(); - if (error == SSL_ERROR_HTTP_REQUEST) - { - throw new InvalidOperationException("Unencrypted HTTP traffic was sent to an HTTPS endpoint"); - } - var errorString = OpenSsl.ERR_error_string(error); - throw new IOException($"TLS handshake failed: {nameof(OpenSsl.SSL_do_handshake)} error {error}. {errorString}"); - } - } - - await FlushAsync(cancellationToken); - - if (ret == 1) - { - return; - } - } - } - finally - { - _protocolsHandle.Free(); - } - } - - public string GetNegotiatedApplicationProtocol() - { - OpenSsl.SSL_get0_alpn_selected(_ssl, out var protocol); - return protocol; - } - - private static unsafe int AlpnSelectCallback(IntPtr ssl, out byte* @out, out byte outlen, byte* @in, uint inlen, IntPtr arg) - { - var protocols = GCHandle.FromIntPtr(arg); - var server = (byte[])protocols.Target; - - fixed (byte* serverPtr = server) - { - return OpenSsl.SSL_select_next_proto(out @out, out outlen, serverPtr, (uint)server.Length, @in, (uint)inlen) == OpenSsl.OPENSSL_NPN_NEGOTIATED - ? OpenSsl.SSL_TLSEXT_ERR_OK - : OpenSsl.SSL_TLSEXT_ERR_NOACK; - } - } - - private static byte[] ToWireFormat(IEnumerable protocols) - { - var buffer = new byte[protocols.Count() + protocols.Sum(protocol => protocol.Length)]; - - var offset = 0; - foreach (var protocol in protocols) - { - buffer[offset++] = (byte)protocol.Length; - offset += Encoding.ASCII.GetBytes(protocol, 0, protocol.Length, buffer, offset); - } - - return buffer; - } - } -}