Exemplo n.º 1
0
static struct msg_data* msg_data_create(const struct ast_msg *msg, const char *to, const char *from)
{
	char *tag;
	struct msg_data *mdata = ao2_alloc(sizeof(*mdata), msg_data_destroy);

	if (!mdata) {
		return NULL;
	}

	/* typecast to suppress const warning */
	mdata->msg = ast_msg_ref((struct ast_msg*)msg);

	/* starts with 'pjsip:' the 'pj' needs to be removed and maybe even
	   the entire string. */
	if (!(to = strchr(to, ':'))) {
		ao2_ref(mdata, -1);
		return NULL;
	}

	/* Make sure we start with sip: */
	mdata->to = ast_begins_with(to, "sip:") ? ast_strdup(++to) : ast_strdup(to - 3);
	mdata->from = ast_strdup(from);

	/* sometimes from can still contain the tag at this point, so remove it */
	if ((tag = strchr(mdata->from, ';'))) {
		*tag = '\0';
	}
	return mdata;
}
Exemplo n.º 2
0
static char *assign_uuid(struct ast_json *json_channel)
{
	const char *channel_name = ast_json_string_get(ast_json_object_get(json_channel, "name"));
	enum hep_uuid_type uuid_type = hepv3_get_uuid_type();
	char *uuid = NULL;

	if (!channel_name) {
		return NULL;
	}

	if (uuid_type == HEP_UUID_TYPE_CALL_ID) {
		struct ast_channel *chan = NULL;
		char buf[128];

		if (ast_begins_with(channel_name, "PJSIP")) {
			chan = ast_channel_get_by_name(channel_name);

			if (chan && !ast_func_read(chan, "CHANNEL(pjsip,call-id)", buf, sizeof(buf))) {
				uuid = ast_strdup(buf);
			}
		} else if (ast_begins_with(channel_name, "SIP")) {
			chan = ast_channel_get_by_name(channel_name);

			if (chan && !ast_func_read(chan, "SIP_HEADER(call-id)", buf, sizeof(buf))) {
				uuid = ast_strdup(buf);
			}
		}

		ast_channel_cleanup(chan);
	}

	/* If we couldn't get the call-id or didn't want it, just use the channel name */
	if (!uuid) {
		uuid = ast_strdup(channel_name);
	}

	return uuid;
}
Exemplo n.º 3
0
char *ast_crypt(const char *key, const char *salt)
{
	struct crypt_data data = {};
	const char *crypted = crypt_r(key, salt, &data);

	/* Crypt may return success even if it doesn't recognize the salt. But
	 * in those cases it always mangles the salt in some way.
	 */
	if (!crypted || !ast_begins_with(crypted, salt)) {
		return NULL;
	}

	return ast_strdup(crypted);
}
Exemplo n.º 4
0
char *ast_crypt(const char *key, const char *salt)
{
	const char *crypted;
	SCOPED_MUTEX(lock, &crypt_mutex);

	crypted = crypt(key, salt);

	/* Crypt may return success even if it doesn't recognize the salt. But
	 * in those cases it always mangles the salt in some way.
	 */
	if (!crypted || !ast_begins_with(crypted, salt)) {
		return NULL;
	}

	return ast_strdup(crypted);
}
Exemplo n.º 5
0
/*!
 * \internal
 * \brief ARI HTTP handler.
 *
 * This handler takes the HTTP request and turns it into the appropriate
 * RESTful request (conversion to JSON, routing, etc.)
 *
 * \param ser TCP session.
 * \param urih URI handler.
 * \param uri URI requested.
 * \param method HTTP method.
 * \param get_params HTTP \c GET params.
 * \param headers HTTP headers.
 */
static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
                            const struct ast_http_uri *urih,
                            const char *uri,
                            enum ast_http_method method,
                            struct ast_variable *get_params,
                            struct ast_variable *headers)
{
    RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
    RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
    RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
    struct ast_ari_response response = {};
    RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);

    if (!response_body) {
        ast_http_request_close_on_completion(ser);
        ast_http_error(ser, 500, "Server Error", "Out of memory");
        return 0;
    }

    response.headers = ast_str_create(40);
    if (!response.headers) {
        ast_http_request_close_on_completion(ser);
        ast_http_error(ser, 500, "Server Error", "Out of memory");
        return 0;
    }

    conf = ast_ari_config_get();
    if (!conf || !conf->general) {
        ast_free(response.headers);
        ast_http_request_close_on_completion(ser);
        ast_http_error(ser, 500, "Server Error", "URI handler config missing");
        return 0;
    }

    process_cors_request(headers, &response);

    /* Process form data from a POST. It could be mixed with query
     * parameters, which seems a bit odd. But it's allowed, so that's okay
     * with us.
     */
    post_vars = ast_http_get_post_vars(ser, headers);
    if (!post_vars) {
        switch (errno) {
        case EFBIG:
            ast_ari_response_error(&response, 413,
                                   "Request Entity Too Large",
                                   "Request body too large");
            goto request_failed;
        case ENOMEM:
            ast_http_request_close_on_completion(ser);
            ast_ari_response_error(&response, 500,
                                   "Internal Server Error",
                                   "Out of memory");
            goto request_failed;
        case EIO:
            ast_ari_response_error(&response, 400,
                                   "Bad Request", "Error parsing request body");
            goto request_failed;
        }
    }
    if (get_params == NULL) {
        get_params = post_vars;
    } else if (get_params && post_vars) {
        /* Has both post_vars and get_params */
        struct ast_variable *last_var = post_vars;
        while (last_var->next) {
            last_var = last_var->next;
        }
        /* The duped get_params will get freed when post_vars gets
         * ast_variables_destroyed.
         */
        last_var->next = ast_variables_dup(get_params);
        get_params = post_vars;
    }

    user = authenticate_user(get_params, headers);
    if (response.response_code > 0) {
        /* POST parameter processing error. Do nothing. */
    } else if (!user) {
        /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
         * message is used by an origin server to challenge the
         * authorization of a user agent. This response MUST include a
         * WWW-Authenticate header field containing at least one
         * challenge applicable to the requested resource.
         */
        ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required");

        /* Section 1.2:
         *   realm       = "realm" "=" realm-value
         *   realm-value = quoted-string
         * Section 2:
         *   challenge   = "Basic" realm
         */
        ast_str_append(&response.headers, 0,
                       "WWW-Authenticate: Basic realm=\"%s\"\r\n",
                       conf->general->auth_realm);
    } else if (!ast_fully_booted) {
        ast_http_request_close_on_completion(ser);
        ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted");
    } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
        ast_ari_response_error(&response, 403, "Forbidden", "Write access denied");
    } else if (ast_ends_with(uri, "/")) {
        remove_trailing_slash(uri, &response);
    } else if (ast_begins_with(uri, "api-docs/")) {
        /* Serving up API docs */
        if (method != AST_HTTP_GET) {
            ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method");
        } else {
            /* Skip the api-docs prefix */
            ast_ari_get_docs(strchr(uri, '/') + 1, headers, &response);
        }
    } else {
        /* Other RESTful resources */
        ast_ari_invoke(ser, uri, method, get_params, headers,
                       &response);
    }

    if (response.no_response) {
        /* The handler indicates no further response is necessary.
         * Probably because it already handled it */
        ast_free(response.headers);
        return 0;
    }

request_failed:
    /* If you explicitly want to have no content, set message to
     * ast_json_null().
     */
    ast_assert(response.message != NULL);
    ast_assert(response.response_code > 0);

    /* response.message could be NULL, in which case the empty response_body
     * is correct
     */
    if (response.message && !ast_json_is_null(response.message)) {
        ast_str_append(&response.headers, 0,
                       "Content-type: application/json\r\n");
        if (ast_json_dump_str_format(response.message, &response_body,
                                     conf->general->format) != 0) {
            /* Error encoding response */
            response.response_code = 500;
            response.response_text = "Internal Server Error";
            ast_str_set(&response_body, 0, "%s", "");
            ast_str_set(&response.headers, 0, "%s", "");
        }
    }

    ast_debug(3, "Examining ARI response:\n%d %s\n%s\n%s\n", response.response_code,
              response.response_text, ast_str_buffer(response.headers), ast_str_buffer(response_body));
    ast_http_send(ser, method, response.response_code,
                  response.response_text, response.headers, response_body,
                  0, 0);
    /* ast_http_send takes ownership, so we don't have to free them */
    response_body = NULL;

    ast_json_unref(response.message);
    return 0;
}
Exemplo n.º 6
0
void ast_ari_get_docs(const char *uri, struct ast_variable *headers,
                      struct ast_ari_response *response)
{
    RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
    RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
    RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
    struct ast_json *obj = NULL;
    struct ast_variable *host = NULL;
    struct ast_json_error error = {};
    struct stat file_stat;

    ast_debug(3, "%s(%s)\n", __func__, uri);

    absolute_path_builder = ast_str_create(80);
    if (absolute_path_builder == NULL) {
        ast_ari_response_alloc_failed(response);
        return;
    }

    /* absolute path to the rest-api directory */
    ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
    ast_str_append(&absolute_path_builder, 0, "/rest-api/");
    absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
    if (absolute_api_dirname == NULL) {
        ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
        ast_ari_response_error(
            response, 500, "Internal Server Error",
            "Cannot find rest-api directory");
        return;
    }

    /* absolute path to the requested file */
    ast_str_append(&absolute_path_builder, 0, "%s", uri);
    absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
    if (absolute_filename == NULL) {
        switch (errno) {
        case ENAMETOOLONG:
        case ENOENT:
        case ENOTDIR:
            ast_ari_response_error(
                response, 404, "Not Found",
                "Resource not found");
            break;
        case EACCES:
            ast_ari_response_error(
                response, 403, "Forbidden",
                "Permission denied");
            break;
        default:
            ast_log(LOG_ERROR,
                    "Error determining real path for uri '%s': %s\n",
                    uri, strerror(errno));
            ast_ari_response_error(
                response, 500, "Internal Server Error",
                "Cannot find file");
            break;
        }
        return;
    }

    if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
        /* HACKERZ! */
        ast_log(LOG_ERROR,
                "Invalid attempt to access '%s' (not in %s)\n",
                absolute_filename, absolute_api_dirname);
        ast_ari_response_error(
            response, 404, "Not Found",
            "Resource not found");
        return;
    }

    if (stat(absolute_filename, &file_stat) == 0) {
        if (!(file_stat.st_mode & S_IFREG)) {
            /* Not a file */
            ast_ari_response_error(
                response, 403, "Forbidden",
                "Invalid access");
            return;
        }
    } else {
        /* Does not exist */
        ast_ari_response_error(
            response, 404, "Not Found",
            "Resource not found");
        return;
    }

    /* Load resource object from file */
    obj = ast_json_load_new_file(absolute_filename, &error);
    if (obj == NULL) {
        ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
                error.source, error.line, error.column, error.text);
        ast_ari_response_error(
            response, 500, "Internal Server Error",
            "Yikes! Cannot parse resource");
        return;
    }

    /* Update the basePath properly */
    if (ast_json_object_get(obj, "basePath") != NULL) {
        for (host = headers; host; host = host->next) {
            if (strcasecmp(host->name, "Host") == 0) {
                break;
            }
        }
        if (host != NULL) {
            ast_json_object_set(
                obj, "basePath",
                ast_json_stringf("http://%s/ari", host->value));
        } else {
            /* Without the host, we don't have the basePath */
            ast_json_object_del(obj, "basePath");
        }
    }

    ast_ari_response_ok(response, obj);
}
Exemplo n.º 7
0
/*!
 * \internal
 * \brief ARI HTTP handler.
 *
 * This handler takes the HTTP request and turns it into the appropriate
 * RESTful request (conversion to JSON, routing, etc.)
 *
 * \param ser TCP session.
 * \param urih URI handler.
 * \param uri URI requested.
 * \param method HTTP method.
 * \param get_params HTTP \c GET params.
 * \param headers HTTP headers.
 */
static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
				const struct ast_http_uri *urih,
				const char *uri,
				enum ast_http_method method,
				struct ast_variable *get_params,
				struct ast_variable *headers)
{
	RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
	RAII_VAR(struct ast_str *, response_headers, ast_str_create(40), ast_free);
	RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
	RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
	struct ast_ari_response response = {};
	int ret = 0;

	if (!response_headers || !response_body) {
		return -1;
	}

	response.headers = ast_str_create(40);
	if (!response.headers) {
		return -1;
	}

	conf = ast_ari_config_get();
	if (!conf || !conf->general) {
		return -1;
	}

	process_cors_request(headers, &response);

	user = authenticate_user(get_params, headers);
	if (!user) {
		/* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
		 * message is used by an origin server to challenge the
		 * authorization of a user agent. This response MUST include a
		 * WWW-Authenticate header field containing at least one
		 * challenge applicable to the requested resource.
		 */
		response.response_code = 401;
		response.response_text = "Unauthorized";

		/* Section 1.2:
		 *   realm       = "realm" "=" realm-value
		 *   realm-value = quoted-string
		 * Section 2:
		 *   challenge   = "Basic" realm
		 */
		ast_str_append(&response.headers, 0,
			"WWW-Authenticate: Basic realm=\"%s\"\r\n",
			conf->general->auth_realm);
		response.message = ast_json_pack("{s: s}",
			"error", "Authentication required");
	} else if (!ast_fully_booted) {
		response.response_code = 503;
		response.response_text = "Service Unavailable";
		response.message = ast_json_pack("{s: s}",
			"error", "Asterisk not booted");
	} else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
		response.message = ast_json_pack("{s: s}",
			"error", "Write access denied");
		response.response_code = 403;
		response.response_text = "Forbidden";
	} else if (ast_ends_with(uri, "/")) {
		remove_trailing_slash(uri, &response);
	} else if (ast_begins_with(uri, "api-docs/")) {
		/* Serving up API docs */
		if (method != AST_HTTP_GET) {
			response.message =
				ast_json_pack("{s: s}",
					      "message", "Unsupported method");
			response.response_code = 405;
			response.response_text = "Method Not Allowed";
		} else {
			/* Skip the api-docs prefix */
			ast_ari_get_docs(strchr(uri, '/') + 1, headers, &response);
		}
	} else {
		/* Other RESTful resources */
		ast_ari_invoke(ser, uri, method, get_params, headers,
			&response);
	}

	if (response.no_response) {
		/* The handler indicates no further response is necessary.
		 * Probably because it already handled it */
		return 0;
	}

	/* If you explicitly want to have no content, set message to
	 * ast_json_null().
	 */
	ast_assert(response.message != NULL);
	ast_assert(response.response_code > 0);

	ast_str_append(&response_headers, 0, "%s", ast_str_buffer(response.headers));

	/* response.message could be NULL, in which case the empty response_body
	 * is correct
	 */
	if (response.message && !ast_json_is_null(response.message)) {
		ast_str_append(&response_headers, 0,
			       "Content-type: application/json\r\n");
		if (ast_json_dump_str_format(response.message, &response_body,
				conf->general->format) != 0) {
			/* Error encoding response */
			response.response_code = 500;
			response.response_text = "Internal Server Error";
			ast_str_set(&response_body, 0, "%s", "");
			ast_str_set(&response_headers, 0, "%s", "");
			ret = -1;
		}
	}

	ast_http_send(ser, method, response.response_code,
		      response.response_text, response_headers, response_body,
		      0, 0);
	/* ast_http_send takes ownership, so we don't have to free them */
	response_headers = NULL;
	response_body = NULL;

	ast_json_unref(response.message);
	return ret;
}