diff --git a/channels/sip/sdp_crypto.c b/channels/sip/sdp_crypto.c index 8a0d38a423..c78f473b24 100644 --- a/channels/sip/sdp_crypto.c +++ b/channels/sip/sdp_crypto.c @@ -33,6 +33,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") +#include #include "asterisk/options.h" #include "asterisk/utils.h" #include "include/sdp_crypto.h" @@ -49,24 +50,17 @@ extern struct ast_srtp_policy_res *res_srtp_policy; struct sdp_crypto { char *a_crypto; unsigned char local_key[SRTP_MASTER_LEN]; - char *tag; + int tag; char local_key64[SRTP_MASTER_LEN64]; unsigned char remote_key[SRTP_MASTER_LEN]; }; static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, unsigned long ssrc, int inbound); -static struct sdp_crypto *sdp_crypto_alloc(void) -{ - return ast_calloc(1, sizeof(struct sdp_crypto)); -} - void sdp_crypto_destroy(struct sdp_crypto *crypto) { ast_free(crypto->a_crypto); crypto->a_crypto = NULL; - ast_free(crypto->tag); - crypto->tag = NULL; ast_free(crypto); } @@ -80,9 +74,11 @@ struct sdp_crypto *sdp_crypto_setup(void) return NULL; } - if (!(p = sdp_crypto_alloc())) { + p = ast_calloc(1, sizeof(*p)); + if (!p) { return NULL; } + p->tag = 1; if (res_srtp->get_random(p->local_key, sizeof(p->local_key)) < 0) { sdp_crypto_destroy(p); @@ -95,13 +91,13 @@ struct sdp_crypto *sdp_crypto_setup(void) if (key_len != SRTP_MASTER_LEN) { ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", key_len, SRTP_MASTER_LEN); - ast_free(p); + sdp_crypto_destroy(p); return NULL; } if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) { ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); - ast_free(p); + sdp_crypto_destroy(p); return NULL; } @@ -197,13 +193,15 @@ int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_in char *key_params = NULL; char *key_param = NULL; char *session_params = NULL; - char *key_salt = NULL; - char *lifetime = NULL; + char *key_salt = NULL; /* The actual master key and key salt */ + char *lifetime = NULL; /* Key lifetime (# of RTP packets) */ + char *mki = NULL; /* Master Key Index */ int found = 0; int key_len = 0; int suite_val = 0; unsigned char remote_key[SRTP_MASTER_LEN]; int taglen = 0; + double sdes_lifetime; if (!ast_rtp_engine_srtp_is_registered()) { return -1; @@ -218,12 +216,17 @@ int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_in session_params = strsep(&str, " "); if (!tag || !suite) { - ast_log(LOG_WARNING, "Unrecognized a=%s", attr); + ast_log(LOG_WARNING, "Unrecognized a=%s\n", attr); + return -1; + } + + if (sscanf(tag, "%30d", &p->tag) != 1 || p->tag <= 0 || p->tag > 9) { + ast_log(LOG_WARNING, "Unacceptable a=crypto tag: %s\n", tag); return -1; } if (!ast_strlen_zero(session_params)) { - ast_log(LOG_WARNING, "Unsupported crypto parameters: %s", session_params); + ast_log(LOG_WARNING, "Unsupported crypto parameters: %s\n", session_params); return -1; } @@ -241,33 +244,88 @@ int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_in } while ((key_param = strsep(&key_params, ";"))) { + unsigned int n_lifetime; char *method = NULL; char *info = NULL; method = strsep(&key_param, ":"); info = strsep(&key_param, ";"); + sdes_lifetime = 0; - if (!strcmp(method, "inline")) { - key_salt = strsep(&info, "|"); - lifetime = strsep(&info, "|"); + if (strcmp(method, "inline")) { + continue; + } - if (lifetime) { - ast_log(LOG_NOTICE, "Crypto life time unsupported: %s\n", attr); - continue; - } + key_salt = strsep(&info, "|"); + /* The next parameter can be either lifetime or MKI */ + lifetime = strsep(&info, "|"); + if (!lifetime) { found = 1; break; } + + mki = strchr(lifetime, ':'); + if (mki) { + mki = lifetime; + lifetime = NULL; + } else { + mki = strsep(&info, "|"); + } + + if (mki && *mki != '1') { + ast_log(LOG_NOTICE, "Crypto MKI handling is not supported: ignoring attribute %s\n", attr); + continue; + } + + if (lifetime) { + if (!strncmp(lifetime, "2^", 2)) { + char *lifetime_val = lifetime + 2; + + /* Exponential lifetime */ + if (sscanf(lifetime_val, "%30u", &n_lifetime) != 1) { + ast_log(LOG_NOTICE, "Failed to parse lifetime value in crypto attribute: %s\n", attr); + continue; + } + + if (n_lifetime > 48) { + /* Yeah... that's a bit big. */ + ast_log(LOG_NOTICE, "Crypto lifetime exponent of '%u' is a bit large; using 48\n", n_lifetime); + n_lifetime = 48; + } + sdes_lifetime = pow(2, n_lifetime); + } else { + /* Decimal lifetime */ + if (sscanf(lifetime, "%30u", &n_lifetime) != 1) { + ast_log(LOG_NOTICE, "Failed to parse lifetime value in crypto attribute: %s\n", attr); + continue; + } + sdes_lifetime = n_lifetime; + } + + /* Accept anything above 10 hours. Less than 10; reject. */ + if (sdes_lifetime < 1800000) { + ast_log(LOG_NOTICE, "Rejecting crypto attribute '%s': lifetime '%f' too short\n", attr, sdes_lifetime); + continue; + } + } + + ast_debug(2, "Crypto attribute '%s' accepted with lifetime '%f', MKI '%s'\n", + attr, sdes_lifetime, mki ? mki : "-"); + + found = 1; + break; } if (!found) { - ast_log(LOG_NOTICE, "SRTP crypto offer not acceptable\n"); + ast_log(LOG_NOTICE, "SRTP crypto offer not acceptable: '%s'\n", attr); return -1; } - if ((key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key))) != SRTP_MASTER_LEN) { - ast_log(LOG_WARNING, "SRTP descriptions key %d != %d\n", key_len, SRTP_MASTER_LEN); + key_len = ast_base64decode(remote_key, key_salt, sizeof(remote_key)); + if (key_len != SRTP_MASTER_LEN) { + ast_log(LOG_WARNING, "SRTP descriptions key length '%d' != master length '%d'\n", + key_len, SRTP_MASTER_LEN); return -1; } @@ -281,15 +339,6 @@ int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_in return -1; } - if (!p->tag) { - ast_debug(1, "Accepting crypto tag %s\n", tag); - p->tag = ast_strdup(tag); - if (!p->tag) { - ast_log(LOG_ERROR, "Could not allocate memory for tag\n"); - return -1; - } - } - /* Finally, rebuild the crypto line */ return sdp_crypto_offer(p, taglen); } @@ -301,13 +350,13 @@ int sdp_crypto_offer(struct sdp_crypto *p, int taglen) ast_free(p->a_crypto); } - if (ast_asprintf(&p->a_crypto, "a=crypto:%s AES_CM_128_HMAC_SHA1_%i inline:%s\r\n", - p->tag ? p->tag : "1", taglen, p->local_key64) == -1) { + if (ast_asprintf(&p->a_crypto, "a=crypto:%d AES_CM_128_HMAC_SHA1_%i inline:%s\r\n", + p->tag, taglen, p->local_key64) == -1) { ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); return -1; } - ast_debug(1, "Crypto line: %s", p->a_crypto); + ast_debug(1, "Crypto line: %s\n", p->a_crypto); return 0; }