[Pkg-voip-commits] [asterisk] 01/03: Upstream fix for CVE-2017-14603 / AST-2017-008

Bernhard Schmidt berni at moszumanska.debian.org
Sat Sep 23 20:04:33 UTC 2017


This is an automated email from the git hooks/post-receive script.

berni pushed a commit to branch jessie
in repository asterisk.

commit 328f41df8068cef440e1c9ea385a04e0cbc3b359
Author: Bernhard Schmidt <berni at debian.org>
Date:   Sat Sep 23 21:06:54 2017 +0200

    Upstream fix for CVE-2017-14603 / AST-2017-008
    
    Closes: #876328
---
 debian/patches/AST-2017-008-11.diff | 778 ++++++++++++++++++++++++++++++++++++
 debian/patches/series               |   1 +
 2 files changed, 779 insertions(+)

diff --git a/debian/patches/AST-2017-008-11.diff b/debian/patches/AST-2017-008-11.diff
new file mode 100644
index 0000000..5221e6b
--- /dev/null
+++ b/debian/patches/AST-2017-008-11.diff
@@ -0,0 +1,778 @@
+From fe2ba2f3ca60d33bc789c6ae8e03ee26dc1b637c Mon Sep 17 00:00:00 2001
+From: Richard Mudgett <rmudgett at digium.com>
+Date: Wed, 13 Sep 2017 12:07:42 -0500
+Subject: [PATCH] AST-2017-008: Improve RTP and RTCP packet processing.
+
+Validate RTCP packets before processing them.
+
+* Validate that the received packet is of a minimum length and apply the
+RFC3550 RTCP packet validation checks.
+
+* Fixed potentially reading garbage beyond the received RTCP record data.
+
+* Fixed rtp->themssrc only being set once when the remote could change
+the SSRC.  We would effectively stop handling the RTCP statistic records.
+
+* Fixed rtp->themssrc to not treat a zero value as special by adding
+rtp->themssrc_valid to indicate if rtp->themssrc is available.
+
+ASTERISK-27274
+
+Make strict RTP learning more flexible.
+
+Direct media can cause strict RTP to attempt to learn a remote address
+again before it has had a chance to learn the remote address the first
+time.  Because of the rapid relearn requests, strict RTP could latch onto
+the first remote address and fail to latch onto the direct media remote
+address.  As a result, you have one way audio until the call is placed on
+and off hold.
+
+The new algorithm learns remote addresses for a set time (1.5 seconds)
+before locking the remote address.  In addition, we must see a configured
+number of remote packets from the same address in a row before switching.
+
+* Fixed strict RTP learning from always accepting the first new address
+packet as the new stream.
+
+* Fixed strict RTP to initialize the expected sequence number with the
+last received sequence number instead of the last transmitted sequence
+number.
+
+* Fixed the predicted next sequence number calculation in
+rtp_learning_rtp_seq_update() to handle overflow.
+
+ASTERISK-27252
+
+Change-Id: Ia2d3aa6e0f22906c25971e74f10027d96525f31c
+---
+
+diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
+index 4881171..7393d57 100644
+--- a/res/res_rtp_asterisk.c
++++ b/res/res_rtp_asterisk.c
+@@ -115,7 +115,9 @@
+ 	STRICT_RTP_CLOSED,   /*! Drop all RTP packets not coming from source that was learned */
+ };
+ 
+-#define DEFAULT_STRICT_RTP STRICT_RTP_CLOSED
++#define STRICT_RTP_LEARN_TIMEOUT	1500	/*!< milliseconds */
++
++#define DEFAULT_STRICT_RTP -1	/*!< Enabled */
+ #define DEFAULT_ICESUPPORT 1
+ 
+ extern struct ast_srtp_res *res_srtp;
+@@ -199,9 +201,11 @@
+ 
+ /*! \brief RTP learning mode tracking information */
+ struct rtp_learning_info {
++	struct ast_sockaddr proposed_address;	/*!< Proposed remote address for strict RTP */
++	struct timeval start;	/*!< The time learning mode was started */
++	struct timeval received; /*!< The time of the last received packet */
+ 	int max_seq;	/*!< The highest sequence number received */
+ 	int packets;	/*!< The number of remaining packets before the source is accepted */
+-	struct timeval received; /*!< The time of the last received packet */
+ };
+ 
+ #ifdef HAVE_OPENSSL_SRTP
+@@ -223,7 +227,7 @@
+ 	unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
+ 	unsigned int ssrc;		/*!< Synchronization source, RFC 3550, page 10. */
+ 	unsigned int themssrc;		/*!< Their SSRC */
+-	unsigned int rxssrc;
++	unsigned int themssrc_valid;	/*!< True if their SSRC is available. */
+ 	unsigned int lastts;
+ 	unsigned int lastrxts;
+ 	unsigned int lastividtimestamp;
+@@ -1655,8 +1659,6 @@
+ #endif
+ };
+ 
+-static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq);
+-
+ #ifdef HAVE_OPENSSL_SRTP
+ static void dtls_perform_handshake(struct ast_rtp_instance *instance, struct dtls_details *dtls, int rtcp)
+ {
+@@ -1685,6 +1687,8 @@
+ #endif
+ 
+ #ifdef USE_PJPROJECT
++static void rtp_learning_start(struct ast_rtp *rtp);
++
+ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
+ {
+ 	struct ast_rtp_instance *instance = ice->user_data;
+@@ -1721,8 +1725,8 @@
+ 		return;
+ 	}
+ 
+-	rtp->strict_rtp_state = STRICT_RTP_LEARN;
+-	rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
++	ast_verb(4, "%p -- Strict RTP learning after ICE completion\n", rtp);
++	rtp_learning_start(rtp);
+ }
+ 
+ static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
+@@ -2355,7 +2359,7 @@
+  */
+ static void rtp_learning_seq_init(struct rtp_learning_info *info, uint16_t seq)
+ {
+-	info->max_seq = seq - 1;
++	info->max_seq = seq;
+ 	info->packets = learning_min_sequential;
+ 	memset(&info->received, 0, sizeof(info->received));
+ }
+@@ -2372,14 +2376,17 @@
+  */
+ static int rtp_learning_rtp_seq_update(struct rtp_learning_info *info, uint16_t seq)
+ {
++	/*
++	 * During the learning mode the minimum amount of media we'll accept is
++	 * 10ms so give a reasonable 5ms buffer just in case we get it sporadically.
++	 */
+ 	if (!ast_tvzero(info->received) && ast_tvdiff_ms(ast_tvnow(), info->received) < 5) {
+-		/* During the probation period the minimum amount of media we'll accept is
+-		 * 10ms so give a reasonable 5ms buffer just in case we get it sporadically.
++		/*
++		 * Reject a flood of packets as acceptable for learning.
++		 * Reset the needed packets.
+ 		 */
+-		return 1;
+-	}
+-
+-	if (seq == info->max_seq + 1) {
++		info->packets = learning_min_sequential - 1;
++	} else if (seq == (uint16_t) (info->max_seq + 1)) {
+ 		/* packet is in sequence */
+ 		info->packets--;
+ 	} else {
+@@ -2389,7 +2396,23 @@
+ 	info->max_seq = seq;
+ 	info->received = ast_tvnow();
+ 
+-	return (info->packets == 0);
++	return info->packets;
++}
++
++/*!
++ * \brief Start the strictrtp learning mode.
++ *
++ * \param rtp RTP session description
++ *
++ * \return Nothing
++ */
++static void rtp_learning_start(struct ast_rtp *rtp)
++{
++	rtp->strict_rtp_state = STRICT_RTP_LEARN;
++	memset(&rtp->rtp_source_learn.proposed_address, 0,
++		sizeof(rtp->rtp_source_learn.proposed_address));
++	rtp->rtp_source_learn.start = ast_tvnow();
++	rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t) rtp->lastrxseqno);
+ }
+ 
+ #ifdef USE_PJPROJECT
+@@ -2546,10 +2569,7 @@
+ 	/* Set default parameters on the newly created RTP structure */
+ 	rtp->ssrc = ast_random();
+ 	rtp->seqno = ast_random() & 0x7fff;
+-	rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_LEARN : STRICT_RTP_OPEN);
+-	if (strictrtp) {
+-		rtp_learning_seq_init(&rtp->rtp_source_learn, (uint16_t)rtp->seqno);
+-	}
++	rtp->strict_rtp_state = (strictrtp ? STRICT_RTP_CLOSED : STRICT_RTP_OPEN);
+ 
+ 	/* Create a new socket for us to listen on and use */
+ 	if ((rtp->s =
+@@ -3867,13 +3887,86 @@
+ 	return &rtp->f;
+ }
+ 
++static const char *rtcp_payload_type2str(unsigned int pt)
++{
++	const char *str;
++
++	switch (pt) {
++	case RTCP_PT_SR:
++		str = "Sender Report";
++		break;
++	case RTCP_PT_RR:
++		str = "Receiver Report";
++		break;
++	case RTCP_PT_FUR:
++		/* Full INTRA-frame Request / Fast Update Request */
++		str = "H.261 FUR";
++		break;
++	case RTCP_PT_SDES:
++		str = "Source Description";
++		break;
++	case RTCP_PT_BYE:
++		str = "BYE";
++		break;
++	default:
++		str = "Unknown";
++		break;
++	}
++	return str;
++}
++
++/*
++ * Unshifted RTCP header bit field masks
++ */
++#define RTCP_LENGTH_MASK			0xFFFF
++#define RTCP_PAYLOAD_TYPE_MASK		0xFF
++#define RTCP_REPORT_COUNT_MASK		0x1F
++#define RTCP_PADDING_MASK			0x01
++#define RTCP_VERSION_MASK			0x03
++
++/*
++ * RTCP header bit field shift offsets
++ */
++#define RTCP_LENGTH_SHIFT			0
++#define RTCP_PAYLOAD_TYPE_SHIFT		16
++#define RTCP_REPORT_COUNT_SHIFT		24
++#define RTCP_PADDING_SHIFT			29
++#define RTCP_VERSION_SHIFT			30
++
++#define RTCP_VERSION				2U
++#define RTCP_VERSION_SHIFTED		(RTCP_VERSION << RTCP_VERSION_SHIFT)
++#define RTCP_VERSION_MASK_SHIFTED	(RTCP_VERSION_MASK << RTCP_VERSION_SHIFT)
++
++/*
++ * RTCP first packet record validity header mask and value.
++ *
++ * RFC3550 intentionally defines the encoding of RTCP_PT_SR and RTCP_PT_RR
++ * such that they differ in the least significant bit.  Either of these two
++ * payload types MUST be the first RTCP packet record in a compound packet.
++ *
++ * RFC3550 checks the padding bit in the algorithm they use to check the
++ * RTCP packet for validity.  However, we aren't masking the padding bit
++ * to check since we don't know if it is a compound RTCP packet or not.
++ */
++#define RTCP_VALID_MASK (RTCP_VERSION_MASK_SHIFTED | (((RTCP_PAYLOAD_TYPE_MASK & ~0x1)) << RTCP_PAYLOAD_TYPE_SHIFT))
++#define RTCP_VALID_VALUE (RTCP_VERSION_SHIFTED | (RTCP_PT_SR << RTCP_PAYLOAD_TYPE_SHIFT))
++
++#define RTCP_SR_BLOCK_WORD_LENGTH 5
++#define RTCP_RR_BLOCK_WORD_LENGTH 6
++#define RTCP_HEADER_SSRC_LENGTH   2
++
+ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
+ {
+ 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+ 	struct ast_sockaddr addr;
+ 	unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
+ 	unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
+-	int res, packetwords, position = 0;
++	int res;
++	unsigned int packetwords;
++	unsigned int position;
++	unsigned int first_word;
++	/*! True if we have seen an acceptable SSRC to learn the remote RTCP address */
++	unsigned int ssrc_seen;
+ 	struct ast_frame *f = &ast_null_frame;
+ 
+ 	/* Read in RTCP data from the socket */
+@@ -3918,56 +4011,170 @@
+ 
+ 	packetwords = res / 4;
+ 
+-	ast_debug(1, "Got RTCP report of %d bytes\n", res);
++	ast_debug(1, "Got RTCP report of %d bytes from %s\n",
++		res, ast_sockaddr_stringify(&addr));
+ 
++	/*
++	 * Validate the RTCP packet according to an adapted and slightly
++	 * modified RFC3550 validation algorithm.
++	 */
++	if (packetwords < RTCP_HEADER_SSRC_LENGTH) {
++		ast_debug(1, "%p -- RTCP from %s: Frame size (%u words) is too short\n",
++			rtp, ast_sockaddr_stringify(&addr), packetwords);
++		return &ast_null_frame;
++	}
++	position = 0;
++	first_word = ntohl(rtcpheader[position]);
++	if ((first_word & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
++		ast_debug(1, "%p -- RTCP from %s: Failed first packet validity check\n",
++			rtp, ast_sockaddr_stringify(&addr));
++		return &ast_null_frame;
++	}
++	do {
++		position += ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
++		if (packetwords <= position) {
++			break;
++		}
++		first_word = ntohl(rtcpheader[position]);
++	} while ((first_word & RTCP_VERSION_MASK_SHIFTED) == RTCP_VERSION_SHIFTED);
++	if (position != packetwords) {
++		ast_debug(1, "%p -- RTCP from %s: Failed packet version or length check\n",
++			rtp, ast_sockaddr_stringify(&addr));
++		return &ast_null_frame;
++	}
++
++	/*
++	 * Note: RFC3605 points out that true NAT (vs NAPT) can cause RTCP
++	 * to have a different IP address and port than RTP.  Otherwise, when
++	 * strictrtp is enabled we could reject RTCP packets not coming from
++	 * the learned RTP IP address if it is available.
++	 */
++
++	/*
++	 * strictrtp safety needs SSRC to match before we use the
++	 * sender's address for symmetrical RTP to send our RTCP
++	 * reports.
++	 *
++	 * If strictrtp is not enabled then claim to have already seen
++	 * a matching SSRC so we'll accept this packet's address for
++	 * symmetrical RTP.
++	 */
++	ssrc_seen = rtp->strict_rtp_state == STRICT_RTP_OPEN;
++
++	position = 0;
+ 	while (position < packetwords) {
+-		int i, pt, rc;
+-		unsigned int length, dlsr, lsr, msw, lsw, comp;
++		unsigned int i;
++		unsigned int pt;
++		unsigned int rc;
++		unsigned int ssrc;
++		/*! True if the ssrc value we have is valid and not garbage because it doesn't exist. */
++		unsigned int ssrc_valid;
++		unsigned int length;
++		unsigned int min_length;
++		unsigned int dlsr, lsr, msw, lsw, comp;
+ 		struct timeval now;
+ 		double rttsec, reported_jitter, reported_normdev_jitter_current, normdevrtt_current, reported_lost, reported_normdev_lost_current;
+ 		uint64_t rtt = 0;
+ 
+ 		i = position;
+-		length = ntohl(rtcpheader[i]);
+-		pt = (length & 0xff0000) >> 16;
+-		rc = (length & 0x1f000000) >> 24;
+-		length &= 0xffff;
++		first_word = ntohl(rtcpheader[i]);
++		pt = (first_word >> RTCP_PAYLOAD_TYPE_SHIFT) & RTCP_PAYLOAD_TYPE_MASK;
++		rc = (first_word >> RTCP_REPORT_COUNT_SHIFT) & RTCP_REPORT_COUNT_MASK;
++		/* RFC3550 says 'length' is the number of words in the packet - 1 */
++		length = ((first_word >> RTCP_LENGTH_SHIFT) & RTCP_LENGTH_MASK) + 1;
+ 
+-		if ((i + length) > packetwords) {
+-			if (rtpdebug)
+-				ast_debug(1, "RTCP Read too short\n");
++		/* Check expected RTCP packet record length */
++		min_length = RTCP_HEADER_SSRC_LENGTH;
++		switch (pt) {
++		case RTCP_PT_SR:
++			min_length += RTCP_SR_BLOCK_WORD_LENGTH;
++			/* fall through */
++		case RTCP_PT_RR:
++			min_length += (rc * RTCP_RR_BLOCK_WORD_LENGTH);
++			break;
++		case RTCP_PT_FUR:
++			break;
++		case RTCP_PT_SDES:
++		case RTCP_PT_BYE:
++			/*
++			 * There may not be a SSRC/CSRC present.  The packet is
++			 * useless but still valid if it isn't present.
++			 *
++			 * We don't know what min_length should be so disable the check
++			 */
++			min_length = length;
++			break;
++		default:
++			ast_debug(1, "%p -- RTCP from %s: %u(%s) skipping record\n",
++				rtp, ast_sockaddr_stringify(&addr), pt, rtcp_payload_type2str(pt));
++			if (rtcp_debug_test_addr(&addr)) {
++				ast_verbose("\n");
++				ast_verbose("RTCP from %s: %u(%s) skipping record\n",
++					ast_sockaddr_stringify(&addr), pt, rtcp_payload_type2str(pt));
++			}
++			position += length;
++			continue;
++		}
++		if (length < min_length) {
++			ast_debug(1, "%p -- RTCP from %s: %u(%s) length field less than expected minimum.  Min:%u Got:%u\n",
++				rtp, ast_sockaddr_stringify(&addr), pt, rtcp_payload_type2str(pt),
++				min_length - 1, length - 1);
+ 			return &ast_null_frame;
+ 		}
+ 
+-		if ((rtp->strict_rtp_state != STRICT_RTP_OPEN) && (ntohl(rtcpheader[i + 1]) != rtp->themssrc)) {
+-			/* Skip over this RTCP record as it does not contain the correct SSRC */
+-			position += (length + 1);
+-			ast_debug(1, "%p -- Received RTCP report from %s, dropping due to strict RTP protection. Received SSRC '%u' but expected '%u'\n",
+-				rtp, ast_sockaddr_stringify(&addr), ntohl(rtcpheader[i + 1]), rtp->themssrc);
+-			continue;
+-		}
+-
+-		if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
+-			/* Send to whoever sent to us */
+-			if (ast_sockaddr_cmp(&rtp->rtcp->them, &addr)) {
+-				ast_sockaddr_copy(&rtp->rtcp->them, &addr);
+-				if (rtpdebug)
+-					ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
+-						ast_sockaddr_stringify(&rtp->rtcp->them));
+-			}
++		/* Get the RTCP record SSRC if defined for the record */
++		ssrc_valid = 1;
++		switch (pt) {
++		case RTCP_PT_SR:
++		case RTCP_PT_RR:
++		case RTCP_PT_FUR:
++			ssrc = ntohl(rtcpheader[i + 1]);
++			break;
++		case RTCP_PT_SDES:
++		case RTCP_PT_BYE:
++		default:
++			ssrc = 0;
++			ssrc_valid = 0;
++			break;
+ 		}
+ 
+ 		if (rtcp_debug_test_addr(&addr)) {
+-			ast_verbose("\n\nGot RTCP from %s\n",
+-				    ast_sockaddr_stringify(&addr));
+-			ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown");
+-			ast_verbose("Reception reports: %d\n", rc);
+-			ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]);
++			ast_verbose("\n");
++			ast_verbose("RTCP from %s\n", ast_sockaddr_stringify(&addr));
++			ast_verbose("PT: %u(%s)\n", pt, rtcp_payload_type2str(pt));
++			ast_verbose("Reception reports: %u\n", rc);
++			ast_verbose("SSRC of sender: %u\n", ssrc);
+ 		}
+ 
+-		i += 2; /* Advance past header and ssrc */
++		if (ssrc_valid && rtp->themssrc_valid) {
++			if (ssrc != rtp->themssrc) {
++				/*
++				 * Skip over this RTCP record as it does not contain the
++				 * correct SSRC.  We should not act upon RTCP records
++				 * for a different stream.
++				 */
++				position += length;
++				ast_debug(1, "%p -- RTCP from %s: Skipping record, received SSRC '%u' != expected '%u'\n",
++					rtp, ast_sockaddr_stringify(&addr), ssrc, rtp->themssrc);
++				continue;
++			}
++			ssrc_seen = 1;
++		}
++
++		if (ssrc_seen && ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
++			/* Send to whoever sent to us */
++			if (ast_sockaddr_cmp(&rtp->rtcp->them, &addr)) {
++				ast_sockaddr_copy(&rtp->rtcp->them, &addr);
++				if (rtpdebug) {
++					ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
++						ast_sockaddr_stringify(&addr));
++				}
++			}
++		}
++
++		i += RTCP_HEADER_SSRC_LENGTH; /* Advance past header and ssrc */
+ 		if (rc == 0 && pt == RTCP_PT_RR) {      /* We're receiving a receiver report with no reports, which is ok */
+-			position += (length + 1);
++			position += length;
+ 			continue;
+ 		}
+ 
+@@ -3983,7 +4190,7 @@
+ 				ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2]));
+ 				ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4]));
+ 			}
+-			i += 5;
++			i += RTCP_SR_BLOCK_WORD_LENGTH;
+ 			if (rc < 1)
+ 				break;
+ 			/* Intentional fall through */
+@@ -4153,21 +4360,18 @@
+ 		case RTCP_PT_SDES:
+ 			if (rtcp_debug_test_addr(&addr))
+ 				ast_verbose("Received an SDES from %s\n",
+-					    ast_sockaddr_stringify(&rtp->rtcp->them));
++					ast_sockaddr_stringify(&addr));
+ 			break;
+ 		case RTCP_PT_BYE:
+ 			if (rtcp_debug_test_addr(&addr))
+ 				ast_verbose("Received a BYE from %s\n",
+-					    ast_sockaddr_stringify(&rtp->rtcp->them));
++					ast_sockaddr_stringify(&addr));
+ 			break;
+ 		default:
+-			ast_debug(1, "Unknown RTCP packet (pt=%d) received from %s\n",
+-				  pt, ast_sockaddr_stringify(&rtp->rtcp->them));
+ 			break;
+ 		}
+-		position += (length + 1);
++		position += length;
+ 	}
+-
+ 	rtp->rtcp->rtcp_info = 1;
+ 
+ 	return f;
+@@ -4344,39 +4548,156 @@
+ 		return &ast_null_frame;
+ 	}
+ 
++	/* If the version is not what we expected by this point then just drop the packet */
++	if (version != 2) {
++		return &ast_null_frame;
++	}
++
+ 	/* If strict RTP protection is enabled see if we need to learn the remote address or if we need to drop the packet */
+-	if (rtp->strict_rtp_state == STRICT_RTP_LEARN) {
+-		if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
+-			/* We are learning a new address but have received traffic from the existing address,
+-			 * accept it but reset the current learning for the new source so it only takes over
+-			 * once sufficient traffic has been received. */
+-			rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
++	switch (rtp->strict_rtp_state) {
++	case STRICT_RTP_LEARN:
++		/*
++		 * Scenario setup:
++		 * PartyA -- Ast1 -- Ast2 -- PartyB
++		 *
++		 * The learning timeout is necessary for Ast1 to handle the above
++		 * setup where PartyA calls PartyB and Ast2 initiates direct media
++		 * between Ast1 and PartyB.  Ast1 may lock onto the Ast2 stream and
++		 * never learn the PartyB stream when it starts.  The timeout makes
++		 * Ast1 stay in the learning state long enough to see and learn the
++		 * RTP stream from PartyB.
++		 *
++		 * To mitigate against attack, the learning state cannot switch
++		 * streams while there are competing streams.  The competing streams
++		 * interfere with each other's qualification.  Once we accept a
++		 * stream and reach the timeout, an attacker cannot interfere
++		 * anymore.
++		 *
++		 * Here are a few scenarios and each one assumes that the streams
++		 * are continuous:
++		 *
++		 * 1) We already have a known stream source address and the known
++		 * stream wants to change to a new source address.  An attacking
++		 * stream will block learning the new stream source.  After the
++		 * timeout we re-lock onto the original stream source address which
++		 * likely went away.  The result is one way audio.
++		 *
++		 * 2) We already have a known stream source address and the known
++		 * stream doesn't want to change source addresses.  An attacking
++		 * stream will not be able to replace the known stream.  After the
++		 * timeout we re-lock onto the known stream.  The call is not
++		 * affected.
++		 *
++		 * 3) We don't have a known stream source address.  This presumably
++		 * is the start of a call.  Competing streams will result in staying
++		 * in learning mode until a stream becomes the victor and we reach
++		 * the timeout.  We cannot exit learning if we have no known stream
++		 * to lock onto.  The result is one way audio until there is a victor.
++		 *
++		 * If we learn a stream source address before the timeout we will be
++		 * in scenario 1) or 2) when a competing stream starts.
++		 */
++		if (!ast_sockaddr_isnull(&rtp->strict_rtp_address)
++			&& STRICT_RTP_LEARN_TIMEOUT < ast_tvdiff_ms(ast_tvnow(), rtp->rtp_source_learn.start)) {
++			ast_verb(4, "%p -- Strict RTP learning complete - Locking on source address %s\n",
++				rtp, ast_sockaddr_stringify(&rtp->strict_rtp_address));
++			rtp->strict_rtp_state = STRICT_RTP_CLOSED;
++
++			/*
++			 * Clear the alternate remote address after learning.
++			 *
++			 * We should not leave this address laying around.
++			 * It gets set only on a chan_sip reINVITE glare.
++			 * We don't want a stale address interfering with
++			 * the next learning time.
++			 */
++			ast_sockaddr_setnull(&rtp->alt_rtp_address);
+ 		} else {
+-			/* Hmm, not the strict address. Perhaps we're getting audio from the alternate? */
+-			if (!ast_sockaddr_cmp(&rtp->alt_rtp_address, &addr)) {
+-				/* ooh, we did! You're now the new expected address, son! */
+-				ast_sockaddr_copy(&rtp->strict_rtp_address,
+-						  &addr);
+-			} else {
+-				/* Start trying to learn from the new address. If we pass a probationary period with
+-				 * it, that means we've stopped getting RTP from the original source and we should
+-				 * switch to it.
++			if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
++				/*
++				 * We are open to learning a new address but have received
++				 * traffic from the current address, accept it and reset
++				 * the learning counts for a new source.  When no more
++				 * current source packets arrive a new source can take over
++				 * once sufficient traffic is received.
+ 				 */
+-				if (rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
+-					ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets\n",
+-							rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
+-					return &ast_null_frame;
+-				}
+-				ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
++				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
++				break;
+ 			}
+ 
+-			ast_verb(4, "%p -- Probation passed - setting RTP source address to %s\n", rtp, ast_sockaddr_stringify(&addr));
+-			rtp->strict_rtp_state = STRICT_RTP_CLOSED;
++			/*
++			 * We give preferential treatment to the requested remote address
++			 * (negotiated SDP address) where we are to send our RTP.  However,
++			 * the other end has no obligation to send from that address even
++			 * though it is practically a requirement when NAT is involved.
++			 */
++			if (!ast_sockaddr_cmp(&remote_address, &addr)) {
++				/* Accept the negotiated remote RTP stream as the source */
++				ast_verb(4, "%p -- Strict RTP switching to RTP remote address %s as source\n",
++					rtp, ast_sockaddr_stringify(&addr));
++				ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
++				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
++				break;
++			}
++			/* Treat the alternate remote address as another negotiated SDP address. */
++			if (!ast_sockaddr_isnull(&rtp->alt_rtp_address)
++				&& !ast_sockaddr_cmp(&rtp->alt_rtp_address, &addr)) {
++				/* ooh, we did! You're now the new expected address, son! */
++				ast_verb(4, "%p -- Strict RTP switching to RTP alt remote address %s as source\n",
++					rtp, ast_sockaddr_stringify(&addr));
++				ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
++				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
++				break;
++			}
++
++			/*
++			 * Trying to learn a new address.  If we pass a probationary period
++			 * with it, that means we've stopped getting RTP from the original
++			 * source and we should switch to it.
++			 */
++			if (!ast_sockaddr_cmp(&rtp->rtp_source_learn.proposed_address, &addr)) {
++				if (!rtp_learning_rtp_seq_update(&rtp->rtp_source_learn, seqno)) {
++					/* Accept the new RTP stream */
++					ast_verb(4, "%p -- Strict RTP switching source address to %s\n",
++						rtp, ast_sockaddr_stringify(&addr));
++					ast_sockaddr_copy(&rtp->strict_rtp_address, &addr);
++					rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
++					break;
++				}
++				/* Not ready to accept the RTP stream candidate */
++				ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Will switch to it in %d packets.\n",
++					rtp, ast_sockaddr_stringify(&addr), rtp->rtp_source_learn.packets);
++			} else {
++				/*
++				 * This is either an attacking stream or
++				 * the start of the expected new stream.
++				 */
++				ast_sockaddr_copy(&rtp->rtp_source_learn.proposed_address, &addr);
++				rtp_learning_seq_init(&rtp->rtp_source_learn, seqno);
++				ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection. Qualifying new stream.\n",
++					rtp, ast_sockaddr_stringify(&addr));
++			}
++			return &ast_null_frame;
+ 		}
+-	} else if (rtp->strict_rtp_state == STRICT_RTP_CLOSED && ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
++		/* Fall through */
++	case STRICT_RTP_CLOSED:
++		/*
++		 * We should not allow a stream address change if the SSRC matches
++		 * once strictrtp learning is closed.  Any kind of address change
++		 * like this should have happened while we were in the learning
++		 * state.  We do not want to allow the possibility of an attacker
++		 * interfering with the RTP stream after the learning period.
++		 * An attacker could manage to get an RTCP packet redirected to
++		 * them which can contain the SSRC value.
++		 */
++		if (!ast_sockaddr_cmp(&rtp->strict_rtp_address, &addr)) {
++			break;
++		}
+ 		ast_debug(1, "%p -- Received RTP packet from %s, dropping due to strict RTP protection.\n",
+ 			rtp, ast_sockaddr_stringify(&addr));
+ 		return &ast_null_frame;
++	case STRICT_RTP_OPEN:
++		break;
+ 	}
+ 
+ 	/* If symmetric RTP is enabled see if the remote side is not what we expected and change where we are sending audio */
+@@ -4401,11 +4722,6 @@
+ 		return &ast_null_frame;
+ 	}
+ 
+-	/* If the version is not what we expected by this point then just drop the packet */
+-	if (version != 2) {
+-		return &ast_null_frame;
+-	}
+-
+ 	/* Pull out the various other fields we will need */
+ 	payloadtype = (seqno & 0x7f0000) >> 16;
+ 	padding = seqno & (1 << 29);
+@@ -4418,7 +4734,7 @@
+ 
+ 	AST_LIST_HEAD_INIT_NOLOCK(&frames);
+ 	/* Force a marker bit and change SSRC if the SSRC changes */
+-	if (rtp->rxssrc && rtp->rxssrc != ssrc) {
++	if (rtp->themssrc_valid && rtp->themssrc != ssrc) {
+ 		struct ast_frame *f, srcupdate = {
+ 			AST_FRAME_CONTROL,
+ 			.subclass.integer = AST_CONTROL_SRCCHANGE,
+@@ -4445,8 +4761,8 @@
+ 			rtp->rtcp->received_prior = 0;
+ 		}
+ 	}
+-
+-	rtp->rxssrc = ssrc;
++	rtp->themssrc = ssrc; /* Record their SSRC to put in future RR */
++	rtp->themssrc_valid = 1;
+ 
+ 	/* Remove any padding bytes that may be present */
+ 	if (padding) {
+@@ -4498,10 +4814,6 @@
+ 
+ 	prev_seqno = rtp->lastrxseqno;
+ 	rtp->lastrxseqno = seqno;
+-
+-	if (!rtp->themssrc) {
+-		rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
+-	}
+ 
+ 	if (rtp_debug_test_addr(&addr)) {
+ 		ast_verbose("Got  RTP packet from    %s (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6d)\n",
+@@ -4771,13 +5083,14 @@
+ 
+ 	rtp->rxseqno = 0;
+ 
+-	if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN && !ast_sockaddr_isnull(addr) &&
+-		ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
++	if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN
++		&& !ast_sockaddr_isnull(addr) && ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
+ 		/* We only need to learn a new strict source address if we've been told the source is
+ 		 * changing to something different.
+ 		 */
+-		rtp->strict_rtp_state = STRICT_RTP_LEARN;
+-		rtp_learning_seq_init(&rtp->rtp_source_learn, rtp->seqno);
++		ast_verb(4, "%p -- Strict RTP learning after remote address set to: %s\n",
++			rtp, ast_sockaddr_stringify(addr));
++		rtp_learning_start(rtp);
+ 	}
+ 
+ #ifdef HAVE_OPENSSL_SRTP
+@@ -4805,7 +5118,23 @@
+ 	 */
+ 	ast_sockaddr_copy(&rtp->alt_rtp_address, addr);
+ 
+-	return;
++	if (strictrtp && rtp->strict_rtp_state != STRICT_RTP_OPEN
++		&& !ast_sockaddr_isnull(addr) && ast_sockaddr_cmp(addr, &rtp->strict_rtp_address)) {
++		/*
++		 * We only need to learn a new strict source address if we've been told the
++		 * source may be changing to something different.
++		 *
++		 * XXX NOTE: The alternate source address is only set because of a reINVITE
++		 * glare in chan_sip.  A reINVITE glare is supposed to be retried after a
++		 * backoff delay so it shouldn't be needed at all.  However, I found this
++		 * as the best description of why it was added:
++		 * http://lists.digium.com/pipermail/asterisk-dev/2009-May/038348.html
++		 * https://reviewboard.asterisk.org/r/252/
++		 */
++		ast_verb(4, "%p -- Strict RTP learning after alternate remote address set to: %s\n",
++			rtp, ast_sockaddr_stringify(addr));
++		rtp_learning_start(rtp);
++	}
+ }
+ 
+ /*! \brief Write t140 redundacy frame
diff --git a/debian/patches/series b/debian/patches/series
index 87e8a63..bee18cc 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -47,3 +47,4 @@ AST-2016-007.patch
 AST-2016-009-11.diff
 AST-2017-005-11.diff
 AST-2017-006-11.diff
+AST-2017-008-11.diff

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list