]>
Commit | Line | Data |
---|---|---|
f381c272 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: integrity_iint.c | |
13 | * - implements the integrity hooks: integrity_inode_alloc, | |
14 | * integrity_inode_free | |
15 | * - cache integrity information associated with an inode | |
16 | * using a rbtree tree. | |
17 | */ | |
18 | #include <linux/slab.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/spinlock.h> | |
21 | #include <linux/rbtree.h> | |
e3c4abbf DK |
22 | #include <linux/file.h> |
23 | #include <linux/uaccess.h> | |
0c343af8 | 24 | #include <linux/security.h> |
f381c272 MZ |
25 | #include "integrity.h" |
26 | ||
27 | static struct rb_root integrity_iint_tree = RB_ROOT; | |
a10bf26b | 28 | static DEFINE_RWLOCK(integrity_iint_lock); |
f381c272 MZ |
29 | static struct kmem_cache *iint_cache __read_mostly; |
30 | ||
0c343af8 MG |
31 | struct dentry *integrity_dir; |
32 | ||
f381c272 MZ |
33 | /* |
34 | * __integrity_iint_find - return the iint associated with an inode | |
35 | */ | |
36 | static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) | |
37 | { | |
38 | struct integrity_iint_cache *iint; | |
39 | struct rb_node *n = integrity_iint_tree.rb_node; | |
40 | ||
f381c272 MZ |
41 | while (n) { |
42 | iint = rb_entry(n, struct integrity_iint_cache, rb_node); | |
43 | ||
44 | if (inode < iint->inode) | |
45 | n = n->rb_left; | |
46 | else if (inode > iint->inode) | |
47 | n = n->rb_right; | |
48 | else | |
49 | break; | |
50 | } | |
51 | if (!n) | |
52 | return NULL; | |
53 | ||
54 | return iint; | |
55 | } | |
56 | ||
57 | /* | |
58 | * integrity_iint_find - return the iint associated with an inode | |
59 | */ | |
60 | struct integrity_iint_cache *integrity_iint_find(struct inode *inode) | |
61 | { | |
62 | struct integrity_iint_cache *iint; | |
63 | ||
64 | if (!IS_IMA(inode)) | |
65 | return NULL; | |
66 | ||
a10bf26b | 67 | read_lock(&integrity_iint_lock); |
f381c272 | 68 | iint = __integrity_iint_find(inode); |
a10bf26b | 69 | read_unlock(&integrity_iint_lock); |
f381c272 MZ |
70 | |
71 | return iint; | |
72 | } | |
73 | ||
74 | static void iint_free(struct integrity_iint_cache *iint) | |
75 | { | |
a35c3fb6 DK |
76 | kfree(iint->ima_hash); |
77 | iint->ima_hash = NULL; | |
f381c272 MZ |
78 | iint->version = 0; |
79 | iint->flags = 0UL; | |
e2598077 | 80 | iint->atomic_flags = 0UL; |
d79d72e0 MZ |
81 | iint->ima_file_status = INTEGRITY_UNKNOWN; |
82 | iint->ima_mmap_status = INTEGRITY_UNKNOWN; | |
83 | iint->ima_bprm_status = INTEGRITY_UNKNOWN; | |
c6af8efe | 84 | iint->ima_read_status = INTEGRITY_UNKNOWN; |
d906c10d | 85 | iint->ima_creds_status = INTEGRITY_UNKNOWN; |
fb788d8b | 86 | iint->evm_status = INTEGRITY_UNKNOWN; |
96d450bb | 87 | iint->measured_pcrs = 0; |
f381c272 MZ |
88 | kmem_cache_free(iint_cache, iint); |
89 | } | |
90 | ||
91 | /** | |
bf2276d1 | 92 | * integrity_inode_get - find or allocate an iint associated with an inode |
f381c272 | 93 | * @inode: pointer to the inode |
bf2276d1 DK |
94 | * @return: allocated iint |
95 | * | |
96 | * Caller must lock i_mutex | |
f381c272 | 97 | */ |
bf2276d1 | 98 | struct integrity_iint_cache *integrity_inode_get(struct inode *inode) |
f381c272 MZ |
99 | { |
100 | struct rb_node **p; | |
bf2276d1 DK |
101 | struct rb_node *node, *parent = NULL; |
102 | struct integrity_iint_cache *iint, *test_iint; | |
f381c272 | 103 | |
bf2276d1 DK |
104 | iint = integrity_iint_find(inode); |
105 | if (iint) | |
106 | return iint; | |
f381c272 | 107 | |
bf2276d1 DK |
108 | iint = kmem_cache_alloc(iint_cache, GFP_NOFS); |
109 | if (!iint) | |
110 | return NULL; | |
f381c272 | 111 | |
a10bf26b | 112 | write_lock(&integrity_iint_lock); |
f381c272 MZ |
113 | |
114 | p = &integrity_iint_tree.rb_node; | |
115 | while (*p) { | |
116 | parent = *p; | |
117 | test_iint = rb_entry(parent, struct integrity_iint_cache, | |
118 | rb_node); | |
f381c272 MZ |
119 | if (inode < test_iint->inode) |
120 | p = &(*p)->rb_left; | |
f381c272 | 121 | else |
bf2276d1 | 122 | p = &(*p)->rb_right; |
f381c272 MZ |
123 | } |
124 | ||
bf2276d1 DK |
125 | iint->inode = inode; |
126 | node = &iint->rb_node; | |
f381c272 | 127 | inode->i_flags |= S_IMA; |
bf2276d1 DK |
128 | rb_link_node(node, parent, p); |
129 | rb_insert_color(node, &integrity_iint_tree); | |
f381c272 | 130 | |
a10bf26b | 131 | write_unlock(&integrity_iint_lock); |
bf2276d1 | 132 | return iint; |
f381c272 MZ |
133 | } |
134 | ||
135 | /** | |
136 | * integrity_inode_free - called on security_inode_free | |
137 | * @inode: pointer to the inode | |
138 | * | |
139 | * Free the integrity information(iint) associated with an inode. | |
140 | */ | |
141 | void integrity_inode_free(struct inode *inode) | |
142 | { | |
143 | struct integrity_iint_cache *iint; | |
144 | ||
145 | if (!IS_IMA(inode)) | |
146 | return; | |
147 | ||
a10bf26b | 148 | write_lock(&integrity_iint_lock); |
f381c272 MZ |
149 | iint = __integrity_iint_find(inode); |
150 | rb_erase(&iint->rb_node, &integrity_iint_tree); | |
a10bf26b | 151 | write_unlock(&integrity_iint_lock); |
f381c272 MZ |
152 | |
153 | iint_free(iint); | |
154 | } | |
155 | ||
156 | static void init_once(void *foo) | |
157 | { | |
158 | struct integrity_iint_cache *iint = foo; | |
159 | ||
2bb930ab | 160 | memset(iint, 0, sizeof(*iint)); |
d79d72e0 MZ |
161 | iint->ima_file_status = INTEGRITY_UNKNOWN; |
162 | iint->ima_mmap_status = INTEGRITY_UNKNOWN; | |
163 | iint->ima_bprm_status = INTEGRITY_UNKNOWN; | |
c6af8efe | 164 | iint->ima_read_status = INTEGRITY_UNKNOWN; |
d906c10d | 165 | iint->ima_creds_status = INTEGRITY_UNKNOWN; |
24e0198e | 166 | iint->evm_status = INTEGRITY_UNKNOWN; |
0d73a552 | 167 | mutex_init(&iint->mutex); |
f381c272 MZ |
168 | } |
169 | ||
170 | static int __init integrity_iintcache_init(void) | |
171 | { | |
172 | iint_cache = | |
173 | kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache), | |
174 | 0, SLAB_PANIC, init_once); | |
f381c272 MZ |
175 | return 0; |
176 | } | |
177 | security_initcall(integrity_iintcache_init); | |
e3c4abbf DK |
178 | |
179 | ||
180 | /* | |
181 | * integrity_kernel_read - read data from the file | |
182 | * | |
183 | * This is a function for reading file content instead of kernel_read(). | |
184 | * It does not perform locking checks to ensure it cannot be blocked. | |
185 | * It does not perform security checks because it is irrelevant for IMA. | |
186 | * | |
187 | */ | |
188 | int integrity_kernel_read(struct file *file, loff_t offset, | |
bb543e39 | 189 | void *addr, unsigned long count) |
e3c4abbf DK |
190 | { |
191 | mm_segment_t old_fs; | |
192 | char __user *buf = (char __user *)addr; | |
6fb5032e | 193 | ssize_t ret; |
e3c4abbf DK |
194 | |
195 | if (!(file->f_mode & FMODE_READ)) | |
196 | return -EBADF; | |
197 | ||
198 | old_fs = get_fs(); | |
199 | set_fs(get_ds()); | |
6fb5032e | 200 | ret = __vfs_read(file, buf, count, &offset); |
e3c4abbf | 201 | set_fs(old_fs); |
6fb5032e | 202 | |
e3c4abbf DK |
203 | return ret; |
204 | } | |
205 | ||
c9cd2ce2 DK |
206 | /* |
207 | * integrity_load_keys - load integrity keys hook | |
208 | * | |
209 | * Hooks is called from init/main.c:kernel_init_freeable() | |
210 | * when rootfs is ready | |
211 | */ | |
212 | void __init integrity_load_keys(void) | |
213 | { | |
214 | ima_load_x509(); | |
2ce523eb | 215 | evm_load_x509(); |
c9cd2ce2 | 216 | } |
0c343af8 MG |
217 | |
218 | static int __init integrity_fs_init(void) | |
219 | { | |
220 | integrity_dir = securityfs_create_dir("integrity", NULL); | |
221 | if (IS_ERR(integrity_dir)) { | |
222 | pr_err("Unable to create integrity sysfs dir: %ld\n", | |
223 | PTR_ERR(integrity_dir)); | |
224 | integrity_dir = NULL; | |
225 | return PTR_ERR(integrity_dir); | |
226 | } | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | late_initcall(integrity_fs_init) |