Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Functions | Variables
res_stir_shaken.c File Reference
#include "asterisk.h"
#include <openssl/evp.h>
#include "asterisk/module.h"
#include "asterisk/sorcery.h"
#include "asterisk/time.h"
#include "asterisk/json.h"
#include "asterisk/astdb.h"
#include "asterisk/paths.h"
#include "asterisk/conversions.h"
#include "asterisk/pbx.h"
#include "asterisk/global_datastores.h"
#include "asterisk/app.h"
#include "asterisk/test.h"
#include "asterisk/res_stir_shaken.h"
#include "res_stir_shaken/stir_shaken.h"
#include "res_stir_shaken/general.h"
#include "res_stir_shaken/store.h"
#include "res_stir_shaken/certificate.h"
#include "res_stir_shaken/curl.h"
Include dependency graph for res_stir_shaken.c:

Go to the source code of this file.

Data Structures

struct  ast_stir_shaken_payload
 
struct  stir_shaken_datastore
 

Macros

#define AST_DB_FAMILY   "STIR_SHAKEN"
 
#define EXPIRATION_BUFFER   15
 
#define MAX_PATH_LEN   256
 
#define STIR_SHAKEN_DIR_NAME   "stir_shaken"
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static void add_public_key_to_astdb (const char *public_cert_url, const char *filepath)
 Add the public key details and file path to AstDB. More...
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
int ast_stir_shaken_add_verification (struct ast_channel *chan, const char *identity, const char *attestation, enum ast_stir_shaken_verification_result result)
 Add a STIR/SHAKEN verification result to a channel. More...
 
unsigned int ast_stir_shaken_get_signature_timeout (void)
 Retrieve the value for 'signature_timeout' from 'general' config object. More...
 
void ast_stir_shaken_payload_free (struct ast_stir_shaken_payload *payload)
 Free a STIR/SHAKEN payload. More...
 
char * ast_stir_shaken_payload_get_public_cert_url (const struct ast_stir_shaken_payload *payload)
 Retrieve the value for 'public_cert_url' from an ast_stir_shaken_payload. More...
 
unsigned char * ast_stir_shaken_payload_get_signature (const struct ast_stir_shaken_payload *payload)
 Retrieve the value for 'signature' from an ast_stir_shaken_payload. More...
 
struct ast_stir_shaken_payloadast_stir_shaken_sign (struct ast_json *json)
 Sign a JSON STIR/SHAKEN payload. More...
 
struct ast_sorceryast_stir_shaken_sorcery (void)
 Retrieve the stir/shaken sorcery context. More...
 
struct ast_stir_shaken_payloadast_stir_shaken_verify (const char *header, const char *payload, const char *signature, const char *algorithm, const char *public_cert_url)
 Verify a JSON STIR/SHAKEN payload. More...
 
 AST_TEST_DEFINE (test_stir_shaken_sign)
 
 AST_TEST_DEFINE (test_stir_shaken_verify)
 
static char * curl_and_check_expiration (const char *public_cert_url, const char *path, int *curl)
 Downloads the public cert from public_cert_url. If curl is non-zero, that signals CURL has already been run, and we should bail here. The entry is added to AstDB as well. More...
 
static char * get_path_to_public_key (const char *public_cert_url)
 Returns the path to the downloaded file for the provided URL. More...
 
static int load_module (void)
 
static int public_key_is_expired (const char *public_cert_url)
 Check to see if the public key is expired. More...
 
static int reload_module (void)
 
static void remove_public_key_from_astdb (const char *public_cert_url)
 Remove the public key details and associated information from AstDB. More...
 
static char * run_curl (const char *public_cert_url, const char *path)
 CURL the file located at public_cert_url to the specified path. More...
 
static void set_public_key_expiration (const char *public_cert_url, const struct curl_cb_data *data)
 Sets the expiration for the public key based on the provided fields. If Cache-Control is present, use it. Otherwise, use Expires. More...
 
static int stir_shaken_add_attest (struct ast_json *json, const char *attest)
 Adds the 'attest' field to the JWT. More...
 
static int stir_shaken_add_iat (struct ast_json *json)
 Adds the 'iat' field to the JWT. More...
 
static int stir_shaken_add_origid (struct ast_json *json)
 Adds the 'origid' field to the JWT. More...
 
static int stir_shaken_add_x5u (struct ast_json *json, const char *x5u)
 Adds the 'x5u' (public key URL) field to the JWT. More...
 
static void stir_shaken_datastore_destroy_cb (void *data)
 The callback to destroy a stir_shaken_datastore. More...
 
static void stir_shaken_datastore_free (struct stir_shaken_datastore *datastore)
 Frees a stir_shaken_datastore structure. More...
 
static int stir_shaken_read (struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
 Retrieves STIR/SHAKEN verification information for the channel via dialplan. Examples: More...
 
static unsigned char * stir_shaken_sign (char *json_str, EVP_PKEY *private_key)
 Signs the payload and returns the signature. More...
 
static const char * stir_shaken_verification_result_to_string (enum ast_stir_shaken_verification_result result)
 Convert an ast_stir_shaken_verification_result to string representation. More...
 
static struct ast_stir_shaken_payloadstir_shaken_verify_json (struct ast_json *json)
 Verifies the necessary contents are in the JSON and returns a ast_stir_shaken_payload with the extracted values. More...
 
static int stir_shaken_verify_signature (const char *msg, const char *signature, EVP_PKEY *public_key)
 Verifies the signature using a public key. More...
 
static void test_stir_shaken_add_fake_astdb_entry (const char *public_cert_url, const char *file_path)
 
static int test_stir_shaken_write_temp_key (char *file_path, int private)
 Create a private or public key certificate. More...
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "STIR/SHAKEN Module for Asterisk" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1, .requires = "res_curl", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static const struct ast_datastore_info stir_shaken_datastore_info
 
static struct ast_custom_function stir_shaken_function
 
static struct ast_sorcerystir_shaken_sorcery
 

Macro Definition Documentation

◆ AST_DB_FAMILY

#define AST_DB_FAMILY   "STIR_SHAKEN"

Definition at line 147 of file res_stir_shaken.c.

Referenced by add_public_key_to_astdb(), and remove_public_key_from_astdb().

◆ EXPIRATION_BUFFER

#define EXPIRATION_BUFFER   15

Definition at line 158 of file res_stir_shaken.c.

Referenced by set_public_key_expiration().

◆ MAX_PATH_LEN

#define MAX_PATH_LEN   256

Definition at line 153 of file res_stir_shaken.c.

Referenced by get_path_to_public_key(), and remove_public_key_from_astdb().

◆ STIR_SHAKEN_DIR_NAME

#define STIR_SHAKEN_DIR_NAME   "stir_shaken"

Definition at line 150 of file res_stir_shaken.c.

Referenced by ast_stir_shaken_verify().

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1677 of file res_stir_shaken.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1677 of file res_stir_shaken.c.

◆ add_public_key_to_astdb()

static void add_public_key_to_astdb ( const char *  public_cert_url,
const char *  filepath 
)
static

Add the public key details and file path to AstDB.

Parameters
public_cert_urlThe public cert URL
filepathThe path to the file

Definition at line 456 of file res_stir_shaken.c.

References AST_DB_FAMILY, ast_db_put(), and ast_sha1_hash().

Referenced by ast_stir_shaken_verify(), curl_and_check_expiration(), and test_stir_shaken_add_fake_astdb_entry().

457 {
458  char hash[41];
459 
460  ast_sha1_hash(hash, public_cert_url);
461 
462  ast_db_put(AST_DB_FAMILY, public_cert_url, hash);
463  ast_db_put(hash, "path", filepath);
464 }
#define AST_DB_FAMILY
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:327

◆ AST_MODULE_SELF_SYM()

struct ast_module* AST_MODULE_SELF_SYM ( void  )

Definition at line 1677 of file res_stir_shaken.c.

◆ ast_stir_shaken_add_verification()

int ast_stir_shaken_add_verification ( struct ast_channel chan,
const char *  identity,
const char *  attestation,
enum ast_stir_shaken_verification_result  result 
)

Add a STIR/SHAKEN verification result to a channel.

Parameters
chanThe channel
identityThe identity
attestationThe attestation
resultThe verification result
Return values
-1on failure
0on success

Definition at line 277 of file res_stir_shaken.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_name(), ast_channel_unlock, ast_datastore_alloc, ast_log, ast_strdup, stir_shaken_datastore::attestation, ast_datastore::data, stir_shaken_datastore::identity, LOG_ERROR, NULL, result, stir_shaken_datastore_free(), and stir_shaken_datastore::verify_result.

Referenced by stir_shaken_incoming_request().

279 {
280  struct stir_shaken_datastore *ss_datastore;
281  struct ast_datastore *datastore;
282  const char *chan_name;
283 
284  if (!chan) {
285  ast_log(LOG_ERROR, "Channel is required to add STIR/SHAKEN verification\n");
286  return -1;
287  }
288 
289  chan_name = ast_channel_name(chan);
290 
291  if (!identity) {
292  ast_log(LOG_ERROR, "No identity to add STIR/SHAKEN verification to channel "
293  "%s\n", chan_name);
294  return -1;
295  }
296 
297  if (!attestation) {
298  ast_log(LOG_ERROR, "Attestation cannot be NULL to add STIR/SHAKEN verification to "
299  "channel %s\n", chan_name);
300  return -1;
301  }
302 
303  ss_datastore = ast_calloc(1, sizeof(*ss_datastore));
304  if (!ss_datastore) {
305  ast_log(LOG_ERROR, "Failed to allocate space for STIR/SHAKEN datastore for "
306  "channel %s\n", chan_name);
307  return -1;
308  }
309 
310  ss_datastore->identity = ast_strdup(identity);
311  if (!ss_datastore->identity) {
312  ast_log(LOG_ERROR, "Failed to allocate space for STIR/SHAKEN datastore "
313  "identity for channel %s\n", chan_name);
314  stir_shaken_datastore_free(ss_datastore);
315  return -1;
316  }
317 
318  ss_datastore->attestation = ast_strdup(attestation);
319  if (!ss_datastore->attestation) {
320  ast_log(LOG_ERROR, "Failed to allocate space for STIR/SHAKEN datastore "
321  "attestation for channel %s\n", chan_name);
322  stir_shaken_datastore_free(ss_datastore);
323  return -1;
324  }
325 
326  ss_datastore->verify_result = result;
327 
329  if (!datastore) {
330  ast_log(LOG_ERROR, "Failed to allocate space for datastore for channel "
331  "%s\n", chan_name);
332  stir_shaken_datastore_free(ss_datastore);
333  return -1;
334  }
335 
336  datastore->data = ss_datastore;
337 
338  ast_channel_lock(chan);
339  ast_channel_datastore_add(chan, datastore);
340  ast_channel_unlock(chan);
341 
342  return 0;
343 }
static void stir_shaken_datastore_free(struct stir_shaken_datastore *datastore)
Frees a stir_shaken_datastore structure.
#define ast_channel_lock(chan)
Definition: channel.h:2945
static const struct ast_datastore_info stir_shaken_datastore_info
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
Structure for a data store object.
Definition: datastore.h:68
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
#define ast_channel_unlock(chan)
Definition: channel.h:2946
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
enum ast_stir_shaken_verification_result verify_result
void * data
Definition: datastore.h:70
const char * ast_channel_name(const struct ast_channel *chan)
static PGresult * result
Definition: cel_pgsql.c:88
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:89
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2390

◆ ast_stir_shaken_get_signature_timeout()

unsigned int ast_stir_shaken_get_signature_timeout ( void  )

Retrieve the value for 'signature_timeout' from 'general' config object.

Return values
Thesignature timeout

Definition at line 203 of file res_stir_shaken.c.

References ast_stir_shaken_signature_timeout(), and stir_shaken_general_get().

Referenced by compare_timestamp().

204 {
206 }
struct stir_shaken_general * stir_shaken_general_get()
Retrieve the stir/shaken &#39;general&#39; configuration object.
Definition: general.c:54
unsigned int ast_stir_shaken_signature_timeout(const struct stir_shaken_general *cfg)
Retrieve the &#39;signature_timeout&#39; general configuration option value.
Definition: general.c:92

◆ ast_stir_shaken_payload_free()

void ast_stir_shaken_payload_free ( struct ast_stir_shaken_payload payload)

Free a STIR/SHAKEN payload.

Definition at line 178 of file res_stir_shaken.c.

References ast_stir_shaken_payload::algorithm, ast_free, ast_json_unref(), ast_stir_shaken_payload::header, ast_stir_shaken_payload::payload, ast_stir_shaken_payload::public_cert_url, and ast_stir_shaken_payload::signature.

Referenced by add_identity_header(), ast_stir_shaken_sign(), ast_stir_shaken_verify(), AST_TEST_DEFINE(), stir_shaken_incoming_request(), and stir_shaken_verify_json().

179 {
180  if (!payload) {
181  return;
182  }
183 
184  ast_json_unref(payload->header);
185  ast_json_unref(payload->payload);
186  ast_free(payload->algorithm);
187  ast_free(payload->public_cert_url);
188  ast_free(payload->signature);
189 
190  ast_free(payload);
191 }
struct ast_json * header
unsigned char * signature
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
#define ast_free(a)
Definition: astmm.h:182
struct ast_json * payload

◆ ast_stir_shaken_payload_get_public_cert_url()

char* ast_stir_shaken_payload_get_public_cert_url ( const struct ast_stir_shaken_payload payload)

Retrieve the value for 'public_cert_url' from an ast_stir_shaken_payload.

Parameters
payloadThe payload
Return values
Thepublic key URL

Definition at line 198 of file res_stir_shaken.c.

References NULL, and ast_stir_shaken_payload::public_cert_url.

Referenced by add_identity_header().

199 {
200  return payload ? payload->public_cert_url : NULL;
201 }
#define NULL
Definition: resample.c:96

◆ ast_stir_shaken_payload_get_signature()

unsigned char* ast_stir_shaken_payload_get_signature ( const struct ast_stir_shaken_payload payload)

Retrieve the value for 'signature' from an ast_stir_shaken_payload.

Parameters
payloadThe payload
Return values
Thesignature

Definition at line 193 of file res_stir_shaken.c.

References NULL, and ast_stir_shaken_payload::signature.

Referenced by add_identity_header().

194 {
195  return payload ? payload->signature : NULL;
196 }
unsigned char * signature
#define NULL
Definition: resample.c:96

◆ ast_stir_shaken_sign()

struct ast_stir_shaken_payload* ast_stir_shaken_sign ( struct ast_json json)

Sign a JSON STIR/SHAKEN payload.

Note
This function will automatically add the "attest", "iat", and "origid" fields.
Parameters
jsonThe JWT to sign
Return values
ast_stir_shaken_payloadon success
NULLon failure

Definition at line 1046 of file res_stir_shaken.c.

References ao2_cleanup, ast_calloc, ast_free, ast_json_dump_string, ast_json_object_get(), ast_json_string_get(), ast_log, ast_stir_shaken_payload_free(), ast_strdup, cleanup(), ast_stir_shaken_payload::header, LOG_ERROR, NULL, ast_stir_shaken_payload::payload, ast_stir_shaken_payload::public_cert_url, ast_stir_shaken_payload::signature, stir_shaken_add_attest(), stir_shaken_add_iat(), stir_shaken_add_origid(), stir_shaken_add_x5u(), stir_shaken_certificate_get_attestation(), stir_shaken_certificate_get_by_caller_id_number(), stir_shaken_certificate_get_private_key(), stir_shaken_certificate_get_public_cert_url(), stir_shaken_sign(), and stir_shaken_verify_json().

Referenced by add_identity_header(), and AST_TEST_DEFINE().

1047 {
1048  struct ast_stir_shaken_payload *ss_payload;
1049  unsigned char *signature;
1050  const char *public_cert_url;
1051  const char *caller_id_num;
1052  const char *header;
1053  const char *payload;
1054  struct ast_json *tmp_json;
1055  char *msg = NULL;
1056  size_t msg_len;
1057  struct stir_shaken_certificate *cert = NULL;
1058 
1059  ss_payload = stir_shaken_verify_json(json);
1060  if (!ss_payload) {
1061  return NULL;
1062  }
1063 
1064  /* From the payload section of the JSON, get the orig section, and then get
1065  * the value of tn. This will be the caller ID number */
1067  ast_json_object_get(json, "payload"), "orig"), "tn"));
1068  if (!caller_id_num) {
1069  ast_log(LOG_ERROR, "Failed to get caller ID number from JWT\n");
1070  goto cleanup;
1071  }
1072 
1074  if (!cert) {
1075  ast_log(LOG_ERROR, "Failed to retrieve certificate for caller ID "
1076  "'%s'\n", caller_id_num);
1077  goto cleanup;
1078  }
1079 
1080  public_cert_url = stir_shaken_certificate_get_public_cert_url(cert);
1081  if (stir_shaken_add_x5u(json, public_cert_url)) {
1082  ast_log(LOG_ERROR, "Failed to add 'x5u' (public cert URL) to payload\n");
1083  goto cleanup;
1084  }
1085  ss_payload->public_cert_url = ast_strdup(public_cert_url);
1086 
1088  ast_log(LOG_ERROR, "Failed to add 'attest' to payload\n");
1089  goto cleanup;
1090  }
1091 
1092  if (stir_shaken_add_origid(json)) {
1093  ast_log(LOG_ERROR, "Failed to add 'origid' to payload\n");
1094  goto cleanup;
1095  }
1096 
1097  if (stir_shaken_add_iat(json)) {
1098  ast_log(LOG_ERROR, "Failed to add 'iat' to payload\n");
1099  goto cleanup;
1100  }
1101 
1102  /* Get the header and the payload. Combine them to get the message to sign */
1103  tmp_json = ast_json_object_get(json, "header");
1104  header = ast_json_dump_string(tmp_json);
1105  tmp_json = ast_json_object_get(json, "payload");
1106  payload = ast_json_dump_string(tmp_json);
1107  msg_len = strlen(header) + strlen(payload) + 2;
1108  msg = ast_calloc(1, msg_len);
1109  if (!msg) {
1110  ast_log(LOG_ERROR, "Failed to allocate space for message to sign\n");
1111  goto cleanup;
1112  }
1113  snprintf(msg, msg_len, "%s.%s", header, payload);
1114 
1116  if (!signature) {
1117  goto cleanup;
1118  }
1119 
1120  ss_payload->signature = signature;
1121  ao2_cleanup(cert);
1122  ast_free(msg);
1123 
1124  return ss_payload;
1125 
1126 cleanup:
1127  ao2_cleanup(cert);
1128  ast_stir_shaken_payload_free(ss_payload);
1129  ast_free(msg);
1130  return NULL;
1131 }
struct ast_json * header
unsigned char * signature
static int stir_shaken_add_iat(struct ast_json *json)
Adds the &#39;iat&#39; field to the JWT.
static unsigned char * stir_shaken_sign(char *json_str, EVP_PKEY *private_key)
Signs the payload and returns the signature.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:763
void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload)
Free a STIR/SHAKEN payload.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
const char * stir_shaken_certificate_get_attestation(struct stir_shaken_certificate *cert)
Get the attestation level associated with a certificate.
Definition: certificate.c:101
#define ast_log
Definition: astobj2.c:42
static struct ast_stir_shaken_payload * stir_shaken_verify_json(struct ast_json *json)
Verifies the necessary contents are in the JSON and returns a ast_stir_shaken_payload with the extrac...
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
#define LOG_ERROR
Definition: logger.h:285
struct stir_shaken_certificate * stir_shaken_certificate_get_by_caller_id_number(const char *caller_id_number)
Get a STIR/SHAKEN certificate by caller ID number.
Definition: certificate.c:84
static int stir_shaken_add_x5u(struct ast_json *json, const char *x5u)
Adds the &#39;x5u&#39; (public key URL) field to the JWT.
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
struct ast_json * payload
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
EVP_PKEY * stir_shaken_certificate_get_private_key(struct stir_shaken_certificate *cert)
Get the private key associated with a certificate.
Definition: certificate.c:106
Abstract JSON element (object, array, string, int, ...).
static int stir_shaken_add_attest(struct ast_json *json, const char *attest)
Adds the &#39;attest&#39; field to the JWT.
const char * stir_shaken_certificate_get_public_cert_url(struct stir_shaken_certificate *cert)
Get the public key URL associated with a certificate.
Definition: certificate.c:96
static int stir_shaken_add_origid(struct ast_json *json)
Adds the &#39;origid&#39; field to the JWT.

◆ ast_stir_shaken_sorcery()

struct ast_sorcery* ast_stir_shaken_sorcery ( void  )

◆ ast_stir_shaken_verify()

struct ast_stir_shaken_payload* ast_stir_shaken_verify ( const char *  header,
const char *  payload,
const char *  signature,
const char *  algorithm,
const char *  public_cert_url 
)

Verify a JSON STIR/SHAKEN payload.

Parameters
headerThe payload header
payloadThe payload section
signatureThe payload signature
algorithmThe signature algorithm
public_cert_urlThe public key URL
Return values
ast_stir_shaken_payloadon success
NULLon failure

Definition at line 620 of file res_stir_shaken.c.

References add_public_key_to_astdb(), ast_stir_shaken_payload::algorithm, ast_asprintf, ast_calloc, ast_config_AST_DATA_DIR, ast_debug, ast_free, ast_json_load_string(), ast_log, ast_stir_shaken_payload_free(), ast_strdup, ast_strlen_zero, curl_and_check_expiration(), get_path_to_public_key(), ast_stir_shaken_payload::header, LOG_ERROR, NULL, ast_stir_shaken_payload::payload, ast_stir_shaken_payload::public_cert_url, public_key_is_expired(), RAII_VAR, remove_public_key_from_astdb(), run_curl(), ast_stir_shaken_payload::signature, STIR_SHAKEN_DIR_NAME, stir_shaken_read_key(), and stir_shaken_verify_signature().

Referenced by AST_TEST_DEFINE(), and stir_shaken_incoming_request().

622 {
623  struct ast_stir_shaken_payload *ret_payload;
624  EVP_PKEY *public_key;
625  int curl = 0;
626  RAII_VAR(char *, file_path, NULL, ast_free);
627  RAII_VAR(char *, dir_path, NULL, ast_free);
628  RAII_VAR(char *, combined_str, NULL, ast_free);
629  size_t combined_size;
630 
631  if (ast_strlen_zero(header)) {
632  ast_log(LOG_ERROR, "'header' is required for STIR/SHAKEN verification\n");
633  return NULL;
634  }
635 
636  if (ast_strlen_zero(payload)) {
637  ast_log(LOG_ERROR, "'payload' is required for STIR/SHAKEN verification\n");
638  return NULL;
639  }
640 
641  if (ast_strlen_zero(signature)) {
642  ast_log(LOG_ERROR, "'signature' is required for STIR/SHAKEN verification\n");
643  return NULL;
644  }
645 
646  if (ast_strlen_zero(algorithm)) {
647  ast_log(LOG_ERROR, "'algorithm' is required for STIR/SHAKEN verification\n");
648  return NULL;
649  }
650 
652  ast_log(LOG_ERROR, "'public_cert_url' is required for STIR/SHAKEN verification\n");
653  return NULL;
654  }
655 
656  /* Check to see if we have already downloaded this public cert. The reason we
657  * store the file path is because:
658  *
659  * 1. If, for some reason, the default directory changes, we still know where
660  * to look for the files we already have.
661  *
662  * 2. In the future, if we want to add a way to store the certs in multiple
663  * {configurable) directories, we already have the storage mechanism in place.
664  * The only thing that would be left to do is pull from the configuration.
665  */
667  if (ast_asprintf(&dir_path, "%s/keys/%s", ast_config_AST_DATA_DIR, STIR_SHAKEN_DIR_NAME) < 0) {
668  return NULL;
669  }
670 
671  /* If we don't have an entry in AstDB, CURL from the provided URL */
672  if (ast_strlen_zero(file_path)) {
673  /* Remove this entry from the database, since we will be
674  * downloading a new file anyways.
675  */
677 
678  /* Go ahead and free file_path, in case anything was allocated above */
679  ast_free(file_path);
680 
681  /* Download to the default path */
682  file_path = run_curl(public_cert_url, dir_path);
683  if (!file_path) {
684  return NULL;
685  }
686 
687  /* Signal that we have already downloaded a new file, no reason to do it again */
688  curl = 1;
689 
690  /* We should have a successful download at this point, so
691  * add an entry to the database.
692  */
694  }
695 
696  /* Check to see if the cert we downloaded (or already had) is expired */
698 
699  ast_debug(3, "Public cert '%s' is expired\n", public_cert_url);
700 
702 
703  /* If this fails, then there's nothing we can do */
704  ast_free(file_path);
705  file_path = curl_and_check_expiration(public_cert_url, dir_path, &curl);
706  if (!file_path) {
707  return NULL;
708  }
709  }
710 
711  /* First attempt to read the key. If it fails, try downloading the file,
712  * unless we already did. Check for expiration again */
713  public_key = stir_shaken_read_key(file_path, 0);
714  if (!public_key) {
715 
716  ast_debug(3, "Failed first read of public key file '%s'\n", file_path);
717 
719 
720  ast_free(file_path);
721  file_path = curl_and_check_expiration(public_cert_url, dir_path, &curl);
722  if (!file_path) {
723  return NULL;
724  }
725 
726  public_key = stir_shaken_read_key(file_path, 0);
727  if (!public_key) {
728  ast_log(LOG_ERROR, "Failed to read public key from '%s'\n", file_path);
730  return NULL;
731  }
732  }
733 
734  /* Combine the header and payload to get the original signed message: header.payload */
735  combined_size = strlen(header) + strlen(payload) + 2;
736  combined_str = ast_calloc(1, combined_size);
737  if (!combined_str) {
738  ast_log(LOG_ERROR, "Failed to allocate space for message to verify\n");
739  EVP_PKEY_free(public_key);
740  return NULL;
741  }
742  snprintf(combined_str, combined_size, "%s.%s", header, payload);
743  if (stir_shaken_verify_signature(combined_str, signature, public_key)) {
744  ast_log(LOG_ERROR, "Failed to verify signature\n");
745  EVP_PKEY_free(public_key);
746  return NULL;
747  }
748 
749  /* We don't need the public key anymore */
750  EVP_PKEY_free(public_key);
751 
752  ret_payload = ast_calloc(1, sizeof(*ret_payload));
753  if (!ret_payload) {
754  ast_log(LOG_ERROR, "Failed to allocate STIR/SHAKEN payload\n");
755  return NULL;
756  }
757 
758  ret_payload->header = ast_json_load_string(header, NULL);
759  if (!ret_payload->header) {
760  ast_log(LOG_ERROR, "Failed to create JSON from header\n");
761  ast_stir_shaken_payload_free(ret_payload);
762  return NULL;
763  }
764 
765  ret_payload->payload = ast_json_load_string(payload, NULL);
766  if (!ret_payload->payload) {
767  ast_log(LOG_ERROR, "Failed to create JSON from payload\n");
768  ast_stir_shaken_payload_free(ret_payload);
769  return NULL;
770  }
771 
772  ret_payload->signature = (unsigned char *)ast_strdup(signature);
773  ret_payload->algorithm = ast_strdup(algorithm);
775 
776  return ret_payload;
777 }
struct ast_json * header
unsigned char * signature
static char * run_curl(const char *public_cert_url, const char *path)
CURL the file located at public_cert_url to the specified path.
EVP_PKEY * stir_shaken_read_key(const char *path, int priv)
Reads the public (or private) key from the specified path.
Definition: stir_shaken.c:89
static void remove_public_key_from_astdb(const char *public_cert_url)
Remove the public key details and associated information from AstDB.
void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload)
Free a STIR/SHAKEN payload.
struct ast_json * ast_json_load_string(const char *input, struct ast_json_error *error)
Parse null terminated string into a JSON object or array.
Definition: json.c:546
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
static void add_public_key_to_astdb(const char *public_cert_url, const char *filepath)
Add the public key details and file path to AstDB.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
#define ast_strlen_zero(foo)
Definition: strings.h:52
static char * curl_and_check_expiration(const char *public_cert_url, const char *path, int *curl)
Downloads the public cert from public_cert_url. If curl is non-zero, that signals CURL has already be...
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
#define LOG_ERROR
Definition: logger.h:285
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
struct ast_json * payload
static int stir_shaken_verify_signature(const char *msg, const char *signature, EVP_PKEY *public_key)
Verifies the signature using a public key.
static char * get_path_to_public_key(const char *public_cert_url)
Returns the path to the downloaded file for the provided URL.
#define STIR_SHAKEN_DIR_NAME
static int public_key_is_expired(const char *public_cert_url)
Check to see if the public key is expired.

◆ AST_TEST_DEFINE() [1/2]

AST_TEST_DEFINE ( test_stir_shaken_sign  )

Definition at line 1333 of file res_stir_shaken.c.

References ast_json_free(), ast_json_pack(), ast_stir_shaken_payload_free(), ast_stir_shaken_sign(), AST_TEST_FAIL, AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, sip_to_pjsip::info(), NULL, ast_stir_shaken_payload::payload, RAII_VAR, STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT, STIR_SHAKEN_TYPE, TEST_EXECUTE, TEST_INIT, test_stir_shaken_cleanup_cert(), test_stir_shaken_create_cert(), and test_stir_shaken_write_temp_key().

1334 {
1335  char *caller_id_number = "1234567";
1336  char file_path[] = "/tmp/stir_shaken_private.XXXXXX";
1337  RAII_VAR(char *, rm_on_exit, file_path, unlink);
1338  RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
1340 
1341  switch (cmd) {
1342  case TEST_INIT:
1343  info->name = "stir_shaken_sign";
1344  info->category = "/res/res_stir_shaken/";
1345  info->summary = "STIR/SHAKEN sign unit test";
1346  info->description =
1347  "Tests signing a JWT with a private key.";
1348  return AST_TEST_NOT_RUN;
1349  case TEST_EXECUTE:
1350  break;
1351  }
1352 
1353  /* We only need a private key to sign */
1354  test_stir_shaken_write_temp_key(file_path, 1);
1355  test_stir_shaken_create_cert(caller_id_number, file_path);
1356 
1357  /* Test missing header section */
1358  json = ast_json_pack("{s: {s: {s: s}}}", "payload", "orig", "tn", caller_id_number);
1359  payload = ast_stir_shaken_sign(json);
1360  if (payload) {
1361  ast_test_status_update(test, "Signed an invalid JWT (missing 'header')\n");
1362  test_stir_shaken_cleanup_cert(caller_id_number);
1363  return AST_TEST_FAIL;
1364  }
1365 
1366  /* Test missing payload section */
1367  ast_json_free(json);
1368  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}}", "header", "alg",
1370  "x5u", "http://testing123");
1371  payload = ast_stir_shaken_sign(json);
1372  if (payload) {
1373  ast_test_status_update(test, "Signed an invalid JWT (missing 'payload')\n");
1374  test_stir_shaken_cleanup_cert(caller_id_number);
1375  return AST_TEST_FAIL;
1376  }
1377 
1378  /* Test missing alg section */
1379  ast_json_free(json);
1380  json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "ppt",
1381  STIR_SHAKEN_PPT, "typ", STIR_SHAKEN_TYPE, "x5u", "http://testing123", "payload",
1382  "orig", "tn", caller_id_number);
1383  payload = ast_stir_shaken_sign(json);
1384  if (payload) {
1385  ast_test_status_update(test, "Signed an invalid JWT (missing 'alg')\n");
1386  test_stir_shaken_cleanup_cert(caller_id_number);
1387  return AST_TEST_FAIL;
1388  }
1389 
1390  /* Test invalid alg value */
1391  ast_json_free(json);
1392  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1393  "invalid algorithm", "ppt", STIR_SHAKEN_PPT, "typ", STIR_SHAKEN_TYPE,
1394  "x5u", "http://testing123", "payload", "orig", "tn", caller_id_number);
1395  payload = ast_stir_shaken_sign(json);
1396  if (payload) {
1397  ast_test_status_update(test, "Signed an invalid JWT (wrong 'alg')\n");
1398  test_stir_shaken_cleanup_cert(caller_id_number);
1399  return AST_TEST_FAIL;
1400  }
1401 
1402  /* Test missing ppt section */
1403  ast_json_free(json);
1404  json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1405  STIR_SHAKEN_ENCRYPTION_ALGORITHM, "typ", STIR_SHAKEN_TYPE, "x5u", "http://testing123",
1406  "payload", "orig", "tn", caller_id_number);
1407  payload = ast_stir_shaken_sign(json);
1408  if (payload) {
1409  ast_test_status_update(test, "Signed an invalid JWT (missing 'ppt')\n");
1410  test_stir_shaken_cleanup_cert(caller_id_number);
1411  return AST_TEST_FAIL;
1412  }
1413 
1414  /* Test invalid ppt value */
1415  ast_json_free(json);
1416  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1417  STIR_SHAKEN_ENCRYPTION_ALGORITHM, "ppt", "invalid ppt", "typ", STIR_SHAKEN_TYPE,
1418  "x5u", "http://testing123", "payload", "orig", "tn", caller_id_number);
1419  payload = ast_stir_shaken_sign(json);
1420  if (payload) {
1421  ast_test_status_update(test, "Signed an invalid JWT (wrong 'ppt')\n");
1422  test_stir_shaken_cleanup_cert(caller_id_number);
1423  return AST_TEST_FAIL;
1424  }
1425 
1426  /* Test missing typ section */
1427  ast_json_free(json);
1428  json = ast_json_pack("{s: {s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1429  STIR_SHAKEN_ENCRYPTION_ALGORITHM, "ppt", STIR_SHAKEN_PPT, "x5u", "http://testing123",
1430  "payload", "orig", "tn", caller_id_number);
1431  payload = ast_stir_shaken_sign(json);
1432  if (payload) {
1433  ast_test_status_update(test, "Signed an invalid JWT (missing 'typ')\n");
1434  test_stir_shaken_cleanup_cert(caller_id_number);
1435  return AST_TEST_FAIL;
1436  }
1437 
1438  /* Test invalid typ value */
1439  ast_json_free(json);
1440  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1441  STIR_SHAKEN_ENCRYPTION_ALGORITHM, "ppt", STIR_SHAKEN_PPT, "typ", "invalid typ",
1442  "x5u", "http://testing123", "payload", "orig", "tn", caller_id_number);
1443  payload = ast_stir_shaken_sign(json);
1444  if (payload) {
1445  ast_test_status_update(test, "Signed an invalid JWT (wrong 'typ')\n");
1446  test_stir_shaken_cleanup_cert(caller_id_number);
1447  return AST_TEST_FAIL;
1448  }
1449 
1450  /* Test missing orig section */
1451  ast_json_free(json);
1452  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: s}}", "header", "alg",
1454  "x5u", "http://testing123", "payload", "filler", "filler");
1455  payload = ast_stir_shaken_sign(json);
1456  if (payload) {
1457  ast_test_status_update(test, "Signed an invalid JWT (missing 'orig')\n");
1458  test_stir_shaken_cleanup_cert(caller_id_number);
1459  return AST_TEST_FAIL;
1460  }
1461 
1462  /* Test missing tn section */
1463  ast_json_free(json);
1464  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: s}}", "header", "alg",
1466  "x5u", "http://testing123", "payload", "orig", "filler");
1467  payload = ast_stir_shaken_sign(json);
1468  if (payload) {
1469  ast_test_status_update(test, "Signed an invalid JWT (missing 'tn')\n");
1470  test_stir_shaken_cleanup_cert(caller_id_number);
1471  return AST_TEST_FAIL;
1472  }
1473 
1474  /* Test valid JWT */
1475  ast_json_free(json);
1476  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1478  "x5u", "http://testing123", "payload", "orig", "tn", caller_id_number);
1479  payload = ast_stir_shaken_sign(json);
1480  if (!payload) {
1481  ast_test_status_update(test, "Failed to sign a valid JWT\n");
1482  test_stir_shaken_cleanup_cert(caller_id_number);
1483  return AST_TEST_FAIL;
1484  }
1485 
1486  test_stir_shaken_cleanup_cert(caller_id_number);
1487 
1488  return AST_TEST_PASS;
1489 }
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
void ast_json_free(void *p)
Asterisk&#39;s custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload)
Free a STIR/SHAKEN payload.
#define NULL
Definition: resample.c:96
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
struct ast_stir_shaken_payload * ast_stir_shaken_sign(struct ast_json *json)
Sign a JSON STIR/SHAKEN payload.
#define STIR_SHAKEN_ENCRYPTION_ALGORITHM
int test_stir_shaken_create_cert(const char *caller_id_number, const char *file_path)
Definition: certificate.c:306
int test_stir_shaken_cleanup_cert(const char *caller_id_number)
Definition: certificate.c:280
def info(msg)
#define STIR_SHAKEN_PPT
struct ast_json * payload
static int test_stir_shaken_write_temp_key(char *file_path, int private)
Create a private or public key certificate.
Abstract JSON element (object, array, string, int, ...).
#define STIR_SHAKEN_TYPE

◆ AST_TEST_DEFINE() [2/2]

AST_TEST_DEFINE ( test_stir_shaken_verify  )

Definition at line 1491 of file res_stir_shaken.c.

References ast_json_dump_string, ast_json_free(), ast_json_object_get(), ast_json_pack(), ast_stir_shaken_payload_free(), ast_stir_shaken_sign(), ast_stir_shaken_verify(), AST_TEST_FAIL, AST_TEST_NOT_RUN, AST_TEST_PASS, ast_test_status_update, ast_stir_shaken_payload::header, sip_to_pjsip::info(), NULL, ast_stir_shaken_payload::payload, ast_stir_shaken_payload::public_cert_url, RAII_VAR, remove_public_key_from_astdb(), STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT, STIR_SHAKEN_TYPE, TEST_EXECUTE, TEST_INIT, test_stir_shaken_add_fake_astdb_entry(), test_stir_shaken_cleanup_cert(), test_stir_shaken_create_cert(), and test_stir_shaken_write_temp_key().

1492 {
1493  char *caller_id_number = "1234567";
1494  char *public_cert_url = "http://testing123";
1495  char *header;
1496  char *payload;
1497  struct ast_json *tmp_json;
1498  char public_path[] = "/tmp/stir_shaken_public.XXXXXX";
1499  char private_path[] = "/tmp/stir_shaken_public.XXXXXX";
1500  RAII_VAR(char *, rm_on_exit_public, public_path, unlink);
1501  RAII_VAR(char *, rm_on_exit_private, private_path, unlink);
1502  RAII_VAR(struct ast_json *, json, NULL, ast_json_free);
1504  RAII_VAR(struct ast_stir_shaken_payload *, returned_payload, NULL, ast_stir_shaken_payload_free);
1505 
1506  switch (cmd) {
1507  case TEST_INIT:
1508  info->name = "stir_shaken_verify";
1509  info->category = "/res/res_stir_shaken/";
1510  info->summary = "STIR/SHAKEN verify unit test";
1511  info->description =
1512  "Tests verifying a signature with a public key";
1513  return AST_TEST_NOT_RUN;
1514  case TEST_EXECUTE:
1515  break;
1516  }
1517 
1518  /* We need the private key to sign, but we also need the corresponding
1519  * public key to verify */
1520  test_stir_shaken_write_temp_key(public_path, 0);
1521  test_stir_shaken_write_temp_key(private_path, 1);
1522  test_stir_shaken_create_cert(caller_id_number, private_path);
1523 
1524  /* Get the signature */
1525  json = ast_json_pack("{s: {s: s, s: s, s: s, s: s}, s: {s: {s: s}}}", "header", "alg",
1527  "x5u", public_cert_url, "payload", "orig", "tn", caller_id_number);
1528  signed_payload = ast_stir_shaken_sign(json);
1529  if (!signed_payload) {
1530  ast_test_status_update(test, "Failed to sign a valid JWT\n");
1531  test_stir_shaken_cleanup_cert(caller_id_number);
1532  return AST_TEST_FAIL;
1533  }
1534 
1535  /* Get the header and payload for ast_stir_shaken_verify */
1536  tmp_json = ast_json_object_get(json, "header");
1537  header = ast_json_dump_string(tmp_json);
1538  tmp_json = ast_json_object_get(json, "payload");
1539  payload = ast_json_dump_string(tmp_json);
1540 
1541  /* Test empty header parameter */
1542  returned_payload = ast_stir_shaken_verify("", payload, (const char *)signed_payload->signature,
1543  STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_cert_url);
1544  if (returned_payload) {
1545  ast_test_status_update(test, "Verified a signature with missing 'header'\n");
1546  test_stir_shaken_cleanup_cert(caller_id_number);
1547  return AST_TEST_FAIL;
1548  }
1549 
1550  /* Test empty payload parameter */
1551  returned_payload = ast_stir_shaken_verify(header, "", (const char *)signed_payload->signature,
1552  STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_cert_url);
1553  if (returned_payload) {
1554  ast_test_status_update(test, "Verified a signature with missing 'payload'\n");
1555  test_stir_shaken_cleanup_cert(caller_id_number);
1556  return AST_TEST_FAIL;
1557  }
1558 
1559  /* Test empty signature parameter */
1560  returned_payload = ast_stir_shaken_verify(header, payload, "",
1561  STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_cert_url);
1562  if (returned_payload) {
1563  ast_test_status_update(test, "Verified a signature with missing 'signature'\n");
1564  test_stir_shaken_cleanup_cert(caller_id_number);
1565  return AST_TEST_FAIL;
1566  }
1567 
1568  /* Test empty algorithm parameter */
1569  returned_payload = ast_stir_shaken_verify(header, payload, (const char *)signed_payload->signature,
1570  "", public_cert_url);
1571  if (returned_payload) {
1572  ast_test_status_update(test, "Verified a signature with missing 'algorithm'\n");
1573  test_stir_shaken_cleanup_cert(caller_id_number);
1574  return AST_TEST_FAIL;
1575  }
1576 
1577  /* Test empty public key URL */
1578  returned_payload = ast_stir_shaken_verify(header, payload, (const char *)signed_payload->signature,
1580  if (returned_payload) {
1581  ast_test_status_update(test, "Verified a signature with missing 'public key URL'\n");
1582  test_stir_shaken_cleanup_cert(caller_id_number);
1583  return AST_TEST_FAIL;
1584  }
1585 
1586  /* Trick the function into thinking we've already downloaded the key */
1587  test_stir_shaken_add_fake_astdb_entry(public_cert_url, public_path);
1588 
1589  /* Verify a valid signature */
1590  returned_payload = ast_stir_shaken_verify(header, payload, (const char *)signed_payload->signature,
1591  STIR_SHAKEN_ENCRYPTION_ALGORITHM, public_cert_url);
1592  if (!returned_payload) {
1593  ast_test_status_update(test, "Failed to verify a valid signature\n");
1594  remove_public_key_from_astdb(public_cert_url);
1595  test_stir_shaken_cleanup_cert(caller_id_number);
1596  return AST_TEST_FAIL;
1597  }
1598 
1599  remove_public_key_from_astdb(public_cert_url);
1600 
1601  test_stir_shaken_cleanup_cert(caller_id_number);
1602 
1603  return AST_TEST_PASS;
1604 }
struct ast_json * header
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
void ast_json_free(void *p)
Asterisk&#39;s custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
static void remove_public_key_from_astdb(const char *public_cert_url)
Remove the public key details and associated information from AstDB.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:763
void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload)
Free a STIR/SHAKEN payload.
#define NULL
Definition: resample.c:96
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
struct ast_stir_shaken_payload * ast_stir_shaken_sign(struct ast_json *json)
Sign a JSON STIR/SHAKEN payload.
#define STIR_SHAKEN_ENCRYPTION_ALGORITHM
int test_stir_shaken_create_cert(const char *caller_id_number, const char *file_path)
Definition: certificate.c:306
int test_stir_shaken_cleanup_cert(const char *caller_id_number)
Definition: certificate.c:280
def info(msg)
#define STIR_SHAKEN_PPT
struct ast_json * payload
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
static int test_stir_shaken_write_temp_key(char *file_path, int private)
Create a private or public key certificate.
Abstract JSON element (object, array, string, int, ...).
#define STIR_SHAKEN_TYPE
static void test_stir_shaken_add_fake_astdb_entry(const char *public_cert_url, const char *file_path)
struct ast_stir_shaken_payload * ast_stir_shaken_verify(const char *header, const char *payload, const char *signature, const char *algorithm, const char *public_cert_url)
Verify a JSON STIR/SHAKEN payload.

◆ curl_and_check_expiration()

static char* curl_and_check_expiration ( const char *  public_cert_url,
const char *  path,
int *  curl 
)
static

Downloads the public cert from public_cert_url. If curl is non-zero, that signals CURL has already been run, and we should bail here. The entry is added to AstDB as well.

Note
filename will need to be freed by the caller
Parameters
public_cert_urlThe public cert URL
pathThe path to download the file to
curlFlag signaling if we have run CURL or not
Return values
NULLon failure
fullpath filename on success

Definition at line 594 of file res_stir_shaken.c.

References add_public_key_to_astdb(), ast_free, ast_log, LOG_ERROR, NULL, public_key_is_expired(), and run_curl().

Referenced by ast_stir_shaken_verify().

595 {
596  char *filename;
597 
598  if (curl) {
599  ast_log(LOG_ERROR, "Already downloaded public key '%s'\n", path);
600  return NULL;
601  }
602 
603  filename = run_curl(public_cert_url, path);
604  if (!filename) {
605  return NULL;
606  }
607 
608  if (public_key_is_expired(public_cert_url)) {
609  ast_log(LOG_ERROR, "Newly downloaded public key '%s' is expired\n", path);
610  ast_free(filename);
611  return NULL;
612  }
613 
614  *curl = 1;
615  add_public_key_to_astdb(public_cert_url, filename);
616 
617  return filename;
618 }
static char * run_curl(const char *public_cert_url, const char *path)
CURL the file located at public_cert_url to the specified path.
#define NULL
Definition: resample.c:96
static void add_public_key_to_astdb(const char *public_cert_url, const char *filepath)
Add the public key details and file path to AstDB.
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
#define ast_free(a)
Definition: astmm.h:182
static int public_key_is_expired(const char *public_cert_url)
Check to see if the public key is expired.

◆ get_path_to_public_key()

static char* get_path_to_public_key ( const char *  public_cert_url)
static

Returns the path to the downloaded file for the provided URL.

Parameters
public_cert_urlThe public cert URL
Return values
Emptystring if not present in AstDB
Thefile path if present in AstDB

Definition at line 434 of file res_stir_shaken.c.

References ast_db_get(), ast_sha1_hash(), ast_strdup, ast_strlen_zero, and MAX_PATH_LEN.

Referenced by ast_stir_shaken_verify().

435 {
436  char hash[41];
437  char file_path[MAX_PATH_LEN];
438 
439  ast_sha1_hash(hash, public_cert_url);
440 
441  ast_db_get(hash, "path", file_path, sizeof(file_path));
442 
443  if (ast_strlen_zero(file_path)) {
444  file_path[0] = '\0';
445  }
446 
447  return ast_strdup(file_path);
448 }
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define ast_strlen_zero(foo)
Definition: strings.h:52
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
#define MAX_PATH_LEN
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:412

◆ load_module()

static int load_module ( void  )
static

Definition at line 1636 of file res_stir_shaken.c.

References ast_custom_function_register, ast_log, AST_MODFLAG_GLOBAL_SYMBOLS, AST_MODFLAG_LOAD_ORDER, AST_MODPRI_CHANNEL_DEPEND, AST_MODULE_INFO(), AST_MODULE_LOAD_DECLINE, AST_MODULE_SUPPORT_CORE, ast_sorcery_load(), ast_sorcery_open, ast_stir_shaken_sorcery(), AST_TEST_REGISTER, ASTERISK_GPL_KEY, LOG_ERROR, reload(), reload_module(), stir_shaken_certificate_load(), stir_shaken_general_load(), stir_shaken_store_load(), and unload_module().

1637 {
1638  int res = 0;
1639 
1641  ast_log(LOG_ERROR, "stir/shaken - failed to open sorcery\n");
1642  return AST_MODULE_LOAD_DECLINE;
1643  }
1644 
1645  if (stir_shaken_general_load()) {
1646  unload_module();
1647  return AST_MODULE_LOAD_DECLINE;
1648  }
1649 
1650  if (stir_shaken_store_load()) {
1651  unload_module();
1652  return AST_MODULE_LOAD_DECLINE;
1653  }
1654 
1656  unload_module();
1657  return AST_MODULE_LOAD_DECLINE;
1658  }
1659 
1661 
1663 
1664  AST_TEST_REGISTER(test_stir_shaken_sign);
1665  AST_TEST_REGISTER(test_stir_shaken_verify);
1666 
1667  return res;
1668 }
static struct ast_sorcery * stir_shaken_sorcery
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
struct ast_sorcery * ast_stir_shaken_sorcery(void)
Retrieve the stir/shaken sorcery context.
static struct ast_custom_function stir_shaken_function
int stir_shaken_certificate_load(void)
Load time initialization for the stir/shaken &#39;certificate&#39; configuration.
Definition: certificate.c:355
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
static int unload_module(void)
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
void ast_sorcery_load(const struct ast_sorcery *sorcery)
Inform any wizards to load persistent objects.
Definition: sorcery.c:1377
#define ast_sorcery_open()
Definition: sorcery.h:408
int stir_shaken_general_load(void)
Load time initialization for the stir/shaken &#39;general&#39; configuration.
Definition: general.c:248
int stir_shaken_store_load(void)
Load time initialization for the stir/shaken &#39;store&#39; configuration.
Definition: store.c:180
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508

◆ public_key_is_expired()

static int public_key_is_expired ( const char *  public_cert_url)
static

Check to see if the public key is expired.

Parameters
public_cert_urlThe public cert URL
Return values
1if expired
0if not expired

Definition at line 405 of file res_stir_shaken.c.

References ast_db_get(), ast_sha1_hash(), ast_str_to_ulong(), ast_strlen_zero, ast_tvcmp(), and ast_tvnow().

Referenced by ast_stir_shaken_verify(), and curl_and_check_expiration().

406 {
407  struct timeval current_time = ast_tvnow();
408  struct timeval expires = { .tv_sec = 0, .tv_usec = 0 };
409  char expiration[32];
410  char hash[41];
411 
412  ast_sha1_hash(hash, public_cert_url);
413  ast_db_get(hash, "expiration", expiration, sizeof(expiration));
414 
415  if (ast_strlen_zero(expiration)) {
416  return 1;
417  }
418 
419  if (ast_str_to_ulong(expiration, (unsigned long *)&expires.tv_sec)) {
420  return 1;
421  }
422 
423  return ast_tvcmp(current_time, expires) == -1 ? 0 : 1;
424 }
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_strlen_zero(foo)
Definition: strings.h:52
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
int ast_tvcmp(struct timeval _a, struct timeval _b)
Compres two struct timeval instances returning -1, 0, 1 if the first arg is smaller, equal or greater to the second.
Definition: time.h:128
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:412
int ast_str_to_ulong(const char *str, unsigned long *res)
Convert the given string to an unsigned long.
Definition: conversions.c:80

◆ reload_module()

static int reload_module ( void  )
static

Definition at line 1608 of file res_stir_shaken.c.

References ast_sorcery_reload().

Referenced by load_module().

1609 {
1610  if (stir_shaken_sorcery) {
1612  }
1613 
1614  return 0;
1615 }
static struct ast_sorcery * stir_shaken_sorcery
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition: sorcery.c:1408

◆ remove_public_key_from_astdb()

static void remove_public_key_from_astdb ( const char *  public_cert_url)
static

Remove the public key details and associated information from AstDB.

Parameters
public_cert_urlThe public cert URL

Definition at line 471 of file res_stir_shaken.c.

References ast_db_del(), ast_db_deltree(), AST_DB_FAMILY, ast_db_get(), ast_sha1_hash(), MAX_PATH_LEN, and NULL.

Referenced by ast_stir_shaken_verify(), and AST_TEST_DEFINE().

472 {
473  char hash[41];
474  char filepath[MAX_PATH_LEN];
475 
476  ast_sha1_hash(hash, public_cert_url);
477 
478  /* Remove this public key from storage */
479  ast_db_get(hash, "path", filepath, sizeof(filepath));
480 
481  /* Remove the actual file from the system */
482  remove(filepath);
483 
484  ast_db_del(AST_DB_FAMILY, public_cert_url);
485  ast_db_deltree(hash, NULL);
486 }
#define NULL
Definition: resample.c:96
#define AST_DB_FAMILY
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
#define MAX_PATH_LEN
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:412
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:429
int ast_db_deltree(const char *family, const char *keytree)
Delete one or more entries in astdb.
Definition: main/db.c:457

◆ run_curl()

static char* run_curl ( const char *  public_cert_url,
const char *  path 
)
static

CURL the file located at public_cert_url to the specified path.

Note
filename will need to be freed by the caller
Parameters
public_cert_urlThe public cert URL
pathThe path to download the file to
Return values
NULLon failure
fullpath filename on success

Definition at line 557 of file res_stir_shaken.c.

References ast_log, curl_cb_data_create(), curl_cb_data_free(), curl_public_key(), LOG_ERROR, NULL, and set_public_key_expiration().

Referenced by ast_stir_shaken_verify(), and curl_and_check_expiration().

558 {
559  struct curl_cb_data *data;
560  char *filename;
561 
562  data = curl_cb_data_create();
563  if (!data) {
564  ast_log(LOG_ERROR, "Failed to create CURL callback data\n");
565  return NULL;
566  }
567 
568  filename = curl_public_key(public_cert_url, path, data);
569  if (!filename) {
570  ast_log(LOG_ERROR, "Could not retrieve public key for '%s'\n", public_cert_url);
571  curl_cb_data_free(data);
572  return NULL;
573  }
574 
575  set_public_key_expiration(public_cert_url, data);
576  curl_cb_data_free(data);
577 
578  return filename;
579 }
void curl_cb_data_free(struct curl_cb_data *data)
Free a curl_cb_data struct.
Definition: curl.c:51
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
char * curl_public_key(const char *public_cert_url, const char *path, struct curl_cb_data *data)
CURL the public key from the provided URL to the specified path.
Definition: curl.c:185
#define LOG_ERROR
Definition: logger.h:285
static void set_public_key_expiration(const char *public_cert_url, const struct curl_cb_data *data)
Sets the expiration for the public key based on the provided fields. If Cache-Control is present...
struct curl_cb_data * curl_cb_data_create(void)
Allocate memory for a curl_cb_data struct.
Definition: curl.c:42

◆ set_public_key_expiration()

static void set_public_key_expiration ( const char *  public_cert_url,
const struct curl_cb_data data 
)
static

Sets the expiration for the public key based on the provided fields. If Cache-Control is present, use it. Otherwise, use Expires.

Parameters
public_cert_urlThe URL to the public certificate
dataThe CURL callback data containing expiration data

Definition at line 352 of file res_stir_shaken.c.

References ast_db_put(), ast_sha1_hash(), ast_str_to_uint(), ast_strlen_zero, ast_tvnow(), curl_cb_data_get_cache_control(), curl_cb_data_get_expires(), EXPIRATION_BUFFER, and value.

Referenced by run_curl().

353 {
354  char time_buf[32];
355  char *value;
356  struct timeval actual_expires = ast_tvnow();
357  char hash[41];
358 
359  ast_sha1_hash(hash, public_cert_url);
360 
361  value = curl_cb_data_get_cache_control(data);
362  if (!ast_strlen_zero(value)) {
363  char *str_max_age;
364 
365  str_max_age = strstr(value, "s-maxage");
366  if (!str_max_age) {
367  str_max_age = strstr(value, "max-age");
368  }
369 
370  if (str_max_age) {
371  unsigned int max_age;
372  char *equal = strchr(str_max_age, '=');
373  if (equal && !ast_str_to_uint(equal + 1, &max_age)) {
374  actual_expires.tv_sec += max_age;
375  }
376  }
377  } else {
378  value = curl_cb_data_get_expires(data);
379  if (!ast_strlen_zero(value)) {
380  struct tm expires_time;
381 
382  strptime(value, "%a, %d %b %Y %T %z", &expires_time);
383  expires_time.tm_isdst = -1;
384  actual_expires.tv_sec = mktime(&expires_time);
385  }
386  }
387 
388  if (ast_strlen_zero(value)) {
389  actual_expires.tv_sec += EXPIRATION_BUFFER;
390  }
391 
392  snprintf(time_buf, sizeof(time_buf), "%30lu", actual_expires.tv_sec);
393 
394  ast_db_put(hash, "expiration", time_buf);
395 }
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define EXPIRATION_BUFFER
int value
Definition: syslog.c:37
#define ast_strlen_zero(foo)
Definition: strings.h:52
char * curl_cb_data_get_expires(const struct curl_cb_data *data)
Get the expires field from a curl_cb_data struct.
Definition: curl.c:72
char * curl_cb_data_get_cache_control(const struct curl_cb_data *data)
Get the cache_control field from a curl_cb_data struct.
Definition: curl.c:63
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
int ast_str_to_uint(const char *str, unsigned int *res)
Convert the given string to an unsigned integer.
Definition: conversions.c:56
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:327

◆ stir_shaken_add_attest()

static int stir_shaken_add_attest ( struct ast_json json,
const char *  attest 
)
static

Adds the 'attest' field to the JWT.

Parameters
jsonThe JWT
attestThe value to set attest to
Return values
0on success
-1on failure

Definition at line 990 of file res_stir_shaken.c.

References ast_json_object_get(), ast_json_object_set(), ast_json_string_create(), and value.

Referenced by ast_stir_shaken_sign().

991 {
992  struct ast_json *value;
993 
994  value = ast_json_string_create(attest);
995  if (!value) {
996  return -1;
997  }
998 
999  return ast_json_object_set(ast_json_object_get(json, "payload"), "attest", value);
1000 }
int value
Definition: syslog.c:37
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:404
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
Definition: json.c:268
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
Abstract JSON element (object, array, string, int, ...).

◆ stir_shaken_add_iat()

static int stir_shaken_add_iat ( struct ast_json json)
static

Adds the 'iat' field to the JWT.

Parameters
jsonThe JWT
Return values
0on success
-1on failure

Definition at line 1033 of file res_stir_shaken.c.

References ast_json_integer_create(), ast_json_object_get(), ast_json_object_set(), ast_tvnow(), and value.

Referenced by ast_stir_shaken_sign().

1034 {
1035  struct ast_json *value;
1036  struct timeval tv;
1037  int timestamp;
1038 
1039  tv = ast_tvnow();
1040  timestamp = tv.tv_sec + tv.tv_usec / 1000;
1041  value = ast_json_integer_create(timestamp);
1042 
1043  return ast_json_object_set(ast_json_object_get(json, "payload"), "iat", value);
1044 }
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
int value
Definition: syslog.c:37
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:404
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
Abstract JSON element (object, array, string, int, ...).
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.
Definition: json.c:317

◆ stir_shaken_add_origid()

static int stir_shaken_add_origid ( struct ast_json json)
static

Adds the 'origid' field to the JWT.

Parameters
jsonThe JWT
Return values
0on success
-1on failure

Definition at line 1010 of file res_stir_shaken.c.

References ast_json_object_get(), ast_json_object_set(), ast_json_string_create(), ast_uuid_generate_str(), AST_UUID_STR_LEN, and value.

Referenced by ast_stir_shaken_sign().

1011 {
1012  struct ast_json *value;
1013  char uuid_str[AST_UUID_STR_LEN];
1014 
1015  ast_uuid_generate_str(uuid_str, sizeof(uuid_str));
1016  if (strlen(uuid_str) != (AST_UUID_STR_LEN - 1)) {
1017  return -1;
1018  }
1019 
1020  value = ast_json_string_create(uuid_str);
1021 
1022  return ast_json_object_set(ast_json_object_get(json, "payload"), "origid", value);
1023 }
#define AST_UUID_STR_LEN
Definition: uuid.h:27
int value
Definition: syslog.c:37
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:404
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
Definition: json.c:268
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition: uuid.c:143
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
Abstract JSON element (object, array, string, int, ...).

◆ stir_shaken_add_x5u()

static int stir_shaken_add_x5u ( struct ast_json json,
const char *  x5u 
)
static

Adds the 'x5u' (public key URL) field to the JWT.

Parameters
jsonThe JWT
x5uThe public key URL
Return values
0on success
-1on failure

Definition at line 969 of file res_stir_shaken.c.

References ast_json_object_get(), ast_json_object_set(), ast_json_string_create(), and value.

Referenced by ast_stir_shaken_sign().

970 {
971  struct ast_json *value;
972 
973  value = ast_json_string_create(x5u);
974  if (!value) {
975  return -1;
976  }
977 
978  return ast_json_object_set(ast_json_object_get(json, "header"), "x5u", value);
979 }
int value
Definition: syslog.c:37
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:404
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
Definition: json.c:268
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
Abstract JSON element (object, array, string, int, ...).

◆ stir_shaken_datastore_destroy_cb()

static void stir_shaken_datastore_destroy_cb ( void *  data)
static

The callback to destroy a stir_shaken_datastore.

Parameters
dataThe stir_shaken_datastore

Definition at line 265 of file res_stir_shaken.c.

References stir_shaken_datastore_free().

266 {
267  struct stir_shaken_datastore *datastore = data;
268  stir_shaken_datastore_free(datastore);
269 }
static void stir_shaken_datastore_free(struct stir_shaken_datastore *datastore)
Frees a stir_shaken_datastore structure.

◆ stir_shaken_datastore_free()

static void stir_shaken_datastore_free ( struct stir_shaken_datastore datastore)
static

Frees a stir_shaken_datastore structure.

Parameters
datastoreThe datastore to free

Definition at line 249 of file res_stir_shaken.c.

References ast_free, stir_shaken_datastore::attestation, and stir_shaken_datastore::identity.

Referenced by ast_stir_shaken_add_verification(), and stir_shaken_datastore_destroy_cb().

250 {
251  if (!datastore) {
252  return;
253  }
254 
255  ast_free(datastore->identity);
256  ast_free(datastore->attestation);
257  ast_free(datastore);
258 }
#define ast_free(a)
Definition: astmm.h:182

◆ stir_shaken_read()

static int stir_shaken_read ( struct ast_channel chan,
const char *  function,
char *  data,
char *  buf,
size_t  len 
)
static

Retrieves STIR/SHAKEN verification information for the channel via dialplan. Examples:

STIR_SHAKEN(count) STIR_SHAKEN(0, identity) STIR_SHAKEN(1, attestation) STIR_SHAKEN(27, verify_result)

Return values
-1on failure
0on success

Definition at line 1145 of file res_stir_shaken.c.

References args, AST_APP_ARG, ast_channel_datastores(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DECLARE_APP_ARGS, AST_LIST_TRAVERSE, ast_log, AST_STANDARD_APP_ARGS, ast_str_to_uint(), ast_strdupa, ast_strip(), ast_strlen_zero, stir_shaken_datastore::attestation, ast_datastore::data, first, stir_shaken_datastore::identity, ast_datastore::info, LOG_ERROR, LOG_WARNING, parse(), stir_shaken_verification_result_to_string(), and stir_shaken_datastore::verify_result.

1147 {
1148  struct stir_shaken_datastore *ss_datastore;
1149  struct ast_datastore *datastore;
1150  char *parse;
1151  char *first;
1152  char *second;
1153  unsigned int target_index, current_index = 0;
1155  AST_APP_ARG(first_param);
1156  AST_APP_ARG(second_param);
1157  );
1158 
1159  if (ast_strlen_zero(data)) {
1160  ast_log(LOG_WARNING, "%s requires at least one argument\n", function);
1161  return -1;
1162  }
1163 
1164  if (!chan) {
1165  ast_log(LOG_ERROR, "No channel for %s function\n", function);
1166  return -1;
1167  }
1168 
1169  parse = ast_strdupa(data);
1170 
1171  AST_STANDARD_APP_ARGS(args, parse);
1172 
1173  first = ast_strip(args.first_param);
1174  if (ast_strlen_zero(first)) {
1175  ast_log(LOG_ERROR, "An argument must be passed to %s\n", function);
1176  return -1;
1177  }
1178 
1179  second = ast_strip(args.second_param);
1180 
1181  /* Check if we are only looking for the number of STIR/SHAKEN verification results */
1182  if (!strcasecmp(first, "count")) {
1183 
1184  size_t count = 0;
1185 
1186  if (!ast_strlen_zero(second)) {
1187  ast_log(LOG_ERROR, "%s only takes 1 paramater for 'count'\n", function);
1188  return -1;
1189  }
1190 
1191  ast_channel_lock(chan);
1192  AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1193  if (datastore->info != &stir_shaken_datastore_info) {
1194  continue;
1195  }
1196  count++;
1197  }
1198  ast_channel_unlock(chan);
1199 
1200  snprintf(buf, len, "%zu", count);
1201  return 0;
1202  }
1203 
1204  /* If we aren't doing a count, then there should be two parameters. The field
1205  * we are searching for will be the second parameter. The index is the first.
1206  */
1207  if (ast_strlen_zero(second)) {
1208  ast_log(LOG_ERROR, "Retrieving a value using %s requires two paramaters (index, value) "
1209  "- only index was given\n", function);
1210  return -1;
1211  }
1212 
1213  if (ast_str_to_uint(first, &target_index)) {
1214  ast_log(LOG_ERROR, "Failed to convert index %s to integer for function %s\n",
1215  first, function);
1216  return -1;
1217  }
1218 
1219  /* We don't store by uid for the datastore, so just search for the specified index */
1220  ast_channel_lock(chan);
1221  AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1222  if (datastore->info != &stir_shaken_datastore_info) {
1223  continue;
1224  }
1225 
1226  if (current_index == target_index) {
1227  break;
1228  }
1229 
1230  current_index++;
1231  }
1232  ast_channel_unlock(chan);
1233  if (current_index != target_index || !datastore) {
1234  ast_log(LOG_WARNING, "No STIR/SHAKEN results for index '%s'\n", first);
1235  return -1;
1236  }
1237  ss_datastore = datastore->data;
1238 
1239  if (!strcasecmp(second, "identity")) {
1240  ast_copy_string(buf, ss_datastore->identity, len);
1241  } else if (!strcasecmp(second, "attestation")) {
1242  ast_copy_string(buf, ss_datastore->attestation, len);
1243  } else if (!strcasecmp(second, "verify_result")) {
1245  } else {
1246  ast_log(LOG_ERROR, "No such value '%s' for %s\n", second, function);
1247  return -1;
1248  }
1249 
1250  return 0;
1251 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
static const char * stir_shaken_verification_result_to_string(enum ast_stir_shaken_verification_result result)
Convert an ast_stir_shaken_verification_result to string representation.
static const struct ast_datastore_info stir_shaken_datastore_info
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
Structure for a data store object.
Definition: datastore.h:68
const char * args
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
struct ast_datastore_list * ast_channel_datastores(struct ast_channel *chan)
const struct ast_datastore_info * info
Definition: datastore.h:71
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define LOG_ERROR
Definition: logger.h:285
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct sla_ringing_trunk * first
Definition: app_meetme.c:1092
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
enum ast_stir_shaken_verification_result verify_result
int ast_str_to_uint(const char *str, unsigned int *res)
Convert the given string to an unsigned integer.
Definition: conversions.c:56
void * data
Definition: datastore.h:70
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
Definition: search.h:40
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define AST_APP_ARG(name)
Define an application argument.

◆ stir_shaken_sign()

static unsigned char* stir_shaken_sign ( char *  json_str,
EVP_PKEY *  private_key 
)
static

Signs the payload and returns the signature.

Parameters
json_strThe string representation of the JSON
private_keyThe private key used to sign the payload
Return values
signatureon success
NULLon failure

Definition at line 892 of file res_stir_shaken.c.

References ast_base64url_encode(), ast_calloc, ast_free, ast_log, cleanup(), LOG_ERROR, NULL, and ast_stir_shaken_payload::signature.

Referenced by ast_stir_shaken_sign().

893 {
894  EVP_MD_CTX *mdctx = NULL;
895  int ret = 0;
896  unsigned char *encoded_signature = NULL;
897  unsigned char *signature = NULL;
898  size_t encoded_length = 0;
899  size_t signature_length = 0;
900 
901  mdctx = EVP_MD_CTX_create();
902  if (!mdctx) {
903  ast_log(LOG_ERROR, "Failed to create Message Digest Context\n");
904  goto cleanup;
905  }
906 
907  ret = EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, private_key);
908  if (ret != 1) {
909  ast_log(LOG_ERROR, "Failed to initialize Message Digest Context\n");
910  goto cleanup;
911  }
912 
913  ret = EVP_DigestSignUpdate(mdctx, json_str, strlen(json_str));
914  if (ret != 1) {
915  ast_log(LOG_ERROR, "Failed to update Message Digest Context\n");
916  goto cleanup;
917  }
918 
919  ret = EVP_DigestSignFinal(mdctx, NULL, &signature_length);
920  if (ret != 1) {
921  ast_log(LOG_ERROR, "Failed initial phase of Message Digest Context signing\n");
922  goto cleanup;
923  }
924 
925  signature = ast_calloc(1, sizeof(unsigned char) * signature_length);
926  if (!signature) {
927  ast_log(LOG_ERROR, "Failed to allocate space for signature\n");
928  goto cleanup;
929  }
930 
931  ret = EVP_DigestSignFinal(mdctx, signature, &signature_length);
932  if (ret != 1) {
933  ast_log(LOG_ERROR, "Failed final phase of Message Digest Context signing\n");
934  goto cleanup;
935  }
936 
937  /* There are 6 bits to 1 base64 URL digit, so in order to get the size of the base64 encoded
938  * signature, we need to multiply by the number of bits in a byte and divide by 6. Since
939  * there's rounding when doing base64 conversions, add 3 bytes, just in case, and account
940  * for padding. Add another byte for the NULL-terminator.
941  */
942  encoded_length = ((signature_length * 4 / 3 + 3) & ~3) + 1;
943  encoded_signature = ast_calloc(1, encoded_length);
944  if (!encoded_signature) {
945  ast_log(LOG_ERROR, "Failed to allocate space for encoded signature\n");
946  goto cleanup;
947  }
948 
949  ast_base64url_encode((char *)encoded_signature, signature, signature_length, encoded_length);
950 
951 cleanup:
952  if (mdctx) {
953  EVP_MD_CTX_destroy(mdctx);
954  }
955  ast_free(signature);
956 
957  return encoded_signature;
958 }
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
int ast_base64url_encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64 URL.
Definition: main/utils.c:516
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124

◆ stir_shaken_verification_result_to_string()

static const char* stir_shaken_verification_result_to_string ( enum ast_stir_shaken_verification_result  result)
static

Convert an ast_stir_shaken_verification_result to string representation.

Parameters
resultThe result to convert
Return values
emptystring if not a valid enum value
stringrepresentation of result otherwise

Definition at line 216 of file res_stir_shaken.c.

References AST_STIR_SHAKEN_VERIFY_MISMATCH, AST_STIR_SHAKEN_VERIFY_NOT_PRESENT, AST_STIR_SHAKEN_VERIFY_PASSED, and AST_STIR_SHAKEN_VERIFY_SIGNATURE_FAILED.

Referenced by stir_shaken_read().

217 {
218  switch (result) {
220  return "Verification not present";
222  return "Signature failed";
224  return "Verification mismatch";
226  return "Verification passed";
227  default:
228  break;
229  }
230 
231  return "";
232 }
static PGresult * result
Definition: cel_pgsql.c:88

◆ stir_shaken_verify_json()

static struct ast_stir_shaken_payload* stir_shaken_verify_json ( struct ast_json json)
static

Verifies the necessary contents are in the JSON and returns a ast_stir_shaken_payload with the extracted values.

Parameters
jsonThe JSON to verify
Returns
ast_stir_shaken_payload on success
NULL on failure

Definition at line 788 of file res_stir_shaken.c.

References ast_stir_shaken_payload::algorithm, ast_calloc, ast_json_deep_copy(), ast_json_object_get(), ast_json_string_get(), ast_log, ast_stir_shaken_payload_free(), ast_strdup, ast_strlen_zero, cleanup(), ast_stir_shaken_payload::header, LOG_ERROR, NULL, ast_stir_shaken_payload::payload, STIR_SHAKEN_ENCRYPTION_ALGORITHM, STIR_SHAKEN_PPT, and STIR_SHAKEN_TYPE.

Referenced by ast_stir_shaken_sign().

789 {
791  struct ast_json *obj;
792  const char *val;
793 
794  payload = ast_calloc(1, sizeof(*payload));
795  if (!payload) {
796  ast_log(LOG_ERROR, "Failed to allocate STIR/SHAKEN payload\n");
797  goto cleanup;
798  }
799 
800  /* Look through the header first */
801  obj = ast_json_object_get(json, "header");
802  if (!obj) {
803  ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have the required field 'header'\n");
804  goto cleanup;
805  }
806 
807  payload->header = ast_json_deep_copy(obj);
808  if (!payload->header) {
809  ast_log(LOG_ERROR, "STIR_SHAKEN payload failed to copy 'header'\n");
810  goto cleanup;
811  }
812 
813  /* Check the ppt value for "shaken" */
814  val = ast_json_string_get(ast_json_object_get(obj, "ppt"));
815  if (ast_strlen_zero(val)) {
816  ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have the required field 'ppt'\n");
817  goto cleanup;
818  }
819  if (strcmp(val, STIR_SHAKEN_PPT)) {
820  ast_log(LOG_ERROR, "STIR/SHAKEN JWT field 'ppt' did not have "
821  "required value '%s' (was '%s')\n", STIR_SHAKEN_PPT, val);
822  goto cleanup;
823  }
824 
825  /* Check the typ value for "passport" */
826  val = ast_json_string_get(ast_json_object_get(obj, "typ"));
827  if (ast_strlen_zero(val)) {
828  ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have the required field 'typ'\n");
829  goto cleanup;
830  }
831  if (strcmp(val, STIR_SHAKEN_TYPE)) {
832  ast_log(LOG_ERROR, "STIR/SHAKEN JWT field 'typ' did not have "
833  "required value '%s' (was '%s')\n", STIR_SHAKEN_TYPE, val);
834  goto cleanup;
835  }
836 
837  /* Check the alg value for "ES256" */
838  val = ast_json_string_get(ast_json_object_get(obj, "alg"));
839  if (ast_strlen_zero(val)) {
840  ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have required field 'alg'\n");
841  goto cleanup;
842  }
843  if (strcmp(val, STIR_SHAKEN_ENCRYPTION_ALGORITHM)) {
844  ast_log(LOG_ERROR, "STIR/SHAKEN JWT field 'alg' did not have "
845  "required value '%s' (was '%s')\n", STIR_SHAKEN_ENCRYPTION_ALGORITHM, val);
846  goto cleanup;
847  }
848 
849  payload->algorithm = ast_strdup(val);
850  if (!payload->algorithm) {
851  ast_log(LOG_ERROR, "STIR/SHAKEN payload failed to copy 'algorithm'\n");
852  goto cleanup;
853  }
854 
855  /* Now let's check the payload section */
856  obj = ast_json_object_get(json, "payload");
857  if (!obj) {
858  ast_log(LOG_ERROR, "STIR/SHAKEN payload JWT did not have required field 'payload'\n");
859  goto cleanup;
860  }
861 
862  /* Check the orig tn value for not NULL */
864  if (ast_strlen_zero(val)) {
865  ast_log(LOG_ERROR, "STIR/SHAKEN JWT did not have required field 'orig->tn'\n");
866  goto cleanup;
867  }
868 
869  /* Payload seems sane. Copy it and return on success */
870  payload->payload = ast_json_deep_copy(obj);
871  if (!payload->payload) {
872  ast_log(LOG_ERROR, "STIR/SHAKEN payload failed to copy 'payload'\n");
873  goto cleanup;
874  }
875 
876  return payload;
877 
878 cleanup:
880  return NULL;
881 }
struct ast_json * header
Definition: ast_expr2.c:325
void ast_stir_shaken_payload_free(struct ast_stir_shaken_payload *payload)
Free a STIR/SHAKEN payload.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_log
Definition: astobj2.c:42
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
#define STIR_SHAKEN_ENCRYPTION_ALGORITHM
#define LOG_ERROR
Definition: logger.h:285
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define STIR_SHAKEN_PPT
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
struct ast_json * payload
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
Abstract JSON element (object, array, string, int, ...).
#define STIR_SHAKEN_TYPE
struct ast_json * ast_json_deep_copy(const struct ast_json *value)
Copy a JSON value, and its children.
Definition: json.c:620

◆ stir_shaken_verify_signature()

static int stir_shaken_verify_signature ( const char *  msg,
const char *  signature,
EVP_PKEY *  public_key 
)
static

Verifies the signature using a public key.

Parameters
msgThe payload
signatureThe signature to verify
public_keyThe public key used for verification
Return values
-1on failure
0on success

Definition at line 498 of file res_stir_shaken.c.

References ast_base64url_decode(), ast_calloc, ast_free, ast_log, LOG_ERROR, and NULL.

Referenced by ast_stir_shaken_verify().

499 {
500  EVP_MD_CTX *mdctx = NULL;
501  int ret = 0;
502  unsigned char *decoded_signature;
503  size_t signature_length, decoded_signature_length;
504 
505  mdctx = EVP_MD_CTX_create();
506  if (!mdctx) {
507  ast_log(LOG_ERROR, "Failed to create Message Digest Context\n");
508  return -1;
509  }
510 
511  ret = EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, public_key);
512  if (ret != 1) {
513  ast_log(LOG_ERROR, "Failed to initialize Message Digest Context\n");
514  EVP_MD_CTX_destroy(mdctx);
515  return -1;
516  }
517 
518  ret = EVP_DigestVerifyUpdate(mdctx, (unsigned char *)msg, strlen(msg));
519  if (ret != 1) {
520  ast_log(LOG_ERROR, "Failed to update Message Digest Context\n");
521  EVP_MD_CTX_destroy(mdctx);
522  return -1;
523  }
524 
525  /* We need to decode the signature from base64 URL to bytes. Make sure we have
526  * at least enough characters for this check */
527  signature_length = strlen(signature);
528  decoded_signature_length = (signature_length * 3 / 4);
529  decoded_signature = ast_calloc(1, decoded_signature_length);
530  ast_base64url_decode(decoded_signature, signature, decoded_signature_length);
531 
532  ret = EVP_DigestVerifyFinal(mdctx, decoded_signature, decoded_signature_length);
533  if (ret != 1) {
534  ast_log(LOG_ERROR, "Failed final phase of signature verification\n");
535  EVP_MD_CTX_destroy(mdctx);
536  ast_free(decoded_signature);
537  return -1;
538  }
539 
540  EVP_MD_CTX_destroy(mdctx);
541  ast_free(decoded_signature);
542 
543  return 0;
544 }
#define NULL
Definition: resample.c:96
#define ast_log
Definition: astobj2.c:42
int ast_base64url_decode(unsigned char *dst, const char *src, int max)
Decode data from base64 URL.
Definition: main/utils.c:427
#define LOG_ERROR
Definition: logger.h:285
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204

◆ test_stir_shaken_add_fake_astdb_entry()

static void test_stir_shaken_add_fake_astdb_entry ( const char *  public_cert_url,
const char *  file_path 
)
static

Definition at line 1260 of file res_stir_shaken.c.

References add_public_key_to_astdb(), ast_db_put(), ast_sha1_hash(), and ast_tvnow().

Referenced by AST_TEST_DEFINE().

1261 {
1262  struct timeval expires = ast_tvnow();
1263  char time_buf[32];
1264  char hash[41];
1265 
1266  ast_sha1_hash(hash, public_cert_url);
1267  add_public_key_to_astdb(public_cert_url, file_path);
1268  snprintf(time_buf, sizeof(time_buf), "%30lu", expires.tv_sec + 300);
1269 
1270  ast_db_put(hash, "expiration", time_buf);
1271 }
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
static void add_public_key_to_astdb(const char *public_cert_url, const char *filepath)
Add the public key details and file path to AstDB.
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: main/utils.c:264
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:327

◆ test_stir_shaken_write_temp_key()

static int test_stir_shaken_write_temp_key ( char *  file_path,
int  private 
)
static

Create a private or public key certificate.

Parameters
file_pathThe path of the file to create
privateSet to 0 if public, 1 if private
Return values
-1on failure
0on success

Definition at line 1282 of file res_stir_shaken.c.

References ast_log, errno, make_ari_stubs::file, LOG_ERROR, and type.

Referenced by AST_TEST_DEFINE().

1283 {
1284  FILE *file;
1285  int fd;
1286  char *data;
1287  char *type = private ? "private" : "public";
1288  char *private_data =
1289  "-----BEGIN EC PRIVATE KEY-----\n"
1290  "MHcCAQEEIC+xv2GKNTDd81vJM8rwGAGNqgklKKxz9Qejn+pcRPC1oAoGCCqGSM49\n"
1291  "AwEHoUQDQgAEq12QXu8lH295ZMZ4udKy5VV8wVgE4qSOnkdofn3hEDsh6QTKTZg9\n"
1292  "W6PncYAVnmOFRL4cTGRbmAIShN4naZk2Yg==\n"
1293  "-----END EC PRIVATE KEY-----";
1294  char *public_data =
1295  "-----BEGIN CERTIFICATE-----\n"
1296  "MIIBzDCCAXGgAwIBAgIUXDt6EC0OixT1iRSSPV3jB/zQAlQwCgYIKoZIzj0EAwIw\n"
1297  "RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu\n"
1298  "dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTA0MTMwNjM3MjRaFw0yMzA3MTcw\n"
1299  "NjM3MjRaMGoxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTESMBAGA1UEBwwJU29t\n"
1300  "ZXdoZXJlMRowGAYDVQQKDBFBY21lVGVsZWNvbSwgSW5jLjENMAsGA1UECwwEVk9J\n"
1301  "UDEPMA0GA1UEAwwGU0hBS0VOMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEq12Q\n"
1302  "Xu8lH295ZMZ4udKy5VV8wVgE4qSOnkdofn3hEDsh6QTKTZg9W6PncYAVnmOFRL4c\n"
1303  "TGRbmAIShN4naZk2YqMaMBgwFgYIKwYBBQUHARoECjAIoAYWBDEwMDEwCgYIKoZI\n"
1304  "zj0EAwIDSQAwRgIhAMa9Ky38DgVaIgVm9Mgws/qN3zxjMQXfxEExAbDwyq/WAiEA\n"
1305  "zbC29mvtSulwbvQJ4fBdFU84cFC3Ctu1QrCeFOiZHc4=\n"
1306  "-----END CERTIFICATE-----";
1307 
1308  fd = mkstemp(file_path);
1309  if (fd < 0) {
1310  ast_log(LOG_ERROR, "Failed to create temp %s file: %s\n", type, strerror(errno));
1311  return -1;
1312  }
1313 
1314  file = fdopen(fd, "w");
1315  if (!file) {
1316  ast_log(LOG_ERROR, "Failed to create temp %s key file: %s\n", type, strerror(errno));
1317  close(fd);
1318  return -1;
1319  }
1320 
1321  data = private ? private_data : public_data;
1322  if (fputs(data, file) == EOF) {
1323  ast_log(LOG_ERROR, "Failed to write temp %s key file\n", type);
1324  fclose(file);
1325  return -1;
1326  }
1327 
1328  fclose(file);
1329 
1330  return 0;
1331 }
static const char type[]
Definition: chan_ooh323.c:109
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
int errno

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1617 of file res_stir_shaken.c.

References ast_custom_function_unregister(), ast_sorcery_unref, AST_TEST_UNREGISTER, NULL, stir_shaken_certificate_unload(), stir_shaken_general_unload(), and stir_shaken_store_unload().

Referenced by load_module().

1618 {
1619  int res = 0;
1620 
1624 
1627 
1629 
1630  AST_TEST_UNREGISTER(test_stir_shaken_sign);
1631  AST_TEST_UNREGISTER(test_stir_shaken_verify);
1632 
1633  return res;
1634 }
int stir_shaken_general_unload(void)
Unload time cleanup for the stir/shaken &#39;general&#39; configuration.
Definition: general.c:232
static struct ast_sorcery * stir_shaken_sorcery
#define NULL
Definition: resample.c:96
static struct ast_custom_function stir_shaken_function
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1502
int stir_shaken_certificate_unload(void)
Unload time cleanup for the stir/shaken &#39;certificate&#39; configuration.
Definition: certificate.c:347
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
int stir_shaken_store_unload(void)
Unload time cleanup for the stir/shaken &#39;store&#39; configuration.
Definition: store.c:172

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "STIR/SHAKEN Module for Asterisk" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1, .requires = "res_curl", }
static

Definition at line 1677 of file res_stir_shaken.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1677 of file res_stir_shaken.c.

◆ stir_shaken_datastore_info

const struct ast_datastore_info stir_shaken_datastore_info
static
Initial value:
= {
.type = "STIR/SHAKEN VERIFICATION",
}
static void stir_shaken_datastore_destroy_cb(void *data)
The callback to destroy a stir_shaken_datastore.

Definition at line 272 of file res_stir_shaken.c.

◆ stir_shaken_function

struct ast_custom_function stir_shaken_function
static
Initial value:
= {
.name = "STIR_SHAKEN",
}
static int stir_shaken_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
Retrieves STIR/SHAKEN verification information for the channel via dialplan. Examples: ...

Definition at line 1253 of file res_stir_shaken.c.

◆ stir_shaken_sorcery

struct ast_sorcery* stir_shaken_sorcery
static

Definition at line 144 of file res_stir_shaken.c.

Referenced by ast_stir_shaken_sorcery().