diff --git a/api/handler/copy.go b/api/handler/copy.go index fea9ccdd1..5ebf36eb5 100644 --- a/api/handler/copy.go +++ b/api/handler/copy.go @@ -71,6 +71,15 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { return } + if err = h.checkBucketOwner(r, srcBucket, r.Header.Get(api.AmzSourceExpectedBucketOwner)); err != nil { + h.logAndSendError(w, "source expected owner doesn't match", reqInfo, err) + return + } + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + if inf, err = h.obj.GetObjectInfo(r.Context(), srcBucket, srcObject); err != nil { h.logAndSendError(w, "could not find object", reqInfo, err) return diff --git a/api/handler/delete.go b/api/handler/delete.go index 71c61a0bd..742a7bbbd 100644 --- a/api/handler/delete.go +++ b/api/handler/delete.go @@ -44,6 +44,11 @@ type DeleteObjectsResponse struct { func (h *handler) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) { reqInfo := api.GetReqInfo(r.Context()) + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + if err := h.obj.DeleteObject(r.Context(), reqInfo.BucketName, reqInfo.ObjectName); err != nil { h.log.Error("could not delete object", zap.String("request_id", reqInfo.RequestID), @@ -100,6 +105,11 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re DeletedObjects: make([]ObjectIdentifier, 0, len(toRemove)), } + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + if errs := h.obj.DeleteObjects(r.Context(), reqInfo.BucketName, toRemove); errs != nil && !requested.Quiet { additional := []zap.Field{ zap.Strings("objects_name", toRemove), @@ -135,6 +145,10 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re func (h *handler) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) { reqInfo := api.GetReqInfo(r.Context()) + if err := h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } if err := h.obj.DeleteBucket(r.Context(), &layer.DeleteBucketParams{Name: reqInfo.BucketName}); err != nil { h.logAndSendError(w, "couldn't delete bucket", reqInfo, err) } diff --git a/api/handler/get.go b/api/handler/get.go index 40a3ed04d..2233f28f9 100644 --- a/api/handler/get.go +++ b/api/handler/get.go @@ -93,6 +93,11 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) { return } + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + if inf, err = h.obj.GetObjectInfo(r.Context(), reqInfo.BucketName, reqInfo.ObjectName); err != nil { h.logAndSendError(w, "could not find object", reqInfo, err) return diff --git a/api/handler/head.go b/api/handler/head.go index 3db3db500..128a2836d 100644 --- a/api/handler/head.go +++ b/api/handler/head.go @@ -31,6 +31,11 @@ func (h *handler) HeadObjectHandler(w http.ResponseWriter, r *http.Request) { reqInfo = api.GetReqInfo(r.Context()) ) + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + if inf, err = h.obj.GetObjectInfo(r.Context(), reqInfo.BucketName, reqInfo.ObjectName); err != nil { h.logAndSendError(w, "could not fetch object info", reqInfo, err) return @@ -56,7 +61,11 @@ func (h *handler) HeadBucketHandler(w http.ResponseWriter, r *http.Request) { bktInfo, err := h.obj.GetBucketInfo(r.Context(), reqInfo.BucketName) if err != nil { - h.logAndSendError(w, "could not fetch object info", reqInfo, err) + h.logAndSendError(w, "could not get bucket info", reqInfo, err) + return + } + if err = checkOwner(bktInfo, r.Header.Get(api.AmzExpectedBucketOwner)); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) return } diff --git a/api/handler/object_list.go b/api/handler/object_list.go index 796a83e2a..ef8f0dd4e 100644 --- a/api/handler/object_list.go +++ b/api/handler/object_list.go @@ -21,6 +21,11 @@ func (h *handler) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) { return } + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + list, err := h.obj.ListObjectsV1(r.Context(), params) if err != nil { h.logAndSendError(w, "something went wrong", reqInfo, err) @@ -60,6 +65,11 @@ func (h *handler) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) { return } + if err = h.checkBucketOwner(r, reqInfo.BucketName); err != nil { + h.logAndSendError(w, "expected owner doesn't match", reqInfo, err) + return + } + list, err := h.obj.ListObjectsV2(r.Context(), params) if err != nil { h.logAndSendError(w, "something went wrong", reqInfo, err) diff --git a/api/handler/util.go b/api/handler/util.go index e87432b30..8ce597737 100644 --- a/api/handler/util.go +++ b/api/handler/util.go @@ -18,3 +18,23 @@ func (h *handler) logAndSendError(w http.ResponseWriter, logText string, reqInfo h.log.Error(logText, fields...) api.WriteErrorResponse(w, reqInfo, err) } + +func (h *handler) checkBucketOwner(r *http.Request, bucket string, header ...string) error { + var expected string + if len(header) == 0 { + expected = r.Header.Get(api.AmzExpectedBucketOwner) + } else { + expected = header[0] + } + + if len(expected) == 0 { + return nil + } + + bktInfo, err := h.obj.GetBucketInfo(r.Context(), bucket) + if err != nil { + return err + } + + return checkOwner(bktInfo, expected) +} diff --git a/api/headers.go b/api/headers.go index c75f71522..0eaef4fc9 100644 --- a/api/headers.go +++ b/api/headers.go @@ -30,15 +30,16 @@ const ( IfMatch = "If-Match" IfNoneMatch = "If-None-Match" - AmzCopyIfModifiedSince = "X-Amz-Copy-Source-If-Modified-Since" - AmzCopyIfUnmodifiedSince = "X-Amz-Copy-Source-If-Unmodified-Since" - AmzCopyIfMatch = "X-Amz-Copy-Source-If-Match" - AmzCopyIfNoneMatch = "X-Amz-Copy-Source-If-None-Match" - AmzACL = "X-Amz-Acl" - AmzGrantFullControl = "X-Amz-Grant-Full-Control" - AmzGrantRead = "X-Amz-Grant-Read" - AmzGrantWrite = "X-Amz-Grant-Write" - AmzExpectedBucketOwner = "X-Amz-Expected-Bucket-Owner" + AmzCopyIfModifiedSince = "X-Amz-Copy-Source-If-Modified-Since" + AmzCopyIfUnmodifiedSince = "X-Amz-Copy-Source-If-Unmodified-Since" + AmzCopyIfMatch = "X-Amz-Copy-Source-If-Match" + AmzCopyIfNoneMatch = "X-Amz-Copy-Source-If-None-Match" + AmzACL = "X-Amz-Acl" + AmzGrantFullControl = "X-Amz-Grant-Full-Control" + AmzGrantRead = "X-Amz-Grant-Read" + AmzGrantWrite = "X-Amz-Grant-Write" + AmzExpectedBucketOwner = "X-Amz-Expected-Bucket-Owner" + AmzSourceExpectedBucketOwner = "X-Amz-Source-Expected-Bucket-Owner" ContainerID = "X-Container-Id" )