| 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.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <list> | |
| 9 #include <string> | |
| 10 #include <vector> | |
| 11 | |
| 12 #include <cert.h> | |
| 13 #include <pk11pub.h> | |
| 14 | |
| 15 #include "base/logging.h" | |
| 16 #include "base/values.h" | |
| 17 #include "net/base/net_errors.h" | |
| 18 #include "net/cert/cert_database.h" | |
| 19 #include "net/cert/nss_cert_database.h" | |
| 20 #include "net/cert/x509_cert_types.h" | |
| 21 #include "net/cert/x509_certificate.h" | |
| 22 | |
| 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 { | |
| 32 | |
| 33 namespace { | |
| 34 | |
| 35 // Keys for converting classes below to/from dictionaries. | |
| 36 const char kCommonNameKey[] = "CommonName"; | |
| 37 const char kLocalityKey[] = "Locality"; | |
| 38 const char kOrganizationKey[] = "Organization"; | |
| 39 const char kOrganizationalUnitKey[] = "OrganizationalUnit"; | |
| 40 const char kIssuerCaRefKey[] = "IssuerCARef"; | |
| 41 const char kIssuerKey[] = "Issuer"; | |
| 42 const char kSubjectKey[] = "Subject"; | |
| 43 const char kEnrollmentUriKey[] = "EnrollmentURI"; | |
| 44 | |
| 45 bool GetAsListOfStrings(const base::Value& value, | |
| 46 std::vector<std::string>* result) { | |
| 47 const base::ListValue* list = NULL; | |
| 48 if (!value.GetAsList(&list)) | |
| 49 return false; | |
| 50 result->clear(); | |
| 51 result->reserve(list->GetSize()); | |
| 52 for (size_t i = 0; i < list->GetSize(); i++) { | |
| 53 std::string item; | |
| 54 if (!list->GetString(i, &item)) | |
| 55 return false; | |
| 56 result->push_back(item); | |
| 57 } | |
| 58 return true; | |
| 59 } | |
| 60 | |
| 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. | |
| 71 class IssuerFilter { | |
| 72 public: | |
| 73 explicit IssuerFilter(const IssuerSubjectPattern& issuer) | |
| 74 : issuer_(issuer) {} | |
| 75 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 76 return !issuer_.Matches(cert.get()->issuer()); | |
| 77 } | |
| 78 private: | |
| 79 const IssuerSubjectPattern& issuer_; | |
| 80 }; | |
| 81 | |
| 82 // Functor to filter out non-matching subjects. | |
| 83 class SubjectFilter { | |
| 84 public: | |
| 85 explicit SubjectFilter(const IssuerSubjectPattern& subject) | |
| 86 : subject_(subject) {} | |
| 87 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 88 return !subject_.Matches(cert.get()->subject()); | |
| 89 } | |
| 90 private: | |
| 91 const IssuerSubjectPattern& subject_; | |
| 92 }; | |
| 93 | |
| 94 // Functor to filter out certs that don't have private keys, or are invalid. | |
| 95 class PrivateKeyFilter { | |
| 96 public: | |
| 97 explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {} | |
| 98 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 99 return cert_db_->CheckUserCert(cert.get()) != net::OK; | |
| 100 } | |
| 101 private: | |
| 102 net::CertDatabase* cert_db_; | |
| 103 }; | |
| 104 | |
| 105 // Functor to filter out certs that don't have an issuer in the associated | |
| 106 // IssuerCARef list. | |
| 107 class IssuerCaRefFilter { | |
| 108 public: | |
| 109 explicit IssuerCaRefFilter(const vector<string>& issuer_ca_ref_list) | |
| 110 : issuer_ca_ref_list_(issuer_ca_ref_list) {} | |
| 111 bool operator()(const scoped_refptr<net::X509Certificate>& cert) const { | |
| 112 // Find the certificate issuer for each certificate. | |
| 113 // TODO(gspencer): this functionality should be available from | |
| 114 // X509Certificate or NSSCertDatabase. | |
| 115 CERTCertificate* issuer_cert = CERT_FindCertIssuer( | |
| 116 cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA); | |
| 117 | |
| 118 if (issuer_cert && issuer_cert->nickname) { | |
| 119 // Separate the nickname stored in the certificate at the colon, since | |
| 120 // NSS likes to store it as token:nickname. | |
| 121 const char* delimiter = ::strchr(issuer_cert->nickname, ':'); | |
| 122 if (delimiter) { | |
| 123 delimiter++; // move past the colon. | |
| 124 vector<string>::const_iterator pat_iter = issuer_ca_ref_list_.begin(); | |
| 125 while (pat_iter != issuer_ca_ref_list_.end()) { | |
| 126 if (::strcmp(delimiter, pat_iter->c_str()) == 0) | |
| 127 return false; | |
| 128 ++pat_iter; | |
| 129 } | |
| 130 } | |
| 131 } | |
| 132 return true; | |
| 133 } | |
| 134 private: | |
| 135 const vector<string>& issuer_ca_ref_list_; | |
| 136 }; | |
| 137 | |
| 138 } // namespace | |
| 139 | |
| 140 //////////////////////////////////////////////////////////////////////////////// | |
| 141 // IssuerSubjectPattern | |
| 142 IssuerSubjectPattern::IssuerSubjectPattern(const std::string& common_name, | |
| 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 | |
| 245 // Start with all the certs, and narrow it down from there. | |
| 246 net::CertificateList all_certs; | |
| 247 CertificateStlList matching_certs; | |
| 248 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs); | |
| 249 | |
| 250 if (all_certs.empty()) | |
| 251 return NULL; | |
| 252 | |
| 253 for (net::CertificateList::iterator iter = all_certs.begin(); | |
| 254 iter != all_certs.end(); ++iter) { | |
| 255 matching_certs.push_back(*iter); | |
| 256 } | |
| 257 | |
| 258 // Strip off any certs that don't have the right issuer and/or subject. | |
| 259 if (!issuer_.Empty()) { | |
| 260 matching_certs.remove_if(IssuerFilter(issuer_)); | |
| 261 if (matching_certs.empty()) | |
| 262 return NULL; | |
| 263 } | |
| 264 | |
| 265 if (!subject_.Empty()) { | |
| 266 matching_certs.remove_if(SubjectFilter(subject_)); | |
| 267 if (matching_certs.empty()) | |
| 268 return NULL; | |
| 269 } | |
| 270 | |
| 271 if (!issuer_ca_ref_list_.empty()) { | |
| 272 matching_certs.remove_if(IssuerCaRefFilter(issuer_ca_ref_list_)); | |
| 273 if (matching_certs.empty()) | |
| 274 return NULL; | |
| 275 } | |
| 276 | |
| 277 // 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 | |
| 279 // underlying PKCS11 calls), so we do this last to reduce the number of times | |
| 280 // we have to call it. | |
| 281 PrivateKeyFilter private_filter(net::CertDatabase::GetInstance()); | |
| 282 matching_certs.remove_if(private_filter); | |
| 283 | |
| 284 if (matching_certs.empty()) | |
| 285 return NULL; | |
| 286 | |
| 287 // 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. | |
| 289 scoped_refptr<net::X509Certificate> latest(NULL); | |
| 290 | |
| 291 // Iterate over the rest looking for the one that was issued latest. | |
| 292 for (CertificateStlList::iterator iter = matching_certs.begin(); | |
| 293 iter != matching_certs.end(); ++iter) { | |
| 294 if (!latest.get() || (*iter)->valid_start() > latest->valid_start()) | |
| 295 latest = *iter; | |
| 296 } | |
| 297 | |
| 298 return latest; | |
| 299 } | |
| 300 | |
| 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 | |
| OLD | NEW |