From d08c24ea253fdc45cb9b9a264fc2608f463153ce Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 16 Sep 2022 14:22:09 +0400 Subject: [PATCH] [#1420] object/acl: Fix correlation of object session to request In previous implementation of `neofs-node` app object session was not checked for substitution of the object related to it. Also, for access checks, the session object was substituted instead of the one from the request. This, on the one hand, made it possible to inherit the session from the parent object for authorization for certain actions. On the other hand, it covered the mentioned object substitution, which is a critical vulnerability. Next changes are applied to processing of all Object service requests: - check if object session relates to the requested object - use requested object in access checks. Disclosed problem of object context inheritance will be solved within Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 5 + cmd/neofs-cli/modules/session/util.go | 2 +- go.mod | 4 +- go.sum | 20 +--- pkg/services/object/acl/v2/service.go | 123 ++++++++++++++++-------- pkg/services/object/acl/v2/util.go | 78 +++++++-------- pkg/services/object/acl/v2/util_test.go | 30 ++++++ 7 files changed, 160 insertions(+), 102 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 530b34fb470..e7f7d5b93f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ Changelog for NeoFS Node - Description of command `netmap nodeinfo` (#1821) - Proper status for object.Delete if session token is missing (#1697) - Fail startup if metabase has an old version (#1809) +- Correlation of object session to request (#1420) ### Removed - Remove WIF and NEP2 support in `neofs-cli`'s --wallet flag (#1128) @@ -36,6 +37,10 @@ Replace using the `control netmap-snapshot` command with `netmap snapshot` one i Node can now specify additional addresses in `ExternalAddr` attribute. To allow a node to dial other nodes external address, use `apiclient.allow_external` config setting. +When issuing an object session token for root (virtual, "big") objects, +additionally include all members of the split-chain. If session context +includes root object only, it is not spread to physical ("small") objects. + ## [0.32.0] - 2022-09-14 - Pungdo (풍도, 楓島) ### Added diff --git a/cmd/neofs-cli/modules/session/util.go b/cmd/neofs-cli/modules/session/util.go index 6a710da803b..674b90613eb 100644 --- a/cmd/neofs-cli/modules/session/util.go +++ b/cmd/neofs-cli/modules/session/util.go @@ -63,7 +63,7 @@ func Prepare(cmd *cobra.Command, cnr cid.ID, obj *oid.ID, key *ecdsa.PrivateKey, tok.BindContainer(cnr) if obj != nil { - tok.LimitByObject(*obj) + tok.LimitByObjects(*obj) } err := tok.Sign(*key) diff --git a/go.mod b/go.mod index 130d3367142..b774748221d 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/nspcc-dev/hrw v1.0.9 github.com/nspcc-dev/neo-go v0.99.2 github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b // indirect - github.com/nspcc-dev/neofs-api-go/v2 v2.13.2-0.20220919124434-cf868188ef9c + github.com/nspcc-dev/neofs-api-go/v2 v2.13.2-0.20221004142957-5fc2644c680d github.com/nspcc-dev/neofs-contract v0.15.5 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.6.0.20220926102839-c6576c8112ee github.com/nspcc-dev/tzhash v1.6.1 @@ -97,3 +97,5 @@ require ( gopkg.in/ini.v1 v1.66.6 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) + +replace github.com/nspcc-dev/neofs-sdk-go => github.com/cthulhu-rider/neofs-sdk-go v0.0.0-20221004143526-5e5b260f1600 diff --git a/go.sum b/go.sum index 8d6b54298f9..874f977000a 100644 --- a/go.sum +++ b/go.sum @@ -64,7 +64,6 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= -github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs= github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -75,7 +74,6 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -140,6 +138,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cthulhu-rider/neofs-sdk-go v0.0.0-20221004143526-5e5b260f1600 h1:1Zdx3DkGlGSFRYliWprCwx1rsfULG08GnlYWFG7drXY= +github.com/cthulhu-rider/neofs-sdk-go v0.0.0-20221004143526-5e5b260f1600/go.mod h1:HIU7csNSqyYf71rgr4H5qitMZMxVpovBPl7m05y4V9g= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -159,12 +159,10 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -362,7 +360,6 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -436,25 +433,21 @@ github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+d github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= github.com/nspcc-dev/dbft v0.0.0-20220629112714-fd49ca59d354/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= -github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= github.com/nspcc-dev/neo-go v0.99.1-pre.0.20220714084516-54849ef3e58e/go.mod h1:/y5Sl8p3YheTygriBtCCMWKkDOek8HcvSo5ds2rJtKI= github.com/nspcc-dev/neo-go v0.99.2 h1:Fq79FI6BJkj/XkgWtrURSdXgXIeBHCgbKauBw3LOvZ4= github.com/nspcc-dev/neo-go v0.99.2/go.mod h1:9P0yWqhZX7i/ChJ+zjtiStO1uPTolPFUM+L5oNznU8E= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220713145417-4f184498bc42/go.mod h1:QBE0I30F2kOAISNpT5oks82yF4wkkUq3SCfI3Hqgx/Y= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b h1:J7QZNmnO84esVuPbBo88fwAG4XVnDjlSTiO1ewLNCkQ= github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-api-go/v2 v2.13.2-0.20220919124434-cf868188ef9c h1:YZwtBY9uypaShbe/NLhosDanIfxt8VhQlSLYUeFIWv8= -github.com/nspcc-dev/neofs-api-go/v2 v2.13.2-0.20220919124434-cf868188ef9c/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM= +github.com/nspcc-dev/neofs-api-go/v2 v2.13.2-0.20221004142957-5fc2644c680d h1:Oc15A8gDoP/TC5kdJi6TW9AnOp5dYiecZ0tJDRUV7vg= +github.com/nspcc-dev/neofs-api-go/v2 v2.13.2-0.20221004142957-5fc2644c680d/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM= github.com/nspcc-dev/neofs-contract v0.15.3/go.mod h1:BXVZUZUJxrmmDETglXHI8+5DSgn84B9y5DoSWqEjYCs= github.com/nspcc-dev/neofs-contract v0.15.5 h1:6Fsr1VRaG1klCWipWWPHvVkKaVS85tcxxsNDbvVB2zk= github.com/nspcc-dev/neofs-contract v0.15.5/go.mod h1:gN5bo2TlMvLbySImmg76DVj3jVmYgti2VVlQ+h/tcr0= @@ -463,10 +456,6 @@ github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BE github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.6.0.20220926102839-c6576c8112ee h1:QR2YyUCGiI0nEIMeE3TKJSYroT7EkQ6WIN5I8mm/5CA= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.6.0.20220926102839-c6576c8112ee/go.mod h1:lJ1K24ZW5MsUrAi2741cs8/gZ/jj61ilHe2NyfMuYMs= github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= @@ -940,7 +929,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= diff --git a/pkg/services/object/acl/v2/service.go b/pkg/services/object/acl/v2/service.go index 33d04f1203e..38d71f95abc 100644 --- a/pkg/services/object/acl/v2/service.go +++ b/pkg/services/object/acl/v2/service.go @@ -113,11 +113,23 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream return err } + obj, err := getObjectIDFromRequestBody(request.GetBody()) + if err != nil { + return err + } + sTok, err := originalSessionToken(request.GetMetaHeader()) if err != nil { return err } + if sTok != nil { + err = assertSessionRelation(*sTok, cnr, obj) + if err != nil { + return err + } + } + bTok, err := originalBearerToken(request.GetMetaHeader()) if err != nil { return err @@ -135,12 +147,7 @@ func (b Service) Get(request *objectV2.GetRequest, stream object.GetObjectStream return err } - reqInfo.obj, err = getObjectIDFromRequestBody(request.GetBody()) - if err != nil { - return err - } - - useObjectIDFromSession(&reqInfo, sTok) + reqInfo.obj = obj if !b.checker.CheckBasicACL(reqInfo) { return basicACLErr(reqInfo) @@ -172,11 +179,23 @@ func (b Service) Head( return nil, err } + obj, err := getObjectIDFromRequestBody(request.GetBody()) + if err != nil { + return nil, err + } + sTok, err := originalSessionToken(request.GetMetaHeader()) if err != nil { return nil, err } + if sTok != nil { + err = assertSessionRelation(*sTok, cnr, obj) + if err != nil { + return nil, err + } + } + bTok, err := originalBearerToken(request.GetMetaHeader()) if err != nil { return nil, err @@ -194,12 +213,7 @@ func (b Service) Head( return nil, err } - reqInfo.obj, err = getObjectIDFromRequestBody(request.GetBody()) - if err != nil { - return nil, err - } - - useObjectIDFromSession(&reqInfo, sTok) + reqInfo.obj = obj if !b.checker.CheckBasicACL(reqInfo) { return nil, basicACLErr(reqInfo) @@ -228,6 +242,13 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr return err } + if sTok != nil { + err = assertSessionRelation(*sTok, id, nil) + if err != nil { + return err + } + } + bTok, err := originalBearerToken(request.GetMetaHeader()) if err != nil { return err @@ -245,11 +266,6 @@ func (b Service) Search(request *objectV2.SearchRequest, stream object.SearchStr return err } - reqInfo.obj, err = getObjectIDFromRequestBody(request.GetBody()) - if err != nil { - return err - } - if !b.checker.CheckBasicACL(reqInfo) { return basicACLErr(reqInfo) } else if err := b.checker.CheckEACL(request, reqInfo); err != nil { @@ -271,11 +287,23 @@ func (b Service) Delete( return nil, err } + obj, err := getObjectIDFromRequestBody(request.GetBody()) + if err != nil { + return nil, err + } + sTok, err := originalSessionToken(request.GetMetaHeader()) if err != nil { return nil, err } + if sTok != nil { + err = assertSessionRelation(*sTok, cnr, obj) + if err != nil { + return nil, err + } + } + bTok, err := originalBearerToken(request.GetMetaHeader()) if err != nil { return nil, err @@ -293,12 +321,7 @@ func (b Service) Delete( return nil, err } - reqInfo.obj, err = getObjectIDFromRequestBody(request.GetBody()) - if err != nil { - return nil, err - } - - useObjectIDFromSession(&reqInfo, sTok) + reqInfo.obj = obj if !b.checker.CheckBasicACL(reqInfo) { return nil, basicACLErr(reqInfo) @@ -315,11 +338,23 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb return err } + obj, err := getObjectIDFromRequestBody(request.GetBody()) + if err != nil { + return err + } + sTok, err := originalSessionToken(request.GetMetaHeader()) if err != nil { return err } + if sTok != nil { + err = assertSessionRelation(*sTok, cnr, obj) + if err != nil { + return err + } + } + bTok, err := originalBearerToken(request.GetMetaHeader()) if err != nil { return err @@ -337,11 +372,7 @@ func (b Service) GetRange(request *objectV2.GetRangeRequest, stream object.GetOb return err } - reqInfo.obj, err = getObjectIDFromRequestBody(request.GetBody()) - if err != nil { - return err - } - useObjectIDFromSession(&reqInfo, sTok) + reqInfo.obj = obj if !b.checker.CheckBasicACL(reqInfo) { return basicACLErr(reqInfo) @@ -364,11 +395,23 @@ func (b Service) GetRangeHash( return nil, err } + obj, err := getObjectIDFromRequestBody(request.GetBody()) + if err != nil { + return nil, err + } + sTok, err := originalSessionToken(request.GetMetaHeader()) if err != nil { return nil, err } + if sTok != nil { + err = assertSessionRelation(*sTok, cnr, obj) + if err != nil { + return nil, err + } + } + bTok, err := originalBearerToken(request.GetMetaHeader()) if err != nil { return nil, err @@ -386,12 +429,7 @@ func (b Service) GetRangeHash( return nil, err } - reqInfo.obj, err = getObjectIDFromRequestBody(request.GetBody()) - if err != nil { - return nil, err - } - - useObjectIDFromSession(&reqInfo, sTok) + reqInfo.obj = obj if !b.checker.CheckBasicACL(reqInfo) { return nil, basicACLErr(reqInfo) @@ -427,6 +465,11 @@ func (p putStreamBasicChecker) Send(request *objectV2.PutRequest) error { return fmt.Errorf("invalid object owner: %w", err) } + obj, err := decodeRequestedObject(part.GetObjectID(), false) + if err != nil { + return err + } + var sTok *sessionSDK.Object if tokV2 := request.GetMetaHeader().GetSessionToken(); tokV2 != nil { @@ -436,6 +479,11 @@ func (p putStreamBasicChecker) Send(request *objectV2.PutRequest) error { if err != nil { return fmt.Errorf("invalid session token: %w", err) } + + err = assertSessionRelation(*sTok, cnr, obj) + if err != nil { + return err + } } bTok, err := originalBearerToken(request.GetMetaHeader()) @@ -455,12 +503,7 @@ func (p putStreamBasicChecker) Send(request *objectV2.PutRequest) error { return err } - reqInfo.obj, err = getObjectIDFromRequestBody(part) - if err != nil { - return err - } - - useObjectIDFromSession(&reqInfo, sTok) + reqInfo.obj = obj if !p.source.checker.CheckBasicACL(reqInfo) || !p.source.checker.StickyBitCheck(reqInfo, idOwner) { return basicACLErr(reqInfo) diff --git a/pkg/services/object/acl/v2/util.go b/pkg/services/object/acl/v2/util.go index 3d2797c2268..fdbe09d8d63 100644 --- a/pkg/services/object/acl/v2/util.go +++ b/pkg/services/object/acl/v2/util.go @@ -93,29 +93,26 @@ func originalSessionToken(header *sessionV2.RequestMetaHeader) (*sessionSDK.Obje return &tok, nil } -func getObjectIDFromRequestBody(body interface{}) (*oid.ID, error) { - var idV2 *refsV2.ObjectID +// getObjectIDFromRequestBody decodes oid.ID from the common interface of the +// object reference's holders. Returns an error if object ID is missing in the request. +func getObjectIDFromRequestBody(body interface{ GetAddress() *refsV2.Address }) (*oid.ID, error) { + return decodeRequestedObject(body.GetAddress().GetObjectID(), true) +} - switch v := body.(type) { - default: - return nil, nil - case interface { - GetObjectID() *refsV2.ObjectID - }: - idV2 = v.GetObjectID() - case interface { - GetAddress() *refsV2.Address - }: - idV2 = v.GetAddress().GetObjectID() - } +// decodeRequestedObject decodes oid.ID from refsV2.ObjectID message if latter +// is presented, otherwise returns an error iff message is required. +func decodeRequestedObject(m *refsV2.ObjectID, required bool) (*oid.ID, error) { + if m == nil { + if required { + return nil, errors.New("missing object ID") + } - if idV2 == nil { return nil, nil } var id oid.ID - err := id.ReadFromV2(*idV2) + err := id.ReadFromV2(*m) if err != nil { return nil, err } @@ -123,34 +120,6 @@ func getObjectIDFromRequestBody(body interface{}) (*oid.ID, error) { return &id, nil } -func useObjectIDFromSession(req *RequestInfo, token *sessionSDK.Object) { - if token == nil { - return - } - - // TODO(@cthulhu-rider): It'd be nice to not pull object identifiers from - // the token, but assert them. Track #1420 - var tokV2 sessionV2.Token - token.WriteToV2(&tokV2) - - ctx, ok := tokV2.GetBody().GetContext().(*sessionV2.ObjectSessionContext) - if !ok { - panic(fmt.Sprintf("wrong object session context %T, is it verified?", tokV2.GetBody().GetContext())) - } - - idV2 := ctx.GetAddress().GetObjectID() - if idV2 == nil { - return - } - - req.obj = new(oid.ID) - - err := req.obj.ReadFromV2(*idV2) - if err != nil { - panic(fmt.Sprintf("unexpected protocol violation error after correct session token decoding: %v", err)) - } -} - func ownerFromToken(token *sessionSDK.Object) (*user.ID, *keys.PublicKey, error) { // 1. First check signature of session token. if !token.VerifySignature() { @@ -231,3 +200,24 @@ func assertVerb(tok sessionSDK.Object, op acl.Op) bool { return false } + +// assertSessionRelation checks if given token describing the NeoFS session +// relates to the given container and optional object. Missing object +// means that the context isn't bound to any NeoFS object in the container. +// Returns no error iff relation is correct. Criteria: +// +// session is bound to the given container +// object is not specified or session is bound to this object +// +// Session MUST be bound to the particular container, otherwise behavior is undefined. +func assertSessionRelation(tok sessionSDK.Object, cnr cid.ID, obj *oid.ID) error { + if !tok.AssertContainer(cnr) { + return errors.New("requested container is not related to the session") + } + + if obj != nil && !tok.AssertObject(*obj) { + return errors.New("requested object is not related to the session") + } + + return nil +} diff --git a/pkg/services/object/acl/v2/util_test.go b/pkg/services/object/acl/v2/util_test.go index 792114f0da1..bac91e1684a 100644 --- a/pkg/services/object/acl/v2/util_test.go +++ b/pkg/services/object/acl/v2/util_test.go @@ -10,6 +10,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/v2/session" bearertest "github.com/nspcc-dev/neofs-sdk-go/bearer/test" aclsdk "github.com/nspcc-dev/neofs-sdk-go/container/acl" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" sessionSDK "github.com/nspcc-dev/neofs-sdk-go/session" sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test" "github.com/stretchr/testify/require" @@ -104,3 +106,31 @@ func TestIsVerbCompatible(t *testing.T) { } } } + +func TestAssertSessionRelation(t *testing.T) { + var tok sessionSDK.Object + cnr := cidtest.ID() + cnrOther := cidtest.ID() + obj := oidtest.ID() + objOther := oidtest.ID() + + // make sure ids differ, otherwise test won't work correctly + require.False(t, cnrOther.Equals(cnr)) + require.False(t, objOther.Equals(obj)) + + // bind session to the container (required) + tok.BindContainer(cnr) + + // test container-global session + require.NoError(t, assertSessionRelation(tok, cnr, nil)) + require.NoError(t, assertSessionRelation(tok, cnr, &obj)) + require.Error(t, assertSessionRelation(tok, cnrOther, nil)) + require.Error(t, assertSessionRelation(tok, cnrOther, &obj)) + + // limit the session to the particular object + tok.LimitByObjects(obj) + + // test fixed object session (here obj arg must be non-nil everywhere) + require.NoError(t, assertSessionRelation(tok, cnr, &obj)) + require.Error(t, assertSessionRelation(tok, cnr, &objOther)) +}