From 8b3cb3a0de02a31f720f9c738561fc45d62c2c1e Mon Sep 17 00:00:00 2001 From: Anis Elleuch Date: Thu, 28 Jul 2016 02:51:55 +0200 Subject: [PATCH] POST Policy, multiple fixes: AccessDenied with unmet conditions, ${filename} in Key, missing filename in multipart (#2304) * Unsatisfied conditions will return AccessDenied instead of MissingFields * Require form-field `file` in POST policy and make `filename` an optional attribute * S3 feature: Replace in Key by filename attribute passed in multipart --- bucket-handlers.go | 8 +++++++- handler-utils.go | 15 +++++++++------ signature-v4-postpolicyform.go | 12 ++++++------ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/bucket-handlers.go b/bucket-handlers.go index 457dfd30e..650612b80 100644 --- a/bucket-handlers.go +++ b/bucket-handlers.go @@ -347,7 +347,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h return } - fileBody, formValues, err := extractHTTPFormValues(reader) + fileBody, fileName, formValues, err := extractHTTPFormValues(reader) if err != nil { errorIf(err, "Unable to parse form values.") writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) @@ -357,6 +357,12 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h formValues["Bucket"] = bucket object := formValues["Key"] + if fileName != "" && strings.Contains(object, "${filename}") { + // S3 feature to replace ${filename} found in Key form field + // by the filename attribute passed in multipart + object = strings.Replace(object, "${filename}", fileName, -1) + } + // Verify policy signature. apiErr := doesPolicySignatureMatch(formValues) if apiErr != ErrNone { diff --git a/handler-utils.go b/handler-utils.go index 120afd50d..987a29520 100644 --- a/handler-utils.go +++ b/handler-utils.go @@ -99,29 +99,32 @@ func extractMetadataFromHeader(header http.Header) map[string]string { return metadata } -func extractHTTPFormValues(reader *multipart.Reader) (io.Reader, map[string]string, error) { +func extractHTTPFormValues(reader *multipart.Reader) (io.Reader, string, map[string]string, error) { /// HTML Form values formValues := make(map[string]string) filePart := new(bytes.Buffer) + fileName := "" var err error for err == nil { var part *multipart.Part part, err = reader.NextPart() if part != nil { - if part.FileName() == "" { + canonicalFormName := http.CanonicalHeaderKey(part.FormName()) + if canonicalFormName != "File" { var buffer []byte buffer, err = ioutil.ReadAll(part) if err != nil { - return nil, nil, err + return nil, "", nil, err } - formValues[http.CanonicalHeaderKey(part.FormName())] = string(buffer) + formValues[canonicalFormName] = string(buffer) } else { if _, err = io.Copy(filePart, part); err != nil { - return nil, nil, err + return nil, "", nil, err } + fileName = part.FileName() } } } - return filePart, formValues, nil + return filePart, fileName, formValues, nil } diff --git a/signature-v4-postpolicyform.go b/signature-v4-postpolicyform.go index e1f26289c..784b45a1e 100644 --- a/signature-v4-postpolicyform.go +++ b/signature-v4-postpolicyform.go @@ -170,32 +170,32 @@ func checkPostPolicy(formValues map[string]string) APIErrorCode { } if postPolicyForm.Conditions.Policies["$bucket"].Operator == "eq" { if formValues["Bucket"] != postPolicyForm.Conditions.Policies["$bucket"].Value { - return ErrMissingFields + return ErrAccessDenied } } if postPolicyForm.Conditions.Policies["$x-amz-date"].Operator == "eq" { if formValues["X-Amz-Date"] != postPolicyForm.Conditions.Policies["$x-amz-date"].Value { - return ErrMissingFields + return ErrAccessDenied } } if postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "starts-with" { if !strings.HasPrefix(formValues["Content-Type"], postPolicyForm.Conditions.Policies["$Content-Type"].Value) { - return ErrMissingFields + return ErrAccessDenied } } if postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "eq" { if formValues["Content-Type"] != postPolicyForm.Conditions.Policies["$Content-Type"].Value { - return ErrMissingFields + return ErrAccessDenied } } if postPolicyForm.Conditions.Policies["$key"].Operator == "starts-with" { if !strings.HasPrefix(formValues["Key"], postPolicyForm.Conditions.Policies["$key"].Value) { - return ErrMissingFields + return ErrAccessDenied } } if postPolicyForm.Conditions.Policies["$key"].Operator == "eq" { if formValues["Key"] != postPolicyForm.Conditions.Policies["$key"].Value { - return ErrMissingFields + return ErrAccessDenied } } return ErrNone