Commit | Line | Data |
---|---|---|
a10e763b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 LT |
2 | /* Updated: Karl MacMillan <kmacmillan@tresys.com> |
3 | * | |
1872981b | 4 | * Added conditional policy language extensions |
1da177e4 | 5 | * |
82c21bfa | 6 | * Updated: Hewlett-Packard <paul@paul-moore.com> |
3bb56b25 | 7 | * |
1872981b | 8 | * Added support for the policy capability bitmap |
3bb56b25 PM |
9 | * |
10 | * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. | |
1da177e4 LT |
11 | * Copyright (C) 2003 - 2004 Tresys Technology, LLC |
12 | * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> | |
1da177e4 LT |
13 | */ |
14 | ||
1da177e4 LT |
15 | #include <linux/kernel.h> |
16 | #include <linux/pagemap.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/vmalloc.h> | |
19 | #include <linux/fs.h> | |
920f50b2 | 20 | #include <linux/fs_context.h> |
0619f0f5 | 21 | #include <linux/mount.h> |
bb003079 | 22 | #include <linux/mutex.h> |
1da177e4 LT |
23 | #include <linux/init.h> |
24 | #include <linux/string.h> | |
25 | #include <linux/security.h> | |
26 | #include <linux/major.h> | |
27 | #include <linux/seq_file.h> | |
28 | #include <linux/percpu.h> | |
af601e46 | 29 | #include <linux/audit.h> |
f5269710 | 30 | #include <linux/uaccess.h> |
7a627e3b | 31 | #include <linux/kobject.h> |
0f7e4c33 | 32 | #include <linux/ctype.h> |
1da177e4 LT |
33 | |
34 | /* selinuxfs pseudo filesystem for exporting the security policy API. | |
35 | Based on the proc code and the fs/nfsd/nfsctl.c code. */ | |
36 | ||
37 | #include "flask.h" | |
38 | #include "avc.h" | |
39 | #include "avc_ss.h" | |
40 | #include "security.h" | |
41 | #include "objsec.h" | |
42 | #include "conditional.h" | |
43 | ||
1da177e4 LT |
44 | enum sel_inos { |
45 | SEL_ROOT_INO = 2, | |
46 | SEL_LOAD, /* load policy */ | |
47 | SEL_ENFORCE, /* get or set enforcing status */ | |
48 | SEL_CONTEXT, /* validate context */ | |
49 | SEL_ACCESS, /* compute access decision */ | |
50 | SEL_CREATE, /* compute create labeling decision */ | |
51 | SEL_RELABEL, /* compute relabeling decision */ | |
52 | SEL_USER, /* compute reachable user contexts */ | |
53 | SEL_POLICYVERS, /* return policy version for this kernel */ | |
54 | SEL_COMMIT_BOOLS, /* commit new boolean values */ | |
55 | SEL_MLS, /* return if MLS policy is enabled */ | |
56 | SEL_DISABLE, /* disable SELinux until next reboot */ | |
1da177e4 LT |
57 | SEL_MEMBER, /* compute polyinstantiation membership decision */ |
58 | SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ | |
4e5ab4cb | 59 | SEL_COMPAT_NET, /* whether to use old compat network packet controls */ |
3f12070e EP |
60 | SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ |
61 | SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ | |
11904167 | 62 | SEL_STATUS, /* export current status using mmap() */ |
cee74f47 | 63 | SEL_POLICY, /* allow userspace to read the in kernel policy */ |
f9df6458 | 64 | SEL_VALIDATE_TRANS, /* compute validatetrans decision */ |
6174eafc | 65 | SEL_INO_NEXT, /* The next inode number to use */ |
1da177e4 LT |
66 | }; |
67 | ||
0619f0f5 SS |
68 | struct selinux_fs_info { |
69 | struct dentry *bool_dir; | |
70 | unsigned int bool_num; | |
71 | char **bool_pending_names; | |
72 | unsigned int *bool_pending_values; | |
73 | struct dentry *class_dir; | |
74 | unsigned long last_class_ino; | |
75 | bool policy_opened; | |
76 | struct dentry *policycap_dir; | |
77 | struct mutex mutex; | |
78 | unsigned long last_ino; | |
79 | struct selinux_state *state; | |
80 | struct super_block *sb; | |
81 | }; | |
82 | ||
83 | static int selinux_fs_info_create(struct super_block *sb) | |
84 | { | |
85 | struct selinux_fs_info *fsi; | |
86 | ||
87 | fsi = kzalloc(sizeof(*fsi), GFP_KERNEL); | |
88 | if (!fsi) | |
89 | return -ENOMEM; | |
90 | ||
91 | mutex_init(&fsi->mutex); | |
92 | fsi->last_ino = SEL_INO_NEXT - 1; | |
93 | fsi->state = &selinux_state; | |
94 | fsi->sb = sb; | |
95 | sb->s_fs_info = fsi; | |
96 | return 0; | |
97 | } | |
98 | ||
99 | static void selinux_fs_info_free(struct super_block *sb) | |
100 | { | |
101 | struct selinux_fs_info *fsi = sb->s_fs_info; | |
102 | int i; | |
103 | ||
104 | if (fsi) { | |
105 | for (i = 0; i < fsi->bool_num; i++) | |
106 | kfree(fsi->bool_pending_names[i]); | |
107 | kfree(fsi->bool_pending_names); | |
108 | kfree(fsi->bool_pending_values); | |
109 | } | |
110 | kfree(sb->s_fs_info); | |
111 | sb->s_fs_info = NULL; | |
112 | } | |
6174eafc | 113 | |
3bb56b25 PM |
114 | #define SEL_INITCON_INO_OFFSET 0x01000000 |
115 | #define SEL_BOOL_INO_OFFSET 0x02000000 | |
116 | #define SEL_CLASS_INO_OFFSET 0x04000000 | |
117 | #define SEL_POLICYCAP_INO_OFFSET 0x08000000 | |
118 | #define SEL_INO_MASK 0x00ffffff | |
f0ee2e46 | 119 | |
1da177e4 LT |
120 | #define TMPBUFLEN 12 |
121 | static ssize_t sel_read_enforce(struct file *filp, char __user *buf, | |
122 | size_t count, loff_t *ppos) | |
123 | { | |
0619f0f5 | 124 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
1da177e4 LT |
125 | char tmpbuf[TMPBUFLEN]; |
126 | ssize_t length; | |
127 | ||
aa8e712c | 128 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", |
0619f0f5 | 129 | enforcing_enabled(fsi->state)); |
1da177e4 LT |
130 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); |
131 | } | |
132 | ||
133 | #ifdef CONFIG_SECURITY_SELINUX_DEVELOP | |
1872981b | 134 | static ssize_t sel_write_enforce(struct file *file, const char __user *buf, |
1da177e4 LT |
135 | size_t count, loff_t *ppos) |
136 | ||
137 | { | |
0619f0f5 SS |
138 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
139 | struct selinux_state *state = fsi->state; | |
b77a493b | 140 | char *page = NULL; |
1da177e4 | 141 | ssize_t length; |
aa8e712c | 142 | int old_value, new_value; |
1da177e4 | 143 | |
bfd51626 | 144 | if (count >= PAGE_SIZE) |
8365a719 | 145 | return -ENOMEM; |
b77a493b EP |
146 | |
147 | /* No partial writes. */ | |
b77a493b | 148 | if (*ppos != 0) |
8365a719 | 149 | return -EINVAL; |
b77a493b | 150 | |
8365a719 AV |
151 | page = memdup_user_nul(buf, count); |
152 | if (IS_ERR(page)) | |
153 | return PTR_ERR(page); | |
1da177e4 LT |
154 | |
155 | length = -EINVAL; | |
156 | if (sscanf(page, "%d", &new_value) != 1) | |
157 | goto out; | |
158 | ||
ea49d10e SS |
159 | new_value = !!new_value; |
160 | ||
0619f0f5 | 161 | old_value = enforcing_enabled(state); |
aa8e712c | 162 | if (new_value != old_value) { |
6b6bc620 SS |
163 | length = avc_has_perm(&selinux_state, |
164 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
165 | SECCLASS_SECURITY, SECURITY__SETENFORCE, |
166 | NULL); | |
1da177e4 LT |
167 | if (length) |
168 | goto out; | |
cdfb6b34 | 169 | audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, |
4195ed42 | 170 | "enforcing=%d old_enforcing=%d auid=%u ses=%u" |
6c5a682e | 171 | " enabled=1 old-enabled=1 lsm=selinux res=1", |
aa8e712c | 172 | new_value, old_value, |
581abc09 | 173 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
6c5a682e | 174 | audit_get_sessionid(current)); |
0619f0f5 | 175 | enforcing_set(state, new_value); |
aa8e712c | 176 | if (new_value) |
6b6bc620 | 177 | avc_ss_reset(state->avc, 0); |
aa8e712c | 178 | selnl_notify_setenforce(new_value); |
0619f0f5 | 179 | selinux_status_update_setenforce(state, new_value); |
aa8e712c | 180 | if (!new_value) |
42df744c | 181 | call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); |
1da177e4 LT |
182 | } |
183 | length = count; | |
184 | out: | |
8365a719 | 185 | kfree(page); |
1da177e4 LT |
186 | return length; |
187 | } | |
188 | #else | |
189 | #define sel_write_enforce NULL | |
190 | #endif | |
191 | ||
9c2e08c5 | 192 | static const struct file_operations sel_enforce_ops = { |
1da177e4 LT |
193 | .read = sel_read_enforce, |
194 | .write = sel_write_enforce, | |
57a62c23 | 195 | .llseek = generic_file_llseek, |
1da177e4 LT |
196 | }; |
197 | ||
3f12070e EP |
198 | static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, |
199 | size_t count, loff_t *ppos) | |
200 | { | |
0619f0f5 SS |
201 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
202 | struct selinux_state *state = fsi->state; | |
3f12070e EP |
203 | char tmpbuf[TMPBUFLEN]; |
204 | ssize_t length; | |
496ad9aa | 205 | ino_t ino = file_inode(filp)->i_ino; |
3f12070e | 206 | int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ? |
0619f0f5 SS |
207 | security_get_reject_unknown(state) : |
208 | !security_get_allow_unknown(state); | |
3f12070e EP |
209 | |
210 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown); | |
211 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | |
212 | } | |
213 | ||
214 | static const struct file_operations sel_handle_unknown_ops = { | |
215 | .read = sel_read_handle_unknown, | |
57a62c23 | 216 | .llseek = generic_file_llseek, |
3f12070e EP |
217 | }; |
218 | ||
11904167 KK |
219 | static int sel_open_handle_status(struct inode *inode, struct file *filp) |
220 | { | |
0619f0f5 SS |
221 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
222 | struct page *status = selinux_kernel_status_page(fsi->state); | |
11904167 KK |
223 | |
224 | if (!status) | |
225 | return -ENOMEM; | |
226 | ||
227 | filp->private_data = status; | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
232 | static ssize_t sel_read_handle_status(struct file *filp, char __user *buf, | |
233 | size_t count, loff_t *ppos) | |
234 | { | |
235 | struct page *status = filp->private_data; | |
236 | ||
237 | BUG_ON(!status); | |
238 | ||
239 | return simple_read_from_buffer(buf, count, ppos, | |
240 | page_address(status), | |
241 | sizeof(struct selinux_kernel_status)); | |
242 | } | |
243 | ||
244 | static int sel_mmap_handle_status(struct file *filp, | |
245 | struct vm_area_struct *vma) | |
246 | { | |
247 | struct page *status = filp->private_data; | |
248 | unsigned long size = vma->vm_end - vma->vm_start; | |
249 | ||
250 | BUG_ON(!status); | |
251 | ||
252 | /* only allows one page from the head */ | |
253 | if (vma->vm_pgoff > 0 || size != PAGE_SIZE) | |
254 | return -EIO; | |
255 | /* disallow writable mapping */ | |
256 | if (vma->vm_flags & VM_WRITE) | |
257 | return -EPERM; | |
258 | /* disallow mprotect() turns it into writable */ | |
259 | vma->vm_flags &= ~VM_MAYWRITE; | |
260 | ||
261 | return remap_pfn_range(vma, vma->vm_start, | |
262 | page_to_pfn(status), | |
263 | size, vma->vm_page_prot); | |
264 | } | |
265 | ||
266 | static const struct file_operations sel_handle_status_ops = { | |
267 | .open = sel_open_handle_status, | |
268 | .read = sel_read_handle_status, | |
269 | .mmap = sel_mmap_handle_status, | |
270 | .llseek = generic_file_llseek, | |
271 | }; | |
272 | ||
1da177e4 | 273 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE |
1872981b | 274 | static ssize_t sel_write_disable(struct file *file, const char __user *buf, |
1da177e4 LT |
275 | size_t count, loff_t *ppos) |
276 | ||
277 | { | |
0619f0f5 | 278 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
8365a719 | 279 | char *page; |
1da177e4 LT |
280 | ssize_t length; |
281 | int new_value; | |
4195ed42 | 282 | int enforcing; |
1da177e4 | 283 | |
89b223bf PM |
284 | /* NOTE: we are now officially considering runtime disable as |
285 | * deprecated, and using it will become increasingly painful | |
286 | * (e.g. sleeping/blocking) as we progress through future | |
287 | * kernel releases until eventually it is removed | |
288 | */ | |
289 | pr_err("SELinux: Runtime disable is deprecated, use selinux=0 on the kernel cmdline.\n"); | |
290 | ||
bfd51626 | 291 | if (count >= PAGE_SIZE) |
8365a719 | 292 | return -ENOMEM; |
b77a493b EP |
293 | |
294 | /* No partial writes. */ | |
b77a493b | 295 | if (*ppos != 0) |
8365a719 | 296 | return -EINVAL; |
b77a493b | 297 | |
8365a719 AV |
298 | page = memdup_user_nul(buf, count); |
299 | if (IS_ERR(page)) | |
300 | return PTR_ERR(page); | |
1da177e4 LT |
301 | |
302 | length = -EINVAL; | |
303 | if (sscanf(page, "%d", &new_value) != 1) | |
304 | goto out; | |
305 | ||
306 | if (new_value) { | |
4195ed42 | 307 | enforcing = enforcing_enabled(fsi->state); |
0619f0f5 | 308 | length = selinux_disable(fsi->state); |
b77a493b | 309 | if (length) |
1da177e4 | 310 | goto out; |
cdfb6b34 | 311 | audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, |
4195ed42 | 312 | "enforcing=%d old_enforcing=%d auid=%u ses=%u" |
6c5a682e | 313 | " enabled=0 old-enabled=1 lsm=selinux res=1", |
4195ed42 | 314 | enforcing, enforcing, |
581abc09 | 315 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
6c5a682e | 316 | audit_get_sessionid(current)); |
1da177e4 LT |
317 | } |
318 | ||
319 | length = count; | |
320 | out: | |
8365a719 | 321 | kfree(page); |
1da177e4 LT |
322 | return length; |
323 | } | |
324 | #else | |
325 | #define sel_write_disable NULL | |
326 | #endif | |
327 | ||
9c2e08c5 | 328 | static const struct file_operations sel_disable_ops = { |
1da177e4 | 329 | .write = sel_write_disable, |
57a62c23 | 330 | .llseek = generic_file_llseek, |
1da177e4 LT |
331 | }; |
332 | ||
333 | static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, | |
1872981b | 334 | size_t count, loff_t *ppos) |
1da177e4 LT |
335 | { |
336 | char tmpbuf[TMPBUFLEN]; | |
337 | ssize_t length; | |
338 | ||
339 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX); | |
340 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | |
341 | } | |
342 | ||
9c2e08c5 | 343 | static const struct file_operations sel_policyvers_ops = { |
1da177e4 | 344 | .read = sel_read_policyvers, |
57a62c23 | 345 | .llseek = generic_file_llseek, |
1da177e4 LT |
346 | }; |
347 | ||
348 | /* declaration for sel_write_load */ | |
02a52c5c SS |
349 | static int sel_make_bools(struct selinux_fs_info *fsi, |
350 | struct selinux_policy *newpolicy); | |
351 | static int sel_make_classes(struct selinux_fs_info *fsi, | |
352 | struct selinux_policy *newpolicy); | |
e47c8fc5 CP |
353 | |
354 | /* declaration for sel_make_class_dirs */ | |
a1c2aa1e | 355 | static struct dentry *sel_make_dir(struct dentry *dir, const char *name, |
e47c8fc5 | 356 | unsigned long *ino); |
1da177e4 LT |
357 | |
358 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, | |
359 | size_t count, loff_t *ppos) | |
360 | { | |
0619f0f5 | 361 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
1da177e4 LT |
362 | char tmpbuf[TMPBUFLEN]; |
363 | ssize_t length; | |
364 | ||
0719aaf5 | 365 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", |
0619f0f5 | 366 | security_mls_enabled(fsi->state)); |
1da177e4 LT |
367 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); |
368 | } | |
369 | ||
9c2e08c5 | 370 | static const struct file_operations sel_mls_ops = { |
1da177e4 | 371 | .read = sel_read_mls, |
57a62c23 | 372 | .llseek = generic_file_llseek, |
1da177e4 LT |
373 | }; |
374 | ||
cee74f47 EP |
375 | struct policy_load_memory { |
376 | size_t len; | |
377 | void *data; | |
378 | }; | |
379 | ||
380 | static int sel_open_policy(struct inode *inode, struct file *filp) | |
381 | { | |
0619f0f5 SS |
382 | struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; |
383 | struct selinux_state *state = fsi->state; | |
cee74f47 EP |
384 | struct policy_load_memory *plm = NULL; |
385 | int rc; | |
386 | ||
387 | BUG_ON(filp->private_data); | |
388 | ||
0619f0f5 | 389 | mutex_lock(&fsi->mutex); |
cee74f47 | 390 | |
6b6bc620 SS |
391 | rc = avc_has_perm(&selinux_state, |
392 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 | 393 | SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); |
cee74f47 EP |
394 | if (rc) |
395 | goto err; | |
396 | ||
397 | rc = -EBUSY; | |
0619f0f5 | 398 | if (fsi->policy_opened) |
cee74f47 EP |
399 | goto err; |
400 | ||
401 | rc = -ENOMEM; | |
402 | plm = kzalloc(sizeof(*plm), GFP_KERNEL); | |
403 | if (!plm) | |
404 | goto err; | |
405 | ||
0619f0f5 | 406 | if (i_size_read(inode) != security_policydb_len(state)) { |
5955102c | 407 | inode_lock(inode); |
0619f0f5 | 408 | i_size_write(inode, security_policydb_len(state)); |
5955102c | 409 | inode_unlock(inode); |
cee74f47 EP |
410 | } |
411 | ||
0619f0f5 | 412 | rc = security_read_policy(state, &plm->data, &plm->len); |
cee74f47 EP |
413 | if (rc) |
414 | goto err; | |
415 | ||
0619f0f5 | 416 | fsi->policy_opened = 1; |
cee74f47 EP |
417 | |
418 | filp->private_data = plm; | |
419 | ||
0619f0f5 | 420 | mutex_unlock(&fsi->mutex); |
cee74f47 EP |
421 | |
422 | return 0; | |
423 | err: | |
0619f0f5 | 424 | mutex_unlock(&fsi->mutex); |
cee74f47 EP |
425 | |
426 | if (plm) | |
427 | vfree(plm->data); | |
428 | kfree(plm); | |
429 | return rc; | |
430 | } | |
431 | ||
432 | static int sel_release_policy(struct inode *inode, struct file *filp) | |
433 | { | |
0619f0f5 | 434 | struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; |
cee74f47 EP |
435 | struct policy_load_memory *plm = filp->private_data; |
436 | ||
437 | BUG_ON(!plm); | |
438 | ||
0619f0f5 | 439 | fsi->policy_opened = 0; |
cee74f47 EP |
440 | |
441 | vfree(plm->data); | |
442 | kfree(plm); | |
443 | ||
444 | return 0; | |
445 | } | |
446 | ||
447 | static ssize_t sel_read_policy(struct file *filp, char __user *buf, | |
448 | size_t count, loff_t *ppos) | |
449 | { | |
450 | struct policy_load_memory *plm = filp->private_data; | |
451 | int ret; | |
452 | ||
6b6bc620 SS |
453 | ret = avc_has_perm(&selinux_state, |
454 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 | 455 | SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); |
cee74f47 | 456 | if (ret) |
0da74120 | 457 | return ret; |
cee74f47 | 458 | |
0da74120 | 459 | return simple_read_from_buffer(buf, count, ppos, plm->data, plm->len); |
cee74f47 EP |
460 | } |
461 | ||
ac9a1f6d | 462 | static vm_fault_t sel_mmap_policy_fault(struct vm_fault *vmf) |
845ca30f | 463 | { |
11bac800 | 464 | struct policy_load_memory *plm = vmf->vma->vm_file->private_data; |
845ca30f EP |
465 | unsigned long offset; |
466 | struct page *page; | |
467 | ||
468 | if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE)) | |
469 | return VM_FAULT_SIGBUS; | |
470 | ||
471 | offset = vmf->pgoff << PAGE_SHIFT; | |
472 | if (offset >= roundup(plm->len, PAGE_SIZE)) | |
473 | return VM_FAULT_SIGBUS; | |
474 | ||
475 | page = vmalloc_to_page(plm->data + offset); | |
476 | get_page(page); | |
477 | ||
478 | vmf->page = page; | |
479 | ||
480 | return 0; | |
481 | } | |
482 | ||
7cbea8dc | 483 | static const struct vm_operations_struct sel_mmap_policy_ops = { |
845ca30f EP |
484 | .fault = sel_mmap_policy_fault, |
485 | .page_mkwrite = sel_mmap_policy_fault, | |
486 | }; | |
487 | ||
ad3fa08c | 488 | static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) |
845ca30f EP |
489 | { |
490 | if (vma->vm_flags & VM_SHARED) { | |
491 | /* do not allow mprotect to make mapping writable */ | |
492 | vma->vm_flags &= ~VM_MAYWRITE; | |
493 | ||
494 | if (vma->vm_flags & VM_WRITE) | |
495 | return -EACCES; | |
496 | } | |
497 | ||
314e51b9 | 498 | vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; |
845ca30f EP |
499 | vma->vm_ops = &sel_mmap_policy_ops; |
500 | ||
501 | return 0; | |
502 | } | |
503 | ||
cee74f47 EP |
504 | static const struct file_operations sel_policy_ops = { |
505 | .open = sel_open_policy, | |
506 | .read = sel_read_policy, | |
845ca30f | 507 | .mmap = sel_mmap_policy, |
cee74f47 | 508 | .release = sel_release_policy, |
47a93a5b | 509 | .llseek = generic_file_llseek, |
cee74f47 EP |
510 | }; |
511 | ||
02a52c5c SS |
512 | static int sel_make_policy_nodes(struct selinux_fs_info *fsi, |
513 | struct selinux_policy *newpolicy) | |
0619f0f5 SS |
514 | { |
515 | int ret; | |
516 | ||
02a52c5c | 517 | ret = sel_make_bools(fsi, newpolicy); |
0619f0f5 SS |
518 | if (ret) { |
519 | pr_err("SELinux: failed to load policy booleans\n"); | |
520 | return ret; | |
521 | } | |
522 | ||
02a52c5c | 523 | ret = sel_make_classes(fsi, newpolicy); |
0619f0f5 SS |
524 | if (ret) { |
525 | pr_err("SELinux: failed to load policy classes\n"); | |
526 | return ret; | |
527 | } | |
528 | ||
0619f0f5 SS |
529 | return 0; |
530 | } | |
531 | ||
1872981b | 532 | static ssize_t sel_write_load(struct file *file, const char __user *buf, |
1da177e4 LT |
533 | size_t count, loff_t *ppos) |
534 | ||
535 | { | |
0619f0f5 | 536 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
02a52c5c | 537 | struct selinux_policy *newpolicy; |
1da177e4 LT |
538 | ssize_t length; |
539 | void *data = NULL; | |
540 | ||
0619f0f5 | 541 | mutex_lock(&fsi->mutex); |
1da177e4 | 542 | |
6b6bc620 SS |
543 | length = avc_has_perm(&selinux_state, |
544 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 | 545 | SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); |
1da177e4 LT |
546 | if (length) |
547 | goto out; | |
548 | ||
b77a493b EP |
549 | /* No partial writes. */ |
550 | length = -EINVAL; | |
551 | if (*ppos != 0) | |
1da177e4 | 552 | goto out; |
1da177e4 | 553 | |
b77a493b EP |
554 | length = -ENOMEM; |
555 | data = vmalloc(count); | |
556 | if (!data) | |
1da177e4 | 557 | goto out; |
1da177e4 LT |
558 | |
559 | length = -EFAULT; | |
560 | if (copy_from_user(data, buf, count) != 0) | |
561 | goto out; | |
562 | ||
02a52c5c | 563 | length = security_load_policy(fsi->state, data, count, &newpolicy); |
4262fb51 GT |
564 | if (length) { |
565 | pr_warn_ratelimited("SELinux: failed to load policy\n"); | |
1da177e4 | 566 | goto out; |
4262fb51 | 567 | } |
1da177e4 | 568 | |
02a52c5c SS |
569 | length = sel_make_policy_nodes(fsi, newpolicy); |
570 | if (length) { | |
571 | selinux_policy_cancel(fsi->state, newpolicy); | |
b77a493b | 572 | goto out1; |
02a52c5c SS |
573 | } |
574 | ||
575 | selinux_policy_commit(fsi->state, newpolicy); | |
b77a493b EP |
576 | |
577 | length = count; | |
e47c8fc5 CP |
578 | |
579 | out1: | |
cdfb6b34 | 580 | audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, |
d141136f | 581 | "auid=%u ses=%u lsm=selinux res=1", |
581abc09 | 582 | from_kuid(&init_user_ns, audit_get_loginuid(current)), |
4746ec5b | 583 | audit_get_sessionid(current)); |
1da177e4 | 584 | out: |
0619f0f5 | 585 | mutex_unlock(&fsi->mutex); |
1da177e4 LT |
586 | vfree(data); |
587 | return length; | |
588 | } | |
589 | ||
9c2e08c5 | 590 | static const struct file_operations sel_load_ops = { |
1da177e4 | 591 | .write = sel_write_load, |
57a62c23 | 592 | .llseek = generic_file_llseek, |
1da177e4 LT |
593 | }; |
594 | ||
1872981b | 595 | static ssize_t sel_write_context(struct file *file, char *buf, size_t size) |
1da177e4 | 596 | { |
0619f0f5 SS |
597 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
598 | struct selinux_state *state = fsi->state; | |
b77a493b | 599 | char *canon = NULL; |
ce9982d0 | 600 | u32 sid, len; |
1da177e4 LT |
601 | ssize_t length; |
602 | ||
6b6bc620 SS |
603 | length = avc_has_perm(&selinux_state, |
604 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 | 605 | SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL); |
1da177e4 | 606 | if (length) |
b77a493b | 607 | goto out; |
1da177e4 | 608 | |
0619f0f5 | 609 | length = security_context_to_sid(state, buf, size, &sid, GFP_KERNEL); |
b77a493b EP |
610 | if (length) |
611 | goto out; | |
1da177e4 | 612 | |
0619f0f5 | 613 | length = security_sid_to_context(state, sid, &canon, &len); |
b77a493b EP |
614 | if (length) |
615 | goto out; | |
ce9982d0 | 616 | |
b77a493b | 617 | length = -ERANGE; |
ce9982d0 | 618 | if (len > SIMPLE_TRANSACTION_LIMIT) { |
f8b69a5f | 619 | pr_err("SELinux: %s: context size (%u) exceeds " |
744ba35e | 620 | "payload max\n", __func__, len); |
1da177e4 | 621 | goto out; |
ce9982d0 | 622 | } |
1da177e4 | 623 | |
ce9982d0 SS |
624 | memcpy(buf, canon, len); |
625 | length = len; | |
1da177e4 | 626 | out: |
ce9982d0 | 627 | kfree(canon); |
1da177e4 LT |
628 | return length; |
629 | } | |
630 | ||
1da177e4 LT |
631 | static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, |
632 | size_t count, loff_t *ppos) | |
633 | { | |
0619f0f5 | 634 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
1da177e4 LT |
635 | char tmpbuf[TMPBUFLEN]; |
636 | ssize_t length; | |
637 | ||
0619f0f5 | 638 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", fsi->state->checkreqprot); |
1da177e4 LT |
639 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); |
640 | } | |
641 | ||
1872981b | 642 | static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, |
1da177e4 LT |
643 | size_t count, loff_t *ppos) |
644 | { | |
0619f0f5 | 645 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
8365a719 | 646 | char *page; |
1da177e4 LT |
647 | ssize_t length; |
648 | unsigned int new_value; | |
649 | ||
6b6bc620 SS |
650 | length = avc_has_perm(&selinux_state, |
651 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
652 | SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, |
653 | NULL); | |
1da177e4 | 654 | if (length) |
8365a719 | 655 | return length; |
1da177e4 | 656 | |
bfd51626 | 657 | if (count >= PAGE_SIZE) |
8365a719 | 658 | return -ENOMEM; |
b77a493b EP |
659 | |
660 | /* No partial writes. */ | |
b77a493b | 661 | if (*ppos != 0) |
8365a719 | 662 | return -EINVAL; |
b77a493b | 663 | |
8365a719 AV |
664 | page = memdup_user_nul(buf, count); |
665 | if (IS_ERR(page)) | |
666 | return PTR_ERR(page); | |
1da177e4 LT |
667 | |
668 | length = -EINVAL; | |
669 | if (sscanf(page, "%u", &new_value) != 1) | |
670 | goto out; | |
671 | ||
e9c38f9f SS |
672 | if (new_value) { |
673 | char comm[sizeof(current->comm)]; | |
674 | ||
675 | memcpy(comm, current->comm, sizeof(comm)); | |
676 | pr_warn_once("SELinux: %s (%d) set checkreqprot to 1. This is deprecated and will be rejected in a future kernel release.\n", | |
677 | comm, current->pid); | |
678 | } | |
679 | ||
0619f0f5 | 680 | fsi->state->checkreqprot = new_value ? 1 : 0; |
1da177e4 LT |
681 | length = count; |
682 | out: | |
8365a719 | 683 | kfree(page); |
1da177e4 LT |
684 | return length; |
685 | } | |
9c2e08c5 | 686 | static const struct file_operations sel_checkreqprot_ops = { |
1da177e4 LT |
687 | .read = sel_read_checkreqprot, |
688 | .write = sel_write_checkreqprot, | |
57a62c23 | 689 | .llseek = generic_file_llseek, |
1da177e4 LT |
690 | }; |
691 | ||
f9df6458 AP |
692 | static ssize_t sel_write_validatetrans(struct file *file, |
693 | const char __user *buf, | |
694 | size_t count, loff_t *ppos) | |
695 | { | |
0619f0f5 SS |
696 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
697 | struct selinux_state *state = fsi->state; | |
f9df6458 AP |
698 | char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; |
699 | char *req = NULL; | |
700 | u32 osid, nsid, tsid; | |
701 | u16 tclass; | |
702 | int rc; | |
703 | ||
6b6bc620 SS |
704 | rc = avc_has_perm(&selinux_state, |
705 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 | 706 | SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL); |
f9df6458 AP |
707 | if (rc) |
708 | goto out; | |
709 | ||
710 | rc = -ENOMEM; | |
711 | if (count >= PAGE_SIZE) | |
712 | goto out; | |
713 | ||
714 | /* No partial writes. */ | |
715 | rc = -EINVAL; | |
716 | if (*ppos != 0) | |
717 | goto out; | |
718 | ||
0b884d25 AV |
719 | req = memdup_user_nul(buf, count); |
720 | if (IS_ERR(req)) { | |
721 | rc = PTR_ERR(req); | |
722 | req = NULL; | |
f9df6458 | 723 | goto out; |
0b884d25 | 724 | } |
f9df6458 AP |
725 | |
726 | rc = -ENOMEM; | |
727 | oldcon = kzalloc(count + 1, GFP_KERNEL); | |
728 | if (!oldcon) | |
729 | goto out; | |
730 | ||
731 | newcon = kzalloc(count + 1, GFP_KERNEL); | |
732 | if (!newcon) | |
733 | goto out; | |
734 | ||
735 | taskcon = kzalloc(count + 1, GFP_KERNEL); | |
736 | if (!taskcon) | |
737 | goto out; | |
738 | ||
739 | rc = -EINVAL; | |
740 | if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) | |
741 | goto out; | |
742 | ||
0619f0f5 | 743 | rc = security_context_str_to_sid(state, oldcon, &osid, GFP_KERNEL); |
f9df6458 AP |
744 | if (rc) |
745 | goto out; | |
746 | ||
0619f0f5 | 747 | rc = security_context_str_to_sid(state, newcon, &nsid, GFP_KERNEL); |
f9df6458 AP |
748 | if (rc) |
749 | goto out; | |
750 | ||
0619f0f5 | 751 | rc = security_context_str_to_sid(state, taskcon, &tsid, GFP_KERNEL); |
f9df6458 AP |
752 | if (rc) |
753 | goto out; | |
754 | ||
0619f0f5 | 755 | rc = security_validate_transition_user(state, osid, nsid, tsid, tclass); |
f9df6458 AP |
756 | if (!rc) |
757 | rc = count; | |
758 | out: | |
759 | kfree(req); | |
760 | kfree(oldcon); | |
761 | kfree(newcon); | |
762 | kfree(taskcon); | |
763 | return rc; | |
764 | } | |
765 | ||
766 | static const struct file_operations sel_transition_ops = { | |
767 | .write = sel_write_validatetrans, | |
768 | .llseek = generic_file_llseek, | |
769 | }; | |
770 | ||
1da177e4 LT |
771 | /* |
772 | * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c | |
773 | */ | |
1872981b EP |
774 | static ssize_t sel_write_access(struct file *file, char *buf, size_t size); |
775 | static ssize_t sel_write_create(struct file *file, char *buf, size_t size); | |
776 | static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size); | |
777 | static ssize_t sel_write_user(struct file *file, char *buf, size_t size); | |
778 | static ssize_t sel_write_member(struct file *file, char *buf, size_t size); | |
1da177e4 | 779 | |
631d2b49 | 780 | static ssize_t (*const write_op[])(struct file *, char *, size_t) = { |
1da177e4 LT |
781 | [SEL_ACCESS] = sel_write_access, |
782 | [SEL_CREATE] = sel_write_create, | |
783 | [SEL_RELABEL] = sel_write_relabel, | |
784 | [SEL_USER] = sel_write_user, | |
785 | [SEL_MEMBER] = sel_write_member, | |
ce9982d0 | 786 | [SEL_CONTEXT] = sel_write_context, |
1da177e4 LT |
787 | }; |
788 | ||
789 | static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) | |
790 | { | |
496ad9aa | 791 | ino_t ino = file_inode(file)->i_ino; |
1da177e4 LT |
792 | char *data; |
793 | ssize_t rv; | |
794 | ||
6e20a64a | 795 | if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) |
1da177e4 LT |
796 | return -EINVAL; |
797 | ||
798 | data = simple_transaction_get(file, buf, size); | |
799 | if (IS_ERR(data)) | |
800 | return PTR_ERR(data); | |
801 | ||
1872981b EP |
802 | rv = write_op[ino](file, data, size); |
803 | if (rv > 0) { | |
1da177e4 LT |
804 | simple_transaction_set(file, rv); |
805 | rv = size; | |
806 | } | |
807 | return rv; | |
808 | } | |
809 | ||
9c2e08c5 | 810 | static const struct file_operations transaction_ops = { |
1da177e4 LT |
811 | .write = selinux_transaction_write, |
812 | .read = simple_transaction_read, | |
813 | .release = simple_transaction_release, | |
57a62c23 | 814 | .llseek = generic_file_llseek, |
1da177e4 LT |
815 | }; |
816 | ||
817 | /* | |
818 | * payload - write methods | |
819 | * If the method has a response, the response should be put in buf, | |
820 | * and the length returned. Otherwise return 0 or and -error. | |
821 | */ | |
822 | ||
1872981b | 823 | static ssize_t sel_write_access(struct file *file, char *buf, size_t size) |
1da177e4 | 824 | { |
0619f0f5 SS |
825 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
826 | struct selinux_state *state = fsi->state; | |
b77a493b | 827 | char *scon = NULL, *tcon = NULL; |
1da177e4 LT |
828 | u32 ssid, tsid; |
829 | u16 tclass; | |
1da177e4 LT |
830 | struct av_decision avd; |
831 | ssize_t length; | |
832 | ||
6b6bc620 SS |
833 | length = avc_has_perm(&selinux_state, |
834 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 | 835 | SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL); |
1da177e4 | 836 | if (length) |
b77a493b | 837 | goto out; |
1da177e4 LT |
838 | |
839 | length = -ENOMEM; | |
c1a7368a | 840 | scon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 | 841 | if (!scon) |
b77a493b | 842 | goto out; |
1da177e4 | 843 | |
b77a493b | 844 | length = -ENOMEM; |
c1a7368a | 845 | tcon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 LT |
846 | if (!tcon) |
847 | goto out; | |
1da177e4 LT |
848 | |
849 | length = -EINVAL; | |
19439d05 | 850 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) |
b77a493b | 851 | goto out; |
1da177e4 | 852 | |
0619f0f5 | 853 | length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); |
b77a493b EP |
854 | if (length) |
855 | goto out; | |
856 | ||
0619f0f5 | 857 | length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); |
b77a493b EP |
858 | if (length) |
859 | goto out; | |
1da177e4 | 860 | |
0619f0f5 | 861 | security_compute_av_user(state, ssid, tsid, tclass, &avd); |
1da177e4 LT |
862 | |
863 | length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, | |
8a6f83af | 864 | "%x %x %x %x %u %x", |
f1c6381a | 865 | avd.allowed, 0xffffffff, |
1da177e4 | 866 | avd.auditallow, avd.auditdeny, |
8a6f83af | 867 | avd.seqno, avd.flags); |
1da177e4 | 868 | out: |
b77a493b | 869 | kfree(tcon); |
1da177e4 LT |
870 | kfree(scon); |
871 | return length; | |
872 | } | |
873 | ||
1872981b | 874 | static ssize_t sel_write_create(struct file *file, char *buf, size_t size) |
1da177e4 | 875 | { |
0619f0f5 SS |
876 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
877 | struct selinux_state *state = fsi->state; | |
b77a493b | 878 | char *scon = NULL, *tcon = NULL; |
f50a3ec9 | 879 | char *namebuf = NULL, *objname = NULL; |
1da177e4 LT |
880 | u32 ssid, tsid, newsid; |
881 | u16 tclass; | |
882 | ssize_t length; | |
b77a493b | 883 | char *newcon = NULL; |
1da177e4 | 884 | u32 len; |
f50a3ec9 | 885 | int nargs; |
1da177e4 | 886 | |
6b6bc620 SS |
887 | length = avc_has_perm(&selinux_state, |
888 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
889 | SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, |
890 | NULL); | |
1da177e4 | 891 | if (length) |
b77a493b | 892 | goto out; |
1da177e4 LT |
893 | |
894 | length = -ENOMEM; | |
c1a7368a | 895 | scon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 | 896 | if (!scon) |
b77a493b | 897 | goto out; |
1da177e4 | 898 | |
b77a493b | 899 | length = -ENOMEM; |
c1a7368a | 900 | tcon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 LT |
901 | if (!tcon) |
902 | goto out; | |
1da177e4 | 903 | |
f50a3ec9 KK |
904 | length = -ENOMEM; |
905 | namebuf = kzalloc(size + 1, GFP_KERNEL); | |
906 | if (!namebuf) | |
907 | goto out; | |
908 | ||
1da177e4 | 909 | length = -EINVAL; |
f50a3ec9 KK |
910 | nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf); |
911 | if (nargs < 3 || nargs > 4) | |
b77a493b | 912 | goto out; |
0f7e4c33 KK |
913 | if (nargs == 4) { |
914 | /* | |
915 | * If and when the name of new object to be queried contains | |
916 | * either whitespace or multibyte characters, they shall be | |
917 | * encoded based on the percentage-encoding rule. | |
918 | * If not encoded, the sscanf logic picks up only left-half | |
919 | * of the supplied name; splitted by a whitespace unexpectedly. | |
920 | */ | |
921 | char *r, *w; | |
922 | int c1, c2; | |
923 | ||
924 | r = w = namebuf; | |
925 | do { | |
926 | c1 = *r++; | |
927 | if (c1 == '+') | |
928 | c1 = ' '; | |
929 | else if (c1 == '%') { | |
af7ff2c2 AS |
930 | c1 = hex_to_bin(*r++); |
931 | if (c1 < 0) | |
0f7e4c33 | 932 | goto out; |
af7ff2c2 AS |
933 | c2 = hex_to_bin(*r++); |
934 | if (c2 < 0) | |
0f7e4c33 KK |
935 | goto out; |
936 | c1 = (c1 << 4) | c2; | |
937 | } | |
938 | *w++ = c1; | |
939 | } while (c1 != '\0'); | |
940 | ||
f50a3ec9 | 941 | objname = namebuf; |
0f7e4c33 | 942 | } |
1da177e4 | 943 | |
0619f0f5 | 944 | length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); |
b77a493b EP |
945 | if (length) |
946 | goto out; | |
947 | ||
0619f0f5 | 948 | length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); |
b77a493b EP |
949 | if (length) |
950 | goto out; | |
1da177e4 | 951 | |
0619f0f5 SS |
952 | length = security_transition_sid_user(state, ssid, tsid, tclass, |
953 | objname, &newsid); | |
b77a493b EP |
954 | if (length) |
955 | goto out; | |
1da177e4 | 956 | |
0619f0f5 | 957 | length = security_sid_to_context(state, newsid, &newcon, &len); |
b77a493b EP |
958 | if (length) |
959 | goto out; | |
1da177e4 | 960 | |
b77a493b | 961 | length = -ERANGE; |
1da177e4 | 962 | if (len > SIMPLE_TRANSACTION_LIMIT) { |
f8b69a5f | 963 | pr_err("SELinux: %s: context size (%u) exceeds " |
744ba35e | 964 | "payload max\n", __func__, len); |
b77a493b | 965 | goto out; |
1da177e4 LT |
966 | } |
967 | ||
968 | memcpy(buf, newcon, len); | |
969 | length = len; | |
b77a493b | 970 | out: |
1da177e4 | 971 | kfree(newcon); |
f50a3ec9 | 972 | kfree(namebuf); |
1da177e4 | 973 | kfree(tcon); |
1da177e4 LT |
974 | kfree(scon); |
975 | return length; | |
976 | } | |
977 | ||
1872981b | 978 | static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) |
1da177e4 | 979 | { |
0619f0f5 SS |
980 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
981 | struct selinux_state *state = fsi->state; | |
b77a493b | 982 | char *scon = NULL, *tcon = NULL; |
1da177e4 LT |
983 | u32 ssid, tsid, newsid; |
984 | u16 tclass; | |
985 | ssize_t length; | |
b77a493b | 986 | char *newcon = NULL; |
1da177e4 LT |
987 | u32 len; |
988 | ||
6b6bc620 SS |
989 | length = avc_has_perm(&selinux_state, |
990 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
991 | SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, |
992 | NULL); | |
1da177e4 | 993 | if (length) |
b77a493b | 994 | goto out; |
1da177e4 LT |
995 | |
996 | length = -ENOMEM; | |
c1a7368a | 997 | scon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 | 998 | if (!scon) |
b77a493b | 999 | goto out; |
1da177e4 | 1000 | |
b77a493b | 1001 | length = -ENOMEM; |
c1a7368a | 1002 | tcon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 LT |
1003 | if (!tcon) |
1004 | goto out; | |
1da177e4 LT |
1005 | |
1006 | length = -EINVAL; | |
1007 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | |
b77a493b | 1008 | goto out; |
1da177e4 | 1009 | |
0619f0f5 | 1010 | length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); |
b77a493b EP |
1011 | if (length) |
1012 | goto out; | |
1013 | ||
0619f0f5 | 1014 | length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); |
b77a493b EP |
1015 | if (length) |
1016 | goto out; | |
1da177e4 | 1017 | |
0619f0f5 | 1018 | length = security_change_sid(state, ssid, tsid, tclass, &newsid); |
b77a493b EP |
1019 | if (length) |
1020 | goto out; | |
1da177e4 | 1021 | |
0619f0f5 | 1022 | length = security_sid_to_context(state, newsid, &newcon, &len); |
b77a493b EP |
1023 | if (length) |
1024 | goto out; | |
1da177e4 | 1025 | |
b77a493b EP |
1026 | length = -ERANGE; |
1027 | if (len > SIMPLE_TRANSACTION_LIMIT) | |
1028 | goto out; | |
1da177e4 LT |
1029 | |
1030 | memcpy(buf, newcon, len); | |
1031 | length = len; | |
b77a493b | 1032 | out: |
1da177e4 | 1033 | kfree(newcon); |
1da177e4 | 1034 | kfree(tcon); |
1da177e4 LT |
1035 | kfree(scon); |
1036 | return length; | |
1037 | } | |
1038 | ||
1872981b | 1039 | static ssize_t sel_write_user(struct file *file, char *buf, size_t size) |
1da177e4 | 1040 | { |
0619f0f5 SS |
1041 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
1042 | struct selinux_state *state = fsi->state; | |
b77a493b EP |
1043 | char *con = NULL, *user = NULL, *ptr; |
1044 | u32 sid, *sids = NULL; | |
1da177e4 LT |
1045 | ssize_t length; |
1046 | char *newcon; | |
1047 | int i, rc; | |
1048 | u32 len, nsids; | |
1049 | ||
6b6bc620 SS |
1050 | length = avc_has_perm(&selinux_state, |
1051 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
1052 | SECCLASS_SECURITY, SECURITY__COMPUTE_USER, |
1053 | NULL); | |
1da177e4 | 1054 | if (length) |
6eab04a8 | 1055 | goto out; |
1da177e4 LT |
1056 | |
1057 | length = -ENOMEM; | |
c1a7368a | 1058 | con = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 | 1059 | if (!con) |
6eab04a8 | 1060 | goto out; |
1da177e4 | 1061 | |
b77a493b | 1062 | length = -ENOMEM; |
c1a7368a | 1063 | user = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 LT |
1064 | if (!user) |
1065 | goto out; | |
1da177e4 LT |
1066 | |
1067 | length = -EINVAL; | |
1068 | if (sscanf(buf, "%s %s", con, user) != 2) | |
b77a493b | 1069 | goto out; |
1da177e4 | 1070 | |
0619f0f5 | 1071 | length = security_context_str_to_sid(state, con, &sid, GFP_KERNEL); |
b77a493b EP |
1072 | if (length) |
1073 | goto out; | |
1da177e4 | 1074 | |
0619f0f5 | 1075 | length = security_get_user_sids(state, sid, user, &sids, &nsids); |
b77a493b EP |
1076 | if (length) |
1077 | goto out; | |
1da177e4 LT |
1078 | |
1079 | length = sprintf(buf, "%u", nsids) + 1; | |
1080 | ptr = buf + length; | |
1081 | for (i = 0; i < nsids; i++) { | |
0619f0f5 | 1082 | rc = security_sid_to_context(state, sids[i], &newcon, &len); |
1da177e4 LT |
1083 | if (rc) { |
1084 | length = rc; | |
b77a493b | 1085 | goto out; |
1da177e4 LT |
1086 | } |
1087 | if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { | |
1088 | kfree(newcon); | |
1089 | length = -ERANGE; | |
b77a493b | 1090 | goto out; |
1da177e4 LT |
1091 | } |
1092 | memcpy(ptr, newcon, len); | |
1093 | kfree(newcon); | |
1094 | ptr += len; | |
1095 | length += len; | |
1096 | } | |
b77a493b | 1097 | out: |
1da177e4 | 1098 | kfree(sids); |
1da177e4 | 1099 | kfree(user); |
1da177e4 LT |
1100 | kfree(con); |
1101 | return length; | |
1102 | } | |
1103 | ||
1872981b | 1104 | static ssize_t sel_write_member(struct file *file, char *buf, size_t size) |
1da177e4 | 1105 | { |
0619f0f5 SS |
1106 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
1107 | struct selinux_state *state = fsi->state; | |
b77a493b | 1108 | char *scon = NULL, *tcon = NULL; |
1da177e4 LT |
1109 | u32 ssid, tsid, newsid; |
1110 | u16 tclass; | |
1111 | ssize_t length; | |
b77a493b | 1112 | char *newcon = NULL; |
1da177e4 LT |
1113 | u32 len; |
1114 | ||
6b6bc620 SS |
1115 | length = avc_has_perm(&selinux_state, |
1116 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
1117 | SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, |
1118 | NULL); | |
1da177e4 | 1119 | if (length) |
b77a493b | 1120 | goto out; |
1da177e4 LT |
1121 | |
1122 | length = -ENOMEM; | |
c1a7368a | 1123 | scon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 | 1124 | if (!scon) |
6eab04a8 | 1125 | goto out; |
1da177e4 | 1126 | |
b77a493b | 1127 | length = -ENOMEM; |
c1a7368a | 1128 | tcon = kzalloc(size + 1, GFP_KERNEL); |
1da177e4 LT |
1129 | if (!tcon) |
1130 | goto out; | |
1da177e4 LT |
1131 | |
1132 | length = -EINVAL; | |
1133 | if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) | |
b77a493b | 1134 | goto out; |
1da177e4 | 1135 | |
0619f0f5 | 1136 | length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); |
b77a493b EP |
1137 | if (length) |
1138 | goto out; | |
1139 | ||
0619f0f5 | 1140 | length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); |
b77a493b EP |
1141 | if (length) |
1142 | goto out; | |
1da177e4 | 1143 | |
0619f0f5 | 1144 | length = security_member_sid(state, ssid, tsid, tclass, &newsid); |
b77a493b EP |
1145 | if (length) |
1146 | goto out; | |
1da177e4 | 1147 | |
0619f0f5 | 1148 | length = security_sid_to_context(state, newsid, &newcon, &len); |
b77a493b EP |
1149 | if (length) |
1150 | goto out; | |
1da177e4 | 1151 | |
b77a493b | 1152 | length = -ERANGE; |
1da177e4 | 1153 | if (len > SIMPLE_TRANSACTION_LIMIT) { |
f8b69a5f | 1154 | pr_err("SELinux: %s: context size (%u) exceeds " |
744ba35e | 1155 | "payload max\n", __func__, len); |
b77a493b | 1156 | goto out; |
1da177e4 LT |
1157 | } |
1158 | ||
1159 | memcpy(buf, newcon, len); | |
1160 | length = len; | |
b77a493b | 1161 | out: |
1da177e4 | 1162 | kfree(newcon); |
1da177e4 | 1163 | kfree(tcon); |
1da177e4 LT |
1164 | kfree(scon); |
1165 | return length; | |
1166 | } | |
1167 | ||
1168 | static struct inode *sel_make_inode(struct super_block *sb, int mode) | |
1169 | { | |
1170 | struct inode *ret = new_inode(sb); | |
1171 | ||
1172 | if (ret) { | |
1173 | ret->i_mode = mode; | |
078cd827 | 1174 | ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); |
1da177e4 LT |
1175 | } |
1176 | return ret; | |
1177 | } | |
1178 | ||
1da177e4 LT |
1179 | static ssize_t sel_read_bool(struct file *filep, char __user *buf, |
1180 | size_t count, loff_t *ppos) | |
1181 | { | |
0619f0f5 | 1182 | struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; |
1da177e4 LT |
1183 | char *page = NULL; |
1184 | ssize_t length; | |
1da177e4 LT |
1185 | ssize_t ret; |
1186 | int cur_enforcing; | |
496ad9aa | 1187 | unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; |
d313f948 | 1188 | const char *name = filep->f_path.dentry->d_name.name; |
1da177e4 | 1189 | |
0619f0f5 | 1190 | mutex_lock(&fsi->mutex); |
1da177e4 | 1191 | |
b77a493b | 1192 | ret = -EINVAL; |
0619f0f5 SS |
1193 | if (index >= fsi->bool_num || strcmp(name, |
1194 | fsi->bool_pending_names[index])) | |
0da74120 | 1195 | goto out_unlock; |
1da177e4 | 1196 | |
b77a493b | 1197 | ret = -ENOMEM; |
1872981b | 1198 | page = (char *)get_zeroed_page(GFP_KERNEL); |
b77a493b | 1199 | if (!page) |
0da74120 | 1200 | goto out_unlock; |
1da177e4 | 1201 | |
0619f0f5 | 1202 | cur_enforcing = security_get_bool_value(fsi->state, index); |
1da177e4 LT |
1203 | if (cur_enforcing < 0) { |
1204 | ret = cur_enforcing; | |
0da74120 | 1205 | goto out_unlock; |
1da177e4 | 1206 | } |
1da177e4 | 1207 | length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, |
0619f0f5 | 1208 | fsi->bool_pending_values[index]); |
0619f0f5 | 1209 | mutex_unlock(&fsi->mutex); |
0da74120 JH |
1210 | ret = simple_read_from_buffer(buf, count, ppos, page, length); |
1211 | out_free: | |
b77a493b | 1212 | free_page((unsigned long)page); |
1da177e4 | 1213 | return ret; |
0da74120 JH |
1214 | |
1215 | out_unlock: | |
1216 | mutex_unlock(&fsi->mutex); | |
1217 | goto out_free; | |
1da177e4 LT |
1218 | } |
1219 | ||
1220 | static ssize_t sel_write_bool(struct file *filep, const char __user *buf, | |
1221 | size_t count, loff_t *ppos) | |
1222 | { | |
0619f0f5 | 1223 | struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; |
1da177e4 | 1224 | char *page = NULL; |
d313f948 | 1225 | ssize_t length; |
1da177e4 | 1226 | int new_value; |
496ad9aa | 1227 | unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; |
d313f948 | 1228 | const char *name = filep->f_path.dentry->d_name.name; |
1da177e4 | 1229 | |
0da74120 JH |
1230 | if (count >= PAGE_SIZE) |
1231 | return -ENOMEM; | |
1232 | ||
1233 | /* No partial writes. */ | |
1234 | if (*ppos != 0) | |
1235 | return -EINVAL; | |
1236 | ||
1237 | page = memdup_user_nul(buf, count); | |
1238 | if (IS_ERR(page)) | |
1239 | return PTR_ERR(page); | |
1240 | ||
0619f0f5 | 1241 | mutex_lock(&fsi->mutex); |
1da177e4 | 1242 | |
6b6bc620 SS |
1243 | length = avc_has_perm(&selinux_state, |
1244 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
1245 | SECCLASS_SECURITY, SECURITY__SETBOOL, |
1246 | NULL); | |
1da177e4 LT |
1247 | if (length) |
1248 | goto out; | |
1249 | ||
b77a493b | 1250 | length = -EINVAL; |
0619f0f5 SS |
1251 | if (index >= fsi->bool_num || strcmp(name, |
1252 | fsi->bool_pending_names[index])) | |
d313f948 | 1253 | goto out; |
d313f948 | 1254 | |
1da177e4 LT |
1255 | length = -EINVAL; |
1256 | if (sscanf(page, "%d", &new_value) != 1) | |
1257 | goto out; | |
1258 | ||
1259 | if (new_value) | |
1260 | new_value = 1; | |
1261 | ||
0619f0f5 | 1262 | fsi->bool_pending_values[index] = new_value; |
1da177e4 LT |
1263 | length = count; |
1264 | ||
1265 | out: | |
0619f0f5 | 1266 | mutex_unlock(&fsi->mutex); |
8365a719 | 1267 | kfree(page); |
1da177e4 LT |
1268 | return length; |
1269 | } | |
1270 | ||
9c2e08c5 | 1271 | static const struct file_operations sel_bool_ops = { |
1872981b EP |
1272 | .read = sel_read_bool, |
1273 | .write = sel_write_bool, | |
57a62c23 | 1274 | .llseek = generic_file_llseek, |
1da177e4 LT |
1275 | }; |
1276 | ||
1277 | static ssize_t sel_commit_bools_write(struct file *filep, | |
1278 | const char __user *buf, | |
1279 | size_t count, loff_t *ppos) | |
1280 | { | |
0619f0f5 | 1281 | struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; |
1da177e4 | 1282 | char *page = NULL; |
d313f948 | 1283 | ssize_t length; |
1da177e4 LT |
1284 | int new_value; |
1285 | ||
0da74120 JH |
1286 | if (count >= PAGE_SIZE) |
1287 | return -ENOMEM; | |
1288 | ||
1289 | /* No partial writes. */ | |
1290 | if (*ppos != 0) | |
1291 | return -EINVAL; | |
1292 | ||
1293 | page = memdup_user_nul(buf, count); | |
1294 | if (IS_ERR(page)) | |
1295 | return PTR_ERR(page); | |
1296 | ||
0619f0f5 | 1297 | mutex_lock(&fsi->mutex); |
1da177e4 | 1298 | |
6b6bc620 SS |
1299 | length = avc_has_perm(&selinux_state, |
1300 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
1301 | SECCLASS_SECURITY, SECURITY__SETBOOL, |
1302 | NULL); | |
1da177e4 LT |
1303 | if (length) |
1304 | goto out; | |
1305 | ||
1da177e4 LT |
1306 | length = -EINVAL; |
1307 | if (sscanf(page, "%d", &new_value) != 1) | |
1308 | goto out; | |
1309 | ||
b77a493b | 1310 | length = 0; |
0619f0f5 SS |
1311 | if (new_value && fsi->bool_pending_values) |
1312 | length = security_set_bools(fsi->state, fsi->bool_num, | |
1313 | fsi->bool_pending_values); | |
1da177e4 | 1314 | |
b77a493b EP |
1315 | if (!length) |
1316 | length = count; | |
1da177e4 LT |
1317 | |
1318 | out: | |
0619f0f5 | 1319 | mutex_unlock(&fsi->mutex); |
8365a719 | 1320 | kfree(page); |
1da177e4 LT |
1321 | return length; |
1322 | } | |
1323 | ||
9c2e08c5 | 1324 | static const struct file_operations sel_commit_bools_ops = { |
1872981b | 1325 | .write = sel_commit_bools_write, |
57a62c23 | 1326 | .llseek = generic_file_llseek, |
1da177e4 LT |
1327 | }; |
1328 | ||
0c92d7c7 | 1329 | static void sel_remove_entries(struct dentry *de) |
1da177e4 | 1330 | { |
ad52184b AV |
1331 | d_genocide(de); |
1332 | shrink_dcache_parent(de); | |
1da177e4 LT |
1333 | } |
1334 | ||
1335 | #define BOOL_DIR_NAME "booleans" | |
1336 | ||
02a52c5c SS |
1337 | static int sel_make_bools(struct selinux_fs_info *fsi, |
1338 | struct selinux_policy *newpolicy) | |
1da177e4 | 1339 | { |
60abd318 | 1340 | int ret; |
1da177e4 LT |
1341 | ssize_t len; |
1342 | struct dentry *dentry = NULL; | |
0619f0f5 | 1343 | struct dentry *dir = fsi->bool_dir; |
1da177e4 LT |
1344 | struct inode *inode = NULL; |
1345 | struct inode_security_struct *isec; | |
1346 | char **names = NULL, *page; | |
60abd318 | 1347 | u32 i, num; |
1da177e4 LT |
1348 | int *values = NULL; |
1349 | u32 sid; | |
1350 | ||
1351 | /* remove any existing files */ | |
0619f0f5 SS |
1352 | for (i = 0; i < fsi->bool_num; i++) |
1353 | kfree(fsi->bool_pending_names[i]); | |
1354 | kfree(fsi->bool_pending_names); | |
1355 | kfree(fsi->bool_pending_values); | |
1356 | fsi->bool_num = 0; | |
1357 | fsi->bool_pending_names = NULL; | |
1358 | fsi->bool_pending_values = NULL; | |
1da177e4 | 1359 | |
0c92d7c7 | 1360 | sel_remove_entries(dir); |
1da177e4 | 1361 | |
b77a493b | 1362 | ret = -ENOMEM; |
1872981b EP |
1363 | page = (char *)get_zeroed_page(GFP_KERNEL); |
1364 | if (!page) | |
b77a493b | 1365 | goto out; |
1da177e4 | 1366 | |
02a52c5c | 1367 | ret = security_get_bools(newpolicy, &num, &names, &values); |
b77a493b | 1368 | if (ret) |
1da177e4 LT |
1369 | goto out; |
1370 | ||
1371 | for (i = 0; i < num; i++) { | |
b77a493b | 1372 | ret = -ENOMEM; |
1da177e4 | 1373 | dentry = d_alloc_name(dir, names[i]); |
b77a493b EP |
1374 | if (!dentry) |
1375 | goto out; | |
1376 | ||
1377 | ret = -ENOMEM; | |
1da177e4 | 1378 | inode = sel_make_inode(dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); |
7e4237fa | 1379 | if (!inode) { |
1380 | dput(dentry); | |
b77a493b | 1381 | goto out; |
7e4237fa | 1382 | } |
1da177e4 | 1383 | |
b77a493b | 1384 | ret = -ENAMETOOLONG; |
cc1dad71 | 1385 | len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); |
7e4237fa | 1386 | if (len >= PAGE_SIZE) { |
1387 | dput(dentry); | |
1388 | iput(inode); | |
b77a493b | 1389 | goto out; |
7e4237fa | 1390 | } |
b77a493b | 1391 | |
80788c22 | 1392 | isec = selinux_inode(inode); |
02a52c5c | 1393 | ret = selinux_policy_genfs_sid(newpolicy, "selinuxfs", page, |
aa8e712c | 1394 | SECCLASS_FILE, &sid); |
4262fb51 | 1395 | if (ret) { |
900fde06 GT |
1396 | pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n", |
1397 | page); | |
1398 | sid = SECINITSID_SECURITY; | |
4262fb51 GT |
1399 | } |
1400 | ||
1da177e4 | 1401 | isec->sid = sid; |
42059112 | 1402 | isec->initialized = LABEL_INITIALIZED; |
1da177e4 | 1403 | inode->i_fop = &sel_bool_ops; |
bce34bc0 | 1404 | inode->i_ino = i|SEL_BOOL_INO_OFFSET; |
1da177e4 LT |
1405 | d_add(dentry, inode); |
1406 | } | |
0619f0f5 SS |
1407 | fsi->bool_num = num; |
1408 | fsi->bool_pending_names = names; | |
1409 | fsi->bool_pending_values = values; | |
b77a493b EP |
1410 | |
1411 | free_page((unsigned long)page); | |
1412 | return 0; | |
1da177e4 LT |
1413 | out: |
1414 | free_page((unsigned long)page); | |
b77a493b | 1415 | |
1da177e4 | 1416 | if (names) { |
9a5f04bf JJ |
1417 | for (i = 0; i < num; i++) |
1418 | kfree(names[i]); | |
1da177e4 LT |
1419 | kfree(names); |
1420 | } | |
20c19e41 | 1421 | kfree(values); |
0c92d7c7 | 1422 | sel_remove_entries(dir); |
b77a493b EP |
1423 | |
1424 | return ret; | |
1da177e4 LT |
1425 | } |
1426 | ||
1da177e4 LT |
1427 | static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, |
1428 | size_t count, loff_t *ppos) | |
1429 | { | |
6b6bc620 SS |
1430 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
1431 | struct selinux_state *state = fsi->state; | |
1da177e4 LT |
1432 | char tmpbuf[TMPBUFLEN]; |
1433 | ssize_t length; | |
1434 | ||
6b6bc620 SS |
1435 | length = scnprintf(tmpbuf, TMPBUFLEN, "%u", |
1436 | avc_get_cache_threshold(state->avc)); | |
1da177e4 LT |
1437 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); |
1438 | } | |
1439 | ||
1872981b EP |
1440 | static ssize_t sel_write_avc_cache_threshold(struct file *file, |
1441 | const char __user *buf, | |
1da177e4 LT |
1442 | size_t count, loff_t *ppos) |
1443 | ||
1444 | { | |
6b6bc620 SS |
1445 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
1446 | struct selinux_state *state = fsi->state; | |
8365a719 | 1447 | char *page; |
1da177e4 | 1448 | ssize_t ret; |
309c5fad | 1449 | unsigned int new_value; |
1da177e4 | 1450 | |
6b6bc620 SS |
1451 | ret = avc_has_perm(&selinux_state, |
1452 | current_sid(), SECINITSID_SECURITY, | |
be0554c9 SS |
1453 | SECCLASS_SECURITY, SECURITY__SETSECPARAM, |
1454 | NULL); | |
b77a493b | 1455 | if (ret) |
8365a719 | 1456 | return ret; |
1da177e4 | 1457 | |
b77a493b | 1458 | if (count >= PAGE_SIZE) |
8365a719 | 1459 | return -ENOMEM; |
1da177e4 | 1460 | |
b77a493b | 1461 | /* No partial writes. */ |
b77a493b | 1462 | if (*ppos != 0) |
8365a719 | 1463 | return -EINVAL; |
1da177e4 | 1464 | |
8365a719 AV |
1465 | page = memdup_user_nul(buf, count); |
1466 | if (IS_ERR(page)) | |
1467 | return PTR_ERR(page); | |
1da177e4 | 1468 | |
b77a493b EP |
1469 | ret = -EINVAL; |
1470 | if (sscanf(page, "%u", &new_value) != 1) | |
1da177e4 | 1471 | goto out; |
1da177e4 | 1472 | |
6b6bc620 | 1473 | avc_set_cache_threshold(state->avc, new_value); |
b77a493b | 1474 | |
1da177e4 | 1475 | ret = count; |
1da177e4 | 1476 | out: |
8365a719 | 1477 | kfree(page); |
1da177e4 LT |
1478 | return ret; |
1479 | } | |
1480 | ||
1481 | static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, | |
1482 | size_t count, loff_t *ppos) | |
1483 | { | |
6b6bc620 SS |
1484 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; |
1485 | struct selinux_state *state = fsi->state; | |
1da177e4 | 1486 | char *page; |
b77a493b | 1487 | ssize_t length; |
1da177e4 LT |
1488 | |
1489 | page = (char *)__get_free_page(GFP_KERNEL); | |
b77a493b EP |
1490 | if (!page) |
1491 | return -ENOMEM; | |
1492 | ||
6b6bc620 | 1493 | length = avc_get_hash_stats(state->avc, page); |
b77a493b EP |
1494 | if (length >= 0) |
1495 | length = simple_read_from_buffer(buf, count, ppos, page, length); | |
1da177e4 | 1496 | free_page((unsigned long)page); |
b77a493b EP |
1497 | |
1498 | return length; | |
1da177e4 LT |
1499 | } |
1500 | ||
66f8e2f0 JVS |
1501 | static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf, |
1502 | size_t count, loff_t *ppos) | |
1503 | { | |
1504 | struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; | |
1505 | struct selinux_state *state = fsi->state; | |
1506 | char *page; | |
1507 | ssize_t length; | |
1508 | ||
1509 | page = (char *)__get_free_page(GFP_KERNEL); | |
1510 | if (!page) | |
1511 | return -ENOMEM; | |
1512 | ||
1513 | length = security_sidtab_hash_stats(state, page); | |
1514 | if (length >= 0) | |
1515 | length = simple_read_from_buffer(buf, count, ppos, page, | |
1516 | length); | |
1517 | free_page((unsigned long)page); | |
1518 | ||
1519 | return length; | |
1520 | } | |
1521 | ||
1522 | static const struct file_operations sel_sidtab_hash_stats_ops = { | |
1523 | .read = sel_read_sidtab_hash_stats, | |
1524 | .llseek = generic_file_llseek, | |
1525 | }; | |
1526 | ||
9c2e08c5 | 1527 | static const struct file_operations sel_avc_cache_threshold_ops = { |
1da177e4 LT |
1528 | .read = sel_read_avc_cache_threshold, |
1529 | .write = sel_write_avc_cache_threshold, | |
57a62c23 | 1530 | .llseek = generic_file_llseek, |
1da177e4 LT |
1531 | }; |
1532 | ||
9c2e08c5 | 1533 | static const struct file_operations sel_avc_hash_stats_ops = { |
1da177e4 | 1534 | .read = sel_read_avc_hash_stats, |
57a62c23 | 1535 | .llseek = generic_file_llseek, |
1da177e4 LT |
1536 | }; |
1537 | ||
1538 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | |
1539 | static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) | |
1540 | { | |
1541 | int cpu; | |
1542 | ||
4f4b6c1a | 1543 | for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) { |
1da177e4 LT |
1544 | if (!cpu_possible(cpu)) |
1545 | continue; | |
1546 | *idx = cpu + 1; | |
1547 | return &per_cpu(avc_cache_stats, cpu); | |
1548 | } | |
8d269a8e | 1549 | (*idx)++; |
1da177e4 LT |
1550 | return NULL; |
1551 | } | |
1552 | ||
1553 | static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos) | |
1554 | { | |
1555 | loff_t n = *pos - 1; | |
1556 | ||
1557 | if (*pos == 0) | |
1558 | return SEQ_START_TOKEN; | |
1559 | ||
1560 | return sel_avc_get_stat_idx(&n); | |
1561 | } | |
1562 | ||
1563 | static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |
1564 | { | |
1565 | return sel_avc_get_stat_idx(pos); | |
1566 | } | |
1567 | ||
1568 | static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) | |
1569 | { | |
1570 | struct avc_cache_stats *st = v; | |
1571 | ||
710a0647 ME |
1572 | if (v == SEQ_START_TOKEN) { |
1573 | seq_puts(seq, | |
1574 | "lookups hits misses allocations reclaims frees\n"); | |
1575 | } else { | |
257313b2 LT |
1576 | unsigned int lookups = st->lookups; |
1577 | unsigned int misses = st->misses; | |
1578 | unsigned int hits = lookups - misses; | |
1579 | seq_printf(seq, "%u %u %u %u %u %u\n", lookups, | |
1580 | hits, misses, st->allocations, | |
1da177e4 | 1581 | st->reclaims, st->frees); |
257313b2 | 1582 | } |
1da177e4 LT |
1583 | return 0; |
1584 | } | |
1585 | ||
1586 | static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) | |
1587 | { } | |
1588 | ||
1996a109 | 1589 | static const struct seq_operations sel_avc_cache_stats_seq_ops = { |
1da177e4 LT |
1590 | .start = sel_avc_stats_seq_start, |
1591 | .next = sel_avc_stats_seq_next, | |
1592 | .show = sel_avc_stats_seq_show, | |
1593 | .stop = sel_avc_stats_seq_stop, | |
1594 | }; | |
1595 | ||
1596 | static int sel_open_avc_cache_stats(struct inode *inode, struct file *file) | |
1597 | { | |
1598 | return seq_open(file, &sel_avc_cache_stats_seq_ops); | |
1599 | } | |
1600 | ||
9c2e08c5 | 1601 | static const struct file_operations sel_avc_cache_stats_ops = { |
1da177e4 LT |
1602 | .open = sel_open_avc_cache_stats, |
1603 | .read = seq_read, | |
1604 | .llseek = seq_lseek, | |
1605 | .release = seq_release, | |
1606 | }; | |
1607 | #endif | |
1608 | ||
1609 | static int sel_make_avc_files(struct dentry *dir) | |
1610 | { | |
0619f0f5 SS |
1611 | struct super_block *sb = dir->d_sb; |
1612 | struct selinux_fs_info *fsi = sb->s_fs_info; | |
b77a493b | 1613 | int i; |
cda37124 | 1614 | static const struct tree_descr files[] = { |
1da177e4 LT |
1615 | { "cache_threshold", |
1616 | &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, | |
1617 | { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO }, | |
1618 | #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS | |
1619 | { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO }, | |
1620 | #endif | |
1621 | }; | |
1622 | ||
6e20a64a | 1623 | for (i = 0; i < ARRAY_SIZE(files); i++) { |
1da177e4 LT |
1624 | struct inode *inode; |
1625 | struct dentry *dentry; | |
1626 | ||
1627 | dentry = d_alloc_name(dir, files[i].name); | |
b77a493b EP |
1628 | if (!dentry) |
1629 | return -ENOMEM; | |
1da177e4 LT |
1630 | |
1631 | inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); | |
7e4237fa | 1632 | if (!inode) { |
1633 | dput(dentry); | |
b77a493b | 1634 | return -ENOMEM; |
7e4237fa | 1635 | } |
b77a493b | 1636 | |
1da177e4 | 1637 | inode->i_fop = files[i].ops; |
0619f0f5 | 1638 | inode->i_ino = ++fsi->last_ino; |
1da177e4 LT |
1639 | d_add(dentry, inode); |
1640 | } | |
b77a493b EP |
1641 | |
1642 | return 0; | |
1da177e4 LT |
1643 | } |
1644 | ||
66f8e2f0 JVS |
1645 | static int sel_make_ss_files(struct dentry *dir) |
1646 | { | |
1647 | struct super_block *sb = dir->d_sb; | |
1648 | struct selinux_fs_info *fsi = sb->s_fs_info; | |
1649 | int i; | |
1650 | static struct tree_descr files[] = { | |
1651 | { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO }, | |
1652 | }; | |
1653 | ||
1654 | for (i = 0; i < ARRAY_SIZE(files); i++) { | |
1655 | struct inode *inode; | |
1656 | struct dentry *dentry; | |
1657 | ||
1658 | dentry = d_alloc_name(dir, files[i].name); | |
1659 | if (!dentry) | |
1660 | return -ENOMEM; | |
1661 | ||
1662 | inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); | |
1663 | if (!inode) { | |
1664 | dput(dentry); | |
1665 | return -ENOMEM; | |
1666 | } | |
1667 | ||
1668 | inode->i_fop = files[i].ops; | |
1669 | inode->i_ino = ++fsi->last_ino; | |
1670 | d_add(dentry, inode); | |
1671 | } | |
1672 | ||
1673 | return 0; | |
1674 | } | |
1675 | ||
1872981b | 1676 | static ssize_t sel_read_initcon(struct file *file, char __user *buf, |
f0ee2e46 JC |
1677 | size_t count, loff_t *ppos) |
1678 | { | |
0619f0f5 | 1679 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
f0ee2e46 JC |
1680 | char *con; |
1681 | u32 sid, len; | |
1682 | ssize_t ret; | |
1683 | ||
496ad9aa | 1684 | sid = file_inode(file)->i_ino&SEL_INO_MASK; |
0619f0f5 | 1685 | ret = security_sid_to_context(fsi->state, sid, &con, &len); |
b77a493b | 1686 | if (ret) |
f0ee2e46 JC |
1687 | return ret; |
1688 | ||
1689 | ret = simple_read_from_buffer(buf, count, ppos, con, len); | |
1690 | kfree(con); | |
1691 | return ret; | |
1692 | } | |
1693 | ||
1694 | static const struct file_operations sel_initcon_ops = { | |
1695 | .read = sel_read_initcon, | |
57a62c23 | 1696 | .llseek = generic_file_llseek, |
f0ee2e46 JC |
1697 | }; |
1698 | ||
1699 | static int sel_make_initcon_files(struct dentry *dir) | |
1700 | { | |
b77a493b | 1701 | int i; |
f0ee2e46 JC |
1702 | |
1703 | for (i = 1; i <= SECINITSID_NUM; i++) { | |
1704 | struct inode *inode; | |
1705 | struct dentry *dentry; | |
e3e0b582 SS |
1706 | const char *s = security_get_initial_sid_context(i); |
1707 | ||
1708 | if (!s) | |
1709 | continue; | |
1710 | dentry = d_alloc_name(dir, s); | |
b77a493b EP |
1711 | if (!dentry) |
1712 | return -ENOMEM; | |
f0ee2e46 JC |
1713 | |
1714 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); | |
7e4237fa | 1715 | if (!inode) { |
1716 | dput(dentry); | |
b77a493b | 1717 | return -ENOMEM; |
7e4237fa | 1718 | } |
b77a493b | 1719 | |
f0ee2e46 JC |
1720 | inode->i_fop = &sel_initcon_ops; |
1721 | inode->i_ino = i|SEL_INITCON_INO_OFFSET; | |
1722 | d_add(dentry, inode); | |
1723 | } | |
b77a493b EP |
1724 | |
1725 | return 0; | |
f0ee2e46 JC |
1726 | } |
1727 | ||
e47c8fc5 CP |
1728 | static inline unsigned long sel_class_to_ino(u16 class) |
1729 | { | |
1730 | return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; | |
1731 | } | |
1732 | ||
1733 | static inline u16 sel_ino_to_class(unsigned long ino) | |
1734 | { | |
92ae9e82 | 1735 | return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1); |
e47c8fc5 CP |
1736 | } |
1737 | ||
1738 | static inline unsigned long sel_perm_to_ino(u16 class, u32 perm) | |
1739 | { | |
1740 | return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET; | |
1741 | } | |
1742 | ||
1743 | static inline u32 sel_ino_to_perm(unsigned long ino) | |
1744 | { | |
1745 | return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); | |
1746 | } | |
1747 | ||
1872981b | 1748 | static ssize_t sel_read_class(struct file *file, char __user *buf, |
e47c8fc5 CP |
1749 | size_t count, loff_t *ppos) |
1750 | { | |
496ad9aa | 1751 | unsigned long ino = file_inode(file)->i_ino; |
cc1dad71 | 1752 | char res[TMPBUFLEN]; |
7e78c875 | 1753 | ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_class(ino)); |
cc1dad71 | 1754 | return simple_read_from_buffer(buf, count, ppos, res, len); |
e47c8fc5 CP |
1755 | } |
1756 | ||
1757 | static const struct file_operations sel_class_ops = { | |
1758 | .read = sel_read_class, | |
57a62c23 | 1759 | .llseek = generic_file_llseek, |
e47c8fc5 CP |
1760 | }; |
1761 | ||
1872981b | 1762 | static ssize_t sel_read_perm(struct file *file, char __user *buf, |
e47c8fc5 CP |
1763 | size_t count, loff_t *ppos) |
1764 | { | |
496ad9aa | 1765 | unsigned long ino = file_inode(file)->i_ino; |
cc1dad71 | 1766 | char res[TMPBUFLEN]; |
7e78c875 | 1767 | ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino)); |
cc1dad71 | 1768 | return simple_read_from_buffer(buf, count, ppos, res, len); |
e47c8fc5 CP |
1769 | } |
1770 | ||
1771 | static const struct file_operations sel_perm_ops = { | |
1772 | .read = sel_read_perm, | |
57a62c23 | 1773 | .llseek = generic_file_llseek, |
e47c8fc5 CP |
1774 | }; |
1775 | ||
3bb56b25 PM |
1776 | static ssize_t sel_read_policycap(struct file *file, char __user *buf, |
1777 | size_t count, loff_t *ppos) | |
1778 | { | |
0619f0f5 | 1779 | struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; |
3bb56b25 PM |
1780 | int value; |
1781 | char tmpbuf[TMPBUFLEN]; | |
1782 | ssize_t length; | |
496ad9aa | 1783 | unsigned long i_ino = file_inode(file)->i_ino; |
3bb56b25 | 1784 | |
0619f0f5 | 1785 | value = security_policycap_supported(fsi->state, i_ino & SEL_INO_MASK); |
3bb56b25 PM |
1786 | length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); |
1787 | ||
1788 | return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); | |
1789 | } | |
1790 | ||
1791 | static const struct file_operations sel_policycap_ops = { | |
1792 | .read = sel_read_policycap, | |
57a62c23 | 1793 | .llseek = generic_file_llseek, |
3bb56b25 PM |
1794 | }; |
1795 | ||
02a52c5c SS |
1796 | static int sel_make_perm_files(struct selinux_policy *newpolicy, |
1797 | char *objclass, int classvalue, | |
1798 | struct dentry *dir) | |
e47c8fc5 | 1799 | { |
b77a493b | 1800 | int i, rc, nperms; |
e47c8fc5 CP |
1801 | char **perms; |
1802 | ||
02a52c5c | 1803 | rc = security_get_permissions(newpolicy, objclass, &perms, &nperms); |
e47c8fc5 | 1804 | if (rc) |
b77a493b | 1805 | return rc; |
e47c8fc5 CP |
1806 | |
1807 | for (i = 0; i < nperms; i++) { | |
1808 | struct inode *inode; | |
1809 | struct dentry *dentry; | |
1810 | ||
b77a493b | 1811 | rc = -ENOMEM; |
e47c8fc5 | 1812 | dentry = d_alloc_name(dir, perms[i]); |
b77a493b EP |
1813 | if (!dentry) |
1814 | goto out; | |
e47c8fc5 | 1815 | |
b77a493b | 1816 | rc = -ENOMEM; |
e47c8fc5 | 1817 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); |
7e4237fa | 1818 | if (!inode) { |
1819 | dput(dentry); | |
b77a493b | 1820 | goto out; |
7e4237fa | 1821 | } |
b77a493b | 1822 | |
e47c8fc5 CP |
1823 | inode->i_fop = &sel_perm_ops; |
1824 | /* i+1 since perm values are 1-indexed */ | |
c1a7368a | 1825 | inode->i_ino = sel_perm_to_ino(classvalue, i + 1); |
e47c8fc5 CP |
1826 | d_add(dentry, inode); |
1827 | } | |
b77a493b EP |
1828 | rc = 0; |
1829 | out: | |
e47c8fc5 CP |
1830 | for (i = 0; i < nperms; i++) |
1831 | kfree(perms[i]); | |
1832 | kfree(perms); | |
e47c8fc5 CP |
1833 | return rc; |
1834 | } | |
1835 | ||
02a52c5c SS |
1836 | static int sel_make_class_dir_entries(struct selinux_policy *newpolicy, |
1837 | char *classname, int index, | |
1838 | struct dentry *dir) | |
e47c8fc5 | 1839 | { |
0619f0f5 SS |
1840 | struct super_block *sb = dir->d_sb; |
1841 | struct selinux_fs_info *fsi = sb->s_fs_info; | |
e47c8fc5 CP |
1842 | struct dentry *dentry = NULL; |
1843 | struct inode *inode = NULL; | |
1844 | int rc; | |
1845 | ||
1846 | dentry = d_alloc_name(dir, "index"); | |
b77a493b EP |
1847 | if (!dentry) |
1848 | return -ENOMEM; | |
e47c8fc5 CP |
1849 | |
1850 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); | |
7e4237fa | 1851 | if (!inode) { |
1852 | dput(dentry); | |
b77a493b | 1853 | return -ENOMEM; |
7e4237fa | 1854 | } |
e47c8fc5 CP |
1855 | |
1856 | inode->i_fop = &sel_class_ops; | |
1857 | inode->i_ino = sel_class_to_ino(index); | |
1858 | d_add(dentry, inode); | |
1859 | ||
0619f0f5 | 1860 | dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino); |
a1c2aa1e AV |
1861 | if (IS_ERR(dentry)) |
1862 | return PTR_ERR(dentry); | |
e47c8fc5 | 1863 | |
02a52c5c | 1864 | rc = sel_make_perm_files(newpolicy, classname, index, dentry); |
e47c8fc5 | 1865 | |
e47c8fc5 CP |
1866 | return rc; |
1867 | } | |
1868 | ||
02a52c5c SS |
1869 | static int sel_make_classes(struct selinux_fs_info *fsi, |
1870 | struct selinux_policy *newpolicy) | |
e47c8fc5 | 1871 | { |
0619f0f5 | 1872 | |
b77a493b | 1873 | int rc, nclasses, i; |
e47c8fc5 CP |
1874 | char **classes; |
1875 | ||
1876 | /* delete any existing entries */ | |
0619f0f5 | 1877 | sel_remove_entries(fsi->class_dir); |
e47c8fc5 | 1878 | |
02a52c5c | 1879 | rc = security_get_classes(newpolicy, &classes, &nclasses); |
b77a493b EP |
1880 | if (rc) |
1881 | return rc; | |
e47c8fc5 CP |
1882 | |
1883 | /* +2 since classes are 1-indexed */ | |
0619f0f5 | 1884 | fsi->last_class_ino = sel_class_to_ino(nclasses + 2); |
e47c8fc5 CP |
1885 | |
1886 | for (i = 0; i < nclasses; i++) { | |
1887 | struct dentry *class_name_dir; | |
1888 | ||
0619f0f5 SS |
1889 | class_name_dir = sel_make_dir(fsi->class_dir, classes[i], |
1890 | &fsi->last_class_ino); | |
a1c2aa1e AV |
1891 | if (IS_ERR(class_name_dir)) { |
1892 | rc = PTR_ERR(class_name_dir); | |
b77a493b | 1893 | goto out; |
a1c2aa1e | 1894 | } |
e47c8fc5 CP |
1895 | |
1896 | /* i+1 since class values are 1-indexed */ | |
02a52c5c | 1897 | rc = sel_make_class_dir_entries(newpolicy, classes[i], i + 1, |
e47c8fc5 CP |
1898 | class_name_dir); |
1899 | if (rc) | |
b77a493b | 1900 | goto out; |
e47c8fc5 | 1901 | } |
b77a493b EP |
1902 | rc = 0; |
1903 | out: | |
e47c8fc5 CP |
1904 | for (i = 0; i < nclasses; i++) |
1905 | kfree(classes[i]); | |
1906 | kfree(classes); | |
e47c8fc5 CP |
1907 | return rc; |
1908 | } | |
1909 | ||
0619f0f5 | 1910 | static int sel_make_policycap(struct selinux_fs_info *fsi) |
3bb56b25 PM |
1911 | { |
1912 | unsigned int iter; | |
1913 | struct dentry *dentry = NULL; | |
1914 | struct inode *inode = NULL; | |
1915 | ||
3bb56b25 | 1916 | for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { |
4dc2fce3 | 1917 | if (iter < ARRAY_SIZE(selinux_policycap_names)) |
0619f0f5 | 1918 | dentry = d_alloc_name(fsi->policycap_dir, |
4dc2fce3 | 1919 | selinux_policycap_names[iter]); |
3bb56b25 | 1920 | else |
0619f0f5 | 1921 | dentry = d_alloc_name(fsi->policycap_dir, "unknown"); |
3bb56b25 PM |
1922 | |
1923 | if (dentry == NULL) | |
1924 | return -ENOMEM; | |
1925 | ||
0619f0f5 | 1926 | inode = sel_make_inode(fsi->sb, S_IFREG | 0444); |
7e4237fa | 1927 | if (inode == NULL) { |
1928 | dput(dentry); | |
3bb56b25 | 1929 | return -ENOMEM; |
7e4237fa | 1930 | } |
3bb56b25 PM |
1931 | |
1932 | inode->i_fop = &sel_policycap_ops; | |
1933 | inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; | |
1934 | d_add(dentry, inode); | |
1935 | } | |
1936 | ||
1937 | return 0; | |
1938 | } | |
1939 | ||
a1c2aa1e | 1940 | static struct dentry *sel_make_dir(struct dentry *dir, const char *name, |
0dd4ae51 | 1941 | unsigned long *ino) |
1da177e4 | 1942 | { |
a1c2aa1e | 1943 | struct dentry *dentry = d_alloc_name(dir, name); |
1da177e4 LT |
1944 | struct inode *inode; |
1945 | ||
a1c2aa1e AV |
1946 | if (!dentry) |
1947 | return ERR_PTR(-ENOMEM); | |
1948 | ||
1949 | inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO); | |
1950 | if (!inode) { | |
1951 | dput(dentry); | |
1952 | return ERR_PTR(-ENOMEM); | |
1953 | } | |
b77a493b | 1954 | |
1da177e4 LT |
1955 | inode->i_op = &simple_dir_inode_operations; |
1956 | inode->i_fop = &simple_dir_operations; | |
0dd4ae51 | 1957 | inode->i_ino = ++(*ino); |
40e906f8 | 1958 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
d8c76e6f | 1959 | inc_nlink(inode); |
1da177e4 | 1960 | d_add(dentry, inode); |
edb20fb5 | 1961 | /* bump link count on parent directory, too */ |
ce0b16dd | 1962 | inc_nlink(d_inode(dir)); |
b77a493b | 1963 | |
a1c2aa1e | 1964 | return dentry; |
1da177e4 LT |
1965 | } |
1966 | ||
0619f0f5 SS |
1967 | #define NULL_FILE_NAME "null" |
1968 | ||
920f50b2 | 1969 | static int sel_fill_super(struct super_block *sb, struct fs_context *fc) |
1da177e4 | 1970 | { |
0619f0f5 | 1971 | struct selinux_fs_info *fsi; |
1da177e4 LT |
1972 | int ret; |
1973 | struct dentry *dentry; | |
a1c2aa1e | 1974 | struct inode *inode; |
1da177e4 LT |
1975 | struct inode_security_struct *isec; |
1976 | ||
cda37124 | 1977 | static const struct tree_descr selinux_files[] = { |
1da177e4 LT |
1978 | [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR}, |
1979 | [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR}, | |
ce9982d0 | 1980 | [SEL_CONTEXT] = {"context", &transaction_ops, S_IRUGO|S_IWUGO}, |
1da177e4 LT |
1981 | [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO}, |
1982 | [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO}, | |
1983 | [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO}, | |
1984 | [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO}, | |
1985 | [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO}, | |
1986 | [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR}, | |
1987 | [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO}, | |
1988 | [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, | |
1989 | [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, | |
1990 | [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, | |
3f12070e EP |
1991 | [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, |
1992 | [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, | |
11904167 | 1993 | [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, |
72e8c859 | 1994 | [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, |
f9df6458 AP |
1995 | [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, |
1996 | S_IWUGO}, | |
1da177e4 LT |
1997 | /* last one */ {""} |
1998 | }; | |
0619f0f5 SS |
1999 | |
2000 | ret = selinux_fs_info_create(sb); | |
2001 | if (ret) | |
2002 | goto err; | |
2003 | ||
1da177e4 LT |
2004 | ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); |
2005 | if (ret) | |
161ce45a | 2006 | goto err; |
1da177e4 | 2007 | |
0619f0f5 SS |
2008 | fsi = sb->s_fs_info; |
2009 | fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino); | |
2010 | if (IS_ERR(fsi->bool_dir)) { | |
2011 | ret = PTR_ERR(fsi->bool_dir); | |
2012 | fsi->bool_dir = NULL; | |
161ce45a | 2013 | goto err; |
a1c2aa1e | 2014 | } |
1da177e4 | 2015 | |
b77a493b | 2016 | ret = -ENOMEM; |
1da177e4 | 2017 | dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); |
b77a493b | 2018 | if (!dentry) |
161ce45a | 2019 | goto err; |
1da177e4 | 2020 | |
b77a493b | 2021 | ret = -ENOMEM; |
1da177e4 | 2022 | inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); |
7e4237fa | 2023 | if (!inode) { |
2024 | dput(dentry); | |
161ce45a | 2025 | goto err; |
7e4237fa | 2026 | } |
b77a493b | 2027 | |
0619f0f5 | 2028 | inode->i_ino = ++fsi->last_ino; |
80788c22 | 2029 | isec = selinux_inode(inode); |
1da177e4 LT |
2030 | isec->sid = SECINITSID_DEVNULL; |
2031 | isec->sclass = SECCLASS_CHR_FILE; | |
42059112 | 2032 | isec->initialized = LABEL_INITIALIZED; |
1da177e4 LT |
2033 | |
2034 | init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); | |
2035 | d_add(dentry, inode); | |
1da177e4 | 2036 | |
0619f0f5 | 2037 | dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino); |
a1c2aa1e AV |
2038 | if (IS_ERR(dentry)) { |
2039 | ret = PTR_ERR(dentry); | |
161ce45a | 2040 | goto err; |
a1c2aa1e | 2041 | } |
1da177e4 LT |
2042 | |
2043 | ret = sel_make_avc_files(dentry); | |
66f8e2f0 JVS |
2044 | |
2045 | dentry = sel_make_dir(sb->s_root, "ss", &fsi->last_ino); | |
2046 | if (IS_ERR(dentry)) { | |
2047 | ret = PTR_ERR(dentry); | |
2048 | goto err; | |
2049 | } | |
2050 | ||
2051 | ret = sel_make_ss_files(dentry); | |
1da177e4 | 2052 | if (ret) |
161ce45a | 2053 | goto err; |
f0ee2e46 | 2054 | |
0619f0f5 | 2055 | dentry = sel_make_dir(sb->s_root, "initial_contexts", &fsi->last_ino); |
a1c2aa1e AV |
2056 | if (IS_ERR(dentry)) { |
2057 | ret = PTR_ERR(dentry); | |
f0ee2e46 | 2058 | goto err; |
a1c2aa1e | 2059 | } |
f0ee2e46 JC |
2060 | |
2061 | ret = sel_make_initcon_files(dentry); | |
2062 | if (ret) | |
2063 | goto err; | |
2064 | ||
0619f0f5 SS |
2065 | fsi->class_dir = sel_make_dir(sb->s_root, "class", &fsi->last_ino); |
2066 | if (IS_ERR(fsi->class_dir)) { | |
2067 | ret = PTR_ERR(fsi->class_dir); | |
2068 | fsi->class_dir = NULL; | |
3bb56b25 | 2069 | goto err; |
a1c2aa1e | 2070 | } |
3bb56b25 | 2071 | |
0619f0f5 SS |
2072 | fsi->policycap_dir = sel_make_dir(sb->s_root, "policy_capabilities", |
2073 | &fsi->last_ino); | |
2074 | if (IS_ERR(fsi->policycap_dir)) { | |
2075 | ret = PTR_ERR(fsi->policycap_dir); | |
2076 | fsi->policycap_dir = NULL; | |
3bb56b25 | 2077 | goto err; |
a1c2aa1e | 2078 | } |
0619f0f5 | 2079 | |
02a52c5c SS |
2080 | ret = sel_make_policycap(fsi); |
2081 | if (ret) { | |
2082 | pr_err("SELinux: failed to load policy capabilities\n"); | |
0619f0f5 | 2083 | goto err; |
02a52c5c SS |
2084 | } |
2085 | ||
b77a493b | 2086 | return 0; |
161ce45a | 2087 | err: |
f8b69a5f | 2088 | pr_err("SELinux: %s: failed while creating inodes\n", |
744ba35e | 2089 | __func__); |
0619f0f5 SS |
2090 | |
2091 | selinux_fs_info_free(sb); | |
2092 | ||
b77a493b | 2093 | return ret; |
1da177e4 LT |
2094 | } |
2095 | ||
920f50b2 | 2096 | static int sel_get_tree(struct fs_context *fc) |
1da177e4 | 2097 | { |
920f50b2 DH |
2098 | return get_tree_single(fc, sel_fill_super); |
2099 | } | |
2100 | ||
2101 | static const struct fs_context_operations sel_context_ops = { | |
2102 | .get_tree = sel_get_tree, | |
2103 | }; | |
2104 | ||
2105 | static int sel_init_fs_context(struct fs_context *fc) | |
2106 | { | |
2107 | fc->ops = &sel_context_ops; | |
2108 | return 0; | |
1da177e4 LT |
2109 | } |
2110 | ||
0619f0f5 SS |
2111 | static void sel_kill_sb(struct super_block *sb) |
2112 | { | |
2113 | selinux_fs_info_free(sb); | |
2114 | kill_litter_super(sb); | |
2115 | } | |
2116 | ||
1da177e4 LT |
2117 | static struct file_system_type sel_fs_type = { |
2118 | .name = "selinuxfs", | |
920f50b2 | 2119 | .init_fs_context = sel_init_fs_context, |
0619f0f5 | 2120 | .kill_sb = sel_kill_sb, |
1da177e4 LT |
2121 | }; |
2122 | ||
2123 | struct vfsmount *selinuxfs_mount; | |
0619f0f5 | 2124 | struct path selinux_null; |
1da177e4 LT |
2125 | |
2126 | static int __init init_sel_fs(void) | |
2127 | { | |
0619f0f5 SS |
2128 | struct qstr null_name = QSTR_INIT(NULL_FILE_NAME, |
2129 | sizeof(NULL_FILE_NAME)-1); | |
1da177e4 LT |
2130 | int err; |
2131 | ||
6c5a682e | 2132 | if (!selinux_enabled_boot) |
1da177e4 | 2133 | return 0; |
7a627e3b | 2134 | |
f9bb4882 EB |
2135 | err = sysfs_create_mount_point(fs_kobj, "selinux"); |
2136 | if (err) | |
2137 | return err; | |
7a627e3b | 2138 | |
1da177e4 | 2139 | err = register_filesystem(&sel_fs_type); |
7a627e3b | 2140 | if (err) { |
f9bb4882 | 2141 | sysfs_remove_mount_point(fs_kobj, "selinux"); |
b77a493b | 2142 | return err; |
7a627e3b | 2143 | } |
b77a493b | 2144 | |
765927b2 | 2145 | selinux_null.mnt = selinuxfs_mount = kern_mount(&sel_fs_type); |
b77a493b | 2146 | if (IS_ERR(selinuxfs_mount)) { |
f8b69a5f | 2147 | pr_err("selinuxfs: could not mount!\n"); |
b77a493b EP |
2148 | err = PTR_ERR(selinuxfs_mount); |
2149 | selinuxfs_mount = NULL; | |
1da177e4 | 2150 | } |
0619f0f5 SS |
2151 | selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root, |
2152 | &null_name); | |
2153 | if (IS_ERR(selinux_null.dentry)) { | |
2154 | pr_err("selinuxfs: could not lookup null!\n"); | |
2155 | err = PTR_ERR(selinux_null.dentry); | |
2156 | selinux_null.dentry = NULL; | |
2157 | } | |
b77a493b | 2158 | |
1da177e4 LT |
2159 | return err; |
2160 | } | |
2161 | ||
2162 | __initcall(init_sel_fs); | |
2163 | ||
2164 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | |
2165 | void exit_sel_fs(void) | |
2166 | { | |
f9bb4882 | 2167 | sysfs_remove_mount_point(fs_kobj, "selinux"); |
fd40ffc7 | 2168 | dput(selinux_null.dentry); |
423e0ab0 | 2169 | kern_unmount(selinuxfs_mount); |
1da177e4 LT |
2170 | unregister_filesystem(&sel_fs_type); |
2171 | } | |
2172 | #endif |