From 3d25e8316bd08fcdfea17550afc14b77a52b939f Mon Sep 17 00:00:00 2001 From: Honza Bambas Date: Tue, 8 Feb 2011 16:12:29 -0800 Subject: [PATCH] Fix bug 573043. r=jmathies@mozilla.com, a=blocker. --HG-- rename : security/manager/ssl/public/nsISSLStatus.idl => netwerk/base/public/nsISSLStatus.idl rename : security/manager/boot/public/nsISSLStatusProvider.idl => netwerk/base/public/nsISSLStatusProvider.idl rename : security/manager/ssl/public/nsIX509Cert.idl => netwerk/base/public/nsIX509Cert.idl --- extensions/auth/nsAuthSSPI.cpp | 169 ++++++++++++++++-- extensions/auth/nsAuthSSPI.h | 4 + netwerk/base/public/Makefile.in | 3 + .../base}/public/nsISSLStatus.idl | 0 .../base}/public/nsISSLStatusProvider.idl | 0 .../base}/public/nsIX509Cert.idl | 0 netwerk/protocol/http/nsHttpNTLMAuth.cpp | 66 +++++++ netwerk/protocol/http/nsHttpNTLMAuth.h | 6 + security/manager/boot/public/Makefile.in | 1 - security/manager/ssl/public/Makefile.in | 2 - 10 files changed, 229 insertions(+), 22 deletions(-) rename {security/manager/ssl => netwerk/base}/public/nsISSLStatus.idl (100%) rename {security/manager/boot => netwerk/base}/public/nsISSLStatusProvider.idl (100%) rename {security/manager/ssl => netwerk/base}/public/nsIX509Cert.idl (100%) diff --git a/extensions/auth/nsAuthSSPI.cpp b/extensions/auth/nsAuthSSPI.cpp index 9390c59d6b5..7b2e47e737d 100644 --- a/extensions/auth/nsAuthSSPI.cpp +++ b/extensions/auth/nsAuthSSPI.cpp @@ -21,6 +21,7 @@ * Contributor(s): * Darin Fisher * Jim Mathies + * Guillermo Robla Vicario * * 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 @@ -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 = PR_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 = PR_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 = PR_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 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(PR_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 = PR_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; diff --git a/extensions/auth/nsAuthSSPI.h b/extensions/auth/nsAuthSSPI.h index c4050fe9834..a7945e800f7 100644 --- a/extensions/auth/nsAuthSSPI.h +++ b/extensions/auth/nsAuthSSPI.h @@ -20,6 +20,7 @@ * * Contributor(s): * Darin Fisher + * Guillermo Robla Vicario * * 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; + PRBool mIsFirst; + void* mCertDERData; + PRUint32 mCertDERLength; }; #endif /* nsAuthSSPI_h__ */ diff --git a/netwerk/base/public/Makefile.in b/netwerk/base/public/Makefile.in index 633c096a98f..3bf85069870 100644 --- a/netwerk/base/public/Makefile.in +++ b/netwerk/base/public/Makefile.in @@ -59,6 +59,7 @@ SDK_XPIDLSRCS = \ nsIUploadChannel.idl \ nsIUnicharStreamListener.idl \ nsITraceableChannel.idl \ + nsIX509Cert.idl \ $(NULL) XPIDLSRCS = \ @@ -143,6 +144,8 @@ XPIDLSRCS = \ nsIRedirectResultListener.idl \ mozIThirdPartyUtil.idl \ nsISerializationHelper.idl \ + nsISSLStatus.idl \ + nsISSLStatusProvider.idl \ $(NULL) ifdef MOZ_IPC diff --git a/security/manager/ssl/public/nsISSLStatus.idl b/netwerk/base/public/nsISSLStatus.idl similarity index 100% rename from security/manager/ssl/public/nsISSLStatus.idl rename to netwerk/base/public/nsISSLStatus.idl diff --git a/security/manager/boot/public/nsISSLStatusProvider.idl b/netwerk/base/public/nsISSLStatusProvider.idl similarity index 100% rename from security/manager/boot/public/nsISSLStatusProvider.idl rename to netwerk/base/public/nsISSLStatusProvider.idl diff --git a/security/manager/ssl/public/nsIX509Cert.idl b/netwerk/base/public/nsIX509Cert.idl similarity index 100% rename from security/manager/ssl/public/nsIX509Cert.idl rename to netwerk/base/public/nsIX509Cert.idl diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.cpp b/netwerk/protocol/http/nsHttpNTLMAuth.cpp index 2fb16ae1789..3cc0de159bf 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp +++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp @@ -22,6 +22,7 @@ * Contributor(s): * Darin Fisher * Jim Mathies + * Guillermo Robla Vicario * * 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 = PR_TRUE; + // NOTE: we don't define any session state, but we do use the pointer. *identityInvalid = PR_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 = PR_FALSE; // Prompt user for domain, username, and password. *identityInvalid = PR_TRUE; @@ -366,8 +375,65 @@ 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) */ + PRBool isHttps; + rv = uri->SchemeIs("https", &isHttps); + if (NS_FAILED(rv)) + return rv; + + // When the url starts with https, 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. + if (isHttps && mUseNative) { + nsCOMPtr channel = do_QueryInterface(authChannel, &rv); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr security; + rv = channel->GetSecurityInfo(getter_AddRefs(security)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr + statusProvider(do_QueryInterface(security)); + NS_ENSURE_TRUE(statusProvider, NS_ERROR_FAILURE); + + rv = statusProvider->GetSSLStatus(getter_AddRefs(security)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr status(do_QueryInterface(security)); + NS_ENSURE_TRUE(status, NS_ERROR_FAILURE); + + nsCOMPtr 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 diff --git a/netwerk/protocol/http/nsHttpNTLMAuth.h b/netwerk/protocol/http/nsHttpNTLMAuth.h index ed22a3f5939..485605fb5e4 100644 --- a/netwerk/protocol/http/nsHttpNTLMAuth.h +++ b/netwerk/protocol/http/nsHttpNTLMAuth.h @@ -20,6 +20,7 @@ * * Contributor(s): * Darin Fisher + * Guillermo Robla Vicario * * 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. + PRBool mUseNative; }; #endif // !nsHttpNTLMAuth_h__ diff --git a/security/manager/boot/public/Makefile.in b/security/manager/boot/public/Makefile.in index 82d90cecaf2..70b8f30c3be 100644 --- a/security/manager/boot/public/Makefile.in +++ b/security/manager/boot/public/Makefile.in @@ -50,7 +50,6 @@ SDK_XPIDLSRCS = \ $(NULL) XPIDLSRCS = \ - nsISSLStatusProvider.idl \ nsIBufEntropyCollector.idl \ $(NULL) diff --git a/security/manager/ssl/public/Makefile.in b/security/manager/ssl/public/Makefile.in index fa84d3ac04e..27829846378 100644 --- a/security/manager/ssl/public/Makefile.in +++ b/security/manager/ssl/public/Makefile.in @@ -53,7 +53,6 @@ SDK_XPIDLSRCS = \ nsIASN1Sequence.idl \ nsICertificateDialogs.idl \ nsICRLInfo.idl \ - nsIX509Cert.idl \ nsIX509CertDB.idl \ nsIX509CertValidity.idl \ $(NULL) @@ -80,7 +79,6 @@ XPIDLSRCS = \ nsIGenKeypairInfoDlg.idl \ nsITokenDialogs.idl \ nsITokenPasswordDialogs.idl \ - nsISSLStatus.idl \ nsIKeygenThread.idl \ nsICMSSecureMessage.idl \ nsIUserCertPicker.idl \