/* * This is a variant of fs/namei.c:permission() or inode_permission() which * skips over EROFS tests (because we perform copyup on EROFS). */ static int __inode_permission(struct inode *inode, int mask) { int retval; /* nobody gets write access to an immutable file */ if ((mask & MAY_WRITE) && IS_IMMUTABLE(inode)) return -EACCES; /* Ordinary permission routines do not understand MAY_APPEND. */ if (inode->i_op && inode->i_op->permission) { retval = inode->i_op->permission(inode, mask); if (!retval) { /* * Exec permission on a regular file is denied if none * of the execute bits are set. * * This check should be done by the ->permission() * method. */ if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode) && !(inode->i_mode & S_IXUGO)) return -EACCES; } } else { retval = generic_permission(inode, mask); } if (retval) return retval; return security_inode_permission(inode, mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND)); }
static int h_permission(struct inode *h_inode, int mask, struct vfsmount *h_mnt, int brperm) { int err; const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); err = -EACCES; if ((write_mask && IS_IMMUTABLE(h_inode)) || ((mask & MAY_EXEC) && S_ISREG(h_inode->i_mode) && ((h_mnt->mnt_flags & MNT_NOEXEC) || !(h_inode->i_mode & S_IXUGO)))) goto out; /* * - skip the lower fs test in the case of write to ro branch. * - nfs dir permission write check is optimized, but a policy for * link/rename requires a real check. * - nfs always sets MS_POSIXACL regardless its mount option 'noacl.' * in this case, generic_permission() returns -EOPNOTSUPP. */ if ((write_mask && !au_br_writable(brperm)) || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) && write_mask && !(mask & MAY_READ)) || !h_inode->i_op->permission) { /* AuLabel(generic_permission); */ /* AuDbg("get_acl %pf\n", h_inode->i_op->get_acl); */ err = generic_permission(h_inode, mask); if (err == -EOPNOTSUPP && au_test_nfs_noacl(h_inode)) err = h_inode->i_op->permission(h_inode, mask); AuTraceErr(err); } else { /* AuLabel(h_inode->permission); */ err = h_inode->i_op->permission(h_inode, mask); AuTraceErr(err); } if (!err) err = devcgroup_inode_permission(h_inode, mask); if (!err) err = security_inode_permission(h_inode, mask); #if 0 if (!err) { /* todo: do we need to call ima_path_check()? */ struct path h_path = { .dentry = .mnt = h_mnt }; err = ima_path_check(&h_path, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), IMA_COUNT_LEAVE); } #endif out: return err; }
int handle_permission(struct inode *inode, int mask) { int submask; umode_t mode = inode->i_mode; if (mask & MAY_WRITE) { /* * Nobody gets write access to a read-only fs. */ if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; /* * Nobody gets write access to an immutable file. */ if (IS_IMMUTABLE(inode)) return -EACCES; } /* * MAY_EXEC on regular files requires special handling: We override * filesystem execute permissions if the mode bits aren't set. */ if ((mask & MAY_EXEC) && S_ISREG(mode) && !(mode & S_IXUGO)) return -EACCES; /* Ordinary permission routines do not understand MAY_APPEND. */ submask = mask & ~MAY_APPEND; #ifdef IN_KERNEL_CHANGE_NOT_SUPP /* FIXME! we don't have nameidata for handle lookup. So not sure how * we can check for inode->permissions. We already limit the call * to CAP_DAC_OVERRIDE. So we should be able to skip the ACL check. * But we also skip security_inode_permission below. */ if (inode->i_op && inode->i_op->permission) retval = inode->i_op->permission(inode, submask, nd); else retval = generic_permission(inode, submask, NULL); if (retval) return retval; return security_inode_permission(inode, mask, nd); #else return generic_permission(inode, submask, NULL); #endif }
/* Basically copied from the kernel vfs permission(), but we've changed * the following: (1) the IS_RDONLY check is skipped, and (2) if you set * the mount option `nfsperms=insceure', we assume that -EACCES means that * the export is read-only and we should check standard Unix permissions. * This means that NFS ACL checks (or other advanced permission features) * are bypassed. */ static int inode_permission(struct inode *inode, int mask, struct nameidata *nd, int bindex) { int retval, submask; if (mask & MAY_WRITE) { /* The first branch is allowed to be really readonly. */ if (bindex == 0) { umode_t mode = inode->i_mode; if (IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) return -EROFS; } /* * Nobody gets write access to an immutable file. */ if (IS_IMMUTABLE(inode)) return -EACCES; } /* Ordinary permission routines do not understand MAY_APPEND. */ submask = mask & ~MAY_APPEND; if (inode->i_op && inode->i_op->permission) { retval = inode->i_op->permission(inode, submask, nd); if ((retval == -EACCES) && (submask & MAY_WRITE) && (!strcmp("nfs", (inode)->i_sb->s_type->name)) && (nd) && (nd->mnt) && (nd->mnt->mnt_sb) && (branchperms(nd->mnt->mnt_sb, bindex) & MAY_NFSRO)) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) retval = vfs_permission(inode, submask); #else retval = generic_permission(inode, submask, NULL); #endif } } else { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) retval = vfs_permission(inode, submask); #else retval = generic_permission(inode, submask, NULL); #endif } if (retval && retval != -EROFS) /* ignore EROFS */ return retval; retval = security_inode_permission(inode, mask, nd); return ((retval == -EROFS) ? 0 : retval); /* ignore EROFS */ }
int ovl_permission(struct inode *inode, int mask) { struct ovl_entry *oe; struct dentry *alias = NULL; struct inode *realinode; struct dentry *realdentry; bool is_upper; int err; if (S_ISDIR(inode->i_mode)) { oe = inode->i_private; } else if (mask & MAY_NOT_BLOCK) { return -ECHILD; } else { /* * For non-directories find an alias and get the info * from there. */ spin_lock(&inode->i_lock); if (WARN_ON(list_empty(&inode->i_dentry))) { spin_unlock(&inode->i_lock); return -ENOENT; } alias = list_entry(inode->i_dentry.next, struct dentry, d_alias); dget(alias); spin_unlock(&inode->i_lock); oe = alias->d_fsdata; } realdentry = ovl_entry_real(oe, &is_upper); /* Careful in RCU walk mode */ realinode = ACCESS_ONCE(realdentry->d_inode); if (!realinode) { WARN_ON(!(mask & MAY_NOT_BLOCK)); err = -ENOENT; goto out_dput; } if (mask & MAY_WRITE) { umode_t mode = realinode->i_mode; /* * Writes will always be redirected to upper layer, so * ignore lower layer being read-only. * * If the overlay itself is read-only then proceed * with the permission check, don't return EROFS. * This will only happen if this is the lower layer of * another overlayfs. * * If upper fs becomes read-only after the overlay was * constructed return EROFS to prevent modification of * upper layer. */ err = -EROFS; if (is_upper && !IS_RDONLY(inode) && IS_RDONLY(realinode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) goto out_dput; /* * Nobody gets write access to an immutable file. */ err = -EACCES; if (IS_IMMUTABLE(realinode)) goto out_dput; } if (realinode->i_op->permission) err = realinode->i_op->permission(realinode, mask); else err = generic_permission(realinode, mask); if (!err) err = devcgroup_inode_permission(realinode, mask); if (!err) err = security_inode_permission(realinode, mask); out_dput: dput(alias); return err; }