Merge 'Extend aws_sigv4 code to suite S3 client needs' from Pavel Emelyanov
The AWS signature-generating code was moved from alternator some time ago as is. Now it's clear that in which places it should be extended to work for S3 client as well. The enhancements are - Support UNSIGNED-PAYLOAD to omit calculating checksums for request body - Include full URL path into the signature, not just hard-coded "/" string - Don't check datastamp expiration if not asked for This is a part of #13493 Closes #13535 * github.com:scylladb/scylladb: utils/aws: Brush up the aws_sigv4.hh header utils/aws: Export timepoint formatter utils/aws: Omit datestamp expiration checks when not needed utils/aws: Add canonical-uri argument utils/aws: Support unsigned-payload signatures
This commit is contained in:
@@ -322,8 +322,8 @@ future<std::string> 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());
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ static std::string apply_sha256(const std::vector<temporary_buffer<char>>& 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<std::string_view, std::string_view>& signed_headers_map,
|
||||
const std::vector<temporary_buffer<char>>& 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<std::string_view> orig_datestamp, std::string_view signed_headers_str, const std::map<std::string_view, std::string_view>& signed_headers_map,
|
||||
const std::vector<temporary_buffer<char>>* 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";
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
#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<char, 32>;
|
||||
|
||||
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<std::string_view, std::string_view>& signed_headers_map,
|
||||
const std::vector<temporary_buffer<char>>& 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<std::string_view> orig_datestamp, std::string_view signed_headers_str, const std::map<std::string_view, std::string_view>& signed_headers_map,
|
||||
const std::vector<temporary_buffer<char>>* 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<temporary_buffer<char>>* 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
|
||||
|
||||
Reference in New Issue
Block a user