sign: StreamingSign should use region from client. (#4577)

This is a fix to make streaming signature to behave
the same as regular signature and presigned signature.

Fixes https://github.com/minio/minio-go/issues/718
This commit is contained in:
Harshavardhana 2017-06-21 11:30:34 -07:00 committed by GitHub
parent 9d3d64df2d
commit cec8b238f3

View File

@ -41,13 +41,10 @@ const (
) )
// getChunkSignature - get chunk signature. // getChunkSignature - get chunk signature.
func getChunkSignature(seedSignature string, date time.Time, hashedChunk string) string { func getChunkSignature(seedSignature string, region string, date time.Time, hashedChunk string) string {
// Access credentials. // Access credentials.
cred := serverConfig.GetCredential() cred := serverConfig.GetCredential()
// Server region.
region := serverConfig.GetRegion()
// Calculate string to sign. // Calculate string to sign.
stringToSign := signV4ChunkedAlgorithm + "\n" + stringToSign := signV4ChunkedAlgorithm + "\n" +
date.Format(iso8601Format) + "\n" + date.Format(iso8601Format) + "\n" +
@ -69,12 +66,12 @@ func getChunkSignature(seedSignature string, date time.Time, hashedChunk string)
// - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html // - http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
// returns signature, error otherwise if the signature mismatches or any other // returns signature, error otherwise if the signature mismatches or any other
// error while parsing and validating. // error while parsing and validating.
func calculateSeedSignature(r *http.Request) (signature string, date time.Time, errCode APIErrorCode) { func calculateSeedSignature(r *http.Request) (signature string, region string, date time.Time, errCode APIErrorCode) {
// Access credentials. // Access credentials.
cred := serverConfig.GetCredential() cred := serverConfig.GetCredential()
// Server region. // Configured region.
region := serverConfig.GetRegion() confRegion := serverConfig.GetRegion()
// Copy request. // Copy request.
req := *r req := *r
@ -85,7 +82,7 @@ func calculateSeedSignature(r *http.Request) (signature string, date time.Time,
// Parse signature version '4' header. // Parse signature version '4' header.
signV4Values, errCode := parseSignV4(v4Auth) signV4Values, errCode := parseSignV4(v4Auth)
if errCode != ErrNone { if errCode != ErrNone {
return "", time.Time{}, errCode return "", "", time.Time{}, errCode
} }
// Payload streaming. // Payload streaming.
@ -93,32 +90,32 @@ func calculateSeedSignature(r *http.Request) (signature string, date time.Time,
// Payload for STREAMING signature should be 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD' // Payload for STREAMING signature should be 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD'
if payload != req.Header.Get("X-Amz-Content-Sha256") { if payload != req.Header.Get("X-Amz-Content-Sha256") {
return "", time.Time{}, ErrContentSHA256Mismatch return "", "", time.Time{}, ErrContentSHA256Mismatch
} }
// Extract all the signed headers along with its values. // Extract all the signed headers along with its values.
extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, r) extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, r)
if errCode != ErrNone { if errCode != ErrNone {
return "", time.Time{}, errCode return "", "", time.Time{}, errCode
} }
// Verify if the access key id matches. // Verify if the access key id matches.
if signV4Values.Credential.accessKey != cred.AccessKey { if signV4Values.Credential.accessKey != cred.AccessKey {
return "", time.Time{}, ErrInvalidAccessKeyID return "", "", time.Time{}, ErrInvalidAccessKeyID
} }
// Verify if region is valid. // Verify if region is valid.
sRegion := signV4Values.Credential.scope.region region = signV4Values.Credential.scope.region
// Should validate region, only if region is set. Some operations // Should validate region, only if region is set. Some operations
// do not need region validated for example GetBucketLocation. // do not need region validated for example GetBucketLocation.
if !isValidRegion(sRegion, region) { if !isValidRegion(region, confRegion) {
return "", time.Time{}, ErrInvalidRegion return "", "", time.Time{}, ErrInvalidRegion
} }
// Extract date, if not present throw error. // Extract date, if not present throw error.
var dateStr string var dateStr string
if dateStr = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); dateStr == "" { if dateStr = req.Header.Get(http.CanonicalHeaderKey("x-amz-date")); dateStr == "" {
if dateStr = r.Header.Get("Date"); dateStr == "" { if dateStr = r.Header.Get("Date"); dateStr == "" {
return "", time.Time{}, ErrMissingDateHeader return "", "", time.Time{}, ErrMissingDateHeader
} }
} }
// Parse date header. // Parse date header.
@ -126,7 +123,7 @@ func calculateSeedSignature(r *http.Request) (signature string, date time.Time,
date, err = time.Parse(iso8601Format, dateStr) date, err = time.Parse(iso8601Format, dateStr)
if err != nil { if err != nil {
errorIf(err, "Unable to parse date", dateStr) errorIf(err, "Unable to parse date", dateStr)
return "", time.Time{}, ErrMalformedDate return "", "", time.Time{}, ErrMalformedDate
} }
// Query string. // Query string.
@ -146,11 +143,11 @@ func calculateSeedSignature(r *http.Request) (signature string, date time.Time,
// Verify if signature match. // Verify if signature match.
if newSignature != signV4Values.Signature { if newSignature != signV4Values.Signature {
return "", time.Time{}, ErrSignatureDoesNotMatch return "", "", time.Time{}, ErrSignatureDoesNotMatch
} }
// Return caculated signature. // Return caculated signature.
return newSignature, date, ErrNone return newSignature, region, date, ErrNone
} }
const maxLineLength = 4 * humanize.KiByte // assumed <= bufio.defaultBufSize 4KiB const maxLineLength = 4 * humanize.KiByte // assumed <= bufio.defaultBufSize 4KiB
@ -168,7 +165,7 @@ var errMalformedEncoding = errors.New("malformed chunked encoding")
// NewChunkedReader is not needed by normal applications. The http package // NewChunkedReader is not needed by normal applications. The http package
// automatically decodes chunking when reading response bodies. // automatically decodes chunking when reading response bodies.
func newSignV4ChunkedReader(req *http.Request) (io.Reader, APIErrorCode) { func newSignV4ChunkedReader(req *http.Request) (io.Reader, APIErrorCode) {
seedSignature, seedDate, errCode := calculateSeedSignature(req) seedSignature, region, seedDate, errCode := calculateSeedSignature(req)
if errCode != ErrNone { if errCode != ErrNone {
return nil, errCode return nil, errCode
} }
@ -176,6 +173,7 @@ func newSignV4ChunkedReader(req *http.Request) (io.Reader, APIErrorCode) {
reader: bufio.NewReader(req.Body), reader: bufio.NewReader(req.Body),
seedSignature: seedSignature, seedSignature: seedSignature,
seedDate: seedDate, seedDate: seedDate,
region: region,
chunkSHA256Writer: sha256.New(), chunkSHA256Writer: sha256.New(),
state: readChunkHeader, state: readChunkHeader,
}, ErrNone }, ErrNone
@ -187,6 +185,7 @@ type s3ChunkedReader struct {
reader *bufio.Reader reader *bufio.Reader
seedSignature string seedSignature string
seedDate time.Time seedDate time.Time
region string
state chunkState state chunkState
lastChunk bool lastChunk bool
chunkSignature string chunkSignature string
@ -304,7 +303,7 @@ func (cr *s3ChunkedReader) Read(buf []byte) (n int, err error) {
// Calculate the hashed chunk. // Calculate the hashed chunk.
hashedChunk := hex.EncodeToString(cr.chunkSHA256Writer.Sum(nil)) hashedChunk := hex.EncodeToString(cr.chunkSHA256Writer.Sum(nil))
// Calculate the chunk signature. // Calculate the chunk signature.
newSignature := getChunkSignature(cr.seedSignature, cr.seedDate, hashedChunk) newSignature := getChunkSignature(cr.seedSignature, cr.region, cr.seedDate, hashedChunk)
if cr.chunkSignature != newSignature { if cr.chunkSignature != newSignature {
// Chunk signature doesn't match we return signature does not match. // Chunk signature doesn't match we return signature does not match.
cr.err = errSignatureMismatch cr.err = errSignatureMismatch