credentials_provider_cached.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/auth/credentials.h>
  6. #include <aws/auth/private/credentials_utils.h>
  7. #include <aws/common/clock.h>
  8. #include <aws/common/mutex.h>
  9. #include <aws/common/time.h>
  10. #include <inttypes.h>
  11. /*
  12. ToDo: credentials expiration environment overrides
  13. AWS_STATIC_STRING_FROM_LITERAL(s_credential_expiration_env_var, "AWS_CREDENTIAL_EXPIRATION");
  14. */
  15. #define REFRESH_CREDENTIALS_EARLY_DURATION_SECONDS 10
  16. struct aws_credentials_provider_cached {
  17. struct aws_credentials_provider *source;
  18. struct aws_credentials *cached_credentials;
  19. struct aws_mutex lock;
  20. uint64_t refresh_interval_in_ns;
  21. uint64_t next_refresh_time;
  22. aws_io_clock_fn *high_res_clock_fn;
  23. aws_io_clock_fn *system_clock_fn;
  24. struct aws_linked_list pending_queries;
  25. };
  26. static void s_aws_credentials_query_list_notify_and_clean_up(
  27. struct aws_linked_list *query_list,
  28. struct aws_allocator *allocator,
  29. struct aws_credentials *credentials,
  30. int error_code) {
  31. while (!aws_linked_list_empty(query_list)) {
  32. struct aws_linked_list_node *node = aws_linked_list_pop_front(query_list);
  33. struct aws_credentials_query *query = AWS_CONTAINER_OF(node, struct aws_credentials_query, node);
  34. query->callback(credentials, error_code, query->user_data);
  35. aws_credentials_query_clean_up(query);
  36. aws_mem_release(allocator, query);
  37. }
  38. }
  39. static void s_swap_cached_credentials(
  40. struct aws_credentials_provider *provider,
  41. struct aws_credentials *new_credentials) {
  42. struct aws_credentials_provider_cached *cached_provider = provider->impl;
  43. aws_credentials_release(cached_provider->cached_credentials);
  44. cached_provider->cached_credentials = new_credentials;
  45. if (cached_provider->cached_credentials != NULL) {
  46. aws_credentials_acquire(cached_provider->cached_credentials);
  47. AWS_LOGF_DEBUG(
  48. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  49. "(id=%p) Cached credentials provider succesfully sourced credentials on refresh",
  50. (void *)provider);
  51. } else {
  52. AWS_LOGF_DEBUG(
  53. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  54. "(id=%p) Cached credentials provider was unable to source credentials on refresh",
  55. (void *)provider);
  56. }
  57. }
  58. static void s_cached_credentials_provider_get_credentials_async_callback(
  59. struct aws_credentials *credentials,
  60. int error_code,
  61. void *user_data) {
  62. struct aws_credentials_provider *provider = user_data;
  63. struct aws_credentials_provider_cached *impl = provider->impl;
  64. aws_mutex_lock(&impl->lock);
  65. /*
  66. * Move pending queries so that we can do notifications outside the lock
  67. */
  68. struct aws_linked_list pending_queries;
  69. aws_linked_list_init(&pending_queries);
  70. aws_linked_list_swap_contents(&pending_queries, &impl->pending_queries);
  71. uint64_t next_refresh_time_in_ns = UINT64_MAX;
  72. uint64_t high_res_now = 0;
  73. if (!impl->high_res_clock_fn(&high_res_now)) {
  74. if (impl->refresh_interval_in_ns > 0) {
  75. next_refresh_time_in_ns = high_res_now + impl->refresh_interval_in_ns;
  76. }
  77. uint64_t credentials_expiration_timepoint_seconds = UINT64_MAX;
  78. if (credentials != NULL) {
  79. credentials_expiration_timepoint_seconds = aws_credentials_get_expiration_timepoint_seconds(credentials);
  80. }
  81. /*
  82. * If the sourced credentials have an explicit expiration time, we should always use that time
  83. * rather than the much cruder, mechanical refresh setting on the caching wrapper.
  84. */
  85. if (credentials_expiration_timepoint_seconds < UINT64_MAX) {
  86. uint64_t system_now = 0;
  87. if (!impl->system_clock_fn(&system_now)) {
  88. uint64_t system_now_seconds =
  89. aws_timestamp_convert(system_now, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL);
  90. if (credentials_expiration_timepoint_seconds >=
  91. system_now_seconds + REFRESH_CREDENTIALS_EARLY_DURATION_SECONDS) {
  92. next_refresh_time_in_ns = high_res_now;
  93. next_refresh_time_in_ns += aws_timestamp_convert(
  94. credentials_expiration_timepoint_seconds - system_now_seconds -
  95. REFRESH_CREDENTIALS_EARLY_DURATION_SECONDS,
  96. AWS_TIMESTAMP_SECS,
  97. AWS_TIMESTAMP_NANOS,
  98. NULL);
  99. }
  100. }
  101. }
  102. }
  103. impl->next_refresh_time = next_refresh_time_in_ns;
  104. AWS_LOGF_DEBUG(
  105. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  106. "(id=%p) Cached credentials provider next refresh time set to %" PRIu64,
  107. (void *)provider,
  108. impl->next_refresh_time);
  109. s_swap_cached_credentials(provider, credentials);
  110. aws_mutex_unlock(&impl->lock);
  111. AWS_LOGF_DEBUG(
  112. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  113. "(id=%p) Cached credentials provider notifying pending queries of new credentials",
  114. (void *)provider);
  115. s_aws_credentials_query_list_notify_and_clean_up(&pending_queries, provider->allocator, credentials, error_code);
  116. }
  117. static int s_cached_credentials_provider_get_credentials_async(
  118. struct aws_credentials_provider *provider,
  119. aws_on_get_credentials_callback_fn callback,
  120. void *user_data) {
  121. struct aws_credentials_provider_cached *impl = provider->impl;
  122. uint64_t current_time = 0;
  123. impl->high_res_clock_fn(&current_time);
  124. bool should_submit_query = false;
  125. bool perform_callback = false;
  126. struct aws_credentials *credentials = NULL;
  127. aws_mutex_lock(&impl->lock);
  128. if (impl->cached_credentials != NULL && current_time < impl->next_refresh_time) {
  129. perform_callback = true;
  130. credentials = impl->cached_credentials;
  131. aws_credentials_acquire(credentials);
  132. } else {
  133. struct aws_credentials_query *query =
  134. aws_mem_acquire(provider->allocator, sizeof(struct aws_credentials_query));
  135. if (query != NULL) {
  136. aws_credentials_query_init(query, provider, callback, user_data);
  137. should_submit_query = aws_linked_list_empty(&impl->pending_queries);
  138. aws_linked_list_push_back(&impl->pending_queries, &query->node);
  139. } else {
  140. perform_callback = true;
  141. }
  142. }
  143. aws_mutex_unlock(&impl->lock);
  144. if (should_submit_query) {
  145. AWS_LOGF_INFO(
  146. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  147. "(id=%p) Cached credentials provider has expired credentials. Requerying.",
  148. (void *)provider);
  149. aws_credentials_provider_get_credentials(
  150. impl->source, s_cached_credentials_provider_get_credentials_async_callback, provider);
  151. } else if (!perform_callback) {
  152. AWS_LOGF_DEBUG(
  153. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  154. "(id=%p) Cached credentials provider has expired credentials. Waiting on existing query.",
  155. (void *)provider);
  156. }
  157. if (perform_callback) {
  158. if (credentials != NULL) {
  159. AWS_LOGF_DEBUG(
  160. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  161. "(id=%p) Cached credentials provider successfully sourced from cache",
  162. (void *)provider);
  163. } else {
  164. AWS_LOGF_DEBUG(
  165. AWS_LS_AUTH_CREDENTIALS_PROVIDER,
  166. "(id=%p) Cached credentials provider failed to source credentials while skipping requery",
  167. (void *)provider);
  168. }
  169. callback(credentials, (credentials != NULL) ? AWS_ERROR_SUCCESS : aws_last_error(), user_data);
  170. aws_credentials_release(credentials);
  171. }
  172. return AWS_OP_SUCCESS;
  173. }
  174. static void s_cached_credentials_provider_destroy(struct aws_credentials_provider *provider) {
  175. struct aws_credentials_provider_cached *impl = provider->impl;
  176. if (impl == NULL) {
  177. return;
  178. }
  179. aws_credentials_provider_release(impl->source);
  180. /* Invoke our own shutdown callback */
  181. aws_credentials_provider_invoke_shutdown_callback(provider);
  182. if (impl->cached_credentials != NULL) {
  183. aws_credentials_release(impl->cached_credentials);
  184. }
  185. aws_mutex_clean_up(&impl->lock);
  186. aws_mem_release(provider->allocator, provider);
  187. }
  188. static struct aws_credentials_provider_vtable s_aws_credentials_provider_cached_vtable = {
  189. .get_credentials = s_cached_credentials_provider_get_credentials_async,
  190. .destroy = s_cached_credentials_provider_destroy,
  191. };
  192. struct aws_credentials_provider *aws_credentials_provider_new_cached(
  193. struct aws_allocator *allocator,
  194. const struct aws_credentials_provider_cached_options *options) {
  195. AWS_ASSERT(options->source != NULL);
  196. struct aws_credentials_provider *provider = NULL;
  197. struct aws_credentials_provider_cached *impl = NULL;
  198. aws_mem_acquire_many(
  199. allocator,
  200. 2,
  201. &provider,
  202. sizeof(struct aws_credentials_provider),
  203. &impl,
  204. sizeof(struct aws_credentials_provider_cached));
  205. if (!provider) {
  206. return NULL;
  207. }
  208. AWS_ZERO_STRUCT(*provider);
  209. AWS_ZERO_STRUCT(*impl);
  210. aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_cached_vtable, impl);
  211. if (aws_mutex_init(&impl->lock)) {
  212. goto on_error;
  213. }
  214. aws_linked_list_init(&impl->pending_queries);
  215. impl->source = options->source;
  216. aws_credentials_provider_acquire(impl->source);
  217. if (options->refresh_time_in_milliseconds > 0) {
  218. impl->refresh_interval_in_ns = aws_timestamp_convert(
  219. options->refresh_time_in_milliseconds, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL);
  220. } else {
  221. /*
  222. * TODO: query AWS_CREDENTIAL_EXPIRATION for a refresh override
  223. *
  224. * This must be an ISO 8601 time interval which we don't have a parser for yet (one could be cobbled
  225. * together from the existing timestamp parser). Does not seem important enough to get bogged down in atm.
  226. * Punting for now.
  227. */
  228. impl->refresh_interval_in_ns = 0;
  229. }
  230. if (options->high_res_clock_fn != NULL) {
  231. impl->high_res_clock_fn = options->high_res_clock_fn;
  232. } else {
  233. impl->high_res_clock_fn = &aws_high_res_clock_get_ticks;
  234. }
  235. if (options->system_clock_fn != NULL) {
  236. impl->system_clock_fn = options->system_clock_fn;
  237. } else {
  238. impl->system_clock_fn = &aws_sys_clock_get_ticks;
  239. }
  240. provider->shutdown_options = options->shutdown_options;
  241. return provider;
  242. on_error:
  243. aws_credentials_provider_destroy(provider);
  244. return NULL;
  245. }