diff --git a/go.mod b/go.mod index b59a745129..829137aa21 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 496575257a..9ef86245df 100644 Binary files a/go.sum and b/go.sum differ diff --git a/pkg/core/client/errors.go b/pkg/core/client/errors.go new file mode 100644 index 0000000000..a2aac5e55e --- /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 c19b0c36ca..3fb4484da3 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 76190fef65..738b4fcd94 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 6e15430e37..bc8ed26ba9 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 0000000000..9e5211973e --- /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 df07a67b1e..a41c9826f5 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 521aa71ec5..3c0f15f65f 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 })