Merge pull request #1275 from harshavardhana/signature

error: Signature errors should be returned with APIErrorCode.
master
Anand Babu (AB) Periasamy 9 years ago
commit ae5c65d3c6
  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