RFC 3329 and mediasec implementation

RS
Rahul Sharma
Fri, Mar 29, 2019 4:00 PM

Hi all,

I'm implementing client-initiated mediasec negotiation[2] with partial RFC
3329[1] support in the PJSIP library. My target application uses the PJSUA
interface. This is still at an early stage and since I'd like to upstream
this change I think it's best to start a discussion about the approach and
get your views on it.

Outline of expected behaviour:

  1. In initial REGISTER message, set the following headers:
    Require: mediasec
    Proxy-Require: mediasec
    Security-Client: sdes-srtp; mediasec // A separate header for every
    supported mechanism. At least one mechanism must be enabled for the build.
  • We refer to this set of headers as the "mediasec initial headers"
  1. Server responds with 401 Unauthorised and a set of Security-Server
    headers:
    Security-Server: msrp-tls;mediasec
    Security-Server: sdes-srtp;mediasec
    Security-Server: dtls-srtp;mediasec
  • We refer to this set of headers as the "mediasec server headers"
    These headers must be stored for this session. The client must send these
    to the server as Security-Verify headers in every request message for this
    session.
    ** If the Server does not send the Security-Server headers in the REGISTER
    response, registration is aborted.
  1. client responds to 401 Unauthorised message as usual and also adds the
    following headers (the saved Security-Server headers are sent back as
    Security-Verify headers):
    Require: mediasec
    Proxy-Require: mediasec
    Security-Client: sdes-srtp; mediasec
    Security-Verify: msrp-tls;mediasec
    Security-Verify: sdes-srtp;mediasec
    Security-Verify: dtls-srtp;mediasec
  • We refer to this set of headers as the "mediasec verification headers"
  1. Server responds with 200 OK
  2. client sends INVITE request with the mediasec verification headers and
    sets an additional SDP attribute:
    a=3ge2ae:Requested
  3. Server responds with 200 OK
  4. Any further SIP Request messages must also include the mediasec
    verification headers.

Outline of code changes:
I will send an update if I come across alternative implementation ideas.
Please let me know if the following looks like the correct way to proceed.

  1. Inserting the mediasec headers
    sip_auth_client.c: pjsip_auth_clt_init_req() looks like the right place for
    this. I inserted some test headers and they appear in all requests.

/* Initialize outgoing request. */
PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
pjsip_tx_data *tdata )
{
...
pj_strdup(tdata->pool,&hs->credential.digest.uri, &uri);
pj_strdup(tdata->pool, &hs->credential.digest.algorithm,
&sess->pref.algorithm);
}

            pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);
        }
    }
}
  • pjsip_hdr hdr_list;

  • pj_list_init(&hdr_list);

  • pjsip_generic_string_hdr *tmp_hdr;

  • const pj_str_t STR_PROXY_REQ = { "Proxy-Require", 13 };

  • pj_str_t security_proxy_req_val = { "mediasec", 8 };

  • tmp_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_PROXY_REQ,

  •                                   &security_proxy_req_val);
    
  • pj_list_push_back(&hdr_list, (pjsip_hdr*)tmp_hdr);

  • const pj_str_t STR_REQ = { "Require", 7 };

  • pj_str_t security_req_val = { "mediasec", 8 };

  • tmp_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_REQ,

  •                                   &security_req_val);
    
  • pj_list_push_back(&hdr_list, (pjsip_hdr*)tmp_hdr);

  • const pj_str_t STR_SECURITY_CLIENT = { "Security-Client", 15 };

  • pj_str_t security_client_val = { "sdes-srtp;mediasec", 18 };

  • tmp_hdr = pjsip_generic_string_hdr_create(tdata->pool,
    &STR_SECURITY_CLIENT,

  •                                   &security_client_val);
    
  • pj_list_push_back(&hdr_list, (pjsip_hdr*)tmp_hdr);

  • pjsip_hdr *hdr = hdr_list.next;

  • while (hdr != &hdr_list) {

  •   pjsip_hdr *next = hdr->next;
    
  •   pjsip_msg_add_hdr(tdata->msg, hdr);
    
  •   hdr = next;
    
  • }

    return PJ_SUCCESS;
    }

  1. Pass in a list of security negotiation parameters to
    pjsip_auth_clt_init_req() that need to be added to SIP requests (instead of
    hardcoded values in the above test code)
    This can be done by adding a new data member to struct pjsip_auth_clt_sess
    in sip_auth.h of type struct pjsip_security_neg {pj_str_t
    security_param_list; pj_str_t security_client_list; pj_str_t
    security_server_list;}

  2. Initialise auth_clt_sess.security_neg to send the "mediasec initial
    headers"
    This can be done in pjsua_ac.c: pjsua_acc_set_registration()

  3. New parameters to enable/disable the features
    pjsua_ac.c: pjsua_acc_set_registration() should only add the headers if the
    application has enabled the feature.
    RFC 3329 first hop security negotiation is a separate feature that may be
    fully implemented if needed by someone else in the future. The mediasec
    feature works as an extension to RFC 3329. Therefore these features must
    have separate flags to enable/disable.
    struct pjsua_acc_config in pjsua.h can be extended to add two new fields:
    pjsua_acc_config.use_first_hop_security_neg = PJ_TRUE
    //enables rfc 3329 first hop security negotiation
    pjsua_acc_config.use_srtp_mediasec = PJMEDIA_SRTP_MEDIASEC_ENABLE;
    //enables mediasec support for SRTP. Requires
    use_first_hop_security_neg = PJ_TRUE.

These can be used to set similarly named members in struct pjsua_acc in
pjsua_acc.c: pjsua_acc_add()
pjsua_acc.use_first_hop_security_neg
pjsua_acc.use_srtp_mediasec

In (3) above, pjsua_acc_set_registration() will then have access to these
parameters to check if the security negotiation headers need to be added.

  1. Update pjsip_auth_clt_sess.pjsip_security_neg.security_server_list when
    client receives the Security-Server headers in a 401 Unauthorized message
    from the server.
    So far, the client is set up to only send the "mediasec initial headers" in
    all SIP request messages. To create the set of "mediasec verification
    headers" pjsip_auth_clt_sess.pjsip_security_neg.security_server_list must
    be updated.

I'm still working on this and will send another update for that. The
options I'm exploring include implementing Security-Server header parsing
in sip_parser.c and adding it to a new field in struct pjsip_rx_data; or
extracting the info from pjsip_rx_data.msg in pjsip_auth_clt_reinit_req().

  1. Instantiate the media plane security mechanism based on the client and
    server side mediasec parameters. Add support sdes-srtp only for now.

  2. In the INVITE message, add attribute "a=3ge2ae:requested" to the SDP
    attributes
    This can be done in sdp.c: print_session() based on some flags set in
    struct pjmedia_sdp_session to indicate mediasec support.

Prerequisites:
PJSIP must be built with

define PJMEDIA_HAS_SRTP                1

// At least one out of SDES or DTLS support:

define PJMEDIA_SRTP_HAS_SDES            1

define PJMEDIA_SRTP_HAS_DTLS            1

References:
[1] https://tools.ietf.org/html/rfc3329
[2] https://tools.ietf.org/html/draft-dawes-dispatch-mediasec-parameter-07

Regards,
Rahul

Hi all, I'm implementing client-initiated mediasec negotiation[2] with partial RFC 3329[1] support in the PJSIP library. My target application uses the PJSUA interface. This is still at an early stage and since I'd like to upstream this change I think it's best to start a discussion about the approach and get your views on it. Outline of expected behaviour: 1) In initial REGISTER message, set the following headers: Require: mediasec Proxy-Require: mediasec Security-Client: sdes-srtp; mediasec // A separate header for every supported mechanism. At least one mechanism must be enabled for the build. * We refer to this set of headers as the "mediasec initial headers" 2) Server responds with 401 Unauthorised and a set of Security-Server headers: Security-Server: msrp-tls;mediasec Security-Server: sdes-srtp;mediasec Security-Server: dtls-srtp;mediasec * We refer to this set of headers as the "mediasec server headers" These headers must be stored for this session. The client must send these to the server as Security-Verify headers in every request message for this session. ** If the Server does not send the Security-Server headers in the REGISTER response, registration is aborted. 3) client responds to 401 Unauthorised message as usual and also adds the following headers (the saved Security-Server headers are sent back as Security-Verify headers): Require: mediasec Proxy-Require: mediasec Security-Client: sdes-srtp; mediasec Security-Verify: msrp-tls;mediasec Security-Verify: sdes-srtp;mediasec Security-Verify: dtls-srtp;mediasec * We refer to this set of headers as the "mediasec verification headers" 4) Server responds with 200 OK 5) client sends INVITE request with the mediasec verification headers and sets an additional SDP attribute: a=3ge2ae:Requested 6) Server responds with 200 OK 7) Any further SIP Request messages must also include the mediasec verification headers. Outline of code changes: I will send an update if I come across alternative implementation ideas. Please let me know if the following looks like the correct way to proceed. 1) Inserting the mediasec headers sip_auth_client.c: pjsip_auth_clt_init_req() looks like the right place for this. I inserted some test headers and they appear in all requests. /* Initialize outgoing request. */ PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess, pjsip_tx_data *tdata ) { ... pj_strdup(tdata->pool,&hs->credential.digest.uri, &uri); pj_strdup(tdata->pool, &hs->credential.digest.algorithm, &sess->pref.algorithm); } pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs); } } } + pjsip_hdr hdr_list; + pj_list_init(&hdr_list); + pjsip_generic_string_hdr *tmp_hdr; + const pj_str_t STR_PROXY_REQ = { "Proxy-Require", 13 }; + pj_str_t security_proxy_req_val = { "mediasec", 8 }; + tmp_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_PROXY_REQ, + &security_proxy_req_val); + pj_list_push_back(&hdr_list, (pjsip_hdr*)tmp_hdr); + const pj_str_t STR_REQ = { "Require", 7 }; + pj_str_t security_req_val = { "mediasec", 8 }; + tmp_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_REQ, + &security_req_val); + pj_list_push_back(&hdr_list, (pjsip_hdr*)tmp_hdr); + const pj_str_t STR_SECURITY_CLIENT = { "Security-Client", 15 }; + pj_str_t security_client_val = { "sdes-srtp;mediasec", 18 }; + tmp_hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_SECURITY_CLIENT, + &security_client_val); + pj_list_push_back(&hdr_list, (pjsip_hdr*)tmp_hdr); + pjsip_hdr *hdr = hdr_list.next; + while (hdr != &hdr_list) { + pjsip_hdr *next = hdr->next; + pjsip_msg_add_hdr(tdata->msg, hdr); + hdr = next; + } return PJ_SUCCESS; } 2) Pass in a list of security negotiation parameters to pjsip_auth_clt_init_req() that need to be added to SIP requests (instead of hardcoded values in the above test code) This can be done by adding a new data member to struct pjsip_auth_clt_sess in sip_auth.h of type struct pjsip_security_neg {pj_str_t security_param_list; pj_str_t security_client_list; pj_str_t security_server_list;} 3) Initialise auth_clt_sess.security_neg to send the "mediasec initial headers" This can be done in pjsua_ac.c: pjsua_acc_set_registration() 4) New parameters to enable/disable the features pjsua_ac.c: pjsua_acc_set_registration() should only add the headers if the application has enabled the feature. RFC 3329 first hop security negotiation is a separate feature that may be fully implemented if needed by someone else in the future. The mediasec feature works as an extension to RFC 3329. Therefore these features must have separate flags to enable/disable. struct pjsua_acc_config in pjsua.h can be extended to add two new fields: pjsua_acc_config.use_first_hop_security_neg = PJ_TRUE //enables rfc 3329 first hop security negotiation pjsua_acc_config.use_srtp_mediasec = PJMEDIA_SRTP_MEDIASEC_ENABLE; //enables mediasec support for SRTP. Requires use_first_hop_security_neg = PJ_TRUE. These can be used to set similarly named members in struct pjsua_acc in pjsua_acc.c: pjsua_acc_add() pjsua_acc.use_first_hop_security_neg pjsua_acc.use_srtp_mediasec In (3) above, pjsua_acc_set_registration() will then have access to these parameters to check if the security negotiation headers need to be added. 5) Update pjsip_auth_clt_sess.pjsip_security_neg.security_server_list when client receives the Security-Server headers in a 401 Unauthorized message from the server. So far, the client is set up to only send the "mediasec initial headers" in all SIP request messages. To create the set of "mediasec verification headers" pjsip_auth_clt_sess.pjsip_security_neg.security_server_list must be updated. I'm still working on this and will send another update for that. The options I'm exploring include implementing Security-Server header parsing in sip_parser.c and adding it to a new field in struct pjsip_rx_data; or extracting the info from pjsip_rx_data.msg in pjsip_auth_clt_reinit_req(). 6) Instantiate the media plane security mechanism based on the client and server side mediasec parameters. Add support sdes-srtp only for now. 7) In the INVITE message, add attribute "a=3ge2ae:requested" to the SDP attributes This can be done in sdp.c: print_session() based on some flags set in struct pjmedia_sdp_session to indicate mediasec support. Prerequisites: PJSIP must be built with # define PJMEDIA_HAS_SRTP 1 // At least one out of SDES or DTLS support: # define PJMEDIA_SRTP_HAS_SDES 1 # define PJMEDIA_SRTP_HAS_DTLS 1 References: [1] https://tools.ietf.org/html/rfc3329 [2] https://tools.ietf.org/html/draft-dawes-dispatch-mediasec-parameter-07 Regards, Rahul
MM
Michael Maier
Sun, Mar 31, 2019 7:03 PM

Hello Rahul,

On 29.03.19 at 17:00 Rahul Sharma wrote:

Hi all,

I'm implementing client-initiated mediasec negotiation[2] with partial RFC
3329[1] support in the PJSIP library. My target application uses the PJSUA
interface. This is still at an early stage and since I'd like to upstream
this change I think it's best to start a discussion about the approach and
get your views on it.

I would be pleased If you could provide a patch against pjsip 2.8! Then
I could test your implementation if it works with Deutsche Telekom.

Thanks
Michael

Hello Rahul, On 29.03.19 at 17:00 Rahul Sharma wrote: > Hi all, > > I'm implementing client-initiated mediasec negotiation[2] with partial RFC > 3329[1] support in the PJSIP library. My target application uses the PJSUA > interface. This is still at an early stage and since I'd like to upstream > this change I think it's best to start a discussion about the approach and > get your views on it. I would be pleased If you could provide a patch against pjsip 2.8! Then I could test your implementation if it works with Deutsche Telekom. Thanks Michael