#include #include #include #include #include #include #include #include #include #include "lve_internal.h" #include "lve_debug.h" #include "resource.h" #include "lve_global_params.h" extern struct net init_net; extern kgid_t proc_super_kgid; struct _proc_white_list { const char *name; const int len; }; #define PWL_INIT(a) {.name = a, .len = sizeof(a) - 1} static struct _proc_white_list root_list[] = { PWL_INIT("version"), PWL_INIT("stat"), PWL_INIT("uptime"), PWL_INIT("loadavg"), PWL_INIT("filesystems"), PWL_INIT("stat"), PWL_INIT("cmdline"), PWL_INIT("meminfo"), PWL_INIT("mounts"), PWL_INIT("cpuinfo"), PWL_INIT("net"), }; static struct _proc_white_list net_list[] = { PWL_INIT("tcp"), PWL_INIT("tcp6"), PWL_INIT("udp"), PWL_INIT("udp6"), PWL_INIT("assocs"), PWL_INIT("raw"), PWL_INIT("raw6"), PWL_INIT("unix"), PWL_INIT("dev"), }; static struct _proc_white_list lve_list[] = { PWL_INIT("task_sched_stat"), }; static bool check_list(struct dentry *de, struct _proc_white_list *list, int listsz) { int i; /* XXX need profile */ for (i=0; i< listsz; i++) { if (de->d_name.len != list[i].len) continue; if (!strncmp(de->d_name.name, list[i].name, list[i].len)) { return true; } } return false; } #define root_check_list(de) check_list(de, root_list, ARRAY_SIZE(root_list)) #define net_check_list(de) check_list(de, net_list, ARRAY_SIZE(net_list)) #define lve_check_list(de) check_list(de, lve_list, ARRAY_SIZE(lve_list)) int sandbox_permission(struct inode *inode, int mask) { struct dentry *de; int rc = 0; /* no security check for a priveledged user */ if (in_group_p(proc_super_kgid)) return 0; if (inode->i_sb->s_magic != PROC_SUPER_MAGIC) return 0; if (mask & MAY_NOT_BLOCK) return -ECHILD; de = d_find_alias(inode); if (!de) return 0; /* disabling ftrace_enabled turns kprobes off, it affects kmodlve */ /* so we don't want to have such possibility */ if ((mask & MAY_WRITE) && strncmp(de->d_name.name, "ftrace_enabled", de->d_name.len) == 0) { rc = -EPERM; goto out; } if (capable(CAP_SYS_RESOURCE)) { rc = 0; goto out; } #ifdef FEAT_PROC_PROT if ((strncmp(de->d_parent->d_name.name, "lve", sizeof("lve")) == 0) && S_ISDIR(de->d_parent->d_inode->i_mode)) { if (!lve_check_list(de)) { rc = -EPERM; goto out; } } if (de->d_parent->d_inode->i_ino == PROC_ROOT_INO && S_ISREG(de->d_inode->i_mode) && de->d_inode->i_ino != PROC_ROOT_INO) { if (!root_check_list(de)) { rc = -EPERM; goto out; } } if (!capable(CAP_NET_ADMIN) && !strncmp(de->d_parent->d_name.name, "net", sizeof("net")) && isdigit(de->d_parent->d_parent->d_name.name[0]) && de->d_parent->d_parent->d_parent->d_inode->i_ino == PROC_ROOT_INO) { /* * Disable access to /proc/net/{tcp,udp,unix} if * kernel.proc_disable_net = 1 as per * https://docs.cloudlinux.com/shared/cloudlinux_os_kernel/ */ if (net_check_list(de)) { if (param_is_enabled(LVE_PROC_DISABLE_NET)) { rc = -EPERM; goto out; } /* * Disable access to other /proc/net/ files otherwise */ } else { rc = -EPERM; goto out; } } #endif out: dput(de); return rc; } int sandbox_inode_rmdir(struct inode *dir, struct dentry *de) { /* only allow LVE cgroups removal made by the init thread */ if (current != lve_init_task && dir && de && dir->i_sb->s_magic == CGROUP_SUPER_MAGIC && (!strncmp(de->d_name.name, "lvp", sizeof("lvp") - 1) || !strncmp(de->d_name.name, "lve", sizeof("lve") - 1) || !strncmp(de->d_name.name, "rmv", sizeof("rmv") - 1))) return -ENOTEMPTY; return 0; } void sandbox_d_instantiate(struct dentry *de, struct inode *inode) { if (!lve_io_stat_available) return; if (!inode || inode->i_sb->s_magic != PROC_SUPER_MAGIC) return; if (!strncmp(de->d_name.name, "io", sizeof("io") - 1)) inode->i_mode |= S_IRUGO; }