21 #define _TRACE_PREFIX_ "a",__LINE__, ""
29 #include "stir_shaken.h"
31 static const char *as_rc_map[] = {
32 [AST_STIR_SHAKEN_AS_SUCCESS] =
"success",
33 [AST_STIR_SHAKEN_AS_DISABLED] =
"disabled",
34 [AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS] =
"invalid_arguments",
35 [AST_STIR_SHAKEN_AS_MISSING_PARAMETERS] =
"missing_parameters",
36 [AST_STIR_SHAKEN_AS_INTERNAL_ERROR] =
"internal_error",
37 [AST_STIR_SHAKEN_AS_NO_TN_FOR_CALLERID] =
"no_tn_for_callerid",
38 [AST_STIR_SHAKEN_AS_NO_PRIVATE_KEY_AVAIL] =
"no_private_key_avail",
39 [AST_STIR_SHAKEN_AS_NO_PUBLIC_CERT_URL_AVAIL] =
"no_public_cert_url_avail",
40 [AST_STIR_SHAKEN_AS_NO_ATTEST_LEVEL] =
"no_attest_level",
41 [AST_STIR_SHAKEN_AS_IDENTITY_HDR_EXISTS] =
"identity_header_exists",
42 [AST_STIR_SHAKEN_AS_NO_TO_HDR] =
"no_to_hdr",
43 [AST_STIR_SHAKEN_AS_TO_HDR_BAD_URI] =
"to_hdr_bad_uri",
44 [AST_STIR_SHAKEN_AS_SIGN_ENCODE_FAILURE]
"sign_encode_failure",
47 const char *as_response_code_to_str(
48 enum ast_stir_shaken_as_response_code as_rc)
51 as_rc_map[as_rc] : NULL;
54 static void ctx_destructor(
void *obj)
58 ao2_cleanup(ctx->etn);
65 enum ast_stir_shaken_as_response_code
66 ast_stir_shaken_as_ctx_create(
const char *orig_tn,
68 const char *profile_name,
75 RAII_VAR(
char *, canon_dest_tn , canonicalize_tn_alloc(dest_tn), ast_free);
76 RAII_VAR(
char *, canon_orig_tn , canonicalize_tn_alloc(orig_tn), ast_free);
78 SCOPE_ENTER(3,
"%s: Enter\n", tag);
81 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS,
82 LOG_ERROR,
"%s: Must provide caller_id/orig_tn\n", tag);
86 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS,
87 LOG_ERROR,
"%s: Must provide dest_tn\n", tag);
90 if (ast_strlen_zero(tag)) {
91 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS,
92 LOG_ERROR,
"%s: Must provide tag\n", tag);
96 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS,
97 LOG_ERROR,
"%s: Must provide ctxout\n", tag);
100 if (ast_strlen_zero(profile_name)) {
101 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_DISABLED,
102 "%s: Disabled due to missing profile name\n", tag);
105 as_cfg = as_get_cfg();
106 if (as_cfg->global_disable) {
107 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_DISABLED,
108 "%s: Globally disabled\n", tag);
111 eprofile = eprofile_get_cfg(profile_name);
113 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_DISABLED,
114 LOG_ERROR,
"%s: No profile for profile name '%s'. Call will continue\n", tag,
118 if (!PROFILE_ALLOW_ATTEST(eprofile)) {
119 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_DISABLED,
120 "%s: Disabled by profile\n", tag);
123 etn = tn_get_etn(canon_orig_tn, eprofile);
125 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_DISABLED,
126 "%s: No tn for orig_tn '%s'\n", tag, canon_orig_tn);
132 ao2_cleanup(eprofile);
136 if (etn->acfg_common.attest_level == attest_level_NOT_SET) {
137 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_MISSING_PARAMETERS,
139 "'%s': No attest_level specified in tn, profile or attestation objects\n",
143 if (ast_strlen_zero(etn->acfg_common.public_cert_url)) {
144 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_NO_PUBLIC_CERT_URL_AVAIL,
145 LOG_ERROR,
"%s: No public cert url in tn %s, profile or attestation objects\n",
149 if (etn->acfg_common.raw_key_length == 0) {
150 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_NO_PRIVATE_KEY_AVAIL,
151 LOG_ERROR,
"%s: No private key in tn %s, profile or attestation objects\n",
155 ctx = ao2_alloc_options(
sizeof(*ctx), ctx_destructor,
158 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
159 LOG_ERROR,
"%s: Unable to allocate memory for ctx\n", tag);
163 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
164 LOG_ERROR,
"%s: Unable to allocate memory for ctx\n", tag);
168 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
169 LOG_ERROR,
"%s: Unable to allocate memory for ctx\n", tag);
173 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
174 LOG_ERROR,
"%s: Unable to allocate memory for ctx\n", tag);
178 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
179 LOG_ERROR,
"%s: Unable to allocate memory for ctx\n", tag);
186 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
187 LOG_ERROR,
"%s: Unable to allocate memory for ctx\n", tag);
196 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_SUCCESS,
"%s: Done\n", tag);
201 return ENUM_BOOL(ctx->etn->acfg_common.send_mky, send_mky);
204 enum ast_stir_shaken_as_response_code
205 ast_stir_shaken_as_ctx_add_fingerprint(
208 char *compacted_fp =
ast_alloca(strlen(fingerprint) + 1);
209 const char *f = fingerprint;
210 char *fp = compacted_fp;
213 SCOPE_ENTER(4,
"%s: Add fingerprint %s:%s\n", ctx ? ctx->tag :
"",
216 if (!ctx || ast_strlen_zero(alg) || ast_strlen_zero(fingerprint)) {
217 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS,
218 "%s: Missing arguments\n", ctx->tag);
221 if (!ENUM_BOOL(ctx->etn->acfg_common.send_mky, send_mky)) {
222 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_DISABLED,
223 "%s: Not needed\n", ctx->tag);
234 rc =
ast_asprintf(&combined,
"%s:%s", alg, compacted_fp);
236 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
237 "%s: Can't allocate memory for comobined string\n", ctx->tag);
242 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
243 "%s: Can't add entry to vector\n", ctx->tag);
246 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_SUCCESS,
247 "%s: Done\n", ctx->tag);
255 #define CREATE_JSON_SET_OBJ(__val, __obj, __name) \
257 struct ast_json *__var; \
258 if (!(__var = __val)) {\
259 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR, \
260 LOG_ERROR, "%s: Cannot allocate one of the JSON objects\n", \
263 if (ast_json_object_set(__obj, __name, __var)) { \
264 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR, \
265 LOG_ERROR, "%s: Cannot set one of the JSON objects\n", \
272 #define CREATE_JSON_APPEND_ARRAY(__val, __obj) \
274 struct ast_json *__var; \
275 if (!(__var = __val)) {\
276 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR, \
277 LOG_ERROR, "%s: Cannot allocate one of the JSON objects\n", \
280 if (ast_json_array_append(__obj, __var)) { \
281 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR, \
282 LOG_ERROR, "%s: Cannot set one of the JSON objects\n", \
289 static enum ast_stir_shaken_as_response_code pack_payload(
300 char origid[AST_UUID_STR_LEN];
301 char *payload_str = NULL;
302 SCOPE_ENTER(3,
"%s: Enter\n", ctx->tag);
320 attest_level_to_str(ctx->etn->acfg_common.attest_level)),
330 && ENUM_BOOL(ctx->etn->acfg_common.send_mky, send_mky)) {
339 char *fp = strchr(afp,
':');
355 ast_trace(2,
"Payload: %s\n", payload_str);
356 jwt_add_grants_json(jwt, payload_str);
359 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_SUCCESS,
"Done\n");
363 enum ast_stir_shaken_as_response_code ast_stir_shaken_attest(
366 RAII_VAR(jwt_t *, jwt, NULL, jwt_free);
368 char *encoded = NULL;
369 enum ast_stir_shaken_as_response_code as_rc;
371 SCOPE_ENTER(3,
"%s: Attestation: orig: %s dest: %s\n",
372 ctx ? ctx->tag :
"NULL", ctx ? ctx->orig_tn :
"NULL",
373 ctx ? ctx->dest_tn :
"NULL");
376 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_VS_INTERNAL_ERROR, LOG_ERROR,
377 "%s: No context object!\n",
"NULL");
380 if (header == NULL) {
381 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INVALID_ARGUMENTS,
382 LOG_ERROR,
"%s: Header buffer was NULL\n", ctx->tag);
387 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
388 LOG_ERROR,
"%s: Cannot create JWT\n", ctx->tag);
394 alg = jwt_str_alg(STIR_SHAKEN_ENCRYPTION_ALGORITHM);
395 jwt_set_alg(jwt, alg, (
const unsigned char *)ctx->etn->acfg_common.raw_key,
396 ctx->etn->acfg_common.raw_key_length);
397 jwt_add_header(jwt,
"ppt", STIR_SHAKEN_PPT);
398 jwt_add_header(jwt,
"typ", STIR_SHAKEN_TYPE);
399 jwt_add_header(jwt,
"x5u", ctx->etn->acfg_common.public_cert_url);
401 as_rc = pack_payload(ctx, jwt);
402 if (as_rc != AST_STIR_SHAKEN_AS_SUCCESS) {
403 SCOPE_EXIT_LOG_RTN_VALUE(as_rc,
404 LOG_ERROR,
"%s: Cannot pack payload\n", ctx->tag);
407 encoded = jwt_encode_str(jwt);
409 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_SIGN_ENCODE_FAILURE,
410 LOG_ERROR,
"%s: Unable to sign/encode JWT\n", ctx->tag);
414 encoded, ctx->etn->acfg_common.public_cert_url, jwt_alg_str(alg),
416 ast_std_free(encoded);
418 SCOPE_EXIT_LOG_RTN_VALUE(AST_STIR_SHAKEN_AS_INTERNAL_ERROR,
419 LOG_ERROR,
"%s: Unable to allocate memory for identity header\n",
423 SCOPE_EXIT_RTN_VALUE(AST_STIR_SHAKEN_AS_SUCCESS,
"%s: Done\n", ctx->tag);
441 if (as_config_load()) {
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
#define ARRAY_IN_BOUNDS(v, a)
Checks to see if value is within the bounds of the given array.
Main Channel structure associated with a channel.
#define AST_VECTOR_ADD_SORTED(vec, elem, cmp)
Add an element into a sorted vector.
Asterisk main include file. File version handling, generic pbx functions.
TN configuration for stir/shaken.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Universally unique identifier support.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
struct ast_json * ast_json_string_create(const char *value)
Construct a JSON string from value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_channel_cleanup(c)
Cleanup a channel reference.
char * ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format)
Encode a JSON value to a string.
struct ast_json * ast_json_array_create(void)
Create a empty JSON array.
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
#define AST_VECTOR_RESET(vec, cleanup)
Reset vector.
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Module has failed to load, may be in an inconsistent state.
struct ast_json * ast_json_object_create(void)
Create a new JSON object.
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
#define ast_channel_ref(c)
Increase channel reference count.
Profile configuration for stir/shaken.
Abstract JSON element (object, array, string, int, ...).
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
struct ast_json * ast_json_integer_create(intmax_t value)
Create a JSON integer.