AppArmor security module overview
Subsystems (20)
cover AppArmor security module overview
lkml-explanatory.txt
PATCH 4/45 Pass struct vfsmount to the inode_setattr LSM hook
This is needed for computing pathnames in the AppArmor LSM.
--- fs/attr.c | 4 ++-- include/linux/security.h | 8 ++++++-- security/dummy.c | 3 ++- security/selinux/hooks.c | 5 +++-- 4 files changed, 13 insertions(+), 7 deletions(-)
fs/attr.c +2 -2
@@ -144,13 +144,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
@@ -358,6 +358,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:
@@ -1221,7 +1222,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,
@@ -1708,11 +1710,12 @@ static inline int security_inode_permiss }static inline 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); } static inline int security_inode_getattr (struct vfsmount *mnt,
@@ -2412,6 +2415,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
@@ -328,7 +328,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/selinux/hooks.c +3 -2
@@ -2267,11 +2267,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 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/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-)
fs/namei.c +1 -1
@@ -1946,7 +1946,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
@@ -308,6 +308,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:
@@ -1213,7 +1214,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);
@@ -1650,11 +1652,12 @@ static inline int security_inode_symlinkstatic inline 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); } static inline int security_inode_rmdir (struct inode *dir,
@@ -2371,6 +2374,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
@@ -288,7 +288,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/selinux/hooks.c +2 -1
@@ -2207,7 +2207,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
@@ -560,11 +560,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
@@ -1855,7 +1855,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 );
@@ -1907,11 +1908,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 ;case S_IFDIR : error = -EPERM ;
fs/nfsd/vfs.c +2 -1
@@ -1194,7 +1194,8 @@ nfsd_create(struct svc_rqst *rqstp, strucase S_IFBLK :case S_IFIFO :case S_IFSOCK : - host_err =vfs_mknod (dirp, dchild, iap->ia_mode, rdev); + host_err =vfs_mknod (dirp, dchild, exp->ex_mnt, iap->ia_mode, + rdev);break ;default :printk ("nfsd: bad file type %o in nfsd_create\n" , type);
include/linux/fs.h +1 -1
@@ -993,7 +993,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
@@ -807,7 +807,7 @@ static int unix_bind(struct socket *sock*/ mode =S_IFSOCK | (SOCK_INODE (sock)->i_mode & ~current->fs->umask); - err =vfs_mknod (nd.dentry->d_inode, dentry, mode,0 ); + err =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, mode,0 );if (err) goto out_mknod_dput;mutex_unlock (&nd.dentry->d_inode->i_mutex);
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
@@ -470,6 +470,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;
@@ -478,6 +479,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,
@@ -487,7 +489,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
@@ -2203,7 +2203,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 );
@@ -2249,7 +2250,8 @@ asmlinkage long sys_symlinkat(const charif (IS_ERR (dentry)) goto out_unlock; - error =vfs_symlink (nd.dentry->d_inode, dentry, from,S_IALLUGO ); + error =vfs_symlink (nd.dentry->d_inode, dentry, nd.mnt, from, +S_IALLUGO );dput (dentry); out_unlock:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/vfs.c +8 -4
@@ -1436,6 +1436,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;
@@ -1462,6 +1463,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 )
@@ -1469,20 +1471,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
@@ -994,7 +994,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 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 | 18 +++++++++++++----- security/dummy.c | 6 ++++-- security/selinux/hooks.c | 9 +++++++-- 4 files changed, 26 insertions(+), 10 deletions(-)
fs/namei.c +2 -1
@@ -2293,7 +2293,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 +13 -5
@@ -289,8 +289,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.
@@ -1212,8 +1214,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);
@@ -1628,12 +1631,15 @@ 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) {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); } static inline int security_inode_unlink (struct inode *dir,
@@ -2359,8 +2365,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
@@ -270,8 +270,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/selinux/hooks.c +7 -2
@@ -2182,11 +2182,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
@@ -542,14 +542,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
@@ -2024,7 +2024,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 );
@@ -2088,7 +2088,7 @@ static long do_rmdir(int dfd, const char error =PTR_ERR (dentry);if (IS_ERR (dentry)) goto exit2; - error =vfs_rmdir (nd.dentry->d_inode, dentry); + error =vfs_rmdir (nd.dentry->d_inode, dentry, nd.mnt);dput (dentry); exit2:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/nfs4recover.c +1 -1
@@ -276,7 +276,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
@@ -1666,6 +1666,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;
@@ -1680,6 +1681,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);
@@ -1697,21 +1699,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
@@ -775,7 +775,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
@@ -996,7 +996,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 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 | 12 ++++++++---- security/dummy.c | 3 ++- security/selinux/hooks.c | 5 +++-- 4 files changed, 14 insertions(+), 8 deletions(-)
fs/namei.c +1 -1
@@ -2121,7 +2121,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 +8 -4
@@ -298,6 +298,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.
@@ -1218,7 +1219,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,
@@ -1645,11 +1647,12 @@ 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) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_unlink (dir, dentry); +return security_ops->inode_unlink (dir, dentry, mnt); } static inline int security_inode_symlink (struct inode *dir,
@@ -2377,7 +2380,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
@@ -278,7 +278,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/selinux/hooks.c +3 -2
@@ -2197,11 +2197,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
@@ -598,19 +598,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,NULL );
fs/namei.c +12 -7
@@ -2401,7 +2401,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;
@@ -2444,7 +2445,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;
@@ -2472,7 +2474,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);
@@ -2501,9 +2504,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,
@@ -2575,8 +2580,8 @@ static int do_rename(int olddfd, const cif (new_dentry == trap) 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); exit5:dput (new_dentry); exit4:
fs/nfsd/vfs.c +2 -1
@@ -1630,7 +1630,8 @@ nfsd_rename(struct svc_rqst *rqstp, stru host_err = -EPERM ; } else#endif - 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
@@ -998,7 +998,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.
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
@@ -143,18 +143,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 ; }
@@ -362,7 +364,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 ;
@@ -375,7 +378,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 ;
@@ -397,7 +400,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; }
@@ -411,7 +414,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; }
@@ -426,7 +429,7 @@ sys_flistxattr(int fd, char __user *listif (!f)return error;audit_inode (NULL , f->f_path.dentry->d_inode); - 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 | 12 +++++++----- security/dummy.c | 2 +- security/selinux/hooks.c | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-)
fs/xattr.c +1 -1
@@ -149,7 +149,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 +7 -5
@@ -395,7 +395,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
@@ -1250,7 +1250,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);const char *(*inode_xattr_getsuffix) (void );int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);
@@ -1791,11 +1791,12 @@ static inline int security_inode_getxattreturn security_ops->inode_getxattr (dentry, mnt, name); } -static inline int security_inode_listxattr (struct dentry *dentry) +static inline 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); }static inline int security_inode_removexattr (struct dentry *dentry,char *name)
@@ -2495,7 +2496,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
@@ -374,7 +374,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/selinux/hooks.c +1 -1
@@ -2399,7 +2399,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
@@ -1957,6 +1957,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 ;
@@ -1989,14 +1990,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
@@ -165,7 +165,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;
@@ -438,7 +438,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 ];
@@ -449,7 +449,7 @@ removexattr(struct dentry *d, char __useif (error <0 )return error; -return vfs_removexattr (d, kname); +return vfs_removexattr (dentry, mnt, kname); }asmlinkage long
@@ -461,7 +461,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; }
@@ -475,7 +475,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; }
@@ -492,7 +492,7 @@ sys_fremovexattr(int fd, char __user *nareturn error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - 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 | 15 +++++++++------ security/commoncap.c | 3 ++- security/dummy.c | 3 ++- security/selinux/hooks.c | 3 ++- 5 files changed, 16 insertions(+), 10 deletions(-)
fs/xattr.c +1 -1
@@ -177,7 +177,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 -6
@@ -50,7 +50,7 @@ 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_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);extern void cap_task_reparent_to_init (struct task_struct *p);extern int cap_syslog (int type);
@@ -1251,7 +1251,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);const char *(*inode_xattr_getsuffix) (void );int (*inode_getsecurity)(const struct inode *inode,const char *name,void *buffer,size_t size,int err);int (*inode_setsecurity)(struct inode *inode,const char *name,const void *value,size_t size,int flags);
@@ -1799,11 +1800,12 @@ static inline int security_inode_listxatreturn security_ops->inode_listxattr (dentry, mnt); } -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) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_removexattr (dentry, name); +return security_ops->inode_removexattr (dentry, mnt, name); }static inline const char *security_inode_xattr_getsuffix (void )
@@ -2502,9 +2504,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 const char *security_inode_xattr_getsuffix (void )
security/commoncap.c +2 -1
@@ -200,7 +200,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 (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/dummy.c +2 -1
@@ -379,7 +379,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/selinux/hooks.c +2 -1
@@ -2404,7 +2404,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)) { if (!strncmp(name, XATTR_SECURITY_PREFIX,
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
@@ -1761,52 +1761,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);
@@ -1820,33 +1819,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;
@@ -1866,9 +1904,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;
@@ -1915,10 +1952,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 ;
@@ -1930,29 +1967,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 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
@@ -963,6 +963,8 @@ extern int proc_doulongvec_minmax(ctl_taextern int proc_doulongvec_ms_jiffies_minmax (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
@@ -1110,6 +1110,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
@@ -1427,40 +1427,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;
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
@@ -1409,6 +1409,10 @@ static int may_delete(struct inode *dir,BUG_ON (victim->d_parent->d_inode != dir);audit_inode_child (victim->d_name.name, victim->d_inode, dir); +#if 0 +if (nd) + nd->flags |=LOOKUP_CONTINUE ; +#endif error =permission (dir,MAY_WRITE |MAY_EXEC ,NULL );if (error)return error;
@@ -1446,6 +1450,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); }
PATCH 38/45 AppArmor: Module and LSM hooks
In reply to [email protected]Module parameters, LSM hooks, initialization and teardown.
security/apparmor/lsm.c new file +790 -0
@@ -0,0 +1,790 @@ +/* +* 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 "apparmor.h" +#include "inline.h" + +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; +struct aa_task_context *child_cxt; +struct aa_profile *child_profile; +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); + child_cxt =aa_task_context (child); + child_profile = child_cxt ? child_cxt->profile :NULL ; +if (cxt && (parent->nsproxy != child->nsproxy)) { +aa_audit_message (NULL ,GFP_ATOMIC ,"REJECTING ptrace across " +"namespace of %d by %d" , + parent->pid, child->pid); + error = -EPERM ; + }else { + error =aa_may_ptrace (cxt, child_profile); +if (cxt &&PROFILE_COMPLAIN (cxt->profile)) { +aa_audit_message (cxt->profile,GFP_ATOMIC , +"LOGPROF-HINT ptrace pid=%d child=%d " +"(%d profile %s active %s)" , + current->pid, child->pid, current->pid, + cxt->profile->parent->name, + cxt->profile->name); + } + } +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, name, mask); + } +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, dentry, mnt,"mkdir" ,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, dentry, mnt,"rmdir" ,MAY_WRITE ); + +aa_put_profile (profile); + +out: +return error; +} + +static int aa_permission (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, dentry, mnt, mask, check); +aa_put_profile (profile); + } +return error; +} + +static int apparmor_inode_create (struct inode *dir,struct dentry *dentry, +struct vfsmount *mnt,int mask) +{ +return aa_permission (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 (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 (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 (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, old_dentry, old_mnt, +MAY_READ |MAY_WRITE , check); + +if (!error && new_mnt) { + error =aa_perm (profile, 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 &= (MAY_READ |MAY_WRITE |MAY_EXEC ); +if (S_ISDIR (inode->i_mode)) { + check |=AA_CHECK_DIR ; +/* allow traverse accesses to directories */ + mask &= ~MAY_EXEC ; + } +return aa_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, dentry, mnt, operation, + 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 apparmor_file_permission (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) { +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 ; + mask &= (MAY_READ |MAY_WRITE |MAY_EXEC ); + error =aa_permission (inode, dentry, mnt, mask, check); + } +aa_put_profile (profile); + +out: +return error; +} + +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,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 (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) +{ +return aa_mmap (file, prot, flags); +} + +static int apparmor_file_mprotect (struct vm_area_struct *vma, +unsigned long reqprot,unsigned long prot) +{ +return aa_mmap (vma->vm_file, 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,"setprofile" )) { +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) { +aa_put_profile (profile); +aa_audit_message (NULL ,GFP_KERNEL ,"Attempt by " +"confined task %d [user %d] to " +"assign profile to task %d" , + current->pid, current->uid, + task->pid); +return -EACCES ; + } + error =aa_setprocattr_setprofile (task, args); + }else { +AA_ERROR ("Unknown setprocattr command '%.*s' " +"by task %d [user %d] for task %d" , + size <16 ? (int )size :16 , + command, + current->pid, + current->uid, + task->pid); + error = -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, + + .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) +{ +printk (KERN_INFO "AppArmor: %s" , str); +aa_audit_message (NULL ,GFP_KERNEL ,"%s" , str); +} + +static int __init apparmor_init (void ) +{ +int error; + +if ((error =create_apparmorfs ())) { +AA_ERROR ("Unable to activate AppArmor filesystem\n" ); + goto createfs_out; + } + +if ((error =alloc_null_complain_profile ())){ +AA_ERROR ("Unable to allocate null complain profile\n" ); + goto alloc_out; + } + +if ((error =register_security (&apparmor_ops))) { +AA_ERROR ("Unable to load 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_null_complain_profile (); + +alloc_out: +destroy_apparmorfs (); + +createfs_out: +return error; + +} + +static void __exit apparmor_exit(void) +{ +/* Remove and release all the profiles on the profile list. */ +mutex_lock (&aa_interface_lock); +write_lock (&profile_list_lock); +while (!list_empty (&profile_list)) { +struct aa_profile *profile = + list_entry(profile_list.next, struct aa_profile, list); + +/* Remove the profile from each task context it is on. */ +lock_profile (profile); + profile->isstale =1 ; +aa_unconfine_tasks (profile); +unlock_profile (profile); + +/* Release the profile itself. */ +list_del_init (&profile->list); +aa_put_profile (profile); + } +write_unlock (&profile_list_lock); + +/* FIXME: cleanup profiles references on files */ + +free_null_complain_profile (); + +/* +* Delay for an rcu cycle to make sure that all active task +* context readers have finished, and all profiles have been +* freed by their rcu callbacks. +*/ +synchronize_rcu (); + +destroy_apparmorfs (); +mutex_unlock (&aa_interface_lock); + +if (unregister_security (&apparmor_ops)) +info_message ("Unable to properly unregister AppArmor" ); + +info_message ("AppArmor protection removed" ); +} + +module_init (apparmor_init); +module_exit (apparmor_exit); + +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 | 232 ++++++++++++ security/apparmor/match.h | 83 ++++ security/apparmor/module_interface.c | 643 +++++++++++++++++++++++++++++++++++ 3 files changed, 958 insertions(+)
security/apparmor/match.c new file +232 -0
@@ -0,0 +1,232 @@ +/* +* 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 "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; + } + + 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_match - match @path against @dfa starting in @state +* @dfa: the dfa to match @path against +* @state: the state to start matching in +* @path: the path to match against the dfa +* +* aa_dfa_match will match the full path length and return the state it +* finished matching in. The final state is used to look up the accepting +* label. +*/ +unsigned int aa_dfa_match (struct aa_dfa *dfa,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 =1 , pos; + +/* 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 ACCEPT_TABLE (dfa)[state]; +}
security/apparmor/match.h new file +83 -0
83 lines not on this page · raw message
security/apparmor/module_interface.c new file +643 -0
643 lines not on this page · raw message
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
@@ -440,10 +440,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 ;
@@ -451,17 +449,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 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 | 9 ++++++--- security/dummy.c | 2 +- security/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c +1 -1
@@ -2214,7 +2214,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 +6 -3
@@ -301,6 +301,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:
@@ -1213,8 +1214,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);
@@ -1644,11 +1645,12 @@ static inline int security_inode_unlinkstatic inline int security_inode_symlink (struct inode *dir,struct dentry *dentry, +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); } static inline int security_inode_mkdir (struct inode *dir,
@@ -2369,6 +2371,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
@@ -282,7 +282,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/selinux/hooks.c +2 -1
@@ -2202,7 +2202,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 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
@@ -396,7 +396,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;
@@ -415,7 +416,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;
@@ -428,6 +429,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 ;
@@ -438,6 +440,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst goto out; dentry = fhp->fh_dentry; + mnt = fhp->fh_export->ex_mnt; inode = dentry->d_inode;if (S_ISDIR (inode->i_mode)) flags =NFS4_ACL_DIR ;
@@ -449,12 +452,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_nfserr;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 );if (host_error <0 ) goto out_nfserr; }
@@ -1982,7 +1987,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
@@ -69,8 +69,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;
@@ -193,8 +193,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 ;
@@ -221,7 +221,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; }
@@ -236,7 +236,7 @@ sys_setxattr(char __user *path, char __u error =user_path_walk (path, &nd);if (error)return error; - error =setxattr (nd.dentry, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags);path_release (&nd);return error; }
@@ -251,7 +251,7 @@ sys_lsetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);if (error)return error; - error =setxattr (nd.dentry, name, value, size, flags); + error =setxattr (nd.dentry, nd.mnt, name, value, size, flags);path_release (&nd);return error; }
@@ -269,7 +269,7 @@ sys_fsetxattr(int fd, char __user *name,return error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =setxattr (dentry, name, value, size, flags); + error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags);fput (f);return error; }
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 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 | 13 ++++++++----- security/dummy.c | 3 ++- security/selinux/hooks.c | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-)
fs/xattr.c +1 -1
@@ -116,7 +116,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 +8 -5
@@ -391,7 +391,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
@@ -1248,7 +1248,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);const char *(*inode_xattr_getsuffix) (void );
@@ -1782,11 +1783,12 @@ static inline void security_inode_post_s security_ops->inode_post_setxattr (dentry, mnt, name, value, size, 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) {if (unlikely (IS_PRIVATE (dentry->d_inode)))return 0 ; -return security_ops->inode_getxattr (dentry, name); +return security_ops->inode_getxattr (dentry, mnt, name); }static inline int security_inode_listxattr (struct dentry *dentry)
@@ -2487,7 +2489,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
@@ -368,7 +368,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/selinux/hooks.c +2 -1
@@ -2393,7 +2393,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 42/45 AppArmor: add lock subtyping so lockdep does not report false dependencies
In reply to [email protected]AppArmor uses lock subtyping to avoid false positives from lockdep. The profile lock is often taken nested, but it is guaranteed to be in a lock safe order and not the same lock when done, so it is safe.
A third lock type (aa_lock_task_release) is given to the profile lock when it is taken in soft irq context during task release (aa_release). This is to avoid a false positive between the task lock and the profile lock. In task context the profile lock wraps the task lock with irqs off, but the kernel takes the task lock with irqs enabled. This won't ever result in a deadlock because aa_release doesn't need to take the task lock of the dead task that is released.
--- security/apparmor/apparmor.h | 7 +++++++ security/apparmor/inline.h | 25 ++++++++++++++++++------- security/apparmor/locking.txt | 21 +++++++++++++++------ security/apparmor/main.c | 6 +++--- 4 files changed, 43 insertions(+), 16 deletions(-)
security/apparmor/apparmor.h +7 -0
@@ -185,6 +185,13 @@ struct aa_audit {#define AA_CHECK_DIR 2/* file type is directory */ #define AA_CHECK_MANGLE 4/* leave extra room for name mangling */ +/* lock subtypes so lockdep does not raise false dependencies */ +enum aa_lock_class { + aa_lock_normal, + aa_lock_nested, + aa_lock_task_release +}; +/* main.c */ extern int alloc_null_complain_profile (void );extern void free_null_complain_profile (void );
security/apparmor/inline.h +18 -7
@@ -99,7 +99,8 @@ static inline void aa_free_task_context(* While the profile is locked, local interrupts are disabled. This also * gives us RCU reader safety. */ -static inline void lock_profile (struct aa_profile *profile) +static inline void lock_profile_nested (struct aa_profile *profile, +enum aa_lock_class lock_class) {/* We always lock top-level profiles instead of children. */ if (profile)
@@ -112,7 +113,13 @@ static inline void lock_profile(struct a* the task_free_security hook, which may run in RCU context. */ if (profile) -spin_lock_irqsave (&profile->lock, profile->int_flags); +spin_lock_irqsave_nested (&profile->lock, profile->int_flags, + lock_class); +} + +static inline void lock_profile (struct aa_profile *profile) +{ +lock_profile_nested (profile, aa_lock_normal); }/**
@@ -161,17 +168,21 @@ static inline void lock_both_profiles(st*/ if (!profile1 || profile1 == profile2) {if (profile2) -spin_lock_irqsave (&profile2->lock, profile2->int_flags); +spin_lock_irqsave_nested (&profile2->lock, + profile2->int_flags, + aa_lock_normal); }else if (profile1 > profile2) {/* profile1 cannot be NULL here. */ -spin_lock_irqsave (&profile1->lock, profile1->int_flags); +spin_lock_irqsave_nested (&profile1->lock, profile1->int_flags, + aa_lock_normal);if (profile2) -spin_lock (&profile2->lock); +spin_lock_nested (&profile2->lock, aa_lock_nested); }else {/* profile2 cannot be NULL here. */ -spin_lock_irqsave (&profile2->lock, profile2->int_flags); -spin_lock (&profile1->lock); +spin_lock_irqsave_nested (&profile2->lock, profile2->int_flags, + aa_lock_normal); +spin_lock_nested (&profile1->lock, aa_lock_nested); } }
security/apparmor/locking.txt +15 -6
@@ -51,9 +51,18 @@ list, and can sleep. This ensures that p won't race with itself. We release the profile_list_lock as soon as possible to avoid stalling exec during profile loading/replacement/removal. -lock_dep reports a false 'possible irq lock inversion dependency detected' -when the profile lock is taken in aa_release. This is due to that the -task_lock is often taken inside the profile lock but other kernel code -takes the task_lock with interrupts enabled. A deadlock will not actually -occur because apparmor does not take the task_lock in hard_irq or soft_irq -context. +AppArmor uses lock subtyping to avoid false positives from lockdep. The +profile lock is often taken nested, but it is guaranteed to be in a lock +safe order and not the same lock when done, so it is safe. + +A third lock type (aa_lock_task_release) is given to the profile lock +when it is taken in soft irq context during task release (aa_release). +This is to avoid a false positive between the task lock and the profile +lock. In task context the profile lock wraps the task lock with irqs +off, but the kernel takes the task lock with irqs enabled. This won't +result in a deadlock because for a deadlock to occur the kernel must +take dead task A's lock (irqs on), the rcu callback hook freeing +dead task A must be run and AppArmor must be changing the profile on +dead task A. The kernel should not be taking a dead task's task_lock +at the same time the task is being freed by task rcu cleanup other wise +the task would not be out of its quiescent period.
security/apparmor/main.c +3 -3
@@ -1081,8 +1081,8 @@ void aa_release(struct task_struct *task* sufficient to prevent the replacement race so we do not lock * the task. * -* lock_dep reports a false 'possible irq lock inversion dependency' -* between the profile lock and the task_lock. +* 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
@@ -1095,7 +1095,7 @@ void aa_release(struct task_struct *task repeat: profile =aa_get_profile (task);if (profile) { -lock_profile (profile); +lock_profile_nested (profile, aa_lock_task_release); cxt =aa_task_context (task);if (unlikely (!cxt || cxt->profile != profile)) {unlock_profile (profile);
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
@@ -296,7 +296,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); }/*
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/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-)
fs/namei.c +1 -1
@@ -1946,7 +1946,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
@@ -308,6 +308,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:
@@ -1213,7 +1214,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);
@@ -1650,11 +1652,12 @@ static inline int security_inode_symlinkstatic inline 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); } static inline int security_inode_rmdir (struct inode *dir,
@@ -2371,6 +2374,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
@@ -288,7 +288,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/selinux/hooks.c +2 -1
@@ -2207,7 +2207,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 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 | 11 +++++++---- security/dummy.c | 2 +- security/selinux/hooks.c | 2 +- 4 files changed, 10 insertions(+), 7 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 +7 -4
@@ -338,6 +338,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.
@@ -1223,7 +1224,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,
@@ -1693,11 +1694,12 @@ static inline int security_inode_rename new_dir, new_dentry); } -static inline int security_inode_readlink (struct dentry *dentry) +static inline 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); } static inline int security_inode_follow_link (struct dentry *dentry,
@@ -2407,7 +2409,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
@@ -312,7 +312,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/selinux/hooks.c +1 -1
@@ -2237,7 +2237,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 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/selinux/hooks.c | 5 +++-- 4 files changed, 13 insertions(+), 7 deletions(-)
fs/attr.c +2 -2
@@ -144,13 +144,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
@@ -358,6 +358,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:
@@ -1221,7 +1222,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,
@@ -1708,11 +1710,12 @@ static inline int security_inode_permiss }static inline 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); } static inline int security_inode_getattr (struct vfsmount *mnt,
@@ -2412,6 +2415,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
@@ -328,7 +328,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/selinux/hooks.c +3 -2
@@ -2267,11 +2267,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 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
@@ -383,7 +383,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;
@@ -391,7 +391,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
@@ -417,7 +417,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;
@@ -1992,12 +1992,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
@@ -522,6 +522,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);
@@ -572,7 +574,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;
@@ -600,6 +602,10 @@ static int chown_common(struct dentry * }if (!S_ISDIR (inode->i_mode)) newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID ; +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);
@@ -615,7 +621,7 @@ asmlinkage long sys_chown(const char __u error =user_path_walk (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );path_release (&nd); out:return error;
@@ -635,7 +641,7 @@ asmlinkage long sys_fchownat(int dfd, co error =__user_walk_fd (dfd, filename, follow, &nd);if (error) goto out; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );path_release (&nd); out:return error;
@@ -649,7 +655,7 @@ asmlinkage long sys_lchown(const char __ error =user_path_walk_link (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, nd.mnt, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group,NULL );path_release (&nd); out:return error;
@@ -668,7 +674,7 @@ asmlinkage long sys_fchown(unsigned int dentry = file->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =chown_common (dentry, file->f_path.mnt, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group, file);fput (file); out:return error;
include/linux/fs.h +3 -0
@@ -351,6 +351,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; };
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 | 18 +++++++++++++----- security/dummy.c | 4 +++- security/selinux/hooks.c | 8 ++++++-- 4 files changed, 26 insertions(+), 10 deletions(-)
fs/namei.c +4 -2
@@ -2417,7 +2417,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;
@@ -2451,7 +2452,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 +13 -5
@@ -336,8 +336,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.
@@ -1230,7 +1232,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);
@@ -1696,14 +1700,16 @@ 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) {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); } static inline int security_inode_readlink (struct dentry *dentry,
@@ -2419,8 +2425,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
@@ -310,8 +310,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/selinux/hooks.c +6 -2
@@ -2238,8 +2238,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 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 new file +1399 -0
@@ -0,0 +1,1399 @@ +/* +* 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" +}; + +/* NULL complain profile +* +* Used when in complain mode, to emit Permitting messages for non-existant +* profiles and hats. This is necessary because of selective mode, in which +* case we need a complain null_profile and enforce null_profile +* +* The null_complain_profile cannot be statically allocated, because it +* can be associated to files which keep their reference even if apparmor is +* unloaded +*/ +struct aa_profile *null_complain_profile; + +static inline void aa_permerror2result (int perm_result,struct aa_audit *sa) +{ +if (perm_result ==0 ) {/* success */ + sa->result =1 ; + sa->error_code =0 ; + }else {/* -ve internal error code or +ve mask of denied perms */ + sa->result =0 ; + sa->error_code = perm_result; + } +} + +/** +* 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 +* +* 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 l_mode, t_mode; + + l_mode =aa_match (profile->file_rules, link); + t_mode =aa_match (profile->file_rules, target); + +/* Link always requires 'l' on the link, a subset of the +* target's 'r', 'w', 'x', and 'm' permissions on the link, and +* if the link has 'x', an exact match of all the execute flags +* ('i', 'u', 'U', 'p', 'P'). +*/ +#define RWXM (MAY_READ | MAY_WRITE | MAY_EXEC | AA_EXEC_MMAP) +if ((l_mode &AA_MAY_LINK ) && + (l_mode &RWXM ) && !(l_mode & ~t_mode &RWXM ) && + (!(l_mode &MAY_EXEC ) || + ((l_mode &AA_EXEC_MODIFIERS ) == (t_mode &AA_EXEC_MODIFIERS ) && + (l_mode &AA_EXEC_UNSAFE ) == (t_mode &AA_EXEC_UNSAFE )))) +return 0 ; +#undef RWXM +/* FIXME: There currenly is no way to report which permissions +* we expect in t_mode, so linking could fail even after learning +* the required l_mode. +*/ +return AA_MAY_LINK ; +} + +/** +* mangle -- escape special characters in str +* @str: string to escape +* @buffer: buffer containing str +* +* Escape special characters in @str, which is contained in @buffer. @str must +* be aligned to the end of the buffer, and the space between @buffer and @str +* may be used for escaping. +* +* Returns @str if no escaping was necessary, a pointer to the beginning of the +* escaped string, or NULL if there was not enough space in @buffer. When +* called with a NULL buffer, the return value tells whether any escaping is +* necessary. +*/ +static const char *mangle (const char *str,char *buffer) +{ +static const char c_escape[] = { + ['\a' ] ='a' , ['\b' ] ='b' , + ['\f' ] ='f' , ['\n' ] ='n' , + ['\r' ] ='r' , ['\t' ] ='t' , + ['\v' ] ='v' , + [' ' ] =' ' , ['\\' ] ='\\' , + }; +const char *s; +char *t, c; + +#define mangle_escape (c) \ + unlikely((unsigned char)(c) < ARRAY_SIZE(c_escape) && \ + c_escape[(unsigned char)c]) + +for (s = (char *)str; (c = *s) !='\0' ; s++) +if (mangle_escape (c)) + goto escape; +return str; + +escape: +if (!buffer) +return NULL ; +for (s = str, t = buffer; (c = *s) !='\0' ; s++) { +if (mangle_escape (c)) { +if (t == s) +return NULL ; + *t++ ='\\' ; + *t++ = c_escape[(unsigned char )c]; + }else + *t++ = c; + } + *t++ ='\0' ; + +#undef mangle_escape + +return buffer; +} + +/** +* 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); + +/* Make sure we have enough space for name mangling. */ +if (!IS_ERR (name) && + (check &AA_CHECK_MANGLE ) && name - buf <= size /2 ) + name =ERR_PTR (-ENAMETOOLONG ); + +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 mask, +int check) +{ +int denied_mask, error; + +again: + sa->buffer =NULL ; + sa->name =aa_get_name (dentry, mnt, &sa->buffer, check); + +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 )) + denied_mask =0 ; +else + denied_mask =PTR_ERR (sa->name); + sa->name =NULL ; + }else + denied_mask =aa_file_denied (profile, sa->name, mask); + +aa_permerror2result (denied_mask, sa); + + error =aa_audit (profile, sa); + +aa_put_name_buffer (sa->buffer); +if (error == -ENAMETOOLONG ) { +BUG_ON (check &AA_CHECK_MANGLE ); + check |=AA_CHECK_MANGLE ; + goto again; + } + +return error; +} + +/** +* attach_nullprofile - allocate and attach a null_profile hat to profile +* @profile: profile to attach a null_profile hat to. +* +* Return %0 (success) or error (-%ENOMEM) +*/ +int attach_nullprofile (struct aa_profile *profile) +{ +struct aa_profile *hat =NULL ; +char *hatname =NULL ; + + hat =alloc_aa_profile (); +if (!hat) + goto fail; +if (profile->flags.complain) + hatname =kstrdup ("null-complain-profile" ,GFP_KERNEL ); +else + hatname =kstrdup ("null-profile" ,GFP_KERNEL ); +if (!hatname) + goto fail; + + hat->flags.complain = profile->flags.complain; + hat->name = hatname; + hat->parent = profile; + + profile->null_profile = hat; + +return 0 ; + +fail: +kfree (hatname); +free_aa_profile (hat); + +return -ENOMEM ; +} + +/** +* alloc_null_complain_profile - Allocate the global null_complain_profile. +* +* Return %0 (success) or error (-%ENOMEM) +*/ +int alloc_null_complain_profile (void ) +{ + null_complain_profile =alloc_aa_profile (); +if (!null_complain_profile) + goto fail; + + null_complain_profile->name = +kstrdup ("null-complain-profile" ,GFP_KERNEL ); + +if (!null_complain_profile->name) + goto fail; + + null_complain_profile->flags.complain =1 ; +if (attach_nullprofile (null_complain_profile)) + goto fail; + +return 0 ; + +fail: +/* free_aa_profile is safe for freeing partially constructed objects */ +free_aa_profile (null_complain_profile); + null_complain_profile =NULL ; + +return -ENOMEM ; +} + +/** +* free_null_complain_profile - Free null profiles +*/ +void free_null_complain_profile (void ) +{ +aa_put_profile (null_complain_profile); + null_complain_profile =NULL ; +} + +/** +* aa_audit_message - Log a message to the audit subsystem +* @profile: profile to check against +* @gfp: allocation flags +* @flags: audit flags +* @fmt: varargs fmt +*/ +int aa_audit_message (struct aa_profile *profile,gfp_t gfp,const char *fmt, + ...) +{ +int ret; +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_MSG ; + sa.name = fmt; +va_start (sa.vaval, fmt); + sa.flags =0 ; + sa.gfp_mask = gfp; + sa.error_code =0 ; + sa.result =0 ;/* fake failure: force message to be logged */ + + ret =aa_audit (profile, &sa); + +va_end (sa.vaval); + +return ret; +} + +/** +* aa_audit_syscallreject - Log a syscall rejection to the audit subsystem +* @profile: profile to check against +* @msg: string describing syscall being rejected +* @gfp: memory allocation flags +*/ +int aa_audit_syscallreject (struct aa_profile *profile,gfp_t gfp, +const char *msg) +{ +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_SYSCALL ; + sa.name = msg; + sa.flags =0 ; + sa.gfp_mask = gfp; + sa.error_code =0 ; + sa.result =0 ;/* failure */ + +return aa_audit (profile, &sa); +} + +/** +* 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) +{ +struct audit_buffer *ab =NULL ; +struct audit_context *audit_cxt; + +const char *logcls; +unsigned int flags; +int audit =0 , + complain =0 , + error = -EINVAL , + opspec_error = -EACCES ; + +const gfp_t gfp_mask = sa->gfp_mask; + +/* +* sa->result: 1 success, 0 failure +* sa->error_code: success: 0 +* failure: +ve mask of failed permissions or -ve +* system error +*/ + +if (likely (sa->result)) { +if (likely (!PROFILE_AUDIT (profile))) { +/* nothing to log */ + error =0 ; + goto out; + }else { + audit =1 ; + logcls ="AUDITING" ; + } + }else if (sa->error_code <0 ) { +audit_log (current->audit_context, gfp_mask,AUDIT_APPARMOR , +"Internal error auditing event type %d (error %d)" , + sa->type, sa->error_code); +AA_ERROR ("Internal error auditing event type %d (error %d)\n" , + sa->type, sa->error_code); + error = sa->error_code; + goto out; + }else if (sa->type ==AA_AUDITTYPE_SYSCALL ) { +/* Currently AA_AUDITTYPE_SYSCALL is for rejects only. +* Values set by aa_audit_syscallreject will get us here. +*/ + logcls ="REJECTING" ; + }else { + complain =PROFILE_COMPLAIN (profile); + logcls = complain ?"PERMITTING" :"REJECTING" ; + } + +/* In future extend w/ per-profile flags +* (flags |= sa->profile->flags) +*/ + flags = sa->flags; +if (apparmor_logsyscall) + flags |=AA_AUDITFLAG_AUDITSS_SYSCALL ; + + +/* Force full audit syscall logging regardless of global setting if +* we are rejecting a syscall +*/ +if (sa->type ==AA_AUDITTYPE_SYSCALL ) { + audit_cxt = current->audit_context; + }else { + audit_cxt = (flags &AA_AUDITFLAG_AUDITSS_SYSCALL ) ? + current->audit_context :NULL ; + } + + ab =audit_log_start (audit_cxt, gfp_mask,AUDIT_APPARMOR ); + +if (!ab) { +AA_ERROR ("Unable to log event (%d) to audit subsys\n" , + sa->type); +if (complain) + error =0 ; + goto out; + } + +/* messages get special handling */ +if (sa->type ==AA_AUDITTYPE_MSG ) { +audit_log_vformat (ab, sa->name, sa->vaval); +audit_log_end (ab); + error =0 ; + goto out; + } + +if (sa->type &AA_MANGLE_NAME ) { + sa->name =mangle (sa->name, sa->buffer); +if (!sa->name) +return -ENAMETOOLONG ; + } +if (sa->type &AA_MANGLE_NAME2 ) { + sa->name2 =mangle (sa->name2, sa->buffer2); +if (!sa->name2) +return -ENAMETOOLONG ; + + } + +/* log operation */ + +audit_log_format (ab,"%s " , logcls);/* REJECTING/ALLOWING/etc */ + +#define NOFLAGS (x) ((x) & ~(AA_MANGLE_NAME | AA_MANGLE_NAME2)) + +switch (NOFLAGS (sa->type)) { +case NOFLAGS (AA_AUDITTYPE_FILE ): { +int perm = audit ? sa->mask : sa->error_code; + +audit_log_format (ab,"%s%s%s%s%s access to %s " , + perm &AA_EXEC_MMAP ?"m" :"" , + perm &MAY_READ ?"r" :"" , + perm &MAY_WRITE ?"w" :"" , + perm &MAY_EXEC ?"x" :"" , + perm &AA_MAY_LINK ?"l" :"" , + sa->name); + opspec_error = -EPERM ; +break ; + } +case NOFLAGS (AA_AUDITTYPE_DIR ): +audit_log_format (ab,"%s on %s " , sa->name2, sa->name); +break ; +case NOFLAGS (AA_AUDITTYPE_ATTR ): { +struct iattr *iattr = sa->iattr; + +audit_log_format (ab, +"attribute (%s%s%s%s%s%s%s) change to %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_SET ) || + (iattr->ia_valid &ATTR_ATIME )) ?"atime," :"" , + ((iattr->ia_valid &ATTR_MTIME_SET ) || + (iattr->ia_valid &ATTR_MTIME )) ?"mtime," :"" , + iattr->ia_valid &ATTR_CTIME ?"ctime," :"" , + sa->name); +break ; + } +case NOFLAGS (AA_AUDITTYPE_XATTR ): +audit_log_format (ab,"%s on %s " , sa->name2, sa->name); +break ; +case NOFLAGS (AA_AUDITTYPE_LINK ): +audit_log_format (ab,"link access from %s to %s " , sa->name, + sa->name2); +break ; +case NOFLAGS (AA_AUDITTYPE_CAP ): +audit_log_format (ab,"access to capability '%s' " , + capability_names[sa->capability]); + opspec_error = -EPERM ; +break ; +case NOFLAGS (AA_AUDITTYPE_SYSCALL ): +audit_log_format (ab,"access to syscall '%s' " , sa->name); + opspec_error = -EPERM ; +break ; +default : +WARN_ON (1 ); +return error; + } + +#undef NOFLAGS + +audit_log_format (ab,"(%d profile %s active %s)" , + current->pid, profile->parent->name, profile->name); + +audit_log_end (ab); + +if (complain) + error =0 ; +else + error = sa->result ?0 : opspec_error; +out: +return error; +} + +/** +* 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; + + sa.type =AA_AUDITTYPE_ATTR ; + sa.iattr = iattr; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + + 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,MAY_WRITE , 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,struct dentry *dentry, +struct vfsmount *mnt,const char *operation, +int mask,int check) +{ +struct inode *inode = dentry->d_inode; +int error; +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_XATTR ; + sa.name2 = operation; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + +if (inode &&S_ISDIR (inode->i_mode)) + check |=AA_CHECK_DIR ; + + error =aa_perm_dentry (profile, dentry, mnt, &sa, mask, 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,struct dentry *dentry, +struct vfsmount *mnt,int mask,int check) +{ +struct aa_audit sa; +int error =0 ; + +if (mask ==0 ) + goto out; + + sa.type =AA_AUDITTYPE_FILE ; + sa.mask = mask; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + error =aa_perm_dentry (profile, dentry, mnt, &sa, mask, 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,struct dentry *dentry, +struct vfsmount *mnt,const char *operation,int mask) +{ +struct aa_audit sa; + + sa.type =AA_AUDITTYPE_DIR ; + sa.name2 = operation; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + +return aa_perm_dentry (profile, dentry, mnt, &sa, mask,AA_CHECK_DIR ); +} + +int aa_perm_path (struct aa_profile *profile,const char *name,int mask) +{ +struct aa_audit sa; +int denied_mask; + + sa.type =AA_AUDITTYPE_FILE ; + sa.mask = mask; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + sa.name = name; + + denied_mask =aa_file_denied (profile, name, mask); +aa_permerror2result (denied_mask, &sa); + +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 occurance 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); + + sa.type =AA_AUDITTYPE_CAP ; + sa.name =NULL ; + sa.capability = cap; + sa.flags =0 ; + sa.error_code =0 ; + sa.result = !error; + sa.gfp_mask =GFP_ATOMIC ; + + 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 denied_mask = -EPERM , error, check =0 ; +struct aa_audit sa; + +again: + sa.buffer =NULL ; + sa.name =aa_get_name (link, link_mnt, &sa.buffer, check); + sa.buffer2 =NULL ; + sa.name2 =aa_get_name (target, target_mnt, &sa.buffer2, check); + +if (IS_ERR (sa.name)) { + denied_mask =PTR_ERR (sa.name); + sa.name =NULL ; + } +if (IS_ERR (sa.name2)) { + denied_mask =PTR_ERR (sa.name2); + sa.name2 =NULL ; + } + +if (sa.name && sa.name2) + denied_mask =aa_link_denied (profile, sa.name, sa.name2); + +aa_permerror2result (denied_mask, &sa); + + sa.type =AA_AUDITTYPE_LINK ; + sa.flags =0 ; + sa.gfp_mask =GFP_KERNEL ; + + error =aa_audit (profile, &sa); + +aa_put_name_buffer (sa.buffer); +aa_put_name_buffer (sa.buffer2); +if (error == -ENAMETOOLONG ) { +BUG_ON (check &AA_CHECK_MANGLE ); + check |=AA_CHECK_MANGLE ; + goto again; + } + +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->hat_magic); +unlock_profile (profile); + +if (APPARMOR_COMPLAIN (child_cxt) && + profile == null_complain_profile) +aa_audit_message (profile,GFP_KERNEL , +"LOGPROF-HINT fork child=%d " +"(%d profile %s active %s)" , + child->pid, current->pid, + profile->parent->name, profile->name); +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,char *buffer, +int mandatory,int complain) +{ +struct aa_profile *new_profile; + +/* Locate new profile */ + new_profile =aa_find_profile (name); +if (new_profile) { +AA_DEBUG ("%s: setting profile %s\n" , + __FUNCTION__, new_profile->name); + }else if (mandatory && profile) { + name =mangle (name, buffer); +if (complain) { +aa_audit_message (profile,GFP_KERNEL ,"LOGPROF-HINT " +"missing_mandatory_profile image '%s' " +"(%d profile %s active %s)" , + name, current->pid, + profile->parent->name, profile->name); + profile =aa_dup_profile (null_complain_profile); + }else { +aa_audit_message (profile,GFP_KERNEL ,"REJECTING " +"exec(2) of image '%s'. Profile " +"mandatory and not found. " +"(%d profile %s active %s)" , + name, current->pid, + profile->parent->name, profile->name); +return ERR_PTR (-EPERM ); + } + }else { +/* Only way we can get into this code is if task +* is unconfined. +*/ +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 =AA_EXEC_UNSAFE , complain =0 ; + +AA_DEBUG ("%s\n" , __FUNCTION__); + + filename =aa_get_name (filp->f_dentry, filp->f_vfsmnt, &buffer, +AA_CHECK_MANGLE ); +if (IS_ERR (filename)) { +AA_ERROR ("%s: Failed to get filename" , __FUNCTION__); +return -ENOENT ; + } + +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 & (MAY_EXEC |AA_EXEC_MODIFIERS )) { +switch (exec_mode & (MAY_EXEC |AA_EXEC_MODIFIERS )) { +case MAY_EXEC |AA_EXEC_INHERIT : +AA_DEBUG ("%s: INHERIT %s\n" , + __FUNCTION__, + filename); +/* nothing to be done here */ + goto cleanup; + +case MAY_EXEC |AA_EXEC_UNCONFINED : +AA_DEBUG ("%s: UNCONFINED %s\n" , + __FUNCTION__, + filename); + +/* detach current profile */ + new_profile =NULL ; +break ; + +case MAY_EXEC |AA_EXEC_PROFILE : +AA_DEBUG ("%s: PROFILE %s\n" , + __FUNCTION__, + filename); + new_profile =aa_register_find (profile, + filename, + buffer,1 , + complain); +break ; + +default : +AA_ERROR ("Rejecting exec(2) of image '%s'. " +"Unknown exec qualifier %x " +"(%d profile %s active %s)\n" , + filename, + exec_mode &AA_EXEC_MODIFIERS , + current->pid, + profile->parent->name, + profile->name); + new_profile =ERR_PTR (-EPERM ); +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 (null_complain_profile); + exec_mode |=AA_EXEC_UNSAFE ; + }else { + filename =mangle (filename, buffer); +aa_audit_message (profile,GFP_KERNEL ,"REJECTING " +"exec(2) of image '%s'. Unable to " +"determine exec qualifier. " +"(%d profile %s active %s)" , + filename, current->pid, + profile->parent->name, profile->name); + new_profile =ERR_PTR (-EPERM ); + } + }else { +/* Unconfined task, load profile if it exists */ + new_profile =aa_register_find (NULL , filename, buffer,0 ,0 ); +if (new_profile ==NULL ) + goto cleanup; + } + +if (IS_ERR (new_profile)) + goto cleanup; + + old_profile =__aa_replace_profile (current, new_profile,0 ); +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 ) { + filename =mangle (filename, buffer); +aa_audit_message (profile,GFP_KERNEL , +"REJECTING exec(2) of image '%s'. " +"Unable to change profile, ptraced by " +"%d. (%d profile %s active %s)" , + filename, current->parent->pid, + current->pid, + profile->parent->name, profile->name); + } + 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 )) { +unsigned long bprm_flags; + + bprm_flags =AA_SECURE_EXEC_NEEDED ; + bprm->security = (void *) + ((unsigned long )bprm->security | bprm_flags); + } + +if (complain && new_profile == null_complain_profile) +aa_audit_message (new_profile,GFP_ATOMIC , +"LOGPROF-HINT changing_profile " +"(%d profile %s active %s)" , + current->pid, + new_profile->parent->name, new_profile->name); +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. +* +* lock_dep reports a false 'possible irq lock inversion dependency' +* between the profile lock and the task_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 (profile); + 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 ); +unlock_profile (profile); +aa_put_profile (profile); + } +} + +/** +* do_change_hat - actually switch hats +* @hat_name: name of hat to switch to +* @new_cxt: new aa_task_context to use on profile change +* @hat_magic: new magic value to use +* +* Switch to a new hat. Returns %0 on success, error otherwise. +*/ +static int do_change_hat (const char *hat_name, +struct aa_task_context *new_cxt,u64 hat_magic) +{ +struct aa_task_context *cxt =aa_task_context (current); +struct aa_profile *sub; +int error =0 ; + +/* +* Note: the profile and sub-profiles cannot go away under us here; +* no need to grab an additional reference count. +*/ + sub =__aa_find_profile (hat_name, &cxt->profile->parent->sub); + +if ((current->ptrace &PT_PTRACED ) &&aa_may_ptrace (cxt, sub)) +return -EPERM ; + +if (sub) { +/* change hat */ +aa_change_task_context (current, new_cxt, sub, hat_magic); + }else { +struct aa_profile *profile = cxt->profile; + +if (APPARMOR_COMPLAIN (cxt)) { +aa_audit_message (profile,GFP_ATOMIC , +"LOGPROF-HINT unknown_hat %s " +"(%d profile %s active %s)" , + hat_name, current->pid, + profile->parent->name, profile->name); + }else { +AA_DEBUG ("%s: Unknown hatname '%s'. " +"Changing to NULL profile " +"(%d profile %s active %s)\n" , + __FUNCTION__, + hat_name, + current->pid, + profile->parent->name, + profile->name); + error = -EACCES ; + } +/* +* Switch to the NULL profile: it grants no accesses, so in +* learning mode all accesses will get logged, and in enforce +* mode all accesses will be denied. +* +* In learning mode, this allows us to learn about new hats. +*/ +aa_change_task_context (current, new_cxt, + cxt->profile->null_profile, hat_magic); + } + +return error; +} + +/** +* aa_change_hat - change hat to/from subprofile +* @hat_name: hat to change to +* @hat_magic: magic cookie 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 @hat_magic 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 hat_magic) +{ +struct aa_task_context *cxt, *new_cxt; +struct aa_profile *profile =NULL ; +int error =0 ; + +/* Dump out above debugging in WARN mode if we are in AUDIT mode */ +if (APPARMOR_AUDIT (aa_task_context (current))) { +aa_audit_message (NULL ,GFP_KERNEL ,"change_hat %s, 0x%llx " +"(pid %d)" , + hat_name ? hat_name :"NULL" , hat_magic, + current->pid); + } + + new_cxt =aa_alloc_task_context (GFP_KERNEL ); +if (!new_cxt) +return -ENOMEM ; + + cxt =lock_task_and_profiles (current,NULL ); +if (!cxt) { +/* An unconfined process cannot change_hat(). */ + error = -EPERM ; + goto out; + } + +/* No need to get reference count: we do not sleep. */ + profile = cxt->profile; + +/* check to see if the confined process has any hats. */ +if (list_empty (&profile->parent->sub) && !PROFILE_COMPLAIN (profile)) { + error = -ECHILD ; + goto out; + } + +if (profile == profile->parent) { +/* We are in the parent profile. */ +if (hat_name) { +AA_DEBUG ("%s: switching to %s, 0x%llx\n" , + __FUNCTION__, + hat_name, + hat_magic); + error =do_change_hat (hat_name, new_cxt, hat_magic); + } + }else { +/* +* We are in a child profile. +* +* Check to make sure magic is same as what was passed when +* we switched into this profile. Handle special casing of +* NULL magic which confines task to subprofile and prohibits +* further change_hats. +*/ +if (hat_magic && hat_magic == cxt->hat_magic) { +if (!hat_name) { +/* Return from subprofile back to parent. */ +aa_change_task_context (current, new_cxt, + profile->parent,0 ); + }else { +/* +* Change to another (sibling) profile, and +* stick with the same hat_magic. +*/ + error =do_change_hat (hat_name, new_cxt, + cxt->hat_magic); + } + }else if (cxt->hat_magic) { +AA_ERROR ("KILLING process %d " +"Invalid change_hat() magic# 0x%llx " +"(hatname %s profile %s active %s)\n" , + current->pid, hat_magic, + hat_name ? hat_name :"NULL" , + profile->parent->name, + profile->name); + +/* terminate current process */ + (void )send_sig_info (SIGKILL ,NULL , current); + }else {/* cxt->hat_magic == 0 */ +AA_ERROR ("KILLING process %d " +"Task was confined to current subprofile " +"(profile %s active %s)\n" , + current->pid, + profile->parent->name, + profile->name); + +/* terminate current process */ + (void )send_sig_info (SIGKILL ,NULL , current); + } + + } + +out: +if (aa_task_context (current) != new_cxt) +aa_free_task_context (new_cxt); +task_unlock (current); +unlock_profile (profile); +return error; +} + +/** +* __aa_replace_profile - replace a task's profile +* @task: task to switch the profile of +* @profile: profile to switch to +* @hat_magic: magic cookie 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, +u32 hat_magic) +{ +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, cxt->hat_magic); + }else +aa_change_task_context (task, new_cxt, profile,0 ); + +task_unlock (task); +unlock_both_profiles (profile, old_profile); +return old_profile; +} + +/** +* lock_task_and_profile - 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); + 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 +* @hat_magic: hat value to switch to (0 for no hat) +*/ +void aa_change_task_context (struct task_struct *task, +struct aa_task_context *new_cxt, +struct aa_profile *profile,u64 hat_magic) +{ +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->hat_magic = hat_magic; + new_cxt->task = task; + new_cxt->profile =aa_dup_profile (profile); +list_move (&new_cxt->list, &profile->parent->task_contexts); + } +rcu_assign_pointer (task->security, new_cxt); +}
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 | 5 +++-- fs/nfsd/vfs.c | 3 ++- include/linux/fs.h | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-)
fs/ecryptfs/inode.c +7 -2
@@ -411,19 +411,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 +3 -2
@@ -2268,7 +2268,7 @@ 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;
@@ -2346,7 +2346,8 @@ asmlinkage long sys_linkat(int olddfd, c error =PTR_ERR (new_dentry);if (IS_ERR (new_dentry)) goto out_unlock; - 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);dput (new_dentry); out_unlock:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/vfs.c +2 -1
@@ -1536,7 +1536,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
@@ -995,7 +995,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 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
@@ -453,10 +453,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
@@ -2105,7 +2105,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 );
@@ -2169,7 +2169,7 @@ static long do_unlinkat(int dfd, const c inode = dentry->d_inode;if (inode)atomic_inc (&inode->i_count); - error =vfs_unlink (nd.dentry->d_inode, dentry); + error =vfs_unlink (nd.dentry->d_inode, dentry, nd.mnt); exit2:dput (dentry); }
fs/nfsd/nfs4recover.c +1 -1
@@ -261,7 +261,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
@@ -1704,7 +1704,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
@@ -997,7 +997,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
@@ -749,7 +749,7 @@ asmlinkage long sys_mq_unlink(const charif (inode)atomic_inc (&inode->i_count); - err =vfs_unlink (dentry->d_parent->d_inode, dentry); + err =vfs_unlink (dentry->d_parent->d_inode, dentry, mqueue_mnt); out_err:dput (dentry);
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
@@ -440,10 +440,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 ;
@@ -451,17 +449,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 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 | 3 ++- fs/nfsd/vfs.c | 8 ++++---- fs/open.c | 28 +++++++++++++++------------- fs/reiserfs/xattr.c | 6 +++--- fs/sysfs/file.c | 2 +- fs/utimes.c | 11 ++++++----- include/linux/fs.h | 6 +++--- mm/filemap.c | 2 +- mm/tiny-shmem.c | 2 +- 14 files changed, 45 insertions(+), 37 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;
fs/ecryptfs/inode.c +3 -1
@@ -870,12 +870,14 @@ 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; crypt_stat = &ecryptfs_inode_to_private (dentry->d_inode)->crypt_stat; lower_dentry =ecryptfs_dentry_to_lower (dentry); + lower_mnt =ecryptfs_dentry_to_lower_mnt (dentry); inode = dentry->d_inode; lower_inode =ecryptfs_inode_to_lower (inode);if (ia->ia_valid &ATTR_SIZE ) {
@@ -890,7 +892,7 @@ static int ecryptfs_setattr(struct dentrif (rc <0 ) goto out; } - rc =notify_change (lower_dentry, ia); + rc =notify_change (lower_dentry, lower_mnt, ia); out:fsstack_copy_attr_all (inode, lower_inode,NULL );return rc;
fs/exec.c +2 -1
@@ -1564,7 +1564,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);
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 +2 -1
@@ -1598,7 +1598,8 @@ int may_open(struct nameidata *nd, int aif (!error) {DQUOT_INIT (inode); - error =do_truncate (dentry,0 ,ATTR_MTIME |ATTR_CTIME ,NULL ); + error =do_truncate (dentry, nd->mnt,0 , +ATTR_MTIME |ATTR_CTIME ,NULL ); }put_write_access (inode);if (error)
fs/nfsd/vfs.c +4 -4
@@ -358,7 +358,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); }
@@ -893,13 +893,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 ;mutex_lock (&dentry->d_inode->i_mutex); -notify_change (dentry, &ia); +notify_change (dentry, mnt, &ia);mutex_unlock (&dentry->d_inode->i_mutex); }
@@ -958,7 +958,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
@@ -193,8 +193,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;
@@ -214,7 +214,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; }
@@ -269,7 +269,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_access (inode);
@@ -321,7 +321,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:
@@ -521,7 +522,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_putf:
@@ -556,7 +557,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); dput_and_out:
@@ -570,7 +571,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;
@@ -599,7 +601,7 @@ static int chown_common(struct dentry *if (!S_ISDIR (inode->i_mode)) newattrs.ia_valid |=ATTR_KILL_SUID |ATTR_KILL_SGID ;mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =notify_change (dentry, mnt, &newattrs);mutex_unlock (&inode->i_mutex); out:return error;
@@ -613,7 +615,7 @@ asmlinkage long sys_chown(const char __u error =user_path_walk (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);path_release (&nd); out:return error;
@@ -633,7 +635,7 @@ asmlinkage long sys_fchownat(int dfd, co error =__user_walk_fd (dfd, filename, follow, &nd);if (error) goto out; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);path_release (&nd); out:return error;
@@ -647,7 +649,7 @@ asmlinkage long sys_lchown(const char __ error =user_path_walk_link (filename, &nd);if (error) goto out; - error =chown_common (nd.dentry, user, group); + error =chown_common (nd.dentry, nd.mnt, user, group);path_release (&nd); out:return error;
@@ -666,7 +668,7 @@ asmlinkage long sys_fchown(unsigned int dentry = file->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =chown_common (dentry, user, group); + error =chown_common (dentry, file->f_path.mnt, user, group);fput (file); out:return error;
fs/reiserfs/xattr.c +3 -3
@@ -479,7 +479,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 (fp->f_path.dentry, &newattrs); + err =notify_change (fp->f_path.dentry,NULL , &newattrs);if (err) goto out_filp;
@@ -819,7 +819,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;
@@ -871,7 +871,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
@@ -554,7 +554,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 ; - res =notify_change (victim, &newattrs); + res =notify_change (victim,NULL , &newattrs);mutex_unlock (&inode->i_mutex); }dput (victim);
fs/utimes.c +6 -5
@@ -46,7 +46,7 @@ long do_utimes(int dfd, char __user *fil {int error;struct nameidata nd; -struct dentry *dentry; +struct path path;struct inode *inode;struct iattr newattrs;struct file *f =NULL ;
@@ -64,16 +64,17 @@ long do_utimes(int dfd, char __user *fil f =fget (dfd);if (!f) goto out; - dentry = f->f_path.dentry; + path = f->f_path; }else { error =__user_walk_fd (dfd, filename, (flags &AT_SYMLINK_NOFOLLOW ) ?0 :LOOKUP_FOLLOW , &nd);if (error) goto out; - dentry = nd.dentry; + path.dentry = nd.dentry; + path.mnt = nd.mnt; } - inode = dentry->d_inode; + inode = path.dentry->d_inode; error = -EROFS ;if (IS_RDONLY (inode))
@@ -111,7 +112,7 @@ long do_utimes(int dfd, char __user *fil goto dput_and_out; }mutex_lock (&inode->i_mutex); - error =notify_change (dentry, &newattrs); + error =notify_change (path.dentry, path.mnt, &newattrs);mutex_unlock (&inode->i_mutex); dput_and_out:if (f)
include/linux/fs.h +3 -3
@@ -1470,8 +1470,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 );
@@ -1624,7 +1624,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
@@ -1910,7 +1910,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
@@ -86,7 +86,7 @@ struct file *shmem_file_setup(char *name file->f_mode =FMODE_WRITE |FMODE_READ ;/* 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 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 | 40 +++++++++++++++++++++++++--------------- security/commoncap.c | 4 ++-- security/dummy.c | 9 ++++++--- security/selinux/hooks.c | 8 ++++++-- 5 files changed, 41 insertions(+), 24 deletions(-)
fs/xattr.c +2 -2
@@ -80,7 +80,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 ;
@@ -88,7 +88,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 +25 -15
@@ -49,7 +49,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_task_post_setuid (uid_t old_ruid,uid_t old_euid,uid_t old_suid,int flags);extern void cap_task_reparent_to_init (struct task_struct *p);
@@ -384,11 +384,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.
@@ -1242,9 +1242,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);
@@ -1760,20 +1762,24 @@ static inline void security_inode_delete security_ops->inode_delete (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) {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); } -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) {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); }static inline int security_inode_getxattr (struct dentry *dentry,char *name)
@@ -2467,14 +2473,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
@@ -190,8 +190,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 (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/dummy.c +6 -3
@@ -350,8 +350,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) &&
@@ -360,7 +361,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/selinux/hooks.c +6 -2
@@ -2305,7 +2305,9 @@ static int selinux_inode_getattr(structreturn dentry_has_perm (current, mnt, dentry,FILE__GETATTR ); } -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;
@@ -2365,7 +2367,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 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
@@ -143,18 +143,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 ; }
@@ -362,7 +364,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 ;
@@ -375,7 +378,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 ;
@@ -397,7 +400,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; }
@@ -411,7 +414,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; }
@@ -426,7 +429,7 @@ sys_flistxattr(int fd, char __user *listif (!f)return error;audit_inode (NULL , f->f_path.dentry->d_inode); - 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 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
@@ -1779,9 +1779,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
@@ -1868,3 +1868,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
@@ -299,6 +299,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
@@ -103,5 +103,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 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 | 259 +++++++++++++++++++++++++++++++++++++++++ security/apparmor/apparmorfs.c | 250 +++++++++++++++++++++++++++++++++++++++ security/apparmor/inline.h | 219 ++++++++++++++++++++++++++++++++++ security/apparmor/list.c | 94 ++++++++++++++ security/apparmor/locking.txt | 59 +++++++++ security/apparmor/procattr.c | 138 +++++++++++++++++++++ 8 files changed, 1041 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 +259 -0
259 lines not on this page · raw message
security/apparmor/apparmorfs.c new file +250 -0
250 lines not on this page · raw message
security/apparmor/inline.h new file +219 -0
219 lines not on this page · raw message
security/apparmor/list.c new file +94 -0
94 lines not on this page · raw message
security/apparmor/locking.txt new file +59 -0
59 lines not on this page · raw message
security/apparmor/procattr.c new file +138 -0
138 lines not on this page · raw message
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/selinux/hooks.c | 5 +++-- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c +1 -1
@@ -1869,7 +1869,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
@@ -323,6 +323,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.
@@ -1218,7 +1219,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);
@@ -1670,11 +1671,12 @@ 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) {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); } static inline int security_inode_rename (struct inode *old_dir,
@@ -2388,6 +2390,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
@@ -299,7 +299,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/selinux/hooks.c +3 -2
@@ -2218,11 +2218,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 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
@@ -509,11 +509,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
@@ -1934,7 +1934,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 );
@@ -1978,7 +1979,7 @@ asmlinkage long sys_mkdirat(int dfd, conif (!IS_POSIXACL (nd.dentry->d_inode)) mode &= ~current->fs->umask; - error =vfs_mkdir (nd.dentry->d_inode, dentry, mode); + error =vfs_mkdir (nd.dentry->d_inode, dentry, nd.mnt, mode);dput (dentry); out_unlock:mutex_unlock (&nd.dentry->d_inode->i_mutex);
fs/nfsd/nfs4recover.c +2 -1
@@ -156,7 +156,8 @@ nfsd4_create_clid_dir(struct nfs4_clientdprintk ("NFSD: nfsd4_create_clid_dir: DIRECTORY EXISTS\n" ); 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 ); out_put:dput (dentry); out_unlock:
fs/nfsd/vfs.c +5 -3
@@ -1115,6 +1115,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;
@@ -1131,6 +1132,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;
@@ -1147,7 +1149,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 {
@@ -1186,7 +1188,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 :
@@ -1201,7 +1203,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
@@ -992,7 +992,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 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
@@ -2034,6 +2034,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);
@@ -2041,12 +2045,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 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
@@ -296,7 +296,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); }/*
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
@@ -1783,7 +1783,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 );
@@ -1806,14 +1806,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;
@@ -1832,6 +1832,8 @@ static char *__d_path(struct dentry *den *--buffer ='/' ; out: +if (vfsmount_locked) +spin_unlock (&vfsmount_lock);spin_unlock (&dcache_lock);return buffer;
PATCH 36/45 Export audit subsystem for use by modules
In reply to [email protected]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 | 5 +++++ kernel/audit.c | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-)
include/linux/audit.h +5 -0
@@ -114,6 +114,8 @@#define AUDIT_ANOM_PROMISCUOUS 1700/* Device changed promiscuous mode */ #define AUDIT_ANOM_ABEND 1701/* Process ended abnormally */ +#define AUDIT_APPARMOR 1500/* AppArmor audit */ +#define AUDIT_KERNEL 2000/* Asynchronous audit record. NOT A REQUEST. */ /* Rule flags */
@@ -499,6 +501,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
@@ -1054,8 +1054,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;
@@ -1311,3 +1310,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 41/45 Add AppArmor LSM to security/Makefile
In reply to [email protected]--- security/Kconfig | 1 + security/Makefile | 1 + security/apparmor/Kconfig | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-)
security/Kconfig +1 -0
@@ -94,6 +94,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 +2 -1
@@ -1,6 +1,7 @@ config SECURITY_APPARMOR tristate "AppArmor support" - depends on SECURITY!=n + depends on SECURITY + select AUDIT help This enables the AppArmor security module. Required userspace tools (if they are not included in your
PATCH 1/45 Pass struct vfsmount to the inode_create 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 | 9 ++++++--- security/dummy.c | 2 +- security/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-)
fs/namei.c +1 -1
@@ -1521,7 +1521,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
@@ -283,6 +283,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:
@@ -1204,8 +1205,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);
@@ -1611,11 +1612,12 @@ static inline int security_inode_init_sestatic inline 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); } static inline int security_inode_link (struct dentry *old_dentry,
@@ -2338,6 +2340,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
@@ -265,7 +265,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/selinux/hooks.c +2 -1
@@ -2176,7 +2176,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 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 | 12 ++++++++---- security/dummy.c | 3 ++- security/selinux/hooks.c | 3 ++- 4 files changed, 13 insertions(+), 7 deletions(-)
fs/namei.c +1 -1
@@ -2041,7 +2041,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 +8 -4
@@ -318,6 +318,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
@@ -1222,7 +1223,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,
@@ -1671,11 +1673,12 @@ static inline int security_inode_mkdir ( }static inline int security_inode_rmdir (struct inode *dir, -struct dentry *dentry) +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); } static inline int security_inode_mknod (struct inode *dir,
@@ -2396,7 +2399,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
@@ -295,7 +295,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/selinux/hooks.c +2 -1
@@ -2219,7 +2219,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 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 | 58 ++++++++++++++++++++++++----------------------- include/linux/security.h | 53 +++++++++++++++++++++++++----------------- include/linux/xattr.h | 8 +++--- security/commoncap.c | 4 +-- security/dummy.c | 10 ++++---- security/selinux/hooks.c | 10 ++++---- 6 files changed, 80 insertions(+), 63 deletions(-)
fs/xattr.c +30 -28
@@ -70,7 +70,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;
@@ -80,7 +80,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 ;
@@ -107,7 +107,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;
@@ -116,7 +116,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;
@@ -144,12 +144,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 ;
@@ -165,7 +165,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;
@@ -177,7 +178,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;
@@ -197,7 +198,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 ;
@@ -224,7 +225,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; }
@@ -239,7 +240,7 @@ sys_setxattr(char __user *path, char __u error =user_path_walk (path, &nd);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 );path_release (&nd);return error; }
@@ -254,7 +255,7 @@ sys_lsetxattr(char __user *path, char __ error =user_path_walk_link (path, &nd);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 );path_release (&nd);return error; }
@@ -272,7 +273,7 @@ sys_fsetxattr(int fd, char __user *name,return error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags); + error =setxattr (dentry, f->f_vfsmnt, name, value, size, flags, f);fput (f);return error; }
@@ -282,7 +283,7 @@ sys_fsetxattr(int fd, char __user *name,*/ 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 ;
@@ -302,7 +303,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 ;
@@ -325,7 +326,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; }
@@ -340,7 +341,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; }
@@ -355,7 +356,7 @@ sys_fgetxattr(int fd, char __user *name,if (!f)return error;audit_inode (NULL , f->f_path.dentry->d_inode); - 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, f);fput (f);return error; }
@@ -365,7 +366,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 ;
@@ -378,7 +379,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 ;
@@ -400,7 +401,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; }
@@ -414,7 +415,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; }
@@ -429,7 +430,7 @@ sys_flistxattr(int fd, char __user *listif (!f)return error;audit_inode (NULL , f->f_path.dentry->d_inode); - error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size); + error =listxattr (f->f_path.dentry, f->f_path.mnt, list, size, f);fput (f);return error; }
@@ -438,7 +439,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 ];
@@ -449,7 +451,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
@@ -461,7 +463,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; }
@@ -475,7 +477,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; }
@@ -492,7 +494,7 @@ sys_fremovexattr(int fd, char __user *nareturn error; dentry = f->f_path.dentry;audit_inode (NULL , dentry->d_inode); - error =removexattr (dentry, f->f_path.mnt, name); + error =removexattr (dentry, f->f_path.mnt, name, f);fput (f);return error; }
include/linux/security.h +32 -21
129 lines not on this page · raw message
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
@@ -191,7 +191,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 (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
@@ -201,7 +201,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 (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) &&
security/dummy.c +6 -4
@@ -352,7 +352,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) &&
@@ -369,18 +369,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/selinux/hooks.c +6 -4
@@ -2307,7 +2307,7 @@ static int selinux_inode_getattr(structstatic 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;
@@ -2394,18 +2394,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)) { if (!strncmp(name, XATTR_SECURITY_PREFIX,
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
@@ -1130,25 +1130,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 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/reiserfs/file.c | 2 +- fs/splice.c | 4 ++-- fs/xfs/linux-2.6/xfs_lrw.c | 2 +- include/linux/fs.h | 4 ++-- mm/filemap.c | 12 ++++++------ mm/filemap_xip.c | 2 +- mm/shmem.c | 2 +- 8 files changed, 15 insertions(+), 15 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/reiserfs/file.c +1 -1
@@ -1335,7 +1335,7 @@ static ssize_t reiserfs_file_write(strucif (count ==0 ) goto out; - res =remove_suid (file->f_path.dentry); + res =remove_suid (&file->f_path);if (res) goto out;
fs/splice.c +2 -2
@@ -796,7 +796,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;
@@ -845,7 +845,7 @@ generic_file_splice_write(struct pipe_in err =should_remove_suid (out->f_path.dentry);if (unlikely (err)) {mutex_lock (&inode->i_mutex); - err =__remove_suid (out->f_path.dentry, err); + err =__remove_suid (&out->f_path, err);mutex_unlock (&inode->i_mutex);if (err)return err;
fs/xfs/linux-2.6/xfs_lrw.c +1 -1
@@ -798,7 +798,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
@@ -1697,9 +1697,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 +6 -6
@@ -1905,20 +1905,20 @@ 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 kill =should_remove_suid (dentry); +int kill =should_remove_suid (path->dentry);if (unlikely (kill)) -return __remove_suid (dentry, kill); +return __remove_suid (path, kill);return 0 ; }
@@ -2269,7 +2269,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
@@ -405,7 +405,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
@@ -1516,7 +1516,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 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
@@ -560,11 +560,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
@@ -1855,7 +1855,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 );
@@ -1907,11 +1908,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 ;case S_IFDIR : error = -EPERM ;
fs/nfsd/vfs.c +2 -1
@@ -1194,7 +1194,8 @@ nfsd_create(struct svc_rqst *rqstp, strucase S_IFBLK :case S_IFIFO :case S_IFSOCK : - host_err =vfs_mknod (dirp, dchild, iap->ia_mode, rdev); + host_err =vfs_mknod (dirp, dchild, exp->ex_mnt, iap->ia_mode, + rdev);break ;default :printk ("nfsd: bad file type %o in nfsd_create\n" , type);
include/linux/fs.h +1 -1
@@ -993,7 +993,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
@@ -807,7 +807,7 @@ static int unix_bind(struct socket *sock*/ mode =S_IFSOCK | (SOCK_INODE (sock)->i_mode & ~current->fs->umask); - err =vfs_mknod (nd.dentry->d_inode, dentry, mode,0 ); + err =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, mode,0 );if (err) goto out_mknod_dput;mutex_unlock (&nd.dentry->d_inode->i_mutex);
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
@@ -1469,7 +1469,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
@@ -378,11 +378,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;
@@ -390,7 +391,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
@@ -479,13 +480,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 )
@@ -497,14 +498,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)) {
@@ -514,7 +516,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)) {
@@ -1942,7 +1944,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
@@ -106,7 +106,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;
@@ -278,7 +279,8 @@ sys_fsetxattr(int fd, char __user *name,* 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 ;
@@ -298,7 +300,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 ;
@@ -321,7 +323,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; }
@@ -336,7 +338,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; }
@@ -351,7 +353,7 @@ sys_fgetxattr(int fd, char __user *name,if (!f)return error;audit_inode (NULL , f->f_path.dentry->d_inode); - 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
@@ -77,7 +77,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 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/selinux/hooks.c | 5 +++-- 4 files changed, 13 insertions(+), 7 deletions(-)
fs/attr.c +2 -2
@@ -144,13 +144,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
@@ -358,6 +358,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:
@@ -1221,7 +1222,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,
@@ -1708,11 +1710,12 @@ static inline int security_inode_permiss }static inline 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); } static inline int security_inode_getattr (struct vfsmount *mnt,
@@ -2412,6 +2415,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
@@ -328,7 +328,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/selinux/hooks.c +3 -2
@@ -2267,11 +2267,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 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/selinux/hooks.c | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-)
fs/namei.c +1 -1
@@ -1946,7 +1946,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
@@ -308,6 +308,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:
@@ -1213,7 +1214,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);
@@ -1650,11 +1652,12 @@ static inline int security_inode_symlinkstatic inline 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); } static inline int security_inode_rmdir (struct inode *dir,
@@ -2371,6 +2374,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
@@ -288,7 +288,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/selinux/hooks.c +2 -1
@@ -2207,7 +2207,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 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 | 18 +++++++++++++----- security/dummy.c | 4 +++- security/selinux/hooks.c | 8 ++++++-- 4 files changed, 26 insertions(+), 10 deletions(-)
fs/namei.c +4 -2
@@ -2417,7 +2417,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;
@@ -2451,7 +2452,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 +13 -5
@@ -336,8 +336,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.
@@ -1230,7 +1232,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);
@@ -1696,14 +1700,16 @@ 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) {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); } static inline int security_inode_readlink (struct dentry *dentry,
@@ -2419,8 +2425,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
@@ -310,8 +310,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/selinux/hooks.c +6 -2
@@ -2238,8 +2238,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 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
@@ -1783,7 +1783,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 );
@@ -1806,14 +1806,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;
@@ -1832,6 +1832,8 @@ static char *__d_path(struct dentry *den *--buffer ='/' ; out: +if (vfsmount_locked) +spin_unlock (&vfsmount_lock);spin_unlock (&dcache_lock);return buffer;
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
@@ -560,11 +560,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
@@ -1855,7 +1855,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 );
@@ -1907,11 +1908,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 ;case S_IFDIR : error = -EPERM ;
fs/nfsd/vfs.c +2 -1
@@ -1194,7 +1194,8 @@ nfsd_create(struct svc_rqst *rqstp, strucase S_IFBLK :case S_IFIFO :case S_IFSOCK : - host_err =vfs_mknod (dirp, dchild, iap->ia_mode, rdev); + host_err =vfs_mknod (dirp, dchild, exp->ex_mnt, iap->ia_mode, + rdev);break ;default :printk ("nfsd: bad file type %o in nfsd_create\n" , type);
include/linux/fs.h +1 -1
@@ -993,7 +993,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
@@ -807,7 +807,7 @@ static int unix_bind(struct socket *sock*/ mode =S_IFSOCK | (SOCK_INODE (sock)->i_mode & ~current->fs->umask); - err =vfs_mknod (nd.dentry->d_inode, dentry, mode,0 ); + err =vfs_mknod (nd.dentry->d_inode, dentry, nd.mnt, mode,0 );if (err) goto out_mknod_dput;mutex_unlock (&nd.dentry->d_inode->i_mutex);
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
@@ -143,18 +143,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 ; }
@@ -362,7 +364,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 ;
@@ -375,7 +378,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 ;
@@ -397,7 +400,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; }
@@ -411,7 +414,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; }
@@ -426,7 +429,7 @@ sys_flistxattr(int fd, char __user *listif (!f)return error;audit_inode (NULL , f->f_path.dentry->d_inode); - 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 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
@@ -440,10 +440,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 ;
@@ -451,17 +449,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:
and with the actual introductory text this time
This post contains patches to include the AppArmor application security framework, with request for inclusion. It contains fixes for almost all of the feedback received from the previous post. A second follow up posting will address passing NULL nameidata.
Changes since previous post:
- Refactor d_path() patches: Separate changes to d_path(), getcwd(), and /proc/mounts from __d_path() cleanups.
- Switch from file_permission() to vfs_permission() in do_path_lookup() and sys_fchdir(): this avoids calling permission() with a NULL nameidata there.
- Fix file_permission() to not use NULL nameidata for its remaining users:
it makes little sense to replace file_permission() with vfs_permission() everywhere.
- Remove special casing for access to /proc/self/attr/current by adding rules to policy user side.
- Remove redundant fn's in lsm.c by calling cap functions directly from the security operations vector.
- Disallow ptracing process with different namespace.
- Use beX_to_cpu instead on ntoX in dfa unpack code.
- Fix potential overflow in unpack bounds checking.
- Limit profile recursion depth to 1 level.
- Factor out sysctl pathname code from selinux to add generic sysctl_pathname() function in kernel/sysctl.c. Replace special casing of sysctl write with finer grained mediation using sysctl_pathname() function to provide pathname for sysctl mediation.
- Escape special characters in pathnames when used in audit messages.
- Remove use of task->comm from audit messages. The use of task->comm was incorrect and only used as a human readable hint.
- Some structural cleanups on apparmors audit code paths.
- Set LOOKUP_CONTINUE flag when checking parent permissions. This allows permission functions to tell between parent and leaf checks. Check for (LOOKUP_PARENT | LOOKUP_CONTINUE) in the inode_permission apparmor hook.
- Drop rejection of CLONE_NEWNS since the kernel already requires CAP_SYS_ADMIN.
- Add a missing dput() in apparmorfs_detry_refcount().
- remove kernel doc style comment header from comments that are not in kernel doc format
- use lock subtyping to address lockdep reporint possible irq lock inversion
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-May_07/
Explaining the AppArmor design in detail would take by far too much space here, so let me refer you to the technical documentation for that. Included is a low-level walk-through of the system and basic tools, and some examples. The manual pages included in the apparmor-parser package are worth a read as well.
Re: [AppArmor 35/45] Allow permission functions to tell between parent and leaf checks
In reply to [email protected]Hi!
Quoted 4 lines
Set the
LOOKUP_CONTINUEflag when checking parent permissions. This allows permission functions to tell between parent and leaf checks.Signed-off-by Andreas Gruenbacher <[email protected]>
I guess you should sign this off.
fs/namei.c static int may_delete(struct inode *dir, · line 1415
Quoted 10 lines
fs/namei.c +4
-0@@ -1409,6 +1409,10 @@ static int may_delete(struct inode *dir,BUG_ON (victim->d_parent->d_inode != dir);audit_inode_child (victim->d_name.name, victim->d_inode, dir); +#if 0 +if (nd) + nd->flags |=LOOKUP_CONTINUE ; +#endif
We don't add disabled code to kernel.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 37/45] AppArmor: Main Part
In reply to [email protected]Hi!
Quoted 6 lines
The underlying functions by which the AppArmor LSM hooks are implemented.
Signed-off-by John Johansen <[email protected]>Signed-off-by Andreas Gruenbacher <[email protected]>+#include "
inline.h"
Select a better name for include?
Quoted 1 line
+static inline void
aa_permerror2result(int perm_result, struct aa_audit *sa)
Betternameoffunction?
Quoted 43 lines
security/apparmor/main.c new file +43
-0+/** +* mangle -- escape special characters in str +* @str: string to escape +* @buffer: buffer containing str +* +* Escape special characters in @str, which is contained in @buffer. @str must +* be aligned to the end of the buffer, and the space between @buffer and @str +* may be used for escaping. +* +* Returns @str if no escaping was necessary, a pointer to the beginning of the +* escaped string, or NULL if there was not enough space in @buffer. When +* called with a NULL buffer, the return value tells whether any escaping is +* necessary. +*/ +static const char *mangle (const char *str,char *buffer) +{ +static const char c_escape[] = { + ['\a' ] ='a' , ['\b' ] ='b' , + ['\f' ] ='f' , ['\n' ] ='n' , + ['\r' ] ='r' , ['\t' ] ='t' , + ['\v' ] ='v' , + [' ' ] =' ' , ['\\' ] ='\\' , + }; +const char *s; +char *t, c; + +#define mangle_escape (c) \ + unlikely((unsigned char)(c) < ARRAY_SIZE(c_escape) && \ + c_escape[(unsigned char)c]) + +for (s = (char *)str; (c = *s) !='\0' ; s++) +if (mangle_escape (c)) + goto escape; +return str; + +escape: +if (!buffer) +return NULL ; +for (s = str, t = buffer; (c = *s) !='\0' ; s++) { +if (mangle_escape (c)) { +if (t == s) +return NULL ; + *t++ ='\\' ;
security/apparmor/main.c line 155
Quoted 10 lines
security/apparmor/main.c new file +10
-0+ *t++ = c_escape[(unsigned char )c]; + }else + *t++ = c; + } + *t++ ='\0' ; + +#undef mangle_escape + +return buffer; +}
I do not think we want this in kernel.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to [email protected]security/apparmor/lsm.c line 63
Quoted 10 lines
Module parameters, LSM hooks, initialization and teardown.
Signed-off-by John Johansen <[email protected]>Signed-off-by Andreas Gruenbacher <[email protected]>security/apparmor/lsm.c new file +4
-0+/* 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" );
WTF? Why is this configurable?
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Hi!
Quoted 2 lines
Pathname matching, transition table loading, profile loading and manipulation.
So we get small interpretter of state machines, and reason we need is is 'apparmor is misdesigned and works with paths when it should have worked with handles'.
If you solve the 'new file problem', aa becomes subset of selinux.. And I'm pretty sure patch will be nicer than this.
Pavel
Quoted 176 lines
security/apparmor/match.c new file +176
-0+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; + } + + 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_match - match @path against @dfa starting in @state +* @dfa: the dfa to match @path against +* @state: the state to start matching in +* @path: the path to match against the dfa +* +* aa_dfa_match will match the full path length and return the state it +* finished matching in. The final state is used to look up the accepting +* label. +*/ +unsigned int aa_dfa_match (struct aa_dfa *dfa,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 =1 , pos; + +/* 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 ACCEPT_TABLE (dfa)[state]; +}
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Pavel MachekQuoted 1 line · Pavel Machek
Why is this configurable?
The maximum length of a pathname is an arbitrary limit: we don't want to allocate arbitrary amounts of of kernel memory for pathnames so we introduce this limit and set it to a reasonable value. In the unlikely case that someone uses insanely long pathnames, this limit can be increased.
Andreas
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Quoted 7 lines · Andreas Gruenbacher
Quoted 1 line · Pavel Machek
Why is this configurable?
The maximum length of a pathname is an arbitrary limit: we don't want to allocate arbitrary amounts of of kernel memory for pathnames so we introduce this limit and set it to a reasonable value. In the unlikely case that someone uses insanely long pathnames, this limit can be increased.
vfs does not have configurable pathname limit, and I do not see what is so special about AA to require this kind of uglyness.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Quoted 4 lines · Pavel Machek
Quoted 3 lines · Andreas Gruenbacher
Quoted 1 line · Pavel Machek
Why is this configurable?
Quoted 8 lines
Quoted 5 lines
The maximum length of a pathname is an arbitrary limit: we don't want to allocate arbitrary amounts of of kernel memory for pathnames so we introduce this limit and set it to a reasonable value. In the unlikely case that someone uses insanely long pathnames, this limit can be increased.
vfs does not have configurable pathname limit, and I do not see what is so special about AA to require this kind of uglyness.
You very well know that the vfs has a limit of PATH_MAX characters (4096) for pathnames. This means that at most that many characters can be passed at once.
I've really got enough of your perpetual unfounded rants.
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Quoted 11 lines · Andreas Gruenbacher
Quoted 10 lines · Pavel Machek
Quoted 8 lines · Andreas Gruenbacher
Quoted 1 line · Pavel Machek
Why is this configurable?
The maximum length of a pathname is an arbitrary limit: we don't want to allocate arbitrary amounts of of kernel memory for pathnames so we introduce this limit and set it to a reasonable value. In the unlikely case that someone uses insanely long pathnames, this limit can be increased.
Quoted 6 lines
Quoted 2 lines
vfs does not have configurable pathname limit, and I do not see what is so special about AA to require this kind of uglyness.
You very well know that the vfs has a limit of
PATH_MAXcharacters (4096) for pathnames. This means that at most that many characters can be passed at once.
Sorry then. Why not reuse the PATH_MAX when it exists already?
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Quoted 12 lines · Pavel Machek
Quoted 11 lines · Andreas Gruenbacher
Quoted 10 lines · Pavel Machek
Quoted 8 lines · Andreas Gruenbacher
Quoted 1 line · Pavel Machek
Why is this configurable?
The maximum length of a pathname is an arbitrary limit: we don't want to allocate arbitrary amounts of of kernel memory for pathnames so we introduce this limit and set it to a reasonable value. In the unlikely case that someone uses insanely long pathnames, this limit can be increased.
Quoted 6 lines
Quoted 6 lines
Quoted 2 lines
vfs does not have configurable pathname limit, and I do not see what is so special about AA to require this kind of uglyness.
You very well know that the vfs has a limit of
PATH_MAXcharacters (4096) for pathnames. This means that at most that many characters can be passed at once.
What users can do is something like this:
chdir("some/long/path");
chdir("some/even/longer/path");
...and the total length of the path can then exceed PATH_MAX characters. We can only accept pathnames up to some upper limit, and we need to somehow define what that limit is supposed to be. We could use PATH_MAX or some other arbitrary number. In most situations PATH_MAX will be fine, but that's not always guaranteed to be the case. So what's wrong about making this configurable for special situations that we might run into? Module parameters are *really* dead cheap.
Andreas
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Hi!
Quoted 10 lines
Quoted 3 lines
Quoted 3 lines
You very well know that the vfs has a limit of
PATH_MAXcharacters (4096) for pathnames. This means that at most that many characters can be passed at once.What users can do is something like this:
chdir("some/long/path"); chdir("some/even/longer/path"); ...
Quoted 7 lines
and the total length of the path can then exceed
PATH_MAXcharacters. We can only accept pathnames up to some upper limit, and we need to somehow define what that limit is supposed to be. We could usePATH_MAXor some other arbitrary number. In most situationsPATH_MAXwill be fine, but that's not always guaranteed to be the case. So what's wrong about making this configurable for special situations that we might run into? Module parameters are *really* dead cheap.
Parameters are cheap, but this one is ugly.
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere. Perhaps vfs should be modified not to allow such crazy paths? But placing limit in aa is ugly.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course. /proc only uses pathnames in a few places, but /proc/mounts will silently fail and produce garbage entries. That's not ideal of course; we should fix that somehow.
Note that this has nothing to do with the AppArmor discussion ...
Quoted 2 lines
Perhaps vfs should be modified not to allow such crazy paths? But placing limit in aa is ugly.
Dream on. Redefining fundamental vfs semantics is not an option; we should rather make sure that we fail gracefully. Considering the alternatives, I still prefer the configurable limit. That's way more useful than allowing a process to DOS the kernel with AppArmor.
Andreas
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Pavel MachekQuoted 8 lines · Pavel Machek
Hi!
Quoted 2 lines
Pathname matching, transition table loading, profile loading and manipulation.
So we get small interpretter of state machines, and reason we need is is 'apparmor is misdesigned and works with paths when it should have worked with handles'.
I assume you mean labels instead of handles.
AppArmor's design is around paths not labels, and independent of whether or not you like AppArmor, this design leads to a useful security model distinct from the SELinux security model (which is useful in its own ways). The differences between those models cannot be argued away, neither is a subset of the other, and neither is a misdesign. I would be thankful if you could stop spreading this lie.
Quoted 2 lines
If you solve the 'new file problem', aa becomes subset of selinux. And I'm pretty sure patch will be nicer than this.
You are quite mistaken. SELinux turns pathnames into labels when it initially labels all files (when a policy is rolled out), whereas AppArmor computes the "label" of each file when a file is opened. The two models start to diverge as soon as files are renamed: in SELinux, labels stick with the files. In AppArmor, "labels" stick with the names.
So what you advocate for is a hybrid between the SELinux and the AppArmor model, not a superset.
It could be that the SELinux folks will solve the issues they are having with new files using something better than restorecond in the future, perhaps even an in-kernel mechanism (although I somewhat doubt it). But then again, their basic model makes sense even without any live file relabeling, and so that's probably not very high up on the priority list.
Andreas
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Andreas GruenbacherQuoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
I don't mean this as a flame, but isn't the above statement the very crux of this discussion? Why should AppArmor be different from the rest of the kernel in its usage of pathnames (basis for decisions vs. informational reporting to userspace)? And if it is ok for AppArmor to generate and use pathnames as its basis of decisions on each open, then is it also ok for audit, inotify, and others to use them in the same manner? If the audit developers or inotify developers had come with patches that used d_path or equivalent in the same manner as AppArmor, don't you think they would have gotten the same resistance? And if you are truly trying to create a mechanism (in AppArmor) that you can ultimately apply widely to the system (going beyond AppArmor's original limited focus on a small set of network-facing daemons), aren't you concerned about the implications of having to generate a pathname on each open just to decide what to do? Is this really the "path" you want to take ;)?
Another question: it seems like the read-only bind mount folks gave up on propagating the vfsmounts down and switched to a rather different approach (checking near the entry points, using mount writer counters). So similarly, what makes AppArmor fundamentally different that it wouldn't take a similar approach to what they are doing vs. propagating the vfsmounts down? Or do you think they made the wrong choice? If so, why?
Just trying to understand your position better...
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Andreas GruenbacherQuoted 10 lines · Andreas Gruenbacher
Quoted 8 lines · Pavel Machek
Hi!
Quoted 2 lines
Pathname matching, transition table loading, profile loading and manipulation.
So we get small interpretter of state machines, and reason we need is is 'apparmor is misdesigned and works with paths when it should have worked with handles'.
Quoted 8 lines
I assume you mean labels instead of handles.
AppArmor's design is around paths not labels, and independent of whether or not you like AppArmor, this design leads to a useful security model distinct from the SELinux security model (which is useful in its own ways). The differences between those models cannot be argued away, neither is a subset of the other, and neither is a misdesign. I would be thankful if you could stop spreading this lie.
I have a hard time distinguishing AppArmor's "model" from its implementation; every time we suggest that one might emulate much of AppArmor's functionality on SELinux (as in SEEdit), someone points to a specific characteristic of the AppArmor implementation that cannot be emulated in this manner. But is that implementation characteristic an actual requirement or just how it happens to have been done to date in AA? And I get the impression that even if we extended SELinux in certain ways to ease such emulation, the AA folks would never be satisfied because the implementation would still differ. Can we separate the desired functionality and actual requirements from the implementation specifics?
Quoted 8 lines
Quoted 2 lines
If you solve the 'new file problem', aa becomes subset of selinux. And I'm pretty sure patch will be nicer than this.
You are quite mistaken. SELinux turns pathnames into labels when it initially labels all files (when a policy is rolled out), whereas AppArmor computes the "label" of each file when a file is opened. The two models start to diverge as soon as files are renamed: in SELinux, labels stick with the files. In AppArmor, "labels" stick with the names.
I'd argue a bit with that characterization, given that:
- in the case of SELinux, the pathname is never used as a basis for decisions by the kernel,
- under AA, each file may have an arbitrary set of "labels" or "policies" applied to it depending on what programs are accessing it and what names are being used to reference it - there is no system view of the subjects and objects and thus no way to identify the overall system policy for a given file.
- names are far less tranquil than labels.
Quoted 8 lines
So what you advocate for is a hybrid between the SELinux and the AppArmor model, not a superset.
It could be that the SELinux folks will solve the issues they are having with new files using something better than restorecond in the future, perhaps even an in-kernel mechanism (although I somewhat doubt it). But then again, their basic model makes sense even without any live file relabeling, and so that's probably not very high up on the priority list.
Live file relabeling (non-tranquility) tends to break one's ability to show anything about preservation of confidentiality or integrity (particularly in the absence of complete revocation support).
On the new files issue, it wouldn't be difficult or even a real divergence from our existing model to introduce the component name (not a "full" pathname, but the last component) as an additional input to the decision for labeling new files (along with the existing use of the creating process' label, the parent directory label, and the kind of new file) at creation time, and that would reduce the need somewhat to modify some applications that create files of multiple security contexts in the same directory. That would further help the SEEdit folks in emulating AA on top of SELinux, but as before, I don't get the impression that the AA folks will ever be satisfied with such an emulation, not because of any real requirement but merely because they are tied to their implementation specifics.
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 20 lines · Stephen Smalley
Quoted 10 lines · Andreas Gruenbacher
Quoted 8 lines · Pavel Machek
Hi!
Quoted 2 lines
Pathname matching, transition table loading, profile loading and manipulation.
So we get small interpretter of state machines, and reason we need is is 'apparmor is misdesigned and works with paths when it should have worked with handles'.
Quoted 8 lines
I assume you mean labels instead of handles.
AppArmor's design is around paths not labels, and independent of whether or not you like AppArmor, this design leads to a useful security model distinct from the SELinux security model (which is useful in its own ways). The differences between those models cannot be argued away, neither is a subset of the other, and neither is a misdesign. I would be thankful if you could stop spreading this lie.
Quoted 11 lines
I have a hard time distinguishing AppArmor's "model" from its implementation; every time we suggest that one might emulate much of AppArmor's functionality on SELinux (as in SEEdit), someone points to a specific characteristic of the AppArmor implementation that cannot be emulated in this manner. But is that implementation characteristic an actual requirement or just how it happens to have been done to date in AA? And I get the impression that even if we extended SELinux in certain ways to ease such emulation, the AA folks would never be satisfied because the implementation would still differ. Can we separate the desired functionality and actual requirements from the implementation specifics?
That's a really good point, is there a description of the AA "model" anywhere that we could see to determine if there really is a way to possibly use the current SELinux internals to show this model to the user?
thanks,
greg k-h
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Stephen SmalleyQuoted 10 lines · Stephen Smalley
Quoted 5 lines · Andreas Gruenbacher
[...] SELinux turns pathnames into labels when it
initially labels all files (when a policy is rolled out), whereas AppArmor computes the "label" of each file when a file is opened. The two models start to diverge as soon as files are renamed: in SELinux, labels stick with the files. In AppArmor, "labels" stick with the names.I'd argue a bit with that characterization, given that:
- in the case of SELinux, the pathname is never used as a basis for decisions by the kernel,
Indeed, the kernel component of SELinux only uses file labels for making file access decisions and not pathnames. But those labels were initially created by a trusted process (e.g. restorecon) based on pathnames, and this initial labeling is an essential part of the SELinux model. So in a sense, disregarding creation and relabeling of files, one could argue that SELinux makes decisions based on the pathnames that files had when they were labeled.
In SELinux, labels are the only thing that distinguishes between files. So if at one point you find that you need to distinguish between files that share a label, you have to split the label and reclassify the files in addition to adjusting the policy. Again, the usual approach for reclassifying files will probably be pathname based.
In contrast, AppArmor does not use labels, and the pathnames at the time of access distinguish between files. Since files do not have labels, no relabeling is necessary in order to change policy.
Quoted 5 lines
- under AA, each file may have an arbitrary set of "labels" or "policies" applied to it depending on what programs are accessing it and what names are being used to reference it - there is no system view of the subjects and objects and thus no way to identify the overall system policy for a given file.
Look at it this way: under SELinux, the set of files that share a label forms an equivalence class -- they are all treated identically by the system's security policy. The rules in AppArmor profiles also define equivalence classes in the sense that they partition the filesystem namespace into sets of files that are treated identically, but this classification is not explicit -- the entire rule base contributes to the classification. This doesn't mean that there is not a global policy, just that the policy is modeled differently. The equivalence classes are not directly obvious from the AA profiles.
Contrast this with SEEdit, which compiles AA-style rules into labels (and thus equivalence classes). The resulting SELinux policy is a static snapshot that cannot easily accommodate rule base changes, is more limited with respect to new files (which would likely be fixable), and behaves differently in complex ways with file renames. What's more, most likely the compiled policy will be anywhere from very hard to impossible to analyze, so you pretty much lose on all ends.
I'm not saying that labels are crap and that SELinux is wrong. In fact, labels are useful for some things like model verification and information flow analysis. What I'm saying is that AppArmor and SELinux implement different models, and those models cannot be modeled in terms of each other.
Note that I'm not embarking on implementation aspects here at all, only on the fundamental model differences.
Quoted 1 line
- names are far less tranquil than labels.
If I'm getting things right, a tranquil system with respect to labels would be one that does not permit re-labeling, while a tranquil system with respect to path names would be one that does not permit renaming. Both approaches would buy greater analyzability with reduced usability, and both seem unrealistic to me. SELinux and AppArmor evidently have different goals, and tranquility is more important to SELinux.
AppArmor is meant to be relatively easy to understand, manage, and customize, and introducing a labels layer wouldn't help these goals. SELinux is applicable in areas where AppArmor is not (e.g., MLS), but this comes at a cost. For me the question is not SELinux or AppArmor, but if AppArmor's security model is a good solution in common scenarios. In my opinion, AppArmor is a better answer than SELinux in a number of scenarios. This gives it value, nonwithstanding the fact that SELinux can be taken further.
Thanks,
Andreas
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 2 lines · Andreas Gruenbacher
AppArmor is meant to be relatively easy to understand, manage, and customize, and introducing a labels layer wouldn't help these goals.
Woah, that describes the userspace side of AA just fine, it means nothing when it comes to the in-kernel implementation. There is no reason that you can't implement the same functionality using some totally different in-kernel solution if possible.
Quoted 6 lines
SELinux is applicable in areas where AppArmor is not (e.g., MLS), but this comes at a cost. For me the question is not SELinux or AppArmor, but if AppArmor's security model is a good solution in common scenarios. In my opinion, AppArmor is a better answer than SELinux in a number of scenarios. This gives it value, nonwithstanding the fact that SELinux can be taken further.
I am still not completely certian that we can not properly implement AA functionality using a SELinux backend solution. Yes, the current tools that try to implement this are still lacking, and maybe the kernel needs to change, but that is possible.
I still want to see a definition of the AA "model" that we can then use to try to implement using whatever solution works best. As that seems to be missing the current argument of if AA can or can not be implemented using SELinux or something totally different should be stopped.
So, AA developers, do you have such a document anywhere? I know there are some old research papers, do they properly describe the current model you are trying to implement here?
thanks,
greg k-h
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 27 lines · Greg KH
Quoted 2 lines · Andreas Gruenbacher
AppArmor is meant to be relatively easy to understand, manage, and customize, and introducing a labels layer wouldn't help these goals.
Woah, that describes the userspace side of AA just fine, it means nothing when it comes to the in-kernel implementation. There is no reason that you can't implement the same functionality using some totally different in-kernel solution if possible.
Quoted 6 lines
SELinux is applicable in areas where AppArmor is not (e.g., MLS), but this comes at a cost. For me the question is not SELinux or AppArmor, but if AppArmor's security model is a good solution in common scenarios. In my opinion, AppArmor is a better answer than SELinux in a number of scenarios. This gives it value, nonwithstanding the fact that SELinux can be taken further.
I am still not completely certian that we can not properly implement AA functionality using a SELinux backend solution. Yes, the current tools that try to implement this are still lacking, and maybe the kernel needs to change, but that is possible.
I still want to see a definition of the AA "model" that we can then use to try to implement using whatever solution works best. As that seems to be missing the current argument of if AA can or can not be implemented using SELinux or something totally different should be stopped.
Quoted 3 lines
So, AA developers, do you have such a document anywhere? I know there are some old research papers, do they properly describe the current model you are trying to implement here?
Greg,
to implement the AA approach useing SELinux you need to have a way that
files that are renamed or created get tagged with the right label automaticaly with no possible race condition.
If this can be done then it _may_ be possible to do the job that AA is aimed at with SELinux, but the work nessasary to figure out what lables are needed on what file would still make it a non-trivial task.
as I understand it SELinux puts one label on each file, so if you have three files accessed by two programs such that program A accesses files X Y
program B accesses files Y Z
then files X Y and Z all need seperate labels with the policy stateing that program A need to access labels X, Y and program B needs to access files Y Z
extended out this can come close to giving each file it's own label. AA essentially does this and calls the label the path and computes it at runtime instead of storing it somewhere.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
Hello.
Quoted 5 lines · David Lang
as I understand it SELinux puts one label on each file, so if you have three files accessed by two programs such that
program A accesses files X Y
program B accesses files Y Z
Quoted 7 lines
then files X Y and Z all need separate labels with the policy stateing that program A need to access labels X, Y and program B needs to access files Y Z
extended out this can come close to giving each file it's own label. AA essentially does this and calls the label the path and computes it at runtime instead of storing it somewhere.
I tried to give each file it's own label, but I couldn't do it.
http://sourceforge.jp/projects/tomoyo/document/nsf2003-en.pdf
There are many elements that forms too strong barrier between pathname and labels, such as bind-mounts, hard links, newly created files, renamed files, temporary files and so on. So I gave up giving each file a label that can be used as an identifier,
and took an approach to forbid unneeded mount operations, unneeded link operations, unneeded renaming operations to keep the pathname represent it's own identifier as much as possible.
Thanks.
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
Quoted 1 line · On Sat, 9 Jun 2007 11:01:41 +0900 Tetsuo Handa <[email protected]>…
From the discussion so far, it seems that the different "model" that AA
is trying to implement, is to do in one step what SELinux does in two steps; that is trying to combine labelling and enforcement into a single step. If this is so, then why can't it just feed its automatic labelling into SELinux enforcement code?
Quoted 2 lines
I tried to give each file it's own label, but I couldn't do it.
http://sourceforge.jp/projects/tomoyo/document/nsf2003-en.pdf
That paper seems entirely focused on the automatic generation of policy,
and doesn't seem to help the discussion along. For instance, there may
be a way to implement AA on top of SELinux _without_ giving each and every file its own label.
Quoted 5 lines
There are many elements that forms too strong barrier between pathname and labels, such as bind-mounts, hard links, newly created files, renamed files, temporary files and so on. So I gave up giving each file a label that can be used as an identifier,
and took an approach to forbid unneeded mount operations, unneeded link operations, unneeded renaming operations to keep the pathname represent it's own identifier as much as possible.
AA must have a function that decides the security rights for any given path in order to make its enforcement decisions. It must surely be able to deal with all those things you listed above (bind-mounts,hard links etc). So why can't those decisions be turned into labels that are fed into SELinux enforcement code?
Sean.
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
Quoted 25 lines · Sean
On Sat, 9 Jun 2007 11:01:41 +0900
Tetsuo Handa <[email protected]> wrote:From the discussion so far, it seems that the different "model" that AA is trying to implement, is to do in one step what SELinux does in two steps; that is trying to combine labelling and enforcement into a single step. If this is so, then why can't it just feed its automatic labelling into SELinux enforcement code?
Quoted 2 lines
I tried to give each file it's own label, but I couldn't do it.
http://sourceforge.jp/projects/tomoyo/document/nsf2003-en.pdfThat paper seems entirely focused on the automatic generation of policy,
and doesn't seem to help the discussion along. For instance, there may
be a way to implement AA on top of SELinux _without_ giving each and every file its own label.Quoted 5 lines
There are many elements that forms too strong barrier between pathname and labels, such as bind-mounts, hard links, newly created files, renamed files, temporary files and so on. So I gave up giving each file a label that can be used as an identifier,
and took an approach to forbid unneeded mount operations, unneeded link operations, unneeded renaming operations to keep the pathname represent it's own identifier as much as possible.
Quoted 5 lines
AA must have a function that decides the security rights for any given path in order to make its enforcement decisions. It must surely be able to deal with all those things you listed above (bind-mounts,hard links etc). So why can't those decisions be turned into labels that are fed into SELinux enforcement code?
with AA hardlinks are effectivly different labels on the same file
one of the big problems with SELinux is what label to put on new files (including temp files), the AA approach avoids this (frequent) problem entirely. In exchange AA picks up the (infrequent) problems of bind-mounts and hard-link creation. People have tried to equate these prolems to show that AA has just as many problems as SELinux, but you can run systems for decades without creating hard-links or bind-mounts
also you seriously misunderstand the AA approach
AA does NOT try to create a security policy for every file on the system.
Instead AA policies are based on specific programs, and each policy states what files that program is allowed to access.
if you are useing AA to secure all exposed services on a box you don't have to try to write a policy to describe what gcc is allowed to access (unless through policy you give one of your exposed services permission to run gcc, and even then I'm not sure if gcc would inherit restrictions from it's parent or just use it's own)
the resulting policy is much easier to understand (and therefor check) becouse it is orders of magnatude smaller then any comprehensive SELinux policy.
the AA policy is also much easier to understand becouse you can look at it in pieces, understand that piece, and then forget it and move on to the next piece.
for example, if you write a policy for apache that limits it's access to it's log files, install directories, and document root. then you write a policy for your log analysis tool to access it's libraries, report directories (under the apache document root) and the apache log files (read only), these two policies are independant, you don't have to think about one while creating the other (which you would have to do if you had to put one label on apache binaries, another on normal web documents, a third on the reports, a fourth on the log files, and a fifth on the binaries for the log analysis tool. and this is ignoring any overlap in libraries!)
AA also lets a sysadmin dip their toe in the water and just write a policy for Apache, not for anything else, then write a policy for firefox, then write a policy for their mail client, then for bittorrent, etc. there is no need or push to try and secure everything all at once, and no need to re-label files (and change any policies that used the old labels) when you discover a new interaction.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
Quoted 2 lines · On Fri, 8 Jun 2007 21:56:06 -0700 (PDT) [email protected] wrote:
with AA hardlinks are effectivly different labels on the same file
So what? SELinux can be be altered to accept whatever label you generate. Pass it whatever label you want.
Quoted 6 lines
one of the big problems with SELinux is what label to put on new files (including temp files), the AA approach avoids this (frequent) problem entirely. In exchange AA picks up the (infrequent) problems of bind-mounts and hard-link creation. People have tried to equate these prolems to show that AA has just as many problems as SELinux, but you can run systems for decades without creating hard-links or bind-mounts
You are thinking about the way SELinux operates today, not how it might
operate to accommodate AA inclusion in the kernel. Instead of SELinux
always obtaining labels from file attributes, it could ask AA for them and you could generate them however you like.
Quoted 6 lines
also you seriously misunderstand the AA approach
AA does NOT try to create a security policy for every file on the system.
Instead AA policies are based on specific programs, and each policy states what files that program is allowed to access.
please read a bit more carefully, I was responding to someone else who made that claim.
Quoted 6 lines
if you are useing AA to secure all exposed services on a box you don't have to try to write a policy to describe what gcc is allowed to access (unless through policy you give one of your exposed services permission to run gcc, and even then I'm not sure if gcc would inherit restrictions from it's parent or just use it's own)
Quoted 8 lines
the resulting policy is much easier to understand (and therefor check) becouse it is orders of magnatude smaller then any comprehensive SELinux policy.
the AA policy is also much easier to understand becouse you can look at it in pieces, understand that piece, and then forget it and move on to the next piece.
Nobody is asking you to change the AA policy file. It lives in user space. But i fail to see the problem in translating it into SELinux terms for the user transparently.
Quoted 10 lines
for example, if you write a policy for apache that limits it's access to it's log files, install directories, and document root. then you write a policy for your log analysis tool to access it's libraries, report directories (under the apache document root) and the apache log files (read only), these two policies are independant, you don't have to think about one while creating the other (which you would have to do if you had to put one label on apache binaries, another on normal web documents, a third on the reports, a fourth on the log files, and a fifth on the binaries for the log analysis tool. and this is ignoring any overlap in libraries!)
Again, try to think outside the box a bit. This isn't about using SELinux as it exists today. But imagine an SELinux that would ask you to supply a security label for each file _instead_ of looking up that label itself. Wouldn't that let you implement everything you wanted while still using much of the SELinux infrastructure that is already in the kernel?
Quoted 6 lines
AA also lets a sysadmin dip their toe in the water and just write a policy for Apache, not for anything else, then write a policy for firefox, then write a policy for their mail client, then for bittorrent, etc. there is no need or push to try and secure everything all at once, and no need to re-label files (and change any policies that used the old labels) when you discover a new interaction.
And so it could remain; this is about implementation, not model.
Sean
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Greg KHQuoted 5 lines · Greg KH
I still want to see a definition of the AA "model" that we can then use to try to implement using whatever solution works best. As that seems to be missing the current argument of if AA can or can not be implemented using SELinux or something totally different should be stopped.
the way I would describe the difference betwen AA and SELinux is:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
AA is like a default deny firewall, you describe what you want to happen, and it blocks everything else without you even having to realize that it's there.
now I know that this isn't a perfect analyogy, that SELinux doesn't allow something to happen unless it's been told to let it, but in terms of complexity and the amount of work to configure things I think the analogy is close.
the fact that the SELinux policy _will_ affect the entire systems means one of two things.
1. you have a policy that exactly describes how every part of the system operates
or
2. you have a policy that's exessivly permissive in some parts of the system becouse 'that works' and you either don't understand that part of the system well enough, or don't have time to write a more complete policy.
I would argue that with the number of files on a system nowdays (483,000 on my 'minimalistic' gentoo server, 442,000 on my slackware laptop, 800,000 on a ubuntu server at work) it's not possible to do #1, so any deployed policy (especially one done by a disto that needs to work for all it's users) is going to follow #2, frequently to the point where it's not really adding much security.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
In reply to SeanQuoted 22 lines · Sean
Quoted 4 lines
the AA policy is also much easier to understand becouse you can look at it in pieces, understand that piece, and then forget it and move on to the next piece.
Nobody is asking you to change the AA policy file. It lives in user space. But i fail to see the problem in translating it into SELinux terms for the user transparently.
Quoted 10 lines
for example, if you write a policy for apache that limits it's access to it's log files, install directories, and document root. then you write a policy for your log analysis tool to access it's libraries, report directories (under the apache document root) and the apache log files (read only), these two policies are independant, you don't have to think about one while creating the other (which you would have to do if you had to put one label on apache binaries, another on normal web documents, a third on the reports, a fourth on the log files, and a fifth on the binaries for the log analysis tool. and this is ignoring any overlap in libraries!)
Quoted 5 lines
Again, try to think outside the box a bit. This isn't about using SELinux as it exists today. But imagine an SELinux that would ask you to supply a security label for each file _instead_ of looking up that label itself. Wouldn't that let you implement everything you wanted while still using much of the SELinux infrastructure that is already in the kernel?
so are you suggesting that SELinux would call out to userspace for every file open to get the label for that file?
just off the top of my head
what would all these kernel->userspace->kernel transitions do to performance?
would SELinux give userspace the full path to that file?
if so wouldn't it have to implement most of what AA adds to do this?
if not how would userspace figure out what label to hand back without this info?
how would SELinux figure out the permissions for the userspace Daemon?
how would you change both the rules for labels in the kernel and the policy for assigning labels in userspace without any race conditions?
Quoted 8 lines
Quoted 6 lines
AA also lets a sysadmin dip their toe in the water and just write a policy for Apache, not for anything else, then write a policy for firefox, then write a policy for their mail client, then for bittorrent, etc. there is no need or push to try and secure everything all at once, and no need to re-label files (and change any policies that used the old labels) when you discover a new interaction.
And so it could remain; this is about implementation, not model.
yes, you could add all the AA code to SELinux and then say that the result is implemented in SELinux, you may even save a little bit of code in some parts of it (but I would argue that you add more code in others, say for the userpace interface and userspace labeling code), but the result wouldn't be in the spirit of SELinux.
it may be possible to write something that resembles AA in SELinux policy (once you solve the problem of how to label newly created files securely), but it's also possible to write a webserver in COBOL to run out of inetd, that doesn't mean that it makes any sort of sense to do either one.
on the other hand, it may be a good idea. let's see how people really use AA once they have it available and the SELinux folks can work on duplicating the functionality, if they do then the existing AA interface could be phased out over time, or the internal implementation could change. but arguing that SELinux _may_ be able to do the job of AA _someday_ should not prevent AA from being included today (especially when so many of the SELinux developers are so opposed to the very concept of AA, which doesn't indicate that they are about to rush out and implement the pieces needed to make it work)
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
Quoted 3 lines · On Fri, 8 Jun 2007 22:38:57 -0700 (PDT) [email protected] wrote:
so are you suggesting that SELinux would call out to userspace for every file open to get the label for that file?
No, i'm not. You must already have a kernel function in the current implementation of AA that decides the proper policy for each path. Why not use it to feed labels into SELinux.
Sean
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 5 lines · On Fri, 8 Jun 2007 22:18:40 -0700 (PDT) [email protected] wrote:
the way I would describe the difference betwen AA and SELinux is:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
Quoted 8 lines
AA is like a default deny firewall, you describe what you want to happen, and it blocks everything else without you even having to realize that it's there.
now I know that this isn't a perfect analyogy, that SELinux doesn't allow something to happen unless it's been told to let it, but in terms of complexity and the amount of work to configure things I think the analogy is close.
It must be drop dead simple to modify SELinux to be default-deny. That seems like it could be done in a small patch instead of requiring a huge new infrastructure.
Let's assume that everyone agrees that AA is a good idea. Which parts of it absolutely can't be implemented in terms of SELinux? SELinux isn't fixed in stone, it can be altered if necessary to accommodate AA (as in the example above of becoming default-deny).
Sean.
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
In reply to SeanQuoted 7 lines · Sean
Quoted 3 lines
so are you suggesting that SELinux would call out to userspace for every file open to get the label for that file?
No, i'm not. You must already have a kernel function in the current implementation of AA that decides the proper policy for each path. Why not use it to feed labels into SELinux.
if it was this easy just have SELinux set the label == path
you first need to figure out what the path is. right now this can't be done, the AA paches provide this capability.
second, the AA policies aren't based just on the path, they are based on the program accessing the path, then the path. you can have two different policies for two different programs accessing the same path, but for most programs (although, not nessasarily most activity) there will be no policy, and therefor no need to check the path.
but even if you did these things, why would it be an advantage to use a mechanism to create a dummy label and pass it off to different code rather then just decideing at that point? once the AA code knows what the policy for this path is for this program (which it would need to know to set the label) how is it a win to pass this off to another chunk of code? you would also need to make sure that the SELinux code didn't try to cache the label for future use either, becouse in the future the access may be from another program and so the policy that's needed is different.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to SeanQuoted 12 lines · Sean
Quoted 9 lines · On Fri, 8 Jun 2007 22:18:40 -0700 (PDT) [email protected] wrote:
the way I would describe the difference betwen AA and SELinux is:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
AA is like a default deny firewall, you describe what you want to happen, and it blocks everything else without you even having to realize that it's there.
Quoted 8 lines
Quoted 4 lines
now I know that this isn't a perfect analyogy, that SELinux doesn't allow something to happen unless it's been told to let it, but in terms of complexity and the amount of work to configure things I think the analogy is close.
It must be drop dead simple to modify SELinux to be default-deny. That seems like it could be done in a small patch instead of requiring a huge new infrastructure.
did you read my explination of the analogy?
Quoted 4 lines
Let's assume that everyone agrees that AA is a good idea. Which parts of it absolutely can't be implemented in terms of SELinux? SELinux isn't fixed in stone, it can be altered if necessary to accommodate AA (as in the example above of becoming default-deny).
what SELinux cannot do is figure out what label to assign a new file.
but the bigger problem in changing SELinux to behave like AA is that the SELinux people disagree with the concept of AA. they don't believe that it's secure, so why would they add useless bloat that would only complicate their code and make systems less secure? I don't happen to agree with their opinion of AA obviously, but they have the right to their opinion, and it is their code. why should they be asked to implement and support something they disagree with so fundamentally?
remember that the security hooks in the kernel are not SELinux API's, they are the Loadable Security Model API. What the AA people are asking for is for the LSM API to be modified enough to let their code run (after that (and working in parallel) they will work on getting the rest of their code approved for the kernel, but the LSM hooks are the most critical)
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
In reply to [email protected]Quoted 3 lines · On Sat, 9 Jun 2007 00:04:15 -0700 (PDT) [email protected] wrote:
if it was this easy just have SELinux set the label == path you first need to figure out what the path is. right now this can't be done, the AA paches provide this capability.
The question is: why not just extend SELinux to include AA functionality rather than doing a whole new subsystem. What exactly about AA demands an entire new infrastructure rather than just building on what already exists in the kernel?
Quoted 5 lines
second, the AA policies aren't based just on the path, they are based on the program accessing the path, then the path. you can have two different policies for two different programs accessing the same path, but for most programs (although, not nessasarily most activity) there will be no policy, and therefor no need to check the path.
It seems the main purported advantage of AA is it doesn't require maintaining labels on files etc. In fact, that's the only conceptual difference I can see other than a simpler policy file format. So why not just make an AA extension to SELinux that implements this main difference (ie. create labels on the fly).
Then have a userspace program that converts the pretty-peace-and-love AA policy file format into the baby-killing SELinux format and feed it into the kernel.
All of a sudden you've implemented the main features of AA with very few changes to the kernel. It should be more maintainable, and much easier to get accepted into the kernel.
Quoted 3 lines
but even if you did these things, why would it be an advantage to use a mechanism to create a dummy label and pass it off to different code rather then just decideing at that point?
Because it requires you to reimplement much of what is already in the kernel. It requires you to be able to understand an entire new policy mechanism instead of just piggybacking on what already exists.
Quoted 2 lines
once the AA code knows what the policy
for this path is for this program (which it would need to know to set the
Again you're only looking at the way the AA code is _today_. If it were
refactored to be an extension of SELinux, there would be no reason for the
AA kernel code to know any policy whatsoever. All it would need to know
is a path-to-label mapping. SELinux would then enforce the AA policy
that it received from your userspace tool that translates your native AA policy format into SELinux-lingo.
Quoted 1 line
label) how is it a win to pass this off to another chunk of code? you
It's a win because the policy enforcement code is already in the kernel. All you have to do is extend SELinux to create labels on the fly and provide a userspace tool to convert the nice AA policy files into something SELinux can use.
Quoted 3 lines
would also need to make sure that the SELinux code didn't try to cache the label for future use either, becouse in the future the access may be from another program and so the policy that's needed is different.
You seem to be quibbling over small little unimportant details and refusing
to part with your current implementation. It would seem the easiest way to
get the functionality you want into the kernel is to be a bit more flexible on implementation.
Sean
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 1 line · On Sat, 9 Jun 2007 00:13:22 -0700 (PDT) [email protected] wrote:
did you read my explination of the analogy?
It was a rather poor analogy i'm afraid. But the point i make still stands. So far you've failed to show any reason SELinux can't be reasonably extended to handle all the features you care about.
Quoted 1 line
what SELinux cannot do is figure out what label to assign a new file.
That would be the job of your AA extension.
Quoted 7 lines
but the bigger problem in changing SELinux to behave like AA is that the SELinux people disagree with the concept of AA. they don't believe that it's secure, so why would they add useless bloat that would only complicate their code and make systems less secure? I don't happen to agree with their opinion of AA obviously, but they have the right to their opinion, and it is their code. why should they be asked to implement and support something they disagree with so fundamentally?
So now you're claiming the real reason to let AA into the kernel is politics?
Just show that the feature can be easily added to SELinux and made as an option for the end user to choose and it should go a long way to silencing the opponents.
Quoted 6 lines
remember that the security hooks in the kernel are not SELinux API's, they are the Loadable Security Model API. What the AA people are asking for is for the LSM API to be modified enough to let their code run (after that (and working in parallel) they will work on getting the rest of their code approved for the kernel, but the LSM hooks are the most critical)
Remember that the SELinux API's essentially belong to everyone under the GPL. So its not an excuse for falling into NIH syndrome and putting a bunch of new stuff into the kernel that could instead be added as a small extension to what already exists.
Sean
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
In reply to SeanQuoted 19 lines · Sean
Quoted 3 lines · On Sat, 9 Jun 2007 00:04:15 -0700 (PDT) [email protected] wrote:
if it was this easy just have SELinux set the label == path you first need to figure out what the path is. right now this can't be done, the AA paches provide this capability.
The question is: why not just extend SELinux to include AA functionality rather than doing a whole new subsystem. What exactly about AA demands an entire new infrastructure rather than just building on what already exists in the kernel?
Quoted 5 lines
second, the AA policies aren't based just on the path, they are based on the program accessing the path, then the path. you can have two different policies for two different programs accessing the same path, but for most programs (although, not nessasarily most activity) there will be no policy, and therefor no need to check the path.
Quoted 5 lines
It seems the main purported advantage of AA is it doesn't require maintaining labels on files etc. In fact, that's the only conceptual difference I can see other than a simpler policy file format. So why not just make an AA extension to SELinux that implements this main difference (ie. create labels on the fly).
becouse the SELinux people don't want to have this in their code for one thing.
you seem to be ignoring the SELinux people who say that pathnames are fundamentally different from labels, labels stay with the data if the file is renamed, path names do not. multiple hard-links to the same file will always have the same label for SELinux, but could have very different permissions with AA
labels are part of policy, policy is not supposed to be decided by the kernel.
SELinux treats all files with the same label the same. to have the same ability to treat every file differntly that AA has SELinux would have to give every file a different label.
Quoted 7 lines
Then have a userspace program that converts the pretty-peace-and-love AA policy file format into the baby-killing SELinux format and feed it into the kernel.
All of a sudden you've implemented the main features of AA with very few changes to the kernel. It should be more maintainable, and much easier to get accepted into the kernel.
how will you know how many labels you need to put into your policy that you load into the kernel?
how will the kernel figure out what label to use for a file
and the userspace code that converts the policy needs to know the names when it feeds the policy into the kernel.
and you still need to implement the new LSM hooks that AA is asking for to figure out what the path to a file is.
Quoted 7 lines
Quoted 3 lines
but even if you did these things, why would it be an advantage to use a mechanism to create a dummy label and pass it off to different code rather then just decideing at that point?
Because it requires you to reimplement much of what is already in the kernel. It requires you to be able to understand an entire new policy mechanism instead of just piggybacking on what already exists.
the policy mechanism is supposed to be the LSM hooks, and AA is trying to re-use them.
Quoted 9 lines
Quoted 2 lines
once the AA code knows what the policy
for this path is for this program (which it would need to know to set theAgain you're only looking at the way the AA code is _today_. If it were
refactored to be an extension of SELinux, there would be no reason for the
AA kernel code to know any policy whatsoever. All it would need to know
is a path-to-label mapping. SELinux would then enforce the AA policy
that it received from your userspace tool that translates your native AA policy format into SELinux-lingo.
after you change SELinux to be able to do everything that AA does then you can tell SELinux to act like AA, true but irrelavent.
Quoted 7 lines
Quoted 1 line
label) how is it a win to pass this off to another chunk of code? you
It's a win because the policy enforcement code is already in the kernel. All you have to do is extend SELinux to create labels on the fly and provide a userspace tool to convert the nice AA policy files into something SELinux can use.
Quoted 8 lines
Quoted 3 lines
would also need to make sure that the SELinux code didn't try to cache the label for future use either, becouse in the future the access may be from another program and so the policy that's needed is different.
You seem to be quibbling over small little unimportant details and refusing
to part with your current implementation. It would seem the easiest way to
get the functionality you want into the kernel is to be a bit more flexible on implementation.
first off, and for the record, it's not _my_ implementation. I have nothing to do with writing AA.
I am just someone who manages hundreds of servers for which AA would be a good fit. In the past I've gone to a lot of effort to get less security then AA would provide to implement seperate services in seperate chroot sandboxes. I'm looking for easier and better options, I've looked at SELinux and don't believe that I can produce a reasonable policy in a reasonable amount of time (and I don't trust distro vendors to do it for me, they have to allow a lot of things that don't make sense on my systems, and I occasionally need to allow something that wouldn't make sense in the general case, let alone all the software I run that the disto doesn't know anything about)
chroot sandboxes, virtual machines, containers all have the problem that when you need to have more then one application interacting they need to be put togeather and the basic mechanism doesn't provide you any security against each other.
SELinux is aiming for 'perfect' security, I'll readily admit that, just like I'll admit that AA is only aiming for 'good enough' security, but that 'good enough' security would help me and I don't see any way to get to SELinux's 'perfect' security.
I also don't care about the details of how it gets implemented, but when the AA people have a working implementation, and the SELinux people are strongly opposed to the concept, I don't see any advantage in trying to get the AA people to throw away a lot of their working code to try and get people (many of who have be very insulting frankly) to accept such fandamental changes.
if the SELinux people had responded to the announcement of AA with "that's a nice idea, if we add these snippits from your code to SELinux then we can do the same thing" it would be a very different story.
but as always patches talk louder then anything else, if you believe that the efforts should be combined so strongly why don't you start submitting the appropriate patches to SELinux to make it able to do what AA does?
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to SeanQuoted 10 lines · Sean
Quoted 5 lines
remember that the security hooks in the kernel are not SELinux API's, they are the Loadable Security Model API. What the AA people are asking for is for the LSM API to be modified enough to let their code run (after that (and working in parallel) they will work on getting the rest of their code approved for the kernel, but the LSM hooks are the most critical)
Remember that the SELinux API's essentially belong to everyone under the GPL. So its not an excuse for falling into NIH syndrome and putting a bunch of new stuff into the kernel that could instead be added as a small extension to what already exists.
but the SELinux API's are not the core security API's in Linux, the LSM API's are. and AA is useing the LSM API's (extending them where they and SELinux don't do what's needed)
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 4 lines · On Sat, 9 Jun 2007 01:06:09 -0700 (PDT) [email protected] wrote:
but the SELinux API's are not the core security API's in Linux, the LSM API's are. and AA is useing the LSM API's (extending them where they and SELinux don't do what's needed)
Calling LSM "core" and pretending that SELinux can't do 90% of what you want doesn't change the facts on the ground. Clinging to the current AA implementation instead of honestly considering reasonable alternatives does not inspire confidence or teamwork.
Sean.
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
In reply to [email protected]Quoted 2 lines · On Sat, 9 Jun 2007 01:03:15 -0700 (PDT) [email protected] wrote:
becouse the SELinux people don't want to have this in their code for one thing.
Tuff nuggies to the SELinux people.. Show them code good enough they'd be embarrassed to reject.
Quoted 8 lines
you seem to be ignoring the SELinux people who say that pathnames are fundamentally different from labels, labels stay with the data if the file is renamed, path names do not. multiple hard-links to the same file will always have the same label for SELinux, but could have very different permissions with AA
labels are part of policy, policy is not supposed to be decided by the kernel.
Not sure why you're rehashing this. We all know that not everyone agrees with AA. The point is users should have a choice.. choice is good. But that's not a justification for bloating the kernel with a bunch of code that isn't needed and can be refactored into a small extension of what already exists.
Quoted 3 lines
SELinux treats all files with the same label the same. to have the same ability to treat every file differntly that AA has SELinux would have to give every file a different label.
I'm not convinced in practice you really need a unique label for every file. Large swathes of the system would have a shared label etc.. So let's not get caught up on theoretical arguments that don't really play in practice. Has anyone on the AA team actually _tried_ to extend SELinux instead of reinventing the wheel?
Quoted 4 lines
how will you know how many labels you need to put into your policy that you load into the kernel?
how will the kernel figure out what label to use for a file
The AA extension will have a path-to-label mapping. Conceptually that's exactly what its doing now. Look at the arguments by AA proponents that the only difference between the AA method and SELinux is _when_ those labels are created... Sorry i don't have a link handy, but i can dig one up if you dispute that this argument has been made by the AA folks.
Quoted 4 lines
and the userspace code that converts the policy needs to know the names when it feeds the policy into the kernel.
and you still need to implement the new LSM hooks that AA is asking for to figure out what the path to a file is.
True, but so what?
Quoted 2 lines
the policy mechanism is supposed to be the LSM hooks, and AA is trying to re-use them.
Who says that's what is "supposed" to be in all situations? It makes more
sense to target the best API that lets you implement the features you want.
Quoted 2 lines
after you change SELinux to be able to do everything that AA does then you can tell SELinux to act like AA, true but irrelavent.
BS. All we're talking about is an extension that allows SELinux to
generate labels on the fly. That goes most the way towards giving
you all the functionality you're after and should be a much smaller patch in the end, reusing code that already exists in the kernel.
Quoted 13 lines
first off, and for the record, it's not _my_ implementation. I have nothing to do with writing AA.
I am just someone who manages hundreds of servers for which AA would be a good fit. In the past I've gone to a lot of effort to get less security then AA would provide to implement seperate services in seperate chroot sandboxes. I'm looking for easier and better options, I've looked at SELinux and don't believe that I can produce a reasonable policy in a reasonable amount of time (and I don't trust distro vendors to do it for me, they have to allow a lot of things that don't make sense on my systems, and I occasionally need to allow something that wouldn't make sense in the general case, let alone all the software I run that the disto doesn't know anything about)
Whoa. Again you're mistaking the current state of SELinux, rather than
SELinux + AA extension. If such a beast provides the same features you
get with the current AA implementation, why would you care?
Quoted 9 lines
chroot sandboxes, virtual machines, containers all have the problem that when you need to have more then one application interacting they need to be put togeather and the basic mechanism doesn't provide you any security against each other.
SELinux is aiming for 'perfect' security, I'll readily admit that, just like I'll admit that AA is only aiming for 'good enough' security, but that 'good enough' security would help me and I don't see any way to get to SELinux's 'perfect' security.
All that is irrelevant to the discussion though.
Quoted 6 lines
Also don't care about the details of how it gets implemented, but when the AA people have a working implementation, and the SELinux people are strongly opposed to the concept, I don't see any advantage in trying to get the AA people to throw away a lot of their working code to try and get people (many of who have be very insulting frankly) to accept such fandamental changes.
But "working implementation" is _not_ the criteria for acceptance into the kernel and that's what this discussion is about.
Quoted 3 lines
if the SELinux people had responded to the announcement of AA with "that's a nice idea, if we add these snippits from your code to SELinux then we can do the same thing" it would be a very different story.
To be fair, that's not their job.
Quoted 3 lines
but as always patches talk louder then anything else, if you believe that the efforts should be combined so strongly why don't you start submitting the appropriate patches to SELinux to make it able to do what AA does?
Because i'm not the one trying to get something into the kernel. I'm not the one who has to show that my patches are reasonable and make best use of the current kernel infrastructure possible.
Sean
Re: [AppArmor 39/45] AppArmor: Profile loading andmanipulation,pathname matching
In reply to SeanQuoted 3 lines · Sean
All of a sudden you've implemented the main features of AA with very few changes to the kernel. It should be more maintainable, and much easier to get accepted into the kernel.
Do you agree with passing "struct vfsmount" to VFS helper functions and LSM hooks and introducing d_namespace_path() so that the AA extension can calculate the requested pathname and map the requested pathname to SELinux's labels?
Re: [AppArmor 39/45] AppArmor: Profile loading andmanipulation,pathname matching
Quoted 8 lines · On Sat, 9 Jun 2007 20:26:57 +0900 Tetsuo Handa <[email protected]>…
Quoted 3 lines · Sean
All of a sudden you've implemented the main features of AA with very few changes to the kernel. It should be more maintainable, and much easier to get accepted into the kernel.
Do you agree with passing "struct vfsmount" to VFS helper functions and LSM hooks and introducing
d_namespace_path()so that the AA extension can calculate the requested pathname and map the requested pathname to SELinux's labels?
Frankly i'm not in a position to judge, but if that's the best way to provide the desired functionality, then it sounds good. But please make sure you bounce this all off someone who actually knows what they're talking about. ;o) Really I was just casually following along this ongoing conversation and had a more conceptual/design question about how things were implemented. A few people explained how AA labelling at "runtime" wasn't conceptually very different than what SELinux did. All that begged the question as to why that functionality couldn't just be tacked on to SELinux?
Sean
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Andreas GruenbacherHi!
Quoted 10 lines
Quoted 3 lines
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course. /proc only uses pathnames in a few places, but
/proc/mountswill silently fail and produce garbage entries. That's not ideal of course; we should fix that somehow.Note that this has nothing to do with the AppArmor discussion ...
This has everything to do with AA discussion. You took unreliable, for-user-info kernel subsystem, and made security subsystem depend on it. Oops.
Quoted 5 lines
Quoted 2 lines
Perhaps vfs should be modified not to allow such crazy paths? But placing limit in aa is ugly.
Dream on. Redefining fundamental vfs semantics is not an option; we should rather make sure that we fail gracefully. Considering the
And instead of fixing "too long pathnames are problem in kernel" in any clean way, you "simply" included configurable-but-impossible-to-configure-right limit into apparmor. And now you want that merged. Dream on.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 39/45] AppArmor: Profile loading andmanipulation,pathname matching
In reply to SeanQuoted 12 lines · Sean
Quoted 8 lines · On Sat, 9 Jun 2007 20:26:57 +0900 Tetsuo Handa <[email protected]>…
Quoted 3 lines · Sean
All of a sudden you've implemented the main features of AA with very few changes to the kernel. It should be more maintainable, and much easier to get accepted into the kernel.
Do you agree with passing "struct vfsmount" to VFS helper functions and LSM hooks and introducing
d_namespace_path()so that the AA extension can calculate the requested pathname and map the requested pathname to SELinux's labels?
Quoted 8 lines
Frankly i'm not in a position to judge, but if that's the best way to provide the desired functionality, then it sounds good. But please make sure you bounce this all off someone who actually knows what they're talking about. ;o) Really I was just casually following along this ongoing conversation and had a more conceptual/design question about how things were implemented. A few people explained how AA labelling at "runtime" wasn't conceptually very different than what SELinux did. All that begged the question as to why that functionality couldn't just be tacked on to SELinux?
Sean,
since you aren't in a position to judge what's acceptable and I'm not in
a position to change code our exchange is pointless.
I apologize to the list for the excessive messasges.
David Lang
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Pavel MachekQuoted 4 lines · Pavel Machek
Quoted 4 lines
Quoted 3 lines
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Quoted 8 lines
Quoted 6 lines
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course. /proc only uses pathnames in a few places, but
/proc/mountswill silently fail and produce garbage entries. That's not ideal of course; we should fix that somehow.Note that this has nothing to do with the AppArmor discussion ...
This has everything to do with AA discussion.
How pathnames are used in /proc has *nothing* to do with AppAmor.
Quoted 2 lines
You took unreliable, for-user-info kernel subsystem, and made security subsystem depend on it. Oops.
Wrong. I said that the kernel uses pathnames for informational purposes only, not that they are unreliable. Don't turn words around in my mouth. The dcache has a *very* precise idea of pathnames, because this is what it is there for.
The pathnames that d_path computes exactly reflect what the dcache knows. (This requires the d_path fixes that are included in the AppArmor series and have also been posted independently, and the reasons why these fixes are needed are well explained in the mails.) The resulting pathnames are anything but unreliable.
Quoted 10 lines
Quoted 5 lines
Quoted 2 lines
Perhaps vfs should be modified not to allow such crazy paths? But placing limit in aa is ugly.
Dream on. Redefining fundamental vfs semantics is not an option; we should rather make sure that we fail gracefully. Considering the
And instead of fixing "too long pathnames are problem in kernel" in any clean way, you "simply" included configurable-but-impossible-to-configure-right limit into apparmor. And now you want that merged. Dream on.
The choice here is between the following two things: either allow pathnames to grow to arbitrary lengths, or cap them at some point. The former has the benefit of working with all possible pathnames, and the drawback that users can consume arbitrary amounts of kernel memory by constructing arbitrarily long paths.
The latter has the benefit of limiting the amount of kernel memory consumed to some upper bound, and the drawback that access to exceptionally long paths (beyond the arbitrary limit) is denied. So in a sense, the limit in AppArmor does what you are asking for: it modifies things so that the kernel does not allow "such crazy paths". It does not introduce this as a vfs limit though, because the vfs has all right not to care.
Andreas
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Greg KHQuoted 8 lines · Greg KH
Quoted 2 lines · Andreas Gruenbacher
AppArmor is meant to be relatively easy to understand, manage, and
customize, and introducing a labels layer wouldn't help these goals.Woah, that describes the userspace side of AA just fine, it means nothing when it comes to the in-kernel implementation. There is no reason that you can't implement the same functionality using some totally different in-kernel solution if possible.
I agree that the in-kernel implementation could use different abstractions than user-space, provided that the underlying implementation details can be hidden well enough. The key phrase here is "if possible", and in fact "if possible" is much too strong: very many things in software are possible, including user-space drives and a stable kernel module ABI. Some things make sense; others are genuinely bad ideas while still possible.
The things in my reply you chose not to quote make up the essential part of the model, argue why mapping from an AppArmor-like user-space to a label-based in-kernel model is fundamentally hard, how implementation details cannot be hidden, and how such a mapping would lead to disadvantages no matter which way you look at it.
Quoted 11 lines
Quoted 6 lines
SELinux is applicable in areas where AppArmor is not (e.g., MLS), but this comes at a cost. For me the question is not SELinux or AppArmor, but if AppArmor's security model is a good solution in common scenarios. In my opinion, AppArmor is a better answer than SELinux in a number of scenarios. This gives it value, nonwithstanding the fact that SELinux can be taken further.
I am still not completely certian that we can not properly implement AA functionality using a SELinux backend solution. Yes, the current tools that try to implement this are still lacking, and maybe the kernel needs to change, but that is possible.
I did not pull all of this out of my hat ad hoc. The AppArmor team spent a fair amount of time researching various ways how AppArmor-like semantics could be implemented on top of SELinux, as well as ways how AppArmor could be implemented better. We *really* tried hard. The reason why we are still proposing this non-SELinux approach is because none of the alternatives worked out.
If things were as simple as mapping an AppArmor frontend to the SELinux backend, even with extensions to the SELinux backend (and I know that it wouldn't be impossible to extend SELinux in reasonable ways), this would indeed be nice. The issues that SEEdit is having unfortunately only confirm what we were already certain to know: it just doesn't work.
Quoted 5 lines
I still want to see a definition of the AA "model" that we can then use to try to implement using whatever solution works best. As that seems to be missing the current argument of if AA can or can not be implemented using SELinux or something totally different should be stopped.
There is no need to start all over implementing something from scratch. People have already tried emulating AppArmor on top of SELinux, and SEEdit is the current best result. All it takes is the time to understand the SELinux and AppArmor models. From there it is not hard to see that SEEdit does the best it can do, and how it is just not a good idea. There are a few things that could be improved with additional SELinux in-kernel infrastructure, but the fundamental problems remain. It just remains a very bad idea.
Quoted 3 lines
So, AA developers, do you have such a document anywhere? I know there are some old research papers, do they properly describe the current model you are trying to implement here?
We wrote an AppArmor technical documentation, and it was posted as part of the last two AppArmor submissions. It describes the model, and how the model is implemented. If you need a better description of the model, let us know how we can improve it.
http://forgeftp.novell.com//apparmor/LKML_Submission-May_07/techdoc.pdf
What you'll not find in there is a detailed comparison between AppArmor and SELinux, and the problems that emulating AppArmor on top of SELinux would cause -- some details on that can be found in the SEEdit documentation, and in addition, this LKML thread seems best to me at the moment.
Andreas
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to SeanQuoted 2 lines · Sean
Clinging to the current AA implementation instead of honestly considering reasonable alternatives does not inspire confidence or teamwork.
What you imply is pretty insulting. I can assure you we looked into many possible implementation choices, and we considered a fair number of alternatives.
Have you seen the ``AppArmor FAQ'' thread?
Andreas
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 5 lines · [email protected] wrote:
On Sat, 9 Jun 2007, Sean wrote:
<snip>what SELinux cannot do is figure out what label to assign a new file.
Nit: SELinux figures out what to label new files fine, just not based on the name. This works in most cases, eg., when user_t creates a file in /tmp it becomes user_tmp_t, incidentally this is something that AA cannot handle, if the filenames aren't normalized (they normally aren't). For example, my ssh agent socket is stored in /tmp/ssh-XXXXXXXX, where the X's are random characters, AA can't differentiate admin ssh agents from unprivileged user ssh agents, showing a serious flaw in their model.
The complaint is that name-based labeling doesn't currently exist (and as Sean has stated that doesn't mean it _can't_ exist, just that it doesn't currently). In practice this has not been as big of an issue as you are making it out to be. Granted restorecond has a tiny race, and I wouldn't recommend using it on very security sensitive files but for usability having it relabel user_home_t to user_http_content_t isn't a problem (and causes no security issues).
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 1 line · On Jun 09, 2007, at 01:18:40, [email protected] wrote:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
WRONG. You clearly don't understand SELinux at all. Try booting in enforcing mode with an empty policy file (well, not quite empty, there are a few mandatory labels you have to create before it's a valid policy file). /sbin/init will load the initial policy, attempt to re-exec() itself... and promptly grind to a halt. End-of-story.
Typical "targetted" policies leave all user logins as unrestricted, adding security for daemons but not getting in the way of users who would otherwise turn SELinux off. On the other hand, a targeted policy has a "trusted" type for user logins which is explicitly allowed access to everything.
That said, if you actually want your system to *work* with any default-deny policy then you have to describe EVERYTHING anyways. How exactly do you expect AppArmor to "work" if you don't allow users to run "/bin/passwd", for example.
Cheers,
Kyle Moffett
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Andreas GruenbacherQuoted 7 lines · On Sat, 9 Jun 2007 17:17:57 +0200 Andreas Gruenbacher <[email protected]> wrote:
Quoted 2 lines · Sean
Clinging to the current AA implementation instead of honestly considering reasonable alternatives does not inspire confidence or teamwork.
What you imply is pretty insulting. I can assure you we looked into many possible implementation choices, and we considered a fair number of alternatives.
Sorry for any unintended insult. The comment was meant solely for the person with whom i was talking who seemed only to be cheerleading for inclusion in current form without being willing or able to discuss alternative implementations.
Sean.
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Kyle MoffettQuoted 4 lines · Kyle Moffett
Quoted 1 line · On Jun 09, 2007, at 01:18:40, [email protected] wrote:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
WRONG. You clearly don't understand SELinux at all. Try booting in enforcing mode with an empty policy file (well, not quite empty, there are a few mandatory labels you have to create before it's a valid policy file).
/sbin/initwill load the initial policy, attempt to re-exec() itself... and promptly grind to a halt. End-of-story.
sigh,
two paragraphs below what you quoted I acknowledged exactly what you
state. however since you must tag everything before you turn on any security it seems to me that you have to define everything, which is a similar amount of work as you would have to do for a default allow policy.
Quoted 1 line
Typical "targetted" policies leave all user logins as unrestricted, adding security for daemons but not getting in the way of users who would otherwise turn SELinux off. On the other hand, a targeted policy has a "trusted" type for user logins which is explicitly allowed access to everything.
Ok, it sounds as if I did misunderstand SELinux. I thought that by labeling the individual files you couldn't do the 'only restrict apache' type of thing.
Quoted 1 line
That said, if you actually want your system to *work* with any default-deny policy then you have to describe EVERYTHING anyways. How exactly do you expect AppArmor to "work" if you don't allow users to run "
/bin/passwd", for example.
for AA you don't try to define permissions for every executable, and ones that you don't define policy are unrestricted.
so as I understand this with SELinux you will have lots of labels around your system (more as you lock down the system more) you need to define policy so that your unrestricted users must have access to every label, and every time you create a new label you need to go back to all your policies to see if the new label needs to be allowed from that policy
is this correct?
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 10 lines · On Jun 09, 2007, at 12:46:40, [email protected] wrote:
Quoted 1 line · Kyle Moffett
Typical "targetted" policies leave all user logins as unrestricted, adding security for daemons but not getting in the way of users who would otherwise turn SELinux off. On the other hand, a targeted policy has a "trusted" type for user logins which is explicitly allowed access to everything.
Ok, it sounds as if I did misunderstand SELinux. I thought that by labeling the individual files you couldn't do the 'only restrict apache' type of thing.
Quoted 1 line
That said, if you actually want your system to *work* with any default-deny policy then you have to describe EVERYTHING anyways. How exactly do you expect AppArmor to "work" if you don't allow users to run "
/bin/passwd", for example.for AA you don't try to define permissions for every executable, and ones that you don't define policy are unrestricted.
so as I understand this with SELinux you will have lots of labels around your system (more as you lock down the system more) you need to define policy so that your unrestricted users must have access to every label, and every time you create a new label you need to go back to all your policies to see if the new label needs to be allowed from that policy
Actually, it's easier than that. There are type attributes which may be assigned to an arbitrary set of types, and each "type" field in an access rule may use either a type or an attribute. So you don't actually need to modify existing rules when adding new types, you just add the appropriate existing attributes to your new type. For example, you could set up a "logfile" attribute which allows logrotate to archive old versions and allows audit-admin users to modify/delete them, then whenever you need to add a new logfile you just declare the "my_foo_log_t" type to have the "logfile" attribute.
On the other hand, I seem to recall that typical "targeted" policies don't grant most of the additional access via access rules, they instead add a special case to the fundamental "constraints" in the policy (IE: If the subject type has the "trusted" attribute then skip some of the other type-based checks).
Cheers,
Kyle Moffett
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 6 lines · Kyle Moffett
Quoted 5 lines · On Jun 09, 2007, at 12:46:40, [email protected] wrote:
Quoted 1 line · Kyle Moffett
Typical "targetted" policies leave all user logins as unrestricted, adding security for daemons but not getting in the way of users who would otherwise turn SELinux off. On the other hand, a targeted policy has a "trusted" type for user logins which is explicitly allowed access to everything.
Ok, it sounds as if I did misunderstand SELinux. I thought that by labeling the individual files you couldn't do the 'only restrict apache' type of thing.
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
That said, if you actually want your system to *work* with any default-deny policy then you have to describe EVERYTHING anyways. How exactly do you expect AppArmor to "work" if you don't allow users to run "
/bin/passwd", for example.for AA you don't try to define permissions for every executable, and ones that you don't define policy are unrestricted.
so as I understand this with SELinux you will have lots of labels around your system (more as you lock down the system more) you need to define policy so that your unrestricted users must have access to every label, and every time you create a new label you need to go back to all your policies to see if the new label needs to be allowed from that policy
Actually, it's easier than that. There are type attributes which may be assigned to an arbitrary set of types, and each "type" field in an access rule may use either a type or an attribute. So you don't actually need to modify existing rules when adding new types, you just add the appropriate existing attributes to your new type. For example, you could set up a "logfile" attribute which allows logrotate to archive old versions and allows audit-admin users to modify/delete them, then whenever you need to add a new logfile you just declare the "
my_foo_log_t" type to have the "logfile" attribute.
isn't this just the flip side of the same problem?
every time you define a new attribute you need to go through all the files and decide if the new attribute needs to be given to that file.
David Lang
Quoted 6 lines
On the other hand, I seem to recall that typical "targeted" policies don't grant most of the additional access via access rules, they instead add a special case to the fundamental "constraints" in the policy (IE: If the subject type has the "trusted" attribute then skip some of the other type-based checks).
Cheers,
Kyle Moffett
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation,pathname matching
In reply to SeanQuoted 2 lines · --- Sean
The question is: why not just extend SELinux to include AA functionality rather than doing a whole new subsystem.
Because, as hard as it seems for some people to believe, not everyone wants Type Enforcement. SELinux is a fine implementation of type enforcement, but if you don't want what it does it would be silly to require that it be used in order to accomplish something else, like name based access control.
If the same things made everyone feel "secure" there would be no optional security facilities (audit, cryptfs, /dev/random, ACLs). It appears that the AA folks are sufficiently unimpressed with SELinux they want to do something different. I understand that there is a contingent that believes security == SELinux. There are also people who believe security == cryptography or security == virus scanners. I'm happy that they have found what works for them.
Also, "just extend" implies that it would be easy to do. I suggest you go read the SELinux MLS code, and go read some of the discussions about getting MLS working for the RedHat LSP before you go throwing "just" around.
Casey Schaufler
[email protected]
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 9 lines · On Jun 09, 2007, at 13:32:05, [email protected] wrote:
Quoted 4 lines · Kyle Moffett
Quoted 1 line · On Jun 09, 2007, at 12:46:40, [email protected] wrote:
so as I understand this with SELinux you will have lots of labels around your system (more as you lock down the system more) you need to define policy so that your unrestricted users must have access to every label, and every time you create a new label you need to go back to all your policies to see if the new label needs to be allowed from that policy
Actually, it's easier than that. There are type attributes which may be assigned to an arbitrary set of types, and each "type" field in an access rule may use either a type or an attribute. So you don't actually need to modify existing rules when adding new types, you just add the appropriate existing attributes to your new type. For example, you could set up a "logfile" attribute which allows logrotate to archive old versions and allows audit-admin users to modify/delete them, then whenever you need to add a new logfile you just declare the "
my_foo_log_t" type to have the "logfile" attribute.isn't this just the flip side of the same problem?
every time you define a new attribute you need to go through all the files and decide if the new attribute needs to be given to that file.
No you don't, you can add attributes to a type after-the-fact. In concept this problem is very similar to programming: You have various documented interfaces used by different policy files to interact with each other. As long as your policy files conform to the documented interfaces then you *DONT* have to manually inspect each file because you can make basic assumptions. On the other hand, when you break that interface "contract" you will get very unexpected results. For the above example:
My syslog policy file would create a "logfile" attribute and types for "/var/log/auth/auth.log", "/var/log/kern/kern.log", and "/var/log/messages". It would also create a "logdaemon" attribute which has automatic type transitions to create files in different "/var/log/*" directories Finally, it would allow the syslogd type to create and append to its specific file types for "auth.log", "kern.log", and "messages".
My logrotate policy file would depend on the syslog policy and would declare the logrotate daemon type as a "logdaemon", and additionally allow logrotate to read, rename, append, and delete "logfile" types. Since logrotate is a "logdaemon", it already has the appropriate type transitions for new types.
My samba policy file would depend on the syslog policy and would declare the samba daemon type as a "logdaemon" and the "/var/log/samba/*" type as a "logfile". Then it would add a type transition rule so when "logdaemon" creates new files in "samba_log_dir_t", they have the appropriate "samba_log_t" label. Finally, samba would allow itself to append to "samba_log_t" files.
Note that now when "logrotate" runs and rotates files in /var/log/samba, it will automatically create the new files with type "samba_log_t", even though there are no *direct* associations between those types. If the syslog policy file was poorly written it could seriously adversely affect the security of the system, but hopefully that's obvious :-D. Policy development is _hard_, it's a whole separate state-machine and pseudo-programming-language that should mostly be left to security professionals or very experienced developers/sysadmins.
Cheers,
Kyle Moffett
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 11 lines · Kyle Moffett
Quoted 9 lines · On Jun 09, 2007, at 13:32:05, [email protected] wrote:
Quoted 4 lines · Kyle Moffett
Quoted 1 line · On Jun 09, 2007, at 12:46:40, [email protected] wrote:
so as I understand this with SELinux you will have lots of labels around your system (more as you lock down the system more) you need to define policy so that your unrestricted users must have access to every label, and every time you create a new label you need to go back to all your policies to see if the new label needs to be allowed from that policy
Actually, it's easier than that. There are type attributes which may be assigned to an arbitrary set of types, and each "type" field in an access rule may use either a type or an attribute. So you don't actually need to modify existing rules when adding new types, you just add the appropriate existing attributes to your new type. For example, you could set up a "logfile" attribute which allows logrotate to archive old versions and allows audit-admin users to modify/delete them, then whenever you need to add a new logfile you just declare the "
my_foo_log_t" type to have the "logfile" attribute.isn't this just the flip side of the same problem?
every time you define a new attribute you need to go through all the files and decide if the new attribute needs to be given to that file.
Quoted 7 lines
No you don't, you can add attributes to a type after-the-fact. In concept this problem is very similar to programming: You have various documented interfaces used by different policy files to interact with each other. As long as your policy files conform to the documented interfaces then you *DONT* have to manually inspect each file because you can make basic assumptions. On the other hand, when you break that interface "contract" you will get very unexpected results. For the above example:
My syslog policy file would create a "logfile" attribute and types for "
/var/log/auth/auth.log", "/var/log/kern/kern.log", and "/var/log/messages". It would also create a "logdaemon" attribute which has automatic type transitions to create files in different "/var/log/*" directories Finally, it would allow the syslogd type to create and append to its specific file types for "auth.log", "kern.log", and "messages".My logrotate policy file would depend on the syslog policy and would declare the logrotate daemon type as a "logdaemon", and additionally allow logrotate to read, rename, append, and delete "logfile" types. Since logrotate is a "logdaemon", it already has the appropriate type transitions for new types.
My samba policy file would depend on the syslog policy and would declare the samba daemon type as a "logdaemon" and the "
/var/log/samba/*" type as a "logfile". Then it would add a type transition rule so when "logdaemon" creates new files in "samba_log_dir_t", they have the appropriate "samba_log_t" label. Finally, samba would allow itself to append to "samba_log_t" files.
if you have your policy figured out and then go and apply it to applications then you are correct.
I'm talking about the situation where you start off by defining a policy for Samba, and then afterwords decide that you want to have seperate "logfile" and "logdaemon" type then you would need to go back and re-examine all the files to tell what needs to be labeled as what.
if you can do all the policy design in advance (and get it right) then SELinux is a great solution. if you don't have that time and have to do the policy incrementaly you will end up revisiting and revising your entire policy each time you go to add something.
Quoted 1 line
Note that now when "logrotate" runs and rotates files in
/var/log/samba, it will automatically create the new files with type "samba_log_t", even though there are no *direct* associations between those types. If the syslog policy file was poorly written it could seriously adversely affect the security of the system, but hopefully that's obvious :-D. Policy development is _hard_, it's a whole separate state-machine and pseudo-programming-language that should mostly be left to security professionals or very experienced developers/sysadmins.
I _am_ a security professional. I've been doing security sysadmin work on Linux for over a decade now. From your description I'm exactly the type of person you are saying should be figuring this out. So please back off of the 'this is hard, you should leave it to the professionals' line a little bit ;-)
if the AA policies can be compiled into SELinux policies that would work (currently they can't and many SELinux people oppose the features that would need to be added to make it possible) but the compile process leaves room for more bugs in an areas that's going to be hard to investigate. (This isn't a fault of SELinux, it's a common issue with compilers, the compiled 'thing' is designed to be machine friendly, not user friendly. it doesn't matte if it's compiling C into machine code or high-level firewall rules into iptables commands or AA policies into SELinux labels and policies). adding a complex intermediate layer does not nessasarily add security, but it definantly adds complication.
once SELinux has a way for a trusted user to edit a security sensitive file (via the process of creating a file and renameing it into place) without needing SELinux aware tools and have the result accepted as valid by the rest of the system then it becomes possible to implement an AA to SELinux policy compiler, but saying that AA should not be accepted becouse it's possible to modify SELinux and write such a compile with sufficant effort is not reasonable. While there are many cases where multiple ways of doing something are not allowed into the kernel (suspend/hibernate is an example) there are also cases where multiple ways of doing things are allowed, and the LSM hooks are in place explicitly to permit different types of security modules to use them.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Greg KHHi!
Quoted 21 lines
Quoted 9 lines
Quoted 8 lines
I assume you mean labels instead of handles.
AppArmor's design is around paths not labels, and independent of whether or not you like AppArmor, this design leads to a useful security model distinct from the SELinux security model (which is useful in its own ways). The differences between those models cannot be argued away, neither is a subset of the other, and neither is a misdesign. I would be thankful if you could stop spreading this lie.
Quoted 11 lines
I have a hard time distinguishing AppArmor's "model" from its implementation; every time we suggest that one might emulate much of AppArmor's functionality on SELinux (as in SEEdit), someone points to a specific characteristic of the AppArmor implementation that cannot be emulated in this manner. But is that implementation characteristic an actual requirement or just how it happens to have been done to date in AA? And I get the impression that even if we extended SELinux in certain ways to ease such emulation, the AA folks would never be satisfied because the implementation would still differ. Can we separate the desired functionality and actual requirements from the implementation specifics?
Quoted 4 lines
That's a really good point, is there a description of the AA "model" anywhere that we could see to determine if there really is a way to possibly use the current SELinux internals to show this model to the user?
Hmm, techdoc.pdf (attached) is supposed to describe this "model", but it is more of "AA works like this" with no explanations.... and includes (probably unwanted) quirks like various races during path resolution.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Hi!
Quoted 18 lines
Quoted 3 lines
So, AA developers, do you have such a document anywhere? I know there are some old research papers, do they properly describe the current model you are trying to implement here?
Greg,
to implement the AA approach useing SELinux you need to have a way that
files that are renamed or created get tagged with the right label automaticaly with no possible race condition.If this can be done then it _may_ be possible to do the job that AA is aimed at with SELinux, but the work nessasary to figure out what lables are needed on what file would still make it a non-trivial task.
as I understand it SELinux puts one label on each file, so if you have three files accessed by two programs such that
program A accesses files X Y
program B accesses files Y Z
Quoted 7 lines
then files X Y and Z all need seperate labels with the policy stateing that program A need to access labels X, Y and program B needs to access files Y Z
extended out this can come close to giving each file it's own label. AA essentially does this and calls the label the path and computes it at runtime instead of storing it somewhere.
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 24 lines · Pavel Machek
Hi!
Quoted 22 lines
Quoted 3 lines
So, AA developers, do you have such a document anywhere? I know there are some old research papers, do they properly describe the current model you are trying to implement here?
Greg,
to implement the AA approach useing SELinux you need to have a way that
files that are renamed or created get tagged with the right label automaticaly with no possible race condition.If this can be done then it _may_ be possible to do the job that AA is aimed at with SELinux, but the work nessasary to figure out what lables are needed on what file would still make it a non-trivial task.
as I understand it SELinux puts one label on each file, so if you have three files accessed by two programs such that
program A accesses files X Y
program B accesses files Y Zthen files X Y and Z all need seperate labels with the policy stateing that program A need to access labels X, Y and program B needs to access files Y Z
Quoted 6 lines
Quoted 3 lines
extended out this can come close to giving each file it's own label. AA essentially does this and calls the label the path and computes it at runtime instead of storing it somewhere.
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
and if each file has it's own label you are going to need regex or similar to deal with them as well.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Greg KHQuoted 12 lines · Andreas Gruenbacher
Quoted 10 lines · Greg KH
Quoted 3 lines · Andreas Gruenbacher
AppArmor is meant to be relatively easy to understand, manage, and
customize, and introducing a labels layer wouldn't help these goals.Woah, that describes the userspace side of AA just fine, it means nothing when it comes to the in-kernel implementation. There is no reason that you can't implement the same functionality using some totally different in-kernel solution if possible.
Quoted 7 lines
I agree that the in-kernel implementation could use different abstractions than user-space, provided that the underlying implementation details can be hidden well enough. The key phrase here is "if possible", and in fact "if possible" is much too strong: very many things in software are possible, including user-space drives and a stable kernel module ABI. Some things make sense; others are genuinely bad ideas while still possible.
In particular, to layer AppArmor on top of SELinux, the following problems must be addressed:
* New files: when a file is created, it is labeled according to the type of the creating process and the type of the parent directory. Applications can also use libselinux to use application logic to relabel the file, but that is not 'mandatory' policy, and fails in cases like cp and mv. AppArmor lets you create a policy that e..g says "/home/*/.plan r" to permit fingerd to read everyone's .plan file, should it ever exist, and you cannot emulate that with SELinux.
* Renamed Files: Renaming a file changes the policy with respect to that file in AA. To emulate this in SELinux, you would have to have a way to instantly re-label the file upon rename.
* Renamed Directory trees: The above problem is compounded with directory trees. Changing the name at the top of a large, bushy tree can require instant relabeling of millions of files.
* New Policies: The SEEdit approach of compiling AA profiles into SELinux labels involves computing the partition set of files, so that each element of the partition set is unique, and corresponds to all the policies that treat every file in the element identically. If you create a new profile that touches *some* of the files in such an element, then you have to split that synthetic label, re-compute the partition set, and re-label the file system.
* File Systems That Do Not Support Labels: The most important being NFS3 and FAT. Because they do not support labels at all, SELinux has to give you an all-or-nothing access control on the entire remote volume. AA can give you nuanced access control in these file systems.
You could support all of these features in SELinux, but only by adding an in-kernel file matching mechanism similar to AppArmor. It would basically load an AppArmor policy into the kernel, label files as they are brought from disk into the cache, and then use SELinux to do the access controls.
That doesn't make it a good idea:
* The patch would be at least as complex and intrusive as the proposed AppArmor patch, there is no simplicity already-upstream savings here.
* It would require the VFS and d_path patches that AppArmor needs to pass mount points down.
* It would make AppArmor's ability to change policies on a live system more difficult.
* The necessary extensions would not be appealing to the SELinux community.
LSM is the common code that AA and SELinux have agreed to be mutually useful. Forcing AA to sit on top of SELinux would harm both AA and SELinux.
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com AppArmor Chat: irc.oftc.net/#apparmor
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 5 lines · --- [email protected] wrote:
Quoted 2 lines
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
and if each file has it's own label you are going to need regex or similar to deal with them as well.
Now that you're going to have to explain. Nothing like that on any of the MLS systems I'm familiar with, and I think that I know just about all of them.
Casey Schaufler
[email protected]
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 5 lines · Casey Schaufler
Quoted 3 lines · --- [email protected] wrote:
Quoted 3 lines
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
Quoted 7 lines
Quoted 3 lines
and if each file has it's own label you are going to need regex or similar to deal with them as well.
Now that you're going to have to explain. Nothing like that on any of the MLS systems I'm familiar with, and I think that I know just about all of them.
I suspect that David meant that if you were using "unique label per file" as an implementation technique to implement AA on top of SELinux, that you would then need a regexp to discern labels.
It's hard to recall with all the noise, but at this point in the thread the discussion is about the best way to implement AA. Some have alleged that AA layered on top of SELinux is the best way. I think that is clearly wrong; AA layered on top of SELinux is possible, but would require a bunch of enhancements to SELinux first, and the result would be more complex than the proposed AA patch and have weaker functionality and performance.
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com AppArmor Chat: irc.oftc.net/#apparmor
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 6 lines · [email protected] wrote:
Quoted 5 lines · Greg KH
I still want to see a definition of the AA "model" that we can then use to try to implement using whatever solution works best. As that seems to be missing the current argument of if AA can or can not be implemented using SELinux or something totally different should be stopped.
Quoted 8 lines
the way I would describe the difference betwen AA and SELinux is:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
AA is like a default deny firewall, you describe what you want to happen, and it blocks everything else without you even having to realize that it's there.
That's not quite right:
* SELinux Strict Policy is a default-deny system: it specifies everything that is permitted system wide, and all else is denied.
* AA and the SELinux Targeted Policy are hybrid systems:
o default-deny within a policy or profile: confined processes are only permitted to do what the policy says, and all else is denied.
o default-allow system wide: unconfined processes are allowed to do anything that classic DAC permissions allow.
Crispin
Signature · 3 lines
Crispin Cowan, Ph.D. http://crispincowan.com/~crispin/ Director of Software Engineering http://novell.com AppArmor Chat: irc.oftc.net/#apparmor
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Hi!
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
extended out this can come close to giving each file it's own label. AA essentially does this and calls the label the path and computes it at runtime instead of storing it somewhere.
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
and if each file has it's own label you are going to need regex or similar to deal with them as well.
But you have that regex in _user_ space, in a place where policy is loaded into kernel.
AA has regex parser in _kernel_ space, which is very wrong.
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Crispin CowanQuoted 21 lines · Crispin Cowan
Quoted 8 lines · [email protected] wrote:
Quoted 6 lines · Greg KH
I still want to see a definition of the AA "model" that we can then use to try to implement using whatever solution works best. As that seems to be missing the current argument of if AA can or can not be implemented using SELinux or something totally different should be stopped.
Quoted 9 lines
the way I would describe the difference betwen AA and SELinux is:
SELinux is like a default allow IPS system, you have to describe EVERYTHING to the system so that it knows what to allow and what to stop.
AA is like a default deny firewall, you describe what you want to happen, and it blocks everything else without you even having to realize that it's there.
That's not quite right:
Quoted 9 lines
* SELinux Strict Policy is a default-deny system: it specifies everything that is permitted system wide, and all else is denied.
* AA and the SELinux Targeted Policy are hybrid systems:
o default-deny within a policy or profile: confined processes are only permitted to do what the policy says, and all else is denied.
o default-allow system wide: unconfined processes are allowed to do anything that classic DAC permissions allow.
Still not completely correct, though the targeted policy has an unconfined domain (unconfined_t) the policy still has allow rules for everything unconfined can do, 2 examples of things unconfined still can't do (because they aren't allowed by the targeted policy) is execmem and a while back when there was a /proc exploit that required setattr on /proc/self/environ; unconfined_t wasn't able to do that either (and therefore the exploit didn't work on a targeted system).
That said, the differentiation between strict and targeted is going away soon so that one can have some users be unconfined (but still with a few restrictions) and others can be fully restricted.
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Stephen SmalleyQuoted 11 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was no, then we could stop the entire discussion right there.
Quoted 5 lines
Why should AppArmor be different from the rest of the kernel in its usage of pathnames (basis for decisions vs. informational reporting to userspace)? And if it is ok for AppArmor to generate and use pathnames as its basis of decisions on each open, then is it also ok for audit, inotify, and others to use them in the same manner?
Audit and inotify don't make any decisions based on pathnames, or on SELinux labels for that matter, they only report. That being said, sure those parts of the kernel that report pathnames should report them correctly -- I guess there is no disagreement about that.
Quoted 6 lines
Another question: it seems like the read-only bind mount folks gave up on propagating the vfsmounts down and switched to a rather different approach (checking near the entry points, using mount writer counters). So similarly, what makes AppArmor fundamentally different that it wouldn't take a similar approach to what they are doing vs. propagating the vfsmounts down?
Without the vfsmounts propagated down you won't know the pathnames. Whether or not a different problem can be solved without the vfsmounts is not really relevant.
Thanks,
Andreas
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Pavel MachekQuoted 4 lines · Pavel Machek
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
extended out this can come close to giving each file it's own label. AA essentially does this and calls the label the path and computes it at runtime instead of storing it somewhere.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
and if each file has it's own label you are going to need regex or similar to deal with them as well.
But you have that regex in _user_ space, in a place where policy is loaded into kernel.
then the kernel is going to have to call out to userspace every time a file is created or renamed and the policy is going to be enforced incorrectly until userspace finished labeling/relabeling whatever is moved. building this sort of race condigion for security into the kernel is highly questionable at best.
Quoted 1 line
AA has regex parser in _kernel_ space, which is very wrong.
see Linus' rants about why it's not automaticaly the best thing to move functionality into userspace.
remember that the files covered by an AA policy can change as files are renamed. this isn't the case with SELinux so it doesn't have this sort of problem.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Crispin CowanQuoted 9 lines · Crispin Cowan
Quoted 8 lines · Casey Schaufler
Quoted 6 lines · --- [email protected] wrote:
Quoted 3 lines
Yes, and in the process, AA stores compiled regular expressions in kernel. Ouch. I'll take "each file it's own label" over _that_ any time.
and if each file has it's own label you are going to need regex or similar to deal with them as well.
Quoted 7 lines
Quoted 4 lines
Now that you're going to have to explain. Nothing like that on any of the MLS systems I'm familiar with, and I think that I know just about all of them.
I suspect that David meant that if you were using "unique label per file" as an implementation technique to implement AA on top of SELinux, that you would then need a regexp to discern labels.
exactly.
say that we give each file a unique label, and for simplicity we set the label == path (note that this raises the issue, what will SELinux do when there are multiple paths to the same file)
now say that you want to grant apache access to all files that have labels that follow the pattern '/home/*/http/* ?
you are either going to use regex matching, or you are going to have to enumerate every label that matches this (potentially a very large list). and if you try to generate the enumerated list you need to add a label to the list if a file is renamed or created to match the pattern, and delete a file from the list if it is renamed to no longer match the pattern
Quoted 7 lines
It's hard to recall with all the noise, but at this point in the thread the discussion is about the best way to implement AA. Some have alleged that AA layered on top of SELinux is the best way. I think that is clearly wrong; AA layered on top of SELinux is possible, but would require a bunch of enhancements to SELinux first, and the result would be more complex than the proposed AA patch and have weaker functionality and performance.
AA as-is needs to figure out how to deal with bind-mounts, and how to handle hardlink creation in a more ganular manner (and potentially other resources like network sockets), but it's useful now even without these improvements
AA over SELinux would need for SELinux to figure out how to handle file creation, file renames, and multiple paths for the same file (hard-links and bind-mounts). In addition a userspace daemon would have to be written to re-label files and/or change policy on the fly as files are renamed. the result would still have race conditions due to the need to re-label large numbers of files
ACPI should have taught everyone that sometimes putting an interpreter in the kernel really is the best option. looking at the problems of bouncing back out to userspace for file creation and renames it looks like a regex in the kernel is a lot safer and more reliable.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 3 lines · On Sun, 10 Jun 2007 23:45:16 -0700 (PDT) [email protected] wrote:
say that we give each file a unique label, and for simplicity we set the label == path (note that this raises the issue, what will SELinux do when there are multiple paths to the same file)
So don't do that then.
Quoted 8 lines
now say that you want to grant apache access to all files that have labels that follow the pattern '
/home/*/http/*?you are either going to use regex matching, or you are going to have to enumerate every label that matches this (potentially a very large list). and if you try to generate the enumerated list you need to add a label to the list if a file is renamed or created to match the pattern, and delete a file from the list if it is renamed to no longer match the pattern
If AA requires regex matching in the kernel, perhaps it really isn't appropriate for inclusion. Surely there has to be a better way than requiring the kernel to do regex matches at runtime?
Quoted 6 lines
AA over SELinux would need for SELinux to figure out how to handle file creation, file renames, and multiple paths for the same file (hard-links and bind-mounts). In addition a userspace daemon would have to be written to re-label files and/or change policy on the fly as files are renamed. the result would still have race conditions due to the need to re-label large numbers of files
WRONG. The labels would be obtained from AA as needed, never recorded in the file attributes. This would change nothing about what AA needed to compute at runtime, just the way it implements the result.
Quoted 4 lines
ACPI should have taught everyone that sometimes putting an interpreter in the kernel really is the best option. looking at the problems of bouncing back out to userspace for file creation and renames it looks like a regex in the kernel is a lot safer and more reliable.
There hasn't yet been shown a requirement for a userspace daemon to implement AA over SeLinux.
Sean
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
Quoted 28 lines · Sean
Quoted 3 lines · [email protected] wrote:
say that we give each file a unique label, and for simplicity we set the label == path (note that this raises the issue, what will SELinux do when there are multiple paths to the same file)
So don't do that then.
Quoted 8 lines
now say that you want to grant apache access to all files that have labels that follow the pattern '
/home/*/http/*?you are either going to use regex matching, or you are going to have to enumerate every label that matches this (potentially a very large list). and if you try to generate the enumerated list you need to add a label to the list if a file is renamed or created to match the pattern, and delete a file from the list if it is renamed to no longer match the pattern
If AA requires regex matching in the kernel, perhaps it really isn't appropriate for inclusion. Surely there has to be a better way than requiring the kernel to do regex matches at runtime?
Quoted 6 lines
AA over SELinux would need for SELinux to figure out how to handle file creation, file renames, and multiple paths for the same file (hard-links and bind-mounts). In addition a userspace daemon would have to be written to re-label files and/or change policy on the fly as files are renamed. the result would still have race conditions due to the need to re-label large numbers of files
Quoted 3 lines
WRONG. The labels would be obtained from AA as needed, never recorded in the file attributes. This would change nothing about what AA needed to compute at runtime, just the way it implements the result.
Ok, you are proposing throwing out all the label handling that SELinux does, including any caching. forgive me if I agree with the SELinux people that this is a very bad idea.
Quoted 7 lines
Quoted 4 lines
ACPI should have taught everyone that sometimes putting an interpreter in the kernel really is the best option. looking at the problems of bouncing back out to userspace for file creation and renames it looks like a regex in the kernel is a lot safer and more reliable.
There hasn't yet been shown a requirement for a userspace daemon to implement AA over SeLinux.
I thought the userspace component was what you were proposing instead of doing the regex matching in the kernel. if this isn't it what exactly are you proposing?
you don't want the regex matching in the kernel.
you don't want a userspace component to do the regex matching when files are created or renamed.
how exactly do you propose to figure out what should happen to a file when it is created or it (or a parent directory) is renamed?
AA policies are defined in terms of regex expressions. you say that this should be able to be done on top of SELinux somehow without changing the policies. so somewhere, something needs to interpret the regex to see if it matches the path. this needs to be either kernel code or userspace code. you have ruled out kernel code and are now claiming that userspace isn't needed.
David Lang
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Hi!
Quoted 4 lines
ACPI should have taught everyone that sometimes putting an interpreter in the kernel really is the best option. looking at the problems of bouncing back out to userspace for file creation and renames it looks like a regex in the kernel is a lot safer and more reliable.
What do ACPI and AA have in common?
* they both start with A
* there are both nightmare
* they both put interpretter into kernel
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to [email protected]Quoted 3 lines · On Mon, 11 Jun 2007 02:33:30 -0700 (PDT) [email protected] wrote:
Ok, you are proposing throwing out all the label handling that SELinux does, including any caching. forgive me if I agree with the SELinux people that this is a very bad idea.
Well presumably AA would be doing caching etc.. so that doesn't seem like a problem. The SELinux people seem to think that accepting AA into the kernel and supporting path based security at all is a mistake. I guess I forgive you for agreeing with them ;)
Quoted 3 lines
I thought the userspace component was what you were proposing instead of doing the regex matching in the kernel. if this isn't it what exactly are you proposing?
No.. i've said quite a few times now that i'm not talking about calling out to userspace. The entire discussion of regex matching is a completely separate discussion. It's either the right thing to do, or not. But the same issues in regard to regex matching apply whether AA is built on top of SELinux or not.
Quoted 6 lines
AA policies are defined in terms of regex expressions. you say that this should be able to be done on top of SELinux somehow without changing the policies. so somewhere, something needs to interpret the regex to see if it matches the path. this needs to be either kernel code or userspace code. you have ruled out kernel code and are now claiming that userspace isn't needed.
For whatever it's worth, i'll repeat again. The AA kernel extension would
be associating paths with labels (using regex, or not). At that point all policy decisions would be enforced by SELinux using standard SELinux policy
rules. The SELinux policy would be a translated version of the AA policy
file. The translation could of course happen in userland.
The net affect of all that... is that you get a version of SELinux which
can be configured with the user friendly AA policy file format. And,
files won't need to carry around security labels with them. I leave the debate about whether that's a good idea in general to others. But from what i can tell, it's the only significant difference between SELinux and AA.
Depending on the way it was implemented, its conceivable that users could mix and match native SELinux policy with custom AA policies as they saw fit.
Sean
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Andreas GruenbacherQuoted 10 lines · Andreas Gruenbacher
Quoted 9 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
Quoted 7 lines
Quoted 2 lines
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was no, then we could stop the entire discussion right there.
There is a difference between using the pathname at the kernel/userland interface as part of configuring a security mechanism and using it as the basis for the runtime checking itself. Further, there is a difference between generating and matching full pathnames on each access vs. caching information in the parent dentry and making decisions based on that cached information and the last component-name. The only question I saw being answered was the abstract one of "can a pathname play a role in the security mechanism" not "should the kernel be regenerating a full path on each open and glob matching it against a list of file globs."
Quoted 6 lines
Quoted 5 lines
Why should AppArmor be different from the rest of the kernel in its usage of pathnames (basis for decisions vs. informational reporting to userspace)? And if it is ok for AppArmor to generate and use pathnames as its basis of decisions on each open, then is it also ok for audit, inotify, and others to use them in the same manner?
Quoted 6 lines
Audit and inotify don't make any decisions based on pathnames, or on SELinux labels for that matter, they only report. That being said, sure those parts of the kernel that report pathnames should report them correctly -- I guess there is no disagreement about that.
From a userland perspective, audit and inotify allow you to specify
watches on pathnames, and those watches trigger actions by the audit and inotify subsystems when those files are accessed. The kernel mechanism however is inode-based, not pathname-based; the pathname is merely looked up when the watch is added and mapped to an inode. That's my point - why should AA be different? Would you really recommend that audit or inotify call d_path() on each open and glob match the result against a list of audit or inotify watches? BTW, audit does allow filters on SELinux labels these days.
Quoted 10 lines
Quoted 6 lines
Another question: it seems like the read-only bind mount folks gave up on propagating the vfsmounts down and switched to a rather different approach (checking near the entry points, using mount writer counters). So similarly, what makes AppArmor fundamentally different that it wouldn't take a similar approach to what they are doing vs. propagating the vfsmounts down?
Without the vfsmounts propagated down you won't know the pathnames. Whether or not a different problem can be solved without the vfsmounts is not really relevant.
Well, that presumes that your mechanism has to generate full pathnames on each access check. But even if so, you could be doing your checking in the higher level code then where you have a vfsmount available.
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Andreas GruenbacherQuoted 7 lines · Andreas Gruenbacher
Quoted 5 lines · Stephen Smalley
- under AA, each file may have an arbitrary set of "labels" or "policies" applied to it depending on what programs are accessing it and what names are being used to reference it - there is no system view of the subjects and objects and thus no way to identify the overall system policy for a given file.
Quoted 9 lines
Look at it this way: under SELinux, the set of files that share a label forms an equivalence class -- they are all treated identically by the system's security policy. The rules in AppArmor profiles also define equivalence classes in the sense that they partition the filesystem namespace into sets of files that are treated identically, but this classification is not explicit -- the entire rule base contributes to the classification. This doesn't mean that there is not a global policy, just that the policy is modeled differently. The equivalence classes are not directly obvious from the AA profiles.
No, it really does mean that there is no global policy, and it goes beyond "not directly obvious" to "can not be determined" from the AA profiles. You can't compose the set of AA profiles and say anything useful, because they are written in terms of ambiguous and unstable identifiers. /a/b/c may refer to completely different objects in two different profiles, or to the same object as /d/e/f in the same or another profile.
Quoted 7 lines
Contrast this with SEEdit, which compiles AA-style rules into labels (and thus equivalence classes). The resulting SELinux policy is a static snapshot that cannot easily accommodate rule base changes, is more limited with respect to new files (which would likely be fixable), and behaves differently in complex ways with file renames. What's more, most likely the compiled policy will be anywhere from very hard to impossible to analyze, so you pretty much lose on all ends.
Just to clarify, you can change the allowed accesses from a given subject to a given object without relabeling, just by changing the policy allow rules; you only have to relabel the object in the case where you want to distinguish that object from another object with the same label for the same subject. I think the new file situation could be improved without any major change to the SELinux model, and am not opposed to leveraging the component name there, as previously noted. On the file rename case, I think we have it right - access rights shouldn't change automatically when a file is renamed, any more than DAC ownership or file modes should.
Quoted 8 lines
Quoted 1 line
- names are far less tranquil than labels.
If I'm getting things right, a tranquil system with respect to labels would be one that does not permit re-labeling, while a tranquil system with respect to path names would be one that does not permit renaming. Both approaches would buy greater analyzability with reduced usability, and both seem unrealistic to me. SELinux and AppArmor evidently have different goals, and tranquility is more important to SELinux.
Tranquility is important to correctness and understandability of policy; if labels (or pathnames in your case) can change at any time, then you have the problems of revocation of access (impractical to completely implement in Linux) and your effective policy now varies over time, so you have to consider time as a factor in your policy analysis.
Quoted 7 lines
AppArmor is meant to be relatively easy to understand, manage, and customize, and introducing a labels layer wouldn't help these goals. SELinux is applicable in areas where AppArmor is not (e.g., MLS), but this comes at a cost. For me the question is not SELinux or AppArmor, but if AppArmor's security model is a good solution in common scenarios. In my opinion, AppArmor is a better answer than SELinux in a number of scenarios. This gives it value, nonwithstanding the fact that SELinux can be taken further.
I'd agree that we shouldn't try to emulate AA as it is on SELinux. The question is more of whether we can meet the higher level functionality goals that make some people want to use AA via SELinux. That requires separating those goals from the implementation details of AA.
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Stephen SmalleyQuoted 14 lines · Stephen Smalley
Quoted 13 lines · Andreas Gruenbacher
Quoted 11 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
Quoted 8 lines
Quoted 4 lines
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was no, then we could stop the entire discussion right there.
There is a difference between using the pathname at the kernel/userland interface as part of configuring a security mechanism and using it as the basis for the runtime checking itself.
Yes, there is a difference. When I say pathname based security mechanism, I literally mean a pathname based security mechanism, meaning the pathnames determine the outcome o the decision. This includes designs that are based on different abstractions internally, but the bahavior observable from user-space must be the same (or else, it's a different model).
Unfortunately, translating pathnames to labels destroys this fundamental abstraction. We explained why this is so in the following postings:
http://lkml.org/lkml/2007/6/9/94 http://lkml.org/lkml/2007/6/10/141
Quoted 4 lines
Further, there is a difference between generating and matching full pathnames on each access vs. caching information in the parent dentry and making decisions based on that cached information and the last component-name.
Wait, you are mixing two issues here: access checks on existing files, and the creation of new files. For AppArmor as it stands today the two are the same, but when looking at emulating AppArmor using labels, they are not. Let's look at things one at a time.
Generating and matching full pathnames on each access takes time, no question about that. (In fact we are not checking on each access but only when pathnames are involved, such as on open. Filehandle based operations do not require access checks, but that's not a very important difference at this level of discussion.) That's a quantitative statement though, not a qualitative one: retrieving xattrs and checking labels also takes time; additional checks are never for free. We find that doing the pathname checks is easily fast enough. You may disagree, but then you don't have to use AppArmor, and we are not standing in your way.
As far as new files are concerned, basing decisions on the parent dentry and component name requires that you know where in the filesystem hierarchy this dentry is located: with bind mounts, the same dentry shows up in multiple locations in a process's namespace, corresponding to different pathnames. In other words, to make the right decision, the dentry alone is not enough; it takes a <dentry, vfsmount> pair. So there we are again.
Quoted 1 line
From the point on where you have a <dentry, vfsmount> pair of objects, you can
do two things: you can compute the full pathname and base your decision on that, or you can do some caching to hopefully cut some of that work short frequently enough to set off the additional cost. The difference between the two approaches is quantitative -- if there is a difference in results, then that's obviously a bug. I believe that caching could speed up things measurably, but up to this point, neither I nor anybody else had the time to look into it, and so we are not doing it -- not yet, any perhaps never at all. It may be counter to your intuition, but doing those checks is not a big issue.
Quoted 4 lines
The only question I saw being answered was the abstract one of "can a pathname play a role in the security mechanism" not "should the kernel be regenerating a full path on each open and glob matching it against a list of file globs."
No glob matching involved, and no list of patterns. There is a single DFA per profile (and each process is in at most one profile at any one time), and each access check involves exactly one DFA traversal.
[cut here for lack of time right now -- will take another look later]
Quoted 7 lines
Quoted 3 lines
Without the vfsmounts propagated down you won't know the pathnames. Whether or not a different problem can be solved without the vfsmounts is not really relevant.
Well, that presumes that your mechanism has to generate full pathnames on each access check. But even if so, you could be doing your checking in the higher level code then where you have a vfsmount available.
The LSM hooks are pretty high-level, meant for being able to plug in different access checks. That's the right level of abstraction. We just need the vfsmounts passed down as well. That's what the bulk of common code patches are there for.
Andreas
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
Quoted 12 lines · Andreas Gruenbacher
Quoted 11 lines · Stephen Smalley
Quoted 10 lines · Andreas Gruenbacher
Quoted 9 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 2 lines
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was
That was the decision at ksummit last year, yes.
Quoted 46 lines
Quoted 5 lines
Quoted 1 line
no, then we could stop the entire discussion right there.
There is a difference between using the pathname at the kernel/userland interface as part of configuring a security mechanism and using it as the basis for the runtime checking itself.
Yes, there is a difference. When I say pathname based security mechanism, I literally mean a pathname based security mechanism, meaning the pathnames determine the outcome o the decision. This includes designs that are based on different abstractions internally, but the bahavior observable from user-space must be the same (or else, it's a different model).
Unfortunately, translating pathnames to labels destroys this fundamental abstraction. We explained why this is so in the following postings:
http://lkml.org/lkml/2007/6/9/94 http://lkml.org/lkml/2007/6/10/141Quoted 4 lines
Further, there is a difference between generating and matching full pathnames on each access vs. caching information in the parent dentry and making decisions based on that cached information and the last component-name.
Wait, you are mixing two issues here: access checks on existing files, and the creation of new files. For AppArmor as it stands today the two are the same, but when looking at emulating AppArmor using labels, they are not. Let's look at things one at a time.
Generating and matching full pathnames on each access takes time, no question about that. (In fact we are not checking on each access but only when pathnames are involved, such as on open. Filehandle based operations do not require access checks, but that's not a very important difference at this level of discussion.) That's a quantitative statement though, not a qualitative one: retrieving xattrs and checking labels also takes time; additional checks are never for free. We find that doing the pathname checks is easily fast enough. You may disagree, but then you don't have to use AppArmor, and we are not standing in your way.
As far as new files are concerned, basing decisions on the parent dentry and component name requires that you know where in the filesystem hierarchy this dentry is located: with bind mounts, the same dentry shows up in multiple locations in a process's namespace, corresponding to different pathnames. In other words, to make the right decision, the dentry alone is not enough; it takes a <dentry, vfsmount> pair. So there we are again.
Quoted 1 line
From the point on where you have a <dentry, vfsmount> pair of objects, you can
Quoted 9 lines
do two things: you can compute the full pathname and base your decision on that, or you can do some caching to hopefully cut some of that work short frequently enough to set off the additional cost. The difference between the two approaches is quantitative -- if there is a difference in results, then that's obviously a bug. I believe that caching could speed up things measurably, but up to this point, neither I nor anybody else had the time to look into it, and so we are not doing it -- not yet, any perhaps never at all. It may be counter to your intuition, but doing those checks is not a big issue.
My approach in DTE, which used pathnames to assign TE labels in the kernel, was to use a 'shadow tree' to the vfsmnt+dentry tree, filled out at policy load time to the depth of the deepest policy rule. For the behavior I was after, bind mounts were handled by storing pointers from the new mount to the original, but the behavior AA wants would be different. Namespace clones are easily handled this way by copying the cached pointers to the shadow tree. And it was pretty fast.
When I talked about this with Tony last year, the biggest shortcoming to use this for AA was the wildcards. For instance, if there is a rule for /var/log/HOSTS/*/messages, then when /var/log/HOSTS/sergelap/ is created, a new rule would have to be created on the fly to tag /var/log/HOSTS/sergelap/messages if it is created. How to represent that when only /var/log or /var/log/HOSTS exists at policy load time would be, well, a fun problem to solve actually :)
-serge
Quoted 29 lines
Quoted 4 lines
The only question I saw being answered was the abstract one of "can a pathname play a role in the security mechanism" not "should the kernel be regenerating a full path on each open and glob matching it against a list of file globs."
No glob matching involved, and no list of patterns. There is a single DFA per profile (and each process is in at most one profile at any one time), and each access check involves exactly one DFA traversal.
[cut here for lack of time right now -- will take another look later]
Quoted 7 lines
Quoted 3 lines
Without the vfsmounts propagated down you won't know the pathnames. Whether or not a different problem can be solved without the vfsmounts is not really relevant.
Well, that presumes that your mechanism has to generate full pathnames on each access check. But even if so, you could be doing your checking in the higher level code then where you have a vfsmount available.
The LSM hooks are pretty high-level, meant for being able to plug in different access checks. That's the right level of abstraction. We just need the vfsmounts passed down as well. That's what the bulk of common code patches are there for.
Andreas
-
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Serge E. HallynQuoted 1 line · Serge E. Hallyn
Quoting Stephen Smalley ([email protected]):
[...]
Quoted 19 lines
Quoted 4 lines
If we added support for named type transitions to SELinux, as proposed earlier by Kyle Moffett during this discussion, wouldn't that address that issue without needing a DTE-like approach? The concept is to add
Haven't read his message, but based on what you laid out here sure, that sounds good. It still, like my dte approach, might have some trouble with the wildcard/regex rules AA allows. And while it might perfectly reproduce my original DTE behavior, I don't think it does what AA wants on bind mounts. (Whether what AA wants for bind mounts makes sense I'm still not convinced, especially with user mounts coming soon (or already here?), but I'm staying out of that discussion for now)
Quoted 6 lines
the last component name as a further input to the labeling decision for new files, in addition to the existing use of the creating process' label, the parent directory label, and the kind of file. Then, you could have something like:
type_transition<domain> var_log_hosts_t:file "messages"messages_t;
Quoted 7 lines
Quoted 4 lines
The last component name is already available, so that doesn't require any changes to LSM, and it would be a straightforward extension of SELinux to support the above - it doesn't change the model at all, just adds a further input to the new file labeling logic.
And eliminates the need for restorecond?
Unlikely in the short term - restorecond is also used to reset contexts on critical files in /etc that might loose the context because tools used to update them are not correctly preserving contexts (e.g., /etc/mtab, etc/resolv.conf).
Actually - this whole notion restorecond as a critical component of SELinux because of a "new file problem" is pretty overblown. The default config file ships with:
/etc/resolv.conf/etc/samba/secrets.tdb/etc/mtab/var/run/utmp/var/log/wtmp
~/public_html
~/.mozilla/plugins/libflashplayer.so
So the only things that would be helped by type_transition rules with a name component would be public_html and libflashplayer.so.
Karl
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Serge E. HallynQuoted 86 lines · Serge E. Hallyn
Quoted 12 lines · Andreas Gruenbacher
Quoted 11 lines · Stephen Smalley
Quoted 10 lines · Andreas Gruenbacher
Quoted 9 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 2 lines
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was
That was the decision at ksummit last year, yes.
Quoted 46 lines
Quoted 5 lines
Quoted 1 line
no, then we could stop the entire discussion right there.
There is a difference between using the pathname at the kernel/userland interface as part of configuring a security mechanism and using it as the basis for the runtime checking itself.
Yes, there is a difference. When I say pathname based security mechanism, I literally mean a pathname based security mechanism, meaning the pathnames determine the outcome o the decision. This includes designs that are based on different abstractions internally, but the bahavior observable from user-space must be the same (or else, it's a different model).
Unfortunately, translating pathnames to labels destroys this fundamental abstraction. We explained why this is so in the following postings:
http://lkml.org/lkml/2007/6/9/94 http://lkml.org/lkml/2007/6/10/141Quoted 4 lines
Further, there is a difference between generating and matching full pathnames on each access vs. caching information in the parent dentry and making decisions based on that cached information and the last component-name.
Wait, you are mixing two issues here: access checks on existing files, and the creation of new files. For AppArmor as it stands today the two are the same, but when looking at emulating AppArmor using labels, they are not. Let's look at things one at a time.
Generating and matching full pathnames on each access takes time, no question about that. (In fact we are not checking on each access but only when pathnames are involved, such as on open. Filehandle based operations do not require access checks, but that's not a very important difference at this level of discussion.) That's a quantitative statement though, not a qualitative one: retrieving xattrs and checking labels also takes time; additional checks are never for free. We find that doing the pathname checks is easily fast enough. You may disagree, but then you don't have to use AppArmor, and we are not standing in your way.
As far as new files are concerned, basing decisions on the parent dentry and component name requires that you know where in the filesystem hierarchy this dentry is located: with bind mounts, the same dentry shows up in multiple locations in a process's namespace, corresponding to different pathnames. In other words, to make the right decision, the dentry alone is not enough; it takes a <dentry, vfsmount> pair. So there we are again.
Quoted 1 line
From the point on where you have a <dentry, vfsmount> pair of objects, you can
Quoted 9 lines
do two things: you can compute the full pathname and base your decision on that, or you can do some caching to hopefully cut some of that work short frequently enough to set off the additional cost. The difference between the two approaches is quantitative -- if there is a difference in results, then that's obviously a bug. I believe that caching could speed up things measurably, but up to this point, neither I nor anybody else had the time to look into it, and so we are not doing it -- not yet, any perhaps never at all. It may be counter to your intuition, but doing those checks is not a big issue.
My approach in DTE, which used pathnames to assign TE labels in the kernel, was to use a 'shadow tree' to the vfsmnt+dentry tree, filled out at policy load time to the depth of the deepest policy rule. For the behavior I was after, bind mounts were handled by storing pointers from the new mount to the original, but the behavior AA wants would be different. Namespace clones are easily handled this way by copying the cached pointers to the shadow tree. And it was pretty fast.
Quoted 7 lines
When I talked about this with Tony last year, the biggest shortcoming to use this for AA was the wildcards. For instance, if there is a rule for
/var/log/HOSTS/*/messages, then when/var/log/HOSTS/sergelap/is created, a new rule would have to be created on the fly to tag/var/log/HOSTS/sergelap/messagesif it is created. How to represent that when only/var/logor/var/log/HOSTSexists at policy load time would be, well, a fun problem to solve actually :)
If we added support for named type transitions to SELinux, as proposed earlier by Kyle Moffett during this discussion, wouldn't that address that issue without needing a DTE-like approach? The concept is to add the last component name as a further input to the labeling decision for new files, in addition to the existing use of the creating process' label, the parent directory label, and the kind of file. Then, you could have something like:type_transition <domain> var_log_hosts_t:file "messages" messages_t;
The last component name is already available, so that doesn't require any changes to LSM, and it would be a straightforward extension of SELinux to support the above - it doesn't change the model at all, just adds a further input to the new file labeling logic.
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Andreas GruenbacherHi!
Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Quoted 8 lines
Quoted 6 lines
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course. /proc only uses pathnames in a few places, but
/proc/mountswill silently fail and produce garbage entries. That's not ideal of course; we should fix that somehow.Note that this has nothing to do with the AppArmor discussion ...
This has everything to do with AA discussion.
Quoted 7 lines
How pathnames are used in /proc has *nothing* to do with AppAmor.
Quoted 2 lines
You took unreliable, for-user-info kernel subsystem, and made security subsystem depend on it. Oops.
Wrong. I said that the kernel uses pathnames for informational purposes only, not that they are unreliable. Don't turn words around in my
Well, we know they are unreliable from other sources.
Quoted 5 lines
The pathnames that
d_pathcomputes exactly reflect what the dcache knows. (This requires thed_pathfixes that are included in the AppArmor series and have also been posted independently, and the reasons why these fixes are needed are well explained in the mails.) The resulting pathnames are anything but unreliable.
Yes?
Like... everything but AA works with very long paths?
Like... deleted files handling, where AA used to pick random name?
Like... races during tree renames, where AA may use name file never ever had?
Pavel
Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Andreas GruenbacherQuoted 30 lines · Andreas Gruenbacher
Quoted 14 lines · Stephen Smalley
Quoted 13 lines · Andreas Gruenbacher
Quoted 11 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
Quoted 8 lines
Quoted 4 lines
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was no, then we could stop the entire discussion right there.
There is a difference between using the pathname at the kernel/userland interface as part of configuring a security mechanism and using it as the basis for the runtime checking itself.
Yes, there is a difference. When I say pathname based security mechanism, I literally mean a pathname based security mechanism, meaning the pathnames determine the outcome o the decision. This includes designs that are based on different abstractions internally, but the bahavior observable from user-space must be the same (or else, it's a different model).
Quoted 5 lines
Unfortunately, translating pathnames to labels destroys this fundamental abstraction. We explained why this is so in the following postings:
http://lkml.org/lkml/2007/6/9/94 http://lkml.org/lkml/2007/6/10/141
I don't really see the specific reasons explained in the first posting; the second one has a list of problems, but I'd question their validity:
- New files: Adding the last component name as a further input to the logic for determining the label of a new file would address many of the concerns here.
- Renaming: Do you honestly believe that renaming a file or directory tree should automatically change its protection without explicit action by the user/admin? I don't, naturally, and Linux DAC certainly doesn't work that way. I view the change-protections-on-rename behavior of AA as a flaw, not something to be emulated.
- New Policies: So there may be more work to be done up front in the label-based approach when developing policies. Isn't that where you want to front load the work? Versus doing it at runtime? Do you expect users to be rewriting their policies constantly on their production systems?
- File systems that do not support labels: I'm a bit surprised that you would advocate this given your experience in developing EA and ACL support for local filesystems and NFS. The pathname-based approach in NFS seems even scarier than in the local case - the clients may have different views of the namespace than one another or the server and the potential for inconsistent views of the tree (particularly as it is being modified) during access checks is only greater in the NFS case, right? It may be more expedient to implement, but is it the right technical approach?
But possibly the larger question here is whether your abstraction is fundamentally broken/leaky. Even with your "native" implementation in AA, you concede that there are cases where it breaks down, right? e.g. as the path returned by d_path may in fact differ from the path that was looked up, as the tree can change between time-of-lookup and time-of-path-generation?
Quoted 1 line
[cut here for lack of time right now -- will take another look later]
I'm hoping you'll ultimately respond to the questions I have raised (a couple times now) about why this fundamentally differs from audit and/or inotify, which take pathnames as part of configuring the mechanism but do not generate them and match them on each access.
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Without the vfsmounts propagated down you won't know the pathnames. Whether or not a different problem can be solved without the vfsmounts is not really relevant.
Quoted 8 lines
Quoted 3 lines
Well, that presumes that your mechanism has to generate full pathnames on each access check. But even if so, you could be doing your checking in the higher level code then where you have a vfsmount available.
The LSM hooks are pretty high-level, meant for being able to plug in different access checks. That's the right level of abstraction. We just need the vfsmounts passed down as well. That's what the bulk of common code patches are there for.
Well, if you succeed in that, does that mean that the read-only bind mount folks should go back to that approach? Just looking for a little bit of consistency among different efforts...
Signature · 2 lines
Stephen Smalley National Security Agency
Re: [AppArmor 38/45] AppArmor: Module and LSM hooks
In reply to Stephen SmalleyQuoted 95 lines · Stephen Smalley
Quoted 86 lines · Serge E. Hallyn
Quoted 12 lines · Andreas Gruenbacher
Quoted 11 lines · Stephen Smalley
Quoted 10 lines · Andreas Gruenbacher
Quoted 9 lines · Stephen Smalley
Quoted 7 lines · Andreas Gruenbacher
Quoted 3 lines · Pavel Machek
How will kernel work with very long paths? I'd suspect some problems, if path is 1MB long and I attempt to print it in /proc somewhere.
Pathnames are only used for informational purposes in the kernel, except in AppArmor of course.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 2 lines
I don't mean this as a flame, but isn't the above statement the very crux of this discussion?
I think the question at the core of it all is, shall a pathname based security mechanism be allowed. I was under the impression that this question had already been answered affirmatively. If the answer here was
That was the decision at ksummit last year, yes.
Quoted 46 lines
Quoted 5 lines
Quoted 1 line
no, then we could stop the entire discussion right there.
There is a difference between using the pathname at the kernel/userland interface as part of configuring a security mechanism and using it as the basis for the runtime checking itself.
Yes, there is a difference. When I say pathname based security mechanism, I literally mean a pathname based security mechanism, meaning the pathnames determine the outcome o the decision. This includes designs that are based on different abstractions internally, but the bahavior observable from user-space must be the same (or else, it's a different model).
Unfortunately, translating pathnames to labels destroys this fundamental abstraction. We explained why this is so in the following postings:
http://lkml.org/lkml/2007/6/9/94 http://lkml.org/lkml/2007/6/10/141Quoted 4 lines
Further, there is a difference between generating and matching full pathnames on each access vs. caching information in the parent dentry and making decisions based on that cached information and the last component-name.
Wait, you are mixing two issues here: access checks on existing files, and the creation of new files. For AppArmor as it stands today the two are the same, but when looking at emulating AppArmor using labels, they are not. Let's look at things one at a time.
Generating and matching full pathnames on each access takes time, no question about that. (In fact we are not checking on each access but only when pathnames are involved, such as on open. Filehandle based operations do not require access checks, but that's not a very important difference at this level of discussion.) That's a quantitative statement though, not a qualitative one: retrieving xattrs and checking labels also takes time; additional checks are never for free. We find that doing the pathname checks is easily fast enough. You may disagree, but then you don't have to use AppArmor, and we are not standing in your way.
As far as new files are concerned, basing decisions on the parent dentry and component name requires that you know where in the filesystem hierarchy this dentry is located: with bind mounts, the same dentry shows up in multiple locations in a process's namespace, corresponding to different pathnames. In other words, to make the right decision, the dentry alone is not enough; it takes a <dentry, vfsmount> pair. So there we are again.
Quoted 1 line
From the point on where you have a <dentry, vfsmount> pair of objects, you can
Quoted 9 lines
do two things: you can compute the full pathname and base your decision on that, or you can do some caching to hopefully cut some of that work short frequently enough to set off the additional cost. The difference between the two approaches is quantitative -- if there is a difference in results, then that's obviously a bug. I believe that caching could speed up things measurably, but up to this point, neither I nor anybody else had the time to look into it, and so we are not doing it -- not yet, any perhaps never at all. It may be counter to your intuition, but doing those checks is not a big issue.
My approach in DTE, which used pathnames to assign TE labels in the kernel, was to use a 'shadow tree' to the vfsmnt+dentry tree, filled out at policy load time to the depth of the deepest policy rule. For the behavior I was after, bind mounts were handled by storing pointers from the new mount to the original, but the behavior AA wants would be different. Namespace clones are easily handled this way by copying the cached pointers to the shadow tree. And it was pretty fast.
Quoted 7 lines
When I talked about this with Tony last year, the biggest shortcoming to use this for AA was the wildcards. For instance, if there is a rule for
/var/log/HOSTS/*/messages, then when/var/log/HOSTS/sergelap/is created, a new rule would have to be created on the fly to tag/var/log/HOSTS/sergelap/messagesif it is created. How to represent that when only/var/logor/var/log/HOSTSexists at policy load time would be, well, a fun problem to solve actually :)
Quoted 3 lines
If we added support for named type transitions to SELinux, as proposed earlier by Kyle Moffett during this discussion, wouldn't that address that issue without needing a DTE-like approach? The concept is to add
Haven't read his message, but based on what you laid out here sure, that sounds good. It still, like my dte approach, might have some trouble with the wildcard/regex rules AA allows. And while it might perfectly reproduce my original DTE behavior, I don't think it does what AA wants on bind mounts. (Whether what AA wants for bind mounts makes sense I'm still not convinced, especially with user mounts coming soon (or already here?), but I'm staying out of that discussion for now)
Quoted 10 lines
the last component name as a further input to the labeling decision for new files, in addition to the existing use of the creating process' label, the parent directory label, and the kind of file. Then, you could have something like:
type_transition<domain> var_log_hosts_t:file "messages"messages_t;The last component name is already available, so that doesn't require any changes to LSM, and it would be a straightforward extension of SELinux to support the above - it doesn't change the model at all, just adds a further input to the new file labeling logic.
And eliminates the need for restorecond?
thanks,
-serge
Re: [AppArmor 39/45] AppArmor: Profile loading and manipulation, pathname matching
In reply to Pavel MachekQuoted 4 lines · Pavel Machek
But you have that regex in _user_ space, in a place where policy is loaded into kernel.
AA has regex parser in _kernel_ space, which is very wrong.
That regex parser only applies user defined policy. The logical connection between your two points doesn't exist.
Regards,
Lars
Signature · 8 lines
Teamlead Kernel, SuSE Labs, Research and Development SUSE LINUX Products GmbH, GF: Markus Rex, HRB 16746 (AG Nürnberg) "Experience is the name everyone gives to their mistakes." -- Oscar Wilde - To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html