// 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.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Claims;
using System.Security.Principal;
using Microsoft.AspNetCore.HttpSys.Internal;
using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.Server.HttpSys
{
// See the native HTTP_SERVER_AUTHENTICATION_INFO structure documentation for additional information.
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa364638(v=vs.85).aspx
///
/// Exposes the Http.Sys authentication configurations.
///
public sealed class AuthenticationManager
{
private static readonly int AuthInfoSize =
Marshal.SizeOf();
private UrlGroup _urlGroup;
private AuthenticationSchemes _authSchemes;
private bool _allowAnonymous = true;
internal AuthenticationManager()
{
}
public AuthenticationSchemes Schemes
{
get { return _authSchemes; }
set
{
_authSchemes = value;
SetUrlGroupSecurity();
}
}
public bool AllowAnonymous
{
get { return _allowAnonymous; }
set { _allowAnonymous = value; }
}
internal void SetUrlGroupSecurity(UrlGroup urlGroup)
{
Debug.Assert(_urlGroup == null, "SetUrlGroupSecurity called more than once.");
_urlGroup = urlGroup;
SetUrlGroupSecurity();
}
private unsafe void SetUrlGroupSecurity()
{
if (_urlGroup == null)
{
// Not started yet.
return;
}
HttpApiTypes.HTTP_SERVER_AUTHENTICATION_INFO authInfo =
new HttpApiTypes.HTTP_SERVER_AUTHENTICATION_INFO();
authInfo.Flags = HttpApiTypes.HTTP_FLAGS.HTTP_PROPERTY_FLAG_PRESENT;
var authSchemes = (HttpApiTypes.HTTP_AUTH_TYPES)_authSchemes;
if (authSchemes != HttpApiTypes.HTTP_AUTH_TYPES.NONE)
{
authInfo.AuthSchemes = authSchemes;
// TODO:
// NTLM auth sharing (on by default?) DisableNTLMCredentialCaching
// Kerberos auth sharing (off by default?) HTTP_AUTH_EX_FLAG_ENABLE_KERBEROS_CREDENTIAL_CACHING
// Mutual Auth - ReceiveMutualAuth
// Digest domain and realm - HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS
// Basic realm - HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS
IntPtr infoptr = new IntPtr(&authInfo);
_urlGroup.SetProperty(
HttpApiTypes.HTTP_SERVER_PROPERTY.HttpServerAuthenticationProperty,
infoptr, (uint)AuthInfoSize);
}
}
internal static IList GenerateChallenges(AuthenticationSchemes authSchemes)
{
IList challenges = new List();
if (authSchemes == AuthenticationSchemes.None)
{
return challenges;
}
// Order by strength.
if ((authSchemes & AuthenticationSchemes.Kerberos) == AuthenticationSchemes.Kerberos)
{
challenges.Add("Kerberos");
}
if ((authSchemes & AuthenticationSchemes.Negotiate) == AuthenticationSchemes.Negotiate)
{
challenges.Add("Negotiate");
}
if ((authSchemes & AuthenticationSchemes.NTLM) == AuthenticationSchemes.NTLM)
{
challenges.Add("NTLM");
}
/*if ((_authSchemes & AuthenticationSchemes.Digest) == AuthenticationSchemes.Digest)
{
// TODO:
throw new NotImplementedException("Digest challenge generation has not been implemented.");
// challenges.Add("Digest");
}*/
if ((authSchemes & AuthenticationSchemes.Basic) == AuthenticationSchemes.Basic)
{
// TODO: Realm
challenges.Add("Basic");
}
return challenges;
}
internal void SetAuthenticationChallenge(RequestContext context)
{
IList challenges = GenerateChallenges(context.Response.AuthenticationChallenges);
if (challenges.Count > 0)
{
context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate]
= StringValues.Concat(context.Response.Headers[HttpKnownHeaderNames.WWWAuthenticate], challenges.ToArray());
}
}
}
}