private bool XPUpdate(string challenge, HttpWebRequest httpWebRequest) { SecurityStatus status; NTAuthentication securityContext = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this); if (securityContext == null) { return(false); } int index = (challenge == null) ? -1 : AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { httpWebRequest.ServicePoint.SetCachedChannelBinding(httpWebRequest.ChallengedUri, securityContext.ChannelBinding); this.ClearSession(httpWebRequest); return(true); } if (httpWebRequest.ResponseStatusCode != httpWebRequest.CurrentAuthenticationState.StatusCodeMatch) { httpWebRequest.ServicePoint.SetCachedChannelBinding(httpWebRequest.ChallengedUri, securityContext.ChannelBinding); this.ClearSession(httpWebRequest); return(true); } string incomingBlob = RefineDigestChallenge(challenge, index); securityContext.GetOutgoingDigestBlob(incomingBlob, httpWebRequest.CurrentMethod.Name, null, null, false, true, out status); httpWebRequest.CurrentAuthenticationState.Authorization.MutuallyAuthenticated = securityContext.IsMutualAuthFlag; return(securityContext.IsCompleted); }
internal unsafe HttpListenerContext HandleAuthentication(RequestContextBase memoryBlob, out bool stoleBlob) { string challenge = null; HttpListenerContext context3; stoleBlob = false; string verb = UnsafeNclNativeMethods.HttpApi.GetVerb(memoryBlob.RequestBlob); string knownHeader = UnsafeNclNativeMethods.HttpApi.GetKnownHeader(memoryBlob.RequestBlob, 0x18); ulong connectionId = memoryBlob.RequestBlob.ConnectionId; ulong requestId = memoryBlob.RequestBlob.RequestId; bool isSecureConnection = memoryBlob.RequestBlob.pSslInfo != null; DisconnectAsyncResult disconnectResult = (DisconnectAsyncResult) this.DisconnectResults[connectionId]; if (this.UnsafeConnectionNtlmAuthentication) { if (knownHeader == null) { WindowsPrincipal principal = (disconnectResult == null) ? null : disconnectResult.AuthenticatedConnection; if (principal != null) { stoleBlob = true; HttpListenerContext context = new HttpListenerContext(this, memoryBlob); context.SetIdentity(principal, null); context.Request.ReleasePins(); return context; } } else if (disconnectResult != null) { disconnectResult.AuthenticatedConnection = null; } } stoleBlob = true; HttpListenerContext context2 = null; NTAuthentication digestContext = null; NTAuthentication newContext = null; NTAuthentication authentication3 = null; System.Net.AuthenticationSchemes none = System.Net.AuthenticationSchemes.None; System.Net.AuthenticationSchemes authenticationSchemes = this.AuthenticationSchemes; System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy extendedProtectionPolicy = this.m_ExtendedProtectionPolicy; try { ExtendedProtectionSelector selector; SecurityStatus invalidToken; ChannelBinding binding; string str6; ArrayList list; if ((disconnectResult != null) && !disconnectResult.StartOwningDisconnectHandling()) { disconnectResult = null; } if (disconnectResult != null) { digestContext = disconnectResult.Session; } context2 = new HttpListenerContext(this, memoryBlob); AuthenticationSelectorInfo authenticationDelegate = this.m_AuthenticationDelegate; if (authenticationDelegate != null) { try { context2.Request.ReleasePins(); authenticationSchemes = authenticationDelegate.Delegate(context2.Request); if (!authenticationDelegate.AdvancedAuth && ((authenticationSchemes & (System.Net.AuthenticationSchemes.IntegratedWindowsAuthentication | System.Net.AuthenticationSchemes.Digest)) != System.Net.AuthenticationSchemes.None)) { throw this.m_SecurityException; } goto Label_01A2; } catch (Exception exception) { if (NclUtilities.IsFatal(exception)) { throw; } if (Logging.On) { Logging.PrintError(Logging.HttpListener, this, "HandleAuthentication", SR.GetString("net_log_listener_delegate_exception", new object[] { exception })); } this.SendError(requestId, HttpStatusCode.InternalServerError, null); context2.Close(); return null; } } stoleBlob = false; Label_01A2: selector = this.m_ExtendedProtectionSelectorDelegate; if (selector != null) { extendedProtectionPolicy = selector(context2.Request); if (extendedProtectionPolicy == null) { extendedProtectionPolicy = new System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy(PolicyEnforcement.Never); } } int length = -1; if ((knownHeader != null) && ((authenticationSchemes & ~System.Net.AuthenticationSchemes.Anonymous) != System.Net.AuthenticationSchemes.None)) { length = 0; while (length < knownHeader.Length) { if (((knownHeader[length] == ' ') || (knownHeader[length] == '\t')) || ((knownHeader[length] == '\r') || (knownHeader[length] == '\n'))) { break; } length++; } if (length < knownHeader.Length) { if (((authenticationSchemes & System.Net.AuthenticationSchemes.Negotiate) != System.Net.AuthenticationSchemes.None) && (string.Compare(knownHeader, 0, "Negotiate", 0, length, StringComparison.OrdinalIgnoreCase) == 0)) { none = System.Net.AuthenticationSchemes.Negotiate; } else if (((authenticationSchemes & System.Net.AuthenticationSchemes.Ntlm) != System.Net.AuthenticationSchemes.None) && (string.Compare(knownHeader, 0, "NTLM", 0, length, StringComparison.OrdinalIgnoreCase) == 0)) { none = System.Net.AuthenticationSchemes.Ntlm; } else if (((authenticationSchemes & System.Net.AuthenticationSchemes.Digest) != System.Net.AuthenticationSchemes.None) && (string.Compare(knownHeader, 0, "Digest", 0, length, StringComparison.OrdinalIgnoreCase) == 0)) { none = System.Net.AuthenticationSchemes.Digest; } else if (((authenticationSchemes & System.Net.AuthenticationSchemes.Basic) != System.Net.AuthenticationSchemes.None) && (string.Compare(knownHeader, 0, "Basic", 0, length, StringComparison.OrdinalIgnoreCase) == 0)) { none = System.Net.AuthenticationSchemes.Basic; } else if (Logging.On) { Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString("net_log_listener_unsupported_authentication_scheme", new object[] { knownHeader, authenticationSchemes })); } } } HttpStatusCode internalServerError = HttpStatusCode.InternalServerError; bool flag2 = false; if (none == System.Net.AuthenticationSchemes.None) { if (Logging.On) { Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString("net_log_listener_unmatched_authentication_scheme", new object[] { ValidationHelper.ToString(authenticationSchemes), (knownHeader == null) ? "<null>" : knownHeader })); } if ((authenticationSchemes & System.Net.AuthenticationSchemes.Anonymous) != System.Net.AuthenticationSchemes.None) { if (!stoleBlob) { stoleBlob = true; context2.Request.ReleasePins(); } return context2; } internalServerError = HttpStatusCode.Unauthorized; context2.Request.DetachBlob(memoryBlob); context2.Close(); context2 = null; goto Label_07AA; } byte[] bytes = null; byte[] inArray = null; string str4 = null; length++; while (length < knownHeader.Length) { if (((knownHeader[length] != ' ') && (knownHeader[length] != '\t')) && ((knownHeader[length] != '\r') && (knownHeader[length] != '\n'))) { break; } length++; } string incomingBlob = (length < knownHeader.Length) ? knownHeader.Substring(length) : ""; IPrincipal principal2 = null; switch (none) { case System.Net.AuthenticationSchemes.Digest: { binding = this.GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); authentication3 = new NTAuthentication(true, "WDigest", null, this.GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding); str4 = authentication3.GetOutgoingDigestBlob(incomingBlob, verb, null, this.Realm, false, false, out invalidToken); if (invalidToken == SecurityStatus.OK) { str4 = null; } if (!authentication3.IsValidContext) { break; } SafeCloseHandle contextToken = null; try { if (!this.CheckSpn(authentication3, isSecureConnection, extendedProtectionPolicy)) { internalServerError = HttpStatusCode.Unauthorized; } else { context2.Request.ServiceName = authentication3.ClientSpecifiedSpn; contextToken = authentication3.GetContextToken(out invalidToken); if (invalidToken != SecurityStatus.OK) { internalServerError = this.HttpStatusFromSecurityStatus(invalidToken); } else if (contextToken == null) { internalServerError = HttpStatusCode.Unauthorized; } else { principal2 = new WindowsPrincipal(this.CreateWindowsIdentity(contextToken.DangerousGetHandle(), "Digest", WindowsAccountType.Normal, true)); } } } finally { if (contextToken != null) { contextToken.Close(); } } newContext = authentication3; if (str4 != null) { challenge = "Digest " + str4; } goto Label_0761; } case System.Net.AuthenticationSchemes.Negotiate: case System.Net.AuthenticationSchemes.Ntlm: str6 = (none == System.Net.AuthenticationSchemes.Ntlm) ? "NTLM" : "Negotiate"; if ((digestContext == null) || !(digestContext.Package == str6)) { goto Label_0549; } authentication3 = digestContext; goto Label_056D; case System.Net.AuthenticationSchemes.Basic: try { bytes = Convert.FromBase64String(incomingBlob); incomingBlob = WebHeaderCollection.HeaderEncoding.GetString(bytes, 0, bytes.Length); length = incomingBlob.IndexOf(':'); if (length != -1) { string username = incomingBlob.Substring(0, length); string password = incomingBlob.Substring(length + 1); principal2 = new GenericPrincipal(new HttpListenerBasicIdentity(username, password), null); } else { internalServerError = HttpStatusCode.BadRequest; } } catch (FormatException) { } goto Label_0761; default: goto Label_0761; } internalServerError = this.HttpStatusFromSecurityStatus(invalidToken); goto Label_0761; Label_0549: binding = this.GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); authentication3 = new NTAuthentication(true, str6, null, this.GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding); Label_056D: try { bytes = Convert.FromBase64String(incomingBlob); } catch (FormatException) { internalServerError = HttpStatusCode.BadRequest; flag2 = true; } if (!flag2) { inArray = authentication3.GetOutgoingBlob(bytes, false, out invalidToken); flag2 = !authentication3.IsValidContext; if (flag2) { if (((invalidToken == SecurityStatus.InvalidHandle) && (digestContext == null)) && ((bytes != null) && (bytes.Length > 0))) { invalidToken = SecurityStatus.InvalidToken; } internalServerError = this.HttpStatusFromSecurityStatus(invalidToken); } } if (inArray != null) { str4 = Convert.ToBase64String(inArray); } if (!flag2) { if (authentication3.IsCompleted) { SafeCloseHandle handle2 = null; try { if (!this.CheckSpn(authentication3, isSecureConnection, extendedProtectionPolicy)) { internalServerError = HttpStatusCode.Unauthorized; } else { context2.Request.ServiceName = authentication3.ClientSpecifiedSpn; handle2 = authentication3.GetContextToken(out invalidToken); if (invalidToken != SecurityStatus.OK) { internalServerError = this.HttpStatusFromSecurityStatus(invalidToken); } else { WindowsPrincipal principal3 = new WindowsPrincipal(this.CreateWindowsIdentity(handle2.DangerousGetHandle(), authentication3.ProtocolName, WindowsAccountType.Normal, true)); principal2 = principal3; if (this.UnsafeConnectionNtlmAuthentication && (authentication3.ProtocolName == "NTLM")) { if (disconnectResult == null) { this.RegisterForDisconnectNotification(connectionId, ref disconnectResult); } if (disconnectResult != null) { lock (this.DisconnectResults.SyncRoot) { if (this.UnsafeConnectionNtlmAuthentication) { disconnectResult.AuthenticatedConnection = principal3; } } } } } } goto Label_0761; } finally { if (handle2 != null) { handle2.Close(); } } } newContext = authentication3; challenge = (none == System.Net.AuthenticationSchemes.Ntlm) ? "NTLM" : "Negotiate"; if (!string.IsNullOrEmpty(str4)) { challenge = challenge + " " + str4; } } Label_0761: if (principal2 != null) { context2.SetIdentity(principal2, str4); } else { if (Logging.On) { Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString("net_log_listener_create_valid_identity_failed")); } context2.Request.DetachBlob(memoryBlob); context2.Close(); context2 = null; } Label_07AA: list = null; if (context2 == null) { if (challenge != null) { AddChallenge(ref list, challenge); } else { if (newContext != null) { if (newContext == authentication3) { authentication3 = null; } if (newContext != digestContext) { NTAuthentication authentication4 = newContext; newContext = null; authentication4.CloseContext(); } else { newContext = null; } } if (internalServerError != HttpStatusCode.Unauthorized) { this.SendError(requestId, internalServerError, null); return null; } list = this.BuildChallenge(authenticationSchemes, connectionId, out newContext, extendedProtectionPolicy, isSecureConnection); } } if ((disconnectResult == null) && (newContext != null)) { this.RegisterForDisconnectNotification(connectionId, ref disconnectResult); if (disconnectResult == null) { if (newContext != null) { if (newContext == authentication3) { authentication3 = null; } if (newContext != digestContext) { NTAuthentication authentication5 = newContext; newContext = null; authentication5.CloseContext(); } else { newContext = null; } } this.SendError(requestId, HttpStatusCode.InternalServerError, null); context2.Request.DetachBlob(memoryBlob); context2.Close(); return null; } } if (digestContext != newContext) { if (digestContext == authentication3) { authentication3 = null; } NTAuthentication authentication6 = digestContext; digestContext = newContext; disconnectResult.Session = newContext; if (authentication6 != null) { if ((authenticationSchemes & System.Net.AuthenticationSchemes.Digest) != System.Net.AuthenticationSchemes.None) { this.SaveDigestContext(authentication6); } else { authentication6.CloseContext(); } } } if (context2 == null) { this.SendError(requestId, ((list != null) && (list.Count > 0)) ? HttpStatusCode.Unauthorized : HttpStatusCode.Forbidden, list); return null; } if (!stoleBlob) { stoleBlob = true; context2.Request.ReleasePins(); } context3 = context2; } catch { if (context2 != null) { context2.Request.DetachBlob(memoryBlob); context2.Close(); } if (newContext != null) { if (newContext == authentication3) { authentication3 = null; } if (newContext != digestContext) { NTAuthentication authentication7 = newContext; newContext = null; authentication7.CloseContext(); } else { newContext = null; } } throw; } finally { try { if ((digestContext != null) && (digestContext != newContext)) { if ((newContext == null) && (disconnectResult != null)) { disconnectResult.Session = null; } if ((authenticationSchemes & System.Net.AuthenticationSchemes.Digest) != System.Net.AuthenticationSchemes.None) { this.SaveDigestContext(digestContext); } else { digestContext.CloseContext(); } } if (((authentication3 != null) && (digestContext != authentication3)) && (newContext != authentication3)) { authentication3.CloseContext(); } } finally { if (disconnectResult != null) { disconnectResult.FinishOwningDisconnectHandling(); } } } return context3; }
private ArrayList BuildChallenge(System.Net.AuthenticationSchemes authenticationScheme, ulong connectionId, out NTAuthentication newContext, System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy policy, bool isSecureConnection) { ArrayList challenges = null; newContext = null; if ((authenticationScheme & System.Net.AuthenticationSchemes.Negotiate) != System.Net.AuthenticationSchemes.None) { AddChallenge(ref challenges, "Negotiate"); } if ((authenticationScheme & System.Net.AuthenticationSchemes.Ntlm) != System.Net.AuthenticationSchemes.None) { AddChallenge(ref challenges, "NTLM"); } if ((authenticationScheme & System.Net.AuthenticationSchemes.Digest) != System.Net.AuthenticationSchemes.None) { NTAuthentication authentication = null; try { SecurityStatus status; string str = null; ChannelBinding channelBinding = this.GetChannelBinding(connectionId, isSecureConnection, policy); authentication = new NTAuthentication(true, "WDigest", null, this.GetContextFlags(policy, isSecureConnection), channelBinding); str = authentication.GetOutgoingDigestBlob(null, null, null, this.Realm, false, false, out status); if (authentication.IsValidContext) { newContext = authentication; } AddChallenge(ref challenges, "Digest" + (string.IsNullOrEmpty(str) ? "" : (" " + str))); } finally { if ((authentication != null) && (newContext != authentication)) { authentication.CloseContext(); } } } if ((authenticationScheme & System.Net.AuthenticationSchemes.Basic) != System.Net.AuthenticationSchemes.None) { AddChallenge(ref challenges, "Basic realm=\"" + this.Realm + "\""); } return challenges; }
private ArrayList BuildChallenge(AuthenticationSchemes authenticationScheme, ulong connectionId, out NTAuthentication newContext, ExtendedProtectionPolicy policy, bool isSecureConnection) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::BuildChallenge() authenticationScheme:" + authenticationScheme.ToString()); ArrayList challenges = null; newContext = null; if ((authenticationScheme & AuthenticationSchemes.Negotiate) != 0) { AddChallenge(ref challenges, NegotiateClient.AuthType); } if ((authenticationScheme & AuthenticationSchemes.Ntlm) != 0) { AddChallenge(ref challenges, NtlmClient.AuthType); } if ((authenticationScheme & AuthenticationSchemes.Digest) != 0) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::BuildChallenge() package:WDigest"); NTAuthentication context = null; try { string outBlob = null; ChannelBinding binding = GetChannelBinding(connectionId, isSecureConnection, policy); context = new NTAuthentication(true, NegotiationInfoClass.WDigest, null, GetContextFlags(policy, isSecureConnection), binding); SecurityStatus statusCode; outBlob = context.GetOutgoingDigestBlob(null, null, null, Realm, false, false, out statusCode); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::BuildChallenge() GetOutgoingDigestBlob() returned IsCompleted:" + context.IsCompleted + " statusCode:" + statusCode + " outBlob:[" + outBlob + "]"); if (context.IsValidContext) { newContext = context; } AddChallenge(ref challenges, DigestClient.AuthType + (string.IsNullOrEmpty(outBlob) ? "" : " " + outBlob)); } finally { if (context != null && newContext != context) { context.CloseContext(); } } } if ((authenticationScheme & AuthenticationSchemes.Basic) != 0) { AddChallenge(ref challenges, BasicClient.AuthType + " realm=\"" + Realm + "\""); } return challenges; }
internal HttpListenerContext HandleAuthentication(RequestContextBase memoryBlob, out bool stoleBlob) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() memoryBlob:0x" + ((IntPtr) memoryBlob.RequestBlob).ToString("x")); string challenge = null; stoleBlob = false; // Some things we need right away. Lift them out now while it's convenient. string verb = UnsafeNclNativeMethods.HttpApi.GetVerb(memoryBlob.RequestBlob); string authorizationHeader = UnsafeNclNativeMethods.HttpApi.GetKnownHeader(memoryBlob.RequestBlob, (int) HttpRequestHeader.Authorization); ulong connectionId = memoryBlob.RequestBlob->ConnectionId; ulong requestId = memoryBlob.RequestBlob->RequestId; bool isSecureConnection = memoryBlob.RequestBlob->pSslInfo != null; GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() authorizationHeader:" + ValidationHelper.ToString(authorizationHeader)); // if the app has turned on AuthPersistence, an anonymous request might // be authenticated by virtue of it coming on a connection that was // previously authenticated. // assurance that we do this only for NTLM/Negotiate is not here, but in the // code that caches WindowsIdentity instances in the Dictionary. DisconnectAsyncResult disconnectResult = (DisconnectAsyncResult) DisconnectResults[connectionId]; if (UnsafeConnectionNtlmAuthentication) { if (authorizationHeader == null) { WindowsPrincipal principal = disconnectResult == null ? null : disconnectResult.AuthenticatedConnection; if (principal != null) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() got principal:" + ValidationHelper.ToString(principal) + " principal.Identity.Name:" + ValidationHelper.ToString(principal.Identity.Name) + " creating request"); stoleBlob = true; HttpListenerContext ntlmContext = new HttpListenerContext(this, memoryBlob); ntlmContext.SetIdentity(principal, null); ntlmContext.Request.ReleasePins(); return ntlmContext; } } else { // They sent an authorization - destroy their previous credentials. GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() clearing principal cache"); if (disconnectResult != null) { disconnectResult.AuthenticatedConnection = null; } } } // 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; AuthenticationSchemes headerScheme = AuthenticationSchemes.None; AuthenticationSchemes authenticationScheme = AuthenticationSchemes; ExtendedProtectionPolicy extendedProtectionPolicy = m_ExtendedProtectionPolicy; try { // Take over handling disconnects for now. if (disconnectResult != null && !disconnectResult.StartOwningDisconnectHandling()) { // Oops! Just disconnected just then. Pretend we didn't see the disconnectResult. disconnectResult = null; } // 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; } httpContext = new HttpListenerContext(this, memoryBlob); AuthenticationSelectorInfo authenticationSelector = m_AuthenticationDelegate; if (authenticationSelector != null) { try { httpContext.Request.ReleasePins(); authenticationScheme = authenticationSelector.Delegate(httpContext.Request); // Cache the results of authenticationSelector (if any) httpContext.AuthenticationSchemes = authenticationScheme; if (!authenticationSelector.AdvancedAuth && (authenticationScheme & (AuthenticationSchemes.Negotiate | AuthenticationSchemes.Ntlm | AuthenticationSchemes.Digest)) != 0) { throw m_SecurityException; } GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() AuthenticationSchemeSelectorDelegate() returned authenticationScheme:" + authenticationScheme); } catch (Exception exception) { if (NclUtilities.IsFatal(exception)) throw; if (Logging.On) Logging.PrintError(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_delegate_exception, exception)); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() AuthenticationSchemeSelectorDelegate() returned authenticationScheme:" + authenticationScheme); SendError(requestId, HttpStatusCode.InternalServerError, null); httpContext.Close(); return null; } } else { // We didn't give the request to the user yet, so we haven't lost control of the unmanaged blob and can // continue to reuse the buffer. stoleBlob = false; } ExtendedProtectionSelector extendedProtectionSelector = m_ExtendedProtectionSelectorDelegate; if (extendedProtectionSelector != null) { extendedProtectionPolicy = extendedProtectionSelector(httpContext.Request); if (extendedProtectionPolicy == null) { extendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never); } // Cache the results of extendedProtectionSelector (if any) httpContext.ExtendedProtectionPolicy = extendedProtectionPolicy; } // Then figure out what scheme they're trying (if any are allowed) int index = -1; if (authorizationHeader != null && (authenticationScheme & ~AuthenticationSchemes.Anonymous) != AuthenticationSchemes.None) { // Find the end of the scheme name. Trust that HTTP.SYS parsed out just our header ok. for (index = 0; index < authorizationHeader.Length; index++) { if (authorizationHeader[index] == ' ' || authorizationHeader[index] == '\t' || authorizationHeader[index] == '\r' || authorizationHeader[index] == '\n') { break; } } // Currently only allow one Authorization scheme/header per request. if (index < authorizationHeader.Length) { if ((authenticationScheme & AuthenticationSchemes.Negotiate) != AuthenticationSchemes.None && string.Compare(authorizationHeader, 0, NegotiateClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Negotiate; } else if ((authenticationScheme & AuthenticationSchemes.Ntlm) != AuthenticationSchemes.None && string.Compare(authorizationHeader, 0, NtlmClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Ntlm; } else if ((authenticationScheme & AuthenticationSchemes.Digest) != AuthenticationSchemes.None && string.Compare(authorizationHeader, 0, DigestClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Digest; } else if ((authenticationScheme & AuthenticationSchemes.Basic) != AuthenticationSchemes.None && string.Compare(authorizationHeader, 0, BasicClient.AuthType, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Basic; } else { if (Logging.On) Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_unsupported_authentication_scheme, authorizationHeader , authenticationScheme)); } } } // httpError holds the error we will return if an Authorization header is present but can't be authenticated HttpStatusCode httpError = HttpStatusCode.InternalServerError; bool error = false; // See if we found an acceptable auth header if (headerScheme == AuthenticationSchemes.None) { if (Logging.On) Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_unmatched_authentication_scheme, ValidationHelper.ToString(authenticationScheme), (authorizationHeader == null ? "<null>" : authorizationHeader))); // If anonymous is allowed, just return the context. Otherwise go for the 401. if ((authenticationScheme & AuthenticationSchemes.Anonymous) != AuthenticationSchemes.None) { if (!stoleBlob) { stoleBlob = true; httpContext.Request.ReleasePins(); } return httpContext; } httpError = HttpStatusCode.Unauthorized; httpContext.Request.DetachBlob(memoryBlob); httpContext.Close(); httpContext = null; } else { // Perform Authentication byte[] bytes = null; byte[] decodedOutgoingBlob = null; string outBlob = null; // Find the beginning of the blob. Trust that HTTP.SYS parsed out just our header ok. for (index++; index < authorizationHeader.Length; index++) { if (authorizationHeader[index] != ' ' && authorizationHeader[index] != '\t' && authorizationHeader[index] != '\r' && authorizationHeader[index] != '\n') { break; } } string inBlob = index < authorizationHeader.Length ? authorizationHeader.Substring(index) : ""; IPrincipal principal = null; SecurityStatus statusCodeNew; ChannelBinding binding; GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() Performing Authentication headerScheme:" + ValidationHelper.ToString(headerScheme)); switch (headerScheme) { case AuthenticationSchemes.Digest: GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() package:WDigest headerScheme:" + headerScheme); // WDigest had some weird behavior. This is what I have discovered: // Local accounts don't work, only domain accounts. The domain (i.e. REDMOND) is implied. Not sure how it is chosen. // If the domain is specified and the credentials are correct, it works. If they're not (domain, username or password): // AcceptSecurityContext (GetOutgoingDigestBlob) returns success but with a bogus 4k challenge, and // QuerySecurityContextToken (GetContextToken) fails with NoImpersonation. // If the domain isn't specified, AcceptSecurityContext returns NoAuthenticatingAuthority for a bad username, // and LogonDenied for a bad password. // Also interesting is that WDigest requires us to keep a reference to the previous context, but fails if we // actually pass it in! (It't ok to pass it in for the first request, but not if nc > 1.) For Whidbey, // we create a new context and associate it with the connection, just like NTLM, but instead of using it for // the next request on the connection, we always create a new context and swap the old one out. As long // as we keep the old one around until after we authenticate with the new one, it works. For this reason, // we also keep these contexts around past the lifetime of the connection, so that KeepAlive=false works. binding = GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); context = new NTAuthentication(true, NegotiationInfoClass.WDigest, null, GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() verb:" + verb + " context.IsValidContext:" + context.IsValidContext.ToString()); outBlob = context.GetOutgoingDigestBlob(inBlob, verb, null, Realm, false, false, out statusCodeNew); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetOutgoingDigestBlob() returned IsCompleted:" + context.IsCompleted + " statusCodeNew:" + statusCodeNew + " outBlob:[" + outBlob + "]"); // WDigest bug: sometimes when AcceptSecurityContext returns success, it provides a bogus, empty 4k buffer. // Ignore it. (Should find out what's going on here from WDigest people.) if (statusCodeNew == SecurityStatus.OK) { outBlob = null; } if (context.IsValidContext) { SafeCloseHandle userContext = null; try { if (!CheckSpn(context, isSecureConnection, extendedProtectionPolicy)) { httpError = HttpStatusCode.Unauthorized; } else { httpContext.Request.ServiceName = context.ClientSpecifiedSpn; userContext = context.GetContextToken(out statusCodeNew); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetContextToken() returned:" + statusCodeNew.ToString()); if (statusCodeNew != SecurityStatus.OK) { httpError = HttpStatusFromSecurityStatus(statusCodeNew); } else if (userContext == null) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() error: GetContextToken() returned:null statusCodeNew:" + statusCodeNew.ToString()); httpError = HttpStatusCode.Unauthorized; } else { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() creating new WindowsIdentity() from userContext:" + userContext.DangerousGetHandle().ToString("x8")); principal = new WindowsPrincipal(CreateWindowsIdentity(userContext.DangerousGetHandle(), DigestClient.AuthType, WindowsAccountType.Normal, true)); } } } finally { if (userContext!=null) { userContext.Close(); } } newContext = context; if (outBlob != null) { challenge = DigestClient.AuthType + " " + outBlob; } } else { httpError = HttpStatusFromSecurityStatus(statusCodeNew); } break; case AuthenticationSchemes.Negotiate: case AuthenticationSchemes.Ntlm: GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() headerScheme:" + headerScheme); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() returned context#" + ValidationHelper.HashString(oldContext) + " for connectionId:" + connectionId); string package = headerScheme == AuthenticationSchemes.Ntlm ? NtlmClient.AuthType : NegotiateClient.AuthType; if (oldContext != null && oldContext.Package == package) { context = oldContext; } else { binding = GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); context = new NTAuthentication(true, package, null, GetContextFlags(extendedProtectionPolicy, isSecureConnection), binding); } try { bytes = Convert.FromBase64String(inBlob); } catch (FormatException) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() FromBase64String threw a FormatException."); httpError = HttpStatusCode.BadRequest; error = true; } if (!error) { decodedOutgoingBlob = context.GetOutgoingBlob(bytes, false, out statusCodeNew); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetOutgoingBlob() returned IsCompleted:" + context.IsCompleted + " statusCodeNew:" + statusCodeNew); error = !context.IsValidContext; if (error) { // Bug #474228: 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 == SecurityStatus.InvalidHandle && oldContext == null && bytes != null && bytes.Length > 0) { statusCodeNew = SecurityStatus.InvalidToken; } httpError = HttpStatusFromSecurityStatus(statusCodeNew); } } if (decodedOutgoingBlob!=null) { outBlob = Convert.ToBase64String(decodedOutgoingBlob); } if (!error) { if (context.IsCompleted) { SafeCloseHandle userContext = null; try { if (!CheckSpn(context, isSecureConnection, extendedProtectionPolicy)) { httpError = HttpStatusCode.Unauthorized; } else { httpContext.Request.ServiceName = context.ClientSpecifiedSpn; userContext = context.GetContextToken(out statusCodeNew); if (statusCodeNew != SecurityStatus.OK) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() GetContextToken() failed with statusCodeNew:" + statusCodeNew.ToString()); httpError = HttpStatusFromSecurityStatus(statusCodeNew); } else { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() creating new WindowsIdentity() from userContext:" + userContext.DangerousGetHandle().ToString("x8")); WindowsPrincipal windowsPrincipal = new WindowsPrincipal(CreateWindowsIdentity(userContext.DangerousGetHandle(), context.ProtocolName, WindowsAccountType.Normal, true)); principal = windowsPrincipal; // if appropriate, cache this credential on this connection if (UnsafeConnectionNtlmAuthentication && context.ProtocolName == NegotiationInfoClass.NTLM) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() inserting principal#" + ValidationHelper.HashString(principal) + " for connectionId:" + connectionId); // We may need to call WaitForDisconnect. if (disconnectResult == null) { RegisterForDisconnectNotification(connectionId, ref disconnectResult); } if (disconnectResult != null) { lock (DisconnectResults.SyncRoot) { if (UnsafeConnectionNtlmAuthentication) { disconnectResult.AuthenticatedConnection = windowsPrincipal; } } } else { // Registration failed - UnsafeConnectionNtlmAuthentication ignored. GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() RegisterForDisconnectNotification() failed."); } } } } } finally { if (userContext!=null) { userContext.Close(); } } } else { // auth incomplete newContext = context; challenge = (headerScheme==AuthenticationSchemes.Ntlm ? NtlmClient.AuthType : NegotiateClient.AuthType); if (!String.IsNullOrEmpty(outBlob)) { challenge += " " + outBlob; } } } break; case AuthenticationSchemes.Basic: try { bytes = Convert.FromBase64String(inBlob); inBlob = WebHeaderCollection.HeaderEncoding.GetString(bytes, 0, bytes.Length); index = inBlob.IndexOf(':'); if (index!=-1) { string userName = inBlob.Substring(0, index); string password = inBlob.Substring(index+1); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() basic identity found, userName:"******"HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() FromBase64String threw a FormatException."); } break; } if (principal != null) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() got principal:" + ValidationHelper.ToString(principal) + " principal.Identity.Name:" + ValidationHelper.ToString(principal.Identity.Name) + " creating request"); httpContext.SetIdentity(principal, outBlob); } else { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() handshake has failed"); if(Logging.On)Logging.PrintWarning(Logging.HttpListener, this, "HandleAuthentication", SR.GetString(SR.net_log_listener_create_valid_identity_failed)); httpContext.Request.DetachBlob(memoryBlob); httpContext.Close(); httpContext = null; } } // if we're not giving a request to the application, we need to send an error ArrayList challenges = null; if (httpContext == null) { // If we already have a challenge, just use it. Otherwise put a challenge for each acceptable scheme. if (challenge != null) { AddChallenge(ref challenges, challenge); } 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; } } // If we're sending something besides 401, do it here. if (httpError != HttpStatusCode.Unauthorized) { GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() failed context#" + ValidationHelper.HashString(context) + " for connectionId:" + connectionId + " because of error:" + httpError.ToString()); SendError(requestId, httpError, null); return null; } challenges = BuildChallenge(authenticationScheme, connectionId, out newContext, extendedProtectionPolicy, isSecureConnection); } } // 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) { RegisterForDisconnectNotification(connectionId, ref disconnectResult); // Failed - send 500. if (disconnectResult == null) { if (newContext != null) { if (newContext == context) { context = null; } if (newContext != oldContext) { NTAuthentication toClose = newContext; newContext = null; toClose.CloseContext(); } else { newContext = null; } } GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() failed context#" + ValidationHelper.HashString(context) + " for connectionId:" + connectionId + " because of failed HttpWaitForDisconnect"); SendError(requestId, HttpStatusCode.InternalServerError, null); httpContext.Request.DetachBlob(memoryBlob); httpContext.Close(); 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; disconnectResult.Session = newContext; if (toClose != null) { // Save digest context in digest cache, we may need it later because of // subsequest responses to the same req on the same/diff connection if ((authenticationScheme & AuthenticationSchemes.Digest) != 0) { SaveDigestContext(toClose); } else { toClose.CloseContext(); } } } // Send the 401 here. if (httpContext == null) { SendError(requestId, challenges != null && challenges.Count > 0 ? HttpStatusCode.Unauthorized : HttpStatusCode.Forbidden, challenges); GlobalLog.Print("HttpListener#" + ValidationHelper.HashString(this) + "::HandleAuthentication() SendUnauthorized(Scheme:" + authenticationScheme + ")"); return null; } if (!stoleBlob) { stoleBlob = true; httpContext.Request.ReleasePins(); } return httpContext; } catch { if (httpContext != null) { httpContext.Request.DetachBlob(memoryBlob); httpContext.Close(); } 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; } } throw; } finally { try { // Clean up the previous context if necessary. if (oldContext != null && oldContext != newContext) { // Clear out Session if it wasn't already. if (newContext == null && disconnectResult != null) { disconnectResult.Session = null; } // Save digest context in digest cache, we may need it later because of // subsequest responses to the same req on the same/diff connection if ((authenticationScheme & AuthenticationSchemes.Digest) != 0) { SaveDigestContext(oldContext); } else { oldContext.CloseContext(); } } // Delete any context created but not stored. if (context != null && oldContext != context && newContext != context) { context.CloseContext(); } } finally { // Check if the connection got deleted while in this method, and clear out the hashtables if it did. // In a nested finally because if this doesn't happen, we leak. if (disconnectResult != null) { disconnectResult.FinishOwningDisconnectHandling(); } } } }
public static bool Update(string challenge, WebRequest webRequest) { GlobalLog.Print("XPDigestClient::Update(): " + challenge); HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; GlobalLog.Assert(httpWebRequest != null, "XPDigestClient::Update() httpWebRequest==null", ""); if (httpWebRequest == null || httpWebRequest.ChallengedUri == null) { // // there has been no challenge: // 1) the request never went on the wire // 2) somebody other than us is calling into AuthenticationManager // GlobalLog.Print("XPDigestClient::Update(): no request. returning true"); return(true); } NTAuthentication authSession = sessions[httpWebRequest.CurrentAuthenticationState] as NTAuthentication; GlobalLog.Print("XPDigestClient::Update() key:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " retrieved authSession:" + ValidationHelper.HashString(authSession)); if (authSession == null) { return(false); } int index = challenge == null ? -1 : AuthenticationManager.FindSubstringNotInQuotes(challenge.ToLower(CultureInfo.InvariantCulture), DigestClient.Signature); if (index < 0) { GlobalLog.Print("XPDigestClient::Update(): no challenge. returning true"); return(true); } // here's how we know if the handshake is complete when we get the response back, // (keeping in mind that we need to support stale credentials): // !40X - complete & success // 40X & stale=false - complete & failure // 40X & stale=true - !complete if (httpWebRequest.ResponseStatusCode != httpWebRequest.CurrentAuthenticationState.StatusCodeMatch) { GlobalLog.Print("XPDigestClient::Update() removing authSession:" + ValidationHelper.HashString(authSession) + " from:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState)); sessions.Remove(httpWebRequest.CurrentAuthenticationState); GlobalLog.Print("XPDigestClient::Update(): no status code match. returning true"); return(true); } int blobBegin = index + DigestClient.SignatureSize; string incoming = null; // // there may be multiple challenges. If the next character after the // package name is not a comma then it is challenge data // if (challenge.Length > blobBegin && challenge[blobBegin] != ',') { ++blobBegin; } else { index = -1; } if (index >= 0 && challenge.Length > blobBegin) { incoming = challenge.Substring(blobBegin); } // we should get here only on invalid or stale credentials: bool handshakeComplete; string clientResponse = authSession.GetOutgoingDigestBlob(incoming, httpWebRequest.CurrentMethod, out handshakeComplete); GlobalLog.Print("XPDigestClient::Update() GetOutgoingDigestBlob(" + incoming + ") returns:" + ValidationHelper.ToString(clientResponse)); GlobalLog.Assert(handshakeComplete, "XPDigestClient::Update() handshakeComplete==false", ""); GlobalLog.Print("XPDigestClient::Update() GetOutgoingBlob() returns clientResponse:[" + ValidationHelper.ToString(clientResponse) + "] handshakeComplete:" + handshakeComplete.ToString()); return(handshakeComplete); }