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 }