Blame view

security/integrity/iint.c 5.63 KB
f381c2722   Mimi Zohar   integrity: move i...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * Copyright (C) 2008 IBM Corporation
   *
   * Authors:
   * Mimi Zohar <zohar@us.ibm.com>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation, version 2 of the
   * License.
   *
   * File: integrity_iint.c
   *	- implements the integrity hooks: integrity_inode_alloc,
   *	  integrity_inode_free
   *	- cache integrity information associated with an inode
   *	  using a rbtree tree.
   */
  #include <linux/slab.h>
  #include <linux/module.h>
  #include <linux/spinlock.h>
  #include <linux/rbtree.h>
e3c4abbfa   Dmitry Kasatkin   integrity: define...
22
23
  #include <linux/file.h>
  #include <linux/uaccess.h>
f381c2722   Mimi Zohar   integrity: move i...
24
25
26
  #include "integrity.h"
  
  static struct rb_root integrity_iint_tree = RB_ROOT;
a10bf26b2   Dmitry Kasatkin   ima: replace iint...
27
  static DEFINE_RWLOCK(integrity_iint_lock);
f381c2722   Mimi Zohar   integrity: move i...
28
  static struct kmem_cache *iint_cache __read_mostly;
f381c2722   Mimi Zohar   integrity: move i...
29
30
31
32
33
34
35
  /*
   * __integrity_iint_find - return the iint associated with an inode
   */
  static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
  {
  	struct integrity_iint_cache *iint;
  	struct rb_node *n = integrity_iint_tree.rb_node;
f381c2722   Mimi Zohar   integrity: move i...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
  	while (n) {
  		iint = rb_entry(n, struct integrity_iint_cache, rb_node);
  
  		if (inode < iint->inode)
  			n = n->rb_left;
  		else if (inode > iint->inode)
  			n = n->rb_right;
  		else
  			break;
  	}
  	if (!n)
  		return NULL;
  
  	return iint;
  }
  
  /*
   * integrity_iint_find - return the iint associated with an inode
   */
  struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
  {
  	struct integrity_iint_cache *iint;
  
  	if (!IS_IMA(inode))
  		return NULL;
a10bf26b2   Dmitry Kasatkin   ima: replace iint...
61
  	read_lock(&integrity_iint_lock);
f381c2722   Mimi Zohar   integrity: move i...
62
  	iint = __integrity_iint_find(inode);
a10bf26b2   Dmitry Kasatkin   ima: replace iint...
63
  	read_unlock(&integrity_iint_lock);
f381c2722   Mimi Zohar   integrity: move i...
64
65
66
67
68
69
  
  	return iint;
  }
  
  static void iint_free(struct integrity_iint_cache *iint)
  {
a35c3fb64   Dmitry Kasatkin   ima: use dynamica...
70
71
  	kfree(iint->ima_hash);
  	iint->ima_hash = NULL;
f381c2722   Mimi Zohar   integrity: move i...
72
73
  	iint->version = 0;
  	iint->flags = 0UL;
d79d72e02   Mimi Zohar   ima: per hook cac...
74
75
76
77
  	iint->ima_file_status = INTEGRITY_UNKNOWN;
  	iint->ima_mmap_status = INTEGRITY_UNKNOWN;
  	iint->ima_bprm_status = INTEGRITY_UNKNOWN;
  	iint->ima_module_status = INTEGRITY_UNKNOWN;
fb788d8b9   Dmitry Kasatkin   evm: clean verifi...
78
  	iint->evm_status = INTEGRITY_UNKNOWN;
f381c2722   Mimi Zohar   integrity: move i...
79
80
81
82
  	kmem_cache_free(iint_cache, iint);
  }
  
  /**
bf2276d10   Dmitry Kasatkin   ima: allocating i...
83
   * integrity_inode_get - find or allocate an iint associated with an inode
f381c2722   Mimi Zohar   integrity: move i...
84
   * @inode: pointer to the inode
bf2276d10   Dmitry Kasatkin   ima: allocating i...
85
86
87
   * @return: allocated iint
   *
   * Caller must lock i_mutex
f381c2722   Mimi Zohar   integrity: move i...
88
   */
bf2276d10   Dmitry Kasatkin   ima: allocating i...
89
  struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
f381c2722   Mimi Zohar   integrity: move i...
90
91
  {
  	struct rb_node **p;
bf2276d10   Dmitry Kasatkin   ima: allocating i...
92
93
  	struct rb_node *node, *parent = NULL;
  	struct integrity_iint_cache *iint, *test_iint;
f381c2722   Mimi Zohar   integrity: move i...
94

bf2276d10   Dmitry Kasatkin   ima: allocating i...
95
96
97
  	iint = integrity_iint_find(inode);
  	if (iint)
  		return iint;
f381c2722   Mimi Zohar   integrity: move i...
98

bf2276d10   Dmitry Kasatkin   ima: allocating i...
99
100
101
  	iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
  	if (!iint)
  		return NULL;
f381c2722   Mimi Zohar   integrity: move i...
102

a10bf26b2   Dmitry Kasatkin   ima: replace iint...
103
  	write_lock(&integrity_iint_lock);
f381c2722   Mimi Zohar   integrity: move i...
104
105
106
107
108
109
  
  	p = &integrity_iint_tree.rb_node;
  	while (*p) {
  		parent = *p;
  		test_iint = rb_entry(parent, struct integrity_iint_cache,
  				     rb_node);
f381c2722   Mimi Zohar   integrity: move i...
110
111
  		if (inode < test_iint->inode)
  			p = &(*p)->rb_left;
f381c2722   Mimi Zohar   integrity: move i...
112
  		else
bf2276d10   Dmitry Kasatkin   ima: allocating i...
113
  			p = &(*p)->rb_right;
f381c2722   Mimi Zohar   integrity: move i...
114
  	}
bf2276d10   Dmitry Kasatkin   ima: allocating i...
115
116
  	iint->inode = inode;
  	node = &iint->rb_node;
f381c2722   Mimi Zohar   integrity: move i...
117
  	inode->i_flags |= S_IMA;
bf2276d10   Dmitry Kasatkin   ima: allocating i...
118
119
  	rb_link_node(node, parent, p);
  	rb_insert_color(node, &integrity_iint_tree);
f381c2722   Mimi Zohar   integrity: move i...
120

a10bf26b2   Dmitry Kasatkin   ima: replace iint...
121
  	write_unlock(&integrity_iint_lock);
bf2276d10   Dmitry Kasatkin   ima: allocating i...
122
  	return iint;
f381c2722   Mimi Zohar   integrity: move i...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  }
  
  /**
   * integrity_inode_free - called on security_inode_free
   * @inode: pointer to the inode
   *
   * Free the integrity information(iint) associated with an inode.
   */
  void integrity_inode_free(struct inode *inode)
  {
  	struct integrity_iint_cache *iint;
  
  	if (!IS_IMA(inode))
  		return;
a10bf26b2   Dmitry Kasatkin   ima: replace iint...
137
  	write_lock(&integrity_iint_lock);
f381c2722   Mimi Zohar   integrity: move i...
138
139
  	iint = __integrity_iint_find(inode);
  	rb_erase(&iint->rb_node, &integrity_iint_tree);
a10bf26b2   Dmitry Kasatkin   ima: replace iint...
140
  	write_unlock(&integrity_iint_lock);
f381c2722   Mimi Zohar   integrity: move i...
141
142
143
144
145
146
147
  
  	iint_free(iint);
  }
  
  static void init_once(void *foo)
  {
  	struct integrity_iint_cache *iint = foo;
2bb930abc   Dmitry Kasatkin   integrity: fix ch...
148
  	memset(iint, 0, sizeof(*iint));
f381c2722   Mimi Zohar   integrity: move i...
149
150
  	iint->version = 0;
  	iint->flags = 0UL;
d79d72e02   Mimi Zohar   ima: per hook cac...
151
152
153
154
  	iint->ima_file_status = INTEGRITY_UNKNOWN;
  	iint->ima_mmap_status = INTEGRITY_UNKNOWN;
  	iint->ima_bprm_status = INTEGRITY_UNKNOWN;
  	iint->ima_module_status = INTEGRITY_UNKNOWN;
24e0198ef   Dmitry Kasatkin   evm: replace hmac...
155
  	iint->evm_status = INTEGRITY_UNKNOWN;
f381c2722   Mimi Zohar   integrity: move i...
156
157
158
159
160
161
162
  }
  
  static int __init integrity_iintcache_init(void)
  {
  	iint_cache =
  	    kmem_cache_create("iint_cache", sizeof(struct integrity_iint_cache),
  			      0, SLAB_PANIC, init_once);
f381c2722   Mimi Zohar   integrity: move i...
163
164
165
  	return 0;
  }
  security_initcall(integrity_iintcache_init);
e3c4abbfa   Dmitry Kasatkin   integrity: define...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
  
  
  /*
   * integrity_kernel_read - read data from the file
   *
   * This is a function for reading file content instead of kernel_read().
   * It does not perform locking checks to ensure it cannot be blocked.
   * It does not perform security checks because it is irrelevant for IMA.
   *
   */
  int integrity_kernel_read(struct file *file, loff_t offset,
  			  char *addr, unsigned long count)
  {
  	mm_segment_t old_fs;
  	char __user *buf = (char __user *)addr;
6fb5032eb   Dmitry Kasatkin   VFS: refactor vfs...
181
  	ssize_t ret;
e3c4abbfa   Dmitry Kasatkin   integrity: define...
182
183
184
185
186
187
  
  	if (!(file->f_mode & FMODE_READ))
  		return -EBADF;
  
  	old_fs = get_fs();
  	set_fs(get_ds());
6fb5032eb   Dmitry Kasatkin   VFS: refactor vfs...
188
  	ret = __vfs_read(file, buf, count, &offset);
e3c4abbfa   Dmitry Kasatkin   integrity: define...
189
  	set_fs(old_fs);
6fb5032eb   Dmitry Kasatkin   VFS: refactor vfs...
190

e3c4abbfa   Dmitry Kasatkin   integrity: define...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	return ret;
  }
  
  /*
   * integrity_read_file - read entire file content into the buffer
   *
   * This is function opens a file, allocates the buffer of required
   * size, read entire file content to the buffer and closes the file
   *
   * It is used only by init code.
   *
   */
  int __init integrity_read_file(const char *path, char **data)
  {
  	struct file *file;
  	loff_t size;
  	char *buf;
  	int rc = -EINVAL;
  
  	file = filp_open(path, O_RDONLY, 0);
  	if (IS_ERR(file)) {
  		rc = PTR_ERR(file);
  		pr_err("Unable to open file: %s (%d)", path, rc);
  		return rc;
  	}
  
  	size = i_size_read(file_inode(file));
  	if (size <= 0)
  		goto out;
  
  	buf = kmalloc(size, GFP_KERNEL);
  	if (!buf) {
  		rc = -ENOMEM;
  		goto out;
  	}
  
  	rc = integrity_kernel_read(file, 0, buf, size);
  	if (rc < 0)
  		kfree(buf);
  	else if (rc != size)
  		rc = -EIO;
  	else
  		*data = buf;
  out:
  	fput(file);
  	return rc;
  }
c9cd2ce2b   Dmitry Kasatkin   integrity: provid...
238
239
240
241
242
243
244
245
246
247
248
  
  /*
   * integrity_load_keys - load integrity keys hook
   *
   * Hooks is called from init/main.c:kernel_init_freeable()
   * when rootfs is ready
   */
  void __init integrity_load_keys(void)
  {
  	ima_load_x509();
  }