}
try {
- // selected bundled certificates in sending order: wireCerts = cert + chain
- CertList wireCerts(1, cert);
-
- while (const auto bundledCert = Ssl::ReadOptionalCertificate(bio)) {
- assert(!wireCerts.empty()); // this->cert is there (at least)
-
- // We cannot chain any certificate after a self-signed certificate. This
- // check also protects the IssuedBy() check below from adding duplicated
- // (i.e. listed multiple times in the bundle) self-signed certificates.
- if (SelfSigned(*wireCerts.back())) {
- debugs(83, DBG_PARSE_NOTE(2), "WARNING: Ignoring certificate after a self-signed one: " << *bundledCert);
- continue; // ... but keep going to report all ignored certificates
- }
-
- // add the bundled certificate if it extends the chain further
- if (IssuedBy(*wireCerts.back(), *bundledCert)) {
- debugs(83, DBG_PARSE_NOTE(3), "Adding issuer CA: " << *bundledCert);
- // OpenSSL API requires that we order certificates such that the
- // chain can be appended directly into the on-wire traffic.
- chain.emplace_back(bundledCert);
- wireCerts.emplace_back(bundledCert);
- continue;
- }
-
- debugs(83, DBG_PARSE_NOTE(2), "WARNING: Ignoring certificate that does not extend the chain: " << *bundledCert);
+ // Squid sends `cert` (loaded above) followed by certificates in `chain`
+ // (formed below by loading and sorting the remaining certificates).
+
+ // load all the remaining configured certificates
+ CertList candidates;
+ while (const auto c = Ssl::ReadOptionalCertificate(bio))
+ candidates.emplace_back(c);
+
+ // Push certificates into `chain` in on-the-wire order, as defined by
+ // RFC 8446 Section 4.4.2: "Each following certificate SHOULD directly
+ // certify the one immediately preceding it."
+ while (!candidates.empty()) {
+ const auto precedingCert = chain.empty() ? cert : chain.back();
+
+ // We cannot chain any certificate after a self-signed certificate.
+ // This check also protects the IssuedBy() search below from adding
+ // duplicated (i.e. listed multiple times) self-signed certificates.
+ if (SelfSigned(*precedingCert))
+ break;
+
+ const auto issuerPos = std::find_if(candidates.begin(), candidates.end(), [&](const CertPointer &i) {
+ return IssuedBy(*precedingCert, *i);
+ });
+ if (issuerPos == candidates.end())
+ break;
+
+ const auto &issuer = *issuerPos;
+ debugs(83, DBG_PARSE_NOTE(3), "Adding CA certificate: " << *issuer);
+ chain.emplace_back(issuer);
+ candidates.erase(issuerPos);
}
+
+ for (const auto &c: candidates)
+ debugs(83, DBG_IMPORTANT, "WARNING: Ignoring certificate that does not extend the chain: " << *c);
}
catch (...) {
// TODO: Reject configs with malformed intermediate certs instead.