changeset 8384:52d0c4832570 quic

Address validation using NEW_TOKEN frame.
author Sergey Kandaurov <pluknet@nginx.com>
date Thu, 14 May 2020 15:47:24 +0300
parents 7ea34e13937f
children fb7422074258
files src/event/ngx_event_quic.c src/event/ngx_event_quic_transport.c src/event/ngx_event_quic_transport.h
diffstat 3 files changed, 86 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/event/ngx_event_quic.c
+++ b/src/event/ngx_event_quic.c
@@ -187,6 +187,7 @@ static ngx_int_t ngx_quic_payload_handle
 static ngx_int_t ngx_quic_send_ack(ngx_connection_t *c, ngx_quic_header_t *pkt);
 static ngx_int_t ngx_quic_send_cc(ngx_connection_t *c,
     enum ssl_encryption_level_t level, ngx_uint_t err);
+static ngx_int_t ngx_quic_send_new_token(ngx_connection_t *c);
 
 static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
     ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f);
@@ -544,6 +545,7 @@ static ngx_int_t
 ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
     ngx_quic_header_t *pkt, ngx_connection_handler_pt handler)
 {
+    ngx_int_t               rc;
     ngx_uint_t              i;
     ngx_quic_tp_t          *ctp;
     ngx_quic_secrets_t     *keys;
@@ -642,7 +644,22 @@ ngx_quic_new_connection(ngx_connection_t
         return NGX_ERROR;
     }
 
-    if (tp->retry) {
+    if (pkt->token.len) {
+        rc = ngx_quic_validate_token(c, pkt);
+
+        if (rc == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
+            return NGX_ERROR;
+        }
+
+        if (rc == NGX_DECLINED) {
+            ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
+            return ngx_quic_retry(c);
+        }
+
+        /* NGX_OK */
+
+    } else if (tp->retry) {
         return ngx_quic_retry(c);
     }
 
@@ -1951,6 +1968,35 @@ ngx_quic_send_cc(ngx_connection_t *c, en
 
 
 static ngx_int_t
+ngx_quic_send_new_token(ngx_connection_t *c)
+{
+    ngx_str_t          token;
+    ngx_quic_frame_t  *frame;
+
+    if (!c->quic->tp.retry) {
+        return NGX_OK;
+    }
+
+    if (ngx_quic_new_token(c, &token) != NGX_OK) {
+        return NGX_ERROR;
+    }
+
+    frame = ngx_quic_alloc_frame(c, 0);
+    if (frame == NULL) {
+        return NGX_ERROR;
+    }
+
+    frame->level = ssl_encryption_application;
+    frame->type = NGX_QUIC_FT_NEW_TOKEN;
+    frame->u.token.length = token.len;
+    frame->u.token.data = token.data;
+    ngx_sprintf(frame->info, "NEW_TOKEN");
+    ngx_quic_queue_frame(c->quic, frame);
+
+    return NGX_OK;
+}
+
+static ngx_int_t
 ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
     ngx_quic_ack_frame_t *ack)
 {
@@ -2405,6 +2451,10 @@ ngx_quic_crypto_input(ngx_connection_t *
         ngx_sprintf(frame->info, "HANDSHAKE DONE on handshake completed");
         ngx_quic_queue_frame(c->quic, frame);
 
+        if (ngx_quic_send_new_token(c) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
         /*
          * Generating next keys before a key update is received.
          * See quic-tls 9.4 Header Protection Timing Side-Channels.
--- a/src/event/ngx_event_quic_transport.c
+++ b/src/event/ngx_event_quic_transport.c
@@ -72,6 +72,8 @@ static size_t ngx_quic_create_ack(u_char
 static size_t ngx_quic_create_crypto(u_char *p,
     ngx_quic_crypto_frame_t *crypto);
 static size_t ngx_quic_create_hs_done(u_char *p);
+static size_t ngx_quic_create_new_token(u_char *p,
+    ngx_quic_new_token_frame_t *token);
 static size_t ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf);
 static size_t ngx_quic_create_max_streams(u_char *p,
     ngx_quic_max_streams_frame_t *ms);
@@ -1128,6 +1130,9 @@ ngx_quic_create_frame(u_char *p, ngx_qui
     case NGX_QUIC_FT_HANDSHAKE_DONE:
         return ngx_quic_create_hs_done(p);
 
+    case NGX_QUIC_FT_NEW_TOKEN:
+        return ngx_quic_create_new_token(p, &f->u.token);
+
     case NGX_QUIC_FT_STREAM0:
     case NGX_QUIC_FT_STREAM1:
     case NGX_QUIC_FT_STREAM2:
@@ -1232,6 +1237,30 @@ ngx_quic_create_hs_done(u_char *p)
 
 
 static size_t
+ngx_quic_create_new_token(u_char *p, ngx_quic_new_token_frame_t *token)
+{
+    size_t   len;
+    u_char  *start;
+
+    if (p == NULL) {
+        len = ngx_quic_varint_len(NGX_QUIC_FT_NEW_TOKEN);
+        len += ngx_quic_varint_len(token->length);
+        len += token->length;
+
+        return len;
+    }
+
+    start = p;
+
+    ngx_quic_build_int(&p, NGX_QUIC_FT_NEW_TOKEN);
+    ngx_quic_build_int(&p, token->length);
+    p = ngx_cpymem(p, token->data, token->length);
+
+    return p - start;
+}
+
+
+static size_t
 ngx_quic_create_stream(u_char *p, ngx_quic_stream_frame_t *sf)
 {
     size_t   len;
--- a/src/event/ngx_event_quic_transport.h
+++ b/src/event/ngx_event_quic_transport.h
@@ -132,6 +132,11 @@ typedef struct {
 } ngx_quic_new_conn_id_frame_t;
 
 
+typedef struct {
+    uint64_t                                    length;
+    u_char                                     *data;
+} ngx_quic_new_token_frame_t;
+
 /*
  * common layout for CRYPTO and STREAM frames;
  * conceptually, CRYPTO frame is also a stream
@@ -242,6 +247,7 @@ struct ngx_quic_frame_s {
         ngx_quic_crypto_frame_t                 crypto;
         ngx_quic_ordered_frame_t                ord;
         ngx_quic_new_conn_id_frame_t            ncid;
+        ngx_quic_new_token_frame_t              token;
         ngx_quic_stream_frame_t                 stream;
         ngx_quic_max_data_frame_t               max_data;
         ngx_quic_close_frame_t                  close;