From: Christos Tsantilas Date: Mon, 1 Aug 2016 11:11:47 +0000 (+0300) Subject: merge from trunk-r14768 X-Git-Tag: SQUID_4_0_13~5^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=212e5aee4a29820cbdb0cab6dc98ba4e8b4ba3c4;p=thirdparty%2Fsquid.git merge from trunk-r14768 --- 212e5aee4a29820cbdb0cab6dc98ba4e8b4ba3c4 diff --cc src/security/PeerConnector.cc index bc19bee365,b3332088ae..720ffa7f03 --- a/src/security/PeerConnector.cc +++ b/src/security/PeerConnector.cc @@@ -11,13 -11,13 +11,15 @@@ #include "squid.h" #include "acl/FilledChecklist.h" #include "comm/Loops.h" +#include "Downloader.h" #include "errorpage.h" #include "fde.h" +#include "http/Stream.h" #include "HttpRequest.h" #include "security/NegotiationHistory.h" + #include "security/PeerConnector.h" #include "SquidConfig.h" + #if USE_OPENSSL #include "ssl/bio.h" #include "ssl/cert_validate_message.h" #include "ssl/Config.h" @@@ -33,9 -33,9 +35,10 @@@ Security::PeerConnector::PeerConnector( callback(aCallback), negotiationTimeout(timeout), startTime(squid_curtime), - useCertValidator_(true) + useCertValidator_(true), + certsDownloads(0) { + debugs(83, 5, "Security::PeerConnector constructed, this=" << (void*)this); // if this throws, the caller's cb dialer is not our CbDialer Must(dynamic_cast(callback->getDialer())); } @@@ -361,31 -382,10 +385,32 @@@ Security::PeerConnector::handleNegotiat } void - Ssl::PeerConnector::noteWantRead() + Security::PeerConnector::noteWantRead() { - setReadTimeout(); const int fd = serverConnection()->fd; ++#if USE_OPENSSL + Security::SessionPtr ssl = fd_table[fd].ssl.get(); + BIO *b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); + if (srvBio->holdRead()) { + if (srvBio->gotHello()) { + if (checkForMissingCertificates()) + return; // Wait to download certificates before proceed. + + srvBio->holdRead(false); + // schedule a negotiateSSl to allow openSSL parse received data - Ssl::PeerConnector::NegotiateSsl(fd, this); ++ Security::PeerConnector::NegotiateSsl(fd, this); + return; + } else if (srvBio->gotHelloFailed()) { + srvBio->holdRead(false); + debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd); + // schedule a negotiateSSl to allow openSSL parse received data - Ssl::PeerConnector::NegotiateSsl(fd, this); ++ Security::PeerConnector::NegotiateSsl(fd, this); + return; + } + } - ++#endif + setReadTimeout(); Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0); } @@@ -513,104 -515,3 +540,105 @@@ Security::PeerConnector::status() cons return buf.content(); } ++#if USE_OPENSSL +/// CallDialer to allow use Downloader objects within PeerConnector class. +class PeerConnectorCertDownloaderDialer: public Downloader::CbDialer +{ +public: - typedef void (Ssl::PeerConnector::*Method)(SBuf &object, int status); ++ typedef void (Security::PeerConnector::*Method)(SBuf &object, int status); + - PeerConnectorCertDownloaderDialer(Method method, Ssl::PeerConnector *pc): ++ PeerConnectorCertDownloaderDialer(Method method, Security::PeerConnector *pc): + method_(method), + peerConnector_(pc) {} + + /* CallDialer API */ + virtual bool canDial(AsyncCall &call) { return peerConnector_.valid(); } + virtual void dial(AsyncCall &call) { ((&(*peerConnector_))->*method_)(object, status); } - Method method_; ///< The Ssl::PeerConnector method to dial - CbcPointer peerConnector_; ///< The Ssl::PeerConnector object ++ Method method_; ///< The Security::PeerConnector method to dial ++ CbcPointer peerConnector_; ///< The Security::PeerConnector object +}; + +void - Ssl::PeerConnector::startCertDownloading(SBuf &url) ++Security::PeerConnector::startCertDownloading(SBuf &url) +{ + AsyncCall::Pointer certCallback = asyncCall(81, 4, - "Ssl::PeerConnector::certDownloadingDone", - PeerConnectorCertDownloaderDialer(&Ssl::PeerConnector::certDownloadingDone, this)); ++ "Security::PeerConnector::certDownloadingDone", ++ PeerConnectorCertDownloaderDialer(&Security::PeerConnector::certDownloadingDone, this)); + + const Downloader *csd = dynamic_cast(request->downloader.valid()); + Downloader *dl = new Downloader(url, certCallback, csd ? csd->nestedLevel() + 1 : 1); + AsyncJob::Start(dl); +} + +void - Ssl::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus) ++Security::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus) +{ + ++certsDownloads; + debugs(81, 5, "Certificate downloading status: " << downloadStatus << " certificate size: " << obj.length()); + + // get ServerBio from SSL object + const int fd = serverConnection()->fd; + Security::SessionPtr ssl = fd_table[fd].ssl.get(); + BIO *b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); + + // Parse Certificate. Assume that it is in DER format. + // According to RFC 4325: + // The server must provide a DER encoded certificate or a collection + // collection of certificates in a "certs-only" CMS message. + // The applications MUST accept DER encoded certificates and SHOULD + // be able to accept collection of certificates. + // TODO: support collection of certificates + const unsigned char *raw = (const unsigned char*)obj.rawContent(); + if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) { + char buffer[1024]; + debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024)); + const Security::CertList &certsList = srvBio->serverCertificatesIfAny(); + if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert, certsList)) { + urlsOfMissingCerts.push(SBuf(issuerUri)); + } + Ssl::SSL_add_untrusted_cert(ssl, cert); + } + + // Check if there are URIs to download from and if yes start downloading + // the first in queue. + if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) { + startCertDownloading(urlsOfMissingCerts.front()); + urlsOfMissingCerts.pop(); + return; + } + + srvBio->holdRead(false); - Ssl::PeerConnector::NegotiateSsl(serverConnection()->fd, this); ++ Security::PeerConnector::NegotiateSsl(serverConnection()->fd, this); +} + +bool - Ssl::PeerConnector::checkForMissingCertificates() ++Security::PeerConnector::checkForMissingCertificates() +{ + // Check for nested SSL certificates downloads. For example when the + // certificate located in an SSL site which requires to download a + // a missing certificate (... from an SSL site which requires to ...). + + const Downloader *csd = request->downloader.get(); + if (csd && csd->nestedLevel() >= MaxNestedDownloads) + return false; + + const int fd = serverConnection()->fd; + Security::SessionPtr ssl = fd_table[fd].ssl.get(); + BIO *b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); + const Security::CertList &certs = srvBio->serverCertificatesIfAny(); + + if (certs.size()) { + debugs(83, 5, "SSL server sent " << certs.size() << " certificates"); + Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs); + if (urlsOfMissingCerts.size()) { + startCertDownloading(urlsOfMissingCerts.front()); + urlsOfMissingCerts.pop(); + return true; + } + } + + return false; +} - ++#endif //USE_OPENSSL diff --cc src/security/PeerConnector.h index 15351324a5,5d434398c8..fad47517cd --- a/src/security/PeerConnector.h +++ b/src/security/PeerConnector.h @@@ -14,13 -14,13 +14,14 @@@ #include "base/AsyncJob.h" #include "CommCalls.h" #include "security/EncryptorAnswer.h" + #include "security/forward.h" + #if USE_OPENSSL #include "ssl/support.h" + #endif #include +#include - #if USE_OPENSSL - class HttpRequest; class ErrorState; class AccessLogEntry; @@@ -125,18 -125,6 +126,20 @@@ protected /// Squid COMM_SELECT_READ handler. void noteWantRead(); ++#if USE_OPENSSL + /// Run the certificates list sent by the SSL server and check if there + /// are missing certificates. Adds to the urlOfMissingCerts list the + /// URLS of missing certificates if this information provided by the + /// issued certificates with Authority Info Access extension. + bool checkForMissingCertificates(); + + /// Start downloading procedure for the given URL. + void startCertDownloading(SBuf &url); + + /// Called by Downloader after a certificate object downloaded. + void certDownloadingDone(SBuf &object, int status); ++#endif + /// Called when the openSSL SSL_connect function needs to write data to /// the remote SSL server. Sets the Squid COMM_SELECT_WRITE handler. virtual void noteWantWrite(); @@@ -198,13 -182,9 +203,12 @@@ private time_t negotiationTimeout; ///< the SSL connection timeout to use time_t startTime; ///< when the peer connector negotiation started bool useCertValidator_; ///< whether the certificate validator should bypassed + /// The list of URLs where missing certificates should be downloaded. + std::queue urlsOfMissingCerts; + unsigned int certsDownloads; ///< the number of downloaded missing certificates }; - } // namespace Ssl + } // namespace Security - #endif /* USE_OPENSSL */ #endif /* SQUID_SRC_SSL_PEERCONNECTOR_H */