Hello!
I wrote some code, which adds basic media encryption support to be used with
Deutsche Telekom. The attached patch is based on Asterisk 16.3
and works for me :-) - not fully tested yet. If you want to use it, you have to
enable media_encryption=sdes for the extension (and
transport tls and tls1.2). Use at your own risk!
The current patch lacks a basic mediasec option, which prevents adding the
mediasec headers to each *initial* REGISTER or to each INVITE (if
sdes is activated). As of today, I don't know how to solve this problem without
too much changes.
Anyway: It looks like the additional HEADERs seem not to disrupt other ISPs
(tested with one other ISP). This option should be accessible in
rtp, session and register environment. Maybe there is a possibility to exchange
data between register, session and rtp environment. This way, it
would be possible to dynamically set mediasec in session and rtp based on the
result of the initial register. It would be necessary at the
same time, to dynamically disable sdes encryption if activation of mediasec
didn't succeed.
One more open point is the check for the 3 headers using the same name
(Security-Server and Security-Verify). How can they be checked
regarding order? Is there a function to get each value of the same header?
Maybe based on an array index? This way it would be possible to
create
the Security-Verify headers dynamically based on the 494 or 401 response.
Another yet missing point is the qualify OPTIONS package. I'm not sure where to
add the mediasec headers exactly (which function?). At the
moment, the Response after OPTION request is (if already registered):
SIP/2.0 494 Security Agreement Required
CSeq: 21671 OPTIONS
Security-Server: msrp-tls;mediasec
Security-Server: sdes-srtp;mediasec
Security-Server: dtls-srtp;mediasec
If you are not already registered, you get a 403 Forbidden.
The UPDATE package (used as a watchdog circuit during a call each 15 minutes)
seems not to be affected - I couldn't find any problem at this
point.
ReINVITEs seem to work fine, too.
What exactly does this patch? Below are the enhancements compared to a "normal"
REGISTER or INVITE and the involved function in asterisk.
------------------------------------------
*Initial* REGISTER
==================
Request
REGISTER
Security-Client: sdes-srtp;mediasec
Proxy-Require: mediasec
Require: mediasec
asterisk: handle_client_registration()
Response
401 Unauthorized
Security-Server: msrp-tls;mediasec
Security-Server: sdes-srtp;mediasec
Security-Server: dtls-srtp;mediasec
WWW-Authenticate: ...
Request
REGISTER
Security-Client: sdes-srtp;mediasec
Proxy-Require: mediasec
Require: mediasec
Authorization: ...
Security-Verify: msrp-tls;mediasec
Security-Verify: sdes-srtp;mediasec
Security-Verify: dtls-srtp;mediasec
(the last 3 headers must have this order)
asterisk: handle_registration_response() -> handle_client_registration()
Response
200 OK
------------------------------------------
------------------------------------------
ReREGISTER (variant 1)
======================
Request
REGISTER
(no additions)
asterisk: handle_client_registration()
Response
200 OK
------------------------------------------
------------------------------------------
ReREGISTER (variant 2)
======================
Request
REGISTER
(no additions)
asterisk: handle_registration_response()
Response
494 Security Agreement Required
Security-Server: msrp-tls;mediasec
Security-Server: sdes-srtp;mediasec
Security-Server: dtls-srtp;mediasec
Request
REGISTER
Security-Verify: msrp-tls;mediasec
Security-Verify: sdes-srtp;mediasec
Security-Verify: dtls-srtp;mediasec
(the 3 headers must have this order)
asterisk: handle_registration_response() -> handle_client_registration()
Response
401 Unauthorized
Security-Server: msrp-tls;mediasec
Security-Server: sdes-srtp;mediasec
Security-Server: dtls-srtp;mediasec
WWW-Authenticate: ...
Request
REGISTER
Security-Verify: msrp-tls;mediasec
Security-Verify: sdes-srtp;mediasec
Security-Verify: dtls-srtp;mediasec
(the 3 headers must have this order)
Authorization: ...
asterisk: handle_registration_response()
Response
200 OK
-------------------------------------------
An outbound call (INVITE) is done like this:
-------------------------------------------
INVITE
Security-Verify: msrp-tls;mediasec
Security-Verify: sdes-srtp;mediasec
Security-Verify: dtls-srtp;mediasec
(the 3 headers must have this order)
asterisk: ast_sip_session_create_invite()
SDP
a=3ge2ae:requested
asterisk: add_crypto_to_stream()
-------------------------------------------
Thanks
Michael
diff -urN asterisk-16.3.0.orig/res/res_pjsip_outbound_registration.c asterisk-16.3.0/res/res_pjsip_outbound_registration.c
--- asterisk-16.3.0.orig/res/res_pjsip_outbound_registration.c 2019-04-04 16:49:57.000000000 +0200
+++ asterisk-16.3.0/res/res_pjsip_outbound_registration.c 2019-05-29 17:28:18.061000000 +0200
@@ -361,6 +361,10 @@
char *transport_name;
/*! \brief The name of the registration sorcery object */
char *registration_name;
+ /*! \brief Indicator if it's the first Register in a call. Hast to be set to 0 if the OK response has been seen. Has to be set to 1 on unregister and initial try. */
+ unsigned int initial_reg;
+ /*! \brief Indicator, if there was a 494 response before */
+ unsigned int is494;
};
/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -597,6 +601,23 @@
pj_strassign(&hdr->values[hdr->count++], &PATH_NAME);
}
+ /* todo: check for config variable */
+ /* Add some header for mediasec */
+ /* only, if it's the first time - SIP_REGISTRATION_REJECTED_TEMPORARY could be initial, too (retry e.g.) - that's why there is an additional initial_reg */
+ if (client_state->status == SIP_REGISTRATION_UNREGISTERED || client_state->initial_reg) {
+ client_state->initial_reg = 1;
+ ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec");
+ ast_sip_add_header(tdata,"Proxy-Require","mediasec");
+ ast_sip_add_header(tdata,"Require","mediasec");
+ }
+
+ /* answer for 494 */
+ if (client_state->is494) {
+ ast_sip_add_header(tdata,"Security-Verify","msrp-tls;mediasec");
+ ast_sip_add_header(tdata,"Security-Verify","sdes-srtp;mediasec");
+ ast_sip_add_header(tdata,"Security-Verify","dtls-srtp;mediasec");
+ }
+
registration_client_send(client_state, tdata);
return 0;
@@ -918,6 +939,32 @@
ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
server_uri, client_uri);
pjsip_tx_data_add_ref(tdata);
+
+ /* Add MEDIASEC headers */
+ static const pj_str_t headerName = { "Security-Server", 15 };
+ pjsip_generic_string_hdr *secSrv;
+ secSrv = pjsip_msg_find_hdr_by_name(response->rdata->msg_info.msg, &headerName, NULL);
+ if (secSrv) {
+ response->client_state->is494=0;
+ /* Not needed - they are already there from initial REGISTER
+ ast_sip_add_header(tdata,"Security-Client","sdes-srtp;mediasec");
+ ast_sip_add_header(tdata,"Proxy-Require","mediasec");
+ ast_sip_add_header(tdata,"Require","mediasec"); */
+
+ static const pj_str_t headerNameVrfy = { "Security-Verify", 15 };
+ pjsip_generic_string_hdr *secVrfy;
+ secVrfy = pjsip_msg_find_hdr_by_name(tdata->msg, &headerNameVrfy, NULL);
+
+ /* This happens, if 494 was the reason for the 401 - because the Re-REGISTER already contained it */
+ /* but the original Register didn't contain it - therefore we have to check for the originating Register */
+ if (! secVrfy) {
+ ast_verb(3, "Adding MEDIASEC headers\n");
+ ast_sip_add_header(tdata,"Security-Verify","msrp-tls;mediasec");
+ ast_sip_add_header(tdata,"Security-Verify","sdes-srtp;mediasec");
+ ast_sip_add_header(tdata,"Security-Verify","dtls-srtp;mediasec");
+ }
+ }
+
res = registration_client_send(response->client_state, tdata);
/* Save the cseq that actually got sent. */
@@ -943,6 +990,10 @@
if (response->expiration) {
int next_registration_round;
+ /* following Registers aren't initial-Registers any more */
+ response->client_state->initial_reg=0;
+ response->client_state->is494=0;
+
/* If the registration went fine simply reschedule registration for the future */
ast_debug(1, "Outbound registration to '%s' with client '%s' successful\n", server_uri, client_uri);
update_client_state_status(response->client_state, SIP_REGISTRATION_REGISTERED);
@@ -959,6 +1010,10 @@
response->client_state->registration_name);
} else {
ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
+ /* following Registers are initial-Registers */
+ response->client_state->initial_reg=1;
+ response->client_state->is494=0;
+
update_client_state_status(response->client_state, SIP_REGISTRATION_UNREGISTERED);
ast_sip_transport_monitor_unregister(response->rdata->tp_info.transport,
registration_transport_shutdown_cb, response->client_state->registration_name,
@@ -966,6 +1021,20 @@
}
} else if (response->client_state->destroy) {
/* We need to deal with the pending destruction instead. */
+ } else if (response->code == 494) {
+ if (response->client_state->is494) {
+ ast_log(LOG_WARNING, "MEDIASEC registration to '%s' with client '%s' failed (494-loop detected), stopping registration attempt\n",
+ server_uri, client_uri);
+ /* 494 loop detected! This is fatal! */
+ update_client_state_status(response->client_state, SIP_REGISTRATION_REJECTED_PERMANENT);
+ /* reset is494 */
+ response->client_state->is494=0;
+ } else {
+ /* Try (initial) registration again - but now with additional headers */
+ response->client_state->is494=1;
+ handle_client_registration(response->client_state);
+ return 0;
+ }
} else if (response->retry_after) {
/* If we have been instructed to retry after a period of time, schedule it as such */
schedule_retry(response, response->retry_after, server_uri, client_uri);
diff -urN asterisk-16.3.0.orig/res/res_pjsip_sdp_rtp.c asterisk-16.3.0/res/res_pjsip_sdp_rtp.c
--- asterisk-16.3.0.orig/res/res_pjsip_sdp_rtp.c 2019-04-04 16:49:57.000000000 +0200
+++ asterisk-16.3.0/res/res_pjsip_sdp_rtp.c 2019-05-27 04:13:23.769000000 +0200
@@ -1418,6 +1418,7 @@
static const pj_str_t STR_PASSIVE = { "passive", 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_MEDSECREQ = { "requested", 9 };
enum ast_rtp_dtls_setup setup;
switch (session_media->encryption) {
@@ -1433,6 +1434,8 @@
}
tmp = session_media->srtp;
+ attr = pjmedia_sdp_attr_create(pool, "3ge2ae", &STR_MEDSECREQ);
+ media->attr[media->attr_count++] = attr;
do {
crypto_attribute = ast_sdp_srtp_get_attrib(tmp,
diff -urN asterisk-16.3.0.orig/res/res_pjsip_session.c asterisk-16.3.0/res/res_pjsip_session.c
--- asterisk-16.3.0.orig/res/res_pjsip_session.c 2019-04-04 16:49:57.000000000 +0200
+++ asterisk-16.3.0/res/res_pjsip_session.c 2019-05-29 10:05:49.881000000 +0200
@@ -2109,6 +2109,13 @@
return -1;
}
+ if (session->endpoint->media.rtp.encryption == AST_SIP_MEDIA_ENCRYPT_SDES) {
+ ast_verb(3, "INVITE: Adding MEDIASEC headers\n");
+ ast_sip_add_header(*tdata,"Security-Verify","msrp-tls;mediasec");
+ ast_sip_add_header(*tdata,"Security-Verify","sdes-srtp;mediasec");
+ ast_sip_add_header(*tdata,"Security-Verify","dtls-srtp;mediasec");
+ }
+
return 0;
}
--
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --
asterisk-dev mailing list
To UNSUBSCRIBE or update options visit:
http://lists.digium.com/mailman/listinfo/asterisk-dev