/* * Asterisk -- An open source telephony toolkit. * * Copyright (C) 2022, Commend International * * Maximilian Fridrich * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact * any of the maintainers of this project for assistance; * the project provides a web site, mailing lists and IRC * channels for your use. * * This program is free software, distributed under the terms of * the GNU General Public License Version 2. See the LICENSE file * at the top of the source tree. */ /*! * \file * * \brief Interact with security agreement negotiations and mechanisms * * \author Maximilian Fridrich */ #include "asterisk.h" #include #include "asterisk/res_pjsip.h" static struct ast_sip_security_mechanism *ast_sip_security_mechanisms_alloc(size_t n_params) { struct ast_sip_security_mechanism *mech; mech = ast_calloc(1, sizeof(struct ast_sip_security_mechanism)); if (mech == NULL) { return NULL; } mech->qvalue = 0.0; if (AST_VECTOR_INIT(&mech->mechanism_parameters, n_params) != 0) { ast_free(mech); return NULL; } return mech; } static struct ast_sip_security_mechanism *ast_sip_security_mechanisms_copy( const struct ast_sip_security_mechanism *src) { struct ast_sip_security_mechanism *dst = NULL; int i, n_params; char *param; n_params = AST_VECTOR_SIZE(&src->mechanism_parameters); dst = ast_sip_security_mechanisms_alloc(n_params); if (dst == NULL) { return NULL; } dst->type = src->type; dst->qvalue = src->qvalue; for (i = 0; i < n_params; i++) { param = ast_strdup(AST_VECTOR_GET(&src->mechanism_parameters, i)); AST_VECTOR_APPEND(&dst->mechanism_parameters, param); } return dst; } static void ast_sip_security_mechanisms_destroy(struct ast_sip_security_mechanism *mech) { int i; for (i = 0; i < AST_VECTOR_SIZE(&mech->mechanism_parameters); i++) { ast_free(AST_VECTOR_GET(&mech->mechanism_parameters, i)); } AST_VECTOR_FREE(&mech->mechanism_parameters); ast_free(mech); } void ast_sip_security_mechanisms_vector_copy(struct ast_sip_security_mechanism_vector *dst, const struct ast_sip_security_mechanism_vector *src) { struct ast_sip_security_mechanism *mech; int i; ast_sip_security_mechanisms_vector_destroy(dst); for (i = 0; i < AST_VECTOR_SIZE(src); i++) { mech = AST_VECTOR_GET(src, i); AST_VECTOR_APPEND(dst, ast_sip_security_mechanisms_copy(mech)); } }; void ast_sip_security_mechanisms_vector_destroy(struct ast_sip_security_mechanism_vector *security_mechanisms) { struct ast_sip_security_mechanism *mech; int i; if (!security_mechanisms) { return; } for (i = 0; i < AST_VECTOR_SIZE(security_mechanisms); i++) { mech = AST_VECTOR_GET(security_mechanisms, i); ast_sip_security_mechanisms_destroy(mech); } AST_VECTOR_FREE(security_mechanisms); } static int ast_sip_str_to_security_mechanism_type(const char *security_mechanism) { int result = -1; if (!strcasecmp(security_mechanism, "msrp-tls")) { result = AST_SIP_SECURITY_MECH_MSRP_TLS; } else if (!strcasecmp(security_mechanism, "sdes-srtp")) { result = AST_SIP_SECURITY_MECH_SDES_SRTP; } else if (!strcasecmp(security_mechanism, "dtls-srtp")) { result = AST_SIP_SECURITY_MECH_DTLS_SRTP; } return result; } static char *ast_sip_security_mechanism_type_to_str(enum ast_sip_security_mechanism_type mech_type) { if (mech_type == AST_SIP_SECURITY_MECH_MSRP_TLS) { return "msrp-tls"; } else if (mech_type == AST_SIP_SECURITY_MECH_SDES_SRTP) { return "sdes-srtp"; } else if (mech_type == AST_SIP_SECURITY_MECH_DTLS_SRTP) { return "dtls-srtp"; } else { return NULL; } } static int security_mechanism_to_str(const struct ast_sip_security_mechanism *security_mechanism, int add_qvalue, char **buf) { size_t size; size_t buf_size = 128; int i; char *ret = ast_calloc(buf_size, sizeof(char)); if (ret == NULL) { return ENOMEM; } if (security_mechanism == NULL) { ast_free(ret); return EINVAL; } snprintf(ret, buf_size - 1, "%s", ast_sip_security_mechanism_type_to_str(security_mechanism->type)); if (add_qvalue) { snprintf(ret + strlen(ret), buf_size - 1, ";q=%f.4", security_mechanism->qvalue); } size = AST_VECTOR_SIZE(&security_mechanism->mechanism_parameters); for (i = 0; i < size; ++i) { snprintf(ret + strlen(ret), buf_size - 1, ";%s", AST_VECTOR_GET(&security_mechanism->mechanism_parameters, i)); } *buf = ret; return 0; } int ast_sip_security_mechanisms_to_str(const struct ast_sip_security_mechanism_vector *security_mechanisms, int add_qvalue, char **buf) { size_t vec_size; struct ast_sip_security_mechanism *mech; char *tmp_buf; char ret[512]; size_t i; if (!security_mechanisms) { return -1; } vec_size = AST_VECTOR_SIZE(security_mechanisms); ret[0] = '\0'; for (i = 0; i < vec_size; ++i) { mech = AST_VECTOR_GET(security_mechanisms, i); if (security_mechanism_to_str(mech, add_qvalue, &tmp_buf)) { continue; } snprintf(ret + strlen(ret), sizeof(ret) - 1, "%s%s", tmp_buf, i == vec_size - 1 ? "" : ", "); ast_free(tmp_buf); } *buf = ast_strdup(ret); return 0; } void ast_sip_remove_headers_by_name_and_value(pjsip_msg *msg, const pj_str_t *hdr_name, const char* value) { struct pjsip_generic_string_hdr *hdr = pjsip_msg_find_hdr_by_name(msg, hdr_name, NULL); for (; hdr; hdr = pjsip_msg_find_hdr_by_name(msg, hdr_name, hdr->next)) { if (value == NULL || !pj_strcmp2(&hdr->hvalue, value)) { pj_list_erase(hdr); } if (hdr->next == hdr) { break; } } } /*! * \internal * \brief Parses a string representing a q_value to a float. * * Valid q values must be in the range from 0.0 to 1.0 inclusively. * * \param q_value * \retval The parsed qvalue or -1.0 on failure. */ static float parse_qvalue(const char *q_value) { char *end; float ret = strtof(q_value, &end); if (end == q_value) { /* Not a number. */ return -1.0; } else if ('\0' != *end) { /* Extra character at end of input. */ return -1.0; } else if (ret > 1.0 || ret < 0.0) { /* Out of valid range. */ return -1.0; } return ret; } int ast_sip_str_to_security_mechanism(struct ast_sip_security_mechanism **security_mechanism, const char *value) { struct ast_sip_security_mechanism *mech; char *param; char *tmp; char *mechanism = ast_strdupa(value); int err = 0; int type = -1; mech = ast_sip_security_mechanisms_alloc(1); if (!mech) { err = ENOMEM; goto out; } tmp = ast_strsep(&mechanism, ';', AST_STRSEP_ALL); type = ast_sip_str_to_security_mechanism_type(tmp); if (type == -1) { err = EINVAL; goto out; } mech->type = type; while ((param = ast_strsep(&mechanism, ';', AST_STRSEP_ALL))) { if (!param) { err = EINVAL; goto out; } if (!strncmp(param, "q=", 2)) { mech->qvalue = parse_qvalue(¶m[2]); if (mech->qvalue < 0.0) { err = EINVAL; goto out; } continue; } param = ast_strdup(param); AST_VECTOR_APPEND(&mech->mechanism_parameters, param); } *security_mechanism = mech; out: if (err && (mech != NULL)) { ast_sip_security_mechanisms_destroy(mech); } return err; } int ast_sip_add_security_headers(struct ast_sip_security_mechanism_vector *security_mechanisms, const char *header_name, int add_qval, pjsip_tx_data *tdata) { struct ast_sip_security_mechanism *mech; char *buf; int mech_cnt; int i; int add_qvalue = 1; static const pj_str_t proxy_require = { "Proxy-Require", 13 }; static const pj_str_t require = { "Require", 7 }; if (!security_mechanisms || !tdata) { return EINVAL; } if (!strcmp(header_name, "Security-Client")) { add_qvalue = 0; } else if (strcmp(header_name, "Security-Server") && strcmp(header_name, "Security-Verify")) { return EINVAL; } /* If we're adding Security-Client headers, don't add q-value * even if the function caller requested it. */ add_qvalue = add_qvalue && add_qval; mech_cnt = AST_VECTOR_SIZE(security_mechanisms); for (i = 0; i < mech_cnt; ++i) { mech = AST_VECTOR_GET(security_mechanisms, i); if (security_mechanism_to_str(mech, add_qvalue, &buf)) { continue; } ast_sip_add_header(tdata, header_name, buf); ast_free(buf); } if (pjsip_msg_find_hdr_by_name(tdata->msg, &require, NULL) == NULL) { ast_sip_add_header(tdata, "Require", "mediasec"); } if (pjsip_msg_find_hdr_by_name(tdata->msg, &proxy_require, NULL) == NULL) { ast_sip_add_header(tdata, "Proxy-Require", "mediasec"); } return 0; } void ast_sip_header_to_security_mechanism(const pjsip_generic_string_hdr *hdr, struct ast_sip_security_mechanism_vector *security_mechanisms) { struct ast_sip_security_mechanism *mech; char buf[512]; char *hdr_val; char *mechanism; if (!security_mechanisms || !hdr) { return; } if (pj_stricmp2(&hdr->name, "Security-Client") && pj_stricmp2(&hdr->name, "Security-Server") && pj_stricmp2(&hdr->name, "Security-Verify")) { return; } ast_copy_pj_str(buf, &hdr->hvalue, sizeof(buf)); hdr_val = ast_skip_blanks(buf); while ((mechanism = ast_strsep(&hdr_val, ',', AST_STRSEP_ALL))) { if (!ast_sip_str_to_security_mechanism(&mech, mechanism)) { AST_VECTOR_APPEND(security_mechanisms, mech); } } } int ast_sip_security_mechanism_vector_init(struct ast_sip_security_mechanism_vector *security_mechanisms, const char *value) { char *val = value ? ast_strdupa(value) : NULL; struct ast_sip_security_mechanism *mech; char *mechanism; ast_sip_security_mechanisms_vector_destroy(security_mechanisms); if (AST_VECTOR_INIT(security_mechanisms, 1)) { return -1; } if (!val) { return 0; } while ((mechanism = ast_strsep(&val, ',', AST_STRSEP_ALL))) { if (!ast_sip_str_to_security_mechanism(&mech, mechanism)) { AST_VECTOR_APPEND(security_mechanisms, mech); } } return 0; }