public Authorization Authenticate(string challenge, NetworkCredential credential, object sessionCookie, string spn, ChannelBinding channelBindingToken) { lock (this.sessions) { NTAuthentication clientContext = this.sessions[sessionCookie] as NTAuthentication; if (clientContext == null) { if (credential == null){ return null; } // this.sessions[sessionCookie] = clientContext = new NTAuthentication(false, "WDigest", credential, spn, ContextFlags.Connection, channelBindingToken); } string resp = clientContext.GetOutgoingBlob(challenge); if (!clientContext.IsCompleted) { return new Authorization(resp, false); } else { this.sessions.Remove(sessionCookie); return new Authorization(resp, true); } } }
public Authorization Authenticate(string challenge, NetworkCredential credential, object sessionCookie, string spn, ChannelBinding channelBindingToken) { Authorization authorization; if (Logging.On) { Logging.Enter(Logging.Web, this, "Authenticate", (string) null); } try { lock (this.sessions) { NTAuthentication clientContext = this.sessions[sessionCookie] as NTAuthentication; if (clientContext == null) { if (credential == null) { return null; } this.sessions[sessionCookie] = clientContext = new NTAuthentication(false, "Negotiate", credential, spn, ContextFlags.AcceptStream | ContextFlags.Connection, channelBindingToken); } string token = null; if (!clientContext.IsCompleted) { SecurityStatus status; byte[] incomingBlob = null; if (challenge != null) { incomingBlob = Convert.FromBase64String(challenge); } byte[] inArray = clientContext.GetOutgoingBlob(incomingBlob, false, out status); if (clientContext.IsCompleted && (inArray == null)) { token = "\r\n"; } if (inArray != null) { token = Convert.ToBase64String(inArray); } } else { token = this.GetSecurityLayerOutgoingBlob(challenge, clientContext); } authorization = new Authorization(token, clientContext.IsCompleted); } } finally { if (Logging.On) { Logging.Exit(Logging.Web, this, "Authenticate", (string) null); } } return authorization; }
public Authorization Authenticate(string challenge, NetworkCredential credential, object sessionCookie, string spn, ChannelBinding channelBindingToken) { lock (this.sessions) { NTAuthentication authentication = this.sessions[sessionCookie] as NTAuthentication; if (authentication == null) { if (credential == null) { return null; } this.sessions[sessionCookie] = authentication = new NTAuthentication(false, "WDigest", credential, spn, ContextFlags.Connection, channelBindingToken); } string outgoingBlob = authentication.GetOutgoingBlob(challenge); if (!authentication.IsCompleted) { return new Authorization(outgoingBlob, false); } this.sessions.Remove(sessionCookie); return new Authorization(outgoingBlob, true); } }
private Authorization DoAuthenticate(string challenge, WebRequest webRequest, ICredentials credentials, bool preAuthenticate) { if (credentials == null) { return null; } HttpWebRequest request = webRequest as HttpWebRequest; NTAuthentication securityContext = null; string incomingBlob = null; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return null; } int startIndex = index + SignatureSize; if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { index = -1; } if ((index >= 0) && (challenge.Length > startIndex)) { index = challenge.IndexOf(',', startIndex); if (index != -1) { incomingBlob = challenge.Substring(startIndex, index - startIndex); } else { incomingBlob = challenge.Substring(startIndex); } } securityContext = request.CurrentAuthenticationState.GetSecurityContext(this); } if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(request.ChallengedUri, Signature); string str2 = string.Empty; if ((credential == null) || (!(credential is SystemNetworkCredential) && ((str2 = credential.InternalGetUserName()).Length == 0))) { return null; } if (((str2.Length + credential.InternalGetPassword().Length) + credential.InternalGetDomain().Length) > 0x20f) { return null; } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(request.ChallengedUri, request, credential, this)) { return null; } string computeSpn = request.CurrentAuthenticationState.GetComputeSpn(request); ChannelBinding channelBinding = null; if (request.CurrentAuthenticationState.TransportContext != null) { channelBinding = request.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("NTLM", credential, computeSpn, request, channelBinding); request.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string outgoingBlob = securityContext.GetOutgoingBlob(incomingBlob); if (outgoingBlob == null) { return null; } bool unsafeOrProxyAuthenticatedConnectionSharing = request.UnsafeOrProxyAuthenticatedConnectionSharing; if (unsafeOrProxyAuthenticatedConnectionSharing) { request.LockConnection = true; } request.NtlmKeepAlive = incomingBlob == null; return AuthenticationManager.GetGroupAuthorization(this, "NTLM " + outgoingBlob, securityContext.IsCompleted, securityContext, unsafeOrProxyAuthenticatedConnectionSharing, false); }
private Authorization XPDoAuthenticate(string challenge, HttpWebRequest httpWebRequest, ICredentials credentials, bool preAuthenticate) { NTAuthentication securityContext = null; string incomingBlob = null; SecurityStatus status; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return(null); } securityContext = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this); incomingBlob = RefineDigestChallenge(challenge, index); } else { HttpDigestChallenge challenge2 = challengeCache.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri) as HttpDigestChallenge; if (challenge2 == null) { return(null); } challenge2 = challenge2.CopyAndIncrementNonce(); challenge2.SetFromRequest(httpWebRequest); incomingBlob = challenge2.ToBlob(); } UriComponents uriParts = 0; if (httpWebRequest.CurrentMethod.ConnectRequest) { uriParts = UriComponents.HostAndPort; } else if (httpWebRequest.UsesProxySemantics) { uriParts = UriComponents.HttpRequestUrl; } else { uriParts = UriComponents.PathAndQuery; } string parts = httpWebRequest.GetRemoteResourceUri().GetParts(uriParts, UriFormat.UriEscaped); if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(httpWebRequest.ChallengedUri, Signature); if ((credential == null) || (!(credential is SystemNetworkCredential) && (credential.InternalGetUserName().Length == 0))) { return(null); } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(httpWebRequest.ChallengedUri, httpWebRequest, credential, this)) { return(null); } string computeSpn = httpWebRequest.CurrentAuthenticationState.GetComputeSpn(httpWebRequest); ChannelBinding channelBinding = null; if (httpWebRequest.CurrentAuthenticationState.TransportContext != null) { channelBinding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("WDigest", credential, computeSpn, httpWebRequest, channelBinding); httpWebRequest.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string str4 = securityContext.GetOutgoingDigestBlob(incomingBlob, httpWebRequest.CurrentMethod.Name, parts, null, false, false, out status); if (str4 == null) { return(null); } Authorization authorization = new Authorization("Digest " + str4, securityContext.IsCompleted, string.Empty, securityContext.IsMutualAuthFlag); if (!preAuthenticate && httpWebRequest.PreAuthenticate) { HttpDigestChallenge challenge3 = HttpDigest.Interpret(incomingBlob, -1, httpWebRequest); string[] strArray = (challenge3.Domain == null) ? new string[] { httpWebRequest.ChallengedUri.GetParts(UriComponents.SchemeAndServer, UriFormat.UriEscaped) } : challenge3.Domain.Split(singleSpaceArray); authorization.ProtectionRealm = (challenge3.Domain == null) ? null : strArray; for (int i = 0; i < strArray.Length; i++) { challengeCache.Add(strArray[i], challenge3); } } return(authorization); }
internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlags requestedContextFlags, ChannelBinding channelBinding) { this.thisPtr = thisPtr; this.isServer = isServer; this.package = package; this.credential = credential; this.spn = spn; this.requestedContextFlags = requestedContextFlags; this.channelBinding = channelBinding; }
public static Authorization Authenticate(string challenge, WebRequest webRequest, ICredentials credentials) { GlobalLog.Print("XPDigestClient::Authenticate(): " + challenge); GlobalLog.Assert(credentials != null, "XPDigestClient::Authenticate() credentials==null", ""); if (credentials == null) { return(null); } HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; GlobalLog.Assert(httpWebRequest != null, "XPDigestClient::Authenticate() 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 // return(null); } int index = AuthenticationManager.FindSubstringNotInQuotes(challenge.ToLower(CultureInfo.InvariantCulture), DigestClient.Signature); if (index < 0) { return(null); } 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); } NTAuthentication authSession = sessions[httpWebRequest.CurrentAuthenticationState] as NTAuthentication; GlobalLog.Print("XPDigestClient::Authenticate() key:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " retrieved authSession:" + ValidationHelper.HashString(authSession)); if (authSession == null) { NetworkCredential NC = credentials.GetCredential(httpWebRequest.ChallengedUri, DigestClient.Signature); GlobalLog.Print("XPDigestClient::Authenticate() GetCredential() returns:" + ValidationHelper.ToString(NC)); if (NC == null) { return(null); } string username = NC.UserName; if (username == null || (username.Length == 0 && !(NC is SystemNetworkCredential))) { return(null); } authSession = new NTAuthentication( "WDigest", NC, httpWebRequest.ChallengedUri.AbsolutePath, httpWebRequest.DelegationFix); GlobalLog.Print("XPDigestClient::Authenticate() adding authSession:" + ValidationHelper.HashString(authSession) + " for:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState)); sessions.Add(httpWebRequest.CurrentAuthenticationState, authSession); } bool handshakeComplete; string clientResponse = authSession.GetOutgoingDigestBlob(incoming, httpWebRequest.CurrentMethod, out handshakeComplete); GlobalLog.Print("XPDigestClient::Authenticate() GetOutgoingDigestBlob(" + incoming + ") returns:" + ValidationHelper.ToString(clientResponse)); GlobalLog.Assert(handshakeComplete, "XPDigestClient::Authenticate() handshakeComplete==false", ""); if (!handshakeComplete) { return(null); } // completion is decided in Update() Authorization finalAuthorization = new Authorization(DigestClient.AuthType + " " + clientResponse, false); return(finalAuthorization); }
private Authorization DoAuthenticate(string challenge, WebRequest webRequest, ICredentials credentials, bool preAuthenticate) { GlobalLog.Print("NtlmClient::DoAuthenticate() challenge:[" + ValidationHelper.ToString(challenge) + "] webRequest#" + ValidationHelper.HashString(webRequest) + " credentials#" + ValidationHelper.HashString(credentials) + " preAuthenticate:" + preAuthenticate.ToString()); GlobalLog.Assert(credentials != null, "NtlmClient::DoAuthenticate()|credentials == null"); if (credentials == null) { return null; } HttpWebRequest httpWebRequest = webRequest as HttpWebRequest; GlobalLog.Assert(httpWebRequest != null, "NtlmClient::DoAuthenticate()|httpWebRequest == null"); GlobalLog.Assert(httpWebRequest.ChallengedUri != null, "NtlmClient::DoAuthenticate()|httpWebRequest.ChallengedUri == null"); NTAuthentication authSession = null; string incoming = null; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return null; } int blobBegin = index + SignatureSize; // // 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) { // Strip other modules information in case of multiple challenges // i.e do not take ", NTLM" as part of the following Negotiate blob // Negotiate TlRMTVNTUAACAAAADgAOADgAAAA1wo ... MAbwBmAHQALgBjAG8AbQAAAAAA,NTLM index = challenge.IndexOf(',', blobBegin); if (index != -1) incoming = challenge.Substring(blobBegin, index - blobBegin); else incoming = challenge.Substring(blobBegin); } authSession = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this); GlobalLog.Print("NtlmClient::DoAuthenticate() key:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " retrieved authSession:" + ValidationHelper.HashString(authSession)); } if (authSession==null) { NetworkCredential NC = credentials.GetCredential(httpWebRequest.ChallengedUri, Signature); GlobalLog.Print("NtlmClient::DoAuthenticate() GetCredential() returns:" + ValidationHelper.ToString(NC)); string username = string.Empty; if (NC == null || (!(NC is SystemNetworkCredential) && (username = NC.InternalGetUserName()).Length == 0)) { return null; } ICredentialPolicy policy = AuthenticationManager.CredentialPolicy; if (policy != null && !policy.ShouldSendCredential(httpWebRequest.ChallengedUri, httpWebRequest, NC, this)) return null; SpnToken spn = httpWebRequest.CurrentAuthenticationState.GetComputeSpn(httpWebRequest); GlobalLog.Print("NtlmClient::Authenticate() ChallengedSpn:" + ValidationHelper.ToString(spn)); ChannelBinding binding = null; if (httpWebRequest.CurrentAuthenticationState.TransportContext != null) { binding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } authSession = new NTAuthentication( AuthType, NC, spn, httpWebRequest, binding); GlobalLog.Print("NtlmClient::DoAuthenticate() setting SecurityContext for:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " to authSession:" + ValidationHelper.HashString(authSession)); httpWebRequest.CurrentAuthenticationState.SetSecurityContext(authSession, this); } string clientResponse = authSession.GetOutgoingBlob(incoming); if (clientResponse==null) { return null; } bool canShareConnection = httpWebRequest.UnsafeOrProxyAuthenticatedConnectionSharing; if (canShareConnection) { httpWebRequest.LockConnection = true; } // this is the first leg of an NTLM handshake, // set the NtlmKeepAlive override *STRICTLY* only in this case. httpWebRequest.NtlmKeepAlive = incoming==null; return AuthenticationManager.GetGroupAuthorization(this, AuthType + " " + clientResponse, authSession.IsCompleted, authSession, canShareConnection, false); }
private ArrayList BuildChallenge(AuthenticationSchemes authenticationScheme, ulong connectionId, out NTAuthentication newContext, ExtendedProtectionPolicy policy, bool isSecureConnection) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "AuthenticationScheme:" + authenticationScheme.ToString()); ArrayList challenges = null; newContext = null; if ((authenticationScheme & AuthenticationSchemes.Negotiate) != 0) { AddChallenge(ref challenges, AuthConstants.Negotiate); } if ((authenticationScheme & AuthenticationSchemes.Ntlm) != 0) { AddChallenge(ref challenges, AuthConstants.NTLM); } if ((authenticationScheme & AuthenticationSchemes.Digest) != 0) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "WDigest"); throw new NotImplementedException(); } if ((authenticationScheme & AuthenticationSchemes.Basic) != 0) { AddChallenge(ref challenges, "Basic realm =\"" + Realm + "\""); } return challenges; }
// internal void ValidateCreateContext( string package, bool isServer, NetworkCredential credential, string servicePrincipalName, ChannelBinding channelBinding, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel ) { if (_Exception != null && !_CanRetryAuthentication) { throw _Exception; } if (_Context != null && _Context.IsValidContext) { throw new InvalidOperationException(SR.GetString(SR.net_auth_reauth)); } if (credential == null) { throw new ArgumentNullException("credential"); } if (servicePrincipalName == null) { throw new ArgumentNullException("servicePrincipalName"); } if (impersonationLevel != TokenImpersonationLevel.Identification && impersonationLevel != TokenImpersonationLevel.Impersonation && impersonationLevel != TokenImpersonationLevel.Delegation) { throw new ArgumentOutOfRangeException("impersonationLevel", impersonationLevel.ToString(), SR.GetString(SR.net_auth_supported_impl_levels)); } if (_Context != null && IsServer != isServer) { throw new InvalidOperationException(SR.GetString(SR.net_auth_client_server)); } _Exception = null; _RemoteOk = false; _Framer = new StreamFramer(_InnerStream); _Framer.WriteHeader.MessageId = FrameHeader.HandshakeId; _ExpectedProtectionLevel = protectionLevel; _ExpectedImpersonationLevel = isServer? impersonationLevel: TokenImpersonationLevel.None; _WriteSequenceNumber = 0; _ReadSequenceNumber = 0; ContextFlags flags = ContextFlags.Connection; // A workaround for the client when talking to Win9x on the server side if (protectionLevel == ProtectionLevel.None && !isServer) { package = NegotiationInfoClass.NTLM; } else if (protectionLevel == ProtectionLevel.EncryptAndSign) { flags |= ContextFlags.Confidentiality; } else if (protectionLevel == ProtectionLevel.Sign) { // Assuming user expects NT4 SP4 and above flags |= ContextFlags.ReplayDetect | ContextFlags.SequenceDetect | ContextFlags.InitIntegrity; } if (isServer) { if (_ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { flags |= ContextFlags.AllowMissingBindings; } if (_ExtendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never && _ExtendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy) { flags |= ContextFlags.ProxyBindings; } } else { // According to lzhu server side should not request any of these flags if (protectionLevel != ProtectionLevel.None) {flags |= ContextFlags.MutualAuth;} if (impersonationLevel == TokenImpersonationLevel.Identification) {flags |= ContextFlags.InitIdentify;} if (impersonationLevel == TokenImpersonationLevel.Delegation) {flags |= ContextFlags.Delegate;} } _CanRetryAuthentication = false; // // Security: We used to rely on NetworkCredential class to demand permission // Switched over to explicit ControlPrincipalPermission demand (except for DefaultCredential case) // The mitigated attack is brute-force pasword guessing through SSPI. if (!(credential is SystemNetworkCredential)) ExceptionHelper.ControlPrincipalPermission.Demand(); try { // _Context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding); } catch (Win32Exception e) { throw new AuthenticationException(SR.GetString(SR.net_auth_SSPI), e); } }
private bool CheckSpn(NTAuthentication context, bool isSecureConnection, System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy policy) { if (context.IsKerberos) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_no_spn_kerberos")); } return true; } if (policy.PolicyEnforcement == PolicyEnforcement.Never) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_no_spn_disabled")); } return true; } if (ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_no_spn_cbt")); } return true; } if (!AuthenticationManager.OSSupportsExtendedProtection) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_no_spn_platform")); } return true; } string clientSpecifiedSpn = context.ClientSpecifiedSpn; if (string.IsNullOrEmpty(clientSpecifiedSpn)) { if (policy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_no_spn_whensupported")); } return true; } if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_spn_failed_always")); } return false; } if (string.Compare(clientSpecifiedSpn, "http/localhost", StringComparison.OrdinalIgnoreCase) == 0) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_no_spn_loopback")); } return true; } if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_spn", new object[] { clientSpecifiedSpn })); } ServiceNameCollection serviceNames = this.GetServiceNames(policy); bool flag = false; foreach (string str2 in serviceNames) { if (string.Compare(clientSpecifiedSpn, str2, StringComparison.OrdinalIgnoreCase) == 0) { flag = true; if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_spn_passed")); } break; } } if (Logging.On && !flag) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_spn_failed")); if (serviceNames.Count == 0) { Logging.PrintWarning(Logging.HttpListener, this, "CheckSpn", SR.GetString("net_log_listener_spn_failed_empty")); return flag; } Logging.PrintInfo(Logging.HttpListener, this, SR.GetString("net_log_listener_spn_failed_dump")); foreach (string str3 in serviceNames) { Logging.PrintInfo(Logging.HttpListener, this, "\t" + str3); } } return flag; }
internal HttpListenerContext HandleAuthentication(RequestContextBase memoryBlob, out bool stoleBlob) { if (NetEventSource.IsEnabled) NetEventSource.Info(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 = Interop.HttpApi.GetVerb(memoryBlob.RequestBlob); string authorizationHeader = Interop.HttpApi.GetKnownHeader(memoryBlob.RequestBlob, (int)HttpRequestHeader.Authorization); ulong connectionId = memoryBlob.RequestBlob->ConnectionId; ulong requestId = memoryBlob.RequestBlob->RequestId; bool isSecureConnection = memoryBlob.RequestBlob->pSslInfo != null; if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"HandleAuthentication() authorizationHeader: ({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; DisconnectResults.TryGetValue(connectionId, out disconnectResult); if (UnsafeConnectionNtlmAuthentication) { if (authorizationHeader == null) { WindowsPrincipal principal = disconnectResult?.AuthenticatedConnection; if (principal != null) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Principal: {principal} principal.Identity.Name: {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. if (NetEventSource.IsEnabled) NetEventSource.Info(this, "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 = _extendedProtectionPolicy; try { // Take over handling disconnects for now. if (disconnectResult != null && !disconnectResult.StartOwningDisconnectHandling()) { // 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); AuthenticationSchemeSelector authenticationSelector = _authenticationDelegate; if (authenticationSelector != null) { try { httpContext.Request.ReleasePins(); authenticationScheme = authenticationSelector(httpContext.Request); // Cache the results of authenticationSelector (if any) httpContext.AuthenticationSchemes = authenticationScheme; if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"AuthenticationScheme: {authenticationScheme}"); } catch (Exception exception) when (!ExceptionCheck.IsFatal(exception)) { if (NetEventSource.IsEnabled) { NetEventSource.Error(this, SR.Format(SR.net_log_listener_delegate_exception, exception)); NetEventSource.Info(this, $"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 = _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, AuthenticationTypes.Negotiate, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Negotiate; } else if ((authenticationScheme & AuthenticationSchemes.Ntlm) != AuthenticationSchemes.None && string.Compare(authorizationHeader, 0, AuthenticationTypes.NTLM, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Ntlm; } else if ((authenticationScheme & AuthenticationSchemes.Basic) != AuthenticationSchemes.None && string.Compare(authorizationHeader, 0, AuthenticationTypes.Basic, 0, index, StringComparison.OrdinalIgnoreCase) == 0) { headerScheme = AuthenticationSchemes.Basic; } else { if (NetEventSource.IsEnabled) NetEventSource.Error(this, SR.Format(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 (NetEventSource.IsEnabled) NetEventSource.Error(this, SR.Format(SR.net_log_listener_unmatched_authentication_scheme, authenticationScheme.ToString(), (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; SecurityStatusPal statusCodeNew; ChannelBinding binding; if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Performing Authentication headerScheme: {headerScheme}"); switch (headerScheme) { case AuthenticationSchemes.Negotiate: case AuthenticationSchemes.Ntlm: if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"context: {oldContext} for connectionId: {connectionId}"); string package = headerScheme == AuthenticationSchemes.Ntlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Negotiate; if (oldContext != null && oldContext.Package == package) { context = oldContext; } else { binding = GetChannelBinding(connectionId, isSecureConnection, extendedProtectionPolicy); ContextFlagsPal contextFlags = GetContextFlags(extendedProtectionPolicy, isSecureConnection); context = new NTAuthentication(true, package, CredentialCache.DefaultNetworkCredentials, null, contextFlags, binding); } try { bytes = Convert.FromBase64String(inBlob); } catch (FormatException) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"FormatException from FormBase64String"); httpError = HttpStatusCode.BadRequest; error = true; } if (!error) { decodedOutgoingBlob = context.GetOutgoingBlob(bytes, false, out statusCodeNew); if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"GetOutgoingBlob returned IsCompleted: {context.IsCompleted} and statusCodeNew: {statusCodeNew}"); error = !context.IsValidContext; 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); } } if (decodedOutgoingBlob != null) { outBlob = Convert.ToBase64String(decodedOutgoingBlob); } if (!error) { if (context.IsCompleted) { SecurityContextTokenHandle userContext = null; try { if (!CheckSpn(context, isSecureConnection, extendedProtectionPolicy)) { httpError = HttpStatusCode.Unauthorized; } else { httpContext.Request.ServiceName = context.ClientSpecifiedSpn; SafeDeleteContext securityContext = context.GetContext(out statusCodeNew); if (statusCodeNew.ErrorCode != SecurityStatusPalErrorCode.OK) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"HandleAuthentication GetContextToken failed with statusCodeNew: {statusCodeNew}"); } httpError = HttpStatusFromSecurityStatus(statusCodeNew.ErrorCode); } else { SSPIWrapper.QuerySecurityContextToken(GlobalSSPI.SSPIAuth, securityContext, out userContext); if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"HandleAuthentication creating new WindowsIdentity from user context: {userContext.DangerousGetHandle().ToString("x8")}"); } 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 (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"HandleAuthentication inserting principal: {principal} for connectionId: {connectionId}"); } // We may need to call WaitForDisconnect. if (disconnectResult == null) { RegisterForDisconnectNotification(connectionId, ref disconnectResult); } if (disconnectResult != null) { lock ((DisconnectResults as ICollection).SyncRoot) { if (UnsafeConnectionNtlmAuthentication) { disconnectResult.AuthenticatedConnection = windowsPrincipal; } } } else { // Registration failed - UnsafeConnectionNtlmAuthentication ignored. if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"HandleAuthentication RegisterForDisconnectNotification failed."); } } } } } } finally { if (userContext != null) { userContext.Close(); } } } else { // auth incomplete newContext = context; challenge = (headerScheme == AuthenticationSchemes.Ntlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Negotiate); if (!String.IsNullOrEmpty(outBlob)) { challenge += " " + outBlob; } } } break; case AuthenticationSchemes.Basic: try { bytes = Convert.FromBase64String(inBlob); inBlob = WebHeaderEncoding.GetString(bytes, 0, bytes.Length); index = inBlob.IndexOf(':'); if (index != -1) { string userName = inBlob.Substring(0, index); string password = inBlob.Substring(index + 1); if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Basic Identity found, userName: {userName}"); } principal = new GenericPrincipal(new HttpListenerBasicIdentity(userName, password), null); } else { httpError = HttpStatusCode.BadRequest; } } catch (FormatException) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"FromBase64String threw a FormatException."); } } break; } if (principal != null) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, $"Got principal: {principal}, IdentityName: {principal.Identity.Name} for creating request."); } httpContext.SetIdentity(principal, outBlob); } else { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, "Handshake has 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) { if (NetEventSource.IsEnabled) NetEventSource.Info(this, "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; } } if (NetEventSource.IsEnabled) NetEventSource.Info(this, "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) { toClose.CloseContext(); } } // Send the 401 here. if (httpContext == null) { SendError(requestId, challenges != null && challenges.Count > 0 ? HttpStatusCode.Unauthorized : HttpStatusCode.Forbidden, challenges); if (NetEventSource.IsEnabled) NetEventSource.Info(this, "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; } 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(); } } } }
private bool CheckSpn(NTAuthentication context, bool isSecureConnection, ExtendedProtectionPolicy policy) { // Kerberos does SPN check already in ASC if (context.IsKerberos) { if (NetEventSource.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.IsEnabled) { NetEventSource.Info(this, SR.net_log_listener_no_spn_disabled); } return true; } if (ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) { if (NetEventSource.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.IsEnabled) { NetEventSource.Info(this, SR.net_log_listener_no_spn_whensupported); } return true; } else { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, SR.net_log_listener_spn_failed_always); } return false; } } else if (string.Equals(clientSpn, "http/localhost", StringComparison.OrdinalIgnoreCase)) { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, SR.net_log_listener_no_spn_loopback); } return true; } else { if (NetEventSource.IsEnabled) { NetEventSource.Info(this, SR.net_log_listener_spn, clientSpn); } ServiceNameCollection serviceNames = GetServiceNames(policy); bool found = serviceNames.Contains(clientSpn); if (NetEventSource.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.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; } }
internal void ValidateCreateContext(string package, bool isServer, NetworkCredential credential, string servicePrincipalName, ChannelBinding channelBinding, ProtectionLevel protectionLevel, TokenImpersonationLevel impersonationLevel) { if ((this._Exception != null) && !this._CanRetryAuthentication) { throw this._Exception; } if ((this._Context != null) && this._Context.IsValidContext) { throw new InvalidOperationException(SR.GetString("net_auth_reauth")); } if (credential == null) { throw new ArgumentNullException("credential"); } if (servicePrincipalName == null) { throw new ArgumentNullException("servicePrincipalName"); } if (ComNetOS.IsWin9x && (protectionLevel != ProtectionLevel.None)) { throw new NotSupportedException(SR.GetString("net_auth_no_protection_on_win9x")); } if (((impersonationLevel != TokenImpersonationLevel.Identification) && (impersonationLevel != TokenImpersonationLevel.Impersonation)) && (impersonationLevel != TokenImpersonationLevel.Delegation)) { throw new ArgumentOutOfRangeException("impersonationLevel", impersonationLevel.ToString(), SR.GetString("net_auth_supported_impl_levels")); } if ((this._Context != null) && (this.IsServer != isServer)) { throw new InvalidOperationException(SR.GetString("net_auth_client_server")); } this._Exception = null; this._RemoteOk = false; this._Framer = new StreamFramer(this._InnerStream); this._Framer.WriteHeader.MessageId = 0x16; this._ExpectedProtectionLevel = protectionLevel; this._ExpectedImpersonationLevel = isServer ? impersonationLevel : TokenImpersonationLevel.None; this._WriteSequenceNumber = 0; this._ReadSequenceNumber = 0; ContextFlags connection = ContextFlags.Connection; if ((protectionLevel == ProtectionLevel.None) && !isServer) { package = "NTLM"; } else if (protectionLevel == ProtectionLevel.EncryptAndSign) { connection |= ContextFlags.Confidentiality; } else if (protectionLevel == ProtectionLevel.Sign) { connection |= ContextFlags.AcceptStream | ContextFlags.SequenceDetect | ContextFlags.ReplayDetect; } if (isServer) { if (this._ExtendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported) { connection |= ContextFlags.AllowMissingBindings; } if ((this._ExtendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never) && (this._ExtendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy)) { connection |= ContextFlags.ProxyBindings; } } else { if (protectionLevel != ProtectionLevel.None) { connection |= ContextFlags.MutualAuth; } if (impersonationLevel == TokenImpersonationLevel.Identification) { connection |= ContextFlags.AcceptIntegrity; } if (impersonationLevel == TokenImpersonationLevel.Delegation) { connection |= ContextFlags.Delegate; } } this._CanRetryAuthentication = false; if (!(credential is SystemNetworkCredential)) { ExceptionHelper.ControlPrincipalPermission.Demand(); } try { this._Context = new NTAuthentication(isServer, package, credential, servicePrincipalName, connection, channelBinding); } catch (Win32Exception exception) { throw new AuthenticationException(SR.GetString("net_auth_SSPI"), exception); } }
private Authorization DoAuthenticate(string challenge, WebRequest webRequest, ICredentials credentials, bool preAuthenticate) { if (credentials == null) { return null; } HttpWebRequest request = webRequest as HttpWebRequest; NTAuthentication securityContext = null; string incomingBlob = null; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return null; } int startIndex = index + SignatureSize; if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { index = -1; } if ((index >= 0) && (challenge.Length > startIndex)) { index = challenge.IndexOf(',', startIndex); if (index != -1) { incomingBlob = challenge.Substring(startIndex, index - startIndex); } else { incomingBlob = challenge.Substring(startIndex); } } securityContext = request.CurrentAuthenticationState.GetSecurityContext(this); } if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(request.ChallengedUri, Signature); if ((credential == null) || (!(credential is SystemNetworkCredential) && (credential.InternalGetUserName().Length == 0))) { return null; } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(request.ChallengedUri, request, credential, this)) { return null; } string computeSpn = request.CurrentAuthenticationState.GetComputeSpn(request); ChannelBinding channelBinding = null; if (request.CurrentAuthenticationState.TransportContext != null) { channelBinding = request.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("Kerberos", credential, computeSpn, request, channelBinding); request.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string outgoingBlob = securityContext.GetOutgoingBlob(incomingBlob); if (outgoingBlob == null) { return null; } return new Authorization("Kerberos " + outgoingBlob, securityContext.IsCompleted, string.Empty, securityContext.IsMutualAuthFlag); }
private string GetSecurityLayerOutgoingBlob(string challenge, NTAuthentication clientContext) { int num; if (challenge == null) { return null; } byte[] buffer = Convert.FromBase64String(challenge); try { num = clientContext.VerifySignature(buffer, 0, buffer.Length); } catch (Win32Exception) { return null; } if (((num < 4) || (buffer[0] != 1)) || (((buffer[1] != 0) || (buffer[2] != 0)) || (buffer[3] != 0))) { return null; } byte[] output = null; try { num = clientContext.MakeSignature(buffer, 0, 4, ref output); } catch (Win32Exception) { return null; } return Convert.ToBase64String(output, 0, num); }
private void SaveDigestContext(NTAuthentication digestContext) { if (this.m_SavedDigests == null) { Interlocked.CompareExchange<DigestContext[]>(ref this.m_SavedDigests, new DigestContext[0x400], null); } NTAuthentication context = null; ArrayList extraSavedDigestsBaking = null; lock (this.m_SavedDigests) { int num; if (!this.IsListening) { digestContext.CloseContext(); return; } num = ((num = Environment.TickCount) == 0) ? 1 : num; this.m_NewestContext = (this.m_NewestContext + 1) & 0x3ff; int timestamp = this.m_SavedDigests[this.m_NewestContext].timestamp; context = this.m_SavedDigests[this.m_NewestContext].context; this.m_SavedDigests[this.m_NewestContext].timestamp = num; this.m_SavedDigests[this.m_NewestContext].context = digestContext; if (this.m_OldestContext == this.m_NewestContext) { this.m_OldestContext = (this.m_NewestContext + 1) & 0x3ff; } while (((num - this.m_SavedDigests[this.m_OldestContext].timestamp) >= 300) && (this.m_SavedDigests[this.m_OldestContext].context != null)) { if (extraSavedDigestsBaking == null) { extraSavedDigestsBaking = new ArrayList(); } extraSavedDigestsBaking.Add(this.m_SavedDigests[this.m_OldestContext].context); this.m_SavedDigests[this.m_OldestContext].context = null; this.m_OldestContext = (this.m_OldestContext + 1) & 0x3ff; } if ((context != null) && ((num - timestamp) <= 0x2710)) { if ((this.m_ExtraSavedDigests == null) || ((num - this.m_ExtraSavedDigestsTimestamp) > 0x2710)) { extraSavedDigestsBaking = this.m_ExtraSavedDigestsBaking; this.m_ExtraSavedDigestsBaking = this.m_ExtraSavedDigests; this.m_ExtraSavedDigestsTimestamp = num; this.m_ExtraSavedDigests = new ArrayList(); } this.m_ExtraSavedDigests.Add(context); context = null; } } if (context != null) { context.CloseContext(); } if (extraSavedDigestsBaking != null) { for (int i = 0; i < extraSavedDigestsBaking.Count; i++) { ((NTAuthentication) extraSavedDigestsBaking[i]).CloseContext(); } } }
// Function for SASL security layer negotiation after // authorization completes. // // Returns null for failure, Base64 encoded string on // success. private string GetSecurityLayerOutgoingBlob( string challenge, NTAuthentication clientContext) { // must have a security layer challenge if (challenge == null) return null; // "unwrap" challenge byte[] input = Convert.FromBase64String(challenge); int len; try { len = clientContext.VerifySignature(input, 0, input.Length); } catch (Win32Exception) { // any decrypt failure is an auth failure return null; } // Per RFC 2222 Section 7.2.2: // the client should then expect the server to issue a // token in a subsequent challenge. The client passes // this token to GSS_Unwrap and interprets the first // octet of cleartext as a bit-mask specifying the // security layers supported by the server and the // second through fourth octets as the maximum size // output_message to send to the server. // Section 7.2.3 // The security layer and their corresponding bit-masks // are as follows: // 1 No security layer // 2 Integrity protection // Sender calls GSS_Wrap with conf_flag set to FALSE // 4 Privacy protection // Sender calls GSS_Wrap with conf_flag set to TRUE // // Exchange 2007 and our client only support // "No security layer". Therefore verify first byte is value 1 // and the 2nd-4th bytes are value zero since token size is not // applicable when there is no security layer. if (len < 4 || // expect 4 bytes input[0] != 1 || // first value 1 input[1] != 0 || // rest value 0 input[2] != 0 || input[3] != 0) { return null; } // Continuing with RFC 2222 section 7.2.2: // The client then constructs data, with the first octet // containing the bit-mask specifying the selected security // layer, the second through fourth octets containing in // network byte order the maximum size output_message the client // is able to receive, and the remaining octets containing the // authorization identity. // // So now this contructs the "wrapped" response. The response is // payload is identical to the received server payload and the // "authorization identity" is not supplied as it is unnecessary. // let MakeSignature figure out length of output byte[] output = null; try { len = clientContext.MakeSignature(input, 0, 4, ref output); } catch (Win32Exception) { // any decrypt failure is an auth failure return null; } // return Base64 encoded string of signed payload return Convert.ToBase64String(output, 0, len); }
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; }
public Authorization Authenticate(string challenge, NetworkCredential credential, object sessionCookie, string spn, ChannelBinding channelBindingToken) { if(Logging.On)Logging.Enter(Logging.Web, this, "Authenticate", null); try { lock (this.sessions) { NTAuthentication clientContext = this.sessions[sessionCookie] as NTAuthentication; if (clientContext == null) { if(credential == null){ return null; } this.sessions[sessionCookie] = clientContext = new NTAuthentication(false, "Negotiate", credential, spn, ContextFlags.Connection | ContextFlags.InitIntegrity, channelBindingToken); } byte[] byteResp; string resp = null; if (!clientContext.IsCompleted) { // If auth is not yet completed keep producing // challenge responses with GetOutgoingBlob SecurityStatus statusCode; byte[] decodedChallenge = null; if (challenge != null) { decodedChallenge = Convert.FromBase64String(challenge); } byteResp = clientContext.GetOutgoingBlob( decodedChallenge, false, out statusCode); // Note sure why this is here...keeping it. if (clientContext.IsCompleted && byteResp == null) { resp = "\r\n"; } if (byteResp != null) { resp = Convert.ToBase64String(byteResp); } } else { // If auth completed and still have a challenge then // server may be doing "correct" form of GSSAPI SASL. // Validate incoming and produce outgoing SASL security // layer negotiate message. resp = GetSecurityLayerOutgoingBlob( challenge, clientContext); } return new Authorization(resp, clientContext.IsCompleted); } } finally { if(Logging.On)Logging.Exit(Logging.Web, this, "Authenticate", null); } }
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 Authorization DoAuthenticate(string challenge, WebRequest webRequest, ICredentials credentials, bool preAuthenticate) { if (credentials == null) { return null; } HttpWebRequest request = webRequest as HttpWebRequest; NTAuthentication securityContext = null; string incomingBlob = null; bool flag = false; if (!preAuthenticate) { int signatureIndex = GetSignatureIndex(challenge, out flag); if (signatureIndex < 0) { return null; } int startIndex = signatureIndex + (flag ? "nego2".Length : "negotiate".Length); if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { signatureIndex = -1; } if ((signatureIndex >= 0) && (challenge.Length > startIndex)) { signatureIndex = challenge.IndexOf(',', startIndex); if (signatureIndex != -1) { incomingBlob = challenge.Substring(startIndex, signatureIndex - startIndex); } else { incomingBlob = challenge.Substring(startIndex); } } securityContext = request.CurrentAuthenticationState.GetSecurityContext(this); } if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(request.ChallengedUri, "negotiate"); if ((credential == null) || (!(credential is SystemNetworkCredential) && (credential.InternalGetUserName().Length == 0))) { return null; } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(request.ChallengedUri, request, credential, this)) { return null; } string computeSpn = request.CurrentAuthenticationState.GetComputeSpn(request); ChannelBinding channelBinding = null; if (request.CurrentAuthenticationState.TransportContext != null) { channelBinding = request.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("Negotiate", credential, computeSpn, request, channelBinding); request.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string outgoingBlob = securityContext.GetOutgoingBlob(incomingBlob); if (outgoingBlob == null) { return null; } bool unsafeOrProxyAuthenticatedConnectionSharing = request.UnsafeOrProxyAuthenticatedConnectionSharing; if (unsafeOrProxyAuthenticatedConnectionSharing) { request.LockConnection = true; } request.NtlmKeepAlive = ((incomingBlob == null) && securityContext.IsValidContext) && !securityContext.IsKerberos; return AuthenticationManager.GetGroupAuthorization(this, (flag ? "Nego2" : "Negotiate") + " " + outgoingBlob, securityContext.IsCompleted, securityContext, unsafeOrProxyAuthenticatedConnectionSharing, securityContext.IsKerberos); }
// On Windows XP and up, WDigest.dll supports the Digest authentication scheme (in addition to // support for HTTP client sides, it also supports HTTP server side and SASL) through SSPI. private Authorization XPDoAuthenticate(string challenge, HttpWebRequest httpWebRequest, ICredentials credentials, bool preAuthenticate) { GlobalLog.Print("DigestClient::XPDoAuthenticate() challenge:[" + ValidationHelper.ToString(challenge) + "] httpWebRequest#" + ValidationHelper.HashString(httpWebRequest) + " credentials#" + ValidationHelper.HashString(credentials) + " preAuthenticate:" + preAuthenticate.ToString()); NTAuthentication authSession = null; string incoming = null; int index; if (!preAuthenticate) { index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return null; } authSession = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this); GlobalLog.Print("DigestClient::XPDoAuthenticate() key:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " retrieved authSession:" + ValidationHelper.HashString(authSession)); incoming = RefineDigestChallenge(challenge, index); } else { #if WDIGEST_PREAUTH GlobalLog.Print("DigestClient::XPDoAuthenticate() looking up digestChallenge for prefix:" + httpWebRequest.ChallengedUri.AbsoluteUri); authSession = challengeCache.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri) as NTAuthentication; if (authSession==null) { return null; } #else GlobalLog.Print("DigestClient::XPDoAuthenticate() looking up digestChallenge for prefix:" + httpWebRequest.ChallengedUri.AbsoluteUri); HttpDigestChallenge digestChallenge = challengeCache.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri) as HttpDigestChallenge; if (digestChallenge==null) { return null; } GlobalLog.Print("DigestClient::XPDoAuthenticate() retrieved digestChallenge#" + ValidationHelper.HashString(digestChallenge) + " digestChallenge for prefix:" + httpWebRequest.ChallengedUri.AbsoluteUri); digestChallenge = digestChallenge.CopyAndIncrementNonce(); digestChallenge.SetFromRequest(httpWebRequest); incoming = digestChallenge.ToBlob(); #endif } UriComponents uriParts = 0; Uri remoteUri = httpWebRequest.GetRemoteResourceUri(); if (httpWebRequest.CurrentMethod.ConnectRequest) { uriParts = UriComponents.HostAndPort; // Use the orriginal request Uri, not the proxy Uri remoteUri = httpWebRequest.RequestUri; } else { uriParts = UriComponents.PathAndQuery; } // here we use Address instead of ChallengedUri. This is because the // Digest hash is generated using the uri as it is present on the wire string rawUri = remoteUri.GetParts(uriParts, UriFormat.UriEscaped); GlobalLog.Print("DigestClient::XPDoAuthenticate() rawUri:" + ValidationHelper.ToString(rawUri)); if (authSession==null) { NetworkCredential NC = credentials.GetCredential(httpWebRequest.ChallengedUri, Signature); GlobalLog.Print("DigestClient::XPDoAuthenticate() GetCredential() returns:" + ValidationHelper.ToString(NC)); if (NC == null || (!(NC is SystemNetworkCredential) && NC.InternalGetUserName().Length == 0)) { return null; } ICredentialPolicy policy = AuthenticationManager.CredentialPolicy; if (policy != null && !policy.ShouldSendCredential(httpWebRequest.ChallengedUri, httpWebRequest, NC, this)) return null; SpnToken spn = httpWebRequest.CurrentAuthenticationState.GetComputeSpn(httpWebRequest); GlobalLog.Print("NtlmClient::Authenticate() ChallengedSpn:" + ValidationHelper.ToString(spn)); ChannelBinding binding = null; if (httpWebRequest.CurrentAuthenticationState.TransportContext != null) { binding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } authSession = new NTAuthentication( NegotiationInfoClass.WDigest, NC, spn, httpWebRequest, binding); GlobalLog.Print("DigestClient::XPDoAuthenticate() setting SecurityContext for:" + ValidationHelper.HashString(httpWebRequest.CurrentAuthenticationState) + " to authSession:" + ValidationHelper.HashString(authSession)); httpWebRequest.CurrentAuthenticationState.SetSecurityContext(authSession, this); } SecurityStatus statusCode; string clientResponse; GlobalLog.Print("DigestClient::XPDoAuthenticate() incoming:" + ValidationHelper.ToString(incoming)); #if WDIGEST_PREAUTH clientResponse = authSession.GetOutgoingDigestBlob(incoming, httpWebRequest.CurrentMethod.Name, rawUri, null, preAuthenticate, true, out statusCode); #else clientResponse = authSession.GetOutgoingDigestBlob(incoming, httpWebRequest.CurrentMethod.Name, rawUri, null, false, false, out statusCode); #endif if (clientResponse == null) return null; GlobalLog.Print("DigestClient::XPDoAuthenticate() GetOutgoingDigestBlob(" + incoming + ") returns:" + ValidationHelper.ToString(clientResponse)); Authorization digestResponse = new Authorization(AuthType + " " + clientResponse, authSession.IsCompleted, string.Empty, authSession.IsMutualAuthFlag); if (!preAuthenticate && httpWebRequest.PreAuthenticate) { // add this to the cache of challenges so we can preauthenticate // use this DCR when avaialble to do this without calling HttpDigest.Interpret(): // 762115 3 WDigest should allow an application to retrieve the parsed domain directive HttpDigestChallenge digestChallenge = HttpDigest.Interpret(incoming, -1, httpWebRequest); string[] prefixes = digestChallenge.Domain==null ? new string[]{httpWebRequest.ChallengedUri.GetParts(UriComponents.SchemeAndServer, UriFormat.UriEscaped)} : digestChallenge.Domain.Split(singleSpaceArray); // If Domain property is not set we associate Digest only with the server Uri namespace (used to do with whole server) digestResponse.ProtectionRealm = digestChallenge.Domain==null ? null: prefixes; for (int i=0; i<prefixes.Length; i++) { GlobalLog.Print("DigestClient::XPDoAuthenticate() adding authSession#" + ValidationHelper.HashString(authSession) + " for prefix:" + prefixes[i]); #if WDIGEST_PREAUTH challengeCache.Add(prefixes[i], authSession); #else challengeCache.Add(prefixes[i], digestChallenge); #endif } } return digestResponse; }
private Authorization XPDoAuthenticate(string challenge, HttpWebRequest httpWebRequest, ICredentials credentials, bool preAuthenticate) { NTAuthentication securityContext = null; string incomingBlob = null; SecurityStatus status; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return null; } securityContext = httpWebRequest.CurrentAuthenticationState.GetSecurityContext(this); incomingBlob = RefineDigestChallenge(challenge, index); } else { HttpDigestChallenge challenge2 = challengeCache.Lookup(httpWebRequest.ChallengedUri.AbsoluteUri) as HttpDigestChallenge; if (challenge2 == null) { return null; } challenge2 = challenge2.CopyAndIncrementNonce(); challenge2.SetFromRequest(httpWebRequest); incomingBlob = challenge2.ToBlob(); } UriComponents uriParts = 0; if (httpWebRequest.CurrentMethod.ConnectRequest) { uriParts = UriComponents.HostAndPort; } else if (httpWebRequest.UsesProxySemantics) { uriParts = UriComponents.HttpRequestUrl; } else { uriParts = UriComponents.PathAndQuery; } string parts = httpWebRequest.GetRemoteResourceUri().GetParts(uriParts, UriFormat.UriEscaped); if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(httpWebRequest.ChallengedUri, Signature); if ((credential == null) || (!(credential is SystemNetworkCredential) && (credential.InternalGetUserName().Length == 0))) { return null; } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(httpWebRequest.ChallengedUri, httpWebRequest, credential, this)) { return null; } string computeSpn = httpWebRequest.CurrentAuthenticationState.GetComputeSpn(httpWebRequest); ChannelBinding channelBinding = null; if (httpWebRequest.CurrentAuthenticationState.TransportContext != null) { channelBinding = httpWebRequest.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("WDigest", credential, computeSpn, httpWebRequest, channelBinding); httpWebRequest.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string str4 = securityContext.GetOutgoingDigestBlob(incomingBlob, httpWebRequest.CurrentMethod.Name, parts, null, false, false, out status); if (str4 == null) { return null; } Authorization authorization = new Authorization("Digest " + str4, securityContext.IsCompleted, string.Empty, securityContext.IsMutualAuthFlag); if (!preAuthenticate && httpWebRequest.PreAuthenticate) { HttpDigestChallenge challenge3 = HttpDigest.Interpret(incomingBlob, -1, httpWebRequest); string[] strArray = (challenge3.Domain == null) ? new string[] { httpWebRequest.ChallengedUri.GetParts(UriComponents.SchemeAndServer, UriFormat.UriEscaped) } : challenge3.Domain.Split(singleSpaceArray); authorization.ProtectionRealm = (challenge3.Domain == null) ? null : strArray; for (int i = 0; i < strArray.Length; i++) { challengeCache.Add(strArray[i], challenge3); } } return authorization; }
private void SaveDigestContext(NTAuthentication digestContext) { if (_savedDigests == null) { Interlocked.CompareExchange<DigestContext[]>(ref _savedDigests, new DigestContext[MaximumDigests], null); } // We want to actually close the contexts outside the lock. NTAuthentication oldContext = null; ArrayList digestsToClose = null; lock (_savedDigests) { // If we're stopped, just throw it away. if (!IsListening) { digestContext.CloseContext(); return; } int now = ((now = Environment.TickCount) == 0 ? 1 : now); _newestContext = (_newestContext + 1) & (MaximumDigests - 1); int oldTimestamp = _savedDigests[_newestContext].timestamp; oldContext = _savedDigests[_newestContext].context; _savedDigests[_newestContext].timestamp = now; _savedDigests[_newestContext].context = digestContext; // May need to move this up. if (_oldestContext == _newestContext) { _oldestContext = (_newestContext + 1) & (MaximumDigests - 1); } // Delete additional contexts older than five minutes. while (unchecked(now - _savedDigests[_oldestContext].timestamp) >= DigestLifetimeSeconds && _savedDigests[_oldestContext].context != null) { if (digestsToClose == null) { digestsToClose = new ArrayList(); } digestsToClose.Add(_savedDigests[_oldestContext].context); _savedDigests[_oldestContext].context = null; _oldestContext = (_oldestContext + 1) & (MaximumDigests - 1); } // If the old context is younger than 10 seconds, put it in the backup pile. if (oldContext != null && unchecked(now - oldTimestamp) <= MinimumDigestLifetimeSeconds * 1000) { // Use a two-tier ArrayList system to guarantee each entry lives at least 10 seconds. if (_extraSavedDigests == null || unchecked(now - _extraSavedDigestsTimestamp) > MinimumDigestLifetimeSeconds * 1000) { digestsToClose = _extraSavedDigestsBaking; _extraSavedDigestsBaking = _extraSavedDigests; _extraSavedDigestsTimestamp = now; _extraSavedDigests = new ArrayList(); } _extraSavedDigests.Add(oldContext); oldContext = null; } } if (oldContext != null) { oldContext.CloseContext(); } if (digestsToClose != null) { for (int i = 0; i < digestsToClose.Count; i++) { ((NTAuthentication)digestsToClose[i]).CloseContext(); } } }
internal void ClearSession() { #if !FEATURE_PAL // Security GlobalLog.Print("AuthenticationState#" + ValidationHelper.HashString(this) + "::ClearSession() NTAuthentication#" + ValidationHelper.HashString(SecurityContext)); if (SecurityContext!=null) { SecurityContext.CloseContext(); SecurityContext = null; } #endif // FEATURE_PAL // Security }
internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlagsPal requestedContextFlags, ChannelBinding channelBinding) { ThisPtr = thisPtr; IsServer = isServer; Package = package; Credential = credential; Spn = spn; RequestedContextFlags = requestedContextFlags; ChannelBinding = channelBinding; }
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); }
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(); } } } }
private bool CheckSpn(NTAuthentication context, bool isSecureConnection, ExtendedProtectionPolicy policy) { // Kerberos does SPN check already in ASC if (context.IsKerberos) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(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 (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_disabled)); } return true; } if (ScenarioChecksChannelBinding(isSecureConnection, policy.ProtectionScenario)) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_cbt)); } return true; } if (!AuthenticationManager.OSSupportsExtendedProtection) { GlobalLog.Assert(policy.PolicyEnforcement != PolicyEnforcement.Always, "User managed to set PolicyEnforcement.Always when the OS does not support extended protection!"); if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_platform)); } 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 (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_whensupported)); } return true; } else { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn_failed_always)); } return false; } } else if (ServiceNameCollection.Match(clientSpn, "http/localhost")) { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_no_spn_loopback)); } return true; } else { if (Logging.On) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn, clientSpn)); } ServiceNameCollection serviceNames = GetServiceNames(policy); bool found = serviceNames.Contains(clientSpn); if (Logging.On) { if (found) { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn_passed)); } else { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn_failed)); if (serviceNames.Count == 0) { Logging.PrintWarning(Logging.HttpListener, this, "CheckSpn", SR.GetString(SR.net_log_listener_spn_failed_empty)); } else { Logging.PrintInfo(Logging.HttpListener, this, SR.GetString(SR.net_log_listener_spn_failed_dump)); foreach (string serviceName in serviceNames) { Logging.PrintInfo(Logging.HttpListener, this, "\t" + serviceName); } } } } return found; } }
internal void SetSecurityContext(NTAuthentication securityContext, IAuthenticationModule module) { GlobalLog.Print("AuthenticationState#" + ValidationHelper.HashString(this) + "::SetSecurityContext(" + module.AuthenticationType + ") was NTAuthentication#" + ValidationHelper.HashString(SecurityContext) + " now NTAuthentication#" + ValidationHelper.HashString(securityContext)); SecurityContext = securityContext; }
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 static Authorization GetGroupAuthorization( IAuthenticationModule thisModule, string token, bool finished, NTAuthentication authSession, bool shareAuthenticatedConnections, bool mutualAuth) { return new Authorization( token, finished, (shareAuthenticatedConnections) ? null : (thisModule.GetType().FullName + "/" + authSession.UniqueUserId), mutualAuth); }
private Authorization DoAuthenticate(string challenge, WebRequest webRequest, ICredentials credentials, bool preAuthenticate) { if (credentials == null) { return(null); } HttpWebRequest request = webRequest as HttpWebRequest; NTAuthentication securityContext = null; string incomingBlob = null; if (!preAuthenticate) { int index = AuthenticationManager.FindSubstringNotInQuotes(challenge, Signature); if (index < 0) { return(null); } int startIndex = index + SignatureSize; if ((challenge.Length > startIndex) && (challenge[startIndex] != ',')) { startIndex++; } else { index = -1; } if ((index >= 0) && (challenge.Length > startIndex)) { index = challenge.IndexOf(',', startIndex); if (index != -1) { incomingBlob = challenge.Substring(startIndex, index - startIndex); } else { incomingBlob = challenge.Substring(startIndex); } } securityContext = request.CurrentAuthenticationState.GetSecurityContext(this); } if (securityContext == null) { NetworkCredential credential = credentials.GetCredential(request.ChallengedUri, Signature); if ((credential == null) || (!(credential is SystemNetworkCredential) && (credential.InternalGetUserName().Length == 0))) { return(null); } ICredentialPolicy credentialPolicy = AuthenticationManager.CredentialPolicy; if ((credentialPolicy != null) && !credentialPolicy.ShouldSendCredential(request.ChallengedUri, request, credential, this)) { return(null); } string computeSpn = request.CurrentAuthenticationState.GetComputeSpn(request); ChannelBinding channelBinding = null; if (request.CurrentAuthenticationState.TransportContext != null) { channelBinding = request.CurrentAuthenticationState.TransportContext.GetChannelBinding(ChannelBindingKind.Endpoint); } securityContext = new NTAuthentication("Kerberos", credential, computeSpn, request, channelBinding); request.CurrentAuthenticationState.SetSecurityContext(securityContext, this); } string outgoingBlob = securityContext.GetOutgoingBlob(incomingBlob); if (outgoingBlob == null) { return(null); } return(new Authorization("Kerberos " + outgoingBlob, securityContext.IsCompleted, string.Empty, securityContext.IsMutualAuthFlag)); }