[fix-presigned] add back anonymous request code path + refactoring

This commit is contained in:
Alex 2024-02-27 23:33:26 +01:00
parent 2efa9c5a1a
commit 4c1d42cc5f
Signed by untrusted user: lx
GPG key ID: 0E496D15096376BE
2 changed files with 40 additions and 30 deletions

View file

@ -40,17 +40,26 @@ pub async fn check_payload_signature(
) -> Result<(Option<Key>, Option<Hash>), Error> { ) -> Result<(Option<Key>, Option<Hash>), Error> {
let query = parse_query_map(request.uri())?; let query = parse_query_map(request.uri())?;
let res = if query.contains_key(X_AMZ_ALGORITHM.as_str()) { if query.contains_key(X_AMZ_ALGORITHM.as_str()) {
check_presigned_signature(garage, service, request, query).await check_presigned_signature(garage, service, request, query).await
} else { } else if request.headers().contains_key(AUTHORIZATION) {
check_standard_signature(garage, service, request, query).await check_standard_signature(garage, service, request, query).await
}; } else {
// Unsigned (anonymous) request
if let Err(e) = &res { let content_sha256 = request
error!("ERROR IN SIGNATURE\n{:?}\n{}", request, e); .headers()
.get("x-amz-content-sha256")
.filter(|c| c.as_bytes() != UNSIGNED_PAYLOAD.as_bytes());
if let Some(content_sha256) = content_sha256 {
let sha256 = hex::decode(content_sha256)
.ok()
.and_then(|bytes| Hash::try_from(&bytes))
.ok_or_bad_request("Invalid content sha256 hash")?;
Ok((None, Some(sha256)))
} else {
Ok((None, None))
}
} }
res
} }
async fn check_standard_signature( async fn check_standard_signature(
@ -63,8 +72,11 @@ async fn check_standard_signature(
// Verify that all necessary request headers are signed // Verify that all necessary request headers are signed
let signed_headers = split_signed_headers(&authorization)?; let signed_headers = split_signed_headers(&authorization)?;
if !signed_headers.contains(&HOST) {
return Err(Error::bad_request("Header `Host` should be signed"));
}
for (name, _) in request.headers().iter() { for (name, _) in request.headers().iter() {
if name.as_str().starts_with("x-amz-") || name == CONTENT_TYPE { if name == CONTENT_TYPE || name.as_str().starts_with("x-amz-") {
if !signed_headers.contains(name) { if !signed_headers.contains(name) {
return Err(Error::bad_request(format!( return Err(Error::bad_request(format!(
"Header `{}` should be signed", "Header `{}` should be signed",
@ -73,9 +85,6 @@ async fn check_standard_signature(
} }
} }
} }
if !signed_headers.contains(&HOST) {
return Err(Error::bad_request("Header `Host` should be signed"));
}
let canonical_request = canonical_request( let canonical_request = canonical_request(
service, service,
@ -122,6 +131,9 @@ async fn check_presigned_signature(
// Check that all mandatory signed headers are included // Check that all mandatory signed headers are included
let signed_headers = split_signed_headers(&authorization)?; let signed_headers = split_signed_headers(&authorization)?;
if !signed_headers.contains(&HOST) {
return Err(Error::bad_request("Header `Host` should be signed"));
}
for (name, _) in request.headers().iter() { for (name, _) in request.headers().iter() {
if name.as_str().starts_with("x-amz-") { if name.as_str().starts_with("x-amz-") {
if !signed_headers.contains(name) { if !signed_headers.contains(name) {
@ -132,9 +144,6 @@ async fn check_presigned_signature(
} }
} }
} }
if !signed_headers.contains(&HOST) {
return Err(Error::bad_request("Header `Host` should be signed"));
}
query.remove(X_AMZ_SIGNATURE.as_str()); query.remove(X_AMZ_SIGNATURE.as_str());
let canonical_request = canonical_request( let canonical_request = canonical_request(
@ -254,21 +263,17 @@ pub fn canonical_request(
// Canonical header string calculated from signed headers // Canonical header string calculated from signed headers
signed_headers.sort_by(|h1, h2| h1.as_str().cmp(h2.as_str())); signed_headers.sort_by(|h1, h2| h1.as_str().cmp(h2.as_str()));
let canonical_header_string = { let canonical_header_string = signed_headers
let mut items = Vec::with_capacity(signed_headers.len()); .iter()
for name in signed_headers.iter() { .map(|name| {
let value = headers let value = headers
.get(name) .get(name)
.ok_or_bad_request(format!("signed header `{}` is not present", name))? .ok_or_bad_request(format!("signed header `{}` is not present", name))?
.to_str()?; .to_str()?;
items.push((name, value)); Ok(format!("{}:{}", name.as_str(), value.trim()))
} })
items .collect::<Result<Vec<String>, Error>>()?
.iter() .join("\n");
.map(|(key, value)| format!("{}:{}", key.as_str(), value.trim()))
.collect::<Vec<_>>()
.join("\n")
};
let signed_headers = signed_headers.join(";"); let signed_headers = signed_headers.join(";");
let list = [ let list = [

View file

@ -15,6 +15,11 @@ use super::{compute_scope, sha256sum, HmacSha256, LONG_DATETIME};
use crate::helpers::*; use crate::helpers::*;
use crate::signature::error::*; use crate::signature::error::*;
use crate::signature::payload::{
STREAMING_AWS4_HMAC_SHA256_PAYLOAD, X_AMZ_CONTENT_SH256, X_AMZ_DATE,
};
pub const AWS4_HMAC_SHA256_PAYLOAD: &str = "AWS4-HMAC-SHA256-PAYLOAD";
pub type ReqBody = BoxBody<Error>; pub type ReqBody = BoxBody<Error>;
@ -25,8 +30,8 @@ pub fn parse_streaming_body(
region: &str, region: &str,
service: &str, service: &str,
) -> Result<Request<ReqBody>, Error> { ) -> Result<Request<ReqBody>, Error> {
match req.headers().get("x-amz-content-sha256") { match req.headers().get(X_AMZ_CONTENT_SH256) {
Some(header) if header == "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" => { Some(header) if header == STREAMING_AWS4_HMAC_SHA256_PAYLOAD => {
let signature = content_sha256 let signature = content_sha256
.take() .take()
.ok_or_bad_request("No signature provided")?; .ok_or_bad_request("No signature provided")?;
@ -39,7 +44,7 @@ pub fn parse_streaming_body(
let date = req let date = req
.headers() .headers()
.get("x-amz-date") .get(X_AMZ_DATE)
.ok_or_bad_request("Missing X-Amz-Date field")? .ok_or_bad_request("Missing X-Amz-Date field")?
.to_str()?; .to_str()?;
let date: NaiveDateTime = NaiveDateTime::parse_from_str(date, LONG_DATETIME) let date: NaiveDateTime = NaiveDateTime::parse_from_str(date, LONG_DATETIME)
@ -75,7 +80,7 @@ fn compute_streaming_payload_signature(
content_sha256: Hash, content_sha256: Hash,
) -> Result<Hash, Error> { ) -> Result<Hash, Error> {
let string_to_sign = [ let string_to_sign = [
"AWS4-HMAC-SHA256-PAYLOAD", AWS4_HMAC_SHA256_PAYLOAD,
&date.format(LONG_DATETIME).to_string(), &date.format(LONG_DATETIME).to_string(),
scope, scope,
&hex::encode(previous_signature), &hex::encode(previous_signature),