From 833235b94139bdf43991eb0a5dd59046ef6bb3ac Mon Sep 17 00:00:00 2001 From: Dragos Oancea Date: Fri, 9 Apr 2021 15:45:17 +0000 Subject: [PATCH] [mod_sofia] Add RFC8760 (sha-256, sha-512) enable on the sip profile, eg: --- src/mod/endpoints/mod_sofia/mod_sofia.h | 13 ++ src/mod/endpoints/mod_sofia/sofia.c | 11 ++ src/mod/endpoints/mod_sofia/sofia_reg.c | 162 ++++++++++++++++++++++-- 3 files changed, 172 insertions(+), 14 deletions(-) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index db709b9721..3c2d02a5a7 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -369,6 +369,8 @@ typedef enum { #define SOFIA_MAX_MSG_QUEUE 64 #define SOFIA_MSG_QUEUE_SIZE 1000 +#define SOFIA_MAX_REG_ALGS 7 /* rfc8760 */ + struct mod_sofia_globals { switch_memory_pool_t *pool; switch_hash_t *profile_hash; @@ -599,6 +601,13 @@ typedef enum { KA_INFO } ka_type_t; +typedef enum { + ALG_MD5 = (1 << 0), + ALG_SHA256 = (1 << 1), + ALG_SHA512 = (1 << 2), + ALG_NONE = (1 << 3), +} sofia_auth_algs_t; + struct sofia_profile { int debug; int parse_invite_tel_params; @@ -789,6 +798,8 @@ struct sofia_profile { char *rfc7989_filter; char *acl_inbound_x_token_header; char *acl_proxy_x_token_header; + uint8_t rfc8760_algs_count; + sofia_auth_algs_t auth_algs[SOFIA_MAX_REG_ALGS]; }; @@ -1260,6 +1271,8 @@ void sofia_reg_close_handles(sofia_profile_t *profile); void write_csta_xml_chunk(switch_event_t *event, switch_stream_handle_t stream, const char *csta_event, char *fwd_type); void sofia_glue_clear_soa(switch_core_session_t *session, switch_bool_t partner); +sofia_auth_algs_t sofia_alg_str2id(char *algorithm, switch_bool_t permissive); +switch_status_t sofia_make_digest(sofia_auth_algs_t use_alg, char **digest, const void *input, unsigned int *outputlen); /* For Emacs: * Local Variables: diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index b76a804e8e..82fb6471c9 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -6039,6 +6039,17 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name) profile->proxy_notify_events = switch_core_strdup(profile->pool, val); } else if (!strcasecmp(var, "proxy-info-content-types")) { profile->proxy_info_content_types = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "rfc8760-auth-algorithms")) { + /* the order in which algorithms are allowed matters */ + char *algs_arr[100] = { 0 }; + uint8_t algs = switch_separate_string(val, ',', algs_arr, (sizeof(algs_arr) / sizeof(algs_arr[0]))); + if (algs && algs < SOFIA_MAX_REG_ALGS) { + int i; + for (i = 0; i < algs && algs_arr[i]; i++) { + profile->auth_algs[i] = sofia_alg_str2id(algs_arr[i], SWITCH_TRUE); + } + profile->rfc8760_algs_count = algs; + } } } diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 582de54cb4..6263e05170 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -1123,13 +1123,22 @@ switch_console_callback_match_t *sofia_reg_find_reg_url_with_positive_expires_mu return cbt.list; } +static char * sofia_alg_to_str(sofia_auth_algs_t alg) +{ + if (alg == ALG_SHA256) + return "SHA-256"; + if (alg == ALG_SHA512) + return "SHA-512-256"; + return "MD5"; +} void sofia_reg_auth_challenge(sofia_profile_t *profile, nua_handle_t *nh, sofia_dispatch_event_t *de, sofia_regtype_t regtype, const char *realm, int stale, long exptime) { switch_uuid_t uuid; char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; - char *sql, *auth_str; + char *sql, *auth_str = NULL; + char *auth_str_rfc8760[SOFIA_MAX_REG_ALGS] = {0}; msg_t *msg = NULL; @@ -1147,14 +1156,53 @@ void sofia_reg_auth_challenge(sofia_profile_t *profile, nua_handle_t *nh, sofia_ switch_assert(sql != NULL); sofia_glue_execute_sql_now(profile, &sql, SWITCH_TRUE); - auth_str = switch_mprintf("Digest realm=\"%q\", nonce=\"%q\",%s algorithm=MD5, qop=\"auth\"", realm, uuid_str, stale ? " stale=true," : ""); + if (!profile->rfc8760_algs_count) { + auth_str = switch_mprintf("Digest realm=\"%q\", nonce=\"%q\",%s algorithm=MD5, qop=\"auth\"", realm, uuid_str, stale ? " stale=true," : ""); + } else { + int i; + for (i = 0 ; i < profile->rfc8760_algs_count; i++) { + if (profile->auth_algs[i] != ALG_NONE) { + auth_str_rfc8760[i] = switch_mprintf("Digest realm=\"%q\", nonce=\"%q\",%s algorithm=%s, qop=\"auth\"", realm, uuid_str, stale ? " stale=true," : "", sofia_alg_to_str(profile->auth_algs[i])); + } + } + } if (regtype == REG_REGISTER) { - nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END()); + if (!profile->rfc8760_algs_count) { + nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END()); + } else { + int i; + nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), + TAG_IF(auth_str_rfc8760[0], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[0])), TAG_IF(auth_str_rfc8760[1], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[1])), + TAG_IF(auth_str_rfc8760[2], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[2])), TAG_IF(auth_str_rfc8760[3], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[3])), + TAG_IF(auth_str_rfc8760[4], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[4])), TAG_IF(auth_str_rfc8760[5], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[5])), + TAG_IF(auth_str_rfc8760[6], SIPTAG_WWW_AUTHENTICATE_STR(auth_str_rfc8760[6])), TAG_END()); + for (i = 0 ; i < profile->rfc8760_algs_count; i++) { + switch_safe_free(auth_str_rfc8760[i]); + } + } } else if (regtype == REG_INVITE) { - nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED, + if (!profile->rfc8760_algs_count) { + nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED, + TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), + SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END()); + } else { + int i; + nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED, TAG_IF(msg, NUTAG_WITH_THIS_MSG(msg)), - SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END()); + TAG_IF(auth_str_rfc8760[0], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[0])), + TAG_IF(auth_str_rfc8760[1], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[1])), + TAG_IF(auth_str_rfc8760[2], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[2])), + TAG_IF(auth_str_rfc8760[3], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[3])), + TAG_IF(auth_str_rfc8760[4], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[4])), + TAG_IF(auth_str_rfc8760[5], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[5])), + TAG_IF(auth_str_rfc8760[6], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str_rfc8760[6])), + TAG_END()); + for (i = 0 ; i < profile->rfc8760_algs_count; i++) { + switch_safe_free(auth_str_rfc8760[i]); + } + } + } switch_safe_free(auth_str); @@ -2710,6 +2758,54 @@ static int sofia_reg_regcount_callback(void *pArg, int argc, char **argv, char * return 0; } +static switch_bool_t sofia_alg_is_allowed(sofia_profile_t *profile, sofia_auth_algs_t alg) +{ + int i; + + for (i = 0 ; i < SOFIA_MAX_REG_ALGS; i++) { + if (profile->auth_algs[i] == alg) { + return SWITCH_TRUE; + } + } + return SWITCH_FALSE; +} + +/*we are more permissive with the alg names that come from cfg */ +sofia_auth_algs_t sofia_alg_str2id(char *algorithm, switch_bool_t permissive) +{ + if (!strcasecmp(algorithm, "MD5") || (permissive && !strcasecmp(algorithm, "MD-5"))) { + return ALG_MD5; + } + if (!strcasecmp(algorithm, "SHA-256") || (permissive && !strcasecmp(algorithm, "SHA256"))) { + return ALG_SHA256; + } + if (!strcasecmp(algorithm, "SHA-512-256") || (permissive && !strcasecmp(algorithm, "SHA512")) + || (permissive && !strcasecmp(algorithm, "SHA512-256")) || (permissive && !strcasecmp(algorithm, "SHA-512"))) { + return ALG_SHA512; + } + + return ALG_NONE; +} + +switch_status_t sofia_make_digest(sofia_auth_algs_t use_alg, char **digest, const void *input, unsigned int *outputlen) +{ + switch (use_alg) + { + case ALG_MD5: + switch_digest_string("md5", digest, input, strlen((char *)input), outputlen); + break; + case ALG_SHA256: + switch_digest_string("sha256", digest, input, strlen((char *)input), outputlen); + break; + case ALG_SHA512: + switch_digest_string("sha512-256", digest, input, strlen((char *)input), outputlen); + break; + default: + return SWITCH_STATUS_FALSE; + } + return SWITCH_STATUS_SUCCESS; +} + auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, sip_authorization_t const *authorization, sip_t const *sip, @@ -2724,9 +2820,8 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, { int indexnum; const char *cur; - char uridigest[SWITCH_MD5_DIGEST_STRING_SIZE]; - char bigdigest[SWITCH_MD5_DIGEST_STRING_SIZE]; - char *username, *realm, *nonce, *uri, *qop, *cnonce, *nc, *response, *input = NULL, *input2 = NULL; + char *uridigest = NULL, *bigdigest = NULL, *hexdigest = NULL; + char *username, *realm, *nonce, *uri, *qop, *cnonce, *nc, *response, *algorithm, *input = NULL, *input2 = NULL; auth_res_t ret = AUTH_FORBIDDEN; int first = 0; const char *passwd = NULL; @@ -2737,7 +2832,6 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, char *sql; char *number_alias = NULL; switch_xml_t user = NULL, param, uparams; - char hexdigest[SWITCH_MD5_DIGEST_STRING_SIZE] = ""; char *domain_name = NULL; switch_event_t *params = NULL; const char *auth_acl = NULL; @@ -2747,9 +2841,12 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, const char *user_agent_filter = profile->user_agent_filter; uint32_t max_registrations_perext = profile->max_registrations_perext; char client_port[16]; + uint8_t use_alg; + unsigned int digest_outputlen; + snprintf(client_port, 15, "%d", network_port); - username = realm = nonce = uri = qop = cnonce = nc = response = NULL; + username = realm = nonce = uri = qop = cnonce = nc = response = algorithm = NULL; if (authorization->au_params) { for (indexnum = 0; (cur = authorization->au_params[indexnum]); indexnum++) { @@ -2782,7 +2879,10 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, response = strdup(val); } else if (!strcasecmp(var, "nc") && !nc) { nc = strdup(val); + } else if (!strcasecmp(var, "algorithm") && !algorithm) { + algorithm = strdup(val); } + } free(work); @@ -2807,6 +2907,26 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, } } + if (algorithm) { + if (profile->rfc8760_algs_count) { + switch_bool_t not_allowed = SWITCH_FALSE; + sofia_auth_algs_t alg_id = sofia_alg_str2id(algorithm, SWITCH_FALSE); + if ((alg_id == ALG_NONE) || !sofia_alg_is_allowed(profile, alg_id)) { + not_allowed = SWITCH_TRUE; + } else { + use_alg = alg_id; + } + if (not_allowed) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SIP auth hash algorithm explicitly not allowed. [%s]\n", algorithm); + goto end; + } + } else { + use_alg = ALG_MD5; + } + } else { + use_alg = ALG_MD5; + } + user_agent = (sip && sip->sip_user_agent) ? sip->sip_user_agent->g_string : "unknown"; if (zstr(np)) { @@ -3059,10 +3179,14 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, if (!a1_hash) { input = switch_mprintf("%s:%s:%s", username, realm, passwd); - switch_md5_string(hexdigest, (void *) input, strlen(input)); + + if (sofia_make_digest(use_alg, &hexdigest, (void *)input, &digest_outputlen) != SWITCH_STATUS_SUCCESS) { + switch_safe_free(input); + goto end; + } + switch_safe_free(input); a1_hash = hexdigest; - } if (user_agent_filter) { @@ -3110,7 +3234,10 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, for_the_sake_of_interop: if ((input = switch_mprintf("%s:%q", regstr, uri))) { - switch_md5_string(uridigest, (void *) input, strlen(input)); + if (sofia_make_digest(use_alg, &uridigest, (void *)input, &digest_outputlen) != SWITCH_STATUS_SUCCESS) { + switch_safe_free(input); + goto end; + } } if (nc && cnonce && qop) { @@ -3120,7 +3247,10 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, } if (input2) { - switch_md5_string(bigdigest, (void *) input2, strlen(input2)); + if (sofia_make_digest(use_alg, &bigdigest, (void *)input2, &digest_outputlen) != SWITCH_STATUS_SUCCESS) { + switch_safe_free(input2); + goto end; + } } if (input2 && !strcasecmp(bigdigest, response)) { @@ -3286,6 +3416,10 @@ auth_res_t sofia_reg_parse_auth(sofia_profile_t *profile, switch_safe_free(cnonce); switch_safe_free(nc); switch_safe_free(response); + switch_safe_free(algorithm); + switch_safe_free(uridigest); + switch_safe_free(bigdigest); + switch_safe_free(hexdigest); if (reg_count && !*reg_count) { if ((ret == AUTH_OK || ret == AUTH_RENEWED)) {