Skip to content

Commit

Permalink
eosfs: Workaround assumptions on stat/xattrs auth (#4369)
Browse files Browse the repository at this point in the history
* eosfs: Workaround assumptions on stat/xattrs auth

* Add changelog

* eosfs: some refactoring

* eosfs: small refactoring

* eosfs: small refactoring

---------

Co-authored-by: Fabrizio Furano <fabrizio.furano@gmail.com>
Co-authored-by: fbx <ffurano@blably.com>
  • Loading branch information
3 people authored Dec 4, 2023
1 parent 4fa2098 commit 64ecbc5
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 44 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/eosfs-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Bugfix: Carefully use root credentials to perform system level ops

This PR ensures that system level ops like setlock, setattr, stat... work when invoked from a gateway
This is relevant for eosgrpc, as eosbinary exploited the permissivity of the eos cmdline

https://github.com/cs3org/reva/pull/4369
94 changes: 50 additions & 44 deletions pkg/storage/utils/eosfs/eosfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,12 @@ func (fs *eosfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Referen
return errtypes.BadRequest("eosfs: no metadata set")
}

fn, auth, err := fs.resolveRefAndGetAuth(ctx, ref)
fn, _, err := fs.resolveRefAndGetAuth(ctx, ref)
if err != nil {
return err
}

rootAuth, err := fs.getRootAuth(ctx)
if err != nil {
return err
}
Expand All @@ -538,7 +543,7 @@ func (fs *eosfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Referen

// TODO(labkode): SetArbitraryMetadata does not have semantics for recursivity.
// We set it to false
err := fs.c.SetAttr(ctx, auth, attr, false, false, fn)
err := fs.c.SetAttr(ctx, rootAuth, attr, false, false, fn)
if err != nil {
return errors.Wrap(err, "eosfs: error setting xattr in eos driver")
}
Expand All @@ -551,7 +556,12 @@ func (fs *eosfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Refer
return errtypes.BadRequest("eosfs: no keys set")
}

fn, auth, err := fs.resolveRefAndGetAuth(ctx, ref)
fn, _, err := fs.resolveRefAndGetAuth(ctx, ref)
if err != nil {
return err
}

rootAuth, err := fs.getRootAuth(ctx)
if err != nil {
return err
}
Expand All @@ -566,7 +576,7 @@ func (fs *eosfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Refer
Key: k,
}

err := fs.c.UnsetAttr(ctx, auth, attr, false, fn)
err := fs.c.UnsetAttr(ctx, rootAuth, attr, false, fn)
if err != nil {
if errors.Is(err, eosclient.AttrNotExistsError) {
continue
Expand All @@ -577,30 +587,40 @@ func (fs *eosfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Refer
return nil
}

func (fs *eosfs) getLockPayloads(ctx context.Context, auth eosclient.Authorization, path string) (string, string, error) {
data, err := fs.c.GetAttr(ctx, auth, "sys."+LockPayloadKey, path)
func (fs *eosfs) getLockPayloads(ctx context.Context, path string) (string, string, error) {
// sys attributes want root auth, buddy
rootauth, err := fs.getRootAuth(ctx)
if err != nil {
return "", "", err
}
data, err := fs.c.GetAttr(ctx, rootauth, "sys."+LockPayloadKey, path)
if err != nil {
return "", "", err
}

eoslock, err := fs.c.GetAttr(ctx, auth, "sys."+EosLockKey, path)
eoslock, err := fs.c.GetAttr(ctx, rootauth, "sys."+EosLockKey, path)
if err != nil {
return "", "", err
}

return data.Val, eoslock.Val, nil
}

func (fs *eosfs) removeLockAttrs(ctx context.Context, auth eosclient.Authorization, path string) error {
err := fs.c.UnsetAttr(ctx, auth, &eosclient.Attribute{
func (fs *eosfs) removeLockAttrs(ctx context.Context, path string) error {
rootAuth, err := fs.getRootAuth(ctx)
if err != nil {
return err
}

err = fs.c.UnsetAttr(ctx, rootAuth, &eosclient.Attribute{
Type: SystemAttr,
Key: EosLockKey,
}, false, path)
if err != nil {
return errors.Wrap(err, "eosfs: error unsetting the eos lock")
}

err = fs.c.UnsetAttr(ctx, auth, &eosclient.Attribute{
err = fs.c.UnsetAttr(ctx, rootAuth, &eosclient.Attribute{
Type: SystemAttr,
Key: LockPayloadKey,
}, false, path)
Expand All @@ -611,7 +631,7 @@ func (fs *eosfs) removeLockAttrs(ctx context.Context, auth eosclient.Authorizati
return nil
}

func (fs *eosfs) getLock(ctx context.Context, auth eosclient.Authorization, user *userpb.User, path string, ref *provider.Reference) (*provider.Lock, error) {
func (fs *eosfs) getLock(ctx context.Context, user *userpb.User, path string, ref *provider.Reference) (*provider.Lock, error) {
// the cs3apis require to have the read permission on the resource
// to get the eventual lock.
has, err := fs.userHasReadAccess(ctx, user, ref)
Expand All @@ -622,7 +642,7 @@ func (fs *eosfs) getLock(ctx context.Context, auth eosclient.Authorization, user
return nil, errtypes.BadRequest("user has not read access on resource")
}

d, eosl, err := fs.getLockPayloads(ctx, auth, path)
d, eosl, err := fs.getLockPayloads(ctx, path)
if err != nil {
if !errors.Is(err, eosclient.AttrNotExistsError) {
return nil, errtypes.NotFound("lock not found for ref")
Expand All @@ -636,7 +656,7 @@ func (fs *eosfs) getLock(ctx context.Context, auth eosclient.Authorization, user

if time.Unix(int64(l.Expiration.Seconds), 0).After(time.Now()) {
// the lock expired
if err := fs.removeLockAttrs(ctx, auth, path); err != nil {
if err := fs.removeLockAttrs(ctx, path); err != nil {
return nil, err
}
return nil, errtypes.NotFound("lock not found for ref")
Expand All @@ -657,10 +677,6 @@ func (fs *eosfs) GetLock(ctx context.Context, ref *provider.Reference) (*provide
if err != nil {
return nil, errors.Wrap(err, "eosfs: no user in ctx")
}
auth, err := fs.getUserAuth(ctx, user, path)
if err != nil {
return nil, errors.Wrap(err, "eosfs: error getting uid and gid for user")
}

// the cs3apis require to have the read permission on the resource
// to get the eventual lock.
Expand All @@ -672,7 +688,7 @@ func (fs *eosfs) GetLock(ctx context.Context, ref *provider.Reference) (*provide
return nil, errtypes.BadRequest("user has no read access on resource")
}

return fs.getLock(ctx, auth, user, path, ref)
return fs.getLock(ctx, user, path, ref)
}

func (fs *eosfs) setLock(ctx context.Context, lock *provider.Lock, path string) error {
Expand Down Expand Up @@ -947,11 +963,7 @@ func (fs *eosfs) Unlock(ctx context.Context, ref *provider.Reference, lock *prov
}
path = fs.wrap(ctx, path)

auth, err := fs.getRootAuth(ctx)
if err != nil {
return errors.Wrap(err, "eosfs: error getting uid and gid for user")
}
return fs.removeLockAttrs(ctx, auth, path)
return fs.removeLockAttrs(ctx, path)
}

func (fs *eosfs) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error {
Expand Down Expand Up @@ -1161,6 +1173,17 @@ func (fs *eosfs) ListGrants(ctx context.Context, ref *provider.Reference) ([]*pr
return nil, err
}

// This is invoked just to see if it fails, I know, it's ugly
_, err = fs.c.GetAttrs(ctx, auth, fn)
if err != nil {
return nil, err
}

// Now we get the real info, I know, it's ugly
auth, err = fs.getRootAuth(ctx)
if err != nil {
return nil, err
}
attrs, err := fs.c.GetAttrs(ctx, auth, fn)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1192,23 +1215,12 @@ func (fs *eosfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []st
log := appctx.GetLogger(ctx)
log.Info().Msg("eosfs: get md for ref:" + ref.String())

u, err := getUser(ctx)
_, err := getUser(ctx)
if err != nil {
return nil, err
}

fn := ""
if u.Id.Type == userpb.UserType_USER_TYPE_LIGHTWEIGHT ||
u.Id.Type == userpb.UserType_USER_TYPE_FEDERATED {
p, err := fs.resolve(ctx, ref)
if err != nil {
return nil, errors.Wrap(err, "eosfs: error resolving reference")
}

fn = fs.wrap(ctx, p)
}

auth, err := fs.getUserAuth(ctx, u, fn)
auth, err := fs.getRootAuth(ctx)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1242,7 +1254,7 @@ func (fs *eosfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []st
}
}

fn = fs.wrap(ctx, p)
fn := fs.wrap(ctx, p)
eosFileInfo, err := fs.c.GetFileInfoByPath(ctx, auth, fn)
if err != nil {
return nil, err
Expand All @@ -1254,13 +1266,7 @@ func (fs *eosfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []st
func (fs *eosfs) getMDShareFolder(ctx context.Context, p string, mdKeys []string) (*provider.ResourceInfo, error) {
fn := fs.wrapShadow(ctx, p)

u, err := getUser(ctx)
if err != nil {
return nil, err
}

// lightweight accounts don't have share folders, so we're passing an empty string as path
auth, err := fs.getUserAuth(ctx, u, "")
auth, err := fs.getRootAuth(ctx)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 64ecbc5

Please sign in to comment.