mirror of
https://github.com/pgsty/minio.git
synced 2026-11-01 13:22:02 +03:00
fix: repair an undecodable SSE-C replica on retransmit
PutObjectHandler's precondition callback ran DecryptObjectInfo on the stored object before checkPreconditionsPUT, so an authenticated raw SSE-C replica overwrite was rejected when the stored version could not decrypt. A replica a pre-fix destination (issue #109) left as compress(ciphertext) or a re-encrypted body has an invalid decrypted length, so DecryptObjectInfo returned errObjectTampered and the retransmission that repairs it never ran -- the version stayed damaged through resync. #134's raw-replica exemption only covered the version/ETag duplicate check inside checkPreconditionsPUT, one step too late. Skip the stored object's decryption precondition only for a PURE raw SSE-C replica overwrite (a trusted SSE-C replica write with no public precondition), keyed on the incoming request's restored SSE-C metadata, the same predicate checkPreconditionsPUT uses. Such a write fully replaces the object, so requiring the damaged stored version to decrypt is both wrong and unnecessary. A conditional request keeps the check: DecryptObjectInfo also normalizes the stored sealed ETag to the client-visible one, and If-Match/If-None-Match must compare against that, not the sealed ETag -- skipping it for every replica inverted both conditions. Ordinary writes and non-SSE-C replicas are unchanged. Adds red/green regressions: a raw retransmit over a version staged as an undecodable body returns 500 XMinioObjectTampered before this change and 200 with full customer-key recovery after; and a conditional replica PUT (If-Match / If-None-Match) on the client-visible ETag is honoured rather than inverted. Fixes the single-PUT compression-damage recovery gap in Signed-off-by: Feng Ruohang <rh@vonng.com> #120.
This commit is contained in:
+15
-3
@@ -2227,9 +2227,21 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
r.Header.Get(xhttp.IfMatch) != "" ||
|
||||
r.Header.Get(xhttp.IfNoneMatch) != "" {
|
||||
opts.CheckPrecondFn = func(oi ObjectInfo) bool {
|
||||
if _, err := DecryptObjectInfo(&oi, r); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return true
|
||||
// A pure raw SSE-C replica overwrite (no public precondition) fully
|
||||
// replaces the stored object, so the destination must not first
|
||||
// require the stored object to decrypt: a replica an older destination
|
||||
// bug left as compress(ciphertext) or double-encrypted has an invalid
|
||||
// decrypted length, and requiring it here blocks the retransmission
|
||||
// that repairs it. The predicate is the incoming request's restored
|
||||
// SSE-C metadata, the same one checkPreconditionsPUT uses to exempt the
|
||||
// version/ETag duplicate. A conditional request (If-Match/If-None-Match)
|
||||
// still needs the decrypted, client-visible ETag, so it keeps the check.
|
||||
ssecReplica := isReplicaTrusted(ctx) && crypto.SSEC.IsEncrypted(opts.UserDefined)
|
||||
if !ssecReplica || r.Header.Get(xhttp.IfMatch) != "" || r.Header.Get(xhttp.IfNoneMatch) != "" {
|
||||
if _, err := DecryptObjectInfo(&oi, r); err != nil {
|
||||
writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return checkPreconditionsPUT(ctx, w, r, oi, opts)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user