emit headers on Not Modified per RFC-9110, fix #1330 (#1340)

also fix a small information disclosure where a client with valid token, but no encryption keys, can use Not Modified has an oracle to know if etag matches or not

Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1340
Co-authored-by: trinity-1686a <trinity@deuxfleurs.fr>
Co-committed-by: trinity-1686a <trinity@deuxfleurs.fr>
This commit is contained in:
trinity-1686a
2026-02-15 11:00:31 +00:00
committed by Alex
parent 7ad0f96222
commit b0840ab256
+40 -12
View File
@@ -120,16 +120,42 @@ fn getobject_override_headers(
fn handle_http_precondition(
version: &ObjectVersion,
version_meta: &ObjectVersionMeta,
meta_inner: &ObjectVersionMetaInner,
encryption: EncryptionParams,
req: &Request<()>,
) -> Result<Option<Response<ResBody>>, Error> {
let precondition_headers = PreconditionHeaders::parse(req)?;
if let Some(status_code) = precondition_headers.check(version, &version_meta.etag)? {
let mut response = object_headers(
version,
version_meta,
meta_inner,
encryption,
ChecksumMode { enabled: false },
);
if let Some(header_map) = response.headers_mut() {
use http::header;
let headers_to_keep: Vec<_> = header_map
.drain()
.filter(|(k, _v)| {
k.as_ref().is_some_and(|k| {
[
header::CONTENT_LOCATION,
header::DATE,
header::ETAG,
header::VARY,
header::CACHE_CONTROL,
header::EXPIRES,
]
.contains(k)
})
})
.collect();
header_map.extend(headers_to_keep);
}
Ok(Some(
Response::builder()
.status(status_code)
.body(empty_body())
.unwrap(),
response.status(status_code).body(empty_body()).unwrap(),
))
} else {
Ok(None)
@@ -178,10 +204,6 @@ pub async fn handle_head_without_ctx(
_ => unreachable!(),
};
if let Some(res) = handle_http_precondition(object_version, version_meta, req)? {
return Ok(res);
}
let (encryption, headers) = EncryptionParams::check_decrypt(
&garage,
req.headers(),
@@ -189,6 +211,12 @@ pub async fn handle_head_without_ctx(
OekDerivationInfo::for_object(&object, object_version),
)?;
if let Some(res) =
handle_http_precondition(object_version, version_meta, &headers, encryption, req)?
{
return Ok(res);
}
let checksum_mode = checksum_mode(req);
if let Some(part_number) = part_number {
@@ -305,10 +333,6 @@ pub async fn handle_get_without_ctx(
ObjectVersionData::FirstBlock(meta, _) => meta,
};
if let Some(res) = handle_http_precondition(last_v, last_v_meta, req)? {
return Ok(res);
}
let (enc, headers) = EncryptionParams::check_decrypt(
&garage,
req.headers(),
@@ -316,6 +340,10 @@ pub async fn handle_get_without_ctx(
OekDerivationInfo::for_object(&object, last_v),
)?;
if let Some(res) = handle_http_precondition(last_v, last_v_meta, &headers, enc, req)? {
return Ok(res);
}
let checksum_mode = checksum_mode(req);
let handle_get_info = HandleGetInfo {