aclk_otp.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. #include "aclk_otp.h"
  3. #include "https_client.h"
  4. #include "../daemon/common.h"
  5. #include "../mqtt_websockets/c-rbuf/include/ringbuffer.h"
  6. struct dictionary_singleton {
  7. char *key;
  8. char *result;
  9. };
  10. static int json_extract_singleton(JSON_ENTRY *e)
  11. {
  12. struct dictionary_singleton *data = e->callback_data;
  13. switch (e->type) {
  14. case JSON_OBJECT:
  15. case JSON_ARRAY:
  16. break;
  17. case JSON_STRING:
  18. if (!strcmp(e->name, data->key)) {
  19. data->result = strdupz(e->data.string);
  20. break;
  21. }
  22. break;
  23. case JSON_NUMBER:
  24. case JSON_BOOLEAN:
  25. case JSON_NULL:
  26. break;
  27. }
  28. return 0;
  29. }
  30. // Base-64 decoder.
  31. // Note: This is non-validating, invalid input will be decoded without an error.
  32. // Challenges are packed into json strings so we don't skip newlines.
  33. // Size errors (i.e. invalid input size or insufficient output space) are caught.
  34. static size_t base64_decode(unsigned char *input, size_t input_size, unsigned char *output, size_t output_size)
  35. {
  36. static char lookup[256];
  37. static int first_time=1;
  38. if (first_time)
  39. {
  40. first_time = 0;
  41. for(int i=0; i<256; i++)
  42. lookup[i] = -1;
  43. for(int i='A'; i<='Z'; i++)
  44. lookup[i] = i-'A';
  45. for(int i='a'; i<='z'; i++)
  46. lookup[i] = i-'a' + 26;
  47. for(int i='0'; i<='9'; i++)
  48. lookup[i] = i-'0' + 52;
  49. lookup['+'] = 62;
  50. lookup['/'] = 63;
  51. }
  52. if ((input_size & 3) != 0)
  53. {
  54. error("Can't decode base-64 input length %zu", input_size);
  55. return 0;
  56. }
  57. size_t unpadded_size = (input_size/4) * 3;
  58. if ( unpadded_size > output_size )
  59. {
  60. error("Output buffer size %zu is too small to decode %zu into", output_size, input_size);
  61. return 0;
  62. }
  63. // Don't check padding within full quantums
  64. for (size_t i = 0 ; i < input_size-4 ; i+=4 )
  65. {
  66. uint32_t value = (lookup[input[0]] << 18) + (lookup[input[1]] << 12) + (lookup[input[2]] << 6) + lookup[input[3]];
  67. output[0] = value >> 16;
  68. output[1] = value >> 8;
  69. output[2] = value;
  70. //error("Decoded %c %c %c %c -> %02x %02x %02x", input[0], input[1], input[2], input[3], output[0], output[1], output[2]);
  71. output += 3;
  72. input += 4;
  73. }
  74. // Handle padding only in last quantum
  75. if (input[2] == '=') {
  76. uint32_t value = (lookup[input[0]] << 6) + lookup[input[1]];
  77. output[0] = value >> 4;
  78. //error("Decoded %c %c %c %c -> %02x", input[0], input[1], input[2], input[3], output[0]);
  79. return unpadded_size-2;
  80. }
  81. else if (input[3] == '=') {
  82. uint32_t value = (lookup[input[0]] << 12) + (lookup[input[1]] << 6) + lookup[input[2]];
  83. output[0] = value >> 10;
  84. output[1] = value >> 2;
  85. //error("Decoded %c %c %c %c -> %02x %02x", input[0], input[1], input[2], input[3], output[0], output[1]);
  86. return unpadded_size-1;
  87. }
  88. else
  89. {
  90. uint32_t value = (input[0] << 18) + (input[1] << 12) + (input[2]<<6) + input[3];
  91. output[0] = value >> 16;
  92. output[1] = value >> 8;
  93. output[2] = value;
  94. //error("Decoded %c %c %c %c -> %02x %02x %02x", input[0], input[1], input[2], input[3], output[0], output[1], output[2]);
  95. return unpadded_size;
  96. }
  97. }
  98. static size_t base64_encode(unsigned char *input, size_t input_size, char *output, size_t output_size)
  99. {
  100. uint32_t value;
  101. static char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  102. "abcdefghijklmnopqrstuvwxyz"
  103. "0123456789+/";
  104. if ((input_size/3+1)*4 >= output_size)
  105. {
  106. error("Output buffer for encoding size=%zu is not large enough for %zu-bytes input", output_size, input_size);
  107. return 0;
  108. }
  109. size_t count = 0;
  110. while (input_size>3)
  111. {
  112. value = ((input[0] << 16) + (input[1] << 8) + input[2]) & 0xffffff;
  113. output[0] = lookup[value >> 18];
  114. output[1] = lookup[(value >> 12) & 0x3f];
  115. output[2] = lookup[(value >> 6) & 0x3f];
  116. output[3] = lookup[value & 0x3f];
  117. //error("Base-64 encode (%04x) -> %c %c %c %c\n", value, output[0], output[1], output[2], output[3]);
  118. output += 4;
  119. input += 3;
  120. input_size -= 3;
  121. count += 4;
  122. }
  123. switch (input_size)
  124. {
  125. case 2:
  126. value = (input[0] << 10) + (input[1] << 2);
  127. output[0] = lookup[(value >> 12) & 0x3f];
  128. output[1] = lookup[(value >> 6) & 0x3f];
  129. output[2] = lookup[value & 0x3f];
  130. output[3] = '=';
  131. //error("Base-64 encode (%06x) -> %c %c %c %c\n", (value>>2)&0xffff, output[0], output[1], output[2], output[3]);
  132. count += 4;
  133. break;
  134. case 1:
  135. value = input[0] << 4;
  136. output[0] = lookup[(value >> 6) & 0x3f];
  137. output[1] = lookup[value & 0x3f];
  138. output[2] = '=';
  139. output[3] = '=';
  140. //error("Base-64 encode (%06x) -> %c %c %c %c\n", value, output[0], output[1], output[2], output[3]);
  141. count += 4;
  142. break;
  143. case 0:
  144. break;
  145. }
  146. return count;
  147. }
  148. static int private_decrypt(RSA *p_key, unsigned char * enc_data, int data_len, unsigned char *decrypted)
  149. {
  150. int result = RSA_private_decrypt( data_len, enc_data, decrypted, p_key, RSA_PKCS1_OAEP_PADDING);
  151. if (result == -1) {
  152. char err[512];
  153. ERR_error_string_n(ERR_get_error(), err, sizeof(err));
  154. error("Decryption of the challenge failed: %s", err);
  155. }
  156. return result;
  157. }
  158. // aclk_get_mqtt_otp is slightly modified original code from @amoss
  159. void aclk_get_mqtt_otp(RSA *p_key, char *aclk_hostname, int port, char **mqtt_usr, char **mqtt_pass)
  160. {
  161. char *data_buffer = mallocz(NETDATA_WEB_RESPONSE_INITIAL_SIZE);
  162. debug(D_ACLK, "Performing challenge-response sequence");
  163. if (*mqtt_pass != NULL)
  164. {
  165. freez(*mqtt_pass);
  166. *mqtt_pass = NULL;
  167. }
  168. // curl http://cloud-iam-agent-service:8080/api/v1/auth/node/00000000-0000-0000-0000-000000000000/challenge
  169. // TODO - target host?
  170. char *agent_id = is_agent_claimed();
  171. if (agent_id == NULL)
  172. {
  173. error("Agent was not claimed - cannot perform challenge/response");
  174. goto CLEANUP;
  175. }
  176. char url[1024];
  177. sprintf(url, "/api/v1/auth/node/%s/challenge", agent_id);
  178. info("Retrieving challenge from cloud: %s %d %s", aclk_hostname, port, url);
  179. if (https_request(HTTP_REQ_GET, aclk_hostname, port, url, data_buffer, NETDATA_WEB_RESPONSE_INITIAL_SIZE, NULL))
  180. {
  181. error("Challenge failed: %s", data_buffer);
  182. goto CLEANUP;
  183. }
  184. struct dictionary_singleton challenge = { .key = "challenge", .result = NULL };
  185. debug(D_ACLK, "Challenge response from cloud: %s", data_buffer);
  186. if (json_parse(data_buffer, &challenge, json_extract_singleton) != JSON_OK)
  187. {
  188. freez(challenge.result);
  189. error("Could not parse the json response with the challenge: %s", data_buffer);
  190. goto CLEANUP;
  191. }
  192. if (challenge.result == NULL) {
  193. error("Could not retrieve challenge from auth response: %s", data_buffer);
  194. goto CLEANUP;
  195. }
  196. size_t challenge_len = strlen(challenge.result);
  197. unsigned char decoded[512];
  198. size_t decoded_len = base64_decode((unsigned char*)challenge.result, challenge_len, decoded, sizeof(decoded));
  199. unsigned char plaintext[4096]={};
  200. int decrypted_length = private_decrypt(p_key, decoded, decoded_len, plaintext);
  201. freez(challenge.result);
  202. char encoded[512];
  203. size_t encoded_len = base64_encode(plaintext, decrypted_length, encoded, sizeof(encoded));
  204. encoded[encoded_len] = 0;
  205. debug(D_ACLK, "Encoded len=%zu Decryption len=%d: '%s'", encoded_len, decrypted_length, encoded);
  206. char response_json[4096]={};
  207. sprintf(response_json, "{\"response\":\"%s\"}", encoded);
  208. debug(D_ACLK, "Password phase: %s",response_json);
  209. // TODO - host
  210. sprintf(url, "/api/v1/auth/node/%s/password", agent_id);
  211. if (https_request(HTTP_REQ_POST, aclk_hostname, port, url, data_buffer, NETDATA_WEB_RESPONSE_INITIAL_SIZE, response_json))
  212. {
  213. error("Challenge-response failed: %s", data_buffer);
  214. goto CLEANUP;
  215. }
  216. debug(D_ACLK, "Password response from cloud: %s", data_buffer);
  217. struct dictionary_singleton password = { .key = "password", .result = NULL };
  218. if (json_parse(data_buffer, &password, json_extract_singleton) != JSON_OK)
  219. {
  220. freez(password.result);
  221. error("Could not parse the json response with the password: %s", data_buffer);
  222. goto CLEANUP;
  223. }
  224. if (password.result == NULL ) {
  225. error("Could not retrieve password from auth response");
  226. goto CLEANUP;
  227. }
  228. if (*mqtt_pass != NULL )
  229. freez(*mqtt_pass);
  230. *mqtt_pass = password.result;
  231. if (*mqtt_usr != NULL)
  232. freez(*mqtt_usr);
  233. *mqtt_usr = agent_id;
  234. agent_id = NULL;
  235. CLEANUP:
  236. if (agent_id != NULL)
  237. freez(agent_id);
  238. freez(data_buffer);
  239. return;
  240. }