toolkit/mozapps/shared/CertUtils.jsm
author Chris Jones <jones.chris.g@gmail.com>
Fri, 18 Feb 2011 18:32:12 -0600
changeset 62860 e4b9604fdba59053c877aaf9e5c93d12dd89dd27
parent 51863 c9ee9a98f2d4d4db468d593972e985b6e92634a6
child 82809 e5d8d2fb987db970e2176c7f857cdc14f349f062
permissions -rw-r--r--
Bug 635191: If the object frame has gone away, there's no way to determine IsUpToDate(), so just dispatch the paint-finished event. r=roc a=b

#if 0
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the Update Service.
 *
 * The Initial Developer of the Original Code is Ben Goodger.
 * Portions created by the Initial Developer are Copyright (C) 2004
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *  Darin Fisher <darin@meer.net>
 *  Daniel Veditz <dveditz@mozilla.com>
 *  Jesper Kristensen <mail@jesperkristensen.dk>
 *  Robert Strong <robert.bugzilla@gmail.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
#endif
EXPORTED_SYMBOLS = [ "BadCertHandler", "checkCert" ];

const Ce = Components.Exception;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;

/**
 * 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
 *         cert 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;
  }

  var cert =
      aChannel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
      SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;

  if (aCerts) {
    for (var i = 0; i < aCerts.length; ++i) {
      var error = false;
      var certAttrs = aCerts[i];
      for (var name in certAttrs) {
        if (!(name in cert)) {
          error = true;
          Cu.reportError("Expected attribute '" + name + "' not present in " +
                         "certificate.");
          break;
        }
        if (cert[name] != certAttrs[name]) {
          error = true;
          Cu.reportError("Expected certificate attribute '" + name + "' " +
                         "value incorrect, expected: '" + certAttrs[name] +
                         "', got: '" + cert[name] + "'.");
          break;
        }
      }

      if (!error)
        break;
    }

    if (error) {
      const certCheckErr = "Certificate checks failed. See previous errors " +
                           "for details.";
      Cu.reportError(certCheckErr);
      throw new Ce(certCheckErr, Cr.NS_ERROR_ILLEGAL_VALUE);
    }
  }

  if (aAllowNonBuiltInCerts ===  true)
    return;

  var issuerCert = cert;
  while (issuerCert.issuer && !issuerCert.issuer.equals(issuerCert))
    issuerCert = issuerCert.issuer;

  const certNotBuiltInErr = "Certificate issuer is not built-in.";
  if (!issuerCert)
    throw new Ce(certNotBuiltInErr, Cr.NS_ERROR_ABORT);

  issuerCert = issuerCert.QueryInterface(Ci.nsIX509Cert3);
  var tokenNames = issuerCert.getAllTokenNames({});

  if (!tokenNames || !tokenNames.some(isBuiltinToken))
    throw new Ce(certNotBuiltInErr, Cr.NS_ERROR_ABORT);
}

function isBuiltinToken(tokenName) {
  return tokenName == "Builtin Object Token";
}

/**
 * This class implements nsIBadCertListener.  Its job is to prevent "bad cert"
 * security dialogs from being shown to the user.  It is better to simply fail
 * if the certificate is bad. See bug 304286.
 *
 * @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: function(oldChannel, newChannel, flags, callback) {
    if (this.allowNonBuiltInCerts) {
      callback.onRedirectVerifyCallback(Components.results.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(Components.results.NS_OK);
  },

  // Suppress any certificate errors
  notifyCertProblem: function(socketInfo, status, targetSite) {
    return true;
  },

  // Suppress any ssl errors
  notifySSLError: function(socketInfo, error, targetSite) {
    return true;
  },

  // nsIInterfaceRequestor
  getInterface: function(iid) {
    return this.QueryInterface(iid);
  },

  // nsISupports
  QueryInterface: function(iid) {
    if (!iid.equals(Ci.nsIChannelEventSink) &&
        !iid.equals(Ci.nsIBadCertListener2) &&
        !iid.equals(Ci.nsISSLErrorListener) &&
        !iid.equals(Ci.nsIInterfaceRequestor) &&
        !iid.equals(Ci.nsISupports))
      throw Cr.NS_ERROR_NO_INTERFACE;
    return this;
  }
};