Mercurial > hg > nginx
comparison src/event/quic/ngx_event_quic.c @ 8686:dffb66fb783b quic
QUIC: stateless retry.
Previously, quic connection object was created when Retry packet was sent.
This is neither necessary nor convenient, and contradicts the idea of retry:
protecting from bad clients and saving server resources.
Now, the connection is not created, token is verified cryptographically
instead of holding it in connection.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Fri, 29 Jan 2021 15:53:47 +0300 |
parents | 7df607cb2d11 |
children | 1c6343bd7933 |
comparison
equal
deleted
inserted
replaced
8685:dbe33ef9cd9a | 8686:dffb66fb783b |
---|---|
7 #include <ngx_config.h> | 7 #include <ngx_config.h> |
8 #include <ngx_core.h> | 8 #include <ngx_core.h> |
9 #include <ngx_event.h> | 9 #include <ngx_event.h> |
10 #include <ngx_event_quic_transport.h> | 10 #include <ngx_event_quic_transport.h> |
11 #include <ngx_event_quic_protection.h> | 11 #include <ngx_event_quic_protection.h> |
12 #include <ngx_sha1.h> | |
12 | 13 |
13 | 14 |
14 /* 0-RTT and 1-RTT data exist in the same packet number space, | 15 /* 0-RTT and 1-RTT data exist in the same packet number space, |
15 * so we have 3 packet number spaces: | 16 * so we have 3 packet number spaces: |
16 * | 17 * |
111 | 112 |
112 uint32_t version; | 113 uint32_t version; |
113 ngx_str_t scid; /* initial client ID */ | 114 ngx_str_t scid; /* initial client ID */ |
114 ngx_str_t dcid; /* server (our own) ID */ | 115 ngx_str_t dcid; /* server (our own) ID */ |
115 ngx_str_t odcid; /* original server ID */ | 116 ngx_str_t odcid; /* original server ID */ |
116 ngx_str_t token; | |
117 | 117 |
118 struct sockaddr *sockaddr; | 118 struct sockaddr *sockaddr; |
119 socklen_t socklen; | 119 socklen_t socklen; |
120 | 120 |
121 ngx_queue_t client_ids; | 121 ngx_queue_t client_ids; |
173 unsigned error_app:1; | 173 unsigned error_app:1; |
174 unsigned send_timer_set:1; | 174 unsigned send_timer_set:1; |
175 unsigned closing:1; | 175 unsigned closing:1; |
176 unsigned draining:1; | 176 unsigned draining:1; |
177 unsigned key_phase:1; | 177 unsigned key_phase:1; |
178 unsigned in_retry:1; | |
179 unsigned initialized:1; | |
180 unsigned validated:1; | 178 unsigned validated:1; |
181 } ngx_quic_connection_t; | 179 } ngx_quic_connection_t; |
182 | 180 |
183 | 181 |
184 typedef struct { | 182 typedef struct { |
233 ngx_quic_header_t *inpkt); | 231 ngx_quic_header_t *inpkt); |
234 static ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id); | 232 static ngx_int_t ngx_quic_create_server_id(ngx_connection_t *c, u_char *id); |
235 #if (NGX_QUIC_BPF) | 233 #if (NGX_QUIC_BPF) |
236 static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id); | 234 static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id); |
237 #endif | 235 #endif |
238 static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c); | 236 static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c, |
239 static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token); | 237 ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); |
238 static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key, | |
239 ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry); | |
240 static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, | |
241 u_char buf[20]); | |
240 static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c, | 242 static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c, |
241 ngx_quic_header_t *pkt); | 243 u_char *key, ngx_quic_header_t *pkt); |
242 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); | 244 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); |
243 static ngx_inline size_t ngx_quic_max_udp_payload(ngx_connection_t *c); | 245 static ngx_inline size_t ngx_quic_max_udp_payload(ngx_connection_t *c); |
244 static void ngx_quic_input_handler(ngx_event_t *rev); | 246 static void ngx_quic_input_handler(ngx_event_t *rev); |
245 | 247 |
246 static void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc); | 248 static void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc); |
251 | 253 |
252 static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b, | 254 static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b, |
253 ngx_quic_conf_t *conf); | 255 ngx_quic_conf_t *conf); |
254 static ngx_int_t ngx_quic_process_packet(ngx_connection_t *c, | 256 static ngx_int_t ngx_quic_process_packet(ngx_connection_t *c, |
255 ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); | 257 ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); |
256 static ngx_int_t ngx_quic_init_secrets(ngx_connection_t *c); | 258 static ngx_int_t ngx_quic_send_early_cc(ngx_connection_t *c, |
259 ngx_quic_header_t *inpkt, ngx_uint_t err, const char *reason); | |
257 static void ngx_quic_discard_ctx(ngx_connection_t *c, | 260 static void ngx_quic_discard_ctx(ngx_connection_t *c, |
258 enum ssl_encryption_level_t level); | 261 enum ssl_encryption_level_t level); |
259 static ngx_int_t ngx_quic_check_peer(ngx_quic_connection_t *qc, | 262 static ngx_int_t ngx_quic_check_peer(ngx_quic_connection_t *qc, |
260 ngx_quic_header_t *pkt); | 263 ngx_quic_header_t *pkt); |
261 static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c, | 264 static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c, |
671 } | 674 } |
672 | 675 |
673 p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : ""); | 676 p = ngx_slprintf(p, last, "%s", qc->closing ? " closing" : ""); |
674 p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : ""); | 677 p = ngx_slprintf(p, last, "%s", qc->draining ? " draining" : ""); |
675 p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : ""); | 678 p = ngx_slprintf(p, last, "%s", qc->key_phase ? " kp" : ""); |
676 p = ngx_slprintf(p, last, "%s", qc->in_retry ? " retry" : ""); | |
677 p = ngx_slprintf(p, last, "%s", qc->validated? " valid" : ""); | 679 p = ngx_slprintf(p, last, "%s", qc->validated? " valid" : ""); |
678 | 680 |
679 } else { | 681 } else { |
680 p = ngx_slprintf(p, last, " early"); | 682 p = ngx_slprintf(p, last, " early"); |
681 } | 683 } |
1012 return; | 1014 return; |
1013 } | 1015 } |
1014 | 1016 |
1015 qc = ngx_quic_get_connection(c); | 1017 qc = ngx_quic_get_connection(c); |
1016 | 1018 |
1017 ngx_add_timer(c->read, qc->in_retry ? NGX_QUIC_RETRY_TIMEOUT | 1019 if (qc == NULL) { |
1018 : qc->tp.max_idle_timeout); | 1020 ngx_quic_close_connection(c, NGX_DONE); |
1021 return; | |
1022 } | |
1023 | |
1024 ngx_add_timer(c->read, qc->tp.max_idle_timeout); | |
1025 ngx_quic_connstate_dbg(c); | |
1019 | 1026 |
1020 c->read->handler = ngx_quic_input_handler; | 1027 c->read->handler = ngx_quic_input_handler; |
1021 | 1028 |
1022 ngx_quic_connstate_dbg(c); | |
1023 return; | 1029 return; |
1024 } | 1030 } |
1025 | 1031 |
1026 | 1032 |
1027 static ngx_quic_connection_t * | 1033 static ngx_quic_connection_t * |
1121 ngx_max(2 * qc->tp.max_udp_payload_size, | 1127 ngx_max(2 * qc->tp.max_udp_payload_size, |
1122 14720)); | 1128 14720)); |
1123 qc->congestion.ssthresh = (size_t) -1; | 1129 qc->congestion.ssthresh = (size_t) -1; |
1124 qc->congestion.recovery_start = ngx_current_msec; | 1130 qc->congestion.recovery_start = ngx_current_msec; |
1125 | 1131 |
1126 qc->odcid.len = pkt->dcid.len; | 1132 qc->odcid.len = pkt->odcid.len; |
1127 qc->odcid.data = ngx_pstrdup(c->pool, &pkt->dcid); | 1133 qc->odcid.data = ngx_pstrdup(c->pool, &pkt->odcid); |
1128 if (qc->odcid.data == NULL) { | 1134 if (qc->odcid.data == NULL) { |
1129 return NULL; | 1135 return NULL; |
1130 } | 1136 } |
1131 | 1137 |
1132 qc->dcid.len = NGX_QUIC_SERVER_CID_LEN; | 1138 qc->dcid.len = NGX_QUIC_SERVER_CID_LEN; |
1142 #if (NGX_QUIC_DRAFT_VERSION >= 28) | 1148 #if (NGX_QUIC_DRAFT_VERSION >= 28) |
1143 qc->tp.original_dcid = qc->odcid; | 1149 qc->tp.original_dcid = qc->odcid; |
1144 #endif | 1150 #endif |
1145 qc->tp.initial_scid = qc->dcid; | 1151 qc->tp.initial_scid = qc->dcid; |
1146 | 1152 |
1153 if (pkt->validated && pkt->retried) { | |
1154 qc->tp.retry_scid.len = pkt->dcid.len; | |
1155 qc->tp.retry_scid.data = ngx_pstrdup(c->pool, &pkt->dcid); | |
1156 if (qc->tp.retry_scid.data == NULL) { | |
1157 return NULL; | |
1158 } | |
1159 } | |
1160 | |
1147 qc->scid.len = pkt->scid.len; | 1161 qc->scid.len = pkt->scid.len; |
1148 qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); | 1162 qc->scid.data = ngx_pstrdup(c->pool, &pkt->scid); |
1149 if (qc->scid.data == NULL) { | 1163 if (qc->scid.data == NULL) { |
1150 return NULL; | 1164 return NULL; |
1151 } | 1165 } |
1152 ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len); | |
1153 | 1166 |
1154 cid = ngx_quic_alloc_client_id(c, qc); | 1167 cid = ngx_quic_alloc_client_id(c, qc); |
1155 if (cid == NULL) { | 1168 if (cid == NULL) { |
1156 return NULL; | 1169 return NULL; |
1157 } | 1170 } |
1163 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); | 1176 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); |
1164 qc->nclient_ids++; | 1177 qc->nclient_ids++; |
1165 qc->client_seqnum = 0; | 1178 qc->client_seqnum = 0; |
1166 | 1179 |
1167 qc->server_seqnum = NGX_QUIC_UNSET_PN; | 1180 qc->server_seqnum = NGX_QUIC_UNSET_PN; |
1181 | |
1182 if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &pkt->dcid) | |
1183 != NGX_OK) | |
1184 { | |
1185 return NULL; | |
1186 } | |
1187 | |
1188 c->udp = &qc->udp; | |
1189 | |
1190 if (ngx_quic_insert_server_id(c, &qc->odcid) == NULL) { | |
1191 return NULL; | |
1192 } | |
1193 | |
1194 qc->server_seqnum = 0; | |
1195 | |
1196 if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) { | |
1197 return NULL; | |
1198 } | |
1199 | |
1200 qc->validated = pkt->validated; | |
1168 | 1201 |
1169 return qc; | 1202 return qc; |
1170 } | 1203 } |
1171 | 1204 |
1172 | 1205 |
1342 | 1375 |
1343 #endif | 1376 #endif |
1344 | 1377 |
1345 | 1378 |
1346 static ngx_int_t | 1379 static ngx_int_t |
1347 ngx_quic_send_retry(ngx_connection_t *c) | 1380 ngx_quic_send_retry(ngx_connection_t *c, ngx_quic_conf_t *conf, |
1348 { | 1381 ngx_quic_header_t *inpkt) |
1349 ssize_t len; | 1382 { |
1350 ngx_str_t res, token; | 1383 time_t expires; |
1351 ngx_quic_header_t pkt; | 1384 ssize_t len; |
1352 ngx_quic_connection_t *qc; | 1385 ngx_str_t res, token; |
1353 u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE]; | 1386 ngx_quic_header_t pkt; |
1354 | 1387 |
1355 qc = ngx_quic_get_connection(c); | 1388 u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE]; |
1356 | 1389 u_char dcid[NGX_QUIC_SERVER_CID_LEN]; |
1357 if (ngx_quic_new_token(c, &token) != NGX_OK) { | 1390 |
1391 expires = ngx_time() + NGX_QUIC_RETRY_LIFETIME; | |
1392 | |
1393 if (ngx_quic_new_token(c, conf->token_key, &token, &inpkt->dcid, expires, 1) | |
1394 != NGX_OK) | |
1395 { | |
1358 return NGX_ERROR; | 1396 return NGX_ERROR; |
1359 } | 1397 } |
1360 | 1398 |
1361 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | 1399 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); |
1362 pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY; | 1400 pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY; |
1363 pkt.version = qc->version; | 1401 pkt.version = inpkt->version; |
1364 pkt.log = c->log; | 1402 pkt.log = c->log; |
1365 pkt.odcid = qc->odcid; | 1403 |
1366 pkt.dcid = qc->scid; | 1404 pkt.odcid = inpkt->dcid; |
1367 pkt.scid = qc->dcid; | 1405 pkt.dcid = inpkt->scid; |
1406 | |
1407 /* TODO: generate routable dcid */ | |
1408 if (RAND_bytes(dcid, NGX_QUIC_SERVER_CID_LEN) != 1) { | |
1409 return NGX_ERROR; | |
1410 } | |
1411 | |
1412 pkt.scid.len = NGX_QUIC_SERVER_CID_LEN; | |
1413 pkt.scid.data = dcid; | |
1414 | |
1368 pkt.token = token; | 1415 pkt.token = token; |
1369 | 1416 |
1370 res.data = buf; | 1417 res.data = buf; |
1371 | 1418 |
1372 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { | 1419 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { |
1381 len = ngx_quic_send(c, res.data, res.len); | 1428 len = ngx_quic_send(c, res.data, res.len); |
1382 if (len == NGX_ERROR) { | 1429 if (len == NGX_ERROR) { |
1383 return NGX_ERROR; | 1430 return NGX_ERROR; |
1384 } | 1431 } |
1385 | 1432 |
1386 qc->token = token; | 1433 ngx_log_debug(NGX_LOG_DEBUG_EVENT, c->log, 0, |
1387 #if (NGX_QUIC_DRAFT_VERSION < 28) | 1434 "quic retry packet sent to %xV", &pkt.dcid); |
1388 qc->tp.original_dcid = qc->odcid; | 1435 |
1389 #endif | 1436 /* |
1390 qc->tp.retry_scid = qc->dcid; | 1437 * quic-transport 17.2.5.1: A server MUST NOT send more than one Retry |
1391 qc->in_retry = 1; | 1438 * packet in response to a single UDP datagram. |
1392 | 1439 * NGX_DONE will stop quic_input() from processing further |
1393 if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) { | 1440 */ |
1394 return NGX_ERROR; | 1441 return NGX_DONE; |
1395 } | |
1396 | |
1397 return NGX_OK; | |
1398 } | 1442 } |
1399 | 1443 |
1400 | 1444 |
1401 static ngx_int_t | 1445 static ngx_int_t |
1402 ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token) | 1446 ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token, |
1403 { | 1447 ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry) |
1404 int len, iv_len; | 1448 { |
1405 u_char *data, *p, *key, *iv; | 1449 int len, iv_len; |
1406 ngx_msec_t now; | 1450 u_char *p, *iv; |
1407 EVP_CIPHER_CTX *ctx; | 1451 EVP_CIPHER_CTX *ctx; |
1408 const EVP_CIPHER *cipher; | 1452 const EVP_CIPHER *cipher; |
1409 struct sockaddr_in *sin; | 1453 |
1410 #if (NGX_HAVE_INET6) | 1454 u_char in[NGX_QUIC_MAX_TOKEN_SIZE]; |
1411 struct sockaddr_in6 *sin6; | 1455 |
1412 #endif | 1456 ngx_quic_address_hash(c, !is_retry, in); |
1413 ngx_quic_connection_t *qc; | 1457 |
1414 u_char in[NGX_QUIC_MAX_TOKEN_SIZE]; | 1458 p = in + 20; |
1415 | 1459 |
1416 switch (c->sockaddr->sa_family) { | 1460 p = ngx_cpymem(p, &exp, sizeof(time_t)); |
1417 | 1461 |
1418 #if (NGX_HAVE_INET6) | 1462 *p++ = is_retry ? 1 : 0; |
1419 case AF_INET6: | 1463 |
1420 sin6 = (struct sockaddr_in6 *) c->sockaddr; | 1464 if (odcid) { |
1421 | 1465 *p++ = odcid->len; |
1422 len = sizeof(struct in6_addr); | 1466 p = ngx_cpymem(p, odcid->data, odcid->len); |
1423 data = sin6->sin6_addr.s6_addr; | 1467 |
1424 | 1468 } else { |
1425 break; | 1469 *p++ = 0; |
1426 #endif | 1470 } |
1427 | 1471 |
1428 #if (NGX_HAVE_UNIX_DOMAIN) | 1472 len = p - in; |
1429 case AF_UNIX: | |
1430 | |
1431 len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(now)); | |
1432 data = c->addr_text.data; | |
1433 | |
1434 break; | |
1435 #endif | |
1436 | |
1437 default: /* AF_INET */ | |
1438 sin = (struct sockaddr_in *) c->sockaddr; | |
1439 | |
1440 len = sizeof(in_addr_t); | |
1441 data = (u_char *) &sin->sin_addr; | |
1442 | |
1443 break; | |
1444 } | |
1445 | |
1446 p = ngx_cpymem(in, data, len); | |
1447 | |
1448 now = ngx_current_msec; | |
1449 len += sizeof(now); | |
1450 ngx_memcpy(p, &now, sizeof(now)); | |
1451 | 1473 |
1452 cipher = EVP_aes_256_cbc(); | 1474 cipher = EVP_aes_256_cbc(); |
1453 iv_len = EVP_CIPHER_iv_length(cipher); | 1475 iv_len = EVP_CIPHER_iv_length(cipher); |
1454 | 1476 |
1455 token->len = iv_len + len + EVP_CIPHER_block_size(cipher); | 1477 token->len = iv_len + len + EVP_CIPHER_block_size(cipher); |
1461 ctx = EVP_CIPHER_CTX_new(); | 1483 ctx = EVP_CIPHER_CTX_new(); |
1462 if (ctx == NULL) { | 1484 if (ctx == NULL) { |
1463 return NGX_ERROR; | 1485 return NGX_ERROR; |
1464 } | 1486 } |
1465 | 1487 |
1466 qc = ngx_quic_get_connection(c); | |
1467 key = qc->conf->token_key; | |
1468 iv = token->data; | 1488 iv = token->data; |
1469 | 1489 |
1470 if (RAND_bytes(iv, iv_len) <= 0 | 1490 if (RAND_bytes(iv, iv_len) <= 0 |
1471 || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv)) | 1491 || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv)) |
1472 { | 1492 { |
1499 | 1519 |
1500 return NGX_OK; | 1520 return NGX_OK; |
1501 } | 1521 } |
1502 | 1522 |
1503 | 1523 |
1524 static void | |
1525 ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port, u_char buf[20]) | |
1526 { | |
1527 size_t len; | |
1528 u_char *data; | |
1529 ngx_sha1_t sha1; | |
1530 struct sockaddr_in *sin; | |
1531 #if (NGX_HAVE_INET6) | |
1532 struct sockaddr_in6 *sin6; | |
1533 #endif | |
1534 | |
1535 len = (size_t) c->socklen; | |
1536 data = (u_char *) c->sockaddr; | |
1537 | |
1538 if (no_port) { | |
1539 switch (c->sockaddr->sa_family) { | |
1540 | |
1541 #if (NGX_HAVE_INET6) | |
1542 case AF_INET6: | |
1543 sin6 = (struct sockaddr_in6 *) c->sockaddr; | |
1544 | |
1545 len = sizeof(struct in6_addr); | |
1546 data = sin6->sin6_addr.s6_addr; | |
1547 | |
1548 break; | |
1549 #endif | |
1550 | |
1551 case AF_INET: | |
1552 sin = (struct sockaddr_in *) c->sockaddr; | |
1553 | |
1554 len = sizeof(in_addr_t); | |
1555 data = (u_char *) &sin->sin_addr; | |
1556 | |
1557 break; | |
1558 } | |
1559 } | |
1560 | |
1561 ngx_sha1_init(&sha1); | |
1562 ngx_sha1_update(&sha1, data, len); | |
1563 ngx_sha1_final(buf, &sha1); | |
1564 } | |
1565 | |
1566 | |
1504 static ngx_int_t | 1567 static ngx_int_t |
1505 ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt) | 1568 ngx_quic_validate_token(ngx_connection_t *c, u_char *key, |
1506 { | 1569 ngx_quic_header_t *pkt) |
1507 int len, tlen, iv_len; | 1570 { |
1508 u_char *key, *iv, *p, *data; | 1571 int len, tlen, iv_len; |
1509 ngx_msec_t msec; | 1572 u_char *iv, *p; |
1510 EVP_CIPHER_CTX *ctx; | 1573 time_t now, exp; |
1511 const EVP_CIPHER *cipher; | 1574 size_t total; |
1512 struct sockaddr_in *sin; | 1575 ngx_str_t odcid; |
1513 #if (NGX_HAVE_INET6) | 1576 EVP_CIPHER_CTX *ctx; |
1514 struct sockaddr_in6 *sin6; | 1577 const EVP_CIPHER *cipher; |
1515 #endif | 1578 |
1516 ngx_quic_connection_t *qc; | 1579 u_char addr_hash[20]; |
1517 u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE]; | 1580 u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE]; |
1518 | 1581 |
1519 qc = ngx_quic_get_connection(c); | 1582 /* Retry token or NEW_TOKEN in a previous connection */ |
1520 | |
1521 /* Retry token */ | |
1522 | |
1523 if (qc->token.len) { | |
1524 if (pkt->token.len != qc->token.len) { | |
1525 goto bad_token; | |
1526 } | |
1527 | |
1528 if (ngx_memcmp(pkt->token.data, qc->token.data, pkt->token.len) != 0) { | |
1529 goto bad_token; | |
1530 } | |
1531 | |
1532 return NGX_OK; | |
1533 } | |
1534 | |
1535 /* NEW_TOKEN in a previous connection */ | |
1536 | 1583 |
1537 cipher = EVP_aes_256_cbc(); | 1584 cipher = EVP_aes_256_cbc(); |
1538 key = qc->conf->token_key; | |
1539 iv = pkt->token.data; | 1585 iv = pkt->token.data; |
1540 iv_len = EVP_CIPHER_iv_length(cipher); | 1586 iv_len = EVP_CIPHER_iv_length(cipher); |
1541 | 1587 |
1542 /* sanity checks */ | 1588 /* sanity checks */ |
1543 | 1589 |
1544 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) { | 1590 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) { |
1545 goto bad_token; | 1591 goto garbage; |
1546 } | 1592 } |
1547 | 1593 |
1548 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) { | 1594 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) { |
1549 goto bad_token; | 1595 goto garbage; |
1550 } | 1596 } |
1551 | 1597 |
1552 ctx = EVP_CIPHER_CTX_new(); | 1598 ctx = EVP_CIPHER_CTX_new(); |
1553 if (ctx == NULL) { | 1599 if (ctx == NULL) { |
1554 return NGX_ERROR; | 1600 return NGX_ERROR; |
1562 p = pkt->token.data + iv_len; | 1608 p = pkt->token.data + iv_len; |
1563 len = pkt->token.len - iv_len; | 1609 len = pkt->token.len - iv_len; |
1564 | 1610 |
1565 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) { | 1611 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) { |
1566 EVP_CIPHER_CTX_free(ctx); | 1612 EVP_CIPHER_CTX_free(ctx); |
1567 goto bad_token; | 1613 goto garbage; |
1568 } | 1614 } |
1615 total = len; | |
1569 | 1616 |
1570 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) { | 1617 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) { |
1571 EVP_CIPHER_CTX_free(ctx); | 1618 EVP_CIPHER_CTX_free(ctx); |
1619 goto garbage; | |
1620 } | |
1621 total += tlen; | |
1622 | |
1623 EVP_CIPHER_CTX_free(ctx); | |
1624 | |
1625 if (total < (20 + sizeof(time_t) + 2)) { | |
1626 goto garbage; | |
1627 } | |
1628 | |
1629 p = tdec + 20; | |
1630 | |
1631 ngx_memcpy(&exp, p, sizeof(time_t)); | |
1632 p += sizeof(time_t); | |
1633 | |
1634 pkt->retried = (*p++ == 1); | |
1635 | |
1636 ngx_quic_address_hash(c, !pkt->retried, addr_hash); | |
1637 | |
1638 if (ngx_memcmp(tdec, addr_hash, 20) != 0) { | |
1572 goto bad_token; | 1639 goto bad_token; |
1573 } | 1640 } |
1574 | 1641 |
1575 EVP_CIPHER_CTX_free(ctx); | 1642 odcid.len = *p++; |
1576 | 1643 if (odcid.len) { |
1577 switch (c->sockaddr->sa_family) { | 1644 if (odcid.len > NGX_QUIC_MAX_CID_LEN) { |
1578 | 1645 goto bad_token; |
1579 #if (NGX_HAVE_INET6) | 1646 } |
1580 case AF_INET6: | 1647 |
1581 sin6 = (struct sockaddr_in6 *) c->sockaddr; | 1648 if ((size_t)(tdec + total - p) < odcid.len) { |
1582 | 1649 goto bad_token; |
1583 len = sizeof(struct in6_addr); | 1650 } |
1584 data = sin6->sin6_addr.s6_addr; | 1651 |
1585 | 1652 odcid.data = p; |
1586 break; | 1653 p += odcid.len; |
1587 #endif | 1654 } |
1588 | 1655 |
1589 #if (NGX_HAVE_UNIX_DOMAIN) | 1656 now = ngx_time(); |
1590 case AF_UNIX: | 1657 |
1591 | 1658 if (now > exp) { |
1592 len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(msec)); | |
1593 data = c->addr_text.data; | |
1594 | |
1595 break; | |
1596 #endif | |
1597 | |
1598 default: /* AF_INET */ | |
1599 sin = (struct sockaddr_in *) c->sockaddr; | |
1600 | |
1601 len = sizeof(in_addr_t); | |
1602 data = (u_char *) &sin->sin_addr; | |
1603 | |
1604 break; | |
1605 } | |
1606 | |
1607 if (ngx_memcmp(tdec, data, len) != 0) { | |
1608 goto bad_token; | |
1609 } | |
1610 | |
1611 ngx_memcpy(&msec, tdec + len, sizeof(msec)); | |
1612 | |
1613 if (ngx_current_msec - msec > NGX_QUIC_RETRY_LIFETIME) { | |
1614 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token"); | 1659 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic expired token"); |
1615 return NGX_DECLINED; | 1660 return NGX_DECLINED; |
1616 } | 1661 } |
1617 | 1662 |
1663 if (odcid.len) { | |
1664 pkt->odcid.len = odcid.len; | |
1665 pkt->odcid.data = ngx_pstrdup(c->pool, &odcid); | |
1666 if (pkt->odcid.data == NULL) { | |
1667 return NGX_ERROR; | |
1668 } | |
1669 | |
1670 } else { | |
1671 pkt->odcid = pkt->dcid; | |
1672 } | |
1673 | |
1674 pkt->validated = 1; | |
1675 | |
1618 return NGX_OK; | 1676 return NGX_OK; |
1619 | 1677 |
1678 garbage: | |
1679 | |
1680 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic garbage token"); | |
1681 | |
1682 return NGX_ABORT; | |
1683 | |
1620 bad_token: | 1684 bad_token: |
1621 | 1685 |
1622 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token"); | 1686 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token"); |
1623 | |
1624 qc->error = NGX_QUIC_ERR_INVALID_TOKEN; | |
1625 qc->error_reason = "invalid_token"; | |
1626 | 1687 |
1627 return NGX_DECLINED; | 1688 return NGX_DECLINED; |
1628 } | 1689 } |
1629 | 1690 |
1630 | 1691 |
1815 "quic ngx_quic_close_connection rc:%i", rc); | 1876 "quic ngx_quic_close_connection rc:%i", rc); |
1816 | 1877 |
1817 qc = ngx_quic_get_connection(c); | 1878 qc = ngx_quic_get_connection(c); |
1818 | 1879 |
1819 if (qc == NULL) { | 1880 if (qc == NULL) { |
1820 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | 1881 if (rc == NGX_ERROR) { |
1821 "quic close connection early error"); | 1882 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, |
1883 "quic close connection early error"); | |
1884 } | |
1822 | 1885 |
1823 } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) { | 1886 } else if (ngx_quic_close_quic(c, rc) == NGX_AGAIN) { |
1824 return; | 1887 return; |
1825 } | 1888 } |
1826 | 1889 |
2097 } | 2160 } |
2098 #endif | 2161 #endif |
2099 | 2162 |
2100 if (rc == NGX_ERROR) { | 2163 if (rc == NGX_ERROR) { |
2101 return NGX_ERROR; | 2164 return NGX_ERROR; |
2165 } | |
2166 | |
2167 if (rc == NGX_DONE) { | |
2168 /* stop further processing */ | |
2169 return NGX_DECLINED; | |
2102 } | 2170 } |
2103 | 2171 |
2104 if (rc == NGX_OK) { | 2172 if (rc == NGX_OK) { |
2105 good = 1; | 2173 good = 1; |
2106 } | 2174 } |
2214 } | 2282 } |
2215 | 2283 |
2216 return NGX_DECLINED; | 2284 return NGX_DECLINED; |
2217 } | 2285 } |
2218 | 2286 |
2219 if (qc->in_retry) { | |
2220 | |
2221 c->log->action = "retrying quic connection"; | |
2222 | |
2223 if (pkt->level != ssl_encryption_initial) { | |
2224 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
2225 "quic discard late retry packet"); | |
2226 return NGX_DECLINED; | |
2227 } | |
2228 | |
2229 if (!pkt->token.len) { | |
2230 ngx_log_error(NGX_LOG_INFO, c->log, 0, | |
2231 "quic discard retry packet without token"); | |
2232 return NGX_DECLINED; | |
2233 } | |
2234 | |
2235 qc->odcid.len = pkt->dcid.len; | |
2236 qc->odcid.data = ngx_pstrdup(c->pool, &pkt->dcid); | |
2237 if (qc->odcid.data == NULL) { | |
2238 return NGX_ERROR; | |
2239 } | |
2240 | |
2241 ngx_quic_clear_temp_server_ids(c); | |
2242 | |
2243 qc->dcid.len = NGX_QUIC_SERVER_CID_LEN; | |
2244 qc->dcid.data = ngx_pnalloc(c->pool, qc->dcid.len); | |
2245 if (qc->dcid.data == NULL) { | |
2246 return NGX_ERROR; | |
2247 } | |
2248 | |
2249 if (ngx_quic_create_server_id(c, qc->dcid.data) != NGX_OK) { | |
2250 return NGX_ERROR; | |
2251 } | |
2252 | |
2253 qc->server_seqnum = 0; | |
2254 | |
2255 if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) { | |
2256 return NGX_ERROR; | |
2257 } | |
2258 | |
2259 qc->tp.initial_scid = qc->dcid; | |
2260 qc->in_retry = 0; | |
2261 | |
2262 if (ngx_quic_init_secrets(c) != NGX_OK) { | |
2263 return NGX_ERROR; | |
2264 } | |
2265 | |
2266 if (ngx_quic_validate_token(c, pkt) != NGX_OK) { | |
2267 return NGX_ERROR; | |
2268 } | |
2269 | |
2270 qc->validated = 1; | |
2271 } | |
2272 | |
2273 } else { | 2287 } else { |
2274 | 2288 |
2275 if (rc == NGX_ABORT) { | 2289 if (rc == NGX_ABORT) { |
2276 return ngx_quic_negotiate_version(c, pkt); | 2290 return ngx_quic_negotiate_version(c, pkt); |
2277 } | 2291 } |
2278 | 2292 |
2279 if (pkt->level == ssl_encryption_initial) { | 2293 if (pkt->level == ssl_encryption_initial) { |
2280 | 2294 c->log->action = "processing initial packet"; |
2281 c->log->action = "creating quic connection"; | |
2282 | 2295 |
2283 if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) { | 2296 if (pkt->dcid.len < NGX_QUIC_CID_LEN_MIN) { |
2284 /* 7.2. Negotiating Connection IDs */ | 2297 /* 7.2. Negotiating Connection IDs */ |
2285 ngx_log_error(NGX_LOG_INFO, c->log, 0, | 2298 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
2286 "quic too short dcid in initial" | 2299 "quic too short dcid in initial" |
2287 " packet: len:%i", pkt->dcid.len); | 2300 " packet: len:%i", pkt->dcid.len); |
2288 return NGX_ERROR; | 2301 return NGX_ERROR; |
2289 } | 2302 } |
2290 | 2303 |
2304 /* process retry and initialize connection IDs */ | |
2305 | |
2306 if (pkt->token.len) { | |
2307 | |
2308 rc = ngx_quic_validate_token(c, conf->token_key, pkt); | |
2309 | |
2310 if (rc == NGX_ERROR) { | |
2311 /* internal error */ | |
2312 return NGX_ERROR; | |
2313 | |
2314 } else if (rc == NGX_ABORT) { | |
2315 /* token cannot be decrypted */ | |
2316 return ngx_quic_send_early_cc(c, pkt, | |
2317 NGX_QUIC_ERR_INVALID_TOKEN, | |
2318 "cannot decrypt token"); | |
2319 } else if (rc == NGX_DECLINED) { | |
2320 /* token is invalid */ | |
2321 | |
2322 if (pkt->retried) { | |
2323 /* invalid Retry token */ | |
2324 return ngx_quic_send_early_cc(c, pkt, | |
2325 NGX_QUIC_ERR_INVALID_TOKEN, | |
2326 "invalid token"); | |
2327 } else if (conf->retry) { | |
2328 /* invalid NEW_TOKEN */ | |
2329 return ngx_quic_send_retry(c, conf, pkt); | |
2330 } | |
2331 } | |
2332 | |
2333 /* NGX_OK */ | |
2334 | |
2335 } else if (conf->retry) { | |
2336 return ngx_quic_send_retry(c, conf, pkt); | |
2337 | |
2338 } else { | |
2339 pkt->odcid = pkt->dcid; | |
2340 } | |
2341 | |
2342 if (ngx_terminate || ngx_exiting) { | |
2343 if (conf->retry) { | |
2344 return ngx_quic_send_retry(c, conf, pkt); | |
2345 } | |
2346 | |
2347 return NGX_ERROR; | |
2348 } | |
2349 | |
2350 c->log->action = "creating quic connection"; | |
2351 | |
2291 qc = ngx_quic_new_connection(c, conf, pkt); | 2352 qc = ngx_quic_new_connection(c, conf, pkt); |
2292 if (qc == NULL) { | 2353 if (qc == NULL) { |
2293 return NGX_ERROR; | |
2294 } | |
2295 | |
2296 c->udp = &qc->udp; | |
2297 | |
2298 if (ngx_terminate || ngx_exiting) { | |
2299 qc->error = NGX_QUIC_ERR_CONNECTION_REFUSED; | |
2300 return NGX_ERROR; | |
2301 } | |
2302 | |
2303 if (pkt->token.len) { | |
2304 rc = ngx_quic_validate_token(c, pkt); | |
2305 | |
2306 if (rc == NGX_OK) { | |
2307 qc->validated = 1; | |
2308 | |
2309 } else if (rc == NGX_ERROR) { | |
2310 return NGX_ERROR; | |
2311 | |
2312 } else { | |
2313 /* NGX_DECLINED */ | |
2314 if (conf->retry) { | |
2315 return ngx_quic_send_retry(c); | |
2316 } | |
2317 } | |
2318 | |
2319 } else if (conf->retry) { | |
2320 return ngx_quic_send_retry(c); | |
2321 } | |
2322 | |
2323 if (ngx_quic_init_secrets(c) != NGX_OK) { | |
2324 return NGX_ERROR; | |
2325 } | |
2326 | |
2327 if (ngx_quic_insert_server_id(c, &qc->odcid) == NULL) { | |
2328 return NGX_ERROR; | |
2329 } | |
2330 | |
2331 qc->server_seqnum = 0; | |
2332 | |
2333 if (ngx_quic_insert_server_id(c, &qc->dcid) == NULL) { | |
2334 return NGX_ERROR; | 2354 return NGX_ERROR; |
2335 } | 2355 } |
2336 | 2356 |
2337 } else if (pkt->level == ssl_encryption_application) { | 2357 } else if (pkt->level == ssl_encryption_application) { |
2338 return ngx_quic_send_stateless_reset(c, conf, pkt); | 2358 return ngx_quic_send_stateless_reset(c, conf, pkt); |
2409 return ngx_quic_keys_update(c, qc->keys); | 2429 return ngx_quic_keys_update(c, qc->keys); |
2410 } | 2430 } |
2411 | 2431 |
2412 | 2432 |
2413 static ngx_int_t | 2433 static ngx_int_t |
2414 ngx_quic_init_secrets(ngx_connection_t *c) | 2434 ngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt, |
2415 { | 2435 ngx_uint_t err, const char *reason) |
2416 ngx_quic_connection_t *qc; | 2436 { |
2417 | 2437 ssize_t len; |
2418 qc = ngx_quic_get_connection(c); | 2438 ngx_str_t res; |
2419 | 2439 ngx_quic_frame_t frame; |
2420 if (ngx_quic_keys_set_initial_secret(c->pool, qc->keys, &qc->odcid) | 2440 ngx_quic_header_t pkt; |
2441 | |
2442 static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; | |
2443 static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; | |
2444 | |
2445 ngx_memzero(&frame, sizeof(ngx_quic_frame_t)); | |
2446 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | |
2447 | |
2448 frame.level = inpkt->level; | |
2449 frame.type = NGX_QUIC_FT_CONNECTION_CLOSE; | |
2450 frame.u.close.error_code = err; | |
2451 | |
2452 frame.u.close.reason.data = (u_char *) reason; | |
2453 frame.u.close.reason.len = ngx_strlen(reason); | |
2454 | |
2455 len = ngx_quic_create_frame(NULL, &frame); | |
2456 if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) { | |
2457 return NGX_ERROR; | |
2458 } | |
2459 | |
2460 ngx_quic_log_frame(c->log, &frame, 1); | |
2461 | |
2462 len = ngx_quic_create_frame(src, &frame); | |
2463 if (len == -1) { | |
2464 return NGX_ERROR; | |
2465 } | |
2466 | |
2467 pkt.keys = ngx_quic_keys_new(c->pool); | |
2468 if (pkt.keys == NULL) { | |
2469 return NGX_ERROR; | |
2470 } | |
2471 | |
2472 if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid) | |
2421 != NGX_OK) | 2473 != NGX_OK) |
2422 { | 2474 { |
2423 return NGX_ERROR; | 2475 return NGX_ERROR; |
2424 } | 2476 } |
2425 | 2477 |
2426 qc->initialized = 1; | 2478 pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG |
2479 | NGX_QUIC_PKT_INITIAL; | |
2480 | |
2481 pkt.num_len = 1; | |
2482 /* | |
2483 * pkt.num = 0; | |
2484 * pkt.trunc = 0; | |
2485 */ | |
2486 | |
2487 pkt.version = inpkt->version; | |
2488 pkt.log = c->log; | |
2489 pkt.level = inpkt->level; | |
2490 pkt.dcid = inpkt->scid; | |
2491 pkt.scid = inpkt->dcid; | |
2492 pkt.payload.data = src; | |
2493 pkt.payload.len = len; | |
2494 | |
2495 res.data = dst; | |
2496 | |
2497 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { | |
2498 return NGX_ERROR; | |
2499 } | |
2500 | |
2501 if (ngx_quic_send(c, res.data, res.len) == NGX_ERROR) { | |
2502 return NGX_ERROR; | |
2503 } | |
2427 | 2504 |
2428 return NGX_OK; | 2505 return NGX_OK; |
2429 } | 2506 } |
2430 | 2507 |
2431 | 2508 |
3154 | 3231 |
3155 if (qc->draining) { | 3232 if (qc->draining) { |
3156 return NGX_OK; | 3233 return NGX_OK; |
3157 } | 3234 } |
3158 | 3235 |
3159 if (!qc->initialized) { | |
3160 /* try to initialize secrets to send an early error */ | |
3161 if (ngx_quic_init_secrets(c) != NGX_OK) { | |
3162 return NGX_OK; | |
3163 } | |
3164 } | |
3165 | |
3166 if (qc->closing | 3236 if (qc->closing |
3167 && ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL) | 3237 && ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL) |
3168 { | 3238 { |
3169 /* dot not send CC too often */ | 3239 /* dot not send CC too often */ |
3170 return NGX_OK; | 3240 return NGX_OK; |
3195 | 3265 |
3196 | 3266 |
3197 static ngx_int_t | 3267 static ngx_int_t |
3198 ngx_quic_send_new_token(ngx_connection_t *c) | 3268 ngx_quic_send_new_token(ngx_connection_t *c) |
3199 { | 3269 { |
3270 time_t expires; | |
3200 ngx_str_t token; | 3271 ngx_str_t token; |
3201 ngx_quic_frame_t *frame; | 3272 ngx_quic_frame_t *frame; |
3202 ngx_quic_connection_t *qc; | 3273 ngx_quic_connection_t *qc; |
3203 | 3274 |
3204 qc = ngx_quic_get_connection(c); | 3275 qc = ngx_quic_get_connection(c); |
3205 | 3276 |
3206 if (!qc->conf->retry) { | 3277 if (!qc->conf->retry) { |
3207 return NGX_OK; | 3278 return NGX_OK; |
3208 } | 3279 } |
3209 | 3280 |
3210 if (ngx_quic_new_token(c, &token) != NGX_OK) { | 3281 expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME; |
3282 | |
3283 if (ngx_quic_new_token(c, qc->conf->token_key, &token, NULL, expires, 0) | |
3284 != NGX_OK) | |
3285 { | |
3211 return NGX_ERROR; | 3286 return NGX_ERROR; |
3212 } | 3287 } |
3213 | 3288 |
3214 frame = ngx_quic_alloc_frame(c); | 3289 frame = ngx_quic_alloc_frame(c); |
3215 if (frame == NULL) { | 3290 if (frame == NULL) { |