Bug 401575 - "Support cert overriding from SSL error pages" [p=johnath r=kaie r=gavin r+sr=bzbarsky ui-r=beltzner a=blocking1.9+ for M9]
authorreed@reedloden.com
Sat, 03 Nov 2007 11:49:47 -0700
changeset 7312 3748367cc42e7101d75ed698c00858dff6366d6f
parent 7311 d3a3acbea32208e7cfe71770336b920af05a0cbe
child 7313 9b44161ce70c304f0da143bd54dc8dc68809755d
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskaie, gavin, beltzner, blocking1
bugs401575
milestone1.9a9pre
Bug 401575 - "Support cert overriding from SSL error pages" [p=johnath r=kaie r=gavin r+sr=bzbarsky ui-r=beltzner a=blocking1.9+ for M9]
browser/base/content/browser.js
browser/locales/en-US/chrome/overrides/netError.dtd
docshell/resources/content/netError.xhtml
dom/locales/en-US/chrome/netError.dtd
security/manager/locales/en-US/chrome/pipnss/pipnss.properties
security/manager/pki/resources/content/exceptionDialog.js
security/manager/ssl/src/nsNSSIOLayer.cpp
toolkit/themes/pinstripe/global/netError.css
toolkit/themes/winstripe/global/netError.css
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1199,16 +1199,22 @@ function delayedStartup()
       ss.init(window);
     } catch(ex) {
       dump("nsSessionStore could not be initialized: " + ex + "\n");
     }
   }
 
   // bookmark-all-tabs command
   gBookmarkAllTabsHandler = new BookmarkAllTabsHandler();
+
+  // Attach a listener to watch for "command" events bubbling up from error
+  // pages.  This lets us fix bugs like 401575 which require error page UI to
+  // do privileged things, without letting error pages have any privilege
+  // themselves.
+  gBrowser.addEventListener("command", BrowserOnCommand, false);
 }
 
 function BrowserShutdown()
 {
   try {
     FullZoom.destroy();
     ContentPrefSink.destroy();
   }
@@ -2300,16 +2306,60 @@ function BrowserImport()
                       "migration", "centerscreen,chrome,resizable=no");
   }
 #else
   window.openDialog("chrome://browser/content/migration/migration.xul",
                     "migration", "modal,centerscreen,chrome,resizable=no");
 #endif
 }
 
+/**
+ * Handle command events bubbling up from error page content
+ */
+function BrowserOnCommand(event) {
+    
+    // Don't trust synthetic events
+    if (!event.isTrusted)
+      return;
+    
+    // If the event came from an ssl error page, it is probably either the "Add
+    // Exception" or "Get Me Out Of Here" button
+    if (/^about:neterror\?e=nssBadCert/.test(event.originalTarget.ownerDocument.documentURI)) {
+      var ot = event.originalTarget;
+      var errorDoc = ot.ownerDocument;
+      
+      if (ot == errorDoc.getElementById('exceptionDialogButton')) {
+        var params = { location : content.location.href,
+                       exceptionAdded : false };
+        window.openDialog('chrome://pippki/content/exceptionDialog.xul',
+                          '','chrome,centerscreen,modal', params);
+        
+        // If the user added the exception cert, attempt to reload the page
+        if (params.exceptionAdded)
+          content.location.reload();
+      }
+      else if (ot == errorDoc.getElementById('getMeOutOfHereButton')) {
+        // Redirect them to a known-functioning page, default start page
+        var prefs = Cc["@mozilla.org/preferences-service;1"]
+                    .getService(Ci.nsIPrefService).getDefaultBranch(null);
+        var url = "about:blank";
+        try {
+          url = prefs.getComplexValue("browser.startup.homepage",
+                                      Ci.nsIPrefLocalizedString).data;
+          // If url is a pipe-delimited set of pages, just take the first one.
+          if (url.indexOf("|") != -1)
+            url = url.split("|")[0];
+        } catch(e) {
+          Components.utils.reportError("Couldn't get homepage pref: " + e);
+        }
+        content.location = url;
+      }
+    }
+  }
+
 function BrowserFullScreen()
 {
   window.fullScreen = !window.fullScreen;
 }
 
 function onFullScreen()
 {
   FullScreen.toggle();
--- a/browser/locales/en-US/chrome/overrides/netError.dtd
+++ b/browser/locales/en-US/chrome/overrides/netError.dtd
@@ -119,24 +119,22 @@
 <!ENTITY nssFailure2.longDesc "
 <ul>
   <li>The page you are trying to view can not be shown because the authenticity of the received data could not be verified.</li>
   <li>Please contact the web site owners to inform them of this problem. Alternatively, use the command found in the help menu to report this broken site.</li>
 </ul>
 ">
 
 <!ENTITY nssBadCert.title "Secure Connection Failed">
-<!ENTITY nssBadCert.longDesc "
+<!ENTITY nssBadCert.longDesc2 "
 <ul>
   <li>This could be a problem with the server's configuration, or it could be
 someone trying to impersonate the server.</li>
   <li>If you have connected to this server successfully in the past, the error may
 be temporary, and you can try again later.</li>
-  <li>You can see and change your current list of servers with known security problems
-  in your advanced encryption settings.</li>
 </ul>
 ">
 
 <!ENTITY sharedLongDesc "
 <ul>
   <li>The site could be temporarily unavailable or too busy. Try again in a few
     moments.</li>
   <li>If you are unable to load any pages, check your computer's network
@@ -152,8 +150,24 @@ be temporary, and you can try again late
 <p>Web site owners who believe their site has been reported as an attack site in error may <a href='http://www.stopbadware.org/home/reviewinfo' >request a review</a>.</p>
 ">
 
 <!ENTITY phishingBlocked.title "Suspected Web Forgery!">
 <!ENTITY phishingBlocked.longDesc "
 <p>Entering any personal information on this page may result in identity theft or other fraud.</p>
 <p>These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.</p>
 ">
+
+<!ENTITY securityOverride.linkText "Or you can add an exception...">
+<!ENTITY securityOverride.getMeOutOfHereButton "Get Me Out of Here">
+<!ENTITY securityOverride.exceptionButtonLabel "Add Exception">
+
+<!-- LOCALIZATION NOTE (securityOverride.warningText) - Do not translate the
+contents of the <xul:button> tags.  The only language content is the label= field,
+which uses strings already defined above. The button is included here (instead of
+netError.xhtml) because it exposes functionality specific to firefox. -->
+
+<!ENTITY securityOverride.warningText "
+<p>You should not add an exception if you are using an internet connection that you do not trust completely, or if you are not used to seeing a warning for this server.</p>
+
+<xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='getMeOutOfHereButton' label='&securityOverride.getMeOutOfHereButton;'/>
+<xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='exceptionDialogButton' label='&securityOverride.exceptionButtonLabel;'/>
+">
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.xhtml
@@ -121,16 +121,28 @@
         }
 
         buttonEl.disabled = true;
       }
 
       function initPage()
       {
         var err = getErrorCode();
+        
+        if (err == "nssBadCert") {
+          // Remove the "Try again" button for security exceptions, since it's
+          // almost certainly useless.
+          document.getElementById("errorTryAgain").style.display = "none";
+        }
+        else {
+          // Remove the override block for non-certificate errors.  CSS-hiding
+          // isn't good enough here, because of bug 39098
+          var secOverride = document.getElementById("securityOverrideDiv");
+          secOverride.parentNode.removeChild(secOverride);
+        }
 
         // if it's an unknown error or there's no title or description
         // defined, get the generic message
         var errTitle = document.getElementById("et_" + err);
         var errDesc  = document.getElementById("ed_" + err);
         if (!errTitle || !errDesc)
         {
           errTitle = document.getElementById("et_generic");
@@ -172,16 +184,22 @@
           // need to remove/add the link element. 
           var favicon = document.getElementById("favicon");
           var faviconParent = favicon.parentNode;
           faviconParent.removeChild(favicon);
           favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
           faviconParent.appendChild(favicon);
         }
       }
+      
+      function showSecuritySection() {
+        // Swap link out, content in
+        document.getElementById('securityOverrideContent').style.display = '';
+        document.getElementById('securityOverrideLink').style.display = 'none';
+      }
     ]]></script>
   </head>
 
   <body dir="&locale.dir;">
 
     <!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
     <div id="errorContainer">
       <div id="errorTitlesContainer">
@@ -218,17 +236,17 @@
         <div id="ed_netReset">&netReset.longDesc;</div>
         <div id="ed_netOffline">&netOffline.longDesc;</div>
         <div id="ed_netInterrupt">&netInterrupt.longDesc;</div>
         <div id="ed_deniedPortAccess">&deniedPortAccess.longDesc;</div>
         <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
         <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
         <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
         <div id="ed_nssFailure2">&nssFailure2.longDesc;</div>
-        <div id="ed_nssBadCert">&nssBadCert.longDesc;</div>
+        <div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
         <div id="ed_malwareBlocked">&malwareBlocked.longDesc;</div>
       </div>
     </div>
 
     <!-- PAGE CONTAINER (for styling purposes only) -->
     <div id="errorPageContainer">
     
       <!-- Error Title -->
@@ -242,16 +260,22 @@
         <!-- Short Description -->
         <div id="errorShortDesc">
           <p id="errorShortDescText" />
         </div>
 
         <!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
         <div id="errorLongDesc" />
 
+        <!-- Override section - For ssl errors only.  Removed on init for other
+             error types.  -->
+        <div id="securityOverrideDiv">
+          <a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
+          <div id="securityOverrideContent" style="display: none;">&securityOverride.warningText;</div>
+        </div>
       </div>
 
       <!-- Retry Button -->
       <xul:button xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                   id="errorTryAgain" label="&retry.label;" oncommand="retryThis(this);" />
 
     </div>
 
--- a/dom/locales/en-US/chrome/netError.dtd
+++ b/dom/locales/en-US/chrome/netError.dtd
@@ -50,26 +50,31 @@
 
 <!ENTITY unknownSocketType.title "Incorrect Response">
 <!ENTITY unknownSocketType.longDesc "<p>The site responded to the network request in an unexpected way and the browser cannot continue.</p>">
 
 <!ENTITY nssFailure2.title "Secure Connection Failed">
 <!ENTITY nssFailure2.longDesc "<p>The page you are trying to view can not be shown because the authenticity of the received data could not be verified.</p><ul><li>Please contact the web site owners to inform them of this problem.</li></ul>">
 
 <!ENTITY nssBadCert.title "Secure Connection Failed">
-<!ENTITY nssBadCert.longDesc "<ul>
+<!ENTITY nssBadCert.longDesc2 "<ul>
 <li>This could be a problem with the server's configuration, or it could be someone trying to impersonate the server.</li>
 <li>If you have connected to this server successfully in the past, the error may be temporary, and you can try again later.</li>
-<li>You can see and change your current list of servers with known security problems in your advanced encryption settings.</li>
 </ul>
 ">
 
 <!ENTITY malwareBlocked.title "Suspected Attack Site!">
 <!ENTITY malwareBlocked.longDesc "
 <p>Attack sites try to install programs that steal private information, use your computer to attack others, or damage your system.</p>
 <p>Web site owners who believe their site has been reported as an attack site in error may <a href='http://www.stopbadware.org/home/reviewinfo' >request a review</a>.</p>
 ">
 
 <!ENTITY phishingBlocked.title "Suspected Web Forgery!">
 <!ENTITY phishingBlocked.longDesc "
 <p>Entering any personal information on this page may result in identity theft or other fraud.</p>
 <p>These types of web forgeries are used in scams known as phishing attacks, in which fraudulent web pages and emails are used to imitate sources you may trust.</p>
 ">
+
+<!ENTITY securityOverride.linkText "Or you can add an exception...">
+<!ENTITY securityOverride.warningText "
+<p>You should not add an exception if you are using an internet connection that you do not trust completely, or if you are not used to seeing a warning for this server.</p>
+<p>If you still wish to add an exception for this site, you can do so in your advanced encryption settings.</p>
+">
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -342,17 +342,17 @@ certErrorIntro=%S uses an invalid securi
 certErrorTrust_SelfSigned=The certificate is not trusted because it is self signed.
 certErrorTrust_UnknownIssuer=The certificate is not trusted because the issuer certificate is unknown.
 certErrorTrust_CaInvalid=The certificate is not trusted because it was issued by an invalid CA certificate.
 certErrorTrust_Issuer=The certificate is not trusted because the issuer certificate is not trusted.
 certErrorTrust_ExpiredIssuer=The certificate is not trusted because the issuer certificate has expired.
 certErrorTrust_Untrusted=The certificate does not come from a trusted source.
 
 certErrorMismatch=The certificate is not valid for the name %S.
-certErrorMismatchSingle=The certificate is only valid for name %S.
+certErrorMismatchSingle2=The certificate is only valid for %S.
 certErrorMismatchMultiple=The certificate is only valid for the following names:
 
 certErrorExpired=The certificate expired on %S.
 certErrorNotYetValid=The certificate will not be valid until %S.
 
 certErrorCodePrefix=(Error code: %S)
 
 CertInfoIssuedFor=Issued to:
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -76,16 +76,27 @@ function initExceptionDialog() {
   gBundleBrand = srGetStrBundle("chrome://branding/locale/brand.properties");
   gPKIBundle = srGetStrBundle("chrome://pippki/locale/pippki.properties");
 
   var brandName = gBundleBrand.GetStringFromName("brandShortName");
   
   setText("warningText", gPKIBundle.formatStringFromName("addExceptionBrandedWarning",
                                                          [brandName], 1));
   gDialog.getButton("extra1").disabled = true;
+  
+  if (window.arguments[0]
+      && window.arguments[0].location) {
+    // We were pre-seeded with a location.  Populate the location bar, and check
+    // the cert
+    document.getElementById("locationTextBox").value = window.arguments[0].location;
+    checkCert();
+  }
+  
+  // Set out parameter to false by default
+  window.arguments[0].exceptionAdded = false;
 }
 
 // returns true if found and global status could be set
 function findRecentBadCert(uri) {
   try {
     var recentCertsSvc = Components.classes["@mozilla.org/security/recentbadcerts;1"]
                          .getService(Components.interfaces.nsIRecentBadCertsService);
     if (!recentCertsSvc)
@@ -316,10 +327,12 @@ function addException() {
     flags |= overrideService.ERROR_MISMATCH;
   if(gSSLStatus.isNotValidAtThisTime)
     flags |= overrideService.ERROR_TIME;
   
   overrideService.rememberValidityOverride(
     getURI().hostPort,
     gCert,
     flags);
+  
+  window.arguments[0].exceptionAdded = true;
   gDialog.acceptDialog();
 }
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -846,17 +846,17 @@ getInvalidCertErrorMessage(PRUint32 mult
         returnedMessage.Append(allNames);
         returnedMessage.Append(NS_LITERAL_STRING("  \n"));
       }
     }
     else { // !multipleNames
       params[0] = allNames.get();
 
       nsString formattedString;
-      rv = component->PIPBundleFormatStringFromName("certErrorMismatchSingle", 
+      rv = component->PIPBundleFormatStringFromName("certErrorMismatchSingle2", 
                                                     params, 1, 
                                                     formattedString);
       if (NS_SUCCEEDED(rv)) {
         returnedMessage.Append(formattedString);
         returnedMessage.Append(NS_LITERAL_STRING("\n"));
       }
     }
   }
--- a/toolkit/themes/pinstripe/global/netError.css
+++ b/toolkit/themes/pinstripe/global/netError.css
@@ -100,16 +100,26 @@ body[dir="rtl"] #brand {
 #brand > p {
   margin: 0;
 }
 
 #errorContainer {
   display: none;
 }
 
+#securityOverrideDiv {
+  padding-top: 10px;
+}
+
+#securityOverrideContent {
+  background-color: #FFF090; /* Pale yellow */
+  padding: 10px;
+  -moz-border-radius: 10px;
+}
+
 /* Custom styling for 'blacklist' error class */
 :root.blacklist #errorTitle, :root.blacklist #errorLongContent,
 :root.blacklist #errorShortDesc, :root.blacklist #errorLongDesc,
 :root.blacklist a {
   background-color: #722; /* Dark red */
   color: white;
 }
 
@@ -120,9 +130,8 @@ body[dir="rtl"] #brand {
 
 :root.blacklist {
   background: #333;
 }
 
 :root.blacklist #errorTryAgain {
   display: none;
 }
-
--- a/toolkit/themes/winstripe/global/netError.css
+++ b/toolkit/themes/winstripe/global/netError.css
@@ -100,16 +100,26 @@ body[dir="rtl"] #brand {
 #brand > p {
   margin: 0;
 }
 
 #errorContainer {
   display: none;
 }
 
+#securityOverrideDiv {
+  padding-top: 10px;
+}
+
+#securityOverrideContent {
+  background-color: #FFF090; /* Pale yellow */
+  padding: 10px;
+  -moz-border-radius: 10px;
+}
+
 /* Custom styling for 'blacklist' error class */
 :root.blacklist #errorTitle, :root.blacklist #errorLongContent,
 :root.blacklist #errorShortDesc, :root.blacklist #errorLongDesc,
 :root.blacklist a {
   background-color: #722; /* Dark red */
   color: white;
 }