mirror of
https://github.com/minio/minio.git
synced 2025-03-30 09:13:41 -04:00
object-handler: skip sha256 calculation if x-amz-content-sha256=="UNSIGNED-PAYLOAD" (#2038)
fixes #2024 #2056
This commit is contained in:
parent
734e779b19
commit
eb5f782c74
@ -27,6 +27,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD" indicates that the
|
||||||
|
// client did not calculate sha256 of the payload.
|
||||||
|
const unsignedPayload = "UNSIGNED-PAYLOAD"
|
||||||
|
|
||||||
|
// Verify if the request http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD"
|
||||||
|
func isRequestUnsignedPayload(r *http.Request) bool {
|
||||||
|
return r.Header.Get("x-amz-content-sha256") == unsignedPayload
|
||||||
|
}
|
||||||
|
|
||||||
// Verify if request has JWT.
|
// Verify if request has JWT.
|
||||||
func isRequestJWT(r *http.Request) bool {
|
func isRequestJWT(r *http.Request) bool {
|
||||||
if _, ok := r.Header["Authorization"]; ok {
|
if _, ok := r.Header["Authorization"]; ok {
|
||||||
@ -126,10 +135,16 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) {
|
|||||||
// Populate back the payload.
|
// Populate back the payload.
|
||||||
r.Body = ioutil.NopCloser(bytes.NewReader(payload))
|
r.Body = ioutil.NopCloser(bytes.NewReader(payload))
|
||||||
validateRegion := true // Validate region.
|
validateRegion := true // Validate region.
|
||||||
|
var sha256sum string
|
||||||
|
if skipSHA256Calculation(r) {
|
||||||
|
sha256sum = unsignedPayload
|
||||||
|
} else {
|
||||||
|
sha256sum = hex.EncodeToString(sum256(payload))
|
||||||
|
}
|
||||||
if isRequestSignatureV4(r) {
|
if isRequestSignatureV4(r) {
|
||||||
return doesSignatureMatch(hex.EncodeToString(sum256(payload)), r, validateRegion)
|
return doesSignatureMatch(sha256sum, r, validateRegion)
|
||||||
} else if isRequestPresignedSignatureV4(r) {
|
} else if isRequestPresignedSignatureV4(r) {
|
||||||
return doesPresignedSignatureMatch(hex.EncodeToString(sum256(payload)), r, validateRegion)
|
return doesPresignedSignatureMatch(sha256sum, r, validateRegion)
|
||||||
}
|
}
|
||||||
return ErrAccessDenied
|
return ErrAccessDenied
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,14 @@ func setGetRespHeaders(w http.ResponseWriter, reqParams url.Values) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// http Header "x-amz-content-sha256" == "UNSIGNED-PAYLOAD" indicates that the
|
||||||
|
// client did not calculate sha256 of the payload. Hence we skip calculating sha256.
|
||||||
|
// We also skip calculating sha256 for presigned requests without "x-amz-content-sha256" header.
|
||||||
|
func skipSHA256Calculation(r *http.Request) bool {
|
||||||
|
shaHeader := r.Header.Get("X-Amz-Content-Sha256")
|
||||||
|
return isRequestUnsignedPayload(r) || (isRequestPresignedSignatureV4(r) && shaHeader == "")
|
||||||
|
}
|
||||||
|
|
||||||
// errAllowableNotFound - For an anon user, return 404 if have ListBucket, 403 otherwise
|
// errAllowableNotFound - For an anon user, return 404 if have ListBucket, 403 otherwise
|
||||||
// this is in keeping with the permissions sections of the docs of both:
|
// this is in keeping with the permissions sections of the docs of both:
|
||||||
// HEAD Object: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html
|
// HEAD Object: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectHEAD.html
|
||||||
@ -594,51 +602,73 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
// Create anonymous object.
|
// Create anonymous object.
|
||||||
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, r.Body, metadata)
|
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, r.Body, metadata)
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
// Initialize a pipe for data pipe line.
|
validateRegion := true // Validate region.
|
||||||
reader, writer := io.Pipe()
|
|
||||||
var wg = &sync.WaitGroup{}
|
if skipSHA256Calculation(r) {
|
||||||
// Start writing in a routine.
|
// Either sha256-header is "UNSIGNED-PAYLOAD" or this is a presigned PUT
|
||||||
wg.Add(1)
|
// request without sha256-header.
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
shaWriter := sha256.New()
|
|
||||||
multiWriter := io.MultiWriter(shaWriter, writer)
|
|
||||||
if _, wErr := io.CopyN(multiWriter, r.Body, size); wErr != nil {
|
|
||||||
// Pipe closed.
|
|
||||||
if wErr == io.ErrClosedPipe {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errorIf(wErr, "Unable to read from HTTP body.")
|
|
||||||
writer.CloseWithError(wErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
shaPayload := shaWriter.Sum(nil)
|
|
||||||
validateRegion := true // Validate region.
|
|
||||||
var s3Error APIErrorCode
|
var s3Error APIErrorCode
|
||||||
if isRequestSignatureV4(r) {
|
if isRequestSignatureV4(r) {
|
||||||
s3Error = doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
s3Error = doesSignatureMatch(unsignedPayload, r, validateRegion)
|
||||||
} else if isRequestPresignedSignatureV4(r) {
|
} else if isRequestPresignedSignatureV4(r) {
|
||||||
s3Error = doesPresignedSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
s3Error = doesPresignedSignatureMatch(unsignedPayload, r, validateRegion)
|
||||||
}
|
}
|
||||||
var sErr error
|
|
||||||
if s3Error != ErrNone {
|
if s3Error != ErrNone {
|
||||||
if s3Error == ErrSignatureDoesNotMatch {
|
if s3Error == ErrSignatureDoesNotMatch {
|
||||||
sErr = errSignatureMismatch
|
err = errSignatureMismatch
|
||||||
} else {
|
} else {
|
||||||
sErr = fmt.Errorf("%v", getAPIError(s3Error))
|
err = fmt.Errorf("%v", getAPIError(s3Error))
|
||||||
}
|
}
|
||||||
writer.CloseWithError(sErr)
|
} else {
|
||||||
return
|
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, r.Body, metadata)
|
||||||
}
|
}
|
||||||
writer.Close()
|
} else {
|
||||||
}()
|
// Sha256 of payload has to be calculated and matched with what was sent in the header.
|
||||||
|
// Initialize a pipe for data pipe line.
|
||||||
|
reader, writer := io.Pipe()
|
||||||
|
var wg = &sync.WaitGroup{}
|
||||||
|
// Start writing in a routine.
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
shaWriter := sha256.New()
|
||||||
|
multiWriter := io.MultiWriter(shaWriter, writer)
|
||||||
|
if _, wErr := io.CopyN(multiWriter, r.Body, size); wErr != nil {
|
||||||
|
// Pipe closed.
|
||||||
|
if wErr == io.ErrClosedPipe {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errorIf(wErr, "Unable to read from HTTP body.")
|
||||||
|
writer.CloseWithError(wErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shaPayload := shaWriter.Sum(nil)
|
||||||
|
var s3Error APIErrorCode
|
||||||
|
if isRequestSignatureV4(r) {
|
||||||
|
s3Error = doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||||
|
} else if isRequestPresignedSignatureV4(r) {
|
||||||
|
s3Error = doesPresignedSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||||
|
}
|
||||||
|
var sErr error
|
||||||
|
if s3Error != ErrNone {
|
||||||
|
if s3Error == ErrSignatureDoesNotMatch {
|
||||||
|
sErr = errSignatureMismatch
|
||||||
|
} else {
|
||||||
|
sErr = fmt.Errorf("%v", getAPIError(s3Error))
|
||||||
|
}
|
||||||
|
writer.CloseWithError(sErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writer.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
// Create object.
|
// Create object.
|
||||||
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, reader, metadata)
|
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, reader, metadata)
|
||||||
// Close the pipe.
|
// Close the pipe.
|
||||||
reader.Close()
|
reader.Close()
|
||||||
// Wait for all the routines to finish.
|
// Wait for all the routines to finish.
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err, "Unable to create an object.")
|
errorIf(err, "Unable to create an object.")
|
||||||
@ -765,31 +795,16 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
hexMD5 := hex.EncodeToString(md5Bytes)
|
hexMD5 := hex.EncodeToString(md5Bytes)
|
||||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, hexMD5)
|
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, hexMD5)
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
// Initialize a pipe for data pipe line.
|
validateRegion := true // Validate region.
|
||||||
reader, writer := io.Pipe()
|
|
||||||
var wg = &sync.WaitGroup{}
|
if skipSHA256Calculation(r) {
|
||||||
// Start writing in a routine.
|
// Either sha256-header is "UNSIGNED-PAYLOAD" or this is a presigned
|
||||||
wg.Add(1)
|
// request without sha256-header.
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
shaWriter := sha256.New()
|
|
||||||
multiWriter := io.MultiWriter(shaWriter, writer)
|
|
||||||
if _, wErr := io.CopyN(multiWriter, r.Body, size); wErr != nil {
|
|
||||||
// Pipe closed, just ignore it.
|
|
||||||
if wErr == io.ErrClosedPipe {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errorIf(wErr, "Unable to read from HTTP request body.")
|
|
||||||
writer.CloseWithError(wErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
shaPayload := shaWriter.Sum(nil)
|
|
||||||
validateRegion := true // Validate region.
|
|
||||||
var s3Error APIErrorCode
|
var s3Error APIErrorCode
|
||||||
if isRequestSignatureV4(r) {
|
if isRequestSignatureV4(r) {
|
||||||
s3Error = doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
s3Error = doesSignatureMatch(unsignedPayload, r, validateRegion)
|
||||||
} else if isRequestPresignedSignatureV4(r) {
|
} else if isRequestPresignedSignatureV4(r) {
|
||||||
s3Error = doesPresignedSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
s3Error = doesPresignedSignatureMatch(unsignedPayload, r, validateRegion)
|
||||||
}
|
}
|
||||||
if s3Error != ErrNone {
|
if s3Error != ErrNone {
|
||||||
if s3Error == ErrSignatureDoesNotMatch {
|
if s3Error == ErrSignatureDoesNotMatch {
|
||||||
@ -797,18 +812,55 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("%v", getAPIError(s3Error))
|
err = fmt.Errorf("%v", getAPIError(s3Error))
|
||||||
}
|
}
|
||||||
writer.CloseWithError(err)
|
} else {
|
||||||
return
|
md5SumHex := hex.EncodeToString(md5Bytes)
|
||||||
|
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, md5SumHex)
|
||||||
}
|
}
|
||||||
// Close the writer.
|
} else {
|
||||||
writer.Close()
|
// Initialize a pipe for data pipe line.
|
||||||
}()
|
reader, writer := io.Pipe()
|
||||||
md5SumHex := hex.EncodeToString(md5Bytes)
|
var wg = &sync.WaitGroup{}
|
||||||
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, md5SumHex)
|
// Start writing in a routine.
|
||||||
// Close the pipe.
|
wg.Add(1)
|
||||||
reader.Close()
|
go func() {
|
||||||
// Wait for all the routines to finish.
|
defer wg.Done()
|
||||||
wg.Wait()
|
shaWriter := sha256.New()
|
||||||
|
multiWriter := io.MultiWriter(shaWriter, writer)
|
||||||
|
if _, wErr := io.CopyN(multiWriter, r.Body, size); wErr != nil {
|
||||||
|
// Pipe closed, just ignore it.
|
||||||
|
if wErr == io.ErrClosedPipe {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
errorIf(wErr, "Unable to read from HTTP request body.")
|
||||||
|
writer.CloseWithError(wErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
shaPayload := shaWriter.Sum(nil)
|
||||||
|
var s3Error APIErrorCode
|
||||||
|
if isRequestSignatureV4(r) {
|
||||||
|
s3Error = doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||||
|
} else if isRequestPresignedSignatureV4(r) {
|
||||||
|
s3Error = doesPresignedSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion)
|
||||||
|
}
|
||||||
|
if s3Error != ErrNone {
|
||||||
|
if s3Error == ErrSignatureDoesNotMatch {
|
||||||
|
err = errSignatureMismatch
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("%v", getAPIError(s3Error))
|
||||||
|
}
|
||||||
|
writer.CloseWithError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Close the writer.
|
||||||
|
writer.Close()
|
||||||
|
}()
|
||||||
|
md5SumHex := hex.EncodeToString(md5Bytes)
|
||||||
|
partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, md5SumHex)
|
||||||
|
// Close the pipe.
|
||||||
|
reader.Close()
|
||||||
|
// Wait for all the routines to finish.
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err, "Unable to create object part.")
|
errorIf(err, "Unable to create object part.")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user