2020-09-21 13:33:49 +00:00
|
|
|
package acl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/ecdsa"
|
2021-05-31 08:55:40 +00:00
|
|
|
"crypto/elliptic"
|
2021-05-18 08:12:51 +00:00
|
|
|
"fmt"
|
2020-09-21 13:33:49 +00:00
|
|
|
|
2021-05-31 08:55:40 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer "github.com/nspcc-dev/neofs-api-go/v2/acl"
|
2020-09-21 13:33:49 +00:00
|
|
|
"github.com/nspcc-dev/neofs-api-go/v2/session"
|
2020-10-21 17:30:54 +00:00
|
|
|
v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature"
|
2020-09-21 16:30:43 +00:00
|
|
|
core "github.com/nspcc-dev/neofs-node/pkg/core/netmap"
|
2021-11-10 07:08:33 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/container"
|
|
|
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
2022-01-20 09:49:03 +00:00
|
|
|
eaclSDK "github.com/nspcc-dev/neofs-sdk-go/eacl"
|
2021-11-10 07:08:33 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/owner"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/signature"
|
|
|
|
sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature"
|
2021-01-27 08:47:40 +00:00
|
|
|
"go.uber.org/zap"
|
2020-09-21 13:33:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
InnerRingFetcher interface {
|
|
|
|
InnerRingKeys() ([][]byte, error)
|
|
|
|
}
|
|
|
|
|
2020-10-02 11:39:32 +00:00
|
|
|
metaWithToken struct {
|
|
|
|
vheader *session.RequestVerificationHeader
|
|
|
|
token *session.SessionToken
|
2020-10-21 13:11:18 +00:00
|
|
|
bearer *bearer.BearerToken
|
2020-12-28 15:59:42 +00:00
|
|
|
src interface{}
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SenderClassifier struct {
|
2021-01-27 08:47:40 +00:00
|
|
|
log *zap.Logger
|
2020-09-21 16:12:52 +00:00
|
|
|
innerRing InnerRingFetcher
|
2020-09-21 16:30:43 +00:00
|
|
|
netmap core.Source
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
// fixme: update classifier constructor
|
2021-01-27 08:47:40 +00:00
|
|
|
func NewSenderClassifier(l *zap.Logger, ir InnerRingFetcher, nm core.Source) SenderClassifier {
|
2020-09-21 16:12:52 +00:00
|
|
|
return SenderClassifier{
|
2021-01-27 08:47:40 +00:00
|
|
|
log: l,
|
2020-09-21 16:12:52 +00:00
|
|
|
innerRing: ir,
|
|
|
|
netmap: nm,
|
|
|
|
}
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-21 16:12:52 +00:00
|
|
|
func (c SenderClassifier) Classify(
|
2020-10-02 11:39:32 +00:00
|
|
|
req metaWithToken,
|
2021-05-31 11:03:17 +00:00
|
|
|
cid *cid.ID,
|
2022-01-20 09:49:03 +00:00
|
|
|
cnr *container.Container) (role eaclSDK.Role, isIR bool, key []byte, err error) {
|
2020-10-02 11:39:32 +00:00
|
|
|
if cid == nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return 0, false, nil, fmt.Errorf("%w: container id is not set", ErrMalformedRequest)
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ownerID, ownerKey, err := requestOwner(req)
|
2020-10-21 17:39:14 +00:00
|
|
|
if err != nil {
|
2020-11-18 07:53:42 +00:00
|
|
|
return 0, false, nil, err
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2021-05-31 08:55:40 +00:00
|
|
|
ownerKeyInBytes := ownerKey.Bytes()
|
2020-10-05 14:13:23 +00:00
|
|
|
|
2020-09-21 13:33:49 +00:00
|
|
|
// todo: get owner from neofs.id if present
|
|
|
|
|
|
|
|
// if request owner is the same as container owner, return RoleUser
|
2021-05-31 10:30:59 +00:00
|
|
|
if ownerID.Equal(cnr.OwnerID()) {
|
2022-01-20 09:49:03 +00:00
|
|
|
return eaclSDK.RoleUser, false, ownerKeyInBytes, nil
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
isInnerRingNode, err := c.isInnerRingKey(ownerKeyInBytes)
|
|
|
|
if err != nil {
|
2021-01-27 08:47:40 +00:00
|
|
|
// do not throw error, try best case matching
|
|
|
|
c.log.Debug("can't check if request from inner ring",
|
|
|
|
zap.String("error", err.Error()))
|
2020-09-21 13:33:49 +00:00
|
|
|
} else if isInnerRingNode {
|
2022-01-20 09:49:03 +00:00
|
|
|
return eaclSDK.RoleSystem, true, ownerKeyInBytes, nil
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2020-09-28 10:54:24 +00:00
|
|
|
isContainerNode, err := c.isContainerKey(ownerKeyInBytes, cid.ToV2().GetValue(), cnr)
|
2020-09-21 13:33:49 +00:00
|
|
|
if err != nil {
|
2021-01-27 08:47:40 +00:00
|
|
|
// error might happen if request has `RoleOther` key and placement
|
|
|
|
// is not possible for previous epoch, so
|
|
|
|
// do not throw error, try best case matching
|
|
|
|
c.log.Debug("can't check if request from container node",
|
|
|
|
zap.String("error", err.Error()))
|
2020-09-21 13:33:49 +00:00
|
|
|
} else if isContainerNode {
|
2022-01-20 09:49:03 +00:00
|
|
|
return eaclSDK.RoleSystem, false, ownerKeyInBytes, nil
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if none of above, return RoleOthers
|
2022-01-20 09:49:03 +00:00
|
|
|
return eaclSDK.RoleOthers, false, ownerKeyInBytes, nil
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2021-05-31 08:55:40 +00:00
|
|
|
func requestOwner(req metaWithToken) (*owner.ID, *keys.PublicKey, error) {
|
2020-10-02 11:39:32 +00:00
|
|
|
if req.vheader == nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("%w: nil verification header", ErrMalformedRequest)
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if session token is presented, use it as truth source
|
2020-10-21 17:30:54 +00:00
|
|
|
if req.token.GetBody() != nil {
|
|
|
|
// verify signature of session token
|
|
|
|
return ownerFromToken(req.token)
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise get original body signature
|
2020-10-02 11:39:32 +00:00
|
|
|
bodySignature := originalBodySignature(req.vheader)
|
2020-09-21 13:33:49 +00:00
|
|
|
if bodySignature == nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("%w: nil at body signature", ErrMalformedRequest)
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2021-05-31 08:55:40 +00:00
|
|
|
key := unmarshalPublicKey(bodySignature.Key())
|
2020-09-21 13:33:49 +00:00
|
|
|
|
2022-01-21 12:15:10 +00:00
|
|
|
return owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key)), key, nil
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
2021-11-10 07:08:33 +00:00
|
|
|
func originalBodySignature(v *session.RequestVerificationHeader) *signature.Signature {
|
2020-09-21 13:33:49 +00:00
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for v.GetOrigin() != nil {
|
|
|
|
v = v.GetOrigin()
|
|
|
|
}
|
|
|
|
|
2021-11-10 07:08:33 +00:00
|
|
|
return signature.NewFromV2(v.GetBodySignature())
|
2020-09-21 13:33:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c SenderClassifier) isInnerRingKey(owner []byte) (bool, error) {
|
|
|
|
innerRingKeys, err := c.innerRing.InnerRingKeys()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// if request owner key in the inner ring list, return RoleSystem
|
|
|
|
for i := range innerRingKeys {
|
|
|
|
if bytes.Equal(innerRingKeys[i], owner) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c SenderClassifier) isContainerKey(
|
|
|
|
owner, cid []byte,
|
|
|
|
cnr *container.Container) (bool, error) {
|
2021-01-11 15:12:38 +00:00
|
|
|
nm, err := core.GetLatestNetworkMap(c.netmap) // first check current netmap
|
2020-09-21 13:33:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
in, err := lookupKeyInContainer(nm, owner, cid, cnr)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
} else if in {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// then check previous netmap, this can happen in-between epoch change
|
|
|
|
// when node migrates data from last epoch container
|
2020-09-21 16:30:43 +00:00
|
|
|
nm, err = core.GetPreviousNetworkMap(c.netmap)
|
2020-09-21 13:33:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return lookupKeyInContainer(nm, owner, cid, cnr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func lookupKeyInContainer(
|
2020-09-21 16:30:43 +00:00
|
|
|
nm *netmap.Netmap,
|
2020-09-21 13:33:49 +00:00
|
|
|
owner, cid []byte,
|
|
|
|
cnr *container.Container) (bool, error) {
|
2020-11-16 09:43:52 +00:00
|
|
|
cnrNodes, err := nm.GetContainerNodes(cnr.PlacementPolicy(), cid)
|
2020-09-21 13:33:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
flatCnrNodes := cnrNodes.Flatten() // we need single array to iterate on
|
|
|
|
for i := range flatCnrNodes {
|
2020-11-05 15:51:43 +00:00
|
|
|
if bytes.Equal(flatCnrNodes[i].PublicKey(), owner) {
|
2020-09-21 13:33:49 +00:00
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
}
|
2020-10-21 17:30:54 +00:00
|
|
|
|
2021-05-31 08:55:40 +00:00
|
|
|
func ownerFromToken(token *session.SessionToken) (*owner.ID, *keys.PublicKey, error) {
|
2020-10-21 17:30:54 +00:00
|
|
|
// 1. First check signature of session token.
|
|
|
|
signWrapper := v2signature.StableMarshalerWrapper{SM: token.GetBody()}
|
2021-11-10 07:08:33 +00:00
|
|
|
if err := sigutil.VerifyDataWithSource(signWrapper, func() (key, sig []byte) {
|
2020-10-21 17:30:54 +00:00
|
|
|
tokenSignature := token.GetSignature()
|
|
|
|
return tokenSignature.GetKey(), tokenSignature.GetSign()
|
|
|
|
}); err != nil {
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("%w: invalid session token signature", ErrMalformedRequest)
|
2020-10-21 17:30:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Then check if session token owner issued the session token
|
2021-05-31 08:55:40 +00:00
|
|
|
tokenIssuerKey := unmarshalPublicKey(token.GetSignature().GetKey())
|
2020-11-19 14:17:15 +00:00
|
|
|
tokenOwner := owner.NewIDFromV2(token.GetBody().GetOwnerID())
|
2020-10-21 17:30:54 +00:00
|
|
|
|
2020-11-19 14:17:15 +00:00
|
|
|
if !isOwnerFromKey(tokenOwner, tokenIssuerKey) {
|
2020-10-21 17:30:54 +00:00
|
|
|
// todo: in this case we can issue all owner keys from neofs.id and check once again
|
2021-05-18 08:12:51 +00:00
|
|
|
return nil, nil, fmt.Errorf("%w: invalid session token owner", ErrMalformedRequest)
|
2020-10-21 17:30:54 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 14:17:15 +00:00
|
|
|
return tokenOwner, tokenIssuerKey, nil
|
2020-10-21 17:30:54 +00:00
|
|
|
}
|
2021-05-31 08:55:40 +00:00
|
|
|
|
|
|
|
func unmarshalPublicKey(bs []byte) *keys.PublicKey {
|
|
|
|
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return pub
|
|
|
|
}
|