tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsHttpNTLMAuth.cpp (13983B)


      1 /* vim:set ts=4 sw=2 sts=2 et ci: */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 // HttpLog.h should generally be included first
      7 #include "HttpLog.h"
      8 
      9 #include "nsHttpNTLMAuth.h"
     10 #include "nsIAuthModule.h"
     11 #include "nsCOMPtr.h"
     12 #include "nsServiceManagerUtils.h"
     13 #include "plbase64.h"
     14 #include "prnetdb.h"
     15 
     16 //-----------------------------------------------------------------------------
     17 
     18 #include "nsIPrefBranch.h"
     19 #include "nsIHttpAuthenticableChannel.h"
     20 #include "nsIURI.h"
     21 #ifdef XP_WIN
     22 #  include "nsIChannel.h"
     23 #  include "nsIX509Cert.h"
     24 #  include "nsITransportSecurityInfo.h"
     25 #endif
     26 #include "mozilla/Base64.h"
     27 #include "mozilla/CheckedInt.h"
     28 #include "mozilla/Maybe.h"
     29 #include "mozilla/Tokenizer.h"
     30 #include "nsCRT.h"
     31 #include "nsNetUtil.h"
     32 #include "nsIChannel.h"
     33 #include "nsUnicharUtils.h"
     34 #include "mozilla/net/HttpAuthUtils.h"
     35 #include "mozilla/ClearOnShutdown.h"
     36 #include "mozilla/net/DNS.h"
     37 #include "mozilla/StaticPrefs_browser.h"
     38 
     39 namespace mozilla {
     40 namespace net {
     41 
     42 static const char kAllowProxies[] = "network.automatic-ntlm-auth.allow-proxies";
     43 static const char kAllowNonFqdn[] =
     44    "network.automatic-ntlm-auth.allow-non-fqdn";
     45 static const char kTrustedURIs[] = "network.automatic-ntlm-auth.trusted-uris";
     46 static const char kForceGeneric[] = "network.auth.force-generic-ntlm";
     47 static const char kSSOinPBmode[] = "network.auth.private-browsing-sso";
     48 
     49 StaticRefPtr<nsHttpNTLMAuth> nsHttpNTLMAuth::gSingleton;
     50 
     51 static bool IsNonFqdn(nsIURI* uri) {
     52  nsAutoCString host;
     53  if (NS_FAILED(uri->GetAsciiHost(host))) {
     54    return false;
     55  }
     56 
     57  // return true if host does not contain a dot and is not an ip address
     58  return !host.IsEmpty() && !host.Contains('.') && !HostIsIPLiteral(host);
     59 }
     60 
     61 // Check to see if we should use our generic (internal) NTLM auth module.
     62 static bool ForceGenericNTLM() {
     63  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     64  if (!prefs) return false;
     65  bool flag = false;
     66 
     67  if (NS_FAILED(prefs->GetBoolPref(kForceGeneric, &flag))) flag = false;
     68 
     69  LOG(("Force use of generic ntlm auth module: %d\n", flag));
     70  return flag;
     71 }
     72 
     73 // Check to see if we should use default credentials for this host or proxy.
     74 static bool CanUseDefaultCredentials(nsIHttpAuthenticableChannel* channel,
     75                                     bool isProxyAuth) {
     76  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     77  if (!prefs) {
     78    return false;
     79  }
     80 
     81  // Proxy should go all the time, it's not considered a privacy leak
     82  // to send default credentials to a proxy.
     83  if (isProxyAuth) {
     84    bool val;
     85    if (NS_FAILED(prefs->GetBoolPref(kAllowProxies, &val))) val = false;
     86    LOG(("Default credentials allowed for proxy: %d\n", val));
     87    return val;
     88  }
     89 
     90  // Prevent using default credentials for authentication when we are in the
     91  // private browsing mode (but not in "never remember history" mode) and when
     92  // not explicitely allowed.  Otherwise, it would cause a privacy data leak.
     93  nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(channel);
     94  MOZ_ASSERT(bareChannel);
     95 
     96  if (NS_UsePrivateBrowsing(bareChannel)) {
     97    bool ssoInPb;
     98    if (NS_SUCCEEDED(prefs->GetBoolPref(kSSOinPBmode, &ssoInPb)) && ssoInPb) {
     99      return true;
    100    }
    101 
    102    if (!StaticPrefs::browser_privatebrowsing_autostart()) {
    103      return false;
    104    }
    105  }
    106 
    107  nsCOMPtr<nsIURI> uri;
    108  (void)channel->GetURI(getter_AddRefs(uri));
    109 
    110  bool allowNonFqdn;
    111  if (NS_FAILED(prefs->GetBoolPref(kAllowNonFqdn, &allowNonFqdn))) {
    112    allowNonFqdn = false;
    113  }
    114  if (allowNonFqdn && uri && IsNonFqdn(uri)) {
    115    LOG(("Host is non-fqdn, default credentials are allowed\n"));
    116    return true;
    117  }
    118 
    119  bool isTrustedHost = (uri && auth::URIMatchesPrefPattern(uri, kTrustedURIs));
    120  LOG(("Default credentials allowed for host: %d\n", isTrustedHost));
    121  return isTrustedHost;
    122 }
    123 
    124 // Dummy class for session state object.  This class doesn't hold any data.
    125 // Instead we use its existence as a flag.  See ChallengeReceived.
    126 class nsNTLMSessionState final : public nsISupports {
    127  ~nsNTLMSessionState() = default;
    128 
    129 public:
    130  NS_DECL_ISUPPORTS
    131 };
    132 NS_IMPL_ISUPPORTS0(nsNTLMSessionState)
    133 
    134 //-----------------------------------------------------------------------------
    135 
    136 already_AddRefed<nsIHttpAuthenticator> nsHttpNTLMAuth::GetOrCreate() {
    137  nsCOMPtr<nsIHttpAuthenticator> authenticator;
    138  if (gSingleton) {
    139    authenticator = gSingleton;
    140  } else {
    141    gSingleton = new nsHttpNTLMAuth();
    142    ClearOnShutdown(&gSingleton);
    143    authenticator = gSingleton;
    144  }
    145 
    146  return authenticator.forget();
    147 }
    148 
    149 NS_IMPL_ISUPPORTS(nsHttpNTLMAuth, nsIHttpAuthenticator)
    150 
    151 NS_IMETHODIMP
    152 nsHttpNTLMAuth::ChallengeReceived(nsIHttpAuthenticableChannel* channel,
    153                                  const nsACString& challenge, bool isProxyAuth,
    154                                  nsISupports** sessionState,
    155                                  nsISupports** continuationState,
    156                                  bool* identityInvalid) {
    157  LOG(("nsHttpNTLMAuth::ChallengeReceived [ss=%p cs=%p]\n", *sessionState,
    158       *continuationState));
    159 
    160  // Use the native NTLM if available
    161  mUseNative = true;
    162 
    163  // NOTE: we don't define any session state, but we do use the pointer.
    164 
    165  *identityInvalid = false;
    166 
    167  /* Always fail Negotiate auth for Tor Browser. We don't need it. */
    168  return NS_ERROR_ABORT;
    169 
    170  // Start a new auth sequence if the challenge is exactly "NTLM".
    171  // If native NTLM auth apis are available and enabled through prefs,
    172  // try to use them.
    173  if (challenge.Equals("NTLM"_ns, nsCaseInsensitiveCStringComparator)) {
    174    nsCOMPtr<nsIAuthModule> module;
    175 
    176 #ifdef MOZ_AUTH_EXTENSION
    177    // Check to see if we should default to our generic NTLM auth module
    178    // through UseGenericNTLM. (We use native auth by default if the
    179    // system provides it.) If *sessionState is non-null, we failed to
    180    // instantiate a native NTLM module the last time, so skip trying again.
    181    bool forceGeneric = ForceGenericNTLM();
    182    if (!forceGeneric && !*sessionState) {
    183      // Check for approved default credentials hosts and proxies. If
    184      // *continuationState is non-null, the last authentication attempt
    185      // failed so skip default credential use.
    186      if (!*continuationState &&
    187          CanUseDefaultCredentials(channel, isProxyAuth)) {
    188        // Try logging in with the user's default credentials. If
    189        // successful, |identityInvalid| is false, which will trigger
    190        // a default credentials attempt once we return.
    191        module = nsIAuthModule::CreateInstance("sys-ntlm");
    192      }
    193 #  ifdef XP_WIN
    194      else {
    195        // Try to use native NTLM and prompt the user for their domain,
    196        // username, and password. (only supported by windows nsAuthSSPI
    197        // module.) Note, for servers that use LMv1 a weak hash of the user's
    198        // password will be sent. We rely on windows internal apis to decide
    199        // whether we should support this older, less secure version of the
    200        // protocol.
    201        module = nsIAuthModule::CreateInstance("sys-ntlm");
    202        *identityInvalid = true;
    203      }
    204 #  endif  // XP_WIN
    205      if (!module) LOG(("Native sys-ntlm auth module not found.\n"));
    206    }
    207 
    208 #  ifdef XP_WIN
    209    // On windows, never fall back unless the user has specifically requested
    210    // so.
    211    if (!forceGeneric && !module) return NS_ERROR_UNEXPECTED;
    212 #  endif
    213 
    214    // If no native support was available. Fall back on our internal NTLM
    215    // implementation.
    216    if (!module) {
    217      if (!*sessionState) {
    218        // Remember the fact that we cannot use the "sys-ntlm" module,
    219        // so we don't ever bother trying again for this auth domain.
    220        RefPtr<nsNTLMSessionState> state = new nsNTLMSessionState();
    221        state.forget(sessionState);
    222      }
    223 
    224      // Use our internal NTLM implementation. Note, this is less secure,
    225      // see bug 520607 for details.
    226      LOG(("Trying to fall back on internal ntlm auth.\n"));
    227      module = nsIAuthModule::CreateInstance("ntlm");
    228 
    229      mUseNative = false;
    230 
    231      // Prompt user for domain, username, and password.
    232      *identityInvalid = true;
    233    }
    234 #endif
    235 
    236    // If this fails, then it means that we cannot do NTLM auth.
    237    if (!module) {
    238      LOG(("No ntlm auth modules available.\n"));
    239      return NS_ERROR_UNEXPECTED;
    240    }
    241 
    242    // A non-null continuation state implies that we failed to authenticate.
    243    // Blow away the old authentication state, and use the new one.
    244    module.forget(continuationState);
    245  }
    246  return NS_OK;
    247 }
    248 
    249 NS_IMETHODIMP
    250 nsHttpNTLMAuth::GenerateCredentialsAsync(
    251    nsIHttpAuthenticableChannel* authChannel,
    252    nsIHttpAuthenticatorCallback* aCallback, const nsACString& challenge,
    253    bool isProxyAuth, const nsAString& domain, const nsAString& username,
    254    const nsAString& password, nsISupports* sessionState,
    255    nsISupports* continuationState, nsICancelable** aCancellable) {
    256  return NS_ERROR_NOT_IMPLEMENTED;
    257 }
    258 
    259 NS_IMETHODIMP
    260 nsHttpNTLMAuth::GenerateCredentials(
    261    nsIHttpAuthenticableChannel* authChannel, const nsACString& aChallenge,
    262    bool isProxyAuth, const nsAString& domain, const nsAString& user,
    263    const nsAString& pass, nsISupports** sessionState,
    264    nsISupports** continuationState, uint32_t* aFlags, nsACString& creds)
    265 
    266 {
    267  LOG(("nsHttpNTLMAuth::GenerateCredentials\n"));
    268 
    269  creds.Truncate();
    270  *aFlags = 0;
    271 
    272  // if user or password is empty, ChallengeReceived returned
    273  // identityInvalid = false, that means we are using default user
    274  // credentials; see  nsAuthSSPI::Init method for explanation of this
    275  // condition
    276  if (user.IsEmpty() || pass.IsEmpty()) *aFlags = USING_INTERNAL_IDENTITY;
    277 
    278  nsresult rv;
    279  nsCOMPtr<nsIAuthModule> module = do_QueryInterface(*continuationState, &rv);
    280  NS_ENSURE_SUCCESS(rv, rv);
    281 
    282  void *inBuf, *outBuf;
    283  uint32_t inBufLen, outBufLen;
    284  Maybe<nsTArray<uint8_t>> certArray;
    285 
    286  // initial challenge
    287  if (aChallenge.Equals("NTLM"_ns, nsCaseInsensitiveCStringComparator)) {
    288    // NTLM service name format is 'HTTP@host' for both http and https
    289    nsCOMPtr<nsIURI> uri;
    290    rv = authChannel->GetURI(getter_AddRefs(uri));
    291    if (NS_FAILED(rv)) return rv;
    292    nsAutoCString serviceName, host;
    293    rv = uri->GetAsciiHost(host);
    294    if (NS_FAILED(rv)) return rv;
    295    serviceName.AppendLiteral("HTTP@");
    296    serviceName.Append(host);
    297    // initialize auth module
    298    uint32_t reqFlags = nsIAuthModule::REQ_DEFAULT;
    299    if (isProxyAuth) reqFlags |= nsIAuthModule::REQ_PROXY_AUTH;
    300 
    301    rv = module->Init(serviceName, reqFlags, domain, user, pass);
    302    if (NS_FAILED(rv)) return rv;
    303 
    304    inBufLen = 0;
    305    inBuf = nullptr;
    306 // This update enables updated Windows machines (Win7 or patched previous
    307 // versions) and Linux machines running Samba (updated for Channel
    308 // Binding), to perform Channel Binding when authenticating using NTLMv2
    309 // and an outer secure channel.
    310 //
    311 // Currently only implemented for Windows, linux support will be landing in
    312 // a separate patch, update this #ifdef accordingly then.
    313 // Extended protection update is just for Linux and Windows machines.
    314 #if defined(XP_WIN) /* || defined (LINUX) */
    315    // We should retrieve the server certificate and compute the CBT,
    316    // but only when we are using the native NTLM implementation and
    317    // not the internal one.
    318    // It is a valid case not having the security info object.  This
    319    // occures when we connect an https site through an ntlm proxy.
    320    // After the ssl tunnel has been created, we get here the second
    321    // time and now generate the CBT from now valid security info.
    322    nsCOMPtr<nsIChannel> channel = do_QueryInterface(authChannel, &rv);
    323    if (NS_FAILED(rv)) return rv;
    324 
    325    nsCOMPtr<nsITransportSecurityInfo> securityInfo;
    326    rv = channel->GetSecurityInfo(getter_AddRefs(securityInfo));
    327    if (NS_FAILED(rv)) return rv;
    328 
    329    if (mUseNative && securityInfo) {
    330      nsCOMPtr<nsIX509Cert> cert;
    331      rv = securityInfo->GetServerCert(getter_AddRefs(cert));
    332      if (NS_FAILED(rv)) return rv;
    333 
    334      if (cert) {
    335        certArray.emplace();
    336        rv = cert->GetRawDER(*certArray);
    337        if (NS_FAILED(rv)) {
    338          return rv;
    339        }
    340 
    341        // If there is a server certificate, we pass it along the
    342        // first time we call GetNextToken().
    343        inBufLen = certArray->Length();
    344        inBuf = certArray->Elements();
    345      }
    346    }
    347 #endif
    348  } else {
    349    // decode challenge; skip past "NTLM " to the start of the base64
    350    // encoded data.
    351    if (aChallenge.Length() < 6) {
    352      return NS_ERROR_UNEXPECTED;  // bogus challenge
    353    }
    354 
    355    // strip off any padding (see bug 230351)
    356    nsDependentCSubstring challenge(aChallenge, 5);
    357    uint32_t len = challenge.Length();
    358    while (len > 0 && challenge[len - 1] == '=') {
    359      len--;
    360    }
    361 
    362    // decode into the input secbuffer
    363    rv = Base64Decode(challenge.BeginReading(), len, (char**)&inBuf, &inBufLen);
    364    if (NS_FAILED(rv)) {
    365      return rv;
    366    }
    367  }
    368 
    369  rv = module->GetNextToken(inBuf, inBufLen, &outBuf, &outBufLen);
    370  if (NS_SUCCEEDED(rv)) {
    371    // base64 encode data in output buffer and prepend "NTLM "
    372    CheckedUint32 credsLen = ((CheckedUint32(outBufLen) + 2) / 3) * 4;
    373    credsLen += 5;  // "NTLM "
    374    credsLen += 1;  // null terminate
    375 
    376    if (!credsLen.isValid()) {
    377      rv = NS_ERROR_FAILURE;
    378    } else {
    379      nsAutoCString encoded;
    380      (void)Base64Encode(nsDependentCSubstring((char*)outBuf, outBufLen),
    381                         encoded);
    382      creds = nsPrintfCString("NTLM %s", encoded.get());
    383    }
    384 
    385    // OK, we are done with |outBuf|
    386    free(outBuf);
    387  }
    388 
    389  // inBuf needs to be freed if it's not pointing into certArray
    390  if (inBuf && !certArray) {
    391    free(inBuf);
    392  }
    393 
    394  return rv;
    395 }
    396 
    397 NS_IMETHODIMP
    398 nsHttpNTLMAuth::GetAuthFlags(uint32_t* flags) {
    399  *flags = CONNECTION_BASED | IDENTITY_INCLUDES_DOMAIN | IDENTITY_ENCRYPTED;
    400  return NS_OK;
    401 }
    402 
    403 }  // namespace net
    404 }  // namespace mozilla