aclk_otp.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "aclk_otp.h"
  3. #include "aclk_util.h"
  4. #include "aclk.h"
  5. #include "daemon/common.h"
  6. #include "mqtt_websockets/c-rbuf/include/ringbuffer.h"
  7. static int aclk_https_request(https_req_t *request, https_req_response_t *response) {
  8. int rc;
  9. // wrapper for ACLK only which loads ACLK specific proxy settings
  10. // then only calls https_request
  11. struct mqtt_wss_proxy proxy_conf = { .host = NULL, .port = 0, .username = NULL, .password = NULL, .type = MQTT_WSS_DIRECT };
  12. aclk_set_proxy((char**)&proxy_conf.host, &proxy_conf.port, (char**)&proxy_conf.username, (char**)&proxy_conf.password, &proxy_conf.type);
  13. if (proxy_conf.type == MQTT_WSS_PROXY_HTTP) {
  14. request->proxy_host = (char*)proxy_conf.host; // TODO make it const as well
  15. request->proxy_port = proxy_conf.port;
  16. request->proxy_username = proxy_conf.username;
  17. request->proxy_password = proxy_conf.password;
  18. }
  19. rc = https_request(request, response);
  20. freez((char*)proxy_conf.host);
  21. freez((char*)proxy_conf.username);
  22. freez((char*)proxy_conf.password);
  23. return rc;
  24. }
  25. struct auth_data {
  26. char *client_id;
  27. char *username;
  28. char *passwd;
  29. };
  30. #define PARSE_ENV_JSON_CHK_TYPE(it, type, name) \
  31. if (json_object_get_type(json_object_iter_peek_value(it)) != type) { \
  32. netdata_log_error("value of key \"%s\" should be %s", name, #type); \
  33. goto exit; \
  34. }
  35. #define JSON_KEY_CLIENTID "clientID"
  36. #define JSON_KEY_USER "username"
  37. #define JSON_KEY_PASS "password"
  38. #define JSON_KEY_TOPICS "topics"
  39. static int parse_passwd_response(const char *json_str, struct auth_data *auth) {
  40. int rc = 1;
  41. json_object *json;
  42. struct json_object_iterator it;
  43. struct json_object_iterator itEnd;
  44. json = json_tokener_parse(json_str);
  45. if (!json) {
  46. netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
  47. return 1;
  48. }
  49. it = json_object_iter_begin(json);
  50. itEnd = json_object_iter_end(json);
  51. while (!json_object_iter_equal(&it, &itEnd)) {
  52. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_CLIENTID)) {
  53. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_CLIENTID)
  54. auth->client_id = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
  55. json_object_iter_next(&it);
  56. continue;
  57. }
  58. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_USER)) {
  59. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_USER)
  60. auth->username = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
  61. json_object_iter_next(&it);
  62. continue;
  63. }
  64. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_PASS)) {
  65. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_PASS)
  66. auth->passwd = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
  67. json_object_iter_next(&it);
  68. continue;
  69. }
  70. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TOPICS)) {
  71. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_TOPICS)
  72. if (aclk_generate_topic_cache(json_object_iter_peek_value(&it))) {
  73. netdata_log_error("Failed to generate topic cache!");
  74. goto exit;
  75. }
  76. json_object_iter_next(&it);
  77. continue;
  78. }
  79. netdata_log_error("Unknown key \"%s\" in passwd response payload. Ignoring", json_object_iter_peek_name(&it));
  80. json_object_iter_next(&it);
  81. }
  82. if (!auth->client_id) {
  83. netdata_log_error(JSON_KEY_CLIENTID " is compulsory key in /password response");
  84. goto exit;
  85. }
  86. if (!auth->passwd) {
  87. netdata_log_error(JSON_KEY_PASS " is compulsory in /password response");
  88. goto exit;
  89. }
  90. if (!auth->username) {
  91. netdata_log_error(JSON_KEY_USER " is compulsory in /password response");
  92. goto exit;
  93. }
  94. rc = 0;
  95. exit:
  96. json_object_put(json);
  97. return rc;
  98. }
  99. #define JSON_KEY_ERTRY "errorNonRetryable"
  100. #define JSON_KEY_EDELAY "errorRetryDelaySeconds"
  101. #define JSON_KEY_EEC "errorCode"
  102. #define JSON_KEY_EMSGKEY "errorMsgKey"
  103. #define JSON_KEY_EMSG "errorMessage"
  104. #if JSON_C_MINOR_VERSION >= 13
  105. static const char *get_json_str_by_path(json_object *json, const char *path) {
  106. json_object *ptr;
  107. if (json_pointer_get(json, path, &ptr)) {
  108. netdata_log_error("Missing compulsory key \"%s\" in error response", path);
  109. return NULL;
  110. }
  111. if (json_object_get_type(ptr) != json_type_string) {
  112. netdata_log_error("Value of Key \"%s\" in error response should be string", path);
  113. return NULL;
  114. }
  115. return json_object_get_string(ptr);
  116. }
  117. static int aclk_parse_otp_error(const char *json_str) {
  118. int rc = 1;
  119. json_object *json, *ptr;
  120. const char *ec;
  121. const char *ek;
  122. const char *emsg;
  123. int block_retry = -1, backoff = -1;
  124. json = json_tokener_parse(json_str);
  125. if (!json) {
  126. netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
  127. return 1;
  128. }
  129. if ((ec = get_json_str_by_path(json, "/" JSON_KEY_EEC)) == NULL)
  130. goto exit;
  131. if ((ek = get_json_str_by_path(json, "/" JSON_KEY_EMSGKEY)) == NULL)
  132. goto exit;
  133. if ((emsg = get_json_str_by_path(json, "/" JSON_KEY_EMSG)) == NULL)
  134. goto exit;
  135. // optional field
  136. if (!json_pointer_get(json, "/" JSON_KEY_ERTRY, &ptr)) {
  137. if (json_object_get_type(ptr) != json_type_boolean) {
  138. netdata_log_error("Error response Key " "/" JSON_KEY_ERTRY " should be of boolean type");
  139. goto exit;
  140. }
  141. block_retry = json_object_get_boolean(ptr);
  142. }
  143. // optional field
  144. if (!json_pointer_get(json, "/" JSON_KEY_EDELAY, &ptr)) {
  145. if (json_object_get_type(ptr) != json_type_int) {
  146. netdata_log_error("Error response Key " "/" JSON_KEY_EDELAY " should be of integer type");
  147. goto exit;
  148. }
  149. backoff = json_object_get_int(ptr);
  150. }
  151. if (block_retry > 0)
  152. aclk_disable_runtime = 1;
  153. if (backoff > 0)
  154. aclk_block_until = now_monotonic_sec() + backoff;
  155. 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);
  156. rc = 0;
  157. exit:
  158. json_object_put(json);
  159. return rc;
  160. }
  161. #else
  162. static int aclk_parse_otp_error(const char *json_str) {
  163. int rc = 1;
  164. int block_retry = -1, backoff = -1;
  165. const char *ec = NULL;
  166. const char *ek = NULL;
  167. const char *emsg = NULL;
  168. json_object *json;
  169. struct json_object_iterator it;
  170. struct json_object_iterator itEnd;
  171. json = json_tokener_parse(json_str);
  172. if (!json) {
  173. netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
  174. return 1;
  175. }
  176. it = json_object_iter_begin(json);
  177. itEnd = json_object_iter_end(json);
  178. while (!json_object_iter_equal(&it, &itEnd)) {
  179. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EMSG)) {
  180. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_EMSG)
  181. emsg = json_object_get_string(json_object_iter_peek_value(&it));
  182. json_object_iter_next(&it);
  183. continue;
  184. }
  185. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EMSGKEY)) {
  186. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_EMSGKEY)
  187. ek = json_object_get_string(json_object_iter_peek_value(&it));
  188. json_object_iter_next(&it);
  189. continue;
  190. }
  191. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EEC)) {
  192. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_EEC)
  193. ec = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
  194. json_object_iter_next(&it);
  195. continue;
  196. }
  197. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_EDELAY)) {
  198. if (json_object_get_type(json_object_iter_peek_value(&it)) != json_type_int) {
  199. netdata_log_error("value of key " JSON_KEY_EDELAY " should be integer");
  200. goto exit;
  201. }
  202. backoff = json_object_get_int(json_object_iter_peek_value(&it));
  203. json_object_iter_next(&it);
  204. continue;
  205. }
  206. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_ERTRY)) {
  207. if (json_object_get_type(json_object_iter_peek_value(&it)) != json_type_boolean) {
  208. netdata_log_error("value of key " JSON_KEY_ERTRY " should be integer");
  209. goto exit;
  210. }
  211. block_retry = json_object_get_boolean(json_object_iter_peek_value(&it));
  212. json_object_iter_next(&it);
  213. continue;
  214. }
  215. netdata_log_error("Unknown key \"%s\" in error response payload. Ignoring", json_object_iter_peek_name(&it));
  216. json_object_iter_next(&it);
  217. }
  218. if (block_retry > 0)
  219. aclk_disable_runtime = 1;
  220. if (backoff > 0)
  221. aclk_block_until = now_monotonic_sec() + backoff;
  222. 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);
  223. rc = 0;
  224. exit:
  225. json_object_put(json);
  226. return rc;
  227. }
  228. #endif
  229. #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110
  230. static EVP_ENCODE_CTX *EVP_ENCODE_CTX_new(void)
  231. {
  232. EVP_ENCODE_CTX *ctx = OPENSSL_malloc(sizeof(*ctx));
  233. if (ctx != NULL) {
  234. memset(ctx, 0, sizeof(*ctx));
  235. }
  236. return ctx;
  237. }
  238. static void EVP_ENCODE_CTX_free(EVP_ENCODE_CTX *ctx)
  239. {
  240. OPENSSL_free(ctx);
  241. return;
  242. }
  243. #endif
  244. #define CHALLENGE_LEN 256
  245. #define CHALLENGE_LEN_BASE64 344
  246. inline static int base64_decode_helper(unsigned char *out, int *outl, const unsigned char *in, int in_len)
  247. {
  248. unsigned char remaining_data[CHALLENGE_LEN];
  249. EVP_ENCODE_CTX *ctx = EVP_ENCODE_CTX_new();
  250. EVP_DecodeInit(ctx);
  251. EVP_DecodeUpdate(ctx, out, outl, in, in_len);
  252. int remainder = 0;
  253. EVP_DecodeFinal(ctx, remaining_data, &remainder);
  254. EVP_ENCODE_CTX_free(ctx);
  255. if (remainder) {
  256. netdata_log_error("Unexpected data at EVP_DecodeFinal");
  257. return 1;
  258. }
  259. return 0;
  260. }
  261. #define OTP_URL_PREFIX "/api/v1/auth/node/"
  262. int aclk_get_otp_challenge(url_t *target, const char *agent_id, unsigned char **challenge, int *challenge_bytes)
  263. {
  264. int rc = 1;
  265. https_req_t req = HTTPS_REQ_T_INITIALIZER;
  266. https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
  267. BUFFER *url = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20, &netdata_buffers_statistics.buffers_aclk);
  268. req.host = target->host;
  269. req.port = target->port;
  270. buffer_sprintf(url, "%s/node/%s/challenge", target->path, agent_id);
  271. req.url = (char *)buffer_tostring(url);
  272. if (aclk_https_request(&req, &resp)) {
  273. netdata_log_error("ACLK_OTP Challenge failed");
  274. buffer_free(url);
  275. return 1;
  276. }
  277. if (resp.http_code != 200) {
  278. netdata_log_error("ACLK_OTP Challenge HTTP code not 200 OK (got %d)", resp.http_code);
  279. buffer_free(url);
  280. if (resp.payload_size)
  281. aclk_parse_otp_error(resp.payload);
  282. goto cleanup_resp;
  283. }
  284. buffer_free(url);
  285. netdata_log_info("ACLK_OTP Got Challenge from Cloud");
  286. json_object *json = json_tokener_parse(resp.payload);
  287. if (!json) {
  288. netdata_log_error("Couldn't parse HTTP GET challenge payload");
  289. goto cleanup_resp;
  290. }
  291. json_object *challenge_json;
  292. if (!json_object_object_get_ex(json, "challenge", &challenge_json)) {
  293. netdata_log_error("No key named \"challenge\" in the returned JSON");
  294. goto cleanup_json;
  295. }
  296. if (!json_object_is_type(challenge_json, json_type_string)) {
  297. netdata_log_error("\"challenge\" is not a string JSON type");
  298. goto cleanup_json;
  299. }
  300. const char *challenge_base64;
  301. if (!(challenge_base64 = json_object_get_string(challenge_json))) {
  302. netdata_log_error("Failed to extract challenge from JSON object");
  303. goto cleanup_json;
  304. }
  305. if (strlen(challenge_base64) != CHALLENGE_LEN_BASE64) {
  306. netdata_log_error("Received Challenge has unexpected length of %zu (expected %d)", strlen(challenge_base64), CHALLENGE_LEN_BASE64);
  307. goto cleanup_json;
  308. }
  309. *challenge = mallocz((CHALLENGE_LEN_BASE64 / 4) * 3);
  310. base64_decode_helper(*challenge, challenge_bytes, (const unsigned char*)challenge_base64, strlen(challenge_base64));
  311. if (*challenge_bytes != CHALLENGE_LEN) {
  312. netdata_log_error("Unexpected challenge length of %d instead of %d", *challenge_bytes, CHALLENGE_LEN);
  313. freez(*challenge);
  314. *challenge = NULL;
  315. goto cleanup_json;
  316. }
  317. rc = 0;
  318. cleanup_json:
  319. json_object_put(json);
  320. cleanup_resp:
  321. https_req_response_free(&resp);
  322. return rc;
  323. }
  324. int aclk_send_otp_response(const char *agent_id, const unsigned char *response, int response_bytes, url_t *target, struct auth_data *mqtt_auth)
  325. {
  326. int len;
  327. int rc = 1;
  328. https_req_t req = HTTPS_REQ_T_INITIALIZER;
  329. https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
  330. req.host = target->host;
  331. req.port = target->port;
  332. req.request_type = HTTP_REQ_POST;
  333. unsigned char base64[CHALLENGE_LEN_BASE64 + 1];
  334. memset(base64, 0, CHALLENGE_LEN_BASE64 + 1);
  335. base64_encode_helper(base64, &len, response, response_bytes);
  336. BUFFER *url = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20, &netdata_buffers_statistics.buffers_aclk);
  337. BUFFER *resp_json = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20, &netdata_buffers_statistics.buffers_aclk);
  338. buffer_sprintf(url, "%s/node/%s/password", target->path, agent_id);
  339. buffer_sprintf(resp_json, "{\"response\":\"%s\"}", base64);
  340. req.url = (char *)buffer_tostring(url);
  341. req.payload = (char *)buffer_tostring(resp_json);
  342. req.payload_size = strlen(req.payload);
  343. if (aclk_https_request(&req, &resp)) {
  344. netdata_log_error("ACLK_OTP Password error trying to post result to password");
  345. goto cleanup_buffers;
  346. }
  347. if (resp.http_code != 201) {
  348. netdata_log_error("ACLK_OTP Password HTTP code not 201 Created (got %d)", resp.http_code);
  349. if (resp.payload_size)
  350. aclk_parse_otp_error(resp.payload);
  351. goto cleanup_response;
  352. }
  353. if (resp.payload_size == 0 || resp.payload == NULL) {
  354. netdata_log_error("ACLK_OTP Password response payload is empty despite returning 201 Created!");
  355. goto cleanup_response;
  356. }
  357. netdata_log_info("ACLK_OTP Got Password from Cloud");
  358. if (parse_passwd_response(resp.payload, mqtt_auth)){
  359. netdata_log_error("Error parsing response of password endpoint");
  360. goto cleanup_response;
  361. }
  362. rc = 0;
  363. cleanup_response:
  364. https_req_response_free(&resp);
  365. cleanup_buffers:
  366. buffer_free(resp_json);
  367. buffer_free(url);
  368. return rc;
  369. }
  370. #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_300
  371. static int private_decrypt(EVP_PKEY *p_key, unsigned char * enc_data, int data_len, unsigned char **decrypted)
  372. #else
  373. static int private_decrypt(RSA *p_key, unsigned char * enc_data, int data_len, unsigned char **decrypted)
  374. #endif
  375. {
  376. int result;
  377. #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_300
  378. size_t outlen = EVP_PKEY_size(p_key);
  379. EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(p_key, NULL);
  380. if (!ctx)
  381. return 1;
  382. if (EVP_PKEY_decrypt_init(ctx) <= 0) {
  383. EVP_PKEY_CTX_free(ctx);
  384. return 1;
  385. }
  386. if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
  387. EVP_PKEY_CTX_free(ctx);
  388. return 1;
  389. }
  390. *decrypted = mallocz(outlen);
  391. if (EVP_PKEY_decrypt(ctx, *decrypted, &outlen, enc_data, data_len) == 1)
  392. result = (int) outlen;
  393. else
  394. result = -1;
  395. EVP_PKEY_CTX_free(ctx);
  396. #else
  397. *decrypted = mallocz(RSA_size(p_key));
  398. result = RSA_private_decrypt(data_len, enc_data, *decrypted, p_key, RSA_PKCS1_OAEP_PADDING);
  399. #endif
  400. if (result == -1)
  401. {
  402. char err[512];
  403. ERR_error_string_n(ERR_get_error(), err, sizeof(err));
  404. netdata_log_error("Decryption of the challenge failed: %s", err);
  405. }
  406. return result;
  407. }
  408. #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_300
  409. int aclk_get_mqtt_otp(EVP_PKEY *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target)
  410. #else
  411. int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target)
  412. #endif
  413. {
  414. unsigned char *challenge = NULL;
  415. int challenge_bytes;
  416. char *agent_id = get_agent_claimid();
  417. if (agent_id == NULL) {
  418. netdata_log_error("Agent was not claimed - cannot perform challenge/response");
  419. return 1;
  420. }
  421. // Get Challenge
  422. if (aclk_get_otp_challenge(target, agent_id, &challenge, &challenge_bytes)) {
  423. netdata_log_error("Error getting challenge");
  424. freez(agent_id);
  425. return 1;
  426. }
  427. // Decrypt Challenge / Get response
  428. unsigned char *response_plaintext = NULL;
  429. int response_plaintext_bytes = private_decrypt(p_key, challenge, challenge_bytes, &response_plaintext);
  430. if (response_plaintext_bytes < 0) {
  431. netdata_log_error("Couldn't decrypt the challenge received");
  432. freez(response_plaintext);
  433. freez(challenge);
  434. freez(agent_id);
  435. return 1;
  436. }
  437. freez(challenge);
  438. // Encode and Send Challenge
  439. struct auth_data data = { .client_id = NULL, .passwd = NULL, .username = NULL };
  440. if (aclk_send_otp_response(agent_id, response_plaintext, response_plaintext_bytes, target, &data)) {
  441. netdata_log_error("Error getting response");
  442. freez(response_plaintext);
  443. freez(agent_id);
  444. return 1;
  445. }
  446. *mqtt_pass = data.passwd;
  447. *mqtt_usr = data.username;
  448. *mqtt_id = data.client_id;
  449. freez(response_plaintext);
  450. freez(agent_id);
  451. return 0;
  452. }
  453. #define JSON_KEY_ENC "encoding"
  454. #define JSON_KEY_AUTH_ENDPOINT "authEndpoint"
  455. #define JSON_KEY_TRP "transports"
  456. #define JSON_KEY_TRP_TYPE "type"
  457. #define JSON_KEY_TRP_ENDPOINT "endpoint"
  458. #define JSON_KEY_BACKOFF "backoff"
  459. #define JSON_KEY_BACKOFF_BASE "base"
  460. #define JSON_KEY_BACKOFF_MAX "maxSeconds"
  461. #define JSON_KEY_BACKOFF_MIN "minSeconds"
  462. #define JSON_KEY_CAPS "capabilities"
  463. static int parse_json_env_transport(json_object *json, aclk_transport_desc_t *trp) {
  464. struct json_object_iterator it;
  465. struct json_object_iterator itEnd;
  466. it = json_object_iter_begin(json);
  467. itEnd = json_object_iter_end(json);
  468. while (!json_object_iter_equal(&it, &itEnd)) {
  469. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TRP_TYPE)) {
  470. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_TRP_TYPE)
  471. if (trp->type != ACLK_TRP_UNKNOWN) {
  472. netdata_log_error(JSON_KEY_TRP_TYPE " set already");
  473. goto exit;
  474. }
  475. trp->type = aclk_transport_type_t_from_str(json_object_get_string(json_object_iter_peek_value(&it)));
  476. if (trp->type == ACLK_TRP_UNKNOWN) {
  477. netdata_log_error(JSON_KEY_TRP_TYPE " unknown type \"%s\"", json_object_get_string(json_object_iter_peek_value(&it)));
  478. goto exit;
  479. }
  480. json_object_iter_next(&it);
  481. continue;
  482. }
  483. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TRP_ENDPOINT)) {
  484. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_TRP_ENDPOINT)
  485. if (trp->endpoint) {
  486. netdata_log_error(JSON_KEY_TRP_ENDPOINT " set already");
  487. goto exit;
  488. }
  489. trp->endpoint = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
  490. json_object_iter_next(&it);
  491. continue;
  492. }
  493. netdata_log_error("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
  494. json_object_iter_next(&it);
  495. }
  496. if (!trp->endpoint) {
  497. netdata_log_error(JSON_KEY_TRP_ENDPOINT " is missing from JSON dictionary");
  498. goto exit;
  499. }
  500. if (trp->type == ACLK_TRP_UNKNOWN) {
  501. netdata_log_error("transport type not set");
  502. goto exit;
  503. }
  504. return 0;
  505. exit:
  506. aclk_transport_desc_t_destroy(trp);
  507. return 1;
  508. }
  509. static int parse_json_env_transports(json_object *json_array, aclk_env_t *env) {
  510. aclk_transport_desc_t *trp;
  511. json_object *obj;
  512. if (env->transports) {
  513. netdata_log_error("transports have been set already");
  514. return 1;
  515. }
  516. env->transport_count = json_object_array_length(json_array);
  517. env->transports = callocz(env->transport_count , sizeof(aclk_transport_desc_t *));
  518. for (size_t i = 0; i < env->transport_count; i++) {
  519. trp = callocz(1, sizeof(aclk_transport_desc_t));
  520. obj = json_object_array_get_idx(json_array, i);
  521. if (parse_json_env_transport(obj, trp)) {
  522. netdata_log_error("error parsing transport idx %d", (int)i);
  523. freez(trp);
  524. return 1;
  525. }
  526. env->transports[i] = trp;
  527. }
  528. return 0;
  529. }
  530. #define MATCHED_CORRECT 1
  531. #define MATCHED_ERROR -1
  532. #define NOT_MATCHED 0
  533. static int parse_json_backoff_int(struct json_object_iterator *it, int *out, const char* name, int min, int max) {
  534. if (!strcmp(json_object_iter_peek_name(it), name)) {
  535. if (json_object_get_type(json_object_iter_peek_value(it)) != json_type_int) {
  536. netdata_log_error("Could not parse \"%s\". Not an integer as expected.", name);
  537. return MATCHED_ERROR;
  538. }
  539. *out = json_object_get_int(json_object_iter_peek_value(it));
  540. if (*out < min || *out > max) {
  541. netdata_log_error("Value of \"%s\"=%d out of range (%d-%d).", name, *out, min, max);
  542. return MATCHED_ERROR;
  543. }
  544. return MATCHED_CORRECT;
  545. }
  546. return NOT_MATCHED;
  547. }
  548. static int parse_json_backoff(json_object *json, aclk_backoff_t *backoff) {
  549. struct json_object_iterator it;
  550. struct json_object_iterator itEnd;
  551. int ret;
  552. it = json_object_iter_begin(json);
  553. itEnd = json_object_iter_end(json);
  554. while (!json_object_iter_equal(&it, &itEnd)) {
  555. if ( (ret = parse_json_backoff_int(&it, &backoff->base, JSON_KEY_BACKOFF_BASE, 1, 10)) ) {
  556. if (ret == MATCHED_ERROR) {
  557. return 1;
  558. }
  559. json_object_iter_next(&it);
  560. continue;
  561. }
  562. if ( (ret = parse_json_backoff_int(&it, &backoff->max_s, JSON_KEY_BACKOFF_MAX, 500, INT_MAX)) ) {
  563. if (ret == MATCHED_ERROR) {
  564. return 1;
  565. }
  566. json_object_iter_next(&it);
  567. continue;
  568. }
  569. if ( (ret = parse_json_backoff_int(&it, &backoff->min_s, JSON_KEY_BACKOFF_MIN, 0, INT_MAX)) ) {
  570. if (ret == MATCHED_ERROR) {
  571. return 1;
  572. }
  573. json_object_iter_next(&it);
  574. continue;
  575. }
  576. netdata_log_error("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
  577. json_object_iter_next(&it);
  578. }
  579. return 0;
  580. }
  581. static int parse_json_env_caps(json_object *json, aclk_env_t *env) {
  582. json_object *obj;
  583. const char *str;
  584. if (env->capabilities) {
  585. netdata_log_error("transports have been set already");
  586. return 1;
  587. }
  588. env->capability_count = json_object_array_length(json);
  589. // empty capabilities list is allowed
  590. if (!env->capability_count)
  591. return 0;
  592. env->capabilities = callocz(env->capability_count , sizeof(char *));
  593. for (size_t i = 0; i < env->capability_count; i++) {
  594. obj = json_object_array_get_idx(json, i);
  595. if (json_object_get_type(obj) != json_type_string) {
  596. netdata_log_error("Capability at index %d not a string!", (int)i);
  597. return 1;
  598. }
  599. str = json_object_get_string(obj);
  600. if (!str) {
  601. netdata_log_error("Error parsing capabilities");
  602. return 1;
  603. }
  604. env->capabilities[i] = strdupz(str);
  605. }
  606. return 0;
  607. }
  608. static int parse_json_env(const char *json_str, aclk_env_t *env) {
  609. json_object *json;
  610. struct json_object_iterator it;
  611. struct json_object_iterator itEnd;
  612. json = json_tokener_parse(json_str);
  613. if (!json) {
  614. netdata_log_error("JSON-C failed to parse the payload of http response of /env endpoint");
  615. return 1;
  616. }
  617. it = json_object_iter_begin(json);
  618. itEnd = json_object_iter_end(json);
  619. while (!json_object_iter_equal(&it, &itEnd)) {
  620. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_AUTH_ENDPOINT)) {
  621. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_AUTH_ENDPOINT)
  622. if (env->auth_endpoint) {
  623. netdata_log_error("authEndpoint set already");
  624. goto exit;
  625. }
  626. env->auth_endpoint = strdupz(json_object_get_string(json_object_iter_peek_value(&it)));
  627. json_object_iter_next(&it);
  628. continue;
  629. }
  630. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_ENC)) {
  631. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_string, JSON_KEY_ENC)
  632. if (env->encoding != ACLK_ENC_UNKNOWN) {
  633. netdata_log_error(JSON_KEY_ENC " set already");
  634. goto exit;
  635. }
  636. env->encoding = aclk_encoding_type_t_from_str(json_object_get_string(json_object_iter_peek_value(&it)));
  637. json_object_iter_next(&it);
  638. continue;
  639. }
  640. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_TRP)) {
  641. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_TRP)
  642. json_object *now = json_object_iter_peek_value(&it);
  643. parse_json_env_transports(now, env);
  644. json_object_iter_next(&it);
  645. continue;
  646. }
  647. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_BACKOFF)) {
  648. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_object, JSON_KEY_BACKOFF)
  649. if (parse_json_backoff(json_object_iter_peek_value(&it), &env->backoff)) {
  650. env->backoff.base = 0;
  651. netdata_log_error("Error parsing Backoff parameters in env");
  652. goto exit;
  653. }
  654. json_object_iter_next(&it);
  655. continue;
  656. }
  657. if (!strcmp(json_object_iter_peek_name(&it), JSON_KEY_CAPS)) {
  658. PARSE_ENV_JSON_CHK_TYPE(&it, json_type_array, JSON_KEY_CAPS)
  659. if (parse_json_env_caps(json_object_iter_peek_value(&it), env)) {
  660. netdata_log_error("Error parsing capabilities list");
  661. goto exit;
  662. }
  663. json_object_iter_next(&it);
  664. continue;
  665. }
  666. netdata_log_error("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
  667. json_object_iter_next(&it);
  668. }
  669. // Check all compulsory keys have been set
  670. if (env->transport_count < 1) {
  671. netdata_log_error("env has to return at least one transport");
  672. goto exit;
  673. }
  674. if (!env->auth_endpoint) {
  675. netdata_log_error(JSON_KEY_AUTH_ENDPOINT " is compulsory");
  676. goto exit;
  677. }
  678. if (env->encoding == ACLK_ENC_UNKNOWN) {
  679. netdata_log_error(JSON_KEY_ENC " is compulsory");
  680. goto exit;
  681. }
  682. if (!env->backoff.base) {
  683. netdata_log_error(JSON_KEY_BACKOFF " is compulsory");
  684. goto exit;
  685. }
  686. json_object_put(json);
  687. return 0;
  688. exit:
  689. aclk_env_t_destroy(env);
  690. json_object_put(json);
  691. return 1;
  692. }
  693. int aclk_get_env(aclk_env_t *env, const char* aclk_hostname, int aclk_port) {
  694. BUFFER *buf = buffer_create(1024, &netdata_buffers_statistics.buffers_aclk);
  695. https_req_t req = HTTPS_REQ_T_INITIALIZER;
  696. https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
  697. req.request_type = HTTP_REQ_GET;
  698. char *agent_id = get_agent_claimid();
  699. if (agent_id == NULL)
  700. {
  701. netdata_log_error("Agent was not claimed - cannot perform challenge/response");
  702. buffer_free(buf);
  703. return 1;
  704. }
  705. buffer_sprintf(buf, "/api/v1/env?v=%s&cap=proto,ctx&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id);
  706. freez(agent_id);
  707. req.host = (char*)aclk_hostname;
  708. req.port = aclk_port;
  709. req.url = buf->buffer;
  710. if (aclk_https_request(&req, &resp)) {
  711. netdata_log_error("Error trying to contact env endpoint");
  712. https_req_response_free(&resp);
  713. buffer_free(buf);
  714. return 2;
  715. }
  716. if (resp.http_code != 200) {
  717. netdata_log_error("The HTTP code not 200 OK (Got %d)", resp.http_code);
  718. if (resp.payload_size)
  719. aclk_parse_otp_error(resp.payload);
  720. https_req_response_free(&resp);
  721. buffer_free(buf);
  722. return 3;
  723. }
  724. if (!resp.payload || !resp.payload_size) {
  725. netdata_log_error("Unexpected empty payload as response to /env call");
  726. https_req_response_free(&resp);
  727. buffer_free(buf);
  728. return 4;
  729. }
  730. if (parse_json_env(resp.payload, env)) {
  731. netdata_log_error("error parsing /env message");
  732. https_req_response_free(&resp);
  733. buffer_free(buf);
  734. return 5;
  735. }
  736. netdata_log_info("Getting Cloud /env successful");
  737. https_req_response_free(&resp);
  738. buffer_free(buf);
  739. return 0;
  740. }