Skip to content

Commit a31a657

Browse files
Fix recognition of user provided unknown checksums (#624)
1 parent ab764f5 commit a31a657

9 files changed

Lines changed: 275 additions & 51 deletions

File tree

include/aws/s3/private/s3_checksums.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ struct aws_s3_meta_request_checksum_config_storage {
5656

5757
enum aws_s3_checksum_location location;
5858
enum aws_s3_checksum_algorithm checksum_algorithm;
59+
struct aws_byte_buf unknown_checksum_algo; /* if checksum algo is unknown this will have checksum name */
5960
bool validate_response_checksum;
6061
struct {
6162
bool crc64nvme;

include/aws/s3/private/s3_request_messages.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ struct aws_http_message *aws_s3_message_util_copy_http_message_no_body_filter_he
3636
struct aws_http_message *message,
3737
const struct aws_byte_cursor *excluded_headers_arrays,
3838
size_t excluded_headers_size,
39-
bool exclude_x_amz_meta);
39+
bool exclude_x_amz_meta,
40+
bool exclude_x_checksum);
4041

4142
/* Copy headers from one message to the other and exclude specific headers.
4243
* exclude_x_amz_meta controls whether S3 user metadata headers (prefixed with "x-amz-meta) are excluded.*/
@@ -46,7 +47,8 @@ void aws_s3_message_util_copy_headers(
4647
struct aws_http_message *dest_message,
4748
const struct aws_byte_cursor *excluded_headers_arrays,
4849
size_t excluded_headers_size,
49-
bool exclude_x_amz_meta);
50+
bool exclude_x_amz_meta,
51+
bool exclude_x_checksum);
5052

5153
AWS_S3_API
5254
struct aws_input_stream *aws_s3_message_util_assign_body(

include/aws/s3/s3_client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ enum aws_s3_checksum_algorithm {
288288
AWS_SCA_XXHASH3_64,
289289
AWS_SCA_XXHASH3_128,
290290
AWS_SCA_END = AWS_SCA_XXHASH3_128,
291+
AWS_SCA_UNKNOWN, /* special value for forwards compat to indicate checksum type that crt is not aware of */
291292
};
292293

293294
enum aws_s3_checksum_location {

source/s3_auto_ranged_put.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,13 +957,15 @@ static struct aws_future_http_message *s_s3_prepare_list_parts(struct aws_s3_req
957957
message,
958958
g_s3_list_parts_excluded_headers,
959959
g_s3_list_parts_excluded_headers_count,
960+
true,
960961
true);
961962
} else {
962963
aws_s3_message_util_copy_headers(
963964
meta_request->initial_request_message,
964965
message,
965966
g_s3_list_parts_with_checksum_excluded_headers,
966967
g_s3_list_parts_with_checksum_excluded_headers_count,
968+
true,
967969
true);
968970
}
969971
AWS_ASSERT(message);

source/s3_checksums.c

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,18 @@ int aws_checksum_compute(
453453
}
454454
}
455455

456+
static const struct aws_byte_cursor s_checksum_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-");
457+
458+
static void s_byte_buf_to_upper(struct aws_byte_buf *buf) {
459+
AWS_PRECONDITION(buf);
460+
461+
for (size_t i = 0; i < buf->len; ++i) {
462+
if (buf->buffer[i] >= 'a' && buf->buffer[i] <= 'z') {
463+
buf->buffer[i] = buf->buffer[i] + ('A' - 'a');
464+
}
465+
}
466+
}
467+
456468
static int s_init_and_verify_checksum_config_from_headers(
457469
struct aws_s3_meta_request_checksum_config_storage *checksum_config,
458470
const struct aws_http_message *message,
@@ -463,34 +475,63 @@ static int s_init_and_verify_checksum_config_from_headers(
463475
struct aws_byte_cursor header_value;
464476
AWS_ZERO_STRUCT(header_value);
465477

466-
for (size_t i = 0; i < AWS_ARRAY_SIZE(s_checksum_algo_priority_list); i++) {
467-
enum aws_s3_checksum_algorithm algorithm = s_checksum_algo_priority_list[i];
468-
const struct aws_byte_cursor algorithm_header_name =
469-
aws_get_http_header_name_from_checksum_algorithm(algorithm);
470-
if (aws_http_headers_get(headers, algorithm_header_name, &header_value) == AWS_OP_SUCCESS) {
471-
if (header_algo == AWS_SCA_NONE) {
472-
header_algo = algorithm;
473-
} else {
474-
/* If there are multiple checksum headers set, it's malformed request */
475-
AWS_LOGF_ERROR(
476-
AWS_LS_S3_META_REQUEST,
477-
"id=%p Could not create auto-ranged-put meta request; multiple checksum headers has been set",
478-
log_id);
479-
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
478+
/*
479+
* We want to detect if there are any checksum headers, whether known or unknown.
480+
* Current approach first looks for any header that starts with x-amz-checksum and then
481+
* if it finds one, it checks whether it maps to any known checksum headers.
482+
* if not then we mark checksum as unknown.
483+
*/
484+
bool has_checksum_value_header = false;
485+
struct aws_byte_cursor checksum_header_name;
486+
for (size_t i = 0; i < aws_http_headers_count(headers); ++i) {
487+
struct aws_http_header header;
488+
if (aws_http_headers_get_index(headers, i, &header)) {
489+
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
490+
}
491+
492+
if (aws_byte_cursor_starts_with_ignore_case(&header.name, &s_checksum_prefix)) {
493+
checksum_header_name = header.name;
494+
has_checksum_value_header = true;
495+
break;
496+
}
497+
}
498+
499+
if (has_checksum_value_header) {
500+
for (size_t i = 0; i < AWS_ARRAY_SIZE(s_checksum_algo_priority_list); i++) {
501+
enum aws_s3_checksum_algorithm algorithm = s_checksum_algo_priority_list[i];
502+
const struct aws_byte_cursor algorithm_header_name =
503+
aws_get_http_header_name_from_checksum_algorithm(algorithm);
504+
if (aws_http_headers_get(headers, algorithm_header_name, &header_value) == AWS_OP_SUCCESS) {
505+
if (header_algo == AWS_SCA_NONE) {
506+
header_algo = algorithm;
507+
} else {
508+
/* If there are multiple checksum headers set, it's malformed request */
509+
AWS_LOGF_ERROR(
510+
AWS_LS_S3_META_REQUEST,
511+
"id=%p Could not create auto-ranged-put meta request; multiple checksum headers has been set",
512+
log_id);
513+
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
514+
}
480515
}
481516
}
482517
}
483-
if (header_algo == AWS_SCA_NONE) {
518+
519+
if (!has_checksum_value_header) {
484520
/* No checksum header found, done */
485521
return AWS_OP_SUCCESS;
486522
}
487523

524+
if (header_algo == AWS_SCA_NONE) {
525+
header_algo = AWS_SCA_UNKNOWN;
526+
}
527+
488528
if (checksum_config->has_full_object_checksum) {
489529
/* If the full object checksum has been set, it's malformed request */
490530
AWS_LOGF_ERROR(
491531
AWS_LS_S3_META_REQUEST,
492532
"id=%p: Could not create auto-ranged-put meta request; full object checksum is set from multiple ways.",
493533
log_id);
534+
494535
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
495536
}
496537

@@ -509,6 +550,13 @@ static int s_init_and_verify_checksum_config_from_headers(
509550
**/
510551
checksum_config->location = AWS_SCL_NONE;
511552

553+
if (header_algo == AWS_SCA_UNKNOWN) {
554+
aws_byte_cursor_advance(&checksum_header_name, s_checksum_prefix.len);
555+
aws_byte_buf_init_copy_from_cursor(
556+
&checksum_config->unknown_checksum_algo, checksum_config->allocator, checksum_header_name);
557+
s_byte_buf_to_upper(&checksum_config->unknown_checksum_algo);
558+
}
559+
512560
/* Set full object checksum from the header value. */
513561
aws_byte_buf_init_copy_from_cursor(
514562
&checksum_config->full_object_checksum, checksum_config->allocator, header_value);
@@ -526,6 +574,8 @@ int aws_s3_meta_request_checksum_config_storage_init(
526574
/* Zero out the struct and set the allocator regardless. */
527575
internal_config->allocator = allocator;
528576

577+
/* Potential improvement here is that right now unless you configure checksum (which sdks seem to do),
578+
the checksum value in the message is not detected and does not get propagated to create/complete */
529579
if (!config) {
530580
return AWS_OP_SUCCESS;
531581
}
@@ -631,4 +681,5 @@ void aws_s3_meta_request_checksum_config_storage_cleanup(
631681
if (internal_config->has_full_object_checksum) {
632682
aws_byte_buf_clean_up(&internal_config->full_object_checksum);
633683
}
684+
aws_byte_buf_clean_up(&internal_config->unknown_checksum_algo);
634685
}

source/s3_request_messages.c

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,9 @@ const struct aws_byte_cursor g_s3_create_multipart_upload_excluded_headers[] = {
2222
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-MD5"),
2323
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"),
2424
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source-range"),
25-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-crc64nvme"),
26-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-crc32c"),
27-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-crc32"),
28-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-sha1"),
29-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-sha256"),
3025
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
3126
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-create-session-mode"),
27+
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-sdk-checksum-algorithm"),
3228
};
3329

3430
const size_t g_s3_create_multipart_upload_excluded_headers_count =
@@ -58,11 +54,6 @@ const struct aws_byte_cursor g_s3_upload_part_excluded_headers[] = {
5854
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-mode"),
5955
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-retain-until-date"),
6056
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-object-lock-legal-hold"),
61-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-crc64nvme"),
62-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-crc32c"),
63-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-crc32"),
64-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-sha1"),
65-
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-sha256"),
6657
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("if-none-match"),
6758
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-create-session-mode"),
6859
};
@@ -248,6 +239,7 @@ const struct aws_byte_cursor g_s3_create_session_allowed_headers[] = {
248239
const size_t g_s3_create_session_allowed_headers_count = AWS_ARRAY_SIZE(g_s3_create_session_allowed_headers);
249240

250241
static const struct aws_byte_cursor s_x_amz_meta_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-meta-");
242+
static const struct aws_byte_cursor s_x_amz_checksum_prefix = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-");
251243

252244
static const struct aws_byte_cursor s_checksum_type_header =
253245
AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-checksum-type");
@@ -299,7 +291,8 @@ struct aws_http_message *aws_s3_create_multipart_upload_message_new(
299291
base_message,
300292
g_s3_create_multipart_upload_excluded_headers,
301293
AWS_ARRAY_SIZE(g_s3_create_multipart_upload_excluded_headers),
302-
false /*exclude_x_amz_meta*/);
294+
false /*exclude_x_amz_meta*/,
295+
true /*exclude_x_checksum_meta*/);
303296

304297
if (message == NULL) {
305298
return NULL;
@@ -322,7 +315,12 @@ struct aws_http_message *aws_s3_create_multipart_upload_message_new(
322315
}
323316

324317
if (checksum_config && (checksum_config->location != AWS_SCL_NONE || checksum_config->has_full_object_checksum)) {
325-
if (checksum_config->checksum_algorithm) {
318+
if (checksum_config->checksum_algorithm == AWS_SCA_UNKNOWN) {
319+
struct aws_byte_cursor checksum_name = aws_byte_cursor_from_buf(&checksum_config->unknown_checksum_algo);
320+
if (aws_http_headers_set(headers, g_checksum_algorithm_header_name, checksum_name)) {
321+
goto error_clean_up;
322+
}
323+
} else if (checksum_config->checksum_algorithm) {
326324
if (aws_http_headers_set(
327325
headers,
328326
g_checksum_algorithm_header_name,
@@ -374,7 +372,8 @@ struct aws_http_message *aws_s3_upload_part_message_new_streaming(
374372
base_message,
375373
g_s3_upload_part_excluded_headers,
376374
AWS_ARRAY_SIZE(g_s3_upload_part_excluded_headers),
377-
true /*exclude_x_amz_meta*/);
375+
true /*exclude_x_amz_meta*/,
376+
true /*exclude_x_checksum_meta*/);
378377

379378
if (message == NULL) {
380379
return NULL;
@@ -429,7 +428,8 @@ struct aws_http_message *aws_s3_upload_part_message_new(
429428
base_message,
430429
g_s3_upload_part_excluded_headers,
431430
AWS_ARRAY_SIZE(g_s3_upload_part_excluded_headers),
432-
true /*exclude_x_amz_meta*/);
431+
true /*exclude_x_amz_meta*/,
432+
true /*exclude_x_checksum_meta*/);
433433

434434
if (message == NULL) {
435435
return NULL;
@@ -477,7 +477,8 @@ struct aws_http_message *aws_s3_upload_part_copy_message_new(
477477
base_message,
478478
g_s3_upload_part_excluded_headers,
479479
AWS_ARRAY_SIZE(g_s3_upload_part_excluded_headers),
480-
true /*exclude_x_amz_meta*/);
480+
true /*exclude_x_amz_meta*/,
481+
true /*exclude_x_checksum_meta*/);
481482

482483
if (message == NULL) {
483484
goto error_clean_up;
@@ -706,15 +707,17 @@ struct aws_http_message *aws_s3_complete_multipart_message_new(
706707
base_message,
707708
g_s3_complete_multipart_upload_with_checksum_excluded_headers,
708709
AWS_ARRAY_SIZE(g_s3_complete_multipart_upload_with_checksum_excluded_headers),
709-
true /*exclude_x_amz_meta*/);
710+
true /*exclude_x_amz_meta*/,
711+
false /*exclude_x_checksum_meta*/);
710712

711713
} else {
712714
message = aws_s3_message_util_copy_http_message_no_body_filter_headers(
713715
allocator,
714716
base_message,
715717
g_s3_complete_multipart_upload_excluded_headers,
716718
AWS_ARRAY_SIZE(g_s3_complete_multipart_upload_excluded_headers),
717-
true /*exclude_x_amz_meta*/);
719+
true /*exclude_x_amz_meta*/,
720+
false /*exclude_x_checksum_meta*/);
718721
}
719722

720723
struct aws_http_headers *headers = NULL;
@@ -863,7 +866,8 @@ struct aws_http_message *aws_s3_abort_multipart_upload_message_new(
863866
base_message,
864867
g_s3_abort_multipart_upload_excluded_headers,
865868
AWS_ARRAY_SIZE(g_s3_abort_multipart_upload_excluded_headers),
866-
true /*exclude_x_amz_meta*/);
869+
true /*exclude_x_amz_meta*/,
870+
true /*exclude_x_checksum_meta*/);
867871

868872
if (aws_s3_message_util_set_multipart_request_path(allocator, upload_id, 0, false, message)) {
869873
goto error_clean_up;
@@ -1141,15 +1145,16 @@ struct aws_http_message *aws_s3_message_util_copy_http_message_no_body_all_heade
11411145
struct aws_allocator *allocator,
11421146
struct aws_http_message *base_message) {
11431147

1144-
return aws_s3_message_util_copy_http_message_no_body_filter_headers(allocator, base_message, NULL, 0, false);
1148+
return aws_s3_message_util_copy_http_message_no_body_filter_headers(allocator, base_message, NULL, 0, false, false);
11451149
}
11461150

11471151
struct aws_http_message *aws_s3_message_util_copy_http_message_no_body_filter_headers(
11481152
struct aws_allocator *allocator,
11491153
struct aws_http_message *base_message,
11501154
const struct aws_byte_cursor *excluded_header_array,
11511155
size_t excluded_header_array_size,
1152-
bool exclude_x_amz_meta) {
1156+
bool exclude_x_amz_meta,
1157+
bool exclude_x_amz_checksum) {
11531158

11541159
AWS_PRECONDITION(allocator);
11551160
AWS_PRECONDITION(base_message);
@@ -1178,7 +1183,12 @@ struct aws_http_message *aws_s3_message_util_copy_http_message_no_body_filter_he
11781183
}
11791184

11801185
aws_s3_message_util_copy_headers(
1181-
base_message, message, excluded_header_array, excluded_header_array_size, exclude_x_amz_meta);
1186+
base_message,
1187+
message,
1188+
excluded_header_array,
1189+
excluded_header_array_size,
1190+
exclude_x_amz_meta,
1191+
exclude_x_amz_checksum);
11821192

11831193
return message;
11841194

@@ -1192,7 +1202,8 @@ void aws_s3_message_util_copy_headers(
11921202
struct aws_http_message *dest_message,
11931203
const struct aws_byte_cursor *excluded_header_array,
11941204
size_t excluded_header_array_size,
1195-
bool exclude_x_amz_meta) {
1205+
bool exclude_x_amz_meta,
1206+
bool exclude_x_amz_checksum) {
11961207

11971208
size_t num_headers = aws_http_message_get_header_count(source_message);
11981209

@@ -1222,6 +1233,12 @@ void aws_s3_message_util_copy_headers(
12221233
}
12231234
}
12241235

1236+
if (exclude_x_amz_checksum) {
1237+
if (aws_byte_cursor_starts_with_ignore_case(&header.name, &s_x_amz_checksum_prefix)) {
1238+
continue;
1239+
}
1240+
}
1241+
12251242
error |= aws_http_message_add_header(dest_message, header);
12261243
(void)error;
12271244
AWS_ASSERT(!error);

tests/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ add_test_case(test_s3_upload_part_message_fail)
2525
add_test_case(test_s3_complete_multipart_message_new)
2626
add_test_case(test_s3_abort_multipart_upload_message_new)
2727

28+
add_net_test_case(test_s3_put_object_custom_md5)
29+
2830
add_net_test_case(test_s3_client_create_destroy)
2931
add_net_test_case(test_s3_client_create_error)
3032
add_net_test_case(test_s3_client_create_error_with_invalid_memory_limit_config)

0 commit comments

Comments
 (0)