1 |
/* |
2 |
* security/ccsecurity/proc_if.c |
3 |
* |
4 |
* Copyright (C) 2005-2010 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.8.0-pre 2010/09/01 |
7 |
* |
8 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
9 |
* See README.ccs for ChangeLog. |
10 |
* |
11 |
*/ |
12 |
|
13 |
#include <linux/types.h> |
14 |
#include <linux/kernel.h> |
15 |
#include <asm/io.h> |
16 |
#include <linux/proc_fs.h> |
17 |
#include <linux/module.h> |
18 |
#include <linux/version.h> |
19 |
#include "internal.h" |
20 |
|
21 |
static bool ccs_check_task_acl(struct ccs_request_info *r, |
22 |
const struct ccs_acl_info *ptr) |
23 |
{ |
24 |
const struct ccs_task_acl *acl = container_of(ptr, typeof(*acl), head); |
25 |
return !ccs_pathcmp(r->param.task.domainname, acl->domainname); |
26 |
} |
27 |
|
28 |
/** |
29 |
* ccs_write_self - write() for /proc/ccs/self_domain interface. |
30 |
* |
31 |
* @file: Pointer to "struct file". |
32 |
* @buf: Domainname to transit to. |
33 |
* @count: Size of @buf. |
34 |
* @ppos: Unused. |
35 |
* |
36 |
* Returns @count on success, negative value otherwise. |
37 |
*/ |
38 |
static ssize_t ccs_write_self(struct file *file, const char __user *buf, |
39 |
size_t count, loff_t *ppos) |
40 |
{ |
41 |
char *data; |
42 |
int error; |
43 |
if (!count || count >= CCS_EXEC_TMPSIZE - 10) |
44 |
return -ENOMEM; |
45 |
data = kzalloc(count + 1, CCS_GFP_FLAGS); |
46 |
if (!data) |
47 |
return -ENOMEM; |
48 |
if (copy_from_user(data, buf, count)) { |
49 |
error = -EFAULT; |
50 |
goto out; |
51 |
} |
52 |
ccs_normalize_line(data); |
53 |
if (ccs_correct_domain(data)) { |
54 |
const int idx = ccs_read_lock(); |
55 |
struct ccs_path_info name; |
56 |
struct ccs_request_info r; |
57 |
name.name = data; |
58 |
ccs_fill_path_info(&name); |
59 |
/* Check "task manual_transit" permission. */ |
60 |
ccs_init_request_info(&r, CCS_MAC_FILE_EXECUTE); |
61 |
r.param_type = CCS_TYPE_MANUAL_TASK_ACL; |
62 |
r.param.task.domainname = &name; |
63 |
ccs_check_acl(&r, ccs_check_task_acl); |
64 |
if (!r.granted) |
65 |
error = -EPERM; |
66 |
else |
67 |
error = ccs_assign_domain(data, r.profile, |
68 |
ccs_current_domain()->group, |
69 |
true) ? 0 : -ENOENT; |
70 |
ccs_read_unlock(idx); |
71 |
} else |
72 |
error = -EINVAL; |
73 |
out: |
74 |
kfree(data); |
75 |
return error ? error : count; |
76 |
} |
77 |
|
78 |
/** |
79 |
* ccs_read_self - read() for /proc/ccs/self_domain interface. |
80 |
* |
81 |
* @file: Pointer to "struct file". |
82 |
* @buf: Domainname which current thread belongs to. |
83 |
* @count: Size of @buf. |
84 |
* @ppos: Bytes read by now. |
85 |
* |
86 |
* Returns read size on success, negative value otherwise. |
87 |
*/ |
88 |
static int ccs_read_self(struct file *file, char __user *buf, size_t count, |
89 |
loff_t *ppos) |
90 |
{ |
91 |
const char *domain = ccs_current_domain()->domainname->name; |
92 |
const int len = strlen(domain) - *ppos; |
93 |
const int pos = *ppos; |
94 |
if (pos >= len) |
95 |
return 0; |
96 |
if (copy_to_user(buf, domain + pos, len - pos)) |
97 |
return -EFAULT; |
98 |
return len - pos; |
99 |
} |
100 |
|
101 |
/* Operations for /proc/ccs/self_domain interface. */ |
102 |
static |
103 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) |
104 |
const |
105 |
#endif |
106 |
struct file_operations ccs_self_operations = { |
107 |
.write = ccs_write_self, |
108 |
.read = ccs_read_self, |
109 |
}; |
110 |
|
111 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 4, 23) |
112 |
#if !defined(RHEL_VERSION) || RHEL_VERSION != 3 || !defined(RHEL_UPDATE) || RHEL_UPDATE != 9 |
113 |
/** |
114 |
* PDE - Get "struct proc_dir_entry". |
115 |
* |
116 |
* @inode: Pointer to "struct inode". |
117 |
* |
118 |
* Returns pointer to "struct proc_dir_entry" |
119 |
* |
120 |
* This is for compatibility with older kernels. |
121 |
*/ |
122 |
static inline struct proc_dir_entry *PDE(const struct inode *inode) |
123 |
{ |
124 |
return (struct proc_dir_entry *) inode->u.generic_ip; |
125 |
} |
126 |
#endif |
127 |
#endif |
128 |
|
129 |
/** |
130 |
* ccs_open - open() for /proc/ccs/ interface. |
131 |
* |
132 |
* @inode: Pointer to "struct inode". |
133 |
* @file: Pointer to "struct file". |
134 |
* |
135 |
* Returns 0 on success, negative value otherwise. |
136 |
*/ |
137 |
static int ccs_open(struct inode *inode, struct file *file) |
138 |
{ |
139 |
return ccs_open_control(((u8 *) PDE(inode)->data) - ((u8 *) NULL), |
140 |
file); |
141 |
} |
142 |
|
143 |
/** |
144 |
* ccs_release - close() for /proc/ccs/ interface. |
145 |
* |
146 |
* @inode: Pointer to "struct inode". |
147 |
* @file: Pointer to "struct file". |
148 |
* |
149 |
* Returns 0 on success, negative value otherwise. |
150 |
*/ |
151 |
static int ccs_release(struct inode *inode, struct file *file) |
152 |
{ |
153 |
return ccs_close_control(file); |
154 |
} |
155 |
|
156 |
/** |
157 |
* ccs_poll - poll() for /proc/ccs/ interface. |
158 |
* |
159 |
* @file: Pointer to "struct file". |
160 |
* @wait: Pointer to "poll_table". |
161 |
* |
162 |
* Returns 0 on success, negative value otherwise. |
163 |
*/ |
164 |
static unsigned int ccs_poll(struct file *file, poll_table *wait) |
165 |
{ |
166 |
return ccs_poll_control(file, wait); |
167 |
} |
168 |
|
169 |
/** |
170 |
* ccs_read - read() for /proc/ccs/ interface. |
171 |
* |
172 |
* @file: Pointer to "struct file". |
173 |
* @buf: Pointer to buffer. |
174 |
* @count: Size of @buf. |
175 |
* @ppos: Unused. |
176 |
* |
177 |
* Returns bytes read on success, negative value otherwise. |
178 |
*/ |
179 |
static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, |
180 |
loff_t *ppos) |
181 |
{ |
182 |
return ccs_read_control(file, buf, count); |
183 |
} |
184 |
|
185 |
/** |
186 |
* ccs_write - write() for /proc/ccs/ interface. |
187 |
* |
188 |
* @file: Pointer to "struct file". |
189 |
* @buf: Pointer to buffer. |
190 |
* @count: Size of @buf. |
191 |
* @ppos: Unused. |
192 |
* |
193 |
* Returns @count on success, negative value otherwise. |
194 |
*/ |
195 |
static ssize_t ccs_write(struct file *file, const char __user *buf, |
196 |
size_t count, loff_t *ppos) |
197 |
{ |
198 |
return ccs_write_control(file, buf, count); |
199 |
} |
200 |
|
201 |
/* Operations for /proc/ccs/ interface. */ |
202 |
static |
203 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17) |
204 |
const |
205 |
#endif |
206 |
struct file_operations ccs_operations = { |
207 |
.open = ccs_open, |
208 |
.release = ccs_release, |
209 |
.poll = ccs_poll, |
210 |
.read = ccs_read, |
211 |
.write = ccs_write, |
212 |
}; |
213 |
|
214 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) |
215 |
|
216 |
struct iattr; |
217 |
|
218 |
/** |
219 |
* proc_notify_change - Update inode's attributes and reflect to the dentry. |
220 |
* |
221 |
* @dentry: Pointer to "struct dentry". |
222 |
* @iattr: Pointer to "struct iattr". |
223 |
* |
224 |
* Returns 0 on success, negative value otherwise. |
225 |
* |
226 |
* The 2.4 kernels don't allow chmod()/chown() for files in /proc , |
227 |
* while the 2.6 kernels allow. |
228 |
* To permit management of /proc/ccs/ interface by non-root user, |
229 |
* I modified to allow chmod()/chown() of /proc/ccs/ interface like 2.6 kernels |
230 |
* by adding "struct inode_operations"->setattr hook. |
231 |
*/ |
232 |
static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) |
233 |
{ |
234 |
struct inode *inode = dentry->d_inode; |
235 |
struct proc_dir_entry *de = PDE(inode); |
236 |
int error; |
237 |
|
238 |
error = inode_change_ok(inode, iattr); |
239 |
if (error) |
240 |
goto out; |
241 |
|
242 |
error = inode_setattr(inode, iattr); |
243 |
if (error) |
244 |
goto out; |
245 |
|
246 |
de->uid = inode->i_uid; |
247 |
de->gid = inode->i_gid; |
248 |
de->mode = inode->i_mode; |
249 |
out: |
250 |
return error; |
251 |
} |
252 |
|
253 |
/* The inode operations for /proc/ccs/ directory. */ |
254 |
static struct inode_operations ccs_dir_inode_operations; |
255 |
|
256 |
/* The inode operations for files under /proc/ccs/ directory. */ |
257 |
static struct inode_operations ccs_file_inode_operations; |
258 |
#endif |
259 |
|
260 |
/** |
261 |
* ccs_create_entry - Create interface files under /proc/ccs/ directory. |
262 |
* |
263 |
* @name: The name of the interface file. |
264 |
* @mode: The permission of the interface file. |
265 |
* @parent: The parent directory. |
266 |
* @key: Type of interface. |
267 |
* |
268 |
* Returns nothing. |
269 |
*/ |
270 |
static void __init ccs_create_entry(const char *name, const mode_t mode, |
271 |
struct proc_dir_entry *parent, |
272 |
const u8 key) |
273 |
{ |
274 |
struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); |
275 |
if (entry) { |
276 |
entry->proc_fops = &ccs_operations; |
277 |
entry->data = ((u8 *) NULL) + key; |
278 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) |
279 |
if (entry->proc_iops) |
280 |
ccs_file_inode_operations = *entry->proc_iops; |
281 |
if (!ccs_file_inode_operations.setattr) |
282 |
ccs_file_inode_operations.setattr = proc_notify_change; |
283 |
entry->proc_iops = &ccs_file_inode_operations; |
284 |
#endif |
285 |
} |
286 |
} |
287 |
|
288 |
/** |
289 |
* ccs_proc_init - Initialize /proc/ccs/ interface. |
290 |
* |
291 |
* Returns 0. |
292 |
*/ |
293 |
static void __init ccs_proc_init(void) |
294 |
{ |
295 |
struct proc_dir_entry *ccs_dir = proc_mkdir("ccs", NULL); |
296 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) |
297 |
if (ccs_dir->proc_iops) |
298 |
ccs_dir_inode_operations = *ccs_dir->proc_iops; |
299 |
if (!ccs_dir_inode_operations.setattr) |
300 |
ccs_dir_inode_operations.setattr = proc_notify_change; |
301 |
ccs_dir->proc_iops = &ccs_dir_inode_operations; |
302 |
#endif |
303 |
ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); |
304 |
ccs_create_entry("domain_policy", 0600, ccs_dir, CCS_DOMAINPOLICY); |
305 |
ccs_create_entry("exception_policy", 0600, ccs_dir, |
306 |
CCS_EXCEPTIONPOLICY); |
307 |
ccs_create_entry("grant_log", 0400, ccs_dir, CCS_GRANTLOG); |
308 |
ccs_create_entry("reject_log", 0400, ccs_dir, CCS_REJECTLOG); |
309 |
ccs_create_entry(".domain_status", 0600, ccs_dir, CCS_DOMAIN_STATUS); |
310 |
ccs_create_entry(".process_status", 0600, ccs_dir, |
311 |
CCS_PROCESS_STATUS); |
312 |
ccs_create_entry("meminfo", 0600, ccs_dir, CCS_MEMINFO); |
313 |
ccs_create_entry("profile", 0600, ccs_dir, CCS_PROFILE); |
314 |
ccs_create_entry("manager", 0600, ccs_dir, CCS_MANAGER); |
315 |
ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); |
316 |
ccs_create_entry(".execute_handler", 0666, ccs_dir, |
317 |
CCS_EXECUTE_HANDLER); |
318 |
{ |
319 |
struct proc_dir_entry *e = create_proc_entry("self_domain", |
320 |
0666, ccs_dir); |
321 |
if (e) |
322 |
e->proc_fops = &ccs_self_operations; |
323 |
} |
324 |
} |
325 |
|
326 |
static int __init ccs_init_module(void) |
327 |
{ |
328 |
int i; |
329 |
for (i = 0; i < CCS_MAX_POLICY; i++) |
330 |
INIT_LIST_HEAD(&ccs_policy_list[i]); |
331 |
for (i = 0; i < CCS_MAX_GROUP; i++) |
332 |
INIT_LIST_HEAD(&ccs_group_list[i]); |
333 |
for (i = 0; i < CCS_MAX_LIST; i++) |
334 |
INIT_LIST_HEAD(&ccs_shared_list[i]); |
335 |
if (ccsecurity_ops.disabled) |
336 |
return -EINVAL; |
337 |
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 0) |
338 |
MOD_INC_USE_COUNT; |
339 |
#endif |
340 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) |
341 |
if (init_srcu_struct(&ccs_ss)) |
342 |
panic("Out of memory."); |
343 |
#endif |
344 |
ccs_proc_init(); |
345 |
ccs_mm_init(); |
346 |
ccs_capability_init(); |
347 |
ccs_file_init(); |
348 |
ccs_network_init(); |
349 |
ccs_signal_init(); |
350 |
ccs_mount_init(); |
351 |
ccs_policy_io_init(); |
352 |
ccs_domain_init(); |
353 |
return 0; |
354 |
} |
355 |
|
356 |
MODULE_LICENSE("GPL"); |
357 |
module_init(ccs_init_module); |