[#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:
parent
ab2614ec2d
commit
ae81d6660a
10 changed files with 535 additions and 42 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue