forked from mirrors/gecko-dev
		
	 d60d69e2cb
			
		
	
	
		d60d69e2cb
		
	
	
	
	
		
			
			MozReview-Commit-ID: DjDkL20wRg0 --HG-- extra : rebase_source : a343d83d1f4e97e4ba56d0f57fec93079df0b5ea
		
			
				
	
	
		
			426 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			426 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* vim:set ts=4 sw=4 sts=4 et ci: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| // HttpLog.h should generally be included first
 | |
| #include "HttpLog.h"
 | |
| 
 | |
| #include "nsHttpNTLMAuth.h"
 | |
| #include "nsIAuthModule.h"
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsServiceManagerUtils.h"
 | |
| #include "plbase64.h"
 | |
| #include "plstr.h"
 | |
| #include "prnetdb.h"
 | |
| 
 | |
| //-----------------------------------------------------------------------------
 | |
| 
 | |
| #include "nsIPrefBranch.h"
 | |
| #include "nsIPrefService.h"
 | |
| #include "nsIHttpAuthenticableChannel.h"
 | |
| #include "nsIURI.h"
 | |
| #ifdef XP_WIN
 | |
| #include "nsIChannel.h"
 | |
| #include "nsIX509Cert.h"
 | |
| #include "nsISSLStatus.h"
 | |
| #include "nsISSLStatusProvider.h"
 | |
| #endif
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/Base64.h"
 | |
| #include "mozilla/CheckedInt.h"
 | |
| #include "mozilla/Tokenizer.h"
 | |
| #include "mozilla/UniquePtr.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "nsNetUtil.h"
 | |
| #include "nsIChannel.h"
 | |
| #include "nsUnicharUtils.h"
 | |
| #include "mozilla/net/HttpAuthUtils.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace net {
 | |
| 
 | |
| static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
 | |
| static const char kAllowNonFqdn[] = "network.automatic-ntlm-auth.allow-non-fqdn";
 | |
| static const char kTrustedURIs[]  = "network.automatic-ntlm-auth.trusted-uris";
 | |
| static const char kForceGeneric[] = "network.auth.force-generic-ntlm";
 | |
| static const char kSSOinPBmode[] = "network.auth.private-browsing-sso";
 | |
| 
 | |
| static bool
 | |
| IsNonFqdn(nsIURI *uri)
 | |
| {
 | |
|     nsAutoCString host;
 | |
|     PRNetAddr addr;
 | |
| 
 | |
|     if (NS_FAILED(uri->GetAsciiHost(host)))
 | |
|         return false;
 | |
| 
 | |
|     // return true if host does not contain a dot and is not an ip address
 | |
|     return !host.IsEmpty() && !host.Contains('.') &&
 | |
|            PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
 | |
| }
 | |
| 
 | |
| // Check to see if we should use our generic (internal) NTLM auth module.
 | |
| static bool
 | |
| ForceGenericNTLM()
 | |
| {
 | |
|     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
 | |
|     if (!prefs)
 | |
|         return false;
 | |
|     bool flag = false;
 | |
| 
 | |
|     if (NS_FAILED(prefs->GetBoolPref(kForceGeneric, &flag)))
 | |
|         flag = false;
 | |
| 
 | |
|     LOG(("Force use of generic ntlm auth module: %d\n", flag));
 | |
|     return flag;
 | |
| }
 | |
| 
 | |
| // Check to see if we should use default credentials for this host or proxy.
 | |
| static bool
 | |
| CanUseDefaultCredentials(nsIHttpAuthenticableChannel *channel,
 | |
|                          bool isProxyAuth)
 | |
| {
 | |
|     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
 | |
|     if (!prefs) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // Proxy should go all the time, it's not considered a privacy leak
 | |
|     // to send default credentials to a proxy.
 | |
|     if (isProxyAuth) {
 | |
|         bool val;
 | |
|         if (NS_FAILED(prefs->GetBoolPref(kAllowProxies, &val)))
 | |
|             val = false;
 | |
|         LOG(("Default credentials allowed for proxy: %d\n", val));
 | |
|         return val;
 | |
|     }
 | |
| 
 | |
|     // Prevent using default credentials for authentication when we are in the
 | |
|     // private browsing mode (but not in "never remember history" mode) and when
 | |
|     // not explicitely allowed.  Otherwise, it would cause a privacy data leak.
 | |
|     nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
 | |
|     MOZ_ASSERT(bareChannel);
 | |
| 
 | |
|     if (NS_UsePrivateBrowsing(bareChannel)) {
 | |
|         bool ssoInPb;
 | |
|         if (NS_SUCCEEDED(prefs->GetBoolPref(kSSOinPBmode, &ssoInPb)) &&
 | |
|             ssoInPb) {
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         bool dontRememberHistory;
 | |
|         if (NS_SUCCEEDED(prefs->GetBoolPref("browser.privatebrowsing.autostart",
 | |
|                                             &dontRememberHistory)) &&
 | |
|             !dontRememberHistory) {
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIURI> uri;
 | |
|     Unused << channel->GetURI(getter_AddRefs(uri));
 | |
| 
 | |
|     bool allowNonFqdn;
 | |
|     if (NS_FAILED(prefs->GetBoolPref(kAllowNonFqdn, &allowNonFqdn)))
 | |
|         allowNonFqdn = false;
 | |
|     if (allowNonFqdn && uri && IsNonFqdn(uri)) {
 | |
|         LOG(("Host is non-fqdn, default credentials are allowed\n"));
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool isTrustedHost = (uri && auth::URIMatchesPrefPattern(uri, kTrustedURIs));
 | |
|     LOG(("Default credentials allowed for host: %d\n", isTrustedHost));
 | |
|     return isTrustedHost;
 | |
| }
 | |
| 
 | |
| // Dummy class for session state object.  This class doesn't hold any data.
 | |
| // Instead we use its existence as a flag.  See ChallengeReceived.
 | |
| class nsNTLMSessionState final : public nsISupports
 | |
| {
 | |
|     ~nsNTLMSessionState() {}
 | |
| public:
 | |
|     NS_DECL_ISUPPORTS
 | |
| };
 | |
| NS_IMPL_ISUPPORTS0(nsNTLMSessionState)
 | |
| 
 | |
| //-----------------------------------------------------------------------------
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(nsHttpNTLMAuth, nsIHttpAuthenticator)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel *channel,
 | |
|                                   const char     *challenge,
 | |
|                                   bool            isProxyAuth,
 | |
|                                   nsISupports   **sessionState,
 | |
|                                   nsISupports   **continuationState,
 | |
|                                   bool           *identityInvalid)
 | |
| {
 | |
|     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;
 | |
| 
 | |
|     // Start a new auth sequence if the challenge is exactly "NTLM".
 | |
|     // If native NTLM auth apis are available and enabled through prefs,
 | |
|     // try to use them.
 | |
|     if (PL_strcasecmp(challenge, "NTLM") == 0) {
 | |
|         nsCOMPtr<nsISupports> module;
 | |
| 
 | |
|         // Check to see if we should default to our generic NTLM auth module
 | |
|         // through UseGenericNTLM. (We use native auth by default if the
 | |
|         // system provides it.) If *sessionState is non-null, we failed to
 | |
|         // instantiate a native NTLM module the last time, so skip trying again.
 | |
|         bool forceGeneric = ForceGenericNTLM();
 | |
|         if (!forceGeneric && !*sessionState) {
 | |
|             // Check for approved default credentials hosts and proxies. If
 | |
|             // *continuationState is non-null, the last authentication attempt
 | |
|             // failed so skip default credential use.
 | |
|             if (!*continuationState && CanUseDefaultCredentials(channel, isProxyAuth)) {
 | |
|                 // Try logging in with the user's default credentials. If
 | |
|                 // successful, |identityInvalid| is false, which will trigger
 | |
|                 // a default credentials attempt once we return.
 | |
|                 module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
 | |
|             }
 | |
| #ifdef XP_WIN
 | |
|             else {
 | |
|                 // Try to use native NTLM and prompt the user for their domain,
 | |
|                 // username, and password. (only supported by windows nsAuthSSPI module.)
 | |
|                 // Note, for servers that use LMv1 a weak hash of the user's password
 | |
|                 // will be sent. We rely on windows internal apis to decide whether
 | |
|                 // we should support this older, less secure version of the protocol.
 | |
|                 module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
 | |
|                 *identityInvalid = true;
 | |
|             }
 | |
| #endif // XP_WIN
 | |
|             if (!module)
 | |
|                 LOG(("Native sys-ntlm auth module not found.\n"));
 | |
|         }
 | |
| 
 | |
| #ifdef XP_WIN
 | |
|         // On windows, never fall back unless the user has specifically requested so.
 | |
|         if (!forceGeneric && !module)
 | |
|             return NS_ERROR_UNEXPECTED;
 | |
| #endif
 | |
| 
 | |
|         // If no native support was available. Fall back on our internal NTLM implementation.
 | |
|         if (!module) {
 | |
|             if (!*sessionState) {
 | |
|                 // Remember the fact that we cannot use the "sys-ntlm" module,
 | |
|                 // so we don't ever bother trying again for this auth domain.
 | |
|                 *sessionState = new nsNTLMSessionState();
 | |
|                 if (!*sessionState)
 | |
|                     return NS_ERROR_OUT_OF_MEMORY;
 | |
|                 NS_ADDREF(*sessionState);
 | |
|             }
 | |
| 
 | |
|             // Use our internal NTLM implementation. Note, this is less secure,
 | |
|             // 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;
 | |
|         }
 | |
| 
 | |
|         // If this fails, then it means that we cannot do NTLM auth.
 | |
|         if (!module) {
 | |
|             LOG(("No ntlm auth modules available.\n"));
 | |
|             return NS_ERROR_UNEXPECTED;
 | |
|         }
 | |
| 
 | |
|         // A non-null continuation state implies that we failed to authenticate.
 | |
|         // Blow away the old authentication state, and use the new one.
 | |
|         module.swap(*continuationState);
 | |
|     }
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsHttpNTLMAuth::GenerateCredentialsAsync(nsIHttpAuthenticableChannel *authChannel,
 | |
|                                          nsIHttpAuthenticatorCallback* aCallback,
 | |
|                                          const char *challenge,
 | |
|                                          bool isProxyAuth,
 | |
|                                          const char16_t *domain,
 | |
|                                          const char16_t *username,
 | |
|                                          const char16_t *password,
 | |
|                                          nsISupports *sessionState,
 | |
|                                          nsISupports *continuationState,
 | |
|                                          nsICancelable **aCancellable)
 | |
| {
 | |
|   return NS_ERROR_NOT_IMPLEMENTED;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsHttpNTLMAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
 | |
|                                     const char      *challenge,
 | |
|                                     bool             isProxyAuth,
 | |
|                                     const char16_t *domain,
 | |
|                                     const char16_t *user,
 | |
|                                     const char16_t *pass,
 | |
|                                     nsISupports    **sessionState,
 | |
|                                     nsISupports    **continuationState,
 | |
|                                     uint32_t       *aFlags,
 | |
|                                     char           **creds)
 | |
| 
 | |
| {
 | |
|     LOG(("nsHttpNTLMAuth::GenerateCredentials\n"));
 | |
| 
 | |
|     *creds = nullptr;
 | |
|     *aFlags = 0;
 | |
| 
 | |
|     // if user or password is empty, ChallengeReceived returned
 | |
|     // identityInvalid = false, that means we are using default user
 | |
|     // credentials; see  nsAuthSSPI::Init method for explanation of this
 | |
|     // condition
 | |
|     if (!user || !pass)
 | |
|         *aFlags = USING_INTERNAL_IDENTITY;
 | |
| 
 | |
|     nsresult rv;
 | |
|     nsCOMPtr<nsIAuthModule> module = do_QueryInterface(*continuationState, &rv);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     void *inBuf, *outBuf;
 | |
|     uint32_t inBufLen, outBufLen;
 | |
| 
 | |
|     // initial challenge
 | |
|     if (PL_strcasecmp(challenge, "NTLM") == 0) {
 | |
|         // NTLM service name format is 'HTTP@host' for both http and https
 | |
|         nsCOMPtr<nsIURI> uri;
 | |
|         rv = authChannel->GetURI(getter_AddRefs(uri));
 | |
|         if (NS_FAILED(rv))
 | |
|             return rv;
 | |
|         nsAutoCString serviceName, host;
 | |
|         rv = uri->GetAsciiHost(host);
 | |
|         if (NS_FAILED(rv))
 | |
|             return rv;
 | |
|         serviceName.AppendLiteral("HTTP@");
 | |
|         serviceName.Append(host);
 | |
|         // initialize auth module
 | |
|         uint32_t reqFlags = nsIAuthModule::REQ_DEFAULT;
 | |
|         if (isProxyAuth)
 | |
|             reqFlags |= nsIAuthModule::REQ_PROXY_AUTH;
 | |
| 
 | |
|         rv = module->Init(serviceName.get(), reqFlags, domain, user, pass);
 | |
|         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;
 | |
| 
 | |
|             uint32_t length;
 | |
|             uint8_t* certArray;
 | |
|             rv = cert->GetRawDER(&length, &certArray);
 | |
|             if (NS_FAILED(rv))
 | |
|                 return rv;
 | |
| 
 | |
|             // 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 = nullptr;
 | |
|         }
 | |
| #else // Extended protection update is just for Linux and Windows machines.
 | |
|         inBufLen = 0;
 | |
|         inBuf = nullptr;
 | |
| #endif
 | |
|     }
 | |
|     else {
 | |
|         // decode challenge; skip past "NTLM " to the start of the base64
 | |
|         // encoded data.
 | |
|         int len = strlen(challenge);
 | |
|         if (len < 6)
 | |
|             return NS_ERROR_UNEXPECTED; // bogus challenge
 | |
|         challenge += 5;
 | |
|         len -= 5;
 | |
| 
 | |
|         // strip off any padding (see bug 230351)
 | |
|         while (challenge[len - 1] == '=')
 | |
|           len--;
 | |
| 
 | |
|         // decode into the input secbuffer
 | |
|         rv = Base64Decode(challenge, len, (char**)&inBuf, &inBufLen);
 | |
|         if (NS_FAILED(rv)) {
 | |
|             return rv;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     rv = module->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen);
 | |
|     if (NS_SUCCEEDED(rv)) {
 | |
|         // base64 encode data in output buffer and prepend "NTLM "
 | |
|         CheckedUint32 credsLen = ((CheckedUint32(outBufLen) + 2) / 3) * 4;
 | |
|         credsLen += 5; // "NTLM "
 | |
|         credsLen += 1; // null terminate
 | |
| 
 | |
|         if (!credsLen.isValid()) {
 | |
|           rv = NS_ERROR_FAILURE;
 | |
|         } else {
 | |
|           *creds = (char *) moz_xmalloc(credsLen.value());
 | |
|           memcpy(*creds, "NTLM ", 5);
 | |
|           PL_Base64Encode((char *) outBuf, outBufLen, *creds + 5);
 | |
|           (*creds)[credsLen.value() - 1] = '\0'; // null terminate
 | |
|         }
 | |
| 
 | |
|         // OK, we are done with |outBuf|
 | |
|         free(outBuf);
 | |
|     }
 | |
| 
 | |
|     if (inBuf)
 | |
|         free(inBuf);
 | |
| 
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsHttpNTLMAuth::GetAuthFlags(uint32_t *flags)
 | |
| {
 | |
|     *flags = CONNECTION_BASED | IDENTITY_INCLUDES_DOMAIN | IDENTITY_ENCRYPTED;
 | |
|     return NS_OK;
 | |
| }
 | |
| 
 | |
| } // namespace net
 | |
| } // namespace mozilla
 |