toolkit/modules/CertUtils.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 #if 0
     2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     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 #endif
     7 this.EXPORTED_SYMBOLS = [ "BadCertHandler", "checkCert", "readCertPrefs", "validateCert" ];
     9 const Ce = Components.Exception;
    10 const Ci = Components.interfaces;
    11 const Cr = Components.results;
    12 const Cu = Components.utils;
    14 Components.utils.import("resource://gre/modules/Services.jsm");
    16 /**
    17  * Reads a set of expected certificate attributes from preferences. The returned
    18  * array can be passed to validateCert or checkCert to validate that a
    19  * certificate matches the expected attributes. The preferences should look like
    20  * this:
    21  *   prefix.1.attribute1
    22  *   prefix.1.attribute2
    23  *   prefix.2.attribute1
    24  *   etc.
    25  * Each numeric branch contains a set of required attributes for a single
    26  * certificate. Having multiple numeric branches means that multiple
    27  * certificates would be accepted by validateCert.
    28  *
    29  * @param  aPrefBranch
    30  *         The prefix for all preferences, should end with a ".".
    31  * @return An array of JS objects with names / values corresponding to the
    32  *         expected certificate's attribute names / values.
    33  */
    34 this.readCertPrefs =
    35   function readCertPrefs(aPrefBranch) {
    36   if (Services.prefs.getBranch(aPrefBranch).getChildList("").length == 0)
    37     return null;
    39   let certs = [];
    40   let counter = 1;
    41   while (true) {
    42     let prefBranchCert = Services.prefs.getBranch(aPrefBranch + counter + ".");
    43     let prefCertAttrs = prefBranchCert.getChildList("");
    44     if (prefCertAttrs.length == 0)
    45       break;
    47     let certAttrs = {};
    48     for each (let prefCertAttr in prefCertAttrs)
    49       certAttrs[prefCertAttr] = prefBranchCert.getCharPref(prefCertAttr);
    51     certs.push(certAttrs);
    52     counter++;
    53   }
    55   return certs;
    56 }
    58 /**
    59  * Verifies that an nsIX509Cert matches the expected certificate attribute
    60  * values.
    61  *
    62  * @param  aCertificate
    63  *         The nsIX509Cert to compare to the expected attributes.
    64  * @param  aCerts
    65  *         An array of JS objects with names / values corresponding to the
    66  *         expected certificate's attribute names / values. If this is null or
    67  *         an empty array then no checks are performed.
    68  * @throws NS_ERROR_ILLEGAL_VALUE if a certificate attribute name from the
    69  *         aCerts param does not exist or the value for a certificate attribute
    70  *         from the aCerts param is different than the expected value or
    71  *         aCertificate wasn't specified and aCerts is not null or an empty
    72  *         array.
    73  */
    74 this.validateCert =
    75   function validateCert(aCertificate, aCerts) {
    76   // If there are no certificate requirements then just exit
    77   if (!aCerts || aCerts.length == 0)
    78     return;
    80   if (!aCertificate) {
    81     const missingCertErr = "A required certificate was not present.";
    82     Cu.reportError(missingCertErr);
    83     throw new Ce(missingCertErr, Cr.NS_ERROR_ILLEGAL_VALUE);
    84   }
    86   var errors = [];
    87   for (var i = 0; i < aCerts.length; ++i) {
    88     var error = false;
    89     var certAttrs = aCerts[i];
    90     for (var name in certAttrs) {
    91       if (!(name in aCertificate)) {
    92         error = true;
    93         errors.push("Expected attribute '" + name + "' not present in " +
    94                     "certificate.");
    95         break;
    96       }
    97       if (aCertificate[name] != certAttrs[name]) {
    98         error = true;
    99         errors.push("Expected certificate attribute '" + name + "' " +
   100                     "value incorrect, expected: '" + certAttrs[name] +
   101                     "', got: '" + aCertificate[name] + "'.");
   102         break;
   103       }
   104     }
   106     if (!error)
   107       break;
   108   }
   110   if (error) {
   111     errors.forEach(Cu.reportError.bind(Cu));
   112     const certCheckErr = "Certificate checks failed. See previous errors " +
   113                          "for details.";
   114     Cu.reportError(certCheckErr);
   115     throw new Ce(certCheckErr, Cr.NS_ERROR_ILLEGAL_VALUE);
   116   }
   117 }
   119 /**
   120  * Checks if the connection must be HTTPS and if so, only allows built-in
   121  * certificates and validates application specified certificate attribute
   122  * values.
   123  * See bug 340198 and bug 544442.
   124  *
   125  * @param  aChannel
   126  *         The nsIChannel that will have its certificate checked.
   127  * @param  aAllowNonBuiltInCerts (optional)
   128  *         When true certificates that aren't builtin are allowed. When false
   129  *         or not specified the certificate must be a builtin certificate.
   130  * @param  aCerts (optional)
   131  *         An array of JS objects with names / values corresponding to the
   132  *         channel's expected certificate's attribute names / values. If it
   133  *         isn't null or not specified the the scheme for the channel's
   134  *         originalURI must be https.
   135  * @throws NS_ERROR_UNEXPECTED if a certificate is expected and the URI scheme
   136  *         is not https.
   137  *         NS_ERROR_ILLEGAL_VALUE if a certificate attribute name from the
   138  *         aCerts param does not exist or the value for a certificate attribute
   139  *         from the aCerts  param is different than the expected value.
   140  *         NS_ERROR_ABORT if the certificate issuer is not built-in.
   141  */
   142 this.checkCert =
   143   function checkCert(aChannel, aAllowNonBuiltInCerts, aCerts) {
   144   if (!aChannel.originalURI.schemeIs("https")) {
   145     // Require https if there are certificate values to verify
   146     if (aCerts) {
   147       throw new Ce("SSL is required and URI scheme is not https.",
   148                    Cr.NS_ERROR_UNEXPECTED);
   149     }
   150     return;
   151   }
   153   var cert =
   154       aChannel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
   155       SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
   157   validateCert(cert, aCerts);
   159   if (aAllowNonBuiltInCerts ===  true)
   160     return;
   162   var issuerCert = cert;
   163   while (issuerCert.issuer && !issuerCert.issuer.equals(issuerCert))
   164     issuerCert = issuerCert.issuer;
   166   const certNotBuiltInErr = "Certificate issuer is not built-in.";
   167   if (!issuerCert)
   168     throw new Ce(certNotBuiltInErr, Cr.NS_ERROR_ABORT);
   170   issuerCert = issuerCert.QueryInterface(Ci.nsIX509Cert3);
   171   var tokenNames = issuerCert.getAllTokenNames({});
   173   if (!tokenNames || !tokenNames.some(isBuiltinToken))
   174     throw new Ce(certNotBuiltInErr, Cr.NS_ERROR_ABORT);
   175 }
   177 function isBuiltinToken(tokenName) {
   178   return tokenName == "Builtin Object Token";
   179 }
   181 /**
   182  * This class implements nsIBadCertListener.  Its job is to prevent "bad cert"
   183  * security dialogs from being shown to the user.  It is better to simply fail
   184  * if the certificate is bad. See bug 304286.
   185  *
   186  * @param  aAllowNonBuiltInCerts (optional)
   187  *         When true certificates that aren't builtin are allowed. When false
   188  *         or not specified the certificate must be a builtin certificate.
   189  */
   190 this.BadCertHandler =
   191   function BadCertHandler(aAllowNonBuiltInCerts) {
   192   this.allowNonBuiltInCerts = aAllowNonBuiltInCerts;
   193 }
   194 BadCertHandler.prototype = {
   196   // nsIChannelEventSink
   197   asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
   198     if (this.allowNonBuiltInCerts) {
   199       callback.onRedirectVerifyCallback(Components.results.NS_OK);
   200       return;
   201     }
   203     // make sure the certificate of the old channel checks out before we follow
   204     // a redirect from it.  See bug 340198.
   205     // Don't call checkCert for internal redirects. See bug 569648.
   206     if (!(flags & Ci.nsIChannelEventSink.REDIRECT_INTERNAL))
   207       checkCert(oldChannel);
   209     callback.onRedirectVerifyCallback(Components.results.NS_OK);
   210   },
   212   // nsIInterfaceRequestor
   213   getInterface: function(iid) {
   214     return this.QueryInterface(iid);
   215   },
   217   // nsISupports
   218   QueryInterface: function(iid) {
   219     if (!iid.equals(Ci.nsIChannelEventSink) &&
   220         !iid.equals(Ci.nsIInterfaceRequestor) &&
   221         !iid.equals(Ci.nsISupports))
   222       throw Cr.NS_ERROR_NO_INTERFACE;
   223     return this;
   224   }
   225 };

mercurial