summaryrefslogtreecommitdiffstats
path: root/aclk/aclk_otp.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 12:08:18 +0000
commit5da14042f70711ea5cf66e034699730335462f66 (patch)
tree0f6354ccac934ed87a2d555f45be4c831cf92f4a /aclk/aclk_otp.c
parentReleasing debian version 1.44.3-2. (diff)
downloadnetdata-5da14042f70711ea5cf66e034699730335462f66.tar.xz
netdata-5da14042f70711ea5cf66e034699730335462f66.zip
Merging upstream version 1.45.3+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'aclk/aclk_otp.c')
-rw-r--r--aclk/aclk_otp.c883
1 files changed, 0 insertions, 883 deletions
diff --git a/aclk/aclk_otp.c b/aclk/aclk_otp.c
deleted file mode 100644
index 207ca08cf..000000000
--- a/aclk/aclk_otp.c
+++ /dev/null
@@ -1,883 +0,0 @@
-
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "aclk_otp.h"
-#include "aclk_util.h"
-#include "aclk.h"
-
-#include "daemon/common.h"
-
-#include "mqtt_websockets/c-rbuf/include/ringbuffer.h"
-
-static int aclk_https_request(https_req_t *request, https_req_response_t *response) {
- int rc;
- // wrapper for ACLK only which loads ACLK specific proxy settings
- // then only calls https_request
- struct mqtt_wss_proxy proxy_conf = { .host = NULL, .port = 0, .username = NULL, .password = NULL, .type = MQTT_WSS_DIRECT };
- aclk_set_proxy((char**)&proxy_conf.host, &proxy_conf.port, (char**)&proxy_conf.username, (char**)&proxy_conf.password, &proxy_conf.type);
-
- if (proxy_conf.type == MQTT_WSS_PROXY_HTTP) {
- request->proxy_host = (char*)proxy_conf.host; // TODO make it const as well
- request->proxy_port = proxy_conf.port;
- request->proxy_username = proxy_conf.username;
- request->proxy_password = proxy_conf.password;
- }
-
- rc = https_request(request, response);
- freez((char*)proxy_conf.host);
- freez((char*)proxy_conf.username);
- freez((char*)proxy_conf.password);
- return rc;
-}
-
-struct auth_data {
- char *client_id;
- char *username;
- char *passwd;
-};
-
-#define PARSE_ENV_JSON_CHK_TYPE(it, type, name) \
- if (json_object_get_type(json_object_iter_peek_value(it)) != type) { \
- netdata_log_error("value of key \"%s\" should be %s", name, #type); \
- goto exit; \
- }
-
-#define JSON_KEY_CLIENTID "clientID"
-#define JSON_KEY_USER "username"
-#define JSON_KEY_PASS "password"
-#define JSON_KEY_TOPICS "topics"
-
-static int parse_passwd_response(const char *json_str, struct auth_data *auth) {
- int rc = 1;
- json_object *json;
- struct json_object_iterator it;
- struct json_object_iterator itEnd;
-
- json = json_tokener_parse(json_str);
- if (!json) {
- netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
- return 1;
- }
-
- it = json_object_iter_begin(json);
- itEnd = json_object_iter_end(json);
-
- while (!json_object_iter_equal(&it, &itEnd)) {
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_CLIENTID)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_CLIENTID)
-
- auth->client_id = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_USER)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_USER)
-
- auth->username = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_PASS)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_PASS)
-
- auth->passwd = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TOPICS)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_TOPICS)
-
- if (aclk_generate_topic_cache(json_object_iter_peek_value(&it))) {
- netdata_log_error("Failed to generate topic cache!");
- goto exit;
- }
- json_object_iter_next(&it);
- continue;
- }
- netdata_log_error("Unknown key \"%s\" in passwd response payload. Ignoring", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
-
- if (!auth->client_id) {
- netdata_log_error(JSON_KEY_CLIENTID " is compulsory key in /password response");
- goto exit;
- }
- if (!auth->passwd) {
- netdata_log_error(JSON_KEY_PASS " is compulsory in /password response");
- goto exit;
- }
- if (!auth->username) {
- netdata_log_error(JSON_KEY_USER " is compulsory in /password response");
- goto exit;
- }
-
- rc = 0;
-exit:
- json_object_put(json);
- return rc;
-}
-
-#define JSON_KEY_ERTRY "errorNonRetryable"
-#define JSON_KEY_EDELAY "errorRetryDelaySeconds"
-#define JSON_KEY_EEC "errorCode"
-#define JSON_KEY_EMSGKEY "errorMsgKey"
-#define JSON_KEY_EMSG "errorMessage"
-#if JSON_C_MINOR_VERSION >= 13
-static const char *get_json_str_by_path(json_object *json, const char *path) {
- json_object *ptr;
- if (json_pointer_get(json, path, &ptr)) {
- netdata_log_error("Missing compulsory key \"%s\" in error response", path);
- return NULL;
- }
- if (json_object_get_type(ptr) != json_type_string) {
- netdata_log_error("Value of Key \"%s\" in error response should be string", path);
- return NULL;
- }
- return json_object_get_string(ptr);
-}
-
-static int aclk_parse_otp_error(const char *json_str) {
- int rc = 1;
- json_object *json, *ptr;
- const char *ec;
- const char *ek;
- const char *emsg;
- int block_retry = -1, backoff = -1;
-
-
- json = json_tokener_parse(json_str);
- if (!json) {
- netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
- return 1;
- }
-
- if ((ec = get_json_str_by_path(json, "/" JSON_KEY_EEC)) == NULL)
- goto exit;
-
- if ((ek = get_json_str_by_path(json, "/" JSON_KEY_EMSGKEY)) == NULL)
- goto exit;
-
- if ((emsg = get_json_str_by_path(json, "/" JSON_KEY_EMSG)) == NULL)
- goto exit;
-
- // optional field
- if (!json_pointer_get(json, "/" JSON_KEY_ERTRY, &ptr)) {
- if (json_object_get_type(ptr) != json_type_boolean) {
- netdata_log_error("Error response Key " "/" JSON_KEY_ERTRY " should be of boolean type");
- goto exit;
- }
- block_retry = json_object_get_boolean(ptr);
- }
-
- // optional field
- if (!json_pointer_get(json, "/" JSON_KEY_EDELAY, &ptr)) {
- if (json_object_get_type(ptr) != json_type_int) {
- netdata_log_error("Error response Key " "/" JSON_KEY_EDELAY " should be of integer type");
- goto exit;
- }
- backoff = json_object_get_int(ptr);
- }
-
- if (block_retry > 0)
- aclk_disable_runtime = 1;
-
- if (backoff > 0)
- aclk_block_until = now_monotonic_sec() + backoff;
-
- netdata_log_error("Cloud returned EC=\"%s\", Msg-Key:\"%s\", Msg:\"%s\", BlockRetry:%s, Backoff:%ds (-1 unset by cloud)", ec, ek, emsg, block_retry > 0 ? "true" : "false", backoff);
- rc = 0;
-exit:
- json_object_put(json);
- return rc;
-}
-#else
-static int aclk_parse_otp_error(const char *json_str) {
- int rc = 1;
- int block_retry = -1, backoff = -1;
-
- const char *ec = NULL;
- const char *ek = NULL;
- const char *emsg = NULL;
-
- json_object *json;
- struct json_object_iterator it;
- struct json_object_iterator itEnd;
-
- json = json_tokener_parse(json_str);
- if (!json) {
- netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
- return 1;
- }
-
- it = json_object_iter_begin(json);
- itEnd = json_object_iter_end(json);
-
- while (!json_object_iter_equal(&it, &itEnd)) {
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EMSG)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_EMSG)
-
- emsg = json_object_get_string(json_object_iter_peek_value(&it));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EMSGKEY)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_EMSGKEY)
-
- ek = json_object_get_string(json_object_iter_peek_value(&it));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EEC)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_EEC)
-
- ec = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EDELAY)) {
- if (json_object_get_type(json_object_iter_peek_value(&it)) != json_type_int) {
- netdata_log_error("value of key " JSON_KEY_EDELAY " should be integer");
- goto exit;
- }
-
- backoff = json_object_get_int(json_object_iter_peek_value(&it));
- json_object_iter_next(&it);
- continue;
- }
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_ERTRY)) {
- if (json_object_get_type(json_object_iter_peek_value(&it)) != json_type_boolean) {
- netdata_log_error("value of key " JSON_KEY_ERTRY " should be integer");
- goto exit;
- }
-
- block_retry = json_object_get_boolean(json_object_iter_peek_value(&it));
- json_object_iter_next(&it);
- continue;
- }
- netdata_log_error("Unknown key \"%s\" in error response payload. Ignoring", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
-
- if (block_retry > 0)
- aclk_disable_runtime = 1;
-
- if (backoff > 0)
- aclk_block_until = now_monotonic_sec() + backoff;
-
- netdata_log_error("Cloud returned EC=\"%s\", Msg-Key:\"%s\", Msg:\"%s\", BlockRetry:%s, Backoff:%ds (-1 unset by cloud)", ec, ek, emsg, block_retry > 0 ? "true" : "false", backoff);
- rc = 0;
-exit:
- json_object_put(json);
- return rc;
-}
-#endif
-
-#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110
-static EVP_ENCODE_CTX *EVP_ENCODE_CTX_new(void)
-{
- EVP_ENCODE_CTX *ctx = OPENSSL_malloc(sizeof(*ctx));
-
- if (ctx != NULL) {
- memset(ctx, 0, sizeof(*ctx));
- }
- return ctx;
-}
-static void EVP_ENCODE_CTX_free(EVP_ENCODE_CTX *ctx)
-{
- OPENSSL_free(ctx);
- return;
-}
-#endif
-
-#define CHALLENGE_LEN 256
-#define CHALLENGE_LEN_BASE64 344
-inline static int base64_decode_helper(unsigned char *out, int *outl, const unsigned char *in, int in_len)
-{
- unsigned char remaining_data[CHALLENGE_LEN];
- EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
- EVP_DecodeInit(ctx);
- EVP_DecodeUpdate(ctx, out, outl, in, in_len);
- int remainder = 0;
- EVP_DecodeFinal(ctx, remaining_data, &remainder);
- EVP_ENCODE_CTX_free(ctx);
- if (remainder) {
- netdata_log_error("Unexpected data at EVP_DecodeFinal");
- return 1;
- }
- return 0;
-}
-
-#define OTP_URL_PREFIX "/api/v1/auth/node/"
-int aclk_get_otp_challenge(url_t *target, const char *agent_id, unsigned char **challenge, int *challenge_bytes)
-{
- int rc = 1;
- https_req_t req = HTTPS_REQ_T_INITIALIZER;
- https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
-
- BUFFER *url = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20, &netdata_buffers_statistics.buffers_aclk);
-
- req.host = target->host;
- req.port = target->port;
- buffer_sprintf(url, "%s/node/%s/challenge", target->path, agent_id);
- req.url = (char *)buffer_tostring(url);
-
- if (aclk_https_request(&req, &resp)) {
- netdata_log_error("ACLK_OTP Challenge failed");
- buffer_free(url);
- return 1;
- }
- if (resp.http_code != 200) {
- netdata_log_error("ACLK_OTP Challenge HTTP code not 200 OK (got %d)", resp.http_code);
- buffer_free(url);
- if (resp.payload_size)
- aclk_parse_otp_error(resp.payload);
- goto cleanup_resp;
- }
- buffer_free(url);
-
- netdata_log_info("ACLK_OTP Got Challenge from Cloud");
-
- json_object *json = json_tokener_parse(resp.payload);
- if (!json) {
- netdata_log_error("Couldn't parse HTTP GET challenge payload");
- goto cleanup_resp;
- }
- json_object *challenge_json;
- if (!json_object_object_get_ex(json, "challenge", &challenge_json)) {
- netdata_log_error("No key named \"challenge\" in the returned JSON");
- goto cleanup_json;
- }
- if (!json_object_is_type(challenge_json, json_type_string)) {
- netdata_log_error("\"challenge\" is not a string JSON type");
- goto cleanup_json;
- }
- const char *challenge_base64;
- if (!(challenge_base64 = json_object_get_string(challenge_json))) {
- netdata_log_error("Failed to extract challenge from JSON object");
- goto cleanup_json;
- }
- if (strlen(challenge_base64) != CHALLENGE_LEN_BASE64) {
- netdata_log_error("Received Challenge has unexpected length of %zu (expected %d)", strlen(challenge_base64), CHALLENGE_LEN_BASE64);
- goto cleanup_json;
- }
-
- *challenge = mallocz((CHALLENGE_LEN_BASE64 / 4) * 3);
- base64_decode_helper(*challenge, challenge_bytes, (const unsigned char*)challenge_base64, strlen(challenge_base64));
- if (*challenge_bytes != CHALLENGE_LEN) {
- netdata_log_error("Unexpected challenge length of %d instead of %d", *challenge_bytes, CHALLENGE_LEN);
- freez(*challenge);
- *challenge = NULL;
- goto cleanup_json;
- }
- rc = 0;
-
-cleanup_json:
- json_object_put(json);
-cleanup_resp:
- https_req_response_free(&resp);
- return rc;
-}
-
-int aclk_send_otp_response(const char *agent_id, const unsigned char *response, int response_bytes, url_t *target, struct auth_data *mqtt_auth)
-{
- int len;
- int rc = 1;
- https_req_t req = HTTPS_REQ_T_INITIALIZER;
- https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
-
- req.host = target->host;
- req.port = target->port;
- req.request_type = HTTP_REQ_POST;
-
- unsigned char base64[CHALLENGE_LEN_BASE64 + 1];
- memset(base64, 0, CHALLENGE_LEN_BASE64 + 1);
-
- base64_encode_helper(base64, &len, response, response_bytes);
-
- BUFFER *url = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20, &netdata_buffers_statistics.buffers_aclk);
- BUFFER *resp_json = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20, &netdata_buffers_statistics.buffers_aclk);
-
- buffer_sprintf(url, "%s/node/%s/password", target->path, agent_id);
- buffer_sprintf(resp_json, "{\"response\":\"%s\"}", base64);
-
- req.url = (char *)buffer_tostring(url);
- req.payload = (char *)buffer_tostring(resp_json);
- req.payload_size = strlen(req.payload);
-
- if (aclk_https_request(&req, &resp)) {
- netdata_log_error("ACLK_OTP Password error trying to post result to password");
- goto cleanup_buffers;
- }
- if (resp.http_code != 201) {
- netdata_log_error("ACLK_OTP Password HTTP code not 201 Created (got %d)", resp.http_code);
- if (resp.payload_size)
- aclk_parse_otp_error(resp.payload);
- goto cleanup_response;
- }
- if (resp.payload_size == 0 || resp.payload == NULL) {
- netdata_log_error("ACLK_OTP Password response payload is empty despite returning 201 Created!");
- goto cleanup_response;
- }
- netdata_log_info("ACLK_OTP Got Password from Cloud");
-
- if (parse_passwd_response(resp.payload, mqtt_auth)){
- netdata_log_error("Error parsing response of password endpoint");
- goto cleanup_response;
- }
-
- rc = 0;
-
-cleanup_response:
- https_req_response_free(&resp);
-cleanup_buffers:
- buffer_free(resp_json);
- buffer_free(url);
- return rc;
-}
-
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_300
-static int private_decrypt(EVP_PKEY *p_key, unsigned char * enc_data, int data_len, unsigned char **decrypted)
-#else
-static int private_decrypt(RSA *p_key, unsigned char * enc_data, int data_len, unsigned char **decrypted)
-#endif
-{
- int result;
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_300
- size_t outlen = EVP_PKEY_size(p_key);
- EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(p_key, NULL);
- if (!ctx)
- return 1;
-
- if (EVP_PKEY_decrypt_init(ctx) <= 0) {
- EVP_PKEY_CTX_free(ctx);
- return 1;
- }
-
- if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
- EVP_PKEY_CTX_free(ctx);
- return 1;
- }
-
- *decrypted = mallocz(outlen);
-
- if (EVP_PKEY_decrypt(ctx, *decrypted, &outlen, enc_data, data_len) == 1)
- result = (int) outlen;
- else
- result = -1;
-
- EVP_PKEY_CTX_free(ctx);
-#else
- *decrypted = mallocz(RSA_size(p_key));
- result = RSA_private_decrypt(data_len, enc_data, *decrypted, p_key, RSA_PKCS1_OAEP_PADDING);
-#endif
- if (result == -1)
- {
- char err[512];
- ERR_error_string_n(ERR_get_error(), err, sizeof(err));
- netdata_log_error("Decryption of the challenge failed: %s", err);
- }
- return result;
-}
-
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_300
-int aclk_get_mqtt_otp(EVP_PKEY *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target)
-#else
-int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target)
-#endif
-{
- unsigned char *challenge = NULL;
- int challenge_bytes;
-
- char *agent_id = get_agent_claimid();
- if (agent_id == NULL) {
- netdata_log_error("Agent was not claimed - cannot perform challenge/response");
- return 1;
- }
-
- // Get Challenge
- if (aclk_get_otp_challenge(target, agent_id, &challenge, &challenge_bytes)) {
- netdata_log_error("Error getting challenge");
- freez(agent_id);
- return 1;
- }
-
- // Decrypt Challenge / Get response
- unsigned char *response_plaintext = NULL;
- int response_plaintext_bytes = private_decrypt(p_key, challenge, challenge_bytes, &response_plaintext);
- if (response_plaintext_bytes < 0) {
- netdata_log_error("Couldn't decrypt the challenge received");
- freez(response_plaintext);
- freez(challenge);
- freez(agent_id);
- return 1;
- }
- freez(challenge);
-
- // Encode and Send Challenge
- struct auth_data data = { .client_id = NULL, .passwd = NULL, .username = NULL };
- if (aclk_send_otp_response(agent_id, response_plaintext, response_plaintext_bytes, target, &data)) {
- netdata_log_error("Error getting response");
- freez(response_plaintext);
- freez(agent_id);
- return 1;
- }
-
- *mqtt_pass = data.passwd;
- *mqtt_usr = data.username;
- *mqtt_id = data.client_id;
-
- freez(response_plaintext);
- freez(agent_id);
- return 0;
-}
-
-#define JSON_KEY_ENC "encoding"
-#define JSON_KEY_AUTH_ENDPOINT "authEndpoint"
-#define JSON_KEY_TRP "transports"
-#define JSON_KEY_TRP_TYPE "type"
-#define JSON_KEY_TRP_ENDPOINT "endpoint"
-#define JSON_KEY_BACKOFF "backoff"
-#define JSON_KEY_BACKOFF_BASE "base"
-#define JSON_KEY_BACKOFF_MAX "maxSeconds"
-#define JSON_KEY_BACKOFF_MIN "minSeconds"
-#define JSON_KEY_CAPS "capabilities"
-
-static int parse_json_env_transport(json_object *json, aclk_transport_desc_t *trp) {
- struct json_object_iterator it;
- struct json_object_iterator itEnd;
-
- it = json_object_iter_begin(json);
- itEnd = json_object_iter_end(json);
-
- while (!json_object_iter_equal(&it, &itEnd)) {
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TRP_TYPE)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_TRP_TYPE)
- if (trp->type != ACLK_TRP_UNKNOWN) {
- netdata_log_error(JSON_KEY_TRP_TYPE " set already");
- goto exit;
- }
- trp->type = aclk_transport_type_t_from_str(json_object_get_string(json_object_iter_peek_value(&it)));
- if (trp->type == ACLK_TRP_UNKNOWN) {
- netdata_log_error(JSON_KEY_TRP_TYPE " unknown type \"%s\"", json_object_get_string(json_object_iter_peek_value(&it)));
- goto exit;
- }
- json_object_iter_next(&it);
- continue;
- }
-
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TRP_ENDPOINT)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_TRP_ENDPOINT)
- if (trp->endpoint) {
- netdata_log_error(JSON_KEY_TRP_ENDPOINT " set already");
- goto exit;
- }
- trp->endpoint = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
-
- netdata_log_error("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
-
- if (!trp->endpoint) {
- netdata_log_error(JSON_KEY_TRP_ENDPOINT " is missing from JSON dictionary");
- goto exit;
- }
-
- if (trp->type == ACLK_TRP_UNKNOWN) {
- netdata_log_error("transport type not set");
- goto exit;
- }
-
- return 0;
-
-exit:
- aclk_transport_desc_t_destroy(trp);
- return 1;
-}
-
-static int parse_json_env_transports(json_object *json_array, aclk_env_t *env) {
- aclk_transport_desc_t *trp;
- json_object *obj;
-
- if (env->transports) {
- netdata_log_error("transports have been set already");
- return 1;
- }
-
- env->transport_count = json_object_array_length(json_array);
-
- env->transports = callocz(env->transport_count , sizeof(aclk_transport_desc_t *));
-
- for (size_t i = 0; i < env->transport_count; i++) {
- trp = callocz(1, sizeof(aclk_transport_desc_t));
- obj = json_object_array_get_idx(json_array, i);
- if (parse_json_env_transport(obj, trp)) {
- netdata_log_error("error parsing transport idx %d", (int)i);
- freez(trp);
- return 1;
- }
- env->transports[i] = trp;
- }
-
- return 0;
-}
-
-#define MATCHED_CORRECT 1
-#define MATCHED_ERROR -1
-#define NOT_MATCHED 0
-static int parse_json_backoff_int(struct json_object_iterator *it, int *out, const char* name, int min, int max) {
- if (!strcmp(json_object_iter_peek_name(it), name)) {
- if (json_object_get_type(json_object_iter_peek_value(it)) != json_type_int) {
- netdata_log_error("Could not parse \"%s\". Not an integer as expected.", name);
- return MATCHED_ERROR;
- }
-
- *out = json_object_get_int(json_object_iter_peek_value(it));
-
- if (*out < min || *out > max) {
- netdata_log_error("Value of \"%s\"=%d out of range (%d-%d).", name, *out, min, max);
- return MATCHED_ERROR;
- }
-
- return MATCHED_CORRECT;
- }
- return NOT_MATCHED;
-}
-
-static int parse_json_backoff(json_object *json, aclk_backoff_t *backoff) {
- struct json_object_iterator it;
- struct json_object_iterator itEnd;
- int ret;
-
- it = json_object_iter_begin(json);
- itEnd = json_object_iter_end(json);
-
- while (!json_object_iter_equal(&it, &itEnd)) {
- if ( (ret = parse_json_backoff_int(&it, &backoff->base, JSON_KEY_BACKOFF_BASE, 1, 10)) ) {
- if (ret == MATCHED_ERROR) {
- return 1;
- }
- json_object_iter_next(&it);
- continue;
- }
-
- if ( (ret = parse_json_backoff_int(&it, &backoff->max_s, JSON_KEY_BACKOFF_MAX, 500, INT_MAX)) ) {
- if (ret == MATCHED_ERROR) {
- return 1;
- }
- json_object_iter_next(&it);
- continue;
- }
-
- if ( (ret = parse_json_backoff_int(&it, &backoff->min_s, JSON_KEY_BACKOFF_MIN, 0, INT_MAX)) ) {
- if (ret == MATCHED_ERROR) {
- return 1;
- }
- json_object_iter_next(&it);
- continue;
- }
-
- netdata_log_error("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
-
- return 0;
-}
-
-static int parse_json_env_caps(json_object *json, aclk_env_t *env) {
- json_object *obj;
- const char *str;
-
- if (env->capabilities) {
- netdata_log_error("transports have been set already");
- return 1;
- }
-
- env->capability_count = json_object_array_length(json);
-
- // empty capabilities list is allowed
- if (!env->capability_count)
- return 0;
-
- env->capabilities = callocz(env->capability_count , sizeof(char *));
-
- for (size_t i = 0; i < env->capability_count; i++) {
- obj = json_object_array_get_idx(json, i);
- if (json_object_get_type(obj) != json_type_string) {
- netdata_log_error("Capability at index %d not a string!", (int)i);
- return 1;
- }
- str = json_object_get_string(obj);
- if (!str) {
- netdata_log_error("Error parsing capabilities");
- return 1;
- }
- env->capabilities[i] = strdupz(str);
- }
-
- return 0;
-}
-
-static int parse_json_env(const char *json_str, aclk_env_t *env) {
- json_object *json;
- struct json_object_iterator it;
- struct json_object_iterator itEnd;
-
- json = json_tokener_parse(json_str);
- if (!json) {
- netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
- return 1;
- }
-
- it = json_object_iter_begin(json);
- itEnd = json_object_iter_end(json);
-
- while (!json_object_iter_equal(&it, &itEnd)) {
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_AUTH_ENDPOINT)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_AUTH_ENDPOINT)
- if (env->auth_endpoint) {
- netdata_log_error("authEndpoint set already");
- goto exit;
- }
- env->auth_endpoint = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
-
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_ENC)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_ENC)
- if (env->encoding != ACLK_ENC_UNKNOWN) {
- netdata_log_error(JSON_KEY_ENC " set already");
- goto exit;
- }
- env->encoding = aclk_encoding_type_t_from_str(json_object_get_string(json_object_iter_peek_value(&it)));
- json_object_iter_next(&it);
- continue;
- }
-
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TRP)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_TRP)
-
- json_object *now = json_object_iter_peek_value(&it);
- parse_json_env_transports(now, env);
-
- json_object_iter_next(&it);
- continue;
- }
-
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_BACKOFF)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_object, JSON_KEY_BACKOFF)
-
- if (parse_json_backoff(json_object_iter_peek_value(&it), &env->backoff)) {
- env->backoff.base = 0;
- netdata_log_error("Error parsing Backoff parameters in env");
- goto exit;
- }
-
- json_object_iter_next(&it);
- continue;
- }
-
- if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_CAPS)) {
- PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_CAPS)
-
- if (parse_json_env_caps(json_object_iter_peek_value(&it), env)) {
- netdata_log_error("Error parsing capabilities list");
- goto exit;
- }
-
- json_object_iter_next(&it);
- continue;
- }
-
- netdata_log_error("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
-
- // Check all compulsory keys have been set
- if (env->transport_count < 1) {
- netdata_log_error("env has to return at least one transport");
- goto exit;
- }
- if (!env->auth_endpoint) {
- netdata_log_error(JSON_KEY_AUTH_ENDPOINT " is compulsory");
- goto exit;
- }
- if (env->encoding == ACLK_ENC_UNKNOWN) {
- netdata_log_error(JSON_KEY_ENC " is compulsory");
- goto exit;
- }
- if (!env->backoff.base) {
- netdata_log_error(JSON_KEY_BACKOFF " is compulsory");
- goto exit;
- }
-
- json_object_put(json);
- return 0;
-
-exit:
- aclk_env_t_destroy(env);
- json_object_put(json);
- return 1;
-}
-
-int aclk_get_env(aclk_env_t *env, const char* aclk_hostname, int aclk_port) {
- BUFFER *buf = buffer_create(1024, &netdata_buffers_statistics.buffers_aclk);
-
- https_req_t req = HTTPS_REQ_T_INITIALIZER;
- https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
-
- req.request_type = HTTP_REQ_GET;
-
- char *agent_id = get_agent_claimid();
- if (agent_id == NULL)
- {
- netdata_log_error("Agent was not claimed - cannot perform challenge/response");
- buffer_free(buf);
- return 1;
- }
-
- buffer_sprintf(buf, "/api/v1/env?v=%s&cap=proto,ctx&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id);
-
- freez(agent_id);
-
- req.host = (char*)aclk_hostname;
- req.port = aclk_port;
- req.url = buf->buffer;
- if (aclk_https_request(&req, &resp)) {
- netdata_log_error("Error trying to contact env endpoint");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 2;
- }
- if (resp.http_code != 200) {
- netdata_log_error("The HTTP code not 200 OK (Got %d)", resp.http_code);
- if (resp.payload_size)
- aclk_parse_otp_error(resp.payload);
- https_req_response_free(&resp);
- buffer_free(buf);
- return 3;
- }
-
- if (!resp.payload || !resp.payload_size) {
- netdata_log_error("Unexpected empty payload as response to /env call");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 4;
- }
-
- if (parse_json_env(resp.payload, env)) {
- netdata_log_error("error parsing /env message");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 5;
- }
-
- netdata_log_info("Getting Cloud /env successful");
-
- https_req_response_free(&resp);
- buffer_free(buf);
- return 0;
-}