Package: asterisk Version: 1:1.4.21.2~dfsg-3 Severity: normal Tags: patch As described upstream [0], IAX2 encryption is broken in the Debian version of asterisk:
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. This makes it practically impossible to turn IAX2 encryption in most of my calls because the connection constantly cuts off. I have attached a debdiff which applies the upstream patch. I would be interested in pushing for this to get included in the next lenny release. What do you think? Cheers, Francois [0] http://bugs.digium.com/view.php?id=14607 -- System Information: Debian Release: squeeze/sid APT prefers unstable APT policy: (500, 'unstable'), (1, 'experimental') Architecture: amd64 (x86_64) Kernel: Linux 2.6.28.9-grsec (SMP w/2 CPU cores; PREEMPT) Locale: LANG=fr_CA.UTF-8, LC_CTYPE=fr_CA.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Versions of packages asterisk depends on: ii adduser 3.110 add and remove users and groups pn asterisk-config | aste <none> (no description available) pn asterisk-sounds-main <none> (no description available) ii libasound2 1.0.19-1 shared library for ALSA applicatio pn libc-client2007b <none> (no description available) ii libc6 2.9-6 GNU C Library: Shared libraries ii libcap2 1:2.16-4 support for getting/setting POSIX. ii libcurl3 7.18.2-8.1 Multi-protocol file transfer libra ii libgcc1 1:4.3.3-5 GCC support library ii libgsm1 1.0.12-1 Shared libraries for GSM speech co pn libiksemel3 <none> (no description available) ii libncurses5 5.7+20090314-1 shared libraries for terminal hand ii libnewt0.52 0.52.2-11.3 Not Erik's Windowing Toolkit - tex ii libogg0 1.1.3-5 Ogg Bitstream Library ii libpopt0 1.14-4 lib for parsing cmdline parameters ii libpq5 8.3.7-1 PostgreSQL C client library pn libpri1.0 <none> (no description available) pn libradiusclient-ng2 <none> (no description available) ii libsnmp15 5.4.1~dfsg-12 SNMP (Simple Network Management Pr ii libspeex1 1.2~rc1-1 The Speex codec runtime library ii libspeexdsp1 1.2~rc1-1 The Speex extended runtime library pn libsqlite0 <none> (no description available) ii libssl0.9.8 0.9.8g-15 SSL shared libraries ii libstdc++6 4.3.3-5 The GNU Standard C++ Library v3 pn libtonezone1 <none> (no description available) ii libvorbis0a 1.2.0.dfsg-4 The Vorbis General Audio Compressi ii libvorbisenc2 1.2.0.dfsg-4 The Vorbis General Audio Compressi pn libvpb0 <none> (no description available) ii unixodbc 2.2.11-16 ODBC tools libraries ii zlib1g 1:1.2.3.3.dfsg-13 compression library - runtime asterisk recommends no packages. Versions of packages asterisk suggests: pn asterisk-dev <none> (no description available) pn asterisk-doc <none> (no description available) pn asterisk-h323 <none> (no description available) ii ekiga 2.0.12-1+nmu1+b1 H.323 and SIP compatible VoIP clie pn kphone <none> (no description available) pn ohphone <none> (no description available) ii twinkle 1:1.4.2-1 Voice over Internet Protocol (VoIP
diff -u asterisk-1.4.21.2~dfsg/debian/changelog asterisk-1.4.21.2~dfsg/debian/changelog --- asterisk-1.4.21.2~dfsg/debian/changelog +++ asterisk-1.4.21.2~dfsg/debian/changelog @@ -1,3 +1,9 @@ +asterisk (1:1.4.21.2~dfsg-3.1) unstable; urgency=low + + * Fix for IAX2 encrypted channels dropping out due to normal packet loss + + -- Francois Marier <[email protected]> Sat, 28 Mar 2009 16:29:50 +1300 + asterisk (1:1.4.21.2~dfsg-3) unstable; urgency=medium [ Faidon Liambotis ] diff -u asterisk-1.4.21.2~dfsg/debian/patches/series asterisk-1.4.21.2~dfsg/debian/patches/series --- asterisk-1.4.21.2~dfsg/debian/patches/series +++ asterisk-1.4.21.2~dfsg/debian/patches/series @@ -16,6 +16,8 @@ ael-segfault-fix +iax2encryption_fix + ### new features pubkey_jnctn func_devstate only in patch2: unchanged: --- asterisk-1.4.21.2~dfsg.orig/debian/patches/iax2encryption_fix +++ asterisk-1.4.21.2~dfsg/debian/patches/iax2encryption_fix @@ -0,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 <[email protected]> + +--- 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;

