Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 303637) +++ channels/chan_sip.c (working copy) @@ -1541,7 +1541,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno); /*------ SRTP Support -------- */ -static int setup_srtp(struct sip_srtp **srtp); +static int setup_srtp(struct sip_srtp **srtp, int suite); static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a); /*------ T38 Support --------- */ @@ -5203,22 +5203,29 @@ } if (ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP)) { + unsigned int suite; + char s1[SIPBUFSIZE]; + + suite = (ast_test_flag(&p->flags[1], SIP_PAGE2_USE_SRTP) >> 28); + ast_verbose(VERBOSE_PREFIX_2 "Encrypted Media is required, offering cryptosuite %s.\n", + ast_getcryptosuitename_multiple(s1, SIPBUFSIZE, suite, 0)); + if (ast_test_flag(&p->flags[0], SIP_REINVITE)) { ast_debug(1, "Direct media not possible when using SRTP, ignoring canreinvite setting\n"); ast_clear_flag(&p->flags[0], SIP_REINVITE); } - if (p->rtp && !p->srtp && setup_srtp(&p->srtp) < 0) { + if (p->rtp && !p->srtp && setup_srtp(&p->srtp, suite) < 0) { ast_log(LOG_WARNING, "SRTP audio setup failed\n"); return -1; } - if (p->vrtp && !p->vsrtp && setup_srtp(&p->vsrtp) < 0) { + if (p->vrtp && !p->vsrtp && setup_srtp(&p->vsrtp, suite) < 0) { ast_log(LOG_WARNING, "SRTP video setup failed\n"); return -1; } - if (p->trtp && !p->vsrtp && setup_srtp(&p->tsrtp) < 0) { + if (p->trtp && !p->vsrtp && setup_srtp(&p->tsrtp, suite) < 0) { ast_log(LOG_WARNING, "SRTP text setup failed\n"); return -1; } @@ -10578,9 +10585,14 @@ if (!srtp->crypto) { srtp->crypto = sdp_crypto_setup(); } - if (srtp->crypto && (sdp_crypto_offer(srtp->crypto) >= 0)) { + if (srtp->crypto) { + int res; + + res = sdp_crypto_offer(srtp->crypto, ast_test_flag(srtp, SRTP_CRYPTO_SUITE) >> 4); + if (res >= 0) { *a_crypto = sdp_crypto_attrib(srtp->crypto); } + } if (!*a_crypto) { ast_log(LOG_WARNING, "No SRTP key management enabled\n"); @@ -16476,6 +16488,7 @@ int x = 0, load_realtime; format_t codec = 0; int realtimepeers; + char s1[SIPBUFSIZE]; realtimepeers = ast_check_realtime("sippeers"); @@ -16616,7 +16629,8 @@ ast_cli(fd, " RTP Engine : %s\n", peer->engine); ast_cli(fd, " Parkinglot : %s\n", peer->parkinglot); ast_cli(fd, " Use Reason : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON))); - ast_cli(fd, " Encryption : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP))); + ast_getcryptosuitename_multiple(s1, SIPBUFSIZE, ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP) >> 28, 0); + ast_cli(fd, " Encryption : %s\n", s1); ast_cli(fd, "\n"); peer = unref_peer(peer, "sip_show_peer: unref_peer: done with peer ptr"); } else if (peer && type == 1) { /* manager listing */ @@ -16672,7 +16686,8 @@ astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se); astman_append(s, "SIP-Sess-Min: %d\r\n", peer->stimer.st_min_se); astman_append(s, "SIP-RTP-Engine: %s\r\n", peer->engine); - astman_append(s, "SIP-Encryption: %s\r\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP) ? "Y" : "N"); + ast_getcryptosuitename_multiple(s1, SIPBUFSIZE, ast_test_flag(&peer->flags[1], SIP_PAGE2_USE_SRTP), 0); + astman_append(s, "SIP-Encryption: %s\r\n", s1); /* - is enumerated */ astman_append(s, "SIP-DTMFmode: %s\r\n", dtmfmode2str(ast_test_flag(&peer->flags[0], SIP_DTMF))); @@ -26391,7 +26406,10 @@ } else if (!strcasecmp(v->name, "use_q850_reason")) { ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON); } else if (!strcasecmp(v->name, "encryption")) { - ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_USE_SRTP); + int error = ast_parse_encryption(&peer->flags[1], v->value); + if (error) { + ast_log(LOG_WARNING, "Cryptosuite configuration errors found in line %d : %s = %s\n", v->lineno, v->name, v->value); + } } else if (!strcasecmp(v->name, "snom_aoc_enabled")) { ast_set2_flag(&peer->flags[2], ast_true(v->value), SIP_PAGE3_SNOM_AOC); } @@ -28210,8 +28228,22 @@ } /* SRTP */ -static int setup_srtp(struct sip_srtp **srtp) +static void set_srtp_suite_flags(struct sip_srtp *srtp, int suite) { + char s1[SIPBUFSIZE]; + + ast_clear_flag(srtp, SRTP_CRYPTO_SUITE_USE_AES_80); + ast_clear_flag(srtp, SRTP_CRYPTO_SUITE_USE_AES_32); + ast_clear_flag(srtp, SRTP_CRYPTO_SUITE_USE_F8_80); + if (suite <= 7 && suite >= 0) { + ast_set_flag(srtp, suite << 4); + } + ast_verbose(VERBOSE_PREFIX_2 "SRTP_CRYPTO_SUITE is set to %s.\n", + ast_getcryptosuitename_multiple(s1, SIPBUFSIZE, suite, 0)); +} + +static int setup_srtp(struct sip_srtp **srtp, int suite) +{ if (!ast_rtp_engine_srtp_is_registered()) { ast_log(LOG_ERROR, "No SRTP module loaded, can't setup SRTP session.\n"); return -1; @@ -28221,11 +28253,15 @@ return -1; } + set_srtp_suite_flags(*srtp, suite); + return 0; } static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a) { + int suite; + if (strncasecmp(a, "crypto:", 7)) { return FALSE; } @@ -28235,7 +28271,7 @@ return FALSE; } - if (setup_srtp(srtp) < 0) { + if (setup_srtp(srtp, 0) < 0) { return FALSE; } } @@ -28250,9 +28286,11 @@ return FALSE; } - if (sdp_crypto_process((*srtp)->crypto, a, rtp) < 0) { + suite = sdp_crypto_process((*srtp)->crypto, a, rtp); + if (suite < 0) { return FALSE; } + set_srtp_suite_flags(*srtp, suite); ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK); Index: channels/sip/include/sip.h =================================================================== --- channels/sip/include/sip.h (revision 303637) +++ channels/sip/include/sip.h (working copy) @@ -335,7 +335,10 @@ #define SIP_PAGE2_UDPTL_DESTINATION (1 << 25) /*!< DP: Use source IP of RTP as destination if NAT is enabled */ #define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 26) /*!< DP: Always set up video, even if endpoints don't support it */ #define SIP_PAGE2_HAVEPEERCONTEXT (1 << 27) /*< Are we associated with a configured peer context? */ -#define SIP_PAGE2_USE_SRTP (1 << 28) /*!< DP: Whether we should offer (only) SRTP */ +#define SIP_PAGE2_USE_SRTP (7 << 28) /*!< DP: Whether we should offer (only) SRTP */ +#define SIP_PAGE2_USE_SRTP_AES_32 (1 << 28) /*!< DP: SRTP suite to offer */ +#define SIP_PAGE2_USE_SRTP_AES_80 (2 << 28) /*!< DP: SRTP suite to offer */ +#define SIP_PAGE2_USE_SRTP_F8_80 (4 << 28) /*!< DP: SRTP suite to offer */ #define SIP_PAGE2_FLAGS_TO_COPY \ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \ Index: channels/sip/include/sdp_crypto.h =================================================================== --- channels/sip/include/sdp_crypto.h (revision 303637) +++ channels/sip/include/sdp_crypto.h (working copy) @@ -32,7 +32,51 @@ struct sdp_crypto; -/*! \brief Initialize an return an sdp_crypto struct +/*! \brief Definition of supported media cryptosuites */ +struct ast_cryptosuite_list { + format_t bits; /*!< bitmask value */ + char *name; /*!< short name: aes_32|aes_80|f8_80 */ + char *desc; /*!< Description string to use in a=crypto line */ +}; + +const struct ast_cryptosuite_list *ast_get_cryptosuite_list_index(int idx); +const struct ast_cryptosuite_list *ast_get_cryptosuite_list(size_t *size); + +/*! \brief Get the name of a cryptosuite + * \param suite id of cryptosuite + * \param getdesc indicates whether to return desc versus name + * \return A static string containing the name of the cryptosuite or "unknown" if unknown. + */ +char * ast_getcryptosuitename(format_t suite, int getdesc); + +/*! \brief Get the names of a set of cryptosuites + * \param buf a buffer for the output string + * \param size size of buf (bytes) + * \param suite the cryptosuite bits (combined IDs of cryptosuites) + * \param getdesc indicates whether to return desc versus name + * Prints a list of readable cryptosuite names corresponding to "suite". + * ex: for suite=AST_AES_CM_128_HMAC_SHA1_80|AST_AES_CM_128_HMAC_SHA1_32 it will return "0x3 (aes_80|aes_32)" + * if getdesc=1 it will return "0x3 (AES_CM_128_HMAC_SHA1_80|AES_CM_128_HMAC_SHA1_32)" + * \return The return value is buf. + */ +char * ast_getcryptosuitename_multiple(char *buf, size_t size, format_t suite, int getdesc); + +/*! + * \brief Gets a cryptosuite index from a name. + * \param nameordesc string of cryptosuite + * \return This returns the form of the cryptosuite in binary on success, 0 on error. + */ +format_t ast_getcryptosuitebyname(const char *nameordesc); + +/*! \brief Parse an "encryptio" line in a channel or device configuration + * \param list the [comma delimited] string from the conf file + * \param flags flag set to be updated + * \return Returns number of errors encountered during parsing + */ +int ast_parse_encryption(struct ast_flags *flags, const char *list); + + +/*! \brief Initialize and return an sdp_crypto struct * * \details * This function allocates a new sdp_crypto struct and initializes its values @@ -52,8 +96,8 @@ * \param attr the a:crypto line from SDP * \param rtp The rtp instance associated with the SDP being parsed * - * \retval 0 success - * \retval nonzero failure + * \retval suite_val (enum ast_srtp_suite) success + * \retval -1 failure */ int sdp_crypto_process(struct sdp_crypto *p, const char *attr, struct ast_rtp_instance *rtp); @@ -63,12 +107,13 @@ * \details * The offer is stored on the sdp_crypto struct in a_crypto * - * \param A valid sdp_crypto struct + * \param p A valid sdp_crypto struct + * \param suite valid cryptosuite value * * \retval 0 success * \retval nonzero failure */ -int sdp_crypto_offer(struct sdp_crypto *p); +int sdp_crypto_offer(struct sdp_crypto *p, enum ast_srtp_suite suite); /*! \brief Return the a_crypto value of the sdp_crypto struct Index: channels/sip/include/srtp.h =================================================================== --- channels/sip/include/srtp.h (revision 303637) +++ channels/sip/include/srtp.h (working copy) @@ -34,6 +34,10 @@ #define SRTP_ENCR_OPTIONAL (1 << 1) /* SRTP encryption optional */ #define SRTP_CRYPTO_ENABLE (1 << 2) #define SRTP_CRYPTO_OFFER_OK (1 << 3) +#define SRTP_CRYPTO_SUITE (7 << 4) /* Preferred SRTP encryption suite */ +#define SRTP_CRYPTO_SUITE_USE_AES_32 (1 << 4) +#define SRTP_CRYPTO_SUITE_USE_AES_80 (2 << 4) +#define SRTP_CRYPTO_SUITE_USE_F8_80 (4 << 4) /*! \brief structure for secure RTP audio */ struct sip_srtp { Index: channels/sip/sdp_crypto.c =================================================================== --- channels/sip/sdp_crypto.c (revision 303637) +++ channels/sip/sdp_crypto.c (working copy) @@ -31,7 +31,9 @@ #include "asterisk/options.h" #include "asterisk/utils.h" +#include "asterisk/res_srtp.h" #include "include/sdp_crypto.h" +#include "include/sip.h" #define SRTP_MASTER_LEN 30 #define SRTP_MASTERKEY_LEN 16 @@ -47,6 +49,130 @@ char local_key64[SRTP_MASTER_LEN64]; }; +/*! \brief Definition of supported media cryptosuites */ +static const struct ast_cryptosuite_list AST_CRYPTOSUITE_LIST[] = { + { AST_AES_CM_128_HMAC_SHA1_80 , "aes_80", "AES_CM_128_HMAC_SHA1_80" }, + { AST_AES_CM_128_HMAC_SHA1_32 , "aes_32", "AES_CM_128_HMAC_SHA1_32" }, + { AST_F8_128_HMAC_SHA1_80 , "f8_80", "F8_128_HMAC_SHA1_80" }, +}; + +const struct ast_cryptosuite_list *ast_get_cryptosuite_list_index(int idx) +{ + return &AST_CRYPTOSUITE_LIST[idx]; +} + +const struct ast_cryptosuite_list *ast_get_cryptosuite_list(size_t *size) +{ + *size = ARRAY_LEN(AST_CRYPTOSUITE_LIST); + return AST_CRYPTOSUITE_LIST; +} + +format_t ast_getcryptosuitebyname(const char *nameordesc) +{ + int x, all; + format_t suite = 0; + + all = strcasecmp(nameordesc, "all") ? 0 : 1; + for (x = 0; x < ARRAY_LEN(AST_CRYPTOSUITE_LIST); x++) { + if (all || + !strcasecmp(AST_CRYPTOSUITE_LIST[x].name, nameordesc) || + !strcasecmp(AST_CRYPTOSUITE_LIST[x].desc, nameordesc)) { + suite |= AST_CRYPTOSUITE_LIST[x].bits; + if (!all) { + break; + } + } + } + + return suite; +} + +char * ast_getcryptosuitename(format_t suite, int getdesc) +{ + int x; + char *ret = "unknown"; + for (x = 0; x < ARRAY_LEN(AST_CRYPTOSUITE_LIST); x++) { + if (AST_CRYPTOSUITE_LIST[x].bits == suite) { + if (getdesc) { + ret = AST_CRYPTOSUITE_LIST[x].desc; + } else { + ret = AST_CRYPTOSUITE_LIST[x].name; + } + break; + } + } + return ret; +} + +char * ast_getcryptosuitename_multiple(char *buf, size_t size, format_t suite, int getdesc) +{ + int x; + unsigned len; + char *start, *end = buf; + + if (!size) + return buf; + snprintf(end, size, "0x%llx (", (unsigned long long) suite); + len = strlen(end); + end += len; + size -= len; + start = end; + for (x = 0; x < ARRAY_LEN(AST_CRYPTOSUITE_LIST); x++) { + if (AST_CRYPTOSUITE_LIST[x].bits & suite) { + if (getdesc) { + snprintf(end, size, "%s|", AST_CRYPTOSUITE_LIST[x].desc); + } else { + snprintf(end, size, "%s|", AST_CRYPTOSUITE_LIST[x].name); + } + len = strlen(end); + end += len; + size -= len; + } + } + if (start == end) + ast_copy_string(start, "nothing)", size); + else if (size > 1) + *(end - 1) = ')'; + return buf; +} + +int ast_parse_encryption(struct ast_flags *flags, const char *list) +{ + int errors = 0; + char *parse = NULL, *this = NULL; + format_t suite = 0; + + ast_clear_flag(flags, SIP_PAGE2_USE_SRTP_AES_80); + ast_clear_flag(flags, SIP_PAGE2_USE_SRTP_AES_32); + ast_clear_flag(flags, SIP_PAGE2_USE_SRTP_F8_80); + parse = ast_strdupa(list); + while ((this = strsep(&parse, ","))) { + if (!strcasecmp(this, "yes")) { + ast_set_flag(flags, SIP_PAGE2_USE_SRTP_AES_80); + break; + } else if (!strcasecmp(this, "no")) { + break; + } else if (!(suite = ast_getcryptosuitebyname(this))) { + ast_log(LOG_WARNING, "Cannot allow unknown cryptosuite '%s'\n", this); + errors++; + continue; + } + switch (suite) { + case AST_AES_CM_128_HMAC_SHA1_32: + ast_set_flag(flags, SIP_PAGE2_USE_SRTP_AES_32); + break; + case AST_AES_CM_128_HMAC_SHA1_80: + ast_set_flag(flags, SIP_PAGE2_USE_SRTP_AES_80); + break; + case AST_F8_128_HMAC_SHA1_80: + ast_set_flag(flags, SIP_PAGE2_USE_SRTP_F8_80); + break; + } + } + + return errors; +} + 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) @@ -281,21 +407,34 @@ snprintf(p->a_crypto, attr_len + 10, "a=crypto:%s %s inline:%s\r\n", tag, suite, p->local_key64); } - return 0; + return suite_val; } -int sdp_crypto_offer(struct sdp_crypto *p) +int sdp_crypto_offer(struct sdp_crypto *p, enum ast_srtp_suite suite) { - char crypto_buf[128]; - const char *crypto_suite = "AES_CM_128_HMAC_SHA1_80"; /* Crypto offer */ + char crypto_buf[384], s1[SIPBUFSIZE]; + int csocount, i, rc, len = 0; + char cso[4][64] = { "null", "null", "null", "null" }; if (p->a_crypto) { ast_free(p->a_crypto); } - if (snprintf(crypto_buf, sizeof(crypto_buf), "a=crypto:1 %s inline:%s\r\n", crypto_suite, p->local_key64) < 1) { + ast_getcryptosuitename_multiple(s1, SIPBUFSIZE, suite, 1); + ast_verbose(VERBOSE_PREFIX_2 "Using %s for srtp crypto offer.\n", s1); + csocount = sscanf(s1, "%*s (%[^|)]|%[^|)]|%[^|)]", cso[1], cso[2], cso[3]); + if (csocount < 2 || !strcmp(cso[1], "nothing")) { return -1; } + for (i = 1; i <= csocount; i++) { + rc = snprintf(crypto_buf+len, sizeof(crypto_buf), + "a=crypto:%d %s inline:%s\r\n", + i, cso[i], p->local_key64); + if (rc < 1) { + return -1; + } + len += rc; + } if (!(p->a_crypto = ast_strdup(crypto_buf))) { return -1; Index: include/asterisk/res_srtp.h =================================================================== --- include/asterisk/res_srtp.h (revision 303637) +++ include/asterisk/res_srtp.h (working copy) @@ -42,9 +42,9 @@ /* Crypto suites */ enum ast_srtp_suite { - AST_AES_CM_128_HMAC_SHA1_80 = 1, - AST_AES_CM_128_HMAC_SHA1_32 = 2, - AST_F8_128_HMAC_SHA1_80 = 3 + AST_AES_CM_128_HMAC_SHA1_32 = 1, + AST_AES_CM_128_HMAC_SHA1_80 = 2, + AST_F8_128_HMAC_SHA1_80 = 4 }; struct ast_srtp_policy_res {