diff options
Diffstat (limited to 'security/integrity')
-rw-r--r-- | security/integrity/ima/ima.h | 21 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 13 | ||||
-rw-r--r-- | security/integrity/ima/ima_iint.c | 153 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 204 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 2 |
5 files changed, 152 insertions, 241 deletions
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 3fbcd1dda0ef..08408bd71462 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
@@ -70,6 +70,7 @@ int ima_init(void); | |||
70 | void ima_cleanup(void); | 70 | void ima_cleanup(void); |
71 | int ima_fs_init(void); | 71 | int ima_fs_init(void); |
72 | void ima_fs_cleanup(void); | 72 | void ima_fs_cleanup(void); |
73 | int ima_inode_alloc(struct inode *inode); | ||
73 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, | 74 | int ima_add_template_entry(struct ima_template_entry *entry, int violation, |
74 | const char *op, struct inode *inode); | 75 | const char *op, struct inode *inode); |
75 | int ima_calc_hash(struct file *file, char *digest); | 76 | int ima_calc_hash(struct file *file, char *digest); |
@@ -96,24 +97,20 @@ static inline unsigned long ima_hash_key(u8 *digest) | |||
96 | } | 97 | } |
97 | 98 | ||
98 | /* iint cache flags */ | 99 | /* iint cache flags */ |
99 | #define IMA_MEASURED 1 | 100 | #define IMA_MEASURED 0x01 |
100 | 101 | ||
101 | /* integrity data associated with an inode */ | 102 | /* integrity data associated with an inode */ |
102 | struct ima_iint_cache { | 103 | struct ima_iint_cache { |
104 | struct rb_node rb_node; /* rooted in ima_iint_tree */ | ||
105 | struct inode *inode; /* back pointer to inode in question */ | ||
103 | u64 version; /* track inode changes */ | 106 | u64 version; /* track inode changes */ |
104 | unsigned long flags; | 107 | unsigned char flags; |
105 | u8 digest[IMA_DIGEST_SIZE]; | 108 | u8 digest[IMA_DIGEST_SIZE]; |
106 | struct mutex mutex; /* protects: version, flags, digest */ | 109 | struct mutex mutex; /* protects: version, flags, digest */ |
107 | long readcount; /* measured files readcount */ | ||
108 | long writecount; /* measured files writecount */ | ||
109 | long opencount; /* opens reference count */ | ||
110 | struct kref refcount; /* ima_iint_cache reference count */ | ||
111 | struct rcu_head rcu; | ||
112 | }; | 110 | }; |
113 | 111 | ||
114 | /* LIM API function definitions */ | 112 | /* LIM API function definitions */ |
115 | int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, | 113 | int ima_must_measure(struct inode *inode, int mask, int function); |
116 | int mask, int function); | ||
117 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); | 114 | int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file); |
118 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, | 115 | void ima_store_measurement(struct ima_iint_cache *iint, struct file *file, |
119 | const unsigned char *filename); | 116 | const unsigned char *filename); |
@@ -122,13 +119,11 @@ int ima_store_template(struct ima_template_entry *entry, int violation, | |||
122 | void ima_template_show(struct seq_file *m, void *e, | 119 | void ima_template_show(struct seq_file *m, void *e, |
123 | enum ima_show_type show); | 120 | enum ima_show_type show); |
124 | 121 | ||
125 | /* radix tree calls to lookup, insert, delete | 122 | /* rbtree tree calls to lookup, insert, delete |
126 | * integrity data associated with an inode. | 123 | * integrity data associated with an inode. |
127 | */ | 124 | */ |
128 | struct ima_iint_cache *ima_iint_insert(struct inode *inode); | 125 | struct ima_iint_cache *ima_iint_insert(struct inode *inode); |
129 | struct ima_iint_cache *ima_iint_find_get(struct inode *inode); | 126 | struct ima_iint_cache *ima_iint_find(struct inode *inode); |
130 | void iint_free(struct kref *kref); | ||
131 | void iint_rcu_free(struct rcu_head *rcu); | ||
132 | 127 | ||
133 | /* IMA policy related functions */ | 128 | /* IMA policy related functions */ |
134 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; | 129 | enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; |
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 52015d098fdf..da36d2c085a4 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c | |||
@@ -105,20 +105,13 @@ err_out: | |||
105 | * mask: contains the permission mask | 105 | * mask: contains the permission mask |
106 | * fsmagic: hex value | 106 | * fsmagic: hex value |
107 | * | 107 | * |
108 | * Must be called with iint->mutex held. | 108 | * Return 0 to measure. For matching a DONT_MEASURE policy, no policy, |
109 | * | 109 | * or other error, return an error code. |
110 | * Return 0 to measure. Return 1 if already measured. | ||
111 | * For matching a DONT_MEASURE policy, no policy, or other | ||
112 | * error, return an error code. | ||
113 | */ | 110 | */ |
114 | int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode, | 111 | int ima_must_measure(struct inode *inode, int mask, int function) |
115 | int mask, int function) | ||
116 | { | 112 | { |
117 | int must_measure; | 113 | int must_measure; |
118 | 114 | ||
119 | if (iint->flags & IMA_MEASURED) | ||
120 | return 1; | ||
121 | |||
122 | must_measure = ima_match_policy(inode, function, mask); | 115 | must_measure = ima_match_policy(inode, function, mask); |
123 | return must_measure ? 0 : -EACCES; | 116 | return must_measure ? 0 : -EACCES; |
124 | } | 117 | } |
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c index afba4aef812f..4ae73040ab7b 100644 --- a/security/integrity/ima/ima_iint.c +++ b/security/integrity/ima/ima_iint.c | |||
@@ -12,98 +12,119 @@ | |||
12 | * File: ima_iint.c | 12 | * File: ima_iint.c |
13 | * - implements the IMA hooks: ima_inode_alloc, ima_inode_free | 13 | * - implements the IMA hooks: ima_inode_alloc, ima_inode_free |
14 | * - cache integrity information associated with an inode | 14 | * - cache integrity information associated with an inode |
15 | * using a radix tree. | 15 | * using a rbtree tree. |
16 | */ | 16 | */ |
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/module.h> | 18 | #include <linux/module.h> |
19 | #include <linux/spinlock.h> | 19 | #include <linux/spinlock.h> |
20 | #include <linux/radix-tree.h> | 20 | #include <linux/rbtree.h> |
21 | #include "ima.h" | 21 | #include "ima.h" |
22 | 22 | ||
23 | RADIX_TREE(ima_iint_store, GFP_ATOMIC); | 23 | static struct rb_root ima_iint_tree = RB_ROOT; |
24 | DEFINE_SPINLOCK(ima_iint_lock); | 24 | static DEFINE_SPINLOCK(ima_iint_lock); |
25 | static struct kmem_cache *iint_cache __read_mostly; | 25 | static struct kmem_cache *iint_cache __read_mostly; |
26 | 26 | ||
27 | int iint_initialized = 0; | 27 | int iint_initialized = 0; |
28 | 28 | ||
29 | /* ima_iint_find_get - return the iint associated with an inode | 29 | /* |
30 | * | 30 | * __ima_iint_find - return the iint associated with an inode |
31 | * ima_iint_find_get gets a reference to the iint. Caller must | ||
32 | * remember to put the iint reference. | ||
33 | */ | 31 | */ |
34 | struct ima_iint_cache *ima_iint_find_get(struct inode *inode) | 32 | static struct ima_iint_cache *__ima_iint_find(struct inode *inode) |
35 | { | 33 | { |
36 | struct ima_iint_cache *iint; | 34 | struct ima_iint_cache *iint; |
35 | struct rb_node *n = ima_iint_tree.rb_node; | ||
36 | |||
37 | assert_spin_locked(&ima_iint_lock); | ||
38 | |||
39 | while (n) { | ||
40 | iint = rb_entry(n, struct ima_iint_cache, rb_node); | ||
41 | |||
42 | if (inode < iint->inode) | ||
43 | n = n->rb_left; | ||
44 | else if (inode > iint->inode) | ||
45 | n = n->rb_right; | ||
46 | else | ||
47 | break; | ||
48 | } | ||
49 | if (!n) | ||
50 | return NULL; | ||
37 | 51 | ||
38 | rcu_read_lock(); | ||
39 | iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode); | ||
40 | if (!iint) | ||
41 | goto out; | ||
42 | kref_get(&iint->refcount); | ||
43 | out: | ||
44 | rcu_read_unlock(); | ||
45 | return iint; | 52 | return iint; |
46 | } | 53 | } |
47 | 54 | ||
48 | /** | 55 | /* |
49 | * ima_inode_alloc - allocate an iint associated with an inode | 56 | * ima_iint_find - return the iint associated with an inode |
50 | * @inode: pointer to the inode | ||
51 | */ | 57 | */ |
52 | int ima_inode_alloc(struct inode *inode) | 58 | struct ima_iint_cache *ima_iint_find(struct inode *inode) |
53 | { | 59 | { |
54 | struct ima_iint_cache *iint = NULL; | 60 | struct ima_iint_cache *iint; |
55 | int rc = 0; | ||
56 | |||
57 | iint = kmem_cache_alloc(iint_cache, GFP_NOFS); | ||
58 | if (!iint) | ||
59 | return -ENOMEM; | ||
60 | 61 | ||
61 | rc = radix_tree_preload(GFP_NOFS); | 62 | if (!IS_IMA(inode)) |
62 | if (rc < 0) | 63 | return NULL; |
63 | goto out; | ||
64 | 64 | ||
65 | spin_lock(&ima_iint_lock); | 65 | spin_lock(&ima_iint_lock); |
66 | rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint); | 66 | iint = __ima_iint_find(inode); |
67 | spin_unlock(&ima_iint_lock); | 67 | spin_unlock(&ima_iint_lock); |
68 | radix_tree_preload_end(); | ||
69 | out: | ||
70 | if (rc < 0) | ||
71 | kmem_cache_free(iint_cache, iint); | ||
72 | 68 | ||
73 | return rc; | 69 | return iint; |
74 | } | 70 | } |
75 | 71 | ||
76 | /* iint_free - called when the iint refcount goes to zero */ | 72 | static void iint_free(struct ima_iint_cache *iint) |
77 | void iint_free(struct kref *kref) | ||
78 | { | 73 | { |
79 | struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache, | ||
80 | refcount); | ||
81 | iint->version = 0; | 74 | iint->version = 0; |
82 | iint->flags = 0UL; | 75 | iint->flags = 0UL; |
83 | if (iint->readcount != 0) { | ||
84 | printk(KERN_INFO "%s: readcount: %ld\n", __func__, | ||
85 | iint->readcount); | ||
86 | iint->readcount = 0; | ||
87 | } | ||
88 | if (iint->writecount != 0) { | ||
89 | printk(KERN_INFO "%s: writecount: %ld\n", __func__, | ||
90 | iint->writecount); | ||
91 | iint->writecount = 0; | ||
92 | } | ||
93 | if (iint->opencount != 0) { | ||
94 | printk(KERN_INFO "%s: opencount: %ld\n", __func__, | ||
95 | iint->opencount); | ||
96 | iint->opencount = 0; | ||
97 | } | ||
98 | kref_init(&iint->refcount); | ||
99 | kmem_cache_free(iint_cache, iint); | 76 | kmem_cache_free(iint_cache, iint); |
100 | } | 77 | } |
101 | 78 | ||
102 | void iint_rcu_free(struct rcu_head *rcu_head) | 79 | /** |
80 | * ima_inode_alloc - allocate an iint associated with an inode | ||
81 | * @inode: pointer to the inode | ||
82 | */ | ||
83 | int ima_inode_alloc(struct inode *inode) | ||
103 | { | 84 | { |
104 | struct ima_iint_cache *iint = container_of(rcu_head, | 85 | struct rb_node **p; |
105 | struct ima_iint_cache, rcu); | 86 | struct rb_node *new_node, *parent = NULL; |
106 | kref_put(&iint->refcount, iint_free); | 87 | struct ima_iint_cache *new_iint, *test_iint; |
88 | int rc; | ||
89 | |||
90 | new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS); | ||
91 | if (!new_iint) | ||
92 | return -ENOMEM; | ||
93 | |||
94 | new_iint->inode = inode; | ||
95 | new_node = &new_iint->rb_node; | ||
96 | |||
97 | mutex_lock(&inode->i_mutex); /* i_flags */ | ||
98 | spin_lock(&ima_iint_lock); | ||
99 | |||
100 | p = &ima_iint_tree.rb_node; | ||
101 | while (*p) { | ||
102 | parent = *p; | ||
103 | test_iint = rb_entry(parent, struct ima_iint_cache, rb_node); | ||
104 | |||
105 | rc = -EEXIST; | ||
106 | if (inode < test_iint->inode) | ||
107 | p = &(*p)->rb_left; | ||
108 | else if (inode > test_iint->inode) | ||
109 | p = &(*p)->rb_right; | ||
110 | else | ||
111 | goto out_err; | ||
112 | } | ||
113 | |||
114 | inode->i_flags |= S_IMA; | ||
115 | rb_link_node(new_node, parent, p); | ||
116 | rb_insert_color(new_node, &ima_iint_tree); | ||
117 | |||
118 | spin_unlock(&ima_iint_lock); | ||
119 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
120 | |||
121 | return 0; | ||
122 | out_err: | ||
123 | spin_unlock(&ima_iint_lock); | ||
124 | mutex_unlock(&inode->i_mutex); /* i_flags */ | ||
125 | iint_free(new_iint); | ||
126 | |||
127 | return rc; | ||
107 | } | 128 | } |
108 | 129 | ||
109 | /** | 130 | /** |
@@ -116,11 +137,15 @@ void ima_inode_free(struct inode *inode) | |||
116 | { | 137 | { |
117 | struct ima_iint_cache *iint; | 138 | struct ima_iint_cache *iint; |
118 | 139 | ||
140 | if (!IS_IMA(inode)) | ||
141 | return; | ||
142 | |||
119 | spin_lock(&ima_iint_lock); | 143 | spin_lock(&ima_iint_lock); |
120 | iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); | 144 | iint = __ima_iint_find(inode); |
145 | rb_erase(&iint->rb_node, &ima_iint_tree); | ||
121 | spin_unlock(&ima_iint_lock); | 146 | spin_unlock(&ima_iint_lock); |
122 | if (iint) | 147 | |
123 | call_rcu(&iint->rcu, iint_rcu_free); | 148 | iint_free(iint); |
124 | } | 149 | } |
125 | 150 | ||
126 | static void init_once(void *foo) | 151 | static void init_once(void *foo) |
@@ -131,10 +156,6 @@ static void init_once(void *foo) | |||
131 | iint->version = 0; | 156 | iint->version = 0; |
132 | iint->flags = 0UL; | 157 | iint->flags = 0UL; |
133 | mutex_init(&iint->mutex); | 158 | mutex_init(&iint->mutex); |
134 | iint->readcount = 0; | ||
135 | iint->writecount = 0; | ||
136 | iint->opencount = 0; | ||
137 | kref_init(&iint->refcount); | ||
138 | } | 159 | } |
139 | 160 | ||
140 | static int __init ima_iintcache_init(void) | 161 | static int __init ima_iintcache_init(void) |
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index e662b89d4079..39d66dc2b8e9 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c | |||
@@ -36,179 +36,71 @@ static int __init hash_setup(char *str) | |||
36 | } | 36 | } |
37 | __setup("ima_hash=", hash_setup); | 37 | __setup("ima_hash=", hash_setup); |
38 | 38 | ||
39 | struct ima_imbalance { | ||
40 | struct hlist_node node; | ||
41 | unsigned long fsmagic; | ||
42 | }; | ||
43 | |||
44 | /* | 39 | /* |
45 | * ima_limit_imbalance - emit one imbalance message per filesystem type | 40 | * ima_rdwr_violation_check |
46 | * | 41 | * |
47 | * Maintain list of filesystem types that do not measure files properly. | 42 | * Only invalidate the PCR for measured files: |
48 | * Return false if unknown, true if known. | ||
49 | */ | ||
50 | static bool ima_limit_imbalance(struct file *file) | ||
51 | { | ||
52 | static DEFINE_SPINLOCK(ima_imbalance_lock); | ||
53 | static HLIST_HEAD(ima_imbalance_list); | ||
54 | |||
55 | struct super_block *sb = file->f_dentry->d_sb; | ||
56 | struct ima_imbalance *entry; | ||
57 | struct hlist_node *node; | ||
58 | bool found = false; | ||
59 | |||
60 | rcu_read_lock(); | ||
61 | hlist_for_each_entry_rcu(entry, node, &ima_imbalance_list, node) { | ||
62 | if (entry->fsmagic == sb->s_magic) { | ||
63 | found = true; | ||
64 | break; | ||
65 | } | ||
66 | } | ||
67 | rcu_read_unlock(); | ||
68 | if (found) | ||
69 | goto out; | ||
70 | |||
71 | entry = kmalloc(sizeof(*entry), GFP_NOFS); | ||
72 | if (!entry) | ||
73 | goto out; | ||
74 | entry->fsmagic = sb->s_magic; | ||
75 | spin_lock(&ima_imbalance_lock); | ||
76 | /* | ||
77 | * we could have raced and something else might have added this fs | ||
78 | * to the list, but we don't really care | ||
79 | */ | ||
80 | hlist_add_head_rcu(&entry->node, &ima_imbalance_list); | ||
81 | spin_unlock(&ima_imbalance_lock); | ||
82 | printk(KERN_INFO "IMA: unmeasured files on fsmagic: %lX\n", | ||
83 | entry->fsmagic); | ||
84 | out: | ||
85 | return found; | ||
86 | } | ||
87 | |||
88 | /* ima_read_write_check - reflect possible reading/writing errors in the PCR. | ||
89 | * | ||
90 | * When opening a file for read, if the file is already open for write, | ||
91 | * the file could change, resulting in a file measurement error. | ||
92 | * | ||
93 | * Opening a file for write, if the file is already open for read, results | ||
94 | * in a time of measure, time of use (ToMToU) error. | ||
95 | * | ||
96 | * In either case invalidate the PCR. | ||
97 | */ | ||
98 | enum iint_pcr_error { TOMTOU, OPEN_WRITERS }; | ||
99 | static void ima_read_write_check(enum iint_pcr_error error, | ||
100 | struct ima_iint_cache *iint, | ||
101 | struct inode *inode, | ||
102 | const unsigned char *filename) | ||
103 | { | ||
104 | switch (error) { | ||
105 | case TOMTOU: | ||
106 | if (iint->readcount > 0) | ||
107 | ima_add_violation(inode, filename, "invalid_pcr", | ||
108 | "ToMToU"); | ||
109 | break; | ||
110 | case OPEN_WRITERS: | ||
111 | if (iint->writecount > 0) | ||
112 | ima_add_violation(inode, filename, "invalid_pcr", | ||
113 | "open_writers"); | ||
114 | break; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Update the counts given an fmode_t | ||
120 | */ | ||
121 | static void ima_inc_counts(struct ima_iint_cache *iint, fmode_t mode) | ||
122 | { | ||
123 | BUG_ON(!mutex_is_locked(&iint->mutex)); | ||
124 | |||
125 | iint->opencount++; | ||
126 | if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | ||
127 | iint->readcount++; | ||
128 | if (mode & FMODE_WRITE) | ||
129 | iint->writecount++; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * ima_counts_get - increment file counts | ||
134 | * | ||
135 | * Maintain read/write counters for all files, but only | ||
136 | * invalidate the PCR for measured files: | ||
137 | * - Opening a file for write when already open for read, | 43 | * - Opening a file for write when already open for read, |
138 | * results in a time of measure, time of use (ToMToU) error. | 44 | * results in a time of measure, time of use (ToMToU) error. |
139 | * - Opening a file for read when already open for write, | 45 | * - Opening a file for read when already open for write, |
140 | * could result in a file measurement error. | 46 | * could result in a file measurement error. |
141 | * | 47 | * |
142 | */ | 48 | */ |
143 | void ima_counts_get(struct file *file) | 49 | static void ima_rdwr_violation_check(struct file *file) |
144 | { | 50 | { |
145 | struct dentry *dentry = file->f_path.dentry; | 51 | struct dentry *dentry = file->f_path.dentry; |
146 | struct inode *inode = dentry->d_inode; | 52 | struct inode *inode = dentry->d_inode; |
147 | fmode_t mode = file->f_mode; | 53 | fmode_t mode = file->f_mode; |
148 | struct ima_iint_cache *iint; | ||
149 | int rc; | 54 | int rc; |
55 | bool send_tomtou = false, send_writers = false; | ||
150 | 56 | ||
151 | if (!iint_initialized || !S_ISREG(inode->i_mode)) | 57 | if (!S_ISREG(inode->i_mode) || !ima_initialized) |
152 | return; | 58 | return; |
153 | iint = ima_iint_find_get(inode); | 59 | |
154 | if (!iint) | 60 | mutex_lock(&inode->i_mutex); /* file metadata: permissions, xattr */ |
155 | return; | ||
156 | mutex_lock(&iint->mutex); | ||
157 | if (!ima_initialized) | ||
158 | goto out; | ||
159 | rc = ima_must_measure(iint, inode, MAY_READ, FILE_CHECK); | ||
160 | if (rc < 0) | ||
161 | goto out; | ||
162 | 61 | ||
163 | if (mode & FMODE_WRITE) { | 62 | if (mode & FMODE_WRITE) { |
164 | ima_read_write_check(TOMTOU, iint, inode, dentry->d_name.name); | 63 | if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) |
64 | send_tomtou = true; | ||
165 | goto out; | 65 | goto out; |
166 | } | 66 | } |
167 | ima_read_write_check(OPEN_WRITERS, iint, inode, dentry->d_name.name); | ||
168 | out: | ||
169 | ima_inc_counts(iint, file->f_mode); | ||
170 | mutex_unlock(&iint->mutex); | ||
171 | 67 | ||
172 | kref_put(&iint->refcount, iint_free); | 68 | rc = ima_must_measure(inode, MAY_READ, FILE_CHECK); |
69 | if (rc < 0) | ||
70 | goto out; | ||
71 | |||
72 | if (atomic_read(&inode->i_writecount) > 0) | ||
73 | send_writers = true; | ||
74 | out: | ||
75 | mutex_unlock(&inode->i_mutex); | ||
76 | |||
77 | if (send_tomtou) | ||
78 | ima_add_violation(inode, dentry->d_name.name, "invalid_pcr", | ||
79 | "ToMToU"); | ||
80 | if (send_writers) | ||
81 | ima_add_violation(inode, dentry->d_name.name, "invalid_pcr", | ||
82 | "open_writers"); | ||
173 | } | 83 | } |
174 | 84 | ||
175 | /* | 85 | static void ima_check_last_writer(struct ima_iint_cache *iint, |
176 | * Decrement ima counts | 86 | struct inode *inode, |
177 | */ | 87 | struct file *file) |
178 | static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode, | ||
179 | struct file *file) | ||
180 | { | 88 | { |
181 | mode_t mode = file->f_mode; | 89 | mode_t mode = file->f_mode; |
182 | BUG_ON(!mutex_is_locked(&iint->mutex)); | ||
183 | 90 | ||
184 | iint->opencount--; | 91 | mutex_lock(&iint->mutex); |
185 | if ((mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) | 92 | if (mode & FMODE_WRITE && |
186 | iint->readcount--; | 93 | atomic_read(&inode->i_writecount) == 1 && |
187 | if (mode & FMODE_WRITE) { | 94 | iint->version != inode->i_version) |
188 | iint->writecount--; | 95 | iint->flags &= ~IMA_MEASURED; |
189 | if (iint->writecount == 0) { | 96 | mutex_unlock(&iint->mutex); |
190 | if (iint->version != inode->i_version) | ||
191 | iint->flags &= ~IMA_MEASURED; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (((iint->opencount < 0) || | ||
196 | (iint->readcount < 0) || | ||
197 | (iint->writecount < 0)) && | ||
198 | !ima_limit_imbalance(file)) { | ||
199 | printk(KERN_INFO "%s: open/free imbalance (r:%ld w:%ld o:%ld)\n", | ||
200 | __func__, iint->readcount, iint->writecount, | ||
201 | iint->opencount); | ||
202 | dump_stack(); | ||
203 | } | ||
204 | } | 97 | } |
205 | 98 | ||
206 | /** | 99 | /** |
207 | * ima_file_free - called on __fput() | 100 | * ima_file_free - called on __fput() |
208 | * @file: pointer to file structure being freed | 101 | * @file: pointer to file structure being freed |
209 | * | 102 | * |
210 | * Flag files that changed, based on i_version; | 103 | * Flag files that changed, based on i_version |
211 | * and decrement the iint readcount/writecount. | ||
212 | */ | 104 | */ |
213 | void ima_file_free(struct file *file) | 105 | void ima_file_free(struct file *file) |
214 | { | 106 | { |
@@ -217,14 +109,12 @@ void ima_file_free(struct file *file) | |||
217 | 109 | ||
218 | if (!iint_initialized || !S_ISREG(inode->i_mode)) | 110 | if (!iint_initialized || !S_ISREG(inode->i_mode)) |
219 | return; | 111 | return; |
220 | iint = ima_iint_find_get(inode); | 112 | |
113 | iint = ima_iint_find(inode); | ||
221 | if (!iint) | 114 | if (!iint) |
222 | return; | 115 | return; |
223 | 116 | ||
224 | mutex_lock(&iint->mutex); | 117 | ima_check_last_writer(iint, inode, file); |
225 | ima_dec_counts(iint, inode, file); | ||
226 | mutex_unlock(&iint->mutex); | ||
227 | kref_put(&iint->refcount, iint_free); | ||
228 | } | 118 | } |
229 | 119 | ||
230 | static int process_measurement(struct file *file, const unsigned char *filename, | 120 | static int process_measurement(struct file *file, const unsigned char *filename, |
@@ -236,12 +126,22 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
236 | 126 | ||
237 | if (!ima_initialized || !S_ISREG(inode->i_mode)) | 127 | if (!ima_initialized || !S_ISREG(inode->i_mode)) |
238 | return 0; | 128 | return 0; |
239 | iint = ima_iint_find_get(inode); | 129 | |
240 | if (!iint) | 130 | rc = ima_must_measure(inode, mask, function); |
241 | return -ENOMEM; | 131 | if (rc != 0) |
132 | return rc; | ||
133 | retry: | ||
134 | iint = ima_iint_find(inode); | ||
135 | if (!iint) { | ||
136 | rc = ima_inode_alloc(inode); | ||
137 | if (!rc || rc == -EEXIST) | ||
138 | goto retry; | ||
139 | return rc; | ||
140 | } | ||
242 | 141 | ||
243 | mutex_lock(&iint->mutex); | 142 | mutex_lock(&iint->mutex); |
244 | rc = ima_must_measure(iint, inode, mask, function); | 143 | |
144 | rc = iint->flags & IMA_MEASURED ? 1 : 0; | ||
245 | if (rc != 0) | 145 | if (rc != 0) |
246 | goto out; | 146 | goto out; |
247 | 147 | ||
@@ -250,7 +150,6 @@ static int process_measurement(struct file *file, const unsigned char *filename, | |||
250 | ima_store_measurement(iint, file, filename); | 150 | ima_store_measurement(iint, file, filename); |
251 | out: | 151 | out: |
252 | mutex_unlock(&iint->mutex); | 152 | mutex_unlock(&iint->mutex); |
253 | kref_put(&iint->refcount, iint_free); | ||
254 | return rc; | 153 | return rc; |
255 | } | 154 | } |
256 | 155 | ||
@@ -313,6 +212,7 @@ int ima_file_check(struct file *file, int mask) | |||
313 | { | 212 | { |
314 | int rc; | 213 | int rc; |
315 | 214 | ||
215 | ima_rdwr_violation_check(file); | ||
316 | rc = process_measurement(file, file->f_dentry->d_name.name, | 216 | rc = process_measurement(file, file->f_dentry->d_name.name, |
317 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), | 217 | mask & (MAY_READ | MAY_WRITE | MAY_EXEC), |
318 | FILE_CHECK); | 218 | FILE_CHECK); |
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index aef8c0a923ab..d661afbe474c 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c | |||
@@ -253,6 +253,8 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, | |||
253 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, | 253 | result = security_filter_rule_init(entry->lsm[lsm_rule].type, |
254 | Audit_equal, args, | 254 | Audit_equal, args, |
255 | &entry->lsm[lsm_rule].rule); | 255 | &entry->lsm[lsm_rule].rule); |
256 | if (!entry->lsm[lsm_rule].rule) | ||
257 | return -EINVAL; | ||
256 | return result; | 258 | return result; |
257 | } | 259 | } |
258 | 260 | ||