aboutsummaryrefslogtreecommitdiffstats
path: root/channels/chan_iax2.c
diff options
context:
space:
mode:
authordvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-03-11 17:40:10 +0000
committerdvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2009-03-11 17:40:10 +0000
commite469ef7e13d24a7e82318e73b18b9b0da7a469a6 (patch)
treec0cc6a4adaa8bc2f2c7cd90fd6cf674b4980446d /channels/chan_iax2.c
parentbf47c003531ca9a986ff478ae1dafcbb4ce5292a (diff)
Merged revisions 181371 via svnmerge from
https://origsvn.digium.com/svn/asterisk/trunk ................ r181371 | dvossel | 2009-03-11 12:34:57 -0500 (Wed, 11 Mar 2009) | 17 lines 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/ ........ ................ git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.6.1@181373 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/chan_iax2.c')
-rw-r--r--channels/chan_iax2.c59
1 files changed, 45 insertions, 14 deletions
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 398f716c8..42bf308e5 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -609,7 +609,9 @@ struct chan_iax2_pvt {
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*/
@@ -978,6 +980,9 @@ static void iax2_free_variable_datastore(void *);
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",
@@ -2436,11 +2441,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;
}
@@ -3998,7 +4014,7 @@ static int iax2_key_rotate(const void *vpvt)
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]);
@@ -4734,10 +4750,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, ast_aes_encrypt_key *ecx, ast_aes_decrypt_key *dcx)
+static void build_encryption_keys(const unsigned char *digest, struct chan_iax2_pvt *pvt)
{
- ast_aes_encrypt_key(digest, ecx);
- ast_aes_decrypt_key(digest, dcx);
+ 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)
@@ -4890,7 +4915,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);
@@ -4995,6 +5020,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)
@@ -5046,6 +5072,10 @@ static int iax2_send(struct chan_iax2_pvt *pvt, struct ast_frame *f, unsigned in
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");
}
@@ -6488,7 +6518,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, 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;
@@ -6528,8 +6558,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) {
@@ -6570,7 +6601,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))) {
@@ -6581,7 +6612,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;
@@ -6600,7 +6631,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) {
@@ -7289,9 +7320,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);