mirror of
https://github.com/pgsty/minio.git
synced 2026-09-05 18:16:16 +03:00
fix: validate explicit multipart checksum type
Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -420,8 +420,74 @@ func testAPICompleteMultipartChecksumTypeMismatch(obj ObjectLayer, instanceType,
|
|||||||
t.Fatalf("%s: object was created despite a rejected checksum type", instanceType)
|
t.Fatalf("%s: object was created despite a rejected checksum type", instanceType)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run(test.name+"-type-only", func(t *testing.T) {
|
||||||
|
objectName := "type-mismatch/type-only-" + test.name
|
||||||
|
uploadID := newMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName,
|
||||||
|
typ.String(), test.createdType)
|
||||||
|
etags := uploadPartsHTTP(t, apiRouter, credentials, bucketName, objectName, uploadID, typ, partData)
|
||||||
|
partCS := []string{mustChecksum(t, typ, partData[0]), mustChecksum(t, typ, partData[1])}
|
||||||
|
rec := completeMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName, uploadID, etags, partCS,
|
||||||
|
map[string]string{xhttp.AmzChecksumType: test.providedType})
|
||||||
|
if rec.Code != http.StatusBadRequest {
|
||||||
|
t.Fatalf("%s: checksum type-only mismatch returned %d, want 400", instanceType, rec.Code)
|
||||||
|
}
|
||||||
|
apiErr := apiError(t, rec)
|
||||||
|
if apiErr.Code != "BadDigest" {
|
||||||
|
t.Fatalf("%s: expected BadDigest, got %q", instanceType, apiErr.Code)
|
||||||
|
}
|
||||||
|
wantMessage := fmt.Sprintf("The checksum type %s does not match the multipart upload checksum type %s.", test.providedType, test.createdType)
|
||||||
|
if apiErr.Message != wantMessage {
|
||||||
|
t.Fatalf("%s: expected message %q, got %q", instanceType, wantMessage, apiErr.Message)
|
||||||
|
}
|
||||||
|
if _, err := obj.GetObjectInfo(t.Context(), bucketName, objectName, ObjectOptions{}); err == nil {
|
||||||
|
t.Fatalf("%s: object was created despite a rejected checksum type-only assertion", instanceType)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, test := range []struct {
|
||||||
|
name string
|
||||||
|
providedType string
|
||||||
|
withChecksum bool
|
||||||
|
}{
|
||||||
|
{name: "unknown-type-only", providedType: "NOT_A_TYPE"},
|
||||||
|
{name: "lowercase-type-only", providedType: "full_object"},
|
||||||
|
{name: "unknown-with-checksum", providedType: "NOT_A_TYPE", withChecksum: true},
|
||||||
|
} {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
objectName := "type-mismatch/invalid-" + test.name
|
||||||
|
uploadID := newMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName,
|
||||||
|
typ.String(), xhttp.AmzChecksumTypeComposite)
|
||||||
|
etags := uploadPartsHTTP(t, apiRouter, credentials, bucketName, objectName, uploadID, typ, partData)
|
||||||
|
partCS := []string{mustChecksum(t, typ, partData[0]), mustChecksum(t, typ, partData[1])}
|
||||||
|
headers := map[string]string{xhttp.AmzChecksumType: test.providedType}
|
||||||
|
if test.withChecksum {
|
||||||
|
headers[typ.Key()] = mustChecksum(t, typ, full)
|
||||||
|
}
|
||||||
|
rec := completeMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName, uploadID, etags, partCS, headers)
|
||||||
|
if rec.Code != http.StatusBadRequest || apiErrorCode(t, rec) != "InvalidArgument" {
|
||||||
|
t.Fatalf("%s: invalid checksum type returned %d %s", instanceType, rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
if _, err := obj.GetObjectInfo(t.Context(), bucketName, objectName, ObjectOptions{}); err == nil {
|
||||||
|
t.Fatalf("%s: object was created despite an invalid checksum type", instanceType)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("matching-type-only", func(t *testing.T) {
|
||||||
|
objectName := "type-mismatch/matching-type-only"
|
||||||
|
uploadID := newMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName,
|
||||||
|
typ.String(), xhttp.AmzChecksumTypeComposite)
|
||||||
|
etags := uploadPartsHTTP(t, apiRouter, credentials, bucketName, objectName, uploadID, typ, partData)
|
||||||
|
partCS := []string{mustChecksum(t, typ, partData[0]), mustChecksum(t, typ, partData[1])}
|
||||||
|
rec := completeMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName, uploadID, etags, partCS,
|
||||||
|
map[string]string{xhttp.AmzChecksumType: xhttp.AmzChecksumTypeComposite})
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("%s: matching checksum type-only assertion returned %d %s", instanceType, rec.Code, rec.Body.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("omitted-type-is-not-composite", func(t *testing.T) {
|
t.Run("omitted-type-is-not-composite", func(t *testing.T) {
|
||||||
objectName := "type-mismatch/omitted-type"
|
objectName := "type-mismatch/omitted-type"
|
||||||
uploadID := newMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName,
|
uploadID := newMultipartUploadHTTP(t, apiRouter, credentials, bucketName, objectName,
|
||||||
|
|||||||
@@ -1173,9 +1173,9 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
|
|||||||
var checksumType hash.ChecksumType
|
var checksumType hash.ChecksumType
|
||||||
if cs := fi.Metadata[hash.MinIOMultipartChecksum]; cs != "" {
|
if cs := fi.Metadata[hash.MinIOMultipartChecksum]; cs != "" {
|
||||||
checksumType = hash.NewChecksumType(cs, fi.Metadata[hash.MinIOMultipartChecksumType])
|
checksumType = hash.NewChecksumType(cs, fi.Metadata[hash.MinIOMultipartChecksumType])
|
||||||
|
expectedType := checksumType | hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
||||||
if opts.WantChecksum != nil {
|
if opts.WantChecksum != nil {
|
||||||
providedType := opts.WantChecksum.Type | hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
providedType := opts.WantChecksum.Type | hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
||||||
expectedType := checksumType | hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
|
||||||
if providedType.Base() != expectedType.Base() {
|
if providedType.Base() != expectedType.Base() {
|
||||||
return oi, InvalidArgument{
|
return oi, InvalidArgument{
|
||||||
Bucket: bucket,
|
Bucket: bucket,
|
||||||
@@ -1183,8 +1183,16 @@ func (er erasureObjects) CompleteMultipartUpload(ctx context.Context, bucket str
|
|||||||
Err: fmt.Errorf("checksum algorithm mismatch. got %q expected %q", providedType.String(), expectedType.String()),
|
Err: fmt.Errorf("checksum algorithm mismatch. got %q expected %q", providedType.String(), expectedType.String()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if opts.wantChecksumTypeSet && providedType.ObjType() != expectedType.ObjType() {
|
}
|
||||||
return oi, completeMultipartChecksumTypeMismatch(providedType.ObjType(), expectedType.ObjType())
|
if opts.wantChecksumType != "" {
|
||||||
|
providedObjectType := opts.wantChecksumType
|
||||||
|
// CRC64NVME is always canonicalized to FULL_OBJECT. Preserve this
|
||||||
|
// behavior until its exact AWS wire semantics have been probed.
|
||||||
|
if checksumType.Base().Is(hash.ChecksumCRC64NVME) {
|
||||||
|
providedObjectType = xhttp.AmzChecksumTypeFullObject
|
||||||
|
}
|
||||||
|
if providedObjectType != expectedType.ObjType() {
|
||||||
|
return oi, completeMultipartChecksumTypeMismatch(opts.wantChecksumType, expectedType.ObjType())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checksumType |= hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
checksumType |= hash.ChecksumMultipart | hash.ChecksumIncludesMultipart
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ type ObjectOptions struct {
|
|||||||
Expiration ExpirationOptions
|
Expiration ExpirationOptions
|
||||||
LifecycleAuditEvent lcAuditEvent
|
LifecycleAuditEvent lcAuditEvent
|
||||||
|
|
||||||
WantChecksum *hash.Checksum // x-amz-checksum-XXX checksum sent to PutObject/ CompleteMultipartUpload.
|
WantChecksum *hash.Checksum // x-amz-checksum-XXX checksum sent to PutObject/ CompleteMultipartUpload.
|
||||||
wantChecksumTypeSet bool // x-amz-checksum-type was explicitly set on CompleteMultipartUpload.
|
wantChecksumType string // explicit x-amz-checksum-type value on CompleteMultipartUpload.
|
||||||
|
|
||||||
WantServerSideChecksumType hash.ChecksumType // if set, we compute a server-side checksum of this type
|
WantServerSideChecksumType hash.ChecksumType // if set, we compute a server-side checksum of this type
|
||||||
|
|
||||||
|
|||||||
@@ -469,11 +469,17 @@ func completeMultipartOpts(ctx context.Context, r *http.Request, bucket, object
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opts.wantChecksumType = r.Header.Get(xhttp.AmzChecksumType)
|
||||||
|
switch opts.wantChecksumType {
|
||||||
|
case "", xhttp.AmzChecksumTypeComposite, xhttp.AmzChecksumTypeFullObject:
|
||||||
|
default:
|
||||||
|
return opts, hash.ErrInvalidChecksum
|
||||||
|
}
|
||||||
|
|
||||||
opts.WantChecksum, err = hash.GetContentChecksum(r.Header)
|
opts.WantChecksum, err = hash.GetContentChecksum(r.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return opts, err
|
return opts, err
|
||||||
}
|
}
|
||||||
opts.wantChecksumTypeSet = r.Header.Get(xhttp.AmzChecksumType) != ""
|
|
||||||
opts.MTime = mtime
|
opts.MTime = mtime
|
||||||
opts.UserDefined = make(map[string]string)
|
opts.UserDefined = make(map[string]string)
|
||||||
// Transfer SSEC key in opts.EncryptFn
|
// Transfer SSEC key in opts.EncryptFn
|
||||||
|
|||||||
Reference in New Issue
Block a user