tor-browser

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

nsHTTPSOnlyStreamListener.cpp (9352B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "nsHTTPSOnlyStreamListener.h"
      8 
      9 #include "NSSErrorsService.h"
     10 #include "mozilla/TimeStamp.h"
     11 #include "mozilla/dom/WindowGlobalParent.h"
     12 #include "mozilla/glean/DomSecurityMetrics.h"
     13 #include "mozpkix/pkixnss.h"
     14 #include "nsCOMPtr.h"
     15 #include "nsHTTPSOnlyUtils.h"
     16 #include "nsIChannel.h"
     17 #include "nsIRequest.h"
     18 #include "nsITransportSecurityInfo.h"
     19 #include "nsIURI.h"
     20 #include "nsIWebProgressListener.h"
     21 #include "nsPrintfCString.h"
     22 #include "secerr.h"
     23 #include "sslerr.h"
     24 
     25 NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener,
     26                  nsIRequestObserver)
     27 
     28 nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener(
     29    nsIStreamListener* aListener, nsILoadInfo* aLoadInfo)
     30    : mListener(aListener), mCreationStart(mozilla::TimeStamp::Now()) {
     31  RefPtr<mozilla::dom::WindowGlobalParent> wgp =
     32      mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
     33          aLoadInfo->GetInnerWindowID());
     34  // For Top-level document loads (which don't have a requesting window-context)
     35  // we compute these flags once we create the Document in nsSecureBrowserUI.
     36  if (wgp) {
     37    wgp->TopWindowContext()->AddSecurityState(
     38        nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED);
     39  }
     40 }
     41 
     42 NS_IMETHODIMP
     43 nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest* aRequest,
     44                                           nsIInputStream* aInputStream,
     45                                           uint64_t aOffset, uint32_t aCount) {
     46  return mListener->OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
     47 }
     48 
     49 NS_IMETHODIMP
     50 nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest* request) {
     51  return mListener->OnStartRequest(request);
     52 }
     53 
     54 NS_IMETHODIMP
     55 nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest* request,
     56                                         nsresult aStatus) {
     57  nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     58 
     59  // Note: CouldBeHttpsOnlyError also returns true if there was no error
     60  if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(channel, aStatus)) {
     61    RecordUpgradeTelemetry(request, aStatus);
     62    LogUpgradeFailure(request, aStatus);
     63 
     64    // If the request failed and there is a requesting window-context, set
     65    // HTTPS-Only state flag to indicate a failed upgrade.
     66    // For Top-level document loads (which don't have a requesting
     67    // window-context) we simply check in the UI code whether we landed on the
     68    // HTTPS-Only error page.
     69    if (NS_FAILED(aStatus)) {
     70      nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
     71      RefPtr<mozilla::dom::WindowGlobalParent> wgp =
     72          mozilla::dom::WindowGlobalParent::GetByInnerWindowId(
     73              loadInfo->GetInnerWindowID());
     74 
     75      if (wgp) {
     76        wgp->TopWindowContext()->AddSecurityState(
     77            nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED);
     78      }
     79    }
     80  }
     81 
     82  return mListener->OnStopRequest(request, aStatus);
     83 }
     84 
     85 void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest* request,
     86                                                       nsresult aStatus) {
     87  // 1. Get time between now and when the initial upgrade request started
     88  mozilla::TimeDuration duration = mozilla::TimeStamp::Now() - mCreationStart;
     89 
     90  // 2. Assemble the category string
     91  // [!] All strings have to be present in Histograms.json
     92  nsresult rv;
     93  nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
     94  if (NS_FAILED(rv)) {
     95    return;
     96  }
     97 
     98  nsAutoCString category;
     99  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    100  nsContentPolicyType internalType = loadInfo->InternalContentPolicyType();
    101 
    102  if (internalType == nsIContentPolicy::TYPE_DOCUMENT) {
    103    category.AppendLiteral("top_");
    104  } else {
    105    category.AppendLiteral("sub_");
    106  }
    107 
    108  if (NS_SUCCEEDED(aStatus)) {
    109    category.AppendLiteral("successful");
    110  } else {
    111    int32_t code = -1 * NS_ERROR_GET_CODE(aStatus);
    112 
    113    if (aStatus == NS_ERROR_REDIRECT_LOOP) {
    114      category.AppendLiteral("f_redirectloop");
    115    } else if (aStatus == NS_ERROR_NET_TIMEOUT ||
    116               aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL) {
    117      category.AppendLiteral("f_timeout");
    118    } else if (aStatus == NS_BINDING_ABORTED) {
    119      category.AppendLiteral("f_aborted");
    120    } else if (aStatus == NS_ERROR_CONNECTION_REFUSED) {
    121      category.AppendLiteral("f_cxnrefused");
    122    } else if (mozilla::psm::IsNSSErrorCode(code)) {
    123      switch (code) {
    124        case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
    125          category.AppendLiteral("f_ssl_selfsignd");
    126          break;
    127        case SSL_ERROR_BAD_CERT_DOMAIN:
    128          category.AppendLiteral("f_ssl_badcertdm");
    129          break;
    130        case SEC_ERROR_UNKNOWN_ISSUER:
    131          category.AppendLiteral("f_ssl_unkwnissr");
    132          break;
    133        default:
    134          category.AppendLiteral("f_ssl_other");
    135          break;
    136      }
    137    } else {
    138      category.AppendLiteral("f_other");
    139    }
    140  }
    141  mozilla::glean::security::https_only_mode_upgrade_time.Get(category)
    142      .AccumulateRawDuration(duration);
    143 
    144  bool success = NS_SUCCEEDED(aStatus);
    145  ExtContentPolicyType externalType = loadInfo->GetExternalContentPolicyType();
    146  auto typeKey = nsAutoCString("unknown");
    147 
    148  if (externalType == ExtContentPolicy::TYPE_MEDIA) {
    149    switch (internalType) {
    150      case nsIContentPolicy::TYPE_INTERNAL_AUDIO:
    151      case nsIContentPolicy::TYPE_INTERNAL_TRACK:
    152        typeKey = "audio"_ns;
    153        break;
    154 
    155      case nsIContentPolicy::TYPE_INTERNAL_VIDEO:
    156        typeKey = "video"_ns;
    157        break;
    158 
    159      default:
    160        MOZ_ASSERT_UNREACHABLE();
    161        break;
    162    }
    163  } else {
    164    switch (externalType) {
    165      case ExtContentPolicy::TYPE_SCRIPT:
    166        typeKey = "script"_ns;
    167        break;
    168 
    169      case ExtContentPolicy::TYPE_OBJECT:
    170        typeKey = "object"_ns;
    171        break;
    172 
    173      case ExtContentPolicy::TYPE_DOCUMENT:
    174        typeKey = "document"_ns;
    175        break;
    176 
    177      case ExtContentPolicy::TYPE_SUBDOCUMENT:
    178        typeKey = "subdocument"_ns;
    179        break;
    180 
    181      case ExtContentPolicy::TYPE_XMLHTTPREQUEST:
    182        typeKey = "xmlhttprequest"_ns;
    183        break;
    184 
    185      case ExtContentPolicy::TYPE_IMAGE:
    186      case ExtContentPolicy::TYPE_IMAGESET:
    187        typeKey = "image"_ns;
    188        break;
    189 
    190      case ExtContentPolicy::TYPE_DTD:
    191        typeKey = "dtd"_ns;
    192        break;
    193 
    194      case ExtContentPolicy::TYPE_FONT:
    195      case ExtContentPolicy::TYPE_UA_FONT:
    196        typeKey = "font"_ns;
    197        break;
    198 
    199      case ExtContentPolicy::TYPE_FETCH:
    200        typeKey = "fetch"_ns;
    201        break;
    202 
    203      case ExtContentPolicy::TYPE_WEBSOCKET:
    204        typeKey = "websocket"_ns;
    205        break;
    206 
    207      case ExtContentPolicy::TYPE_STYLESHEET:
    208        typeKey = "stylesheet"_ns;
    209        break;
    210 
    211      case ExtContentPolicy::TYPE_CSP_REPORT:
    212        typeKey = "cspreport"_ns;
    213        break;
    214 
    215      case ExtContentPolicy::TYPE_WEB_MANIFEST:
    216        typeKey = "webmanifest"_ns;
    217        break;
    218 
    219      case ExtContentPolicy::TYPE_PING:
    220        typeKey = "ping"_ns;
    221        break;
    222 
    223      case ExtContentPolicy::TYPE_XSLT:
    224        typeKey = "xslt"_ns;
    225        break;
    226 
    227      case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA:
    228        typeKey = "proxied-webrtc"_ns;
    229        break;
    230 
    231      case ExtContentPolicy::TYPE_INVALID:
    232      case ExtContentPolicy::TYPE_OTHER:
    233      case ExtContentPolicy::TYPE_MEDIA:  // already handled above
    234      case ExtContentPolicy::TYPE_BEACON:
    235      case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD:
    236      case ExtContentPolicy::TYPE_SPECULATIVE:
    237      case ExtContentPolicy::TYPE_WEB_TRANSPORT:
    238      case ExtContentPolicy::TYPE_WEB_IDENTITY:
    239      case ExtContentPolicy::TYPE_JSON:
    240        break;
    241        // Do not add default: so that compilers can catch the missing case.
    242    }
    243  }
    244 
    245  // Needs bug 1657470 (New Metric Type: "Keyed Categorical") before
    246  // this can be migrated to Glean.
    247  mozilla::glean::security::https_only_mode_upgrade_type
    248      .Get(typeKey, success ? "true"_ns : "false"_ns)
    249      .Add();
    250 }
    251 
    252 void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest* request,
    253                                                  nsresult aStatus) {
    254  // If the request failed we'll log it to the console with the error-code
    255  if (NS_SUCCEEDED(aStatus)) {
    256    return;
    257  }
    258  nsresult rv;
    259  // Try to query for the channel-object
    260  nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
    261  if (NS_FAILED(rv)) {
    262    return;
    263  }
    264 
    265  nsCOMPtr<nsIURI> uri;
    266  rv = channel->GetURI(getter_AddRefs(uri));
    267  if (NS_FAILED(rv)) {
    268    return;
    269  }
    270  // Logging URI as well as Module- and Error-Code
    271  AutoTArray<nsString, 2> params = {
    272      NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()),
    273      NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u",
    274                                            NS_ERROR_GET_MODULE(aStatus),
    275                                            NS_ERROR_GET_CODE(aStatus)))};
    276 
    277  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    278  nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedRequest", params,
    279                                       nsIScriptError::errorFlag, loadInfo,
    280                                       uri);
    281 }