1 // Copyright (c) 2012 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.
5 #include "chromeos/network/client_cert_util.h"
14 #include "base/values.h"
15 #include "chromeos/network/certificate_pattern.h"
16 #include "net/base/net_errors.h"
17 #include "net/cert/cert_database.h"
18 #include "net/cert/nss_cert_database.h"
19 #include "net/cert/x509_cert_types.h"
20 #include "net/cert/x509_certificate.h"
21 #include "third_party/cros_system_api/dbus/service_constants.h"
25 namespace client_cert
{
29 // Functor to filter out non-matching issuers.
32 explicit IssuerFilter(const IssuerSubjectPattern
& issuer
)
34 bool operator()(const scoped_refptr
<net::X509Certificate
>& cert
) const {
35 return !CertPrincipalMatches(issuer_
, cert
.get()->issuer());
38 const IssuerSubjectPattern
& issuer_
;
41 // Functor to filter out non-matching subjects.
44 explicit SubjectFilter(const IssuerSubjectPattern
& subject
)
45 : subject_(subject
) {}
46 bool operator()(const scoped_refptr
<net::X509Certificate
>& cert
) const {
47 return !CertPrincipalMatches(subject_
, cert
.get()->subject());
50 const IssuerSubjectPattern
& subject_
;
53 // Functor to filter out certs that don't have private keys, or are invalid.
54 class PrivateKeyFilter
{
56 explicit PrivateKeyFilter(net::CertDatabase
* cert_db
) : cert_db_(cert_db
) {}
57 bool operator()(const scoped_refptr
<net::X509Certificate
>& cert
) const {
58 return cert_db_
->CheckUserCert(cert
.get()) != net::OK
;
61 net::CertDatabase
* cert_db_
;
64 // Functor to filter out certs that don't have an issuer in the associated
66 class IssuerCaFilter
{
68 explicit IssuerCaFilter(const std::vector
<std::string
>& issuer_ca_pems
)
69 : issuer_ca_pems_(issuer_ca_pems
) {}
70 bool operator()(const scoped_refptr
<net::X509Certificate
>& cert
) const {
71 // Find the certificate issuer for each certificate.
72 // TODO(gspencer): this functionality should be available from
73 // X509Certificate or NSSCertDatabase.
74 CERTCertificate
* issuer_cert
= CERT_FindCertIssuer(
75 cert
.get()->os_cert_handle(), PR_Now(), certUsageAnyCA
);
80 std::string pem_encoded
;
81 if (!net::X509Certificate::GetPEMEncoded(issuer_cert
, &pem_encoded
)) {
82 LOG(ERROR
) << "Couldn't PEM-encode certificate.";
86 return (std::find(issuer_ca_pems_
.begin(), issuer_ca_pems_
.end(),
88 issuer_ca_pems_
.end());
91 const std::vector
<std::string
>& issuer_ca_pems_
;
96 // Returns true only if any fields set in this pattern match exactly with
97 // similar fields in the principal. If organization_ or organizational_unit_
98 // are set, then at least one of the organizations or units in the principal
100 bool CertPrincipalMatches(const IssuerSubjectPattern
& pattern
,
101 const net::CertPrincipal
& principal
) {
102 if (!pattern
.common_name().empty() &&
103 pattern
.common_name() != principal
.common_name
) {
107 if (!pattern
.locality().empty() &&
108 pattern
.locality() != principal
.locality_name
) {
112 if (!pattern
.organization().empty()) {
113 if (std::find(principal
.organization_names
.begin(),
114 principal
.organization_names
.end(),
115 pattern
.organization()) ==
116 principal
.organization_names
.end()) {
121 if (!pattern
.organizational_unit().empty()) {
122 if (std::find(principal
.organization_unit_names
.begin(),
123 principal
.organization_unit_names
.end(),
124 pattern
.organizational_unit()) ==
125 principal
.organization_unit_names
.end()) {
133 scoped_refptr
<net::X509Certificate
> GetCertificateMatch(
134 const CertificatePattern
& pattern
) {
135 typedef std::list
<scoped_refptr
<net::X509Certificate
> > CertificateStlList
;
137 // Start with all the certs, and narrow it down from there.
138 net::CertificateList all_certs
;
139 CertificateStlList matching_certs
;
140 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs
);
142 if (all_certs
.empty())
145 for (net::CertificateList::iterator iter
= all_certs
.begin();
146 iter
!= all_certs
.end(); ++iter
) {
147 matching_certs
.push_back(*iter
);
150 // Strip off any certs that don't have the right issuer and/or subject.
151 if (!pattern
.issuer().Empty()) {
152 matching_certs
.remove_if(IssuerFilter(pattern
.issuer()));
153 if (matching_certs
.empty())
157 if (!pattern
.subject().Empty()) {
158 matching_certs
.remove_if(SubjectFilter(pattern
.subject()));
159 if (matching_certs
.empty())
163 if (!pattern
.issuer_ca_pems().empty()) {
164 matching_certs
.remove_if(IssuerCaFilter(pattern
.issuer_ca_pems()));
165 if (matching_certs
.empty())
169 // Eliminate any certs that don't have private keys associated with
170 // them. The CheckUserCert call in the filter is a little slow (because of
171 // underlying PKCS11 calls), so we do this last to reduce the number of times
172 // we have to call it.
173 PrivateKeyFilter
private_filter(net::CertDatabase::GetInstance());
174 matching_certs
.remove_if(private_filter
);
176 if (matching_certs
.empty())
179 // We now have a list of certificates that match the pattern we're
180 // looking for. Now we find the one with the latest start date.
181 scoped_refptr
<net::X509Certificate
> latest(NULL
);
183 // Iterate over the rest looking for the one that was issued latest.
184 for (CertificateStlList::iterator iter
= matching_certs
.begin();
185 iter
!= matching_certs
.end(); ++iter
) {
186 if (!latest
.get() || (*iter
)->valid_start() > latest
->valid_start())
193 void SetShillProperties(const client_cert::ConfigType cert_config_type
,
194 const std::string
& tpm_slot
,
195 const std::string
& tpm_pin
,
196 const std::string
* pkcs11_id
,
197 base::DictionaryValue
* properties
) {
198 const char* tpm_pin_property
= NULL
;
199 switch (cert_config_type
) {
200 case CONFIG_TYPE_NONE
: {
203 case CONFIG_TYPE_OPENVPN
: {
204 tpm_pin_property
= flimflam::kOpenVPNPinProperty
;
206 properties
->SetStringWithoutPathExpansion(
207 flimflam::kOpenVPNClientCertIdProperty
, *pkcs11_id
);
211 case CONFIG_TYPE_IPSEC
: {
212 tpm_pin_property
= flimflam::kL2tpIpsecPinProperty
;
213 if (!tpm_slot
.empty()) {
214 properties
->SetStringWithoutPathExpansion(
215 flimflam::kL2tpIpsecClientCertSlotProperty
, tpm_slot
);
218 properties
->SetStringWithoutPathExpansion(
219 flimflam::kL2tpIpsecClientCertIdProperty
, *pkcs11_id
);
223 case CONFIG_TYPE_EAP
: {
224 tpm_pin_property
= flimflam::kEapPinProperty
;
226 // Shill requires both CertID and KeyID for TLS connections, despite the
227 // fact that by convention they are the same ID.
228 properties
->SetStringWithoutPathExpansion(flimflam::kEapCertIdProperty
,
230 properties
->SetStringWithoutPathExpansion(flimflam::kEapKeyIdProperty
,
236 DCHECK(tpm_pin_property
);
237 if (!tpm_pin
.empty())
238 properties
->SetStringWithoutPathExpansion(tpm_pin_property
, tpm_pin
);
241 } // namespace client_cert
243 } // namespace chromeos