OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/cros/certificate_pattern.h" | 5 #include "chrome/browser/chromeos/cros/certificate_pattern_matcher.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <cert.h> |
| 8 #include <pk11pub.h> |
| 9 |
8 #include <list> | 10 #include <list> |
9 #include <string> | 11 #include <string> |
10 #include <vector> | 12 #include <vector> |
11 | 13 |
12 #include <cert.h> | 14 #include "chromeos/network/certificate_pattern.h" |
13 #include <pk11pub.h> | |
14 | |
15 #include "base/logging.h" | |
16 #include "base/values.h" | |
17 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
18 #include "net/cert/cert_database.h" | 16 #include "net/cert/cert_database.h" |
19 #include "net/cert/nss_cert_database.h" | 17 #include "net/cert/nss_cert_database.h" |
20 #include "net/cert/x509_cert_types.h" | 18 #include "net/cert/x509_cert_types.h" |
21 #include "net/cert/x509_certificate.h" | 19 #include "net/cert/x509_certificate.h" |
22 | 20 |
23 // To shorten some of those long lines below. | |
24 using base::DictionaryValue; | |
25 using base::ListValue; | |
26 using std::find; | |
27 using std::list; | |
28 using std::string; | |
29 using std::vector; | |
30 | |
31 namespace chromeos { | 21 namespace chromeos { |
32 | 22 |
33 namespace { | 23 namespace { |
34 | 24 |
35 // Keys for converting classes below to/from dictionaries. | 25 // Returns true only if any fields set in this pattern match exactly with |
36 const char kCommonNameKey[] = "CommonName"; | 26 // similar fields in the principal. If organization_ or organizational_unit_ |
37 const char kLocalityKey[] = "Locality"; | 27 // are set, then at least one of the organizations or units in the principal |
38 const char kOrganizationKey[] = "Organization"; | 28 // must match. |
39 const char kOrganizationalUnitKey[] = "OrganizationalUnit"; | 29 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern, |
40 const char kIssuerCaRefKey[] = "IssuerCARef"; | 30 const net::CertPrincipal& principal) { |
41 const char kIssuerKey[] = "Issuer"; | 31 if (!pattern.common_name().empty() && |
42 const char kSubjectKey[] = "Subject"; | 32 pattern.common_name() != principal.common_name) { |
43 const char kEnrollmentUriKey[] = "EnrollmentURI"; | 33 return false; |
| 34 } |
44 | 35 |
45 bool GetAsListOfStrings(const base::Value& value, | 36 if (!pattern.locality().empty() && |
46 std::vector<std::string>* result) { | 37 pattern.locality() != principal.locality_name) { |
47 const base::ListValue* list = NULL; | |
48 if (!value.GetAsList(&list)) | |
49 return false; | 38 return false; |
50 result->clear(); | 39 } |
51 result->reserve(list->GetSize()); | 40 |
52 for (size_t i = 0; i < list->GetSize(); i++) { | 41 if (!pattern.organization().empty()) { |
53 std::string item; | 42 if (std::find(principal.organization_names.begin(), |
54 if (!list->GetString(i, &item)) | 43 principal.organization_names.end(), |
| 44 pattern.organization()) == |
| 45 principal.organization_names.end()) { |
55 return false; | 46 return false; |
56 result->push_back(item); | 47 } |
57 } | 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 |
58 return true; | 59 return true; |
59 } | 60 } |
60 | 61 |
61 ListValue* CreateListFromStrings(const vector<string>& strings) { | |
62 ListValue* new_list = new ListValue; | |
63 for (vector<string>::const_iterator iter = strings.begin(); | |
64 iter != strings.end(); ++iter) { | |
65 new_list->Append(new StringValue(*iter)); | |
66 } | |
67 return new_list; | |
68 } | |
69 | |
70 // Functor to filter out non-matching issuers. | 62 // Functor to filter out non-matching issuers. |
71 class IssuerFilter { | 63 class IssuerFilter { |
72 public: | 64 public: |
73 explicit IssuerFilter(const IssuerSubjectPattern& issuer) | 65 explicit IssuerFilter(const IssuerSubjectPattern& issuer) |
74 : issuer_(issuer) {} | 66 : issuer_(issuer) {} |
75 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | 67 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { |
76 return !issuer_.Matches(cert.get()->issuer()); | 68 return !CertPrincipalMatches(issuer_, cert.get()->issuer()); |
77 } | 69 } |
78 private: | 70 private: |
79 const IssuerSubjectPattern& issuer_; | 71 const IssuerSubjectPattern& issuer_; |
80 }; | 72 }; |
81 | 73 |
82 // Functor to filter out non-matching subjects. | 74 // Functor to filter out non-matching subjects. |
83 class SubjectFilter { | 75 class SubjectFilter { |
84 public: | 76 public: |
85 explicit SubjectFilter(const IssuerSubjectPattern& subject) | 77 explicit SubjectFilter(const IssuerSubjectPattern& subject) |
86 : subject_(subject) {} | 78 : subject_(subject) {} |
87 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | 79 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { |
88 return !subject_.Matches(cert.get()->subject()); | 80 return !CertPrincipalMatches(subject_, cert.get()->subject()); |
89 } | 81 } |
90 private: | 82 private: |
91 const IssuerSubjectPattern& subject_; | 83 const IssuerSubjectPattern& subject_; |
92 }; | 84 }; |
93 | 85 |
94 // Functor to filter out certs that don't have private keys, or are invalid. | 86 // Functor to filter out certs that don't have private keys, or are invalid. |
95 class PrivateKeyFilter { | 87 class PrivateKeyFilter { |
96 public: | 88 public: |
97 explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {} | 89 explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {} |
98 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | 90 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { |
99 return cert_db_->CheckUserCert(cert.get()) != net::OK; | 91 return cert_db_->CheckUserCert(cert.get()) != net::OK; |
100 } | 92 } |
101 private: | 93 private: |
102 net::CertDatabase* cert_db_; | 94 net::CertDatabase* cert_db_; |
103 }; | 95 }; |
104 | 96 |
105 // Functor to filter out certs that don't have an issuer in the associated | 97 // Functor to filter out certs that don't have an issuer in the associated |
106 // IssuerCARef list. | 98 // IssuerCARef list. |
107 class IssuerCaRefFilter { | 99 class IssuerCaRefFilter { |
108 public: | 100 public: |
109 explicit IssuerCaRefFilter(const vector<string>& issuer_ca_ref_list) | 101 explicit IssuerCaRefFilter(const std::vector<std::string>& issuer_ca_ref_list) |
110 : issuer_ca_ref_list_(issuer_ca_ref_list) {} | 102 : issuer_ca_ref_list_(issuer_ca_ref_list) {} |
111 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | 103 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { |
112 // Find the certificate issuer for each certificate. | 104 // Find the certificate issuer for each certificate. |
113 // TODO(gspencer): this functionality should be available from | 105 // TODO(gspencer): this functionality should be available from |
114 // X509Certificate or NSSCertDatabase. | 106 // X509Certificate or NSSCertDatabase. |
115 CERTCertificate* issuer_cert = CERT_FindCertIssuer( | 107 CERTCertificate* issuer_cert = CERT_FindCertIssuer( |
116 cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA); | 108 cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA); |
117 | 109 |
118 if (issuer_cert && issuer_cert->nickname) { | 110 if (issuer_cert && issuer_cert->nickname) { |
119 // Separate the nickname stored in the certificate at the colon, since | 111 // Separate the nickname stored in the certificate at the colon, since |
120 // NSS likes to store it as token:nickname. | 112 // NSS likes to store it as token:nickname. |
121 const char* delimiter = ::strchr(issuer_cert->nickname, ':'); | 113 const char* delimiter = ::strchr(issuer_cert->nickname, ':'); |
122 if (delimiter) { | 114 if (delimiter) { |
123 delimiter++; // move past the colon. | 115 delimiter++; // move past the colon. |
124 vector<string>::const_iterator pat_iter = issuer_ca_ref_list_.begin(); | 116 std::vector<std::string>::const_iterator pat_iter = |
| 117 issuer_ca_ref_list_.begin(); |
125 while (pat_iter != issuer_ca_ref_list_.end()) { | 118 while (pat_iter != issuer_ca_ref_list_.end()) { |
126 if (::strcmp(delimiter, pat_iter->c_str()) == 0) | 119 if (*pat_iter == delimiter) |
127 return false; | 120 return false; |
128 ++pat_iter; | 121 ++pat_iter; |
129 } | 122 } |
130 } | 123 } |
131 } | 124 } |
132 return true; | 125 return true; |
133 } | 126 } |
134 private: | 127 private: |
135 const vector<string>& issuer_ca_ref_list_; | 128 const std::vector<std::string>& issuer_ca_ref_list_; |
136 }; | 129 }; |
137 | 130 |
138 } // namespace | 131 } // namespace |
139 | 132 |
140 //////////////////////////////////////////////////////////////////////////////// | 133 scoped_refptr<net::X509Certificate> GetCertificateMatch( |
141 // IssuerSubjectPattern | 134 const CertificatePattern& pattern) { |
142 IssuerSubjectPattern::IssuerSubjectPattern(const std::string& common_name, | 135 typedef std::list<scoped_refptr<net::X509Certificate> > CertificateStlList; |
143 const std::string& locality, | |
144 const std::string& organization, | |
145 const std::string& organizational_unit) | |
146 : common_name_(common_name), | |
147 locality_(locality), | |
148 organization_(organization), | |
149 organizational_unit_(organizational_unit) { } | |
150 | |
151 IssuerSubjectPattern::IssuerSubjectPattern() {} | |
152 | |
153 IssuerSubjectPattern::~IssuerSubjectPattern() {} | |
154 | |
155 bool IssuerSubjectPattern::Matches(const net::CertPrincipal& principal) const { | |
156 if (!common_name_.empty() && common_name_ != principal.common_name) | |
157 return false; | |
158 | |
159 if (!locality_.empty() && locality_ != principal.locality_name) | |
160 return false; | |
161 | |
162 if (!organization_.empty()) { | |
163 if (find(principal.organization_names.begin(), | |
164 principal.organization_names.end(), organization_) == | |
165 principal.organization_names.end()) { | |
166 return false; | |
167 } | |
168 } | |
169 | |
170 if (!organizational_unit_.empty()) { | |
171 if (find(principal.organization_unit_names.begin(), | |
172 principal.organization_unit_names.end(), | |
173 organizational_unit_) == principal.organization_unit_names.end()) { | |
174 return false; | |
175 } | |
176 } | |
177 | |
178 return true; | |
179 } | |
180 | |
181 bool IssuerSubjectPattern::Empty() const { | |
182 return common_name_.empty() && | |
183 locality_.empty() && | |
184 organization_.empty() && | |
185 organizational_unit_.empty(); | |
186 } | |
187 | |
188 void IssuerSubjectPattern::Clear() { | |
189 common_name_.clear(); | |
190 locality_.clear(); | |
191 organization_.clear(); | |
192 organizational_unit_.clear(); | |
193 } | |
194 | |
195 DictionaryValue* IssuerSubjectPattern::CreateAsDictionary() const { | |
196 DictionaryValue* dict = new DictionaryValue; | |
197 if (!common_name_.empty()) | |
198 dict->SetString(kCommonNameKey, common_name_); | |
199 if (!locality_.empty()) | |
200 dict->SetString(kLocalityKey, locality_); | |
201 if (!organization_.empty()) | |
202 dict->SetString(kOrganizationKey, organization_); | |
203 if (!organizational_unit_.empty()) | |
204 dict->SetString(kOrganizationalUnitKey, organizational_unit_); | |
205 return dict; | |
206 } | |
207 | |
208 bool IssuerSubjectPattern::CopyFromDictionary(const DictionaryValue& dict) { | |
209 Clear(); | |
210 dict.GetString(kCommonNameKey, &common_name_); | |
211 dict.GetString(kLocalityKey, &locality_); | |
212 dict.GetString(kOrganizationKey, &organization_); | |
213 dict.GetString(kOrganizationalUnitKey, &organizational_unit_); | |
214 // If the dictionary wasn't empty, but we are, or vice versa, then something | |
215 // went wrong. | |
216 DCHECK(dict.empty() == Empty()); | |
217 if (dict.empty() != Empty()) | |
218 return false; | |
219 return true; | |
220 } | |
221 | |
222 //////////////////////////////////////////////////////////////////////////////// | |
223 // CertificatePattern | |
224 | |
225 CertificatePattern::CertificatePattern() {} | |
226 | |
227 CertificatePattern::~CertificatePattern() {} | |
228 | |
229 bool CertificatePattern::Empty() const { | |
230 return issuer_ca_ref_list_.empty() && | |
231 issuer_.Empty() && | |
232 subject_.Empty(); | |
233 } | |
234 | |
235 void CertificatePattern::Clear() { | |
236 issuer_ca_ref_list_.clear(); | |
237 issuer_.Clear(); | |
238 subject_.Clear(); | |
239 enrollment_uri_list_.clear(); | |
240 } | |
241 | |
242 scoped_refptr<net::X509Certificate> CertificatePattern::GetMatch() const { | |
243 typedef list<scoped_refptr<net::X509Certificate> > CertificateStlList; | |
244 | 136 |
245 // Start with all the certs, and narrow it down from there. | 137 // Start with all the certs, and narrow it down from there. |
246 net::CertificateList all_certs; | 138 net::CertificateList all_certs; |
247 CertificateStlList matching_certs; | 139 CertificateStlList matching_certs; |
248 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs); | 140 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs); |
249 | 141 |
250 if (all_certs.empty()) | 142 if (all_certs.empty()) |
251 return NULL; | 143 return NULL; |
252 | 144 |
253 for (net::CertificateList::iterator iter = all_certs.begin(); | 145 for (net::CertificateList::iterator iter = all_certs.begin(); |
254 iter != all_certs.end(); ++iter) { | 146 iter != all_certs.end(); ++iter) { |
255 matching_certs.push_back(*iter); | 147 matching_certs.push_back(*iter); |
256 } | 148 } |
257 | 149 |
258 // Strip off any certs that don't have the right issuer and/or subject. | 150 // Strip off any certs that don't have the right issuer and/or subject. |
259 if (!issuer_.Empty()) { | 151 if (!pattern.issuer().Empty()) { |
260 matching_certs.remove_if(IssuerFilter(issuer_)); | 152 matching_certs.remove_if(IssuerFilter(pattern.issuer())); |
261 if (matching_certs.empty()) | 153 if (matching_certs.empty()) |
262 return NULL; | 154 return NULL; |
263 } | 155 } |
264 | 156 |
265 if (!subject_.Empty()) { | 157 if (!pattern.subject().Empty()) { |
266 matching_certs.remove_if(SubjectFilter(subject_)); | 158 matching_certs.remove_if(SubjectFilter(pattern.subject())); |
267 if (matching_certs.empty()) | 159 if (matching_certs.empty()) |
268 return NULL; | 160 return NULL; |
269 } | 161 } |
270 | 162 |
271 if (!issuer_ca_ref_list_.empty()) { | 163 if (!pattern.issuer_ca_ref_list().empty()) { |
272 matching_certs.remove_if(IssuerCaRefFilter(issuer_ca_ref_list_)); | 164 matching_certs.remove_if(IssuerCaRefFilter(pattern.issuer_ca_ref_list())); |
273 if (matching_certs.empty()) | 165 if (matching_certs.empty()) |
274 return NULL; | 166 return NULL; |
275 } | 167 } |
276 | 168 |
277 // Eliminate any certs that don't have private keys associated with | 169 // Eliminate any certs that don't have private keys associated with |
278 // them. The CheckUserCert call in the filter is a little slow (because of | 170 // them. The CheckUserCert call in the filter is a little slow (because of |
279 // underlying PKCS11 calls), so we do this last to reduce the number of times | 171 // underlying PKCS11 calls), so we do this last to reduce the number of times |
280 // we have to call it. | 172 // we have to call it. |
281 PrivateKeyFilter private_filter(net::CertDatabase::GetInstance()); | 173 PrivateKeyFilter private_filter(net::CertDatabase::GetInstance()); |
282 matching_certs.remove_if(private_filter); | 174 matching_certs.remove_if(private_filter); |
283 | 175 |
284 if (matching_certs.empty()) | 176 if (matching_certs.empty()) |
285 return NULL; | 177 return NULL; |
286 | 178 |
287 // We now have a list of certificates that match the pattern we're | 179 // We now have a list of certificates that match the pattern we're |
288 // looking for. Now we find the one with the latest start date. | 180 // looking for. Now we find the one with the latest start date. |
289 scoped_refptr<net::X509Certificate> latest(NULL); | 181 scoped_refptr<net::X509Certificate> latest(NULL); |
290 | 182 |
291 // Iterate over the rest looking for the one that was issued latest. | 183 // Iterate over the rest looking for the one that was issued latest. |
292 for (CertificateStlList::iterator iter = matching_certs.begin(); | 184 for (CertificateStlList::iterator iter = matching_certs.begin(); |
293 iter != matching_certs.end(); ++iter) { | 185 iter != matching_certs.end(); ++iter) { |
294 if (!latest.get() || (*iter)->valid_start() > latest->valid_start()) | 186 if (!latest.get() || (*iter)->valid_start() > latest->valid_start()) |
295 latest = *iter; | 187 latest = *iter; |
296 } | 188 } |
297 | 189 |
298 return latest; | 190 return latest; |
299 } | 191 } |
300 | 192 |
301 DictionaryValue* CertificatePattern::CreateAsDictionary() const { | |
302 DictionaryValue* dict = new base::DictionaryValue; | |
303 | |
304 if (!issuer_ca_ref_list_.empty()) | |
305 dict->Set(kIssuerCaRefKey, CreateListFromStrings(issuer_ca_ref_list_)); | |
306 | |
307 if (!issuer_.Empty()) | |
308 dict->Set(kIssuerKey, issuer_.CreateAsDictionary()); | |
309 | |
310 if (!subject_.Empty()) | |
311 dict->Set(kSubjectKey, subject_.CreateAsDictionary()); | |
312 | |
313 if (!enrollment_uri_list_.empty()) | |
314 dict->Set(kEnrollmentUriKey, CreateListFromStrings(enrollment_uri_list_)); | |
315 return dict; | |
316 } | |
317 | |
318 bool CertificatePattern::CopyFromDictionary(const DictionaryValue &dict) { | |
319 const DictionaryValue* child_dict = NULL; | |
320 const ListValue* child_list = NULL; | |
321 Clear(); | |
322 | |
323 // All of these are optional. | |
324 if (dict.GetList(kIssuerCaRefKey, &child_list) && child_list) { | |
325 if (!GetAsListOfStrings(*child_list, &issuer_ca_ref_list_)) | |
326 return false; | |
327 } | |
328 if (dict.GetDictionary(kIssuerKey, &child_dict) && child_dict) { | |
329 if (!issuer_.CopyFromDictionary(*child_dict)) | |
330 return false; | |
331 } | |
332 child_dict = NULL; | |
333 if (dict.GetDictionary(kSubjectKey, &child_dict) && child_dict) { | |
334 if (!subject_.CopyFromDictionary(*child_dict)) | |
335 return false; | |
336 } | |
337 child_list = NULL; | |
338 if (dict.GetList(kEnrollmentUriKey, &child_list) && child_list) { | |
339 if (!GetAsListOfStrings(*child_list, &enrollment_uri_list_)) | |
340 return false; | |
341 } | |
342 | |
343 // If we didn't copy anything from the dictionary, then it had better be | |
344 // empty. | |
345 DCHECK(dict.empty() == Empty()); | |
346 if (dict.empty() != Empty()) | |
347 return false; | |
348 | |
349 return true; | |
350 } | |
351 | |
352 } // namespace chromeos | 193 } // namespace chromeos |
OLD | NEW |