From 4661f6597555a524f98a9d35d7231bc39a5dd230 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Sep 2021 09:02:02 +0300 Subject: [PATCH] [#645] client/cache: Check response public key in all client operations There is a need to check if public key in the RPC response matches the public key of the related storage node declared in network map. Define `ErrWrongPublicKey` error. Implement RPC response handler's constructor `AssertKeyResponseCallback` which checks public key. Construct handler and pass it to client's option `WithResponseInfoHandler`. Signed-off-by: Leonard Lyubich --- go.mod | 2 +- go.sum | Bin 98216 -> 98276 bytes pkg/core/client/errors.go | 9 ++++++++ pkg/core/client/util.go | 14 +++++++++++++ pkg/network/cache/client.go | 4 +++- pkg/services/object/get/v2/util.go | 28 ++++++++++++++++++++----- pkg/services/object/internal/key.go | 19 +++++++++++++++++ pkg/services/object/put/v2/streamer.go | 8 +++++++ pkg/services/object/search/v2/util.go | 14 ++++++++++--- 9 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 pkg/core/client/errors.go create mode 100644 pkg/services/object/internal/key.go diff --git a/go.mod b/go.mod index b59a74512..829137aa2 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/multiformats/go-multiaddr v0.4.0 github.com/nspcc-dev/hrw v1.0.9 github.com/nspcc-dev/neo-go v0.97.1 - github.com/nspcc-dev/neofs-api-go v1.29.0 + github.com/nspcc-dev/neofs-api-go v1.29.1-0.20210929081312-010b1b011827 github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210520210714-9dee13f0d556 github.com/nspcc-dev/tzhash v1.4.0 github.com/panjf2000/ants/v2 v2.4.0 diff --git a/go.sum b/go.sum index 496575257a53a20120db1eb07d0b5e267d45231d..9ef86245dfd09d714de3a62bba701c3bd0b97475 100644 GIT binary patch delta 130 zcmZ4SpY_Rq)(tt+eGPRD^o$IQ3=J%eEDbCSjSY=-4GayE43i8D4K0k!6*3I1+?)bj z15C30U9%%{D@>B}Q$q9&JhDAKg515dbMs1*jPt@v(u$LeOk5{Fn5L;{sYk%f&Bv!d Gd=3C0I47zA delta 71 zcmaFzpLNB5)(tt+4Ga`A46Q;kjLQSfyv)oZO(Mec&78FZ{Pm0VLqY?x_5C8N@=eT5 bE%QoT0>iQ`3?@IEron8fXRw)h#>3|To&Xtp diff --git a/pkg/core/client/errors.go b/pkg/core/client/errors.go new file mode 100644 index 000000000..a2aac5e55 --- /dev/null +++ b/pkg/core/client/errors.go @@ -0,0 +1,9 @@ +package client + +import ( + "errors" +) + +// ErrWrongPublicKey is returned when the client's response is signed with a key different +// from the one declared in the network map. +var ErrWrongPublicKey = errors.New("public key is different from the key in the network map") diff --git a/pkg/core/client/util.go b/pkg/core/client/util.go index c19b0c36c..3fb4484da 100644 --- a/pkg/core/client/util.go +++ b/pkg/core/client/util.go @@ -1,8 +1,10 @@ package client import ( + "bytes" "fmt" + "github.com/nspcc-dev/neofs-api-go/pkg/client" "github.com/nspcc-dev/neofs-node/pkg/network" ) @@ -40,3 +42,15 @@ func NodeInfoFromNetmapElement(dst *NodeInfo, info interface { }) { nodeInfoFromKeyAddr(dst, info.PublicKey(), info.Addresses()) } + +// AssertKeyResponseCallback returns client response callback which checks if the response was signed by expected key. +// Returns ErrWrongPublicKey in case of key mismatch. +func AssertKeyResponseCallback(expectedKey []byte) func(client.ResponseMetaInfo) error { + return func(info client.ResponseMetaInfo) error { + if !bytes.Equal(info.ResponderKey(), expectedKey) { + return ErrWrongPublicKey + } + + return nil + } +} diff --git a/pkg/network/cache/client.go b/pkg/network/cache/client.go index 76190fef6..738b4fcd9 100644 --- a/pkg/network/cache/client.go +++ b/pkg/network/cache/client.go @@ -62,7 +62,9 @@ func (c *ClientCache) Get(info clientcore.NodeInfo) (client.Client, error) { return cli, nil } - cli := newMultiClient(netAddr, c.opts) + cli := newMultiClient(netAddr, append(c.opts, + client.WithResponseInfoHandler(clientcore.AssertKeyResponseCallback(info.PublicKey())), + )) c.clients[cacheKey] = cli diff --git a/pkg/services/object/get/v2/util.go b/pkg/services/object/get/v2/util.go index 6e15430e3..bc8ed26ba 100644 --- a/pkg/services/object/get/v2/util.go +++ b/pkg/services/object/get/v2/util.go @@ -23,6 +23,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/network" objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object" getsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/get" + "github.com/nspcc-dev/neofs-node/pkg/services/object/internal" "github.com/nspcc-dev/neofs-node/pkg/services/object/util" "github.com/nspcc-dev/tzhash/tz" ) @@ -55,7 +56,7 @@ func (s *Service) toPrm(req *objectV2.GetRequest, stream objectSvc.GetObjectStre if !commonPrm.LocalOnly() { var onceResign sync.Once - p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) (*objectSDK.Object, error) { + p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) (*objectSDK.Object, error) { var err error // once compose and resign forwarding request @@ -106,6 +107,11 @@ func (s *Service) toPrm(req *objectV2.GetRequest, stream objectSvc.GetObjectStre return nil, fmt.Errorf("reading the response failed: %w", err) } + // verify response key + if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("response verification failed: %w", err) @@ -177,7 +183,7 @@ func (s *Service) toRangePrm(req *objectV2.GetRangeRequest, stream objectSvc.Get if !commonPrm.LocalOnly() { var onceResign sync.Once - p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) (*objectSDK.Object, error) { + p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) (*objectSDK.Object, error) { var err error // once compose and resign forwarding request @@ -221,6 +227,11 @@ func (s *Service) toRangePrm(req *objectV2.GetRangeRequest, stream objectSvc.Get return nil, fmt.Errorf("reading the response failed: %w", err) } + // verify response key + if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("could not verify %T: %w", resp, err) @@ -340,7 +351,7 @@ func (s *Service) toHeadPrm(ctx context.Context, req *objectV2.HeadRequest, resp if !commonPrm.LocalOnly() { var onceResign sync.Once - p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) (*objectSDK.Object, error) { + p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) (*objectSDK.Object, error) { var err error // once compose and resign forwarding request @@ -369,6 +380,11 @@ func (s *Service) toHeadPrm(ctx context.Context, req *objectV2.HeadRequest, resp return nil, fmt.Errorf("sending the request failed: %w", err) } + // verify response key + if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("response verification failed: %w", err) @@ -508,11 +524,13 @@ func toShortObjectHeader(hdr *object.Object) objectV2.GetHeaderPart { return sh } -func groupAddressRequestForwarder(f func(network.Address, client.Client) (*objectSDK.Object, error)) getsvc.RequestForwarder { +func groupAddressRequestForwarder(f func(network.Address, client.Client, []byte) (*objectSDK.Object, error)) getsvc.RequestForwarder { return func(info client.NodeInfo, c client.Client) (*objectSDK.Object, error) { var ( firstErr error res *objectSDK.Object + + key = info.PublicKey() ) info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) { @@ -528,7 +546,7 @@ func groupAddressRequestForwarder(f func(network.Address, client.Client) (*objec // would be nice to log otherwise }() - res, err = f(addr, c) + res, err = f(addr, c, key) return }) diff --git a/pkg/services/object/internal/key.go b/pkg/services/object/internal/key.go new file mode 100644 index 000000000..9e5211973 --- /dev/null +++ b/pkg/services/object/internal/key.go @@ -0,0 +1,19 @@ +package internal + +import ( + "bytes" + + "github.com/nspcc-dev/neofs-api-go/v2/session" + "github.com/nspcc-dev/neofs-node/pkg/core/client" +) + +// VerifyResponseKeyV2 checks if response is signed with expected key. Returns client.ErrWrongPublicKey if not. +func VerifyResponseKeyV2(expectedKey []byte, resp interface { + GetVerificationHeader() *session.ResponseVerificationHeader +}) error { + if !bytes.Equal(resp.GetVerificationHeader().GetBodySignature().GetKey(), expectedKey) { + return client.ErrWrongPublicKey + } + + return nil +} diff --git a/pkg/services/object/put/v2/streamer.go b/pkg/services/object/put/v2/streamer.go index df07a67b1..a41c9826f 100644 --- a/pkg/services/object/put/v2/streamer.go +++ b/pkg/services/object/put/v2/streamer.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-node/pkg/core/client" "github.com/nspcc-dev/neofs-node/pkg/network" + "github.com/nspcc-dev/neofs-node/pkg/services/object/internal" putsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/put" "github.com/nspcc-dev/neofs-node/pkg/services/object/util" ) @@ -129,6 +130,8 @@ func (s *streamer) relayRequest(info client.NodeInfo, c client.Client) error { // open stream resp := new(object.PutResponse) + key := info.PublicKey() + var firstErr error info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) { @@ -173,6 +176,11 @@ func (s *streamer) relayRequest(info client.NodeInfo, c client.Client) error { return } + // verify response key + if err = internal.VerifyResponseKeyV2(key, resp); err != nil { + return + } + // verify response structure err = signature.VerifyServiceMessage(resp) if err != nil { diff --git a/pkg/services/object/search/v2/util.go b/pkg/services/object/search/v2/util.go index 521aa71ec..3c0f15f65 100644 --- a/pkg/services/object/search/v2/util.go +++ b/pkg/services/object/search/v2/util.go @@ -17,6 +17,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/core/client" "github.com/nspcc-dev/neofs-node/pkg/network" objectSvc "github.com/nspcc-dev/neofs-node/pkg/services/object" + "github.com/nspcc-dev/neofs-node/pkg/services/object/internal" searchsvc "github.com/nspcc-dev/neofs-node/pkg/services/object/search" "github.com/nspcc-dev/neofs-node/pkg/services/object/util" ) @@ -46,7 +47,7 @@ func (s *Service) toPrm(req *objectV2.SearchRequest, stream objectSvc.SearchStre if !commonPrm.LocalOnly() { var onceResign sync.Once - p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client) ([]*objectSDK.ID, error) { + p.SetRequestForwarder(groupAddressRequestForwarder(func(addr network.Address, c client.Client, pubkey []byte) ([]*objectSDK.ID, error) { var err error // once compose and resign forwarding request @@ -89,6 +90,11 @@ func (s *Service) toPrm(req *objectV2.SearchRequest, stream objectSvc.SearchStre return nil, fmt.Errorf("reading the response failed: %w", err) } + // verify response key + if err = internal.VerifyResponseKeyV2(pubkey, resp); err != nil { + return nil, err + } + // verify response structure if err := signature.VerifyServiceMessage(resp); err != nil { return nil, fmt.Errorf("could not verify %T: %w", resp, err) @@ -111,11 +117,13 @@ func (s *Service) toPrm(req *objectV2.SearchRequest, stream objectSvc.SearchStre return p, nil } -func groupAddressRequestForwarder(f func(network.Address, client.Client) ([]*objectSDK.ID, error)) searchsvc.RequestForwarder { +func groupAddressRequestForwarder(f func(network.Address, client.Client, []byte) ([]*objectSDK.ID, error)) searchsvc.RequestForwarder { return func(info client.NodeInfo, c client.Client) ([]*objectSDK.ID, error) { var ( firstErr error res []*objectSDK.ID + + key = info.PublicKey() ) info.AddressGroup().IterateAddresses(func(addr network.Address) (stop bool) { @@ -131,7 +139,7 @@ func groupAddressRequestForwarder(f func(network.Address, client.Client) ([]*obj // would be nice to log otherwise }() - res, err = f(addr, c) + res, err = f(addr, c, key) return })