github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/vfs/permissions.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package vfs
    16  
    17  import (
    18  	"math"
    19  	"strings"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/context"
    23  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/kernel/auth"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/limits"
    26  	"github.com/SagerNet/gvisor/pkg/syserror"
    27  )
    28  
    29  // AccessTypes is a bitmask of Unix file permissions.
    30  //
    31  // +stateify savable
    32  type AccessTypes uint16
    33  
    34  // Bits in AccessTypes.
    35  const (
    36  	MayExec  AccessTypes = 1
    37  	MayWrite AccessTypes = 2
    38  	MayRead  AccessTypes = 4
    39  )
    40  
    41  // OnlyRead returns true if access _only_ allows read.
    42  func (a AccessTypes) OnlyRead() bool {
    43  	return a == MayRead
    44  }
    45  
    46  // MayRead returns true if access allows read.
    47  func (a AccessTypes) MayRead() bool {
    48  	return a&MayRead != 0
    49  }
    50  
    51  // MayWrite returns true if access allows write.
    52  func (a AccessTypes) MayWrite() bool {
    53  	return a&MayWrite != 0
    54  }
    55  
    56  // MayExec returns true if access allows exec.
    57  func (a AccessTypes) MayExec() bool {
    58  	return a&MayExec != 0
    59  }
    60  
    61  // GenericCheckPermissions checks that creds has the given access rights on a
    62  // file with the given permissions, UID, and GID, subject to the rules of
    63  // fs/namei.c:generic_permission().
    64  func GenericCheckPermissions(creds *auth.Credentials, ats AccessTypes, mode linux.FileMode, kuid auth.KUID, kgid auth.KGID) error {
    65  	// Check permission bits.
    66  	perms := uint16(mode.Permissions())
    67  	if creds.EffectiveKUID == kuid {
    68  		perms >>= 6
    69  	} else if creds.InGroup(kgid) {
    70  		perms >>= 3
    71  	}
    72  	if uint16(ats)&perms == uint16(ats) {
    73  		// All permission bits match, access granted.
    74  		return nil
    75  	}
    76  
    77  	// Caller capabilities require that the file's KUID and KGID are mapped in
    78  	// the caller's user namespace; compare
    79  	// kernel/capability.c:privileged_wrt_inode_uidgid().
    80  	if !kuid.In(creds.UserNamespace).Ok() || !kgid.In(creds.UserNamespace).Ok() {
    81  		return linuxerr.EACCES
    82  	}
    83  	// CAP_DAC_READ_SEARCH allows the caller to read and search arbitrary
    84  	// directories, and read arbitrary non-directory files.
    85  	if (mode.IsDir() && !ats.MayWrite()) || ats.OnlyRead() {
    86  		if creds.HasCapability(linux.CAP_DAC_READ_SEARCH) {
    87  			return nil
    88  		}
    89  	}
    90  	// CAP_DAC_OVERRIDE allows arbitrary access to directories, read/write
    91  	// access to non-directory files, and execute access to non-directory files
    92  	// for which at least one execute bit is set.
    93  	if mode.IsDir() || !ats.MayExec() || (mode.Permissions()&0111 != 0) {
    94  		if creds.HasCapability(linux.CAP_DAC_OVERRIDE) {
    95  			return nil
    96  		}
    97  	}
    98  	return linuxerr.EACCES
    99  }
   100  
   101  // MayLink determines whether creating a hard link to a file with the given
   102  // mode, kuid, and kgid is permitted.
   103  //
   104  // This corresponds to Linux's fs/namei.c:may_linkat.
   105  func MayLink(creds *auth.Credentials, mode linux.FileMode, kuid auth.KUID, kgid auth.KGID) error {
   106  	// Source inode owner can hardlink all they like; otherwise, it must be a
   107  	// safe source.
   108  	if CanActAsOwner(creds, kuid) {
   109  		return nil
   110  	}
   111  
   112  	// Only regular files can be hard linked.
   113  	if mode.FileType() != linux.S_IFREG {
   114  		return linuxerr.EPERM
   115  	}
   116  
   117  	// Setuid files should not get pinned to the filesystem.
   118  	if mode&linux.S_ISUID != 0 {
   119  		return linuxerr.EPERM
   120  	}
   121  
   122  	// Executable setgid files should not get pinned to the filesystem, but we
   123  	// don't support S_IXGRP anyway.
   124  
   125  	// Hardlinking to unreadable or unwritable sources is dangerous.
   126  	if err := GenericCheckPermissions(creds, MayRead|MayWrite, mode, kuid, kgid); err != nil {
   127  		return linuxerr.EPERM
   128  	}
   129  	return nil
   130  }
   131  
   132  // AccessTypesForOpenFlags returns the access types required to open a file
   133  // with the given OpenOptions.Flags. Note that this is NOT the same thing as
   134  // the set of accesses permitted for the opened file:
   135  //
   136  // - O_TRUNC causes MayWrite to be set in the returned AccessTypes (since it
   137  // mutates the file), but does not permit writing to the open file description
   138  // thereafter.
   139  //
   140  // - "Linux reserves the special, nonstandard access mode 3 (binary 11) in
   141  // flags to mean: check for read and write permission on the file and return a
   142  // file descriptor that can't be used for reading or writing." - open(2). Thus
   143  // AccessTypesForOpenFlags returns MayRead|MayWrite in this case.
   144  //
   145  // Use May{Read,Write}FileWithOpenFlags() for these checks instead.
   146  func AccessTypesForOpenFlags(opts *OpenOptions) AccessTypes {
   147  	ats := AccessTypes(0)
   148  	if opts.FileExec {
   149  		ats |= MayExec
   150  	}
   151  
   152  	switch opts.Flags & linux.O_ACCMODE {
   153  	case linux.O_RDONLY:
   154  		if opts.Flags&linux.O_TRUNC != 0 {
   155  			return ats | MayRead | MayWrite
   156  		}
   157  		return ats | MayRead
   158  	case linux.O_WRONLY:
   159  		return ats | MayWrite
   160  	default:
   161  		return ats | MayRead | MayWrite
   162  	}
   163  }
   164  
   165  // MayReadFileWithOpenFlags returns true if a file with the given open flags
   166  // should be readable.
   167  func MayReadFileWithOpenFlags(flags uint32) bool {
   168  	switch flags & linux.O_ACCMODE {
   169  	case linux.O_RDONLY, linux.O_RDWR:
   170  		return true
   171  	default:
   172  		return false
   173  	}
   174  }
   175  
   176  // MayWriteFileWithOpenFlags returns true if a file with the given open flags
   177  // should be writable.
   178  func MayWriteFileWithOpenFlags(flags uint32) bool {
   179  	switch flags & linux.O_ACCMODE {
   180  	case linux.O_WRONLY, linux.O_RDWR:
   181  		return true
   182  	default:
   183  		return false
   184  	}
   185  }
   186  
   187  // CheckSetStat checks that creds has permission to change the metadata of a
   188  // file with the given permissions, UID, and GID as specified by stat, subject
   189  // to the rules of Linux's fs/attr.c:setattr_prepare().
   190  func CheckSetStat(ctx context.Context, creds *auth.Credentials, opts *SetStatOptions, mode linux.FileMode, kuid auth.KUID, kgid auth.KGID) error {
   191  	stat := &opts.Stat
   192  	if stat.Mask&linux.STATX_SIZE != 0 {
   193  		limit, err := CheckLimit(ctx, 0, int64(stat.Size))
   194  		if err != nil {
   195  			return err
   196  		}
   197  		if limit < int64(stat.Size) {
   198  			return syserror.ErrExceedsFileSizeLimit
   199  		}
   200  	}
   201  	if stat.Mask&linux.STATX_MODE != 0 {
   202  		if !CanActAsOwner(creds, kuid) {
   203  			return linuxerr.EPERM
   204  		}
   205  		// TODO(b/30815691): "If the calling process is not privileged (Linux:
   206  		// does not have the CAP_FSETID capability), and the group of the file
   207  		// does not match the effective group ID of the process or one of its
   208  		// supplementary group IDs, the S_ISGID bit will be turned off, but
   209  		// this will not cause an error to be returned." - chmod(2)
   210  	}
   211  	if stat.Mask&linux.STATX_UID != 0 {
   212  		if !((creds.EffectiveKUID == kuid && auth.KUID(stat.UID) == kuid) ||
   213  			HasCapabilityOnFile(creds, linux.CAP_CHOWN, kuid, kgid)) {
   214  			return linuxerr.EPERM
   215  		}
   216  	}
   217  	if stat.Mask&linux.STATX_GID != 0 {
   218  		if !((creds.EffectiveKUID == kuid && creds.InGroup(auth.KGID(stat.GID))) ||
   219  			HasCapabilityOnFile(creds, linux.CAP_CHOWN, kuid, kgid)) {
   220  			return linuxerr.EPERM
   221  		}
   222  	}
   223  	if opts.NeedWritePerm && !creds.HasCapability(linux.CAP_DAC_OVERRIDE) {
   224  		if err := GenericCheckPermissions(creds, MayWrite, mode, kuid, kgid); err != nil {
   225  			return err
   226  		}
   227  	}
   228  	if stat.Mask&(linux.STATX_ATIME|linux.STATX_MTIME|linux.STATX_CTIME) != 0 {
   229  		if !CanActAsOwner(creds, kuid) {
   230  			if (stat.Mask&linux.STATX_ATIME != 0 && stat.Atime.Nsec != linux.UTIME_NOW) ||
   231  				(stat.Mask&linux.STATX_MTIME != 0 && stat.Mtime.Nsec != linux.UTIME_NOW) ||
   232  				(stat.Mask&linux.STATX_CTIME != 0 && stat.Ctime.Nsec != linux.UTIME_NOW) {
   233  				return linuxerr.EPERM
   234  			}
   235  			if err := GenericCheckPermissions(creds, MayWrite, mode, kuid, kgid); err != nil {
   236  				return err
   237  			}
   238  		}
   239  	}
   240  	return nil
   241  }
   242  
   243  // CheckDeleteSticky checks whether the sticky bit is set on a directory with
   244  // the given file mode, and if so, checks whether creds has permission to
   245  // remove a file owned by childKUID from a directory with the given mode.
   246  // CheckDeleteSticky is consistent with fs/linux.h:check_sticky().
   247  func CheckDeleteSticky(creds *auth.Credentials, parentMode linux.FileMode, parentKUID auth.KUID, childKUID auth.KUID, childKGID auth.KGID) error {
   248  	if parentMode&linux.ModeSticky == 0 {
   249  		return nil
   250  	}
   251  	if creds.EffectiveKUID == childKUID ||
   252  		creds.EffectiveKUID == parentKUID ||
   253  		HasCapabilityOnFile(creds, linux.CAP_FOWNER, childKUID, childKGID) {
   254  		return nil
   255  	}
   256  	return linuxerr.EPERM
   257  }
   258  
   259  // CanActAsOwner returns true if creds can act as the owner of a file with the
   260  // given owning UID, consistent with Linux's
   261  // fs/inode.c:inode_owner_or_capable().
   262  func CanActAsOwner(creds *auth.Credentials, kuid auth.KUID) bool {
   263  	if creds.EffectiveKUID == kuid {
   264  		return true
   265  	}
   266  	return creds.HasCapability(linux.CAP_FOWNER) && creds.UserNamespace.MapFromKUID(kuid).Ok()
   267  }
   268  
   269  // HasCapabilityOnFile returns true if creds has the given capability with
   270  // respect to a file with the given owning UID and GID, consistent with Linux's
   271  // kernel/capability.c:capable_wrt_inode_uidgid().
   272  func HasCapabilityOnFile(creds *auth.Credentials, cp linux.Capability, kuid auth.KUID, kgid auth.KGID) bool {
   273  	return creds.HasCapability(cp) && creds.UserNamespace.MapFromKUID(kuid).Ok() && creds.UserNamespace.MapFromKGID(kgid).Ok()
   274  }
   275  
   276  // CheckLimit enforces file size rlimits. It returns error if the write
   277  // operation must not proceed. Otherwise it returns the max length allowed to
   278  // without violating the limit.
   279  func CheckLimit(ctx context.Context, offset, size int64) (int64, error) {
   280  	fileSizeLimit := limits.FromContextOrDie(ctx).Get(limits.FileSize).Cur
   281  	if fileSizeLimit > math.MaxInt64 {
   282  		return size, nil
   283  	}
   284  	if offset >= int64(fileSizeLimit) {
   285  		return 0, syserror.ErrExceedsFileSizeLimit
   286  	}
   287  	remaining := int64(fileSizeLimit) - offset
   288  	if remaining < size {
   289  		return remaining, nil
   290  	}
   291  	return size, nil
   292  }
   293  
   294  // CheckXattrPermissions checks permissions for extended attribute access.
   295  // This is analogous to fs/xattr.c:xattr_permission(). Some key differences:
   296  // * Does not check for read-only filesystem property.
   297  // * Does not check inode immutability or append only mode. In both cases EPERM
   298  //   must be returned by filesystem implementations.
   299  // * Does not do inode permission checks. Filesystem implementations should
   300  //   handle inode permission checks as they may differ across implementations.
   301  func CheckXattrPermissions(creds *auth.Credentials, ats AccessTypes, mode linux.FileMode, kuid auth.KUID, name string) error {
   302  	switch {
   303  	case strings.HasPrefix(name, linux.XATTR_TRUSTED_PREFIX):
   304  		// The trusted.* namespace can only be accessed by privileged
   305  		// users.
   306  		if creds.HasCapability(linux.CAP_SYS_ADMIN) {
   307  			return nil
   308  		}
   309  		if ats.MayWrite() {
   310  			return linuxerr.EPERM
   311  		}
   312  		return linuxerr.ENODATA
   313  	case strings.HasPrefix(name, linux.XATTR_USER_PREFIX):
   314  		// In the user.* namespace, only regular files and directories can have
   315  		// extended attributes. For sticky directories, only the owner and
   316  		// privileged users can write attributes.
   317  		filetype := mode.FileType()
   318  		if filetype != linux.ModeRegular && filetype != linux.ModeDirectory {
   319  			if ats.MayWrite() {
   320  				return linuxerr.EPERM
   321  			}
   322  			return linuxerr.ENODATA
   323  		}
   324  		if filetype == linux.ModeDirectory && mode&linux.ModeSticky != 0 && ats.MayWrite() && !CanActAsOwner(creds, kuid) {
   325  			return linuxerr.EPERM
   326  		}
   327  	}
   328  	return nil
   329  }
   330  
   331  // ClearSUIDAndSGID clears the setuid and/or setgid bits after a chown or write.
   332  // Depending on the mode, neither bit, only the setuid bit, or both are cleared.
   333  func ClearSUIDAndSGID(mode uint32) uint32 {
   334  	// Directories don't have their bits changed.
   335  	if mode&linux.ModeDirectory == linux.ModeDirectory {
   336  		return mode
   337  	}
   338  
   339  	// Changing owners always disables the setuid bit. It disables
   340  	// the setgid bit when the file is executable.
   341  	mode &= ^uint32(linux.ModeSetUID)
   342  	if sgid := uint32(linux.ModeSetGID | linux.ModeGroupExec); mode&sgid == sgid {
   343  		mode &= ^uint32(linux.ModeSetGID)
   344  	}
   345  	return mode
   346  }