diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 08b977481..4dba722f2 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -1018,6 +1018,20 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h writeErrorResponse(ctx, w, apiErr, r.URL) return } + checksum, err := hash.GetContentChecksum(formValues) + if err != nil { + apiErr := errorCodes.ToAPIErr(ErrMalformedPOSTRequest) + apiErr.Description = fmt.Sprintf("%s (%v)", apiErr.Description, fmt.Errorf("Invalid checksum: %w", err)) + writeErrorResponse(ctx, w, apiErr, r.URL) + return + } + if checksum != nil && checksum.Type.Trailing() { + // Not officially supported in POST requests. + apiErr := errorCodes.ToAPIErr(ErrMalformedPOSTRequest) + apiErr.Description = fmt.Sprintf("%s (%v)", apiErr.Description, errors.New("Trailing checksums not available for POST operations")) + writeErrorResponse(ctx, w, apiErr, r.URL) + return + } formValues.Set("Bucket", bucket) if fileName != "" && strings.Contains(formValues.Get("Key"), "${filename}") { @@ -1073,6 +1087,13 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } + if checksum != nil && checksum.Valid() { + err = hashReader.AddChecksum(r, false) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } + } // Handle policy if it is set. if len(policyBytes) > 0 { @@ -1167,6 +1188,13 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } + if checksum != nil && checksum.Valid() { + err = hashReader.AddChecksum(r, true) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } + } pReader, err = pReader.WithEncryption(hashReader, &objectEncryptionKey) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) @@ -1226,6 +1254,11 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h return } + // Add checksum header. + if checksum != nil && checksum.Valid() { + hash.AddChecksumHeader(w, checksum.AsMap()) + } + // Decide what http response to send depending on success_action_status parameter switch successStatus { case "201": diff --git a/cmd/object-api-options.go b/cmd/object-api-options.go index a89a753b2..909713cf5 100644 --- a/cmd/object-api-options.go +++ b/cmd/object-api-options.go @@ -291,7 +291,7 @@ func putOpts(ctx context.Context, r *http.Request, bucket, object string, metada metadata = make(map[string]string) } - wantCRC, err := hash.GetContentChecksum(r) + wantCRC, err := hash.GetContentChecksum(r.Header) if err != nil { return opts, InvalidArgument{ Bucket: bucket, @@ -377,7 +377,7 @@ func completeMultipartOpts(ctx context.Context, r *http.Request, bucket, object } } } - opts.WantChecksum, err = hash.GetContentChecksum(r) + opts.WantChecksum, err = hash.GetContentChecksum(r.Header) if err != nil { return opts, InvalidArgument{ Bucket: bucket, diff --git a/internal/hash/checksum.go b/internal/hash/checksum.go index 86b4b78d0..10f596e6b 100644 --- a/internal/hash/checksum.go +++ b/internal/hash/checksum.go @@ -341,7 +341,7 @@ func (c *Checksum) AsMap() map[string]string { // TransferChecksumHeader will transfer any checksum value that has been checked. // If checksum was trailing, they must have been added to r.Trailer. func TransferChecksumHeader(w http.ResponseWriter, r *http.Request) { - c, err := GetContentChecksum(r) + c, err := GetContentChecksum(r.Header) if err != nil || c == nil { return } @@ -375,8 +375,8 @@ func AddChecksumHeader(w http.ResponseWriter, c map[string]string) { // GetContentChecksum returns content checksum. // Returns ErrInvalidChecksum if so. // Returns nil, nil if no checksum. -func GetContentChecksum(r *http.Request) (*Checksum, error) { - if trailing := r.Header.Values(xhttp.AmzTrailer); len(trailing) > 0 { +func GetContentChecksum(h http.Header) (*Checksum, error) { + if trailing := h.Values(xhttp.AmzTrailer); len(trailing) > 0 { var res *Checksum for _, header := range trailing { var duplicates bool @@ -402,7 +402,7 @@ func GetContentChecksum(r *http.Request) (*Checksum, error) { return res, nil } } - t, s := getContentChecksum(r) + t, s := getContentChecksum(h) if t == ChecksumNone { if s == "" { return nil, nil @@ -418,21 +418,21 @@ func GetContentChecksum(r *http.Request) (*Checksum, error) { // getContentChecksum returns content checksum type and value. // Returns ChecksumInvalid if so. -func getContentChecksum(r *http.Request) (t ChecksumType, s string) { +func getContentChecksum(h http.Header) (t ChecksumType, s string) { t = ChecksumNone - alg := r.Header.Get(xhttp.AmzChecksumAlgo) + alg := h.Get(xhttp.AmzChecksumAlgo) if alg != "" { t |= NewChecksumType(alg) if t.IsSet() { hdr := t.Key() - if s = r.Header.Get(hdr); s == "" { + if s = h.Get(hdr); s == "" { return ChecksumNone, "" } } return t, s } checkType := func(c ChecksumType) { - if got := r.Header.Get(c.Key()); got != "" { + if got := h.Get(c.Key()); got != "" { // If already set, invalid if t != ChecksumNone { t = ChecksumInvalid diff --git a/internal/hash/reader.go b/internal/hash/reader.go index b4c987ee9..1dff95a62 100644 --- a/internal/hash/reader.go +++ b/internal/hash/reader.go @@ -170,7 +170,7 @@ func (r *Reader) SetExpectedMax(expectedMax int64) { // https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html // Returns ErrInvalidChecksum if a problem with the checksum is found. func (r *Reader) AddChecksum(req *http.Request, ignoreValue bool) error { - cs, err := GetContentChecksum(req) + cs, err := GetContentChecksum(req.Header) if err != nil { return ErrInvalidChecksum } @@ -181,6 +181,16 @@ func (r *Reader) AddChecksum(req *http.Request, ignoreValue bool) error { if cs.Type.Trailing() { r.trailer = req.Trailer } + return r.AddNonTrailingChecksum(cs, ignoreValue) +} + +// AddNonTrailingChecksum will add a checksum to the reader. +// The checksum cannot be trailing. +func (r *Reader) AddNonTrailingChecksum(cs *Checksum, ignoreValue bool) error { + if cs == nil { + return nil + } + r.contentHash = *cs if ignoreValue { // Do not validate, but allow for transfer return nil