/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved. * iSulad licensed under the Mulan PSL v1. * You can use this software according to the terms and conditions of the Mulan PSL v1. * You may obtain a copy of Mulan PSL v1 at: * http://license.coscl.org.cn/MulanPSL * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR * PURPOSE. * See the Mulan PSL v1 for more details. * Author: tanyifeng * Create: 2018-11-08 * Description: provide container http function ******************************************************************************/ #include #include #include #include "http.h" #include "buffer.h" #include "log.h" #include "utils.h" size_t fwrite_buffer(const char *ptr, size_t eltsize, size_t nmemb, void *buffer_) { size_t size = eltsize * nmemb; struct Buffer *buffer = buffer_; int status = 0; status = buffer_append(buffer, ptr, size); if (status != 0) { ERROR("Failed to write buffer\n"); } return size; } size_t fwrite_file(const void *ptr, size_t size, size_t nmemb, void *stream) { size_t written = fwrite(ptr, size, nmemb, (FILE *)stream); return written; } size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf) { return eltsize * nmemb; } void free_http_get_options(struct http_get_options *options) { if (options == NULL) { return; } free(options->accepts); options->accepts = NULL; free(options->authorization); options->authorization = NULL; free(options->unix_socket_path); options->unix_socket_path = NULL; /* The options->output is a FILE pointer, we should not free it here */ free(options); return; } void http_global_init(void) { curl_global_init(CURL_GLOBAL_NOTHING); } void http_global_cleanup(void) { curl_global_cleanup(); } struct curl_slist *http_get_chunk_header(const struct http_get_options *options) { int ret = 0; int nret; size_t len = 0; struct curl_slist *chunk = NULL; char *header = NULL; if (options->with_header_auth && options->authorization) { if (strlen(options->authorization) > (SIZE_MAX - strlen("Authorization: ")) - 1) { ERROR("Invalid authorization option"); ret = -1; goto out; } len = strlen(options->authorization) + strlen("Authorization: ") + 1; header = util_common_calloc_s(len); if (header == NULL) { ERROR("Out of memory"); ret = -1; goto out; } nret = snprintf(header, len, "Authorization: %s", options->authorization); if (nret < 0 || (size_t)nret >= len) { ERROR("Failed to print string"); ret = -1; goto out; } chunk = curl_slist_append(chunk, header); free(header); header = NULL; } if (options->with_header_json) { chunk = curl_slist_append(chunk, "Content-Type: application/json"); // Disable "Expect: 100-continue" chunk = curl_slist_append(chunk, "Expect:"); } if (options->with_header_accept && options->accepts) { if (strlen(options->accepts) > (SIZE_MAX - strlen("Accept: ")) - 1) { ERROR("Invalid accepts option"); ret = -1; goto out; } len = strlen(options->accepts) + strlen("Accept: ") + 1; header = util_common_calloc_s(len); if (header == NULL) { ERROR("Out of memory"); ret = -1; goto out; } nret = snprintf(header, len, "Accept: %s", options->accepts); if (nret < 0 || (size_t)nret >= len) { ERROR("Failed to print string"); ret = -1; goto out; } chunk = curl_slist_append(chunk, header); free(header); header = NULL; } out: if (ret) { curl_slist_free_all(chunk); chunk = NULL; } free(header); return chunk; } static int http_custom_options(CURL *curl_handle, const struct http_get_options *options) { int ret = 0; if (curl_handle == NULL || options == NULL) { return -1; } if (options->unix_socket_path) { curl_easy_setopt(curl_handle, CURLOPT_UNIX_SOCKET_PATH, options->unix_socket_path); } if (options->with_head) { curl_easy_setopt(curl_handle, CURLOPT_HEADER, 1L); } if (options->with_body == 0) { curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L); } /* disable progress meter, set to 0L to enable and disable debug output */ if (options->show_progress == 0) { curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); } else if (options->show_progress && options->progressinfo && options->progress_info_op) { curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, options->progress_info_op); /* pass the struct pointer into the progress function */ curl_easy_setopt(curl_handle, CURLOPT_PROGRESSDATA, options->progressinfo); curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L); } else { curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L); } if (options->input) { curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, options->input); curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, options->input_len); curl_easy_setopt(curl_handle, CURLOPT_POST, 1L); } return ret; } static void close_file(FILE *pagefile) { if (pagefile != NULL) { fclose(pagefile); } } static void free_rpath(char *rpath) { free(rpath); } static void check_buf_len(const char *errbuf) { size_t len = 0; len = strlen(errbuf); if (len > 0) { fprintf(stderr, "%s%s", errbuf, ((errbuf[len - 1] != '\n') ? "\n" : "")); } } static void buffer_empty_on_condition(struct http_get_options *options) { if (options == NULL) { return; } if (options->output && options->outputtype == HTTP_REQUEST_STRBUF) { buffer_empty(options->output); } if (options->with_header_auth && options->authorization) { options->with_header_auth = 0; } } static void curl_getinfo_on_condition(long *response_code, CURL *curl_handle, char **tmp) { if (response_code != NULL) { curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, response_code); } curl_easy_getinfo(curl_handle, CURLINFO_REDIRECT_URL, tmp); } static int ensure_path_file(char **rpath, const struct http_get_options *options, FILE **pagefile) { if (util_ensure_path(rpath, options->output)) { return -1; } *pagefile = util_fopen(*rpath, "w+"); if (*pagefile == NULL) { ERROR("Failed to open file %s\n", options->output); return -1; } return 0; } static struct curl_slist *set_custom_header(CURL *curl_handle, const struct http_get_options *options) { struct curl_slist *chunk = NULL; chunk = http_get_chunk_header(options); if (chunk) { /* set our custom set of headers */ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, chunk); } return chunk; } int http_request(const char *url, struct http_get_options *options, long *response_code, int recursive_len) { #define MAX_REDIRCT_NUMS 32 CURL *curl_handle = NULL; CURLcode curl_result = CURLE_OK; struct curl_slist *chunk = NULL; FILE *pagefile = NULL; char *rpath = NULL; int ret = 0; char errbuf[CURL_ERROR_SIZE] = { 0 }; bool strbuf_args; bool file_args; char *redir_url = NULL; char *tmp = NULL; if (recursive_len + 1 >= MAX_REDIRCT_NUMS) { ERROR("reach the max redirect num"); return -1; } http_global_init(); /* init the curl session */ curl_handle = curl_easy_init(); if (curl_handle == NULL) { return -1; } /* set URL to get here */ curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); /* complete connection within 5 seconds */ curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 5L); /* provide a buffer to store errors in */ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errbuf); curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); ret = http_custom_options(curl_handle, options); if (ret) { goto out; } chunk = set_custom_header(curl_handle, options); strbuf_args = options->output && options->outputtype == HTTP_REQUEST_STRBUF; file_args = options->output && options->outputtype == HTTP_REQUEST_FILE; if (strbuf_args) { curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, options->output); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, fwrite_buffer); } else if (file_args) { /* open the file */ if (ensure_path_file(&rpath, options, &pagefile) == -1) { goto out; } curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, fwrite_file); } else { /* do nothing */ } /* get it! */ curl_result = curl_easy_perform(curl_handle); if (curl_result != CURLE_OK) { check_buf_len(errbuf); ret = -1; } else { curl_getinfo_on_condition(response_code, curl_handle, &tmp); if (tmp) { redir_url = util_strdup_s(tmp); } } out: close_file(pagefile); free_rpath(rpath); /* cleanup curl stuff */ curl_easy_cleanup(curl_handle); curl_slist_free_all(chunk); if (redir_url) { buffer_empty_on_condition(options); if (http_request(redir_url, options, response_code, recursive_len + 1)) { ERROR("Failed to get http request\n"); ret = -1; } free(redir_url); } return ret; } int authz_http_request(const char *username, const char *action, char **resp) { char *request_body = NULL; char err_msg[AUTHZ_ERROR_MSG_SIZE] = { 0 }; long response_code = 0; int ret = 0; int nret = 0; size_t length = 0; struct http_get_options *options = NULL; if (strlen(username) > ((SIZE_MAX - strlen(action)) - strlen(":")) - 1) { ERROR("Invalid arguments"); return -1; } length = strlen(username) + strlen(":") + strlen(action) + 1; request_body = util_common_calloc_s(length); if (request_body == NULL) { ERROR("Out of memory"); *resp = util_strdup_s("Inernal server error: Out of memory"); return -1; } nret = snprintf(request_body, length, "%s:%s", username, action); if (nret < 0 || (size_t)nret >= length) { ERROR("Failed to print string"); free(request_body); return -1; } options = util_common_calloc_s(sizeof(struct http_get_options)); if (options == NULL) { ERROR("Failed to malloc http_get_options"); *resp = util_strdup_s("Inernal server error: Out of memory"); free(request_body); return -1; } options->with_head = 1; options->with_header_json = 1; options->input = request_body; options->input_len = strlen(request_body); options->unix_socket_path = util_strdup_s(AUTHZ_UNIX_SOCK); ret = http_request(AUTHZ_REQUEST_URL, options, &response_code, 0); if (ret != 0) { ERROR("Failed to request authz plugin. Is server running ?"); *resp = util_strdup_s("Failed to request authz plugin. Is server running ?"); ret = -1; goto out; } if (response_code != StatusOK) { ret = -1; nret = snprintf(err_msg, sizeof(err_msg), "action '%s' for user '%s': permission denied", action, username); if (nret < 0 || (size_t)nret >= sizeof(err_msg)) { ERROR("Out of memory"); *resp = util_strdup_s("Inernal server error: Out of memory"); goto out; } *resp = util_strdup_s(err_msg); goto out; } out: free(request_body); if (options != NULL) { free(options->unix_socket_path); free(options); } return ret; }