diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 46b3b5ff81..d8e00881e4 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -690,7 +690,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*/ @@ -1059,6 +1061,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", @@ -2528,11 +2533,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; } @@ -4104,7 +4120,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]); @@ -4840,10 +4856,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) @@ -4996,7 +5021,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); @@ -5101,6 +5126,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) @@ -5152,6 +5178,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"); } @@ -6648,7 +6678,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; @@ -6688,8 +6718,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) { @@ -6730,7 +6761,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))) { @@ -6741,7 +6772,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; @@ -6760,7 +6791,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) { @@ -7454,9 +7485,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); diff --git a/channels/iax2-parser.h b/channels/iax2-parser.h index e40669d3d4..e75cd4209d 100644 --- 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; @@ -89,41 +90,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 */ + 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;