res_pjsip_sdp_rtp: Add support for optimistic SRTP.

Optimistic SRTP is the ability to enable SRTP but not have it be
a fatal requirement. If SRTP can be used it will be, if not it won't be.
This gives you a better chance of using it without having your sessions
fail when it can't be.

Encrypt all the things!

Review: https://reviewboard.asterisk.org/r/3992/
........

Merged revisions 428222 from http://svn.asterisk.org/svn/asterisk/branches/13


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@428224 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/42/42/1
Joshua Colp 11 years ago
parent b2e766a6b7
commit 7f8b7ace72

@ -79,6 +79,13 @@ The features.conf general section has three new configurable options:
For more information on what these options do, see the Asterisk wiki: For more information on what these options do, see the Asterisk wiki:
https://wiki.asterisk.org/wiki/x/W4fAAQ https://wiki.asterisk.org/wiki/x/W4fAAQ
Channel Drivers
------------------
chan_pjsip
------------------
* New 'media_encryption_optimistic' endpoint setting. This will use SRTP
when possible but does not consider lack of it a failure.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
--- Functionality changes from Asterisk 12 to Asterisk 13 -------------------- --- Functionality changes from Asterisk 12 to Asterisk 13 --------------------

@ -655,6 +655,8 @@
;media_encryption=no ; Determines whether res_pjsip will use and enforce ;media_encryption=no ; Determines whether res_pjsip will use and enforce
; usage of media encryption for this endpoint (default: ; usage of media encryption for this endpoint (default:
; "no") ; "no")
;media_encryption_optimistic=no ; Use encryption if possible but don't fail the call
; if not possible.
;inband_progress=no ; Determines whether chan_pjsip will indicate ringing ;inband_progress=no ; Determines whether chan_pjsip will indicate ringing
; using inband progress (default: "no") ; using inband progress (default: "no")
;call_group= ; The numeric pickup groups for a channel (default: "") ;call_group= ; The numeric pickup groups for a channel (default: "")

@ -0,0 +1,31 @@
"""add media encryption optimistic to pjsip
Revision ID: 945b1098bdd
Revises: 15b1430ad6f1
Create Date: 2014-11-19 07:47:52.490388
"""
# revision identifiers, used by Alembic.
revision = '945b1098bdd'
down_revision = '15b1430ad6f1'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import ENUM
YESNO_NAME = 'yesno_values'
YESNO_VALUES = ['yes', 'no']
def upgrade():
############################# Enums ##############################
# yesno_values have already been created, so use postgres enum object
# type to get around "already created" issue - works okay with mysql
yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
op.add_column('ps_endpoints', sa.Column('media_encryption_optimistic', yesno_values))
def downgrade():
op.drop_column('ps_endpoints', 'media_encryption_optimistic')

@ -483,6 +483,8 @@ struct ast_sip_media_rtp_configuration {
unsigned int srtp_tag_32; unsigned int srtp_tag_32;
/*! Do we use media encryption? what type? */ /*! Do we use media encryption? what type? */
enum ast_sip_session_media_encryption encryption; enum ast_sip_session_media_encryption encryption;
/*! Do we want to optimistically support encryption if possible? */
unsigned int encryption_optimistic;
}; };
/*! /*!

@ -73,6 +73,8 @@ struct ast_sip_session_media {
struct ast_sip_session_sdp_handler *handler; struct ast_sip_session_sdp_handler *handler;
/*! \brief Holds SRTP information */ /*! \brief Holds SRTP information */
struct ast_sdp_srtp *srtp; struct ast_sdp_srtp *srtp;
/*! \brief What type of encryption is in use on this stream */
enum ast_sip_session_media_encryption encryption;
/*! \brief The media transport in use for this stream */ /*! \brief The media transport in use for this stream */
pj_str_t transport; pj_str_t transport;
/*! \brief Stream is on hold by remote side */ /*! \brief Stream is on hold by remote side */

@ -431,6 +431,14 @@
</enumlist> </enumlist>
</description> </description>
</configOption> </configOption>
<configOption name="media_encryption_optimistic" default="no">
<synopsis>Determines whether encryption should be used if possible but does not terminate the
session if not achieved.</synopsis>
<description><para>
This option only applies if <replaceable>media_encryption</replaceable> is
set to <literal>sdes</literal> or <literal>dtls</literal>.
</para></description>
</configOption>
<configOption name="inband_progress" default="no"> <configOption name="inband_progress" default="no">
<synopsis>Determines whether chan_pjsip will indicate ringing using inband <synopsis>Determines whether chan_pjsip will indicate ringing using inband
progress.</synopsis> progress.</synopsis>
@ -1504,6 +1512,9 @@
<parameter name="MediaEncryption"> <parameter name="MediaEncryption">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='media_encryption']/synopsis/node())"/></para> <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='media_encryption']/synopsis/node())"/></para>
</parameter> </parameter>
<parameter name="MediaEncryptionOptimistic">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='media_encryption_optimistic']/synopsis/node())"/></para>
</parameter>
<parameter name="UseAvpf"> <parameter name="UseAvpf">
<para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='use_avpf']/synopsis/node())"/></para> <para><xi:include xpointer="xpointer(/docs/configInfo[@name='res_pjsip']/configFile[@name='pjsip.conf']/configObject[@name='endpoint']/configOption[@name='use_avpf']/synopsis/node())"/></para>
</parameter> </parameter>

@ -1764,6 +1764,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_setup", "", dtls_handler, dtlssetup_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_setup", "", dtls_handler, dtlssetup_to_str, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_fingerprint", "", dtls_handler, dtlsfingerprint_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtls_fingerprint", "", dtls_handler, dtlsfingerprint_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "srtp_tag_32", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.srtp_tag_32));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_encryption_optimistic", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.encryption_optimistic));
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "redirect_method", "user", redirect_handler, NULL, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "redirect_method", "user", redirect_handler, NULL, NULL, 0, 0);
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "set_var", "", set_var_handler, set_var_to_str, set_var_to_vl, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct ast_sip_endpoint, message_context)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "message_context", "", OPT_STRINGFIELD_T, 1, STRFLDSET(struct ast_sip_endpoint, message_context));

@ -494,14 +494,41 @@ static void process_ice_attributes(struct ast_sip_session *session, struct ast_s
ice->start(session_media->rtp); ice->start(session_media->rtp);
} }
/*! \brief figure out if media stream has crypto lines for sdes */
static int media_stream_has_crypto(const struct pjmedia_sdp_media *stream)
{
int i;
for (i = 0; i < stream->attr_count; i++) {
pjmedia_sdp_attr *attr;
/* check the stream for the required crypto attribute */
attr = stream->attr[i];
if (pj_strcmp2(&attr->name, "crypto")) {
continue;
}
return 1;
}
return 0;
}
/*! \brief figure out media transport encryption type from the media transport string */ /*! \brief figure out media transport encryption type from the media transport string */
static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t transport) static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t transport,
const struct pjmedia_sdp_media *stream, unsigned int *optimistic)
{ {
RAII_VAR(char *, transport_str, ast_strndup(transport.ptr, transport.slen), ast_free); RAII_VAR(char *, transport_str, ast_strndup(transport.ptr, transport.slen), ast_free);
*optimistic = 0;
if (strstr(transport_str, "UDP/TLS")) { if (strstr(transport_str, "UDP/TLS")) {
return AST_SIP_MEDIA_ENCRYPT_DTLS; return AST_SIP_MEDIA_ENCRYPT_DTLS;
} else if (strstr(transport_str, "SAVP")) { } else if (strstr(transport_str, "SAVP")) {
return AST_SIP_MEDIA_ENCRYPT_SDES; return AST_SIP_MEDIA_ENCRYPT_SDES;
} else if (media_stream_has_crypto(stream)) {
*optimistic = 1;
return AST_SIP_MEDIA_ENCRYPT_SDES;
} else { } else {
return AST_SIP_MEDIA_ENCRYPT_NONE; return AST_SIP_MEDIA_ENCRYPT_NONE;
} }
@ -523,22 +550,31 @@ static enum ast_sip_session_media_encryption check_endpoint_media_transport(
{ {
enum ast_sip_session_media_encryption incoming_encryption; enum ast_sip_session_media_encryption incoming_encryption;
char transport_end = stream->desc.transport.ptr[stream->desc.transport.slen - 1]; char transport_end = stream->desc.transport.ptr[stream->desc.transport.slen - 1];
unsigned int optimistic;
if ((transport_end == 'F' && !endpoint->media.rtp.use_avpf) if ((transport_end == 'F' && !endpoint->media.rtp.use_avpf)
|| (transport_end != 'F' && endpoint->media.rtp.use_avpf)) { || (transport_end != 'F' && endpoint->media.rtp.use_avpf)) {
return AST_SIP_MEDIA_TRANSPORT_INVALID; return AST_SIP_MEDIA_TRANSPORT_INVALID;
} }
incoming_encryption = get_media_encryption_type(stream->desc.transport); incoming_encryption = get_media_encryption_type(stream->desc.transport, stream, &optimistic);
if (incoming_encryption == endpoint->media.rtp.encryption) { if (incoming_encryption == endpoint->media.rtp.encryption) {
return incoming_encryption; return incoming_encryption;
} }
if (endpoint->media.rtp.force_avp) { if (endpoint->media.rtp.force_avp ||
endpoint->media.rtp.encryption_optimistic) {
return incoming_encryption; return incoming_encryption;
} }
/* If an optimistic offer has been made but encryption is not enabled consider it as having
* no offer of crypto at all instead of invalid so the session proceeds.
*/
if (optimistic) {
return AST_SIP_MEDIA_ENCRYPT_NONE;
}
return AST_SIP_MEDIA_TRANSPORT_INVALID; return AST_SIP_MEDIA_TRANSPORT_INVALID;
} }
@ -697,7 +733,7 @@ static int setup_media_encryption(struct ast_sip_session *session,
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *sdp,
const struct pjmedia_sdp_media *stream) const struct pjmedia_sdp_media *stream)
{ {
switch (session->endpoint->media.rtp.encryption) { switch (session_media->encryption) {
case AST_SIP_MEDIA_ENCRYPT_SDES: case AST_SIP_MEDIA_ENCRYPT_SDES:
if (setup_sdes_srtp(session_media, stream)) { if (setup_sdes_srtp(session_media, stream)) {
return -1; return -1;
@ -726,6 +762,8 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
char host[NI_MAXHOST]; char host[NI_MAXHOST];
RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr); RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);
enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
int res;
/* If port is 0, ignore this media stream */ /* If port is 0, ignore this media stream */
if (!stream->desc.port) { if (!stream->desc.port) {
@ -740,9 +778,12 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
} }
/* Ensure incoming transport is compatible with the endpoint's configuration */ /* Ensure incoming transport is compatible with the endpoint's configuration */
if (!session->endpoint->media.rtp.use_received_transport && if (!session->endpoint->media.rtp.use_received_transport) {
check_endpoint_media_transport(session->endpoint, stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { encryption = check_endpoint_media_transport(session->endpoint, stream);
return -1;
if (encryption == AST_SIP_MEDIA_TRANSPORT_INVALID) {
return -1;
}
} }
ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host));
@ -758,13 +799,27 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
return -1; return -1;
} }
if (session->endpoint->media.rtp.use_received_transport) { res = setup_media_encryption(session, session_media, sdp, stream);
if (res) {
if (!session->endpoint->media.rtp.encryption_optimistic) {
/* If optimistic encryption is disabled and crypto should have been enabled
* but was not this session must fail.
*/
return -1;
}
/* There is no encryption, sad. */
session_media->encryption = AST_SIP_MEDIA_ENCRYPT_NONE;
}
/* If we've been explicitly configured to use the received transport OR if
* encryption is on and crypto is present use the received transport.
* This is done in case of optimistic because it may come in as RTP/AVP or RTP/SAVP depending
* on the configuration of the remote endpoint (optimistic themselves or mandatory).
*/
if ((session->endpoint->media.rtp.use_received_transport) ||
((encryption == AST_SIP_MEDIA_ENCRYPT_SDES) && !res)) {
pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport); pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport);
} }
if (setup_media_encryption(session, session_media, sdp, stream)) {
return -1;
}
if (set_caps(session, session_media, stream)) { if (set_caps(session, session_media, stream)) {
return 0; return 0;
@ -788,7 +843,7 @@ static int add_crypto_to_stream(struct ast_sip_session *session,
static const pj_str_t STR_ACTPASS = { "actpass", 7 }; static const pj_str_t STR_ACTPASS = { "actpass", 7 };
static const pj_str_t STR_HOLDCONN = { "holdconn", 8 }; static const pj_str_t STR_HOLDCONN = { "holdconn", 8 };
switch (session->endpoint->media.rtp.encryption) { switch (session_media->encryption) {
case AST_SIP_MEDIA_ENCRYPT_NONE: case AST_SIP_MEDIA_ENCRYPT_NONE:
case AST_SIP_MEDIA_TRANSPORT_INVALID: case AST_SIP_MEDIA_TRANSPORT_INVALID:
break; break;
@ -923,11 +978,14 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
} }
media->desc.media = pj_str(session_media->stream_type); media->desc.media = pj_str(session_media->stream_type);
if (session->endpoint->media.rtp.use_received_transport && pj_strlen(&session_media->transport)) { if (pj_strlen(&session_media->transport)) {
/* If a transport has already been specified use it */
media->desc.transport = session_media->transport; media->desc.transport = session_media->transport;
} else { } else {
media->desc.transport = pj_str(ast_sdp_get_rtp_profile( media->desc.transport = pj_str(ast_sdp_get_rtp_profile(
session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES, /* Optimistic encryption places crypto in the normal RTP/AVP profile */
!session->endpoint->media.rtp.encryption_optimistic &&
(session_media->encryption == AST_SIP_MEDIA_ENCRYPT_SDES),
session_media->rtp, session->endpoint->media.rtp.use_avpf, session_media->rtp, session->endpoint->media.rtp.use_avpf,
session->endpoint->media.rtp.force_avp)); session->endpoint->media.rtp.force_avp));
} }
@ -1063,7 +1121,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr); RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);
char host[NI_MAXHOST]; char host[NI_MAXHOST];
int fdno; int fdno, res;
if (!session->channel) { if (!session->channel) {
return 1; return 1;
@ -1084,10 +1142,18 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1; return -1;
} }
if (setup_media_encryption(session, session_media, remote, remote_stream)) { res = setup_media_encryption(session, session_media, remote, remote_stream);
if (!session->endpoint->media.rtp.encryption_optimistic && res) {
/* If optimistic encryption is disabled and crypto should have been enabled but was not
* this session must fail.
*/
return -1; return -1;
} }
if (!remote_stream->conn && !remote->conn) {
return 1;
}
ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));
/* Ensure that the address provided is valid */ /* Ensure that the address provided is valid */
@ -1137,6 +1203,9 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
session_media->remotely_held = 0; session_media->remotely_held = 0;
} }
/* This purposely resets the encryption to the configured in case it gets added later */
session_media->encryption = session->endpoint->media.rtp.encryption;
return 1; return 1;
} }

@ -1000,6 +1000,7 @@ static int add_session_media(void *obj, void *arg, int flags)
if (!session_media) { if (!session_media) {
return CMP_STOP; return CMP_STOP;
} }
session_media->encryption = session->endpoint->media.rtp.encryption;
/* Safe use of strcpy */ /* Safe use of strcpy */
strcpy(session_media->stream_type, handler_list->stream_type); strcpy(session_media->stream_type, handler_list->stream_type);
ao2_link(session->media, session_media); ao2_link(session->media, session_media);
@ -1046,6 +1047,8 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
return NULL; return NULL;
} }
session->endpoint = ao2_bump(endpoint);
session->media = ao2_container_alloc(MEDIA_BUCKETS, session_media_hash, session_media_cmp); session->media = ao2_container_alloc(MEDIA_BUCKETS, session_media_hash, session_media_cmp);
if (!session->media) { if (!session->media) {
return NULL; return NULL;
@ -1061,7 +1064,6 @@ struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
ast_sip_dialog_set_endpoint(inv_session->dlg, endpoint); ast_sip_dialog_set_endpoint(inv_session->dlg, endpoint);
pjsip_dlg_inc_session(inv_session->dlg, &session_module); pjsip_dlg_inc_session(inv_session->dlg, &session_module);
inv_session->mod_data[session_module.id] = ao2_bump(session); inv_session->mod_data[session_module.id] = ao2_bump(session);
session->endpoint = ao2_bump(endpoint);
session->contact = ao2_bump(contact); session->contact = ao2_bump(contact);
session->inv_session = inv_session; session->inv_session = inv_session;
session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);

Loading…
Cancel
Save