Browse Source

[tcp] Guard against malformed TCP options

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
fef8e34b6f
2 changed files with 53 additions and 13 deletions
  1. 0
    2
      src/include/ipxe/tcp.h
  2. 53
    11
      src/net/tcp.c

+ 0
- 2
src/include/ipxe/tcp.h View File

140
 
140
 
141
 /** Parsed TCP options */
141
 /** Parsed TCP options */
142
 struct tcp_options {
142
 struct tcp_options {
143
-	/** MSS option, if present */
144
-	const struct tcp_mss_option *mssopt;
145
 	/** Window scale option, if present */
143
 	/** Window scale option, if present */
146
 	const struct tcp_window_scale_option *wsopt;
144
 	const struct tcp_window_scale_option *wsopt;
147
 	/** SACK permitted option, if present */
145
 	/** SACK permitted option, if present */

+ 53
- 11
src/net/tcp.c View File

904
 /**
904
 /**
905
  * Parse TCP received options
905
  * Parse TCP received options
906
  *
906
  *
907
- * @v tcp		TCP connection
908
- * @v data		Raw options data
909
- * @v len		Raw options length
907
+ * @v tcp		TCP connection (may be NULL)
908
+ * @v tcphdr		TCP header
909
+ * @v hlen		TCP header length
910
  * @v options		Options structure to fill in
910
  * @v options		Options structure to fill in
911
+ * @ret rc		Return status code
911
  */
912
  */
912
-static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
913
-			  size_t len, struct tcp_options *options ) {
914
-	const void *end = ( data + len );
913
+static int tcp_rx_opts ( struct tcp_connection *tcp,
914
+			 const struct tcp_header *tcphdr, size_t hlen,
915
+			 struct tcp_options *options ) {
916
+	const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) );
917
+	const void *end = ( ( ( void * ) tcphdr ) + hlen );
915
 	const struct tcp_option *option;
918
 	const struct tcp_option *option;
916
 	unsigned int kind;
919
 	unsigned int kind;
920
+	size_t remaining;
921
+	size_t min;
917
 
922
 
923
+	/* Sanity check */
924
+	assert ( hlen >= sizeof ( *tcphdr ) );
925
+
926
+	/* Parse options */
918
 	memset ( options, 0, sizeof ( *options ) );
927
 	memset ( options, 0, sizeof ( *options ) );
919
-	while ( data < end ) {
928
+	while ( ( remaining = ( end - data ) ) ) {
929
+
930
+		/* Extract option code */
920
 		option = data;
931
 		option = data;
921
 		kind = option->kind;
932
 		kind = option->kind;
933
+
934
+		/* Handle single-byte options */
922
 		if ( kind == TCP_OPTION_END )
935
 		if ( kind == TCP_OPTION_END )
923
-			return;
936
+			break;
924
 		if ( kind == TCP_OPTION_NOP ) {
937
 		if ( kind == TCP_OPTION_NOP ) {
925
 			data++;
938
 			data++;
926
 			continue;
939
 			continue;
927
 		}
940
 		}
941
+
942
+		/* Handle multi-byte options */
943
+		min = sizeof ( *option );
928
 		switch ( kind ) {
944
 		switch ( kind ) {
929
 		case TCP_OPTION_MSS:
945
 		case TCP_OPTION_MSS:
930
-			options->mssopt = data;
946
+			/* Ignore received MSS */
931
 			break;
947
 			break;
932
 		case TCP_OPTION_WS:
948
 		case TCP_OPTION_WS:
933
 			options->wsopt = data;
949
 			options->wsopt = data;
950
+			min = sizeof ( *options->wsopt );
934
 			break;
951
 			break;
935
 		case TCP_OPTION_SACK_PERMITTED:
952
 		case TCP_OPTION_SACK_PERMITTED:
936
 			options->spopt = data;
953
 			options->spopt = data;
954
+			min = sizeof ( *options->spopt );
937
 			break;
955
 			break;
938
 		case TCP_OPTION_SACK:
956
 		case TCP_OPTION_SACK:
939
 			/* Ignore received SACKs */
957
 			/* Ignore received SACKs */
940
 			break;
958
 			break;
941
 		case TCP_OPTION_TS:
959
 		case TCP_OPTION_TS:
942
 			options->tsopt = data;
960
 			options->tsopt = data;
961
+			min = sizeof ( *options->tsopt );
943
 			break;
962
 			break;
944
 		default:
963
 		default:
945
 			DBGC ( tcp, "TCP %p received unknown option %d\n",
964
 			DBGC ( tcp, "TCP %p received unknown option %d\n",
946
 			       tcp, kind );
965
 			       tcp, kind );
947
 			break;
966
 			break;
948
 		}
967
 		}
968
+		if ( remaining < min ) {
969
+			DBGC ( tcp, "TCP %p received truncated option %d\n",
970
+			       tcp, kind );
971
+			return -EINVAL;
972
+		}
973
+		if ( option->length < min ) {
974
+			DBGC ( tcp, "TCP %p received underlength option %d\n",
975
+			       tcp, kind );
976
+			return -EINVAL;
977
+		}
978
+		if ( option->length > remaining ) {
979
+			DBGC ( tcp, "TCP %p received overlength option %d\n",
980
+			       tcp, kind );
981
+			return -EINVAL;
982
+		}
949
 		data += option->length;
983
 		data += option->length;
950
 	}
984
 	}
985
+
986
+	return 0;
951
 }
987
 }
952
 
988
 
953
 /**
989
 /**
1011
 			tcp->snd_win_scale = options->wsopt->scale;
1047
 			tcp->snd_win_scale = options->wsopt->scale;
1012
 			tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
1048
 			tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
1013
 		}
1049
 		}
1050
+		DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window "
1051
+		       "x%d, RX window x%d\n", tcp,
1052
+		       ( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ),
1053
+		       ( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ),
1054
+		       ( 1 << tcp->snd_win_scale ),
1055
+		       ( 1 << tcp->rcv_win_scale ) );
1014
 	}
1056
 	}
1015
 
1057
 
1016
 	/* Ignore duplicate SYN */
1058
 	/* Ignore duplicate SYN */
1369
 	ack = ntohl ( tcphdr->ack );
1411
 	ack = ntohl ( tcphdr->ack );
1370
 	raw_win = ntohs ( tcphdr->win );
1412
 	raw_win = ntohs ( tcphdr->win );
1371
 	flags = tcphdr->flags;
1413
 	flags = tcphdr->flags;
1372
-	tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
1373
-		      ( hlen - sizeof ( *tcphdr ) ), &options );
1414
+	if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 )
1415
+		goto discard;
1374
 	if ( tcp && options.tsopt )
1416
 	if ( tcp && options.tsopt )
1375
 		tcp->ts_val = ntohl ( options.tsopt->tsval );
1417
 		tcp->ts_val = ntohl ( options.tsopt->tsval );
1376
 	iob_pull ( iobuf, hlen );
1418
 	iob_pull ( iobuf, hlen );

Loading…
Cancel
Save