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, &region_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(&parameters->endpoint);
967     aws_byte_buf_clean_up(&parameters->role_arn);
968     aws_byte_buf_clean_up(&parameters->role_session_name);
969     aws_byte_buf_clean_up(&parameters->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, &region);
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, &region, 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, &parameters->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(&parameters->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             &parameters->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                 &parameters->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, &parameters->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(&parameters->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