ARI: Fix crash if integer values used in JSON payload 'variables' object.
authorRichard Mudgett <rmudgett@digium.com>
Fri, 27 Feb 2015 18:31:31 +0000 (18:31 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 27 Feb 2015 18:31:31 +0000 (18:31 +0000)
Sending the following ARI commands caused Asterisk to crash if the JSON
body 'variables' object passes values of types other than strings.

POST /ari/channels
POST /ari/channels/{channelid}
PUT /ari/endpoints/sendMessage
PUT /ari/endpoints/{tech}/{resource}/sendMessage

* Eliminated RAII_VAR usage in ast_ari_channels_originate_with_id(),
ast_ari_channels_originate(), ast_ari_endpoints_send_message(), and
ast_ari_endpoints_send_message_to_endpoint().

ASTERISK-24751 #close
Reported by:  jeffrey putnam

Review: https://reviewboard.asterisk.org/r/4447/
........

Merged revisions 432404 from http://svn.asterisk.org/svn/asterisk/branches/13

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@432405 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/json.h
main/json.c
res/ari/resource_channels.c
res/ari/resource_endpoints.c
res/res_ari_endpoints.c
rest-api/api-docs/endpoints.json

index 8cb74a4..28ebfbd 100644 (file)
@@ -1010,13 +1010,24 @@ struct ast_party_id;
  */
 struct ast_json *ast_json_party_id(struct ast_party_id *party);
 
+enum ast_json_to_ast_vars_code {
+       /*! \brief Conversion successful */
+       AST_JSON_TO_AST_VARS_CODE_SUCCESS,
+       /*!
+        * \brief Conversion failed because invalid value type supplied.
+        * \note Only string values allowed.
+        */
+       AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE,
+       /*! \brief Conversion failed because of allocation failure. (Out Of Memory) */
+       AST_JSON_TO_AST_VARS_CODE_OOM,
+};
+
 /*!
  * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
  * \since 12.5.0
  *
  * \param json_variables The JSON blob containing the variable
  * \param variables An out reference to the variables to populate.
- *        The pointer to the variables should be NULL when calling this.
  *
  * \code
  * struct ast_json *json_variables = ast_json_pack("[ { s: s } ]", "foo", "bar");
@@ -1026,10 +1037,9 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party);
  * res = ast_json_to_ast_variables(json_variables, &variables);
  * \endcode
  *
- * \retval 0 success
- * \retval -1 error
+ * \return Conversion enum ast_json_to_ast_vars_code status
  */
-int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
+enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables);
 
 /*!@}*/
 
index 88e8077..35e6f16 100644 (file)
@@ -882,32 +882,47 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party)
        return ast_json_ref(json_party_id);
 }
 
-int ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
+enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
 {
        struct ast_json_iter *it_json_var;
 
        *variables = NULL;
 
        for (it_json_var = ast_json_object_iter(json_variables); it_json_var;
-                it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
+               it_json_var = ast_json_object_iter_next(json_variables, it_json_var)) {
                struct ast_variable *new_var;
                const char *key = ast_json_object_iter_key(it_json_var);
+               const char *value;
+               struct ast_json *json_value;
 
                if (ast_strlen_zero(key)) {
                        continue;
                }
 
-               new_var = ast_variable_new(key,
-                                          ast_json_string_get(ast_json_object_iter_value(it_json_var)),
-                                          "");
+               json_value = ast_json_object_iter_value(it_json_var);
+               if (ast_json_typeof(json_value) != AST_JSON_STRING) {
+                       /* Error: Only strings allowed */
+                       ast_variables_destroy(*variables);
+                       *variables = NULL;
+                       return AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE;
+               }
+               value = ast_json_string_get(json_value);
+               /* Should never be NULL.  Otherwise, how could it be a string type? */
+               ast_assert(value != NULL);
+               if (!value) {
+                       /* To be safe. */
+                       continue;
+               }
+               new_var = ast_variable_new(key, value, "");
                if (!new_var) {
+                       /* Error: OOM */
                        ast_variables_destroy(*variables);
                        *variables = NULL;
-                       return -1;
+                       return AST_JSON_TO_AST_VARS_CODE_OOM;
                }
 
                ast_variable_list_append(variables, new_var);
        }
 
-       return 0;
+       return AST_JSON_TO_AST_VARS_CODE_SUCCESS;
 }
index 6938351..fb1aa03 100644 (file)
@@ -1147,11 +1147,44 @@ static void ari_channels_handle_originate_with_id(const char *args_endpoint,
        return;
 }
 
+/*!
+ * \internal
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 13.3.0
+ *
+ * \param[out] response HTTP response if error
+ * \param json_variables The JSON blob containing the variable
+ * \param[out] variables An out reference to the variables to populate.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+{
+       enum ast_json_to_ast_vars_code res;
+
+       res = ast_json_to_ast_variables(json_variables, variables);
+       switch (res) {
+       case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
+               return 0;
+       case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
+               ast_ari_response_error(response, 400, "Bad Request",
+                       "Only string values in the 'variables' object allowed");
+               break;
+       case AST_JSON_TO_AST_VARS_CODE_OOM:
+               ast_ari_response_alloc_failed(response);
+               break;
+       }
+       ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
+
+       return -1;
+}
+
 void ast_ari_channels_originate_with_id(struct ast_variable *headers,
        struct ast_ari_channels_originate_with_id_args *args,
        struct ast_ari_response *response)
 {
-       RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+       struct ast_variable *variables = NULL;
 
        /* Parse any query parameters out of the body parameter */
        if (args->variables) {
@@ -1159,12 +1192,9 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
 
                ast_ari_channels_originate_with_id_parse_body(args->variables, args);
                json_variables = ast_json_object_get(args->variables, "variables");
-               if (json_variables) {
-                       if (ast_json_to_ast_variables(json_variables, &variables)) {
-                               ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
-                               ast_ari_response_alloc_failed(response);
-                               return;
-                       }
+               if (json_variables
+                       && json_to_ast_variables(response, json_variables, &variables)) {
+                       return;
                }
        }
 
@@ -1183,13 +1213,14 @@ void ast_ari_channels_originate_with_id(struct ast_variable *headers,
                args->other_channel_id,
                args->originator,
                response);
+       ast_variables_destroy(variables);
 }
 
 void ast_ari_channels_originate(struct ast_variable *headers,
        struct ast_ari_channels_originate_args *args,
        struct ast_ari_response *response)
 {
-       RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+       struct ast_variable *variables = NULL;
 
        /* Parse any query parameters out of the body parameter */
        if (args->variables) {
@@ -1197,12 +1228,9 @@ void ast_ari_channels_originate(struct ast_variable *headers,
 
                ast_ari_channels_originate_parse_body(args->variables, args);
                json_variables = ast_json_object_get(args->variables, "variables");
-               if (json_variables) {
-                       if (ast_json_to_ast_variables(json_variables, &variables)) {
-                               ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
-                               ast_ari_response_alloc_failed(response);
-                               return;
-                       }
+               if (json_variables
+                       && json_to_ast_variables(response, json_variables, &variables)) {
+                       return;
                }
        }
 
@@ -1221,6 +1249,7 @@ void ast_ari_channels_originate(struct ast_variable *headers,
                args->other_channel_id,
                args->originator,
                response);
+       ast_variables_destroy(variables);
 }
 
 void ast_ari_channels_get_channel_var(struct ast_variable *headers,
index 4f91e78..f794969 100644 (file)
@@ -220,35 +220,66 @@ static void send_message(const char *to, const char *from, const char *body, str
        response->response_text = "Accepted";
 }
 
+/*!
+ * \internal
+ * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
+ * \since 13.3.0
+ *
+ * \param[out] response HTTP response if error
+ * \param json_variables The JSON blob containing the variable
+ * \param[out] variables An out reference to the variables to populate.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
+{
+       enum ast_json_to_ast_vars_code res;
+
+       res = ast_json_to_ast_variables(json_variables, variables);
+       switch (res) {
+       case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
+               return 0;
+       case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
+               ast_ari_response_error(response, 400, "Bad Request",
+                       "Only string values in the 'variables' object allowed");
+               break;
+       case AST_JSON_TO_AST_VARS_CODE_OOM:
+               ast_ari_response_alloc_failed(response);
+               break;
+       }
+       ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
+
+       return -1;
+}
+
 void ast_ari_endpoints_send_message(struct ast_variable *headers,
        struct ast_ari_endpoints_send_message_args *args,
        struct ast_ari_response *response)
 {
-       RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
+       struct ast_variable *variables = NULL;
 
        if (args->variables) {
                struct ast_json *json_variables;
 
                ast_ari_endpoints_send_message_parse_body(args->variables, args);
                json_variables = ast_json_object_get(args->variables, "variables");
-               if (json_variables) {
-                       if (ast_json_to_ast_variables(json_variables, &variables)) {
-                               ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
-                               ast_ari_response_alloc_failed(response);
-                               return;
-                       }
+               if (json_variables
+                       && json_to_ast_variables(response, json_variables, &variables)) {
+                       return;
                }
        }
 
        send_message(args->to, args->from, args->body, variables, response);
+       ast_variables_destroy(variables);
 }
 
 void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
        struct ast_ari_endpoints_send_message_to_endpoint_args *args,
        struct ast_ari_response *response)
 {
-       RAII_VAR(struct ast_variable *, variables, NULL, ast_variables_destroy);
-       RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
+       struct ast_variable *variables = NULL;
+       struct ast_endpoint_snapshot *snapshot;
        char msg_to[128];
        char *tech = ast_strdupa(args->tech);
 
@@ -259,23 +290,21 @@ void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers,
                        "Endpoint not found");
                return;
        }
+       ao2_ref(snapshot, -1);
 
        if (args->variables) {
                struct ast_json *json_variables;
 
                ast_ari_endpoints_send_message_to_endpoint_parse_body(args->variables, args);
                json_variables = ast_json_object_get(args->variables, "variables");
-
-               if (json_variables) {
-                       if (ast_json_to_ast_variables(json_variables, &variables)) {
-                               ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
-                               ast_ari_response_alloc_failed(response);
-                               return;
-                       }
+               if (json_variables
+                       && json_to_ast_variables(response, json_variables, &variables)) {
+                       return;
                }
        }
 
        snprintf(msg_to, sizeof(msg_to), "%s:%s", ast_str_to_lower(tech), args->resource);
 
        send_message(msg_to, args->from, args->body, variables, response);
+       ast_variables_destroy(variables);
 }
index 3ebe668..8d8ed71 100644 (file)
@@ -181,6 +181,7 @@ static void ast_ari_endpoints_send_message_cb(
                break;
        case 500: /* Internal Server Error */
        case 501: /* Not Implemented */
+       case 400: /* Invalid parameters for sending a message. */
        case 404: /* Endpoint not found */
                is_valid = 1;
                break;
index e7b4ba7..17b8847 100644 (file)
                                        ],
                                        "errorResponses": [
                                                {
+                                                       "code": 400,
+                                                       "reason": "Invalid parameters for sending a message."
+                                               },
+                                               {
                                                        "code": 404,
                                                        "reason": "Endpoint not found"
                                                }