credentials_utils.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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/date_time.h>
  7. #include <aws/common/json.h>
  8. #include <aws/common/string.h>
  9. #include <aws/common/uuid.h>
  10. #include <aws/http/connection.h>
  11. #include <aws/http/request_response.h>
  12. #include <aws/http/status_code.h>
  13. #if defined(_MSC_VER)
  14. # pragma warning(disable : 4232)
  15. #endif /* _MSC_VER */
  16. static struct aws_auth_http_system_vtable s_default_function_table = {
  17. .aws_http_connection_manager_new = aws_http_connection_manager_new,
  18. .aws_http_connection_manager_release = aws_http_connection_manager_release,
  19. .aws_http_connection_manager_acquire_connection = aws_http_connection_manager_acquire_connection,
  20. .aws_http_connection_manager_release_connection = aws_http_connection_manager_release_connection,
  21. .aws_http_connection_make_request = aws_http_connection_make_request,
  22. .aws_http_stream_activate = aws_http_stream_activate,
  23. .aws_http_stream_get_connection = aws_http_stream_get_connection,
  24. .aws_http_stream_get_incoming_response_status = aws_http_stream_get_incoming_response_status,
  25. .aws_http_stream_release = aws_http_stream_release,
  26. .aws_http_connection_close = aws_http_connection_close,
  27. };
  28. const struct aws_auth_http_system_vtable *g_aws_credentials_provider_http_function_table = &s_default_function_table;
  29. void aws_credentials_query_init(
  30. struct aws_credentials_query *query,
  31. struct aws_credentials_provider *provider,
  32. aws_on_get_credentials_callback_fn *callback,
  33. void *user_data) {
  34. AWS_ZERO_STRUCT(*query);
  35. query->provider = provider;
  36. query->user_data = user_data;
  37. query->callback = callback;
  38. aws_credentials_provider_acquire(provider);
  39. }
  40. void aws_credentials_query_clean_up(struct aws_credentials_query *query) {
  41. if (query != NULL) {
  42. aws_credentials_provider_release(query->provider);
  43. }
  44. }
  45. void aws_credentials_provider_init_base(
  46. struct aws_credentials_provider *provider,
  47. struct aws_allocator *allocator,
  48. struct aws_credentials_provider_vtable *vtable,
  49. void *impl) {
  50. provider->allocator = allocator;
  51. provider->vtable = vtable;
  52. provider->impl = impl;
  53. aws_atomic_init_int(&provider->ref_count, 1);
  54. }
  55. void aws_credentials_provider_invoke_shutdown_callback(struct aws_credentials_provider *provider) {
  56. if (provider && provider->shutdown_options.shutdown_callback) {
  57. provider->shutdown_options.shutdown_callback(provider->shutdown_options.shutdown_user_data);
  58. }
  59. }
  60. static bool s_parse_expiration_value_from_json_object(
  61. struct aws_json_value *value,
  62. const struct aws_parse_credentials_from_json_doc_options *options,
  63. uint64_t *expiration_timepoint_in_seconds) {
  64. if (value == NULL) {
  65. AWS_LOGF_INFO(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "No credentials Expiration field in Json document.");
  66. return false;
  67. }
  68. struct aws_byte_cursor expiration_cursor = {
  69. .ptr = NULL,
  70. .len = 0,
  71. };
  72. switch (options->expiration_format) {
  73. case AWS_PCEF_STRING_ISO_8601_DATE: {
  74. if (aws_json_value_get_string(value, &expiration_cursor)) {
  75. AWS_LOGF_INFO(
  76. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  77. "Unabled to extract credentials Expiration field from Json document.");
  78. return false;
  79. }
  80. if (expiration_cursor.len == 0) {
  81. AWS_LOGF_INFO(
  82. AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Parsed a credentials json document with empty expiration.");
  83. return false;
  84. }
  85. struct aws_date_time expiration;
  86. if (aws_date_time_init_from_str_cursor(&expiration, &expiration_cursor, AWS_DATE_FORMAT_ISO_8601)) {
  87. AWS_LOGF_INFO(
  88. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  89. "credentials Expiration in Json document is not a valid ISO_8601 date string.");
  90. return false;
  91. }
  92. *expiration_timepoint_in_seconds = (uint64_t)aws_date_time_as_epoch_secs(&expiration);
  93. return true;
  94. }
  95. case AWS_PCEF_NUMBER_UNIX_EPOCH: {
  96. double expiration_value = 0;
  97. if (aws_json_value_get_number(value, &expiration_value)) {
  98. AWS_LOGF_INFO(
  99. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  100. "Unabled to extract credentials Expiration field from Json document.");
  101. return false;
  102. }
  103. *expiration_timepoint_in_seconds = (uint64_t)expiration_value;
  104. return true;
  105. }
  106. default:
  107. return false;
  108. }
  109. }
  110. struct aws_credentials *aws_parse_credentials_from_aws_json_object(
  111. struct aws_allocator *allocator,
  112. struct aws_json_value *document_root,
  113. const struct aws_parse_credentials_from_json_doc_options *options) {
  114. AWS_FATAL_ASSERT(allocator);
  115. AWS_FATAL_ASSERT(document_root);
  116. AWS_FATAL_ASSERT(options);
  117. AWS_FATAL_ASSERT(options->access_key_id_name);
  118. AWS_FATAL_ASSERT(options->secret_access_key_name);
  119. if (options->token_required) {
  120. AWS_FATAL_ASSERT(options->token_name);
  121. }
  122. if (options->expiration_required) {
  123. AWS_FATAL_ASSERT(options->expiration_name);
  124. }
  125. struct aws_credentials *credentials = NULL;
  126. struct aws_json_value *access_key_id = NULL;
  127. struct aws_json_value *secrete_access_key = NULL;
  128. struct aws_json_value *token = NULL;
  129. struct aws_json_value *creds_expiration = NULL;
  130. bool parse_error = true;
  131. /*
  132. * Pull out the credentials components
  133. */
  134. struct aws_byte_cursor access_key_id_cursor;
  135. access_key_id =
  136. aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str((char *)options->access_key_id_name));
  137. if (!aws_json_value_is_string(access_key_id) ||
  138. aws_json_value_get_string(access_key_id, &access_key_id_cursor) == AWS_OP_ERR) {
  139. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse AccessKeyId from Json document.");
  140. goto done;
  141. }
  142. struct aws_byte_cursor secrete_access_key_cursor;
  143. secrete_access_key = aws_json_value_get_from_object(
  144. document_root, aws_byte_cursor_from_c_str((char *)options->secret_access_key_name));
  145. if (!aws_json_value_is_string(secrete_access_key) ||
  146. aws_json_value_get_string(secrete_access_key, &secrete_access_key_cursor) == AWS_OP_ERR) {
  147. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse SecretAccessKey from Json document.");
  148. goto done;
  149. }
  150. struct aws_byte_cursor token_cursor;
  151. if (options->token_name) {
  152. token = aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str((char *)options->token_name));
  153. if (!aws_json_value_is_string(token) || aws_json_value_get_string(token, &token_cursor) == AWS_OP_ERR) {
  154. if (options->token_required) {
  155. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse Token from Json document.");
  156. goto done;
  157. }
  158. }
  159. }
  160. // needed to avoid uninitialized local variable error
  161. uint64_t expiration_timepoint_in_seconds = UINT64_MAX;
  162. if (options->expiration_name) {
  163. creds_expiration =
  164. aws_json_value_get_from_object(document_root, aws_byte_cursor_from_c_str((char *)options->expiration_name));
  165. if (!s_parse_expiration_value_from_json_object(creds_expiration, options, &expiration_timepoint_in_seconds)) {
  166. if (options->expiration_required) {
  167. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse Expiration from Json document.");
  168. goto done;
  169. }
  170. }
  171. }
  172. /*
  173. * Build the credentials
  174. */
  175. if (access_key_id_cursor.len == 0 || secrete_access_key_cursor.len == 0) {
  176. AWS_LOGF_ERROR(
  177. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  178. "Parsed an unexpected credentials json document, either access key, secret key is empty.");
  179. goto done;
  180. }
  181. struct aws_byte_cursor session_token_cursor;
  182. AWS_ZERO_STRUCT(session_token_cursor);
  183. if (token) {
  184. aws_json_value_get_string(token, &session_token_cursor);
  185. if (options->token_required && session_token_cursor.len == 0) {
  186. AWS_LOGF_ERROR(
  187. AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Parsed an unexpected credentials json document with empty token.");
  188. goto done;
  189. }
  190. }
  191. credentials = aws_credentials_new(
  192. allocator,
  193. access_key_id_cursor,
  194. secrete_access_key_cursor,
  195. session_token_cursor,
  196. expiration_timepoint_in_seconds);
  197. if (credentials == NULL) {
  198. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to allocate memory for credentials.");
  199. parse_error = false;
  200. goto done;
  201. }
  202. done:
  203. if (parse_error) {
  204. aws_raise_error(AWS_AUTH_PROVIDER_PARSER_UNEXPECTED_RESPONSE);
  205. }
  206. return credentials;
  207. }
  208. struct aws_credentials *aws_parse_credentials_from_json_document(
  209. struct aws_allocator *allocator,
  210. const char *document,
  211. const struct aws_parse_credentials_from_json_doc_options *options) {
  212. struct aws_json_value *document_root =
  213. aws_json_value_new_from_string(allocator, aws_byte_cursor_from_c_str(document));
  214. if (document_root == NULL) {
  215. AWS_LOGF_ERROR(AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to parse document as Json document.");
  216. return NULL;
  217. }
  218. struct aws_credentials *credentials = aws_parse_credentials_from_aws_json_object(allocator, document_root, options);
  219. aws_json_value_destroy(document_root);
  220. return credentials;
  221. }
  222. static bool s_is_transient_network_error(int error_code) {
  223. return error_code == AWS_ERROR_HTTP_CONNECTION_CLOSED || error_code == AWS_ERROR_HTTP_SERVER_CLOSED ||
  224. error_code == AWS_IO_SOCKET_CLOSED || error_code == AWS_IO_SOCKET_CONNECT_ABORTED ||
  225. error_code == AWS_IO_SOCKET_CONNECTION_REFUSED || error_code == AWS_IO_SOCKET_NETWORK_DOWN ||
  226. error_code == AWS_IO_DNS_QUERY_FAILED || error_code == AWS_IO_DNS_NO_ADDRESS_FOR_HOST ||
  227. error_code == AWS_IO_SOCKET_TIMEOUT || error_code == AWS_IO_TLS_NEGOTIATION_TIMEOUT ||
  228. error_code == AWS_HTTP_STATUS_CODE_408_REQUEST_TIMEOUT;
  229. }
  230. enum aws_retry_error_type aws_credentials_provider_compute_retry_error_type(int response_code, int error_code) {
  231. enum aws_retry_error_type error_type = response_code >= 400 && response_code < 500
  232. ? AWS_RETRY_ERROR_TYPE_CLIENT_ERROR
  233. : AWS_RETRY_ERROR_TYPE_SERVER_ERROR;
  234. if (s_is_transient_network_error(error_code)) {
  235. error_type = AWS_RETRY_ERROR_TYPE_TRANSIENT;
  236. }
  237. /* server throttling us is retryable */
  238. if (response_code == AWS_HTTP_STATUS_CODE_429_TOO_MANY_REQUESTS) {
  239. /* force a new connection on this. */
  240. error_type = AWS_RETRY_ERROR_TYPE_THROTTLING;
  241. }
  242. return error_type;
  243. }