@@ -34,29 +34,41 @@ static void port_rx_pre(QUIC_PORT *port);
34
34
* @struct validation_token
35
35
* @brief Represents a validation token for secure connection handling.
36
36
*
37
- * This struct is used to store information related to a validation token,
38
- * including the token buffer, original connection ID, and an integrity tag
39
- * for secure validation of QUIC connections.
37
+ * This struct is used to store information related to a validation token.
40
38
*
41
- * @var validation_token::token_buf
42
- * A character array holding the token data. The size of this array is
43
- * based on the length of the string "openssltoken" minus one for the null
44
- * terminator .
39
+ * @var validation_token::is_retry
40
+ * True iff this validation token is for a token sent in a RETRY packet.
41
+ * Otherwise, this token is from a NEW_TOKEN_packet. Iff this value is true,
42
+ * then ODCID and RSCID are set .
45
43
*
46
- * @var validation_token::token_odcid
44
+ * @var validation_token::timestamp
45
+ * Time that the validation token was minted.
46
+ *
47
+ * @var validation_token::odcid
47
48
* An original connection ID (`QUIC_CONN_ID`) used to identify the QUIC
48
49
* connection. This ID helps associate the token with a specific connection.
50
+ * This will only be valid for validation tokens from RETRY packets.
51
+ *
52
+ * @var validation_token::rscid
53
+ * DCID that the client will use as the DCID of the subsequent initial packet
54
+ * i.e the "new" DCID.
55
+ * This will only be valid for validation tokens from RETRY packets.
56
+ *
57
+ * @var validation_token::remote_addr_len
58
+ * Length of the following character array.
49
59
*
50
- * @var validation_token::integrity_tag
51
- * A character array for the integrity tag, with a length defined by
52
- * `QUIC_RETRY_INTEGRITY_TAG_LEN`. This tag is used to verify the integrity
53
- * of the token during the connection process.
60
+ * @var validation_token::remote_addr
61
+ * A character array holding the raw address of the client requesting the
62
+ * connection.
54
63
*/
55
- struct validation_token {
56
- char token_buf [sizeof ("openssltoken" ) - 1 ];
57
- QUIC_CONN_ID token_odcid ;
58
- char integrity_tag [QUIC_RETRY_INTEGRITY_TAG_LEN ];
59
- };
64
+ typedef struct validation_token {
65
+ OSSL_TIME timestamp ;
66
+ QUIC_CONN_ID odcid ;
67
+ QUIC_CONN_ID rscid ;
68
+ size_t remote_addr_len ;
69
+ unsigned char * remote_addr ;
70
+ unsigned char is_retry ;
71
+ } QUIC_VALIDATION_TOKEN ;
60
72
61
73
DEFINE_LIST_OF_IMPL (ch , QUIC_CHANNEL );
62
74
DEFINE_LIST_OF_IMPL (incoming_ch , QUIC_CHANNEL );
@@ -647,9 +659,133 @@ static int port_try_handle_stateless_reset(QUIC_PORT *port, const QUIC_URXE *e)
647
659
return i > 0 ;
648
660
}
649
661
650
- #define TOKEN_LEN (sizeof("openssltoken") + \
651
- QUIC_RETRY_INTEGRITY_TAG_LEN - 1 + \
652
- sizeof(unsigned char))
662
+ static void cleanup_validation_token (QUIC_VALIDATION_TOKEN * token )
663
+ {
664
+ OPENSSL_free (token -> remote_addr );
665
+ }
666
+
667
+ /**
668
+ * @brief Generates a validation token for a RETRY packet.
669
+ *
670
+ * @param peer Address of the client peer receiving the packet.
671
+ * @param odcid DCID of the connection attempt.
672
+ * @param rscid Retry source connection ID of the connection attempt.
673
+ * @param token Address of token to fill data.
674
+ *
675
+ * @return 1 if validation token is filled successfully, 0 otherwise.
676
+ */
677
+ static int generate_retry_token (BIO_ADDR * peer , QUIC_CONN_ID odcid ,
678
+ QUIC_CONN_ID rscid , QUIC_VALIDATION_TOKEN * token )
679
+ {
680
+ token -> is_retry = 1 ;
681
+ token -> timestamp = ossl_time_now ();
682
+ token -> remote_addr = NULL ;
683
+ token -> odcid = odcid ;
684
+ token -> rscid = rscid ;
685
+
686
+ if (!BIO_ADDR_rawaddress (peer , NULL , & token -> remote_addr_len )
687
+ || token -> remote_addr_len == 0
688
+ || (token -> remote_addr = OPENSSL_malloc (token -> remote_addr_len )) == NULL
689
+ || !BIO_ADDR_rawaddress (peer , token -> remote_addr ,
690
+ & token -> remote_addr_len )) {
691
+ cleanup_validation_token (token );
692
+ return 0 ;
693
+ }
694
+
695
+ return 1 ;
696
+ }
697
+
698
+ /**
699
+ * @brief Marshals a validation token into a new buffer.
700
+ *
701
+ * Dynamically allocates |buffer| and stores the size of |buffer| in BUFFER_LEN.
702
+ * Note that it will also allocate an extra QUIC_RETRY_INTEGRITY_TAG_LEN bytes.
703
+ * The caller is responsible for freeing |buffer|.
704
+ *
705
+ * @param token Validation token.
706
+ * @param buffer Callee will allocate a buffer and store the address here.
707
+ * @param buffer_len Size of |buffer|.
708
+ */
709
+ static int marshal_validation_token (QUIC_VALIDATION_TOKEN * token ,
710
+ unsigned char * * buffer , size_t * buffer_len )
711
+ {
712
+ WPACKET wpkt = {0 };
713
+ BUF_MEM * buf_mem = BUF_MEM_new ();
714
+
715
+ if (buf_mem == NULL || (token -> is_retry != 0 && token -> is_retry != 1 ))
716
+ return 0 ;
717
+
718
+ if (!WPACKET_init (& wpkt , buf_mem )
719
+ || !WPACKET_memset (& wpkt , token -> is_retry , 1 )
720
+ || !WPACKET_memcpy (& wpkt , & token -> timestamp ,
721
+ sizeof (token -> timestamp ))
722
+ || (token -> is_retry
723
+ && (!WPACKET_sub_memcpy_u8 (& wpkt , & token -> odcid .id ,
724
+ token -> odcid .id_len )
725
+ || !WPACKET_sub_memcpy_u8 (& wpkt , & token -> rscid .id ,
726
+ token -> rscid .id_len )))
727
+ || !WPACKET_sub_memcpy_u8 (& wpkt , token -> remote_addr , token -> remote_addr_len )
728
+ || !WPACKET_allocate_bytes (& wpkt , QUIC_RETRY_INTEGRITY_TAG_LEN , NULL )
729
+ || !WPACKET_get_total_written (& wpkt , buffer_len )
730
+ || !WPACKET_finish (& wpkt )) {
731
+ WPACKET_cleanup (& wpkt );
732
+ BUF_MEM_free (buf_mem );
733
+ return 0 ;
734
+ }
735
+
736
+ * buffer = (unsigned char * )buf_mem -> data ;
737
+ buf_mem -> data = NULL ;
738
+ BUF_MEM_free (buf_mem );
739
+ return 1 ;
740
+ }
741
+
742
+ /**
743
+ * @brief Parses contents of a buffer into a validation token.
744
+ *
745
+ * VALIDATION_TOKEN should already be initalized. Does some basic sanity checks.
746
+ *
747
+ * @param token Validation token to fill data in.
748
+ * @param buf Buffer of previously marshaled validation token.
749
+ * @param buf_len Length of |buf|.
750
+ */
751
+ static int parse_validation_token (QUIC_VALIDATION_TOKEN * token ,
752
+ const unsigned char * buf , size_t buf_len )
753
+ {
754
+ PACKET pkt , subpkt ;
755
+
756
+ if (buf == NULL || token == NULL )
757
+ return 0 ;
758
+
759
+ token -> remote_addr = NULL ;
760
+
761
+ if (!PACKET_buf_init (& pkt , buf , buf_len )
762
+ || !PACKET_copy_bytes (& pkt , & token -> is_retry , sizeof (token -> is_retry ))
763
+ || !(token -> is_retry == 0 || token -> is_retry == 1 )
764
+ || !PACKET_copy_bytes (& pkt , (unsigned char * )& token -> timestamp ,
765
+ sizeof (token -> timestamp ))
766
+ || (token -> is_retry
767
+ && (!PACKET_get_length_prefixed_1 (& pkt , & subpkt )
768
+ || (token -> odcid .id_len = (unsigned char )PACKET_remaining (& subpkt ))
769
+ > QUIC_MAX_CONN_ID_LEN
770
+ || !PACKET_copy_bytes (& subpkt ,
771
+ (unsigned char * )& token -> odcid .id ,
772
+ token -> odcid .id_len )
773
+ || !PACKET_get_length_prefixed_1 (& pkt , & subpkt )
774
+ || (token -> rscid .id_len = (unsigned char )PACKET_remaining (& subpkt ))
775
+ > QUIC_MAX_CONN_ID_LEN
776
+ || !PACKET_copy_bytes (& subpkt , (unsigned char * )& token -> rscid .id ,
777
+ token -> rscid .id_len )))
778
+ || !PACKET_get_length_prefixed_1 (& pkt , & subpkt )
779
+ || (token -> remote_addr_len = PACKET_remaining (& subpkt )) == 0
780
+ || (token -> remote_addr = OPENSSL_malloc (token -> remote_addr_len )) == NULL
781
+ || !PACKET_copy_bytes (& subpkt , token -> remote_addr , token -> remote_addr_len )
782
+ || PACKET_remaining (& pkt ) != 0 ) {
783
+ cleanup_validation_token (token );
784
+ return 0 ;
785
+ }
786
+
787
+ return 1 ;
788
+ }
653
789
654
790
/**
655
791
* @brief Sends a QUIC Retry packet to a client.
@@ -678,22 +814,13 @@ static void port_send_retry(QUIC_PORT *port,
678
814
QUIC_PKT_HDR * client_hdr )
679
815
{
680
816
BIO_MSG msg [1 ];
681
- unsigned char buffer [512 ];
817
+ unsigned char buffer [512 ], * token_buf = NULL ;
682
818
WPACKET wpkt ;
683
- size_t written ;
684
- QUIC_PKT_HDR hdr ;
685
- struct validation_token token ;
686
- size_t token_len = TOKEN_LEN ;
687
- unsigned char * integrity_tag ;
819
+ size_t written , token_buf_len ;
820
+ QUIC_PKT_HDR hdr = {0 };
821
+ QUIC_VALIDATION_TOKEN token = {0 };
688
822
int ok ;
689
823
690
- /* TODO(QUIC_SERVER): generate proper validation token */
691
- memcpy (token .token_buf , "openssltoken" , sizeof ("openssltoken" ) - 1 );
692
-
693
- token .token_odcid = client_hdr -> dst_conn_id ;
694
- token_len += token .token_odcid .id_len ;
695
- integrity_tag = (unsigned char * )& token .token_odcid +
696
- token .token_odcid .id_len + sizeof (token .token_odcid .id_len );
697
824
/*
698
825
* 17.2.5.1 Sending a Retry packet
699
826
* dst ConnId is src ConnId we got from client
@@ -708,23 +835,31 @@ static void port_send_retry(QUIC_PORT *port,
708
835
*/
709
836
ok = ossl_quic_lcidm_get_unused_cid (port -> lcidm , & hdr .src_conn_id );
710
837
if (ok == 0 )
711
- return ;
838
+ goto err ;
839
+
840
+ /* Generate retry validation token */
841
+ if (!generate_retry_token (peer , client_hdr -> dst_conn_id ,
842
+ hdr .src_conn_id , & token )
843
+ || !marshal_validation_token (& token , & token_buf , & token_buf_len )
844
+ || !ossl_assert (token_buf_len >= QUIC_RETRY_INTEGRITY_TAG_LEN ))
845
+ goto err ;
712
846
713
847
hdr .dst_conn_id = client_hdr -> src_conn_id ;
714
848
hdr .type = QUIC_PKT_TYPE_RETRY ;
715
849
hdr .fixed = 1 ;
716
850
hdr .version = 1 ;
717
- hdr .len = token_len ;
718
- hdr .data = ( unsigned char * ) & token ;
851
+ hdr .len = token_buf_len ;
852
+ hdr .data = token_buf ;
719
853
ok = ossl_quic_calculate_retry_integrity_tag (port -> engine -> libctx ,
720
854
port -> engine -> propq , & hdr ,
721
855
& client_hdr -> dst_conn_id ,
722
- integrity_tag );
856
+ token_buf + token_buf_len
857
+ - QUIC_RETRY_INTEGRITY_TAG_LEN );
723
858
if (ok == 0 )
724
- return ;
859
+ goto err ;
725
860
726
- hdr .token = ( unsigned char * ) & token ;
727
- hdr .token_len = token_len ;
861
+ hdr .token = hdr . data ;
862
+ hdr .token_len = hdr . len ;
728
863
729
864
msg [0 ].data = buffer ;
730
865
msg [0 ].peer = peer ;
@@ -733,20 +868,20 @@ static void port_send_retry(QUIC_PORT *port,
733
868
734
869
ok = WPACKET_init_static_len (& wpkt , buffer , sizeof (buffer ), 0 );
735
870
if (ok == 0 )
736
- return ;
871
+ goto err ;
737
872
738
873
ok = ossl_quic_wire_encode_pkt_hdr (& wpkt , client_hdr -> dst_conn_id .id_len ,
739
874
& hdr , NULL );
740
875
if (ok == 0 )
741
- return ;
876
+ goto err ;
742
877
743
878
ok = WPACKET_get_total_written (& wpkt , & msg [0 ].data_len );
744
879
if (ok == 0 )
745
- return ;
880
+ goto err ;
746
881
747
882
ok = WPACKET_finish (& wpkt );
748
883
if (ok == 0 )
749
- return ;
884
+ goto err ;
750
885
751
886
/*
752
887
* TODO(QUIC SERVER) need to retry this in the event it return EAGAIN
@@ -756,6 +891,9 @@ static void port_send_retry(QUIC_PORT *port,
756
891
ERR_raise_data (ERR_LIB_SSL , SSL_R_QUIC_NETWORK_ERROR ,
757
892
"port retry send failed due to network BIO I/O error" );
758
893
894
+ err :
895
+ cleanup_validation_token (& token );
896
+ OPENSSL_free (token_buf );
759
897
}
760
898
761
899
/**
@@ -853,39 +991,96 @@ static void port_send_version_negotiation(QUIC_PORT *port, BIO_ADDR *peer,
853
991
* This function checks the validity of a token contained in the provided
854
992
* QUIC packet header (`QUIC_PKT_HDR *hdr`). The validation process involves
855
993
* verifying that the token matches an expected format and value. If the
856
- * token is valid, the function extracts the original connection ID (ODCID)
857
- * and stores it in the provided `QUIC_CONN_ID *odcid`.
994
+ * token is from a RETRY packet, the function extracts the original connection
995
+ * ID (ODCID)/original source connection ID (SCID) and stores it in the provided
996
+ * parameters. If the token is from a NEW_TOKEN packet, the values will be
997
+ * derived instead.
858
998
*
859
999
* @param hdr Pointer to the QUIC packet header containing the token.
860
- * @param odcid Pointer to the connection ID structure to store the ODCID if
861
- * the token is valid.
862
- * @return 1 if the token is valid and ODCID is extracted successfully,
1000
+ * @param port Pointer to the QUIC port from which to send the packet.
1001
+ * @param peer Address of the client peer receiving the packet.
1002
+ * @param odcid Pointer to the connection ID structure to store the ODCID if the
1003
+ * token is valid.
1004
+ * @param scid Pointer to the connection ID structure to store the SCID if the
1005
+ * token is valid.
1006
+ *
1007
+ * @return 1 if the token is valid and ODCID/SCID are successfully set.
863
1008
* 0 otherwise.
864
1009
*
865
1010
* The function performs the following checks:
866
- * - Verifies that the token length meets the required minimum.
867
- * - Confirms the token buffer matches the expected "openssltoken" string.
868
- * -
1011
+ * - Token length meets the required minimum.
1012
+ * - Buffer matches expected format.
1013
+ * - Peer address matches previous connection address.
1014
+ * - Token has not expired. Currently set to 10 seconds for tokens from RETRY
1015
+ * packets and 60 minutes for tokens from NEW_TOKEN packets. This may be
1016
+ * configurable in the future.
869
1017
*/
870
- static int port_validate_token (QUIC_PKT_HDR * hdr , QUIC_CONN_ID * odcid )
1018
+ static int port_validate_token (QUIC_PKT_HDR * hdr , QUIC_PORT * port ,
1019
+ BIO_ADDR * peer , QUIC_CONN_ID * odcid ,
1020
+ QUIC_CONN_ID * scid )
871
1021
{
872
- int valid ;
873
- struct validation_token * token ;
874
-
875
- memset (odcid , 0 , sizeof (QUIC_CONN_ID ));
1022
+ int ret = 0 ;
1023
+ QUIC_VALIDATION_TOKEN token = { 0 };
1024
+ unsigned long long time_diff ;
1025
+ size_t remote_addr_len ;
1026
+ unsigned char * remote_addr = NULL ;
1027
+ OSSL_TIME now = ossl_time_now ();
1028
+
1029
+ if (!parse_validation_token (& token , hdr -> token , hdr -> token_len ))
1030
+ goto err ;
876
1031
877
- token = (struct validation_token * )hdr -> token ;
878
- if (token == NULL || hdr -> token_len <= (TOKEN_LEN - QUIC_RETRY_INTEGRITY_TAG_LEN ))
879
- return 0 ;
1032
+ /*
1033
+ * Validate token timestamp. Current time should not be before the token
1034
+ * timestamp.
1035
+ */
1036
+ if (ossl_time_compare (now , token .timestamp ) < 0 )
1037
+ goto err ;
1038
+ time_diff = ossl_time2seconds (ossl_time_abs_difference (token .timestamp ,
1039
+ now ));
1040
+ if ((token .is_retry && time_diff > 10 )
1041
+ || (!token .is_retry && time_diff > 3600 ))
1042
+ goto err ;
880
1043
881
- valid = memcmp (token -> token_buf , "openssltoken" , sizeof ("openssltoken" ) - 1 );
882
- if (valid != 0 )
883
- return 0 ;
1044
+ /* Validate remote address */
1045
+ if (!BIO_ADDR_rawaddress (peer , NULL , & remote_addr_len )
1046
+ || remote_addr_len != token .remote_addr_len
1047
+ || (remote_addr = OPENSSL_malloc (remote_addr_len )) == NULL
1048
+ || !BIO_ADDR_rawaddress (peer , remote_addr , & remote_addr_len )
1049
+ || memcmp (remote_addr , token .remote_addr , remote_addr_len ) != 0 )
1050
+ goto err ;
884
1051
885
- odcid -> id_len = token -> token_odcid .id_len ;
886
- memcpy (odcid -> id , token -> token_odcid .id , token -> token_odcid .id_len );
1052
+ /*
1053
+ * Set ODCID and SCID. If the token is from a RETRY packet, retrieve both
1054
+ * from the token. Otherwise, generate a new ODCID and use the header's
1055
+ * source connection ID for SCID.
1056
+ */
1057
+ if (token .is_retry ) {
1058
+ /*
1059
+ * We're parsing a packet header before its gone through AEAD validation
1060
+ * here, so there is a chance we are dealing with corrupted data. Make
1061
+ * Sure the dcid encoded in the token matches the headers dcid to
1062
+ * mitigate that.
1063
+ * TODO(QUIC SERVER): Consider handling AEAD validation at the port
1064
+ * level rather than the QRX/channel level to eliminate the need for
1065
+ * this.
1066
+ */
1067
+ if (token .rscid .id_len != hdr -> dst_conn_id .id_len
1068
+ || memcmp (& token .rscid .id , & hdr -> dst_conn_id .id ,
1069
+ token .rscid .id_len ) != 0 )
1070
+ goto err ;
1071
+ * odcid = token .odcid ;
1072
+ * scid = token .rscid ;
1073
+ } else {
1074
+ if (!ossl_quic_lcidm_get_unused_cid (port -> lcidm , odcid ))
1075
+ goto err ;
1076
+ * scid = hdr -> src_conn_id ;
1077
+ }
887
1078
888
- return 1 ;
1079
+ ret = 1 ;
1080
+ err :
1081
+ cleanup_validation_token (& token );
1082
+ OPENSSL_free (remote_addr );
1083
+ return ret ;
889
1084
}
890
1085
891
1086
/*
@@ -899,7 +1094,7 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg,
899
1094
PACKET pkt ;
900
1095
QUIC_PKT_HDR hdr ;
901
1096
QUIC_CHANNEL * ch = NULL , * new_ch = NULL ;
902
- QUIC_CONN_ID odcid ;
1097
+ QUIC_CONN_ID odcid , scid ;
903
1098
uint64_t cause_flags = 0 ;
904
1099
905
1100
/* Don't handle anything if we are no longer running. */
@@ -991,11 +1186,11 @@ static void port_default_packet_handler(QUIC_URXE *e, void *arg,
991
1186
if (hdr .token == NULL ) {
992
1187
port_send_retry (port , & e -> peer , & hdr );
993
1188
goto undesirable ;
994
- } else if (port_validate_token (& hdr , & odcid ) == 0 ) {
1189
+ } else if (port_validate_token (& hdr , port , & e -> peer , & odcid , & scid ) != 1 ) {
995
1190
goto undesirable ;
996
1191
}
997
1192
998
- port_bind_channel (port , & e -> peer , & hdr . src_conn_id , & hdr .dst_conn_id ,
1193
+ port_bind_channel (port , & e -> peer , & scid , & hdr .dst_conn_id ,
999
1194
& odcid , & new_ch );
1000
1195
1001
1196
/*
0 commit comments