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.

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

mercurial