18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Updated: Karl MacMillan <kmacmillan@tresys.com> 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Added conditional policy language extensions 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Updated: Hewlett-Packard <paul@paul-moore.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Added support for the policy capability bitmap 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2007 Hewlett-Packard Development Company, L.P. 118c2ecf20Sopenharmony_ci * Copyright (C) 2003 - 2004 Tresys Technology, LLC 128c2ecf20Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com> 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 198c2ecf20Sopenharmony_ci#include <linux/fs.h> 208c2ecf20Sopenharmony_ci#include <linux/fs_context.h> 218c2ecf20Sopenharmony_ci#include <linux/mount.h> 228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 238c2ecf20Sopenharmony_ci#include <linux/namei.h> 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/string.h> 268c2ecf20Sopenharmony_ci#include <linux/security.h> 278c2ecf20Sopenharmony_ci#include <linux/major.h> 288c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 298c2ecf20Sopenharmony_ci#include <linux/percpu.h> 308c2ecf20Sopenharmony_ci#include <linux/audit.h> 318c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 328c2ecf20Sopenharmony_ci#include <linux/kobject.h> 338c2ecf20Sopenharmony_ci#include <linux/ctype.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* selinuxfs pseudo filesystem for exporting the security policy API. 368c2ecf20Sopenharmony_ci Based on the proc code and the fs/nfsd/nfsctl.c code. */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "flask.h" 398c2ecf20Sopenharmony_ci#include "avc.h" 408c2ecf20Sopenharmony_ci#include "avc_ss.h" 418c2ecf20Sopenharmony_ci#include "security.h" 428c2ecf20Sopenharmony_ci#include "objsec.h" 438c2ecf20Sopenharmony_ci#include "conditional.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cienum sel_inos { 468c2ecf20Sopenharmony_ci SEL_ROOT_INO = 2, 478c2ecf20Sopenharmony_ci SEL_LOAD, /* load policy */ 488c2ecf20Sopenharmony_ci SEL_ENFORCE, /* get or set enforcing status */ 498c2ecf20Sopenharmony_ci SEL_CONTEXT, /* validate context */ 508c2ecf20Sopenharmony_ci SEL_ACCESS, /* compute access decision */ 518c2ecf20Sopenharmony_ci SEL_CREATE, /* compute create labeling decision */ 528c2ecf20Sopenharmony_ci SEL_RELABEL, /* compute relabeling decision */ 538c2ecf20Sopenharmony_ci SEL_USER, /* compute reachable user contexts */ 548c2ecf20Sopenharmony_ci SEL_POLICYVERS, /* return policy version for this kernel */ 558c2ecf20Sopenharmony_ci SEL_COMMIT_BOOLS, /* commit new boolean values */ 568c2ecf20Sopenharmony_ci SEL_MLS, /* return if MLS policy is enabled */ 578c2ecf20Sopenharmony_ci SEL_DISABLE, /* disable SELinux until next reboot */ 588c2ecf20Sopenharmony_ci SEL_MEMBER, /* compute polyinstantiation membership decision */ 598c2ecf20Sopenharmony_ci SEL_CHECKREQPROT, /* check requested protection, not kernel-applied one */ 608c2ecf20Sopenharmony_ci SEL_COMPAT_NET, /* whether to use old compat network packet controls */ 618c2ecf20Sopenharmony_ci SEL_REJECT_UNKNOWN, /* export unknown reject handling to userspace */ 628c2ecf20Sopenharmony_ci SEL_DENY_UNKNOWN, /* export unknown deny handling to userspace */ 638c2ecf20Sopenharmony_ci SEL_STATUS, /* export current status using mmap() */ 648c2ecf20Sopenharmony_ci SEL_POLICY, /* allow userspace to read the in kernel policy */ 658c2ecf20Sopenharmony_ci SEL_VALIDATE_TRANS, /* compute validatetrans decision */ 668c2ecf20Sopenharmony_ci SEL_INO_NEXT, /* The next inode number to use */ 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistruct selinux_fs_info { 708c2ecf20Sopenharmony_ci struct dentry *bool_dir; 718c2ecf20Sopenharmony_ci unsigned int bool_num; 728c2ecf20Sopenharmony_ci char **bool_pending_names; 738c2ecf20Sopenharmony_ci unsigned int *bool_pending_values; 748c2ecf20Sopenharmony_ci struct dentry *class_dir; 758c2ecf20Sopenharmony_ci unsigned long last_class_ino; 768c2ecf20Sopenharmony_ci bool policy_opened; 778c2ecf20Sopenharmony_ci struct dentry *policycap_dir; 788c2ecf20Sopenharmony_ci unsigned long last_ino; 798c2ecf20Sopenharmony_ci struct selinux_state *state; 808c2ecf20Sopenharmony_ci struct super_block *sb; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int selinux_fs_info_create(struct super_block *sb) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci fsi = kzalloc(sizeof(*fsi), GFP_KERNEL); 888c2ecf20Sopenharmony_ci if (!fsi) 898c2ecf20Sopenharmony_ci return -ENOMEM; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci fsi->last_ino = SEL_INO_NEXT - 1; 928c2ecf20Sopenharmony_ci fsi->state = &selinux_state; 938c2ecf20Sopenharmony_ci fsi->sb = sb; 948c2ecf20Sopenharmony_ci sb->s_fs_info = fsi; 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void selinux_fs_info_free(struct super_block *sb) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = sb->s_fs_info; 1018c2ecf20Sopenharmony_ci int i; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (fsi) { 1048c2ecf20Sopenharmony_ci for (i = 0; i < fsi->bool_num; i++) 1058c2ecf20Sopenharmony_ci kfree(fsi->bool_pending_names[i]); 1068c2ecf20Sopenharmony_ci kfree(fsi->bool_pending_names); 1078c2ecf20Sopenharmony_ci kfree(fsi->bool_pending_values); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci kfree(sb->s_fs_info); 1108c2ecf20Sopenharmony_ci sb->s_fs_info = NULL; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define SEL_INITCON_INO_OFFSET 0x01000000 1148c2ecf20Sopenharmony_ci#define SEL_BOOL_INO_OFFSET 0x02000000 1158c2ecf20Sopenharmony_ci#define SEL_CLASS_INO_OFFSET 0x04000000 1168c2ecf20Sopenharmony_ci#define SEL_POLICYCAP_INO_OFFSET 0x08000000 1178c2ecf20Sopenharmony_ci#define SEL_INO_MASK 0x00ffffff 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci#define BOOL_DIR_NAME "booleans" 1208c2ecf20Sopenharmony_ci#define CLASS_DIR_NAME "class" 1218c2ecf20Sopenharmony_ci#define POLICYCAP_DIR_NAME "policy_capabilities" 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define TMPBUFLEN 12 1248c2ecf20Sopenharmony_cistatic ssize_t sel_read_enforce(struct file *filp, char __user *buf, 1258c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 1288c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 1298c2ecf20Sopenharmony_ci ssize_t length; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%d", 1328c2ecf20Sopenharmony_ci enforcing_enabled(fsi->state)); 1338c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_DEVELOP 1378c2ecf20Sopenharmony_cistatic ssize_t sel_write_enforce(struct file *file, const char __user *buf, 1388c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 1428c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 1438c2ecf20Sopenharmony_ci char *page = NULL; 1448c2ecf20Sopenharmony_ci ssize_t length; 1458c2ecf20Sopenharmony_ci int old_value, new_value; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 1488c2ecf20Sopenharmony_ci return -ENOMEM; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* No partial writes. */ 1518c2ecf20Sopenharmony_ci if (*ppos != 0) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci page = memdup_user_nul(buf, count); 1558c2ecf20Sopenharmony_ci if (IS_ERR(page)) 1568c2ecf20Sopenharmony_ci return PTR_ERR(page); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci length = -EINVAL; 1598c2ecf20Sopenharmony_ci if (sscanf(page, "%d", &new_value) != 1) 1608c2ecf20Sopenharmony_ci goto out; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci new_value = !!new_value; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci old_value = enforcing_enabled(state); 1658c2ecf20Sopenharmony_ci if (new_value != old_value) { 1668c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 1678c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 1688c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__SETENFORCE, 1698c2ecf20Sopenharmony_ci NULL); 1708c2ecf20Sopenharmony_ci if (length) 1718c2ecf20Sopenharmony_ci goto out; 1728c2ecf20Sopenharmony_ci audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, 1738c2ecf20Sopenharmony_ci "enforcing=%d old_enforcing=%d auid=%u ses=%u" 1748c2ecf20Sopenharmony_ci " enabled=1 old-enabled=1 lsm=selinux res=1", 1758c2ecf20Sopenharmony_ci new_value, old_value, 1768c2ecf20Sopenharmony_ci from_kuid(&init_user_ns, audit_get_loginuid(current)), 1778c2ecf20Sopenharmony_ci audit_get_sessionid(current)); 1788c2ecf20Sopenharmony_ci enforcing_set(state, new_value); 1798c2ecf20Sopenharmony_ci if (new_value) 1808c2ecf20Sopenharmony_ci avc_ss_reset(state->avc, 0); 1818c2ecf20Sopenharmony_ci selnl_notify_setenforce(new_value); 1828c2ecf20Sopenharmony_ci selinux_status_update_setenforce(state, new_value); 1838c2ecf20Sopenharmony_ci if (!new_value) 1848c2ecf20Sopenharmony_ci call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci length = count; 1878c2ecf20Sopenharmony_ciout: 1888c2ecf20Sopenharmony_ci kfree(page); 1898c2ecf20Sopenharmony_ci return length; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci#else 1928c2ecf20Sopenharmony_ci#define sel_write_enforce NULL 1938c2ecf20Sopenharmony_ci#endif 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic const struct file_operations sel_enforce_ops = { 1968c2ecf20Sopenharmony_ci .read = sel_read_enforce, 1978c2ecf20Sopenharmony_ci .write = sel_write_enforce, 1988c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 1998c2ecf20Sopenharmony_ci}; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, 2028c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 2058c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 2068c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 2078c2ecf20Sopenharmony_ci ssize_t length; 2088c2ecf20Sopenharmony_ci ino_t ino = file_inode(filp)->i_ino; 2098c2ecf20Sopenharmony_ci int handle_unknown = (ino == SEL_REJECT_UNKNOWN) ? 2108c2ecf20Sopenharmony_ci security_get_reject_unknown(state) : 2118c2ecf20Sopenharmony_ci !security_get_allow_unknown(state); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%d", handle_unknown); 2148c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic const struct file_operations sel_handle_unknown_ops = { 2188c2ecf20Sopenharmony_ci .read = sel_read_handle_unknown, 2198c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic int sel_open_handle_status(struct inode *inode, struct file *filp) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 2258c2ecf20Sopenharmony_ci struct page *status = selinux_kernel_status_page(fsi->state); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (!status) 2288c2ecf20Sopenharmony_ci return -ENOMEM; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci filp->private_data = status; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic ssize_t sel_read_handle_status(struct file *filp, char __user *buf, 2368c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct page *status = filp->private_data; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci BUG_ON(!status); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, 2438c2ecf20Sopenharmony_ci page_address(status), 2448c2ecf20Sopenharmony_ci sizeof(struct selinux_kernel_status)); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int sel_mmap_handle_status(struct file *filp, 2488c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct page *status = filp->private_data; 2518c2ecf20Sopenharmony_ci unsigned long size = vma->vm_end - vma->vm_start; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci BUG_ON(!status); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* only allows one page from the head */ 2568c2ecf20Sopenharmony_ci if (vma->vm_pgoff > 0 || size != PAGE_SIZE) 2578c2ecf20Sopenharmony_ci return -EIO; 2588c2ecf20Sopenharmony_ci /* disallow writable mapping */ 2598c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_WRITE) 2608c2ecf20Sopenharmony_ci return -EPERM; 2618c2ecf20Sopenharmony_ci /* disallow mprotect() turns it into writable */ 2628c2ecf20Sopenharmony_ci vma->vm_flags &= ~VM_MAYWRITE; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return remap_pfn_range(vma, vma->vm_start, 2658c2ecf20Sopenharmony_ci page_to_pfn(status), 2668c2ecf20Sopenharmony_ci size, vma->vm_page_prot); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const struct file_operations sel_handle_status_ops = { 2708c2ecf20Sopenharmony_ci .open = sel_open_handle_status, 2718c2ecf20Sopenharmony_ci .read = sel_read_handle_status, 2728c2ecf20Sopenharmony_ci .mmap = sel_mmap_handle_status, 2738c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_DISABLE 2778c2ecf20Sopenharmony_cistatic ssize_t sel_write_disable(struct file *file, const char __user *buf, 2788c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 2828c2ecf20Sopenharmony_ci char *page; 2838c2ecf20Sopenharmony_ci ssize_t length; 2848c2ecf20Sopenharmony_ci int new_value; 2858c2ecf20Sopenharmony_ci int enforcing; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* NOTE: we are now officially considering runtime disable as 2888c2ecf20Sopenharmony_ci * deprecated, and using it will become increasingly painful 2898c2ecf20Sopenharmony_ci * (e.g. sleeping/blocking) as we progress through future 2908c2ecf20Sopenharmony_ci * kernel releases until eventually it is removed 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci pr_err("SELinux: Runtime disable is deprecated, use selinux=0 on the kernel cmdline.\n"); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 2958c2ecf20Sopenharmony_ci return -ENOMEM; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* No partial writes. */ 2988c2ecf20Sopenharmony_ci if (*ppos != 0) 2998c2ecf20Sopenharmony_ci return -EINVAL; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci page = memdup_user_nul(buf, count); 3028c2ecf20Sopenharmony_ci if (IS_ERR(page)) 3038c2ecf20Sopenharmony_ci return PTR_ERR(page); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci length = -EINVAL; 3068c2ecf20Sopenharmony_ci if (sscanf(page, "%d", &new_value) != 1) 3078c2ecf20Sopenharmony_ci goto out; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (new_value) { 3108c2ecf20Sopenharmony_ci enforcing = enforcing_enabled(fsi->state); 3118c2ecf20Sopenharmony_ci length = selinux_disable(fsi->state); 3128c2ecf20Sopenharmony_ci if (length) 3138c2ecf20Sopenharmony_ci goto out; 3148c2ecf20Sopenharmony_ci audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_STATUS, 3158c2ecf20Sopenharmony_ci "enforcing=%d old_enforcing=%d auid=%u ses=%u" 3168c2ecf20Sopenharmony_ci " enabled=0 old-enabled=1 lsm=selinux res=1", 3178c2ecf20Sopenharmony_ci enforcing, enforcing, 3188c2ecf20Sopenharmony_ci from_kuid(&init_user_ns, audit_get_loginuid(current)), 3198c2ecf20Sopenharmony_ci audit_get_sessionid(current)); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci length = count; 3238c2ecf20Sopenharmony_ciout: 3248c2ecf20Sopenharmony_ci kfree(page); 3258c2ecf20Sopenharmony_ci return length; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci#else 3288c2ecf20Sopenharmony_ci#define sel_write_disable NULL 3298c2ecf20Sopenharmony_ci#endif 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic const struct file_operations sel_disable_ops = { 3328c2ecf20Sopenharmony_ci .write = sel_write_disable, 3338c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic ssize_t sel_read_policyvers(struct file *filp, char __user *buf, 3378c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 3408c2ecf20Sopenharmony_ci ssize_t length; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%u", POLICYDB_VERSION_MAX); 3438c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic const struct file_operations sel_policyvers_ops = { 3478c2ecf20Sopenharmony_ci .read = sel_read_policyvers, 3488c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci/* declaration for sel_write_load */ 3528c2ecf20Sopenharmony_cistatic int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir, 3538c2ecf20Sopenharmony_ci unsigned int *bool_num, char ***bool_pending_names, 3548c2ecf20Sopenharmony_ci unsigned int **bool_pending_values); 3558c2ecf20Sopenharmony_cistatic int sel_make_classes(struct selinux_policy *newpolicy, 3568c2ecf20Sopenharmony_ci struct dentry *class_dir, 3578c2ecf20Sopenharmony_ci unsigned long *last_class_ino); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* declaration for sel_make_class_dirs */ 3608c2ecf20Sopenharmony_cistatic struct dentry *sel_make_dir(struct dentry *dir, const char *name, 3618c2ecf20Sopenharmony_ci unsigned long *ino); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* declaration for sel_make_policy_nodes */ 3648c2ecf20Sopenharmony_cistatic struct dentry *sel_make_disconnected_dir(struct super_block *sb, 3658c2ecf20Sopenharmony_ci unsigned long *ino); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* declaration for sel_make_policy_nodes */ 3688c2ecf20Sopenharmony_cistatic void sel_remove_entries(struct dentry *de); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic ssize_t sel_read_mls(struct file *filp, char __user *buf, 3718c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 3748c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 3758c2ecf20Sopenharmony_ci ssize_t length; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%d", 3788c2ecf20Sopenharmony_ci security_mls_enabled(fsi->state)); 3798c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic const struct file_operations sel_mls_ops = { 3838c2ecf20Sopenharmony_ci .read = sel_read_mls, 3848c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistruct policy_load_memory { 3888c2ecf20Sopenharmony_ci size_t len; 3898c2ecf20Sopenharmony_ci void *data; 3908c2ecf20Sopenharmony_ci}; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic int sel_open_policy(struct inode *inode, struct file *filp) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; 3958c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 3968c2ecf20Sopenharmony_ci struct policy_load_memory *plm = NULL; 3978c2ecf20Sopenharmony_ci int rc; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci BUG_ON(filp->private_data); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci mutex_lock(&fsi->state->policy_mutex); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci rc = avc_has_perm(&selinux_state, 4048c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 4058c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); 4068c2ecf20Sopenharmony_ci if (rc) 4078c2ecf20Sopenharmony_ci goto err; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci rc = -EBUSY; 4108c2ecf20Sopenharmony_ci if (fsi->policy_opened) 4118c2ecf20Sopenharmony_ci goto err; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci rc = -ENOMEM; 4148c2ecf20Sopenharmony_ci plm = kzalloc(sizeof(*plm), GFP_KERNEL); 4158c2ecf20Sopenharmony_ci if (!plm) 4168c2ecf20Sopenharmony_ci goto err; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci rc = security_read_policy(state, &plm->data, &plm->len); 4198c2ecf20Sopenharmony_ci if (rc) 4208c2ecf20Sopenharmony_ci goto err; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if ((size_t)i_size_read(inode) != plm->len) { 4238c2ecf20Sopenharmony_ci inode_lock(inode); 4248c2ecf20Sopenharmony_ci i_size_write(inode, plm->len); 4258c2ecf20Sopenharmony_ci inode_unlock(inode); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci fsi->policy_opened = 1; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci filp->private_data = plm; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return 0; 4358c2ecf20Sopenharmony_cierr: 4368c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (plm) 4398c2ecf20Sopenharmony_ci vfree(plm->data); 4408c2ecf20Sopenharmony_ci kfree(plm); 4418c2ecf20Sopenharmony_ci return rc; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int sel_release_policy(struct inode *inode, struct file *filp) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = inode->i_sb->s_fs_info; 4478c2ecf20Sopenharmony_ci struct policy_load_memory *plm = filp->private_data; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci BUG_ON(!plm); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci fsi->policy_opened = 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci vfree(plm->data); 4548c2ecf20Sopenharmony_ci kfree(plm); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic ssize_t sel_read_policy(struct file *filp, char __user *buf, 4608c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct policy_load_memory *plm = filp->private_data; 4638c2ecf20Sopenharmony_ci int ret; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ret = avc_has_perm(&selinux_state, 4668c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 4678c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__READ_POLICY, NULL); 4688c2ecf20Sopenharmony_ci if (ret) 4698c2ecf20Sopenharmony_ci return ret; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, plm->data, plm->len); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic vm_fault_t sel_mmap_policy_fault(struct vm_fault *vmf) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct policy_load_memory *plm = vmf->vma->vm_file->private_data; 4778c2ecf20Sopenharmony_ci unsigned long offset; 4788c2ecf20Sopenharmony_ci struct page *page; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE)) 4818c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 4848c2ecf20Sopenharmony_ci if (offset >= roundup(plm->len, PAGE_SIZE)) 4858c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci page = vmalloc_to_page(plm->data + offset); 4888c2ecf20Sopenharmony_ci get_page(page); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci vmf->page = page; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic const struct vm_operations_struct sel_mmap_policy_ops = { 4968c2ecf20Sopenharmony_ci .fault = sel_mmap_policy_fault, 4978c2ecf20Sopenharmony_ci .page_mkwrite = sel_mmap_policy_fault, 4988c2ecf20Sopenharmony_ci}; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int sel_mmap_policy(struct file *filp, struct vm_area_struct *vma) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_SHARED) { 5038c2ecf20Sopenharmony_ci /* do not allow mprotect to make mapping writable */ 5048c2ecf20Sopenharmony_ci vma->vm_flags &= ~VM_MAYWRITE; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (vma->vm_flags & VM_WRITE) 5078c2ecf20Sopenharmony_ci return -EACCES; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; 5118c2ecf20Sopenharmony_ci vma->vm_ops = &sel_mmap_policy_ops; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic const struct file_operations sel_policy_ops = { 5178c2ecf20Sopenharmony_ci .open = sel_open_policy, 5188c2ecf20Sopenharmony_ci .read = sel_read_policy, 5198c2ecf20Sopenharmony_ci .mmap = sel_mmap_policy, 5208c2ecf20Sopenharmony_ci .release = sel_release_policy, 5218c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cistatic void sel_remove_old_bool_data(unsigned int bool_num, char **bool_names, 5258c2ecf20Sopenharmony_ci unsigned int *bool_values) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci u32 i; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* bool_dir cleanup */ 5308c2ecf20Sopenharmony_ci for (i = 0; i < bool_num; i++) 5318c2ecf20Sopenharmony_ci kfree(bool_names[i]); 5328c2ecf20Sopenharmony_ci kfree(bool_names); 5338c2ecf20Sopenharmony_ci kfree(bool_values); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int sel_make_policy_nodes(struct selinux_fs_info *fsi, 5378c2ecf20Sopenharmony_ci struct selinux_policy *newpolicy) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci int ret = 0; 5408c2ecf20Sopenharmony_ci struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry; 5418c2ecf20Sopenharmony_ci unsigned int tmp_bool_num, old_bool_num; 5428c2ecf20Sopenharmony_ci char **tmp_bool_names, **old_bool_names; 5438c2ecf20Sopenharmony_ci unsigned int *tmp_bool_values, *old_bool_values; 5448c2ecf20Sopenharmony_ci unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */ 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino); 5478c2ecf20Sopenharmony_ci if (IS_ERR(tmp_parent)) 5488c2ecf20Sopenharmony_ci return PTR_ERR(tmp_parent); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci tmp_ino = fsi->bool_dir->d_inode->i_ino - 1; /* sel_make_dir will increment and set */ 5518c2ecf20Sopenharmony_ci tmp_bool_dir = sel_make_dir(tmp_parent, BOOL_DIR_NAME, &tmp_ino); 5528c2ecf20Sopenharmony_ci if (IS_ERR(tmp_bool_dir)) { 5538c2ecf20Sopenharmony_ci ret = PTR_ERR(tmp_bool_dir); 5548c2ecf20Sopenharmony_ci goto out; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci tmp_ino = fsi->class_dir->d_inode->i_ino - 1; /* sel_make_dir will increment and set */ 5588c2ecf20Sopenharmony_ci tmp_class_dir = sel_make_dir(tmp_parent, CLASS_DIR_NAME, &tmp_ino); 5598c2ecf20Sopenharmony_ci if (IS_ERR(tmp_class_dir)) { 5608c2ecf20Sopenharmony_ci ret = PTR_ERR(tmp_class_dir); 5618c2ecf20Sopenharmony_ci goto out; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num, 5658c2ecf20Sopenharmony_ci &tmp_bool_names, &tmp_bool_values); 5668c2ecf20Sopenharmony_ci if (ret) { 5678c2ecf20Sopenharmony_ci pr_err("SELinux: failed to load policy booleans\n"); 5688c2ecf20Sopenharmony_ci goto out; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci ret = sel_make_classes(newpolicy, tmp_class_dir, 5728c2ecf20Sopenharmony_ci &fsi->last_class_ino); 5738c2ecf20Sopenharmony_ci if (ret) { 5748c2ecf20Sopenharmony_ci pr_err("SELinux: failed to load policy classes\n"); 5758c2ecf20Sopenharmony_ci goto out; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* booleans */ 5798c2ecf20Sopenharmony_ci old_dentry = fsi->bool_dir; 5808c2ecf20Sopenharmony_ci lock_rename(tmp_bool_dir, old_dentry); 5818c2ecf20Sopenharmony_ci d_exchange(tmp_bool_dir, fsi->bool_dir); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci old_bool_num = fsi->bool_num; 5848c2ecf20Sopenharmony_ci old_bool_names = fsi->bool_pending_names; 5858c2ecf20Sopenharmony_ci old_bool_values = fsi->bool_pending_values; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci fsi->bool_num = tmp_bool_num; 5888c2ecf20Sopenharmony_ci fsi->bool_pending_names = tmp_bool_names; 5898c2ecf20Sopenharmony_ci fsi->bool_pending_values = tmp_bool_values; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci sel_remove_old_bool_data(old_bool_num, old_bool_names, old_bool_values); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci fsi->bool_dir = tmp_bool_dir; 5948c2ecf20Sopenharmony_ci unlock_rename(tmp_bool_dir, old_dentry); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* classes */ 5978c2ecf20Sopenharmony_ci old_dentry = fsi->class_dir; 5988c2ecf20Sopenharmony_ci lock_rename(tmp_class_dir, old_dentry); 5998c2ecf20Sopenharmony_ci d_exchange(tmp_class_dir, fsi->class_dir); 6008c2ecf20Sopenharmony_ci fsi->class_dir = tmp_class_dir; 6018c2ecf20Sopenharmony_ci unlock_rename(tmp_class_dir, old_dentry); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ciout: 6048c2ecf20Sopenharmony_ci /* Since the other temporary dirs are children of tmp_parent 6058c2ecf20Sopenharmony_ci * this will handle all the cleanup in the case of a failure before 6068c2ecf20Sopenharmony_ci * the swapover 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci sel_remove_entries(tmp_parent); 6098c2ecf20Sopenharmony_ci dput(tmp_parent); /* d_genocide() only handles the children */ 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return ret; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic ssize_t sel_write_load(struct file *file, const char __user *buf, 6158c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 6198c2ecf20Sopenharmony_ci struct selinux_load_state load_state; 6208c2ecf20Sopenharmony_ci ssize_t length; 6218c2ecf20Sopenharmony_ci void *data = NULL; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci mutex_lock(&fsi->state->policy_mutex); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 6268c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 6278c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__LOAD_POLICY, NULL); 6288c2ecf20Sopenharmony_ci if (length) 6298c2ecf20Sopenharmony_ci goto out; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* No partial writes. */ 6328c2ecf20Sopenharmony_ci length = -EINVAL; 6338c2ecf20Sopenharmony_ci if (*ppos != 0) 6348c2ecf20Sopenharmony_ci goto out; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci length = -ENOMEM; 6378c2ecf20Sopenharmony_ci data = vmalloc(count); 6388c2ecf20Sopenharmony_ci if (!data) 6398c2ecf20Sopenharmony_ci goto out; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci length = -EFAULT; 6428c2ecf20Sopenharmony_ci if (copy_from_user(data, buf, count) != 0) 6438c2ecf20Sopenharmony_ci goto out; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci length = security_load_policy(fsi->state, data, count, &load_state); 6468c2ecf20Sopenharmony_ci if (length) { 6478c2ecf20Sopenharmony_ci pr_warn_ratelimited("SELinux: failed to load policy\n"); 6488c2ecf20Sopenharmony_ci goto out; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci length = sel_make_policy_nodes(fsi, load_state.policy); 6528c2ecf20Sopenharmony_ci if (length) { 6538c2ecf20Sopenharmony_ci selinux_policy_cancel(fsi->state, &load_state); 6548c2ecf20Sopenharmony_ci goto out; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci selinux_policy_commit(fsi->state, &load_state); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci length = count; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci audit_log(audit_context(), GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, 6628c2ecf20Sopenharmony_ci "auid=%u ses=%u lsm=selinux res=1", 6638c2ecf20Sopenharmony_ci from_kuid(&init_user_ns, audit_get_loginuid(current)), 6648c2ecf20Sopenharmony_ci audit_get_sessionid(current)); 6658c2ecf20Sopenharmony_ciout: 6668c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 6678c2ecf20Sopenharmony_ci vfree(data); 6688c2ecf20Sopenharmony_ci return length; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic const struct file_operations sel_load_ops = { 6728c2ecf20Sopenharmony_ci .write = sel_write_load, 6738c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 6748c2ecf20Sopenharmony_ci}; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic ssize_t sel_write_context(struct file *file, char *buf, size_t size) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 6798c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 6808c2ecf20Sopenharmony_ci char *canon = NULL; 6818c2ecf20Sopenharmony_ci u32 sid, len; 6828c2ecf20Sopenharmony_ci ssize_t length; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 6858c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 6868c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__CHECK_CONTEXT, NULL); 6878c2ecf20Sopenharmony_ci if (length) 6888c2ecf20Sopenharmony_ci goto out; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci length = security_context_to_sid(state, buf, size, &sid, GFP_KERNEL); 6918c2ecf20Sopenharmony_ci if (length) 6928c2ecf20Sopenharmony_ci goto out; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci length = security_sid_to_context(state, sid, &canon, &len); 6958c2ecf20Sopenharmony_ci if (length) 6968c2ecf20Sopenharmony_ci goto out; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci length = -ERANGE; 6998c2ecf20Sopenharmony_ci if (len > SIMPLE_TRANSACTION_LIMIT) { 7008c2ecf20Sopenharmony_ci pr_err("SELinux: %s: context size (%u) exceeds " 7018c2ecf20Sopenharmony_ci "payload max\n", __func__, len); 7028c2ecf20Sopenharmony_ci goto out; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci memcpy(buf, canon, len); 7068c2ecf20Sopenharmony_ci length = len; 7078c2ecf20Sopenharmony_ciout: 7088c2ecf20Sopenharmony_ci kfree(canon); 7098c2ecf20Sopenharmony_ci return length; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic ssize_t sel_read_checkreqprot(struct file *filp, char __user *buf, 7138c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 7168c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 7178c2ecf20Sopenharmony_ci ssize_t length; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%u", 7208c2ecf20Sopenharmony_ci checkreqprot_get(fsi->state)); 7218c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic ssize_t sel_write_checkreqprot(struct file *file, const char __user *buf, 7258c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 7288c2ecf20Sopenharmony_ci char *page; 7298c2ecf20Sopenharmony_ci ssize_t length; 7308c2ecf20Sopenharmony_ci unsigned int new_value; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 7338c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 7348c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__SETCHECKREQPROT, 7358c2ecf20Sopenharmony_ci NULL); 7368c2ecf20Sopenharmony_ci if (length) 7378c2ecf20Sopenharmony_ci return length; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 7408c2ecf20Sopenharmony_ci return -ENOMEM; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* No partial writes. */ 7438c2ecf20Sopenharmony_ci if (*ppos != 0) 7448c2ecf20Sopenharmony_ci return -EINVAL; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci page = memdup_user_nul(buf, count); 7478c2ecf20Sopenharmony_ci if (IS_ERR(page)) 7488c2ecf20Sopenharmony_ci return PTR_ERR(page); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci length = -EINVAL; 7518c2ecf20Sopenharmony_ci if (sscanf(page, "%u", &new_value) != 1) 7528c2ecf20Sopenharmony_ci goto out; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (new_value) { 7558c2ecf20Sopenharmony_ci char comm[sizeof(current->comm)]; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci memcpy(comm, current->comm, sizeof(comm)); 7588c2ecf20Sopenharmony_ci pr_warn_once("SELinux: %s (%d) set checkreqprot to 1. This is deprecated and will be rejected in a future kernel release.\n", 7598c2ecf20Sopenharmony_ci comm, current->pid); 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci checkreqprot_set(fsi->state, (new_value ? 1 : 0)); 7638c2ecf20Sopenharmony_ci length = count; 7648c2ecf20Sopenharmony_ciout: 7658c2ecf20Sopenharmony_ci kfree(page); 7668c2ecf20Sopenharmony_ci return length; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_cistatic const struct file_operations sel_checkreqprot_ops = { 7698c2ecf20Sopenharmony_ci .read = sel_read_checkreqprot, 7708c2ecf20Sopenharmony_ci .write = sel_write_checkreqprot, 7718c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 7728c2ecf20Sopenharmony_ci}; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic ssize_t sel_write_validatetrans(struct file *file, 7758c2ecf20Sopenharmony_ci const char __user *buf, 7768c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 7798c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 7808c2ecf20Sopenharmony_ci char *oldcon = NULL, *newcon = NULL, *taskcon = NULL; 7818c2ecf20Sopenharmony_ci char *req = NULL; 7828c2ecf20Sopenharmony_ci u32 osid, nsid, tsid; 7838c2ecf20Sopenharmony_ci u16 tclass; 7848c2ecf20Sopenharmony_ci int rc; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci rc = avc_has_perm(&selinux_state, 7878c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 7888c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__VALIDATE_TRANS, NULL); 7898c2ecf20Sopenharmony_ci if (rc) 7908c2ecf20Sopenharmony_ci goto out; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci rc = -ENOMEM; 7938c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 7948c2ecf20Sopenharmony_ci goto out; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* No partial writes. */ 7978c2ecf20Sopenharmony_ci rc = -EINVAL; 7988c2ecf20Sopenharmony_ci if (*ppos != 0) 7998c2ecf20Sopenharmony_ci goto out; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci req = memdup_user_nul(buf, count); 8028c2ecf20Sopenharmony_ci if (IS_ERR(req)) { 8038c2ecf20Sopenharmony_ci rc = PTR_ERR(req); 8048c2ecf20Sopenharmony_ci req = NULL; 8058c2ecf20Sopenharmony_ci goto out; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci rc = -ENOMEM; 8098c2ecf20Sopenharmony_ci oldcon = kzalloc(count + 1, GFP_KERNEL); 8108c2ecf20Sopenharmony_ci if (!oldcon) 8118c2ecf20Sopenharmony_ci goto out; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci newcon = kzalloc(count + 1, GFP_KERNEL); 8148c2ecf20Sopenharmony_ci if (!newcon) 8158c2ecf20Sopenharmony_ci goto out; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci taskcon = kzalloc(count + 1, GFP_KERNEL); 8188c2ecf20Sopenharmony_ci if (!taskcon) 8198c2ecf20Sopenharmony_ci goto out; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci rc = -EINVAL; 8228c2ecf20Sopenharmony_ci if (sscanf(req, "%s %s %hu %s", oldcon, newcon, &tclass, taskcon) != 4) 8238c2ecf20Sopenharmony_ci goto out; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci rc = security_context_str_to_sid(state, oldcon, &osid, GFP_KERNEL); 8268c2ecf20Sopenharmony_ci if (rc) 8278c2ecf20Sopenharmony_ci goto out; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci rc = security_context_str_to_sid(state, newcon, &nsid, GFP_KERNEL); 8308c2ecf20Sopenharmony_ci if (rc) 8318c2ecf20Sopenharmony_ci goto out; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci rc = security_context_str_to_sid(state, taskcon, &tsid, GFP_KERNEL); 8348c2ecf20Sopenharmony_ci if (rc) 8358c2ecf20Sopenharmony_ci goto out; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci rc = security_validate_transition_user(state, osid, nsid, tsid, tclass); 8388c2ecf20Sopenharmony_ci if (!rc) 8398c2ecf20Sopenharmony_ci rc = count; 8408c2ecf20Sopenharmony_ciout: 8418c2ecf20Sopenharmony_ci kfree(req); 8428c2ecf20Sopenharmony_ci kfree(oldcon); 8438c2ecf20Sopenharmony_ci kfree(newcon); 8448c2ecf20Sopenharmony_ci kfree(taskcon); 8458c2ecf20Sopenharmony_ci return rc; 8468c2ecf20Sopenharmony_ci} 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic const struct file_operations sel_transition_ops = { 8498c2ecf20Sopenharmony_ci .write = sel_write_validatetrans, 8508c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 8518c2ecf20Sopenharmony_ci}; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci/* 8548c2ecf20Sopenharmony_ci * Remaining nodes use transaction based IO methods like nfsd/nfsctl.c 8558c2ecf20Sopenharmony_ci */ 8568c2ecf20Sopenharmony_cistatic ssize_t sel_write_access(struct file *file, char *buf, size_t size); 8578c2ecf20Sopenharmony_cistatic ssize_t sel_write_create(struct file *file, char *buf, size_t size); 8588c2ecf20Sopenharmony_cistatic ssize_t sel_write_relabel(struct file *file, char *buf, size_t size); 8598c2ecf20Sopenharmony_cistatic ssize_t sel_write_user(struct file *file, char *buf, size_t size); 8608c2ecf20Sopenharmony_cistatic ssize_t sel_write_member(struct file *file, char *buf, size_t size); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic ssize_t (*const write_op[])(struct file *, char *, size_t) = { 8638c2ecf20Sopenharmony_ci [SEL_ACCESS] = sel_write_access, 8648c2ecf20Sopenharmony_ci [SEL_CREATE] = sel_write_create, 8658c2ecf20Sopenharmony_ci [SEL_RELABEL] = sel_write_relabel, 8668c2ecf20Sopenharmony_ci [SEL_USER] = sel_write_user, 8678c2ecf20Sopenharmony_ci [SEL_MEMBER] = sel_write_member, 8688c2ecf20Sopenharmony_ci [SEL_CONTEXT] = sel_write_context, 8698c2ecf20Sopenharmony_ci}; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic ssize_t selinux_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci ino_t ino = file_inode(file)->i_ino; 8748c2ecf20Sopenharmony_ci char *data; 8758c2ecf20Sopenharmony_ci ssize_t rv; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) 8788c2ecf20Sopenharmony_ci return -EINVAL; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci data = simple_transaction_get(file, buf, size); 8818c2ecf20Sopenharmony_ci if (IS_ERR(data)) 8828c2ecf20Sopenharmony_ci return PTR_ERR(data); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci rv = write_op[ino](file, data, size); 8858c2ecf20Sopenharmony_ci if (rv > 0) { 8868c2ecf20Sopenharmony_ci simple_transaction_set(file, rv); 8878c2ecf20Sopenharmony_ci rv = size; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci return rv; 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic const struct file_operations transaction_ops = { 8938c2ecf20Sopenharmony_ci .write = selinux_transaction_write, 8948c2ecf20Sopenharmony_ci .read = simple_transaction_read, 8958c2ecf20Sopenharmony_ci .release = simple_transaction_release, 8968c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci/* 9008c2ecf20Sopenharmony_ci * payload - write methods 9018c2ecf20Sopenharmony_ci * If the method has a response, the response should be put in buf, 9028c2ecf20Sopenharmony_ci * and the length returned. Otherwise return 0 or and -error. 9038c2ecf20Sopenharmony_ci */ 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic ssize_t sel_write_access(struct file *file, char *buf, size_t size) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 9088c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 9098c2ecf20Sopenharmony_ci char *scon = NULL, *tcon = NULL; 9108c2ecf20Sopenharmony_ci u32 ssid, tsid; 9118c2ecf20Sopenharmony_ci u16 tclass; 9128c2ecf20Sopenharmony_ci struct av_decision avd; 9138c2ecf20Sopenharmony_ci ssize_t length; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 9168c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 9178c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__COMPUTE_AV, NULL); 9188c2ecf20Sopenharmony_ci if (length) 9198c2ecf20Sopenharmony_ci goto out; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci length = -ENOMEM; 9228c2ecf20Sopenharmony_ci scon = kzalloc(size + 1, GFP_KERNEL); 9238c2ecf20Sopenharmony_ci if (!scon) 9248c2ecf20Sopenharmony_ci goto out; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci length = -ENOMEM; 9278c2ecf20Sopenharmony_ci tcon = kzalloc(size + 1, GFP_KERNEL); 9288c2ecf20Sopenharmony_ci if (!tcon) 9298c2ecf20Sopenharmony_ci goto out; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci length = -EINVAL; 9328c2ecf20Sopenharmony_ci if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) 9338c2ecf20Sopenharmony_ci goto out; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); 9368c2ecf20Sopenharmony_ci if (length) 9378c2ecf20Sopenharmony_ci goto out; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); 9408c2ecf20Sopenharmony_ci if (length) 9418c2ecf20Sopenharmony_ci goto out; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci security_compute_av_user(state, ssid, tsid, tclass, &avd); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, 9468c2ecf20Sopenharmony_ci "%x %x %x %x %u %x", 9478c2ecf20Sopenharmony_ci avd.allowed, 0xffffffff, 9488c2ecf20Sopenharmony_ci avd.auditallow, avd.auditdeny, 9498c2ecf20Sopenharmony_ci avd.seqno, avd.flags); 9508c2ecf20Sopenharmony_ciout: 9518c2ecf20Sopenharmony_ci kfree(tcon); 9528c2ecf20Sopenharmony_ci kfree(scon); 9538c2ecf20Sopenharmony_ci return length; 9548c2ecf20Sopenharmony_ci} 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic ssize_t sel_write_create(struct file *file, char *buf, size_t size) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 9598c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 9608c2ecf20Sopenharmony_ci char *scon = NULL, *tcon = NULL; 9618c2ecf20Sopenharmony_ci char *namebuf = NULL, *objname = NULL; 9628c2ecf20Sopenharmony_ci u32 ssid, tsid, newsid; 9638c2ecf20Sopenharmony_ci u16 tclass; 9648c2ecf20Sopenharmony_ci ssize_t length; 9658c2ecf20Sopenharmony_ci char *newcon = NULL; 9668c2ecf20Sopenharmony_ci u32 len; 9678c2ecf20Sopenharmony_ci int nargs; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 9708c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 9718c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__COMPUTE_CREATE, 9728c2ecf20Sopenharmony_ci NULL); 9738c2ecf20Sopenharmony_ci if (length) 9748c2ecf20Sopenharmony_ci goto out; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci length = -ENOMEM; 9778c2ecf20Sopenharmony_ci scon = kzalloc(size + 1, GFP_KERNEL); 9788c2ecf20Sopenharmony_ci if (!scon) 9798c2ecf20Sopenharmony_ci goto out; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci length = -ENOMEM; 9828c2ecf20Sopenharmony_ci tcon = kzalloc(size + 1, GFP_KERNEL); 9838c2ecf20Sopenharmony_ci if (!tcon) 9848c2ecf20Sopenharmony_ci goto out; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci length = -ENOMEM; 9878c2ecf20Sopenharmony_ci namebuf = kzalloc(size + 1, GFP_KERNEL); 9888c2ecf20Sopenharmony_ci if (!namebuf) 9898c2ecf20Sopenharmony_ci goto out; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci length = -EINVAL; 9928c2ecf20Sopenharmony_ci nargs = sscanf(buf, "%s %s %hu %s", scon, tcon, &tclass, namebuf); 9938c2ecf20Sopenharmony_ci if (nargs < 3 || nargs > 4) 9948c2ecf20Sopenharmony_ci goto out; 9958c2ecf20Sopenharmony_ci if (nargs == 4) { 9968c2ecf20Sopenharmony_ci /* 9978c2ecf20Sopenharmony_ci * If and when the name of new object to be queried contains 9988c2ecf20Sopenharmony_ci * either whitespace or multibyte characters, they shall be 9998c2ecf20Sopenharmony_ci * encoded based on the percentage-encoding rule. 10008c2ecf20Sopenharmony_ci * If not encoded, the sscanf logic picks up only left-half 10018c2ecf20Sopenharmony_ci * of the supplied name; splitted by a whitespace unexpectedly. 10028c2ecf20Sopenharmony_ci */ 10038c2ecf20Sopenharmony_ci char *r, *w; 10048c2ecf20Sopenharmony_ci int c1, c2; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci r = w = namebuf; 10078c2ecf20Sopenharmony_ci do { 10088c2ecf20Sopenharmony_ci c1 = *r++; 10098c2ecf20Sopenharmony_ci if (c1 == '+') 10108c2ecf20Sopenharmony_ci c1 = ' '; 10118c2ecf20Sopenharmony_ci else if (c1 == '%') { 10128c2ecf20Sopenharmony_ci c1 = hex_to_bin(*r++); 10138c2ecf20Sopenharmony_ci if (c1 < 0) 10148c2ecf20Sopenharmony_ci goto out; 10158c2ecf20Sopenharmony_ci c2 = hex_to_bin(*r++); 10168c2ecf20Sopenharmony_ci if (c2 < 0) 10178c2ecf20Sopenharmony_ci goto out; 10188c2ecf20Sopenharmony_ci c1 = (c1 << 4) | c2; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci *w++ = c1; 10218c2ecf20Sopenharmony_ci } while (c1 != '\0'); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci objname = namebuf; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); 10278c2ecf20Sopenharmony_ci if (length) 10288c2ecf20Sopenharmony_ci goto out; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); 10318c2ecf20Sopenharmony_ci if (length) 10328c2ecf20Sopenharmony_ci goto out; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci length = security_transition_sid_user(state, ssid, tsid, tclass, 10358c2ecf20Sopenharmony_ci objname, &newsid); 10368c2ecf20Sopenharmony_ci if (length) 10378c2ecf20Sopenharmony_ci goto out; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci length = security_sid_to_context(state, newsid, &newcon, &len); 10408c2ecf20Sopenharmony_ci if (length) 10418c2ecf20Sopenharmony_ci goto out; 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci length = -ERANGE; 10448c2ecf20Sopenharmony_ci if (len > SIMPLE_TRANSACTION_LIMIT) { 10458c2ecf20Sopenharmony_ci pr_err("SELinux: %s: context size (%u) exceeds " 10468c2ecf20Sopenharmony_ci "payload max\n", __func__, len); 10478c2ecf20Sopenharmony_ci goto out; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci memcpy(buf, newcon, len); 10518c2ecf20Sopenharmony_ci length = len; 10528c2ecf20Sopenharmony_ciout: 10538c2ecf20Sopenharmony_ci kfree(newcon); 10548c2ecf20Sopenharmony_ci kfree(namebuf); 10558c2ecf20Sopenharmony_ci kfree(tcon); 10568c2ecf20Sopenharmony_ci kfree(scon); 10578c2ecf20Sopenharmony_ci return length; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 10638c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 10648c2ecf20Sopenharmony_ci char *scon = NULL, *tcon = NULL; 10658c2ecf20Sopenharmony_ci u32 ssid, tsid, newsid; 10668c2ecf20Sopenharmony_ci u16 tclass; 10678c2ecf20Sopenharmony_ci ssize_t length; 10688c2ecf20Sopenharmony_ci char *newcon = NULL; 10698c2ecf20Sopenharmony_ci u32 len; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 10728c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 10738c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__COMPUTE_RELABEL, 10748c2ecf20Sopenharmony_ci NULL); 10758c2ecf20Sopenharmony_ci if (length) 10768c2ecf20Sopenharmony_ci goto out; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci length = -ENOMEM; 10798c2ecf20Sopenharmony_ci scon = kzalloc(size + 1, GFP_KERNEL); 10808c2ecf20Sopenharmony_ci if (!scon) 10818c2ecf20Sopenharmony_ci goto out; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci length = -ENOMEM; 10848c2ecf20Sopenharmony_ci tcon = kzalloc(size + 1, GFP_KERNEL); 10858c2ecf20Sopenharmony_ci if (!tcon) 10868c2ecf20Sopenharmony_ci goto out; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci length = -EINVAL; 10898c2ecf20Sopenharmony_ci if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) 10908c2ecf20Sopenharmony_ci goto out; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); 10938c2ecf20Sopenharmony_ci if (length) 10948c2ecf20Sopenharmony_ci goto out; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); 10978c2ecf20Sopenharmony_ci if (length) 10988c2ecf20Sopenharmony_ci goto out; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci length = security_change_sid(state, ssid, tsid, tclass, &newsid); 11018c2ecf20Sopenharmony_ci if (length) 11028c2ecf20Sopenharmony_ci goto out; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci length = security_sid_to_context(state, newsid, &newcon, &len); 11058c2ecf20Sopenharmony_ci if (length) 11068c2ecf20Sopenharmony_ci goto out; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci length = -ERANGE; 11098c2ecf20Sopenharmony_ci if (len > SIMPLE_TRANSACTION_LIMIT) 11108c2ecf20Sopenharmony_ci goto out; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci memcpy(buf, newcon, len); 11138c2ecf20Sopenharmony_ci length = len; 11148c2ecf20Sopenharmony_ciout: 11158c2ecf20Sopenharmony_ci kfree(newcon); 11168c2ecf20Sopenharmony_ci kfree(tcon); 11178c2ecf20Sopenharmony_ci kfree(scon); 11188c2ecf20Sopenharmony_ci return length; 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic ssize_t sel_write_user(struct file *file, char *buf, size_t size) 11228c2ecf20Sopenharmony_ci{ 11238c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 11248c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 11258c2ecf20Sopenharmony_ci char *con = NULL, *user = NULL, *ptr; 11268c2ecf20Sopenharmony_ci u32 sid, *sids = NULL; 11278c2ecf20Sopenharmony_ci ssize_t length; 11288c2ecf20Sopenharmony_ci char *newcon; 11298c2ecf20Sopenharmony_ci int i, rc; 11308c2ecf20Sopenharmony_ci u32 len, nsids; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 11338c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 11348c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__COMPUTE_USER, 11358c2ecf20Sopenharmony_ci NULL); 11368c2ecf20Sopenharmony_ci if (length) 11378c2ecf20Sopenharmony_ci goto out; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci length = -ENOMEM; 11408c2ecf20Sopenharmony_ci con = kzalloc(size + 1, GFP_KERNEL); 11418c2ecf20Sopenharmony_ci if (!con) 11428c2ecf20Sopenharmony_ci goto out; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci length = -ENOMEM; 11458c2ecf20Sopenharmony_ci user = kzalloc(size + 1, GFP_KERNEL); 11468c2ecf20Sopenharmony_ci if (!user) 11478c2ecf20Sopenharmony_ci goto out; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci length = -EINVAL; 11508c2ecf20Sopenharmony_ci if (sscanf(buf, "%s %s", con, user) != 2) 11518c2ecf20Sopenharmony_ci goto out; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, con, &sid, GFP_KERNEL); 11548c2ecf20Sopenharmony_ci if (length) 11558c2ecf20Sopenharmony_ci goto out; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci length = security_get_user_sids(state, sid, user, &sids, &nsids); 11588c2ecf20Sopenharmony_ci if (length) 11598c2ecf20Sopenharmony_ci goto out; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci length = sprintf(buf, "%u", nsids) + 1; 11628c2ecf20Sopenharmony_ci ptr = buf + length; 11638c2ecf20Sopenharmony_ci for (i = 0; i < nsids; i++) { 11648c2ecf20Sopenharmony_ci rc = security_sid_to_context(state, sids[i], &newcon, &len); 11658c2ecf20Sopenharmony_ci if (rc) { 11668c2ecf20Sopenharmony_ci length = rc; 11678c2ecf20Sopenharmony_ci goto out; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci if ((length + len) >= SIMPLE_TRANSACTION_LIMIT) { 11708c2ecf20Sopenharmony_ci kfree(newcon); 11718c2ecf20Sopenharmony_ci length = -ERANGE; 11728c2ecf20Sopenharmony_ci goto out; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci memcpy(ptr, newcon, len); 11758c2ecf20Sopenharmony_ci kfree(newcon); 11768c2ecf20Sopenharmony_ci ptr += len; 11778c2ecf20Sopenharmony_ci length += len; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ciout: 11808c2ecf20Sopenharmony_ci kfree(sids); 11818c2ecf20Sopenharmony_ci kfree(user); 11828c2ecf20Sopenharmony_ci kfree(con); 11838c2ecf20Sopenharmony_ci return length; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic ssize_t sel_write_member(struct file *file, char *buf, size_t size) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 11898c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 11908c2ecf20Sopenharmony_ci char *scon = NULL, *tcon = NULL; 11918c2ecf20Sopenharmony_ci u32 ssid, tsid, newsid; 11928c2ecf20Sopenharmony_ci u16 tclass; 11938c2ecf20Sopenharmony_ci ssize_t length; 11948c2ecf20Sopenharmony_ci char *newcon = NULL; 11958c2ecf20Sopenharmony_ci u32 len; 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 11988c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 11998c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__COMPUTE_MEMBER, 12008c2ecf20Sopenharmony_ci NULL); 12018c2ecf20Sopenharmony_ci if (length) 12028c2ecf20Sopenharmony_ci goto out; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci length = -ENOMEM; 12058c2ecf20Sopenharmony_ci scon = kzalloc(size + 1, GFP_KERNEL); 12068c2ecf20Sopenharmony_ci if (!scon) 12078c2ecf20Sopenharmony_ci goto out; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci length = -ENOMEM; 12108c2ecf20Sopenharmony_ci tcon = kzalloc(size + 1, GFP_KERNEL); 12118c2ecf20Sopenharmony_ci if (!tcon) 12128c2ecf20Sopenharmony_ci goto out; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci length = -EINVAL; 12158c2ecf20Sopenharmony_ci if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) 12168c2ecf20Sopenharmony_ci goto out; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, scon, &ssid, GFP_KERNEL); 12198c2ecf20Sopenharmony_ci if (length) 12208c2ecf20Sopenharmony_ci goto out; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci length = security_context_str_to_sid(state, tcon, &tsid, GFP_KERNEL); 12238c2ecf20Sopenharmony_ci if (length) 12248c2ecf20Sopenharmony_ci goto out; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci length = security_member_sid(state, ssid, tsid, tclass, &newsid); 12278c2ecf20Sopenharmony_ci if (length) 12288c2ecf20Sopenharmony_ci goto out; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci length = security_sid_to_context(state, newsid, &newcon, &len); 12318c2ecf20Sopenharmony_ci if (length) 12328c2ecf20Sopenharmony_ci goto out; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci length = -ERANGE; 12358c2ecf20Sopenharmony_ci if (len > SIMPLE_TRANSACTION_LIMIT) { 12368c2ecf20Sopenharmony_ci pr_err("SELinux: %s: context size (%u) exceeds " 12378c2ecf20Sopenharmony_ci "payload max\n", __func__, len); 12388c2ecf20Sopenharmony_ci goto out; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci memcpy(buf, newcon, len); 12428c2ecf20Sopenharmony_ci length = len; 12438c2ecf20Sopenharmony_ciout: 12448c2ecf20Sopenharmony_ci kfree(newcon); 12458c2ecf20Sopenharmony_ci kfree(tcon); 12468c2ecf20Sopenharmony_ci kfree(scon); 12478c2ecf20Sopenharmony_ci return length; 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic struct inode *sel_make_inode(struct super_block *sb, int mode) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci struct inode *ret = new_inode(sb); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci if (ret) { 12558c2ecf20Sopenharmony_ci ret->i_mode = mode; 12568c2ecf20Sopenharmony_ci ret->i_atime = ret->i_mtime = ret->i_ctime = current_time(ret); 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci return ret; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_cistatic ssize_t sel_read_bool(struct file *filep, char __user *buf, 12628c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; 12658c2ecf20Sopenharmony_ci char *page = NULL; 12668c2ecf20Sopenharmony_ci ssize_t length; 12678c2ecf20Sopenharmony_ci ssize_t ret; 12688c2ecf20Sopenharmony_ci int cur_enforcing; 12698c2ecf20Sopenharmony_ci unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; 12708c2ecf20Sopenharmony_ci const char *name = filep->f_path.dentry->d_name.name; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci mutex_lock(&fsi->state->policy_mutex); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci ret = -EINVAL; 12758c2ecf20Sopenharmony_ci if (index >= fsi->bool_num || strcmp(name, 12768c2ecf20Sopenharmony_ci fsi->bool_pending_names[index])) 12778c2ecf20Sopenharmony_ci goto out_unlock; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci ret = -ENOMEM; 12808c2ecf20Sopenharmony_ci page = (char *)get_zeroed_page(GFP_KERNEL); 12818c2ecf20Sopenharmony_ci if (!page) 12828c2ecf20Sopenharmony_ci goto out_unlock; 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci cur_enforcing = security_get_bool_value(fsi->state, index); 12858c2ecf20Sopenharmony_ci if (cur_enforcing < 0) { 12868c2ecf20Sopenharmony_ci ret = cur_enforcing; 12878c2ecf20Sopenharmony_ci goto out_unlock; 12888c2ecf20Sopenharmony_ci } 12898c2ecf20Sopenharmony_ci length = scnprintf(page, PAGE_SIZE, "%d %d", cur_enforcing, 12908c2ecf20Sopenharmony_ci fsi->bool_pending_values[index]); 12918c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 12928c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(buf, count, ppos, page, length); 12938c2ecf20Sopenharmony_ciout_free: 12948c2ecf20Sopenharmony_ci free_page((unsigned long)page); 12958c2ecf20Sopenharmony_ci return ret; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ciout_unlock: 12988c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 12998c2ecf20Sopenharmony_ci goto out_free; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic ssize_t sel_write_bool(struct file *filep, const char __user *buf, 13038c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 13048c2ecf20Sopenharmony_ci{ 13058c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; 13068c2ecf20Sopenharmony_ci char *page = NULL; 13078c2ecf20Sopenharmony_ci ssize_t length; 13088c2ecf20Sopenharmony_ci int new_value; 13098c2ecf20Sopenharmony_ci unsigned index = file_inode(filep)->i_ino & SEL_INO_MASK; 13108c2ecf20Sopenharmony_ci const char *name = filep->f_path.dentry->d_name.name; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 13138c2ecf20Sopenharmony_ci return -ENOMEM; 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci /* No partial writes. */ 13168c2ecf20Sopenharmony_ci if (*ppos != 0) 13178c2ecf20Sopenharmony_ci return -EINVAL; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci page = memdup_user_nul(buf, count); 13208c2ecf20Sopenharmony_ci if (IS_ERR(page)) 13218c2ecf20Sopenharmony_ci return PTR_ERR(page); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci mutex_lock(&fsi->state->policy_mutex); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 13268c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 13278c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__SETBOOL, 13288c2ecf20Sopenharmony_ci NULL); 13298c2ecf20Sopenharmony_ci if (length) 13308c2ecf20Sopenharmony_ci goto out; 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_ci length = -EINVAL; 13338c2ecf20Sopenharmony_ci if (index >= fsi->bool_num || strcmp(name, 13348c2ecf20Sopenharmony_ci fsi->bool_pending_names[index])) 13358c2ecf20Sopenharmony_ci goto out; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci length = -EINVAL; 13388c2ecf20Sopenharmony_ci if (sscanf(page, "%d", &new_value) != 1) 13398c2ecf20Sopenharmony_ci goto out; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (new_value) 13428c2ecf20Sopenharmony_ci new_value = 1; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci fsi->bool_pending_values[index] = new_value; 13458c2ecf20Sopenharmony_ci length = count; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ciout: 13488c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 13498c2ecf20Sopenharmony_ci kfree(page); 13508c2ecf20Sopenharmony_ci return length; 13518c2ecf20Sopenharmony_ci} 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_cistatic const struct file_operations sel_bool_ops = { 13548c2ecf20Sopenharmony_ci .read = sel_read_bool, 13558c2ecf20Sopenharmony_ci .write = sel_write_bool, 13568c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 13578c2ecf20Sopenharmony_ci}; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic ssize_t sel_commit_bools_write(struct file *filep, 13608c2ecf20Sopenharmony_ci const char __user *buf, 13618c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filep)->i_sb->s_fs_info; 13648c2ecf20Sopenharmony_ci char *page = NULL; 13658c2ecf20Sopenharmony_ci ssize_t length; 13668c2ecf20Sopenharmony_ci int new_value; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 13698c2ecf20Sopenharmony_ci return -ENOMEM; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* No partial writes. */ 13728c2ecf20Sopenharmony_ci if (*ppos != 0) 13738c2ecf20Sopenharmony_ci return -EINVAL; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci page = memdup_user_nul(buf, count); 13768c2ecf20Sopenharmony_ci if (IS_ERR(page)) 13778c2ecf20Sopenharmony_ci return PTR_ERR(page); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci mutex_lock(&fsi->state->policy_mutex); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci length = avc_has_perm(&selinux_state, 13828c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 13838c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__SETBOOL, 13848c2ecf20Sopenharmony_ci NULL); 13858c2ecf20Sopenharmony_ci if (length) 13868c2ecf20Sopenharmony_ci goto out; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci length = -EINVAL; 13898c2ecf20Sopenharmony_ci if (sscanf(page, "%d", &new_value) != 1) 13908c2ecf20Sopenharmony_ci goto out; 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci length = 0; 13938c2ecf20Sopenharmony_ci if (new_value && fsi->bool_pending_values) 13948c2ecf20Sopenharmony_ci length = security_set_bools(fsi->state, fsi->bool_num, 13958c2ecf20Sopenharmony_ci fsi->bool_pending_values); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (!length) 13988c2ecf20Sopenharmony_ci length = count; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ciout: 14018c2ecf20Sopenharmony_ci mutex_unlock(&fsi->state->policy_mutex); 14028c2ecf20Sopenharmony_ci kfree(page); 14038c2ecf20Sopenharmony_ci return length; 14048c2ecf20Sopenharmony_ci} 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_cistatic const struct file_operations sel_commit_bools_ops = { 14078c2ecf20Sopenharmony_ci .write = sel_commit_bools_write, 14088c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 14098c2ecf20Sopenharmony_ci}; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic void sel_remove_entries(struct dentry *de) 14128c2ecf20Sopenharmony_ci{ 14138c2ecf20Sopenharmony_ci d_genocide(de); 14148c2ecf20Sopenharmony_ci shrink_dcache_parent(de); 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cistatic int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir, 14188c2ecf20Sopenharmony_ci unsigned int *bool_num, char ***bool_pending_names, 14198c2ecf20Sopenharmony_ci unsigned int **bool_pending_values) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci int ret; 14228c2ecf20Sopenharmony_ci ssize_t len; 14238c2ecf20Sopenharmony_ci struct dentry *dentry = NULL; 14248c2ecf20Sopenharmony_ci struct inode *inode = NULL; 14258c2ecf20Sopenharmony_ci struct inode_security_struct *isec; 14268c2ecf20Sopenharmony_ci char **names = NULL, *page; 14278c2ecf20Sopenharmony_ci u32 i, num; 14288c2ecf20Sopenharmony_ci int *values = NULL; 14298c2ecf20Sopenharmony_ci u32 sid; 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci ret = -ENOMEM; 14328c2ecf20Sopenharmony_ci page = (char *)get_zeroed_page(GFP_KERNEL); 14338c2ecf20Sopenharmony_ci if (!page) 14348c2ecf20Sopenharmony_ci goto out; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ret = security_get_bools(newpolicy, &num, &names, &values); 14378c2ecf20Sopenharmony_ci if (ret) 14388c2ecf20Sopenharmony_ci goto out; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 14418c2ecf20Sopenharmony_ci ret = -ENOMEM; 14428c2ecf20Sopenharmony_ci dentry = d_alloc_name(bool_dir, names[i]); 14438c2ecf20Sopenharmony_ci if (!dentry) 14448c2ecf20Sopenharmony_ci goto out; 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci ret = -ENOMEM; 14478c2ecf20Sopenharmony_ci inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR); 14488c2ecf20Sopenharmony_ci if (!inode) { 14498c2ecf20Sopenharmony_ci dput(dentry); 14508c2ecf20Sopenharmony_ci goto out; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci ret = -ENAMETOOLONG; 14548c2ecf20Sopenharmony_ci len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); 14558c2ecf20Sopenharmony_ci if (len >= PAGE_SIZE) { 14568c2ecf20Sopenharmony_ci dput(dentry); 14578c2ecf20Sopenharmony_ci iput(inode); 14588c2ecf20Sopenharmony_ci goto out; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci isec = selinux_inode(inode); 14628c2ecf20Sopenharmony_ci ret = selinux_policy_genfs_sid(newpolicy, "selinuxfs", page, 14638c2ecf20Sopenharmony_ci SECCLASS_FILE, &sid); 14648c2ecf20Sopenharmony_ci if (ret) { 14658c2ecf20Sopenharmony_ci pr_warn_ratelimited("SELinux: no sid found, defaulting to security isid for %s\n", 14668c2ecf20Sopenharmony_ci page); 14678c2ecf20Sopenharmony_ci sid = SECINITSID_SECURITY; 14688c2ecf20Sopenharmony_ci } 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci isec->sid = sid; 14718c2ecf20Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 14728c2ecf20Sopenharmony_ci inode->i_fop = &sel_bool_ops; 14738c2ecf20Sopenharmony_ci inode->i_ino = i|SEL_BOOL_INO_OFFSET; 14748c2ecf20Sopenharmony_ci d_add(dentry, inode); 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci *bool_num = num; 14778c2ecf20Sopenharmony_ci *bool_pending_names = names; 14788c2ecf20Sopenharmony_ci *bool_pending_values = values; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci free_page((unsigned long)page); 14818c2ecf20Sopenharmony_ci return 0; 14828c2ecf20Sopenharmony_ciout: 14838c2ecf20Sopenharmony_ci free_page((unsigned long)page); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci if (names) { 14868c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) 14878c2ecf20Sopenharmony_ci kfree(names[i]); 14888c2ecf20Sopenharmony_ci kfree(names); 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci kfree(values); 14918c2ecf20Sopenharmony_ci sel_remove_entries(bool_dir); 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci return ret; 14948c2ecf20Sopenharmony_ci} 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_cistatic ssize_t sel_read_avc_cache_threshold(struct file *filp, char __user *buf, 14978c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 15008c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 15018c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 15028c2ecf20Sopenharmony_ci ssize_t length; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%u", 15058c2ecf20Sopenharmony_ci avc_get_cache_threshold(state->avc)); 15068c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_cistatic ssize_t sel_write_avc_cache_threshold(struct file *file, 15108c2ecf20Sopenharmony_ci const char __user *buf, 15118c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci{ 15148c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 15158c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 15168c2ecf20Sopenharmony_ci char *page; 15178c2ecf20Sopenharmony_ci ssize_t ret; 15188c2ecf20Sopenharmony_ci unsigned int new_value; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci ret = avc_has_perm(&selinux_state, 15218c2ecf20Sopenharmony_ci current_sid(), SECINITSID_SECURITY, 15228c2ecf20Sopenharmony_ci SECCLASS_SECURITY, SECURITY__SETSECPARAM, 15238c2ecf20Sopenharmony_ci NULL); 15248c2ecf20Sopenharmony_ci if (ret) 15258c2ecf20Sopenharmony_ci return ret; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (count >= PAGE_SIZE) 15288c2ecf20Sopenharmony_ci return -ENOMEM; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci /* No partial writes. */ 15318c2ecf20Sopenharmony_ci if (*ppos != 0) 15328c2ecf20Sopenharmony_ci return -EINVAL; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci page = memdup_user_nul(buf, count); 15358c2ecf20Sopenharmony_ci if (IS_ERR(page)) 15368c2ecf20Sopenharmony_ci return PTR_ERR(page); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci ret = -EINVAL; 15398c2ecf20Sopenharmony_ci if (sscanf(page, "%u", &new_value) != 1) 15408c2ecf20Sopenharmony_ci goto out; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci avc_set_cache_threshold(state->avc, new_value); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci ret = count; 15458c2ecf20Sopenharmony_ciout: 15468c2ecf20Sopenharmony_ci kfree(page); 15478c2ecf20Sopenharmony_ci return ret; 15488c2ecf20Sopenharmony_ci} 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_cistatic ssize_t sel_read_avc_hash_stats(struct file *filp, char __user *buf, 15518c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 15528c2ecf20Sopenharmony_ci{ 15538c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 15548c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 15558c2ecf20Sopenharmony_ci char *page; 15568c2ecf20Sopenharmony_ci ssize_t length; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci page = (char *)__get_free_page(GFP_KERNEL); 15598c2ecf20Sopenharmony_ci if (!page) 15608c2ecf20Sopenharmony_ci return -ENOMEM; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci length = avc_get_hash_stats(state->avc, page); 15638c2ecf20Sopenharmony_ci if (length >= 0) 15648c2ecf20Sopenharmony_ci length = simple_read_from_buffer(buf, count, ppos, page, length); 15658c2ecf20Sopenharmony_ci free_page((unsigned long)page); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci return length; 15688c2ecf20Sopenharmony_ci} 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_cistatic ssize_t sel_read_sidtab_hash_stats(struct file *filp, char __user *buf, 15718c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 15728c2ecf20Sopenharmony_ci{ 15738c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(filp)->i_sb->s_fs_info; 15748c2ecf20Sopenharmony_ci struct selinux_state *state = fsi->state; 15758c2ecf20Sopenharmony_ci char *page; 15768c2ecf20Sopenharmony_ci ssize_t length; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci page = (char *)__get_free_page(GFP_KERNEL); 15798c2ecf20Sopenharmony_ci if (!page) 15808c2ecf20Sopenharmony_ci return -ENOMEM; 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci length = security_sidtab_hash_stats(state, page); 15838c2ecf20Sopenharmony_ci if (length >= 0) 15848c2ecf20Sopenharmony_ci length = simple_read_from_buffer(buf, count, ppos, page, 15858c2ecf20Sopenharmony_ci length); 15868c2ecf20Sopenharmony_ci free_page((unsigned long)page); 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ci return length; 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic const struct file_operations sel_sidtab_hash_stats_ops = { 15928c2ecf20Sopenharmony_ci .read = sel_read_sidtab_hash_stats, 15938c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 15948c2ecf20Sopenharmony_ci}; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic const struct file_operations sel_avc_cache_threshold_ops = { 15978c2ecf20Sopenharmony_ci .read = sel_read_avc_cache_threshold, 15988c2ecf20Sopenharmony_ci .write = sel_write_avc_cache_threshold, 15998c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 16008c2ecf20Sopenharmony_ci}; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic const struct file_operations sel_avc_hash_stats_ops = { 16038c2ecf20Sopenharmony_ci .read = sel_read_avc_hash_stats, 16048c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 16058c2ecf20Sopenharmony_ci}; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS 16088c2ecf20Sopenharmony_cistatic struct avc_cache_stats *sel_avc_get_stat_idx(loff_t *idx) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci int cpu; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci for (cpu = *idx; cpu < nr_cpu_ids; ++cpu) { 16138c2ecf20Sopenharmony_ci if (!cpu_possible(cpu)) 16148c2ecf20Sopenharmony_ci continue; 16158c2ecf20Sopenharmony_ci *idx = cpu + 1; 16168c2ecf20Sopenharmony_ci return &per_cpu(avc_cache_stats, cpu); 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci (*idx)++; 16198c2ecf20Sopenharmony_ci return NULL; 16208c2ecf20Sopenharmony_ci} 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_cistatic void *sel_avc_stats_seq_start(struct seq_file *seq, loff_t *pos) 16238c2ecf20Sopenharmony_ci{ 16248c2ecf20Sopenharmony_ci loff_t n = *pos - 1; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci if (*pos == 0) 16278c2ecf20Sopenharmony_ci return SEQ_START_TOKEN; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci return sel_avc_get_stat_idx(&n); 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_cistatic void *sel_avc_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos) 16338c2ecf20Sopenharmony_ci{ 16348c2ecf20Sopenharmony_ci return sel_avc_get_stat_idx(pos); 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic int sel_avc_stats_seq_show(struct seq_file *seq, void *v) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct avc_cache_stats *st = v; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) { 16428c2ecf20Sopenharmony_ci seq_puts(seq, 16438c2ecf20Sopenharmony_ci "lookups hits misses allocations reclaims frees\n"); 16448c2ecf20Sopenharmony_ci } else { 16458c2ecf20Sopenharmony_ci unsigned int lookups = st->lookups; 16468c2ecf20Sopenharmony_ci unsigned int misses = st->misses; 16478c2ecf20Sopenharmony_ci unsigned int hits = lookups - misses; 16488c2ecf20Sopenharmony_ci seq_printf(seq, "%u %u %u %u %u %u\n", lookups, 16498c2ecf20Sopenharmony_ci hits, misses, st->allocations, 16508c2ecf20Sopenharmony_ci st->reclaims, st->frees); 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci return 0; 16538c2ecf20Sopenharmony_ci} 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_cistatic void sel_avc_stats_seq_stop(struct seq_file *seq, void *v) 16568c2ecf20Sopenharmony_ci{ } 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic const struct seq_operations sel_avc_cache_stats_seq_ops = { 16598c2ecf20Sopenharmony_ci .start = sel_avc_stats_seq_start, 16608c2ecf20Sopenharmony_ci .next = sel_avc_stats_seq_next, 16618c2ecf20Sopenharmony_ci .show = sel_avc_stats_seq_show, 16628c2ecf20Sopenharmony_ci .stop = sel_avc_stats_seq_stop, 16638c2ecf20Sopenharmony_ci}; 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic int sel_open_avc_cache_stats(struct inode *inode, struct file *file) 16668c2ecf20Sopenharmony_ci{ 16678c2ecf20Sopenharmony_ci return seq_open(file, &sel_avc_cache_stats_seq_ops); 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic const struct file_operations sel_avc_cache_stats_ops = { 16718c2ecf20Sopenharmony_ci .open = sel_open_avc_cache_stats, 16728c2ecf20Sopenharmony_ci .read = seq_read, 16738c2ecf20Sopenharmony_ci .llseek = seq_lseek, 16748c2ecf20Sopenharmony_ci .release = seq_release, 16758c2ecf20Sopenharmony_ci}; 16768c2ecf20Sopenharmony_ci#endif 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_cistatic int sel_make_avc_files(struct dentry *dir) 16798c2ecf20Sopenharmony_ci{ 16808c2ecf20Sopenharmony_ci struct super_block *sb = dir->d_sb; 16818c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = sb->s_fs_info; 16828c2ecf20Sopenharmony_ci int i; 16838c2ecf20Sopenharmony_ci static const struct tree_descr files[] = { 16848c2ecf20Sopenharmony_ci { "cache_threshold", 16858c2ecf20Sopenharmony_ci &sel_avc_cache_threshold_ops, S_IRUGO|S_IWUSR }, 16868c2ecf20Sopenharmony_ci { "hash_stats", &sel_avc_hash_stats_ops, S_IRUGO }, 16878c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS 16888c2ecf20Sopenharmony_ci { "cache_stats", &sel_avc_cache_stats_ops, S_IRUGO }, 16898c2ecf20Sopenharmony_ci#endif 16908c2ecf20Sopenharmony_ci }; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(files); i++) { 16938c2ecf20Sopenharmony_ci struct inode *inode; 16948c2ecf20Sopenharmony_ci struct dentry *dentry; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci dentry = d_alloc_name(dir, files[i].name); 16978c2ecf20Sopenharmony_ci if (!dentry) 16988c2ecf20Sopenharmony_ci return -ENOMEM; 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); 17018c2ecf20Sopenharmony_ci if (!inode) { 17028c2ecf20Sopenharmony_ci dput(dentry); 17038c2ecf20Sopenharmony_ci return -ENOMEM; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci inode->i_fop = files[i].ops; 17078c2ecf20Sopenharmony_ci inode->i_ino = ++fsi->last_ino; 17088c2ecf20Sopenharmony_ci d_add(dentry, inode); 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci return 0; 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_cistatic int sel_make_ss_files(struct dentry *dir) 17158c2ecf20Sopenharmony_ci{ 17168c2ecf20Sopenharmony_ci struct super_block *sb = dir->d_sb; 17178c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = sb->s_fs_info; 17188c2ecf20Sopenharmony_ci int i; 17198c2ecf20Sopenharmony_ci static struct tree_descr files[] = { 17208c2ecf20Sopenharmony_ci { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO }, 17218c2ecf20Sopenharmony_ci }; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(files); i++) { 17248c2ecf20Sopenharmony_ci struct inode *inode; 17258c2ecf20Sopenharmony_ci struct dentry *dentry; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci dentry = d_alloc_name(dir, files[i].name); 17288c2ecf20Sopenharmony_ci if (!dentry) 17298c2ecf20Sopenharmony_ci return -ENOMEM; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode); 17328c2ecf20Sopenharmony_ci if (!inode) { 17338c2ecf20Sopenharmony_ci dput(dentry); 17348c2ecf20Sopenharmony_ci return -ENOMEM; 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci inode->i_fop = files[i].ops; 17388c2ecf20Sopenharmony_ci inode->i_ino = ++fsi->last_ino; 17398c2ecf20Sopenharmony_ci d_add(dentry, inode); 17408c2ecf20Sopenharmony_ci } 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci return 0; 17438c2ecf20Sopenharmony_ci} 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_cistatic ssize_t sel_read_initcon(struct file *file, char __user *buf, 17468c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 17478c2ecf20Sopenharmony_ci{ 17488c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 17498c2ecf20Sopenharmony_ci char *con; 17508c2ecf20Sopenharmony_ci u32 sid, len; 17518c2ecf20Sopenharmony_ci ssize_t ret; 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci sid = file_inode(file)->i_ino&SEL_INO_MASK; 17548c2ecf20Sopenharmony_ci ret = security_sid_to_context(fsi->state, sid, &con, &len); 17558c2ecf20Sopenharmony_ci if (ret) 17568c2ecf20Sopenharmony_ci return ret; 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(buf, count, ppos, con, len); 17598c2ecf20Sopenharmony_ci kfree(con); 17608c2ecf20Sopenharmony_ci return ret; 17618c2ecf20Sopenharmony_ci} 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_cistatic const struct file_operations sel_initcon_ops = { 17648c2ecf20Sopenharmony_ci .read = sel_read_initcon, 17658c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 17668c2ecf20Sopenharmony_ci}; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic int sel_make_initcon_files(struct dentry *dir) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci int i; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci for (i = 1; i <= SECINITSID_NUM; i++) { 17738c2ecf20Sopenharmony_ci struct inode *inode; 17748c2ecf20Sopenharmony_ci struct dentry *dentry; 17758c2ecf20Sopenharmony_ci const char *s = security_get_initial_sid_context(i); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (!s) 17788c2ecf20Sopenharmony_ci continue; 17798c2ecf20Sopenharmony_ci dentry = d_alloc_name(dir, s); 17808c2ecf20Sopenharmony_ci if (!dentry) 17818c2ecf20Sopenharmony_ci return -ENOMEM; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); 17848c2ecf20Sopenharmony_ci if (!inode) { 17858c2ecf20Sopenharmony_ci dput(dentry); 17868c2ecf20Sopenharmony_ci return -ENOMEM; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci inode->i_fop = &sel_initcon_ops; 17908c2ecf20Sopenharmony_ci inode->i_ino = i|SEL_INITCON_INO_OFFSET; 17918c2ecf20Sopenharmony_ci d_add(dentry, inode); 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci return 0; 17958c2ecf20Sopenharmony_ci} 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_cistatic inline unsigned long sel_class_to_ino(u16 class) 17988c2ecf20Sopenharmony_ci{ 17998c2ecf20Sopenharmony_ci return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; 18008c2ecf20Sopenharmony_ci} 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_cistatic inline u16 sel_ino_to_class(unsigned long ino) 18038c2ecf20Sopenharmony_ci{ 18048c2ecf20Sopenharmony_ci return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1); 18058c2ecf20Sopenharmony_ci} 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_cistatic inline unsigned long sel_perm_to_ino(u16 class, u32 perm) 18088c2ecf20Sopenharmony_ci{ 18098c2ecf20Sopenharmony_ci return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic inline u32 sel_ino_to_perm(unsigned long ino) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); 18158c2ecf20Sopenharmony_ci} 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_cistatic ssize_t sel_read_class(struct file *file, char __user *buf, 18188c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 18198c2ecf20Sopenharmony_ci{ 18208c2ecf20Sopenharmony_ci unsigned long ino = file_inode(file)->i_ino; 18218c2ecf20Sopenharmony_ci char res[TMPBUFLEN]; 18228c2ecf20Sopenharmony_ci ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_class(ino)); 18238c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, res, len); 18248c2ecf20Sopenharmony_ci} 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_cistatic const struct file_operations sel_class_ops = { 18278c2ecf20Sopenharmony_ci .read = sel_read_class, 18288c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 18298c2ecf20Sopenharmony_ci}; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_cistatic ssize_t sel_read_perm(struct file *file, char __user *buf, 18328c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 18338c2ecf20Sopenharmony_ci{ 18348c2ecf20Sopenharmony_ci unsigned long ino = file_inode(file)->i_ino; 18358c2ecf20Sopenharmony_ci char res[TMPBUFLEN]; 18368c2ecf20Sopenharmony_ci ssize_t len = scnprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino)); 18378c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, res, len); 18388c2ecf20Sopenharmony_ci} 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_cistatic const struct file_operations sel_perm_ops = { 18418c2ecf20Sopenharmony_ci .read = sel_read_perm, 18428c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 18438c2ecf20Sopenharmony_ci}; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic ssize_t sel_read_policycap(struct file *file, char __user *buf, 18468c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 18478c2ecf20Sopenharmony_ci{ 18488c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = file_inode(file)->i_sb->s_fs_info; 18498c2ecf20Sopenharmony_ci int value; 18508c2ecf20Sopenharmony_ci char tmpbuf[TMPBUFLEN]; 18518c2ecf20Sopenharmony_ci ssize_t length; 18528c2ecf20Sopenharmony_ci unsigned long i_ino = file_inode(file)->i_ino; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci value = security_policycap_supported(fsi->state, i_ino & SEL_INO_MASK); 18558c2ecf20Sopenharmony_ci length = scnprintf(tmpbuf, TMPBUFLEN, "%d", value); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci return simple_read_from_buffer(buf, count, ppos, tmpbuf, length); 18588c2ecf20Sopenharmony_ci} 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_cistatic const struct file_operations sel_policycap_ops = { 18618c2ecf20Sopenharmony_ci .read = sel_read_policycap, 18628c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 18638c2ecf20Sopenharmony_ci}; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_cistatic int sel_make_perm_files(struct selinux_policy *newpolicy, 18668c2ecf20Sopenharmony_ci char *objclass, int classvalue, 18678c2ecf20Sopenharmony_ci struct dentry *dir) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci int i, rc, nperms; 18708c2ecf20Sopenharmony_ci char **perms; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci rc = security_get_permissions(newpolicy, objclass, &perms, &nperms); 18738c2ecf20Sopenharmony_ci if (rc) 18748c2ecf20Sopenharmony_ci return rc; 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci for (i = 0; i < nperms; i++) { 18778c2ecf20Sopenharmony_ci struct inode *inode; 18788c2ecf20Sopenharmony_ci struct dentry *dentry; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci rc = -ENOMEM; 18818c2ecf20Sopenharmony_ci dentry = d_alloc_name(dir, perms[i]); 18828c2ecf20Sopenharmony_ci if (!dentry) 18838c2ecf20Sopenharmony_ci goto out; 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci rc = -ENOMEM; 18868c2ecf20Sopenharmony_ci inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); 18878c2ecf20Sopenharmony_ci if (!inode) { 18888c2ecf20Sopenharmony_ci dput(dentry); 18898c2ecf20Sopenharmony_ci goto out; 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci inode->i_fop = &sel_perm_ops; 18938c2ecf20Sopenharmony_ci /* i+1 since perm values are 1-indexed */ 18948c2ecf20Sopenharmony_ci inode->i_ino = sel_perm_to_ino(classvalue, i + 1); 18958c2ecf20Sopenharmony_ci d_add(dentry, inode); 18968c2ecf20Sopenharmony_ci } 18978c2ecf20Sopenharmony_ci rc = 0; 18988c2ecf20Sopenharmony_ciout: 18998c2ecf20Sopenharmony_ci for (i = 0; i < nperms; i++) 19008c2ecf20Sopenharmony_ci kfree(perms[i]); 19018c2ecf20Sopenharmony_ci kfree(perms); 19028c2ecf20Sopenharmony_ci return rc; 19038c2ecf20Sopenharmony_ci} 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_cistatic int sel_make_class_dir_entries(struct selinux_policy *newpolicy, 19068c2ecf20Sopenharmony_ci char *classname, int index, 19078c2ecf20Sopenharmony_ci struct dentry *dir) 19088c2ecf20Sopenharmony_ci{ 19098c2ecf20Sopenharmony_ci struct super_block *sb = dir->d_sb; 19108c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi = sb->s_fs_info; 19118c2ecf20Sopenharmony_ci struct dentry *dentry = NULL; 19128c2ecf20Sopenharmony_ci struct inode *inode = NULL; 19138c2ecf20Sopenharmony_ci int rc; 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci dentry = d_alloc_name(dir, "index"); 19168c2ecf20Sopenharmony_ci if (!dentry) 19178c2ecf20Sopenharmony_ci return -ENOMEM; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); 19208c2ecf20Sopenharmony_ci if (!inode) { 19218c2ecf20Sopenharmony_ci dput(dentry); 19228c2ecf20Sopenharmony_ci return -ENOMEM; 19238c2ecf20Sopenharmony_ci } 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci inode->i_fop = &sel_class_ops; 19268c2ecf20Sopenharmony_ci inode->i_ino = sel_class_to_ino(index); 19278c2ecf20Sopenharmony_ci d_add(dentry, inode); 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci dentry = sel_make_dir(dir, "perms", &fsi->last_class_ino); 19308c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) 19318c2ecf20Sopenharmony_ci return PTR_ERR(dentry); 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci rc = sel_make_perm_files(newpolicy, classname, index, dentry); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci return rc; 19368c2ecf20Sopenharmony_ci} 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_cistatic int sel_make_classes(struct selinux_policy *newpolicy, 19398c2ecf20Sopenharmony_ci struct dentry *class_dir, 19408c2ecf20Sopenharmony_ci unsigned long *last_class_ino) 19418c2ecf20Sopenharmony_ci{ 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci int rc, nclasses, i; 19448c2ecf20Sopenharmony_ci char **classes; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci rc = security_get_classes(newpolicy, &classes, &nclasses); 19478c2ecf20Sopenharmony_ci if (rc) 19488c2ecf20Sopenharmony_ci return rc; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci /* +2 since classes are 1-indexed */ 19518c2ecf20Sopenharmony_ci *last_class_ino = sel_class_to_ino(nclasses + 2); 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci for (i = 0; i < nclasses; i++) { 19548c2ecf20Sopenharmony_ci struct dentry *class_name_dir; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci class_name_dir = sel_make_dir(class_dir, classes[i], 19578c2ecf20Sopenharmony_ci last_class_ino); 19588c2ecf20Sopenharmony_ci if (IS_ERR(class_name_dir)) { 19598c2ecf20Sopenharmony_ci rc = PTR_ERR(class_name_dir); 19608c2ecf20Sopenharmony_ci goto out; 19618c2ecf20Sopenharmony_ci } 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci /* i+1 since class values are 1-indexed */ 19648c2ecf20Sopenharmony_ci rc = sel_make_class_dir_entries(newpolicy, classes[i], i + 1, 19658c2ecf20Sopenharmony_ci class_name_dir); 19668c2ecf20Sopenharmony_ci if (rc) 19678c2ecf20Sopenharmony_ci goto out; 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci rc = 0; 19708c2ecf20Sopenharmony_ciout: 19718c2ecf20Sopenharmony_ci for (i = 0; i < nclasses; i++) 19728c2ecf20Sopenharmony_ci kfree(classes[i]); 19738c2ecf20Sopenharmony_ci kfree(classes); 19748c2ecf20Sopenharmony_ci return rc; 19758c2ecf20Sopenharmony_ci} 19768c2ecf20Sopenharmony_ci 19778c2ecf20Sopenharmony_cistatic int sel_make_policycap(struct selinux_fs_info *fsi) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci unsigned int iter; 19808c2ecf20Sopenharmony_ci struct dentry *dentry = NULL; 19818c2ecf20Sopenharmony_ci struct inode *inode = NULL; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci for (iter = 0; iter <= POLICYDB_CAPABILITY_MAX; iter++) { 19848c2ecf20Sopenharmony_ci if (iter < ARRAY_SIZE(selinux_policycap_names)) 19858c2ecf20Sopenharmony_ci dentry = d_alloc_name(fsi->policycap_dir, 19868c2ecf20Sopenharmony_ci selinux_policycap_names[iter]); 19878c2ecf20Sopenharmony_ci else 19888c2ecf20Sopenharmony_ci dentry = d_alloc_name(fsi->policycap_dir, "unknown"); 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci if (dentry == NULL) 19918c2ecf20Sopenharmony_ci return -ENOMEM; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci inode = sel_make_inode(fsi->sb, S_IFREG | 0444); 19948c2ecf20Sopenharmony_ci if (inode == NULL) { 19958c2ecf20Sopenharmony_ci dput(dentry); 19968c2ecf20Sopenharmony_ci return -ENOMEM; 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci inode->i_fop = &sel_policycap_ops; 20008c2ecf20Sopenharmony_ci inode->i_ino = iter | SEL_POLICYCAP_INO_OFFSET; 20018c2ecf20Sopenharmony_ci d_add(dentry, inode); 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci return 0; 20058c2ecf20Sopenharmony_ci} 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_cistatic struct dentry *sel_make_dir(struct dentry *dir, const char *name, 20088c2ecf20Sopenharmony_ci unsigned long *ino) 20098c2ecf20Sopenharmony_ci{ 20108c2ecf20Sopenharmony_ci struct dentry *dentry = d_alloc_name(dir, name); 20118c2ecf20Sopenharmony_ci struct inode *inode; 20128c2ecf20Sopenharmony_ci 20138c2ecf20Sopenharmony_ci if (!dentry) 20148c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci inode = sel_make_inode(dir->d_sb, S_IFDIR | S_IRUGO | S_IXUGO); 20178c2ecf20Sopenharmony_ci if (!inode) { 20188c2ecf20Sopenharmony_ci dput(dentry); 20198c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 20208c2ecf20Sopenharmony_ci } 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 20238c2ecf20Sopenharmony_ci inode->i_fop = &simple_dir_operations; 20248c2ecf20Sopenharmony_ci inode->i_ino = ++(*ino); 20258c2ecf20Sopenharmony_ci /* directory inodes start off with i_nlink == 2 (for "." entry) */ 20268c2ecf20Sopenharmony_ci inc_nlink(inode); 20278c2ecf20Sopenharmony_ci d_add(dentry, inode); 20288c2ecf20Sopenharmony_ci /* bump link count on parent directory, too */ 20298c2ecf20Sopenharmony_ci inc_nlink(d_inode(dir)); 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci return dentry; 20328c2ecf20Sopenharmony_ci} 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_cistatic struct dentry *sel_make_disconnected_dir(struct super_block *sb, 20358c2ecf20Sopenharmony_ci unsigned long *ino) 20368c2ecf20Sopenharmony_ci{ 20378c2ecf20Sopenharmony_ci struct inode *inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO); 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci if (!inode) 20408c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci inode->i_op = &simple_dir_inode_operations; 20438c2ecf20Sopenharmony_ci inode->i_fop = &simple_dir_operations; 20448c2ecf20Sopenharmony_ci inode->i_ino = ++(*ino); 20458c2ecf20Sopenharmony_ci /* directory inodes start off with i_nlink == 2 (for "." entry) */ 20468c2ecf20Sopenharmony_ci inc_nlink(inode); 20478c2ecf20Sopenharmony_ci return d_obtain_alias(inode); 20488c2ecf20Sopenharmony_ci} 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci#define NULL_FILE_NAME "null" 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic int sel_fill_super(struct super_block *sb, struct fs_context *fc) 20538c2ecf20Sopenharmony_ci{ 20548c2ecf20Sopenharmony_ci struct selinux_fs_info *fsi; 20558c2ecf20Sopenharmony_ci int ret; 20568c2ecf20Sopenharmony_ci struct dentry *dentry; 20578c2ecf20Sopenharmony_ci struct inode *inode; 20588c2ecf20Sopenharmony_ci struct inode_security_struct *isec; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci static const struct tree_descr selinux_files[] = { 20618c2ecf20Sopenharmony_ci [SEL_LOAD] = {"load", &sel_load_ops, S_IRUSR|S_IWUSR}, 20628c2ecf20Sopenharmony_ci [SEL_ENFORCE] = {"enforce", &sel_enforce_ops, S_IRUGO|S_IWUSR}, 20638c2ecf20Sopenharmony_ci [SEL_CONTEXT] = {"context", &transaction_ops, S_IRUGO|S_IWUGO}, 20648c2ecf20Sopenharmony_ci [SEL_ACCESS] = {"access", &transaction_ops, S_IRUGO|S_IWUGO}, 20658c2ecf20Sopenharmony_ci [SEL_CREATE] = {"create", &transaction_ops, S_IRUGO|S_IWUGO}, 20668c2ecf20Sopenharmony_ci [SEL_RELABEL] = {"relabel", &transaction_ops, S_IRUGO|S_IWUGO}, 20678c2ecf20Sopenharmony_ci [SEL_USER] = {"user", &transaction_ops, S_IRUGO|S_IWUGO}, 20688c2ecf20Sopenharmony_ci [SEL_POLICYVERS] = {"policyvers", &sel_policyvers_ops, S_IRUGO}, 20698c2ecf20Sopenharmony_ci [SEL_COMMIT_BOOLS] = {"commit_pending_bools", &sel_commit_bools_ops, S_IWUSR}, 20708c2ecf20Sopenharmony_ci [SEL_MLS] = {"mls", &sel_mls_ops, S_IRUGO}, 20718c2ecf20Sopenharmony_ci [SEL_DISABLE] = {"disable", &sel_disable_ops, S_IWUSR}, 20728c2ecf20Sopenharmony_ci [SEL_MEMBER] = {"member", &transaction_ops, S_IRUGO|S_IWUGO}, 20738c2ecf20Sopenharmony_ci [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR}, 20748c2ecf20Sopenharmony_ci [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, 20758c2ecf20Sopenharmony_ci [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, 20768c2ecf20Sopenharmony_ci [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, 20778c2ecf20Sopenharmony_ci [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, 20788c2ecf20Sopenharmony_ci [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops, 20798c2ecf20Sopenharmony_ci S_IWUGO}, 20808c2ecf20Sopenharmony_ci /* last one */ {""} 20818c2ecf20Sopenharmony_ci }; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci ret = selinux_fs_info_create(sb); 20848c2ecf20Sopenharmony_ci if (ret) 20858c2ecf20Sopenharmony_ci goto err; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); 20888c2ecf20Sopenharmony_ci if (ret) 20898c2ecf20Sopenharmony_ci goto err; 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci fsi = sb->s_fs_info; 20928c2ecf20Sopenharmony_ci fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino); 20938c2ecf20Sopenharmony_ci if (IS_ERR(fsi->bool_dir)) { 20948c2ecf20Sopenharmony_ci ret = PTR_ERR(fsi->bool_dir); 20958c2ecf20Sopenharmony_ci fsi->bool_dir = NULL; 20968c2ecf20Sopenharmony_ci goto err; 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci ret = -ENOMEM; 21008c2ecf20Sopenharmony_ci dentry = d_alloc_name(sb->s_root, NULL_FILE_NAME); 21018c2ecf20Sopenharmony_ci if (!dentry) 21028c2ecf20Sopenharmony_ci goto err; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci ret = -ENOMEM; 21058c2ecf20Sopenharmony_ci inode = sel_make_inode(sb, S_IFCHR | S_IRUGO | S_IWUGO); 21068c2ecf20Sopenharmony_ci if (!inode) { 21078c2ecf20Sopenharmony_ci dput(dentry); 21088c2ecf20Sopenharmony_ci goto err; 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci inode->i_ino = ++fsi->last_ino; 21128c2ecf20Sopenharmony_ci isec = selinux_inode(inode); 21138c2ecf20Sopenharmony_ci isec->sid = SECINITSID_DEVNULL; 21148c2ecf20Sopenharmony_ci isec->sclass = SECCLASS_CHR_FILE; 21158c2ecf20Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); 21188c2ecf20Sopenharmony_ci d_add(dentry, inode); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci dentry = sel_make_dir(sb->s_root, "avc", &fsi->last_ino); 21218c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 21228c2ecf20Sopenharmony_ci ret = PTR_ERR(dentry); 21238c2ecf20Sopenharmony_ci goto err; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci ret = sel_make_avc_files(dentry); 21278c2ecf20Sopenharmony_ci if (ret) 21288c2ecf20Sopenharmony_ci goto err; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci dentry = sel_make_dir(sb->s_root, "ss", &fsi->last_ino); 21318c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 21328c2ecf20Sopenharmony_ci ret = PTR_ERR(dentry); 21338c2ecf20Sopenharmony_ci goto err; 21348c2ecf20Sopenharmony_ci } 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci ret = sel_make_ss_files(dentry); 21378c2ecf20Sopenharmony_ci if (ret) 21388c2ecf20Sopenharmony_ci goto err; 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci dentry = sel_make_dir(sb->s_root, "initial_contexts", &fsi->last_ino); 21418c2ecf20Sopenharmony_ci if (IS_ERR(dentry)) { 21428c2ecf20Sopenharmony_ci ret = PTR_ERR(dentry); 21438c2ecf20Sopenharmony_ci goto err; 21448c2ecf20Sopenharmony_ci } 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci ret = sel_make_initcon_files(dentry); 21478c2ecf20Sopenharmony_ci if (ret) 21488c2ecf20Sopenharmony_ci goto err; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci fsi->class_dir = sel_make_dir(sb->s_root, CLASS_DIR_NAME, &fsi->last_ino); 21518c2ecf20Sopenharmony_ci if (IS_ERR(fsi->class_dir)) { 21528c2ecf20Sopenharmony_ci ret = PTR_ERR(fsi->class_dir); 21538c2ecf20Sopenharmony_ci fsi->class_dir = NULL; 21548c2ecf20Sopenharmony_ci goto err; 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci fsi->policycap_dir = sel_make_dir(sb->s_root, POLICYCAP_DIR_NAME, 21588c2ecf20Sopenharmony_ci &fsi->last_ino); 21598c2ecf20Sopenharmony_ci if (IS_ERR(fsi->policycap_dir)) { 21608c2ecf20Sopenharmony_ci ret = PTR_ERR(fsi->policycap_dir); 21618c2ecf20Sopenharmony_ci fsi->policycap_dir = NULL; 21628c2ecf20Sopenharmony_ci goto err; 21638c2ecf20Sopenharmony_ci } 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci ret = sel_make_policycap(fsi); 21668c2ecf20Sopenharmony_ci if (ret) { 21678c2ecf20Sopenharmony_ci pr_err("SELinux: failed to load policy capabilities\n"); 21688c2ecf20Sopenharmony_ci goto err; 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci return 0; 21728c2ecf20Sopenharmony_cierr: 21738c2ecf20Sopenharmony_ci pr_err("SELinux: %s: failed while creating inodes\n", 21748c2ecf20Sopenharmony_ci __func__); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci selinux_fs_info_free(sb); 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci return ret; 21798c2ecf20Sopenharmony_ci} 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_cistatic int sel_get_tree(struct fs_context *fc) 21828c2ecf20Sopenharmony_ci{ 21838c2ecf20Sopenharmony_ci return get_tree_single(fc, sel_fill_super); 21848c2ecf20Sopenharmony_ci} 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_cistatic const struct fs_context_operations sel_context_ops = { 21878c2ecf20Sopenharmony_ci .get_tree = sel_get_tree, 21888c2ecf20Sopenharmony_ci}; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_cistatic int sel_init_fs_context(struct fs_context *fc) 21918c2ecf20Sopenharmony_ci{ 21928c2ecf20Sopenharmony_ci fc->ops = &sel_context_ops; 21938c2ecf20Sopenharmony_ci return 0; 21948c2ecf20Sopenharmony_ci} 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_cistatic void sel_kill_sb(struct super_block *sb) 21978c2ecf20Sopenharmony_ci{ 21988c2ecf20Sopenharmony_ci selinux_fs_info_free(sb); 21998c2ecf20Sopenharmony_ci kill_litter_super(sb); 22008c2ecf20Sopenharmony_ci} 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_cistatic struct file_system_type sel_fs_type = { 22038c2ecf20Sopenharmony_ci .name = "selinuxfs", 22048c2ecf20Sopenharmony_ci .init_fs_context = sel_init_fs_context, 22058c2ecf20Sopenharmony_ci .kill_sb = sel_kill_sb, 22068c2ecf20Sopenharmony_ci}; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistruct vfsmount *selinuxfs_mount; 22098c2ecf20Sopenharmony_cistruct path selinux_null; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_cistatic int __init init_sel_fs(void) 22128c2ecf20Sopenharmony_ci{ 22138c2ecf20Sopenharmony_ci struct qstr null_name = QSTR_INIT(NULL_FILE_NAME, 22148c2ecf20Sopenharmony_ci sizeof(NULL_FILE_NAME)-1); 22158c2ecf20Sopenharmony_ci int err; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci if (!selinux_enabled_boot) 22188c2ecf20Sopenharmony_ci return 0; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci err = sysfs_create_mount_point(fs_kobj, "selinux"); 22218c2ecf20Sopenharmony_ci if (err) 22228c2ecf20Sopenharmony_ci return err; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci err = register_filesystem(&sel_fs_type); 22258c2ecf20Sopenharmony_ci if (err) { 22268c2ecf20Sopenharmony_ci sysfs_remove_mount_point(fs_kobj, "selinux"); 22278c2ecf20Sopenharmony_ci return err; 22288c2ecf20Sopenharmony_ci } 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci selinux_null.mnt = kern_mount(&sel_fs_type); 22318c2ecf20Sopenharmony_ci if (IS_ERR(selinux_null.mnt)) { 22328c2ecf20Sopenharmony_ci pr_err("selinuxfs: could not mount!\n"); 22338c2ecf20Sopenharmony_ci err = PTR_ERR(selinux_null.mnt); 22348c2ecf20Sopenharmony_ci selinux_null.mnt = NULL; 22358c2ecf20Sopenharmony_ci return err; 22368c2ecf20Sopenharmony_ci } 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root, 22398c2ecf20Sopenharmony_ci &null_name); 22408c2ecf20Sopenharmony_ci if (IS_ERR(selinux_null.dentry)) { 22418c2ecf20Sopenharmony_ci pr_err("selinuxfs: could not lookup null!\n"); 22428c2ecf20Sopenharmony_ci err = PTR_ERR(selinux_null.dentry); 22438c2ecf20Sopenharmony_ci selinux_null.dentry = NULL; 22448c2ecf20Sopenharmony_ci return err; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci return err; 22488c2ecf20Sopenharmony_ci} 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci__initcall(init_sel_fs); 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_DISABLE 22538c2ecf20Sopenharmony_civoid exit_sel_fs(void) 22548c2ecf20Sopenharmony_ci{ 22558c2ecf20Sopenharmony_ci sysfs_remove_mount_point(fs_kobj, "selinux"); 22568c2ecf20Sopenharmony_ci dput(selinux_null.dentry); 22578c2ecf20Sopenharmony_ci kern_unmount(selinuxfs_mount); 22588c2ecf20Sopenharmony_ci unregister_filesystem(&sel_fs_type); 22598c2ecf20Sopenharmony_ci} 22608c2ecf20Sopenharmony_ci#endif 2261