credentials_utils.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/auth/private/credentials_utils.h>
  6. #include <aws/common/clock.h>
  7. #include <aws/common/date_time.h>
  8. #include <aws/common/json.h>
  9. #include <aws/common/string.h>
  10. #include <aws/common/uuid.h>
  11. #include <aws/http/connection.h>
  12. #include <aws/http/request_response.h>
  13. #include <aws/http/status_code.h>
  14. #include <aws/sdkutils/aws_profile.h>
  15. #if defined(_MSC_VER)
  16. # pragma warning(disable : 4232)
  17. #endif /* _MSC_VER */
  18. static struct aws_auth_http_system_vtable s_default_function_table = {
  19. .aws_http_connection_manager_new = aws_http_connection_manager_new,
  20. .aws_http_connection_manager_release = aws_http_connection_manager_release,
  21. .aws_http_connection_manager_acquire_connection = aws_http_connection_manager_acquire_connection,
  22. .aws_http_connection_manager_release_connection = aws_http_connection_manager_release_connection,
  23. .aws_http_connection_make_request = aws_http_connection_make_request,
  24. .aws_http_stream_activate = aws_http_stream_activate,
  25. .aws_http_stream_get_connection = aws_http_stream_get_connection,
  26. .aws_http_stream_get_incoming_response_status = aws_http_stream_get_incoming_response_status,
  27. .aws_http_stream_release = aws_http_stream_release,
  28. .aws_http_connection_close = aws_http_connection_close,
  29. };
  30. const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table = &s_default_function_table;
  31. void aws_credentials_query_init(
  32. struct aws_credentials_query *query,
  33. struct aws_credentials_provider *provider,
  34. aws_on_get_credentials_callback_fn *callback,
  35. void *user_data) {
  36. AWS_ZERO_STRUCT(*query);
  37. query->provider = provider;
  38. query->user_data = user_data;
  39. query->callback = callback;
  40. aws_credentials_provider_acquire(provider);
  41. }
  42. void aws_credentials_query_clean_up(struct aws_credentials_query *query) {
  43. if (query != NULL) {
  44. aws_credentials_provider_release(query->provider);
  45. }
  46. }
  47. void aws_credentials_provider_init_base(
  48. struct aws_credentials_provider *provider,
  49. struct aws_allocator *allocator,
  50. struct aws_credentials_provider_vtable *vtable,
  51. void *impl) {
  52. provider->allocator = allocator;
  53. provider->vtable = vtable;
  54. provider->impl = impl;
  55. aws_atomic_init_int(&provider->ref_count, 1);
  56. }
  57. void aws_credentials_provider_invoke_shutdown_callback(struct aws_credentials_provider *provider) {
  58. if (provider && provider->shutdown_options.shutdown_callback) {
  59. provider->shutdown_options.shutdown_callback(provider->shutdown_options.shutdown_user_data);
  60. }
  61. }
  62. static bool s_parse_expiration_value_from_json_object(
  63. struct aws_json_value *value,
  64. const struct aws_parse_credentials_from_json_doc_options *options,
  65. uint64_t *expiration_timepoint_in_seconds) {
  66. if (value == NULL) {
  67. AWS_LOGF_INFO(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "No credentials Expiration field in Json document.");
  68. return false;
  69. }
  70. struct aws_byte_cursor expiration_cursor = {
  71. .ptr = NULL,
  72. .len = 0,
  73. };
  74. switch (options->expiration_format) {
  75. case AWS_PCEF_STRING_ISO_8601_DATE: {
  76. if (aws_json_value_get_string(value, &expiration_cursor)) {
  77. AWS_LOGF_INFO(
  78. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  79. "Unabled to extract credentials Expiration field from Json document.");
  80. return false;
  81. }
  82. if (expiration_cursor.len == 0) {
  83. AWS_LOGF_INFO(
  84. AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Parsed a credentials json document with empty expiration.");
  85. return false;
  86. }
  87. struct aws_date_time expiration;
  88. if (aws_date_time_init_from_str_cursor(&expiration, &expiration_cursor, AWS_DATE_FORMAT_ISO_8601)) {
  89. AWS_LOGF_INFO(
  90. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  91. "credentials Expiration in Json document is not a valid ISO_8601 date string.");
  92. return false;
  93. }
  94. *expiration_timepoint_in_seconds = (uint64_t)aws_date_time_as_epoch_secs(&expiration);
  95. return true;
  96. }
  97. case AWS_PCEF_NUMBER_UNIX_EPOCH: {
  98. double expiration_value = 0;
  99. if (aws_json_value_get_number(value, &expiration_value)) {
  100. AWS_LOGF_INFO(
  101. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  102. "Unabled to extract credentials Expiration field from Json document.");
  103. return false;
  104. }
  105. *expiration_timepoint_in_seconds = (uint64_t)expiration_value;
  106. return true;
  107. }
  108. case AWS_PCEF_NUMBER_UNIX_EPOCH_MS: {
  109. double expiration_value_ms = 0;
  110. if (aws_json_value_get_number(value, &expiration_value_ms)) {
  111. AWS_LOGF_INFO(
  112. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  113. "Unabled to extract credentials Expiration field from Json document.");
  114. return false;
  115. }
  116. *expiration_timepoint_in_seconds =
  117. aws_timestamp_convert((uint64_t)expiration_value_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL);
  118. return true;
  119. }
  120. default:
  121. return false;
  122. }
  123. }
  124. struct aws_credentials *aws_parse_credentials_from_aws_json_object(
  125. struct aws_allocator *allocator,
  126. struct aws_json_value *document_root,
  127. const struct aws_parse_credentials_from_json_doc_options *options) {
  128. AWS_FATAL_ASSERT(allocator);
  129. AWS_FATAL_ASSERT(document_root);
  130. AWS_FATAL_ASSERT(options);
  131. AWS_FATAL_ASSERT(options->access_key_id_name);
  132. AWS_FATAL_ASSERT(options->secret_access_key_name);
  133. if (options->token_required) {
  134. AWS_FATAL_ASSERT(options->token_name);
  135. }
  136. if (options->expiration_required) {
  137. AWS_FATAL_ASSERT(options->expiration_name);
  138. }
  139. struct aws_credentials *credentials = NULL;
  140. struct aws_json_value *access_key_id = NULL;
  141. struct aws_json_value *secrete_access_key = NULL;
  142. struct aws_json_value *token = NULL;
  143. struct aws_json_value *creds_expiration = NULL;
  144. bool parse_error = true;
  145. /*
  146. * Pull out the credentials components
  147. */
  148. struct aws_byte_cursor access_key_id_cursor;
  149. access_key_id =
  150. aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str((char *)options->access_key_id_name));
  151. if (!aws_json_value_is_string(access_key_id) ||
  152. aws_json_value_get_string(access_key_id, &access_key_id_cursor) == AWS_OP_ERR) {
  153. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse AccessKeyId from Json document.");
  154. goto done;
  155. }
  156. struct aws_byte_cursor secrete_access_key_cursor;
  157. secrete_access_key = aws_json_value_get_from_object(
  158. document_root, aws_byte_cursor_from_c_str((char *)options->secret_access_key_name));
  159. if (!aws_json_value_is_string(secrete_access_key) ||
  160. aws_json_value_get_string(secrete_access_key, &secrete_access_key_cursor) == AWS_OP_ERR) {
  161. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse SecretAccessKey from Json document.");
  162. goto done;
  163. }
  164. struct aws_byte_cursor token_cursor;
  165. if (options->token_name) {
  166. token = aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str((char *)options->token_name));
  167. if (!aws_json_value_is_string(token) || aws_json_value_get_string(token, &token_cursor) == AWS_OP_ERR) {
  168. if (options->token_required) {
  169. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse Token from Json document.");
  170. goto done;
  171. }
  172. }
  173. }
  174. // needed to avoid uninitialized local variable error
  175. uint64_t expiration_timepoint_in_seconds = UINT64_MAX;
  176. if (options->expiration_name) {
  177. creds_expiration =
  178. aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str((char *)options->expiration_name));
  179. if (!s_parse_expiration_value_from_json_object(creds_expiration, options, &expiration_timepoint_in_seconds)) {
  180. if (options->expiration_required) {
  181. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse Expiration from Json document.");
  182. goto done;
  183. }
  184. }
  185. }
  186. /*
  187. * Build the credentials
  188. */
  189. if (access_key_id_cursor.len == 0 || secrete_access_key_cursor.len == 0) {
  190. AWS_LOGF_ERROR(
  191. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  192. "Parsed an unexpected credentials json document, either access key, secret key is empty.");
  193. goto done;
  194. }
  195. struct aws_byte_cursor session_token_cursor;
  196. AWS_ZERO_STRUCT(session_token_cursor);
  197. if (token) {
  198. aws_json_value_get_string(token, &session_token_cursor);
  199. if (options->token_required && session_token_cursor.len == 0) {
  200. AWS_LOGF_ERROR(
  201. AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Parsed an unexpected credentials json document with empty token.");
  202. goto done;
  203. }
  204. }
  205. credentials = aws_credentials_new(
  206. allocator,
  207. access_key_id_cursor,
  208. secrete_access_key_cursor,
  209. session_token_cursor,
  210. expiration_timepoint_in_seconds);
  211. if (credentials == NULL) {
  212. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to allocate memory for credentials.");
  213. parse_error = false;
  214. goto done;
  215. }
  216. done:
  217. if (parse_error) {
  218. aws_raise_error(AWS_AUTH_PROVIDER_PARSER_UNEXPECTED_RESPONSE);
  219. }
  220. return credentials;
  221. }
  222. struct aws_credentials *aws_parse_credentials_from_json_document(
  223. struct aws_allocator *allocator,
  224. struct aws_byte_cursor document,
  225. const struct aws_parse_credentials_from_json_doc_options *options) {
  226. struct aws_credentials *credentials = NULL;
  227. struct aws_json_value *document_root = aws_json_value_new_from_string(allocator, document);
  228. if (document_root == NULL) {
  229. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document.");
  230. return NULL;
  231. }
  232. struct aws_json_value *top_level_object = NULL;
  233. if (options->top_level_object_name) {
  234. top_level_object =
  235. aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str(options->top_level_object_name));
  236. if (!top_level_object) {
  237. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "failed to parse top level object in json document.");
  238. goto done;
  239. }
  240. }
  241. credentials = aws_parse_credentials_from_aws_json_object(
  242. allocator, top_level_object ? top_level_object : document_root, options);
  243. done:
  244. aws_json_value_destroy(document_root);
  245. return credentials;
  246. }
  247. static bool s_is_transient_network_error(int error_code) {
  248. return error_code == AWS_ERROR_HTTP_CONNECTION_CLOSED || error_code == AWS_ERROR_HTTP_SERVER_CLOSED ||
  249. error_code == AWS_IO_SOCKET_CLOSED || error_code == AWS_IO_SOCKET_CONNECT_ABORTED ||
  250. error_code == AWS_IO_SOCKET_CONNECTION_REFUSED || error_code == AWS_IO_SOCKET_NETWORK_DOWN ||
  251. error_code == AWS_IO_DNS_QUERY_FAILED || error_code == AWS_IO_DNS_NO_ADDRESS_FOR_HOST ||
  252. error_code == AWS_IO_SOCKET_TIMEOUT || error_code == AWS_IO_TLS_NEGOTIATION_TIMEOUT ||
  253. error_code == AWS_HTTP_STATUS_CODE_408_REQUEST_TIMEOUT;
  254. }
  255. enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int response_code, int error_code) {
  256. enum aws_retry_error_type error_type = response_code >= 400 && response_code < 500
  257. ? AWS_RETRY_ERROR_TYPE_CLIENT_ERROR
  258. : AWS_RETRY_ERROR_TYPE_SERVER_ERROR;
  259. if (s_is_transient_network_error(error_code)) {
  260. error_type = AWS_RETRY_ERROR_TYPE_TRANSIENT;
  261. }
  262. /* server throttling us is retryable */
  263. if (response_code == AWS_HTTP_STATUS_CODE_429_TOO_MANY_REQUESTS) {
  264. /* force a new connection on this. */
  265. error_type = AWS_RETRY_ERROR_TYPE_THROTTLING;
  266. }
  267. return error_type;
  268. }
  269. struct aws_profile_collection *aws_load_profile_collection_from_config_file(
  270. struct aws_allocator *allocator,
  271. struct aws_byte_cursor config_file_name_override) {
  272. struct aws_profile_collection *config_profiles = NULL;
  273. struct aws_string *config_file_path = NULL;
  274. config_file_path = aws_get_config_file_path(allocator, &config_file_name_override);
  275. if (!config_file_path) {
  276. AWS_LOGF_ERROR(
  277. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  278. "Failed to resolve config file path: %s",
  279. aws_error_str(aws_last_error()));
  280. return NULL;
  281. }
  282. config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG);
  283. if (config_profiles != NULL) {
  284. AWS_LOGF_DEBUG(
  285. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  286. "Successfully built config profile collection from file at (%s)",
  287. aws_string_c_str(config_file_path));
  288. } else {
  289. AWS_LOGF_ERROR(
  290. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  291. "Failed to build config profile collection from file at (%s) : %s",
  292. aws_string_c_str(config_file_path),
  293. aws_error_str(aws_last_error()));
  294. }
  295. aws_string_destroy(config_file_path);
  296. return config_profiles;
  297. }