comparison src/event/ngx_event_quic.c @ 8383:7ea34e13937f quic

Address validation using Retry packets. The behaviour is toggled with the new directive "quic_retry on|off". QUIC token construction is made suitable for issuing with NEW_TOKEN.
author Sergey Kandaurov <pluknet@nginx.com>
date Thu, 14 May 2020 15:47:18 +0300
parents b7704303a7e5
children 52d0c4832570
comparison
equal deleted inserted replaced
8382:b7704303a7e5 8383:7ea34e13937f
121 121
122 unsigned send_timer_set:1; 122 unsigned send_timer_set:1;
123 unsigned closing:1; 123 unsigned closing:1;
124 unsigned draining:1; 124 unsigned draining:1;
125 unsigned key_phase:1; 125 unsigned key_phase:1;
126 unsigned in_retry:1;
126 }; 127 };
127 128
128 129
129 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c, 130 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c,
130 ngx_quic_frame_t *frame, void *data); 131 ngx_quic_frame_t *frame, void *data);
152 153
153 static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, 154 static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
154 ngx_quic_tp_t *tp, ngx_quic_header_t *pkt, 155 ngx_quic_tp_t *tp, ngx_quic_header_t *pkt,
155 ngx_connection_handler_pt handler); 156 ngx_connection_handler_pt handler);
156 static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid); 157 static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid);
158 static ngx_int_t ngx_quic_retry(ngx_connection_t *c);
159 static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token);
160 static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
161 ngx_quic_header_t *pkt);
157 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c); 162 static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
158 static void ngx_quic_input_handler(ngx_event_t *rev); 163 static void ngx_quic_input_handler(ngx_event_t *rev);
159 164
160 static void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc); 165 static void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
161 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc); 166 static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc);
163 static ngx_int_t ngx_quic_close_streams(ngx_connection_t *c, 168 static ngx_int_t ngx_quic_close_streams(ngx_connection_t *c,
164 ngx_quic_connection_t *qc); 169 ngx_quic_connection_t *qc);
165 170
166 static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b); 171 static ngx_int_t ngx_quic_input(ngx_connection_t *c, ngx_buf_t *b);
167 static ngx_inline u_char *ngx_quic_skip_zero_padding(ngx_buf_t *b); 172 static ngx_inline u_char *ngx_quic_skip_zero_padding(ngx_buf_t *b);
173 static ngx_int_t ngx_quic_retry_input(ngx_connection_t *c,
174 ngx_quic_header_t *pkt);
168 static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c, 175 static ngx_int_t ngx_quic_initial_input(ngx_connection_t *c,
169 ngx_quic_header_t *pkt); 176 ngx_quic_header_t *pkt);
170 static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c, 177 static ngx_int_t ngx_quic_handshake_input(ngx_connection_t *c,
171 ngx_quic_header_t *pkt); 178 ngx_quic_header_t *pkt);
172 static ngx_int_t ngx_quic_early_input(ngx_connection_t *c, 179 static ngx_int_t ngx_quic_early_input(ngx_connection_t *c,
522 if (ngx_quic_new_connection(c, ssl, tp, &pkt, handler) != NGX_OK) { 529 if (ngx_quic_new_connection(c, ssl, tp, &pkt, handler) != NGX_OK) {
523 ngx_quic_close_connection(c, NGX_ERROR); 530 ngx_quic_close_connection(c, NGX_ERROR);
524 return; 531 return;
525 } 532 }
526 533
527 ngx_add_timer(c->read, c->quic->tp.max_idle_timeout); 534 ngx_add_timer(c->read, c->quic->in_retry ? NGX_QUIC_RETRY_TIMEOUT
535 : c->quic->tp.max_idle_timeout);
528 536
529 c->read->handler = ngx_quic_input_handler; 537 c->read->handler = ngx_quic_input_handler;
530 538
531 return; 539 return;
532 } 540 }
623 if (qc->scid.data == NULL) { 631 if (qc->scid.data == NULL) {
624 return NGX_ERROR; 632 return NGX_ERROR;
625 } 633 }
626 ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len); 634 ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len);
627 635
628 qc->token.len = pkt->token.len;
629 qc->token.data = ngx_pnalloc(c->pool, qc->token.len);
630 if (qc->token.data == NULL) {
631 return NGX_ERROR;
632 }
633 ngx_memcpy(qc->token.data, pkt->token.data, qc->token.len);
634
635 keys = &c->quic->keys[ssl_encryption_initial]; 636 keys = &c->quic->keys[ssl_encryption_initial];
636 637
637 if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server, 638 if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server,
638 &qc->odcid) 639 &qc->odcid)
639 != NGX_OK) 640 != NGX_OK)
640 { 641 {
641 return NGX_ERROR; 642 return NGX_ERROR;
642 } 643 }
643 644
645 if (tp->retry) {
646 return ngx_quic_retry(c);
647 }
648
644 pkt->secret = &keys->client; 649 pkt->secret = &keys->client;
645 pkt->level = ssl_encryption_initial; 650 pkt->level = ssl_encryption_initial;
646 pkt->plaintext = buf; 651 pkt->plaintext = buf;
647 652
648 ctx = ngx_quic_get_send_ctx(qc, pkt->level); 653 ctx = ngx_quic_get_send_ctx(qc, pkt->level);
698 703
699 qc->odcid.len = odcid->len; 704 qc->odcid.len = odcid->len;
700 qc->odcid.data = ngx_pstrdup(c->pool, odcid); 705 qc->odcid.data = ngx_pstrdup(c->pool, odcid);
701 if (qc->odcid.data == NULL) { 706 if (qc->odcid.data == NULL) {
702 return NGX_ERROR; 707 return NGX_ERROR;
708 }
709
710 return NGX_OK;
711 }
712
713
714 static ngx_int_t
715 ngx_quic_retry(ngx_connection_t *c)
716 {
717 ssize_t len;
718 ngx_str_t res, token;
719 ngx_quic_header_t pkt;
720 u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
721
722 if (ngx_quic_new_token(c, &token) != NGX_OK) {
723 return NGX_ERROR;
724 }
725
726 ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
727 pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY;
728 pkt.log = c->log;
729 pkt.odcid = c->quic->odcid;
730 pkt.dcid = c->quic->scid;
731 pkt.scid = c->quic->dcid;
732 pkt.token = token;
733
734 res.data = buf;
735
736 if (ngx_quic_encrypt(&pkt, NULL, &res) != NGX_OK) {
737 return NGX_ERROR;
738 }
739
740 #ifdef NGX_QUIC_DEBUG_PACKETS
741 ngx_quic_hexdump(c->log, "quic packet to send", res.data, res.len);
742 #endif
743
744 len = c->send(c, res.data, res.len);
745 if (len == NGX_ERROR || (size_t) len != res.len) {
746 return NGX_ERROR;
747 }
748
749 c->quic->token = token;
750 c->quic->tp.original_connection_id = c->quic->odcid;
751 c->quic->in_retry = 1;
752
753 return NGX_OK;
754 }
755
756
757 static ngx_int_t
758 ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token)
759 {
760 int len, iv_len;
761 u_char *data, *p, *key, *iv;
762 ngx_msec_t now;
763 EVP_CIPHER_CTX *ctx;
764 const EVP_CIPHER *cipher;
765 struct sockaddr_in *sin;
766 #if (NGX_HAVE_INET6)
767 struct sockaddr_in6 *sin6;
768 #endif
769 u_char in[NGX_QUIC_MAX_TOKEN_SIZE];
770
771 switch (c->sockaddr->sa_family) {
772
773 #if (NGX_HAVE_INET6)
774 case AF_INET6:
775 sin6 = (struct sockaddr_in6 *) c->sockaddr;
776
777 len = sizeof(struct in6_addr);
778 data = sin6->sin6_addr.s6_addr;
779
780 break;
781 #endif
782
783 #if (NGX_HAVE_UNIX_DOMAIN)
784 case AF_UNIX:
785
786 len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(now));
787 data = c->addr_text.data;
788
789 break;
790 #endif
791
792 default: /* AF_INET */
793 sin = (struct sockaddr_in *) c->sockaddr;
794
795 len = sizeof(in_addr_t);
796 data = (u_char *) &sin->sin_addr;
797
798 break;
799 }
800
801 p = ngx_cpymem(in, data, len);
802
803 now = ngx_current_msec;
804 len += sizeof(now);
805 ngx_memcpy(p, &now, sizeof(now));
806
807 cipher = EVP_aes_256_cbc();
808 iv_len = EVP_CIPHER_iv_length(cipher);
809
810 token->len = iv_len + len + EVP_CIPHER_block_size(cipher);
811 token->data = ngx_pnalloc(c->pool, token->len);
812 if (token->data == NULL) {
813 return NGX_ERROR;
814 }
815
816 ctx = EVP_CIPHER_CTX_new();
817 if (ctx == NULL) {
818 return NGX_ERROR;
819 }
820
821 key = c->quic->tp.token_key;
822 iv = token->data;
823
824 if (RAND_bytes(iv, iv_len) <= 0
825 || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
826 {
827 EVP_CIPHER_CTX_free(ctx);
828 return NGX_ERROR;
829 }
830
831 token->len = iv_len;
832
833 if (EVP_EncryptUpdate(ctx, token->data + token->len, &len, in, len) != 1) {
834 EVP_CIPHER_CTX_free(ctx);
835 return NGX_ERROR;
836 }
837
838 token->len += len;
839
840 if (EVP_EncryptFinal_ex(ctx, token->data + token->len, &len) <= 0) {
841 EVP_CIPHER_CTX_free(ctx);
842 return NGX_ERROR;
843 }
844
845 token->len += len;
846
847 EVP_CIPHER_CTX_free(ctx);
848
849 #ifdef NGX_QUIC_DEBUG_PACKETS
850 ngx_quic_hexdump(c->log, "quic new token", token->data, token->len);
851 #endif
852
853 return NGX_OK;
854 }
855
856
857 static ngx_int_t
858 ngx_quic_validate_token(ngx_connection_t *c, ngx_quic_header_t *pkt)
859 {
860 int len, tlen, iv_len;
861 u_char *key, *iv, *p, *data;
862 ngx_msec_t msec;
863 EVP_CIPHER_CTX *ctx;
864 const EVP_CIPHER *cipher;
865 struct sockaddr_in *sin;
866 #if (NGX_HAVE_INET6)
867 struct sockaddr_in6 *sin6;
868 #endif
869 ngx_quic_connection_t *qc;
870 u_char tdec[NGX_QUIC_MAX_TOKEN_SIZE];
871
872 if (pkt->token.len == 0) {
873 return NGX_ERROR;
874 }
875
876 qc = c->quic;
877
878 /* Retry token */
879
880 if (qc->token.len) {
881 if (pkt->token.len != qc->token.len) {
882 return NGX_ERROR;
883 }
884
885 if (ngx_memcmp(pkt->token.data, qc->token.data, pkt->token.len) != 0) {
886 return NGX_ERROR;
887 }
888
889 return NGX_OK;
890 }
891
892 /* NEW_TOKEN in a previous connection */
893
894 cipher = EVP_aes_256_cbc();
895 key = c->quic->tp.token_key;
896 iv = pkt->token.data;
897 iv_len = EVP_CIPHER_iv_length(cipher);
898
899 /* sanity checks */
900
901 if (pkt->token.len < (size_t) iv_len + EVP_CIPHER_block_size(cipher)) {
902 return NGX_ERROR;
903 }
904
905 if (pkt->token.len > (size_t) iv_len + NGX_QUIC_MAX_TOKEN_SIZE) {
906 return NGX_ERROR;
907 }
908
909 ctx = EVP_CIPHER_CTX_new();
910 if (ctx == NULL) {
911 return NGX_ERROR;
912 }
913
914 if (!EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv)) {
915 EVP_CIPHER_CTX_free(ctx);
916 return NGX_ERROR;
917 }
918
919 p = pkt->token.data + iv_len;
920 len = pkt->token.len - iv_len;
921
922 if (EVP_DecryptUpdate(ctx, tdec, &len, p, len) != 1) {
923 EVP_CIPHER_CTX_free(ctx);
924 return NGX_ERROR;
925 }
926
927 if (EVP_DecryptFinal_ex(ctx, tdec + len, &tlen) <= 0) {
928 EVP_CIPHER_CTX_free(ctx);
929 return NGX_ERROR;
930 }
931
932 EVP_CIPHER_CTX_free(ctx);
933
934 switch (c->sockaddr->sa_family) {
935
936 #if (NGX_HAVE_INET6)
937 case AF_INET6:
938 sin6 = (struct sockaddr_in6 *) c->sockaddr;
939
940 len = sizeof(struct in6_addr);
941 data = sin6->sin6_addr.s6_addr;
942
943 break;
944 #endif
945
946 #if (NGX_HAVE_UNIX_DOMAIN)
947 case AF_UNIX:
948
949 len = ngx_min(c->addr_text.len, NGX_QUIC_MAX_TOKEN_SIZE - sizeof(msec));
950 data = c->addr_text.data;
951
952 break;
953 #endif
954
955 default: /* AF_INET */
956 sin = (struct sockaddr_in *) c->sockaddr;
957
958 len = sizeof(in_addr_t);
959 data = (u_char *) &sin->sin_addr;
960
961 break;
962 }
963
964 if (ngx_memcmp(tdec, data, len) != 0) {
965 return NGX_ERROR;
966 }
967
968 ngx_memcpy(&msec, tdec + len, sizeof(msec));
969
970 if (ngx_current_msec - msec > NGX_QUIC_RETRY_LIFETIME) {
971 return NGX_DECLINED;
703 } 972 }
704 973
705 return NGX_OK; 974 return NGX_OK;
706 } 975 }
707 976
774 static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE]; 1043 static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
775 1044
776 b.start = buf; 1045 b.start = buf;
777 b.end = buf + sizeof(buf); 1046 b.end = buf + sizeof(buf);
778 b.pos = b.last = b.start; 1047 b.pos = b.last = b.start;
1048 b.memory = 1;
779 1049
780 c = rev->data; 1050 c = rev->data;
781 qc = c->quic; 1051 qc = c->quic;
782 1052
783 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "quic input handler"); 1053 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0, "quic input handler");
1044 pkt.raw = b; 1314 pkt.raw = b;
1045 pkt.data = p; 1315 pkt.data = p;
1046 pkt.len = b->last - p; 1316 pkt.len = b->last - p;
1047 pkt.log = c->log; 1317 pkt.log = c->log;
1048 pkt.flags = p[0]; 1318 pkt.flags = p[0];
1319
1320 if (c->quic->in_retry) {
1321 return ngx_quic_retry_input(c, &pkt);
1322 }
1049 1323
1050 /* TODO: check current state */ 1324 /* TODO: check current state */
1051 if (ngx_quic_long_pkt(pkt.flags)) { 1325 if (ngx_quic_long_pkt(pkt.flags)) {
1052 1326
1053 if (ngx_quic_pkt_in(pkt.flags)) { 1327 if (ngx_quic_pkt_in(pkt.flags)) {
1109 return b->pos; 1383 return b->pos;
1110 } 1384 }
1111 1385
1112 1386
1113 static ngx_int_t 1387 static ngx_int_t
1388 ngx_quic_retry_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
1389 {
1390 ngx_quic_secrets_t *keys;
1391 ngx_quic_send_ctx_t *ctx;
1392 ngx_quic_connection_t *qc;
1393 static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE];
1394
1395 c->log->action = "retrying quic connection";
1396
1397 qc = c->quic;
1398
1399 if (ngx_buf_size(pkt->raw) < NGX_QUIC_MIN_INITIAL_SIZE) {
1400 ngx_log_error(NGX_LOG_INFO, c->log, 0,
1401 "quic UDP datagram is too small for initial packet");
1402 return NGX_OK;
1403 }
1404
1405 if (ngx_quic_parse_long_header(pkt) != NGX_OK) {
1406 return NGX_ERROR;
1407 }
1408
1409 if (ngx_quic_pkt_zrtt(pkt->flags)) {
1410 ngx_log_error(NGX_LOG_INFO, c->log, 0,
1411 "quic discard inflight 0-RTT packet");
1412 return NGX_OK;
1413 }
1414
1415 if (!ngx_quic_pkt_in(pkt->flags)) {
1416 ngx_log_error(NGX_LOG_INFO, c->log, 0,
1417 "quic invalid initial packet: 0x%xi", pkt->flags);
1418 return NGX_ERROR;
1419 }
1420
1421 if (ngx_quic_parse_initial_header(pkt) != NGX_OK) {
1422 return NGX_ERROR;
1423 }
1424
1425 if (ngx_quic_new_dcid(c, &pkt->dcid) != NGX_OK) {
1426 return NGX_ERROR;
1427 }
1428
1429 qc = c->quic;
1430
1431 keys = &c->quic->keys[ssl_encryption_initial];
1432
1433 if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server,
1434 &qc->odcid)
1435 != NGX_OK)
1436 {
1437 return NGX_ERROR;
1438 }
1439
1440 c->quic->in_retry = 0;
1441
1442 if (ngx_quic_validate_token(c, pkt) != NGX_OK) {
1443 ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid token");
1444 return NGX_ERROR;
1445 }
1446
1447 pkt->secret = &keys->client;
1448 pkt->level = ssl_encryption_initial;
1449 pkt->plaintext = buf;
1450
1451 ctx = ngx_quic_get_send_ctx(qc, pkt->level);
1452
1453 if (ngx_quic_decrypt(pkt, NULL, &ctx->largest_pn) != NGX_OK) {
1454 return NGX_ERROR;
1455 }
1456
1457 if (ngx_quic_init_connection(c) != NGX_OK) {
1458 return NGX_ERROR;
1459 }
1460
1461 if (ngx_quic_payload_handler(c, pkt) != NGX_OK) {
1462 return NGX_ERROR;
1463 }
1464
1465 /* pos is at header end, adjust by actual packet length */
1466 pkt->raw->pos += pkt->len;
1467
1468 (void) ngx_quic_skip_zero_padding(pkt->raw);
1469
1470 return ngx_quic_input(c, pkt->raw);
1471 }
1472
1473
1474 static ngx_int_t
1114 ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt) 1475 ngx_quic_initial_input(ngx_connection_t *c, ngx_quic_header_t *pkt)
1115 { 1476 {
1116 ngx_ssl_conn_t *ssl_conn; 1477 ngx_ssl_conn_t *ssl_conn;
1117 ngx_quic_secrets_t *keys; 1478 ngx_quic_secrets_t *keys;
1118 ngx_quic_send_ctx_t *ctx; 1479 ngx_quic_send_ctx_t *ctx;