When the bump-server-first used we do not always know the hostname of the server
we are connecting to. Thus the the hostname validity check for the
SSL server certificate will fail with SQUID_X509_V_ERR_DOMAIN_MISMATCH error.
This patch does not check if server certificate is valid for the hostname on
bump-server-first case and move this check after the http request received
from the web client
#include "comm/Loops.h"
#include "comm/Write.h"
#include "comm/TcpAcceptor.h"
+#include "errorpage.h"
#include "eui/Config.h"
#include "fde.h"
#include "HttpHdrContRange.h"
readSomeData();
}
+#if USE_SSL
+bool ConnStateData::serveDelayedError(ClientSocketContext *context)
+{
+ ClientHttpRequest *http = context->http;
+ StoreEntry *e = bumpServerFirstErrorEntry();
+ if (!e)
+ return false;
+
+ if (!e->isEmpty()) {
+ //Failed? Here we should get the error from conn and send it to client
+ // The error stored in ConnStateData::bumpFirstEntry, replace the
+ // ClientHttpRequest store entry with this.
+ clientStreamNode *node = context->getClientReplyContext();
+ clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+ assert (repContext);
+ debugs(33, 5, "Connection first has failed for " << http->uri << ". Respond with an error");
+ repContext->setReplyToStoreEntry(e);
+ context->pullData();
+ flags.readMore = false;
+ return true;
+ }
+
+ // We are in ssl-bump first mode. We have not the server connect name when
+ // we connected to server so we have to check certificates subject with our server name
+ if (X509 *server_cert = getBumpServerCert()) {
+ HttpRequest *request = http->request;
+ if (!Ssl::checkX509ServerValidity(server_cert, request->GetHost())) {
+ debugs(33, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate does not match domainname " << request->GetHost());
+
+ ACLFilledChecklist check(Config.ssl_client.cert_error, request, dash_str);
+ if (Comm::IsConnOpen(pinning.serverConnection))
+ check.fd(pinning.serverConnection->fd);
+
+ if (check.fastCheck() != ACCESS_ALLOWED) {
+ clientStreamNode *node = context->getClientReplyContext();
+ clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
+ assert (repContext);
+
+ // Create an error object and fill it
+ ErrorState *err = errorCon(ERR_SECURE_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+ err->src_addr = clientConnection->remote;
+#ifdef EPROTO
+ err->xerrno = EPROTO;
+#else
+ err->xerrno = EACCES;
+#endif
+ Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail( SQUID_X509_V_ERR_DOMAIN_MISMATCH, server_cert);
+ err->detail = errDetail;
+ repContext->setReplyToError(request->method, err);
+ assert(context->http->out.offset == 0);
+ context->pullData();
+ flags.readMore = false;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+#endif //USE_SSL
+
static void
clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *context, const HttpRequestMethod& method, HttpVersion http_ver)
{
}
#if USE_SSL
- if (conn->switchedToHttps() && conn->bumpServerFirstErrorEntry() &&
- !Comm::IsConnOpen(conn->pinning.serverConnection)) {
- //Failed? Here we should get the error from conn and send it to client
- // The error stored in ConnStateData::bumpFirstEntry, replace the
- // ClientHttpRequest store entry with this.
- clientStreamNode *node = context->getClientReplyContext();
- clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
- assert (repContext);
- debugs(33, 5, "Connection first has failed for " << http->uri << ". Respond with an error");
- StoreEntry *e = conn->bumpServerFirstErrorEntry();
- Must(e && !e->isEmpty());
- repContext->setReplyToStoreEntry(e);
- context->pullData();
- conn->flags.readMore = false;
+ if (conn->switchedToHttps() && conn->serveDelayedError(context))
goto finish;
- }
#endif
/* Do we expect a request-body? */
/// Holds the squid error reply in the case of bump server first error
StoreEntry *bumpServerFirstErrorEntry() const {return bumpErrorEntry;}
void setBumpServerCert(X509 *serverCert) {bumpServerCert.reset(serverCert);}
+ X509 *getBumpServerCert() {return bumpServerCert.get();}
+ bool serveDelayedError(ClientSocketContext *context);
#else
bool switchedToHttps() const { return false; }
#endif
if (unparsedrequest)
errstate->request_hdrs = xstrdup(unparsedrequest);
- if (status == HTTP_NOT_IMPLEMENTED && http->request)
+#if USE_AUTH
+ errstate->auth_user_request = auth_user_request;
+#endif
+ setReplyToError(method, errstate);
+}
+
+void clientReplyContext::setReplyToError(const HttpRequestMethod& method, ErrorState *errstate)
+{
+ if (errstate->httpStatus == HTTP_NOT_IMPLEMENTED && http->request)
/* prevent confusion over whether we default to persistent or not */
http->request->flags.proxy_keepalive = 0;
http->al.http.code = errstate->httpStatus;
createStoreEntry(method, request_flags());
-#if USE_AUTH
- errstate->auth_user_request = auth_user_request;
-#endif
assert(errstate->callback_data == NULL);
errorAppendEntry(http->storeEntry(), errstate);
/* Now the caller reads to get this */
#else
void * unused);
#endif
+ void setReplyToError(const HttpRequestMethod& method, ErrorState *errstate);
void createStoreEntry(const HttpRequestMethod& m, request_flags flags);
void removeStoreReference(store_client ** scp, StoreEntry ** ep);
void removeClientStoreReference(store_client **scp, ClientHttpRequest *http);
SSL_set_session(ssl, peer->sslSession);
} else {
- SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)request->GetHost());
+ if (request->protocol != AnyP::PROTO_SSL_PEEK)
+ SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)request->GetHost());
+ // else we do not have the ssl server name yet, but only its IP address.
// We need to set SNI TLS extension only in the case we are
// connecting direct to origin server
return matchDomainName(server, cn[0] == '*' ? cn + 1 : cn);
}
+bool Ssl::checkX509ServerValidity(X509 *cert, const char *server)
+{
+ return matchX509CommonNames(cert, (void *)server, check_domain);
+}
+
/// \ingroup ServerProtocolSSLInternal
static int
ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
debugs(83, 5, "SSL Certificate signature OK: " << buffer);
if (server) {
- int found = Ssl::matchX509CommonNames(peer_cert, (void *)server, check_domain);
-
- if (!found) {
+ if (!Ssl::checkX509ServerValidity(peer_cert, server)) {
debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
ok = 0;
error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
*/
int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data, ASN1_STRING *cn_data));
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Check if the certificate is valid for a server
+ \param cert The X509 cert to check.
+ \param server The server name.
+ \return true if the certificate is valid for the server or false otherwise.
+ */
+bool checkX509ServerValidity(X509 *cert, const char *server);
+
/**
\ingroup ServerProtocolSSLAPI
* Convert a given ASN1_TIME to a string form.