|
|
|
|
@ -8,6 +8,7 @@
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
|
#include <linux/crypto.h>
|
|
|
|
|
#include <crypto/aes.h>
|
|
|
|
|
#include <crypto/hash.h>
|
|
|
|
|
#include <net/icmp.h>
|
|
|
|
|
#include <net/ip.h>
|
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
|
@ -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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|