/// <summary> /// Handle windows NTLM/Kerberos authentication. /// Note: NTLM/Kerberos cannot do a man in middle operation /// we do for HTTPS requests. /// As such we will be sending local credentials of current /// User to server to authenticate requests. /// To disable this set ProxyServer.EnableWinAuth to false. /// </summary> private async Task handle401UnAuthorized(SessionEventArgs args) { string headerName = null; HttpHeader authHeader = null; var response = args.HttpClient.Response; // check in non-unique headers first var header = response.Headers.NonUniqueHeaders.FirstOrDefault(x => authHeaderNames.Contains(x.Key)); if (!header.Equals(new KeyValuePair <string, List <HttpHeader> >())) { headerName = header.Key; } if (headerName != null) { authHeader = response.Headers.NonUniqueHeaders[headerName] .FirstOrDefault( x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase))); } // check in unique headers if (authHeader == null) { headerName = null; // check in non-unique headers first var uHeader = response.Headers.Headers.FirstOrDefault(x => authHeaderNames.Contains(x.Key)); if (!uHeader.Equals(new KeyValuePair <string, HttpHeader>())) { headerName = uHeader.Key; } if (headerName != null) { authHeader = authSchemes.Any(x => response.Headers.Headers[headerName].Value .StartsWith(x, StringComparison.OrdinalIgnoreCase)) ? response.Headers.Headers[headerName] : null; } } if (authHeader != null) { string scheme = authSchemes.Contains(authHeader.Value) ? authHeader.Value : null; var expectedAuthState = scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED; if (!WinAuthEndPoint.ValidateWinAuthState(args.HttpClient.Data, expectedAuthState)) { // Invalid state, create proper error message to client await rewriteUnauthorizedResponse(args); return; } var request = args.HttpClient.Request; // clear any existing headers to avoid confusing bad servers request.Headers.RemoveHeader(KnownHeaders.Authorization); // initial value will match exactly any of the schemes if (scheme != null) { string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host, scheme, args.HttpClient.Data); string auth = string.Concat(scheme, clientToken); // replace existing authorization header if any request.Headers.SetOrAddHeaderValue(KnownHeaders.Authorization, auth); // don't need to send body for Authorization request if (request.HasBody) { request.ContentLength = 0; } } else { // challenge value will start with any of the scheme selected scheme = authSchemes.First(x => authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) && authHeader.Value.Length > x.Length + 1); string serverToken = authHeader.Value.Substring(scheme.Length + 1); string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host, serverToken, args.HttpClient.Data); string auth = string.Concat(scheme, clientToken); // there will be an existing header from initial client request request.Headers.SetOrAddHeaderValue(KnownHeaders.Authorization, auth); // send body for final auth request if (request.OriginalHasBody) { request.ContentLength = request.Body.Length; } args.HttpClient.Connection.IsWinAuthenticated = true; } // Need to revisit this. // Should we cache all Set-Cookie headers from server during auth process // and send it to client after auth? // Let ResponseHandler send the updated request args.ReRequest = true; } }
public void Test_Acquire_Client_Token() { var token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", Guid.NewGuid()); Assert.IsTrue(token.Length > 1); }
/// <summary> /// Handle windows NTLM authentication /// Can expand this for Kerberos in future /// Note: NTLM/Kerberos cannot do a man in middle operation /// we do for HTTPS requests. /// As such we will be sending local credentials of current /// User to server to authenticate requests. /// To disable this set ProxyServer.EnableWinAuth to false /// </summary> internal async Task <bool> Handle401UnAuthorized(SessionEventArgs args) { string headerName = null; HttpHeader authHeader = null; //check in non-unique headers first var header = args.WebSession.Response .NonUniqueResponseHeaders .FirstOrDefault(x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase))); if (!header.Equals(new KeyValuePair <string, List <HttpHeader> >())) { headerName = header.Key; } if (headerName != null) { authHeader = args.WebSession.Response .NonUniqueResponseHeaders[headerName] .Where(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase))) .FirstOrDefault(); } //check in unique headers if (authHeader == null) { //check in non-unique headers first var uHeader = args.WebSession.Response .ResponseHeaders .FirstOrDefault(x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase))); if (!uHeader.Equals(new KeyValuePair <string, HttpHeader>())) { headerName = uHeader.Key; } if (headerName != null) { authHeader = authSchemes.Any(x => args.WebSession.Response .ResponseHeaders[headerName].Value.StartsWith(x, StringComparison.OrdinalIgnoreCase)) ? args.WebSession.Response.ResponseHeaders[headerName] : null; } } if (authHeader != null) { var scheme = authSchemes.FirstOrDefault(x => authHeader.Value.Equals(x, StringComparison.OrdinalIgnoreCase)); //initial value will match exactly any of the schemes if (scheme != null) { var clientToken = WinAuthHandler.GetInitialAuthToken(args.WebSession.Request.Host, scheme, args.Id); args.WebSession.Request.RequestHeaders.Add("Authorization", new HttpHeader("Authorization", string.Concat(scheme, clientToken))); } //challenge value will start with any of the scheme selected else { scheme = authSchemes.FirstOrDefault(x => authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) && authHeader.Value.Length > x.Length + 1); var serverToken = authHeader.Value.Substring(scheme.Length + 1); var clientToken = WinAuthHandler.GetFinalAuthToken(args.WebSession.Request.Host, serverToken, args.Id); args.WebSession.Request.RequestHeaders["Authorization"] = new HttpHeader("Authorization", string.Concat(scheme, clientToken)); } //clear current response await args.ClearResponse(); var disposed = await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args, false); return(disposed); } return(false); }
/// <summary> /// Handle windows NTLM authentication /// Can expand this for Kerberos in future /// Note: NTLM/Kerberos cannot do a man in middle operation /// we do for HTTPS requests. /// As such we will be sending local credentials of current /// User to server to authenticate requests. /// To disable this set ProxyServer.EnableWinAuth to false /// </summary> internal async Task <bool> Handle401UnAuthorized(SessionEventArgs args) { string headerName = null; HttpHeader authHeader = null; //check in non-unique headers first var header = args.WebSession.Response.ResponseHeaders.NonUniqueHeaders.FirstOrDefault( x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase))); if (!header.Equals(new KeyValuePair <string, List <HttpHeader> >())) { headerName = header.Key; } if (headerName != null) { authHeader = args.WebSession.Response.ResponseHeaders.NonUniqueHeaders[headerName] .FirstOrDefault(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase))); } //check in unique headers if (authHeader == null) { //check in non-unique headers first var uHeader = args.WebSession.Response.ResponseHeaders.Headers.FirstOrDefault(x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase))); if (!uHeader.Equals(new KeyValuePair <string, HttpHeader>())) { headerName = uHeader.Key; } if (headerName != null) { authHeader = authSchemes.Any(x => args.WebSession.Response.ResponseHeaders.Headers[headerName].Value .StartsWith(x, StringComparison.OrdinalIgnoreCase)) ? args.WebSession.Response.ResponseHeaders.Headers[headerName] : null; } } if (authHeader != null) { string scheme = authSchemes.FirstOrDefault(x => authHeader.Value.Equals(x, StringComparison.OrdinalIgnoreCase)); //clear any existing headers to avoid confusing bad servers if (args.WebSession.Request.RequestHeaders.NonUniqueHeaders.ContainsKey("Authorization")) { args.WebSession.Request.RequestHeaders.NonUniqueHeaders.Remove("Authorization"); } //initial value will match exactly any of the schemes if (scheme != null) { string clientToken = WinAuthHandler.GetInitialAuthToken(args.WebSession.Request.Host, scheme, args.Id); var auth = new HttpHeader("Authorization", string.Concat(scheme, clientToken)); //replace existing authorization header if any if (args.WebSession.Request.RequestHeaders.Headers.ContainsKey("Authorization")) { args.WebSession.Request.RequestHeaders.Headers["Authorization"] = auth; } else { args.WebSession.Request.RequestHeaders.Headers.Add("Authorization", auth); } //don't need to send body for Authorization request if (args.WebSession.Request.HasBody) { args.WebSession.Request.ContentLength = 0; } } //challenge value will start with any of the scheme selected else { scheme = authSchemes.FirstOrDefault(x => authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) && authHeader.Value.Length > x.Length + 1); string serverToken = authHeader.Value.Substring(scheme.Length + 1); string clientToken = WinAuthHandler.GetFinalAuthToken(args.WebSession.Request.Host, serverToken, args.Id); //there will be an existing header from initial client request args.WebSession.Request.RequestHeaders.Headers["Authorization"] = new HttpHeader("Authorization", string.Concat(scheme, clientToken)); //send body for final auth request if (args.WebSession.Request.HasBody) { args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length; } } //Need to revisit this. //Should we cache all Set-Cokiee headers from server during auth process //and send it to client after auth? //clear current server response await args.ClearResponse(); //request again with updated authorization header //and server cookies bool disposed = await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args, false); return(disposed); } return(false); }
public void Test_Acquire_Client_Token() { string token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", new InternalDataStore()); Assert.IsTrue(token.Length > 1); }