Wed, 31 Dec 2014 06:09:35 +0100
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 };