From: Christos Tsantilas Date: Mon, 14 Dec 2015 19:09:24 +0000 (+0200) Subject: merge from trunk r14444 X-Git-Tag: SQUID_4_0_13~5^2~18 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d3b1bee6fff7792f482ff280e8400c6a3e83560f;p=thirdparty%2Fsquid.git merge from trunk r14444 --- d3b1bee6fff7792f482ff280e8400c6a3e83560f diff --cc src/Downloader.cc index 5ab4fe5abf,0000000000..34bdfc7522 mode 100644,000000..100644 --- a/src/Downloader.cc +++ b/src/Downloader.cc @@@ -1,221 -1,0 +1,222 @@@ +#include "squid.h" +#include "client_side.h" +#include "client_side_request.h" +#include "client_side_reply.h" +#include "Downloader.h" +#include "http/one/RequestParser.h" + +CBDATA_CLASS_INIT(Downloader); + +Downloader::Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level): + AsyncJob("Downloader"), + ConnStateData(xact), + url_(url), + callback(aCallback), + status(Http::scNone), + level_(level) +{ +} + +Downloader::~Downloader() +{ + debugs(33 , 2, "Downloader Finished"); +} + +void +Downloader::callException(const std::exception &e) +{ + debugs(33 , 2, "Downloader caught:" << e.what()); + AsyncJob::callException(e); +} + +bool +Downloader::doneAll() const +{ + return (!callback || callback->canceled()) && AsyncJob::doneAll(); +} + +void +Downloader::start() +{ + BodyProducer::start(); + HttpControlMsgSink::start(); + if (ClientSocketContext *context = parseOneRequest()) { + context->registerWithConn(); + processParsedRequest(context); + + /**/ + if (context->flags.deferred) { - if (context != context->http->getConn()->getCurrentContext().getRaw()) ++ if (context != context->http->getConn()->pipeline.front().getRaw()) + context->deferRecipientForLater(context->deferredparams.node, context->deferredparams.rep, context->deferredparams.queuedBuffer); + else + context->http->getConn()->handleReply(context->deferredparams.rep, context->deferredparams.queuedBuffer); + } + /**/ + + } + +} + +void +Downloader::noteMoreBodySpaceAvailable(BodyPipe::Pointer) +{ + // This method required only if we need to support uploading data to server + // Currently only GET requests are supported + assert(0); +} + +void +Downloader::noteBodyConsumerAborted(BodyPipe::Pointer) +{ + // This method required only if we need to support uploading data to server + // Currently only GET requests are supported + assert(0); +} + +ClientSocketContext * +Downloader::parseOneRequest() +{ + const HttpRequestMethod method = Http::METHOD_GET; + + char *uri = strdup(url_.c_str()); + HttpRequest *const request = HttpRequest::CreateFromUrlAndMethod(uri, method); + if (!request) { + debugs(33, 5, "Invalid FTP URL: " << uri); + safe_free(uri); + return NULL; //earlyError(...) + } + request->http_ver = Http::ProtocolVersion(); + request->header.putStr(Http::HdrType::HOST, request->url.host()); + request->header.putTime(Http::HdrType::DATE, squid_curtime); + + ClientHttpRequest *const http = new ClientHttpRequest(this); + http->request = request; + HTTPMSGLOCK(http->request); + http->req_sz = 0; + http->uri = uri; + + ClientSocketContext *const context = new ClientSocketContext(NULL, http); + StoreIOBuffer tempBuffer; + tempBuffer.data = context->reqbuf; + tempBuffer.length = HTTP_REQBUF_SZ; + + ClientStreamData newServer = new clientReplyContext(http); + ClientStreamData newClient = context; + clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach, + clientReplyStatus, newServer, clientSocketRecipient, + clientSocketDetach, newClient, tempBuffer); + + context->flags.parsed_ok = 1; + return context; +} + +void +Downloader::processParsedRequest(ClientSocketContext *context) +{ + Must(context != NULL); - Must(getConcurrentRequestCount() == 1); ++ Must(pipeline.nrequests == 1); + + ClientHttpRequest *const http = context->http; + assert(http != NULL); + + debugs(33, 4, "forwarding request to server side"); + assert(http->storeEntry() == NULL); + clientProcessRequest(this, Http1::RequestParserPointer(), context); +} + +time_t +Downloader::idleTimeout() const +{ + // No need to be implemented for connection-less ConnStateData object. + assert(0); + return 0; +} + +void - Downloader::writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call) ++Downloader::writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) +{ +} + +void +Downloader::handleReply(HttpReply *reply, StoreIOBuffer receivedData) +{ ++ ClientSocketContext::Pointer context = pipeline.front(); + bool existingContent = reply ? reply->content_length : 0; - bool exceedSize = (getCurrentContext()->startOfOutput() && existingContent > -1 && (size_t)existingContent > MaxObjectSize) || ++ bool exceedSize = (context->startOfOutput() && existingContent > -1 && (size_t)existingContent > MaxObjectSize) || + ((object.length() + receivedData.length) > MaxObjectSize); + + if (exceedSize) { + status = Http::scInternalServerError; + callBack(); + return; + } + + debugs(33, 4, "Received " << receivedData.length << + " object data, offset: " << receivedData.offset << + " error flag:" << receivedData.flags.error); + + if (receivedData.length > 0) { + object.append(receivedData.data, receivedData.length); - getCurrentContext()->http->out.size += receivedData.length; - getCurrentContext()->noteSentBodyBytes(receivedData.length); ++ context->http->out.size += receivedData.length; ++ context->noteSentBodyBytes(receivedData.length); + } + - switch (getCurrentContext()->socketState()) { ++ switch (context->socketState()) { + case STREAM_NONE: + debugs(33, 3, "Get more data"); - getCurrentContext()->pullData(); ++ context->pullData(); + break; + case STREAM_COMPLETE: + debugs(33, 3, "Object data transfer successfully complete"); + status = Http::scOkay; + callBack(); + break; + case STREAM_UNPLANNED_COMPLETE: + debugs(33, 3, "Object data transfer failed: STREAM_UNPLANNED_COMPLETE"); + status = Http::scInternalServerError; + callBack(); + break; + case STREAM_FAILED: + debugs(33, 3, "Object data transfer failed: STREAM_FAILED"); + status = Http::scInternalServerError; + callBack(); + break; + default: + fatal("unreachable code"); + } +} + +void +Downloader::downloadFinished() +{ + debugs(33, 3, "fake call, to just delete the Downloader"); + + // Not really needed. Squid will delete this object because "doneAll" is true. + //deleteThis("completed"); +} + +void +Downloader::callBack() +{ + CbDialer *dialer = dynamic_cast(callback->getDialer()); + Must(dialer); + dialer->status = status; + if (status == Http::scOkay) + dialer->object = object; + ScheduleCallHere(callback); + callback = NULL; + // Calling deleteThis method here to finish Downloader + // may result to squid crash. + // This method called by handleReply method which maybe called + // by ClientHttpRequest::doCallouts. The doCallouts after this object deleted + // may operate on non valid objects. + // Schedule a fake call here just to force squid to delete this object + CallJobHere(33, 7, CbcPointer(this), Downloader, downloadFinished); +} + +bool +Downloader::isOpen() const +{ + return cbdataReferenceValid(this) && // XXX: checking "this" in a method + callback != NULL; +} diff --cc src/Downloader.h index f17580989e,0000000000..152d2d8e0b mode 100644,000000..100644 --- a/src/Downloader.h +++ b/src/Downloader.h @@@ -1,69 -1,0 +1,69 @@@ +#ifndef SQUID_DOWNLOADER_H +#define SQUID_DOWNLOADER_H + +#include "client_side.h" +#include "cbdata.h" + +class Downloader: public ConnStateData +{ + CBDATA_CLASS(Downloader); + // XXX CBDATA_CLASS expands to nonvirtual toCbdata, AsyncJob::toCbdata + // is pure virtual. breaks build on clang if override is used + +public: + + /// Callback data to use with Downloader callbacks; + class CbDialer { + public: + CbDialer(): status(Http::scNone) {} + virtual ~CbDialer() {} + SBuf object; + Http::StatusCode status; + }; + + explicit Downloader(SBuf &url, const MasterXaction::Pointer &xact, AsyncCall::Pointer &aCallback, unsigned int level = 0); + virtual ~Downloader(); + + /// Fake call used internally by Downloader. + void downloadFinished(); + + /// The nested level of Downloader object (downloads inside downloads) + unsigned int nestedLevel() const {return level_;} + + /* ConnStateData API */ + virtual bool isOpen() const; + + /* AsyncJob API */ + virtual void callException(const std::exception &e); + virtual bool doneAll() const; + + /*Bodypipe API*/ + virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer); + virtual void noteBodyConsumerAborted(BodyPipe::Pointer); + +protected: + /* ConnStateData API */ + virtual ClientSocketContext *parseOneRequest(); + virtual void processParsedRequest(ClientSocketContext *context); + virtual time_t idleTimeout() const; - virtual void writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call); ++ virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call); + virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData); + + /* AsyncJob API */ + virtual void start(); + +private: + /// Schedules for execution the "callback" with parameters the status + /// and object + void callBack(); + + static const size_t MaxObjectSize = 1*1024*1024; ///< The maximum allowed object size. + + SBuf url_; ///< The url to download + AsyncCall::Pointer callback; ///< callback to call when download finishes + Http::StatusCode status; ///< The download status code + SBuf object; //object data + unsigned int level_; ///< Holds the nested downloads level +}; + +#endif diff --cc src/client_side.cc index e21c2abb02,c377c0c98c..0e4612df09 --- a/src/client_side.cc +++ b/src/client_side.cc @@@ -810,10 -673,8 +673,9 @@@ ConnStateData::swanSong( debugs(33, 2, HERE << clientConnection); flags.readMore = false; DeregisterRunner(this); - clientdbEstablished(clientConnection->remote, -1); /* decrement */ + if (clientConnection != NULL) + clientdbEstablished(clientConnection->remote, -1); /* decrement */ - assert(areAllContextsForThisConnection()); - freeAllContexts(); + pipeline.terminateAll(0); unpinConnection(true); @@@ -3417,12 -3135,7 +3145,9 @@@ ConnStateData::ConnStateData(const Mast pinning.peer = NULL; // store the details required for creating more MasterXaction objects as new requests come in - clientConnection = xact->tcpClient; - port = xact->squidPort; - if (port != NULL) - transferProtocol = port->transport; // default to the *_port protocol= setting. may change later. - log_addr = xact->tcpClient->remote; + if (xact->tcpClient != NULL) + log_addr = xact->tcpClient->remote; ++ log_addr.applyMask(Config.Addrs.client_netmask); // register to receive notice of Squid signal events diff --cc src/client_side.h index 78e4fae6ef,5cfd05da97..d264c20599 --- a/src/client_side.h +++ b/src/client_side.h @@@ -182,35 -201,13 +201,13 @@@ public /// Traffic parsing bool clientParseRequests(); void readNextRequest(); - ClientSocketContext::Pointer getCurrentContext() const; - void addContextToQueue(ClientSocketContext * context); - int getConcurrentRequestCount() const; - virtual bool isOpen() const; - - /// Update flags and timeout after the first byte received - void receivedFirstByte(); - // HttpControlMsgSink API - virtual void sendControlMsg(HttpControlMsg msg); + /// try to make progress on a transaction or read more I/O + void kick(); - // Client TCP connection details from comm layer. - Comm::ConnectionPointer clientConnection; - - /** - * The transfer protocol currently being spoken on this connection. - * HTTP/1 CONNECT and HTTP/2 SETTINGS offers the ability to change - * protocols on the fly. - */ - AnyP::ProtocolVersion transferProtocol; - - struct In { - In(); - ~In(); - bool maybeMakeSpaceAvailable(); - bool isOpen() const; ++ virtual bool isOpen() const; - Http1::TeChunkedParser *bodyParser; ///< parses chunked request body - SBuf buf; - } in; + Http1::TeChunkedParser *bodyParser; ///< parses HTTP/1.1 chunked request body /** number of body bytes we need to comm_read for the "current" request * @@@ -264,15 -254,7 +254,11 @@@ AsyncCall::Pointer closeHandler; /*The close handler for pinned server side connection*/ } pinning; - /// Squid listening port details where this connection arrived. - AnyP::PortCfgPointer port; - + /// If the port is not set then it is a connection-less object + /// created by an internal squid subsystem + bool connectionless() const { return port == NULL; } ++ bool transparent() const; - bool reading() const; - void stopReading(); ///< cancels comm_read if it is scheduled /// true if we stopped receiving the request const char *stoppedReceiving() const { return stoppedReceiving_; } diff --cc src/servers/Server.cc index 0000000000,e60911f8d9..9065f92fa4 mode 000000,100644..100644 --- a/src/servers/Server.cc +++ b/src/servers/Server.cc @@@ -1,0 -1,206 +1,208 @@@ + /* + * Copyright (C) 1996-2015 The Squid Software Foundation and contributors + * + * Squid software is distributed under GPLv2+ license and includes + * contributions from numerous individuals and organizations. + * Please see the COPYING and CONTRIBUTORS files for details. + */ + + #include "squid.h" + #include "anyp/PortCfg.h" + #include "client_side.h" + #include "comm.h" + #include "comm/Read.h" + #include "Debug.h" + #include "fd.h" + #include "fde.h" + #include "MasterXaction.h" + #include "servers/Server.h" + #include "SquidConfig.h" + #include "StatCounters.h" + #include "tools.h" + + Server::Server(const MasterXaction::Pointer &xact) : + AsyncJob("::Server"), // kids overwrite + clientConnection(xact->tcpClient), - transferProtocol(xact->squidPort->transport), + port(xact->squidPort), + receivedFirstByte_(false) -{} ++{ ++ if (xact->squidPort != NULL) ++ transferProtocol = xact->squidPort->transport; ++} + + bool + Server::doneAll() const + { + // servers are not done while the connection is open + return !Comm::IsConnOpen(clientConnection) && + BodyProducer::doneAll(); + } + + void + Server::start() + { + // TODO: shuffle activity from ConnStateData + } + + void + Server::swanSong() + { + if (Comm::IsConnOpen(clientConnection)) + clientConnection->close(); + + BodyProducer::swanSong(); + } + + void + Server::stopReading() + { + if (reading()) { + Comm::ReadCancel(clientConnection->fd, reader); + reader = NULL; + } + } + + bool + Server::maybeMakeSpaceAvailable() + { + if (inBuf.spaceSize() < 2) { + const SBuf::size_type haveCapacity = inBuf.length() + inBuf.spaceSize(); + if (haveCapacity >= Config.maxRequestBufferSize) { + debugs(33, 4, "request buffer full: client_request_buffer_max_size=" << Config.maxRequestBufferSize); + return false; + } + if (haveCapacity == 0) { + // haveCapacity is based on the SBuf visible window of the MemBlob buffer, which may fill up. + // at which point bump the buffer back to default. This allocates a new MemBlob with any un-parsed bytes. + inBuf.reserveCapacity(CLIENT_REQ_BUF_SZ); + } else { + const SBuf::size_type wantCapacity = min(static_cast(Config.maxRequestBufferSize), haveCapacity*2); + inBuf.reserveCapacity(wantCapacity); + } + debugs(33, 2, "growing request buffer: available=" << inBuf.spaceSize() << " used=" << inBuf.length()); + } + return (inBuf.spaceSize() >= 2); + } + + void + Server::readSomeData() + { + if (reading()) + return; + + debugs(33, 4, clientConnection << ": reading request..."); + + // we can only read if there is more than 1 byte of space free + if (Config.maxRequestBufferSize - inBuf.length() < 2) + return; + + typedef CommCbMemFunT Dialer; + reader = JobCallback(33, 5, Dialer, this, Server::doClientRead); + Comm::Read(clientConnection, reader); + } + + void + Server::doClientRead(const CommIoCbParams &io) + { + debugs(33,5, io.conn); + Must(reading()); + reader = NULL; + + /* Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up */ + if (io.flag == Comm::ERR_CLOSING) { + debugs(33,5, io.conn << " closing Bailout."); + return; + } + + assert(Comm::IsConnOpen(clientConnection)); + assert(io.conn->fd == clientConnection->fd); + + /* + * Don't reset the timeout value here. The value should be + * counting Config.Timeout.request and applies to the request + * as a whole, not individual read() calls. + * Plus, it breaks our lame *HalfClosed() detection + */ + + maybeMakeSpaceAvailable(); + CommIoCbParams rd(this); // will be expanded with ReadNow results + rd.conn = io.conn; + switch (Comm::ReadNow(rd, inBuf)) { + case Comm::INPROGRESS: + + if (inBuf.isEmpty()) + debugs(33, 2, io.conn << ": no data to process, " << xstrerr(rd.xerrno)); + readSomeData(); + return; + + case Comm::OK: + statCounter.client_http.kbytes_in += rd.size; + if (!receivedFirstByte_) + receivedFirstByte(); + // may comm_close or setReplyToError + if (!handleReadData()) + return; + + /* Continue to process previously read data */ + break; + + case Comm::ENDFILE: // close detected by 0-byte read + debugs(33, 5, io.conn << " closed?"); + + if (connFinishedWithConn(rd.size)) { + clientConnection->close(); + return; + } + + /* It might be half-closed, we can't tell */ + fd_table[io.conn->fd].flags.socket_eof = true; + commMarkHalfClosed(io.conn->fd); + fd_note(io.conn->fd, "half-closed"); + + /* There is one more close check at the end, to detect aborted + * (partial) requests. At this point we can't tell if the request + * is partial. + */ + + /* Continue to process previously read data */ + break; + + // case Comm::COMM_ERROR: + default: // no other flags should ever occur + debugs(33, 2, io.conn << ": got flag " << rd.flag << "; " << xstrerr(rd.xerrno)); + pipeline.terminateAll(rd.xerrno); + io.conn->close(); + return; + } + + afterClientRead(); + } + + /** callback handling the Comm::Write completion + * + * Will call afterClientWrite(size_t) to sync the I/O state. + * Then writeSomeData() to initiate any followup writes that + * could be immediately done. + */ + void + Server::clientWriteDone(const CommIoCbParams &io) + { + debugs(33,5, io.conn); + Must(writer != nullptr); + writer = nullptr; + + /* Bail out quickly on Comm::ERR_CLOSING - close handlers will tidy up */ + if (io.flag == Comm::ERR_CLOSING || !Comm::IsConnOpen(clientConnection)) { + debugs(33,5, io.conn << " closing Bailout."); + return; + } + + Must(io.conn->fd == clientConnection->fd); + + if (io.flag && pipeline.front()) + pipeline.front()->initiateClose("write failure"); + + afterClientWrite(io.size); // update state + writeSomeData(); // maybe schedules another write + } + diff --cc src/ssl/PeerConnector.cc index 880b9f236e,f2b3ecf44f..c3c7410455 --- a/src/ssl/PeerConnector.cc +++ b/src/ssl/PeerConnector.cc @@@ -612,105 -585,7 +608,105 @@@ Ssl::PeerConnector::status() cons return buf.content(); } +/// CallDialer to allow use Downloader objects within PeerConnector class. +class PeerConnectorCertDownloaderDialer: public CallDialer, public Downloader::CbDialer +{ +public: + typedef void (Ssl::PeerConnector::*Method)(SBuf &object, int status); + + PeerConnectorCertDownloaderDialer(Method method, Ssl::PeerConnector *pc): + method_(method), + peerConnector_(pc) {} + + /* CallDialer API */ + virtual bool canDial(AsyncCall &call) { return peerConnector_.valid(); } + void dial(AsyncCall &call) { ((&(*peerConnector_))->*method_)(object, status); } + virtual void print(std::ostream &os) const { + os << '(' << peerConnector_.get() << ", Http Status:" << status << ')'; + } + + Method method_; ///< The Ssl::PeerConnector method to dial + CbcPointer peerConnector_; ///< The Ssl::PeerConnector object +}; + +void +Ssl::PeerConnector::startCertDownloading(SBuf &url) +{ + AsyncCall::Pointer certCallback = asyncCall(81, 4, + "Ssl::PeerConnector::certDownloadingDone", + PeerConnectorCertDownloaderDialer(&Ssl::PeerConnector::certDownloadingDone, this)); + + const Downloader *csd = dynamic_cast(request->clientConnectionManager.valid()); + MasterXaction *xaction = new MasterXaction; + Downloader *dl = new Downloader(url, xaction, certCallback, csd ? csd->nestedLevel() + 1 : 1); + AsyncJob::Start(dl); +} + +void +Ssl::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus) +{ + certsDownloads++; + debugs(81, 5, "OK! certificate downloaded, status: " << downloadStatus << " data size: " << obj.length()); + + // Get ServerBio from SSL object + const int fd = serverConnection()->fd; + SSL *ssl = fd_table[fd].ssl; + BIO *b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); + + // Parse Certificate. Assume that it is in DER format. Probably we should handle PEM or other formats too + 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 Ssl::X509_STACK_Pointer &certsList = srvBio->serverCertificates(); + if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert, certsList)) { + urlsOfMissingCerts.push(SBuf(issuerUri)); + } + Ssl::SSL_add_untrusted_cert(ssl, cert); + } + + // Check if has uri to donwload and add it to urlsOfMissingCerts + if (urlsOfMissingCerts.size() && certsDownloads <= MaxCertsDownloads) { + startCertDownloading(urlsOfMissingCerts.front()); + urlsOfMissingCerts.pop(); + return; + } + + srvBio->holdRead(false); + Ssl::PeerConnector::NegotiateSsl(serverConnection()->fd, this); +} + +bool +Ssl::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 = dynamic_cast(request->clientConnectionManager.valid()); + if (csd && csd->nestedLevel() >= MaxNestedDownloads) + return false; + + const int fd = serverConnection()->fd; + SSL *ssl = fd_table[fd].ssl; + BIO *b = SSL_get_rbio(ssl); + Ssl::ServerBio *srvBio = static_cast(b->ptr); + const Ssl::X509_STACK_Pointer &certs = srvBio->serverCertificates(); + + if (certs.get() && sk_X509_num(certs.get())) { + debugs(83, 5, "SSL server sent " << sk_X509_num(certs.get()) << " certificates"); + Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs); + if (urlsOfMissingCerts.size()) { + startCertDownloading(urlsOfMissingCerts.front()); + urlsOfMissingCerts.pop(); + return true; + } + } + + return false; +} + - SSL_CTX * + Security::ContextPtr Ssl::BlindPeerConnector::getSslContext() { if (const CachePeer *peer = serverConnection()->getPeer()) { diff --cc src/ssl/PeerConnector.h index f00980927a,5d5548b350..4ecbaffe4e --- a/src/ssl/PeerConnector.h +++ b/src/ssl/PeerConnector.h @@@ -177,17 -167,8 +179,14 @@@ private /// Check SSL errors returned from cert validator against sslproxy_cert_error access list Ssl::CertErrors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&); - /// Callback function called when squid receive message from cert validator helper - static void sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &); - /// A wrapper function for negotiateSsl for use with Comm::SetSelect static void NegotiateSsl(int fd, void *data); + + /// The maximum allowed missing certificates downloads + static const unsigned int MaxCertsDownloads = 10; + /// The maximum allowed nested certificates downloads + static const unsigned int MaxNestedDownloads = 3; + AsyncCall::Pointer callback; ///< we call this with the results AsyncCall::Pointer closeHandler; ///< we call this when the connection closed time_t negotiationTimeout; ///< the SSL connection timeout to use diff --cc src/ssl/support.cc index 5e314e0599,429fd81206..edcadbcbdc --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@@ -33,10 -33,7 +33,10 @@@ #include +// TODO: Move ssl_ex_index_* global variables from global.cc here. +int ssl_ex_index_ssl_untrusted_chain = -1; + - static void setSessionCallbacks(SSL_CTX *ctx); + static void setSessionCallbacks(Security::ContextPtr ctx); Ipc::MemMap *SslSessionCache = NULL; const char *SslSessionCacheName = "ssl_session_cache"; @@@ -474,29 -471,8 +474,9 @@@ Ssl::Initialize(void ssl_ex_index_ssl_errors = SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors); ssl_ex_index_ssl_cert_chain = SSL_get_ex_new_index(0, (void *) "ssl_cert_chain", NULL, NULL, &ssl_free_CertChain); ssl_ex_index_ssl_validation_counter = SSL_get_ex_new_index(0, (void *) "ssl_validation_counter", NULL, NULL, &ssl_free_int); + ssl_ex_index_ssl_untrusted_chain = SSL_get_ex_new_index(0, (void *) "ssl_untrusted_chain", NULL, NULL, &ssl_free_CertChain); } - bool - Ssl::loadCerts(const char *certsFile, Ssl::CertsIndexedList &list) - { - BIO *in = BIO_new_file(certsFile, "r"); - if (!in) { - debugs(83, DBG_IMPORTANT, "Failed to open '" << certsFile << "' to load certificates"); - return false; - } - - X509 *aCert; - while((aCert = PEM_read_bio_X509(in, NULL, NULL, NULL))) { - static char buffer[2048]; - X509_NAME_oneline(X509_get_subject_name(aCert), buffer, sizeof(buffer)); - list.insert(std::pair(SBuf(buffer), aCert)); - } - debugs(83, 4, "Loaded " << list.size() << " certificates from file: '" << certsFile << "'"); - BIO_free(in); - return true; - } - #if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) static void ssl_info_cb(const SSL *ssl, int where, int ret) @@@ -1144,35 -1097,29 +1101,55 @@@ void Ssl::addChainToSslContext(Security } } +static const char * +hasAuthorityInfoAccessCaIssuers(X509 *cert) +{ + AUTHORITY_INFO_ACCESS *info; + if (!cert) + return NULL; + info = (AUTHORITY_INFO_ACCESS *)X509_get_ext_d2i(cert, NID_info_access, NULL, NULL); + if (!info) + return NULL; + + static char uri[MAX_URL]; + uri[0] = '\0'; + + for (int i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { + ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i); + if (OBJ_obj2nid(ad->method) == NID_ad_ca_issuers) { + if (ad->location->type == GEN_URI) { + xstrncpy(uri, (char *)ASN1_STRING_data(ad->location->d.uniformResourceIdentifier), sizeof(uri)); + } + break; + } + } + AUTHORITY_INFO_ACCESS_free(info); + return uri[0] != '\0' ? uri : NULL; +} + + bool + Ssl::loadCerts(const char *certsFile, Ssl::CertsIndexedList &list) + { + BIO *in = BIO_new_file(certsFile, "r"); + if (!in) { + debugs(83, DBG_IMPORTANT, "Failed to open '" << certsFile << "' to load certificates"); + return false; + } + + X509 *aCert; + while((aCert = PEM_read_bio_X509(in, NULL, NULL, NULL))) { + static char buffer[2048]; + X509_NAME_oneline(X509_get_subject_name(aCert), buffer, sizeof(buffer)); + list.insert(std::pair(SBuf(buffer), aCert)); + } + debugs(83, 4, "Loaded " << list.size() << " certificates from file: '" << certsFile << "'"); + BIO_free(in); + return true; + } + /// quickly find a certificate with a given issuer in Ssl::CertsIndexedList. static X509 * -findCertByIssuerFast(X509_STORE_CTX *ctx, Ssl::CertsIndexedList &list, X509 *cert) +findCertByIssuerFast(Ssl::CertsIndexedList &list, X509 *cert) { static char buffer[2048]; diff --cc src/ssl/support.h index 6777249202,86b4931ded..9146c36bd0 --- a/src/ssl/support.h +++ b/src/ssl/support.h @@@ -25,10 -25,8 +25,9 @@@ #if HAVE_OPENSSL_ENGINE_H #include #endif +#include #include - /** \defgroup ServerProtocolSSLAPI Server-Side SSL API \ingroup ServerProtocol @@@ -257,15 -242,15 +289,22 @@@ bool configureSSLUsingPkeyAndCertFromMe \ingroup ServerProtocolSSLAPI * Adds the certificates in certList to the certificate chain of the SSL context */ - void addChainToSslContext(SSL_CTX *sslContext, STACK_OF(X509) *certList); + void addChainToSslContext(Security::ContextPtr sslContext, STACK_OF(X509) *certList); + + /** + \ingroup ServerProtocolSSLAPI + * Configures sslContext to use squid untrusted certificates internal list + * to complete certificate chains when verifies SSL servers certificates. + */ + void useSquidUntrusted(SSL_CTX *sslContext); +/** + \ingroup ServerProtocolSSLAPI + * Configures sslContext to use squid untrusted certificates internal list + * to complete certificate chains when verifies SSL servers certificates. + */ +void useSquidUntrusted(SSL_CTX *sslContext); + /** \ingroup ServerProtocolSSLAPI * Read certificate, private key and any certificates which must be chained from files.