diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h index 8408f0ae86..ebb8048f50 100644 --- a/pjmedia/include/pjmedia/config.h +++ b/pjmedia/include/pjmedia/config.h @@ -1100,6 +1100,16 @@ # define PJMEDIA_SRTP_DTLS_OSSL_CIPHERS "DEFAULT" #endif +/** + * Enabled this to check the source address of ClientHello message coming + * from a valid address. See PJ_ICE_SESS_CHECK_SRC_ADDR when ICE is used. + * + * Default value: 0 + */ +#ifndef PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR +# define PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR 0 +#endif + /** * Maximum number of SRTP cryptos. diff --git a/pjmedia/src/pjmedia/transport_srtp_dtls.c b/pjmedia/src/pjmedia/transport_srtp_dtls.c index f57ba1bc57..4b9ba78e14 100644 --- a/pjmedia/src/pjmedia/transport_srtp_dtls.c +++ b/pjmedia/src/pjmedia/transport_srtp_dtls.c @@ -1178,6 +1178,19 @@ static pj_status_t get_rem_addrs(dtls_srtp *ds, return PJ_SUCCESS; } +static pj_bool_t is_valid_src_addr(dtls_srtp *ds, unsigned idx, + pj_sockaddr *src_addr) +{ + pj_sockaddr *rem_addr; + + if (idx == RTP_CHANNEL) { + rem_addr = &ds->rem_addr; + } else { + rem_addr = &ds->rem_rtcp; + } + return (pj_sockaddr_cmp(rem_addr, src_addr) == 0); +} + /* Check if an incoming packet is a DTLS packet (rfc5764 section 5.1.2) */ #define IS_DTLS_PKT(pkt, pkt_len) (*(char*)pkt > 19 && *(char*)pkt < 64) @@ -1361,6 +1374,50 @@ static pj_status_t dtls_on_recv(pjmedia_transport *tp, unsigned idx, (ds->setup == DTLS_SETUP_ACTPASS || ds->setup == DTLS_SETUP_PASSIVE)) { pj_status_t status; + +#if defined(PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR) && \ + PJMEDIA_SRTP_DTLS_CHECK_HELLO_ADDR==1 + + /* Check the souce address with the specified remote address from + * the SDP. When ICE is used, the source address checking will be + * done in ICE session. + */ + if (!ds->use_ice) { + pjmedia_transport_info info; + pj_sockaddr src_addr; + pj_bool_t src_addr_avail = PJ_TRUE; + + pjmedia_transport_get_info(ds->srtp->member_tp, &info); + if (idx == RTP_CHANNEL) { + if (!pj_sockaddr_has_addr(&info.src_rtp_name)) { + src_addr_avail = PJ_FALSE; + } else { + pj_sockaddr_cp(&src_addr, &info.src_rtp_name); + } + } else { + if (!pj_sockaddr_has_addr(&info.src_rtcp_name)) { + src_addr_avail = PJ_FALSE; + } else { + pj_sockaddr_cp(&src_addr, &info.src_rtcp_name); + } + } + + if (!src_addr_avail || !is_valid_src_addr(ds, idx, &src_addr)) { + char psrc_addr[PJ_INET6_ADDRSTRLEN] = "Unknown"; + + if (src_addr_avail) { + pj_sockaddr_print(&src_addr, psrc_addr, + sizeof(psrc_addr), 3); + } + PJ_LOG(2, (ds->base.name, "DTLS-SRTP %s ignoring %lu bytes, " + "from unrecognized src addr [%s]", CHANNEL_TO_STRING(idx), + (unsigned long)size, psrc_addr)); + + DTLS_UNLOCK(ds); + return PJ_SUCCESS; + } + } +#endif ds->setup = DTLS_SETUP_PASSIVE; status = ssl_handshake_channel(ds, idx); if (status != PJ_SUCCESS) { diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h index e904c3ac48..d586b9ab71 100644 --- a/pjnath/include/pjnath/config.h +++ b/pjnath/include/pjnath/config.h @@ -394,6 +394,16 @@ # define PJ_ICE_NOMINATED_CHECK_DELAY (4*PJ_STUN_RTO_VALUE) #endif + /** + * Specify whether to check the source address of the incoming messages. + * The source address will be compared to the remote candidate which has + * a completed connectivity check or received a connectivity check. + * + * Defalut: 1 (yes) + */ +#ifndef PJ_ICE_SESS_CHECK_SRC_ADDR +# define PJ_ICE_SESS_CHECK_SRC_ADDR 1 +#endif /** * Minimum interval value to be used for sending STUN keep-alive on the ICE diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index e796b2539a..db0c89c9f3 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -201,6 +201,12 @@ typedef struct pj_ice_sess_comp */ pj_stun_session *stun_sess; + /** + * The remote candidate checked address. This is expected address that + * the remote going to use. + */ + pj_sockaddr rcand_check_addr; + } pj_ice_sess_comp; @@ -317,6 +323,13 @@ struct pj_ice_sess_cand */ pj_sockaddr rel_addr; + /** + * Indicate that remote connectivity check has been received or the check + * has been successful for this candidate. It is applicable for + * remote candidate only. + * + */ + pj_bool_t checked; }; @@ -672,6 +685,15 @@ typedef struct pj_ice_sess_options */ pj_ice_sess_trickle trickle; + /** + * Specify whether to check the source address of the incoming messages. + * The source address will be compared to the remote candidate which has + * a completed connectivity check or received a connectivity check. + * + * Default value is PJ_ICE_SESS_CHECK_SRC_ADDR. + */ + pj_bool_t check_src_addr; + } pj_ice_sess_options; diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 8afe4d181c..468e4be430 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -325,6 +325,7 @@ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt) opt->controlled_agent_want_nom_timeout = ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; opt->trickle = PJ_ICE_SESS_TRICKLE_DISABLED; + opt->check_src_addr = PJ_ICE_SESS_CHECK_SRC_ADDR; } /* @@ -2883,6 +2884,8 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, &ice->clist, check), (check->nominated ? " (nominated)" : " (not nominated)"))); + check->rcand->checked = PJ_TRUE; + /* Get the STUN XOR-MAPPED-ADDRESS attribute. */ xaddr = (pj_stun_xor_mapped_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0); @@ -3141,7 +3144,6 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, uc_attr = (pj_stun_use_candidate_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USE_CANDIDATE, 0); - /* Get ICE-CONTROLLING or ICE-CONTROLLED */ role_attr = (pj_stun_uint64_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLING, 0); @@ -3447,6 +3449,8 @@ static void handle_incoming_check(pj_ice_sess *ice, */ c->nominated = ((rcheck->use_candidate) || c->nominated); + rcand->checked = PJ_TRUE; + if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN || c->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { @@ -3734,6 +3738,46 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, PJ_RACE_ME(5); + if (ice->opt.check_src_addr) { + pj_bool_t check_addr = PJ_TRUE; + pj_sockaddr *raddr = &comp->rcand_check_addr; + char psrc_addr[PJ_INET6_ADDRSTRLEN] = {0}; + + if (pj_sockaddr_has_addr(src_addr)) { + pj_sockaddr_print(src_addr, psrc_addr, sizeof(psrc_addr), 3); + } + + if (!pj_sockaddr_has_addr(raddr)) { + for (i = 0; i < ice->rcand_cnt; ++i) { + if (ice->rcand[i].comp_id == comp_id && + ice->rcand[i].checked && + pj_sockaddr_cmp(src_addr, &ice->rcand[i].addr) == 0) + { + /* Before ICE completed, it is still allowed to receive + * data from other candidates. + */ + if (ice->is_complete) { + pj_sockaddr_cp(raddr, src_addr); + PJ_LOG(4, (ice->obj_name, "Using [%s] as valid" + " address for component [%d]", + psrc_addr, comp_id)); + } + check_addr = PJ_FALSE; + break; + } + } + } + if (check_addr && + (!pj_sockaddr_has_addr(raddr) || + pj_sockaddr_cmp(src_addr, raddr) != 0)) + { + PJ_LOG(4, (ice->obj_name, "Ignoring incoming message for " + "component [%d] because source addr [%s] unrecognized", + comp_id, psrc_addr)); + return PJ_SUCCESS; + } + } + (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, src_addr, src_addr_len); status = PJ_SUCCESS;