Skip to content

Commit 7b4e082

Browse files
committed
sn/object: Verify header/payload checksums received from remote SNs
Follow 4df4aba. Signed-off-by: Leonard Lyubich <[email protected]>
1 parent 85e982a commit 7b4e082

File tree

3 files changed

+63
-14
lines changed

3 files changed

+63
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Changelog for NeoFS Node
1515
- SN does not sign object GET/HEAD responses to requests with API version > v2.17 (#3406)
1616
- CLI object `head`/`get` commands now verify header/payload checksums of received objects (#3406)
1717
- IR now verifies header/payload checksums of received objects (#3406)
18+
- SN now verifies header/payload checksums of objects received from remote SNs (#3406)
1819

1920
### Removed
2021

pkg/services/object/internal/client/client.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ func GetObject(prm GetObjectPrm) (*GetObjectRes, error) {
148148
}
149149

150150
prm.cliPrm.WithXHeaders(prm.xHeaders...)
151-
prm.cliPrm.SkipChecksumVerification()
152151

153152
obj, rdr, err := prm.cli.ObjectGetInit(prm.ctx, prm.cnr, prm.obj, prm.signer, prm.cliPrm)
154153
if err != nil {
@@ -230,7 +229,6 @@ func HeadObject(prm HeadObjectPrm) (*HeadObjectRes, error) {
230229
}
231230

232231
prm.cliPrm.WithXHeaders(prm.xHeaders...)
233-
prm.cliPrm.SkipChecksumVerification()
234232

235233
hdr, err := prm.cli.ObjectHead(prm.ctx, prm.cnr, prm.obj, prm.signer, prm.cliPrm)
236234
if err != nil {

pkg/services/object/server.go

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/binary"
1010
"errors"
1111
"fmt"
12+
"hash"
1213
"io"
1314
"sync"
1415
"time"
@@ -712,14 +713,14 @@ func convertHeadPrm(signer ecdsa.PrivateKey, req *protoobject.HeadRequest, resp
712713
var hdr *object.Object
713714
return hdr, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error {
714715
var err error
715-
hdr, err = getHeaderFromRemoteNode(ctx, conn, req)
716+
hdr, err = getHeaderFromRemoteNode(ctx, conn, req, addr.Object())
716717
return err // TODO: log error
717718
})
718719
})
719720
return p, nil
720721
}
721722

722-
func getHeaderFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.HeadRequest) (*object.Object, error) {
723+
func getHeaderFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.HeadRequest, reqOID oid.ID) (*object.Object, error) {
723724
resp, err := protoobject.NewObjectServiceClient(conn).Head(ctx, req)
724725
if err != nil {
725726
return nil, fmt.Errorf("sending the request failed: %w", err)
@@ -770,6 +771,10 @@ func getHeaderFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *pr
770771
return nil, errors.New("missing signature")
771772
}
772773

774+
if err := checkHeaderAgainstID(v.Header.Header, reqOID); err != nil {
775+
return nil, err
776+
}
777+
773778
hdr = v.Header.Header
774779
idSig = v.Header.Signature
775780
case *protoobject.HeadResponse_Body_SplitInfo:
@@ -1101,12 +1106,17 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream
11011106
}
11021107

11031108
var onceResign sync.Once
1104-
var onceHdr sync.Once
1105-
var respondedPayload int
11061109
meta := req.GetMetaHeader()
11071110
if meta == nil {
11081111
return getsvc.Prm{}, errors.New("missing meta header")
11091112
}
1113+
1114+
proxyCtx := getProxyContext{
1115+
req: req,
1116+
reqOID: addr.Object(),
1117+
respStream: stream,
1118+
}
1119+
11101120
p.SetRequestForwarder(func(ctx context.Context, node client.NodeInfo, c client.MultiAddressClient) (*object.Object, error) {
11111121
var err error
11121122
onceResign.Do(func() {
@@ -1122,7 +1132,7 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream
11221132
}
11231133

11241134
return nil, c.ForEachGRPCConn(ctx, func(ctx context.Context, conn *grpc.ClientConn) error {
1125-
err := continueGetFromRemoteNode(ctx, conn, req, stream, &onceHdr, &respondedPayload)
1135+
err := proxyCtx.continueWithConn(ctx, conn)
11261136
if errors.Is(err, io.EOF) {
11271137
return nil
11281138
}
@@ -1132,8 +1142,19 @@ func convertGetPrm(signer ecdsa.PrivateKey, req *protoobject.GetRequest, stream
11321142
return p, nil
11331143
}
11341144

1135-
func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *protoobject.GetRequest, stream *getStream, onceHdr *sync.Once, respondedPayload *int) error {
1136-
getStream, err := protoobject.NewObjectServiceClient(conn).Get(ctx, req)
1145+
type getProxyContext struct {
1146+
req *protoobject.GetRequest
1147+
reqOID oid.ID
1148+
respStream *getStream
1149+
1150+
onceHdr sync.Once
1151+
respondedPayload int
1152+
payloadHashCheck []byte
1153+
payloadHashGot hash.Hash
1154+
}
1155+
1156+
func (x *getProxyContext) continueWithConn(ctx context.Context, conn *grpc.ClientConn) error {
1157+
getStream, err := protoobject.NewObjectServiceClient(conn).Get(ctx, x.req)
11371158
if err != nil {
11381159
return fmt.Errorf("stream opening failed: %w", err)
11391160
}
@@ -1147,6 +1168,9 @@ func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *
11471168
if !headWas {
11481169
return io.ErrUnexpectedEOF
11491170
}
1171+
if !bytes.Equal(x.payloadHashGot.Sum(nil), x.payloadHashCheck) {
1172+
return errors.New("received payload mismatches checksum from header")
1173+
}
11501174
return io.EOF
11511175
}
11521176
return fmt.Errorf("reading the response failed: %w", err)
@@ -1167,6 +1191,17 @@ func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *
11671191
if v == nil || v.Init == nil {
11681192
return errors.New("nil header oneof field")
11691193
}
1194+
1195+
if v.Init.Header == nil {
1196+
return errors.New("invalid response: missing header")
1197+
}
1198+
if v.Init.Header.PayloadHash == nil {
1199+
return errors.New("invalid response: invalid header: missing payload hash")
1200+
}
1201+
if err := checkHeaderAgainstID(v.Init.Header, x.reqOID); err != nil {
1202+
return err
1203+
}
1204+
11701205
mo := &protoobject.Object{
11711206
ObjectId: v.Init.ObjectId,
11721207
Signature: v.Init.Signature,
@@ -1177,27 +1212,31 @@ func continueGetFromRemoteNode(ctx context.Context, conn *grpc.ClientConn, req *
11771212
if err != nil {
11781213
return err
11791214
}
1180-
onceHdr.Do(func() {
1181-
err = stream.WriteHeader(obj)
1215+
x.onceHdr.Do(func() {
1216+
err = x.respStream.WriteHeader(obj)
11821217
})
11831218
if err != nil {
11841219
return fmt.Errorf("could not write object header in Get forwarder: %w", err)
11851220
}
1221+
1222+
x.payloadHashCheck = v.Init.Header.PayloadHash.Sum
1223+
x.payloadHashGot = sha256.New()
11861224
case *protoobject.GetResponse_Body_Chunk:
11871225
if !headWas {
11881226
return errors.New("incorrect message sequence")
11891227
}
11901228
fullChunk := v.Chunk
1191-
respChunk := chunkToSend(*respondedPayload, readPayload, fullChunk)
1229+
respChunk := chunkToSend(x.respondedPayload, readPayload, fullChunk)
11921230
if len(respChunk) == 0 {
11931231
readPayload += len(fullChunk)
11941232
continue
11951233
}
1196-
if err := stream.WriteChunk(respChunk); err != nil {
1234+
if err := x.respStream.WriteChunk(respChunk); err != nil {
11971235
return fmt.Errorf("could not write object chunk in Get forwarder: %w", err)
11981236
}
11991237
readPayload += len(fullChunk)
1200-
*respondedPayload += len(respChunk)
1238+
x.respondedPayload += len(respChunk)
1239+
x.payloadHashGot.Write(respChunk) // never returns an error according to docs
12011240
case *protoobject.GetResponse_Body_SplitInfo:
12021241
if v == nil || v.SplitInfo == nil {
12031242
return errors.New("nil split info oneof field")
@@ -2261,3 +2300,14 @@ func needSignGetResponse(req interface {
22612300
mjr := ver.GetMajor() // NPE-safe
22622301
return mjr < 2 || mjr == 2 && ver.GetMinor() <= 17
22632302
}
2303+
2304+
func checkHeaderAgainstID(hdr *protoobject.Header, id oid.ID) error {
2305+
b := make([]byte, hdr.MarshaledSize())
2306+
hdr.MarshalStable(b)
2307+
2308+
if oid.NewFromObjectHeaderBinary(b) != id {
2309+
return errors.New("received header mismatches ID")
2310+
}
2311+
2312+
return nil
2313+
}

0 commit comments

Comments
 (0)