| 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 "chromeos/network/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 // IssuerCAPEMs list. | |
| 99 class IssuerCaFilter { | |
| 100 public: | |
| 101 explicit IssuerCaFilter(const std::vector<std::string>& issuer_ca_pems) | |
| 102 : issuer_ca_pems_(issuer_ca_pems) {} | |
| 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) | |
| 111 return true; | |
| 112 | |
| 113 std::string pem_encoded; | |
| 114 if (!net::X509Certificate::GetPEMEncoded(issuer_cert, &pem_encoded)) { | |
| 115 LOG(ERROR) << "Couldn't PEM-encode certificate."; | |
| 116 return true; | |
| 117 } | |
| 118 | |
| 119 return (std::find(issuer_ca_pems_.begin(), issuer_ca_pems_.end(), | |
| 120 pem_encoded) == | |
| 121 issuer_ca_pems_.end()); | |
| 122 } | |
| 123 private: | |
| 124 const std::vector<std::string>& issuer_ca_pems_; | |
| 125 }; | |
| 126 | |
| 127 } // namespace | |
| 128 | |
| 129 namespace certificate_pattern { | |
| 130 | |
| 131 scoped_refptr<net::X509Certificate> GetCertificateMatch( | |
| 132 const CertificatePattern& pattern) { | |
| 133 typedef std::list<scoped_refptr<net::X509Certificate> > CertificateStlList; | |
| 134 | |
| 135 // Start with all the certs, and narrow it down from there. | |
| 136 net::CertificateList all_certs; | |
| 137 CertificateStlList matching_certs; | |
| 138 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs); | |
| 139 | |
| 140 if (all_certs.empty()) | |
| 141 return NULL; | |
| 142 | |
| 143 for (net::CertificateList::iterator iter = all_certs.begin(); | |
| 144 iter != all_certs.end(); ++iter) { | |
| 145 matching_certs.push_back(*iter); | |
| 146 } | |
| 147 | |
| 148 // Strip off any certs that don't have the right issuer and/or subject. | |
| 149 if (!pattern.issuer().Empty()) { | |
| 150 matching_certs.remove_if(IssuerFilter(pattern.issuer())); | |
| 151 if (matching_certs.empty()) | |
| 152 return NULL; | |
| 153 } | |
| 154 | |
| 155 if (!pattern.subject().Empty()) { | |
| 156 matching_certs.remove_if(SubjectFilter(pattern.subject())); | |
| 157 if (matching_certs.empty()) | |
| 158 return NULL; | |
| 159 } | |
| 160 | |
| 161 if (!pattern.issuer_ca_pems().empty()) { | |
| 162 matching_certs.remove_if(IssuerCaFilter(pattern.issuer_ca_pems())); | |
| 163 if (matching_certs.empty()) | |
| 164 return NULL; | |
| 165 } | |
| 166 | |
| 167 // Eliminate any certs that don't have private keys associated with | |
| 168 // them. The CheckUserCert call in the filter is a little slow (because of | |
| 169 // underlying PKCS11 calls), so we do this last to reduce the number of times | |
| 170 // we have to call it. | |
| 171 PrivateKeyFilter private_filter(net::CertDatabase::GetInstance()); | |
| 172 matching_certs.remove_if(private_filter); | |
| 173 | |
| 174 if (matching_certs.empty()) | |
| 175 return NULL; | |
| 176 | |
| 177 // We now have a list of certificates that match the pattern we're | |
| 178 // looking for. Now we find the one with the latest start date. | |
| 179 scoped_refptr<net::X509Certificate> latest(NULL); | |
| 180 | |
| 181 // Iterate over the rest looking for the one that was issued latest. | |
| 182 for (CertificateStlList::iterator iter = matching_certs.begin(); | |
| 183 iter != matching_certs.end(); ++iter) { | |
| 184 if (!latest.get() || (*iter)->valid_start() > latest->valid_start()) | |
| 185 latest = *iter; | |
| 186 } | |
| 187 | |
| 188 return latest; | |
| 189 } | |
| 190 | |
| 191 } // namespace certificate_pattern | |
| 192 | |
| 193 } // namespace chromeos | |
| OLD | NEW |