mirror of https://github.com/minio/minio.git
				
				
				
			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.
 | ||||
| // Used for parsing the location from the request body for MakeBucketbucket.
 | ||||
| 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"` | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ func sumMD5(data []byte) []byte { | |||
| } | ||||
| 
 | ||||
| // 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 { | ||||
| 		return ErrInternalError | ||||
| 	} | ||||
|  | @ -121,7 +121,6 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) { | |||
| 	} | ||||
| 	// Populate back the payload.
 | ||||
| 	r.Body = ioutil.NopCloser(bytes.NewReader(payload)) | ||||
| 	validateRegion := true // Validate region.
 | ||||
| 	var sha256sum string | ||||
| 	// Skips calculating sha256 on the payload on server,
 | ||||
| 	// if client requested for it.
 | ||||
|  | @ -131,9 +130,9 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) { | |||
| 		sha256sum = hex.EncodeToString(sum256(payload)) | ||||
| 	} | ||||
| 	if isRequestSignatureV4(r) { | ||||
| 		return doesSignatureMatch(sha256sum, r, validateRegion) | ||||
| 		return doesSignatureMatch(sha256sum, r, region) | ||||
| 	} else if isRequestPresignedSignatureV4(r) { | ||||
| 		return doesPresignedSignatureMatch(sha256sum, r, validateRegion) | ||||
| 		return doesPresignedSignatureMatch(sha256sum, r, region) | ||||
| 	} | ||||
| 	return ErrAccessDenied | ||||
| } | ||||
|  | @ -145,13 +144,19 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) { | |||
| // request headers and body are used to calculate the signature validating
 | ||||
| // the client signature present in request.
 | ||||
| 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) | ||||
| 	if aType != authTypePresigned && aType != authTypeSigned { | ||||
| 		// For all unhandled auth types return error AccessDenied.
 | ||||
| 		return ErrAccessDenied | ||||
| 	} | ||||
| 	// Validates the request for both Presigned and Signed.
 | ||||
| 	return isReqAuthenticated(r) | ||||
| 	return isReqAuthenticated(r, region) | ||||
| } | ||||
| 
 | ||||
| // 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 { | ||||
| 			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) | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -82,7 +82,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http | |||
| 			return | ||||
| 		} | ||||
| 	case authTypeSigned, authTypePresigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -149,7 +149,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http | |||
| 			return | ||||
| 		} | ||||
| 	case authTypeSigned, authTypePresigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  |  | |||
|  | @ -94,7 +94,7 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r * | |||
| 			return | ||||
| 		} | ||||
| 	case authTypeSigned, authTypePresigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, "us-east-1"); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -150,7 +150,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			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.
 | ||||
| 	if s3Error := checkAuth(r); s3Error != ErrNone { | ||||
| 	// Proceed to validate signature.
 | ||||
| 	if s3Error := checkAuthWithRegion(r, ""); s3Error != ErrNone { | ||||
| 		errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 		writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 		return | ||||
|  | @ -243,7 +244,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			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.
 | ||||
| 	if s3Error := checkAuth(r); s3Error != ErrNone { | ||||
| 	if s3Error := checkAuthWithRegion(r, "us-east-1"); s3Error != ErrNone { | ||||
| 		errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 		writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 		return | ||||
|  | @ -490,7 +491,7 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  |  | |||
|  | @ -140,7 +140,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht | |||
| 		writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) | ||||
| 		return | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
| 		} | ||||
|  | @ -223,7 +223,7 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r | |||
| 		writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) | ||||
| 		return | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
| 		} | ||||
|  | @ -269,7 +269,7 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht | |||
| 		writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path) | ||||
| 		return | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
| 		} | ||||
|  |  | |||
|  | @ -33,34 +33,28 @@ func isValidLocationConstraint(r *http.Request) (s3Error APIErrorCode) { | |||
| 	// If the request has no body with content-length set to 0,
 | ||||
| 	// we do not have to validate location constraint. Bucket will
 | ||||
| 	// be created at default region.
 | ||||
| 	if r.ContentLength == 0 { | ||||
| 		return ErrNone | ||||
| 	} | ||||
| 	locationConstraint := createBucketLocationConfiguration{} | ||||
| 	if err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength); err != nil { | ||||
| 		if err == io.EOF && r.ContentLength == -1 { | ||||
| 			// EOF is a valid condition here when ContentLength is -1.
 | ||||
| 			return ErrNone | ||||
| 	err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength) | ||||
| 	if err == nil || err == io.EOF { | ||||
| 		// Successfully decoded, proceed to verify the region.
 | ||||
| 		// 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") | ||||
| 		// 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.
 | ||||
| 	incomingRegion := locationConstraint.Location | ||||
| 	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 | ||||
| 	} | ||||
| 	// 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.
 | ||||
|  |  | |||
|  | @ -111,7 +111,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -223,7 +223,7 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -280,7 +280,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -518,7 +518,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -667,7 +667,7 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -707,7 +707,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -765,7 +765,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite | |||
| 			return | ||||
| 		} | ||||
| 	case authTypePresigned, authTypeSigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  | @ -874,7 +874,7 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http. | |||
| 			return | ||||
| 		} | ||||
| 	case authTypeSigned, authTypePresigned: | ||||
| 		if s3Error := isReqAuthenticated(r); s3Error != ErrNone { | ||||
| 		if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone { | ||||
| 			errorIf(errSignatureMismatch, dumpRequest(r)) | ||||
| 			writeErrorResponse(w, r, s3Error, r.URL.Path) | ||||
| 			return | ||||
|  |  | |||
|  | @ -194,13 +194,10 @@ 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(hashedPayload string, r *http.Request, validateRegion bool) APIErrorCode { | ||||
| func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region string) APIErrorCode { | ||||
| 	// Access credentials.
 | ||||
| 	cred := serverConfig.GetCredential() | ||||
| 
 | ||||
| 	// Server region.
 | ||||
| 	region := serverConfig.GetRegion() | ||||
| 
 | ||||
| 	// Copy request
 | ||||
| 	req := *r | ||||
| 
 | ||||
|  | @ -223,15 +220,13 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, validate | |||
| 
 | ||||
| 	// Verify if region is valid.
 | ||||
| 	sRegion := pSignValues.Credential.scope.region | ||||
| 	// 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 { | ||||
| 	// Should validate region, only if region is set.
 | ||||
| 	if region == "" { | ||||
| 		region = sRegion | ||||
| 	} | ||||
| 	if !isValidRegion(sRegion, region) { | ||||
| 		return ErrInvalidRegion | ||||
| 	} | ||||
| 
 | ||||
| 	// Extract all the signed headers along with its values.
 | ||||
| 	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
 | ||||
| //     - 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, validateRegion bool) APIErrorCode { | ||||
| func doesSignatureMatch(hashedPayload string, r *http.Request, region string) APIErrorCode { | ||||
| 	// Access credentials.
 | ||||
| 	cred := serverConfig.GetCredential() | ||||
| 
 | ||||
| 	// Server region.
 | ||||
| 	region := serverConfig.GetRegion() | ||||
| 
 | ||||
| 	// Copy request.
 | ||||
| 	req := *r | ||||
| 
 | ||||
|  | @ -372,14 +364,17 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, validateRegion bo | |||
| 
 | ||||
| 	// Verify if region is valid.
 | ||||
| 	sRegion := signV4Values.Credential.scope.region | ||||
| 	// 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 | ||||
| 		} | ||||
| 	// Region is set to be empty, we use whatever was sent by the
 | ||||
| 	// request and proceed further. This is a work-around to address
 | ||||
| 	// 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) { | ||||
| 		return ErrInvalidRegion | ||||
| 	} | ||||
| 	region = sRegion | ||||
| 
 | ||||
| 	// Extract date, if not present throw error.
 | ||||
| 	var date string | ||||
|  |  | |||
|  | @ -106,15 +106,15 @@ func TestDoesPresignedSignatureMatch(t *testing.T) { | |||
| 	credentialTemplate := "%s/%s/%s/s3/aws4_request" | ||||
| 
 | ||||
| 	testCases := []struct { | ||||
| 		queryParams  map[string]string | ||||
| 		headers      map[string]string | ||||
| 		verifyRegion bool | ||||
| 		expected     APIErrorCode | ||||
| 		queryParams map[string]string | ||||
| 		headers     map[string]string | ||||
| 		region      string | ||||
| 		expected    APIErrorCode | ||||
| 	}{ | ||||
| 		// (0) Should error without a set URL query.
 | ||||
| 		{ | ||||
| 			verifyRegion: false, | ||||
| 			expected:     ErrInvalidQueryParams, | ||||
| 			region:   "us-east-1", | ||||
| 			expected: ErrInvalidQueryParams, | ||||
| 		}, | ||||
| 		// (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-Credential":    fmt.Sprintf(credentialTemplate, "Z7IXGOO6BZ0REAN1Q26I", now.Format(yyyymmdd), "us-west-1"), | ||||
| 			}, | ||||
| 			verifyRegion: false, | ||||
| 			expected:     ErrInvalidAccessKeyID, | ||||
| 			region:   "us-west-1", | ||||
| 			expected: ErrInvalidAccessKeyID, | ||||
| 		}, | ||||
| 		// (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-Content-Sha256": "ThisIsNotThePayloadHash", | ||||
| 			}, | ||||
| 			verifyRegion: false, | ||||
| 			expected:     ErrContentSHA256Mismatch, | ||||
| 			region:   "us-west-1", | ||||
| 			expected: ErrContentSHA256Mismatch, | ||||
| 		}, | ||||
| 		// (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-Content-Sha256": payload, | ||||
| 			}, | ||||
| 			verifyRegion: true, | ||||
| 			expected:     ErrInvalidRegion, | ||||
| 			region:   "us-east-1", | ||||
| 			expected: ErrInvalidRegion, | ||||
| 		}, | ||||
| 		// (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-Content-Sha256": payload, | ||||
| 			}, | ||||
| 			verifyRegion: false, | ||||
| 			expected:     ErrUnsignedHeaders, | ||||
| 			region:   "us-west-1", | ||||
| 			expected: ErrUnsignedHeaders, | ||||
| 		}, | ||||
| 		// (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-Content-Sha256": payload, | ||||
| 			}, | ||||
| 			verifyRegion: true, | ||||
| 			expected:     ErrUnsignedHeaders, | ||||
| 			region:   serverConfig.GetRegion(), | ||||
| 			expected: ErrUnsignedHeaders, | ||||
| 		}, | ||||
| 		// (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-Content-Sha256": payload, | ||||
| 			}, | ||||
| 			verifyRegion: false, | ||||
| 			expected:     ErrExpiredPresignRequest, | ||||
| 			region:   serverConfig.GetRegion(), | ||||
| 			expected: ErrExpiredPresignRequest, | ||||
| 		}, | ||||
| 		// (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-Content-Sha256": payload, | ||||
| 			}, | ||||
| 			verifyRegion: false, | ||||
| 			expected:     ErrSignatureDoesNotMatch, | ||||
| 			region:   serverConfig.GetRegion(), | ||||
| 			expected: ErrSignatureDoesNotMatch, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
|  | @ -243,7 +243,7 @@ func TestDoesPresignedSignatureMatch(t *testing.T) { | |||
| 		} | ||||
| 
 | ||||
| 		// Check if it matches!
 | ||||
| 		err := doesPresignedSignatureMatch(payload, req, testCase.verifyRegion) | ||||
| 		err := doesPresignedSignatureMatch(payload, req, testCase.region) | ||||
| 		if err != testCase.expected { | ||||
| 			t.Errorf("(%d) expected to get %s, instead got %s", i, niceError(testCase.expected), niceError(err)) | ||||
| 		} | ||||
|  |  | |||
|  | @ -19,10 +19,11 @@ package cmd | |||
| import ( | ||||
| 	"encoding/hex" | ||||
| 	"fmt" | ||||
| 	"github.com/minio/sha256-simd" | ||||
| 	"hash" | ||||
| 	"io" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/minio/sha256-simd" | ||||
| ) | ||||
| 
 | ||||
| // 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.
 | ||||
| func (v *signVerifyReader) Verify() error { | ||||
| 	validateRegion := true // Defaults to validating region.
 | ||||
| 	region := serverConfig.GetRegion() | ||||
| 	shaPayloadHex := hex.EncodeToString(v.HashWriter.Sum(nil)) | ||||
| 	if skipContentSha256Cksum(v.Request) { | ||||
| 		// Sets 'UNSIGNED-PAYLOAD' if client requested to not calculated sha256.
 | ||||
|  | @ -58,9 +59,9 @@ func (v *signVerifyReader) Verify() error { | |||
| 	// Signature verification block.
 | ||||
| 	var s3Error APIErrorCode | ||||
| 	if isRequestSignatureV4(v.Request) { | ||||
| 		s3Error = doesSignatureMatch(shaPayloadHex, v.Request, validateRegion) | ||||
| 		s3Error = doesSignatureMatch(shaPayloadHex, v.Request, region) | ||||
| 	} else if isRequestPresignedSignatureV4(v.Request) { | ||||
| 		s3Error = doesPresignedSignatureMatch(shaPayloadHex, v.Request, validateRegion) | ||||
| 		s3Error = doesPresignedSignatureMatch(shaPayloadHex, v.Request, region) | ||||
| 	} else { | ||||
| 		// Couldn't figure out the request type, set the error as AccessDenied.
 | ||||
| 		s3Error = ErrAccessDenied | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue