blob: d1872adf0c474c5fbe8f5210fcc26aa9c9130df7 [file] [log] [blame]
Thomas Gleixnera10e7632019-05-31 01:09:32 -07001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/* Updated: Karl MacMillan <kmacmillan@tresys.com>
3 *
Eric Paris18729812008-04-17 14:15:45 -04004 * Added conditional policy language extensions
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
Paul Moore82c21bf2011-08-01 11:10:33 +00006 * Updated: Hewlett-Packard <paul@paul-moore.com>
Paul Moore3bb56b22008-01-29 08:38:19 -05007 *
Eric Paris18729812008-04-17 14:15:45 -04008 * Added support for the policy capability bitmap
Paul Moore3bb56b22008-01-29 08:38:19 -05009 *
10 * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 * Copyright (C) 2003 - 2004 Tresys Technology, LLC
12 * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 */
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/kernel.h>
16#include <linux/pagemap.h>
17#include <linux/slab.h>
18#include <linux/vmalloc.h>
19#include <linux/fs.h>
David Howells920f50b2019-03-25 16:38:30 +000020#include <linux/fs_context.h>
Stephen Smalley0619f0f2018-03-20 11:59:11 -040021#include <linux/mount.h>
Ingo Molnarbb0030792006-03-22 00:09:14 -080022#include <linux/mutex.h>
Daniel Burgener0eea6092020-08-19 15:59:35 -040023#include <linux/namei.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/init.h>
25#include <linux/string.h>
26#include <linux/security.h>
27#include <linux/major.h>
28#include <linux/seq_file.h>
29#include <linux/percpu.h>
Steve Grubbaf601e42006-01-04 14:08:39 +000030#include <linux/audit.h>
Eric Parisf5269712008-05-14 11:27:45 -040031#include <linux/uaccess.h>
Greg Kroah-Hartman7a627e32011-05-10 15:34:16 -070032#include <linux/kobject.h>
Kohei Kaigai0f7e4c32011-05-26 14:59:25 -040033#include <linux/ctype.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
35/* selinuxfs pseudo filesystem for exporting the security policy API.
36 Based on the proc code and the fs/nfsd/nfsctl.c code. */
37
38#include "flask.h"
39#include "avc.h"
40#include "avc_ss.h"
41#include "security.h"
42#include "objsec.h"
43#include "conditional.h"
44
Linus Torvalds1da177e2005-04-16 15:20:36 -070045enum sel_inos {
46 SEL_ROOT_INO = 2,
47 SEL_LOAD, /* load policy */
48 SEL_ENFORCE, /* get or set enforcing status */
49 SEL_CONTEXT, /* validate context */
50 SEL_ACCESS, /* compute access decision */
51 SEL_CREATE, /* compute create labeling decision */
52 SEL_RELABEL, /* compute relabeling decision */
53 SEL_USER, /* compute reachable user contexts */
54 SEL_POLICYVERS, /* return policy version for this kernel */
55 SEL_COMMIT_BOOLS, /* commit new boolean values */
56 SEL_MLS, /* return if MLS policy is enabled */
57 SEL_DISABLE, /* disable SELinux until next reboot */
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 SEL_MEMBER, /* compute polyinstantiation membership decision */
59 SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */
James Morris4e5ab4c2006-06-09 00:33:33 -070060 SEL_COMPAT_NET, /* whether to use old compat network packet controls */
Eric Paris3f120702007-09-21 14:37:10 -040061 SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */
62 SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */
KaiGai Kohei11904162010-09-14 18:28:39 +090063 SEL_STATUS, /* export current status using mmap() */
Eric Pariscee74f42010-10-13 17:50:25 -040064 SEL_POLICY, /* allow userspace to read the in kernel policy */
Andrew Perepechkof9df6452015-12-24 11:09:41 -050065 SEL_VALIDATE_TRANS, /* compute validatetrans decision */
James Carter6174eaf2007-04-04 16:18:39 -040066 SEL_INO_NEXT, /* The next inode number to use */
Linus Torvalds1da177e2005-04-16 15:20:36 -070067};
68
Stephen Smalley0619f0f2018-03-20 11:59:11 -040069struct selinux_fs_info {
70 struct dentry *bool_dir;
71 unsigned int bool_num;
72 char **bool_pending_names;
73 unsigned int *bool_pending_values;
74 struct dentry *class_dir;
75 unsigned long last_class_ino;
76 bool policy_opened;
77 struct dentry *policycap_dir;
78 struct mutex mutex;
79 unsigned long last_ino;
80 struct selinux_state *state;
81 struct super_block *sb;
82};
83
84static int selinux_fs_info_create(struct super_block *sb)
85{
86 struct selinux_fs_info *fsi;
87
88 fsi = kzalloc(sizeof(*fsi), GFP_KERNEL);
89 if (!fsi)
90 return -ENOMEM;
91
92 mutex_init(&fsi->mutex);
93 fsi->last_ino = SEL_INO_NEXT - 1;
94 fsi->state = &selinux_state;
95 fsi->sb = sb;
96 sb->s_fs_info = fsi;
97 return 0;
98}
99
100static void selinux_fs_info_free(struct super_block *sb)
101{
102 struct selinux_fs_info *fsi = sb->s_fs_info;
103 int i;
104
105 if (fsi) {
106 for (i = 0; i < fsi->bool_num; i++)
107 kfree(fsi->bool_pending_names[i]);
108 kfree(fsi->bool_pending_names);
109 kfree(fsi->bool_pending_values);
110 }
111 kfree(sb->s_fs_info);
112 sb->s_fs_info = NULL;
113}
James Carter6174eaf2007-04-04 16:18:39 -0400114
Paul Moore3bb56b22008-01-29 08:38:19 -0500115#define SEL_INITCON_INO_OFFSET 0x01000000
116#define SEL_BOOL_INO_OFFSET 0x02000000
117#define SEL_CLASS_INO_OFFSET 0x04000000
118#define SEL_POLICYCAP_INO_OFFSET 0x08000000
119#define SEL_INO_MASK 0x00ffffff
James Carterf0ee2e42007-04-04 10:11:29 -0400120
Daniel Burgener613ba182020-08-19 15:59:34 -0400121#define BOOL_DIR_NAME "booleans"
122#define CLASS_DIR_NAME "class"
123#define POLICYCAP_DIR_NAME "policy_capabilities"
124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125#define TMPBUFLEN 12
126static ssize_t sel_read_enforce(struct file *filp, char __user *buf,
127 size_t count, loff_t *ppos)
128{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400129 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 char tmpbuf[TMPBUFLEN];
131 ssize_t length;
132
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500133 length = scnprintf(tmpbuf, TMPBUFLEN, "%d",
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400134 enforcing_enabled(fsi->state));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
136}
137
138#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
Eric Paris18729812008-04-17 14:15:45 -0400139static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 size_t count, loff_t *ppos)
141
142{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400143 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
144 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -0500145 char *page = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 ssize_t length;
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500147 int old_value, new_value;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Davi Arnautbfd51622005-10-30 14:59:24 -0800149 if (count >= PAGE_SIZE)
Al Viro8365a712015-12-24 00:08:06 -0500150 return -ENOMEM;
Eric Parisb77a4932010-11-23 11:40:08 -0500151
152 /* No partial writes. */
Eric Parisb77a4932010-11-23 11:40:08 -0500153 if (*ppos != 0)
Al Viro8365a712015-12-24 00:08:06 -0500154 return -EINVAL;
Eric Parisb77a4932010-11-23 11:40:08 -0500155
Al Viro8365a712015-12-24 00:08:06 -0500156 page = memdup_user_nul(buf, count);
157 if (IS_ERR(page))
158 return PTR_ERR(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
160 length = -EINVAL;
161 if (sscanf(page, "%d", &new_value) != 1)
162 goto out;
163
Stephen Smalleyea49d10ee2016-11-18 09:30:38 -0500164 new_value = !!new_value;
165
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400166 old_value = enforcing_enabled(state);
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500167 if (new_value != old_value) {
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500168 length = avc_has_perm(&selinux_state,
169 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500170 SECCLASS_SECURITY, SECURITY__SETENFORCE,
171 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 if (length)
173 goto out;
Richard Guy Briggscdfb6b32018-05-12 21:58:20 -0400174 audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS,
Richard Guy Briggs4195ed42018-04-09 19:34:22 -0400175 "enforcing=%d old_enforcing=%d auid=%u ses=%u"
Stephen Smalley6c5a6822019-12-17 09:15:10 -0500176 " enabled=1 old-enabled=1 lsm=selinux res=1",
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500177 new_value, old_value,
Eric W. Biederman581abc02012-08-20 00:09:36 -0700178 from_kuid(&init_user_ns, audit_get_loginuid(current)),
Stephen Smalley6c5a6822019-12-17 09:15:10 -0500179 audit_get_sessionid(current));
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400180 enforcing_set(state, new_value);
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500181 if (new_value)
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500182 avc_ss_reset(state->avc, 0);
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500183 selnl_notify_setenforce(new_value);
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400184 selinux_status_update_setenforce(state, new_value);
Stephen Smalleyaa8e7122018-03-01 18:48:02 -0500185 if (!new_value)
Janne Karhunen42df7442019-06-14 15:20:14 +0300186 call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
188 length = count;
189out:
Al Viro8365a712015-12-24 00:08:06 -0500190 kfree(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 return length;
192}
193#else
194#define sel_write_enforce NULL
195#endif
196
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800197static const struct file_operations sel_enforce_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 .read = sel_read_enforce,
199 .write = sel_write_enforce,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200200 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201};
202
Eric Paris3f120702007-09-21 14:37:10 -0400203static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf,
204 size_t count, loff_t *ppos)
205{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400206 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
207 struct selinux_state *state = fsi->state;
Eric Paris3f120702007-09-21 14:37:10 -0400208 char tmpbuf[TMPBUFLEN];
209 ssize_t length;
Al Viro496ad9a2013-01-23 17:07:38 -0500210 ino_t ino = file_inode(filp)->i_ino;
Eric Paris3f120702007-09-21 14:37:10 -0400211 int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ?
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400212 security_get_reject_unknown(state) :
213 !security_get_allow_unknown(state);
Eric Paris3f120702007-09-21 14:37:10 -0400214
215 length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown);
216 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
217}
218
219static const struct file_operations sel_handle_unknown_ops = {
220 .read = sel_read_handle_unknown,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200221 .llseek = generic_file_llseek,
Eric Paris3f120702007-09-21 14:37:10 -0400222};
223
KaiGai Kohei11904162010-09-14 18:28:39 +0900224static int sel_open_handle_status(struct inode *inode, struct file *filp)
225{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400226 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
227 struct page *status = selinux_kernel_status_page(fsi->state);
KaiGai Kohei11904162010-09-14 18:28:39 +0900228
229 if (!status)
230 return -ENOMEM;
231
232 filp->private_data = status;
233
234 return 0;
235}
236
237static ssize_t sel_read_handle_status(struct file *filp, char __user *buf,
238 size_t count, loff_t *ppos)
239{
240 struct page *status = filp->private_data;
241
242 BUG_ON(!status);
243
244 return simple_read_from_buffer(buf, count, ppos,
245 page_address(status),
246 sizeof(struct selinux_kernel_status));
247}
248
249static int sel_mmap_handle_status(struct file *filp,
250 struct vm_area_struct *vma)
251{
252 struct page *status = filp->private_data;
253 unsigned long size = vma->vm_end - vma->vm_start;
254
255 BUG_ON(!status);
256
257 /* only allows one page from the head */
258 if (vma->vm_pgoff > 0 || size != PAGE_SIZE)
259 return -EIO;
260 /* disallow writable mapping */
261 if (vma->vm_flags & VM_WRITE)
262 return -EPERM;
263 /* disallow mprotect() turns it into writable */
264 vma->vm_flags &= ~VM_MAYWRITE;
265
266 return remap_pfn_range(vma, vma->vm_start,
267 page_to_pfn(status),
268 size, vma->vm_page_prot);
269}
270
271static const struct file_operations sel_handle_status_ops = {
272 .open = sel_open_handle_status,
273 .read = sel_read_handle_status,
274 .mmap = sel_mmap_handle_status,
275 .llseek = generic_file_llseek,
276};
277
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278#ifdef CONFIG_SECURITY_SELINUX_DISABLE
Eric Paris18729812008-04-17 14:15:45 -0400279static ssize_t sel_write_disable(struct file *file, const char __user *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 size_t count, loff_t *ppos)
281
282{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400283 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
Al Viro8365a712015-12-24 00:08:06 -0500284 char *page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 ssize_t length;
286 int new_value;
Richard Guy Briggs4195ed42018-04-09 19:34:22 -0400287 int enforcing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
Paul Moore89b223b2019-12-18 21:45:08 -0500289 /* NOTE: we are now officially considering runtime disable as
290 * deprecated, and using it will become increasingly painful
291 * (e.g. sleeping/blocking) as we progress through future
292 * kernel releases until eventually it is removed
293 */
294 pr_err("SELinux: Runtime disable is deprecated, use selinux=0 on the kernel cmdline.\n");
295
Davi Arnautbfd51622005-10-30 14:59:24 -0800296 if (count >= PAGE_SIZE)
Al Viro8365a712015-12-24 00:08:06 -0500297 return -ENOMEM;
Eric Parisb77a4932010-11-23 11:40:08 -0500298
299 /* No partial writes. */
Eric Parisb77a4932010-11-23 11:40:08 -0500300 if (*ppos != 0)
Al Viro8365a712015-12-24 00:08:06 -0500301 return -EINVAL;
Eric Parisb77a4932010-11-23 11:40:08 -0500302
Al Viro8365a712015-12-24 00:08:06 -0500303 page = memdup_user_nul(buf, count);
304 if (IS_ERR(page))
305 return PTR_ERR(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307 length = -EINVAL;
308 if (sscanf(page, "%d", &new_value) != 1)
309 goto out;
310
311 if (new_value) {
Richard Guy Briggs4195ed42018-04-09 19:34:22 -0400312 enforcing = enforcing_enabled(fsi->state);
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400313 length = selinux_disable(fsi->state);
Eric Parisb77a4932010-11-23 11:40:08 -0500314 if (length)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 goto out;
Richard Guy Briggscdfb6b32018-05-12 21:58:20 -0400316 audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS,
Richard Guy Briggs4195ed42018-04-09 19:34:22 -0400317 "enforcing=%d old_enforcing=%d auid=%u ses=%u"
Stephen Smalley6c5a6822019-12-17 09:15:10 -0500318 " enabled=0 old-enabled=1 lsm=selinux res=1",
Richard Guy Briggs4195ed42018-04-09 19:34:22 -0400319 enforcing, enforcing,
Eric W. Biederman581abc02012-08-20 00:09:36 -0700320 from_kuid(&init_user_ns, audit_get_loginuid(current)),
Stephen Smalley6c5a6822019-12-17 09:15:10 -0500321 audit_get_sessionid(current));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 }
323
324 length = count;
325out:
Al Viro8365a712015-12-24 00:08:06 -0500326 kfree(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 return length;
328}
329#else
330#define sel_write_disable NULL
331#endif
332
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800333static const struct file_operations sel_disable_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 .write = sel_write_disable,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200335 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336};
337
338static ssize_t sel_read_policyvers(struct file *filp, char __user *buf,
Eric Paris18729812008-04-17 14:15:45 -0400339 size_t count, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
341 char tmpbuf[TMPBUFLEN];
342 ssize_t length;
343
344 length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX);
345 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
346}
347
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800348static const struct file_operations sel_policyvers_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 .read = sel_read_policyvers,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200350 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351};
352
353/* declaration for sel_write_load */
Daniel Burgener66ec3842020-08-19 15:59:33 -0400354static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir,
355 unsigned int *bool_num, char ***bool_pending_names,
356 unsigned int **bool_pending_values);
357static int sel_make_classes(struct selinux_policy *newpolicy,
358 struct dentry *class_dir,
359 unsigned long *last_class_ino);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -0400360
361/* declaration for sel_make_class_dirs */
Al Viroa1c2aa12012-03-18 20:36:59 -0400362static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -0400363 unsigned long *ino);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
Daniel Burgener0eea6092020-08-19 15:59:35 -0400365/* declaration for sel_make_policy_nodes */
366static struct dentry *sel_make_disconnected_dir(struct super_block *sb,
367 unsigned long *ino);
368
369/* declaration for sel_make_policy_nodes */
Daniel Burgeneraeecf4a2020-08-19 15:59:32 -0400370static void sel_remove_entries(struct dentry *de);
371
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372static ssize_t sel_read_mls(struct file *filp, char __user *buf,
373 size_t count, loff_t *ppos)
374{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400375 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 char tmpbuf[TMPBUFLEN];
377 ssize_t length;
378
Guido Trentalancia0719aaf2010-02-03 16:40:20 +0100379 length = scnprintf(tmpbuf, TMPBUFLEN, "%d",
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400380 security_mls_enabled(fsi->state));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
382}
383
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800384static const struct file_operations sel_mls_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 .read = sel_read_mls,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200386 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387};
388
Eric Pariscee74f42010-10-13 17:50:25 -0400389struct policy_load_memory {
390 size_t len;
391 void *data;
392};
393
394static int sel_open_policy(struct inode *inode, struct file *filp)
395{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400396 struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
397 struct selinux_state *state = fsi->state;
Eric Pariscee74f42010-10-13 17:50:25 -0400398 struct policy_load_memory *plm = NULL;
399 int rc;
400
401 BUG_ON(filp->private_data);
402
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400403 mutex_lock(&fsi->mutex);
Eric Pariscee74f42010-10-13 17:50:25 -0400404
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500405 rc = avc_has_perm(&selinux_state,
406 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500407 SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
Eric Pariscee74f42010-10-13 17:50:25 -0400408 if (rc)
409 goto err;
410
411 rc = -EBUSY;
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400412 if (fsi->policy_opened)
Eric Pariscee74f42010-10-13 17:50:25 -0400413 goto err;
414
415 rc = -ENOMEM;
416 plm = kzalloc(sizeof(*plm), GFP_KERNEL);
417 if (!plm)
418 goto err;
419
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400420 if (i_size_read(inode) != security_policydb_len(state)) {
Al Viro59551022016-01-22 15:40:57 -0500421 inode_lock(inode);
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400422 i_size_write(inode, security_policydb_len(state));
Al Viro59551022016-01-22 15:40:57 -0500423 inode_unlock(inode);
Eric Pariscee74f42010-10-13 17:50:25 -0400424 }
425
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400426 rc = security_read_policy(state, &plm->data, &plm->len);
Eric Pariscee74f42010-10-13 17:50:25 -0400427 if (rc)
428 goto err;
429
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400430 fsi->policy_opened = 1;
Eric Pariscee74f42010-10-13 17:50:25 -0400431
432 filp->private_data = plm;
433
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400434 mutex_unlock(&fsi->mutex);
Eric Pariscee74f42010-10-13 17:50:25 -0400435
436 return 0;
437err:
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400438 mutex_unlock(&fsi->mutex);
Eric Pariscee74f42010-10-13 17:50:25 -0400439
440 if (plm)
441 vfree(plm->data);
442 kfree(plm);
443 return rc;
444}
445
446static int sel_release_policy(struct inode *inode, struct file *filp)
447{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400448 struct selinux_fs_info *fsi = inode->i_sb->s_fs_info;
Eric Pariscee74f42010-10-13 17:50:25 -0400449 struct policy_load_memory *plm = filp->private_data;
450
451 BUG_ON(!plm);
452
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400453 fsi->policy_opened = 0;
Eric Pariscee74f42010-10-13 17:50:25 -0400454
455 vfree(plm->data);
456 kfree(plm);
457
458 return 0;
459}
460
461static ssize_t sel_read_policy(struct file *filp, char __user *buf,
462 size_t count, loff_t *ppos)
463{
464 struct policy_load_memory *plm = filp->private_data;
465 int ret;
466
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500467 ret = avc_has_perm(&selinux_state,
468 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500469 SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL);
Eric Pariscee74f42010-10-13 17:50:25 -0400470 if (ret)
Jann Horn0da74122018-06-28 20:39:54 -0400471 return ret;
Eric Pariscee74f42010-10-13 17:50:25 -0400472
Jann Horn0da74122018-06-28 20:39:54 -0400473 return simple_read_from_buffer(buf, count, ppos, plm->data, plm->len);
Eric Pariscee74f42010-10-13 17:50:25 -0400474}
475
Souptick Joarderac9a1f62018-04-14 21:02:41 +0530476static vm_fault_t sel_mmap_policy_fault(struct vm_fault *vmf)
Eric Paris845ca302010-10-13 17:50:31 -0400477{
Dave Jiang11bac802017-02-24 14:56:41 -0800478 struct policy_load_memory *plm = vmf->vma->vm_file->private_data;
Eric Paris845ca302010-10-13 17:50:31 -0400479 unsigned long offset;
480 struct page *page;
481
482 if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE))
483 return VM_FAULT_SIGBUS;
484
485 offset = vmf->pgoff << PAGE_SHIFT;
486 if (offset >= roundup(plm->len, PAGE_SIZE))
487 return VM_FAULT_SIGBUS;
488
489 page = vmalloc_to_page(plm->data + offset);
490 get_page(page);
491
492 vmf->page = page;
493
494 return 0;
495}
496
Kirill A. Shutemov7cbea8d2015-09-09 15:39:26 -0700497static const struct vm_operations_struct sel_mmap_policy_ops = {
Eric Paris845ca302010-10-13 17:50:31 -0400498 .fault = sel_mmap_policy_fault,
499 .page_mkwrite = sel_mmap_policy_fault,
500};
501
James Morrisad3fa082011-08-30 10:50:12 +1000502static int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma)
Eric Paris845ca302010-10-13 17:50:31 -0400503{
504 if (vma->vm_flags & VM_SHARED) {
505 /* do not allow mprotect to make mapping writable */
506 vma->vm_flags &= ~VM_MAYWRITE;
507
508 if (vma->vm_flags & VM_WRITE)
509 return -EACCES;
510 }
511
Konstantin Khlebnikov314e51b2012-10-08 16:29:02 -0700512 vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
Eric Paris845ca302010-10-13 17:50:31 -0400513 vma->vm_ops = &sel_mmap_policy_ops;
514
515 return 0;
516}
517
Eric Pariscee74f42010-10-13 17:50:25 -0400518static const struct file_operations sel_policy_ops = {
519 .open = sel_open_policy,
520 .read = sel_read_policy,
Eric Paris845ca302010-10-13 17:50:31 -0400521 .mmap = sel_mmap_policy,
Eric Pariscee74f42010-10-13 17:50:25 -0400522 .release = sel_release_policy,
Eric Paris47a93a52012-02-16 15:08:39 -0500523 .llseek = generic_file_llseek,
Eric Pariscee74f42010-10-13 17:50:25 -0400524};
525
Daniel Burgener0eea6092020-08-19 15:59:35 -0400526static void sel_remove_old_bool_data(unsigned int bool_num, char **bool_names,
527 unsigned int *bool_values)
Daniel Burgeneraeecf4a2020-08-19 15:59:32 -0400528{
529 u32 i;
530
531 /* bool_dir cleanup */
Daniel Burgener0eea6092020-08-19 15:59:35 -0400532 for (i = 0; i < bool_num; i++)
533 kfree(bool_names[i]);
534 kfree(bool_names);
535 kfree(bool_values);
Daniel Burgeneraeecf4a2020-08-19 15:59:32 -0400536}
537
Stephen Smalley02a52c52020-08-07 09:29:34 -0400538static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
539 struct selinux_policy *newpolicy)
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400540{
Daniel Burgener0eea6092020-08-19 15:59:35 -0400541 int ret = 0;
542 struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry;
543 unsigned int tmp_bool_num, old_bool_num;
544 char **tmp_bool_names, **old_bool_names;
545 unsigned int *tmp_bool_values, *old_bool_values;
546 unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400547
Daniel Burgener0eea6092020-08-19 15:59:35 -0400548 tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino);
549 if (IS_ERR(tmp_parent))
550 return PTR_ERR(tmp_parent);
Daniel Burgeneraeecf4a2020-08-19 15:59:32 -0400551
Daniel Burgener0eea6092020-08-19 15:59:35 -0400552 tmp_ino = fsi->bool_dir->d_inode->i_ino - 1; /* sel_make_dir will increment and set */
553 tmp_bool_dir = sel_make_dir(tmp_parent, BOOL_DIR_NAME, &tmp_ino);
554 if (IS_ERR(tmp_bool_dir)) {
555 ret = PTR_ERR(tmp_bool_dir);
556 goto out;
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400557 }
558
Daniel Burgener0eea6092020-08-19 15:59:35 -0400559 tmp_ino = fsi->class_dir->d_inode->i_ino - 1; /* sel_make_dir will increment and set */
560 tmp_class_dir = sel_make_dir(tmp_parent, CLASS_DIR_NAME, &tmp_ino);
561 if (IS_ERR(tmp_class_dir)) {
562 ret = PTR_ERR(tmp_class_dir);
563 goto out;
564 }
565
566 ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num,
567 &tmp_bool_names, &tmp_bool_values);
568 if (ret) {
569 pr_err("SELinux: failed to load policy booleans\n");
570 goto out;
571 }
572
573 ret = sel_make_classes(newpolicy, tmp_class_dir,
Daniel Burgener66ec3842020-08-19 15:59:33 -0400574 &fsi->last_class_ino);
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400575 if (ret) {
576 pr_err("SELinux: failed to load policy classes\n");
Daniel Burgener0eea6092020-08-19 15:59:35 -0400577 goto out;
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400578 }
579
Daniel Burgener0eea6092020-08-19 15:59:35 -0400580 /* booleans */
581 old_dentry = fsi->bool_dir;
582 lock_rename(tmp_bool_dir, old_dentry);
583 d_exchange(tmp_bool_dir, fsi->bool_dir);
584
585 old_bool_num = fsi->bool_num;
586 old_bool_names = fsi->bool_pending_names;
587 old_bool_values = fsi->bool_pending_values;
588
589 fsi->bool_num = tmp_bool_num;
590 fsi->bool_pending_names = tmp_bool_names;
591 fsi->bool_pending_values = tmp_bool_values;
592
593 sel_remove_old_bool_data(old_bool_num, old_bool_names, old_bool_values);
594
595 fsi->bool_dir = tmp_bool_dir;
596 unlock_rename(tmp_bool_dir, old_dentry);
597
598 /* classes */
599 old_dentry = fsi->class_dir;
600 lock_rename(tmp_class_dir, old_dentry);
601 d_exchange(tmp_class_dir, fsi->class_dir);
602 fsi->class_dir = tmp_class_dir;
603 unlock_rename(tmp_class_dir, old_dentry);
604
605out:
606 /* Since the other temporary dirs are children of tmp_parent
607 * this will handle all the cleanup in the case of a failure before
608 * the swapover
609 */
610 sel_remove_entries(tmp_parent);
611 dput(tmp_parent); /* d_genocide() only handles the children */
612
613 return ret;
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400614}
615
Eric Paris18729812008-04-17 14:15:45 -0400616static ssize_t sel_write_load(struct file *file, const char __user *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 size_t count, loff_t *ppos)
618
619{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400620 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
Stephen Smalley02a52c52020-08-07 09:29:34 -0400621 struct selinux_policy *newpolicy;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 ssize_t length;
623 void *data = NULL;
624
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400625 mutex_lock(&fsi->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500627 length = avc_has_perm(&selinux_state,
628 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500629 SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 if (length)
631 goto out;
632
Eric Parisb77a4932010-11-23 11:40:08 -0500633 /* No partial writes. */
634 length = -EINVAL;
635 if (*ppos != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Eric Parisb77a4932010-11-23 11:40:08 -0500638 length = -ENOMEM;
639 data = vmalloc(count);
640 if (!data)
641 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 length = -EFAULT;
644 if (copy_from_user(data, buf, count) != 0)
645 goto out;
646
Stephen Smalley02a52c52020-08-07 09:29:34 -0400647 length = security_load_policy(fsi->state, data, count, &newpolicy);
Gary Tierney4262fb52017-01-09 10:07:31 -0500648 if (length) {
649 pr_warn_ratelimited("SELinux: failed to load policy\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 goto out;
Gary Tierney4262fb52017-01-09 10:07:31 -0500651 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Stephen Smalley02a52c52020-08-07 09:29:34 -0400653 length = sel_make_policy_nodes(fsi, newpolicy);
654 if (length) {
655 selinux_policy_cancel(fsi->state, newpolicy);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -0400656 goto out1;
Stephen Smalley02a52c52020-08-07 09:29:34 -0400657 }
658
659 selinux_policy_commit(fsi->state, newpolicy);
Eric Parisb77a4932010-11-23 11:40:08 -0500660
661 length = count;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -0400662
663out1:
Richard Guy Briggscdfb6b32018-05-12 21:58:20 -0400664 audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD,
Richard Guy Briggsd1411362018-04-09 19:36:31 -0400665 "auid=%u ses=%u lsm=selinux res=1",
Eric W. Biederman581abc02012-08-20 00:09:36 -0700666 from_kuid(&init_user_ns, audit_get_loginuid(current)),
Eric Paris4746ec52008-01-08 10:06:53 -0500667 audit_get_sessionid(current));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668out:
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400669 mutex_unlock(&fsi->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 vfree(data);
671 return length;
672}
673
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800674static const struct file_operations sel_load_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 .write = sel_write_load,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200676 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677};
678
Eric Paris18729812008-04-17 14:15:45 -0400679static ssize_t sel_write_context(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400681 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
682 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -0500683 char *canon = NULL;
Stephen Smalleyce9982d2005-11-08 21:34:33 -0800684 u32 sid, len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 ssize_t length;
686
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500687 length = avc_has_perm(&selinux_state,
688 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500689 SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 if (length)
Eric Parisb77a4932010-11-23 11:40:08 -0500691 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400693 length = security_context_to_sid(state, buf, size, &sid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -0500694 if (length)
695 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400697 length = security_sid_to_context(state, sid, &canon, &len);
Eric Parisb77a4932010-11-23 11:40:08 -0500698 if (length)
699 goto out;
Stephen Smalleyce9982d2005-11-08 21:34:33 -0800700
Eric Parisb77a4932010-11-23 11:40:08 -0500701 length = -ERANGE;
Stephen Smalleyce9982d2005-11-08 21:34:33 -0800702 if (len > SIMPLE_TRANSACTION_LIMIT) {
peter enderborgf8b69a52018-06-12 10:09:06 +0200703 pr_err("SELinux: %s: context size (%u) exceeds "
Eric Paris744ba352008-04-17 11:52:44 -0400704 "payload max\n", __func__, len);
Stephen Smalleyce9982d2005-11-08 21:34:33 -0800705 goto out;
706 }
707
708 memcpy(buf, canon, len);
709 length = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710out:
Stephen Smalleyce9982d2005-11-08 21:34:33 -0800711 kfree(canon);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 return length;
713}
714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715static ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf,
716 size_t count, loff_t *ppos)
717{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400718 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 char tmpbuf[TMPBUFLEN];
720 ssize_t length;
721
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400722 length = scnprintf(tmpbuf, TMPBUFLEN, "%u", fsi->state->checkreqprot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
724}
725
Eric Paris18729812008-04-17 14:15:45 -0400726static ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 size_t count, loff_t *ppos)
728{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400729 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
Al Viro8365a712015-12-24 00:08:06 -0500730 char *page;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 ssize_t length;
732 unsigned int new_value;
733
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500734 length = avc_has_perm(&selinux_state,
735 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500736 SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT,
737 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 if (length)
Al Viro8365a712015-12-24 00:08:06 -0500739 return length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Davi Arnautbfd51622005-10-30 14:59:24 -0800741 if (count >= PAGE_SIZE)
Al Viro8365a712015-12-24 00:08:06 -0500742 return -ENOMEM;
Eric Parisb77a4932010-11-23 11:40:08 -0500743
744 /* No partial writes. */
Eric Parisb77a4932010-11-23 11:40:08 -0500745 if (*ppos != 0)
Al Viro8365a712015-12-24 00:08:06 -0500746 return -EINVAL;
Eric Parisb77a4932010-11-23 11:40:08 -0500747
Al Viro8365a712015-12-24 00:08:06 -0500748 page = memdup_user_nul(buf, count);
749 if (IS_ERR(page))
750 return PTR_ERR(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
752 length = -EINVAL;
753 if (sscanf(page, "%u", &new_value) != 1)
754 goto out;
755
Stephen Smalleye9c38f92020-01-08 11:24:47 -0500756 if (new_value) {
757 char comm[sizeof(current->comm)];
758
759 memcpy(comm, current->comm, sizeof(comm));
760 pr_warn_once("SELinux: %s (%d) set checkreqprot to 1. This is deprecated and will be rejected in a future kernel release.\n",
761 comm, current->pid);
762 }
763
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400764 fsi->state->checkreqprot = new_value ? 1 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 length = count;
766out:
Al Viro8365a712015-12-24 00:08:06 -0500767 kfree(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 return length;
769}
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800770static const struct file_operations sel_checkreqprot_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 .read = sel_read_checkreqprot,
772 .write = sel_write_checkreqprot,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200773 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774};
775
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500776static ssize_t sel_write_validatetrans(struct file *file,
777 const char __user *buf,
778 size_t count, loff_t *ppos)
779{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400780 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
781 struct selinux_state *state = fsi->state;
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500782 char *oldcon = NULL, *newcon = NULL, *taskcon = NULL;
783 char *req = NULL;
784 u32 osid, nsid, tsid;
785 u16 tclass;
786 int rc;
787
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500788 rc = avc_has_perm(&selinux_state,
789 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500790 SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL);
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500791 if (rc)
792 goto out;
793
794 rc = -ENOMEM;
795 if (count >= PAGE_SIZE)
796 goto out;
797
798 /* No partial writes. */
799 rc = -EINVAL;
800 if (*ppos != 0)
801 goto out;
802
Al Viro0b884d22017-05-13 18:12:07 -0400803 req = memdup_user_nul(buf, count);
804 if (IS_ERR(req)) {
805 rc = PTR_ERR(req);
806 req = NULL;
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500807 goto out;
Al Viro0b884d22017-05-13 18:12:07 -0400808 }
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500809
810 rc = -ENOMEM;
811 oldcon = kzalloc(count + 1, GFP_KERNEL);
812 if (!oldcon)
813 goto out;
814
815 newcon = kzalloc(count + 1, GFP_KERNEL);
816 if (!newcon)
817 goto out;
818
819 taskcon = kzalloc(count + 1, GFP_KERNEL);
820 if (!taskcon)
821 goto out;
822
823 rc = -EINVAL;
824 if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4)
825 goto out;
826
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400827 rc = security_context_str_to_sid(state, oldcon, &osid, GFP_KERNEL);
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500828 if (rc)
829 goto out;
830
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400831 rc = security_context_str_to_sid(state, newcon, &nsid, GFP_KERNEL);
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500832 if (rc)
833 goto out;
834
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400835 rc = security_context_str_to_sid(state, taskcon, &tsid, GFP_KERNEL);
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500836 if (rc)
837 goto out;
838
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400839 rc = security_validate_transition_user(state, osid, nsid, tsid, tclass);
Andrew Perepechkof9df6452015-12-24 11:09:41 -0500840 if (!rc)
841 rc = count;
842out:
843 kfree(req);
844 kfree(oldcon);
845 kfree(newcon);
846 kfree(taskcon);
847 return rc;
848}
849
850static const struct file_operations sel_transition_ops = {
851 .write = sel_write_validatetrans,
852 .llseek = generic_file_llseek,
853};
854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855/*
856 * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c
857 */
Eric Paris18729812008-04-17 14:15:45 -0400858static ssize_t sel_write_access(struct file *file, char *buf, size_t size);
859static ssize_t sel_write_create(struct file *file, char *buf, size_t size);
860static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size);
861static ssize_t sel_write_user(struct file *file, char *buf, size_t size);
862static ssize_t sel_write_member(struct file *file, char *buf, size_t size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
Eric Biggers631d2b42018-07-17 10:43:56 -0700864static ssize_t (*const write_op[])(struct file *, char *, size_t) = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 [SEL_ACCESS] = sel_write_access,
866 [SEL_CREATE] = sel_write_create,
867 [SEL_RELABEL] = sel_write_relabel,
868 [SEL_USER] = sel_write_user,
869 [SEL_MEMBER] = sel_write_member,
Stephen Smalleyce9982d2005-11-08 21:34:33 -0800870 [SEL_CONTEXT] = sel_write_context,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871};
872
873static ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos)
874{
Al Viro496ad9a2013-01-23 17:07:38 -0500875 ino_t ino = file_inode(file)->i_ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 char *data;
877 ssize_t rv;
878
Nicolas Kaiser6e20a642006-01-06 00:11:22 -0800879 if (ino >= ARRAY_SIZE(write_op) || !write_op[ino])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 return -EINVAL;
881
882 data = simple_transaction_get(file, buf, size);
883 if (IS_ERR(data))
884 return PTR_ERR(data);
885
Eric Paris18729812008-04-17 14:15:45 -0400886 rv = write_op[ino](file, data, size);
887 if (rv > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 simple_transaction_set(file, rv);
889 rv = size;
890 }
891 return rv;
892}
893
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -0800894static const struct file_operations transaction_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 .write = selinux_transaction_write,
896 .read = simple_transaction_read,
897 .release = simple_transaction_release,
Arnd Bergmann57a62c22010-07-07 23:40:10 +0200898 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899};
900
901/*
902 * payload - write methods
903 * If the method has a response, the response should be put in buf,
904 * and the length returned. Otherwise return 0 or and -error.
905 */
906
Eric Paris18729812008-04-17 14:15:45 -0400907static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400909 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
910 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -0500911 char *scon = NULL, *tcon = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 u32 ssid, tsid;
913 u16 tclass;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 struct av_decision avd;
915 ssize_t length;
916
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500917 length = avc_has_perm(&selinux_state,
918 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500919 SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 if (length)
Eric Parisb77a4932010-11-23 11:40:08 -0500921 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
923 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +0800924 scon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 if (!scon)
Eric Parisb77a4932010-11-23 11:40:08 -0500926 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Eric Parisb77a4932010-11-23 11:40:08 -0500928 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +0800929 tcon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 if (!tcon)
931 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
933 length = -EINVAL;
Stephen Smalley19439d02010-01-14 17:28:10 -0500934 if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
Eric Parisb77a4932010-11-23 11:40:08 -0500935 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400937 length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -0500938 if (length)
939 goto out;
940
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400941 length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -0500942 if (length)
943 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400945 security_compute_av_user(state, ssid, tsid, tclass, &avd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
KaiGai Kohei8a6f83a2009-04-01 10:07:57 +0900948 "%x %x %x %x %u %x",
Eric Parisf1c63812009-02-12 14:50:54 -0500949 avd.allowed, 0xffffffff,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 avd.auditallow, avd.auditdeny,
KaiGai Kohei8a6f83a2009-04-01 10:07:57 +0900951 avd.seqno, avd.flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952out:
Eric Parisb77a4932010-11-23 11:40:08 -0500953 kfree(tcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 kfree(scon);
955 return length;
956}
957
Eric Paris18729812008-04-17 14:15:45 -0400958static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959{
Stephen Smalley0619f0f2018-03-20 11:59:11 -0400960 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
961 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -0500962 char *scon = NULL, *tcon = NULL;
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +0100963 char *namebuf = NULL, *objname = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 u32 ssid, tsid, newsid;
965 u16 tclass;
966 ssize_t length;
Eric Parisb77a4932010-11-23 11:40:08 -0500967 char *newcon = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 u32 len;
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +0100969 int nargs;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
Stephen Smalley6b6bc622018-03-05 11:47:56 -0500971 length = avc_has_perm(&selinux_state,
972 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -0500973 SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE,
974 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 if (length)
Eric Parisb77a4932010-11-23 11:40:08 -0500976 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +0800979 scon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 if (!scon)
Eric Parisb77a4932010-11-23 11:40:08 -0500981 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
Eric Parisb77a4932010-11-23 11:40:08 -0500983 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +0800984 tcon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 if (!tcon)
986 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +0100988 length = -ENOMEM;
989 namebuf = kzalloc(size + 1, GFP_KERNEL);
990 if (!namebuf)
Eric Parisb77a4932010-11-23 11:40:08 -0500991 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +0100993 length = -EINVAL;
994 nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf);
995 if (nargs < 3 || nargs > 4)
996 goto out;
Kohei Kaigai0f7e4c32011-05-26 14:59:25 -0400997 if (nargs == 4) {
998 /*
999 * If and when the name of new object to be queried contains
1000 * either whitespace or multibyte characters, they shall be
1001 * encoded based on the percentage-encoding rule.
1002 * If not encoded, the sscanf logic picks up only left-half
1003 * of the supplied name; splitted by a whitespace unexpectedly.
1004 */
1005 char *r, *w;
1006 int c1, c2;
1007
1008 r = w = namebuf;
1009 do {
1010 c1 = *r++;
1011 if (c1 == '+')
1012 c1 = ' ';
1013 else if (c1 == '%') {
Andy Shevchenkoaf7ff2c2011-11-15 15:11:41 -08001014 c1 = hex_to_bin(*r++);
1015 if (c1 < 0)
Kohei Kaigai0f7e4c32011-05-26 14:59:25 -04001016 goto out;
Andy Shevchenkoaf7ff2c2011-11-15 15:11:41 -08001017 c2 = hex_to_bin(*r++);
1018 if (c2 < 0)
Kohei Kaigai0f7e4c32011-05-26 14:59:25 -04001019 goto out;
1020 c1 = (c1 << 4) | c2;
1021 }
1022 *w++ = c1;
1023 } while (c1 != '\0');
1024
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +01001025 objname = namebuf;
Kohei Kaigai0f7e4c32011-05-26 14:59:25 -04001026 }
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +01001027
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001028 length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001029 if (length)
1030 goto out;
1031
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001032 length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001033 if (length)
1034 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001036 length = security_transition_sid_user(state, ssid, tsid, tclass,
1037 objname, &newsid);
Eric Parisb77a4932010-11-23 11:40:08 -05001038 if (length)
1039 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001041 length = security_sid_to_context(state, newsid, &newcon, &len);
Eric Parisb77a4932010-11-23 11:40:08 -05001042 if (length)
1043 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044
Eric Parisb77a4932010-11-23 11:40:08 -05001045 length = -ERANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 if (len > SIMPLE_TRANSACTION_LIMIT) {
peter enderborgf8b69a52018-06-12 10:09:06 +02001047 pr_err("SELinux: %s: context size (%u) exceeds "
Eric Paris744ba352008-04-17 11:52:44 -04001048 "payload max\n", __func__, len);
Eric Parisb77a4932010-11-23 11:40:08 -05001049 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 }
1051
1052 memcpy(buf, newcon, len);
1053 length = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054out:
Eric Parisb77a4932010-11-23 11:40:08 -05001055 kfree(newcon);
Kohei Kaigaif50a3ec2011-04-01 15:39:26 +01001056 kfree(namebuf);
Eric Parisb77a4932010-11-23 11:40:08 -05001057 kfree(tcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 kfree(scon);
1059 return length;
1060}
1061
Eric Paris18729812008-04-17 14:15:45 -04001062static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001064 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
1065 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -05001066 char *scon = NULL, *tcon = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 u32 ssid, tsid, newsid;
1068 u16 tclass;
1069 ssize_t length;
Eric Parisb77a4932010-11-23 11:40:08 -05001070 char *newcon = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 u32 len;
1072
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001073 length = avc_has_perm(&selinux_state,
1074 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -05001075 SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL,
1076 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 if (length)
Eric Parisb77a4932010-11-23 11:40:08 -05001078 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079
1080 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001081 scon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 if (!scon)
Eric Parisb77a4932010-11-23 11:40:08 -05001083 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084
Eric Parisb77a4932010-11-23 11:40:08 -05001085 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001086 tcon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 if (!tcon)
1088 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089
1090 length = -EINVAL;
1091 if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
Eric Parisb77a4932010-11-23 11:40:08 -05001092 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001094 length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001095 if (length)
1096 goto out;
1097
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001098 length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001099 if (length)
1100 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001102 length = security_change_sid(state, ssid, tsid, tclass, &newsid);
Eric Parisb77a4932010-11-23 11:40:08 -05001103 if (length)
1104 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001106 length = security_sid_to_context(state, newsid, &newcon, &len);
Eric Parisb77a4932010-11-23 11:40:08 -05001107 if (length)
1108 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109
Eric Parisb77a4932010-11-23 11:40:08 -05001110 length = -ERANGE;
1111 if (len > SIMPLE_TRANSACTION_LIMIT)
1112 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113
1114 memcpy(buf, newcon, len);
1115 length = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116out:
Eric Parisb77a4932010-11-23 11:40:08 -05001117 kfree(newcon);
1118 kfree(tcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 kfree(scon);
1120 return length;
1121}
1122
Eric Paris18729812008-04-17 14:15:45 -04001123static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001125 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
1126 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -05001127 char *con = NULL, *user = NULL, *ptr;
1128 u32 sid, *sids = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 ssize_t length;
1130 char *newcon;
1131 int i, rc;
1132 u32 len, nsids;
1133
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001134 length = avc_has_perm(&selinux_state,
1135 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -05001136 SECCLASS_SECURITY, SECURITY__COMPUTE_USER,
1137 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 if (length)
Justin P. Mattock6eab04a2011-04-08 19:49:08 -07001139 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140
1141 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001142 con = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 if (!con)
Justin P. Mattock6eab04a2011-04-08 19:49:08 -07001144 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145
Eric Parisb77a4932010-11-23 11:40:08 -05001146 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001147 user = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 if (!user)
1149 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
1151 length = -EINVAL;
1152 if (sscanf(buf, "%s %s", con, user) != 2)
Eric Parisb77a4932010-11-23 11:40:08 -05001153 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001155 length = security_context_str_to_sid(state, con, &sid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001156 if (length)
1157 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001159 length = security_get_user_sids(state, sid, user, &sids, &nsids);
Eric Parisb77a4932010-11-23 11:40:08 -05001160 if (length)
1161 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
1163 length = sprintf(buf, "%u", nsids) + 1;
1164 ptr = buf + length;
1165 for (i = 0; i < nsids; i++) {
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001166 rc = security_sid_to_context(state, sids[i], &newcon, &len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 if (rc) {
1168 length = rc;
Eric Parisb77a4932010-11-23 11:40:08 -05001169 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 }
1171 if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) {
1172 kfree(newcon);
1173 length = -ERANGE;
Eric Parisb77a4932010-11-23 11:40:08 -05001174 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 }
1176 memcpy(ptr, newcon, len);
1177 kfree(newcon);
1178 ptr += len;
1179 length += len;
1180 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181out:
Eric Parisb77a4932010-11-23 11:40:08 -05001182 kfree(sids);
1183 kfree(user);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 kfree(con);
1185 return length;
1186}
1187
Eric Paris18729812008-04-17 14:15:45 -04001188static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001190 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
1191 struct selinux_state *state = fsi->state;
Eric Parisb77a4932010-11-23 11:40:08 -05001192 char *scon = NULL, *tcon = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 u32 ssid, tsid, newsid;
1194 u16 tclass;
1195 ssize_t length;
Eric Parisb77a4932010-11-23 11:40:08 -05001196 char *newcon = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 u32 len;
1198
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001199 length = avc_has_perm(&selinux_state,
1200 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -05001201 SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER,
1202 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 if (length)
Eric Parisb77a4932010-11-23 11:40:08 -05001204 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205
1206 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001207 scon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 if (!scon)
Justin P. Mattock6eab04a2011-04-08 19:49:08 -07001209 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
Eric Parisb77a4932010-11-23 11:40:08 -05001211 length = -ENOMEM;
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001212 tcon = kzalloc(size + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 if (!tcon)
1214 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
1216 length = -EINVAL;
1217 if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
Eric Parisb77a4932010-11-23 11:40:08 -05001218 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001220 length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001221 if (length)
1222 goto out;
1223
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001224 length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001225 if (length)
1226 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001228 length = security_member_sid(state, ssid, tsid, tclass, &newsid);
Eric Parisb77a4932010-11-23 11:40:08 -05001229 if (length)
1230 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001232 length = security_sid_to_context(state, newsid, &newcon, &len);
Eric Parisb77a4932010-11-23 11:40:08 -05001233 if (length)
1234 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Eric Parisb77a4932010-11-23 11:40:08 -05001236 length = -ERANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 if (len > SIMPLE_TRANSACTION_LIMIT) {
peter enderborgf8b69a52018-06-12 10:09:06 +02001238 pr_err("SELinux: %s: context size (%u) exceeds "
Eric Paris744ba352008-04-17 11:52:44 -04001239 "payload max\n", __func__, len);
Eric Parisb77a4932010-11-23 11:40:08 -05001240 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 }
1242
1243 memcpy(buf, newcon, len);
1244 length = len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245out:
Eric Parisb77a4932010-11-23 11:40:08 -05001246 kfree(newcon);
1247 kfree(tcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 kfree(scon);
1249 return length;
1250}
1251
1252static struct inode *sel_make_inode(struct super_block *sb, int mode)
1253{
1254 struct inode *ret = new_inode(sb);
1255
1256 if (ret) {
1257 ret->i_mode = mode;
Deepa Dinamani078cd822016-09-14 07:48:04 -07001258 ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259 }
1260 return ret;
1261}
1262
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263static ssize_t sel_read_bool(struct file *filep, char __user *buf,
1264 size_t count, loff_t *ppos)
1265{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001266 struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267 char *page = NULL;
1268 ssize_t length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 ssize_t ret;
1270 int cur_enforcing;
Al Viro496ad9a2013-01-23 17:07:38 -05001271 unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK;
Stephen Smalleyd313f94832007-11-26 11:12:53 -05001272 const char *name = filep->f_path.dentry->d_name.name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001274 mutex_lock(&fsi->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Eric Parisb77a4932010-11-23 11:40:08 -05001276 ret = -EINVAL;
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001277 if (index >= fsi->bool_num || strcmp(name,
1278 fsi->bool_pending_names[index]))
Jann Horn0da74122018-06-28 20:39:54 -04001279 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
Eric Parisb77a4932010-11-23 11:40:08 -05001281 ret = -ENOMEM;
Eric Paris18729812008-04-17 14:15:45 -04001282 page = (char *)get_zeroed_page(GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001283 if (!page)
Jann Horn0da74122018-06-28 20:39:54 -04001284 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001286 cur_enforcing = security_get_bool_value(fsi->state, index);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 if (cur_enforcing < 0) {
1288 ret = cur_enforcing;
Jann Horn0da74122018-06-28 20:39:54 -04001289 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing,
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001292 fsi->bool_pending_values[index]);
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001293 mutex_unlock(&fsi->mutex);
Jann Horn0da74122018-06-28 20:39:54 -04001294 ret = simple_read_from_buffer(buf, count, ppos, page, length);
1295out_free:
Eric Parisb77a4932010-11-23 11:40:08 -05001296 free_page((unsigned long)page);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 return ret;
Jann Horn0da74122018-06-28 20:39:54 -04001298
1299out_unlock:
1300 mutex_unlock(&fsi->mutex);
1301 goto out_free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302}
1303
1304static ssize_t sel_write_bool(struct file *filep, const char __user *buf,
1305 size_t count, loff_t *ppos)
1306{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001307 struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 char *page = NULL;
Stephen Smalleyd313f94832007-11-26 11:12:53 -05001309 ssize_t length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 int new_value;
Al Viro496ad9a2013-01-23 17:07:38 -05001311 unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK;
Stephen Smalleyd313f94832007-11-26 11:12:53 -05001312 const char *name = filep->f_path.dentry->d_name.name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313
Jann Horn0da74122018-06-28 20:39:54 -04001314 if (count >= PAGE_SIZE)
1315 return -ENOMEM;
1316
1317 /* No partial writes. */
1318 if (*ppos != 0)
1319 return -EINVAL;
1320
1321 page = memdup_user_nul(buf, count);
1322 if (IS_ERR(page))
1323 return PTR_ERR(page);
1324
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001325 mutex_lock(&fsi->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001327 length = avc_has_perm(&selinux_state,
1328 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -05001329 SECCLASS_SECURITY, SECURITY__SETBOOL,
1330 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 if (length)
1332 goto out;
1333
Eric Parisb77a4932010-11-23 11:40:08 -05001334 length = -EINVAL;
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001335 if (index >= fsi->bool_num || strcmp(name,
1336 fsi->bool_pending_names[index]))
Stephen Smalleyd313f94832007-11-26 11:12:53 -05001337 goto out;
Stephen Smalleyd313f94832007-11-26 11:12:53 -05001338
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 length = -EINVAL;
1340 if (sscanf(page, "%d", &new_value) != 1)
1341 goto out;
1342
1343 if (new_value)
1344 new_value = 1;
1345
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001346 fsi->bool_pending_values[index] = new_value;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 length = count;
1348
1349out:
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001350 mutex_unlock(&fsi->mutex);
Al Viro8365a712015-12-24 00:08:06 -05001351 kfree(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 return length;
1353}
1354
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -08001355static const struct file_operations sel_bool_ops = {
Eric Paris18729812008-04-17 14:15:45 -04001356 .read = sel_read_bool,
1357 .write = sel_write_bool,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001358 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359};
1360
1361static ssize_t sel_commit_bools_write(struct file *filep,
1362 const char __user *buf,
1363 size_t count, loff_t *ppos)
1364{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001365 struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 char *page = NULL;
Stephen Smalleyd313f94832007-11-26 11:12:53 -05001367 ssize_t length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 int new_value;
1369
Jann Horn0da74122018-06-28 20:39:54 -04001370 if (count >= PAGE_SIZE)
1371 return -ENOMEM;
1372
1373 /* No partial writes. */
1374 if (*ppos != 0)
1375 return -EINVAL;
1376
1377 page = memdup_user_nul(buf, count);
1378 if (IS_ERR(page))
1379 return PTR_ERR(page);
1380
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001381 mutex_lock(&fsi->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001383 length = avc_has_perm(&selinux_state,
1384 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -05001385 SECCLASS_SECURITY, SECURITY__SETBOOL,
1386 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387 if (length)
1388 goto out;
1389
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390 length = -EINVAL;
1391 if (sscanf(page, "%d", &new_value) != 1)
1392 goto out;
1393
Eric Parisb77a4932010-11-23 11:40:08 -05001394 length = 0;
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001395 if (new_value && fsi->bool_pending_values)
1396 length = security_set_bools(fsi->state, fsi->bool_num,
1397 fsi->bool_pending_values);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Eric Parisb77a4932010-11-23 11:40:08 -05001399 if (!length)
1400 length = count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
1402out:
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001403 mutex_unlock(&fsi->mutex);
Al Viro8365a712015-12-24 00:08:06 -05001404 kfree(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001405 return length;
1406}
1407
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -08001408static const struct file_operations sel_commit_bools_ops = {
Eric Paris18729812008-04-17 14:15:45 -04001409 .write = sel_commit_bools_write,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001410 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411};
1412
Christopher J. PeBenito0c92d7c2007-05-23 09:12:07 -04001413static void sel_remove_entries(struct dentry *de)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414{
Al Viroad521842014-12-24 14:56:48 -05001415 d_genocide(de);
1416 shrink_dcache_parent(de);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417}
1418
Daniel Burgener66ec3842020-08-19 15:59:33 -04001419static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir,
1420 unsigned int *bool_num, char ***bool_pending_names,
1421 unsigned int **bool_pending_values)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422{
Ondrej Mosnacek60abd312020-02-03 12:27:20 +01001423 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 ssize_t len;
1425 struct dentry *dentry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 struct inode *inode = NULL;
1427 struct inode_security_struct *isec;
1428 char **names = NULL, *page;
Ondrej Mosnacek60abd312020-02-03 12:27:20 +01001429 u32 i, num;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 int *values = NULL;
1431 u32 sid;
1432
Eric Parisb77a4932010-11-23 11:40:08 -05001433 ret = -ENOMEM;
Eric Paris18729812008-04-17 14:15:45 -04001434 page = (char *)get_zeroed_page(GFP_KERNEL);
1435 if (!page)
Eric Parisb77a4932010-11-23 11:40:08 -05001436 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
Stephen Smalley02a52c52020-08-07 09:29:34 -04001438 ret = security_get_bools(newpolicy, &num, &names, &values);
Eric Parisb77a4932010-11-23 11:40:08 -05001439 if (ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 goto out;
1441
1442 for (i = 0; i < num; i++) {
Eric Parisb77a4932010-11-23 11:40:08 -05001443 ret = -ENOMEM;
Daniel Burgener66ec3842020-08-19 15:59:33 -04001444 dentry = d_alloc_name(bool_dir, names[i]);
Eric Parisb77a4932010-11-23 11:40:08 -05001445 if (!dentry)
1446 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
Eric Parisb77a4932010-11-23 11:40:08 -05001448 ret = -ENOMEM;
Daniel Burgener66ec3842020-08-19 15:59:33 -04001449 inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
nixiaoming7e4237f2018-08-05 17:10:36 +08001450 if (!inode) {
1451 dput(dentry);
Eric Parisb77a4932010-11-23 11:40:08 -05001452 goto out;
nixiaoming7e4237f2018-08-05 17:10:36 +08001453 }
Eric Parisb77a4932010-11-23 11:40:08 -05001454
Eric Parisb77a4932010-11-23 11:40:08 -05001455 ret = -ENAMETOOLONG;
Al Virocc1dad72012-04-02 19:40:47 -04001456 len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
nixiaoming7e4237f2018-08-05 17:10:36 +08001457 if (len >= PAGE_SIZE) {
1458 dput(dentry);
1459 iput(inode);
Eric Parisb77a4932010-11-23 11:40:08 -05001460 goto out;
nixiaoming7e4237f2018-08-05 17:10:36 +08001461 }
Eric Parisb77a4932010-11-23 11:40:08 -05001462
Casey Schaufler80788c22018-09-21 17:19:11 -07001463 isec = selinux_inode(inode);
Stephen Smalley02a52c52020-08-07 09:29:34 -04001464 ret = selinux_policy_genfs_sid(newpolicy, "selinuxfs", page,
Stephen Smalleyaa8e7122018-03-01 18:48:02 -05001465 SECCLASS_FILE, &sid);
Gary Tierney4262fb52017-01-09 10:07:31 -05001466 if (ret) {
Gary Tierney900fde02017-01-09 10:07:32 -05001467 pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n",
1468 page);
1469 sid = SECINITSID_SECURITY;
Gary Tierney4262fb52017-01-09 10:07:31 -05001470 }
1471
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 isec->sid = sid;
Andreas Gruenbacher42059112016-11-10 22:18:27 +01001473 isec->initialized = LABEL_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 inode->i_fop = &sel_bool_ops;
James Carterbce34bc2007-04-04 16:18:50 -04001475 inode->i_ino = i|SEL_BOOL_INO_OFFSET;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 d_add(dentry, inode);
1477 }
Daniel Burgener66ec3842020-08-19 15:59:33 -04001478 *bool_num = num;
1479 *bool_pending_names = names;
1480 *bool_pending_values = values;
Eric Parisb77a4932010-11-23 11:40:08 -05001481
1482 free_page((unsigned long)page);
1483 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484out:
1485 free_page((unsigned long)page);
Eric Parisb77a4932010-11-23 11:40:08 -05001486
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 if (names) {
Jesper Juhl9a5f04b2005-06-25 14:58:51 -07001488 for (i = 0; i < num; i++)
1489 kfree(names[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 kfree(names);
1491 }
Davi Arnaut20c19e42005-10-23 12:57:16 -07001492 kfree(values);
Daniel Burgener66ec3842020-08-19 15:59:33 -04001493 sel_remove_entries(bool_dir);
Eric Parisb77a4932010-11-23 11:40:08 -05001494
1495 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496}
1497
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498static ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf,
1499 size_t count, loff_t *ppos)
1500{
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001501 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
1502 struct selinux_state *state = fsi->state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 char tmpbuf[TMPBUFLEN];
1504 ssize_t length;
1505
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001506 length = scnprintf(tmpbuf, TMPBUFLEN, "%u",
1507 avc_get_cache_threshold(state->avc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
1509}
1510
Eric Paris18729812008-04-17 14:15:45 -04001511static ssize_t sel_write_avc_cache_threshold(struct file *file,
1512 const char __user *buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 size_t count, loff_t *ppos)
1514
1515{
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001516 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
1517 struct selinux_state *state = fsi->state;
Al Viro8365a712015-12-24 00:08:06 -05001518 char *page;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 ssize_t ret;
Heinrich Schuchardt309c5fa2016-06-10 23:14:26 +02001520 unsigned int new_value;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001522 ret = avc_has_perm(&selinux_state,
1523 current_sid(), SECINITSID_SECURITY,
Stephen Smalleybe0554c2017-01-09 10:07:31 -05001524 SECCLASS_SECURITY, SECURITY__SETSECPARAM,
1525 NULL);
Eric Parisb77a4932010-11-23 11:40:08 -05001526 if (ret)
Al Viro8365a712015-12-24 00:08:06 -05001527 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
Eric Parisb77a4932010-11-23 11:40:08 -05001529 if (count >= PAGE_SIZE)
Al Viro8365a712015-12-24 00:08:06 -05001530 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Eric Parisb77a4932010-11-23 11:40:08 -05001532 /* No partial writes. */
Eric Parisb77a4932010-11-23 11:40:08 -05001533 if (*ppos != 0)
Al Viro8365a712015-12-24 00:08:06 -05001534 return -EINVAL;
Eric Parisb77a4932010-11-23 11:40:08 -05001535
Al Viro8365a712015-12-24 00:08:06 -05001536 page = memdup_user_nul(buf, count);
1537 if (IS_ERR(page))
1538 return PTR_ERR(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
Eric Parisb77a4932010-11-23 11:40:08 -05001540 ret = -EINVAL;
1541 if (sscanf(page, "%u", &new_value) != 1)
1542 goto out;
1543
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001544 avc_set_cache_threshold(state->avc, new_value);
Eric Parisb77a4932010-11-23 11:40:08 -05001545
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 ret = count;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547out:
Al Viro8365a712015-12-24 00:08:06 -05001548 kfree(page);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549 return ret;
1550}
1551
1552static ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf,
1553 size_t count, loff_t *ppos)
1554{
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001555 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
1556 struct selinux_state *state = fsi->state;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 char *page;
Eric Parisb77a4932010-11-23 11:40:08 -05001558 ssize_t length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 page = (char *)__get_free_page(GFP_KERNEL);
Eric Parisb77a4932010-11-23 11:40:08 -05001561 if (!page)
1562 return -ENOMEM;
1563
Stephen Smalley6b6bc622018-03-05 11:47:56 -05001564 length = avc_get_hash_stats(state->avc, page);
Eric Parisb77a4932010-11-23 11:40:08 -05001565 if (length >= 0)
1566 length = simple_read_from_buffer(buf, count, ppos, page, length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 free_page((unsigned long)page);
Eric Parisb77a4932010-11-23 11:40:08 -05001568
1569 return length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570}
1571
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +01001572static ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf,
1573 size_t count, loff_t *ppos)
1574{
1575 struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info;
1576 struct selinux_state *state = fsi->state;
1577 char *page;
1578 ssize_t length;
1579
1580 page = (char *)__get_free_page(GFP_KERNEL);
1581 if (!page)
1582 return -ENOMEM;
1583
1584 length = security_sidtab_hash_stats(state, page);
1585 if (length >= 0)
1586 length = simple_read_from_buffer(buf, count, ppos, page,
1587 length);
1588 free_page((unsigned long)page);
1589
1590 return length;
1591}
1592
1593static const struct file_operations sel_sidtab_hash_stats_ops = {
1594 .read = sel_read_sidtab_hash_stats,
1595 .llseek = generic_file_llseek,
1596};
1597
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -08001598static const struct file_operations sel_avc_cache_threshold_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 .read = sel_read_avc_cache_threshold,
1600 .write = sel_write_avc_cache_threshold,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001601 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602};
1603
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -08001604static const struct file_operations sel_avc_hash_stats_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 .read = sel_read_avc_hash_stats,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001606 .llseek = generic_file_llseek,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607};
1608
1609#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
1610static struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx)
1611{
1612 int cpu;
1613
Rusty Russell4f4b6c12009-01-01 10:12:15 +10301614 for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 if (!cpu_possible(cpu))
1616 continue;
1617 *idx = cpu + 1;
1618 return &per_cpu(avc_cache_stats, cpu);
1619 }
Vasily Averin8d269a82020-02-01 10:47:47 +03001620 (*idx)++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 return NULL;
1622}
1623
1624static void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos)
1625{
1626 loff_t n = *pos - 1;
1627
1628 if (*pos == 0)
1629 return SEQ_START_TOKEN;
1630
1631 return sel_avc_get_stat_idx(&n);
1632}
1633
1634static void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos)
1635{
1636 return sel_avc_get_stat_idx(pos);
1637}
1638
1639static int sel_avc_stats_seq_show(struct seq_file *seq, void *v)
1640{
1641 struct avc_cache_stats *st = v;
1642
Markus Elfring710a0642017-01-15 14:04:53 +01001643 if (v == SEQ_START_TOKEN) {
1644 seq_puts(seq,
1645 "lookups hits misses allocations reclaims frees\n");
1646 } else {
Linus Torvalds257313b2011-05-19 21:22:53 -07001647 unsigned int lookups = st->lookups;
1648 unsigned int misses = st->misses;
1649 unsigned int hits = lookups - misses;
1650 seq_printf(seq, "%u %u %u %u %u %u\n", lookups,
1651 hits, misses, st->allocations,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 st->reclaims, st->frees);
Linus Torvalds257313b2011-05-19 21:22:53 -07001653 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 return 0;
1655}
1656
1657static void sel_avc_stats_seq_stop(struct seq_file *seq, void *v)
1658{ }
1659
Jan Engelhardt1996a102008-01-23 00:02:58 +01001660static const struct seq_operations sel_avc_cache_stats_seq_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 .start = sel_avc_stats_seq_start,
1662 .next = sel_avc_stats_seq_next,
1663 .show = sel_avc_stats_seq_show,
1664 .stop = sel_avc_stats_seq_stop,
1665};
1666
1667static int sel_open_avc_cache_stats(struct inode *inode, struct file *file)
1668{
1669 return seq_open(file, &sel_avc_cache_stats_seq_ops);
1670}
1671
Arjan van de Ven9c2e08c2007-02-12 00:55:37 -08001672static const struct file_operations sel_avc_cache_stats_ops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 .open = sel_open_avc_cache_stats,
1674 .read = seq_read,
1675 .llseek = seq_lseek,
1676 .release = seq_release,
1677};
1678#endif
1679
1680static int sel_make_avc_files(struct dentry *dir)
1681{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001682 struct super_block *sb = dir->d_sb;
1683 struct selinux_fs_info *fsi = sb->s_fs_info;
Eric Parisb77a4932010-11-23 11:40:08 -05001684 int i;
Eric Biggerscda37122017-03-25 21:15:37 -07001685 static const struct tree_descr files[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 { "cache_threshold",
1687 &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR },
1688 { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO },
1689#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
1690 { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO },
1691#endif
1692 };
1693
Nicolas Kaiser6e20a642006-01-06 00:11:22 -08001694 for (i = 0; i < ARRAY_SIZE(files); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 struct inode *inode;
1696 struct dentry *dentry;
1697
1698 dentry = d_alloc_name(dir, files[i].name);
Eric Parisb77a4932010-11-23 11:40:08 -05001699 if (!dentry)
1700 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701
1702 inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
nixiaoming7e4237f2018-08-05 17:10:36 +08001703 if (!inode) {
1704 dput(dentry);
Eric Parisb77a4932010-11-23 11:40:08 -05001705 return -ENOMEM;
nixiaoming7e4237f2018-08-05 17:10:36 +08001706 }
Eric Parisb77a4932010-11-23 11:40:08 -05001707
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 inode->i_fop = files[i].ops;
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001709 inode->i_ino = ++fsi->last_ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 d_add(dentry, inode);
1711 }
Eric Parisb77a4932010-11-23 11:40:08 -05001712
1713 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714}
1715
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +01001716static int sel_make_ss_files(struct dentry *dir)
1717{
1718 struct super_block *sb = dir->d_sb;
1719 struct selinux_fs_info *fsi = sb->s_fs_info;
1720 int i;
1721 static struct tree_descr files[] = {
1722 { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO },
1723 };
1724
1725 for (i = 0; i < ARRAY_SIZE(files); i++) {
1726 struct inode *inode;
1727 struct dentry *dentry;
1728
1729 dentry = d_alloc_name(dir, files[i].name);
1730 if (!dentry)
1731 return -ENOMEM;
1732
1733 inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
1734 if (!inode) {
1735 dput(dentry);
1736 return -ENOMEM;
1737 }
1738
1739 inode->i_fop = files[i].ops;
1740 inode->i_ino = ++fsi->last_ino;
1741 d_add(dentry, inode);
1742 }
1743
1744 return 0;
1745}
1746
Eric Paris18729812008-04-17 14:15:45 -04001747static ssize_t sel_read_initcon(struct file *file, char __user *buf,
James Carterf0ee2e42007-04-04 10:11:29 -04001748 size_t count, loff_t *ppos)
1749{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001750 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
James Carterf0ee2e42007-04-04 10:11:29 -04001751 char *con;
1752 u32 sid, len;
1753 ssize_t ret;
1754
Al Viro496ad9a2013-01-23 17:07:38 -05001755 sid = file_inode(file)->i_ino&SEL_INO_MASK;
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001756 ret = security_sid_to_context(fsi->state, sid, &con, &len);
Eric Parisb77a4932010-11-23 11:40:08 -05001757 if (ret)
James Carterf0ee2e42007-04-04 10:11:29 -04001758 return ret;
1759
1760 ret = simple_read_from_buffer(buf, count, ppos, con, len);
1761 kfree(con);
1762 return ret;
1763}
1764
1765static const struct file_operations sel_initcon_ops = {
1766 .read = sel_read_initcon,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001767 .llseek = generic_file_llseek,
James Carterf0ee2e42007-04-04 10:11:29 -04001768};
1769
1770static int sel_make_initcon_files(struct dentry *dir)
1771{
Eric Parisb77a4932010-11-23 11:40:08 -05001772 int i;
James Carterf0ee2e42007-04-04 10:11:29 -04001773
1774 for (i = 1; i <= SECINITSID_NUM; i++) {
1775 struct inode *inode;
1776 struct dentry *dentry;
Stephen Smalleye3e0b582020-02-24 11:10:23 -05001777 const char *s = security_get_initial_sid_context(i);
1778
1779 if (!s)
1780 continue;
1781 dentry = d_alloc_name(dir, s);
Eric Parisb77a4932010-11-23 11:40:08 -05001782 if (!dentry)
1783 return -ENOMEM;
James Carterf0ee2e42007-04-04 10:11:29 -04001784
1785 inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
nixiaoming7e4237f2018-08-05 17:10:36 +08001786 if (!inode) {
1787 dput(dentry);
Eric Parisb77a4932010-11-23 11:40:08 -05001788 return -ENOMEM;
nixiaoming7e4237f2018-08-05 17:10:36 +08001789 }
Eric Parisb77a4932010-11-23 11:40:08 -05001790
James Carterf0ee2e42007-04-04 10:11:29 -04001791 inode->i_fop = &sel_initcon_ops;
1792 inode->i_ino = i|SEL_INITCON_INO_OFFSET;
1793 d_add(dentry, inode);
1794 }
Eric Parisb77a4932010-11-23 11:40:08 -05001795
1796 return 0;
James Carterf0ee2e42007-04-04 10:11:29 -04001797}
1798
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001799static inline unsigned long sel_class_to_ino(u16 class)
1800{
1801 return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET;
1802}
1803
1804static inline u16 sel_ino_to_class(unsigned long ino)
1805{
Eric Paris92ae9e82012-04-04 13:46:46 -04001806 return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001807}
1808
1809static inline unsigned long sel_perm_to_ino(u16 class, u32 perm)
1810{
1811 return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET;
1812}
1813
1814static inline u32 sel_ino_to_perm(unsigned long ino)
1815{
1816 return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1);
1817}
1818
Eric Paris18729812008-04-17 14:15:45 -04001819static ssize_t sel_read_class(struct file *file, char __user *buf,
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001820 size_t count, loff_t *ppos)
1821{
Al Viro496ad9a2013-01-23 17:07:38 -05001822 unsigned long ino = file_inode(file)->i_ino;
Al Virocc1dad72012-04-02 19:40:47 -04001823 char res[TMPBUFLEN];
liuyang347e78c872020-01-07 09:39:18 +08001824 ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_class(ino));
Al Virocc1dad72012-04-02 19:40:47 -04001825 return simple_read_from_buffer(buf, count, ppos, res, len);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001826}
1827
1828static const struct file_operations sel_class_ops = {
1829 .read = sel_read_class,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001830 .llseek = generic_file_llseek,
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001831};
1832
Eric Paris18729812008-04-17 14:15:45 -04001833static ssize_t sel_read_perm(struct file *file, char __user *buf,
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001834 size_t count, loff_t *ppos)
1835{
Al Viro496ad9a2013-01-23 17:07:38 -05001836 unsigned long ino = file_inode(file)->i_ino;
Al Virocc1dad72012-04-02 19:40:47 -04001837 char res[TMPBUFLEN];
liuyang347e78c872020-01-07 09:39:18 +08001838 ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino));
Al Virocc1dad72012-04-02 19:40:47 -04001839 return simple_read_from_buffer(buf, count, ppos, res, len);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001840}
1841
1842static const struct file_operations sel_perm_ops = {
1843 .read = sel_read_perm,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001844 .llseek = generic_file_llseek,
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001845};
1846
Paul Moore3bb56b22008-01-29 08:38:19 -05001847static ssize_t sel_read_policycap(struct file *file, char __user *buf,
1848 size_t count, loff_t *ppos)
1849{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001850 struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info;
Paul Moore3bb56b22008-01-29 08:38:19 -05001851 int value;
1852 char tmpbuf[TMPBUFLEN];
1853 ssize_t length;
Al Viro496ad9a2013-01-23 17:07:38 -05001854 unsigned long i_ino = file_inode(file)->i_ino;
Paul Moore3bb56b22008-01-29 08:38:19 -05001855
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001856 value = security_policycap_supported(fsi->state, i_ino & SEL_INO_MASK);
Paul Moore3bb56b22008-01-29 08:38:19 -05001857 length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value);
1858
1859 return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
1860}
1861
1862static const struct file_operations sel_policycap_ops = {
1863 .read = sel_read_policycap,
Arnd Bergmann57a62c22010-07-07 23:40:10 +02001864 .llseek = generic_file_llseek,
Paul Moore3bb56b22008-01-29 08:38:19 -05001865};
1866
Stephen Smalley02a52c52020-08-07 09:29:34 -04001867static int sel_make_perm_files(struct selinux_policy *newpolicy,
1868 char *objclass, int classvalue,
1869 struct dentry *dir)
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001870{
Eric Parisb77a4932010-11-23 11:40:08 -05001871 int i, rc, nperms;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001872 char **perms;
1873
Stephen Smalley02a52c52020-08-07 09:29:34 -04001874 rc = security_get_permissions(newpolicy, objclass, &perms, &nperms);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001875 if (rc)
Eric Parisb77a4932010-11-23 11:40:08 -05001876 return rc;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001877
1878 for (i = 0; i < nperms; i++) {
1879 struct inode *inode;
1880 struct dentry *dentry;
1881
Eric Parisb77a4932010-11-23 11:40:08 -05001882 rc = -ENOMEM;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001883 dentry = d_alloc_name(dir, perms[i]);
Eric Parisb77a4932010-11-23 11:40:08 -05001884 if (!dentry)
1885 goto out;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001886
Eric Parisb77a4932010-11-23 11:40:08 -05001887 rc = -ENOMEM;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001888 inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
nixiaoming7e4237f2018-08-05 17:10:36 +08001889 if (!inode) {
1890 dput(dentry);
Eric Parisb77a4932010-11-23 11:40:08 -05001891 goto out;
nixiaoming7e4237f2018-08-05 17:10:36 +08001892 }
Eric Parisb77a4932010-11-23 11:40:08 -05001893
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001894 inode->i_fop = &sel_perm_ops;
1895 /* i+1 since perm values are 1-indexed */
wzt.wzt@gmail.comc1a73682010-04-09 19:30:29 +08001896 inode->i_ino = sel_perm_to_ino(classvalue, i + 1);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001897 d_add(dentry, inode);
1898 }
Eric Parisb77a4932010-11-23 11:40:08 -05001899 rc = 0;
1900out:
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001901 for (i = 0; i < nperms; i++)
1902 kfree(perms[i]);
1903 kfree(perms);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001904 return rc;
1905}
1906
Stephen Smalley02a52c52020-08-07 09:29:34 -04001907static int sel_make_class_dir_entries(struct selinux_policy *newpolicy,
1908 char *classname, int index,
1909 struct dentry *dir)
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001910{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001911 struct super_block *sb = dir->d_sb;
1912 struct selinux_fs_info *fsi = sb->s_fs_info;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001913 struct dentry *dentry = NULL;
1914 struct inode *inode = NULL;
1915 int rc;
1916
1917 dentry = d_alloc_name(dir, "index");
Eric Parisb77a4932010-11-23 11:40:08 -05001918 if (!dentry)
1919 return -ENOMEM;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001920
1921 inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO);
nixiaoming7e4237f2018-08-05 17:10:36 +08001922 if (!inode) {
1923 dput(dentry);
Eric Parisb77a4932010-11-23 11:40:08 -05001924 return -ENOMEM;
nixiaoming7e4237f2018-08-05 17:10:36 +08001925 }
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001926
1927 inode->i_fop = &sel_class_ops;
1928 inode->i_ino = sel_class_to_ino(index);
1929 d_add(dentry, inode);
1930
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001931 dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino);
Al Viroa1c2aa12012-03-18 20:36:59 -04001932 if (IS_ERR(dentry))
1933 return PTR_ERR(dentry);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001934
Stephen Smalley02a52c52020-08-07 09:29:34 -04001935 rc = sel_make_perm_files(newpolicy, classname, index, dentry);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001936
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001937 return rc;
1938}
1939
Daniel Burgener66ec3842020-08-19 15:59:33 -04001940static int sel_make_classes(struct selinux_policy *newpolicy,
1941 struct dentry *class_dir,
1942 unsigned long *last_class_ino)
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001943{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001944
Eric Parisb77a4932010-11-23 11:40:08 -05001945 int rc, nclasses, i;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001946 char **classes;
1947
Stephen Smalley02a52c52020-08-07 09:29:34 -04001948 rc = security_get_classes(newpolicy, &classes, &nclasses);
Eric Parisb77a4932010-11-23 11:40:08 -05001949 if (rc)
1950 return rc;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001951
1952 /* +2 since classes are 1-indexed */
Daniel Burgener66ec3842020-08-19 15:59:33 -04001953 *last_class_ino = sel_class_to_ino(nclasses + 2);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001954
1955 for (i = 0; i < nclasses; i++) {
1956 struct dentry *class_name_dir;
1957
Daniel Burgener66ec3842020-08-19 15:59:33 -04001958 class_name_dir = sel_make_dir(class_dir, classes[i],
1959 last_class_ino);
Al Viroa1c2aa12012-03-18 20:36:59 -04001960 if (IS_ERR(class_name_dir)) {
1961 rc = PTR_ERR(class_name_dir);
Eric Parisb77a4932010-11-23 11:40:08 -05001962 goto out;
Al Viroa1c2aa12012-03-18 20:36:59 -04001963 }
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001964
1965 /* i+1 since class values are 1-indexed */
Stephen Smalley02a52c52020-08-07 09:29:34 -04001966 rc = sel_make_class_dir_entries(newpolicy, classes[i], i + 1,
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001967 class_name_dir);
1968 if (rc)
Eric Parisb77a4932010-11-23 11:40:08 -05001969 goto out;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001970 }
Eric Parisb77a4932010-11-23 11:40:08 -05001971 rc = 0;
1972out:
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001973 for (i = 0; i < nclasses; i++)
1974 kfree(classes[i]);
1975 kfree(classes);
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04001976 return rc;
1977}
1978
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001979static int sel_make_policycap(struct selinux_fs_info *fsi)
Paul Moore3bb56b22008-01-29 08:38:19 -05001980{
1981 unsigned int iter;
1982 struct dentry *dentry = NULL;
1983 struct inode *inode = NULL;
1984
Paul Moore3bb56b22008-01-29 08:38:19 -05001985 for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) {
Stephen Smalley4dc2fce2017-05-18 16:58:31 -04001986 if (iter < ARRAY_SIZE(selinux_policycap_names))
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001987 dentry = d_alloc_name(fsi->policycap_dir,
Stephen Smalley4dc2fce2017-05-18 16:58:31 -04001988 selinux_policycap_names[iter]);
Paul Moore3bb56b22008-01-29 08:38:19 -05001989 else
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001990 dentry = d_alloc_name(fsi->policycap_dir, "unknown");
Paul Moore3bb56b22008-01-29 08:38:19 -05001991
1992 if (dentry == NULL)
1993 return -ENOMEM;
1994
Stephen Smalley0619f0f2018-03-20 11:59:11 -04001995 inode = sel_make_inode(fsi->sb, S_IFREG | 0444);
nixiaoming7e4237f2018-08-05 17:10:36 +08001996 if (inode == NULL) {
1997 dput(dentry);
Paul Moore3bb56b22008-01-29 08:38:19 -05001998 return -ENOMEM;
nixiaoming7e4237f2018-08-05 17:10:36 +08001999 }
Paul Moore3bb56b22008-01-29 08:38:19 -05002000
2001 inode->i_fop = &sel_policycap_ops;
2002 inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET;
2003 d_add(dentry, inode);
2004 }
2005
2006 return 0;
2007}
2008
Al Viroa1c2aa12012-03-18 20:36:59 -04002009static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
Christopher J. PeBenito0dd4ae52007-05-23 09:12:08 -04002010 unsigned long *ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011{
Al Viroa1c2aa12012-03-18 20:36:59 -04002012 struct dentry *dentry = d_alloc_name(dir, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 struct inode *inode;
2014
Al Viroa1c2aa12012-03-18 20:36:59 -04002015 if (!dentry)
2016 return ERR_PTR(-ENOMEM);
2017
2018 inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO);
2019 if (!inode) {
2020 dput(dentry);
2021 return ERR_PTR(-ENOMEM);
2022 }
Eric Parisb77a4932010-11-23 11:40:08 -05002023
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 inode->i_op = &simple_dir_inode_operations;
2025 inode->i_fop = &simple_dir_operations;
Christopher J. PeBenito0dd4ae52007-05-23 09:12:08 -04002026 inode->i_ino = ++(*ino);
James Morris40e906f2006-03-22 00:09:16 -08002027 /* directory inodes start off with i_nlink == 2 (for "." entry) */
Dave Hansend8c76e62006-09-30 23:29:04 -07002028 inc_nlink(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 d_add(dentry, inode);
James Morrisedb20fb2006-03-22 00:09:20 -08002030 /* bump link count on parent directory, too */
David Howellsce0b16d2015-02-19 10:47:02 +00002031 inc_nlink(d_inode(dir));
Eric Parisb77a4932010-11-23 11:40:08 -05002032
Al Viroa1c2aa12012-03-18 20:36:59 -04002033 return dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034}
2035
Daniel Burgener0eea6092020-08-19 15:59:35 -04002036static struct dentry *sel_make_disconnected_dir(struct super_block *sb,
2037 unsigned long *ino)
2038{
2039 struct inode *inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
2040
2041 if (!inode)
2042 return ERR_PTR(-ENOMEM);
2043
2044 inode->i_op = &simple_dir_inode_operations;
2045 inode->i_fop = &simple_dir_operations;
2046 inode->i_ino = ++(*ino);
2047 /* directory inodes start off with i_nlink == 2 (for "." entry) */
2048 inc_nlink(inode);
2049 return d_obtain_alias(inode);
2050}
2051
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002052#define NULL_FILE_NAME "null"
2053
David Howells920f50b2019-03-25 16:38:30 +00002054static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002056 struct selinux_fs_info *fsi;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 int ret;
2058 struct dentry *dentry;
Al Viroa1c2aa12012-03-18 20:36:59 -04002059 struct inode *inode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 struct inode_security_struct *isec;
2061
Eric Biggerscda37122017-03-25 21:15:37 -07002062 static const struct tree_descr selinux_files[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR},
2064 [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR},
Stephen Smalleyce9982d2005-11-08 21:34:33 -08002065 [SEL_CONTEXT] = {"context", &transaction_ops, S_IRUGO|S_IWUGO},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO},
2067 [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO},
2068 [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO},
2069 [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO},
2070 [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO},
2071 [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR},
2072 [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO},
2073 [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR},
2074 [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO},
2075 [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
Eric Paris3f120702007-09-21 14:37:10 -04002076 [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
2077 [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
KaiGai Kohei11904162010-09-14 18:28:39 +09002078 [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
Eric Paris72e8c8592012-02-16 15:08:39 -05002079 [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
Andrew Perepechkof9df6452015-12-24 11:09:41 -05002080 [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
2081 S_IWUGO},
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 /* last one */ {""}
2083 };
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002084
2085 ret = selinux_fs_info_create(sb);
2086 if (ret)
2087 goto err;
2088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files);
2090 if (ret)
James Morris161ce452006-03-22 00:09:17 -08002091 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002093 fsi = sb->s_fs_info;
2094 fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino);
2095 if (IS_ERR(fsi->bool_dir)) {
2096 ret = PTR_ERR(fsi->bool_dir);
2097 fsi->bool_dir = NULL;
James Morris161ce452006-03-22 00:09:17 -08002098 goto err;
Al Viroa1c2aa12012-03-18 20:36:59 -04002099 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
Eric Parisb77a4932010-11-23 11:40:08 -05002101 ret = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME);
Eric Parisb77a4932010-11-23 11:40:08 -05002103 if (!dentry)
James Morris161ce452006-03-22 00:09:17 -08002104 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105
Eric Parisb77a4932010-11-23 11:40:08 -05002106 ret = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO);
nixiaoming7e4237f2018-08-05 17:10:36 +08002108 if (!inode) {
2109 dput(dentry);
James Morris161ce452006-03-22 00:09:17 -08002110 goto err;
nixiaoming7e4237f2018-08-05 17:10:36 +08002111 }
Eric Parisb77a4932010-11-23 11:40:08 -05002112
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002113 inode->i_ino = ++fsi->last_ino;
Casey Schaufler80788c22018-09-21 17:19:11 -07002114 isec = selinux_inode(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 isec->sid = SECINITSID_DEVNULL;
2116 isec->sclass = SECCLASS_CHR_FILE;
Andreas Gruenbacher42059112016-11-10 22:18:27 +01002117 isec->initialized = LABEL_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118
2119 init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
2120 d_add(dentry, inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002122 dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino);
Al Viroa1c2aa12012-03-18 20:36:59 -04002123 if (IS_ERR(dentry)) {
2124 ret = PTR_ERR(dentry);
James Morris161ce452006-03-22 00:09:17 -08002125 goto err;
Al Viroa1c2aa12012-03-18 20:36:59 -04002126 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127
2128 ret = sel_make_avc_files(dentry);
Jeff Vander Stoep66f8e2f2019-11-22 10:33:06 +01002129
2130 dentry = sel_make_dir(sb->s_root, "ss", &fsi->last_ino);
2131 if (IS_ERR(dentry)) {
2132 ret = PTR_ERR(dentry);
2133 goto err;
2134 }
2135
2136 ret = sel_make_ss_files(dentry);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 if (ret)
James Morris161ce452006-03-22 00:09:17 -08002138 goto err;
James Carterf0ee2e42007-04-04 10:11:29 -04002139
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002140 dentry = sel_make_dir(sb->s_root, "initial_contexts", &fsi->last_ino);
Al Viroa1c2aa12012-03-18 20:36:59 -04002141 if (IS_ERR(dentry)) {
2142 ret = PTR_ERR(dentry);
James Carterf0ee2e42007-04-04 10:11:29 -04002143 goto err;
Al Viroa1c2aa12012-03-18 20:36:59 -04002144 }
James Carterf0ee2e42007-04-04 10:11:29 -04002145
2146 ret = sel_make_initcon_files(dentry);
2147 if (ret)
2148 goto err;
2149
Daniel Burgener613ba182020-08-19 15:59:34 -04002150 fsi->class_dir = sel_make_dir(sb->s_root, CLASS_DIR_NAME, &fsi->last_ino);
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002151 if (IS_ERR(fsi->class_dir)) {
2152 ret = PTR_ERR(fsi->class_dir);
2153 fsi->class_dir = NULL;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04002154 goto err;
Al Viroa1c2aa12012-03-18 20:36:59 -04002155 }
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04002156
Daniel Burgener613ba182020-08-19 15:59:34 -04002157 fsi->policycap_dir = sel_make_dir(sb->s_root, POLICYCAP_DIR_NAME,
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002158 &fsi->last_ino);
2159 if (IS_ERR(fsi->policycap_dir)) {
2160 ret = PTR_ERR(fsi->policycap_dir);
2161 fsi->policycap_dir = NULL;
Christopher J. PeBenitoe47c8fc52007-05-23 09:12:09 -04002162 goto err;
Al Viroa1c2aa12012-03-18 20:36:59 -04002163 }
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002164
Stephen Smalley02a52c52020-08-07 09:29:34 -04002165 ret = sel_make_policycap(fsi);
2166 if (ret) {
2167 pr_err("SELinux: failed to load policy capabilities\n");
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002168 goto err;
Stephen Smalley02a52c52020-08-07 09:29:34 -04002169 }
2170
Eric Parisb77a4932010-11-23 11:40:08 -05002171 return 0;
James Morris161ce452006-03-22 00:09:17 -08002172err:
peter enderborgf8b69a52018-06-12 10:09:06 +02002173 pr_err("SELinux: %s: failed while creating inodes\n",
Eric Paris744ba352008-04-17 11:52:44 -04002174 __func__);
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002175
2176 selinux_fs_info_free(sb);
2177
Eric Parisb77a4932010-11-23 11:40:08 -05002178 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179}
2180
David Howells920f50b2019-03-25 16:38:30 +00002181static int sel_get_tree(struct fs_context *fc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182{
David Howells920f50b2019-03-25 16:38:30 +00002183 return get_tree_single(fc, sel_fill_super);
2184}
2185
2186static const struct fs_context_operations sel_context_ops = {
2187 .get_tree = sel_get_tree,
2188};
2189
2190static int sel_init_fs_context(struct fs_context *fc)
2191{
2192 fc->ops = &sel_context_ops;
2193 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194}
2195
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002196static void sel_kill_sb(struct super_block *sb)
2197{
2198 selinux_fs_info_free(sb);
2199 kill_litter_super(sb);
2200}
2201
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202static struct file_system_type sel_fs_type = {
2203 .name = "selinuxfs",
David Howells920f50b2019-03-25 16:38:30 +00002204 .init_fs_context = sel_init_fs_context,
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002205 .kill_sb = sel_kill_sb,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206};
2207
2208struct vfsmount *selinuxfs_mount;
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002209struct path selinux_null;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210
2211static int __init init_sel_fs(void)
2212{
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002213 struct qstr null_name = QSTR_INIT(NULL_FILE_NAME,
2214 sizeof(NULL_FILE_NAME)-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215 int err;
2216
Stephen Smalley6c5a6822019-12-17 09:15:10 -05002217 if (!selinux_enabled_boot)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 return 0;
Greg Kroah-Hartman7a627e32011-05-10 15:34:16 -07002219
Eric W. Biedermanf9bb4882015-05-13 17:35:41 -05002220 err = sysfs_create_mount_point(fs_kobj, "selinux");
2221 if (err)
2222 return err;
Greg Kroah-Hartman7a627e32011-05-10 15:34:16 -07002223
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 err = register_filesystem(&sel_fs_type);
Greg Kroah-Hartman7a627e32011-05-10 15:34:16 -07002225 if (err) {
Eric W. Biedermanf9bb4882015-05-13 17:35:41 -05002226 sysfs_remove_mount_point(fs_kobj, "selinux");
Eric Parisb77a4932010-11-23 11:40:08 -05002227 return err;
Greg Kroah-Hartman7a627e32011-05-10 15:34:16 -07002228 }
Eric Parisb77a4932010-11-23 11:40:08 -05002229
Al Viro765927b2012-06-26 21:58:53 +04002230 selinux_null.mnt = selinuxfs_mount = kern_mount(&sel_fs_type);
Eric Parisb77a4932010-11-23 11:40:08 -05002231 if (IS_ERR(selinuxfs_mount)) {
peter enderborgf8b69a52018-06-12 10:09:06 +02002232 pr_err("selinuxfs: could not mount!\n");
Eric Parisb77a4932010-11-23 11:40:08 -05002233 err = PTR_ERR(selinuxfs_mount);
2234 selinuxfs_mount = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 }
Stephen Smalley0619f0f2018-03-20 11:59:11 -04002236 selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root,
2237 &null_name);
2238 if (IS_ERR(selinux_null.dentry)) {
2239 pr_err("selinuxfs: could not lookup null!\n");
2240 err = PTR_ERR(selinux_null.dentry);
2241 selinux_null.dentry = NULL;
2242 }
Eric Parisb77a4932010-11-23 11:40:08 -05002243
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 return err;
2245}
2246
2247__initcall(init_sel_fs);
2248
2249#ifdef CONFIG_SECURITY_SELINUX_DISABLE
2250void exit_sel_fs(void)
2251{
Eric W. Biedermanf9bb4882015-05-13 17:35:41 -05002252 sysfs_remove_mount_point(fs_kobj, "selinux");
Stephen Smalleyfd40ffc2018-04-09 14:36:05 -04002253 dput(selinux_null.dentry);
Tim Chen423e0ab2011-07-19 09:32:38 -07002254 kern_unmount(selinuxfs_mount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 unregister_filesystem(&sel_fs_type);
2256}
2257#endif