asterisk/res/res_pjsip_stir_shaken.c

555 lines
19 KiB
C
Raw Normal View History

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2020, Sangoma Technologies Corporation
*
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
* Ben Ford <bford@sangoma.com>
*
* 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.
*/
/*** MODULEINFO
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
<depend>pjproject</depend>
<depend>res_pjsip</depend>
<depend>res_pjsip_session</depend>
<depend>res_stir_shaken</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
#include "asterisk/res_pjsip.h"
#include "asterisk/res_pjsip_session.h"
#include "asterisk/module.h"
#include "asterisk/res_stir_shaken.h"
/*! The Date header will not be valid after this many milliseconds (60 seconds recommended) */
#define STIR_SHAKEN_DATE_HEADER_TIMEOUT 60000
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
/*!
* \brief Get the attestation from the payload
*
* \param json_str The JSON string representation of the payload
*
* \retval Empty string on failure
* \retval The attestation on success
*/
static char *get_attestation_from_payload(const char *json_str)
{
RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
char *attestation;
json = ast_json_load_string(json_str, NULL);
attestation = (char *)ast_json_string_get(ast_json_object_get(json, "attest"));
if (!ast_strlen_zero(attestation)) {
return attestation;
}
return "";
}
/*!
* \brief Compare the caller ID from the INVITE with the one in the payload
*
* \param caller_id
* \param json_str The JSON string representation of the payload
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
*
* \retval -1 on failure
* \retval 0 on success
*/
static int compare_caller_id(char *caller_id, const char *json_str)
{
RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
char *caller_id_other;
json = ast_json_load_string(json_str, NULL);
caller_id_other = (char *)ast_json_string_get(ast_json_object_get(
ast_json_object_get(json, "orig"), "tn"));
if (strcmp(caller_id, caller_id_other)) {
return -1;
}
return 0;
}
/*!
* \brief Compare the current timestamp with the one in the payload. If the difference
* is greater than the signature timeout, it's not valid anymore
*
* \param json_str The JSON string representation of the payload
*
* \retval -1 on failure
* \retval 0 on success
*/
static int compare_timestamp(const char *json_str)
{
RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
long int timestamp;
struct timeval now = ast_tvnow();
#ifdef TEST_FRAMEWORK
ast_debug(3, "Ignoring STIR/SHAKEN timestamp\n");
return 0;
#endif
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
json = ast_json_load_string(json_str, NULL);
timestamp = ast_json_integer_get(ast_json_object_get(json, "iat"));
if (now.tv_sec - timestamp > ast_stir_shaken_get_signature_timeout()) {
return -1;
}
return 0;
}
static int check_date_header(pjsip_rx_data *rdata)
{
static const pj_str_t date_hdr_str = { "Date", 4 };
char *date_hdr_val;
struct ast_tm date_hdr_tm;
struct timeval date_hdr_timeval;
struct timeval current_timeval;
char *remainder;
char timezone[80] = { 0 };
int64_t time_diff;
date_hdr_val = ast_sip_rdata_get_header_value(rdata, date_hdr_str);
if (ast_strlen_zero(date_hdr_val)) {
ast_log(LOG_ERROR, "Failed to get Date header from incoming INVITE for STIR/SHAKEN\n");
return -1;
}
if (!(remainder = ast_strptime(date_hdr_val, "%a, %d %b %Y %T", &date_hdr_tm))) {
ast_log(LOG_ERROR, "Failed to parse Date header\n");
return -1;
}
sscanf(remainder, "%79s", timezone);
if (ast_strlen_zero(timezone)) {
ast_log(LOG_ERROR, "A timezone is required for STIR/SHAKEN Date header, but we didn't get one\n");
return -1;
}
date_hdr_timeval = ast_mktime(&date_hdr_tm, timezone);
current_timeval = ast_tvnow();
time_diff = ast_tvdiff_ms(current_timeval, date_hdr_timeval);
if (time_diff < 0) {
/* An INVITE from the future! */
ast_log(LOG_ERROR, "STIR/SHAKEN Date header has a future date\n");
return -1;
} else if (time_diff > STIR_SHAKEN_DATE_HEADER_TIMEOUT) {
ast_log(LOG_ERROR, "STIR/SHAKEN Date header was outside of the allowable range (60 seconds)\n");
return -1;
}
return 0;
}
/* Send a response back and end the session */
static void stir_shaken_inv_end_session(struct ast_sip_session *session, pjsip_rx_data *rdata, int response_code, const pj_str_t response_str)
{
pjsip_tx_data *tdata;
if (pjsip_inv_end_session(session->inv_session, response_code, &response_str, &tdata) == PJ_SUCCESS) {
pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL);
}
ast_hangup(session->channel);
}
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
/*!
* \internal
* \brief Session supplement callback on an incoming INVITE request
*
* When we receive an INVITE, check it for STIR/SHAKEN information and
* decide what to do from there
*
* \param session The session that has received an INVITE
* \param rdata The incoming INVITE
*/
static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
{
static const pj_str_t identity_str = { "Identity", 8 };
const pj_str_t bad_identity_info_str = {
AST_STIR_SHAKEN_RESPONSE_STR_BAD_IDENTITY_INFO,
strlen(AST_STIR_SHAKEN_RESPONSE_STR_BAD_IDENTITY_INFO)
};
const pj_str_t unsupported_credential_str = {
AST_STIR_SHAKEN_RESPONSE_STR_UNSUPPORTED_CREDENTIAL,
strlen(AST_STIR_SHAKEN_RESPONSE_STR_UNSUPPORTED_CREDENTIAL)
};
const pj_str_t stale_date_str = {
AST_STIR_SHAKEN_RESPONSE_STR_STALE_DATE,
strlen(AST_STIR_SHAKEN_RESPONSE_STR_STALE_DATE)
};
const pj_str_t use_supported_passport_format_str = {
AST_STIR_SHAKEN_RESPONSE_STR_USE_SUPPORTED_PASSPORT_FORMAT,
strlen(AST_STIR_SHAKEN_RESPONSE_STR_USE_SUPPORTED_PASSPORT_FORMAT)
};
const pj_str_t invalid_identity_hdr_str = {
AST_STIR_SHAKEN_RESPONSE_STR_INVALID_IDENTITY_HEADER,
strlen(AST_STIR_SHAKEN_RESPONSE_STR_INVALID_IDENTITY_HEADER)
};
const pj_str_t server_internal_error_str = { "Server Internal Error", 21 };
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
char *identity_hdr_val;
char *encoded_val;
struct ast_channel *chan = session->channel;
char *caller_id = session->id.number.str;
RAII_VAR(char *, header, NULL, ast_free);
RAII_VAR(char *, payload, NULL, ast_free);
char *signature;
char *algorithm;
char *public_cert_url;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
char *attestation;
char *ppt;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
int mismatch = 0;
struct ast_stir_shaken_payload *ss_payload;
int failure_code = 0;
RAII_VAR(struct stir_shaken_profile *, profile, NULL, ao2_cleanup);
/* Check if this is a reinvite. If it is, we don't need to do anything */
if (rdata->msg_info.to->tag.slen) {
return 0;
}
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
profile = ast_stir_shaken_get_profile(session->endpoint->stir_shaken_profile);
/* Profile should be checked first as it takes priority over anything else.
* If there is a profile and it doesn't have verification enabled, do nothing.
* If there is no profile and the stir_shaken option is either not set or does
* not support verification, do nothing.
*/
if ((profile && !ast_stir_shaken_profile_supports_verification(profile))
|| (!profile && (session->endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_VERIFY) == 0)) {
return 0;
}
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
identity_hdr_val = ast_sip_rdata_get_header_value(rdata, identity_str);
if (ast_strlen_zero(identity_hdr_val)) {
ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_NOT_PRESENT);
return 0;
}
encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);
header = ast_base64url_decode_string(encoded_val);
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
if (ast_strlen_zero(header)) {
ast_debug(3, "STIR/SHAKEN INVITE for %s is missing header\n",
ast_sorcery_object_get_id(session->endpoint));
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_BAD_IDENTITY_INFO, bad_identity_info_str);
return 1;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
}
encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);
payload = ast_base64url_decode_string(encoded_val);
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
if (ast_strlen_zero(payload)) {
ast_debug(3, "STIR/SHAKEN INVITE for %s is missing payload\n",
ast_sorcery_object_get_id(session->endpoint));
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_BAD_IDENTITY_INFO, bad_identity_info_str);
return 1;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
}
/* It's fine to leave the signature encoded */
signature = strtok_r(identity_hdr_val, ";", &identity_hdr_val);
if (ast_strlen_zero(signature)) {
ast_debug(3, "STIR/SHAKEN INVITE for %s is missing signature\n",
ast_sorcery_object_get_id(session->endpoint));
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_BAD_IDENTITY_INFO, bad_identity_info_str);
return 1;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
}
/* Trim "info=<" to get public cert URL */
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
strtok_r(identity_hdr_val, "<", &identity_hdr_val);
public_cert_url = strtok_r(identity_hdr_val, ">", &identity_hdr_val);
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
/* Make sure the public URL is actually a URL */
if (ast_strlen_zero(public_cert_url) || !ast_begins_with(public_cert_url, "http")) {
/* RFC8224 states that if we can't acquire the credentials needed
* by the verification service, we should send a 436 */
ast_debug(3, "STIR/SHAKEN INVITE for %s did not have valid URL (%s)\n",
ast_sorcery_object_get_id(session->endpoint), public_cert_url);
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_BAD_IDENTITY_INFO, bad_identity_info_str);
return 1;
}
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
algorithm = strtok_r(identity_hdr_val, ";", &identity_hdr_val);
if (ast_strlen_zero(algorithm)) {
/* RFC8224 states that if the algorithm is not specified, use ES256 */
algorithm = STIR_SHAKEN_ENCRYPTION_ALGORITHM;
} else {
strtok_r(algorithm, "=", &algorithm);
if (strcmp(algorithm, STIR_SHAKEN_ENCRYPTION_ALGORITHM)) {
/* RFC8224 states that if we don't support the algorithm, send a 437 */
ast_debug(3, "STIR/SHAKEN INVITE for %s uses an unsupported algorithm (%s)\n",
ast_sorcery_object_get_id(session->endpoint), algorithm);
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_UNSUPPORTED_CREDENTIAL, unsupported_credential_str);
return 1;
}
}
/* The only thing left should be ppt=shaken (which could have more values later),
* unless using the compact PASSport form */
strtok_r(identity_hdr_val, "=", &identity_hdr_val);
ppt = ast_strip(identity_hdr_val);
if (!ast_strlen_zero(ppt) && strcmp(ppt, STIR_SHAKEN_PPT)) {
ast_log(LOG_ERROR, "STIR/SHAKEN INVITE for %s has unsupported ppt (%s)\n",
ast_sorcery_object_get_id(session->endpoint), ppt);
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_USE_SUPPORTED_PASSPORT_FORMAT, use_supported_passport_format_str);
return 1;
}
if (check_date_header(rdata)) {
ast_debug(3, "STIR/SHAKEN INVITE for %s has old Date header\n",
ast_sorcery_object_get_id(session->endpoint));
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_STALE_DATE, stale_date_str);
return 1;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
}
attestation = get_attestation_from_payload(payload);
ss_payload = ast_stir_shaken_verify_with_profile(header, payload, signature, algorithm, public_cert_url, &failure_code, profile);
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
if (!ss_payload) {
if (failure_code == AST_STIR_SHAKEN_VERIFY_FAILED_TO_GET_CERT) {
/* RFC8224 states that if we can't get the credentials we need, send a 437 */
ast_debug(3, "STIR/SHAKEN INVITE for %s failed to acquire cert during verification process\n",
ast_sorcery_object_get_id(session->endpoint));
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_UNSUPPORTED_CREDENTIAL, unsupported_credential_str);
} else if (failure_code == AST_STIR_SHAKEN_VERIFY_FAILED_MEMORY_ALLOC) {
ast_log(LOG_ERROR, "Failed to allocate memory during STIR/SHAKEN verification"
" for %s\n", ast_sorcery_object_get_id(session->endpoint));
stir_shaken_inv_end_session(session, rdata, 500, server_internal_error_str);
} else if (failure_code == AST_STIR_SHAKEN_VERIFY_FAILED_SIGNATURE_VALIDATION) {
/* RFC8224 states that if we can't validate the signature, send a 438 */
ast_debug(3, "STIR/SHAKEN INVITE for %s failed signature validation during verification process\n",
ast_sorcery_object_get_id(session->endpoint));
ast_stir_shaken_add_verification(chan, caller_id, attestation, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
stir_shaken_inv_end_session(session, rdata, AST_STIR_SHAKEN_RESPONSE_CODE_INVALID_IDENTITY_HEADER, invalid_identity_hdr_str);
}
return 1;
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
}
ast_stir_shaken_payload_free(ss_payload);
mismatch |= compare_caller_id(caller_id, payload);
mismatch |= compare_timestamp(payload);
if (mismatch) {
ast_stir_shaken_add_verification(chan, caller_id, attestation, AST_STIR_SHAKEN_VERIFY_MISMATCH);
return 0;
}
ast_stir_shaken_add_verification(chan, caller_id, attestation, AST_STIR_SHAKEN_VERIFY_PASSED);
return 0;
}
static int add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
{
static const pj_str_t identity_str = { "Identity", 8 };
pjsip_generic_string_hdr *identity_hdr;
pj_str_t identity_val;
pjsip_fromto_hdr *old_identity;
pjsip_fromto_hdr *to;
pjsip_sip_uri *uri;
char *signature;
char *public_cert_url;
struct ast_json *header;
struct ast_json *payload;
char *dumped_string;
RAII_VAR(char *, dest_tn, NULL, ast_free);
RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
RAII_VAR(struct ast_stir_shaken_payload *, ss_payload, NULL, ast_stir_shaken_payload_free);
RAII_VAR(char *, encoded_header, NULL, ast_free);
RAII_VAR(char *, encoded_payload, NULL, ast_free);
RAII_VAR(char *, combined_str, NULL, ast_free);
size_t combined_size;
old_identity = pjsip_msg_find_hdr_by_name(tdata->msg, &identity_str, NULL);
if (old_identity) {
return 0;
}
to = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
if (!to) {
ast_log(LOG_ERROR, "Failed to find To header while adding STIR/SHAKEN Identity header\n");
return -1;
}
uri = pjsip_uri_get_uri(to->uri);
if (!uri) {
ast_log(LOG_ERROR, "Failed to retrieve URI from To header while adding STIR/SHAKEN Identity header\n");
return -1;
}
dest_tn = ast_malloc(uri->user.slen + 1);
if (!dest_tn) {
ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN dest->tn\n");
return -1;
}
/* Remove everything except 0-9, *, and # in telephone number according to RFC 8224
* (required by RFC 8225 as part of canonicalization) */
{
int i;
const char *s = uri->user.ptr;
char *new_tn = dest_tn;
/* We're only removing characters, if anything, so the buffer is guaranteed to be large enough */
for (i = 0; i < uri->user.slen; i++) {
if (isdigit(*s) || *s == '#' || *s == '*') { /* Only characters allowed */
*new_tn++ = *s;
}
s++;
}
*new_tn = '\0';
ast_debug(4, "Canonicalized telephone number %.*s -> %s\n", (int) uri->user.slen, uri->user.ptr, dest_tn);
}
/* x5u (public key URL), attestation, and origid will be added by ast_stir_shaken_sign */
json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: [s]}, s: {s: s}}}",
"header", "alg", "ES256", "ppt", "shaken", "typ", "passport",
"payload", "dest", "tn", dest_tn, "orig", "tn",
session->id.number.str);
if (!json) {
ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN JSON\n");
return -1;
}
ss_payload = ast_stir_shaken_sign(json);
if (!ss_payload) {
ast_log(LOG_ERROR, "Failed to sign STIR/SHAKEN payload\n");
return -1;
}
header = ast_json_object_get(json, "header");
dumped_string = ast_json_dump_string(header);
encoded_header = ast_base64url_encode_string(dumped_string);
ast_json_free(dumped_string);
if (!encoded_header) {
ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");
return -1;
}
payload = ast_json_object_get(json, "payload");
/* Fields must appear in lexiographic order: https://www.rfc-editor.org/rfc/rfc8588.html#section-6
* https://www.rfc-editor.org/rfc/rfc8225.html#section-9 */
dumped_string = ast_json_dump_string_sorted(payload);
encoded_payload = ast_base64url_encode_string(dumped_string);
ast_json_free(dumped_string);
if (!encoded_payload) {
ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");
return -1;
}
signature = (char *)ast_stir_shaken_payload_get_signature(ss_payload);
public_cert_url = ast_stir_shaken_payload_get_public_cert_url(ss_payload);
/* The format for the identity header:
* header.payload.signature;info=<public_cert_url>alg=STIR_SHAKEN_ENCRYPTION_ALGORITHM;ppt=STIR_SHAKEN_PPT
*/
combined_size = strlen(encoded_header) + 1 + strlen(encoded_payload) + 1
+ strlen(signature) + strlen(";info=<>alg=;ppt=") + strlen(public_cert_url)
+ strlen(STIR_SHAKEN_ENCRYPTION_ALGORITHM) + strlen(STIR_SHAKEN_PPT) + 1;
combined_str = ast_calloc(1, combined_size);
if (!combined_str) {
ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN identity string\n");
return -1;
}
snprintf(combined_str, combined_size, "%s.%s.%s;info=<%s>alg=%s;ppt=%s", encoded_header,
encoded_payload, signature, public_cert_url, STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT);
identity_val = pj_str(combined_str);
identity_hdr = pjsip_generic_string_hdr_create(tdata->pool, &identity_str, &identity_val);
if (!identity_hdr) {
ast_log(LOG_ERROR, "Failed to create STIR/SHAKEN Identity header\n");
return -1;
}
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)identity_hdr);
return 0;
}
static void add_date_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
{
static const pj_str_t date_str = { "Date", 4 };
pjsip_fromto_hdr *old_date;
old_date = pjsip_msg_find_hdr_by_name(tdata->msg, &date_str, NULL);
if (old_date) {
ast_debug(3, "Found old STIR/SHAKEN date header, no need to add one\n");
return;
}
ast_sip_add_date_header(tdata);
}
static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
RAII_VAR(struct stir_shaken_profile *, profile, NULL, ao2_cleanup);
profile = ast_stir_shaken_get_profile(session->endpoint->stir_shaken_profile);
/* Profile should be checked first as it takes priority over anything else.
* If there is a profile and it doesn't have attestation enabled, do nothing.
* If there is no profile and the stir_shaken option is either not set or does
* not support attestation, do nothing.
*/
if ((profile && !ast_stir_shaken_profile_supports_attestation(profile))
|| (!profile && (session->endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_ATTEST) == 0)) {
return;
}
if (ast_strlen_zero(session->id.number.str) && session->id.number.valid) {
return;
}
/* If adding the Identity header fails for some reason, there's no point
* adding the Date header.
*/
if ((add_identity_header(session, tdata)) != 0) {
return;
}
add_date_header(session, tdata);
}
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
static struct ast_sip_session_supplement stir_shaken_supplement = {
.method = "INVITE",
.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, /* Run AFTER channel creation */
.incoming_request = stir_shaken_incoming_request,
.outgoing_request = stir_shaken_outgoing_request,
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
};
static int unload_module(void)
{
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
ast_sip_session_unregister_supplement(&stir_shaken_supplement);
return 0;
}
static int load_module(void)
{
res_stir_shaken: Add inbound INVITE support. Integrated STIR/SHAKEN support with incoming INVITES. Upon receiving an INVITE, the Identity header is retrieved, parsing the message to verify the signature. If any of the parsing fails, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT will be added to the channel for this caller ID. If verification itself fails, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED will be added. If anything in the payload does not line up with the SIP signaling, AST_STIR_SHAKEN_VERIFY_MISMATCH will be added. If all of the above steps pass, then AST_STIR_SHAKEN_VERIFY_PASSED will be added, completing the verification process. A new config option has been added to the general section for stir_shaken.conf. "signature_timeout" is the amount of time a signature will be considered valid. If an INVITE is received and the amount of time between when it was received and when it was signed is greater than signature_timeout, verification will fail. Some changes were also made to signing and verification. There was an error where the whole JSON string was being signed rather than the header combined with the payload. This has been changed to sign the correct thing. Verification has been changed to do this as well, and the unit tests have been updated to reflect these changes. A couple of utility functions have also been added. One decodes a BASE64 string and returns the decoded string, doing all the length calculations for you. The other retrieves a string value from a header in a rdata object. Change-Id: I855f857be3d1c63b64812ac35d9ce0534085b913
2020-05-19 19:46:45 +00:00
ast_sip_session_register_supplement(&stir_shaken_supplement);
return AST_MODULE_LOAD_SUCCESS;
}
#undef AST_BUILDOPT_SUM
#define AST_BUILDOPT_SUM ""
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJSIP STIR/SHAKEN Module for Asterisk",
.support_level = AST_MODULE_SUPPORT_CORE,
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_DEFAULT,
.requires = "res_pjsip,res_pjsip_session,res_stir_shaken",
);