From bbdcfa7c0374a0655f24ee7b935abe7010fe7401 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 13 Jan 2016 14:31:21 -0800 Subject: [PATCH] #27 Forward client certificates. --- samples/IISSample/Startup.cs | 7 +++ .../ForwardedTlsConnectionFeature.cs | 56 +++++++++++++++++++ .../IISPlatformHandlerMiddleware.cs | 21 ++++++- .../IISPlatformHandlerOptions.cs | 5 ++ .../project.json | 1 + 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/Microsoft.AspNet.IISPlatformHandler/ForwardedTlsConnectionFeature.cs diff --git a/samples/IISSample/Startup.cs b/samples/IISSample/Startup.cs index c177ee977..ed23bf990 100644 --- a/samples/IISSample/Startup.cs +++ b/samples/IISSample/Startup.cs @@ -26,16 +26,23 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) await context.Response.WriteAsync("User - " + context.User.Identity.Name + Environment.NewLine); await context.Response.WriteAsync("PathBase: " + context.Request.PathBase.Value + Environment.NewLine); await context.Response.WriteAsync("Path: " + context.Request.Path.Value + Environment.NewLine); + await context.Response.WriteAsync("ClientCert: " + context.Connection.ClientCertificate + Environment.NewLine); + + await context.Response.WriteAsync(Environment.NewLine + "Headers:" + Environment.NewLine); foreach (var header in context.Request.Headers) { await context.Response.WriteAsync(header.Key + ": " + header.Value + Environment.NewLine); } + + await context.Response.WriteAsync(Environment.NewLine + "Environment Variables:" + Environment.NewLine); var vars = Environment.GetEnvironmentVariables(); foreach (var key in vars.Keys) { var value = vars[key]; await context.Response.WriteAsync(key + ": " + value + Environment.NewLine); } + + // throw new Exception("Test Exception"); }); } diff --git a/src/Microsoft.AspNet.IISPlatformHandler/ForwardedTlsConnectionFeature.cs b/src/Microsoft.AspNet.IISPlatformHandler/ForwardedTlsConnectionFeature.cs new file mode 100644 index 000000000..ee81dff78 --- /dev/null +++ b/src/Microsoft.AspNet.IISPlatformHandler/ForwardedTlsConnectionFeature.cs @@ -0,0 +1,56 @@ +// 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.AspNet.Http.Features; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; + +namespace Microsoft.AspNet.IISPlatformHandler +{ + internal class ForwardedTlsConnectionFeature : ITlsConnectionFeature + { + private StringValues _header; + private X509Certificate2 _certificate; + private ILogger _logger; + + public ForwardedTlsConnectionFeature(ILogger logger, StringValues header) + { + _logger = logger; + _header = header; + } + + public X509Certificate2 ClientCertificate + { + get + { + if (_certificate == null && _header != StringValues.Empty) + { + try + { + var bytes = Convert.FromBase64String(_header); + _certificate = new X509Certificate2(bytes); + } + catch (Exception ex) + { + _logger.LogWarning("Failed to apply the client certificate.", ex); + } + } + return _certificate; + } + set + { + _certificate = value; + _header = StringValues.Empty; + } + } + + public Task GetClientCertificateAsync(CancellationToken cancellationToken) + { + return Task.FromResult(ClientCertificate); + } + } +} diff --git a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs index f99a701de..bcc13952f 100644 --- a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs +++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs @@ -3,13 +3,16 @@ using System; using System.Globalization; +using System.Security.Cryptography.X509Certificates; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNet.Builder; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Http.Features.Authentication; using Microsoft.AspNet.Http.Features.Authentication.Internal; using Microsoft.Extensions.Internal; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; @@ -18,16 +21,22 @@ namespace Microsoft.AspNet.IISPlatformHandler public class IISPlatformHandlerMiddleware { private const string XIISWindowsAuthToken = "X-IIS-WindowsAuthToken"; + private const string MSPlatformHandlerClientCert = "MS-PLATFORM-HANDLER-CLIENTCERT"; private readonly RequestDelegate _next; private readonly IISPlatformHandlerOptions _options; + private readonly ILogger _logger; - public IISPlatformHandlerMiddleware(RequestDelegate next, IOptions options) + public IISPlatformHandlerMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions options) { if (next == null) { throw new ArgumentNullException(nameof(next)); } + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } if (options == null) { throw new ArgumentNullException(nameof(options)); @@ -35,10 +44,20 @@ public IISPlatformHandlerMiddleware(RequestDelegate next, IOptions(); } public async Task Invoke(HttpContext httpContext) { + if (_options.FlowClientCertificate) + { + var header = httpContext.Request.Headers[MSPlatformHandlerClientCert]; + if (!StringValues.IsNullOrEmpty(header)) + { + httpContext.Features.Set(new ForwardedTlsConnectionFeature(_logger, header)); + } + } + if (_options.FlowWindowsAuthentication) { var winPrincipal = UpdateUser(httpContext); diff --git a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerOptions.cs b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerOptions.cs index 9d4a4e12e..7244a834f 100644 --- a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerOptions.cs +++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerOptions.cs @@ -22,6 +22,11 @@ public class IISPlatformHandlerOptions /// public bool FlowWindowsAuthentication { get; set; } = true; + /// + /// Populates the ITLSConnectionFeature if the MS-PLATFORM-HANDLER-CLIENTCERT request header is present. + /// + public bool FlowClientCertificate { get; set; } = true; + /// /// Additional information about the authentication type which is made available to the application. /// diff --git a/src/Microsoft.AspNet.IISPlatformHandler/project.json b/src/Microsoft.AspNet.IISPlatformHandler/project.json index e9a6179e4..a06a24d4e 100644 --- a/src/Microsoft.AspNet.IISPlatformHandler/project.json +++ b/src/Microsoft.AspNet.IISPlatformHandler/project.json @@ -13,6 +13,7 @@ "Microsoft.AspNet.Hosting.Abstractions": "1.0.0-*", "Microsoft.AspNet.Http": "1.0.0-*", "Microsoft.AspNet.Http.Extensions": "1.0.0-*", + "Microsoft.Extensions.Logging.Abstractions": "1.0.0-*", "Microsoft.Extensions.Options": "1.0.0-*", "Microsoft.Extensions.SecurityHelper.Sources": { "type": "build",