diff --git a/api-errors.go b/api-errors.go index 4b28ac8bb..0ef40712d 100644 --- a/api-errors.go +++ b/api-errors.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. diff --git a/auth-handler.go b/auth-handler.go index 5d6eb5974..286f67ec1 100644 --- a/auth-handler.go +++ b/auth-handler.go @@ -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 } diff --git a/bucket-handlers.go b/bucket-handlers.go index 16494417f..61117e7a7 100644 --- a/bucket-handlers.go +++ b/bucket-handlers.go @@ -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) + apiErr := doesPolicySignatureMatch(formValues) + if apiErr != ErrNone { + writeErrorResponse(w, r, apiErr, r.URL.Path) return } - if !ok { - writeErrorResponse(w, r, ErrSignatureDoesNotMatch, 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) diff --git a/flags.go b/flags.go deleted file mode 100644 index d2b3439d7..000000000 --- a/flags.go +++ /dev/null @@ -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 diff --git a/generic-handlers.go b/generic-handlers.go index e25d78c18..cc2a03bd3 100644 --- a/generic-handlers.go +++ b/generic-handlers.go @@ -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 } diff --git a/object-handlers.go b/object-handlers.go index ec5542e4f..cc2199c86 100644 --- a/object-handlers.go +++ b/object-handlers.go @@ -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() diff --git a/signature-v4-errors.go b/signature-v4-errors.go deleted file mode 100644 index dce0bd391..000000000 --- a/signature-v4-errors.go +++ /dev/null @@ -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() -) diff --git a/signature-v4-parser.go b/signature-v4-parser.go index e4c7eab64..afcb4ce0d 100644 --- a/signature-v4-parser.go +++ b/signature-v4-parser.go @@ -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 } diff --git a/signature-v4-postpolicyform.go b/signature-v4-postpolicyform.go index 9cd5c4dad..20ea45dc7 100644 --- a/signature-v4-postpolicyform.go +++ b/signature-v4-postpolicyform.go @@ -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 } diff --git a/signature-v4.go b/signature-v4.go index 7ca6544f4..9bdd65d19 100644 --- a/signature-v4.go +++ b/signature-v4.go @@ -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 }