]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
ARI: Add support for Cross-Origin Resource Sharing (CORS), origin headers
authorJason Parker <jparker@digium.com>
Fri, 12 Jul 2013 17:52:52 +0000 (17:52 +0000)
committerJason Parker <jparker@digium.com>
Fri, 12 Jul 2013 17:52:52 +0000 (17:52 +0000)
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

res/res_stasis_http.c
res/stasis_http/cli.c
res/stasis_http/config.c
res/stasis_http/internal.h

index 7cd92f48f1cd16a58d9813e2d57d2757e7eda244..e9dab79f58da91491e9a0412d59c25135c1867fc 100644 (file)
@@ -293,6 +293,26 @@ static void add_allow_header(struct stasis_rest_handlers *handler,
        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_HEADERS "Access-Control-Request-Headers"
 #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
-        * set of steps.
+        * set of steps."
         */
        if (origin == NULL) {
                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
         * 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.
         *
-        * "The Origin header can only contain a single origin as the user agent
-        * will not follow redirects.
-        *
-        * TODO - pull list of allowed origins from config
+        * The Origin header can only contain a single origin as the user agent
+        * will not follow redirects."
         */
+       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
         * 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
         * 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."
         */
 
@@ -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
         * 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."
         */
        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
         * 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."
-        *
-        * 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
         * Access-Control-Allow-Origin header, with the value of the Origin
         * header as value, and add a single Access-Control-Allow-Credentials
         * 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."
-        *
-        * TODO - when we add authentication, this will change to
-        * Access-Control-Allow-Credentials.
         */
        ast_str_append(&response->headers, 0,
                       "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
         * or more Access-Control-Expose-Headers headers, with as values the
index 98d082b2c62394f55f7dbf3e16dcdb8477a88659..ac974dc148e8e7a0a79b65b7268b3363da9794a5 100644 (file)
@@ -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, "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));
        return CLI_SUCCESS;
 }
index 2181907d57db4e4e4ca8391fd44ca2369b0fec74..7ccfe8e591eba81b26dd8a6e47a66b7a661e84e7 100644 (file)
@@ -158,6 +158,9 @@ static struct aco_type *user[] = ACO_TYPES(&user_option);
 static void conf_destructor(void *obj)
 {
        struct ari_conf *cfg = obj;
+
+       ast_string_field_free_memory(cfg->general);
+
        ao2_cleanup(cfg->general);
        ao2_cleanup(cfg->users);
 }
@@ -180,6 +183,10 @@ static void *conf_alloc(void)
        }
        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,
                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,
                FLDSET(struct ari_conf_general, auth_realm),
                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,
                OPT_NOOP_T, 0, 0);
index 659f4a2aeed2744570c9724d1a1aa89294eef681..7cc67cfbf76ac72ade67b250fc79db507f15031b 100644 (file)
@@ -67,6 +67,10 @@ struct ari_conf_general {
        enum ast_json_encoding_format format;
        /*! Authentication realm */
        char auth_realm[ARI_AUTH_REALM_LEN];
+
+       AST_DECLARE_STRING_FIELDS(
+               AST_STRING_FIELD(allowed_origins);
+       );
 };
 
 /*! \brief Password format */