123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887 |
- // 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"
- struct dictionary_singleton {
- char *key;
- char *result;
- };
- static int json_extract_singleton(JSON_ENTRY *e)
- {
- struct dictionary_singleton *data = e->callback_data;
- switch (e->type) {
- case JSON_OBJECT:
- case JSON_ARRAY:
- break;
- case JSON_STRING:
- if (!strcmp(e->name, data->key)) {
- data->result = strdupz(e->data.string);
- break;
- }
- break;
- case JSON_NUMBER:
- case JSON_BOOLEAN:
- case JSON_NULL:
- break;
- }
- return 0;
- }
- // Base-64 decoder.
- // Note: This is non-validating, invalid input will be decoded without an error.
- // Challenges are packed into json strings so we don't skip newlines.
- // Size errors (i.e. invalid input size or insufficient output space) are caught.
- static size_t base64_decode(unsigned char *input, size_t input_size, unsigned char *output, size_t output_size)
- {
- static char lookup[256];
- static int first_time=1;
- if (first_time)
- {
- first_time = 0;
- for(int i=0; i<256; i++)
- lookup[i] = -1;
- for(int i='A'; i<='Z'; i++)
- lookup[i] = i-'A';
- for(int i='a'; i<='z'; i++)
- lookup[i] = i-'a' + 26;
- for(int i='0'; i<='9'; i++)
- lookup[i] = i-'0' + 52;
- lookup['+'] = 62;
- lookup['/'] = 63;
- }
- if ((input_size & 3) != 0)
- {
- error("Can't decode base-64 input length %zu", input_size);
- return 0;
- }
- size_t unpadded_size = (input_size/4) * 3;
- if ( unpadded_size > output_size )
- {
- error("Output buffer size %zu is too small to decode %zu into", output_size, input_size);
- return 0;
- }
- // Don't check padding within full quantums
- for (size_t i = 0 ; i < input_size-4 ; i+=4 )
- {
- uint32_t value = (lookup[input[0]] << 18) + (lookup[input[1]] << 12) + (lookup[input[2]] << 6) + lookup[input[3]];
- output[0] = value >> 16;
- output[1] = value >> 8;
- output[2] = value;
- //error("Decoded %c %c %c %c -> %02x %02x %02x", input[0], input[1], input[2], input[3], output[0], output[1], output[2]);
- output += 3;
- input += 4;
- }
- // Handle padding only in last quantum
- if (input[2] == '=') {
- uint32_t value = (lookup[input[0]] << 6) + lookup[input[1]];
- output[0] = value >> 4;
- //error("Decoded %c %c %c %c -> %02x", input[0], input[1], input[2], input[3], output[0]);
- return unpadded_size-2;
- }
- else if (input[3] == '=') {
- uint32_t value = (lookup[input[0]] << 12) + (lookup[input[1]] << 6) + lookup[input[2]];
- output[0] = value >> 10;
- output[1] = value >> 2;
- //error("Decoded %c %c %c %c -> %02x %02x", input[0], input[1], input[2], input[3], output[0], output[1]);
- return unpadded_size-1;
- }
- else
- {
- uint32_t value = (input[0] << 18) + (input[1] << 12) + (input[2]<<6) + input[3];
- output[0] = value >> 16;
- output[1] = value >> 8;
- output[2] = value;
- //error("Decoded %c %c %c %c -> %02x %02x %02x", input[0], input[1], input[2], input[3], output[0], output[1], output[2]);
- return unpadded_size;
- }
- }
- static size_t base64_encode(unsigned char *input, size_t input_size, char *output, size_t output_size)
- {
- uint32_t value;
- static char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789+/";
- if ((input_size/3+1)*4 >= output_size)
- {
- error("Output buffer for encoding size=%zu is not large enough for %zu-bytes input", output_size, input_size);
- return 0;
- }
- size_t count = 0;
- while (input_size>3)
- {
- value = ((input[0] << 16) + (input[1] << 8) + input[2]) & 0xffffff;
- output[0] = lookup[value >> 18];
- output[1] = lookup[(value >> 12) & 0x3f];
- output[2] = lookup[(value >> 6) & 0x3f];
- output[3] = lookup[value & 0x3f];
- //error("Base-64 encode (%04x) -> %c %c %c %c\n", value, output[0], output[1], output[2], output[3]);
- output += 4;
- input += 3;
- input_size -= 3;
- count += 4;
- }
- switch (input_size)
- {
- case 2:
- value = (input[0] << 10) + (input[1] << 2);
- output[0] = lookup[(value >> 12) & 0x3f];
- output[1] = lookup[(value >> 6) & 0x3f];
- output[2] = lookup[value & 0x3f];
- output[3] = '=';
- //error("Base-64 encode (%06x) -> %c %c %c %c\n", (value>>2)&0xffff, output[0], output[1], output[2], output[3]);
- count += 4;
- break;
- case 1:
- value = input[0] << 4;
- output[0] = lookup[(value >> 6) & 0x3f];
- output[1] = lookup[value & 0x3f];
- output[2] = '=';
- output[3] = '=';
- //error("Base-64 encode (%06x) -> %c %c %c %c\n", value, output[0], output[1], output[2], output[3]);
- count += 4;
- break;
- case 0:
- break;
- }
- return count;
- }
- static int private_decrypt(RSA *p_key, unsigned char * enc_data, int data_len, unsigned char *decrypted)
- {
- int result = RSA_private_decrypt( data_len, enc_data, decrypted, p_key, RSA_PKCS1_OAEP_PADDING);
- if (result == -1) {
- char err[512];
- ERR_error_string_n(ERR_get_error(), err, sizeof(err));
- error("Decryption of the challenge failed: %s", err);
- }
- return result;
- }
- 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, .type = MQTT_WSS_DIRECT };
- aclk_set_proxy((char**)&proxy_conf.host, &proxy_conf.port, &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;
- }
- rc = https_request(request, response);
- freez((char*)proxy_conf.host);
- 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) { \
- 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) {
- 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))) {
- error("Failed to generate topic cache!");
- goto exit;
- }
- json_object_iter_next(&it);
- continue;
- }
- error("Unknown key \"%s\" in passwd response payload. Ignoring", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
- if (!auth->client_id) {
- error(JSON_KEY_CLIENTID " is compulsory key in /password response");
- goto exit;
- }
- if (!auth->passwd) {
- error(JSON_KEY_PASS " is compulsory in /password response");
- goto exit;
- }
- if (!auth->username) {
- 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)) {
- error("Missing compulsory key \"%s\" in error response", path);
- return NULL;
- }
- if (json_object_get_type(ptr) != json_type_string) {
- 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) {
- 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) {
- 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) {
- 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;
- 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) {
- 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) {
- 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) {
- 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;
- }
- 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;
- 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
- #define OTP_URL_PREFIX "/api/v1/auth/node/"
- int aclk_get_mqtt_otp(RSA *p_key, char **mqtt_id, char **mqtt_usr, char **mqtt_pass, url_t *target) {
- // TODO this fnc will be rewritten and simplified in following PRs
- // still carries lot of baggage from ACLK Legacy
- int rc = 1;
- BUFFER *url = buffer_create(strlen(OTP_URL_PREFIX) + UUID_STR_LEN + 20);
- https_req_t req = HTTPS_REQ_T_INITIALIZER;
- https_req_response_t resp = HTTPS_REQ_RESPONSE_T_INITIALIZER;
- char *agent_id = is_agent_claimed();
- if (agent_id == NULL)
- {
- error("Agent was not claimed - cannot perform challenge/response");
- goto cleanup;
- }
- // GET Challenge
- req.host = target->host;
- req.port = target->port;
- buffer_sprintf(url, "%s/node/%s/challenge", target->path, agent_id);
- req.url = url->buffer;
- if (aclk_https_request(&req, &resp)) {
- error ("ACLK_OTP Challenge failed");
- goto cleanup;
- }
- if (resp.http_code != 200) {
- error ("ACLK_OTP Challenge HTTP code not 200 OK (got %d)", resp.http_code);
- if (resp.payload_size)
- aclk_parse_otp_error(resp.payload);
- goto cleanup_resp;
- }
- info ("ACLK_OTP Got Challenge from Cloud");
- struct dictionary_singleton challenge = { .key = "challenge", .result = NULL };
- if (json_parse(resp.payload, &challenge, json_extract_singleton) != JSON_OK)
- {
- freez(challenge.result);
- error("Could not parse the the challenge");
- goto cleanup_resp;
- }
- if (challenge.result == NULL) {
- error("Could not retrieve challenge JSON key from challenge response");
- goto cleanup_resp;
- }
- // Decrypt the Challenge and Calculate Response
- size_t challenge_len = strlen(challenge.result);
- unsigned char decoded[512];
- size_t decoded_len = base64_decode((unsigned char*)challenge.result, challenge_len, decoded, sizeof(decoded));
- freez(challenge.result);
- unsigned char plaintext[4096]={};
- int decrypted_length = private_decrypt(p_key, decoded, decoded_len, plaintext);
- char encoded[512];
- size_t encoded_len = base64_encode(plaintext, decrypted_length, encoded, sizeof(encoded));
- encoded[encoded_len] = 0;
- debug(D_ACLK, "Encoded len=%zu Decryption len=%d: '%s'", encoded_len, decrypted_length, encoded);
- char response_json[4096]={};
- sprintf(response_json, "{\"response\":\"%s\"}", encoded);
- debug(D_ACLK, "Password phase: %s",response_json);
- https_req_response_free(&resp);
- https_req_response_init(&resp);
- // POST password
- req.request_type = HTTP_REQ_POST;
- buffer_flush(url);
- buffer_sprintf(url, "%s/node/%s/password", target->path, agent_id);
- req.url = url->buffer;
- req.payload = response_json;
- req.payload_size = strlen(response_json);
- if (aclk_https_request(&req, &resp)) {
- error ("ACLK_OTP Password error trying to post result to password");
- goto cleanup;
- }
- if (resp.http_code != 201) {
- 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_resp;
- }
- info ("ACLK_OTP Got Password from Cloud");
- struct auth_data data = { .client_id = NULL, .passwd = NULL, .username = NULL };
-
- if (parse_passwd_response(resp.payload, &data)){
- error("Error parsing response of password endpoint");
- goto cleanup_resp;
- }
- *mqtt_pass = data.passwd;
- *mqtt_usr = data.username;
- *mqtt_id = data.client_id;
- rc = 0;
- cleanup_resp:
- https_req_response_free(&resp);
- cleanup:
- freez(agent_id);
- buffer_free(url);
- return rc;
- }
- #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) {
- 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) {
- 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) {
- 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;
- }
-
- error ("unknown JSON key in dictionary (\"%s\")", json_object_iter_peek_name(&it));
- json_object_iter_next(&it);
- }
- if (!trp->endpoint) {
- error (JSON_KEY_TRP_ENDPOINT " is missing from JSON dictionary");
- goto exit;
- }
- if (trp->type == ACLK_TRP_UNKNOWN) {
- 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) {
- 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)) {
- 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) {
- 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) {
- 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;
- }
- 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) {
- 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) {
- error("Capability at index %d not a string!", (int)i);
- return 1;
- }
- str = json_object_get_string(obj);
- if (!str) {
- 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) {
- 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) {
- 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) {
- 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;
- 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)) {
- error("Error parsing capabilities list");
- goto exit;
- }
- json_object_iter_next(&it);
- continue;
- }
- 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) {
- error("env has to return at least one transport");
- goto exit;
- }
- if (!env->auth_endpoint) {
- error(JSON_KEY_AUTH_ENDPOINT " is compulsory");
- goto exit;
- }
- if (env->encoding == ACLK_ENC_UNKNOWN) {
- error(JSON_KEY_ENC " is compulsory");
- goto exit;
- }
- if (!env->backoff.base) {
- 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);
- 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 = is_agent_claimed();
- if (agent_id == NULL)
- {
- error("Agent was not claimed - cannot perform challenge/response");
- buffer_free(buf);
- return 1;
- }
- #ifdef ENABLE_NEW_CLOUD_PROTOCOL
- buffer_sprintf(buf, "/api/v1/env?v=%s&cap=json,proto&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id);
- #else
- buffer_sprintf(buf, "/api/v1/env?v=%s&cap=json&claim_id=%s", &(VERSION[1]) /* skip 'v' at beginning */, agent_id);
- #endif
- freez(agent_id);
- req.host = (char*)aclk_hostname;
- req.port = aclk_port;
- req.url = buf->buffer;
- if (aclk_https_request(&req, &resp)) {
- error("Error trying to contact env endpoint");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 1;
- }
- if (resp.http_code != 200) {
- error("The HTTP code not 200 OK (Got %d)", resp.http_code);
- https_req_response_free(&resp);
- buffer_free(buf);
- return 1;
- }
- if (!resp.payload || !resp.payload_size) {
- error("Unexpected empty payload as response to /env call");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 1;
- }
- if (parse_json_env(resp.payload, env)) {
- error ("error parsing /env message");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 1;
- }
- info("Getting Cloud /env successful");
- https_req_response_free(&resp);
- buffer_free(buf);
- return 0;
- }
|