--- /dev/null
- if (context != context->http->getConn()->getCurrentContext().getRaw())
+#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) {
- Must(getConcurrentRequestCount() == 1);
++ 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);
- Downloader::writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call)
++ 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
- bool exceedSize = (getCurrentContext()->startOfOutput() && existingContent > -1 && (size_t)existingContent > MaxObjectSize) ||
++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;
- getCurrentContext()->http->out.size += receivedData.length;
- getCurrentContext()->noteSentBodyBytes(receivedData.length);
++ 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);
- switch (getCurrentContext()->socketState()) {
++ context->http->out.size += receivedData.length;
++ context->noteSentBodyBytes(receivedData.length);
+ }
+
- getCurrentContext()->pullData();
++ switch (context->socketState()) {
+ case STREAM_NONE:
+ debugs(33, 3, "Get more data");
++ 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<CbDialer*>(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<Downloader>(this), Downloader, downloadFinished);
+}
+
+bool
+Downloader::isOpen() const
+{
+ return cbdataReferenceValid(this) && // XXX: checking "this" in a method
+ callback != NULL;
+}
--- /dev/null
- virtual void writeControlMsgAndCall(ClientSocketContext *context, HttpReply *rep, AsyncCall::Pointer &call);
+#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(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
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);
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
/// 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
*
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_; }
--- /dev/null
- transferProtocol(xact->squidPort->transport),
+ /*
+ * 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),
-{}
+ 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<SBuf::size_type>(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<Server, CommIoCbParams> 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
+ }
+
return buf.content();
}
- SSL_CTX *
+/// 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<Ssl::PeerConnector> 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<const Downloader*>(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<Ssl::ServerBio *>(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<const Downloader*>(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<Ssl::ServerBio *>(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;
+}
+
+ Security::ContextPtr
Ssl::BlindPeerConnector::getSslContext()
{
if (const CachePeer *peer = serverConnection()->getPeer()) {
/// 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
#include <cerrno>
- static void setSessionCallbacks(SSL_CTX *ctx);
+// TODO: Move ssl_ex_index_* global variables from global.cc here.
+int ssl_ex_index_ssl_untrusted_chain = -1;
+
+ static void setSessionCallbacks(Security::ContextPtr ctx);
Ipc::MemMap *SslSessionCache = NULL;
const char *SslSessionCacheName = "ssl_session_cache";
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, X509 *>(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)
}
}
+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, X509 *>(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];
#if HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
+#include <queue>
#include <map>
-
/**
\defgroup ServerProtocolSSLAPI Server-Side SSL API
\ingroup ServerProtocol
\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.