MEDIUM: ssl: provide our own BIO.

Instead of letting the OpenSSL code handle the file descriptor directly,
provide a custom BIO, that will use the underlying XPRT to send/recv data.
This will let us implement QUIC later, and probably clean the upper layer,
if/when the SSL code provide its own subscribe code, so that the upper layers
won't have to care if we're still waiting for the handshake to complete or not.
This commit is contained in:
Olivier Houchard 2019-04-07 22:00:38 +02:00
parent e179d0e88f
commit a8955d57ed
1 changed files with 128 additions and 9 deletions

View File

@ -238,8 +238,12 @@ static struct {
.capture_cipherlist = 0,
};
static BIO_METHOD *ha_meth;
struct ssl_sock_ctx {
struct connection *conn;
SSL *ssl;
BIO *bio;
struct xprt_ops *xprt;
void *xprt_ctx;
int xprt_st; /* transport layer state, initialized to zero */
@ -250,6 +254,81 @@ struct ssl_sock_ctx {
DECLARE_STATIC_POOL(ssl_sock_ctx_pool, "ssl_sock_ctx_pool", sizeof(struct ssl_sock_ctx));
/* Methods to implement OpenSSL BIO */
static int ha_ssl_write(BIO *h, const char *buf, int num)
{
struct buffer tmpbuf;
struct ssl_sock_ctx *ctx;
int ret;
ctx = BIO_get_data(h);
tmpbuf.size = num;
tmpbuf.area = (void *)(uintptr_t)buf;
tmpbuf.data = num;
tmpbuf.head = 0;
ret = ctx->xprt->snd_buf(ctx->conn, ctx->xprt_ctx, &tmpbuf, num, 0);
if (ret == 0 && !(ctx->conn->flags & CO_FL_ERROR))
BIO_set_retry_write(h);
return ret;
}
static int ha_ssl_gets(BIO *h, char *buf, int size)
{
return 0;
}
static int ha_ssl_puts(BIO *h, const char *str)
{
return ha_ssl_write(h, str, strlen(str));
}
static int ha_ssl_read(BIO *h, char *buf, int size)
{
struct buffer tmpbuf;
struct ssl_sock_ctx *ctx;
int ret;
ctx = BIO_get_data(h);
tmpbuf.size = size;
tmpbuf.area = buf;
tmpbuf.data = 0;
tmpbuf.head = 0;
ret = ctx->xprt->rcv_buf(ctx->conn, ctx->xprt_ctx, &tmpbuf, size, 0);
if (ret == 0 && !(ctx->conn->flags & CO_FL_ERROR))
BIO_set_retry_read(h);
return ret;
}
static long ha_ssl_ctrl(BIO *h, int cmd, long arg1, void *arg2)
{
int ret = 0;
switch (cmd) {
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
ret = 1;
break;
}
return ret;
}
static int ha_ssl_new(BIO *h)
{
BIO_set_init(h, 1);
BIO_set_data(h, NULL);
BIO_clear_flags(h, ~0);
return 1;
}
static int ha_ssl_free(BIO *data)
{
return 1;
}
#if defined(USE_THREAD) && ((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER))
static HA_RWLOCK_T *ssl_rwlocks;
@ -5111,6 +5190,14 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx)
}
ctx->sent_early_data = 0;
ctx->tmp_early_data = -1;
ctx->conn = conn;
/* Only work with sockets for now, this should be adapted when we'll
* add QUIC support.
*/
ctx->xprt = xprt_get(XPRT_RAW);
if (ctx->xprt->init)
ctx->xprt->init(conn, &ctx->xprt_ctx);
if (global.maxsslconn && sslconns >= global.maxsslconn) {
conn->err_code = CO_ER_SSL_TOO_MANY;
@ -5133,11 +5220,8 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx)
conn->err_code = CO_ER_SSL_NO_MEM;
goto err;
}
/* set fd on SSL session context */
if (!SSL_set_fd(ctx->ssl, conn->handle.fd)) {
SSL_free(ctx->ssl);
ctx->ssl = NULL;
ctx->bio = BIO_new(ha_meth);
if (!ctx->bio) {
if (may_retry--) {
pool_gc(NULL);
goto retry_connect;
@ -5145,6 +5229,12 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx)
conn->err_code = CO_ER_SSL_NO_MEM;
goto err;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000
ctx->bio->ptr = ctx;
#else
BIO_set_data(ctx->bio, ctx);
#endif
SSL_set_bio(ctx->ssl, ctx->bio, ctx->bio);
/* set connection pointer */
if (!SSL_set_ex_data(ctx->ssl, ssl_app_data_index, conn)) {
@ -5195,10 +5285,8 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx)
goto err;
}
/* set fd on SSL session context */
if (!SSL_set_fd(ctx->ssl, conn->handle.fd)) {
SSL_free(ctx->ssl);
ctx->ssl = NULL;
ctx->bio = BIO_new(ha_meth);
if (!ctx->bio) {
if (may_retry--) {
pool_gc(NULL);
goto retry_accept;
@ -5206,6 +5294,12 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx)
conn->err_code = CO_ER_SSL_NO_MEM;
goto err;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000
ctx->bio->ptr = ctx;
#else
BIO_set_data(ctx->bio, ctx);
#endif
SSL_set_bio(ctx->ssl, ctx->bio, ctx->bio);
/* set connection pointer */
if (!SSL_set_ex_data(ctx->ssl, ssl_app_data_index, conn)) {
@ -9744,6 +9838,26 @@ static void __ssl_sock_init(void)
#endif
/* Load SSL string for the verbose & debug mode. */
ERR_load_SSL_strings();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ha_meth = malloc(sizeof(*ha_meth));
bzero(ha_meth, sizeof(*ha_metho));
ha_meth->bwrite = ha_ssl_write;
ha_meth->bread = ha_ssl_read;
ha_meth->ctrl = ha_ssl_ctrl;
ha_meth->create = ha_ssl_new;
ha_meth->destroy = ha_ssl_free;
ha_meth->bputs = ha_ssl_puts;
ha_meth->bgets = ha_ssl_gets;
#else
ha_meth = BIO_meth_new(0x666, "ha methods");
BIO_meth_set_write(ha_meth, ha_ssl_write);
BIO_meth_set_read(ha_meth, ha_ssl_read);
BIO_meth_set_ctrl(ha_meth, ha_ssl_ctrl);
BIO_meth_set_create(ha_meth, ha_ssl_new);
BIO_meth_set_destroy(ha_meth, ha_ssl_free);
BIO_meth_set_puts(ha_meth, ha_ssl_puts);
BIO_meth_set_gets(ha_meth, ha_ssl_gets);
#endif
}
/* Compute and register the version string */
@ -9848,6 +9962,11 @@ static void __ssl_sock_deinit(void)
#if ((OPENSSL_VERSION_NUMBER >= 0x00907000L) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_cleanup_all_ex_data();
#endif
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
free(ha_meth);
#else
BIO_meth_free(ha_meth);
#endif
}