Fix CopyObjectPart broken source encryption support (#6699)

Current master didn't support CopyObjectPart when source
was encrypted, this PR fixes this by allowing range
CopySource decryption at different sequence numbers.

Fixes #6698
master
Harshavardhana 6 years ago committed by kannappanr
parent bab4c90c45
commit 555d54371c
  1. 17
      cmd/encryption-v1.go
  2. 40
      cmd/object-handlers.go

@ -332,7 +332,7 @@ func DecryptRequestWithSequenceNumberR(client io.Reader, h http.Header, bucket,
// DecryptCopyRequestR - same as DecryptCopyRequest, but with a // DecryptCopyRequestR - same as DecryptCopyRequest, but with a
// Reader // Reader
func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string, metadata map[string]string) (io.Reader, error) { func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
var ( var (
key []byte key []byte
err error err error
@ -343,7 +343,7 @@ func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string,
return nil, err return nil, err
} }
} }
return newDecryptReader(client, key, bucket, object, 0, metadata) return newDecryptReader(client, key, bucket, object, seqNumber, metadata)
} }
func newDecryptReader(client io.Reader, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { func newDecryptReader(client io.Reader, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) {
@ -365,17 +365,6 @@ func newDecryptReaderWithObjectKey(client io.Reader, objectEncryptionKey []byte,
return reader, nil return reader, nil
} }
// GetEncryptedOffsetLength - returns encrypted offset and length
// along with sequence number
func GetEncryptedOffsetLength(startOffset, length int64, objInfo ObjectInfo) (seqNumber uint32, encStartOffset, encLength int64) {
if !isEncryptedMultipart(objInfo) {
seqNumber, encStartOffset, encLength = getEncryptedSinglePartOffsetLength(startOffset, length, objInfo)
return
}
seqNumber, encStartOffset, encLength = getEncryptedMultipartsOffsetLength(startOffset, length, objInfo)
return
}
// DecryptBlocksRequestR - same as DecryptBlocksRequest but with a // DecryptBlocksRequestR - same as DecryptBlocksRequest but with a
// reader // reader
func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, offset, func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, offset,
@ -389,7 +378,7 @@ func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, offset,
var reader io.Reader var reader io.Reader
var err error var err error
if copySource { if copySource {
reader, err = DecryptCopyRequestR(inputReader, h, bucket, object, oi.UserDefined) reader, err = DecryptCopyRequestR(inputReader, h, bucket, object, seqNumber, oi.UserDefined)
} else { } else {
reader, err = DecryptRequestWithSequenceNumberR(inputReader, h, bucket, object, seqNumber, oi.UserDefined) reader, err = DecryptRequestWithSequenceNumberR(inputReader, h, bucket, object, seqNumber, oi.UserDefined)
} }

@ -701,30 +701,12 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
getObjectNInfo = api.CacheAPI().GetObjectNInfo getObjectNInfo = api.CacheAPI().GetObjectNInfo
} }
// Get request range.
var rs *HTTPRangeSpec
rangeHeader := r.Header.Get("x-amz-copy-source-range")
if rangeHeader != "" {
var parseRangeErr error
if rs, parseRangeErr = parseRequestRangeSpec(rangeHeader); parseRangeErr != nil {
// Handle only errInvalidRange. Ignore other
// parse error and treat it as regular Get
// request like Amazon S3.
if parseRangeErr == errInvalidRange {
writeErrorResponse(w, ErrInvalidRange, r.URL)
return
}
// log the error.
logger.LogIf(ctx, parseRangeErr)
}
}
var lock = noLock var lock = noLock
if !cpSrcDstSame { if !cpSrcDstSame {
lock = readLock lock = readLock
} }
var rs *HTTPRangeSpec
gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, lock, srcOpts) gr, err := getObjectNInfo(ctx, srcBucket, srcObject, rs, r.Header, lock, srcOpts)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
@ -1485,8 +1467,11 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
} }
// Get the object offset & length // Get the object offset & length
startOffset, length, _ := rs.GetOffsetLength(actualPartSize) startOffset, length, err := rs.GetOffsetLength(actualPartSize)
actualPartSize = length if err != nil {
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return
}
/// maximum copy size for multipart objects in a single operation /// maximum copy size for multipart objects in a single operation
if isMaxAllowedPartSize(length) { if isMaxAllowedPartSize(length) {
@ -1494,8 +1479,8 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
return return
} }
actualPartSize = length
var reader io.Reader var reader io.Reader
var getLength = length
var li ListPartsInfo var li ListPartsInfo
li, err = objectAPI.ListObjectParts(ctx, dstBucket, dstObject, uploadID, 0, 1) li, err = objectAPI.ListObjectParts(ctx, dstBucket, dstObject, uploadID, 0, 1)
@ -1503,6 +1488,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
// Read compression metadata preserved in the init multipart for the decision. // Read compression metadata preserved in the init multipart for the decision.
_, compressPart := li.UserDefined[ReservedMetadataPrefix+"compression"] _, compressPart := li.UserDefined[ReservedMetadataPrefix+"compression"]
isCompressed := compressPart isCompressed := compressPart
@ -1535,7 +1521,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
if objectAPI.IsEncryptionSupported() && !isCompressed { if objectAPI.IsEncryptionSupported() && !isCompressed {
if crypto.IsEncrypted(li.UserDefined) { if crypto.IsEncrypted(li.UserDefined) {
if !hasServerSideEncryptionHeader(r.Header) { if !crypto.SSEC.IsRequested(r.Header) && crypto.SSEC.IsEncrypted(li.UserDefined) {
writeErrorResponse(w, ErrSSEMultipartEncrypted, r.URL) writeErrorResponse(w, ErrSSEMultipartEncrypted, r.URL)
return return
} }
@ -1567,18 +1553,18 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
} }
info := ObjectInfo{Size: length} info := ObjectInfo{Size: length}
size := info.EncryptedSize() srcInfo.Reader, err = hash.NewReader(reader, info.EncryptedSize(), "", "", length)
srcInfo.Reader, err = hash.NewReader(reader, size, "", "", actualPartSize)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return
} }
} }
} }
// Copy source object to destination, if source and destination // Copy source object to destination, if source and destination
// object is same then only metadata is updated. // object is same then only metadata is updated.
partInfo, err := objectAPI.CopyObjectPart(ctx, srcBucket, srcObject, dstBucket, partInfo, err := objectAPI.CopyObjectPart(ctx, srcBucket, srcObject, dstBucket, dstObject, uploadID, partID,
dstObject, uploadID, partID, startOffset, getLength, srcInfo, srcOpts, dstOpts) startOffset, length, srcInfo, srcOpts, dstOpts)
if err != nil { if err != nil {
writeErrorResponse(w, toAPIErrorCode(err), r.URL) writeErrorResponse(w, toAPIErrorCode(err), r.URL)
return return

Loading…
Cancel
Save