tor-browser

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

nsHTTPSOnlyUtils.h (13728B)


      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 #ifndef nsHTTPSOnlyUtils_h___
      8 #define nsHTTPSOnlyUtils_h___
      9 
     10 #include "mozilla/net/DocumentLoadListener.h"
     11 #include "nsIScriptError.h"
     12 #include "nsISupports.h"
     13 
     14 class nsHTTPSOnlyUtils {
     15 public:
     16  enum UpgradeMode {
     17    NO_UPGRADE_MODE,
     18    HTTPS_ONLY_MODE,
     19    HTTPS_FIRST_MODE,
     20    SCHEMELESS_HTTPS_FIRST_MODE
     21  };
     22 
     23  /**
     24   * Returns the upgrade mode which should be used for a given load, based on
     25   * the prefs currently set.
     26   * @param aFromPrivateWindow   Whether the load in question is from a private
     27   *                             window.
     28   * @param aSchemelessInputType Information about the load possibly originating
     29   *                             from a schemeful or schemeless address bar
     30   *                             input.
     31   * @return                     Upgrade mode as an enum.
     32   */
     33  static UpgradeMode GetUpgradeMode(
     34      bool aFromPrivateWindow,
     35      nsILoadInfo::SchemelessInputType aSchemelessInputType =
     36          nsILoadInfo::SchemelessInputTypeUnset);
     37 
     38  /**
     39   * Returns the upgrade mode which should be used for a given load, based on
     40   * the prefs currently set.
     41   * @param aLoadInfo Load info for the load in question.
     42   * @return          Upgrade mode as an enum.
     43   */
     44  static UpgradeMode GetUpgradeMode(nsILoadInfo* aLoadInfo);
     45 
     46  /**
     47   * Potentially fires an http request for a top-level load (provided by
     48   * aDocumentLoadListener) in the background to avoid long timeouts in case
     49   * the upgraded https top-level load most likely will result in timeout.
     50   * @param aDocumentLoadListener The Document listener associated with
     51   *                              the original top-level channel.
     52   */
     53  static void PotentiallyFireHttpRequestToShortenTimout(
     54      mozilla::net::DocumentLoadListener* aDocumentLoadListener);
     55 
     56  /**
     57   * Determines if a request should get upgraded because of the HTTPS-Only mode.
     58   * If true, the httpsOnlyStatus flag in LoadInfo gets updated and a message is
     59   * logged in the console.
     60   * @param  aURI      nsIURI of request
     61   * @param  aLoadInfo nsILoadInfo of request
     62   * @return           true if request should get upgraded
     63   */
     64  static bool ShouldUpgradeRequest(nsIURI* aURI, nsILoadInfo* aLoadInfo);
     65 
     66  /**
     67   * Determines if a request should get upgraded because of the HTTPS-Only mode.
     68   * If true, a message is logged in the console.
     69   * @param  aURI      nsIURI of request
     70   * @param  aLoadInfo nsILoadInfo of request
     71   * @return           true if request should get upgraded
     72   */
     73  static bool ShouldUpgradeWebSocket(nsIURI* aURI, nsILoadInfo* aLoadInfo);
     74 
     75  /**
     76   * Determines if we might get stuck in an upgrade-downgrade-endless loop
     77   * where https-only upgrades the request to https and the website downgrades
     78   * the scheme to http again causing an endless upgrade downgrade loop. E.g.
     79   * https-only upgrades to https and the website answers with a meta-refresh
     80   * to downgrade to same-origin http version. Similarly this method breaks
     81   * the endless cycle for JS based redirects and 302 based redirects.
     82   * Note this function is also used when we got an HTTPS RR for the website.
     83   * @param  aURI      nsIURI of request
     84   * @param  aLoadInfo nsILoadInfo of request
     85   * @param  aOptions an options object indicating if the function
     86   *                  should be consulted for https-only or https-first mode or
     87   *                  the case that an HTTPS RR is presented.
     88   * @return           true if an endless loop is detected
     89   */
     90  enum class UpgradeDowngradeEndlessLoopOptions {
     91    EnforceForHTTPSOnlyMode,
     92    EnforceForHTTPSFirstMode,
     93    EnforceForHTTPSRR,
     94  };
     95  static bool IsUpgradeDowngradeEndlessLoop(
     96      nsIURI* aOldURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo,
     97      const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions =
     98          {});
     99 
    100  /**
    101   * Determines if a request should get upgraded because of the HTTPS-First
    102   * mode. If true, the httpsOnlyStatus in LoadInfo gets updated and a message
    103   * is logged in the console.
    104   * @param  aURI      nsIURI of request
    105   * @param  aLoadInfo nsILoadInfo of request
    106   * @return           true if request should get upgraded
    107   */
    108  static bool ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
    109                                             nsILoadInfo* aLoadInfo);
    110 
    111  /**
    112   * Determines if the request was previously upgraded with HTTPS-First, creates
    113   * a downgraded URI and logs to console.
    114   * @param  aStatus               Status code
    115   * @param  aDocumentLoadListener Failed document load listener
    116   * @return                       URI with http-scheme or nullptr
    117   */
    118  static already_AddRefed<nsIURI> PotentiallyDowngradeHttpsFirstRequest(
    119      mozilla::net::DocumentLoadListener* aDocumentLoadListener,
    120      nsresult aStatus);
    121 
    122  /**
    123   * Checks if the error code is on a block-list of codes that are probably
    124   * not related to a HTTPS-Only Mode upgrade.
    125   * @param  aChannel The failed Channel.
    126   * @param  aError Error Code from Request
    127   * @return        false if error is not related to upgrade
    128   */
    129  static bool CouldBeHttpsOnlyError(nsIChannel* aChannel, nsresult aError);
    130 
    131  /**
    132   * Logs localized message to either content console or browser console
    133   * @param aName            Localization key
    134   * @param aParams          Localization parameters
    135   * @param aFlags           Logging Flag (see nsIScriptError)
    136   * @param aLoadInfo        The loadinfo of the request.
    137   * @param [aURI]           Optional: URI to log
    138   * @param [aUseHttpsFirst] Optional: Log using HTTPS-First (vs. HTTPS-Only)
    139   */
    140  static void LogLocalizedString(const char* aName,
    141                                 const nsTArray<nsString>& aParams,
    142                                 uint32_t aFlags, nsILoadInfo* aLoadInfo,
    143                                 nsIURI* aURI = nullptr,
    144                                 bool aUseHttpsFirst = false);
    145 
    146  /**
    147   * Tests if a upgrade exception is set for a given principal.
    148   * @param  aPrincipal   The principal for whom the exception should be checked
    149   * @param  aUpgradeMode The upgrade mode that should be checked for
    150   * @return              True if exempt
    151   */
    152  static bool TestIfPrincipalIsExempt(nsIPrincipal* aPrincipal,
    153                                      UpgradeMode aUpgradeMode);
    154 
    155  /**
    156   * Tests if the HTTPS-Only Mode upgrade exception is set for channel result
    157   * principal and sets or removes the httpsOnlyStatus-flag on the loadinfo
    158   * accordingly.
    159   * Note: This function only adds an exemption for loads of TYPE_DOCUMENT.
    160   * @param  aChannel The channel to be checked
    161   */
    162  static void TestSitePermissionAndPotentiallyAddExemption(
    163      nsIChannel* aChannel);
    164 
    165  /**
    166   * Checks whether CORS or mixed content requests are safe because they'll get
    167   * upgraded to HTTPS
    168   * @param  aLoadInfo nsILoadInfo of request
    169   * @return           true if it's safe to accept
    170   */
    171  static bool IsSafeToAcceptCORSOrMixedContent(nsILoadInfo* aLoadInfo);
    172 
    173  /**
    174   * Checks if two URIs are same origin modulo the difference that
    175   * aToURI scheme is downgraded to http from https aFromURI.
    176   * @param aFromURI nsIURI using scheme of https
    177   * @param aToURI nsIURI using scheme of http
    178   * @return true, if URIs are equal except scheme and ref
    179   */
    180  static bool IsHttpDowngrade(nsIURI* aFromURI, nsIURI* aToURI);
    181 
    182  /**
    183   * Will add a special temporary HTTPS-Only exception that only applies to
    184   * HTTPS-First, and is not exposed in the UI.
    185   * @param aURI      The URL for whose HTTP principal the exception should be
    186   *                  added
    187   * @param aLoadInfo The loadinfo of the request triggering this exception to
    188   *                  be added (needs to match aURI)
    189   */
    190  static nsresult AddHTTPSFirstException(nsCOMPtr<nsIURI> aURI,
    191                                         nsILoadInfo* const aLoadInfo);
    192 
    193  /**
    194   * Determines which HTTPS-Only status flags should get propagated to
    195   * sub-resources or sub-documents. As sub-resources and sub-documents are
    196   * exempt when the top-level document is exempt, we need to copy the "exempt"
    197   * flag. The HTTPS-First "upgraded" flag should not be copied to prevent a
    198   * unwanted downgrade (Bug 1885949).
    199   * @param aHttpsOnlyStatus The HTTPS-Only status of the top-level document.
    200   * @return The HTTPS-Only status that the sub-resource/document should
    201   * receive.
    202   */
    203  static uint32_t GetStatusForSubresourceLoad(uint32_t aHttpsOnlyStatus);
    204 
    205  /**
    206   * When a downgrade is happening because of HTTPS-First, this function will
    207   * update the load state for the new load accordingly. This includes
    208   * information about the downgrade for later telemetry use.
    209   * @param aDocumentLoadListener The calling document load listener.
    210   * @param aLoadState The load state to be updated
    211   */
    212  static void UpdateLoadStateAfterHTTPSFirstDowngrade(
    213      mozilla::net::DocumentLoadListener* aDocumentLoadListener,
    214      nsDocShellLoadState* aLoadState);
    215 
    216  /**
    217   * When a load is successful, this should be called by the document load
    218   * listener. In two cases, telemetry is then recorded:
    219   * a) If downgrade data has been passed, the passed load is a successful
    220   *    downgrade, which means telemetry based on the downgrade data will be
    221   *    submitted.
    222   * b) If the passed load info indicates that this load has been upgraded by
    223   *    HTTPS-First, this means the upgrade was successful, which will be
    224   *    recorded to telemetry.
    225   */
    226  static void SubmitHTTPSFirstTelemetry(
    227      nsCOMPtr<nsILoadInfo> const& aLoadInfo,
    228      RefPtr<HTTPSFirstDowngradeData> const& aHttpsFirstDowngradeData);
    229 
    230 private:
    231  /**
    232   * Checks if it can be ruled out that the error has something
    233   * to do with an HTTPS upgrade.
    234   * @param  aError error code
    235   * @return        true if error is unrelated to the upgrade
    236   */
    237  static bool HttpsUpgradeUnrelatedErrorCode(nsresult aError);
    238  /**
    239   * Logs localized message to either content console or browser console
    240   * @param aMessage         Message to log
    241   * @param aFlags           Logging Flag (see nsIScriptError)
    242   * @param aLoadInfo        The loadinfo of the request.
    243   * @param [aURI]           Optional: URI to log
    244   * @param [aUseHttpsFirst] Optional: Log using HTTPS-First (vs. HTTPS-Only)
    245   */
    246  static void LogMessage(const nsAString& aMessage, uint32_t aFlags,
    247                         nsILoadInfo* aLoadInfo, nsIURI* aURI = nullptr,
    248                         bool aUseHttpsFirst = false);
    249 
    250  /**
    251   * Checks whether the URI ends with .onion
    252   * @param  aURI URI object
    253   * @return      true if the URI is an Onion URI
    254   */
    255  static bool OnionException(nsIURI* aURI);
    256 
    257  /**
    258   * Checks whether the URI is a loopback- or local-IP
    259   * @param  aURI URI object
    260   * @return      true if the URI is either loopback or local
    261   */
    262  static bool LoopbackOrLocalException(nsIURI* aURI);
    263 
    264  /**
    265   * Checks whether the host of the URI ends with a suffix that is not in the
    266   * public suffix list.
    267   * @param aURI URI object
    268   * @return     true if the host of the URI ends with a unknown suffix
    269   */
    270  static bool UnknownPublicSuffixException(nsIURI* aURI);
    271 };
    272 
    273 /**
    274 * Helper class to perform an http request with a N milliseconds
    275 * delay. If that http request is 'receiving data' before the
    276 * upgraded https request, then it's a strong indicator that
    277 * the https request will result in a timeout and hence we
    278 * cancel the https request which will result in displaying
    279 * the exception page.
    280 */
    281 class TestHTTPAnswerRunnable final : public mozilla::Runnable,
    282                                     public nsIStreamListener,
    283                                     public nsIInterfaceRequestor,
    284                                     public nsITimerCallback {
    285 public:
    286  // TestHTTPAnswerRunnable needs to implement all these channel related
    287  // interfaces because otherwise our Necko code is not happy, but we
    288  // really only care about ::OnStartRequest.
    289  NS_DECL_ISUPPORTS_INHERITED
    290  NS_DECL_NSIRUNNABLE
    291  NS_DECL_NSIREQUESTOBSERVER
    292  NS_DECL_NSISTREAMLISTENER
    293  NS_DECL_NSIINTERFACEREQUESTOR
    294  NS_DECL_NSITIMERCALLBACK
    295 
    296  explicit TestHTTPAnswerRunnable(
    297      nsIURI* aURI, mozilla::net::DocumentLoadListener* aDocumentLoadListener);
    298 
    299 protected:
    300  ~TestHTTPAnswerRunnable() = default;
    301 
    302 private:
    303  /**
    304   * Checks whether the HTTP background request results in a redirect
    305   * to the same upgraded top-level HTTPS URL
    306   * @param  aChannel a nsIHttpChannel object
    307   * @return  true if the backgroundchannel is redirected
    308   */
    309  static bool IsBackgroundRequestRedirected(nsIHttpChannel* aChannel);
    310 
    311  RefPtr<nsIURI> mURI;
    312  // We're keeping a reference to DocumentLoadListener instead of a specific
    313  // channel, because the current top-level channel can change (for example
    314  // through redirects)
    315  RefPtr<mozilla::net::DocumentLoadListener> mDocumentLoadListener;
    316  RefPtr<nsITimer> mTimer;
    317  bool mHasHTTPSRR{false};
    318 };
    319 
    320 /**
    321 * Data about a HTTPS-First downgrade used for Telemetry. We need to store this
    322 * instead of directly submitting it when deciding to downgrade, because it is
    323 * only interesting for us if the downgraded load is actually succesful.
    324 */
    325 struct HTTPSFirstDowngradeData
    326    : public mozilla::RefCounted<HTTPSFirstDowngradeData> {
    327  MOZ_DECLARE_REFCOUNTED_TYPENAME(HTTPSFirstDowngradeData)
    328  mozilla::TimeDuration downgradeTime;
    329  bool isOnTimer = false;
    330  bool isSchemeless = false;
    331 };
    332 
    333 #endif /* nsHTTPSOnlyUtils_h___ */