mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 11:25:35 +00:00
AST-2022-002 - res_stir_shaken/curl: Add ACL checks for Identity header.
Adds a new configuration option, stir_shaken_profile, in pjsip.conf that can be specified on a per endpoint basis. This option will reference a stir_shaken_profile that can be configured in stir_shaken.conf. The type of this option must be 'profile'. The stir_shaken option can be specified on this object with the same values as before (attest, verify, on), but it cannot be off since having the profile itself implies wanting STIR/SHAKEN support. You can also specify an ACL from acl.conf (along with permit and deny lines in the object itself) that will be used to limit what interfaces Asterisk will attempt to retrieve information from when reading the Identity header. ASTERISK-29476 Change-Id: I87fa61f78a9ea0cd42530691a30da3c781842406
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
#include "asterisk/global_datastores.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
#include "asterisk/res_stir_shaken.h"
|
||||
#include "res_stir_shaken/stir_shaken.h"
|
||||
@@ -45,6 +46,7 @@
|
||||
#include "res_stir_shaken/store.h"
|
||||
#include "res_stir_shaken/certificate.h"
|
||||
#include "res_stir_shaken/curl.h"
|
||||
#include "res_stir_shaken/profile.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<configInfo name="res_stir_shaken" language="en_US">
|
||||
@@ -108,6 +110,29 @@
|
||||
<synopsis>The caller ID number to match on.</synopsis>
|
||||
</configOption>
|
||||
</configObject>
|
||||
<configObject name="profile">
|
||||
<synopsis>STIR/SHAKEN profile configuration options</synopsis>
|
||||
<configOption name="type">
|
||||
<synopsis>Must be of type 'profile'.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="stir_shaken" default="on">
|
||||
<synopsis>STIR/SHAKEN configuration settings</synopsis>
|
||||
<description><para>
|
||||
Attest, verify, or do both STIR/SHAKEN operations. On incoming
|
||||
INVITEs, the Identity header will be checked for validity. On
|
||||
outgoing INVITEs, an Identity header will be added.</para>
|
||||
</description>
|
||||
</configOption>
|
||||
<configOption name="acllist" default="">
|
||||
<synopsis>An existing ACL from acl.conf to use</synopsis>
|
||||
</configOption>
|
||||
<configOption name="permit" default="">
|
||||
<synopsis>An IP or subnet to permit</synopsis>
|
||||
</configOption>
|
||||
<configOption name="deny" default="">
|
||||
<synopsis>An IP or subnet to deny</synopsis>
|
||||
</configOption>
|
||||
</configObject>
|
||||
</configFile>
|
||||
</configInfo>
|
||||
<function name="STIR_SHAKEN" language="en_US">
|
||||
@@ -205,6 +230,33 @@ unsigned int ast_stir_shaken_get_signature_timeout(void)
|
||||
return ast_stir_shaken_signature_timeout(stir_shaken_general_get());
|
||||
}
|
||||
|
||||
struct stir_shaken_profile *ast_stir_shaken_get_profile(const char *id)
|
||||
{
|
||||
if (ast_strlen_zero(id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ast_stir_shaken_get_profile_by_name(id);
|
||||
}
|
||||
|
||||
unsigned int ast_stir_shaken_profile_supports_attestation(const struct stir_shaken_profile *profile)
|
||||
{
|
||||
if (!profile) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (profile->stir_shaken & STIR_SHAKEN_ATTEST);
|
||||
}
|
||||
|
||||
unsigned int ast_stir_shaken_profile_supports_verification(const struct stir_shaken_profile *profile)
|
||||
{
|
||||
if (!profile) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (profile->stir_shaken & STIR_SHAKEN_VERIFY);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Convert an ast_stir_shaken_verification_result to string representation
|
||||
*
|
||||
@@ -556,7 +608,7 @@ static int stir_shaken_verify_signature(const char *msg, const char *signature,
|
||||
* \retval NULL on failure
|
||||
* \retval full path filename on success
|
||||
*/
|
||||
static char *run_curl(const char *public_cert_url, const char *path)
|
||||
static char *run_curl(const char *public_cert_url, const char *path, const struct ast_acl_list *acl)
|
||||
{
|
||||
struct curl_cb_data *data;
|
||||
char *filename;
|
||||
@@ -567,7 +619,7 @@ static char *run_curl(const char *public_cert_url, const char *path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filename = curl_public_key(public_cert_url, path, data);
|
||||
filename = curl_public_key(public_cert_url, path, data, acl);
|
||||
if (!filename) {
|
||||
ast_log(LOG_ERROR, "Could not retrieve public key for '%s'\n", public_cert_url);
|
||||
curl_cb_data_free(data);
|
||||
@@ -593,7 +645,7 @@ static char *run_curl(const char *public_cert_url, const char *path)
|
||||
* \retval NULL on failure
|
||||
* \retval full path filename on success
|
||||
*/
|
||||
static char *curl_and_check_expiration(const char *public_cert_url, const char *path, int *curl)
|
||||
static char *curl_and_check_expiration(const char *public_cert_url, const char *path, int *curl, const struct ast_acl_list *acl)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
@@ -602,7 +654,7 @@ static char *curl_and_check_expiration(const char *public_cert_url, const char *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filename = run_curl(public_cert_url, path);
|
||||
filename = run_curl(public_cert_url, path, acl);
|
||||
if (!filename) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -664,7 +716,8 @@ static int stir_shaken_verify_check_empty_strings(const char *header, const char
|
||||
* \retval 0 on success
|
||||
* \retval 1 on failure
|
||||
*/
|
||||
static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char **file_path, char **dir_path, int *curl)
|
||||
static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char **file_path, char **dir_path, int *curl,
|
||||
const struct ast_acl_list *acl)
|
||||
{
|
||||
*file_path = get_path_to_public_key(public_cert_url);
|
||||
if (ast_asprintf(dir_path, "%s/keys/%s", ast_config_AST_DATA_DIR, STIR_SHAKEN_DIR_NAME) < 0) {
|
||||
@@ -682,7 +735,7 @@ static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char
|
||||
ast_free(*file_path);
|
||||
|
||||
/* Download to the default path */
|
||||
*file_path = run_curl(public_cert_url, *dir_path);
|
||||
*file_path = run_curl(public_cert_url, *dir_path, acl);
|
||||
if (!(*file_path)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -706,7 +759,7 @@ static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char
|
||||
* \retval 1 on failure
|
||||
*/
|
||||
static int stir_shaken_verify_validate_cert(const char *public_cert_url, char **file_path, char *dir_path, int *curl,
|
||||
EVP_PKEY **public_key)
|
||||
EVP_PKEY **public_key, const struct ast_acl_list *acl)
|
||||
{
|
||||
if (public_key_is_expired(public_cert_url)) {
|
||||
|
||||
@@ -716,7 +769,7 @@ static int stir_shaken_verify_validate_cert(const char *public_cert_url, char **
|
||||
|
||||
/* If this fails, then there's nothing we can do */
|
||||
ast_free(*file_path);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl, acl);
|
||||
if (!(*file_path)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -732,7 +785,7 @@ static int stir_shaken_verify_validate_cert(const char *public_cert_url, char **
|
||||
remove_public_key_from_astdb(public_cert_url);
|
||||
|
||||
ast_free(*file_path);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl, acl);
|
||||
if (!(*file_path)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -758,6 +811,12 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify(const char *header, const
|
||||
|
||||
struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, const char *payload, const char *signature,
|
||||
const char *algorithm, const char *public_cert_url, int *failure_code)
|
||||
{
|
||||
return ast_stir_shaken_verify_with_profile(header, payload, signature, algorithm, public_cert_url, failure_code, NULL);
|
||||
}
|
||||
|
||||
struct ast_stir_shaken_payload *ast_stir_shaken_verify_with_profile(const char *header, const char *payload, const char *signature,
|
||||
const char *algorithm, const char *public_cert_url, int *failure_code, const struct stir_shaken_profile *profile)
|
||||
{
|
||||
struct ast_stir_shaken_payload *ret_payload;
|
||||
EVP_PKEY *public_key;
|
||||
@@ -766,11 +825,14 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, cons
|
||||
RAII_VAR(char *, dir_path, NULL, ast_free);
|
||||
RAII_VAR(char *, combined_str, NULL, ast_free);
|
||||
size_t combined_size;
|
||||
const struct ast_acl_list *acl;
|
||||
|
||||
if (stir_shaken_verify_check_empty_strings(header, payload, signature, algorithm, public_cert_url)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
acl = profile ? (const struct ast_acl_list *)profile->acl : NULL;
|
||||
|
||||
/* Check to see if we have already downloaded this public cert. The reason we
|
||||
* store the file path is because:
|
||||
*
|
||||
@@ -781,12 +843,12 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, cons
|
||||
* {configurable) directories, we already have the storage mechanism in place.
|
||||
* The only thing that would be left to do is pull from the configuration.
|
||||
*/
|
||||
if (stir_shaken_verify_setup_file_paths(public_cert_url, &file_path, &dir_path, &curl)) {
|
||||
if (stir_shaken_verify_setup_file_paths(public_cert_url, &file_path, &dir_path, &curl, acl)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check to see if the cert we downloaded (or already had) is expired */
|
||||
if (stir_shaken_verify_validate_cert(public_cert_url, &file_path, dir_path, &curl, &public_key)) {
|
||||
if (stir_shaken_verify_validate_cert(public_cert_url, &file_path, dir_path, &curl, &public_key, acl)) {
|
||||
*failure_code = AST_STIR_SHAKEN_VERIFY_FAILED_TO_GET_CERT;
|
||||
return NULL;
|
||||
}
|
||||
@@ -1679,6 +1741,7 @@ static int unload_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
stir_shaken_profile_unload();
|
||||
stir_shaken_certificate_unload();
|
||||
stir_shaken_store_unload();
|
||||
stir_shaken_general_unload();
|
||||
@@ -1718,6 +1781,11 @@ static int load_module(void)
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (stir_shaken_profile_load()) {
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_sorcery_load(ast_stir_shaken_sorcery());
|
||||
|
||||
res |= ast_custom_function_register(&stir_shaken_function);
|
||||
|
Reference in New Issue
Block a user