[Pkg-voip-commits] r7763 - in /asterisk/branches/lenny/debian: changelog patches/iax2-encryption-fix patches/series

paravoid at alioth.debian.org paravoid at alioth.debian.org
Sat Nov 7 07:43:15 UTC 2009


Author: paravoid
Date: Sat Nov  7 07:43:14 2009
New Revision: 7763

URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=7763
Log:
Fix IAX2 encryption severe breakage. Thanks to Francois Marier for finding
the upstream bug report and preparing a fix. (Closes: #521641)

Added:
    asterisk/branches/lenny/debian/patches/iax2-encryption-fix
Modified:
    asterisk/branches/lenny/debian/changelog
    asterisk/branches/lenny/debian/patches/series

Modified: asterisk/branches/lenny/debian/changelog
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/changelog?rev=7763&op=diff
==============================================================================
--- asterisk/branches/lenny/debian/changelog (original)
+++ asterisk/branches/lenny/debian/changelog Sat Nov  7 07:43:14 2009
@@ -7,8 +7,10 @@
       (Closes: #522528)
     - "SIP responses expose valid usernames", AST-2009-008.
       (Closes: #554487)
-
- -- Faidon Liambotis <paravoid at debian.org>  Sat, 07 Nov 2009 09:34:02 +0200
+  * Fix IAX2 encryption severe breakage. Thanks to Francois Marier for finding
+    the upstream bug report and preparing a fix. (Closes: #521641)
+
+ -- Faidon Liambotis <paravoid at debian.org>  Sat, 07 Nov 2009 09:42:56 +0200
 
 asterisk (1:1.4.21.2~dfsg-3) unstable; urgency=medium
 

Added: asterisk/branches/lenny/debian/patches/iax2-encryption-fix
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/iax2-encryption-fix?rev=7763&op=file
==============================================================================
--- asterisk/branches/lenny/debian/patches/iax2-encryption-fix (added)
+++ asterisk/branches/lenny/debian/patches/iax2-encryption-fix Sat Nov  7 07:43:14 2009
@@ -1,0 +1,254 @@
+encrypted IAX2 during packet loss causes decryption to fail on
+retransmitted frames
+
+If an iax channel is encrypted, and a retransmit frame is sent, that
+packet's iseqno is updated while it is encrypted. This causes the
+entire frame to be corrupted. When the corrupted frame is sent, the
+other side decrypts it and sends a VNAK back because the decrypted
+frame doesn't make any sense. When we get the VNAK, we look through
+the sent queue and send the same corrupted frame causing a loop. To
+fix this, encrypted frames requiring retransmission are decrypted,
+updated, then re-encrypted. Since key-rotation may change the key held
+by the pvt struct, the keys used for encryption/decryption are held
+within the iax_frame to guarantee they remain correct.
+
+(source: http://bugs.digium.com/view.php?id=14607)
+
+  -- Francois Marier <francois at debian.org>
+
+--- a/channels/chan_iax2.c
++++ b/channels/chan_iax2.c
+@@ -574,7 +574,9 @@ struct chan_iax2_pvt {
+ 	int encmethods;
+ 	/*! Encryption AES-128 Key */
+ 	aes_encrypt_ctx ecx;
+-	/*! Decryption AES-128 Key */
++	/*! Decryption AES-128 Key corresponding to ecx */
++	aes_decrypt_ctx mydcx;
++	/*! Decryption AES-128 Key used to decrypt peer frames */
+ 	aes_decrypt_ctx dcx;
+ 	/*! 32 bytes of semi-random data */
+ 	unsigned char semirand[32];
+@@ -860,6 +862,10 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
+ static void realtime_update_peer(const char *peername, struct sockaddr_in *sin, time_t regtime);
+ static void prune_peers(void);
+ 
++static int decode_frame(aes_decrypt_ctx *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen);
++static int encrypt_frame(aes_encrypt_ctx *ecx, struct ast_iax2_full_hdr *fh, unsigned char *poo, int *datalen);
++static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt);
++  
+ static const struct ast_channel_tech iax2_tech = {
+ 	.type = "IAX2",
+ 	.description = tdesc,
+@@ -2134,11 +2140,22 @@ static int update_packet(struct iax_frame *f)
+ {
+ 	/* Called with iaxsl lock held, and iaxs[callno] non-NULL */
+ 	struct ast_iax2_full_hdr *fh = f->data;
++	struct ast_frame af;
++
++	/* if frame is encrypted. decrypt before updating it. */
++	if (f->encmethods) {
++		decode_frame(&f->mydcx, fh, &af, &f->datalen);
++	}
+ 	/* Mark this as a retransmission */
+ 	fh->dcallno = ntohs(IAX_FLAG_RETRANS | f->dcallno);
+ 	/* Update iseqno */
+ 	f->iseqno = iaxs[f->callno]->iseqno;
+ 	fh->iseqno = f->iseqno;
++
++	/* Now re-encrypt the frame */
++	if (f->encmethods) {
++		encrypt_frame(&f->ecx, fh, f->semirand, &f->datalen);
++	}
+ 	return 0;
+ }
+ 
+@@ -4098,10 +4115,19 @@ static int iax2_trunk_queue(struct chan_iax2_pvt *pvt, struct iax_frame *fr)
+ 	return 0;
+ }
+ 
+-static void build_enc_keys(const unsigned char *digest, aes_encrypt_ctx *ecx, aes_decrypt_ctx *dcx)
++static void build_encryption_keys(const unsigned char *digest, struct chan_iax2_pvt *pvt)
+ {
+-	aes_encrypt_key128(digest, ecx);
+-	aes_decrypt_key128(digest, dcx);
++	build_ecx_key(digest, pvt);
++	aes_decrypt_key128(digest, &pvt->dcx);
++}
++  
++static void build_ecx_key(const unsigned char *digest, struct chan_iax2_pvt *pvt)
++{
++	/* it is required to hold the corresponding decrypt key to our encrypt key
++	 * in the pvt struct because queued frames occasionally need to be decrypted and
++	 * re-encrypted when updated for a retransmission */
++	aes_encrypt_key128(digest, &pvt->ecx);
++	aes_decrypt_key128(digest, &pvt->mydcx);
+ }
+ 
+ static void memcpy_decrypt(unsigned char *dst, const unsigned char *src, int len, aes_decrypt_ctx *dcx)
+@@ -4254,7 +4280,7 @@ static int decrypt_frame(int callno, struct ast_iax2_full_hdr *fh, struct ast_fr
+ 			MD5Update(&md5, (unsigned char *)iaxs[callno]->challenge, strlen(iaxs[callno]->challenge));
+ 			MD5Update(&md5, (unsigned char *)tmppw, strlen(tmppw));
+ 			MD5Final(digest, &md5);
+-			build_enc_keys(digest, &iaxs[callno]->ecx, &iaxs[callno]->dcx);
++			build_encryption_keys(digest, iaxs[callno]);
+ 			res = decode_frame(&iaxs[callno]->dcx, fh, f, datalen);
+ 			if (!res) {
+ 				ast_set_flag(iaxs[callno], IAX_KEYPOPULATED);
+@@ -4349,6 +4375,7 @@ static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned in
+ 	fr->callno = pvt->callno;
+ 	fr->transfer = transfer;
+ 	fr->final = final;
++	fr->encmethods = 0;
+ 	if (!sendmini) {
+ 		/* We need a full frame */
+ 		if (seqno > -1)
+@@ -4402,6 +4429,10 @@ static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned in
+ 						iax_showframe(fr, NULL, 2, &pvt->addr, fr->datalen - sizeof(struct ast_iax2_full_hdr));
+ 				}
+ 				encrypt_frame(&pvt->ecx, fh, pvt->semirand, &fr->datalen);
++				fr->encmethods = pvt->encmethods;
++				fr->ecx = pvt->ecx;
++				fr->mydcx = pvt->mydcx;
++				memcpy(fr->semirand, pvt->semirand, sizeof(fr->semirand));
+ 			} else
+ 				ast_log(LOG_WARNING, "Supposed to send packet encrypted, but no key?\n");
+ 		}
+@@ -5591,7 +5622,7 @@ return_unref:
+ 	return res;
+ }
+ 
+-static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, aes_encrypt_ctx *ecx, aes_decrypt_ctx *dcx)
++static int authenticate(const char *challenge, const char *secret, const char *keyn, int authmethods, struct iax_ie_data *ied, struct sockaddr_in *sin, struct chan_iax2_pvt *pvt)
+ {
+ 	int res = -1;
+ 	int x;
+@@ -5631,8 +5662,9 @@ static int authenticate(const char *challenge, const char *secret, const char *k
+ 			/* If they support md5, authenticate with it.  */
+ 			for (x=0;x<16;x++)
+ 				sprintf(digres + (x << 1),  "%2.2x", digest[x]); /* safe */
+-			if (ecx && dcx)
+-				build_enc_keys(digest, ecx, dcx);
++			if (pvt) {
++				build_encryption_keys(digest, pvt);
++			}
+ 			iax_ie_append_str(ied, IAX_IE_MD5_RESULT, digres);
+ 			res = 0;
+ 		} else if (authmethods & IAX_AUTH_PLAINTEXT) {
+@@ -5673,7 +5705,7 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
+ 	/* Check for override RSA authentication first */
+ 	if (!ast_strlen_zero(override) || !ast_strlen_zero(okey)) {
+ 		/* Normal password authentication */
+-		res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, &p->ecx, &p->dcx);
++		res = authenticate(p->challenge, override, okey, authmethods, &ied, sin, p);
+ 	} else {
+ 		struct ao2_iterator i = ao2_iterator_init(peers, 0);
+ 		while ((peer = ao2_iterator_next(&i))) {
+@@ -5684,7 +5716,7 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
+ 			    && (!peer->addr.sin_addr.s_addr || ((sin->sin_addr.s_addr & peer->mask.s_addr) == (peer->addr.sin_addr.s_addr & peer->mask.s_addr)))
+ 			    /* No specified host, or this is our host */
+ 				) {
+-				res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
++				res = authenticate(p->challenge, peer->secret, peer->outkey, authmethods, &ied, sin, p);
+ 				if (!res) {
+ 					peer_unref(peer);
+ 					break;
+@@ -5703,7 +5735,7 @@ static int authenticate_reply(struct chan_iax2_pvt *p, struct sockaddr_in *sin,
+ 					peer_unref(peer);
+ 					return -1;
+ 				}
+-				res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, &p->ecx, &p->dcx);
++				res = authenticate(p->challenge, peer->secret,peer->outkey, authmethods, &ied, sin, p);
+ 				peer_unref(peer);
+ 			}
+ 			if (!peer) {
+@@ -6322,9 +6354,9 @@ static int registry_rerequest(struct iax_ies *ies, int callno, struct sockaddr_i
+ 				char tmpkey[256];
+ 				ast_copy_string(tmpkey, reg->secret + 1, sizeof(tmpkey));
+ 				tmpkey[strlen(tmpkey) - 1] = '\0';
+-				res = authenticate(challenge, NULL, tmpkey, authmethods, &ied, sin, NULL, NULL);
++				res = authenticate(challenge, NULL, tmpkey, authmethods, &ied, sin, NULL);
+ 			} else
+-				res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL, NULL);
++				res = authenticate(challenge, reg->secret, NULL, authmethods, &ied, sin, NULL);
+ 			if (!res) {
+ 				reg->regstate = REG_STATE_AUTHSENT;
+ 				return send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_REGREQ, 0, ied.buf, ied.pos, -1);
+--- a/channels/iax2-parser.h
++++ b/channels/iax2-parser.h
+@@ -19,6 +19,7 @@
+ #define _IAX2_PARSER_H
+ 
+ #include "asterisk/linkedlists.h"
++#include "asterisk/aes.h"
+ 
+ struct iax_ies {
+ 	char *called_number;
+@@ -86,41 +87,49 @@ struct iax_frame {
+ 	int sockfd;
+ #endif
+ 
+-	/* /Our/ call number */
++	/*! /Our/ call number */
+ 	unsigned short callno;
+-	/* /Their/ call number */
++	/*! /Their/ call number */
+ 	unsigned short dcallno;
+-	/* Start of raw frame (outgoing only) */
++	/*! Start of raw frame (outgoing only) */
+ 	void *data;
+-	/* Length of frame (outgoing only) */
++	/*! Length of frame (outgoing only) */
+ 	int datalen;
+-	/* How many retries so far? */
++	/*! How many retries so far? */
+ 	int retries;
+-	/* Outgoing relative timestamp (ms) */
++	/*! Outgoing relative timestamp (ms) */
+ 	unsigned int ts;
+-	/* How long to wait before retrying */
++	/*! How long to wait before retrying */
+ 	int retrytime;
+-	/* Are we received out of order?  */
++	/*! Are we received out of order?  */
+ 	unsigned int outoforder:1;
+-	/* Have we been sent at all yet? */
++	/*! Have we been sent at all yet? */
+ 	unsigned int sentyet:1;
+-	/* Non-zero if should be sent to transfer peer */
++	/*! Non-zero if should be sent to transfer peer */
+ 	unsigned int transfer:1;
+-	/* Non-zero if this is the final message */
++	/*! Non-zero if this is the final message */
+ 	unsigned int final:1;
+-	/* Ingress or outgres */
++	/*! Ingress or outgres */
+ 	unsigned int direction:2;
+-	/* Can this frame be cached? */
++	/*! Can this frame be cached? */
+ 	unsigned int cacheable:1;
+-	/* Outgoing Packet sequence number */
++	/*! Outgoing Packet sequence number */
+ 	int oseqno;
+-	/* Next expected incoming packet sequence number */
++	/*! Next expected incoming packet sequence number */
+ 	int iseqno;
+-	/* Retransmission ID */
++	/*! Retransmission ID */
+ 	int retrans;
+-	/* Easy linking */
++	/*! is this packet encrypted or not. if set this varible holds encryption methods*/
++	int encmethods;
++	/*! store encrypt key */
++	aes_encrypt_ctx ecx;
++	/*! store decrypt key which corresponds to ecx */
++	aes_decrypt_ctx mydcx;
++	/*! random data for encryption pad */
++	unsigned char semirand[32];
++	/*! Easy linking */
+ 	AST_LIST_ENTRY(iax_frame) list;
+-	/* Actual, isolated frame header */
++	/*! Actual, isolated frame header */
+ 	struct ast_frame af;
+ 	/*! Amount of space _allocated_ for data */
+ 	size_t afdatalen;

Modified: asterisk/branches/lenny/debian/patches/series
URL: http://svn.debian.org/wsvn/pkg-voip/asterisk/branches/lenny/debian/patches/series?rev=7763&op=diff
==============================================================================
--- asterisk/branches/lenny/debian/patches/series (original)
+++ asterisk/branches/lenny/debian/patches/series Sat Nov  7 07:43:14 2009
@@ -89,6 +89,8 @@
 zap-fix-cause34
 
 ### lenny fixes
+iax2-encryption-fix
+
 AST-2009-001
 
 r132790




More information about the Pkg-voip-commits mailing list