Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F7159499
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
51 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/deps/pjsip/pjmedia/src/pjmedia/transport_srtp.c b/deps/pjsip/pjmedia/src/pjmedia/transport_srtp.c
index ca588ef1..6458c24c 100644
--- a/deps/pjsip/pjmedia/src/pjmedia/transport_srtp.c
+++ b/deps/pjsip/pjmedia/src/pjmedia/transport_srtp.c
@@ -1,1733 +1,1737 @@
/* $Id: transport_srtp.c 4366 2013-02-21 20:41:31Z ming $ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/transport_srtp.h>
#include <pjmedia/endpoint.h>
#include <pjlib-util/base64.h>
#include <pj/assert.h>
#include <pj/ctype.h>
#include <pj/lock.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
#if defined(PJMEDIA_EXTERNAL_SRTP) && (PJMEDIA_EXTERNAL_SRTP != 0)
# include <srtp/srtp.h>
#else
# include <srtp.h>
#endif
#define THIS_FILE "transport_srtp.c"
/* Maximum size of outgoing packet */
#define MAX_RTP_BUFFER_LEN PJMEDIA_MAX_MTU
#define MAX_RTCP_BUFFER_LEN PJMEDIA_MAX_MTU
/* Maximum SRTP crypto key length */
#define MAX_KEY_LEN 128
/* Initial value of probation counter. When probation counter > 0,
* it means SRTP is in probation state, and it may restart when
* srtp_unprotect() returns err_status_replay_*
*/
#define PROBATION_CNT_INIT 100
#define DEACTIVATE_MEDIA(pool, m) pjmedia_sdp_media_deactivate(pool, m)
static const pj_str_t ID_RTP_AVP = { "RTP/AVP", 7 };
static const pj_str_t ID_RTP_SAVP = { "RTP/SAVP", 8 };
static const pj_str_t ID_INACTIVE = { "inactive", 8 };
static const pj_str_t ID_CRYPTO = { "crypto", 6 };
typedef struct crypto_suite
{
char *name;
cipher_type_id_t cipher_type;
unsigned cipher_key_len;
auth_type_id_t auth_type;
unsigned auth_key_len;
unsigned srtp_auth_tag_len;
unsigned srtcp_auth_tag_len;
sec_serv_t service;
} crypto_suite;
/* Crypto suites as defined on RFC 4568 */
static crypto_suite crypto_suites[] = {
/* plain RTP/RTCP (no cipher & no auth) */
{"NULL", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none},
/* cipher AES_CM, auth HMAC_SHA1, auth tag len = 10 octets */
{"AES_CM_128_HMAC_SHA1_80", AES_128_ICM, 30, HMAC_SHA1, 20, 10, 10,
sec_serv_conf_and_auth},
/* cipher AES_CM, auth HMAC_SHA1, auth tag len = 4 octets */
{"AES_CM_128_HMAC_SHA1_32", AES_128_ICM, 30, HMAC_SHA1, 20, 4, 10,
sec_serv_conf_and_auth},
/*
* F8_128_HMAC_SHA1_8 not supported by libsrtp?
* {"F8_128_HMAC_SHA1_8", NULL_CIPHER, 0, NULL_AUTH, 0, 0, 0, sec_serv_none}
*/
};
typedef struct transport_srtp
{
pjmedia_transport base; /**< Base transport interface. */
pj_pool_t *pool; /**< Pool for transport SRTP. */
pj_lock_t *mutex; /**< Mutex for libsrtp contexts.*/
char rtp_tx_buffer[MAX_RTP_BUFFER_LEN];
char rtcp_tx_buffer[MAX_RTCP_BUFFER_LEN];
pjmedia_srtp_setting setting;
unsigned media_option;
/* SRTP policy */
pj_bool_t session_inited;
pj_bool_t offerer_side;
pj_bool_t bypass_srtp;
char tx_key[MAX_KEY_LEN];
char rx_key[MAX_KEY_LEN];
pjmedia_srtp_crypto tx_policy;
pjmedia_srtp_crypto rx_policy;
/* Temporary policy for negotiation */
pjmedia_srtp_crypto tx_policy_neg;
pjmedia_srtp_crypto rx_policy_neg;
/* libSRTP contexts */
srtp_t srtp_tx_ctx;
srtp_t srtp_rx_ctx;
/* Stream information */
void *user_data;
void (*rtp_cb)( void *user_data,
void *pkt,
pj_ssize_t size);
void (*rtcp_cb)(void *user_data,
void *pkt,
pj_ssize_t size);
/* Transport information */
pjmedia_transport *member_tp; /**< Underlying transport. */
/* SRTP usage policy of peer. This field is updated when media is starting.
* This is useful when SRTP is in optional mode and peer is using mandatory
* mode, so when local is about to reinvite/update, it should offer
* RTP/SAVP instead of offering RTP/AVP.
*/
pjmedia_srtp_use peer_use;
/* When probation counter > 0, it means SRTP is in probation state,
* and it may restart when srtp_unprotect() returns err_status_replay_*
*/
unsigned probation_cnt;
} transport_srtp;
/*
* This callback is called by transport when incoming rtp is received
*/
static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size);
/*
* This callback is called by transport when incoming rtcp is received
*/
static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size);
/*
* These are media transport operations.
*/
static pj_status_t transport_get_info (pjmedia_transport *tp,
pjmedia_transport_info *info);
static pj_status_t transport_attach (pjmedia_transport *tp,
void *user_data,
const pj_sockaddr_t *rem_addr,
const pj_sockaddr_t *rem_rtcp,
unsigned addr_len,
void (*rtp_cb)(void*,
void*,
pj_ssize_t),
void (*rtcp_cb)(void*,
void*,
pj_ssize_t));
static void transport_detach (pjmedia_transport *tp,
void *strm);
static pj_status_t transport_send_rtp( pjmedia_transport *tp,
const void *pkt,
pj_size_t size);
static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
const void *pkt,
pj_size_t size);
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
const pj_sockaddr_t *addr,
unsigned addr_len,
const void *pkt,
pj_size_t size);
static pj_status_t transport_media_create(pjmedia_transport *tp,
pj_pool_t *sdp_pool,
unsigned options,
const pjmedia_sdp_session *sdp_remote,
unsigned media_index);
static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
pj_pool_t *sdp_pool,
pjmedia_sdp_session *sdp_local,
const pjmedia_sdp_session *sdp_remote,
unsigned media_index);
static pj_status_t transport_media_start (pjmedia_transport *tp,
pj_pool_t *pool,
const pjmedia_sdp_session *sdp_local,
const pjmedia_sdp_session *sdp_remote,
unsigned media_index);
static pj_status_t transport_media_stop(pjmedia_transport *tp);
static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
pjmedia_dir dir,
unsigned pct_lost);
static pj_status_t transport_destroy (pjmedia_transport *tp);
static pjmedia_transport_op transport_srtp_op =
{
&transport_get_info,
&transport_attach,
&transport_detach,
&transport_send_rtp,
&transport_send_rtcp,
&transport_send_rtcp2,
&transport_media_create,
&transport_encode_sdp,
&transport_media_start,
&transport_media_stop,
&transport_simulate_lost,
&transport_destroy
};
/* This function may also be used by other module, e.g: pjmedia/errno.c,
* it should have C compatible declaration.
*/
PJ_BEGIN_DECL
const char* get_libsrtp_errstr(int err);
PJ_END_DECL
const char* get_libsrtp_errstr(int err)
{
#if defined(PJ_HAS_ERROR_STRING) && (PJ_HAS_ERROR_STRING != 0)
static char *liberr[] = {
"ok", /* err_status_ok = 0 */
"unspecified failure", /* err_status_fail = 1 */
"unsupported parameter", /* err_status_bad_param = 2 */
"couldn't allocate memory", /* err_status_alloc_fail = 3 */
"couldn't deallocate properly", /* err_status_dealloc_fail = 4 */
"couldn't initialize", /* err_status_init_fail = 5 */
"can't process as much data as requested",
/* err_status_terminus = 6 */
"authentication failure", /* err_status_auth_fail = 7 */
"cipher failure", /* err_status_cipher_fail = 8 */
"replay check failed (bad index)", /* err_status_replay_fail = 9 */
"replay check failed (index too old)",
/* err_status_replay_old = 10 */
"algorithm failed test routine", /* err_status_algo_fail = 11 */
"unsupported operation", /* err_status_no_such_op = 12 */
"no appropriate context found", /* err_status_no_ctx = 13 */
"unable to perform desired validation",
/* err_status_cant_check = 14 */
"can't use key any more", /* err_status_key_expired = 15 */
"error in use of socket", /* err_status_socket_err = 16 */
"error in use POSIX signals", /* err_status_signal_err = 17 */
"nonce check failed", /* err_status_nonce_bad = 18 */
"couldn't read data", /* err_status_read_fail = 19 */
"couldn't write data", /* err_status_write_fail = 20 */
"error pasring data", /* err_status_parse_err = 21 */
"error encoding data", /* err_status_encode_err = 22 */
"error while using semaphores", /* err_status_semaphore_err = 23 */
"error while using pfkey" /* err_status_pfkey_err = 24 */
};
if (err >= 0 && err < (int)PJ_ARRAY_SIZE(liberr)) {
return liberr[err];
} else {
static char msg[32];
pj_ansi_snprintf(msg, sizeof(msg), "Unknown libsrtp error %d", err);
return msg;
}
#else
static char msg[32];
pj_ansi_snprintf(msg, sizeof(msg), "libsrtp error %d", err);
return msg;
#endif
}
static pj_bool_t libsrtp_initialized;
static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt);
PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt)
{
#if PJMEDIA_LIBSRTP_AUTO_INIT_DEINIT
if (libsrtp_initialized == PJ_FALSE) {
err_status_t err;
err = srtp_init();
if (err != err_status_ok) {
PJ_LOG(4, (THIS_FILE, "Failed to initialize libsrtp: %s",
get_libsrtp_errstr(err)));
return PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS)
{
/* There will be memory leak when it fails to schedule libsrtp
* deinitialization, however the memory leak could be harmless,
* since in modern OS's memory used by an application is released
* when the application terminates.
*/
PJ_LOG(4, (THIS_FILE, "Failed to register libsrtp deinit."));
}
libsrtp_initialized = PJ_TRUE;
}
#else
PJ_UNUSED_ARG(endpt);
#endif
return PJ_SUCCESS;
}
static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt)
{
err_status_t err;
/* Note that currently this SRTP init/deinit is not equipped with
* reference counter, it should be safe as normally there is only
* one single instance of media endpoint and even if it isn't, the
* pjmedia_transport_srtp_create() will invoke SRTP init (the only
* drawback should be the delay described by #788).
*/
PJ_UNUSED_ARG(endpt);
#if defined(PJMEDIA_EXTERNAL_SRTP) && (PJMEDIA_EXTERNAL_SRTP != 0)
# if defined(PJMEDIA_SRTP_HAS_DEINIT) && PJMEDIA_SRTP_HAS_DEINIT!=0
err = srtp_deinit();
# elif defined(PJMEDIA_SRTP_HAS_SHUTDOWN) && PJMEDIA_SRTP_HAS_SHUTDOWN!=0
err = srtp_shutdown();
# else
err = err_status_ok;
# endif
#else
err = srtp_deinit();
#endif
if (err != err_status_ok) {
PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s",
get_libsrtp_errstr(err)));
}
libsrtp_initialized = PJ_FALSE;
}
static int get_crypto_idx(const pj_str_t* crypto_name)
{
int i;
int cs_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]);
/* treat unspecified crypto_name as crypto 'NULL' */
if (crypto_name->slen == 0)
return 0;
for (i=0; i<cs_cnt; ++i) {
if (!pj_stricmp2(crypto_name, crypto_suites[i].name))
return i;
}
return -1;
}
static int srtp_crypto_cmp(const pjmedia_srtp_crypto* c1,
const pjmedia_srtp_crypto* c2)
{
int r;
r = pj_strcmp(&c1->key, &c2->key);
if (r != 0)
return r;
r = pj_stricmp(&c1->name, &c2->name);
if (r != 0)
return r;
return (c1->flags != c2->flags);
}
static pj_bool_t srtp_crypto_empty(const pjmedia_srtp_crypto* c)
{
return (c->name.slen==0 || c->key.slen==0);
}
PJ_DEF(void) pjmedia_srtp_setting_default(pjmedia_srtp_setting *opt)
{
unsigned i;
pj_assert(opt);
pj_bzero(opt, sizeof(pjmedia_srtp_setting));
opt->close_member_tp = PJ_TRUE;
opt->use = PJMEDIA_SRTP_OPTIONAL;
/* Copy default crypto-suites, but skip crypto 'NULL' */
opt->crypto_count = sizeof(crypto_suites)/sizeof(crypto_suites[0]) - 1;
for (i=0; i<opt->crypto_count; ++i)
opt->crypto[i].name = pj_str(crypto_suites[i+1].name);
}
/*
* Create an SRTP media transport.
*/
PJ_DEF(pj_status_t) pjmedia_transport_srtp_create(
pjmedia_endpt *endpt,
pjmedia_transport *tp,
const pjmedia_srtp_setting *opt,
pjmedia_transport **p_tp)
{
pj_pool_t *pool;
transport_srtp *srtp;
pj_status_t status;
unsigned i;
PJ_ASSERT_RETURN(endpt && tp && p_tp, PJ_EINVAL);
/* Check crypto availability */
if (opt && opt->crypto_count == 0 &&
opt->use == PJMEDIA_SRTP_MANDATORY)
return PJMEDIA_SRTP_ESDPREQCRYPTO;
/* Check crypto */
if (opt && opt->use != PJMEDIA_SRTP_DISABLED) {
for (i=0; i < opt->crypto_count; ++i) {
int cs_idx = get_crypto_idx(&opt->crypto[i].name);
/* check crypto name */
if (cs_idx == -1)
return PJMEDIA_SRTP_ENOTSUPCRYPTO;
/* check key length */
if (opt->crypto[i].key.slen &&
opt->crypto[i].key.slen <
(pj_ssize_t)crypto_suites[cs_idx].cipher_key_len)
return PJMEDIA_SRTP_EINKEYLEN;
}
}
/* Init libsrtp. */
status = pjmedia_srtp_init_lib(endpt);
if (status != PJ_SUCCESS)
return status;
pool = pjmedia_endpt_create_pool(endpt, "srtp%p", 1000, 1000);
srtp = PJ_POOL_ZALLOC_T(pool, transport_srtp);
srtp->pool = pool;
srtp->session_inited = PJ_FALSE;
srtp->bypass_srtp = PJ_FALSE;
srtp->probation_cnt = PROBATION_CNT_INIT;
if (opt) {
srtp->setting = *opt;
if (opt->use == PJMEDIA_SRTP_DISABLED)
srtp->setting.crypto_count = 0;
for (i=0; i < srtp->setting.crypto_count; ++i) {
int cs_idx = get_crypto_idx(&opt->crypto[i].name);
pj_str_t tmp_key = opt->crypto[i].key;
/* re-set crypto */
srtp->setting.crypto[i].name = pj_str(crypto_suites[cs_idx].name);
/* cut key length */
if (tmp_key.slen)
tmp_key.slen = crypto_suites[cs_idx].cipher_key_len;
pj_strdup(pool, &srtp->setting.crypto[i].key, &tmp_key);
}
} else {
pjmedia_srtp_setting_default(&srtp->setting);
}
status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &srtp->mutex);
if (status != PJ_SUCCESS) {
pj_pool_release(pool);
return status;
}
/* Initialize base pjmedia_transport */
pj_memcpy(srtp->base.name, pool->obj_name, PJ_MAX_OBJ_NAME);
if (tp)
srtp->base.type = tp->type;
else
srtp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP;
srtp->base.op = &transport_srtp_op;
/* Set underlying transport */
srtp->member_tp = tp;
/* Initialize peer's SRTP usage mode. */
srtp->peer_use = srtp->setting.use;
/* Done */
*p_tp = &srtp->base;
return PJ_SUCCESS;
}
/*
* Initialize and start SRTP session with the given parameters.
*/
PJ_DEF(pj_status_t) pjmedia_transport_srtp_start(
pjmedia_transport *tp,
const pjmedia_srtp_crypto *tx,
const pjmedia_srtp_crypto *rx)
{
transport_srtp *srtp = (transport_srtp*) tp;
srtp_policy_t tx_;
srtp_policy_t rx_;
err_status_t err;
int cr_tx_idx = 0;
int au_tx_idx = 0;
int cr_rx_idx = 0;
int au_rx_idx = 0;
pj_status_t status = PJ_SUCCESS;
PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL);
pj_lock_acquire(srtp->mutex);
if (srtp->session_inited) {
pjmedia_transport_srtp_stop(tp);
}
/* Get encryption and authentication method */
cr_tx_idx = au_tx_idx = get_crypto_idx(&tx->name);
if (tx->flags & PJMEDIA_SRTP_NO_ENCRYPTION)
cr_tx_idx = 0;
if (tx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION)
au_tx_idx = 0;
cr_rx_idx = au_rx_idx = get_crypto_idx(&rx->name);
if (rx->flags & PJMEDIA_SRTP_NO_ENCRYPTION)
cr_rx_idx = 0;
if (rx->flags & PJMEDIA_SRTP_NO_AUTHENTICATION)
au_rx_idx = 0;
/* Check whether the crypto-suite requested is supported */
if (cr_tx_idx == -1 || cr_rx_idx == -1 || au_tx_idx == -1 ||
au_rx_idx == -1)
{
status = PJMEDIA_SRTP_ENOTSUPCRYPTO;
goto on_return;
}
/* If all options points to 'NULL' method, just bypass SRTP */
if (cr_tx_idx == 0 && cr_rx_idx == 0 && au_tx_idx == 0 && au_rx_idx == 0) {
srtp->bypass_srtp = PJ_TRUE;
goto on_return;
}
/* Check key length */
if (tx->key.slen != (pj_ssize_t)crypto_suites[cr_tx_idx].cipher_key_len ||
rx->key.slen != (pj_ssize_t)crypto_suites[cr_rx_idx].cipher_key_len)
{
status = PJMEDIA_SRTP_EINKEYLEN;
goto on_return;
}
/* Init transmit direction */
pj_bzero(&tx_, sizeof(srtp_policy_t));
pj_memmove(srtp->tx_key, tx->key.ptr, tx->key.slen);
if (cr_tx_idx && au_tx_idx)
tx_.rtp.sec_serv = sec_serv_conf_and_auth;
else if (cr_tx_idx)
tx_.rtp.sec_serv = sec_serv_conf;
else if (au_tx_idx)
tx_.rtp.sec_serv = sec_serv_auth;
else
tx_.rtp.sec_serv = sec_serv_none;
tx_.key = (uint8_t*)srtp->tx_key;
tx_.ssrc.type = ssrc_any_outbound;
tx_.ssrc.value = 0;
tx_.rtp.cipher_type = crypto_suites[cr_tx_idx].cipher_type;
tx_.rtp.cipher_key_len = crypto_suites[cr_tx_idx].cipher_key_len;
tx_.rtp.auth_type = crypto_suites[au_tx_idx].auth_type;
tx_.rtp.auth_key_len = crypto_suites[au_tx_idx].auth_key_len;
tx_.rtp.auth_tag_len = crypto_suites[au_tx_idx].srtp_auth_tag_len;
tx_.rtcp = tx_.rtp;
tx_.rtcp.auth_tag_len = crypto_suites[au_tx_idx].srtcp_auth_tag_len;
tx_.next = NULL;
err = srtp_create(&srtp->srtp_tx_ctx, &tx_);
if (err != err_status_ok) {
status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
goto on_return;
}
srtp->tx_policy = *tx;
pj_strset(&srtp->tx_policy.key, srtp->tx_key, tx->key.slen);
srtp->tx_policy.name=pj_str(crypto_suites[get_crypto_idx(&tx->name)].name);
/* Init receive direction */
pj_bzero(&rx_, sizeof(srtp_policy_t));
pj_memmove(srtp->rx_key, rx->key.ptr, rx->key.slen);
if (cr_rx_idx && au_rx_idx)
rx_.rtp.sec_serv = sec_serv_conf_and_auth;
else if (cr_rx_idx)
rx_.rtp.sec_serv = sec_serv_conf;
else if (au_rx_idx)
rx_.rtp.sec_serv = sec_serv_auth;
else
rx_.rtp.sec_serv = sec_serv_none;
rx_.key = (uint8_t*)srtp->rx_key;
rx_.ssrc.type = ssrc_any_inbound;
rx_.ssrc.value = 0;
rx_.rtp.sec_serv = crypto_suites[cr_rx_idx].service;
rx_.rtp.cipher_type = crypto_suites[cr_rx_idx].cipher_type;
rx_.rtp.cipher_key_len = crypto_suites[cr_rx_idx].cipher_key_len;
rx_.rtp.auth_type = crypto_suites[au_rx_idx].auth_type;
rx_.rtp.auth_key_len = crypto_suites[au_rx_idx].auth_key_len;
rx_.rtp.auth_tag_len = crypto_suites[au_rx_idx].srtp_auth_tag_len;
rx_.rtcp = rx_.rtp;
rx_.rtcp.auth_tag_len = crypto_suites[au_rx_idx].srtcp_auth_tag_len;
rx_.next = NULL;
err = srtp_create(&srtp->srtp_rx_ctx, &rx_);
if (err != err_status_ok) {
srtp_dealloc(srtp->srtp_tx_ctx);
status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
goto on_return;
}
srtp->rx_policy = *rx;
pj_strset(&srtp->rx_policy.key, srtp->rx_key, rx->key.slen);
srtp->rx_policy.name=pj_str(crypto_suites[get_crypto_idx(&rx->name)].name);
/* Declare SRTP session initialized */
srtp->session_inited = PJ_TRUE;
/* Logging stuffs */
#if PJ_LOG_MAX_LEVEL >= 5
{
char b64[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)];
int b64_len;
/* TX crypto and key */
b64_len = sizeof(b64);
status = pj_base64_encode((pj_uint8_t*)tx->key.ptr, tx->key.slen,
b64, &b64_len);
if (status != PJ_SUCCESS)
b64_len = pj_ansi_sprintf(b64, "--key too long--");
else
b64[b64_len] = '\0';
PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s",
srtp->tx_policy.name.ptr, b64));
if (srtp->tx_policy.flags) {
PJ_LOG(5,(srtp->pool->obj_name, "TX: disable%s%s",
(cr_tx_idx?"":" enc"),
(au_tx_idx?"":" auth")));
}
/* RX crypto and key */
b64_len = sizeof(b64);
status = pj_base64_encode((pj_uint8_t*)rx->key.ptr, rx->key.slen,
b64, &b64_len);
if (status != PJ_SUCCESS)
b64_len = pj_ansi_sprintf(b64, "--key too long--");
else
b64[b64_len] = '\0';
PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s",
srtp->rx_policy.name.ptr, b64));
if (srtp->rx_policy.flags) {
PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s",
(cr_rx_idx?"":" enc"),
(au_rx_idx?"":" auth")));
}
}
#endif
on_return:
pj_lock_release(srtp->mutex);
return status;
}
/*
* Stop SRTP session.
*/
PJ_DEF(pj_status_t) pjmedia_transport_srtp_stop(pjmedia_transport *srtp)
{
transport_srtp *p_srtp = (transport_srtp*) srtp;
err_status_t err;
PJ_ASSERT_RETURN(srtp, PJ_EINVAL);
pj_lock_acquire(p_srtp->mutex);
if (!p_srtp->session_inited) {
pj_lock_release(p_srtp->mutex);
return PJ_SUCCESS;
}
err = srtp_dealloc(p_srtp->srtp_rx_ctx);
if (err != err_status_ok) {
PJ_LOG(4, (p_srtp->pool->obj_name,
"Failed to dealloc RX SRTP context: %s",
get_libsrtp_errstr(err)));
}
err = srtp_dealloc(p_srtp->srtp_tx_ctx);
if (err != err_status_ok) {
PJ_LOG(4, (p_srtp->pool->obj_name,
"Failed to dealloc TX SRTP context: %s",
get_libsrtp_errstr(err)));
}
p_srtp->session_inited = PJ_FALSE;
pj_bzero(&p_srtp->rx_policy, sizeof(p_srtp->rx_policy));
pj_bzero(&p_srtp->tx_policy, sizeof(p_srtp->tx_policy));
pj_lock_release(p_srtp->mutex);
return PJ_SUCCESS;
}
PJ_DEF(pjmedia_transport *) pjmedia_transport_srtp_get_member(
pjmedia_transport *tp)
{
transport_srtp *srtp = (transport_srtp*) tp;
PJ_ASSERT_RETURN(tp, NULL);
return srtp->member_tp;
}
static pj_status_t transport_get_info(pjmedia_transport *tp,
pjmedia_transport_info *info)
{
transport_srtp *srtp = (transport_srtp*) tp;
pjmedia_srtp_info srtp_info;
int spc_info_idx;
PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
PJ_ASSERT_RETURN(info->specific_info_cnt <
PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXCNT, PJ_ETOOMANY);
PJ_ASSERT_RETURN(sizeof(pjmedia_srtp_info) <=
PJMEDIA_TRANSPORT_SPECIFIC_INFO_MAXSIZE, PJ_ENOMEM);
srtp_info.active = srtp->session_inited;
srtp_info.rx_policy = srtp->rx_policy;
srtp_info.tx_policy = srtp->tx_policy;
srtp_info.use = srtp->setting.use;
srtp_info.peer_use = srtp->peer_use;
spc_info_idx = info->specific_info_cnt++;
info->spc_info[spc_info_idx].type = PJMEDIA_TRANSPORT_TYPE_SRTP;
info->spc_info[spc_info_idx].cbsize = sizeof(srtp_info);
pj_memcpy(&info->spc_info[spc_info_idx].buffer, &srtp_info,
sizeof(srtp_info));
return pjmedia_transport_get_info(srtp->member_tp, info);
}
static pj_status_t transport_attach(pjmedia_transport *tp,
void *user_data,
const pj_sockaddr_t *rem_addr,
const pj_sockaddr_t *rem_rtcp,
unsigned addr_len,
void (*rtp_cb) (void*, void*,
pj_ssize_t),
void (*rtcp_cb)(void*, void*,
pj_ssize_t))
{
transport_srtp *srtp = (transport_srtp*) tp;
pj_status_t status;
PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
/* Save the callbacks */
pj_lock_acquire(srtp->mutex);
srtp->rtp_cb = rtp_cb;
srtp->rtcp_cb = rtcp_cb;
srtp->user_data = user_data;
pj_lock_release(srtp->mutex);
/* Attach itself to transport */
status = pjmedia_transport_attach(srtp->member_tp, srtp, rem_addr,
rem_rtcp, addr_len, &srtp_rtp_cb,
&srtp_rtcp_cb);
if (status != PJ_SUCCESS) {
pj_lock_acquire(srtp->mutex);
srtp->rtp_cb = NULL;
srtp->rtcp_cb = NULL;
srtp->user_data = NULL;
pj_lock_release(srtp->mutex);
return status;
}
return PJ_SUCCESS;
}
static void transport_detach(pjmedia_transport *tp, void *strm)
{
transport_srtp *srtp = (transport_srtp*) tp;
PJ_UNUSED_ARG(strm);
PJ_ASSERT_ON_FAIL(tp, return);
if (srtp->member_tp) {
pjmedia_transport_detach(srtp->member_tp, srtp);
}
/* Clear up application infos from transport */
pj_lock_acquire(srtp->mutex);
srtp->rtp_cb = NULL;
srtp->rtcp_cb = NULL;
srtp->user_data = NULL;
pj_lock_release(srtp->mutex);
}
static pj_status_t transport_send_rtp( pjmedia_transport *tp,
const void *pkt,
pj_size_t size)
{
pj_status_t status;
transport_srtp *srtp = (transport_srtp*) tp;
int len = (int)size;
err_status_t err;
if (srtp->bypass_srtp)
return pjmedia_transport_send_rtp(srtp->member_tp, pkt, size);
if (size > sizeof(srtp->rtp_tx_buffer) - 10)
return PJ_ETOOBIG;
pj_memcpy(srtp->rtp_tx_buffer, pkt, size);
pj_lock_acquire(srtp->mutex);
if (!srtp->session_inited) {
pj_lock_release(srtp->mutex);
return PJ_EINVALIDOP;
}
err = srtp_protect(srtp->srtp_tx_ctx, srtp->rtp_tx_buffer, &len);
pj_lock_release(srtp->mutex);
if (err == err_status_ok) {
status = pjmedia_transport_send_rtp(srtp->member_tp,
srtp->rtp_tx_buffer, len);
} else {
status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
return status;
}
static pj_status_t transport_send_rtcp(pjmedia_transport *tp,
const void *pkt,
pj_size_t size)
{
return transport_send_rtcp2(tp, NULL, 0, pkt, size);
}
static pj_status_t transport_send_rtcp2(pjmedia_transport *tp,
const pj_sockaddr_t *addr,
unsigned addr_len,
const void *pkt,
pj_size_t size)
{
pj_status_t status;
transport_srtp *srtp = (transport_srtp*) tp;
int len = (int)size;
err_status_t err;
if (srtp->bypass_srtp) {
return pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len,
pkt, size);
}
if (size > sizeof(srtp->rtcp_tx_buffer) - 10)
return PJ_ETOOBIG;
pj_memcpy(srtp->rtcp_tx_buffer, pkt, size);
pj_lock_acquire(srtp->mutex);
if (!srtp->session_inited) {
pj_lock_release(srtp->mutex);
return PJ_EINVALIDOP;
}
err = srtp_protect_rtcp(srtp->srtp_tx_ctx, srtp->rtcp_tx_buffer, &len);
pj_lock_release(srtp->mutex);
if (err == err_status_ok) {
status = pjmedia_transport_send_rtcp2(srtp->member_tp, addr, addr_len,
srtp->rtcp_tx_buffer, len);
} else {
status = PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
return status;
}
static pj_status_t transport_simulate_lost(pjmedia_transport *tp,
pjmedia_dir dir,
unsigned pct_lost)
{
transport_srtp *srtp = (transport_srtp *) tp;
PJ_ASSERT_RETURN(tp, PJ_EINVAL);
return pjmedia_transport_simulate_lost(srtp->member_tp, dir, pct_lost);
}
static pj_status_t transport_destroy (pjmedia_transport *tp)
{
transport_srtp *srtp = (transport_srtp *) tp;
pj_status_t status;
PJ_ASSERT_RETURN(tp, PJ_EINVAL);
if (srtp->setting.close_member_tp && srtp->member_tp) {
pjmedia_transport_close(srtp->member_tp);
}
status = pjmedia_transport_srtp_stop(tp);
/* In case mutex is being acquired by other thread */
pj_lock_acquire(srtp->mutex);
pj_lock_release(srtp->mutex);
pj_lock_destroy(srtp->mutex);
pj_pool_release(srtp->pool);
return status;
}
/*
* This callback is called by transport when incoming rtp is received
*/
static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
{
transport_srtp *srtp = (transport_srtp *) user_data;
int len = size;
err_status_t err;
void (*cb)(void*, void*, pj_ssize_t) = NULL;
void *cb_data = NULL;
if (srtp->bypass_srtp) {
srtp->rtp_cb(srtp->user_data, pkt, size);
return;
}
if (size < 0) {
return;
}
/* Make sure buffer is 32bit aligned */
PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return );
if (srtp->probation_cnt > 0)
--srtp->probation_cnt;
pj_lock_acquire(srtp->mutex);
if (!srtp->session_inited) {
pj_lock_release(srtp->mutex);
return;
}
err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
if (srtp->probation_cnt > 0 &&
(err == err_status_replay_old || err == err_status_replay_fail))
{
/* Handle such condition that stream is updated (RTP seq is reinited
* & SRTP is restarted), but some old packets are still coming
* so SRTP is learning wrong RTP seq. While the newly inited RTP seq
* comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect()
* will return err_status_replay_*. Restarting SRTP can resolve this.
*/
pjmedia_srtp_crypto tx, rx;
pj_status_t status;
tx = srtp->tx_policy;
rx = srtp->rx_policy;
status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp,
&tx, &rx);
if (status != PJ_SUCCESS) {
PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s",
get_libsrtp_errstr(err)));
} else if (!srtp->bypass_srtp) {
err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
}
}
if (err != err_status_ok) {
PJ_LOG(5,(srtp->pool->obj_name,
"Failed to unprotect SRTP, pkt size=%d, err=%s",
size, get_libsrtp_errstr(err)));
} else {
cb = srtp->rtp_cb;
cb_data = srtp->user_data;
}
pj_lock_release(srtp->mutex);
if (cb) {
(*cb)(cb_data, pkt, len);
}
}
/*
* This callback is called by transport when incoming rtcp is received
*/
static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size)
{
transport_srtp *srtp = (transport_srtp *) user_data;
int len = size;
err_status_t err;
void (*cb)(void*, void*, pj_ssize_t) = NULL;
void *cb_data = NULL;
if (srtp->bypass_srtp) {
srtp->rtcp_cb(srtp->user_data, pkt, size);
return;
}
if (size < 0) {
return;
}
/* Make sure buffer is 32bit aligned */
PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return );
pj_lock_acquire(srtp->mutex);
if (!srtp->session_inited) {
pj_lock_release(srtp->mutex);
return;
}
err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
if (err != err_status_ok) {
PJ_LOG(5,(srtp->pool->obj_name,
"Failed to unprotect SRTCP, pkt size=%d, err=%s",
size, get_libsrtp_errstr(err)));
} else {
cb = srtp->rtcp_cb;
cb_data = srtp->user_data;
}
pj_lock_release(srtp->mutex);
if (cb) {
(*cb)(cb_data, pkt, len);
}
}
/* Generate crypto attribute, including crypto key.
* If crypto-suite chosen is crypto NULL, just return PJ_SUCCESS,
* and set buffer_len = 0.
*/
static pj_status_t generate_crypto_attr_value(pj_pool_t *pool,
char *buffer, int *buffer_len,
pjmedia_srtp_crypto *crypto,
int tag)
{
pj_status_t status;
int cs_idx = get_crypto_idx(&crypto->name);
char b64_key[PJ_BASE256_TO_BASE64_LEN(MAX_KEY_LEN)+1];
int b64_key_len = sizeof(b64_key);
int print_len;
if (cs_idx == -1)
return PJMEDIA_SRTP_ENOTSUPCRYPTO;
/* Crypto-suite NULL. */
if (cs_idx == 0) {
*buffer_len = 0;
return PJ_SUCCESS;
}
/* Generate key if not specified. */
if (crypto->key.slen == 0) {
pj_bool_t key_ok;
char key[MAX_KEY_LEN];
err_status_t err;
unsigned i;
PJ_ASSERT_RETURN(MAX_KEY_LEN >= crypto_suites[cs_idx].cipher_key_len,
PJ_ETOOSMALL);
do {
key_ok = PJ_TRUE;
err = crypto_get_random((unsigned char*)key,
crypto_suites[cs_idx].cipher_key_len);
if (err != err_status_ok) {
PJ_LOG(5,(THIS_FILE, "Failed generating random key: %s",
get_libsrtp_errstr(err)));
return PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
for (i=0; i<crypto_suites[cs_idx].cipher_key_len && key_ok; ++i)
if (key[i] == 0) key_ok = PJ_FALSE;
} while (!key_ok);
crypto->key.ptr = (char*)
pj_pool_zalloc(pool,
crypto_suites[cs_idx].cipher_key_len);
pj_memcpy(crypto->key.ptr, key, crypto_suites[cs_idx].cipher_key_len);
crypto->key.slen = crypto_suites[cs_idx].cipher_key_len;
}
if (crypto->key.slen != (pj_ssize_t)crypto_suites[cs_idx].cipher_key_len)
return PJMEDIA_SRTP_EINKEYLEN;
/* Key transmitted via SDP should be base64 encoded. */
status = pj_base64_encode((pj_uint8_t*)crypto->key.ptr, crypto->key.slen,
b64_key, &b64_key_len);
if (status != PJ_SUCCESS) {
PJ_LOG(5,(THIS_FILE, "Failed encoding plain key to base64"));
return status;
}
b64_key[b64_key_len] = '\0';
PJ_ASSERT_RETURN(*buffer_len >= (crypto->name.slen + \
b64_key_len + 16), PJ_ETOOSMALL);
/* Print the crypto attribute value. */
print_len = pj_ansi_snprintf(buffer, *buffer_len, "%d %s inline:%s",
tag,
crypto_suites[cs_idx].name,
b64_key);
if (print_len < 1 || print_len >= *buffer_len)
return PJ_ETOOSMALL;
*buffer_len = print_len;
return PJ_SUCCESS;
}
/* Parse crypto attribute line */
static pj_status_t parse_attr_crypto(pj_pool_t *pool,
const pjmedia_sdp_attr *attr,
pjmedia_srtp_crypto *crypto,
int *tag)
{
pj_str_t input;
char *token;
pj_size_t token_len;
pj_str_t tmp;
pj_status_t status;
int itmp;
pj_bzero(crypto, sizeof(*crypto));
pj_strdup_with_null(pool, &input, &attr->value);
/* Tag */
token = strtok(input.ptr, " ");
if (!token) {
PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting tag"));
return PJMEDIA_SDP_EINATTR;
}
token_len = pj_ansi_strlen(token);
/* Tag must not use leading zeroes. */
if (token_len > 1 && *token == '0')
return PJMEDIA_SDP_EINATTR;
/* Tag must be decimal, i.e: contains only digit '0'-'9'. */
for (itmp = 0; itmp < token_len; ++itmp)
if (!pj_isdigit(token[itmp]))
return PJMEDIA_SDP_EINATTR;
/* Get tag value. */
*tag = atoi(token);
/* Crypto-suite */
token = strtok(NULL, " ");
if (!token) {
PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting crypto suite"));
return PJMEDIA_SDP_EINATTR;
}
crypto->name = pj_str(token);
/* Key method */
token = strtok(NULL, ":");
if (!token) {
PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key method"));
return PJMEDIA_SDP_EINATTR;
}
if (pj_ansi_stricmp(token, "inline")) {
PJ_LOG(4,(THIS_FILE, "Attribute crypto key method '%s' not supported!",
token));
return PJMEDIA_SDP_EINATTR;
}
/* Key */
token = strtok(NULL, "| ");
if (!token) {
PJ_LOG(4,(THIS_FILE, "Attribute crypto expecting key"));
return PJMEDIA_SDP_EINATTR;
}
tmp = pj_str(token);
if (PJ_BASE64_TO_BASE256_LEN(tmp.slen) > MAX_KEY_LEN) {
PJ_LOG(4,(THIS_FILE, "Key too long"));
return PJMEDIA_SRTP_EINKEYLEN;
}
/* Decode key */
crypto->key.ptr = (char*) pj_pool_zalloc(pool, MAX_KEY_LEN);
itmp = MAX_KEY_LEN;
status = pj_base64_decode(&tmp, (pj_uint8_t*)crypto->key.ptr,
&itmp);
if (status != PJ_SUCCESS) {
PJ_LOG(4,(THIS_FILE, "Failed decoding crypto key from base64"));
return status;
}
crypto->key.slen = itmp;
return PJ_SUCCESS;
}
static pj_status_t transport_media_create(pjmedia_transport *tp,
pj_pool_t *sdp_pool,
unsigned options,
const pjmedia_sdp_session *sdp_remote,
unsigned media_index)
{
struct transport_srtp *srtp = (struct transport_srtp*) tp;
unsigned member_tp_option;
PJ_ASSERT_RETURN(tp, PJ_EINVAL);
pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg));
pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg));
srtp->media_option = options;
member_tp_option = options | PJMEDIA_TPMED_NO_TRANSPORT_CHECKING;
srtp->offerer_side = sdp_remote == NULL;
/* Validations */
if (srtp->offerer_side) {
if (srtp->setting.use == PJMEDIA_SRTP_DISABLED)
goto BYPASS_SRTP;
} else {
pjmedia_sdp_media *m_rem;
m_rem = sdp_remote->media[media_index];
/* Nothing to do on inactive media stream */
if (pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL))
goto BYPASS_SRTP;
/* Validate remote media transport based on SRTP usage option.
*/
switch (srtp->setting.use) {
case PJMEDIA_SRTP_DISABLED:
if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
return PJMEDIA_SRTP_ESDPINTRANSPORT;
goto BYPASS_SRTP;
case PJMEDIA_SRTP_OPTIONAL:
break;
case PJMEDIA_SRTP_MANDATORY:
if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0)
return PJMEDIA_SRTP_ESDPINTRANSPORT;
break;
}
}
goto PROPAGATE_MEDIA_CREATE;
BYPASS_SRTP:
srtp->bypass_srtp = PJ_TRUE;
member_tp_option &= ~PJMEDIA_TPMED_NO_TRANSPORT_CHECKING;
PROPAGATE_MEDIA_CREATE:
return pjmedia_transport_media_create(srtp->member_tp, sdp_pool,
member_tp_option, sdp_remote,
media_index);
}
static pj_status_t transport_encode_sdp(pjmedia_transport *tp,
pj_pool_t *sdp_pool,
pjmedia_sdp_session *sdp_local,
const pjmedia_sdp_session *sdp_remote,
unsigned media_index)
{
struct transport_srtp *srtp = (struct transport_srtp*) tp;
pjmedia_sdp_media *m_rem, *m_loc;
enum { MAXLEN = 512 };
char buffer[MAXLEN];
int buffer_len;
pj_status_t status;
pjmedia_sdp_attr *attr;
pj_str_t attr_value;
unsigned i, j;
PJ_ASSERT_RETURN(tp && sdp_pool && sdp_local, PJ_EINVAL);
pj_bzero(&srtp->rx_policy_neg, sizeof(srtp->rx_policy_neg));
pj_bzero(&srtp->tx_policy_neg, sizeof(srtp->tx_policy_neg));
srtp->offerer_side = sdp_remote == NULL;
m_rem = sdp_remote ? sdp_remote->media[media_index] : NULL;
m_loc = sdp_local->media[media_index];
/* Bypass if media transport is not RTP/AVP or RTP/SAVP */
if (pj_stricmp(&m_loc->desc.transport, &ID_RTP_AVP) != 0 &&
pj_stricmp(&m_loc->desc.transport, &ID_RTP_SAVP) != 0)
goto BYPASS_SRTP;
+ /* Do nothing if we are in bypass */
+ if (srtp->bypass_srtp)
+ goto BYPASS_SRTP;
+
/* If the media is inactive, do nothing. */
/* No, we still need to process SRTP offer/answer even if the media is
* marked as inactive, because the transport is still alive in this
* case (e.g. for keep-alive). See:
* http://trac.pjsip.org/repos/ticket/1079
*/
/*
if (pjmedia_sdp_media_find_attr(m_loc, &ID_INACTIVE, NULL) ||
(m_rem && pjmedia_sdp_media_find_attr(m_rem, &ID_INACTIVE, NULL)))
goto BYPASS_SRTP;
*/
/* Check remote media transport & set local media transport
* based on SRTP usage option.
*/
if (srtp->offerer_side) {
/* Generate transport */
switch (srtp->setting.use) {
case PJMEDIA_SRTP_DISABLED:
goto BYPASS_SRTP;
case PJMEDIA_SRTP_OPTIONAL:
m_loc->desc.transport =
(srtp->peer_use == PJMEDIA_SRTP_MANDATORY)?
ID_RTP_SAVP : ID_RTP_AVP;
break;
case PJMEDIA_SRTP_MANDATORY:
m_loc->desc.transport = ID_RTP_SAVP;
break;
}
/* Generate crypto attribute if not yet */
if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) {
for (i=0; i<srtp->setting.crypto_count; ++i) {
/* Offer crypto-suites based on setting. */
buffer_len = MAXLEN;
status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len,
&srtp->setting.crypto[i],
i+1);
if (status != PJ_SUCCESS)
return status;
/* If buffer_len==0, just skip the crypto attribute. */
if (buffer_len) {
pj_strset(&attr_value, buffer, buffer_len);
attr = pjmedia_sdp_attr_create(srtp->pool, ID_CRYPTO.ptr,
&attr_value);
m_loc->attr[m_loc->attr_count++] = attr;
}
}
}
} else {
/* Answerer side */
pj_assert(sdp_remote && m_rem);
/* Generate transport */
switch (srtp->setting.use) {
case PJMEDIA_SRTP_DISABLED:
if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
return PJMEDIA_SRTP_ESDPINTRANSPORT;
goto BYPASS_SRTP;
case PJMEDIA_SRTP_OPTIONAL:
m_loc->desc.transport = m_rem->desc.transport;
break;
case PJMEDIA_SRTP_MANDATORY:
if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) != 0)
return PJMEDIA_SRTP_ESDPINTRANSPORT;
m_loc->desc.transport = ID_RTP_SAVP;
break;
}
/* Generate crypto attribute if not yet */
if (pjmedia_sdp_media_find_attr(m_loc, &ID_CRYPTO, NULL) == NULL) {
pjmedia_srtp_crypto tmp_rx_crypto;
pj_bool_t has_crypto_attr = PJ_FALSE;
int matched_idx = -1;
int chosen_tag = 0;
int tags[64]; /* assume no more than 64 crypto attrs in a media */
unsigned cr_attr_count = 0;
/* Find supported crypto-suite, get the tag, and assign policy_local */
for (i=0; i<m_rem->attr_count; ++i) {
if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0)
continue;
has_crypto_attr = PJ_TRUE;
status = parse_attr_crypto(srtp->pool, m_rem->attr[i],
&tmp_rx_crypto, &tags[cr_attr_count]);
if (status != PJ_SUCCESS)
return status;
/* Check duplicated tag */
for (j=0; j<cr_attr_count; ++j) {
if (tags[j] == tags[cr_attr_count]) {
DEACTIVATE_MEDIA(sdp_pool, m_loc);
return PJMEDIA_SRTP_ESDPDUPCRYPTOTAG;
}
}
if (matched_idx == -1) {
/* lets see if the crypto-suite offered is supported */
for (j=0; j<srtp->setting.crypto_count; ++j)
if (pj_stricmp(&tmp_rx_crypto.name,
&srtp->setting.crypto[j].name) == 0)
{
int cs_idx = get_crypto_idx(&tmp_rx_crypto.name);
/* Force to use test key */
/* bad keys for snom: */
//char *hex_test_key = "58b29c5c8f42308120ce857e439f2d"
// "7810a8b10ad0b1446be5470faea496";
//char *hex_test_key = "20a26aac7ba062d356ff52b61e3993"
// "ccb78078f12c64db94b9c294927fd0";
//pj_str_t *test_key = &srtp->setting.crypto[j].key;
//char *raw_test_key = pj_pool_zalloc(srtp->pool, 64);
//hex_string_to_octet_string(
// raw_test_key,
// hex_test_key,
// strlen(hex_test_key));
//pj_strset(test_key, raw_test_key,
// crypto_suites[cs_idx].cipher_key_len);
/* EO Force to use test key */
if (tmp_rx_crypto.key.slen !=
(int)crypto_suites[cs_idx].cipher_key_len)
return PJMEDIA_SRTP_EINKEYLEN;
srtp->rx_policy_neg = tmp_rx_crypto;
chosen_tag = tags[cr_attr_count];
matched_idx = j;
break;
}
}
cr_attr_count++;
}
/* Check crypto negotiation result */
switch (srtp->setting.use) {
case PJMEDIA_SRTP_DISABLED:
pj_assert(!"Should never reach here");
break;
case PJMEDIA_SRTP_OPTIONAL:
/* bypass SRTP when no crypto-attr and remote uses RTP/AVP */
if (!has_crypto_attr &&
pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0)
goto BYPASS_SRTP;
/* bypass SRTP when nothing match and remote uses RTP/AVP */
else if (matched_idx == -1 &&
pj_stricmp(&m_rem->desc.transport, &ID_RTP_AVP) == 0)
goto BYPASS_SRTP;
break;
case PJMEDIA_SRTP_MANDATORY:
/* Do nothing, intentional */
break;
}
/* No crypto attr */
if (!has_crypto_attr) {
DEACTIVATE_MEDIA(sdp_pool, m_loc);
return PJMEDIA_SRTP_ESDPREQCRYPTO;
}
/* No crypto match */
if (matched_idx == -1) {
DEACTIVATE_MEDIA(sdp_pool, m_loc);
return PJMEDIA_SRTP_ENOTSUPCRYPTO;
}
/* we have to generate crypto answer,
* with srtp->tx_policy_neg matched the offer
* and rem_tag contains matched offer tag.
*/
buffer_len = MAXLEN;
status = generate_crypto_attr_value(srtp->pool, buffer, &buffer_len,
&srtp->setting.crypto[matched_idx],
chosen_tag);
if (status != PJ_SUCCESS)
return status;
srtp->tx_policy_neg = srtp->setting.crypto[matched_idx];
/* If buffer_len==0, just skip the crypto attribute. */
if (buffer_len) {
pj_strset(&attr_value, buffer, buffer_len);
attr = pjmedia_sdp_attr_create(sdp_pool, ID_CRYPTO.ptr,
&attr_value);
m_loc->attr[m_loc->attr_count++] = attr;
}
/* At this point, we get valid rx_policy_neg & tx_policy_neg. */
}
}
goto PROPAGATE_MEDIA_CREATE;
BYPASS_SRTP:
/* Do not update this flag here as actually the media session hasn't been
* updated.
*/
//srtp->bypass_srtp = PJ_TRUE;
PROPAGATE_MEDIA_CREATE:
return pjmedia_transport_encode_sdp(srtp->member_tp, sdp_pool,
sdp_local, sdp_remote, media_index);
}
static pj_status_t transport_media_start(pjmedia_transport *tp,
pj_pool_t *pool,
const pjmedia_sdp_session *sdp_local,
const pjmedia_sdp_session *sdp_remote,
unsigned media_index)
{
struct transport_srtp *srtp = (struct transport_srtp*) tp;
pjmedia_sdp_media *m_rem, *m_loc;
pj_status_t status;
unsigned i;
PJ_ASSERT_RETURN(tp && pool && sdp_local && sdp_remote, PJ_EINVAL);
m_rem = sdp_remote->media[media_index];
m_loc = sdp_local->media[media_index];
if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP) == 0)
srtp->peer_use = PJMEDIA_SRTP_MANDATORY;
else
srtp->peer_use = PJMEDIA_SRTP_OPTIONAL;
/* For answerer side, this function will just have to start SRTP */
/* Check remote media transport & set local media transport
* based on SRTP usage option.
*/
if (srtp->offerer_side) {
if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) {
if (pjmedia_sdp_media_find_attr(m_rem, &ID_CRYPTO, NULL)) {
DEACTIVATE_MEDIA(pool, m_loc);
return PJMEDIA_SRTP_ESDPINCRYPTO;
}
goto BYPASS_SRTP;
} else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) {
// Regardless the answer's transport type (RTP/AVP or RTP/SAVP),
// the answer must be processed through in optional mode.
// Please note that at this point transport type is ensured to be
// RTP/AVP or RTP/SAVP, see transport_media_create()
//if (pj_stricmp(&m_rem->desc.transport, &m_loc->desc.transport)) {
//DEACTIVATE_MEDIA(pool, m_loc);
//return PJMEDIA_SDP_EINPROTO;
//}
} else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) {
if (pj_stricmp(&m_rem->desc.transport, &ID_RTP_SAVP)) {
DEACTIVATE_MEDIA(pool, m_loc);
return PJMEDIA_SDP_EINPROTO;
}
}
}
if (srtp->offerer_side) {
/* find supported crypto-suite, get the tag, and assign policy_local */
pjmedia_srtp_crypto tmp_tx_crypto;
pj_bool_t has_crypto_attr = PJ_FALSE;
int rem_tag;
for (i=0; i<m_rem->attr_count; ++i) {
if (pj_stricmp(&m_rem->attr[i]->name, &ID_CRYPTO) != 0)
continue;
/* more than one crypto attribute in media answer */
if (has_crypto_attr) {
DEACTIVATE_MEDIA(pool, m_loc);
return PJMEDIA_SRTP_ESDPAMBIGUEANS;
}
has_crypto_attr = PJ_TRUE;
status = parse_attr_crypto(srtp->pool, m_rem->attr[i],
&tmp_tx_crypto, &rem_tag);
if (status != PJ_SUCCESS)
return status;
/* our offer tag is always ordered by setting */
if (rem_tag < 1 || rem_tag > (int)srtp->setting.crypto_count) {
DEACTIVATE_MEDIA(pool, m_loc);
return PJMEDIA_SRTP_ESDPINCRYPTOTAG;
}
/* match the crypto name */
if (pj_stricmp(&tmp_tx_crypto.name,
&srtp->setting.crypto[rem_tag-1].name) != 0)
{
DEACTIVATE_MEDIA(pool, m_loc);
return PJMEDIA_SRTP_ECRYPTONOTMATCH;
}
srtp->tx_policy_neg = srtp->setting.crypto[rem_tag-1];
srtp->rx_policy_neg = tmp_tx_crypto;
}
if (srtp->setting.use == PJMEDIA_SRTP_DISABLED) {
/* should never reach here */
goto BYPASS_SRTP;
} else if (srtp->setting.use == PJMEDIA_SRTP_OPTIONAL) {
if (!has_crypto_attr)
goto BYPASS_SRTP;
} else if (srtp->setting.use == PJMEDIA_SRTP_MANDATORY) {
if (!has_crypto_attr) {
DEACTIVATE_MEDIA(pool, m_loc);
return PJMEDIA_SRTP_ESDPREQCRYPTO;
}
}
/* At this point, we get valid rx_policy_neg & tx_policy_neg. */
}
/* Make sure we have the SRTP policies */
if (srtp_crypto_empty(&srtp->tx_policy_neg) ||
srtp_crypto_empty(&srtp->rx_policy_neg))
{
goto BYPASS_SRTP;
}
/* Reset probation counts */
srtp->probation_cnt = PROBATION_CNT_INIT;
/* Got policy_local & policy_remote, let's initalize the SRTP */
/* Ticket #1075: media_start() is called whenever media description
* gets updated, e.g: call hold, however we should restart SRTP only
* when the SRTP policy settings are updated.
*/
if (srtp_crypto_cmp(&srtp->tx_policy_neg, &srtp->tx_policy) ||
srtp_crypto_cmp(&srtp->rx_policy_neg, &srtp->rx_policy))
{
status = pjmedia_transport_srtp_start(tp,
&srtp->tx_policy_neg,
&srtp->rx_policy_neg);
if (status != PJ_SUCCESS)
return status;
}
srtp->bypass_srtp = PJ_FALSE;
goto PROPAGATE_MEDIA_START;
BYPASS_SRTP:
srtp->bypass_srtp = PJ_TRUE;
srtp->peer_use = PJMEDIA_SRTP_DISABLED;
if (srtp->session_inited) {
pjmedia_transport_srtp_stop(tp);
}
PROPAGATE_MEDIA_START:
return pjmedia_transport_media_start(srtp->member_tp, pool,
sdp_local, sdp_remote,
media_index);
}
static pj_status_t transport_media_stop(pjmedia_transport *tp)
{
struct transport_srtp *srtp = (struct transport_srtp*) tp;
pj_status_t status;
PJ_ASSERT_RETURN(tp, PJ_EINVAL);
status = pjmedia_transport_media_stop(srtp->member_tp);
if (status != PJ_SUCCESS)
PJ_LOG(4, (srtp->pool->obj_name,
"SRTP failed stop underlying media transport."));
return pjmedia_transport_srtp_stop(tp);
}
/* Utility */
PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp,
pj_bool_t is_rtp,
void *pkt,
int *pkt_len)
{
transport_srtp *srtp = (transport_srtp *)tp;
err_status_t err;
if (srtp->bypass_srtp)
return PJ_SUCCESS;
PJ_ASSERT_RETURN(tp && pkt && (*pkt_len>0), PJ_EINVAL);
PJ_ASSERT_RETURN(srtp->session_inited, PJ_EINVALIDOP);
/* Make sure buffer is 32bit aligned */
PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return PJ_EINVAL);
pj_lock_acquire(srtp->mutex);
if (!srtp->session_inited) {
pj_lock_release(srtp->mutex);
return PJ_EINVALIDOP;
}
if (is_rtp)
err = srtp_unprotect(srtp->srtp_rx_ctx, pkt, pkt_len);
else
err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, pkt, pkt_len);
if (err != err_status_ok) {
PJ_LOG(5,(srtp->pool->obj_name,
"Failed to unprotect SRTP, pkt size=%d, err=%s",
*pkt_len, get_libsrtp_errstr(err)));
}
pj_lock_release(srtp->mutex);
return (err==err_status_ok) ? PJ_SUCCESS : PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 6:27 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408932
Default Alt Text
(51 KB)
Attached To
Mode
rPYNSIPSIMPLE python3-sipsimple
Attached
Detach File
Event Timeline
Log In to Comment