fix: fixes sigv4 and presigned url auth errors.

Fixes #1540
Fixes #1538
Fixes #1513
Fixes #1425

Fixes SigV4 authentication and presigned URL error handling. Adds two sets of errors in the `s3err` package for these authentication mechanisms.

* Adds a check to return a custom "not supported" error when `X-Amz-Security-Token` is present in presigned URLs.
* Adds a check to return a custom "not supported" error when the `AWS4-ECDSA-P256-SHA256` algorithm is used in presigned URLs.
This commit is contained in:
niksis02
2025-09-18 00:09:49 +04:00
parent c2c359e9f0
commit 6176d9eb46
11 changed files with 600 additions and 382 deletions

View File

@@ -17,9 +17,7 @@ package middlewares
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"strconv"
"time"
@@ -52,9 +50,27 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string)
return nil
}
// Check X-Amz-Date header
date := ctx.Get("X-Amz-Date")
if date == "" {
return s3err.GetAPIError(s3err.ErrMissingDateHeader)
}
// Parse the date and check the date validity
tdate, err := time.Parse(iso8601Format, date)
if err != nil {
return s3err.GetAPIError(s3err.ErrMissingDateHeader)
}
// Validate the dates difference
err = utils.ValidateDate(tdate)
if err != nil {
return err
}
authorization := ctx.Get("Authorization")
if authorization == "" {
return s3err.GetAPIError(s3err.ErrAuthHeaderEmpty)
return s3err.GetAPIError(s3err.ErrInvalidAuthHeader)
}
authData, err := utils.ParseAuthorization(authorization)
@@ -63,11 +79,7 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string)
}
if authData.Region != region {
return s3err.APIError{
Code: "SignatureDoesNotMatch",
Description: fmt.Sprintf("Credential should be scoped to a valid Region, not %v", authData.Region),
HTTPStatusCode: http.StatusForbidden,
}
return s3err.MalformedAuth.IncorrectRegion(region, authData.Region)
}
utils.ContextKeyIsRoot.Set(ctx, authData.Access == root.Access)
@@ -80,29 +92,11 @@ func VerifyV4Signature(root RootUserConfig, iam auth.IAMService, region string)
return err
}
utils.ContextKeyAccount.Set(ctx, account)
// Check X-Amz-Date header
date := ctx.Get("X-Amz-Date")
if date == "" {
return s3err.GetAPIError(s3err.ErrMissingDateHeader)
}
// Parse the date and check the date validity
tdate, err := time.Parse(iso8601Format, date)
if err != nil {
return s3err.GetAPIError(s3err.ErrMalformedDate)
}
if date[:8] != authData.Date {
return s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)
return s3err.MalformedAuth.DateMismatch()
}
// Validate the dates difference
err = utils.ValidateDate(tdate)
if err != nil {
return err
}
utils.ContextKeyAccount.Set(ctx, account)
var contentLength int64
contentLengthStr := ctx.Get("Content-Length")

View File

@@ -32,10 +32,15 @@ func VerifyPresignedV4Signature(root RootUserConfig, iam auth.IAMService, region
if utils.ContextKeyPublicBucket.IsSet(ctx) {
return nil
}
if ctx.Query("X-Amz-Signature") == "" {
if !utils.IsPresignedURLAuth(ctx) {
return nil
}
if ctx.Request().URI().QueryArgs().Has("X-Amz-Security-Token") {
// OIDC Authorization with X-Amz-Security-Token is not supported
return s3err.QueryAuthErrors.SecurityTokenNotSupported()
}
// Set in the context the "authenticated" key, in case the authentication succeeds,
// otherwise the middleware will return the caucht error
utils.ContextKeyAuthenticated.Set(ctx, true)

View File

@@ -31,7 +31,7 @@ import (
func AuthorizePublicBucketAccess(be backend.Backend, s3action string, policyPermission auth.Action, permission auth.Permission) fiber.Handler {
return func(ctx *fiber.Ctx) error {
// skip for authenticated requests
if ctx.Query("X-Amz-Algorithm") != "" || ctx.Get("Authorization") != "" {
if utils.IsPresignedURLAuth(ctx) || ctx.Get("Authorization") != "" {
return nil
}

View File

@@ -103,7 +103,7 @@ func (ar *AuthReader) validateSignature() error {
// Parse the date and check the date validity
tdate, err := time.Parse(iso8601Format, date)
if err != nil {
return s3err.GetAPIError(s3err.ErrMalformedDate)
return s3err.GetAPIError(s3err.ErrMissingDateHeader)
}
return CheckValidSignature(ar.ctx, ar.auth, ar.secret, hashPayload, tdate, int64(ar.size))
@@ -184,54 +184,61 @@ func ParseAuthorization(authorization string) (AuthData, error) {
}
if len(authParts) < 2 {
return a, s3err.GetAPIError(s3err.ErrMissingFields)
return a, s3err.GetAPIError(s3err.ErrInvalidAuthHeader)
}
algo := authParts[0]
if algo != "AWS4-HMAC-SHA256" {
return a, s3err.GetAPIError(s3err.ErrSignatureVersionNotSupported)
return a, s3err.GetAPIError(s3err.ErrUnsupportedAuthorizationType)
}
kvData := authParts[1]
kvPairs := strings.Split(kvData, ",")
// we are expecting at least Credential, SignedHeaders, and Signature
// key value pairs here
if len(kvPairs) < 3 {
return a, s3err.GetAPIError(s3err.ErrMissingFields)
if len(kvPairs) != 3 {
return a, s3err.MalformedAuth.MissingComponents()
}
var access, region, signedHeaders, signature, date string
for _, kv := range kvPairs {
for i, kv := range kvPairs {
keyValue := strings.Split(kv, "=")
if len(keyValue) != 2 {
switch {
case strings.HasPrefix(kv, "Credential"):
return a, s3err.GetAPIError(s3err.ErrCredMalformed)
case strings.HasPrefix(kv, "SignedHeaders"):
return a, s3err.GetAPIError(s3err.ErrInvalidQueryParams)
}
return a, s3err.GetAPIError(s3err.ErrMissingFields)
return a, s3err.MalformedAuth.MalformedComponent(kv)
}
key, value := keyValue[0], keyValue[1]
switch i {
case 0:
if key != "Credential" {
return a, s3err.MalformedAuth.MissingCredential()
}
case 1:
if key != "SignedHeaders" {
return a, s3err.MalformedAuth.MissingSignedHeaders()
}
case 2:
if key != "Signature" {
return a, s3err.MalformedAuth.MissingSignature()
}
}
key := strings.TrimSpace(keyValue[0])
value := strings.TrimSpace(keyValue[1])
switch key {
case "Credential":
creds := strings.Split(value, "/")
if len(creds) != 5 {
return a, s3err.GetAPIError(s3err.ErrCredMalformed)
return a, s3err.MalformedAuth.MalformedCredential()
}
if creds[3] != "s3" {
return a, s3err.GetAPIError(s3err.ErrSignatureIncorrService)
return a, s3err.MalformedAuth.IncorrectService(creds[3])
}
if creds[4] != "aws4_request" {
return a, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)
return a, s3err.MalformedAuth.InvalidTerminal(creds[4])
}
_, err := time.Parse(yyyymmdd, creds[1])
if err != nil {
return a, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)
return a, s3err.MalformedAuth.InvalidDateFormat(creds[1])
}
access = creds[0]
date = creds[1]

View File

@@ -18,7 +18,6 @@ import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
@@ -35,6 +34,9 @@ import (
const (
unsignedPayload string = "UNSIGNED-PAYLOAD"
algoHMAC string = "AWS4-HMAC-SHA256"
algoECDSA string = "AWS4-ECDSA-P256-SHA256"
)
// PresignedAuthReader is an io.Reader that validates presigned request authorization
@@ -136,65 +138,70 @@ func ParsePresignedURIParts(ctx *fiber.Ctx) (AuthData, error) {
// Get and verify algorithm query parameter
algo := ctx.Query("X-Amz-Algorithm")
if algo == "" {
return a, s3err.GetAPIError(s3err.ErrInvalidQueryParams)
}
if algo != "AWS4-HMAC-SHA256" {
return a, s3err.GetAPIError(s3err.ErrInvalidQuerySignatureAlgo)
err := validateAlgorithm(algo)
if err != nil {
return a, err
}
// Parse and validate credentials query parameter
credsQuery := ctx.Query("X-Amz-Credential")
if credsQuery == "" {
return a, s3err.GetAPIError(s3err.ErrInvalidQueryParams)
return a, s3err.QueryAuthErrors.MissingRequiredParams()
}
creds := strings.Split(credsQuery, "/")
if len(creds) != 5 {
return a, s3err.GetAPIError(s3err.ErrCredMalformed)
return a, s3err.QueryAuthErrors.MalformedCredential()
}
// validate the service
if creds[3] != "s3" {
return a, s3err.GetAPIError(s3err.ErrSignatureIncorrService)
return a, s3err.QueryAuthErrors.IncorrectService(creds[3])
}
// validate the terminal
if creds[4] != "aws4_request" {
return a, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)
return a, s3err.QueryAuthErrors.IncorrectTerminal(creds[4])
}
_, err := time.Parse(yyyymmdd, creds[1])
// validate the date
_, err = time.Parse(yyyymmdd, creds[1])
if err != nil {
return a, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)
return a, s3err.QueryAuthErrors.InvalidDateFormat(creds[1])
}
region, ok := ContextKeyRegion.Get(ctx).(string)
if !ok {
region = ""
}
// validate the region
if creds[2] != region {
return a, s3err.QueryAuthErrors.IncorrectRegion(region, creds[2])
}
// Parse and validate Date query param
date := ctx.Query("X-Amz-Date")
if date == "" {
return a, s3err.GetAPIError(s3err.ErrInvalidQueryParams)
return a, s3err.QueryAuthErrors.MissingRequiredParams()
}
tdate, err := time.Parse(iso8601Format, date)
if err != nil {
return a, s3err.GetAPIError(s3err.ErrMalformedDate)
return a, s3err.QueryAuthErrors.InvalidXAmzDateFormat()
}
if date[:8] != creds[1] {
return a, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)
}
if ContextKeyRegion.Get(ctx) != creds[2] {
return a, s3err.APIError{
Code: "SignatureDoesNotMatch",
Description: fmt.Sprintf("Credential should be scoped to a valid Region, not %v", creds[2]),
HTTPStatusCode: http.StatusForbidden,
}
return a, s3err.QueryAuthErrors.DateMismatch(creds[1], date[:8])
}
signature := ctx.Query("X-Amz-Signature")
if signature == "" {
return a, s3err.GetAPIError(s3err.ErrInvalidQueryParams)
return a, s3err.QueryAuthErrors.MissingRequiredParams()
}
signedHdrs := ctx.Query("X-Amz-SignedHeaders")
if signedHdrs == "" {
return a, s3err.GetAPIError(s3err.ErrInvalidQueryParams)
return a, s3err.QueryAuthErrors.MissingRequiredParams()
}
// Validate X-Amz-Expires query param and check if request is expired
@@ -215,20 +222,20 @@ func ParsePresignedURIParts(ctx *fiber.Ctx) (AuthData, error) {
func validateExpiration(str string, date time.Time) error {
if str == "" {
return s3err.GetAPIError(s3err.ErrInvalidQueryParams)
return s3err.QueryAuthErrors.MissingRequiredParams()
}
exp, err := strconv.Atoi(str)
if err != nil {
return s3err.GetAPIError(s3err.ErrMalformedExpires)
return s3err.QueryAuthErrors.ExpiresNumber()
}
if exp < 0 {
return s3err.GetAPIError(s3err.ErrNegativeExpires)
return s3err.QueryAuthErrors.ExpiresNegative()
}
if exp > 604800 {
return s3err.GetAPIError(s3err.ErrMaximumExpires)
return s3err.QueryAuthErrors.ExpiresTooLarge()
}
now := time.Now()
@@ -240,3 +247,43 @@ func validateExpiration(str string, date time.Time) error {
return nil
}
// validateAlgorithm validates the algorithm
// for AWS4-ECDSA-P256-SHA256 it returns a custom non AWS error
// currently only AWS4-HMAC-SHA256 algorithm is supported
func validateAlgorithm(algo string) error {
switch algo {
case "":
return s3err.QueryAuthErrors.MissingRequiredParams()
case algoHMAC:
return nil
case algoECDSA:
return s3err.QueryAuthErrors.OnlyHMACSupported()
default:
// all other algorithms are considerd as invalid
return s3err.QueryAuthErrors.UnsupportedAlgorithm()
}
}
// IsPresignedURLAuth determines if the request is presigned:
// which is authorization with query params
func IsPresignedURLAuth(ctx *fiber.Ctx) bool {
algo := ctx.Query("X-Amz-Algorithm")
creds := ctx.Query("X-Amz-Credential")
signature := ctx.Query("X-Amz-Signature")
signedHeaders := ctx.Query("X-Amz-SignedHeaders")
expires := ctx.Query("X-Amz-Expires")
return !isEmpty(algo, creds, signature, signedHeaders, expires)
}
// isEmpty checks if all the given strings are empty
func isEmpty(args ...string) bool {
for _, a := range args {
if a != "" {
return false
}
}
return true
}

View File

@@ -18,6 +18,7 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/versity/versitygw/s3err"
)
@@ -37,7 +38,7 @@ func Test_validateExpiration(t *testing.T) {
str: "",
date: time.Now(),
},
err: s3err.GetAPIError(s3err.ErrInvalidQueryParams),
err: s3err.QueryAuthErrors.MissingRequiredParams(),
},
{
name: "invalid-expiration",
@@ -45,7 +46,7 @@ func Test_validateExpiration(t *testing.T) {
str: "invalid_expiration",
date: time.Now(),
},
err: s3err.GetAPIError(s3err.ErrMalformedExpires),
err: s3err.QueryAuthErrors.ExpiresNumber(),
},
{
name: "negative-expiration",
@@ -53,7 +54,7 @@ func Test_validateExpiration(t *testing.T) {
str: "-320",
date: time.Now(),
},
err: s3err.GetAPIError(s3err.ErrNegativeExpires),
err: s3err.QueryAuthErrors.ExpiresNegative(),
},
{
name: "exceeding-expiration",
@@ -61,7 +62,7 @@ func Test_validateExpiration(t *testing.T) {
str: "6048000",
date: time.Now(),
},
err: s3err.GetAPIError(s3err.ErrMaximumExpires),
err: s3err.QueryAuthErrors.ExpiresTooLarge(),
},
{
name: "expired value",
@@ -98,3 +99,22 @@ func Test_validateExpiration(t *testing.T) {
})
}
}
func Test_validateAlgorithm(t *testing.T) {
tests := []struct {
name string
algo string
err error
}{
{"empty", "", s3err.QueryAuthErrors.MissingRequiredParams()},
{"AWS4-HMAC-SHA256", "AWS4-HMAC-SHA256", nil},
{"AWS4-ECDSA-P256-SHA256", "AWS4-ECDSA-P256-SHA256", s3err.QueryAuthErrors.OnlyHMACSupported()},
{"invalid", "invalid algo", s3err.QueryAuthErrors.UnsupportedAlgorithm()},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateAlgorithm(tt.algo)
assert.EqualValues(t, tt.err, err)
})
}
}

95
s3err/presigned-urls.go Normal file
View File

@@ -0,0 +1,95 @@
// Copyright 2023 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package s3err
import (
"fmt"
"net/http"
)
// Factory for building AuthorizationQueryParametersError errors.
func authQueryParamError(format string, args ...any) APIError {
return APIError{
Code: "AuthorizationQueryParametersError",
Description: fmt.Sprintf(format, args...),
HTTPStatusCode: http.StatusBadRequest,
}
}
var QueryAuthErrors = struct {
UnsupportedAlgorithm func() APIError
MalformedCredential func() APIError
IncorrectService func(string) APIError
IncorrectRegion func(expected, actual string) APIError
IncorrectTerminal func(string) APIError
InvalidDateFormat func(string) APIError
DateMismatch func(expected, actual string) APIError
ExpiresTooLarge func() APIError
ExpiresNegative func() APIError
ExpiresNumber func() APIError
MissingRequiredParams func() APIError
InvalidXAmzDateFormat func() APIError
RequestNotYetValid func() APIError
RequestExpired func() APIError
InvalidAccessKeyId func() APIError
// a custom non-AWS error
OnlyHMACSupported func() APIError
SecurityTokenNotSupported func() APIError
}{
UnsupportedAlgorithm: func() APIError {
return authQueryParamError(`X-Amz-Algorithm only supports "AWS4-HMAC-SHA256 and AWS4-ECDSA-P256-SHA256"`)
},
MalformedCredential: func() APIError {
return authQueryParamError(`Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting "<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request".`)
},
IncorrectService: func(s string) APIError {
return authQueryParamError(`Error parsing the X-Amz-Credential parameter; incorrect service %q. This endpoint belongs to "s3".`, s)
},
IncorrectRegion: func(expected, actual string) APIError {
return authQueryParamError(`Error parsing the X-Amz-Credential parameter; the region %q is wrong; expecting %q`, actual, expected)
},
IncorrectTerminal: func(s string) APIError {
return authQueryParamError(`Error parsing the X-Amz-Credential parameter; incorrect terminal %q. This endpoint uses "aws4_request".`, s)
},
InvalidDateFormat: func(s string) APIError {
return authQueryParamError(`Error parsing the X-Amz-Credential parameter; incorrect date format %q. This date in the credential must be in the format "yyyyMMdd".`, s)
},
DateMismatch: func(expected, actual string) APIError {
return authQueryParamError(`Invalid credential date %q. This date is not the same as X-Amz-Date: %q.`, expected, actual)
},
ExpiresTooLarge: func() APIError {
return authQueryParamError("X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds")
},
ExpiresNegative: func() APIError {
return authQueryParamError("X-Amz-Expires must be non-negative")
},
ExpiresNumber: func() APIError {
return authQueryParamError("X-Amz-Expires should be a number")
},
MissingRequiredParams: func() APIError {
return authQueryParamError("Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.")
},
InvalidXAmzDateFormat: func() APIError {
return authQueryParamError(`X-Amz-Date must be in the ISO8601 Long Format "yyyyMMdd'T'HHmmss'Z'"`)
},
// a custom non-AWS error
OnlyHMACSupported: func() APIError {
return authQueryParamError("X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\"")
},
SecurityTokenNotSupported: func() APIError {
return authQueryParamError("Authorization with X-Amz-Security-Token is not supported")
},
}

View File

@@ -98,8 +98,8 @@ const (
ErrBucketTaggingLimited
ErrObjectTaggingLimited
ErrInvalidURLEncodedTagging
ErrAuthHeaderEmpty
ErrSignatureVersionNotSupported
ErrInvalidAuthHeader
ErrUnsupportedAuthorizationType
ErrMalformedPOSTRequest
ErrPOSTFileRequired
ErrPostPolicyConditionInvalidFormat
@@ -107,24 +107,13 @@ const (
ErrEntityTooLarge
ErrMissingFields
ErrMissingCredTag
ErrCredMalformed
ErrMalformedXML
ErrMalformedDate
ErrMalformedPresignedDate
ErrMalformedCredentialDate
ErrMissingSignHeadersTag
ErrMissingSignTag
ErrUnsignedHeaders
ErrInvalidQueryParams
ErrInvalidQuerySignatureAlgo
ErrExpiredPresignRequest
ErrMalformedExpires
ErrNegativeExpires
ErrMaximumExpires
ErrSignatureDoesNotMatch
ErrSignatureDateDoesNotMatch
ErrSignatureTerminationStr
ErrSignatureIncorrService
ErrContentSHA256Mismatch
ErrInvalidSHA256Paylod
ErrMissingContentLength
@@ -402,14 +391,14 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrAuthHeaderEmpty: {
ErrInvalidAuthHeader: {
Code: "InvalidArgument",
Description: "Authorization header is invalid -- one and only one ' ' (space) required.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrSignatureVersionNotSupported: {
Code: "InvalidRequest",
Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.",
ErrUnsupportedAuthorizationType: {
Code: "InvalidArgument",
Description: "Unsupported Authorization Type",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMalformedPOSTRequest: {
@@ -447,21 +436,6 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "Missing Credential field for this request.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrCredMalformed: {
Code: "AuthorizationQueryParametersError",
Description: "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request\".",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMalformedDate: {
Code: "MalformedDate",
Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMalformedPresignedDate: {
Code: "AuthorizationQueryParametersError",
Description: "X-Amz-Date must be in the ISO8601 Long Format \"yyyyMMdd'T'HHmmss'Z'\".",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingSignHeadersTag: {
Code: "InvalidArgument",
Description: "Signature header missing SignedHeaders field.",
@@ -477,36 +451,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "There were headers present in the request which were not signed.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidQueryParams: {
Code: "AuthorizationQueryParametersError",
Description: "Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidQuerySignatureAlgo: {
Code: "AuthorizationQueryParametersError",
Description: "X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\".",
HTTPStatusCode: http.StatusBadRequest,
},
ErrExpiredPresignRequest: {
Code: "AccessDenied",
Description: "Request has expired.",
HTTPStatusCode: http.StatusForbidden,
},
ErrMalformedExpires: {
Code: "AuthorizationQueryParametersError",
Description: "X-Amz-Expires should be a number.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrNegativeExpires: {
Code: "AuthorizationQueryParametersError",
Description: "X-Amz-Expires must be non-negative.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMaximumExpires: {
Code: "AuthorizationQueryParametersError",
Description: "X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidAccessKeyID: {
Code: "InvalidAccessKeyId",
Description: "The AWS Access Key Id you provided does not exist in our records.",
@@ -522,21 +471,6 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The request signature we calculated does not match the signature you provided. Check your key and signing method.",
HTTPStatusCode: http.StatusForbidden,
},
ErrSignatureDateDoesNotMatch: {
Code: "SignatureDoesNotMatch",
Description: "Date in Credential scope does not match YYYYMMDD from ISO-8601 version of date from HTTP.",
HTTPStatusCode: http.StatusForbidden,
},
ErrSignatureTerminationStr: {
Code: "SignatureDoesNotMatch",
Description: "Credential should be scoped with a valid terminator: 'aws4_request'.",
HTTPStatusCode: http.StatusForbidden,
},
ErrSignatureIncorrService: {
Code: "SignatureDoesNotMatch",
Description: "Credential should be scoped to correct service: s3.",
HTTPStatusCode: http.StatusForbidden,
},
ErrContentSHA256Mismatch: {
Code: "XAmzContentSHA256Mismatch",
Description: "The provided 'x-amz-content-sha256' header does not match what was computed.",
@@ -659,7 +593,7 @@ var errorCodeResponse = map[ErrorCode]APIError{
},
ErrRequestTimeTooSkewed: {
Code: "RequestTimeTooSkewed",
Description: "The difference between the request time and the server's time is too large.",
Description: "The difference between the request time and the current time is too large.",
HTTPStatusCode: http.StatusForbidden,
},
ErrInvalidBucketAclWithObjectOwnership: {

77
s3err/sigv4.go Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2023 Versity Software
// This file is licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package s3err
import (
"fmt"
"net/http"
)
// Factory for building AuthorizationHeaderMalformed errors.
func malformedAuthError(format string, args ...any) APIError {
return APIError{
Code: "AuthorizationHeaderMalformed",
Description: fmt.Sprintf("The authorization header is malformed; %s", fmt.Sprintf(format, args...)),
HTTPStatusCode: http.StatusForbidden,
}
}
var MalformedAuth = struct {
InvalidDateFormat func(string) APIError
MalformedCredential func() APIError
MissingCredential func() APIError
MissingSignature func() APIError
MissingSignedHeaders func() APIError
InvalidTerminal func(string) APIError
IncorrectRegion func(expected, actual string) APIError
IncorrectService func(string) APIError
MalformedComponent func(string) APIError
MissingComponents func() APIError
DateMismatch func() APIError
}{
InvalidDateFormat: func(s string) APIError {
return malformedAuthError("incorrect date format %q. This date in the credential must be in the format \"yyyyMMdd\".", s)
},
MalformedCredential: func() APIError {
return malformedAuthError("the Credential is mal-formed; expecting \"<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request\".")
},
MissingCredential: func() APIError {
return malformedAuthError("missing Credential.")
},
MissingSignature: func() APIError {
return malformedAuthError("missing Signature.")
},
MissingSignedHeaders: func() APIError {
return malformedAuthError("missing SignedHeaders.")
},
InvalidTerminal: func(s string) APIError {
return malformedAuthError("incorrect terminal %q. This endpoint uses \"aws4_request\".", s)
},
IncorrectRegion: func(expected, actual string) APIError {
return malformedAuthError("the region %q is wrong; expecting %q", actual, expected)
},
IncorrectService: func(s string) APIError {
return malformedAuthError("incorrect service %q. This endpoint belongs to \"s3\".", s)
},
MalformedComponent: func(s string) APIError {
return malformedAuthError("the authorization component %q is malformed.", s)
},
MissingComponents: func() APIError {
return malformedAuthError("the authorization header requires three components: Credential, SignedHeaders, and Signature.")
},
DateMismatch: func() APIError {
return malformedAuthError("The authorization header is malformed; Invalid credential date. Date is not the same as X-Amz-Date.")
},
}

View File

@@ -17,16 +17,20 @@ package integration
func TestAuthentication(s *S3Conf) {
Authentication_invalid_auth_header(s)
Authentication_unsupported_signature_version(s)
Authentication_malformed_credentials(s)
Authentication_malformed_credentials_invalid_parts(s)
Authentication_credentials_terminated_string(s)
Authentication_missing_components(s)
Authentication_malformed_component(s)
Authentication_missing_credentials(s)
Authentication_missing_signedheaders(s)
Authentication_missing_signature(s)
Authentication_malformed_credential(s)
Authentication_credentials_invalid_terminal(s)
Authentication_credentials_incorrect_service(s)
Authentication_credentials_incorrect_region(s)
Authentication_credentials_invalid_date(s)
Authentication_credentials_future_date(s)
Authentication_credentials_past_date(s)
Authentication_credentials_non_existing_access_key(s)
Authentication_invalid_signed_headers(s)
//TODO: handle the case with signed headers
Authentication_missing_date_header(s)
Authentication_invalid_date_header(s)
Authentication_date_mismatch(s)
@@ -37,10 +41,13 @@ func TestAuthentication(s *S3Conf) {
}
func TestPresignedAuthentication(s *S3Conf) {
PresignedAuth_security_token_not_supported(s)
PresignedAuth_unsupported_algorithm(s)
PresignedAuth_ECDSA_not_supported(s)
PresignedAuth_missing_signature_query_param(s)
PresignedAuth_missing_credentials_query_param(s)
PresignedAuth_malformed_creds_invalid_parts(s)
PresignedAuth_malformed_creds_invalid_parts(s)
PresignedAuth_creds_invalid_terminal(s)
PresignedAuth_creds_incorrect_service(s)
PresignedAuth_creds_incorrect_region(s)
PresignedAuth_creds_invalid_date(s)
@@ -1020,16 +1027,19 @@ func GetIntTests() IntTests {
return IntTests{
"Authentication_invalid_auth_header": Authentication_invalid_auth_header,
"Authentication_unsupported_signature_version": Authentication_unsupported_signature_version,
"Authentication_malformed_credentials": Authentication_malformed_credentials,
"Authentication_malformed_credentials_invalid_parts": Authentication_malformed_credentials_invalid_parts,
"Authentication_credentials_terminated_string": Authentication_credentials_terminated_string,
"Authentication_missing_components": Authentication_missing_components,
"Authentication_malformed_component": Authentication_malformed_component,
"Authentication_missing_credentials": Authentication_missing_credentials,
"Authentication_missing_signedheaders": Authentication_missing_signedheaders,
"Authentication_missing_signature": Authentication_missing_signature,
"Authentication_malformed_credential": Authentication_malformed_credential,
"Authentication_credentials_invalid_terminal": Authentication_credentials_invalid_terminal,
"Authentication_credentials_incorrect_service": Authentication_credentials_incorrect_service,
"Authentication_credentials_incorrect_region": Authentication_credentials_incorrect_region,
"Authentication_credentials_invalid_date": Authentication_credentials_invalid_date,
"Authentication_credentials_future_date": Authentication_credentials_future_date,
"Authentication_credentials_past_date": Authentication_credentials_past_date,
"Authentication_credentials_non_existing_access_key": Authentication_credentials_non_existing_access_key,
"Authentication_invalid_signed_headers": Authentication_invalid_signed_headers,
"Authentication_missing_date_header": Authentication_missing_date_header,
"Authentication_invalid_date_header": Authentication_invalid_date_header,
"Authentication_date_mismatch": Authentication_date_mismatch,
@@ -1037,10 +1047,13 @@ func GetIntTests() IntTests {
"Authentication_invalid_sha256_payload_hash": Authentication_invalid_sha256_payload_hash,
"Authentication_incorrect_md5": Authentication_incorrect_md5,
"Authentication_signature_error_incorrect_secret_key": Authentication_signature_error_incorrect_secret_key,
"PresignedAuth_security_token_not_supported": PresignedAuth_security_token_not_supported,
"PresignedAuth_unsupported_algorithm": PresignedAuth_unsupported_algorithm,
"PresignedAuth_ECDSA_not_supported": PresignedAuth_ECDSA_not_supported,
"PresignedAuth_missing_signature_query_param": PresignedAuth_missing_signature_query_param,
"PresignedAuth_missing_credentials_query_param": PresignedAuth_missing_credentials_query_param,
"PresignedAuth_malformed_creds_invalid_parts": PresignedAuth_malformed_creds_invalid_parts,
"PresignedAuth_creds_invalid_terminator": PresignedAuth_creds_invalid_terminator,
"PresignedAuth_creds_invalid_terminal": PresignedAuth_creds_invalid_terminal,
"PresignedAuth_creds_incorrect_service": PresignedAuth_creds_incorrect_service,
"PresignedAuth_creds_incorrect_region": PresignedAuth_creds_incorrect_region,
"PresignedAuth_creds_invalid_date": PresignedAuth_creds_invalid_date,

View File

@@ -68,11 +68,7 @@ func Authentication_invalid_auth_header(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingFields)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAuthHeader))
})
}
@@ -94,16 +90,117 @@ func Authentication_unsupported_signature_version(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureVersionNotSupported)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrUnsupportedAuthorizationType))
})
}
func Authentication_malformed_credentials(s *S3Conf) error {
testName := "Authentication_malformed_credentials"
func Authentication_missing_components(s *S3Conf) error {
testName := "Authentication_missing_components"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
// missing SignedHeaders component
req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential=access/20250912/us-east-1/s3/aws4_request,Signature=5fb279ae552098ea7c5c807df54cdb159e74939e19449b29831552639ec34b29")
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingComponents())
})
}
func Authentication_malformed_component(s *S3Conf) error {
testName := "Authentication_malformed_component"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
// malformed SignedHeaders
req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential=access/20250912/us-east-1/s3/aws4_request,SignedHeaders-Content-Length,Signature=signature")
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MalformedComponent("SignedHeaders-Content-Length"))
})
}
func Authentication_missing_credentials(s *S3Conf) error {
testName := "Authentication_missing_credentials"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
// missing Credentials
req.Header.Set("Authorization", "AWS4-HMAC-SHA256 missing_creds=access/20250912/us-east-1/s3/aws4_request,SignedHeaders=content-length;x-amz-date,Signature=5fb279ae552098ea7c5c807df54cdb159e74939e19449b29831552639ec34b29")
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingCredential())
})
}
func Authentication_missing_signedheaders(s *S3Conf) error {
testName := "Authentication_missing_signedheaders"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
// missing SignedHeaders
req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential=access/20250912/us-east-1/s3/aws4_request,missing=content-length;x-amz-date,Signature=5fb279ae552098ea7c5c807df54cdb159e74939e19449b29831552639ec34b29")
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingSignedHeaders())
})
}
func Authentication_missing_signature(s *S3Conf) error {
testName := "Authentication_missing_signature"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
// missing Signature
req.Header.Set("Authorization", "AWS4-HMAC-SHA256 Credential=access/20250912/us-east-1/s3/aws4_request,SignedHeaders=content-length;x-amz-date,missing=5fb279ae552098ea7c5c807df54cdb159e74939e19449b29831552639ec34b29")
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MissingSignature())
})
}
func Authentication_malformed_credential(s *S3Conf) error {
testName := "Authentication_malformed_credential"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
@@ -113,7 +210,7 @@ func Authentication_malformed_credentials(s *S3Conf) error {
}, func(req *http.Request) error {
authHdr := req.Header.Get("Authorization")
regExp := regexp.MustCompile("Credential=[^,]+,")
hdr := regExp.ReplaceAllString(authHdr, "Credential-access/32234/us-east-1/s3/aws4_request,")
hdr := regExp.ReplaceAllString(authHdr, "Credential=access/32234/us-east-1/s3/extra/things,")
req.Header.Set("Authorization", hdr)
resp, err := s.httpClient.Do(req)
@@ -121,43 +218,12 @@ func Authentication_malformed_credentials(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.MalformedCredential())
})
}
func Authentication_malformed_credentials_invalid_parts(s *S3Conf) error {
testName := "Authentication_malformed_credentials_invalid_parts"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
authHdr := req.Header.Get("Authorization")
regExp := regexp.MustCompile("Credential=[^,]+,")
hdr := regExp.ReplaceAllString(authHdr, "Credential=access/32234/us-east-1/s3,")
req.Header.Set("Authorization", hdr)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil {
return err
}
return nil
})
}
func Authentication_credentials_terminated_string(s *S3Conf) error {
testName := "Authentication_credentials_terminated_string"
func Authentication_credentials_invalid_terminal(s *S3Conf) error {
testName := "Authentication_credentials_invalid_terminal"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
@@ -175,11 +241,7 @@ func Authentication_credentials_terminated_string(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.InvalidTerminal("aws_request"))
})
}
@@ -189,20 +251,20 @@ func Authentication_credentials_incorrect_service(s *S3Conf) error {
testName: testName,
method: http.MethodGet,
body: nil,
service: "ec2",
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
authHdr := req.Header.Get("Authorization")
regExp := regexp.MustCompile("Credential=[^,]+,")
hdr := regExp.ReplaceAllString(authHdr, "Credential=access/32234/us-east-1/ec2/aws4_request,")
req.Header.Set("Authorization", hdr)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectService("ec2"))
})
}
@@ -221,22 +283,12 @@ func Authentication_credentials_incorrect_region(s *S3Conf) error {
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
apiErr := s3err.APIError{
Code: "SignatureDoesNotMatch",
Description: fmt.Sprintf("Credential should be scoped to a valid Region, not %v", cfg.awsRegion),
HTTPStatusCode: http.StatusForbidden,
}
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, apiErr); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.IncorrectRegion(s.awsRegion, cfg.awsRegion))
})
}
@@ -259,11 +311,7 @@ func Authentication_credentials_invalid_date(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.InvalidDateFormat("3223423234"))
})
}
@@ -360,38 +408,7 @@ func Authentication_credentials_non_existing_access_key(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID)); err != nil {
return err
}
return nil
})
}
func Authentication_invalid_signed_headers(s *S3Conf) error {
testName := "Authentication_invalid_signed_headers"
return authHandler(s, &authConfig{
testName: testName,
method: http.MethodGet,
body: nil,
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
authHdr := req.Header.Get("Authorization")
regExp := regexp.MustCompile("SignedHeaders=[^,]+,")
hdr := regExp.ReplaceAllString(authHdr, "SignedHeaders-host;x-amz-content-sha256;x-amz-date,")
req.Header.Set("Authorization", hdr)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
})
}
@@ -411,11 +428,7 @@ func Authentication_missing_date_header(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader))
})
}
@@ -435,11 +448,7 @@ func Authentication_invalid_date_header(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMalformedDate)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMissingDateHeader))
})
}
@@ -452,18 +461,22 @@ func Authentication_date_mismatch(s *S3Conf) error {
service: "s3",
date: time.Now(),
}, func(req *http.Request) error {
req.Header.Set("X-Amz-Date", "20220830T095525Z")
err := createUsers(s, []user{testuser1})
if err != nil {
return err
}
authHdr := req.Header.Get("Authorization")
regExp := regexp.MustCompile("Credential=[^,]+,")
hdr := regExp.ReplaceAllString(authHdr, "Credential=grt1/20250912/us-east-1/s3/aws4_request,")
req.Header.Set("Authorization", hdr)
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.MalformedAuth.DateMismatch())
})
}
@@ -483,11 +496,7 @@ func Authentication_invalid_sha256_payload_hash(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidSHA256Paylod)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidSHA256Paylod))
})
}
@@ -508,11 +517,7 @@ func Authentication_incorrect_payload_hash(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrContentSHA256Mismatch))
})
}
@@ -533,11 +538,7 @@ func Authentication_incorrect_md5(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidDigest)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidDigest))
})
}
@@ -558,11 +559,33 @@ func Authentication_signature_error_incorrect_secret_key(s *S3Conf) error {
return err
}
defer resp.Body.Close()
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch)); err != nil {
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDoesNotMatch))
})
}
func PresignedAuth_security_token_not_supported(s *S3Conf) error {
testName := "PresignedAuth_security_token_not_supported"
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
cancel()
if err != nil {
return err
}
return nil
uri := v4req.URL + "&X-Amz-Security-Token=my_token"
req, err := http.NewRequest(v4req.Method, uri, nil)
if err != nil {
return err
}
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.SecurityTokenNotSupported())
})
}
@@ -588,11 +611,66 @@ func PresignedAuth_unsupported_algorithm(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQuerySignatureAlgo)); err != nil {
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.UnsupportedAlgorithm())
})
}
func PresignedAuth_ECDSA_not_supported(s *S3Conf) error {
testName := "PresignedAuth_ECDSA_not_supported"
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
cancel()
if err != nil {
return err
}
return nil
uri := strings.Replace(v4req.URL, "AWS4-HMAC-SHA256", "AWS4-ECDSA-P256-SHA256", 1)
req, err := http.NewRequest(v4req.Method, uri, nil)
if err != nil {
return err
}
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.OnlyHMACSupported())
})
}
func PresignedAuth_missing_signature_query_param(s *S3Conf) error {
testName := "PresignedAuth_missing_signature_query_param"
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
cancel()
if err != nil {
return err
}
urlParsed, err := url.Parse(v4req.URL)
if err != nil {
return err
}
queries := urlParsed.Query()
queries.Del("X-Amz-Signature")
urlParsed.RawQuery = queries.Encode()
req, err := http.NewRequest(v4req.Method, urlParsed.String(), nil)
if err != nil {
return err
}
resp, err := s.httpClient.Do(req)
if err != nil {
return err
}
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
})
}
@@ -625,11 +703,7 @@ func PresignedAuth_missing_credentials_query_param(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
})
}
@@ -662,16 +736,12 @@ func PresignedAuth_malformed_creds_invalid_parts(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrCredMalformed)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MalformedCredential())
})
}
func PresignedAuth_creds_invalid_terminator(s *S3Conf) error {
testName := "PresignedAuth_creds_invalid_terminator"
func PresignedAuth_creds_invalid_terminal(s *S3Conf) error {
testName := "PresignedAuth_creds_invalid_terminal"
return presignedAuthHandler(s, testName, func(client *s3.PresignClient, bucket string) error {
ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
v4req, err := client.PresignDeleteBucket(ctx, &s3.DeleteBucketInput{Bucket: &bucket})
@@ -695,11 +765,7 @@ func PresignedAuth_creds_invalid_terminator(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureTerminationStr)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectTerminal("aws5_request"))
})
}
@@ -728,11 +794,7 @@ func PresignedAuth_creds_incorrect_service(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureIncorrService)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectService("sns"))
})
}
@@ -766,15 +828,7 @@ func PresignedAuth_creds_incorrect_region(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.APIError{
Code: "SignatureDoesNotMatch",
Description: fmt.Sprintf("Credential should be scoped to a valid Region, not %v", cfg.awsRegion),
HTTPStatusCode: http.StatusForbidden,
}); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.IncorrectRegion(s.awsRegion, cfg.awsRegion))
})
}
@@ -803,11 +857,7 @@ func PresignedAuth_creds_invalid_date(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.InvalidDateFormat("32234Z34"))
})
}
@@ -836,11 +886,7 @@ func PresignedAuth_non_existing_access_key_id(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidAccessKeyID))
})
}
@@ -873,11 +919,7 @@ func PresignedAuth_missing_date_query(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
})
}
@@ -891,6 +933,14 @@ func PresignedAuth_dates_mismatch(s *S3Conf) error {
return err
}
urlParsed, err := url.Parse(v4req.URL)
if err != nil {
return err
}
tDate := urlParsed.Query().Get("X-Amz-Date")
date := tDate[:8]
uri, err := changeAuthCred(v4req.URL, "20060102", credDate)
if err != nil {
return err
@@ -906,11 +956,7 @@ func PresignedAuth_dates_mismatch(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrSignatureDateDoesNotMatch)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.DateMismatch("20060102", date))
})
}
@@ -943,11 +989,7 @@ func PresignedAuth_missing_signed_headers_query_param(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
})
}
@@ -980,11 +1022,7 @@ func PresignedAuth_missing_expiration_query_param(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidQueryParams)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.MissingRequiredParams())
})
}
@@ -1017,11 +1055,7 @@ func PresignedAuth_invalid_expiration_query_param(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMalformedExpires)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.ExpiresNumber())
})
}
@@ -1054,11 +1088,7 @@ func PresignedAuth_negative_expiration_query_param(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrNegativeExpires)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.ExpiresNegative())
})
}
@@ -1091,11 +1121,7 @@ func PresignedAuth_exceeding_expiration_query_param(s *S3Conf) error {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrMaximumExpires)); err != nil {
return err
}
return nil
return checkHTTPResponseApiErr(resp, s3err.QueryAuthErrors.ExpiresTooLarge())
})
}