]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: Handle sending early data to server.
authorOlivier Houchard <ohouchard@haproxy.com>
Fri, 3 Nov 2017 15:27:47 +0000 (16:27 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 8 Nov 2017 13:11:10 +0000 (14:11 +0100)
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.

include/types/connection.h
include/types/server.h
src/backend.c
src/proto_http.c
src/ssl_sock.c

index beb0b71b88ee7ed3141ebbd1dfcbbbce9b8ad338..eb674556c23695568fefe89e6544b0a07287bfbc 100644 (file)
@@ -226,6 +226,7 @@ enum {
        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_NO_TARGET,    /* unknown target (not client nor server) */
+       CO_ER_SSL_EARLY_FAILED, /* Server refused early data */
 };
 
 /* source address settings for outgoing connections */
index 4a31934d550bd231e6b2fb5ca6eaf80f6b7daf4f..76225f7d326ff146e3127c5b53e4589693433d1a 100644 (file)
@@ -167,6 +167,7 @@ enum srv_initaddr {
 #define SRV_SSL_O_NONE         0x0000
 #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_EARLY_DATA   0x400  /* Allow using early data */
 #endif
 
 struct pid_list {
index 9dbbd91984811b004300059643fc45edcd73063e..ca064b695389e315182a273d636acbc959825450 100644 (file)
@@ -1038,7 +1038,7 @@ static void assign_tproxy_address(struct stream *s)
  */
 int connect_server(struct stream *s)
 {
-       struct connection *cli_conn;
+       struct connection *cli_conn = NULL;
        struct connection *srv_conn;
        struct conn_stream *srv_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 */
                srv_conn->send_proxy_ofs = 0;
+               cli_conn = objt_conn(strm_orig(s));
+
                if (srv && srv->pp_opts) {
                        srv_conn->flags |= CO_FL_PRIVATE;
                        srv_conn->send_proxy_ofs = 1; /* must compute size */
-                       cli_conn = objt_conn(strm_orig(s));
                        if (cli_conn)
                                conn_get_to_addr(cli_conn);
                }
@@ -1208,6 +1209,15 @@ int connect_server(struct stream *s)
 
        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)
                return err;
 
index bfce2cf802547432ef1849a55ae3f8e3551c6876..05731793926882db42afa1adae01ec9753e7a0c1 100644 (file)
@@ -5147,6 +5147,17 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
                        channel_auto_close(rep);
                        rep->analysers &= AN_RES_FLT_END;
                        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;
                        channel_truncate(rep);
                        http_reply_and_close(s, txn->status, http_error_message(s));
index abb09d03eda7bf1e3cbf4e319e2bb0c2112614c7..72d9b8aeee5dfd4b019d7f2b06ee9f1672dd5a4a 100644 (file)
@@ -5210,6 +5210,22 @@ check_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:
 
@@ -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,
                            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;
                        if (ret == SSL_READ_EARLY_DATA_SUCCESS ||
                            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)
                                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) {
                                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;
+                               }
                        }
                        ret = SSL_write_early_data(conn->xprt_ctx, bo_ptr(buf), try, &written_data);
                        if (ret == 1) {
                                ret = written_data;
                                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
@@ -5600,6 +5635,13 @@ static void ssl_sock_close(struct connection *conn) {
  */
 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)
                return;
        if (!clean)
@@ -7705,6 +7747,13 @@ static int srv_parse_no_ssl(char **args, int *cur_arg, struct proxy *px, struct
        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 */
 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.
  */
 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 */
        { "check-sni",               srv_parse_check_sni,          1, 1 }, /* set SNI */
        { "check-ssl",               srv_parse_check_ssl,          0, 1 }, /* enable SSL for health checks */