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
19namespace net {
20
21ClientCertStoreNSS::ClientCertStoreNSS(
22    const PasswordDelegateFactory& password_delegate_factory)
23    : password_delegate_factory_(password_delegate_factory) {}
24
25ClientCertStoreNSS::~ClientCertStoreNSS() {}
26
27void 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
51void 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
116void 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
137bool 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