diff options
Diffstat (limited to 'pkg/sentry/fsimpl/fuse')
-rw-r--r-- | pkg/sentry/fsimpl/fuse/fusefs.go | 118 |
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() |