aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Homutov <vl@nginx.com>2021-04-13 14:41:52 +0300
committerVladimir Homutov <vl@nginx.com>2021-04-13 14:41:52 +0300
commite0b73cf6a2eba148e09fdbe2e16b9a53d1b546cc (patch)
tree94930907e94929794f0193ef36a710e3118a220a
parent8df0b6bb2c3477cd0666b796b4e1a7373c4a600c (diff)
downloadnginx-e0b73cf6a2eba148e09fdbe2e16b9a53d1b546cc.tar.gz
nginx-e0b73cf6a2eba148e09fdbe2e16b9a53d1b546cc.zip
QUIC: separate files for tokens related processing.
-rw-r--r--auto/modules2
-rw-r--r--src/event/quic/ngx_event_quic.c278
-rw-r--r--src/event/quic/ngx_event_quic_connection.h8
-rw-r--r--src/event/quic/ngx_event_quic_tokens.c292
-rw-r--r--src/event/quic/ngx_event_quic_tokens.h22
5 files changed, 317 insertions, 285 deletions
diff --git a/auto/modules b/auto/modules
index 925741537..fcaed67b4 100644
--- a/auto/modules
+++ b/auto/modules
@@ -1347,6 +1347,7 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YESYES ]; then
src/event/quic/ngx_event_quic_connid.h \
src/event/quic/ngx_event_quic_migration.h \
src/event/quic/ngx_event_quic_streams.h \
+ src/event/quic/ngx_event_quic_tokens.h \
src/event/quic/ngx_event_quic_ack.h \
src/event/quic/ngx_event_quic_output.h"
ngx_module_srcs="src/event/quic/ngx_event_quic.c \
@@ -1356,6 +1357,7 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YESYES ]; then
src/event/quic/ngx_event_quic_connid.c \
src/event/quic/ngx_event_quic_migration.c \
src/event/quic/ngx_event_quic_streams.c \
+ src/event/quic/ngx_event_quic_tokens.c \
src/event/quic/ngx_event_quic_ack.c \
src/event/quic/ngx_event_quic_output.c"
diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c
index 365488701..cf9e64628 100644
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -7,7 +7,6 @@
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
-#include <ngx_sha1.h>
#include <ngx_event_quic_connection.h>
@@ -42,10 +41,6 @@ static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
ngx_quic_header_t *pkt);
-static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port,
- u_char buf[20]);
-static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
- u_char *key, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
static void ngx_quic_input_handler(ngx_event_t *rev);
@@ -620,32 +615,6 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
}
-ngx_int_t
-ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
- u_char *token)
-{
- ngx_str_t tmp;
-
- tmp.data = secret;
- tmp.len = NGX_QUIC_SR_KEY_LEN;
-
- if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
- NGX_QUIC_SR_TOKEN_LEN)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
-
-#if (NGX_DEBUG)
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic stateless reset token %*xs",
- (size_t) NGX_QUIC_SR_TOKEN_LEN, token);
-#endif
-
- return NGX_OK;
-}
-
-
static ngx_int_t
ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
{
@@ -690,253 +659,6 @@ ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
-ngx_int_t
-ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token,
- ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry)
-{
- int len, iv_len;
- u_char *p, *iv;
- EVP_CIPHER_CTX *ctx;
- const EVP_CIPHER *cipher;
-
- u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
-
- ngx_quic_address_hash(c, !is_retry, in);
-
- p = in + 20;
-
- p = ngx_cpymem(p, &exp, sizeof(time_t));
-
- *p++ = is_retry ? 1 : 0;
-
- if (odcid) {
- *p++ = odcid->len;
- p = ngx_cpymem(p, odcid->data, odcid->len);
-
- } else {
- *p++ = 0;
- }
-
- len = p - in;
-
- cipher = EVP_aes_256_cbc();
- iv_len = EVP_CIPHER_iv_length(cipher);
-
- token->len = iv_len + len + EVP_CIPHER_block_size(cipher);
- token->data = ngx_pnalloc(c->pool, token->len);
- if (token->data == NULL) {
- return NGX_ERROR;
- }
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL) {
- return NGX_ERROR;
- }
-
- iv = token->data;
-
- if (RAND_bytes(iv, iv_len) <= 0
- || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
- {
- EVP_CIPHER_CTX_free(ctx);
- return NGX_ERROR;
- }
-
- token->len = iv_len;
-
- if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
- EVP_CIPHER_CTX_free(ctx);
- return NGX_ERROR;
- }
-
- token->len += len;
-
- if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
- EVP_CIPHER_CTX_free(ctx);
- return NGX_ERROR;
- }
-
- token->len += len;
-
- EVP_CIPHER_CTX_free(ctx);
-
-#ifdef NGX_QUIC_DEBUG_PACKETS
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic new token len:%uz %xV", token->len, token);
-#endif
-
- return NGX_OK;
-}
-
-
-static void
-ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20])
-{
- size_t len;
- u_char *data;
- ngx_sha1_t sha1;
- struct sockaddr_in *sin;
-#if (NGX_HAVE_INET6)
- struct sockaddr_in6 *sin6;
-#endif
-
- len = (size_t) c->socklen;
- data = (u_char *) c->sockaddr;
-
- if (no_port) {
- switch (c->sockaddr->sa_family) {
-
-#if (NGX_HAVE_INET6)
- case AF_INET6:
- sin6 = (struct sockaddr_in6 *) c->sockaddr;
-
- len = sizeof(struct in6_addr);
- data = sin6->sin6_addr.s6_addr;
-
- break;
-#endif
-
- case AF_INET:
- sin = (struct sockaddr_in *) c->sockaddr;
-
- len = sizeof(in_addr_t);
- data = (u_char *) &sin->sin_addr;
-
- break;
- }
- }
-
- ngx_sha1_init(&sha1);
- ngx_sha1_update(&sha1, data, len);
- ngx_sha1_final(buf, &sha1);
-}
-
-
-static ngx_int_t
-ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
- ngx_quic_header_t *pkt)
-{
- int len, tlen, iv_len;
- u_char *iv, *p;
- time_t now, exp;
- size_t total;
- ngx_str_t odcid;
- EVP_CIPHER_CTX *ctx;
- const EVP_CIPHER *cipher;
-
- u_char addr_hash[20];
- u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
-
- /* Retry token or NEW_TOKEN in a previous connection */
-
- cipher = EVP_aes_256_cbc();
- iv = pkt->token.data;
- iv_len = EVP_CIPHER_iv_length(cipher);
-
- /* sanity checks */
-
- if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
- goto garbage;
- }
-
- if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
- goto garbage;
- }
-
- ctx = EVP_CIPHER_CTX_new();
- if (ctx == NULL) {
- return NGX_ERROR;
- }
-
- if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
- EVP_CIPHER_CTX_free(ctx);
- return NGX_ERROR;
- }
-
- p = pkt->token.data + iv_len;
- len = pkt->token.len - iv_len;
-
- if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
- EVP_CIPHER_CTX_free(ctx);
- goto garbage;
- }
- total = len;
-
- if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
- EVP_CIPHER_CTX_free(ctx);
- goto garbage;
- }
- total += tlen;
-
- EVP_CIPHER_CTX_free(ctx);
-
- if (total < (20 + sizeof(time_t) + 2)) {
- goto garbage;
- }
-
- p = tdec + 20;
-
- ngx_memcpy(&exp, p, sizeof(time_t));
- p += sizeof(time_t);
-
- pkt->retried = (*p++ == 1);
-
- ngx_quic_address_hash(c, !pkt->retried, addr_hash);
-
- if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
- goto bad_token;
- }
-
- odcid.len = *p++;
- if (odcid.len) {
- if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
- goto bad_token;
- }
-
- if ((size_t)(tdec + total - p) < odcid.len) {
- goto bad_token;
- }
-
- odcid.data = p;
- p += odcid.len;
- }
-
- now = ngx_time();
-
- if (now > exp) {
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
- return NGX_DECLINED;
- }
-
- if (odcid.len) {
- pkt->odcid.len = odcid.len;
- pkt->odcid.data = ngx_pstrdup(c->pool, &odcid);
- if (pkt->odcid.data == NULL) {
- return NGX_ERROR;
- }
-
- } else {
- pkt->odcid = pkt->dcid;
- }
-
- pkt->validated = 1;
-
- return NGX_OK;
-
-garbage:
-
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");
-
- return NGX_ABORT;
-
-bad_token:
-
- ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
-
- return NGX_DECLINED;
-}
-
-
static ngx_int_t
ngx_quic_init_connection(ngx_connection_t *c)
{
diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h
index 6c89ab820..9e86394f7 100644
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -21,13 +21,11 @@ typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t;
#include <ngx_event_quic_migration.h>
#include <ngx_event_quic_connid.h>
#include <ngx_event_quic_streams.h>
+#include <ngx_event_quic_tokens.h>
#include <ngx_event_quic_ack.h>
#include <ngx_event_quic_output.h>
-#define NGX_QUIC_MAX_TOKEN_SIZE 64
- /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
-
/* quic-recovery, section 6.2.2, kInitialRtt */
#define NGX_QUIC_INITIAL_RTT 333 /* ms */
@@ -206,10 +204,6 @@ struct ngx_quic_connection_s {
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
void ngx_quic_shutdown_quic(ngx_connection_t *c);
-ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
- u_char *secret, u_char *token);
-ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
- ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
/********************************* DEBUG *************************************/
diff --git a/src/event/quic/ngx_event_quic_tokens.c b/src/event/quic/ngx_event_quic_tokens.c
new file mode 100644
index 000000000..cbfc356d7
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_tokens.c
@@ -0,0 +1,292 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_sha1.h>
+#include <ngx_event_quic_protection.h>
+
+
+#define NGX_QUIC_MAX_TOKEN_SIZE 64
+ /* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
+
+
+static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port,
+ u_char buf[20]);
+
+
+ngx_int_t
+ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
+ u_char *token)
+{
+ ngx_str_t tmp;
+
+ tmp.data = secret;
+ tmp.len = NGX_QUIC_SR_KEY_LEN;
+
+ if (ngx_quic_derive_key(c->log, "sr_token_key", &tmp, cid, token,
+ NGX_QUIC_SR_TOKEN_LEN)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+#if (NGX_DEBUG)
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic stateless reset token %*xs",
+ (size_t) NGX_QUIC_SR_TOKEN_LEN, token);
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token,
+ ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry)
+{
+ int len, iv_len;
+ u_char *p, *iv;
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *cipher;
+
+ u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
+
+ ngx_quic_address_hash(c, !is_retry, in);
+
+ p = in + 20;
+
+ p = ngx_cpymem(p, &exp, sizeof(time_t));
+
+ *p++ = is_retry ? 1 : 0;
+
+ if (odcid) {
+ *p++ = odcid->len;
+ p = ngx_cpymem(p, odcid->data, odcid->len);
+
+ } else {
+ *p++ = 0;
+ }
+
+ len = p - in;
+
+ cipher = EVP_aes_256_cbc();
+ iv_len = EVP_CIPHER_iv_length(cipher);
+
+ token->len = iv_len + len + EVP_CIPHER_block_size(cipher);
+ token->data = ngx_pnalloc(c->pool, token->len);
+ if (token->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ iv = token->data;
+
+ if (RAND_bytes(iv, iv_len) <= 0
+ || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
+ {
+ EVP_CIPHER_CTX_free(ctx);
+ return NGX_ERROR;
+ }
+
+ token->len = iv_len;
+
+ if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
+ EVP_CIPHER_CTX_free(ctx);
+ return NGX_ERROR;
+ }
+
+ token->len += len;
+
+ if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
+ EVP_CIPHER_CTX_free(ctx);
+ return NGX_ERROR;
+ }
+
+ token->len += len;
+
+ EVP_CIPHER_CTX_free(ctx);
+
+#ifdef NGX_QUIC_DEBUG_PACKETS
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic new token len:%uz %xV", token->len, token);
+#endif
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20])
+{
+ size_t len;
+ u_char *data;
+ ngx_sha1_t sha1;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ len = (size_t) c->socklen;
+ data = (u_char *) c->sockaddr;
+
+ if (no_port) {
+ switch (c->sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->sockaddr;
+
+ len = sizeof(struct in6_addr);
+ data = sin6->sin6_addr.s6_addr;
+
+ break;
+#endif
+
+ case AF_INET:
+ sin = (struct sockaddr_in *) c->sockaddr;
+
+ len = sizeof(in_addr_t);
+ data = (u_char *) &sin->sin_addr;
+
+ break;
+ }
+ }
+
+ ngx_sha1_init(&sha1);
+ ngx_sha1_update(&sha1, data, len);
+ ngx_sha1_final(buf, &sha1);
+}
+
+
+ngx_int_t
+ngx_quic_validate_token(ngx_connection_t *c, u_char *key,
+ ngx_quic_header_t *pkt)
+{
+ int len, tlen, iv_len;
+ u_char *iv, *p;
+ time_t now, exp;
+ size_t total;
+ ngx_str_t odcid;
+ EVP_CIPHER_CTX *ctx;
+ const EVP_CIPHER *cipher;
+
+ u_char addr_hash[20];
+ u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
+
+ /* Retry token or NEW_TOKEN in a previous connection */
+
+ cipher = EVP_aes_256_cbc();
+ iv = pkt->token.data;
+ iv_len = EVP_CIPHER_iv_length(cipher);
+
+ /* sanity checks */
+
+ if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
+ goto garbage;
+ }
+
+ if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
+ goto garbage;
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
+ EVP_CIPHER_CTX_free(ctx);
+ return NGX_ERROR;
+ }
+
+ p = pkt->token.data + iv_len;
+ len = pkt->token.len - iv_len;
+
+ if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
+ EVP_CIPHER_CTX_free(ctx);
+ goto garbage;
+ }
+ total = len;
+
+ if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
+ EVP_CIPHER_CTX_free(ctx);
+ goto garbage;
+ }
+ total += tlen;
+
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (total < (20 + sizeof(time_t) + 2)) {
+ goto garbage;
+ }
+
+ p = tdec + 20;
+
+ ngx_memcpy(&exp, p, sizeof(time_t));
+ p += sizeof(time_t);
+
+ pkt->retried = (*p++ == 1);
+
+ ngx_quic_address_hash(c, !pkt->retried, addr_hash);
+
+ if (ngx_memcmp(tdec, addr_hash, 20) != 0) {
+ goto bad_token;
+ }
+
+ odcid.len = *p++;
+ if (odcid.len) {
+ if (odcid.len > NGX_QUIC_MAX_CID_LEN) {
+ goto bad_token;
+ }
+
+ if ((size_t)(tdec + total - p) < odcid.len) {
+ goto bad_token;
+ }
+
+ odcid.data = p;
+ p += odcid.len;
+ }
+
+ now = ngx_time();
+
+ if (now > exp) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token");
+ return NGX_DECLINED;
+ }
+
+ if (odcid.len) {
+ pkt->odcid.len = odcid.len;
+ pkt->odcid.data = ngx_pstrdup(c->pool, &odcid);
+ if (pkt->odcid.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ pkt->odcid = pkt->dcid;
+ }
+
+ pkt->validated = 1;
+
+ return NGX_OK;
+
+garbage:
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token");
+
+ return NGX_ABORT;
+
+bad_token:
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
+
+ return NGX_DECLINED;
+}
diff --git a/src/event/quic/ngx_event_quic_tokens.h b/src/event/quic/ngx_event_quic_tokens.h
new file mode 100644
index 000000000..f3185db22
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_tokens.h
@@ -0,0 +1,22 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_
+#define _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
+ u_char *secret, u_char *token);
+ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
+ ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
+ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
+ u_char *key, ngx_quic_header_t *pkt);
+
+#endif /* _NGX_EVENT_QUIC_TOKENS_H_INCLUDED_ */