From 8268b9a6d418d725327188e058b65e4aa9e2c427 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 21 Aug 2014 05:58:36 +0000 Subject: Close #1735: Implemented secure dialog check of "sips" scheme in Contact/Record-Route header in incoming INVITE/UPDATE requests & responses. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4899 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsip/sip_config.h | 12 ++ pjsip/src/pjsip-ua/sip_inv.c | 241 +++++++++++++++++++++++++++++++++++---- pjsip/src/pjsip/sip_config.c | 3 +- 3 files changed, 230 insertions(+), 26 deletions(-) (limited to 'pjsip') diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h index 27575c8c..f787dc3d 100644 --- a/pjsip/include/pjsip/sip_config.h +++ b/pjsip/include/pjsip/sip_config.h @@ -145,6 +145,18 @@ typedef struct pjsip_cfg_t */ pj_bool_t resolve_hostname_to_get_interface; + /** + * Disable security check on incoming messages in a secure dialog. + * A secure dialog is created when the request that creates the dialog + * uses "sips" scheme in its request URI. Contact URI should use "sips" + * scheme and the top-most Record-Route URI, if any, should use either + * "sips" scheme or "transport=tls" param. See also + * https://trac.pjsip.org/repos/ticket/1735. + * + * Default is PJ_FALSE. + */ + pj_bool_t disable_secure_dlg_check; + } endpt; /** Transaction layer settings. */ diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index 2d411e8c..7252c7aa 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -115,6 +115,9 @@ static pj_status_t handle_timer_response(pjsip_inv_session *inv, const pjsip_rx_data *rdata, pj_bool_t end_sess_on_failure); +static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv, + pjsip_event *e); + static void (*inv_state_handler[])( pjsip_inv_session *inv, pjsip_event *e) = { &inv_on_state_null, @@ -992,6 +995,49 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request3(pjsip_rx_data *rdata, goto on_return; } + /* Ticket #1735: Check Contact/Record-Route header in a secure dialog. */ + if (pjsip_cfg()->endpt.disable_secure_dlg_check == PJ_FALSE && + msg && c_hdr && c_hdr->uri) + { + /* Check Contact header */ + if (!PJSIP_URI_SCHEME_IS_SIPS(c_hdr->uri)) + status = PJSIP_ESESSIONINSECURE; + + /* Check top Record-Route header */ + if (status == PJ_SUCCESS) { + pjsip_rr_hdr *r = (pjsip_rr_hdr*) + pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, + NULL); + if (r && !PJSIP_URI_SCHEME_IS_SIPS(&r->name_addr)) { + /* Not "sips", check if it is "sip" and has param + * "transport=tls". + */ + if (PJSIP_URI_SCHEME_IS_SIP(&r->name_addr)) { + pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) + pjsip_uri_get_uri(r->name_addr.uri); + if (pj_stricmp2(&sip_uri->transport_param, "tls")!=0) + status = PJSIP_ESESSIONINSECURE; + } else { + /* Not "sips" nor "sip", treat it as insecure? */ + status = PJSIP_ESESSIONINSECURE; + } + } + } + + if (status != PJ_SUCCESS) { + pjsip_warning_hdr *w; + pj_str_t warn_text = pj_str("SIPS Required"); + w = pjsip_warning_hdr_create(tmp_pool, 381, + pjsip_endpt_name(endpt), + &warn_text); + if (w) { + pj_list_push_back(&res_hdr_list, w); + } + code = PJSIP_SC_TEMPORARILY_UNAVAILABLE; + goto on_return; + } + } + /* Check the request body, see if it's something that we support, * only when the body hasn't been parsed before. */ @@ -3224,13 +3270,22 @@ static void inv_handle_bye_response( pjsip_inv_session *inv, * Respond to incoming UPDATE request. */ static void inv_respond_incoming_update(pjsip_inv_session *inv, - pjsip_rx_data *rdata) + pjsip_event *e) { pjmedia_sdp_neg_state neg_state; pj_status_t status; pjsip_tx_data *tdata = NULL; + pjsip_rx_data *rdata; pjsip_status_code st_code; + pj_assert(e->type == PJSIP_EVENT_TSX_STATE && + e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); + rdata = e->body.tsx_state.src.rdata; + + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + return; + /* Invoke Session Timers module */ status = pjsip_timer_process_req(inv, rdata, &st_code); if (status != PJ_SUCCESS) { @@ -3385,21 +3440,27 @@ static pj_bool_t inv_handle_update_response( pjsip_inv_session *inv, tsx->status_code/100 == 2) { pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; - status = handle_timer_response(inv, rdata, PJ_FALSE); - if (rdata->msg_info.msg->body) { - /* Only process remote SDP if we have sent local offer */ - if (inv->neg && pjmedia_sdp_neg_get_state(inv->neg) == - PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) - { - status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata); - } else { - PJ_LOG(5,(THIS_FILE, "Ignored message body in %s as no local " - "offer was sent", - pjsip_rx_data_get_info(rdata))); + /* Check routing URI scheme for secure dialog */ + if (inv_check_secure_dlg(inv, e)) { + + status = handle_timer_response(inv, rdata, PJ_FALSE); + + if (rdata->msg_info.msg->body) { + /* Only process remote SDP if we have sent local offer */ + if (inv->neg && pjmedia_sdp_neg_get_state(inv->neg) == + PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) + { + status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata); + } else { + PJ_LOG(5,(THIS_FILE, "Ignored message body in %s as no " + "local offer was sent", + pjsip_rx_data_get_info(rdata))); + } } } - handled = PJ_TRUE; + + handled = PJ_TRUE; } /* Process 502/503 error */ @@ -3516,6 +3577,114 @@ static void inv_respond_incoming_prack(pjsip_inv_session *inv, } +/* Ticket #1735: If this is a secure dialog, make sure that any incoming + * initial/subsequent INVITE/UPDATE request or the 2xx response to INVITE/ + * UPDATE specifies secure Contact and Record-Route headers. + */ +static pj_bool_t inv_check_secure_dlg(pjsip_inv_session *inv, + pjsip_event *e) +{ + pjsip_transaction *tsx = e->body.tsx_state.tsx; + pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); + + if (pjsip_cfg()->endpt.disable_secure_dlg_check == PJ_FALSE && + dlg->secure && e->body.tsx_state.type==PJSIP_EVENT_RX_MSG && + (tsx->role==PJSIP_ROLE_UAC && tsx->status_code/100 == 2 || + tsx->role==PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING) && + (tsx->method.id==PJSIP_INVITE_METHOD || + pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0)) + { + const pjsip_msg *msg = e->body.tsx_state.src.rdata->msg_info.msg; + pj_status_t status = PJ_SUCCESS; + pjsip_contact_hdr *c; + + /* Check Contact header */ + c = (pjsip_contact_hdr*) + pjsip_msg_find_hdr(msg,PJSIP_H_CONTACT, NULL); + if (!(c && c->uri && PJSIP_URI_SCHEME_IS_SIPS(c->uri))) + status = PJSIP_ESESSIONINSECURE; + + /* Check top Record-Route header */ + if (status == PJ_SUCCESS) { + pjsip_rr_hdr *r = (pjsip_rr_hdr*) + pjsip_msg_find_hdr(msg, PJSIP_H_RECORD_ROUTE, + NULL); + if (r && !PJSIP_URI_SCHEME_IS_SIPS(&r->name_addr)) { + /* Not "sips", check if it is "sip" and has param + * "transport=tls". + */ + if (PJSIP_URI_SCHEME_IS_SIP(&r->name_addr)) { + pjsip_sip_uri *sip_uri = (pjsip_sip_uri*) + pjsip_uri_get_uri(r->name_addr.uri); + if (pj_stricmp2(&sip_uri->transport_param, "tls")!=0) + status = PJSIP_ESESSIONINSECURE; + } else { + /* Not "sips" nor "sip", treat it as insecure? */ + status = PJSIP_ESESSIONINSECURE; + } + } + } + + if (status == PJSIP_ESESSIONINSECURE) { + /* Found non-SIPS scheme in Contact/Record-Route header */ + + pj_str_t warn_text = pj_str("SIPS Required"); + + if (tsx->role == PJSIP_ROLE_UAC) { + + /* If we are UAC, terminate the session */ + pjsip_tx_data *bye; + + PJ_LOG(4,(inv->obj_name, + "Secure dialog requires SIPS scheme in Contact and " + "Record-Route headers, ending the session")); + + status = pjsip_inv_end_session(inv, 480, NULL, &bye); + if (status == PJ_SUCCESS && bye) { + pjsip_warning_hdr *w; + w = pjsip_warning_hdr_create(bye->pool, 381, + pjsip_endpt_name(dlg->endpt), + &warn_text); + if (w) + pjsip_msg_add_hdr(bye->msg, (pjsip_hdr*)w); + + status = pjsip_inv_send_msg(inv, bye); + } + + } else { + + /* If we are UAS, reject the request */ + pjsip_rx_data *rdata = e->body.tsx_state.src.rdata; + pjsip_tx_data *tdata; + + PJ_LOG(4,(inv->obj_name, + "Secure dialog requires SIPS scheme in Contact and " + "Route headers, rejecting the request")); + + status = pjsip_dlg_create_response(inv->dlg, rdata, 480, + NULL, &tdata); + if (status == PJ_SUCCESS) { + pjsip_warning_hdr *w; + w = pjsip_warning_hdr_create(tdata->pool, 381, + pjsip_endpt_name(dlg->endpt), + &warn_text); + if (w) + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)w); + + pjsip_dlg_send_response(dlg, tsx, tdata); + } + + } + + return PJ_FALSE; + } + } + + return PJ_TRUE; +} + + + /* * State NULL is before anything is sent/received. */ @@ -3865,6 +4034,12 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) */ pj_assert(0); + inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); + + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + break; + /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, @@ -3872,8 +4047,6 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) if (status != PJ_SUCCESS) break; - inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); - inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); @@ -3889,6 +4062,14 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) */ if (tsx->status_code/100 == 2) { /* This must be receipt of 2xx response */ + pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); + + /* Set state to CONNECTING */ + inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); + + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + break; /* Process session timer response. */ status = handle_timer_response(inv, @@ -3897,15 +4078,9 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) if (status != PJ_SUCCESS) break; - /* Set state to CONNECTING */ - inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e); - inv_check_sdp_in_incoming_msg(inv, tsx, e->body.tsx_state.src.rdata); - /* Send ACK */ - pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG); - inv_send_ack(inv, e); } else { @@ -3942,7 +4117,7 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e) /* * Handle a very early UPDATE */ - inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); + inv_respond_incoming_update(inv, e); } @@ -4060,6 +4235,10 @@ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e) if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pj_status_t status; + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + break; + /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, @@ -4100,6 +4279,10 @@ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e) if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { pj_status_t status; + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + break; + /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, @@ -4149,7 +4332,7 @@ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e) /* * Handle incoming UPDATE */ - inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); + inv_respond_incoming_update(inv, e); } else if (tsx->role == PJSIP_ROLE_UAC && @@ -4357,7 +4540,7 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e) /* * Handle incoming UPDATE */ - inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); + inv_respond_incoming_update(inv, e); } else if (tsx->role == PJSIP_ROLE_UAC && @@ -4487,6 +4670,10 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) return; } + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + return; + /* Save the invite transaction. */ inv->invite_tsx = tsx; @@ -4729,6 +4916,10 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) /* Re-INVITE was accepted. */ + /* Check routing URI scheme for secure dialog */ + if (!inv_check_secure_dlg(inv, e)) + return; + /* Process session timer response. */ status = handle_timer_response(inv, e->body.tsx_state.src.rdata, @@ -4787,7 +4978,7 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) /* * Handle incoming UPDATE */ - inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); + inv_respond_incoming_update(inv, e); } else if (tsx->role == PJSIP_ROLE_UAC && (tsx->state == PJSIP_TSX_STATE_COMPLETED || diff --git a/pjsip/src/pjsip/sip_config.c b/pjsip/src/pjsip/sip_config.c index 3897a054..59748760 100644 --- a/pjsip/src/pjsip/sip_config.c +++ b/pjsip/src/pjsip/sip_config.c @@ -33,7 +33,8 @@ pjsip_cfg_t pjsip_sip_cfg_var = PJSIP_DONT_SWITCH_TO_TLS, PJSIP_FOLLOW_EARLY_MEDIA_FORK, PJSIP_REQ_HAS_VIA_ALIAS, - PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE + PJSIP_RESOLVE_HOSTNAME_TO_GET_INTERFACE, + 0 }, /* Transaction settings */ -- cgit v1.2.3