diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 58a577908..2a7c5df17 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -566,6 +566,29 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h return } + if objectAPI.IsEncryptionSupported() { + if hasSSECustomerHeader(formValues) && !hasSuffix(object, slashSeparator) { // handle SSE-C requests + var reader io.Reader + var key []byte + key, err = ParseSSECustomerHeader(formValues) + if err != nil { + writeErrorResponse(w, toAPIErrorCode(err), r.URL) + return + } + reader, err = newEncryptReader(hashReader, key, metadata) + if err != nil { + writeErrorResponse(w, toAPIErrorCode(err), r.URL) + return + } + info := ObjectInfo{Size: fileSize} + hashReader, err = hash.NewReader(reader, info.EncryptedSize(), "", "") // do not try to verify encrypted content + if err != nil { + writeErrorResponse(w, toAPIErrorCode(err), r.URL) + return + } + } + } + objInfo, err := objectAPI.PutObject(bucket, object, hashReader, metadata) if err != nil { writeErrorResponse(w, toAPIErrorCode(err), r.URL) diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 38f291222..f5708eb0b 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -144,15 +144,15 @@ const ( // hash function. const SSESealAlgorithmDareSha256 = "DARE-SHA256" -// IsSSECustomerRequest returns true if the given HTTP header +// hasSSECustomerHeader returns true if the given HTTP header // contains server-side-encryption with customer provided key fields. -func IsSSECustomerRequest(header http.Header) bool { +func hasSSECustomerHeader(header http.Header) bool { return header.Get(SSECustomerAlgorithm) != "" || header.Get(SSECustomerKey) != "" || header.Get(SSECustomerKeyMD5) != "" } -// IsSSECopyCustomerRequest returns true if the given HTTP header +// hasSSECopyCustomerHeader returns true if the given HTTP header // contains copy source server-side-encryption with customer provided key fields. -func IsSSECopyCustomerRequest(header http.Header) bool { +func hasSSECopyCustomerHeader(header http.Header) bool { return header.Get(SSECopyCustomerAlgorithm) != "" || header.Get(SSECopyCustomerKey) != "" || header.Get(SSECopyCustomerKeyMD5) != "" } @@ -201,6 +201,12 @@ func ParseSSECopyCustomerRequest(r *http.Request) (key []byte, err error) { // ParseSSECustomerRequest parses the SSE-C header fields of the provided request. // It returns the client provided key on success. func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) { + return ParseSSECustomerHeader(r.Header) +} + +// ParseSSECustomerHeader parses the SSE-C header fields and returns +// the client provided key on success. +func ParseSSECustomerHeader(header http.Header) (key []byte, err error) { if !globalIsSSL { // minio only supports HTTP or HTTPS requests not both at the same time // we cannot use r.TLS == nil here because Go's http implementation reflects on // the net.Conn and sets the TLS field of http.Request only if it's an tls.Conn. @@ -208,7 +214,6 @@ func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) { // will always fail -> r.TLS is always nil even for TLS requests. return nil, errInsecureSSERequest } - header := r.Header if algorithm := header.Get(SSECustomerAlgorithm); algorithm != SSECustomerAlgorithmAES256 { return nil, errInvalidSSEAlgorithm } @@ -778,10 +783,10 @@ func DecryptCopyObjectInfo(info *ObjectInfo, headers http.Header) (apiErr APIErr if info.IsDir { return ErrNone, false } - if apiErr, encrypted = ErrNone, info.IsEncrypted(); !encrypted && IsSSECopyCustomerRequest(headers) { + if apiErr, encrypted = ErrNone, info.IsEncrypted(); !encrypted && hasSSECopyCustomerHeader(headers) { apiErr = ErrInvalidEncryptionParameters } else if encrypted { - if !IsSSECopyCustomerRequest(headers) { + if !hasSSECopyCustomerHeader(headers) { apiErr = ErrSSEEncryptedObject return } @@ -805,10 +810,10 @@ func DecryptObjectInfo(info *ObjectInfo, headers http.Header) (apiErr APIErrorCo if info.IsDir { return ErrNone, false } - if apiErr, encrypted = ErrNone, info.IsEncrypted(); !encrypted && IsSSECustomerRequest(headers) { + if apiErr, encrypted = ErrNone, info.IsEncrypted(); !encrypted && hasSSECustomerHeader(headers) { apiErr = ErrInvalidEncryptionParameters } else if encrypted { - if !IsSSECustomerRequest(headers) { + if !hasSSECustomerHeader(headers) { apiErr = ErrSSEEncryptedObject return } diff --git a/cmd/encryption-v1_test.go b/cmd/encryption-v1_test.go index 4c0669fc7..0cc2f74c6 100644 --- a/cmd/encryption-v1_test.go +++ b/cmd/encryption-v1_test.go @@ -22,7 +22,7 @@ import ( "testing" ) -var isSSECopyCustomerRequestTests = []struct { +var hasSSECopyCustomerHeaderTests = []struct { headers map[string]string sseRequest bool }{ @@ -36,18 +36,18 @@ var isSSECopyCustomerRequestTests = []struct { } func TestIsSSECopyCustomerRequest(t *testing.T) { - for i, test := range isSSECopyCustomerRequestTests { + for i, test := range hasSSECopyCustomerHeaderTests { headers := http.Header{} for k, v := range test.headers { headers.Set(k, v) } - if IsSSECopyCustomerRequest(headers) != test.sseRequest { - t.Errorf("Test %d: Expected IsSSECopyCustomerRequest to return %v", i, test.sseRequest) + if hasSSECopyCustomerHeader(headers) != test.sseRequest { + t.Errorf("Test %d: Expected hasSSECopyCustomerHeader to return %v", i, test.sseRequest) } } } -var isSSECustomerRequestTests = []struct { +var hasSSECustomerHeaderTests = []struct { headers map[string]string sseRequest bool }{ @@ -60,14 +60,14 @@ var isSSECustomerRequestTests = []struct { {headers: map[string]string{SSECustomerAlgorithm: "", SSECustomerKey: "", SSECustomerKeyMD5: ""}, sseRequest: false}, // 6 } -func TestIsSSECustomerRequest(t *testing.T) { - for i, test := range isSSECustomerRequestTests { +func TesthasSSECustomerHeader(t *testing.T) { + for i, test := range hasSSECustomerHeaderTests { headers := http.Header{} for k, v := range test.headers { headers.Set(k, v) } - if IsSSECustomerRequest(headers) != test.sseRequest { - t.Errorf("Test %d: Expected IsSSECustomerRequest to return %v", i, test.sseRequest) + if hasSSECustomerHeader(headers) != test.sseRequest { + t.Errorf("Test %d: Expected hasSSECustomerHeader to return %v", i, test.sseRequest) } } } diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index bde2f488a..358377d85 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -147,7 +147,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req var writer io.Writer writer = w if objectAPI.IsEncryptionSupported() { - if IsSSECustomerRequest(r.Header) { + if hasSSECustomerHeader(r.Header) { // Response writer should be limited early on for decryption upto required length, // additionally also skipping mod(offset)64KiB boundaries. writer = ioutil.LimitedWriter(writer, startOffset%(64*1024), length) @@ -397,8 +397,8 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re var encMetadata = make(map[string]string) if objectAPI.IsEncryptionSupported() { var oldKey, newKey []byte - sseCopyC := IsSSECopyCustomerRequest(r.Header) - sseC := IsSSECustomerRequest(r.Header) + sseCopyC := hasSSECopyCustomerHeader(r.Header) + sseC := hasSSECustomerHeader(r.Header) if sseC { newKey, err = ParseSSECustomerRequest(r) if err != nil { @@ -668,7 +668,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req } if objectAPI.IsEncryptionSupported() { - if IsSSECustomerRequest(r.Header) && !hasSuffix(object, slashSeparator) { // handle SSE-C requests + if hasSSECustomerHeader(r.Header) && !hasSuffix(object, slashSeparator) { // handle SSE-C requests reader, err = EncryptRequest(hashReader, r, metadata) if err != nil { writeErrorResponse(w, toAPIErrorCode(err), r.URL) @@ -691,7 +691,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req w.Header().Set("ETag", "\""+objInfo.ETag+"\"") if objectAPI.IsEncryptionSupported() { - if IsSSECustomerRequest(r.Header) { + if hasSSECustomerHeader(r.Header) { w.Header().Set(SSECustomerAlgorithm, r.Header.Get(SSECustomerAlgorithm)) w.Header().Set(SSECustomerKeyMD5, r.Header.Get(SSECustomerKeyMD5)) } @@ -748,7 +748,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r var encMetadata = map[string]string{} if objectAPI.IsEncryptionSupported() { - if IsSSECustomerRequest(r.Header) { + if hasSSECustomerHeader(r.Header) { key, err := ParseSSECustomerRequest(r) if err != nil { writeErrorResponse(w, toAPIErrorCode(err), r.URL) @@ -903,7 +903,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt writeErrorResponse(w, toAPIErrorCode(err), r.URL) return } - sseCopyC := IsSSECopyCustomerRequest(r.Header) + sseCopyC := hasSSECopyCustomerHeader(r.Header) if sseCopyC { // Response writer should be limited early on for decryption upto required length, // additionally also skipping mod(offset)64KiB boundaries. @@ -916,7 +916,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt } } if li.IsEncrypted() { - if !IsSSECustomerRequest(r.Header) { + if !hasSSECustomerHeader(r.Header) { writeErrorResponse(w, ErrSSEMultipartEncrypted, r.URL) return } @@ -1105,7 +1105,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http return } if li.IsEncrypted() { - if !IsSSECustomerRequest(r.Header) { + if !hasSSECustomerHeader(r.Header) { writeErrorResponse(w, ErrSSEMultipartEncrypted, r.URL) return }