error: Signature errors should be returned with APIErrorCode.

The reasoning is that we can reply back with wide range of
S3 error responses, which would provide more richer context
to S3 client.

Fixes #1267
master
Harshavardhana 9 years ago
parent a84c466a40
commit 02ad48466d
  1. 99
      api-errors.go
  2. 20
      auth-handler.go
  3. 17
      bucket-handlers.go
  4. 17
      flags.go
  5. 57
      generic-handlers.go
  6. 52
      object-handlers.go
  7. 54
      signature-v4-errors.go
  8. 86
      signature-v4-parser.go
  9. 24
      signature-v4-postpolicyform.go
  10. 59
      signature-v4.go

@ -87,6 +87,23 @@ const (
ErrObjectExistsAsPrefix
ErrAllAccessDisabled
ErrMalformedPolicy
ErrMissingFields
ErrMissingCredTag
ErrCredMalformed
ErrInvalidRegion
ErrInvalidService
ErrInvalidRequestVersion
ErrMissingSignTag
ErrMissingSignHeadersTag
ErrPolicyAlreadyExpired
ErrMalformedDate
ErrMalformedExpires
ErrAuthHeaderEmpty
ErrDateHeaderMissing
ErrExpiredPresignRequest
ErrMissingDateHeader
ErrInvalidQuerySignatureAlgo
ErrInvalidQueryParams
// Add new error codes here.
)
@ -295,7 +312,87 @@ var errorCodeResponse = map[APIErrorCode]APIError{
},
ErrMalformedPolicy: {
Code: "MalformedPolicy",
Description: "Policy has invalid resource",
Description: "Policy has invalid resource.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingFields: {
Code: "MissingFields",
Description: "Missing fields in request.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingCredTag: {
Code: "InvalidRequest",
Description: "Missing Credential field for this request.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrCredMalformed: {
Code: "CredentialMalformed",
Description: "Credential field malformed does not follow accessKeyID/credScope.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMalformedDate: {
Code: "MalformedDate",
Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidRegion: {
Code: "InvalidRegion",
Description: "Region does not match.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidService: {
Code: "AccessDenied",
Description: "Service scope should be of value 's3'.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrInvalidRequestVersion: {
Code: "AccessDenied",
Description: "Request scope should be of value 'aws4_request'.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingSignTag: {
Code: "AccessDenied",
Description: "Signature header missing Signature field.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingSignHeadersTag: {
Code: "InvalidArgument",
Description: "Signature header missing SignedHeaders field.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrPolicyAlreadyExpired: {
Code: "AccessDenied",
Description: "Invalid according to Policy: Policy expired.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMalformedExpires: {
Code: "MalformedExpires",
Description: "Malformed expires header, expected non-zero number.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrAuthHeaderEmpty: {
Code: "InvalidArgument",
Description: "Authorization header is invalid -- one and only one ' ' (space) required.",
HTTPStatusCode: http.StatusBadRequest,
},
ErrMissingDateHeader: {
Code: "AccessDenied",
Description: "AWS authentication requires a valid Date or x-amz-date header",
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.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,
},
// Add your error structure here.

@ -130,25 +130,9 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) {
// Populate back the payload.
r.Body = ioutil.NopCloser(bytes.NewReader(payload))
if isRequestSignatureV4(r) {
ok, err := doesSignatureMatch(hex.EncodeToString(sum256(payload)), r)
if err != nil {
errorIf(err.Trace(), "Signature verification failed.", nil)
return ErrInternalError
}
if !ok {
return ErrSignatureDoesNotMatch
}
return ErrNone
return doesSignatureMatch(hex.EncodeToString(sum256(payload)), r)
} else if isRequestPresignedSignatureV4(r) {
ok, err := doesPresignedSignatureMatch(r)
if err != nil {
errorIf(err.Trace(), "Presigned signature verification failed.", nil)
return ErrInternalError
}
if !ok {
return ErrSignatureDoesNotMatch
}
return ErrNone
return doesPresignedSignatureMatch(r)
}
return ErrAccessDenied
}

@ -474,22 +474,15 @@ func (api storageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Req
bucket := mux.Vars(r)["bucket"]
formValues["Bucket"] = bucket
object := formValues["Key"]
var ok bool
// Verify policy signature.
ok, err = doesPolicySignatureMatch(formValues)
if err != nil {
errorIf(err.Trace(), "Unable to verify signature.", nil)
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
return
}
if !ok {
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
apiErr := doesPolicySignatureMatch(formValues)
if apiErr != ErrNone {
writeErrorResponse(w, r, apiErr, r.URL.Path)
return
}
if err = checkPostPolicy(formValues); err != nil {
errorIf(err.Trace(), "Invalid request, policy doesn't match.", nil)
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path)
if apiErr = checkPostPolicy(formValues); apiErr != ErrNone {
writeErrorResponse(w, r, apiErr, r.URL.Path)
return
}
objectInfo, err := api.Filesystem.CreateObject(bucket, object, -1, fileBody, nil)

@ -1,17 +0,0 @@
/*
* Minio Cloud Storage, (C) 2015, 2016 Minio, Inc.
*
* 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 main

@ -17,7 +17,6 @@
package main
import (
"errors"
"net/http"
"path"
"regexp"
@ -120,51 +119,43 @@ func (h minioPrivateBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
h.handler.ServeHTTP(w, r)
}
// Supported incoming date formats.
var timeFormats = []string{
// Supported Amz date formats.
var amzDateFormats = []string{
time.RFC1123,
time.RFC1123Z,
iso8601Format,
// Add new AMZ date formats here.
}
// Attempts to parse date string into known date layouts. Date layouts
// currently supported are
// - ``time.RFC1123``
// - ``time.RFC1123Z``
// - ``iso8601Format``
func parseDate(date string) (parsedTime time.Time, e error) {
for _, layout := range timeFormats {
parsedTime, e = time.Parse(layout, date)
// parseAmzDate - parses date string into supported amz date formats.
func parseAmzDate(amzDateStr string) (amzDate time.Time, apiErr APIErrorCode) {
for _, dateFormat := range amzDateFormats {
amzDate, e := time.Parse(dateFormat, amzDateStr)
if e == nil {
return parsedTime, nil
return amzDate, ErrNone
}
}
return time.Time{}, e
return time.Time{}, ErrMalformedDate
}
// Parse date string from incoming header, current supports and verifies
// follow HTTP headers.
//
// - X-Amz-Date
// - X-Minio-Date
// - Date
//
// In following time layouts ``time.RFC1123``, ``time.RFC1123Z`` and
// ``iso8601Format``.
var dateHeaders = []string{
// Supported Amz date headers.
var amzDateHeaders = []string{
"x-amz-date",
"x-minio-date",
"date",
}
func parseDateHeader(req *http.Request) (time.Time, error) {
for _, dateHeader := range dateHeaders {
date := req.Header.Get(http.CanonicalHeaderKey(dateHeader))
if date != "" {
return parseDate(date)
// parseAmzDateHeader - parses supported amz date headers, in
// supported amz date formats.
func parseAmzDateHeader(req *http.Request) (time.Time, APIErrorCode) {
for _, amzDateHeader := range amzDateHeaders {
amzDateStr := req.Header.Get(http.CanonicalHeaderKey(amzDateHeader))
if amzDateStr != "" {
return parseAmzDate(amzDateStr)
}
}
return time.Time{}, errors.New("Date header missing, invalid request.")
// Date header missing.
return time.Time{}, ErrMissingDateHeader
}
type timeHandler struct {
@ -179,17 +170,17 @@ func setTimeValidityHandler(h http.Handler) http.Handler {
func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Verify if date headers are set, if not reject the request
if _, ok := r.Header["Authorization"]; ok {
date, e := parseDateHeader(r)
if e != nil {
amzDate, apiErr := parseAmzDateHeader(r)
if apiErr != ErrNone {
// All our internal APIs are sensitive towards Date
// header, for all requests where Date header is not
// present we will reject such clients.
writeErrorResponse(w, r, ErrRequestTimeTooSkewed, r.URL.Path)
writeErrorResponse(w, r, apiErr, r.URL.Path)
return
}
// Verify if the request date header is more than 5minutes
// late, reject such clients.
if time.Now().UTC().Sub(date)/time.Minute > time.Duration(5)*time.Minute {
if time.Now().UTC().Sub(amzDate)/time.Minute > time.Duration(5)*time.Minute {
writeErrorResponse(w, r, ErrRequestTimeTooSkewed, r.URL.Path)
return
}

@ -19,6 +19,7 @@ package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"net/http"
@ -573,15 +574,8 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
objectInfo, err = api.Filesystem.CreateObject(bucket, object, size, r.Body, nil)
case authTypePresigned:
// For presigned requests verify them right here.
var ok bool
ok, err = doesPresignedSignatureMatch(r)
if err != nil {
errorIf(err.Trace(r.URL.String()), "Presigned signature verification failed.", nil)
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
return
}
if !ok {
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
if apiErr := doesPresignedSignatureMatch(r); apiErr != ErrNone {
writeErrorResponse(w, r, apiErr, r.URL.Path)
return
}
// Create presigned object.
@ -600,14 +594,12 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
return
}
shaPayload := shaWriter.Sum(nil)
ok, serr := doesSignatureMatch(hex.EncodeToString(shaPayload), r)
if serr != nil {
errorIf(serr.Trace(), "Signature verification failed.", nil)
writer.CloseWithError(probe.WrapError(serr))
return
}
if !ok {
writer.CloseWithError(errSignatureMismatch)
if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r); apiErr != ErrNone {
if apiErr == ErrSignatureDoesNotMatch {
writer.CloseWithError(errSignatureMismatch)
return
}
writer.CloseWithError(fmt.Errorf("%v", getAPIError(apiErr)))
return
}
writer.Close()
@ -756,15 +748,9 @@ func (api storageAPI) PutObjectPartHandler(w http.ResponseWriter, r *http.Reques
partMD5, err = api.Filesystem.CreateObjectPart(bucket, object, uploadID, partID, size, r.Body, nil)
case authTypePresigned:
// For presigned requests verify right here.
var ok bool
ok, err = doesPresignedSignatureMatch(r)
if err != nil {
errorIf(err.Trace(r.URL.String()), "Presigned signature verification failed.", nil)
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
return
}
if !ok {
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
apiErr := doesPresignedSignatureMatch(r)
if apiErr != ErrNone {
writeErrorResponse(w, r, apiErr, r.URL.Path)
return
}
partMD5, err = api.Filesystem.CreateObjectPart(bucket, object, uploadID, partID, size, r.Body, nil)
@ -782,14 +768,12 @@ func (api storageAPI) PutObjectPartHandler(w http.ResponseWriter, r *http.Reques
return
}
shaPayload := shaWriter.Sum(nil)
ok, serr := doesSignatureMatch(hex.EncodeToString(shaPayload), r)
if serr != nil {
errorIf(serr.Trace(), "Signature verification failed.", nil)
writer.CloseWithError(probe.WrapError(serr))
return
}
if !ok {
writer.CloseWithError(errSignatureMismatch)
if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r); apiErr != ErrNone {
if apiErr == ErrSignatureDoesNotMatch {
writer.CloseWithError(errSignatureMismatch)
return
}
writer.CloseWithError(fmt.Errorf("%v", getAPIError(apiErr)))
return
}
writer.Close()

@ -1,54 +0,0 @@
/*
* Minio Cloud Storage, (C) 2015, 2016 Minio, Inc.
*
* 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 main
import (
"fmt"
"github.com/minio/minio/pkg/probe"
)
type errFunc func(msg string, a ...string) *probe.Error
// generic error factory which wraps around probe.NewError()
func errFactory() errFunc {
return func(msg string, a ...string) *probe.Error {
return probe.NewError(fmt.Errorf("%s, Args: %s", msg, a)).Untrace()
}
}
// Various signature v4 errors.
var (
ErrPolicyAlreadyExpired = errFactory()
ErrInvalidRegion = errFactory()
ErrInvalidDateFormat = errFactory()
ErrInvalidService = errFactory()
ErrInvalidRequestVersion = errFactory()
ErrMissingFields = errFactory()
ErrMissingCredTag = errFactory()
ErrCredMalformed = errFactory()
ErrMissingSignTag = errFactory()
ErrMissingSignHeadersTag = errFactory()
ErrMissingDateHeader = errFactory()
ErrMalformedDate = errFactory()
ErrMalformedExpires = errFactory()
ErrAuthHeaderEmpty = errFactory()
ErrUnsuppSignAlgo = errFactory()
ErrExpiredPresignRequest = errFactory()
ErrRegionISEmpty = errFactory()
ErrInvalidAccessKey = errFactory()
)

@ -20,8 +20,6 @@ import (
"net/url"
"strings"
"time"
"github.com/minio/minio/pkg/probe"
)
// credentialHeader data type represents structured form of Credential
@ -37,20 +35,20 @@ type credentialHeader struct {
}
// parse credentialHeader string into its structured form.
func parseCredentialHeader(credElement string) (credentialHeader, *probe.Error) {
func parseCredentialHeader(credElement string) (credentialHeader, APIErrorCode) {
creds := strings.Split(strings.TrimSpace(credElement), "=")
if len(creds) != 2 {
return credentialHeader{}, ErrMissingFields("Credential tag has missing fields.", credElement).Trace(credElement)
return credentialHeader{}, ErrMissingFields
}
if creds[0] != "Credential" {
return credentialHeader{}, ErrMissingCredTag("Missing credentials tag.", credElement).Trace(credElement)
return credentialHeader{}, ErrMissingCredTag
}
credElements := strings.Split(strings.TrimSpace(creds[1]), "/")
if len(credElements) != 5 {
return credentialHeader{}, ErrCredMalformed("Credential values malformed.", credElement).Trace(credElement)
return credentialHeader{}, ErrCredMalformed
}
if !isValidAccessKey.MatchString(credElements[0]) {
return credentialHeader{}, ErrInvalidAccessKey("Invalid access key id.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidAccessKeyID
}
// Save access key id.
cred := credentialHeader{
@ -59,47 +57,47 @@ func parseCredentialHeader(credElement string) (credentialHeader, *probe.Error)
var e error
cred.scope.date, e = time.Parse(yyyymmdd, credElements[1])
if e != nil {
return credentialHeader{}, ErrInvalidDateFormat("Invalid date format.", credElement).Trace(credElement)
return credentialHeader{}, ErrMalformedDate
}
if credElements[2] == "" {
return credentialHeader{}, ErrRegionISEmpty("Region is empty.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidRegion
}
cred.scope.region = credElements[2]
if credElements[3] != "s3" {
return credentialHeader{}, ErrInvalidService("Invalid service detected.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidService
}
cred.scope.service = credElements[3]
if credElements[4] != "aws4_request" {
return credentialHeader{}, ErrInvalidRequestVersion("Invalid request version detected.", credElement).Trace(credElement)
return credentialHeader{}, ErrInvalidRequestVersion
}
cred.scope.request = credElements[4]
return cred, nil
return cred, ErrNone
}
// Parse signature string.
func parseSignature(signElement string) (string, *probe.Error) {
func parseSignature(signElement string) (string, APIErrorCode) {
signFields := strings.Split(strings.TrimSpace(signElement), "=")
if len(signFields) != 2 {
return "", ErrMissingFields("Signature tag has missing fields.", signElement).Trace(signElement)
return "", ErrMissingFields
}
if signFields[0] != "Signature" {
return "", ErrMissingSignTag("Signature tag is missing", signElement).Trace(signElement)
return "", ErrMissingSignTag
}
signature := signFields[1]
return signature, nil
return signature, ErrNone
}
// Parse signed headers string.
func parseSignedHeaders(signedHdrElement string) ([]string, *probe.Error) {
func parseSignedHeaders(signedHdrElement string) ([]string, APIErrorCode) {
signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=")
if len(signedHdrFields) != 2 {
return nil, ErrMissingFields("Signed headers tag has missing fields.", signedHdrElement).Trace(signedHdrElement)
return nil, ErrMissingFields
}
if signedHdrFields[0] != "SignedHeaders" {
return nil, ErrMissingSignHeadersTag("Signed headers tag is missing.", signedHdrElement).Trace(signedHdrElement)
return nil, ErrMissingSignHeadersTag
}
signedHeaders := strings.Split(signedHdrFields[1], ";")
return signedHeaders, nil
return signedHeaders, ErrNone
}
// signValues data type represents structured form of AWS Signature V4 header.
@ -125,49 +123,49 @@ type preSignValues struct {
// querystring += &X-Amz-SignedHeaders=signed_headers
// querystring += &X-Amz-Signature=signature
//
func parsePreSignV4(query url.Values) (preSignValues, *probe.Error) {
func parsePreSignV4(query url.Values) (preSignValues, APIErrorCode) {
// Verify if the query algorithm is supported or not.
if query.Get("X-Amz-Algorithm") != signV4Algorithm {
return preSignValues{}, ErrUnsuppSignAlgo("Unsupported algorithm in query string.", query.Get("X-Amz-Algorithm"))
return preSignValues{}, ErrInvalidQuerySignatureAlgo
}
// Initialize signature version '4' structured header.
preSignV4Values := preSignValues{}
var err *probe.Error
var err APIErrorCode
// Save credential.
preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential"))
if err != nil {
return preSignValues{}, err.Trace(query.Get("X-Amz-Credential"))
if err != ErrNone {
return preSignValues{}, err
}
var e error
// Save date in native time.Time.
preSignV4Values.Date, e = time.Parse(iso8601Format, query.Get("X-Amz-Date"))
if e != nil {
return preSignValues{}, ErrMalformedDate("Malformed date string.", query.Get("X-Amz-Date")).Trace(query.Get("X-Amz-Date"))
return preSignValues{}, ErrMalformedDate
}
// Save expires in native time.Duration.
preSignV4Values.Expires, e = time.ParseDuration(query.Get("X-Amz-Expires") + "s")
if e != nil {
return preSignValues{}, ErrMalformedExpires("Malformed expires string.", query.Get("X-Amz-Expires")).Trace(query.Get("X-Amz-Expires"))
return preSignValues{}, ErrMalformedExpires
}
// Save signed headers.
preSignV4Values.SignedHeaders, err = parseSignedHeaders("SignedHeaders=" + query.Get("X-Amz-SignedHeaders"))
if err != nil {
return preSignValues{}, err.Trace(query.Get("X-Amz-SignedHeaders"))
if err != ErrNone {
return preSignValues{}, err
}
// Save signature.
preSignV4Values.Signature, err = parseSignature("Signature=" + query.Get("X-Amz-Signature"))
if err != nil {
return preSignValues{}, err.Trace(query.Get("X-Amz-Signature"))
if err != ErrNone {
return preSignValues{}, err
}
// Return structed form of signature query string.
return preSignV4Values, nil
return preSignV4Values, ErrNone
}
// Parses signature version '4' header of the following form.
@ -175,49 +173,49 @@ func parsePreSignV4(query url.Values) (preSignValues, *probe.Error) {
// Authorization: algorithm Credential=accessKeyID/credScope, \
// SignedHeaders=signedHeaders, Signature=signature
//
func parseSignV4(v4Auth string) (signValues, *probe.Error) {
func parseSignV4(v4Auth string) (signValues, APIErrorCode) {
// Replace all spaced strings, some clients can send spaced
// parameters and some won't. So we pro-actively remove any spaces
// to make parsing easier.
v4Auth = strings.Replace(v4Auth, " ", "", -1)
if v4Auth == "" {
return signValues{}, ErrAuthHeaderEmpty("Auth header empty.").Trace(v4Auth)
return signValues{}, ErrAuthHeaderEmpty
}
// Verify if the header algorithm is supported or not.
if !strings.HasPrefix(v4Auth, signV4Algorithm) {
return signValues{}, ErrUnsuppSignAlgo("Unsupported algorithm in authorization header.", v4Auth).Trace(v4Auth)
return signValues{}, ErrSignatureVersionNotSupported
}
// Strip off the Algorithm prefix.
v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm)
authFields := strings.Split(strings.TrimSpace(v4Auth), ",")
if len(authFields) != 3 {
return signValues{}, ErrMissingFields("Missing fields in authorization header.", v4Auth).Trace(v4Auth)
return signValues{}, ErrMissingFields
}
// Initialize signature version '4' structured header.
signV4Values := signValues{}
var err *probe.Error
var err APIErrorCode
// Save credentail values.
signV4Values.Credential, err = parseCredentialHeader(authFields[0])
if err != nil {
return signValues{}, err.Trace(v4Auth)
if err != ErrNone {
return signValues{}, err
}
// Save signed headers.
signV4Values.SignedHeaders, err = parseSignedHeaders(authFields[1])
if err != nil {
return signValues{}, err.Trace(v4Auth)
if err != ErrNone {
return signValues{}, err
}
// Save signature.
signV4Values.Signature, err = parseSignature(authFields[2])
if err != nil {
return signValues{}, err.Trace(v4Auth)
if err != ErrNone {
return signValues{}, err
}
// Return the structure here.
return signV4Values, nil
return signV4Values, ErrNone
}

@ -159,51 +159,51 @@ func parsePostPolicyFormV4(policy string) (PostPolicyForm, *probe.Error) {
}
// checkPostPolicy - apply policy conditions and validate input values.
func checkPostPolicy(formValues map[string]string) *probe.Error {
func checkPostPolicy(formValues map[string]string) APIErrorCode {
if formValues["X-Amz-Algorithm"] != signV4Algorithm {
return ErrUnsuppSignAlgo("Unsupported signature algorithm in policy form data.", formValues["X-Amz-Algorithm"]).Trace(formValues["X-Amz-Algorithm"])
return ErrSignatureVersionNotSupported
}
/// Decoding policy
policyBytes, e := base64.StdEncoding.DecodeString(formValues["Policy"])
if e != nil {
return probe.NewError(e)
return ErrMalformedPOSTRequest
}
postPolicyForm, err := parsePostPolicyFormV4(string(policyBytes))
if err != nil {
return err.Trace()
return ErrMalformedPOSTRequest
}
if !postPolicyForm.Expiration.After(time.Now().UTC()) {
return ErrPolicyAlreadyExpired("Policy has already expired, please generate a new one.")
return ErrPolicyAlreadyExpired
}
if postPolicyForm.Conditions.Policies["$bucket"].Operator == "eq" {
if formValues["Bucket"] != postPolicyForm.Conditions.Policies["$bucket"].Value {
return ErrMissingFields("Policy bucket is missing.", formValues["Bucket"])
return ErrMissingFields
}
}
if postPolicyForm.Conditions.Policies["$x-amz-date"].Operator == "eq" {
if formValues["X-Amz-Date"] != postPolicyForm.Conditions.Policies["$x-amz-date"].Value {
return ErrMissingFields("Policy date is missing.", formValues["X-Amz-Date"])
return ErrMissingFields
}
}
if postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "starts-with" {
if !strings.HasPrefix(formValues["Content-Type"], postPolicyForm.Conditions.Policies["$Content-Type"].Value) {
return ErrMissingFields("Policy content-type is missing or invalid.", formValues["Content-Type"])
return ErrMissingFields
}
}
if postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "eq" {
if formValues["Content-Type"] != postPolicyForm.Conditions.Policies["$Content-Type"].Value {
return ErrMissingFields("Policy content-Type is missing or invalid.", formValues["Content-Type"])
return ErrMissingFields
}
}
if postPolicyForm.Conditions.Policies["$key"].Operator == "starts-with" {
if !strings.HasPrefix(formValues["Key"], postPolicyForm.Conditions.Policies["$key"].Value) {
return ErrMissingFields("Policy key is missing.", formValues["Key"])
return ErrMissingFields
}
}
if postPolicyForm.Conditions.Policies["$key"].Operator == "eq" {
if formValues["Key"] != postPolicyForm.Conditions.Policies["$key"].Value {
return ErrMissingFields("Policy key is missing.", formValues["Key"])
return ErrMissingFields
}
}
return nil
return ErrNone
}

@ -35,7 +35,6 @@ import (
"time"
"github.com/minio/minio/pkg/crypto/sha256"
"github.com/minio/minio/pkg/probe"
)
// AWS Signature Version '4' constants.
@ -177,7 +176,7 @@ func getSignature(signingKey []byte, stringToSign string) string {
// doesPolicySignatureMatch - Verify query headers with post policy
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
// returns true if matches, false otherwise. if error is not nil then it is always false
func doesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error) {
func doesPolicySignatureMatch(formValues map[string]string) APIErrorCode {
// Access credentials.
cred := serverConfig.GetCredential()
@ -186,24 +185,24 @@ func doesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error)
// Parse credential tag.
credHeader, err := parseCredentialHeader("Credential=" + formValues["X-Amz-Credential"])
if err != nil {
return false, err.Trace(formValues["X-Amz-Credential"])
if err != ErrNone {
return ErrMissingFields
}
// Verify if the access key id matches.
if credHeader.accessKey != cred.AccessKeyID {
return false, ErrInvalidAccessKey("Access key id does not match with our records.", credHeader.accessKey).Trace(credHeader.accessKey)
return ErrInvalidAccessKeyID
}
// Verify if the region is valid.
if !isValidRegion(credHeader.scope.region, region) {
return false, ErrInvalidRegion("Requested region is not recognized.", credHeader.scope.region).Trace(credHeader.scope.region)
return ErrInvalidRegion
}
// Parse date string.
t, e := time.Parse(iso8601Format, formValues["X-Amz-Date"])
if e != nil {
return false, probe.NewError(e)
return ErrMalformedDate
}
// Get signing key.
@ -214,15 +213,15 @@ func doesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error)
// Verify signature.
if newSignature != formValues["X-Amz-Signature"] {
return false, nil
return ErrSignatureDoesNotMatch
}
return true, nil
return ErrNone
}
// 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(r *http.Request) (bool, *probe.Error) {
func doesPresignedSignatureMatch(r *http.Request) APIErrorCode {
// Access credentials.
cred := serverConfig.GetCredential()
@ -234,19 +233,19 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
// Parse request query string.
preSignValues, err := parsePreSignV4(req.URL.Query())
if err != nil {
return false, err.Trace(req.URL.String())
if err != ErrNone {
return err
}
// Verify if the access key id matches.
if preSignValues.Credential.accessKey != cred.AccessKeyID {
return false, ErrInvalidAccessKey("Access key id does not match with our records.", preSignValues.Credential.accessKey).Trace(preSignValues.Credential.accessKey)
return ErrInvalidAccessKeyID
}
// Verify if region is valid.
sRegion := preSignValues.Credential.scope.region
if !isValidRegion(sRegion, region) {
return false, ErrInvalidRegion("Requested region is not recognized.", sRegion).Trace(sRegion)
return ErrInvalidRegion
}
// Extract all the signed headers along with its values.
@ -257,7 +256,7 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
query.Set("X-Amz-Algorithm", signV4Algorithm)
if time.Now().UTC().Sub(preSignValues.Date) > time.Duration(preSignValues.Expires) {
return false, ErrExpiredPresignRequest("Presigned request already expired, please initiate a new request.")
return ErrExpiredPresignRequest
}
// Save the date and expires.
@ -283,19 +282,19 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
// Verify if date query is same.
if req.URL.Query().Get("X-Amz-Date") != query.Get("X-Amz-Date") {
return false, nil
return ErrSignatureDoesNotMatch
}
// Verify if expires query is same.
if req.URL.Query().Get("X-Amz-Expires") != query.Get("X-Amz-Expires") {
return false, nil
return ErrSignatureDoesNotMatch
}
// Verify if signed headers query is same.
if req.URL.Query().Get("X-Amz-SignedHeaders") != query.Get("X-Amz-SignedHeaders") {
return false, nil
return ErrSignatureDoesNotMatch
}
// Verify if credential query is same.
if req.URL.Query().Get("X-Amz-Credential") != query.Get("X-Amz-Credential") {
return false, nil
return ErrSignatureDoesNotMatch
}
/// Verify finally if signature is same.
@ -314,15 +313,15 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
// Verify signature.
if req.URL.Query().Get("X-Amz-Signature") != newSignature {
return false, nil
return ErrSignatureDoesNotMatch
}
return true, nil
return ErrNone
}
// 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) (bool, *probe.Error) {
func doesSignatureMatch(hashedPayload string, r *http.Request) APIErrorCode {
// Access credentials.
cred := serverConfig.GetCredential()
@ -337,8 +336,8 @@ func doesSignatureMatch(hashedPayload string, r *http.Request) (bool, *probe.Err
// Parse signature version '4' header.
signV4Values, err := parseSignV4(v4Auth)
if err != nil {
return false, err.Trace(v4Auth)
if err != ErrNone {
return err
}
// Extract all the signed headers along with its values.
@ -346,26 +345,26 @@ func doesSignatureMatch(hashedPayload string, r *http.Request) (bool, *probe.Err
// Verify if the access key id matches.
if signV4Values.Credential.accessKey != cred.AccessKeyID {
return false, ErrInvalidAccessKey("Access key id does not match with our records.", signV4Values.Credential.accessKey).Trace(signV4Values.Credential.accessKey)
return ErrInvalidAccessKeyID
}
// Verify if region is valid.
sRegion := signV4Values.Credential.scope.region
if !isValidRegion(sRegion, region) {
return false, ErrInvalidRegion("Requested region is not recognized.", sRegion).Trace(sRegion)
return ErrInvalidRegion
}
// Extract date, if not present throw error.
var date string
if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" {
if date = r.Header.Get("Date"); date == "" {
return false, ErrMissingDateHeader("Date header is missing from the request.").Trace()
return ErrMissingDateHeader
}
}
// Parse date header.
t, e := time.Parse(iso8601Format, date)
if e != nil {
return false, probe.NewError(e)
return ErrMalformedDate
}
// Query string.
@ -385,7 +384,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request) (bool, *probe.Err
// Verify if signature match.
if newSignature != signV4Values.Signature {
return false, nil
return ErrSignatureDoesNotMatch
}
return true, nil
return ErrNone
}

Loading…
Cancel
Save