diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index 6bcac0034e..4f5627a048 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -41,12 +41,50 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_RTP_MAX_BUF_LEN 16384 +#define SWITCH_RTP_MAX_CRYPTO_LEN 64 +#define SWITCH_RTP_KEY_LEN 30 +#define SWITCH_RTP_CRYPTO_KEY_32 "AES_CM_128_HMAC_SHA1_32" +#define SWITCH_RTP_CRYPTO_KEY_80 "AES_CM_128_HMAC_SHA1_80" + + +typedef enum { + SWITCH_RTP_CRYPTO_SEND, + SWITCH_RTP_CRYPTO_RECV, + SWITCH_RTP_CRYPTO_MAX +} switch_rtp_crypto_direction_t; + +typedef enum { + NO_CRYPTO, + AES_CM_128_HMAC_SHA1_80, + AES_CM_128_HMAC_SHA1_32 +} switch_rtp_crypto_key_type_t; + +struct switch_rtp_crypto_key { + uint32_t index; + switch_rtp_crypto_key_type_t type; + unsigned char key[SWITCH_RTP_MAX_CRYPTO_LEN]; + switch_size_t keylen; + struct switch_rtp_crypto_key *next; +}; +typedef struct switch_rtp_crypto_key switch_rtp_crypto_key_t; + + + +SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session, + switch_rtp_crypto_direction_t direction, + uint32_t index, + switch_rtp_crypto_key_type_t type, + unsigned char *key, + switch_size_t keylen); + ///\defgroup rtp RTP (RealTime Transport Protocol) ///\ingroup core1 ///\{ typedef void (*switch_rtp_invalid_handler_t) (switch_rtp_t *rtp_session, switch_socket_t * sock, void *data, switch_size_t datalen, switch_sockaddr_t * from_addr); + +SWITCH_DECLARE(void) switch_rtp_get_random(void *buf, uint32_t len); /*! \brief Initilize the RTP System \param pool the memory pool to use for long term allocations @@ -83,7 +121,6 @@ SWITCH_DECLARE(void) switch_rtp_release_port(const char *ip, switch_port_t port) \param samples_per_interval the default samples_per_interval \param ms_per_packet time in microseconds per packet \param flags flags to control behaviour - \param crypto_key optional crypto key \param timer_name timer interface to use \param err a pointer to resolve error messages \param pool a memory pool to use for the session @@ -93,7 +130,9 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session switch_payload_t payload, uint32_t samples_per_interval, uint32_t ms_per_packet, - switch_rtp_flag_t flags, char *crypto_key, char *timer_name, const char **err, + switch_rtp_flag_t flags, + char *timer_name, + const char **err, switch_memory_pool_t *pool); @@ -107,7 +146,6 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session \param samples_per_interval the default samples_per_interval \param ms_per_packet time in microseconds per packet \param flags flags to control behaviour - \param crypto_key optional crypto key \param timer_name timer interface to use \param err a pointer to resolve error messages \param pool a memory pool to use for the session @@ -120,7 +158,10 @@ SWITCH_DECLARE(switch_rtp_t *) switch_rtp_new(const char *rx_host, switch_payload_t payload, uint32_t samples_per_interval, uint32_t ms_per_packet, - switch_rtp_flag_t flags, char *crypto_key, char *timer_name, const char **err, switch_memory_pool_t *pool); + switch_rtp_flag_t flags, + char *timer_name, + const char **err, + switch_memory_pool_t *pool); /*! diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index dbcc14c233..fcacd85f96 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -56,7 +56,7 @@ SWITCH_BEGIN_EXTERN_C #endif SWITCH_DECLARE(switch_status_t) switch_b64_encode(unsigned char *in, switch_size_t ilen, unsigned char *out, switch_size_t olen); -SWITCH_DECLARE(switch_status_t) switch_b64_decode(char *in, char *out, switch_size_t olen); +SWITCH_DECLARE(switch_size_t) switch_b64_decode(char *in, char *out, switch_size_t olen); SWITCH_DECLARE(char *) switch_amp_encode(char *s, char *buf, switch_size_t len); static inline switch_bool_t switch_is_digit_string(const char *s) { diff --git a/src/mod/applications/mod_esf/mod_esf.c b/src/mod/applications/mod_esf/mod_esf.c index a7ddf54309..209c5b25c1 100644 --- a/src/mod/applications/mod_esf/mod_esf.c +++ b/src/mod/applications/mod_esf/mod_esf.c @@ -167,7 +167,7 @@ SWITCH_STANDARD_APP(bcast_function) read_codec->implementation->samples_per_frame, read_codec->implementation->microseconds_per_frame, (switch_rtp_flag_t) flags, - NULL, "soft", &err, switch_core_session_get_pool(session)); + "soft", &err, switch_core_session_get_pool(session)); if (!switch_rtp_ready(rtp_session)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP Error\n"); diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c index 967d1766b1..37d4dea72a 100644 --- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c +++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c @@ -877,7 +877,7 @@ static int activate_rtp(struct private_object *tech_pvt) tech_pvt->codec_num, tech_pvt->read_codec.implementation->samples_per_frame, tech_pvt->read_codec.implementation->microseconds_per_frame, - flags, NULL, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)))) { + flags, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP ERROR %s\n", err); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return 0; diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 3453c4e464..930428fb43 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -90,6 +90,19 @@ static switch_status_t sofia_on_init(switch_core_session_t *session) } if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { + const char *var; + + if ((var = switch_channel_get_variable(channel, SOFIA_SECURE_MEDIA_VARIABLE)) && !switch_strlen_zero(var)) { + if (switch_true(var) || !strcasecmp(var, SWITCH_RTP_CRYPTO_KEY_32)) { + switch_set_flag_locked(tech_pvt, TFLAG_SECURE); + sofia_glue_build_crypto(tech_pvt, 1, AES_CM_128_HMAC_SHA1_32, SWITCH_RTP_CRYPTO_SEND); + } else if (!strcasecmp(var, SWITCH_RTP_CRYPTO_KEY_80)) { + switch_set_flag_locked(tech_pvt, TFLAG_SECURE); + sofia_glue_build_crypto(tech_pvt, 1, AES_CM_128_HMAC_SHA1_80, SWITCH_RTP_CRYPTO_SEND); + } + } + + if (sofia_glue_do_invite(session) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return SWITCH_STATUS_FALSE; @@ -786,6 +799,13 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi tech_pvt = (private_object_t *) switch_core_session_get_private(session); switch_assert(tech_pvt != NULL); + if (msg->message_id == SWITCH_MESSAGE_INDICATE_ANSWER || msg->message_id == SWITCH_MESSAGE_INDICATE_PROGRESS) { + const char *var; + if ((var = switch_channel_get_variable(channel, SOFIA_SECURE_MEDIA_VARIABLE)) && switch_true(var)) { + switch_set_flag_locked(tech_pvt, TFLAG_SECURE); + } + } + switch (msg->message_id) { case SWITCH_MESSAGE_INDICATE_BROADCAST: { const char *ip = NULL, *port = NULL; @@ -1746,6 +1766,7 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session *pool = NULL; done: + if (profile) { sofia_glue_release_profile(profile); } diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index 9609f8a8f4..69c5d1c87b 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -73,6 +73,9 @@ typedef struct private_object private_object_t; #define SOFIA_DEFAULT_PORT "5060" #define SOFIA_DEFAULT_TLS_PORT "5061" #define SOFIA_REFER_TO_VARIABLE "sip_refer_to" +#define SOFIA_SECURE_MEDIA_VARIABLE "sip_secure_media" +#define SOFIA_SECURE_MEDIA_CONFIRMED_VARIABLE "sip_secure_media_confirmed" +#define SOFIA_HAS_CRYPTO_VARIABLE "sip_has_crypto" #include #include @@ -124,7 +127,8 @@ typedef enum { PFLAG_MULTIREG = (1 << 11), PFLAG_SUPRESS_CNG = (1 << 12), PFLAG_TLS = (1 << 13), - PFLAG_CHECKUSER = (1 << 14) + PFLAG_CHECKUSER = (1 << 14), + PFLAG_SECURE = (1 << 15) } PFLAGS; typedef enum { @@ -339,6 +343,13 @@ struct private_object { char *invite_contact; char *local_url; char *gateway_name; + char *local_crypto_key; + char *remote_crypto_key; + unsigned char local_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN]; + unsigned char remote_raw_key[SWITCH_RTP_MAX_CRYPTO_LEN]; + switch_rtp_crypto_key_type_t crypto_send_type; + switch_rtp_crypto_key_type_t crypto_recv_type; + switch_rtp_crypto_key_type_t crypto_type; unsigned long rm_rate; switch_payload_t pt; switch_mutex_t *flag_mutex; @@ -561,3 +572,4 @@ const char *sofia_glue_transport2str(const sofia_transport_t tp); int sofia_glue_transport_has_tls(const sofia_transport_t tp); const char *sofia_glue_get_unknown_header(sip_t const *sip, const char *name); +switch_status_t sofia_glue_build_crypto(private_object_t *tech_pvt, int index, switch_rtp_crypto_key_type_t type, switch_rtp_crypto_direction_t direction); diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 97209fd306..494e946425 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -1010,6 +1010,10 @@ switch_status_t config_sofia(int reload, char *profile_name) if (switch_true(val)) { profile->pflags |= PFLAG_PRESENCE; } + } else if (!strcasecmp(var, "require-secure-rtp")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_SECURE; + } } else if (!strcasecmp(var, "multiple-registrations")) { if (switch_true(val)) { profile->pflags |= PFLAG_MULTIREG; diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index c91e1bcf63..637f832bb9 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -89,6 +89,8 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32 "a=%s\n" "m=audio %d RTP/AVP", tech_pvt->owner_id, tech_pvt->session_id, ip, ip, sr, port); + + if (tech_pvt->rm_encoding) { switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->pt); } else if (tech_pvt->num_codecs) { @@ -126,6 +128,7 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32 switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n"); + if (tech_pvt->rm_encoding) { rate = tech_pvt->rm_rate; switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%d\n", tech_pvt->pt, tech_pvt->rm_encoding, rate); @@ -181,6 +184,11 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32 switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=ptime:%d\n", ptime); } + if (!switch_strlen_zero(tech_pvt->local_crypto_key) && switch_test_flag(tech_pvt, TFLAG_SECURE)) { + switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=crypto:%s\n", tech_pvt->local_crypto_key); + switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=encryption:optional\n"); + } + if (switch_test_flag(tech_pvt, TFLAG_VIDEO) && tech_pvt->video_rm_encoding) { sofia_glue_tech_choose_video_port(tech_pvt); if ((v_port = tech_pvt->adv_sdp_video_port)) { @@ -928,7 +936,7 @@ void sofia_glue_tech_absorb_sdp(private_object_t *tech_pvt) void sofia_glue_deactivate_rtp(private_object_t *tech_pvt) { - int loops = 0; //, sock = -1; + int loops = 0; if (switch_rtp_ready(tech_pvt->rtp_session)) { while (loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) { switch_yield(10000); @@ -1077,6 +1085,102 @@ switch_status_t sofia_glue_tech_set_codec(private_object_t *tech_pvt, int force) return SWITCH_STATUS_SUCCESS; } + +switch_status_t sofia_glue_build_crypto(private_object_t *tech_pvt, int index, switch_rtp_crypto_key_type_t type, switch_rtp_crypto_direction_t direction) +{ + unsigned char b64_key[512] = ""; + const char *type_str; + unsigned char *key; + + char *p; + + if (type == AES_CM_128_HMAC_SHA1_80) { + type_str = SWITCH_RTP_CRYPTO_KEY_80; + } else { + type_str = SWITCH_RTP_CRYPTO_KEY_32; + } + + if (direction == SWITCH_RTP_CRYPTO_SEND) { + key = tech_pvt->local_raw_key; + } else { + key = tech_pvt->remote_raw_key; + + } + + switch_rtp_get_random(key, SWITCH_RTP_KEY_LEN); + switch_b64_encode(key, SWITCH_RTP_KEY_LEN, b64_key, sizeof(b64_key)); + p = strrchr((char *)b64_key, '='); + + while(p && *p && *p == '=') { + *p-- = '\0'; + } + + tech_pvt->local_crypto_key = switch_core_session_sprintf(tech_pvt->session, "%d %s inline:%s", index, type_str, b64_key); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set Local Key [%s]\n", tech_pvt->local_crypto_key); + tech_pvt->crypto_type = type; + + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t sofia_glue_add_crypto(private_object_t *tech_pvt, const char *key_str, switch_rtp_crypto_direction_t direction) +{ + unsigned char key[SWITCH_RTP_MAX_CRYPTO_LEN]; + int index; + switch_rtp_crypto_key_type_t type; + char *p; + + + if (!switch_rtp_ready(tech_pvt->rtp_session)) { + goto bad; + } + + index = atoi(key_str); + + p = strchr(key_str, ' '); + + if (p && *p && *(p+1)) { + p++; + if (!strncasecmp(p, SWITCH_RTP_CRYPTO_KEY_32, strlen(SWITCH_RTP_CRYPTO_KEY_32))) { + type = AES_CM_128_HMAC_SHA1_32; + } else if (!strncasecmp(p, SWITCH_RTP_CRYPTO_KEY_80, strlen(SWITCH_RTP_CRYPTO_KEY_80))) { + type = AES_CM_128_HMAC_SHA1_80; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error near [%s]\n", p); + goto bad; + } + + p = strchr(p, ' '); + if (p && *p && *(p+1)) { + p++; + if (strncasecmp(p, "inline:", 7)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error near [%s]\n", p); + goto bad; + } + + p += 7; + switch_b64_decode(p, (char *)key, sizeof(key)); + + if (direction == SWITCH_RTP_CRYPTO_SEND) { + tech_pvt->crypto_send_type = type; + memcpy(tech_pvt->local_raw_key, key, SWITCH_RTP_KEY_LEN); + } else { + tech_pvt->crypto_recv_type = type; + memcpy(tech_pvt->remote_raw_key, key, SWITCH_RTP_KEY_LEN); + } + return SWITCH_STATUS_SUCCESS; + } + + } + + bad: + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n"); + return SWITCH_STATUS_FALSE; + +} + + switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_flag_t myflags) { int bw, ms; @@ -1175,8 +1279,10 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f tech_pvt->read_codec.implementation->samples_per_frame, tech_pvt->codec_ms * 1000, (switch_rtp_flag_t) flags, - NULL, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)); - + tech_pvt->profile->timer_name, + &err, + switch_core_session_get_pool(tech_pvt->session)); + if (switch_rtp_ready(tech_pvt->rtp_session)) { uint8_t vad_in = switch_test_flag(tech_pvt, TFLAG_VAD_IN) ? 1 : 0; uint8_t vad_out = switch_test_flag(tech_pvt, TFLAG_VAD_OUT) ? 1 : 0; @@ -1243,6 +1349,14 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f switch_rtp_set_cng_pt(tech_pvt->rtp_session, tech_pvt->cng_pt); } + if (tech_pvt->remote_crypto_key && switch_test_flag(tech_pvt, TFLAG_SECURE)) { + sofia_glue_add_crypto(tech_pvt, tech_pvt->remote_crypto_key, SWITCH_RTP_CRYPTO_RECV); + switch_rtp_add_crypto_key(tech_pvt->rtp_session, SWITCH_RTP_CRYPTO_SEND, 1, tech_pvt->crypto_type, tech_pvt->local_raw_key, SWITCH_RTP_KEY_LEN); + switch_rtp_add_crypto_key(tech_pvt->rtp_session, SWITCH_RTP_CRYPTO_RECV, 1, tech_pvt->crypto_type, tech_pvt->remote_raw_key, SWITCH_RTP_KEY_LEN); + switch_channel_set_variable(tech_pvt->channel, SOFIA_SECURE_MEDIA_CONFIRMED_VARIABLE, "true"); + } + + sofia_glue_check_video_codecs(tech_pvt); if (switch_test_flag(tech_pvt, TFLAG_VIDEO) && tech_pvt->video_rm_encoding) { @@ -1250,16 +1364,16 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f sofia_glue_tech_set_video_codec(tech_pvt, 0); tech_pvt->video_rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip, - tech_pvt->local_sdp_video_port, - tech_pvt->remote_sdp_video_ip, - tech_pvt->remote_sdp_video_port, - tech_pvt->video_agreed_pt, - tech_pvt->video_read_codec.implementation->samples_per_frame, - 0, - (switch_rtp_flag_t) flags, - NULL, - NULL, - &err, switch_core_session_get_pool(tech_pvt->session)); + tech_pvt->local_sdp_video_port, + tech_pvt->remote_sdp_video_ip, + tech_pvt->remote_sdp_video_port, + tech_pvt->video_agreed_pt, + tech_pvt->video_read_codec.implementation->samples_per_frame, + 0, + (switch_rtp_flag_t) flags, + NULL, + &err, + switch_core_session_get_pool(tech_pvt->session)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VIDEO RTP [%s] %s:%d->%s:%d codec: %u ms: %d [%s]\n", switch_channel_get_name(tech_pvt->channel), @@ -1396,7 +1510,6 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * } if (stream) { - //switch_ivr_displace_session(tech_pvt->session, stream, 0, "rl"); switch_ivr_broadcast(switch_channel_get_variable(tech_pvt->channel, SWITCH_SIGNAL_BOND_VARIABLE), stream, SMF_ECHO_ALEG | SMF_LOOP); } } @@ -1405,8 +1518,6 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * const char *uuid; switch_core_session_t *b_session; - //const char *stream; - if (tech_pvt->max_missed_packets) { switch_rtp_set_max_missed_packets(tech_pvt->rtp_session, tech_pvt->max_missed_packets); } @@ -1418,14 +1529,6 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * switch_core_session_rwunlock(b_session); } - //if (!(stream = switch_channel_get_variable(tech_pvt->channel, SWITCH_HOLD_MUSIC_VARIABLE))) { - //stream = tech_pvt->profile->hold_music; - //} - - //if (stream) { - //switch_ivr_stop_displace_session(tech_pvt->session, stream); - //} - switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); switch_channel_presence(tech_pvt->channel, "unknown", "unhold"); } @@ -1444,6 +1547,24 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * ptime = atoi(a->a_value); } else if (!strcasecmp(a->a_name, "crypto") && a->a_value) { crypto = a->a_value; + if (tech_pvt->remote_crypto_key) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Already have a key\n"); + } else { + tech_pvt->remote_crypto_key = switch_core_session_strdup(tech_pvt->session, crypto); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set Remote Key [%s]\n", tech_pvt->remote_crypto_key); + + if (switch_strlen_zero(tech_pvt->local_crypto_key)) { + if (switch_stristr(SWITCH_RTP_CRYPTO_KEY_32, crypto)) { + switch_channel_set_variable(tech_pvt->channel, SOFIA_HAS_CRYPTO_VARIABLE, SWITCH_RTP_CRYPTO_KEY_32); + sofia_glue_build_crypto(tech_pvt, atoi(crypto), AES_CM_128_HMAC_SHA1_32, SWITCH_RTP_CRYPTO_SEND); + } else if (switch_stristr(SWITCH_RTP_CRYPTO_KEY_80, crypto)) { + switch_channel_set_variable(tech_pvt->channel, SOFIA_HAS_CRYPTO_VARIABLE, SWITCH_RTP_CRYPTO_KEY_80); + sofia_glue_build_crypto(tech_pvt, atoi(crypto), AES_CM_128_HMAC_SHA1_80, SWITCH_RTP_CRYPTO_SEND); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Crypto Setup Failed!.\n"); + } + } + } } } @@ -1461,7 +1582,6 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * greed: x = 0; - //xxxxxx if (tech_pvt->rm_encoding) { for (map = m->m_rtpmaps; map; map = map->rm_next) { if (map->rm_pt < 96) { @@ -1486,7 +1606,6 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, sdp_session_t * const char *rm_encoding; if (x++ < skip) { - //printf("skip %s\n", map->rm_encoding); continue; } diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 5c6ce7e64b..7f4d610b2e 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -43,14 +43,16 @@ #undef inline #include #include + + #include "stfu.h" -#define MAX_KEY_LEN 64 + #define rtp_header_len 12 #define RTP_START_PORT 16384 #define RTP_END_PORT 32768 -#define MAX_KEY_LEN 64 #define MASTER_KEY_LEN 30 #define RTP_MAGIC_NUMBER 42 +#define MAX_SRTP_ERRS 10 static switch_port_t START_PORT = RTP_START_PORT; static switch_port_t END_PORT = RTP_END_PORT; @@ -121,14 +123,19 @@ struct switch_rtp { switch_sockaddr_t *local_addr; rtp_msg_t send_msg; - srtp_ctx_t *send_ctx; switch_sockaddr_t *remote_addr; rtp_msg_t recv_msg; - srtp_ctx_t *recv_ctx; + uint32_t autoadj_window; uint32_t autoadj_tally; + srtp_ctx_t *send_ctx; + srtp_ctx_t *recv_ctx; + srtp_policy_t send_policy; + srtp_policy_t recv_policy; + uint32_t srtp_errs; + uint16_t seq; uint32_t ssrc; uint8_t sending_dtmf; @@ -168,6 +175,7 @@ struct switch_rtp { uint32_t max_missed_packets; uint32_t missed_count; rtp_msg_t write_msg; + switch_rtp_crypto_key_t *crypto_keys[SWITCH_RTP_CRYPTO_MAX]; }; static int global_init = 0; @@ -277,6 +285,12 @@ SWITCH_DECLARE(void) switch_rtp_init(switch_memory_pool_t *pool) global_init = 1; } +SWITCH_DECLARE(void) switch_rtp_get_random(void *buf, uint32_t len) +{ + crypto_get_random(buf, len); +} + + SWITCH_DECLARE(void) switch_rtp_shutdown(void) { switch_core_hash_destroy(&alloc_hash); @@ -470,16 +484,89 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_ return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session, + switch_rtp_crypto_direction_t direction, + uint32_t index, + switch_rtp_crypto_key_type_t type, + unsigned char *key, + switch_size_t keylen) +{ + + switch_rtp_crypto_key_t *crypto_key; + srtp_policy_t *policy; + err_status_t stat; + + crypto_key = switch_core_alloc(rtp_session->pool, sizeof(*crypto_key)); + + policy = direction == SWITCH_RTP_CRYPTO_RECV ? &rtp_session->recv_policy : &rtp_session->send_policy; + + + crypto_key->type = type; + crypto_key->index = index; + memcpy(crypto_key->key, key, keylen); + crypto_key->next = rtp_session->crypto_keys[direction]; + rtp_session->crypto_keys[direction] = crypto_key; + + + memset(policy, 0, sizeof(*policy)); + switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_SECURE); + + switch(crypto_key->type) { + case AES_CM_128_HMAC_SHA1_80: + crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy->rtp); + break; + case AES_CM_128_HMAC_SHA1_32: + crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy->rtp); + break; + default: + break; + } + + + policy->next = NULL; + policy->key = (uint8_t *) crypto_key->key; + crypto_policy_set_rtcp_default(&policy->rtcp); + policy->rtcp.sec_serv = sec_serv_none; + + policy->rtp.sec_serv = sec_serv_conf_and_auth; + switch(direction) { + case SWITCH_RTP_CRYPTO_RECV: + policy->ssrc.type = ssrc_any_inbound; + if ((stat = srtp_create(&rtp_session->recv_ctx, policy))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating srtp [%d]\n", stat); + return SWITCH_STATUS_FALSE; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Activating Secure RTP RECV\n"); + break; + case SWITCH_RTP_CRYPTO_SEND: + policy->ssrc.type = ssrc_specific; + policy->ssrc.value = rtp_session->ssrc; + + if ((stat = srtp_create(&rtp_session->send_ctx, policy))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating srtp [%d]\n", stat); + return SWITCH_STATUS_FALSE; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Activating Secure RTP SEND\n"); + break; + default: + abort(); + break; + } + + return SWITCH_STATUS_SUCCESS; + +} + SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session, switch_payload_t payload, uint32_t samples_per_interval, uint32_t ms_per_packet, - switch_rtp_flag_t flags, char *crypto_key, char *timer_name, const char **err, + switch_rtp_flag_t flags, + char *timer_name, + const char **err, switch_memory_pool_t *pool) { switch_rtp_t *rtp_session = NULL; - srtp_policy_t policy; - char key[MAX_KEY_LEN]; uint32_t ssrc = rand() & 0xffff; *new_rtp_session = NULL; @@ -507,44 +594,10 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session /* for from address on recvfrom calls */ switch_sockaddr_info_get(&rtp_session->from_addr, NULL, SWITCH_UNSPEC, 0, 0, pool); - memset(&policy, 0, sizeof(policy)); - if (crypto_key) { - int len; - - switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_SECURE); - crypto_policy_set_rtp_default(&policy.rtp); - crypto_policy_set_rtcp_default(&policy.rtcp); - policy.ssrc.type = ssrc_any_inbound; - policy.ssrc.value = ssrc; - policy.key = (uint8_t *) key; - policy.next = NULL; - policy.rtp.sec_serv = sec_serv_conf_and_auth; - policy.rtcp.sec_serv = sec_serv_none; - - /* read key from hexadecimal on command line into an octet string */ - len = hex_string_to_octet_string(key, crypto_key, MASTER_KEY_LEN * 2); - - /* check that hex string is the right length */ - if (len < MASTER_KEY_LEN * 2) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "error: too few digits in key/salt " "(should be %d hexadecimal digits, found %d)\n", MASTER_KEY_LEN * 2, len); - *err = "Crypt Error"; - return SWITCH_STATUS_FALSE; - } - if (strlen(crypto_key) > MASTER_KEY_LEN * 2) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "error: too many digits in key/salt " - "(should be %d hexadecimal digits, found %u)\n", MASTER_KEY_LEN * 2, (unsigned) strlen(crypto_key)); - *err = "Crypt Error"; - return SWITCH_STATUS_FALSE; - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Activating Secure RTP!\n"); - } rtp_session->seq = (uint16_t) rand(); rtp_session->ssrc = ssrc; - rtp_session->send_msg.header.ssrc = htonl(ssrc); + rtp_session->send_msg.header.ssrc = htonl(rtp_session->ssrc); rtp_session->send_msg.header.ts = 0; rtp_session->send_msg.header.m = 0; rtp_session->send_msg.header.pt = (switch_payload_t) htonl(payload); @@ -553,7 +606,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session rtp_session->send_msg.header.x = 0; rtp_session->send_msg.header.cc = 0; - rtp_session->recv_msg.header.ssrc = htonl(ssrc); + rtp_session->recv_msg.header.ssrc = 0; rtp_session->recv_msg.header.ts = 0; rtp_session->recv_msg.header.seq = 0; rtp_session->recv_msg.header.m = 0; @@ -568,20 +621,6 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session rtp_session->samples_per_interval = rtp_session->conf_samples_per_interval = samples_per_interval; rtp_session->timer_name = switch_core_strdup(pool, timer_name); - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE)) { - err_status_t stat; - - if ((stat = srtp_create(&rtp_session->recv_ctx, &policy))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating srtp [%d]\n", stat); - *err = "Crypt Error"; - return SWITCH_STATUS_FALSE; - } - if ((stat = srtp_create(&rtp_session->send_ctx, &policy))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error allocating srtp [%d]\n", stat); - *err = "Crypt Error"; - return SWITCH_STATUS_FALSE; - } - } if (!switch_strlen_zero(timer_name)) { switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_USE_TIMER); @@ -615,11 +654,14 @@ SWITCH_DECLARE(switch_rtp_t *) switch_rtp_new(const char *rx_host, switch_payload_t payload, uint32_t samples_per_interval, uint32_t ms_per_packet, - switch_rtp_flag_t flags, char *crypto_key, char *timer_name, const char **err, switch_memory_pool_t *pool) + switch_rtp_flag_t flags, + char *timer_name, + const char **err, + switch_memory_pool_t *pool) { switch_rtp_t *rtp_session = NULL; - if (switch_rtp_create(&rtp_session, payload, samples_per_interval, ms_per_packet, flags, crypto_key, timer_name, err, pool) != SWITCH_STATUS_SUCCESS) { + if (switch_rtp_create(&rtp_session, payload, samples_per_interval, ms_per_packet, flags, timer_name, err, pool) != SWITCH_STATUS_SUCCESS) { goto end; } @@ -928,6 +970,29 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ return -1; } + if (bytes && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE)) { + int sbytes = (int) bytes; + err_status_t stat; + + + stat = srtp_unprotect(rtp_session->recv_ctx, &rtp_session->recv_msg.header, &sbytes); + if (stat && rtp_session->recv_msg.header.pt != rtp_session->te && rtp_session->recv_msg.header.pt != rtp_session->cng_pt) { + if (++rtp_session->srtp_errs >= MAX_SRTP_ERRS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "error: srtp unprotection failed with code %d%s\n", stat, + stat == err_status_replay_fail ? " (replay check failed)" : stat == err_status_auth_fail ? " (auth check failed)" : ""); + return -1; + } else { + sbytes = 0; + } + } else { + rtp_session->srtp_errs = 0; + } + + bytes = sbytes; + + } + if (rtp_session->jb && bytes && rtp_session->recv_msg.header.pt == rtp_session->payload) { if (rtp_session->recv_msg.header.m) { stfu_n_reset(rtp_session->jb); @@ -1028,20 +1093,6 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ return 2 + rtp_header_len; } - if (bytes && switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE)) { - int sbytes = (int) bytes; - err_status_t stat; - - stat = srtp_unprotect(rtp_session->recv_ctx, &rtp_session->recv_msg.header, &sbytes); - if (stat) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "error: srtp unprotection failed with code %d%s\n", stat, - stat == err_status_replay_fail ? " (replay check failed)" : stat == err_status_auth_fail ? " (auth check failed)" : ""); - return -1; - } - bytes = sbytes; - } - if (bytes > 0) { rtp_session->missed_count = 0; } @@ -1356,7 +1407,6 @@ static int rtp_common_write(switch_rtp_t *rtp_session, if (flags && *flags & SFF_RFC2833) { send_msg->header.pt = rtp_session->te; } - send_msg->header.ssrc = htonl(rtp_session->ssrc); } else { uint8_t m = 0; @@ -1392,17 +1442,7 @@ static int rtp_common_write(switch_rtp_t *rtp_session, bytes = datalen + rtp_header_len; } - if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE)) { - int sbytes = (int) bytes; - err_status_t stat; - - stat = srtp_protect(rtp_session->send_ctx, &send_msg->header, &sbytes); - if (stat) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error: srtp protection failed with code %d\n", stat); - } - - bytes = sbytes; - } + send_msg->header.ssrc = htonl(rtp_session->ssrc); if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_GOOGLEHACK) && rtp_session->send_msg.header.pt == 97) { rtp_session->recv_msg.header.pt = 102; @@ -1517,6 +1557,19 @@ static int rtp_common_write(switch_rtp_t *rtp_session, if (send) { send_msg->header.seq = htons(++rtp_session->seq); + + if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_SECURE)) { + int sbytes = (int) bytes; + err_status_t stat; + + stat = srtp_protect(rtp_session->send_ctx, &send_msg->header, &sbytes); + if (stat) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "error: srtp protection failed with code %d\n", stat); + } + + bytes = sbytes; + } + if (switch_socket_sendto(rtp_session->sock, rtp_session->remote_addr, 0, (void *) send_msg, &bytes) != SWITCH_STATUS_SUCCESS) { rtp_session->seq--; return -1; diff --git a/src/switch_utils.c b/src/switch_utils.c index 7ffc08dac0..a33214e1b0 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -166,7 +166,7 @@ SWITCH_DECLARE(switch_status_t) switch_b64_encode(unsigned char *in, switch_size } -SWITCH_DECLARE(switch_status_t) switch_b64_decode(char *in, char *out, switch_size_t olen) +SWITCH_DECLARE(switch_size_t) switch_b64_decode(char *in, char *out, switch_size_t olen) { char l64[256]; @@ -203,7 +203,7 @@ SWITCH_DECLARE(switch_status_t) switch_b64_decode(char *in, char *out, switch_si op[ol++] = '\0'; - return SWITCH_STATUS_SUCCESS; + return ol; }