Re: [PATCH 1/2] ima: hooks for directory integrity protection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 2013-12-23 at 10:54 +0200, Dmitry Kasatkin wrote: 
> Hi,
> 
> On 12/12/13 15:39, Mimi Zohar wrote:
> > On Mon, 2013-11-18 at 22:24 +0200, Dmitry Kasatkin wrote: 
> >> Both IMA-appraisal and EVM protect the integrity of regular files.
> >> IMA protects file data integrity, while EVM protects the file meta-data
> >> integrity, such as file attributes and extended attributes. This patch
> >> set adds hooks for offline directory integrity protection.
> >>
> >> An inode itself does not have any file name associated with it.  The
> >> association of the file name to inode is done via directory entries.
> >> On a running system, mandatory and/or discretionary access control prevent
> >> unprivileged file deletion, file name change, or hardlink creation.
> >> In an offline attack, without these protections, the association between
> >> a file name and an inode is unprotected. Files can be deleted, renamed
> >> or moved from one directory to another one. In all of these cases,
> >> the integrity of the file data and metadata is good.
> >>
> >> To prevent such attacks, it is necessary to protect integrity of directory
> >> content.
> > Thanks Dmitry for re-posting these 'directory integrity protection'
> > patches.
> > The patches have evolved nicely.  Perhaps not a formal changelog, but a
> > short
> > summary of the major changes, would have been nice.
> 
> Will do in re-post...
> 
> >> This patch adds 2 new hooks for directory integrity protection:
> >>   ima_dir_check() and ima_dir_update().
> > Although these patches are probably bisect safe, as they rely on
> > Kconfig, the normal ordering of patches is to define a function and use
> > it in the same patch.  In the case, like here, where a new function/hook
> > is defined in one subsystem, but called from another, we can split them,
> > but the normal convention is to add the new function/hook first in one
> > patch, and then use it in a subsequent patch.
> 
> Sorry, I did not get...
> 
> >> "normal convention is to add the new function/hook first in one
> patch, and then use it in a subsequent patch. "
> 
> This is what patches do. Add hook in one patch and use in an other..

Yes, define the hook/function, before using it.

thanks,

Mimi

> 
> Do you mean to swap the order of these 2 patches??

> >
> >> ima_dir_check() is called to verify integrity of the the directory during
> >> the initial path lookup.
> >>
> >> ima_dir_update() is called from several places in namei.c, when the directory
> >> content is changing, for updating the directory hash.
> >>
> >> Signed-off-by: Dmitry Kasatkin <d.kasatkin@xxxxxxxxxxx>
> >> ---
> >>  fs/namei.c                        | 42 ++++++++++++++++++++++++++++++++++++---
> >>  fs/open.c                         |  6 ++++++
> >>  include/linux/ima.h               | 23 +++++++++++++++++++++
> >>  net/unix/af_unix.c                |  2 ++
> >>  security/integrity/ima/ima_main.c |  3 +++
> >>  5 files changed, 73 insertions(+), 3 deletions(-)
> >>
> >> diff --git a/fs/namei.c b/fs/namei.c
> >> index 645268f..d0e1821 100644
> >> --- a/fs/namei.c
> >> +++ b/fs/namei.c
> >> @@ -1469,16 +1469,33 @@ static int lookup_slow(struct nameidata *nd, struct path *path)
> >>  	return 0;
> >>  }
> >>
> >> +static inline int may_lookup_ima(struct nameidata *nd, int err)
> >> +{
> >> +	if (err)
> >> +		return err;
> >> +	err = ima_dir_check(&nd->path, MAY_EXEC|MAY_NOT_BLOCK);
> >> +	if (err != -ECHILD)
> >> +		return err;
> > A short comment here, why -ECHILD is special, would be good.
> 
> This is the same as for inode_permission().
> If calling code requires locking, we have to interrupt RCU path walk and
> re-start with ref path walk...
> 
> >> +	if (unlazy_walk(nd, NULL))
> >> +		return -ECHILD;
> >> +	return ima_dir_check(&nd->path, MAY_EXEC);
> >> +}
> >> +
> >>  static inline int may_lookup(struct nameidata *nd)
> >>  {
> >> +	int err = 0;
> >> +
> >>  	if (nd->flags & LOOKUP_RCU) {
> >> -		int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
> >> +		err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
> >>  		if (err != -ECHILD)
> >> -			return err;
> >> +			return may_lookup_ima(nd, err);
> >>  		if (unlazy_walk(nd, NULL))
> >>  			return -ECHILD;
> >>  	}
> >> -	return inode_permission(nd->inode, MAY_EXEC);
> >> +	err = inode_permission(nd->inode, MAY_EXEC);
> >> +	if (err)
> >> +		return err;
> >> +	return ima_dir_check(&nd->path, MAY_EXEC);
> >>  }
> >>
> >>  static inline int handle_dots(struct nameidata *nd, int type)
> >> @@ -2956,6 +2973,8 @@ retry_lookup:
> >>  	}
> >>  	mutex_lock(&dir->d_inode->i_mutex);
> >>  	error = lookup_open(nd, path, file, op, got_write, opened);
> >> +	if (error >= 0 && (*opened & FILE_CREATED))
> >> +		ima_dir_update(&nd->path, NULL);
> >>  	mutex_unlock(&dir->d_inode->i_mutex);
> >>
> >>  	if (error <= 0) {
> >> @@ -3454,6 +3473,8 @@ retry:
> >>  			error = vfs_mknod(path.dentry->d_inode,dentry,mode,0);
> >>  			break;
> >>  	}
> >> +	if (!error)
> >> +		ima_dir_update(&path, dentry);
> >>  out:
> >>  	done_path_create(&path, dentry);
> >>  	if (retry_estale(error, lookup_flags)) {
> >> @@ -3510,6 +3531,8 @@ retry:
> >>  	error = security_path_mkdir(&path, dentry, mode);
> >>  	if (!error)
> >>  		error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
> >> +	if (!error)
> >> +		ima_dir_update(&path, dentry);
> >>  	done_path_create(&path, dentry);
> >>  	if (retry_estale(error, lookup_flags)) {
> >>  		lookup_flags |= LOOKUP_REVAL;
> >> @@ -3626,6 +3649,8 @@ retry:
> >>  	if (error)
> >>  		goto exit3;
> >>  	error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
> >> +	if (!error)
> >> +		ima_dir_update(&nd.path, NULL);
> >>  exit3:
> >>  	dput(dentry);
> >>  exit2:
> >> @@ -3721,6 +3746,8 @@ retry:
> >>  		if (error)
> >>  			goto exit2;
> >>  		error = vfs_unlink(nd.path.dentry->d_inode, dentry);
> >> +		if (!error)
> >> +			ima_dir_update(&nd.path, NULL);
> >>  exit2:
> >>  		dput(dentry);
> >>  	}
> >> @@ -3801,6 +3828,8 @@ retry:
> >>  	error = security_path_symlink(&path, dentry, from->name);
> >>  	if (!error)
> >>  		error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
> >> +	if (!error)
> >> +		ima_dir_update(&path, dentry);
> >>  	done_path_create(&path, dentry);
> >>  	if (retry_estale(error, lookup_flags)) {
> >>  		lookup_flags |= LOOKUP_REVAL;
> >> @@ -3919,6 +3948,8 @@ retry:
> >>  	if (error)
> >>  		goto out_dput;
> >>  	error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
> >> +	if (!error)
> >> +		ima_dir_update(&new_path, NULL);
> >>  out_dput:
> >>  	done_path_create(&new_path, new_dentry);
> >>  	if (retry_estale(error, how)) {
> >> @@ -4171,6 +4202,11 @@ retry:
> >>  		goto exit5;
> >>  	error = vfs_rename(old_dir->d_inode, old_dentry,
> >>  				   new_dir->d_inode, new_dentry);
> >> +	if (!error) {
> >> +		ima_dir_update(&oldnd.path, NULL);
> >> +		if (!path_equal(&oldnd.path, &newnd.path))
> >> +			ima_dir_update(&newnd.path, NULL);
> >> +	}
> >>  exit5:
> >>  	dput(new_dentry);
> >>  exit4:
> >> diff --git a/fs/open.c b/fs/open.c
> >> index d420331..021e2c5 100644
> >> --- a/fs/open.c
> >> +++ b/fs/open.c
> >> @@ -390,6 +390,9 @@ retry:
> >>  	error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
> >>  	if (error)
> >>  		goto dput_and_out;
> >> +	error = ima_dir_check(&path, MAY_EXEC);
> >> +	if (error)
> >> +		goto dput_and_out;
> >>
> >>  	set_fs_pwd(current->fs, &path);
> >>
> >> @@ -420,6 +423,9 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd)
> >>  		goto out_putf;
> >>
> >>  	error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
> >> +	if (error)
> >> +		goto out_putf;
> >> +	error = ima_dir_check(&f.file->f_path, MAY_EXEC);
> >>  	if (!error)
> >>  		set_fs_pwd(current->fs, &f.file->f_path);
> >>  out_putf:
> >> diff --git a/include/linux/ima.h b/include/linux/ima.h
> >> index 1b7f268..e33035e 100644
> >> --- a/include/linux/ima.h
> >> +++ b/include/linux/ima.h
> >> @@ -73,4 +73,27 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
> >>  	return 0;
> >>  }
> >>  #endif /* CONFIG_IMA_APPRAISE */
> >> +
> >> +#ifdef CONFIG_IMA_APPRAISE_DIRECTORIES
> >> +extern int ima_dir_check(struct path *dir, int mask);
> >> +extern int ima_special_check(struct file *file, int mask);
> > No mention was made of this new hook.  This should be a separate patch,
> > with its own patch description.
> 
> Yes. This is a patch split mistake.. Thanks.
> 
> >
> > thanks,
> >
> > Mimi
> >
> >> +extern void ima_dir_update(struct path *dir, struct dentry *dentry);
> >> +#else
> >> +static inline int ima_dir_check(struct path *dir, int mask)
> >> +{
> >> +	return 0;
> >> +}
> >> +
> >> +static inline int ima_special_check(struct file *file, int mask)
> >> +{
> >> +	return 0;
> >> +}
> >> +
> >> +static inline void ima_dir_update(struct path *dir, struct dentry *dentry)
> >> +{
> >> +	return;
> >> +}
> >> +
> >> +#endif /* CONFIG_IMA_APPRAISE_DIRECTORIES */
> >> +
> >>  #endif /* _LINUX_IMA_H */
> >> diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
> >> index 86de99a..6230a50 100644
> >> --- a/net/unix/af_unix.c
> >> +++ b/net/unix/af_unix.c
> >> @@ -115,6 +115,7 @@
> >>  #include <net/checksum.h>
> >>  #include <linux/security.h>
> >>  #include <linux/freezer.h>
> >> +#include <linux/ima.h>
> >>
> >>  struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE];
> >>  EXPORT_SYMBOL_GPL(unix_socket_table);
> >> @@ -841,6 +842,7 @@ static int unix_mknod(const char *sun_path, umode_t mode, struct path *res)
> >>  		if (!err) {
> >>  			res->mnt = mntget(path.mnt);
> >>  			res->dentry = dget(dentry);
> >> +			ima_dir_update(&path, dentry);
> >>  		}
> >>  	}
> >>  	done_path_create(&path, dentry);
> >> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> >> index 6c12811..18d76d8 100644
> >> --- a/security/integrity/ima/ima_main.c
> >> +++ b/security/integrity/ima/ima_main.c
> >> @@ -300,6 +300,9 @@ int ima_bprm_check(struct linux_binprm *bprm)
> >>   */
> >>  int ima_file_check(struct file *file, int mask)
> >>  {
> >> +	if (!S_ISREG(file->f_dentry->d_inode->i_mode))
> >> +		return ima_special_check(file, mask);
> >> +
> >>  	ima_rdwr_violation_check(file);
> >>  	return process_measurement(file, NULL,
> >>  				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
> >
> >
> 
> Thanks
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux