|
|
|
|
@ -5,6 +5,9 @@
|
|
|
|
|
#include <linux/udp.h>
|
|
|
|
|
#include <linux/icmp.h>
|
|
|
|
|
#include <linux/version.h>
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
|
#include <linux/crypto.h>
|
|
|
|
|
#include <crypto/aes.h>
|
|
|
|
|
#include <net/icmp.h>
|
|
|
|
|
#include <net/ip.h>
|
|
|
|
|
#include <net/ipv6.h>
|
|
|
|
|
@ -100,6 +103,63 @@ static void table_push(struct mediaproxy_table *);
|
|
|
|
|
static struct mediaproxy_target *get_target(struct mediaproxy_table *, u_int16_t);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct mp_crypto_context {
|
|
|
|
|
spinlock_t lock; /* protects roc and last_index */
|
|
|
|
|
unsigned char session_key[16];
|
|
|
|
|
unsigned char session_salt[14];
|
|
|
|
|
unsigned char session_auth_key[20];
|
|
|
|
|
u_int32_t roc;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct mediaproxy_target {
|
|
|
|
|
atomic_t refcnt;
|
|
|
|
|
u_int32_t table;
|
|
|
|
|
struct mediaproxy_target_info target;
|
|
|
|
|
|
|
|
|
|
spinlock_t stats_lock;
|
|
|
|
|
struct mediaproxy_stats stats;
|
|
|
|
|
|
|
|
|
|
struct mp_crypto_context decrypt;
|
|
|
|
|
struct mp_crypto_context encrypt;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct mediaproxy_table {
|
|
|
|
|
atomic_t refcnt;
|
|
|
|
|
rwlock_t target_lock;
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
|
|
u_int32_t id;
|
|
|
|
|
struct proc_dir_entry *proc;
|
|
|
|
|
struct proc_dir_entry *status;
|
|
|
|
|
struct proc_dir_entry *control;
|
|
|
|
|
struct proc_dir_entry *list;
|
|
|
|
|
struct proc_dir_entry *blist;
|
|
|
|
|
|
|
|
|
|
struct mediaproxy_target **target[256];
|
|
|
|
|
|
|
|
|
|
unsigned int buckets;
|
|
|
|
|
unsigned int targets;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct mp_cipher {
|
|
|
|
|
const char *name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct mp_hmac {
|
|
|
|
|
const char *name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const struct file_operations proc_control_ops = {
|
|
|
|
|
.write = proc_control_write,
|
|
|
|
|
.open = proc_control_open,
|
|
|
|
|
@ -125,7 +185,7 @@ static const struct file_operations proc_blist_ops = {
|
|
|
|
|
.release = proc_blist_close,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct seq_operations proc_list_seq_ops = {
|
|
|
|
|
static const struct seq_operations proc_list_seq_ops = {
|
|
|
|
|
.start = proc_list_start,
|
|
|
|
|
.next = proc_list_next,
|
|
|
|
|
.stop = proc_list_stop,
|
|
|
|
|
@ -139,13 +199,40 @@ static const struct file_operations proc_main_list_ops = {
|
|
|
|
|
.release = seq_release,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct seq_operations proc_main_list_seq_ops = {
|
|
|
|
|
static const struct seq_operations proc_main_list_seq_ops = {
|
|
|
|
|
.start = proc_main_list_start,
|
|
|
|
|
.next = proc_main_list_next,
|
|
|
|
|
.stop = proc_main_list_stop,
|
|
|
|
|
.show = proc_main_list_show,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct mp_cipher mp_ciphers[] = {
|
|
|
|
|
[MPC_INVALID] = {
|
|
|
|
|
.name = NULL,
|
|
|
|
|
},
|
|
|
|
|
[MPC_NULL] = {
|
|
|
|
|
.name = "NULL",
|
|
|
|
|
},
|
|
|
|
|
[MPC_AES_CM] = {
|
|
|
|
|
.name = "AES-CM",
|
|
|
|
|
},
|
|
|
|
|
[MPC_AES_F8] = {
|
|
|
|
|
.name = "AES-F8",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct mp_hmac mp_hmacs[] = {
|
|
|
|
|
[MPH_INVALID] = {
|
|
|
|
|
.name = NULL,
|
|
|
|
|
},
|
|
|
|
|
[MPH_NULL] = {
|
|
|
|
|
.name = "NULL",
|
|
|
|
|
},
|
|
|
|
|
[MPH_HMAC_SHA1] = {
|
|
|
|
|
.name = "HMAC-SHA1",
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -740,6 +827,171 @@ static int is_valid_address(struct mp_address *mpa) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int validate_srtp(struct mediaproxy_srtp *s) {
|
|
|
|
|
if (s->cipher <= MPC_INVALID)
|
|
|
|
|
return -1;
|
|
|
|
|
if (s->cipher >= __MPC_LAST)
|
|
|
|
|
return -1;
|
|
|
|
|
if (s->hmac <= MPH_INVALID)
|
|
|
|
|
return -1;
|
|
|
|
|
if (s->hmac >= __MPH_LAST)
|
|
|
|
|
return -1;
|
|
|
|
|
if (s->auth_tag_len > 20)
|
|
|
|
|
return -1;
|
|
|
|
|
if (s->mki_len > 128)
|
|
|
|
|
return -1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void aes_ctr_128(unsigned char *out, const unsigned char *in, int in_len,
|
|
|
|
|
struct crypto_cipher *tfm, const char *iv)
|
|
|
|
|
{
|
|
|
|
|
unsigned char ivx[16];
|
|
|
|
|
unsigned char key_block[16];
|
|
|
|
|
unsigned char *p, *q;
|
|
|
|
|
unsigned int left;
|
|
|
|
|
int i;
|
|
|
|
|
u_int64_t *pi, *qi, *ki;
|
|
|
|
|
|
|
|
|
|
if (!tfm)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memcpy(ivx, iv, 16);
|
|
|
|
|
pi = (void *) in;
|
|
|
|
|
qi = (void *) out;
|
|
|
|
|
ki = (void *) key_block;
|
|
|
|
|
left = in_len;
|
|
|
|
|
|
|
|
|
|
while (left) {
|
|
|
|
|
crypto_cipher_encrypt_one(tfm, key_block, ivx);
|
|
|
|
|
|
|
|
|
|
if (unlikely(left < 16)) {
|
|
|
|
|
p = (void *) pi;
|
|
|
|
|
q = (void *) qi;
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
|
*q++ = *p++ ^ key_block[i];
|
|
|
|
|
left--;
|
|
|
|
|
if (!left)
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
panic("BUG!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*qi++ = *pi++ ^ ki[0];
|
|
|
|
|
*qi++ = *pi++ ^ ki[1];
|
|
|
|
|
left -= 16;
|
|
|
|
|
|
|
|
|
|
for (i = 15; i >= 0; i--) {
|
|
|
|
|
ivx[i]++;
|
|
|
|
|
if (likely(ivx[i]))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
done:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int aes_ctr_128_no_ctx(unsigned char *out, const char *in, int in_len,
|
|
|
|
|
const unsigned char *key, const unsigned char *iv)
|
|
|
|
|
{
|
|
|
|
|
struct crypto_cipher *tfm;
|
|
|
|
|
|
|
|
|
|
tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
|
|
|
|
if (IS_ERR(tfm))
|
|
|
|
|
return PTR_ERR(tfm);
|
|
|
|
|
|
|
|
|
|
crypto_cipher_setkey(tfm, key, 16);
|
|
|
|
|
aes_ctr_128(out, in, in_len, tfm, iv);
|
|
|
|
|
|
|
|
|
|
crypto_free_cipher(tfm);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int prf_n(unsigned char *out, int len, const unsigned char *key, const unsigned char *x) {
|
|
|
|
|
unsigned char iv[16];
|
|
|
|
|
unsigned char o[32];
|
|
|
|
|
unsigned char in[32];
|
|
|
|
|
int in_len, ret;
|
|
|
|
|
|
|
|
|
|
memcpy(iv, x, 14);
|
|
|
|
|
iv[14] = iv[15] = 0;
|
|
|
|
|
in_len = len > 16 ? 32 : 16;
|
|
|
|
|
memset(in, 0, in_len);
|
|
|
|
|
|
|
|
|
|
ret = aes_ctr_128_no_ctx(o, in, in_len, key, iv);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
memcpy(out, o, len);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int gen_session_key(unsigned char *out, int len, struct mediaproxy_srtp *s, unsigned char label) {
|
|
|
|
|
unsigned char key_id[7];
|
|
|
|
|
unsigned char x[14];
|
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
|
|
memset(key_id, 0, sizeof(key_id));
|
|
|
|
|
|
|
|
|
|
key_id[0] = label;
|
|
|
|
|
|
|
|
|
|
memcpy(x, s->master_salt, 14);
|
|
|
|
|
for (i = 13 - 6; i < 14; i++)
|
|
|
|
|
x[i] = key_id[i - (13 - 6)] ^ x[i];
|
|
|
|
|
|
|
|
|
|
ret = prf_n(out, len, s->master_key, x);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int gen_session_keys(struct mp_crypto_context *c, struct mediaproxy_srtp *s) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (s->cipher == MPC_NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
ret = gen_session_key(c->session_key, 16, s, 0x00);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
ret = gen_session_key(c->session_auth_key, 20, s, 0x01);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
ret = gen_session_key(c->session_salt, 14, s, 0x02);
|
|
|
|
|
if (ret)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
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],
|
|
|
|
|
s->master_key[4], s->master_key[5], s->master_key[6], s->master_key[7],
|
|
|
|
|
s->master_key[8], s->master_key[9], s->master_key[10], s->master_key[11],
|
|
|
|
|
s->master_key[12], s->master_key[13], s->master_key[14], s->master_key[15]);
|
|
|
|
|
DBG("master salt %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
|
|
|
|
s->master_salt[0], s->master_salt[1], s->master_salt[2], s->master_salt[3],
|
|
|
|
|
s->master_salt[4], s->master_salt[5], s->master_salt[6], s->master_salt[7],
|
|
|
|
|
s->master_salt[8], s->master_salt[9], s->master_salt[10], s->master_salt[11],
|
|
|
|
|
s->master_salt[12], s->master_salt[13]);
|
|
|
|
|
DBG("session key %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
|
|
|
|
c->session_key[0], c->session_key[1], c->session_key[2], c->session_key[3],
|
|
|
|
|
c->session_key[4], c->session_key[5], c->session_key[6], c->session_key[7],
|
|
|
|
|
c->session_key[8], c->session_key[9], c->session_key[10], c->session_key[11],
|
|
|
|
|
c->session_key[12], c->session_key[13], c->session_key[14], c->session_key[15]);
|
|
|
|
|
DBG("session salt %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
|
|
|
|
|
c->session_salt[0], c->session_salt[1], c->session_salt[2], c->session_salt[3],
|
|
|
|
|
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]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int table_new_target(struct mediaproxy_table *t, struct mediaproxy_target_info *i, int update) {
|
|
|
|
|
unsigned char hi, lo;
|
|
|
|
|
@ -763,6 +1015,10 @@ static int table_new_target(struct mediaproxy_table *t, struct mediaproxy_target
|
|
|
|
|
if (i->mirror_addr.family != i->src_addr.family)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (validate_srtp(&i->decrypt))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
if (validate_srtp(&i->encrypt))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
DBG("Creating new target\n");
|
|
|
|
|
|
|
|
|
|
@ -774,8 +1030,18 @@ static int table_new_target(struct mediaproxy_table *t, struct mediaproxy_target
|
|
|
|
|
g->table = t->id;
|
|
|
|
|
atomic_set(&g->refcnt, 1);
|
|
|
|
|
spin_lock_init(&g->stats_lock);
|
|
|
|
|
spin_lock_init(&g->decrypt.lock);
|
|
|
|
|
spin_lock_init(&g->encrypt.lock);
|
|
|
|
|
memcpy(&g->target, i, sizeof(*i));
|
|
|
|
|
|
|
|
|
|
err = gen_session_keys(&g->decrypt, &g->target.decrypt);
|
|
|
|
|
if (err)
|
|
|
|
|
goto fail2;
|
|
|
|
|
err = gen_session_keys(&g->encrypt, &g->target.encrypt);
|
|
|
|
|
if (err)
|
|
|
|
|
goto fail2;
|
|
|
|
|
|
|
|
|
|
err = -ENOMEM;
|
|
|
|
|
if (update)
|
|
|
|
|
gp = NULL;
|
|
|
|
|
else {
|
|
|
|
|
@ -982,7 +1248,7 @@ static ssize_t proc_control_write(struct file *file, const char __user *buf, siz
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
if (buflen != sizeof(msg))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
|
|
inode = file->f_path.dentry->d_inode;
|
|
|
|
|
pde = PDE(inode);
|
|
|
|
|
|