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; }
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; }
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); }
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); }
/*! * \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; }
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); }
/*! * \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; }