diff --git a/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj b/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj index 1409566d32f8c6..06dff89867e1e7 100644 --- a/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj +++ b/src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj @@ -161,94 +161,10 @@ Link="Common\Interop\Windows\Kernel32\Interop.LoadLibraryEx_IntPtr.cs" /> - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs index 8f44e89fdf2c45..5f9b939182b78f 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListener.Windows.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Win32.SafeHandles; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -723,9 +724,9 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) // Figure out what schemes we're allowing, what context we have. stoleBlob = true; HttpListenerContext? httpContext = null; - NTAuthentication? oldContext = null; - NTAuthentication? newContext = null; - NTAuthentication? context = null; + NegotiateAuthentication? sessionContext = null; + bool keepSessionContext = false; + string? contextPackage = null; AuthenticationSchemes headerScheme = AuthenticationSchemes.None; AuthenticationSchemes authenticationScheme = AuthenticationSchemes; ExtendedProtectionPolicy extendedProtectionPolicy = _extendedProtectionPolicy; @@ -741,7 +742,8 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) // Pick out the old context now. By default, it'll be removed in the finally, unless context is set somewhere. if (disconnectResult != null) { - oldContext = disconnectResult.Session; + sessionContext = disconnectResult.Session; + contextPackage = disconnectResult.SessionPackage; } httpContext = new HttpListenerContext(session, memoryBlob); @@ -857,25 +859,29 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) string inBlob = index < authorizationHeader.Length ? authorizationHeader.Substring(index) : ""; IPrincipal? principal = null; - SecurityStatusPal statusCodeNew; + NegotiateAuthenticationStatusCode statusCodeNew; ChannelBinding? binding; if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Performing Authentication headerScheme: {headerScheme}"); switch (headerScheme) { case AuthenticationSchemes.Negotiate: case AuthenticationSchemes.Ntlm: - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"context: {oldContext} for connectionId: {connectionId}"); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"context: {sessionContext} for connectionId: {connectionId}"); string package = headerScheme == AuthenticationSchemes.Ntlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Negotiate; - if (oldContext != null && oldContext.Package == package) - { - context = oldContext; - } - else + if (sessionContext is null || sessionContext.IsAuthenticated || contextPackage != package) { + sessionContext?.Dispose(); + binding = GetChannelBinding(session, connectionId, isSecureConnection, extendedProtectionPolicy); - ContextFlagsPal contextFlags = GetContextFlags(extendedProtectionPolicy); - context = new NTAuthentication(true, package, CredentialCache.DefaultNetworkCredentials, null, contextFlags, binding); + NegotiateAuthenticationServerOptions serverOptions = new NegotiateAuthenticationServerOptions + { + Package = package, + Binding = binding, + Policy = GetAuthenticationExtendedProtectionPolicy(extendedProtectionPolicy) + }; + sessionContext = new NegotiateAuthentication(serverOptions); + contextPackage = package; } try @@ -890,20 +896,12 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) } if (!error) { - decodedOutgoingBlob = context.GetOutgoingBlob(bytes, false, out statusCodeNew); - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"GetOutgoingBlob returned IsCompleted: {context.IsCompleted} and statusCodeNew: {statusCodeNew}"); - error = !context.IsValidContext; + decodedOutgoingBlob = sessionContext.GetOutgoingBlob(bytes, out statusCodeNew); + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"GetOutgoingBlob returned IsAuthenticated: {sessionContext.IsAuthenticated} and statusCodeNew: {statusCodeNew}"); + error = statusCodeNew >= NegotiateAuthenticationStatusCode.GenericFailure; if (error) { - // SSPI Workaround - // If a client sends up a blob on the initial request, Negotiate returns SEC_E_INVALID_HANDLE - // when it should return SEC_E_INVALID_TOKEN. - if (statusCodeNew.ErrorCode == SecurityStatusPalErrorCode.InvalidHandle && oldContext == null && bytes != null && bytes.Length > 0) - { - statusCodeNew = new SecurityStatusPal(SecurityStatusPalErrorCode.InvalidToken); - } - - httpError = HttpStatusFromSecurityStatus(statusCodeNew.ErrorCode); + httpError = HttpStatusFromSecurityStatus(statusCodeNew); } } @@ -915,92 +913,83 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) if (!error) { - if (context.IsCompleted) + if (sessionContext.IsAuthenticated) { - SecurityContextTokenHandle? userContext = null; + httpContext.Request.ServiceName = sessionContext.TargetName; + try { - if (!CheckSpn(context, isSecureConnection, extendedProtectionPolicy)) + IIdentity identity = sessionContext.RemoteIdentity; + + if (identity is not WindowsIdentity windowsIdentity) { - httpError = HttpStatusCode.Unauthorized; + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, + $"HandleAuthentication RemoteIdentity return non-Windows identity: {identity.GetType().Name}"); + } + + httpError = HttpStatusCode.InternalServerError; } else { - httpContext.Request.ServiceName = context.ClientSpecifiedSpn; + WindowsPrincipal windowsPrincipal = new WindowsPrincipal((WindowsIdentity)windowsIdentity.Clone()); - SafeDeleteContext securityContext = context.GetContext(out statusCodeNew)!; - if (statusCodeNew.ErrorCode != SecurityStatusPalErrorCode.OK) + principal = windowsPrincipal; + // if appropriate, cache this credential on this connection + if (UnsafeConnectionNtlmAuthentication && sessionContext.Package == NegotiationInfoClass.NTLM) { if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(this, - $"HandleAuthentication GetContextToken failed with statusCodeNew: {statusCodeNew}"); + $"HandleAuthentication inserting principal: {principal} for connectionId: {connectionId}"); } - httpError = HttpStatusFromSecurityStatus(statusCodeNew.ErrorCode); - } - else - { - SSPIWrapper.QuerySecurityContextToken(GlobalSSPI.SSPIAuth, securityContext, out userContext); - - if (NetEventSource.Log.IsEnabled()) + // We may need to call WaitForDisconnect. + if (disconnectResult == null) { - NetEventSource.Info(this, - $"HandleAuthentication creating new WindowsIdentity from user context: {userContext.DangerousGetHandle():x8}"); + RegisterForDisconnectNotification(session, connectionId, ref disconnectResult); } - - WindowsPrincipal windowsPrincipal = new WindowsPrincipal( - new WindowsIdentity(userContext.DangerousGetHandle(), context.ProtocolName)); - - principal = windowsPrincipal; - // if appropriate, cache this credential on this connection - if (UnsafeConnectionNtlmAuthentication && context.ProtocolName == NegotiationInfoClass.NTLM) + if (disconnectResult != null) { - if (NetEventSource.Log.IsEnabled()) + lock ((DisconnectResults as ICollection).SyncRoot) { - NetEventSource.Info(this, - $"HandleAuthentication inserting principal: {principal} for connectionId: {connectionId}"); - } - - // We may need to call WaitForDisconnect. - if (disconnectResult == null) - { - RegisterForDisconnectNotification(session, connectionId, ref disconnectResult); - } - if (disconnectResult != null) - { - lock ((DisconnectResults as ICollection).SyncRoot) + if (UnsafeConnectionNtlmAuthentication) { - if (UnsafeConnectionNtlmAuthentication) - { - disconnectResult.AuthenticatedConnection = windowsPrincipal; - } + disconnectResult.AuthenticatedConnection = windowsPrincipal; + keepSessionContext = true; } } - else + } + else + { + // Registration failed - UnsafeConnectionNtlmAuthentication ignored. + if (NetEventSource.Log.IsEnabled()) { - // Registration failed - UnsafeConnectionNtlmAuthentication ignored. - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, $"HandleAuthentication RegisterForDisconnectNotification failed."); - } + NetEventSource.Info(this, $"HandleAuthentication RegisterForDisconnectNotification failed."); } } } } } - finally + catch (Exception e) { - userContext?.Dispose(); + if (NetEventSource.Log.IsEnabled()) + { + NetEventSource.Info(this, + $"HandleAuthentication RemoteIdentity failed with exception: {e.Message}"); + } + + httpError = HttpStatusCode.InternalServerError; } } else { // auth incomplete - newContext = context; challenge = string.IsNullOrEmpty(outBlob) ? headerScheme == AuthenticationSchemes.Ntlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Negotiate : outBlob; + keepSessionContext = true; } } break; @@ -1071,24 +1060,8 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) else { // We're starting over. Any context SSPI might have wanted us to keep is useless. - if (newContext != null) - { - if (newContext == context) - { - context = null; - } - - if (newContext != oldContext) - { - NTAuthentication toClose = newContext; - newContext = null; - toClose.CloseContext(); - } - else - { - newContext = null; - } - } + sessionContext?.Dispose(); + sessionContext = null; // If we're sending something besides 401, do it here. if (httpError != HttpStatusCode.Unauthorized) @@ -1098,60 +1071,35 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) return null; } - challenges = BuildChallenge(authenticationScheme, out newContext); + challenges = BuildChallenge(authenticationScheme); } } - // Check if we need to call WaitForDisconnect, because if we do and it fails, we want to send a 500 instead. - if (disconnectResult == null && newContext != null) + // Update Session if necessary. + if (keepSessionContext) { - RegisterForDisconnectNotification(session, connectionId, ref disconnectResult); - - // Failed - send 500. + // Check if we need to call WaitForDisconnect, because if we do and it fails, we want to send a 500 instead. if (disconnectResult == null) { - if (newContext != null) + RegisterForDisconnectNotification(session, connectionId, ref disconnectResult); + + // Failed - send 500. + if (disconnectResult == null) { - if (newContext == context) - { - context = null; - } + sessionContext?.Dispose(); + sessionContext = null; - if (newContext != oldContext) - { - NTAuthentication toClose = newContext; - newContext = null; - toClose.CloseContext(); - } - else - { - newContext = null; - } + if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "connectionId:" + connectionId + " because of failed HttpWaitForDisconnect"); + SendError(session, requestId, HttpStatusCode.InternalServerError, null); + FreeContext(ref httpContext, memoryBlob); + return null; } - - if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "connectionId:" + connectionId + " because of failed HttpWaitForDisconnect"); - SendError(session, requestId, HttpStatusCode.InternalServerError, null); - FreeContext(ref httpContext, memoryBlob); - return null; } - } - // Update Session if necessary. - if (oldContext != newContext) - { - if (oldContext == context) - { - // Prevent the finally from closing this twice. - context = null; - } - - NTAuthentication? toClose = oldContext; - oldContext = newContext; - // TODO: Can disconnectResult be null here? - Debug.Assert(disconnectResult != null); - disconnectResult!.Session = newContext; - - toClose?.CloseContext(); + disconnectResult.Session = sessionContext; + disconnectResult.SessionPackage = contextPackage; + // Prevent finally from disposing the context + sessionContext = null; } // Send the 401 here. @@ -1167,30 +1115,14 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) stoleBlob = true; httpContext.Request.ReleasePins(); } + return httpContext; } catch { FreeContext(ref httpContext, memoryBlob); - if (newContext != null) - { - if (newContext == context) - { - // Prevent the finally from closing this twice. - context = null; - } - - if (newContext != oldContext) - { - NTAuthentication toClose = newContext; - newContext = null; - toClose.CloseContext(); - } - else - { - newContext = null; - } - } + sessionContext?.Dispose(); + sessionContext = null; throw; } finally @@ -1198,21 +1130,16 @@ public HttpListenerContext EndGetContext(IAsyncResult asyncResult) try { // Clean up the previous context if necessary. - if (oldContext != null && oldContext != newContext) + if (sessionContext != null) { // Clear out Session if it wasn't already. - if (newContext == null && disconnectResult != null) + if (disconnectResult != null) { disconnectResult.Session = null; + disconnectResult.SessionPackage = null; } - oldContext.CloseContext(); - } - - // Delete any context created but not stored. - if (context != null && oldContext != context && newContext != context) - { - context.CloseContext(); + sessionContext?.Dispose(); } } finally @@ -1244,7 +1171,7 @@ internal void SetAuthenticationHeaders(HttpListenerContext context) HttpListenerResponse response = context.Response; // We use the cached results from the delegates so that we don't have to call them again here. - ArrayList? challenges = BuildChallenge(context.AuthenticationSchemes, out _); + ArrayList? challenges = BuildChallenge(context.AuthenticationSchemes); // Setting 401 without setting WWW-Authenticate is a protocol violation // but throwing from HttpListener would be a breaking change. @@ -1286,159 +1213,27 @@ internal void SetAuthenticationHeaders(HttpListenerContext context) return result; } - private bool CheckSpn(NTAuthentication context, bool isSecureConnection, ExtendedProtectionPolicy policy) - { - // Kerberos does SPN check already in ASC - if (context.IsKerberos) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, SR.net_log_listener_no_spn_kerberos); - } - return true; - } - - // Don't check the SPN if Extended Protection is off or we already checked the CBT - if (policy.PolicyEnforcement == PolicyEnforcement.Never) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, SR.net_log_listener_no_spn_disabled); - } - return true; - } - - if (ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, SR.net_log_listener_no_spn_cbt); - } - return true; - } - - string? clientSpn = context.ClientSpecifiedSpn; - - // An empty SPN is only allowed in the WhenSupported case - if (string.IsNullOrEmpty(clientSpn)) - { - if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, - SR.net_log_listener_no_spn_whensupported); - } - return true; - } - else - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, - SR.net_log_listener_spn_failed_always); - } - return false; - } - } - else if (string.Equals(clientSpn, "http/localhost", StringComparison.OrdinalIgnoreCase)) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, SR.net_log_listener_no_spn_loopback); - } - - return true; - } - else - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, SR.net_log_listener_spn, clientSpn); - } - - ServiceNameCollection serviceNames = GetServiceNames(policy); - - bool found = serviceNames.Contains(clientSpn); - - if (NetEventSource.Log.IsEnabled()) - { - if (found) - { - NetEventSource.Info(this, SR.net_log_listener_spn_passed); - } - else - { - NetEventSource.Info(this, SR.net_log_listener_spn_failed); - - if (serviceNames.Count == 0) - { - if (NetEventSource.Log.IsEnabled()) - { - NetEventSource.Info(this, SR.net_log_listener_spn_failed_empty); - } - } - else - { - NetEventSource.Info(this, SR.net_log_listener_spn_failed_dump); - - foreach (string serviceName in serviceNames) - { - NetEventSource.Info(this, "\t" + serviceName); - } - } - } - } - - return found; - } - } - - private ServiceNameCollection GetServiceNames(ExtendedProtectionPolicy policy) + private ExtendedProtectionPolicy GetAuthenticationExtendedProtectionPolicy(ExtendedProtectionPolicy policy) { - ServiceNameCollection serviceNames; - - if (policy.CustomServiceNames == null) + if (policy.PolicyEnforcement == PolicyEnforcement.Never || + policy.CustomServiceNames != null) { - if (_defaultServiceNames.ServiceNames.Count == 0) - { - throw new InvalidOperationException(SR.net_listener_no_spns); - } - serviceNames = _defaultServiceNames.ServiceNames; - } - else - { - serviceNames = policy.CustomServiceNames; + return policy; } - return serviceNames; - } - private static bool ScenarioChecksChannelBinding(bool isSecureConnection, ProtectionScenario scenario) - { - return (isSecureConnection && scenario == ProtectionScenario.TransportSelected); - } - - private static ContextFlagsPal GetContextFlags(ExtendedProtectionPolicy policy) - { - ContextFlagsPal result = ContextFlagsPal.Connection; - if (policy.PolicyEnforcement != PolicyEnforcement.Never) + if (_defaultServiceNames.ServiceNames.Count == 0) { - if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported) - { - result |= ContextFlagsPal.AllowMissingBindings; - } - - if (policy.ProtectionScenario == ProtectionScenario.TrustedProxy) - { - result |= ContextFlagsPal.ProxyBindings; - } + throw new InvalidOperationException(SR.net_listener_no_spns); } - return result; + return new ExtendedProtectionPolicy( + policy.PolicyEnforcement, + policy.ProtectionScenario, + _defaultServiceNames.ServiceNames.Merge("HTTP/localhost")); } // This only works for context-destroying errors. - private static HttpStatusCode HttpStatusFromSecurityStatus(SecurityStatusPalErrorCode statusErrorCode) + private static HttpStatusCode HttpStatusFromSecurityStatus(NegotiateAuthenticationStatusCode statusErrorCode) { if (IsCredentialFailure(statusErrorCode)) { @@ -1452,36 +1247,24 @@ private static HttpStatusCode HttpStatusFromSecurityStatus(SecurityStatusPalErro } // This only works for context-destroying errors. - internal static bool IsCredentialFailure(SecurityStatusPalErrorCode error) + internal static bool IsCredentialFailure(NegotiateAuthenticationStatusCode error) { - return error == SecurityStatusPalErrorCode.LogonDenied || - error == SecurityStatusPalErrorCode.UnknownCredentials || - error == SecurityStatusPalErrorCode.NoImpersonation || - error == SecurityStatusPalErrorCode.NoAuthenticatingAuthority || - error == SecurityStatusPalErrorCode.UntrustedRoot || - error == SecurityStatusPalErrorCode.CertExpired || - error == SecurityStatusPalErrorCode.SmartcardLogonRequired || - error == SecurityStatusPalErrorCode.BadBinding; + return error == NegotiateAuthenticationStatusCode.UnknownCredentials || + error == NegotiateAuthenticationStatusCode.CredentialsExpired || + error == NegotiateAuthenticationStatusCode.BadBinding || + error == NegotiateAuthenticationStatusCode.TargetUnknown || + error == NegotiateAuthenticationStatusCode.ImpersonationValidationFailed; } // This only works for context-destroying errors. - internal static bool IsClientFault(SecurityStatusPalErrorCode error) + internal static bool IsClientFault(NegotiateAuthenticationStatusCode error) { - return error == SecurityStatusPalErrorCode.InvalidToken || - error == SecurityStatusPalErrorCode.CannotPack || - error == SecurityStatusPalErrorCode.QopNotSupported || - error == SecurityStatusPalErrorCode.NoCredentials || - error == SecurityStatusPalErrorCode.MessageAltered || - error == SecurityStatusPalErrorCode.OutOfSequence || - error == SecurityStatusPalErrorCode.IncompleteMessage || - error == SecurityStatusPalErrorCode.IncompleteCredentials || - error == SecurityStatusPalErrorCode.WrongPrincipal || - error == SecurityStatusPalErrorCode.TimeSkew || - error == SecurityStatusPalErrorCode.IllegalMessage || - error == SecurityStatusPalErrorCode.CertUnknown || - error == SecurityStatusPalErrorCode.AlgorithmMismatch || - error == SecurityStatusPalErrorCode.SecurityQosFailed || - error == SecurityStatusPalErrorCode.UnsupportedPreauth; + return error == NegotiateAuthenticationStatusCode.InvalidToken || + error == NegotiateAuthenticationStatusCode.QopNotSupported || + error == NegotiateAuthenticationStatusCode.UnknownCredentials || + error == NegotiateAuthenticationStatusCode.MessageAltered || + error == NegotiateAuthenticationStatusCode.OutOfSequence || + error == NegotiateAuthenticationStatusCode.InvalidCredentials; } private static void AddChallenge(ref ArrayList? challenges, string challenge) @@ -1498,12 +1281,10 @@ private static void AddChallenge(ref ArrayList? challenges, string challenge) } } - private ArrayList? BuildChallenge(AuthenticationSchemes authenticationScheme, - out NTAuthentication? newContext) + private ArrayList? BuildChallenge(AuthenticationSchemes authenticationScheme) { if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "AuthenticationScheme:" + authenticationScheme.ToString()); ArrayList? challenges = null; - newContext = null; if ((authenticationScheme & AuthenticationSchemes.Negotiate) != 0) { @@ -1756,9 +1537,6 @@ private sealed class DisconnectAsyncResult : IAsyncResult private readonly NativeOverlapped* _nativeOverlapped; private int _ownershipState; // 0 = normal, 1 = in HandleAuthentication(), 2 = disconnected, 3 = cleaned up - private WindowsPrincipal? _authenticatedConnection; - private NTAuthentication? _session; - internal NativeOverlapped* NativeOverlapped { get @@ -1862,14 +1640,16 @@ private void HandleDisconnect() if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"DisconnectResults {listener.DisconnectResults} removing for _connectionId: {_connectionId}"); listener.DisconnectResults.Remove(_connectionId); - _session?.CloseContext(); + + // Cached identity is disposed with the session context + Session?.Dispose(); // Clean up the identity. This is for scenarios where identity was not cleaned up before due to // identity caching for unsafe ntlm authentication - IDisposable? identity = _authenticatedConnection == null ? null : _authenticatedConnection.Identity as IDisposable; + IDisposable? identity = AuthenticatedConnection?.Identity as IDisposable; if ((identity != null) && - (_authenticatedConnection!.Identity.AuthenticationType == AuthenticationTypes.NTLM) && + (AuthenticatedConnection!.Identity.AuthenticationType == AuthenticationTypes.NTLM) && (listener.UnsafeConnectionNtlmAuthentication)) { identity.Dispose(); @@ -1879,32 +1659,11 @@ private void HandleDisconnect() Debug.Assert(oldValue == 2, $"Expected OwnershipState of 2, saw {oldValue}."); } - internal WindowsPrincipal? AuthenticatedConnection - { - get - { - return _authenticatedConnection; - } + internal WindowsPrincipal? AuthenticatedConnection { get; set; } - set - { - // The previous value can't be disposed because it may be in use by the app. - _authenticatedConnection = value; - } - } - - internal NTAuthentication? Session - { - get - { - return _session; - } + internal NegotiateAuthentication? Session { get; set; } - set - { - _session = value; - } - } + internal string? SessionPackage { get; set; } } } } diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerContext.Windows.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerContext.Windows.cs index 2a45381fc2262d..010b702d6225fc 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerContext.Windows.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpListenerContext.Windows.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.Net.Security; using System.Net.WebSockets; using System.Runtime.InteropServices; using System.Security.Authentication.ExtendedProtection; @@ -89,7 +90,7 @@ internal void Close() { IDisposable? user = _user == null ? null : _user.Identity as IDisposable; - // For unsafe connection ntlm auth we dont dispose this identity as yet since its cached + // For unsafe connection ntlm auth we don't dispose this identity as yet since its cached if ((user != null) && (_user!.Identity!.AuthenticationType != NegotiationInfoClass.NTLM) && (!_listener!.UnsafeConnectionNtlmAuthentication))