Bug 520607 - Remove use of "ntlm" auth module and replace with use of "sys-ntlm". r=wtc, cbiesinger. sr=bz.

This commit is contained in:
Jim Mathies 2009-11-19 16:12:43 -06:00
parent 7bc0b2bd75
commit 1e83668bfe
5 changed files with 130 additions and 58 deletions

View File

@ -20,6 +20,7 @@
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
* Jim Mathies <jmathies@mozilla.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
@ -228,12 +229,8 @@ nsAuthSSPI::Init(const char *serviceName,
{
LOG((" nsAuthSSPI::Init\n"));
// we don't expect to be passed any user credentials
NS_ASSERTION(!domain && !username && !password, "unexpected credentials");
// if we're configured for SPNEGO (Negotiate) or Kerberos, then it's critical
// that the caller supply a service name to be used.
// For NTLM, the service principal name can no longer be null. (Bug 487872)
// 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);
nsresult rv;
@ -266,18 +263,41 @@ nsAuthSSPI::Init(const char *serviceName,
TimeStamp useBefore;
SEC_WINNT_AUTH_IDENTITY_W ai;
SEC_WINNT_AUTH_IDENTITY_W *pai = nsnull;
// domain, username, and password will be null if nsHttpNTLMAuth's ChallengeReceived
// returns false for identityInvalid. Use default credentials in this case by passing
// null for pai.
if (username && password) {
if (domain) {
ai.Domain = const_cast<unsigned short*>(domain);
ai.DomainLength = wcslen(domain);
}
else {
ai.Domain = NULL;
ai.DomainLength = 0;
}
ai.User = const_cast<unsigned short*>(username);
ai.UserLength = wcslen(username);
ai.Password = const_cast<unsigned short*>(password);
ai.PasswordLength = wcslen(password);
ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
pai = &ai;
}
rc = (sspi->AcquireCredentialsHandleW)(NULL,
package,
SECPKG_CRED_OUTBOUND,
NULL,
NULL,
pai,
NULL,
NULL,
&mCred,
&useBefore);
if (rc != SEC_E_OK)
return NS_ERROR_UNEXPECTED;
LOG(("AcquireCredentialsHandle() succeeded.\n"));
return NS_OK;
}
@ -351,6 +371,14 @@ nsAuthSSPI::GetNextToken(const void *inToken,
&ctxAttr,
&ignored);
if (rc == SEC_I_CONTINUE_NEEDED || rc == SEC_E_OK) {
#ifdef PR_LOGGING
if (rc == SEC_E_OK)
LOG(("InitializeSecurityContext: succeeded.\n"));
else
LOG(("InitializeSecurityContext: continue.\n"));
#endif
if (!ob.cbBuffer) {
nsMemory::Free(ob.pvBuffer);
ob.pvBuffer = NULL;

View File

@ -257,15 +257,15 @@ nsHttpNegotiateAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
challenge++;
len = strlen(challenge);
// strip off any padding (see bug 230351)
while (challenge[len - 1] == '=')
len--;
inTokenLen = (len * 3)/4;
inToken = malloc(inTokenLen);
if (!inToken)
return (NS_ERROR_OUT_OF_MEMORY);
// strip off any padding (see bug 230351)
while (challenge[len - 1] == '=')
len--;
//
// Decode the response that followed the "Negotiate" token
//

View File

@ -822,6 +822,15 @@ pref("network.auth.use-sspi", true);
#endif
// Controls which NTLM authentication implementation we default to. True forces
// the use of our generic (internal) NTLM authentication implementation vs. any
// native implementation provided by the os. This pref is for diagnosing issues
// with native NTLM. (See bug 520607 for details.) Using generic NTLM authentication
// can expose the user to reflection attack vulnerabilities. Do not change this
// unless you know what you're doing!
// This pref should be removed 6 months after the release of firefox 3.6.
pref("network.auth.force-generic-ntlm", false);
// The following prefs are used to enable automatic use of the operating
// system's NTLM implementation to silently authenticate the user with their
// Window's domain logon. The trusted-uris pref follows the format of the

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Darin Fisher <darin@meer.net>
* Jim Mathies <jmathies@mozilla.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
@ -54,6 +55,7 @@
static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris";
static const char kForceGeneric[] = "network.auth.force-generic-ntlm";
// XXX MatchesBaseURI and TestPref are duplicated in nsHttpNegotiateAuth.cpp,
// but since that file lives in a separate library we cannot directly share it.
@ -169,37 +171,48 @@ TestPref(nsIURI *uri, const char *pref)
return PR_FALSE;
}
// Check to see if we should use our generic (internal) NTLM auth module.
static PRBool
CanUseSysNTLM(nsIHttpChannel *channel, PRBool isProxyAuth)
ForceGenericNTLM()
{
// check prefs
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs)
return PR_FALSE;
PRBool flag = PR_FALSE;
if (NS_FAILED(prefs->GetBoolPref(kForceGeneric, &flag)))
flag = PR_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 PRBool
CanUseDefaultCredentials(nsIHttpChannel *channel, PRBool isProxyAuth)
{
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs)
return PR_FALSE;
PRBool val;
if (isProxyAuth) {
PRBool val;
if (NS_FAILED(prefs->GetBoolPref(kAllowProxies, &val)))
val = PR_FALSE;
LOG(("sys-ntlm allowed for proxy: %d\n", val));
LOG(("Default credentials allowed for proxy: %d\n", val));
return val;
}
else {
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
if (uri && TestPref(uri, kTrustedURIs)) {
LOG(("sys-ntlm allowed for host\n"));
return PR_TRUE;
}
}
return PR_FALSE;
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
PRBool isTrustedHost = (uri && TestPref(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 existance as a flag. See ChallengeReceived.
class nsNTLMSessionState : public nsISupports
class nsNTLMSessionState : public nsISupports
{
public:
NS_DECL_ISUPPORTS
@ -221,40 +234,58 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel,
LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n",
*sessionState, *continuationState));
// NOTE: we don't define any session state
// NOTE: we don't define any session state, but we do use the pointer.
*identityInvalid = PR_FALSE;
// start new auth sequence if challenge is exactly "NTLM"
// 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;
//
// our session state is non-null to indicate that we've flagged
// this auth domain as not accepting the system's default login.
//
PRBool trySysNTLM = (*sessionState == nsnull);
//
// we may have access to a built-in SSPI library,
// which could be used to authenticate the user without prompting.
//
// if the continuationState is null, then we may want to try using
// the SSPI NTLM module. however, we need to take care to only use
// that module when speaking to a trusted host. because the SSPI
// may send a weak LMv1 hash of the user's password, we cannot just
// send it to any server.
//
if (trySysNTLM && !*continuationState && CanUseSysNTLM(channel, isProxyAuth)) {
module = do_CreateInstance(NS_AUTH_MODULE_CONTRACTID_PREFIX "sys-ntlm");
// 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.
PRBool 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 = PR_TRUE;
}
#endif // XP_WIN
#ifdef PR_LOGGING
if (!module)
LOG(("failed to load sys-ntlm module\n"));
LOG(("Native sys-ntlm auth module not found.\n"));
#endif
}
// it's possible that there is no ntlm-sspi auth module...
#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,
// 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)
@ -262,18 +293,23 @@ nsHttpNTLMAuth::ChallengeReceived(nsIHttpChannel *channel,
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");
// prompt user for domain, username, and password...
// Prompt user for domain, username, and password.
*identityInvalid = PR_TRUE;
}
// if this fails, then it means that we cannot do NTLM auth.
if (!module)
// 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;
}
// non-null continuation state implies that we failed to authenticate.
// blow away the old authentication state, and use the new one.
// 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;
@ -332,16 +368,16 @@ nsHttpNTLMAuth::GenerateCredentials(nsIHttpChannel *httpChannel,
challenge += 5;
len -= 5;
// strip off any padding (see bug 230351)
while (challenge[len - 1] == '=')
len--;
// decode into the input secbuffer
inBufLen = (len * 3)/4; // sufficient size (see plbase64.h)
inBuf = nsMemory::Alloc(inBufLen);
if (!inBuf)
return NS_ERROR_OUT_OF_MEMORY;
// strip off any padding (see bug 230351)
while (challenge[len - 1] == '=')
len--;
if (PL_Base64Decode(challenge, len, (char *) inBuf) == nsnull) {
nsMemory::Free(inBuf);
return NS_ERROR_UNEXPECTED; // improper base64 encoding

View File

@ -802,7 +802,6 @@ nsNTLMAuthModule::Init(const char *serviceName,
const PRUnichar *username,
const PRUnichar *password)
{
NS_ASSERTION(serviceName == nsnull, "unexpected service name");
NS_ASSERTION(serviceFlags == nsIAuthModule::REQ_DEFAULT, "unexpected service flags");
mDomain = domain;