MINOR: ssl: Handle sending early data to server.

This adds a new keyword on the "server" line, "allow-0rtt", if set, we'll try
to send early data to the server, as long as the client sent early data, as
in case the server rejects the early data, we no longer have them, and can't
resend them, so the only option we have is to send back a 425, and we need
to be sure the client knows how to interpret it correctly.
This commit is contained in:
Olivier Houchard 2017-11-03 16:27:47 +01:00 committed by Willy Tarreau
parent cfdef2e312
commit 522eea7110
5 changed files with 78 additions and 5 deletions

View File

@ -226,6 +226,7 @@ enum {
CO_ER_SSL_HANDSHAKE_HB, /* SSL error during handshake with heartbeat present */ CO_ER_SSL_HANDSHAKE_HB, /* SSL error during handshake with heartbeat present */
CO_ER_SSL_KILLED_HB, /* Stopped a TLSv1 heartbeat attack (CVE-2014-0160) */ CO_ER_SSL_KILLED_HB, /* Stopped a TLSv1 heartbeat attack (CVE-2014-0160) */
CO_ER_SSL_NO_TARGET, /* unknown target (not client nor server) */ CO_ER_SSL_NO_TARGET, /* unknown target (not client nor server) */
CO_ER_SSL_EARLY_FAILED, /* Server refused early data */
}; };
/* source address settings for outgoing connections */ /* source address settings for outgoing connections */

View File

@ -167,6 +167,7 @@ enum srv_initaddr {
#define SRV_SSL_O_NONE 0x0000 #define SRV_SSL_O_NONE 0x0000
#define SRV_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */ #define SRV_SSL_O_NO_TLS_TICKETS 0x0100 /* disable session resumption tickets */
#define SRV_SSL_O_NO_REUSE 0x200 /* disable session reuse */ #define SRV_SSL_O_NO_REUSE 0x200 /* disable session reuse */
#define SRV_SSL_O_EARLY_DATA 0x400 /* Allow using early data */
#endif #endif
struct pid_list { struct pid_list {

View File

@ -1038,7 +1038,7 @@ static void assign_tproxy_address(struct stream *s)
*/ */
int connect_server(struct stream *s) int connect_server(struct stream *s)
{ {
struct connection *cli_conn; struct connection *cli_conn = NULL;
struct connection *srv_conn; struct connection *srv_conn;
struct conn_stream *srv_cs; struct conn_stream *srv_cs;
struct conn_stream *old_cs; struct conn_stream *old_cs;
@ -1180,10 +1180,11 @@ int connect_server(struct stream *s)
/* process the case where the server requires the PROXY protocol to be sent */ /* process the case where the server requires the PROXY protocol to be sent */
srv_conn->send_proxy_ofs = 0; srv_conn->send_proxy_ofs = 0;
cli_conn = objt_conn(strm_orig(s));
if (srv && srv->pp_opts) { if (srv && srv->pp_opts) {
srv_conn->flags |= CO_FL_PRIVATE; srv_conn->flags |= CO_FL_PRIVATE;
srv_conn->send_proxy_ofs = 1; /* must compute size */ srv_conn->send_proxy_ofs = 1; /* must compute size */
cli_conn = objt_conn(strm_orig(s));
if (cli_conn) if (cli_conn)
conn_get_to_addr(cli_conn); conn_get_to_addr(cli_conn);
} }
@ -1208,6 +1209,15 @@ int connect_server(struct stream *s)
err = si_connect(&s->si[1]); err = si_connect(&s->si[1]);
if (!reuse && cli_conn && srv &&
(srv->ssl_ctx.options & SRV_SSL_O_EARLY_DATA) &&
(cli_conn->flags & CO_FL_EARLY_DATA) &&
!channel_is_empty(si_oc(&s->si[1])) &&
srv_conn->flags & CO_FL_SSL_WAIT_HS) {
srv_conn->flags &= ~(CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN);
srv_conn->flags |= CO_FL_EARLY_SSL_HS;
}
if (err != SF_ERR_NONE) if (err != SF_ERR_NONE)
return err; return err;

View File

@ -5147,6 +5147,17 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
channel_auto_close(rep); channel_auto_close(rep);
rep->analysers &= AN_RES_FLT_END; rep->analysers &= AN_RES_FLT_END;
txn->status = 502; txn->status = 502;
/* Check to see if the server refused the early data.
* If so, just send a 425
*/
if (objt_cs(s->si[1].end)) {
struct connection *conn = objt_cs(s->si[1].end)->conn;
if (conn->err_code == CO_ER_SSL_EARLY_FAILED)
txn->status = 425;
}
s->si[1].flags |= SI_FL_NOLINGER; s->si[1].flags |= SI_FL_NOLINGER;
channel_truncate(rep); channel_truncate(rep);
http_reply_and_close(s, txn->status, http_error_message(s)); http_reply_and_close(s, txn->status, http_error_message(s));

View File

@ -5210,6 +5210,22 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
goto out_error; goto out_error;
} }
} }
#if (OPENSSL_VERSION_NUMBER >= 0x10101000L)
else {
/*
* If the server refused the early data, we have to send a
* 425 to the client, as we no longer have the data to sent
* them again.
*/
if ((conn->flags & CO_FL_EARLY_DATA) && (objt_server(conn->target))) {
if (SSL_get_early_data_status(conn->xprt_ctx) == SSL_EARLY_DATA_REJECTED) {
conn->err_code = CO_ER_SSL_EARLY_FAILED;
goto out_error;
}
}
}
#endif
reneg_ok: reneg_ok:
@ -5328,7 +5344,8 @@ static int ssl_sock_to_buf(struct connection *conn, struct buffer *buf, int coun
ret = SSL_read_early_data(conn->xprt_ctx, ret = SSL_read_early_data(conn->xprt_ctx,
bi_end(buf), try, &read_length); bi_end(buf), try, &read_length);
if (read_length > 0) if (ret == SSL_READ_EARLY_DATA_SUCCESS &&
read_length > 0)
conn->flags |= CO_FL_EARLY_DATA; conn->flags |= CO_FL_EARLY_DATA;
if (ret == SSL_READ_EARLY_DATA_SUCCESS || if (ret == SSL_READ_EARLY_DATA_SUCCESS ||
ret == SSL_READ_EARLY_DATA_FINISH) { ret == SSL_READ_EARLY_DATA_FINISH) {
@ -5465,16 +5482,34 @@ static int ssl_sock_from_buf(struct connection *conn, struct buffer *buf, int fl
if (conn->tmp_early_data == -1) if (conn->tmp_early_data == -1)
conn->tmp_early_data = 0; conn->tmp_early_data = 0;
max_early = SSL_get_max_early_data(conn->xprt_ctx); if (objt_listener(conn->target))
max_early = SSL_get_max_early_data(conn->xprt_ctx);
else {
if (SSL_get0_session(conn->xprt_ctx))
max_early = SSL_SESSION_get_max_early_data(SSL_get0_session(conn->xprt_ctx));
else
max_early = 0;
}
if (try + conn->tmp_early_data > max_early) { if (try + conn->tmp_early_data > max_early) {
try -= (try + conn->tmp_early_data) - max_early; try -= (try + conn->tmp_early_data) - max_early;
if (try <= 0) if (try <= 0) {
if (objt_server(conn->target)) {
conn->flags &= ~CO_FL_EARLY_SSL_HS;
conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN;
}
break; break;
}
} }
ret = SSL_write_early_data(conn->xprt_ctx, bo_ptr(buf), try, &written_data); ret = SSL_write_early_data(conn->xprt_ctx, bo_ptr(buf), try, &written_data);
if (ret == 1) { if (ret == 1) {
ret = written_data; ret = written_data;
conn->tmp_early_data += ret; conn->tmp_early_data += ret;
if (objt_server(conn->target)) {
conn->flags &= ~CO_FL_EARLY_SSL_HS;
conn->flags |= CO_FL_SSL_WAIT_HS | CO_FL_WAIT_L6_CONN | CO_FL_EARLY_DATA;
}
} }
} else } else
@ -5600,6 +5635,13 @@ static void ssl_sock_close(struct connection *conn) {
*/ */
static void ssl_sock_shutw(struct connection *conn, int clean) static void ssl_sock_shutw(struct connection *conn, int clean)
{ {
/* If we're done with the connection before we did the handshake
* force the handshake anyway, so that the session is in a consistent
* state
*/
if (conn->flags & CO_FL_EARLY_SSL_HS)
SSL_do_handshake(conn->xprt_ctx);
if (conn->flags & CO_FL_HANDSHAKE) if (conn->flags & CO_FL_HANDSHAKE)
return; return;
if (!clean) if (!clean)
@ -7705,6 +7747,13 @@ static int srv_parse_no_ssl(char **args, int *cur_arg, struct proxy *px, struct
return 0; return 0;
} }
/* parse the "allow-0rtt" server keyword */
static int srv_parse_allow_0rtt(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
{
newsrv->ssl_ctx.options |= SRV_SSL_O_EARLY_DATA;
return 0;
}
/* parse the "no-ssl-reuse" server keyword */ /* parse the "no-ssl-reuse" server keyword */
static int srv_parse_no_ssl_reuse(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err) static int srv_parse_no_ssl_reuse(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
{ {
@ -8558,6 +8607,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, {
* not enabled. * not enabled.
*/ */
static struct srv_kw_list srv_kws = { "SSL", { }, { static struct srv_kw_list srv_kws = { "SSL", { }, {
{ "allow-0rtt", srv_parse_allow_0rtt, 0, 1 }, /* Allow using early data on this server */
{ "ca-file", srv_parse_ca_file, 1, 1 }, /* set CAfile to process verify server cert */ { "ca-file", srv_parse_ca_file, 1, 1 }, /* set CAfile to process verify server cert */
{ "check-sni", srv_parse_check_sni, 1, 1 }, /* set SNI */ { "check-sni", srv_parse_check_sni, 1, 1 }, /* set SNI */
{ "check-ssl", srv_parse_check_ssl, 0, 1 }, /* enable SSL for health checks */ { "check-ssl", srv_parse_check_ssl, 0, 1 }, /* enable SSL for health checks */