| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "net/cert/internal/path_builder.h" | 5 #include "net/cert/internal/path_builder.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <unordered_set> | 8 #include <unordered_set> |
| 9 | 9 |
| 10 #include "base/callback_helpers.h" | 10 #include "base/callback_helpers.h" |
| 11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "net/base/net_errors.h" | 12 #include "net/base/net_errors.h" |
| 13 #include "net/cert/internal/cert_issuer_source.h" | 13 #include "net/cert/internal/cert_issuer_source.h" |
| 14 #include "net/cert/internal/cert_issuer_source_collection.h" |
| 14 #include "net/cert/internal/parse_certificate.h" | 15 #include "net/cert/internal/parse_certificate.h" |
| 15 #include "net/cert/internal/parse_name.h" // For CertDebugString. | 16 #include "net/cert/internal/parse_name.h" // For CertDebugString. |
| 16 #include "net/cert/internal/signature_policy.h" | 17 #include "net/cert/internal/signature_policy.h" |
| 17 #include "net/cert/internal/trust_store.h" | 18 #include "net/cert/internal/trust_store.h" |
| 19 #include "net/cert/internal/trust_store_collection.h" |
| 18 #include "net/cert/internal/verify_certificate_chain.h" | 20 #include "net/cert/internal/verify_certificate_chain.h" |
| 19 #include "net/cert/internal/verify_name_match.h" | 21 #include "net/cert/internal/verify_name_match.h" |
| 20 #include "net/der/parser.h" | 22 #include "net/der/parser.h" |
| 21 #include "net/der/tag.h" | 23 #include "net/der/tag.h" |
| 22 | 24 |
| 23 namespace net { | 25 namespace net { |
| 24 | 26 |
| 25 namespace { | 27 namespace { |
| 26 | 28 |
| 27 using CertIssuerSources = std::vector<CertIssuerSource*>; | |
| 28 | |
| 29 // TODO(mattm): decide how much debug logging to keep. | 29 // TODO(mattm): decide how much debug logging to keep. |
| 30 std::string CertDebugString(const ParsedCertificate* cert) { | 30 std::string CertDebugString(const ParsedCertificate* cert) { |
| 31 RDNSequence subject, issuer; | 31 RDNSequence subject, issuer; |
| 32 std::string subject_str, issuer_str; | 32 std::string subject_str, issuer_str; |
| 33 if (!ParseName(cert->tbs().subject_tlv, &subject) || | 33 if (!ParseName(cert->tbs().subject_tlv, &subject) || |
| 34 !ConvertToRFC2253(subject, &subject_str)) | 34 !ConvertToRFC2253(subject, &subject_str)) |
| 35 subject_str = "???"; | 35 subject_str = "???"; |
| 36 if (!ParseName(cert->tbs().issuer_tlv, &issuer) || | 36 if (!ParseName(cert->tbs().issuer_tlv, &issuer) || |
| 37 !ConvertToRFC2253(issuer, &issuer_str)) | 37 !ConvertToRFC2253(issuer, &issuer_str)) |
| 38 issuer_str = "???"; | 38 issuer_str = "???"; |
| 39 | 39 |
| 40 return subject_str + "(" + issuer_str + ")"; | 40 return subject_str + "(" + issuer_str + ")"; |
| 41 } | 41 } |
| 42 | 42 |
| 43 // CertIssuersIter iterates through the intermediates from |cert_issuer_sources| | 43 // CertIssuersIter iterates through the intermediates from |cert_issuer_source| |
| 44 // which may be issuers of |cert|. | 44 // which may be issuers of |cert|. |
| 45 class CertIssuersIter { | 45 class CertIssuersIter { |
| 46 public: | 46 public: |
| 47 // Constructs the CertIssuersIter. |*cert_issuer_sources| must be valid for | 47 // Constructs the CertIssuersIter. |*cert_issuer_source| must be valid for |
| 48 // the lifetime of the CertIssuersIter. | 48 // the lifetime of the CertIssuersIter. |
| 49 CertIssuersIter(scoped_refptr<ParsedCertificate> cert, | 49 CertIssuersIter(scoped_refptr<ParsedCertificate> cert, |
| 50 CertIssuerSources* cert_issuer_sources, | 50 CertIssuerSource* cert_issuer_source); |
| 51 const TrustStore& trust_store); | |
| 52 | 51 |
| 53 // Gets the next candidate issuer. If an issuer is ready synchronously, SYNC | 52 // Gets the next candidate issuer. If an issuer is ready synchronously, SYNC |
| 54 // is returned and the cert is stored in |*out_cert|. If an issuer is not | 53 // is returned and the cert is stored in |*out_cert|. If an issuer is not |
| 55 // ready, ASYNC is returned and |callback| will be called once |*out_cert| has | 54 // ready, ASYNC is returned and |callback| will be called once |*out_cert| has |
| 56 // been set. If |callback| is null, always completes synchronously. | 55 // been set. If |callback| is null, always completes synchronously. |
| 57 // | 56 // |
| 58 // In either case, if all issuers have been exhausted, |*out_cert| is cleared. | 57 // In either case, if all issuers have been exhausted, |*out_cert| is cleared. |
| 59 CompletionStatus GetNextIssuer(scoped_refptr<ParsedCertificate>* out_cert, | 58 CompletionStatus GetNextIssuer(scoped_refptr<ParsedCertificate>* out_cert, |
| 60 const base::Closure& callback); | 59 const base::Closure& callback); |
| 61 | 60 |
| 62 // Returns the |cert| for which issuers are being retrieved. | 61 // Returns the |cert| for which issuers are being retrieved. |
| 63 const ParsedCertificate* cert() const { return cert_.get(); } | 62 const ParsedCertificate* cert() const { return cert_.get(); } |
| 64 scoped_refptr<ParsedCertificate> reference_cert() const { return cert_; } | 63 scoped_refptr<ParsedCertificate> reference_cert() const { return cert_; } |
| 65 | 64 |
| 66 private: | 65 private: |
| 67 void GotAsyncCerts(CertIssuerSource::Request* request); | 66 void GotAsyncCerts(CertIssuerSource::Request* request); |
| 68 | 67 |
| 69 scoped_refptr<ParsedCertificate> cert_; | 68 scoped_refptr<ParsedCertificate> cert_; |
| 70 CertIssuerSources* cert_issuer_sources_; | 69 CertIssuerSource* cert_issuer_source_; |
| 71 | 70 |
| 72 // The list of issuers for |cert_|. This is added to incrementally (first | 71 // The list of issuers for |cert_|. This is added to incrementally (first |
| 73 // synchronous results, then possibly multiple times as asynchronous results | 72 // synchronous results, then possibly multiple times as asynchronous results |
| 74 // arrive.) The issuers may be re-sorted each time new issuers are added, but | 73 // arrive.) The issuers may be re-sorted each time new issuers are added, but |
| 75 // only the results from |cur_| onwards should be sorted, since the earlier | 74 // only the results from |cur_| onwards should be sorted, since the earlier |
| 76 // results were already returned. | 75 // results were already returned. |
| 77 // Elements should not be removed from |issuers_| once added, since | 76 // Elements should not be removed from |issuers_| once added, since |
| 78 // |present_issuers_| will point to data owned by the certs. | 77 // |present_issuers_| will point to data owned by the certs. |
| 79 ParsedCertificateList issuers_; | 78 ParsedCertificateList issuers_; |
| 80 // The index of the next cert in |issuers_| to return. | 79 // The index of the next cert in |issuers_| to return. |
| 81 size_t cur_ = 0; | 80 size_t cur_ = 0; |
| 82 // Set of DER-encoded values for the certs in |issuers_|. Used to prevent | 81 // Set of DER-encoded values for the certs in |issuers_|. Used to prevent |
| 83 // duplicates. This is based on the full DER of the cert to allow different | 82 // duplicates. This is based on the full DER of the cert to allow different |
| 84 // versions of the same certificate to be tried in different candidate paths. | 83 // versions of the same certificate to be tried in different candidate paths. |
| 85 // This points to data owned by |issuers_|. | 84 // This points to data owned by |issuers_|. |
| 86 std::unordered_set<base::StringPiece, base::StringPieceHash> present_issuers_; | 85 std::unordered_set<base::StringPiece, base::StringPieceHash> present_issuers_; |
| 87 | 86 |
| 88 // Tracks whether asynchronous requests have been made yet. | 87 // Tracks whether asynchronous requests have been made yet. |
| 89 bool did_async_query_ = false; | 88 bool did_async_query_ = false; |
| 90 // If asynchronous requests were made, how many of them are still outstanding? | 89 // Owns the Request object for any asynchronous request so that it will be |
| 91 size_t pending_async_results_; | |
| 92 // Owns the Request objects for any asynchronous requests so that they will be | |
| 93 // cancelled if CertIssuersIter is destroyed. | 90 // cancelled if CertIssuersIter is destroyed. |
| 94 std::vector<std::unique_ptr<CertIssuerSource::Request>> | 91 std::unique_ptr<CertIssuerSource::Request> pending_async_request_; |
| 95 pending_async_requests_; | |
| 96 | 92 |
| 97 // When GetNextIssuer was called and returned asynchronously, |*out_cert_| is | 93 // When GetNextIssuer was called and returned asynchronously, |*out_cert_| is |
| 98 // where the result will be stored, and |callback_| will be run when the | 94 // where the result will be stored, and |callback_| will be run when the |
| 99 // result is ready. | 95 // result is ready. |
| 100 scoped_refptr<ParsedCertificate>* out_cert_; | 96 scoped_refptr<ParsedCertificate>* out_cert_; |
| 101 base::Closure callback_; | 97 base::Closure callback_; |
| 102 | 98 |
| 103 DISALLOW_COPY_AND_ASSIGN(CertIssuersIter); | 99 DISALLOW_COPY_AND_ASSIGN(CertIssuersIter); |
| 104 }; | 100 }; |
| 105 | 101 |
| 106 CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert, | 102 CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert, |
| 107 CertIssuerSources* cert_issuer_sources, | 103 CertIssuerSource* cert_issuer_source) |
| 108 const TrustStore& trust_store) | 104 : cert_(in_cert), cert_issuer_source_(cert_issuer_source) { |
| 109 : cert_(in_cert), cert_issuer_sources_(cert_issuer_sources) { | |
| 110 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) << ") created"; | 105 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) << ") created"; |
| 111 trust_store.FindTrustAnchorsByNormalizedName(in_cert->normalized_issuer(), | 106 ParsedCertificateList new_issuers; |
| 112 &issuers_); | 107 cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers); |
| 113 // Insert matching roots into |present_issuers_| in case they also are | 108 for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) { |
| 114 // returned by a CertIssuerSource. It is assumed | 109 if (present_issuers_.find(issuer->der_cert().AsStringPiece()) != |
| 115 // FindTrustAnchorsByNormalizedName does not itself return dupes. | 110 present_issuers_.end()) |
| 116 for (const auto& root : issuers_) | 111 continue; |
| 117 present_issuers_.insert(root->der_cert().AsStringPiece()); | 112 present_issuers_.insert(issuer->der_cert().AsStringPiece()); |
| 118 | 113 issuers_.push_back(std::move(issuer)); |
| 119 for (auto* cert_issuer_source : *cert_issuer_sources_) { | |
| 120 ParsedCertificateList new_issuers; | |
| 121 cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers); | |
| 122 for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) { | |
| 123 if (present_issuers_.find(issuer->der_cert().AsStringPiece()) != | |
| 124 present_issuers_.end()) | |
| 125 continue; | |
| 126 present_issuers_.insert(issuer->der_cert().AsStringPiece()); | |
| 127 issuers_.push_back(std::move(issuer)); | |
| 128 } | |
| 129 } | 114 } |
| 130 // TODO(mattm): sort by notbefore, etc (eg if cert issuer matches a trust | 115 // TODO(mattm): sort by notbefore, etc (eg if cert issuer matches a trust |
| 131 // anchor subject (or is a trust anchor), that should be sorted higher too. | 116 // anchor subject (or is a trust anchor), that should be sorted higher too. |
| 132 // See big list of possible sorting hints in RFC 4158.) | 117 // See big list of possible sorting hints in RFC 4158.) |
| 133 // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that | 118 // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that |
| 134 // is done) | 119 // is done) |
| 135 } | 120 } |
| 136 | 121 |
| 137 CompletionStatus CertIssuersIter::GetNextIssuer( | 122 CompletionStatus CertIssuersIter::GetNextIssuer( |
| 138 scoped_refptr<ParsedCertificate>* out_cert, | 123 scoped_refptr<ParsedCertificate>* out_cert, |
| 139 const base::Closure& callback) { | 124 const base::Closure& callback) { |
| 140 // Should not be called again while already waiting for an async result. | 125 // Should not be called again while already waiting for an async result. |
| 141 DCHECK(callback_.is_null()); | 126 DCHECK(callback_.is_null()); |
| 142 | 127 |
| 143 if (cur_ < issuers_.size()) { | 128 if (cur_ < issuers_.size()) { |
| 144 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 129 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 145 << "): returning item " << cur_ << " of " << issuers_.size(); | 130 << "): returning item " << cur_ << " of " << issuers_.size(); |
| 146 // Still have issuers that haven't been returned yet, return one of them. | 131 // Still have issuers that haven't been returned yet, return one of them. |
| 147 // A reference to the returned issuer is retained, since |present_issuers_| | 132 // A reference to the returned issuer is retained, since |present_issuers_| |
| 148 // points to data owned by it. | 133 // points to data owned by it. |
| 149 *out_cert = issuers_[cur_++]; | 134 *out_cert = issuers_[cur_++]; |
| 150 return CompletionStatus::SYNC; | 135 return CompletionStatus::SYNC; |
| 151 } | 136 } |
| 152 if (did_async_query_) { | 137 if (did_async_query_) { |
| 153 if (pending_async_results_ == 0) { | 138 if (!pending_async_request_) { |
| 154 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 139 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 155 << ") Reached the end of all available issuers."; | 140 << ") Reached the end of all available issuers."; |
| 156 // Reached the end of all available issuers. | 141 // Reached the end of all available issuers. |
| 157 *out_cert = nullptr; | 142 *out_cert = nullptr; |
| 158 return CompletionStatus::SYNC; | 143 return CompletionStatus::SYNC; |
| 159 } | 144 } |
| 160 | 145 |
| 161 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 146 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 162 << ") Still waiting for async results from other " | 147 << ") Still waiting for more async results."; |
| 163 "CertIssuerSources."; | |
| 164 // Still waiting for async results from other CertIssuerSources. | 148 // Still waiting for async results from other CertIssuerSources. |
| 165 out_cert_ = out_cert; | 149 out_cert_ = out_cert; |
| 166 callback_ = callback; | 150 callback_ = callback; |
| 167 return CompletionStatus::ASYNC; | 151 return CompletionStatus::ASYNC; |
| 168 } | 152 } |
| 169 // Reached the end of synchronously gathered issuers. | 153 // Reached the end of synchronously gathered issuers. |
| 170 | 154 |
| 171 if (callback.is_null()) { | 155 if (callback.is_null()) { |
| 172 // Synchronous-only mode, don't try to query async sources. | 156 // Synchronous-only mode, don't try to query async sources. |
| 173 *out_cert = nullptr; | 157 *out_cert = nullptr; |
| 174 return CompletionStatus::SYNC; | 158 return CompletionStatus::SYNC; |
| 175 } | 159 } |
| 176 | 160 |
| 177 // Now issue request(s) for async ones (AIA, etc). | 161 // Now issue request(s) for async ones (AIA, etc). |
| 178 did_async_query_ = true; | 162 did_async_query_ = true; |
| 179 pending_async_results_ = 0; | 163 std::unique_ptr<CertIssuerSource::Request> request; |
| 180 for (auto* cert_issuer_source : *cert_issuer_sources_) { | 164 cert_issuer_source_->AsyncGetIssuersOf( |
| 181 std::unique_ptr<CertIssuerSource::Request> request; | 165 reference_cert(), |
| 182 cert_issuer_source->AsyncGetIssuersOf( | 166 base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)), |
| 183 cert(), | 167 &request); |
| 184 base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)), | 168 if (request) { |
| 185 &request); | 169 DVLOG(1) << "AsyncGetIssuersOf(" << CertDebugString(cert()) |
| 186 if (request) { | 170 << ") pending..."; |
| 187 DVLOG(1) << "AsyncGetIssuersOf(" << CertDebugString(cert()) | 171 pending_async_request_ = std::move(request); |
| 188 << ") pending..."; | |
| 189 pending_async_results_++; | |
| 190 pending_async_requests_.push_back(std::move(request)); | |
| 191 } | |
| 192 } | 172 } |
| 193 | 173 |
| 194 if (pending_async_results_ == 0) { | 174 if (!pending_async_request_) { |
| 195 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 175 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 196 << ") No cert sources have async results."; | 176 << ") No cert sources have async results."; |
| 197 // No cert sources have async results. | 177 // No cert sources have async results. |
| 198 *out_cert = nullptr; | 178 *out_cert = nullptr; |
| 199 return CompletionStatus::SYNC; | 179 return CompletionStatus::SYNC; |
| 200 } | 180 } |
| 201 | 181 |
| 202 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | |
| 203 << ") issued AsyncGetIssuersOf call(s) (n=" << pending_async_results_ | |
| 204 << ")"; | |
| 205 out_cert_ = out_cert; | 182 out_cert_ = out_cert; |
| 206 callback_ = callback; | 183 callback_ = callback; |
| 207 return CompletionStatus::ASYNC; | 184 return CompletionStatus::ASYNC; |
| 208 } | 185 } |
| 209 | 186 |
| 210 void CertIssuersIter::GotAsyncCerts(CertIssuerSource::Request* request) { | 187 void CertIssuersIter::GotAsyncCerts(CertIssuerSource::Request* request) { |
| 211 DVLOG(1) << "CertIssuersIter::GotAsyncCerts(" << CertDebugString(cert()) | 188 DVLOG(1) << "CertIssuersIter::GotAsyncCerts(" << CertDebugString(cert()) |
| 212 << ")"; | 189 << ")"; |
| 190 DCHECK_EQ(pending_async_request_.get(), request); |
| 213 while (true) { | 191 while (true) { |
| 214 scoped_refptr<ParsedCertificate> cert; | 192 scoped_refptr<ParsedCertificate> cert; |
| 215 CompletionStatus status = request->GetNext(&cert); | 193 CompletionStatus status = request->GetNext(&cert); |
| 216 if (!cert) { | 194 if (!cert) { |
| 217 if (status == CompletionStatus::SYNC) { | 195 if (status == CompletionStatus::SYNC) { |
| 218 // Request is exhausted, no more results pending from that | 196 // Request is exhausted, no more results pending from the |
| 219 // CertIssuerSource. | 197 // CertIssuerSource. |
| 220 DCHECK_GT(pending_async_results_, 0U); | 198 pending_async_request_.reset(); |
| 221 pending_async_results_--; | |
| 222 } | 199 } |
| 223 break; | 200 break; |
| 224 } | 201 } |
| 225 DCHECK_EQ(status, CompletionStatus::SYNC); | 202 DCHECK_EQ(status, CompletionStatus::SYNC); |
| 226 if (present_issuers_.find(cert->der_cert().AsStringPiece()) != | 203 if (present_issuers_.find(cert->der_cert().AsStringPiece()) != |
| 227 present_issuers_.end()) | 204 present_issuers_.end()) |
| 228 continue; | 205 continue; |
| 229 present_issuers_.insert(cert->der_cert().AsStringPiece()); | 206 present_issuers_.insert(cert->der_cert().AsStringPiece()); |
| 230 issuers_.push_back(std::move(cert)); | 207 issuers_.push_back(std::move(cert)); |
| 231 } | 208 } |
| 232 | 209 |
| 233 // TODO(mattm): re-sort remaining elements of issuers_ (remaining elements may | 210 // TODO(mattm): re-sort remaining elements of issuers_ (remaining elements may |
| 234 // be more than the ones just inserted, depending on |cur_| value). | 211 // be more than the ones just inserted, depending on |cur_| value). |
| 235 | 212 |
| 236 // Notify that more results are available, if necessary. | 213 // Notify that more results are available, if necessary. |
| 237 if (!callback_.is_null()) { | 214 if (!callback_.is_null()) { |
| 238 if (cur_ < issuers_.size()) { | 215 if (cur_ < issuers_.size()) { |
| 239 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 216 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 240 << "): async returning item " << cur_ << " of " | 217 << "): async returning item " << cur_ << " of " |
| 241 << issuers_.size(); | 218 << issuers_.size(); |
| 242 *out_cert_ = std::move(issuers_[cur_++]); | 219 *out_cert_ = std::move(issuers_[cur_++]); |
| 243 base::ResetAndReturn(&callback_).Run(); | 220 base::ResetAndReturn(&callback_).Run(); |
| 244 } else if (pending_async_results_ == 0) { | 221 } else if (!pending_async_request_) { |
| 245 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 222 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 246 << "): async returning empty result"; | 223 << "): async returning empty result"; |
| 247 *out_cert_ = nullptr; | 224 *out_cert_ = nullptr; |
| 248 base::ResetAndReturn(&callback_).Run(); | 225 base::ResetAndReturn(&callback_).Run(); |
| 249 } else { | 226 } else { |
| 250 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) | 227 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 251 << "): empty result, but other async results " | 228 << "): empty result, but other async results " |
| 252 "pending, waiting.."; | 229 "pending, waiting.."; |
| 253 } | 230 } |
| 254 } | 231 } |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 326 std::set<Key> present_certs_; | 303 std::set<Key> present_certs_; |
| 327 }; | 304 }; |
| 328 | 305 |
| 329 } // namespace | 306 } // namespace |
| 330 | 307 |
| 331 // CertPathIter generates possible paths from |cert| to a trust anchor in | 308 // CertPathIter generates possible paths from |cert| to a trust anchor in |
| 332 // |trust_store|, using intermediates from the |cert_issuer_source| objects if | 309 // |trust_store|, using intermediates from the |cert_issuer_source| objects if |
| 333 // necessary. | 310 // necessary. |
| 334 class CertPathIter { | 311 class CertPathIter { |
| 335 public: | 312 public: |
| 336 CertPathIter(scoped_refptr<ParsedCertificate> cert, | 313 explicit CertPathIter(scoped_refptr<ParsedCertificate> cert); |
| 337 const TrustStore* trust_store); | 314 |
| 315 // Adds a TrustStore to check if certificates are trust anchors during path |
| 316 // building. The |*trust_store| must remain valid for the lifetime of the |
| 317 // CertPathIter. |
| 318 void AddTrustStore(TrustStore* trust_store); |
| 338 | 319 |
| 339 // Adds a CertIssuerSource to provide intermediates for use in path building. | 320 // Adds a CertIssuerSource to provide intermediates for use in path building. |
| 340 // The |*cert_issuer_source| must remain valid for the lifetime of the | 321 // The |*cert_issuer_source| must remain valid for the lifetime of the |
| 341 // CertPathIter. | 322 // CertPathIter. |
| 342 void AddCertIssuerSource(CertIssuerSource* cert_issuer_source); | 323 void AddCertIssuerSource(CertIssuerSource* cert_issuer_source); |
| 343 | 324 |
| 344 // Gets the next candidate path. If a path is ready synchronously, SYNC is | 325 // Gets the next candidate path. If a path is ready synchronously, SYNC is |
| 345 // returned and the path is stored in |*path|. If a path is not ready, | 326 // returned and the path is stored in |*path|. If a path is not ready, |
| 346 // ASYNC is returned and |callback| will be called once |*path| has been set. | 327 // ASYNC is returned and |callback| will be called once |*path| has been set. |
| 347 // In either case, if all paths have been exhausted, |*path| is cleared. | 328 // In either case, if all paths have been exhausted, |*path| is cleared. |
| 348 CompletionStatus GetNextPath(ParsedCertificateList* path, | 329 CompletionStatus GetNextPath(ParsedCertificateList* path, |
| 349 const base::Closure& callback); | 330 const base::Closure& callback); |
| 350 | 331 |
| 351 private: | 332 private: |
| 352 enum State { | 333 enum State { |
| 353 STATE_NONE, | 334 STATE_NONE, |
| 354 STATE_GET_NEXT_ISSUER, | 335 STATE_GET_NEXT_ISSUER, |
| 355 STATE_GET_NEXT_ISSUER_COMPLETE, | 336 STATE_GET_NEXT_ISSUER_COMPLETE, |
| 337 STATE_IS_NEXT_CERT_TRUSTED_COMPLETE, |
| 356 STATE_RETURN_A_PATH, | 338 STATE_RETURN_A_PATH, |
| 357 STATE_BACKTRACK, | 339 STATE_BACKTRACK, |
| 358 }; | 340 }; |
| 359 | 341 |
| 360 CompletionStatus DoLoop(bool allow_async); | 342 CompletionStatus DoLoop(bool allow_async); |
| 361 | 343 |
| 362 CompletionStatus DoGetNextIssuer(bool allow_async); | 344 CompletionStatus DoGetNextIssuer(bool allow_async); |
| 363 CompletionStatus DoGetNextIssuerComplete(); | 345 CompletionStatus DoGetNextIssuerComplete(bool allow_async); |
| 346 CompletionStatus DoIsNextCertTrustedComplete(); |
| 364 CompletionStatus DoBackTrack(); | 347 CompletionStatus DoBackTrack(); |
| 365 | 348 |
| 366 void HandleGotNextIssuer(void); | 349 void HandleGotNextIssuer(void); |
| 350 void HandleIsTrustedCertificate(bool is_trusted); |
| 367 | 351 |
| 368 // Stores the next candidate issuer certificate, until it is used during the | 352 // Stores the next candidate issuer certificate, until it is used during the |
| 369 // STATE_GET_NEXT_ISSUER_COMPLETE step. | 353 // STATE_GET_NEXT_ISSUER_COMPLETE step. |
| 370 scoped_refptr<ParsedCertificate> next_cert_; | 354 scoped_refptr<ParsedCertificate> next_cert_; |
| 371 // The current path being explored, made up of CertIssuerIters. Each node | 355 // The current path being explored, made up of CertIssuerIters. Each node |
| 372 // keeps track of the state of searching for issuers of that cert, so that | 356 // keeps track of the state of searching for issuers of that cert, so that |
| 373 // when backtracking it can resume the search where it left off. | 357 // when backtracking it can resume the search where it left off. |
| 374 CertIssuerIterPath cur_path_; | 358 CertIssuerIterPath cur_path_; |
| 375 // The CertIssuerSources for retrieving candidate issuers. | 359 // The CertIssuerSources for retrieving candidate issuers. |
| 376 CertIssuerSources cert_issuer_sources_; | 360 CertIssuerSourceCollection cert_issuer_source_collection_; |
| 377 // The TrustStore for checking if a path ends in a trust anchor. | 361 // The TrustStores for checking if a path ends in a trust anchor. |
| 378 const TrustStore* trust_store_; | 362 TrustStoreCollection trust_store_collection_; |
| 363 // Request handle for querying the TrustStoreCollection. |
| 364 std::unique_ptr<TrustStore::Request> trust_request_; |
| 365 // Stores the result of trust query for use in the |
| 366 // STATE_IS_NEXT_CERT_TRUSTED_COMPLETE state. |
| 367 bool is_pending_cert_trusted_; |
| 379 // The output variable for storing the next candidate path, which the client | 368 // The output variable for storing the next candidate path, which the client |
| 380 // passes in to GetNextPath. Only used for a single path output. | 369 // passes in to GetNextPath. Only used for a single path output. |
| 381 ParsedCertificateList* out_path_; | 370 ParsedCertificateList* out_path_; |
| 382 // The callback to be called if an async lookup generated a candidate path. | 371 // The callback to be called if an async lookup generated a candidate path. |
| 383 base::Closure callback_; | 372 base::Closure callback_; |
| 384 // Current state of the state machine. | 373 // Current state of the state machine. |
| 385 State next_state_; | 374 State next_state_; |
| 386 | 375 |
| 387 DISALLOW_COPY_AND_ASSIGN(CertPathIter); | 376 DISALLOW_COPY_AND_ASSIGN(CertPathIter); |
| 388 }; | 377 }; |
| 389 | 378 |
| 390 CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert, | 379 CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert) |
| 391 const TrustStore* trust_store) | |
| 392 : next_cert_(std::move(cert)), | 380 : next_cert_(std::move(cert)), |
| 393 trust_store_(trust_store), | |
| 394 next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {} | 381 next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {} |
| 395 | 382 |
| 383 void CertPathIter::AddTrustStore(TrustStore* trust_store) { |
| 384 trust_store_collection_.AddStore(trust_store); |
| 385 cert_issuer_source_collection_.AddSource(trust_store); |
| 386 } |
| 387 |
| 396 void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) { | 388 void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) { |
| 397 cert_issuer_sources_.push_back(cert_issuer_source); | 389 cert_issuer_source_collection_.AddSource(cert_issuer_source); |
| 398 } | 390 } |
| 399 | 391 |
| 400 CompletionStatus CertPathIter::GetNextPath(ParsedCertificateList* path, | 392 CompletionStatus CertPathIter::GetNextPath(ParsedCertificateList* path, |
| 401 const base::Closure& callback) { | 393 const base::Closure& callback) { |
| 402 out_path_ = path; | 394 out_path_ = path; |
| 403 out_path_->clear(); | 395 out_path_->clear(); |
| 404 CompletionStatus rv = DoLoop(!callback.is_null()); | 396 CompletionStatus rv = DoLoop(!callback.is_null()); |
| 405 if (rv == CompletionStatus::ASYNC) { | 397 if (rv == CompletionStatus::ASYNC) { |
| 406 callback_ = callback; | 398 callback_ = callback; |
| 407 } else { | 399 } else { |
| 408 // Clear the reference to the output parameter as a precaution. | 400 // Clear the reference to the output parameter as a precaution. |
| 409 out_path_ = nullptr; | 401 out_path_ = nullptr; |
| 410 } | 402 } |
| 411 return rv; | 403 return rv; |
| 412 } | 404 } |
| 413 | 405 |
| 414 CompletionStatus CertPathIter::DoLoop(bool allow_async) { | 406 CompletionStatus CertPathIter::DoLoop(bool allow_async) { |
| 415 CompletionStatus result = CompletionStatus::SYNC; | 407 CompletionStatus result = CompletionStatus::SYNC; |
| 416 do { | 408 do { |
| 417 State state = next_state_; | 409 State state = next_state_; |
| 418 next_state_ = STATE_NONE; | 410 next_state_ = STATE_NONE; |
| 419 switch (state) { | 411 switch (state) { |
| 420 case STATE_NONE: | 412 case STATE_NONE: |
| 421 NOTREACHED(); | 413 NOTREACHED(); |
| 422 break; | 414 break; |
| 423 case STATE_GET_NEXT_ISSUER: | 415 case STATE_GET_NEXT_ISSUER: |
| 424 result = DoGetNextIssuer(allow_async); | 416 result = DoGetNextIssuer(allow_async); |
| 425 break; | 417 break; |
| 426 case STATE_GET_NEXT_ISSUER_COMPLETE: | 418 case STATE_GET_NEXT_ISSUER_COMPLETE: |
| 427 result = DoGetNextIssuerComplete(); | 419 result = DoGetNextIssuerComplete(allow_async); |
| 420 break; |
| 421 case STATE_IS_NEXT_CERT_TRUSTED_COMPLETE: |
| 422 result = DoIsNextCertTrustedComplete(); |
| 428 break; | 423 break; |
| 429 case STATE_RETURN_A_PATH: | 424 case STATE_RETURN_A_PATH: |
| 430 // If the returned path did not verify, keep looking for other paths | 425 // If the returned path did not verify, keep looking for other paths |
| 431 // (the trust root is not part of cur_path_, so don't need to | 426 // (the trust root is not part of cur_path_, so don't need to |
| 432 // backtrack). | 427 // backtrack). |
| 433 next_state_ = STATE_GET_NEXT_ISSUER; | 428 next_state_ = STATE_GET_NEXT_ISSUER; |
| 434 result = CompletionStatus::SYNC; | 429 result = CompletionStatus::SYNC; |
| 435 break; | 430 break; |
| 436 case STATE_BACKTRACK: | 431 case STATE_BACKTRACK: |
| 437 result = DoBackTrack(); | 432 result = DoBackTrack(); |
| 438 break; | 433 break; |
| 439 } | 434 } |
| 440 } while (result == CompletionStatus::SYNC && next_state_ != STATE_NONE && | 435 } while (result == CompletionStatus::SYNC && next_state_ != STATE_NONE && |
| 441 next_state_ != STATE_RETURN_A_PATH); | 436 next_state_ != STATE_RETURN_A_PATH); |
| 442 | 437 |
| 443 return result; | 438 return result; |
| 444 } | 439 } |
| 445 | 440 |
| 446 CompletionStatus CertPathIter::DoGetNextIssuer(bool allow_async) { | 441 CompletionStatus CertPathIter::DoGetNextIssuer(bool allow_async) { |
| 442 if (cur_path_.Empty()) { |
| 443 // If the target cert matched a trust root, but did not verify |
| 444 // successfully, cur_path_ will be empty. |
| 445 next_state_ = STATE_NONE; |
| 446 return CompletionStatus::SYNC; |
| 447 } |
| 447 next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE; | 448 next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE; |
| 448 CompletionStatus rv = cur_path_.back()->GetNextIssuer( | 449 CompletionStatus rv = cur_path_.back()->GetNextIssuer( |
| 449 &next_cert_, allow_async ? base::Bind(&CertPathIter::HandleGotNextIssuer, | 450 &next_cert_, allow_async ? base::Bind(&CertPathIter::HandleGotNextIssuer, |
| 450 base::Unretained(this)) | 451 base::Unretained(this)) |
| 451 : base::Closure()); | 452 : base::Closure()); |
| 452 return rv; | 453 return rv; |
| 453 } | 454 } |
| 454 | 455 |
| 455 CompletionStatus CertPathIter::DoGetNextIssuerComplete() { | 456 CompletionStatus CertPathIter::DoGetNextIssuerComplete(bool allow_async) { |
| 456 if (next_cert_) { | 457 if (next_cert_) { |
| 457 // Skip this cert if it is already in the chain. | 458 // Skip this cert if it is already in the chain. |
| 458 if (cur_path_.IsPresent(next_cert_.get())) { | 459 if (cur_path_.IsPresent(next_cert_.get())) { |
| 459 next_state_ = STATE_GET_NEXT_ISSUER; | 460 next_state_ = STATE_GET_NEXT_ISSUER; |
| 460 return CompletionStatus::SYNC; | 461 return CompletionStatus::SYNC; |
| 461 } | 462 } |
| 462 // If the cert matches a trust root, this is a (possibly) complete path. | 463 next_state_ = STATE_IS_NEXT_CERT_TRUSTED_COMPLETE; |
| 463 // Signal readiness. Don't add it to cur_path_, since that would cause an | 464 trust_store_collection_.IsTrustedCertificate( |
| 464 // unnecessary lookup of issuers of the trust root. | 465 next_cert_.get(), |
| 465 if (trust_store_->IsTrustedCertificate(next_cert_.get())) { | 466 allow_async ? base::Bind(&CertPathIter::HandleIsTrustedCertificate, |
| 466 DVLOG(1) << "CertPathIter IsTrustedCertificate(" | 467 base::Unretained(this)) |
| 467 << CertDebugString(next_cert_.get()) << ") = true"; | 468 : TrustStore::TrustCallback(), |
| 468 next_state_ = STATE_RETURN_A_PATH; | 469 &is_pending_cert_trusted_, &trust_request_); |
| 469 cur_path_.CopyPath(out_path_); | 470 return trust_request_ ? CompletionStatus::ASYNC : CompletionStatus::SYNC; |
| 470 out_path_->push_back(std::move(next_cert_)); | |
| 471 next_cert_ = nullptr; | |
| 472 return CompletionStatus::SYNC; | |
| 473 } | |
| 474 | |
| 475 cur_path_.Append(base::WrapUnique(new CertIssuersIter( | |
| 476 std::move(next_cert_), &cert_issuer_sources_, *trust_store_))); | |
| 477 next_cert_ = nullptr; | |
| 478 DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString(); | |
| 479 // Continue descending the tree. | |
| 480 next_state_ = STATE_GET_NEXT_ISSUER; | |
| 481 } else { | 471 } else { |
| 482 // TODO(mattm): should also include such paths in CertPathBuilder::Result, | 472 // TODO(mattm): should also include such paths in CertPathBuilder::Result, |
| 483 // maybe with a flag to enable it. Or use a visitor pattern so the caller | 473 // maybe with a flag to enable it. Or use a visitor pattern so the caller |
| 484 // can decide what to do with any failed paths. | 474 // can decide what to do with any failed paths. |
| 485 // No more issuers for current chain, go back up and see if there are any | 475 // No more issuers for current chain, go back up and see if there are any |
| 486 // more for the previous cert. | 476 // more for the previous cert. |
| 487 next_state_ = STATE_BACKTRACK; | 477 next_state_ = STATE_BACKTRACK; |
| 488 } | 478 } |
| 489 return CompletionStatus::SYNC; | 479 return CompletionStatus::SYNC; |
| 490 } | 480 } |
| 491 | 481 |
| 482 CompletionStatus CertPathIter::DoIsNextCertTrustedComplete() { |
| 483 // If the cert matches a trust root, this is a (possibly) complete path. |
| 484 // Signal readiness. Don't add it to cur_path_, since that would cause an |
| 485 // unnecessary lookup of issuers of the trust root. |
| 486 if (is_pending_cert_trusted_) { |
| 487 DVLOG(1) << "CertPathIter IsTrustedCertificate(" |
| 488 << CertDebugString(next_cert_.get()) << ") = true"; |
| 489 next_state_ = STATE_RETURN_A_PATH; |
| 490 cur_path_.CopyPath(out_path_); |
| 491 out_path_->push_back(std::move(next_cert_)); |
| 492 next_cert_ = nullptr; |
| 493 return CompletionStatus::SYNC; |
| 494 } |
| 495 |
| 496 cur_path_.Append(base::WrapUnique(new CertIssuersIter( |
| 497 std::move(next_cert_), &cert_issuer_source_collection_))); |
| 498 next_cert_ = nullptr; |
| 499 DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString(); |
| 500 // Continue descending the tree. |
| 501 next_state_ = STATE_GET_NEXT_ISSUER; |
| 502 return CompletionStatus::SYNC; |
| 503 } |
| 504 |
| 492 CompletionStatus CertPathIter::DoBackTrack() { | 505 CompletionStatus CertPathIter::DoBackTrack() { |
| 493 DVLOG(1) << "CertPathIter backtracking..."; | 506 DVLOG(1) << "CertPathIter backtracking..."; |
| 494 cur_path_.Pop(); | 507 cur_path_.Pop(); |
| 495 if (cur_path_.Empty()) { | 508 if (cur_path_.Empty()) { |
| 496 // Exhausted all paths. | 509 // Exhausted all paths. |
| 497 next_state_ = STATE_NONE; | 510 next_state_ = STATE_NONE; |
| 498 } else { | 511 } else { |
| 499 // Continue exploring issuers of the previous path. | 512 // Continue exploring issuers of the previous path. |
| 500 next_state_ = STATE_GET_NEXT_ISSUER; | 513 next_state_ = STATE_GET_NEXT_ISSUER; |
| 501 } | 514 } |
| 502 return CompletionStatus::SYNC; | 515 return CompletionStatus::SYNC; |
| 503 } | 516 } |
| 504 | 517 |
| 505 void CertPathIter::HandleGotNextIssuer(void) { | 518 void CertPathIter::HandleGotNextIssuer(void) { |
| 506 DCHECK(!callback_.is_null()); | 519 DCHECK(!callback_.is_null()); |
| 507 CompletionStatus rv = DoLoop(true /* allow_async */); | 520 CompletionStatus rv = DoLoop(true /* allow_async */); |
| 508 if (rv == CompletionStatus::SYNC) { | 521 if (rv == CompletionStatus::SYNC) { |
| 509 // Clear the reference to the output parameter as a precaution. | 522 // Clear the reference to the output parameter as a precaution. |
| 510 out_path_ = nullptr; | 523 out_path_ = nullptr; |
| 511 base::ResetAndReturn(&callback_).Run(); | 524 base::ResetAndReturn(&callback_).Run(); |
| 512 } | 525 } |
| 513 } | 526 } |
| 514 | 527 |
| 528 void CertPathIter::HandleIsTrustedCertificate(bool is_trusted) { |
| 529 DCHECK(!callback_.is_null()); |
| 530 is_pending_cert_trusted_ = is_trusted; |
| 531 CompletionStatus rv = DoLoop(true /* allow_async */); |
| 532 if (rv == CompletionStatus::SYNC) |
| 533 base::ResetAndReturn(&callback_).Run(); |
| 534 } |
| 535 |
| 515 CertPathBuilder::ResultPath::ResultPath() = default; | 536 CertPathBuilder::ResultPath::ResultPath() = default; |
| 516 CertPathBuilder::ResultPath::~ResultPath() = default; | 537 CertPathBuilder::ResultPath::~ResultPath() = default; |
| 517 CertPathBuilder::Result::Result() = default; | 538 CertPathBuilder::Result::Result() = default; |
| 518 CertPathBuilder::Result::~Result() = default; | 539 CertPathBuilder::Result::~Result() = default; |
| 519 | 540 |
| 520 CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert, | 541 CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert, |
| 521 const TrustStore* trust_store, | |
| 522 const SignaturePolicy* signature_policy, | 542 const SignaturePolicy* signature_policy, |
| 523 const der::GeneralizedTime& time, | 543 const der::GeneralizedTime& time, |
| 524 Result* result) | 544 Result* result) |
| 525 : cert_path_iter_(new CertPathIter(std::move(cert), trust_store)), | 545 : cert_path_iter_(new CertPathIter(std::move(cert))), |
| 526 trust_store_(trust_store), | |
| 527 signature_policy_(signature_policy), | 546 signature_policy_(signature_policy), |
| 528 time_(time), | 547 time_(time), |
| 529 next_state_(STATE_NONE), | 548 next_state_(STATE_NONE), |
| 530 out_result_(result) {} | 549 out_result_(result) {} |
| 531 | 550 |
| 532 CertPathBuilder::~CertPathBuilder() {} | 551 CertPathBuilder::~CertPathBuilder() {} |
| 533 | 552 |
| 553 void CertPathBuilder::AddTrustStore(TrustStore* trust_store) { |
| 554 cert_path_iter_->AddTrustStore(trust_store); |
| 555 } |
| 556 |
| 534 void CertPathBuilder::AddCertIssuerSource( | 557 void CertPathBuilder::AddCertIssuerSource( |
| 535 CertIssuerSource* cert_issuer_source) { | 558 CertIssuerSource* cert_issuer_source) { |
| 536 cert_path_iter_->AddCertIssuerSource(cert_issuer_source); | 559 cert_path_iter_->AddCertIssuerSource(cert_issuer_source); |
| 537 } | 560 } |
| 538 | 561 |
| 539 CompletionStatus CertPathBuilder::Run(const base::Closure& callback) { | 562 CompletionStatus CertPathBuilder::Run(const base::Closure& callback) { |
| 540 DCHECK_EQ(STATE_NONE, next_state_); | 563 DCHECK_EQ(STATE_NONE, next_state_); |
| 541 next_state_ = STATE_GET_NEXT_PATH; | 564 next_state_ = STATE_GET_NEXT_PATH; |
| 542 CompletionStatus rv = DoLoop(!callback.is_null()); | 565 CompletionStatus rv = DoLoop(!callback.is_null()); |
| 543 | 566 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 586 } | 609 } |
| 587 | 610 |
| 588 CompletionStatus CertPathBuilder::DoGetNextPathComplete() { | 611 CompletionStatus CertPathBuilder::DoGetNextPathComplete() { |
| 589 if (next_path_.empty()) { | 612 if (next_path_.empty()) { |
| 590 // No more paths to check, signal completion. | 613 // No more paths to check, signal completion. |
| 591 next_state_ = STATE_NONE; | 614 next_state_ = STATE_NONE; |
| 592 return CompletionStatus::SYNC; | 615 return CompletionStatus::SYNC; |
| 593 } | 616 } |
| 594 | 617 |
| 595 bool verify_result = VerifyCertificateChainAssumingTrustedRoot( | 618 bool verify_result = VerifyCertificateChainAssumingTrustedRoot( |
| 596 next_path_, *trust_store_, signature_policy_, time_); | 619 next_path_, signature_policy_, time_); |
| 597 DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = " | 620 DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = " |
| 598 << verify_result; | 621 << verify_result; |
| 599 AddResultPath(next_path_, verify_result); | 622 AddResultPath(next_path_, verify_result); |
| 600 | 623 |
| 601 if (verify_result) { | 624 if (verify_result) { |
| 602 // Found a valid path, return immediately. | 625 // Found a valid path, return immediately. |
| 603 // TODO(mattm): add debug/test mode that tries all possible paths. | 626 // TODO(mattm): add debug/test mode that tries all possible paths. |
| 604 next_state_ = STATE_NONE; | 627 next_state_ = STATE_NONE; |
| 605 return CompletionStatus::SYNC; | 628 return CompletionStatus::SYNC; |
| 606 } | 629 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 619 result_path->error = is_success ? OK : ERR_CERT_AUTHORITY_INVALID; | 642 result_path->error = is_success ? OK : ERR_CERT_AUTHORITY_INVALID; |
| 620 // TODO(mattm): set best_result_index based on number or severity of errors. | 643 // TODO(mattm): set best_result_index based on number or severity of errors. |
| 621 if (result_path->error == OK) | 644 if (result_path->error == OK) |
| 622 out_result_->best_result_index = out_result_->paths.size(); | 645 out_result_->best_result_index = out_result_->paths.size(); |
| 623 // TODO(mattm): add flag to only return a single path or all attempted paths? | 646 // TODO(mattm): add flag to only return a single path or all attempted paths? |
| 624 result_path->path = path; | 647 result_path->path = path; |
| 625 out_result_->paths.push_back(std::move(result_path)); | 648 out_result_->paths.push_back(std::move(result_path)); |
| 626 } | 649 } |
| 627 | 650 |
| 628 } // namespace net | 651 } // namespace net |
| OLD | NEW |