[mod_sofia] Add RFC8760 (sha-256, sha-512)

enable on the sip profile, eg:
<param name="rfc8760-auth-algorithms" value="sha-256,md5"/>
This commit is contained in:
Dragos Oancea 2021-04-09 15:45:17 +00:00 committed by Chris Rienzo
parent fd27504a7d
commit 833235b941
3 changed files with 172 additions and 14 deletions

View File

@ -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:

View File

@ -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;
}
}
}

View File

@ -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)) {