Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
T
Titanium-Web-Proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Titanium-Web-Proxy
Commits
255edefd
Commit
255edefd
authored
Apr 05, 2018
by
Björn Weström
Committed by
GitHub
Apr 05, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1 from justcoding121/develop
Merge to latest
parents
dbe023ba
4b2f944a
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
242 additions
and
76 deletions
+242
-76
ProxyTestController.cs
.../Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
+1
-1
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+0
-1
StringExtensions.cs
Titanium.Web.Proxy/Extensions/StringExtensions.cs
+5
-0
Request.cs
Titanium.Web.Proxy/Http/Request.cs
+5
-0
RequestResponseBase.cs
Titanium.Web.Proxy/Http/RequestResponseBase.cs
+10
-5
Response.cs
Titanium.Web.Proxy/Http/Response.cs
+20
-0
OkResponse.cs
Titanium.Web.Proxy/Http/Responses/OkResponse.cs
+8
-0
ExplicitProxyEndPoint.cs
Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
+8
-2
ProxyEndPoint.cs
Titanium.Web.Proxy/Models/ProxyEndPoint.cs
+1
-6
TransparentProxyEndPoint.cs
Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
+1
-1
State.cs
Titanium.Web.Proxy/Network/WinAuth/Security/State.cs
+18
-0
WinAuthEndPoint.cs
...ium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs
+47
-7
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+1
-5
RequestHandler.cs
Titanium.Web.Proxy/RequestHandler.cs
+35
-25
ResponseHandler.cs
Titanium.Web.Proxy/ResponseHandler.cs
+12
-3
WinAuthHandler.cs
Titanium.Web.Proxy/WinAuthHandler.cs
+70
-20
No files found.
Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs
View file @
255edefd
...
@@ -78,7 +78,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
...
@@ -78,7 +78,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.EnableWinAuth = true;
//proxyServer.EnableWinAuth = true;
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
,
true
)
explicitEndPoint
=
new
ExplicitProxyEndPoint
(
IPAddress
.
Any
,
8000
)
{
{
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
//You can set only one of the ExcludedHttpsHostNameRegex and IncludedHttpsHostNameRegex properties, otherwise ArgumentException will be thrown
...
...
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
255edefd
...
@@ -483,7 +483,6 @@ namespace Titanium.Web.Proxy.EventArguments
...
@@ -483,7 +483,6 @@ namespace Titanium.Web.Proxy.EventArguments
}
}
request
.
Body
=
body
;
request
.
Body
=
body
;
request
.
UpdateContentLength
();
}
}
/// <summary>
/// <summary>
...
...
Titanium.Web.Proxy/Extensions/StringExtensions.cs
View file @
255edefd
...
@@ -14,5 +14,10 @@ namespace Titanium.Web.Proxy.Extensions
...
@@ -14,5 +14,10 @@ namespace Titanium.Web.Proxy.Extensions
{
{
return
CultureInfo
.
CurrentCulture
.
CompareInfo
.
IndexOf
(
str
,
value
,
CompareOptions
.
IgnoreCase
)
>=
0
;
return
CultureInfo
.
CurrentCulture
.
CompareInfo
.
IndexOf
(
str
,
value
,
CompareOptions
.
IgnoreCase
)
>=
0
;
}
}
internal
static
int
IndexOfIgnoreCase
(
this
string
str
,
string
value
)
{
return
CultureInfo
.
CurrentCulture
.
CompareInfo
.
IndexOf
(
str
,
value
,
CompareOptions
.
IgnoreCase
);
}
}
}
}
}
Titanium.Web.Proxy/Http/Request.cs
View file @
255edefd
...
@@ -102,6 +102,11 @@ namespace Titanium.Web.Proxy.Http
...
@@ -102,6 +102,11 @@ namespace Titanium.Web.Proxy.Http
internal
override
void
EnsureBodyAvailable
(
bool
throwWhenNotReadYet
=
true
)
internal
override
void
EnsureBodyAvailable
(
bool
throwWhenNotReadYet
=
true
)
{
{
if
(
BodyInternal
!=
null
)
{
return
;
}
//GET request don't have a request body to read
//GET request don't have a request body to read
if
(!
HasBody
)
if
(!
HasBody
)
{
{
...
...
Titanium.Web.Proxy/Http/RequestResponseBase.cs
View file @
255edefd
...
@@ -16,7 +16,7 @@ namespace Titanium.Web.Proxy.Http
...
@@ -16,7 +16,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// <summary>
/// Cached body content as byte array
/// Cached body content as byte array
/// </summary>
/// </summary>
pr
ivate
byte
[]
body
;
pr
otected
byte
[]
BodyInternal
;
/// <summary>
/// <summary>
/// Cached body as string
/// Cached body as string
...
@@ -127,11 +127,11 @@ namespace Titanium.Web.Proxy.Http
...
@@ -127,11 +127,11 @@ namespace Titanium.Web.Proxy.Http
get
get
{
{
EnsureBodyAvailable
();
EnsureBodyAvailable
();
return
body
;
return
BodyInternal
;
}
}
internal
set
internal
set
{
{
body
=
value
;
BodyInternal
=
value
;
bodyString
=
null
;
bodyString
=
null
;
//If there is a content length header update it
//If there is a content length header update it
...
@@ -173,6 +173,11 @@ namespace Titanium.Web.Proxy.Http
...
@@ -173,6 +173,11 @@ namespace Titanium.Web.Proxy.Http
internal
byte
[]
CompressBodyAndUpdateContentLength
()
internal
byte
[]
CompressBodyAndUpdateContentLength
()
{
{
if
(!
IsBodyRead
&&
BodyInternal
==
null
)
{
return
null
;
}
bool
isChunked
=
IsChunked
;
bool
isChunked
=
IsChunked
;
string
contentEncoding
=
ContentEncoding
;
string
contentEncoding
=
ContentEncoding
;
...
@@ -219,7 +224,7 @@ namespace Titanium.Web.Proxy.Http
...
@@ -219,7 +224,7 @@ namespace Titanium.Web.Proxy.Http
internal
void
UpdateContentLength
()
internal
void
UpdateContentLength
()
{
{
ContentLength
=
IsChunked
?
-
1
:
body
?.
Length
??
0
;
ContentLength
=
IsChunked
?
-
1
:
BodyInternal
?.
Length
??
0
;
}
}
/// <summary>
/// <summary>
...
@@ -229,7 +234,7 @@ namespace Titanium.Web.Proxy.Http
...
@@ -229,7 +234,7 @@ namespace Titanium.Web.Proxy.Http
{
{
if
(!
KeepBody
)
if
(!
KeepBody
)
{
{
body
=
null
;
BodyInternal
=
null
;
bodyString
=
null
;
bodyString
=
null
;
}
}
}
}
...
...
Titanium.Web.Proxy/Http/Response.cs
View file @
255edefd
...
@@ -81,6 +81,11 @@ namespace Titanium.Web.Proxy.Http
...
@@ -81,6 +81,11 @@ namespace Titanium.Web.Proxy.Http
internal
override
void
EnsureBodyAvailable
(
bool
throwWhenNotReadYet
=
true
)
internal
override
void
EnsureBodyAvailable
(
bool
throwWhenNotReadYet
=
true
)
{
{
if
(
BodyInternal
!=
null
)
{
return
;
}
if
(!
IsBodyRead
&&
throwWhenNotReadYet
)
if
(!
IsBodyRead
&&
throwWhenNotReadYet
)
{
{
throw
new
Exception
(
"Response body is not read yet. "
+
throw
new
Exception
(
"Response body is not read yet. "
+
...
@@ -123,6 +128,21 @@ namespace Titanium.Web.Proxy.Http
...
@@ -123,6 +128,21 @@ namespace Titanium.Web.Proxy.Http
}
}
}
}
/// <summary>
/// Constructor.
/// </summary>
public
Response
()
{
}
/// <summary>
/// Constructor.
/// </summary>
public
Response
(
byte
[]
body
)
{
Body
=
body
;
}
internal
static
string
CreateResponseLine
(
Version
version
,
int
statusCode
,
string
statusDescription
)
internal
static
string
CreateResponseLine
(
Version
version
,
int
statusCode
,
string
statusDescription
)
{
{
return
$"HTTP/
{
version
.
Major
}
.
{
version
.
Minor
}
{
statusCode
}
{
statusDescription
}
"
;
return
$"HTTP/
{
version
.
Major
}
.
{
version
.
Minor
}
{
statusCode
}
{
statusDescription
}
"
;
...
...
Titanium.Web.Proxy/Http/Responses/OkResponse.cs
View file @
255edefd
...
@@ -15,5 +15,13 @@ namespace Titanium.Web.Proxy.Http.Responses
...
@@ -15,5 +15,13 @@ namespace Titanium.Web.Proxy.Http.Responses
StatusCode
=
(
int
)
HttpStatusCode
.
OK
;
StatusCode
=
(
int
)
HttpStatusCode
.
OK
;
StatusDescription
=
"OK"
;
StatusDescription
=
"OK"
;
}
}
/// <summary>
/// Constructor.
/// </summary>
public
OkResponse
(
byte
[]
body
)
:
this
()
{
Body
=
body
;
}
}
}
}
}
Titanium.Web.Proxy/Models/ExplicitProxyEndPoint.cs
View file @
255edefd
...
@@ -17,6 +17,11 @@ namespace Titanium.Web.Proxy.Models
...
@@ -17,6 +17,11 @@ namespace Titanium.Web.Proxy.Models
internal
bool
IsSystemHttpsProxy
{
get
;
set
;
}
internal
bool
IsSystemHttpsProxy
{
get
;
set
;
}
/// <summary>
/// Enable SSL?
/// </summary>
public
bool
DecryptSsl
{
get
;
}
/// <summary>
/// <summary>
/// Generic certificate to use for SSL decryption.
/// Generic certificate to use for SSL decryption.
/// </summary>
/// </summary>
...
@@ -40,9 +45,10 @@ namespace Titanium.Web.Proxy.Models
...
@@ -40,9 +45,10 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
/// </summary>
/// <param name="ipAddress"></param>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="port"></param>
/// <param name="
enable
Ssl"></param>
/// <param name="
decrypt
Ssl"></param>
public
ExplicitProxyEndPoint
(
IPAddress
ipAddress
,
int
port
,
bool
enableSsl
)
:
base
(
ipAddress
,
port
,
enableSsl
)
public
ExplicitProxyEndPoint
(
IPAddress
ipAddress
,
int
port
,
bool
decryptSsl
=
true
)
:
base
(
ipAddress
,
port
)
{
{
this
.
DecryptSsl
=
decryptSsl
;
}
}
internal
async
Task
InvokeBeforeTunnelConnectRequest
(
ProxyServer
proxyServer
,
TunnelConnectSessionEventArgs
connectArgs
,
ExceptionHandler
exceptionFunc
)
internal
async
Task
InvokeBeforeTunnelConnectRequest
(
ProxyServer
proxyServer
,
TunnelConnectSessionEventArgs
connectArgs
,
ExceptionHandler
exceptionFunc
)
...
...
Titanium.Web.Proxy/Models/ProxyEndPoint.cs
View file @
255edefd
...
@@ -17,11 +17,10 @@ namespace Titanium.Web.Proxy.Models
...
@@ -17,11 +17,10 @@ namespace Titanium.Web.Proxy.Models
/// <param name="ipAddress"></param>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="port"></param>
/// <param name="enableSsl"></param>
/// <param name="enableSsl"></param>
protected
ProxyEndPoint
(
IPAddress
ipAddress
,
int
port
,
bool
enableSsl
)
protected
ProxyEndPoint
(
IPAddress
ipAddress
,
int
port
)
{
{
IpAddress
=
ipAddress
;
IpAddress
=
ipAddress
;
Port
=
port
;
Port
=
port
;
EnableSsl
=
enableSsl
;
}
}
/// <summary>
/// <summary>
...
@@ -39,10 +38,6 @@ namespace Titanium.Web.Proxy.Models
...
@@ -39,10 +38,6 @@ namespace Titanium.Web.Proxy.Models
/// </summary>
/// </summary>
public
int
Port
{
get
;
internal
set
;
}
public
int
Port
{
get
;
internal
set
;
}
/// <summary>
/// Enable SSL?
/// </summary>
public
bool
EnableSsl
{
get
;
}
/// <summary>
/// <summary>
/// Is IPv6 enabled?
/// Is IPv6 enabled?
...
...
Titanium.Web.Proxy/Models/TransparentProxyEndPoint.cs
View file @
255edefd
...
@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Models
...
@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Models
/// <param name="ipAddress"></param>
/// <param name="ipAddress"></param>
/// <param name="port"></param>
/// <param name="port"></param>
/// <param name="enableSsl"></param>
/// <param name="enableSsl"></param>
public
TransparentProxyEndPoint
(
IPAddress
ipAddress
,
int
port
,
bool
enableSsl
)
:
base
(
ipAddress
,
port
,
enableSsl
)
public
TransparentProxyEndPoint
(
IPAddress
ipAddress
,
int
port
)
:
base
(
ipAddress
,
port
)
{
{
GenericCertificateName
=
"localhost"
;
GenericCertificateName
=
"localhost"
;
}
}
...
...
Titanium.Web.Proxy/Network/WinAuth/Security/State.cs
View file @
255edefd
...
@@ -7,12 +7,24 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -7,12 +7,24 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// </summary>
/// </summary>
internal
class
State
internal
class
State
{
{
/// <summary>
/// States during Windows Authentication
/// </summary>
public
enum
WinAuthState
{
UNAUTHORIZED
,
INITIAL_TOKEN
,
FINAL_TOKEN
,
AUTHORIZED
};
internal
State
()
internal
State
()
{
{
Credentials
=
new
Common
.
SecurityHandle
(
0
);
Credentials
=
new
Common
.
SecurityHandle
(
0
);
Context
=
new
Common
.
SecurityHandle
(
0
);
Context
=
new
Common
.
SecurityHandle
(
0
);
LastSeen
=
DateTime
.
Now
;
LastSeen
=
DateTime
.
Now
;
AuthState
=
WinAuthState
.
UNAUTHORIZED
;
}
}
/// <summary>
/// <summary>
...
@@ -30,10 +42,16 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -30,10 +42,16 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
/// </summary>
/// </summary>
internal
DateTime
LastSeen
;
internal
DateTime
LastSeen
;
/// <summary>
/// Current state of the authentication process
/// </summary>
internal
WinAuthState
AuthState
;
internal
void
ResetHandles
()
internal
void
ResetHandles
()
{
{
Credentials
.
Reset
();
Credentials
.
Reset
();
Context
.
Reset
();
Context
.
Reset
();
AuthState
=
WinAuthState
.
UNAUTHORIZED
;
}
}
internal
void
UpdatePresence
()
internal
void
UpdatePresence
()
...
...
Titanium.Web.Proxy/Network/WinAuth/Security/WinAuthEndPoint.cs
View file @
255edefd
...
@@ -38,11 +38,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -38,11 +38,9 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
try
try
{
{
int
result
;
var
state
=
new
State
();
var
state
=
new
State
();
result
=
AcquireCredentialsHandle
(
int
result
=
AcquireCredentialsHandle
(
WindowsIdentity
.
GetCurrent
().
Name
,
WindowsIdentity
.
GetCurrent
().
Name
,
authScheme
,
authScheme
,
SecurityCredentialsOutbound
,
SecurityCredentialsOutbound
,
...
@@ -79,6 +77,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -79,6 +77,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return
null
;
return
null
;
}
}
state
.
AuthState
=
State
.
WinAuthState
.
INITIAL_TOKEN
;
token
=
clientToken
.
GetBytes
();
token
=
clientToken
.
GetBytes
();
authStates
.
Add
(
requestId
,
state
);
authStates
.
Add
(
requestId
,
state
);
}
}
...
@@ -109,13 +108,11 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -109,13 +108,11 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
try
try
{
{
int
result
;
var
state
=
authStates
[
requestId
];
var
state
=
authStates
[
requestId
];
state
.
UpdatePresence
();
state
.
UpdatePresence
();
result
=
InitializeSecurityContext
(
ref
state
.
Credentials
,
int
result
=
InitializeSecurityContext
(
ref
state
.
Credentials
,
ref
state
.
Context
,
ref
state
.
Context
,
hostname
,
hostname
,
StandardContextAttributes
,
StandardContextAttributes
,
...
@@ -135,7 +132,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -135,7 +132,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return
null
;
return
null
;
}
}
authStates
.
Remove
(
requestId
)
;
state
.
AuthState
=
State
.
WinAuthState
.
FINAL_TOKEN
;
token
=
clientToken
.
GetBytes
();
token
=
clientToken
.
GetBytes
();
}
}
finally
finally
...
@@ -166,6 +163,49 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -166,6 +163,49 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
await
Task
.
Delay
(
1000
*
60
);
await
Task
.
Delay
(
1000
*
60
);
}
}
/// <summary>
/// Validates that the current WinAuth state of the connection matches the
/// expectation, used to detect failed authentication
/// </summary>
/// <param name="requestId"></param>
/// <param name="expectedAuthState"></param>
/// <returns></returns>
internal
static
bool
ValidateWinAuthState
(
Guid
requestId
,
State
.
WinAuthState
expectedAuthState
)
{
var
stateExists
=
authStates
.
TryGetValue
(
requestId
,
out
var
state
);
if
(
expectedAuthState
==
State
.
WinAuthState
.
UNAUTHORIZED
)
{
// Validation before initial token
return
stateExists
==
false
||
state
.
AuthState
==
State
.
WinAuthState
.
UNAUTHORIZED
||
state
.
AuthState
==
State
.
WinAuthState
.
AUTHORIZED
;
// Server may require re-authentication on an open connection
}
if
(
expectedAuthState
==
State
.
WinAuthState
.
INITIAL_TOKEN
)
{
// Validation before final token
return
stateExists
&&
(
state
.
AuthState
==
State
.
WinAuthState
.
INITIAL_TOKEN
||
state
.
AuthState
==
State
.
WinAuthState
.
AUTHORIZED
);
// Server may require re-authentication on an open connection
}
throw
new
Exception
(
"Unsupported validation of WinAuthState"
);
}
/// <summary>
/// Set the AuthState to authorized and update the connection state lifetime
/// </summary>
/// <param name="requestId"></param>
internal
static
void
AuthenticatedResponse
(
Guid
requestId
)
{
if
(
authStates
.
TryGetValue
(
requestId
,
out
var
state
))
{
state
.
AuthState
=
State
.
WinAuthState
.
AUTHORIZED
;
state
.
UpdatePresence
();
}
}
#
region
Native
calls
to
secur32
.
dll
#
region
Native
calls
to
secur32
.
dll
[
DllImport
(
"secur32.dll"
,
SetLastError
=
true
)]
[
DllImport
(
"secur32.dll"
,
SetLastError
=
true
)]
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
255edefd
...
@@ -316,13 +316,9 @@ namespace Titanium.Web.Proxy
...
@@ -316,13 +316,9 @@ namespace Titanium.Web.Proxy
bool
isHttp
=
(
protocolType
&
ProxyProtocolType
.
Http
)
>
0
;
bool
isHttp
=
(
protocolType
&
ProxyProtocolType
.
Http
)
>
0
;
bool
isHttps
=
(
protocolType
&
ProxyProtocolType
.
Https
)
>
0
;
bool
isHttps
=
(
protocolType
&
ProxyProtocolType
.
Https
)
>
0
;
if
(
isHttps
)
if
(
isHttps
)
{
{
if
(!
endPoint
.
EnableSsl
)
{
throw
new
Exception
(
"Endpoint do not support Https connections"
);
}
CertificateManager
.
EnsureRootCertificate
();
CertificateManager
.
EnsureRootCertificate
();
...
...
Titanium.Web.Proxy/RequestHandler.cs
View file @
255edefd
...
@@ -79,7 +79,7 @@ namespace Titanium.Web.Proxy
...
@@ -79,7 +79,7 @@ namespace Titanium.Web.Proxy
await
endPoint
.
InvokeBeforeTunnelConnectRequest
(
this
,
connectArgs
,
ExceptionFunc
);
await
endPoint
.
InvokeBeforeTunnelConnectRequest
(
this
,
connectArgs
,
ExceptionFunc
);
//filter out excluded host names
//filter out excluded host names
bool
excluded
=
connectArgs
.
Excluded
;
bool
excluded
=
!
endPoint
.
DecryptSsl
||
connectArgs
.
Excluded
;
if
(
await
CheckAuthorization
(
connectArgs
)
==
false
)
if
(
await
CheckAuthorization
(
connectArgs
)
==
false
)
{
{
...
@@ -97,7 +97,8 @@ namespace Titanium.Web.Proxy
...
@@ -97,7 +97,8 @@ namespace Titanium.Web.Proxy
await
clientStreamWriter
.
WriteResponseAsync
(
response
);
await
clientStreamWriter
.
WriteResponseAsync
(
response
);
var
clientHelloInfo
=
await
SslTools
.
PeekClientHello
(
clientStream
);
var
clientHelloInfo
=
await
SslTools
.
PeekClientHello
(
clientStream
);
bool
isClientHello
=
clientHelloInfo
!=
null
;
bool
isClientHello
=
clientHelloInfo
!=
null
;
if
(
isClientHello
)
if
(
isClientHello
)
{
{
...
@@ -130,7 +131,7 @@ namespace Titanium.Web.Proxy
...
@@ -130,7 +131,7 @@ namespace Titanium.Web.Proxy
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
,
BufferSize
);
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
,
BufferSize
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
,
BufferSize
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
,
BufferSize
);
}
}
catch
(
Exception
e
)
catch
(
Exception
e
)
{
{
ExceptionFunc
(
new
Exception
(
$"Could'nt authenticate client '
{
connectHostname
}
' with fake certificate."
,
e
));
ExceptionFunc
(
new
Exception
(
$"Could'nt authenticate client '
{
connectHostname
}
' with fake certificate."
,
e
));
sslStream
?.
Dispose
();
sslStream
?.
Dispose
();
...
@@ -227,37 +228,46 @@ namespace Titanium.Web.Proxy
...
@@ -227,37 +228,46 @@ namespace Titanium.Web.Proxy
try
try
{
{
if
(
endPoint
.
EnableSsl
)
var
clientHelloInfo
=
await
SslTools
.
PeekClientHello
(
clientStream
);
{
var
clientHelloInfo
=
await
SslTools
.
PeekClientHello
(
clientStream
);
var
isHttps
=
clientHelloInfo
!=
null
;
string
httpsHostName
=
null
;
if
(
clientHelloInfo
!=
null
)
if
(
isHttps
)
{
SslStream
sslStream
=
null
;
try
{
{
var
sslStream
=
new
SslStream
(
clientStream
);
sslStream
=
new
SslStream
(
clientStream
);
clientStream
=
new
CustomBufferedStream
(
sslStream
,
BufferSize
);
string
sni
HostName
=
clientHelloInfo
.
GetServerName
()
??
endPoint
.
GenericCertificateName
;
https
HostName
=
clientHelloInfo
.
GetServerName
()
??
endPoint
.
GenericCertificateName
;
string
certName
=
HttpHelper
.
GetWildCardDomainName
(
sni
HostName
);
string
certName
=
HttpHelper
.
GetWildCardDomainName
(
https
HostName
);
var
certificate
=
await
CertificateManager
.
CreateCertificateAsync
(
certName
);
var
certificate
=
await
CertificateManager
.
CreateCertificateAsync
(
certName
);
try
{
//Successfully managed to authenticate the client using the fake certificate
await
sslStream
.
AuthenticateAsServerAsync
(
certificate
,
false
,
SslProtocols
.
Tls
,
false
);
}
catch
(
Exception
e
)
{
ExceptionFunc
(
new
Exception
(
$"Could'nt authenticate client '
{
sniHostName
}
' with fake certificate."
,
e
));
return
;
}
}
//HTTPS server created - we can now decrypt the client's traffic
//Successfully managed to authenticate the client using the fake certificate
await
sslStream
.
AuthenticateAsServerAsync
(
certificate
,
false
,
SslProtocols
.
Tls
,
false
);
//HTTPS server created - we can now decrypt the client's traffic
clientStream
=
new
CustomBufferedStream
(
sslStream
,
BufferSize
);
clientStreamReader
.
Dispose
();
clientStreamReader
=
new
CustomBinaryReader
(
clientStream
,
BufferSize
);
clientStreamWriter
=
new
HttpResponseWriter
(
clientStream
,
BufferSize
);
}
catch
(
Exception
e
)
{
ExceptionFunc
(
new
Exception
(
$"Could'nt authenticate client '
{
httpsHostName
}
' with fake certificate."
,
e
));
sslStream
?.
Dispose
();
return
;
}
}
}
//HTTPS server created - we can now decrypt the client's traffic
//Now create the request
//Now create the request
await
HandleHttpSessionRequest
(
tcpClient
,
clientStream
,
clientStreamReader
,
clientStreamWriter
,
await
HandleHttpSessionRequest
(
tcpClient
,
clientStream
,
clientStreamReader
,
clientStreamWriter
,
endPoint
.
EnableSsl
?
endPoint
.
GenericCertificate
Name
:
null
,
endPoint
,
null
,
true
);
isHttps
?
httpsHost
Name
:
null
,
endPoint
,
null
,
true
);
}
}
finally
finally
{
{
...
@@ -605,7 +615,7 @@ namespace Titanium.Web.Proxy
...
@@ -605,7 +615,7 @@ namespace Titanium.Web.Proxy
/// <param name="requestHeaders"></param>
/// <param name="requestHeaders"></param>
private
void
PrepareRequestHeaders
(
HeaderCollection
requestHeaders
)
private
void
PrepareRequestHeaders
(
HeaderCollection
requestHeaders
)
{
{
if
(
requestHeaders
.
HeaderExists
(
KnownHeaders
.
AcceptEncoding
))
if
(
requestHeaders
.
HeaderExists
(
KnownHeaders
.
AcceptEncoding
))
{
{
requestHeaders
.
SetOrAddHeaderValue
(
KnownHeaders
.
AcceptEncoding
,
"gzip,deflate"
);
requestHeaders
.
SetOrAddHeaderValue
(
KnownHeaders
.
AcceptEncoding
,
"gzip,deflate"
);
}
}
...
...
Titanium.Web.Proxy/ResponseHandler.cs
View file @
255edefd
...
@@ -7,6 +7,7 @@ using Titanium.Web.Proxy.Compression;
...
@@ -7,6 +7,7 @@ using Titanium.Web.Proxy.Compression;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Exceptions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Network.WinAuth.Security
;
namespace
Titanium.Web.Proxy
namespace
Titanium.Web.Proxy
{
{
...
@@ -28,14 +29,22 @@ namespace Titanium.Web.Proxy
...
@@ -28,14 +29,22 @@ namespace Titanium.Web.Proxy
await
args
.
WebSession
.
ReceiveResponse
();
await
args
.
WebSession
.
ReceiveResponse
();
var
response
=
args
.
WebSession
.
Response
;
var
response
=
args
.
WebSession
.
Response
;
args
.
ReRequest
=
false
;
//check for windows authentication
//check for windows authentication
if
(
isWindowsAuthenticationEnabledAndSupported
&&
response
.
StatusCode
==
(
int
)
HttpStatusCode
.
Unauthorized
)
if
(
isWindowsAuthenticationEnabledAndSupported
)
{
{
await
Handle401UnAuthorized
(
args
);
if
(
response
.
StatusCode
==
(
int
)
HttpStatusCode
.
Unauthorized
)
{
await
Handle401UnAuthorized
(
args
);
}
else
{
WinAuthEndPoint
.
AuthenticatedResponse
(
args
.
WebSession
.
RequestId
);
}
}
}
args
.
ReRequest
=
false
;
response
.
OriginalHasBody
=
response
.
HasBody
;
//if user requested call back then do it
//if user requested call back then do it
if
(!
response
.
Locked
)
if
(!
response
.
Locked
)
...
...
Titanium.Web.Proxy/WinAuthHandler.cs
View file @
255edefd
...
@@ -3,30 +3,32 @@ using System.Collections.Generic;
...
@@ -3,30 +3,32 @@ using System.Collections.Generic;
using
System.Linq
;
using
System.Linq
;
using
System.Threading.Tasks
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Extensions
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Http
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network.WinAuth
;
using
Titanium.Web.Proxy.Network.WinAuth
;
using
Titanium.Web.Proxy.Network.WinAuth.Security
;
namespace
Titanium.Web.Proxy
namespace
Titanium.Web.Proxy
{
{
public
partial
class
ProxyServer
public
partial
class
ProxyServer
{
{
//possible header names
//possible header names
private
static
readonly
List
<
string
>
authHeaderNames
=
new
Lis
t
<
string
>
private
static
readonly
HashSet
<
string
>
authHeaderNames
=
new
HashSe
t
<
string
>
{
{
"WWW-Authenticate"
,
"WWW-Authenticate"
.
ToLower
()
,
//IIS 6.0 messed up names below
//IIS 6.0 messed up names below
"WWWAuthenticate"
,
"WWWAuthenticate"
.
ToLower
()
,
"NTLMAuthorization"
,
"NTLMAuthorization"
.
ToLower
()
,
"NegotiateAuthorization"
,
"NegotiateAuthorization"
.
ToLower
()
,
"KerberosAuthorization"
"KerberosAuthorization"
.
ToLower
()
};
};
private
static
readonly
List
<
string
>
authSchemes
=
new
Lis
t
<
string
>
private
static
readonly
HashSet
<
string
>
authSchemes
=
new
HashSe
t
<
string
>
{
{
"NTLM"
,
"NTLM"
.
ToLower
()
,
"Negotiate"
,
"Negotiate"
.
ToLower
()
,
"Kerberos"
"Kerberos"
.
ToLower
()
};
};
/// <summary>
/// <summary>
...
@@ -47,7 +49,7 @@ namespace Titanium.Web.Proxy
...
@@ -47,7 +49,7 @@ namespace Titanium.Web.Proxy
//check in non-unique headers first
//check in non-unique headers first
var
header
=
response
.
Headers
.
NonUniqueHeaders
.
FirstOrDefault
(
var
header
=
response
.
Headers
.
NonUniqueHeaders
.
FirstOrDefault
(
x
=>
authHeaderNames
.
Any
(
y
=>
x
.
Key
.
Equals
(
y
,
StringComparison
.
OrdinalIgnoreCase
)));
x
=>
authHeaderNames
.
Contains
(
x
.
Key
.
ToLower
(
)));
if
(!
header
.
Equals
(
new
KeyValuePair
<
string
,
List
<
HttpHeader
>>()))
if
(!
header
.
Equals
(
new
KeyValuePair
<
string
,
List
<
HttpHeader
>>()))
{
{
...
@@ -63,8 +65,10 @@ namespace Titanium.Web.Proxy
...
@@ -63,8 +65,10 @@ namespace Titanium.Web.Proxy
//check in unique headers
//check in unique headers
if
(
authHeader
==
null
)
if
(
authHeader
==
null
)
{
{
headerName
=
null
;
//check in non-unique headers first
//check in non-unique headers first
var
uHeader
=
response
.
Headers
.
Headers
.
FirstOrDefault
(
x
=>
authHeaderNames
.
Any
(
y
=>
x
.
Key
.
Equals
(
y
,
StringComparison
.
OrdinalIgnoreCase
)));
var
uHeader
=
response
.
Headers
.
Headers
.
FirstOrDefault
(
x
=>
authHeaderNames
.
Contains
(
x
.
Key
.
ToLower
(
)));
if
(!
uHeader
.
Equals
(
new
KeyValuePair
<
string
,
HttpHeader
>()))
if
(!
uHeader
.
Equals
(
new
KeyValuePair
<
string
,
HttpHeader
>()))
{
{
...
@@ -82,7 +86,16 @@ namespace Titanium.Web.Proxy
...
@@ -82,7 +86,16 @@ namespace Titanium.Web.Proxy
if
(
authHeader
!=
null
)
if
(
authHeader
!=
null
)
{
{
string
scheme
=
authSchemes
.
FirstOrDefault
(
x
=>
authHeader
.
Value
.
Equals
(
x
,
StringComparison
.
OrdinalIgnoreCase
));
string
scheme
=
authSchemes
.
Contains
(
authHeader
.
Value
.
ToLower
())
?
authHeader
.
Value
.
ToLower
()
:
null
;
var
expectedAuthState
=
scheme
==
null
?
State
.
WinAuthState
.
INITIAL_TOKEN
:
State
.
WinAuthState
.
UNAUTHORIZED
;
if
(!
WinAuthEndPoint
.
ValidateWinAuthState
(
args
.
WebSession
.
RequestId
,
expectedAuthState
))
{
// Invalid state, create proper error message to client
await
RewriteUnauthorizedResponse
(
args
);
return
;
}
var
request
=
args
.
WebSession
.
Request
;
var
request
=
args
.
WebSession
.
Request
;
...
@@ -98,7 +111,7 @@ namespace Titanium.Web.Proxy
...
@@ -98,7 +111,7 @@ namespace Titanium.Web.Proxy
//replace existing authorization header if any
//replace existing authorization header if any
request
.
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
Authorization
,
auth
);
request
.
Headers
.
SetOrAddHeaderValue
(
KnownHeaders
.
Authorization
,
auth
);
//don't need to send body for Authorization request
//don't need to send body for Authorization request
if
(
request
.
HasBody
)
if
(
request
.
HasBody
)
{
{
...
@@ -108,7 +121,7 @@ namespace Titanium.Web.Proxy
...
@@ -108,7 +121,7 @@ namespace Titanium.Web.Proxy
//challenge value will start with any of the scheme selected
//challenge value will start with any of the scheme selected
else
else
{
{
scheme
=
authSchemes
.
First
OrDefault
(
x
=>
authHeader
.
Value
.
StartsWith
(
x
,
StringComparison
.
OrdinalIgnoreCase
)
&&
scheme
=
authSchemes
.
First
(
x
=>
authHeader
.
Value
.
StartsWith
(
x
,
StringComparison
.
OrdinalIgnoreCase
)
&&
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
);
...
@@ -130,13 +143,50 @@ namespace Titanium.Web.Proxy
...
@@ -130,13 +143,50 @@ namespace Titanium.Web.Proxy
//Should we cache all Set-Cokiee headers from server during auth process
//Should we cache all Set-Cokiee headers from server during auth process
//and send it to client after auth?
//and send it to client after auth?
//clear current server response
// Let ResponseHandler send the updated request
await
args
.
ClearResponse
();
args
.
ReRequest
=
true
;
}
}
/// <summary>
/// Rewrites the response body for failed authentication
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
internal
async
Task
RewriteUnauthorizedResponse
(
SessionEventArgs
args
)
{
var
response
=
args
.
WebSession
.
Response
;
// Strip authentication headers to avoid credentials prompt in client web browser
foreach
(
var
authHeaderName
in
authHeaderNames
)
{
response
.
Headers
.
RemoveHeader
(
authHeaderName
);
}
//request again with updated authorization header
// Add custom div to body to clarify that the proxy (not the client browser) failed authentication
//and server cookies
string
authErrorMessage
=
"<div class=\"inserted-by-proxy\"><h2>NTLM authentication through Titanium.Web.Proxy ("
+
await
HandleHttpSessionRequestInternal
(
args
.
WebSession
.
ServerConnection
,
args
);
args
.
ProxyClient
.
TcpClient
.
Client
.
LocalEndPoint
+
") failed. Please check credentials.</h2></div>"
;
string
originalErrorMessage
=
"<div class=\"inserted-by-proxy\"><h3>Response from remote web server below.</h3></div><br/>"
;
string
body
=
await
args
.
GetResponseBodyAsString
();
int
idx
=
body
.
IndexOfIgnoreCase
(
"<body>"
);
if
(
idx
>=
0
)
{
var
bodyPos
=
idx
+
"<body>"
.
Length
;
body
=
body
.
Insert
(
bodyPos
,
authErrorMessage
+
originalErrorMessage
);
}
}
else
{
// Cannot parse response body, replace it
body
=
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
+
"<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+
"<body>"
+
authErrorMessage
+
"</body>"
+
"</html>"
;
}
args
.
SetResponseBodyString
(
body
);
}
}
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment