From 400e9309f1c7d70c92439abc26541b67d60bc5fd Mon Sep 17 00:00:00 2001 From: Krishna Srinivas Date: Fri, 9 Sep 2016 22:08:07 +0530 Subject: [PATCH] Signature-V4: Include content-length for signature calculation. (#2643) This is to be compatible with clients which includes content-length for signature calculation (and hence deviating from AWS Signature-v4 spec) --- cmd/signature-v4.go | 15 ++++++++++++++- cmd/utils.go | 13 +++++++++++++ cmd/utils_test.go | 30 +++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/cmd/signature-v4.go b/cmd/signature-v4.go index 2a91e70c2..4edac7afd 100644 --- a/cmd/signature-v4.go +++ b/cmd/signature-v4.go @@ -342,8 +342,21 @@ func doesSignatureMatch(hashedPayload string, r *http.Request, validateRegion bo return ErrContentSHA256Mismatch } + header := req.Header + + // Signature-V4 spec excludes Content-Length from signed headers list for signature calculation. + // But some clients deviate from this rule. Hence we consider Content-Length for signature + // calculation to be compatible with such clients. + for _, h := range signV4Values.SignedHeaders { + if h == "content-length" { + header = cloneHeader(req.Header) + header.Add("content-length", strconv.FormatInt(r.ContentLength, 10)) + break + } + } + // Extract all the signed headers along with its values. - extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, req.Header) + extractedSignedHeaders, errCode := extractSignedHeaders(signV4Values.SignedHeaders, header) if errCode != ErrNone { return errCode } diff --git a/cmd/utils.go b/cmd/utils.go index fa28d7803..0985b87b8 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -21,6 +21,7 @@ import ( "encoding/xml" "errors" "io" + "net/http" "os" "os/exec" "strings" @@ -30,6 +31,18 @@ import ( "github.com/pkg/profile" ) +// make a copy of http.Header +func cloneHeader(h http.Header) http.Header { + h2 := make(http.Header, len(h)) + for k, vv := range h { + vv2 := make([]string, len(vv)) + copy(vv2, vv) + h2[k] = vv2 + + } + return h2 +} + // xmlDecoder provide decoded value in xml. func xmlDecoder(body io.Reader, v interface{}, size int64) error { var lbody io.Reader diff --git a/cmd/utils_test.go b/cmd/utils_test.go index becf63a48..5b30bd38b 100644 --- a/cmd/utils_test.go +++ b/cmd/utils_test.go @@ -16,7 +16,35 @@ package cmd -import "testing" +import ( + "net/http" + "reflect" + "testing" +) + +// Tests http.Header clone. +func TestCloneHeader(t *testing.T) { + headers := []http.Header{ + http.Header{ + "Content-Type": {"text/html; charset=UTF-8"}, + "Content-Length": {"0"}, + }, + http.Header{ + "Content-Length": {"0", "1", "2"}, + }, + http.Header{ + "Expires": {"-1"}, + "Content-Length": {"0"}, + "Content-Encoding": {"gzip"}, + }, + } + for i, header := range headers { + clonedHeader := cloneHeader(header) + if !reflect.DeepEqual(header, clonedHeader) { + t.Errorf("Test %d failed", i+1) + } + } +} // Tests maximum object size. func TestMaxObjectSize(t *testing.T) {