1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5
6 #include <aws/auth/credentials.h>
7
8 #include <aws/auth/external/cJSON.h>
9 #include <aws/auth/private/aws_profile.h>
10 #include <aws/auth/private/credentials_utils.h>
11 #include <aws/common/clock.h>
12 #include <aws/common/date_time.h>
13 #include <aws/common/environment.h>
14 #include <aws/common/string.h>
15 #include <aws/common/uuid.h>
16 #include <aws/common/xml_parser.h>
17 #include <aws/http/connection.h>
18 #include <aws/http/connection_manager.h>
19 #include <aws/http/request_response.h>
20 #include <aws/http/status_code.h>
21 #include <aws/io/file_utils.h>
22 #include <aws/io/logging.h>
23 #include <aws/io/socket.h>
24 #include <aws/io/stream.h>
25 #include <aws/io/tls_channel_handler.h>
26 #include <aws/io/uri.h>
27 #include <inttypes.h>
28
29 #if defined(_MSC_VER)
30 # pragma warning(disable : 4204)
31 # pragma warning(disable : 4232)
32 #endif /* _MSC_VER */
33
34 #define STS_WEB_IDENTITY_RESPONSE_SIZE_INITIAL 2048
35 #define STS_WEB_IDENTITY_RESPONSE_SIZE_LIMIT 10000
36 #define STS_WEB_IDENTITY_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS 2
37 #define STS_WEB_IDENTITY_CREDS_DEFAULT_DURATION_SECONDS 900
38 #define STS_WEB_IDENTITY_MAX_ATTEMPTS 3
39
40 static void s_on_connection_manager_shutdown(void *user_data);
41
42 struct aws_credentials_provider_sts_web_identity_impl {
43 struct aws_http_connection_manager *connection_manager;
44 struct aws_auth_http_system_vtable *function_table;
45 struct aws_string *role_arn;
46 struct aws_string *role_session_name;
47 struct aws_string *token_file_path;
48 };
49
50 static struct aws_auth_http_system_vtable s_default_function_table = {
51 .aws_http_connection_manager_new = aws_http_connection_manager_new,
52 .aws_http_connection_manager_release = aws_http_connection_manager_release,
53 .aws_http_connection_manager_acquire_connection = aws_http_connection_manager_acquire_connection,
54 .aws_http_connection_manager_release_connection = aws_http_connection_manager_release_connection,
55 .aws_http_connection_make_request = aws_http_connection_make_request,
56 .aws_http_stream_activate = aws_http_stream_activate,
57 .aws_http_stream_get_connection = aws_http_stream_get_connection,
58 .aws_http_stream_get_incoming_response_status = aws_http_stream_get_incoming_response_status,
59 .aws_http_stream_release = aws_http_stream_release,
60 .aws_http_connection_close = aws_http_connection_close};
61
62 /*
63 * Tracking structure for each outstanding async query to an sts_web_identity provider
64 */
65 struct sts_web_identity_user_data {
66 /* immutable post-creation */
67 struct aws_allocator *allocator;
68 struct aws_credentials_provider *sts_web_identity_provider;
69 aws_on_get_credentials_callback_fn *original_callback;
70 void *original_user_data;
71
72 /* mutable */
73 struct aws_http_connection *connection;
74 struct aws_http_message *request;
75 struct aws_byte_buf response;
76
77 struct aws_string *access_key_id;
78 struct aws_string *secret_access_key;
79 struct aws_string *session_token;
80 uint64_t expiration_timepoint_in_seconds;
81
82 struct aws_byte_buf payload_buf;
83
84 int status_code;
85 int error_code;
86 int attempt_count;
87 };
88
s_user_data_reset_request_and_response(struct sts_web_identity_user_data * user_data)89 static void s_user_data_reset_request_and_response(struct sts_web_identity_user_data *user_data) {
90 aws_byte_buf_reset(&user_data->response, true /*zero out*/);
91 aws_byte_buf_reset(&user_data->payload_buf, true /*zero out*/);
92 user_data->status_code = 0;
93 if (user_data->request) {
94 aws_input_stream_destroy(aws_http_message_get_body_stream(user_data->request));
95 }
96 aws_http_message_destroy(user_data->request);
97 user_data->request = NULL;
98
99 aws_string_destroy(user_data->access_key_id);
100 user_data->access_key_id = NULL;
101
102 aws_string_destroy_secure(user_data->secret_access_key);
103 user_data->secret_access_key = NULL;
104
105 aws_string_destroy_secure(user_data->session_token);
106 user_data->session_token = NULL;
107 }
108
s_user_data_destroy(struct sts_web_identity_user_data * user_data)109 static void s_user_data_destroy(struct sts_web_identity_user_data *user_data) {
110 if (user_data == NULL) {
111 return;
112 }
113
114 struct aws_credentials_provider_sts_web_identity_impl *impl = user_data->sts_web_identity_provider->impl;
115
116 if (user_data->connection) {
117 impl->function_table->aws_http_connection_manager_release_connection(
118 impl->connection_manager, user_data->connection);
119 }
120 s_user_data_reset_request_and_response(user_data);
121 aws_byte_buf_clean_up(&user_data->response);
122
123 aws_string_destroy(user_data->access_key_id);
124 aws_string_destroy_secure(user_data->secret_access_key);
125 aws_string_destroy_secure(user_data->session_token);
126
127 aws_byte_buf_clean_up(&user_data->payload_buf);
128
129 aws_credentials_provider_release(user_data->sts_web_identity_provider);
130 aws_mem_release(user_data->allocator, user_data);
131 }
132
s_user_data_new(struct aws_credentials_provider * sts_web_identity_provider,aws_on_get_credentials_callback_fn callback,void * user_data)133 static struct sts_web_identity_user_data *s_user_data_new(
134 struct aws_credentials_provider *sts_web_identity_provider,
135 aws_on_get_credentials_callback_fn callback,
136 void *user_data) {
137
138 struct sts_web_identity_user_data *wrapped_user_data =
139 aws_mem_calloc(sts_web_identity_provider->allocator, 1, sizeof(struct sts_web_identity_user_data));
140 if (wrapped_user_data == NULL) {
141 goto on_error;
142 }
143
144 wrapped_user_data->allocator = sts_web_identity_provider->allocator;
145 wrapped_user_data->sts_web_identity_provider = sts_web_identity_provider;
146 aws_credentials_provider_acquire(sts_web_identity_provider);
147 wrapped_user_data->original_user_data = user_data;
148 wrapped_user_data->original_callback = callback;
149
150 if (aws_byte_buf_init(
151 &wrapped_user_data->response,
152 sts_web_identity_provider->allocator,
153 STS_WEB_IDENTITY_RESPONSE_SIZE_INITIAL)) {
154 goto on_error;
155 }
156
157 if (aws_byte_buf_init(&wrapped_user_data->payload_buf, sts_web_identity_provider->allocator, 1024)) {
158 goto on_error;
159 }
160
161 return wrapped_user_data;
162
163 on_error:
164
165 s_user_data_destroy(wrapped_user_data);
166
167 return NULL;
168 }
169
170 /*
171 * In general, the STS_WEB_IDENTITY response document looks something like:
172 <AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
173 <AssumeRoleWithWebIdentityResult>
174 <SubjectFromWebIdentityToken>amzn1.account.AF6RHO7KZU5XRVQJGXK6HB56KR2A</SubjectFromWebIdentityToken>
175 <Audience>client.5498841531868486423.1548@apps.example.com</Audience>
176 <AssumedRoleUser>
177 <Arn>arn:aws:sts::123456789012:assumed-role/FederatedWebIdentityRole/app1</Arn>
178 <AssumedRoleId>AROACLKWSDQRAOEXAMPLE:app1</AssumedRoleId>
179 </AssumedRoleUser>
180 <Credentials>
181 <SessionToken>AQoDYXdzEE0a8ANXXXXXXXXNO1ewxE5TijQyp+IEXAMPLE</SessionToken>
182 <SecretAccessKey>wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY</SecretAccessKey>
183 <Expiration>2014-10-24T23:00:23Z</Expiration>
184 <AccessKeyId>ASgeIAIOSFODNN7EXAMPLE</AccessKeyId>
185 </Credentials>
186 <Provider>www.amazon.com</Provider>
187 </AssumeRoleWithWebIdentityResult>
188 <ResponseMetadata>
189 <RequestId>ad4156e9-bce1-11e2-82e6-6b6efEXAMPLE</RequestId>
190 </ResponseMetadata>
191 </AssumeRoleWithWebIdentityResponse>
192
193 Error Response looks like:
194 <?xml version="1.0" encoding="UTF-8"?>
195 <Error>
196 <Code>ExceptionName</Code>
197 <Message>XXX</Message>
198 <Resource>YYY</Resource>
199 <RequestId>4442587FB7D0A2F9</RequestId>
200 </Error>
201 */
202
s_on_error_node_encountered_fn(struct aws_xml_parser * parser,struct aws_xml_node * node,void * user_data)203 static bool s_on_error_node_encountered_fn(struct aws_xml_parser *parser, struct aws_xml_node *node, void *user_data) {
204
205 struct aws_byte_cursor node_name;
206 AWS_ZERO_STRUCT(node_name);
207
208 if (aws_xml_node_get_name(node, &node_name)) {
209 AWS_LOGF_ERROR(
210 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
211 "(id=%p): While parsing xml error response for sts web identity credentials provider, could not get xml "
212 "node name for function s_on_error_node_encountered_fn.",
213 user_data);
214 return false;
215 }
216
217 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "Error")) {
218 return aws_xml_node_traverse(parser, node, s_on_error_node_encountered_fn, user_data);
219 }
220
221 bool *get_retryable_error = user_data;
222 struct aws_byte_cursor data_cursor;
223 AWS_ZERO_STRUCT(data_cursor);
224
225 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "Code")) {
226 aws_xml_node_as_body(parser, node, &data_cursor);
227 if (aws_byte_cursor_eq_c_str_ignore_case(&data_cursor, "IDPCommunicationError") ||
228 aws_byte_cursor_eq_c_str_ignore_case(&data_cursor, "InvalidIdentityToken")) {
229 *get_retryable_error = true;
230 }
231 }
232
233 return true;
234 }
235
s_parse_retryable_error_from_response(struct aws_allocator * allocator,struct aws_byte_buf * response)236 static bool s_parse_retryable_error_from_response(struct aws_allocator *allocator, struct aws_byte_buf *response) {
237
238 struct aws_xml_parser_options options;
239 AWS_ZERO_STRUCT(options);
240 options.doc = aws_byte_cursor_from_buf(response);
241
242 struct aws_xml_parser *xml_parser = aws_xml_parser_new(allocator, &options);
243
244 if (xml_parser == NULL) {
245 AWS_LOGF_ERROR(
246 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
247 "Failed to init xml parser for sts web identity credentials provider to parse error information.")
248 return false;
249 }
250 bool get_retryable_error = false;
251 if (aws_xml_parser_parse(xml_parser, s_on_error_node_encountered_fn, &get_retryable_error)) {
252 AWS_LOGF_ERROR(
253 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
254 "Failed to parse xml error response for sts web identity with error %s",
255 aws_error_str(aws_last_error()));
256 aws_xml_parser_destroy(xml_parser);
257 return false;
258 }
259
260 aws_xml_parser_destroy(xml_parser);
261 return get_retryable_error;
262 }
263
s_on_creds_node_encountered_fn(struct aws_xml_parser * parser,struct aws_xml_node * node,void * user_data)264 static bool s_on_creds_node_encountered_fn(struct aws_xml_parser *parser, struct aws_xml_node *node, void *user_data) {
265
266 struct aws_byte_cursor node_name;
267 AWS_ZERO_STRUCT(node_name);
268
269 if (aws_xml_node_get_name(node, &node_name)) {
270 AWS_LOGF_ERROR(
271 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
272 "(id=%p): While parsing credentials xml response for sts web identity credentials provider, could not get "
273 "xml node name for function s_on_creds_node_encountered_fn.",
274 user_data);
275 return false;
276 }
277
278 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "AssumeRoleWithWebIdentityResponse") ||
279 aws_byte_cursor_eq_c_str_ignore_case(&node_name, "AssumeRoleWithWebIdentityResult") ||
280 aws_byte_cursor_eq_c_str_ignore_case(&node_name, "Credentials")) {
281 return aws_xml_node_traverse(parser, node, s_on_creds_node_encountered_fn, user_data);
282 }
283
284 struct sts_web_identity_user_data *query_user_data = user_data;
285 struct aws_byte_cursor credential_data;
286 AWS_ZERO_STRUCT(credential_data);
287 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "AccessKeyId")) {
288 aws_xml_node_as_body(parser, node, &credential_data);
289 query_user_data->access_key_id =
290 aws_string_new_from_array(query_user_data->allocator, credential_data.ptr, credential_data.len);
291 }
292
293 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "SecretAccessKey")) {
294 aws_xml_node_as_body(parser, node, &credential_data);
295 query_user_data->secret_access_key =
296 aws_string_new_from_array(query_user_data->allocator, credential_data.ptr, credential_data.len);
297 }
298
299 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "SessionToken")) {
300 aws_xml_node_as_body(parser, node, &credential_data);
301 query_user_data->session_token =
302 aws_string_new_from_array(query_user_data->allocator, credential_data.ptr, credential_data.len);
303 }
304
305 /* As long as we parsed an usable expiration, use it, otherwise use
306 * the existing one: now + 900s, initialized before parsing.
307 */
308 if (aws_byte_cursor_eq_c_str_ignore_case(&node_name, "Expiration")) {
309 aws_xml_node_as_body(parser, node, &credential_data);
310 if (credential_data.len != 0) {
311 struct aws_date_time expiration;
312 if (aws_date_time_init_from_str_cursor(&expiration, &credential_data, AWS_DATE_FORMAT_ISO_8601) ==
313 AWS_OP_SUCCESS) {
314 query_user_data->expiration_timepoint_in_seconds = (uint64_t)aws_date_time_as_epoch_secs(&expiration);
315 } else {
316 query_user_data->error_code = aws_last_error();
317 AWS_LOGF_ERROR(
318 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
319 "Failed to parse time string from sts web identity xml response: %s",
320 aws_error_str(query_user_data->error_code));
321 }
322 }
323 }
324 return true;
325 }
326
s_parse_credentials_from_response(struct sts_web_identity_user_data * query_user_data,struct aws_byte_buf * response)327 static struct aws_credentials *s_parse_credentials_from_response(
328 struct sts_web_identity_user_data *query_user_data,
329 struct aws_byte_buf *response) {
330
331 if (!response || response->len == 0) {
332 return NULL;
333 }
334
335 struct aws_credentials *credentials = NULL;
336
337 struct aws_xml_parser_options options;
338 AWS_ZERO_STRUCT(options);
339 options.doc = aws_byte_cursor_from_buf(response);
340
341 struct aws_xml_parser *xml_parser = aws_xml_parser_new(query_user_data->allocator, &options);
342
343 if (xml_parser == NULL) {
344 AWS_LOGF_ERROR(
345 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
346 "Failed to init xml parser for sts web identity credentials provider to parse error information.")
347 return NULL;
348 }
349 uint64_t now = UINT64_MAX;
350 if (aws_sys_clock_get_ticks(&now) != AWS_OP_SUCCESS) {
351 AWS_LOGF_ERROR(
352 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
353 "Failed to get sys clock for sts web identity credentials provider to parse error information.")
354 goto on_finish;
355 }
356 uint64_t now_seconds = aws_timestamp_convert(now, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL);
357 query_user_data->expiration_timepoint_in_seconds = now_seconds + STS_WEB_IDENTITY_CREDS_DEFAULT_DURATION_SECONDS;
358
359 if (aws_xml_parser_parse(xml_parser, s_on_creds_node_encountered_fn, query_user_data)) {
360 AWS_LOGF_ERROR(
361 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
362 "Failed to parse xml response for sts web identity with error: %s",
363 aws_error_str(aws_last_error()));
364 goto on_finish;
365 }
366
367 if (!query_user_data->access_key_id || !query_user_data->secret_access_key) {
368 goto on_finish;
369 }
370
371 credentials = aws_credentials_new(
372 query_user_data->allocator,
373 aws_byte_cursor_from_string(query_user_data->access_key_id),
374 aws_byte_cursor_from_string(query_user_data->secret_access_key),
375 aws_byte_cursor_from_string(query_user_data->session_token),
376 query_user_data->expiration_timepoint_in_seconds);
377
378 on_finish:
379
380 if (credentials == NULL) {
381 query_user_data->error_code = aws_last_error();
382 }
383
384 if (xml_parser != NULL) {
385 aws_xml_parser_destroy(xml_parser);
386 xml_parser = NULL;
387 }
388
389 return credentials;
390 }
391
392 /*
393 * No matter the result, this always gets called assuming that user_data is successfully allocated
394 */
s_finalize_get_credentials_query(struct sts_web_identity_user_data * user_data)395 static void s_finalize_get_credentials_query(struct sts_web_identity_user_data *user_data) {
396 /* Try to build credentials from whatever, if anything, was in the result */
397 struct aws_credentials *credentials = NULL;
398 if (user_data->status_code == AWS_HTTP_STATUS_CODE_200_OK) {
399 credentials = s_parse_credentials_from_response(user_data, &user_data->response);
400 }
401
402 if (credentials != NULL) {
403 AWS_LOGF_INFO(
404 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
405 "(id=%p) STS_WEB_IDENTITY credentials provider successfully queried credentials",
406 (void *)user_data->sts_web_identity_provider);
407 } else {
408 AWS_LOGF_WARN(
409 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
410 "(id=%p) STS_WEB_IDENTITY credentials provider failed to query credentials",
411 (void *)user_data->sts_web_identity_provider);
412
413 if (user_data->error_code == AWS_ERROR_SUCCESS) {
414 user_data->error_code = AWS_AUTH_CREDENTIALS_PROVIDER_STS_WEB_IDENTITY_SOURCE_FAILURE;
415 }
416 }
417
418 /* pass the credentials back */
419 user_data->original_callback(credentials, user_data->error_code, user_data->original_user_data);
420
421 /* clean up */
422 s_user_data_destroy(user_data);
423 aws_credentials_release(credentials);
424 }
425
s_on_incoming_body_fn(struct aws_http_stream * stream,const struct aws_byte_cursor * body,void * wrapped_user_data)426 static int s_on_incoming_body_fn(
427 struct aws_http_stream *stream,
428 const struct aws_byte_cursor *body,
429 void *wrapped_user_data) {
430
431 (void)stream;
432
433 struct sts_web_identity_user_data *user_data = wrapped_user_data;
434 struct aws_credentials_provider_sts_web_identity_impl *impl = user_data->sts_web_identity_provider->impl;
435
436 AWS_LOGF_TRACE(
437 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
438 "(id=%p) STS_WEB_IDENTITY credentials provider received %zu response bytes",
439 (void *)user_data->sts_web_identity_provider,
440 body->len);
441
442 if (body->len + user_data->response.len > STS_WEB_IDENTITY_RESPONSE_SIZE_LIMIT) {
443 impl->function_table->aws_http_connection_close(user_data->connection);
444 AWS_LOGF_ERROR(
445 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
446 "(id=%p) STS_WEB_IDENTITY credentials provider query response exceeded maximum allowed length",
447 (void *)user_data->sts_web_identity_provider);
448
449 return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
450 }
451
452 if (aws_byte_buf_append_dynamic(&user_data->response, body)) {
453 impl->function_table->aws_http_connection_close(user_data->connection);
454 AWS_LOGF_ERROR(
455 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
456 "(id=%p) STS_WEB_IDENTITY credentials provider query error appending response: %s",
457 (void *)user_data->sts_web_identity_provider,
458 aws_error_str(aws_last_error()));
459
460 return AWS_OP_ERR;
461 }
462
463 return AWS_OP_SUCCESS;
464 }
465
s_on_incoming_headers_fn(struct aws_http_stream * stream,enum aws_http_header_block header_block,const struct aws_http_header * header_array,size_t num_headers,void * wrapped_user_data)466 static int s_on_incoming_headers_fn(
467 struct aws_http_stream *stream,
468 enum aws_http_header_block header_block,
469 const struct aws_http_header *header_array,
470 size_t num_headers,
471 void *wrapped_user_data) {
472
473 (void)header_array;
474 (void)num_headers;
475
476 if (header_block != AWS_HTTP_HEADER_BLOCK_MAIN) {
477 return AWS_OP_SUCCESS;
478 }
479
480 struct sts_web_identity_user_data *user_data = wrapped_user_data;
481 if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) {
482 if (user_data->status_code == 0) {
483 struct aws_credentials_provider_sts_web_identity_impl *impl = user_data->sts_web_identity_provider->impl;
484 if (impl->function_table->aws_http_stream_get_incoming_response_status(stream, &user_data->status_code)) {
485 AWS_LOGF_ERROR(
486 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
487 "(id=%p) STS_WEB_IDENTITY credentials provider failed to get http status code: %s",
488 (void *)user_data->sts_web_identity_provider,
489 aws_error_str(aws_last_error()));
490
491 return AWS_OP_ERR;
492 }
493 AWS_LOGF_DEBUG(
494 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
495 "(id=%p) STS_WEB_IDENTITY credentials provider query received http status code %d",
496 (void *)user_data->sts_web_identity_provider,
497 user_data->status_code);
498 }
499 }
500
501 return AWS_OP_SUCCESS;
502 }
503
504 static void s_query_credentials(struct sts_web_identity_user_data *user_data);
505
s_on_stream_complete_fn(struct aws_http_stream * stream,int error_code,void * data)506 static void s_on_stream_complete_fn(struct aws_http_stream *stream, int error_code, void *data) {
507 struct sts_web_identity_user_data *user_data = data;
508
509 struct aws_credentials_provider_sts_web_identity_impl *impl = user_data->sts_web_identity_provider->impl;
510 struct aws_http_connection *connection = impl->function_table->aws_http_stream_get_connection(stream);
511 impl->function_table->aws_http_stream_release(stream);
512 impl->function_table->aws_http_connection_manager_release_connection(impl->connection_manager, connection);
513
514 /*
515 * On anything other than a 200, if we can retry the request based on
516 * error response, retry it, otherwise, call the finalize function.
517 */
518 if (user_data->status_code != AWS_HTTP_STATUS_CODE_200_OK || error_code != AWS_OP_SUCCESS) {
519 if (++user_data->attempt_count < STS_WEB_IDENTITY_MAX_ATTEMPTS && user_data->response.len) {
520 if (s_parse_retryable_error_from_response(user_data->allocator, &user_data->response)) {
521 s_query_credentials(user_data);
522 return;
523 }
524 }
525 }
526
527 s_finalize_get_credentials_query(user_data);
528 }
529
530 static struct aws_http_header s_host_header = {
531 .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("host"),
532 .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("sts.amazonaws.com"),
533 };
534
535 static struct aws_http_header s_content_type_header = {
536 .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("content-type"),
537 .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("application/x-www-form-urlencoded"),
538 };
539
540 static struct aws_http_header s_api_version_header = {
541 .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-api-version"),
542 .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("2011-06-15"),
543 };
544 static struct aws_http_header s_accept_header = {
545 .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Accept"),
546 .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("*/*"),
547 };
548
549 static struct aws_http_header s_user_agent_header = {
550 .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("User-Agent"),
551 .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws-sdk-crt/sts-web-identity-credentials-provider"),
552 };
553
554 static struct aws_http_header s_keep_alive_header = {
555 .name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
556 .value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("keep-alive"),
557 };
558
559 static struct aws_byte_cursor s_content_length = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("content-length");
560 static struct aws_byte_cursor s_path = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("/");
561
s_make_sts_web_identity_http_query(struct sts_web_identity_user_data * user_data,struct aws_byte_cursor * body_cursor)562 static int s_make_sts_web_identity_http_query(
563 struct sts_web_identity_user_data *user_data,
564 struct aws_byte_cursor *body_cursor) {
565 AWS_FATAL_ASSERT(user_data->connection);
566
567 struct aws_http_stream *stream = NULL;
568 struct aws_input_stream *input_stream = NULL;
569 struct aws_http_message *request = aws_http_message_new_request(user_data->allocator);
570 if (request == NULL) {
571 return AWS_OP_ERR;
572 }
573
574 struct aws_credentials_provider_sts_web_identity_impl *impl = user_data->sts_web_identity_provider->impl;
575
576 char content_length[21];
577 AWS_ZERO_ARRAY(content_length);
578 snprintf(content_length, sizeof(content_length), "%" PRIu64, (uint64_t)body_cursor->len);
579
580 struct aws_http_header content_len_header = {
581 .name = s_content_length,
582 .value = aws_byte_cursor_from_c_str(content_length),
583 };
584
585 if (aws_http_message_add_header(request, content_len_header)) {
586 goto on_error;
587 }
588
589 if (aws_http_message_add_header(request, s_content_type_header)) {
590 goto on_error;
591 }
592
593 if (aws_http_message_add_header(request, s_host_header)) {
594 goto on_error;
595 }
596
597 if (aws_http_message_add_header(request, s_api_version_header)) {
598 goto on_error;
599 }
600
601 if (aws_http_message_add_header(request, s_accept_header)) {
602 goto on_error;
603 }
604
605 if (aws_http_message_add_header(request, s_user_agent_header)) {
606 goto on_error;
607 }
608
609 if (aws_http_message_add_header(request, s_keep_alive_header)) {
610 goto on_error;
611 }
612
613 input_stream = aws_input_stream_new_from_cursor(user_data->allocator, body_cursor);
614 if (!input_stream) {
615 goto on_error;
616 }
617
618 aws_http_message_set_body_stream(request, input_stream);
619
620 if (aws_http_message_set_request_path(request, s_path)) {
621 goto on_error;
622 }
623
624 if (aws_http_message_set_request_method(request, aws_http_method_post)) {
625 goto on_error;
626 }
627
628 user_data->request = request;
629
630 struct aws_http_make_request_options request_options = {
631 .self_size = sizeof(request_options),
632 .on_response_headers = s_on_incoming_headers_fn,
633 .on_response_header_block_done = NULL,
634 .on_response_body = s_on_incoming_body_fn,
635 .on_complete = s_on_stream_complete_fn,
636 .user_data = user_data,
637 .request = request,
638 };
639
640 stream = impl->function_table->aws_http_connection_make_request(user_data->connection, &request_options);
641
642 if (!stream) {
643 goto on_error;
644 }
645
646 if (impl->function_table->aws_http_stream_activate(stream)) {
647 goto on_error;
648 }
649
650 return AWS_OP_SUCCESS;
651
652 on_error:
653 impl->function_table->aws_http_stream_release(stream);
654 aws_input_stream_destroy(input_stream);
655 aws_http_message_destroy(request);
656 user_data->request = NULL;
657 return AWS_OP_ERR;
658 }
659
s_query_credentials(struct sts_web_identity_user_data * user_data)660 static void s_query_credentials(struct sts_web_identity_user_data *user_data) {
661 AWS_FATAL_ASSERT(user_data->connection);
662
663 struct aws_credentials_provider_sts_web_identity_impl *impl = user_data->sts_web_identity_provider->impl;
664
665 /* "Clear" the result */
666 s_user_data_reset_request_and_response(user_data);
667
668 /*
669 * Calculate body message:
670 * "Action=AssumeRoleWithWebIdentity"
671 * + "&Version=2011-06-15"
672 * + "&RoleSessionName=" + url_encode(role_session_name)
673 * + "&RoleArn=" + url_encode(role_arn)
674 * + "&WebIdentityToken=" + url_encode(token);
675 */
676 struct aws_byte_buf token_buf;
677 bool success = false;
678
679 AWS_ZERO_STRUCT(token_buf);
680
681 struct aws_byte_cursor work_cursor =
682 aws_byte_cursor_from_c_str("Action=AssumeRoleWithWebIdentity&Version=2011-06-15&RoleArn=");
683 if (aws_byte_buf_append_dynamic(&user_data->payload_buf, &work_cursor)) {
684 goto on_finish;
685 }
686
687 work_cursor = aws_byte_cursor_from_string(impl->role_arn);
688 if (aws_byte_buf_append_encoding_uri_param(&user_data->payload_buf, &work_cursor)) {
689 goto on_finish;
690 }
691
692 work_cursor = aws_byte_cursor_from_c_str("&RoleSessionName=");
693 if (aws_byte_buf_append_dynamic(&user_data->payload_buf, &work_cursor)) {
694 goto on_finish;
695 }
696
697 work_cursor = aws_byte_cursor_from_string(impl->role_session_name);
698 if (aws_byte_buf_append_encoding_uri_param(&user_data->payload_buf, &work_cursor)) {
699 goto on_finish;
700 }
701
702 work_cursor = aws_byte_cursor_from_c_str("&WebIdentityToken=");
703 if (aws_byte_buf_append_dynamic(&user_data->payload_buf, &work_cursor)) {
704 goto on_finish;
705 }
706
707 if (aws_byte_buf_init_from_file(&token_buf, user_data->allocator, aws_string_c_str(impl->token_file_path))) {
708 goto on_finish;
709 }
710 work_cursor = aws_byte_cursor_from_buf(&token_buf);
711 if (aws_byte_buf_append_encoding_uri_param(&user_data->payload_buf, &work_cursor)) {
712 goto on_finish;
713 }
714 struct aws_byte_cursor body_cursor = aws_byte_cursor_from_buf(&user_data->payload_buf);
715
716 if (s_make_sts_web_identity_http_query(user_data, &body_cursor) == AWS_OP_ERR) {
717 goto on_finish;
718 }
719 success = true;
720
721 on_finish:
722 aws_byte_buf_clean_up(&token_buf);
723 if (!success) {
724 s_finalize_get_credentials_query(user_data);
725 }
726 }
727
s_on_acquire_connection(struct aws_http_connection * connection,int error_code,void * data)728 static void s_on_acquire_connection(struct aws_http_connection *connection, int error_code, void *data) {
729 struct sts_web_identity_user_data *user_data = data;
730
731 if (connection == NULL) {
732 AWS_LOGF_WARN(
733 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
734 "id=%p: STS_WEB_IDENTITY provider failed to acquire a connection, error code %d(%s)",
735 (void *)user_data->sts_web_identity_provider,
736 error_code,
737 aws_error_str(error_code));
738
739 s_finalize_get_credentials_query(user_data);
740 return;
741 }
742
743 user_data->connection = connection;
744
745 s_query_credentials(user_data);
746 }
747
s_credentials_provider_sts_web_identity_get_credentials_async(struct aws_credentials_provider * provider,aws_on_get_credentials_callback_fn callback,void * user_data)748 static int s_credentials_provider_sts_web_identity_get_credentials_async(
749 struct aws_credentials_provider *provider,
750 aws_on_get_credentials_callback_fn callback,
751 void *user_data) {
752
753 struct aws_credentials_provider_sts_web_identity_impl *impl = provider->impl;
754
755 struct sts_web_identity_user_data *wrapped_user_data = s_user_data_new(provider, callback, user_data);
756 if (wrapped_user_data == NULL) {
757 goto error;
758 }
759
760 impl->function_table->aws_http_connection_manager_acquire_connection(
761 impl->connection_manager, s_on_acquire_connection, wrapped_user_data);
762
763 return AWS_OP_SUCCESS;
764
765 error:
766 s_user_data_destroy(wrapped_user_data);
767 return AWS_OP_ERR;
768 }
769
s_credentials_provider_sts_web_identity_destroy(struct aws_credentials_provider * provider)770 static void s_credentials_provider_sts_web_identity_destroy(struct aws_credentials_provider *provider) {
771 struct aws_credentials_provider_sts_web_identity_impl *impl = provider->impl;
772 if (impl == NULL) {
773 return;
774 }
775
776 aws_string_destroy(impl->role_arn);
777 aws_string_destroy(impl->role_session_name);
778 aws_string_destroy(impl->token_file_path);
779 /* aws_http_connection_manager_release will eventually leads to call of s_on_connection_manager_shutdown,
780 * which will do memory release for provider and impl. So We should be freeing impl
781 * related memory first, then call aws_http_connection_manager_release.
782 */
783 if (impl->connection_manager) {
784 impl->function_table->aws_http_connection_manager_release(impl->connection_manager);
785 } else {
786 /* If provider setup failed halfway through, connection_manager might not exist.
787 * In this case invoke shutdown completion callback directly to finish cleanup */
788 s_on_connection_manager_shutdown(provider);
789 }
790
791 /* freeing the provider takes place in the shutdown callback below */
792 }
793
794 static struct aws_credentials_provider_vtable s_aws_credentials_provider_sts_web_identity_vtable = {
795 .get_credentials = s_credentials_provider_sts_web_identity_get_credentials_async,
796 .destroy = s_credentials_provider_sts_web_identity_destroy,
797 };
798
s_on_connection_manager_shutdown(void * user_data)799 static void s_on_connection_manager_shutdown(void *user_data) {
800 struct aws_credentials_provider *provider = user_data;
801
802 aws_credentials_provider_invoke_shutdown_callback(provider);
803 aws_mem_release(provider->allocator, provider);
804 }
805
806 AWS_STATIC_STRING_FROM_LITERAL(s_region_config, "region");
807 AWS_STATIC_STRING_FROM_LITERAL(s_region_env, "AWS_DEFAULT_REGION");
808 AWS_STATIC_STRING_FROM_LITERAL(s_role_arn_config, "role_arn");
809 AWS_STATIC_STRING_FROM_LITERAL(s_role_arn_env, "AWS_ROLE_ARN");
810 AWS_STATIC_STRING_FROM_LITERAL(s_role_session_name_config, "role_session_name");
811 AWS_STATIC_STRING_FROM_LITERAL(s_role_session_name_env, "AWS_ROLE_SESSION_NAME");
812 AWS_STATIC_STRING_FROM_LITERAL(s_token_file_path_config, "web_identity_token_file");
813 AWS_STATIC_STRING_FROM_LITERAL(s_token_file_path_env, "AWS_WEB_IDENTITY_TOKEN_FILE");
814
815 struct sts_web_identity_parameters {
816 struct aws_allocator *allocator;
817 /* region is actually used to construct endpoint */
818 struct aws_byte_buf endpoint;
819 struct aws_byte_buf role_arn;
820 struct aws_byte_buf role_session_name;
821 struct aws_byte_buf token_file_path;
822 };
823
s_load_profile(struct aws_allocator * allocator)824 struct aws_profile_collection *s_load_profile(struct aws_allocator *allocator) {
825
826 struct aws_profile_collection *config_profiles = NULL;
827 struct aws_string *config_file_path = NULL;
828
829 config_file_path = aws_get_config_file_path(allocator, NULL);
830 if (!config_file_path) {
831 AWS_LOGF_ERROR(
832 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
833 "Failed to resolve config file path during sts web identity provider initialization: %s",
834 aws_error_str(aws_last_error()));
835 goto on_error;
836 }
837
838 config_profiles = aws_profile_collection_new_from_file(allocator, config_file_path, AWS_PST_CONFIG);
839 if (config_profiles != NULL) {
840 AWS_LOGF_DEBUG(
841 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
842 "Successfully built config profile collection from file at (%s)",
843 aws_string_c_str(config_file_path));
844 } else {
845 AWS_LOGF_ERROR(
846 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
847 "Failed to build config profile collection from file at (%s) : %s",
848 aws_string_c_str(config_file_path),
849 aws_error_str(aws_last_error()));
850 goto on_error;
851 }
852
853 aws_string_destroy(config_file_path);
854 return config_profiles;
855
856 on_error:
857 aws_string_destroy(config_file_path);
858 aws_profile_collection_destroy(config_profiles);
859 return NULL;
860 }
861
862 static struct aws_byte_cursor s_default_profile_name_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("default");
863 static struct aws_byte_cursor s_dot_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(".");
864 static struct aws_byte_cursor s_amazonaws_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(".amazonaws.com");
865 static struct aws_byte_cursor s_cn_cursor = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(".cn");
866 AWS_STATIC_STRING_FROM_LITERAL(s_sts_service_name, "sts");
867
s_construct_endpoint(struct aws_allocator * allocator,struct aws_byte_buf * endpoint,const struct aws_string * region,const struct aws_string * service_name)868 static int s_construct_endpoint(
869 struct aws_allocator *allocator,
870 struct aws_byte_buf *endpoint,
871 const struct aws_string *region,
872 const struct aws_string *service_name) {
873
874 if (!allocator || !endpoint || !region || !service_name) {
875 return AWS_ERROR_INVALID_ARGUMENT;
876 }
877 aws_byte_buf_clean_up(endpoint);
878
879 struct aws_byte_cursor service_cursor = aws_byte_cursor_from_string(service_name);
880 if (aws_byte_buf_init_copy_from_cursor(endpoint, allocator, service_cursor)) {
881 goto on_error;
882 }
883
884 if (aws_byte_buf_append_dynamic(endpoint, &s_dot_cursor)) {
885 goto on_error;
886 }
887
888 struct aws_byte_cursor region_cursor;
889 region_cursor = aws_byte_cursor_from_array(region->bytes, region->len);
890 if (aws_byte_buf_append_dynamic(endpoint, ®ion_cursor)) {
891 goto on_error;
892 }
893
894 if (aws_byte_buf_append_dynamic(endpoint, &s_amazonaws_cursor)) {
895 goto on_error;
896 }
897
898 if (aws_string_eq_c_str_ignore_case(region, "cn-north-1") ||
899 aws_string_eq_c_str_ignore_case(region, "cn-northwest-1")) {
900 if (aws_byte_buf_append_dynamic(endpoint, &s_cn_cursor)) {
901 goto on_error;
902 }
903 }
904 return AWS_OP_SUCCESS;
905
906 on_error:
907 aws_byte_buf_clean_up(endpoint);
908 return AWS_OP_ERR;
909 }
910
s_generate_uuid_to_buf(struct aws_allocator * allocator,struct aws_byte_buf * dst)911 static int s_generate_uuid_to_buf(struct aws_allocator *allocator, struct aws_byte_buf *dst) {
912
913 if (!allocator || !dst) {
914 return AWS_ERROR_INVALID_ARGUMENT;
915 }
916
917 struct aws_uuid uuid;
918 if (aws_uuid_init(&uuid)) {
919 AWS_LOGF_ERROR(
920 AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to initiate an uuid struct: %s", aws_error_str(aws_last_error()));
921 return aws_last_error();
922 }
923
924 char uuid_str[AWS_UUID_STR_LEN] = {0};
925 struct aws_byte_buf uuid_buf = aws_byte_buf_from_array(uuid_str, sizeof(uuid_str));
926 uuid_buf.len = 0;
927 if (aws_uuid_to_str(&uuid, &uuid_buf)) {
928 AWS_LOGF_ERROR(
929 AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to stringify uuid: %s", aws_error_str(aws_last_error()));
930 return aws_last_error();
931 }
932 if (aws_byte_buf_init_copy(dst, allocator, &uuid_buf)) {
933 AWS_LOGF_ERROR(
934 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
935 "Failed to generate role session name during sts web identity provider initialization: %s",
936 aws_error_str(aws_last_error()));
937 return aws_last_error();
938 }
939 return AWS_OP_SUCCESS;
940 }
941
s_check_or_get_with_profile_config(struct aws_allocator * allocator,const struct aws_profile * profile,struct aws_string ** target,const struct aws_string * config_key)942 static void s_check_or_get_with_profile_config(
943 struct aws_allocator *allocator,
944 const struct aws_profile *profile,
945 struct aws_string **target,
946 const struct aws_string *config_key) {
947
948 if (!allocator || !profile || !config_key) {
949 return;
950 }
951 if ((!(*target) || !(*target)->len)) {
952 if (*target) {
953 aws_string_destroy(*target);
954 }
955 const struct aws_profile_property *property = aws_profile_get_property(profile, config_key);
956 if (property) {
957 *target = aws_string_new_from_string(allocator, aws_profile_property_get_value(property));
958 }
959 }
960 }
961
s_parameters_destroy(struct sts_web_identity_parameters * parameters)962 static void s_parameters_destroy(struct sts_web_identity_parameters *parameters) {
963 if (!parameters) {
964 return;
965 }
966 aws_byte_buf_clean_up(¶meters->endpoint);
967 aws_byte_buf_clean_up(¶meters->role_arn);
968 aws_byte_buf_clean_up(¶meters->role_session_name);
969 aws_byte_buf_clean_up(¶meters->token_file_path);
970 aws_mem_release(parameters->allocator, parameters);
971 }
972
s_parameters_new(struct aws_allocator * allocator)973 static struct sts_web_identity_parameters *s_parameters_new(struct aws_allocator *allocator) {
974
975 struct sts_web_identity_parameters *parameters =
976 aws_mem_calloc(allocator, 1, sizeof(struct sts_web_identity_parameters));
977 if (parameters == NULL) {
978 return NULL;
979 }
980 parameters->allocator = allocator;
981
982 bool success = false;
983 struct aws_string *region = NULL;
984 struct aws_string *role_arn = NULL;
985 struct aws_string *role_session_name = NULL;
986 struct aws_string *token_file_path = NULL;
987
988 /* check environment variables */
989 aws_get_environment_value(allocator, s_region_env, ®ion);
990 aws_get_environment_value(allocator, s_role_arn_env, &role_arn);
991 aws_get_environment_value(allocator, s_role_session_name_env, &role_session_name);
992 aws_get_environment_value(allocator, s_token_file_path_env, &token_file_path);
993
994 /**
995 * check config profile if either region, role_arn or token_file_path or role_session_name is not resolved from
996 * environment variable. Role session name can also be generated by us using uuid if not found from both sources.
997 */
998 struct aws_profile_collection *config_profile = NULL;
999 struct aws_string *profile_name = NULL;
1000 const struct aws_profile *profile = NULL;
1001 bool get_all_parameters =
1002 (region && region->len && role_arn && role_arn->len && token_file_path && token_file_path->len);
1003 if (!get_all_parameters) {
1004 config_profile = s_load_profile(allocator);
1005 profile_name = aws_get_profile_name(allocator, &s_default_profile_name_cursor);
1006 if (config_profile && profile_name) {
1007 profile = aws_profile_collection_get_profile(config_profile, profile_name);
1008 }
1009
1010 if (!profile) {
1011 AWS_LOGF_ERROR(
1012 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
1013 "Failed to resolve either region, role arn or token file path during sts web identity provider "
1014 "initialization.")
1015 goto on_finish;
1016
1017 } else {
1018 s_check_or_get_with_profile_config(allocator, profile, ®ion, s_region_config);
1019 s_check_or_get_with_profile_config(allocator, profile, &role_arn, s_role_arn_config);
1020 s_check_or_get_with_profile_config(allocator, profile, &role_session_name, s_role_session_name_config);
1021 s_check_or_get_with_profile_config(allocator, profile, &token_file_path, s_token_file_path_config);
1022 }
1023 }
1024
1025 /* determin endpoint */
1026 if (s_construct_endpoint(allocator, ¶meters->endpoint, region, s_sts_service_name)) {
1027 AWS_LOGF_ERROR(
1028 AWS_LS_AUTH_CREDENTIALS_PROVIDER, "Failed to construct sts endpoint with, probably region is missing.");
1029 goto on_finish;
1030 }
1031
1032 /* determine role_arn */
1033 if (!role_arn || !role_arn->len ||
1034 aws_byte_buf_init_copy_from_cursor(¶meters->role_arn, allocator, aws_byte_cursor_from_string(role_arn))) {
1035 AWS_LOGF_ERROR(
1036 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
1037 "Failed to resolve role arn during sts web identity provider initialization.")
1038 goto on_finish;
1039 }
1040
1041 /* determine token_file_path */
1042 if (!token_file_path || !token_file_path->len ||
1043 aws_byte_buf_init_copy_from_cursor(
1044 ¶meters->token_file_path, allocator, aws_byte_cursor_from_string(token_file_path))) {
1045 AWS_LOGF_ERROR(
1046 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
1047 "Failed to resolve token file path during sts web identity provider initialization.")
1048 goto on_finish;
1049 }
1050
1051 /* determine role_session_name */
1052 if (role_session_name && role_session_name->len) {
1053 if (aws_byte_buf_init_copy_from_cursor(
1054 ¶meters->role_session_name, allocator, aws_byte_cursor_from_string(role_session_name))) {
1055 goto on_finish;
1056 }
1057 } else if (s_generate_uuid_to_buf(allocator, ¶meters->role_session_name)) {
1058 goto on_finish;
1059 }
1060
1061 AWS_LOGF_DEBUG(
1062 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
1063 "Successfully loaded all required parameters for sts web identity credentials provider.")
1064 success = true;
1065
1066 on_finish:
1067 aws_string_destroy(region);
1068 aws_string_destroy(role_arn);
1069 aws_string_destroy(role_session_name);
1070 aws_string_destroy(token_file_path);
1071 aws_string_destroy(profile_name);
1072 aws_profile_collection_destroy(config_profile);
1073 if (!success) {
1074 s_parameters_destroy(parameters);
1075 parameters = NULL;
1076 }
1077 return parameters;
1078 }
1079
aws_credentials_provider_new_sts_web_identity(struct aws_allocator * allocator,const struct aws_credentials_provider_sts_web_identity_options * options)1080 struct aws_credentials_provider *aws_credentials_provider_new_sts_web_identity(
1081 struct aws_allocator *allocator,
1082 const struct aws_credentials_provider_sts_web_identity_options *options) {
1083
1084 struct sts_web_identity_parameters *parameters = s_parameters_new(allocator);
1085 if (!parameters) {
1086 return NULL;
1087 }
1088
1089 struct aws_tls_connection_options tls_connection_options;
1090 AWS_ZERO_STRUCT(tls_connection_options);
1091
1092 struct aws_credentials_provider *provider = NULL;
1093 struct aws_credentials_provider_sts_web_identity_impl *impl = NULL;
1094
1095 aws_mem_acquire_many(
1096 allocator,
1097 2,
1098 &provider,
1099 sizeof(struct aws_credentials_provider),
1100 &impl,
1101 sizeof(struct aws_credentials_provider_sts_web_identity_impl));
1102
1103 if (!provider) {
1104 goto on_error;
1105 }
1106
1107 AWS_ZERO_STRUCT(*provider);
1108 AWS_ZERO_STRUCT(*impl);
1109
1110 aws_credentials_provider_init_base(provider, allocator, &s_aws_credentials_provider_sts_web_identity_vtable, impl);
1111
1112 if (!options->tls_ctx) {
1113 AWS_LOGF_ERROR(
1114 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
1115 "a TLS context must be provided to the STS web identity credentials provider");
1116 aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1117 return NULL;
1118 }
1119
1120 aws_tls_connection_options_init_from_ctx(&tls_connection_options, options->tls_ctx);
1121 struct aws_byte_cursor host = aws_byte_cursor_from_buf(¶meters->endpoint);
1122 if (aws_tls_connection_options_set_server_name(&tls_connection_options, allocator, &host)) {
1123 AWS_LOGF_ERROR(
1124 AWS_LS_AUTH_CREDENTIALS_PROVIDER,
1125 "(id=%p): failed to create a tls connection options with error %s",
1126 (void *)provider,
1127 aws_error_str(aws_last_error()));
1128 goto on_error;
1129 }
1130
1131 struct aws_socket_options socket_options;
1132 AWS_ZERO_STRUCT(socket_options);
1133 socket_options.type = AWS_SOCKET_STREAM;
1134 socket_options.domain = AWS_SOCKET_IPV4;
1135 socket_options.connect_timeout_ms = (uint32_t)aws_timestamp_convert(
1136 STS_WEB_IDENTITY_CONNECT_TIMEOUT_DEFAULT_IN_SECONDS, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL);
1137
1138 struct aws_http_connection_manager_options manager_options;
1139 AWS_ZERO_STRUCT(manager_options);
1140 manager_options.bootstrap = options->bootstrap;
1141 manager_options.initial_window_size = STS_WEB_IDENTITY_RESPONSE_SIZE_LIMIT;
1142 manager_options.socket_options = &socket_options;
1143 manager_options.host = host;
1144 manager_options.port = 443;
1145 manager_options.max_connections = 2;
1146 manager_options.shutdown_complete_callback = s_on_connection_manager_shutdown;
1147 manager_options.shutdown_complete_user_data = provider;
1148 manager_options.tls_connection_options = &tls_connection_options;
1149
1150 impl->function_table = options->function_table;
1151 if (impl->function_table == NULL) {
1152 impl->function_table = &s_default_function_table;
1153 }
1154
1155 impl->connection_manager = impl->function_table->aws_http_connection_manager_new(allocator, &manager_options);
1156 if (impl->connection_manager == NULL) {
1157 goto on_error;
1158 }
1159
1160 impl->role_arn = aws_string_new_from_array(allocator, parameters->role_arn.buffer, parameters->role_arn.len);
1161 if (impl->role_arn == NULL) {
1162 goto on_error;
1163 }
1164
1165 impl->role_session_name =
1166 aws_string_new_from_array(allocator, parameters->role_session_name.buffer, parameters->role_session_name.len);
1167 if (impl->role_session_name == NULL) {
1168 goto on_error;
1169 }
1170
1171 impl->token_file_path =
1172 aws_string_new_from_array(allocator, parameters->token_file_path.buffer, parameters->token_file_path.len);
1173 if (impl->token_file_path == NULL) {
1174 goto on_error;
1175 }
1176
1177 provider->shutdown_options = options->shutdown_options;
1178 s_parameters_destroy(parameters);
1179 aws_tls_connection_options_clean_up(&tls_connection_options);
1180 return provider;
1181
1182 on_error:
1183
1184 aws_credentials_provider_destroy(provider);
1185 s_parameters_destroy(parameters);
1186 aws_tls_connection_options_clean_up(&tls_connection_options);
1187 return NULL;
1188 }
1189