Home | History | Annotate | Download | only in ssl
      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 
     21 ClientCertStoreNSS::ClientCertStoreNSS(
     22     const PasswordDelegateFactory& password_delegate_factory)
     23     : password_delegate_factory_(password_delegate_factory) {}
     24 
     25 ClientCertStoreNSS::~ClientCertStoreNSS() {}
     26 
     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 
     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 
    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 
    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