Commit 9139ab26 authored by Honfika's avatar Honfika

Use of Guid as a session identifer #427

parent 348e14ae
...@@ -164,8 +164,12 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -164,8 +164,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount); WriteToConsole("Active Client Connections:" + ((ProxyServer)sender).ClientConnectionCount);
WriteToConsole(e.WebSession.Request.Url); WriteToConsole(e.WebSession.Request.Url);
// create custom id for the request and store it in the UserData property
// It can be a simple integer, Guid, or any type
e.UserData = Guid.NewGuid();
// read request headers // read request headers
requestHeaderHistory[e.Id] = e.WebSession.Request.Headers; requestHeaderHistory[(Guid)e.UserData] = e.WebSession.Request.Headers;
////This sample shows how to get the multipart form data headers ////This sample shows how to get the multipart form data headers
//if (e.WebSession.Request.Host == "mail.yahoo.com" && e.WebSession.Request.IsMultipartFormData) //if (e.WebSession.Request.Host == "mail.yahoo.com" && e.WebSession.Request.IsMultipartFormData)
...@@ -221,7 +225,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -221,7 +225,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount); WriteToConsole("Active Server Connections:" + ((ProxyServer)sender).ServerConnectionCount);
var ext = System.IO.Path.GetExtension(e.WebSession.Request.RequestUri.AbsolutePath); string ext = System.IO.Path.GetExtension(e.WebSession.Request.RequestUri.AbsolutePath);
//if (ext == ".gif" || ext == ".png" || ext == ".jpg") //if (ext == ".gif" || ext == ".png" || ext == ".jpg")
//{ //{
......
using System; using System;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Network.WinAuth; using Titanium.Web.Proxy.Network.WinAuth;
namespace Titanium.Web.Proxy.UnitTests namespace Titanium.Web.Proxy.UnitTests
...@@ -10,7 +11,7 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -10,7 +11,7 @@ namespace Titanium.Web.Proxy.UnitTests
[TestMethod] [TestMethod]
public void Test_Acquire_Client_Token() public void Test_Acquire_Client_Token()
{ {
string token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", Guid.NewGuid()); string token = WinAuthHandler.GetInitialAuthToken("mylocalserver.com", "NTLM", new InternalDataStore());
Assert.IsTrue(token.Length > 1); Assert.IsTrue(token.Length > 1);
} }
} }
......
...@@ -74,10 +74,14 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -74,10 +74,14 @@ namespace Titanium.Web.Proxy.EventArguments
internal ProxyClient ProxyClient { get; } internal ProxyClient ProxyClient { get; }
/// <summary> /// <summary>
/// Returns a unique Id for this request/response session which is /// Returns a user data for this request/response session which is
/// same as the RequestId of WebSession. /// same as the user data of WebSession.
/// </summary> /// </summary>
public Guid Id => WebSession.RequestId; public object UserData
{
get => WebSession.UserData;
set => WebSession.UserData = value;
}
/// <summary> /// <summary>
/// Does this session uses SSL? /// Does this session uses SSL?
......
...@@ -272,10 +272,12 @@ namespace Titanium.Web.Proxy ...@@ -272,10 +272,12 @@ namespace Titanium.Web.Proxy
await connection.StreamWriter.WriteLineAsync("SM", cancellationToken); await connection.StreamWriter.WriteLineAsync("SM", cancellationToken);
await connection.StreamWriter.WriteLineAsync(cancellationToken); await connection.StreamWriter.WriteLineAsync(cancellationToken);
await TcpHelper.SendHttp2(clientStream, connection.Stream, BufferSize, #if NETCOREAPP2_1
await Http2Helper.SendHttp2(clientStream, connection.Stream, BufferSize,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); },
(buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); },
connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc); connectArgs.CancellationTokenSource, clientConnection.Id, ExceptionFunc);
#endif
} }
} }
} }
......
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
...@@ -20,7 +21,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -20,7 +21,6 @@ namespace Titanium.Web.Proxy.Http
{ {
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
RequestId = Guid.NewGuid();
Request = request ?? new Request(); Request = request ?? new Request();
Response = response ?? new Response(); Response = response ?? new Response();
} }
...@@ -31,9 +31,14 @@ namespace Titanium.Web.Proxy.Http ...@@ -31,9 +31,14 @@ namespace Titanium.Web.Proxy.Http
internal TcpServerConnection ServerConnection { get; set; } internal TcpServerConnection ServerConnection { get; set; }
/// <summary> /// <summary>
/// Request ID. /// Stores internal data for the session.
/// </summary> /// </summary>
public Guid RequestId { get; } internal InternalDataStore Data { get; } = new InternalDataStore();
/// <summary>
/// Gets or sets the user data.
/// </summary>
public object UserData { get; set; }
/// <summary> /// <summary>
/// Override UpStreamEndPoint for this request; Local NIC via request is made /// Override UpStreamEndPoint for this request; Local NIC via request is made
......
using System.Collections.Generic;
namespace Titanium.Web.Proxy.Http
{
class InternalDataStore : Dictionary<string, object>
{
public bool TryGetValueAs<T>(string key, out T value)
{
bool result = TryGetValue(key, out var value1);
if (result)
{
value = (T)value1;
}
else
{
value = default;
}
return result;
}
public T GetAs<T>(string key)
{
return (T)this[key];
}
}
}
\ No newline at end of file
...@@ -7,6 +7,7 @@ using System.Linq; ...@@ -7,6 +7,7 @@ using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Security.Principal; using System.Security.Principal;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Network.WinAuth.Security namespace Titanium.Web.Proxy.Network.WinAuth.Security
{ {
...@@ -14,19 +15,16 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -14,19 +15,16 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
internal class WinAuthEndPoint internal class WinAuthEndPoint
{ {
/// <summary> private const string authStateKey = "AuthState";
/// Keep track of auth states for reuse in final challenge response
/// </summary>
private static readonly IDictionary<Guid, State> authStates = new ConcurrentDictionary<Guid, State>();
/// <summary> /// <summary>
/// Acquire the intial client token to send /// Acquire the intial client token to send
/// </summary> /// </summary>
/// <param name="hostname"></param> /// <param name="hostname"></param>
/// <param name="authScheme"></param> /// <param name="authScheme"></param>
/// <param name="requestId"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
internal static byte[] AcquireInitialSecurityToken(string hostname, string authScheme, Guid requestId) internal static byte[] AcquireInitialSecurityToken(string hostname, string authScheme, InternalDataStore data)
{ {
byte[] token; byte[] token;
...@@ -75,7 +73,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -75,7 +73,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
state.AuthState = State.WinAuthState.INITIAL_TOKEN; state.AuthState = State.WinAuthState.INITIAL_TOKEN;
token = clientToken.GetBytes(); token = clientToken.GetBytes();
authStates.Add(requestId, state); data.Add(authStateKey, state);
} }
finally finally
{ {
...@@ -91,9 +89,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -91,9 +89,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// </summary> /// </summary>
/// <param name="hostname"></param> /// <param name="hostname"></param>
/// <param name="serverChallenge"></param> /// <param name="serverChallenge"></param>
/// <param name="requestId"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
internal static byte[] AcquireFinalSecurityToken(string hostname, byte[] serverChallenge, Guid requestId) internal static byte[] AcquireFinalSecurityToken(string hostname, byte[] serverChallenge, InternalDataStore data)
{ {
byte[] token; byte[] token;
...@@ -104,7 +102,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -104,7 +102,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
try try
{ {
var state = authStates[requestId]; var state = data.GetAs<State>(authStateKey);
state.UpdatePresence(); state.UpdatePresence();
...@@ -120,7 +118,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -120,7 +118,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
out clientToken, out clientToken,
out NewContextAttributes, out NewContextAttributes,
out NewLifeTime); out NewLifeTime);
if (result != SuccessfulResult) if (result != SuccessfulResult)
{ {
return null; return null;
...@@ -138,35 +136,16 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -138,35 +136,16 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return token; return token;
} }
/// <summary>
/// Clear any hanging states
/// </summary>
/// <param name="stateCacheTimeOutMinutes"></param>
internal static async void ClearIdleStates(int stateCacheTimeOutMinutes)
{
var cutOff = DateTime.Now.AddMinutes(-1 * stateCacheTimeOutMinutes);
var outdated = authStates.Where(x => x.Value.LastSeen < cutOff).ToList();
foreach (var cache in outdated)
{
authStates.Remove(cache.Key);
}
// after a minute come back to check for outdated certificates in cache
await Task.Delay(1000 * 60);
}
/// <summary> /// <summary>
/// Validates that the current WinAuth state of the connection matches the /// Validates that the current WinAuth state of the connection matches the
/// expectation, used to detect failed authentication /// expectation, used to detect failed authentication
/// </summary> /// </summary>
/// <param name="requestId"></param> /// <param name="data"></param>
/// <param name="expectedAuthState"></param> /// <param name="expectedAuthState"></param>
/// <returns></returns> /// <returns></returns>
internal static bool ValidateWinAuthState(Guid requestId, State.WinAuthState expectedAuthState) internal static bool ValidateWinAuthState(InternalDataStore data, State.WinAuthState expectedAuthState)
{ {
bool stateExists = authStates.TryGetValue(requestId, out var state); bool stateExists = data.TryGetValueAs(authStateKey, out State state);
if (expectedAuthState == State.WinAuthState.UNAUTHORIZED) if (expectedAuthState == State.WinAuthState.UNAUTHORIZED)
{ {
...@@ -188,10 +167,10 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security ...@@ -188,10 +167,10 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// <summary> /// <summary>
/// Set the AuthState to authorized and update the connection state lifetime /// Set the AuthState to authorized and update the connection state lifetime
/// </summary> /// </summary>
/// <param name="requestId"></param> /// <param name="data"></param>
internal static void AuthenticatedResponse(Guid requestId) internal static void AuthenticatedResponse(InternalDataStore data)
{ {
if (authStates.TryGetValue(requestId, out var state)) if (data.TryGetValueAs(authStateKey, out State state))
{ {
state.AuthState = State.WinAuthState.AUTHORIZED; state.AuthState = State.WinAuthState.AUTHORIZED;
state.UpdatePresence(); state.UpdatePresence();
......
using System; using System;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Network.WinAuth.Security; using Titanium.Web.Proxy.Network.WinAuth.Security;
namespace Titanium.Web.Proxy.Network.WinAuth namespace Titanium.Web.Proxy.Network.WinAuth
...@@ -16,26 +17,26 @@ namespace Titanium.Web.Proxy.Network.WinAuth ...@@ -16,26 +17,26 @@ namespace Titanium.Web.Proxy.Network.WinAuth
/// </summary> /// </summary>
/// <param name="serverHostname"></param> /// <param name="serverHostname"></param>
/// <param name="authScheme"></param> /// <param name="authScheme"></param>
/// <param name="requestId"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
internal static string GetInitialAuthToken(string serverHostname, string authScheme, Guid requestId) internal static string GetInitialAuthToken(string serverHostname, string authScheme, InternalDataStore data)
{ {
var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(serverHostname, authScheme, requestId); var tokenBytes = WinAuthEndPoint.AcquireInitialSecurityToken(serverHostname, authScheme, data);
return string.Concat(" ", Convert.ToBase64String(tokenBytes)); return string.Concat(" ", Convert.ToBase64String(tokenBytes));
} }
/// <summary> /// <summary>
/// Get the final token given the server challenge token /// Get the final token given the server challenge token
/// </summary> /// </summary>
/// <param name="serverHostname"></param> /// <param name="serverHostname"></param>
/// <param name="serverToken"></param> /// <param name="serverToken"></param>
/// <param name="requestId"></param> /// <param name="data"></param>
/// <returns></returns> /// <returns></returns>
internal static string GetFinalAuthToken(string serverHostname, string serverToken, Guid requestId) internal static string GetFinalAuthToken(string serverHostname, string serverToken, InternalDataStore data)
{ {
var tokenBytes = var tokenBytes =
WinAuthEndPoint.AcquireFinalSecurityToken(serverHostname, Convert.FromBase64String(serverToken), WinAuthEndPoint.AcquireFinalSecurityToken(serverHostname, Convert.FromBase64String(serverToken),
requestId); data);
return string.Concat(" ", Convert.ToBase64String(tokenBytes)); return string.Concat(" ", Convert.ToBase64String(tokenBytes));
} }
......
...@@ -511,11 +511,6 @@ namespace Titanium.Web.Proxy ...@@ -511,11 +511,6 @@ namespace Titanium.Web.Proxy
CertificateManager.ClearIdleCertificates(); CertificateManager.ClearIdleCertificates();
if (RunTime.IsWindows && !RunTime.IsRunningOnMono)
{
WinAuthEndPoint.ClearIdleStates(2);
}
foreach (var endPoint in ProxyEndPoints) foreach (var endPoint in ProxyEndPoints)
{ {
Listen(endPoint); Listen(endPoint);
......
...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy ...@@ -39,7 +39,7 @@ namespace Titanium.Web.Proxy
} }
else else
{ {
WinAuthEndPoint.AuthenticatedResponse(args.WebSession.RequestId); WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
} }
} }
......
...@@ -96,7 +96,7 @@ namespace Titanium.Web.Proxy ...@@ -96,7 +96,7 @@ namespace Titanium.Web.Proxy
var expectedAuthState = var expectedAuthState =
scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED; scheme == null ? State.WinAuthState.INITIAL_TOKEN : State.WinAuthState.UNAUTHORIZED;
if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.RequestId, expectedAuthState)) if (!WinAuthEndPoint.ValidateWinAuthState(args.WebSession.Data, expectedAuthState))
{ {
// Invalid state, create proper error message to client // Invalid state, create proper error message to client
await RewriteUnauthorizedResponse(args); await RewriteUnauthorizedResponse(args);
...@@ -111,7 +111,7 @@ namespace Titanium.Web.Proxy ...@@ -111,7 +111,7 @@ namespace Titanium.Web.Proxy
// initial value will match exactly any of the schemes // initial value will match exactly any of the schemes
if (scheme != null) if (scheme != null)
{ {
string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host, scheme, args.Id); string clientToken = WinAuthHandler.GetInitialAuthToken(request.Host, scheme, args.WebSession.Data);
string auth = string.Concat(scheme, clientToken); string auth = string.Concat(scheme, clientToken);
...@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy ...@@ -133,7 +133,7 @@ namespace Titanium.Web.Proxy
authHeader.Value.Length > x.Length + 1); authHeader.Value.Length > x.Length + 1);
string serverToken = authHeader.Value.Substring(scheme.Length + 1); string serverToken = authHeader.Value.Substring(scheme.Length + 1);
string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host, serverToken, args.Id); string clientToken = WinAuthHandler.GetFinalAuthToken(request.Host, serverToken, args.WebSession.Data);
string auth = string.Concat(scheme, clientToken); string auth = string.Concat(scheme, clientToken);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment