1 #include "httpclient.h" 2 #include "utilities/utils.h" 5 #include <openssl/crypto.h> 6 #include <openssl/err.h> 7 #include <openssl/evp.h> 8 #include <openssl/opensslv.h> 9 #include <openssl/pem.h> 10 #include <openssl/pkcs12.h> 11 #include <openssl/rand.h> 12 #include <openssl/rsa.h> 13 #include <openssl/ssl.h> 15 #include "crypto/openssl_compat.h" 30 static size_t writeString(
void* contents,
size_t size,
size_t nmemb,
void* userp) {
36 if (arg->out.length() + size * nmemb >
static_cast<uint64_t
>(arg->limit)) {
40 (
static_cast<WriteStringArg*
>(userp))->out.append(static_cast<char*>(contents), size * nmemb);
46 HttpClient::HttpClient() : user_agent(
std::string(
"Aktualizr/") + AKTUALIZR_VERSION) {
47 curl = curl_easy_init();
48 if (curl ==
nullptr) {
49 throw std::runtime_error(
"Could not initialize curl");
54 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
55 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 60L);
56 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 60L);
59 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeString);
60 curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
62 curl_easy_setopt(curl, CURLOPT_VERBOSE, get_curlopt_verbose());
64 headers = curl_slist_append(headers,
"Content-Type: application/json");
65 headers = curl_slist_append(headers,
"Accept: */*");
66 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
67 curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
70 HttpClient::HttpClient(
const HttpClient& curl_in) : pkcs11_key(curl_in.pkcs11_key), pkcs11_cert(curl_in.pkcs11_key) {
71 curl = curl_easy_duphandle(curl_in.curl);
73 struct curl_slist* inlist = curl_in.headers;
75 struct curl_slist* tmp;
77 while (inlist !=
nullptr) {
78 tmp = curl_slist_append(headers, inlist->data);
81 curl_slist_free_all(headers);
82 throw std::runtime_error(
"curl_slist_append returned null");
86 inlist = inlist->next;
92 HttpClient::~HttpClient() {
93 curl_slist_free_all(headers);
94 curl_easy_cleanup(curl);
97 HttpResponse HttpClient::get(
const std::string& url, int64_t maxsize) {
98 CURL* curl_get = curl_easy_duphandle(curl);
102 curl_easy_setopt(curl_get, CURLOPT_SSLENGINE,
"pkcs11");
103 curl_easy_setopt(curl_get, CURLOPT_SSLKEYTYPE,
"ENG");
107 curl_easy_setopt(curl_get, CURLOPT_SSLCERTTYPE,
"ENG");
112 curl_easy_setopt(curl_get, CURLOPT_POSTFIELDS,
"");
113 curl_easy_setopt(curl_get, CURLOPT_URL, url.c_str());
114 curl_easy_setopt(curl_get, CURLOPT_HTTPGET, 1L);
118 curl_easy_setopt(curl_get, CURLOPT_MAXFILESIZE_LARGE, maxsize);
120 curl_easy_setopt(curl_get, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
121 curl_easy_setopt(curl_get, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
122 LOG_DEBUG <<
"GET " << url;
123 HttpResponse response = perform(curl_get, RETRY_TIMES, maxsize);
124 curl_easy_cleanup(curl_get);
128 void HttpClient::setCerts(
const std::string& ca, CryptoSource ca_source,
const std::string& cert,
129 CryptoSource cert_source,
const std::string& pkey, CryptoSource pkey_source) {
130 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
131 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
132 curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
134 if (ca_source == CryptoSource::kPkcs11) {
135 throw std::runtime_error(
"Accessing CA certificate on PKCS11 devices isn't currently supported");
137 std::unique_ptr<TemporaryFile> tmp_ca_file = std_::make_unique<TemporaryFile>(
"tls-ca");
138 tmp_ca_file->PutContents(ca);
139 curl_easy_setopt(curl, CURLOPT_CAINFO, tmp_ca_file->Path().c_str());
140 tls_ca_file = std::move_if_noexcept(tmp_ca_file);
142 if (cert_source == CryptoSource::kPkcs11) {
143 curl_easy_setopt(curl, CURLOPT_SSLCERT, cert.c_str());
144 curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,
"ENG");
146 std::unique_ptr<TemporaryFile> tmp_cert_file = std_::make_unique<TemporaryFile>(
"tls-cert");
147 tmp_cert_file->PutContents(cert);
148 curl_easy_setopt(curl, CURLOPT_SSLCERT, tmp_cert_file->Path().c_str());
149 curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,
"PEM");
150 tls_cert_file = std::move_if_noexcept(tmp_cert_file);
152 pkcs11_cert = (cert_source == CryptoSource::kPkcs11);
154 if (pkey_source == CryptoSource::kPkcs11) {
155 curl_easy_setopt(curl, CURLOPT_SSLENGINE,
"pkcs11");
156 curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
157 curl_easy_setopt(curl, CURLOPT_SSLKEY, pkey.c_str());
158 curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE,
"ENG");
160 std::unique_ptr<TemporaryFile> tmp_pkey_file = std_::make_unique<TemporaryFile>(
"tls-pkey");
161 tmp_pkey_file->PutContents(pkey);
162 curl_easy_setopt(curl, CURLOPT_SSLKEY, tmp_pkey_file->Path().c_str());
163 curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE,
"PEM");
164 tls_pkey_file = std::move_if_noexcept(tmp_pkey_file);
166 pkcs11_key = (pkey_source == CryptoSource::kPkcs11);
169 HttpResponse HttpClient::post(
const std::string& url,
const Json::Value&
data) {
170 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
171 curl_easy_setopt(curl, CURLOPT_POST, 1);
172 std::string data_str = Json::FastWriter().write(
data);
173 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_str.c_str());
174 LOG_TRACE <<
"post request body:" <<
data;
175 return perform(curl, RETRY_TIMES, HttpInterface::kPostRespLimit);
178 HttpResponse HttpClient::put(
const std::string& url,
const Json::Value&
data) {
179 CURL* curl_put = curl_easy_duphandle(curl);
183 curl_easy_setopt(curl_put, CURLOPT_SSLENGINE,
"pkcs11");
184 curl_easy_setopt(curl_put, CURLOPT_SSLKEYTYPE,
"ENG");
188 curl_easy_setopt(curl_put, CURLOPT_SSLCERTTYPE,
"ENG");
191 curl_easy_setopt(curl_put, CURLOPT_URL, url.c_str());
192 std::string data_str = Json::FastWriter().write(
data);
193 curl_easy_setopt(curl_put, CURLOPT_POSTFIELDS, data_str.c_str());
194 curl_easy_setopt(curl_put, CURLOPT_CUSTOMREQUEST,
"PUT");
195 LOG_TRACE <<
"put request body:" <<
data;
196 HttpResponse result = perform(curl_put, RETRY_TIMES, HttpInterface::kPutRespLimit);
197 curl_easy_cleanup(curl_put);
201 HttpResponse HttpClient::perform(CURL* curl_handler,
int retry_times, int64_t size_limit) {
203 response_arg.limit = size_limit;
204 curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, (
void*)&response_arg);
205 CURLcode result = curl_easy_perform(curl_handler);
206 curl_easy_getinfo(curl_handler, CURLINFO_RESPONSE_CODE, &http_code);
207 HttpResponse response(response_arg.out, http_code, result, (result != CURLE_OK) ? curl_easy_strerror(result) :
"");
208 if (response.curl_code != CURLE_OK || response.http_status_code >= 500) {
209 std::ostringstream error_message;
210 error_message <<
"curl error " << response.curl_code <<
" (http code " << response.http_status_code
211 <<
"): " << response.error_message;
212 LOG_ERROR << error_message.str();
213 if (retry_times != 0) {
215 response = perform(curl_handler, --retry_times, size_limit);
218 LOG_TRACE <<
"response http code: " << response.http_status_code;
219 LOG_TRACE <<
"response: " << response.body;
223 HttpResponse HttpClient::download(
const std::string& url, curl_write_callback callback,
void* userp) {
224 CURL* curl_download = curl_easy_duphandle(curl);
228 curl_easy_setopt(curl_download, CURLOPT_SSLENGINE,
"pkcs11");
229 curl_easy_setopt(curl_download, CURLOPT_SSLKEYTYPE,
"ENG");
233 curl_easy_setopt(curl_download, CURLOPT_SSLCERTTYPE,
"ENG");
236 curl_easy_setopt(curl_download, CURLOPT_URL, url.c_str());
237 curl_easy_setopt(curl_download, CURLOPT_HTTPGET, 1L);
238 curl_easy_setopt(curl_download, CURLOPT_FOLLOWLOCATION, 1L);
239 curl_easy_setopt(curl_download, CURLOPT_WRITEFUNCTION, callback);
240 curl_easy_setopt(curl_download, CURLOPT_WRITEDATA, userp);
241 curl_easy_setopt(curl_download, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
242 curl_easy_setopt(curl_download, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
244 CURLcode result = curl_easy_perform(curl_download);
245 curl_easy_getinfo(curl_download, CURLINFO_RESPONSE_CODE, &http_code);
246 HttpResponse response(
"", http_code, result, (result != CURLE_OK) ? curl_easy_strerror(result) :
"");
247 curl_easy_cleanup(curl_download);
Helper class to manage curl_global_init/curl_global_cleanup calls.