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
master
Anis Elleuch 8 years ago committed by Harshavardhana
parent 2f7358a8a6
commit 8b3cb3a0de
  1. 8
      bucket-handlers.go
  2. 15
      handler-utils.go
  3. 12
      signature-v4-postpolicyform.go

@ -347,7 +347,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
return return
} }
fileBody, formValues, err := extractHTTPFormValues(reader) fileBody, fileName, formValues, err := extractHTTPFormValues(reader)
if err != nil { if err != nil {
errorIf(err, "Unable to parse form values.") errorIf(err, "Unable to parse form values.")
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path) writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path)
@ -357,6 +357,12 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
formValues["Bucket"] = bucket formValues["Bucket"] = bucket
object := formValues["Key"] 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. // Verify policy signature.
apiErr := doesPolicySignatureMatch(formValues) apiErr := doesPolicySignatureMatch(formValues)
if apiErr != ErrNone { if apiErr != ErrNone {

@ -99,29 +99,32 @@ func extractMetadataFromHeader(header http.Header) map[string]string {
return metadata 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 /// HTML Form values
formValues := make(map[string]string) formValues := make(map[string]string)
filePart := new(bytes.Buffer) filePart := new(bytes.Buffer)
fileName := ""
var err error var err error
for err == nil { for err == nil {
var part *multipart.Part var part *multipart.Part
part, err = reader.NextPart() part, err = reader.NextPart()
if part != nil { if part != nil {
if part.FileName() == "" { canonicalFormName := http.CanonicalHeaderKey(part.FormName())
if canonicalFormName != "File" {
var buffer []byte var buffer []byte
buffer, err = ioutil.ReadAll(part) buffer, err = ioutil.ReadAll(part)
if err != nil { if err != nil {
return nil, nil, err return nil, "", nil, err
} }
formValues[http.CanonicalHeaderKey(part.FormName())] = string(buffer) formValues[canonicalFormName] = string(buffer)
} else { } else {
if _, err = io.Copy(filePart, part); err != nil { 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
} }

@ -170,32 +170,32 @@ func checkPostPolicy(formValues map[string]string) APIErrorCode {
} }
if postPolicyForm.Conditions.Policies["$bucket"].Operator == "eq" { if postPolicyForm.Conditions.Policies["$bucket"].Operator == "eq" {
if formValues["Bucket"] != postPolicyForm.Conditions.Policies["$bucket"].Value { if formValues["Bucket"] != postPolicyForm.Conditions.Policies["$bucket"].Value {
return ErrMissingFields return ErrAccessDenied
} }
} }
if postPolicyForm.Conditions.Policies["$x-amz-date"].Operator == "eq" { if postPolicyForm.Conditions.Policies["$x-amz-date"].Operator == "eq" {
if formValues["X-Amz-Date"] != postPolicyForm.Conditions.Policies["$x-amz-date"].Value { 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 postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "starts-with" {
if !strings.HasPrefix(formValues["Content-Type"], postPolicyForm.Conditions.Policies["$Content-Type"].Value) { 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 postPolicyForm.Conditions.Policies["$Content-Type"].Operator == "eq" {
if formValues["Content-Type"] != postPolicyForm.Conditions.Policies["$Content-Type"].Value { if formValues["Content-Type"] != postPolicyForm.Conditions.Policies["$Content-Type"].Value {
return ErrMissingFields return ErrAccessDenied
} }
} }
if postPolicyForm.Conditions.Policies["$key"].Operator == "starts-with" { if postPolicyForm.Conditions.Policies["$key"].Operator == "starts-with" {
if !strings.HasPrefix(formValues["Key"], postPolicyForm.Conditions.Policies["$key"].Value) { if !strings.HasPrefix(formValues["Key"], postPolicyForm.Conditions.Policies["$key"].Value) {
return ErrMissingFields return ErrAccessDenied
} }
} }
if postPolicyForm.Conditions.Policies["$key"].Operator == "eq" { if postPolicyForm.Conditions.Policies["$key"].Operator == "eq" {
if formValues["Key"] != postPolicyForm.Conditions.Policies["$key"].Value { if formValues["Key"] != postPolicyForm.Conditions.Policies["$key"].Value {
return ErrMissingFields return ErrAccessDenied
} }
} }
return ErrNone return ErrNone

Loading…
Cancel
Save