Files
versity-versitygw/s3api/utils/auth-reader.go
T
dualfroz 8271bea5c1 fix: return full authorization header in unsupported authorization type error
the unsupported authorization type error only echoed the algorithm back in ArgumentValue, while s3 returns the whole Authorization header there. the parse error now carries the raw header and the s3 error mapping uses it, which also lets the bats test skipped for #1705 run again.

Fixes #2489
2026-10-07 19:37:46 +02:00

172 lines
5.3 KiB
Go

// 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 utils
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"errors"
"time"
"github.com/gofiber/fiber/v3"
"github.com/versity/versitygw/internal/sigv4auth"
"github.com/versity/versitygw/s3err"
)
const (
iso8601Format = sigv4auth.ISO8601Format
yyyymmdd = sigv4auth.YYYYMMDD
)
func HexBytes(s string) string {
return sigv4auth.HexBytes(s)
}
const (
service = sigv4auth.ServiceS3
)
// CheckValidSignature validates the ctx v4 auth signature against
// derivedKey — the request's kSigning value, either derived locally from a
// known secret or obtained from a standalone IAM service that never reveals
// the secret itself.
func CheckValidSignature(ctx fiber.Ctx, auth AuthData, derivedKey []byte, checksum string, tdate time.Time, contentLen int64) (string, error) {
result, err := sigv4auth.CheckSignature(ctx, auth, derivedKey, checksum, tdate, contentLen, sigv4auth.CheckOptions{
Service: service,
})
if err != nil {
return "", mapSigV4Error(err)
}
return result.CanonicalString, nil
}
// AuthData is the parsed authorization data from the header.
type AuthData = sigv4auth.AuthData
// ParseAuthorization returns the parsed fields for the aws v4 auth header
// example authorization string from aws docs:
// Authorization: AWS4-HMAC-SHA256
// Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,
// SignedHeaders=host;range;x-amz-date,
// Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
func ParseAuthorization(authorization string) (AuthData, error) {
authData, err := sigv4auth.ParseAuthorization(authorization, service)
if err != nil {
return AuthData{}, mapSigV4Error(err)
}
return authData, nil
}
type CredentialsScope = sigv4auth.CredentialsScope
type CredsError interface {
MalformedCredential(string) s3err.S3Error
IncorrectService(string, string) s3err.S3Error
IncorrectTerminal(string, string) s3err.S3Error
InvalidDateFormat(string, string) s3err.S3Error
}
func ParseCredentials(input string, errHandler CredsError) (*CredentialsScope, error) {
creds, err := sigv4auth.ParseCredentials(input, service)
if err != nil {
return nil, mapCredentialsError(input, err, errHandler)
}
return creds, nil
}
// SignPostPolicy signs a POST-policy document with derivedKey
func SignPostPolicy(base64Policy string, derivedKey []byte) (string, error) {
h := hmac.New(sha256.New, derivedKey)
h.Write([]byte(base64Policy))
return hex.EncodeToString(h.Sum(nil)), nil
}
func mapSigV4Error(err error) error {
var parseErr *sigv4auth.ParseError
if errors.As(err, &parseErr) {
return mapAuthParseError(parseErr)
}
var headersErr *sigv4auth.HeadersNotSignedError
if errors.As(err, &headersErr) {
return s3err.GetHeadersNotSignedErr(headersErr.Headers)
}
var sigErr *sigv4auth.SignatureMismatchError
if errors.As(err, &sigErr) {
return s3err.GetSignatureDoesNotMatchErr(
sigErr.AccessKeyID,
sigErr.StringToSign,
sigErr.SignatureProvided,
sigErr.StringToSignBytes,
sigErr.CanonicalRequest,
sigErr.CanonicalRequestBytes,
)
}
return err
}
func mapAuthParseError(err *sigv4auth.ParseError) error {
switch err.Kind {
case sigv4auth.ErrInvalidAuthorizationHeader:
return s3err.GetInvalidArgumentErr(s3err.InvalidArgAuthHeader, err.Input)
case sigv4auth.ErrUnsupportedAuthorizationVersion:
return s3err.GetAPIError(s3err.ErrUnsupportedAuthorizationMechanism)
case sigv4auth.ErrInvalidAuthorizationType:
return s3err.GetInvalidArgumentErr(s3err.InvalidArgAuthorizationType, err.Input)
case sigv4auth.ErrMissingComponents:
return s3err.MalformedAuth.MissingComponents()
case sigv4auth.ErrMissingCredential:
return s3err.MalformedAuth.MissingCredential()
case sigv4auth.ErrMissingSignedHeaders:
return s3err.MalformedAuth.MissingSignedHeaders()
case sigv4auth.ErrMissingSignature:
return s3err.MalformedAuth.MissingSignature()
case sigv4auth.ErrMalformedComponent:
return s3err.MalformedAuth.MalformedComponent(err.Value)
default:
return mapCredentialsParseError(err, s3err.MalformedAuth)
}
}
func mapCredentialsError(input string, err error, errHandler CredsError) error {
var parseErr *sigv4auth.ParseError
if !errors.As(err, &parseErr) {
return err
}
if parseErr.Input == "" {
parseErr.Input = input
}
return mapCredentialsParseError(parseErr, errHandler)
}
func mapCredentialsParseError(err *sigv4auth.ParseError, errHandler CredsError) error {
switch err.Kind {
case sigv4auth.ErrMalformedCredential:
return errHandler.MalformedCredential(err.Input)
case sigv4auth.ErrIncorrectService:
return errHandler.IncorrectService(err.Input, err.Actual)
case sigv4auth.ErrIncorrectTerminal:
return errHandler.IncorrectTerminal(err.Input, err.Actual)
case sigv4auth.ErrInvalidDateFormat:
return errHandler.InvalidDateFormat(err.Input, err.Value)
default:
return err
}
}