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
172 lines
5.3 KiB
Go
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
|
|
}
|
|
}
|