• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/ssl/client_cert_store_nss.h"
6 
7 #include <nss.h>
8 #include <ssl.h>
9 
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/strings/string_piece.h"
15 #include "base/threading/worker_pool.h"
16 #include "crypto/nss_crypto_module_delegate.h"
17 #include "net/cert/x509_util.h"
18 
19 namespace net {
20 
ClientCertStoreNSS(const PasswordDelegateFactory & password_delegate_factory)21 ClientCertStoreNSS::ClientCertStoreNSS(
22     const PasswordDelegateFactory& password_delegate_factory)
23     : password_delegate_factory_(password_delegate_factory) {}
24 
~ClientCertStoreNSS()25 ClientCertStoreNSS::~ClientCertStoreNSS() {}
26 
GetClientCerts(const SSLCertRequestInfo & request,CertificateList * selected_certs,const base::Closure & callback)27 void ClientCertStoreNSS::GetClientCerts(const SSLCertRequestInfo& request,
28                                          CertificateList* selected_certs,
29                                          const base::Closure& callback) {
30   scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate;
31   if (!password_delegate_factory_.is_null()) {
32     password_delegate.reset(
33         password_delegate_factory_.Run(request.host_and_port));
34   }
35   if (base::WorkerPool::PostTaskAndReply(
36           FROM_HERE,
37           base::Bind(&ClientCertStoreNSS::GetClientCertsOnWorkerThread,
38                      // Caller is responsible for keeping the ClientCertStore
39                      // alive until the callback is run.
40                      base::Unretained(this),
41                      base::Passed(&password_delegate),
42                      &request,
43                      selected_certs),
44           callback,
45           true))
46     return;
47   selected_certs->clear();
48   callback.Run();
49 }
50 
GetClientCertsImpl(CERTCertList * cert_list,const SSLCertRequestInfo & request,bool query_nssdb,CertificateList * selected_certs)51 void ClientCertStoreNSS::GetClientCertsImpl(CERTCertList* cert_list,
52                                             const SSLCertRequestInfo& request,
53                                             bool query_nssdb,
54                                             CertificateList* selected_certs) {
55   DCHECK(cert_list);
56   DCHECK(selected_certs);
57 
58   selected_certs->clear();
59 
60   // Create a "fake" CERTDistNames structure. No public API exists to create
61   // one from a list of issuers.
62   CERTDistNames ca_names;
63   ca_names.arena = NULL;
64   ca_names.nnames = 0;
65   ca_names.names = NULL;
66   ca_names.head = NULL;
67 
68   std::vector<SECItem> ca_names_items(request.cert_authorities.size());
69   for (size_t i = 0; i < request.cert_authorities.size(); ++i) {
70     const std::string& authority = request.cert_authorities[i];
71     ca_names_items[i].type = siBuffer;
72     ca_names_items[i].data =
73         reinterpret_cast<unsigned char*>(const_cast<char*>(authority.data()));
74     ca_names_items[i].len = static_cast<unsigned int>(authority.size());
75   }
76   ca_names.nnames = static_cast<int>(ca_names_items.size());
77   if (!ca_names_items.empty())
78     ca_names.names = &ca_names_items[0];
79 
80   size_t num_raw = 0;
81   for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
82        !CERT_LIST_END(node, cert_list);
83        node = CERT_LIST_NEXT(node)) {
84     ++num_raw;
85     // Only offer unexpired certificates.
86     if (CERT_CheckCertValidTimes(node->cert, PR_Now(), PR_TRUE) !=
87         secCertTimeValid) {
88       DVLOG(2) << "skipped expired cert: "
89                << base::StringPiece(node->cert->nickname);
90       continue;
91     }
92 
93     scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
94         node->cert, X509Certificate::OSCertHandles());
95 
96     // Check if the certificate issuer is allowed by the server.
97     if (request.cert_authorities.empty() ||
98         (!query_nssdb &&
99          cert->IsIssuedByEncoded(request.cert_authorities)) ||
100         (query_nssdb &&
101          NSS_CmpCertChainWCANames(node->cert, &ca_names) == SECSuccess)) {
102       DVLOG(2) << "matched cert: " << base::StringPiece(node->cert->nickname);
103       selected_certs->push_back(cert);
104     }
105     else
106       DVLOG(2) << "skipped non-matching cert: "
107                << base::StringPiece(node->cert->nickname);
108   }
109   DVLOG(2) << "num_raw:" << num_raw
110            << " num_selected:" << selected_certs->size();
111 
112   std::sort(selected_certs->begin(), selected_certs->end(),
113             x509_util::ClientCertSorter());
114 }
115 
GetClientCertsOnWorkerThread(scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate,const SSLCertRequestInfo * request,CertificateList * selected_certs)116 void ClientCertStoreNSS::GetClientCertsOnWorkerThread(
117     scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate,
118     const SSLCertRequestInfo* request,
119     CertificateList* selected_certs) {
120   CERTCertList* client_certs = CERT_FindUserCertsByUsage(
121       CERT_GetDefaultCertDB(),
122       certUsageSSLClient,
123       PR_FALSE,
124       PR_FALSE,
125       password_delegate.get());
126   // It is ok for a user not to have any client certs.
127   if (!client_certs) {
128     DVLOG(2) << "No client certs found.";
129     selected_certs->clear();
130     return;
131   }
132 
133   GetClientCertsImpl(client_certs, *request, true, selected_certs);
134   CERT_DestroyCertList(client_certs);
135 }
136 
SelectClientCertsForTesting(const CertificateList & input_certs,const SSLCertRequestInfo & request,CertificateList * selected_certs)137 bool ClientCertStoreNSS::SelectClientCertsForTesting(
138     const CertificateList& input_certs,
139     const SSLCertRequestInfo& request,
140     CertificateList* selected_certs) {
141   CERTCertList* cert_list = CERT_NewCertList();
142   if (!cert_list)
143     return false;
144   for (size_t i = 0; i < input_certs.size(); ++i) {
145     CERT_AddCertToListTail(
146         cert_list, CERT_DupCertificate(input_certs[i]->os_cert_handle()));
147   }
148 
149   GetClientCertsImpl(cert_list, request, false, selected_certs);
150   CERT_DestroyCertList(cert_list);
151   return true;
152 }
153 
154 }  // namespace net
155