162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Security-Enhanced Linux (SELinux) security module 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file contains the SELinux hook function implementations. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Stephen Smalley, <stephen.smalley.work@gmail.com> 862306a36Sopenharmony_ci * Chris Vance, <cvance@nai.com> 962306a36Sopenharmony_ci * Wayne Salamon, <wsalamon@nai.com> 1062306a36Sopenharmony_ci * James Morris <jmorris@redhat.com> 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (C) 2001,2002 Networks Associates Technology, Inc. 1362306a36Sopenharmony_ci * Copyright (C) 2003-2008 Red Hat, Inc., James Morris <jmorris@redhat.com> 1462306a36Sopenharmony_ci * Eric Paris <eparis@redhat.com> 1562306a36Sopenharmony_ci * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. 1662306a36Sopenharmony_ci * <dgoeddel@trustedcs.com> 1762306a36Sopenharmony_ci * Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P. 1862306a36Sopenharmony_ci * Paul Moore <paul@paul-moore.com> 1962306a36Sopenharmony_ci * Copyright (C) 2007 Hitachi Software Engineering Co., Ltd. 2062306a36Sopenharmony_ci * Yuichi Nakamura <ynakam@hitachisoft.jp> 2162306a36Sopenharmony_ci * Copyright (C) 2016 Mellanox Technologies 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/init.h> 2562306a36Sopenharmony_ci#include <linux/kd.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/kernel_read_file.h> 2862306a36Sopenharmony_ci#include <linux/errno.h> 2962306a36Sopenharmony_ci#include <linux/sched/signal.h> 3062306a36Sopenharmony_ci#include <linux/sched/task.h> 3162306a36Sopenharmony_ci#include <linux/lsm_hooks.h> 3262306a36Sopenharmony_ci#include <linux/xattr.h> 3362306a36Sopenharmony_ci#include <linux/capability.h> 3462306a36Sopenharmony_ci#include <linux/unistd.h> 3562306a36Sopenharmony_ci#include <linux/mm.h> 3662306a36Sopenharmony_ci#include <linux/mman.h> 3762306a36Sopenharmony_ci#include <linux/slab.h> 3862306a36Sopenharmony_ci#include <linux/pagemap.h> 3962306a36Sopenharmony_ci#include <linux/proc_fs.h> 4062306a36Sopenharmony_ci#include <linux/swap.h> 4162306a36Sopenharmony_ci#include <linux/spinlock.h> 4262306a36Sopenharmony_ci#include <linux/syscalls.h> 4362306a36Sopenharmony_ci#include <linux/dcache.h> 4462306a36Sopenharmony_ci#include <linux/file.h> 4562306a36Sopenharmony_ci#include <linux/fdtable.h> 4662306a36Sopenharmony_ci#include <linux/namei.h> 4762306a36Sopenharmony_ci#include <linux/mount.h> 4862306a36Sopenharmony_ci#include <linux/fs_context.h> 4962306a36Sopenharmony_ci#include <linux/fs_parser.h> 5062306a36Sopenharmony_ci#include <linux/netfilter_ipv4.h> 5162306a36Sopenharmony_ci#include <linux/netfilter_ipv6.h> 5262306a36Sopenharmony_ci#include <linux/tty.h> 5362306a36Sopenharmony_ci#include <net/icmp.h> 5462306a36Sopenharmony_ci#include <net/ip.h> /* for local_port_range[] */ 5562306a36Sopenharmony_ci#include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ 5662306a36Sopenharmony_ci#include <net/inet_connection_sock.h> 5762306a36Sopenharmony_ci#include <net/net_namespace.h> 5862306a36Sopenharmony_ci#include <net/netlabel.h> 5962306a36Sopenharmony_ci#include <linux/uaccess.h> 6062306a36Sopenharmony_ci#include <asm/ioctls.h> 6162306a36Sopenharmony_ci#include <linux/atomic.h> 6262306a36Sopenharmony_ci#include <linux/bitops.h> 6362306a36Sopenharmony_ci#include <linux/interrupt.h> 6462306a36Sopenharmony_ci#include <linux/netdevice.h> /* for network interface checks */ 6562306a36Sopenharmony_ci#include <net/netlink.h> 6662306a36Sopenharmony_ci#include <linux/tcp.h> 6762306a36Sopenharmony_ci#include <linux/udp.h> 6862306a36Sopenharmony_ci#include <linux/dccp.h> 6962306a36Sopenharmony_ci#include <linux/sctp.h> 7062306a36Sopenharmony_ci#include <net/sctp/structs.h> 7162306a36Sopenharmony_ci#include <linux/quota.h> 7262306a36Sopenharmony_ci#include <linux/un.h> /* for Unix socket types */ 7362306a36Sopenharmony_ci#include <net/af_unix.h> /* for Unix socket types */ 7462306a36Sopenharmony_ci#include <linux/parser.h> 7562306a36Sopenharmony_ci#include <linux/nfs_mount.h> 7662306a36Sopenharmony_ci#include <net/ipv6.h> 7762306a36Sopenharmony_ci#include <linux/hugetlb.h> 7862306a36Sopenharmony_ci#include <linux/personality.h> 7962306a36Sopenharmony_ci#include <linux/audit.h> 8062306a36Sopenharmony_ci#include <linux/string.h> 8162306a36Sopenharmony_ci#include <linux/mutex.h> 8262306a36Sopenharmony_ci#include <linux/posix-timers.h> 8362306a36Sopenharmony_ci#include <linux/syslog.h> 8462306a36Sopenharmony_ci#include <linux/user_namespace.h> 8562306a36Sopenharmony_ci#include <linux/export.h> 8662306a36Sopenharmony_ci#include <linux/msg.h> 8762306a36Sopenharmony_ci#include <linux/shm.h> 8862306a36Sopenharmony_ci#include <linux/bpf.h> 8962306a36Sopenharmony_ci#include <linux/kernfs.h> 9062306a36Sopenharmony_ci#include <linux/stringhash.h> /* for hashlen_string() */ 9162306a36Sopenharmony_ci#include <uapi/linux/mount.h> 9262306a36Sopenharmony_ci#include <linux/fsnotify.h> 9362306a36Sopenharmony_ci#include <linux/fanotify.h> 9462306a36Sopenharmony_ci#include <linux/io_uring.h> 9562306a36Sopenharmony_ci#include <linux/hck/lite_hck_ced.h> 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#include "avc.h" 9862306a36Sopenharmony_ci#include "objsec.h" 9962306a36Sopenharmony_ci#include "netif.h" 10062306a36Sopenharmony_ci#include "netnode.h" 10162306a36Sopenharmony_ci#include "netport.h" 10262306a36Sopenharmony_ci#include "ibpkey.h" 10362306a36Sopenharmony_ci#include "xfrm.h" 10462306a36Sopenharmony_ci#include "netlabel.h" 10562306a36Sopenharmony_ci#include "audit.h" 10662306a36Sopenharmony_ci#include "avc_ss.h" 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define SELINUX_INODE_INIT_XATTRS 1 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct selinux_state selinux_state; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* SECMARK reference count */ 11362306a36Sopenharmony_cistatic atomic_t selinux_secmark_refcount = ATOMIC_INIT(0); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_DEVELOP 11662306a36Sopenharmony_cistatic int selinux_enforcing_boot __initdata; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int __init enforcing_setup(char *str) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci unsigned long enforcing; 12162306a36Sopenharmony_ci if (!kstrtoul(str, 0, &enforcing)) 12262306a36Sopenharmony_ci selinux_enforcing_boot = enforcing ? 1 : 0; 12362306a36Sopenharmony_ci return 1; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci__setup("enforcing=", enforcing_setup); 12662306a36Sopenharmony_ci#else 12762306a36Sopenharmony_ci#define selinux_enforcing_boot 1 12862306a36Sopenharmony_ci#endif 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciint selinux_enabled_boot __initdata = 1; 13162306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM 13262306a36Sopenharmony_cistatic int __init selinux_enabled_setup(char *str) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned long enabled; 13562306a36Sopenharmony_ci if (!kstrtoul(str, 0, &enabled)) 13662306a36Sopenharmony_ci selinux_enabled_boot = enabled ? 1 : 0; 13762306a36Sopenharmony_ci return 1; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci__setup("selinux=", selinux_enabled_setup); 14062306a36Sopenharmony_ci#endif 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic int __init checkreqprot_setup(char *str) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci unsigned long checkreqprot; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (!kstrtoul(str, 0, &checkreqprot)) { 14762306a36Sopenharmony_ci if (checkreqprot) 14862306a36Sopenharmony_ci pr_err("SELinux: checkreqprot set to 1 via kernel parameter. This is no longer supported.\n"); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci return 1; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci__setup("checkreqprot=", checkreqprot_setup); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/** 15562306a36Sopenharmony_ci * selinux_secmark_enabled - Check to see if SECMARK is currently enabled 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * Description: 15862306a36Sopenharmony_ci * This function checks the SECMARK reference counter to see if any SECMARK 15962306a36Sopenharmony_ci * targets are currently configured, if the reference counter is greater than 16062306a36Sopenharmony_ci * zero SECMARK is considered to be enabled. Returns true (1) if SECMARK is 16162306a36Sopenharmony_ci * enabled, false (0) if SECMARK is disabled. If the always_check_network 16262306a36Sopenharmony_ci * policy capability is enabled, SECMARK is always considered enabled. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_cistatic int selinux_secmark_enabled(void) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci return (selinux_policycap_alwaysnetwork() || 16862306a36Sopenharmony_ci atomic_read(&selinux_secmark_refcount)); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/** 17262306a36Sopenharmony_ci * selinux_peerlbl_enabled - Check to see if peer labeling is currently enabled 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * Description: 17562306a36Sopenharmony_ci * This function checks if NetLabel or labeled IPSEC is enabled. Returns true 17662306a36Sopenharmony_ci * (1) if any are enabled or false (0) if neither are enabled. If the 17762306a36Sopenharmony_ci * always_check_network policy capability is enabled, peer labeling 17862306a36Sopenharmony_ci * is always considered enabled. 17962306a36Sopenharmony_ci * 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_cistatic int selinux_peerlbl_enabled(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci return (selinux_policycap_alwaysnetwork() || 18462306a36Sopenharmony_ci netlbl_enabled() || selinux_xfrm_enabled()); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int selinux_netcache_avc_callback(u32 event) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci if (event == AVC_CALLBACK_RESET) { 19062306a36Sopenharmony_ci sel_netif_flush(); 19162306a36Sopenharmony_ci sel_netnode_flush(); 19262306a36Sopenharmony_ci sel_netport_flush(); 19362306a36Sopenharmony_ci synchronize_net(); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int selinux_lsm_notifier_avc_callback(u32 event) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci if (event == AVC_CALLBACK_RESET) { 20162306a36Sopenharmony_ci sel_ib_pkey_flush(); 20262306a36Sopenharmony_ci call_blocking_lsm_notifier(LSM_POLICY_CHANGE, NULL); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* 20962306a36Sopenharmony_ci * initialise the security for the init task 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_cistatic void cred_init_security(void) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct task_security_struct *tsec; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci tsec = selinux_cred(unrcu_pointer(current->real_cred)); 21662306a36Sopenharmony_ci tsec->osid = tsec->sid = SECINITSID_KERNEL; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* 22062306a36Sopenharmony_ci * get the security ID of a set of credentials 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic inline u32 cred_sid(const struct cred *cred) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci const struct task_security_struct *tsec; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci tsec = selinux_cred(cred); 22762306a36Sopenharmony_ci return tsec->sid; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void __ad_net_init(struct common_audit_data *ad, 23162306a36Sopenharmony_ci struct lsm_network_audit *net, 23262306a36Sopenharmony_ci int ifindex, struct sock *sk, u16 family) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci ad->type = LSM_AUDIT_DATA_NET; 23562306a36Sopenharmony_ci ad->u.net = net; 23662306a36Sopenharmony_ci net->netif = ifindex; 23762306a36Sopenharmony_ci net->sk = sk; 23862306a36Sopenharmony_ci net->family = family; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void ad_net_init_from_sk(struct common_audit_data *ad, 24262306a36Sopenharmony_ci struct lsm_network_audit *net, 24362306a36Sopenharmony_ci struct sock *sk) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci __ad_net_init(ad, net, 0, sk, 0); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void ad_net_init_from_iif(struct common_audit_data *ad, 24962306a36Sopenharmony_ci struct lsm_network_audit *net, 25062306a36Sopenharmony_ci int ifindex, u16 family) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci __ad_net_init(ad, net, ifindex, NULL, family); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* 25662306a36Sopenharmony_ci * get the objective security ID of a task 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic inline u32 task_sid_obj(const struct task_struct *task) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci u32 sid; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci rcu_read_lock(); 26362306a36Sopenharmony_ci sid = cred_sid(__task_cred(task)); 26462306a36Sopenharmony_ci rcu_read_unlock(); 26562306a36Sopenharmony_ci return sid; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* 27162306a36Sopenharmony_ci * Try reloading inode security labels that have been marked as invalid. The 27262306a36Sopenharmony_ci * @may_sleep parameter indicates when sleeping and thus reloading labels is 27362306a36Sopenharmony_ci * allowed; when set to false, returns -ECHILD when the label is 27462306a36Sopenharmony_ci * invalid. The @dentry parameter should be set to a dentry of the inode. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic int __inode_security_revalidate(struct inode *inode, 27762306a36Sopenharmony_ci struct dentry *dentry, 27862306a36Sopenharmony_ci bool may_sleep) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci might_sleep_if(may_sleep); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (selinux_initialized() && 28562306a36Sopenharmony_ci isec->initialized != LABEL_INITIALIZED) { 28662306a36Sopenharmony_ci if (!may_sleep) 28762306a36Sopenharmony_ci return -ECHILD; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* 29062306a36Sopenharmony_ci * Try reloading the inode security label. This will fail if 29162306a36Sopenharmony_ci * @opt_dentry is NULL and no dentry for this inode can be 29262306a36Sopenharmony_ci * found; in that case, continue using the old label. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci inode_doinit_with_dentry(inode, dentry); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic struct inode_security_struct *inode_security_novalidate(struct inode *inode) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci return selinux_inode(inode); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci int error; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci error = __inode_security_revalidate(inode, NULL, !rcu); 30962306a36Sopenharmony_ci if (error) 31062306a36Sopenharmony_ci return ERR_PTR(error); 31162306a36Sopenharmony_ci return selinux_inode(inode); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* 31562306a36Sopenharmony_ci * Get the security label of an inode. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_cistatic struct inode_security_struct *inode_security(struct inode *inode) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci __inode_security_revalidate(inode, NULL, true); 32062306a36Sopenharmony_ci return selinux_inode(inode); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return selinux_inode(inode); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* 33162306a36Sopenharmony_ci * Get the security label of a dentry's backing inode. 33262306a36Sopenharmony_ci */ 33362306a36Sopenharmony_cistatic struct inode_security_struct *backing_inode_security(struct dentry *dentry) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci __inode_security_revalidate(inode, dentry, true); 33862306a36Sopenharmony_ci return selinux_inode(inode); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void inode_free_security(struct inode *inode) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 34462306a36Sopenharmony_ci struct superblock_security_struct *sbsec; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!isec) 34762306a36Sopenharmony_ci return; 34862306a36Sopenharmony_ci sbsec = selinux_superblock(inode->i_sb); 34962306a36Sopenharmony_ci /* 35062306a36Sopenharmony_ci * As not all inode security structures are in a list, we check for 35162306a36Sopenharmony_ci * empty list outside of the lock to make sure that we won't waste 35262306a36Sopenharmony_ci * time taking a lock doing nothing. 35362306a36Sopenharmony_ci * 35462306a36Sopenharmony_ci * The list_del_init() function can be safely called more than once. 35562306a36Sopenharmony_ci * It should not be possible for this function to be called with 35662306a36Sopenharmony_ci * concurrent list_add(), but for better safety against future changes 35762306a36Sopenharmony_ci * in the code, we use list_empty_careful() here. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_ci if (!list_empty_careful(&isec->list)) { 36062306a36Sopenharmony_ci spin_lock(&sbsec->isec_lock); 36162306a36Sopenharmony_ci list_del_init(&isec->list); 36262306a36Sopenharmony_ci spin_unlock(&sbsec->isec_lock); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistruct selinux_mnt_opts { 36762306a36Sopenharmony_ci u32 fscontext_sid; 36862306a36Sopenharmony_ci u32 context_sid; 36962306a36Sopenharmony_ci u32 rootcontext_sid; 37062306a36Sopenharmony_ci u32 defcontext_sid; 37162306a36Sopenharmony_ci}; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void selinux_free_mnt_opts(void *mnt_opts) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci kfree(mnt_opts); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cienum { 37962306a36Sopenharmony_ci Opt_error = -1, 38062306a36Sopenharmony_ci Opt_context = 0, 38162306a36Sopenharmony_ci Opt_defcontext = 1, 38262306a36Sopenharmony_ci Opt_fscontext = 2, 38362306a36Sopenharmony_ci Opt_rootcontext = 3, 38462306a36Sopenharmony_ci Opt_seclabel = 4, 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci#define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg} 38862306a36Sopenharmony_cistatic const struct { 38962306a36Sopenharmony_ci const char *name; 39062306a36Sopenharmony_ci int len; 39162306a36Sopenharmony_ci int opt; 39262306a36Sopenharmony_ci bool has_arg; 39362306a36Sopenharmony_ci} tokens[] = { 39462306a36Sopenharmony_ci A(context, true), 39562306a36Sopenharmony_ci A(fscontext, true), 39662306a36Sopenharmony_ci A(defcontext, true), 39762306a36Sopenharmony_ci A(rootcontext, true), 39862306a36Sopenharmony_ci A(seclabel, false), 39962306a36Sopenharmony_ci}; 40062306a36Sopenharmony_ci#undef A 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic int match_opt_prefix(char *s, int l, char **arg) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci int i; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tokens); i++) { 40762306a36Sopenharmony_ci size_t len = tokens[i].len; 40862306a36Sopenharmony_ci if (len > l || memcmp(s, tokens[i].name, len)) 40962306a36Sopenharmony_ci continue; 41062306a36Sopenharmony_ci if (tokens[i].has_arg) { 41162306a36Sopenharmony_ci if (len == l || s[len] != '=') 41262306a36Sopenharmony_ci continue; 41362306a36Sopenharmony_ci *arg = s + len + 1; 41462306a36Sopenharmony_ci } else if (len != l) 41562306a36Sopenharmony_ci continue; 41662306a36Sopenharmony_ci return tokens[i].opt; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci return Opt_error; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci#define SEL_MOUNT_FAIL_MSG "SELinux: duplicate or incompatible mount options\n" 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int may_context_mount_sb_relabel(u32 sid, 42462306a36Sopenharmony_ci struct superblock_security_struct *sbsec, 42562306a36Sopenharmony_ci const struct cred *cred) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(cred); 42862306a36Sopenharmony_ci int rc; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, 43162306a36Sopenharmony_ci FILESYSTEM__RELABELFROM, NULL); 43262306a36Sopenharmony_ci if (rc) 43362306a36Sopenharmony_ci return rc; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci rc = avc_has_perm(tsec->sid, sid, SECCLASS_FILESYSTEM, 43662306a36Sopenharmony_ci FILESYSTEM__RELABELTO, NULL); 43762306a36Sopenharmony_ci return rc; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int may_context_mount_inode_relabel(u32 sid, 44162306a36Sopenharmony_ci struct superblock_security_struct *sbsec, 44262306a36Sopenharmony_ci const struct cred *cred) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(cred); 44562306a36Sopenharmony_ci int rc; 44662306a36Sopenharmony_ci rc = avc_has_perm(tsec->sid, sbsec->sid, SECCLASS_FILESYSTEM, 44762306a36Sopenharmony_ci FILESYSTEM__RELABELFROM, NULL); 44862306a36Sopenharmony_ci if (rc) 44962306a36Sopenharmony_ci return rc; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci rc = avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, 45262306a36Sopenharmony_ci FILESYSTEM__ASSOCIATE, NULL); 45362306a36Sopenharmony_ci return rc; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int selinux_is_genfs_special_handling(struct super_block *sb) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci /* Special handling. Genfs but also in-core setxattr handler */ 45962306a36Sopenharmony_ci return !strcmp(sb->s_type->name, "sysfs") || 46062306a36Sopenharmony_ci !strcmp(sb->s_type->name, "pstore") || 46162306a36Sopenharmony_ci !strcmp(sb->s_type->name, "debugfs") || 46262306a36Sopenharmony_ci !strcmp(sb->s_type->name, "tracefs") || 46362306a36Sopenharmony_ci !strcmp(sb->s_type->name, "rootfs") || 46462306a36Sopenharmony_ci (selinux_policycap_cgroupseclabel() && 46562306a36Sopenharmony_ci (!strcmp(sb->s_type->name, "cgroup") || 46662306a36Sopenharmony_ci !strcmp(sb->s_type->name, "cgroup2"))); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int selinux_is_sblabel_mnt(struct super_block *sb) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* 47462306a36Sopenharmony_ci * IMPORTANT: Double-check logic in this function when adding a new 47562306a36Sopenharmony_ci * SECURITY_FS_USE_* definition! 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci BUILD_BUG_ON(SECURITY_FS_USE_MAX != 7); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci switch (sbsec->behavior) { 48062306a36Sopenharmony_ci case SECURITY_FS_USE_XATTR: 48162306a36Sopenharmony_ci case SECURITY_FS_USE_TRANS: 48262306a36Sopenharmony_ci case SECURITY_FS_USE_TASK: 48362306a36Sopenharmony_ci case SECURITY_FS_USE_NATIVE: 48462306a36Sopenharmony_ci return 1; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci case SECURITY_FS_USE_GENFS: 48762306a36Sopenharmony_ci return selinux_is_genfs_special_handling(sb); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Never allow relabeling on context mounts */ 49062306a36Sopenharmony_ci case SECURITY_FS_USE_MNTPOINT: 49162306a36Sopenharmony_ci case SECURITY_FS_USE_NONE: 49262306a36Sopenharmony_ci default: 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int sb_check_xattr_support(struct super_block *sb) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 50062306a36Sopenharmony_ci struct dentry *root = sb->s_root; 50162306a36Sopenharmony_ci struct inode *root_inode = d_backing_inode(root); 50262306a36Sopenharmony_ci u32 sid; 50362306a36Sopenharmony_ci int rc; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* 50662306a36Sopenharmony_ci * Make sure that the xattr handler exists and that no 50762306a36Sopenharmony_ci * error other than -ENODATA is returned by getxattr on 50862306a36Sopenharmony_ci * the root directory. -ENODATA is ok, as this may be 50962306a36Sopenharmony_ci * the first boot of the SELinux kernel before we have 51062306a36Sopenharmony_ci * assigned xattr values to the filesystem. 51162306a36Sopenharmony_ci */ 51262306a36Sopenharmony_ci if (!(root_inode->i_opflags & IOP_XATTR)) { 51362306a36Sopenharmony_ci pr_warn("SELinux: (dev %s, type %s) has no xattr support\n", 51462306a36Sopenharmony_ci sb->s_id, sb->s_type->name); 51562306a36Sopenharmony_ci goto fallback; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci rc = __vfs_getxattr(root, root_inode, XATTR_NAME_SELINUX, NULL, 0); 51962306a36Sopenharmony_ci if (rc < 0 && rc != -ENODATA) { 52062306a36Sopenharmony_ci if (rc == -EOPNOTSUPP) { 52162306a36Sopenharmony_ci pr_warn("SELinux: (dev %s, type %s) has no security xattr handler\n", 52262306a36Sopenharmony_ci sb->s_id, sb->s_type->name); 52362306a36Sopenharmony_ci goto fallback; 52462306a36Sopenharmony_ci } else { 52562306a36Sopenharmony_ci pr_warn("SELinux: (dev %s, type %s) getxattr errno %d\n", 52662306a36Sopenharmony_ci sb->s_id, sb->s_type->name, -rc); 52762306a36Sopenharmony_ci return rc; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cifallback: 53362306a36Sopenharmony_ci /* No xattr support - try to fallback to genfs if possible. */ 53462306a36Sopenharmony_ci rc = security_genfs_sid(sb->s_type->name, "/", 53562306a36Sopenharmony_ci SECCLASS_DIR, &sid); 53662306a36Sopenharmony_ci if (rc) 53762306a36Sopenharmony_ci return -EOPNOTSUPP; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pr_warn("SELinux: (dev %s, type %s) falling back to genfs\n", 54062306a36Sopenharmony_ci sb->s_id, sb->s_type->name); 54162306a36Sopenharmony_ci sbsec->behavior = SECURITY_FS_USE_GENFS; 54262306a36Sopenharmony_ci sbsec->sid = sid; 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int sb_finish_set_opts(struct super_block *sb) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 54962306a36Sopenharmony_ci struct dentry *root = sb->s_root; 55062306a36Sopenharmony_ci struct inode *root_inode = d_backing_inode(root); 55162306a36Sopenharmony_ci int rc = 0; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (sbsec->behavior == SECURITY_FS_USE_XATTR) { 55462306a36Sopenharmony_ci rc = sb_check_xattr_support(sb); 55562306a36Sopenharmony_ci if (rc) 55662306a36Sopenharmony_ci return rc; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci sbsec->flags |= SE_SBINITIALIZED; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * Explicitly set or clear SBLABEL_MNT. It's not sufficient to simply 56362306a36Sopenharmony_ci * leave the flag untouched because sb_clone_mnt_opts might be handing 56462306a36Sopenharmony_ci * us a superblock that needs the flag to be cleared. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci if (selinux_is_sblabel_mnt(sb)) 56762306a36Sopenharmony_ci sbsec->flags |= SBLABEL_MNT; 56862306a36Sopenharmony_ci else 56962306a36Sopenharmony_ci sbsec->flags &= ~SBLABEL_MNT; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Initialize the root inode. */ 57262306a36Sopenharmony_ci rc = inode_doinit_with_dentry(root_inode, root); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Initialize any other inodes associated with the superblock, e.g. 57562306a36Sopenharmony_ci inodes created prior to initial policy load or inodes created 57662306a36Sopenharmony_ci during get_sb by a pseudo filesystem that directly 57762306a36Sopenharmony_ci populates itself. */ 57862306a36Sopenharmony_ci spin_lock(&sbsec->isec_lock); 57962306a36Sopenharmony_ci while (!list_empty(&sbsec->isec_head)) { 58062306a36Sopenharmony_ci struct inode_security_struct *isec = 58162306a36Sopenharmony_ci list_first_entry(&sbsec->isec_head, 58262306a36Sopenharmony_ci struct inode_security_struct, list); 58362306a36Sopenharmony_ci struct inode *inode = isec->inode; 58462306a36Sopenharmony_ci list_del_init(&isec->list); 58562306a36Sopenharmony_ci spin_unlock(&sbsec->isec_lock); 58662306a36Sopenharmony_ci inode = igrab(inode); 58762306a36Sopenharmony_ci if (inode) { 58862306a36Sopenharmony_ci if (!IS_PRIVATE(inode)) 58962306a36Sopenharmony_ci inode_doinit_with_dentry(inode, NULL); 59062306a36Sopenharmony_ci iput(inode); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci spin_lock(&sbsec->isec_lock); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci spin_unlock(&sbsec->isec_lock); 59562306a36Sopenharmony_ci return rc; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int bad_option(struct superblock_security_struct *sbsec, char flag, 59962306a36Sopenharmony_ci u32 old_sid, u32 new_sid) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci char mnt_flags = sbsec->flags & SE_MNTMASK; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* check if the old mount command had the same options */ 60462306a36Sopenharmony_ci if (sbsec->flags & SE_SBINITIALIZED) 60562306a36Sopenharmony_ci if (!(sbsec->flags & flag) || 60662306a36Sopenharmony_ci (old_sid != new_sid)) 60762306a36Sopenharmony_ci return 1; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* check if we were passed the same options twice, 61062306a36Sopenharmony_ci * aka someone passed context=a,context=b 61162306a36Sopenharmony_ci */ 61262306a36Sopenharmony_ci if (!(sbsec->flags & SE_SBINITIALIZED)) 61362306a36Sopenharmony_ci if (mnt_flags & flag) 61462306a36Sopenharmony_ci return 1; 61562306a36Sopenharmony_ci return 0; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci/* 61962306a36Sopenharmony_ci * Allow filesystems with binary mount data to explicitly set mount point 62062306a36Sopenharmony_ci * labeling information. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_cistatic int selinux_set_mnt_opts(struct super_block *sb, 62362306a36Sopenharmony_ci void *mnt_opts, 62462306a36Sopenharmony_ci unsigned long kern_flags, 62562306a36Sopenharmony_ci unsigned long *set_kern_flags) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci const struct cred *cred = current_cred(); 62862306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 62962306a36Sopenharmony_ci struct dentry *root = sb->s_root; 63062306a36Sopenharmony_ci struct selinux_mnt_opts *opts = mnt_opts; 63162306a36Sopenharmony_ci struct inode_security_struct *root_isec; 63262306a36Sopenharmony_ci u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0; 63362306a36Sopenharmony_ci u32 defcontext_sid = 0; 63462306a36Sopenharmony_ci int rc = 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* 63762306a36Sopenharmony_ci * Specifying internal flags without providing a place to 63862306a36Sopenharmony_ci * place the results is not allowed 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_ci if (kern_flags && !set_kern_flags) 64162306a36Sopenharmony_ci return -EINVAL; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci mutex_lock(&sbsec->lock); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (!selinux_initialized()) { 64662306a36Sopenharmony_ci if (!opts) { 64762306a36Sopenharmony_ci /* Defer initialization until selinux_complete_init, 64862306a36Sopenharmony_ci after the initial policy is loaded and the security 64962306a36Sopenharmony_ci server is ready to handle calls. */ 65062306a36Sopenharmony_ci if (kern_flags & SECURITY_LSM_NATIVE_LABELS) { 65162306a36Sopenharmony_ci sbsec->flags |= SE_SBNATIVE; 65262306a36Sopenharmony_ci *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci goto out; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci rc = -EINVAL; 65762306a36Sopenharmony_ci pr_warn("SELinux: Unable to set superblock options " 65862306a36Sopenharmony_ci "before the security server is initialized\n"); 65962306a36Sopenharmony_ci goto out; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* 66362306a36Sopenharmony_ci * Binary mount data FS will come through this function twice. Once 66462306a36Sopenharmony_ci * from an explicit call and once from the generic calls from the vfs. 66562306a36Sopenharmony_ci * Since the generic VFS calls will not contain any security mount data 66662306a36Sopenharmony_ci * we need to skip the double mount verification. 66762306a36Sopenharmony_ci * 66862306a36Sopenharmony_ci * This does open a hole in which we will not notice if the first 66962306a36Sopenharmony_ci * mount using this sb set explicit options and a second mount using 67062306a36Sopenharmony_ci * this sb does not set any security options. (The first options 67162306a36Sopenharmony_ci * will be used for both mounts) 67262306a36Sopenharmony_ci */ 67362306a36Sopenharmony_ci if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA) 67462306a36Sopenharmony_ci && !opts) 67562306a36Sopenharmony_ci goto out; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci root_isec = backing_inode_security_novalidate(root); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* 68062306a36Sopenharmony_ci * parse the mount options, check if they are valid sids. 68162306a36Sopenharmony_ci * also check if someone is trying to mount the same sb more 68262306a36Sopenharmony_ci * than once with different security options. 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_ci if (opts) { 68562306a36Sopenharmony_ci if (opts->fscontext_sid) { 68662306a36Sopenharmony_ci fscontext_sid = opts->fscontext_sid; 68762306a36Sopenharmony_ci if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, 68862306a36Sopenharmony_ci fscontext_sid)) 68962306a36Sopenharmony_ci goto out_double_mount; 69062306a36Sopenharmony_ci sbsec->flags |= FSCONTEXT_MNT; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci if (opts->context_sid) { 69362306a36Sopenharmony_ci context_sid = opts->context_sid; 69462306a36Sopenharmony_ci if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, 69562306a36Sopenharmony_ci context_sid)) 69662306a36Sopenharmony_ci goto out_double_mount; 69762306a36Sopenharmony_ci sbsec->flags |= CONTEXT_MNT; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci if (opts->rootcontext_sid) { 70062306a36Sopenharmony_ci rootcontext_sid = opts->rootcontext_sid; 70162306a36Sopenharmony_ci if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, 70262306a36Sopenharmony_ci rootcontext_sid)) 70362306a36Sopenharmony_ci goto out_double_mount; 70462306a36Sopenharmony_ci sbsec->flags |= ROOTCONTEXT_MNT; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci if (opts->defcontext_sid) { 70762306a36Sopenharmony_ci defcontext_sid = opts->defcontext_sid; 70862306a36Sopenharmony_ci if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, 70962306a36Sopenharmony_ci defcontext_sid)) 71062306a36Sopenharmony_ci goto out_double_mount; 71162306a36Sopenharmony_ci sbsec->flags |= DEFCONTEXT_MNT; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (sbsec->flags & SE_SBINITIALIZED) { 71662306a36Sopenharmony_ci /* previously mounted with options, but not on this attempt? */ 71762306a36Sopenharmony_ci if ((sbsec->flags & SE_MNTMASK) && !opts) 71862306a36Sopenharmony_ci goto out_double_mount; 71962306a36Sopenharmony_ci rc = 0; 72062306a36Sopenharmony_ci goto out; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (strcmp(sb->s_type->name, "proc") == 0) 72462306a36Sopenharmony_ci sbsec->flags |= SE_SBPROC | SE_SBGENFS; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (!strcmp(sb->s_type->name, "debugfs") || 72762306a36Sopenharmony_ci !strcmp(sb->s_type->name, "tracefs") || 72862306a36Sopenharmony_ci !strcmp(sb->s_type->name, "binder") || 72962306a36Sopenharmony_ci !strcmp(sb->s_type->name, "bpf") || 73062306a36Sopenharmony_ci !strcmp(sb->s_type->name, "pstore") || 73162306a36Sopenharmony_ci !strcmp(sb->s_type->name, "securityfs")) 73262306a36Sopenharmony_ci sbsec->flags |= SE_SBGENFS; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (!strcmp(sb->s_type->name, "sysfs") || 73562306a36Sopenharmony_ci !strcmp(sb->s_type->name, "cgroup") || 73662306a36Sopenharmony_ci !strcmp(sb->s_type->name, "cgroup2")) 73762306a36Sopenharmony_ci sbsec->flags |= SE_SBGENFS | SE_SBGENFS_XATTR; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (!sbsec->behavior) { 74062306a36Sopenharmony_ci /* 74162306a36Sopenharmony_ci * Determine the labeling behavior to use for this 74262306a36Sopenharmony_ci * filesystem type. 74362306a36Sopenharmony_ci */ 74462306a36Sopenharmony_ci rc = security_fs_use(sb); 74562306a36Sopenharmony_ci if (rc) { 74662306a36Sopenharmony_ci pr_warn("%s: security_fs_use(%s) returned %d\n", 74762306a36Sopenharmony_ci __func__, sb->s_type->name, rc); 74862306a36Sopenharmony_ci goto out; 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* 75362306a36Sopenharmony_ci * If this is a user namespace mount and the filesystem type is not 75462306a36Sopenharmony_ci * explicitly whitelisted, then no contexts are allowed on the command 75562306a36Sopenharmony_ci * line and security labels must be ignored. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci if (sb->s_user_ns != &init_user_ns && 75862306a36Sopenharmony_ci strcmp(sb->s_type->name, "tmpfs") && 75962306a36Sopenharmony_ci strcmp(sb->s_type->name, "ramfs") && 76062306a36Sopenharmony_ci strcmp(sb->s_type->name, "devpts") && 76162306a36Sopenharmony_ci strcmp(sb->s_type->name, "overlay")) { 76262306a36Sopenharmony_ci if (context_sid || fscontext_sid || rootcontext_sid || 76362306a36Sopenharmony_ci defcontext_sid) { 76462306a36Sopenharmony_ci rc = -EACCES; 76562306a36Sopenharmony_ci goto out; 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci if (sbsec->behavior == SECURITY_FS_USE_XATTR) { 76862306a36Sopenharmony_ci sbsec->behavior = SECURITY_FS_USE_MNTPOINT; 76962306a36Sopenharmony_ci rc = security_transition_sid(current_sid(), 77062306a36Sopenharmony_ci current_sid(), 77162306a36Sopenharmony_ci SECCLASS_FILE, NULL, 77262306a36Sopenharmony_ci &sbsec->mntpoint_sid); 77362306a36Sopenharmony_ci if (rc) 77462306a36Sopenharmony_ci goto out; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci goto out_set_opts; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* sets the context of the superblock for the fs being mounted. */ 78062306a36Sopenharmony_ci if (fscontext_sid) { 78162306a36Sopenharmony_ci rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred); 78262306a36Sopenharmony_ci if (rc) 78362306a36Sopenharmony_ci goto out; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci sbsec->sid = fscontext_sid; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * Switch to using mount point labeling behavior. 79062306a36Sopenharmony_ci * sets the label used on all file below the mountpoint, and will set 79162306a36Sopenharmony_ci * the superblock context if not already set. 79262306a36Sopenharmony_ci */ 79362306a36Sopenharmony_ci if (sbsec->flags & SE_SBNATIVE) { 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * This means we are initializing a superblock that has been 79662306a36Sopenharmony_ci * mounted before the SELinux was initialized and the 79762306a36Sopenharmony_ci * filesystem requested native labeling. We had already 79862306a36Sopenharmony_ci * returned SECURITY_LSM_NATIVE_LABELS in *set_kern_flags 79962306a36Sopenharmony_ci * in the original mount attempt, so now we just need to set 80062306a36Sopenharmony_ci * the SECURITY_FS_USE_NATIVE behavior. 80162306a36Sopenharmony_ci */ 80262306a36Sopenharmony_ci sbsec->behavior = SECURITY_FS_USE_NATIVE; 80362306a36Sopenharmony_ci } else if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { 80462306a36Sopenharmony_ci sbsec->behavior = SECURITY_FS_USE_NATIVE; 80562306a36Sopenharmony_ci *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci if (context_sid) { 80962306a36Sopenharmony_ci if (!fscontext_sid) { 81062306a36Sopenharmony_ci rc = may_context_mount_sb_relabel(context_sid, sbsec, 81162306a36Sopenharmony_ci cred); 81262306a36Sopenharmony_ci if (rc) 81362306a36Sopenharmony_ci goto out; 81462306a36Sopenharmony_ci sbsec->sid = context_sid; 81562306a36Sopenharmony_ci } else { 81662306a36Sopenharmony_ci rc = may_context_mount_inode_relabel(context_sid, sbsec, 81762306a36Sopenharmony_ci cred); 81862306a36Sopenharmony_ci if (rc) 81962306a36Sopenharmony_ci goto out; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci if (!rootcontext_sid) 82262306a36Sopenharmony_ci rootcontext_sid = context_sid; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci sbsec->mntpoint_sid = context_sid; 82562306a36Sopenharmony_ci sbsec->behavior = SECURITY_FS_USE_MNTPOINT; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (rootcontext_sid) { 82962306a36Sopenharmony_ci rc = may_context_mount_inode_relabel(rootcontext_sid, sbsec, 83062306a36Sopenharmony_ci cred); 83162306a36Sopenharmony_ci if (rc) 83262306a36Sopenharmony_ci goto out; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci root_isec->sid = rootcontext_sid; 83562306a36Sopenharmony_ci root_isec->initialized = LABEL_INITIALIZED; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (defcontext_sid) { 83962306a36Sopenharmony_ci if (sbsec->behavior != SECURITY_FS_USE_XATTR && 84062306a36Sopenharmony_ci sbsec->behavior != SECURITY_FS_USE_NATIVE) { 84162306a36Sopenharmony_ci rc = -EINVAL; 84262306a36Sopenharmony_ci pr_warn("SELinux: defcontext option is " 84362306a36Sopenharmony_ci "invalid for this filesystem type\n"); 84462306a36Sopenharmony_ci goto out; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (defcontext_sid != sbsec->def_sid) { 84862306a36Sopenharmony_ci rc = may_context_mount_inode_relabel(defcontext_sid, 84962306a36Sopenharmony_ci sbsec, cred); 85062306a36Sopenharmony_ci if (rc) 85162306a36Sopenharmony_ci goto out; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci sbsec->def_sid = defcontext_sid; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ciout_set_opts: 85862306a36Sopenharmony_ci rc = sb_finish_set_opts(sb); 85962306a36Sopenharmony_ciout: 86062306a36Sopenharmony_ci mutex_unlock(&sbsec->lock); 86162306a36Sopenharmony_ci return rc; 86262306a36Sopenharmony_ciout_double_mount: 86362306a36Sopenharmony_ci rc = -EINVAL; 86462306a36Sopenharmony_ci pr_warn("SELinux: mount invalid. Same superblock, different " 86562306a36Sopenharmony_ci "security settings for (dev %s, type %s)\n", sb->s_id, 86662306a36Sopenharmony_ci sb->s_type->name); 86762306a36Sopenharmony_ci goto out; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int selinux_cmp_sb_context(const struct super_block *oldsb, 87162306a36Sopenharmony_ci const struct super_block *newsb) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct superblock_security_struct *old = selinux_superblock(oldsb); 87462306a36Sopenharmony_ci struct superblock_security_struct *new = selinux_superblock(newsb); 87562306a36Sopenharmony_ci char oldflags = old->flags & SE_MNTMASK; 87662306a36Sopenharmony_ci char newflags = new->flags & SE_MNTMASK; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (oldflags != newflags) 87962306a36Sopenharmony_ci goto mismatch; 88062306a36Sopenharmony_ci if ((oldflags & FSCONTEXT_MNT) && old->sid != new->sid) 88162306a36Sopenharmony_ci goto mismatch; 88262306a36Sopenharmony_ci if ((oldflags & CONTEXT_MNT) && old->mntpoint_sid != new->mntpoint_sid) 88362306a36Sopenharmony_ci goto mismatch; 88462306a36Sopenharmony_ci if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid) 88562306a36Sopenharmony_ci goto mismatch; 88662306a36Sopenharmony_ci if (oldflags & ROOTCONTEXT_MNT) { 88762306a36Sopenharmony_ci struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root); 88862306a36Sopenharmony_ci struct inode_security_struct *newroot = backing_inode_security(newsb->s_root); 88962306a36Sopenharmony_ci if (oldroot->sid != newroot->sid) 89062306a36Sopenharmony_ci goto mismatch; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci return 0; 89362306a36Sopenharmony_cimismatch: 89462306a36Sopenharmony_ci pr_warn("SELinux: mount invalid. Same superblock, " 89562306a36Sopenharmony_ci "different security settings for (dev %s, " 89662306a36Sopenharmony_ci "type %s)\n", newsb->s_id, newsb->s_type->name); 89762306a36Sopenharmony_ci return -EBUSY; 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cistatic int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, 90162306a36Sopenharmony_ci struct super_block *newsb, 90262306a36Sopenharmony_ci unsigned long kern_flags, 90362306a36Sopenharmony_ci unsigned long *set_kern_flags) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci int rc = 0; 90662306a36Sopenharmony_ci const struct superblock_security_struct *oldsbsec = 90762306a36Sopenharmony_ci selinux_superblock(oldsb); 90862306a36Sopenharmony_ci struct superblock_security_struct *newsbsec = selinux_superblock(newsb); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci int set_fscontext = (oldsbsec->flags & FSCONTEXT_MNT); 91162306a36Sopenharmony_ci int set_context = (oldsbsec->flags & CONTEXT_MNT); 91262306a36Sopenharmony_ci int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci /* 91562306a36Sopenharmony_ci * Specifying internal flags without providing a place to 91662306a36Sopenharmony_ci * place the results is not allowed. 91762306a36Sopenharmony_ci */ 91862306a36Sopenharmony_ci if (kern_flags && !set_kern_flags) 91962306a36Sopenharmony_ci return -EINVAL; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci mutex_lock(&newsbsec->lock); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * if the parent was able to be mounted it clearly had no special lsm 92562306a36Sopenharmony_ci * mount options. thus we can safely deal with this superblock later 92662306a36Sopenharmony_ci */ 92762306a36Sopenharmony_ci if (!selinux_initialized()) { 92862306a36Sopenharmony_ci if (kern_flags & SECURITY_LSM_NATIVE_LABELS) { 92962306a36Sopenharmony_ci newsbsec->flags |= SE_SBNATIVE; 93062306a36Sopenharmony_ci *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci goto out; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* how can we clone if the old one wasn't set up?? */ 93662306a36Sopenharmony_ci BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci /* if fs is reusing a sb, make sure that the contexts match */ 93962306a36Sopenharmony_ci if (newsbsec->flags & SE_SBINITIALIZED) { 94062306a36Sopenharmony_ci mutex_unlock(&newsbsec->lock); 94162306a36Sopenharmony_ci if ((kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) 94262306a36Sopenharmony_ci *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; 94362306a36Sopenharmony_ci return selinux_cmp_sb_context(oldsb, newsb); 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci newsbsec->flags = oldsbsec->flags; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci newsbsec->sid = oldsbsec->sid; 94962306a36Sopenharmony_ci newsbsec->def_sid = oldsbsec->def_sid; 95062306a36Sopenharmony_ci newsbsec->behavior = oldsbsec->behavior; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (newsbsec->behavior == SECURITY_FS_USE_NATIVE && 95362306a36Sopenharmony_ci !(kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) { 95462306a36Sopenharmony_ci rc = security_fs_use(newsb); 95562306a36Sopenharmony_ci if (rc) 95662306a36Sopenharmony_ci goto out; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !set_context) { 96062306a36Sopenharmony_ci newsbsec->behavior = SECURITY_FS_USE_NATIVE; 96162306a36Sopenharmony_ci *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (set_context) { 96562306a36Sopenharmony_ci u32 sid = oldsbsec->mntpoint_sid; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci if (!set_fscontext) 96862306a36Sopenharmony_ci newsbsec->sid = sid; 96962306a36Sopenharmony_ci if (!set_rootcontext) { 97062306a36Sopenharmony_ci struct inode_security_struct *newisec = backing_inode_security(newsb->s_root); 97162306a36Sopenharmony_ci newisec->sid = sid; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci newsbsec->mntpoint_sid = sid; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci if (set_rootcontext) { 97662306a36Sopenharmony_ci const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root); 97762306a36Sopenharmony_ci struct inode_security_struct *newisec = backing_inode_security(newsb->s_root); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci newisec->sid = oldisec->sid; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci sb_finish_set_opts(newsb); 98362306a36Sopenharmony_ciout: 98462306a36Sopenharmony_ci mutex_unlock(&newsbsec->lock); 98562306a36Sopenharmony_ci return rc; 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci/* 98962306a36Sopenharmony_ci * NOTE: the caller is responsible for freeing the memory even if on error. 99062306a36Sopenharmony_ci */ 99162306a36Sopenharmony_cistatic int selinux_add_opt(int token, const char *s, void **mnt_opts) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct selinux_mnt_opts *opts = *mnt_opts; 99462306a36Sopenharmony_ci u32 *dst_sid; 99562306a36Sopenharmony_ci int rc; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (token == Opt_seclabel) 99862306a36Sopenharmony_ci /* eaten and completely ignored */ 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci if (!s) 100162306a36Sopenharmony_ci return -EINVAL; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (!selinux_initialized()) { 100462306a36Sopenharmony_ci pr_warn("SELinux: Unable to set superblock options before the security server is initialized\n"); 100562306a36Sopenharmony_ci return -EINVAL; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (!opts) { 100962306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 101062306a36Sopenharmony_ci if (!opts) 101162306a36Sopenharmony_ci return -ENOMEM; 101262306a36Sopenharmony_ci *mnt_opts = opts; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci switch (token) { 101662306a36Sopenharmony_ci case Opt_context: 101762306a36Sopenharmony_ci if (opts->context_sid || opts->defcontext_sid) 101862306a36Sopenharmony_ci goto err; 101962306a36Sopenharmony_ci dst_sid = &opts->context_sid; 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci case Opt_fscontext: 102262306a36Sopenharmony_ci if (opts->fscontext_sid) 102362306a36Sopenharmony_ci goto err; 102462306a36Sopenharmony_ci dst_sid = &opts->fscontext_sid; 102562306a36Sopenharmony_ci break; 102662306a36Sopenharmony_ci case Opt_rootcontext: 102762306a36Sopenharmony_ci if (opts->rootcontext_sid) 102862306a36Sopenharmony_ci goto err; 102962306a36Sopenharmony_ci dst_sid = &opts->rootcontext_sid; 103062306a36Sopenharmony_ci break; 103162306a36Sopenharmony_ci case Opt_defcontext: 103262306a36Sopenharmony_ci if (opts->context_sid || opts->defcontext_sid) 103362306a36Sopenharmony_ci goto err; 103462306a36Sopenharmony_ci dst_sid = &opts->defcontext_sid; 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci default: 103762306a36Sopenharmony_ci WARN_ON(1); 103862306a36Sopenharmony_ci return -EINVAL; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci rc = security_context_str_to_sid(s, dst_sid, GFP_KERNEL); 104162306a36Sopenharmony_ci if (rc) 104262306a36Sopenharmony_ci pr_warn("SELinux: security_context_str_to_sid (%s) failed with errno=%d\n", 104362306a36Sopenharmony_ci s, rc); 104462306a36Sopenharmony_ci return rc; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cierr: 104762306a36Sopenharmony_ci pr_warn(SEL_MOUNT_FAIL_MSG); 104862306a36Sopenharmony_ci return -EINVAL; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_cistatic int show_sid(struct seq_file *m, u32 sid) 105262306a36Sopenharmony_ci{ 105362306a36Sopenharmony_ci char *context = NULL; 105462306a36Sopenharmony_ci u32 len; 105562306a36Sopenharmony_ci int rc; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci rc = security_sid_to_context(sid, &context, &len); 105862306a36Sopenharmony_ci if (!rc) { 105962306a36Sopenharmony_ci bool has_comma = strchr(context, ','); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci seq_putc(m, '='); 106262306a36Sopenharmony_ci if (has_comma) 106362306a36Sopenharmony_ci seq_putc(m, '\"'); 106462306a36Sopenharmony_ci seq_escape(m, context, "\"\n\\"); 106562306a36Sopenharmony_ci if (has_comma) 106662306a36Sopenharmony_ci seq_putc(m, '\"'); 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci kfree(context); 106962306a36Sopenharmony_ci return rc; 107062306a36Sopenharmony_ci} 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic int selinux_sb_show_options(struct seq_file *m, struct super_block *sb) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 107562306a36Sopenharmony_ci int rc; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (!(sbsec->flags & SE_SBINITIALIZED)) 107862306a36Sopenharmony_ci return 0; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci if (!selinux_initialized()) 108162306a36Sopenharmony_ci return 0; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (sbsec->flags & FSCONTEXT_MNT) { 108462306a36Sopenharmony_ci seq_putc(m, ','); 108562306a36Sopenharmony_ci seq_puts(m, FSCONTEXT_STR); 108662306a36Sopenharmony_ci rc = show_sid(m, sbsec->sid); 108762306a36Sopenharmony_ci if (rc) 108862306a36Sopenharmony_ci return rc; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci if (sbsec->flags & CONTEXT_MNT) { 109162306a36Sopenharmony_ci seq_putc(m, ','); 109262306a36Sopenharmony_ci seq_puts(m, CONTEXT_STR); 109362306a36Sopenharmony_ci rc = show_sid(m, sbsec->mntpoint_sid); 109462306a36Sopenharmony_ci if (rc) 109562306a36Sopenharmony_ci return rc; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci if (sbsec->flags & DEFCONTEXT_MNT) { 109862306a36Sopenharmony_ci seq_putc(m, ','); 109962306a36Sopenharmony_ci seq_puts(m, DEFCONTEXT_STR); 110062306a36Sopenharmony_ci rc = show_sid(m, sbsec->def_sid); 110162306a36Sopenharmony_ci if (rc) 110262306a36Sopenharmony_ci return rc; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci if (sbsec->flags & ROOTCONTEXT_MNT) { 110562306a36Sopenharmony_ci struct dentry *root = sb->s_root; 110662306a36Sopenharmony_ci struct inode_security_struct *isec = backing_inode_security(root); 110762306a36Sopenharmony_ci seq_putc(m, ','); 110862306a36Sopenharmony_ci seq_puts(m, ROOTCONTEXT_STR); 110962306a36Sopenharmony_ci rc = show_sid(m, isec->sid); 111062306a36Sopenharmony_ci if (rc) 111162306a36Sopenharmony_ci return rc; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci if (sbsec->flags & SBLABEL_MNT) { 111462306a36Sopenharmony_ci seq_putc(m, ','); 111562306a36Sopenharmony_ci seq_puts(m, SECLABEL_STR); 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic inline u16 inode_mode_to_security_class(umode_t mode) 112162306a36Sopenharmony_ci{ 112262306a36Sopenharmony_ci switch (mode & S_IFMT) { 112362306a36Sopenharmony_ci case S_IFSOCK: 112462306a36Sopenharmony_ci return SECCLASS_SOCK_FILE; 112562306a36Sopenharmony_ci case S_IFLNK: 112662306a36Sopenharmony_ci return SECCLASS_LNK_FILE; 112762306a36Sopenharmony_ci case S_IFREG: 112862306a36Sopenharmony_ci return SECCLASS_FILE; 112962306a36Sopenharmony_ci case S_IFBLK: 113062306a36Sopenharmony_ci return SECCLASS_BLK_FILE; 113162306a36Sopenharmony_ci case S_IFDIR: 113262306a36Sopenharmony_ci return SECCLASS_DIR; 113362306a36Sopenharmony_ci case S_IFCHR: 113462306a36Sopenharmony_ci return SECCLASS_CHR_FILE; 113562306a36Sopenharmony_ci case S_IFIFO: 113662306a36Sopenharmony_ci return SECCLASS_FIFO_FILE; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci return SECCLASS_FILE; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_cistatic inline int default_protocol_stream(int protocol) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci return (protocol == IPPROTO_IP || protocol == IPPROTO_TCP || 114662306a36Sopenharmony_ci protocol == IPPROTO_MPTCP); 114762306a36Sopenharmony_ci} 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic inline int default_protocol_dgram(int protocol) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci return (protocol == IPPROTO_IP || protocol == IPPROTO_UDP); 115262306a36Sopenharmony_ci} 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_cistatic inline u16 socket_type_to_security_class(int family, int type, int protocol) 115562306a36Sopenharmony_ci{ 115662306a36Sopenharmony_ci bool extsockclass = selinux_policycap_extsockclass(); 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci switch (family) { 115962306a36Sopenharmony_ci case PF_UNIX: 116062306a36Sopenharmony_ci switch (type) { 116162306a36Sopenharmony_ci case SOCK_STREAM: 116262306a36Sopenharmony_ci case SOCK_SEQPACKET: 116362306a36Sopenharmony_ci return SECCLASS_UNIX_STREAM_SOCKET; 116462306a36Sopenharmony_ci case SOCK_DGRAM: 116562306a36Sopenharmony_ci case SOCK_RAW: 116662306a36Sopenharmony_ci return SECCLASS_UNIX_DGRAM_SOCKET; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci case PF_INET: 117062306a36Sopenharmony_ci case PF_INET6: 117162306a36Sopenharmony_ci switch (type) { 117262306a36Sopenharmony_ci case SOCK_STREAM: 117362306a36Sopenharmony_ci case SOCK_SEQPACKET: 117462306a36Sopenharmony_ci if (default_protocol_stream(protocol)) 117562306a36Sopenharmony_ci return SECCLASS_TCP_SOCKET; 117662306a36Sopenharmony_ci else if (extsockclass && protocol == IPPROTO_SCTP) 117762306a36Sopenharmony_ci return SECCLASS_SCTP_SOCKET; 117862306a36Sopenharmony_ci else 117962306a36Sopenharmony_ci return SECCLASS_RAWIP_SOCKET; 118062306a36Sopenharmony_ci case SOCK_DGRAM: 118162306a36Sopenharmony_ci if (default_protocol_dgram(protocol)) 118262306a36Sopenharmony_ci return SECCLASS_UDP_SOCKET; 118362306a36Sopenharmony_ci else if (extsockclass && (protocol == IPPROTO_ICMP || 118462306a36Sopenharmony_ci protocol == IPPROTO_ICMPV6)) 118562306a36Sopenharmony_ci return SECCLASS_ICMP_SOCKET; 118662306a36Sopenharmony_ci else 118762306a36Sopenharmony_ci return SECCLASS_RAWIP_SOCKET; 118862306a36Sopenharmony_ci case SOCK_DCCP: 118962306a36Sopenharmony_ci return SECCLASS_DCCP_SOCKET; 119062306a36Sopenharmony_ci default: 119162306a36Sopenharmony_ci return SECCLASS_RAWIP_SOCKET; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci break; 119462306a36Sopenharmony_ci case PF_NETLINK: 119562306a36Sopenharmony_ci switch (protocol) { 119662306a36Sopenharmony_ci case NETLINK_ROUTE: 119762306a36Sopenharmony_ci return SECCLASS_NETLINK_ROUTE_SOCKET; 119862306a36Sopenharmony_ci case NETLINK_SOCK_DIAG: 119962306a36Sopenharmony_ci return SECCLASS_NETLINK_TCPDIAG_SOCKET; 120062306a36Sopenharmony_ci case NETLINK_NFLOG: 120162306a36Sopenharmony_ci return SECCLASS_NETLINK_NFLOG_SOCKET; 120262306a36Sopenharmony_ci case NETLINK_XFRM: 120362306a36Sopenharmony_ci return SECCLASS_NETLINK_XFRM_SOCKET; 120462306a36Sopenharmony_ci case NETLINK_SELINUX: 120562306a36Sopenharmony_ci return SECCLASS_NETLINK_SELINUX_SOCKET; 120662306a36Sopenharmony_ci case NETLINK_ISCSI: 120762306a36Sopenharmony_ci return SECCLASS_NETLINK_ISCSI_SOCKET; 120862306a36Sopenharmony_ci case NETLINK_AUDIT: 120962306a36Sopenharmony_ci return SECCLASS_NETLINK_AUDIT_SOCKET; 121062306a36Sopenharmony_ci case NETLINK_FIB_LOOKUP: 121162306a36Sopenharmony_ci return SECCLASS_NETLINK_FIB_LOOKUP_SOCKET; 121262306a36Sopenharmony_ci case NETLINK_CONNECTOR: 121362306a36Sopenharmony_ci return SECCLASS_NETLINK_CONNECTOR_SOCKET; 121462306a36Sopenharmony_ci case NETLINK_NETFILTER: 121562306a36Sopenharmony_ci return SECCLASS_NETLINK_NETFILTER_SOCKET; 121662306a36Sopenharmony_ci case NETLINK_DNRTMSG: 121762306a36Sopenharmony_ci return SECCLASS_NETLINK_DNRT_SOCKET; 121862306a36Sopenharmony_ci case NETLINK_KOBJECT_UEVENT: 121962306a36Sopenharmony_ci return SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET; 122062306a36Sopenharmony_ci case NETLINK_GENERIC: 122162306a36Sopenharmony_ci return SECCLASS_NETLINK_GENERIC_SOCKET; 122262306a36Sopenharmony_ci case NETLINK_SCSITRANSPORT: 122362306a36Sopenharmony_ci return SECCLASS_NETLINK_SCSITRANSPORT_SOCKET; 122462306a36Sopenharmony_ci case NETLINK_RDMA: 122562306a36Sopenharmony_ci return SECCLASS_NETLINK_RDMA_SOCKET; 122662306a36Sopenharmony_ci case NETLINK_CRYPTO: 122762306a36Sopenharmony_ci return SECCLASS_NETLINK_CRYPTO_SOCKET; 122862306a36Sopenharmony_ci default: 122962306a36Sopenharmony_ci return SECCLASS_NETLINK_SOCKET; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci case PF_PACKET: 123262306a36Sopenharmony_ci return SECCLASS_PACKET_SOCKET; 123362306a36Sopenharmony_ci case PF_KEY: 123462306a36Sopenharmony_ci return SECCLASS_KEY_SOCKET; 123562306a36Sopenharmony_ci case PF_APPLETALK: 123662306a36Sopenharmony_ci return SECCLASS_APPLETALK_SOCKET; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (extsockclass) { 124062306a36Sopenharmony_ci switch (family) { 124162306a36Sopenharmony_ci case PF_AX25: 124262306a36Sopenharmony_ci return SECCLASS_AX25_SOCKET; 124362306a36Sopenharmony_ci case PF_IPX: 124462306a36Sopenharmony_ci return SECCLASS_IPX_SOCKET; 124562306a36Sopenharmony_ci case PF_NETROM: 124662306a36Sopenharmony_ci return SECCLASS_NETROM_SOCKET; 124762306a36Sopenharmony_ci case PF_ATMPVC: 124862306a36Sopenharmony_ci return SECCLASS_ATMPVC_SOCKET; 124962306a36Sopenharmony_ci case PF_X25: 125062306a36Sopenharmony_ci return SECCLASS_X25_SOCKET; 125162306a36Sopenharmony_ci case PF_ROSE: 125262306a36Sopenharmony_ci return SECCLASS_ROSE_SOCKET; 125362306a36Sopenharmony_ci case PF_DECnet: 125462306a36Sopenharmony_ci return SECCLASS_DECNET_SOCKET; 125562306a36Sopenharmony_ci case PF_ATMSVC: 125662306a36Sopenharmony_ci return SECCLASS_ATMSVC_SOCKET; 125762306a36Sopenharmony_ci case PF_RDS: 125862306a36Sopenharmony_ci return SECCLASS_RDS_SOCKET; 125962306a36Sopenharmony_ci case PF_IRDA: 126062306a36Sopenharmony_ci return SECCLASS_IRDA_SOCKET; 126162306a36Sopenharmony_ci case PF_PPPOX: 126262306a36Sopenharmony_ci return SECCLASS_PPPOX_SOCKET; 126362306a36Sopenharmony_ci case PF_LLC: 126462306a36Sopenharmony_ci return SECCLASS_LLC_SOCKET; 126562306a36Sopenharmony_ci case PF_CAN: 126662306a36Sopenharmony_ci return SECCLASS_CAN_SOCKET; 126762306a36Sopenharmony_ci case PF_TIPC: 126862306a36Sopenharmony_ci return SECCLASS_TIPC_SOCKET; 126962306a36Sopenharmony_ci case PF_BLUETOOTH: 127062306a36Sopenharmony_ci return SECCLASS_BLUETOOTH_SOCKET; 127162306a36Sopenharmony_ci case PF_IUCV: 127262306a36Sopenharmony_ci return SECCLASS_IUCV_SOCKET; 127362306a36Sopenharmony_ci case PF_RXRPC: 127462306a36Sopenharmony_ci return SECCLASS_RXRPC_SOCKET; 127562306a36Sopenharmony_ci case PF_ISDN: 127662306a36Sopenharmony_ci return SECCLASS_ISDN_SOCKET; 127762306a36Sopenharmony_ci case PF_PHONET: 127862306a36Sopenharmony_ci return SECCLASS_PHONET_SOCKET; 127962306a36Sopenharmony_ci case PF_IEEE802154: 128062306a36Sopenharmony_ci return SECCLASS_IEEE802154_SOCKET; 128162306a36Sopenharmony_ci case PF_CAIF: 128262306a36Sopenharmony_ci return SECCLASS_CAIF_SOCKET; 128362306a36Sopenharmony_ci case PF_ALG: 128462306a36Sopenharmony_ci return SECCLASS_ALG_SOCKET; 128562306a36Sopenharmony_ci case PF_NFC: 128662306a36Sopenharmony_ci return SECCLASS_NFC_SOCKET; 128762306a36Sopenharmony_ci case PF_VSOCK: 128862306a36Sopenharmony_ci return SECCLASS_VSOCK_SOCKET; 128962306a36Sopenharmony_ci case PF_KCM: 129062306a36Sopenharmony_ci return SECCLASS_KCM_SOCKET; 129162306a36Sopenharmony_ci case PF_QIPCRTR: 129262306a36Sopenharmony_ci return SECCLASS_QIPCRTR_SOCKET; 129362306a36Sopenharmony_ci case PF_SMC: 129462306a36Sopenharmony_ci return SECCLASS_SMC_SOCKET; 129562306a36Sopenharmony_ci case PF_XDP: 129662306a36Sopenharmony_ci return SECCLASS_XDP_SOCKET; 129762306a36Sopenharmony_ci case PF_MCTP: 129862306a36Sopenharmony_ci return SECCLASS_MCTP_SOCKET; 129962306a36Sopenharmony_ci#if PF_MAX > 46 130062306a36Sopenharmony_ci#error New address family defined, please update this function. 130162306a36Sopenharmony_ci#endif 130262306a36Sopenharmony_ci } 130362306a36Sopenharmony_ci } 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci return SECCLASS_SOCKET; 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_cistatic int selinux_genfs_get_sid(struct dentry *dentry, 130962306a36Sopenharmony_ci u16 tclass, 131062306a36Sopenharmony_ci u16 flags, 131162306a36Sopenharmony_ci u32 *sid) 131262306a36Sopenharmony_ci{ 131362306a36Sopenharmony_ci int rc; 131462306a36Sopenharmony_ci struct super_block *sb = dentry->d_sb; 131562306a36Sopenharmony_ci char *buffer, *path; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci buffer = (char *)__get_free_page(GFP_KERNEL); 131862306a36Sopenharmony_ci if (!buffer) 131962306a36Sopenharmony_ci return -ENOMEM; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci path = dentry_path_raw(dentry, buffer, PAGE_SIZE); 132262306a36Sopenharmony_ci if (IS_ERR(path)) 132362306a36Sopenharmony_ci rc = PTR_ERR(path); 132462306a36Sopenharmony_ci else { 132562306a36Sopenharmony_ci if (flags & SE_SBPROC) { 132662306a36Sopenharmony_ci /* each process gets a /proc/PID/ entry. Strip off the 132762306a36Sopenharmony_ci * PID part to get a valid selinux labeling. 132862306a36Sopenharmony_ci * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ 132962306a36Sopenharmony_ci while (path[1] >= '0' && path[1] <= '9') { 133062306a36Sopenharmony_ci path[1] = '/'; 133162306a36Sopenharmony_ci path++; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci rc = security_genfs_sid(sb->s_type->name, 133562306a36Sopenharmony_ci path, tclass, sid); 133662306a36Sopenharmony_ci if (rc == -ENOENT) { 133762306a36Sopenharmony_ci /* No match in policy, mark as unlabeled. */ 133862306a36Sopenharmony_ci *sid = SECINITSID_UNLABELED; 133962306a36Sopenharmony_ci rc = 0; 134062306a36Sopenharmony_ci } 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci free_page((unsigned long)buffer); 134362306a36Sopenharmony_ci return rc; 134462306a36Sopenharmony_ci} 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistatic int inode_doinit_use_xattr(struct inode *inode, struct dentry *dentry, 134762306a36Sopenharmony_ci u32 def_sid, u32 *sid) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci#define INITCONTEXTLEN 255 135062306a36Sopenharmony_ci char *context; 135162306a36Sopenharmony_ci unsigned int len; 135262306a36Sopenharmony_ci int rc; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci len = INITCONTEXTLEN; 135562306a36Sopenharmony_ci context = kmalloc(len + 1, GFP_NOFS); 135662306a36Sopenharmony_ci if (!context) 135762306a36Sopenharmony_ci return -ENOMEM; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci context[len] = '\0'; 136062306a36Sopenharmony_ci rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); 136162306a36Sopenharmony_ci if (rc == -ERANGE) { 136262306a36Sopenharmony_ci kfree(context); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* Need a larger buffer. Query for the right size. */ 136562306a36Sopenharmony_ci rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); 136662306a36Sopenharmony_ci if (rc < 0) 136762306a36Sopenharmony_ci return rc; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci len = rc; 137062306a36Sopenharmony_ci context = kmalloc(len + 1, GFP_NOFS); 137162306a36Sopenharmony_ci if (!context) 137262306a36Sopenharmony_ci return -ENOMEM; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci context[len] = '\0'; 137562306a36Sopenharmony_ci rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, 137662306a36Sopenharmony_ci context, len); 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci if (rc < 0) { 137962306a36Sopenharmony_ci kfree(context); 138062306a36Sopenharmony_ci if (rc != -ENODATA) { 138162306a36Sopenharmony_ci pr_warn("SELinux: %s: getxattr returned %d for dev=%s ino=%ld\n", 138262306a36Sopenharmony_ci __func__, -rc, inode->i_sb->s_id, inode->i_ino); 138362306a36Sopenharmony_ci return rc; 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci *sid = def_sid; 138662306a36Sopenharmony_ci return 0; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci rc = security_context_to_sid_default(context, rc, sid, 139062306a36Sopenharmony_ci def_sid, GFP_NOFS); 139162306a36Sopenharmony_ci if (rc) { 139262306a36Sopenharmony_ci char *dev = inode->i_sb->s_id; 139362306a36Sopenharmony_ci unsigned long ino = inode->i_ino; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (rc == -EINVAL) { 139662306a36Sopenharmony_ci pr_notice_ratelimited("SELinux: inode=%lu on dev=%s was found to have an invalid context=%s. This indicates you may need to relabel the inode or the filesystem in question.\n", 139762306a36Sopenharmony_ci ino, dev, context); 139862306a36Sopenharmony_ci } else { 139962306a36Sopenharmony_ci pr_warn("SELinux: %s: context_to_sid(%s) returned %d for dev=%s ino=%ld\n", 140062306a36Sopenharmony_ci __func__, context, -rc, dev, ino); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci kfree(context); 140462306a36Sopenharmony_ci return 0; 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci/* The inode's security attributes must be initialized before first use. */ 140862306a36Sopenharmony_cistatic int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci struct superblock_security_struct *sbsec = NULL; 141162306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 141262306a36Sopenharmony_ci u32 task_sid, sid = 0; 141362306a36Sopenharmony_ci u16 sclass; 141462306a36Sopenharmony_ci struct dentry *dentry; 141562306a36Sopenharmony_ci int rc = 0; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (isec->initialized == LABEL_INITIALIZED) 141862306a36Sopenharmony_ci return 0; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci spin_lock(&isec->lock); 142162306a36Sopenharmony_ci if (isec->initialized == LABEL_INITIALIZED) 142262306a36Sopenharmony_ci goto out_unlock; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci if (isec->sclass == SECCLASS_FILE) 142562306a36Sopenharmony_ci isec->sclass = inode_mode_to_security_class(inode->i_mode); 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci sbsec = selinux_superblock(inode->i_sb); 142862306a36Sopenharmony_ci if (!(sbsec->flags & SE_SBINITIALIZED)) { 142962306a36Sopenharmony_ci /* Defer initialization until selinux_complete_init, 143062306a36Sopenharmony_ci after the initial policy is loaded and the security 143162306a36Sopenharmony_ci server is ready to handle calls. */ 143262306a36Sopenharmony_ci spin_lock(&sbsec->isec_lock); 143362306a36Sopenharmony_ci if (list_empty(&isec->list)) 143462306a36Sopenharmony_ci list_add(&isec->list, &sbsec->isec_head); 143562306a36Sopenharmony_ci spin_unlock(&sbsec->isec_lock); 143662306a36Sopenharmony_ci goto out_unlock; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci sclass = isec->sclass; 144062306a36Sopenharmony_ci task_sid = isec->task_sid; 144162306a36Sopenharmony_ci sid = isec->sid; 144262306a36Sopenharmony_ci isec->initialized = LABEL_PENDING; 144362306a36Sopenharmony_ci spin_unlock(&isec->lock); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci switch (sbsec->behavior) { 144662306a36Sopenharmony_ci /* 144762306a36Sopenharmony_ci * In case of SECURITY_FS_USE_NATIVE we need to re-fetch the labels 144862306a36Sopenharmony_ci * via xattr when called from delayed_superblock_init(). 144962306a36Sopenharmony_ci */ 145062306a36Sopenharmony_ci case SECURITY_FS_USE_NATIVE: 145162306a36Sopenharmony_ci case SECURITY_FS_USE_XATTR: 145262306a36Sopenharmony_ci if (!(inode->i_opflags & IOP_XATTR)) { 145362306a36Sopenharmony_ci sid = sbsec->def_sid; 145462306a36Sopenharmony_ci break; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci /* Need a dentry, since the xattr API requires one. 145762306a36Sopenharmony_ci Life would be simpler if we could just pass the inode. */ 145862306a36Sopenharmony_ci if (opt_dentry) { 145962306a36Sopenharmony_ci /* Called from d_instantiate or d_splice_alias. */ 146062306a36Sopenharmony_ci dentry = dget(opt_dentry); 146162306a36Sopenharmony_ci } else { 146262306a36Sopenharmony_ci /* 146362306a36Sopenharmony_ci * Called from selinux_complete_init, try to find a dentry. 146462306a36Sopenharmony_ci * Some filesystems really want a connected one, so try 146562306a36Sopenharmony_ci * that first. We could split SECURITY_FS_USE_XATTR in 146662306a36Sopenharmony_ci * two, depending upon that... 146762306a36Sopenharmony_ci */ 146862306a36Sopenharmony_ci dentry = d_find_alias(inode); 146962306a36Sopenharmony_ci if (!dentry) 147062306a36Sopenharmony_ci dentry = d_find_any_alias(inode); 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci if (!dentry) { 147362306a36Sopenharmony_ci /* 147462306a36Sopenharmony_ci * this is can be hit on boot when a file is accessed 147562306a36Sopenharmony_ci * before the policy is loaded. When we load policy we 147662306a36Sopenharmony_ci * may find inodes that have no dentry on the 147762306a36Sopenharmony_ci * sbsec->isec_head list. No reason to complain as these 147862306a36Sopenharmony_ci * will get fixed up the next time we go through 147962306a36Sopenharmony_ci * inode_doinit with a dentry, before these inodes could 148062306a36Sopenharmony_ci * be used again by userspace. 148162306a36Sopenharmony_ci */ 148262306a36Sopenharmony_ci goto out_invalid; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci rc = inode_doinit_use_xattr(inode, dentry, sbsec->def_sid, 148662306a36Sopenharmony_ci &sid); 148762306a36Sopenharmony_ci dput(dentry); 148862306a36Sopenharmony_ci if (rc) 148962306a36Sopenharmony_ci goto out; 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci case SECURITY_FS_USE_TASK: 149262306a36Sopenharmony_ci sid = task_sid; 149362306a36Sopenharmony_ci break; 149462306a36Sopenharmony_ci case SECURITY_FS_USE_TRANS: 149562306a36Sopenharmony_ci /* Default to the fs SID. */ 149662306a36Sopenharmony_ci sid = sbsec->sid; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci /* Try to obtain a transition SID. */ 149962306a36Sopenharmony_ci rc = security_transition_sid(task_sid, sid, 150062306a36Sopenharmony_ci sclass, NULL, &sid); 150162306a36Sopenharmony_ci if (rc) 150262306a36Sopenharmony_ci goto out; 150362306a36Sopenharmony_ci break; 150462306a36Sopenharmony_ci case SECURITY_FS_USE_MNTPOINT: 150562306a36Sopenharmony_ci sid = sbsec->mntpoint_sid; 150662306a36Sopenharmony_ci break; 150762306a36Sopenharmony_ci default: 150862306a36Sopenharmony_ci /* Default to the fs superblock SID. */ 150962306a36Sopenharmony_ci sid = sbsec->sid; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if ((sbsec->flags & SE_SBGENFS) && 151262306a36Sopenharmony_ci (!S_ISLNK(inode->i_mode) || 151362306a36Sopenharmony_ci selinux_policycap_genfs_seclabel_symlinks())) { 151462306a36Sopenharmony_ci /* We must have a dentry to determine the label on 151562306a36Sopenharmony_ci * procfs inodes */ 151662306a36Sopenharmony_ci if (opt_dentry) { 151762306a36Sopenharmony_ci /* Called from d_instantiate or 151862306a36Sopenharmony_ci * d_splice_alias. */ 151962306a36Sopenharmony_ci dentry = dget(opt_dentry); 152062306a36Sopenharmony_ci } else { 152162306a36Sopenharmony_ci /* Called from selinux_complete_init, try to 152262306a36Sopenharmony_ci * find a dentry. Some filesystems really want 152362306a36Sopenharmony_ci * a connected one, so try that first. 152462306a36Sopenharmony_ci */ 152562306a36Sopenharmony_ci dentry = d_find_alias(inode); 152662306a36Sopenharmony_ci if (!dentry) 152762306a36Sopenharmony_ci dentry = d_find_any_alias(inode); 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci /* 153062306a36Sopenharmony_ci * This can be hit on boot when a file is accessed 153162306a36Sopenharmony_ci * before the policy is loaded. When we load policy we 153262306a36Sopenharmony_ci * may find inodes that have no dentry on the 153362306a36Sopenharmony_ci * sbsec->isec_head list. No reason to complain as 153462306a36Sopenharmony_ci * these will get fixed up the next time we go through 153562306a36Sopenharmony_ci * inode_doinit() with a dentry, before these inodes 153662306a36Sopenharmony_ci * could be used again by userspace. 153762306a36Sopenharmony_ci */ 153862306a36Sopenharmony_ci if (!dentry) 153962306a36Sopenharmony_ci goto out_invalid; 154062306a36Sopenharmony_ci rc = selinux_genfs_get_sid(dentry, sclass, 154162306a36Sopenharmony_ci sbsec->flags, &sid); 154262306a36Sopenharmony_ci if (rc) { 154362306a36Sopenharmony_ci dput(dentry); 154462306a36Sopenharmony_ci goto out; 154562306a36Sopenharmony_ci } 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci if ((sbsec->flags & SE_SBGENFS_XATTR) && 154862306a36Sopenharmony_ci (inode->i_opflags & IOP_XATTR)) { 154962306a36Sopenharmony_ci rc = inode_doinit_use_xattr(inode, dentry, 155062306a36Sopenharmony_ci sid, &sid); 155162306a36Sopenharmony_ci if (rc) { 155262306a36Sopenharmony_ci dput(dentry); 155362306a36Sopenharmony_ci goto out; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci dput(dentry); 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci break; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ciout: 156262306a36Sopenharmony_ci spin_lock(&isec->lock); 156362306a36Sopenharmony_ci if (isec->initialized == LABEL_PENDING) { 156462306a36Sopenharmony_ci if (rc) { 156562306a36Sopenharmony_ci isec->initialized = LABEL_INVALID; 156662306a36Sopenharmony_ci goto out_unlock; 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 156962306a36Sopenharmony_ci isec->sid = sid; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ciout_unlock: 157362306a36Sopenharmony_ci spin_unlock(&isec->lock); 157462306a36Sopenharmony_ci return rc; 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ciout_invalid: 157762306a36Sopenharmony_ci spin_lock(&isec->lock); 157862306a36Sopenharmony_ci if (isec->initialized == LABEL_PENDING) { 157962306a36Sopenharmony_ci isec->initialized = LABEL_INVALID; 158062306a36Sopenharmony_ci isec->sid = sid; 158162306a36Sopenharmony_ci } 158262306a36Sopenharmony_ci spin_unlock(&isec->lock); 158362306a36Sopenharmony_ci return 0; 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci/* Convert a Linux signal to an access vector. */ 158762306a36Sopenharmony_cistatic inline u32 signal_to_av(int sig) 158862306a36Sopenharmony_ci{ 158962306a36Sopenharmony_ci u32 perm = 0; 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci switch (sig) { 159262306a36Sopenharmony_ci case SIGCHLD: 159362306a36Sopenharmony_ci /* Commonly granted from child to parent. */ 159462306a36Sopenharmony_ci perm = PROCESS__SIGCHLD; 159562306a36Sopenharmony_ci break; 159662306a36Sopenharmony_ci case SIGKILL: 159762306a36Sopenharmony_ci /* Cannot be caught or ignored */ 159862306a36Sopenharmony_ci perm = PROCESS__SIGKILL; 159962306a36Sopenharmony_ci break; 160062306a36Sopenharmony_ci case SIGSTOP: 160162306a36Sopenharmony_ci /* Cannot be caught or ignored */ 160262306a36Sopenharmony_ci perm = PROCESS__SIGSTOP; 160362306a36Sopenharmony_ci break; 160462306a36Sopenharmony_ci default: 160562306a36Sopenharmony_ci /* All other signals. */ 160662306a36Sopenharmony_ci perm = PROCESS__SIGNAL; 160762306a36Sopenharmony_ci break; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci return perm; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci#if CAP_LAST_CAP > 63 161462306a36Sopenharmony_ci#error Fix SELinux to handle capabilities > 63. 161562306a36Sopenharmony_ci#endif 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci/* Check whether a task is allowed to use a capability. */ 161862306a36Sopenharmony_cistatic int cred_has_capability(const struct cred *cred, 161962306a36Sopenharmony_ci int cap, unsigned int opts, bool initns) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct common_audit_data ad; 162262306a36Sopenharmony_ci struct av_decision avd; 162362306a36Sopenharmony_ci u16 sclass; 162462306a36Sopenharmony_ci u32 sid = cred_sid(cred); 162562306a36Sopenharmony_ci u32 av = CAP_TO_MASK(cap); 162662306a36Sopenharmony_ci int rc; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_CAP; 162962306a36Sopenharmony_ci ad.u.cap = cap; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci switch (CAP_TO_INDEX(cap)) { 163262306a36Sopenharmony_ci case 0: 163362306a36Sopenharmony_ci sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS; 163462306a36Sopenharmony_ci break; 163562306a36Sopenharmony_ci case 1: 163662306a36Sopenharmony_ci sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS; 163762306a36Sopenharmony_ci break; 163862306a36Sopenharmony_ci default: 163962306a36Sopenharmony_ci pr_err("SELinux: out of range capability %d\n", cap); 164062306a36Sopenharmony_ci BUG(); 164162306a36Sopenharmony_ci return -EINVAL; 164262306a36Sopenharmony_ci } 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd); 164562306a36Sopenharmony_ci if (!(opts & CAP_OPT_NOAUDIT)) { 164662306a36Sopenharmony_ci int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad); 164762306a36Sopenharmony_ci if (rc2) 164862306a36Sopenharmony_ci return rc2; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci return rc; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci/* Check whether a task has a particular permission to an inode. 165462306a36Sopenharmony_ci The 'adp' parameter is optional and allows other audit 165562306a36Sopenharmony_ci data to be passed (e.g. the dentry). */ 165662306a36Sopenharmony_cistatic int inode_has_perm(const struct cred *cred, 165762306a36Sopenharmony_ci struct inode *inode, 165862306a36Sopenharmony_ci u32 perms, 165962306a36Sopenharmony_ci struct common_audit_data *adp) 166062306a36Sopenharmony_ci{ 166162306a36Sopenharmony_ci struct inode_security_struct *isec; 166262306a36Sopenharmony_ci u32 sid; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci if (unlikely(IS_PRIVATE(inode))) 166562306a36Sopenharmony_ci return 0; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci sid = cred_sid(cred); 166862306a36Sopenharmony_ci isec = selinux_inode(inode); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); 167162306a36Sopenharmony_ci} 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci/* Same as inode_has_perm, but pass explicit audit data containing 167462306a36Sopenharmony_ci the dentry to help the auditing code to more easily generate the 167562306a36Sopenharmony_ci pathname if needed. */ 167662306a36Sopenharmony_cistatic inline int dentry_has_perm(const struct cred *cred, 167762306a36Sopenharmony_ci struct dentry *dentry, 167862306a36Sopenharmony_ci u32 av) 167962306a36Sopenharmony_ci{ 168062306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 168162306a36Sopenharmony_ci struct common_audit_data ad; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 168462306a36Sopenharmony_ci ad.u.dentry = dentry; 168562306a36Sopenharmony_ci __inode_security_revalidate(inode, dentry, true); 168662306a36Sopenharmony_ci return inode_has_perm(cred, inode, av, &ad); 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci/* Same as inode_has_perm, but pass explicit audit data containing 169062306a36Sopenharmony_ci the path to help the auditing code to more easily generate the 169162306a36Sopenharmony_ci pathname if needed. */ 169262306a36Sopenharmony_cistatic inline int path_has_perm(const struct cred *cred, 169362306a36Sopenharmony_ci const struct path *path, 169462306a36Sopenharmony_ci u32 av) 169562306a36Sopenharmony_ci{ 169662306a36Sopenharmony_ci struct inode *inode = d_backing_inode(path->dentry); 169762306a36Sopenharmony_ci struct common_audit_data ad; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_PATH; 170062306a36Sopenharmony_ci ad.u.path = *path; 170162306a36Sopenharmony_ci __inode_security_revalidate(inode, path->dentry, true); 170262306a36Sopenharmony_ci return inode_has_perm(cred, inode, av, &ad); 170362306a36Sopenharmony_ci} 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci/* Same as path_has_perm, but uses the inode from the file struct. */ 170662306a36Sopenharmony_cistatic inline int file_path_has_perm(const struct cred *cred, 170762306a36Sopenharmony_ci struct file *file, 170862306a36Sopenharmony_ci u32 av) 170962306a36Sopenharmony_ci{ 171062306a36Sopenharmony_ci struct common_audit_data ad; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_FILE; 171362306a36Sopenharmony_ci ad.u.file = file; 171462306a36Sopenharmony_ci return inode_has_perm(cred, file_inode(file), av, &ad); 171562306a36Sopenharmony_ci} 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 171862306a36Sopenharmony_cistatic int bpf_fd_pass(const struct file *file, u32 sid); 171962306a36Sopenharmony_ci#endif 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci/* Check whether a task can use an open file descriptor to 172262306a36Sopenharmony_ci access an inode in a given way. Check access to the 172362306a36Sopenharmony_ci descriptor itself, and then use dentry_has_perm to 172462306a36Sopenharmony_ci check a particular permission to the file. 172562306a36Sopenharmony_ci Access to the descriptor is implicitly granted if it 172662306a36Sopenharmony_ci has the same SID as the process. If av is zero, then 172762306a36Sopenharmony_ci access to the file is not checked, e.g. for cases 172862306a36Sopenharmony_ci where only the descriptor is affected like seek. */ 172962306a36Sopenharmony_cistatic int file_has_perm(const struct cred *cred, 173062306a36Sopenharmony_ci struct file *file, 173162306a36Sopenharmony_ci u32 av) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct file_security_struct *fsec = selinux_file(file); 173462306a36Sopenharmony_ci struct inode *inode = file_inode(file); 173562306a36Sopenharmony_ci struct common_audit_data ad; 173662306a36Sopenharmony_ci u32 sid = cred_sid(cred); 173762306a36Sopenharmony_ci int rc; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_FILE; 174062306a36Sopenharmony_ci ad.u.file = file; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci if (sid != fsec->sid) { 174362306a36Sopenharmony_ci rc = avc_has_perm(sid, fsec->sid, 174462306a36Sopenharmony_ci SECCLASS_FD, 174562306a36Sopenharmony_ci FD__USE, 174662306a36Sopenharmony_ci &ad); 174762306a36Sopenharmony_ci if (rc) 174862306a36Sopenharmony_ci goto out; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 175262306a36Sopenharmony_ci rc = bpf_fd_pass(file, cred_sid(cred)); 175362306a36Sopenharmony_ci if (rc) 175462306a36Sopenharmony_ci return rc; 175562306a36Sopenharmony_ci#endif 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci /* av is zero if only checking access to the descriptor. */ 175862306a36Sopenharmony_ci rc = 0; 175962306a36Sopenharmony_ci if (av) 176062306a36Sopenharmony_ci rc = inode_has_perm(cred, inode, av, &ad); 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ciout: 176362306a36Sopenharmony_ci return rc; 176462306a36Sopenharmony_ci} 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci/* 176762306a36Sopenharmony_ci * Determine the label for an inode that might be unioned. 176862306a36Sopenharmony_ci */ 176962306a36Sopenharmony_cistatic int 177062306a36Sopenharmony_ciselinux_determine_inode_label(const struct task_security_struct *tsec, 177162306a36Sopenharmony_ci struct inode *dir, 177262306a36Sopenharmony_ci const struct qstr *name, u16 tclass, 177362306a36Sopenharmony_ci u32 *_new_isid) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci const struct superblock_security_struct *sbsec = 177662306a36Sopenharmony_ci selinux_superblock(dir->i_sb); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci if ((sbsec->flags & SE_SBINITIALIZED) && 177962306a36Sopenharmony_ci (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) { 178062306a36Sopenharmony_ci *_new_isid = sbsec->mntpoint_sid; 178162306a36Sopenharmony_ci } else if ((sbsec->flags & SBLABEL_MNT) && 178262306a36Sopenharmony_ci tsec->create_sid) { 178362306a36Sopenharmony_ci *_new_isid = tsec->create_sid; 178462306a36Sopenharmony_ci } else { 178562306a36Sopenharmony_ci const struct inode_security_struct *dsec = inode_security(dir); 178662306a36Sopenharmony_ci return security_transition_sid(tsec->sid, 178762306a36Sopenharmony_ci dsec->sid, tclass, 178862306a36Sopenharmony_ci name, _new_isid); 178962306a36Sopenharmony_ci } 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci return 0; 179262306a36Sopenharmony_ci} 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci/* Check whether a task can create a file. */ 179562306a36Sopenharmony_cistatic int may_create(struct inode *dir, 179662306a36Sopenharmony_ci struct dentry *dentry, 179762306a36Sopenharmony_ci u16 tclass) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 180062306a36Sopenharmony_ci struct inode_security_struct *dsec; 180162306a36Sopenharmony_ci struct superblock_security_struct *sbsec; 180262306a36Sopenharmony_ci u32 sid, newsid; 180362306a36Sopenharmony_ci struct common_audit_data ad; 180462306a36Sopenharmony_ci int rc; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci dsec = inode_security(dir); 180762306a36Sopenharmony_ci sbsec = selinux_superblock(dir->i_sb); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci sid = tsec->sid; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 181262306a36Sopenharmony_ci ad.u.dentry = dentry; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, 181562306a36Sopenharmony_ci DIR__ADD_NAME | DIR__SEARCH, 181662306a36Sopenharmony_ci &ad); 181762306a36Sopenharmony_ci if (rc) 181862306a36Sopenharmony_ci return rc; 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci rc = selinux_determine_inode_label(tsec, dir, &dentry->d_name, tclass, 182162306a36Sopenharmony_ci &newsid); 182262306a36Sopenharmony_ci if (rc) 182362306a36Sopenharmony_ci return rc; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad); 182662306a36Sopenharmony_ci if (rc) 182762306a36Sopenharmony_ci return rc; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci return avc_has_perm(newsid, sbsec->sid, 183062306a36Sopenharmony_ci SECCLASS_FILESYSTEM, 183162306a36Sopenharmony_ci FILESYSTEM__ASSOCIATE, &ad); 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci#define MAY_LINK 0 183562306a36Sopenharmony_ci#define MAY_UNLINK 1 183662306a36Sopenharmony_ci#define MAY_RMDIR 2 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci/* Check whether a task can link, unlink, or rmdir a file/directory. */ 183962306a36Sopenharmony_cistatic int may_link(struct inode *dir, 184062306a36Sopenharmony_ci struct dentry *dentry, 184162306a36Sopenharmony_ci int kind) 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci{ 184462306a36Sopenharmony_ci struct inode_security_struct *dsec, *isec; 184562306a36Sopenharmony_ci struct common_audit_data ad; 184662306a36Sopenharmony_ci u32 sid = current_sid(); 184762306a36Sopenharmony_ci u32 av; 184862306a36Sopenharmony_ci int rc; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci dsec = inode_security(dir); 185162306a36Sopenharmony_ci isec = backing_inode_security(dentry); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 185462306a36Sopenharmony_ci ad.u.dentry = dentry; 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci av = DIR__SEARCH; 185762306a36Sopenharmony_ci av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); 185862306a36Sopenharmony_ci rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, av, &ad); 185962306a36Sopenharmony_ci if (rc) 186062306a36Sopenharmony_ci return rc; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci switch (kind) { 186362306a36Sopenharmony_ci case MAY_LINK: 186462306a36Sopenharmony_ci av = FILE__LINK; 186562306a36Sopenharmony_ci break; 186662306a36Sopenharmony_ci case MAY_UNLINK: 186762306a36Sopenharmony_ci av = FILE__UNLINK; 186862306a36Sopenharmony_ci break; 186962306a36Sopenharmony_ci case MAY_RMDIR: 187062306a36Sopenharmony_ci av = DIR__RMDIR; 187162306a36Sopenharmony_ci break; 187262306a36Sopenharmony_ci default: 187362306a36Sopenharmony_ci pr_warn("SELinux: %s: unrecognized kind %d\n", 187462306a36Sopenharmony_ci __func__, kind); 187562306a36Sopenharmony_ci return 0; 187662306a36Sopenharmony_ci } 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci rc = avc_has_perm(sid, isec->sid, isec->sclass, av, &ad); 187962306a36Sopenharmony_ci return rc; 188062306a36Sopenharmony_ci} 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic inline int may_rename(struct inode *old_dir, 188362306a36Sopenharmony_ci struct dentry *old_dentry, 188462306a36Sopenharmony_ci struct inode *new_dir, 188562306a36Sopenharmony_ci struct dentry *new_dentry) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec; 188862306a36Sopenharmony_ci struct common_audit_data ad; 188962306a36Sopenharmony_ci u32 sid = current_sid(); 189062306a36Sopenharmony_ci u32 av; 189162306a36Sopenharmony_ci int old_is_dir, new_is_dir; 189262306a36Sopenharmony_ci int rc; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci old_dsec = inode_security(old_dir); 189562306a36Sopenharmony_ci old_isec = backing_inode_security(old_dentry); 189662306a36Sopenharmony_ci old_is_dir = d_is_dir(old_dentry); 189762306a36Sopenharmony_ci new_dsec = inode_security(new_dir); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci ad.u.dentry = old_dentry; 190262306a36Sopenharmony_ci rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, 190362306a36Sopenharmony_ci DIR__REMOVE_NAME | DIR__SEARCH, &ad); 190462306a36Sopenharmony_ci if (rc) 190562306a36Sopenharmony_ci return rc; 190662306a36Sopenharmony_ci rc = avc_has_perm(sid, old_isec->sid, 190762306a36Sopenharmony_ci old_isec->sclass, FILE__RENAME, &ad); 190862306a36Sopenharmony_ci if (rc) 190962306a36Sopenharmony_ci return rc; 191062306a36Sopenharmony_ci if (old_is_dir && new_dir != old_dir) { 191162306a36Sopenharmony_ci rc = avc_has_perm(sid, old_isec->sid, 191262306a36Sopenharmony_ci old_isec->sclass, DIR__REPARENT, &ad); 191362306a36Sopenharmony_ci if (rc) 191462306a36Sopenharmony_ci return rc; 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci ad.u.dentry = new_dentry; 191862306a36Sopenharmony_ci av = DIR__ADD_NAME | DIR__SEARCH; 191962306a36Sopenharmony_ci if (d_is_positive(new_dentry)) 192062306a36Sopenharmony_ci av |= DIR__REMOVE_NAME; 192162306a36Sopenharmony_ci rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad); 192262306a36Sopenharmony_ci if (rc) 192362306a36Sopenharmony_ci return rc; 192462306a36Sopenharmony_ci if (d_is_positive(new_dentry)) { 192562306a36Sopenharmony_ci new_isec = backing_inode_security(new_dentry); 192662306a36Sopenharmony_ci new_is_dir = d_is_dir(new_dentry); 192762306a36Sopenharmony_ci rc = avc_has_perm(sid, new_isec->sid, 192862306a36Sopenharmony_ci new_isec->sclass, 192962306a36Sopenharmony_ci (new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad); 193062306a36Sopenharmony_ci if (rc) 193162306a36Sopenharmony_ci return rc; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci return 0; 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci/* Check whether a task can perform a filesystem operation. */ 193862306a36Sopenharmony_cistatic int superblock_has_perm(const struct cred *cred, 193962306a36Sopenharmony_ci struct super_block *sb, 194062306a36Sopenharmony_ci u32 perms, 194162306a36Sopenharmony_ci struct common_audit_data *ad) 194262306a36Sopenharmony_ci{ 194362306a36Sopenharmony_ci struct superblock_security_struct *sbsec; 194462306a36Sopenharmony_ci u32 sid = cred_sid(cred); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci sbsec = selinux_superblock(sb); 194762306a36Sopenharmony_ci return avc_has_perm(sid, sbsec->sid, SECCLASS_FILESYSTEM, perms, ad); 194862306a36Sopenharmony_ci} 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci/* Convert a Linux mode and permission mask to an access vector. */ 195162306a36Sopenharmony_cistatic inline u32 file_mask_to_av(int mode, int mask) 195262306a36Sopenharmony_ci{ 195362306a36Sopenharmony_ci u32 av = 0; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (!S_ISDIR(mode)) { 195662306a36Sopenharmony_ci if (mask & MAY_EXEC) 195762306a36Sopenharmony_ci av |= FILE__EXECUTE; 195862306a36Sopenharmony_ci if (mask & MAY_READ) 195962306a36Sopenharmony_ci av |= FILE__READ; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci if (mask & MAY_APPEND) 196262306a36Sopenharmony_ci av |= FILE__APPEND; 196362306a36Sopenharmony_ci else if (mask & MAY_WRITE) 196462306a36Sopenharmony_ci av |= FILE__WRITE; 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci } else { 196762306a36Sopenharmony_ci if (mask & MAY_EXEC) 196862306a36Sopenharmony_ci av |= DIR__SEARCH; 196962306a36Sopenharmony_ci if (mask & MAY_WRITE) 197062306a36Sopenharmony_ci av |= DIR__WRITE; 197162306a36Sopenharmony_ci if (mask & MAY_READ) 197262306a36Sopenharmony_ci av |= DIR__READ; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci return av; 197662306a36Sopenharmony_ci} 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci/* Convert a Linux file to an access vector. */ 197962306a36Sopenharmony_cistatic inline u32 file_to_av(const struct file *file) 198062306a36Sopenharmony_ci{ 198162306a36Sopenharmony_ci u32 av = 0; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci if (file->f_mode & FMODE_READ) 198462306a36Sopenharmony_ci av |= FILE__READ; 198562306a36Sopenharmony_ci if (file->f_mode & FMODE_WRITE) { 198662306a36Sopenharmony_ci if (file->f_flags & O_APPEND) 198762306a36Sopenharmony_ci av |= FILE__APPEND; 198862306a36Sopenharmony_ci else 198962306a36Sopenharmony_ci av |= FILE__WRITE; 199062306a36Sopenharmony_ci } 199162306a36Sopenharmony_ci if (!av) { 199262306a36Sopenharmony_ci /* 199362306a36Sopenharmony_ci * Special file opened with flags 3 for ioctl-only use. 199462306a36Sopenharmony_ci */ 199562306a36Sopenharmony_ci av = FILE__IOCTL; 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci return av; 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci/* 200262306a36Sopenharmony_ci * Convert a file to an access vector and include the correct 200362306a36Sopenharmony_ci * open permission. 200462306a36Sopenharmony_ci */ 200562306a36Sopenharmony_cistatic inline u32 open_file_to_av(struct file *file) 200662306a36Sopenharmony_ci{ 200762306a36Sopenharmony_ci u32 av = file_to_av(file); 200862306a36Sopenharmony_ci struct inode *inode = file_inode(file); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci if (selinux_policycap_openperm() && 201162306a36Sopenharmony_ci inode->i_sb->s_magic != SOCKFS_MAGIC) 201262306a36Sopenharmony_ci av |= FILE__OPEN; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci return av; 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci/* Hook functions begin here. */ 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_cistatic int selinux_binder_set_context_mgr(const struct cred *mgr) 202062306a36Sopenharmony_ci{ 202162306a36Sopenharmony_ci return avc_has_perm(current_sid(), cred_sid(mgr), SECCLASS_BINDER, 202262306a36Sopenharmony_ci BINDER__SET_CONTEXT_MGR, NULL); 202362306a36Sopenharmony_ci} 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cistatic int selinux_binder_transaction(const struct cred *from, 202662306a36Sopenharmony_ci const struct cred *to) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci u32 mysid = current_sid(); 202962306a36Sopenharmony_ci u32 fromsid = cred_sid(from); 203062306a36Sopenharmony_ci u32 tosid = cred_sid(to); 203162306a36Sopenharmony_ci int rc; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci if (mysid != fromsid) { 203462306a36Sopenharmony_ci rc = avc_has_perm(mysid, fromsid, SECCLASS_BINDER, 203562306a36Sopenharmony_ci BINDER__IMPERSONATE, NULL); 203662306a36Sopenharmony_ci if (rc) 203762306a36Sopenharmony_ci return rc; 203862306a36Sopenharmony_ci } 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci return avc_has_perm(fromsid, tosid, 204162306a36Sopenharmony_ci SECCLASS_BINDER, BINDER__CALL, NULL); 204262306a36Sopenharmony_ci} 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_cistatic int selinux_binder_transfer_binder(const struct cred *from, 204562306a36Sopenharmony_ci const struct cred *to) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci return avc_has_perm(cred_sid(from), cred_sid(to), 204862306a36Sopenharmony_ci SECCLASS_BINDER, BINDER__TRANSFER, 204962306a36Sopenharmony_ci NULL); 205062306a36Sopenharmony_ci} 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_cistatic int selinux_binder_transfer_file(const struct cred *from, 205362306a36Sopenharmony_ci const struct cred *to, 205462306a36Sopenharmony_ci const struct file *file) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci u32 sid = cred_sid(to); 205762306a36Sopenharmony_ci struct file_security_struct *fsec = selinux_file(file); 205862306a36Sopenharmony_ci struct dentry *dentry = file->f_path.dentry; 205962306a36Sopenharmony_ci struct inode_security_struct *isec; 206062306a36Sopenharmony_ci struct common_audit_data ad; 206162306a36Sopenharmony_ci int rc; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_PATH; 206462306a36Sopenharmony_ci ad.u.path = file->f_path; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci if (sid != fsec->sid) { 206762306a36Sopenharmony_ci rc = avc_has_perm(sid, fsec->sid, 206862306a36Sopenharmony_ci SECCLASS_FD, 206962306a36Sopenharmony_ci FD__USE, 207062306a36Sopenharmony_ci &ad); 207162306a36Sopenharmony_ci if (rc) 207262306a36Sopenharmony_ci return rc; 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 207662306a36Sopenharmony_ci rc = bpf_fd_pass(file, sid); 207762306a36Sopenharmony_ci if (rc) 207862306a36Sopenharmony_ci return rc; 207962306a36Sopenharmony_ci#endif 208062306a36Sopenharmony_ci 208162306a36Sopenharmony_ci if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) 208262306a36Sopenharmony_ci return 0; 208362306a36Sopenharmony_ci 208462306a36Sopenharmony_ci isec = backing_inode_security(dentry); 208562306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file), 208662306a36Sopenharmony_ci &ad); 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cistatic int selinux_ptrace_access_check(struct task_struct *child, 209062306a36Sopenharmony_ci unsigned int mode) 209162306a36Sopenharmony_ci{ 209262306a36Sopenharmony_ci u32 sid = current_sid(); 209362306a36Sopenharmony_ci u32 csid = task_sid_obj(child); 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci if (mode & PTRACE_MODE_READ) 209662306a36Sopenharmony_ci return avc_has_perm(sid, csid, SECCLASS_FILE, FILE__READ, 209762306a36Sopenharmony_ci NULL); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci return avc_has_perm(sid, csid, SECCLASS_PROCESS, PROCESS__PTRACE, 210062306a36Sopenharmony_ci NULL); 210162306a36Sopenharmony_ci} 210262306a36Sopenharmony_ci 210362306a36Sopenharmony_cistatic int selinux_ptrace_traceme(struct task_struct *parent) 210462306a36Sopenharmony_ci{ 210562306a36Sopenharmony_ci return avc_has_perm(task_sid_obj(parent), task_sid_obj(current), 210662306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__PTRACE, NULL); 210762306a36Sopenharmony_ci} 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_cistatic int selinux_capget(const struct task_struct *target, kernel_cap_t *effective, 211062306a36Sopenharmony_ci kernel_cap_t *inheritable, kernel_cap_t *permitted) 211162306a36Sopenharmony_ci{ 211262306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(target), 211362306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__GETCAP, NULL); 211462306a36Sopenharmony_ci} 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_cistatic int selinux_capset(struct cred *new, const struct cred *old, 211762306a36Sopenharmony_ci const kernel_cap_t *effective, 211862306a36Sopenharmony_ci const kernel_cap_t *inheritable, 211962306a36Sopenharmony_ci const kernel_cap_t *permitted) 212062306a36Sopenharmony_ci{ 212162306a36Sopenharmony_ci return avc_has_perm(cred_sid(old), cred_sid(new), SECCLASS_PROCESS, 212262306a36Sopenharmony_ci PROCESS__SETCAP, NULL); 212362306a36Sopenharmony_ci} 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci/* 212662306a36Sopenharmony_ci * (This comment used to live with the selinux_task_setuid hook, 212762306a36Sopenharmony_ci * which was removed). 212862306a36Sopenharmony_ci * 212962306a36Sopenharmony_ci * Since setuid only affects the current process, and since the SELinux 213062306a36Sopenharmony_ci * controls are not based on the Linux identity attributes, SELinux does not 213162306a36Sopenharmony_ci * need to control this operation. However, SELinux does control the use of 213262306a36Sopenharmony_ci * the CAP_SETUID and CAP_SETGID capabilities using the capable hook. 213362306a36Sopenharmony_ci */ 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_cistatic int selinux_capable(const struct cred *cred, struct user_namespace *ns, 213662306a36Sopenharmony_ci int cap, unsigned int opts) 213762306a36Sopenharmony_ci{ 213862306a36Sopenharmony_ci return cred_has_capability(cred, cap, opts, ns == &init_user_ns); 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) 214262306a36Sopenharmony_ci{ 214362306a36Sopenharmony_ci const struct cred *cred = current_cred(); 214462306a36Sopenharmony_ci int rc = 0; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (!sb) 214762306a36Sopenharmony_ci return 0; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci switch (cmds) { 215062306a36Sopenharmony_ci case Q_SYNC: 215162306a36Sopenharmony_ci case Q_QUOTAON: 215262306a36Sopenharmony_ci case Q_QUOTAOFF: 215362306a36Sopenharmony_ci case Q_SETINFO: 215462306a36Sopenharmony_ci case Q_SETQUOTA: 215562306a36Sopenharmony_ci case Q_XQUOTAOFF: 215662306a36Sopenharmony_ci case Q_XQUOTAON: 215762306a36Sopenharmony_ci case Q_XSETQLIM: 215862306a36Sopenharmony_ci rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAMOD, NULL); 215962306a36Sopenharmony_ci break; 216062306a36Sopenharmony_ci case Q_GETFMT: 216162306a36Sopenharmony_ci case Q_GETINFO: 216262306a36Sopenharmony_ci case Q_GETQUOTA: 216362306a36Sopenharmony_ci case Q_XGETQUOTA: 216462306a36Sopenharmony_ci case Q_XGETQSTAT: 216562306a36Sopenharmony_ci case Q_XGETQSTATV: 216662306a36Sopenharmony_ci case Q_XGETNEXTQUOTA: 216762306a36Sopenharmony_ci rc = superblock_has_perm(cred, sb, FILESYSTEM__QUOTAGET, NULL); 216862306a36Sopenharmony_ci break; 216962306a36Sopenharmony_ci default: 217062306a36Sopenharmony_ci rc = 0; /* let the kernel handle invalid cmds */ 217162306a36Sopenharmony_ci break; 217262306a36Sopenharmony_ci } 217362306a36Sopenharmony_ci return rc; 217462306a36Sopenharmony_ci} 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_cistatic int selinux_quota_on(struct dentry *dentry) 217762306a36Sopenharmony_ci{ 217862306a36Sopenharmony_ci const struct cred *cred = current_cred(); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci return dentry_has_perm(cred, dentry, FILE__QUOTAON); 218162306a36Sopenharmony_ci} 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_cistatic int selinux_syslog(int type) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci switch (type) { 218662306a36Sopenharmony_ci case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */ 218762306a36Sopenharmony_ci case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */ 218862306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, 218962306a36Sopenharmony_ci SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, NULL); 219062306a36Sopenharmony_ci case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */ 219162306a36Sopenharmony_ci case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */ 219262306a36Sopenharmony_ci /* Set level of messages printed to console */ 219362306a36Sopenharmony_ci case SYSLOG_ACTION_CONSOLE_LEVEL: 219462306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, 219562306a36Sopenharmony_ci SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, 219662306a36Sopenharmony_ci NULL); 219762306a36Sopenharmony_ci } 219862306a36Sopenharmony_ci /* All other syslog types */ 219962306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, 220062306a36Sopenharmony_ci SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, NULL); 220162306a36Sopenharmony_ci} 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci/* 220462306a36Sopenharmony_ci * Check that a process has enough memory to allocate a new virtual 220562306a36Sopenharmony_ci * mapping. 0 means there is enough memory for the allocation to 220662306a36Sopenharmony_ci * succeed and -ENOMEM implies there is not. 220762306a36Sopenharmony_ci * 220862306a36Sopenharmony_ci * Do not audit the selinux permission check, as this is applied to all 220962306a36Sopenharmony_ci * processes that allocate mappings. 221062306a36Sopenharmony_ci */ 221162306a36Sopenharmony_cistatic int selinux_vm_enough_memory(struct mm_struct *mm, long pages) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci int rc, cap_sys_admin = 0; 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN, 221662306a36Sopenharmony_ci CAP_OPT_NOAUDIT, true); 221762306a36Sopenharmony_ci if (rc == 0) 221862306a36Sopenharmony_ci cap_sys_admin = 1; 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci return cap_sys_admin; 222162306a36Sopenharmony_ci} 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci/* binprm security operations */ 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_cistatic u32 ptrace_parent_sid(void) 222662306a36Sopenharmony_ci{ 222762306a36Sopenharmony_ci u32 sid = 0; 222862306a36Sopenharmony_ci struct task_struct *tracer; 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci rcu_read_lock(); 223162306a36Sopenharmony_ci tracer = ptrace_parent(current); 223262306a36Sopenharmony_ci if (tracer) 223362306a36Sopenharmony_ci sid = task_sid_obj(tracer); 223462306a36Sopenharmony_ci rcu_read_unlock(); 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci return sid; 223762306a36Sopenharmony_ci} 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_cistatic int check_nnp_nosuid(const struct linux_binprm *bprm, 224062306a36Sopenharmony_ci const struct task_security_struct *old_tsec, 224162306a36Sopenharmony_ci const struct task_security_struct *new_tsec) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); 224462306a36Sopenharmony_ci int nosuid = !mnt_may_suid(bprm->file->f_path.mnt); 224562306a36Sopenharmony_ci int rc; 224662306a36Sopenharmony_ci u32 av; 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci if (!nnp && !nosuid) 224962306a36Sopenharmony_ci return 0; /* neither NNP nor nosuid */ 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (new_tsec->sid == old_tsec->sid) 225262306a36Sopenharmony_ci return 0; /* No change in credentials */ 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci /* 225562306a36Sopenharmony_ci * If the policy enables the nnp_nosuid_transition policy capability, 225662306a36Sopenharmony_ci * then we permit transitions under NNP or nosuid if the 225762306a36Sopenharmony_ci * policy allows the corresponding permission between 225862306a36Sopenharmony_ci * the old and new contexts. 225962306a36Sopenharmony_ci */ 226062306a36Sopenharmony_ci if (selinux_policycap_nnp_nosuid_transition()) { 226162306a36Sopenharmony_ci av = 0; 226262306a36Sopenharmony_ci if (nnp) 226362306a36Sopenharmony_ci av |= PROCESS2__NNP_TRANSITION; 226462306a36Sopenharmony_ci if (nosuid) 226562306a36Sopenharmony_ci av |= PROCESS2__NOSUID_TRANSITION; 226662306a36Sopenharmony_ci rc = avc_has_perm(old_tsec->sid, new_tsec->sid, 226762306a36Sopenharmony_ci SECCLASS_PROCESS2, av, NULL); 226862306a36Sopenharmony_ci if (!rc) 226962306a36Sopenharmony_ci return 0; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci /* 227362306a36Sopenharmony_ci * We also permit NNP or nosuid transitions to bounded SIDs, 227462306a36Sopenharmony_ci * i.e. SIDs that are guaranteed to only be allowed a subset 227562306a36Sopenharmony_ci * of the permissions of the current SID. 227662306a36Sopenharmony_ci */ 227762306a36Sopenharmony_ci rc = security_bounded_transition(old_tsec->sid, 227862306a36Sopenharmony_ci new_tsec->sid); 227962306a36Sopenharmony_ci if (!rc) 228062306a36Sopenharmony_ci return 0; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci /* 228362306a36Sopenharmony_ci * On failure, preserve the errno values for NNP vs nosuid. 228462306a36Sopenharmony_ci * NNP: Operation not permitted for caller. 228562306a36Sopenharmony_ci * nosuid: Permission denied to file. 228662306a36Sopenharmony_ci */ 228762306a36Sopenharmony_ci if (nnp) 228862306a36Sopenharmony_ci return -EPERM; 228962306a36Sopenharmony_ci return -EACCES; 229062306a36Sopenharmony_ci} 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_cistatic int selinux_bprm_creds_for_exec(struct linux_binprm *bprm) 229362306a36Sopenharmony_ci{ 229462306a36Sopenharmony_ci const struct task_security_struct *old_tsec; 229562306a36Sopenharmony_ci struct task_security_struct *new_tsec; 229662306a36Sopenharmony_ci struct inode_security_struct *isec; 229762306a36Sopenharmony_ci struct common_audit_data ad; 229862306a36Sopenharmony_ci struct inode *inode = file_inode(bprm->file); 229962306a36Sopenharmony_ci int rc; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* SELinux context only depends on initial program or script and not 230262306a36Sopenharmony_ci * the script interpreter */ 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci old_tsec = selinux_cred(current_cred()); 230562306a36Sopenharmony_ci new_tsec = selinux_cred(bprm->cred); 230662306a36Sopenharmony_ci isec = inode_security(inode); 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci /* Default to the current task SID. */ 230962306a36Sopenharmony_ci new_tsec->sid = old_tsec->sid; 231062306a36Sopenharmony_ci new_tsec->osid = old_tsec->sid; 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci /* Reset fs, key, and sock SIDs on execve. */ 231362306a36Sopenharmony_ci new_tsec->create_sid = 0; 231462306a36Sopenharmony_ci new_tsec->keycreate_sid = 0; 231562306a36Sopenharmony_ci new_tsec->sockcreate_sid = 0; 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci if (old_tsec->exec_sid) { 231862306a36Sopenharmony_ci new_tsec->sid = old_tsec->exec_sid; 231962306a36Sopenharmony_ci /* Reset exec SID on execve. */ 232062306a36Sopenharmony_ci new_tsec->exec_sid = 0; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci /* Fail on NNP or nosuid if not an allowed transition. */ 232362306a36Sopenharmony_ci rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); 232462306a36Sopenharmony_ci if (rc) 232562306a36Sopenharmony_ci return rc; 232662306a36Sopenharmony_ci } else { 232762306a36Sopenharmony_ci /* Check for a default transition on this program. */ 232862306a36Sopenharmony_ci rc = security_transition_sid(old_tsec->sid, 232962306a36Sopenharmony_ci isec->sid, SECCLASS_PROCESS, NULL, 233062306a36Sopenharmony_ci &new_tsec->sid); 233162306a36Sopenharmony_ci if (rc) 233262306a36Sopenharmony_ci return rc; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci /* 233562306a36Sopenharmony_ci * Fallback to old SID on NNP or nosuid if not an allowed 233662306a36Sopenharmony_ci * transition. 233762306a36Sopenharmony_ci */ 233862306a36Sopenharmony_ci rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); 233962306a36Sopenharmony_ci if (rc) 234062306a36Sopenharmony_ci new_tsec->sid = old_tsec->sid; 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_FILE; 234462306a36Sopenharmony_ci ad.u.file = bprm->file; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci if (new_tsec->sid == old_tsec->sid) { 234762306a36Sopenharmony_ci rc = avc_has_perm(old_tsec->sid, isec->sid, 234862306a36Sopenharmony_ci SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); 234962306a36Sopenharmony_ci if (rc) 235062306a36Sopenharmony_ci return rc; 235162306a36Sopenharmony_ci } else { 235262306a36Sopenharmony_ci /* Check permissions for the transition. */ 235362306a36Sopenharmony_ci rc = avc_has_perm(old_tsec->sid, new_tsec->sid, 235462306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__TRANSITION, &ad); 235562306a36Sopenharmony_ci if (rc) 235662306a36Sopenharmony_ci return rc; 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_ci rc = avc_has_perm(new_tsec->sid, isec->sid, 235962306a36Sopenharmony_ci SECCLASS_FILE, FILE__ENTRYPOINT, &ad); 236062306a36Sopenharmony_ci if (rc) 236162306a36Sopenharmony_ci return rc; 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci /* Check for shared state */ 236462306a36Sopenharmony_ci if (bprm->unsafe & LSM_UNSAFE_SHARE) { 236562306a36Sopenharmony_ci rc = avc_has_perm(old_tsec->sid, new_tsec->sid, 236662306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__SHARE, 236762306a36Sopenharmony_ci NULL); 236862306a36Sopenharmony_ci if (rc) 236962306a36Sopenharmony_ci return -EPERM; 237062306a36Sopenharmony_ci } 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci /* Make sure that anyone attempting to ptrace over a task that 237362306a36Sopenharmony_ci * changes its SID has the appropriate permit */ 237462306a36Sopenharmony_ci if (bprm->unsafe & LSM_UNSAFE_PTRACE) { 237562306a36Sopenharmony_ci u32 ptsid = ptrace_parent_sid(); 237662306a36Sopenharmony_ci if (ptsid != 0) { 237762306a36Sopenharmony_ci rc = avc_has_perm(ptsid, new_tsec->sid, 237862306a36Sopenharmony_ci SECCLASS_PROCESS, 237962306a36Sopenharmony_ci PROCESS__PTRACE, NULL); 238062306a36Sopenharmony_ci if (rc) 238162306a36Sopenharmony_ci return -EPERM; 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci } 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci /* Clear any possibly unsafe personality bits on exec: */ 238662306a36Sopenharmony_ci bprm->per_clear |= PER_CLEAR_ON_SETID; 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci /* Enable secure mode for SIDs transitions unless 238962306a36Sopenharmony_ci the noatsecure permission is granted between 239062306a36Sopenharmony_ci the two SIDs, i.e. ahp returns 0. */ 239162306a36Sopenharmony_ci rc = avc_has_perm(old_tsec->sid, new_tsec->sid, 239262306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__NOATSECURE, 239362306a36Sopenharmony_ci NULL); 239462306a36Sopenharmony_ci bprm->secureexec |= !!rc; 239562306a36Sopenharmony_ci } 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci return 0; 239862306a36Sopenharmony_ci} 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_cistatic int match_file(const void *p, struct file *file, unsigned fd) 240162306a36Sopenharmony_ci{ 240262306a36Sopenharmony_ci return file_has_perm(p, file, file_to_av(file)) ? fd + 1 : 0; 240362306a36Sopenharmony_ci} 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci/* Derived from fs/exec.c:flush_old_files. */ 240662306a36Sopenharmony_cistatic inline void flush_unauthorized_files(const struct cred *cred, 240762306a36Sopenharmony_ci struct files_struct *files) 240862306a36Sopenharmony_ci{ 240962306a36Sopenharmony_ci struct file *file, *devnull = NULL; 241062306a36Sopenharmony_ci struct tty_struct *tty; 241162306a36Sopenharmony_ci int drop_tty = 0; 241262306a36Sopenharmony_ci unsigned n; 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci tty = get_current_tty(); 241562306a36Sopenharmony_ci if (tty) { 241662306a36Sopenharmony_ci spin_lock(&tty->files_lock); 241762306a36Sopenharmony_ci if (!list_empty(&tty->tty_files)) { 241862306a36Sopenharmony_ci struct tty_file_private *file_priv; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci /* Revalidate access to controlling tty. 242162306a36Sopenharmony_ci Use file_path_has_perm on the tty path directly 242262306a36Sopenharmony_ci rather than using file_has_perm, as this particular 242362306a36Sopenharmony_ci open file may belong to another process and we are 242462306a36Sopenharmony_ci only interested in the inode-based check here. */ 242562306a36Sopenharmony_ci file_priv = list_first_entry(&tty->tty_files, 242662306a36Sopenharmony_ci struct tty_file_private, list); 242762306a36Sopenharmony_ci file = file_priv->file; 242862306a36Sopenharmony_ci if (file_path_has_perm(cred, file, FILE__READ | FILE__WRITE)) 242962306a36Sopenharmony_ci drop_tty = 1; 243062306a36Sopenharmony_ci } 243162306a36Sopenharmony_ci spin_unlock(&tty->files_lock); 243262306a36Sopenharmony_ci tty_kref_put(tty); 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci /* Reset controlling tty. */ 243562306a36Sopenharmony_ci if (drop_tty) 243662306a36Sopenharmony_ci no_tty(); 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci /* Revalidate access to inherited open files. */ 243962306a36Sopenharmony_ci n = iterate_fd(files, 0, match_file, cred); 244062306a36Sopenharmony_ci if (!n) /* none found? */ 244162306a36Sopenharmony_ci return; 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci devnull = dentry_open(&selinux_null, O_RDWR, cred); 244462306a36Sopenharmony_ci if (IS_ERR(devnull)) 244562306a36Sopenharmony_ci devnull = NULL; 244662306a36Sopenharmony_ci /* replace all the matching ones with this */ 244762306a36Sopenharmony_ci do { 244862306a36Sopenharmony_ci replace_fd(n - 1, devnull, 0); 244962306a36Sopenharmony_ci } while ((n = iterate_fd(files, n, match_file, cred)) != 0); 245062306a36Sopenharmony_ci if (devnull) 245162306a36Sopenharmony_ci fput(devnull); 245262306a36Sopenharmony_ci} 245362306a36Sopenharmony_ci 245462306a36Sopenharmony_ci/* 245562306a36Sopenharmony_ci * Prepare a process for imminent new credential changes due to exec 245662306a36Sopenharmony_ci */ 245762306a36Sopenharmony_cistatic void selinux_bprm_committing_creds(struct linux_binprm *bprm) 245862306a36Sopenharmony_ci{ 245962306a36Sopenharmony_ci struct task_security_struct *new_tsec; 246062306a36Sopenharmony_ci struct rlimit *rlim, *initrlim; 246162306a36Sopenharmony_ci int rc, i; 246262306a36Sopenharmony_ci 246362306a36Sopenharmony_ci new_tsec = selinux_cred(bprm->cred); 246462306a36Sopenharmony_ci if (new_tsec->sid == new_tsec->osid) 246562306a36Sopenharmony_ci return; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci /* Close files for which the new task SID is not authorized. */ 246862306a36Sopenharmony_ci flush_unauthorized_files(bprm->cred, current->files); 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci /* Always clear parent death signal on SID transitions. */ 247162306a36Sopenharmony_ci current->pdeath_signal = 0; 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci /* Check whether the new SID can inherit resource limits from the old 247462306a36Sopenharmony_ci * SID. If not, reset all soft limits to the lower of the current 247562306a36Sopenharmony_ci * task's hard limit and the init task's soft limit. 247662306a36Sopenharmony_ci * 247762306a36Sopenharmony_ci * Note that the setting of hard limits (even to lower them) can be 247862306a36Sopenharmony_ci * controlled by the setrlimit check. The inclusion of the init task's 247962306a36Sopenharmony_ci * soft limit into the computation is to avoid resetting soft limits 248062306a36Sopenharmony_ci * higher than the default soft limit for cases where the default is 248162306a36Sopenharmony_ci * lower than the hard limit, e.g. RLIMIT_CORE or RLIMIT_STACK. 248262306a36Sopenharmony_ci */ 248362306a36Sopenharmony_ci rc = avc_has_perm(new_tsec->osid, new_tsec->sid, SECCLASS_PROCESS, 248462306a36Sopenharmony_ci PROCESS__RLIMITINH, NULL); 248562306a36Sopenharmony_ci if (rc) { 248662306a36Sopenharmony_ci /* protect against do_prlimit() */ 248762306a36Sopenharmony_ci task_lock(current); 248862306a36Sopenharmony_ci for (i = 0; i < RLIM_NLIMITS; i++) { 248962306a36Sopenharmony_ci rlim = current->signal->rlim + i; 249062306a36Sopenharmony_ci initrlim = init_task.signal->rlim + i; 249162306a36Sopenharmony_ci rlim->rlim_cur = min(rlim->rlim_max, initrlim->rlim_cur); 249262306a36Sopenharmony_ci } 249362306a36Sopenharmony_ci task_unlock(current); 249462306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_POSIX_TIMERS)) 249562306a36Sopenharmony_ci update_rlimit_cpu(current, rlimit(RLIMIT_CPU)); 249662306a36Sopenharmony_ci } 249762306a36Sopenharmony_ci} 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci/* 250062306a36Sopenharmony_ci * Clean up the process immediately after the installation of new credentials 250162306a36Sopenharmony_ci * due to exec 250262306a36Sopenharmony_ci */ 250362306a36Sopenharmony_cistatic void selinux_bprm_committed_creds(struct linux_binprm *bprm) 250462306a36Sopenharmony_ci{ 250562306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 250662306a36Sopenharmony_ci u32 osid, sid; 250762306a36Sopenharmony_ci int rc; 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ci osid = tsec->osid; 251062306a36Sopenharmony_ci sid = tsec->sid; 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci if (sid == osid) 251362306a36Sopenharmony_ci return; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci /* Check whether the new SID can inherit signal state from the old SID. 251662306a36Sopenharmony_ci * If not, clear itimers to avoid subsequent signal generation and 251762306a36Sopenharmony_ci * flush and unblock signals. 251862306a36Sopenharmony_ci * 251962306a36Sopenharmony_ci * This must occur _after_ the task SID has been updated so that any 252062306a36Sopenharmony_ci * kill done after the flush will be checked against the new SID. 252162306a36Sopenharmony_ci */ 252262306a36Sopenharmony_ci rc = avc_has_perm(osid, sid, SECCLASS_PROCESS, PROCESS__SIGINH, NULL); 252362306a36Sopenharmony_ci if (rc) { 252462306a36Sopenharmony_ci clear_itimer(); 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci spin_lock_irq(&unrcu_pointer(current->sighand)->siglock); 252762306a36Sopenharmony_ci if (!fatal_signal_pending(current)) { 252862306a36Sopenharmony_ci flush_sigqueue(¤t->pending); 252962306a36Sopenharmony_ci flush_sigqueue(¤t->signal->shared_pending); 253062306a36Sopenharmony_ci flush_signal_handlers(current, 1); 253162306a36Sopenharmony_ci sigemptyset(¤t->blocked); 253262306a36Sopenharmony_ci recalc_sigpending(); 253362306a36Sopenharmony_ci } 253462306a36Sopenharmony_ci spin_unlock_irq(&unrcu_pointer(current->sighand)->siglock); 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci /* Wake up the parent if it is waiting so that it can recheck 253862306a36Sopenharmony_ci * wait permission to the new task SID. */ 253962306a36Sopenharmony_ci read_lock(&tasklist_lock); 254062306a36Sopenharmony_ci __wake_up_parent(current, unrcu_pointer(current->real_parent)); 254162306a36Sopenharmony_ci read_unlock(&tasklist_lock); 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci/* superblock security operations */ 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_cistatic int selinux_sb_alloc_security(struct super_block *sb) 254762306a36Sopenharmony_ci{ 254862306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci mutex_init(&sbsec->lock); 255162306a36Sopenharmony_ci INIT_LIST_HEAD(&sbsec->isec_head); 255262306a36Sopenharmony_ci spin_lock_init(&sbsec->isec_lock); 255362306a36Sopenharmony_ci sbsec->sid = SECINITSID_UNLABELED; 255462306a36Sopenharmony_ci sbsec->def_sid = SECINITSID_FILE; 255562306a36Sopenharmony_ci sbsec->mntpoint_sid = SECINITSID_UNLABELED; 255662306a36Sopenharmony_ci 255762306a36Sopenharmony_ci return 0; 255862306a36Sopenharmony_ci} 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_cistatic inline int opt_len(const char *s) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci bool open_quote = false; 256362306a36Sopenharmony_ci int len; 256462306a36Sopenharmony_ci char c; 256562306a36Sopenharmony_ci 256662306a36Sopenharmony_ci for (len = 0; (c = s[len]) != '\0'; len++) { 256762306a36Sopenharmony_ci if (c == '"') 256862306a36Sopenharmony_ci open_quote = !open_quote; 256962306a36Sopenharmony_ci if (c == ',' && !open_quote) 257062306a36Sopenharmony_ci break; 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci return len; 257362306a36Sopenharmony_ci} 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_cistatic int selinux_sb_eat_lsm_opts(char *options, void **mnt_opts) 257662306a36Sopenharmony_ci{ 257762306a36Sopenharmony_ci char *from = options; 257862306a36Sopenharmony_ci char *to = options; 257962306a36Sopenharmony_ci bool first = true; 258062306a36Sopenharmony_ci int rc; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci while (1) { 258362306a36Sopenharmony_ci int len = opt_len(from); 258462306a36Sopenharmony_ci int token; 258562306a36Sopenharmony_ci char *arg = NULL; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci token = match_opt_prefix(from, len, &arg); 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci if (token != Opt_error) { 259062306a36Sopenharmony_ci char *p, *q; 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci /* strip quotes */ 259362306a36Sopenharmony_ci if (arg) { 259462306a36Sopenharmony_ci for (p = q = arg; p < from + len; p++) { 259562306a36Sopenharmony_ci char c = *p; 259662306a36Sopenharmony_ci if (c != '"') 259762306a36Sopenharmony_ci *q++ = c; 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci arg = kmemdup_nul(arg, q - arg, GFP_KERNEL); 260062306a36Sopenharmony_ci if (!arg) { 260162306a36Sopenharmony_ci rc = -ENOMEM; 260262306a36Sopenharmony_ci goto free_opt; 260362306a36Sopenharmony_ci } 260462306a36Sopenharmony_ci } 260562306a36Sopenharmony_ci rc = selinux_add_opt(token, arg, mnt_opts); 260662306a36Sopenharmony_ci kfree(arg); 260762306a36Sopenharmony_ci arg = NULL; 260862306a36Sopenharmony_ci if (unlikely(rc)) { 260962306a36Sopenharmony_ci goto free_opt; 261062306a36Sopenharmony_ci } 261162306a36Sopenharmony_ci } else { 261262306a36Sopenharmony_ci if (!first) { // copy with preceding comma 261362306a36Sopenharmony_ci from--; 261462306a36Sopenharmony_ci len++; 261562306a36Sopenharmony_ci } 261662306a36Sopenharmony_ci if (to != from) 261762306a36Sopenharmony_ci memmove(to, from, len); 261862306a36Sopenharmony_ci to += len; 261962306a36Sopenharmony_ci first = false; 262062306a36Sopenharmony_ci } 262162306a36Sopenharmony_ci if (!from[len]) 262262306a36Sopenharmony_ci break; 262362306a36Sopenharmony_ci from += len + 1; 262462306a36Sopenharmony_ci } 262562306a36Sopenharmony_ci *to = '\0'; 262662306a36Sopenharmony_ci return 0; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_cifree_opt: 262962306a36Sopenharmony_ci if (*mnt_opts) { 263062306a36Sopenharmony_ci selinux_free_mnt_opts(*mnt_opts); 263162306a36Sopenharmony_ci *mnt_opts = NULL; 263262306a36Sopenharmony_ci } 263362306a36Sopenharmony_ci return rc; 263462306a36Sopenharmony_ci} 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_cistatic int selinux_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts) 263762306a36Sopenharmony_ci{ 263862306a36Sopenharmony_ci struct selinux_mnt_opts *opts = mnt_opts; 263962306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci /* 264262306a36Sopenharmony_ci * Superblock not initialized (i.e. no options) - reject if any 264362306a36Sopenharmony_ci * options specified, otherwise accept. 264462306a36Sopenharmony_ci */ 264562306a36Sopenharmony_ci if (!(sbsec->flags & SE_SBINITIALIZED)) 264662306a36Sopenharmony_ci return opts ? 1 : 0; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci /* 264962306a36Sopenharmony_ci * Superblock initialized and no options specified - reject if 265062306a36Sopenharmony_ci * superblock has any options set, otherwise accept. 265162306a36Sopenharmony_ci */ 265262306a36Sopenharmony_ci if (!opts) 265362306a36Sopenharmony_ci return (sbsec->flags & SE_MNTMASK) ? 1 : 0; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci if (opts->fscontext_sid) { 265662306a36Sopenharmony_ci if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, 265762306a36Sopenharmony_ci opts->fscontext_sid)) 265862306a36Sopenharmony_ci return 1; 265962306a36Sopenharmony_ci } 266062306a36Sopenharmony_ci if (opts->context_sid) { 266162306a36Sopenharmony_ci if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, 266262306a36Sopenharmony_ci opts->context_sid)) 266362306a36Sopenharmony_ci return 1; 266462306a36Sopenharmony_ci } 266562306a36Sopenharmony_ci if (opts->rootcontext_sid) { 266662306a36Sopenharmony_ci struct inode_security_struct *root_isec; 266762306a36Sopenharmony_ci 266862306a36Sopenharmony_ci root_isec = backing_inode_security(sb->s_root); 266962306a36Sopenharmony_ci if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, 267062306a36Sopenharmony_ci opts->rootcontext_sid)) 267162306a36Sopenharmony_ci return 1; 267262306a36Sopenharmony_ci } 267362306a36Sopenharmony_ci if (opts->defcontext_sid) { 267462306a36Sopenharmony_ci if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, 267562306a36Sopenharmony_ci opts->defcontext_sid)) 267662306a36Sopenharmony_ci return 1; 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci return 0; 267962306a36Sopenharmony_ci} 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_cistatic int selinux_sb_remount(struct super_block *sb, void *mnt_opts) 268262306a36Sopenharmony_ci{ 268362306a36Sopenharmony_ci struct selinux_mnt_opts *opts = mnt_opts; 268462306a36Sopenharmony_ci struct superblock_security_struct *sbsec = selinux_superblock(sb); 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci if (!(sbsec->flags & SE_SBINITIALIZED)) 268762306a36Sopenharmony_ci return 0; 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci if (!opts) 269062306a36Sopenharmony_ci return 0; 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci if (opts->fscontext_sid) { 269362306a36Sopenharmony_ci if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, 269462306a36Sopenharmony_ci opts->fscontext_sid)) 269562306a36Sopenharmony_ci goto out_bad_option; 269662306a36Sopenharmony_ci } 269762306a36Sopenharmony_ci if (opts->context_sid) { 269862306a36Sopenharmony_ci if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, 269962306a36Sopenharmony_ci opts->context_sid)) 270062306a36Sopenharmony_ci goto out_bad_option; 270162306a36Sopenharmony_ci } 270262306a36Sopenharmony_ci if (opts->rootcontext_sid) { 270362306a36Sopenharmony_ci struct inode_security_struct *root_isec; 270462306a36Sopenharmony_ci root_isec = backing_inode_security(sb->s_root); 270562306a36Sopenharmony_ci if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, 270662306a36Sopenharmony_ci opts->rootcontext_sid)) 270762306a36Sopenharmony_ci goto out_bad_option; 270862306a36Sopenharmony_ci } 270962306a36Sopenharmony_ci if (opts->defcontext_sid) { 271062306a36Sopenharmony_ci if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, 271162306a36Sopenharmony_ci opts->defcontext_sid)) 271262306a36Sopenharmony_ci goto out_bad_option; 271362306a36Sopenharmony_ci } 271462306a36Sopenharmony_ci return 0; 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ciout_bad_option: 271762306a36Sopenharmony_ci pr_warn("SELinux: unable to change security options " 271862306a36Sopenharmony_ci "during remount (dev %s, type=%s)\n", sb->s_id, 271962306a36Sopenharmony_ci sb->s_type->name); 272062306a36Sopenharmony_ci return -EINVAL; 272162306a36Sopenharmony_ci} 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_cistatic int selinux_sb_kern_mount(struct super_block *sb) 272462306a36Sopenharmony_ci{ 272562306a36Sopenharmony_ci const struct cred *cred = current_cred(); 272662306a36Sopenharmony_ci struct common_audit_data ad; 272762306a36Sopenharmony_ci 272862306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 272962306a36Sopenharmony_ci ad.u.dentry = sb->s_root; 273062306a36Sopenharmony_ci return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); 273162306a36Sopenharmony_ci} 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_cistatic int selinux_sb_statfs(struct dentry *dentry) 273462306a36Sopenharmony_ci{ 273562306a36Sopenharmony_ci const struct cred *cred = current_cred(); 273662306a36Sopenharmony_ci struct common_audit_data ad; 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 273962306a36Sopenharmony_ci ad.u.dentry = dentry->d_sb->s_root; 274062306a36Sopenharmony_ci return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); 274162306a36Sopenharmony_ci} 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_cistatic int selinux_mount(const char *dev_name, 274462306a36Sopenharmony_ci const struct path *path, 274562306a36Sopenharmony_ci const char *type, 274662306a36Sopenharmony_ci unsigned long flags, 274762306a36Sopenharmony_ci void *data) 274862306a36Sopenharmony_ci{ 274962306a36Sopenharmony_ci const struct cred *cred = current_cred(); 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci if (flags & MS_REMOUNT) 275262306a36Sopenharmony_ci return superblock_has_perm(cred, path->dentry->d_sb, 275362306a36Sopenharmony_ci FILESYSTEM__REMOUNT, NULL); 275462306a36Sopenharmony_ci else 275562306a36Sopenharmony_ci return path_has_perm(cred, path, FILE__MOUNTON); 275662306a36Sopenharmony_ci} 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_cistatic int selinux_move_mount(const struct path *from_path, 275962306a36Sopenharmony_ci const struct path *to_path) 276062306a36Sopenharmony_ci{ 276162306a36Sopenharmony_ci const struct cred *cred = current_cred(); 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci return path_has_perm(cred, to_path, FILE__MOUNTON); 276462306a36Sopenharmony_ci} 276562306a36Sopenharmony_ci 276662306a36Sopenharmony_cistatic int selinux_umount(struct vfsmount *mnt, int flags) 276762306a36Sopenharmony_ci{ 276862306a36Sopenharmony_ci const struct cred *cred = current_cred(); 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci return superblock_has_perm(cred, mnt->mnt_sb, 277162306a36Sopenharmony_ci FILESYSTEM__UNMOUNT, NULL); 277262306a36Sopenharmony_ci} 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_cistatic int selinux_fs_context_submount(struct fs_context *fc, 277562306a36Sopenharmony_ci struct super_block *reference) 277662306a36Sopenharmony_ci{ 277762306a36Sopenharmony_ci const struct superblock_security_struct *sbsec = selinux_superblock(reference); 277862306a36Sopenharmony_ci struct selinux_mnt_opts *opts; 277962306a36Sopenharmony_ci 278062306a36Sopenharmony_ci /* 278162306a36Sopenharmony_ci * Ensure that fc->security remains NULL when no options are set 278262306a36Sopenharmony_ci * as expected by selinux_set_mnt_opts(). 278362306a36Sopenharmony_ci */ 278462306a36Sopenharmony_ci if (!(sbsec->flags & (FSCONTEXT_MNT|CONTEXT_MNT|DEFCONTEXT_MNT))) 278562306a36Sopenharmony_ci return 0; 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 278862306a36Sopenharmony_ci if (!opts) 278962306a36Sopenharmony_ci return -ENOMEM; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci if (sbsec->flags & FSCONTEXT_MNT) 279262306a36Sopenharmony_ci opts->fscontext_sid = sbsec->sid; 279362306a36Sopenharmony_ci if (sbsec->flags & CONTEXT_MNT) 279462306a36Sopenharmony_ci opts->context_sid = sbsec->mntpoint_sid; 279562306a36Sopenharmony_ci if (sbsec->flags & DEFCONTEXT_MNT) 279662306a36Sopenharmony_ci opts->defcontext_sid = sbsec->def_sid; 279762306a36Sopenharmony_ci fc->security = opts; 279862306a36Sopenharmony_ci return 0; 279962306a36Sopenharmony_ci} 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_cistatic int selinux_fs_context_dup(struct fs_context *fc, 280262306a36Sopenharmony_ci struct fs_context *src_fc) 280362306a36Sopenharmony_ci{ 280462306a36Sopenharmony_ci const struct selinux_mnt_opts *src = src_fc->security; 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci if (!src) 280762306a36Sopenharmony_ci return 0; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci fc->security = kmemdup(src, sizeof(*src), GFP_KERNEL); 281062306a36Sopenharmony_ci return fc->security ? 0 : -ENOMEM; 281162306a36Sopenharmony_ci} 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_cistatic const struct fs_parameter_spec selinux_fs_parameters[] = { 281462306a36Sopenharmony_ci fsparam_string(CONTEXT_STR, Opt_context), 281562306a36Sopenharmony_ci fsparam_string(DEFCONTEXT_STR, Opt_defcontext), 281662306a36Sopenharmony_ci fsparam_string(FSCONTEXT_STR, Opt_fscontext), 281762306a36Sopenharmony_ci fsparam_string(ROOTCONTEXT_STR, Opt_rootcontext), 281862306a36Sopenharmony_ci fsparam_flag (SECLABEL_STR, Opt_seclabel), 281962306a36Sopenharmony_ci {} 282062306a36Sopenharmony_ci}; 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_cistatic int selinux_fs_context_parse_param(struct fs_context *fc, 282362306a36Sopenharmony_ci struct fs_parameter *param) 282462306a36Sopenharmony_ci{ 282562306a36Sopenharmony_ci struct fs_parse_result result; 282662306a36Sopenharmony_ci int opt; 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_ci opt = fs_parse(fc, selinux_fs_parameters, param, &result); 282962306a36Sopenharmony_ci if (opt < 0) 283062306a36Sopenharmony_ci return opt; 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ci return selinux_add_opt(opt, param->string, &fc->security); 283362306a36Sopenharmony_ci} 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci/* inode security operations */ 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_cistatic int selinux_inode_alloc_security(struct inode *inode) 283862306a36Sopenharmony_ci{ 283962306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 284062306a36Sopenharmony_ci u32 sid = current_sid(); 284162306a36Sopenharmony_ci 284262306a36Sopenharmony_ci spin_lock_init(&isec->lock); 284362306a36Sopenharmony_ci INIT_LIST_HEAD(&isec->list); 284462306a36Sopenharmony_ci isec->inode = inode; 284562306a36Sopenharmony_ci isec->sid = SECINITSID_UNLABELED; 284662306a36Sopenharmony_ci isec->sclass = SECCLASS_FILE; 284762306a36Sopenharmony_ci isec->task_sid = sid; 284862306a36Sopenharmony_ci isec->initialized = LABEL_INVALID; 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci return 0; 285162306a36Sopenharmony_ci} 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_cistatic void selinux_inode_free_security(struct inode *inode) 285462306a36Sopenharmony_ci{ 285562306a36Sopenharmony_ci inode_free_security(inode); 285662306a36Sopenharmony_ci} 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_cistatic int selinux_dentry_init_security(struct dentry *dentry, int mode, 285962306a36Sopenharmony_ci const struct qstr *name, 286062306a36Sopenharmony_ci const char **xattr_name, void **ctx, 286162306a36Sopenharmony_ci u32 *ctxlen) 286262306a36Sopenharmony_ci{ 286362306a36Sopenharmony_ci u32 newsid; 286462306a36Sopenharmony_ci int rc; 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci rc = selinux_determine_inode_label(selinux_cred(current_cred()), 286762306a36Sopenharmony_ci d_inode(dentry->d_parent), name, 286862306a36Sopenharmony_ci inode_mode_to_security_class(mode), 286962306a36Sopenharmony_ci &newsid); 287062306a36Sopenharmony_ci if (rc) 287162306a36Sopenharmony_ci return rc; 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci if (xattr_name) 287462306a36Sopenharmony_ci *xattr_name = XATTR_NAME_SELINUX; 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci return security_sid_to_context(newsid, (char **)ctx, 287762306a36Sopenharmony_ci ctxlen); 287862306a36Sopenharmony_ci} 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_cistatic int selinux_dentry_create_files_as(struct dentry *dentry, int mode, 288162306a36Sopenharmony_ci struct qstr *name, 288262306a36Sopenharmony_ci const struct cred *old, 288362306a36Sopenharmony_ci struct cred *new) 288462306a36Sopenharmony_ci{ 288562306a36Sopenharmony_ci u32 newsid; 288662306a36Sopenharmony_ci int rc; 288762306a36Sopenharmony_ci struct task_security_struct *tsec; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci rc = selinux_determine_inode_label(selinux_cred(old), 289062306a36Sopenharmony_ci d_inode(dentry->d_parent), name, 289162306a36Sopenharmony_ci inode_mode_to_security_class(mode), 289262306a36Sopenharmony_ci &newsid); 289362306a36Sopenharmony_ci if (rc) 289462306a36Sopenharmony_ci return rc; 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci tsec = selinux_cred(new); 289762306a36Sopenharmony_ci tsec->create_sid = newsid; 289862306a36Sopenharmony_ci return 0; 289962306a36Sopenharmony_ci} 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_cistatic int selinux_inode_init_security(struct inode *inode, struct inode *dir, 290262306a36Sopenharmony_ci const struct qstr *qstr, 290362306a36Sopenharmony_ci struct xattr *xattrs, int *xattr_count) 290462306a36Sopenharmony_ci{ 290562306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 290662306a36Sopenharmony_ci struct superblock_security_struct *sbsec; 290762306a36Sopenharmony_ci struct xattr *xattr = lsm_get_xattr_slot(xattrs, xattr_count); 290862306a36Sopenharmony_ci u32 newsid, clen; 290962306a36Sopenharmony_ci int rc; 291062306a36Sopenharmony_ci char *context; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci sbsec = selinux_superblock(dir->i_sb); 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci newsid = tsec->create_sid; 291562306a36Sopenharmony_ci 291662306a36Sopenharmony_ci rc = selinux_determine_inode_label(tsec, dir, qstr, 291762306a36Sopenharmony_ci inode_mode_to_security_class(inode->i_mode), 291862306a36Sopenharmony_ci &newsid); 291962306a36Sopenharmony_ci if (rc) 292062306a36Sopenharmony_ci return rc; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci /* Possibly defer initialization to selinux_complete_init. */ 292362306a36Sopenharmony_ci if (sbsec->flags & SE_SBINITIALIZED) { 292462306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 292562306a36Sopenharmony_ci isec->sclass = inode_mode_to_security_class(inode->i_mode); 292662306a36Sopenharmony_ci isec->sid = newsid; 292762306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 292862306a36Sopenharmony_ci } 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci if (!selinux_initialized() || 293162306a36Sopenharmony_ci !(sbsec->flags & SBLABEL_MNT)) 293262306a36Sopenharmony_ci return -EOPNOTSUPP; 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci if (xattr) { 293562306a36Sopenharmony_ci rc = security_sid_to_context_force(newsid, 293662306a36Sopenharmony_ci &context, &clen); 293762306a36Sopenharmony_ci if (rc) 293862306a36Sopenharmony_ci return rc; 293962306a36Sopenharmony_ci xattr->value = context; 294062306a36Sopenharmony_ci xattr->value_len = clen; 294162306a36Sopenharmony_ci xattr->name = XATTR_SELINUX_SUFFIX; 294262306a36Sopenharmony_ci } 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_ci return 0; 294562306a36Sopenharmony_ci} 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_cistatic int selinux_inode_init_security_anon(struct inode *inode, 294862306a36Sopenharmony_ci const struct qstr *name, 294962306a36Sopenharmony_ci const struct inode *context_inode) 295062306a36Sopenharmony_ci{ 295162306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 295262306a36Sopenharmony_ci struct common_audit_data ad; 295362306a36Sopenharmony_ci struct inode_security_struct *isec; 295462306a36Sopenharmony_ci int rc; 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci if (unlikely(!selinux_initialized())) 295762306a36Sopenharmony_ci return 0; 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci isec = selinux_inode(inode); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci /* 296262306a36Sopenharmony_ci * We only get here once per ephemeral inode. The inode has 296362306a36Sopenharmony_ci * been initialized via inode_alloc_security but is otherwise 296462306a36Sopenharmony_ci * untouched. 296562306a36Sopenharmony_ci */ 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci if (context_inode) { 296862306a36Sopenharmony_ci struct inode_security_struct *context_isec = 296962306a36Sopenharmony_ci selinux_inode(context_inode); 297062306a36Sopenharmony_ci if (context_isec->initialized != LABEL_INITIALIZED) { 297162306a36Sopenharmony_ci pr_err("SELinux: context_inode is not initialized\n"); 297262306a36Sopenharmony_ci return -EACCES; 297362306a36Sopenharmony_ci } 297462306a36Sopenharmony_ci 297562306a36Sopenharmony_ci isec->sclass = context_isec->sclass; 297662306a36Sopenharmony_ci isec->sid = context_isec->sid; 297762306a36Sopenharmony_ci } else { 297862306a36Sopenharmony_ci isec->sclass = SECCLASS_ANON_INODE; 297962306a36Sopenharmony_ci rc = security_transition_sid( 298062306a36Sopenharmony_ci tsec->sid, tsec->sid, 298162306a36Sopenharmony_ci isec->sclass, name, &isec->sid); 298262306a36Sopenharmony_ci if (rc) 298362306a36Sopenharmony_ci return rc; 298462306a36Sopenharmony_ci } 298562306a36Sopenharmony_ci 298662306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 298762306a36Sopenharmony_ci /* 298862306a36Sopenharmony_ci * Now that we've initialized security, check whether we're 298962306a36Sopenharmony_ci * allowed to actually create this type of anonymous inode. 299062306a36Sopenharmony_ci */ 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_ANONINODE; 299362306a36Sopenharmony_ci ad.u.anonclass = name ? (const char *)name->name : "?"; 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci return avc_has_perm(tsec->sid, 299662306a36Sopenharmony_ci isec->sid, 299762306a36Sopenharmony_ci isec->sclass, 299862306a36Sopenharmony_ci FILE__CREATE, 299962306a36Sopenharmony_ci &ad); 300062306a36Sopenharmony_ci} 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_cistatic int selinux_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode) 300362306a36Sopenharmony_ci{ 300462306a36Sopenharmony_ci return may_create(dir, dentry, SECCLASS_FILE); 300562306a36Sopenharmony_ci} 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_cistatic int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) 300862306a36Sopenharmony_ci{ 300962306a36Sopenharmony_ci return may_link(dir, old_dentry, MAY_LINK); 301062306a36Sopenharmony_ci} 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_cistatic int selinux_inode_unlink(struct inode *dir, struct dentry *dentry) 301362306a36Sopenharmony_ci{ 301462306a36Sopenharmony_ci return may_link(dir, dentry, MAY_UNLINK); 301562306a36Sopenharmony_ci} 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_cistatic int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name) 301862306a36Sopenharmony_ci{ 301962306a36Sopenharmony_ci return may_create(dir, dentry, SECCLASS_LNK_FILE); 302062306a36Sopenharmony_ci} 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_cistatic int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mask) 302362306a36Sopenharmony_ci{ 302462306a36Sopenharmony_ci return may_create(dir, dentry, SECCLASS_DIR); 302562306a36Sopenharmony_ci} 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_cistatic int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry) 302862306a36Sopenharmony_ci{ 302962306a36Sopenharmony_ci return may_link(dir, dentry, MAY_RMDIR); 303062306a36Sopenharmony_ci} 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_cistatic int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) 303362306a36Sopenharmony_ci{ 303462306a36Sopenharmony_ci return may_create(dir, dentry, inode_mode_to_security_class(mode)); 303562306a36Sopenharmony_ci} 303662306a36Sopenharmony_ci 303762306a36Sopenharmony_cistatic int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry, 303862306a36Sopenharmony_ci struct inode *new_inode, struct dentry *new_dentry) 303962306a36Sopenharmony_ci{ 304062306a36Sopenharmony_ci return may_rename(old_inode, old_dentry, new_inode, new_dentry); 304162306a36Sopenharmony_ci} 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_cistatic int selinux_inode_readlink(struct dentry *dentry) 304462306a36Sopenharmony_ci{ 304562306a36Sopenharmony_ci const struct cred *cred = current_cred(); 304662306a36Sopenharmony_ci 304762306a36Sopenharmony_ci return dentry_has_perm(cred, dentry, FILE__READ); 304862306a36Sopenharmony_ci} 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_cistatic int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode, 305162306a36Sopenharmony_ci bool rcu) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci const struct cred *cred = current_cred(); 305462306a36Sopenharmony_ci struct common_audit_data ad; 305562306a36Sopenharmony_ci struct inode_security_struct *isec; 305662306a36Sopenharmony_ci u32 sid; 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 305962306a36Sopenharmony_ci ad.u.dentry = dentry; 306062306a36Sopenharmony_ci sid = cred_sid(cred); 306162306a36Sopenharmony_ci isec = inode_security_rcu(inode, rcu); 306262306a36Sopenharmony_ci if (IS_ERR(isec)) 306362306a36Sopenharmony_ci return PTR_ERR(isec); 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, isec->sclass, FILE__READ, &ad); 306662306a36Sopenharmony_ci} 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_cistatic noinline int audit_inode_permission(struct inode *inode, 306962306a36Sopenharmony_ci u32 perms, u32 audited, u32 denied, 307062306a36Sopenharmony_ci int result) 307162306a36Sopenharmony_ci{ 307262306a36Sopenharmony_ci struct common_audit_data ad; 307362306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_INODE; 307662306a36Sopenharmony_ci ad.u.inode = inode; 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ci return slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, 307962306a36Sopenharmony_ci audited, denied, result, &ad); 308062306a36Sopenharmony_ci} 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_cistatic int selinux_inode_permission(struct inode *inode, int mask) 308362306a36Sopenharmony_ci{ 308462306a36Sopenharmony_ci const struct cred *cred = current_cred(); 308562306a36Sopenharmony_ci u32 perms; 308662306a36Sopenharmony_ci bool from_access; 308762306a36Sopenharmony_ci bool no_block = mask & MAY_NOT_BLOCK; 308862306a36Sopenharmony_ci struct inode_security_struct *isec; 308962306a36Sopenharmony_ci u32 sid; 309062306a36Sopenharmony_ci struct av_decision avd; 309162306a36Sopenharmony_ci int rc, rc2; 309262306a36Sopenharmony_ci u32 audited, denied; 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci from_access = mask & MAY_ACCESS; 309562306a36Sopenharmony_ci mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci /* No permission to check. Existence test. */ 309862306a36Sopenharmony_ci if (!mask) 309962306a36Sopenharmony_ci return 0; 310062306a36Sopenharmony_ci 310162306a36Sopenharmony_ci if (unlikely(IS_PRIVATE(inode))) 310262306a36Sopenharmony_ci return 0; 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci perms = file_mask_to_av(inode->i_mode, mask); 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci sid = cred_sid(cred); 310762306a36Sopenharmony_ci isec = inode_security_rcu(inode, no_block); 310862306a36Sopenharmony_ci if (IS_ERR(isec)) 310962306a36Sopenharmony_ci return PTR_ERR(isec); 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, 311262306a36Sopenharmony_ci &avd); 311362306a36Sopenharmony_ci audited = avc_audit_required(perms, &avd, rc, 311462306a36Sopenharmony_ci from_access ? FILE__AUDIT_ACCESS : 0, 311562306a36Sopenharmony_ci &denied); 311662306a36Sopenharmony_ci if (likely(!audited)) 311762306a36Sopenharmony_ci return rc; 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_ci rc2 = audit_inode_permission(inode, perms, audited, denied, rc); 312062306a36Sopenharmony_ci if (rc2) 312162306a36Sopenharmony_ci return rc2; 312262306a36Sopenharmony_ci return rc; 312362306a36Sopenharmony_ci} 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_cistatic int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) 312662306a36Sopenharmony_ci{ 312762306a36Sopenharmony_ci const struct cred *cred = current_cred(); 312862306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 312962306a36Sopenharmony_ci unsigned int ia_valid = iattr->ia_valid; 313062306a36Sopenharmony_ci __u32 av = FILE__WRITE; 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */ 313362306a36Sopenharmony_ci if (ia_valid & ATTR_FORCE) { 313462306a36Sopenharmony_ci ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE | 313562306a36Sopenharmony_ci ATTR_FORCE); 313662306a36Sopenharmony_ci if (!ia_valid) 313762306a36Sopenharmony_ci return 0; 313862306a36Sopenharmony_ci } 313962306a36Sopenharmony_ci 314062306a36Sopenharmony_ci if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | 314162306a36Sopenharmony_ci ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) 314262306a36Sopenharmony_ci return dentry_has_perm(cred, dentry, FILE__SETATTR); 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci if (selinux_policycap_openperm() && 314562306a36Sopenharmony_ci inode->i_sb->s_magic != SOCKFS_MAGIC && 314662306a36Sopenharmony_ci (ia_valid & ATTR_SIZE) && 314762306a36Sopenharmony_ci !(ia_valid & ATTR_FILE)) 314862306a36Sopenharmony_ci av |= FILE__OPEN; 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci return dentry_has_perm(cred, dentry, av); 315162306a36Sopenharmony_ci} 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_cistatic int selinux_inode_getattr(const struct path *path) 315462306a36Sopenharmony_ci{ 315562306a36Sopenharmony_ci return path_has_perm(current_cred(), path, FILE__GETATTR); 315662306a36Sopenharmony_ci} 315762306a36Sopenharmony_ci 315862306a36Sopenharmony_cistatic bool has_cap_mac_admin(bool audit) 315962306a36Sopenharmony_ci{ 316062306a36Sopenharmony_ci const struct cred *cred = current_cred(); 316162306a36Sopenharmony_ci unsigned int opts = audit ? CAP_OPT_NONE : CAP_OPT_NOAUDIT; 316262306a36Sopenharmony_ci 316362306a36Sopenharmony_ci if (cap_capable(cred, &init_user_ns, CAP_MAC_ADMIN, opts)) 316462306a36Sopenharmony_ci return false; 316562306a36Sopenharmony_ci if (cred_has_capability(cred, CAP_MAC_ADMIN, opts, true)) 316662306a36Sopenharmony_ci return false; 316762306a36Sopenharmony_ci return true; 316862306a36Sopenharmony_ci} 316962306a36Sopenharmony_ci 317062306a36Sopenharmony_cistatic int selinux_inode_setxattr(struct mnt_idmap *idmap, 317162306a36Sopenharmony_ci struct dentry *dentry, const char *name, 317262306a36Sopenharmony_ci const void *value, size_t size, int flags) 317362306a36Sopenharmony_ci{ 317462306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 317562306a36Sopenharmony_ci struct inode_security_struct *isec; 317662306a36Sopenharmony_ci struct superblock_security_struct *sbsec; 317762306a36Sopenharmony_ci struct common_audit_data ad; 317862306a36Sopenharmony_ci u32 newsid, sid = current_sid(); 317962306a36Sopenharmony_ci int rc = 0; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci if (strcmp(name, XATTR_NAME_SELINUX)) { 318262306a36Sopenharmony_ci rc = cap_inode_setxattr(dentry, name, value, size, flags); 318362306a36Sopenharmony_ci if (rc) 318462306a36Sopenharmony_ci return rc; 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci /* Not an attribute we recognize, so just check the 318762306a36Sopenharmony_ci ordinary setattr permission. */ 318862306a36Sopenharmony_ci return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); 318962306a36Sopenharmony_ci } 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_ci if (!selinux_initialized()) 319262306a36Sopenharmony_ci return (inode_owner_or_capable(idmap, inode) ? 0 : -EPERM); 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci sbsec = selinux_superblock(inode->i_sb); 319562306a36Sopenharmony_ci if (!(sbsec->flags & SBLABEL_MNT)) 319662306a36Sopenharmony_ci return -EOPNOTSUPP; 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci if (!inode_owner_or_capable(idmap, inode)) 319962306a36Sopenharmony_ci return -EPERM; 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_DENTRY; 320262306a36Sopenharmony_ci ad.u.dentry = dentry; 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_ci isec = backing_inode_security(dentry); 320562306a36Sopenharmony_ci rc = avc_has_perm(sid, isec->sid, isec->sclass, 320662306a36Sopenharmony_ci FILE__RELABELFROM, &ad); 320762306a36Sopenharmony_ci if (rc) 320862306a36Sopenharmony_ci return rc; 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci rc = security_context_to_sid(value, size, &newsid, 321162306a36Sopenharmony_ci GFP_KERNEL); 321262306a36Sopenharmony_ci if (rc == -EINVAL) { 321362306a36Sopenharmony_ci if (!has_cap_mac_admin(true)) { 321462306a36Sopenharmony_ci struct audit_buffer *ab; 321562306a36Sopenharmony_ci size_t audit_size; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci /* We strip a nul only if it is at the end, otherwise the 321862306a36Sopenharmony_ci * context contains a nul and we should audit that */ 321962306a36Sopenharmony_ci if (value) { 322062306a36Sopenharmony_ci const char *str = value; 322162306a36Sopenharmony_ci 322262306a36Sopenharmony_ci if (str[size - 1] == '\0') 322362306a36Sopenharmony_ci audit_size = size - 1; 322462306a36Sopenharmony_ci else 322562306a36Sopenharmony_ci audit_size = size; 322662306a36Sopenharmony_ci } else { 322762306a36Sopenharmony_ci audit_size = 0; 322862306a36Sopenharmony_ci } 322962306a36Sopenharmony_ci ab = audit_log_start(audit_context(), 323062306a36Sopenharmony_ci GFP_ATOMIC, AUDIT_SELINUX_ERR); 323162306a36Sopenharmony_ci if (!ab) 323262306a36Sopenharmony_ci return rc; 323362306a36Sopenharmony_ci audit_log_format(ab, "op=setxattr invalid_context="); 323462306a36Sopenharmony_ci audit_log_n_untrustedstring(ab, value, audit_size); 323562306a36Sopenharmony_ci audit_log_end(ab); 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci return rc; 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci rc = security_context_to_sid_force(value, 324062306a36Sopenharmony_ci size, &newsid); 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci if (rc) 324362306a36Sopenharmony_ci return rc; 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci rc = avc_has_perm(sid, newsid, isec->sclass, 324662306a36Sopenharmony_ci FILE__RELABELTO, &ad); 324762306a36Sopenharmony_ci if (rc) 324862306a36Sopenharmony_ci return rc; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci rc = security_validate_transition(isec->sid, newsid, 325162306a36Sopenharmony_ci sid, isec->sclass); 325262306a36Sopenharmony_ci if (rc) 325362306a36Sopenharmony_ci return rc; 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci return avc_has_perm(newsid, 325662306a36Sopenharmony_ci sbsec->sid, 325762306a36Sopenharmony_ci SECCLASS_FILESYSTEM, 325862306a36Sopenharmony_ci FILESYSTEM__ASSOCIATE, 325962306a36Sopenharmony_ci &ad); 326062306a36Sopenharmony_ci} 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_cistatic int selinux_inode_set_acl(struct mnt_idmap *idmap, 326362306a36Sopenharmony_ci struct dentry *dentry, const char *acl_name, 326462306a36Sopenharmony_ci struct posix_acl *kacl) 326562306a36Sopenharmony_ci{ 326662306a36Sopenharmony_ci return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); 326762306a36Sopenharmony_ci} 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_cistatic int selinux_inode_get_acl(struct mnt_idmap *idmap, 327062306a36Sopenharmony_ci struct dentry *dentry, const char *acl_name) 327162306a36Sopenharmony_ci{ 327262306a36Sopenharmony_ci return dentry_has_perm(current_cred(), dentry, FILE__GETATTR); 327362306a36Sopenharmony_ci} 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_cistatic int selinux_inode_remove_acl(struct mnt_idmap *idmap, 327662306a36Sopenharmony_ci struct dentry *dentry, const char *acl_name) 327762306a36Sopenharmony_ci{ 327862306a36Sopenharmony_ci return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); 327962306a36Sopenharmony_ci} 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_cistatic void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, 328262306a36Sopenharmony_ci const void *value, size_t size, 328362306a36Sopenharmony_ci int flags) 328462306a36Sopenharmony_ci{ 328562306a36Sopenharmony_ci struct inode *inode = d_backing_inode(dentry); 328662306a36Sopenharmony_ci struct inode_security_struct *isec; 328762306a36Sopenharmony_ci u32 newsid; 328862306a36Sopenharmony_ci int rc; 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci if (strcmp(name, XATTR_NAME_SELINUX)) { 329162306a36Sopenharmony_ci /* Not an attribute we recognize, so nothing to do. */ 329262306a36Sopenharmony_ci return; 329362306a36Sopenharmony_ci } 329462306a36Sopenharmony_ci 329562306a36Sopenharmony_ci if (!selinux_initialized()) { 329662306a36Sopenharmony_ci /* If we haven't even been initialized, then we can't validate 329762306a36Sopenharmony_ci * against a policy, so leave the label as invalid. It may 329862306a36Sopenharmony_ci * resolve to a valid label on the next revalidation try if 329962306a36Sopenharmony_ci * we've since initialized. 330062306a36Sopenharmony_ci */ 330162306a36Sopenharmony_ci return; 330262306a36Sopenharmony_ci } 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci rc = security_context_to_sid_force(value, size, 330562306a36Sopenharmony_ci &newsid); 330662306a36Sopenharmony_ci if (rc) { 330762306a36Sopenharmony_ci pr_err("SELinux: unable to map context to SID" 330862306a36Sopenharmony_ci "for (%s, %lu), rc=%d\n", 330962306a36Sopenharmony_ci inode->i_sb->s_id, inode->i_ino, -rc); 331062306a36Sopenharmony_ci return; 331162306a36Sopenharmony_ci } 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci isec = backing_inode_security(dentry); 331462306a36Sopenharmony_ci spin_lock(&isec->lock); 331562306a36Sopenharmony_ci isec->sclass = inode_mode_to_security_class(inode->i_mode); 331662306a36Sopenharmony_ci isec->sid = newsid; 331762306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 331862306a36Sopenharmony_ci spin_unlock(&isec->lock); 331962306a36Sopenharmony_ci} 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_cistatic int selinux_inode_getxattr(struct dentry *dentry, const char *name) 332262306a36Sopenharmony_ci{ 332362306a36Sopenharmony_ci const struct cred *cred = current_cred(); 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_ci return dentry_has_perm(cred, dentry, FILE__GETATTR); 332662306a36Sopenharmony_ci} 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_cistatic int selinux_inode_listxattr(struct dentry *dentry) 332962306a36Sopenharmony_ci{ 333062306a36Sopenharmony_ci const struct cred *cred = current_cred(); 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci return dentry_has_perm(cred, dentry, FILE__GETATTR); 333362306a36Sopenharmony_ci} 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_cistatic int selinux_inode_removexattr(struct mnt_idmap *idmap, 333662306a36Sopenharmony_ci struct dentry *dentry, const char *name) 333762306a36Sopenharmony_ci{ 333862306a36Sopenharmony_ci if (strcmp(name, XATTR_NAME_SELINUX)) { 333962306a36Sopenharmony_ci int rc = cap_inode_removexattr(idmap, dentry, name); 334062306a36Sopenharmony_ci if (rc) 334162306a36Sopenharmony_ci return rc; 334262306a36Sopenharmony_ci 334362306a36Sopenharmony_ci /* Not an attribute we recognize, so just check the 334462306a36Sopenharmony_ci ordinary setattr permission. */ 334562306a36Sopenharmony_ci return dentry_has_perm(current_cred(), dentry, FILE__SETATTR); 334662306a36Sopenharmony_ci } 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci if (!selinux_initialized()) 334962306a36Sopenharmony_ci return 0; 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci /* No one is allowed to remove a SELinux security label. 335262306a36Sopenharmony_ci You can change the label, but all data must be labeled. */ 335362306a36Sopenharmony_ci return -EACCES; 335462306a36Sopenharmony_ci} 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_cistatic int selinux_path_notify(const struct path *path, u64 mask, 335762306a36Sopenharmony_ci unsigned int obj_type) 335862306a36Sopenharmony_ci{ 335962306a36Sopenharmony_ci int ret; 336062306a36Sopenharmony_ci u32 perm; 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci struct common_audit_data ad; 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_PATH; 336562306a36Sopenharmony_ci ad.u.path = *path; 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci /* 336862306a36Sopenharmony_ci * Set permission needed based on the type of mark being set. 336962306a36Sopenharmony_ci * Performs an additional check for sb watches. 337062306a36Sopenharmony_ci */ 337162306a36Sopenharmony_ci switch (obj_type) { 337262306a36Sopenharmony_ci case FSNOTIFY_OBJ_TYPE_VFSMOUNT: 337362306a36Sopenharmony_ci perm = FILE__WATCH_MOUNT; 337462306a36Sopenharmony_ci break; 337562306a36Sopenharmony_ci case FSNOTIFY_OBJ_TYPE_SB: 337662306a36Sopenharmony_ci perm = FILE__WATCH_SB; 337762306a36Sopenharmony_ci ret = superblock_has_perm(current_cred(), path->dentry->d_sb, 337862306a36Sopenharmony_ci FILESYSTEM__WATCH, &ad); 337962306a36Sopenharmony_ci if (ret) 338062306a36Sopenharmony_ci return ret; 338162306a36Sopenharmony_ci break; 338262306a36Sopenharmony_ci case FSNOTIFY_OBJ_TYPE_INODE: 338362306a36Sopenharmony_ci perm = FILE__WATCH; 338462306a36Sopenharmony_ci break; 338562306a36Sopenharmony_ci default: 338662306a36Sopenharmony_ci return -EINVAL; 338762306a36Sopenharmony_ci } 338862306a36Sopenharmony_ci 338962306a36Sopenharmony_ci /* blocking watches require the file:watch_with_perm permission */ 339062306a36Sopenharmony_ci if (mask & (ALL_FSNOTIFY_PERM_EVENTS)) 339162306a36Sopenharmony_ci perm |= FILE__WATCH_WITH_PERM; 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_ci /* watches on read-like events need the file:watch_reads permission */ 339462306a36Sopenharmony_ci if (mask & (FS_ACCESS | FS_ACCESS_PERM | FS_CLOSE_NOWRITE)) 339562306a36Sopenharmony_ci perm |= FILE__WATCH_READS; 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ci return path_has_perm(current_cred(), path, perm); 339862306a36Sopenharmony_ci} 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci/* 340162306a36Sopenharmony_ci * Copy the inode security context value to the user. 340262306a36Sopenharmony_ci * 340362306a36Sopenharmony_ci * Permission check is handled by selinux_inode_getxattr hook. 340462306a36Sopenharmony_ci */ 340562306a36Sopenharmony_cistatic int selinux_inode_getsecurity(struct mnt_idmap *idmap, 340662306a36Sopenharmony_ci struct inode *inode, const char *name, 340762306a36Sopenharmony_ci void **buffer, bool alloc) 340862306a36Sopenharmony_ci{ 340962306a36Sopenharmony_ci u32 size; 341062306a36Sopenharmony_ci int error; 341162306a36Sopenharmony_ci char *context = NULL; 341262306a36Sopenharmony_ci struct inode_security_struct *isec; 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci /* 341562306a36Sopenharmony_ci * If we're not initialized yet, then we can't validate contexts, so 341662306a36Sopenharmony_ci * just let vfs_getxattr fall back to using the on-disk xattr. 341762306a36Sopenharmony_ci */ 341862306a36Sopenharmony_ci if (!selinux_initialized() || 341962306a36Sopenharmony_ci strcmp(name, XATTR_SELINUX_SUFFIX)) 342062306a36Sopenharmony_ci return -EOPNOTSUPP; 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci /* 342362306a36Sopenharmony_ci * If the caller has CAP_MAC_ADMIN, then get the raw context 342462306a36Sopenharmony_ci * value even if it is not defined by current policy; otherwise, 342562306a36Sopenharmony_ci * use the in-core value under current policy. 342662306a36Sopenharmony_ci * Use the non-auditing forms of the permission checks since 342762306a36Sopenharmony_ci * getxattr may be called by unprivileged processes commonly 342862306a36Sopenharmony_ci * and lack of permission just means that we fall back to the 342962306a36Sopenharmony_ci * in-core context value, not a denial. 343062306a36Sopenharmony_ci */ 343162306a36Sopenharmony_ci isec = inode_security(inode); 343262306a36Sopenharmony_ci if (has_cap_mac_admin(false)) 343362306a36Sopenharmony_ci error = security_sid_to_context_force(isec->sid, &context, 343462306a36Sopenharmony_ci &size); 343562306a36Sopenharmony_ci else 343662306a36Sopenharmony_ci error = security_sid_to_context(isec->sid, 343762306a36Sopenharmony_ci &context, &size); 343862306a36Sopenharmony_ci if (error) 343962306a36Sopenharmony_ci return error; 344062306a36Sopenharmony_ci error = size; 344162306a36Sopenharmony_ci if (alloc) { 344262306a36Sopenharmony_ci *buffer = context; 344362306a36Sopenharmony_ci goto out_nofree; 344462306a36Sopenharmony_ci } 344562306a36Sopenharmony_ci kfree(context); 344662306a36Sopenharmony_ciout_nofree: 344762306a36Sopenharmony_ci return error; 344862306a36Sopenharmony_ci} 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_cistatic int selinux_inode_setsecurity(struct inode *inode, const char *name, 345162306a36Sopenharmony_ci const void *value, size_t size, int flags) 345262306a36Sopenharmony_ci{ 345362306a36Sopenharmony_ci struct inode_security_struct *isec = inode_security_novalidate(inode); 345462306a36Sopenharmony_ci struct superblock_security_struct *sbsec; 345562306a36Sopenharmony_ci u32 newsid; 345662306a36Sopenharmony_ci int rc; 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci if (strcmp(name, XATTR_SELINUX_SUFFIX)) 345962306a36Sopenharmony_ci return -EOPNOTSUPP; 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci sbsec = selinux_superblock(inode->i_sb); 346262306a36Sopenharmony_ci if (!(sbsec->flags & SBLABEL_MNT)) 346362306a36Sopenharmony_ci return -EOPNOTSUPP; 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci if (!value || !size) 346662306a36Sopenharmony_ci return -EACCES; 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci rc = security_context_to_sid(value, size, &newsid, 346962306a36Sopenharmony_ci GFP_KERNEL); 347062306a36Sopenharmony_ci if (rc) 347162306a36Sopenharmony_ci return rc; 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci spin_lock(&isec->lock); 347462306a36Sopenharmony_ci isec->sclass = inode_mode_to_security_class(inode->i_mode); 347562306a36Sopenharmony_ci isec->sid = newsid; 347662306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 347762306a36Sopenharmony_ci spin_unlock(&isec->lock); 347862306a36Sopenharmony_ci return 0; 347962306a36Sopenharmony_ci} 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_cistatic int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) 348262306a36Sopenharmony_ci{ 348362306a36Sopenharmony_ci const int len = sizeof(XATTR_NAME_SELINUX); 348462306a36Sopenharmony_ci 348562306a36Sopenharmony_ci if (!selinux_initialized()) 348662306a36Sopenharmony_ci return 0; 348762306a36Sopenharmony_ci 348862306a36Sopenharmony_ci if (buffer && len <= buffer_size) 348962306a36Sopenharmony_ci memcpy(buffer, XATTR_NAME_SELINUX, len); 349062306a36Sopenharmony_ci return len; 349162306a36Sopenharmony_ci} 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_cistatic void selinux_inode_getsecid(struct inode *inode, u32 *secid) 349462306a36Sopenharmony_ci{ 349562306a36Sopenharmony_ci struct inode_security_struct *isec = inode_security_novalidate(inode); 349662306a36Sopenharmony_ci *secid = isec->sid; 349762306a36Sopenharmony_ci} 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_cistatic int selinux_inode_copy_up(struct dentry *src, struct cred **new) 350062306a36Sopenharmony_ci{ 350162306a36Sopenharmony_ci u32 sid; 350262306a36Sopenharmony_ci struct task_security_struct *tsec; 350362306a36Sopenharmony_ci struct cred *new_creds = *new; 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci if (new_creds == NULL) { 350662306a36Sopenharmony_ci new_creds = prepare_creds(); 350762306a36Sopenharmony_ci if (!new_creds) 350862306a36Sopenharmony_ci return -ENOMEM; 350962306a36Sopenharmony_ci } 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_ci tsec = selinux_cred(new_creds); 351262306a36Sopenharmony_ci /* Get label from overlay inode and set it in create_sid */ 351362306a36Sopenharmony_ci selinux_inode_getsecid(d_inode(src), &sid); 351462306a36Sopenharmony_ci tsec->create_sid = sid; 351562306a36Sopenharmony_ci *new = new_creds; 351662306a36Sopenharmony_ci return 0; 351762306a36Sopenharmony_ci} 351862306a36Sopenharmony_ci 351962306a36Sopenharmony_cistatic int selinux_inode_copy_up_xattr(const char *name) 352062306a36Sopenharmony_ci{ 352162306a36Sopenharmony_ci /* The copy_up hook above sets the initial context on an inode, but we 352262306a36Sopenharmony_ci * don't then want to overwrite it by blindly copying all the lower 352362306a36Sopenharmony_ci * xattrs up. Instead, we have to filter out SELinux-related xattrs. 352462306a36Sopenharmony_ci */ 352562306a36Sopenharmony_ci if (strcmp(name, XATTR_NAME_SELINUX) == 0) 352662306a36Sopenharmony_ci return 1; /* Discard */ 352762306a36Sopenharmony_ci /* 352862306a36Sopenharmony_ci * Any other attribute apart from SELINUX is not claimed, supported 352962306a36Sopenharmony_ci * by selinux. 353062306a36Sopenharmony_ci */ 353162306a36Sopenharmony_ci return -EOPNOTSUPP; 353262306a36Sopenharmony_ci} 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci/* kernfs node operations */ 353562306a36Sopenharmony_ci 353662306a36Sopenharmony_cistatic int selinux_kernfs_init_security(struct kernfs_node *kn_dir, 353762306a36Sopenharmony_ci struct kernfs_node *kn) 353862306a36Sopenharmony_ci{ 353962306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 354062306a36Sopenharmony_ci u32 parent_sid, newsid, clen; 354162306a36Sopenharmony_ci int rc; 354262306a36Sopenharmony_ci char *context; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci rc = kernfs_xattr_get(kn_dir, XATTR_NAME_SELINUX, NULL, 0); 354562306a36Sopenharmony_ci if (rc == -ENODATA) 354662306a36Sopenharmony_ci return 0; 354762306a36Sopenharmony_ci else if (rc < 0) 354862306a36Sopenharmony_ci return rc; 354962306a36Sopenharmony_ci 355062306a36Sopenharmony_ci clen = (u32)rc; 355162306a36Sopenharmony_ci context = kmalloc(clen, GFP_KERNEL); 355262306a36Sopenharmony_ci if (!context) 355362306a36Sopenharmony_ci return -ENOMEM; 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci rc = kernfs_xattr_get(kn_dir, XATTR_NAME_SELINUX, context, clen); 355662306a36Sopenharmony_ci if (rc < 0) { 355762306a36Sopenharmony_ci kfree(context); 355862306a36Sopenharmony_ci return rc; 355962306a36Sopenharmony_ci } 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci rc = security_context_to_sid(context, clen, &parent_sid, 356262306a36Sopenharmony_ci GFP_KERNEL); 356362306a36Sopenharmony_ci kfree(context); 356462306a36Sopenharmony_ci if (rc) 356562306a36Sopenharmony_ci return rc; 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci if (tsec->create_sid) { 356862306a36Sopenharmony_ci newsid = tsec->create_sid; 356962306a36Sopenharmony_ci } else { 357062306a36Sopenharmony_ci u16 secclass = inode_mode_to_security_class(kn->mode); 357162306a36Sopenharmony_ci struct qstr q; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci q.name = kn->name; 357462306a36Sopenharmony_ci q.hash_len = hashlen_string(kn_dir, kn->name); 357562306a36Sopenharmony_ci 357662306a36Sopenharmony_ci rc = security_transition_sid(tsec->sid, 357762306a36Sopenharmony_ci parent_sid, secclass, &q, 357862306a36Sopenharmony_ci &newsid); 357962306a36Sopenharmony_ci if (rc) 358062306a36Sopenharmony_ci return rc; 358162306a36Sopenharmony_ci } 358262306a36Sopenharmony_ci 358362306a36Sopenharmony_ci rc = security_sid_to_context_force(newsid, 358462306a36Sopenharmony_ci &context, &clen); 358562306a36Sopenharmony_ci if (rc) 358662306a36Sopenharmony_ci return rc; 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_ci rc = kernfs_xattr_set(kn, XATTR_NAME_SELINUX, context, clen, 358962306a36Sopenharmony_ci XATTR_CREATE); 359062306a36Sopenharmony_ci kfree(context); 359162306a36Sopenharmony_ci return rc; 359262306a36Sopenharmony_ci} 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci 359562306a36Sopenharmony_ci/* file security operations */ 359662306a36Sopenharmony_ci 359762306a36Sopenharmony_cistatic int selinux_revalidate_file_permission(struct file *file, int mask) 359862306a36Sopenharmony_ci{ 359962306a36Sopenharmony_ci const struct cred *cred = current_cred(); 360062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci /* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */ 360362306a36Sopenharmony_ci if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE)) 360462306a36Sopenharmony_ci mask |= MAY_APPEND; 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci return file_has_perm(cred, file, 360762306a36Sopenharmony_ci file_mask_to_av(inode->i_mode, mask)); 360862306a36Sopenharmony_ci} 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_cistatic int selinux_file_permission(struct file *file, int mask) 361162306a36Sopenharmony_ci{ 361262306a36Sopenharmony_ci struct inode *inode = file_inode(file); 361362306a36Sopenharmony_ci struct file_security_struct *fsec = selinux_file(file); 361462306a36Sopenharmony_ci struct inode_security_struct *isec; 361562306a36Sopenharmony_ci u32 sid = current_sid(); 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci if (!mask) 361862306a36Sopenharmony_ci /* No permission to check. Existence test. */ 361962306a36Sopenharmony_ci return 0; 362062306a36Sopenharmony_ci 362162306a36Sopenharmony_ci isec = inode_security(inode); 362262306a36Sopenharmony_ci if (sid == fsec->sid && fsec->isid == isec->sid && 362362306a36Sopenharmony_ci fsec->pseqno == avc_policy_seqno()) 362462306a36Sopenharmony_ci /* No change since file_open check. */ 362562306a36Sopenharmony_ci return 0; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci return selinux_revalidate_file_permission(file, mask); 362862306a36Sopenharmony_ci} 362962306a36Sopenharmony_ci 363062306a36Sopenharmony_cistatic int selinux_file_alloc_security(struct file *file) 363162306a36Sopenharmony_ci{ 363262306a36Sopenharmony_ci struct file_security_struct *fsec = selinux_file(file); 363362306a36Sopenharmony_ci u32 sid = current_sid(); 363462306a36Sopenharmony_ci 363562306a36Sopenharmony_ci fsec->sid = sid; 363662306a36Sopenharmony_ci fsec->fown_sid = sid; 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_ci return 0; 363962306a36Sopenharmony_ci} 364062306a36Sopenharmony_ci 364162306a36Sopenharmony_ci/* 364262306a36Sopenharmony_ci * Check whether a task has the ioctl permission and cmd 364362306a36Sopenharmony_ci * operation to an inode. 364462306a36Sopenharmony_ci */ 364562306a36Sopenharmony_cistatic int ioctl_has_perm(const struct cred *cred, struct file *file, 364662306a36Sopenharmony_ci u32 requested, u16 cmd) 364762306a36Sopenharmony_ci{ 364862306a36Sopenharmony_ci struct common_audit_data ad; 364962306a36Sopenharmony_ci struct file_security_struct *fsec = selinux_file(file); 365062306a36Sopenharmony_ci struct inode *inode = file_inode(file); 365162306a36Sopenharmony_ci struct inode_security_struct *isec; 365262306a36Sopenharmony_ci struct lsm_ioctlop_audit ioctl; 365362306a36Sopenharmony_ci u32 ssid = cred_sid(cred); 365462306a36Sopenharmony_ci int rc; 365562306a36Sopenharmony_ci u8 driver = cmd >> 8; 365662306a36Sopenharmony_ci u8 xperm = cmd & 0xff; 365762306a36Sopenharmony_ci 365862306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IOCTL_OP; 365962306a36Sopenharmony_ci ad.u.op = &ioctl; 366062306a36Sopenharmony_ci ad.u.op->cmd = cmd; 366162306a36Sopenharmony_ci ad.u.op->path = file->f_path; 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci if (ssid != fsec->sid) { 366462306a36Sopenharmony_ci rc = avc_has_perm(ssid, fsec->sid, 366562306a36Sopenharmony_ci SECCLASS_FD, 366662306a36Sopenharmony_ci FD__USE, 366762306a36Sopenharmony_ci &ad); 366862306a36Sopenharmony_ci if (rc) 366962306a36Sopenharmony_ci goto out; 367062306a36Sopenharmony_ci } 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci if (unlikely(IS_PRIVATE(inode))) 367362306a36Sopenharmony_ci return 0; 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci isec = inode_security(inode); 367662306a36Sopenharmony_ci rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass, 367762306a36Sopenharmony_ci requested, driver, xperm, &ad); 367862306a36Sopenharmony_ciout: 367962306a36Sopenharmony_ci return rc; 368062306a36Sopenharmony_ci} 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_cistatic int selinux_file_ioctl(struct file *file, unsigned int cmd, 368362306a36Sopenharmony_ci unsigned long arg) 368462306a36Sopenharmony_ci{ 368562306a36Sopenharmony_ci const struct cred *cred = current_cred(); 368662306a36Sopenharmony_ci int error = 0; 368762306a36Sopenharmony_ci 368862306a36Sopenharmony_ci switch (cmd) { 368962306a36Sopenharmony_ci case FIONREAD: 369062306a36Sopenharmony_ci case FIBMAP: 369162306a36Sopenharmony_ci case FIGETBSZ: 369262306a36Sopenharmony_ci case FS_IOC_GETFLAGS: 369362306a36Sopenharmony_ci case FS_IOC_GETVERSION: 369462306a36Sopenharmony_ci error = file_has_perm(cred, file, FILE__GETATTR); 369562306a36Sopenharmony_ci break; 369662306a36Sopenharmony_ci 369762306a36Sopenharmony_ci case FS_IOC_SETFLAGS: 369862306a36Sopenharmony_ci case FS_IOC_SETVERSION: 369962306a36Sopenharmony_ci error = file_has_perm(cred, file, FILE__SETATTR); 370062306a36Sopenharmony_ci break; 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci /* sys_ioctl() checks */ 370362306a36Sopenharmony_ci case FIONBIO: 370462306a36Sopenharmony_ci case FIOASYNC: 370562306a36Sopenharmony_ci error = file_has_perm(cred, file, 0); 370662306a36Sopenharmony_ci break; 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ci case KDSKBENT: 370962306a36Sopenharmony_ci case KDSKBSENT: 371062306a36Sopenharmony_ci error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG, 371162306a36Sopenharmony_ci CAP_OPT_NONE, true); 371262306a36Sopenharmony_ci break; 371362306a36Sopenharmony_ci 371462306a36Sopenharmony_ci case FIOCLEX: 371562306a36Sopenharmony_ci case FIONCLEX: 371662306a36Sopenharmony_ci if (!selinux_policycap_ioctl_skip_cloexec()) 371762306a36Sopenharmony_ci error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd); 371862306a36Sopenharmony_ci break; 371962306a36Sopenharmony_ci 372062306a36Sopenharmony_ci /* default case assumes that the command will go 372162306a36Sopenharmony_ci * to the file's ioctl() function. 372262306a36Sopenharmony_ci */ 372362306a36Sopenharmony_ci default: 372462306a36Sopenharmony_ci error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd); 372562306a36Sopenharmony_ci } 372662306a36Sopenharmony_ci return error; 372762306a36Sopenharmony_ci} 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_cistatic int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, 373062306a36Sopenharmony_ci unsigned long arg) 373162306a36Sopenharmony_ci{ 373262306a36Sopenharmony_ci /* 373362306a36Sopenharmony_ci * If we are in a 64-bit kernel running 32-bit userspace, we need to 373462306a36Sopenharmony_ci * make sure we don't compare 32-bit flags to 64-bit flags. 373562306a36Sopenharmony_ci */ 373662306a36Sopenharmony_ci switch (cmd) { 373762306a36Sopenharmony_ci case FS_IOC32_GETFLAGS: 373862306a36Sopenharmony_ci cmd = FS_IOC_GETFLAGS; 373962306a36Sopenharmony_ci break; 374062306a36Sopenharmony_ci case FS_IOC32_SETFLAGS: 374162306a36Sopenharmony_ci cmd = FS_IOC_SETFLAGS; 374262306a36Sopenharmony_ci break; 374362306a36Sopenharmony_ci case FS_IOC32_GETVERSION: 374462306a36Sopenharmony_ci cmd = FS_IOC_GETVERSION; 374562306a36Sopenharmony_ci break; 374662306a36Sopenharmony_ci case FS_IOC32_SETVERSION: 374762306a36Sopenharmony_ci cmd = FS_IOC_SETVERSION; 374862306a36Sopenharmony_ci break; 374962306a36Sopenharmony_ci default: 375062306a36Sopenharmony_ci break; 375162306a36Sopenharmony_ci } 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ci return selinux_file_ioctl(file, cmd, arg); 375462306a36Sopenharmony_ci} 375562306a36Sopenharmony_ci 375662306a36Sopenharmony_cistatic int default_noexec __ro_after_init; 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_cistatic int file_map_prot_check(struct file *file, unsigned long prot, int shared) 375962306a36Sopenharmony_ci{ 376062306a36Sopenharmony_ci const struct cred *cred = current_cred(); 376162306a36Sopenharmony_ci u32 sid = cred_sid(cred); 376262306a36Sopenharmony_ci int rc = 0; 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci if (default_noexec && 376562306a36Sopenharmony_ci (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) || 376662306a36Sopenharmony_ci (!shared && (prot & PROT_WRITE)))) { 376762306a36Sopenharmony_ci /* 376862306a36Sopenharmony_ci * We are making executable an anonymous mapping or a 376962306a36Sopenharmony_ci * private file mapping that will also be writable. 377062306a36Sopenharmony_ci * This has an additional check. 377162306a36Sopenharmony_ci */ 377262306a36Sopenharmony_ci rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, 377362306a36Sopenharmony_ci PROCESS__EXECMEM, NULL); 377462306a36Sopenharmony_ci if (rc) 377562306a36Sopenharmony_ci goto error; 377662306a36Sopenharmony_ci } 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci if (file) { 377962306a36Sopenharmony_ci /* read access is always possible with a mapping */ 378062306a36Sopenharmony_ci u32 av = FILE__READ; 378162306a36Sopenharmony_ci 378262306a36Sopenharmony_ci /* write access only matters if the mapping is shared */ 378362306a36Sopenharmony_ci if (shared && (prot & PROT_WRITE)) 378462306a36Sopenharmony_ci av |= FILE__WRITE; 378562306a36Sopenharmony_ci 378662306a36Sopenharmony_ci if (prot & PROT_EXEC) 378762306a36Sopenharmony_ci av |= FILE__EXECUTE; 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ci return file_has_perm(cred, file, av); 379062306a36Sopenharmony_ci } 379162306a36Sopenharmony_ci 379262306a36Sopenharmony_cierror: 379362306a36Sopenharmony_ci return rc; 379462306a36Sopenharmony_ci} 379562306a36Sopenharmony_ci 379662306a36Sopenharmony_cistatic int selinux_mmap_addr(unsigned long addr) 379762306a36Sopenharmony_ci{ 379862306a36Sopenharmony_ci int rc = 0; 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { 380162306a36Sopenharmony_ci u32 sid = current_sid(); 380262306a36Sopenharmony_ci rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, 380362306a36Sopenharmony_ci MEMPROTECT__MMAP_ZERO, NULL); 380462306a36Sopenharmony_ci } 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci return rc; 380762306a36Sopenharmony_ci} 380862306a36Sopenharmony_ci 380962306a36Sopenharmony_cistatic int selinux_mmap_file(struct file *file, 381062306a36Sopenharmony_ci unsigned long reqprot __always_unused, 381162306a36Sopenharmony_ci unsigned long prot, unsigned long flags) 381262306a36Sopenharmony_ci{ 381362306a36Sopenharmony_ci struct common_audit_data ad; 381462306a36Sopenharmony_ci int rc; 381562306a36Sopenharmony_ci 381662306a36Sopenharmony_ci if (file) { 381762306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_FILE; 381862306a36Sopenharmony_ci ad.u.file = file; 381962306a36Sopenharmony_ci rc = inode_has_perm(current_cred(), file_inode(file), 382062306a36Sopenharmony_ci FILE__MAP, &ad); 382162306a36Sopenharmony_ci if (rc) 382262306a36Sopenharmony_ci return rc; 382362306a36Sopenharmony_ci } 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci return file_map_prot_check(file, prot, 382662306a36Sopenharmony_ci (flags & MAP_TYPE) == MAP_SHARED); 382762306a36Sopenharmony_ci} 382862306a36Sopenharmony_ci 382962306a36Sopenharmony_cistatic int selinux_file_mprotect(struct vm_area_struct *vma, 383062306a36Sopenharmony_ci unsigned long reqprot __always_unused, 383162306a36Sopenharmony_ci unsigned long prot) 383262306a36Sopenharmony_ci{ 383362306a36Sopenharmony_ci const struct cred *cred = current_cred(); 383462306a36Sopenharmony_ci u32 sid = cred_sid(cred); 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci if (default_noexec && 383762306a36Sopenharmony_ci (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { 383862306a36Sopenharmony_ci int rc = 0; 383962306a36Sopenharmony_ci if (vma_is_initial_heap(vma)) { 384062306a36Sopenharmony_ci rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, 384162306a36Sopenharmony_ci PROCESS__EXECHEAP, NULL); 384262306a36Sopenharmony_ci } else if (!vma->vm_file && (vma_is_initial_stack(vma) || 384362306a36Sopenharmony_ci vma_is_stack_for_current(vma))) { 384462306a36Sopenharmony_ci rc = avc_has_perm(sid, sid, SECCLASS_PROCESS, 384562306a36Sopenharmony_ci PROCESS__EXECSTACK, NULL); 384662306a36Sopenharmony_ci } else if (vma->vm_file && vma->anon_vma) { 384762306a36Sopenharmony_ci /* 384862306a36Sopenharmony_ci * We are making executable a file mapping that has 384962306a36Sopenharmony_ci * had some COW done. Since pages might have been 385062306a36Sopenharmony_ci * written, check ability to execute the possibly 385162306a36Sopenharmony_ci * modified content. This typically should only 385262306a36Sopenharmony_ci * occur for text relocations. 385362306a36Sopenharmony_ci */ 385462306a36Sopenharmony_ci rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD); 385562306a36Sopenharmony_ci } 385662306a36Sopenharmony_ci if (rc) 385762306a36Sopenharmony_ci return rc; 385862306a36Sopenharmony_ci } 385962306a36Sopenharmony_ci 386062306a36Sopenharmony_ci return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); 386162306a36Sopenharmony_ci} 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_cistatic int selinux_file_lock(struct file *file, unsigned int cmd) 386462306a36Sopenharmony_ci{ 386562306a36Sopenharmony_ci const struct cred *cred = current_cred(); 386662306a36Sopenharmony_ci 386762306a36Sopenharmony_ci return file_has_perm(cred, file, FILE__LOCK); 386862306a36Sopenharmony_ci} 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_cistatic int selinux_file_fcntl(struct file *file, unsigned int cmd, 387162306a36Sopenharmony_ci unsigned long arg) 387262306a36Sopenharmony_ci{ 387362306a36Sopenharmony_ci const struct cred *cred = current_cred(); 387462306a36Sopenharmony_ci int err = 0; 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_ci switch (cmd) { 387762306a36Sopenharmony_ci case F_SETFL: 387862306a36Sopenharmony_ci if ((file->f_flags & O_APPEND) && !(arg & O_APPEND)) { 387962306a36Sopenharmony_ci err = file_has_perm(cred, file, FILE__WRITE); 388062306a36Sopenharmony_ci break; 388162306a36Sopenharmony_ci } 388262306a36Sopenharmony_ci fallthrough; 388362306a36Sopenharmony_ci case F_SETOWN: 388462306a36Sopenharmony_ci case F_SETSIG: 388562306a36Sopenharmony_ci case F_GETFL: 388662306a36Sopenharmony_ci case F_GETOWN: 388762306a36Sopenharmony_ci case F_GETSIG: 388862306a36Sopenharmony_ci case F_GETOWNER_UIDS: 388962306a36Sopenharmony_ci /* Just check FD__USE permission */ 389062306a36Sopenharmony_ci err = file_has_perm(cred, file, 0); 389162306a36Sopenharmony_ci break; 389262306a36Sopenharmony_ci case F_GETLK: 389362306a36Sopenharmony_ci case F_SETLK: 389462306a36Sopenharmony_ci case F_SETLKW: 389562306a36Sopenharmony_ci case F_OFD_GETLK: 389662306a36Sopenharmony_ci case F_OFD_SETLK: 389762306a36Sopenharmony_ci case F_OFD_SETLKW: 389862306a36Sopenharmony_ci#if BITS_PER_LONG == 32 389962306a36Sopenharmony_ci case F_GETLK64: 390062306a36Sopenharmony_ci case F_SETLK64: 390162306a36Sopenharmony_ci case F_SETLKW64: 390262306a36Sopenharmony_ci#endif 390362306a36Sopenharmony_ci err = file_has_perm(cred, file, FILE__LOCK); 390462306a36Sopenharmony_ci break; 390562306a36Sopenharmony_ci } 390662306a36Sopenharmony_ci 390762306a36Sopenharmony_ci return err; 390862306a36Sopenharmony_ci} 390962306a36Sopenharmony_ci 391062306a36Sopenharmony_cistatic void selinux_file_set_fowner(struct file *file) 391162306a36Sopenharmony_ci{ 391262306a36Sopenharmony_ci struct file_security_struct *fsec; 391362306a36Sopenharmony_ci 391462306a36Sopenharmony_ci fsec = selinux_file(file); 391562306a36Sopenharmony_ci fsec->fown_sid = current_sid(); 391662306a36Sopenharmony_ci} 391762306a36Sopenharmony_ci 391862306a36Sopenharmony_cistatic int selinux_file_send_sigiotask(struct task_struct *tsk, 391962306a36Sopenharmony_ci struct fown_struct *fown, int signum) 392062306a36Sopenharmony_ci{ 392162306a36Sopenharmony_ci struct file *file; 392262306a36Sopenharmony_ci u32 sid = task_sid_obj(tsk); 392362306a36Sopenharmony_ci u32 perm; 392462306a36Sopenharmony_ci struct file_security_struct *fsec; 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci /* struct fown_struct is never outside the context of a struct file */ 392762306a36Sopenharmony_ci file = container_of(fown, struct file, f_owner); 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci fsec = selinux_file(file); 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_ci if (!signum) 393262306a36Sopenharmony_ci perm = signal_to_av(SIGIO); /* as per send_sigio_to_task */ 393362306a36Sopenharmony_ci else 393462306a36Sopenharmony_ci perm = signal_to_av(signum); 393562306a36Sopenharmony_ci 393662306a36Sopenharmony_ci return avc_has_perm(fsec->fown_sid, sid, 393762306a36Sopenharmony_ci SECCLASS_PROCESS, perm, NULL); 393862306a36Sopenharmony_ci} 393962306a36Sopenharmony_ci 394062306a36Sopenharmony_cistatic int selinux_file_receive(struct file *file) 394162306a36Sopenharmony_ci{ 394262306a36Sopenharmony_ci const struct cred *cred = current_cred(); 394362306a36Sopenharmony_ci 394462306a36Sopenharmony_ci return file_has_perm(cred, file, file_to_av(file)); 394562306a36Sopenharmony_ci} 394662306a36Sopenharmony_ci 394762306a36Sopenharmony_cistatic int selinux_file_open(struct file *file) 394862306a36Sopenharmony_ci{ 394962306a36Sopenharmony_ci struct file_security_struct *fsec; 395062306a36Sopenharmony_ci struct inode_security_struct *isec; 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci fsec = selinux_file(file); 395362306a36Sopenharmony_ci isec = inode_security(file_inode(file)); 395462306a36Sopenharmony_ci /* 395562306a36Sopenharmony_ci * Save inode label and policy sequence number 395662306a36Sopenharmony_ci * at open-time so that selinux_file_permission 395762306a36Sopenharmony_ci * can determine whether revalidation is necessary. 395862306a36Sopenharmony_ci * Task label is already saved in the file security 395962306a36Sopenharmony_ci * struct as its SID. 396062306a36Sopenharmony_ci */ 396162306a36Sopenharmony_ci fsec->isid = isec->sid; 396262306a36Sopenharmony_ci fsec->pseqno = avc_policy_seqno(); 396362306a36Sopenharmony_ci /* 396462306a36Sopenharmony_ci * Since the inode label or policy seqno may have changed 396562306a36Sopenharmony_ci * between the selinux_inode_permission check and the saving 396662306a36Sopenharmony_ci * of state above, recheck that access is still permitted. 396762306a36Sopenharmony_ci * Otherwise, access might never be revalidated against the 396862306a36Sopenharmony_ci * new inode label or new policy. 396962306a36Sopenharmony_ci * This check is not redundant - do not remove. 397062306a36Sopenharmony_ci */ 397162306a36Sopenharmony_ci return file_path_has_perm(file->f_cred, file, open_file_to_av(file)); 397262306a36Sopenharmony_ci} 397362306a36Sopenharmony_ci 397462306a36Sopenharmony_ci/* task security operations */ 397562306a36Sopenharmony_ci 397662306a36Sopenharmony_cistatic int selinux_task_alloc(struct task_struct *task, 397762306a36Sopenharmony_ci unsigned long clone_flags) 397862306a36Sopenharmony_ci{ 397962306a36Sopenharmony_ci u32 sid = current_sid(); 398062306a36Sopenharmony_ci 398162306a36Sopenharmony_ci return avc_has_perm(sid, sid, SECCLASS_PROCESS, PROCESS__FORK, NULL); 398262306a36Sopenharmony_ci} 398362306a36Sopenharmony_ci 398462306a36Sopenharmony_ci/* 398562306a36Sopenharmony_ci * prepare a new set of credentials for modification 398662306a36Sopenharmony_ci */ 398762306a36Sopenharmony_cistatic int selinux_cred_prepare(struct cred *new, const struct cred *old, 398862306a36Sopenharmony_ci gfp_t gfp) 398962306a36Sopenharmony_ci{ 399062306a36Sopenharmony_ci const struct task_security_struct *old_tsec = selinux_cred(old); 399162306a36Sopenharmony_ci struct task_security_struct *tsec = selinux_cred(new); 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci *tsec = *old_tsec; 399462306a36Sopenharmony_ci return 0; 399562306a36Sopenharmony_ci} 399662306a36Sopenharmony_ci 399762306a36Sopenharmony_ci/* 399862306a36Sopenharmony_ci * transfer the SELinux data to a blank set of creds 399962306a36Sopenharmony_ci */ 400062306a36Sopenharmony_cistatic void selinux_cred_transfer(struct cred *new, const struct cred *old) 400162306a36Sopenharmony_ci{ 400262306a36Sopenharmony_ci const struct task_security_struct *old_tsec = selinux_cred(old); 400362306a36Sopenharmony_ci struct task_security_struct *tsec = selinux_cred(new); 400462306a36Sopenharmony_ci 400562306a36Sopenharmony_ci *tsec = *old_tsec; 400662306a36Sopenharmony_ci} 400762306a36Sopenharmony_ci 400862306a36Sopenharmony_cistatic void selinux_cred_getsecid(const struct cred *c, u32 *secid) 400962306a36Sopenharmony_ci{ 401062306a36Sopenharmony_ci *secid = cred_sid(c); 401162306a36Sopenharmony_ci} 401262306a36Sopenharmony_ci 401362306a36Sopenharmony_ci/* 401462306a36Sopenharmony_ci * set the security data for a kernel service 401562306a36Sopenharmony_ci * - all the creation contexts are set to unlabelled 401662306a36Sopenharmony_ci */ 401762306a36Sopenharmony_cistatic int selinux_kernel_act_as(struct cred *new, u32 secid) 401862306a36Sopenharmony_ci{ 401962306a36Sopenharmony_ci struct task_security_struct *tsec = selinux_cred(new); 402062306a36Sopenharmony_ci u32 sid = current_sid(); 402162306a36Sopenharmony_ci int ret; 402262306a36Sopenharmony_ci 402362306a36Sopenharmony_ci ret = avc_has_perm(sid, secid, 402462306a36Sopenharmony_ci SECCLASS_KERNEL_SERVICE, 402562306a36Sopenharmony_ci KERNEL_SERVICE__USE_AS_OVERRIDE, 402662306a36Sopenharmony_ci NULL); 402762306a36Sopenharmony_ci if (ret == 0) { 402862306a36Sopenharmony_ci tsec->sid = secid; 402962306a36Sopenharmony_ci tsec->create_sid = 0; 403062306a36Sopenharmony_ci tsec->keycreate_sid = 0; 403162306a36Sopenharmony_ci tsec->sockcreate_sid = 0; 403262306a36Sopenharmony_ci } 403362306a36Sopenharmony_ci return ret; 403462306a36Sopenharmony_ci} 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_ci/* 403762306a36Sopenharmony_ci * set the file creation context in a security record to the same as the 403862306a36Sopenharmony_ci * objective context of the specified inode 403962306a36Sopenharmony_ci */ 404062306a36Sopenharmony_cistatic int selinux_kernel_create_files_as(struct cred *new, struct inode *inode) 404162306a36Sopenharmony_ci{ 404262306a36Sopenharmony_ci struct inode_security_struct *isec = inode_security(inode); 404362306a36Sopenharmony_ci struct task_security_struct *tsec = selinux_cred(new); 404462306a36Sopenharmony_ci u32 sid = current_sid(); 404562306a36Sopenharmony_ci int ret; 404662306a36Sopenharmony_ci 404762306a36Sopenharmony_ci ret = avc_has_perm(sid, isec->sid, 404862306a36Sopenharmony_ci SECCLASS_KERNEL_SERVICE, 404962306a36Sopenharmony_ci KERNEL_SERVICE__CREATE_FILES_AS, 405062306a36Sopenharmony_ci NULL); 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci if (ret == 0) 405362306a36Sopenharmony_ci tsec->create_sid = isec->sid; 405462306a36Sopenharmony_ci return ret; 405562306a36Sopenharmony_ci} 405662306a36Sopenharmony_ci 405762306a36Sopenharmony_cistatic int selinux_kernel_module_request(char *kmod_name) 405862306a36Sopenharmony_ci{ 405962306a36Sopenharmony_ci struct common_audit_data ad; 406062306a36Sopenharmony_ci 406162306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_KMOD; 406262306a36Sopenharmony_ci ad.u.kmod_name = kmod_name; 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, SECCLASS_SYSTEM, 406562306a36Sopenharmony_ci SYSTEM__MODULE_REQUEST, &ad); 406662306a36Sopenharmony_ci} 406762306a36Sopenharmony_ci 406862306a36Sopenharmony_cistatic int selinux_kernel_module_from_file(struct file *file) 406962306a36Sopenharmony_ci{ 407062306a36Sopenharmony_ci struct common_audit_data ad; 407162306a36Sopenharmony_ci struct inode_security_struct *isec; 407262306a36Sopenharmony_ci struct file_security_struct *fsec; 407362306a36Sopenharmony_ci u32 sid = current_sid(); 407462306a36Sopenharmony_ci int rc; 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci /* init_module */ 407762306a36Sopenharmony_ci if (file == NULL) 407862306a36Sopenharmony_ci return avc_has_perm(sid, sid, SECCLASS_SYSTEM, 407962306a36Sopenharmony_ci SYSTEM__MODULE_LOAD, NULL); 408062306a36Sopenharmony_ci 408162306a36Sopenharmony_ci /* finit_module */ 408262306a36Sopenharmony_ci 408362306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_FILE; 408462306a36Sopenharmony_ci ad.u.file = file; 408562306a36Sopenharmony_ci 408662306a36Sopenharmony_ci fsec = selinux_file(file); 408762306a36Sopenharmony_ci if (sid != fsec->sid) { 408862306a36Sopenharmony_ci rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); 408962306a36Sopenharmony_ci if (rc) 409062306a36Sopenharmony_ci return rc; 409162306a36Sopenharmony_ci } 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_ci isec = inode_security(file_inode(file)); 409462306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, 409562306a36Sopenharmony_ci SYSTEM__MODULE_LOAD, &ad); 409662306a36Sopenharmony_ci} 409762306a36Sopenharmony_ci 409862306a36Sopenharmony_cistatic int selinux_kernel_read_file(struct file *file, 409962306a36Sopenharmony_ci enum kernel_read_file_id id, 410062306a36Sopenharmony_ci bool contents) 410162306a36Sopenharmony_ci{ 410262306a36Sopenharmony_ci int rc = 0; 410362306a36Sopenharmony_ci 410462306a36Sopenharmony_ci switch (id) { 410562306a36Sopenharmony_ci case READING_MODULE: 410662306a36Sopenharmony_ci rc = selinux_kernel_module_from_file(contents ? file : NULL); 410762306a36Sopenharmony_ci break; 410862306a36Sopenharmony_ci default: 410962306a36Sopenharmony_ci break; 411062306a36Sopenharmony_ci } 411162306a36Sopenharmony_ci 411262306a36Sopenharmony_ci return rc; 411362306a36Sopenharmony_ci} 411462306a36Sopenharmony_ci 411562306a36Sopenharmony_cistatic int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents) 411662306a36Sopenharmony_ci{ 411762306a36Sopenharmony_ci int rc = 0; 411862306a36Sopenharmony_ci 411962306a36Sopenharmony_ci switch (id) { 412062306a36Sopenharmony_ci case LOADING_MODULE: 412162306a36Sopenharmony_ci rc = selinux_kernel_module_from_file(NULL); 412262306a36Sopenharmony_ci break; 412362306a36Sopenharmony_ci default: 412462306a36Sopenharmony_ci break; 412562306a36Sopenharmony_ci } 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_ci return rc; 412862306a36Sopenharmony_ci} 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_cistatic int selinux_task_setpgid(struct task_struct *p, pid_t pgid) 413162306a36Sopenharmony_ci{ 413262306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 413362306a36Sopenharmony_ci PROCESS__SETPGID, NULL); 413462306a36Sopenharmony_ci} 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_cistatic int selinux_task_getpgid(struct task_struct *p) 413762306a36Sopenharmony_ci{ 413862306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 413962306a36Sopenharmony_ci PROCESS__GETPGID, NULL); 414062306a36Sopenharmony_ci} 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_cistatic int selinux_task_getsid(struct task_struct *p) 414362306a36Sopenharmony_ci{ 414462306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 414562306a36Sopenharmony_ci PROCESS__GETSESSION, NULL); 414662306a36Sopenharmony_ci} 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_cistatic void selinux_current_getsecid_subj(u32 *secid) 414962306a36Sopenharmony_ci{ 415062306a36Sopenharmony_ci *secid = current_sid(); 415162306a36Sopenharmony_ci} 415262306a36Sopenharmony_ci 415362306a36Sopenharmony_cistatic void selinux_task_getsecid_obj(struct task_struct *p, u32 *secid) 415462306a36Sopenharmony_ci{ 415562306a36Sopenharmony_ci *secid = task_sid_obj(p); 415662306a36Sopenharmony_ci} 415762306a36Sopenharmony_ci 415862306a36Sopenharmony_cistatic int selinux_task_setnice(struct task_struct *p, int nice) 415962306a36Sopenharmony_ci{ 416062306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 416162306a36Sopenharmony_ci PROCESS__SETSCHED, NULL); 416262306a36Sopenharmony_ci} 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_cistatic int selinux_task_setioprio(struct task_struct *p, int ioprio) 416562306a36Sopenharmony_ci{ 416662306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 416762306a36Sopenharmony_ci PROCESS__SETSCHED, NULL); 416862306a36Sopenharmony_ci} 416962306a36Sopenharmony_ci 417062306a36Sopenharmony_cistatic int selinux_task_getioprio(struct task_struct *p) 417162306a36Sopenharmony_ci{ 417262306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 417362306a36Sopenharmony_ci PROCESS__GETSCHED, NULL); 417462306a36Sopenharmony_ci} 417562306a36Sopenharmony_ci 417662306a36Sopenharmony_cistatic int selinux_task_prlimit(const struct cred *cred, const struct cred *tcred, 417762306a36Sopenharmony_ci unsigned int flags) 417862306a36Sopenharmony_ci{ 417962306a36Sopenharmony_ci u32 av = 0; 418062306a36Sopenharmony_ci 418162306a36Sopenharmony_ci if (!flags) 418262306a36Sopenharmony_ci return 0; 418362306a36Sopenharmony_ci if (flags & LSM_PRLIMIT_WRITE) 418462306a36Sopenharmony_ci av |= PROCESS__SETRLIMIT; 418562306a36Sopenharmony_ci if (flags & LSM_PRLIMIT_READ) 418662306a36Sopenharmony_ci av |= PROCESS__GETRLIMIT; 418762306a36Sopenharmony_ci return avc_has_perm(cred_sid(cred), cred_sid(tcred), 418862306a36Sopenharmony_ci SECCLASS_PROCESS, av, NULL); 418962306a36Sopenharmony_ci} 419062306a36Sopenharmony_ci 419162306a36Sopenharmony_cistatic int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, 419262306a36Sopenharmony_ci struct rlimit *new_rlim) 419362306a36Sopenharmony_ci{ 419462306a36Sopenharmony_ci struct rlimit *old_rlim = p->signal->rlim + resource; 419562306a36Sopenharmony_ci 419662306a36Sopenharmony_ci /* Control the ability to change the hard limit (whether 419762306a36Sopenharmony_ci lowering or raising it), so that the hard limit can 419862306a36Sopenharmony_ci later be used as a safe reset point for the soft limit 419962306a36Sopenharmony_ci upon context transitions. See selinux_bprm_committing_creds. */ 420062306a36Sopenharmony_ci if (old_rlim->rlim_max != new_rlim->rlim_max) 420162306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), 420262306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__SETRLIMIT, NULL); 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci return 0; 420562306a36Sopenharmony_ci} 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_cistatic int selinux_task_setscheduler(struct task_struct *p) 420862306a36Sopenharmony_ci{ 420962306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 421062306a36Sopenharmony_ci PROCESS__SETSCHED, NULL); 421162306a36Sopenharmony_ci} 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_cistatic int selinux_task_getscheduler(struct task_struct *p) 421462306a36Sopenharmony_ci{ 421562306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 421662306a36Sopenharmony_ci PROCESS__GETSCHED, NULL); 421762306a36Sopenharmony_ci} 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_cistatic int selinux_task_movememory(struct task_struct *p) 422062306a36Sopenharmony_ci{ 422162306a36Sopenharmony_ci return avc_has_perm(current_sid(), task_sid_obj(p), SECCLASS_PROCESS, 422262306a36Sopenharmony_ci PROCESS__SETSCHED, NULL); 422362306a36Sopenharmony_ci} 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_cistatic int selinux_task_kill(struct task_struct *p, struct kernel_siginfo *info, 422662306a36Sopenharmony_ci int sig, const struct cred *cred) 422762306a36Sopenharmony_ci{ 422862306a36Sopenharmony_ci u32 secid; 422962306a36Sopenharmony_ci u32 perm; 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci if (!sig) 423262306a36Sopenharmony_ci perm = PROCESS__SIGNULL; /* null signal; existence test */ 423362306a36Sopenharmony_ci else 423462306a36Sopenharmony_ci perm = signal_to_av(sig); 423562306a36Sopenharmony_ci if (!cred) 423662306a36Sopenharmony_ci secid = current_sid(); 423762306a36Sopenharmony_ci else 423862306a36Sopenharmony_ci secid = cred_sid(cred); 423962306a36Sopenharmony_ci return avc_has_perm(secid, task_sid_obj(p), SECCLASS_PROCESS, perm, NULL); 424062306a36Sopenharmony_ci} 424162306a36Sopenharmony_ci 424262306a36Sopenharmony_cistatic void selinux_task_to_inode(struct task_struct *p, 424362306a36Sopenharmony_ci struct inode *inode) 424462306a36Sopenharmony_ci{ 424562306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 424662306a36Sopenharmony_ci u32 sid = task_sid_obj(p); 424762306a36Sopenharmony_ci 424862306a36Sopenharmony_ci spin_lock(&isec->lock); 424962306a36Sopenharmony_ci isec->sclass = inode_mode_to_security_class(inode->i_mode); 425062306a36Sopenharmony_ci isec->sid = sid; 425162306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 425262306a36Sopenharmony_ci spin_unlock(&isec->lock); 425362306a36Sopenharmony_ci} 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_cistatic int selinux_userns_create(const struct cred *cred) 425662306a36Sopenharmony_ci{ 425762306a36Sopenharmony_ci u32 sid = current_sid(); 425862306a36Sopenharmony_ci 425962306a36Sopenharmony_ci return avc_has_perm(sid, sid, SECCLASS_USER_NAMESPACE, 426062306a36Sopenharmony_ci USER_NAMESPACE__CREATE, NULL); 426162306a36Sopenharmony_ci} 426262306a36Sopenharmony_ci 426362306a36Sopenharmony_ci/* Returns error only if unable to parse addresses */ 426462306a36Sopenharmony_cistatic int selinux_parse_skb_ipv4(struct sk_buff *skb, 426562306a36Sopenharmony_ci struct common_audit_data *ad, u8 *proto) 426662306a36Sopenharmony_ci{ 426762306a36Sopenharmony_ci int offset, ihlen, ret = -EINVAL; 426862306a36Sopenharmony_ci struct iphdr _iph, *ih; 426962306a36Sopenharmony_ci 427062306a36Sopenharmony_ci offset = skb_network_offset(skb); 427162306a36Sopenharmony_ci ih = skb_header_pointer(skb, offset, sizeof(_iph), &_iph); 427262306a36Sopenharmony_ci if (ih == NULL) 427362306a36Sopenharmony_ci goto out; 427462306a36Sopenharmony_ci 427562306a36Sopenharmony_ci ihlen = ih->ihl * 4; 427662306a36Sopenharmony_ci if (ihlen < sizeof(_iph)) 427762306a36Sopenharmony_ci goto out; 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci ad->u.net->v4info.saddr = ih->saddr; 428062306a36Sopenharmony_ci ad->u.net->v4info.daddr = ih->daddr; 428162306a36Sopenharmony_ci ret = 0; 428262306a36Sopenharmony_ci 428362306a36Sopenharmony_ci if (proto) 428462306a36Sopenharmony_ci *proto = ih->protocol; 428562306a36Sopenharmony_ci 428662306a36Sopenharmony_ci switch (ih->protocol) { 428762306a36Sopenharmony_ci case IPPROTO_TCP: { 428862306a36Sopenharmony_ci struct tcphdr _tcph, *th; 428962306a36Sopenharmony_ci 429062306a36Sopenharmony_ci if (ntohs(ih->frag_off) & IP_OFFSET) 429162306a36Sopenharmony_ci break; 429262306a36Sopenharmony_ci 429362306a36Sopenharmony_ci offset += ihlen; 429462306a36Sopenharmony_ci th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); 429562306a36Sopenharmony_ci if (th == NULL) 429662306a36Sopenharmony_ci break; 429762306a36Sopenharmony_ci 429862306a36Sopenharmony_ci ad->u.net->sport = th->source; 429962306a36Sopenharmony_ci ad->u.net->dport = th->dest; 430062306a36Sopenharmony_ci break; 430162306a36Sopenharmony_ci } 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci case IPPROTO_UDP: { 430462306a36Sopenharmony_ci struct udphdr _udph, *uh; 430562306a36Sopenharmony_ci 430662306a36Sopenharmony_ci if (ntohs(ih->frag_off) & IP_OFFSET) 430762306a36Sopenharmony_ci break; 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ci offset += ihlen; 431062306a36Sopenharmony_ci uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); 431162306a36Sopenharmony_ci if (uh == NULL) 431262306a36Sopenharmony_ci break; 431362306a36Sopenharmony_ci 431462306a36Sopenharmony_ci ad->u.net->sport = uh->source; 431562306a36Sopenharmony_ci ad->u.net->dport = uh->dest; 431662306a36Sopenharmony_ci break; 431762306a36Sopenharmony_ci } 431862306a36Sopenharmony_ci 431962306a36Sopenharmony_ci case IPPROTO_DCCP: { 432062306a36Sopenharmony_ci struct dccp_hdr _dccph, *dh; 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci if (ntohs(ih->frag_off) & IP_OFFSET) 432362306a36Sopenharmony_ci break; 432462306a36Sopenharmony_ci 432562306a36Sopenharmony_ci offset += ihlen; 432662306a36Sopenharmony_ci dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); 432762306a36Sopenharmony_ci if (dh == NULL) 432862306a36Sopenharmony_ci break; 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci ad->u.net->sport = dh->dccph_sport; 433162306a36Sopenharmony_ci ad->u.net->dport = dh->dccph_dport; 433262306a36Sopenharmony_ci break; 433362306a36Sopenharmony_ci } 433462306a36Sopenharmony_ci 433562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IP_SCTP) 433662306a36Sopenharmony_ci case IPPROTO_SCTP: { 433762306a36Sopenharmony_ci struct sctphdr _sctph, *sh; 433862306a36Sopenharmony_ci 433962306a36Sopenharmony_ci if (ntohs(ih->frag_off) & IP_OFFSET) 434062306a36Sopenharmony_ci break; 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci offset += ihlen; 434362306a36Sopenharmony_ci sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); 434462306a36Sopenharmony_ci if (sh == NULL) 434562306a36Sopenharmony_ci break; 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci ad->u.net->sport = sh->source; 434862306a36Sopenharmony_ci ad->u.net->dport = sh->dest; 434962306a36Sopenharmony_ci break; 435062306a36Sopenharmony_ci } 435162306a36Sopenharmony_ci#endif 435262306a36Sopenharmony_ci default: 435362306a36Sopenharmony_ci break; 435462306a36Sopenharmony_ci } 435562306a36Sopenharmony_ciout: 435662306a36Sopenharmony_ci return ret; 435762306a36Sopenharmony_ci} 435862306a36Sopenharmony_ci 435962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 436062306a36Sopenharmony_ci 436162306a36Sopenharmony_ci/* Returns error only if unable to parse addresses */ 436262306a36Sopenharmony_cistatic int selinux_parse_skb_ipv6(struct sk_buff *skb, 436362306a36Sopenharmony_ci struct common_audit_data *ad, u8 *proto) 436462306a36Sopenharmony_ci{ 436562306a36Sopenharmony_ci u8 nexthdr; 436662306a36Sopenharmony_ci int ret = -EINVAL, offset; 436762306a36Sopenharmony_ci struct ipv6hdr _ipv6h, *ip6; 436862306a36Sopenharmony_ci __be16 frag_off; 436962306a36Sopenharmony_ci 437062306a36Sopenharmony_ci offset = skb_network_offset(skb); 437162306a36Sopenharmony_ci ip6 = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h); 437262306a36Sopenharmony_ci if (ip6 == NULL) 437362306a36Sopenharmony_ci goto out; 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci ad->u.net->v6info.saddr = ip6->saddr; 437662306a36Sopenharmony_ci ad->u.net->v6info.daddr = ip6->daddr; 437762306a36Sopenharmony_ci ret = 0; 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci nexthdr = ip6->nexthdr; 438062306a36Sopenharmony_ci offset += sizeof(_ipv6h); 438162306a36Sopenharmony_ci offset = ipv6_skip_exthdr(skb, offset, &nexthdr, &frag_off); 438262306a36Sopenharmony_ci if (offset < 0) 438362306a36Sopenharmony_ci goto out; 438462306a36Sopenharmony_ci 438562306a36Sopenharmony_ci if (proto) 438662306a36Sopenharmony_ci *proto = nexthdr; 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci switch (nexthdr) { 438962306a36Sopenharmony_ci case IPPROTO_TCP: { 439062306a36Sopenharmony_ci struct tcphdr _tcph, *th; 439162306a36Sopenharmony_ci 439262306a36Sopenharmony_ci th = skb_header_pointer(skb, offset, sizeof(_tcph), &_tcph); 439362306a36Sopenharmony_ci if (th == NULL) 439462306a36Sopenharmony_ci break; 439562306a36Sopenharmony_ci 439662306a36Sopenharmony_ci ad->u.net->sport = th->source; 439762306a36Sopenharmony_ci ad->u.net->dport = th->dest; 439862306a36Sopenharmony_ci break; 439962306a36Sopenharmony_ci } 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_ci case IPPROTO_UDP: { 440262306a36Sopenharmony_ci struct udphdr _udph, *uh; 440362306a36Sopenharmony_ci 440462306a36Sopenharmony_ci uh = skb_header_pointer(skb, offset, sizeof(_udph), &_udph); 440562306a36Sopenharmony_ci if (uh == NULL) 440662306a36Sopenharmony_ci break; 440762306a36Sopenharmony_ci 440862306a36Sopenharmony_ci ad->u.net->sport = uh->source; 440962306a36Sopenharmony_ci ad->u.net->dport = uh->dest; 441062306a36Sopenharmony_ci break; 441162306a36Sopenharmony_ci } 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci case IPPROTO_DCCP: { 441462306a36Sopenharmony_ci struct dccp_hdr _dccph, *dh; 441562306a36Sopenharmony_ci 441662306a36Sopenharmony_ci dh = skb_header_pointer(skb, offset, sizeof(_dccph), &_dccph); 441762306a36Sopenharmony_ci if (dh == NULL) 441862306a36Sopenharmony_ci break; 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_ci ad->u.net->sport = dh->dccph_sport; 442162306a36Sopenharmony_ci ad->u.net->dport = dh->dccph_dport; 442262306a36Sopenharmony_ci break; 442362306a36Sopenharmony_ci } 442462306a36Sopenharmony_ci 442562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IP_SCTP) 442662306a36Sopenharmony_ci case IPPROTO_SCTP: { 442762306a36Sopenharmony_ci struct sctphdr _sctph, *sh; 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci sh = skb_header_pointer(skb, offset, sizeof(_sctph), &_sctph); 443062306a36Sopenharmony_ci if (sh == NULL) 443162306a36Sopenharmony_ci break; 443262306a36Sopenharmony_ci 443362306a36Sopenharmony_ci ad->u.net->sport = sh->source; 443462306a36Sopenharmony_ci ad->u.net->dport = sh->dest; 443562306a36Sopenharmony_ci break; 443662306a36Sopenharmony_ci } 443762306a36Sopenharmony_ci#endif 443862306a36Sopenharmony_ci /* includes fragments */ 443962306a36Sopenharmony_ci default: 444062306a36Sopenharmony_ci break; 444162306a36Sopenharmony_ci } 444262306a36Sopenharmony_ciout: 444362306a36Sopenharmony_ci return ret; 444462306a36Sopenharmony_ci} 444562306a36Sopenharmony_ci 444662306a36Sopenharmony_ci#endif /* IPV6 */ 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_cistatic int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad, 444962306a36Sopenharmony_ci char **_addrp, int src, u8 *proto) 445062306a36Sopenharmony_ci{ 445162306a36Sopenharmony_ci char *addrp; 445262306a36Sopenharmony_ci int ret; 445362306a36Sopenharmony_ci 445462306a36Sopenharmony_ci switch (ad->u.net->family) { 445562306a36Sopenharmony_ci case PF_INET: 445662306a36Sopenharmony_ci ret = selinux_parse_skb_ipv4(skb, ad, proto); 445762306a36Sopenharmony_ci if (ret) 445862306a36Sopenharmony_ci goto parse_error; 445962306a36Sopenharmony_ci addrp = (char *)(src ? &ad->u.net->v4info.saddr : 446062306a36Sopenharmony_ci &ad->u.net->v4info.daddr); 446162306a36Sopenharmony_ci goto okay; 446262306a36Sopenharmony_ci 446362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 446462306a36Sopenharmony_ci case PF_INET6: 446562306a36Sopenharmony_ci ret = selinux_parse_skb_ipv6(skb, ad, proto); 446662306a36Sopenharmony_ci if (ret) 446762306a36Sopenharmony_ci goto parse_error; 446862306a36Sopenharmony_ci addrp = (char *)(src ? &ad->u.net->v6info.saddr : 446962306a36Sopenharmony_ci &ad->u.net->v6info.daddr); 447062306a36Sopenharmony_ci goto okay; 447162306a36Sopenharmony_ci#endif /* IPV6 */ 447262306a36Sopenharmony_ci default: 447362306a36Sopenharmony_ci addrp = NULL; 447462306a36Sopenharmony_ci goto okay; 447562306a36Sopenharmony_ci } 447662306a36Sopenharmony_ci 447762306a36Sopenharmony_ciparse_error: 447862306a36Sopenharmony_ci pr_warn( 447962306a36Sopenharmony_ci "SELinux: failure in selinux_parse_skb()," 448062306a36Sopenharmony_ci " unable to parse packet\n"); 448162306a36Sopenharmony_ci return ret; 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ciokay: 448462306a36Sopenharmony_ci if (_addrp) 448562306a36Sopenharmony_ci *_addrp = addrp; 448662306a36Sopenharmony_ci return 0; 448762306a36Sopenharmony_ci} 448862306a36Sopenharmony_ci 448962306a36Sopenharmony_ci/** 449062306a36Sopenharmony_ci * selinux_skb_peerlbl_sid - Determine the peer label of a packet 449162306a36Sopenharmony_ci * @skb: the packet 449262306a36Sopenharmony_ci * @family: protocol family 449362306a36Sopenharmony_ci * @sid: the packet's peer label SID 449462306a36Sopenharmony_ci * 449562306a36Sopenharmony_ci * Description: 449662306a36Sopenharmony_ci * Check the various different forms of network peer labeling and determine 449762306a36Sopenharmony_ci * the peer label/SID for the packet; most of the magic actually occurs in 449862306a36Sopenharmony_ci * the security server function security_net_peersid_cmp(). The function 449962306a36Sopenharmony_ci * returns zero if the value in @sid is valid (although it may be SECSID_NULL) 450062306a36Sopenharmony_ci * or -EACCES if @sid is invalid due to inconsistencies with the different 450162306a36Sopenharmony_ci * peer labels. 450262306a36Sopenharmony_ci * 450362306a36Sopenharmony_ci */ 450462306a36Sopenharmony_cistatic int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) 450562306a36Sopenharmony_ci{ 450662306a36Sopenharmony_ci int err; 450762306a36Sopenharmony_ci u32 xfrm_sid; 450862306a36Sopenharmony_ci u32 nlbl_sid; 450962306a36Sopenharmony_ci u32 nlbl_type; 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci err = selinux_xfrm_skb_sid(skb, &xfrm_sid); 451262306a36Sopenharmony_ci if (unlikely(err)) 451362306a36Sopenharmony_ci return -EACCES; 451462306a36Sopenharmony_ci err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); 451562306a36Sopenharmony_ci if (unlikely(err)) 451662306a36Sopenharmony_ci return -EACCES; 451762306a36Sopenharmony_ci 451862306a36Sopenharmony_ci err = security_net_peersid_resolve(nlbl_sid, 451962306a36Sopenharmony_ci nlbl_type, xfrm_sid, sid); 452062306a36Sopenharmony_ci if (unlikely(err)) { 452162306a36Sopenharmony_ci pr_warn( 452262306a36Sopenharmony_ci "SELinux: failure in selinux_skb_peerlbl_sid()," 452362306a36Sopenharmony_ci " unable to determine packet's peer label\n"); 452462306a36Sopenharmony_ci return -EACCES; 452562306a36Sopenharmony_ci } 452662306a36Sopenharmony_ci 452762306a36Sopenharmony_ci return 0; 452862306a36Sopenharmony_ci} 452962306a36Sopenharmony_ci 453062306a36Sopenharmony_ci/** 453162306a36Sopenharmony_ci * selinux_conn_sid - Determine the child socket label for a connection 453262306a36Sopenharmony_ci * @sk_sid: the parent socket's SID 453362306a36Sopenharmony_ci * @skb_sid: the packet's SID 453462306a36Sopenharmony_ci * @conn_sid: the resulting connection SID 453562306a36Sopenharmony_ci * 453662306a36Sopenharmony_ci * If @skb_sid is valid then the user:role:type information from @sk_sid is 453762306a36Sopenharmony_ci * combined with the MLS information from @skb_sid in order to create 453862306a36Sopenharmony_ci * @conn_sid. If @skb_sid is not valid then @conn_sid is simply a copy 453962306a36Sopenharmony_ci * of @sk_sid. Returns zero on success, negative values on failure. 454062306a36Sopenharmony_ci * 454162306a36Sopenharmony_ci */ 454262306a36Sopenharmony_cistatic int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) 454362306a36Sopenharmony_ci{ 454462306a36Sopenharmony_ci int err = 0; 454562306a36Sopenharmony_ci 454662306a36Sopenharmony_ci if (skb_sid != SECSID_NULL) 454762306a36Sopenharmony_ci err = security_sid_mls_copy(sk_sid, skb_sid, 454862306a36Sopenharmony_ci conn_sid); 454962306a36Sopenharmony_ci else 455062306a36Sopenharmony_ci *conn_sid = sk_sid; 455162306a36Sopenharmony_ci 455262306a36Sopenharmony_ci return err; 455362306a36Sopenharmony_ci} 455462306a36Sopenharmony_ci 455562306a36Sopenharmony_ci/* socket security operations */ 455662306a36Sopenharmony_ci 455762306a36Sopenharmony_cistatic int socket_sockcreate_sid(const struct task_security_struct *tsec, 455862306a36Sopenharmony_ci u16 secclass, u32 *socksid) 455962306a36Sopenharmony_ci{ 456062306a36Sopenharmony_ci if (tsec->sockcreate_sid > SECSID_NULL) { 456162306a36Sopenharmony_ci *socksid = tsec->sockcreate_sid; 456262306a36Sopenharmony_ci return 0; 456362306a36Sopenharmony_ci } 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci return security_transition_sid(tsec->sid, tsec->sid, 456662306a36Sopenharmony_ci secclass, NULL, socksid); 456762306a36Sopenharmony_ci} 456862306a36Sopenharmony_ci 456962306a36Sopenharmony_cistatic int sock_has_perm(struct sock *sk, u32 perms) 457062306a36Sopenharmony_ci{ 457162306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 457262306a36Sopenharmony_ci struct common_audit_data ad; 457362306a36Sopenharmony_ci struct lsm_network_audit net; 457462306a36Sopenharmony_ci 457562306a36Sopenharmony_ci if (sksec->sid == SECINITSID_KERNEL) 457662306a36Sopenharmony_ci return 0; 457762306a36Sopenharmony_ci 457862306a36Sopenharmony_ci ad_net_init_from_sk(&ad, &net, sk); 457962306a36Sopenharmony_ci 458062306a36Sopenharmony_ci return avc_has_perm(current_sid(), sksec->sid, sksec->sclass, perms, 458162306a36Sopenharmony_ci &ad); 458262306a36Sopenharmony_ci} 458362306a36Sopenharmony_ci 458462306a36Sopenharmony_cistatic int selinux_socket_create(int family, int type, 458562306a36Sopenharmony_ci int protocol, int kern) 458662306a36Sopenharmony_ci{ 458762306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 458862306a36Sopenharmony_ci u32 newsid; 458962306a36Sopenharmony_ci u16 secclass; 459062306a36Sopenharmony_ci int rc; 459162306a36Sopenharmony_ci 459262306a36Sopenharmony_ci if (kern) 459362306a36Sopenharmony_ci return 0; 459462306a36Sopenharmony_ci 459562306a36Sopenharmony_ci secclass = socket_type_to_security_class(family, type, protocol); 459662306a36Sopenharmony_ci rc = socket_sockcreate_sid(tsec, secclass, &newsid); 459762306a36Sopenharmony_ci if (rc) 459862306a36Sopenharmony_ci return rc; 459962306a36Sopenharmony_ci 460062306a36Sopenharmony_ci return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); 460162306a36Sopenharmony_ci} 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_cistatic int selinux_socket_post_create(struct socket *sock, int family, 460462306a36Sopenharmony_ci int type, int protocol, int kern) 460562306a36Sopenharmony_ci{ 460662306a36Sopenharmony_ci const struct task_security_struct *tsec = selinux_cred(current_cred()); 460762306a36Sopenharmony_ci struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); 460862306a36Sopenharmony_ci struct sk_security_struct *sksec; 460962306a36Sopenharmony_ci u16 sclass = socket_type_to_security_class(family, type, protocol); 461062306a36Sopenharmony_ci u32 sid = SECINITSID_KERNEL; 461162306a36Sopenharmony_ci int err = 0; 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci if (!kern) { 461462306a36Sopenharmony_ci err = socket_sockcreate_sid(tsec, sclass, &sid); 461562306a36Sopenharmony_ci if (err) 461662306a36Sopenharmony_ci return err; 461762306a36Sopenharmony_ci } 461862306a36Sopenharmony_ci 461962306a36Sopenharmony_ci isec->sclass = sclass; 462062306a36Sopenharmony_ci isec->sid = sid; 462162306a36Sopenharmony_ci isec->initialized = LABEL_INITIALIZED; 462262306a36Sopenharmony_ci 462362306a36Sopenharmony_ci if (sock->sk) { 462462306a36Sopenharmony_ci sksec = sock->sk->sk_security; 462562306a36Sopenharmony_ci sksec->sclass = sclass; 462662306a36Sopenharmony_ci sksec->sid = sid; 462762306a36Sopenharmony_ci /* Allows detection of the first association on this socket */ 462862306a36Sopenharmony_ci if (sksec->sclass == SECCLASS_SCTP_SOCKET) 462962306a36Sopenharmony_ci sksec->sctp_assoc_state = SCTP_ASSOC_UNSET; 463062306a36Sopenharmony_ci 463162306a36Sopenharmony_ci err = selinux_netlbl_socket_post_create(sock->sk, family); 463262306a36Sopenharmony_ci } 463362306a36Sopenharmony_ci 463462306a36Sopenharmony_ci return err; 463562306a36Sopenharmony_ci} 463662306a36Sopenharmony_ci 463762306a36Sopenharmony_cistatic int selinux_socket_socketpair(struct socket *socka, 463862306a36Sopenharmony_ci struct socket *sockb) 463962306a36Sopenharmony_ci{ 464062306a36Sopenharmony_ci struct sk_security_struct *sksec_a = socka->sk->sk_security; 464162306a36Sopenharmony_ci struct sk_security_struct *sksec_b = sockb->sk->sk_security; 464262306a36Sopenharmony_ci 464362306a36Sopenharmony_ci sksec_a->peer_sid = sksec_b->sid; 464462306a36Sopenharmony_ci sksec_b->peer_sid = sksec_a->sid; 464562306a36Sopenharmony_ci 464662306a36Sopenharmony_ci return 0; 464762306a36Sopenharmony_ci} 464862306a36Sopenharmony_ci 464962306a36Sopenharmony_ci/* Range of port numbers used to automatically bind. 465062306a36Sopenharmony_ci Need to determine whether we should perform a name_bind 465162306a36Sopenharmony_ci permission check between the socket and the port number. */ 465262306a36Sopenharmony_ci 465362306a36Sopenharmony_cistatic int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) 465462306a36Sopenharmony_ci{ 465562306a36Sopenharmony_ci struct sock *sk = sock->sk; 465662306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 465762306a36Sopenharmony_ci u16 family; 465862306a36Sopenharmony_ci int err; 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_ci err = sock_has_perm(sk, SOCKET__BIND); 466162306a36Sopenharmony_ci if (err) 466262306a36Sopenharmony_ci goto out; 466362306a36Sopenharmony_ci 466462306a36Sopenharmony_ci /* If PF_INET or PF_INET6, check name_bind permission for the port. */ 466562306a36Sopenharmony_ci family = sk->sk_family; 466662306a36Sopenharmony_ci if (family == PF_INET || family == PF_INET6) { 466762306a36Sopenharmony_ci char *addrp; 466862306a36Sopenharmony_ci struct common_audit_data ad; 466962306a36Sopenharmony_ci struct lsm_network_audit net = {0,}; 467062306a36Sopenharmony_ci struct sockaddr_in *addr4 = NULL; 467162306a36Sopenharmony_ci struct sockaddr_in6 *addr6 = NULL; 467262306a36Sopenharmony_ci u16 family_sa; 467362306a36Sopenharmony_ci unsigned short snum; 467462306a36Sopenharmony_ci u32 sid, node_perm; 467562306a36Sopenharmony_ci 467662306a36Sopenharmony_ci /* 467762306a36Sopenharmony_ci * sctp_bindx(3) calls via selinux_sctp_bind_connect() 467862306a36Sopenharmony_ci * that validates multiple binding addresses. Because of this 467962306a36Sopenharmony_ci * need to check address->sa_family as it is possible to have 468062306a36Sopenharmony_ci * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. 468162306a36Sopenharmony_ci */ 468262306a36Sopenharmony_ci if (addrlen < offsetofend(struct sockaddr, sa_family)) 468362306a36Sopenharmony_ci return -EINVAL; 468462306a36Sopenharmony_ci family_sa = address->sa_family; 468562306a36Sopenharmony_ci switch (family_sa) { 468662306a36Sopenharmony_ci case AF_UNSPEC: 468762306a36Sopenharmony_ci case AF_INET: 468862306a36Sopenharmony_ci if (addrlen < sizeof(struct sockaddr_in)) 468962306a36Sopenharmony_ci return -EINVAL; 469062306a36Sopenharmony_ci addr4 = (struct sockaddr_in *)address; 469162306a36Sopenharmony_ci if (family_sa == AF_UNSPEC) { 469262306a36Sopenharmony_ci if (family == PF_INET6) { 469362306a36Sopenharmony_ci /* Length check from inet6_bind_sk() */ 469462306a36Sopenharmony_ci if (addrlen < SIN6_LEN_RFC2133) 469562306a36Sopenharmony_ci return -EINVAL; 469662306a36Sopenharmony_ci /* Family check from __inet6_bind() */ 469762306a36Sopenharmony_ci goto err_af; 469862306a36Sopenharmony_ci } 469962306a36Sopenharmony_ci /* see __inet_bind(), we only want to allow 470062306a36Sopenharmony_ci * AF_UNSPEC if the address is INADDR_ANY 470162306a36Sopenharmony_ci */ 470262306a36Sopenharmony_ci if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) 470362306a36Sopenharmony_ci goto err_af; 470462306a36Sopenharmony_ci family_sa = AF_INET; 470562306a36Sopenharmony_ci } 470662306a36Sopenharmony_ci snum = ntohs(addr4->sin_port); 470762306a36Sopenharmony_ci addrp = (char *)&addr4->sin_addr.s_addr; 470862306a36Sopenharmony_ci break; 470962306a36Sopenharmony_ci case AF_INET6: 471062306a36Sopenharmony_ci if (addrlen < SIN6_LEN_RFC2133) 471162306a36Sopenharmony_ci return -EINVAL; 471262306a36Sopenharmony_ci addr6 = (struct sockaddr_in6 *)address; 471362306a36Sopenharmony_ci snum = ntohs(addr6->sin6_port); 471462306a36Sopenharmony_ci addrp = (char *)&addr6->sin6_addr.s6_addr; 471562306a36Sopenharmony_ci break; 471662306a36Sopenharmony_ci default: 471762306a36Sopenharmony_ci goto err_af; 471862306a36Sopenharmony_ci } 471962306a36Sopenharmony_ci 472062306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_NET; 472162306a36Sopenharmony_ci ad.u.net = &net; 472262306a36Sopenharmony_ci ad.u.net->sport = htons(snum); 472362306a36Sopenharmony_ci ad.u.net->family = family_sa; 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_ci if (snum) { 472662306a36Sopenharmony_ci int low, high; 472762306a36Sopenharmony_ci 472862306a36Sopenharmony_ci inet_get_local_port_range(sock_net(sk), &low, &high); 472962306a36Sopenharmony_ci 473062306a36Sopenharmony_ci if (inet_port_requires_bind_service(sock_net(sk), snum) || 473162306a36Sopenharmony_ci snum < low || snum > high) { 473262306a36Sopenharmony_ci err = sel_netport_sid(sk->sk_protocol, 473362306a36Sopenharmony_ci snum, &sid); 473462306a36Sopenharmony_ci if (err) 473562306a36Sopenharmony_ci goto out; 473662306a36Sopenharmony_ci err = avc_has_perm(sksec->sid, sid, 473762306a36Sopenharmony_ci sksec->sclass, 473862306a36Sopenharmony_ci SOCKET__NAME_BIND, &ad); 473962306a36Sopenharmony_ci if (err) 474062306a36Sopenharmony_ci goto out; 474162306a36Sopenharmony_ci } 474262306a36Sopenharmony_ci } 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci switch (sksec->sclass) { 474562306a36Sopenharmony_ci case SECCLASS_TCP_SOCKET: 474662306a36Sopenharmony_ci node_perm = TCP_SOCKET__NODE_BIND; 474762306a36Sopenharmony_ci break; 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_ci case SECCLASS_UDP_SOCKET: 475062306a36Sopenharmony_ci node_perm = UDP_SOCKET__NODE_BIND; 475162306a36Sopenharmony_ci break; 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_ci case SECCLASS_DCCP_SOCKET: 475462306a36Sopenharmony_ci node_perm = DCCP_SOCKET__NODE_BIND; 475562306a36Sopenharmony_ci break; 475662306a36Sopenharmony_ci 475762306a36Sopenharmony_ci case SECCLASS_SCTP_SOCKET: 475862306a36Sopenharmony_ci node_perm = SCTP_SOCKET__NODE_BIND; 475962306a36Sopenharmony_ci break; 476062306a36Sopenharmony_ci 476162306a36Sopenharmony_ci default: 476262306a36Sopenharmony_ci node_perm = RAWIP_SOCKET__NODE_BIND; 476362306a36Sopenharmony_ci break; 476462306a36Sopenharmony_ci } 476562306a36Sopenharmony_ci 476662306a36Sopenharmony_ci err = sel_netnode_sid(addrp, family_sa, &sid); 476762306a36Sopenharmony_ci if (err) 476862306a36Sopenharmony_ci goto out; 476962306a36Sopenharmony_ci 477062306a36Sopenharmony_ci if (family_sa == AF_INET) 477162306a36Sopenharmony_ci ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; 477262306a36Sopenharmony_ci else 477362306a36Sopenharmony_ci ad.u.net->v6info.saddr = addr6->sin6_addr; 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_ci err = avc_has_perm(sksec->sid, sid, 477662306a36Sopenharmony_ci sksec->sclass, node_perm, &ad); 477762306a36Sopenharmony_ci if (err) 477862306a36Sopenharmony_ci goto out; 477962306a36Sopenharmony_ci } 478062306a36Sopenharmony_ciout: 478162306a36Sopenharmony_ci return err; 478262306a36Sopenharmony_cierr_af: 478362306a36Sopenharmony_ci /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */ 478462306a36Sopenharmony_ci if (sksec->sclass == SECCLASS_SCTP_SOCKET) 478562306a36Sopenharmony_ci return -EINVAL; 478662306a36Sopenharmony_ci return -EAFNOSUPPORT; 478762306a36Sopenharmony_ci} 478862306a36Sopenharmony_ci 478962306a36Sopenharmony_ci/* This supports connect(2) and SCTP connect services such as sctp_connectx(3) 479062306a36Sopenharmony_ci * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst 479162306a36Sopenharmony_ci */ 479262306a36Sopenharmony_cistatic int selinux_socket_connect_helper(struct socket *sock, 479362306a36Sopenharmony_ci struct sockaddr *address, int addrlen) 479462306a36Sopenharmony_ci{ 479562306a36Sopenharmony_ci struct sock *sk = sock->sk; 479662306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 479762306a36Sopenharmony_ci int err; 479862306a36Sopenharmony_ci 479962306a36Sopenharmony_ci err = sock_has_perm(sk, SOCKET__CONNECT); 480062306a36Sopenharmony_ci if (err) 480162306a36Sopenharmony_ci return err; 480262306a36Sopenharmony_ci if (addrlen < offsetofend(struct sockaddr, sa_family)) 480362306a36Sopenharmony_ci return -EINVAL; 480462306a36Sopenharmony_ci 480562306a36Sopenharmony_ci /* connect(AF_UNSPEC) has special handling, as it is a documented 480662306a36Sopenharmony_ci * way to disconnect the socket 480762306a36Sopenharmony_ci */ 480862306a36Sopenharmony_ci if (address->sa_family == AF_UNSPEC) 480962306a36Sopenharmony_ci return 0; 481062306a36Sopenharmony_ci 481162306a36Sopenharmony_ci /* 481262306a36Sopenharmony_ci * If a TCP, DCCP or SCTP socket, check name_connect permission 481362306a36Sopenharmony_ci * for the port. 481462306a36Sopenharmony_ci */ 481562306a36Sopenharmony_ci if (sksec->sclass == SECCLASS_TCP_SOCKET || 481662306a36Sopenharmony_ci sksec->sclass == SECCLASS_DCCP_SOCKET || 481762306a36Sopenharmony_ci sksec->sclass == SECCLASS_SCTP_SOCKET) { 481862306a36Sopenharmony_ci struct common_audit_data ad; 481962306a36Sopenharmony_ci struct lsm_network_audit net = {0,}; 482062306a36Sopenharmony_ci struct sockaddr_in *addr4 = NULL; 482162306a36Sopenharmony_ci struct sockaddr_in6 *addr6 = NULL; 482262306a36Sopenharmony_ci unsigned short snum; 482362306a36Sopenharmony_ci u32 sid, perm; 482462306a36Sopenharmony_ci 482562306a36Sopenharmony_ci /* sctp_connectx(3) calls via selinux_sctp_bind_connect() 482662306a36Sopenharmony_ci * that validates multiple connect addresses. Because of this 482762306a36Sopenharmony_ci * need to check address->sa_family as it is possible to have 482862306a36Sopenharmony_ci * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. 482962306a36Sopenharmony_ci */ 483062306a36Sopenharmony_ci switch (address->sa_family) { 483162306a36Sopenharmony_ci case AF_INET: 483262306a36Sopenharmony_ci addr4 = (struct sockaddr_in *)address; 483362306a36Sopenharmony_ci if (addrlen < sizeof(struct sockaddr_in)) 483462306a36Sopenharmony_ci return -EINVAL; 483562306a36Sopenharmony_ci snum = ntohs(addr4->sin_port); 483662306a36Sopenharmony_ci break; 483762306a36Sopenharmony_ci case AF_INET6: 483862306a36Sopenharmony_ci addr6 = (struct sockaddr_in6 *)address; 483962306a36Sopenharmony_ci if (addrlen < SIN6_LEN_RFC2133) 484062306a36Sopenharmony_ci return -EINVAL; 484162306a36Sopenharmony_ci snum = ntohs(addr6->sin6_port); 484262306a36Sopenharmony_ci break; 484362306a36Sopenharmony_ci default: 484462306a36Sopenharmony_ci /* Note that SCTP services expect -EINVAL, whereas 484562306a36Sopenharmony_ci * others expect -EAFNOSUPPORT. 484662306a36Sopenharmony_ci */ 484762306a36Sopenharmony_ci if (sksec->sclass == SECCLASS_SCTP_SOCKET) 484862306a36Sopenharmony_ci return -EINVAL; 484962306a36Sopenharmony_ci else 485062306a36Sopenharmony_ci return -EAFNOSUPPORT; 485162306a36Sopenharmony_ci } 485262306a36Sopenharmony_ci 485362306a36Sopenharmony_ci err = sel_netport_sid(sk->sk_protocol, snum, &sid); 485462306a36Sopenharmony_ci if (err) 485562306a36Sopenharmony_ci return err; 485662306a36Sopenharmony_ci 485762306a36Sopenharmony_ci switch (sksec->sclass) { 485862306a36Sopenharmony_ci case SECCLASS_TCP_SOCKET: 485962306a36Sopenharmony_ci perm = TCP_SOCKET__NAME_CONNECT; 486062306a36Sopenharmony_ci break; 486162306a36Sopenharmony_ci case SECCLASS_DCCP_SOCKET: 486262306a36Sopenharmony_ci perm = DCCP_SOCKET__NAME_CONNECT; 486362306a36Sopenharmony_ci break; 486462306a36Sopenharmony_ci case SECCLASS_SCTP_SOCKET: 486562306a36Sopenharmony_ci perm = SCTP_SOCKET__NAME_CONNECT; 486662306a36Sopenharmony_ci break; 486762306a36Sopenharmony_ci } 486862306a36Sopenharmony_ci 486962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_NET; 487062306a36Sopenharmony_ci ad.u.net = &net; 487162306a36Sopenharmony_ci ad.u.net->dport = htons(snum); 487262306a36Sopenharmony_ci ad.u.net->family = address->sa_family; 487362306a36Sopenharmony_ci err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); 487462306a36Sopenharmony_ci if (err) 487562306a36Sopenharmony_ci return err; 487662306a36Sopenharmony_ci } 487762306a36Sopenharmony_ci 487862306a36Sopenharmony_ci return 0; 487962306a36Sopenharmony_ci} 488062306a36Sopenharmony_ci 488162306a36Sopenharmony_ci/* Supports connect(2), see comments in selinux_socket_connect_helper() */ 488262306a36Sopenharmony_cistatic int selinux_socket_connect(struct socket *sock, 488362306a36Sopenharmony_ci struct sockaddr *address, int addrlen) 488462306a36Sopenharmony_ci{ 488562306a36Sopenharmony_ci int err; 488662306a36Sopenharmony_ci struct sock *sk = sock->sk; 488762306a36Sopenharmony_ci 488862306a36Sopenharmony_ci err = selinux_socket_connect_helper(sock, address, addrlen); 488962306a36Sopenharmony_ci if (err) 489062306a36Sopenharmony_ci return err; 489162306a36Sopenharmony_ci 489262306a36Sopenharmony_ci return selinux_netlbl_socket_connect(sk, address); 489362306a36Sopenharmony_ci} 489462306a36Sopenharmony_ci 489562306a36Sopenharmony_cistatic int selinux_socket_listen(struct socket *sock, int backlog) 489662306a36Sopenharmony_ci{ 489762306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__LISTEN); 489862306a36Sopenharmony_ci} 489962306a36Sopenharmony_ci 490062306a36Sopenharmony_cistatic int selinux_socket_accept(struct socket *sock, struct socket *newsock) 490162306a36Sopenharmony_ci{ 490262306a36Sopenharmony_ci int err; 490362306a36Sopenharmony_ci struct inode_security_struct *isec; 490462306a36Sopenharmony_ci struct inode_security_struct *newisec; 490562306a36Sopenharmony_ci u16 sclass; 490662306a36Sopenharmony_ci u32 sid; 490762306a36Sopenharmony_ci 490862306a36Sopenharmony_ci err = sock_has_perm(sock->sk, SOCKET__ACCEPT); 490962306a36Sopenharmony_ci if (err) 491062306a36Sopenharmony_ci return err; 491162306a36Sopenharmony_ci 491262306a36Sopenharmony_ci isec = inode_security_novalidate(SOCK_INODE(sock)); 491362306a36Sopenharmony_ci spin_lock(&isec->lock); 491462306a36Sopenharmony_ci sclass = isec->sclass; 491562306a36Sopenharmony_ci sid = isec->sid; 491662306a36Sopenharmony_ci spin_unlock(&isec->lock); 491762306a36Sopenharmony_ci 491862306a36Sopenharmony_ci newisec = inode_security_novalidate(SOCK_INODE(newsock)); 491962306a36Sopenharmony_ci newisec->sclass = sclass; 492062306a36Sopenharmony_ci newisec->sid = sid; 492162306a36Sopenharmony_ci newisec->initialized = LABEL_INITIALIZED; 492262306a36Sopenharmony_ci 492362306a36Sopenharmony_ci return 0; 492462306a36Sopenharmony_ci} 492562306a36Sopenharmony_ci 492662306a36Sopenharmony_cistatic int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, 492762306a36Sopenharmony_ci int size) 492862306a36Sopenharmony_ci{ 492962306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__WRITE); 493062306a36Sopenharmony_ci} 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_cistatic int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, 493362306a36Sopenharmony_ci int size, int flags) 493462306a36Sopenharmony_ci{ 493562306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__READ); 493662306a36Sopenharmony_ci} 493762306a36Sopenharmony_ci 493862306a36Sopenharmony_cistatic int selinux_socket_getsockname(struct socket *sock) 493962306a36Sopenharmony_ci{ 494062306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__GETATTR); 494162306a36Sopenharmony_ci} 494262306a36Sopenharmony_ci 494362306a36Sopenharmony_cistatic int selinux_socket_getpeername(struct socket *sock) 494462306a36Sopenharmony_ci{ 494562306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__GETATTR); 494662306a36Sopenharmony_ci} 494762306a36Sopenharmony_ci 494862306a36Sopenharmony_cistatic int selinux_socket_setsockopt(struct socket *sock, int level, int optname) 494962306a36Sopenharmony_ci{ 495062306a36Sopenharmony_ci int err; 495162306a36Sopenharmony_ci 495262306a36Sopenharmony_ci err = sock_has_perm(sock->sk, SOCKET__SETOPT); 495362306a36Sopenharmony_ci if (err) 495462306a36Sopenharmony_ci return err; 495562306a36Sopenharmony_ci 495662306a36Sopenharmony_ci return selinux_netlbl_socket_setsockopt(sock, level, optname); 495762306a36Sopenharmony_ci} 495862306a36Sopenharmony_ci 495962306a36Sopenharmony_cistatic int selinux_socket_getsockopt(struct socket *sock, int level, 496062306a36Sopenharmony_ci int optname) 496162306a36Sopenharmony_ci{ 496262306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__GETOPT); 496362306a36Sopenharmony_ci} 496462306a36Sopenharmony_ci 496562306a36Sopenharmony_cistatic int selinux_socket_shutdown(struct socket *sock, int how) 496662306a36Sopenharmony_ci{ 496762306a36Sopenharmony_ci return sock_has_perm(sock->sk, SOCKET__SHUTDOWN); 496862306a36Sopenharmony_ci} 496962306a36Sopenharmony_ci 497062306a36Sopenharmony_cistatic int selinux_socket_unix_stream_connect(struct sock *sock, 497162306a36Sopenharmony_ci struct sock *other, 497262306a36Sopenharmony_ci struct sock *newsk) 497362306a36Sopenharmony_ci{ 497462306a36Sopenharmony_ci struct sk_security_struct *sksec_sock = sock->sk_security; 497562306a36Sopenharmony_ci struct sk_security_struct *sksec_other = other->sk_security; 497662306a36Sopenharmony_ci struct sk_security_struct *sksec_new = newsk->sk_security; 497762306a36Sopenharmony_ci struct common_audit_data ad; 497862306a36Sopenharmony_ci struct lsm_network_audit net; 497962306a36Sopenharmony_ci int err; 498062306a36Sopenharmony_ci 498162306a36Sopenharmony_ci ad_net_init_from_sk(&ad, &net, other); 498262306a36Sopenharmony_ci 498362306a36Sopenharmony_ci err = avc_has_perm(sksec_sock->sid, sksec_other->sid, 498462306a36Sopenharmony_ci sksec_other->sclass, 498562306a36Sopenharmony_ci UNIX_STREAM_SOCKET__CONNECTTO, &ad); 498662306a36Sopenharmony_ci if (err) 498762306a36Sopenharmony_ci return err; 498862306a36Sopenharmony_ci 498962306a36Sopenharmony_ci /* server child socket */ 499062306a36Sopenharmony_ci sksec_new->peer_sid = sksec_sock->sid; 499162306a36Sopenharmony_ci err = security_sid_mls_copy(sksec_other->sid, 499262306a36Sopenharmony_ci sksec_sock->sid, &sksec_new->sid); 499362306a36Sopenharmony_ci if (err) 499462306a36Sopenharmony_ci return err; 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci /* connecting socket */ 499762306a36Sopenharmony_ci sksec_sock->peer_sid = sksec_new->sid; 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_ci return 0; 500062306a36Sopenharmony_ci} 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_cistatic int selinux_socket_unix_may_send(struct socket *sock, 500362306a36Sopenharmony_ci struct socket *other) 500462306a36Sopenharmony_ci{ 500562306a36Sopenharmony_ci struct sk_security_struct *ssec = sock->sk->sk_security; 500662306a36Sopenharmony_ci struct sk_security_struct *osec = other->sk->sk_security; 500762306a36Sopenharmony_ci struct common_audit_data ad; 500862306a36Sopenharmony_ci struct lsm_network_audit net; 500962306a36Sopenharmony_ci 501062306a36Sopenharmony_ci ad_net_init_from_sk(&ad, &net, other->sk); 501162306a36Sopenharmony_ci 501262306a36Sopenharmony_ci return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, 501362306a36Sopenharmony_ci &ad); 501462306a36Sopenharmony_ci} 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_cistatic int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, 501762306a36Sopenharmony_ci char *addrp, u16 family, u32 peer_sid, 501862306a36Sopenharmony_ci struct common_audit_data *ad) 501962306a36Sopenharmony_ci{ 502062306a36Sopenharmony_ci int err; 502162306a36Sopenharmony_ci u32 if_sid; 502262306a36Sopenharmony_ci u32 node_sid; 502362306a36Sopenharmony_ci 502462306a36Sopenharmony_ci err = sel_netif_sid(ns, ifindex, &if_sid); 502562306a36Sopenharmony_ci if (err) 502662306a36Sopenharmony_ci return err; 502762306a36Sopenharmony_ci err = avc_has_perm(peer_sid, if_sid, 502862306a36Sopenharmony_ci SECCLASS_NETIF, NETIF__INGRESS, ad); 502962306a36Sopenharmony_ci if (err) 503062306a36Sopenharmony_ci return err; 503162306a36Sopenharmony_ci 503262306a36Sopenharmony_ci err = sel_netnode_sid(addrp, family, &node_sid); 503362306a36Sopenharmony_ci if (err) 503462306a36Sopenharmony_ci return err; 503562306a36Sopenharmony_ci return avc_has_perm(peer_sid, node_sid, 503662306a36Sopenharmony_ci SECCLASS_NODE, NODE__RECVFROM, ad); 503762306a36Sopenharmony_ci} 503862306a36Sopenharmony_ci 503962306a36Sopenharmony_cistatic int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, 504062306a36Sopenharmony_ci u16 family) 504162306a36Sopenharmony_ci{ 504262306a36Sopenharmony_ci int err = 0; 504362306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 504462306a36Sopenharmony_ci u32 sk_sid = sksec->sid; 504562306a36Sopenharmony_ci struct common_audit_data ad; 504662306a36Sopenharmony_ci struct lsm_network_audit net; 504762306a36Sopenharmony_ci char *addrp; 504862306a36Sopenharmony_ci 504962306a36Sopenharmony_ci ad_net_init_from_iif(&ad, &net, skb->skb_iif, family); 505062306a36Sopenharmony_ci err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); 505162306a36Sopenharmony_ci if (err) 505262306a36Sopenharmony_ci return err; 505362306a36Sopenharmony_ci 505462306a36Sopenharmony_ci if (selinux_secmark_enabled()) { 505562306a36Sopenharmony_ci err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, 505662306a36Sopenharmony_ci PACKET__RECV, &ad); 505762306a36Sopenharmony_ci if (err) 505862306a36Sopenharmony_ci return err; 505962306a36Sopenharmony_ci } 506062306a36Sopenharmony_ci 506162306a36Sopenharmony_ci err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); 506262306a36Sopenharmony_ci if (err) 506362306a36Sopenharmony_ci return err; 506462306a36Sopenharmony_ci err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad); 506562306a36Sopenharmony_ci 506662306a36Sopenharmony_ci return err; 506762306a36Sopenharmony_ci} 506862306a36Sopenharmony_ci 506962306a36Sopenharmony_cistatic int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) 507062306a36Sopenharmony_ci{ 507162306a36Sopenharmony_ci int err, peerlbl_active, secmark_active; 507262306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 507362306a36Sopenharmony_ci u16 family = sk->sk_family; 507462306a36Sopenharmony_ci u32 sk_sid = sksec->sid; 507562306a36Sopenharmony_ci struct common_audit_data ad; 507662306a36Sopenharmony_ci struct lsm_network_audit net; 507762306a36Sopenharmony_ci char *addrp; 507862306a36Sopenharmony_ci 507962306a36Sopenharmony_ci if (family != PF_INET && family != PF_INET6) 508062306a36Sopenharmony_ci return 0; 508162306a36Sopenharmony_ci 508262306a36Sopenharmony_ci /* Handle mapped IPv4 packets arriving via IPv6 sockets */ 508362306a36Sopenharmony_ci if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) 508462306a36Sopenharmony_ci family = PF_INET; 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_ci /* If any sort of compatibility mode is enabled then handoff processing 508762306a36Sopenharmony_ci * to the selinux_sock_rcv_skb_compat() function to deal with the 508862306a36Sopenharmony_ci * special handling. We do this in an attempt to keep this function 508962306a36Sopenharmony_ci * as fast and as clean as possible. */ 509062306a36Sopenharmony_ci if (!selinux_policycap_netpeer()) 509162306a36Sopenharmony_ci return selinux_sock_rcv_skb_compat(sk, skb, family); 509262306a36Sopenharmony_ci 509362306a36Sopenharmony_ci secmark_active = selinux_secmark_enabled(); 509462306a36Sopenharmony_ci peerlbl_active = selinux_peerlbl_enabled(); 509562306a36Sopenharmony_ci if (!secmark_active && !peerlbl_active) 509662306a36Sopenharmony_ci return 0; 509762306a36Sopenharmony_ci 509862306a36Sopenharmony_ci ad_net_init_from_iif(&ad, &net, skb->skb_iif, family); 509962306a36Sopenharmony_ci err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); 510062306a36Sopenharmony_ci if (err) 510162306a36Sopenharmony_ci return err; 510262306a36Sopenharmony_ci 510362306a36Sopenharmony_ci if (peerlbl_active) { 510462306a36Sopenharmony_ci u32 peer_sid; 510562306a36Sopenharmony_ci 510662306a36Sopenharmony_ci err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); 510762306a36Sopenharmony_ci if (err) 510862306a36Sopenharmony_ci return err; 510962306a36Sopenharmony_ci err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif, 511062306a36Sopenharmony_ci addrp, family, peer_sid, &ad); 511162306a36Sopenharmony_ci if (err) { 511262306a36Sopenharmony_ci selinux_netlbl_err(skb, family, err, 0); 511362306a36Sopenharmony_ci return err; 511462306a36Sopenharmony_ci } 511562306a36Sopenharmony_ci err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, 511662306a36Sopenharmony_ci PEER__RECV, &ad); 511762306a36Sopenharmony_ci if (err) { 511862306a36Sopenharmony_ci selinux_netlbl_err(skb, family, err, 0); 511962306a36Sopenharmony_ci return err; 512062306a36Sopenharmony_ci } 512162306a36Sopenharmony_ci } 512262306a36Sopenharmony_ci 512362306a36Sopenharmony_ci if (secmark_active) { 512462306a36Sopenharmony_ci err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, 512562306a36Sopenharmony_ci PACKET__RECV, &ad); 512662306a36Sopenharmony_ci if (err) 512762306a36Sopenharmony_ci return err; 512862306a36Sopenharmony_ci } 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci return err; 513162306a36Sopenharmony_ci} 513262306a36Sopenharmony_ci 513362306a36Sopenharmony_cistatic int selinux_socket_getpeersec_stream(struct socket *sock, 513462306a36Sopenharmony_ci sockptr_t optval, sockptr_t optlen, 513562306a36Sopenharmony_ci unsigned int len) 513662306a36Sopenharmony_ci{ 513762306a36Sopenharmony_ci int err = 0; 513862306a36Sopenharmony_ci char *scontext = NULL; 513962306a36Sopenharmony_ci u32 scontext_len; 514062306a36Sopenharmony_ci struct sk_security_struct *sksec = sock->sk->sk_security; 514162306a36Sopenharmony_ci u32 peer_sid = SECSID_NULL; 514262306a36Sopenharmony_ci 514362306a36Sopenharmony_ci if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || 514462306a36Sopenharmony_ci sksec->sclass == SECCLASS_TCP_SOCKET || 514562306a36Sopenharmony_ci sksec->sclass == SECCLASS_SCTP_SOCKET) 514662306a36Sopenharmony_ci peer_sid = sksec->peer_sid; 514762306a36Sopenharmony_ci if (peer_sid == SECSID_NULL) 514862306a36Sopenharmony_ci return -ENOPROTOOPT; 514962306a36Sopenharmony_ci 515062306a36Sopenharmony_ci err = security_sid_to_context(peer_sid, &scontext, 515162306a36Sopenharmony_ci &scontext_len); 515262306a36Sopenharmony_ci if (err) 515362306a36Sopenharmony_ci return err; 515462306a36Sopenharmony_ci if (scontext_len > len) { 515562306a36Sopenharmony_ci err = -ERANGE; 515662306a36Sopenharmony_ci goto out_len; 515762306a36Sopenharmony_ci } 515862306a36Sopenharmony_ci 515962306a36Sopenharmony_ci if (copy_to_sockptr(optval, scontext, scontext_len)) 516062306a36Sopenharmony_ci err = -EFAULT; 516162306a36Sopenharmony_ciout_len: 516262306a36Sopenharmony_ci if (copy_to_sockptr(optlen, &scontext_len, sizeof(scontext_len))) 516362306a36Sopenharmony_ci err = -EFAULT; 516462306a36Sopenharmony_ci kfree(scontext); 516562306a36Sopenharmony_ci return err; 516662306a36Sopenharmony_ci} 516762306a36Sopenharmony_ci 516862306a36Sopenharmony_cistatic int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) 516962306a36Sopenharmony_ci{ 517062306a36Sopenharmony_ci u32 peer_secid = SECSID_NULL; 517162306a36Sopenharmony_ci u16 family; 517262306a36Sopenharmony_ci struct inode_security_struct *isec; 517362306a36Sopenharmony_ci 517462306a36Sopenharmony_ci if (skb && skb->protocol == htons(ETH_P_IP)) 517562306a36Sopenharmony_ci family = PF_INET; 517662306a36Sopenharmony_ci else if (skb && skb->protocol == htons(ETH_P_IPV6)) 517762306a36Sopenharmony_ci family = PF_INET6; 517862306a36Sopenharmony_ci else if (sock) 517962306a36Sopenharmony_ci family = sock->sk->sk_family; 518062306a36Sopenharmony_ci else 518162306a36Sopenharmony_ci goto out; 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_ci if (sock && family == PF_UNIX) { 518462306a36Sopenharmony_ci isec = inode_security_novalidate(SOCK_INODE(sock)); 518562306a36Sopenharmony_ci peer_secid = isec->sid; 518662306a36Sopenharmony_ci } else if (skb) 518762306a36Sopenharmony_ci selinux_skb_peerlbl_sid(skb, family, &peer_secid); 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ciout: 519062306a36Sopenharmony_ci *secid = peer_secid; 519162306a36Sopenharmony_ci if (peer_secid == SECSID_NULL) 519262306a36Sopenharmony_ci return -EINVAL; 519362306a36Sopenharmony_ci return 0; 519462306a36Sopenharmony_ci} 519562306a36Sopenharmony_ci 519662306a36Sopenharmony_cistatic int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) 519762306a36Sopenharmony_ci{ 519862306a36Sopenharmony_ci struct sk_security_struct *sksec; 519962306a36Sopenharmony_ci 520062306a36Sopenharmony_ci sksec = kzalloc(sizeof(*sksec), priority); 520162306a36Sopenharmony_ci if (!sksec) 520262306a36Sopenharmony_ci return -ENOMEM; 520362306a36Sopenharmony_ci 520462306a36Sopenharmony_ci sksec->peer_sid = SECINITSID_UNLABELED; 520562306a36Sopenharmony_ci sksec->sid = SECINITSID_UNLABELED; 520662306a36Sopenharmony_ci sksec->sclass = SECCLASS_SOCKET; 520762306a36Sopenharmony_ci selinux_netlbl_sk_security_reset(sksec); 520862306a36Sopenharmony_ci sk->sk_security = sksec; 520962306a36Sopenharmony_ci 521062306a36Sopenharmony_ci return 0; 521162306a36Sopenharmony_ci} 521262306a36Sopenharmony_ci 521362306a36Sopenharmony_cistatic void selinux_sk_free_security(struct sock *sk) 521462306a36Sopenharmony_ci{ 521562306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 521662306a36Sopenharmony_ci 521762306a36Sopenharmony_ci sk->sk_security = NULL; 521862306a36Sopenharmony_ci selinux_netlbl_sk_security_free(sksec); 521962306a36Sopenharmony_ci kfree(sksec); 522062306a36Sopenharmony_ci} 522162306a36Sopenharmony_ci 522262306a36Sopenharmony_cistatic void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) 522362306a36Sopenharmony_ci{ 522462306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 522562306a36Sopenharmony_ci struct sk_security_struct *newsksec = newsk->sk_security; 522662306a36Sopenharmony_ci 522762306a36Sopenharmony_ci newsksec->sid = sksec->sid; 522862306a36Sopenharmony_ci newsksec->peer_sid = sksec->peer_sid; 522962306a36Sopenharmony_ci newsksec->sclass = sksec->sclass; 523062306a36Sopenharmony_ci 523162306a36Sopenharmony_ci selinux_netlbl_sk_security_reset(newsksec); 523262306a36Sopenharmony_ci} 523362306a36Sopenharmony_ci 523462306a36Sopenharmony_cistatic void selinux_sk_getsecid(const struct sock *sk, u32 *secid) 523562306a36Sopenharmony_ci{ 523662306a36Sopenharmony_ci if (!sk) 523762306a36Sopenharmony_ci *secid = SECINITSID_ANY_SOCKET; 523862306a36Sopenharmony_ci else { 523962306a36Sopenharmony_ci const struct sk_security_struct *sksec = sk->sk_security; 524062306a36Sopenharmony_ci 524162306a36Sopenharmony_ci *secid = sksec->sid; 524262306a36Sopenharmony_ci } 524362306a36Sopenharmony_ci} 524462306a36Sopenharmony_ci 524562306a36Sopenharmony_cistatic void selinux_sock_graft(struct sock *sk, struct socket *parent) 524662306a36Sopenharmony_ci{ 524762306a36Sopenharmony_ci struct inode_security_struct *isec = 524862306a36Sopenharmony_ci inode_security_novalidate(SOCK_INODE(parent)); 524962306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 525062306a36Sopenharmony_ci 525162306a36Sopenharmony_ci if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || 525262306a36Sopenharmony_ci sk->sk_family == PF_UNIX) 525362306a36Sopenharmony_ci isec->sid = sksec->sid; 525462306a36Sopenharmony_ci sksec->sclass = isec->sclass; 525562306a36Sopenharmony_ci} 525662306a36Sopenharmony_ci 525762306a36Sopenharmony_ci/* 525862306a36Sopenharmony_ci * Determines peer_secid for the asoc and updates socket's peer label 525962306a36Sopenharmony_ci * if it's the first association on the socket. 526062306a36Sopenharmony_ci */ 526162306a36Sopenharmony_cistatic int selinux_sctp_process_new_assoc(struct sctp_association *asoc, 526262306a36Sopenharmony_ci struct sk_buff *skb) 526362306a36Sopenharmony_ci{ 526462306a36Sopenharmony_ci struct sock *sk = asoc->base.sk; 526562306a36Sopenharmony_ci u16 family = sk->sk_family; 526662306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 526762306a36Sopenharmony_ci struct common_audit_data ad; 526862306a36Sopenharmony_ci struct lsm_network_audit net; 526962306a36Sopenharmony_ci int err; 527062306a36Sopenharmony_ci 527162306a36Sopenharmony_ci /* handle mapped IPv4 packets arriving via IPv6 sockets */ 527262306a36Sopenharmony_ci if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) 527362306a36Sopenharmony_ci family = PF_INET; 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci if (selinux_peerlbl_enabled()) { 527662306a36Sopenharmony_ci asoc->peer_secid = SECSID_NULL; 527762306a36Sopenharmony_ci 527862306a36Sopenharmony_ci /* This will return peer_sid = SECSID_NULL if there are 527962306a36Sopenharmony_ci * no peer labels, see security_net_peersid_resolve(). 528062306a36Sopenharmony_ci */ 528162306a36Sopenharmony_ci err = selinux_skb_peerlbl_sid(skb, family, &asoc->peer_secid); 528262306a36Sopenharmony_ci if (err) 528362306a36Sopenharmony_ci return err; 528462306a36Sopenharmony_ci 528562306a36Sopenharmony_ci if (asoc->peer_secid == SECSID_NULL) 528662306a36Sopenharmony_ci asoc->peer_secid = SECINITSID_UNLABELED; 528762306a36Sopenharmony_ci } else { 528862306a36Sopenharmony_ci asoc->peer_secid = SECINITSID_UNLABELED; 528962306a36Sopenharmony_ci } 529062306a36Sopenharmony_ci 529162306a36Sopenharmony_ci if (sksec->sctp_assoc_state == SCTP_ASSOC_UNSET) { 529262306a36Sopenharmony_ci sksec->sctp_assoc_state = SCTP_ASSOC_SET; 529362306a36Sopenharmony_ci 529462306a36Sopenharmony_ci /* Here as first association on socket. As the peer SID 529562306a36Sopenharmony_ci * was allowed by peer recv (and the netif/node checks), 529662306a36Sopenharmony_ci * then it is approved by policy and used as the primary 529762306a36Sopenharmony_ci * peer SID for getpeercon(3). 529862306a36Sopenharmony_ci */ 529962306a36Sopenharmony_ci sksec->peer_sid = asoc->peer_secid; 530062306a36Sopenharmony_ci } else if (sksec->peer_sid != asoc->peer_secid) { 530162306a36Sopenharmony_ci /* Other association peer SIDs are checked to enforce 530262306a36Sopenharmony_ci * consistency among the peer SIDs. 530362306a36Sopenharmony_ci */ 530462306a36Sopenharmony_ci ad_net_init_from_sk(&ad, &net, asoc->base.sk); 530562306a36Sopenharmony_ci err = avc_has_perm(sksec->peer_sid, asoc->peer_secid, 530662306a36Sopenharmony_ci sksec->sclass, SCTP_SOCKET__ASSOCIATION, 530762306a36Sopenharmony_ci &ad); 530862306a36Sopenharmony_ci if (err) 530962306a36Sopenharmony_ci return err; 531062306a36Sopenharmony_ci } 531162306a36Sopenharmony_ci return 0; 531262306a36Sopenharmony_ci} 531362306a36Sopenharmony_ci 531462306a36Sopenharmony_ci/* Called whenever SCTP receives an INIT or COOKIE ECHO chunk. This 531562306a36Sopenharmony_ci * happens on an incoming connect(2), sctp_connectx(3) or 531662306a36Sopenharmony_ci * sctp_sendmsg(3) (with no association already present). 531762306a36Sopenharmony_ci */ 531862306a36Sopenharmony_cistatic int selinux_sctp_assoc_request(struct sctp_association *asoc, 531962306a36Sopenharmony_ci struct sk_buff *skb) 532062306a36Sopenharmony_ci{ 532162306a36Sopenharmony_ci struct sk_security_struct *sksec = asoc->base.sk->sk_security; 532262306a36Sopenharmony_ci u32 conn_sid; 532362306a36Sopenharmony_ci int err; 532462306a36Sopenharmony_ci 532562306a36Sopenharmony_ci if (!selinux_policycap_extsockclass()) 532662306a36Sopenharmony_ci return 0; 532762306a36Sopenharmony_ci 532862306a36Sopenharmony_ci err = selinux_sctp_process_new_assoc(asoc, skb); 532962306a36Sopenharmony_ci if (err) 533062306a36Sopenharmony_ci return err; 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci /* Compute the MLS component for the connection and store 533362306a36Sopenharmony_ci * the information in asoc. This will be used by SCTP TCP type 533462306a36Sopenharmony_ci * sockets and peeled off connections as they cause a new 533562306a36Sopenharmony_ci * socket to be generated. selinux_sctp_sk_clone() will then 533662306a36Sopenharmony_ci * plug this into the new socket. 533762306a36Sopenharmony_ci */ 533862306a36Sopenharmony_ci err = selinux_conn_sid(sksec->sid, asoc->peer_secid, &conn_sid); 533962306a36Sopenharmony_ci if (err) 534062306a36Sopenharmony_ci return err; 534162306a36Sopenharmony_ci 534262306a36Sopenharmony_ci asoc->secid = conn_sid; 534362306a36Sopenharmony_ci 534462306a36Sopenharmony_ci /* Set any NetLabel labels including CIPSO/CALIPSO options. */ 534562306a36Sopenharmony_ci return selinux_netlbl_sctp_assoc_request(asoc, skb); 534662306a36Sopenharmony_ci} 534762306a36Sopenharmony_ci 534862306a36Sopenharmony_ci/* Called when SCTP receives a COOKIE ACK chunk as the final 534962306a36Sopenharmony_ci * response to an association request (initited by us). 535062306a36Sopenharmony_ci */ 535162306a36Sopenharmony_cistatic int selinux_sctp_assoc_established(struct sctp_association *asoc, 535262306a36Sopenharmony_ci struct sk_buff *skb) 535362306a36Sopenharmony_ci{ 535462306a36Sopenharmony_ci struct sk_security_struct *sksec = asoc->base.sk->sk_security; 535562306a36Sopenharmony_ci 535662306a36Sopenharmony_ci if (!selinux_policycap_extsockclass()) 535762306a36Sopenharmony_ci return 0; 535862306a36Sopenharmony_ci 535962306a36Sopenharmony_ci /* Inherit secid from the parent socket - this will be picked up 536062306a36Sopenharmony_ci * by selinux_sctp_sk_clone() if the association gets peeled off 536162306a36Sopenharmony_ci * into a new socket. 536262306a36Sopenharmony_ci */ 536362306a36Sopenharmony_ci asoc->secid = sksec->sid; 536462306a36Sopenharmony_ci 536562306a36Sopenharmony_ci return selinux_sctp_process_new_assoc(asoc, skb); 536662306a36Sopenharmony_ci} 536762306a36Sopenharmony_ci 536862306a36Sopenharmony_ci/* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting 536962306a36Sopenharmony_ci * based on their @optname. 537062306a36Sopenharmony_ci */ 537162306a36Sopenharmony_cistatic int selinux_sctp_bind_connect(struct sock *sk, int optname, 537262306a36Sopenharmony_ci struct sockaddr *address, 537362306a36Sopenharmony_ci int addrlen) 537462306a36Sopenharmony_ci{ 537562306a36Sopenharmony_ci int len, err = 0, walk_size = 0; 537662306a36Sopenharmony_ci void *addr_buf; 537762306a36Sopenharmony_ci struct sockaddr *addr; 537862306a36Sopenharmony_ci struct socket *sock; 537962306a36Sopenharmony_ci 538062306a36Sopenharmony_ci if (!selinux_policycap_extsockclass()) 538162306a36Sopenharmony_ci return 0; 538262306a36Sopenharmony_ci 538362306a36Sopenharmony_ci /* Process one or more addresses that may be IPv4 or IPv6 */ 538462306a36Sopenharmony_ci sock = sk->sk_socket; 538562306a36Sopenharmony_ci addr_buf = address; 538662306a36Sopenharmony_ci 538762306a36Sopenharmony_ci while (walk_size < addrlen) { 538862306a36Sopenharmony_ci if (walk_size + sizeof(sa_family_t) > addrlen) 538962306a36Sopenharmony_ci return -EINVAL; 539062306a36Sopenharmony_ci 539162306a36Sopenharmony_ci addr = addr_buf; 539262306a36Sopenharmony_ci switch (addr->sa_family) { 539362306a36Sopenharmony_ci case AF_UNSPEC: 539462306a36Sopenharmony_ci case AF_INET: 539562306a36Sopenharmony_ci len = sizeof(struct sockaddr_in); 539662306a36Sopenharmony_ci break; 539762306a36Sopenharmony_ci case AF_INET6: 539862306a36Sopenharmony_ci len = sizeof(struct sockaddr_in6); 539962306a36Sopenharmony_ci break; 540062306a36Sopenharmony_ci default: 540162306a36Sopenharmony_ci return -EINVAL; 540262306a36Sopenharmony_ci } 540362306a36Sopenharmony_ci 540462306a36Sopenharmony_ci if (walk_size + len > addrlen) 540562306a36Sopenharmony_ci return -EINVAL; 540662306a36Sopenharmony_ci 540762306a36Sopenharmony_ci err = -EINVAL; 540862306a36Sopenharmony_ci switch (optname) { 540962306a36Sopenharmony_ci /* Bind checks */ 541062306a36Sopenharmony_ci case SCTP_PRIMARY_ADDR: 541162306a36Sopenharmony_ci case SCTP_SET_PEER_PRIMARY_ADDR: 541262306a36Sopenharmony_ci case SCTP_SOCKOPT_BINDX_ADD: 541362306a36Sopenharmony_ci err = selinux_socket_bind(sock, addr, len); 541462306a36Sopenharmony_ci break; 541562306a36Sopenharmony_ci /* Connect checks */ 541662306a36Sopenharmony_ci case SCTP_SOCKOPT_CONNECTX: 541762306a36Sopenharmony_ci case SCTP_PARAM_SET_PRIMARY: 541862306a36Sopenharmony_ci case SCTP_PARAM_ADD_IP: 541962306a36Sopenharmony_ci case SCTP_SENDMSG_CONNECT: 542062306a36Sopenharmony_ci err = selinux_socket_connect_helper(sock, addr, len); 542162306a36Sopenharmony_ci if (err) 542262306a36Sopenharmony_ci return err; 542362306a36Sopenharmony_ci 542462306a36Sopenharmony_ci /* As selinux_sctp_bind_connect() is called by the 542562306a36Sopenharmony_ci * SCTP protocol layer, the socket is already locked, 542662306a36Sopenharmony_ci * therefore selinux_netlbl_socket_connect_locked() 542762306a36Sopenharmony_ci * is called here. The situations handled are: 542862306a36Sopenharmony_ci * sctp_connectx(3), sctp_sendmsg(3), sendmsg(2), 542962306a36Sopenharmony_ci * whenever a new IP address is added or when a new 543062306a36Sopenharmony_ci * primary address is selected. 543162306a36Sopenharmony_ci * Note that an SCTP connect(2) call happens before 543262306a36Sopenharmony_ci * the SCTP protocol layer and is handled via 543362306a36Sopenharmony_ci * selinux_socket_connect(). 543462306a36Sopenharmony_ci */ 543562306a36Sopenharmony_ci err = selinux_netlbl_socket_connect_locked(sk, addr); 543662306a36Sopenharmony_ci break; 543762306a36Sopenharmony_ci } 543862306a36Sopenharmony_ci 543962306a36Sopenharmony_ci if (err) 544062306a36Sopenharmony_ci return err; 544162306a36Sopenharmony_ci 544262306a36Sopenharmony_ci addr_buf += len; 544362306a36Sopenharmony_ci walk_size += len; 544462306a36Sopenharmony_ci } 544562306a36Sopenharmony_ci 544662306a36Sopenharmony_ci return 0; 544762306a36Sopenharmony_ci} 544862306a36Sopenharmony_ci 544962306a36Sopenharmony_ci/* Called whenever a new socket is created by accept(2) or sctp_peeloff(3). */ 545062306a36Sopenharmony_cistatic void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk, 545162306a36Sopenharmony_ci struct sock *newsk) 545262306a36Sopenharmony_ci{ 545362306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 545462306a36Sopenharmony_ci struct sk_security_struct *newsksec = newsk->sk_security; 545562306a36Sopenharmony_ci 545662306a36Sopenharmony_ci /* If policy does not support SECCLASS_SCTP_SOCKET then call 545762306a36Sopenharmony_ci * the non-sctp clone version. 545862306a36Sopenharmony_ci */ 545962306a36Sopenharmony_ci if (!selinux_policycap_extsockclass()) 546062306a36Sopenharmony_ci return selinux_sk_clone_security(sk, newsk); 546162306a36Sopenharmony_ci 546262306a36Sopenharmony_ci newsksec->sid = asoc->secid; 546362306a36Sopenharmony_ci newsksec->peer_sid = asoc->peer_secid; 546462306a36Sopenharmony_ci newsksec->sclass = sksec->sclass; 546562306a36Sopenharmony_ci selinux_netlbl_sctp_sk_clone(sk, newsk); 546662306a36Sopenharmony_ci} 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_cistatic int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk) 546962306a36Sopenharmony_ci{ 547062306a36Sopenharmony_ci struct sk_security_struct *ssksec = ssk->sk_security; 547162306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 547262306a36Sopenharmony_ci 547362306a36Sopenharmony_ci ssksec->sclass = sksec->sclass; 547462306a36Sopenharmony_ci ssksec->sid = sksec->sid; 547562306a36Sopenharmony_ci 547662306a36Sopenharmony_ci /* replace the existing subflow label deleting the existing one 547762306a36Sopenharmony_ci * and re-recreating a new label using the updated context 547862306a36Sopenharmony_ci */ 547962306a36Sopenharmony_ci selinux_netlbl_sk_security_free(ssksec); 548062306a36Sopenharmony_ci return selinux_netlbl_socket_post_create(ssk, ssk->sk_family); 548162306a36Sopenharmony_ci} 548262306a36Sopenharmony_ci 548362306a36Sopenharmony_cistatic int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb, 548462306a36Sopenharmony_ci struct request_sock *req) 548562306a36Sopenharmony_ci{ 548662306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 548762306a36Sopenharmony_ci int err; 548862306a36Sopenharmony_ci u16 family = req->rsk_ops->family; 548962306a36Sopenharmony_ci u32 connsid; 549062306a36Sopenharmony_ci u32 peersid; 549162306a36Sopenharmony_ci 549262306a36Sopenharmony_ci err = selinux_skb_peerlbl_sid(skb, family, &peersid); 549362306a36Sopenharmony_ci if (err) 549462306a36Sopenharmony_ci return err; 549562306a36Sopenharmony_ci err = selinux_conn_sid(sksec->sid, peersid, &connsid); 549662306a36Sopenharmony_ci if (err) 549762306a36Sopenharmony_ci return err; 549862306a36Sopenharmony_ci req->secid = connsid; 549962306a36Sopenharmony_ci req->peer_secid = peersid; 550062306a36Sopenharmony_ci 550162306a36Sopenharmony_ci return selinux_netlbl_inet_conn_request(req, family); 550262306a36Sopenharmony_ci} 550362306a36Sopenharmony_ci 550462306a36Sopenharmony_cistatic void selinux_inet_csk_clone(struct sock *newsk, 550562306a36Sopenharmony_ci const struct request_sock *req) 550662306a36Sopenharmony_ci{ 550762306a36Sopenharmony_ci struct sk_security_struct *newsksec = newsk->sk_security; 550862306a36Sopenharmony_ci 550962306a36Sopenharmony_ci newsksec->sid = req->secid; 551062306a36Sopenharmony_ci newsksec->peer_sid = req->peer_secid; 551162306a36Sopenharmony_ci /* NOTE: Ideally, we should also get the isec->sid for the 551262306a36Sopenharmony_ci new socket in sync, but we don't have the isec available yet. 551362306a36Sopenharmony_ci So we will wait until sock_graft to do it, by which 551462306a36Sopenharmony_ci time it will have been created and available. */ 551562306a36Sopenharmony_ci 551662306a36Sopenharmony_ci /* We don't need to take any sort of lock here as we are the only 551762306a36Sopenharmony_ci * thread with access to newsksec */ 551862306a36Sopenharmony_ci selinux_netlbl_inet_csk_clone(newsk, req->rsk_ops->family); 551962306a36Sopenharmony_ci} 552062306a36Sopenharmony_ci 552162306a36Sopenharmony_cistatic void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) 552262306a36Sopenharmony_ci{ 552362306a36Sopenharmony_ci u16 family = sk->sk_family; 552462306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 552562306a36Sopenharmony_ci 552662306a36Sopenharmony_ci /* handle mapped IPv4 packets arriving via IPv6 sockets */ 552762306a36Sopenharmony_ci if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) 552862306a36Sopenharmony_ci family = PF_INET; 552962306a36Sopenharmony_ci 553062306a36Sopenharmony_ci selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); 553162306a36Sopenharmony_ci} 553262306a36Sopenharmony_ci 553362306a36Sopenharmony_cistatic int selinux_secmark_relabel_packet(u32 sid) 553462306a36Sopenharmony_ci{ 553562306a36Sopenharmony_ci const struct task_security_struct *tsec; 553662306a36Sopenharmony_ci u32 tsid; 553762306a36Sopenharmony_ci 553862306a36Sopenharmony_ci tsec = selinux_cred(current_cred()); 553962306a36Sopenharmony_ci tsid = tsec->sid; 554062306a36Sopenharmony_ci 554162306a36Sopenharmony_ci return avc_has_perm(tsid, sid, SECCLASS_PACKET, PACKET__RELABELTO, 554262306a36Sopenharmony_ci NULL); 554362306a36Sopenharmony_ci} 554462306a36Sopenharmony_ci 554562306a36Sopenharmony_cistatic void selinux_secmark_refcount_inc(void) 554662306a36Sopenharmony_ci{ 554762306a36Sopenharmony_ci atomic_inc(&selinux_secmark_refcount); 554862306a36Sopenharmony_ci} 554962306a36Sopenharmony_ci 555062306a36Sopenharmony_cistatic void selinux_secmark_refcount_dec(void) 555162306a36Sopenharmony_ci{ 555262306a36Sopenharmony_ci atomic_dec(&selinux_secmark_refcount); 555362306a36Sopenharmony_ci} 555462306a36Sopenharmony_ci 555562306a36Sopenharmony_cistatic void selinux_req_classify_flow(const struct request_sock *req, 555662306a36Sopenharmony_ci struct flowi_common *flic) 555762306a36Sopenharmony_ci{ 555862306a36Sopenharmony_ci flic->flowic_secid = req->secid; 555962306a36Sopenharmony_ci} 556062306a36Sopenharmony_ci 556162306a36Sopenharmony_cistatic int selinux_tun_dev_alloc_security(void **security) 556262306a36Sopenharmony_ci{ 556362306a36Sopenharmony_ci struct tun_security_struct *tunsec; 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_ci tunsec = kzalloc(sizeof(*tunsec), GFP_KERNEL); 556662306a36Sopenharmony_ci if (!tunsec) 556762306a36Sopenharmony_ci return -ENOMEM; 556862306a36Sopenharmony_ci tunsec->sid = current_sid(); 556962306a36Sopenharmony_ci 557062306a36Sopenharmony_ci *security = tunsec; 557162306a36Sopenharmony_ci return 0; 557262306a36Sopenharmony_ci} 557362306a36Sopenharmony_ci 557462306a36Sopenharmony_cistatic void selinux_tun_dev_free_security(void *security) 557562306a36Sopenharmony_ci{ 557662306a36Sopenharmony_ci kfree(security); 557762306a36Sopenharmony_ci} 557862306a36Sopenharmony_ci 557962306a36Sopenharmony_cistatic int selinux_tun_dev_create(void) 558062306a36Sopenharmony_ci{ 558162306a36Sopenharmony_ci u32 sid = current_sid(); 558262306a36Sopenharmony_ci 558362306a36Sopenharmony_ci /* we aren't taking into account the "sockcreate" SID since the socket 558462306a36Sopenharmony_ci * that is being created here is not a socket in the traditional sense, 558562306a36Sopenharmony_ci * instead it is a private sock, accessible only to the kernel, and 558662306a36Sopenharmony_ci * representing a wide range of network traffic spanning multiple 558762306a36Sopenharmony_ci * connections unlike traditional sockets - check the TUN driver to 558862306a36Sopenharmony_ci * get a better understanding of why this socket is special */ 558962306a36Sopenharmony_ci 559062306a36Sopenharmony_ci return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE, 559162306a36Sopenharmony_ci NULL); 559262306a36Sopenharmony_ci} 559362306a36Sopenharmony_ci 559462306a36Sopenharmony_cistatic int selinux_tun_dev_attach_queue(void *security) 559562306a36Sopenharmony_ci{ 559662306a36Sopenharmony_ci struct tun_security_struct *tunsec = security; 559762306a36Sopenharmony_ci 559862306a36Sopenharmony_ci return avc_has_perm(current_sid(), tunsec->sid, SECCLASS_TUN_SOCKET, 559962306a36Sopenharmony_ci TUN_SOCKET__ATTACH_QUEUE, NULL); 560062306a36Sopenharmony_ci} 560162306a36Sopenharmony_ci 560262306a36Sopenharmony_cistatic int selinux_tun_dev_attach(struct sock *sk, void *security) 560362306a36Sopenharmony_ci{ 560462306a36Sopenharmony_ci struct tun_security_struct *tunsec = security; 560562306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 560662306a36Sopenharmony_ci 560762306a36Sopenharmony_ci /* we don't currently perform any NetLabel based labeling here and it 560862306a36Sopenharmony_ci * isn't clear that we would want to do so anyway; while we could apply 560962306a36Sopenharmony_ci * labeling without the support of the TUN user the resulting labeled 561062306a36Sopenharmony_ci * traffic from the other end of the connection would almost certainly 561162306a36Sopenharmony_ci * cause confusion to the TUN user that had no idea network labeling 561262306a36Sopenharmony_ci * protocols were being used */ 561362306a36Sopenharmony_ci 561462306a36Sopenharmony_ci sksec->sid = tunsec->sid; 561562306a36Sopenharmony_ci sksec->sclass = SECCLASS_TUN_SOCKET; 561662306a36Sopenharmony_ci 561762306a36Sopenharmony_ci return 0; 561862306a36Sopenharmony_ci} 561962306a36Sopenharmony_ci 562062306a36Sopenharmony_cistatic int selinux_tun_dev_open(void *security) 562162306a36Sopenharmony_ci{ 562262306a36Sopenharmony_ci struct tun_security_struct *tunsec = security; 562362306a36Sopenharmony_ci u32 sid = current_sid(); 562462306a36Sopenharmony_ci int err; 562562306a36Sopenharmony_ci 562662306a36Sopenharmony_ci err = avc_has_perm(sid, tunsec->sid, SECCLASS_TUN_SOCKET, 562762306a36Sopenharmony_ci TUN_SOCKET__RELABELFROM, NULL); 562862306a36Sopenharmony_ci if (err) 562962306a36Sopenharmony_ci return err; 563062306a36Sopenharmony_ci err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, 563162306a36Sopenharmony_ci TUN_SOCKET__RELABELTO, NULL); 563262306a36Sopenharmony_ci if (err) 563362306a36Sopenharmony_ci return err; 563462306a36Sopenharmony_ci tunsec->sid = sid; 563562306a36Sopenharmony_ci 563662306a36Sopenharmony_ci return 0; 563762306a36Sopenharmony_ci} 563862306a36Sopenharmony_ci 563962306a36Sopenharmony_ci#ifdef CONFIG_NETFILTER 564062306a36Sopenharmony_ci 564162306a36Sopenharmony_cistatic unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb, 564262306a36Sopenharmony_ci const struct nf_hook_state *state) 564362306a36Sopenharmony_ci{ 564462306a36Sopenharmony_ci int ifindex; 564562306a36Sopenharmony_ci u16 family; 564662306a36Sopenharmony_ci char *addrp; 564762306a36Sopenharmony_ci u32 peer_sid; 564862306a36Sopenharmony_ci struct common_audit_data ad; 564962306a36Sopenharmony_ci struct lsm_network_audit net; 565062306a36Sopenharmony_ci int secmark_active, peerlbl_active; 565162306a36Sopenharmony_ci 565262306a36Sopenharmony_ci if (!selinux_policycap_netpeer()) 565362306a36Sopenharmony_ci return NF_ACCEPT; 565462306a36Sopenharmony_ci 565562306a36Sopenharmony_ci secmark_active = selinux_secmark_enabled(); 565662306a36Sopenharmony_ci peerlbl_active = selinux_peerlbl_enabled(); 565762306a36Sopenharmony_ci if (!secmark_active && !peerlbl_active) 565862306a36Sopenharmony_ci return NF_ACCEPT; 565962306a36Sopenharmony_ci 566062306a36Sopenharmony_ci family = state->pf; 566162306a36Sopenharmony_ci if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) 566262306a36Sopenharmony_ci return NF_DROP; 566362306a36Sopenharmony_ci 566462306a36Sopenharmony_ci ifindex = state->in->ifindex; 566562306a36Sopenharmony_ci ad_net_init_from_iif(&ad, &net, ifindex, family); 566662306a36Sopenharmony_ci if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) 566762306a36Sopenharmony_ci return NF_DROP; 566862306a36Sopenharmony_ci 566962306a36Sopenharmony_ci if (peerlbl_active) { 567062306a36Sopenharmony_ci int err; 567162306a36Sopenharmony_ci 567262306a36Sopenharmony_ci err = selinux_inet_sys_rcv_skb(state->net, ifindex, 567362306a36Sopenharmony_ci addrp, family, peer_sid, &ad); 567462306a36Sopenharmony_ci if (err) { 567562306a36Sopenharmony_ci selinux_netlbl_err(skb, family, err, 1); 567662306a36Sopenharmony_ci return NF_DROP; 567762306a36Sopenharmony_ci } 567862306a36Sopenharmony_ci } 567962306a36Sopenharmony_ci 568062306a36Sopenharmony_ci if (secmark_active) 568162306a36Sopenharmony_ci if (avc_has_perm(peer_sid, skb->secmark, 568262306a36Sopenharmony_ci SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) 568362306a36Sopenharmony_ci return NF_DROP; 568462306a36Sopenharmony_ci 568562306a36Sopenharmony_ci if (netlbl_enabled()) 568662306a36Sopenharmony_ci /* we do this in the FORWARD path and not the POST_ROUTING 568762306a36Sopenharmony_ci * path because we want to make sure we apply the necessary 568862306a36Sopenharmony_ci * labeling before IPsec is applied so we can leverage AH 568962306a36Sopenharmony_ci * protection */ 569062306a36Sopenharmony_ci if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0) 569162306a36Sopenharmony_ci return NF_DROP; 569262306a36Sopenharmony_ci 569362306a36Sopenharmony_ci return NF_ACCEPT; 569462306a36Sopenharmony_ci} 569562306a36Sopenharmony_ci 569662306a36Sopenharmony_cistatic unsigned int selinux_ip_output(void *priv, struct sk_buff *skb, 569762306a36Sopenharmony_ci const struct nf_hook_state *state) 569862306a36Sopenharmony_ci{ 569962306a36Sopenharmony_ci struct sock *sk; 570062306a36Sopenharmony_ci u32 sid; 570162306a36Sopenharmony_ci 570262306a36Sopenharmony_ci if (!netlbl_enabled()) 570362306a36Sopenharmony_ci return NF_ACCEPT; 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_ci /* we do this in the LOCAL_OUT path and not the POST_ROUTING path 570662306a36Sopenharmony_ci * because we want to make sure we apply the necessary labeling 570762306a36Sopenharmony_ci * before IPsec is applied so we can leverage AH protection */ 570862306a36Sopenharmony_ci sk = skb->sk; 570962306a36Sopenharmony_ci if (sk) { 571062306a36Sopenharmony_ci struct sk_security_struct *sksec; 571162306a36Sopenharmony_ci 571262306a36Sopenharmony_ci if (sk_listener(sk)) 571362306a36Sopenharmony_ci /* if the socket is the listening state then this 571462306a36Sopenharmony_ci * packet is a SYN-ACK packet which means it needs to 571562306a36Sopenharmony_ci * be labeled based on the connection/request_sock and 571662306a36Sopenharmony_ci * not the parent socket. unfortunately, we can't 571762306a36Sopenharmony_ci * lookup the request_sock yet as it isn't queued on 571862306a36Sopenharmony_ci * the parent socket until after the SYN-ACK is sent. 571962306a36Sopenharmony_ci * the "solution" is to simply pass the packet as-is 572062306a36Sopenharmony_ci * as any IP option based labeling should be copied 572162306a36Sopenharmony_ci * from the initial connection request (in the IP 572262306a36Sopenharmony_ci * layer). it is far from ideal, but until we get a 572362306a36Sopenharmony_ci * security label in the packet itself this is the 572462306a36Sopenharmony_ci * best we can do. */ 572562306a36Sopenharmony_ci return NF_ACCEPT; 572662306a36Sopenharmony_ci 572762306a36Sopenharmony_ci /* standard practice, label using the parent socket */ 572862306a36Sopenharmony_ci sksec = sk->sk_security; 572962306a36Sopenharmony_ci sid = sksec->sid; 573062306a36Sopenharmony_ci } else 573162306a36Sopenharmony_ci sid = SECINITSID_KERNEL; 573262306a36Sopenharmony_ci if (selinux_netlbl_skbuff_setsid(skb, state->pf, sid) != 0) 573362306a36Sopenharmony_ci return NF_DROP; 573462306a36Sopenharmony_ci 573562306a36Sopenharmony_ci return NF_ACCEPT; 573662306a36Sopenharmony_ci} 573762306a36Sopenharmony_ci 573862306a36Sopenharmony_ci 573962306a36Sopenharmony_cistatic unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, 574062306a36Sopenharmony_ci const struct nf_hook_state *state) 574162306a36Sopenharmony_ci{ 574262306a36Sopenharmony_ci struct sock *sk; 574362306a36Sopenharmony_ci struct sk_security_struct *sksec; 574462306a36Sopenharmony_ci struct common_audit_data ad; 574562306a36Sopenharmony_ci struct lsm_network_audit net; 574662306a36Sopenharmony_ci u8 proto = 0; 574762306a36Sopenharmony_ci 574862306a36Sopenharmony_ci sk = skb_to_full_sk(skb); 574962306a36Sopenharmony_ci if (sk == NULL) 575062306a36Sopenharmony_ci return NF_ACCEPT; 575162306a36Sopenharmony_ci sksec = sk->sk_security; 575262306a36Sopenharmony_ci 575362306a36Sopenharmony_ci ad_net_init_from_iif(&ad, &net, state->out->ifindex, state->pf); 575462306a36Sopenharmony_ci if (selinux_parse_skb(skb, &ad, NULL, 0, &proto)) 575562306a36Sopenharmony_ci return NF_DROP; 575662306a36Sopenharmony_ci 575762306a36Sopenharmony_ci if (selinux_secmark_enabled()) 575862306a36Sopenharmony_ci if (avc_has_perm(sksec->sid, skb->secmark, 575962306a36Sopenharmony_ci SECCLASS_PACKET, PACKET__SEND, &ad)) 576062306a36Sopenharmony_ci return NF_DROP_ERR(-ECONNREFUSED); 576162306a36Sopenharmony_ci 576262306a36Sopenharmony_ci if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) 576362306a36Sopenharmony_ci return NF_DROP_ERR(-ECONNREFUSED); 576462306a36Sopenharmony_ci 576562306a36Sopenharmony_ci return NF_ACCEPT; 576662306a36Sopenharmony_ci} 576762306a36Sopenharmony_ci 576862306a36Sopenharmony_cistatic unsigned int selinux_ip_postroute(void *priv, 576962306a36Sopenharmony_ci struct sk_buff *skb, 577062306a36Sopenharmony_ci const struct nf_hook_state *state) 577162306a36Sopenharmony_ci{ 577262306a36Sopenharmony_ci u16 family; 577362306a36Sopenharmony_ci u32 secmark_perm; 577462306a36Sopenharmony_ci u32 peer_sid; 577562306a36Sopenharmony_ci int ifindex; 577662306a36Sopenharmony_ci struct sock *sk; 577762306a36Sopenharmony_ci struct common_audit_data ad; 577862306a36Sopenharmony_ci struct lsm_network_audit net; 577962306a36Sopenharmony_ci char *addrp; 578062306a36Sopenharmony_ci int secmark_active, peerlbl_active; 578162306a36Sopenharmony_ci 578262306a36Sopenharmony_ci /* If any sort of compatibility mode is enabled then handoff processing 578362306a36Sopenharmony_ci * to the selinux_ip_postroute_compat() function to deal with the 578462306a36Sopenharmony_ci * special handling. We do this in an attempt to keep this function 578562306a36Sopenharmony_ci * as fast and as clean as possible. */ 578662306a36Sopenharmony_ci if (!selinux_policycap_netpeer()) 578762306a36Sopenharmony_ci return selinux_ip_postroute_compat(skb, state); 578862306a36Sopenharmony_ci 578962306a36Sopenharmony_ci secmark_active = selinux_secmark_enabled(); 579062306a36Sopenharmony_ci peerlbl_active = selinux_peerlbl_enabled(); 579162306a36Sopenharmony_ci if (!secmark_active && !peerlbl_active) 579262306a36Sopenharmony_ci return NF_ACCEPT; 579362306a36Sopenharmony_ci 579462306a36Sopenharmony_ci sk = skb_to_full_sk(skb); 579562306a36Sopenharmony_ci 579662306a36Sopenharmony_ci#ifdef CONFIG_XFRM 579762306a36Sopenharmony_ci /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec 579862306a36Sopenharmony_ci * packet transformation so allow the packet to pass without any checks 579962306a36Sopenharmony_ci * since we'll have another chance to perform access control checks 580062306a36Sopenharmony_ci * when the packet is on it's final way out. 580162306a36Sopenharmony_ci * NOTE: there appear to be some IPv6 multicast cases where skb->dst 580262306a36Sopenharmony_ci * is NULL, in this case go ahead and apply access control. 580362306a36Sopenharmony_ci * NOTE: if this is a local socket (skb->sk != NULL) that is in the 580462306a36Sopenharmony_ci * TCP listening state we cannot wait until the XFRM processing 580562306a36Sopenharmony_ci * is done as we will miss out on the SA label if we do; 580662306a36Sopenharmony_ci * unfortunately, this means more work, but it is only once per 580762306a36Sopenharmony_ci * connection. */ 580862306a36Sopenharmony_ci if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && 580962306a36Sopenharmony_ci !(sk && sk_listener(sk))) 581062306a36Sopenharmony_ci return NF_ACCEPT; 581162306a36Sopenharmony_ci#endif 581262306a36Sopenharmony_ci 581362306a36Sopenharmony_ci family = state->pf; 581462306a36Sopenharmony_ci if (sk == NULL) { 581562306a36Sopenharmony_ci /* Without an associated socket the packet is either coming 581662306a36Sopenharmony_ci * from the kernel or it is being forwarded; check the packet 581762306a36Sopenharmony_ci * to determine which and if the packet is being forwarded 581862306a36Sopenharmony_ci * query the packet directly to determine the security label. */ 581962306a36Sopenharmony_ci if (skb->skb_iif) { 582062306a36Sopenharmony_ci secmark_perm = PACKET__FORWARD_OUT; 582162306a36Sopenharmony_ci if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) 582262306a36Sopenharmony_ci return NF_DROP; 582362306a36Sopenharmony_ci } else { 582462306a36Sopenharmony_ci secmark_perm = PACKET__SEND; 582562306a36Sopenharmony_ci peer_sid = SECINITSID_KERNEL; 582662306a36Sopenharmony_ci } 582762306a36Sopenharmony_ci } else if (sk_listener(sk)) { 582862306a36Sopenharmony_ci /* Locally generated packet but the associated socket is in the 582962306a36Sopenharmony_ci * listening state which means this is a SYN-ACK packet. In 583062306a36Sopenharmony_ci * this particular case the correct security label is assigned 583162306a36Sopenharmony_ci * to the connection/request_sock but unfortunately we can't 583262306a36Sopenharmony_ci * query the request_sock as it isn't queued on the parent 583362306a36Sopenharmony_ci * socket until after the SYN-ACK packet is sent; the only 583462306a36Sopenharmony_ci * viable choice is to regenerate the label like we do in 583562306a36Sopenharmony_ci * selinux_inet_conn_request(). See also selinux_ip_output() 583662306a36Sopenharmony_ci * for similar problems. */ 583762306a36Sopenharmony_ci u32 skb_sid; 583862306a36Sopenharmony_ci struct sk_security_struct *sksec; 583962306a36Sopenharmony_ci 584062306a36Sopenharmony_ci sksec = sk->sk_security; 584162306a36Sopenharmony_ci if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) 584262306a36Sopenharmony_ci return NF_DROP; 584362306a36Sopenharmony_ci /* At this point, if the returned skb peerlbl is SECSID_NULL 584462306a36Sopenharmony_ci * and the packet has been through at least one XFRM 584562306a36Sopenharmony_ci * transformation then we must be dealing with the "final" 584662306a36Sopenharmony_ci * form of labeled IPsec packet; since we've already applied 584762306a36Sopenharmony_ci * all of our access controls on this packet we can safely 584862306a36Sopenharmony_ci * pass the packet. */ 584962306a36Sopenharmony_ci if (skb_sid == SECSID_NULL) { 585062306a36Sopenharmony_ci switch (family) { 585162306a36Sopenharmony_ci case PF_INET: 585262306a36Sopenharmony_ci if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) 585362306a36Sopenharmony_ci return NF_ACCEPT; 585462306a36Sopenharmony_ci break; 585562306a36Sopenharmony_ci case PF_INET6: 585662306a36Sopenharmony_ci if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) 585762306a36Sopenharmony_ci return NF_ACCEPT; 585862306a36Sopenharmony_ci break; 585962306a36Sopenharmony_ci default: 586062306a36Sopenharmony_ci return NF_DROP_ERR(-ECONNREFUSED); 586162306a36Sopenharmony_ci } 586262306a36Sopenharmony_ci } 586362306a36Sopenharmony_ci if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) 586462306a36Sopenharmony_ci return NF_DROP; 586562306a36Sopenharmony_ci secmark_perm = PACKET__SEND; 586662306a36Sopenharmony_ci } else { 586762306a36Sopenharmony_ci /* Locally generated packet, fetch the security label from the 586862306a36Sopenharmony_ci * associated socket. */ 586962306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 587062306a36Sopenharmony_ci peer_sid = sksec->sid; 587162306a36Sopenharmony_ci secmark_perm = PACKET__SEND; 587262306a36Sopenharmony_ci } 587362306a36Sopenharmony_ci 587462306a36Sopenharmony_ci ifindex = state->out->ifindex; 587562306a36Sopenharmony_ci ad_net_init_from_iif(&ad, &net, ifindex, family); 587662306a36Sopenharmony_ci if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) 587762306a36Sopenharmony_ci return NF_DROP; 587862306a36Sopenharmony_ci 587962306a36Sopenharmony_ci if (secmark_active) 588062306a36Sopenharmony_ci if (avc_has_perm(peer_sid, skb->secmark, 588162306a36Sopenharmony_ci SECCLASS_PACKET, secmark_perm, &ad)) 588262306a36Sopenharmony_ci return NF_DROP_ERR(-ECONNREFUSED); 588362306a36Sopenharmony_ci 588462306a36Sopenharmony_ci if (peerlbl_active) { 588562306a36Sopenharmony_ci u32 if_sid; 588662306a36Sopenharmony_ci u32 node_sid; 588762306a36Sopenharmony_ci 588862306a36Sopenharmony_ci if (sel_netif_sid(state->net, ifindex, &if_sid)) 588962306a36Sopenharmony_ci return NF_DROP; 589062306a36Sopenharmony_ci if (avc_has_perm(peer_sid, if_sid, 589162306a36Sopenharmony_ci SECCLASS_NETIF, NETIF__EGRESS, &ad)) 589262306a36Sopenharmony_ci return NF_DROP_ERR(-ECONNREFUSED); 589362306a36Sopenharmony_ci 589462306a36Sopenharmony_ci if (sel_netnode_sid(addrp, family, &node_sid)) 589562306a36Sopenharmony_ci return NF_DROP; 589662306a36Sopenharmony_ci if (avc_has_perm(peer_sid, node_sid, 589762306a36Sopenharmony_ci SECCLASS_NODE, NODE__SENDTO, &ad)) 589862306a36Sopenharmony_ci return NF_DROP_ERR(-ECONNREFUSED); 589962306a36Sopenharmony_ci } 590062306a36Sopenharmony_ci 590162306a36Sopenharmony_ci return NF_ACCEPT; 590262306a36Sopenharmony_ci} 590362306a36Sopenharmony_ci#endif /* CONFIG_NETFILTER */ 590462306a36Sopenharmony_ci 590562306a36Sopenharmony_cistatic int selinux_netlink_send(struct sock *sk, struct sk_buff *skb) 590662306a36Sopenharmony_ci{ 590762306a36Sopenharmony_ci int rc = 0; 590862306a36Sopenharmony_ci unsigned int msg_len; 590962306a36Sopenharmony_ci unsigned int data_len = skb->len; 591062306a36Sopenharmony_ci unsigned char *data = skb->data; 591162306a36Sopenharmony_ci struct nlmsghdr *nlh; 591262306a36Sopenharmony_ci struct sk_security_struct *sksec = sk->sk_security; 591362306a36Sopenharmony_ci u16 sclass = sksec->sclass; 591462306a36Sopenharmony_ci u32 perm; 591562306a36Sopenharmony_ci 591662306a36Sopenharmony_ci while (data_len >= nlmsg_total_size(0)) { 591762306a36Sopenharmony_ci nlh = (struct nlmsghdr *)data; 591862306a36Sopenharmony_ci 591962306a36Sopenharmony_ci /* NOTE: the nlmsg_len field isn't reliably set by some netlink 592062306a36Sopenharmony_ci * users which means we can't reject skb's with bogus 592162306a36Sopenharmony_ci * length fields; our solution is to follow what 592262306a36Sopenharmony_ci * netlink_rcv_skb() does and simply skip processing at 592362306a36Sopenharmony_ci * messages with length fields that are clearly junk 592462306a36Sopenharmony_ci */ 592562306a36Sopenharmony_ci if (nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > data_len) 592662306a36Sopenharmony_ci return 0; 592762306a36Sopenharmony_ci 592862306a36Sopenharmony_ci rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm); 592962306a36Sopenharmony_ci if (rc == 0) { 593062306a36Sopenharmony_ci rc = sock_has_perm(sk, perm); 593162306a36Sopenharmony_ci if (rc) 593262306a36Sopenharmony_ci return rc; 593362306a36Sopenharmony_ci } else if (rc == -EINVAL) { 593462306a36Sopenharmony_ci /* -EINVAL is a missing msg/perm mapping */ 593562306a36Sopenharmony_ci pr_warn_ratelimited("SELinux: unrecognized netlink" 593662306a36Sopenharmony_ci " message: protocol=%hu nlmsg_type=%hu sclass=%s" 593762306a36Sopenharmony_ci " pid=%d comm=%s\n", 593862306a36Sopenharmony_ci sk->sk_protocol, nlh->nlmsg_type, 593962306a36Sopenharmony_ci secclass_map[sclass - 1].name, 594062306a36Sopenharmony_ci task_pid_nr(current), current->comm); 594162306a36Sopenharmony_ci if (enforcing_enabled() && 594262306a36Sopenharmony_ci !security_get_allow_unknown()) 594362306a36Sopenharmony_ci return rc; 594462306a36Sopenharmony_ci rc = 0; 594562306a36Sopenharmony_ci } else if (rc == -ENOENT) { 594662306a36Sopenharmony_ci /* -ENOENT is a missing socket/class mapping, ignore */ 594762306a36Sopenharmony_ci rc = 0; 594862306a36Sopenharmony_ci } else { 594962306a36Sopenharmony_ci return rc; 595062306a36Sopenharmony_ci } 595162306a36Sopenharmony_ci 595262306a36Sopenharmony_ci /* move to the next message after applying netlink padding */ 595362306a36Sopenharmony_ci msg_len = NLMSG_ALIGN(nlh->nlmsg_len); 595462306a36Sopenharmony_ci if (msg_len >= data_len) 595562306a36Sopenharmony_ci return 0; 595662306a36Sopenharmony_ci data_len -= msg_len; 595762306a36Sopenharmony_ci data += msg_len; 595862306a36Sopenharmony_ci } 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_ci return rc; 596162306a36Sopenharmony_ci} 596262306a36Sopenharmony_ci 596362306a36Sopenharmony_cistatic void ipc_init_security(struct ipc_security_struct *isec, u16 sclass) 596462306a36Sopenharmony_ci{ 596562306a36Sopenharmony_ci isec->sclass = sclass; 596662306a36Sopenharmony_ci isec->sid = current_sid(); 596762306a36Sopenharmony_ci} 596862306a36Sopenharmony_ci 596962306a36Sopenharmony_cistatic int ipc_has_perm(struct kern_ipc_perm *ipc_perms, 597062306a36Sopenharmony_ci u32 perms) 597162306a36Sopenharmony_ci{ 597262306a36Sopenharmony_ci struct ipc_security_struct *isec; 597362306a36Sopenharmony_ci struct common_audit_data ad; 597462306a36Sopenharmony_ci u32 sid = current_sid(); 597562306a36Sopenharmony_ci 597662306a36Sopenharmony_ci isec = selinux_ipc(ipc_perms); 597762306a36Sopenharmony_ci 597862306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 597962306a36Sopenharmony_ci ad.u.ipc_id = ipc_perms->key; 598062306a36Sopenharmony_ci 598162306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); 598262306a36Sopenharmony_ci} 598362306a36Sopenharmony_ci 598462306a36Sopenharmony_cistatic int selinux_msg_msg_alloc_security(struct msg_msg *msg) 598562306a36Sopenharmony_ci{ 598662306a36Sopenharmony_ci struct msg_security_struct *msec; 598762306a36Sopenharmony_ci 598862306a36Sopenharmony_ci msec = selinux_msg_msg(msg); 598962306a36Sopenharmony_ci msec->sid = SECINITSID_UNLABELED; 599062306a36Sopenharmony_ci 599162306a36Sopenharmony_ci return 0; 599262306a36Sopenharmony_ci} 599362306a36Sopenharmony_ci 599462306a36Sopenharmony_ci/* message queue security operations */ 599562306a36Sopenharmony_cistatic int selinux_msg_queue_alloc_security(struct kern_ipc_perm *msq) 599662306a36Sopenharmony_ci{ 599762306a36Sopenharmony_ci struct ipc_security_struct *isec; 599862306a36Sopenharmony_ci struct common_audit_data ad; 599962306a36Sopenharmony_ci u32 sid = current_sid(); 600062306a36Sopenharmony_ci 600162306a36Sopenharmony_ci isec = selinux_ipc(msq); 600262306a36Sopenharmony_ci ipc_init_security(isec, SECCLASS_MSGQ); 600362306a36Sopenharmony_ci 600462306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 600562306a36Sopenharmony_ci ad.u.ipc_id = msq->key; 600662306a36Sopenharmony_ci 600762306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, 600862306a36Sopenharmony_ci MSGQ__CREATE, &ad); 600962306a36Sopenharmony_ci} 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_cistatic int selinux_msg_queue_associate(struct kern_ipc_perm *msq, int msqflg) 601262306a36Sopenharmony_ci{ 601362306a36Sopenharmony_ci struct ipc_security_struct *isec; 601462306a36Sopenharmony_ci struct common_audit_data ad; 601562306a36Sopenharmony_ci u32 sid = current_sid(); 601662306a36Sopenharmony_ci 601762306a36Sopenharmony_ci isec = selinux_ipc(msq); 601862306a36Sopenharmony_ci 601962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 602062306a36Sopenharmony_ci ad.u.ipc_id = msq->key; 602162306a36Sopenharmony_ci 602262306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, 602362306a36Sopenharmony_ci MSGQ__ASSOCIATE, &ad); 602462306a36Sopenharmony_ci} 602562306a36Sopenharmony_ci 602662306a36Sopenharmony_cistatic int selinux_msg_queue_msgctl(struct kern_ipc_perm *msq, int cmd) 602762306a36Sopenharmony_ci{ 602862306a36Sopenharmony_ci u32 perms; 602962306a36Sopenharmony_ci 603062306a36Sopenharmony_ci switch (cmd) { 603162306a36Sopenharmony_ci case IPC_INFO: 603262306a36Sopenharmony_ci case MSG_INFO: 603362306a36Sopenharmony_ci /* No specific object, just general system-wide information. */ 603462306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, 603562306a36Sopenharmony_ci SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); 603662306a36Sopenharmony_ci case IPC_STAT: 603762306a36Sopenharmony_ci case MSG_STAT: 603862306a36Sopenharmony_ci case MSG_STAT_ANY: 603962306a36Sopenharmony_ci perms = MSGQ__GETATTR | MSGQ__ASSOCIATE; 604062306a36Sopenharmony_ci break; 604162306a36Sopenharmony_ci case IPC_SET: 604262306a36Sopenharmony_ci perms = MSGQ__SETATTR; 604362306a36Sopenharmony_ci break; 604462306a36Sopenharmony_ci case IPC_RMID: 604562306a36Sopenharmony_ci perms = MSGQ__DESTROY; 604662306a36Sopenharmony_ci break; 604762306a36Sopenharmony_ci default: 604862306a36Sopenharmony_ci return 0; 604962306a36Sopenharmony_ci } 605062306a36Sopenharmony_ci 605162306a36Sopenharmony_ci return ipc_has_perm(msq, perms); 605262306a36Sopenharmony_ci} 605362306a36Sopenharmony_ci 605462306a36Sopenharmony_cistatic int selinux_msg_queue_msgsnd(struct kern_ipc_perm *msq, struct msg_msg *msg, int msqflg) 605562306a36Sopenharmony_ci{ 605662306a36Sopenharmony_ci struct ipc_security_struct *isec; 605762306a36Sopenharmony_ci struct msg_security_struct *msec; 605862306a36Sopenharmony_ci struct common_audit_data ad; 605962306a36Sopenharmony_ci u32 sid = current_sid(); 606062306a36Sopenharmony_ci int rc; 606162306a36Sopenharmony_ci 606262306a36Sopenharmony_ci isec = selinux_ipc(msq); 606362306a36Sopenharmony_ci msec = selinux_msg_msg(msg); 606462306a36Sopenharmony_ci 606562306a36Sopenharmony_ci /* 606662306a36Sopenharmony_ci * First time through, need to assign label to the message 606762306a36Sopenharmony_ci */ 606862306a36Sopenharmony_ci if (msec->sid == SECINITSID_UNLABELED) { 606962306a36Sopenharmony_ci /* 607062306a36Sopenharmony_ci * Compute new sid based on current process and 607162306a36Sopenharmony_ci * message queue this message will be stored in 607262306a36Sopenharmony_ci */ 607362306a36Sopenharmony_ci rc = security_transition_sid(sid, isec->sid, 607462306a36Sopenharmony_ci SECCLASS_MSG, NULL, &msec->sid); 607562306a36Sopenharmony_ci if (rc) 607662306a36Sopenharmony_ci return rc; 607762306a36Sopenharmony_ci } 607862306a36Sopenharmony_ci 607962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 608062306a36Sopenharmony_ci ad.u.ipc_id = msq->key; 608162306a36Sopenharmony_ci 608262306a36Sopenharmony_ci /* Can this process write to the queue? */ 608362306a36Sopenharmony_ci rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, 608462306a36Sopenharmony_ci MSGQ__WRITE, &ad); 608562306a36Sopenharmony_ci if (!rc) 608662306a36Sopenharmony_ci /* Can this process send the message */ 608762306a36Sopenharmony_ci rc = avc_has_perm(sid, msec->sid, SECCLASS_MSG, 608862306a36Sopenharmony_ci MSG__SEND, &ad); 608962306a36Sopenharmony_ci if (!rc) 609062306a36Sopenharmony_ci /* Can the message be put in the queue? */ 609162306a36Sopenharmony_ci rc = avc_has_perm(msec->sid, isec->sid, SECCLASS_MSGQ, 609262306a36Sopenharmony_ci MSGQ__ENQUEUE, &ad); 609362306a36Sopenharmony_ci 609462306a36Sopenharmony_ci return rc; 609562306a36Sopenharmony_ci} 609662306a36Sopenharmony_ci 609762306a36Sopenharmony_cistatic int selinux_msg_queue_msgrcv(struct kern_ipc_perm *msq, struct msg_msg *msg, 609862306a36Sopenharmony_ci struct task_struct *target, 609962306a36Sopenharmony_ci long type, int mode) 610062306a36Sopenharmony_ci{ 610162306a36Sopenharmony_ci struct ipc_security_struct *isec; 610262306a36Sopenharmony_ci struct msg_security_struct *msec; 610362306a36Sopenharmony_ci struct common_audit_data ad; 610462306a36Sopenharmony_ci u32 sid = task_sid_obj(target); 610562306a36Sopenharmony_ci int rc; 610662306a36Sopenharmony_ci 610762306a36Sopenharmony_ci isec = selinux_ipc(msq); 610862306a36Sopenharmony_ci msec = selinux_msg_msg(msg); 610962306a36Sopenharmony_ci 611062306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 611162306a36Sopenharmony_ci ad.u.ipc_id = msq->key; 611262306a36Sopenharmony_ci 611362306a36Sopenharmony_ci rc = avc_has_perm(sid, isec->sid, 611462306a36Sopenharmony_ci SECCLASS_MSGQ, MSGQ__READ, &ad); 611562306a36Sopenharmony_ci if (!rc) 611662306a36Sopenharmony_ci rc = avc_has_perm(sid, msec->sid, 611762306a36Sopenharmony_ci SECCLASS_MSG, MSG__RECEIVE, &ad); 611862306a36Sopenharmony_ci return rc; 611962306a36Sopenharmony_ci} 612062306a36Sopenharmony_ci 612162306a36Sopenharmony_ci/* Shared Memory security operations */ 612262306a36Sopenharmony_cistatic int selinux_shm_alloc_security(struct kern_ipc_perm *shp) 612362306a36Sopenharmony_ci{ 612462306a36Sopenharmony_ci struct ipc_security_struct *isec; 612562306a36Sopenharmony_ci struct common_audit_data ad; 612662306a36Sopenharmony_ci u32 sid = current_sid(); 612762306a36Sopenharmony_ci 612862306a36Sopenharmony_ci isec = selinux_ipc(shp); 612962306a36Sopenharmony_ci ipc_init_security(isec, SECCLASS_SHM); 613062306a36Sopenharmony_ci 613162306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 613262306a36Sopenharmony_ci ad.u.ipc_id = shp->key; 613362306a36Sopenharmony_ci 613462306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_SHM, 613562306a36Sopenharmony_ci SHM__CREATE, &ad); 613662306a36Sopenharmony_ci} 613762306a36Sopenharmony_ci 613862306a36Sopenharmony_cistatic int selinux_shm_associate(struct kern_ipc_perm *shp, int shmflg) 613962306a36Sopenharmony_ci{ 614062306a36Sopenharmony_ci struct ipc_security_struct *isec; 614162306a36Sopenharmony_ci struct common_audit_data ad; 614262306a36Sopenharmony_ci u32 sid = current_sid(); 614362306a36Sopenharmony_ci 614462306a36Sopenharmony_ci isec = selinux_ipc(shp); 614562306a36Sopenharmony_ci 614662306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 614762306a36Sopenharmony_ci ad.u.ipc_id = shp->key; 614862306a36Sopenharmony_ci 614962306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_SHM, 615062306a36Sopenharmony_ci SHM__ASSOCIATE, &ad); 615162306a36Sopenharmony_ci} 615262306a36Sopenharmony_ci 615362306a36Sopenharmony_ci/* Note, at this point, shp is locked down */ 615462306a36Sopenharmony_cistatic int selinux_shm_shmctl(struct kern_ipc_perm *shp, int cmd) 615562306a36Sopenharmony_ci{ 615662306a36Sopenharmony_ci u32 perms; 615762306a36Sopenharmony_ci 615862306a36Sopenharmony_ci switch (cmd) { 615962306a36Sopenharmony_ci case IPC_INFO: 616062306a36Sopenharmony_ci case SHM_INFO: 616162306a36Sopenharmony_ci /* No specific object, just general system-wide information. */ 616262306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, 616362306a36Sopenharmony_ci SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); 616462306a36Sopenharmony_ci case IPC_STAT: 616562306a36Sopenharmony_ci case SHM_STAT: 616662306a36Sopenharmony_ci case SHM_STAT_ANY: 616762306a36Sopenharmony_ci perms = SHM__GETATTR | SHM__ASSOCIATE; 616862306a36Sopenharmony_ci break; 616962306a36Sopenharmony_ci case IPC_SET: 617062306a36Sopenharmony_ci perms = SHM__SETATTR; 617162306a36Sopenharmony_ci break; 617262306a36Sopenharmony_ci case SHM_LOCK: 617362306a36Sopenharmony_ci case SHM_UNLOCK: 617462306a36Sopenharmony_ci perms = SHM__LOCK; 617562306a36Sopenharmony_ci break; 617662306a36Sopenharmony_ci case IPC_RMID: 617762306a36Sopenharmony_ci perms = SHM__DESTROY; 617862306a36Sopenharmony_ci break; 617962306a36Sopenharmony_ci default: 618062306a36Sopenharmony_ci return 0; 618162306a36Sopenharmony_ci } 618262306a36Sopenharmony_ci 618362306a36Sopenharmony_ci return ipc_has_perm(shp, perms); 618462306a36Sopenharmony_ci} 618562306a36Sopenharmony_ci 618662306a36Sopenharmony_cistatic int selinux_shm_shmat(struct kern_ipc_perm *shp, 618762306a36Sopenharmony_ci char __user *shmaddr, int shmflg) 618862306a36Sopenharmony_ci{ 618962306a36Sopenharmony_ci u32 perms; 619062306a36Sopenharmony_ci 619162306a36Sopenharmony_ci if (shmflg & SHM_RDONLY) 619262306a36Sopenharmony_ci perms = SHM__READ; 619362306a36Sopenharmony_ci else 619462306a36Sopenharmony_ci perms = SHM__READ | SHM__WRITE; 619562306a36Sopenharmony_ci 619662306a36Sopenharmony_ci return ipc_has_perm(shp, perms); 619762306a36Sopenharmony_ci} 619862306a36Sopenharmony_ci 619962306a36Sopenharmony_ci/* Semaphore security operations */ 620062306a36Sopenharmony_cistatic int selinux_sem_alloc_security(struct kern_ipc_perm *sma) 620162306a36Sopenharmony_ci{ 620262306a36Sopenharmony_ci struct ipc_security_struct *isec; 620362306a36Sopenharmony_ci struct common_audit_data ad; 620462306a36Sopenharmony_ci u32 sid = current_sid(); 620562306a36Sopenharmony_ci 620662306a36Sopenharmony_ci isec = selinux_ipc(sma); 620762306a36Sopenharmony_ci ipc_init_security(isec, SECCLASS_SEM); 620862306a36Sopenharmony_ci 620962306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 621062306a36Sopenharmony_ci ad.u.ipc_id = sma->key; 621162306a36Sopenharmony_ci 621262306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_SEM, 621362306a36Sopenharmony_ci SEM__CREATE, &ad); 621462306a36Sopenharmony_ci} 621562306a36Sopenharmony_ci 621662306a36Sopenharmony_cistatic int selinux_sem_associate(struct kern_ipc_perm *sma, int semflg) 621762306a36Sopenharmony_ci{ 621862306a36Sopenharmony_ci struct ipc_security_struct *isec; 621962306a36Sopenharmony_ci struct common_audit_data ad; 622062306a36Sopenharmony_ci u32 sid = current_sid(); 622162306a36Sopenharmony_ci 622262306a36Sopenharmony_ci isec = selinux_ipc(sma); 622362306a36Sopenharmony_ci 622462306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IPC; 622562306a36Sopenharmony_ci ad.u.ipc_id = sma->key; 622662306a36Sopenharmony_ci 622762306a36Sopenharmony_ci return avc_has_perm(sid, isec->sid, SECCLASS_SEM, 622862306a36Sopenharmony_ci SEM__ASSOCIATE, &ad); 622962306a36Sopenharmony_ci} 623062306a36Sopenharmony_ci 623162306a36Sopenharmony_ci/* Note, at this point, sma is locked down */ 623262306a36Sopenharmony_cistatic int selinux_sem_semctl(struct kern_ipc_perm *sma, int cmd) 623362306a36Sopenharmony_ci{ 623462306a36Sopenharmony_ci int err; 623562306a36Sopenharmony_ci u32 perms; 623662306a36Sopenharmony_ci 623762306a36Sopenharmony_ci switch (cmd) { 623862306a36Sopenharmony_ci case IPC_INFO: 623962306a36Sopenharmony_ci case SEM_INFO: 624062306a36Sopenharmony_ci /* No specific object, just general system-wide information. */ 624162306a36Sopenharmony_ci return avc_has_perm(current_sid(), SECINITSID_KERNEL, 624262306a36Sopenharmony_ci SECCLASS_SYSTEM, SYSTEM__IPC_INFO, NULL); 624362306a36Sopenharmony_ci case GETPID: 624462306a36Sopenharmony_ci case GETNCNT: 624562306a36Sopenharmony_ci case GETZCNT: 624662306a36Sopenharmony_ci perms = SEM__GETATTR; 624762306a36Sopenharmony_ci break; 624862306a36Sopenharmony_ci case GETVAL: 624962306a36Sopenharmony_ci case GETALL: 625062306a36Sopenharmony_ci perms = SEM__READ; 625162306a36Sopenharmony_ci break; 625262306a36Sopenharmony_ci case SETVAL: 625362306a36Sopenharmony_ci case SETALL: 625462306a36Sopenharmony_ci perms = SEM__WRITE; 625562306a36Sopenharmony_ci break; 625662306a36Sopenharmony_ci case IPC_RMID: 625762306a36Sopenharmony_ci perms = SEM__DESTROY; 625862306a36Sopenharmony_ci break; 625962306a36Sopenharmony_ci case IPC_SET: 626062306a36Sopenharmony_ci perms = SEM__SETATTR; 626162306a36Sopenharmony_ci break; 626262306a36Sopenharmony_ci case IPC_STAT: 626362306a36Sopenharmony_ci case SEM_STAT: 626462306a36Sopenharmony_ci case SEM_STAT_ANY: 626562306a36Sopenharmony_ci perms = SEM__GETATTR | SEM__ASSOCIATE; 626662306a36Sopenharmony_ci break; 626762306a36Sopenharmony_ci default: 626862306a36Sopenharmony_ci return 0; 626962306a36Sopenharmony_ci } 627062306a36Sopenharmony_ci 627162306a36Sopenharmony_ci err = ipc_has_perm(sma, perms); 627262306a36Sopenharmony_ci return err; 627362306a36Sopenharmony_ci} 627462306a36Sopenharmony_ci 627562306a36Sopenharmony_cistatic int selinux_sem_semop(struct kern_ipc_perm *sma, 627662306a36Sopenharmony_ci struct sembuf *sops, unsigned nsops, int alter) 627762306a36Sopenharmony_ci{ 627862306a36Sopenharmony_ci u32 perms; 627962306a36Sopenharmony_ci 628062306a36Sopenharmony_ci if (alter) 628162306a36Sopenharmony_ci perms = SEM__READ | SEM__WRITE; 628262306a36Sopenharmony_ci else 628362306a36Sopenharmony_ci perms = SEM__READ; 628462306a36Sopenharmony_ci 628562306a36Sopenharmony_ci return ipc_has_perm(sma, perms); 628662306a36Sopenharmony_ci} 628762306a36Sopenharmony_ci 628862306a36Sopenharmony_cistatic int selinux_ipc_permission(struct kern_ipc_perm *ipcp, short flag) 628962306a36Sopenharmony_ci{ 629062306a36Sopenharmony_ci u32 av = 0; 629162306a36Sopenharmony_ci 629262306a36Sopenharmony_ci av = 0; 629362306a36Sopenharmony_ci if (flag & S_IRUGO) 629462306a36Sopenharmony_ci av |= IPC__UNIX_READ; 629562306a36Sopenharmony_ci if (flag & S_IWUGO) 629662306a36Sopenharmony_ci av |= IPC__UNIX_WRITE; 629762306a36Sopenharmony_ci 629862306a36Sopenharmony_ci if (av == 0) 629962306a36Sopenharmony_ci return 0; 630062306a36Sopenharmony_ci 630162306a36Sopenharmony_ci return ipc_has_perm(ipcp, av); 630262306a36Sopenharmony_ci} 630362306a36Sopenharmony_ci 630462306a36Sopenharmony_cistatic void selinux_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid) 630562306a36Sopenharmony_ci{ 630662306a36Sopenharmony_ci struct ipc_security_struct *isec = selinux_ipc(ipcp); 630762306a36Sopenharmony_ci *secid = isec->sid; 630862306a36Sopenharmony_ci} 630962306a36Sopenharmony_ci 631062306a36Sopenharmony_cistatic void selinux_d_instantiate(struct dentry *dentry, struct inode *inode) 631162306a36Sopenharmony_ci{ 631262306a36Sopenharmony_ci if (inode) 631362306a36Sopenharmony_ci inode_doinit_with_dentry(inode, dentry); 631462306a36Sopenharmony_ci} 631562306a36Sopenharmony_ci 631662306a36Sopenharmony_cistatic int selinux_getprocattr(struct task_struct *p, 631762306a36Sopenharmony_ci const char *name, char **value) 631862306a36Sopenharmony_ci{ 631962306a36Sopenharmony_ci const struct task_security_struct *__tsec; 632062306a36Sopenharmony_ci u32 sid; 632162306a36Sopenharmony_ci int error; 632262306a36Sopenharmony_ci unsigned len; 632362306a36Sopenharmony_ci 632462306a36Sopenharmony_ci rcu_read_lock(); 632562306a36Sopenharmony_ci __tsec = selinux_cred(__task_cred(p)); 632662306a36Sopenharmony_ci 632762306a36Sopenharmony_ci if (current != p) { 632862306a36Sopenharmony_ci error = avc_has_perm(current_sid(), __tsec->sid, 632962306a36Sopenharmony_ci SECCLASS_PROCESS, PROCESS__GETATTR, NULL); 633062306a36Sopenharmony_ci if (error) 633162306a36Sopenharmony_ci goto bad; 633262306a36Sopenharmony_ci } 633362306a36Sopenharmony_ci 633462306a36Sopenharmony_ci if (!strcmp(name, "current")) 633562306a36Sopenharmony_ci sid = __tsec->sid; 633662306a36Sopenharmony_ci else if (!strcmp(name, "prev")) 633762306a36Sopenharmony_ci sid = __tsec->osid; 633862306a36Sopenharmony_ci else if (!strcmp(name, "exec")) 633962306a36Sopenharmony_ci sid = __tsec->exec_sid; 634062306a36Sopenharmony_ci else if (!strcmp(name, "fscreate")) 634162306a36Sopenharmony_ci sid = __tsec->create_sid; 634262306a36Sopenharmony_ci else if (!strcmp(name, "keycreate")) 634362306a36Sopenharmony_ci sid = __tsec->keycreate_sid; 634462306a36Sopenharmony_ci else if (!strcmp(name, "sockcreate")) 634562306a36Sopenharmony_ci sid = __tsec->sockcreate_sid; 634662306a36Sopenharmony_ci else { 634762306a36Sopenharmony_ci error = -EINVAL; 634862306a36Sopenharmony_ci goto bad; 634962306a36Sopenharmony_ci } 635062306a36Sopenharmony_ci rcu_read_unlock(); 635162306a36Sopenharmony_ci 635262306a36Sopenharmony_ci if (!sid) 635362306a36Sopenharmony_ci return 0; 635462306a36Sopenharmony_ci 635562306a36Sopenharmony_ci error = security_sid_to_context(sid, value, &len); 635662306a36Sopenharmony_ci if (error) 635762306a36Sopenharmony_ci return error; 635862306a36Sopenharmony_ci return len; 635962306a36Sopenharmony_ci 636062306a36Sopenharmony_cibad: 636162306a36Sopenharmony_ci rcu_read_unlock(); 636262306a36Sopenharmony_ci return error; 636362306a36Sopenharmony_ci} 636462306a36Sopenharmony_ci 636562306a36Sopenharmony_cistatic int selinux_setprocattr(const char *name, void *value, size_t size) 636662306a36Sopenharmony_ci{ 636762306a36Sopenharmony_ci struct task_security_struct *tsec; 636862306a36Sopenharmony_ci struct cred *new; 636962306a36Sopenharmony_ci u32 mysid = current_sid(), sid = 0, ptsid; 637062306a36Sopenharmony_ci int error; 637162306a36Sopenharmony_ci char *str = value; 637262306a36Sopenharmony_ci 637362306a36Sopenharmony_ci /* 637462306a36Sopenharmony_ci * Basic control over ability to set these attributes at all. 637562306a36Sopenharmony_ci */ 637662306a36Sopenharmony_ci if (!strcmp(name, "exec")) 637762306a36Sopenharmony_ci error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, 637862306a36Sopenharmony_ci PROCESS__SETEXEC, NULL); 637962306a36Sopenharmony_ci else if (!strcmp(name, "fscreate")) 638062306a36Sopenharmony_ci error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, 638162306a36Sopenharmony_ci PROCESS__SETFSCREATE, NULL); 638262306a36Sopenharmony_ci else if (!strcmp(name, "keycreate")) 638362306a36Sopenharmony_ci error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, 638462306a36Sopenharmony_ci PROCESS__SETKEYCREATE, NULL); 638562306a36Sopenharmony_ci else if (!strcmp(name, "sockcreate")) 638662306a36Sopenharmony_ci error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, 638762306a36Sopenharmony_ci PROCESS__SETSOCKCREATE, NULL); 638862306a36Sopenharmony_ci else if (!strcmp(name, "current")) 638962306a36Sopenharmony_ci error = avc_has_perm(mysid, mysid, SECCLASS_PROCESS, 639062306a36Sopenharmony_ci PROCESS__SETCURRENT, NULL); 639162306a36Sopenharmony_ci else 639262306a36Sopenharmony_ci error = -EINVAL; 639362306a36Sopenharmony_ci if (error) 639462306a36Sopenharmony_ci return error; 639562306a36Sopenharmony_ci 639662306a36Sopenharmony_ci /* Obtain a SID for the context, if one was specified. */ 639762306a36Sopenharmony_ci if (size && str[0] && str[0] != '\n') { 639862306a36Sopenharmony_ci if (str[size-1] == '\n') { 639962306a36Sopenharmony_ci str[size-1] = 0; 640062306a36Sopenharmony_ci size--; 640162306a36Sopenharmony_ci } 640262306a36Sopenharmony_ci error = security_context_to_sid(value, size, 640362306a36Sopenharmony_ci &sid, GFP_KERNEL); 640462306a36Sopenharmony_ci if (error == -EINVAL && !strcmp(name, "fscreate")) { 640562306a36Sopenharmony_ci if (!has_cap_mac_admin(true)) { 640662306a36Sopenharmony_ci struct audit_buffer *ab; 640762306a36Sopenharmony_ci size_t audit_size; 640862306a36Sopenharmony_ci 640962306a36Sopenharmony_ci /* We strip a nul only if it is at the end, otherwise the 641062306a36Sopenharmony_ci * context contains a nul and we should audit that */ 641162306a36Sopenharmony_ci if (str[size - 1] == '\0') 641262306a36Sopenharmony_ci audit_size = size - 1; 641362306a36Sopenharmony_ci else 641462306a36Sopenharmony_ci audit_size = size; 641562306a36Sopenharmony_ci ab = audit_log_start(audit_context(), 641662306a36Sopenharmony_ci GFP_ATOMIC, 641762306a36Sopenharmony_ci AUDIT_SELINUX_ERR); 641862306a36Sopenharmony_ci if (!ab) 641962306a36Sopenharmony_ci return error; 642062306a36Sopenharmony_ci audit_log_format(ab, "op=fscreate invalid_context="); 642162306a36Sopenharmony_ci audit_log_n_untrustedstring(ab, value, audit_size); 642262306a36Sopenharmony_ci audit_log_end(ab); 642362306a36Sopenharmony_ci 642462306a36Sopenharmony_ci return error; 642562306a36Sopenharmony_ci } 642662306a36Sopenharmony_ci error = security_context_to_sid_force(value, size, 642762306a36Sopenharmony_ci &sid); 642862306a36Sopenharmony_ci } 642962306a36Sopenharmony_ci if (error) 643062306a36Sopenharmony_ci return error; 643162306a36Sopenharmony_ci } 643262306a36Sopenharmony_ci 643362306a36Sopenharmony_ci new = prepare_creds(); 643462306a36Sopenharmony_ci if (!new) 643562306a36Sopenharmony_ci return -ENOMEM; 643662306a36Sopenharmony_ci 643762306a36Sopenharmony_ci /* Permission checking based on the specified context is 643862306a36Sopenharmony_ci performed during the actual operation (execve, 643962306a36Sopenharmony_ci open/mkdir/...), when we know the full context of the 644062306a36Sopenharmony_ci operation. See selinux_bprm_creds_for_exec for the execve 644162306a36Sopenharmony_ci checks and may_create for the file creation checks. The 644262306a36Sopenharmony_ci operation will then fail if the context is not permitted. */ 644362306a36Sopenharmony_ci tsec = selinux_cred(new); 644462306a36Sopenharmony_ci if (!strcmp(name, "exec")) { 644562306a36Sopenharmony_ci tsec->exec_sid = sid; 644662306a36Sopenharmony_ci } else if (!strcmp(name, "fscreate")) { 644762306a36Sopenharmony_ci tsec->create_sid = sid; 644862306a36Sopenharmony_ci } else if (!strcmp(name, "keycreate")) { 644962306a36Sopenharmony_ci if (sid) { 645062306a36Sopenharmony_ci error = avc_has_perm(mysid, sid, 645162306a36Sopenharmony_ci SECCLASS_KEY, KEY__CREATE, NULL); 645262306a36Sopenharmony_ci if (error) 645362306a36Sopenharmony_ci goto abort_change; 645462306a36Sopenharmony_ci } 645562306a36Sopenharmony_ci tsec->keycreate_sid = sid; 645662306a36Sopenharmony_ci } else if (!strcmp(name, "sockcreate")) { 645762306a36Sopenharmony_ci tsec->sockcreate_sid = sid; 645862306a36Sopenharmony_ci } else if (!strcmp(name, "current")) { 645962306a36Sopenharmony_ci error = -EINVAL; 646062306a36Sopenharmony_ci if (sid == 0) 646162306a36Sopenharmony_ci goto abort_change; 646262306a36Sopenharmony_ci 646362306a36Sopenharmony_ci /* Only allow single threaded processes to change context */ 646462306a36Sopenharmony_ci if (!current_is_single_threaded()) { 646562306a36Sopenharmony_ci error = security_bounded_transition(tsec->sid, sid); 646662306a36Sopenharmony_ci if (error) 646762306a36Sopenharmony_ci goto abort_change; 646862306a36Sopenharmony_ci } 646962306a36Sopenharmony_ci 647062306a36Sopenharmony_ci /* Check permissions for the transition. */ 647162306a36Sopenharmony_ci error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, 647262306a36Sopenharmony_ci PROCESS__DYNTRANSITION, NULL); 647362306a36Sopenharmony_ci if (error) 647462306a36Sopenharmony_ci goto abort_change; 647562306a36Sopenharmony_ci 647662306a36Sopenharmony_ci /* Check for ptracing, and update the task SID if ok. 647762306a36Sopenharmony_ci Otherwise, leave SID unchanged and fail. */ 647862306a36Sopenharmony_ci ptsid = ptrace_parent_sid(); 647962306a36Sopenharmony_ci if (ptsid != 0) { 648062306a36Sopenharmony_ci error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, 648162306a36Sopenharmony_ci PROCESS__PTRACE, NULL); 648262306a36Sopenharmony_ci if (error) 648362306a36Sopenharmony_ci goto abort_change; 648462306a36Sopenharmony_ci } 648562306a36Sopenharmony_ci 648662306a36Sopenharmony_ci tsec->sid = sid; 648762306a36Sopenharmony_ci } else { 648862306a36Sopenharmony_ci error = -EINVAL; 648962306a36Sopenharmony_ci goto abort_change; 649062306a36Sopenharmony_ci } 649162306a36Sopenharmony_ci 649262306a36Sopenharmony_ci commit_creds(new); 649362306a36Sopenharmony_ci CALL_HCK_LITE_HOOK(ced_setattr_insert_lhck, current); 649462306a36Sopenharmony_ci return size; 649562306a36Sopenharmony_ci 649662306a36Sopenharmony_ciabort_change: 649762306a36Sopenharmony_ci abort_creds(new); 649862306a36Sopenharmony_ci return error; 649962306a36Sopenharmony_ci} 650062306a36Sopenharmony_ci 650162306a36Sopenharmony_cistatic int selinux_ismaclabel(const char *name) 650262306a36Sopenharmony_ci{ 650362306a36Sopenharmony_ci return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0); 650462306a36Sopenharmony_ci} 650562306a36Sopenharmony_ci 650662306a36Sopenharmony_cistatic int selinux_secid_to_secctx(u32 secid, char **secdata, u32 *seclen) 650762306a36Sopenharmony_ci{ 650862306a36Sopenharmony_ci return security_sid_to_context(secid, 650962306a36Sopenharmony_ci secdata, seclen); 651062306a36Sopenharmony_ci} 651162306a36Sopenharmony_ci 651262306a36Sopenharmony_cistatic int selinux_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid) 651362306a36Sopenharmony_ci{ 651462306a36Sopenharmony_ci return security_context_to_sid(secdata, seclen, 651562306a36Sopenharmony_ci secid, GFP_KERNEL); 651662306a36Sopenharmony_ci} 651762306a36Sopenharmony_ci 651862306a36Sopenharmony_cistatic void selinux_release_secctx(char *secdata, u32 seclen) 651962306a36Sopenharmony_ci{ 652062306a36Sopenharmony_ci kfree(secdata); 652162306a36Sopenharmony_ci} 652262306a36Sopenharmony_ci 652362306a36Sopenharmony_cistatic void selinux_inode_invalidate_secctx(struct inode *inode) 652462306a36Sopenharmony_ci{ 652562306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 652662306a36Sopenharmony_ci 652762306a36Sopenharmony_ci spin_lock(&isec->lock); 652862306a36Sopenharmony_ci isec->initialized = LABEL_INVALID; 652962306a36Sopenharmony_ci spin_unlock(&isec->lock); 653062306a36Sopenharmony_ci} 653162306a36Sopenharmony_ci 653262306a36Sopenharmony_ci/* 653362306a36Sopenharmony_ci * called with inode->i_mutex locked 653462306a36Sopenharmony_ci */ 653562306a36Sopenharmony_cistatic int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen) 653662306a36Sopenharmony_ci{ 653762306a36Sopenharmony_ci int rc = selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, 653862306a36Sopenharmony_ci ctx, ctxlen, 0); 653962306a36Sopenharmony_ci /* Do not return error when suppressing label (SBLABEL_MNT not set). */ 654062306a36Sopenharmony_ci return rc == -EOPNOTSUPP ? 0 : rc; 654162306a36Sopenharmony_ci} 654262306a36Sopenharmony_ci 654362306a36Sopenharmony_ci/* 654462306a36Sopenharmony_ci * called with inode->i_mutex locked 654562306a36Sopenharmony_ci */ 654662306a36Sopenharmony_cistatic int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen) 654762306a36Sopenharmony_ci{ 654862306a36Sopenharmony_ci return __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_SELINUX, 654962306a36Sopenharmony_ci ctx, ctxlen, 0); 655062306a36Sopenharmony_ci} 655162306a36Sopenharmony_ci 655262306a36Sopenharmony_cistatic int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) 655362306a36Sopenharmony_ci{ 655462306a36Sopenharmony_ci int len = 0; 655562306a36Sopenharmony_ci len = selinux_inode_getsecurity(&nop_mnt_idmap, inode, 655662306a36Sopenharmony_ci XATTR_SELINUX_SUFFIX, ctx, true); 655762306a36Sopenharmony_ci if (len < 0) 655862306a36Sopenharmony_ci return len; 655962306a36Sopenharmony_ci *ctxlen = len; 656062306a36Sopenharmony_ci return 0; 656162306a36Sopenharmony_ci} 656262306a36Sopenharmony_ci#ifdef CONFIG_KEYS 656362306a36Sopenharmony_ci 656462306a36Sopenharmony_cistatic int selinux_key_alloc(struct key *k, const struct cred *cred, 656562306a36Sopenharmony_ci unsigned long flags) 656662306a36Sopenharmony_ci{ 656762306a36Sopenharmony_ci const struct task_security_struct *tsec; 656862306a36Sopenharmony_ci struct key_security_struct *ksec; 656962306a36Sopenharmony_ci 657062306a36Sopenharmony_ci ksec = kzalloc(sizeof(struct key_security_struct), GFP_KERNEL); 657162306a36Sopenharmony_ci if (!ksec) 657262306a36Sopenharmony_ci return -ENOMEM; 657362306a36Sopenharmony_ci 657462306a36Sopenharmony_ci tsec = selinux_cred(cred); 657562306a36Sopenharmony_ci if (tsec->keycreate_sid) 657662306a36Sopenharmony_ci ksec->sid = tsec->keycreate_sid; 657762306a36Sopenharmony_ci else 657862306a36Sopenharmony_ci ksec->sid = tsec->sid; 657962306a36Sopenharmony_ci 658062306a36Sopenharmony_ci k->security = ksec; 658162306a36Sopenharmony_ci return 0; 658262306a36Sopenharmony_ci} 658362306a36Sopenharmony_ci 658462306a36Sopenharmony_cistatic void selinux_key_free(struct key *k) 658562306a36Sopenharmony_ci{ 658662306a36Sopenharmony_ci struct key_security_struct *ksec = k->security; 658762306a36Sopenharmony_ci 658862306a36Sopenharmony_ci k->security = NULL; 658962306a36Sopenharmony_ci kfree(ksec); 659062306a36Sopenharmony_ci} 659162306a36Sopenharmony_ci 659262306a36Sopenharmony_cistatic int selinux_key_permission(key_ref_t key_ref, 659362306a36Sopenharmony_ci const struct cred *cred, 659462306a36Sopenharmony_ci enum key_need_perm need_perm) 659562306a36Sopenharmony_ci{ 659662306a36Sopenharmony_ci struct key *key; 659762306a36Sopenharmony_ci struct key_security_struct *ksec; 659862306a36Sopenharmony_ci u32 perm, sid; 659962306a36Sopenharmony_ci 660062306a36Sopenharmony_ci switch (need_perm) { 660162306a36Sopenharmony_ci case KEY_NEED_VIEW: 660262306a36Sopenharmony_ci perm = KEY__VIEW; 660362306a36Sopenharmony_ci break; 660462306a36Sopenharmony_ci case KEY_NEED_READ: 660562306a36Sopenharmony_ci perm = KEY__READ; 660662306a36Sopenharmony_ci break; 660762306a36Sopenharmony_ci case KEY_NEED_WRITE: 660862306a36Sopenharmony_ci perm = KEY__WRITE; 660962306a36Sopenharmony_ci break; 661062306a36Sopenharmony_ci case KEY_NEED_SEARCH: 661162306a36Sopenharmony_ci perm = KEY__SEARCH; 661262306a36Sopenharmony_ci break; 661362306a36Sopenharmony_ci case KEY_NEED_LINK: 661462306a36Sopenharmony_ci perm = KEY__LINK; 661562306a36Sopenharmony_ci break; 661662306a36Sopenharmony_ci case KEY_NEED_SETATTR: 661762306a36Sopenharmony_ci perm = KEY__SETATTR; 661862306a36Sopenharmony_ci break; 661962306a36Sopenharmony_ci case KEY_NEED_UNLINK: 662062306a36Sopenharmony_ci case KEY_SYSADMIN_OVERRIDE: 662162306a36Sopenharmony_ci case KEY_AUTHTOKEN_OVERRIDE: 662262306a36Sopenharmony_ci case KEY_DEFER_PERM_CHECK: 662362306a36Sopenharmony_ci return 0; 662462306a36Sopenharmony_ci default: 662562306a36Sopenharmony_ci WARN_ON(1); 662662306a36Sopenharmony_ci return -EPERM; 662762306a36Sopenharmony_ci 662862306a36Sopenharmony_ci } 662962306a36Sopenharmony_ci 663062306a36Sopenharmony_ci sid = cred_sid(cred); 663162306a36Sopenharmony_ci key = key_ref_to_ptr(key_ref); 663262306a36Sopenharmony_ci ksec = key->security; 663362306a36Sopenharmony_ci 663462306a36Sopenharmony_ci return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, perm, NULL); 663562306a36Sopenharmony_ci} 663662306a36Sopenharmony_ci 663762306a36Sopenharmony_cistatic int selinux_key_getsecurity(struct key *key, char **_buffer) 663862306a36Sopenharmony_ci{ 663962306a36Sopenharmony_ci struct key_security_struct *ksec = key->security; 664062306a36Sopenharmony_ci char *context = NULL; 664162306a36Sopenharmony_ci unsigned len; 664262306a36Sopenharmony_ci int rc; 664362306a36Sopenharmony_ci 664462306a36Sopenharmony_ci rc = security_sid_to_context(ksec->sid, 664562306a36Sopenharmony_ci &context, &len); 664662306a36Sopenharmony_ci if (!rc) 664762306a36Sopenharmony_ci rc = len; 664862306a36Sopenharmony_ci *_buffer = context; 664962306a36Sopenharmony_ci return rc; 665062306a36Sopenharmony_ci} 665162306a36Sopenharmony_ci 665262306a36Sopenharmony_ci#ifdef CONFIG_KEY_NOTIFICATIONS 665362306a36Sopenharmony_cistatic int selinux_watch_key(struct key *key) 665462306a36Sopenharmony_ci{ 665562306a36Sopenharmony_ci struct key_security_struct *ksec = key->security; 665662306a36Sopenharmony_ci u32 sid = current_sid(); 665762306a36Sopenharmony_ci 665862306a36Sopenharmony_ci return avc_has_perm(sid, ksec->sid, SECCLASS_KEY, KEY__VIEW, NULL); 665962306a36Sopenharmony_ci} 666062306a36Sopenharmony_ci#endif 666162306a36Sopenharmony_ci#endif 666262306a36Sopenharmony_ci 666362306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_INFINIBAND 666462306a36Sopenharmony_cistatic int selinux_ib_pkey_access(void *ib_sec, u64 subnet_prefix, u16 pkey_val) 666562306a36Sopenharmony_ci{ 666662306a36Sopenharmony_ci struct common_audit_data ad; 666762306a36Sopenharmony_ci int err; 666862306a36Sopenharmony_ci u32 sid = 0; 666962306a36Sopenharmony_ci struct ib_security_struct *sec = ib_sec; 667062306a36Sopenharmony_ci struct lsm_ibpkey_audit ibpkey; 667162306a36Sopenharmony_ci 667262306a36Sopenharmony_ci err = sel_ib_pkey_sid(subnet_prefix, pkey_val, &sid); 667362306a36Sopenharmony_ci if (err) 667462306a36Sopenharmony_ci return err; 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IBPKEY; 667762306a36Sopenharmony_ci ibpkey.subnet_prefix = subnet_prefix; 667862306a36Sopenharmony_ci ibpkey.pkey = pkey_val; 667962306a36Sopenharmony_ci ad.u.ibpkey = &ibpkey; 668062306a36Sopenharmony_ci return avc_has_perm(sec->sid, sid, 668162306a36Sopenharmony_ci SECCLASS_INFINIBAND_PKEY, 668262306a36Sopenharmony_ci INFINIBAND_PKEY__ACCESS, &ad); 668362306a36Sopenharmony_ci} 668462306a36Sopenharmony_ci 668562306a36Sopenharmony_cistatic int selinux_ib_endport_manage_subnet(void *ib_sec, const char *dev_name, 668662306a36Sopenharmony_ci u8 port_num) 668762306a36Sopenharmony_ci{ 668862306a36Sopenharmony_ci struct common_audit_data ad; 668962306a36Sopenharmony_ci int err; 669062306a36Sopenharmony_ci u32 sid = 0; 669162306a36Sopenharmony_ci struct ib_security_struct *sec = ib_sec; 669262306a36Sopenharmony_ci struct lsm_ibendport_audit ibendport; 669362306a36Sopenharmony_ci 669462306a36Sopenharmony_ci err = security_ib_endport_sid(dev_name, port_num, 669562306a36Sopenharmony_ci &sid); 669662306a36Sopenharmony_ci 669762306a36Sopenharmony_ci if (err) 669862306a36Sopenharmony_ci return err; 669962306a36Sopenharmony_ci 670062306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_IBENDPORT; 670162306a36Sopenharmony_ci ibendport.dev_name = dev_name; 670262306a36Sopenharmony_ci ibendport.port = port_num; 670362306a36Sopenharmony_ci ad.u.ibendport = &ibendport; 670462306a36Sopenharmony_ci return avc_has_perm(sec->sid, sid, 670562306a36Sopenharmony_ci SECCLASS_INFINIBAND_ENDPORT, 670662306a36Sopenharmony_ci INFINIBAND_ENDPORT__MANAGE_SUBNET, &ad); 670762306a36Sopenharmony_ci} 670862306a36Sopenharmony_ci 670962306a36Sopenharmony_cistatic int selinux_ib_alloc_security(void **ib_sec) 671062306a36Sopenharmony_ci{ 671162306a36Sopenharmony_ci struct ib_security_struct *sec; 671262306a36Sopenharmony_ci 671362306a36Sopenharmony_ci sec = kzalloc(sizeof(*sec), GFP_KERNEL); 671462306a36Sopenharmony_ci if (!sec) 671562306a36Sopenharmony_ci return -ENOMEM; 671662306a36Sopenharmony_ci sec->sid = current_sid(); 671762306a36Sopenharmony_ci 671862306a36Sopenharmony_ci *ib_sec = sec; 671962306a36Sopenharmony_ci return 0; 672062306a36Sopenharmony_ci} 672162306a36Sopenharmony_ci 672262306a36Sopenharmony_cistatic void selinux_ib_free_security(void *ib_sec) 672362306a36Sopenharmony_ci{ 672462306a36Sopenharmony_ci kfree(ib_sec); 672562306a36Sopenharmony_ci} 672662306a36Sopenharmony_ci#endif 672762306a36Sopenharmony_ci 672862306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 672962306a36Sopenharmony_cistatic int selinux_bpf(int cmd, union bpf_attr *attr, 673062306a36Sopenharmony_ci unsigned int size) 673162306a36Sopenharmony_ci{ 673262306a36Sopenharmony_ci u32 sid = current_sid(); 673362306a36Sopenharmony_ci int ret; 673462306a36Sopenharmony_ci 673562306a36Sopenharmony_ci switch (cmd) { 673662306a36Sopenharmony_ci case BPF_MAP_CREATE: 673762306a36Sopenharmony_ci ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__MAP_CREATE, 673862306a36Sopenharmony_ci NULL); 673962306a36Sopenharmony_ci break; 674062306a36Sopenharmony_ci case BPF_PROG_LOAD: 674162306a36Sopenharmony_ci ret = avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, 674262306a36Sopenharmony_ci NULL); 674362306a36Sopenharmony_ci break; 674462306a36Sopenharmony_ci default: 674562306a36Sopenharmony_ci ret = 0; 674662306a36Sopenharmony_ci break; 674762306a36Sopenharmony_ci } 674862306a36Sopenharmony_ci 674962306a36Sopenharmony_ci return ret; 675062306a36Sopenharmony_ci} 675162306a36Sopenharmony_ci 675262306a36Sopenharmony_cistatic u32 bpf_map_fmode_to_av(fmode_t fmode) 675362306a36Sopenharmony_ci{ 675462306a36Sopenharmony_ci u32 av = 0; 675562306a36Sopenharmony_ci 675662306a36Sopenharmony_ci if (fmode & FMODE_READ) 675762306a36Sopenharmony_ci av |= BPF__MAP_READ; 675862306a36Sopenharmony_ci if (fmode & FMODE_WRITE) 675962306a36Sopenharmony_ci av |= BPF__MAP_WRITE; 676062306a36Sopenharmony_ci return av; 676162306a36Sopenharmony_ci} 676262306a36Sopenharmony_ci 676362306a36Sopenharmony_ci/* This function will check the file pass through unix socket or binder to see 676462306a36Sopenharmony_ci * if it is a bpf related object. And apply corresponding checks on the bpf 676562306a36Sopenharmony_ci * object based on the type. The bpf maps and programs, not like other files and 676662306a36Sopenharmony_ci * socket, are using a shared anonymous inode inside the kernel as their inode. 676762306a36Sopenharmony_ci * So checking that inode cannot identify if the process have privilege to 676862306a36Sopenharmony_ci * access the bpf object and that's why we have to add this additional check in 676962306a36Sopenharmony_ci * selinux_file_receive and selinux_binder_transfer_files. 677062306a36Sopenharmony_ci */ 677162306a36Sopenharmony_cistatic int bpf_fd_pass(const struct file *file, u32 sid) 677262306a36Sopenharmony_ci{ 677362306a36Sopenharmony_ci struct bpf_security_struct *bpfsec; 677462306a36Sopenharmony_ci struct bpf_prog *prog; 677562306a36Sopenharmony_ci struct bpf_map *map; 677662306a36Sopenharmony_ci int ret; 677762306a36Sopenharmony_ci 677862306a36Sopenharmony_ci if (file->f_op == &bpf_map_fops) { 677962306a36Sopenharmony_ci map = file->private_data; 678062306a36Sopenharmony_ci bpfsec = map->security; 678162306a36Sopenharmony_ci ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, 678262306a36Sopenharmony_ci bpf_map_fmode_to_av(file->f_mode), NULL); 678362306a36Sopenharmony_ci if (ret) 678462306a36Sopenharmony_ci return ret; 678562306a36Sopenharmony_ci } else if (file->f_op == &bpf_prog_fops) { 678662306a36Sopenharmony_ci prog = file->private_data; 678762306a36Sopenharmony_ci bpfsec = prog->aux->security; 678862306a36Sopenharmony_ci ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, 678962306a36Sopenharmony_ci BPF__PROG_RUN, NULL); 679062306a36Sopenharmony_ci if (ret) 679162306a36Sopenharmony_ci return ret; 679262306a36Sopenharmony_ci } 679362306a36Sopenharmony_ci return 0; 679462306a36Sopenharmony_ci} 679562306a36Sopenharmony_ci 679662306a36Sopenharmony_cistatic int selinux_bpf_map(struct bpf_map *map, fmode_t fmode) 679762306a36Sopenharmony_ci{ 679862306a36Sopenharmony_ci u32 sid = current_sid(); 679962306a36Sopenharmony_ci struct bpf_security_struct *bpfsec; 680062306a36Sopenharmony_ci 680162306a36Sopenharmony_ci bpfsec = map->security; 680262306a36Sopenharmony_ci return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, 680362306a36Sopenharmony_ci bpf_map_fmode_to_av(fmode), NULL); 680462306a36Sopenharmony_ci} 680562306a36Sopenharmony_ci 680662306a36Sopenharmony_cistatic int selinux_bpf_prog(struct bpf_prog *prog) 680762306a36Sopenharmony_ci{ 680862306a36Sopenharmony_ci u32 sid = current_sid(); 680962306a36Sopenharmony_ci struct bpf_security_struct *bpfsec; 681062306a36Sopenharmony_ci 681162306a36Sopenharmony_ci bpfsec = prog->aux->security; 681262306a36Sopenharmony_ci return avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, 681362306a36Sopenharmony_ci BPF__PROG_RUN, NULL); 681462306a36Sopenharmony_ci} 681562306a36Sopenharmony_ci 681662306a36Sopenharmony_cistatic int selinux_bpf_map_alloc(struct bpf_map *map) 681762306a36Sopenharmony_ci{ 681862306a36Sopenharmony_ci struct bpf_security_struct *bpfsec; 681962306a36Sopenharmony_ci 682062306a36Sopenharmony_ci bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL); 682162306a36Sopenharmony_ci if (!bpfsec) 682262306a36Sopenharmony_ci return -ENOMEM; 682362306a36Sopenharmony_ci 682462306a36Sopenharmony_ci bpfsec->sid = current_sid(); 682562306a36Sopenharmony_ci map->security = bpfsec; 682662306a36Sopenharmony_ci 682762306a36Sopenharmony_ci return 0; 682862306a36Sopenharmony_ci} 682962306a36Sopenharmony_ci 683062306a36Sopenharmony_cistatic void selinux_bpf_map_free(struct bpf_map *map) 683162306a36Sopenharmony_ci{ 683262306a36Sopenharmony_ci struct bpf_security_struct *bpfsec = map->security; 683362306a36Sopenharmony_ci 683462306a36Sopenharmony_ci map->security = NULL; 683562306a36Sopenharmony_ci kfree(bpfsec); 683662306a36Sopenharmony_ci} 683762306a36Sopenharmony_ci 683862306a36Sopenharmony_cistatic int selinux_bpf_prog_alloc(struct bpf_prog_aux *aux) 683962306a36Sopenharmony_ci{ 684062306a36Sopenharmony_ci struct bpf_security_struct *bpfsec; 684162306a36Sopenharmony_ci 684262306a36Sopenharmony_ci bpfsec = kzalloc(sizeof(*bpfsec), GFP_KERNEL); 684362306a36Sopenharmony_ci if (!bpfsec) 684462306a36Sopenharmony_ci return -ENOMEM; 684562306a36Sopenharmony_ci 684662306a36Sopenharmony_ci bpfsec->sid = current_sid(); 684762306a36Sopenharmony_ci aux->security = bpfsec; 684862306a36Sopenharmony_ci 684962306a36Sopenharmony_ci return 0; 685062306a36Sopenharmony_ci} 685162306a36Sopenharmony_ci 685262306a36Sopenharmony_cistatic void selinux_bpf_prog_free(struct bpf_prog_aux *aux) 685362306a36Sopenharmony_ci{ 685462306a36Sopenharmony_ci struct bpf_security_struct *bpfsec = aux->security; 685562306a36Sopenharmony_ci 685662306a36Sopenharmony_ci aux->security = NULL; 685762306a36Sopenharmony_ci kfree(bpfsec); 685862306a36Sopenharmony_ci} 685962306a36Sopenharmony_ci#endif 686062306a36Sopenharmony_ci 686162306a36Sopenharmony_cistruct lsm_blob_sizes selinux_blob_sizes __ro_after_init = { 686262306a36Sopenharmony_ci .lbs_cred = sizeof(struct task_security_struct), 686362306a36Sopenharmony_ci .lbs_file = sizeof(struct file_security_struct), 686462306a36Sopenharmony_ci .lbs_inode = sizeof(struct inode_security_struct), 686562306a36Sopenharmony_ci .lbs_ipc = sizeof(struct ipc_security_struct), 686662306a36Sopenharmony_ci .lbs_msg_msg = sizeof(struct msg_security_struct), 686762306a36Sopenharmony_ci .lbs_superblock = sizeof(struct superblock_security_struct), 686862306a36Sopenharmony_ci .lbs_xattr_count = SELINUX_INODE_INIT_XATTRS, 686962306a36Sopenharmony_ci}; 687062306a36Sopenharmony_ci 687162306a36Sopenharmony_ci#ifdef CONFIG_PERF_EVENTS 687262306a36Sopenharmony_cistatic int selinux_perf_event_open(struct perf_event_attr *attr, int type) 687362306a36Sopenharmony_ci{ 687462306a36Sopenharmony_ci u32 requested, sid = current_sid(); 687562306a36Sopenharmony_ci 687662306a36Sopenharmony_ci if (type == PERF_SECURITY_OPEN) 687762306a36Sopenharmony_ci requested = PERF_EVENT__OPEN; 687862306a36Sopenharmony_ci else if (type == PERF_SECURITY_CPU) 687962306a36Sopenharmony_ci requested = PERF_EVENT__CPU; 688062306a36Sopenharmony_ci else if (type == PERF_SECURITY_KERNEL) 688162306a36Sopenharmony_ci requested = PERF_EVENT__KERNEL; 688262306a36Sopenharmony_ci else if (type == PERF_SECURITY_TRACEPOINT) 688362306a36Sopenharmony_ci requested = PERF_EVENT__TRACEPOINT; 688462306a36Sopenharmony_ci else 688562306a36Sopenharmony_ci return -EINVAL; 688662306a36Sopenharmony_ci 688762306a36Sopenharmony_ci return avc_has_perm(sid, sid, SECCLASS_PERF_EVENT, 688862306a36Sopenharmony_ci requested, NULL); 688962306a36Sopenharmony_ci} 689062306a36Sopenharmony_ci 689162306a36Sopenharmony_cistatic int selinux_perf_event_alloc(struct perf_event *event) 689262306a36Sopenharmony_ci{ 689362306a36Sopenharmony_ci struct perf_event_security_struct *perfsec; 689462306a36Sopenharmony_ci 689562306a36Sopenharmony_ci perfsec = kzalloc(sizeof(*perfsec), GFP_KERNEL); 689662306a36Sopenharmony_ci if (!perfsec) 689762306a36Sopenharmony_ci return -ENOMEM; 689862306a36Sopenharmony_ci 689962306a36Sopenharmony_ci perfsec->sid = current_sid(); 690062306a36Sopenharmony_ci event->security = perfsec; 690162306a36Sopenharmony_ci 690262306a36Sopenharmony_ci return 0; 690362306a36Sopenharmony_ci} 690462306a36Sopenharmony_ci 690562306a36Sopenharmony_cistatic void selinux_perf_event_free(struct perf_event *event) 690662306a36Sopenharmony_ci{ 690762306a36Sopenharmony_ci struct perf_event_security_struct *perfsec = event->security; 690862306a36Sopenharmony_ci 690962306a36Sopenharmony_ci event->security = NULL; 691062306a36Sopenharmony_ci kfree(perfsec); 691162306a36Sopenharmony_ci} 691262306a36Sopenharmony_ci 691362306a36Sopenharmony_cistatic int selinux_perf_event_read(struct perf_event *event) 691462306a36Sopenharmony_ci{ 691562306a36Sopenharmony_ci struct perf_event_security_struct *perfsec = event->security; 691662306a36Sopenharmony_ci u32 sid = current_sid(); 691762306a36Sopenharmony_ci 691862306a36Sopenharmony_ci return avc_has_perm(sid, perfsec->sid, 691962306a36Sopenharmony_ci SECCLASS_PERF_EVENT, PERF_EVENT__READ, NULL); 692062306a36Sopenharmony_ci} 692162306a36Sopenharmony_ci 692262306a36Sopenharmony_cistatic int selinux_perf_event_write(struct perf_event *event) 692362306a36Sopenharmony_ci{ 692462306a36Sopenharmony_ci struct perf_event_security_struct *perfsec = event->security; 692562306a36Sopenharmony_ci u32 sid = current_sid(); 692662306a36Sopenharmony_ci 692762306a36Sopenharmony_ci return avc_has_perm(sid, perfsec->sid, 692862306a36Sopenharmony_ci SECCLASS_PERF_EVENT, PERF_EVENT__WRITE, NULL); 692962306a36Sopenharmony_ci} 693062306a36Sopenharmony_ci#endif 693162306a36Sopenharmony_ci 693262306a36Sopenharmony_ci#ifdef CONFIG_IO_URING 693362306a36Sopenharmony_ci/** 693462306a36Sopenharmony_ci * selinux_uring_override_creds - check the requested cred override 693562306a36Sopenharmony_ci * @new: the target creds 693662306a36Sopenharmony_ci * 693762306a36Sopenharmony_ci * Check to see if the current task is allowed to override it's credentials 693862306a36Sopenharmony_ci * to service an io_uring operation. 693962306a36Sopenharmony_ci */ 694062306a36Sopenharmony_cistatic int selinux_uring_override_creds(const struct cred *new) 694162306a36Sopenharmony_ci{ 694262306a36Sopenharmony_ci return avc_has_perm(current_sid(), cred_sid(new), 694362306a36Sopenharmony_ci SECCLASS_IO_URING, IO_URING__OVERRIDE_CREDS, NULL); 694462306a36Sopenharmony_ci} 694562306a36Sopenharmony_ci 694662306a36Sopenharmony_ci/** 694762306a36Sopenharmony_ci * selinux_uring_sqpoll - check if a io_uring polling thread can be created 694862306a36Sopenharmony_ci * 694962306a36Sopenharmony_ci * Check to see if the current task is allowed to create a new io_uring 695062306a36Sopenharmony_ci * kernel polling thread. 695162306a36Sopenharmony_ci */ 695262306a36Sopenharmony_cistatic int selinux_uring_sqpoll(void) 695362306a36Sopenharmony_ci{ 695462306a36Sopenharmony_ci u32 sid = current_sid(); 695562306a36Sopenharmony_ci 695662306a36Sopenharmony_ci return avc_has_perm(sid, sid, 695762306a36Sopenharmony_ci SECCLASS_IO_URING, IO_URING__SQPOLL, NULL); 695862306a36Sopenharmony_ci} 695962306a36Sopenharmony_ci 696062306a36Sopenharmony_ci/** 696162306a36Sopenharmony_ci * selinux_uring_cmd - check if IORING_OP_URING_CMD is allowed 696262306a36Sopenharmony_ci * @ioucmd: the io_uring command structure 696362306a36Sopenharmony_ci * 696462306a36Sopenharmony_ci * Check to see if the current domain is allowed to execute an 696562306a36Sopenharmony_ci * IORING_OP_URING_CMD against the device/file specified in @ioucmd. 696662306a36Sopenharmony_ci * 696762306a36Sopenharmony_ci */ 696862306a36Sopenharmony_cistatic int selinux_uring_cmd(struct io_uring_cmd *ioucmd) 696962306a36Sopenharmony_ci{ 697062306a36Sopenharmony_ci struct file *file = ioucmd->file; 697162306a36Sopenharmony_ci struct inode *inode = file_inode(file); 697262306a36Sopenharmony_ci struct inode_security_struct *isec = selinux_inode(inode); 697362306a36Sopenharmony_ci struct common_audit_data ad; 697462306a36Sopenharmony_ci 697562306a36Sopenharmony_ci ad.type = LSM_AUDIT_DATA_FILE; 697662306a36Sopenharmony_ci ad.u.file = file; 697762306a36Sopenharmony_ci 697862306a36Sopenharmony_ci return avc_has_perm(current_sid(), isec->sid, 697962306a36Sopenharmony_ci SECCLASS_IO_URING, IO_URING__CMD, &ad); 698062306a36Sopenharmony_ci} 698162306a36Sopenharmony_ci#endif /* CONFIG_IO_URING */ 698262306a36Sopenharmony_ci 698362306a36Sopenharmony_ci/* 698462306a36Sopenharmony_ci * IMPORTANT NOTE: When adding new hooks, please be careful to keep this order: 698562306a36Sopenharmony_ci * 1. any hooks that don't belong to (2.) or (3.) below, 698662306a36Sopenharmony_ci * 2. hooks that both access structures allocated by other hooks, and allocate 698762306a36Sopenharmony_ci * structures that can be later accessed by other hooks (mostly "cloning" 698862306a36Sopenharmony_ci * hooks), 698962306a36Sopenharmony_ci * 3. hooks that only allocate structures that can be later accessed by other 699062306a36Sopenharmony_ci * hooks ("allocating" hooks). 699162306a36Sopenharmony_ci * 699262306a36Sopenharmony_ci * Please follow block comment delimiters in the list to keep this order. 699362306a36Sopenharmony_ci */ 699462306a36Sopenharmony_cistatic struct security_hook_list selinux_hooks[] __ro_after_init = { 699562306a36Sopenharmony_ci LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr), 699662306a36Sopenharmony_ci LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction), 699762306a36Sopenharmony_ci LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder), 699862306a36Sopenharmony_ci LSM_HOOK_INIT(binder_transfer_file, selinux_binder_transfer_file), 699962306a36Sopenharmony_ci 700062306a36Sopenharmony_ci LSM_HOOK_INIT(ptrace_access_check, selinux_ptrace_access_check), 700162306a36Sopenharmony_ci LSM_HOOK_INIT(ptrace_traceme, selinux_ptrace_traceme), 700262306a36Sopenharmony_ci LSM_HOOK_INIT(capget, selinux_capget), 700362306a36Sopenharmony_ci LSM_HOOK_INIT(capset, selinux_capset), 700462306a36Sopenharmony_ci LSM_HOOK_INIT(capable, selinux_capable), 700562306a36Sopenharmony_ci LSM_HOOK_INIT(quotactl, selinux_quotactl), 700662306a36Sopenharmony_ci LSM_HOOK_INIT(quota_on, selinux_quota_on), 700762306a36Sopenharmony_ci LSM_HOOK_INIT(syslog, selinux_syslog), 700862306a36Sopenharmony_ci LSM_HOOK_INIT(vm_enough_memory, selinux_vm_enough_memory), 700962306a36Sopenharmony_ci 701062306a36Sopenharmony_ci LSM_HOOK_INIT(netlink_send, selinux_netlink_send), 701162306a36Sopenharmony_ci 701262306a36Sopenharmony_ci LSM_HOOK_INIT(bprm_creds_for_exec, selinux_bprm_creds_for_exec), 701362306a36Sopenharmony_ci LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds), 701462306a36Sopenharmony_ci LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds), 701562306a36Sopenharmony_ci 701662306a36Sopenharmony_ci LSM_HOOK_INIT(sb_free_mnt_opts, selinux_free_mnt_opts), 701762306a36Sopenharmony_ci LSM_HOOK_INIT(sb_mnt_opts_compat, selinux_sb_mnt_opts_compat), 701862306a36Sopenharmony_ci LSM_HOOK_INIT(sb_remount, selinux_sb_remount), 701962306a36Sopenharmony_ci LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount), 702062306a36Sopenharmony_ci LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options), 702162306a36Sopenharmony_ci LSM_HOOK_INIT(sb_statfs, selinux_sb_statfs), 702262306a36Sopenharmony_ci LSM_HOOK_INIT(sb_mount, selinux_mount), 702362306a36Sopenharmony_ci LSM_HOOK_INIT(sb_umount, selinux_umount), 702462306a36Sopenharmony_ci LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts), 702562306a36Sopenharmony_ci LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts), 702662306a36Sopenharmony_ci 702762306a36Sopenharmony_ci LSM_HOOK_INIT(move_mount, selinux_move_mount), 702862306a36Sopenharmony_ci 702962306a36Sopenharmony_ci LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security), 703062306a36Sopenharmony_ci LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as), 703162306a36Sopenharmony_ci 703262306a36Sopenharmony_ci LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security), 703362306a36Sopenharmony_ci LSM_HOOK_INIT(inode_init_security, selinux_inode_init_security), 703462306a36Sopenharmony_ci LSM_HOOK_INIT(inode_init_security_anon, selinux_inode_init_security_anon), 703562306a36Sopenharmony_ci LSM_HOOK_INIT(inode_create, selinux_inode_create), 703662306a36Sopenharmony_ci LSM_HOOK_INIT(inode_link, selinux_inode_link), 703762306a36Sopenharmony_ci LSM_HOOK_INIT(inode_unlink, selinux_inode_unlink), 703862306a36Sopenharmony_ci LSM_HOOK_INIT(inode_symlink, selinux_inode_symlink), 703962306a36Sopenharmony_ci LSM_HOOK_INIT(inode_mkdir, selinux_inode_mkdir), 704062306a36Sopenharmony_ci LSM_HOOK_INIT(inode_rmdir, selinux_inode_rmdir), 704162306a36Sopenharmony_ci LSM_HOOK_INIT(inode_mknod, selinux_inode_mknod), 704262306a36Sopenharmony_ci LSM_HOOK_INIT(inode_rename, selinux_inode_rename), 704362306a36Sopenharmony_ci LSM_HOOK_INIT(inode_readlink, selinux_inode_readlink), 704462306a36Sopenharmony_ci LSM_HOOK_INIT(inode_follow_link, selinux_inode_follow_link), 704562306a36Sopenharmony_ci LSM_HOOK_INIT(inode_permission, selinux_inode_permission), 704662306a36Sopenharmony_ci LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr), 704762306a36Sopenharmony_ci LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr), 704862306a36Sopenharmony_ci LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr), 704962306a36Sopenharmony_ci LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr), 705062306a36Sopenharmony_ci LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr), 705162306a36Sopenharmony_ci LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr), 705262306a36Sopenharmony_ci LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr), 705362306a36Sopenharmony_ci LSM_HOOK_INIT(inode_set_acl, selinux_inode_set_acl), 705462306a36Sopenharmony_ci LSM_HOOK_INIT(inode_get_acl, selinux_inode_get_acl), 705562306a36Sopenharmony_ci LSM_HOOK_INIT(inode_remove_acl, selinux_inode_remove_acl), 705662306a36Sopenharmony_ci LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity), 705762306a36Sopenharmony_ci LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity), 705862306a36Sopenharmony_ci LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity), 705962306a36Sopenharmony_ci LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), 706062306a36Sopenharmony_ci LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), 706162306a36Sopenharmony_ci LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr), 706262306a36Sopenharmony_ci LSM_HOOK_INIT(path_notify, selinux_path_notify), 706362306a36Sopenharmony_ci 706462306a36Sopenharmony_ci LSM_HOOK_INIT(kernfs_init_security, selinux_kernfs_init_security), 706562306a36Sopenharmony_ci 706662306a36Sopenharmony_ci LSM_HOOK_INIT(file_permission, selinux_file_permission), 706762306a36Sopenharmony_ci LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), 706862306a36Sopenharmony_ci LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), 706962306a36Sopenharmony_ci LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), 707062306a36Sopenharmony_ci LSM_HOOK_INIT(mmap_file, selinux_mmap_file), 707162306a36Sopenharmony_ci LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), 707262306a36Sopenharmony_ci LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect), 707362306a36Sopenharmony_ci LSM_HOOK_INIT(file_lock, selinux_file_lock), 707462306a36Sopenharmony_ci LSM_HOOK_INIT(file_fcntl, selinux_file_fcntl), 707562306a36Sopenharmony_ci LSM_HOOK_INIT(file_set_fowner, selinux_file_set_fowner), 707662306a36Sopenharmony_ci LSM_HOOK_INIT(file_send_sigiotask, selinux_file_send_sigiotask), 707762306a36Sopenharmony_ci LSM_HOOK_INIT(file_receive, selinux_file_receive), 707862306a36Sopenharmony_ci 707962306a36Sopenharmony_ci LSM_HOOK_INIT(file_open, selinux_file_open), 708062306a36Sopenharmony_ci 708162306a36Sopenharmony_ci LSM_HOOK_INIT(task_alloc, selinux_task_alloc), 708262306a36Sopenharmony_ci LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare), 708362306a36Sopenharmony_ci LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer), 708462306a36Sopenharmony_ci LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid), 708562306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as), 708662306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as), 708762306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), 708862306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_load_data, selinux_kernel_load_data), 708962306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file), 709062306a36Sopenharmony_ci LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), 709162306a36Sopenharmony_ci LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), 709262306a36Sopenharmony_ci LSM_HOOK_INIT(task_getsid, selinux_task_getsid), 709362306a36Sopenharmony_ci LSM_HOOK_INIT(current_getsecid_subj, selinux_current_getsecid_subj), 709462306a36Sopenharmony_ci LSM_HOOK_INIT(task_getsecid_obj, selinux_task_getsecid_obj), 709562306a36Sopenharmony_ci LSM_HOOK_INIT(task_setnice, selinux_task_setnice), 709662306a36Sopenharmony_ci LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), 709762306a36Sopenharmony_ci LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio), 709862306a36Sopenharmony_ci LSM_HOOK_INIT(task_prlimit, selinux_task_prlimit), 709962306a36Sopenharmony_ci LSM_HOOK_INIT(task_setrlimit, selinux_task_setrlimit), 710062306a36Sopenharmony_ci LSM_HOOK_INIT(task_setscheduler, selinux_task_setscheduler), 710162306a36Sopenharmony_ci LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), 710262306a36Sopenharmony_ci LSM_HOOK_INIT(task_movememory, selinux_task_movememory), 710362306a36Sopenharmony_ci LSM_HOOK_INIT(task_kill, selinux_task_kill), 710462306a36Sopenharmony_ci LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), 710562306a36Sopenharmony_ci LSM_HOOK_INIT(userns_create, selinux_userns_create), 710662306a36Sopenharmony_ci 710762306a36Sopenharmony_ci LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), 710862306a36Sopenharmony_ci LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid), 710962306a36Sopenharmony_ci 711062306a36Sopenharmony_ci LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate), 711162306a36Sopenharmony_ci LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl), 711262306a36Sopenharmony_ci LSM_HOOK_INIT(msg_queue_msgsnd, selinux_msg_queue_msgsnd), 711362306a36Sopenharmony_ci LSM_HOOK_INIT(msg_queue_msgrcv, selinux_msg_queue_msgrcv), 711462306a36Sopenharmony_ci 711562306a36Sopenharmony_ci LSM_HOOK_INIT(shm_associate, selinux_shm_associate), 711662306a36Sopenharmony_ci LSM_HOOK_INIT(shm_shmctl, selinux_shm_shmctl), 711762306a36Sopenharmony_ci LSM_HOOK_INIT(shm_shmat, selinux_shm_shmat), 711862306a36Sopenharmony_ci 711962306a36Sopenharmony_ci LSM_HOOK_INIT(sem_associate, selinux_sem_associate), 712062306a36Sopenharmony_ci LSM_HOOK_INIT(sem_semctl, selinux_sem_semctl), 712162306a36Sopenharmony_ci LSM_HOOK_INIT(sem_semop, selinux_sem_semop), 712262306a36Sopenharmony_ci 712362306a36Sopenharmony_ci LSM_HOOK_INIT(d_instantiate, selinux_d_instantiate), 712462306a36Sopenharmony_ci 712562306a36Sopenharmony_ci LSM_HOOK_INIT(getprocattr, selinux_getprocattr), 712662306a36Sopenharmony_ci LSM_HOOK_INIT(setprocattr, selinux_setprocattr), 712762306a36Sopenharmony_ci 712862306a36Sopenharmony_ci LSM_HOOK_INIT(ismaclabel, selinux_ismaclabel), 712962306a36Sopenharmony_ci LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid), 713062306a36Sopenharmony_ci LSM_HOOK_INIT(release_secctx, selinux_release_secctx), 713162306a36Sopenharmony_ci LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx), 713262306a36Sopenharmony_ci LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx), 713362306a36Sopenharmony_ci LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx), 713462306a36Sopenharmony_ci 713562306a36Sopenharmony_ci LSM_HOOK_INIT(unix_stream_connect, selinux_socket_unix_stream_connect), 713662306a36Sopenharmony_ci LSM_HOOK_INIT(unix_may_send, selinux_socket_unix_may_send), 713762306a36Sopenharmony_ci 713862306a36Sopenharmony_ci LSM_HOOK_INIT(socket_create, selinux_socket_create), 713962306a36Sopenharmony_ci LSM_HOOK_INIT(socket_post_create, selinux_socket_post_create), 714062306a36Sopenharmony_ci LSM_HOOK_INIT(socket_socketpair, selinux_socket_socketpair), 714162306a36Sopenharmony_ci LSM_HOOK_INIT(socket_bind, selinux_socket_bind), 714262306a36Sopenharmony_ci LSM_HOOK_INIT(socket_connect, selinux_socket_connect), 714362306a36Sopenharmony_ci LSM_HOOK_INIT(socket_listen, selinux_socket_listen), 714462306a36Sopenharmony_ci LSM_HOOK_INIT(socket_accept, selinux_socket_accept), 714562306a36Sopenharmony_ci LSM_HOOK_INIT(socket_sendmsg, selinux_socket_sendmsg), 714662306a36Sopenharmony_ci LSM_HOOK_INIT(socket_recvmsg, selinux_socket_recvmsg), 714762306a36Sopenharmony_ci LSM_HOOK_INIT(socket_getsockname, selinux_socket_getsockname), 714862306a36Sopenharmony_ci LSM_HOOK_INIT(socket_getpeername, selinux_socket_getpeername), 714962306a36Sopenharmony_ci LSM_HOOK_INIT(socket_getsockopt, selinux_socket_getsockopt), 715062306a36Sopenharmony_ci LSM_HOOK_INIT(socket_setsockopt, selinux_socket_setsockopt), 715162306a36Sopenharmony_ci LSM_HOOK_INIT(socket_shutdown, selinux_socket_shutdown), 715262306a36Sopenharmony_ci LSM_HOOK_INIT(socket_sock_rcv_skb, selinux_socket_sock_rcv_skb), 715362306a36Sopenharmony_ci LSM_HOOK_INIT(socket_getpeersec_stream, 715462306a36Sopenharmony_ci selinux_socket_getpeersec_stream), 715562306a36Sopenharmony_ci LSM_HOOK_INIT(socket_getpeersec_dgram, selinux_socket_getpeersec_dgram), 715662306a36Sopenharmony_ci LSM_HOOK_INIT(sk_free_security, selinux_sk_free_security), 715762306a36Sopenharmony_ci LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security), 715862306a36Sopenharmony_ci LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid), 715962306a36Sopenharmony_ci LSM_HOOK_INIT(sock_graft, selinux_sock_graft), 716062306a36Sopenharmony_ci LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request), 716162306a36Sopenharmony_ci LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone), 716262306a36Sopenharmony_ci LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect), 716362306a36Sopenharmony_ci LSM_HOOK_INIT(sctp_assoc_established, selinux_sctp_assoc_established), 716462306a36Sopenharmony_ci LSM_HOOK_INIT(mptcp_add_subflow, selinux_mptcp_add_subflow), 716562306a36Sopenharmony_ci LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request), 716662306a36Sopenharmony_ci LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone), 716762306a36Sopenharmony_ci LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established), 716862306a36Sopenharmony_ci LSM_HOOK_INIT(secmark_relabel_packet, selinux_secmark_relabel_packet), 716962306a36Sopenharmony_ci LSM_HOOK_INIT(secmark_refcount_inc, selinux_secmark_refcount_inc), 717062306a36Sopenharmony_ci LSM_HOOK_INIT(secmark_refcount_dec, selinux_secmark_refcount_dec), 717162306a36Sopenharmony_ci LSM_HOOK_INIT(req_classify_flow, selinux_req_classify_flow), 717262306a36Sopenharmony_ci LSM_HOOK_INIT(tun_dev_free_security, selinux_tun_dev_free_security), 717362306a36Sopenharmony_ci LSM_HOOK_INIT(tun_dev_create, selinux_tun_dev_create), 717462306a36Sopenharmony_ci LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue), 717562306a36Sopenharmony_ci LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach), 717662306a36Sopenharmony_ci LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open), 717762306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_INFINIBAND 717862306a36Sopenharmony_ci LSM_HOOK_INIT(ib_pkey_access, selinux_ib_pkey_access), 717962306a36Sopenharmony_ci LSM_HOOK_INIT(ib_endport_manage_subnet, 718062306a36Sopenharmony_ci selinux_ib_endport_manage_subnet), 718162306a36Sopenharmony_ci LSM_HOOK_INIT(ib_free_security, selinux_ib_free_security), 718262306a36Sopenharmony_ci#endif 718362306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_NETWORK_XFRM 718462306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_policy_free_security, selinux_xfrm_policy_free), 718562306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_policy_delete_security, selinux_xfrm_policy_delete), 718662306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_state_free_security, selinux_xfrm_state_free), 718762306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_state_delete_security, selinux_xfrm_state_delete), 718862306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_policy_lookup, selinux_xfrm_policy_lookup), 718962306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_state_pol_flow_match, 719062306a36Sopenharmony_ci selinux_xfrm_state_pol_flow_match), 719162306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_decode_session, selinux_xfrm_decode_session), 719262306a36Sopenharmony_ci#endif 719362306a36Sopenharmony_ci 719462306a36Sopenharmony_ci#ifdef CONFIG_KEYS 719562306a36Sopenharmony_ci LSM_HOOK_INIT(key_free, selinux_key_free), 719662306a36Sopenharmony_ci LSM_HOOK_INIT(key_permission, selinux_key_permission), 719762306a36Sopenharmony_ci LSM_HOOK_INIT(key_getsecurity, selinux_key_getsecurity), 719862306a36Sopenharmony_ci#ifdef CONFIG_KEY_NOTIFICATIONS 719962306a36Sopenharmony_ci LSM_HOOK_INIT(watch_key, selinux_watch_key), 720062306a36Sopenharmony_ci#endif 720162306a36Sopenharmony_ci#endif 720262306a36Sopenharmony_ci 720362306a36Sopenharmony_ci#ifdef CONFIG_AUDIT 720462306a36Sopenharmony_ci LSM_HOOK_INIT(audit_rule_known, selinux_audit_rule_known), 720562306a36Sopenharmony_ci LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match), 720662306a36Sopenharmony_ci LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free), 720762306a36Sopenharmony_ci#endif 720862306a36Sopenharmony_ci 720962306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 721062306a36Sopenharmony_ci LSM_HOOK_INIT(bpf, selinux_bpf), 721162306a36Sopenharmony_ci LSM_HOOK_INIT(bpf_map, selinux_bpf_map), 721262306a36Sopenharmony_ci LSM_HOOK_INIT(bpf_prog, selinux_bpf_prog), 721362306a36Sopenharmony_ci LSM_HOOK_INIT(bpf_map_free_security, selinux_bpf_map_free), 721462306a36Sopenharmony_ci LSM_HOOK_INIT(bpf_prog_free_security, selinux_bpf_prog_free), 721562306a36Sopenharmony_ci#endif 721662306a36Sopenharmony_ci 721762306a36Sopenharmony_ci#ifdef CONFIG_PERF_EVENTS 721862306a36Sopenharmony_ci LSM_HOOK_INIT(perf_event_open, selinux_perf_event_open), 721962306a36Sopenharmony_ci LSM_HOOK_INIT(perf_event_free, selinux_perf_event_free), 722062306a36Sopenharmony_ci LSM_HOOK_INIT(perf_event_read, selinux_perf_event_read), 722162306a36Sopenharmony_ci LSM_HOOK_INIT(perf_event_write, selinux_perf_event_write), 722262306a36Sopenharmony_ci#endif 722362306a36Sopenharmony_ci 722462306a36Sopenharmony_ci#ifdef CONFIG_IO_URING 722562306a36Sopenharmony_ci LSM_HOOK_INIT(uring_override_creds, selinux_uring_override_creds), 722662306a36Sopenharmony_ci LSM_HOOK_INIT(uring_sqpoll, selinux_uring_sqpoll), 722762306a36Sopenharmony_ci LSM_HOOK_INIT(uring_cmd, selinux_uring_cmd), 722862306a36Sopenharmony_ci#endif 722962306a36Sopenharmony_ci 723062306a36Sopenharmony_ci /* 723162306a36Sopenharmony_ci * PUT "CLONING" (ACCESSING + ALLOCATING) HOOKS HERE 723262306a36Sopenharmony_ci */ 723362306a36Sopenharmony_ci LSM_HOOK_INIT(fs_context_submount, selinux_fs_context_submount), 723462306a36Sopenharmony_ci LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup), 723562306a36Sopenharmony_ci LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param), 723662306a36Sopenharmony_ci LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts), 723762306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_NETWORK_XFRM 723862306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone), 723962306a36Sopenharmony_ci#endif 724062306a36Sopenharmony_ci 724162306a36Sopenharmony_ci /* 724262306a36Sopenharmony_ci * PUT "ALLOCATING" HOOKS HERE 724362306a36Sopenharmony_ci */ 724462306a36Sopenharmony_ci LSM_HOOK_INIT(msg_msg_alloc_security, selinux_msg_msg_alloc_security), 724562306a36Sopenharmony_ci LSM_HOOK_INIT(msg_queue_alloc_security, 724662306a36Sopenharmony_ci selinux_msg_queue_alloc_security), 724762306a36Sopenharmony_ci LSM_HOOK_INIT(shm_alloc_security, selinux_shm_alloc_security), 724862306a36Sopenharmony_ci LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security), 724962306a36Sopenharmony_ci LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security), 725062306a36Sopenharmony_ci LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security), 725162306a36Sopenharmony_ci LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx), 725262306a36Sopenharmony_ci LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx), 725362306a36Sopenharmony_ci LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security), 725462306a36Sopenharmony_ci LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security), 725562306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_INFINIBAND 725662306a36Sopenharmony_ci LSM_HOOK_INIT(ib_alloc_security, selinux_ib_alloc_security), 725762306a36Sopenharmony_ci#endif 725862306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_NETWORK_XFRM 725962306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_policy_alloc_security, selinux_xfrm_policy_alloc), 726062306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_state_alloc, selinux_xfrm_state_alloc), 726162306a36Sopenharmony_ci LSM_HOOK_INIT(xfrm_state_alloc_acquire, 726262306a36Sopenharmony_ci selinux_xfrm_state_alloc_acquire), 726362306a36Sopenharmony_ci#endif 726462306a36Sopenharmony_ci#ifdef CONFIG_KEYS 726562306a36Sopenharmony_ci LSM_HOOK_INIT(key_alloc, selinux_key_alloc), 726662306a36Sopenharmony_ci#endif 726762306a36Sopenharmony_ci#ifdef CONFIG_AUDIT 726862306a36Sopenharmony_ci LSM_HOOK_INIT(audit_rule_init, selinux_audit_rule_init), 726962306a36Sopenharmony_ci#endif 727062306a36Sopenharmony_ci#ifdef CONFIG_BPF_SYSCALL 727162306a36Sopenharmony_ci LSM_HOOK_INIT(bpf_map_alloc_security, selinux_bpf_map_alloc), 727262306a36Sopenharmony_ci LSM_HOOK_INIT(bpf_prog_alloc_security, selinux_bpf_prog_alloc), 727362306a36Sopenharmony_ci#endif 727462306a36Sopenharmony_ci#ifdef CONFIG_PERF_EVENTS 727562306a36Sopenharmony_ci LSM_HOOK_INIT(perf_event_alloc, selinux_perf_event_alloc), 727662306a36Sopenharmony_ci#endif 727762306a36Sopenharmony_ci}; 727862306a36Sopenharmony_ci 727962306a36Sopenharmony_cistatic __init int selinux_init(void) 728062306a36Sopenharmony_ci{ 728162306a36Sopenharmony_ci pr_info("SELinux: Initializing.\n"); 728262306a36Sopenharmony_ci 728362306a36Sopenharmony_ci memset(&selinux_state, 0, sizeof(selinux_state)); 728462306a36Sopenharmony_ci enforcing_set(selinux_enforcing_boot); 728562306a36Sopenharmony_ci selinux_avc_init(); 728662306a36Sopenharmony_ci mutex_init(&selinux_state.status_lock); 728762306a36Sopenharmony_ci mutex_init(&selinux_state.policy_mutex); 728862306a36Sopenharmony_ci 728962306a36Sopenharmony_ci /* Set the security state for the initial task. */ 729062306a36Sopenharmony_ci cred_init_security(); 729162306a36Sopenharmony_ci 729262306a36Sopenharmony_ci default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC); 729362306a36Sopenharmony_ci if (!default_noexec) 729462306a36Sopenharmony_ci pr_notice("SELinux: virtual memory is executable by default\n"); 729562306a36Sopenharmony_ci 729662306a36Sopenharmony_ci avc_init(); 729762306a36Sopenharmony_ci 729862306a36Sopenharmony_ci avtab_cache_init(); 729962306a36Sopenharmony_ci 730062306a36Sopenharmony_ci ebitmap_cache_init(); 730162306a36Sopenharmony_ci 730262306a36Sopenharmony_ci hashtab_cache_init(); 730362306a36Sopenharmony_ci 730462306a36Sopenharmony_ci security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); 730562306a36Sopenharmony_ci 730662306a36Sopenharmony_ci if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) 730762306a36Sopenharmony_ci panic("SELinux: Unable to register AVC netcache callback\n"); 730862306a36Sopenharmony_ci 730962306a36Sopenharmony_ci if (avc_add_callback(selinux_lsm_notifier_avc_callback, AVC_CALLBACK_RESET)) 731062306a36Sopenharmony_ci panic("SELinux: Unable to register AVC LSM notifier callback\n"); 731162306a36Sopenharmony_ci 731262306a36Sopenharmony_ci if (selinux_enforcing_boot) 731362306a36Sopenharmony_ci pr_debug("SELinux: Starting in enforcing mode\n"); 731462306a36Sopenharmony_ci else 731562306a36Sopenharmony_ci pr_debug("SELinux: Starting in permissive mode\n"); 731662306a36Sopenharmony_ci 731762306a36Sopenharmony_ci fs_validate_description("selinux", selinux_fs_parameters); 731862306a36Sopenharmony_ci 731962306a36Sopenharmony_ci return 0; 732062306a36Sopenharmony_ci} 732162306a36Sopenharmony_ci 732262306a36Sopenharmony_cistatic void delayed_superblock_init(struct super_block *sb, void *unused) 732362306a36Sopenharmony_ci{ 732462306a36Sopenharmony_ci selinux_set_mnt_opts(sb, NULL, 0, NULL); 732562306a36Sopenharmony_ci} 732662306a36Sopenharmony_ci 732762306a36Sopenharmony_civoid selinux_complete_init(void) 732862306a36Sopenharmony_ci{ 732962306a36Sopenharmony_ci pr_debug("SELinux: Completing initialization.\n"); 733062306a36Sopenharmony_ci 733162306a36Sopenharmony_ci /* Set up any superblocks initialized prior to the policy load. */ 733262306a36Sopenharmony_ci pr_debug("SELinux: Setting up existing superblocks.\n"); 733362306a36Sopenharmony_ci iterate_supers(delayed_superblock_init, NULL); 733462306a36Sopenharmony_ci} 733562306a36Sopenharmony_ci 733662306a36Sopenharmony_ci/* SELinux requires early initialization in order to label 733762306a36Sopenharmony_ci all processes and objects when they are created. */ 733862306a36Sopenharmony_ciDEFINE_LSM(selinux) = { 733962306a36Sopenharmony_ci .name = "selinux", 734062306a36Sopenharmony_ci .flags = LSM_FLAG_LEGACY_MAJOR | LSM_FLAG_EXCLUSIVE, 734162306a36Sopenharmony_ci .enabled = &selinux_enabled_boot, 734262306a36Sopenharmony_ci .blobs = &selinux_blob_sizes, 734362306a36Sopenharmony_ci .init = selinux_init, 734462306a36Sopenharmony_ci}; 734562306a36Sopenharmony_ci 734662306a36Sopenharmony_ci#if defined(CONFIG_NETFILTER) 734762306a36Sopenharmony_cistatic const struct nf_hook_ops selinux_nf_ops[] = { 734862306a36Sopenharmony_ci { 734962306a36Sopenharmony_ci .hook = selinux_ip_postroute, 735062306a36Sopenharmony_ci .pf = NFPROTO_IPV4, 735162306a36Sopenharmony_ci .hooknum = NF_INET_POST_ROUTING, 735262306a36Sopenharmony_ci .priority = NF_IP_PRI_SELINUX_LAST, 735362306a36Sopenharmony_ci }, 735462306a36Sopenharmony_ci { 735562306a36Sopenharmony_ci .hook = selinux_ip_forward, 735662306a36Sopenharmony_ci .pf = NFPROTO_IPV4, 735762306a36Sopenharmony_ci .hooknum = NF_INET_FORWARD, 735862306a36Sopenharmony_ci .priority = NF_IP_PRI_SELINUX_FIRST, 735962306a36Sopenharmony_ci }, 736062306a36Sopenharmony_ci { 736162306a36Sopenharmony_ci .hook = selinux_ip_output, 736262306a36Sopenharmony_ci .pf = NFPROTO_IPV4, 736362306a36Sopenharmony_ci .hooknum = NF_INET_LOCAL_OUT, 736462306a36Sopenharmony_ci .priority = NF_IP_PRI_SELINUX_FIRST, 736562306a36Sopenharmony_ci }, 736662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6) 736762306a36Sopenharmony_ci { 736862306a36Sopenharmony_ci .hook = selinux_ip_postroute, 736962306a36Sopenharmony_ci .pf = NFPROTO_IPV6, 737062306a36Sopenharmony_ci .hooknum = NF_INET_POST_ROUTING, 737162306a36Sopenharmony_ci .priority = NF_IP6_PRI_SELINUX_LAST, 737262306a36Sopenharmony_ci }, 737362306a36Sopenharmony_ci { 737462306a36Sopenharmony_ci .hook = selinux_ip_forward, 737562306a36Sopenharmony_ci .pf = NFPROTO_IPV6, 737662306a36Sopenharmony_ci .hooknum = NF_INET_FORWARD, 737762306a36Sopenharmony_ci .priority = NF_IP6_PRI_SELINUX_FIRST, 737862306a36Sopenharmony_ci }, 737962306a36Sopenharmony_ci { 738062306a36Sopenharmony_ci .hook = selinux_ip_output, 738162306a36Sopenharmony_ci .pf = NFPROTO_IPV6, 738262306a36Sopenharmony_ci .hooknum = NF_INET_LOCAL_OUT, 738362306a36Sopenharmony_ci .priority = NF_IP6_PRI_SELINUX_FIRST, 738462306a36Sopenharmony_ci }, 738562306a36Sopenharmony_ci#endif /* IPV6 */ 738662306a36Sopenharmony_ci}; 738762306a36Sopenharmony_ci 738862306a36Sopenharmony_cistatic int __net_init selinux_nf_register(struct net *net) 738962306a36Sopenharmony_ci{ 739062306a36Sopenharmony_ci return nf_register_net_hooks(net, selinux_nf_ops, 739162306a36Sopenharmony_ci ARRAY_SIZE(selinux_nf_ops)); 739262306a36Sopenharmony_ci} 739362306a36Sopenharmony_ci 739462306a36Sopenharmony_cistatic void __net_exit selinux_nf_unregister(struct net *net) 739562306a36Sopenharmony_ci{ 739662306a36Sopenharmony_ci nf_unregister_net_hooks(net, selinux_nf_ops, 739762306a36Sopenharmony_ci ARRAY_SIZE(selinux_nf_ops)); 739862306a36Sopenharmony_ci} 739962306a36Sopenharmony_ci 740062306a36Sopenharmony_cistatic struct pernet_operations selinux_net_ops = { 740162306a36Sopenharmony_ci .init = selinux_nf_register, 740262306a36Sopenharmony_ci .exit = selinux_nf_unregister, 740362306a36Sopenharmony_ci}; 740462306a36Sopenharmony_ci 740562306a36Sopenharmony_cistatic int __init selinux_nf_ip_init(void) 740662306a36Sopenharmony_ci{ 740762306a36Sopenharmony_ci int err; 740862306a36Sopenharmony_ci 740962306a36Sopenharmony_ci if (!selinux_enabled_boot) 741062306a36Sopenharmony_ci return 0; 741162306a36Sopenharmony_ci 741262306a36Sopenharmony_ci pr_debug("SELinux: Registering netfilter hooks\n"); 741362306a36Sopenharmony_ci 741462306a36Sopenharmony_ci err = register_pernet_subsys(&selinux_net_ops); 741562306a36Sopenharmony_ci if (err) 741662306a36Sopenharmony_ci panic("SELinux: register_pernet_subsys: error %d\n", err); 741762306a36Sopenharmony_ci 741862306a36Sopenharmony_ci return 0; 741962306a36Sopenharmony_ci} 742062306a36Sopenharmony_ci__initcall(selinux_nf_ip_init); 742162306a36Sopenharmony_ci#endif /* CONFIG_NETFILTER */ 7422