} else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
A->allow.kind = Ssl::bumpServerFirst;
bumpCfgStyleNow = bcsNew;
+ } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpPeekAndSplice]) == 0) {
+ A->allow.kind = Ssl::bumpPeekAndSplice;
+ bumpCfgStyleNow = bcsNew;
} else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
A->allow.kind = Ssl::bumpNone;
bumpCfgStyleNow = bcsNew;
the client, using a mimicked server certificate. Works with both
CONNECT requests and intercepted SSL connections.
+ peek-and-splice
+ Decides if the connection should bumped or not based on
+ client-to-squid and server-to-squid SSL hello messages.
+
none
Become a TCP tunnel without decoding the connection.
Works with both CONNECT requests and intercepted SSL
#include "ClientInfo.h"
#endif
#if USE_SSL
+#include "ssl/bio.h"
#include "ssl/ProxyCerts.h"
#include "ssl/context_storage.h"
#include "ssl/helper.h"
static SSL *
httpsCreate(const Comm::ConnectionPointer &conn, SSL_CTX *sslContext)
{
- if (SSL *ssl = Ssl::Create(sslContext, conn->fd, "client https start")) {
+ if (SSL *ssl = Ssl::CreateServer(sslContext, conn->fd, "client https start")) {
debugs(33, 5, "httpsCreate: will negotate SSL on " << conn);
return ssl;
}
return NULL;
}
-/** negotiate an SSL connection */
-static void
-clientNegotiateSSL(int fd, void *data)
+static bool
+Squid_SSL_accept(ConnStateData *conn, PF *callback)
{
- ConnStateData *conn = (ConnStateData *)data;
- X509 *client_cert;
+ int fd = conn->clientConnection->fd;
SSL *ssl = fd_table[fd].ssl;
int ret;
switch (ssl_error) {
case SSL_ERROR_WANT_READ:
- Comm::SetSelect(fd, COMM_SELECT_READ, clientNegotiateSSL, conn, 0);
- return;
+ Comm::SetSelect(fd, COMM_SELECT_READ, callback, conn, 0);
+ return false;
case SSL_ERROR_WANT_WRITE:
- Comm::SetSelect(fd, COMM_SELECT_WRITE, clientNegotiateSSL, conn, 0);
- return;
+ Comm::SetSelect(fd, COMM_SELECT_WRITE, callback, conn, 0);
+ return false;
case SSL_ERROR_SYSCALL:
if (ret == 0) {
- debugs(83, 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Aborted by client");
+ debugs(83, 2, "Error negotiating SSL connection on FD " << fd << ": Aborted by client: " << ssl_error);
comm_close(fd);
- return;
+ return false;
} else {
int hard = 1;
if (errno == ECONNRESET)
hard = 0;
- debugs(83, hard ? 1 : 2, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
+ debugs(83, hard ? 1 : 2, "Error negotiating SSL connection on FD " <<
fd << ": " << strerror(errno) << " (" << errno << ")");
comm_close(fd);
- return;
+ return false;
}
case SSL_ERROR_ZERO_RETURN:
- debugs(83, DBG_IMPORTANT, "clientNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": Closed by client");
+ debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " << fd << ": Closed by client");
comm_close(fd);
- return;
+ return false;
default:
- debugs(83, DBG_IMPORTANT, "clientNegotiateSSL: Error negotiating SSL connection on FD " <<
+ debugs(83, DBG_IMPORTANT, "Error negotiating SSL connection on FD " <<
fd << ": " << ERR_error_string(ERR_get_error(), NULL) <<
" (" << ssl_error << "/" << ret << ")");
comm_close(fd);
- return;
+ return false;
}
/* NOTREACHED */
}
+ return true;
+}
+
+/** negotiate an SSL connection */
+static void
+clientNegotiateSSL(int fd, void *data)
+{
+ ConnStateData *conn = (ConnStateData *)data;
+ X509 *client_cert;
+ SSL *ssl = fd_table[fd].ssl;
+
+ if (!Squid_SSL_accept(conn, clientNegotiateSSL))
+ return;
if (SSL_session_reused(ssl)) {
debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
} else {
debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
- SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
- getSslContextDone(ctx, true);
+ if (sslServerBump && sslServerBump->mode == Ssl::bumpPeekAndSplice) {
+ doPeekAndSpliceStep();
+ SSL *ssl = fd_table[clientConnection->fd].ssl;
+ bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
+ if (!ret)
+ debugs(33, 5, HERE << "Failed to set certificates to ssl object for PeekAndSplice mode");
+ } else {
+ SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
+ getSslContextDone(ctx, true);
+ }
return;
}
}
sslBumpCertKey = certProperties.dbKey().c_str();
assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
- debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
- Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
- SSL_CTX * dynCtx = NULL;
- Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache.get(sslBumpCertKey.termedBuf());
- if (cachedCtx && (dynCtx = cachedCtx->get())) {
- debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache");
- if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
- debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid");
- getSslContextDone(dynCtx);
- return;
+ // Disable caching for bumpPeekAndSplice mode
+ if (!(sslServerBump && sslServerBump->mode == Ssl::bumpPeekAndSplice)) {
+ debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
+ Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
+ SSL_CTX * dynCtx = NULL;
+ Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache.get(sslBumpCertKey.termedBuf());
+ if (cachedCtx && (dynCtx = cachedCtx->get())) {
+ debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache");
+ if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
+ debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid");
+ getSslContextDone(dynCtx);
+ return;
+ } else {
+ debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
+ ssl_ctx_cache.del(sslBumpCertKey.termedBuf());
+ }
} else {
- debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
- ssl_ctx_cache.del(sslBumpCertKey.termedBuf());
+ debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
}
- } else {
- debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
}
#if USE_SSL_CRTD
#endif // USE_SSL_CRTD
debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
- dynCtx = Ssl::generateSslContext(certProperties, *port);
- getSslContextDone(dynCtx, true);
+ if (sslServerBump && sslServerBump->mode == Ssl::bumpPeekAndSplice) {
+ doPeekAndSpliceStep();
+ SSL *ssl = fd_table[clientConnection->fd].ssl;
+ if (!Ssl::configureSSL(ssl, certProperties, *port))
+ debugs(33, 5, HERE << "Failed to set certificates to ssl object for PeekAndSplice mode");
+ } else {
+ SSL_CTX *dynCtx = Ssl::generateSslContext(certProperties, *port);
+ getSslContextDone(dynCtx, true);
+ }
return;
}
getSslContextDone(NULL);
FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request);
return;
}
+ else if (bumpServerMode == Ssl::bumpPeekAndSplice) {
+ request->flags.sslPeek = true;
+ sslServerBump = new Ssl::ServerBump(request, NULL, Ssl::bumpPeekAndSplice);
+ startPeekAndSplice();
+ return;
+ }
// otherwise, use sslConnectHostOrIp
getSslContextStart();
}
+/** negotiate an SSL connection */
+static void
+clientPeekAndSpliceSSL(int fd, void *data)
+{
+ ConnStateData *conn = (ConnStateData *)data;
+ SSL *ssl = fd_table[fd].ssl;
+
+ debugs(83, 2, "Start peek and splice on" << fd);
+
+ if (!Squid_SSL_accept(conn, clientPeekAndSpliceSSL))
+ debugs(83, 2, "SSL_accept failed.");
+
+ BIO *b = SSL_get_rbio(ssl);
+ assert(b);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ if (bio->gotHello()) {
+ debugs(83, 2, "I got hello. Start forwarding the request!!! ");
+ Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
+ Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
+ conn->startPeekAndSpliceDone();
+ return;
+ }
+}
+
+void ConnStateData::startPeekAndSplice()
+{
+ // will call httpsPeeked() with certificate and connection, eventually
+ SSL_CTX *unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
+ fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
+
+ if (!httpsCreate(clientConnection, unConfiguredCTX))
+ return;
+
+ // commSetConnTimeout() was called for this request before we switched.
+
+ // Disable the client read handler until CachePeer selection is complete
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientPeekAndSpliceSSL, this, 0);
+ switchedToHttps_ = true;
+
+ SSL *ssl = fd_table[clientConnection->fd].ssl;
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+ bio->hold(true);
+}
+
+void
+ConnStateData::startPeekAndSpliceDone()
+{
+ FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request);
+}
+
+void
+ConnStateData::doPeekAndSpliceStep()
+{
+ SSL *ssl = fd_table[clientConnection->fd].ssl;
+ BIO *b = SSL_get_rbio(ssl);
+ assert(b);
+ Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
+
+ debugs(33, 5, HERE << "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
+ bio->hold(false);
+
+ Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, this, 0);
+ switchedToHttps_ = true;
+}
+
void
ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
{
void quitAfterError(HttpRequest *request); // meant to be private
#if USE_SSL
+ /// Initializes and starts a peek-and-splice negotiation with the SSL client
+ void startPeekAndSplice();
+ /// Called when the initialization of peek-and-splice negotiation finidhed
+ void startPeekAndSpliceDone();
+ /// Called when a peek-and-splice step finished. For example after
+ /// server-side SSL certificates received and client-side SSL certificates
+ /// generated
+ void doPeekAndSpliceStep();
/// called by FwdState when it is done bumping the server
void httpsPeeked(Comm::ConnectionPointer serverConnection);
/// returns raw sslBump mode value
Ssl::BumpMode sslBumpNeed() const { return sslBumpNeed_; }
/// returns true if and only if the request needs to be bumped
- bool sslBumpNeeded() const { return sslBumpNeed_ == Ssl::bumpServerFirst || sslBumpNeed_ == Ssl::bumpClientFirst; }
+ bool sslBumpNeeded() const { return sslBumpNeed_ == Ssl::bumpServerFirst || sslBumpNeed_ == Ssl::bumpClientFirst || sslBumpNeed_ == Ssl::bumpPeekAndSplice; }
/// set the sslBumpNeeded state
void sslBumpNeed(Ssl::BumpMode mode);
void sslBumpStart();
#include "urn.h"
#include "whois.h"
#if USE_SSL
+#include "ssl/bio.h"
#include "ssl/cert_validate_message.h"
#include "ssl/Config.h"
#include "ssl/helper.h"
unsigned long ssl_lib_error = SSL_ERROR_NONE;
SSL *ssl = fd_table[fd].ssl;
int ret;
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
if ((ret = SSL_connect(ssl)) <= 0) {
int ssl_error = SSL_get_error(ssl, ret);
return;
case SSL_ERROR_WANT_WRITE:
+ if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice && srvBio->holdWrite()) {
+ debugs(81, DBG_IMPORTANT, "hold write on SSL connection on FD " << fd);
+ checkForPeekAndSplice();
+ return;
+ }
Comm::SetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
return;
dispatch();
}
+void
+FwdState::checkForPeekAndSplice()
+{
+ SSL *ssl = fd_table[serverConn->fd].ssl;
+ BIO *b = SSL_get_rbio(ssl);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+ debugs(83,5, "Will check for peek and splice on fd " << serverConn->fd);
+ const bool splice = false;
+ if (!splice) {
+ //Allow write, proceed with the connection
+ srvBio->holdWrite(false);
+ srvBio->recordInput(false);
+ Comm::SetSelect(serverConn->fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
+ debugs(83,5, "Retry the fwdNegotiateSSL on fd " << serverConn->fd);
+ }
+}
+
void
FwdState::sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &validationResponse)
{
assert(sslContext);
- SSL *ssl = Ssl::Create(sslContext, fd, "server https start");
+ SSL *ssl = Ssl::CreateClient(sslContext, fd, "server https start");
if (!ssl) {
ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
// TODO: create Ssl::ErrorDetail with OpenSSL-supplied error code
if (peer->sslSession)
SSL_set_session(ssl, peer->sslSession);
+ } else if (request->clientConnectionManager->sslBumpMode == Ssl::bumpPeekAndSplice) {
+ SSL *clientSsl = fd_table[request->clientConnectionManager->clientConnection->fd].ssl;
+ BIO *b = SSL_get_rbio(clientSsl);
+ Ssl::ClientBio *clnBio = static_cast<Ssl::ClientBio *>(b->ptr);
+ const Ssl::Bio::sslFeatures &features = clnBio->getFeatures();
+ if (features.sslVersion != -1) {
+ SSL_set_ssl_method(ssl, Ssl::method(features.toSquidSSLVersion()));
+ if (!features.serverName.empty())
+ SSL_set_tlsext_host_name(ssl, features.serverName.c_str());
+ if (!features.clientRequestedCiphers.empty())
+ SSL_set_cipher_list(ssl, features.clientRequestedCiphers.c_str());
+#ifdef SSL_OP_NO_COMPRESSION /* XXX: OpenSSL 0.9.8k lacks SSL_OP_NO_COMPRESSION */
+ if (features.compressMethod == 0)
+ SSL_set_options(ssl, SSL_OP_NO_COMPRESSION);
+#endif
+ if (features.sslVersion >= 3) {
+ b = SSL_get_rbio(ssl);
+ Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+ srvBio->setClientRandom(features.client_random);
+ srvBio->recordInput(true);
+ }
+ }
} else {
// While we are peeking at the certificate, we may not know the server
// name that the client will request (after interception or CONNECT)
void connectStart();
void connectDone(const Comm::ConnectionPointer & conn, comm_err_t status, int xerrno);
void connectTimeout(int fd);
+#if USE_SSL
void initiateSSL();
void negotiateSSL(int fd);
+ void checkForPeekAndSplice();
+#endif
bool checkRetry();
bool checkRetriable();
void dispatch();
CBDATA_NAMESPACED_CLASS_INIT(Ssl, ServerBump);
-Ssl::ServerBump::ServerBump(HttpRequest *fakeRequest, StoreEntry *e):
+Ssl::ServerBump::ServerBump(HttpRequest *fakeRequest, StoreEntry *e, Ssl::BumpMode md):
request(fakeRequest),
- sslErrors(NULL)
+ sslErrors(NULL),
+ mode(md)
{
debugs(33, 4, HERE << "will peek at " << request->GetHost() << ':' << request->port);
const char *uri = urlCanonical(request);
class ServerBump
{
public:
- explicit ServerBump(HttpRequest *fakeRequest, StoreEntry *e = NULL);
+ explicit ServerBump(HttpRequest *fakeRequest, StoreEntry *e = NULL, Ssl::BumpMode mode = Ssl::bumpServerFirst);
~ServerBump();
/// faked, minimal request; required by server-side API
StoreEntry *entry; ///< for receiving Squid-generated error messages
Ssl::X509_Pointer serverCert; ///< HTTPS server certificate
Ssl::Errors *sslErrors; ///< SSL [certificate validation] errors
+ Ssl::BumpMode mode; ///< The SSL server bump mode
private:
store_client *sc; ///< dummy client to prevent entry trimming
*/
#include "squid.h"
+#include "ssl/support.h"
/* support.cc says this is needed */
#if USE_SSL
#include "comm.h"
+#include "ip/Address.h"
+#include "fde.h"
+#include "globals.h"
#include "Mem.h"
#include "ssl/bio.h"
#if HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
#endif
+#undef DO_SSLV23
+
// TODO: fde.h should probably export these for wrappers like ours
extern int default_read_method(int, char *, int);
extern int default_write_method(int, const char *, int);
};
BIO *
-Ssl::Bio::Create(const int fd)
+Ssl::Bio::Create(const int fd, Ssl::Bio::Type type)
{
if (BIO *bio = BIO_new(&SquidMethods)) {
- BIO_int_ctrl(bio, BIO_C_SET_FD, BIO_NOCLOSE, fd);
+ BIO_int_ctrl(bio, BIO_C_SET_FD, type, fd);
return bio;
}
return NULL;
// else if (where & SSL_CB_HANDSHAKE_DONE)
// debugs(83, 9, "SSL connection established");
- debugs(83, 7, "FD " << fd_ << " now: " << where << ' ' <<
+ debugs(83, 7, "FD " << fd_ << " now: 0x" << std::hex << where << std::dec << ' ' <<
SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")");
}
+bool
+Ssl::ClientBio::isClientHello(int state)
+{
+ return (state == SSL2_ST_GET_CLIENT_HELLO_A ||
+ state == SSL3_ST_SR_CLNT_HELLO_A ||
+ state == SSL23_ST_SR_CLNT_HELLO_A ||
+ state == SSL23_ST_SR_CLNT_HELLO_B ||
+ state == SSL3_ST_SR_CLNT_HELLO_B ||
+ state == SSL3_ST_SR_CLNT_HELLO_C
+ );
+}
+
+void
+Ssl::ClientBio::stateChanged(const SSL *ssl, int where, int ret)
+{
+ Ssl::Bio::stateChanged(ssl, where, ret);
+}
+
+int
+Ssl::ClientBio::write(const char *buf, int size, BIO *table)
+{
+ if (holdWrite_) {
+ BIO_set_retry_write(table);
+ return 0;
+ }
+
+ return Ssl::Bio::write(buf, size, table);
+}
+
+const char *objToString(unsigned char const *bytes, int len)
+{
+ static std::string buf;
+ buf.clear();
+ for(int i = 0; i < len; i++ ) {
+ char tmp[3];
+ snprintf(tmp, sizeof(tmp), "%.2x", bytes[i]);
+ buf.append(tmp);
+ }
+ return buf.c_str();
+}
+
+int
+Ssl::ClientBio::read(char *buf, int size, BIO *table)
+{
+ if (headerState < 2) {
+
+ if (rbuf.isNull())
+ rbuf.init(1024, 4096);
+
+ size = rbuf.spaceSize() > size ? size : rbuf.spaceSize();
+
+ if (!size)
+ return 0;
+
+ int bytes = Ssl::Bio::read(buf, size, table);
+ if (!bytes)
+ return 0;
+ rbuf.append(buf, bytes);
+ debugs(83, 7, "rbuf size: " << rbuf.contentSize());
+ }
+
+ if (headerState == 0) {
+
+ const unsigned char *head = (const unsigned char *)rbuf.content();
+ const char *s = objToString(head, rbuf.contentSize());
+ debugs(83, 7, "SSL Header: " << s);
+ if (rbuf.contentSize() < 5) {
+ BIO_set_retry_read(table);
+ return 0;
+ }
+
+ if (head[0] == 0x16) {
+ debugs(83, 7, "SSL version 3 handshake message");
+ headerBytes = (head[3] << 8) + head[4];
+ debugs(83, 7, "SSL Header Size: " << headerBytes);
+#ifdef DO_SSLV23
+ } else if ((head[0] & 0x80) && head[2] == 0x01 && head[3] == 0x03) {
+ debugs(83, 7, "SSL version 2 handshake message with v3 support");
+ headerBytes = head[1];
+#endif
+ }else {
+ debugs(83, 7, "Not an SSL acceptable handshake message (SSLv2 message?)");
+ return -1;
+ }
+
+ headerState = 1; //Next state
+ }
+
+ if (headerState == 1) {
+ const unsigned char *head = (const unsigned char *)rbuf.content();
+ const char *s = objToString(head, rbuf.contentSize());
+ debugs(83, 7, "SSL Header: " << s);
+
+ if (headerBytes > rbuf.contentSize()) {
+ BIO_set_retry_read(table);
+ return -1;
+ }
+ features.get((const unsigned char *)rbuf.content());
+ headerState = 2;
+ }
+
+ if (holdRead_) {
+ debugs(83, 7, "Hold flag is set, retry latter. (Hold " << size << "bytes)");
+ BIO_set_retry_read(table);
+ return -1;
+ }
+
+ if (headerState >=2) {
+ if (rbuf.hasContent()) {
+ int bytes = (size <= rbuf.contentSize() ? size : rbuf.contentSize());
+ memcpy(buf, rbuf.content(), bytes);
+ rbuf.consume(bytes);
+ return bytes;
+ } else
+ return Ssl::Bio::read(buf, size, table);
+ }
+
+ return -1;
+}
+
+void
+Ssl::ServerBio::stateChanged(const SSL *ssl, int where, int ret)
+{
+ Ssl::Bio::stateChanged(ssl, where, ret);
+}
+
+void
+Ssl::ServerBio::setClientRandom(const unsigned char *r)
+{
+ memcpy(clientRandom, r, SSL3_RANDOM_SIZE);
+ randomSet = true;
+};
+
+int
+Ssl::ServerBio::read(char *buf, int size, BIO *table)
+{
+ int bytes = Ssl::Bio::read(buf, size, table);
+
+ if (bytes > 0 && record_) {
+ if (rbuf.isNull())
+ rbuf.init(1024, 8196);
+ rbuf.append(buf, bytes);
+ }
+ return bytes;
+}
+
+int
+Ssl::ServerBio::write(const char *buf, int size, BIO *table)
+{
+
+ if (holdWrite_) {
+ debugs(83, 7, "Hold write, for SSL connection on " << fd_);
+ BIO_set_retry_write(table);
+ return -1;
+ }
+
+ if (!helloBuild) {
+ if (
+ buf[1] >= 3 //it is an SSL Version3 message
+ && buf[0] == 0x16 // and it is a Handshake/Hello message
+ ) {
+ if (helloMsg.isNull())
+ helloMsg.init(1024, 4096);
+
+ //Hello message is the first message we write to server
+ assert(!helloMsg.hasContent());
+
+ SSL *ssl = fd_table[fd_].ssl;
+ if (randomSet && ssl && ssl->s3) {
+ assert(size > 11 + SSL3_RANDOM_SIZE);
+ helloMsg.append(buf, 11);
+ //The random number is stored in the 11 position of the
+ // message we are going to sent
+ helloMsg.append((char *)clientRandom, SSL3_RANDOM_SIZE);
+ size_t len = size - 11 - SSL3_RANDOM_SIZE;
+ helloMsg.append(buf + 11 + SSL3_RANDOM_SIZE, len);
+
+ // We need to fix the random in SSL struct:
+ memcpy(ssl->s3->client_random, clientRandom, SSL3_RANDOM_SIZE);
+ // We also need to fix the raw message in SSL struct
+ // stored in SSL->init_buf. Looks that it is used to get
+ // digest of the previous sent SSL message, to compute keys
+ // for encryption/decryption:
+ memcpy(ssl->init_buf->data + 6, clientRandom, SSL3_RANDOM_SIZE);
+
+ debugs(83, 7, "SSL HELLO message for FD " << fd_ << ": Random number is adjusted");
+ }
+ }
+ helloBuild = true;
+ helloMsgSize = helloMsg.contentSize();
+ }
+
+ if (helloMsg.hasContent()) {
+ debugs(83, 7, "buffered write for FD " << fd_);
+ int ret = Ssl::Bio::write(helloMsg.content(), helloMsg.contentSize(), table);
+ helloMsg.consume(ret);
+ if (helloMsg.hasContent()) {
+ // We need to retry sendind data.
+ // Say to openSSL to retry sending hello message
+ BIO_set_retry_write(table);
+ return -1;
+ }
+
+ // Sending hello message complete. Do not send more data for now...
+ holdWrite_ = true;
+ // The size should be less than the size of the hello message
+ assert(size >= helloMsgSize);
+ return helloMsgSize;
+ } else
+ return Ssl::Bio::write(buf, size, table);
+}
+
+void
+Ssl::ServerBio::flush(BIO *table)
+{
+ if (helloMsg.hasContent()) {
+ int ret = Ssl::Bio::write(helloMsg.content(), helloMsg.contentSize(), table);
+ helloMsg.consume(ret);
+ }
+}
+
/// initializes BIO table after allocation
static int
squid_bio_create(BIO *bi)
case BIO_C_SET_FD: {
assert(arg2);
const int fd = *static_cast<int*>(arg2);
- Ssl::Bio *bio = new Ssl::Bio(fd);
+ Ssl::Bio *bio;
+ if (arg1 == Ssl::Bio::BIO_TO_SERVER)
+ bio = new Ssl::ServerBio(fd);
+ else
+ bio = new Ssl::ClientBio(fd);
assert(!table->ptr);
table->ptr = bio;
table->init = 1;
if (table->init) {
Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr);
assert(bio);
- bio->flush();
+ bio->flush(table);
return 1;
}
return 0;
}
}
+Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1)
+{
+ memset(client_random, 0, SSL3_RANDOM_SIZE);
+}
+
+int Ssl::Bio::sslFeatures::toSquidSSLVersion() const
+{
+ if(sslVersion == SSL2_VERSION)
+ return 2;
+ else if(sslVersion == SSL3_VERSION)
+ return 3;
+ else if(sslVersion == TLS1_VERSION)
+ return 4;
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ else if(sslVersion == TLS1_1_VERSION)
+ return 5;
+ else if(sslVersion == TLS1_2_VERSION)
+ return 6;
+#endif
+ else
+ return 1;
+}
+
+bool
+Ssl::Bio::sslFeatures::get(const SSL *ssl)
+{
+ sslVersion = SSL_version(ssl);
+ debugs(83, 7, "SSL version: " << SSL_get_version(ssl) << " (" << sslVersion << ")");
+
+ if(const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))
+ serverName = server;
+ debugs(83, 7, "SNI server name: " << serverName);
+
+ if (ssl->session->compress_meth)
+ compressMethod = ssl->session->compress_meth;
+ else if(sslVersion >= 3) //if it is 3 or newer version then compression is disabled
+ compressMethod = 0;
+ debugs(83, 7, "SSL compression: " << compressMethod);
+
+ STACK_OF(SSL_CIPHER) * ciphers = NULL;
+ if (ssl->server)
+ ciphers = ssl->session->ciphers;
+ else
+ ciphers = ssl->cipher_list;
+ if (ciphers) {
+ for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) {
+ SSL_CIPHER *c = sk_SSL_CIPHER_value(ciphers, i);
+ if (c != NULL) {
+ if(!clientRequestedCiphers.empty())
+ clientRequestedCiphers.append(":");
+ clientRequestedCiphers.append(c->name);
+ }
+ }
+ }
+ debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
+
+ if (sslVersion >=3 && ssl->s3 && ssl->s3->client_random[0]) {
+ memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+ }
+
+#if 0 /* XXX: OpenSSL 0.9.8k lacks at least some of these tlsext_* fields */
+ //The following extracted for logging purpuses:
+ // TLSEXT_TYPE_ec_point_formats
+ unsigned char *p;
+ int len;
+ if (ssl->server) {
+ p = ssl->session->tlsext_ecpointformatlist;
+ len = ssl->session->tlsext_ecpointformatlist_length;
+ } else {
+ p = ssl->tlsext_ecpointformatlist;
+ len = ssl->tlsext_ecpointformatlist_length;
+ }
+ if (p) {
+ ecPointFormatList = objToString(p, len);
+ debugs(83, 7, "tlsExtension ecPointFormatList of length " << len << " :" << ecPointFormatList);
+ }
+
+ // TLSEXT_TYPE_elliptic_curves
+ if (ssl->server) {
+ p = ssl->session->tlsext_ellipticcurvelist;
+ len = ssl->session->tlsext_ellipticcurvelist_length;
+ } else {
+ p = ssl->tlsext_ellipticcurvelist;
+ len = ssl->tlsext_ellipticcurvelist_length;
+ }
+ if (p) {
+ ellipticCurves = objToString(p, len);
+ debugs(83, 7, "tlsExtension ellipticCurveList of length " << len <<" :" << ellipticCurves);
+ }
+ // TLSEXT_TYPE_opaque_prf_input
+ p = NULL;
+ if (ssl->server) {
+ if (ssl->s3 && ssl->s3->client_opaque_prf_input) {
+ p = (unsigned char *)ssl->s3->client_opaque_prf_input;
+ len = ssl->s3->client_opaque_prf_input_len;
+ }
+ } else {
+ p = (unsigned char *)ssl->tlsext_opaque_prf_input;
+ len = ssl->tlsext_opaque_prf_input_len;
+ }
+ if (p) {
+ debugs(83, 7, "tlsExtension client-opaque-prf-input of length " << len);
+ opaquePrf = objToString(p, len);
+ }
+#endif
+ return true;
+}
+
+bool
+Ssl::Bio::sslFeatures::get(const unsigned char *hello)
+{
+ // The SSL handshake message should starts with a 0x16 byte
+ if (hello[0] == 0x16) {
+ return parseV3Hello(hello);
+#ifdef DO_SSLV23
+ } else if ((hello[0] & 0x80) && hello[2] == 0x01 && hello[3] == 0x03) {
+ return parseV23Hello(hello);
+#endif
+ }
+
+ debugs(83, 7, "Not a known SSL handshake message");
+ return false;
+}
+
+bool
+Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *hello)
+{
+ debugs(83, 7, "Get fake features from v3 hello message.");
+ // The SSL version exist in the 2nd and 3rd bytes
+ sslVersion = (hello[1] << 8) | hello[2];
+ debugs(83, 7, "Get fake features. Version :" << std::hex << std::setw(8) << std::setfill('0')<< sslVersion);
+
+ // The following hello message size exist in 4th and 5th bytes
+ int helloSize = (hello[3] << 8) | hello[4];
+ helloSize += 5; //Include the 5 header bytes.
+
+ //For SSLv3 or TLSv1.* protocols we can get some more informations
+ if (hello[1] == 0x3 && hello[5] == 0x1 /*HELLO A message*/) {
+ // Get the correct version of the sub-hello message
+ sslVersion = (hello[9] << 8) | hello[10];
+ //Get Client Random number. It starts on the position 11 of hello message
+ memcpy(client_random, hello + 11, SSL3_RANDOM_SIZE);
+ debugs(83, 7, "Client random: " << objToString(client_random, SSL3_RANDOM_SIZE));
+
+ // At the position 43 (11+SSL3_RANDOM_SIZE)
+ int sessIDLen = (int)hello[43];
+ debugs(83, 7, "Session ID Length: " << sessIDLen);
+
+ //Ciphers list. It is stored after the Session ID.
+ const unsigned char *ciphers = hello + 44 + sessIDLen;
+ int ciphersLen = (ciphers[0] << 8) | ciphers[1];
+ ciphers += 2;
+ if (ciphersLen) {
+ const SSL_METHOD *method = SSLv3_method();
+ int cs = method->put_cipher_by_char(NULL, NULL);
+ assert(cs > 0);
+ for (int i = 0; i < ciphersLen; i += cs) {
+ const SSL_CIPHER *c = method->get_cipher_by_char((ciphers + i));
+ if (c != NULL) {
+ if(!clientRequestedCiphers.empty())
+ clientRequestedCiphers.append(":");
+ clientRequestedCiphers.append(c->name);
+ }
+ }
+ }
+ debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
+
+ // Compression field: 1 bytes the number of compression methods and
+ // 1 byte for each compression method
+ const unsigned char *compression = ciphers + ciphersLen;
+ if (compression[0] > 1)
+ compressMethod = 1;
+ else
+ compressMethod = 0;
+ debugs(83, 7, "SSL compression methods number: " << (int)compression[0]);
+
+ const unsigned char *extensions = compression + 1 + (int)compression[0];
+ if (extensions < hello + helloSize) {
+ int extensionsLen = (extensions[0] << 8) | extensions[1];
+ const unsigned char *ext = extensions + 2;
+ while (ext < extensions+extensionsLen){
+ short extType = (ext[0] << 8) | ext[1];
+ ext += 2;
+ short extLen = (ext[0] << 8) | ext[1];
+ ext += 2;
+ debugs(83, 7, "SSL Exntension: " << std::hex << extType << " of size:" << extLen);
+ //The SNI extension has the type 0 (extType == 0)
+ // The two first bytes indicates the length of the SNI data (should be extLen-2)
+ // The next byte is the hostname type, it should be '0' for normal hostname (ext[2] == 0)
+ // The 3rd and 4th bytes are the length of the hostname
+ if (extType == 0 && ext[2] == 0) {
+ int hostLen = (ext[3] << 8) | ext[4];
+ serverName.assign((const char *)(ext+5), hostLen);
+ debugs(83, 7, "Found server name: " << serverName);
+ }
+ ext += extLen;
+ }
+ }
+ }
+ return true;
+}
+
+bool
+Ssl::Bio::sslFeatures::parseV23Hello(const unsigned char *hello)
+{
+#ifdef DO_SSLV23
+ debugs(83, 7, "Get fake features from v23 hello message.");
+ sslVersion = (hello[3] << 8) | hello[4];
+ debugs(83, 7, "Get fake features. Version :" << std::hex << std::setw(8) << std::setfill('0')<< sslVersion);
+
+ // The following hello message size exist in 2nd byte
+ int helloSize = hello[1];
+ helloSize += 2; //Include the 2 header bytes.
+
+ //Ciphers list. It is stored after the Session ID.
+
+ int ciphersLen = (hello[5] << 8) | hello[6];
+ const unsigned char *ciphers = hello + 11;
+ if (ciphersLen) {
+ const SSL_METHOD *method = SSLv23_method();
+ int cs = method->put_cipher_by_char(NULL, NULL);
+ assert(cs > 0);
+ for (int i = 0; i < ciphersLen; i += cs) {
+ // The v2 hello messages cipher has 3 bytes.
+ // The v2 cipher has the first byte not null
+ // Because we are going to sent only v3 message we
+ // are ignoring these ciphers
+ if (ciphers[i] != 0)
+ continue;
+ const SSL_CIPHER *c = method->get_cipher_by_char((ciphers + i + 1));
+ if (c != NULL) {
+ if(!clientRequestedCiphers.empty())
+ clientRequestedCiphers.append(":");
+ clientRequestedCiphers.append(c->name);
+ }
+ }
+ }
+ debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers);
+
+ //Get Client Random number. It starts on the position 11 of hello message
+ memcpy(client_random, ciphers + ciphersLen, SSL3_RANDOM_SIZE);
+ debugs(83, 7, "Client random: " << objToString(client_random, SSL3_RANDOM_SIZE));
+
+ compressMethod = 0;
+ return true;
+#else
+ return false;
+#endif
+}
+
+std::ostream &
+Ssl::Bio::sslFeatures::print(std::ostream &os) const
+{
+ static std::string buf;
+ return os << "v" << sslVersion <<
+ " SNI:" << (serverName.empty() ? "-" : serverName) <<
+ " comp:" << compressMethod <<
+ " Ciphers:" << clientRequestedCiphers <<
+ " Random:" << objToString(client_random, SSL3_RANDOM_SIZE) <<
+ " ecPointFormats:" << ecPointFormatList <<
+ " ec:" << ellipticCurves <<
+ " opaquePrf:" << opaquePrf;
+}
+
#endif /* USE_SSL */
#ifndef SQUID_SSL_BIO_H
#define SQUID_SSL_BIO_H
+#include "MemBuf.h"
+#include <iosfwd>
#if HAVE_OPENSSL_BIO_H
#include <openssl/bio.h>
#endif
/// BIO source and sink node, handling socket I/O and monitoring SSL state
class Bio {
public:
+ enum Type {
+ BIO_TO_CLIENT = 6000,
+ BIO_TO_SERVER
+ };
+
+ /// Class to store SSL connection features
+ class sslFeatures {
+ public:
+ sslFeatures();
+ bool get(const SSL *ssl); ///< Retrieves the features from SSL object
+ bool get(const unsigned char *hello); ///< Retrieves the features from raw SSL hello message
+ bool parseV3Hello(const unsigned char *hello);
+ bool parseV23Hello(const unsigned char *hello);
+ /// Prints to os stream a human readable form of sslFeatures object
+ std::ostream & print(std::ostream &os) const;
+ /// Converts to the internal squid SSL version form the sslVersion
+ int toSquidSSLVersion() const;
+ public:
+ int sslVersion; ///< The requested/used SSL version
+ int compressMethod; ///< The requested/used compressed method
+ std::string serverName; ///< The SNI hostname, if any
+ std::string clientRequestedCiphers; ///< The client requested ciphers
+ std::string ecPointFormatList;///< tlsExtension ecPointFormatList
+ std::string ellipticCurves; ///< tlsExtension ellipticCurveList
+ std::string opaquePrf; ///< tlsExtension opaquePrf
+ /// The client random number
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ };
explicit Bio(const int anFd);
~Bio();
- int write(const char *buf, int size, BIO *table);
- int read(char *buf, int size, BIO *table);
- void flush() {} // we do not buffer (yet?)
+ /// Writes the given data to socket
+ virtual int write(const char *buf, int size, BIO *table);
+
+ /// Reads data from socket
+ virtual int read(char *buf, int size, BIO *table);
- int fd() const { return fd_; }
+ /// Flushes any buffered data to socket.
+ /// The Ssl::Bio does not buffer any data, so this method has nothing to do
+ virtual void flush(BIO *table) {}
+
+ int fd() const { return fd_; } ///< The SSL socket descriptor
/// Called by linked SSL connection whenever state changes, an alert
/// appears, or an error occurs. See SSL_set_info_callback().
- void stateChanged(const SSL *ssl, int where, int ret);
+ virtual void stateChanged(const SSL *ssl, int where, int ret);
/// Creates a low-level BIO table, creates a high-level Ssl::Bio object
/// for a given socket, and then links the two together via BIO_C_SET_FD.
- static BIO *Create(const int fd);
+ static BIO *Create(const int fd, Type type);
/// Tells ssl connection to use BIO and monitor state via stateChanged()
static void Link(SSL *ssl, BIO *bio);
-private:
+protected:
const int fd_; ///< the SSL socket we are reading and writing
};
+/// BIO node to handle socket IO for squid client side
+class ClientBio: public Bio {
+public:
+ explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), headerState(0), headerBytes(0) {}
+
+ /// The ClientBio version of the Ssl::Bio::stateChanged method
+ /// When the client hello message retrieved, fill the
+ /// "features" member with the client provided informations.
+ virtual void stateChanged(const SSL *ssl, int where, int ret);
+ /// The ClientBio version of the Ssl::Bio::write method
+ virtual int write(const char *buf, int size, BIO *table);
+ /// The ClientBio version of the Ssl::Bio::read method
+ /// If the holdRead flag is true then it does not write any data
+ /// to socket and sets the "read retry" flag of the BIO to true
+ virtual int read(char *buf, int size, BIO *table);
+ /// Return true if the client hello message received and analized
+ bool gotHello() {return features.sslVersion != -1;}
+ /// Return the SSL features requested by SSL client
+ const Bio::sslFeatures &getFeatures() const {return features;}
+ /// Prevents or allow writting on socket.
+ void hold(bool h) {holdRead_ = holdWrite_ = h;}
+
+private:
+ /// True if the SSL state corresponds to a hello message
+ bool isClientHello(int state);
+ /// The futures retrieved from client SSL hello message
+ Bio::sslFeatures features;
+ bool holdRead_; ///< The read hold state of the bio.
+ bool holdWrite_; ///< The write hold state of the bio.
+ MemBuf rbuf; ///< Used to buffer input data.
+ int headerState;
+ int headerBytes;
+};
+
+/// BIO node to handle socket IO for squid server side
+class ServerBio: public Bio {
+public:
+ explicit ServerBio(const int anFd): Bio(anFd), randomSet(false), helloMsgSize(0), helloBuild(false), holdWrite_(false), record_(false) {}
+ /// The ServerBio version of the Ssl::Bio::stateChanged method
+ virtual void stateChanged(const SSL *ssl, int where, int ret);
+ /// The ServerBio version of the Ssl::Bio::write method
+ /// If a clientRandom number is set then rewrites the raw hello message
+ /// "client random" field with the provided random number.
+ /// It may buffer the output packets.
+ virtual int write(const char *buf, int size, BIO *table);
+ /// The ServerBio version of the Ssl::Bio::read method
+ /// If the record flag is set then append the data to the rbuf member
+ virtual int read(char *buf, int size, BIO *table);
+ /// The ServerBio version of the Ssl::Bio::flush method.
+ /// Flushes any buffered data
+ virtual void flush(BIO *table);
+ /// Sets the random number to use in client SSL HELLO message
+ void setClientRandom(const unsigned char *r);
+
+ bool holdWrite() const {return holdWrite_;}
+ void holdWrite(bool h) {holdWrite_ = h;}
+ void recordInput(bool r) {record_ = r;}
+ const MemBuf &rBufData() {return rbuf;}
+private:
+ /// A random number to use as "client random" in client hello message
+ unsigned char clientRandom[SSL3_RANDOM_SIZE];
+ bool randomSet; ///< True if the clientRandom member is set and can be used
+ MemBuf helloMsg; ///< Used to buffer output data.
+ int helloMsgSize;
+ bool helloBuild; ///< True if the client hello message sent to the server
+ bool holdWrite_; ///< The write hold state of the bio.
+ bool record_;
+ MemBuf rbuf; ///< Used to buffer input data.
+};
+
+inline
+std::ostream &operator <<(std::ostream &os, Ssl::Bio::sslFeatures const &f)
+{
+ return f.print(os);
+}
+
} // namespace Ssl
#endif /* SQUID_SSL_BIO_H */
"none",
"client-first",
"server-first",
+ "peek-and-splice",
NULL
};
return sslContext;
}
-SSL_CTX *
-sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile)
+int Ssl::OpenSSLtoSquidSSLVersion(int sslVersion)
{
- int ssl_error;
+ if(sslVersion == SSL2_VERSION)
+ return 2;
+ else if(sslVersion == SSL3_VERSION)
+ return 3;
+ else if(sslVersion == TLS1_VERSION)
+ return 4;
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+ else if(sslVersion == TLS1_1_VERSION)
+ return 5;
+ else if(sslVersion == TLS1_2_VERSION)
+ return 6;
+#endif
+ else
+ return 1;
+}
+
+
#if OPENSSL_VERSION_NUMBER < 0x00909000L
- SSL_METHOD *method;
+SSL_METHOD *
#else
- const SSL_METHOD *method;
+const SSL_METHOD *
#endif
- SSL_CTX *sslContext;
- long fl = Ssl::parse_flags(flags);
+Ssl::method(int version)
+{
+ switch (version) {
- ssl_initialize();
+ case 2:
+#ifndef OPENSSL_NO_SSL2
+ debugs(83, 5, "Using SSLv2.");
+ return SSLv2_client_method();
+#else
+ debugs(83, DBG_IMPORTANT, "SSLv2 is not available in this Proxy.");
+ return NULL;
+#endif
+ break;
- if (!keyfile)
- keyfile = certfile;
+ case 3:
+ debugs(83, 5, "Using SSLv3.");
+ return SSLv3_client_method();
+ break;
- if (!certfile)
- certfile = keyfile;
+ case 4:
+ debugs(83, 5, "Using TLSv1.");
+ return TLSv1_client_method();
+ break;
+
+ case 5:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L // NP: not sure exactly which sub-version yet.
+ debugs(83, 5, "Using TLSv1.1.");
+ return TLSv1_1_client_method();
+#else
+ debugs(83, DBG_IMPORTANT, "TLSv1.1 is not available in this Proxy.");
+ return NULL;
+#endif
+ break;
+
+ case 6:
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L // NP: not sure exactly which sub-version yet.
+ debugs(83, 5, "Using TLSv1.2");
+ return TLSv1_2_client_method();
+#else
+ debugs(83, DBG_IMPORTANT, "TLSv1.2 is not available in this Proxy.");
+ return NULL;
+#endif
+ break;
+ case 1:
+
+ default:
+ debugs(83, 5, "Using SSLv2/SSLv3.");
+ return SSLv23_client_method();
+ break;
+ }
+
+ //Not reached
+ return NULL;
+}
+
+const SSL_METHOD *
+Ssl::serverMethod(int version)
+{
switch (version) {
case 2:
#ifndef OPENSSL_NO_SSL2
debugs(83, 5, "Using SSLv2.");
- method = SSLv2_client_method();
+ return SSLv2_server_method();
#else
debugs(83, DBG_IMPORTANT, "SSLv2 is not available in this Proxy.");
return NULL;
case 3:
debugs(83, 5, "Using SSLv3.");
- method = SSLv3_client_method();
+ return SSLv3_server_method();
break;
case 4:
debugs(83, 5, "Using TLSv1.");
- method = TLSv1_client_method();
+ return TLSv1_server_method();
break;
case 5:
#if OPENSSL_VERSION_NUMBER >= 0x10001000L // NP: not sure exactly which sub-version yet.
debugs(83, 5, "Using TLSv1.1.");
- method = TLSv1_1_client_method();
+ return TLSv1_1_server_method();
#else
debugs(83, DBG_IMPORTANT, "TLSv1.1 is not available in this Proxy.");
return NULL;
case 6:
#if OPENSSL_VERSION_NUMBER >= 0x10001000L // NP: not sure exactly which sub-version yet.
debugs(83, 5, "Using TLSv1.2");
- method = TLSv1_2_client_method();
+ return TLSv1_2_server_method();
#else
debugs(83, DBG_IMPORTANT, "TLSv1.2 is not available in this Proxy.");
return NULL;
default:
debugs(83, 5, "Using SSLv2/SSLv3.");
- method = SSLv23_client_method();
+ return SSLv23_server_method();
break;
}
+ //Not reached
+ return NULL;
+}
+
+SSL_CTX *
+sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile)
+{
+ int ssl_error;
+#if OPENSSL_VERSION_NUMBER < 0x00909000L
+ SSL_METHOD *method;
+#else
+ const SSL_METHOD *method;
+#endif
+ SSL_CTX *sslContext;
+ long fl = Ssl::parse_flags(flags);
+
+ ssl_initialize();
+
+ if (!keyfile)
+ keyfile = certfile;
+
+ if (!certfile)
+ certfile = keyfile;
+
+ if (!(method = Ssl::method(version)))
+ return NULL;
+
sslContext = SSL_CTX_new(method);
if (sslContext == NULL) {
/// \ingroup ServerProtocolSSLInternal
/// Create SSL context and apply ssl certificate and private key to it.
-static SSL_CTX *
-createSSLContext(Ssl::X509_Pointer & x509, Ssl::EVP_PKEY_Pointer & pkey, AnyP::PortCfg &port)
+SSL_CTX *
+Ssl::createSSLContext(Ssl::X509_Pointer & x509, Ssl::EVP_PKEY_Pointer & pkey, AnyP::PortCfg &port)
{
Ssl::SSL_CTX_Pointer sslContext(SSL_CTX_new(port.contextMethod));
return createSSLContext(cert, pkey, port);
}
+bool
+Ssl::configureSSL(SSL *ssl, CertificateProperties const &properties, AnyP::PortCfg &port)
+{
+ Ssl::X509_Pointer cert;
+ Ssl::EVP_PKEY_Pointer pkey;
+ if (!generateSslCertificate(cert, pkey, properties))
+ return false;
+
+ if (!cert)
+ return false;
+
+ if (!pkey)
+ return false;
+
+ if (!SSL_use_certificate(ssl, cert.get()))
+ return false;
+
+ if (!SSL_use_PrivateKey(ssl, pkey.get()))
+ return false;
+
+ return true;
+}
+
+bool
+Ssl::configureSSLUsingPkeyAndCertFromMemory(SSL *ssl, const char *data, AnyP::PortCfg &port)
+{
+ Ssl::X509_Pointer cert;
+ Ssl::EVP_PKEY_Pointer pkey;
+ if (!readCertAndPrivateKeyFromMemory(cert, pkey, data))
+ return false;
+
+ if (!cert || !pkey)
+ return false;
+
+ if (!SSL_use_certificate(ssl, cert.get()))
+ return false;
+
+ if (!SSL_use_PrivateKey(ssl, pkey.get()))
+ return false;
+
+ return true;
+}
+
bool Ssl::verifySslCertificate(SSL_CTX * sslContext, CertificateProperties const &properties)
{
// Temporary ssl for getting X509 certificate from SSL_CTX.
}
SSL *
-Ssl::Create(SSL_CTX *sslContext, const int fd, const char *squidCtx)
+SslCreate(SSL_CTX *sslContext, const int fd, Ssl::Bio::Type type, const char *squidCtx)
{
const char *errAction = NULL;
int errCode = 0;
if (SSL *ssl = SSL_new(sslContext)) {
// without BIO, we would call SSL_set_fd(ssl, fd) instead
- if (BIO *bio = Ssl::Bio::Create(fd)) {
+ if (BIO *bio = Ssl::Bio::Create(fd, type)) {
Ssl::Bio::Link(ssl, bio); // cannot fail
fd_table[fd].ssl = ssl;
return NULL;
}
+SSL *
+Ssl::CreateClient(SSL_CTX *sslContext, const int fd, const char *squidCtx)
+{
+ return SslCreate(sslContext, fd, Ssl::Bio::BIO_TO_SERVER, squidCtx);
+}
+
+SSL *
+Ssl::CreateServer(SSL_CTX *sslContext, const int fd, const char *squidCtx)
+{
+ return SslCreate(sslContext, fd, Ssl::Bio::BIO_TO_CLIENT, squidCtx);
+}
+
+
#endif /* USE_SSL */
typedef CbDataList<Ssl::ssl_error_t> Errors;
-/// Creates SSL connection structure and initializes SSL I/O (Comm and BIO).
+/// Creates SSL Client connection structure and initializes SSL I/O (Comm and BIO).
/// On errors, emits DBG_IMPORTANT with details and returns NULL.
-SSL *Create(SSL_CTX *sslContext, const int fd, const char *squidCtx);
+SSL *CreateClient(SSL_CTX *sslContext, const int fd, const char *squidCtx);
+
+/// Creates SSL Server connection structure and initializes SSL I/O (Comm and BIO).
+/// On errors, emits DBG_IMPORTANT with details and returns NULL.
+SSL *CreateServer(SSL_CTX *sslContext, const int fd, const char *squidCtx);
} //namespace Ssl
\ingroup ServerProtocolSSLAPI
* Supported ssl-bump modes
*/
-enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpEnd};
+enum BumpMode {bumpNone = 0, bumpClientFirst, bumpServerFirst, bumpPeekAndSplice, bumpEnd};
/**
\ingroup ServerProtocolSSLAPI
*/
SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data, AnyP::PortCfg &port);
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Create an SSL context using the provided certificate and key
+ */
+SSL_CTX * createSSLContext(Ssl::X509_Pointer & x509, Ssl::EVP_PKEY_Pointer & pkey, AnyP::PortCfg &port);
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Generates a certificate and a private key using provided properies and set it
+ * to SSL object.
+ */
+bool configureSSL(SSL *ssl, CertificateProperties const &properties, AnyP::PortCfg &port);
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * Read private key and certificate from memory and set it to SSL object
+ * using their.
+ */
+bool configureSSLUsingPkeyAndCertFromMemory(SSL *ssl, const char *data, AnyP::PortCfg &port);
+
+
/**
\ingroup ServerProtocolSSLAPI
* Adds the certificates in certList to the certificate chain of the SSL context
\return true if SNI set false otherwise
*/
bool setClientSNI(SSL *ssl, const char *fqdn);
+
+int OpenSSLtoSquidSSLVersion(int sslVersion);
+
+#if OPENSSL_VERSION_NUMBER < 0x00909000L
+SSL_METHOD *method(int version);
+#else
+const SSL_METHOD *method(int version);
+#endif
+
+const SSL_METHOD *serverMethod(int version);
} //namespace Ssl
#if _SQUID_WINDOWS_