From 1d2173c7aed0587cdd434e69e89a3972a2642cd8 Mon Sep 17 00:00:00 2001 From: Alexander Traud Date: Wed, 13 Jul 2016 12:24:46 +0200 Subject: res_srtp: Enable AES-256 and AES-GCM. ASTERISK-26190 #close Change-Id: I11326d80edd656524a51a19450e586c583aa0a0b --- channels/chan_sip.c | 28 ++- configure | 374 ++++++++++++++++++++++++++++++++++++++- configure.ac | 10 ++ include/asterisk/autoconfig.h.in | 10 ++ include/asterisk/res_srtp.h | 14 +- include/asterisk/sdp_srtp.h | 18 +- main/sdp_srtp.c | 373 +++++++++++++++++++++++++++++++------- res/res_pjsip_sdp_rtp.c | 26 ++- res/res_srtp.c | 65 +++++-- 9 files changed, 805 insertions(+), 113 deletions(-) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 599ecd639..ca8c0fdf2 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -284,6 +284,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/features_config.h" #include "asterisk/http_websocket.h" #include "asterisk/format_cache.h" +#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT */ /*** DOCUMENTATION @@ -13220,21 +13221,34 @@ static void get_our_media_address(struct sip_pvt *p, int needvideo, int needtext static char *crypto_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32) { + struct ast_sdp_srtp *tmp = srtp; char *a_crypto; - const char *orig_crypto; - if (!srtp || dtls_enabled) { + if (!tmp || dtls_enabled) { return NULL; } - orig_crypto = ast_sdp_srtp_get_attrib(srtp, dtls_enabled, default_taglen_32); - if (ast_strlen_zero(orig_crypto)) { + a_crypto = ast_strdup(""); + if (!a_crypto) { return NULL; } - if (ast_asprintf(&a_crypto, "a=crypto:%s\r\n", orig_crypto) == -1) { - return NULL; - } + do { + char *copy = a_crypto; + const char *orig_crypto = ast_sdp_srtp_get_attrib(tmp, dtls_enabled, default_taglen_32); + + if (ast_strlen_zero(orig_crypto)) { + ast_free(copy); + return NULL; + } + if (ast_asprintf(&a_crypto, "%sa=crypto:%s\r\n", copy, orig_crypto) == -1) { + ast_free(copy); + return NULL; + } + + ast_free(copy); + } while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list))); + return a_crypto; } diff --git a/configure b/configure index aaad401d7..a15ecc674 100755 --- a/configure +++ b/configure @@ -778,6 +778,18 @@ PBX_SRTP_SHUTDOWN SRTP_SHUTDOWN_DIR SRTP_SHUTDOWN_INCLUDE SRTP_SHUTDOWN_LIB +PBX_SRTP_GCM +SRTP_GCM_DIR +SRTP_GCM_INCLUDE +SRTP_GCM_LIB +PBX_SRTP_192 +SRTP_192_DIR +SRTP_192_INCLUDE +SRTP_192_LIB +PBX_SRTP_256 +SRTP_256_DIR +SRTP_256_INCLUDE +SRTP_256_LIB PBX_SRTP SRTP_DIR SRTP_INCLUDE @@ -11395,6 +11407,42 @@ fi +SRTP_256_DESCRIP="SRTP Library AES-256 (ICM)" +SRTP_256_OPTION=srtp +SRTP_256_DIR=${SRTP_DIR} + +PBX_SRTP_256=0 + + + + + + + +SRTP_192_DESCRIP="SRTP Library AES-192 (ICM)" +SRTP_192_OPTION=srtp +SRTP_192_DIR=${SRTP_DIR} + +PBX_SRTP_192=0 + + + + + + + +SRTP_GCM_DESCRIP="SRTP Library AES-128 (GCM) and AES-256 (GCM)" +SRTP_GCM_OPTION=srtp +SRTP_GCM_DIR=${SRTP_DIR} + +PBX_SRTP_GCM=0 + + + + + + + SRTP_SHUTDOWN_DESCRIP="SRTP Library Shutdown Function" SRTP_SHUTDOWN_OPTION=srtp SRTP_SHUTDOWN_DIR=${SRTP_DIR} @@ -13802,7 +13850,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13848,7 +13896,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13872,7 +13920,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13917,7 +13965,7 @@ else We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -13941,7 +13989,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ -#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; @@ -31820,6 +31868,322 @@ rm -f core conftest.err conftest.$ac_objext \ CFLAGS="${saved_cflags}" fi +if test "$PBX_SRTP" = "1"; +then + +if test "x${PBX_SRTP_256}" != "x1" -a "${USE_SRTP_256}" != "no"; then + pbxlibdir="" + # if --with-SRTP_256=DIR has been specified, use it. + if test "x${SRTP_256_DIR}" != "x"; then + if test -d ${SRTP_256_DIR}/lib; then + pbxlibdir="-L${SRTP_256_DIR}/lib" + else + pbxlibdir="-L${SRTP_256_DIR}" + fi + fi + pbxfuncname="crypto_policy_set_aes_cm_256_hmac_sha1_80" + if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers + AST_SRTP_256_FOUND=yes + else + ast_ext_lib_check_save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} " + as_ac_Lib=`$as_echo "ac_cv_lib_srtp_${pbxfuncname}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsrtp" >&5 +$as_echo_n "checking for ${pbxfuncname} in -lsrtp... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsrtp ${pbxlibdir} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ${pbxfuncname} (); +int +main () +{ +return ${pbxfuncname} (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + AST_SRTP_256_FOUND=yes +else + AST_SRTP_256_FOUND=no +fi + + CFLAGS="${ast_ext_lib_check_save_CFLAGS}" + fi + + # now check for the header. + if test "${AST_SRTP_256_FOUND}" = "yes"; then + SRTP_256_LIB="${pbxlibdir} -lsrtp " + # if --with-SRTP_256=DIR has been specified, use it. + if test "x${SRTP_256_DIR}" != "x"; then + SRTP_256_INCLUDE="-I${SRTP_256_DIR}/include" + fi + SRTP_256_INCLUDE="${SRTP_256_INCLUDE} " + if test "x" = "x" ; then # no header, assume found + SRTP_256_HEADER_FOUND="1" + else # check for the header + ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${CPPFLAGS} ${SRTP_256_INCLUDE}" + ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" +if test "x$ac_cv_header_" = xyes; then : + SRTP_256_HEADER_FOUND=1 +else + SRTP_256_HEADER_FOUND=0 +fi + + + CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" + fi + if test "x${SRTP_256_HEADER_FOUND}" = "x0" ; then + SRTP_256_LIB="" + SRTP_256_INCLUDE="" + else + if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library + SRTP_256_LIB="" + fi + PBX_SRTP_256=1 + cat >>confdefs.h <<_ACEOF +#define HAVE_SRTP_256 1 +_ACEOF + + fi + fi +fi + + + +if test "x${PBX_SRTP_192}" != "x1" -a "${USE_SRTP_192}" != "no"; then + pbxlibdir="" + # if --with-SRTP_192=DIR has been specified, use it. + if test "x${SRTP_192_DIR}" != "x"; then + if test -d ${SRTP_192_DIR}/lib; then + pbxlibdir="-L${SRTP_192_DIR}/lib" + else + pbxlibdir="-L${SRTP_192_DIR}" + fi + fi + pbxfuncname="crypto_policy_set_aes_cm_192_hmac_sha1_80" + if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers + AST_SRTP_192_FOUND=yes + else + ast_ext_lib_check_save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} " + as_ac_Lib=`$as_echo "ac_cv_lib_srtp_${pbxfuncname}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsrtp" >&5 +$as_echo_n "checking for ${pbxfuncname} in -lsrtp... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsrtp ${pbxlibdir} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ${pbxfuncname} (); +int +main () +{ +return ${pbxfuncname} (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + AST_SRTP_192_FOUND=yes +else + AST_SRTP_192_FOUND=no +fi + + CFLAGS="${ast_ext_lib_check_save_CFLAGS}" + fi + + # now check for the header. + if test "${AST_SRTP_192_FOUND}" = "yes"; then + SRTP_192_LIB="${pbxlibdir} -lsrtp " + # if --with-SRTP_192=DIR has been specified, use it. + if test "x${SRTP_192_DIR}" != "x"; then + SRTP_192_INCLUDE="-I${SRTP_192_DIR}/include" + fi + SRTP_192_INCLUDE="${SRTP_192_INCLUDE} " + if test "x" = "x" ; then # no header, assume found + SRTP_192_HEADER_FOUND="1" + else # check for the header + ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${CPPFLAGS} ${SRTP_192_INCLUDE}" + ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" +if test "x$ac_cv_header_" = xyes; then : + SRTP_192_HEADER_FOUND=1 +else + SRTP_192_HEADER_FOUND=0 +fi + + + CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" + fi + if test "x${SRTP_192_HEADER_FOUND}" = "x0" ; then + SRTP_192_LIB="" + SRTP_192_INCLUDE="" + else + if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library + SRTP_192_LIB="" + fi + PBX_SRTP_192=1 + cat >>confdefs.h <<_ACEOF +#define HAVE_SRTP_192 1 +_ACEOF + + fi + fi +fi + + + +if test "x${PBX_SRTP_GCM}" != "x1" -a "${USE_SRTP_GCM}" != "no"; then + pbxlibdir="" + # if --with-SRTP_GCM=DIR has been specified, use it. + if test "x${SRTP_GCM_DIR}" != "x"; then + if test -d ${SRTP_GCM_DIR}/lib; then + pbxlibdir="-L${SRTP_GCM_DIR}/lib" + else + pbxlibdir="-L${SRTP_GCM_DIR}" + fi + fi + pbxfuncname="aes_gcm_128_openssl" + if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers + AST_SRTP_GCM_FOUND=yes + else + ast_ext_lib_check_save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} " + as_ac_Lib=`$as_echo "ac_cv_lib_srtp_${pbxfuncname}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsrtp" >&5 +$as_echo_n "checking for ${pbxfuncname} in -lsrtp... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsrtp ${pbxlibdir} $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ${pbxfuncname} (); +int +main () +{ +return ${pbxfuncname} (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + AST_SRTP_GCM_FOUND=yes +else + AST_SRTP_GCM_FOUND=no +fi + + CFLAGS="${ast_ext_lib_check_save_CFLAGS}" + fi + + # now check for the header. + if test "${AST_SRTP_GCM_FOUND}" = "yes"; then + SRTP_GCM_LIB="${pbxlibdir} -lsrtp " + # if --with-SRTP_GCM=DIR has been specified, use it. + if test "x${SRTP_GCM_DIR}" != "x"; then + SRTP_GCM_INCLUDE="-I${SRTP_GCM_DIR}/include" + fi + SRTP_GCM_INCLUDE="${SRTP_GCM_INCLUDE} " + if test "x" = "x" ; then # no header, assume found + SRTP_GCM_HEADER_FOUND="1" + else # check for the header + ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${CPPFLAGS} ${SRTP_GCM_INCLUDE}" + ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" +if test "x$ac_cv_header_" = xyes; then : + SRTP_GCM_HEADER_FOUND=1 +else + SRTP_GCM_HEADER_FOUND=0 +fi + + + CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" + fi + if test "x${SRTP_GCM_HEADER_FOUND}" = "x0" ; then + SRTP_GCM_LIB="" + SRTP_GCM_INCLUDE="" + else + if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library + SRTP_GCM_LIB="" + fi + PBX_SRTP_GCM=1 + cat >>confdefs.h <<_ACEOF +#define HAVE_SRTP_GCM 1 +_ACEOF + + fi + fi +fi + + +fi + if test "x${PBX_SRTP_SHUTDOWN}" != "x1" -a "${USE_SRTP_SHUTDOWN}" != "no"; then pbxlibdir="" diff --git a/configure.ac b/configure.ac index 6256ef7fb..59587c0f4 100644 --- a/configure.ac +++ b/configure.ac @@ -530,6 +530,9 @@ AST_EXT_LIB_SETUP_DEPENDENT([SPEEX_PREPROCESS], [speex_preprocess_ctl], [], [spe AST_EXT_LIB_SETUP([SQLITE], [SQLite], [sqlite]) AST_EXT_LIB_SETUP([SQLITE3], [SQLite], [sqlite3]) AST_EXT_LIB_SETUP([SRTP], [Secure RTP], [srtp]) +AST_EXT_LIB_SETUP_OPTIONAL([SRTP_256], [SRTP Library AES-256 (ICM)], [SRTP], [srtp]) +AST_EXT_LIB_SETUP_OPTIONAL([SRTP_192], [SRTP Library AES-192 (ICM)], [SRTP], [srtp]) +AST_EXT_LIB_SETUP_OPTIONAL([SRTP_GCM], [SRTP Library AES-128 (GCM) and AES-256 (GCM)], [SRTP], [srtp]) AST_EXT_LIB_SETUP_OPTIONAL([SRTP_SHUTDOWN], [SRTP Library Shutdown Function], [SRTP], [srtp]) AST_EXT_LIB_SETUP([OPENSSL], [OpenSSL Secure Sockets Layer], [ssl]) AST_EXT_LIB_SETUP_OPTIONAL([RT], [Realtime functions], [rt]) @@ -2461,6 +2464,13 @@ then CFLAGS="${saved_cflags}" fi +if test "$PBX_SRTP" = "1"; +then + AST_EXT_LIB_CHECK([SRTP_256], [srtp], [crypto_policy_set_aes_cm_256_hmac_sha1_80]) + AST_EXT_LIB_CHECK([SRTP_192], [srtp], [crypto_policy_set_aes_cm_192_hmac_sha1_80]) + AST_EXT_LIB_CHECK([SRTP_GCM], [srtp], [aes_gcm_128_openssl]) +fi + AST_EXT_LIB_CHECK([SRTP_SHUTDOWN], [srtp], [srtp_shutdown], [srtp/srtp.h]) for ver in 2.0 2.2 2.4 2.6; do diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index cdcc4d006..380ac2efb 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -838,6 +838,16 @@ /* Define to 1 if you have the Secure RTP library. */ #undef HAVE_SRTP +/* Define to 1 if SRTP has the SRTP Library AES-192 (ICM) feature. */ +#undef HAVE_SRTP_192 + +/* Define to 1 if SRTP has the SRTP Library AES-256 (ICM) feature. */ +#undef HAVE_SRTP_256 + +/* Define to 1 if SRTP has the SRTP Library AES-128 (GCM) and AES-256 (GCM) + feature. */ +#undef HAVE_SRTP_GCM + /* Define to 1 if SRTP has the SRTP Library Shutdown Function feature. */ #undef HAVE_SRTP_SHUTDOWN diff --git a/include/asterisk/res_srtp.h b/include/asterisk/res_srtp.h index c7fdc40b7..741d4f9ac 100644 --- a/include/asterisk/res_srtp.h +++ b/include/asterisk/res_srtp.h @@ -21,6 +21,8 @@ #ifndef _ASTERISK_RES_SRTP_H #define _ASTERISK_RES_SRTP_H +#include "asterisk.h" /* for size_t */ + struct ast_srtp; struct ast_srtp_policy; struct ast_rtp_instance; @@ -52,9 +54,17 @@ struct ast_srtp_res { /* Crypto suites */ enum ast_srtp_suite { + /* https://www.iana.org/assignments/srtp-protection/srtp-protection.xhtml */ 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, + AST_AES_CM_256_HMAC_SHA1_80, + AST_AES_CM_256_HMAC_SHA1_32, + AST_AES_GCM_128 = 7, + AST_AES_GCM_256, + AST_AES_GCM_128_8, + AST_AES_GCM_256_8, + AST_AES_CM_192_HMAC_SHA1_80, + AST_AES_CM_192_HMAC_SHA1_32, }; struct ast_srtp_policy_res { diff --git a/include/asterisk/sdp_srtp.h b/include/asterisk/sdp_srtp.h index 772c3c38d..5d8f0e7d1 100644 --- a/include/asterisk/sdp_srtp.h +++ b/include/asterisk/sdp_srtp.h @@ -20,8 +20,7 @@ * * \brief SRTP and SDP Security descriptions * - * Specified in RFC 4568 - * Specified in RFC 3711 + * Specified in RFC 3711, 6188, 7714, and 4568 * * \author Mikael Magnusson */ @@ -29,7 +28,8 @@ #ifndef _SDP_SRTP_H #define _SDP_SRTP_H -#include +#include "asterisk/linkedlists.h" /* for AST_LIST_ENTRY */ +#include "asterisk/rtp_engine.h" /* for ast_rtp_instance */ struct ast_sdp_crypto; @@ -37,12 +37,18 @@ struct ast_sdp_crypto; struct ast_sdp_srtp { unsigned int flags; struct ast_sdp_crypto *crypto; + AST_LIST_ENTRY(ast_sdp_srtp) sdp_srtp_list; }; /* SRTP flags */ -#define AST_SRTP_CRYPTO_OFFER_OK (1 << 1) -#define AST_SRTP_CRYPTO_TAG_32 (1 << 2) -#define AST_SRTP_CRYPTO_TAG_80 (1 << 3) +#define AST_SRTP_CRYPTO_OFFER_OK (1 << 1) +#define AST_SRTP_CRYPTO_TAG_32 (1 << 2) +#define AST_SRTP_CRYPTO_TAG_80 (1 << 3) +#define AST_SRTP_CRYPTO_TAG_16 (1 << 4) +#define AST_SRTP_CRYPTO_TAG_8 (1 << 5) +#define AST_SRTP_CRYPTO_AES_192 (1 << 6) +#define AST_SRTP_CRYPTO_AES_256 (1 << 7) +#define AST_SRTP_CRYPTO_OLD_NAME (1 << 8) /*! * \brief allocate a ast_sdp_srtp structure diff --git a/main/sdp_srtp.c b/main/sdp_srtp.c index c92387bc2..a55aedf82 100644 --- a/main/sdp_srtp.c +++ b/main/sdp_srtp.c @@ -20,8 +20,7 @@ * * \brief SRTP and SDP Security descriptions * - * Specified in RFC 3711 - * Specified in RFC 4568 + * Specified in RFC 3711, 6188, 7714, and 4568 * * \author Mikael Magnusson */ @@ -34,15 +33,15 @@ ASTERISK_REGISTER_FILE() -#include -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/sdp_srtp.h" +#include /* for pow */ +#include /* for SRTP_MAX_KEY_LEN, etc */ -#define SRTP_MASTER_LEN 30 -#define SRTP_MASTERKEY_LEN 16 -#define SRTP_MASTERSALT_LEN ((SRTP_MASTER_LEN) - (SRTP_MASTERKEY_LEN)) -#define SRTP_MASTER_LEN64 (((SRTP_MASTER_LEN) * 8 + 5) / 6 + 1) +#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT, etc */ +#include "asterisk/logger.h" /* for ast_log, LOG_ERROR, etc */ +#include "asterisk/rtp_engine.h" /* for ast_rtp_engine_dtls, etc */ +#include "asterisk/sdp_srtp.h" /* for ast_sdp_srtp, etc */ +#include "asterisk/strings.h" /* for ast_strlen_zero */ +#include "asterisk/utils.h" /* for ast_set_flag, ast_test_flag, etc */ extern struct ast_srtp_res *res_srtp; extern struct ast_srtp_policy_res *res_srtp_policy; @@ -59,22 +58,31 @@ struct ast_sdp_srtp *ast_sdp_srtp_alloc(void) void ast_sdp_srtp_destroy(struct ast_sdp_srtp *srtp) { - if (srtp->crypto) { - ast_sdp_crypto_destroy(srtp->crypto); + struct ast_sdp_srtp *next; + + for (next = AST_LIST_NEXT(srtp, sdp_srtp_list); + srtp; + srtp = next, next = srtp ? AST_LIST_NEXT(srtp, sdp_srtp_list) : NULL) { + if (srtp->crypto) { + ast_sdp_crypto_destroy(srtp->crypto); + } + srtp->crypto = NULL; + ast_free(srtp); } - srtp->crypto = NULL; - ast_free(srtp); } struct ast_sdp_crypto { char *a_crypto; - unsigned char local_key[SRTP_MASTER_LEN]; + unsigned char local_key[SRTP_MAX_KEY_LEN]; int tag; - char local_key64[SRTP_MASTER_LEN64]; - unsigned char remote_key[SRTP_MASTER_LEN]; + char local_key64[((SRTP_MAX_KEY_LEN) * 8 + 5) / 6 + 1]; + unsigned char remote_key[SRTP_MAX_KEY_LEN]; + int key_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 ast_sdp_crypto *sdp_crypto_alloc(const int key_len); +static struct ast_sdp_crypto *crypto_init_keys(struct ast_sdp_crypto *p, const int key_len); +static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, int key_len, unsigned long ssrc, int inbound); void ast_sdp_crypto_destroy(struct ast_sdp_crypto *crypto) { @@ -83,57 +91,67 @@ void ast_sdp_crypto_destroy(struct ast_sdp_crypto *crypto) ast_free(crypto); } -struct ast_sdp_crypto *ast_sdp_crypto_alloc(void) +static struct ast_sdp_crypto *crypto_init_keys(struct ast_sdp_crypto *p, const int key_len) { - struct ast_sdp_crypto *p; - int key_len; - unsigned char remote_key[SRTP_MASTER_LEN]; + unsigned char remote_key[key_len]; - if (!ast_rtp_engine_srtp_is_registered()) { + if (res_srtp->get_random(p->local_key, key_len) < 0) { return NULL; } - if (!(p = ast_calloc(1, sizeof(*p)))) { + ast_base64encode(p->local_key64, p->local_key, key_len, sizeof(p->local_key64)); + + p->key_len = ast_base64decode(remote_key, p->local_key64, sizeof(remote_key)); + + if (p->key_len != key_len) { + ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", p->key_len, key_len); return NULL; } - p->tag = 1; - if (res_srtp->get_random(p->local_key, sizeof(p->local_key)) < 0) { - ast_sdp_crypto_destroy(p); + if (memcmp(remote_key, p->local_key, p->key_len)) { + ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); return NULL; } - ast_base64encode(p->local_key64, p->local_key, SRTP_MASTER_LEN, sizeof(p->local_key64)); + ast_debug(1 , "local_key64 %s len %zu\n", p->local_key64, strlen(p->local_key64)); - key_len = ast_base64decode(remote_key, p->local_key64, sizeof(remote_key)); + return p; +} - if (key_len != SRTP_MASTER_LEN) { - ast_log(LOG_ERROR, "base64 encode/decode bad len %d != %d\n", key_len, SRTP_MASTER_LEN); - ast_sdp_crypto_destroy(p); +static struct ast_sdp_crypto *sdp_crypto_alloc(const int key_len) +{ + struct ast_sdp_crypto *p, *result; + + if (!ast_rtp_engine_srtp_is_registered()) { return NULL; } - if (memcmp(remote_key, p->local_key, SRTP_MASTER_LEN)) { - ast_log(LOG_ERROR, "base64 encode/decode bad key\n"); - ast_sdp_crypto_destroy(p); + if (!(p = ast_calloc(1, sizeof(*p)))) { return NULL; } + p->tag = 1; - ast_debug(1 , "local_key64 %s len %zu\n", p->local_key64, strlen(p->local_key64)); + /* default is a key which uses AST_AES_CM_128_HMAC_SHA1_xx */ + result = crypto_init_keys(p, key_len); + if (!result) { + ast_sdp_crypto_destroy(p); + } - return p; + return result; } -static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, unsigned long ssrc, int inbound) +struct ast_sdp_crypto *ast_sdp_crypto_alloc(void) { - const unsigned char *master_salt = NULL; + return sdp_crypto_alloc(SRTP_MASTER_KEY_LEN); +} +static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, const unsigned char *master_key, int key_len, unsigned long ssrc, int inbound) +{ if (!ast_rtp_engine_srtp_is_registered()) { return -1; } - master_salt = master_key + SRTP_MASTERKEY_LEN; - if (res_srtp_policy->set_master_key(policy, master_key, SRTP_MASTERKEY_LEN, master_salt, SRTP_MASTERSALT_LEN) < 0) { + if (res_srtp_policy->set_master_key(policy, master_key, key_len, NULL, 0) < 0) { return -1; } @@ -147,7 +165,7 @@ static int set_crypto_policy(struct ast_srtp_policy *policy, int suite_val, cons return 0; } -static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned char *remote_key, struct ast_rtp_instance *rtp) +static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned char *remote_key, int key_len, struct ast_rtp_instance *rtp) { struct ast_srtp_policy *local_policy = NULL; struct ast_srtp_policy *remote_policy = NULL; @@ -174,11 +192,11 @@ static int crypto_activate(struct ast_sdp_crypto *p, int suite_val, unsigned cha goto err; } - if (set_crypto_policy(local_policy, suite_val, p->local_key, stats.local_ssrc, 0) < 0) { + if (set_crypto_policy(local_policy, suite_val, p->local_key, key_len, stats.local_ssrc, 0) < 0) { goto err; } - if (set_crypto_policy(remote_policy, suite_val, remote_key, 0, 1) < 0) { + if (set_crypto_policy(remote_policy, suite_val, remote_key, key_len, 0, 1) < 0) { goto err; } @@ -215,12 +233,15 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr char *lifetime = NULL; /* Key lifetime (# of RTP packets) */ char *mki = NULL; /* Master Key Index */ int found = 0; - int key_len = 0; + int key_len_from_sdp; + int key_len_expected; + int tag_from_sdp; int suite_val = 0; - unsigned char remote_key[SRTP_MASTER_LEN]; - int taglen = 0; + unsigned char remote_key[SRTP_MAX_KEY_LEN]; + int taglen; double sdes_lifetime; - struct ast_sdp_crypto *crypto = srtp->crypto; + struct ast_sdp_crypto *crypto; + struct ast_sdp_srtp *tmp; if (!ast_rtp_engine_srtp_is_registered()) { return -1; @@ -239,7 +260,7 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr } /* RFC4568 9.1 - tag is 1-9 digits, greater than zero */ - if (sscanf(tag, "%30d", &crypto->tag) != 1 || crypto->tag <= 0 || crypto->tag > 999999999) { + if (sscanf(tag, "%30d", &tag_from_sdp) != 1 || tag_from_sdp <= 0 || tag_from_sdp > 999999999) { ast_log(LOG_WARNING, "Unacceptable a=crypto tag: %s\n", tag); return -1; } @@ -249,16 +270,107 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr return -1; } + /* On egress, Asterisk sent several crypto lines in the SIP/SDP offer + The remote party might have choosen another line than the first */ + for (tmp = srtp; tmp && tmp->crypto && tmp->crypto->tag != tag_from_sdp;) { + tmp = AST_LIST_NEXT(tmp, sdp_srtp_list); + } + if (tmp) { /* tag matched an already created crypto line */ + unsigned int flags = tmp->flags; + + /* Make that crypto line the head of the list, not by changing the + list structure but by exchanging the content of the list members */ + crypto = tmp->crypto; + tmp->crypto = srtp->crypto; + tmp->flags = srtp->flags; + srtp->crypto = crypto; + srtp->flags = flags; + } else { + crypto = srtp->crypto; + crypto->tag = tag_from_sdp; + } + if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_80")) { suite_val = AST_AES_CM_128_HMAC_SHA1_80; ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); - taglen = 80; + key_len_expected = 30; } else if (!strcmp(suite, "AES_CM_128_HMAC_SHA1_32")) { suite_val = AST_AES_CM_128_HMAC_SHA1_32; ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); - taglen = 32; + key_len_expected = 30; +#ifdef HAVE_SRTP_192 + } else if (!strcmp(suite, "AES_192_CM_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + key_len_expected = 38; + } else if (!strcmp(suite, "AES_192_CM_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + key_len_expected = 38; + /* RFC used a different name while in draft, some still use that */ + } else if (!strcmp(suite, "AES_CM_192_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 38; + } else if (!strcmp(suite, "AES_CM_192_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_192_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_192); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 38; +#endif +#ifdef HAVE_SRTP_256 + } else if (!strcmp(suite, "AES_256_CM_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = 46; + } else if (!strcmp(suite, "AES_256_CM_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = 46; + /* RFC used a different name while in draft, some still use that */ + } else if (!strcmp(suite, "AES_CM_256_HMAC_SHA1_80")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_80; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_80); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 46; + } else if (!strcmp(suite, "AES_CM_256_HMAC_SHA1_32")) { + suite_val = AST_AES_CM_256_HMAC_SHA1_32; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_32); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + ast_set_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME); + key_len_expected = 46; +#endif +#ifdef HAVE_SRTP_GCM + } else if (!strcmp(suite, "AEAD_AES_128_GCM")) { + suite_val = AST_AES_GCM_128; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_16); + key_len_expected = AES_128_GCM_KEYSIZE_WSALT; + } else if (!strcmp(suite, "AEAD_AES_256_GCM")) { + suite_val = AST_AES_GCM_256; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_16); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = AES_256_GCM_KEYSIZE_WSALT; + /* RFC contained a (too) short auth tag for RTP media, some still use that */ + } else if (!strcmp(suite, "AEAD_AES_128_GCM_8")) { + suite_val = AST_AES_GCM_128_8; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_8); + key_len_expected = AES_128_GCM_KEYSIZE_WSALT; + } else if (!strcmp(suite, "AEAD_AES_256_GCM_8")) { + suite_val = AST_AES_GCM_256_8; + ast_set_flag(srtp, AST_SRTP_CRYPTO_TAG_8); + ast_set_flag(srtp, AST_SRTP_CRYPTO_AES_256); + key_len_expected = AES_256_GCM_KEYSIZE_WSALT; +#endif } else { - ast_log(LOG_WARNING, "Unsupported crypto suite: %s\n", suite); + ast_verb(1, "Unsupported crypto suite: %s\n", suite); return -1; } @@ -341,23 +453,55 @@ int ast_sdp_crypto_process(struct ast_rtp_instance *rtp, struct ast_sdp_srtp *sr return -1; } - 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); + key_len_from_sdp = ast_base64decode(remote_key, key_salt, sizeof(remote_key)); + if (key_len_from_sdp != key_len_expected) { + ast_log(LOG_WARNING, "SRTP descriptions key length is '%d', not '%d'\n", + key_len_from_sdp, key_len_expected); return -1; } - if (!memcmp(crypto->remote_key, remote_key, sizeof(crypto->remote_key))) { + /* on default, the key is 30 (AES-128); throw that away (only) when the suite changed actually */ + /* ingress: optional, but saves one expensive call to get_random(.) */ + /* egress: required, because the local key was communicated before the remote key is processed */ + if (crypto->key_len != key_len_from_sdp) { + if (!crypto_init_keys(crypto, key_len_from_sdp)) { + return -1; + } + } else if (!memcmp(crypto->remote_key, remote_key, key_len_from_sdp)) { ast_debug(1, "SRTP remote key unchanged; maintaining current policy\n"); ast_set_flag(srtp, AST_SRTP_CRYPTO_OFFER_OK); return 0; } - memcpy(crypto->remote_key, remote_key, sizeof(crypto->remote_key)); - if (crypto_activate(crypto, suite_val, remote_key, rtp) < 0) { + if (key_len_from_sdp > sizeof(crypto->remote_key)) { + ast_log(LOG_ERROR, + "SRTP key buffer is %zu although it must be at least %d bytes\n", + sizeof(crypto->remote_key), key_len_from_sdp); return -1; } + memcpy(crypto->remote_key, remote_key, key_len_from_sdp); + + if (crypto_activate(crypto, suite_val, remote_key, key_len_from_sdp, rtp) < 0) { + return -1; + } + + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_32)) { + taglen = 32; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_16)) { + taglen = 16; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_8)) { + taglen = 8; + } else { + taglen = 80; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_256)) { + taglen |= 0x0200; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_192)) { + taglen |= 0x0100; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME)) { + taglen |= 0x0080; + } /* Finally, rebuild the crypto line */ if (ast_sdp_crypto_build_offer(crypto, taglen)) { @@ -375,10 +519,30 @@ int ast_sdp_crypto_build_offer(struct ast_sdp_crypto *p, int taglen) ast_free(p->a_crypto); } - if (ast_asprintf(&p->a_crypto, "%d AES_CM_128_HMAC_SHA1_%i inline:%s", - p->tag, taglen, p->local_key64) == -1) { - ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); - return -1; + if ((taglen & 0x007f) == 8) { + if (ast_asprintf(&p->a_crypto, "%d AEAD_AES_%d_GCM_%d inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), taglen & 0x007f, p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } + } else if ((taglen & 0x007f) == 16) { + if (ast_asprintf(&p->a_crypto, "%d AEAD_AES_%d_GCM inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } + } else if ((taglen & 0x0300) && !(taglen & 0x0080)) { + if (ast_asprintf(&p->a_crypto, "%d AES_%d_CM_HMAC_SHA1_%d inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), taglen & 0x007f, p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } + } else { + if (ast_asprintf(&p->a_crypto, "%d AES_CM_%d_HMAC_SHA1_%d inline:%s", + p->tag, 128 + ((taglen & 0x0300) >> 2), taglen & 0x007f, p->local_key64) == -1) { + ast_log(LOG_ERROR, "Could not allocate memory for crypto line\n"); + return -1; + } } ast_debug(1, "Crypto line: a=crypto:%s\n", p->a_crypto); @@ -388,7 +552,7 @@ int ast_sdp_crypto_build_offer(struct ast_sdp_crypto *p, int taglen) const char *ast_sdp_srtp_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32) { - int taglen = default_taglen_32 ? 32 : 80; + int taglen; if (!srtp) { return NULL; @@ -396,7 +560,72 @@ const char *ast_sdp_srtp_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, /* Set encryption properties */ if (!srtp->crypto) { - srtp->crypto = ast_sdp_crypto_alloc(); + + if (AST_LIST_NEXT(srtp, sdp_srtp_list)) { + srtp->crypto = ast_sdp_crypto_alloc(); + ast_log(LOG_ERROR, "SRTP SDP list was not empty\n"); + } else { + const int len = default_taglen_32 ? AST_SRTP_CRYPTO_TAG_32 : AST_SRTP_CRYPTO_TAG_80; + const int attr[][3] = { + /* This array creates the following list: + * a=crypto:1 AES_CM_128_HMAC_SHA1_ ... + * a=crypto:2 AEAD_AES_128_GCM ... + * a=crypto:3 AES_256_CM_HMAC_SHA1_ ... + * a=crypto:4 AEAD_AES_256_GCM ... + * a=crypto:5 AES_192_CM_HMAC_SHA1_ ... + * something like 'AEAD_AES_192_GCM' is not specified by the RFCs + * + * If you want to prefer another crypto suite or you want to + * exclude a suite, change this array and recompile Asterisk. + * This list cannot be changed from rtp.conf because you should + * know what you are doing. Especially AES-192 and AES-GCM are + * broken in many VoIP clients, see + * https://github.com/cisco/libsrtp/pull/170 + * https://github.com/cisco/libsrtp/pull/184 + * Furthermore, AES-GCM uses a shorter crypto-suite string which + * causes Nokia phones based on Symbian/S60 to reject the whole + * INVITE with status 500, even if a matching suite was offered. + * AES-256 might just waste your processor cycles, especially if + * your TLS transport is not secured with equivalent grade, see + * https://security.stackexchange.com/q/61361 + * Therefore, AES-128 was preferred here. + * + * If you want to enable one of those defines, please, go for + * CFLAGS='-DENABLE_SRTP_AES_GCM' ./configure && sudo make install + */ + { len, 0, 30 }, +#if defined(HAVE_SRTP_GCM) && defined(ENABLE_SRTP_AES_GCM) + { AST_SRTP_CRYPTO_TAG_16, 0, AES_128_GCM_KEYSIZE_WSALT }, +#endif +#if defined(HAVE_SRTP_256) && defined(ENABLE_SRTP_AES_256) + { len, AST_SRTP_CRYPTO_AES_256, 46 }, +#endif +#if defined(HAVE_SRTP_GCM) && defined(ENABLE_SRTP_AES_GCM) && defined(ENABLE_SRTP_AES_256) + { AST_SRTP_CRYPTO_TAG_16, AST_SRTP_CRYPTO_AES_256, AES_256_GCM_KEYSIZE_WSALT }, +#endif +#if defined(HAVE_SRTP_192) && defined(ENABLE_SRTP_AES_192) + { len, AST_SRTP_CRYPTO_AES_192, 38 }, +#endif + }; + struct ast_sdp_srtp *tmp = srtp; + int i; + + for (i = 0; i < ARRAY_LEN(attr); i++) { + if (attr[i][0]) { + ast_set_flag(tmp, attr[i][0]); + } + if (attr[i][1]) { + ast_set_flag(tmp, attr[i][1]); + } + tmp->crypto = sdp_crypto_alloc(attr[i][2]); /* key_len */ + tmp->crypto->tag = (i + 1); /* tag starts at 1 */ + + if (i < ARRAY_LEN(attr) - 1) { + AST_LIST_NEXT(tmp, sdp_srtp_list) = ast_sdp_srtp_alloc(); + tmp = AST_LIST_NEXT(tmp, sdp_srtp_list); + } + } + } } if (dtls_enabled) { @@ -409,6 +638,20 @@ const char *ast_sdp_srtp_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, taglen = 80; } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_32)) { taglen = 32; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_16)) { + taglen = 16; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_TAG_8)) { + taglen = 8; + } else { + taglen = default_taglen_32 ? 32 : 80; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_256)) { + taglen |= 0x0200; + } else if (ast_test_flag(srtp, AST_SRTP_CRYPTO_AES_192)) { + taglen |= 0x0100; + } + if (ast_test_flag(srtp, AST_SRTP_CRYPTO_OLD_NAME)) { + taglen |= 0x0080; } if (srtp->crypto && (ast_sdp_crypto_build_offer(srtp->crypto, taglen) >= 0)) { diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 048209ce1..6610ef126 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -51,6 +51,7 @@ ASTERISK_REGISTER_FILE() #include "asterisk/acl.h" #include "asterisk/sdp_srtp.h" #include "asterisk/dsp.h" +#include "asterisk/linkedlists.h" /* for AST_LIST_NEXT */ #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" @@ -938,6 +939,7 @@ static int add_crypto_to_stream(struct ast_sip_session *session, enum ast_rtp_dtls_hash hash; const char *crypto_attribute; struct ast_rtp_engine_dtls *dtls; + struct ast_sdp_srtp *tmp; static const pj_str_t STR_NEW = { "new", 3 }; static const pj_str_t STR_EXISTING = { "existing", 8 }; static const pj_str_t STR_ACTIVE = { "active", 6 }; @@ -957,16 +959,22 @@ static int add_crypto_to_stream(struct ast_sip_session *session, } } - crypto_attribute = ast_sdp_srtp_get_attrib(session_media->srtp, - 0 /* DTLS running? No */, - session->endpoint->media.rtp.srtp_tag_32 /* 32 byte tag length? */); - if (!crypto_attribute) { - /* No crypto attribute to add, bad news */ - return -1; - } + tmp = session_media->srtp; + + do { + crypto_attribute = ast_sdp_srtp_get_attrib(tmp, + 0 /* DTLS running? No */, + session->endpoint->media.rtp.srtp_tag_32 /* 32 byte tag length? */); + if (!crypto_attribute) { + /* No crypto attribute to add, bad news */ + return -1; + } + + attr = pjmedia_sdp_attr_create(pool, "crypto", + pj_cstr(&stmp, crypto_attribute)); + media->attr[media->attr_count++] = attr; + } while ((tmp = AST_LIST_NEXT(tmp, sdp_srtp_list))); - attr = pjmedia_sdp_attr_create(pool, "crypto", pj_cstr(&stmp, crypto_attribute)); - media->attr[media->attr_count++] = attr; break; case AST_SIP_MEDIA_ENCRYPT_DTLS: if (setup_dtls_srtp(session, session_media)) { diff --git a/res/res_srtp.c b/res/res_srtp.c index 0b1fb73e7..59fda76dd 100644 --- a/res/res_srtp.c +++ b/res/res_srtp.c @@ -35,7 +35,7 @@ /* See https://wiki.asterisk.org/wiki/display/AST/Secure+Calling */ -#include "asterisk.h" +#include "asterisk.h" /* for NULL, size_t, memcpy, etc */ ASTERISK_REGISTER_FILE() @@ -46,12 +46,13 @@ ASTERISK_REGISTER_FILE() #include #endif -#include "asterisk/lock.h" -#include "asterisk/sched.h" -#include "asterisk/module.h" -#include "asterisk/options.h" -#include "asterisk/rtp_engine.h" -#include "asterisk/astobj2.h" +#include "asterisk/astobj2.h" /* for ao2_t_ref, etc */ +#include "asterisk/frame.h" /* for AST_FRIENDLY_OFFSET */ +#include "asterisk/logger.h" /* for ast_log, ast_debug, etc */ +#include "asterisk/module.h" /* for ast_module_info, etc */ +#include "asterisk/res_srtp.h" /* for ast_srtp_cb, ast_srtp_suite, etc */ +#include "asterisk/rtp_engine.h" /* for ast_rtp_engine_register_srtp, etc */ +#include "asterisk/utils.h" /* for ast_free, ast_calloc */ struct ast_srtp { struct ast_rtp_instance *rtp; @@ -257,23 +258,49 @@ static int policy_set_suite(crypto_policy_t *p, enum ast_srtp_suite suite) { switch (suite) { case AST_AES_CM_128_HMAC_SHA1_80: - p->cipher_type = AES_128_ICM; - p->cipher_key_len = 30; - p->auth_type = HMAC_SHA1; - p->auth_key_len = 20; - p->auth_tag_len = 10; - p->sec_serv = sec_serv_conf_and_auth; + crypto_policy_set_aes_cm_128_hmac_sha1_80(p); return 0; case AST_AES_CM_128_HMAC_SHA1_32: - p->cipher_type = AES_128_ICM; - p->cipher_key_len = 30; - p->auth_type = HMAC_SHA1; - p->auth_key_len = 20; - p->auth_tag_len = 4; - p->sec_serv = sec_serv_conf_and_auth; + crypto_policy_set_aes_cm_128_hmac_sha1_32(p); return 0; +#ifdef HAVE_SRTP_192 + case AST_AES_CM_192_HMAC_SHA1_80: + crypto_policy_set_aes_cm_192_hmac_sha1_80(p); + return 0; + + case AST_AES_CM_192_HMAC_SHA1_32: + crypto_policy_set_aes_cm_192_hmac_sha1_32(p); + return 0; +#endif +#ifdef HAVE_SRTP_256 + case AST_AES_CM_256_HMAC_SHA1_80: + crypto_policy_set_aes_cm_256_hmac_sha1_80(p); + return 0; + + case AST_AES_CM_256_HMAC_SHA1_32: + crypto_policy_set_aes_cm_256_hmac_sha1_32(p); + return 0; +#endif +#ifdef HAVE_SRTP_GCM + case AST_AES_GCM_128: + crypto_policy_set_aes_gcm_128_16_auth(p); + return 0; + + case AST_AES_GCM_256: + crypto_policy_set_aes_gcm_256_16_auth(p); + return 0; + + case AST_AES_GCM_128_8: + crypto_policy_set_aes_gcm_128_8_auth(p); + return 0; + + case AST_AES_GCM_256_8: + crypto_policy_set_aes_gcm_256_8_auth(p); + return 0; +#endif + default: ast_log(LOG_ERROR, "Invalid crypto suite: %u\n", suite); return -1; -- cgit v1.2.3