diff --git a/alternator/server.cc b/alternator/server.cc index 7515f9896e..0dae61bb80 100644 --- a/alternator/server.cc +++ b/alternator/server.cc @@ -322,8 +322,8 @@ future server::verify_signature(const request& req, const chunked_c user_signature = std::move(user_signature)] (key_cache::value_ptr key_ptr) { std::string signature; try { - signature = utils::aws::get_signature(user, *key_ptr, std::string_view(host), req._method, - datestamp, signed_headers_str, signed_headers_map, content, region, service, ""); + signature = utils::aws::get_signature(user, *key_ptr, std::string_view(host), "/", req._method, + datestamp, signed_headers_str, signed_headers_map, &content, region, service, ""); } catch (const std::exception& e) { throw api_error::invalid_signature(e.what()); } diff --git a/utils/aws_sigv4.cc b/utils/aws_sigv4.cc index ae495d30d9..44f8ff7f52 100644 --- a/utils/aws_sigv4.cc +++ b/utils/aws_sigv4.cc @@ -47,7 +47,7 @@ static std::string apply_sha256(const std::vector>& msg) return to_hex(hasher.finalize()); } -static std::string format_time_point(db_clock::time_point tp) { +std::string format_time_point(db_clock::time_point tp) { time_t time_point_repr = db_clock::to_time_t(tp); std::string time_point_str; time_point_str.resize(17); @@ -74,29 +74,31 @@ void check_expiry(std::string_view signature_date) { } } -std::string get_signature(std::string_view access_key_id, std::string_view secret_access_key, std::string_view host, std::string_view method, - std::string_view orig_datestamp, std::string_view signed_headers_str, const std::map& signed_headers_map, - const std::vector>& body_content, std::string_view region, std::string_view service, std::string_view query_string) { +std::string get_signature(std::string_view access_key_id, std::string_view secret_access_key, + std::string_view host, std::string_view canonical_uri, std::string_view method, + std::optional orig_datestamp, std::string_view signed_headers_str, const std::map& signed_headers_map, + const std::vector>* body_content, std::string_view region, std::string_view service, std::string_view query_string) { auto amz_date_it = signed_headers_map.find("x-amz-date"); if (amz_date_it == signed_headers_map.end()) { throw std::runtime_error("X-Amz-Date header is mandatory for signature verification"); } std::string_view amz_date = amz_date_it->second; - check_expiry(amz_date); std::string_view datestamp = amz_date.substr(0, 8); - if (datestamp != orig_datestamp) { - throw std::runtime_error( - format("X-Amz-Date date does not match the provided datestamp. Expected {}, got {}", - orig_datestamp, datestamp)); + if (orig_datestamp) { + check_expiry(amz_date); + if (datestamp != *orig_datestamp) { + throw std::runtime_error( + format("X-Amz-Date date does not match the provided datestamp. Expected {}, got {}", + *orig_datestamp, datestamp)); + } } - std::string_view canonical_uri = "/"; std::stringstream canonical_headers; for (const auto& header : signed_headers_map) { canonical_headers << fmt::format("{}:{}", header.first, header.second) << '\n'; } - std::string payload_hash = apply_sha256(body_content); + std::string payload_hash = body_content != nullptr ? apply_sha256(*body_content) : "UNSIGNED-PAYLOAD"; std::string canonical_request = fmt::format("{}\n{}\n{}\n{}\n{}\n{}", method, canonical_uri, query_string, canonical_headers.str(), signed_headers_str, payload_hash); std::string_view algorithm = "AWS4-HMAC-SHA256"; diff --git a/utils/aws_sigv4.hh b/utils/aws_sigv4.hh index 9117463d59..335eb1c4e7 100644 --- a/utils/aws_sigv4.hh +++ b/utils/aws_sigv4.hh @@ -6,8 +6,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +#pragma once + #include -#include "utils/hashers.hh" +#include "db_clock.hh" // The declared below get_signature() method makes the Signature string for AWS // authenticated requests as described in [1]. It can be used in two ways. @@ -29,9 +31,17 @@ using hmac_sha256_digest = std::array; namespace aws { -std::string get_signature(std::string_view access_key_id, std::string_view secret_access_key, std::string_view host, std::string_view method, - std::string_view orig_datestamp, std::string_view signed_headers_str, const std::map& signed_headers_map, - const std::vector>& body_content, std::string_view region, std::string_view service, std::string_view query_string); +std::string get_signature(std::string_view access_key_id, std::string_view secret_access_key, + std::string_view host, std::string_view canonical_uri, std::string_view method, + std::optional orig_datestamp, std::string_view signed_headers_str, const std::map& signed_headers_map, + const std::vector>* body_content, std::string_view region, std::string_view service, std::string_view query_string); + +// Convenience alias not to pass obscure nullptr argument to get_signature() +static inline constexpr std::vector>* unsigned_content = nullptr; +// Same for datestamp checking +static inline auto omit_datestamp_expiration_check = std::nullopt; + +std::string format_time_point(db_clock::time_point tp); } // aws namespace } // utils namespace