aspnetcore/src/Microsoft.AspNet.IISPlatfor.../IISPlatformHandlerMiddlewar...

170 lines
6.4 KiB
C#

// 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.Globalization;
using System.Linq;
using System.Net;
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.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.IISPlatformHandler
{
public class IISPlatformHandlerMiddleware
{
private const string XForwardedForHeaderName = "X-Forwarded-For";
private const string XForwardedProtoHeaderName = "X-Forwarded-Proto";
private const string XIISWindowsAuthToken = "X-IIS-WindowsAuthToken";
private const string XOriginalPortName = "X-Original-Port";
private const string XOriginalProtoName = "X-Original-Proto";
private const string XOriginalIPName = "X-Original-IP";
private readonly RequestDelegate _next;
private readonly IISPlatformHandlerOptions _options;
public IISPlatformHandlerMiddleware(RequestDelegate next, IISPlatformHandlerOptions options)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_next = next;
_options = options;
}
public async Task Invoke(HttpContext httpContext)
{
UpdateScheme(httpContext);
UpdateRemoteIp(httpContext);
if (_options.FlowWindowsAuthentication)
{
var winPrincipal = UpdateUser(httpContext);
var handler = new AuthenticationHandler(httpContext, _options, winPrincipal);
AttachAuthenticationHandler(handler);
try
{
await _next(httpContext);
}
finally
{
DetachAuthenticationhandler(handler);
}
}
else
{
await _next(httpContext);
}
}
private static void UpdateScheme(HttpContext httpContext)
{
var xForwardProtoHeaderValue = httpContext.Request.Headers[XForwardedProtoHeaderName];
if (!string.IsNullOrEmpty(xForwardProtoHeaderValue))
{
if (!string.IsNullOrEmpty(httpContext.Request.Scheme))
{
httpContext.Request.Headers[XOriginalProtoName] = httpContext.Request.Scheme;
}
httpContext.Request.Scheme = xForwardProtoHeaderValue;
}
}
private static void UpdateRemoteIp(HttpContext httpContext)
{
var xForwardedForHeaderValue = httpContext.Request.Headers.GetCommaSeparatedValues(XForwardedForHeaderName);
if (xForwardedForHeaderValue != null && xForwardedForHeaderValue.Length > 0)
{
IPAddress ipFromHeader;
int? port;
if (IPAddressWithPortParser.TryParse(xForwardedForHeaderValue[0], out ipFromHeader, out port))
{
var connection = httpContext.Connection;
var remoteIPString = connection.RemoteIpAddress?.ToString();
if (!string.IsNullOrEmpty(remoteIPString))
{
httpContext.Request.Headers[XOriginalIPName] = remoteIPString;
}
if (port.HasValue)
{
if (connection.RemotePort != 0)
{
httpContext.Request.Headers[XOriginalPortName] = connection.RemotePort.ToString(CultureInfo.InvariantCulture);
}
connection.RemotePort = port.Value;
}
connection.RemoteIpAddress = ipFromHeader;
}
}
}
private WindowsPrincipal UpdateUser(HttpContext httpContext)
{
var xIISWindowsAuthToken = httpContext.Request.Headers[XIISWindowsAuthToken];
int hexHandle;
WindowsPrincipal winPrincipal = null;
if (!StringValues.IsNullOrEmpty(xIISWindowsAuthToken)
&& int.TryParse(xIISWindowsAuthToken, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hexHandle))
{
// Always create the identity if the handle exists, we need to dispose it so it does not leak.
var handle = new IntPtr(hexHandle);
var winIdentity = new WindowsIdentity(handle);
// WindowsIdentity just duplicated the handle so we need to close the original.
NativeMethods.CloseHandle(handle);
httpContext.Response.RegisterForDispose(winIdentity);
winPrincipal = new WindowsPrincipal(winIdentity);
if (_options.AutomaticAuthentication)
{
var existingPrincipal = httpContext.User;
if (existingPrincipal != null)
{
httpContext.User = SecurityHelper.MergeUserPrincipal(existingPrincipal, winPrincipal);
}
else
{
httpContext.User = winPrincipal;
}
}
}
return winPrincipal;
}
private void AttachAuthenticationHandler(AuthenticationHandler handler)
{
var auth = handler.HttpContext.Features.Get<IHttpAuthenticationFeature>();
if (auth == null)
{
auth = new HttpAuthenticationFeature();
handler.HttpContext.Features.Set(auth);
}
handler.PriorHandler = auth.Handler;
auth.Handler = handler;
}
private void DetachAuthenticationhandler(AuthenticationHandler handler)
{
var auth = handler.HttpContext.Features.Get<IHttpAuthenticationFeature>();
if (auth != null)
{
auth.Handler = handler.PriorHandler;
}
}
}
}