ima: special files integrity verification implementation
This patch implements special files integrity verification hooks.
It maintains a hashes of symbolic links, device nodes' major and
minor numbers.
Signed-off-by: Dmitry Kasatkin <d.kasatkin@samsung.com>
diff --git a/fs/namei.c b/fs/namei.c
index d0d858b..f3675e2 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2958,7 +2958,7 @@
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);
+ ima_dir_update(&nd->path, NULL, NULL);
mutex_unlock(&dir->d_inode->i_mutex);
if (error <= 0) {
@@ -3460,7 +3460,7 @@
break;
}
if (!error)
- ima_dir_update(&path, dentry);
+ ima_dir_update(&path, dentry, NULL);
out:
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
@@ -3519,7 +3519,7 @@
if (!error)
error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
if (!error)
- ima_dir_update(&path, dentry);
+ ima_dir_update(&path, dentry, NULL);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3639,7 +3639,7 @@
goto exit3;
error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
if (!error)
- ima_dir_update(&nd.path, NULL);
+ ima_dir_update(&nd.path, NULL, NULL);
exit3:
dput(dentry);
exit2:
@@ -3761,7 +3761,7 @@
goto exit2;
error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode);
if (!error)
- ima_dir_update(&nd.path, NULL);
+ ima_dir_update(&nd.path, NULL, NULL);
exit2:
dput(dentry);
}
@@ -3854,7 +3854,7 @@
if (!error)
error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
if (!error)
- ima_dir_update(&path, dentry);
+ ima_dir_update(&path, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3998,7 +3998,7 @@
goto out_dput;
error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry, &delegated_inode);
if (!error)
- ima_dir_update(&new_path, NULL);
+ ima_dir_update(&new_path, NULL, NULL);
out_dput:
done_path_create(&new_path, new_dentry);
if (delegated_inode) {
@@ -4317,9 +4317,9 @@
new_dir->d_inode, new_dentry,
&delegated_inode, flags);
if (!error) {
- ima_dir_update(&oldnd.path, NULL);
+ ima_dir_update(&oldnd.path, NULL, NULL);
if (!path_equal(&oldnd.path, &newnd.path))
- ima_dir_update(&newnd.path, NULL);
+ ima_dir_update(&newnd.path, NULL, NULL);
}
exit5:
dput(new_dentry);
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 704d5ef..a980e0e 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -83,7 +83,8 @@
#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);
-extern void ima_dir_update(struct path *dir, struct dentry *dentry);
+extern void ima_dir_update(struct path *dir, struct dentry *dentry,
+ const char *link);
#else
static inline int ima_dir_check(struct path *dir, int mask)
{
@@ -95,7 +96,8 @@
return 0;
}
-static inline void ima_dir_update(struct path *dir, struct dentry *dentry)
+static inline void ima_dir_update(struct path *dir, struct dentry *dentry,
+ const char *link)
{
return;
}
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index ce84666..18c6412 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -849,7 +849,7 @@
if (!err) {
res->mnt = mntget(path.mnt);
res->dentry = dget(dentry);
- ima_dir_update(&path, dentry);
+ ima_dir_update(&path, dentry, NULL);
}
}
done_path_create(&path, dentry);
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 38e243d..3af74e1 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -155,7 +155,7 @@
/* IMA policy related functions */
enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK,
- FIRMWARE_CHECK, DIR_CHECK, POST_SETATTR};
+ FIRMWARE_CHECK, DIR_CHECK, SPECIAL_CHECK, POST_SETATTR};
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
int flags);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index a99eb6d..250c3c0 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -325,6 +325,9 @@
{
char *pathname = NULL;
+ if (!path->mnt)
+ return NULL;
+
*pathbuf = __getname();
if (*pathbuf) {
pathname = d_absolute_path(path, *pathbuf, PATH_MAX);
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index a86332d..3857686 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -202,7 +202,7 @@
cause = "missing-hash";
status = INTEGRITY_NOLABEL;
- if (opened & FILE_CREATED) {
+ if (S_ISREG(inode->i_mode) && (opened & FILE_CREATED)) {
iint->flags |= IMA_NEW_FILE;
status = INTEGRITY_PASS;
}
diff --git a/security/integrity/ima/ima_dir.c b/security/integrity/ima/ima_dir.c
index c3f9c1d..516bbd7 100644
--- a/security/integrity/ima/ima_dir.c
+++ b/security/integrity/ima/ima_dir.c
@@ -131,11 +131,12 @@
static int ima_dir_collect(struct integrity_iint_cache *iint,
struct path *path, struct file *file,
struct evm_ima_xattr_data **xattr_value,
- int *xattr_len)
+ int *xattr_len, const char *link)
{
struct dentry *dentry = path->dentry;
struct inode *inode = dentry->d_inode;
int rc = -EINVAL;
+ u32 dev;
struct {
struct ima_digest_data hdr;
char digest[IMA_MAX_DIGEST_SIZE];
@@ -157,6 +158,14 @@
case S_IFDIR:
rc = ima_calc_dir_hash(path, file, &hash.hdr);
break;
+ case S_IFIFO: case S_IFSOCK:
+ case S_IFCHR: case S_IFBLK:
+ dev = new_encode_dev(inode->i_rdev);
+ rc = ima_calc_buffer_hash(&dev, sizeof(dev), &hash.hdr);
+ break;
+ case S_IFLNK:
+ rc = ima_calc_buffer_hash(link, strlen(link), &hash.hdr);
+ break;
default:
pr_debug("UKNOWN: dentry: %s, 0%o\n",
dentry->d_name.name, inode->i_mode & S_IFMT);
@@ -182,14 +191,16 @@
return rc;
}
-static int dir_measurement(struct path *path, struct file *file, int mask)
+static int dir_measurement(struct path *path, struct file *file, int mask,
+ const char *link)
{
struct dentry *dentry = path->dentry;
struct inode *inode = dentry->d_inode;
struct integrity_iint_cache *iint;
char *pathbuf = NULL;
const char *pathname;
- int rc = 0, action, xattr_len = 0, func = DIR_CHECK;
+ int rc = 0, action, xattr_len = 0;
+ int func = S_ISDIR(inode->i_mode) ? DIR_CHECK : SPECIAL_CHECK;
struct evm_ima_xattr_data *xattr_value = NULL;
if (!ima_dir_enabled || !ima_initialized)
@@ -211,9 +222,10 @@
mutex_lock(&inode->i_mutex);
} else {
+ int func = S_ISDIR(inode->i_mode) ? DIR_CHECK : SPECIAL_CHECK;
/* Determine if in appraise/measurement policy,
* returns IMA_MEASURE, IMA_APPRAISE bitmask. */
- action = ima_must_appraise(inode, mask, DIR_CHECK);
+ action = ima_must_appraise(inode, mask, func);
if (!action)
return 0;
@@ -240,7 +252,7 @@
if (!action)
goto out_locked;
- rc = ima_dir_collect(iint, path, file, &xattr_value, &xattr_len);
+ rc = ima_dir_collect(iint, path, file, &xattr_value, &xattr_len, link);
if (rc)
goto out_locked;
@@ -267,19 +279,17 @@
{
BUG_ON(!S_ISDIR(dir->dentry->d_inode->i_mode));
- return dir_measurement(dir, NULL, mask);
+ return dir_measurement(dir, NULL, mask, NULL);
}
EXPORT_SYMBOL_GPL(ima_dir_check);
int ima_special_check(struct file *file, int mask)
{
- if (!S_ISDIR(file->f_dentry->d_inode->i_mode))
- return 0;
- return dir_measurement(&file->f_path, file, mask);
+ return dir_measurement(&file->f_path, file, mask, NULL);
}
static void ima_dir_update_xattr(struct integrity_iint_cache *iint,
- struct path *path)
+ struct path *path, const char *link)
{
struct dentry *dentry = path->dentry;
struct inode *inode = NULL;
@@ -287,12 +297,13 @@
if (!iint) {
/* if iint is NULL, then we allocated iint for new directory */
- int action;
+ int action, func;
inode = dentry->d_inode;
+ func = S_ISDIR(inode->i_mode) ? DIR_CHECK : SPECIAL_CHECK;
/* Determine if in appraise/measurement policy */
- action = ima_must_appraise(inode, MAY_READ, DIR_CHECK);
+ action = ima_must_appraise(inode, MAY_READ, func);
if (!action)
return;
@@ -307,7 +318,7 @@
iint->ima_file_status = INTEGRITY_PASS;
}
- rc = ima_dir_collect(iint, path, NULL, NULL, NULL);
+ rc = ima_dir_collect(iint, path, NULL, NULL, NULL, link);
if (!rc)
ima_fix_xattr(dentry, iint);
out:
@@ -324,7 +335,7 @@
* and is used to re-calculate and update integrity data.
* It is called with dir i_mutex locked.
*/
-void ima_dir_update(struct path *dir, struct dentry *dentry)
+void ima_dir_update(struct path *dir, struct dentry *dentry, const char *link)
{
struct inode *inode = dir->dentry->d_inode;
struct integrity_iint_cache *iint;
@@ -345,7 +356,7 @@
/* new entry -> set initial security.ima value */
struct path path = { .mnt = dir->mnt, .dentry = dentry };
BUG_ON(!dentry->d_inode);
- ima_dir_update_xattr(NULL, &path);
+ ima_dir_update_xattr(NULL, &path, link);
}
/* do not reset flags for directories, correct ?
@@ -353,6 +364,12 @@
*/
iint->flags &= ~IMA_COLLECTED;
if (iint->flags & IMA_APPRAISE)
- ima_dir_update_xattr(iint, dir);
+ ima_dir_update_xattr(iint, dir, NULL);
}
EXPORT_SYMBOL_GPL(ima_dir_update);
+
+int ima_link_check(struct dentry *dentry, const char *link)
+{
+ struct path path = { .mnt = NULL, .dentry = dentry };
+ return dir_measurement(&path, NULL, MAY_READ, link);
+}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 296f4de..f36cf35 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -521,6 +521,8 @@
entry->func = BPRM_CHECK;
else if (strcmp(args[0].from, "DIR_CHECK") == 0)
entry->func = DIR_CHECK;
+ else if (strcmp(args[0].from, "SPECIAL_CHECK") == 0)
+ entry->func = SPECIAL_CHECK;
else
result = -EINVAL;
if (!result)