diff --git a/samples/IISSample/Startup.cs b/samples/IISSample/Startup.cs index c177ee9778..234f71e870 100644 --- a/samples/IISSample/Startup.cs +++ b/samples/IISSample/Startup.cs @@ -26,10 +26,15 @@ namespace IISSample 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) { diff --git a/src/Microsoft.AspNet.IISPlatformHandler/ForwardedTlsConnectionFeature.cs b/src/Microsoft.AspNet.IISPlatformHandler/ForwardedTlsConnectionFeature.cs new file mode 100644 index 0000000000..c877568492 --- /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 read 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 f99a701ded..bcc13952f0 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 @@ namespace Microsoft.AspNet.IISPlatformHandler _next = next; _options = options.Value; + _logger = loggerFactory.CreateLogger(); } 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 9d4a4e12e2..7244a834f9 100644 --- a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerOptions.cs +++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerOptions.cs @@ -22,6 +22,11 @@ namespace Microsoft.AspNet.Builder /// 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 e9a6179e42..a06a24d4eb 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",