diff --git a/README.md b/README.md index 7e7095320..e084d6587 100644 --- a/README.md +++ b/README.md @@ -1283,6 +1283,22 @@ Optionally included keys are: This flag does not contradict with `SDES-nonew`, `SDES-only-` and `SDES-no-` flags. It just orders the list of crypto suites already prepared to be sent out. + - `offerer_pref:`*SUITES LIST* + + The list of preferred crypto suites to be selected for the offerer. + + It provides a possiblity to select specific crypto suite(s) for the offerer from + the given list of crypto suites received in the offer. + + This will be used later on, when processing an answer from + the recipient and generating an answer to be sent out towards offerer. + + Furthermore, this is being decided not when the answer is processed, + but already when the offer is processed. + + Flag usage example: + `SDES-offerer_pref:AES_256_CM_HMAC_SHA;AES_256_CM_HMAC_SHA1_32;` + - `pad` RFC 4568 (section 6.1) is somewhat ambiguous regarding the base64 encoding format of diff --git a/daemon/call.c b/daemon/call.c index 0b75cdf52..06d0b6053 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -1686,12 +1686,14 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi /* SDES options coming to us for processing */ GQueue *cpq_in = &this->sdes_in; - const GQueue *offered_cpq = other ? &other->sdes_in : NULL; + GQueue *offered_cpq = other ? &other->sdes_in : NULL; if (!flags) return; - /* requested order of crypto suites */ + /* requested order of crypto suites - generated offer */ const GQueue *cpq_order = &flags->sdes_order; + /* preferred crypto suites for the offerer - generated answer */ + const GQueue *offered_order = &flags->sdes_offerer_pref; bool is_offer = (flags->opmode == OP_OFFER || flags->opmode == OP_REQUEST); @@ -1778,6 +1780,9 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi /* make sure our bit field is large enough */ assert(num_crypto_suites <= sizeof(types_offered) * 8); + /* always consider by default that offerer doesn't need re-ordering */ + MEDIA_CLEAR(other, REORDER_FORCED); + /* add offered crypto parameters */ for (GList *l = offered_cpq ? offered_cpq->head : NULL; l; l = l->next) { struct crypto_params_sdes *offered_cps = l->data; @@ -1897,21 +1902,79 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi g_queue_push_tail(cpq, cps_orig); } } + + /* set preferences list of crypto suites for the offerer, if given */ + if ((offered_order && offered_order->head) && (offered_cpq && offered_cpq->head)) { + ilog(LOG_DEBUG, "The crypto suites for the offerer will be re-ordered."); + + struct crypto_params_sdes * cps_found; + GQueue offered_cpq_orig_list = *offered_cpq; + + g_queue_init(offered_cpq); /* re-initialize offered crypto suites */ + + for (GList *l = offered_order->head; l; l = l->next) + { + str * cs_name = l->data; + GList * elem = g_queue_find_custom(&offered_cpq_orig_list, cs_name, crypto_params_sdes_cmp); + + if (!elem) + continue; + + cps_found = elem->data; + + /* check sdes_only limitations */ + if (crypto_params_sdes_check_limitations(flags->sdes_only, + flags->sdes_no, cps_found->params.crypto_suite)) { + g_queue_delete_link(&offered_cpq_orig_list, elem); + crypto_params_sdes_free(cps_found); + continue; + } + + ilog(LOG_DEBUG, "Reordering suites for offerer, adding: %s (cps tag: %d)", + cps_found->params.crypto_suite->name, cps_found->tag); + + /* affects a proper handling of crypto suites ordering, + * when sending processed answer to the media session originator */ + MEDIA_SET(other, REORDER_FORCED); + + g_queue_push_tail(offered_cpq, cps_found); + g_queue_delete_link(&offered_cpq_orig_list, elem); + } + + /* now add the rest */ + while ((cps_found = g_queue_pop_head(&offered_cpq_orig_list))) + { + ilog(LOG_DEBUG, "Reordering suites for offerer, adding: %s (cps tag: %d)", + cps_found->params.crypto_suite->name, cps_found->tag); + + /* check sdes_only limitations */ + if (crypto_params_sdes_check_limitations(flags->sdes_only, + flags->sdes_no, cps_found->params.crypto_suite)) { + crypto_params_sdes_free(cps_found); + continue; + } + + g_queue_push_tail(offered_cpq, cps_found); + } + + /* clear older data we are poiting using a copy now */ + crypto_params_sdes_queue_clear(&offered_cpq_orig_list); + } } /* OP_ANSWER */ else { - // we pick the first supported crypto suite + /* we pick the first supported crypto suite */ struct crypto_params_sdes *cps = cpq->head ? cpq->head->data : NULL; struct crypto_params_sdes *cps_in = cpq_in->head ? cpq_in->head->data : NULL; struct crypto_params_sdes *offered_cps = (offered_cpq && offered_cpq->head) ? offered_cpq->head->data : NULL; if (flags && flags->sdes_static && cps) { - // reverse logic: instead of looking for a matching crypto suite to put in - // our answer, we want to leave what we already had. however, this is only - // valid if the currently present crypto suite matches the offer + /* reverse logic: instead of looking for a matching crypto suite to put in + * our answer, we want to leave what we already had. however, this is only + * valid if the currently present crypto suite matches the offer */ for (GList *l = cpq_in->head; l; l = l->next) { struct crypto_params_sdes *check_cps = l->data; if (check_cps->params.crypto_suite == cps->params.crypto_suite @@ -1924,11 +1987,12 @@ static void __generate_crypto(const struct sdp_ng_flags *flags, struct call_medi } } - if (offered_cps) { + /* don't try to match, if the offerer requested some suite preferences */ + if (offered_cps && !MEDIA_ISSET(this, REORDER_FORCED) ) { ilogs(crypto, LOG_DEBUG, "Looking for matching crypto suite to offered %u:%s", offered_cps->tag, offered_cps->params.crypto_suite->name); - // check if we can do SRTP<>SRTP passthrough. the crypto suite that was accepted - // must have been present in what was offered to us + /* check if we can do SRTP<>SRTP passthrough. the crypto suite that was accepted + * must have been present in what was offered to us */ for (GList *l = cpq_in->head; l; l = l->next) { struct crypto_params_sdes *check_cps = l->data; if (check_cps->params.crypto_suite == offered_cps->params.crypto_suite) { diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index a3df6408d..a89376bb9 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -520,6 +520,11 @@ INLINE void ng_sdes_option(struct sdp_ng_flags *out, str *s, void *dummy) { if (call_ng_flags_prefix(out, s, "order:", call_ng_flags_str_q_multi, &out->sdes_order)) return; + /* Crypto suite preferences for the offerer */ + if (call_ng_flags_prefix(out, s, "offerer_pref:", call_ng_flags_str_q_multi, + &out->sdes_offerer_pref)) + return; + switch (__csh_lookup(s)) { case CSH_LOOKUP("no"): case CSH_LOOKUP("off"): @@ -964,6 +969,9 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { return; if (call_ng_flags_prefix(out, s, "SDES-order:", call_ng_flags_str_q_multi, &out->sdes_order)) return; + if (call_ng_flags_prefix(out, s, "SDES-offerer_pref:", call_ng_flags_str_q_multi, + &out->sdes_offerer_pref)) + return; if (call_ng_flags_prefix(out, s, "SDES-", ng_sdes_option, NULL)) return; if (call_ng_flags_prefix(out, s, "OSRTP-", ng_osrtp_option, NULL)) @@ -1599,6 +1607,7 @@ void call_ng_free_flags(struct sdp_ng_flags *flags) { g_queue_clear_full(&flags->codec_consume, free); g_queue_clear_full(&flags->codec_mask, free); g_queue_clear_full(&flags->sdes_order, free); + g_queue_clear_full(&flags->sdes_offerer_pref, free); } static enum load_limit_reasons call_offer_session_limit(void) { diff --git a/include/call.h b/include/call.h index c2dd55911..197ac0d10 100644 --- a/include/call.h +++ b/include/call.h @@ -183,6 +183,7 @@ enum { #define MEDIA_FLAG_RTCP_GEN 0x08000000 #define MEDIA_FLAG_ECHO 0x10000000 #define MEDIA_FLAG_BLACKHOLE 0x20000000 +#define MEDIA_FLAG_REORDER_FORCED 0x40000000 /* access macros */ #define SP_ISSET(p, f) bf_isset(&(p)->sp_flags, SP_FLAG_ ## f) diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 9fa577d69..d1e8bd79b 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -56,6 +56,7 @@ struct sdp_ng_flags { GHashTable *sdes_no; /* individual crypto suites which are excluded */ GHashTable *sdes_only; /* individual crypto suites which are only accepted */ GQueue sdes_order; /* the order, in which crypto suites are being added to the SDP */ + GQueue sdes_offerer_pref; /* preferred crypto suites to be selected for the offerer */ str dtls_fingerprint; enum { ICE_DEFAULT = 0, diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index a7c957a09..482019f65 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -14612,6 +14612,346 @@ a=rtcp:PORT a=crypto:4 AES_256_CM_HMAC_SHA1_32 inline:CRYPTO256 SDP +new_call; + +offer('SDES offerer preferences', { ICE => 'remove', DTLS => 'off', SDES => [ 'offerer_pref:AES_256_CM_HMAC_SHA1_32;AES_256_CM_HMAC_SHA1_80' ] }, < 'remove' }, < 'remove', DTLS => 'off', SDES => [ 'offerer_pref:AES_256_CM_HMAC_SHA1_32;AES_256_CM_HMAC_SHA1_80' ] }, < 'remove' }, < 'remove', DTLS => 'off', SDES => [ 'offerer_pref:AES_256_CM_HMAC_SHA1_32;AES_256_CM_HMAC_SHA1_80' ] }, < 'remove' }, < 'remove', DTLS => 'off', SDES => [ 'offerer_pref:AES_256_CM_HMAC_SHA1_32' ] }, < 'remove' }, < 'remove', DTLS => 'off', SDES => [ 'nonew', 'offerer_pref:AES_256_CM_HMAC_SHA1_32;AES_256_CM_HMAC_SHA1_80' ] }, < 'remove' }, < 'remove', DTLS => 'off', SDES => [ 'only-AES_256_CM_HMAC_SHA1_80', 'offerer_pref:AES_256_CM_HMAC_SHA1_32;AES_256_CM_HMAC_SHA1_80' ] }, < 'remove' }, <