[#529] objectcore: Fix object content validation

There are old objects where the owner of the object
may not match the one who issued the token.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2023-07-28 15:44:35 +03:00
parent ab2614ec2d
commit ae81d6660a
10 changed files with 535 additions and 42 deletions

View file

@ -1,20 +1,26 @@
package object
import (
"bytes"
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/sha256"
"errors"
"fmt"
"strconv"
objectV2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
)
// FormatValidator represents an object format validator.
@ -26,8 +32,16 @@ type FormatValidator struct {
type FormatValidatorOption func(*cfg)
type cfg struct {
netState netmap.State
e LockSource
netState netmap.State
e LockSource
ir InnerRing
netmap netmap.Source
containers container.Source
verifyTokenIssuer bool
}
type InnerRing interface {
InnerRingKeys() ([][]byte, error)
}
// DeleteHandler is an interface of delete queue processor.
@ -159,13 +173,117 @@ func (v *FormatValidator) validateSignatureKey(obj *objectSDK.Object) error {
return v.checkOwnerKey(ownerID, key)
}
if !token.Issuer().Equals(ownerID) {
return fmt.Errorf("(%T) different token issuer and object owner identifiers %s/%s", v, token.Issuer(), ownerID)
if v.verifyTokenIssuer {
signerIsIROrContainerNode, err := v.isIROrContainerNode(obj, binKey)
if err != nil {
return err
}
if signerIsIROrContainerNode {
return nil
}
if !token.Issuer().Equals(ownerID) {
return fmt.Errorf("(%T) different token issuer and object owner identifiers %s/%s", v, token.Issuer(), ownerID)
}
return nil
}
return nil
}
func (v *FormatValidator) isIROrContainerNode(obj *objectSDK.Object, signerKey []byte) (bool, error) {
pKey, err := keys.NewPublicKeyFromBytes(signerKey, elliptic.P256())
if err != nil {
return false, fmt.Errorf("(%T) failed to unmarshal signer public key: %w", v, err)
}
isIR, err := v.isInnerRingKey(pKey.Bytes())
if err != nil {
return false, fmt.Errorf("(%T) failed to check if signer is inner ring node: %w", v, err)
}
if isIR {
return true, nil
}
isContainerNode, err := v.isContainerNode(pKey.Bytes(), obj)
if err != nil {
return false, fmt.Errorf("(%T) failed to check if signer is container node: %w", v, err)
}
return isContainerNode, nil
}
func (v *FormatValidator) isInnerRingKey(key []byte) (bool, error) {
innerRingKeys, err := v.ir.InnerRingKeys()
if err != nil {
return false, err
}
for i := range innerRingKeys {
if bytes.Equal(innerRingKeys[i], key) {
return true, nil
}
}
return false, nil
}
func (v *FormatValidator) isContainerNode(key []byte, obj *objectSDK.Object) (bool, error) {
cnrID, containerIDSet := obj.ContainerID()
if !containerIDSet {
return false, errNilCID
}
cnrIDBin := make([]byte, sha256.Size)
cnrID.Encode(cnrIDBin)
cnr, err := v.containers.Get(cnrID)
if err != nil {
return false, fmt.Errorf("failed to get container (id=%s): %w", cnrID.EncodeToString(), err)
}
lastNetmap, err := netmap.GetLatestNetworkMap(v.netmap)
if err != nil {
return false, fmt.Errorf("failed to get latest netmap: %w", err)
}
isContainerNode, err := v.isContainerNodeKey(lastNetmap, cnr, cnrIDBin, key)
if err != nil {
return false, fmt.Errorf("failed to check latest netmap for container nodes: %w", err)
}
if isContainerNode {
return true, nil
}
previousNetmap, err := netmap.GetPreviousNetworkMap(v.netmap)
if err != nil {
return false, fmt.Errorf("failed to get previous netmap: %w", err)
}
isContainerNode, err = v.isContainerNodeKey(previousNetmap, cnr, cnrIDBin, key)
if err != nil {
return false, fmt.Errorf("failed to check previous netmap for container nodes: %w", err)
}
return isContainerNode, nil
}
func (v *FormatValidator) isContainerNodeKey(nm *netmapSDK.NetMap, cnr *container.Container, cnrIDBin, key []byte) (bool, error) {
cnrVectors, err := nm.ContainerNodes(cnr.Value.PlacementPolicy(), cnrIDBin)
if err != nil {
return false, err
}
for i := range cnrVectors {
for j := range cnrVectors[i] {
if bytes.Equal(cnrVectors[i][j].PublicKey(), key) {
return true, nil
}
}
}
return false, nil
}
func (v *FormatValidator) checkOwnerKey(id user.ID, key frostfsecdsa.PublicKey) error {
var id2 user.ID
user.IDFromKey(&id2, (ecdsa.PublicKey)(key))
@ -387,3 +505,31 @@ func WithLockSource(e LockSource) FormatValidatorOption {
c.e = e
}
}
// WithInnerRing return option to set Inner Ring source.
func WithInnerRing(ir InnerRing) FormatValidatorOption {
return func(c *cfg) {
c.ir = ir
}
}
// WithNetmapSource return option to set Netmap source.
func WithNetmapSource(ns netmap.Source) FormatValidatorOption {
return func(c *cfg) {
c.netmap = ns
}
}
// WithContainersSource return option to set Containers source.
func WithContainersSource(cs container.Source) FormatValidatorOption {
return func(c *cfg) {
c.containers = cs
}
}
// WithVerifySessionTokenIssuer return option to set verify session token issuer value.
func WithVerifySessionTokenIssuer(verifySessionTokenIssuer bool) FormatValidatorOption {
return func(c *cfg) {
c.verifyTokenIssuer = verifySessionTokenIssuer
}
}