static pj_status_t transport_destroy (pjmedia_transport *tp) { transport_srtp *srtp = (transport_srtp *) tp; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* Close keying */ for (i=0; i < srtp->keying_cnt; i++) pjmedia_transport_close(srtp->keying[i]); /* Close member if configured */ 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; }
static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pj_status_t status; 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); }
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); pj_lock_acquire(srtp->mutex); if (srtp->setting.close_member_tp && srtp->member_tp) { pjmedia_transport_close(srtp->member_tp); } status = pjmedia_transport_srtp_stop(tp); pj_lock_release(srtp->mutex); pj_lock_destroy(srtp->mutex); pj_pool_release(srtp->pool); return status; }
static pj_status_t start_srtp(transport_srtp *srtp) { /* Make sure we have the SRTP policies */ if (srtp_crypto_empty(&srtp->tx_policy_neg) || srtp_crypto_empty(&srtp->rx_policy_neg)) { srtp->bypass_srtp = PJ_TRUE; srtp->peer_use = PJMEDIA_SRTP_DISABLED; if (srtp->session_inited) { pjmedia_transport_srtp_stop(&srtp->base); } return PJ_SUCCESS; } /* 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)) { pj_status_t status; status = pjmedia_transport_srtp_start(&srtp->base, &srtp->tx_policy_neg, &srtp->rx_policy_neg); if (status != PJ_SUCCESS) return status; } srtp->bypass_srtp = PJ_FALSE; return PJ_SUCCESS; }
static pj_status_t transport_media_stop(pjmedia_transport *tp) { struct transport_srtp *srtp = (struct transport_srtp*) tp; pj_status_t status; unsigned i; PJ_ASSERT_RETURN(tp, PJ_EINVAL); /* Invoke media_stop() of all keying methods */ for (i=0; i < srtp->keying_cnt; ++i) { pjmedia_transport_media_stop(srtp->keying[i]); } /* Invoke media_stop() of member tp */ 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.")); /* Finally, stop SRTP */ return pjmedia_transport_srtp_stop(tp); }
/* * 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; int crypto_suites_cnt; PJ_ASSERT_RETURN(tp && tx && rx, PJ_EINVAL); if (srtp->session_inited) { pjmedia_transport_srtp_stop(tp); } crypto_suites_cnt = sizeof(crypto_suites)/sizeof(crypto_suites[0]); /* 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) return PJMEDIA_SRTP_ENOTSUPCRYPTO; /* 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; return PJ_SUCCESS; } /* 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) return PJMEDIA_SRTP_EINKEYLEN; /* 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) { return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } 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); return PJMEDIA_ERRNO_FROM_LIBSRTP(err); } 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; PJ_LOG(5, (srtp->pool->obj_name, "TX: %s key=%s", srtp->tx_policy.name.ptr, octet_string_hex_string(tx->key.ptr, tx->key.slen))); if (srtp->tx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"TX: disable%s%s", (cr_tx_idx?"":" enc"), (au_tx_idx?"":" auth"))); } PJ_LOG(5, (srtp->pool->obj_name, "RX: %s key=%s", srtp->rx_policy.name.ptr, octet_string_hex_string(rx->key.ptr, rx->key.slen))); if (srtp->rx_policy.flags) { PJ_LOG(5,(srtp->pool->obj_name,"RX: disable%s%s", (cr_rx_idx?"":" enc"), (au_rx_idx?"":" auth"))); } 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; }
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); }