mirror of https://github.com/minio/minio.git
Merge pull request #1275 from harshavardhana/signature
error: Signature errors should be returned with APIErrorCode.
This commit is contained in:
commit
ae5c65d3c6
|
@ -87,6 +87,23 @@ const (
|
||||||
ErrObjectExistsAsPrefix
|
ErrObjectExistsAsPrefix
|
||||||
ErrAllAccessDisabled
|
ErrAllAccessDisabled
|
||||||
ErrMalformedPolicy
|
ErrMalformedPolicy
|
||||||
|
ErrMissingFields
|
||||||
|
ErrMissingCredTag
|
||||||
|
ErrCredMalformed
|
||||||
|
ErrInvalidRegion
|
||||||
|
ErrInvalidService
|
||||||
|
ErrInvalidRequestVersion
|
||||||
|
ErrMissingSignTag
|
||||||
|
ErrMissingSignHeadersTag
|
||||||
|
ErrPolicyAlreadyExpired
|
||||||
|
ErrMalformedDate
|
||||||
|
ErrMalformedExpires
|
||||||
|
ErrAuthHeaderEmpty
|
||||||
|
ErrDateHeaderMissing
|
||||||
|
ErrExpiredPresignRequest
|
||||||
|
ErrMissingDateHeader
|
||||||
|
ErrInvalidQuerySignatureAlgo
|
||||||
|
ErrInvalidQueryParams
|
||||||
// Add new error codes here.
|
// Add new error codes here.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -295,7 +312,87 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
||||||
},
|
},
|
||||||
ErrMalformedPolicy: {
|
ErrMalformedPolicy: {
|
||||||
Code: "MalformedPolicy",
|
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,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
// Add your error structure here.
|
// Add your error structure here.
|
||||||
|
|
|
@ -130,25 +130,9 @@ func isReqAuthenticated(r *http.Request) (s3Error APIErrorCode) {
|
||||||
// Populate back the payload.
|
// Populate back the payload.
|
||||||
r.Body = ioutil.NopCloser(bytes.NewReader(payload))
|
r.Body = ioutil.NopCloser(bytes.NewReader(payload))
|
||||||
if isRequestSignatureV4(r) {
|
if isRequestSignatureV4(r) {
|
||||||
ok, err := doesSignatureMatch(hex.EncodeToString(sum256(payload)), r)
|
return doesSignatureMatch(hex.EncodeToString(sum256(payload)), r)
|
||||||
if err != nil {
|
|
||||||
errorIf(err.Trace(), "Signature verification failed.", nil)
|
|
||||||
return ErrInternalError
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return ErrSignatureDoesNotMatch
|
|
||||||
}
|
|
||||||
return ErrNone
|
|
||||||
} else if isRequestPresignedSignatureV4(r) {
|
} else if isRequestPresignedSignatureV4(r) {
|
||||||
ok, err := doesPresignedSignatureMatch(r)
|
return doesPresignedSignatureMatch(r)
|
||||||
if err != nil {
|
|
||||||
errorIf(err.Trace(), "Presigned signature verification failed.", nil)
|
|
||||||
return ErrInternalError
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
return ErrSignatureDoesNotMatch
|
|
||||||
}
|
|
||||||
return ErrNone
|
|
||||||
}
|
}
|
||||||
return ErrAccessDenied
|
return ErrAccessDenied
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,22 +474,15 @@ func (api storageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Req
|
||||||
bucket := mux.Vars(r)["bucket"]
|
bucket := mux.Vars(r)["bucket"]
|
||||||
formValues["Bucket"] = bucket
|
formValues["Bucket"] = bucket
|
||||||
object := formValues["Key"]
|
object := formValues["Key"]
|
||||||
var ok bool
|
|
||||||
|
|
||||||
// Verify policy signature.
|
// Verify policy signature.
|
||||||
ok, err = doesPolicySignatureMatch(formValues)
|
apiErr := doesPolicySignatureMatch(formValues)
|
||||||
if err != nil {
|
if apiErr != ErrNone {
|
||||||
errorIf(err.Trace(), "Unable to verify signature.", nil)
|
writeErrorResponse(w, r, apiErr, r.URL.Path)
|
||||||
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !ok {
|
if apiErr = checkPostPolicy(formValues); apiErr != ErrNone {
|
||||||
writeErrorResponse(w, r, ErrSignatureDoesNotMatch, r.URL.Path)
|
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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
objectInfo, err := api.Filesystem.CreateObject(bucket, object, -1, fileBody, nil)
|
objectInfo, err := api.Filesystem.CreateObject(bucket, object, -1, fileBody, nil)
|
||||||
|
|
17
flags.go
17
flags.go
|
@ -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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -120,51 +119,43 @@ func (h minioPrivateBucketHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
|
||||||
h.handler.ServeHTTP(w, r)
|
h.handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supported incoming date formats.
|
// Supported Amz date formats.
|
||||||
var timeFormats = []string{
|
var amzDateFormats = []string{
|
||||||
time.RFC1123,
|
time.RFC1123,
|
||||||
time.RFC1123Z,
|
time.RFC1123Z,
|
||||||
iso8601Format,
|
iso8601Format,
|
||||||
|
// Add new AMZ date formats here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to parse date string into known date layouts. Date layouts
|
// parseAmzDate - parses date string into supported amz date formats.
|
||||||
// currently supported are
|
func parseAmzDate(amzDateStr string) (amzDate time.Time, apiErr APIErrorCode) {
|
||||||
// - ``time.RFC1123``
|
for _, dateFormat := range amzDateFormats {
|
||||||
// - ``time.RFC1123Z``
|
amzDate, e := time.Parse(dateFormat, amzDateStr)
|
||||||
// - ``iso8601Format``
|
|
||||||
func parseDate(date string) (parsedTime time.Time, e error) {
|
|
||||||
for _, layout := range timeFormats {
|
|
||||||
parsedTime, e = time.Parse(layout, date)
|
|
||||||
if e == nil {
|
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
|
// Supported Amz date headers.
|
||||||
// follow HTTP headers.
|
var amzDateHeaders = []string{
|
||||||
//
|
|
||||||
// - X-Amz-Date
|
|
||||||
// - X-Minio-Date
|
|
||||||
// - Date
|
|
||||||
//
|
|
||||||
// In following time layouts ``time.RFC1123``, ``time.RFC1123Z`` and
|
|
||||||
// ``iso8601Format``.
|
|
||||||
var dateHeaders = []string{
|
|
||||||
"x-amz-date",
|
"x-amz-date",
|
||||||
"x-minio-date",
|
"x-minio-date",
|
||||||
"date",
|
"date",
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseDateHeader(req *http.Request) (time.Time, error) {
|
// parseAmzDateHeader - parses supported amz date headers, in
|
||||||
for _, dateHeader := range dateHeaders {
|
// supported amz date formats.
|
||||||
date := req.Header.Get(http.CanonicalHeaderKey(dateHeader))
|
func parseAmzDateHeader(req *http.Request) (time.Time, APIErrorCode) {
|
||||||
if date != "" {
|
for _, amzDateHeader := range amzDateHeaders {
|
||||||
return parseDate(date)
|
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 {
|
type timeHandler struct {
|
||||||
|
@ -179,17 +170,17 @@ func setTimeValidityHandler(h http.Handler) http.Handler {
|
||||||
func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// Verify if date headers are set, if not reject the request
|
// Verify if date headers are set, if not reject the request
|
||||||
if _, ok := r.Header["Authorization"]; ok {
|
if _, ok := r.Header["Authorization"]; ok {
|
||||||
date, e := parseDateHeader(r)
|
amzDate, apiErr := parseAmzDateHeader(r)
|
||||||
if e != nil {
|
if apiErr != ErrNone {
|
||||||
// All our internal APIs are sensitive towards Date
|
// All our internal APIs are sensitive towards Date
|
||||||
// header, for all requests where Date header is not
|
// header, for all requests where Date header is not
|
||||||
// present we will reject such clients.
|
// present we will reject such clients.
|
||||||
writeErrorResponse(w, r, ErrRequestTimeTooSkewed, r.URL.Path)
|
writeErrorResponse(w, r, apiErr, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Verify if the request date header is more than 5minutes
|
// Verify if the request date header is more than 5minutes
|
||||||
// late, reject such clients.
|
// 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)
|
writeErrorResponse(w, r, ErrRequestTimeTooSkewed, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"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)
|
objectInfo, err = api.Filesystem.CreateObject(bucket, object, size, r.Body, nil)
|
||||||
case authTypePresigned:
|
case authTypePresigned:
|
||||||
// For presigned requests verify them right here.
|
// For presigned requests verify them right here.
|
||||||
var ok bool
|
if apiErr := doesPresignedSignatureMatch(r); apiErr != ErrNone {
|
||||||
ok, err = doesPresignedSignatureMatch(r)
|
writeErrorResponse(w, r, apiErr, r.URL.Path)
|
||||||
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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Create presigned object.
|
// Create presigned object.
|
||||||
|
@ -600,14 +594,12 @@ func (api storageAPI) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
shaPayload := shaWriter.Sum(nil)
|
shaPayload := shaWriter.Sum(nil)
|
||||||
ok, serr := doesSignatureMatch(hex.EncodeToString(shaPayload), r)
|
if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r); apiErr != ErrNone {
|
||||||
if serr != nil {
|
if apiErr == ErrSignatureDoesNotMatch {
|
||||||
errorIf(serr.Trace(), "Signature verification failed.", nil)
|
writer.CloseWithError(errSignatureMismatch)
|
||||||
writer.CloseWithError(probe.WrapError(serr))
|
return
|
||||||
return
|
}
|
||||||
}
|
writer.CloseWithError(fmt.Errorf("%v", getAPIError(apiErr)))
|
||||||
if !ok {
|
|
||||||
writer.CloseWithError(errSignatureMismatch)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writer.Close()
|
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)
|
partMD5, err = api.Filesystem.CreateObjectPart(bucket, object, uploadID, partID, size, r.Body, nil)
|
||||||
case authTypePresigned:
|
case authTypePresigned:
|
||||||
// For presigned requests verify right here.
|
// For presigned requests verify right here.
|
||||||
var ok bool
|
apiErr := doesPresignedSignatureMatch(r)
|
||||||
ok, err = doesPresignedSignatureMatch(r)
|
if apiErr != ErrNone {
|
||||||
if err != nil {
|
writeErrorResponse(w, r, apiErr, r.URL.Path)
|
||||||
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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
partMD5, err = api.Filesystem.CreateObjectPart(bucket, object, uploadID, partID, size, r.Body, nil)
|
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
|
return
|
||||||
}
|
}
|
||||||
shaPayload := shaWriter.Sum(nil)
|
shaPayload := shaWriter.Sum(nil)
|
||||||
ok, serr := doesSignatureMatch(hex.EncodeToString(shaPayload), r)
|
if apiErr := doesSignatureMatch(hex.EncodeToString(shaPayload), r); apiErr != ErrNone {
|
||||||
if serr != nil {
|
if apiErr == ErrSignatureDoesNotMatch {
|
||||||
errorIf(serr.Trace(), "Signature verification failed.", nil)
|
writer.CloseWithError(errSignatureMismatch)
|
||||||
writer.CloseWithError(probe.WrapError(serr))
|
return
|
||||||
return
|
}
|
||||||
}
|
writer.CloseWithError(fmt.Errorf("%v", getAPIError(apiErr)))
|
||||||
if !ok {
|
|
||||||
writer.CloseWithError(errSignatureMismatch)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writer.Close()
|
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"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/probe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// credentialHeader data type represents structured form of Credential
|
// credentialHeader data type represents structured form of Credential
|
||||||
|
@ -37,20 +35,20 @@ type credentialHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse credentialHeader string into its structured form.
|
// 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), "=")
|
creds := strings.Split(strings.TrimSpace(credElement), "=")
|
||||||
if len(creds) != 2 {
|
if len(creds) != 2 {
|
||||||
return credentialHeader{}, ErrMissingFields("Credential tag has missing fields.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrMissingFields
|
||||||
}
|
}
|
||||||
if creds[0] != "Credential" {
|
if creds[0] != "Credential" {
|
||||||
return credentialHeader{}, ErrMissingCredTag("Missing credentials tag.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrMissingCredTag
|
||||||
}
|
}
|
||||||
credElements := strings.Split(strings.TrimSpace(creds[1]), "/")
|
credElements := strings.Split(strings.TrimSpace(creds[1]), "/")
|
||||||
if len(credElements) != 5 {
|
if len(credElements) != 5 {
|
||||||
return credentialHeader{}, ErrCredMalformed("Credential values malformed.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrCredMalformed
|
||||||
}
|
}
|
||||||
if !isValidAccessKey.MatchString(credElements[0]) {
|
if !isValidAccessKey.MatchString(credElements[0]) {
|
||||||
return credentialHeader{}, ErrInvalidAccessKey("Invalid access key id.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrInvalidAccessKeyID
|
||||||
}
|
}
|
||||||
// Save access key id.
|
// Save access key id.
|
||||||
cred := credentialHeader{
|
cred := credentialHeader{
|
||||||
|
@ -59,47 +57,47 @@ func parseCredentialHeader(credElement string) (credentialHeader, *probe.Error)
|
||||||
var e error
|
var e error
|
||||||
cred.scope.date, e = time.Parse(yyyymmdd, credElements[1])
|
cred.scope.date, e = time.Parse(yyyymmdd, credElements[1])
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return credentialHeader{}, ErrInvalidDateFormat("Invalid date format.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrMalformedDate
|
||||||
}
|
}
|
||||||
if credElements[2] == "" {
|
if credElements[2] == "" {
|
||||||
return credentialHeader{}, ErrRegionISEmpty("Region is empty.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrInvalidRegion
|
||||||
}
|
}
|
||||||
cred.scope.region = credElements[2]
|
cred.scope.region = credElements[2]
|
||||||
if credElements[3] != "s3" {
|
if credElements[3] != "s3" {
|
||||||
return credentialHeader{}, ErrInvalidService("Invalid service detected.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrInvalidService
|
||||||
}
|
}
|
||||||
cred.scope.service = credElements[3]
|
cred.scope.service = credElements[3]
|
||||||
if credElements[4] != "aws4_request" {
|
if credElements[4] != "aws4_request" {
|
||||||
return credentialHeader{}, ErrInvalidRequestVersion("Invalid request version detected.", credElement).Trace(credElement)
|
return credentialHeader{}, ErrInvalidRequestVersion
|
||||||
}
|
}
|
||||||
cred.scope.request = credElements[4]
|
cred.scope.request = credElements[4]
|
||||||
return cred, nil
|
return cred, ErrNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse signature string.
|
// Parse signature string.
|
||||||
func parseSignature(signElement string) (string, *probe.Error) {
|
func parseSignature(signElement string) (string, APIErrorCode) {
|
||||||
signFields := strings.Split(strings.TrimSpace(signElement), "=")
|
signFields := strings.Split(strings.TrimSpace(signElement), "=")
|
||||||
if len(signFields) != 2 {
|
if len(signFields) != 2 {
|
||||||
return "", ErrMissingFields("Signature tag has missing fields.", signElement).Trace(signElement)
|
return "", ErrMissingFields
|
||||||
}
|
}
|
||||||
if signFields[0] != "Signature" {
|
if signFields[0] != "Signature" {
|
||||||
return "", ErrMissingSignTag("Signature tag is missing", signElement).Trace(signElement)
|
return "", ErrMissingSignTag
|
||||||
}
|
}
|
||||||
signature := signFields[1]
|
signature := signFields[1]
|
||||||
return signature, nil
|
return signature, ErrNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse signed headers string.
|
// Parse signed headers string.
|
||||||
func parseSignedHeaders(signedHdrElement string) ([]string, *probe.Error) {
|
func parseSignedHeaders(signedHdrElement string) ([]string, APIErrorCode) {
|
||||||
signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=")
|
signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=")
|
||||||
if len(signedHdrFields) != 2 {
|
if len(signedHdrFields) != 2 {
|
||||||
return nil, ErrMissingFields("Signed headers tag has missing fields.", signedHdrElement).Trace(signedHdrElement)
|
return nil, ErrMissingFields
|
||||||
}
|
}
|
||||||
if signedHdrFields[0] != "SignedHeaders" {
|
if signedHdrFields[0] != "SignedHeaders" {
|
||||||
return nil, ErrMissingSignHeadersTag("Signed headers tag is missing.", signedHdrElement).Trace(signedHdrElement)
|
return nil, ErrMissingSignHeadersTag
|
||||||
}
|
}
|
||||||
signedHeaders := strings.Split(signedHdrFields[1], ";")
|
signedHeaders := strings.Split(signedHdrFields[1], ";")
|
||||||
return signedHeaders, nil
|
return signedHeaders, ErrNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// signValues data type represents structured form of AWS Signature V4 header.
|
// 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-SignedHeaders=signed_headers
|
||||||
// querystring += &X-Amz-Signature=signature
|
// 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.
|
// Verify if the query algorithm is supported or not.
|
||||||
if query.Get("X-Amz-Algorithm") != signV4Algorithm {
|
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.
|
// Initialize signature version '4' structured header.
|
||||||
preSignV4Values := preSignValues{}
|
preSignV4Values := preSignValues{}
|
||||||
|
|
||||||
var err *probe.Error
|
var err APIErrorCode
|
||||||
// Save credential.
|
// Save credential.
|
||||||
preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential"))
|
preSignV4Values.Credential, err = parseCredentialHeader("Credential=" + query.Get("X-Amz-Credential"))
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return preSignValues{}, err.Trace(query.Get("X-Amz-Credential"))
|
return preSignValues{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var e error
|
var e error
|
||||||
// Save date in native time.Time.
|
// Save date in native time.Time.
|
||||||
preSignV4Values.Date, e = time.Parse(iso8601Format, query.Get("X-Amz-Date"))
|
preSignV4Values.Date, e = time.Parse(iso8601Format, query.Get("X-Amz-Date"))
|
||||||
if e != nil {
|
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.
|
// Save expires in native time.Duration.
|
||||||
preSignV4Values.Expires, e = time.ParseDuration(query.Get("X-Amz-Expires") + "s")
|
preSignV4Values.Expires, e = time.ParseDuration(query.Get("X-Amz-Expires") + "s")
|
||||||
if e != nil {
|
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.
|
// Save signed headers.
|
||||||
preSignV4Values.SignedHeaders, err = parseSignedHeaders("SignedHeaders=" + query.Get("X-Amz-SignedHeaders"))
|
preSignV4Values.SignedHeaders, err = parseSignedHeaders("SignedHeaders=" + query.Get("X-Amz-SignedHeaders"))
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return preSignValues{}, err.Trace(query.Get("X-Amz-SignedHeaders"))
|
return preSignValues{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save signature.
|
// Save signature.
|
||||||
preSignV4Values.Signature, err = parseSignature("Signature=" + query.Get("X-Amz-Signature"))
|
preSignV4Values.Signature, err = parseSignature("Signature=" + query.Get("X-Amz-Signature"))
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return preSignValues{}, err.Trace(query.Get("X-Amz-Signature"))
|
return preSignValues{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return structed form of signature query string.
|
// Return structed form of signature query string.
|
||||||
return preSignV4Values, nil
|
return preSignV4Values, ErrNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses signature version '4' header of the following form.
|
// 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, \
|
// Authorization: algorithm Credential=accessKeyID/credScope, \
|
||||||
// SignedHeaders=signedHeaders, Signature=signature
|
// 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
|
// Replace all spaced strings, some clients can send spaced
|
||||||
// parameters and some won't. So we pro-actively remove any spaces
|
// parameters and some won't. So we pro-actively remove any spaces
|
||||||
// to make parsing easier.
|
// to make parsing easier.
|
||||||
v4Auth = strings.Replace(v4Auth, " ", "", -1)
|
v4Auth = strings.Replace(v4Auth, " ", "", -1)
|
||||||
if v4Auth == "" {
|
if v4Auth == "" {
|
||||||
return signValues{}, ErrAuthHeaderEmpty("Auth header empty.").Trace(v4Auth)
|
return signValues{}, ErrAuthHeaderEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if the header algorithm is supported or not.
|
// Verify if the header algorithm is supported or not.
|
||||||
if !strings.HasPrefix(v4Auth, signV4Algorithm) {
|
if !strings.HasPrefix(v4Auth, signV4Algorithm) {
|
||||||
return signValues{}, ErrUnsuppSignAlgo("Unsupported algorithm in authorization header.", v4Auth).Trace(v4Auth)
|
return signValues{}, ErrSignatureVersionNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip off the Algorithm prefix.
|
// Strip off the Algorithm prefix.
|
||||||
v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm)
|
v4Auth = strings.TrimPrefix(v4Auth, signV4Algorithm)
|
||||||
authFields := strings.Split(strings.TrimSpace(v4Auth), ",")
|
authFields := strings.Split(strings.TrimSpace(v4Auth), ",")
|
||||||
if len(authFields) != 3 {
|
if len(authFields) != 3 {
|
||||||
return signValues{}, ErrMissingFields("Missing fields in authorization header.", v4Auth).Trace(v4Auth)
|
return signValues{}, ErrMissingFields
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize signature version '4' structured header.
|
// Initialize signature version '4' structured header.
|
||||||
signV4Values := signValues{}
|
signV4Values := signValues{}
|
||||||
|
|
||||||
var err *probe.Error
|
var err APIErrorCode
|
||||||
// Save credentail values.
|
// Save credentail values.
|
||||||
signV4Values.Credential, err = parseCredentialHeader(authFields[0])
|
signV4Values.Credential, err = parseCredentialHeader(authFields[0])
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return signValues{}, err.Trace(v4Auth)
|
return signValues{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save signed headers.
|
// Save signed headers.
|
||||||
signV4Values.SignedHeaders, err = parseSignedHeaders(authFields[1])
|
signV4Values.SignedHeaders, err = parseSignedHeaders(authFields[1])
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return signValues{}, err.Trace(v4Auth)
|
return signValues{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save signature.
|
// Save signature.
|
||||||
signV4Values.Signature, err = parseSignature(authFields[2])
|
signV4Values.Signature, err = parseSignature(authFields[2])
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return signValues{}, err.Trace(v4Auth)
|
return signValues{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the structure here.
|
// 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.
|
// 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 {
|
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
|
/// Decoding policy
|
||||||
policyBytes, e := base64.StdEncoding.DecodeString(formValues["Policy"])
|
policyBytes, e := base64.StdEncoding.DecodeString(formValues["Policy"])
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return probe.NewError(e)
|
return ErrMalformedPOSTRequest
|
||||||
}
|
}
|
||||||
postPolicyForm, err := parsePostPolicyFormV4(string(policyBytes))
|
postPolicyForm, err := parsePostPolicyFormV4(string(policyBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Trace()
|
return ErrMalformedPOSTRequest
|
||||||
}
|
}
|
||||||
if !postPolicyForm.Expiration.After(time.Now().UTC()) {
|
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 postPolicyForm.Conditions.Policies["$bucket"].Operator == "eq" {
|
||||||
if formValues["Bucket"] != postPolicyForm.Conditions.Policies["$bucket"].Value {
|
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 postPolicyForm.Conditions.Policies["$x-amz-date"].Operator == "eq" {
|
||||||
if formValues["X-Amz-Date"] != postPolicyForm.Conditions.Policies["$x-amz-date"].Value {
|
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 postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "starts-with" {
|
||||||
if !strings.HasPrefix(formValues["Content-Type"], postPolicyForm.Conditions.Policies["$Content-Type"].Value) {
|
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 postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "eq" {
|
||||||
if formValues["Content-Type"] != postPolicyForm.Conditions.Policies["$Content-Type"].Value {
|
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 postPolicyForm.Conditions.Policies["$key"].Operator == "starts-with" {
|
||||||
if !strings.HasPrefix(formValues["Key"], postPolicyForm.Conditions.Policies["$key"].Value) {
|
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 postPolicyForm.Conditions.Policies["$key"].Operator == "eq" {
|
||||||
if formValues["Key"] != postPolicyForm.Conditions.Policies["$key"].Value {
|
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"
|
"time"
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/crypto/sha256"
|
"github.com/minio/minio/pkg/crypto/sha256"
|
||||||
"github.com/minio/minio/pkg/probe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AWS Signature Version '4' constants.
|
// AWS Signature Version '4' constants.
|
||||||
|
@ -177,7 +176,7 @@ func getSignature(signingKey []byte, stringToSign string) string {
|
||||||
// doesPolicySignatureMatch - Verify query headers with post policy
|
// doesPolicySignatureMatch - Verify query headers with post policy
|
||||||
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
|
// - 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
|
// 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.
|
// Access credentials.
|
||||||
cred := serverConfig.GetCredential()
|
cred := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
@ -186,24 +185,24 @@ func doesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error)
|
||||||
|
|
||||||
// Parse credential tag.
|
// Parse credential tag.
|
||||||
credHeader, err := parseCredentialHeader("Credential=" + formValues["X-Amz-Credential"])
|
credHeader, err := parseCredentialHeader("Credential=" + formValues["X-Amz-Credential"])
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return false, err.Trace(formValues["X-Amz-Credential"])
|
return ErrMissingFields
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if the access key id matches.
|
// Verify if the access key id matches.
|
||||||
if credHeader.accessKey != cred.AccessKeyID {
|
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.
|
// Verify if the region is valid.
|
||||||
if !isValidRegion(credHeader.scope.region, region) {
|
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.
|
// Parse date string.
|
||||||
t, e := time.Parse(iso8601Format, formValues["X-Amz-Date"])
|
t, e := time.Parse(iso8601Format, formValues["X-Amz-Date"])
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return false, probe.NewError(e)
|
return ErrMalformedDate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get signing key.
|
// Get signing key.
|
||||||
|
@ -214,15 +213,15 @@ func doesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error)
|
||||||
|
|
||||||
// Verify signature.
|
// Verify signature.
|
||||||
if newSignature != formValues["X-Amz-Signature"] {
|
if newSignature != formValues["X-Amz-Signature"] {
|
||||||
return false, nil
|
return ErrSignatureDoesNotMatch
|
||||||
}
|
}
|
||||||
return true, nil
|
return ErrNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// doesPresignedSignatureMatch - Verify query headers with presigned signature
|
// doesPresignedSignatureMatch - Verify query headers with presigned signature
|
||||||
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
|
// - 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
|
// 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.
|
// Access credentials.
|
||||||
cred := serverConfig.GetCredential()
|
cred := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
@ -234,19 +233,19 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
|
||||||
|
|
||||||
// Parse request query string.
|
// Parse request query string.
|
||||||
preSignValues, err := parsePreSignV4(req.URL.Query())
|
preSignValues, err := parsePreSignV4(req.URL.Query())
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return false, err.Trace(req.URL.String())
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify if the access key id matches.
|
// Verify if the access key id matches.
|
||||||
if preSignValues.Credential.accessKey != cred.AccessKeyID {
|
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.
|
// Verify if region is valid.
|
||||||
sRegion := preSignValues.Credential.scope.region
|
sRegion := preSignValues.Credential.scope.region
|
||||||
if !isValidRegion(sRegion, 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.
|
// 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)
|
query.Set("X-Amz-Algorithm", signV4Algorithm)
|
||||||
|
|
||||||
if time.Now().UTC().Sub(preSignValues.Date) > time.Duration(preSignValues.Expires) {
|
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.
|
// Save the date and expires.
|
||||||
|
@ -283,19 +282,19 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
|
||||||
|
|
||||||
// Verify if date query is same.
|
// Verify if date query is same.
|
||||||
if req.URL.Query().Get("X-Amz-Date") != query.Get("X-Amz-Date") {
|
if req.URL.Query().Get("X-Amz-Date") != query.Get("X-Amz-Date") {
|
||||||
return false, nil
|
return ErrSignatureDoesNotMatch
|
||||||
}
|
}
|
||||||
// Verify if expires query is same.
|
// Verify if expires query is same.
|
||||||
if req.URL.Query().Get("X-Amz-Expires") != query.Get("X-Amz-Expires") {
|
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.
|
// Verify if signed headers query is same.
|
||||||
if req.URL.Query().Get("X-Amz-SignedHeaders") != query.Get("X-Amz-SignedHeaders") {
|
if req.URL.Query().Get("X-Amz-SignedHeaders") != query.Get("X-Amz-SignedHeaders") {
|
||||||
return false, nil
|
return ErrSignatureDoesNotMatch
|
||||||
}
|
}
|
||||||
// Verify if credential query is same.
|
// Verify if credential query is same.
|
||||||
if req.URL.Query().Get("X-Amz-Credential") != query.Get("X-Amz-Credential") {
|
if req.URL.Query().Get("X-Amz-Credential") != query.Get("X-Amz-Credential") {
|
||||||
return false, nil
|
return ErrSignatureDoesNotMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify finally if signature is same.
|
/// Verify finally if signature is same.
|
||||||
|
@ -314,15 +313,15 @@ func doesPresignedSignatureMatch(r *http.Request) (bool, *probe.Error) {
|
||||||
|
|
||||||
// Verify signature.
|
// Verify signature.
|
||||||
if req.URL.Query().Get("X-Amz-Signature") != newSignature {
|
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
|
// doesSignatureMatch - Verify authorization header with calculated header in accordance with
|
||||||
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html
|
// - 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
|
// 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.
|
// Access credentials.
|
||||||
cred := serverConfig.GetCredential()
|
cred := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
@ -337,8 +336,8 @@ func doesSignatureMatch(hashedPayload string, r *http.Request) (bool, *probe.Err
|
||||||
|
|
||||||
// Parse signature version '4' header.
|
// Parse signature version '4' header.
|
||||||
signV4Values, err := parseSignV4(v4Auth)
|
signV4Values, err := parseSignV4(v4Auth)
|
||||||
if err != nil {
|
if err != ErrNone {
|
||||||
return false, err.Trace(v4Auth)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract all the signed headers along with its values.
|
// 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.
|
// Verify if the access key id matches.
|
||||||
if signV4Values.Credential.accessKey != cred.AccessKeyID {
|
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.
|
// Verify if region is valid.
|
||||||
sRegion := signV4Values.Credential.scope.region
|
sRegion := signV4Values.Credential.scope.region
|
||||||
if !isValidRegion(sRegion, 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.
|
// Extract date, if not present throw error.
|
||||||
var date string
|
var date string
|
||||||
if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" {
|
if date = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" {
|
||||||
if date = r.Header.Get("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.
|
// Parse date header.
|
||||||
t, e := time.Parse(iso8601Format, date)
|
t, e := time.Parse(iso8601Format, date)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return false, probe.NewError(e)
|
return ErrMalformedDate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query string.
|
// Query string.
|
||||||
|
@ -385,7 +384,7 @@ func doesSignatureMatch(hashedPayload string, r *http.Request) (bool, *probe.Err
|
||||||
|
|
||||||
// Verify if signature match.
|
// Verify if signature match.
|
||||||
if newSignature != signV4Values.Signature {
|
if newSignature != signV4Values.Signature {
|
||||||
return false, nil
|
return ErrSignatureDoesNotMatch
|
||||||
}
|
}
|
||||||
return true, nil
|
return ErrNone
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue