From ebe395788b21adf1bbbb52eadafe7156f11cea33 Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Wed, 7 Dec 2022 23:56:07 +0100 Subject: [PATCH] feat: Encrypt s3zip file index (#16179) --- cmd/encryption-v1.go | 38 ++++++++++++++++++++++++++++++++ cmd/object-api-datatypes.go | 19 ++++++++++++---- cmd/object-multipart-handlers.go | 5 +++++ cmd/s3-zip-handlers.go | 25 +++++++++++++++++---- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/cmd/encryption-v1.go b/cmd/encryption-v1.go index 3d3f672f3..8ab4f91b1 100644 --- a/cmd/encryption-v1.go +++ b/cmd/encryption-v1.go @@ -1083,6 +1083,44 @@ func (o *ObjectInfo) metadataDecrypter() objectMetaDecryptFn { } } +// metadataEncryptFn provides an encryption function for metadata. +// Will return nil, nil if unencrypted. +func (o *ObjectInfo) metadataEncryptFn(headers http.Header) (objectMetaEncryptFn, error) { + kind, _ := crypto.IsEncrypted(o.UserDefined) + switch kind { + case crypto.SSEC: + if crypto.SSECopy.IsRequested(headers) { + key, err := crypto.SSECopy.ParseHTTP(headers) + if err != nil { + return nil, err + } + objectEncryptionKey, err := decryptObjectMeta(key[:], o.Bucket, o.Name, o.UserDefined) + if err != nil { + return nil, err + } + if len(objectEncryptionKey) == 32 { + var key crypto.ObjectKey + copy(key[:], objectEncryptionKey) + return metadataEncrypter(key), nil + } + return nil, errors.New("metadataEncryptFn: unexpected key size") + } + case crypto.S3, crypto.S3KMS: + objectEncryptionKey, err := decryptObjectMeta(nil, o.Bucket, o.Name, o.UserDefined) + if err != nil { + return nil, err + } + if len(objectEncryptionKey) == 32 { + var key crypto.ObjectKey + copy(key[:], objectEncryptionKey) + return metadataEncrypter(key), nil + } + return nil, errors.New("metadataEncryptFn: unexpected key size") + } + + return nil, nil +} + // decryptChecksums will attempt to decode checksums and return it/them if set. func (o *ObjectInfo) decryptChecksums() map[string]string { data := o.Checksum diff --git a/cmd/object-api-datatypes.go b/cmd/object-api-datatypes.go index e1be71a78..ca5101ef4 100644 --- a/cmd/object-api-datatypes.go +++ b/cmd/object-api-datatypes.go @@ -26,6 +26,7 @@ import ( "github.com/minio/madmin-go/v2" "github.com/minio/minio/internal/bucket/replication" "github.com/minio/minio/internal/hash" + "github.com/minio/minio/internal/logger" ) // BackendType - represents different backend types. @@ -181,8 +182,9 @@ type ObjectInfo struct { Checksum []byte } -// ArchiveInfo returns any saved zip archive meta information -func (o ObjectInfo) ArchiveInfo() []byte { +// ArchiveInfo returns any saved zip archive meta information. +// It will be decrypted if needed. +func (o *ObjectInfo) ArchiveInfo() []byte { if len(o.UserDefined) == 0 { return nil } @@ -190,11 +192,20 @@ func (o ObjectInfo) ArchiveInfo() []byte { if !ok { return nil } - return []byte(z) + data := []byte(z) + if v, ok := o.UserDefined[archiveTypeMetadataKey]; ok && v == archiveTypeEnc { + decrypted, err := o.metadataDecrypter()(archiveTypeEnc, data) + if err != nil { + logger.LogIf(GlobalContext, err) + return nil + } + data = decrypted + } + return data } // Clone - Returns a cloned copy of current objectInfo -func (o ObjectInfo) Clone() (cinfo ObjectInfo) { +func (o *ObjectInfo) Clone() (cinfo ObjectInfo) { cinfo = ObjectInfo{ Bucket: o.Bucket, Name: o.Name, diff --git a/cmd/object-multipart-handlers.go b/cmd/object-multipart-handlers.go index 5c499b6b2..773c8a2c9 100644 --- a/cmd/object-multipart-handlers.go +++ b/cmd/object-multipart-handlers.go @@ -676,6 +676,11 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite } } + opts.EncryptFn, err = objInfo.metadataEncryptFn(r.Header) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } if r.Header.Get(xMinIOExtract) == "true" && strings.HasSuffix(object, archiveExt) { opts := ObjectOptions{VersionID: objInfo.VersionID, MTime: objInfo.ModTime} if _, err := updateObjectMetadataWithZipInfo(ctx, objectAPI, bucket, object, opts); err != nil { diff --git a/cmd/s3-zip-handlers.go b/cmd/s3-zip-handlers.go index ef6baf00f..55f003d73 100644 --- a/cmd/s3-zip-handlers.go +++ b/cmd/s3-zip-handlers.go @@ -38,6 +38,7 @@ import ( const ( archiveType = "zip" + archiveTypeEnc = "zip-enc" archiveExt = "." + archiveType // ".zip" archiveSeparator = "/" archivePattern = archiveExt + archiveSeparator // ".zip/" @@ -152,6 +153,12 @@ func (api objectAPIHandlers) getObjectInArchiveFileHandler(ctx context.Context, zipInfo := zipObjInfo.ArchiveInfo() if len(zipInfo) == 0 { + opts.EncryptFn, err = zipObjInfo.metadataEncryptFn(r.Header) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } + zipInfo, err = updateObjectMetadataWithZipInfo(ctx, objectAPI, bucket, zipPath, opts) } if err != nil { @@ -451,6 +458,11 @@ func (api objectAPIHandlers) headObjectInArchiveFileHandler(ctx context.Context, zipInfo := zipObjInfo.ArchiveInfo() if len(zipInfo) == 0 { + opts.EncryptFn, err = zipObjInfo.metadataEncryptFn(r.Header) + if err != nil { + writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) + return + } zipInfo, err = updateObjectMetadataWithZipInfo(ctx, objectAPI, bucket, zipPath, opts) } if err != nil { @@ -491,7 +503,8 @@ func (api objectAPIHandlers) headObjectInArchiveFileHandler(ctx context.Context, w.WriteHeader(http.StatusOK) } -// Update the passed zip object metadata with the zip contents info, file name, modtime, size, etc.. +// Update the passed zip object metadata with the zip contents info, file name, modtime, size, etc. +// The returned zip index will de decrypted. func updateObjectMetadataWithZipInfo(ctx context.Context, objectAPI ObjectLayer, bucket, object string, opts ObjectOptions) ([]byte, error) { files, srcInfo, err := getFilesListFromZIPObject(ctx, objectAPI, bucket, object, opts) if err != nil { @@ -502,14 +515,18 @@ func updateObjectMetadataWithZipInfo(ctx context.Context, objectAPI ObjectLayer, if err != nil { return nil, err } - - srcInfo.UserDefined[archiveTypeMetadataKey] = archiveType + at := archiveType zipInfoStr := string(zipInfo) + if opts.EncryptFn != nil { + at = archiveTypeEnc + zipInfoStr = string(opts.EncryptFn(archiveTypeEnc, zipInfo)) + } + srcInfo.UserDefined[archiveTypeMetadataKey] = at popts := ObjectOptions{ MTime: srcInfo.ModTime, VersionID: srcInfo.VersionID, EvalMetadataFn: func(oi *ObjectInfo) error { - oi.UserDefined[archiveTypeMetadataKey] = archiveType + oi.UserDefined[archiveTypeMetadataKey] = at oi.UserDefined[archiveInfoMetadataKey] = zipInfoStr return nil },