summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry')
-rw-r--r--pkg/sentry/fsimpl/fuse/fusefs.go118
1 files changed, 90 insertions, 28 deletions
diff --git a/pkg/sentry/fsimpl/fuse/fusefs.go b/pkg/sentry/fsimpl/fuse/fusefs.go
index fb0ba2c6d..fef857afb 100644
--- a/pkg/sentry/fsimpl/fuse/fusefs.go
+++ b/pkg/sentry/fsimpl/fuse/fusefs.go
@@ -50,19 +50,11 @@ type filesystemOptions struct {
// mopts contains the raw, unparsed mount options passed to this filesystem.
mopts string
- // userID specifies the numeric uid of the mount owner.
- // This option should not be specified by the filesystem owner.
- // It is set by libfuse (or, if libfuse is not used, must be set
- // by the filesystem itself). For more information, see man page
- // for fuse(8)
- userID uint32
-
- // groupID specifies the numeric gid of the mount owner.
- // This option should not be specified by the filesystem owner.
- // It is set by libfuse (or, if libfuse is not used, must be set
- // by the filesystem itself). For more information, see man page
- // for fuse(8)
- groupID uint32
+ // uid of the mount owner.
+ uid auth.KUID
+
+ // gid of the mount owner.
+ gid auth.KGID
// rootMode specifies the the file mode of the filesystem's root.
rootMode linux.FileMode
@@ -76,6 +68,19 @@ type filesystemOptions struct {
// specified as "max_read" in fs parameters.
// If not specified by user, use math.MaxUint32 as default value.
maxRead uint32
+
+ // defaultPermissions is the default_permissions mount option. It instructs
+ // the kernel to perform a standard unix permission checks based on
+ // ownership and mode bits, instead of deferring the check to the server.
+ //
+ // Immutable after mount.
+ defaultPermissions bool
+
+ // allowOther is the allow_other mount option. It allows processes that
+ // don't own the FUSE mount to call into it.
+ //
+ // Immutable after mount.
+ allowOther bool
}
// filesystem implements vfs.FilesystemImpl.
@@ -115,14 +120,14 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
mopts := vfs.GenericParseMountOptions(opts.Data)
deviceDescriptorStr, ok := mopts["fd"]
if !ok {
- log.Warningf("%s.GetFilesystem: communication file descriptor N (obtained by opening /dev/fuse) must be specified as 'fd=N'", fsType.Name())
+ ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option fd missing")
return nil, nil, syserror.EINVAL
}
delete(mopts, "fd")
deviceDescriptor, err := strconv.ParseInt(deviceDescriptorStr, 10 /* base */, 32 /* bitSize */)
if err != nil {
- log.Debugf("%s.GetFilesystem: device FD '%v' not parsable: %v", fsType.Name(), deviceDescriptorStr, err)
+ ctx.Debugf("fusefs.FilesystemType.GetFilesystem: invalid fd: %q (%v)", deviceDescriptorStr, err)
return nil, nil, syserror.EINVAL
}
@@ -144,38 +149,54 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
// Parse and set all the other supported FUSE mount options.
// TODO(gVisor.dev/issue/3229): Expand the supported mount options.
- if userIDStr, ok := mopts["user_id"]; ok {
+ if uidStr, ok := mopts["user_id"]; ok {
delete(mopts, "user_id")
- userID, err := strconv.ParseUint(userIDStr, 10, 32)
+ uid, err := strconv.ParseUint(uidStr, 10, 32)
if err != nil {
- log.Warningf("%s.GetFilesystem: invalid user_id: user_id=%s", fsType.Name(), userIDStr)
+ log.Warningf("%s.GetFilesystem: invalid user_id: user_id=%s", fsType.Name(), uidStr)
return nil, nil, syserror.EINVAL
}
- fsopts.userID = uint32(userID)
+ kuid := creds.UserNamespace.MapToKUID(auth.UID(uid))
+ if !kuid.Ok() {
+ ctx.Warningf("fusefs.FilesystemType.GetFilesystem: unmapped uid: %d", uid)
+ return nil, nil, syserror.EINVAL
+ }
+ fsopts.uid = kuid
+ } else {
+ ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option user_id missing")
+ return nil, nil, syserror.EINVAL
}
- if groupIDStr, ok := mopts["group_id"]; ok {
+ if gidStr, ok := mopts["group_id"]; ok {
delete(mopts, "group_id")
- groupID, err := strconv.ParseUint(groupIDStr, 10, 32)
+ gid, err := strconv.ParseUint(gidStr, 10, 32)
if err != nil {
- log.Warningf("%s.GetFilesystem: invalid group_id: group_id=%s", fsType.Name(), groupIDStr)
+ log.Warningf("%s.GetFilesystem: invalid group_id: group_id=%s", fsType.Name(), gidStr)
+ return nil, nil, syserror.EINVAL
+ }
+ kgid := creds.UserNamespace.MapToKGID(auth.GID(gid))
+ if !kgid.Ok() {
+ ctx.Warningf("fusefs.FilesystemType.GetFilesystem: unmapped gid: %d", gid)
return nil, nil, syserror.EINVAL
}
- fsopts.groupID = uint32(groupID)
+ fsopts.gid = kgid
+ } else {
+ ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option group_id missing")
+ return nil, nil, syserror.EINVAL
}
- rootMode := linux.FileMode(0777)
- modeStr, ok := mopts["rootmode"]
- if ok {
+ if modeStr, ok := mopts["rootmode"]; ok {
delete(mopts, "rootmode")
mode, err := strconv.ParseUint(modeStr, 8, 32)
if err != nil {
log.Warningf("%s.GetFilesystem: invalid mode: %q", fsType.Name(), modeStr)
return nil, nil, syserror.EINVAL
}
- rootMode = linux.FileMode(mode)
+ fsopts.rootMode = linux.FileMode(mode)
+ } else {
+ ctx.Warningf("fusefs.FilesystemType.GetFilesystem: mandatory mount option rootmode missing")
+ return nil, nil, syserror.EINVAL
}
- fsopts.rootMode = rootMode
// Set the maxInFlightRequests option.
fsopts.maxActiveRequests = maxActiveRequestsDefault
@@ -195,6 +216,16 @@ func (fsType FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
fsopts.maxRead = math.MaxUint32
}
+ if _, ok := mopts["default_permissions"]; ok {
+ delete(mopts, "default_permissions")
+ fsopts.defaultPermissions = true
+ }
+
+ if _, ok := mopts["allow_other"]; ok {
+ delete(mopts, "allow_other")
+ fsopts.allowOther = true
+ }
+
// Check for unparsed options.
if len(mopts) != 0 {
log.Warningf("%s.GetFilesystem: unsupported or unknown options: %v", fsType.Name(), mopts)
@@ -326,6 +357,37 @@ func (fs *filesystem) newInode(ctx context.Context, nodeID uint64, attr linux.FU
return i
}
+// CheckPermissions implements kernfs.Inode.CheckPermissions.
+func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
+ // Since FUSE operations are ultimately backed by a userspace process (the
+ // fuse daemon), allowing a process to call into fusefs grants the daemon
+ // ptrace-like capabilities over the calling process. Because of this, by
+ // default FUSE only allows the mount owner to interact with the
+ // filesystem. This explicitly excludes setuid/setgid processes.
+ //
+ // This behaviour can be overriden with the 'allow_other' mount option.
+ //
+ // See fs/fuse/dir.c:fuse_allow_current_process() in Linux.
+ if !i.fs.opts.allowOther {
+ if creds.RealKUID != i.fs.opts.uid ||
+ creds.EffectiveKUID != i.fs.opts.uid ||
+ creds.SavedKUID != i.fs.opts.uid ||
+ creds.RealKGID != i.fs.opts.gid ||
+ creds.EffectiveKGID != i.fs.opts.gid ||
+ creds.SavedKGID != i.fs.opts.gid {
+ return syserror.EACCES
+ }
+ }
+
+ // By default, fusefs delegates all permission checks to the server.
+ // However, standard unix permission checks can be enabled with the
+ // default_permissions mount option.
+ if i.fs.opts.defaultPermissions {
+ return i.InodeAttrs.CheckPermissions(ctx, creds, ats)
+ }
+ return nil
+}
+
// Open implements kernfs.Inode.Open.
func (i *inode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
isDir := i.InodeAttrs.Mode().IsDir()