OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "chrome/browser/chromeos/cros/certificate_pattern_matcher.h" | |
6 | |
7 #include <cert.h> | |
8 #include <pk11pub.h> | |
9 | |
10 #include <list> | |
11 #include <string> | |
12 #include <vector> | |
13 | |
14 #include "chromeos/network/certificate_pattern.h" | |
15 #include "net/base/net_errors.h" | |
16 #include "net/cert/cert_database.h" | |
17 #include "net/cert/nss_cert_database.h" | |
18 #include "net/cert/x509_cert_types.h" | |
19 #include "net/cert/x509_certificate.h" | |
20 | |
21 namespace chromeos { | |
22 | |
23 namespace { | |
24 | |
25 // Returns true only if any fields set in this pattern match exactly with | |
26 // similar fields in the principal. If organization_ or organizational_unit_ | |
27 // are set, then at least one of the organizations or units in the principal | |
28 // must match. | |
29 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern, | |
30 const net::CertPrincipal& principal) { | |
31 if (!pattern.common_name().empty() && | |
32 pattern.common_name() != principal.common_name) { | |
33 return false; | |
34 } | |
35 | |
36 if (!pattern.locality().empty() && | |
37 pattern.locality() != principal.locality_name) { | |
38 return false; | |
39 } | |
40 | |
41 if (!pattern.organization().empty()) { | |
42 if (std::find(principal.organization_names.begin(), | |
43 principal.organization_names.end(), | |
44 pattern.organization()) == | |
45 principal.organization_names.end()) { | |
46 return false; | |
47 } | |
48 } | |
49 | |
50 if (!pattern.organizational_unit().empty()) { | |
51 if (std::find(principal.organization_unit_names.begin(), | |
52 principal.organization_unit_names.end(), | |
53 pattern.organizational_unit()) == | |
54 principal.organization_unit_names.end()) { | |
55 return false; | |
56 } | |
57 } | |
58 | |
59 return true; | |
60 } | |
61 | |
62 // Functor to filter out non-matching issuers. | |
63 class IssuerFilter { | |
64 public: | |
65 explicit IssuerFilter(const IssuerSubjectPattern& issuer) | |
66 : issuer_(issuer) {} | |
67 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
68 return !CertPrincipalMatches(issuer_, cert.get()->issuer()); | |
69 } | |
70 private: | |
71 const IssuerSubjectPattern& issuer_; | |
72 }; | |
73 | |
74 // Functor to filter out non-matching subjects. | |
75 class SubjectFilter { | |
76 public: | |
77 explicit SubjectFilter(const IssuerSubjectPattern& subject) | |
78 : subject_(subject) {} | |
79 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
80 return !CertPrincipalMatches(subject_, cert.get()->subject()); | |
81 } | |
82 private: | |
83 const IssuerSubjectPattern& subject_; | |
84 }; | |
85 | |
86 // Functor to filter out certs that don't have private keys, or are invalid. | |
87 class PrivateKeyFilter { | |
88 public: | |
89 explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {} | |
90 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
91 return cert_db_->CheckUserCert(cert.get()) != net::OK; | |
92 } | |
93 private: | |
94 net::CertDatabase* cert_db_; | |
95 }; | |
96 | |
97 // Functor to filter out certs that don't have an issuer in the associated | |
98 // IssuerCARef list. | |
99 class IssuerCaRefFilter { | |
100 public: | |
101 explicit IssuerCaRefFilter(const std::vector<std::string>& issuer_ca_ref_list) | |
102 : issuer_ca_ref_list_(issuer_ca_ref_list) {} | |
103 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
104 // Find the certificate issuer for each certificate. | |
105 // TODO(gspencer): this functionality should be available from | |
106 // X509Certificate or NSSCertDatabase. | |
107 CERTCertificate* issuer_cert = CERT_FindCertIssuer( | |
108 cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA); | |
109 | |
110 if (issuer_cert && issuer_cert->nickname) { | |
111 // Separate the nickname stored in the certificate at the colon, since | |
112 // NSS likes to store it as token:nickname. | |
113 const char* delimiter = ::strchr(issuer_cert->nickname, ':'); | |
114 if (delimiter) { | |
115 delimiter++; // move past the colon. | |
116 std::vector<std::string>::const_iterator pat_iter = | |
117 issuer_ca_ref_list_.begin(); | |
118 while (pat_iter != issuer_ca_ref_list_.end()) { | |
119 if (*pat_iter == delimiter) | |
120 return false; | |
121 ++pat_iter; | |
122 } | |
123 } | |
124 } | |
125 return true; | |
126 } | |
127 private: | |
128 const std::vector<std::string>& issuer_ca_ref_list_; | |
129 }; | |
130 | |
131 } // namespace | |
132 | |
133 scoped_refptr<net::X509Certificate> GetCertificateMatch( | |
134 const CertificatePattern& pattern) { | |
135 typedef std::list<scoped_refptr<net::X509Certificate> > CertificateStlList; | |
136 | |
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); | |
141 | |
142 if (all_certs.empty()) | |
143 return NULL; | |
144 | |
145 for (net::CertificateList::iterator iter = all_certs.begin(); | |
146 iter != all_certs.end(); ++iter) { | |
147 matching_certs.push_back(*iter); | |
148 } | |
149 | |
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()) | |
154 return NULL; | |
155 } | |
156 | |
157 if (!pattern.subject().Empty()) { | |
158 matching_certs.remove_if(SubjectFilter(pattern.subject())); | |
159 if (matching_certs.empty()) | |
160 return NULL; | |
161 } | |
162 | |
163 if (!pattern.issuer_ca_ref_list().empty()) { | |
164 matching_certs.remove_if(IssuerCaRefFilter(pattern.issuer_ca_ref_list())); | |
165 if (matching_certs.empty()) | |
166 return NULL; | |
167 } | |
168 | |
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); | |
175 | |
176 if (matching_certs.empty()) | |
177 return NULL; | |
178 | |
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); | |
182 | |
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()) | |
187 latest = *iter; | |
188 } | |
189 | |
190 return latest; | |
191 } | |
192 | |
193 } // namespace chromeos | |
OLD | NEW |