[asterisk-commits] russell: trunk r135158 - in /trunk: ./ channels/ configs/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Aug 1 13:16:25 CDT 2008


Author: russell
Date: Fri Aug  1 13:16:24 2008
New Revision: 135158

URL: http://svn.digium.com/view/asterisk?view=rev&rev=135158
Log:
Merge changes from team/bbryant/keyrotation

This set of changes enhances IAX2 encryption support by adding key rotation
to provide enhanced security.  The key used for encryption is rotated right 
after the call gets set up, and then again every few minutes.  This was
discussed at the last AstriDevCon.  For interoperability with older versions
of Asterisk, there is an option that disables key rotation.

(closes issue #13018)
Reported by: bbryant
Patches:
      07072008__iax2_key_rotation.diff uploaded by bbryant (license 36)
Tested by: russell, bbryant

Modified:
    trunk/CHANGES
    trunk/channels/chan_iax2.c
    trunk/channels/iax2-parser.c
    trunk/channels/iax2.h
    trunk/configs/iax.conf.sample

Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=135158&r1=135157&r2=135158
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Aug  1 13:16:24 2008
@@ -137,6 +137,10 @@
 IAX Changes
 -----------
  * Existing DNS manager lookups extended to check for SRV records.
+ * IAX2 encryption support has been improved to support periodic key rotation
+   within a call for enhanced security.  The option "keyrotate" has been
+   provided to disable this functionality to preserve backwards compatibility
+   with older versions of IAX2 that do not support key rotation.
 
 CLI Changes
 -----------

Modified: trunk/channels/chan_iax2.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_iax2.c?view=diff&rev=135158&r1=135157&r2=135158
==============================================================================
--- trunk/channels/chan_iax2.c (original)
+++ trunk/channels/chan_iax2.c Fri Aug  1 13:16:24 2008
@@ -203,6 +203,23 @@
 #define DEFAULT_FREQ_OK		60 * 1000	/* How often to check for the host to be up */
 #define DEFAULT_FREQ_NOTOK	10 * 1000	/* How often to check, if the host is down... */
 
+/* if a pvt has encryption setup done and is running on the call */
+#define IAX_CALLENCRYPTED(pvt) \
+	(ast_test_flag(pvt, IAX_ENCRYPTED) && ast_test_flag(pvt, IAX_KEYPOPULATED))
+
+#define IAX_DEBUGDIGEST(msg, key) do { \
+		int idx; \
+		char digest[33] = ""; \
+		\
+		if (!iaxdebug) \
+			break; \
+		\
+		for (idx = 0; idx < 16; idx++) \
+			sprintf(digest + (idx << 1), "%2.2x", (unsigned char) key[idx]); \
+		\
+		ast_log(LOG_NOTICE, msg " IAX_COMMAND_RTKEY to rotate key to '%s'\n", digest); \
+	} while(0)
+
 static	struct io_context *io;
 static	struct sched_context *sched;
 
@@ -277,6 +294,7 @@
 						     response, so that we've achieved a three-way handshake with
 						     them before sending voice or anything else*/
 	IAX_ALLOWFWDOWNLOAD = (1 << 26),	/*!< Allow the FWDOWNL command? */
+	IAX_NOKEYROTATE = (1 << 27), /*!< Disable key rotation with encryption */
 };
 
 static int global_rtautoclear = 120;
@@ -588,6 +606,9 @@
 	ast_aes_encrypt_key ecx;
 	/*! Decryption AES-128 Key */
 	ast_aes_decrypt_key dcx;
+	/*! scheduler id associated with iax_key_rotate 
+	 * for encrypted calls*/
+	int keyrotateid;
 	/*! 32 bytes of semi-random data */
 	unsigned char semirand[32];
 	/*! Associated registry */
@@ -1411,6 +1432,7 @@
 	AST_SCHED_DEL(sched, pvt->authid);
 	AST_SCHED_DEL(sched, pvt->initid);
 	AST_SCHED_DEL(sched, pvt->jbid);
+	AST_SCHED_DEL(sched, pvt->keyrotateid);
 }
 
 static void iax2_frame_free(struct iax_frame *fr)
@@ -1479,6 +1501,7 @@
 	tmp->autoid = -1;
 	tmp->authid = -1;
 	tmp->initid = -1;
+	tmp->keyrotateid = -1;
 
 	ast_string_field_set(tmp,exten, "s");
 	ast_string_field_set(tmp,host, host);
@@ -1768,7 +1791,7 @@
 			iaxs[x]->pingid = iax2_sched_add(sched, ping_time * 1000, send_ping, (void *)(long)x);
 			iaxs[x]->lagid = iax2_sched_add(sched, lagrq_time * 1000, send_lagrq, (void *)(long)x);
 			iaxs[x]->amaflags = amaflags;
-			ast_copy_flags(iaxs[x], (&globalflags), IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+			ast_copy_flags(iaxs[x], &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_NOKEYROTATE);
 			
 			ast_string_field_set(iaxs[x], accountcode, accountcode);
 			ast_string_field_set(iaxs[x], mohinterpret, mohinterpret);
@@ -3384,7 +3407,7 @@
 	if (peer->maxms && ((peer->lastms > peer->maxms) || (peer->lastms < 0)))
 		goto return_unref;
 
-	ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+	ast_copy_flags(cai, peer, IAX_SENDANI | IAX_TRUNK | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_NOKEYROTATE);
 	cai->maxtime = peer->maxms;
 	cai->capability = peer->capability;
 	cai->encmethods = peer->encmethods;
@@ -3808,12 +3831,54 @@
 	return &ast_null_frame;
 }
 
+static int iax2_key_rotate(const void *vpvt)
+{
+	int res = 0;
+	struct chan_iax2_pvt *pvt = (void *) vpvt;
+	struct MD5Context md5;
+	char key[17] = "";
+	struct iax_ie_data ied = {
+		.pos = 0,	
+	};
+
+	ast_mutex_lock(&iaxsl[pvt->callno]);
+
+	pvt->keyrotateid = 
+		ast_sched_add(sched, 120000 + (ast_random() % 180001), iax2_key_rotate, vpvt);
+
+	snprintf(key, sizeof(key), "%lX", ast_random());
+
+	MD5Init(&md5);
+	MD5Update(&md5, (unsigned char *) key, strlen(key));
+	MD5Final((unsigned char *) key, &md5);
+
+	IAX_DEBUGDIGEST("Sending", key);
+
+	iax_ie_append_raw(&ied, IAX_IE_CHALLENGE, key, 16);
+
+	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);
+
+	ast_mutex_unlock(&iaxsl[pvt->callno]);
+
+	return res;
+}
+
 static int iax2_start_transfer(unsigned short callno0, unsigned short callno1, int mediaonly)
 {
 	int res;
 	struct iax_ie_data ied0;
 	struct iax_ie_data ied1;
 	unsigned int transferid = (unsigned int)ast_random();
+
+	if (IAX_CALLENCRYPTED(iaxs[callno0]) || IAX_CALLENCRYPTED(iaxs[callno1])) {
+		ast_debug(1, "transfers are not supported for encrypted calls at this time");
+		ast_set_flag(iaxs[callno0], IAX_NOTRANSFER);
+		ast_set_flag(iaxs[callno1], IAX_NOTRANSFER);
+		return 0;
+	}
+
 	memset(&ied0, 0, sizeof(ied0));
 	iaxs[callno0]->transferid = transferid;
 	iax_ie_append_addr(&ied0, IAX_IE_APPARENT_ADDR, &iaxs[callno1]->addr);
@@ -4720,8 +4785,23 @@
 	 * (the endpoint should detect the lost packet itself).  But, we want to do this here, so that we
 	 * increment the "predicted timestamps" for voice, if we're predicting */
 	if(f->frametype == AST_FRAME_VOICE && f->datalen == 0)
-	    return 0;
-
+		return 0;
+#if 0
+	ast_log(LOG_NOTICE, 
+		"f->frametype %c= AST_FRAME_VOICE, %sencrypted, %srotation scheduled...\n",
+		*("=!" + (f->frametype == AST_FRAME_VOICE)),
+		IAX_CALLENCRYPTED(pvt) ? "" : "not ",
+		pvt->keyrotateid != -1 ? "" : "no "
+	);
+#endif
+
+	if (pvt->keyrotateid == -1 && f->frametype == AST_FRAME_VOICE && IAX_CALLENCRYPTED(pvt)) {
+		if (ast_test_flag(pvt, IAX_NOKEYROTATE)) {
+			pvt->keyrotateid = -2;
+		} else {
+			iax2_key_rotate(pvt);
+		}
+	}
 
 	if ((ast_test_flag(pvt, IAX_TRUNK) || 
 			(((fts & 0xFFFF0000L) == (lastsent & 0xFFFF0000L)) ||
@@ -5896,6 +5976,7 @@
 		ast_copy_flags(iaxs[callno], user, IAX_CODEC_USER_FIRST);
 		ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOPREFS);
 		ast_copy_flags(iaxs[callno], user, IAX_CODEC_NOCAP);
+		ast_copy_flags(iaxs[callno], user, IAX_NOKEYROTATE);
 		iaxs[callno]->encmethods = user->encmethods;
 		/* Store the requested username if not specified */
 		if (ast_strlen_zero(iaxs[callno]->username))
@@ -9394,7 +9475,20 @@
 					iaxs[fr->callno]->transferring = TRANSFER_NONE;
 					iaxs[fr->callno]->mediareleased = 1;
 				}
-				break;	
+				break;
+			case IAX_COMMAND_RTKEY:
+				if (!IAX_CALLENCRYPTED(iaxs[fr->callno])) {
+					ast_log(LOG_WARNING, 
+						"we've been told to rotate our encryption key, "
+						"but this isn't an encrypted call. bad things will happen.\n"
+					);
+					break;
+				}
+
+				IAX_DEBUGDIGEST("Receiving", ies.challenge);
+
+				ast_aes_decrypt_key((unsigned char *) ies.challenge, &iaxs[fr->callno]->dcx);
+				break;
 			case IAX_COMMAND_DPREP:
 				complete_dpreply(iaxs[fr->callno], &ies);
 				break;
@@ -9993,7 +10087,7 @@
 	memset(&cai, 0, sizeof(cai));
 	cai.capability = iax2_capability;
 
-	ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
+	ast_copy_flags(&cai, &globalflags, IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_NOKEYROTATE);
 	
 	/* Populate our address from the given */
 	if (create_addr(pds.peer, NULL, &sin, &cai)) {
@@ -10012,7 +10106,7 @@
 	}
 
 	/* If this is a trunk, update it now */
-	ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);	
+	ast_copy_flags(iaxs[callno], &cai, IAX_TRUNK | IAX_SENDANI | IAX_NOTRANSFER | IAX_TRANSFERMEDIA | IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_NOKEYROTATE);
 	if (ast_test_flag(&cai, IAX_TRUNK)) {
 		int new_callno;
 		if ((new_callno = make_trunk(callno, 1)) != -1)
@@ -10353,6 +10447,9 @@
 
 	if (peer) {
 		if (firstpass) {
+			if (ast_test_flag(&globalflags, IAX_NOKEYROTATE)) {
+				ast_copy_flags(peer, &globalflags, IAX_NOKEYROTATE);
+			}
 			ast_copy_flags(peer, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF);
 			peer->encmethods = iax2_encryption;
 			peer->adsi = adsi;
@@ -10403,6 +10500,11 @@
 				peer->authmethods = get_auth_methods(v->value);
 			} else if (!strcasecmp(v->name, "encryption")) {
 				peer->encmethods = get_encrypt_methods(v->value);
+			} else if (!strcasecmp(v->name, "keyrotate")) {
+				if (ast_false(v->value))
+					ast_set_flag(peer, IAX_NOKEYROTATE);
+				else
+					ast_clear_flag(peer, IAX_NOKEYROTATE);
 			} else if (!strcasecmp(v->name, "transfer")) {
 				if (!strcasecmp(v->value, "mediaonly")) {
 					ast_set_flags_to(peer, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);	
@@ -10625,7 +10727,7 @@
 			user->adsi = adsi;
 			ast_string_field_set(user, name, name);
 			ast_string_field_set(user, language, language);
-			ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP);	
+			ast_copy_flags(user, &globalflags, IAX_USEJITTERBUF | IAX_FORCEJITTERBUF | IAX_CODEC_USER_FIRST | IAX_CODEC_NOPREFS | IAX_CODEC_NOCAP | IAX_NOKEYROTATE);	
 			ast_clear_flag(user, IAX_HASCALLERID);
 			ast_string_field_set(user, cid_name, "");
 			ast_string_field_set(user, cid_num, "");
@@ -10671,6 +10773,11 @@
 				user->authmethods = get_auth_methods(v->value);
 			} else if (!strcasecmp(v->name, "encryption")) {
 				user->encmethods = get_encrypt_methods(v->value);
+			} else if (!strcasecmp(v->name, "keyrotate")) {
+				if (ast_false(v->value))
+					ast_set_flag(user, IAX_NOKEYROTATE);
+				else
+					ast_clear_flag(user, IAX_NOKEYROTATE);
 			} else if (!strcasecmp(v->name, "transfer")) {
 				if (!strcasecmp(v->value, "mediaonly")) {
 					ast_set_flags_to(user, IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);	
@@ -11032,7 +11139,12 @@
 			authdebug = ast_true(v->value);
 		else if (!strcasecmp(v->name, "encryption"))
 			iax2_encryption = get_encrypt_methods(v->value);
-		else if (!strcasecmp(v->name, "transfer")) {
+		else if (!strcasecmp(v->name, "keyrotate")) {
+			if (ast_false(v->value))
+				ast_set_flag((&globalflags), IAX_NOKEYROTATE);
+			else
+				ast_clear_flag((&globalflags), IAX_NOKEYROTATE);
+		} else if (!strcasecmp(v->name, "transfer")) {
 			if (!strcasecmp(v->value, "mediaonly")) {
 				ast_set_flags_to((&globalflags), IAX_NOTRANSFER|IAX_TRANSFERMEDIA, IAX_TRANSFERMEDIA);	
 			} else if (ast_true(v->value)) {

Modified: trunk/channels/iax2-parser.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/iax2-parser.c?view=diff&rev=135158&r1=135157&r2=135158
==============================================================================
--- trunk/channels/iax2-parser.c (original)
+++ trunk/channels/iax2-parser.c Fri Aug  1 13:16:24 2008
@@ -85,6 +85,16 @@
 		snprintf(output, maxlen, "IPV4 %s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
 	} else {
 		ast_copy_string(output, "Invalid Address", maxlen);
+	}
+}
+
+static void dump_string_hex(char *output, int maxlen, void *value, int len)
+{
+	int i = 0;
+
+	while (len-- && (i + 1) * 4 < maxlen) {
+		sprintf(output + (4 * i), "\\x%2.2x", *((unsigned char *)value + i));
+		i++;
 	}
 }
 
@@ -229,7 +239,7 @@
 	{ IAX_IE_ADSICPE, "ADSICPE", dump_short },
 	{ IAX_IE_DNID, "DNID", dump_string },
 	{ IAX_IE_AUTHMETHODS, "AUTHMETHODS", dump_short },
-	{ IAX_IE_CHALLENGE, "CHALLENGE", dump_string },
+	{ IAX_IE_CHALLENGE, "CHALLENGE", dump_string_hex },
 	{ IAX_IE_MD5_RESULT, "MD5 RESULT", dump_string },
 	{ IAX_IE_RSA_RESULT, "RSA RESULT", dump_string },
 	{ IAX_IE_APPARENT_ADDR, "APPARENT ADDRESS", dump_addr },

Modified: trunk/channels/iax2.h
URL: http://svn.digium.com/view/asterisk/trunk/channels/iax2.h?view=diff&rev=135158&r1=135157&r2=135158
==============================================================================
--- trunk/channels/iax2.h (original)
+++ trunk/channels/iax2.h Fri Aug  1 13:16:24 2008
@@ -109,6 +109,8 @@
 	IAX_COMMAND_FWDATA =    37,
 	/*! Transfer media only */
 	IAX_COMMAND_TXMEDIA =   38,
+	/*! Command to rotate key */
+	IAX_COMMAND_RTKEY = 	39,
 };
 
 /*! By default require re-registration once per minute */

Modified: trunk/configs/iax.conf.sample
URL: http://svn.digium.com/view/asterisk/trunk/configs/iax.conf.sample?view=diff&rev=135158&r1=135157&r2=135158
==============================================================================
--- trunk/configs/iax.conf.sample (original)
+++ trunk/configs/iax.conf.sample Fri Aug  1 13:16:24 2008
@@ -172,6 +172,15 @@
 ;
 ; trunkmtu = 0
 ;
+; Enable IAX2 encryption.  The default is no.
+;
+; encryption = yes
+;
+; This is a compatibility option for older versions of IAX2 that do not support
+; key rotation with encryption.  This option will disable the IAX_COMMAND_RTENC message.
+; default is on
+;
+; keyrotate=off
 
 ; This option defines the maximum size an IAX2 trunk can grow to. The default value is 128000 bytes which
 ; represents 40ms uncompressed linear with 200 channels. Depending on different things though
@@ -385,6 +394,12 @@
 ;accountcode=markster0101
 ;permit=209.16.236.73/255.255.255.0
 ;language=en		; Use english as default language
+;encryption=yes ; Enable IAX2 encryption.  The default is no.
+;keyrotate=off ; This is a compatibility option for older versions of
+;              ; IAX2 that do not support key rotation with encryption. 
+;              ; This option will disable the IAX_COMMAND_RTENC message. 
+;              ; default is on.
+;              ; 
 ;
 ; Peers may also be specified, with a secret and
 ; a remote hostname.
@@ -407,8 +422,13 @@
 ;qualifyfreqnotok = 10000	; how frequently to ping the peer when it's
 				; either LAGGED or UNAVAILABLE, in milliseconds
 ;jitterbuffer=no		; Turn off jitter buffer for this peer
-
-;
+;
+;encryption=yes ; Enable IAX2 encryption.  The default is no.
+;keyrotate=off ; This is a compatibility option for older versions of
+;              ; IAX2 that do not support key rotation with encryption. 
+;              ; This option will disable the IAX_COMMAND_RTENC message. 
+;              ; default is on.
+;              ; 
 ; Peers can remotely register as well, so that they can be mobile.  Default
 ; IP's can also optionally be given but are not required.  Caller*ID can be
 ; suggested to the other side as well if it is for example a phone instead of




More information about the asterisk-commits mailing list