diff --git a/kernel-module/xt_MEDIAPROXY.c b/kernel-module/xt_MEDIAPROXY.c index 32aea933f..2742768f4 100644 --- a/kernel-module/xt_MEDIAPROXY.c +++ b/kernel-module/xt_MEDIAPROXY.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,11 @@ MODULE_LICENSE("GPL"); +struct mp_hmac; +struct mp_cipher; + + + static struct proc_dir_entry *my_proc_root; static struct proc_dir_entry *proc_list; @@ -114,6 +120,9 @@ struct mp_crypto_context { unsigned char session_auth_key[20]; u_int32_t roc; struct crypto_cipher *tfm; + struct crypto_shash *shash; + const struct mp_cipher *cipher; + const struct mp_hmac *hmac; }; struct mediaproxy_target { @@ -148,10 +157,34 @@ struct mediaproxy_table { struct mp_cipher { const char *name; + const char *tfm_name; }; struct mp_hmac { const char *name; + const char *tfm_name; +}; + +/* XXX shared */ +struct rtp_header { + unsigned char v_p_x_cc; + unsigned char m_pt; + u_int16_t seq_num; + u_int32_t timestamp; + u_int32_t ssrc; + u_int32_t csrc[]; +} __attribute__ ((packed)); +struct rtp_extension { + u_int16_t undefined; + u_int16_t length; +} __attribute__ ((packed)); + + +struct rtp_parsed { + struct rtp_header *header; + unsigned int header_len; + unsigned char *payload; + unsigned int payload_len; }; @@ -216,9 +249,11 @@ static const struct mp_cipher mp_ciphers[] = { }, [MPC_AES_CM] = { .name = "AES-CM", + .tfm_name = "aes", }, [MPC_AES_F8] = { .name = "AES-F8", + .tfm_name = "aes", }, }; @@ -231,6 +266,7 @@ static const struct mp_hmac mp_hmacs[] = { }, [MPH_HMAC_SHA1] = { .name = "HMAC-SHA1", + .tfm_name = "hmac(sha1)", }, }; @@ -353,6 +389,13 @@ static struct mediaproxy_table *new_table_link(u_int32_t id) { +static void free_crypto_context(struct mp_crypto_context *c) { + if (c->tfm) + crypto_free_cipher(c->tfm); + if (c->shash) + crypto_free_shash(c->shash); +} + static void target_push(struct mediaproxy_target *t) { if (!t) return; @@ -362,10 +405,8 @@ static void target_push(struct mediaproxy_target *t) { DBG("Freeing target\n"); - if (t->decrypt.tfm) - crypto_free_cipher(t->decrypt.tfm); - if (t->encrypt.tfm) - crypto_free_cipher(t->encrypt.tfm); + free_crypto_context(&t->decrypt); + free_crypto_context(&t->encrypt); kfree(t); } @@ -851,6 +892,7 @@ static int validate_srtp(struct mediaproxy_srtp *s) { +/* XXX shared code */ static void aes_ctr_128(unsigned char *out, const unsigned char *in, int in_len, struct crypto_cipher *tfm, const char *iv) { @@ -960,26 +1002,42 @@ static int gen_session_key(unsigned char *out, int len, struct mediaproxy_srtp * static int gen_session_keys(struct mp_crypto_context *c, struct mediaproxy_srtp *s) { int ret; + const char *err; - if (s->cipher == MPC_NULL) + if (s->cipher == MPC_NULL && s->hmac == MPH_NULL) return 0; + err = "failed to generate session key"; ret = gen_session_key(c->session_key, 16, s, 0x00); if (ret) - return ret; + goto error; ret = gen_session_key(c->session_auth_key, 20, s, 0x01); if (ret) - return ret; + goto error; ret = gen_session_key(c->session_salt, 14, s, 0x02); if (ret) - return ret; + goto error; + + if (c->cipher->tfm_name) { + err = "failed to load cipher"; + c->tfm = crypto_alloc_cipher(c->cipher->tfm_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(c->tfm)) { + ret = PTR_ERR(c->tfm); + c->tfm = NULL; + goto error; + } + crypto_cipher_setkey(c->tfm, c->session_key, 16); + } - c->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(c->tfm)) { - ret = PTR_ERR(c->tfm); - c->tfm = NULL; - return ret; + if (c->hmac->tfm_name) { + err = "failed to load HMAC"; + c->shash = crypto_alloc_shash(c->hmac->tfm_name, 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(c->shash)) { + ret = PTR_ERR(c->shash); + c->shash = NULL; + goto error; + } + crypto_shash_setkey(c->shash, c->session_auth_key, 20); } - crypto_cipher_setkey(c->tfm, c->session_key, 16); DBG("master key %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", s->master_key[0], s->master_key[1], s->master_key[2], s->master_key[3], @@ -1001,11 +1059,29 @@ static int gen_session_keys(struct mp_crypto_context *c, struct mediaproxy_srtp c->session_salt[4], c->session_salt[5], c->session_salt[6], c->session_salt[7], c->session_salt[8], c->session_salt[9], c->session_salt[10], c->session_salt[11], c->session_salt[12], c->session_salt[13]); + DBG("session auth key %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + c->session_auth_key[0], c->session_auth_key[1], c->session_auth_key[2], c->session_auth_key[3], + c->session_auth_key[4], c->session_auth_key[5], c->session_auth_key[6], c->session_auth_key[7], + c->session_auth_key[8], c->session_auth_key[9], c->session_auth_key[10], c->session_auth_key[11], + c->session_auth_key[12], c->session_auth_key[13], c->session_auth_key[14], c->session_auth_key[15], + c->session_auth_key[16], c->session_auth_key[17], c->session_auth_key[18], c->session_auth_key[19]); return 0; + +error: + if (c->tfm) + crypto_free_cipher(c->tfm); + if (c->shash) + crypto_free_shash(c->shash); + printk(KERN_ERR "Failed to generate session keys: %s\n", err); + return ret; } +static void crypto_context_init(struct mp_crypto_context *c, struct mediaproxy_srtp *s) { + c->cipher = &mp_ciphers[s->cipher]; + c->hmac = &mp_hmacs[s->hmac]; +} static int table_new_target(struct mediaproxy_table *t, struct mediaproxy_target_info *i, int update) { unsigned char hi, lo; @@ -1047,6 +1123,8 @@ static int table_new_target(struct mediaproxy_table *t, struct mediaproxy_target spin_lock_init(&g->decrypt.lock); spin_lock_init(&g->encrypt.lock); memcpy(&g->target, i, sizeof(*i)); + crypto_context_init(&g->decrypt, &g->target.decrypt); + crypto_context_init(&g->encrypt, &g->target.encrypt); err = gen_session_keys(&g->decrypt, &g->target.decrypt); if (err) @@ -1455,6 +1533,153 @@ drop: +/* XXX shared code */ +static int parse_rtp(struct rtp_parsed *rtp, struct sk_buff *skb) { + struct rtp_extension *ext; + int ext_len; + + memset(rtp, 0, sizeof(*rtp)); + + if (skb->len < sizeof(*rtp->header)) + return -1; + rtp->header = (void *) skb->data; + if ((rtp->header->v_p_x_cc & 0xc0) != 0x80) /* version 2 */ + return -1; + rtp->header_len = sizeof(*rtp->header); + + /* csrc list */ + rtp->header_len += (rtp->header->v_p_x_cc & 0xf) * 4; + if (skb->len < rtp->header_len) + return -1; + rtp->payload = skb->data + rtp->header_len; + rtp->payload_len = skb->len - rtp->header_len; + + if ((rtp->header->v_p_x_cc & 0x10)) { + /* extension */ + if (rtp->payload_len < sizeof(*ext)) + return -1; + ext = (void *) rtp->payload; + ext_len = 4 + ntohs(ext->length) * 4; + if (rtp->payload_len < ext_len) + return -1; + rtp->payload += ext_len; + rtp->payload_len -= ext_len; + } + + DBG("rtp header parsed, payload length is %u\n", rtp->payload_len); + + return 0; +} + +/* XXX shared code */ +static u_int64_t packet_index(struct mp_crypto_context *c, + struct mediaproxy_srtp *s, struct rtp_header *rtp) +{ + u_int16_t seq; + u_int64_t index; + long long int diff; + + seq = ntohs(rtp->seq_num); + + spin_lock(&c->lock); + + /* rfc 3711 section 3.3.1 */ + if (unlikely(!s->last_index)) + s->last_index = seq; + + /* rfc 3711 appendix A, modified, and sections 3.3 and 3.3.1 */ + index = ((u_int64_t) c->roc << 16) | seq; + diff = index - s->last_index; + if (diff >= 0) { + if (diff < 0x8000) + s->last_index = index; + else if (index >= 0x10000) + index -= 0x10000; + } + else { + if (diff >= -0x8000) + ; + else { + index += 0x10000; + c->roc++; + s->last_index = index; + } + } + + spin_unlock(&c->lock); + + return index; +} + +static int srtp_auth_validate(struct mp_crypto_context *c, + struct mediaproxy_srtp *s, struct rtp_parsed *r) +{ + unsigned char *auth_tag; + unsigned char hmac[20]; + struct shash_desc *dsc; + u_int64_t pkt_idx; + u_int32_t roc; + + if (!c->hmac) + return 0; + if (!c->shash) + return -1; + + if (r->payload_len < s->auth_tag_len) + return -1; + + r->payload_len -= s->auth_tag_len; + auth_tag = r->payload + r->payload_len; + + if (r->payload_len < s->mki_len) + return -1; + r->payload_len -= s->mki_len; + + if (!s->auth_tag_len) + return 0; + + pkt_idx = packet_index(c, s, r->header); + roc = htonl((pkt_idx & 0xffffffff0000ULL) >> 16); + + dsc = kmalloc(sizeof(*dsc) + crypto_shash_descsize(c->shash), GFP_ATOMIC); + if (!dsc) + return -1; + + dsc->tfm = c->shash; + dsc->flags = 0; + + if (crypto_shash_init(dsc)) + goto error; + + crypto_shash_update(dsc, (void *) r->header, r->header_len + r->payload_len); + crypto_shash_update(dsc, (void *) &roc, sizeof(roc)); + + crypto_shash_final(dsc, hmac); + + kfree(dsc); + + DBG("calculated HMAC %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + hmac[0], hmac[1], hmac[2], hmac[3], + hmac[4], hmac[5], hmac[6], hmac[7], + hmac[8], hmac[9], hmac[10], hmac[11], + hmac[12], hmac[13], hmac[14], hmac[15], + hmac[16], hmac[17], hmac[18], hmac[19]); + DBG("packet auth tag %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + auth_tag[0], auth_tag[1], auth_tag[2], auth_tag[3], + auth_tag[4], auth_tag[5], auth_tag[6], auth_tag[7], + auth_tag[8], auth_tag[9], auth_tag[10], auth_tag[11], + auth_tag[12], auth_tag[13], auth_tag[14], auth_tag[15], + auth_tag[16], auth_tag[17], auth_tag[18], auth_tag[19]); + + if (memcmp(auth_tag, hmac, s->auth_tag_len)) + return -1; + return 0; + +error: + kfree(dsc); + return -1; +} + static unsigned int mediaproxy46(struct sk_buff *skb, struct mediaproxy_table *t) { struct udphdr *uh; struct mediaproxy_target *g; @@ -1463,6 +1688,7 @@ static unsigned int mediaproxy46(struct sk_buff *skb, struct mediaproxy_table *t unsigned int datalen; unsigned long flags; u_int32_t *u32; + struct rtp_parsed rtp; skb_reset_transport_header(skb); uh = udp_hdr(skb); @@ -1497,7 +1723,15 @@ not_stun: goto skip2; DBG("target found, src "MIPF" -> dst "MIPF"\n", MIPP(g->target.src_addr), MIPP(g->target.dst_addr)); + DBG("target decrypt hmac and cipher are %s and %s", g->decrypt.hmac->name, + g->decrypt.cipher->name); + + if (parse_rtp(&rtp, skb)) + goto not_rtp; + if (srtp_auth_validate(&g->decrypt, &g->target.decrypt, &rtp)) + goto skip3; +not_rtp: if (g->target.mirror_addr.family) { DBG("sending mirror packet to dst "MIPF"\n", MIPP(g->target.mirror_addr)); skb2 = skb_copy(skb, GFP_ATOMIC); @@ -1525,6 +1759,8 @@ not_stun: return NF_DROP; +skip3: + target_push(g); skip2: kfree_skb(skb); table_push(t); @@ -1672,12 +1908,14 @@ static struct xt_target xt_mediaproxy_regs[] = { static int __init init(void) { int ret; + const char *err; printk(KERN_NOTICE "Registering xt_MEDIAPROXY module - version %s\n", MEDIAPROXY_VERSION); rwlock_init(&table_lock); ret = -ENOMEM; + err = "could not register /proc/ entries"; my_proc_root = proc_mkdir("mediaproxy", NULL); if (!my_proc_root) goto fail; @@ -1695,6 +1933,7 @@ static int __init init(void) { /* proc_list->owner = THIS_MODULE; */ proc_list->proc_fops = &proc_main_list_ops; + err = "could not register xtables target"; ret = xt_register_targets(xt_mediaproxy_regs, ARRAY_SIZE(xt_mediaproxy_regs)); if (ret) goto fail; @@ -1706,6 +1945,8 @@ fail: clear_proc(&proc_list); clear_proc(&my_proc_root); + printk(KERN_ERR "Failed to load xt_MEDIAPROXY module: %s\n", err); + return ret; }