Commit | Line | Data |
---|---|---|
3323eec9 MZ |
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) { | |
be38e0fd | 76 | spin_lock(&ima_iint_lock); |
3323eec9 MZ |
77 | iint = radix_tree_lookup(&ima_iint_store, |
78 | (unsigned long)inode); | |
be38e0fd | 79 | spin_unlock(&ima_iint_lock); |
3323eec9 MZ |
80 | } else |
81 | iint = NULL; | |
82 | } | |
83 | radix_tree_preload_end(); | |
84 | return iint; | |
85 | } | |
86 | ||
87 | /** | |
88 | * ima_inode_alloc - allocate an iint associated with an inode | |
89 | * @inode: pointer to the inode | |
90 | * | |
91 | * Return 0 on success, 1 on failure. | |
92 | */ | |
93 | int ima_inode_alloc(struct inode *inode) | |
94 | { | |
95 | struct ima_iint_cache *iint; | |
96 | ||
97 | if (!ima_initialized) | |
98 | return 0; | |
99 | ||
100 | iint = ima_iint_insert(inode); | |
101 | if (!iint) | |
102 | return 1; | |
103 | return 0; | |
104 | } | |
105 | ||
106 | /* ima_iint_find_insert_get - get the iint associated with an inode | |
107 | * | |
108 | * Most insertions are done at inode_alloc, except those allocated | |
109 | * before late_initcall. When the iint does not exist, allocate it, | |
110 | * initialize and insert it, and increment the iint refcount. | |
111 | * | |
112 | * (Can't initialize at security_initcall before any inodes are | |
113 | * allocated, got to wait at least until proc_init.) | |
114 | * | |
115 | * Return the iint. | |
116 | */ | |
117 | struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode) | |
118 | { | |
119 | struct ima_iint_cache *iint = NULL; | |
120 | ||
121 | iint = ima_iint_find_get(inode); | |
122 | if (iint) | |
123 | return iint; | |
124 | ||
125 | iint = ima_iint_insert(inode); | |
126 | if (iint) | |
127 | kref_get(&iint->refcount); | |
128 | ||
129 | return iint; | |
130 | } | |
1df9f0a7 | 131 | EXPORT_SYMBOL_GPL(ima_iint_find_insert_get); |
3323eec9 MZ |
132 | |
133 | /* iint_free - called when the iint refcount goes to zero */ | |
134 | void iint_free(struct kref *kref) | |
135 | { | |
136 | struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache, | |
137 | refcount); | |
138 | iint->version = 0; | |
139 | iint->flags = 0UL; | |
1df9f0a7 MZ |
140 | if (iint->readcount != 0) { |
141 | printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__, | |
142 | iint->readcount); | |
143 | iint->readcount = 0; | |
144 | } | |
145 | if (iint->writecount != 0) { | |
146 | printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__, | |
147 | iint->writecount); | |
148 | iint->writecount = 0; | |
149 | } | |
150 | if (iint->opencount != 0) { | |
151 | printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__, | |
152 | iint->opencount); | |
153 | iint->opencount = 0; | |
154 | } | |
3323eec9 MZ |
155 | kref_set(&iint->refcount, 1); |
156 | kmem_cache_free(iint_cache, iint); | |
157 | } | |
158 | ||
159 | void iint_rcu_free(struct rcu_head *rcu_head) | |
160 | { | |
161 | struct ima_iint_cache *iint = container_of(rcu_head, | |
162 | struct ima_iint_cache, rcu); | |
163 | kref_put(&iint->refcount, iint_free); | |
164 | } | |
165 | ||
166 | /** | |
167 | * ima_iint_delete - called on integrity_inode_free | |
168 | * @inode: pointer to the inode | |
169 | * | |
170 | * Free the integrity information(iint) associated with an inode. | |
171 | */ | |
172 | void ima_iint_delete(struct inode *inode) | |
173 | { | |
174 | struct ima_iint_cache *iint; | |
175 | ||
176 | if (!ima_initialized) | |
177 | return; | |
178 | spin_lock(&ima_iint_lock); | |
179 | iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode); | |
180 | spin_unlock(&ima_iint_lock); | |
181 | if (iint) | |
182 | call_rcu(&iint->rcu, iint_rcu_free); | |
183 | } | |
184 | ||
185 | static void init_once(void *foo) | |
186 | { | |
187 | struct ima_iint_cache *iint = foo; | |
188 | ||
189 | memset(iint, 0, sizeof *iint); | |
190 | iint->version = 0; | |
191 | iint->flags = 0UL; | |
192 | mutex_init(&iint->mutex); | |
193 | iint->readcount = 0; | |
194 | iint->writecount = 0; | |
1df9f0a7 | 195 | iint->opencount = 0; |
3323eec9 MZ |
196 | kref_set(&iint->refcount, 1); |
197 | } | |
198 | ||
199 | void ima_iintcache_init(void) | |
200 | { | |
201 | iint_cache = | |
202 | kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0, | |
203 | SLAB_PANIC, init_once); | |
204 | } |