2020-03-23 20:00:09 +00:00
|
|
|
/*
|
|
|
|
* 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>
|
2020-03-23 20:00:09 +00:00
|
|
|
*
|
|
|
|
* 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>
|
2020-03-23 20:00:09 +00:00
|
|
|
<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"
|
2020-03-23 20:00:09 +00:00
|
|
|
#include "asterisk/module.h"
|
|
|
|
|
|
|
|
#include "asterisk/res_stir_shaken.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
|
|
|
/*!
|
|
|
|
* \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 json_str The JSON string represntation of the payload
|
|
|
|
*
|
|
|
|
* \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();
|
|
|
|
|
2020-06-24 16:49:11 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \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 };
|
|
|
|
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_key_url;
|
|
|
|
char *attestation;
|
|
|
|
int mismatch = 0;
|
|
|
|
struct ast_stir_shaken_payload *ss_payload;
|
|
|
|
|
2020-06-24 16:49:11 +00:00
|
|
|
if (!session->endpoint->stir_shaken) {
|
|
|
|
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_base64decode_string(encoded_val);
|
|
|
|
if (ast_strlen_zero(header)) {
|
|
|
|
ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
encoded_val = strtok_r(identity_hdr_val, ".", &identity_hdr_val);
|
|
|
|
payload = ast_base64decode_string(encoded_val);
|
|
|
|
if (ast_strlen_zero(payload)) {
|
|
|
|
ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* It's fine to leave the signature encoded */
|
|
|
|
signature = strtok_r(identity_hdr_val, ";", &identity_hdr_val);
|
|
|
|
if (ast_strlen_zero(signature)) {
|
|
|
|
ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Trim "info=<" to get public key URL */
|
|
|
|
strtok_r(identity_hdr_val, "<", &identity_hdr_val);
|
|
|
|
public_key_url = strtok_r(identity_hdr_val, ">", &identity_hdr_val);
|
|
|
|
if (ast_strlen_zero(public_key_url)) {
|
|
|
|
ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
algorithm = strtok_r(identity_hdr_val, ";", &identity_hdr_val);
|
|
|
|
if (ast_strlen_zero(algorithm)) {
|
|
|
|
ast_stir_shaken_add_verification(chan, caller_id, "", AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
attestation = get_attestation_from_payload(payload);
|
|
|
|
|
|
|
|
ss_payload = ast_stir_shaken_verify(header, payload, signature, algorithm, public_key_url);
|
|
|
|
if (!ss_payload) {
|
|
|
|
ast_stir_shaken_add_verification(chan, caller_id, attestation, AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
res_stir_shaken: Add outbound INVITE support.
Integrated STIR/SHAKEN support with outgoing INVITEs. When an INVITE is
sent, the caller ID will be checked to see if there is a certificate
that corresponds to it. If so, that information will be retrieved and an
Identity header will be added to the SIP message. The format is:
header.payload.signature;info=<public_key_url>alg=ES256;ppt=shaken
Header, payload, and signature are all BASE64 encoded. The public key
URL is retrieved from the certificate. Currently the algorithm and ppt
are ES256 and shaken, respectively. This message is signed and can be
used for verification on the receiving end.
Two new configuration options have been added to the certificate object:
attestation and origid. The attestation is required and must be A, B, or
C. origid is the origination identifier.
A new utility function has been added as well that takes a string,
allocates space, BASE64 encodes it, then returns it, eliminating the
need to calculate the size yourself.
Change-Id: I1f84d6a5839cb2ed152ef4255b380cfc2de662b4
2020-06-02 14:04:23 +00:00
|
|
|
static void 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;
|
|
|
|
char *signature;
|
|
|
|
char *public_key_url;
|
|
|
|
struct ast_json *header;
|
|
|
|
struct ast_json *payload;
|
|
|
|
char *dumped_string;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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}}}", "header", "alg", "ES256", "ppt", "shaken", "typ", "passport",
|
|
|
|
"payload", "orig", "tn", session->id.number.str);
|
|
|
|
if (!json) {
|
|
|
|
ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN JSON\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ss_payload = ast_stir_shaken_sign(json);
|
|
|
|
if (!ss_payload) {
|
|
|
|
ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN payload\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
header = ast_json_object_get(json, "header");
|
|
|
|
dumped_string = ast_json_dump_string(header);
|
|
|
|
encoded_header = ast_base64encode_string(dumped_string);
|
|
|
|
ast_json_free(dumped_string);
|
|
|
|
if (!encoded_header) {
|
|
|
|
ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
payload = ast_json_object_get(json, "payload");
|
|
|
|
dumped_string = ast_json_dump_string(payload);
|
|
|
|
encoded_payload = ast_base64encode_string(dumped_string);
|
|
|
|
ast_json_free(dumped_string);
|
|
|
|
if (!encoded_payload) {
|
|
|
|
ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
signature = (char *)ast_stir_shaken_payload_get_signature(ss_payload);
|
|
|
|
public_key_url = ast_stir_shaken_payload_get_public_key_url(ss_payload);
|
|
|
|
|
|
|
|
/* The format for the identity header:
|
|
|
|
* header.payload.signature;info=<public_key_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_key_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;
|
|
|
|
}
|
|
|
|
snprintf(combined_str, combined_size, "%s.%s.%s;info=<%s>alg=%s;ppt=%s", encoded_header,
|
|
|
|
encoded_payload, signature, public_key_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;
|
|
|
|
}
|
|
|
|
|
|
|
|
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)identity_hdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
|
|
|
|
{
|
2020-06-24 16:49:11 +00:00
|
|
|
if (!session->endpoint->stir_shaken) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
res_stir_shaken: Add outbound INVITE support.
Integrated STIR/SHAKEN support with outgoing INVITEs. When an INVITE is
sent, the caller ID will be checked to see if there is a certificate
that corresponds to it. If so, that information will be retrieved and an
Identity header will be added to the SIP message. The format is:
header.payload.signature;info=<public_key_url>alg=ES256;ppt=shaken
Header, payload, and signature are all BASE64 encoded. The public key
URL is retrieved from the certificate. Currently the algorithm and ppt
are ES256 and shaken, respectively. This message is signed and can be
used for verification on the receiving end.
Two new configuration options have been added to the certificate object:
attestation and origid. The attestation is required and must be A, B, or
C. origid is the origination identifier.
A new utility function has been added as well that takes a string,
allocates space, BASE64 encodes it, then returns it, eliminating the
need to calculate the size yourself.
Change-Id: I1f84d6a5839cb2ed152ef4255b380cfc2de662b4
2020-06-02 14:04:23 +00:00
|
|
|
if (ast_strlen_zero(session->id.number.str) && session->id.number.valid) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_identity_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,
|
res_stir_shaken: Add outbound INVITE support.
Integrated STIR/SHAKEN support with outgoing INVITEs. When an INVITE is
sent, the caller ID will be checked to see if there is a certificate
that corresponds to it. If so, that information will be retrieved and an
Identity header will be added to the SIP message. The format is:
header.payload.signature;info=<public_key_url>alg=ES256;ppt=shaken
Header, payload, and signature are all BASE64 encoded. The public key
URL is retrieved from the certificate. Currently the algorithm and ppt
are ES256 and shaken, respectively. This message is signed and can be
used for verification on the receiving end.
Two new configuration options have been added to the certificate object:
attestation and origid. The attestation is required and must be A, B, or
C. origid is the origination identifier.
A new utility function has been added as well that takes a string,
allocates space, BASE64 encodes it, then returns it, eliminating the
need to calculate the size yourself.
Change-Id: I1f84d6a5839cb2ed152ef4255b380cfc2de662b4
2020-06-02 14:04:23 +00:00
|
|
|
.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
|
|
|
};
|
|
|
|
|
2020-03-23 20:00:09 +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);
|
2020-03-23 20:00:09 +00:00
|
|
|
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);
|
2020-03-23 20:00:09 +00:00
|
|
|
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,
|
2020-11-24 06:55:08 +00:00
|
|
|
"PJSIP STIR/SHAKEN Module for Asterisk",
|
2020-03-23 20:00:09 +00:00
|
|
|
.support_level = AST_MODULE_SUPPORT_CORE,
|
|
|
|
.load = load_module,
|
|
|
|
.unload = unload_module,
|
|
|
|
.load_pri = AST_MODPRI_DEFAULT,
|
2020-11-02 07:24:42 +00:00
|
|
|
.requires = "res_pjsip,res_pjsip_session,res_stir_shaken",
|
2020-03-23 20:00:09 +00:00
|
|
|
);
|