Mercurial > hg > nginx-quic
comparison src/event/ngx_event_quic.c @ 7860: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
7859:b7704303a7e5 | 7860: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; |