|
@@ -226,6 +226,16 @@ int generic_permission(struct inode *inode, int mask,
|
|
|
return -EACCES;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * inode_permission - check for access rights to a given inode
|
|
|
+ * @inode: inode to check permission on
|
|
|
+ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
|
|
|
+ *
|
|
|
+ * Used to check for read/write/execute permissions on an inode.
|
|
|
+ * We use "fsuid" for this, letting us set arbitrary permissions
|
|
|
+ * for filesystem access without changing the "normal" uids which
|
|
|
+ * are used for other things.
|
|
|
+ */
|
|
|
int inode_permission(struct inode *inode, int mask)
|
|
|
{
|
|
|
int retval;
|
|
@@ -247,7 +257,6 @@ int inode_permission(struct inode *inode, int mask)
|
|
|
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);
|
|
|
else
|
|
@@ -264,21 +273,6 @@ int inode_permission(struct inode *inode, int mask)
|
|
|
mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * vfs_permission - check for access rights to a given path
|
|
|
- * @nd: lookup result that describes the path
|
|
|
- * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
|
|
|
- *
|
|
|
- * Used to check for read/write/execute permissions on a path.
|
|
|
- * We use "fsuid" for this, letting us set arbitrary permissions
|
|
|
- * for filesystem access without changing the "normal" uids which
|
|
|
- * are used for other things.
|
|
|
- */
|
|
|
-int vfs_permission(struct nameidata *nd, int mask)
|
|
|
-{
|
|
|
- return inode_permission(nd->path.dentry->d_inode, mask);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* file_permission - check for additional access rights to a given file
|
|
|
* @file: file to check access rights for
|
|
@@ -289,7 +283,7 @@ int vfs_permission(struct nameidata *nd, int mask)
|
|
|
*
|
|
|
* Note:
|
|
|
* Do not use this function in new code. All access checks should
|
|
|
- * be done using vfs_permission().
|
|
|
+ * be done using inode_permission().
|
|
|
*/
|
|
|
int file_permission(struct file *file, int mask)
|
|
|
{
|
|
@@ -527,18 +521,6 @@ out_unlock:
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-/* SMP-safe */
|
|
|
-static __always_inline void
|
|
|
-walk_init_root(const char *name, struct nameidata *nd)
|
|
|
-{
|
|
|
- struct fs_struct *fs = current->fs;
|
|
|
-
|
|
|
- read_lock(&fs->lock);
|
|
|
- nd->path = fs->root;
|
|
|
- path_get(&fs->root);
|
|
|
- read_unlock(&fs->lock);
|
|
|
-}
|
|
|
-
|
|
|
/*
|
|
|
* Wrapper to retry pathname resolution whenever the underlying
|
|
|
* file system returns an ESTALE.
|
|
@@ -576,9 +558,16 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
|
|
|
goto fail;
|
|
|
|
|
|
if (*link == '/') {
|
|
|
+ struct fs_struct *fs = current->fs;
|
|
|
+
|
|
|
path_put(&nd->path);
|
|
|
- walk_init_root(link, nd);
|
|
|
+
|
|
|
+ read_lock(&fs->lock);
|
|
|
+ nd->path = fs->root;
|
|
|
+ path_get(&fs->root);
|
|
|
+ read_unlock(&fs->lock);
|
|
|
}
|
|
|
+
|
|
|
res = link_path_walk(link, nd);
|
|
|
if (nd->depth || res || nd->last_type!=LAST_NORM)
|
|
|
return res;
|
|
@@ -859,7 +848,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
|
|
|
nd->flags |= LOOKUP_CONTINUE;
|
|
|
err = exec_permission_lite(inode);
|
|
|
if (err == -EAGAIN)
|
|
|
- err = vfs_permission(nd, MAY_EXEC);
|
|
|
+ err = inode_permission(nd->path.dentry->d_inode,
|
|
|
+ MAY_EXEC);
|
|
|
if (err)
|
|
|
break;
|
|
|
|
|
@@ -1493,9 +1483,9 @@ int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-int may_open(struct nameidata *nd, int acc_mode, int flag)
|
|
|
+int may_open(struct path *path, int acc_mode, int flag)
|
|
|
{
|
|
|
- struct dentry *dentry = nd->path.dentry;
|
|
|
+ struct dentry *dentry = path->dentry;
|
|
|
struct inode *inode = dentry->d_inode;
|
|
|
int error;
|
|
|
|
|
@@ -1516,13 +1506,13 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
|
|
|
if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
|
|
flag &= ~O_TRUNC;
|
|
|
} else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
|
|
|
- if (nd->path.mnt->mnt_flags & MNT_NODEV)
|
|
|
+ if (path->mnt->mnt_flags & MNT_NODEV)
|
|
|
return -EACCES;
|
|
|
|
|
|
flag &= ~O_TRUNC;
|
|
|
}
|
|
|
|
|
|
- error = vfs_permission(nd, acc_mode);
|
|
|
+ error = inode_permission(inode, acc_mode);
|
|
|
if (error)
|
|
|
return error;
|
|
|
/*
|
|
@@ -1556,6 +1546,9 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
|
|
|
* Refuse to truncate files with mandatory locks held on them.
|
|
|
*/
|
|
|
error = locks_verify_locked(inode);
|
|
|
+ if (!error)
|
|
|
+ error = security_path_truncate(path, 0,
|
|
|
+ ATTR_MTIME|ATTR_CTIME|ATTR_OPEN);
|
|
|
if (!error) {
|
|
|
DQUOT_INIT(inode);
|
|
|
|
|
@@ -1586,14 +1579,18 @@ static int __open_namei_create(struct nameidata *nd, struct path *path,
|
|
|
|
|
|
if (!IS_POSIXACL(dir->d_inode))
|
|
|
mode &= ~current->fs->umask;
|
|
|
+ error = security_path_mknod(&nd->path, path->dentry, mode, 0);
|
|
|
+ if (error)
|
|
|
+ goto out_unlock;
|
|
|
error = vfs_create(dir->d_inode, path->dentry, mode, nd);
|
|
|
+out_unlock:
|
|
|
mutex_unlock(&dir->d_inode->i_mutex);
|
|
|
dput(nd->path.dentry);
|
|
|
nd->path.dentry = path->dentry;
|
|
|
if (error)
|
|
|
return error;
|
|
|
/* Don't check for write permission, don't truncate */
|
|
|
- return may_open(nd, 0, flag & ~O_TRUNC);
|
|
|
+ return may_open(&nd->path, 0, flag & ~O_TRUNC);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1779,7 +1776,7 @@ ok:
|
|
|
if (error)
|
|
|
goto exit;
|
|
|
}
|
|
|
- error = may_open(&nd, acc_mode, flag);
|
|
|
+ error = may_open(&nd.path, acc_mode, flag);
|
|
|
if (error) {
|
|
|
if (will_write)
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
@@ -1999,6 +1996,9 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
|
|
|
error = mnt_want_write(nd.path.mnt);
|
|
|
if (error)
|
|
|
goto out_dput;
|
|
|
+ error = security_path_mknod(&nd.path, dentry, mode, dev);
|
|
|
+ if (error)
|
|
|
+ goto out_drop_write;
|
|
|
switch (mode & S_IFMT) {
|
|
|
case 0: case S_IFREG:
|
|
|
error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
|
|
@@ -2011,6 +2011,7 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
|
|
|
error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
|
|
|
break;
|
|
|
}
|
|
|
+out_drop_write:
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
|
out_dput:
|
|
|
dput(dentry);
|
|
@@ -2070,7 +2071,11 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
|
|
|
error = mnt_want_write(nd.path.mnt);
|
|
|
if (error)
|
|
|
goto out_dput;
|
|
|
+ error = security_path_mkdir(&nd.path, dentry, mode);
|
|
|
+ if (error)
|
|
|
+ goto out_drop_write;
|
|
|
error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
|
|
|
+out_drop_write:
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
|
out_dput:
|
|
|
dput(dentry);
|
|
@@ -2180,7 +2185,11 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|
|
error = mnt_want_write(nd.path.mnt);
|
|
|
if (error)
|
|
|
goto exit3;
|
|
|
+ error = security_path_rmdir(&nd.path, dentry);
|
|
|
+ if (error)
|
|
|
+ goto exit4;
|
|
|
error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
|
|
|
+exit4:
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
|
exit3:
|
|
|
dput(dentry);
|
|
@@ -2265,7 +2274,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
|
|
error = mnt_want_write(nd.path.mnt);
|
|
|
if (error)
|
|
|
goto exit2;
|
|
|
+ error = security_path_unlink(&nd.path, dentry);
|
|
|
+ if (error)
|
|
|
+ goto exit3;
|
|
|
error = vfs_unlink(nd.path.dentry->d_inode, dentry);
|
|
|
+exit3:
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
|
exit2:
|
|
|
dput(dentry);
|
|
@@ -2346,7 +2359,11 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
|
|
|
error = mnt_want_write(nd.path.mnt);
|
|
|
if (error)
|
|
|
goto out_dput;
|
|
|
+ error = security_path_symlink(&nd.path, dentry, from);
|
|
|
+ if (error)
|
|
|
+ goto out_drop_write;
|
|
|
error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
|
|
|
+out_drop_write:
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
|
out_dput:
|
|
|
dput(dentry);
|
|
@@ -2443,7 +2460,11 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
|
|
|
error = mnt_want_write(nd.path.mnt);
|
|
|
if (error)
|
|
|
goto out_dput;
|
|
|
+ error = security_path_link(old_path.dentry, &nd.path, new_dentry);
|
|
|
+ if (error)
|
|
|
+ goto out_drop_write;
|
|
|
error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
|
|
|
+out_drop_write:
|
|
|
mnt_drop_write(nd.path.mnt);
|
|
|
out_dput:
|
|
|
dput(new_dentry);
|
|
@@ -2679,8 +2700,13 @@ asmlinkage long sys_renameat(int olddfd, const char __user *oldname,
|
|
|
error = mnt_want_write(oldnd.path.mnt);
|
|
|
if (error)
|
|
|
goto exit5;
|
|
|
+ error = security_path_rename(&oldnd.path, old_dentry,
|
|
|
+ &newnd.path, new_dentry);
|
|
|
+ if (error)
|
|
|
+ goto exit6;
|
|
|
error = vfs_rename(old_dir->d_inode, old_dentry,
|
|
|
new_dir->d_inode, new_dentry);
|
|
|
+exit6:
|
|
|
mnt_drop_write(oldnd.path.mnt);
|
|
|
exit5:
|
|
|
dput(new_dentry);
|
|
@@ -2750,13 +2776,16 @@ int vfs_follow_link(struct nameidata *nd, const char *link)
|
|
|
/* get the link contents into pagecache */
|
|
|
static char *page_getlink(struct dentry * dentry, struct page **ppage)
|
|
|
{
|
|
|
- struct page * page;
|
|
|
+ char *kaddr;
|
|
|
+ struct page *page;
|
|
|
struct address_space *mapping = dentry->d_inode->i_mapping;
|
|
|
page = read_mapping_page(mapping, 0, NULL);
|
|
|
if (IS_ERR(page))
|
|
|
return (char*)page;
|
|
|
*ppage = page;
|
|
|
- return kmap(page);
|
|
|
+ kaddr = kmap(page);
|
|
|
+ nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1);
|
|
|
+ return kaddr;
|
|
|
}
|
|
|
|
|
|
int page_readlink(struct dentry *dentry, char __user *buffer, int buflen)
|
|
@@ -2849,7 +2878,6 @@ EXPORT_SYMBOL(path_lookup);
|
|
|
EXPORT_SYMBOL(kern_path);
|
|
|
EXPORT_SYMBOL(vfs_path_lookup);
|
|
|
EXPORT_SYMBOL(inode_permission);
|
|
|
-EXPORT_SYMBOL(vfs_permission);
|
|
|
EXPORT_SYMBOL(file_permission);
|
|
|
EXPORT_SYMBOL(unlock_rename);
|
|
|
EXPORT_SYMBOL(vfs_create);
|
|
@@ -2865,3 +2893,10 @@ EXPORT_SYMBOL(vfs_symlink);
|
|
|
EXPORT_SYMBOL(vfs_unlink);
|
|
|
EXPORT_SYMBOL(dentry_unhash);
|
|
|
EXPORT_SYMBOL(generic_readlink);
|
|
|
+
|
|
|
+/* to be mentioned only in INIT_TASK */
|
|
|
+struct fs_struct init_fs = {
|
|
|
+ .count = ATOMIC_INIT(1),
|
|
|
+ .lock = __RW_LOCK_UNLOCKED(init_fs.lock),
|
|
|
+ .umask = 0022,
|
|
|
+};
|