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