diff --git a/pkg/donut/signature-v4.go b/pkg/donut/signature-v4.go index a60a4d7a0..af5be5ddc 100644 --- a/pkg/donut/signature-v4.go +++ b/pkg/donut/signature-v4.go @@ -102,13 +102,10 @@ func urlEncodeName(name string) (string, error) { } // getCanonicalHeaders generate a list of request headers with their values -func (r *Signature) getCanonicalHeaders() string { +func (r *Signature) getCanonicalHeaders(signedHeaders map[string][]string) string { var headers []string vals := make(map[string][]string) - for k, vv := range r.Request.Header { - if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok { - continue // ignored header - } + for k, vv := range signedHeaders { headers = append(headers, strings.ToLower(k)) vals[strings.ToLower(k)] = vv } @@ -137,12 +134,9 @@ func (r *Signature) getCanonicalHeaders() string { } // getSignedHeaders generate a string i.e alphabetically sorted, semicolon-separated list of lowercase request header names -func (r *Signature) getSignedHeaders() string { +func (r *Signature) getSignedHeaders(signedHeaders map[string][]string) string { var headers []string - for k := range r.Request.Header { - if _, ok := ignoredHeaders[http.CanonicalHeaderKey(k)]; ok { - continue // ignored header - } + for k := range signedHeaders { headers = append(headers, strings.ToLower(k)) } headers = append(headers, "host") @@ -150,6 +144,22 @@ func (r *Signature) getSignedHeaders() string { return strings.Join(headers, ";") } +// extractSignedHeaders extract signed headers from Authorization header +func (r *Signature) extractSignedHeaders() map[string][]string { + authFields := strings.Split(strings.TrimSpace(r.AuthHeader), ",") + extractedHeaders := strings.Split(strings.Split(strings.TrimSpace(authFields[1]), "=")[1], ";") + extractedSignedHeadersMap := make(map[string][]string) + for _, header := range extractedHeaders { + val, ok := r.Request.Header[http.CanonicalHeaderKey(header)] + if !ok { + // if not found continue, we will fail later + continue + } + extractedSignedHeadersMap[header] = val + } + return extractedSignedHeadersMap +} + // getCanonicalRequest generate a canonical request of style // // canonicalRequest = @@ -169,9 +179,9 @@ func (r *Signature) getCanonicalRequest() string { r.Request.Method, encodedPath, r.Request.URL.RawQuery, - r.getCanonicalHeaders(), - r.getSignedHeaders(), - r.Request.Header.Get("x-amz-content-sha256"), + r.getCanonicalHeaders(r.extractSignedHeaders()), + r.getSignedHeaders(r.extractSignedHeaders()), + r.Request.Header.Get(http.CanonicalHeaderKey("x-amz-content-sha256")), }, "\n") return canonicalRequest } @@ -214,11 +224,11 @@ func (r *Signature) getSignature(signingKey []byte, stringToSign string) string // returns true if matches, false other wise if error is not nil then it is always false func (r *Signature) DoesSignatureMatch(hashedPayload string) (bool, error) { // set new calulated payload - r.Request.Header.Set("x-amz-content-sha256", hashedPayload) + r.Request.Header.Set("X-Amz-Content-Sha256", hashedPayload) // Add date if not present var date string - if date = r.Request.Header.Get("x-amz-date"); date == "" { + if date = r.Request.Header.Get(http.CanonicalHeaderKey("x-amz-date")); date == "" { if date = r.Request.Header.Get("Date"); date == "" { return false, iodine.New(MissingDateHeader{}, nil) } diff --git a/pkg/server/api/bucket-handlers.go b/pkg/server/api/bucket-handlers.go index 59cd893c4..c3d21e13a 100644 --- a/pkg/server/api/bucket-handlers.go +++ b/pkg/server/api/bucket-handlers.go @@ -405,7 +405,8 @@ func (api Minio) HeadBucketHandler(w http.ResponseWriter, req *http.Request) { case nil: writeSuccessResponse(w, acceptsContentType) case donut.SignatureDoesNotMatch: - writeErrorResponse(w, req, SignatureDoesNotMatch, acceptsContentType, req.URL.Path) + error := getErrorCode(SignatureDoesNotMatch) + w.WriteHeader(error.HTTPStatusCode) case donut.BucketNotFound: error := getErrorCode(NoSuchBucket) w.WriteHeader(error.HTTPStatusCode) diff --git a/pkg/server/api/generic-handlers.go b/pkg/server/api/generic-handlers.go index d74e439ea..64578bc45 100644 --- a/pkg/server/api/generic-handlers.go +++ b/pkg/server/api/generic-handlers.go @@ -45,7 +45,7 @@ const ( ) func parseDate(req *http.Request) (time.Time, error) { - amzDate := req.Header.Get("x-amz-date") + amzDate := req.Header.Get(http.CanonicalHeaderKey("x-amz-date")) switch { case amzDate != "": if _, err := time.Parse(time.RFC1123, amzDate); err == nil { @@ -97,7 +97,7 @@ func (h timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { acceptsContentType := getContentType(r) // Verify if date headers are set, if not reject the request if r.Header.Get("Authorization") != "" { - if r.Header.Get("x-amz-date") == "" && r.Header.Get("Date") == "" { + if r.Header.Get(http.CanonicalHeaderKey("x-amz-date")) == "" && r.Header.Get("Date") == "" { // there is no way to knowing if this is a valid request, could be a attack reject such clients writeErrorResponse(w, r, RequestTimeTooSkewed, acceptsContentType, r.URL.Path) return