Pass the path point representing the union-layer file into security_file_open() so that the correct security state can be divined - otherwise for overlayfs, only the security state for the lower filesystem can be accessed. This is a stopgap and isn't really the correct solution: the correct solution is to make file->f_path point at the union-layer path point and file->f_inode point at the lower file inode - but this requires the union dentry to pin the lower dentry. Signed-off-by: David Howells <dhowells@xxxxxxxxxx> --- fs/ceph/file.c | 3 ++- fs/ceph/super.h | 1 + fs/cifs/cifsfs.h | 1 + fs/cifs/dir.c | 3 ++- fs/namei.c | 11 +++++++---- fs/nfs/dir.c | 6 ++++-- fs/nfs/nfs4_fs.h | 2 +- fs/open.c | 31 ++++++++++++++++++++----------- fs/overlayfs/inode.c | 8 +++++--- include/linux/fs.h | 16 +++++++++++++--- include/linux/security.h | 8 ++++++-- security/capability.c | 4 +++- security/security.c | 6 ++++-- security/selinux/hooks.c | 3 ++- 14 files changed, 71 insertions(+), 32 deletions(-) diff --git a/fs/ceph/file.c b/fs/ceph/file.c index d7e0da8366e6..43abb473bf84 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -228,6 +228,7 @@ out: * file or symlink, return 1 so the VFS can retry. */ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, + const struct path *union_path, struct file *file, unsigned flags, umode_t mode, int *opened) { @@ -302,7 +303,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry, ceph_init_inode_acls(dentry->d_inode, &acls); *opened |= FILE_CREATED; } - err = finish_open(file, dentry, ceph_open, opened); + err = finish_open(file, dentry, union_path, ceph_open, opened); } out_req: if (!req->r_err && req->r_target_inode) diff --git a/fs/ceph/super.h b/fs/ceph/super.h index b82f507979b8..5120b1a04d33 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -849,6 +849,7 @@ extern const struct address_space_operations ceph_aops; extern int ceph_open(struct inode *inode, struct file *file); extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry, + const struct path *union_path, struct file *file, unsigned flags, umode_t mode, int *opened); extern int ceph_release(struct inode *inode, struct file *filp); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 002e0c173939..0ff63fcbecba 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -59,6 +59,7 @@ extern struct inode *cifs_root_iget(struct super_block *); extern int cifs_create(struct inode *, struct dentry *, umode_t, bool excl); extern int cifs_atomic_open(struct inode *, struct dentry *, + const struct path *, struct file *, unsigned, umode_t, int *); extern struct dentry *cifs_lookup(struct inode *, struct dentry *, diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index b72bc29cba23..356bbd981415 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -414,6 +414,7 @@ out: int cifs_atomic_open(struct inode *inode, struct dentry *direntry, + const struct path *union_path, struct file *file, unsigned oflags, umode_t mode, int *opened) { @@ -489,7 +490,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) *opened |= FILE_CREATED; - rc = finish_open(file, direntry, generic_file_open, opened); + rc = finish_open(file, direntry, union_path, generic_file_open, opened); if (rc) { if (server->ops->close) server->ops->close(xid, tcon, &fid); diff --git a/fs/namei.c b/fs/namei.c index 922f27068c4c..5d1e40c047f9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2647,6 +2647,7 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, int *opened) { struct inode *dir = nd->path.dentry->d_inode; + struct path top; unsigned open_flag = open_to_namei_flags(op->open_flag); umode_t mode; int error; @@ -2712,10 +2713,12 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry, if (nd->flags & LOOKUP_DIRECTORY) open_flag |= O_DIRECTORY; + top.dentry = dentry; + top.mnt = nd->path.mnt; file->f_path.dentry = DENTRY_NOT_SET; file->f_path.mnt = nd->path.mnt; - error = dir->i_op->atomic_open(dir, dentry, file, open_flag, mode, - opened); + error = dir->i_op->atomic_open(dir, dentry, &top, file, + open_flag, mode, opened); if (error < 0) { if (create_error && error == -ENOENT) error = create_error; @@ -3062,7 +3065,7 @@ finish_open_created: goto out; BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ - error = vfs_open(&nd->path, file, current_cred()); + error = vfs_open(&nd->path, &nd->path, file, current_cred()); if (!error) { *opened |= FILE_OPENED; } else { @@ -3158,7 +3161,7 @@ static int do_tmpfile(int dfd, struct filename *pathname, if (error) goto out2; file->f_path.mnt = nd->path.mnt; - error = finish_open(file, nd->path.dentry, NULL, opened); + error = finish_open(file, nd->path.dentry, &nd->path, NULL, opened); if (error) goto out2; error = open_check_o_direct(file); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 06e8cfcbb670..0fd37112dc87 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1446,6 +1446,7 @@ static int do_open(struct inode *inode, struct file *filp) static int nfs_finish_open(struct nfs_open_context *ctx, struct dentry *dentry, + const struct path *union_path, struct file *file, unsigned open_flags, int *opened) { @@ -1454,7 +1455,7 @@ static int nfs_finish_open(struct nfs_open_context *ctx, if ((open_flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) *opened |= FILE_CREATED; - err = finish_open(file, dentry, do_open, opened); + err = finish_open(file, dentry, union_path, do_open, opened); if (err) goto out; nfs_file_set_open_context(file, ctx); @@ -1464,6 +1465,7 @@ out: } int nfs_atomic_open(struct inode *dir, struct dentry *dentry, + const struct path *union_path, struct file *file, unsigned open_flags, umode_t mode, int *opened) { @@ -1542,7 +1544,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry, goto out; } - err = nfs_finish_open(ctx, ctx->dentry, file, open_flags, opened); + err = nfs_finish_open(ctx, ctx->dentry, union_path, file, open_flags, opened); trace_nfs_atomic_open_exit(dir, ctx, open_flags, err); put_nfs_open_context(ctx); out: diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index be6cac37ea10..2b68a876780d 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -212,7 +212,7 @@ struct nfs4_mig_recovery_ops { extern const struct dentry_operations nfs4_dentry_operations; /* dir.c */ -int nfs_atomic_open(struct inode *, struct dentry *, struct file *, +int nfs_atomic_open(struct inode *, struct dentry *, const struct path *, struct file *, unsigned, umode_t, int *); /* super.c */ diff --git a/fs/open.c b/fs/open.c index de92c13b58be..0cb66c96924a 100644 --- a/fs/open.c +++ b/fs/open.c @@ -665,6 +665,7 @@ int open_check_o_direct(struct file *f) } static int do_dentry_open(struct file *f, + const struct path *union_path, int (*open)(struct inode *, struct file *), const struct cred *cred) { @@ -707,7 +708,7 @@ static int do_dentry_open(struct file *f, goto cleanup_all; } - error = security_file_open(f, cred); + error = security_file_open(f, union_path, cred); if (error) goto cleanup_all; @@ -755,6 +756,7 @@ cleanup_file: * finish_open - finish opening a file * @file: file pointer * @dentry: pointer to dentry + * @union_path: path userspace actually asked for * @open: open callback * @opened: state of open * @@ -772,7 +774,9 @@ cleanup_file: * * Returns zero on success or -errno if the open failed. */ -int finish_open(struct file *file, struct dentry *dentry, +int finish_open(struct file *file, + struct dentry *dentry, + const struct path *union_path, int (*open)(struct inode *, struct file *), int *opened) { @@ -780,7 +784,7 @@ int finish_open(struct file *file, struct dentry *dentry, BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */ file->f_path.dentry = dentry; - error = do_dentry_open(file, open, current_cred()); + error = do_dentry_open(file, union_path, open, current_cred()); if (!error) *opened |= FILE_OPENED; @@ -792,7 +796,7 @@ EXPORT_SYMBOL(finish_open); * finish_no_open - finish ->atomic_open() without opening the file * * @file: file pointer - * @dentry: dentry or NULL (as returned from ->lookup()) + * @path: The path of the file actually opened (as returned from ->lookup()) * * This can be used to set the result of a successful lookup in ->atomic_open(). * @@ -809,8 +813,9 @@ int finish_no_open(struct file *file, struct dentry *dentry) } EXPORT_SYMBOL(finish_no_open); -struct file *dentry_open(const struct path *path, int flags, - const struct cred *cred) +struct file *_dentry_open(const struct path *path, + const struct path *union_path, int flags, + const struct cred *cred) { int error; struct file *f; @@ -823,7 +828,7 @@ struct file *dentry_open(const struct path *path, int flags, f = get_empty_filp(); if (!IS_ERR(f)) { f->f_flags = flags; - error = vfs_open(path, f, cred); + error = vfs_open(path, union_path, f, cred); if (!error) { /* from now on we need fput() to dispose of f */ error = open_check_o_direct(f); @@ -838,24 +843,28 @@ struct file *dentry_open(const struct path *path, int flags, } return f; } -EXPORT_SYMBOL(dentry_open); +EXPORT_SYMBOL(_dentry_open); /** * vfs_open - open the file at the given path * @path: path to open + * @union_path: path userspace actually asked for * @filp: newly allocated file with f_flag initialized * @cred: credentials to use */ -int vfs_open(const struct path *path, struct file *filp, +int vfs_open(const struct path *path, + const struct path *union_path, + struct file *filp, const struct cred *cred) { struct inode *inode = path->dentry->d_inode; if (inode->i_op->dentry_open) - return inode->i_op->dentry_open(path->dentry, filp, cred); + return inode->i_op->dentry_open(path->dentry, + union_path, filp, cred); else { filp->f_path = *path; - return do_dentry_open(filp, NULL, cred); + return do_dentry_open(filp, union_path, NULL, cred); } } EXPORT_SYMBOL(vfs_open); diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index af2d18c9fcee..87316e93dbfa 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -324,8 +324,10 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, return true; } -static int ovl_dentry_open(struct dentry *dentry, struct file *file, - const struct cred *cred) +static int ovl_dentry_open(struct dentry *dentry, + const struct path *union_path, + struct file *file, + const struct cred *cred) { int err; struct path realpath; @@ -349,7 +351,7 @@ static int ovl_dentry_open(struct dentry *dentry, struct file *file, ovl_path_upper(dentry, &realpath); } - err = vfs_open(&realpath, file, cred); + err = vfs_open(&realpath, union_path, file, cred); out_drop_write: if (want_write) ovl_drop_write(dentry); diff --git a/include/linux/fs.h b/include/linux/fs.h index 1c12c681803f..6f8768b3cad5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1549,13 +1549,15 @@ struct inode_operations { u64 len); int (*update_time)(struct inode *, struct timespec *, int); int (*atomic_open)(struct inode *, struct dentry *, + const struct path *, struct file *, unsigned open_flag, umode_t create_mode, int *opened); int (*tmpfile) (struct inode *, struct dentry *, umode_t); int (*set_acl)(struct inode *, struct posix_acl *, int); /* WARNING: probably going away soon, do not use! */ - int (*dentry_open)(struct dentry *, struct file *, const struct cred *); + int (*dentry_open)(struct dentry *, const struct path *, + struct file *, const struct cred *); } ____cacheline_aligned; ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, @@ -2071,8 +2073,15 @@ extern struct file *file_open_name(struct filename *, int, umode_t); extern struct file *filp_open(const char *, int, umode_t); extern struct file *file_open_root(struct dentry *, struct vfsmount *, const char *, int); -extern int vfs_open(const struct path *, struct file *, const struct cred *); -extern struct file * dentry_open(const struct path *, int, const struct cred *); +extern int vfs_open(const struct path *, const struct path *, + struct file *, const struct cred *); +extern struct file *_dentry_open(const struct path *, const struct path *, + int, const struct cred *); +static inline struct file *dentry_open(const struct path *path, + int flags, const struct cred *cred) +{ + return _dentry_open(path, path, flags, cred); +} extern int filp_close(struct file *, fl_owner_t id); extern struct filename *getname(const char __user *); @@ -2083,6 +2092,7 @@ enum { FILE_OPENED = 2 }; extern int finish_open(struct file *file, struct dentry *dentry, + const struct path *union_path, int (*open)(struct inode *, struct file *), int *opened); extern int finish_no_open(struct file *file, struct dentry *dentry); diff --git a/include/linux/security.h b/include/linux/security.h index 637a24c75d46..78fa30f5d708 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1584,7 +1584,9 @@ struct security_operations { int (*file_send_sigiotask) (struct task_struct *tsk, struct fown_struct *fown, int sig); int (*file_receive) (struct file *file); - int (*file_open) (struct file *file, const struct cred *cred); + int (*file_open) (struct file *file, + const struct path *union_path, + const struct cred *cred); int (*task_create) (unsigned long clone_flags); void (*task_free) (struct task_struct *task); @@ -1863,7 +1865,8 @@ void security_file_set_fowner(struct file *file); int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig); int security_file_receive(struct file *file); -int security_file_open(struct file *file, const struct cred *cred); +int security_file_open(struct file *file, const struct path *union_path, + const struct cred *cred); int security_task_create(unsigned long clone_flags); void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); @@ -2365,6 +2368,7 @@ static inline int security_file_receive(struct file *file) } static inline int security_file_open(struct file *file, + const struct path *union_path, const struct cred *cred) { return 0; diff --git a/security/capability.c b/security/capability.c index 6b21615d1500..10dacb48ff53 100644 --- a/security/capability.c +++ b/security/capability.c @@ -370,7 +370,9 @@ static int cap_file_receive(struct file *file) return 0; } -static int cap_file_open(struct file *file, const struct cred *cred) +static int cap_file_open(struct file *file, + const struct path *union_path, + const struct cred *cred) { return 0; } diff --git a/security/security.c b/security/security.c index 96e2f189ff1e..44b889a88d18 100644 --- a/security/security.c +++ b/security/security.c @@ -804,11 +804,13 @@ int security_file_receive(struct file *file) return security_ops->file_receive(file); } -int security_file_open(struct file *file, const struct cred *cred) +int security_file_open(struct file *file, + const struct path *union_path, + const struct cred *cred) { int ret; - ret = security_ops->file_open(file, cred); + ret = security_ops->file_open(file, union_path, cred); if (ret) return ret; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f3fe7dbbf741..6fd8090cc7a5 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3431,7 +3431,8 @@ static int selinux_file_receive(struct file *file) return file_has_perm(cred, file, file_to_av(file)); } -static int selinux_file_open(struct file *file, const struct cred *cred) +static int selinux_file_open(struct file *file, const struct path *union_path, + const struct cred *cred) { struct file_security_struct *fsec; struct inode_security_struct *isec; -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html