• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is the Netscape security libraries.
15  *
16  * The Initial Developer of the Original Code is
17  * Netscape Communications Corporation.
18  * Portions created by the Initial Developer are Copyright (C) 2000
19  * the Initial Developer. All Rights Reserved.
20  *
21  * Contributor(s):
22  *   Ian McGreer <mcgreer@netscape.com>
23  *   Javier Delgadillo <javi@netscape.com>
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
38 
39 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
40 
41 #include <cert.h>
42 #include <certdb.h>
43 #include <pk11pub.h>
44 #include <secerr.h>
45 
46 #include "base/logging.h"
47 #include "net/base/net_errors.h"
48 #include "net/cert/x509_certificate.h"
49 #include "net/cert/x509_util_nss.h"
50 
51 #if !defined(CERTDB_TERMINAL_RECORD)
52 /* NSS 3.13 renames CERTDB_VALID_PEER to CERTDB_TERMINAL_RECORD
53  * and marks CERTDB_VALID_PEER as deprecated.
54  * If we're using an older version, rename it ourselves.
55  */
56 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
57 #endif
58 
59 namespace mozilla_security_manager {
60 
61 // Based on nsNSSCertificateDB::handleCACertDownload, minus the UI bits.
ImportCACerts(PK11SlotInfo * slot,const net::CertificateList & certificates,net::X509Certificate * root,net::NSSCertDatabase::TrustBits trustBits,net::NSSCertDatabase::ImportCertFailureList * not_imported)62 bool ImportCACerts(PK11SlotInfo* slot,
63                    const net::CertificateList& certificates,
64                    net::X509Certificate* root,
65                    net::NSSCertDatabase::TrustBits trustBits,
66                    net::NSSCertDatabase::ImportCertFailureList* not_imported) {
67   if (!slot || certificates.empty() || !root)
68     return false;
69 
70   // Mozilla had some code here to check if a perm version of the cert exists
71   // already and use that, but CERT_NewTempCertificate actually does that
72   // itself, so we skip it here.
73 
74   if (!CERT_IsCACert(root->os_cert_handle(), NULL)) {
75     not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
76         root, net::ERR_IMPORT_CA_CERT_NOT_CA));
77   } else if (root->os_cert_handle()->isperm) {
78     // Mozilla just returns here, but we continue in case there are other certs
79     // in the list which aren't already imported.
80     // TODO(mattm): should we set/add trust if it differs from the present
81     // settings?
82     not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
83         root, net::ERR_IMPORT_CERT_ALREADY_EXISTS));
84   } else {
85     // Mozilla uses CERT_AddTempCertToPerm, however it is privately exported,
86     // and it doesn't take the slot as an argument either.  Instead, we use
87     // PK11_ImportCert and CERT_ChangeCertTrust.
88     SECStatus srv = PK11_ImportCert(
89         slot,
90         root->os_cert_handle(),
91         CK_INVALID_HANDLE,
92         net::x509_util::GetUniqueNicknameForSlot(
93             root->GetDefaultNickname(net::CA_CERT),
94             &root->os_cert_handle()->derSubject,
95             slot).c_str(),
96         PR_FALSE /* includeTrust (unused) */);
97     if (srv != SECSuccess) {
98       LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
99       return false;
100     }
101     if (!SetCertTrust(root, net::CA_CERT, trustBits))
102       return false;
103   }
104 
105   PRTime now = PR_Now();
106   // Import additional delivered certificates that can be verified.
107   // This is sort of merged in from Mozilla's ImportValidCACertsInList.  Mozilla
108   // uses CERT_FilterCertListByUsage to filter out non-ca certs, but we want to
109   // keep using X509Certificates, so that we can use them to build the
110   // |not_imported| result.  So, we keep using our net::CertificateList and
111   // filter it ourself.
112   for (size_t i = 0; i < certificates.size(); i++) {
113     const scoped_refptr<net::X509Certificate>& cert = certificates[i];
114     if (cert == root) {
115       // we already processed that one
116       continue;
117     }
118 
119     // Mozilla uses CERT_FilterCertListByUsage(certList, certUsageAnyCA,
120     // PR_TRUE).  Afaict, checking !CERT_IsCACert on each cert is equivalent.
121     if (!CERT_IsCACert(cert->os_cert_handle(), NULL)) {
122       not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
123           cert, net::ERR_IMPORT_CA_CERT_NOT_CA));
124       VLOG(1) << "skipping cert (non-ca)";
125       continue;
126     }
127 
128     if (cert->os_cert_handle()->isperm) {
129       not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
130           cert, net::ERR_IMPORT_CERT_ALREADY_EXISTS));
131       VLOG(1) << "skipping cert (perm)";
132       continue;
133     }
134 
135     if (CERT_VerifyCert(CERT_GetDefaultCertDB(), cert->os_cert_handle(),
136         PR_TRUE, certUsageVerifyCA, now, NULL, NULL) != SECSuccess) {
137       // TODO(mattm): use better error code (map PORT_GetError to an appropriate
138       // error value).  (maybe make MapSecurityError or MapCertErrorToCertStatus
139       // public.)
140       not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
141           cert, net::ERR_FAILED));
142       VLOG(1) << "skipping cert (verify) " << PORT_GetError();
143       continue;
144     }
145 
146     // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg.  We use
147     // PK11_ImportCert instead.
148     SECStatus srv = PK11_ImportCert(
149         slot,
150         cert->os_cert_handle(),
151         CK_INVALID_HANDLE,
152         net::x509_util::GetUniqueNicknameForSlot(
153             cert->GetDefaultNickname(net::CA_CERT),
154             &cert->os_cert_handle()->derSubject,
155             slot).c_str(),
156         PR_FALSE /* includeTrust (unused) */);
157     if (srv != SECSuccess) {
158       LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
159       // TODO(mattm): Should we bail or continue on error here?  Mozilla doesn't
160       // check error code at all.
161       not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
162           cert, net::ERR_IMPORT_CA_CERT_FAILED));
163     }
164   }
165 
166   // Any errors importing individual certs will be in listed in |not_imported|.
167   return true;
168 }
169 
170 // Based on nsNSSCertificateDB::ImportServerCertificate.
ImportServerCert(PK11SlotInfo * slot,const net::CertificateList & certificates,net::NSSCertDatabase::TrustBits trustBits,net::NSSCertDatabase::ImportCertFailureList * not_imported)171 bool ImportServerCert(
172     PK11SlotInfo* slot,
173     const net::CertificateList& certificates,
174     net::NSSCertDatabase::TrustBits trustBits,
175     net::NSSCertDatabase::ImportCertFailureList* not_imported) {
176   if (!slot || certificates.empty())
177     return false;
178 
179   for (size_t i = 0; i < certificates.size(); ++i) {
180     const scoped_refptr<net::X509Certificate>& cert = certificates[i];
181 
182     // Mozilla uses CERT_ImportCerts, which doesn't take a slot arg.  We use
183     // PK11_ImportCert instead.
184     SECStatus srv = PK11_ImportCert(
185         slot,
186         cert->os_cert_handle(),
187         CK_INVALID_HANDLE,
188         net::x509_util::GetUniqueNicknameForSlot(
189             cert->GetDefaultNickname(net::SERVER_CERT),
190             &cert->os_cert_handle()->derSubject,
191             slot).c_str(),
192         PR_FALSE /* includeTrust (unused) */);
193     if (srv != SECSuccess) {
194       LOG(ERROR) << "PK11_ImportCert failed with error " << PORT_GetError();
195       not_imported->push_back(net::NSSCertDatabase::ImportCertFailure(
196           cert, net::ERR_IMPORT_SERVER_CERT_FAILED));
197       continue;
198     }
199   }
200 
201   SetCertTrust(certificates[0].get(), net::SERVER_CERT, trustBits);
202   // TODO(mattm): Report SetCertTrust result?  Putting in not_imported
203   // wouldn't quite match up since it was imported...
204 
205   // Any errors importing individual certs will be in listed in |not_imported|.
206   return true;
207 }
208 
209 // Based on nsNSSCertificateDB::SetCertTrust.
210 bool
SetCertTrust(const net::X509Certificate * cert,net::CertType type,net::NSSCertDatabase::TrustBits trustBits)211 SetCertTrust(const net::X509Certificate* cert,
212              net::CertType type,
213              net::NSSCertDatabase::TrustBits trustBits)
214 {
215   const unsigned kSSLTrustBits = net::NSSCertDatabase::TRUSTED_SSL |
216       net::NSSCertDatabase::DISTRUSTED_SSL;
217   const unsigned kEmailTrustBits = net::NSSCertDatabase::TRUSTED_EMAIL |
218       net::NSSCertDatabase::DISTRUSTED_EMAIL;
219   const unsigned kObjSignTrustBits = net::NSSCertDatabase::TRUSTED_OBJ_SIGN |
220       net::NSSCertDatabase::DISTRUSTED_OBJ_SIGN;
221   if ((trustBits & kSSLTrustBits) == kSSLTrustBits ||
222       (trustBits & kEmailTrustBits) == kEmailTrustBits ||
223       (trustBits & kObjSignTrustBits) == kObjSignTrustBits) {
224     LOG(ERROR) << "SetCertTrust called with conflicting trust bits "
225                << trustBits;
226     NOTREACHED();
227     return false;
228   }
229 
230   SECStatus srv;
231   CERTCertificate *nsscert = cert->os_cert_handle();
232   if (type == net::CA_CERT) {
233     // Note that we start with CERTDB_VALID_CA for default trust and explicit
234     // trust, but explicitly distrusted usages will be set to
235     // CERTDB_TERMINAL_RECORD only.
236     CERTCertTrust trust = {CERTDB_VALID_CA, CERTDB_VALID_CA, CERTDB_VALID_CA};
237 
238     if (trustBits & net::NSSCertDatabase::DISTRUSTED_SSL)
239       trust.sslFlags = CERTDB_TERMINAL_RECORD;
240     else if (trustBits & net::NSSCertDatabase::TRUSTED_SSL)
241       trust.sslFlags |= CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
242 
243     if (trustBits & net::NSSCertDatabase::DISTRUSTED_EMAIL)
244       trust.emailFlags = CERTDB_TERMINAL_RECORD;
245     else if (trustBits & net::NSSCertDatabase::TRUSTED_EMAIL)
246       trust.emailFlags |= CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
247 
248     if (trustBits & net::NSSCertDatabase::DISTRUSTED_OBJ_SIGN)
249       trust.objectSigningFlags = CERTDB_TERMINAL_RECORD;
250     else if (trustBits & net::NSSCertDatabase::TRUSTED_OBJ_SIGN)
251       trust.objectSigningFlags |= CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
252 
253     srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert, &trust);
254   } else if (type == net::SERVER_CERT) {
255     CERTCertTrust trust = {0};
256     // We only modify the sslFlags, so copy the other flags.
257     CERT_GetCertTrust(nsscert, &trust);
258     trust.sslFlags = 0;
259 
260     if (trustBits & net::NSSCertDatabase::DISTRUSTED_SSL)
261       trust.sslFlags |= CERTDB_TERMINAL_RECORD;
262     else if (trustBits & net::NSSCertDatabase::TRUSTED_SSL)
263       trust.sslFlags |= CERTDB_TRUSTED | CERTDB_TERMINAL_RECORD;
264 
265     srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert, &trust);
266   } else {
267     // ignore user and email/unknown certs
268     return true;
269   }
270   if (srv != SECSuccess)
271     LOG(ERROR) << "SetCertTrust failed with error " << PORT_GetError();
272   return srv == SECSuccess;
273 }
274 
275 }  // namespace mozilla_security_manager
276