Asterisk - The Open Source Telephony Project GIT-master-f36a736
res_stir_shaken.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2020, Sangoma Technologies Corporation
5 *
6 * Kevin Harwell <kharwell@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*** MODULEINFO
20 <depend>curl</depend>
21 <depend>res_curl</depend>
22 <depend>libjwt</depend>
23 <support_level>core</support_level>
24 ***/
25
26#define _TRACE_PREFIX_ "rss",__LINE__, ""
27
28#include "asterisk.h"
29
30#include "asterisk/app.h"
31#include "asterisk/cli.h"
33#include "asterisk/module.h"
35#include "asterisk/pbx.h"
36
38
40
42{
43 return tn_auth_list_nid;
44}
45
46/* The datastore struct holding verification information for the channel */
48 /* The identitifier for the STIR/SHAKEN verification */
49 char *identity;
50 /* The attestation value */
52 /* The actual verification result */
54};
55
56/*!
57 * \brief Frees a stir_shaken_datastore structure
58 *
59 * \param datastore The datastore to free
60 */
61static void stir_datastore_free(struct stir_datastore *datastore)
62{
63 if (!datastore) {
64 return;
65 }
66
67 ast_free(datastore->identity);
68 ast_free(datastore->attestation);
69 ast_free(datastore);
70}
71
72/*!
73 * \brief The callback to destroy a stir_shaken_datastore
74 *
75 * \param data The stir_shaken_datastore
76 */
77static void stir_datastore_destroy_cb(void *data)
78{
79 struct stir_datastore *datastore = data;
80 stir_datastore_free(datastore);
81}
82
83/* The stir_shaken_datastore info used to add and compare stir_shaken_datastores on the channel */
85 .type = "STIR/SHAKEN VERIFICATION",
87};
88
90 struct ast_stir_shaken_vs_ctx *ctx)
91{
93 struct ast_datastore *chan_datastore;
94 const char *chan_name;
95
96 if (!ctx->chan) {
97 ast_log(LOG_ERROR, "Channel is required to add STIR/SHAKEN verification\n");
98 return -1;
99 }
100
101 chan_name = ast_channel_name(ctx->chan);
102
103 if (!ctx->identity_hdr) {
104 ast_log(LOG_ERROR, "No identity to add STIR/SHAKEN verification to channel "
105 "%s\n", chan_name);
106 return -1;
107 }
108
109 if (!ctx->attestation) {
110 ast_log(LOG_ERROR, "Attestation cannot be NULL to add STIR/SHAKEN verification to "
111 "channel %s\n", chan_name);
112 return -1;
113 }
114
116 if (!stir_datastore) {
117 ast_log(LOG_ERROR, "Failed to allocate space for STIR/SHAKEN datastore for "
118 "channel %s\n", chan_name);
119 return -1;
120 }
121
123 if (!stir_datastore->identity) {
124 ast_log(LOG_ERROR, "Failed to allocate space for STIR/SHAKEN datastore "
125 "identity for channel %s\n", chan_name);
127 return -1;
128 }
129
132 ast_log(LOG_ERROR, "Failed to allocate space for STIR/SHAKEN datastore "
133 "attestation for channel %s\n", chan_name);
135 return -1;
136 }
137
139
141 if (!chan_datastore) {
142 ast_log(LOG_ERROR, "Failed to allocate space for datastore for channel "
143 "%s\n", chan_name);
145 return -1;
146 }
147
148 chan_datastore->data = stir_datastore;
149
151 ast_channel_datastore_add(ctx->chan, chan_datastore);
153
154 return 0;
155}
156
157/*!
158 * \brief Retrieves STIR/SHAKEN verification information for the channel via dialplan.
159 * Examples:
160 *
161 * STIR_SHAKEN(count)
162 * STIR_SHAKEN(0, identity)
163 * STIR_SHAKEN(1, attestation)
164 * STIR_SHAKEN(27, verify_result)
165 *
166 * \retval -1 on failure
167 * \retval 0 on success
168 */
169static int func_read(struct ast_channel *chan, const char *function,
170 char *data, char *buf, size_t len)
171{
173 struct ast_datastore *chan_datastore;
174 char *parse;
175 char *first;
176 char *second;
177 unsigned int target_index, current_index = 0;
179 AST_APP_ARG(first_param);
180 AST_APP_ARG(second_param);
181 );
182
183 if (ast_strlen_zero(data)) {
184 ast_log(LOG_WARNING, "%s requires at least one argument\n", function);
185 return -1;
186 }
187
188 if (!chan) {
189 ast_log(LOG_ERROR, "No channel for %s function\n", function);
190 return -1;
191 }
192
193 parse = ast_strdupa(data);
194
196
197 first = ast_strip(args.first_param);
198 if (ast_strlen_zero(first)) {
199 ast_log(LOG_ERROR, "An argument must be passed to %s\n", function);
200 return -1;
201 }
202
203 second = ast_strip(args.second_param);
204
205 /* Check if we are only looking for the number of STIR/SHAKEN verification results */
206 if (!strcasecmp(first, "count")) {
207 size_t count = 0;
208
209 if (!ast_strlen_zero(second)) {
210 ast_log(LOG_ERROR, "%s only takes 1 paramater for 'count'\n", function);
211 return -1;
212 }
213
214 ast_channel_lock(chan);
215 AST_LIST_TRAVERSE(ast_channel_datastores(chan), chan_datastore, entry) {
216 if (chan_datastore->info != &stir_shaken_datastore_info) {
217 continue;
218 }
219 count++;
220 }
221 ast_channel_unlock(chan);
222
223 snprintf(buf, len, "%zu", count);
224 return 0;
225 }
226
227 /* If we aren't doing a count, then there should be two parameters. The field
228 * we are searching for will be the second parameter. The index is the first.
229 */
230 if (ast_strlen_zero(second)) {
231 ast_log(LOG_ERROR, "Retrieving a value using %s requires two paramaters (index, value) "
232 "- only index was given\n", function);
233 return -1;
234 }
235
236 if (ast_str_to_uint(first, &target_index)) {
237 ast_log(LOG_ERROR, "Failed to convert index %s to integer for function %s\n",
238 first, function);
239 return -1;
240 }
241
242 /* We don't store by uid for the datastore, so just search for the specified index */
243 ast_channel_lock(chan);
244 AST_LIST_TRAVERSE(ast_channel_datastores(chan), chan_datastore, entry) {
245 if (chan_datastore->info != &stir_shaken_datastore_info) {
246 continue;
247 }
248
249 if (current_index == target_index) {
250 break;
251 }
252
253 current_index++;
254 }
255 ast_channel_unlock(chan);
256 if (current_index != target_index || !chan_datastore) {
257 ast_log(LOG_WARNING, "No STIR/SHAKEN results for index '%s'\n", first);
258 return -1;
259 }
260 stir_datastore = chan_datastore->data;
261
262 if (!strcasecmp(second, "identity")) {
264 } else if (!strcasecmp(second, "attestation")) {
266 } else if (!strcasecmp(second, "verify_result")) {
268 } else {
269 ast_log(LOG_ERROR, "No such value '%s' for %s\n", second, function);
270 return -1;
271 }
272
273 return 0;
274}
275
277 .name = "STIR_SHAKEN",
278 .read = func_read,
279};
280
281static int reload_module(void)
282{
283 return common_config_reload();
284}
285
286static int unload_module(void)
287{
288 int res = 0;
289
292
294
295 return 0;
296}
297
298#define TN_AUTH_LIST_OID "1.3.6.1.5.5.7.1.26"
299#define TN_AUTH_LIST_SHORT "TNAuthList"
300#define TN_AUTH_LIST_LONG "TNAuthorizationList"
301
302static int check_for_old_config(void)
303{
304 const char *error_msg = "There appears to be a 'stir_shaken.conf' file"
305 " with old configuration options in it. Please see the new config"
306 " file format in the configs/samples/stir_shaken.conf.sample file"
307 " in the source tree at https://github.com/asterisk/asterisk/raw/master/configs/samples/stir_shaken.conf.sample"
308 " or visit https://docs.asterisk.org/Deployment/STIR-SHAKEN for more information.";
310 struct ast_flags config_flags = { 0 };
311 char *cat = NULL;
312
313 cfg = ast_config_load("stir_shaken.conf", config_flags);
314 if (cfg == CONFIG_STATUS_FILEMISSING) {
315 /*
316 * They may be loading from realtime so the fact that there's
317 * no stir-shaken.conf file isn't an issue for this purpose.
318 */
320 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
321 cfg = NULL;
322 ast_log(LOG_ERROR, "The stir_shaken.conf file is invalid\n");
324 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
325 /* This can never happen but is included for completeness */
326 cfg = NULL;
328 }
329
330 while ((cat = ast_category_browse(cfg, cat))) {
331 const char *val;
332 if (strcasecmp(cat, "general") == 0) {
333 ast_log(LOG_ERROR, "%s\n", error_msg);
335 }
336 val = ast_variable_retrieve(cfg, cat, "type");
337 if (val && (strcasecmp(val, "store") == 0 ||
338 strcasecmp(val, "certificate") == 0)) {
339 ast_log(LOG_ERROR, "%s\n", error_msg);
341 }
342 }
343
345}
346
347static int load_module(void)
348{
349 int res = 0;
350
351 res = check_for_old_config();
352 if (res != AST_MODULE_LOAD_SUCCESS) {
353 return res;
354 }
355
356 res = crypto_load();
357 if (res != AST_MODULE_LOAD_SUCCESS) {
358 return res;
359 }
360
363 if (tn_auth_list_nid < 0) {
366 }
367
368 res = common_config_load();
369 if (res != AST_MODULE_LOAD_SUCCESS) {
371 return res;
372 }
373
375 if (res != 0) {
378 }
379
381}
382
384 .support_level = AST_MODULE_SUPPORT_CORE,
385 .load = load_module,
386 .unload = unload_module,
388 .load_pri = AST_MODPRI_CHANNEL_DEPEND - 1,
389 .requires = "res_curl",
struct sla_ringing_trunk * first
Definition: app_sla.c:332
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2404
#define ast_channel_lock(chan)
Definition: channel.h:2968
struct ast_datastore_list * ast_channel_datastores(struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2969
Standard Command Line Interface.
int common_config_unload(void)
int common_config_reload(void)
int common_config_load(void)
Conversion utility functions.
int ast_str_to_uint(const char *str, unsigned int *res)
Convert the given string to an unsigned integer.
Definition: conversions.c:56
int crypto_register_x509_extension(const char *oid, const char *short_name, const char *long_name)
Register a certificate extension to openssl.
Definition: crypto_utils.c:80
int crypto_unload(void)
Clean up the crypto utils.
Definition: crypto_utils.c:853
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
globally accessible channel datastores
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define CONFIG_STATUS_FILEMISSING
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:784
#define LOG_ERROR
#define LOG_WARNING
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_CHANNEL_DEPEND
Definition: module.h:340
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
static int reload(void)
static void crypto_load(int ifd, int ofd)
refresh RSA keys from file
Definition: res_crypto.c:819
#define TN_AUTH_LIST_SHORT
int ast_stir_shaken_add_result_to_channel(struct ast_stir_shaken_vs_ctx *ctx)
Add a STIR/SHAKEN verification result to a channel.
static const struct ast_datastore_info stir_shaken_datastore_info
static void stir_datastore_destroy_cb(void *data)
The callback to destroy a stir_shaken_datastore.
#define TN_AUTH_LIST_OID
#define TN_AUTH_LIST_LONG
static void stir_datastore_free(struct stir_datastore *datastore)
Frees a stir_shaken_datastore structure.
static struct ast_custom_function stir_shaken_function
static int func_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:
static int reload_module(void)
static int check_for_old_config(void)
static int tn_auth_list_nid
static int load_module(void)
static int unload_module(void)
int get_tn_auth_nid(void)
Retrieves the OpenSSL NID for the TN Auth list extension.
ast_stir_shaken_vs_response_code
#define NULL
Definition: resample.c:96
const char * vs_response_code_to_str(enum ast_stir_shaken_vs_response_code vs_rc)
Return string version of VS response code.
Definition: verification.c:89
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
const struct ast_datastore_info * info
Definition: datastore.h:67
void * data
Definition: datastore.h:66
Structure used to handle boolean flags.
Definition: utils.h:199
const ast_string_field attestation
Definition: verification.h:39
const ast_string_field identity_hdr
Definition: verification.h:39
struct ast_channel * chan
Definition: verification.h:41
enum ast_stir_shaken_vs_response_code failure_reason
Definition: verification.h:48
Definition: search.h:40
enum ast_stir_shaken_vs_response_code verify_result
Definition: ast_expr2.c:325
const char * args
#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:941