signV4: Move pkg/signature to pkg/s3/signature4

Cleanup and move this to relevant path.
This commit is contained in:
Harshavardhana 2016-02-21 17:57:05 -08:00
parent d3966d1dde
commit 653ceee9ee
16 changed files with 210 additions and 190 deletions

View File

@ -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 {

View File

@ -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

View File

@ -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})

View File

@ -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()

View File

@ -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()

View File

@ -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)
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package signature
package signature4
import (
"encoding/base64"

View File

@ -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 {
// <SignedHeaders>\n
// <HashedPayload>
//
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 {
// <SignedHeaders>\n
// <HashedPayload>
//
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)
}

View File

@ -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.

View File

@ -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 {

View File

@ -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)
}

View File

@ -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})
}

View File

@ -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{})
}

View File

@ -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()

View File

@ -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{

View File

@ -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
}