mirror of
https://github.com/minio/minio.git
synced 2025-04-11 23:12:12 -04:00
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.
This commit is contained in:
parent
5fdd768903
commit
64083b9227
@ -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,20 +33,10 @@ 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
|
|
||||||
}
|
|
||||||
errorIf(err, "Unable to xml decode location constraint")
|
|
||||||
// Treat all other failures as XML parsing errors.
|
|
||||||
return ErrMalformedXML
|
|
||||||
} // Successfully decoded, proceed to verify the region.
|
|
||||||
|
|
||||||
// Once region has been obtained we proceed to verify it.
|
// Once region has been obtained we proceed to verify it.
|
||||||
incomingRegion := locationConstraint.Location
|
incomingRegion := locationConstraint.Location
|
||||||
if incomingRegion == "" {
|
if incomingRegion == "" {
|
||||||
@ -61,6 +51,10 @@ func isValidLocationConstraint(r *http.Request) (s3Error APIErrorCode) {
|
|||||||
s3Error = ErrInvalidRegion
|
s3Error = ErrInvalidRegion
|
||||||
}
|
}
|
||||||
return s3Error
|
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 {
|
region = sRegion
|
||||||
|
}
|
||||||
if !isValidRegion(sRegion, region) {
|
if !isValidRegion(sRegion, region) {
|
||||||
return ErrInvalidRegion
|
return ErrInvalidRegion
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
region = sRegion
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
|
// different regions.
|
||||||
|
if region == "" {
|
||||||
|
region = sRegion
|
||||||
|
}
|
||||||
|
// Should validate region, only if region is set.
|
||||||
if !isValidRegion(sRegion, region) {
|
if !isValidRegion(sRegion, region) {
|
||||||
return ErrInvalidRegion
|
return ErrInvalidRegion
|
||||||
}
|
}
|
||||||
}
|
|
||||||
region = sRegion
|
|
||||||
|
|
||||||
// Extract date, if not present throw error.
|
// Extract date, if not present throw error.
|
||||||
var date string
|
var date string
|
||||||
|
@ -108,12 +108,12 @@ func TestDoesPresignedSignatureMatch(t *testing.T) {
|
|||||||
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,7 +126,7 @@ 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,7 +140,7 @@ 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,7 +154,7 @@ 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,7 +168,7 @@ 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,7 +182,7 @@ 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,7 +200,7 @@ 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,7 +218,7 @@ 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…
x
Reference in New Issue
Block a user