mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 573043 - Enable Extended Protection (channel and service bindng) for NTLM authentication, r=jmathies
This commit is contained in:
parent
e78cd66e66
commit
6713d954e6
@ -21,6 +21,7 @@
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
* Jim Mathies <jmathies@mozilla.com>
|
||||
* Guillermo Robla Vicario <groblavicario@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -52,6 +53,7 @@
|
||||
#include "nsIDNSRecord.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsICryptoHash.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
@ -190,6 +192,8 @@ nsAuthSSPI::nsAuthSSPI(pType package)
|
||||
: mServiceFlags(REQ_DEFAULT)
|
||||
, mMaxTokenLen(0)
|
||||
, mPackage(package)
|
||||
, mCertDERData(nsnull)
|
||||
, mCertDERLength(0)
|
||||
{
|
||||
memset(&mCred, 0, sizeof(mCred));
|
||||
memset(&mCtxt, 0, sizeof(mCtxt));
|
||||
@ -212,6 +216,14 @@ nsAuthSSPI::~nsAuthSSPI()
|
||||
void
|
||||
nsAuthSSPI::Reset()
|
||||
{
|
||||
mIsFirst = true;
|
||||
|
||||
if (mCertDERData){
|
||||
nsMemory::Free(mCertDERData);
|
||||
mCertDERData = nsnull;
|
||||
mCertDERLength = 0;
|
||||
}
|
||||
|
||||
if (mCtxt.dwLower || mCtxt.dwUpper) {
|
||||
(sspi->DeleteSecurityContext)(&mCtxt);
|
||||
memset(&mCtxt, 0, sizeof(mCtxt));
|
||||
@ -229,6 +241,10 @@ nsAuthSSPI::Init(const char *serviceName,
|
||||
{
|
||||
LOG((" nsAuthSSPI::Init\n"));
|
||||
|
||||
mIsFirst = true;
|
||||
mCertDERLength = 0;
|
||||
mCertDERData = nsnull;
|
||||
|
||||
// The caller must supply a service name to be used. (For why we now require
|
||||
// a service name for NTLM, see bug 487872.)
|
||||
NS_ENSURE_TRUE(serviceName && *serviceName, NS_ERROR_INVALID_ARG);
|
||||
@ -314,19 +330,32 @@ nsAuthSSPI::Init(const char *serviceName,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The arguments inToken and inTokenLen are used to pass in the server
|
||||
// certificate (when available) in the first call of the function. The
|
||||
// second time these arguments hold an input token.
|
||||
NS_IMETHODIMP
|
||||
nsAuthSSPI::GetNextToken(const void *inToken,
|
||||
PRUint32 inTokenLen,
|
||||
void **outToken,
|
||||
PRUint32 *outTokenLen)
|
||||
{
|
||||
// String for end-point bindings.
|
||||
const char end_point[] = "tls-server-end-point:";
|
||||
const int end_point_length = sizeof(end_point) - 1;
|
||||
const int hash_size = 32; // Size of a SHA256 hash.
|
||||
const int cbt_size = hash_size + end_point_length;
|
||||
|
||||
SECURITY_STATUS rc;
|
||||
TimeStamp ignored;
|
||||
|
||||
DWORD ctxAttr, ctxReq = 0;
|
||||
CtxtHandle *ctxIn;
|
||||
SecBufferDesc ibd, obd;
|
||||
SecBuffer ib, ob;
|
||||
// Optional second input buffer for the CBT (Channel Binding Token)
|
||||
SecBuffer ib[2], ob;
|
||||
// Pointer to the block of memory that stores the CBT
|
||||
char* sspi_cbt = nsnull;
|
||||
SEC_CHANNEL_BINDINGS pendpoint_binding;
|
||||
|
||||
LOG(("entering nsAuthSSPI::GetNextToken()\n"));
|
||||
|
||||
@ -341,26 +370,123 @@ nsAuthSSPI::GetNextToken(const void *inToken,
|
||||
ctxReq |= ISC_REQ_MUTUAL_AUTH;
|
||||
|
||||
if (inToken) {
|
||||
ib.BufferType = SECBUFFER_TOKEN;
|
||||
ib.cbBuffer = inTokenLen;
|
||||
ib.pvBuffer = (void *) inToken;
|
||||
ibd.ulVersion = SECBUFFER_VERSION;
|
||||
ibd.cBuffers = 1;
|
||||
ibd.pBuffers = &ib;
|
||||
ctxIn = &mCtxt;
|
||||
}
|
||||
else {
|
||||
// If there is no input token, then we are starting a new
|
||||
// authentication sequence. If we have already initialized our
|
||||
// security context, then we're in trouble because it means that the
|
||||
// first sequence failed. We need to bail or else we might end up in
|
||||
// an infinite loop.
|
||||
if (mCtxt.dwLower || mCtxt.dwUpper) {
|
||||
if (mIsFirst) {
|
||||
// First time if it comes with a token,
|
||||
// the token represents the server certificate.
|
||||
mIsFirst = false;
|
||||
mCertDERLength = inTokenLen;
|
||||
mCertDERData = nsMemory::Alloc(inTokenLen);
|
||||
if (!mCertDERData)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
memcpy(mCertDERData, inToken, inTokenLen);
|
||||
|
||||
// We are starting a new authentication sequence.
|
||||
// If we have already initialized our
|
||||
// security context, then we're in trouble because it means that the
|
||||
// first sequence failed. We need to bail or else we might end up in
|
||||
// an infinite loop.
|
||||
if (mCtxt.dwLower || mCtxt.dwUpper) {
|
||||
LOG(("Cannot restart authentication sequence!"));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
ctxIn = nsnull;
|
||||
// The certificate needs to be erased before being passed
|
||||
// to InitializeSecurityContextW().
|
||||
inToken = nsnull;
|
||||
inTokenLen = 0;
|
||||
} else {
|
||||
ibd.ulVersion = SECBUFFER_VERSION;
|
||||
ibd.cBuffers = 0;
|
||||
ibd.pBuffers = ib;
|
||||
|
||||
// If we have stored a certificate, the Channel Binding Token
|
||||
// needs to be generated and sent in the first input buffer.
|
||||
if (mCertDERLength > 0) {
|
||||
// First we create a proper Endpoint Binding structure.
|
||||
pendpoint_binding.dwInitiatorAddrType = 0;
|
||||
pendpoint_binding.cbInitiatorLength = 0;
|
||||
pendpoint_binding.dwInitiatorOffset = 0;
|
||||
pendpoint_binding.dwAcceptorAddrType = 0;
|
||||
pendpoint_binding.cbAcceptorLength = 0;
|
||||
pendpoint_binding.dwAcceptorOffset = 0;
|
||||
pendpoint_binding.cbApplicationDataLength = cbt_size;
|
||||
pendpoint_binding.dwApplicationDataOffset =
|
||||
sizeof(SEC_CHANNEL_BINDINGS);
|
||||
|
||||
// Then add it to the array of sec buffers accordingly.
|
||||
ib[ibd.cBuffers].BufferType = SECBUFFER_CHANNEL_BINDINGS;
|
||||
ib[ibd.cBuffers].cbBuffer =
|
||||
pendpoint_binding.cbApplicationDataLength
|
||||
+ pendpoint_binding.dwApplicationDataOffset;
|
||||
|
||||
sspi_cbt = (char *) nsMemory::Alloc(ib[ibd.cBuffers].cbBuffer);
|
||||
if (!sspi_cbt){
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// Helper to write in the memory block that stores the CBT
|
||||
char* sspi_cbt_ptr = sspi_cbt;
|
||||
|
||||
ib[ibd.cBuffers].pvBuffer = sspi_cbt;
|
||||
ibd.cBuffers++;
|
||||
|
||||
memcpy(sspi_cbt_ptr, &pendpoint_binding,
|
||||
pendpoint_binding.dwApplicationDataOffset);
|
||||
sspi_cbt_ptr += pendpoint_binding.dwApplicationDataOffset;
|
||||
|
||||
memcpy(sspi_cbt_ptr, end_point, end_point_length);
|
||||
sspi_cbt_ptr += end_point_length;
|
||||
|
||||
// Start hashing. We are always doing SHA256, but depending
|
||||
// on the certificate, a different alogirthm might be needed.
|
||||
nsCAutoString hashString;
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsICryptoHash> crypto;
|
||||
crypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = crypto->Init(nsICryptoHash::SHA256);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = crypto->Update((unsigned char*)mCertDERData, mCertDERLength);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = crypto->Finish(false, hashString);
|
||||
if (NS_FAILED(rv)) {
|
||||
nsMemory::Free(mCertDERData);
|
||||
mCertDERData = nsnull;
|
||||
mCertDERLength = 0;
|
||||
nsMemory::Free(sspi_cbt);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Once the hash has been computed, we store it in memory right
|
||||
// after the Endpoint structure and the "tls-server-end-point:"
|
||||
// char array.
|
||||
memcpy(sspi_cbt_ptr, hashString.get(), hash_size);
|
||||
|
||||
// Free memory used to store the server certificate
|
||||
nsMemory::Free(mCertDERData);
|
||||
mCertDERData = nsnull;
|
||||
mCertDERLength = 0;
|
||||
} // End of CBT computation.
|
||||
|
||||
// We always need this SECBUFFER.
|
||||
ib[ibd.cBuffers].BufferType = SECBUFFER_TOKEN;
|
||||
ib[ibd.cBuffers].cbBuffer = inTokenLen;
|
||||
ib[ibd.cBuffers].pvBuffer = (void *) inToken;
|
||||
ibd.cBuffers++;
|
||||
ctxIn = &mCtxt;
|
||||
}
|
||||
} else { // First time and without a token (no server certificate)
|
||||
// We are starting a new authentication sequence. If we have already
|
||||
// initialized our security context, then we're in trouble because it
|
||||
// means that the first sequence failed. We need to bail or else we
|
||||
// might end up in an infinite loop.
|
||||
if (mCtxt.dwLower || mCtxt.dwUpper || mCertDERData || mCertDERLength) {
|
||||
LOG(("Cannot restart authentication sequence!"));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
ctxIn = NULL;
|
||||
mIsFirst = false;
|
||||
}
|
||||
|
||||
obd.ulVersion = SECBUFFER_VERSION;
|
||||
@ -369,8 +495,11 @@ nsAuthSSPI::GetNextToken(const void *inToken,
|
||||
ob.BufferType = SECBUFFER_TOKEN;
|
||||
ob.cbBuffer = mMaxTokenLen;
|
||||
ob.pvBuffer = nsMemory::Alloc(ob.cbBuffer);
|
||||
if (!ob.pvBuffer)
|
||||
if (!ob.pvBuffer){
|
||||
if (sspi_cbt)
|
||||
nsMemory::Free(sspi_cbt);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
memset(ob.pvBuffer, 0, ob.cbBuffer);
|
||||
|
||||
NS_ConvertUTF8toUTF16 wSN(mServiceName);
|
||||
@ -396,7 +525,9 @@ nsAuthSSPI::GetNextToken(const void *inToken,
|
||||
else
|
||||
LOG(("InitializeSecurityContext: continue.\n"));
|
||||
#endif
|
||||
|
||||
if (sspi_cbt)
|
||||
nsMemory::Free(sspi_cbt);
|
||||
|
||||
if (!ob.cbBuffer) {
|
||||
nsMemory::Free(ob.pvBuffer);
|
||||
ob.pvBuffer = NULL;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
* Guillermo Robla Vicario <groblavicario@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -81,6 +82,9 @@ private:
|
||||
nsString mDomain;
|
||||
nsString mUsername;
|
||||
nsString mPassword;
|
||||
bool mIsFirst;
|
||||
void* mCertDERData;
|
||||
PRUint32 mCertDERLength;
|
||||
};
|
||||
|
||||
#endif /* nsAuthSSPI_h__ */
|
||||
|
@ -22,6 +22,7 @@
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@meer.net>
|
||||
* Jim Mathies <jmathies@mozilla.com>
|
||||
* Guillermo Robla Vicario <groblavicario@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -52,6 +53,9 @@
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIHttpAuthenticableChannel.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsISSLStatus.h"
|
||||
#include "nsISSLStatusProvider.h"
|
||||
|
||||
static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
|
||||
static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris";
|
||||
@ -235,6 +239,9 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel,
|
||||
LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
|
||||
*sessionState, *continuationState));
|
||||
|
||||
// Use the native NTLM if available
|
||||
mUseNative = true;
|
||||
|
||||
// NOTE: we don't define any session state, but we do use the pointer.
|
||||
|
||||
*identityInvalid = false;
|
||||
@ -298,6 +305,8 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel,
|
||||
// see bug 520607 for details.
|
||||
LOG(("Trying to fall back on internal ntlm auth.\n"));
|
||||
module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "ntlm");
|
||||
|
||||
mUseNative = false;
|
||||
|
||||
// Prompt user for domain, username, and password.
|
||||
*identityInvalid = true;
|
||||
@ -366,8 +375,61 @@ nsHttpNTLMAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
// This update enables updated Windows machines (Win7 or patched previous
|
||||
// versions) and Linux machines running Samba (updated for Channel
|
||||
// Binding), to perform Channel Binding when authenticating using NTLMv2
|
||||
// and an outer secure channel.
|
||||
//
|
||||
// Currently only implemented for Windows, linux support will be landing in
|
||||
// a separate patch, update this #ifdef accordingly then.
|
||||
#if defined (XP_WIN) /* || defined (LINUX) */
|
||||
// We should retrieve the server certificate and compute the CBT,
|
||||
// but only when we are using the native NTLM implementation and
|
||||
// not the internal one.
|
||||
// It is a valid case not having the security info object. This
|
||||
// occures when we connect an https site through an ntlm proxy.
|
||||
// After the ssl tunnel has been created, we get here the second
|
||||
// time and now generate the CBT from now valid security info.
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(authChannel, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsISupports> security;
|
||||
rv = channel->GetSecurityInfo(getter_AddRefs(security));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsISSLStatusProvider> statusProvider =
|
||||
do_QueryInterface(security);
|
||||
|
||||
if (mUseNative && statusProvider) {
|
||||
nsCOMPtr<nsISSLStatus> status;
|
||||
rv = statusProvider->GetSSLStatus(getter_AddRefs(status));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIX509Cert> cert;
|
||||
rv = status->GetServerCert(getter_AddRefs(cert));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
PRUint32 length;
|
||||
PRUint8* certArray;
|
||||
cert->GetRawDER(&length, &certArray);
|
||||
|
||||
// If there is a server certificate, we pass it along the
|
||||
// first time we call GetNextToken().
|
||||
inBufLen = length;
|
||||
inBuf = certArray;
|
||||
} else {
|
||||
// If there is no server certificate, we don't pass anything.
|
||||
inBufLen = 0;
|
||||
inBuf = nsnull;
|
||||
}
|
||||
#else // Extended protection update is just for Linux and Windows machines.
|
||||
inBufLen = 0;
|
||||
inBuf = nsnull;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
// decode challenge; skip past "NTLM " to the start of the base64
|
||||
|
@ -20,6 +20,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Darin Fisher <darin@netscape.com>
|
||||
* Guillermo Robla Vicario <groblavicario@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -48,6 +49,11 @@ public:
|
||||
|
||||
nsHttpNTLMAuth() {}
|
||||
virtual ~nsHttpNTLMAuth() {}
|
||||
|
||||
private:
|
||||
// This flag indicates whether we are using the native NTLM implementation
|
||||
// or the internal one.
|
||||
bool mUseNative;
|
||||
};
|
||||
|
||||
#endif // !nsHttpNTLMAuth_h__
|
||||
|
Loading…
Reference in New Issue
Block a user