mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 10:33:13 +00:00
ARI: Add support for Cross-Origin Resource Sharing (CORS), origin headers
This rejects requests from any unknown origins. (closes issue ASTERISK-21278) Review: https://reviewboard.asterisk.org/r/2667/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394189 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -293,6 +293,26 @@ static void add_allow_header(struct stasis_rest_handlers *handler,
|
|||||||
ast_str_append(&response->headers, 0, "\r\n");
|
ast_str_append(&response->headers, 0, "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int origin_allowed(const char *origin)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ari_conf *, cfg, ari_config_get(), ao2_cleanup);
|
||||||
|
|
||||||
|
char *allowed = ast_strdupa(cfg->general->allowed_origins);
|
||||||
|
char *current;
|
||||||
|
|
||||||
|
while ((current = strsep(&allowed, ","))) {
|
||||||
|
if (!strcmp(current, "*")) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(current, origin)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define ACR_METHOD "Access-Control-Request-Method"
|
#define ACR_METHOD "Access-Control-Request-Method"
|
||||||
#define ACR_HEADERS "Access-Control-Request-Headers"
|
#define ACR_HEADERS "Access-Control-Request-Headers"
|
||||||
#define ACA_METHODS "Access-Control-Allow-Methods"
|
#define ACA_METHODS "Access-Control-Allow-Methods"
|
||||||
@@ -333,7 +353,7 @@ static void handle_options(struct stasis_rest_handlers *handler,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* CORS 6.2, #1 - "If the Origin header is not present terminate this
|
/* CORS 6.2, #1 - "If the Origin header is not present terminate this
|
||||||
* set of steps.
|
* set of steps."
|
||||||
*/
|
*/
|
||||||
if (origin == NULL) {
|
if (origin == NULL) {
|
||||||
return;
|
return;
|
||||||
@@ -343,14 +363,16 @@ static void handle_options(struct stasis_rest_handlers *handler,
|
|||||||
* case-sensitive match for any of the values in list of origins do not
|
* case-sensitive match for any of the values in list of origins do not
|
||||||
* set any additional headers and terminate this set of steps.
|
* set any additional headers and terminate this set of steps.
|
||||||
*
|
*
|
||||||
* "Always matching is acceptable since the list of origins can be
|
* Always matching is acceptable since the list of origins can be
|
||||||
* unbounded.
|
* unbounded.
|
||||||
*
|
*
|
||||||
* "The Origin header can only contain a single origin as the user agent
|
* The Origin header can only contain a single origin as the user agent
|
||||||
* will not follow redirects.
|
* will not follow redirects."
|
||||||
*
|
|
||||||
* TODO - pull list of allowed origins from config
|
|
||||||
*/
|
*/
|
||||||
|
if (!origin_allowed(origin)) {
|
||||||
|
ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
|
/* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
|
||||||
* or if parsing failed, do not set any additional headers and terminate
|
* or if parsing failed, do not set any additional headers and terminate
|
||||||
@@ -397,7 +419,7 @@ static void handle_options(struct stasis_rest_handlers *handler,
|
|||||||
* case-insensitive match for any of the values in list of headers do
|
* case-insensitive match for any of the values in list of headers do
|
||||||
* not set any additional headers and terminate this set of steps.
|
* not set any additional headers and terminate this set of steps.
|
||||||
*
|
*
|
||||||
* "Note: Always matching is acceptable since the list of headers can be
|
* Note: Always matching is acceptable since the list of headers can be
|
||||||
* unbounded."
|
* unbounded."
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -423,7 +445,7 @@ static void handle_options(struct stasis_rest_handlers *handler,
|
|||||||
/* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
|
/* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
|
||||||
* consisting of (a subset of) the list of headers.
|
* consisting of (a subset of) the list of headers.
|
||||||
*
|
*
|
||||||
* "Since the list of headers can be unbounded simply returning headers
|
* Since the list of headers can be unbounded simply returning headers
|
||||||
* can be enough."
|
* can be enough."
|
||||||
*/
|
*/
|
||||||
if (!ast_strlen_zero(acr_headers)) {
|
if (!ast_strlen_zero(acr_headers)) {
|
||||||
@@ -700,25 +722,26 @@ static void process_cors_request(struct ast_variable *headers,
|
|||||||
* case-sensitive match for any of the values in list of origins, do not
|
* case-sensitive match for any of the values in list of origins, do not
|
||||||
* set any additional headers and terminate this set of steps.
|
* set any additional headers and terminate this set of steps.
|
||||||
*
|
*
|
||||||
* "Note: Always matching is acceptable since the list of origins can be
|
* Note: Always matching is acceptable since the list of origins can be
|
||||||
* unbounded."
|
* unbounded."
|
||||||
*
|
|
||||||
* TODO - pull list of allowed origins from config
|
|
||||||
*/
|
*/
|
||||||
|
if (!origin_allowed(origin)) {
|
||||||
|
ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* CORS 6.1, #3 - "If the resource supports credentials add a single
|
/* CORS 6.1, #3 - "If the resource supports credentials add a single
|
||||||
* Access-Control-Allow-Origin header, with the value of the Origin
|
* Access-Control-Allow-Origin header, with the value of the Origin
|
||||||
* header as value, and add a single Access-Control-Allow-Credentials
|
* header as value, and add a single Access-Control-Allow-Credentials
|
||||||
* header with the case-sensitive string "true" as value.
|
* header with the case-sensitive string "true" as value.
|
||||||
*
|
*
|
||||||
* "Otherwise, add a single Access-Control-Allow-Origin header, with
|
* Otherwise, add a single Access-Control-Allow-Origin header, with
|
||||||
* either the value of the Origin header or the string "*" as value."
|
* either the value of the Origin header or the string "*" as value."
|
||||||
*
|
|
||||||
* TODO - when we add authentication, this will change to
|
|
||||||
* Access-Control-Allow-Credentials.
|
|
||||||
*/
|
*/
|
||||||
ast_str_append(&response->headers, 0,
|
ast_str_append(&response->headers, 0,
|
||||||
"Access-Control-Allow-Origin: %s\r\n", origin);
|
"Access-Control-Allow-Origin: %s\r\n", origin);
|
||||||
|
ast_str_append(&response->headers, 0,
|
||||||
|
"Access-Control-Allow-Credentials: true\r\n");
|
||||||
|
|
||||||
/* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
|
/* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
|
||||||
* or more Access-Control-Expose-Headers headers, with as values the
|
* or more Access-Control-Expose-Headers headers, with as values the
|
||||||
|
@@ -71,6 +71,7 @@ static char *ari_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
|||||||
}
|
}
|
||||||
ast_cli(a->fd, "\n");
|
ast_cli(a->fd, "\n");
|
||||||
ast_cli(a->fd, "Auth realm: %s\n", conf->general->auth_realm);
|
ast_cli(a->fd, "Auth realm: %s\n", conf->general->auth_realm);
|
||||||
|
ast_cli(a->fd, "Allowed Origins: %s\n", conf->general->allowed_origins);
|
||||||
ast_cli(a->fd, "User count: %d\n", ao2_container_count(conf->users));
|
ast_cli(a->fd, "User count: %d\n", ao2_container_count(conf->users));
|
||||||
return CLI_SUCCESS;
|
return CLI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@@ -158,6 +158,9 @@ static struct aco_type *user[] = ACO_TYPES(&user_option);
|
|||||||
static void conf_destructor(void *obj)
|
static void conf_destructor(void *obj)
|
||||||
{
|
{
|
||||||
struct ari_conf *cfg = obj;
|
struct ari_conf *cfg = obj;
|
||||||
|
|
||||||
|
ast_string_field_free_memory(cfg->general);
|
||||||
|
|
||||||
ao2_cleanup(cfg->general);
|
ao2_cleanup(cfg->general);
|
||||||
ao2_cleanup(cfg->users);
|
ao2_cleanup(cfg->users);
|
||||||
}
|
}
|
||||||
@@ -180,6 +183,10 @@ static void *conf_alloc(void)
|
|||||||
}
|
}
|
||||||
aco_set_defaults(&general_option, "general", cfg->general);
|
aco_set_defaults(&general_option, "general", cfg->general);
|
||||||
|
|
||||||
|
if (ast_string_field_init(cfg->general, 64)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
|
cfg->users = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK,
|
||||||
AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
|
AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, user_sort_cmp, NULL);
|
||||||
|
|
||||||
@@ -308,6 +315,9 @@ int ari_config_init(void)
|
|||||||
"Asterisk REST Interface", OPT_CHAR_ARRAY_T, 0,
|
"Asterisk REST Interface", OPT_CHAR_ARRAY_T, 0,
|
||||||
FLDSET(struct ari_conf_general, auth_realm),
|
FLDSET(struct ari_conf_general, auth_realm),
|
||||||
ARI_AUTH_REALM_LEN);
|
ARI_AUTH_REALM_LEN);
|
||||||
|
aco_option_register(&cfg_info, "allowed_origins", ACO_EXACT, general_options,
|
||||||
|
"", OPT_STRINGFIELD_T, 0,
|
||||||
|
STRFLDSET(struct ari_conf_general, allowed_origins));
|
||||||
|
|
||||||
aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL,
|
aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL,
|
||||||
OPT_NOOP_T, 0, 0);
|
OPT_NOOP_T, 0, 0);
|
||||||
|
@@ -67,6 +67,10 @@ struct ari_conf_general {
|
|||||||
enum ast_json_encoding_format format;
|
enum ast_json_encoding_format format;
|
||||||
/*! Authentication realm */
|
/*! Authentication realm */
|
||||||
char auth_realm[ARI_AUTH_REALM_LEN];
|
char auth_realm[ARI_AUTH_REALM_LEN];
|
||||||
|
|
||||||
|
AST_DECLARE_STRING_FIELDS(
|
||||||
|
AST_STRING_FIELD(allowed_origins);
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! \brief Password format */
|
/*! \brief Password format */
|
||||||
|
Reference in New Issue
Block a user