Return authHeaderMalformed for an incorrect region in signature (#5618)

master
Harshavardhana 7 years ago committed by kannappanr
parent 7aaf01eb74
commit d90985b6d8
  1. 26
      cmd/signature-v4-parser.go
  2. 21
      cmd/signature-v4-parser_test.go
  3. 46
      cmd/signature-v4.go
  4. 10
      cmd/streaming-signature-v4.go

@ -47,7 +47,7 @@ func (c credentialHeader) getScope() string {
} }
// parse credentialHeader string into its structured form. // parse credentialHeader string into its structured form.
func parseCredentialHeader(credElement string) (ch credentialHeader, aec APIErrorCode) { func parseCredentialHeader(credElement string, region string) (ch credentialHeader, aec APIErrorCode) {
creds := strings.Split(strings.TrimSpace(credElement), "=") creds := strings.Split(strings.TrimSpace(credElement), "=")
if len(creds) != 2 { if len(creds) != 2 {
return ch, ErrMissingFields return ch, ErrMissingFields
@ -71,7 +71,22 @@ func parseCredentialHeader(credElement string) (ch credentialHeader, aec APIErro
if e != nil { if e != nil {
return ch, ErrMalformedCredentialDate return ch, ErrMalformedCredentialDate
} }
cred.scope.region = credElements[2] cred.scope.region = credElements[2]
// Verify if region is valid.
sRegion := cred.scope.region
// 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 ch, ErrAuthorizationHeaderMalformed
}
if credElements[3] != "s3" { if credElements[3] != "s3" {
return ch, ErrInvalidService return ch, ErrInvalidService
} }
@ -150,7 +165,7 @@ func doesV4PresignParamsExist(query url.Values) APIErrorCode {
} }
// Parses all the presigned signature values into separate elements. // Parses all the presigned signature values into separate elements.
func parsePreSignV4(query url.Values) (psv preSignValues, aec APIErrorCode) { func parsePreSignV4(query url.Values, region string) (psv preSignValues, aec APIErrorCode) {
var err APIErrorCode var err APIErrorCode
// verify whether the required query params exist. // verify whether the required query params exist.
err = doesV4PresignParamsExist(query) err = doesV4PresignParamsExist(query)
@ -167,7 +182,7 @@ func parsePreSignV4(query url.Values) (psv preSignValues, aec APIErrorCode) {
preSignV4Values := preSignValues{} preSignV4Values := preSignValues{}
// Save credential. // Save credential.
preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential")) preSignV4Values.Credential, err = parseCredentialHeader("Credential="+query.Get("X-Amz-Credential"), region)
if err != ErrNone { if err != ErrNone {
return psv, err return psv, err
} }
@ -193,6 +208,7 @@ func parsePreSignV4(query url.Values) (psv preSignValues, aec APIErrorCode) {
if preSignV4Values.Expires.Seconds() > 604800 { if preSignV4Values.Expires.Seconds() > 604800 {
return psv, ErrMaximumExpires return psv, ErrMaximumExpires
} }
// Save signed headers. // Save signed headers.
preSignV4Values.SignedHeaders, err = parseSignedHeader("SignedHeaders=" + query.Get("X-Amz-SignedHeaders")) preSignV4Values.SignedHeaders, err = parseSignedHeader("SignedHeaders=" + query.Get("X-Amz-SignedHeaders"))
if err != ErrNone { if err != ErrNone {
@ -214,7 +230,7 @@ func parsePreSignV4(query url.Values) (psv preSignValues, aec APIErrorCode) {
// Authorization: algorithm Credential=accessKeyID/credScope, \ // Authorization: algorithm Credential=accessKeyID/credScope, \
// SignedHeaders=signedHeaders, Signature=signature // SignedHeaders=signedHeaders, Signature=signature
// //
func parseSignV4(v4Auth string) (sv signValues, aec APIErrorCode) { func parseSignV4(v4Auth string, region string) (sv signValues, aec APIErrorCode) {
// Replace all spaced strings, some clients can send spaced // Replace all spaced strings, some clients can send spaced
// parameters and some won't. So we pro-actively remove any spaces // parameters and some won't. So we pro-actively remove any spaces
// to make parsing easier. // to make parsing easier.
@ -240,7 +256,7 @@ func parseSignV4(v4Auth string) (sv signValues, aec APIErrorCode) {
var err APIErrorCode var err APIErrorCode
// Save credentail values. // Save credentail values.
signV4Values.Credential, err = parseCredentialHeader(authFields[0]) signV4Values.Credential, err = parseCredentialHeader(authFields[0], region)
if err != ErrNone { if err != ErrNone {
return sv, err return sv, err
} }

@ -154,6 +154,18 @@ func TestParseCredentialHeader(t *testing.T) {
expectedErrCode: ErrInvalidService, expectedErrCode: ErrInvalidService,
}, },
// Test Case - 7. // Test Case - 7.
// Test case with invalid region.
{
inputCredentialStr: generateCredentialStr(
"Z7IXGOO6BZ0REAN1Q26I",
UTCNow().Format(yyyymmdd),
"us-west-2",
"s3",
"aws4_request"),
expectedCredentials: credentialHeader{},
expectedErrCode: ErrAuthorizationHeaderMalformed,
},
// Test Case - 8.
// Test case with invalid request version. // Test case with invalid request version.
// "aws4_request" is the valid request version. // "aws4_request" is the valid request version.
{ {
@ -166,7 +178,7 @@ func TestParseCredentialHeader(t *testing.T) {
expectedCredentials: credentialHeader{}, expectedCredentials: credentialHeader{},
expectedErrCode: ErrInvalidRequestVersion, expectedErrCode: ErrInvalidRequestVersion,
}, },
// Test Case - 8. // Test Case - 9.
// Test case with right inputs. Expected to return a valid CredentialHeader. // Test case with right inputs. Expected to return a valid CredentialHeader.
// "aws4_request" is the valid request version. // "aws4_request" is the valid request version.
{ {
@ -188,7 +200,7 @@ func TestParseCredentialHeader(t *testing.T) {
} }
for i, testCase := range testCases { for i, testCase := range testCases {
actualCredential, actualErrCode := parseCredentialHeader(testCase.inputCredentialStr) actualCredential, actualErrCode := parseCredentialHeader(testCase.inputCredentialStr, "us-west-1")
// validating the credential fields. // validating the credential fields.
if testCase.expectedErrCode != actualErrCode { if testCase.expectedErrCode != actualErrCode {
t.Fatalf("Test %d: Expected the APIErrCode to be %s, got %s", i+1, errorCodeResponse[testCase.expectedErrCode].Code, errorCodeResponse[actualErrCode].Code) t.Fatalf("Test %d: Expected the APIErrCode to be %s, got %s", i+1, errorCodeResponse[testCase.expectedErrCode].Code, errorCodeResponse[actualErrCode].Code)
@ -399,7 +411,6 @@ func TestParseSignV4(t *testing.T) {
// a valid signature is of form "Signature=" // a valid signature is of form "Signature="
"Signature=abcd", "Signature=abcd",
}, ","), }, ","),
expectedAuthField: signValues{ expectedAuthField: signValues{
Credential: generateCredentials( Credential: generateCredentials(
t, t,
@ -416,7 +427,7 @@ func TestParseSignV4(t *testing.T) {
} }
for i, testCase := range testCases { for i, testCase := range testCases {
parsedAuthField, actualErrCode := parseSignV4(testCase.inputV4AuthStr) parsedAuthField, actualErrCode := parseSignV4(testCase.inputV4AuthStr, "")
if testCase.expectedErrCode != actualErrCode { if testCase.expectedErrCode != actualErrCode {
t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode)
@ -783,7 +794,7 @@ func TestParsePreSignV4(t *testing.T) {
inputQuery.Set(testCase.inputQueryKeyVals[j], testCase.inputQueryKeyVals[j+1]) inputQuery.Set(testCase.inputQueryKeyVals[j], testCase.inputQueryKeyVals[j+1])
} }
// call the function under test. // call the function under test.
parsedPreSign, actualErrCode := parsePreSignV4(inputQuery) parsedPreSign, actualErrCode := parsePreSignV4(inputQuery, "")
if testCase.expectedErrCode != actualErrCode { if testCase.expectedErrCode != actualErrCode {
t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode) t.Fatalf("Test %d: Expected the APIErrCode to be %d, got %d", i+1, testCase.expectedErrCode, actualErrCode)
} }

@ -167,7 +167,7 @@ func doesPolicySignatureV4Match(formValues http.Header) APIErrorCode {
region := globalServerConfig.GetRegion() region := globalServerConfig.GetRegion()
// Parse credential tag. // Parse credential tag.
credHeader, err := parseCredentialHeader("Credential=" + formValues.Get("X-Amz-Credential")) credHeader, err := parseCredentialHeader("Credential="+formValues.Get("X-Amz-Credential"), region)
if err != ErrNone { if err != ErrNone {
return ErrMissingFields return ErrMissingFields
} }
@ -177,14 +177,8 @@ func doesPolicySignatureV4Match(formValues http.Header) APIErrorCode {
return ErrInvalidAccessKeyID return ErrInvalidAccessKeyID
} }
// Verify if the region is valid.
sRegion := credHeader.scope.region
if !isValidRegion(sRegion, region) {
return ErrInvalidRegion
}
// Get signing key. // Get signing key.
signingKey := getSigningKey(cred.SecretKey, credHeader.scope.date, sRegion) signingKey := getSigningKey(cred.SecretKey, credHeader.scope.date, credHeader.scope.region)
// Get signature. // Get signature.
newSignature := getSignature(signingKey, formValues.Get("Policy")) newSignature := getSignature(signingKey, formValues.Get("Policy"))
@ -209,7 +203,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
req := *r req := *r
// Parse request query string. // Parse request query string.
pSignValues, err := parsePreSignV4(req.URL.Query()) pSignValues, err := parsePreSignV4(req.URL.Query(), region)
if err != ErrNone { if err != ErrNone {
return err return err
} }
@ -219,16 +213,6 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
return ErrInvalidAccessKeyID return ErrInvalidAccessKeyID
} }
// Verify if region is valid.
sRegion := pSignValues.Credential.scope.region
// 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. // Extract all the signed headers along with its values.
extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, r) extractedSignedHeaders, errCode := extractSignedHeaders(pSignValues.SignedHeaders, r)
if errCode != ErrNone { if errCode != ErrNone {
@ -260,7 +244,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
query.Set("X-Amz-Date", t.Format(iso8601Format)) query.Set("X-Amz-Date", t.Format(iso8601Format))
query.Set("X-Amz-Expires", strconv.Itoa(expireSeconds)) query.Set("X-Amz-Expires", strconv.Itoa(expireSeconds))
query.Set("X-Amz-SignedHeaders", getSignedHeaders(extractedSignedHeaders)) query.Set("X-Amz-SignedHeaders", getSignedHeaders(extractedSignedHeaders))
query.Set("X-Amz-Credential", cred.AccessKey+"/"+getScope(t, sRegion)) query.Set("X-Amz-Credential", cred.AccessKey+"/"+getScope(t, pSignValues.Credential.scope.region))
// Save other headers available in the request parameters. // Save other headers available in the request parameters.
for k, v := range req.URL.Query() { for k, v := range req.URL.Query() {
@ -292,7 +276,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
// Verify if sha256 payload query is same. // Verify if sha256 payload query is same.
if req.URL.Query().Get("X-Amz-Content-Sha256") != "" { if req.URL.Query().Get("X-Amz-Content-Sha256") != "" {
if req.URL.Query().Get("X-Amz-Content-Sha256") != query.Get("X-Amz-Content-Sha256") { if req.URL.Query().Get("X-Amz-Content-Sha256") != query.Get("X-Amz-Content-Sha256") {
return ErrSignatureDoesNotMatch return ErrContentSHA256Mismatch
} }
} }
@ -305,7 +289,7 @@ func doesPresignedSignatureMatch(hashedPayload string, r *http.Request, region s
presignedStringToSign := getStringToSign(presignedCanonicalReq, t, pSignValues.Credential.getScope()) presignedStringToSign := getStringToSign(presignedCanonicalReq, t, pSignValues.Credential.getScope())
// Get hmac presigned signing key. // Get hmac presigned signing key.
presignedSigningKey := getSigningKey(cred.SecretKey, pSignValues.Credential.scope.date, region) presignedSigningKey := getSigningKey(cred.SecretKey, pSignValues.Credential.scope.date, pSignValues.Credential.scope.region)
// Get new signature. // Get new signature.
newSignature := getSignature(presignedSigningKey, presignedStringToSign) newSignature := getSignature(presignedSigningKey, presignedStringToSign)
@ -331,7 +315,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string) AP
v4Auth := req.Header.Get("Authorization") v4Auth := req.Header.Get("Authorization")
// Parse signature version '4' header. // Parse signature version '4' header.
signV4Values, err := parseSignV4(v4Auth) signV4Values, err := parseSignV4(v4Auth, region)
if err != ErrNone { if err != ErrNone {
return err return err
} }
@ -347,20 +331,6 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string) AP
return ErrInvalidAccessKeyID return ErrInvalidAccessKeyID
} }
// Verify if region is valid.
sRegion := signV4Values.Credential.scope.region
// 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
}
// Extract date, if not present throw error. // Extract date, if not present throw error.
var date string var date string
if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" { if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" {
@ -384,7 +354,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, region string) AP
stringToSign := getStringToSign(canonicalRequest, t, signV4Values.Credential.getScope()) stringToSign := getStringToSign(canonicalRequest, t, signV4Values.Credential.getScope())
// Get hmac signing key. // Get hmac signing key.
signingKey := getSigningKey(cred.SecretKey, signV4Values.Credential.scope.date, region) signingKey := getSigningKey(cred.SecretKey, signV4Values.Credential.scope.date, signV4Values.Credential.scope.region)
// Calculate signature. // Calculate signature.
newSignature := getSignature(signingKey, stringToSign) newSignature := getSignature(signingKey, stringToSign)

@ -70,9 +70,6 @@ func calculateSeedSignature(r *http.Request) (signature string, region string, d
// Access credentials. // Access credentials.
cred := globalServerConfig.GetCredential() cred := globalServerConfig.GetCredential()
// Configured region.
confRegion := globalServerConfig.GetRegion()
// Copy request. // Copy request.
req := *r req := *r
@ -80,7 +77,7 @@ func calculateSeedSignature(r *http.Request) (signature string, region string, d
v4Auth := req.Header.Get("Authorization") v4Auth := req.Header.Get("Authorization")
// Parse signature version '4' header. // Parse signature version '4' header.
signV4Values, errCode := parseSignV4(v4Auth) signV4Values, errCode := parseSignV4(v4Auth, globalServerConfig.GetRegion())
if errCode != ErrNone { if errCode != ErrNone {
return "", "", time.Time{}, errCode return "", "", time.Time{}, errCode
} }
@ -105,11 +102,6 @@ func calculateSeedSignature(r *http.Request) (signature string, region string, d
// Verify if region is valid. // Verify if region is valid.
region = signV4Values.Credential.scope.region region = signV4Values.Credential.scope.region
// Should validate region, only if region is set. Some operations
// do not need region validated for example GetBucketLocation.
if !isValidRegion(region, confRegion) {
return "", "", time.Time{}, ErrInvalidRegion
}
// Extract date, if not present throw error. // Extract date, if not present throw error.
var dateStr string var dateStr string

Loading…
Cancel
Save