9
#include <openssl/pem.h>
10
#include <openssl/err.h>
11
12
+#define CLAIM_RESPONSE_SIZE_LIMIT (10 * 1024 * 1024)
13
+
14
+struct claim_response_buffer {
15
+ BUFFER *wb;
16
+ bool too_large;
17
+};
18
+
19
static bool check_and_generate_certificates() {
20
FILE *fp;
21
EVP_PKEY *pkey = NULL;
83
}
84
85
static size_t response_write_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
79
- BUFFER *wb = stream;
86
+ struct claim_response_buffer *response = stream;
87
+
88
+ if (unlikely(nmemb && size > SIZE_MAX / nmemb)) {
89
+ response->too_large = true;
90
+ return 0;
91
+ }
92
size_t real_size = size * nmemb;
93
82
- buffer_memcat(wb, ptr, real_size);
94
+ if (unlikely(real_size > CLAIM_RESPONSE_SIZE_LIMIT - buffer_strlen(response->wb))) {
95
+ response->too_large = true;
96
+ return 0;
97
+ }
98
+
99
+ buffer_memcat(response->wb, ptr, real_size);
100
101
return real_size;
102
}
171
return 0;
172
}
173
174
+static bool cleanup_curl_request_failure(CURL *curl, struct curl_slist *headers, bool *can_retry, CURLcode res,
175
+ const char *option) {
176
+ claim_agent_failure_reason_set("Cannot configure request (%s failed: %s)", option, curl_easy_strerror(res));
177
+
178
+ curl_easy_cleanup(curl);
179
+ if(headers)
180
+ curl_slist_free_all(headers);
181
+
182
+ *can_retry = false;
183
+ return false;
184
+}
185
+
186
static bool send_curl_request(const char *machine_guid, const char *hostname, const char *token, const char *rooms, const char *url, const char *proxy, bool insecure, bool *can_retry) {
187
CURL *curl;
188
CURLcode res;
189
char target_url[2048];
190
char public_key[2048] = "";
191
+ size_t public_key_bytes_read = 0;
192
FILE *fp;
193
struct curl_slist *headers = NULL;
194
205
// Read the public key
206
CLEAN_CHAR_P *public_key_file = filename_from_path_entry_strdupz(netdata_configured_cloud_dir, "public.pem");
207
fp = fopen(public_key_file, "r");
178
- if (!fp || fread(public_key, 1, sizeof(public_key), fp) == 0) {
208
+ if (!fp || (public_key_bytes_read = fread(public_key, 1, sizeof(public_key) - 1, fp)) == 0) {
209
claim_agent_failure_reason_set("cannot read public key file '%s'", public_key_file);
210
if (fp) fclose(fp);
211
*can_retry = false;
212
return false;
213
}
214
+ public_key[public_key_bytes_read] = '\0';
215
fclose(fp);
216
217
// check if we have trusted.pem
257
return false;
258
}
259
260
+#define CURL_SETOPT_OR_RETURN(option, value) \
261
+ do { \
262
+ res = curl_easy_setopt(curl, option, value); \
263
+ if(unlikely(res != CURLE_OK)) \
264
+ return cleanup_curl_request_failure(curl, headers, can_retry, res, #option); \
265
+ } while(0)
266
+
267
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
230
- curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debug_callback);
268
+ CURL_SETOPT_OR_RETURN(CURLOPT_DEBUGFUNCTION, debug_callback);
269
270
// we will receive the response in this
271
CLEAN_BUFFER *response = buffer_create(0, NULL);
272
+ struct claim_response_buffer response_buffer = {
273
+ .wb = response,
274
+ .too_large = false,
275
+ };
276
277
// configure the request
236
- headers = curl_slist_append(headers, "Content-Type: application/json");
237
- curl_easy_setopt(curl, CURLOPT_URL, target_url);
238
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
239
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buffer_tostring(wb));
240
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
241
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, response_write_callback);
242
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
278
+ struct curl_slist *headers_with_content_type = curl_slist_append(headers, "Content-Type: application/json");
279
+ if(unlikely(!headers_with_content_type)) {
280
+ claim_agent_failure_reason_set("Cannot append Content-Type header to the claim request");
281
+ curl_easy_cleanup(curl);
282
+ if(headers)
283
+ curl_slist_free_all(headers);
284
+ *can_retry = false;
285
+ return false;
286
+ }
287
+ headers = headers_with_content_type;
288
+
289
+ CURL_SETOPT_OR_RETURN(CURLOPT_URL, target_url);
290
+ CURL_SETOPT_OR_RETURN(CURLOPT_CUSTOMREQUEST, "PUT");
291
+ CURL_SETOPT_OR_RETURN(CURLOPT_POSTFIELDS, buffer_tostring(wb));
292
+ CURL_SETOPT_OR_RETURN(CURLOPT_HTTPHEADER, headers);
293
+ CURL_SETOPT_OR_RETURN(CURLOPT_WRITEFUNCTION, response_write_callback);
294
+ CURL_SETOPT_OR_RETURN(CURLOPT_WRITEDATA, &response_buffer);
295
+ CURL_SETOPT_OR_RETURN(CURLOPT_MAXFILESIZE_LARGE, (curl_off_t)CLAIM_RESPONSE_SIZE_LIMIT);
296
297
if(trusted_key_file)
245
- curl_easy_setopt(curl, CURLOPT_CAINFO, trusted_key_file);
298
+ CURL_SETOPT_OR_RETURN(CURLOPT_CAINFO, trusted_key_file);
299
300
// Proxy configuration
301
if (proxy) {
302
if (!*proxy || strcmp(proxy, "none") == 0) {
303
// disable proxy configuration in libcurl
251
- curl_easy_setopt(curl, CURLOPT_PROXY, "");
304
+ CURL_SETOPT_OR_RETURN(CURLOPT_PROXY, "");
305
proxy = "none";
306
}
307
308
else if (strcmp(proxy, "env") != 0) {
309
// set the custom proxy for libcurl
257
- curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
310
+ CURL_SETOPT_OR_RETURN(CURLOPT_PROXY, proxy);
311
}
312
313
else {
318
319
// Insecure option
320
if (insecure) {
268
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
269
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
321
+ CURL_SETOPT_OR_RETURN(CURLOPT_SSL_VERIFYPEER, 0L);
322
+ CURL_SETOPT_OR_RETURN(CURLOPT_SSL_VERIFYHOST, 0L);
323
}
324
325
// Set timeout options
273
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
274
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L);
326
+ CURL_SETOPT_OR_RETURN(CURLOPT_TIMEOUT, 10L);
327
+ CURL_SETOPT_OR_RETURN(CURLOPT_CONNECTTIMEOUT, 5L);
328
+
329
+#undef CURL_SETOPT_OR_RETURN
330
331
// execute the request
332
res = curl_easy_perform(curl);
333
if (res != CURLE_OK) {
279
- claim_agent_failure_reason_set("Request failed with error: %s\n"
280
- "proxy: '%s',\n"
281
- "insecure: %s,\n"
282
- "public key file: '%s',\n"
283
- "trusted key file: '%s'",
284
- curl_easy_strerror(res),
285
- proxy,
286
- insecure ? "true" : "false",
287
- public_key_file ? public_key_file : "none",
288
- trusted_key_file ? trusted_key_file : "none");
334
+ bool response_too_large = (res == CURLE_FILESIZE_EXCEEDED || response_buffer.too_large);
335
+
336
+ if (response_too_large)
337
+ claim_agent_failure_reason_set("Request failed: response body exceeded %zu bytes\n"
338
+ "proxy: '%s',\n"
339
+ "insecure: %s,\n"
340
+ "public key file: '%s',\n"
341
+ "trusted key file: '%s'",
342
+ (size_t)CLAIM_RESPONSE_SIZE_LIMIT,
343
+ proxy,
344
+ insecure ? "true" : "false",
345
+ public_key_file ? public_key_file : "none",
346
+ trusted_key_file ? trusted_key_file : "none");
347
+ else
348
+ claim_agent_failure_reason_set("Request failed with error: %s\n"
349
+ "proxy: '%s',\n"
350
+ "insecure: %s,\n"
351
+ "public key file: '%s',\n"
352
+ "trusted key file: '%s'",
353
+ curl_easy_strerror(res),
354
+ proxy,
355
+ insecure ? "true" : "false",
356
+ public_key_file ? public_key_file : "none",
357
+ trusted_key_file ? trusted_key_file : "none");
358
curl_easy_cleanup(curl);
359
curl_slist_free_all(headers);
291
- *can_retry = true;
360
+ *can_retry = !response_too_large;
361
return false;
362
}
363
388
if (json_object_object_get_ex(parsed_json, "errorMsgKey", &error_key_obj))
389
error_key = json_object_get_string(error_key_obj);
390
322
- if (strcmp(error_key, "ErrInvalidNodeID") == 0)
323
- claim_agent_failure_reason_set("Failed: the node id is invalid");
324
- else if (strcmp(error_key, "ErrInvalidNodeName") == 0)
325
- claim_agent_failure_reason_set("Failed: the node name is invalid");
326
- else if (strcmp(error_key, "ErrInvalidRoomID") == 0)
327
- claim_agent_failure_reason_set("Failed: one or more room ids are invalid");
328
- else if (strcmp(error_key, "ErrInvalidPublicKey") == 0)
329
- claim_agent_failure_reason_set("Failed: the public key is invalid");
391
+ if(error_key) {
392
+ if (strcmp(error_key, "ErrInvalidNodeID") == 0)
393
+ claim_agent_failure_reason_set("Failed: the node id is invalid");
394
+ else if (strcmp(error_key, "ErrInvalidNodeName") == 0)
395
+ claim_agent_failure_reason_set("Failed: the node name is invalid");
396
+ else if (strcmp(error_key, "ErrInvalidRoomID") == 0)
397
+ claim_agent_failure_reason_set("Failed: one or more room ids are invalid");
398
+ else if (strcmp(error_key, "ErrInvalidPublicKey") == 0)
399
+ claim_agent_failure_reason_set("Failed: the public key is invalid");
400
+ else
401
+ claim_agent_failure_reason_set("Failed with description '%s'", error_key);
402
+ }
403
else
331
- claim_agent_failure_reason_set("Failed with description '%s'", error_key);
404
+ claim_agent_failure_reason_set("Failed with a response code %ld", http_status_code);
405
406
json_object_put(parsed_json);
407
}