STIR/SHAKEN: Add Date header, dest->tn, and URL checking.

STIR/SHAKEN requires a Date header alongside the Identity header, so
that has been added. Still on the outgoing side, we were missing the
dest->tn section of the JSON payload, so that has been added as well.
Moving to the incoming side, URL checking has been added to the public
cert URL to ensure that it starts with http.

https://wiki.asterisk.org/wiki/display/AST/OpenSIPit+2021

Change-Id: Idee5b1b5e45bc3b483b3070e46ce322dca5b3f1c
This commit is contained in:
Ben Ford 2021-05-19 13:45:16 -05:00 committed by Joshua Colp
parent e07fd35238
commit cee88c9826
4 changed files with 89 additions and 26 deletions

View File

@ -947,6 +947,17 @@ enum ast_sip_contact_filter {
AST_SIP_CONTACT_FILTER_REACHABLE = (1 << 0),
};
/*!
* \brief Adds a Date header to the tdata, formatted like:
* Date: Wed, 01 Jan 2021 14:53:01 GMT
* \since 16.19.0
*
* \note There is no checking done to see if the header already exists
* before adding it. It's up to the caller of this function to determine
* if that needs to be done or not.
*/
void ast_sip_add_date_header(pjsip_tx_data *tdata);
/*!
* \brief Register a SIP service in Asterisk.
*

View File

@ -2967,6 +2967,18 @@ static pj_sockaddr host_ip_ipv6;
/*! Local host address for IPv6 (string form) */
static char host_ip_ipv6_string[PJ_INET6_ADDRSTRLEN];
void ast_sip_add_date_header(pjsip_tx_data *tdata)
{
char date[256];
struct tm tm;
time_t t = time(NULL);
gmtime_r(&t, &tm);
strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);
ast_sip_add_header(tdata, "Date", date);
}
static int register_service(void *data)
{
pjsip_module **module = data;

View File

@ -247,19 +247,6 @@ static int registrar_add_contact(void *obj, void *arg, int flags)
return 0;
}
/*! \brief Helper function which adds a Date header to a response */
static void registrar_add_date_header(pjsip_tx_data *tdata)
{
char date[256];
struct tm tm;
time_t t = time(NULL);
gmtime_r(&t, &tm);
strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm);
ast_sip_add_header(tdata, "Date", date);
}
static const pj_str_t path_hdr_name = { "Path", 4 };
static int build_path_data(pjsip_rx_data *rdata, struct ast_str **path_str)
@ -898,7 +885,7 @@ static void register_aor_core(pjsip_rx_data *rdata,
ao2_cleanup(response_contact);
/* Add the date header to the response, some UAs use this to set their date and time */
registrar_add_date_header(tdata);
ast_sip_add_date_header(tdata);
ao2_callback(contacts, 0, registrar_add_contact, tdata);

View File

@ -169,7 +169,7 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
return 0;
}
/* Trim "info=<" to get public key URL */
/* Trim "info=<" to get public cert URL */
strtok_r(identity_hdr_val, "<", &identity_hdr_val);
public_cert_url = strtok_r(identity_hdr_val, ">", &identity_hdr_val);
if (ast_strlen_zero(public_cert_url)) {
@ -177,6 +177,12 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
return 0;
}
/* Make sure the public URL is actually a URL */
if (!ast_begins_with(public_cert_url, "http")) {
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);
@ -205,17 +211,20 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
return 0;
}
static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_data *tdata)
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);
@ -225,21 +234,43 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
old_identity = pjsip_msg_find_hdr_by_name(tdata->msg, &identity_str, NULL);
if (old_identity) {
return;
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;
}
ast_copy_pj_str(dest_tn, &uri->user, uri->user.slen + 1);
/* 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);
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;
return -1;
}
ss_payload = ast_stir_shaken_sign(json);
if (!ss_payload) {
ast_log(LOG_ERROR, "Failed to allocate memory for STIR/SHAKEN payload\n");
return;
return -1;
}
header = ast_json_object_get(json, "header");
@ -248,7 +279,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
ast_json_free(dumped_string);
if (!encoded_header) {
ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN header\n");
return;
return -1;
}
payload = ast_json_object_get(json, "payload");
@ -257,7 +288,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
ast_json_free(dumped_string);
if (!encoded_payload) {
ast_log(LOG_ERROR, "Failed to encode STIR/SHAKEN payload\n");
return;
return -1;
}
signature = (char *)ast_stir_shaken_payload_get_signature(ss_payload);
@ -272,7 +303,7 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
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;
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);
@ -281,10 +312,26 @@ static void add_identity_header(const struct ast_sip_session *session, pjsip_tx_
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;
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)
@ -297,7 +344,13 @@ static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_
return;
}
add_identity_header(session, tdata);
/* 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);
}
static struct ast_sip_session_supplement stir_shaken_supplement = {