client_cert_store_nss.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "net/ssl/client_cert_store_nss.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <nss.h>
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <ssl.h>
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/bind.h"
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/location.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/logging.h"
13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/string_piece.h"
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "base/threading/worker_pool.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "crypto/nss_crypto_module_delegate.h"
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/x509_util.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace net {
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)ClientCertStoreNSS::ClientCertStoreNSS(
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const PasswordDelegateFactory& password_delegate_factory)
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : password_delegate_factory_(password_delegate_factory) {}
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)ClientCertStoreNSS::~ClientCertStoreNSS() {}
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ClientCertStoreNSS::GetClientCerts(const SSLCertRequestInfo& request,
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         CertificateList* selected_certs,
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                         const base::Closure& callback) {
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate;
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!password_delegate_factory_.is_null()) {
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    password_delegate.reset(
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        password_delegate_factory_.Run(request.host_and_port));
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (base::WorkerPool::PostTaskAndReply(
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          FROM_HERE,
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          base::Bind(&ClientCertStoreNSS::GetClientCertsOnWorkerThread,
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     // Caller is responsible for keeping the ClientCertStore
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     // alive until the callback is run.
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     base::Unretained(this),
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     base::Passed(&password_delegate),
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     &request,
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     selected_certs),
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          callback,
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          true))
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  selected_certs->clear();
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  callback.Run();
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ClientCertStoreNSS::GetClientCertsImpl(CERTCertList* cert_list,
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                            const SSLCertRequestInfo& request,
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                            bool query_nssdb,
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                            CertificateList* selected_certs) {
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(cert_list);
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(selected_certs);
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  selected_certs->clear();
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Create a "fake" CERTDistNames structure. No public API exists to create
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // one from a list of issuers.
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CERTDistNames ca_names;
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ca_names.arena = NULL;
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ca_names.nnames = 0;
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ca_names.names = NULL;
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ca_names.head = NULL;
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::vector<SECItem> ca_names_items(request.cert_authorities.size());
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  for (size_t i = 0; i < request.cert_authorities.size(); ++i) {
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& authority = request.cert_authorities[i];
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ca_names_items[i].type = siBuffer;
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ca_names_items[i].data =
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        reinterpret_cast<unsigned char*>(const_cast<char*>(authority.data()));
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ca_names_items[i].len = static_cast<unsigned int>(authority.size());
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ca_names.nnames = static_cast<int>(ca_names_items.size());
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!ca_names_items.empty())
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ca_names.names = &ca_names_items[0];
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  size_t num_raw = 0;
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       !CERT_LIST_END(node, cert_list);
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       node = CERT_LIST_NEXT(node)) {
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ++num_raw;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Only offer unexpired certificates.
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (CERT_CheckCertValidTimes(node->cert, PR_Now(), PR_TRUE) !=
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        secCertTimeValid) {
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DVLOG(2) << "skipped expired cert: "
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               << base::StringPiece(node->cert->nickname);
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle(
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        node->cert, X509Certificate::OSCertHandles());
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Check if the certificate issuer is allowed by the server.
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (request.cert_authorities.empty() ||
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        (!query_nssdb &&
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         cert->IsIssuedByEncoded(request.cert_authorities)) ||
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        (query_nssdb &&
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)         NSS_CmpCertChainWCANames(node->cert, &ca_names) == SECSuccess)) {
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DVLOG(2) << "matched cert: " << base::StringPiece(node->cert->nickname);
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      selected_certs->push_back(cert);
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DVLOG(2) << "skipped non-matching cert: "
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               << base::StringPiece(node->cert->nickname);
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DVLOG(2) << "num_raw:" << num_raw
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           << " num_selected:" << selected_certs->size();
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::sort(selected_certs->begin(), selected_certs->end(),
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            x509_util::ClientCertSorter());
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ClientCertStoreNSS::GetClientCertsOnWorkerThread(
117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    scoped_ptr<crypto::CryptoModuleBlockingPasswordDelegate> password_delegate,
118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const SSLCertRequestInfo* request,
119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    CertificateList* selected_certs) {
120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CERTCertList* client_certs = CERT_FindUserCertsByUsage(
121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      CERT_GetDefaultCertDB(),
122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      certUsageSSLClient,
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      PR_FALSE,
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      PR_FALSE,
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      password_delegate.get());
126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // It is ok for a user not to have any client certs.
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (!client_certs) {
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << "No client certs found.";
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    selected_certs->clear();
130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  GetClientCertsImpl(client_certs, *request, true, selected_certs);
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  CERT_DestroyCertList(client_certs);
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool ClientCertStoreNSS::SelectClientCertsForTesting(
138c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const CertificateList& input_certs,
139c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const SSLCertRequestInfo& request,
140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    CertificateList* selected_certs) {
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CERTCertList* cert_list = CERT_NewCertList();
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!cert_list)
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (size_t i = 0; i < input_certs.size(); ++i) {
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    CERT_AddCertToListTail(
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        cert_list, CERT_DupCertificate(input_certs[i]->os_cert_handle()));
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  GetClientCertsImpl(cert_list, request, false, selected_certs);
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CERT_DestroyCertList(cert_list);
1511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return true;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace net
155