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-3ubuntu2) jaunty; urgency=low + + * Fix for IAX2 encrypted channels dropping out due to normal packet loss + + -- Francois Marier Sun, 29 Mar 2009 17:45:27 +1300 + asterisk (1:1.4.21.2~dfsg-3ubuntu1) jaunty; urgency=low * Merge from debian unstable, remaining changes: LP: #313988 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 + +--- 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;