From 653ceee9ee623dd62cd958cf4eb9318452d00424 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Sun, 21 Feb 2016 17:57:05 -0800 Subject: [PATCH] signV4: Move pkg/signature to pkg/s3/signature4 Cleanup and move this to relevant path. --- auth-handler.go | 77 ++++++++++++++- bucket-handlers.go | 6 +- pkg/fs/fs-multipart.go | 6 +- pkg/fs/fs-object.go | 6 +- .../errors.go => s3/signature4/v4-errors.go} | 7 +- pkg/{signature => s3/signature4}/v4-parser.go | 59 ++++++++---- .../signature4/v4-postpolicyform.go} | 2 +- .../signature4/v4-signature.go} | 73 +++++++------- .../utils.go => s3/signature4/v4-utils.go} | 24 +++-- pkg/xl/bucket.go | 4 +- pkg/xl/interfaces.go | 10 +- pkg/xl/multipart.go | 10 +- pkg/xl/xl-v1.go | 8 +- pkg/xl/xl-v2.go | 8 +- routers.go | 6 +- signature.go | 94 ------------------- 16 files changed, 210 insertions(+), 190 deletions(-) rename pkg/{signature/errors.go => s3/signature4/v4-errors.go} (91%) rename pkg/{signature => s3/signature4}/v4-parser.go (72%) rename pkg/{signature/postpolicyform.go => s3/signature4/v4-postpolicyform.go} (99%) rename pkg/{signature/signature-v4.go => s3/signature4/v4-signature.go} (81%) rename pkg/{signature/utils.go => s3/signature4/v4-utils.go} (84%) delete mode 100644 signature.go diff --git a/auth-handler.go b/auth-handler.go index f47131822..dc7fc73cf 100644 --- a/auth-handler.go +++ b/auth-handler.go @@ -16,13 +16,88 @@ package main -import "net/http" +import ( + "crypto/sha256" + "encoding/hex" + "net/http" + "strings" + + "github.com/minio/minio/pkg/s3/signature4" +) const ( signV4Algorithm = "AWS4-HMAC-SHA256" jwtAlgorithm = "Bearer" ) +// Verify if request has JWT. +func isRequestJWT(r *http.Request) bool { + if _, ok := r.Header["Authorization"]; ok { + if strings.HasPrefix(r.Header.Get("Authorization"), jwtAlgorithm) { + return true + } + } + return false +} + +// Verify if request has AWS Signature Version '4'. +func isRequestSignatureV4(r *http.Request) bool { + if _, ok := r.Header["Authorization"]; ok { + if strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) { + return true + } + } + return false +} + +// Verify if request has AWS Presignature Version '4'. +func isRequestPresignedSignatureV4(r *http.Request) bool { + if _, ok := r.URL.Query()["X-Amz-Credential"]; ok { + return true + } + return false +} + +// Verify if request has AWS Post policy Signature Version '4'. +func isRequestPostPolicySignatureV4(r *http.Request) bool { + if _, ok := r.Header["Content-Type"]; ok { + if strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { + return true + } + } + return false +} + +// Verify if request requires ACL check. +func isRequestRequiresACLCheck(r *http.Request) bool { + if isRequestSignatureV4(r) || isRequestPresignedSignatureV4(r) || isRequestPostPolicySignatureV4(r) { + return false + } + return true +} + +// Verify if request has valid AWS Signature Version '4'. +func isSignV4ReqAuthenticated(sign *signature4.Sign, r *http.Request) bool { + auth := sign.SetHTTPRequestToVerify(r) + if isRequestSignatureV4(r) { + dummyPayload := sha256.Sum256([]byte("")) + ok, err := auth.DoesSignatureMatch(hex.EncodeToString(dummyPayload[:])) + if err != nil { + errorIf(err.Trace(), "Signature verification failed.", nil) + return false + } + return ok + } else if isRequestPresignedSignatureV4(r) { + ok, err := auth.DoesPresignedSignatureMatch() + if err != nil { + errorIf(err.Trace(), "Presigned signature verification failed.", nil) + return false + } + return ok + } + return false +} + // authHandler - handles all the incoming authorization headers and // validates them if possible. type authHandler struct { diff --git a/bucket-handlers.go b/bucket-handlers.go index 2103d1799..89587fcaf 100644 --- a/bucket-handlers.go +++ b/bucket-handlers.go @@ -24,11 +24,11 @@ import ( "mime/multipart" "net/http" - "github.com/gorilla/mux" + mux "github.com/gorilla/mux" "github.com/minio/minio/pkg/crypto/sha256" "github.com/minio/minio/pkg/fs" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" ) // GetBucketLocationHandler - GET Bucket location. @@ -368,7 +368,7 @@ func (api storageAPI) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Req writeErrorResponse(w, r, SignatureDoesNotMatch, r.URL.Path) return } - if err = signV4.ApplyPolicyCond(formValues); err != nil { + if err = signature4.ApplyPolicyCond(formValues); err != nil { errorIf(err.Trace(), "Invalid request, policy doesn't match with the endpoint.", nil) writeErrorResponse(w, r, MalformedPOSTRequest, r.URL.Path) return diff --git a/pkg/fs/fs-multipart.go b/pkg/fs/fs-multipart.go index a7f51631b..7d9199d11 100644 --- a/pkg/fs/fs-multipart.go +++ b/pkg/fs/fs-multipart.go @@ -39,7 +39,7 @@ import ( "github.com/minio/minio/pkg/disk" "github.com/minio/minio/pkg/mimedb" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" ) // isValidUploadID - is upload id. @@ -297,7 +297,7 @@ func (a partNumber) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a partNumber) Less(i, j int) bool { return a[i].PartNumber < a[j].PartNumber } // CreateObjectPart - create a part in a multipart session -func (fs Filesystem) CreateObjectPart(bucket, object, uploadID, expectedMD5Sum string, partID int, size int64, data io.Reader, signature *signV4.Signature) (string, *probe.Error) { +func (fs Filesystem) CreateObjectPart(bucket, object, uploadID, expectedMD5Sum string, partID int, size int64, data io.Reader, signature *signature4.Sign) (string, *probe.Error) { di, err := disk.GetInfo(fs.path) if err != nil { return "", probe.NewError(err) @@ -431,7 +431,7 @@ func (fs Filesystem) CreateObjectPart(bucket, object, uploadID, expectedMD5Sum s } // CompleteMultipartUpload - complete a multipart upload and persist the data -func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, data io.Reader, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (fs Filesystem) CompleteMultipartUpload(bucket, object, uploadID string, data io.Reader, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { // Check bucket name is valid. if !IsValidBucketName(bucket) { return ObjectMetadata{}, probe.NewError(BucketNameInvalid{Bucket: bucket}) diff --git a/pkg/fs/fs-object.go b/pkg/fs/fs-object.go index bb9cc123b..27368b55f 100644 --- a/pkg/fs/fs-object.go +++ b/pkg/fs/fs-object.go @@ -34,7 +34,7 @@ import ( "github.com/minio/minio/pkg/ioutils" "github.com/minio/minio/pkg/mimedb" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" ) /// Object Operations @@ -199,7 +199,7 @@ func isMD5SumEqual(expectedMD5Sum, actualMD5Sum string) bool { } // CreateObject - create an object. -func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size int64, data io.Reader, sig *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size int64, data io.Reader, sig *signature4.Sign) (ObjectMetadata, *probe.Error) { di, e := disk.GetInfo(fs.path) if e != nil { return ObjectMetadata{}, probe.NewError(e) @@ -294,7 +294,7 @@ func (fs Filesystem) CreateObject(bucket, object, expectedMD5Sum string, size in } if !ok { file.CloseAndPurge() - return ObjectMetadata{}, signV4.ErrSignDoesNotMath("Signature does not match") + return ObjectMetadata{}, probe.NewError(SignDoesNotMatch{}) } } file.Close() diff --git a/pkg/signature/errors.go b/pkg/s3/signature4/v4-errors.go similarity index 91% rename from pkg/signature/errors.go rename to pkg/s3/signature4/v4-errors.go index 58aaea2e9..cbc8792df 100644 --- a/pkg/signature/errors.go +++ b/pkg/s3/signature4/v4-errors.go @@ -1,5 +1,5 @@ /* - * Minio Cloud Storage, (C) 2015 Minio, Inc. + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package signature +package signature4 import ( "fmt" @@ -24,13 +24,14 @@ import ( 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 errors. +// Various signature v4 errors. var ( ErrPolicyAlreadyExpired = errFactory() ErrInvalidRegion = errFactory() diff --git a/pkg/signature/v4-parser.go b/pkg/s3/signature4/v4-parser.go similarity index 72% rename from pkg/signature/v4-parser.go rename to pkg/s3/signature4/v4-parser.go index 64c3deb1a..0674b5807 100644 --- a/pkg/signature/v4-parser.go +++ b/pkg/s3/signature4/v4-parser.go @@ -1,4 +1,20 @@ -package signature +/* + * Minio Cloud Storage, (C) 2015 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 signature4 import ( "net/url" @@ -8,7 +24,9 @@ import ( "github.com/minio/minio/pkg/probe" ) -type credScope struct { +// credential data type represents structured form of Credential +// string from authorization header. +type credential struct { accessKeyID string scope struct { date time.Time @@ -18,45 +36,46 @@ type credScope struct { } } -func parseCredential(credElement string) (credScope, *probe.Error) { +// parse credential string into its structured form. +func parseCredential(credElement string) (credential, *probe.Error) { creds := strings.Split(strings.TrimSpace(credElement), "=") if len(creds) != 2 { - return credScope{}, ErrMissingFields("Credential tag has missing fields.", credElement).Trace(credElement) + return credential{}, ErrMissingFields("Credential tag has missing fields.", credElement).Trace(credElement) } if creds[0] != "Credential" { - return credScope{}, ErrMissingCredTag("Missing credentials tag.", credElement).Trace(credElement) + return credential{}, ErrMissingCredTag("Missing credentials tag.", credElement).Trace(credElement) } credElements := strings.Split(strings.TrimSpace(creds[1]), "/") if len(credElements) != 5 { - return credScope{}, ErrCredMalformed("Credential values malformed.", credElement).Trace(credElement) + return credential{}, ErrCredMalformed("Credential values malformed.", credElement).Trace(credElement) } if !isValidAccessKey.MatchString(credElements[0]) { - return credScope{}, ErrInvalidAccessKeyID("Invalid access key id.", credElement).Trace(credElement) + return credential{}, ErrInvalidAccessKeyID("Invalid access key id.", credElement).Trace(credElement) } - cred := credScope{ + cred := credential{ accessKeyID: credElements[0], } var e error cred.scope.date, e = time.Parse(yyyymmdd, credElements[1]) if e != nil { - return credScope{}, ErrInvalidDateFormat("Invalid date format.", credElement).Trace(credElement) + return credential{}, ErrInvalidDateFormat("Invalid date format.", credElement).Trace(credElement) } if credElements[2] == "" { - return credScope{}, ErrRegionISEmpty("Region is empty.", credElement).Trace(credElement) + return credential{}, ErrRegionISEmpty("Region is empty.", credElement).Trace(credElement) } cred.scope.region = credElements[2] if credElements[3] != "s3" { - return credScope{}, ErrInvalidService("Invalid service detected.", credElement).Trace(credElement) + return credential{}, ErrInvalidService("Invalid service detected.", credElement).Trace(credElement) } cred.scope.service = credElements[3] if credElements[4] != "aws4_request" { - return credScope{}, ErrInvalidRequestVersion("Invalid request version detected.", credElement).Trace(credElement) + return credential{}, ErrInvalidRequestVersion("Invalid request version detected.", credElement).Trace(credElement) } cred.scope.request = credElements[4] return cred, nil } -// parse signature. +// Parse signature string. func parseSignature(signElement string) (string, *probe.Error) { signFields := strings.Split(strings.TrimSpace(signElement), "=") if len(signFields) != 2 { @@ -69,7 +88,7 @@ func parseSignature(signElement string) (string, *probe.Error) { return signature, nil } -// parse signed headers. +// Parse signed headers string. func parseSignedHeaders(signedHdrElement string) ([]string, *probe.Error) { signedHdrFields := strings.Split(strings.TrimSpace(signedHdrElement), "=") if len(signedHdrFields) != 2 { @@ -82,14 +101,14 @@ func parseSignedHeaders(signedHdrElement string) ([]string, *probe.Error) { return signedHeaders, nil } -// structured version of AWS Signature V4 header. +// signValues data type represents structured form of AWS Signature V4 header. type signValues struct { - Creds credScope + Credential credential SignedHeaders []string Signature string } -// structued version of AWS Signature V4 query string. +// preSignValues data type represents structued form of AWS Signature V4 query string. type preSignValues struct { signValues Date time.Time @@ -115,8 +134,8 @@ func parsePreSignV4(query url.Values) (preSignValues, *probe.Error) { preSignV4Values := preSignValues{} var err *probe.Error - // Save credentail values. - preSignV4Values.Creds, err = parseCredential("Credential=" + query.Get("X-Amz-Credential")) + // Save credential. + preSignV4Values.Credential, err = parseCredential("Credential=" + query.Get("X-Amz-Credential")) if err != nil { return preSignValues{}, err.Trace(query.Get("X-Amz-Credential")) } @@ -181,7 +200,7 @@ func parseSignV4(v4Auth string) (signValues, *probe.Error) { var err *probe.Error // Save credentail values. - signV4Values.Creds, err = parseCredential(authFields[0]) + signV4Values.Credential, err = parseCredential(authFields[0]) if err != nil { return signValues{}, err.Trace(v4Auth) } diff --git a/pkg/signature/postpolicyform.go b/pkg/s3/signature4/v4-postpolicyform.go similarity index 99% rename from pkg/signature/postpolicyform.go rename to pkg/s3/signature4/v4-postpolicyform.go index 006c6d848..47247a433 100644 --- a/pkg/signature/postpolicyform.go +++ b/pkg/s3/signature4/v4-postpolicyform.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package signature +package signature4 import ( "encoding/base64" diff --git a/pkg/signature/signature-v4.go b/pkg/s3/signature4/v4-signature.go similarity index 81% rename from pkg/signature/signature-v4.go rename to pkg/s3/signature4/v4-signature.go index 624722cf4..36ebcbcd8 100644 --- a/pkg/signature/signature-v4.go +++ b/pkg/s3/signature4/v4-signature.go @@ -14,7 +14,15 @@ * limitations under the License. */ -package signature +// Package signature4 implements helper functions to validate AWS +// Signature Version '4' authorization header. +// +// This package provides comprehensive helpers for following signature +// types. +// - Based on Authorization header. +// - Based on Query parameters. +// - Based on Form POST policy. +package signature4 import ( "bytes" @@ -30,8 +38,8 @@ import ( "github.com/minio/minio/pkg/probe" ) -// Signature - local variables -type Signature struct { +// Sign - local variables +type Sign struct { accessKeyID string secretAccessKey string region string @@ -39,6 +47,7 @@ type Signature struct { extractedSignedHeaders http.Header } +// AWS Signature Version '4' constants. const ( signV4Algorithm = "AWS4-HMAC-SHA256" iso8601Format = "20060102T150405Z" @@ -46,7 +55,7 @@ const ( ) // New - initialize a new authorization checkes. -func New(accessKeyID, secretAccessKey, region string) (*Signature, *probe.Error) { +func New(accessKeyID, secretAccessKey, region string) (*Sign, *probe.Error) { if !isValidAccessKey.MatchString(accessKeyID) { return nil, ErrInvalidAccessKeyID("Invalid access key id.", accessKeyID).Trace(accessKeyID) } @@ -56,7 +65,7 @@ func New(accessKeyID, secretAccessKey, region string) (*Signature, *probe.Error) if region == "" { return nil, ErrRegionISEmpty("Region is empty.").Trace() } - signature := &Signature{ + signature := &Sign{ accessKeyID: accessKeyID, secretAccessKey: secretAccessKey, region: region, @@ -65,7 +74,7 @@ func New(accessKeyID, secretAccessKey, region string) (*Signature, *probe.Error) } // SetHTTPRequestToVerify - sets the http request which needs to be verified. -func (s *Signature) SetHTTPRequestToVerify(r *http.Request) *Signature { +func (s *Sign) SetHTTPRequestToVerify(r *http.Request) *Sign { // Do not set http request if its 'nil'. if r == nil { return s @@ -75,7 +84,7 @@ func (s *Signature) SetHTTPRequestToVerify(r *http.Request) *Signature { } // getCanonicalHeaders generate a list of request headers with their values -func (s Signature) getCanonicalHeaders(signedHeaders http.Header) string { +func (s Sign) getCanonicalHeaders(signedHeaders http.Header) string { var headers []string vals := make(http.Header) for k, vv := range signedHeaders { @@ -107,7 +116,7 @@ func (s Signature) getCanonicalHeaders(signedHeaders http.Header) string { } // getSignedHeaders generate a string i.e alphabetically sorted, semicolon-separated list of lowercase request header names -func (s Signature) getSignedHeaders(signedHeaders http.Header) string { +func (s Sign) getSignedHeaders(signedHeaders http.Header) string { var headers []string for k := range signedHeaders { headers = append(headers, strings.ToLower(k)) @@ -127,7 +136,7 @@ func (s Signature) getSignedHeaders(signedHeaders http.Header) string { // \n // // -func (s *Signature) getCanonicalRequest() string { +func (s *Sign) getCanonicalRequest() string { payload := s.httpRequest.Header.Get(http.CanonicalHeaderKey("x-amz-content-sha256")) s.httpRequest.URL.RawQuery = strings.Replace(s.httpRequest.URL.Query().Encode(), "+", "%20", -1) encodedPath := getURLEncodedName(s.httpRequest.URL.Path) @@ -154,7 +163,7 @@ func (s *Signature) getCanonicalRequest() string { // \n // // -func (s Signature) getPresignedCanonicalRequest(presignedQuery string) string { +func (s Sign) getPresignedCanonicalRequest(presignedQuery string) string { rawQuery := strings.Replace(presignedQuery, "+", "%20", -1) encodedPath := getURLEncodedName(s.httpRequest.URL.Path) // Convert any space strings back to "+". @@ -171,7 +180,7 @@ func (s Signature) getPresignedCanonicalRequest(presignedQuery string) string { } // getScope generate a string of a specific date, an AWS region, and a service. -func (s Signature) getScope(t time.Time) string { +func (s Sign) getScope(t time.Time) string { scope := strings.Join([]string{ t.Format(yyyymmdd), s.region, @@ -182,7 +191,7 @@ func (s Signature) getScope(t time.Time) string { } // getStringToSign a string based on selected query values. -func (s Signature) getStringToSign(canonicalRequest string, t time.Time) string { +func (s Sign) getStringToSign(canonicalRequest string, t time.Time) string { stringToSign := signV4Algorithm + "\n" + t.Format(iso8601Format) + "\n" stringToSign = stringToSign + s.getScope(t) + "\n" canonicalRequestBytes := sha256.Sum256([]byte(canonicalRequest)) @@ -191,7 +200,7 @@ func (s Signature) getStringToSign(canonicalRequest string, t time.Time) string } // getSigningKey hmac seed to calculate final signature. -func (s Signature) getSigningKey(t time.Time) []byte { +func (s Sign) getSigningKey(t time.Time) []byte { secret := s.secretAccessKey date := sumHMAC([]byte("AWS4"+secret), []byte(t.Format(yyyymmdd))) region := sumHMAC(date, []byte(s.region)) @@ -201,27 +210,27 @@ func (s Signature) getSigningKey(t time.Time) []byte { } // getSignature final signature in hexadecimal form. -func (s Signature) getSignature(signingKey []byte, stringToSign string) string { +func (s Sign) getSignature(signingKey []byte, stringToSign string) string { return hex.EncodeToString(sumHMAC(signingKey, []byte(stringToSign))) } // 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 (s *Signature) DoesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error) { +func (s *Sign) DoesPolicySignatureMatch(formValues map[string]string) (bool, *probe.Error) { // Parse credential tag. - creds, err := parseCredential("Credential=" + formValues["X-Amz-Credential"]) + credential, err := parseCredential("Credential=" + formValues["X-Amz-Credential"]) if err != nil { return false, err.Trace(formValues["X-Amz-Credential"]) } // Verify if the access key id matches. - if creds.accessKeyID != s.accessKeyID { - return false, ErrInvalidAccessKeyID("Access key id does not match with our records.", creds.accessKeyID).Trace(creds.accessKeyID) + if credential.accessKeyID != s.accessKeyID { + return false, ErrInvalidAccessKeyID("Access key id does not match with our records.", credential.accessKeyID).Trace(credential.accessKeyID) } // Verify if the region is valid. - reqRegion := creds.scope.region + reqRegion := credential.scope.region if !isValidRegion(reqRegion, s.region) { return false, ErrInvalidRegion("Requested region is not recognized.", reqRegion).Trace(reqRegion) } @@ -245,20 +254,20 @@ func (s *Signature) DoesPolicySignatureMatch(formValues map[string]string) (bool // 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 (s *Signature) DoesPresignedSignatureMatch() (bool, *probe.Error) { +func (s *Sign) DoesPresignedSignatureMatch() (bool, *probe.Error) { // Parse request query string. - preSignV4Values, err := parsePreSignV4(s.httpRequest.URL.Query()) + preSignValues, err := parsePreSignV4(s.httpRequest.URL.Query()) if err != nil { return false, err.Trace(s.httpRequest.URL.String()) } // Verify if the access key id matches. - if preSignV4Values.Creds.accessKeyID != s.accessKeyID { - return false, ErrInvalidAccessKeyID("Access key id does not match with our records.", preSignV4Values.Creds.accessKeyID).Trace(preSignV4Values.Creds.accessKeyID) + if preSignValues.Credential.accessKeyID != s.accessKeyID { + return false, ErrInvalidAccessKeyID("Access key id does not match with our records.", preSignValues.Credential.accessKeyID).Trace(preSignValues.Credential.accessKeyID) } // Verify if region is valid. - reqRegion := preSignV4Values.Creds.scope.region + reqRegion := preSignValues.Credential.scope.region if !isValidRegion(reqRegion, s.region) { return false, ErrInvalidRegion("Requested region is not recognized.", reqRegion).Trace(reqRegion) } @@ -267,19 +276,19 @@ func (s *Signature) DoesPresignedSignatureMatch() (bool, *probe.Error) { s.region = reqRegion // Extract all the signed headers along with its values. - s.extractedSignedHeaders = extractSignedHeaders(preSignV4Values.SignedHeaders, s.httpRequest.Header) + s.extractedSignedHeaders = extractSignedHeaders(preSignValues.SignedHeaders, s.httpRequest.Header) // Construct new query. query := make(url.Values) query.Set("X-Amz-Algorithm", signV4Algorithm) - if time.Now().UTC().Sub(preSignV4Values.Date) > time.Duration(preSignV4Values.Expires) { + if time.Now().UTC().Sub(preSignValues.Date) > time.Duration(preSignValues.Expires) { return false, ErrExpiredPresignRequest("Presigned request already expired, please initiate a new request.") } // Save the date and expires. - t := preSignV4Values.Date - expireSeconds := int(time.Duration(preSignV4Values.Expires) / time.Second) + t := preSignValues.Date + expireSeconds := int(time.Duration(preSignValues.Expires) / time.Second) // Construct the query. query.Set("X-Amz-Date", t.Format(iso8601Format)) @@ -325,7 +334,7 @@ func (s *Signature) DoesPresignedSignatureMatch() (bool, *probe.Error) { // 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 (s *Signature) DoesSignatureMatch(hashedPayload string) (bool, *probe.Error) { +func (s *Sign) DoesSignatureMatch(hashedPayload string) (bool, *probe.Error) { // Save authorization header. v4Auth := s.httpRequest.Header.Get("Authorization") @@ -339,12 +348,12 @@ func (s *Signature) DoesSignatureMatch(hashedPayload string) (bool, *probe.Error s.extractedSignedHeaders = extractSignedHeaders(signV4Values.SignedHeaders, s.httpRequest.Header) // Verify if the access key id matches. - if signV4Values.Creds.accessKeyID != s.accessKeyID { - return false, ErrInvalidAccessKeyID("Access key id does not match with our records.", signV4Values.Creds.accessKeyID).Trace(signV4Values.Creds.accessKeyID) + if signV4Values.Credential.accessKeyID != s.accessKeyID { + return false, ErrInvalidAccessKeyID("Access key id does not match with our records.", signV4Values.Credential.accessKeyID).Trace(signV4Values.Credential.accessKeyID) } // Verify if region is valid. - reqRegion := signV4Values.Creds.scope.region + reqRegion := signV4Values.Credential.scope.region if !isValidRegion(reqRegion, s.region) { return false, ErrInvalidRegion("Requested region is not recognized.", reqRegion).Trace(reqRegion) } diff --git a/pkg/signature/utils.go b/pkg/s3/signature4/v4-utils.go similarity index 84% rename from pkg/signature/utils.go rename to pkg/s3/signature4/v4-utils.go index b05aea195..270c1f2a3 100644 --- a/pkg/signature/utils.go +++ b/pkg/s3/signature4/v4-utils.go @@ -1,4 +1,20 @@ -package signature +/* + * 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 signature4 import ( "crypto/hmac" @@ -11,12 +27,6 @@ import ( "github.com/minio/minio/pkg/crypto/sha256" ) -// AccessID and SecretID length in bytes -const ( - MinioAccessID = 20 - MinioSecretID = 40 -) - /// helpers // isValidSecretKey - validate secret key. diff --git a/pkg/xl/bucket.go b/pkg/xl/bucket.go index 645257e94..2e84b0aab 100644 --- a/pkg/xl/bucket.go +++ b/pkg/xl/bucket.go @@ -34,7 +34,7 @@ import ( "github.com/minio/minio/pkg/crypto/sha256" "github.com/minio/minio/pkg/crypto/sha512" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" "github.com/minio/minio/pkg/xl/block" ) @@ -236,7 +236,7 @@ func (b bucket) ReadObject(objectName string) (reader io.ReadCloser, size int64, } // WriteObject - write a new object into bucket -func (b bucket) WriteObject(objectName string, objectData io.Reader, size int64, expectedMD5Sum string, metadata map[string]string, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (b bucket) WriteObject(objectName string, objectData io.Reader, size int64, expectedMD5Sum string, metadata map[string]string, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { b.lock.Lock() defer b.lock.Unlock() if objectName == "" || objectData == nil { diff --git a/pkg/xl/interfaces.go b/pkg/xl/interfaces.go index ad16642d7..79cc9822b 100644 --- a/pkg/xl/interfaces.go +++ b/pkg/xl/interfaces.go @@ -20,7 +20,7 @@ import ( "io" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" ) // Collection of XL specification interfaces @@ -37,7 +37,7 @@ type CloudStorage interface { GetBucketMetadata(bucket string) (BucketMetadata, *probe.Error) SetBucketMetadata(bucket string, metadata map[string]string) *probe.Error ListBuckets() ([]BucketMetadata, *probe.Error) - MakeBucket(bucket string, ACL string, location io.Reader, signature *signV4.Signature) *probe.Error + MakeBucket(bucket string, ACL string, location io.Reader, signature *signature4.Sign) *probe.Error // Bucket operations ListObjects(string, BucketResourcesMetadata) ([]ObjectMetadata, BucketResourcesMetadata, *probe.Error) @@ -46,7 +46,7 @@ type CloudStorage interface { GetObject(w io.Writer, bucket, object string, start, length int64) (int64, *probe.Error) GetObjectMetadata(bucket, object string) (ObjectMetadata, *probe.Error) // bucket, object, expectedMD5Sum, size, reader, metadata, signature - CreateObject(string, string, string, int64, io.Reader, map[string]string, *signV4.Signature) (ObjectMetadata, *probe.Error) + CreateObject(string, string, string, int64, io.Reader, map[string]string, *signature4.Sign) (ObjectMetadata, *probe.Error) Multipart } @@ -55,8 +55,8 @@ type CloudStorage interface { type Multipart interface { NewMultipartUpload(bucket, key, contentType string) (string, *probe.Error) AbortMultipartUpload(bucket, key, uploadID string) *probe.Error - CreateObjectPart(string, string, string, int, string, string, int64, io.Reader, *signV4.Signature) (string, *probe.Error) - CompleteMultipartUpload(bucket, key, uploadID string, data io.Reader, signature *signV4.Signature) (ObjectMetadata, *probe.Error) + CreateObjectPart(string, string, string, int, string, string, int64, io.Reader, *signature4.Sign) (string, *probe.Error) + CompleteMultipartUpload(bucket, key, uploadID string, data io.Reader, signature *signature4.Sign) (ObjectMetadata, *probe.Error) ListMultipartUploads(string, BucketMultipartResourcesMetadata) (BucketMultipartResourcesMetadata, *probe.Error) ListObjectParts(string, string, ObjectResourcesMetadata) (ObjectResourcesMetadata, *probe.Error) } diff --git a/pkg/xl/multipart.go b/pkg/xl/multipart.go index 7349fa925..df03567b1 100644 --- a/pkg/xl/multipart.go +++ b/pkg/xl/multipart.go @@ -35,7 +35,7 @@ import ( "github.com/minio/minio/pkg/crypto/sha256" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" "github.com/minio/minio/pkg/xl/cache/data" ) @@ -109,7 +109,7 @@ func (xl API) AbortMultipartUpload(bucket, key, uploadID string) *probe.Error { } // CreateObjectPart - create a part in a multipart session -func (xl API) CreateObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *signV4.Signature) (string, *probe.Error) { +func (xl API) CreateObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *signature4.Sign) (string, *probe.Error) { xl.lock.Lock() etag, err := xl.createObjectPart(bucket, key, uploadID, partID, "", expectedMD5Sum, size, data, signature) xl.lock.Unlock() @@ -120,7 +120,7 @@ func (xl API) CreateObjectPart(bucket, key, uploadID string, partID int, content } // createObject - internal wrapper function called by CreateObjectPart -func (xl API) createObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *signV4.Signature) (string, *probe.Error) { +func (xl API) createObjectPart(bucket, key, uploadID string, partID int, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *signature4.Sign) (string, *probe.Error) { if !IsValidBucket(bucket) { return "", probe.NewError(BucketNameInvalid{Bucket: bucket}) } @@ -289,7 +289,7 @@ func (xl API) mergeMultipart(parts *CompleteMultipartUpload, uploadID string, fu } // CompleteMultipartUpload - complete a multipart upload and persist the data -func (xl API) CompleteMultipartUpload(bucket, key, uploadID string, data io.Reader, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (xl API) CompleteMultipartUpload(bucket, key, uploadID string, data io.Reader, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { xl.lock.Lock() defer xl.lock.Unlock() size := int64(xl.multiPartObjects[uploadID].Stats().Bytes) @@ -307,7 +307,7 @@ func (xl API) CompleteMultipartUpload(bucket, key, uploadID string, data io.Read return objectMetadata, nil } -func (xl API) completeMultipartUploadV2(bucket, key, uploadID string, data io.Reader, signature *signV4.Signature) (io.Reader, *probe.Error) { +func (xl API) completeMultipartUploadV2(bucket, key, uploadID string, data io.Reader, signature *signature4.Sign) (io.Reader, *probe.Error) { if !IsValidBucket(bucket) { return nil, probe.NewError(BucketNameInvalid{Bucket: bucket}) } diff --git a/pkg/xl/xl-v1.go b/pkg/xl/xl-v1.go index 613684a8f..723f6d5b4 100644 --- a/pkg/xl/xl-v1.go +++ b/pkg/xl/xl-v1.go @@ -35,7 +35,7 @@ import ( "github.com/minio/minio/pkg/crypto/sha256" "github.com/minio/minio/pkg/crypto/sha512" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" "github.com/minio/minio/pkg/xl/block" ) @@ -128,7 +128,7 @@ func (xl API) listObjects(bucket, prefix, marker, delimiter string, maxkeys int) } // putObject - put object -func (xl API) putObject(bucket, object, expectedMD5Sum string, reader io.Reader, size int64, metadata map[string]string, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (xl API) putObject(bucket, object, expectedMD5Sum string, reader io.Reader, size int64, metadata map[string]string, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { if bucket == "" || strings.TrimSpace(bucket) == "" { return ObjectMetadata{}, probe.NewError(InvalidArgument{}) } @@ -160,7 +160,7 @@ func (xl API) putObject(bucket, object, expectedMD5Sum string, reader io.Reader, } // putObject - put object -func (xl API) putObjectPart(bucket, object, expectedMD5Sum, uploadID string, partID int, reader io.Reader, size int64, metadata map[string]string, signature *signV4.Signature) (PartMetadata, *probe.Error) { +func (xl API) putObjectPart(bucket, object, expectedMD5Sum, uploadID string, partID int, reader io.Reader, size int64, metadata map[string]string, signature *signature4.Sign) (PartMetadata, *probe.Error) { if bucket == "" || strings.TrimSpace(bucket) == "" { return PartMetadata{}, probe.NewError(InvalidArgument{}) } @@ -337,7 +337,7 @@ func (xl API) listObjectParts(bucket, object string, resources ObjectResourcesMe } // completeMultipartUpload complete an incomplete multipart upload -func (xl API) completeMultipartUpload(bucket, object, uploadID string, data io.Reader, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (xl API) completeMultipartUpload(bucket, object, uploadID string, data io.Reader, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { if bucket == "" || strings.TrimSpace(bucket) == "" { return ObjectMetadata{}, probe.NewError(InvalidArgument{}) } diff --git a/pkg/xl/xl-v2.go b/pkg/xl/xl-v2.go index 962943b46..7d6a40ba5 100644 --- a/pkg/xl/xl-v2.go +++ b/pkg/xl/xl-v2.go @@ -34,7 +34,7 @@ import ( "github.com/minio/minio/pkg/crypto/sha256" "github.com/minio/minio/pkg/probe" "github.com/minio/minio/pkg/quick" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" "github.com/minio/minio/pkg/xl/cache/data" "github.com/minio/minio/pkg/xl/cache/metadata" ) @@ -267,7 +267,7 @@ func isMD5SumEqual(expectedMD5Sum, actualMD5Sum string) *probe.Error { } // CreateObject - create an object -func (xl API) CreateObject(bucket, key, expectedMD5Sum string, size int64, data io.Reader, metadata map[string]string, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (xl API) CreateObject(bucket, key, expectedMD5Sum string, size int64, data io.Reader, metadata map[string]string, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { xl.lock.Lock() defer xl.lock.Unlock() @@ -280,7 +280,7 @@ func (xl API) CreateObject(bucket, key, expectedMD5Sum string, size int64, data } // createObject - PUT object to cache buffer -func (xl API) createObject(bucket, key, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *signV4.Signature) (ObjectMetadata, *probe.Error) { +func (xl API) createObject(bucket, key, contentType, expectedMD5Sum string, size int64, data io.Reader, signature *signature4.Sign) (ObjectMetadata, *probe.Error) { if len(xl.config.NodeDiskMap) == 0 { if size > int64(xl.config.MaxSize) { generic := GenericObjectError{Bucket: bucket, Object: key} @@ -414,7 +414,7 @@ func (xl API) createObject(bucket, key, contentType, expectedMD5Sum string, size } // MakeBucket - create bucket in cache -func (xl API) MakeBucket(bucketName, acl string, location io.Reader, signature *signV4.Signature) *probe.Error { +func (xl API) MakeBucket(bucketName, acl string, location io.Reader, signature *signature4.Sign) *probe.Error { xl.lock.Lock() defer xl.lock.Unlock() diff --git a/routers.go b/routers.go index 496396d36..45f7d1a6c 100644 --- a/routers.go +++ b/routers.go @@ -27,7 +27,7 @@ import ( "github.com/minio/minio-go" "github.com/minio/minio/pkg/fs" "github.com/minio/minio/pkg/probe" - signV4 "github.com/minio/minio/pkg/signature" + "github.com/minio/minio/pkg/s3/signature4" ) // storageAPI container for S3 compatible API. @@ -37,7 +37,7 @@ type storageAPI struct { // Filesystem instance. Filesystem fs.Filesystem // Signature instance. - Signature *signV4.Signature + Signature *signature4.Sign // Region instance. Region string } @@ -141,7 +141,7 @@ func initAPI(conf cloudServerConfig) storageAPI { fs, err := fs.New(conf.Path, conf.MinFreeDisk) fatalIf(err.Trace(), "Initializing filesystem failed.", nil) - sign, err := signV4.New(conf.AccessKeyID, conf.SecretAccessKey, conf.Region) + sign, err := signature4.New(conf.AccessKeyID, conf.SecretAccessKey, conf.Region) fatalIf(err.Trace(conf.AccessKeyID, conf.SecretAccessKey, conf.Region), "Initializing signature version '4' failed.", nil) return storageAPI{ diff --git a/signature.go b/signature.go deleted file mode 100644 index e96a219e0..000000000 --- a/signature.go +++ /dev/null @@ -1,94 +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 ( - "crypto/sha256" - "encoding/hex" - "net/http" - "strings" - - signV4 "github.com/minio/minio/pkg/signature" -) - -// Verify if request has JWT. -func isRequestJWT(r *http.Request) bool { - if _, ok := r.Header["Authorization"]; ok { - if strings.HasPrefix(r.Header.Get("Authorization"), jwtAlgorithm) { - return ok - } - } - return false -} - -// Verify if request has AWS Signature Version '4'. -func isRequestSignatureV4(r *http.Request) bool { - if _, ok := r.Header["Authorization"]; ok { - if strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) { - return ok - } - } - return false -} - -// Verify if request has AWS Presignature Version '4'. -func isRequestPresignedSignatureV4(r *http.Request) bool { - if _, ok := r.URL.Query()["X-Amz-Credential"]; ok { - return ok - } - return false -} - -// Verify if request has AWS Post policy Signature Version '4'. -func isRequestPostPolicySignatureV4(r *http.Request) bool { - if _, ok := r.Header["Content-Type"]; ok { - if strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { - return true - } - } - return false -} - -// Verify if request requires ACL check. -func isRequestRequiresACLCheck(r *http.Request) bool { - if isRequestSignatureV4(r) || isRequestPresignedSignatureV4(r) || isRequestPostPolicySignatureV4(r) { - return false - } - return true -} - -// Verify if request has valid AWS Signature Version '4'. -func isSignV4ReqAuthenticated(sign *signV4.Signature, r *http.Request) bool { - auth := sign.SetHTTPRequestToVerify(r) - if isRequestSignatureV4(r) { - dummyPayload := sha256.Sum256([]byte("")) - ok, err := auth.DoesSignatureMatch(hex.EncodeToString(dummyPayload[:])) - if err != nil { - errorIf(err.Trace(), "Signature verification failed.", nil) - return false - } - return ok - } else if isRequestPresignedSignatureV4(r) { - ok, err := auth.DoesPresignedSignatureMatch() - if err != nil { - errorIf(err.Trace(), "Presigned signature verification failed.", nil) - return false - } - return ok - } - return false -}