diff --git a/auth-handler.go b/auth-handler.go index 286f67ec1..61bec64e7 100644 --- a/auth-handler.go +++ b/auth-handler.go @@ -129,10 +129,11 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) { } // Populate back the payload. r.Body = ioutil.NopCloser(bytes.NewReader(payload)) + validateRegion := true // Validate region. if isRequestSignatureV4(r) { - return doesSignatureMatch(hex.EncodeToString(sum256(payload)), r) + return doesSignatureMatch(hex.EncodeToString(sum256(payload)), r, validateRegion) } else if isRequestPresignedSignatureV4(r) { - return doesPresignedSignatureMatch(r) + return doesPresignedSignatureMatch(r, validateRegion) } return ErrAccessDenied } diff --git a/bucket-handlers.go b/bucket-handlers.go index 106867a36..e9a5265cd 100644 --- a/bucket-handlers.go +++ b/bucket-handlers.go @@ -18,6 +18,8 @@ package main import ( "bytes" + "encoding/base64" + "encoding/hex" "encoding/xml" "io" "io/ioutil" @@ -88,7 +90,28 @@ func (api objectStorageAPI) GetBucketLocationHandler(w http.ResponseWriter, r *h return } case authTypeSigned, authTypePresigned: - if s3Error := isReqAuthenticated(r); s3Error != ErrNone { + payload, e := ioutil.ReadAll(r.Body) + if e != nil { + writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + return + } + // Verify Content-Md5, if payload is set. + if r.Header.Get("Content-Md5") != "" { + if r.Header.Get("Content-Md5") != base64.StdEncoding.EncodeToString(sumMD5(payload)) { + writeErrorResponse(w, r, ErrBadDigest, r.URL.Path) + return + } + } + // Populate back the payload. + r.Body = ioutil.NopCloser(bytes.NewReader(payload)) + var s3Error APIErrorCode // API error code. + validateRegion := false // Validate region. + if isRequestSignatureV4(r) { + s3Error = doesSignatureMatch(hex.EncodeToString(sum256(payload)), r, validateRegion) + } else if isRequestPresignedSignatureV4(r) { + s3Error = doesPresignedSignatureMatch(r, validateRegion) + } + if s3Error != ErrNone { writeErrorResponse(w, r, s3Error, r.URL.Path) return } @@ -117,7 +140,7 @@ func (api objectStorageAPI) GetBucketLocationHandler(w http.ResponseWriter, r *h Location: region, }) } - setCommonHeaders(w) // write headers. + setCommonHeaders(w) // Write headers. writeSuccessResponse(w, encodedSuccessResponse) } @@ -256,7 +279,28 @@ func (api objectStorageAPI) ListBucketsHandler(w http.ResponseWriter, r *http.Re writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) return case authTypeSigned, authTypePresigned: - if s3Error := isReqAuthenticated(r); s3Error != ErrNone { + payload, e := ioutil.ReadAll(r.Body) + if e != nil { + writeErrorResponse(w, r, ErrInternalError, r.URL.Path) + return + } + // Verify Content-Md5, if payload is set. + if r.Header.Get("Content-Md5") != "" { + if r.Header.Get("Content-Md5") != base64.StdEncoding.EncodeToString(sumMD5(payload)) { + writeErrorResponse(w, r, ErrBadDigest, r.URL.Path) + return + } + } + // Populate back the payload. + r.Body = ioutil.NopCloser(bytes.NewReader(payload)) + var s3Error APIErrorCode // API error code. + validateRegion := false // Validate region. + if isRequestSignatureV4(r) { + s3Error = doesSignatureMatch(hex.EncodeToString(sum256(payload)), r, validateRegion) + } else if isRequestPresignedSignatureV4(r) { + s3Error = doesPresignedSignatureMatch(r, validateRegion) + } + if s3Error != ErrNone { writeErrorResponse(w, r, s3Error, r.URL.Path) return } diff --git a/object-handlers.go b/object-handlers.go index 51e2075c7..f58dccdab 100644 --- a/object-handlers.go +++ b/object-handlers.go @@ -601,8 +601,9 @@ func (api objectStorageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Requ // Create anonymous object. objectInfo, err = api.ObjectAPI.PutObject(bucket, object, size, r.Body, nil) case authTypePresigned: + validateRegion := true // Validate region. // For presigned requests verify them right here. - if apiErr := doesPresignedSignatureMatch(r); apiErr != ErrNone { + if apiErr := doesPresignedSignatureMatch(r, validateRegion); apiErr != ErrNone { writeErrorResponse(w, r, apiErr, r.URL.Path) return } @@ -622,7 +623,8 @@ func (api objectStorageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Requ return } shaPayload := shaWriter.Sum(nil) - if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r); apiErr != ErrNone { + validateRegion := true // Validate region. + if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion); apiErr != ErrNone { if apiErr == ErrSignatureDoesNotMatch { writer.CloseWithError(errSignatureMismatch) return @@ -779,8 +781,9 @@ func (api objectStorageAPI) PutObjectPartHandler(w http.ResponseWriter, r *http. // already allowed. partMD5, err = api.ObjectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, hex.EncodeToString(md5Bytes)) case authTypePresigned: + validateRegion := true // Validate region. // For presigned requests verify right here. - apiErr := doesPresignedSignatureMatch(r) + apiErr := doesPresignedSignatureMatch(r, validateRegion) if apiErr != ErrNone { writeErrorResponse(w, r, apiErr, r.URL.Path) return @@ -800,7 +803,8 @@ func (api objectStorageAPI) PutObjectPartHandler(w http.ResponseWriter, r *http. return } shaPayload := shaWriter.Sum(nil) - if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r); apiErr != ErrNone { + validateRegion := true // Validate region. + if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r, validateRegion); apiErr != ErrNone { if apiErr == ErrSignatureDoesNotMatch { writer.CloseWithError(errSignatureMismatch) return diff --git a/signature-v4.go b/signature-v4.go index 9bdd65d19..93d5060e6 100644 --- a/signature-v4.go +++ b/signature-v4.go @@ -195,7 +195,8 @@ func doesPolicySignatureMatch(formValues map[string]string) APIErrorCode { } // Verify if the region is valid. - if !isValidRegion(credHeader.scope.region, region) { + sRegion := credHeader.scope.region + if !isValidRegion(sRegion, region) { return ErrInvalidRegion } @@ -221,7 +222,7 @@ func doesPolicySignatureMatch(formValues map[string]string) APIErrorCode { // doesPresignedSignatureMatch - Verify query headers with presigned signature // - 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 -func doesPresignedSignatureMatch(r *http.Request) APIErrorCode { +func doesPresignedSignatureMatch(r *http.Request, validateRegion bool) APIErrorCode { // Access credentials. cred := serverConfig.GetCredential() @@ -244,8 +245,14 @@ func doesPresignedSignatureMatch(r *http.Request) APIErrorCode { // Verify if region is valid. sRegion := preSignValues.Credential.scope.region - if !isValidRegion(sRegion, region) { - return ErrInvalidRegion + // Should validate region, only if region is set. Some operations + // do not need region validated for example GetBucketLocation. + if validateRegion { + if !isValidRegion(sRegion, region) { + return ErrInvalidRegion + } + } else { + region = sRegion } // Extract all the signed headers along with its values. @@ -267,7 +274,7 @@ func doesPresignedSignatureMatch(r *http.Request) APIErrorCode { query.Set("X-Amz-Date", t.Format(iso8601Format)) query.Set("X-Amz-Expires", strconv.Itoa(expireSeconds)) query.Set("X-Amz-SignedHeaders", getSignedHeaders(extractedSignedHeaders)) - query.Set("X-Amz-Credential", cred.AccessKeyID+"/"+getScope(t, region)) + query.Set("X-Amz-Credential", cred.AccessKeyID+"/"+getScope(t, sRegion)) // Save other headers available in the request parameters. for k, v := range req.URL.Query() { @@ -321,7 +328,7 @@ func doesPresignedSignatureMatch(r *http.Request) APIErrorCode { // doesSignatureMatch - Verify authorization header with calculated header in accordance with // - 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 -func doesSignatureMatch(hashedPayload string, r *http.Request) APIErrorCode { +func doesSignatureMatch(hashedPayload string, r *http.Request, validateRegion bool) APIErrorCode { // Access credentials. cred := serverConfig.GetCredential() @@ -350,8 +357,14 @@ func doesSignatureMatch(hashedPayload string, r *http.Request) APIErrorCode { // Verify if region is valid. sRegion := signV4Values.Credential.scope.region - if !isValidRegion(sRegion, region) { - return ErrInvalidRegion + // Should validate region, only if region is set. Some operations + // do not need region validated for example GetBucketLocation. + if validateRegion { + if !isValidRegion(sRegion, region) { + return ErrInvalidRegion + } + } else { + region = sRegion } // Extract date, if not present throw error.