diff options
Diffstat (limited to 'security/integrity/ima/ima_iint.c')
-rw-r--r-- | security/integrity/ima/ima_iint.c | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c new file mode 100644 index 000000000000..750db3c993a7 --- /dev/null +++ b/security/integrity/ima/ima_iint.c | |||
@@ -0,0 +1,185 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Mimi Zohar <zohar@us.ibm.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation, version 2 of the | ||
10 | * License. | ||
11 | * | ||
12 | * File: ima_iint.c | ||
13 | * - implements the IMA hooks: ima_inode_alloc, ima_inode_free | ||
14 | * - cache integrity information associated with an inode | ||
15 | * using a radix tree. | ||
16 | */ | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/radix-tree.h> | ||
20 | #include "ima.h" | ||
21 | |||
22 | #define ima_iint_delete ima_inode_free | ||
23 | |||
24 | RADIX_TREE(ima_iint_store, GFP_ATOMIC); | ||
25 | DEFINE_SPINLOCK(ima_iint_lock); | ||
26 | |||
27 | static struct kmem_cache *iint_cache __read_mostly; | ||
28 | |||
29 | /* ima_iint_find_get - return the iint associated with an inode | ||
30 | * | ||
31 | * ima_iint_find_get gets a reference to the iint. Caller must | ||
32 | * remember to put the iint reference. | ||
33 | */ | ||
34 | struct ima_iint_cache *ima_iint_find_get(struct inode *inode) | ||
35 | { | ||
36 | struct ima_iint_cache *iint; | ||
37 | |||
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; | ||
46 | } | ||
47 | |||
48 | /* Allocate memory for the iint associated with the inode | ||
49 | * from the iint_cache slab, initialize the iint, and | ||
50 | * insert it into the radix tree. | ||
51 | * | ||
52 | * On success return a pointer to the iint; on failure return NULL. | ||
53 | */ | ||
54 | struct ima_iint_cache *ima_iint_insert(struct inode *inode) | ||
55 | { | ||
56 | struct ima_iint_cache *iint = NULL; | ||
57 | int rc = 0; | ||
58 | |||
59 | if (!ima_initialized) | ||
60 | return iint; | ||
61 | iint = kmem_cache_alloc(iint_cache, GFP_KERNEL); | ||
62 | if (!iint) | ||
63 | return iint; | ||
64 | |||
65 | rc = radix_tree_preload(GFP_KERNEL); | ||
66 | if (rc < 0) | ||
67 | goto out; | ||
68 | |||
69 | spin_lock(&ima_iint_lock); | ||
70 | rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint); | ||
71 | spin_unlock(&ima_iint_lock); | ||
72 | out: | ||
73 | if (rc < 0) { | ||
74 | kmem_cache_free(iint_cache, iint); | ||
75 | if (rc == -EEXIST) { | ||
76 | iint = radix_tree_lookup(&ima_iint_store, | ||
77 | (unsigned long)inode); | ||
78 | } else | ||
79 | iint = NULL; | ||
80 | } | ||
81 | radix_tree_preload_end(); | ||
82 | return iint; | ||
83 | } | ||
84 | |||
85 | /** | ||
86 | * ima_inode_alloc - allocate an iint associated with an inode | ||
87 | * @inode: pointer to the inode | ||
88 | * | ||
89 | * Return 0 on success, 1 on failure. | ||
90 | */ | ||
91 | int ima_inode_alloc(struct inode *inode) | ||
92 | { | ||
93 | struct ima_iint_cache *iint; | ||
94 | |||
95 | if (!ima_initialized) | ||
96 | return 0; | ||
97 | |||
98 | iint = ima_iint_insert(inode); | ||
99 | if (!iint) | ||
100 | return 1; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | /* ima_iint_find_insert_get - get the iint associated with an inode | ||
105 | * | ||
106 | * Most insertions are done at inode_alloc, except those allocated | ||
107 | * before late_initcall. When the iint does not exist, allocate it, | ||
108 | * initialize and insert it, and increment the iint refcount. | ||
109 | * | ||
110 | * (Can't initialize at security_initcall before any inodes are | ||
111 | * allocated, got to wait at least until proc_init.) | ||
112 | * | ||
113 | * Return the iint. | ||
114 | */ | ||
115 | struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode) | ||
116 | { | ||
117 | struct ima_iint_cache *iint = NULL; | ||
118 | |||
119 | iint = ima_iint_find_get(inode); | ||
120 | if (iint) | ||
121 | return iint; | ||
122 | |||
123 | iint = ima_iint_insert(inode); | ||
124 | if (iint) | ||
125 | kref_get(&iint->refcount); | ||
126 | |||
127 | return iint; | ||
128 | } | ||
129 | |||
130 | /* iint_free - called when the iint refcount goes to zero */ | ||
131 | void iint_free(struct kref *kref) | ||
132 | { | ||
133 | struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache, | ||
134 | refcount); | ||
135 | iint->version = 0; | ||
136 | iint->flags = 0UL; | ||
137 | kref_set(&iint->refcount, 1); | ||
138 | kmem_cache_free(iint_cache, iint); | ||
139 | } | ||
140 | |||
141 | void iint_rcu_free(struct rcu_head *rcu_head) | ||
142 | { | ||
143 | struct ima_iint_cache *iint = container_of(rcu_head, | ||
144 | struct ima_iint_cache, rcu); | ||
145 | kref_put(&iint->refcount, iint_free); | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * ima_iint_delete - called on integrity_inode_free | ||
150 | * @inode: pointer to the inode | ||
151 | * | ||
152 | * Free the integrity information(iint) associated with an inode. | ||
153 | */ | ||
154 | void ima_iint_delete(struct inode *inode) | ||
155 | { | ||
156 | struct ima_iint_cache *iint; | ||
157 | |||
158 | if (!ima_initialized) | ||
159 | return; | ||
160 | spin_lock(&ima_iint_lock); | ||
161 | iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); | ||
162 | spin_unlock(&ima_iint_lock); | ||
163 | if (iint) | ||
164 | call_rcu(&iint->rcu, iint_rcu_free); | ||
165 | } | ||
166 | |||
167 | static void init_once(void *foo) | ||
168 | { | ||
169 | struct ima_iint_cache *iint = foo; | ||
170 | |||
171 | memset(iint, 0, sizeof *iint); | ||
172 | iint->version = 0; | ||
173 | iint->flags = 0UL; | ||
174 | mutex_init(&iint->mutex); | ||
175 | iint->readcount = 0; | ||
176 | iint->writecount = 0; | ||
177 | kref_set(&iint->refcount, 1); | ||
178 | } | ||
179 | |||
180 | void ima_iintcache_init(void) | ||
181 | { | ||
182 | iint_cache = | ||
183 | kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, | ||
184 | SLAB_PANIC, init_once); | ||
185 | } | ||