this implements in-kernel SRTP authentication

git.mgm/mediaproxy-ng/github/master
Richard Fuchs 13 years ago
parent 1507dd3aca
commit 4ec8f81caf

@ -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;
}

Loading…
Cancel
Save