AppArmor security module overview
Subsystems (22)
cover AppArmor security module overview
PATCH 1/45 Pass struct vfsmount to the inode_create LSM hook
This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 9 ++++++--- security/dummy.c | 2 +- security/security.c | 5 +++-- security/selinux/hooks.c | 3 ++- 5 files changed, 13 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -1584,7 +1584,7 @@ int vfs_create(struct inode *dir, structreturn -EACCES ;/* shouldn't it be ENOSYS? */ mode &=S_IALLUGO ; mode |=S_IFREG ; - error =security_inode_create (dir, dentry, mode); + error =security_inode_create (dir, dentry, nd ? nd->mnt :NULL , mode);if (error)return error;DQUOT_INIT (dir);
include/linux/security.h +6 -3
@@ -297,6 +297,7 @@ struct request_sock;* Check permission to create a regular file. * @dir contains inode structure of the parent of the new file. * @dentry contains the dentry structure for the file to be created. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @mode contains the file mode of the file to be created. * Return 0 if permission is granted. * @inode_link:
@@ -1234,8 +1235,8 @@ struct security_operations {void (*inode_free_security) (struct inode *inode);int (*inode_init_security) (struct inode *inode,struct inode *dir,char **name,void **value,size_t *len); -int (*inode_create) (struct inode *dir, -struct dentry *dentry,int mode); +int (*inode_create) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode);int (*inode_link) (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry);int (*inode_unlink) (struct inode *dir,struct dentry *dentry);
@@ -1490,7 +1491,8 @@ int security_inode_alloc(struct inode *ivoid security_inode_free (struct inode *inode);int security_inode_init_security (struct inode *inode,struct inode *dir,char **name,void **value,size_t *len); -int security_inode_create (struct inode *dir,struct dentry *dentry,int mode); +int security_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode);int security_inode_link (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry);int security_inode_unlink (struct inode *dir,struct dentry *dentry);
@@ -1800,6 +1802,7 @@ static inline int security_inode_init_sestatic inline int security_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {return 0 ;
security/dummy.c +1 -1
@@ -262,7 +262,7 @@ static int dummy_inode_init_security (st }static int dummy_inode_create (struct inode *inode,struct dentry *dentry, -int mask) +struct vfsmount *mnt,int mask) {return 0 ; }
security/security.c +3 -2
@@ -328,11 +328,12 @@ int security_inode_init_security(struct }EXPORT_SYMBOL (security_inode_init_security); -int security_inode_create (struct inode *dir,struct dentry *dentry,int mode) +int security_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_create (dir, dentry, mode); +return security_ops->inode_create (dir, dentry, mnt, mode); } int security_inode_link(struct dentry *old_dentry, struct inode *dir,
security/selinux/hooks.c +2 -1
@@ -2184,7 +2184,8 @@ static int selinux_inode_init_security(sreturn 0 ; } -static int selinux_inode_create (struct inode *dir,struct dentry *dentry,int mask) +static int selinux_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) {return may_create (dir, dentry,SECCLASS_FILE ); }
PATCH 2/45 Pass struct path down to remove_suid and children
In reply to [email protected]Required by a later patch that adds a struct vfsmount parameter to notify_change().
--- fs/ntfs/file.c | 2 +- fs/splice.c | 4 ++-- fs/xfs/linux-2.6/xfs_lrw.c | 2 +- include/linux/fs.h | 4 ++-- mm/filemap.c | 16 ++++++++-------- mm/filemap_xip.c | 2 +- mm/shmem.c | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-)
fs/ntfs/file.c +1 -1
@@ -2121,7 +2121,7 @@ static ssize_t ntfs_file_aio_write_noloc goto out;if (!count) goto out; - err =remove_suid (file->f_path.dentry); + err =remove_suid (&file->f_path);if (err) goto out;file_update_time (file);
fs/splice.c +2 -2
@@ -782,7 +782,7 @@ generic_file_splice_write_nolock(structssize_t ret;int err; - err =remove_suid (out->f_path.dentry); + err =remove_suid (&out->f_path);if (unlikely (err))return err;
@@ -842,7 +842,7 @@ generic_file_splice_write(struct pipe_inif (killpriv) err =security_inode_killpriv (out->f_path.dentry);if (!err && killsuid) - err =__remove_suid (out->f_path.dentry, killsuid); + err =__remove_suid (&out->f_path, killsuid);mutex_unlock (&inode->i_mutex);if (err)return err;
fs/xfs/linux-2.6/xfs_lrw.c +1 -1
@@ -734,7 +734,7 @@ start: !capable(CAP_FSETID)) { error =xfs_write_clear_setuid (xip);if (likely (!error)) - error = -remove_suid (file->f_path.dentry); + error = -remove_suid (&file->f_path);if (unlikely (error)) { goto out_unlock_internal; }
include/linux/fs.h +2 -2
@@ -1774,9 +1774,9 @@ extern void __iget(struct inode * inode)extern void clear_inode (struct inode *);extern void destroy_inode (struct inode *);extern struct inode *new_inode (struct super_block *); -extern int __remove_suid (struct dentry *,int ); +extern int __remove_suid (struct path *,int );extern int should_remove_suid (struct dentry *); -extern int remove_suid (struct dentry *); +extern int remove_suid (struct path *);extern void __insert_inode_hash (struct inode *,unsigned long hashval);extern void remove_inode_hash (struct inode *);
mm/filemap.c +8 -8
@@ -1629,26 +1629,26 @@ int should_remove_suid(struct dentry *de }EXPORT_SYMBOL (should_remove_suid); -int __remove_suid (struct dentry *dentry,int kill) +int __remove_suid (struct path *path,int kill) {struct iattr newattrs; newattrs.ia_valid =ATTR_FORCE | kill; -return notify_change (dentry, &newattrs); +return notify_change (path->dentry, &newattrs); } -int remove_suid (struct dentry *dentry) +int remove_suid (struct path *path) { -int killsuid =should_remove_suid (dentry); -int killpriv =security_inode_need_killpriv (dentry); +int killsuid =should_remove_suid (path->dentry); +int killpriv =security_inode_need_killpriv (path->dentry);int error =0 ;if (killpriv <0 )return killpriv;if (killpriv) - error =security_inode_killpriv (dentry); + error =security_inode_killpriv (path->dentry);if (!error && killsuid) - error =__remove_suid (dentry, killsuid); + error =__remove_suid (path, killsuid);return error; }
@@ -2361,7 +2361,7 @@ __generic_file_aio_write_nolock(struct kif (count ==0 ) goto out; - err =remove_suid (file->f_path.dentry); + err =remove_suid (&file->f_path);if (err) goto out;
mm/filemap_xip.c +1 -1
@@ -378,7 +378,7 @@ xip_file_write(struct file *filp, constif (count ==0 ) goto out_backing; - ret =remove_suid (filp->f_path.dentry); + ret =remove_suid (&filp->f_path);if (ret) goto out_backing;
mm/shmem.c +1 -1
@@ -1510,7 +1510,7 @@ shmem_file_write(struct file *file, consif (err || !count) goto out; - err =remove_suid (file->f_path.dentry); + err =remove_suid (&file->f_path);if (err) goto out;
PATCH 3/45 Add a vfsmount parameter to notify_change()
In reply to [email protected]The vfsmount parameter must be set appropriately for files visibile outside the kernel. Files that are only used in a filesystem (e.g., reiserfs xattr files) will have a NULL vfsmount.
--- fs/attr.c | 3 ++- fs/ecryptfs/inode.c | 4 +++- fs/exec.c | 3 ++- fs/fat/file.c | 2 +- fs/hpfs/namei.c | 2 +- fs/namei.c | 2 +- fs/nfsd/vfs.c | 8 ++++---- fs/open.c | 28 +++++++++++++++------------- fs/reiserfs/xattr.c | 6 +++--- fs/sysfs/file.c | 2 +- fs/utimes.c | 2 +- include/linux/fs.h | 6 +++--- mm/filemap.c | 2 +- mm/tiny-shmem.c | 2 +- 14 files changed, 39 insertions(+), 33 deletions(-)
fs/attr.c +2 -1
@@ -100,7 +100,8 @@ int inode_setattr(struct inode * inode, }EXPORT_SYMBOL (inode_setattr); -int notify_change (struct dentry * dentry,struct iattr * attr) +int notify_change (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *attr) {struct inode *inode = dentry->d_inode;mode_t mode = inode->i_mode;
fs/ecryptfs/inode.c +3 -1
@@ -862,6 +862,7 @@ static int ecryptfs_setattr(struct dentr {int rc =0 ;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct inode *inode;struct inode *lower_inode;struct ecryptfs_crypt_stat *crypt_stat;
@@ -872,6 +873,7 @@ static int ecryptfs_setattr(struct dentr inode = dentry->d_inode; lower_inode =ecryptfs_inode_to_lower (inode); lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry);mutex_lock (&crypt_stat->cs_mutex);if (S_ISDIR (dentry->d_inode->i_mode)) crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED );
@@ -922,7 +924,7 @@ static int ecryptfs_setattr(struct dentrif (ia->ia_valid & (ATTR_KILL_SUID |ATTR_KILL_SGID )) ia->ia_valid &= ~ATTR_MODE ; - rc =notify_change (lower_dentry, ia); + rc =notify_change (lower_dentry, lower_mnt, ia); out:fsstack_copy_attr_all (inode, lower_inode);return rc;
fs/exec.c +2 -1
@@ -1783,7 +1783,8 @@ int do_coredump(long signr, int exit_cod goto close_fail;if (!file->f_op->write) goto close_fail; -if (!ispipe &&do_truncate (file->f_path.dentry,0 ,0 , file) !=0 ) +if (!ispipe && +do_truncate (file->f_path.dentry, file->f_path.mnt,0 ,0 , file) !=0 ) goto close_fail; retval = binfmt->core_dump (signr, regs, file, core_limit);
fs/fat/file.c +1 -1
@@ -92,7 +92,7 @@ int fat_generic_ioctl(struct inode *inod }/* This MUST be done before doing anything irreversible... */ - err =notify_change (filp->f_path.dentry, &ia); + err =notify_change (filp->f_path.dentry, filp->f_path.mnt, &ia);if (err) goto up;
fs/hpfs/namei.c +1 -1
@@ -426,7 +426,7 @@ again:/*printk("HPFS: truncating file before delete.\n");*/ newattrs.ia_size =0 ; newattrs.ia_valid =ATTR_SIZE |ATTR_CTIME ; - err =notify_change (dentry, &newattrs); + err =notify_change (dentry,NULL , &newattrs);put_write_access (inode);if (!err) goto again;
fs/namei.c +1 -1
@@ -1668,7 +1668,7 @@ int may_open(struct nameidata *nd, int aif (!error) {DQUOT_INIT (inode); - error =do_truncate (dentry,0 , + error =do_truncate (dentry, nd->mnt,0 ,ATTR_MTIME |ATTR_CTIME |ATTR_OPEN ,NULL ); }
fs/nfsd/vfs.c +4 -4
@@ -388,7 +388,7 @@ nfsd_setattr(struct svc_rqst *rqstp, str err = nfserr_notsync;if (!check_guard || guardtime == inode->i_ctime.tv_sec) {fh_lock (fhp); - host_err =notify_change (dentry, iap); + host_err =notify_change (dentry, fhp->fh_export->ex_mnt, iap); err =nfserrno (host_err);fh_unlock (fhp); }
@@ -943,13 +943,13 @@ out:return err; } -static void kill_suid (struct dentry *dentry) +static void kill_suid (struct dentry *dentry,struct vfsmount *mnt) {struct iattr ia; ia.ia_valid =ATTR_KILL_SUID |ATTR_KILL_SGID |ATTR_KILL_PRIV ;mutex_lock (&dentry->d_inode->i_mutex); -notify_change (dentry, &ia); +notify_change (dentry, mnt, &ia);mutex_unlock (&dentry->d_inode->i_mutex); }
@@ -1008,7 +1008,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s/* clear setuid/setgid flag after write */ if (host_err >=0 && (inode->i_mode & (S_ISUID |S_ISGID ))) -kill_suid (dentry); +kill_suid (dentry, exp->ex_mnt);if (host_err >=0 && stable) {static ino_t last_ino;
fs/open.c +15 -13
@@ -194,8 +194,8 @@ out:return error; } -int do_truncate (struct dentry *dentry,loff_t length,unsigned int time_attrs, -struct file *filp) +int do_truncate (struct dentry *dentry,struct vfsmount *mnt,loff_t length, +unsigned int time_attrs,struct file *filp) {int err;struct iattr newattrs;
@@ -215,7 +215,7 @@ int do_truncate(struct dentry *dentry, l newattrs.ia_valid |=should_remove_suid (dentry);mutex_lock (&dentry->d_inode->i_mutex); - err =notify_change (dentry, &newattrs); + err =notify_change (dentry, mnt, &newattrs);mutex_unlock (&dentry->d_inode->i_mutex);return err; }
@@ -271,7 +271,7 @@ static long do_sys_truncate(const char _ error =locks_verify_truncate (inode,NULL , length);if (!error) {DQUOT_INIT (inode); - error =do_truncate (nd.dentry, length,0 ,NULL ); + error =do_truncate (nd.dentry, nd.mnt, length,0 ,NULL ); } put_write_and_out:
@@ -326,7 +326,8 @@ static long do_sys_ftruncate(unsigned in error =locks_verify_truncate (inode, file, length);if (!error) - error =do_truncate (dentry, length,ATTR_MTIME |ATTR_CTIME , file); + error =do_truncate (dentry, file->f_path.mnt, length, +ATTR_MTIME |ATTR_CTIME , file); out_putf:fput (file); out:
@@ -593,7 +594,7 @@ asmlinkage long sys_fchmod(unsigned int mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - err =notify_change (dentry, &newattrs); + err =notify_change (dentry, file->f_path.mnt, &newattrs);mutex_unlock (&inode->i_mutex); out_drop_write:
@@ -630,7 +631,7 @@ asmlinkage long sys_fchmodat(int dfd, co mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - error =notify_change (nd.dentry, &newattrs); + error =notify_change (nd.dentry, nd.mnt, &newattrs);mutex_unlock (&inode->i_mutex); out_drop_write:
@@ -646,7 +647,8 @@ asmlinkage long sys_chmod(const char __ureturn sys_fchmodat (AT_FDCWD , filename, mode); } -static int chown_common (struct dentry * dentry,uid_t user,gid_t group) +static int chown_common (struct dentry * dentry,struct vfsmount *mnt, +uid_t user,gid_t group) {struct inode * inode;int error;
@@ -673,7 +675,7 @@ static int chown_common(struct dentry * newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID |ATTR_KILL_PRIV ;mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex); out:return error;
@@ -690,7 +692,7 @@ asmlinkage long sys_chown(const char __u error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -715,7 +717,7 @@ asmlinkage long sys_fchownat(int dfd, co error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -734,7 +736,7 @@ asmlinkage long sys_lchown(const char __ error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -758,7 +760,7 @@ asmlinkage long sys_fchown(unsigned int goto out_fput; dentry = file->f_path.dentry;audit_inode (NULL , dentry); - error =chown_common (dentry, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group);mnt_drop_write (file->f_vfsmnt); out_fput:fput (file);
fs/reiserfs/xattr.c +3 -3
@@ -462,7 +462,7 @@ reiserfs_xattr_set(struct inode *inode, newattrs.ia_size = buffer_size; newattrs.ia_valid =ATTR_SIZE |ATTR_CTIME ;mutex_lock (&xinode->i_mutex); - err =notify_change (dentry, &newattrs); + err =notify_change (dentry,NULL , &newattrs);if (err) goto out_filp;
@@ -802,7 +802,7 @@ reiserfs_chown_xattrs_filler(void *buf, }if (!S_ISDIR (xafile->d_inode->i_mode)) - err =notify_change (xafile, attrs); + err =notify_change (xafile,NULL , attrs);dput (xafile);return err;
@@ -854,7 +854,7 @@ int reiserfs_chown_xattrs(struct inode * goto out_dir; } - err =notify_change (dir, attrs); + err =notify_change (dir,NULL , attrs);unlock_kernel (); out_dir:
fs/sysfs/file.c +1 -1
@@ -616,7 +616,7 @@ int sysfs_chmod_file(struct kobject *kob newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - rc =notify_change (victim, &newattrs); + rc =notify_change (victim,NULL , &newattrs);if (rc ==0 ) {mutex_lock (&sysfs_mutex);
fs/utimes.c +1 -1
@@ -135,7 +135,7 @@ long do_utimes(int dfd, char __user *fil } }mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex); mnt_drop_write_and_out:mnt_drop_write (mnt);
include/linux/fs.h +3 -3
@@ -1543,8 +1543,8 @@ static inline int break_lease(struct ino/* fs/open.c */ -extern int do_truncate (struct dentry *,loff_t start,unsigned int time_attrs, -struct file *filp); +extern int do_truncate (struct dentry *,struct vfsmount *,loff_t start, +unsigned int time_attrs,struct file *filp);extern long do_sys_open (int fdf,const char __user *filename,int flags,int mode);extern struct file *filp_open (const char *,int ,int );
@@ -1701,7 +1701,7 @@ extern int do_remount_sb(struct super_bl#ifdef CONFIG_BLOCK extern sector_t bmap (struct inode *,sector_t );#endif -extern int notify_change (struct dentry *,struct iattr *); +extern int notify_change (struct dentry *,struct vfsmount *,struct iattr *);extern int permission (struct inode *,int ,struct nameidata *);extern int generic_permission (struct inode *,int ,int (*check_acl)(struct inode *,int ));
mm/filemap.c +1 -1
@@ -1634,7 +1634,7 @@ int __remove_suid(struct path *path, intstruct iattr newattrs; newattrs.ia_valid =ATTR_FORCE | kill; -return notify_change (path->dentry, &newattrs); +return notify_change (path->dentry, path->mnt, &newattrs); }int remove_suid (struct path *path)
mm/tiny-shmem.c +1 -1
@@ -81,7 +81,7 @@ struct file *shmem_file_setup(char *name inode->i_nlink =0 ;/* It is unlinked */ /* notify everyone as to the change of file size */ - error =do_truncate (dentry, size,0 , file); + error =do_truncate (dentry, file->f_path.mnt, size,0 , file);if (error <0 ) goto close_file;
PATCH 4/45 Pass struct vfsmount to the inode_setattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/attr.c | 4 ++-- include/linux/security.h | 8 ++++++-- security/dummy.c | 3 ++- security/security.c | 5 +++-- security/selinux/hooks.c | 5 +++-- 5 files changed, 16 insertions(+), 9 deletions(-)
fs/attr.c +2 -2
@@ -159,13 +159,13 @@ int notify_change(struct dentry *dentry,down_write (&dentry->d_inode->i_alloc_sem);if (inode->i_op && inode->i_op->setattr) { - error =security_inode_setattr (dentry, attr); + error =security_inode_setattr (dentry, mnt, attr);if (!error) error = inode->i_op->setattr (dentry, attr); }else { error =inode_change_ok (inode, attr);if (!error) - error =security_inode_setattr (dentry, attr); + error =security_inode_setattr (dentry, mnt, attr);if (!error) {if ((ia_valid &ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid &ATTR_GID && attr->ia_gid != inode->i_gid))
include/linux/security.h +6 -2
@@ -372,6 +372,7 @@ struct request_sock;* file attributes change (such as when a file is truncated, chown/chmod * operations, transferring disk quotas, etc). * @dentry contains the dentry structure for the file. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @attr is the iattr structure containing the new file attributes. * Return 0 if permission is granted. * @inode_getattr:
@@ -1251,7 +1252,8 @@ struct security_operations {int (*inode_readlink) (struct dentry *dentry);int (*inode_follow_link) (struct dentry *dentry,struct nameidata *nd);int (*inode_permission) (struct inode *inode,int mask,struct nameidata *nd); -int (*inode_setattr) (struct dentry *dentry,struct iattr *attr); +int (*inode_setattr) (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *attr);int (*inode_getattr) (struct vfsmount *mnt,struct dentry *dentry);void (*inode_delete) (struct inode *inode); int (*inode_setxattr) (struct dentry *dentry, char *name, void *value,
@@ -1506,7 +1508,8 @@ int security_inode_rename(struct inode *int security_inode_readlink (struct dentry *dentry);int security_inode_follow_link (struct dentry *dentry,struct nameidata *nd);int security_inode_permission (struct inode *inode,int mask,struct nameidata *nd); -int security_inode_setattr (struct dentry *dentry,struct iattr *attr); +int security_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *attr);int security_inode_getattr (struct vfsmount *mnt,struct dentry *dentry);void security_inode_delete (struct inode *inode); int security_inode_setxattr(struct dentry *dentry, char *name,
@@ -1874,6 +1877,7 @@ static inline int security_inode_permiss }static inline int security_inode_setattr (struct dentry *dentry, +struct vfsmount *mnt,struct iattr *attr) {return 0 ;
security/dummy.c +2 -1
@@ -325,7 +325,8 @@ static int dummy_inode_permission (strucreturn 0 ; } -static int dummy_inode_setattr (struct dentry *dentry,struct iattr *iattr) +static int dummy_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *iattr) {return 0 ; }
security/security.c +3 -2
@@ -412,11 +412,12 @@ int security_inode_permission(struct ino }EXPORT_SYMBOL (security_inode_permission); -int security_inode_setattr (struct dentry *dentry,struct iattr *attr) +int security_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *attr) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_setattr (dentry, attr); +return security_ops->inode_setattr (dentry, mnt, attr); }int security_inode_getattr (struct vfsmount *mnt,struct dentry *dentry)
security/selinux/hooks.c +3 -2
@@ -2275,11 +2275,12 @@ static int selinux_inode_permission(stru file_mask_to_av(inode->i_mode, mask), NULL); } -static int selinux_inode_setattr (struct dentry *dentry,struct iattr *iattr) +static int selinux_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *iattr) {int rc; - rc = secondary_ops->inode_setattr (dentry, iattr); + rc = secondary_ops->inode_setattr (dentry, mnt, iattr);if (rc)return rc;
PATCH 5/45 Add struct vfsmount parameter to vfs_mkdir()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 5 ++++- fs/namei.c | 5 +++-- fs/nfsd/nfs4recover.c | 3 ++- fs/nfsd/vfs.c | 8 +++++--- include/linux/fs.h | 2 +- 5 files changed, 15 insertions(+), 8 deletions(-)
fs/ecryptfs/inode.c +4 -1
@@ -500,11 +500,14 @@ static int ecryptfs_mkdir(struct inode * {int rc;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); lower_dir_dentry =lock_parent (lower_dentry); - rc =vfs_mkdir (lower_dir_dentry->d_inode, lower_dentry, mode); + rc =vfs_mkdir (lower_dir_dentry->d_inode, lower_dentry, lower_mnt, + mode);if (rc || !lower_dentry->d_inode) goto out; rc =ecryptfs_interpose (lower_dentry, dentry, dir->i_sb,0 );
fs/namei.c +3 -2
@@ -2020,7 +2020,8 @@ asmlinkage long sys_mknod(const char __ureturn sys_mknodat (AT_FDCWD , filename, mode, dev); } -int vfs_mkdir (struct inode *dir,struct dentry *dentry,int mode) +int vfs_mkdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt, +int mode) {int error =may_create (dir, dentry,NULL );
@@ -2067,7 +2068,7 @@ asmlinkage long sys_mkdirat(int dfd, con error =mnt_want_write (nd.mnt);if (error) goto out_dput; - error =vfs_mkdir (nd.dentry->d_inode, dentry, mode); + error =vfs_mkdir (nd.dentry->d_inode, dentry, nd.mnt, mode);mnt_drop_write (nd.mnt); out_dput:dput (dentry);
fs/nfsd/nfs4recover.c +2 -1
@@ -159,7 +159,8 @@ nfsd4_create_clid_dir(struct nfs4_client status =mnt_want_write (rec_dir.mnt);if (status) goto out_put; - status =vfs_mkdir (rec_dir.dentry->d_inode, dentry,S_IRWXU ); + status =vfs_mkdir (rec_dir.dentry->d_inode, dentry, rec_dir.mnt, +S_IRWXU );mnt_drop_write (rec_dir.mnt); out_put:dput (dentry);
fs/nfsd/vfs.c +5 -3
@@ -1165,6 +1165,7 @@ nfsd_create(struct svc_rqst *rqstp, stru int type, dev_t rdev, struct svc_fh *resfhp) { struct dentry *dentry, *dchild = NULL; +struct svc_export *exp;struct inode *dirp;__be32 err;int host_err;
@@ -1181,6 +1182,7 @@ nfsd_create(struct svc_rqst *rqstp, stru goto out; dentry = fhp->fh_dentry; + exp = fhp->fh_export; dirp = dentry->d_inode; err = nfserr_notdir;
@@ -1197,7 +1199,7 @@ nfsd_create(struct svc_rqst *rqstp, stru host_err =PTR_ERR (dchild);if (IS_ERR (dchild)) goto out_nfserr; - err =fh_compose (resfhp, fhp->fh_export, dchild, fhp); + err =fh_compose (resfhp, exp, dchild, fhp);if (err) goto out; }else {
@@ -1236,7 +1238,7 @@ nfsd_create(struct svc_rqst *rqstp, stru host_err =vfs_create (dirp, dchild, iap->ia_mode,NULL );break ;case S_IFDIR : - host_err =vfs_mkdir (dirp, dchild, iap->ia_mode); + host_err =vfs_mkdir (dirp, dchild, exp->ex_mnt, iap->ia_mode);break ;case S_IFCHR :case S_IFBLK :
@@ -1255,7 +1257,7 @@ nfsd_create(struct svc_rqst *rqstp, struif (host_err <0 ) goto out_nfserr; -if (EX_ISSYNC (fhp->fh_export)) { +if (EX_ISSYNC (exp)) { err =nfserrno (nfsd_sync_dir (dentry));write_inode_now (dchild->d_inode,1 ); }
include/linux/fs.h +1 -1
@@ -1074,7 +1074,7 @@ extern void unlock_super(struct super_bl*/ extern int vfs_permission (struct nameidata *,int );extern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *); -extern int vfs_mkdir (struct inode *,struct dentry *,int ); +extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,const char *,int );extern int vfs_link (struct dentry *,struct inode *,struct dentry *);
PATCH 6/45 Pass struct vfsmount to the inode_mkdir LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 8 ++++++-- security/dummy.c | 2 +- security/security.c | 5 +++-- security/selinux/hooks.c | 3 ++- 5 files changed, 13 insertions(+), 7 deletions(-)
fs/namei.c +1 -1
@@ -2032,7 +2032,7 @@ int vfs_mkdir(struct inode *dir, structreturn -EPERM ; mode &= (S_IRWXUGO |S_ISVTX ); - error =security_inode_mkdir (dir, dentry, mode); + error =security_inode_mkdir (dir, dentry, mnt, mode);if (error)return error;
include/linux/security.h +6 -2
@@ -322,6 +322,7 @@ struct request_sock;* associated with inode strcture @dir. * @dir containst the inode structure of parent of the directory to be created. * @dentry contains the dentry structure of new directory. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @mode contains the mode of new directory. * Return 0 if permission is granted. * @inode_rmdir:
@@ -1243,7 +1244,8 @@ struct security_operations {int (*inode_unlink) (struct inode *dir,struct dentry *dentry);int (*inode_symlink) (struct inode *dir,struct dentry *dentry,const char *old_name); -int (*inode_mkdir) (struct inode *dir,struct dentry *dentry,int mode); +int (*inode_mkdir) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode);int (*inode_rmdir) (struct inode *dir,struct dentry *dentry);int (*inode_mknod) (struct inode *dir,struct dentry *dentry,int mode,dev_t dev);
@@ -1500,7 +1502,8 @@ int security_inode_link(struct dentry *oint security_inode_unlink (struct inode *dir,struct dentry *dentry);int security_inode_symlink (struct inode *dir,struct dentry *dentry,const char *old_name); -int security_inode_mkdir (struct inode *dir,struct dentry *dentry,int mode); +int security_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode);int security_inode_rmdir (struct inode *dir,struct dentry *dentry);int security_inode_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev); int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -1833,6 +1836,7 @@ static inline int security_inode_symlinkstatic inline int security_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {return 0 ;
security/dummy.c +1 -1
@@ -285,7 +285,7 @@ static int dummy_inode_symlink (struct i }static int dummy_inode_mkdir (struct inode *inode,struct dentry *dentry, -int mask) +struct vfsmount *mnt,int mask) {return 0 ; }
security/security.c +3 -2
@@ -359,11 +359,12 @@ int security_inode_symlink(struct inodereturn security_ops->inode_symlink (dir, dentry, old_name); } -int security_inode_mkdir (struct inode *dir,struct dentry *dentry,int mode) +int security_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_mkdir (dir, dentry, mode); +return security_ops->inode_mkdir (dir, dentry, mnt, mode); }int security_inode_rmdir (struct inode *dir,struct dentry *dentry)
security/selinux/hooks.c +2 -1
@@ -2215,7 +2215,8 @@ static int selinux_inode_symlink(structreturn may_create (dir, dentry,SECCLASS_LNK_FILE ); } -static int selinux_inode_mkdir (struct inode *dir,struct dentry *dentry,int mask) +static int selinux_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) {return may_create (dir, dentry,SECCLASS_DIR ); }
PATCH 7/45 Add a struct vfsmount parameter to vfs_mknod()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 5 ++++- fs/namei.c | 10 ++++++---- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- net/unix/af_unix.c | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-)
fs/ecryptfs/inode.c +4 -1
@@ -551,11 +551,14 @@ ecryptfs_mknod(struct inode *dir, struct {int rc;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); lower_dir_dentry =lock_parent (lower_dentry); - rc =vfs_mknod (lower_dir_dentry->d_inode, lower_dentry, mode, dev); + rc =vfs_mknod (lower_dir_dentry->d_inode, lower_dentry, lower_mnt, mode, + dev);if (rc || !lower_dentry->d_inode) goto out; rc =ecryptfs_interpose (lower_dentry, dentry, dir->i_sb,0 );
fs/namei.c +6 -4
@@ -1931,7 +1931,8 @@ fail: }EXPORT_SYMBOL_GPL (lookup_create); -int vfs_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev) +int vfs_mknod (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt, +int mode,dev_t dev) {int error =may_create (dir, dentry,NULL );
@@ -1996,11 +1997,12 @@ asmlinkage long sys_mknodat(int dfd, con error =vfs_create (nd.dentry->d_inode,dentry,mode,&nd);break ;case S_IFCHR :case S_IFBLK : - error =vfs_mknod (nd.dentry->d_inode,dentry,mode, -new_decode_dev (dev)); + error =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, + mode,new_decode_dev (dev));break ;case S_IFIFO :case S_IFSOCK : - error =vfs_mknod (nd.dentry->d_inode,dentry,mode,0 ); + error =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, + mode,0 );break ; }mnt_drop_write (nd.mnt);
fs/nfsd/vfs.c +2 -1
@@ -1247,7 +1247,8 @@ nfsd_create(struct svc_rqst *rqstp, stru host_err =mnt_want_write (fhp->fh_export->ex_mnt);if (host_err)break ; - host_err =vfs_mknod (dirp, dchild, iap->ia_mode, rdev); + host_err =vfs_mknod (dirp, dchild, exp->ex_mnt, iap->ia_mode, + rdev);mnt_drop_write (fhp->fh_export->ex_mnt);break ;default :
include/linux/fs.h +1 -1
@@ -1075,7 +1075,7 @@ extern void unlock_super(struct super_blextern int vfs_permission (struct nameidata *,int );extern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *);extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int ); -extern int vfs_mknod (struct inode *,struct dentry *,int ,dev_t ); +extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,const char *,int );extern int vfs_link (struct dentry *,struct inode *,struct dentry *);extern int vfs_rmdir (struct inode *,struct dentry *);
net/unix/af_unix.c +1 -1
@@ -849,7 +849,7 @@ static int unix_bind(struct socket *sock err =mnt_want_write (nd.mnt);if (err) goto out_mknod_dput; - err =vfs_mknod (nd.dentry->d_inode, dentry, mode,0 ); + err =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, mode,0 );mnt_drop_write (nd.mnt);if (err) goto out_mknod_dput;
PATCH 8/45 Pass struct vfsmount to the inode_mknod LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 7 +++++-- security/dummy.c | 2 +- security/security.c | 5 +++-- security/selinux/hooks.c | 5 +++-- 5 files changed, 13 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -1945,7 +1945,7 @@ int vfs_mknod(struct inode *dir, structif (!dir->i_op || !dir->i_op->mknod)return -EPERM ; - error =security_inode_mknod (dir, dentry, mode, dev); + error =security_inode_mknod (dir, dentry, mnt, mode, dev);if (error)return error;
include/linux/security.h +5 -2
@@ -337,6 +337,7 @@ struct request_sock;* and not this hook. * @dir contains the inode structure of parent of the new file. * @dentry contains the dentry structure of the new file. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @mode contains the mode of the new file. * @dev contains the device number. * Return 0 if permission is granted.
@@ -1248,7 +1249,7 @@ struct security_operations { struct vfsmount *mnt, int mode);int (*inode_rmdir) (struct inode *dir,struct dentry *dentry);int (*inode_mknod) (struct inode *dir,struct dentry *dentry, -int mode,dev_t dev); +struct vfsmount *mnt,int mode,dev_t dev);int (*inode_rename) (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry);int (*inode_readlink) (struct dentry *dentry);
@@ -1505,7 +1506,8 @@ int security_inode_symlink(struct inodeint security_inode_mkdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode);int security_inode_rmdir (struct inode *dir,struct dentry *dentry); -int security_inode_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev); +int security_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev);int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry);int security_inode_readlink (struct dentry *dentry);
@@ -1850,6 +1852,7 @@ static inline int security_inode_rmdir (static inline int security_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) {return 0 ;
security/dummy.c +1 -1
@@ -296,7 +296,7 @@ static int dummy_inode_rmdir (struct ino }static int dummy_inode_mknod (struct inode *inode,struct dentry *dentry, -int mode,dev_t dev) +struct vfsmount *mnt,int mode,dev_t dev) {return 0 ; }
security/security.c +3 -2
@@ -374,11 +374,12 @@ int security_inode_rmdir(struct inode *dreturn security_ops->inode_rmdir (dir, dentry); } -int security_inode_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev) +int security_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_mknod (dir, dentry, mode, dev); +return security_ops->inode_mknod (dir, dentry, mnt, mode, dev); } int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
security/selinux/hooks.c +3 -2
@@ -2226,11 +2226,12 @@ static int selinux_inode_rmdir(struct inreturn may_link (dir, dentry,MAY_RMDIR ); } -static int selinux_inode_mknod (struct inode *dir,struct dentry *dentry,int mode,dev_t dev) +static int selinux_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) {int rc; - rc = secondary_ops->inode_mknod (dir, dentry, mode, dev); + rc = secondary_ops->inode_mknod (dir, dentry, mnt, mode, dev);if (rc)return rc;
PATCH 9/45 Add a struct vfsmount parameter to vfs_symlink()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 4 +++- fs/namei.c | 6 ++++-- fs/nfsd/vfs.c | 12 ++++++++---- include/linux/fs.h | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-)
fs/ecryptfs/inode.c +3 -1
@@ -461,6 +461,7 @@ static int ecryptfs_symlink(struct inode {int rc;struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry;umode_t mode;char *encoded_symname;
@@ -469,6 +470,7 @@ static int ecryptfs_symlink(struct inode lower_dentry =ecryptfs_dentry_to_lower (dentry);dget (lower_dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); lower_dir_dentry =lock_parent (lower_dentry); mode =S_IALLUGO ; encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
@@ -478,7 +480,7 @@ static int ecryptfs_symlink(struct inode rc = encoded_symlen; goto out_lock; } - rc =vfs_symlink (lower_dir_dentry->d_inode, lower_dentry, + rc =vfs_symlink (lower_dir_dentry->d_inode, lower_dentry, lower_mnt, encoded_symname, mode);kfree (encoded_symname);if (rc || !lower_dentry->d_inode)
fs/namei.c +4 -2
@@ -2303,7 +2303,8 @@ asmlinkage long sys_unlink(const char __return do_unlinkat (AT_FDCWD , pathname); } -int vfs_symlink (struct inode *dir,struct dentry *dentry,const char *oldname,int mode) +int vfs_symlink (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt, +const char *oldname,int mode) {int error =may_create (dir, dentry,NULL );
@@ -2352,7 +2353,8 @@ asmlinkage long sys_symlinkat(const char error =mnt_want_write (nd.mnt);if (error) goto out_dput; - error =vfs_symlink (nd.dentry->d_inode, dentry, from,S_IALLUGO ); + error =vfs_symlink (nd.dentry->d_inode, dentry, nd.mnt, from, +S_IALLUGO );mnt_drop_write (nd.mnt); out_dput:dput (dentry);
fs/nfsd/vfs.c +8 -4
@@ -1493,6 +1493,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str struct iattr *iap) {struct dentry *dentry, *dnew; +struct svc_export *exp;__be32 err, cerr;int host_err;umode_t mode;
@@ -1519,6 +1520,7 @@ nfsd_symlink(struct svc_rqst *rqstp, strif (iap && (iap->ia_valid &ATTR_MODE )) mode = iap->ia_mode &S_IALLUGO ; + exp = fhp->fh_export;if (unlikely (path[plen] !=0 )) {char *path_alloced =kmalloc (plen+1 ,GFP_KERNEL );if (path_alloced ==NULL )
@@ -1526,20 +1528,22 @@ nfsd_symlink(struct svc_rqst *rqstp, strelse {strncpy (path_alloced, path, plen); path_alloced[plen] =0 ; - host_err =vfs_symlink (dentry->d_inode, dnew, path_alloced, mode); + host_err =vfs_symlink (dentry->d_inode, dnew, + exp->ex_mnt, path_alloced, mode);kfree (path_alloced); } }else - host_err =vfs_symlink (dentry->d_inode, dnew, path, mode); + host_err =vfs_symlink (dentry->d_inode, dnew, exp->ex_mnt, path, + mode);if (!host_err) { -if (EX_ISSYNC (fhp->fh_export)) +if (EX_ISSYNC (exp)) host_err =nfsd_sync_dir (dentry); } err =nfserrno (host_err);fh_unlock (fhp); - cerr =fh_compose (resfhp, fhp->fh_export, dnew, fhp); + cerr =fh_compose (resfhp, exp, dnew, fhp);dput (dnew);if (err==0 ) err = cerr; out:
include/linux/fs.h +1 -1
@@ -1076,7 +1076,7 @@ extern int vfs_permission(struct nameidaextern int vfs_create (struct inode *,struct dentry *,int ,struct nameidata *);extern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t ); -extern int vfs_symlink (struct inode *,struct dentry *,const char *,int ); +extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );extern int vfs_link (struct dentry *,struct inode *,struct dentry *);extern int vfs_rmdir (struct inode *,struct dentry *);extern int vfs_unlink (struct inode *,struct dentry *);
PATCH 10/45 Pass struct vfsmount to the inode_symlink LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 8 +++++--- security/dummy.c | 2 +- security/security.c | 4 ++-- security/selinux/hooks.c | 3 ++- 5 files changed, 11 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -2314,7 +2314,7 @@ int vfs_symlink(struct inode *dir, strucif (!dir->i_op || !dir->i_op->symlink)return -EPERM ; - error =security_inode_symlink (dir, dentry, oldname); + error =security_inode_symlink (dir, dentry, mnt, oldname);if (error)return error;
include/linux/security.h +5 -3
@@ -315,6 +315,7 @@ struct request_sock;* Check the permission to create a symbolic link to a file. * @dir contains the inode structure of parent directory of the symbolic link. * @dentry contains the dentry structure of the symbolic link. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * @old_name contains the pathname of file. * Return 0 if permission is granted. * @inode_mkdir:
@@ -1243,8 +1244,8 @@ struct security_operations {int (*inode_link) (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry);int (*inode_unlink) (struct inode *dir,struct dentry *dentry); -int (*inode_symlink) (struct inode *dir, -struct dentry *dentry,const char *old_name); +int (*inode_symlink) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *old_name);int (*inode_mkdir) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode);int (*inode_rmdir) (struct inode *dir,struct dentry *dentry);
@@ -1502,7 +1503,7 @@ int security_inode_link(struct dentry *o struct dentry *new_dentry);int security_inode_unlink (struct inode *dir,struct dentry *dentry);int security_inode_symlink (struct inode *dir,struct dentry *dentry, -const char *old_name); +struct vfsmount *mnt,const char *old_name);int security_inode_mkdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode);int security_inode_rmdir (struct inode *dir,struct dentry *dentry);
@@ -1831,6 +1832,7 @@ static inline int security_inode_unlinkstatic inline int security_inode_symlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *old_name) {return 0 ;
security/dummy.c +1 -1
@@ -279,7 +279,7 @@ static int dummy_inode_unlink (struct in }static int dummy_inode_symlink (struct inode *inode,struct dentry *dentry, -const char *name) +struct vfsmount *mnt,const char *name) {return 0 ; }
security/security.c +2 -2
@@ -352,11 +352,11 @@ int security_inode_unlink(struct inode * }int security_inode_symlink (struct inode *dir,struct dentry *dentry, -const char *old_name) +struct vfsmount *mnt,const char *old_name) {if (unlikely (IS_PRIVATE (dir)))return 0 ; -return security_ops->inode_symlink (dir, dentry, old_name); +return security_ops->inode_symlink (dir, dentry, mnt, old_name); } int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
security/selinux/hooks.c +2 -1
@@ -2210,7 +2210,8 @@ static int selinux_inode_unlink(struct ireturn may_link (dir, dentry,MAY_UNLINK ); } -static int selinux_inode_symlink (struct inode *dir,struct dentry *dentry,const char *name) +static int selinux_inode_symlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *name) {return may_create (dir, dentry,SECCLASS_LNK_FILE ); }
PATCH 11/45 Pass struct vfsmount to the inode_readlink LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/stat.c | 2 +- include/linux/security.h | 8 +++++--- security/dummy.c | 2 +- security/security.c | 4 ++-- security/selinux/hooks.c | 2 +- 5 files changed, 10 insertions(+), 8 deletions(-)
fs/stat.c +1 -1
@@ -306,7 +306,7 @@ asmlinkage long sys_readlinkat(int dfd, error = -EINVAL ;if (inode->i_op && inode->i_op->readlink) { - error =security_inode_readlink (nd.dentry); + error =security_inode_readlink (nd.dentry, nd.mnt);if (!error) {touch_atime (nd.mnt, nd.dentry); error = inode->i_op->readlink (nd.dentry, buf, bufsiz);
include/linux/security.h +5 -3
@@ -352,6 +352,7 @@ struct request_sock;* @inode_readlink: * Check the permission to read the symbolic link. * @dentry contains the dentry structure for the file link. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * Return 0 if permission is granted. * @inode_follow_link: * Check permission to follow a symbolic link when looking up a pathname.
@@ -1253,7 +1254,7 @@ struct security_operations { struct vfsmount *mnt, int mode, dev_t dev);int (*inode_rename) (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry); -int (*inode_readlink) (struct dentry *dentry); +int (*inode_readlink) (struct dentry *dentry,struct vfsmount *mnt);int (*inode_follow_link) (struct dentry *dentry,struct nameidata *nd);int (*inode_permission) (struct inode *inode,int mask,struct nameidata *nd); int (*inode_setattr) (struct dentry *dentry, struct vfsmount *mnt,
@@ -1511,7 +1512,7 @@ int security_inode_mknod(struct inode *d struct vfsmount *mnt, int mode, dev_t dev);int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry,struct inode *new_dir,struct dentry *new_dentry); -int security_inode_readlink (struct dentry *dentry); +int security_inode_readlink (struct dentry *dentry,struct vfsmount *mnt);int security_inode_follow_link (struct dentry *dentry,struct nameidata *nd);int security_inode_permission (struct inode *inode,int mask,struct nameidata *nd); int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
@@ -1868,7 +1869,8 @@ static inline int security_inode_renamereturn 0 ; } -static inline int security_inode_readlink (struct dentry *dentry) +static inline int security_inode_readlink (struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +1 -1
@@ -309,7 +309,7 @@ static int dummy_inode_rename (struct inreturn 0 ; } -static int dummy_inode_readlink (struct dentry *dentry) +static int dummy_inode_readlink (struct dentry *dentry,struct vfsmount *mnt) {return 0 ; }
security/security.c +2 -2
@@ -392,11 +392,11 @@ int security_inode_rename(struct inode * new_dir, new_dentry); } -int security_inode_readlink (struct dentry *dentry) +int security_inode_readlink (struct dentry *dentry,struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_readlink (dentry); +return security_ops->inode_readlink (dentry, mnt); }int security_inode_follow_link (struct dentry *dentry,struct nameidata *nd)
security/selinux/hooks.c +1 -1
@@ -2245,7 +2245,7 @@ static int selinux_inode_rename(struct ireturn may_rename (old_inode, old_dentry, new_inode, new_dentry); } -static int selinux_inode_readlink (struct dentry *dentry) +static int selinux_inode_readlink (struct dentry *dentry,struct vfsmount *mnt) {return dentry_has_perm (current,NULL , dentry,FILE__READ ); }
PATCH 12/45 Add struct vfsmount parameters to vfs_link()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 9 +++++++-- fs/namei.c | 7 +++++-- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-)
fs/ecryptfs/inode.c +7 -2
@@ -402,19 +402,24 @@ static int ecryptfs_link(struct dentry * struct dentry *new_dentry) {struct dentry *lower_old_dentry; +struct vfsmount *lower_old_mnt;struct dentry *lower_new_dentry; +struct vfsmount *lower_new_mnt;struct dentry *lower_dir_dentry;u64 file_size_save;int rc; file_size_save =i_size_read (old_dentry->d_inode); lower_old_dentry =ecryptfs_dentry_to_lower (old_dentry); + lower_old_mnt =ecryptfs_dentry_to_lower_mnt (old_dentry); lower_new_dentry =ecryptfs_dentry_to_lower (new_dentry); + lower_new_mnt =ecryptfs_dentry_to_lower_mnt (new_dentry);dget (lower_old_dentry);dget (lower_new_dentry); lower_dir_dentry =lock_parent (lower_new_dentry); - rc =vfs_link (lower_old_dentry, lower_dir_dentry->d_inode, - lower_new_dentry); + rc =vfs_link (lower_old_dentry, lower_old_mnt, + lower_dir_dentry->d_inode, lower_new_dentry, + lower_new_mnt);if (rc || !lower_new_dentry->d_inode) goto out_lock; rc =ecryptfs_interpose (lower_new_dentry, new_dentry, dir->i_sb,0 );
fs/namei.c +5 -2
@@ -2373,7 +2373,9 @@ asmlinkage long sys_symlink(const char _return sys_symlinkat (oldname,AT_FDCWD , newname); } -int vfs_link (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry) +int vfs_link (struct dentry *old_dentry,struct vfsmount *old_mnt, +struct inode *dir,struct dentry *new_dentry, +struct vfsmount *new_mnt) {struct inode *inode = old_dentry->d_inode;int error;
@@ -2454,7 +2456,8 @@ asmlinkage long sys_linkat(int olddfd, c error =mnt_want_write (nd.mnt);if (error) goto out_dput; - error =vfs_link (old_nd.dentry, nd.dentry->d_inode, new_dentry); + error =vfs_link (old_nd.dentry, old_nd.mnt, nd.dentry->d_inode, + new_dentry, nd.mnt);mnt_drop_write (nd.mnt); out_dput:dput (new_dentry);
fs/nfsd/vfs.c +2 -1
@@ -1593,7 +1593,8 @@ nfsd_link(struct svc_rqst *rqstp, struct dold = tfhp->fh_dentry; dest = dold->d_inode; - host_err =vfs_link (dold, dirp, dnew); + host_err =vfs_link (dold, tfhp->fh_export->ex_mnt, dirp, + dnew, ffhp->fh_export->ex_mnt);if (!host_err) {if (EX_ISSYNC (ffhp->fh_export)) { err =nfserrno (nfsd_sync_dir (ddir));
include/linux/fs.h +1 -1
@@ -1077,7 +1077,7 @@ extern int vfs_create(struct inode *, stextern int vfs_mkdir (struct inode *,struct dentry *,struct vfsmount *,int );extern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int ); -extern int vfs_link (struct dentry *,struct inode *,struct dentry *); +extern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rmdir (struct inode *,struct dentry *);extern int vfs_unlink (struct inode *,struct dentry *);extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *);
PATCH 13/45 Pass the struct vfsmounts to the inode_link LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 3 ++- include/linux/security.h | 16 +++++++++++----- security/dummy.c | 6 ++++-- security/security.c | 8 +++++--- security/selinux/hooks.c | 9 +++++++-- 5 files changed, 29 insertions(+), 13 deletions(-)
fs/namei.c +2 -1
@@ -2400,7 +2400,8 @@ int vfs_link(struct dentry *old_dentry,if (S_ISDIR (old_dentry->d_inode->i_mode))return -EPERM ; - error =security_inode_link (old_dentry, dir, new_dentry); + error =security_inode_link (old_dentry, old_mnt, dir, new_dentry, + new_mnt);if (error)return error;
include/linux/security.h +11 -5
@@ -303,8 +303,10 @@ struct request_sock;* @inode_link: * Check permission before creating a new hard link to a file. * @old_dentry contains the dentry structure for an existing link to the file. +* @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL). * @dir contains the inode structure of the parent directory of the new link. * @new_dentry contains the dentry structure for the new link. +* @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL). * Return 0 if permission is granted. * @inode_unlink: * Check the permission to remove a hard link to a file.
@@ -1242,8 +1244,9 @@ struct security_operations { char **name, void **value, size_t *len);int (*inode_create) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode); -int (*inode_link) (struct dentry *old_dentry, -struct inode *dir,struct dentry *new_dentry); +int (*inode_link) (struct dentry *old_dentry,struct vfsmount *old_mnt, +struct inode *dir,struct dentry *new_dentry, +struct vfsmount *new_mnt);int (*inode_unlink) (struct inode *dir,struct dentry *dentry);int (*inode_symlink) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,const char *old_name);
@@ -1500,8 +1503,9 @@ int security_inode_init_security(struct char **name, void **value, size_t *len);int security_inode_create (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode); -int security_inode_link (struct dentry *old_dentry,struct inode *dir, -struct dentry *new_dentry); +int security_inode_link (struct dentry *old_dentry,struct vfsmount *old_mnt, +struct inode *dir,struct dentry *new_dentry, +struct vfsmount *new_mnt);int security_inode_unlink (struct inode *dir,struct dentry *dentry);int security_inode_symlink (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,const char *old_name);
@@ -1819,8 +1823,10 @@ static inline int security_inode_create }static inline int security_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *dir, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/dummy.c +4 -2
@@ -267,8 +267,10 @@ static int dummy_inode_create (struct inreturn 0 ; } -static int dummy_inode_link (struct dentry *old_dentry,struct inode *inode, -struct dentry *new_dentry) +static int dummy_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *inode, +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/security.c +5 -3
@@ -336,12 +336,14 @@ int security_inode_create(struct inode *return security_ops->inode_create (dir, dentry, mnt, mode); } -int security_inode_link (struct dentry *old_dentry,struct inode *dir, -struct dentry *new_dentry) +int security_inode_link (struct dentry *old_dentry,struct vfsmount *old_mnt, +struct inode *dir,struct dentry *new_dentry, +struct vfsmount *new_mnt) {if (unlikely (IS_PRIVATE (old_dentry->d_inode)))return 0 ; -return security_ops->inode_link (old_dentry, dir, new_dentry); +return security_ops->inode_link (old_dentry, old_mnt, dir, new_dentry, + new_mnt); }int security_inode_unlink (struct inode *dir,struct dentry *dentry)
security/selinux/hooks.c +7 -2
@@ -2190,11 +2190,16 @@ static int selinux_inode_create(struct ireturn may_create (dir, dentry,SECCLASS_FILE ); } -static int selinux_inode_link (struct dentry *old_dentry,struct inode *dir,struct dentry *new_dentry) +static int selinux_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt, +struct inode *dir, +struct dentry *new_dentry, +struct vfsmount *new_mnt) {int rc; - rc = secondary_ops->inode_link (old_dentry,dir,new_dentry); + rc = secondary_ops->inode_link (old_dentry, old_mnt, dir, new_dentry, + new_mnt);if (rc)return rc;return may_link (dir, old_dentry,MAY_LINK );
PATCH 14/45 Add a struct vfsmount parameter to vfs_rmdir()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 4 +++- fs/namei.c | 4 ++-- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/vfs.c | 8 +++++--- fs/reiserfs/xattr.c | 2 +- include/linux/fs.h | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-)
fs/ecryptfs/inode.c +3 -1
@@ -533,14 +533,16 @@ out:static int ecryptfs_rmdir (struct inode *dir,struct dentry *dentry) {struct dentry *lower_dentry; +struct vfsmount *lower_mnt;struct dentry *lower_dir_dentry;int rc; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry);dget (dentry); lower_dir_dentry =lock_parent (lower_dentry);dget (lower_dentry); - rc =vfs_rmdir (lower_dir_dentry->d_inode, lower_dentry); + rc =vfs_rmdir (lower_dir_dentry->d_inode, lower_dentry, lower_mnt);dput (lower_dentry);if (!rc)d_delete (lower_dentry);
fs/namei.c +2 -2
@@ -2115,7 +2115,7 @@ void dentry_unhash(struct dentry *dentryspin_unlock (&dcache_lock); } -int vfs_rmdir (struct inode *dir,struct dentry *dentry) +int vfs_rmdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt) {int error =may_delete (dir, dentry,1 );
@@ -2182,7 +2182,7 @@ static long do_rmdir(int dfd, const char error =mnt_want_write (nd.mnt);if (error) goto exit3; - error =vfs_rmdir (nd.dentry->d_inode, dentry); + error =vfs_rmdir (nd.dentry->d_inode, dentry, nd.mnt);mnt_drop_write (nd.mnt); exit3:dput (dentry);
fs/nfsd/nfs4recover.c +1 -1
@@ -280,7 +280,7 @@ nfsd4_clear_clid_dir(struct dentry *dir,* a kernel from the future.... */ nfsd4_list_rec_dir (dentry, nfsd4_remove_clid_file);mutex_lock_nested (&dir->d_inode->i_mutex,I_MUTEX_PARENT ); - status =vfs_rmdir (dir->d_inode, dentry); + status =vfs_rmdir (dir->d_inode, dentry, rec_dir.mnt);mutex_unlock (&dir->d_inode->i_mutex);return status; }
fs/nfsd/vfs.c +5 -3
@@ -1730,6 +1730,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru char *fname, int flen) {struct dentry *dentry, *rdentry; +struct svc_export *exp;struct inode *dirp;__be32 err;int host_err;
@@ -1744,6 +1745,7 @@ nfsd_unlink(struct svc_rqst *rqstp, strufh_lock_nested (fhp,I_MUTEX_PARENT ); dentry = fhp->fh_dentry; dirp = dentry->d_inode; + exp = fhp->fh_export; rdentry =lookup_one_len (fname, dentry, flen); host_err =PTR_ERR (rdentry);
@@ -1761,21 +1763,21 @@ nfsd_unlink(struct svc_rqst *rqstp, struif (type !=S_IFDIR ) {/* It's UNLINK */ #ifdef MSNFS -if ((fhp->fh_export->ex_flags &NFSEXP_MSNFS ) && +if ((exp->ex_flags &NFSEXP_MSNFS ) && (atomic_read (&rdentry->d_count) >1 )) { host_err = -EPERM ; } else#endif host_err =vfs_unlink (dirp, rdentry); }else {/* It's RMDIR */ - host_err =vfs_rmdir (dirp, rdentry); + host_err =vfs_rmdir (dirp, rdentry, exp->ex_mnt); }dput (rdentry);if (host_err) goto out_nfserr; -if (EX_ISSYNC (fhp->fh_export)) +if (EX_ISSYNC (exp)) host_err =nfsd_sync_dir (dentry); out_nfserr:
fs/reiserfs/xattr.c +1 -1
@@ -758,7 +758,7 @@ int reiserfs_delete_xattrs(struct inodeif (dir->d_inode->i_nlink <=2 ) { root =get_xa_root (inode->i_sb,XATTR_REPLACE );reiserfs_write_lock_xattrs (inode->i_sb); - err =vfs_rmdir (root->d_inode, dir); + err =vfs_rmdir (root->d_inode, dir,NULL );reiserfs_write_unlock_xattrs (inode->i_sb);dput (root); }else {
include/linux/fs.h +1 -1
@@ -1078,7 +1078,7 @@ extern int vfs_mkdir(struct inode *, strextern int vfs_mknod (struct inode *,struct dentry *,struct vfsmount *,int ,dev_t );extern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );extern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *); -extern int vfs_rmdir (struct inode *,struct dentry *); +extern int vfs_rmdir (struct inode *,struct dentry *,struct vfsmount *);extern int vfs_unlink (struct inode *,struct dentry *);extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *);
PATCH 15/45 Pass struct vfsmount to the inode_rmdir LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 10 +++++++--- security/dummy.c | 3 ++- security/security.c | 5 +++-- security/selinux/hooks.c | 3 ++- 5 files changed, 15 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -2132,7 +2132,7 @@ int vfs_rmdir(struct inode *dir, structif (d_mountpoint (dentry)) error = -EBUSY ;else { - error =security_inode_rmdir (dir, dentry); + error =security_inode_rmdir (dir, dentry, mnt);if (!error) { error = dir->i_op->rmdir (dir, dentry);if (!error)
include/linux/security.h +7 -3
@@ -332,6 +332,7 @@ struct request_sock;* Check the permission to remove a directory. * @dir contains the inode structure of parent of the directory to be removed. * @dentry contains the dentry structure of directory to be removed. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * Return 0 if permission is granted. * @inode_mknod: * Check permissions when creating a special file (or a socket or a fifo
@@ -1252,7 +1253,8 @@ struct security_operations { struct vfsmount *mnt, const char *old_name);int (*inode_mkdir) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode); -int (*inode_rmdir) (struct inode *dir,struct dentry *dentry); +int (*inode_rmdir) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt);int (*inode_mknod) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode,dev_t dev); int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
@@ -1511,7 +1513,8 @@ int security_inode_symlink(struct inode struct vfsmount *mnt, const char *old_name);int security_inode_mkdir (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode); -int security_inode_rmdir (struct inode *dir,struct dentry *dentry); +int security_inode_rmdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt);int security_inode_mknod (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode,dev_t dev); int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -1854,7 +1857,8 @@ static inline int security_inode_mkdir ( }static inline int security_inode_rmdir (struct inode *dir, -struct dentry *dentry) +struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +2 -1
@@ -292,7 +292,8 @@ static int dummy_inode_mkdir (struct inoreturn 0 ; } -static int dummy_inode_rmdir (struct inode *inode,struct dentry *dentry) +static int dummy_inode_rmdir (struct inode *inode,struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/security.c +3 -2
@@ -369,11 +369,12 @@ int security_inode_mkdir(struct inode *dreturn security_ops->inode_mkdir (dir, dentry, mnt, mode); } -int security_inode_rmdir (struct inode *dir,struct dentry *dentry) +int security_inode_rmdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_rmdir (dir, dentry); +return security_ops->inode_rmdir (dir, dentry, mnt); } int security_inode_mknod(struct inode *dir, struct dentry *dentry,
security/selinux/hooks.c +2 -1
@@ -2227,7 +2227,8 @@ static int selinux_inode_mkdir(struct inreturn may_create (dir, dentry,SECCLASS_DIR ); } -static int selinux_inode_rmdir (struct inode *dir,struct dentry *dentry) +static int selinux_inode_rmdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) {return may_link (dir, dentry,MAY_RMDIR ); }
PATCH 16/45 Call lsm hook before unhashing dentry in vfs_rmdir()
In reply to [email protected]If we unhash the dentry before calling the security_inode_rmdir hook, we cannot compute the file's pathname in the hook anymore. AppArmor needs to know the filename in order to decide whether a file may be deleted, though.
--- fs/namei.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
fs/namei.c +7 -6
@@ -2125,6 +2125,10 @@ int vfs_rmdir(struct inode *dir, structif (!dir->i_op || !dir->i_op->rmdir)return -EPERM ; + error =security_inode_rmdir (dir, dentry, mnt); +if (error) +return error; +DQUOT_INIT (dir);mutex_lock (&dentry->d_inode->i_mutex);
@@ -2132,12 +2136,9 @@ int vfs_rmdir(struct inode *dir, structif (d_mountpoint (dentry)) error = -EBUSY ;else { - error =security_inode_rmdir (dir, dentry, mnt); -if (!error) { - error = dir->i_op->rmdir (dir, dentry); -if (!error) - dentry->d_inode->i_flags |=S_DEAD ; - } + error = dir->i_op->rmdir (dir, dentry); +if (!error) + dentry->d_inode->i_flags |=S_DEAD ; }mutex_unlock (&dentry->d_inode->i_mutex);if (!error) {
PATCH 17/45 Add a struct vfsmount parameter to vfs_unlink()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 3 ++- fs/namei.c | 4 ++-- fs/nfsd/nfs4recover.c | 2 +- fs/nfsd/vfs.c | 2 +- include/linux/fs.h | 2 +- ipc/mqueue.c | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-)
fs/ecryptfs/inode.c +2 -1
@@ -444,10 +444,11 @@ static int ecryptfs_unlink(struct inode {int rc =0 ;struct dentry *lower_dentry =ecryptfs_dentry_to_lower (dentry); +struct vfsmount *lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry);struct inode *lower_dir_inode =ecryptfs_inode_to_lower (dir);lock_parent (lower_dentry); - rc =vfs_unlink (lower_dir_inode, lower_dentry); + rc =vfs_unlink (lower_dir_inode, lower_dentry, lower_mnt);if (rc) {printk (KERN_ERR "Error in vfs_unlink; rc = [%d]\n" , rc); goto out_unlock;
fs/namei.c +2 -2
@@ -2201,7 +2201,7 @@ asmlinkage long sys_rmdir(const char __ureturn do_rmdir (AT_FDCWD , pathname); } -int vfs_unlink (struct inode *dir,struct dentry *dentry) +int vfs_unlink (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt) {int error =may_delete (dir, dentry,0 );
@@ -2268,7 +2268,7 @@ static long do_unlinkat(int dfd, const c error =mnt_want_write (nd.mnt);if (error) goto exit2; - error =vfs_unlink (nd.dentry->d_inode, dentry); + error =vfs_unlink (nd.dentry->d_inode, dentry, nd.mnt);mnt_drop_write (nd.mnt); exit2:dput (dentry);
fs/nfsd/nfs4recover.c +1 -1
@@ -265,7 +265,7 @@ nfsd4_remove_clid_file(struct dentry *direturn -EINVAL ; }mutex_lock_nested (&dir->d_inode->i_mutex,I_MUTEX_PARENT ); - status =vfs_unlink (dir->d_inode, dentry); + status =vfs_unlink (dir->d_inode, dentry, rec_dir.mnt);mutex_unlock (&dir->d_inode->i_mutex);return status; }
fs/nfsd/vfs.c +1 -1
@@ -1768,7 +1768,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru host_err = -EPERM; } else#endif - host_err =vfs_unlink (dirp, rdentry); + host_err =vfs_unlink (dirp, rdentry, exp->ex_mnt); }else {/* It's RMDIR */ host_err =vfs_rmdir (dirp, rdentry, exp->ex_mnt); }
include/linux/fs.h +1 -1
@@ -1079,7 +1079,7 @@ extern int vfs_mknod(struct inode *, strextern int vfs_symlink (struct inode *,struct dentry *,struct vfsmount *,const char *,int );extern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rmdir (struct inode *,struct dentry *,struct vfsmount *); -extern int vfs_unlink (struct inode *,struct dentry *); +extern int vfs_unlink (struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *);/*
ipc/mqueue.c +1 -1
@@ -750,7 +750,7 @@ asmlinkage long sys_mq_unlink(const char err =mnt_want_write (mqueue_mnt);if (err) goto out_err; - err =vfs_unlink (dentry->d_parent->d_inode, dentry); + err =vfs_unlink (dentry->d_parent->d_inode, dentry, mqueue_mnt);mnt_drop_write (mqueue_mnt); out_err:dput (dentry);
PATCH 18/45 Pass struct vfsmount to the inode_unlink LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 2 +- include/linux/security.h | 10 +++++++--- security/dummy.c | 3 ++- security/security.c | 5 +++-- security/selinux/hooks.c | 5 +++-- 5 files changed, 16 insertions(+), 9 deletions(-)
fs/namei.c +1 -1
@@ -2217,7 +2217,7 @@ int vfs_unlink(struct inode *dir, structif (d_mountpoint (dentry)) error = -EBUSY ;else { - error =security_inode_unlink (dir, dentry); + error =security_inode_unlink (dir, dentry, mnt);if (!error) error = dir->i_op->unlink (dir, dentry); }
include/linux/security.h +7 -3
@@ -312,6 +312,7 @@ struct request_sock;* Check the permission to remove a hard link to a file. * @dir contains the inode structure of parent directory of the file. * @dentry contains the dentry structure for file to be unlinked. +* @mnt is the vfsmount corresponding to @dentry (may be NULL). * Return 0 if permission is granted. * @inode_symlink: * Check the permission to create a symbolic link to a file.
@@ -1248,7 +1249,8 @@ struct security_operations {int (*inode_link) (struct dentry *old_dentry,struct vfsmount *old_mnt,struct inode *dir,struct dentry *new_dentry,struct vfsmount *new_mnt); -int (*inode_unlink) (struct inode *dir,struct dentry *dentry); +int (*inode_unlink) (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt);int (*inode_symlink) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,const char *old_name); int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
@@ -1508,7 +1510,8 @@ int security_inode_create(struct inode *int security_inode_link (struct dentry *old_dentry,struct vfsmount *old_mnt,struct inode *dir,struct dentry *new_dentry,struct vfsmount *new_mnt); -int security_inode_unlink (struct inode *dir,struct dentry *dentry); +int security_inode_unlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt);int security_inode_symlink (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,const char *old_name); int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
@@ -1835,7 +1838,8 @@ static inline int security_inode_link (s }static inline int security_inode_unlink (struct inode *dir, -struct dentry *dentry) +struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +2 -1
@@ -275,7 +275,8 @@ static int dummy_inode_link (struct dentreturn 0 ; } -static int dummy_inode_unlink (struct inode *inode,struct dentry *dentry) +static int dummy_inode_unlink (struct inode *inode,struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/security.c +3 -2
@@ -346,11 +346,12 @@ int security_inode_link(struct dentry *o new_mnt); } -int security_inode_unlink (struct inode *dir,struct dentry *dentry) +int security_inode_unlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_unlink (dir, dentry); +return security_ops->inode_unlink (dir, dentry, mnt); } int security_inode_symlink(struct inode *dir, struct dentry *dentry,
security/selinux/hooks.c +3 -2
@@ -2205,11 +2205,12 @@ static int selinux_inode_link(struct denreturn may_link (dir, old_dentry,MAY_LINK ); } -static int selinux_inode_unlink (struct inode *dir,struct dentry *dentry) +static int selinux_inode_unlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) {int rc; - rc = secondary_ops->inode_unlink (dir, dentry); + rc = secondary_ops->inode_unlink (dir, dentry, mnt);if (rc)return rc;return may_link (dir, dentry,MAY_UNLINK );
PATCH 19/45 Add struct vfsmount parameters to vfs_rename()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/ecryptfs/inode.c | 7 ++++++- fs/namei.c | 19 ++++++++++++------- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- 4 files changed, 21 insertions(+), 10 deletions(-)
fs/ecryptfs/inode.c +6 -1
@@ -589,19 +589,24 @@ ecryptfs_rename(struct inode *old_dir, s {int rc;struct dentry *lower_old_dentry; +struct vfsmount *lower_old_mnt;struct dentry *lower_new_dentry; +struct vfsmount *lower_new_mnt;struct dentry *lower_old_dir_dentry;struct dentry *lower_new_dir_dentry; lower_old_dentry =ecryptfs_dentry_to_lower (old_dentry); + lower_old_mnt =ecryptfs_dentry_to_lower_mnt (old_dentry); lower_new_dentry =ecryptfs_dentry_to_lower (new_dentry); + lower_new_mnt =ecryptfs_dentry_to_lower_mnt (new_dentry);dget (lower_old_dentry);dget (lower_new_dentry); lower_old_dir_dentry =dget_parent (lower_old_dentry); lower_new_dir_dentry =dget_parent (lower_new_dentry);lock_rename (lower_old_dir_dentry, lower_new_dir_dentry); rc =vfs_rename (lower_old_dir_dentry->d_inode, lower_old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); + lower_old_mnt, lower_new_dir_dentry->d_inode, + lower_new_dentry, lower_new_mnt);if (rc) goto out_lock;fsstack_copy_attr_all (new_dir, lower_new_dir_dentry->d_inode);
fs/namei.c +12 -7
@@ -2513,7 +2513,8 @@ asmlinkage long sys_link(const char __us* locking]. */ static int vfs_rename_dir (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {int error =0 ;struct inode *target;
@@ -2556,7 +2557,8 @@ static int vfs_rename_dir(struct inode * }static int vfs_rename_other (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {struct inode *target;int error;
@@ -2584,7 +2586,8 @@ static int vfs_rename_other(struct inode }int vfs_rename (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {int error;int is_dir =S_ISDIR (old_dentry->d_inode->i_mode);
@@ -2613,9 +2616,11 @@ int vfs_rename(struct inode *old_dir, st old_name =fsnotify_oldname_init (old_dentry->d_name.name);if (is_dir) - error =vfs_rename_dir (old_dir,old_dentry,new_dir,new_dentry); + error =vfs_rename_dir (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);else - error =vfs_rename_other (old_dir,old_dentry,new_dir,new_dentry); + error =vfs_rename_other (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);if (!error) {const char *new_name = old_dentry->d_name.name; fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
@@ -2690,8 +2695,8 @@ static int do_rename(int olddfd, const c error =mnt_want_write (oldnd.mnt);if (error) goto exit5; - error =vfs_rename (old_dir->d_inode, old_dentry, - new_dir->d_inode, new_dentry); + error =vfs_rename (old_dir->d_inode, old_dentry, oldnd.mnt, + new_dir->d_inode, new_dentry, newnd.mnt);mnt_drop_write (oldnd.mnt); exit5:dput (new_dentry);
fs/nfsd/vfs.c +2 -1
@@ -1694,7 +1694,8 @@ nfsd_rename(struct svc_rqst *rqstp, struif (host_err) goto out_dput_new; - host_err =vfs_rename (fdir, odentry, tdir, ndentry); + host_err =vfs_rename (fdir, odentry, ffhp->fh_export->ex_mnt, + tdir, ndentry, tfhp->fh_export->ex_mnt);if (!host_err &&EX_ISSYNC (tfhp->fh_export)) { host_err =nfsd_sync_dir (tdentry);if (!host_err)
include/linux/fs.h +1 -1
@@ -1080,7 +1080,7 @@ extern int vfs_symlink(struct inode *, sextern int vfs_link (struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);extern int vfs_rmdir (struct inode *,struct dentry *,struct vfsmount *);extern int vfs_unlink (struct inode *,struct dentry *,struct vfsmount *); -extern int vfs_rename (struct inode *,struct dentry *,struct inode *,struct dentry *); +extern int vfs_rename (struct inode *,struct dentry *,struct vfsmount *,struct inode *,struct dentry *,struct vfsmount *);/* * VFS dentry helper functions.
Re: [AppArmor 19/45] Add struct vfsmount parameters to vfs_rename()
Quoted 2 lines · On Thu, Oct 25, 2007 at 11:40:43PM -0700, [email protected] wrote:
The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
You know, you really are supposed to understand the code you are modifying...
Quiz: what are those vfsmounts and how are they related?
Al, carefully abstaining from saying what he really thinks of LSM and its users...
Re: [AppArmor 19/45] Add struct vfsmount parameters to vfs_rename()
Quoted 7 lines · Al Viro
Quoted 2 lines · On Thu, Oct 25, 2007 at 11:40:43PM -0700, [email protected] wrote:
The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
You know, you really are supposed to understand the code you are modifying...
Quiz: what are those vfsmounts and how are they related?
In the current code, both vfsmounts are always identical, and so one of the two should go, agreed.
The thought behind passing both vfsmounts was that they could differ but point to the same super_block, in which case renames would still be possible at least from a filesystem point of view. The essential restriction here is that both files must be on the same device; the vfs restriction of not allowing cross-mount renames is arbitrary. Cross-mount renames are not allowed currently, and granted, they may not be very useful, either.
Quoted 2 lines
Al, carefully abstaining from saying what he really thinks of LSM and its users...
As always, it's a pleasure to see the genuine Viro charm at play.
Re: [AppArmor 19/45] Add struct vfsmount parameters to vfs_rename()
Quoted 8 lines · John Johansen
In the current code, both vfsmounts are always identical, and so one of the two should go, agreed.
The thought behind passing both vfsmounts was that they could differ but point to the same
super_block, in which case renames would still be possible at least from a filesystem point of view. The essential restriction here is that both files must be on the same device; the vfs restriction of not allowing cross-mount renames is arbitrary.
It's called "access control". Pathname-based one, BTW. And yes, it's 100% deliberate.
Quoted 2 lines
Cross-mount renames are not allowed currently, and granted, they may not be very useful, either.
<raised brows>
Excuse me, but IIRC LSM was supposed to _add_ restrictions, not to remove existing security checks.
PATCH 20/45 Pass struct vfsmount to the inode_rename LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/namei.c | 6 ++++-- include/linux/security.h | 13 ++++++++++--- security/dummy.c | 4 +++- security/security.c | 7 ++++--- security/selinux/hooks.c | 8 ++++++-- 5 files changed, 27 insertions(+), 11 deletions(-)
fs/namei.c +4 -2
@@ -2529,7 +2529,8 @@ static int vfs_rename_dir(struct inode *return error; } - error =security_inode_rename (old_dir, old_dentry, new_dir, new_dentry); + error =security_inode_rename (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);if (error)return error;
@@ -2563,7 +2564,8 @@ static int vfs_rename_other(struct inodestruct inode *target;int error; - error =security_inode_rename (old_dir, old_dentry, new_dir, new_dentry); + error =security_inode_rename (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt);if (error)return error;
include/linux/security.h +10 -3
@@ -350,8 +350,10 @@ struct request_sock;* Check for permission to rename a file or directory. * @old_dir contains the inode structure for parent of the old link. * @old_dentry contains the dentry structure of the old link. +* @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL). * @new_dir contains the inode structure for parent of the new link. * @new_dentry contains the dentry structure of the new link. +* @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL). * Return 0 if permission is granted. * @inode_readlink: * Check the permission to read the symbolic link.
@@ -1260,7 +1262,9 @@ struct security_operations {int (*inode_mknod) (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode,dev_t dev);int (*inode_rename) (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry); +struct vfsmount *old_mnt, +struct inode *new_dir,struct dentry *new_dentry, +struct vfsmount *new_mnt);int (*inode_readlink) (struct dentry *dentry,struct vfsmount *mnt);int (*inode_follow_link) (struct dentry *dentry,struct nameidata *nd);int (*inode_permission) (struct inode *inode,int mask,struct nameidata *nd);
@@ -1521,7 +1525,8 @@ int security_inode_rmdir(struct inode *dint security_inode_mknod (struct inode *dir,struct dentry *dentry,struct vfsmount *mnt,int mode,dev_t dev);int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry); +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt);int security_inode_readlink (struct dentry *dentry,struct vfsmount *mnt);int security_inode_follow_link (struct dentry *dentry,struct nameidata *nd);int security_inode_permission (struct inode *inode,int mask,struct nameidata *nd);
@@ -1877,8 +1882,10 @@ static inline int security_inode_mknod (static inline int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *new_dir, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/dummy.c +3 -1
@@ -307,8 +307,10 @@ static int dummy_inode_mknod (struct inostatic int dummy_inode_rename (struct inode *old_inode,struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *new_inode, -struct dentry *new_dentry) +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return 0 ; }
security/security.c +4 -3
@@ -387,13 +387,14 @@ int security_inode_mknod(struct inode *d }int security_inode_rename (struct inode *old_dir,struct dentry *old_dentry, -struct inode *new_dir,struct dentry *new_dentry) +struct vfsmount *old_mnt,struct inode *new_dir, +struct dentry *new_dentry,struct vfsmount *new_mnt) {if (unlikely (IS_PRIVATE (old_dentry->d_inode) || (new_dentry->d_inode &&IS_PRIVATE (new_dentry->d_inode))))return 0 ; -return security_ops->inode_rename (old_dir, old_dentry, - new_dir, new_dentry); +return security_ops->inode_rename (old_dir, old_dentry, old_mnt, + new_dir, new_dentry, new_mnt); }int security_inode_readlink (struct dentry *dentry,struct vfsmount *mnt)
security/selinux/hooks.c +6 -2
@@ -2246,8 +2246,12 @@ static int selinux_inode_mknod(struct inreturn may_create (dir, dentry,inode_mode_to_security_class (mode)); } -static int selinux_inode_rename (struct inode *old_inode,struct dentry *old_dentry, -struct inode *new_inode,struct dentry *new_dentry) +static int selinux_inode_rename (struct inode *old_inode, +struct dentry *old_dentry, +struct vfsmount *old_mnt, +struct inode *new_inode, +struct dentry *new_dentry, +struct vfsmount *new_mnt) {return may_rename (old_inode, old_dentry, new_inode, new_dentry); }
PATCH 21/45 Add a struct vfsmount parameter to vfs_setxattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/nfsd/vfs.c | 16 +++++++++++----- fs/xattr.c | 16 ++++++++-------- include/linux/xattr.h | 3 ++- 3 files changed, 21 insertions(+), 14 deletions(-)
fs/nfsd/vfs.c +11 -5
@@ -426,7 +426,8 @@ static ssize_t nfsd_getxattr(struct dent#if defined(CONFIG_NFSD_V4 )static int -set_nfsv4_acl_one (struct dentry *dentry,struct posix_acl *pacl,char *key) +set_nfsv4_acl_one (struct dentry *dentry,struct vfsmount *mnt, +struct posix_acl *pacl,char *key) {int len;size_t buflen;
@@ -445,7 +446,7 @@ set_nfsv4_acl_one(struct dentry *dentry, goto out; } - error =vfs_setxattr (dentry, key, buf, len,0 ); + error =vfs_setxattr (dentry, mnt, key, buf, len,0 ); out:kfree (buf);return error;
@@ -458,6 +459,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst__be32 error;int host_error;struct dentry *dentry; +struct vfsmount *mnt;struct inode *inode;struct posix_acl *pacl =NULL , *dpacl =NULL ;unsigned int flags =0 ;
@@ -468,6 +470,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstreturn error; dentry = fhp->fh_dentry; + mnt = fhp->fh_export->ex_mnt; inode = dentry->d_inode;if (S_ISDIR (inode->i_mode)) flags =NFS4_ACL_DIR ;
@@ -478,12 +481,14 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst }else if (host_error <0 ) goto out_nfserr; - host_error =set_nfsv4_acl_one (dentry, pacl,POSIX_ACL_XATTR_ACCESS ); + host_error =set_nfsv4_acl_one (dentry, mnt, pacl, +POSIX_ACL_XATTR_ACCESS );if (host_error <0 ) goto out_release;if (S_ISDIR (inode->i_mode)) - host_error =set_nfsv4_acl_one (dentry, dpacl,POSIX_ACL_XATTR_DEFAULT ); + host_error =set_nfsv4_acl_one (dentry, mnt, dpacl, +POSIX_ACL_XATTR_DEFAULT ); out_release:posix_acl_release (pacl);
@@ -2052,7 +2057,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i size =0 ;if (size) - error =vfs_setxattr (fhp->fh_dentry, name, value, size,0 ); + error =vfs_setxattr (fhp->fh_dentry, fhp->fh_export->ex_mnt, + name, value, size,0 );else {if (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;
fs/xattr.c +8 -8
@@ -67,8 +67,8 @@ xattr_permission(struct inode *inode, co }int -vfs_setxattr (struct dentry *dentry,char *name,void *value, -size_t size,int flags) +vfs_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +void *value,size_t size,int flags) {struct inode *inode = dentry->d_inode;int error;
@@ -191,8 +191,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);* Extended attribute SET operations */ static long -setxattr (struct dentry *d,char __user *name,void __user *value, -size_t size,int flags) +setxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, +void __user *value,size_t size,int flags) {int error;void *kvalue =NULL ;
@@ -219,7 +219,7 @@ setxattr(struct dentry *d, char __user * } } - error =vfs_setxattr (d, kname, kvalue, size, flags); + error =vfs_setxattr (dentry, mnt, kname, kvalue, size, flags);kfree (kvalue);return error; }
@@ -237,7 +237,7 @@ sys_setxattr(char __user *path, char __u error =mnt_want_write (nd.mnt);if (error)return error; - error =setxattr (nd.dentry, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags);mnt_drop_write (nd.mnt);path_release (&nd);return error;
@@ -256,7 +256,7 @@ sys_lsetxattr(char __user *path, char __ error =mnt_want_write (nd.mnt);if (error)return error; - error =setxattr (nd.dentry, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags);mnt_drop_write (nd.mnt);path_release (&nd);return error;
@@ -278,7 +278,7 @@ sys_fsetxattr(int fd, char __user *name, goto out_fput; dentry = f->f_path.dentry;audit_inode (NULL , dentry); - error =setxattr (dentry, name, value, size, flags); + error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags);mnt_drop_write (f->f_vfsmnt); out_fput:fput (f);
include/linux/xattr.h +2 -1
@@ -48,7 +48,8 @@ struct xattr_handler {ssize_t vfs_getxattr (struct dentry *,char *,void *,size_t );ssize_t vfs_listxattr (struct dentry *d,char *list,size_t size); -int vfs_setxattr (struct dentry *,char *,void *,size_t ,int ); +int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t , +int );int vfs_removexattr (struct dentry *,char *);ssize_t generic_getxattr (struct dentry *dentry,const char *name,void *buffer,size_t size);
PATCH 22/45 Pass struct vfsmount to the inode_setxattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 4 ++-- include/linux/security.h | 35 +++++++++++++++++++++-------------- security/commoncap.c | 4 ++-- security/dummy.c | 9 ++++++--- security/security.c | 15 +++++++++------ security/selinux/hooks.c | 8 ++++++-- 6 files changed, 46 insertions(+), 29 deletions(-)
fs/xattr.c +2 -2
@@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, strureturn error;mutex_lock (&inode->i_mutex); - error =security_inode_setxattr (dentry, name, value, size, flags); + error =security_inode_setxattr (dentry, mnt, name, value, size, flags);if (error) goto out; error = -EOPNOTSUPP ;
@@ -86,7 +86,7 @@ vfs_setxattr(struct dentry *dentry, stru error = inode->i_op->setxattr (dentry, name, value, size, flags);if (!error) {fsnotify_xattr (dentry); -security_inode_post_setxattr (dentry, name, value, +security_inode_post_setxattr (dentry, mnt, name, value, size, flags); } } else if (!strncmp(name, XATTR_SECURITY_PREFIX,
include/linux/security.h +21 -14
@@ -56,7 +56,7 @@ extern void cap_capset_set (struct task_extern int cap_bprm_set_security (struct linux_binprm *bprm);extern void cap_bprm_apply_creds (struct linux_binprm *bprm,int unsafe);extern int cap_bprm_secureexec (struct linux_binprm *bprm); -extern int cap_inode_setxattr (struct dentry *dentry,char *name,void *value,size_t size,int flags); +extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags);extern int cap_inode_removexattr (struct dentry *dentry,char *name);extern int cap_inode_need_killpriv (struct dentry *dentry);extern int cap_inode_killpriv (struct dentry *dentry);
@@ -398,11 +398,11 @@ struct request_sock;* inode. * @inode_setxattr: * Check permission before setting the extended attributes -* @value identified by @name for @dentry. +* @value identified by @name for @dentry and @mnt. * Return 0 if permission is granted. * @inode_post_setxattr: * Update inode security field after successful setxattr operation. -* @value identified by @name for @dentry. +* @value identified by @name for @dentry and @mnt. * @inode_getxattr: * Check permission before obtaining the extended attributes * identified by @name for @dentry.
@@ -1272,9 +1272,11 @@ struct security_operations { struct iattr *attr);int (*inode_getattr) (struct vfsmount *mnt,struct dentry *dentry);void (*inode_delete) (struct inode *inode); -int (*inode_setxattr) (struct dentry *dentry,char *name,void *value, -size_t size,int flags); -void (*inode_post_setxattr) (struct dentry *dentry,char *name,void *value, +int (*inode_setxattr) (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size,int flags); +void (*inode_post_setxattr) (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value,size_t size,int flags);int (*inode_getxattr) (struct dentry *dentry,char *name);int (*inode_listxattr) (struct dentry *dentry);
@@ -1534,10 +1536,11 @@ int security_inode_setattr(struct dentry struct iattr *attr);int security_inode_getattr (struct vfsmount *mnt,struct dentry *dentry);void security_inode_delete (struct inode *inode); -int security_inode_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags); -void security_inode_post_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags); +int security_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size,int flags); +void security_inode_post_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags);int security_inode_getxattr (struct dentry *dentry,char *name);int security_inode_listxattr (struct dentry *dentry);int security_inode_removexattr (struct dentry *dentry,char *name);
@@ -1924,14 +1927,18 @@ static inline int security_inode_getattrstatic inline void security_inode_delete (struct inode *inode) { } -static inline int security_inode_setxattr (struct dentry *dentry,char *name, +static inline int security_inode_setxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name,void *value,size_t size,int flags) { -return cap_inode_setxattr (dentry, name, value, size, flags); +return cap_inode_setxattr (dentry, mnt, name, value, size, flags); } -static inline void security_inode_post_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags) +static inline void security_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name, +void *value,size_t size, +int flags) { }static inline int security_inode_getxattr (struct dentry *dentry,char *name)
security/commoncap.c +2 -2
@@ -361,8 +361,8 @@ int cap_bprm_secureexec (struct linux_bi current->egid != current->gid); } -int cap_inode_setxattr (struct dentry *dentry,char *name,void *value, -size_t size,int flags) +int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +void *value,size_t size,int flags) {if (!strcmp (name,XATTR_NAME_CAPS )) {if (!capable (CAP_SETFCAP ))
security/dummy.c +6 -3
@@ -347,8 +347,9 @@ static void dummy_inode_delete (struct ireturn ; } -static int dummy_inode_setxattr (struct dentry *dentry,char *name,void *value, -size_t size,int flags) +static int dummy_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -357,7 +358,9 @@ static int dummy_inode_setxattr (structreturn 0 ; } -static void dummy_inode_post_setxattr (struct dentry *dentry,char *name,void *value, +static void dummy_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value,size_t size,int flags) { }
security/security.c +9 -6
@@ -441,20 +441,23 @@ void security_inode_delete(struct inode security_ops->inode_delete (inode); } -int security_inode_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags) +int security_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size,int flags) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_setxattr (dentry, name, value, size, flags); +return security_ops->inode_setxattr (dentry, mnt, name, value, size, + flags); } -void security_inode_post_setxattr (struct dentry *dentry,char *name, -void *value,size_t size,int flags) +void security_inode_post_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return ; - security_ops->inode_post_setxattr (dentry, name, value, size, flags); + security_ops->inode_post_setxattr (dentry, mnt, name, value, size, + flags); }int security_inode_getxattr (struct dentry *dentry,char *name)
security/selinux/hooks.c +6 -2
@@ -2332,7 +2332,9 @@ static int selinux_inode_setotherxattr(sreturn dentry_has_perm (current,NULL , dentry,FILE__SETATTR ); } -static int selinux_inode_setxattr (struct dentry *dentry,char *name,void *value,size_t size,int flags) +static int selinux_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags) {struct task_security_struct *tsec = current->security;struct inode *inode = dentry->d_inode;
@@ -2381,7 +2383,9 @@ static int selinux_inode_setxattr(struct &ad); } -static void selinux_inode_post_setxattr (struct dentry *dentry,char *name, +static void selinux_inode_post_setxattr (struct dentry *dentry, +struct vfsmount *mnt, +char *name,void *value,size_t size,int flags) {struct inode *inode = dentry->d_inode;
PATCH 23/45 Add a struct vfsmount parameter to vfs_getxattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/nfsd/nfs4xdr.c | 2 +- fs/nfsd/vfs.c | 21 ++++++++++++--------- fs/xattr.c | 14 ++++++++------ include/linux/nfsd/nfsd.h | 3 ++- include/linux/xattr.h | 3 ++- 5 files changed, 25 insertions(+), 18 deletions(-)
fs/nfsd/nfs4xdr.c +1 -1
@@ -1496,7 +1496,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s }if (bmval0 & (FATTR4_WORD0_ACL |FATTR4_WORD0_ACLSUPPORT |FATTR4_WORD0_SUPPORTED_ATTRS )) { - err =nfsd4_get_nfs4_acl (rqstp, dentry, &acl); + err =nfsd4_get_nfs4_acl (rqstp, dentry, exp->ex_mnt, &acl); aclsupport = (err ==0 );if (bmval0 &FATTR4_WORD0_ACL ) {if (err == -EOPNOTSUPP )
fs/nfsd/vfs.c +12 -9
@@ -408,11 +408,12 @@ out_nfserr:#if defined(CONFIG_NFSD_V2_ACL ) || \ defined(CONFIG_NFSD_V3_ACL ) || \ defined(CONFIG_NFSD_V4 ) -static ssize_t nfsd_getxattr (struct dentry *dentry,char *key,void **buf) +static ssize_t nfsd_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *key,void **buf) {ssize_t buflen; - buflen =vfs_getxattr (dentry, key,NULL ,0 ); + buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 );if (buflen <=0 )return buflen;
@@ -420,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dentif (!*buf)return -ENOMEM ; - return vfs_getxattr(dentry, key, *buf, buflen); + return vfs_getxattr(dentry, mnt, key, *buf, buflen); }#endif
@@ -501,13 +502,13 @@ out_nfserr: }static struct posix_acl * -_get_posix_acl (struct dentry *dentry,char *key) +_get_posix_acl (struct dentry *dentry,struct vfsmount *mnt,char *key) {void *buf =NULL ;struct posix_acl *pacl =NULL ;int buflen; - buflen =nfsd_getxattr (dentry, key, &buf); + buflen =nfsd_getxattr (dentry, mnt, key, &buf);if (!buflen) buflen = -ENODATA ;if (buflen <=0 )
@@ -519,14 +520,15 @@ _get_posix_acl(struct dentry *dentry, ch }int -nfsd4_get_nfs4_acl (struct svc_rqst *rqstp,struct dentry *dentry,struct nfs4_acl **acl) +nfsd4_get_nfs4_acl (struct svc_rqst *rqstp,struct dentry *dentry, +struct vfsmount *mnt,struct nfs4_acl **acl) {struct inode *inode = dentry->d_inode;int error =0 ;struct posix_acl *pacl =NULL , *dpacl =NULL ;unsigned int flags =0 ; - pacl =_get_posix_acl (dentry,POSIX_ACL_XATTR_ACCESS ); + pacl =_get_posix_acl (dentry, mnt,POSIX_ACL_XATTR_ACCESS );if (IS_ERR (pacl) &&PTR_ERR (pacl) == -ENODATA ) pacl =posix_acl_from_mode (inode->i_mode,GFP_KERNEL );if (IS_ERR (pacl)) {
@@ -536,7 +538,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqst }if (S_ISDIR (inode->i_mode)) { - dpacl =_get_posix_acl (dentry,POSIX_ACL_XATTR_DEFAULT ); + dpacl =_get_posix_acl (dentry, mnt,POSIX_ACL_XATTR_DEFAULT );if (IS_ERR (dpacl) &&PTR_ERR (dpacl) == -ENODATA ) dpacl =NULL ;else if (IS_ERR (dpacl)) {
@@ -2012,7 +2014,8 @@ nfsd_get_posix_acl(struct svc_fh *fhp, ireturn ERR_PTR (-EOPNOTSUPP ); } - size =nfsd_getxattr (fhp->fh_dentry, name, &value); + size =nfsd_getxattr (fhp->fh_dentry, fhp->fh_export->ex_mnt, name, + &value);if (size <0 )return ERR_PTR (size);
fs/xattr.c +8 -6
@@ -104,7 +104,8 @@ out:EXPORT_SYMBOL_GPL (vfs_setxattr);ssize_t -vfs_getxattr (struct dentry *dentry,char *name,void *value,size_t size) +vfs_getxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +void *value,size_t size) {struct inode *inode = dentry->d_inode;int error;
@@ -289,7 +290,8 @@ out_fput:* Extended attribute GET operations */ static ssize_t -getxattr (struct dentry *d,char __user *name,void __user *value,size_t size) +getxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, +void __user *value,size_t size) {ssize_t error;void *kvalue =NULL ;
@@ -309,7 +311,7 @@ getxattr(struct dentry *d, char __user *return -ENOMEM ; } - error =vfs_getxattr (d, kname, kvalue, size); + error =vfs_getxattr (dentry, mnt, kname, kvalue, size);if (error >0 ) {if (size &©_to_user (value, kvalue, error)) error = -EFAULT ;
@@ -332,7 +334,7 @@ sys_getxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =getxattr (nd.dentry, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size);path_release (&nd);return error; }
@@ -347,7 +349,7 @@ sys_lgetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =getxattr (nd.dentry, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size);path_release (&nd);return error; }
@@ -362,7 +364,7 @@ sys_fgetxattr(int fd, char __user *name,if (!f)return error;audit_inode (NULL , f->f_path.dentry); - error =getxattr (f->f_path.dentry, name, value, size); + error =getxattr (f->f_path.dentry, f->f_path.mnt, name, value, size);fput (f);return error; }
include/linux/nfsd/nfsd.h +2 -1
@@ -79,7 +79,8 @@ __be32 nfsd_setattr(struct svc_rqst *,#ifdef CONFIG_NFSD_V4 __be32 nfsd4_set_nfs4_acl (struct svc_rqst *,struct svc_fh *,struct nfs4_acl *); -int nfsd4_get_nfs4_acl (struct svc_rqst *,struct dentry *,struct nfs4_acl **); +int nfsd4_get_nfs4_acl (struct svc_rqst *,struct dentry *, +struct vfsmount *mnt,struct nfs4_acl **);#endif /* CONFIG_NFSD_V4 */ __be32 nfsd_create(struct svc_rqst *, struct svc_fh *, char *name, int len, struct iattr *attrs,
include/linux/xattr.h +2 -1
@@ -46,7 +46,8 @@ struct xattr_handler { size_t size, int flags); }; -ssize_t vfs_getxattr (struct dentry *,char *,void *,size_t ); +ssize_t vfs_getxattr (struct dentry *,struct vfsmount *,char *,void *, +size_t );ssize_t vfs_listxattr (struct dentry *d,char *list,size_t size);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ,int );
PATCH 24/45 Pass struct vfsmount to the inode_getxattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 2 +- include/linux/security.h | 11 +++++++---- security/dummy.c | 3 ++- security/security.c | 5 +++-- security/selinux/hooks.c | 3 ++- 5 files changed, 15 insertions(+), 9 deletions(-)
fs/xattr.c +1 -1
@@ -114,7 +114,7 @@ vfs_getxattr(struct dentry *dentry, struif (error)return error; - error =security_inode_getxattr (dentry, name); + error =security_inode_getxattr (dentry, mnt, name);if (error)return error;
include/linux/security.h +7 -4
@@ -405,7 +405,7 @@ struct request_sock;* @value identified by @name for @dentry and @mnt. * @inode_getxattr: * Check permission before obtaining the extended attributes -* identified by @name for @dentry. +* identified by @name for @dentry and @mnt. * Return 0 if permission is granted. * @inode_listxattr: * Check permission before obtaining the list of extended attribute
@@ -1278,7 +1278,8 @@ struct security_operations {struct vfsmount *mnt, char *name, void *value, size_t size, int flags); -int (*inode_getxattr) (struct dentry *dentry,char *name); +int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt, +char *name);int (*inode_listxattr) (struct dentry *dentry);int (*inode_removexattr) (struct dentry *dentry,char *name);int (*inode_need_killpriv) (struct dentry *dentry);
@@ -1541,7 +1542,8 @@ int security_inode_setxattr(struct dentrvoid security_inode_post_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags); -int security_inode_getxattr (struct dentry *dentry,char *name); +int security_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name);int security_inode_listxattr (struct dentry *dentry);int security_inode_removexattr (struct dentry *dentry,char *name);int security_inode_need_killpriv (struct dentry *dentry);
@@ -1941,7 +1943,8 @@ static inline void security_inode_post_s int flags) { } -static inline int security_inode_getxattr (struct dentry *dentry,char *name) +static inline int security_inode_getxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {return 0 ; }
security/dummy.c +2 -1
@@ -365,7 +365,8 @@ static void dummy_inode_post_setxattr (s { } -static int dummy_inode_getxattr (struct dentry *dentry,char *name) +static int dummy_inode_getxattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {return 0 ; }
security/security.c +3 -2
@@ -460,11 +460,12 @@ void security_inode_post_setxattr(struct flags); } -int security_inode_getxattr (struct dentry *dentry,char *name) +int security_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_getxattr (dentry, name); +return security_ops->inode_getxattr (dentry, mnt, name); }int security_inode_listxattr (struct dentry *dentry)
security/selinux/hooks.c +2 -1
@@ -2409,7 +2409,8 @@ static void selinux_inode_post_setxattr(return ; } -static int selinux_inode_getxattr (struct dentry *dentry,char *name) +static int selinux_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); }
PATCH 25/45 Add a struct vfsmount parameter to vfs_listxattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/xattr.c | 25 ++++++++++++++----------- include/linux/xattr.h | 3 ++- 2 files changed, 16 insertions(+), 12 deletions(-)
fs/xattr.c +14 -11
@@ -141,18 +141,20 @@ vfs_getxattr(struct dentry *dentry, struEXPORT_SYMBOL_GPL (vfs_getxattr);ssize_t -vfs_listxattr (struct dentry *d,char *list,size_t size) +vfs_listxattr (struct dentry *dentry,struct vfsmount *mnt,char *list, +size_t size) { +struct inode *inode = dentry->d_inode;ssize_t error; - error =security_inode_listxattr (d); + error =security_inode_listxattr (dentry);if (error)return error; error = -EOPNOTSUPP ; -if (d->d_inode->i_op && d->d_inode->i_op->listxattr) { - error = d->d_inode->i_op->listxattr (d, list, size); - }else { - error =security_inode_listsecurity (d->d_inode, list, size); +if (inode->i_op && inode->i_op->listxattr) + error = inode->i_op->listxattr (dentry, list, size); +else { + error =security_inode_listsecurity (inode, list, size);if (size && error > size) error = -ERANGE ; }
@@ -373,7 +375,8 @@ sys_fgetxattr(int fd, char __user *name,* Extended attribute LIST operations */ static ssize_t -listxattr (struct dentry *d,char __user *list,size_t size) +listxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *list, +size_t size) {ssize_t error;char *klist =NULL ;
@@ -386,7 +389,7 @@ listxattr(struct dentry *d, char __userreturn -ENOMEM ; } - error =vfs_listxattr (d, klist, size); + error =vfs_listxattr (dentry, mnt, klist, size);if (error >0 ) {if (size &©_to_user (list, klist, error)) error = -EFAULT ;
@@ -408,7 +411,7 @@ sys_listxattr(char __user *path, char __ error =user_path_walk (path, &nd);if (error)return error; - error =listxattr (nd.dentry, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size);path_release (&nd);return error; }
@@ -422,7 +425,7 @@ sys_llistxattr(char __user *path, char _ error =user_path_walk_link (path, &nd);if (error)return error; - error =listxattr (nd.dentry, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size);path_release (&nd);return error; }
@@ -437,7 +440,7 @@ sys_flistxattr(int fd, char __user *listif (!f)return error;audit_inode (NULL , f->f_path.dentry); - error =listxattr (f->f_path.dentry, list, size); + error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size);fput (f);return error; }
include/linux/xattr.h +2 -1
@@ -48,7 +48,8 @@ struct xattr_handler {ssize_t vfs_getxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ); -ssize_t vfs_listxattr (struct dentry *d,char *list,size_t size); +ssize_t vfs_listxattr (struct dentry *d,struct vfsmount *,char *list, +size_t size);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ,int );int vfs_removexattr (struct dentry *,char *);
PATCH 26/45 Pass struct vfsmount to the inode_listxattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 2 +- include/linux/security.h | 9 +++++---- security/dummy.c | 2 +- security/security.c | 4 ++-- security/selinux/hooks.c | 2 +- 5 files changed, 10 insertions(+), 9 deletions(-)
fs/xattr.c +1 -1
@@ -147,7 +147,7 @@ vfs_listxattr(struct dentry *dentry, strstruct inode *inode = dentry->d_inode;ssize_t error; - error =security_inode_listxattr (dentry); + error =security_inode_listxattr (dentry, mnt);if (error)return error; error = -EOPNOTSUPP ;
include/linux/security.h +5 -4
@@ -409,7 +409,7 @@ struct request_sock;* Return 0 if permission is granted. * @inode_listxattr: * Check permission before obtaining the list of extended attribute -* names for @dentry. +* names for @dentry and @mnt. * Return 0 if permission is granted. * @inode_removexattr: * Check permission before removing the extended attribute
@@ -1280,7 +1280,7 @@ struct security_operations { size_t size, int flags);int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt,char *name); -int (*inode_listxattr) (struct dentry *dentry); +int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt);int (*inode_removexattr) (struct dentry *dentry,char *name);int (*inode_need_killpriv) (struct dentry *dentry);int (*inode_killpriv) (struct dentry *dentry);
@@ -1544,7 +1544,7 @@ void security_inode_post_setxattr(struct int flags);int security_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt,char *name); -int security_inode_listxattr (struct dentry *dentry); +int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt);int security_inode_removexattr (struct dentry *dentry,char *name);int security_inode_need_killpriv (struct dentry *dentry);int security_inode_killpriv (struct dentry *dentry);
@@ -1949,7 +1949,8 @@ static inline int security_inode_getxattreturn 0 ; } -static inline int security_inode_listxattr (struct dentry *dentry) +static inline int security_inode_listxattr (struct dentry *dentry, +struct vfsmount *mnt) {return 0 ; }
security/dummy.c +1 -1
@@ -371,7 +371,7 @@ static int dummy_inode_getxattr (structreturn 0 ; } -static int dummy_inode_listxattr (struct dentry *dentry) +static int dummy_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) {return 0 ; }
security/security.c +2 -2
@@ -468,11 +468,11 @@ int security_inode_getxattr(struct dentrreturn security_ops->inode_getxattr (dentry, mnt, name); } -int security_inode_listxattr (struct dentry *dentry) +int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_listxattr (dentry); +return security_ops->inode_listxattr (dentry, mnt); }int security_inode_removexattr (struct dentry *dentry,char *name)
security/selinux/hooks.c +1 -1
@@ -2415,7 +2415,7 @@ static int selinux_inode_getxattr (strucreturn dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } -static int selinux_inode_listxattr (struct dentry *dentry) +static int selinux_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); }
PATCH 27/45 Add a struct vfsmount parameter to vfs_removexattr()
In reply to [email protected]The vfsmount will be passed down to the LSM hook so that LSMs can compute pathnames.
--- fs/nfsd/vfs.c | 7 ++++--- fs/xattr.c | 12 ++++++------ include/linux/xattr.h | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-)
fs/nfsd/vfs.c +4 -3
@@ -2027,6 +2027,7 @@ nfsd_get_posix_acl(struct svc_fh *fhp, iint nfsd_set_posix_acl (struct svc_fh *fhp,int type,struct posix_acl *acl) { +struct vfsmount *mnt;struct inode *inode = fhp->fh_dentry->d_inode;char *name;void *value =NULL ;
@@ -2059,14 +2060,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i }else size =0 ; + mnt = fhp->fh_export->ex_mnt;if (size) - error =vfs_setxattr (fhp->fh_dentry, fhp->fh_export->ex_mnt, - name, value, size,0 ); + error =vfs_setxattr (fhp->fh_dentry, mnt, name, value, size,0 );else {if (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;else { - error =vfs_removexattr (fhp->fh_dentry, name); + error =vfs_removexattr (fhp->fh_dentry, mnt, name);if (error == -ENODATA ) error =0 ; }
fs/xattr.c +6 -6
@@ -163,7 +163,7 @@ vfs_listxattr(struct dentry *dentry, strEXPORT_SYMBOL_GPL (vfs_listxattr);int -vfs_removexattr (struct dentry *dentry,char *name) +vfs_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name) {struct inode *inode = dentry->d_inode;int error;
@@ -449,7 +449,7 @@ sys_flistxattr(int fd, char __user *list* Extended attribute REMOVE operations */ static long -removexattr (struct dentry *d,char __user *name) +removexattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name) {int error;char kname[XATTR_NAME_MAX +1 ];
@@ -460,7 +460,7 @@ removexattr(struct dentry *d, char __useif (error <0 )return error; -return vfs_removexattr (d, kname); +return vfs_removexattr (dentry, mnt, kname); }asmlinkage long
@@ -472,7 +472,7 @@ sys_removexattr(char __user *path, char error =user_path_walk (path, &nd);if (error)return error; - error =removexattr (nd.dentry, name); + error =removexattr (nd.dentry, nd.mnt, name);path_release (&nd);return error; }
@@ -486,7 +486,7 @@ sys_lremovexattr(char __user *path, char error =user_path_walk_link (path, &nd);if (error)return error; - error =removexattr (nd.dentry, name); + error =removexattr (nd.dentry, nd.mnt, name);path_release (&nd);return error; }
@@ -503,7 +503,7 @@ sys_fremovexattr(int fd, char __user *nareturn error; dentry = f->f_path.dentry;audit_inode (NULL , dentry); - error =removexattr (dentry, name); + error =removexattr (dentry, f->f_path.mnt, name);fput (f);return error; }
include/linux/xattr.h +1 -1
@@ -52,7 +52,7 @@ ssize_t vfs_listxattr(struct dentry *d, size_t size);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t ,int ); -int vfs_removexattr (struct dentry *,char *); +int vfs_removexattr (struct dentry *,struct vfsmount *,char *);ssize_t generic_getxattr (struct dentry *dentry,const char *name,void *buffer,size_t size);ssize_t generic_listxattr (struct dentry *dentry,char *buffer,size_t buffer_size);
PATCH 28/45 Pass struct vfsmount to the inode_removexattr LSM hook
In reply to [email protected]This is needed for computing pathnames in the AppArmor LSM.
--- fs/xattr.c | 2 +- include/linux/security.h | 14 +++++++++----- security/commoncap.c | 3 ++- security/dummy.c | 3 ++- security/security.c | 5 +++-- security/selinux/hooks.c | 3 ++- 6 files changed, 19 insertions(+), 11 deletions(-)
fs/xattr.c +1 -1
@@ -175,7 +175,7 @@ vfs_removexattr(struct dentry *dentry, sif (error)return error; - error =security_inode_removexattr (dentry, name); + error =security_inode_removexattr (dentry, mnt, name);if (error)return error;
include/linux/security.h +9 -5
@@ -57,7 +57,8 @@ extern int cap_bprm_set_security (structextern void cap_bprm_apply_creds (struct linux_binprm *bprm,int unsafe);extern int cap_bprm_secureexec (struct linux_binprm *bprm);extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags); -extern int cap_inode_removexattr (struct dentry *dentry,char *name); +extern int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name);extern int cap_inode_need_killpriv (struct dentry *dentry);extern int cap_inode_killpriv (struct dentry *dentry);extern int cap_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);
@@ -1281,7 +1282,8 @@ struct security_operations {int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt,char *name);int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt); -int (*inode_removexattr) (struct dentry *dentry,char *name); +int (*inode_removexattr) (struct dentry *dentry,struct vfsmount *mnt, +char *name);int (*inode_need_killpriv) (struct dentry *dentry);int (*inode_killpriv) (struct dentry *dentry);int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);
@@ -1545,7 +1547,8 @@ void security_inode_post_setxattr(structint security_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt,char *name);int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt); -int security_inode_removexattr (struct dentry *dentry,char *name); +int security_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name);int security_inode_need_killpriv (struct dentry *dentry);int security_inode_killpriv (struct dentry *dentry);int security_inode_getsecurity (const struct inode *inode,const char *name,void *buffer,size_t size,int err);
@@ -1955,9 +1958,10 @@ static inline int security_inode_listxatreturn 0 ; } -static inline int security_inode_removexattr (struct dentry *dentry,char *name) +static inline int security_inode_removexattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) { -return cap_inode_removexattr (dentry, name); +return cap_inode_removexattr (dentry, mnt, name); }static inline int security_inode_need_killpriv (struct dentry *dentry)
security/commoncap.c +2 -1
@@ -375,7 +375,8 @@ int cap_inode_setxattr(struct dentry *dereturn 0 ; } -int cap_inode_removexattr (struct dentry *dentry,char *name) +int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) {if (!strcmp (name,XATTR_NAME_CAPS )) {if (!capable (CAP_SETFCAP ))
security/dummy.c +2 -1
@@ -376,7 +376,8 @@ static int dummy_inode_listxattr (structreturn 0 ; } -static int dummy_inode_removexattr (struct dentry *dentry,char *name) +static int dummy_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/security.c +3 -2
@@ -475,11 +475,12 @@ int security_inode_listxattr(struct dentreturn security_ops->inode_listxattr (dentry, mnt); } -int security_inode_removexattr (struct dentry *dentry,char *name) +int security_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, +char *name) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_removexattr (dentry, name); +return security_ops->inode_removexattr (dentry, mnt, name); }int security_inode_need_killpriv (struct dentry *dentry)
security/selinux/hooks.c +2 -1
@@ -2420,7 +2420,8 @@ static int selinux_inode_listxattr (strureturn dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } -static int selinux_inode_removexattr (struct dentry *dentry,char *name) +static int selinux_inode_removexattr (struct dentry *dentry, +struct vfsmount *mnt,char *name) {if (strcmp (name,XATTR_NAME_SELINUX ))return selinux_inode_setotherxattr (dentry, name);
PATCH 29/45 Fix __d_path() for lazy unmounts and make it unambiguous
In reply to [email protected]First, when __d_path() hits a lazily unmounted mount point, it tries to prepend the name of the lazily unmounted dentry to the path name. It gets this wrong, and also overwrites the slash that separates the name from the following pathname component. This patch fixes that; if a process was in directory /foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the missing slash), while the new result with this patch is ``foo/bar''.
Second, it isn't always possible to tell from the __d_path() result whether the specified root and rootmnt (i.e., the chroot) was reached. We need an unambiguous result for AppArmor at least though, so we make sure that paths will only start with a slash if the path leads all the way up to the root.
We also add a @fail_deleted argument, which allows to get rid of some of the mess in sys_getcwd().
This patch leaves getcwd() and d_path() as they were before for everything except for bind-mounted directories; for them, it reports ``/foo/bar'' instead of ``foobar'' in the example described above.
--- fs/dcache.c | 169 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 98 insertions(+), 71 deletions(-)
fs/dcache.c +98 -71
@@ -1763,52 +1763,51 @@ shouldnt_be_hashed: }/** -* d_path - return the path of a dentry +* __d_path - return the path of a dentry * @dentry: dentry to report * @vfsmnt: vfsmnt to which the dentry belongs * @root: root dentry * @rootmnt: vfsmnt to which the root dentry belongs * @buffer: buffer to return value in * @buflen: buffer length +* @fail_deleted: what to return for deleted files * -* Convert a dentry into an ASCII path name. If the entry has been deleted +* Convert a dentry into an ASCII path name. If the entry has been deleted, +* then if @fail_deleted is true, ERR_PTR(-ENOENT) is returned. Otherwise, * the string " (deleted)" is appended. Note that this is ambiguous. * -* Returns the buffer or an error code if the path was too long. +* If @dentry is not connected to @root, the path returned will be relative +* (i.e., it will not start with a slash). * -* "buflen" should be positive. Caller holds the dcache_lock. +* Returns the buffer or an error code. */ -static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -struct dentry *root,struct vfsmount *rootmnt, -char *buffer,int buflen) -{ -char * end = buffer+buflen; -char * retval; -int namelen; +static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, +struct dentry *root,struct vfsmount *rootmnt, +char *buffer,int buflen,int fail_deleted) +{ +int namelen, is_slash; + +if (buflen <2 ) +return ERR_PTR (-ENAMETOOLONG ); + buffer += --buflen; + *buffer ='\0' ; - *--end ='\0' ; - buflen--; +spin_lock (&dcache_lock);if (!IS_ROOT (dentry) &&d_unhashed (dentry)) { - buflen -=10 ; - end -=10 ; -if (buflen <0 ) +if (fail_deleted) { + buffer =ERR_PTR (-ENOENT ); + goto out; + } +if (buflen <10 ) goto Elong; -memcpy (end," (deleted)" ,10 ); + buflen -=10 ; + buffer -=10 ; +memcpy (buffer," (deleted)" ,10 ); } - -if (buflen <1 ) - goto Elong; -/* Get '/' right */ - retval = end-1 ; - *retval ='/' ; - -for (;;) { +while (dentry != root || vfsmnt != rootmnt) {struct dentry * parent; -if (dentry == root && vfsmnt == rootmnt) -break ;if (dentry == vfsmnt->mnt_root ||IS_ROOT (dentry)) { -/* Global root? */ spin_lock (&vfsmount_lock);if (vfsmnt->mnt_parent == vfsmnt) {spin_unlock (&vfsmount_lock);
@@ -1822,33 +1821,72 @@ static char * __d_path( struct dentry *d parent = dentry->d_parent;prefetch (parent); namelen = dentry->d_name.len; - buflen -= namelen +1 ; -if (buflen <0 ) +if (buflen < namelen +1 ) goto Elong; - end -= namelen; -memcpy (end, dentry->d_name.name, namelen); - *--end ='/' ; - retval = end; + buflen -= namelen +1 ; + buffer -= namelen; +memcpy (buffer, dentry->d_name.name, namelen); + *--buffer ='/' ; dentry = parent; } +/* Get '/' right. */ +if (*buffer !='/' ) + *--buffer ='/' ; -return retval; +out: +spin_unlock (&dcache_lock); +return buffer; global_root: +/* +* We went past the (vfsmount, dentry) we were looking for and have +* either hit a root dentry, a lazily unmounted dentry, an +* unconnected dentry, or the file is on a pseudo filesystem. +*/ namelen = dentry->d_name.len; - buflen -= namelen; -if (buflen <0 ) + is_slash = (namelen ==1 && *dentry->d_name.name =='/' ); +if (is_slash || (dentry->d_sb->s_flags &MS_NOUSER )) { +/* +* Make sure we won't return a pathname starting with '/'. +* +* Historically, we also glue together the root dentry and +* remaining name for pseudo filesystems like pipefs, which +* have the MS_NOUSER flag set. This results in pathnames +* like "pipe:[439336]". +*/ +if (*buffer =='/' ) { + buffer++; + buflen++; + } +if (is_slash) + goto out; + } +if (buflen < namelen) goto Elong; - retval -= namelen-1 ;/* hit the slash */ -memcpy (retval, dentry->d_name.name, namelen); -return retval; + buffer -= namelen; +memcpy (buffer, dentry->d_name.name, namelen); + goto out; + Elong: -return ERR_PTR (-ENAMETOOLONG ); + buffer =ERR_PTR (-ENAMETOOLONG ); + goto out; +} + +static char *__connect_d_path (char *path,char *buffer) +{ +if (!IS_ERR (path) && *path !='/' ) { +/* Pretend that disconnected paths are hanging off the root. */ +if (path == buffer) + path =ERR_PTR (-ENAMETOOLONG ); +else + *--path ='/' ; + } +return path; }/* write full pathname into buffer and return start of pathname */ -char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -char *buf,int buflen) +char *d_path (struct dentry *dentry,struct vfsmount *vfsmnt,char *buf, +int buflen) {char *res;struct vfsmount *rootmnt;
@@ -1868,9 +1906,8 @@ char * d_path(struct dentry *dentry, str rootmnt =mntget (current->fs->rootmnt); root =dget (current->fs->root);read_unlock (¤t->fs->lock); -spin_lock (&dcache_lock); - res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen); -spin_unlock (&dcache_lock); + res =__d_path (dentry, vfsmnt, root, rootmnt, buf, buflen,0 ); + res =__connect_d_path (res, buf);dput (root);mntput (rootmnt);return res;
@@ -1917,10 +1954,10 @@ char *dynamic_dname(struct dentry *dentr*/ asmlinkage long sys_getcwd (char __user *buf,unsigned long size) { -int error; +int error, len;struct vfsmount *pwdmnt, *rootmnt;struct dentry *pwd, *root; -char *page = (char *)__get_free_page (GFP_USER ); +char *page = (char *)__get_free_page (GFP_USER ), *cwd;if (!page)return -ENOMEM ;
@@ -1932,29 +1969,19 @@ asmlinkage long sys_getcwd(char __user * root =dget (current->fs->root);read_unlock (¤t->fs->lock); - error = -ENOENT ; -/* Has the current directory has been unlinked? */ -spin_lock (&dcache_lock); -if (pwd->d_parent == pwd || !d_unhashed (pwd)) { -unsigned long len; -char * cwd; - - cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ); -spin_unlock (&dcache_lock); - - error =PTR_ERR (cwd); -if (IS_ERR (cwd)) - goto out; - - error = -ERANGE ; - len =PAGE_SIZE + page - cwd; -if (len <= size) { - error = len; -if (copy_to_user (buf, cwd, len)) - error = -EFAULT ; - } - }else -spin_unlock (&dcache_lock); + cwd =__d_path (pwd, pwdmnt, root, rootmnt, page,PAGE_SIZE ,1 ); + cwd =__connect_d_path (cwd, page); + error =PTR_ERR (cwd); +if (IS_ERR (cwd)) + goto out; + + error = -ERANGE ; + len =PAGE_SIZE + page - cwd; +if (len <= size) { + error = len; +if (copy_to_user (buf, cwd, len)) + error = -EFAULT ; + } out:dput (pwd);
PATCH 30/45 Make d_path() consistent across mount operations
In reply to [email protected]The path that __d_path() computes can become slightly inconsistent when it races with mount operations: it grabs the vfsmount_lock when traversing mount points but immediately drops it again, only to re-grab it when it reaches the next mount point. The result is that the filename computed is not always consisent, and the file may never have had that name. (This is unlikely, but still possible.)
Fix this by grabbing the vfsmount_lock when the first mount point is reached, and holding onto it until the d_cache lookup is completed.
--- fs/dcache.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
fs/dcache.c +8 -6
@@ -1785,7 +1785,7 @@ static char *__d_path(struct dentry *den struct dentry *root, struct vfsmount *rootmnt, char *buffer, int buflen, int fail_deleted) { - int namelen, is_slash; + int namelen, is_slash, vfsmount_locked = 0;if (buflen <2 )return ERR_PTR (-ENAMETOOLONG );
@@ -1808,14 +1808,14 @@ static char *__d_path(struct dentry *denstruct dentry * parent;if (dentry == vfsmnt->mnt_root ||IS_ROOT (dentry)) { -spin_lock (&vfsmount_lock); -if (vfsmnt->mnt_parent == vfsmnt) { -spin_unlock (&vfsmount_lock); - goto global_root; +if (!vfsmount_locked) { +spin_lock (&vfsmount_lock); + vfsmount_locked =1 ; } +if (vfsmnt->mnt_parent == vfsmnt) + goto global_root; dentry = vfsmnt->mnt_mountpoint; vfsmnt = vfsmnt->mnt_parent; -spin_unlock (&vfsmount_lock);continue ; } parent = dentry->d_parent;
@@ -1834,6 +1834,8 @@ static char *__d_path(struct dentry *den *--buffer ='/' ; out: +if (vfsmount_locked) +spin_unlock (&vfsmount_lock);spin_unlock (&dcache_lock);return buffer;
PATCH 31/45 Add d_namespace_path() to compute namespace relative pathnames
In reply to [email protected]In AppArmor, we are interested in pathnames relative to the namespace root. This is the same as d_path() except for the root where the search ends. Add a function for computing the namespace-relative path.
--- fs/dcache.c | 6 +++--- fs/namespace.c | 27 +++++++++++++++++++++++++++ include/linux/dcache.h | 2 ++ include/linux/mount.h | 2 ++ 4 files changed, 34 insertions(+), 3 deletions(-)
fs/dcache.c +3 -3
@@ -1781,9 +1781,9 @@ shouldnt_be_hashed:* * Returns the buffer or an error code. */ -static char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, -struct dentry *root,struct vfsmount *rootmnt, -char *buffer,int buflen,int fail_deleted) +char *__d_path (struct dentry *dentry,struct vfsmount *vfsmnt, +struct dentry *root,struct vfsmount *rootmnt, +char *buffer,int buflen,int fail_deleted) {int namelen, is_slash, vfsmount_locked =0 ;
fs/namespace.c +27 -0
@@ -2321,3 +2321,30 @@ void __put_mnt_ns(struct mnt_namespace *release_mounts (&umount_list);kfree (ns); } + +char *d_namespace_path (struct dentry *dentry,struct vfsmount *vfsmnt, +char *buf,int buflen) +{ +struct vfsmount *rootmnt, *nsrootmnt =NULL ; +struct dentry *root =NULL ; +char *res; + +read_lock (¤t->fs->lock); + rootmnt =mntget (current->fs->rootmnt); +read_unlock (¤t->fs->lock); +spin_lock (&vfsmount_lock); +if (rootmnt->mnt_ns) + nsrootmnt =mntget (rootmnt->mnt_ns->root); +spin_unlock (&vfsmount_lock); +mntput (rootmnt); +if (nsrootmnt) + root =dget (nsrootmnt->mnt_root); + res =__d_path (dentry, vfsmnt, root, nsrootmnt, buf, buflen,1 ); +dput (root); +mntput (nsrootmnt); +/* Prevent empty path for lazily unmounted filesystems. */ +if (!IS_ERR (res) && *res =='\0' ) + *--res ='.' ; +return res; +} +EXPORT_SYMBOL (d_namespace_path);
include/linux/dcache.h +2 -0
@@ -300,6 +300,8 @@ extern int d_validate(struct dentry *, s*/ extern char *dynamic_dname (struct dentry *,char *,int ,const char *, ...); +extern char *__d_path (struct dentry *,struct vfsmount *,struct dentry *, +struct vfsmount *,char *,int ,int );extern char *d_path (struct dentry *,struct vfsmount *,char *,int );/* Allocation counts.. */
include/linux/mount.h +2 -0
@@ -119,5 +119,7 @@ extern void shrink_submounts(struct vfsmextern spinlock_t vfsmount_lock;extern dev_t name_to_dev_t (char *name); +extern char *d_namespace_path (struct dentry *,struct vfsmount *,char *,int ); +#endif #endif /* _LINUX_MOUNT_H */
PATCH 32/45 Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
In reply to [email protected]Struct iattr already contains ia_file since commit cc4e69de from Miklos (which is related to commit befc649c). Use this to pass struct file down the setattr hooks. This allows LSMs to distinguish operations on file descriptors from operations on paths.
--- fs/nfsd/vfs.c | 12 +++++++----- fs/open.c | 16 +++++++++++----- include/linux/fs.h | 3 +++ 3 files changed, 21 insertions(+), 10 deletions(-)
fs/nfsd/vfs.c +7 -5
@@ -413,7 +413,7 @@ static ssize_t nfsd_getxattr(struct dent {ssize_t buflen; - buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 ); + buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 ,NULL );if (buflen <=0 )return buflen;
@@ -421,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dentif (!*buf)return -ENOMEM ; - return vfs_getxattr(dentry, mnt, key, *buf, buflen); + return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL); }#endif
@@ -447,7 +447,7 @@ set_nfsv4_acl_one(struct dentry *dentry, goto out; } - error =vfs_setxattr (dentry, mnt, key, buf, len,0 ); + error =vfs_setxattr (dentry, mnt, key, buf, len,0 ,NULL ); out:kfree (buf);return error;
@@ -2062,12 +2062,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i mnt = fhp->fh_export->ex_mnt;if (size) - error =vfs_setxattr (fhp->fh_dentry, mnt, name, value, size,0 ); + error =vfs_setxattr (fhp->fh_dentry, mnt, name, value, size,0 , +NULL );else {if (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;else { - error =vfs_removexattr (fhp->fh_dentry, mnt, name); + error =vfs_removexattr (fhp->fh_dentry, mnt, name, +NULL );if (error == -ENODATA ) error =0 ; }
fs/open.c +11 -5
@@ -594,6 +594,8 @@ asmlinkage long sys_fchmod(unsigned int mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; + newattrs.ia_valid |=ATTR_FILE ; + newattrs.ia_file = file; err =notify_change (dentry, file->f_path.mnt, &newattrs);mutex_unlock (&inode->i_mutex);
@@ -648,7 +650,7 @@ asmlinkage long sys_chmod(const char __u }static int chown_common (struct dentry * dentry,struct vfsmount *mnt, -uid_t user,gid_t group) +uid_t user,gid_t group,struct file *file) {struct inode * inode;int error;
@@ -674,6 +676,10 @@ static int chown_common(struct dentry *if (!S_ISDIR (inode->i_mode)) newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID |ATTR_KILL_PRIV ; +if (file) { + newattrs.ia_file = file; + newattrs.ia_valid |=ATTR_FILE ; + }mutex_lock (&inode->i_mutex); error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex);
@@ -692,7 +698,7 @@ asmlinkage long sys_chown(const char __u error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -717,7 +723,7 @@ asmlinkage long sys_fchownat(int dfd, co error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -736,7 +742,7 @@ asmlinkage long sys_lchown(const char __ error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -760,7 +766,7 @@ asmlinkage long sys_fchown(unsigned int goto out_fput; dentry = file->f_path.dentry;audit_inode (NULL , dentry); - error =chown_common (dentry, file->f_path.mnt, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group, file);mnt_drop_write (file->f_vfsmnt); out_fput:fput (file);
include/linux/fs.h +3 -0
@@ -362,6 +362,9 @@ struct iattr {* Not an attribute, but an auxilary info for filesystems wanting to * implement an ftruncate() like method. NOTE: filesystem should * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). +* +* The LSM hooks also use this to distinguish operations on a file +* descriptors from operations on pathnames. */ struct file *ia_file; };
Re: [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
Quoted 5 lines · On Thu, 2007-10-25 at 23:40 -0700, [email protected] wrote:
plain text document attachment (file-handle-ops.diff) Struct iattr already contains
ia_filesince commitcc4e69defrom Miklos (which is related to commitbefc649c). Use this to pass struct file down the setattr hooks. This allows LSMs to distinguish operations on file descriptors from operations on paths.
There's a slight problem (other than HCH not liking it) with this approach of passing the open file in iattr: for special files, the struct file pointer makes no sense to the filesystem, since it is always opened by the generic functions.
This wasn't a problem with ftruncate(), because that one only works on regular files, but fchmod/fchown/futimes will work on special files as well, and the filesystem interpreting file->private_data could cause nasty bugs.
So I think the correct solution (which was suggested by Trond and others) is to define an f_op->fsetattr() method, which interested filesystems can define.
Miklos
Quoted 137 lines
Signed-off-by Andreas Gruenbacher <[email protected]>Signed-off-by John Johansen <[email protected]>Cc Miklos Szeredi <[email protected]>--- fs/nfsd/vfs.c | 12 +++++++----- fs/open.c | 16 +++++++++++----- include/linux/fs.h | 3 +++ 3 files changed, 21 insertions(+), 10 deletions(-)fs/nfsd/vfs.c +7
-5@@ -413,7 +413,7 @@ static ssize_t nfsd_getxattr(struct dent {ssize_t buflen; - buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 ); + buflen =vfs_getxattr (dentry, mnt, key,NULL ,0 ,NULL );if (buflen <=0 )return buflen;@@ -421,7 +421,7 @@ static ssize_t nfsd_getxattr(struct dentif (!*buf)return -ENOMEM ; - return vfs_getxattr(dentry, mnt, key, *buf, buflen); + return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL); }#endif @@ -447,7 +447,7 @@ set_nfsv4_acl_one(struct dentry *dentry, goto out; } - error =vfs_setxattr (dentry, mnt, key, buf, len,0 ); + error =vfs_setxattr (dentry, mnt, key, buf, len,0 ,NULL ); out:kfree (buf);return error;@@ -2062,12 +2062,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i mnt = fhp->fh_export->ex_mnt;if (size) - error =vfs_setxattr (fhp->fh_dentry, mnt, name, value, size,0 ); + error =vfs_setxattr (fhp->fh_dentry, mnt, name, value, size,0 , +NULL );else {if (!S_ISDIR (inode->i_mode) && type ==ACL_TYPE_DEFAULT ) error =0 ;else { - error =vfs_removexattr (fhp->fh_dentry, mnt, name); + error =vfs_removexattr (fhp->fh_dentry, mnt, name, +NULL );if (error == -ENODATA ) error =0 ; }fs/open.c +11
-5@@ -594,6 +594,8 @@ asmlinkage long sys_fchmod(unsigned int mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; + newattrs.ia_valid |=ATTR_FILE ; + newattrs.ia_file = file; err =notify_change (dentry, file->f_path.mnt, &newattrs);mutex_unlock (&inode->i_mutex);@@ -648,7 +650,7 @@ asmlinkage long sys_chmod(const char __u }static int chown_common (struct dentry * dentry,struct vfsmount *mnt, -uid_t user,gid_t group) +uid_t user,gid_t group,struct file *file) {struct inode * inode;int error;@@ -674,6 +676,10 @@ static int chown_common(struct dentry *if (!S_ISDIR (inode->i_mode)) newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID |ATTR_KILL_PRIV ; +if (file) { + newattrs.ia_file = file; + newattrs.ia_valid |=ATTR_FILE ; + }mutex_lock (&inode->i_mutex); error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex);@@ -692,7 +698,7 @@ asmlinkage long sys_chown(const char __u error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);@@ -717,7 +723,7 @@ asmlinkage long sys_fchownat(int dfd, co error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);@@ -736,7 +742,7 @@ asmlinkage long sys_lchown(const char __ error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);@@ -760,7 +766,7 @@ asmlinkage long sys_fchown(unsigned int goto out_fput; dentry = file->f_path.dentry;audit_inode (NULL , dentry); - error =chown_common (dentry, file->f_path.mnt, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group, file);mnt_drop_write (file->f_vfsmnt); out_fput:fput (file);include/linux/fs.h +3
-0@@ -362,6 +362,9 @@ struct iattr {* Not an attribute, but an auxilary info for filesystems wanting to * implement an ftruncate() like method. NOTE: filesystem should * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). +* +* The LSM hooks also use this to distinguish operations on a file +* descriptors from operations on pathnames. */ struct file *ia_file; };
Re: [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
Quoted 4 lines · Miklos Szeredi
So I think the correct solution (which was suggested by Trond and others) is to define an
f_op->fsetattr()method, which interested filesystems can define.
And here's the patch, which applies on top of the f_op->fgetattr() patch, recently posted on -fsdevel.
(First time I'm trying to post a patch from this account, hope evolution doesn't mess it up)
Miklos
---
Subject: VFS: new fsetattr() file operation
From: Miklos Szeredi <[email protected]>
Add a new file operation: f_op->fsetattr(), that is invoked by ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr() if it is not defined.
For the reasons why we need this, see patch adding fgetattr().
ftruncate() already passed the open file to the filesystem via the ia_file member of struct iattr. However it is cleaner to have a separate file operation for this, so remove ia_file, ATTR_FILE and convert existing users: fuse and AFS.
fs/afs/dir.c +1 -0
@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil .release = afs_release, .readdir = afs_readdir, .lock = afs_lock, + .fsetattr = afs_fsetattr, };const struct inode_operations afs_dir_inode_operations = {
fs/afs/file.c +1 -0
@@ -36,6 +36,7 @@ const struct file_operations afs_file_op .fsync = afs_fsync, .lock = afs_lock, .flock = afs_flock, + .fsetattr = afs_fsetattr, };const struct inode_operations afs_file_inode_operations = {
fs/afs/inode.c +15 -4
@@ -361,7 +361,8 @@ void afs_clear_inode(struct inode *inode/* * set the attributes of an inode */ -int afs_setattr (struct dentry *dentry,struct iattr *attr) +static int afs_do_setattr (struct dentry *dentry,struct iattr *attr, +struct file *file) {struct afs_vnode *vnode =AFS_FS_I (dentry->d_inode);struct key *key;
@@ -383,8 +384,8 @@ int afs_setattr(struct dentry *dentry, safs_writeback_all (vnode); } -if (attr->ia_valid &ATTR_FILE ) { - key = attr->ia_file->private_data; +if (file) { + key = file->private_data; }else { key =afs_request_key (vnode->volume->cell);if (IS_ERR (key)) {
@@ -394,10 +395,20 @@ int afs_setattr(struct dentry *dentry, s } ret =afs_vnode_setattr (vnode, key, attr); -if (!(attr->ia_valid &ATTR_FILE )) +if (!file)key_put (key); error:_leave (" = %d" , ret);return ret; } + +int afs_setattr (struct dentry *dentry,struct iattr *attr) +{ +return afs_do_setattr (dentry, attr,NULL ); +} + +int afs_fsetattr (struct file *file,struct iattr *attr) +{ +return afs_do_setattr (file->f_path.dentry, attr, file); +}
fs/afs/internal.h +1 -0
@@ -550,6 +550,7 @@ extern void afs_zap_data(struct afs_vnodextern int afs_validate (struct afs_vnode *,struct key *);extern int afs_getattr (struct vfsmount *,struct dentry *,struct kstat *);extern int afs_setattr (struct dentry *,struct iattr *); +extern int afs_fsetattr (struct file *,struct iattr *);extern void afs_clear_inode (struct inode *);/*
fs/attr.c +12 -3
@@ -100,7 +100,7 @@ int inode_setattr(struct inode * inode, }EXPORT_SYMBOL (inode_setattr); -int notify_change (struct dentry * dentry,struct iattr * attr) +int fnotify_change (struct dentry *dentry,struct iattr *attr,struct file *file) {struct inode *inode = dentry->d_inode;mode_t mode = inode->i_mode;
@@ -159,8 +159,12 @@ int notify_change(struct dentry * dentryif (inode->i_op && inode->i_op->setattr) { error =security_inode_setattr (dentry, attr); -if (!error) - error = inode->i_op->setattr (dentry, attr); +if (!error) { +if (file && file->f_op && file->f_op->fsetattr) + error = file->f_op->fsetattr (file, attr); +else + error = inode->i_op->setattr (dentry, attr); + } }else { error =inode_change_ok (inode, attr);if (!error)
@@ -183,4 +187,9 @@ int notify_change(struct dentry * dentryreturn error; } +int notify_change (struct dentry *dentry,struct iattr *attr) +{ +return fnotify_change (dentry, attr,NULL ); +} +EXPORT_SYMBOL (notify_change);
fs/open.c +9 -12
@@ -206,16 +206,12 @@ int do_truncate(struct dentry *dentry, l newattrs.ia_size = length; newattrs.ia_valid =ATTR_SIZE | time_attrs; -if (filp) { - newattrs.ia_file = filp; - newattrs.ia_valid |=ATTR_FILE ; - }/* Remove suid/sgid on truncate too */ newattrs.ia_valid |=should_remove_suid (dentry);mutex_lock (&dentry->d_inode->i_mutex); - err =notify_change (dentry, &newattrs); + err =fnotify_change (dentry, &newattrs, filp);mutex_unlock (&dentry->d_inode->i_mutex);return err; }
@@ -593,7 +589,7 @@ asmlinkage long sys_fchmod(unsigned int mode = inode->i_mode; newattrs.ia_mode = (mode &S_IALLUGO ) | (inode->i_mode & ~S_IALLUGO ); newattrs.ia_valid =ATTR_MODE |ATTR_CTIME ; - err =notify_change (dentry, &newattrs); + err =fnotify_change (dentry, &newattrs, file);mutex_unlock (&inode->i_mutex); out_drop_write:
@@ -646,7 +642,8 @@ asmlinkage long sys_chmod(const char __ureturn sys_fchmodat (AT_FDCWD , filename, mode); } -static int chown_common (struct dentry * dentry,uid_t user,gid_t group) +static int chown_common (struct dentry * dentry,uid_t user,gid_t group, +struct file *file) {struct inode * inode;int error;
@@ -673,7 +670,7 @@ static int chown_common(struct dentry * newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID |ATTR_KILL_PRIV ;mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =fnotify_change (dentry, &newattrs, file);mutex_unlock (&inode->i_mutex); out:return error;
@@ -690,7 +687,7 @@ asmlinkage long sys_chown(const char __u error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -715,7 +712,7 @@ asmlinkage long sys_fchownat(int dfd, co error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -734,7 +731,7 @@ asmlinkage long sys_lchown(const char __ error =mnt_want_write (nd.mnt);if (error) goto out_release; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, user, group,NULL );mnt_drop_write (nd.mnt); out_release:path_release (&nd);
@@ -758,7 +755,7 @@ asmlinkage long sys_fchown(unsigned int goto out_fput; dentry = file->f_path.dentry;audit_inode (NULL , dentry); - error =chown_common (dentry, user, group); + error =chown_common (dentry, user, group, file);mnt_drop_write (file->f_vfsmnt); out_fput:fput (file);
include/linux/fs.h +2 -8
@@ -335,7 +335,6 @@ typedef void (dio_iodone_t)(struct kiocb#define ATTR_ATTR_FLAG 1024#define ATTR_KILL_SUID 2048#define ATTR_KILL_SGID 4096 -#define ATTR_FILE 8192#define ATTR_KILL_PRIV 16384#define ATTR_OPEN 32768/* Truncating from open(O_TRUNC) */
@@ -357,13 +356,6 @@ struct iattr {struct timespec ia_atime;struct timespec ia_mtime;struct timespec ia_ctime; - -/* -* Not an attribute, but an auxilary info for filesystems wanting to -* implement an ftruncate() like method. NOTE: filesystem should -* check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). -*/ -struct file *ia_file; };/*
@@ -1196,6 +1188,7 @@ struct file_operations {int (*setlease)(struct file *,long ,struct file_lock **);int (*revoke)(struct file *,struct address_space *);int (*fgetattr)(struct file *,struct kstat *); +int (*fsetattr)(struct file *,struct iattr *); };struct inode_operations {
@@ -1703,6 +1696,7 @@ extern int do_remount_sb(struct super_blextern sector_t bmap (struct inode *,sector_t );#endif extern int notify_change (struct dentry *,struct iattr *); +extern int fnotify_change (struct dentry *,struct iattr *,struct file *);extern int permission (struct inode *,int ,struct nameidata *);extern int generic_permission (struct inode *,int ,int (*check_acl)(struct inode *,int ));
fs/utimes.c +1 -1
@@ -135,7 +135,7 @@ long do_utimes(int dfd, char __user *fil } }mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =fnotify_change (dentry, &newattrs, f);mutex_unlock (&inode->i_mutex); mnt_drop_write_and_out:mnt_drop_write (mnt);
fs/fuse/dir.c +9 -11
@@ -1060,21 +1060,22 @@ static int fuse_dir_fsync(struct file *freturn file ?fuse_fsync_common (file, de, datasync,1 ) :0 ; } -static bool update_mtime (unsigned ivalid) +static bool update_mtime (unsigned ivalid,bool have_file) {/* Always update if mtime is explicitly set */ if (ivalid &ATTR_MTIME_SET )return true;/* If it's an open(O_TRUNC) or an ftruncate(), don't update */ -if ((ivalid &ATTR_SIZE ) && (ivalid & (ATTR_OPEN |ATTR_FILE ))) +if ((ivalid &ATTR_SIZE ) && ((ivalid &ATTR_OPEN ) || have_file))return false;/* In all other cases update */ return true; } -static void iattr_to_fattr (struct iattr *iattr,struct fuse_setattr_in *arg) +static void iattr_to_fattr (struct iattr *iattr,struct fuse_setattr_in *arg, +bool have_file) {unsigned ivalid = iattr->ia_valid;
@@ -1093,7 +1094,7 @@ static void iattr_to_fattr(struct iattrif (!(ivalid &ATTR_ATIME_SET )) arg->valid |=FATTR_ATIME_NOW ; } -if ((ivalid &ATTR_MTIME ) &&update_mtime (ivalid)) { +if ((ivalid &ATTR_MTIME ) &&update_mtime (ivalid, have_file)) { arg->valid |=FATTR_MTIME ; arg->mtime = iattr->ia_mtime.tv_sec; arg->mtimensec = iattr->ia_mtime.tv_nsec;
@@ -1110,8 +1111,8 @@ static void iattr_to_fattr(struct iattr* vmtruncate() doesn't allow for this case, so do the rlimit checking * and the actual truncation by hand. */ -static int fuse_do_setattr (struct dentry *entry,struct iattr *attr, -struct file *file) +int fuse_do_setattr (struct dentry *entry,struct iattr *attr, +struct file *file) {struct inode *inode = entry->d_inode;struct fuse_conn *fc =get_fuse_conn (inode);
@@ -1149,7 +1150,7 @@ static int fuse_do_setattr(struct dentrymemset (&inarg,0 ,sizeof (inarg));memset (&outarg,0 ,sizeof (outarg)); -iattr_to_fattr (attr, &inarg); +iattr_to_fattr (attr, &inarg, file !=NULL );if (file) {struct fuse_file *ff = file->private_data; inarg.valid |=FATTR_FH ;
@@ -1191,10 +1192,7 @@ static int fuse_do_setattr(struct dentrystatic int fuse_setattr (struct dentry *entry,struct iattr *attr) { -if (attr->ia_valid &ATTR_FILE ) -return fuse_do_setattr (entry, attr, attr->ia_file); -else -return fuse_do_setattr (entry, attr,NULL ); +return fuse_do_setattr (entry, attr,NULL ); } static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
fs/fuse/file.c +7 -0
@@ -918,6 +918,11 @@ static sector_t fuse_bmap(struct addressreturn err ?0 : outarg.block; } +static int fuse_fsetattr (struct file *file,struct iattr *attr) +{ +return fuse_do_setattr (file->f_path.dentry, attr, file); +} +static const struct file_operations fuse_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read,
@@ -932,6 +937,7 @@ static const struct file_operations fuse .lock = fuse_file_lock, .flock = fuse_file_flock, .fgetattr = fuse_file_fgetattr, + .fsetattr = fuse_fsetattr, .splice_read = generic_file_splice_read, };
@@ -946,6 +952,7 @@ static const struct file_operations fuse .lock = fuse_file_lock, .flock = fuse_file_flock, .fgetattr = fuse_file_fgetattr, + .fsetattr = fuse_fsetattr,/* no mmap and splice_read */ };
fs/fuse/fuse_i.h +4 -0
@@ -505,6 +505,10 @@ void fuse_change_attributes(struct inode*/ int fuse_dev_init (void ); + +int fuse_do_setattr (struct dentry *entry,struct iattr *attr, +struct file *file); +/** * Cleanup the client device */
Re: [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
In reply to Miklos SzerediQuoted 7 lines · Miklos Szeredi
Quoted 5 lines · On Thu, 2007-10-25 at 23:40 -0700, [email protected] wrote:
plain text document attachment (file-handle-ops.diff) Struct iattr already contains
ia_filesince commitcc4e69defrom Miklos (which is related to commitbefc649c). Use this to pass struct file down the setattr hooks. This allows LSMs to distinguish operations on file descriptors from operations on paths.
Quoted 5 lines
There's a slight problem (other than HCH not liking it) with this approach of passing the open file in iattr: for special files, the struct file pointer makes no sense to the filesystem, since it is always opened by the generic functions.
true
Quoted 9 lines
This wasn't a problem with
ftruncate(), because that one only works on regular files, but fchmod/fchown/futimes will work on special files as well, and the filesystem interpretingfile->private_datacould cause nasty bugs.So I think the correct solution (which was suggested by Trond and others) is to define an
f_op->fsetattr()method, which interested filesystems can define.
yeah that does sound like the way to go, thank Miklos
regards
john
Re: [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
In reply to Miklos SzerediQuoted 4 lines · Miklos Szeredi
There's a slight problem (other than HCH not liking it) with this approach of passing the open file in iattr: for special files, the struct file pointer makes no sense to the filesystem, since it is always opened by the generic functions.
So what do you think where the inodes come from for syscalls like fchmod? Out of struct file, of course. But your f_op->getattr and f_op->setattr patches are meant for passing struct file down to filesystems anyway, so that completely contradicts what you are saying above.
Quoted 3 lines
So I think the correct solution (which was suggested by Trond and others) is to define an
f_op->fsetattr()method, which interested filesystems can define.
That's nothing but a replacement for ATTR_FILE and iattr->ia_file. Except by removing the ATTR_FILE flag, LSMs will no longer get that information for distinguishing file descriptor operations from other operations.
AppArmor needs to know when notify_change is called on a file descriptor, but it doesn't care about the file descriptor itself. So any way of passing along that information will be fine.
Thanks,
Andreas
Re: [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
Quoted 10 lines · Andreas Gruenbacher
Quoted 4 lines · Miklos Szeredi
There's a slight problem (other than HCH not liking it) with this approach of passing the open file in iattr: for special files, the struct file pointer makes no sense to the filesystem, since it is always opened by the generic functions.
So what do you think where the inodes come from for syscalls like fchmod? Out of struct file, of course. But your
f_op->getattrandf_op->setattrpatches are meant for passing struct file down to filesystems anyway, so that completely contradicts what you are saying above.
For special files, f_op->fsetattr will be NULL, since init_special_inode() will set up i_fop that way.
So the filesystem's fsetattr() will only be called for regular files and/or directories, depending on how it sets up i_fop.
With the ia_file thing, the filesystem would receive the struct file pointer in all cases. So the posted patch would break AFS and FUSE, because they unconditionally use ia_file if available and don't check the file type. They could check the file type, but still the interface would be ugly.
Quoted 11 lines
Quoted 3 lines
So I think the correct solution (which was suggested by Trond and others) is to define an
f_op->fsetattr()method, which interested filesystems can define.That's nothing but a replacement for
ATTR_FILEandiattr->ia_file. Except by removing theATTR_FILEflag, LSMs will no longer get that information for distinguishing file descriptor operations from other operations.AppArmor needs to know when
notify_changeis called on a file descriptor, but it doesn't care about the file descriptor itself. So any way of passing along that information will be fine.
Ah, so if we kept ATTR_FILE and got rid of iattr_file, would that be OK for AppArmor?
Thanks,
Miklos
Re: [AppArmor 32/45] Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
Quoted 10 lines · On Friday 26 October 2007 22:58:11 Miklos Szeredi wrote:
For special files,
f_op->fsetattrwill be NULL, sinceinit_special_inode()will set upi_fopthat way.So the filesystem's
fsetattr()will only be called for regular files and/or directories, depending on how it sets upi_fop.With the
ia_filething, the filesystem would receive the struct file pointer in all cases. So the posted patch would break AFS and FUSE, because they unconditionally useia_fileif available and don't check the file type.
Ah okay, that's what you mean. That's much cleaner than ia_file indeed.
Quoted 1 line
They could check the file type, but still the interface would be ugly.
Agreed, that would be ugly.
Quoted 2 lines
Ah, so if we kept
ATTR_FILEand got rid ofiattr_file, would that be OK for AppArmor?
No problem, yes.
Thanks,
Andreas
PATCH 33/45 Pass struct file down the inode_*xattr security LSM hooks
In reply to [email protected]This allows LSMs to also distinguish between file descriptor and path access for the xattr operations. (The other relevant operations are covered by the setattr hook.)
--- fs/xattr.c | 59 ++++++++++++++++++++++++----------------------- include/linux/security.h | 40 +++++++++++++++++++------------ include/linux/xattr.h | 8 +++--- security/commoncap.c | 4 +-- security/dummy.c | 10 ++++--- security/security.c | 18 +++++++------- security/selinux/hooks.c | 10 ++++--- 7 files changed, 83 insertions(+), 66 deletions(-)
fs/xattr.c +31 -28
@@ -68,7 +68,7 @@ xattr_permission(struct inode *inode, coint vfs_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size,int flags) +void *value,size_t size,int flags,struct file *file) {struct inode *inode = dentry->d_inode;int error;
@@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, strureturn error;mutex_lock (&inode->i_mutex); - error =security_inode_setxattr (dentry, mnt, name, value, size, flags); + error =security_inode_setxattr (dentry, mnt, name, value, size, flags, file);if (error) goto out; error = -EOPNOTSUPP ;
@@ -105,7 +105,7 @@ EXPORT_SYMBOL_GPL(vfs_setxattr);ssize_t vfs_getxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size) +void *value,size_t size,struct file *file) {struct inode *inode = dentry->d_inode;int error;
@@ -114,7 +114,7 @@ vfs_getxattr(struct dentry *dentry, struif (error)return error; - error =security_inode_getxattr (dentry, mnt, name); + error =security_inode_getxattr (dentry, mnt, name, file);if (error)return error;
@@ -142,12 +142,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr);ssize_t vfs_listxattr (struct dentry *dentry,struct vfsmount *mnt,char *list, -size_t size) +size_t size,struct file *file) {struct inode *inode = dentry->d_inode;ssize_t error; - error =security_inode_listxattr (dentry, mnt); + error =security_inode_listxattr (dentry, mnt, file);if (error)return error; error = -EOPNOTSUPP ;
@@ -163,7 +163,8 @@ vfs_listxattr(struct dentry *dentry, strEXPORT_SYMBOL_GPL (vfs_listxattr);int -vfs_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name) +vfs_removexattr (struct dentry *dentry,struct vfsmount *mnt,char *name, +struct file *file) {struct inode *inode = dentry->d_inode;int error;
@@ -175,7 +176,7 @@ vfs_removexattr(struct dentry *dentry, sif (error)return error; - error =security_inode_removexattr (dentry, mnt, name); + error =security_inode_removexattr (dentry, mnt, name, file);if (error)return error;
@@ -195,7 +196,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);*/ static long setxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, -void __user *value,size_t size,int flags) +void __user *value,size_t size,int flags,struct file *file) {int error;void *kvalue =NULL ;
@@ -222,7 +223,7 @@ setxattr(struct dentry *dentry, struct v } } - error =vfs_setxattr (dentry, mnt, kname, kvalue, size, flags); + error =vfs_setxattr (dentry, mnt, kname, kvalue, size, flags, file);kfree (kvalue);return error; }
@@ -240,7 +241,7 @@ sys_setxattr(char __user *path, char __u error =mnt_want_write (nd.mnt);if (error)return error; - error =setxattr (nd.dentry, nd.mnt, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags,NULL );mnt_drop_write (nd.mnt);path_release (&nd);return error;
@@ -259,7 +260,7 @@ sys_lsetxattr(char __user *path, char __ error =mnt_want_write (nd.mnt);if (error)return error; - error =setxattr (nd.dentry, nd.mnt, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags,NULL );mnt_drop_write (nd.mnt);path_release (&nd);return error;
@@ -281,7 +282,7 @@ sys_fsetxattr(int fd, char __user *name, goto out_fput; dentry = f->f_path.dentry;audit_inode (NULL , dentry); - error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags); + error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags,NULL );mnt_drop_write (f->f_vfsmnt); out_fput:fput (f);
@@ -293,7 +294,7 @@ out_fput:*/ static ssize_t getxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, -void __user *value,size_t size) +void __user *value,size_t size,struct file *file) {ssize_t error;void *kvalue =NULL ;
@@ -313,7 +314,7 @@ getxattr(struct dentry *dentry, struct vreturn -ENOMEM ; } - error =vfs_getxattr (dentry, mnt, kname, kvalue, size); + error =vfs_getxattr (dentry, mnt, kname, kvalue, size, file);if (error >0 ) {if (size &©_to_user (value, kvalue, error)) error = -EFAULT ;
@@ -336,7 +337,7 @@ sys_getxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =getxattr (nd.dentry, nd.mnt, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size,NULL );path_release (&nd);return error; }
@@ -351,7 +352,7 @@ sys_lgetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =getxattr (nd.dentry, nd.mnt, name, value, size); + error =getxattr (nd.dentry, nd.mnt, name, value, size,NULL );path_release (&nd);return error; }
@@ -366,7 +367,8 @@ sys_fgetxattr(int fd, char __user *name,if (!f)return error;audit_inode (NULL , f->f_path.dentry); - error =getxattr (f->f_path.dentry, f->f_path.mnt, name, value, size); + error =getxattr (f->f_path.dentry, f->f_path.mnt, name, value, size, +NULL );fput (f);return error; }
@@ -376,7 +378,7 @@ sys_fgetxattr(int fd, char __user *name,*/ static ssize_t listxattr (struct dentry *dentry,struct vfsmount *mnt,char __user *list, -size_t size) +size_t size,struct file *file) {ssize_t error;char *klist =NULL ;
@@ -389,7 +391,7 @@ listxattr(struct dentry *dentry, structreturn -ENOMEM ; } - error =vfs_listxattr (dentry, mnt, klist, size); + error =vfs_listxattr (dentry, mnt, klist, size, file);if (error >0 ) {if (size &©_to_user (list, klist, error)) error = -EFAULT ;
@@ -411,7 +413,7 @@ sys_listxattr(char __user *path, char __ error =user_path_walk (path, &nd);if (error)return error; - error =listxattr (nd.dentry, nd.mnt, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size,NULL );path_release (&nd);return error; }
@@ -425,7 +427,7 @@ sys_llistxattr(char __user *path, char _ error =user_path_walk_link (path, &nd);if (error)return error; - error =listxattr (nd.dentry, nd.mnt, list, size); + error =listxattr (nd.dentry, nd.mnt, list, size,NULL );path_release (&nd);return error; }
@@ -440,7 +442,7 @@ sys_flistxattr(int fd, char __user *listif (!f)return error;audit_inode (NULL , f->f_path.dentry); - error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size); + error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size,NULL );fput (f);return error; }
@@ -449,7 +451,8 @@ sys_flistxattr(int fd, char __user *list* Extended attribute REMOVE operations */ static long -removexattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name) +removexattr (struct dentry *dentry,struct vfsmount *mnt,char __user *name, +struct file *file) {int error;char kname[XATTR_NAME_MAX +1 ];
@@ -460,7 +463,7 @@ removexattr(struct dentry *dentry, strucif (error <0 )return error; -return vfs_removexattr (dentry, mnt, kname); +return vfs_removexattr (dentry, mnt, kname, file); }asmlinkage long
@@ -472,7 +475,7 @@ sys_removexattr(char __user *path, char error =user_path_walk (path, &nd);if (error)return error; - error =removexattr (nd.dentry, nd.mnt, name); + error =removexattr (nd.dentry, nd.mnt, name,NULL );path_release (&nd);return error; }
@@ -486,7 +489,7 @@ sys_lremovexattr(char __user *path, char error =user_path_walk_link (path, &nd);if (error)return error; - error =removexattr (nd.dentry, nd.mnt, name); + error =removexattr (nd.dentry, nd.mnt, name,NULL );path_release (&nd);return error; }
@@ -503,7 +506,7 @@ sys_fremovexattr(int fd, char __user *nareturn error; dentry = f->f_path.dentry;audit_inode (NULL , dentry); - error =removexattr (dentry, f->f_path.mnt, name); + error =removexattr (dentry, f->f_path.mnt, name,NULL );fput (f);return error; }
include/linux/security.h +24 -16
@@ -56,9 +56,9 @@ extern void cap_capset_set (struct task_extern int cap_bprm_set_security (struct linux_binprm *bprm);extern void cap_bprm_apply_creds (struct linux_binprm *bprm,int unsafe);extern int cap_bprm_secureexec (struct linux_binprm *bprm); -extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags); +extern int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags,struct file *file);extern int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name); +char *name,struct file *file);extern int cap_inode_need_killpriv (struct dentry *dentry);extern int cap_inode_killpriv (struct dentry *dentry);extern int cap_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);
@@ -1274,16 +1274,18 @@ struct security_operations {int (*inode_getattr) (struct vfsmount *mnt,struct dentry *dentry);void (*inode_delete) (struct inode *inode);int (*inode_setxattr) (struct dentry *dentry,struct vfsmount *mnt, -char *name,void *value,size_t size,int flags); +char *name,void *value,size_t size,int flags, +struct file *file);void (*inode_post_setxattr) (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags);int (*inode_getxattr) (struct dentry *dentry,struct vfsmount *mnt, -char *name); -int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt); +char *name,struct file *file); +int (*inode_listxattr) (struct dentry *dentry,struct vfsmount *mnt, +struct file *file);int (*inode_removexattr) (struct dentry *dentry,struct vfsmount *mnt, -char *name); +char *name,struct file *file);int (*inode_need_killpriv) (struct dentry *dentry);int (*inode_killpriv) (struct dentry *dentry);int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);
@@ -1540,15 +1542,17 @@ int security_inode_setattr(struct dentryint security_inode_getattr (struct vfsmount *mnt,struct dentry *dentry);void security_inode_delete (struct inode *inode);int security_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, -char *name,void *value,size_t size,int flags); +char *name,void *value,size_t size,int flags, +struct file *file);void security_inode_post_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size,int flags);int security_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, -char *name); -int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt); +char *name,struct file *file); +int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file);int security_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name); +char *name,struct file *file);int security_inode_need_killpriv (struct dentry *dentry);int security_inode_killpriv (struct dentry *dentry);int security_inode_getsecurity (const struct inode *inode,const char *name,void *buffer,size_t size,int err);
@@ -1934,9 +1938,10 @@ static inline void security_inode_deletestatic inline int security_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size,int flags) +void *value,size_t size,int flags, +struct file *file) { -return cap_inode_setxattr (dentry, mnt, name, value, size, flags); +return cap_inode_setxattr (dentry, mnt, name, value, size, flags, file); } static inline void security_inode_post_setxattr (struct dentry *dentry,
@@ -1947,21 +1952,24 @@ static inline void security_inode_post_s { }static inline int security_inode_getxattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) {return 0 ; }static inline int security_inode_listxattr (struct dentry *dentry, -struct vfsmount *mnt) +struct vfsmount *mnt, +struct file *file) {return 0 ; }static inline int security_inode_removexattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) { -return cap_inode_removexattr (dentry, mnt, name); +return cap_inode_removexattr (dentry, mnt, name, file); }static inline int security_inode_need_killpriv (struct dentry *dentry)
include/linux/xattr.h +4 -4
@@ -47,12 +47,12 @@ struct xattr_handler { };ssize_t vfs_getxattr (struct dentry *,struct vfsmount *,char *,void *, -size_t ); +size_t ,struct file *);ssize_t vfs_listxattr (struct dentry *d,struct vfsmount *,char *list, -size_t size); +size_t size,struct file *);int vfs_setxattr (struct dentry *,struct vfsmount *,char *,void *,size_t , -int ); -int vfs_removexattr (struct dentry *,struct vfsmount *,char *); +int ,struct file *); +int vfs_removexattr (struct dentry *,struct vfsmount *,char *,struct file *);ssize_t generic_getxattr (struct dentry *dentry,const char *name,void *buffer,size_t size);ssize_t generic_listxattr (struct dentry *dentry,char *buffer,size_t buffer_size);
security/commoncap.c +2 -2
@@ -362,7 +362,7 @@ int cap_bprm_secureexec (struct linux_bi }int cap_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name, -void *value,size_t size,int flags) +void *value,size_t size,int flags,struct file *file) {if (!strcmp (name,XATTR_NAME_CAPS )) {if (!capable (CAP_SETFCAP ))
@@ -376,7 +376,7 @@ int cap_inode_setxattr(struct dentry *de }int cap_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) {if (!strcmp (name,XATTR_NAME_CAPS )) {if (!capable (CAP_SETFCAP ))
security/dummy.c +6 -4
@@ -349,7 +349,7 @@ static void dummy_inode_delete (struct istatic int dummy_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size, -int flags) +int flags,struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -366,18 +366,20 @@ static void dummy_inode_post_setxattr (s }static int dummy_inode_getxattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) {return 0 ; } -static int dummy_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) +static int dummy_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file) {return 0 ; }static int dummy_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) { if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/security.c +10 -8
@@ -442,12 +442,13 @@ void security_inode_delete(struct inode }int security_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, -char *name,void *value,size_t size,int flags) +char *name,void *value,size_t size,int flags, +struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ;return security_ops->inode_setxattr (dentry, mnt, name, value, size, - flags); + flags, file); } void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
@@ -461,26 +462,27 @@ void security_inode_post_setxattr(struct }int security_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_getxattr (dentry, mnt, name); +return security_ops->inode_getxattr (dentry, mnt, name, file); } -int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) +int security_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_listxattr (dentry, mnt); +return security_ops->inode_listxattr (dentry, mnt, file); }int security_inode_removexattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_removexattr (dentry, mnt, name); +return security_ops->inode_removexattr (dentry, mnt, name, file); }int security_inode_need_killpriv (struct dentry *dentry)
security/selinux/hooks.c +6 -4
@@ -2334,7 +2334,7 @@ static int selinux_inode_setotherxattr(sstatic int selinux_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt,char *name,void *value,size_t size, -int flags) +int flags,struct file *file) {struct task_security_struct *tsec = current->security;struct inode *inode = dentry->d_inode;
@@ -2410,18 +2410,20 @@ static void selinux_inode_post_setxattr( }static int selinux_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, -char *name) +char *name,struct file *file) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); } -static int selinux_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt) +static int selinux_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file) {return dentry_has_perm (current,NULL , dentry,FILE__GETATTR ); }static int selinux_inode_removexattr (struct dentry *dentry, -struct vfsmount *mnt,char *name) +struct vfsmount *mnt,char *name, +struct file *file) {if (strcmp (name,XATTR_NAME_SELINUX ))return selinux_inode_setotherxattr (dentry, name);
PATCH 34/45 Factor out sysctl pathname code
In reply to [email protected]Convert the selinux sysctl pathname computation code into a standalone function.
--- include/linux/sysctl.h | 2 ++ kernel/sysctl.c | 27 +++++++++++++++++++++++++++ security/selinux/hooks.c | 35 +++++------------------------------ 3 files changed, 34 insertions(+), 30 deletions(-)
include/linux/sysctl.h +2 -0
@@ -983,6 +983,8 @@ extern int proc_doulongvec_minmax(structextern int proc_doulongvec_ms_jiffies_minmax (struct ctl_table *table,int ,struct file *,void __user *,size_t *,loff_t *); +extern char *sysctl_pathname (ctl_table *,char *,int ); +extern int do_sysctl (int __user *name,int nlen,void __user *oldval,size_t __user *oldlenp,void __user *newval,size_t newlen);
kernel/sysctl.c +27 -0
@@ -1333,6 +1333,33 @@ struct ctl_table_header *sysctl_head_nexreturn NULL ; } +char *sysctl_pathname (ctl_table *table,char *buffer,int buflen) +{ +if (buflen <1 ) +return NULL ; + buffer += --buflen; + *buffer ='\0' ; + +while (table) { +int namelen =strlen (table->procname); + +if (buflen < namelen +1 ) +return NULL ; + buflen -= namelen +1 ; + buffer -= namelen; +memcpy (buffer, table->procname, namelen); + *--buffer ='/' ; + table = table->parent; + } +if (buflen <4 ) +return NULL ; + buffer -=4 ; +memcpy (buffer,"/sys" ,4 ); + +return buffer; +} +EXPORT_SYMBOL (sysctl_pathname); +#ifdef CONFIG_SYSCTL_SYSCALL int do_sysctl (int __user *name,int nlen,void __user *oldval,size_t __user *oldlenp,void __user *newval,size_t newlen)
security/selinux/hooks.c +5 -30
@@ -1431,40 +1431,15 @@ static int selinux_capable(struct task_sstatic int selinux_sysctl_get_sid (ctl_table *table,u16 tclass,u32 *sid) { -int buflen, rc; -char *buffer, *path, *end; +char *buffer, *path; +int rc = -ENOMEM ; - rc = -ENOMEM ; buffer = (char *)__get_free_page (GFP_KERNEL );if (!buffer) goto out; - - buflen =PAGE_SIZE ; - end = buffer+buflen; - *--end ='\0' ; - buflen--; - path = end-1 ; - *path ='/' ; -while (table) { -const char *name = table->procname; -size_t namelen =strlen (name); - buflen -= namelen +1 ; -if (buflen <0 ) - goto out_free; - end -= namelen; -memcpy (end, name, namelen); - *--end ='/' ; - path = end; - table = table->parent; - } - buflen -=4 ; -if (buflen <0 ) - goto out_free; - end -=4 ; -memcpy (end,"/sys" ,4 ); - path = end; - rc =security_genfs_sid ("proc" , path, tclass, sid); -out_free: + path =sysctl_pathname (table, buffer,PAGE_SIZE ); +if (path) + rc =security_genfs_sid ("proc" , path, tclass, sid);free_page ((unsigned long )buffer); out:return rc;
Re: [AppArmor 34/45] Factor out sysctl pathname code
Quoted 5 lines · On Thu, 25 Oct 2007, [email protected] wrote:
Convert the selinux sysctl pathname computation code into a standalone function.
Signed-off-by Andreas Gruenbacher <[email protected]>Signed-off-by John Johansen <[email protected]>
Signature · 2 lines
James Morris <[email protected]>
PATCH 35/45 Allow permission functions to tell between parent and leaf checks
In reply to [email protected]Set the LOOKUP_CONTINUE flag when checking parent permissions. This allows permission functions to tell between parent and leaf checks.
--- fs/namei.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/namei.c +6 -0
@@ -1472,6 +1472,10 @@ static int may_delete(struct inode *dir,BUG_ON (victim->d_parent->d_inode != dir);audit_inode_child (victim->d_name.name, victim, dir); +#if 0 +if (nd) + nd->flags |=LOOKUP_CONTINUE ; +#endif error =permission (dir,MAY_WRITE |MAY_EXEC ,NULL );if (error)return error;
@@ -1509,6 +1513,8 @@ static inline int may_create(struct inodreturn -EEXIST ;if (IS_DEADDIR (dir))return -ENOENT ; +if (nd) + nd->flags |=LOOKUP_CONTINUE ;return permission (dir,MAY_WRITE |MAY_EXEC , nd); }
Re: [AppArmor 35/45] Allow permission functions to tell between parent and leaf checks
Quoted 14 lines · On Thu, 2007-10-25 at 23:40 -0700, [email protected] wrote:
plain text document attachment (parent-permission.diff)
Set theLOOKUP_CONTINUEflag when checking parent permissions. This allows permission functions to tell between parent and leaf checks.Signed-off-by Andreas Gruenbacher <[email protected]>Signed-off-by John Johansen <[email protected]>--- fs/namei.c | 6 ++++++ 1 file changed, 6 insertions(+)
fs/namei.c static int may_delete(struct inode *dir, · line 1478
Quoted 7 lines
fs/namei.c +4
-0@@ -1472,6 +1472,10 @@ static int may_delete(struct inode *dir,BUG_ON (victim->d_parent->d_inode != dir);audit_inode_child (victim->d_name.name, victim, dir); +#if 0 +if (nd) + nd->flags |=LOOKUP_CONTINUE ; +#endif
#if 0?
Quoted 13 lines
fs/namei.c +2
-0error =permission (dir,MAY_WRITE |MAY_EXEC ,NULL );if (error)return error;@@ -1509,6 +1513,8 @@ static inline int may_create(struct inodreturn -EEXIST ;if (IS_DEADDIR (dir))return -ENOENT ; +if (nd) + nd->flags |=LOOKUP_CONTINUE ;return permission (dir,MAY_WRITE |MAY_EXEC , nd); }
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 35/45] Allow permission functions to tell between parent and leaf checks
Quoted 18 lines · Stephen Smalley
Quoted 17 lines · On Thu, 2007-10-25 at 23:40 -0700, [email protected] wrote:
plain text document attachment (parent-permission.diff)
Set theLOOKUP_CONTINUEflag when checking parent permissions. This allows permission functions to tell between parent and leaf checks.Signed-off-by Andreas Gruenbacher <[email protected]>Signed-off-by John Johansen <[email protected]>--- fs/namei.c | 6 ++++++ 1 file changed, 6 insertions(+)fs/namei.c +0
-0@@ -1472,6 +1472,10 @@ static int may_delete(struct inode *dir,BUG_ON (victim->d_parent->d_inode != dir);audit_inode_child (victim->d_name.name, victim, dir);
Quoted 7 lines
fs/namei.c static int may_delete(struct inode *dir, · line 1478
Quoted 4 lines
fs/namei.c +4
-0+#if 0 +if (nd) + nd->flags |=LOOKUP_CONTINUE ; +#endif #if 0?
Oops, thanks I missed that one.
regards
john
PATCH 36/45 Export audit subsystem for use by modules
In reply to [email protected]Update kenel audit range comments to show AppArmor's registered range of 1500-1599. This range used to be reserved for LSPP but LSPP uses the SE Linux range and the range was given to AppArmor.
Adds necessary export symbols for audit subsystem routines. Changes audit_log_vformat to be externally visible (analagous to vprintf) Patch is not in mainline -- pending AppArmor code submission to lkml
--- include/linux/audit.h | 12 +++++++++++- kernel/audit.c | 6 ++++-- 2 files changed, 15 insertions(+), 3 deletions(-)
include/linux/audit.h +11 -1
@@ -33,7 +33,7 @@* 1200 - 1299 messages internal to the audit daemon * 1300 - 1399 audit event messages * 1400 - 1499 SE Linux use -* 1500 - 1599 kernel LSPP events +* 1500 - 1599 AppArmor use * 1600 - 1699 kernel crypto events * 1700 - 1799 kernel anomaly records * 1800 - 1999 future kernel use (maybe integrity labels and related events)
@@ -116,6 +116,13 @@#define AUDIT_MAC_IPSEC_DELSPD 1414/* Not used */ #define AUDIT_MAC_IPSEC_EVENT 1415/* Audit an IPSec event */ +#define AUDIT_APPARMOR_AUDIT 1501/* AppArmor audited grants */ +#define AUDIT_APPARMOR_ALLOWED 1502/* Allowed Access for learning */ +#define AUDIT_APPARMOR_DENIED 1503 +#define AUDIT_APPARMOR_HINT 1504/* Process Tracking information */ +#define AUDIT_APPARMOR_STATUS 1505/* Changes in config */ +#define AUDIT_APPARMOR_ERROR 1506/* Internal AppArmor Errors */ +#define AUDIT_FIRST_KERN_ANOM_MSG 1700#define AUDIT_LAST_KERN_ANOM_MSG 1799#define AUDIT_ANOM_PROMISCUOUS 1700/* Device changed promiscuous mode */
@@ -513,6 +520,9 @@ extern void audit_log(struct audit_ __attribute__((format(printf,4,5)));extern struct audit_buffer *audit_log_start (struct audit_context *ctx,gfp_t gfp_mask,int type); +extern void audit_log_vformat (struct audit_buffer *ab, +const char *fmt,va_list args) + __attribute__((format (printf,2 ,0 )));extern void audit_log_format (struct audit_buffer *ab,const char *fmt, ...) __attribute__((format (printf,2 ,3 )));
kernel/audit.c +4 -2
@@ -1220,8 +1220,7 @@ static inline int audit_expand(struct au* will be called a second time. Currently, we assume that a printk * can't format message larger than 1024 bytes, so we don't either. */ -static void audit_log_vformat (struct audit_buffer *ab,const char *fmt, -va_list args) +void audit_log_vformat (struct audit_buffer *ab,const char *fmt,va_list args) {int len, avail;struct sk_buff *skb;
@@ -1476,3 +1475,6 @@ EXPORT_SYMBOL(audit_log_start);EXPORT_SYMBOL (audit_log_end);EXPORT_SYMBOL (audit_log_format);EXPORT_SYMBOL (audit_log); +EXPORT_SYMBOL_GPL (audit_log_vformat); +EXPORT_SYMBOL_GPL (audit_log_untrustedstring); +EXPORT_SYMBOL_GPL (audit_log_d_path);
PATCH 37/45 AppArmor: Main Part
In reply to [email protected]The underlying functions by which the AppArmor LSM hooks are implemented.
--- security/apparmor/main.c | 1361 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1361 insertions(+)
security/apparmor/main.c new file +1361 -0
@@ -0,0 +1,1361 @@ +/* +* Copyright (C) 2002-2007 Novell/SUSE +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation, version 2 of the +* License. +* +* AppArmor Core +*/ + +#include <linux/security.h> +#include <linux/namei.h> +#include <linux/audit.h> +#include <linux/mount.h> +#include <linux/ptrace.h> + +#include "apparmor.h" + +#include "inline.h" + +/* +* Table of capability names: we generate it from capabilities.h. +*/ +static const char *capability_names[] = { +#include "capability_names.h" +}; + +struct aa_namespace *default_namespace; + +static int aa_inode_mode (struct inode *inode) +{ +/* if the inode doesn't exist the user is creating it */ +if (!inode || current->fsuid == inode->i_uid) +return AA_USER_SHIFT ; +if (in_group_p (inode->i_gid)) +return AA_GROUP_SHIFT ; +return AA_OTHER_SHIFT ; +} + +/** +* aa_file_denied - check for @mask access on a file +* @profile: profile to check against +* @name: pathname of file +* @mask: permission mask requested for file +* +* Return %0 on success, or else the permissions in @mask that the +* profile denies. +*/ +static int aa_file_denied (struct aa_profile *profile,const char *name, +int mask) +{ +return (mask & ~aa_match (profile->file_rules, name)); +} + +/** +* aa_link_denied - check for permission to link a file +* @profile: profile to check against +* @link: pathname of link being created +* @target: pathname of target to be linked to +* @target_mode: UGO shift for target inode +* @request_mask: the permissions subset valid only if link succeeds +* Return %0 on success, or else the permissions that the profile denies. +*/ +static int aa_link_denied (struct aa_profile *profile,const char *link, +const char *target,int target_mode, +int *request_mask) +{ +unsigned int state; +int l_mode, t_mode, denied_mask =0 ; +int link_mask =AA_MAY_LINK << target_mode; + + *request_mask = link_mask; + + l_mode =aa_match_state (profile->file_rules,DFA_START , link, &state); +if (l_mode & link_mask) { +int mode; +/* test to see if target can be paired with link */ + state =aa_dfa_null_transition (profile->file_rules, state); + mode =aa_match_state (profile->file_rules, state, target, +NULL ); + +if (!(mode & link_mask)) + denied_mask |= link_mask; +if (!(mode & (AA_LINK_SUBSET_TEST << target_mode))) +return denied_mask; + } + +/* do link perm subset test */ + l_mode =aa_match (profile->file_rules, link); + t_mode =aa_match (profile->file_rules, target); + +/* Ignore valid-profile-transition flags. */ + l_mode &= ~AA_SHARED_PERMS ; + t_mode &= ~AA_SHARED_PERMS ; + + *request_mask |= l_mode; + +/* Link always requires 'l' on the link for both parts of the pair. +* If a subset test is required a permission subset test of the +* perms for the link are done against the user:group:other of the +* target's 'r', 'w', 'x', 'a', 'z', and 'm' permissions. +* +* If the link has 'x', an exact match of all the execute flags +* ('i', 'u', 'U', 'p', 'P'). +*/ +#define SUBSET_PERMS (AA_FILE_PERMS & ~AA_LINK_BITS) + denied_mask |= ~l_mode & link_mask; +if (l_mode &SUBSET_PERMS ) { + denied_mask |= (l_mode &SUBSET_PERMS ) & ~t_mode; +if (denied_mask &AA_EXEC_BITS ) + denied_mask |= l_mode &AA_ALL_EXEC_MODS ; +else if (l_mode &AA_EXEC_BITS ) { +if (l_mode &AA_USER_EXEC && + (l_mode &AA_USER_EXEC_MODS ) != + (t_mode &AA_USER_EXEC_MODS )) + denied_mask |=AA_USER_EXEC | + (l_mode &AA_USER_EXEC_MODS ); +if (l_mode &AA_GROUP_EXEC && + (l_mode &AA_GROUP_EXEC_MODS ) != + (t_mode &AA_GROUP_EXEC_MODS )) + denied_mask |=AA_GROUP_EXEC | + (l_mode &AA_GROUP_EXEC_MODS ); +if (l_mode &AA_OTHER_EXEC && + (l_mode &AA_OTHER_EXEC_MODS ) != + (t_mode &AA_OTHER_EXEC_MODS )) + denied_mask |=AA_OTHER_EXEC | + (l_mode &AA_OTHER_EXEC_MODS ); + } + }else + denied_mask |= t_mode | link_mask; +#undef SUBSET_PERMS + +return denied_mask; +} + +/** +* aa_get_name - compute the pathname of a file +* @dentry: dentry of the file +* @mnt: vfsmount of the file +* @buffer: buffer that aa_get_name() allocated +* @check: AA_CHECK_DIR is set if the file is a directory +* +* Returns a pointer to the beginning of the pathname (which usually differs +* from the beginning of the buffer), or an error code. +* +* We need @check to indicate whether the file is a directory or not because +* the file may not yet exist, and so we cannot check the inode's file type. +*/ +static char *aa_get_name (struct dentry *dentry,struct vfsmount *mnt, +char **buffer,int check) +{ +char *name; +int is_dir, size =256 ; + + is_dir = (check &AA_CHECK_DIR ) ?1 :0 ; + +for (;;) { +char *buf =kmalloc (size,GFP_KERNEL ); +if (!buf) +return ERR_PTR (-ENOMEM ); + + name =d_namespace_path (dentry, mnt, buf, size - is_dir); +if (!IS_ERR (name)) { +if (name[0 ] !='/' ) { +/* +* This dentry is not connected to the +* namespace root -- reject access. +*/ +kfree (buf); +return ERR_PTR (-ENOENT ); + } +if (is_dir && name[1 ] !='\0' ) { +/* +* Append "/" to the pathname. The root +* directory is a special case; it already +* ends in slash. +*/ + buf[size -2 ] ='/' ; + buf[size -1 ] ='\0' ; + } + + *buffer = buf; +return name; + } +if (PTR_ERR (name) != -ENAMETOOLONG ) +return name; + +kfree (buf); + size <<=1 ; +if (size > apparmor_path_max) +return ERR_PTR (-ENAMETOOLONG ); + } +} + +static inline void aa_put_name_buffer (char *buffer) +{ +kfree (buffer); +} + +/** +* aa_perm_dentry - check if @profile allows @mask for a file +* @profile: profile to check against +* @dentry: dentry of the file +* @mnt: vfsmount o the file +* @sa: audit context +* @mask: requested profile permissions +* @check: kind of check to perform +* +* Returns 0 upon success, or else an error code. +* +* @check indicates the file type, and whether the file was accessed through +* an open file descriptor (AA_CHECK_FD) or not. +*/ +static int aa_perm_dentry (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,struct aa_audit *sa,int check) +{ +int error; +char *buffer =NULL ; + + sa->name =aa_get_name (dentry, mnt, &buffer, check); + sa->request_mask <<=aa_inode_mode (dentry->d_inode); +if (IS_ERR (sa->name)) { +/* +* deleted files are given a pass on permission checks when +* accessed through a file descriptor. +*/ +if (PTR_ERR (sa->name) == -ENOENT && (check &AA_CHECK_FD )) + sa->denied_mask =0 ; +else { + sa->denied_mask = sa->request_mask; + sa->error_code =PTR_ERR (sa->name); + } + sa->name =NULL ; + }else + sa->denied_mask =aa_file_denied (profile, sa->name, + sa->request_mask); + +if (!sa->denied_mask) + sa->error_code =0 ; + + error =aa_audit (profile, sa); +aa_put_name_buffer (buffer); + +return error; +} + +int alloc_default_namespace (void ) +{ +struct aa_namespace *ns; +char *name =kstrdup ("default" ,GFP_KERNEL ); +if (!name) +return -ENOMEM ; + ns =alloc_aa_namespace (name); +if (!ns) { +kfree (name); +return -ENOMEM ; + } + +write_lock (&profile_ns_list_lock); + default_namespace = ns; +aa_get_namespace (ns); +list_add (&ns->list, &profile_ns_list); +write_unlock (&profile_ns_list_lock); + +return 0 ; +} + +void free_default_namespace (void ) +{ +write_lock (&profile_ns_list_lock); +list_del_init (&default_namespace->list); +write_unlock (&profile_ns_list_lock); +aa_put_namespace (default_namespace); + default_namespace =NULL ; +} + +static void aa_audit_file_sub_mask (struct audit_buffer *ab,char *buffer, +int mask) +{ +char *m = buffer; + +if (mask &AA_EXEC_MMAP ) + *m++ ='m' ; +if (mask &MAY_READ ) + *m++ ='r' ; +if (mask &MAY_WRITE ) + *m++ ='w' ; +else if (mask &MAY_APPEND ) + *m++ ='a' ; +if (mask &MAY_EXEC ) { +if (mask &AA_EXEC_UNSAFE ) { +switch (mask &AA_EXEC_MODIFIERS ) { +case AA_EXEC_UNCONFINED : + *m++ ='u' ; +break ; +case AA_EXEC_PIX : + *m++ ='p' ; +/* fall through */ +case AA_EXEC_INHERIT : + *m++ ='i' ; +break ; +case AA_EXEC_PROFILE : + *m++ ='p' ; +break ; + } + }else { +switch (mask &AA_EXEC_MODIFIERS ) { +case AA_EXEC_UNCONFINED : + *m++ ='U' ; +break ; +case AA_EXEC_PIX : + *m++ ='P' ; +/* fall through */ +case AA_EXEC_INHERIT : + *m++ ='I' ; +break ; +case AA_EXEC_PROFILE : + *m++ ='P' ; +break ; + } + } + *m++ ='x' ; + } +if (mask &AA_MAY_LINK ) + *m++ ='l' ; +if (mask &AA_MAY_LOCK ) + *m++ ='k' ; + *m++ ='\0' ; +} + +static void aa_audit_file_mask (struct audit_buffer *ab,const char *name, +int mask) +{ +char user[10 ], group[10 ], other[10 ]; + +aa_audit_file_sub_mask (ab, user, + (mask &AA_USER_PERMS ) >>AA_USER_SHIFT ); +aa_audit_file_sub_mask (ab, group, + (mask &AA_GROUP_PERMS ) >>AA_GROUP_SHIFT ); +aa_audit_file_sub_mask (ab, other, + (mask &AA_OTHER_PERMS ) >>AA_OTHER_SHIFT ); + +audit_log_format (ab," %s=\"%s:%s:%s\"" , name, user, group, other); +} + +/** +* aa_audit - Log an audit event to the audit subsystem +* @profile: profile to check against +* @sa: audit event +* @audit_cxt: audit context to log message to +* @type: audit event number +*/ +static int aa_audit_base (struct aa_profile *profile,struct aa_audit *sa, +struct audit_context *audit_cxt,int type) +{ +struct audit_buffer *ab =NULL ; + + ab =audit_log_start (audit_cxt, sa->gfp_mask, type); + +if (!ab) { +AA_ERROR ("Unable to log event (%d) to audit subsys\n" , + type); +/* don't fail operations in complain mode even if logging +* fails */ +return type ==AUDIT_APPARMOR_ALLOWED ?0 : -ENOMEM ; + } + +if (sa->operation) +audit_log_format (ab,"operation=\"%s\"" , sa->operation); + +if (sa->info) +audit_log_format (ab," info=\"%s\"" , sa->info); + +if (sa->request_mask) +aa_audit_file_mask (ab,"request_mask" , sa->request_mask); + +if (sa->denied_mask) +aa_audit_file_mask (ab,"denied_mask" , sa->denied_mask); + +if (sa->iattr) { +struct iattr *iattr = sa->iattr; + +audit_log_format (ab," attribute=\"%s%s%s%s%s%s%s\"" , + iattr->ia_valid &ATTR_MODE ?"mode," :"" , + iattr->ia_valid &ATTR_UID ?"uid," :"" , + iattr->ia_valid &ATTR_GID ?"gid," :"" , + iattr->ia_valid &ATTR_SIZE ?"size," :"" , + iattr->ia_valid & (ATTR_ATIME |ATTR_ATIME_SET ) ? +"atime," :"" , + iattr->ia_valid & (ATTR_MTIME |ATTR_MTIME_SET ) ? +"mtime," :"" , + iattr->ia_valid &ATTR_CTIME ?"ctime," :"" ); + } + +if (sa->task) +audit_log_format (ab," task=%d" , sa->task); + +if (sa->parent) +audit_log_format (ab," parent=%d" , sa->parent); + +if (sa->name) { +audit_log_format (ab," name=" ); +audit_log_untrustedstring (ab, sa->name); + } + +if (sa->name2) { +audit_log_format (ab," name2=" ); +audit_log_untrustedstring (ab, sa->name2); + } + +audit_log_format (ab," pid=%d" , current->pid); + +if (profile) { +audit_log_format (ab," profile=" ); +audit_log_untrustedstring (ab, profile->name); + +audit_log_format (ab," namespace=" ); +audit_log_untrustedstring (ab, profile->ns->name); + } + +audit_log_end (ab); + +return type ==AUDIT_APPARMOR_ALLOWED ?0 : sa->error_code; +} + +/** +* aa_audit_syscallreject - Log a syscall rejection to the audit subsystem +* @profile: profile to check against +* @gfp: memory allocation flags +* @msg: string describing syscall being rejected +*/ +int aa_audit_syscallreject (struct aa_profile *profile,gfp_t gfp, +const char *msg) +{ +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.operation ="syscall" ; + sa.name = msg; + sa.gfp_mask = gfp; + sa.error_code = -EPERM ; + +return aa_audit_base (profile, &sa, current->audit_context, +AUDIT_APPARMOR_DENIED ); +} + +int aa_audit_message (struct aa_profile *profile,struct aa_audit *sa, +int type) +{ +struct audit_context *audit_cxt; + + audit_cxt = apparmor_logsyscall ? current->audit_context :NULL ; +return aa_audit_base (profile, sa, audit_cxt, type); +} + +void aa_audit_hint (struct aa_profile *profile,struct aa_audit *sa) +{ +aa_audit_message (profile, sa,AUDIT_APPARMOR_HINT ); +} + +void aa_audit_status (struct aa_profile *profile,struct aa_audit *sa) +{ +aa_audit_message (profile, sa,AUDIT_APPARMOR_STATUS ); +} + +int aa_audit_reject (struct aa_profile *profile,struct aa_audit *sa) +{ +return aa_audit_message (profile, sa,AUDIT_APPARMOR_DENIED ); +} + +/** +* aa_audit - Log an audit event to the audit subsystem +* @profile: profile to check against +* @sa: audit event +*/ +int aa_audit (struct aa_profile *profile,struct aa_audit *sa) +{ +int type =AUDIT_APPARMOR_DENIED ; +struct audit_context *audit_cxt; + +if (likely (!sa->error_code)) { +if (likely (!PROFILE_AUDIT (profile))) +/* nothing to log */ +return 0 ; +else + type =AUDIT_APPARMOR_AUDIT ; + }else if (PROFILE_COMPLAIN (profile)) { + type =AUDIT_APPARMOR_ALLOWED ; + } + + audit_cxt = apparmor_logsyscall ? current->audit_context :NULL ; +return aa_audit_base (profile, sa, audit_cxt, type); +} + +/** +* aa_attr - check if attribute change is allowed +* @profile: profile to check against +* @dentry: dentry of the file to check +* @mnt: vfsmount of the file to check +* @iattr: attribute changes requested +*/ +int aa_attr (struct aa_profile *profile,struct dentry *dentry, +struct vfsmount *mnt,struct iattr *iattr) +{ +struct inode *inode = dentry->d_inode; +int error, check; +struct aa_audit sa; + +memset (&sa,0 ,sizeof (sa)); + sa.operation ="setattr" ; + sa.gfp_mask =GFP_KERNEL ; + sa.iattr = iattr; + sa.request_mask =MAY_WRITE ; + sa.error_code = -EACCES ; + + check =0 ; +if (inode &&S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; +if (iattr->ia_valid &ATTR_FILE ) + check |=AA_CHECK_FD ; + + error =aa_perm_dentry (profile, dentry, mnt, &sa, check); + +return error; +} + +/** +* aa_perm_xattr - check if xattr attribute change is allowed +* @profile: profile to check against +* @dentry: dentry of the file to check +* @mnt: vfsmount of the file to check +* @operation: xattr operation being done +* @mask: access mode requested +* @check: kind of check to perform +*/ +int aa_perm_xattr (struct aa_profile *profile,const char *operation, +struct dentry *dentry,struct vfsmount *mnt,int mask, +int check) +{ +struct inode *inode = dentry->d_inode; +int error; +struct aa_audit sa; + +memset (&sa,0 ,sizeof (sa)); + sa.operation = operation; + sa.gfp_mask =GFP_KERNEL ; + sa.request_mask = mask; + sa.error_code = -EACCES ; + +if (inode &&S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; + + error =aa_perm_dentry (profile, dentry, mnt, &sa, check); + +return error; +} + +/** +* aa_perm - basic apparmor permissions check +* @profile: profile to check against +* @dentry: dentry of the file to check +* @mnt: vfsmount of the file to check +* @mask: access mode requested +* @check: kind of check to perform +* +* Determine if access @mask for the file is authorized by @profile. +* Returns 0 on success, or else an error code. +*/ +int aa_perm (struct aa_profile *profile,const char *operation, +struct dentry *dentry,struct vfsmount *mnt,int mask,int check) +{ +struct aa_audit sa; +int error =0 ; + +if (mask ==0 ) + goto out; + +memset (&sa,0 ,sizeof (sa)); + sa.operation = operation; + sa.gfp_mask =GFP_KERNEL ; + sa.request_mask = mask; + sa.error_code = -EACCES ; + + error =aa_perm_dentry (profile, dentry, mnt, &sa, check); + +out: +return error; +} + +/** +* aa_perm_dir +* @profile: profile to check against +* @dentry: dentry of directory to check +* @mnt: vfsmount of directory to check +* @operation: directory operation being performed +* @mask: access mode requested +* +* Determine if directory operation (make/remove) for dentry is authorized +* by @profile. +* Returns 0 on success, or else an error code. +*/ +int aa_perm_dir (struct aa_profile *profile,const char *operation, +struct dentry *dentry,struct vfsmount *mnt,int mask) +{ +struct aa_audit sa; + +memset (&sa,0 ,sizeof (sa)); + sa.operation = operation; + sa.gfp_mask =GFP_KERNEL ; + sa.request_mask = mask; + sa.error_code = -EACCES ; + +return aa_perm_dentry (profile, dentry, mnt, &sa,AA_CHECK_DIR ); +} + +int aa_perm_path (struct aa_profile *profile,const char *operation, +const char *name,int mask,uid_t owner_uid,uid_t group_uid) +{ +struct aa_audit sa; + +memset (&sa,0 ,sizeof (sa)); + sa.operation = operation; + sa.gfp_mask =GFP_KERNEL ; + sa.request_mask = mask; + sa.name = name; +if (current->fsuid == owner_uid) + sa.request_mask = mask <<AA_USER_SHIFT ; +else if (in_group_p (group_uid)) + sa.request_mask = mask <<AA_GROUP_SHIFT ; +else + sa.request_mask = mask <<AA_OTHER_SHIFT ; + + sa.denied_mask =aa_file_denied (profile, name, sa.request_mask) ; + sa.error_code = sa.denied_mask ? -EACCES :0 ; + +return aa_audit (profile, &sa); +} + +/** +* aa_capability - test permission to use capability +* @cxt: aa_task_context with profile to check against +* @cap: capability to be tested +* +* Look up capability in profile capability set. +* Returns 0 on success, or else an error code. +*/ +int aa_capability (struct aa_task_context *cxt,int cap) +{ +int error =cap_raised (cxt->profile->capabilities, cap) ?0 : -EPERM ; +struct aa_audit sa; + +/* test if cap has alread been logged */ +if (cap_raised (cxt->caps_logged, cap)) { +if (PROFILE_COMPLAIN (cxt->profile)) + error =0 ; +return error; + }else +/* don't worry about rcu replacement of the cxt here. +* caps_logged is a cache to reduce the occurence of +* duplicate messages in the log. The worst that can +* happen is duplicate capability messages shows up in +* the audit log +*/ +cap_raise (cxt->caps_logged, cap); + +memset (&sa,0 ,sizeof (sa)); + sa.operation ="capable" ; + sa.gfp_mask =GFP_ATOMIC ; + sa.name = capability_names[cap]; + sa.error_code = error; + + error =aa_audit (cxt->profile, &sa); + +return error; +} + +/* must be used inside rcu_read_lock or task_lock */ +int aa_may_ptrace (struct aa_task_context *cxt,struct aa_profile *tracee) +{ +if (!cxt || cxt->profile == tracee) +return 0 ; +return aa_capability (cxt,CAP_SYS_PTRACE ); +} + +/** +* aa_link - hard link check +* @profile: profile to check against +* @link: dentry of link being created +* @link_mnt: vfsmount of link being created +* @target: dentry of link target +* @target_mnt: vfsmunt of link target +* +* Returns 0 on success, or else an error code. +*/ +int aa_link (struct aa_profile *profile, +struct dentry *link,struct vfsmount *link_mnt, +struct dentry *target,struct vfsmount *target_mnt) +{ +int error; +struct aa_audit sa; +char *buffer =NULL , *buffer2 =NULL ; + +memset (&sa,0 ,sizeof (sa)); + sa.operation ="inode_link" ; + sa.gfp_mask =GFP_KERNEL ; + sa.name =aa_get_name (link, link_mnt, &buffer,0 ); + sa.name2 =aa_get_name (target, target_mnt, &buffer2,0 ); + +if (IS_ERR (sa.name)) { + sa.error_code =PTR_ERR (sa.name); + sa.name =NULL ; + } +if (IS_ERR (sa.name2)) { + sa.error_code =PTR_ERR (sa.name2); + sa.name2 =NULL ; + } + +if (sa.name && sa.name2) { + sa.denied_mask =aa_link_denied (profile, sa.name, sa.name2, +aa_inode_mode (target->d_inode), + &sa.request_mask); + sa.error_code = sa.denied_mask ? -EACCES :0 ; + } + + error =aa_audit (profile, &sa); + +aa_put_name_buffer (buffer); +aa_put_name_buffer (buffer2); + +return error; +} + +/******************************* +* Global task related functions +*******************************/ + +/** +* aa_clone - initialize the task context for a new task +* @child: task that is being created +* +* Returns 0 on success, or else an error code. +*/ +int aa_clone (struct task_struct *child) +{ +struct aa_task_context *cxt, *child_cxt; +struct aa_profile *profile; + +if (!aa_task_context (current)) +return 0 ; + child_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!child_cxt) +return -ENOMEM ; + +repeat: + profile =aa_get_profile (current); +if (profile) { +lock_profile (profile); + cxt =aa_task_context (current); +if (unlikely (profile->isstale || !cxt || + cxt->profile != profile)) { +/** +* Race with profile replacement or removal, or with +* task context removal. +*/ +unlock_profile (profile); +aa_put_profile (profile); + goto repeat; + } + +/* No need to grab the child's task lock here. */ +aa_change_task_context (child, child_cxt, profile, + cxt->cookie, cxt->previous_profile); +unlock_profile (profile); + +if (APPARMOR_COMPLAIN (child_cxt) && + profile == profile->ns->null_complain_profile) { +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.operation ="clone" ; + sa.gfp_mask =GFP_KERNEL ; + sa.task = child->pid; +aa_audit_hint (profile, &sa); + } +aa_put_profile (profile); + }else +aa_free_task_context (child_cxt); + +return 0 ; +} + +static struct aa_profile * +aa_register_find (struct aa_profile *profile,const char *name,int mandatory, +int complain,struct aa_audit *sa) +{ +struct aa_profile *new_profile; + +/* Locate new profile */ +if (profile) + new_profile =aa_find_profile (profile->ns, name); +else + new_profile =aa_find_profile (default_namespace, name); + +if (new_profile) { +AA_DEBUG ("%s: setting profile %s\n" , + __FUNCTION__, new_profile->name); + }else if (mandatory && profile) { + sa->info ="mandatory profile missing" ; + sa->denied_mask = sa->request_mask;/* shifted MAY_EXEC */ +if (complain) { +aa_audit_hint (profile, sa); + new_profile = +aa_dup_profile (profile->ns->null_complain_profile); + }else { +aa_audit_reject (profile, sa); +return ERR_PTR (-EACCES );/* was -EPERM */ + } + }else { +/* Only way we can get into this code is if task +* is unconfined, or pix. +*/ +AA_DEBUG ("%s: No profile found for exec image '%s'\n" , + __FUNCTION__, + name); + } +return new_profile; +} + +/** +* aa_register - register a new program +* @bprm: binprm of program being registered +* +* Try to register a new program during execve(). This should give the +* new program a valid aa_task_context if confined. +*/ +int aa_register (struct linux_binprm *bprm) +{ +const char *filename; +char *buffer =NULL ; +struct file *filp = bprm->file; +struct aa_profile *profile, *old_profile, *new_profile =NULL ; +int exec_mode, complain =0 , shift; +struct aa_audit sa; + +AA_DEBUG ("%s\n" , __FUNCTION__); + + filename =aa_get_name (filp->f_dentry, filp->f_vfsmnt, &buffer,0 ); +if (IS_ERR (filename)) { +AA_ERROR ("%s: Failed to get filename" , __FUNCTION__); +return -ENOENT ; + } + + shift =aa_inode_mode (filp->f_dentry->d_inode); + exec_mode =AA_EXEC_UNSAFE << shift; + +memset (&sa,0 ,sizeof (sa)); + sa.operation ="exec" ; + sa.gfp_mask =GFP_KERNEL ; + sa.name = filename; + sa.request_mask =MAY_EXEC << shift; + +repeat: + profile =aa_get_profile (current); +if (profile) { + complain =PROFILE_COMPLAIN (profile); + +/* Confined task, determine what mode inherit, unconfined or +* mandatory to load new profile +*/ + exec_mode =aa_match (profile->file_rules, filename); + +if (exec_mode & sa.request_mask) { +switch ((exec_mode >> shift) &AA_EXEC_MODIFIERS ) { +case AA_EXEC_INHERIT : +AA_DEBUG ("%s: INHERIT %s\n" , + __FUNCTION__, + filename); +/* nothing to be done here */ + goto cleanup; + +case AA_EXEC_UNCONFINED : +AA_DEBUG ("%s: UNCONFINED %s\n" , + __FUNCTION__, + filename); + +/* detach current profile */ + new_profile =NULL ; +break ; + +case AA_EXEC_PROFILE : +AA_DEBUG ("%s: PROFILE %s\n" , + __FUNCTION__, + filename); + new_profile =aa_register_find (profile, + filename, +1 , complain, + &sa); +break ; +case AA_EXEC_PIX : +AA_DEBUG ("%s: PROFILE %s\n" , + __FUNCTION__, + filename); + new_profile =aa_register_find (profile, + filename, +0 , complain, + &sa); +if (!new_profile) +/* inherit - nothing to be done here */ + goto cleanup; +break ; + } + + }else if (complain) { +/* There was no entry in calling profile +* describing mode to execute image in. +* Drop into null-profile (disabling secure exec). +*/ + new_profile = +aa_dup_profile (profile->ns->null_complain_profile); + exec_mode |=AA_EXEC_UNSAFE << shift; + }else { + sa.denied_mask = sa.request_mask; +aa_audit_reject (profile, &sa); + new_profile =ERR_PTR (-EPERM ); + } + }else { +/* Unconfined task, load profile if it exists */ + new_profile =aa_register_find (NULL , filename,0 ,0 , &sa); +if (new_profile ==NULL ) + goto cleanup; + } + +if (IS_ERR (new_profile)) + goto cleanup; + + old_profile =__aa_replace_profile (current, new_profile); +if (IS_ERR (old_profile)) { +aa_put_profile (new_profile); +aa_put_profile (profile); +if (PTR_ERR (old_profile) == -ESTALE ) + goto repeat; +if (PTR_ERR (old_profile) == -EPERM ) { + sa.denied_mask = sa.request_mask; + sa.info ="unable to set profile due to ptrace" ; + sa.task = current->parent->pid; +aa_audit_reject (profile, &sa); + } + new_profile = old_profile; + goto cleanup; + } +aa_put_profile (old_profile); +aa_put_profile (profile); + +/* Handle confined exec. +* Can be at this point for the following reasons: +* 1. unconfined switching to confined +* 2. confined switching to different confinement +* 3. confined switching to unconfined +* +* Cases 2 and 3 are marked as requiring secure exec +* (unless policy specified "unsafe exec") +*/ +if (!(exec_mode & (AA_EXEC_UNSAFE << shift))) { +unsigned long bprm_flags; + + bprm_flags =AA_SECURE_EXEC_NEEDED ; + bprm->security = (void *) + ((unsigned long )bprm->security | bprm_flags); + } + +if (complain && new_profile && + new_profile == new_profile->ns->null_complain_profile) { + sa.request_mask =0 ; + sa.name =NULL ; + sa.info ="set profile" ; +aa_audit_hint (new_profile, &sa); + } +cleanup: +aa_put_name_buffer (buffer); +if (IS_ERR (new_profile)) +return PTR_ERR (new_profile); +aa_put_profile (new_profile); +return 0 ; +} + +/** +* aa_release - release a task context +* @task: task being released +* +* This is called after a task has exited and the parent has reaped it. +*/ +void aa_release (struct task_struct *task) +{ +struct aa_task_context *cxt; +struct aa_profile *profile; +/* +* While the task context is still on a profile's task context +* list, another process could replace the profile under us, +* leaving us with a locked profile that is no longer attached +* to this task. So after locking the profile, we check that +* the profile is still attached. The profile lock is +* sufficient to prevent the replacement race so we do not lock +* the task. +* +* Use lock subtyping to avoid lockdep reporting a false irq +* possible inversion between the task_lock and profile_lock +* +* We also avoid taking the task_lock here because lock_dep +* would report another false {softirq-on-W} potential irq_lock +* inversion. +* +* If the task does not have a profile attached we are safe; +* nothing can race with us at this point. +*/ + +repeat: + profile =aa_get_profile (task); +if (profile) { +lock_profile_nested (profile, aa_lock_task_release); + cxt =aa_task_context (task); +if (unlikely (!cxt || cxt->profile != profile)) { +unlock_profile (profile); +aa_put_profile (profile); + goto repeat; + } +aa_change_task_context (task,NULL ,NULL ,0 ,NULL ); +unlock_profile (profile); +aa_put_profile (profile); + } +} + +static int do_change_profile (struct aa_profile *expected, +struct aa_namespace *ns,const char *name, +u64 cookie,int restore,struct aa_audit *sa) +{ +struct aa_profile *new_profile =NULL , *old_profile =NULL , + *previous_profile =NULL ; +struct aa_task_context *new_cxt, *cxt; +int error =0 ; + + sa->name = name; + + new_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!new_cxt) +return -ENOMEM ; + + new_profile =aa_find_profile (ns, name); +if (!new_profile && !restore) { +if (!PROFILE_COMPLAIN (expected)) +return -ENOENT ; + new_profile =aa_dup_profile (ns->null_complain_profile); + } + + cxt =lock_task_and_profiles (current, new_profile); +if (!cxt) { + error = -EPERM ; + goto out; + } + old_profile = cxt->profile; + +if (cxt->profile != expected || (new_profile && new_profile->isstale)) { + error = -ESTALE ; + goto out; + } + +if (cxt->previous_profile) { +if (cxt->cookie != cookie) { + error = -EACCES ; + sa->info ="killing process" ; +aa_audit_reject (cxt->profile, sa); +/* terminate process */ + (void )send_sig_info (SIGKILL ,NULL , current); + goto out; + } + +if (!restore) + previous_profile = cxt->previous_profile; + }else + previous_profile = cxt->profile; + +if ((current->ptrace &PT_PTRACED ) &&aa_may_ptrace (cxt, new_profile)) { + error = -EACCES ; + goto out; + } + +if (new_profile == ns->null_complain_profile) +aa_audit_hint (cxt->profile, sa); + +if (APPARMOR_AUDIT (cxt)) +aa_audit_message (cxt->profile, sa,AUDIT_APPARMOR_AUDIT ); + +if (!restore && cookie) +aa_change_task_context (current, new_cxt, new_profile, cookie, + previous_profile); +else +/* either return to previous_profile, or a permanent change */ +aa_change_task_context (current, new_cxt, new_profile,0 ,NULL ); + +out: +if (aa_task_context (current) != new_cxt) +aa_free_task_context (new_cxt); +task_unlock (current); +unlock_both_profiles (old_profile, new_profile); +aa_put_profile (new_profile); +return error; +} + +/** +* aa_change_profile - perform a one-way profile transition +* @ns_name: name of the profile namespace to change to +* @name: name of profile to change to +* Change to new profile @name. Unlike with hats, there is no way +* to change back. +* +* Returns %0 on success, error otherwise. +*/ +int aa_change_profile (const char *ns_name,const char *name) +{ +struct aa_task_context *cxt; +struct aa_profile *profile; +struct aa_namespace *ns =NULL ; +struct aa_audit sa; +unsigned int state; +int error = -EINVAL ; + +if (!name) +return -EINVAL ; + +memset (&sa,0 ,sizeof (sa)); + sa.gfp_mask =GFP_ATOMIC ; + sa.operation ="change_profile" ; + +repeat: +task_lock (current); + cxt =aa_task_context (current); +if (!cxt) { +task_unlock (current); +return -EPERM ; + } + profile =aa_dup_profile (cxt->profile); +task_unlock (current); + +if (ns_name) + ns =aa_find_namespace (ns_name); +else + ns =aa_get_namespace (profile->ns); +if (!ns) { +aa_put_profile (profile); +return -ENOENT ; + } + +if (PROFILE_COMPLAIN (profile) || + (ns == profile->ns && + (aa_match (profile->file_rules, name) &AA_CHANGE_PROFILE ))) + error =do_change_profile (profile, ns, name,0 ,0 , &sa); +else { +/* check for a rule with a namespace prepended */ +aa_match_state (profile->file_rules,DFA_START , ns->name, + &state); + state =aa_dfa_null_transition (profile->file_rules, state); +if ((aa_match_state (profile->file_rules, state, name,NULL ) & +AA_CHANGE_PROFILE )) + error =do_change_profile (profile, ns, name,0 ,0 , + &sa); +else +/* no permission to transition to profile @name */ + error = -EACCES ; + } + +aa_put_namespace (ns); +aa_put_profile (profile); +if (error == -ESTALE ) + goto repeat; + +return error; +} + +/** +* aa_change_hat - change hat to/from subprofile +* @hat_name: hat to change to +* @cookie: magic value to validate the hat change +* +* Change to new @hat_name, and store the @hat_magic in the current task +* context. If the new @hat_name is %NULL and the @cookie matches that +* stored in the current task context and is not 0, return to the top level +* profile. +* Returns %0 on success, error otherwise. +*/ +int aa_change_hat (const char *hat_name,u64 cookie) +{ +struct aa_task_context *cxt; +struct aa_profile *profile, *previous_profile; +struct aa_audit sa; +int error =0 ; + +memset (&sa,0 ,sizeof (sa)); + sa.gfp_mask =GFP_ATOMIC ; + sa.operation ="change_hat" ; + +repeat: +task_lock (current); + cxt =aa_task_context (current); +if (!cxt) { +task_unlock (current); +return -EPERM ; + } + profile =aa_dup_profile (cxt->profile); + previous_profile =aa_dup_profile (cxt->previous_profile); +task_unlock (current); + +if (hat_name) { +char *name, *profile_name; +if (previous_profile) + profile_name = previous_profile->name; +else + profile_name = profile->name; + + name =kmalloc (strlen (hat_name) +3 +strlen (profile_name), +GFP_KERNEL ); +if (!name) { + error = -ENOMEM ; + goto out; + } +sprintf (name,"%s//%s" , profile_name, hat_name); + error =do_change_profile (profile, profile->ns, name, cookie, +0 , &sa); +kfree (name); + }else if (previous_profile) + error =do_change_profile (profile, profile->ns, + previous_profile->name, cookie,1 , + &sa); +/* else ignore restores when there is no saved profile */ + +out: +aa_put_profile (previous_profile); +aa_put_profile (profile); +if (error == -ESTALE ) + goto repeat; + +return error; +} + +/** +* __aa_replace_profile - replace a task's profile +* @task: task to switch the profile of +* @profile: profile to switch to +* +* Returns a handle to the previous profile upon success, or else an +* error code. +*/ +struct aa_profile *__aa_replace_profile (struct task_struct *task, +struct aa_profile *profile) +{ +struct aa_task_context *cxt, *new_cxt =NULL ; +struct aa_profile *old_profile =NULL ; + +if (profile) { + new_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!new_cxt) +return ERR_PTR (-ENOMEM ); + } + + cxt =lock_task_and_profiles (task, profile); +if (unlikely (profile && profile->isstale)) { +task_unlock (task); +unlock_both_profiles (profile, cxt ? cxt->profile :NULL ); +aa_free_task_context (new_cxt); +return ERR_PTR (-ESTALE ); + } + +if ((current->ptrace &PT_PTRACED ) &&aa_may_ptrace (cxt, profile)) { +task_unlock (task); +unlock_both_profiles (profile, cxt ? cxt->profile :NULL ); +aa_free_task_context (new_cxt); +return ERR_PTR (-EPERM ); + } + +if (cxt) + old_profile =aa_dup_profile (cxt->profile); +aa_change_task_context (task, new_cxt, profile,0 ,NULL ); + +task_unlock (task); +unlock_both_profiles (profile, old_profile); +return old_profile; +} + +/** +* lock_task_and_profiles - lock the task and confining profiles and @profile +* @task: task to lock +* @profile: extra profile to lock in addition to the current profile +* +* Handle the spinning on locking to make sure the task context and +* profile are consistent once all locks are aquired. +* +* return the aa_task_context currently confining the task. The task lock +* will be held whether or not the task is confined. +*/ +struct aa_task_context * +lock_task_and_profiles (struct task_struct *task,struct aa_profile *profile) +{ +struct aa_task_context *cxt; +struct aa_profile *old_profile =NULL ; + +rcu_read_lock (); +repeat: + cxt =aa_task_context (task); +if (cxt) + old_profile = cxt->profile; + +lock_both_profiles (profile, old_profile); +task_lock (task); + +/* check for race with profile transition, replacement or removal */ +if (unlikely (cxt !=aa_task_context (task))) { +task_unlock (task); +unlock_both_profiles (profile, old_profile); + old_profile =NULL ; + goto repeat; + } +rcu_read_unlock (); +return cxt; +} + +static void free_aa_task_context_rcu_callback (struct rcu_head *head) +{ +struct aa_task_context *cxt; + + cxt = container_of(head, struct aa_task_context, rcu); +aa_free_task_context (cxt); +} + +/** +* aa_change_task_context - switch a task to use a new context and profile +* @task: task that is having its task context changed +* @new_cxt: new task context to use after the switch +* @profile: new profile to use after the switch +* @cookie: magic value to switch to +* @previous_profile: profile the task can return to +*/ +void aa_change_task_context (struct task_struct *task, +struct aa_task_context *new_cxt, +struct aa_profile *profile,u64 cookie, +struct aa_profile *previous_profile) +{ +struct aa_task_context *old_cxt =aa_task_context (task); + +if (old_cxt) { +list_del_init (&old_cxt->list); +call_rcu (&old_cxt->rcu, free_aa_task_context_rcu_callback); + } +if (new_cxt) { +/* clear the caps_logged cache, so that new profile/hat has +* chance to emit its own set of cap messages */ + new_cxt->caps_logged =CAP_EMPTY_SET ; + new_cxt->cookie = cookie; + new_cxt->task = task; + new_cxt->profile =aa_dup_profile (profile); + new_cxt->previous_profile =aa_dup_profile (previous_profile); +list_move (&new_cxt->list, &profile->task_contexts); + } +rcu_assign_pointer (task->security, new_cxt); +}
PATCH 38/45 AppArmor: Module and LSM hooks
In reply to [email protected]Module parameters, LSM hooks, initialization and teardown.
--- security/apparmor/lsm.c | 816 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 816 insertions(+)
security/apparmor/lsm.c new file +816 -0
@@ -0,0 +1,816 @@ +/* +* Copyright (C) 1998-2007 Novell/SUSE +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation, version 2 of the +* License. +* +* AppArmor LSM interface +*/ + +#include <linux/security.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include <linux/ctype.h> +#include <linux/sysctl.h> +#include <linux/audit.h> + +#include "apparmor.h" +#include "inline.h" + +/* Boot time disable flag */ +int apparmor_enabled =CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE ; + +static int __init apparmor_enabled_setup (char *str) +{ + apparmor_enabled =simple_strtol (str,NULL ,0 ); +return 1 ; +} +__setup ("apparmor=" , apparmor_enabled_setup); + + +static int param_set_aabool (const char *val,struct kernel_param *kp); +static int param_get_aabool (char *buffer,struct kernel_param *kp); +#define param_check_aabool (name, p) __param_check(name, p, int) + +static int param_set_aauint (const char *val,struct kernel_param *kp); +static int param_get_aauint (char *buffer,struct kernel_param *kp); +#define param_check_aauint (name, p) __param_check(name, p, int) + +/* Flag values, also controllable via /sys/module/apparmor/parameters +* We define special types as we want to do additional mediation. +* +* Complain mode -- in complain mode access failures result in auditing only +* and task is allowed access. audit events are processed by userspace to +* generate policy. Default is 'enforce' (0). +* Value is also togglable per profile and referenced when global value is +* enforce. +*/ +int apparmor_complain =0 ; +module_param_named (complain, apparmor_complain, aabool,S_IRUSR |S_IWUSR ); +MODULE_PARM_DESC (apparmor_complain,"Toggle AppArmor complain mode" ); + +/* Debug mode */ +int apparmor_debug =0 ; +module_param_named (debug, apparmor_debug, aabool,S_IRUSR |S_IWUSR ); +MODULE_PARM_DESC (apparmor_debug,"Toggle AppArmor debug mode" ); + +/* Audit mode */ +int apparmor_audit =0 ; +module_param_named (audit, apparmor_audit, aabool,S_IRUSR |S_IWUSR ); +MODULE_PARM_DESC (apparmor_audit,"Toggle AppArmor audit mode" ); + +/* Syscall logging mode */ +int apparmor_logsyscall =0 ; +module_param_named (logsyscall, apparmor_logsyscall, aabool,S_IRUSR |S_IWUSR ); +MODULE_PARM_DESC (apparmor_logsyscall,"Toggle AppArmor logsyscall mode" ); + +/* Maximum pathname length before accesses will start getting rejected */ +unsigned int apparmor_path_max =2 *PATH_MAX ; +module_param_named (path_max, apparmor_path_max, aauint,S_IRUSR |S_IWUSR ); +MODULE_PARM_DESC (apparmor_path_max,"Maximum pathname length allowed" ); + +static int param_set_aabool (const char *val,struct kernel_param *kp) +{ +if (aa_task_context (current)) +return -EPERM ; +return param_set_bool (val, kp); +} + +static int param_get_aabool (char *buffer,struct kernel_param *kp) +{ +if (aa_task_context (current)) +return -EPERM ; +return param_get_bool (buffer, kp); +} + +static int param_set_aauint (const char *val,struct kernel_param *kp) +{ +if (aa_task_context (current)) +return -EPERM ; +return param_set_uint (val, kp); +} + +static int param_get_aauint (char *buffer,struct kernel_param *kp) +{ +if (aa_task_context (current)) +return -EPERM ; +return param_get_uint (buffer, kp); +} + +static int aa_reject_syscall (struct task_struct *task,gfp_t flags, +const char *name) +{ +struct aa_profile *profile =aa_get_profile (task); +int error =0 ; + +if (profile) { + error =aa_audit_syscallreject (profile, flags, name); +aa_put_profile (profile); + } + +return error; +} + +static int apparmor_ptrace (struct task_struct *parent, +struct task_struct *child) +{ +struct aa_task_context *cxt; +int error =0 ; + +/* +* parent can ptrace child when +* - parent is unconfined +* - parent & child are in the same namespace && +* - parent is in complain mode +* - parent and child are confined by the same profile +* - parent profile has CAP_SYS_PTRACE +*/ + +rcu_read_lock (); + cxt =aa_task_context (parent); +if (cxt) { +if (parent->nsproxy != child->nsproxy) { +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.operation ="ptrace" ; + sa.gfp_mask =GFP_ATOMIC ; + sa.parent = parent->pid; + sa.task = child->pid; + sa.info ="different namespaces" ; +aa_audit_reject (cxt->profile, &sa); + error = -EPERM ; + }else { +struct aa_task_context *child_cxt = +aa_task_context (child); + + error =aa_may_ptrace (cxt, child_cxt ? + child_cxt->profile :NULL ); +if (PROFILE_COMPLAIN (cxt->profile)) { +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.operation ="ptrace" ; + sa.gfp_mask =GFP_ATOMIC ; + sa.parent = parent->pid; + sa.task = child->pid; +aa_audit_hint (cxt->profile, &sa); + } + } + } +rcu_read_unlock (); + +return error; +} + +static int apparmor_capable (struct task_struct *task,int cap) +{ +int error; + +/* cap_capable returns 0 on success, else -EPERM */ + error =cap_capable (task, cap); + +if (!error) { +struct aa_task_context *cxt; + +rcu_read_lock (); + cxt =aa_task_context (task); +if (cxt) + error =aa_capability (cxt, cap); +rcu_read_unlock (); + } + +return error; +} + +static int apparmor_sysctl (struct ctl_table *table,int op) +{ +struct aa_profile *profile =aa_get_profile (current); +int error =0 ; + +if (profile) { +char *buffer, *name; +int mask; + + mask =0 ; +if (op &4 ) + mask |=MAY_READ ; +if (op &2 ) + mask |=MAY_WRITE ; + + error = -ENOMEM ; + buffer = (char *)__get_free_page (GFP_KERNEL ); +if (!buffer) + goto out; + name =sysctl_pathname (table, buffer,PAGE_SIZE ); +if (name && name - buffer >=5 ) { + name -=5 ; +memcpy (name,"/proc" ,5 ); + error =aa_perm_path (profile,"sysctl" , name, mask,0 , +0 ); + } +free_page ((unsigned long )buffer); + } + +out: +return error; +} + +static int apparmor_bprm_set_security (struct linux_binprm *bprm) +{ +/* handle capability bits with setuid, etc */ +cap_bprm_set_security (bprm); +/* already set based on script name */ +if (bprm->sh_bang) +return 0 ; +return aa_register (bprm); +} + +static int apparmor_bprm_secureexec (struct linux_binprm *bprm) +{ +int ret =cap_bprm_secureexec (bprm); + +if (!ret && (unsigned long )bprm->security &AA_SECURE_EXEC_NEEDED ) { +AA_DEBUG ("%s: secureexec required for %s\n" , + __FUNCTION__, bprm->filename); + ret =1 ; + } + +return ret; +} + +static int apparmor_sb_mount (char *dev_name,struct nameidata *nd,char *type, +unsigned long flags,void *data) +{ +return aa_reject_syscall (current,GFP_KERNEL ,"mount" ); +} + +static int apparmor_umount (struct vfsmount *mnt,int flags) +{ +return aa_reject_syscall (current,GFP_KERNEL ,"umount" ); +} + +static int apparmor_inode_mkdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) +{ +struct aa_profile *profile; +int error =0 ; + +if (!mnt || !mediated_filesystem (dir)) + goto out; + + profile =aa_get_profile (current); + +if (profile) + error =aa_perm_dir (profile,"inode_mkdir" , dentry, mnt, +MAY_WRITE ); + +aa_put_profile (profile); + +out: +return error; +} + +static int apparmor_inode_rmdir (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) +{ +struct aa_profile *profile; +int error =0 ; + +if (!mnt || !mediated_filesystem (dir)) + goto out; + + profile =aa_get_profile (current); + +if (profile) + error =aa_perm_dir (profile,"inode_rmdir" , dentry, mnt, +MAY_WRITE ); + +aa_put_profile (profile); + +out: +return error; +} + +static int aa_permission (const char *operation,struct inode *inode, +struct dentry *dentry,struct vfsmount *mnt, +int mask,int check) +{ +int error =0 ; + +if (mnt &&mediated_filesystem (inode)) { +struct aa_profile *profile; + + profile =aa_get_profile (current); +if (profile) + error =aa_perm (profile, operation, dentry, mnt, mask, + check); +aa_put_profile (profile); + } +return error; +} + +static inline int aa_mask_permissions (int mask) +{ +if (mask &MAY_APPEND ) + mask &= (MAY_READ |MAY_APPEND |MAY_EXEC ); +else + mask &= (MAY_READ |MAY_WRITE |MAY_EXEC ); +return mask; +} + +static int apparmor_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) +{ +/* FIXME: may move to MAY_APPEND later */ +return aa_permission ("inode_create" , dir, dentry, mnt,MAY_WRITE ,0 ); +} + +static int apparmor_inode_link (struct dentry *old_dentry, +struct vfsmount *old_mnt,struct inode *dir, +struct dentry *new_dentry, +struct vfsmount *new_mnt) +{ +int error =0 ; +struct aa_profile *profile; + +if (!old_mnt || !new_mnt || !mediated_filesystem (dir)) + goto out; + + profile =aa_get_profile (current); + +if (profile) + error =aa_link (profile, new_dentry, new_mnt, + old_dentry, old_mnt); + +aa_put_profile (profile); + +out: +return error; +} + +static int apparmor_inode_unlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt) +{ +int check =0 ; + +if (S_ISDIR (dentry->d_inode->i_mode)) + check |=AA_CHECK_DIR ; +return aa_permission ("inode_unlink" , dir, dentry, mnt,MAY_WRITE , + check); +} + +static int apparmor_inode_symlink (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,const char *old_name) +{ +return aa_permission ("inode_symlink" , dir, dentry, mnt,MAY_WRITE ,0 ); +} + +static int apparmor_inode_mknod (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mode,dev_t dev) +{ +return aa_permission ("inode_mknod" , dir, dentry, mnt,MAY_WRITE ,0 ); +} + +static int apparmor_inode_rename (struct inode *old_dir, +struct dentry *old_dentry, +struct vfsmount *old_mnt, +struct inode *new_dir, +struct dentry *new_dentry, +struct vfsmount *new_mnt) +{ +struct aa_profile *profile; +int error =0 ; + +if ((!old_mnt && !new_mnt) || !mediated_filesystem (old_dir)) + goto out; + + profile =aa_get_profile (current); + +if (profile) { +struct inode *inode = old_dentry->d_inode; +int check =0 ; + +if (inode &&S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; +if (old_mnt) + error =aa_perm (profile,"inode_rename" , old_dentry, + old_mnt,MAY_READ |MAY_WRITE , check); + +if (!error && new_mnt) { + error =aa_perm (profile,"inode_rename" , new_dentry, + new_mnt,MAY_WRITE , check); + } + } + +aa_put_profile (profile); + +out: +return error; +} + +static int apparmor_inode_permission (struct inode *inode,int mask, +struct nameidata *nd) +{ +int check =0 ; + +if (!nd || nd->flags & (LOOKUP_PARENT |LOOKUP_CONTINUE )) +return 0 ; + mask =aa_mask_permissions (mask); +if (S_ISDIR (inode->i_mode)) { + check |=AA_CHECK_DIR ; +/* allow traverse accesses to directories */ + mask &= ~MAY_EXEC ; + } +return aa_permission ("inode_permission" , inode, nd->dentry, nd->mnt, + mask, check); +} + +static int apparmor_inode_setattr (struct dentry *dentry,struct vfsmount *mnt, +struct iattr *iattr) +{ +int error =0 ; + +if (!mnt) + goto out; + +if (mediated_filesystem (dentry->d_inode)) { +struct aa_profile *profile; + + profile =aa_get_profile (current); +/* +* Mediate any attempt to change attributes of a file +* (chmod, chown, chgrp, etc) +*/ +if (profile) + error =aa_attr (profile, dentry, mnt, iattr); + +aa_put_profile (profile); + } + +out: +return error; +} + +static int aa_xattr_permission (struct dentry *dentry,struct vfsmount *mnt, +const char *operation,int mask, +struct file *file) +{ +int error =0 ; + +if (mnt &&mediated_filesystem (dentry->d_inode)) { +struct aa_profile *profile =aa_get_profile (current); +int check = file ?AA_CHECK_FD :0 ; + +if (profile) + error =aa_perm_xattr (profile, operation, dentry, mnt, + mask, check); +aa_put_profile (profile); + } + +return error; +} + +static int apparmor_inode_setxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,void *value,size_t size, +int flags,struct file *file) +{ +return aa_xattr_permission (dentry, mnt,"xattr set" ,MAY_WRITE , file); +} + +static int apparmor_inode_getxattr (struct dentry *dentry,struct vfsmount *mnt, +char *name,struct file *file) +{ +return aa_xattr_permission (dentry, mnt,"xattr get" ,MAY_READ , file); +} + +static int apparmor_inode_listxattr (struct dentry *dentry,struct vfsmount *mnt, +struct file *file) +{ +return aa_xattr_permission (dentry, mnt,"xattr list" ,MAY_READ , file); +} + +static int apparmor_inode_removexattr (struct dentry *dentry, +struct vfsmount *mnt,char *name, +struct file *file) +{ +return aa_xattr_permission (dentry, mnt,"xattr remove" ,MAY_WRITE , + file); +} + +static int aa_file_permission (const char *op,struct file *file,int mask) +{ +struct aa_profile *profile; +struct aa_profile *file_profile = (struct aa_profile *)file->f_security; +int error =0 ; + +if (!file_profile) + goto out; + +/* +* If this file was opened under a different profile, we +* revalidate the access against the current profile. +*/ + profile =aa_get_profile (current); +if (profile && (file_profile != profile || mask &AA_MAY_LOCK )) { +struct dentry *dentry = file->f_dentry; +struct vfsmount *mnt = file->f_vfsmnt; +struct inode *inode = dentry->d_inode; +int check =AA_CHECK_FD ; + +/* +* FIXME: We should remember which profiles we revalidated +* against. +*/ +if (S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; + error =aa_permission (op, inode, dentry, mnt, mask, check); + } +aa_put_profile (profile); + +out: +return error; +} + +static int apparmor_file_permission (struct file *file,int mask) +{ +return aa_file_permission ("file_permission" , file, +aa_mask_permissions (mask)); +} + +static inline int apparmor_file_lock (struct file *file,unsigned int cmd) +{ +int mask =AA_MAY_LOCK ; +if (cmd ==F_WRLCK ) + mask |=MAY_WRITE ; +return aa_file_permission ("file_lock" , file, mask); +} + +static int apparmor_file_alloc_security (struct file *file) +{ +struct aa_profile *profile; + + profile =aa_get_profile (current); +if (profile) + file->f_security = profile; + +return 0 ; +} + +static void apparmor_file_free_security (struct file *file) +{ +struct aa_profile *file_profile = (struct aa_profile *)file->f_security; + +aa_put_profile (file_profile); +} + +static inline int aa_mmap (struct file *file,const char *operation, +unsigned long prot,unsigned long flags) +{ +struct dentry *dentry; +int mask =0 ; + +if (!file || !file->f_security) +return 0 ; + +if (prot &PROT_READ ) + mask |=MAY_READ ; +/* Private mappings don't require write perms since they don't +* write back to the files */ +if ((prot &PROT_WRITE ) && !(flags &MAP_PRIVATE )) + mask |=MAY_WRITE ; +if (prot &PROT_EXEC ) + mask |=AA_EXEC_MMAP ; + + dentry = file->f_dentry; +return aa_permission (operation, dentry->d_inode, dentry, + file->f_vfsmnt, mask,AA_CHECK_FD ); +} + +static int apparmor_file_mmap (struct file *file,unsigned long reqprot, +unsigned long prot,unsigned long flags, +unsigned long addr,unsigned long addr_only) +{ +return aa_mmap (file,"file_mmap" , prot, flags); +} + +static int apparmor_file_mprotect (struct vm_area_struct *vma, +unsigned long reqprot,unsigned long prot) +{ +return aa_mmap (vma->vm_file,"file_mprotect" , prot, + !(vma->vm_flags &VM_SHARED ) ?MAP_PRIVATE :0 ); +} + +static int apparmor_task_alloc_security (struct task_struct *task) +{ +return aa_clone (task); +} + +/* +* Called from IRQ context from RCU callback. +*/ +static void apparmor_task_free_security (struct task_struct *task) +{ +aa_release (task); +} + +static int apparmor_getprocattr (struct task_struct *task,char *name, +char **value) +{ +unsigned len; +int error; +struct aa_profile *profile; + +/* AppArmor only supports the "current" process attribute */ +if (strcmp (name,"current" ) !=0 ) +return -EINVAL ; + +/* must be task querying itself or admin */ +if (current != task && !capable (CAP_SYS_ADMIN )) +return -EPERM ; + + profile =aa_get_profile (task); + error =aa_getprocattr (profile, value, &len); +aa_put_profile (profile); +if (!error) + error = len; + +return error; +} + +static int apparmor_setprocattr (struct task_struct *task,char *name, +void *value,size_t size) +{ +char *command, *args; +int error; + +if (strcmp (name,"current" ) !=0 || size ==0 || size >=PAGE_SIZE ) +return -EINVAL ; + args = value; + args[size] ='\0' ; + args =strstrip (args); + command =strsep (&args," " ); +if (!args) +return -EINVAL ; +while (isspace (*args)) + args++; +if (!*args) +return -EINVAL ; + +if (strcmp (command,"changehat" ) ==0 ) { +if (current != task) +return -EACCES ; + error =aa_setprocattr_changehat (args); + }else if (strcmp (command,"changeprofile" ) ==0 ) { +if (current != task) +return -EACCES ; + error =aa_setprocattr_changeprofile (args); + }else if (strcmp (command,"setprofile" ) ==0 ) { +struct aa_profile *profile; + +/* Only an unconfined process with admin capabilities +* may change the profile of another task. +*/ + +if (!capable (CAP_SYS_ADMIN )) +return -EACCES ; + + profile =aa_get_profile (current); +if (profile) { +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.operation ="profile_set" ; + sa.gfp_mask =GFP_KERNEL ; + sa.task = task->pid; + sa.info ="from confined process" ; +aa_audit_reject (profile, &sa); +aa_put_profile (profile); +return -EACCES ; + } + error =aa_setprocattr_setprofile (task, args); + }else { +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.operation ="setprocattr" ; + sa.gfp_mask =GFP_KERNEL ; + sa.info ="invalid command" ; + sa.name = command; + sa.task = task->pid; +aa_audit_reject (NULL , &sa); +return -EINVAL ; + } + +if (!error) + error = size; +return error; +} + +struct security_operations apparmor_ops = { + .ptrace = apparmor_ptrace, + .capget = cap_capget, + .capset_check = cap_capset_check, + .capset_set = cap_capset_set, + .sysctl = apparmor_sysctl, + .capable = apparmor_capable, + .syslog = cap_syslog, + + .netlink_send = cap_netlink_send, + .netlink_recv = cap_netlink_recv, + + .bprm_apply_creds = cap_bprm_apply_creds, + .bprm_set_security = apparmor_bprm_set_security, + .bprm_secureexec = apparmor_bprm_secureexec, + + .sb_mount = apparmor_sb_mount, + .sb_umount = apparmor_umount, + + .inode_mkdir = apparmor_inode_mkdir, + .inode_rmdir = apparmor_inode_rmdir, + .inode_create = apparmor_inode_create, + .inode_link = apparmor_inode_link, + .inode_unlink = apparmor_inode_unlink, + .inode_symlink = apparmor_inode_symlink, + .inode_mknod = apparmor_inode_mknod, + .inode_rename = apparmor_inode_rename, + .inode_permission = apparmor_inode_permission, + .inode_setattr = apparmor_inode_setattr, + .inode_setxattr = apparmor_inode_setxattr, + .inode_getxattr = apparmor_inode_getxattr, + .inode_listxattr = apparmor_inode_listxattr, + .inode_removexattr = apparmor_inode_removexattr, + .file_permission = apparmor_file_permission, + .file_alloc_security = apparmor_file_alloc_security, + .file_free_security = apparmor_file_free_security, + .file_mmap = apparmor_file_mmap, + .file_mprotect = apparmor_file_mprotect, + .file_lock = apparmor_file_lock, + + .task_alloc_security = apparmor_task_alloc_security, + .task_free_security = apparmor_task_free_security, + .task_post_setuid = cap_task_post_setuid, + .task_reparent_to_init = cap_task_reparent_to_init, + + .getprocattr = apparmor_getprocattr, + .setprocattr = apparmor_setprocattr, +}; + +static void info_message (const char *str) +{ +struct aa_audit sa; +memset (&sa,0 ,sizeof (sa)); + sa.gfp_mask =GFP_KERNEL ; + sa.info = str; +printk (KERN_INFO "AppArmor: %s" , str); +aa_audit_message (NULL , &sa,AUDIT_APPARMOR_STATUS ); +} + +static int __init apparmor_init (void ) +{ +int error; + +if (!apparmor_enabled) { +info_message ("AppArmor disabled by boottime parameter\n" ); +return 0 ; + } + +if ((error =create_apparmorfs ())) { +AA_ERROR ("Unable to activate AppArmor filesystem\n" ); + goto createfs_out; + } + +if ((error =alloc_default_namespace ())){ +AA_ERROR ("Unable to allocate default profile namespace\n" ); + goto alloc_out; + } + +if ((error =register_security (&apparmor_ops))) { +AA_ERROR ("Unable to register AppArmor\n" ); + goto register_security_out; + } + +if (apparmor_complain) +info_message ("AppArmor initialized: complainmode enabled" ); +else +info_message ("AppArmor initialized" ); + +return error; + +register_security_out: +free_default_namespace (); + +alloc_out: +destroy_apparmorfs (); + +createfs_out: +return error; + +} + +module_init (apparmor_init); + +MODULE_DESCRIPTION ("AppArmor process confinement" ); +MODULE_AUTHOR ("Novell/Immunix, http://bugs.opensuse.org" ); +MODULE_LICENSE ("GPL" );
PATCH 39/45 AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Pathname matching, transition table loading, profile loading and manipulation.
--- security/apparmor/match.c | 273 +++++++++++ security/apparmor/match.h | 85 +++ security/apparmor/module_interface.c | 805 +++++++++++++++++++++++++++++++++++ 3 files changed, 1163 insertions(+)
security/apparmor/match.c new file +273 -0
@@ -0,0 +1,273 @@ +/* +* Copyright (C) 2007 Novell/SUSE +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation, version 2 of the +* License. +* +* Regular expression transition table matching +*/ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include "apparmor.h" +#include "match.h" + +static struct table_header *unpack_table (void *blob,size_t bsize) +{ +struct table_header *table =NULL ; +struct table_header th; +size_t tsize; + +if (bsize <sizeof (struct table_header )) + goto out; + + th.td_id =be16_to_cpu (*(u16 *) (blob)); + th.td_flags =be16_to_cpu (*(u16 *) (blob +2 )); + th.td_lolen =be32_to_cpu (*(u32 *) (blob +8 )); + blob +=sizeof (struct table_header ); + +if (!(th.td_flags ==YYTD_DATA16 || th.td_flags ==YYTD_DATA32 || + th.td_flags ==YYTD_DATA8 )) + goto out; + + tsize =table_size (th.td_lolen, th.td_flags); +if (bsize < tsize) + goto out; + + table =kmalloc (tsize,GFP_KERNEL ); +if (table) { + *table = th; +if (th.td_flags ==YYTD_DATA8 ) +UNPACK_ARRAY (table->td_data, blob, th.td_lolen, + u8, byte_to_byte); +else if (th.td_flags ==YYTD_DATA16 ) +UNPACK_ARRAY (table->td_data, blob, th.td_lolen, + u16, be16_to_cpu); +else +UNPACK_ARRAY (table->td_data, blob, th.td_lolen, + u32, be32_to_cpu); + } + +out: +return table; +} + +int unpack_dfa (struct aa_dfa *dfa,void *blob,size_t size) +{ +int hsize, i; +int error = -ENOMEM ; + +/* get dfa table set header */ +if (size <sizeof (struct table_set_header )) + goto fail; + +if (ntohl (*(u32 *)blob) !=YYTH_MAGIC ) + goto fail; + + hsize =ntohl (*(u32 *)(blob +4 )); +if (size < hsize) + goto fail; + + blob += hsize; + size -= hsize; + + error = -EPROTO ; +while (size >0 ) { +struct table_header *table; + table =unpack_table (blob, size); +if (!table) + goto fail; + +switch (table->td_id) { +case YYTD_ID_ACCEPT : +case YYTD_ID_BASE : + dfa->tables[table->td_id -1 ] = table; +if (table->td_flags !=YYTD_DATA32 ) + goto fail; +break ; +case YYTD_ID_DEF : +case YYTD_ID_NXT : +case YYTD_ID_CHK : + dfa->tables[table->td_id -1 ] = table; +if (table->td_flags !=YYTD_DATA16 ) + goto fail; +break ; +case YYTD_ID_EC : + dfa->tables[table->td_id -1 ] = table; +if (table->td_flags !=YYTD_DATA8 ) + goto fail; +break ; +default : +kfree (table); + goto fail; + } + + blob +=table_size (table->td_lolen, table->td_flags); + size -=table_size (table->td_lolen, table->td_flags); + } + +return 0 ; + +fail: +for (i =0 ; i <ARRAY_SIZE (dfa->tables); i++) { +if (dfa->tables[i]) { +kfree (dfa->tables[i]); + dfa->tables[i] =NULL ; + } + } +return error; +} + +/** +* verify_dfa - verify that all the transitions and states in the dfa tables +* are in bounds. +* @dfa: dfa to test +* +* assumes dfa has gone through the verification done by unpacking +*/ +int verify_dfa (struct aa_dfa *dfa) +{ +size_t i, state_count, trans_count; +int error = -EPROTO ; + +/* check that required tables exist */ +if (!(dfa->tables[YYTD_ID_ACCEPT -1 ] && + dfa->tables[YYTD_ID_DEF -1 ] && + dfa->tables[YYTD_ID_BASE -1 ] && + dfa->tables[YYTD_ID_NXT -1 ] && + dfa->tables[YYTD_ID_CHK -1 ])) + goto out; + +/* accept.size == default.size == base.size */ + state_count = dfa->tables[YYTD_ID_BASE -1 ]->td_lolen; +if (!(state_count == dfa->tables[YYTD_ID_DEF -1 ]->td_lolen && + state_count == dfa->tables[YYTD_ID_ACCEPT -1 ]->td_lolen)) + goto out; + +/* next.size == chk.size */ + trans_count = dfa->tables[YYTD_ID_NXT -1 ]->td_lolen; +if (trans_count != dfa->tables[YYTD_ID_CHK -1 ]->td_lolen) + goto out; + +/* if equivalence classes then its table size must be 256 */ +if (dfa->tables[YYTD_ID_EC -1 ] && + dfa->tables[YYTD_ID_EC -1 ]->td_lolen !=256 ) + goto out; + +for (i =0 ; i < state_count; i++) { +if (DEFAULT_TABLE (dfa)[i] >= state_count) + goto out; +if (BASE_TABLE (dfa)[i] >= trans_count +256 ) + goto out; + } + +for (i =0 ; i < trans_count ; i++) { +if (NEXT_TABLE (dfa)[i] >= state_count) + goto out; +if (CHECK_TABLE (dfa)[i] >= state_count) + goto out; + } + +/* verify accept permissions */ +for (i =0 ; i < state_count; i++) { +int mode =ACCEPT_TABLE (dfa)[i]; + +if (mode & ~AA_VALID_PERM_MASK ) { + goto out; + } + } + + error =0 ; +out: +return error; +} + +struct aa_dfa *aa_match_alloc (void ) +{ +return kzalloc (sizeof (struct aa_dfa ),GFP_KERNEL ); +} + +void aa_match_free (struct aa_dfa *dfa) +{ +if (dfa) { +int i; + +for (i =0 ; i <ARRAY_SIZE (dfa->tables); i++) +kfree (dfa->tables[i]); + } +kfree (dfa); +} + +/** +* aa_dfa_next_state - traverse @dfa to find state @str stops at +* @dfa: the dfa to match @str against +* @start: the state of the dfa to start matching in +* @str: the string to match against the dfa +* +* aa_dfa_next_state will match @str against the dfa and return the state it +* finished matching in. The final state can be used to look up the accepting +* label, or as the start state of a continuing match. +*/ +unsigned int aa_dfa_next_state (struct aa_dfa *dfa,unsigned int start, +const char *str) +{ +u16 *def =DEFAULT_TABLE (dfa); +u32 *base =BASE_TABLE (dfa); +u16 *next =NEXT_TABLE (dfa); +u16 *check =CHECK_TABLE (dfa); +unsigned int state = start, pos; + +if (state ==0 ) +return 0 ; + +/* current state is <state>, matching character *str */ +if (dfa->tables[YYTD_ID_EC -1 ]) { +u8 *equiv =EQUIV_TABLE (dfa); +while (*str) { + pos = base[state] + equiv[(u8)*str++]; +if (check[pos] == state) + state = next[pos]; +else + state = def[state]; + } + }else { +while (*str) { + pos = base[state] + (u8)*str++; +if (check[pos] == state) + state = next[pos]; +else + state = def[state]; + } + } +return state; +} + +/** +* aa_dfa_null_transition - step to next state after null character +* @dfa: the dfa to match against +* @start: the state of the dfa to start matching in +* +* aa_dfa_null_transition transitions to the next state after a null +* character which is not used in standard matching and is only +* used to seperate pairs. +*/ +unsigned int aa_dfa_null_transition (struct aa_dfa *dfa,unsigned int start) +{ +return aa_dfa_next_state (dfa, start,"//" ); +} + +/** +* aa_dfa_match - find accept perm for @str in @state +* @dfa: the dfa to match @str against +* @str: the path to match against the dfa +* +* aa_dfa_match will match @str and return the accept perms for the +* final state. +*/ +unsigned int aa_dfa_match (struct aa_dfa *dfa,const char *str) +{ +return ACCEPT_TABLE (dfa)[aa_dfa_next_state (dfa,DFA_START , str)]; +}
security/apparmor/match.h new file +85 -0
85 lines not on this page · raw message
security/apparmor/module_interface.c new file +805 -0
805 lines not on this page · raw message
PATCH 40/45 AppArmor: all the rest
In reply to [email protected]All the things that didn't nicely fit in a category on their own: kbuild code, declararions and inline functions, /sys/kernel/security/apparmor filesystem for controlling apparmor from user space, profile list functions, locking documentation, /proc/$pid/task/$tid/attr/current access.
--- security/apparmor/Kconfig | 9 + security/apparmor/Makefile | 13 + security/apparmor/apparmor.h | 326 +++++++++++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 252 +++++++++++++++++++++++++++++++ security/apparmor/inline.h | 257 ++++++++++++++++++++++++++++++++ security/apparmor/list.c | 156 +++++++++++++++++++ security/apparmor/locking.txt | 68 ++++++++ security/apparmor/procattr.c | 194 ++++++++++++++++++++++++ 8 files changed, 1275 insertions(+)
security/apparmor/Kconfig new file +9 -0
@@ -0,0 +1,9 @@ +config SECURITY_APPARMOR + tristate "AppArmor support" + depends on SECURITY!=n + help + This enables the AppArmor security module. + Required userspace tools (if they are not included in your + distribution) and further information may be found at + <http://forge.novell.com/modules/xfmod/project/?apparmor> + If you are unsure how to answer this question, answer N.
security/apparmor/Makefile new file +13 -0
@@ -0,0 +1,13 @@ +# Makefile for AppArmor Linux Security Module +# +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + +apparmor-y := main.o list.o procattr.o lsm.o apparmorfs.o \ + module_interface.o match.o + +quiet_cmd_make-caps = GEN $@ +cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ + +$(obj)/main.o : $(obj)/capability_names.h +$(obj)/capability_names.h : $(srctree)/include/linux/capability.h + $(call cmd,make-caps)
security/apparmor/apparmor.h new file +326 -0
326 lines not on this page · raw message
security/apparmor/apparmorfs.c new file +252 -0
252 lines not on this page · raw message
security/apparmor/inline.h new file +257 -0
257 lines not on this page · raw message
security/apparmor/list.c new file +156 -0
156 lines not on this page · raw message
security/apparmor/locking.txt new file +68 -0
68 lines not on this page · raw message
security/apparmor/procattr.c new file +194 -0
194 lines not on this page · raw message
PATCH 41/45 add simple network toggles to apparmor
In reply to [email protected]--- security/apparmor/Makefile | 7 + security/apparmor/apparmor.h | 7 + security/apparmor/lsm.c | 147 ++++++++++++++++++++++++++++++++++- security/apparmor/main.c | 96 ++++++++++++++++++++++ security/apparmor/module_interface.c | 20 ++++ 5 files changed, 273 insertions(+), 4 deletions(-)
security/apparmor/Makefile +6 -1
@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l quiet_cmd_make-caps = GEN $@ cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ -$(obj)/main.o : $(obj)/capability_names.h +quiet_cmd_make-af = GEN $@ +cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@ + +$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h $(obj)/capability_names.h : $(srctree)/include/linux/capability.h $(call cmd,make-caps) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af)
security/apparmor/apparmor.h +7 -0
@@ -16,6 +16,8 @@#include <linux/fs.h> #include <linux/binfmts.h> #include <linux/rcupdate.h> +#include <linux/socket.h> +#include <net/sock.h> /* * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
@@ -174,6 +176,7 @@ struct aa_profile {struct list_head task_contexts;spinlock_t lock;unsigned long int_flags; +u16 network_families[AF_MAX ]; };extern struct list_head profile_ns_list;
@@ -220,6 +223,7 @@ struct aa_audit {int request_mask, denied_mask;struct iattr *iattr;pid_t task, parent; +int family, type, protocol;int error_code; };
@@ -281,6 +285,9 @@ extern void aa_change_task_context(struc struct aa_profile *previous_profile);extern int aa_may_ptrace (struct aa_task_context *cxt,struct aa_profile *tracee); +extern int aa_net_perm (struct aa_profile *profile,char *operation, +int family,int type,int protocol); +extern int aa_revalidate_sk (struct sock *sk,char *operation);/* list.c */ extern struct aa_namespace *__aa_find_namespace(const char *name,
security/apparmor/lsm.c +144 -3
171 lines not on this page · raw message
security/apparmor/main.c +96 -0
120 lines not on this page · raw message
security/apparmor/module_interface.c +20 -0
@@ -283,6 +283,8 @@ static struct aa_profile *aa_unpack_prof {struct aa_profile *profile =NULL ;struct aa_audit sa; +size_t size =0 ; +int i;int error = -EPROTO ;
@@ -311,6 +313,24 @@ static struct aa_profile *aa_unpack_profif (!aa_is_u32 (e, &(profile->capabilities),NULL )) goto fail; + size =aa_is_array (e,"net_allowed_af" ); +if (size) { +if (size >AF_MAX ) + goto fail; + +for (i =0 ; i < size; i++) { +if (!aa_is_u16 (e, &profile->network_families[i],NULL )) + goto fail; + } +if (!aa_is_nameX (e,AA_ARRAYEND ,NULL )) + goto fail; +/* allow unix domain and netlink sockets they are handled +* by IPC +*/ + } + profile->network_families[AF_UNIX ] =0xffff ; + profile->network_families[AF_NETLINK ] =0xffff ; +/* get file rules */ profile->file_rules =aa_unpack_dfa (e);if (IS_ERR (profile->file_rules)) {
PATCH 42/45 Add AppArmor LSM to security/Makefile
In reply to [email protected]--- security/Kconfig | 1 + security/Makefile | 1 + security/apparmor/Kconfig | 21 +++++++++++++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-)
security/Kconfig +1 -0
@@ -104,6 +104,7 @@ config SECURITY_ROOTPLUG If you are unsure how to answer this question, answer N. source security/selinux/Kconfig +source security/apparmor/Kconfig endmenu
security/Makefile +1 -0
@@ -14,5 +14,6 @@ endif obj-$(CONFIG_SECURITY) += security.o dummy.o inode.o # Must precede capability.o in order to stack properly. obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o +obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/ obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
security/apparmor/Kconfig +19 -2
@@ -1,9 +1,26 @@ config SECURITY_APPARMOR - tristate "AppArmor support" - depends on SECURITY!=n + bool "AppArmor support" + depends on SECURITY + select AUDIT help This enables the AppArmor security module. Required userspace tools (if they are not included in your distribution) and further information may be found at <http://forge.novell.com/modules/xfmod/project/?apparmor> + If you are unsure how to answer this question, answer N. + +config SECURITY_APPARMOR_BOOTPARAM_VALUE + int "AppArmor boot parameter default value" + depends on SECURITY_APPARMOR + range 0 1 + default 1 + help + This option sets the default value for the kernel parameter + 'apparmor', which controls whether AppArmor is enabled at + boot. If this option is set to 0 (zero), AppArmor will + be disabled by default, requiring the kernel parameter + be set to 1 (one) to enable it. If this option is + set to 1 (one), AppArmor will be enabled by default. + + If you are unsure how to answer this question, answer 1.
PATCH 43/45 Switch to vfs_permission() in do_path_lookup()
In reply to [email protected]Switch from file_permission() to vfs_permission() in do_path_lookup():
this avoids calling permission() with a NULL nameidata here.
--- fs/namei.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-)
fs/namei.c +6 -7
@@ -1148,25 +1148,24 @@ static int fastcall do_path_lookup(int d nd->dentry =dget (fs->pwd);read_unlock (&fs->lock); }else { -struct dentry *dentry; - file =fget_light (dfd, &fput_needed); retval = -EBADF ;if (!file) goto out_fail; - dentry = file->f_path.dentry; + nd->dentry = file->f_path.dentry; + nd->mnt = file->f_path.mnt; retval = -ENOTDIR ; -if (!S_ISDIR (dentry->d_inode->i_mode)) +if (!S_ISDIR (nd->dentry->d_inode->i_mode)) goto fput_fail; - retval =file_permission (file,MAY_EXEC ); + retval =vfs_permission (nd,MAY_EXEC );if (retval) goto fput_fail; - nd->mnt =mntget (file->f_path.mnt); - nd->dentry =dget (dentry); +mntget (nd->mnt); +dget (nd->dentry);fput_light (file, fput_needed); }
PATCH 44/45 Switch to vfs_permission() in sys_fchdir()
In reply to [email protected]Switch from file_permission() to vfs_permission() in sys_fchdir(): this avoids calling permission() with a NULL nameidata here.
--- fs/open.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-)
fs/open.c +7 -9
@@ -512,10 +512,8 @@ out:asmlinkage long sys_fchdir (unsigned int fd) { +struct nameidata nd;struct file *file; -struct dentry *dentry; -struct inode *inode; -struct vfsmount *mnt;int error; error = -EBADF ;
@@ -523,17 +521,17 @@ asmlinkage long sys_fchdir(unsigned intif (!file) goto out; - dentry = file->f_path.dentry; - mnt = file->f_path.mnt; - inode = dentry->d_inode; + nd.dentry = file->f_path.dentry; + nd.mnt = file->f_path.mnt; + nd.flags =0 ; error = -ENOTDIR ; -if (!S_ISDIR (inode->i_mode)) +if (!S_ISDIR (nd.dentry->d_inode->i_mode)) goto out_putf; - error =file_permission (file,MAY_EXEC ); + error =vfs_permission (&nd,MAY_EXEC );if (!error) -set_fs_pwd (current->fs, mnt, dentry); +set_fs_pwd (current->fs, nd.mnt, nd.dentry); out_putf:fput (file); out:
PATCH 45/45 Fix file_permission()
In reply to [email protected]We cannot easily switch from file_permission() to vfs_permission() everywhere, so fix file_permission() to not use a NULL nameidata for the remaining users.
--- fs/namei.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
fs/namei.c +7 -1
@@ -313,7 +313,13 @@ int vfs_permission(struct nameidata *nd,*/ int file_permission (struct file *file,int mask) { -return permission (file->f_path.dentry->d_inode, mask,NULL ); +struct nameidata nd; + + nd.dentry = file->f_path.dentry; + nd.mnt = file->f_path.mnt; + nd.flags =LOOKUP_ACCESS ; + +return permission (nd.dentry->d_inode, mask, &nd); }/*
On Thu, Oct 25, 2007 at 11:40:24PM -0700, [email protected] wrote:
Sorry this got dropped some how.
This submission of the AppArmor security module is based against -mm.
Any comments and feedback to improve implementation are appreciated.
The patch series consists of five areas:
(1) Pass struct vfsmount through to LSM hooks.
(2) Fixes and improvements to __d_path():
(a) make it unambiguous and exclude unreachable paths from /proc/mounts,
(b) make its result consistent in the face of remounts,
(c) introduce d_namespace_path(), a variant of d_path that goes up to the namespace root instead of the chroot.
(d) the behavior of d_path() and getcwd() remain unchanged, and there is no hidding of unreachable paths in /proc/mounts. The patches addressing these have been seperated from the AppArmor submission and will be introduced at a later date.
Part (a) has been in the -mm tree for a while; this series includes an updated copy of the -mm patch. Parts (b) and (c) shouldn't be too controversial.
(3) Be able to distinguish file descriptor access from access by name in LSM hooks.
Applications expect different behavior from file descriptor accesses and accesses by name in some cases. We need to pass this information down the LSM hooks to allow AppArmor to tell which is which.
(4) Convert the selinux sysctl pathname computation code into a standalone function.
(5) The AppArmor LSM itself.
(See below.)
A tarball of the kernel patches, base user-space utilities, example profiles, and technical documentation (including a walk-through) are available at:
http://forgeftp.novell.com/apparmor/LKML_Submission-Oct-07/
Only the most recent features are covered in brief here for a more complete explaination please refere to the technical documentation.
Changes since previous submission
- fix wrong error code for failed pathname
- fix change_profile ref counting bug
- fix change_hat missing mandatory profile bug
- file rules can now be specified in permission first order
- add append permission which is subset of write permission
- add lock mediation for finer grained control, previously locking only required access right to a file
- added simple Network toggles for course network mediation
- added profile namespaces (currently only available through change_profile)
- added DAC style permissions
- added the ability to specify hard link rules using location pairs
- added per profile namespace default profile
- added pix transition mode
- builtin only
Outstanding Issues
- use of d_namespace_path and buffer allocation to obtain a pathname for mediation.
- conditional passing of the vfsmnt. This can be addressed by rebasing on the lookup intent patches but that has not been done for this submission.
- ipc and signal mediation are a wip and not included.
- fine grained network mediation
- system confinement from boot is a wip and not included.
- documentation needs to be updated to include newest features
Explanation of new features
The user side policy parser now support specifying file rules with permissions specified before the path expression; the old syntax is still supported.
eg.
r /etc/shadow,
Profile Namespaces
AppArmor now allows for profile sets to exist in seperate namespaces. This is the first step in allowing AppArmor to have different policy, per container.
Profile namespaces currently can only be set through change_profile. Confined tasks inherit the namespace of their parent.
User, Group, Other permission masks
AppArmor now allows file permissions to be specified at the user, group, and other level similar to DAC. For each permission set of user, group, other the full AppArmor permission set (rwaxlmk) are provided. The permission group to apply is determined using the fsuid.
The permission sets are seperated using the : character. This deviates from dac but the permission sets are wider, and do not have a set order. If any distinct user:group:other permissions are being expressed then all of the user:group:other permissions for the rule must be expressed. That is to say just writing rw, does not provide user rw, and rw:r does not provide user and group permissions.
eg.
/foo rw:r:r, # give user rw, group and other r
/foo rw::, # give user rw, no permissions to group and other
/foo :r:r, # give group and other read permissions
/foo ::r, # give other read permissions Traditional AppArmor rules are still supported user side and are mapped to the same permission in each of user, group and other. So
/foo rwpx,
is the same as
/foo rwpx:rwpx:rwpx,
For a given rule user, group, other must use the same exec qualifier. Multiple rules can be used to specify different exec qualifiers for each of user, group, or other for a given match.
eg.
/bin/foo px::,
/bin/foo :ix:ix,The link permission is determined by the target files ownership this allows for writting rules that enforce openwall style link restrictions.
/** l::, #allow linking to any file owned by the user
The user, group, other permission masks can be written in a permission first manner as well.
Link rules
Dedicated link rules using source and destination have been added, to allow for tighter control of hard links when necessary. eg.
link /linkname -> /targetname,
if user:group:other link specification is desired then a user:group:other mask containing only the link perm in the appropriate positions can be specified. The permission group to apply is determined by the target and the fsuid.
eg.
link l:: /linkname -> /targetname, # allow if /targetname owned by user
or
l:: /linkname -> /targetname, Both the linkname and the target support full AppArmor globbing.
link l:: /** -> /**, # allow any link to target owned by user
Traditional AA style links are still supported and are mapped by the parser into the newer link pair for the kernel.
/linkname rwl,
is mapped to
link /linkname -> /**,
Quoted 7 lines
Signature · 5 lines
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
On Thu, 25 Oct 2007 23:40:24 -0700
[email protected] wrote:
before going into the LSM / security side of things, I'd like to get the VFS guys to look at your VFS interaction code.
In addition, I'd like to ask you to put a file in Documentation/ somewhere that describes what AppArmor is intended security protection is (it's different from SELinux for sure for example); by having such a document for each LSM user, end users and distros can make a more informed decision which module suits their requirements... and it also makes it possible to look at the implementation to see if it has gaps to the intent, without getting into a pissing contest about which security model is better; but unless the security goals are explicitly described that's a trap that will keep coming back... so please spend some time on getting a good description going here..
Signature · 3 lines
If you want to reach me at my work email, use [email protected] For development, discussion and tips for power savings, visit http://www.lesswatts.org
Quoted 6 lines · Arjan van de Ven
On Thu, 25 Oct 2007 23:40:24 -0700
[email protected] wrote:before going into the LSM / security side of things, I'd like to get the VFS guys to look at your VFS interaction code.
yes, the vfs interaction definitely need their review.
Quoted 11 lines
In addition, I'd like to ask you to put a file in
Documentation/somewhere that describes what AppArmor is intended security protection is (it's different from SELinux for sure for example); by having such a document for each LSM user, end users and distros can make a more informed decision which module suits their requirements... and it also makes it possible to look at the implementation to see if it has gaps to the intent, without getting into a pissing contest about which security model is better; but unless the security goals are explicitly described that's a trap that will keep coming back... so please spend some time on getting a good description going here..
yes this is needed and a good idea in general
thanks
john
Quoted 21 lines · On Fri, 26 Oct 2007 11:34:48 -0700 John Johansen <[email protected]> wrote:
Quoted 6 lines · Arjan van de Ven
On Thu, 25 Oct 2007 23:40:24 -0700
[email protected] wrote:before going into the LSM / security side of things, I'd like to get the VFS guys to look at your VFS interaction code.
yes, the vfs interaction definitely need their review.
Quoted 12 lines
In addition, I'd like to ask you to put a file in
Documentation/somewhere that describes what AppArmor is intended security protection is (it's different from SELinux for sure for example); by having such a document for each LSM user, end users and distros can make a more informed decision which module suits their requirements... and it also makes it possible to look at the implementation to see if it has gaps to the intent, without getting into a pissing contest about which security model is better; but unless the security goals are explicitly described that's a trap that will keep coming back... so please spend some time on getting a good description going here..
Quoted 2 lines
yes this is needed and a good idea in general
would you mind posting your first stab at this to the list shortly, because without that it's nearly impossible to review your patchkit in a sensible way...
Quoted 10 lines · Arjan van de Ven
In addition, I'd like to ask you to put a file in
Documentation/somewhere that describes what AppArmor is intended security protection is (it's different from SELinux for sure for example); by having such a document for each LSM user, end users and distros can make a more informed decision which module suits their requirements... and it also makes it possible to look at the implementation to see if it has gaps to the intent, without getting into a pissing contest about which security model is better; but unless the security goals are explicitly described that's a trap that will keep coming back... so please spend some time on getting a good description going here..
Hmm, I agree that it makes sense to give a short overview of each LSM. A description of the AppArmor model and implementation can be found in the directory that John referred to actually. I'm unsure how much of that makes sense under Documentation/ -- what do you think?
http://forgeftp.novell.com/apparmor/LKML_Submission-Oct-07/techdoc.pdf
I guess actual end user information doesn't belong in the kernel sources; that really seems wrong.
Thanks,
Andreas
Quoted 18 lines · On Fri, 26 Oct 2007 22:44:56 +0200 Andreas Gruenbacher <[email protected]> wrote:
Quoted 11 lines · Arjan van de Ven
In addition, I'd like to ask you to put a file in
Documentation/somewhere that describes what AppArmor is intended security protection is (it's different from SELinux for sure for example); by having such a document for each LSM user, end users and distros can make a more informed decision which module suits their requirements... and it also makes it possible to look at the implementation to see if it has gaps to the intent, without getting into a pissing contest about which security model is better; but unless the security goals are explicitly described that's a trap that will keep coming back... so please spend some time on getting a good description going here..Hmm, I agree that it makes sense to give a short overview of each LSM. A description of the AppArmor model and implementation can be found in the directory that John referred to actually. I'm unsure how much of that makes sense under
Documentation/-- what do you think?
Quoted 5 lines
http://forgeftp.novell.com/apparmor/LKML_Submission-Oct-07/techdoc.pdf
I guess actual end user information doesn't belong in the kernel sources; that really seems wrong.
My main concern for now is a description of what it tries to protect against/in what cases you would expect to use it. THe reason for asking this explicitly is simple: Until now the LSM discussions always ended up in a nasty mixed up mess around disagreeing on the theoretical model of what to protect against and the actual implementation of the threat protection. THe only way I can think of to get out of this mess is to have the submitter of the security model give a description of what his protection model is (and unless it's silly, not argue about that), and then only focus on how the code manages to achieve this model, to make sure there's no big gaps in it, within its own goals/reference.
On the first part (discussion of the model) I doubt we can get people to agree, that's pretty much phylosophical... on the second part (how well the code/design lives up to its own goals) the analysis can be objective and technical.
Signature · 3 lines
If you want to reach me at my work email, use [email protected] For development, discussion and tips for power savings, visit http://www.lesswatts.org
Quoted 2 lines · Arjan van de Ven
My main concern for now is a description of what it tries to protect against/in what cases you would expect to use it.
Okay, I see what you mean. Thanks.
Quoted 11 lines · Arjan van de Ven
My main concern for now is a description of what it tries to protect against/in what cases you would expect to use it. THe reason for asking this explicitly is simple: Until now the LSM discussions always ended up in a nasty mixed up mess around disagreeing on the theoretical model of what to protect against and the actual implementation of the threat protection. THe only way I can think of to get out of this mess is to have the submitter of the security model give a description of what his protection model is (and unless it's silly, not argue about that), and then only focus on how the code manages to achieve this model, to make sure there's no big gaps in it, within its own goals/reference.
I really, really like this proposal. It is essentially what I have always wanted.
Quoted 5 lines
On the first part (discussion of the model) I doubt we can get people to agree, that's pretty much phylosophical... on the second part (how well the code/design lives up to its own goals) the analysis can be objective and technical.
I will try to do that as soon as possible. While I will strive to be both clear and precise, achieving both is challenging. So, if someone discovers a mis-match between the description and the code, would a patch to the description be an acceptable resolution, if it did not render the model silly?
Crispin
Signature · 2 lines
Crispin Cowan, Ph.D. http://mercenarylinux.com/ Itanium. Vista. GPLv3. Complexity at work
Quoted 6 lines · On Fri, 26 Oct 2007 15:16:53 -0700 Crispin Cowan <[email protected]>…
Quoted 6 lines
On the first part (discussion of the model) I doubt we can get people to agree, that's pretty much phylosophical... on the second part (how well the code/design lives up to its own goals) the analysis can be objective and technical.
Quoted 6 lines
I will try to do that as soon as possible. While I will strive to be both clear and precise, achieving both is challenging. So, if someone discovers a mis-match between the description and the code, would a patch to the description be an acceptable resolution, if it did not render the model silly?
I think it's entirely reasonable that if it turns out that the code can't do a certain aspect of the envisioned security (eg not just a code bug but a design level issue), the answer is to adjust the vision...
Signature · 3 lines
If you want to reach me at my work email, use [email protected] For development, discussion and tips for power savings, visit http://www.lesswatts.org
Quoted 2 lines · Arjan van de Ven
before going into the LSM / security side of things, I'd like to get the VFS guys to look at your VFS interaction code.
It's been NACKed a few times, and just reposting it won't help.
Quoted 5 lines · Christoph Hellwig
Quoted 2 lines · Arjan van de Ven
before going into the LSM / security side of things, I'd like to get the VFS guys to look at your VFS interaction code.
It's been NACKed a few times, and just reposting it won't help.
Let me repeat what I have said before in another context. The past discussions on linux-kernel were useful for a while, and we got constructive feedback which we could act upon, but then the feedback became very non-constructive again, and you NACKed patches without giving any good reasons. I have asked for specific feedback but didn't get any in:
- Rejecting the vfsmount additions,
http://marc.info/?l=linux-kernel&m=118350187712375
- VFS layering fuck-up accusation,
http://marc.info/?l=linux-kernel&m=118348050804600
So can we pick up things there again, and have a real discussion about the things you criticize?
Thanks,
Andreas