From: Christos Tsantilas Date: Tue, 13 Nov 2012 18:13:50 +0000 (+0200) Subject: merge from trunk r12441 X-Git-Tag: SQUID_3_4_0_1~458^2~6^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=53251bc3a57a1321e2bf7b8bbc0f26ca5bfc734f;p=thirdparty%2Fsquid.git merge from trunk r12441 --- 53251bc3a57a1321e2bf7b8bbc0f26ca5bfc734f diff --cc src/forward.cc index 638bdbc68a,bb056dd833..2cf3e81bcc --- a/src/forward.cc +++ b/src/forward.cc @@@ -63,14 -65,13 +65,16 @@@ #include "neighbors.h" #include "pconn.h" #include "PeerSelectState.h" - #include "protos.h" + #include "SquidConfig.h" #include "SquidTime.h" #include "Store.h" + #include "StoreClient.h" + #include "urn.h" #include "whois.h" #if USE_SSL +#include "ssl/cert_validate_message.h" +#include "ssl/Config.h" +#include "ssl/helper.h" #include "ssl/support.h" #include "ssl/ErrorDetail.h" #include "ssl/ServerBump.h" @@@ -759,191 -739,9 +742,191 @@@ FwdState::negotiateSSL(int fd serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl); } +#if 1 // USE_SSL_CERT_VALIDATOR + if (Ssl::TheConfig.ssl_crt_validator) { + Ssl::CertValidationRequest validationRequest; + // WARNING: The STACK_OF(*) OpenSSL objects does not support locking. + // If we need to support locking we need to sk_X509_dup the STACK_OF(X509) + // list and lock all of the X509 members of the list. + // Currently we do not use any locking for any of the members of the + // Ssl::CertValidationRequest class. If the ssl object gone, the value returned + // from SSL_get_peer_cert_chain may not exist any more. In this code the + // Ssl::CertValidationRequest object used only to pass data to + // Ssl::CertValidationHelper::submit method. + validationRequest.peerCerts = SSL_get_peer_cert_chain(ssl); + validationRequest.domainName = request->GetHost(); + if (Ssl::Errors *errs = static_cast(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors))) + // validationRequest disappears on return so no need to cbdataReference + validationRequest.errors = errs; + else + validationRequest.errors = NULL; + try { + debugs(83, 5, HERE << "Sending SSL certificate for validation to ssl_crtvd."); - Ssl::CertValidationMsg requestMsg; ++ Ssl::CertValidationMsg requestMsg(Ssl::CrtdMessage::REQUEST); + requestMsg.setCode(Ssl::CertValidationMsg::code_cert_validate); + requestMsg.composeRequest(validationRequest); + debugs(83, 5, HERE << "SSL crtvd request: " << requestMsg.compose().c_str()); + Ssl::CertValidationHelper::GetInstance()->sslSubmit(requestMsg, sslCrtvdHandleReplyWrapper, this); + return; + } catch (const std::exception &e) { + debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " << + "request for " << validationRequest.domainName << + " certificate: " << e.what() << "; will now block to " << + "validate that certificate."); + // fall through to do blocking in-process generation. + ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); + fail(anErr); + if (serverConnection()->getPeer()) { + peerConnectFailed(serverConnection()->getPeer()); + } + serverConn->close(); + self = NULL; + return; + } + } +#endif // USE_SSL_CERT_VALIDATOR + dispatch(); } +#if 1 // USE_SSL_CERT_VALIDATOR +void - FwdState::sslCrtvdHandleReplyWrapper(void *data, char *reply) ++FwdState::sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply) +{ + FwdState * fwd = (FwdState *)(data); + fwd->sslCrtvdHandleReply(reply); +} + +void - FwdState::sslCrtvdHandleReply(const char *reply) ++FwdState::sslCrtvdHandleReply(const HelperReply &reply) +{ + Ssl::Errors *errs = NULL; + Ssl::ErrorDetail *errDetails = NULL; + bool validatorFailed = false; + if (!Comm::IsConnOpen(serverConnection())) { + return; + } + SSL *ssl = fd_table[serverConnection()->fd].ssl; + - if (!reply) { ++ if (!reply.other().hasContent()) { + debugs(83, 1, HERE << "\"ssl_crtvd\" helper return reply"); + validatorFailed = true; + } else { - Ssl::CertValidationMsg replyMsg; ++ Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY); + Ssl::CertValidationResponse validationResponse; + std::string error; + STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(ssl); - if (replyMsg.parse(reply, strlen(reply)) != Ssl::CrtdMessage::OK || ++ if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK || + !replyMsg.parseResponse(validationResponse, peerCerts, error) ) { + debugs(83, 5, HERE << "Reply from ssl_crtvd for " << request->GetHost() << " is incorrect"); + validatorFailed = true; + } else { - if (replyMsg.getCode() == "OK") { ++ if (reply.result == HelperReply::Okay) { + debugs(83, 5, HERE << "Certificate for " << request->GetHost() << " was successfully validated from ssl_crtvd"); - } else if (replyMsg.getCode() == "ERR") { ++ } else if (reply.result == HelperReply::Error) { + debugs(83, 5, HERE << "Certificate for " << request->GetHost() << " found buggy by ssl_crtvd"); + errs = sslCrtvdCheckForErrors(validationResponse, errDetails); + } else { + debugs(83, 5, HERE << "Certificate for " << request->GetHost() << " cannot be validated. ssl_crtvd response: " << replyMsg.getBody()); + validatorFailed = true; + } + + if (!errDetails && !validatorFailed) { + dispatch(); + return; + } + } + } + + ErrorState *anErr = NULL; + if (validatorFailed) { + anErr = new ErrorState(ERR_GATEWAY_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request); + } else { + + // Check the list error with + if (errDetails && request->clientConnectionManager.valid()) { + // remember the server certificate from the ErrorDetail object + if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) { + // remember validation errors, if any + if (errs) { + if (serverBump->sslErrors) + cbdataReferenceDone(serverBump->sslErrors); + serverBump->sslErrors = cbdataReference(errs); + } + } + } + + anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL); + anErr->detail = errDetails; + /*anErr->xerrno= Should preserved*/ + } + + fail(anErr); + if (serverConnection()->getPeer()) { + peerConnectFailed(serverConnection()->getPeer()); + } + serverConn->close(); + self = NULL; + return; +} + +/// Checks errors in the cert. validator response against sslproxy_cert_error. +/// The first honored error, if any, is returned via errDetails parameter. +/// The method returns all seen errors except SSL_ERROR_NONE as Ssl::Errors. +Ssl::Errors * +FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse &resp, Ssl::ErrorDetail *& errDetails) +{ + Ssl::Errors *errs = NULL; + + ACLFilledChecklist *check = NULL; + if (acl_access *acl = Config.ssl_client.cert_error) + check = new ACLFilledChecklist(acl, request, dash_str); + + SSL *ssl = fd_table[serverConnection()->fd].ssl; + typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI; + for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) { + debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason); + + if (i->error_no == SSL_ERROR_NONE) + continue; //ignore???? + + if (!errDetails) { + bool allowed = false; + if (check) { + check->sslErrors = new Ssl::Errors(i->error_no); + if (check->fastCheck() == ACCESS_ALLOWED) + allowed = true; + } + // else the Config.ssl_client.cert_error access list is not defined + // and the first error will cause the error page + + if (allowed) { + debugs(83, 3, "bypassing SSL error " << i->error_no << " in " << "buffer"); + } else { + debugs(83, 5, "confirming SSL error " << i->error_no); + X509 *brokenCert = i->cert; + X509 *peerCert = SSL_get_peer_certificate(ssl); + const char *aReason = i->error_reason.empty() ? NULL : i->error_reason.c_str(); + errDetails = new Ssl::ErrorDetail(i->error_no, peerCert, brokenCert, aReason); + X509_free(peerCert); + } + delete check->sslErrors; + check->sslErrors = NULL; + } + + if (!errs) + errs = new Ssl::Errors(i->error_no); + else + errs->push_back_unique(i->error_no); + } + if (check) + delete check; + + return errs; +} + +#endif // USE_SSL_CERT_VALIDATOR + void FwdState::initiateSSL() { @@@ -1006,22 -804,12 +989,20 @@@ Ssl::setClientSNI(ssl, hostname); } - // Create the ACL check list now, while we have access to more info. - // The list is used in ssl_verify_cb() and is freed in ssl_free(). - if (acl_access *acl = Config.ssl_client.cert_error) { - ACLFilledChecklist *check = new ACLFilledChecklist(acl, request, dash_str); - SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check); +#if 1 // USE_SSL_CERT_VALIDATOR + // If CertValidation Helper used do not lookup checklist for errors, + // but keep a list of errors to send it to CertValidator + if (!Ssl::TheConfig.ssl_crt_validator) { +#endif + // Create the ACL check list now, while we have access to more info. + // The list is used in ssl_verify_cb() and is freed in ssl_free(). + if (acl_access *acl = Config.ssl_client.cert_error) { + ACLFilledChecklist *check = new ACLFilledChecklist(acl, request, dash_str); - if (Comm::IsConnOpen(clientConn)) - check->fd(clientConn->fd); + SSL_set_ex_data(ssl, ssl_ex_index_cert_error_check, check); + } +#if 1 // USE_SSL_CERT_VALIDATOR } +#endif // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE X509 *peeked_cert; diff --cc src/forward.h index b0bae5e3da,0a754c6ee9..2c7ce764ba --- a/src/forward.h +++ b/src/forward.h @@@ -2,14 -2,13 +2,17 @@@ #define SQUID_FORWARD_H #include "Array.h" + #include "base/RefCount.h" #include "comm.h" #include "comm/Connection.h" + #include "err_type.h" #include "fde.h" ++#include "HelperReply.h" + #include "HttpStatusCode.h" #include "ip/Address.h" - #include "RefCount.h" +#if USE_SSL +#include "ssl/support.h" +#endif /* forward decls */ @@@ -80,14 -71,6 +83,14 @@@ public /** return a ConnectionPointer to the current server connection (may or may not be open) */ Comm::ConnectionPointer const & serverConnection() const { return serverConn; }; +#if USE_SSL //&& USE_SSL_CERT_VALIDATOR + /// Callback function called when squid receive message from cert validator helper - static void sslCrtvdHandleReplyWrapper(void *data, char *reply); ++ static void sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply); + /// Process response from cert validator helper - void sslCrtvdHandleReply(const char *reply); ++ void sslCrtvdHandleReply(const HelperReply &reply); + /// Check SSL errors returned from cert validator against sslproxy_cert_error access list + Ssl::Errors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse &, Ssl::ErrorDetail *&); +#endif private: // hidden for safer management of self; use static fwdStart FwdState(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp); diff --cc src/ssl/cert_validate_message.h index 3d22aa64a9,0000000000..b2479bcaeb mode 100644,000000..100644 --- a/src/ssl/cert_validate_message.h +++ b/src/ssl/cert_validate_message.h @@@ -1,116 -1,0 +1,116 @@@ +/* + * $Id$ + */ + +#ifndef SQUID_SSL_CERT_VALIDATE_MESSAGE_H +#define SQUID_SSL_CERT_VALIDATE_MESSAGE_H + +#include "ssl/support.h" +#include "ssl/crtd_message.h" +#include + +namespace Ssl +{ + +/** + * This class is used to hold the required informations to build + * a request message for the certificate validator helper + */ +class CertValidationRequest +{ +public: + STACK_OF(X509) *peerCerts; ///< The list of sent by SSL server + Errors *errors; ///< The list of errors detected + std::string domainName; ///< The server name + CertValidationRequest() : peerCerts(NULL), errors(NULL) {} +}; + +/** + * This class is used to store informations found in certificate validation + * response messages read from certificate validator helper + */ +class CertValidationResponse +{ +public: + /** + * This class used to hold error informations returned from + * cert validator helper. + */ + class RecvdError + { + public: + RecvdError(): id(0), error_no(SSL_ERROR_NONE), cert(NULL) {} + RecvdError(const RecvdError &); + ~RecvdError(); + RecvdError & operator = (const RecvdError &); + void setCert(X509 *); ///< Sets cert to the given certificate + int id; ///< The id of the error + ssl_error_t error_no; ///< The OpenSSL error code + std::string error_reason; ///< A string describing the error + X509 *cert; ///< The broken certificate + }; + + typedef std::vector RecvdErrors; + + /// Search in errors list for the error item with id=errorId. + /// If none found a new RecvdError item added with the given id; + RecvdError &getError(int errorId); + RecvdErrors errors; ///< The list of parsed errors +}; + +/** + * This class is responsible for composing or parsing messages destined to + * or comming from a cert validator helper. + * The messages format is: + * ...\1 + */ +class CertValidationMsg: public CrtdMessage +{ +private: + /** + * This class used to hold the certId/cert pairs found + * in cert validation messages. + */ + class CertItem + { + public: + std::string name; ///< The certificate Id to use + X509 *cert; ///< A pointer to certificate + CertItem(): cert(NULL) {} + CertItem(const CertItem &); + CertItem & operator = (const CertItem &); + ~CertItem(); + void setCert(X509 *); ///< Sets cert to the given certificate + }; + +public: - CertValidationMsg(): CrtdMessage() {} ++ CertValidationMsg(MessageKind kind): CrtdMessage(kind) {} + + /// Build a request message for the cert validation helper + /// using informations provided by vcert object + void composeRequest(CertValidationRequest const &vcert); + + /// Parse a response message and fill the resp object with parsed informations + bool parseResponse(CertValidationResponse &resp, STACK_OF(X509) *peerCerts, std::string &error); + + /// Search a CertItems list for the certificate with ID "name" + X509 *getCertByName(std::vector const &, std::string const & name); + + /// String code for "cert_validate" messages + static const std::string code_cert_validate; + /// Parameter name for passing intended domain name + static const std::string param_domain; + /// Parameter name for passing SSL errors + static const std::string param_error; + /// Parameter name for passing SSL certificates + static const std::string param_cert; + /// Parameter name for passing the major SSL error + static const std::string param_error_name; + /// Parameter name for passing the error reason + static const std::string param_error_reason; + /// Parameter name for passing the error cert ID + static const std::string param_error_cert; +}; + +}//namespace Ssl +#endif // SQUID_SSL_CERT_VALIDATE_MESSAGE_H diff --cc src/ssl/helper.cc index d42db1c730,dd6b00b90f..1dbff287d0 --- a/src/ssl/helper.cc +++ b/src/ssl/helper.cc @@@ -106,85 -104,3 +104,87 @@@ void Ssl::Helper::sslSubmit(CrtdMessag msg += '\n'; helperSubmit(ssl_crtd, msg.c_str(), callback, data); } + +/*ssl_crtd_validator*/ + +Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance() +{ + static Ssl::CertValidationHelper sslHelper; + if (!Ssl::TheConfig.ssl_crt_validator) + return NULL; + return &sslHelper; +} + +Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL) +{ +} + +Ssl::CertValidationHelper::~CertValidationHelper() +{ + Shutdown(); +} + +void Ssl::CertValidationHelper::Init() +{ + assert(ssl_crt_validator == NULL); + + // we need to start ssl_crtd only if some port(s) need to bump SSL + bool found = false; + for (AnyP::PortCfg *s = ::Config.Sockaddr.http; !found && s; s = s->next) + found = s->sslBump; + for (AnyP::PortCfg *s = ::Config.Sockaddr.https; !found && s; s = s->next) + found = s->sslBump; + if (!found) + return; + + ssl_crt_validator = new helper("ssl_crt_validator"); + ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children); + ssl_crt_validator->ipc_type = IPC_STREAM; + // The crtd messages may contain the eol ('\n') character. We are + // going to use the '\1' char as the end-of-message mark. + ssl_crt_validator->eom = '\1'; + assert(ssl_crt_validator->cmdline == NULL); + { + char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator); + char *tmp_begin = tmp; + char * token = NULL; + while ((token = strwordtok(NULL, &tmp))) { + wordlistAdd(&ssl_crt_validator->cmdline, token); + } + safe_free(tmp_begin); + } + helperOpenServers(ssl_crt_validator); +} + +void Ssl::CertValidationHelper::Shutdown() +{ + if (!ssl_crt_validator) + return; + helperShutdown(ssl_crt_validator); + wordlistDestroy(&ssl_crt_validator->cmdline); + delete ssl_crt_validator; + ssl_crt_validator = NULL; +} + +void Ssl::CertValidationHelper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data) +{ + static time_t first_warn = 0; + assert(ssl_crt_validator); + + if (ssl_crt_validator->stats.queue_size >= (int)(ssl_crt_validator->childs.n_running * 2)) { + if (first_warn == 0) + first_warn = squid_curtime; + if (squid_curtime - first_warn > 3 * 60) + fatal("SSL servers not responding for 3 minutes"); + debugs(83, 1, HERE << "Queue overload, rejecting"); - callback(data, (char *)"error 45 Temporary network problem, please retry later"); ++ const char *errMsg = "BH error 45 Temporary network problem, please retry later"; ++ HelperReply failReply(errMsg,strlen(errMsg)); ++ callback(data, failReply); + return; + } + + first_warn = 0; + std::string msg = message.compose(); + msg += '\n'; + helperSubmit(ssl_crt_validator, msg.c_str(), callback, data); +} diff --cc src/ssl/support.cc index d17ca751d3,8cc8b63475..4b42d0278a --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@@ -44,8 -42,7 +42,9 @@@ #include "anyp/PortCfg.h" #include "fde.h" #include "globals.h" +#include "protos.h" + #include "SquidConfig.h" +#include "ssl/Config.h" #include "ssl/ErrorDetail.h" #include "ssl/support.h" #include "ssl/gadgets.h"