error = true; errors.push( "Expected certificate attribute '" + name + "' " + "value incorrect, expected: '" + certAttrs[name] + "', got: '" + aCertificate[name] + "'." ); break; } } if (!error) { break; } } if (error) { errors.forEach(Cu.reportError.bind(Cu)); const certCheckErr = "Certificate checks failed. See previous errors for details."; console.error(certCheckErr); throw new Ce(certCheckErr, Cr.NS_ERROR_ILLEGAL_VALUE); } } /** * Checks if the connection must be HTTPS and if so, only allows built-in * certificates and validates application specified certificate attribute * values. * See bug 340198 and bug 544442. * * @param aChannel * The nsIChannel that will have its certificate checked. * @param aAllowNonBuiltInCerts (optional) * When true certificates that aren't builtin are allowed. When false * or not specified the certificate must be a builtin certificate. * @param aCerts (optional) * An array of JS objects with names / values corresponding to the * channel's expected certificate's attribute names / values. If it * isn't null or not specified the the scheme for the channel's * originalURI must be https. * @throws NS_ERROR_UNEXPECTED if a certificate is expected and the URI scheme * is not https. * NS_ERROR_ILLEGAL_VALUE if a certificate attribute name from the * aCerts param does not exist or the value for a certificate attribute * from the aCerts param is different than the expected value. * NS_ERROR_ABORT if the certificate issuer is not built-in. */ function checkCert(aChannel, aAllowNonBuiltInCerts, aCerts) { if (!aChannel.originalURI.schemeIs("https")) { // Require https if there are certificate values to verify if (aCerts) { throw new Ce( "SSL is required and URI scheme is not https.", Cr.NS_ERROR_UNEXPECTED ); } return; } let secInfo = aChannel.securityInfo; let cert = secInfo.serverCert; validateCert(cert, aCerts); if (aAllowNonBuiltInCerts === true) { return; } const certNotBuiltInErr = "Certificate issuer is not built-in."; if (!secInfo.isBuiltCertChainRootBuiltInRoot) { throw new Ce(certNotBuiltInErr, Cr.NS_ERROR_ABORT); } } /** * This class implements nsIChannelEventSink. Its job is to perform extra checks * on the certificates used for some connections when those connections * redirect. * * @param aAllowNonBuiltInCerts (optional) * When true certificates that aren't builtin are allowed. When false * or not specified the certificate must be a builtin certificate. */ function BadCertHandler(aAllowNonBuiltInCerts) { this.allowNonBuiltInCerts = aAllowNonBuiltInCerts; } BadCertHandler.prototype = { // nsIChannelEventSink asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) { if (this.allowNonBuiltInCerts) { callback.onRedirectVerifyCallback(Cr.NS_OK); return; } // make sure the certificate of the old channel checks out before we follow // a redirect from it. See bug 340198. // Don't call checkCert for internal redirects. See bug 569648. if (!(flags & Ci.nsIChannelEventSink.REDIRECT_INTERNAL)) { checkCert(oldChannel); } callback.onRedirectVerifyCallback(Cr.NS_OK); }, // nsIInterfaceRequestor getInterface(iid) { return this.QueryInterface(iid); }, // nsISupports QueryInterface: ChromeUtils.generateQI([ "nsIChannelEventSink", "nsIInterfaceRequestor", ]), }; export var CertUtils = { BadCertHandler, checkCert, readCertPrefs, validateCert, }; PK