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
d10bb71a
Commit
d10bb71a
authored
May 23, 2017
by
justcoding121
Committed by
justcoding121
May 23, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
#139 Fix Win Auth
parent
d431a3ef
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
218 additions
and
41 deletions
+218
-41
WinAuthTests.cs
Tests/Titanium.Web.Proxy.UnitTests/WinAuthTests.cs
+2
-1
SessionEventArgs.cs
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
+15
-3
EndPoint.cs
Titanium.Web.Proxy/Network/WinAuth/Security/EndPoint.cs
+43
-25
WinAuthHandler.cs
Titanium.Web.Proxy/Network/WinAuth/WinAuthHandler.cs
+11
-4
ProxyServer.cs
Titanium.Web.Proxy/ProxyServer.cs
+9
-0
ResponseHandler.cs
Titanium.Web.Proxy/ResponseHandler.cs
+17
-8
Titanium.Web.Proxy.csproj
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
+1
-0
WinAuthHandler.cs
Titanium.Web.Proxy/WinAuthHandler.cs
+120
-0
No files found.
Tests/Titanium.Web.Proxy.UnitTests/WinAuthTests.cs
View file @
d10bb71a
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
System
;
using
Titanium.Web.Proxy.Network.WinAuth
;
using
Titanium.Web.Proxy.Network.WinAuth
;
namespace
Titanium.Web.Proxy.UnitTests
namespace
Titanium.Web.Proxy.UnitTests
...
@@ -9,7 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests
...
@@ -9,7 +10,7 @@ namespace Titanium.Web.Proxy.UnitTests
[
TestMethod
]
[
TestMethod
]
public
void
Test_Acquire_Client_Token
()
public
void
Test_Acquire_Client_Token
()
{
{
var
token
=
WinAuthHandler
.
GetInitialAuthToken
(
"mylocalserver.com"
);
var
token
=
WinAuthHandler
.
GetInitialAuthToken
(
"mylocalserver.com"
,
"NTLM"
,
Guid
.
NewGuid
()
);
Assert
.
IsTrue
(
token
.
Length
>
1
);
Assert
.
IsTrue
(
token
.
Length
>
1
);
}
}
}
}
...
...
Titanium.Web.Proxy/EventArguments/SessionEventArgs.cs
View file @
d10bb71a
...
@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy.EventArguments
...
@@ -44,7 +44,7 @@ namespace Titanium.Web.Proxy.EventArguments
public
Guid
Id
=>
WebSession
.
RequestId
;
public
Guid
Id
=>
WebSession
.
RequestId
;
/// <summary>
/// <summary>
/// Should we send
a rerequest
/// Should we send
the request again
/// </summary>
/// </summary>
public
bool
ReRequest
{
get
;
set
;
}
public
bool
ReRequest
{
get
;
set
;
}
...
@@ -136,9 +136,21 @@ namespace Titanium.Web.Proxy.EventArguments
...
@@ -136,9 +136,21 @@ namespace Titanium.Web.Proxy.EventArguments
}
}
/// <summary>
/// <summary>
///
Read response body as byte[] for current response
///
reinit response object
/// </summary>
/// </summary>
private
async
Task
ReadResponseBody
()
internal
async
Task
ClearResponse
()
{
//siphon out the body
await
ReadResponseBody
();
WebSession
.
Response
.
Dispose
();
WebSession
.
Response
=
new
Response
();
}
/// <summary>
/// Read response body as byte[] for current response
/// </summary>
private
async
Task
ReadResponseBody
()
{
{
//If not already read (not cached yet)
//If not already read (not cached yet)
if
(
WebSession
.
Response
.
ResponseBody
==
null
)
if
(
WebSession
.
Response
.
ResponseBody
==
null
)
...
...
Titanium.Web.Proxy/Network/WinAuth/Security/EndPoint.cs
View file @
d10bb71a
...
@@ -3,13 +3,30 @@
...
@@ -3,13 +3,30 @@
namespace
Titanium.Web.Proxy.Network.WinAuth.Security
namespace
Titanium.Web.Proxy.Network.WinAuth.Security
{
{
using
System
;
using
System
;
using
System.Collections.Concurrent
;
using
System.Collections.Generic
;
using
System.Runtime.InteropServices
;
using
System.Runtime.InteropServices
;
using
System.Security.Principal
;
using
System.Security.Principal
;
using
static
Common
;
using
static
Common
;
internal
class
EndPoint
internal
class
EndPoint
{
{
internal
static
byte
[]
AcquireInitialSecurityToken
(
string
hostname
)
/// <summary>
/// Keep track of auth states for reuse in final challenge response
/// </summary>
private
static
IDictionary
<
Guid
,
State
>
authStates
=
new
ConcurrentDictionary
<
Guid
,
State
>();
/// <summary>
/// Acquire the intial client token to send
/// </summary>
/// <param name="hostname"></param>
/// <param name="authScheme"></param>
/// <param name="requestId"></param>
/// <returns></returns>
internal
static
byte
[]
AcquireInitialSecurityToken
(
string
hostname
,
string
authScheme
,
Guid
requestId
)
{
{
byte
[]
token
=
null
;
byte
[]
token
=
null
;
...
@@ -28,8 +45,8 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -28,8 +45,8 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
result
=
AcquireCredentialsHandle
(
result
=
AcquireCredentialsHandle
(
WindowsIdentity
.
GetCurrent
().
Name
,
WindowsIdentity
.
GetCurrent
().
Name
,
"NTLM"
,
authScheme
,
Common
.
SecurityCredentialsOutbound
,
SecurityCredentialsOutbound
,
IntPtr
.
Zero
,
IntPtr
.
Zero
,
IntPtr
.
Zero
,
IntPtr
.
Zero
,
0
,
0
,
...
@@ -64,6 +81,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -64,6 +81,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
}
}
token
=
clientToken
.
GetBytes
();
token
=
clientToken
.
GetBytes
();
authStates
.
Add
(
requestId
,
state
);
}
}
finally
finally
{
{
...
@@ -79,8 +97,10 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -79,8 +97,10 @@ 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>
/// <returns></returns>
/// <returns></returns>
internal
static
byte
[]
AcquireFinalSecurityToken
(
string
hostname
,
byte
[]
serverChallenge
)
internal
static
byte
[]
AcquireFinalSecurityToken
(
string
hostname
,
byte
[]
serverChallenge
,
Guid
requestId
)
{
{
byte
[]
token
=
null
;
byte
[]
token
=
null
;
...
@@ -95,27 +115,10 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -95,27 +115,10 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
{
{
int
result
;
int
result
;
var
state
=
new
State
();
var
state
=
authStates
[
requestId
];
result
=
AcquireCredentialsHandle
(
WindowsIdentity
.
GetCurrent
().
Name
,
"NTLM"
,
SecurityCredentialsOutbound
,
IntPtr
.
Zero
,
IntPtr
.
Zero
,
0
,
IntPtr
.
Zero
,
ref
state
.
Credentials
,
ref
NewLifeTime
);
if
(
result
!=
SuccessfulResult
)
{
// Credentials acquire operation failed.
return
null
;
}
result
=
InitializeSecurityContext
(
ref
state
.
Credentials
,
result
=
InitializeSecurityContext
(
ref
state
.
Credentials
,
IntPtr
.
Zero
,
ref
state
.
Context
,
hostname
,
hostname
,
StandardContextAttributes
,
StandardContextAttributes
,
0
,
0
,
...
@@ -134,6 +137,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -134,6 +137,7 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
return
null
;
return
null
;
}
}
authStates
.
Remove
(
requestId
);
token
=
clientToken
.
GetBytes
();
token
=
clientToken
.
GetBytes
();
}
}
finally
finally
...
@@ -156,9 +160,23 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
...
@@ -156,9 +160,23 @@ namespace Titanium.Web.Proxy.Network.WinAuth.Security
ref
SecurityBufferDesciption
pInput
,
//PSecBufferDesc SecBufferDesc
ref
SecurityBufferDesciption
pInput
,
//PSecBufferDesc SecBufferDesc
int
Reserved2
,
int
Reserved2
,
out
SecurityHandle
phNewContext
,
//PCtxtHandle
out
SecurityHandle
phNewContext
,
//PCtxtHandle
out
Common
.
SecurityBufferDesciption
pOutput
,
//PSecBufferDesc SecBufferDesc
out
SecurityBufferDesciption
pOutput
,
//PSecBufferDesc SecBufferDesc
out
uint
pfContextAttr
,
//managed ulong == 64 bits!!!
out
uint
pfContextAttr
,
//managed ulong == 64 bits!!!
out
Common
.
SecurityInteger
ptsExpiry
);
//PTimeStamp
out
SecurityInteger
ptsExpiry
);
//PTimeStamp
[
DllImport
(
"secur32"
,
CharSet
=
CharSet
.
Auto
,
SetLastError
=
true
)]
static
extern
int
InitializeSecurityContext
(
ref
SecurityHandle
phCredential
,
//PCredHandle
ref
SecurityHandle
phContext
,
//PCtxtHandle
string
pszTargetName
,
int
fContextReq
,
int
Reserved1
,
int
TargetDataRep
,
ref
SecurityBufferDesciption
SecBufferDesc
,
//PSecBufferDesc SecBufferDesc
int
Reserved2
,
out
SecurityHandle
phNewContext
,
//PCtxtHandle
out
SecurityBufferDesciption
pOutput
,
//PSecBufferDesc SecBufferDesc
out
uint
pfContextAttr
,
//managed ulong == 64 bits!!!
out
SecurityInteger
ptsExpiry
);
//PTimeStamp
[
DllImport
(
"secur32.dll"
,
CharSet
=
CharSet
.
Auto
,
SetLastError
=
false
)]
[
DllImport
(
"secur32.dll"
,
CharSet
=
CharSet
.
Auto
,
SetLastError
=
false
)]
private
static
extern
int
AcquireCredentialsHandle
(
private
static
extern
int
AcquireCredentialsHandle
(
...
...
Titanium.Web.Proxy/Network/WinAuth/WinAuthHandler.cs
View file @
d10bb71a
...
@@ -15,10 +15,13 @@ namespace Titanium.Web.Proxy.Network.WinAuth
...
@@ -15,10 +15,13 @@ namespace Titanium.Web.Proxy.Network.WinAuth
/// using credentials of user running the proxy server process
/// using credentials of user running the proxy server process
/// </summary>
/// </summary>
/// <param name="serverHostname"></param>
/// <param name="serverHostname"></param>
/// <param name="authScheme"></param>
/// <param name="requestId"></param>
/// <returns></returns>
/// <returns></returns>
public
static
string
GetInitialAuthToken
(
string
serverHostname
)
public
static
string
GetInitialAuthToken
(
string
serverHostname
,
string
authScheme
,
Guid
requestId
)
{
{
var
tokenBytes
=
EndPoint
.
AcquireInitialSecurityToken
(
serverHostname
);
var
tokenBytes
=
EndPoint
.
AcquireInitialSecurityToken
(
serverHostname
,
authScheme
,
requestId
);
return
string
.
Concat
(
" "
,
Convert
.
ToBase64String
(
tokenBytes
));
return
string
.
Concat
(
" "
,
Convert
.
ToBase64String
(
tokenBytes
));
}
}
...
@@ -28,10 +31,14 @@ namespace Titanium.Web.Proxy.Network.WinAuth
...
@@ -28,10 +31,14 @@ namespace Titanium.Web.Proxy.Network.WinAuth
/// </summary>
/// </summary>
/// <param name="serverHostname"></param>
/// <param name="serverHostname"></param>
/// <param name="serverToken"></param>
/// <param name="serverToken"></param>
/// <param name="requestId"></param>
/// <returns></returns>
/// <returns></returns>
public
static
string
GetFinalAuthToken
(
string
serverHostname
,
string
serverToken
)
public
static
string
GetFinalAuthToken
(
string
serverHostname
,
string
serverToken
,
Guid
requestId
)
{
{
var
tokenBytes
=
EndPoint
.
AcquireFinalSecurityToken
(
serverHostname
,
Convert
.
FromBase64String
(
serverToken
));
var
tokenBytes
=
EndPoint
.
AcquireFinalSecurityToken
(
serverHostname
,
Convert
.
FromBase64String
(
serverToken
),
requestId
);
return
string
.
Concat
(
" "
,
Convert
.
ToBase64String
(
tokenBytes
));
return
string
.
Concat
(
" "
,
Convert
.
ToBase64String
(
tokenBytes
));
}
}
...
...
Titanium.Web.Proxy/ProxyServer.cs
View file @
d10bb71a
...
@@ -196,6 +196,15 @@ namespace Titanium.Web.Proxy
...
@@ -196,6 +196,15 @@ namespace Titanium.Web.Proxy
/// </summary>
/// </summary>
public
bool
ForwardToUpstreamGateway
{
get
;
set
;
}
public
bool
ForwardToUpstreamGateway
{
get
;
set
;
}
/// <summary>
/// Enable disable Windows Authentication (NTLM/Kerberos)
/// Note: NTLM/Kerberos will always send local credentials of current user
/// who is running the proxy process. This is because a man
/// in middle attack is not currently supported
/// (which would require windows delegation enabled for this server process)
/// </summary>
public
bool
EnableWinAuth
{
get
;
set
;
}
=
true
;
/// <summary>
/// <summary>
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// Verifies the remote Secure Sockets Layer (SSL) certificate used for authentication
/// </summary>
/// </summary>
...
...
Titanium.Web.Proxy/ResponseHandler.cs
View file @
d10bb71a
...
@@ -36,6 +36,18 @@ namespace Titanium.Web.Proxy
...
@@ -36,6 +36,18 @@ namespace Titanium.Web.Proxy
args
.
WebSession
.
Response
.
ResponseStream
=
args
.
WebSession
.
ServerConnection
.
Stream
;
args
.
WebSession
.
Response
.
ResponseStream
=
args
.
WebSession
.
ServerConnection
.
Stream
;
}
}
//check for windows authentication
if
(
EnableWinAuth
&&
args
.
WebSession
.
Response
.
ResponseStatusCode
==
"401"
)
{
var
disposed
=
await
Handle401UnAuthorized
(
args
);
if
(
disposed
)
{
return
true
;
}
}
args
.
ReRequest
=
false
;
args
.
ReRequest
=
false
;
//If user requested call back then do it
//If user requested call back then do it
...
@@ -44,16 +56,13 @@ namespace Titanium.Web.Proxy
...
@@ -44,16 +56,13 @@ namespace Titanium.Web.Proxy
await
BeforeResponse
.
InvokeParallelAsync
(
this
,
args
);
await
BeforeResponse
.
InvokeParallelAsync
(
this
,
args
);
}
}
//if user requested to send request again
//likely after making modifications from User Response Handler
if
(
args
.
ReRequest
)
if
(
args
.
ReRequest
)
{
{
if
(
args
.
WebSession
.
ServerConnection
!=
null
)
//clear current response
{
await
args
.
ClearResponse
();
args
.
WebSession
.
ServerConnection
.
Dispose
();
var
disposed
=
await
HandleHttpSessionRequestInternal
(
args
.
WebSession
.
ServerConnection
,
args
,
false
);
Interlocked
.
Decrement
(
ref
serverConnectionCount
);
}
var
connection
=
await
GetServerConnection
(
args
);
var
disposed
=
await
HandleHttpSessionRequestInternal
(
null
,
args
,
true
);
return
disposed
;
return
disposed
;
}
}
...
...
Titanium.Web.Proxy/Titanium.Web.Proxy.csproj
View file @
d10bb71a
...
@@ -127,6 +127,7 @@
...
@@ -127,6 +127,7 @@
<Compile
Include=
"Shared\ProxyConstants.cs"
/>
<Compile
Include=
"Shared\ProxyConstants.cs"
/>
<Compile
Include=
"Network\Tcp\TcpRow.cs"
/>
<Compile
Include=
"Network\Tcp\TcpRow.cs"
/>
<Compile
Include=
"Network\Tcp\TcpTable.cs"
/>
<Compile
Include=
"Network\Tcp\TcpTable.cs"
/>
<Compile
Include=
"WinAuthHandler.cs"
/>
</ItemGroup>
</ItemGroup>
<ItemGroup>
<ItemGroup>
<COMReference
Include=
"CERTENROLLLib"
>
<COMReference
Include=
"CERTENROLLLib"
>
...
...
Titanium.Web.Proxy/WinAuthHandler.cs
0 → 100644
View file @
d10bb71a
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Threading.Tasks
;
using
Titanium.Web.Proxy.EventArguments
;
using
Titanium.Web.Proxy.Models
;
using
Titanium.Web.Proxy.Network.WinAuth
;
using
Titanium.Web.Proxy.Extensions
;
namespace
Titanium.Web.Proxy
{
public
partial
class
ProxyServer
{
//possible header names
private
static
List
<
string
>
authHeaderNames
=
new
List
<
string
>()
{
"WWW-Authenticate"
,
//IIS 6.0 messed up names below
"WWWAuthenticate"
,
"NTLMAuthorization"
,
"NegotiateAuthorization"
,
"KerberosAuthorization"
};
private
static
List
<
string
>
authSchemes
=
new
List
<
string
>()
{
"NTLM"
,
"Negotiate"
,
"Kerberos"
};
/// <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
.
ContainsIgnoreCase
(
y
)))
.
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
.
ContainsIgnoreCase
(
x
))
?
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
));
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
;
}
}
}
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