[asterisk-commits] dvossel: trunk r181371 - in /trunk: ./ channels/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 11 12:35:04 CDT 2009


Author: dvossel
Date: Wed Mar 11 12:34:57 2009
New Revision: 181371

URL: http://svn.digium.com/svn-view/asterisk?view=rev&rev=181371
Log:
Merged revisions 181340 via svnmerge from 
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
  r181340 | dvossel | 2009-03-11 12:25:31 -0500 (Wed, 11 Mar 2009) | 11 lines
  
  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.
  
  (closes issue #14607)
  Reported by: stevenla
  Tested by: dvossel
  
  Review: http://reviewboard.digium.com/r/192/
........

Modified:
    trunk/   (props changed)
    trunk/channels/chan_iax2.c
    trunk/channels/iax2-parser.h

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.4-merged' - no diff available.

Modified: trunk/channels/chan_iax2.c
URL: http://svn.digium.com/svn-view/asterisk/trunk/channels/chan_iax2.c?view=diff&rev=181371&r1=181370&r2=181371
==============================================================================
--- trunk/channels/chan_iax2.c (original)
+++ trunk/channels/chan_iax2.c Wed Mar 11 12:34:57 2009
@@ -690,7 +690,9 @@
 	int encmethods;
 	/*! Encryption AES-128 Key */
 	ast_aes_encrypt_key ecx;
-	/*! Decryption AES-128 Key */
+	/*! Decryption AES-128 Key corresponding to ecx */
+	ast_aes_decrypt_key mydcx;
+	/*! Decryption AES-128 Key used to decrypt peer frames */
 	ast_aes_decrypt_key dcx;
 	/*! scheduler id associated with iax_key_rotate 
 	 * for encrypted calls*/
@@ -1059,6 +1061,9 @@
 
 static int acf_channel_read(struct ast_channel *chan, const char *funcname, char *preparse, char *buf, size_t buflen);
 static int acf_channel_write(struct ast_channel *chan, const char *function, char *data, const char *value);
+static int decode_frame(ast_aes_decrypt_key *dcx, struct ast_iax2_full_hdr *fh, struct ast_frame *f, int *datalen);
+static int encrypt_frame(ast_aes_encrypt_key *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",
@@ -2528,11 +2533,22 @@
 {
 	/* 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;
 }
 
@@ -4104,7 +4120,7 @@
 
 	res = send_command(pvt, AST_FRAME_IAX, IAX_COMMAND_RTKEY, 0, ied.buf, ied.pos, -1);
 
-	ast_aes_encrypt_key((unsigned char *) key, &pvt->ecx);
+	build_ecx_key((unsigned char *) key, pvt);
 
 	ast_mutex_unlock(&iaxsl[pvt->callno]);
 
@@ -4840,10 +4856,19 @@
 	return 0;
 }
 
-static void build_enc_keys(const unsigned char *digest, ast_aes_encrypt_key *ecx, ast_aes_decrypt_key *dcx)
-{
-	ast_aes_encrypt_key(digest, ecx);
-	ast_aes_decrypt_key(digest, dcx);
+static void build_encryption_keys(const unsigned char *digest, struct chan_iax2_pvt *pvt)
+{
+	build_ecx_key(digest, pvt);
+	ast_aes_decrypt_key(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 */
+	ast_aes_encrypt_key(digest, &pvt->ecx);
+	ast_aes_decrypt_key(digest, &pvt->mydcx);
 }
 
 static void memcpy_decrypt(unsigned char *dst, const unsigned char *src, int len, ast_aes_decrypt_key *dcx)
@@ -4996,7 +5021,7 @@
 			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);
@@ -5101,6 +5126,7 @@
 	fr->callno = pvt->callno;
 	fr->transfer = transfer;
 	fr->final = final;
+	fr->encmethods = 0;
 	if (!sendmini) {
 		/* We need a full frame */
 		if (seqno > -1)
@@ -5152,6 +5178,10 @@
 				else
 					iax_outputframe(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");
 		}
@@ -6648,7 +6678,7 @@
 	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, ast_aes_encrypt_key *ecx, ast_aes_decrypt_key *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;
@@ -6688,8 +6718,9 @@
 			/* 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) {
@@ -6730,7 +6761,7 @@
 	/* 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))) {
@@ -6741,7 +6772,7 @@
 			    && (!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;
@@ -6760,7 +6791,7 @@
 					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) {
@@ -7454,9 +7485,9 @@
 				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);

Modified: trunk/channels/iax2-parser.h
URL: http://svn.digium.com/svn-view/asterisk/trunk/channels/iax2-parser.h?view=diff&rev=181371&r1=181370&r2=181371
==============================================================================
--- trunk/channels/iax2-parser.h (original)
+++ trunk/channels/iax2-parser.h Wed Mar 11 12:34:57 2009
@@ -19,6 +19,7 @@
 #define _IAX2_PARSER_H
 
 #include "asterisk/linkedlists.h"
+#include "asterisk/aes.h"
 
 struct iax_ies {
 	char *called_number;
@@ -89,41 +90,49 @@
 	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 */
+	ast_aes_encrypt_key ecx;
+	/*! store decrypt key which corresponds to ecx */
+	ast_aes_decrypt_key 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;




More information about the asterisk-commits mailing list