]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
[mod_sofia] Add RFC8760 (sha-256, sha-512)
authorDragos Oancea <dragos@signalwire.com>
Fri, 9 Apr 2021 15:45:17 +0000 (15:45 +0000)
committerChris Rienzo <chris@signalwire.com>
Mon, 12 Apr 2021 13:57:56 +0000 (09:57 -0400)
enable on the sip profile, eg:
<param name="rfc8760-auth-algorithms" value="sha-256,md5"/>

src/mod/endpoints/mod_sofia/mod_sofia.h
src/mod/endpoints/mod_sofia/sofia.c
src/mod/endpoints/mod_sofia/sofia_reg.c

index db709b9721e581a84fb08478df769dae4618b859..3c2d02a5a74e9e624e82cdaae93a944db29fe9c6 100644 (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:
index b76a804e8eed85d9698bca474eea3637e68ed1db..82fb6471c97faeee3c481caaaa79b28d256cbebe 100644 (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;
+                                               }
                                        }
                                }
 
index 582de54cb4b878f4d4b2161c93742f09b3b21bdc..6263e05170d6895a436ea8e90b383db482c390c0 100644 (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)) {