object-handler: skip sha256 calculation if x-amz-content-sha256=="UNSIGNED-PAYLOAD" (#2038)

fixes #2024 #2056
master
Krishna Srinivas 9 years ago committed by Harshavardhana
parent 734e779b19
commit eb5f782c74
  1. 19
      auth-handler.go
  2. 190
      object-handlers.go

@ -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.
// Create object. reader, writer := io.Pipe()
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, reader, metadata) var wg = &sync.WaitGroup{}
// Close the pipe. // Start writing in a routine.
reader.Close() wg.Add(1)
// Wait for all the routines to finish. go func() {
wg.Wait() 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.
md5Sum, err = api.ObjectAPI.PutObject(bucket, object, size, reader, metadata)
// 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 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…
Cancel
Save