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___ */