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("&quot;");
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, &quote);
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, &current_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, &current_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