signature: Region changes should be handled just like AWS. (#2805)

- PutBucket happens with 'us-east-1'.
- ListBuckets happens with any region.
- GetBucketLocation happens with 'us-east-1' and location is returned.
master
Harshavardhana 8 years ago committed by GitHub
parent 5fdd768903
commit 64083b9227
  1. 2
      cmd/api-datatypes.go
  2. 17
      cmd/auth-handler.go
  3. 2
      cmd/auth-handler_test.go
  4. 4
      cmd/bucket-handlers-listobjects.go
  5. 13
      cmd/bucket-handlers.go
  6. 6
      cmd/bucket-policy-handlers.go
  7. 44
      cmd/handler-utils.go
  8. 16
      cmd/object-handlers.go
  9. 39
      cmd/signature-v4.go
  10. 42
      cmd/signature-v4_test.go
  11. 9
      cmd/signature-verify-reader.go

@ -28,7 +28,7 @@ type ObjectIdentifier struct {
// createBucketConfiguration container for bucket configuration request from client. // createBucketConfiguration container for bucket configuration request from client.
// Used for parsing the location from the request body for MakeBucketbucket. // Used for parsing the location from the request body for MakeBucketbucket.
type createBucketLocationConfiguration struct { type createBucketLocationConfiguration struct {
XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CreateBucketConfiguration" json:"-"` XMLName xml.Name `xml:"CreateBucketConfiguration" json:"-"`
Location string `xml:"LocationConstraint"` Location string `xml:"LocationConstraint"`
} }

@ -105,7 +105,7 @@ func sumMD5(data []byte) []byte {
} }
// Verify if request has valid AWS Signature Version '4'. // Verify if request has valid AWS Signature Version '4'.
func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) { func isReqAuthenticated(r *http.Request, region string) (s3Error APIErrorCode) {
if r == nil { if r == nil {
return ErrInternalError return ErrInternalError
} }
@ -121,7 +121,6 @@ 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.
var sha256sum string var sha256sum string
// Skips calculating sha256 on the payload on server, // Skips calculating sha256 on the payload on server,
// if client requested for it. // if client requested for it.
@ -131,9 +130,9 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) {
sha256sum = hex.EncodeToString(sum256(payload)) sha256sum = hex.EncodeToString(sum256(payload))
} }
if isRequestSignatureV4(r) { if isRequestSignatureV4(r) {
return doesSignatureMatch(sha256sum, r, validateRegion) return doesSignatureMatch(sha256sum, r, region)
} else if isRequestPresignedSignatureV4(r) { } else if isRequestPresignedSignatureV4(r) {
return doesPresignedSignatureMatch(sha256sum, r, validateRegion) return doesPresignedSignatureMatch(sha256sum, r, region)
} }
return ErrAccessDenied return ErrAccessDenied
} }
@ -145,13 +144,19 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) {
// request headers and body are used to calculate the signature validating // request headers and body are used to calculate the signature validating
// the client signature present in request. // the client signature present in request.
func checkAuth(r *http.Request) APIErrorCode { func checkAuth(r *http.Request) APIErrorCode {
// Validates the request for both Presigned and Signed
return checkAuthWithRegion(r, serverConfig.GetRegion())
}
// checkAuthWithRegion - similar to checkAuth but takes a custom region.
func checkAuthWithRegion(r *http.Request, region string) APIErrorCode {
// Validates the request for both Presigned and Signed.
aType := getRequestAuthType(r) aType := getRequestAuthType(r)
if aType != authTypePresigned && aType != authTypeSigned { if aType != authTypePresigned && aType != authTypeSigned {
// For all unhandled auth types return error AccessDenied. // For all unhandled auth types return error AccessDenied.
return ErrAccessDenied return ErrAccessDenied
} }
// Validates the request for both Presigned and Signed. return isReqAuthenticated(r, region)
return isReqAuthenticated(r)
} }
// authHandler - handles all the incoming authorization headers and validates them if possible. // authHandler - handles all the incoming authorization headers and validates them if possible.

@ -294,7 +294,7 @@ func TestIsReqAuthenticated(t *testing.T) {
if testCase.s3Error == ErrBadDigest { if testCase.s3Error == ErrBadDigest {
testCase.req.Header.Set("Content-Md5", "garbage") testCase.req.Header.Set("Content-Md5", "garbage")
} }
if s3Error := isReqAuthenticated(testCase.req); s3Error != testCase.s3Error { if s3Error := isReqAuthenticated(testCase.req, serverConfig.GetRegion()); s3Error != testCase.s3Error {
t.Fatalf("Unexpected s3error returned wanted %d, got %d", testCase.s3Error, s3Error) t.Fatalf("Unexpected s3error returned wanted %d, got %d", testCase.s3Error, s3Error)
} }
} }

@ -82,7 +82,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
return return
} }
case authTypeSigned, authTypePresigned: case authTypeSigned, authTypePresigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -149,7 +149,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
return return
} }
case authTypeSigned, authTypePresigned: case authTypeSigned, authTypePresigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return

@ -94,7 +94,7 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
return return
} }
case authTypeSigned, authTypePresigned: case authTypeSigned, authTypePresigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, "us-east-1"); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -150,7 +150,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -197,7 +197,8 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
} }
// List buckets does not support bucket policies, no need to enforce it. // List buckets does not support bucket policies, no need to enforce it.
if s3Error := checkAuth(r); s3Error != ErrNone { // Proceed to validate signature.
if s3Error := checkAuthWithRegion(r, ""); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -243,7 +244,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -357,7 +358,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
} }
// PutBucket does not support policies, use checkAuth to validate signature. // PutBucket does not support policies, use checkAuth to validate signature.
if s3Error := checkAuth(r); s3Error != ErrNone { if s3Error := checkAuthWithRegion(r, "us-east-1"); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -490,7 +491,7 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return

@ -140,7 +140,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path)
return return
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
} }
@ -223,7 +223,7 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path)
return return
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
} }
@ -269,7 +269,7 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path)
return return
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
} }

@ -33,34 +33,28 @@ func isValidLocationConstraint(r *http.Request) (s3Error APIErrorCode) {
// If the request has no body with content-length set to 0, // If the request has no body with content-length set to 0,
// we do not have to validate location constraint. Bucket will // we do not have to validate location constraint. Bucket will
// be created at default region. // be created at default region.
if r.ContentLength == 0 {
return ErrNone
}
locationConstraint := createBucketLocationConfiguration{} locationConstraint := createBucketLocationConfiguration{}
if err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength); err != nil { err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength)
if err == io.EOF && r.ContentLength == -1 { if err == nil || err == io.EOF {
// EOF is a valid condition here when ContentLength is -1. // Successfully decoded, proceed to verify the region.
return ErrNone // Once region has been obtained we proceed to verify it.
incomingRegion := locationConstraint.Location
if incomingRegion == "" {
// Location constraint is empty for region "us-east-1",
// in accordance with protocol.
incomingRegion = "us-east-1"
} }
errorIf(err, "Unable to xml decode location constraint") // Return errInvalidRegion if location constraint does not match
// Treat all other failures as XML parsing errors. // with configured region.
return ErrMalformedXML s3Error = ErrNone
} // Successfully decoded, proceed to verify the region. if serverRegion != incomingRegion {
s3Error = ErrInvalidRegion
// Once region has been obtained we proceed to verify it. }
incomingRegion := locationConstraint.Location return s3Error
if incomingRegion == "" {
// Location constraint is empty for region "us-east-1",
// in accordance with protocol.
incomingRegion = "us-east-1"
}
// Return errInvalidRegion if location constraint does not match
// with configured region.
s3Error = ErrNone
if serverRegion != incomingRegion {
s3Error = ErrInvalidRegion
} }
return s3Error errorIf(err, "Unable to xml decode location constraint")
// Treat all other failures as XML parsing errors.
return ErrMalformedXML
} }
// Supported headers that needs to be extracted. // Supported headers that needs to be extracted.

@ -111,7 +111,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -223,7 +223,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -280,7 +280,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -518,7 +518,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -667,7 +667,7 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter,
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -707,7 +707,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -765,7 +765,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
return return
} }
case authTypePresigned, authTypeSigned: case authTypePresigned, authTypeSigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return
@ -874,7 +874,7 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
return return
} }
case authTypeSigned, authTypePresigned: case authTypeSigned, authTypePresigned:
if s3Error := isReqAuthenticated(r); s3Error != ErrNone { if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
errorIf(errSignatureMismatch, dumpRequest(r)) errorIf(errSignatureMismatch, dumpRequest(r))
writeErrorResponse(w, r, s3Error, r.URL.Path) writeErrorResponse(w, r, s3Error, r.URL.Path)
return return

@ -194,13 +194,10 @@ func doesPolicySignatureMatch(formValues map[string]string) APIErrorCode {
// doesPresignedSignatureMatch - Verify query headers with presigned signature // doesPresignedSignatureMatch - Verify query headers with presigned signature
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html // - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
// returns true if matches, false otherwise. if error is not nil then it is always false // returns true if matches, false otherwise. if error is not nil then it is always false
func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, validateRegion bool) APIErrorCode { func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region string) APIErrorCode {
// Access credentials. // Access credentials.
cred := serverConfig.GetCredential() cred := serverConfig.GetCredential()
// Server region.
region := serverConfig.GetRegion()
// Copy request // Copy request
req := *r req := *r
@ -223,15 +220,13 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, validate
// Verify if region is valid. // Verify if region is valid.
sRegion := pSignValues.Credential.scope.region sRegion := pSignValues.Credential.scope.region
// Should validate region, only if region is set. Some operations // Should validate region, only if region is set.
// do not need region validated for example GetBucketLocation. if region == "" {
if validateRegion {
if !isValidRegion(sRegion, region) {
return ErrInvalidRegion
}
} else {
region = sRegion region = sRegion
} }
if !isValidRegion(sRegion, region) {
return ErrInvalidRegion
}
// Extract all the signed headers along with its values. // Extract all the signed headers along with its values.
extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, req.Header) extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, req.Header)
@ -322,13 +317,10 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, validate
// doesSignatureMatch - Verify authorization header with calculated header in accordance with // doesSignatureMatch - Verify authorization header with calculated header in accordance with
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html // - http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
// returns true if matches, false otherwise. if error is not nil then it is always false // returns true if matches, false otherwise. if error is not nil then it is always false
func doesSignatureMatch(hashedPayload string, r *http.Request, validateRegion bool) APIErrorCode { func doesSignatureMatch(hashedPayload string, r *http.Request, region string) APIErrorCode {
// Access credentials. // Access credentials.
cred := serverConfig.GetCredential() cred := serverConfig.GetCredential()
// Server region.
region := serverConfig.GetRegion()
// Copy request. // Copy request.
req := *r req := *r
@ -372,14 +364,17 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, validateRegion bo
// Verify if region is valid. // Verify if region is valid.
sRegion := signV4Values.Credential.scope.region sRegion := signV4Values.Credential.scope.region
// Should validate region, only if region is set. Some operations // Region is set to be empty, we use whatever was sent by the
// do not need region validated for example GetBucketLocation. // request and proceed further. This is a work-around to address
if validateRegion { // an important problem for ListBuckets() getting signed with
if !isValidRegion(sRegion, region) { // different regions.
return ErrInvalidRegion if region == "" {
} region = sRegion
}
// Should validate region, only if region is set.
if !isValidRegion(sRegion, region) {
return ErrInvalidRegion
} }
region = sRegion
// Extract date, if not present throw error. // Extract date, if not present throw error.
var date string var date string

@ -106,15 +106,15 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
credentialTemplate := "%s/%s/%s/s3/aws4_request" credentialTemplate := "%s/%s/%s/s3/aws4_request"
testCases := []struct { testCases := []struct {
queryParams map[string]string queryParams map[string]string
headers map[string]string headers map[string]string
verifyRegion bool region string
expected APIErrorCode expected APIErrorCode
}{ }{
// (0) Should error without a set URL query. // (0) Should error without a set URL query.
{ {
verifyRegion: false, region: "us-east-1",
expected: ErrInvalidQueryParams, expected: ErrInvalidQueryParams,
}, },
// (1) Should error on an invalid access key. // (1) Should error on an invalid access key.
{ {
@ -126,8 +126,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-SignedHeaders": "host;x-amz-content-sha256;x-amz-date", "X-Amz-SignedHeaders": "host;x-amz-content-sha256;x-amz-date",
"X-Amz-Credential": fmt.Sprintf(credentialTemplate, "Z7IXGOO6BZ0REAN1Q26I", now.Format(yyyymmdd), "us-west-1"), "X-Amz-Credential": fmt.Sprintf(credentialTemplate, "Z7IXGOO6BZ0REAN1Q26I", now.Format(yyyymmdd), "us-west-1"),
}, },
verifyRegion: false, region: "us-west-1",
expected: ErrInvalidAccessKeyID, expected: ErrInvalidAccessKeyID,
}, },
// (2) Should error when the payload sha256 doesn't match. // (2) Should error when the payload sha256 doesn't match.
{ {
@ -140,8 +140,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), "us-west-1"), "X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), "us-west-1"),
"X-Amz-Content-Sha256": "ThisIsNotThePayloadHash", "X-Amz-Content-Sha256": "ThisIsNotThePayloadHash",
}, },
verifyRegion: false, region: "us-west-1",
expected: ErrContentSHA256Mismatch, expected: ErrContentSHA256Mismatch,
}, },
// (3) Should fail with an invalid region. // (3) Should fail with an invalid region.
{ {
@ -154,8 +154,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), "us-west-1"), "X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), "us-west-1"),
"X-Amz-Content-Sha256": payload, "X-Amz-Content-Sha256": payload,
}, },
verifyRegion: true, region: "us-east-1",
expected: ErrInvalidRegion, expected: ErrInvalidRegion,
}, },
// (4) Should NOT fail with an invalid region if it doesn't verify it. // (4) Should NOT fail with an invalid region if it doesn't verify it.
{ {
@ -168,8 +168,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), "us-west-1"), "X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), "us-west-1"),
"X-Amz-Content-Sha256": payload, "X-Amz-Content-Sha256": payload,
}, },
verifyRegion: false, region: "us-west-1",
expected: ErrUnsignedHeaders, expected: ErrUnsignedHeaders,
}, },
// (5) Should fail to extract headers if the host header is not signed. // (5) Should fail to extract headers if the host header is not signed.
{ {
@ -182,8 +182,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), serverConfig.GetRegion()), "X-Amz-Credential": fmt.Sprintf(credentialTemplate, serverConfig.GetCredential().AccessKeyID, now.Format(yyyymmdd), serverConfig.GetRegion()),
"X-Amz-Content-Sha256": payload, "X-Amz-Content-Sha256": payload,
}, },
verifyRegion: true, region: serverConfig.GetRegion(),
expected: ErrUnsignedHeaders, expected: ErrUnsignedHeaders,
}, },
// (6) Should give an expired request if it has expired. // (6) Should give an expired request if it has expired.
{ {
@ -200,8 +200,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-Date": now.AddDate(0, 0, -2).Format(iso8601Format), "X-Amz-Date": now.AddDate(0, 0, -2).Format(iso8601Format),
"X-Amz-Content-Sha256": payload, "X-Amz-Content-Sha256": payload,
}, },
verifyRegion: false, region: serverConfig.GetRegion(),
expected: ErrExpiredPresignRequest, expected: ErrExpiredPresignRequest,
}, },
// (7) Should error if the signature is incorrect. // (7) Should error if the signature is incorrect.
{ {
@ -218,8 +218,8 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
"X-Amz-Date": now.Format(iso8601Format), "X-Amz-Date": now.Format(iso8601Format),
"X-Amz-Content-Sha256": payload, "X-Amz-Content-Sha256": payload,
}, },
verifyRegion: false, region: serverConfig.GetRegion(),
expected: ErrSignatureDoesNotMatch, expected: ErrSignatureDoesNotMatch,
}, },
} }
@ -243,7 +243,7 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
} }
// Check if it matches! // Check if it matches!
err := doesPresignedSignatureMatch(payload, req, testCase.verifyRegion) err := doesPresignedSignatureMatch(payload, req, testCase.region)
if err != testCase.expected { if err != testCase.expected {
t.Errorf("(%d) expected to get %s, instead got %s", i, niceError(testCase.expected), niceError(err)) t.Errorf("(%d) expected to get %s, instead got %s", i, niceError(testCase.expected), niceError(err))
} }

@ -19,10 +19,11 @@ package cmd
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/minio/sha256-simd"
"hash" "hash"
"io" "io"
"net/http" "net/http"
"github.com/minio/sha256-simd"
) )
// signVerifyReader represents an io.Reader compatible interface which // signVerifyReader represents an io.Reader compatible interface which
@ -49,7 +50,7 @@ func isSignVerify(reader io.Reader) bool {
// Verify - verifies signature and returns error upon signature mismatch. // Verify - verifies signature and returns error upon signature mismatch.
func (v *signVerifyReader) Verify() error { func (v *signVerifyReader) Verify() error {
validateRegion := true // Defaults to validating region. region := serverConfig.GetRegion()
shaPayloadHex := hex.EncodeToString(v.HashWriter.Sum(nil)) shaPayloadHex := hex.EncodeToString(v.HashWriter.Sum(nil))
if skipContentSha256Cksum(v.Request) { if skipContentSha256Cksum(v.Request) {
// Sets 'UNSIGNED-PAYLOAD' if client requested to not calculated sha256. // Sets 'UNSIGNED-PAYLOAD' if client requested to not calculated sha256.
@ -58,9 +59,9 @@ func (v *signVerifyReader) Verify() error {
// Signature verification block. // Signature verification block.
var s3Error APIErrorCode var s3Error APIErrorCode
if isRequestSignatureV4(v.Request) { if isRequestSignatureV4(v.Request) {
s3Error = doesSignatureMatch(shaPayloadHex, v.Request, validateRegion) s3Error = doesSignatureMatch(shaPayloadHex, v.Request, region)
} else if isRequestPresignedSignatureV4(v.Request) { } else if isRequestPresignedSignatureV4(v.Request) {
s3Error = doesPresignedSignatureMatch(shaPayloadHex, v.Request, validateRegion) s3Error = doesPresignedSignatureMatch(shaPayloadHex, v.Request, region)
} else { } else {
// Couldn't figure out the request type, set the error as AccessDenied. // Couldn't figure out the request type, set the error as AccessDenied.
s3Error = ErrAccessDenied s3Error = ErrAccessDenied

Loading…
Cancel
Save