1 /**
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0.
4 */
5
6 #include "aws/s3/private/s3_util.h"
7 #include "aws/s3/private/s3_client_impl.h"
8 #include <aws/auth/credentials.h>
9 #include <aws/common/string.h>
10 #include <aws/common/xml_parser.h>
11 #include <aws/http/request_response.h>
12 #include <aws/s3/s3.h>
13 #include <aws/s3/s3_client.h>
14 #include <inttypes.h>
15
16 #ifdef _MSC_VER
17 /* sscanf warning (not currently scanning for strings) */
18 # pragma warning(disable : 4996)
19 #endif
20
21 const struct aws_byte_cursor g_s3_client_version = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(AWS_S3_CLIENT_VERSION);
22 const struct aws_byte_cursor g_s3_service_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3");
23 const struct aws_byte_cursor g_host_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Host");
24 const struct aws_byte_cursor g_range_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Range");
25 const struct aws_byte_cursor g_etag_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("ETag");
26 const struct aws_byte_cursor g_content_range_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Range");
27 const struct aws_byte_cursor g_content_type_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Type");
28 const struct aws_byte_cursor g_content_length_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Length");
29 const struct aws_byte_cursor g_content_md5_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-MD5");
30 const struct aws_byte_cursor g_accept_ranges_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("accept-ranges");
31 const struct aws_byte_cursor g_acl_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-acl");
32 const struct aws_byte_cursor g_post_method = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("POST");
33 const struct aws_byte_cursor g_head_method = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("HEAD");
34 const struct aws_byte_cursor g_delete_method = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("DELETE");
35
36 const struct aws_byte_cursor g_user_agent_header_name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("User-Agent");
37 const struct aws_byte_cursor g_user_agent_header_product_name =
38 AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("CRTS3NativeClient");
39
40 const uint32_t g_s3_max_num_upload_parts = 10000;
41 const size_t g_s3_min_upload_part_size = MB_TO_BYTES(5);
42
copy_http_headers(const struct aws_http_headers * src,struct aws_http_headers * dest)43 void copy_http_headers(const struct aws_http_headers *src, struct aws_http_headers *dest) {
44 AWS_PRECONDITION(src);
45 AWS_PRECONDITION(dest);
46
47 size_t headers_count = aws_http_headers_count(src);
48
49 for (size_t header_index = 0; header_index < headers_count; ++header_index) {
50 struct aws_http_header header;
51
52 aws_http_headers_get_index(src, header_index, &header);
53 aws_http_headers_set(dest, header.name, header.value);
54 }
55 }
56
57 struct top_level_xml_tag_value_user_data {
58 struct aws_allocator *allocator;
59 const struct aws_byte_cursor *tag_name;
60 struct aws_string *result;
61 };
62
s_top_level_xml_tag_value_child_xml_node(struct aws_xml_parser * parser,struct aws_xml_node * node,void * user_data)63 static bool s_top_level_xml_tag_value_child_xml_node(
64 struct aws_xml_parser *parser,
65 struct aws_xml_node *node,
66 void *user_data) {
67
68 struct aws_byte_cursor node_name;
69
70 /* If we can't get the name of the node, stop traversing. */
71 if (aws_xml_node_get_name(node, &node_name)) {
72 return false;
73 }
74
75 struct top_level_xml_tag_value_user_data *xml_user_data = user_data;
76
77 /* If the name of the node is what we are looking for, store the body of the node in our result, and stop
78 * traversing. */
79 if (aws_byte_cursor_eq(&node_name, xml_user_data->tag_name)) {
80
81 struct aws_byte_cursor node_body;
82 aws_xml_node_as_body(parser, node, &node_body);
83
84 xml_user_data->result = aws_string_new_from_cursor(xml_user_data->allocator, &node_body);
85
86 return false;
87 }
88
89 /* If we made it here, the tag hasn't been found yet, so return true to keep looking. */
90 return true;
91 }
92
s_top_level_xml_tag_value_root_xml_node(struct aws_xml_parser * parser,struct aws_xml_node * node,void * user_data)93 static bool s_top_level_xml_tag_value_root_xml_node(
94 struct aws_xml_parser *parser,
95 struct aws_xml_node *node,
96 void *user_data) {
97
98 /* Traverse the root node, and then return false to stop. */
99 aws_xml_node_traverse(parser, node, s_top_level_xml_tag_value_child_xml_node, user_data);
100 return false;
101 }
102
get_top_level_xml_tag_value(struct aws_allocator * allocator,const struct aws_byte_cursor * tag_name,struct aws_byte_cursor * xml_body)103 struct aws_string *get_top_level_xml_tag_value(
104 struct aws_allocator *allocator,
105 const struct aws_byte_cursor *tag_name,
106 struct aws_byte_cursor *xml_body) {
107 AWS_PRECONDITION(allocator);
108 AWS_PRECONDITION(tag_name);
109 AWS_PRECONDITION(xml_body);
110
111 struct aws_xml_parser_options parser_options = {.doc = *xml_body};
112 struct aws_xml_parser *parser = aws_xml_parser_new(allocator, &parser_options);
113
114 struct top_level_xml_tag_value_user_data xml_user_data = {
115 allocator,
116 tag_name,
117 NULL,
118 };
119
120 if (aws_xml_parser_parse(parser, s_top_level_xml_tag_value_root_xml_node, (void *)&xml_user_data)) {
121 aws_string_destroy(xml_user_data.result);
122 xml_user_data.result = NULL;
123 goto clean_up;
124 }
125
126 clean_up:
127
128 aws_xml_parser_destroy(parser);
129
130 return xml_user_data.result;
131 }
132
aws_cached_signing_config_new(struct aws_allocator * allocator,const struct aws_signing_config_aws * signing_config)133 struct aws_cached_signing_config_aws *aws_cached_signing_config_new(
134 struct aws_allocator *allocator,
135 const struct aws_signing_config_aws *signing_config) {
136 AWS_PRECONDITION(allocator);
137 AWS_PRECONDITION(signing_config);
138
139 struct aws_cached_signing_config_aws *cached_signing_config =
140 aws_mem_calloc(allocator, 1, sizeof(struct aws_cached_signing_config_aws));
141
142 cached_signing_config->allocator = allocator;
143
144 cached_signing_config->config.config_type = signing_config->config_type;
145 cached_signing_config->config.algorithm = signing_config->algorithm;
146 cached_signing_config->config.signature_type = signing_config->signature_type;
147
148 AWS_ASSERT(aws_byte_cursor_is_valid(&signing_config->region));
149
150 if (signing_config->region.len > 0) {
151 cached_signing_config->region = aws_string_new_from_cursor(allocator, &signing_config->region);
152
153 cached_signing_config->config.region = aws_byte_cursor_from_string(cached_signing_config->region);
154 }
155
156 AWS_ASSERT(aws_byte_cursor_is_valid(&signing_config->service));
157
158 if (signing_config->service.len > 0) {
159 cached_signing_config->service = aws_string_new_from_cursor(allocator, &signing_config->service);
160
161 cached_signing_config->config.service = aws_byte_cursor_from_string(cached_signing_config->service);
162 }
163
164 cached_signing_config->config.date = signing_config->date;
165
166 cached_signing_config->config.should_sign_header = signing_config->should_sign_header;
167 cached_signing_config->config.flags = signing_config->flags;
168
169 AWS_ASSERT(aws_byte_cursor_is_valid(&signing_config->signed_body_value));
170
171 if (signing_config->service.len > 0) {
172 cached_signing_config->signed_body_value =
173 aws_string_new_from_cursor(allocator, &signing_config->signed_body_value);
174
175 cached_signing_config->config.signed_body_value =
176 aws_byte_cursor_from_string(cached_signing_config->signed_body_value);
177 }
178
179 cached_signing_config->config.signed_body_header = signing_config->signed_body_header;
180
181 if (signing_config->credentials != NULL) {
182 aws_credentials_acquire(signing_config->credentials);
183 cached_signing_config->config.credentials = signing_config->credentials;
184 }
185
186 if (signing_config->credentials_provider != NULL) {
187 aws_credentials_provider_acquire(signing_config->credentials_provider);
188 cached_signing_config->config.credentials_provider = signing_config->credentials_provider;
189 }
190
191 cached_signing_config->config.expiration_in_seconds = signing_config->expiration_in_seconds;
192
193 return cached_signing_config;
194 }
195
aws_cached_signing_config_destroy(struct aws_cached_signing_config_aws * cached_signing_config)196 void aws_cached_signing_config_destroy(struct aws_cached_signing_config_aws *cached_signing_config) {
197 if (cached_signing_config == NULL) {
198 return;
199 }
200
201 aws_credentials_release(cached_signing_config->config.credentials);
202 aws_credentials_provider_release(cached_signing_config->config.credentials_provider);
203
204 aws_string_destroy(cached_signing_config->service);
205 aws_string_destroy(cached_signing_config->region);
206 aws_string_destroy(cached_signing_config->signed_body_value);
207
208 aws_mem_release(cached_signing_config->allocator, cached_signing_config);
209 }
210
aws_s3_init_default_signing_config(struct aws_signing_config_aws * signing_config,const struct aws_byte_cursor region,struct aws_credentials_provider * credentials_provider)211 void aws_s3_init_default_signing_config(
212 struct aws_signing_config_aws *signing_config,
213 const struct aws_byte_cursor region,
214 struct aws_credentials_provider *credentials_provider) {
215 AWS_PRECONDITION(signing_config);
216 AWS_PRECONDITION(credentials_provider);
217
218 AWS_ZERO_STRUCT(*signing_config);
219
220 signing_config->config_type = AWS_SIGNING_CONFIG_AWS;
221 signing_config->algorithm = AWS_SIGNING_ALGORITHM_V4;
222 signing_config->credentials_provider = credentials_provider;
223 signing_config->region = region;
224 signing_config->service = g_s3_service_name;
225 signing_config->signed_body_header = AWS_SBHT_X_AMZ_CONTENT_SHA256;
226 signing_config->signed_body_value = g_aws_signed_body_value_unsigned_payload;
227 }
228
replace_quote_entities(struct aws_allocator * allocator,struct aws_string * str,struct aws_byte_buf * out_buf)229 void replace_quote_entities(struct aws_allocator *allocator, struct aws_string *str, struct aws_byte_buf *out_buf) {
230 AWS_PRECONDITION(str);
231
232 aws_byte_buf_init(out_buf, allocator, str->len);
233
234 struct aws_byte_cursor quote_entity = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(""");
235 struct aws_byte_cursor quote = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("\"");
236
237 size_t i = 0;
238
239 while (i < str->len) {
240 size_t chars_remaining = str->len - i;
241
242 if (chars_remaining >= quote_entity.len &&
243 !strncmp((const char *)&str->bytes[i], (const char *)quote_entity.ptr, quote_entity.len)) {
244 /* Append quote */
245 aws_byte_buf_append(out_buf, "e);
246 i += quote_entity.len;
247 } else {
248 /* Append character */
249 struct aws_byte_cursor character_cursor = aws_byte_cursor_from_array(&str->bytes[i], 1);
250 aws_byte_buf_append(out_buf, &character_cursor);
251 ++i;
252 }
253 }
254 }
255
aws_last_error_or_unknown()256 int aws_last_error_or_unknown() {
257 int error = aws_last_error();
258
259 if (error == AWS_ERROR_SUCCESS) {
260 return AWS_ERROR_UNKNOWN;
261 }
262
263 return error;
264 }
265
aws_s3_add_user_agent_header(struct aws_allocator * allocator,struct aws_http_message * message)266 void aws_s3_add_user_agent_header(struct aws_allocator *allocator, struct aws_http_message *message) {
267 AWS_PRECONDITION(allocator);
268 AWS_PRECONDITION(message);
269
270 const struct aws_byte_cursor space_delimeter = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL(" ");
271 const struct aws_byte_cursor forward_slash = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("/");
272
273 const size_t user_agent_product_version_length =
274 g_user_agent_header_product_name.len + forward_slash.len + g_s3_client_version.len;
275
276 struct aws_http_headers *headers = aws_http_message_get_headers(message);
277 AWS_ASSERT(headers != NULL);
278
279 struct aws_byte_cursor current_user_agent_header;
280 AWS_ZERO_STRUCT(current_user_agent_header);
281
282 struct aws_byte_buf user_agent_buffer;
283 AWS_ZERO_STRUCT(user_agent_buffer);
284
285 if (aws_http_headers_get(headers, g_user_agent_header_name, ¤t_user_agent_header) == AWS_OP_SUCCESS) {
286 /* If the header was found, then create a buffer with the total size we'll need, and append the curent user
287 * agent header with a trailing space. */
288 aws_byte_buf_init(
289 &user_agent_buffer,
290 allocator,
291 current_user_agent_header.len + space_delimeter.len + user_agent_product_version_length);
292
293 aws_byte_buf_append_dynamic(&user_agent_buffer, ¤t_user_agent_header);
294
295 aws_byte_buf_append_dynamic(&user_agent_buffer, &space_delimeter);
296
297 } else {
298 AWS_ASSERT(aws_last_error() == AWS_ERROR_HTTP_HEADER_NOT_FOUND);
299
300 /* If the header was not found, then create a buffer with just the size of the user agent string that is about
301 * to be appended to the buffer. */
302 aws_byte_buf_init(&user_agent_buffer, allocator, user_agent_product_version_length);
303 }
304
305 /* Append the client's user-agent string. */
306 {
307 aws_byte_buf_append_dynamic(&user_agent_buffer, &g_user_agent_header_product_name);
308 aws_byte_buf_append_dynamic(&user_agent_buffer, &forward_slash);
309 aws_byte_buf_append_dynamic(&user_agent_buffer, &g_s3_client_version);
310 }
311
312 /* Apply the updated header. */
313 aws_http_headers_set(headers, g_user_agent_header_name, aws_byte_cursor_from_buf(&user_agent_buffer));
314
315 /* Clean up the scratch buffer. */
316 aws_byte_buf_clean_up(&user_agent_buffer);
317 }
318
aws_s3_parse_content_range_response_header(struct aws_allocator * allocator,struct aws_http_headers * response_headers,uint64_t * out_range_start,uint64_t * out_range_end,uint64_t * out_object_size)319 int aws_s3_parse_content_range_response_header(
320 struct aws_allocator *allocator,
321 struct aws_http_headers *response_headers,
322 uint64_t *out_range_start,
323 uint64_t *out_range_end,
324 uint64_t *out_object_size) {
325 AWS_PRECONDITION(allocator);
326 AWS_PRECONDITION(response_headers);
327
328 struct aws_byte_cursor content_range_header_value;
329
330 if (aws_http_headers_get(response_headers, g_content_range_header_name, &content_range_header_value)) {
331 aws_raise_error(AWS_ERROR_S3_MISSING_CONTENT_RANGE_HEADER);
332 return AWS_OP_ERR;
333 }
334
335 int result = AWS_OP_ERR;
336
337 uint64_t range_start = 0;
338 uint64_t range_end = 0;
339 uint64_t object_size = 0;
340
341 struct aws_string *content_range_header_value_str =
342 aws_string_new_from_cursor(allocator, &content_range_header_value);
343
344 /* Expected Format of header is: "bytes StartByte-EndByte/TotalObjectSize" */
345 int num_fields_found = sscanf(
346 (const char *)content_range_header_value_str->bytes,
347 "bytes %" PRIu64 "-%" PRIu64 "/%" PRIu64,
348 &range_start,
349 &range_end,
350 &object_size);
351
352 if (num_fields_found < 3) {
353 aws_raise_error(AWS_ERROR_S3_INVALID_CONTENT_RANGE_HEADER);
354 goto clean_up;
355 }
356
357 if (out_range_start != NULL) {
358 *out_range_start = range_start;
359 }
360
361 if (out_range_end != NULL) {
362 *out_range_end = range_end;
363 }
364
365 if (out_object_size != NULL) {
366 *out_object_size = object_size;
367 }
368
369 result = AWS_OP_SUCCESS;
370
371 clean_up:
372 aws_string_destroy(content_range_header_value_str);
373 content_range_header_value_str = NULL;
374
375 return result;
376 }
377
aws_s3_parse_content_length_response_header(struct aws_allocator * allocator,struct aws_http_headers * response_headers,uint64_t * out_content_length)378 int aws_s3_parse_content_length_response_header(
379 struct aws_allocator *allocator,
380 struct aws_http_headers *response_headers,
381 uint64_t *out_content_length) {
382 AWS_PRECONDITION(allocator);
383 AWS_PRECONDITION(response_headers);
384 AWS_PRECONDITION(out_content_length);
385
386 struct aws_byte_cursor content_length_header_value;
387
388 if (aws_http_headers_get(response_headers, g_content_length_header_name, &content_length_header_value)) {
389 aws_raise_error(AWS_ERROR_S3_MISSING_CONTENT_LENGTH_HEADER);
390 return AWS_OP_ERR;
391 }
392
393 struct aws_string *content_length_header_value_str =
394 aws_string_new_from_cursor(allocator, &content_length_header_value);
395
396 int result = AWS_OP_ERR;
397
398 if (sscanf((const char *)content_length_header_value_str->bytes, "%" PRIu64, out_content_length) == 1) {
399 result = AWS_OP_SUCCESS;
400 } else {
401 aws_raise_error(AWS_ERROR_S3_INVALID_CONTENT_LENGTH_HEADER);
402 }
403
404 aws_string_destroy(content_length_header_value_str);
405 return result;
406 }
407
aws_s3_get_num_parts(size_t part_size,uint64_t object_range_start,uint64_t object_range_end)408 uint32_t aws_s3_get_num_parts(size_t part_size, uint64_t object_range_start, uint64_t object_range_end) {
409 if ((object_range_start - object_range_end) == 0ULL) {
410 return 0;
411 }
412
413 uint32_t num_parts = 1;
414
415 uint64_t first_part_size = part_size;
416 uint64_t first_part_alignment_offset = object_range_start % part_size;
417
418 /* If the first part size isn't aligned on the assumed part boundary, make it smaller so that it is. */
419 if (first_part_alignment_offset > 0) {
420 first_part_size = part_size - first_part_alignment_offset;
421 }
422
423 uint64_t second_part_start = object_range_start + first_part_size;
424
425 /* If the range has room for a second part, calculate the additional amount of parts. */
426 if (second_part_start <= object_range_end) {
427 uint64_t aligned_range_remainder = object_range_end - second_part_start;
428 num_parts += (uint32_t)(aligned_range_remainder / (uint64_t)part_size);
429
430 if ((aligned_range_remainder % part_size) > 0) {
431 ++num_parts;
432 }
433 }
434
435 return num_parts;
436 }
437
aws_s3_get_part_range(uint64_t object_range_start,uint64_t object_range_end,size_t part_size,uint32_t part_number,uint64_t * out_part_range_start,uint64_t * out_part_range_end)438 void aws_s3_get_part_range(
439 uint64_t object_range_start,
440 uint64_t object_range_end,
441 size_t part_size,
442 uint32_t part_number,
443 uint64_t *out_part_range_start,
444 uint64_t *out_part_range_end) {
445 AWS_PRECONDITION(out_part_range_start);
446 AWS_PRECONDITION(out_part_range_end);
447
448 AWS_ASSERT(part_number > 0);
449
450 const uint32_t part_index = part_number - 1;
451
452 /* Part index is assumed to be in a valid range. */
453 AWS_ASSERT(part_index < aws_s3_get_num_parts(part_size, object_range_start, object_range_end));
454
455 uint64_t part_size_uint64 = (uint64_t)part_size;
456 uint64_t first_part_size = part_size_uint64;
457 uint64_t first_part_alignment_offset = object_range_start % part_size_uint64;
458
459 /* Shrink the part to a smaller size if need be to align to the assumed part boundary. */
460 if (first_part_alignment_offset > 0) {
461 first_part_size = part_size_uint64 - first_part_alignment_offset;
462 }
463
464 if (part_index == 0) {
465 /* If this is the first part, then use the first part size. */
466 *out_part_range_start = object_range_start;
467 *out_part_range_end = *out_part_range_start + first_part_size - 1;
468 } else {
469 /* Else, find the next part by adding the object range + total number of whole parts before this one + initial
470 * part size*/
471 *out_part_range_start = object_range_start + ((uint64_t)(part_index - 1)) * part_size_uint64 + first_part_size;
472 *out_part_range_end = *out_part_range_start + part_size_uint64 - 1;
473 }
474
475 /* Cap the part's range end using the object's range end. */
476 if (*out_part_range_end > object_range_end) {
477 *out_part_range_end = object_range_end;
478 }
479 }
480