summaryrefslogtreecommitdiffstats
path: root/kernel/security
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/security')
-rw-r--r--kernel/security/Kconfig5
-rw-r--r--kernel/security/Makefile2
-rw-r--r--kernel/security/apparmor/Kconfig2
-rw-r--r--kernel/security/apparmor/domain.c12
-rw-r--r--kernel/security/apparmor/lsm.c137
-rw-r--r--kernel/security/capability.c1158
-rw-r--r--kernel/security/commoncap.c151
-rw-r--r--kernel/security/device_cgroup.c6
-rw-r--r--kernel/security/inode.c19
-rw-r--r--kernel/security/integrity/digsig.c4
-rw-r--r--kernel/security/integrity/evm/evm_crypto.c2
-rw-r--r--kernel/security/integrity/evm/evm_main.c10
-rw-r--r--kernel/security/integrity/iint.c3
-rw-r--r--kernel/security/integrity/ima/ima.h27
-rw-r--r--kernel/security/integrity/ima/ima_api.c20
-rw-r--r--kernel/security/integrity/ima/ima_appraise.c8
-rw-r--r--kernel/security/integrity/ima/ima_crypto.c4
-rw-r--r--kernel/security/integrity/ima/ima_init.c13
-rw-r--r--kernel/security/integrity/ima/ima_main.c5
-rw-r--r--kernel/security/integrity/ima/ima_template_lib.c71
-rw-r--r--kernel/security/integrity/ima/ima_template_lib.h22
-rw-r--r--kernel/security/integrity/integrity.h2
-rw-r--r--kernel/security/keys/big_key.c47
-rw-r--r--kernel/security/keys/encrypted-keys/encrypted.c20
-rw-r--r--kernel/security/keys/encrypted-keys/encrypted.h4
-rw-r--r--kernel/security/keys/encrypted-keys/masterkey_trusted.c4
-rw-r--r--kernel/security/keys/gc.c10
-rw-r--r--kernel/security/keys/key.c20
-rw-r--r--kernel/security/keys/keyctl.c30
-rw-r--r--kernel/security/keys/keyring.c12
-rw-r--r--kernel/security/keys/process_keys.c6
-rw-r--r--kernel/security/keys/request_key.c7
-rw-r--r--kernel/security/keys/request_key_auth.c12
-rw-r--r--kernel/security/keys/trusted.c45
-rw-r--r--kernel/security/keys/trusted.h11
-rw-r--r--kernel/security/keys/user_defined.c17
-rw-r--r--kernel/security/lsm_audit.c17
-rw-r--r--kernel/security/security.c960
-rw-r--r--kernel/security/selinux/Kconfig4
-rw-r--r--kernel/security/selinux/avc.c436
-rw-r--r--kernel/security/selinux/hooks.c785
-rw-r--r--kernel/security/selinux/include/avc.h15
-rw-r--r--kernel/security/selinux/include/classmap.h44
-rw-r--r--kernel/security/selinux/include/security.h35
-rw-r--r--kernel/security/selinux/netlabel.c2
-rw-r--r--kernel/security/selinux/selinuxfs.c28
-rw-r--r--kernel/security/selinux/ss/avtab.c104
-rw-r--r--kernel/security/selinux/ss/avtab.h33
-rw-r--r--kernel/security/selinux/ss/conditional.c30
-rw-r--r--kernel/security/selinux/ss/conditional.h6
-rw-r--r--kernel/security/selinux/ss/policydb.c5
-rw-r--r--kernel/security/selinux/ss/services.c235
-rw-r--r--kernel/security/selinux/ss/services.h6
-rw-r--r--kernel/security/selinux/xfrm.c3
-rw-r--r--kernel/security/smack/smack.h95
-rw-r--r--kernel/security/smack/smack_access.c74
-rw-r--r--kernel/security/smack/smack_lsm.c965
-rw-r--r--kernel/security/smack/smack_netfilter.c17
-rw-r--r--kernel/security/smack/smackfs.c893
-rw-r--r--kernel/security/tomoyo/tomoyo.c72
-rw-r--r--kernel/security/yama/Kconfig9
-rw-r--r--kernel/security/yama/yama_lsm.c76
62 files changed, 3917 insertions, 2960 deletions
diff --git a/kernel/security/Kconfig b/kernel/security/Kconfig
index bf4ec4647..e45237897 100644
--- a/kernel/security/Kconfig
+++ b/kernel/security/Kconfig
@@ -132,7 +132,6 @@ choice
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
- default DEFAULT_SECURITY_YAMA if SECURITY_YAMA
default DEFAULT_SECURITY_DAC
help
@@ -151,9 +150,6 @@ choice
config DEFAULT_SECURITY_APPARMOR
bool "AppArmor" if SECURITY_APPARMOR=y
- config DEFAULT_SECURITY_YAMA
- bool "Yama" if SECURITY_YAMA=y
-
config DEFAULT_SECURITY_DAC
bool "Unix Discretionary Access Controls"
@@ -165,7 +161,6 @@ config DEFAULT_SECURITY
default "smack" if DEFAULT_SECURITY_SMACK
default "tomoyo" if DEFAULT_SECURITY_TOMOYO
default "apparmor" if DEFAULT_SECURITY_APPARMOR
- default "yama" if DEFAULT_SECURITY_YAMA
default "" if DEFAULT_SECURITY_DAC
endmenu
diff --git a/kernel/security/Makefile b/kernel/security/Makefile
index 05f1c934d..c9bfbc84f 100644
--- a/kernel/security/Makefile
+++ b/kernel/security/Makefile
@@ -14,7 +14,7 @@ obj-y += commoncap.o
obj-$(CONFIG_MMU) += min_addr.o
# Object file lists
-obj-$(CONFIG_SECURITY) += security.o capability.o
+obj-$(CONFIG_SECURITY) += security.o
obj-$(CONFIG_SECURITYFS) += inode.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/
obj-$(CONFIG_SECURITY_SMACK) += smack/
diff --git a/kernel/security/apparmor/Kconfig b/kernel/security/apparmor/Kconfig
index d49c53960..232469baa 100644
--- a/kernel/security/apparmor/Kconfig
+++ b/kernel/security/apparmor/Kconfig
@@ -33,7 +33,7 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE
config SECURITY_APPARMOR_HASH
bool "SHA1 hash of loaded profiles"
depends on SECURITY_APPARMOR
- depends on CRYPTO
+ select CRYPTO
select CRYPTO_SHA1
default y
diff --git a/kernel/security/apparmor/domain.c b/kernel/security/apparmor/domain.c
index d97cba3e3..dc0027b28 100644
--- a/kernel/security/apparmor/domain.c
+++ b/kernel/security/apparmor/domain.c
@@ -347,9 +347,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
file_inode(bprm->file)->i_mode
};
const char *name = NULL, *target = NULL, *info = NULL;
- int error = cap_bprm_set_creds(bprm);
- if (error)
- return error;
+ int error = 0;
if (bprm->cred_prepared)
return 0;
@@ -531,15 +529,13 @@ cleanup:
*/
int apparmor_bprm_secureexec(struct linux_binprm *bprm)
{
- int ret = cap_bprm_secureexec(bprm);
-
/* the decision to use secure exec is computed in set_creds
* and stored in bprm->unsafe.
*/
- if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED))
- ret = 1;
+ if (bprm->unsafe & AA_SECURE_X_NEEDED)
+ return 1;
- return ret;
+ return 0;
}
/**
diff --git a/kernel/security/apparmor/lsm.c b/kernel/security/apparmor/lsm.c
index e5f156143..dec607c17 100644
--- a/kernel/security/apparmor/lsm.c
+++ b/kernel/security/apparmor/lsm.c
@@ -12,7 +12,7 @@
* License.
*/
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/moduleparam.h>
#include <linux/mm.h>
#include <linux/mman.h>
@@ -96,19 +96,11 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
static int apparmor_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
- int error = cap_ptrace_access_check(child, mode);
- if (error)
- return error;
-
return aa_ptrace(current, child, mode);
}
static int apparmor_ptrace_traceme(struct task_struct *parent)
{
- int error = cap_ptrace_traceme(parent);
- if (error)
- return error;
-
return aa_ptrace(parent, current, PTRACE_MODE_ATTACH);
}
@@ -123,10 +115,10 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
cred = __task_cred(target);
profile = aa_cred_profile(cred);
- *effective = cred->cap_effective;
- *inheritable = cred->cap_inheritable;
- *permitted = cred->cap_permitted;
-
+ /*
+ * cap_capget is stacked ahead of this and will
+ * initialize effective and permitted.
+ */
if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
*effective = cap_intersect(*effective, profile->caps.allow);
*permitted = cap_intersect(*permitted, profile->caps.allow);
@@ -140,13 +132,11 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
struct aa_profile *profile;
- /* cap_capable returns 0 on success, else -EPERM */
- int error = cap_capable(cred, ns, cap, audit);
- if (!error) {
- profile = aa_cred_profile(cred);
- if (!unconfined(profile))
- error = aa_capable(profile, cap, audit);
- }
+ int error = 0;
+
+ profile = aa_cred_profile(cred);
+ if (!unconfined(profile))
+ error = aa_capable(profile, cap, audit);
return error;
}
@@ -615,49 +605,46 @@ static int apparmor_task_setrlimit(struct task_struct *task,
return error;
}
-static struct security_operations apparmor_ops = {
- .name = "apparmor",
-
- .ptrace_access_check = apparmor_ptrace_access_check,
- .ptrace_traceme = apparmor_ptrace_traceme,
- .capget = apparmor_capget,
- .capable = apparmor_capable,
-
- .path_link = apparmor_path_link,
- .path_unlink = apparmor_path_unlink,
- .path_symlink = apparmor_path_symlink,
- .path_mkdir = apparmor_path_mkdir,
- .path_rmdir = apparmor_path_rmdir,
- .path_mknod = apparmor_path_mknod,
- .path_rename = apparmor_path_rename,
- .path_chmod = apparmor_path_chmod,
- .path_chown = apparmor_path_chown,
- .path_truncate = apparmor_path_truncate,
- .inode_getattr = apparmor_inode_getattr,
-
- .file_open = apparmor_file_open,
- .file_permission = apparmor_file_permission,
- .file_alloc_security = apparmor_file_alloc_security,
- .file_free_security = apparmor_file_free_security,
- .mmap_file = apparmor_mmap_file,
- .mmap_addr = cap_mmap_addr,
- .file_mprotect = apparmor_file_mprotect,
- .file_lock = apparmor_file_lock,
-
- .getprocattr = apparmor_getprocattr,
- .setprocattr = apparmor_setprocattr,
-
- .cred_alloc_blank = apparmor_cred_alloc_blank,
- .cred_free = apparmor_cred_free,
- .cred_prepare = apparmor_cred_prepare,
- .cred_transfer = apparmor_cred_transfer,
-
- .bprm_set_creds = apparmor_bprm_set_creds,
- .bprm_committing_creds = apparmor_bprm_committing_creds,
- .bprm_committed_creds = apparmor_bprm_committed_creds,
- .bprm_secureexec = apparmor_bprm_secureexec,
-
- .task_setrlimit = apparmor_task_setrlimit,
+static struct security_hook_list apparmor_hooks[] = {
+ LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
+ LSM_HOOK_INIT(capget, apparmor_capget),
+ LSM_HOOK_INIT(capable, apparmor_capable),
+
+ LSM_HOOK_INIT(path_link, apparmor_path_link),
+ LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
+ LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
+ LSM_HOOK_INIT(path_mkdir, apparmor_path_mkdir),
+ LSM_HOOK_INIT(path_rmdir, apparmor_path_rmdir),
+ LSM_HOOK_INIT(path_mknod, apparmor_path_mknod),
+ LSM_HOOK_INIT(path_rename, apparmor_path_rename),
+ LSM_HOOK_INIT(path_chmod, apparmor_path_chmod),
+ LSM_HOOK_INIT(path_chown, apparmor_path_chown),
+ LSM_HOOK_INIT(path_truncate, apparmor_path_truncate),
+ LSM_HOOK_INIT(inode_getattr, apparmor_inode_getattr),
+
+ LSM_HOOK_INIT(file_open, apparmor_file_open),
+ LSM_HOOK_INIT(file_permission, apparmor_file_permission),
+ LSM_HOOK_INIT(file_alloc_security, apparmor_file_alloc_security),
+ LSM_HOOK_INIT(file_free_security, apparmor_file_free_security),
+ LSM_HOOK_INIT(mmap_file, apparmor_mmap_file),
+ LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect),
+ LSM_HOOK_INIT(file_lock, apparmor_file_lock),
+
+ LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
+ LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
+
+ LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank),
+ LSM_HOOK_INIT(cred_free, apparmor_cred_free),
+ LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
+ LSM_HOOK_INIT(cred_transfer, apparmor_cred_transfer),
+
+ LSM_HOOK_INIT(bprm_set_creds, apparmor_bprm_set_creds),
+ LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds),
+ LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),
+ LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
+
+ LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
};
/*
@@ -667,7 +654,7 @@ static struct security_operations apparmor_ops = {
static int param_set_aabool(const char *val, const struct kernel_param *kp);
static int param_get_aabool(char *buffer, const struct kernel_param *kp);
#define param_check_aabool param_check_bool
-static struct kernel_param_ops param_ops_aabool = {
+static const struct kernel_param_ops param_ops_aabool = {
.flags = KERNEL_PARAM_OPS_FL_NOARG,
.set = param_set_aabool,
.get = param_get_aabool
@@ -676,7 +663,7 @@ static struct kernel_param_ops param_ops_aabool = {
static int param_set_aauint(const char *val, const struct kernel_param *kp);
static int param_get_aauint(char *buffer, const struct kernel_param *kp);
#define param_check_aauint param_check_uint
-static struct kernel_param_ops param_ops_aauint = {
+static const struct kernel_param_ops param_ops_aauint = {
.set = param_set_aauint,
.get = param_get_aauint
};
@@ -684,7 +671,7 @@ static struct kernel_param_ops param_ops_aauint = {
static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp);
static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp);
#define param_check_aalockpolicy param_check_bool
-static struct kernel_param_ops param_ops_aalockpolicy = {
+static const struct kernel_param_ops param_ops_aalockpolicy = {
.flags = KERNEL_PARAM_OPS_FL_NOARG,
.set = param_set_aalockpolicy,
.get = param_get_aalockpolicy
@@ -898,7 +885,7 @@ static int __init apparmor_init(void)
{
int error;
- if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) {
+ if (!apparmor_enabled || !security_module_enable("apparmor")) {
aa_info_message("AppArmor disabled by boot time parameter");
apparmor_enabled = 0;
return 0;
@@ -913,17 +900,10 @@ static int __init apparmor_init(void)
error = set_init_cxt();
if (error) {
AA_ERROR("Failed to set context on init task\n");
- goto register_security_out;
- }
-
- error = register_security(&apparmor_ops);
- if (error) {
- struct cred *cred = (struct cred *)current->real_cred;
- aa_free_task_context(cred_cxt(cred));
- cred_cxt(cred) = NULL;
- AA_ERROR("Unable to register AppArmor\n");
- goto register_security_out;
+ aa_free_root_ns();
+ goto alloc_out;
}
+ security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));
/* Report that AppArmor successfully initialized */
apparmor_initialized = 1;
@@ -936,9 +916,6 @@ static int __init apparmor_init(void)
return error;
-register_security_out:
- aa_free_root_ns();
-
alloc_out:
aa_destroy_aafs();
diff --git a/kernel/security/capability.c b/kernel/security/capability.c
deleted file mode 100644
index 0d03fcc48..000000000
--- a/kernel/security/capability.c
+++ /dev/null
@@ -1,1158 +0,0 @@
-/*
- * Capabilities Linux Security Module
- *
- * This is the default security module in case no other module is loaded.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- */
-
-#include <linux/security.h>
-
-static int cap_binder_set_context_mgr(struct task_struct *mgr)
-{
- return 0;
-}
-
-static int cap_binder_transaction(struct task_struct *from,
- struct task_struct *to)
-{
- return 0;
-}
-
-static int cap_binder_transfer_binder(struct task_struct *from,
- struct task_struct *to)
-{
- return 0;
-}
-
-static int cap_binder_transfer_file(struct task_struct *from,
- struct task_struct *to, struct file *file)
-{
- return 0;
-}
-
-static int cap_syslog(int type)
-{
- return 0;
-}
-
-static int cap_quotactl(int cmds, int type, int id, struct super_block *sb)
-{
- return 0;
-}
-
-static int cap_quota_on(struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_bprm_check_security(struct linux_binprm *bprm)
-{
- return 0;
-}
-
-static void cap_bprm_committing_creds(struct linux_binprm *bprm)
-{
-}
-
-static void cap_bprm_committed_creds(struct linux_binprm *bprm)
-{
-}
-
-static int cap_sb_alloc_security(struct super_block *sb)
-{
- return 0;
-}
-
-static void cap_sb_free_security(struct super_block *sb)
-{
-}
-
-static int cap_sb_copy_data(char *orig, char *copy)
-{
- return 0;
-}
-
-static int cap_sb_remount(struct super_block *sb, void *data)
-{
- return 0;
-}
-
-static int cap_sb_kern_mount(struct super_block *sb, int flags, void *data)
-{
- return 0;
-}
-
-static int cap_sb_show_options(struct seq_file *m, struct super_block *sb)
-{
- return 0;
-}
-
-static int cap_sb_statfs(struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_sb_mount(const char *dev_name, struct path *path,
- const char *type, unsigned long flags, void *data)
-{
- return 0;
-}
-
-static int cap_sb_umount(struct vfsmount *mnt, int flags)
-{
- return 0;
-}
-
-static int cap_sb_pivotroot(struct path *old_path, struct path *new_path)
-{
- return 0;
-}
-
-static int cap_sb_set_mnt_opts(struct super_block *sb,
- struct security_mnt_opts *opts,
- unsigned long kern_flags,
- unsigned long *set_kern_flags)
-
-{
- if (unlikely(opts->num_mnt_opts))
- return -EOPNOTSUPP;
- return 0;
-}
-
-static int cap_sb_clone_mnt_opts(const struct super_block *oldsb,
- struct super_block *newsb)
-{
- return 0;
-}
-
-static int cap_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
-{
- return 0;
-}
-
-static int cap_dentry_init_security(struct dentry *dentry, int mode,
- struct qstr *name, void **ctx,
- u32 *ctxlen)
-{
- return -EOPNOTSUPP;
-}
-
-static int cap_inode_alloc_security(struct inode *inode)
-{
- return 0;
-}
-
-static void cap_inode_free_security(struct inode *inode)
-{
-}
-
-static int cap_inode_init_security(struct inode *inode, struct inode *dir,
- const struct qstr *qstr, const char **name,
- void **value, size_t *len)
-{
- return -EOPNOTSUPP;
-}
-
-static int cap_inode_create(struct inode *inode, struct dentry *dentry,
- umode_t mask)
-{
- return 0;
-}
-
-static int cap_inode_link(struct dentry *old_dentry, struct inode *inode,
- struct dentry *new_dentry)
-{
- return 0;
-}
-
-static int cap_inode_unlink(struct inode *inode, struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_inode_symlink(struct inode *inode, struct dentry *dentry,
- const char *name)
-{
- return 0;
-}
-
-static int cap_inode_mkdir(struct inode *inode, struct dentry *dentry,
- umode_t mask)
-{
- return 0;
-}
-
-static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_inode_mknod(struct inode *inode, struct dentry *dentry,
- umode_t mode, dev_t dev)
-{
- return 0;
-}
-
-static int cap_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
-{
- return 0;
-}
-
-static int cap_inode_readlink(struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_inode_follow_link(struct dentry *dentry,
- struct nameidata *nameidata)
-{
- return 0;
-}
-
-static int cap_inode_permission(struct inode *inode, int mask)
-{
- return 0;
-}
-
-static int cap_inode_setattr(struct dentry *dentry, struct iattr *iattr)
-{
- return 0;
-}
-
-static int cap_inode_getattr(const struct path *path)
-{
- return 0;
-}
-
-static void cap_inode_post_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
-{
-}
-
-static int cap_inode_getxattr(struct dentry *dentry, const char *name)
-{
- return 0;
-}
-
-static int cap_inode_listxattr(struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_inode_getsecurity(const struct inode *inode, const char *name,
- void **buffer, bool alloc)
-{
- return -EOPNOTSUPP;
-}
-
-static int cap_inode_setsecurity(struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
-{
- return -EOPNOTSUPP;
-}
-
-static int cap_inode_listsecurity(struct inode *inode, char *buffer,
- size_t buffer_size)
-{
- return 0;
-}
-
-static void cap_inode_getsecid(const struct inode *inode, u32 *secid)
-{
- *secid = 0;
-}
-
-#ifdef CONFIG_SECURITY_PATH
-static int cap_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode,
- unsigned int dev)
-{
- return 0;
-}
-
-static int cap_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode)
-{
- return 0;
-}
-
-static int cap_path_rmdir(struct path *dir, struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_path_unlink(struct path *dir, struct dentry *dentry)
-{
- return 0;
-}
-
-static int cap_path_symlink(struct path *dir, struct dentry *dentry,
- const char *old_name)
-{
- return 0;
-}
-
-static int cap_path_link(struct dentry *old_dentry, struct path *new_dir,
- struct dentry *new_dentry)
-{
- return 0;
-}
-
-static int cap_path_rename(struct path *old_path, struct dentry *old_dentry,
- struct path *new_path, struct dentry *new_dentry)
-{
- return 0;
-}
-
-static int cap_path_truncate(struct path *path)
-{
- return 0;
-}
-
-static int cap_path_chmod(struct path *path, umode_t mode)
-{
- return 0;
-}
-
-static int cap_path_chown(struct path *path, kuid_t uid, kgid_t gid)
-{
- return 0;
-}
-
-static int cap_path_chroot(struct path *root)
-{
- return 0;
-}
-#endif
-
-static int cap_file_permission(struct file *file, int mask)
-{
- return 0;
-}
-
-static int cap_file_alloc_security(struct file *file)
-{
- return 0;
-}
-
-static void cap_file_free_security(struct file *file)
-{
-}
-
-static int cap_file_ioctl(struct file *file, unsigned int command,
- unsigned long arg)
-{
- return 0;
-}
-
-static int cap_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
- unsigned long prot)
-{
- return 0;
-}
-
-static int cap_file_lock(struct file *file, unsigned int cmd)
-{
- return 0;
-}
-
-static int cap_file_fcntl(struct file *file, unsigned int cmd,
- unsigned long arg)
-{
- return 0;
-}
-
-static void cap_file_set_fowner(struct file *file)
-{
- return;
-}
-
-static int cap_file_send_sigiotask(struct task_struct *tsk,
- struct fown_struct *fown, int sig)
-{
- return 0;
-}
-
-static int cap_file_receive(struct file *file)
-{
- return 0;
-}
-
-static int cap_file_open(struct file *file, const struct cred *cred)
-{
- return 0;
-}
-
-static int cap_task_create(unsigned long clone_flags)
-{
- return 0;
-}
-
-static void cap_task_free(struct task_struct *task)
-{
-}
-
-static int cap_cred_alloc_blank(struct cred *cred, gfp_t gfp)
-{
- return 0;
-}
-
-static void cap_cred_free(struct cred *cred)
-{
-}
-
-static int cap_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
-{
- return 0;
-}
-
-static void cap_cred_transfer(struct cred *new, const struct cred *old)
-{
-}
-
-static int cap_kernel_act_as(struct cred *new, u32 secid)
-{
- return 0;
-}
-
-static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
-{
- return 0;
-}
-
-static int cap_kernel_fw_from_file(struct file *file, char *buf, size_t size)
-{
- return 0;
-}
-
-static int cap_kernel_module_request(char *kmod_name)
-{
- return 0;
-}
-
-static int cap_kernel_module_from_file(struct file *file)
-{
- return 0;
-}
-
-static int cap_task_setpgid(struct task_struct *p, pid_t pgid)
-{
- return 0;
-}
-
-static int cap_task_getpgid(struct task_struct *p)
-{
- return 0;
-}
-
-static int cap_task_getsid(struct task_struct *p)
-{
- return 0;
-}
-
-static void cap_task_getsecid(struct task_struct *p, u32 *secid)
-{
- *secid = 0;
-}
-
-static int cap_task_getioprio(struct task_struct *p)
-{
- return 0;
-}
-
-static int cap_task_setrlimit(struct task_struct *p, unsigned int resource,
- struct rlimit *new_rlim)
-{
- return 0;
-}
-
-static int cap_task_getscheduler(struct task_struct *p)
-{
- return 0;
-}
-
-static int cap_task_movememory(struct task_struct *p)
-{
- return 0;
-}
-
-static int cap_task_wait(struct task_struct *p)
-{
- return 0;
-}
-
-static int cap_task_kill(struct task_struct *p, struct siginfo *info,
- int sig, u32 secid)
-{
- return 0;
-}
-
-static void cap_task_to_inode(struct task_struct *p, struct inode *inode)
-{
-}
-
-static int cap_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
-{
- return 0;
-}
-
-static void cap_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
-{
- *secid = 0;
-}
-
-static int cap_msg_msg_alloc_security(struct msg_msg *msg)
-{
- return 0;
-}
-
-static void cap_msg_msg_free_security(struct msg_msg *msg)
-{
-}
-
-static int cap_msg_queue_alloc_security(struct msg_queue *msq)
-{
- return 0;
-}
-
-static void cap_msg_queue_free_security(struct msg_queue *msq)
-{
-}
-
-static int cap_msg_queue_associate(struct msg_queue *msq, int msqflg)
-{
- return 0;
-}
-
-static int cap_msg_queue_msgctl(struct msg_queue *msq, int cmd)
-{
- return 0;
-}
-
-static int cap_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
- int msgflg)
-{
- return 0;
-}
-
-static int cap_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
- struct task_struct *target, long type, int mode)
-{
- return 0;
-}
-
-static int cap_shm_alloc_security(struct shmid_kernel *shp)
-{
- return 0;
-}
-
-static void cap_shm_free_security(struct shmid_kernel *shp)
-{
-}
-
-static int cap_shm_associate(struct shmid_kernel *shp, int shmflg)
-{
- return 0;
-}
-
-static int cap_shm_shmctl(struct shmid_kernel *shp, int cmd)
-{
- return 0;
-}
-
-static int cap_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr,
- int shmflg)
-{
- return 0;
-}
-
-static int cap_sem_alloc_security(struct sem_array *sma)
-{
- return 0;
-}
-
-static void cap_sem_free_security(struct sem_array *sma)
-{
-}
-
-static int cap_sem_associate(struct sem_array *sma, int semflg)
-{
- return 0;
-}
-
-static int cap_sem_semctl(struct sem_array *sma, int cmd)
-{
- return 0;
-}
-
-static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops,
- unsigned nsops, int alter)
-{
- return 0;
-}
-
-#ifdef CONFIG_SECURITY_NETWORK
-static int cap_unix_stream_connect(struct sock *sock, struct sock *other,
- struct sock *newsk)
-{
- return 0;
-}
-
-static int cap_unix_may_send(struct socket *sock, struct socket *other)
-{
- return 0;
-}
-
-static int cap_socket_create(int family, int type, int protocol, int kern)
-{
- return 0;
-}
-
-static int cap_socket_post_create(struct socket *sock, int family, int type,
- int protocol, int kern)
-{
- return 0;
-}
-
-static int cap_socket_bind(struct socket *sock, struct sockaddr *address,
- int addrlen)
-{
- return 0;
-}
-
-static int cap_socket_connect(struct socket *sock, struct sockaddr *address,
- int addrlen)
-{
- return 0;
-}
-
-static int cap_socket_listen(struct socket *sock, int backlog)
-{
- return 0;
-}
-
-static int cap_socket_accept(struct socket *sock, struct socket *newsock)
-{
- return 0;
-}
-
-static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
-{
- return 0;
-}
-
-static int cap_socket_recvmsg(struct socket *sock, struct msghdr *msg,
- int size, int flags)
-{
- return 0;
-}
-
-static int cap_socket_getsockname(struct socket *sock)
-{
- return 0;
-}
-
-static int cap_socket_getpeername(struct socket *sock)
-{
- return 0;
-}
-
-static int cap_socket_setsockopt(struct socket *sock, int level, int optname)
-{
- return 0;
-}
-
-static int cap_socket_getsockopt(struct socket *sock, int level, int optname)
-{
- return 0;
-}
-
-static int cap_socket_shutdown(struct socket *sock, int how)
-{
- return 0;
-}
-
-static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
-{
- return 0;
-}
-
-static int cap_socket_getpeersec_stream(struct socket *sock,
- char __user *optval,
- int __user *optlen, unsigned len)
-{
- return -ENOPROTOOPT;
-}
-
-static int cap_socket_getpeersec_dgram(struct socket *sock,
- struct sk_buff *skb, u32 *secid)
-{
- return -ENOPROTOOPT;
-}
-
-static int cap_sk_alloc_security(struct sock *sk, int family, gfp_t priority)
-{
- return 0;
-}
-
-static void cap_sk_free_security(struct sock *sk)
-{
-}
-
-static void cap_sk_clone_security(const struct sock *sk, struct sock *newsk)
-{
-}
-
-static void cap_sk_getsecid(struct sock *sk, u32 *secid)
-{
-}
-
-static void cap_sock_graft(struct sock *sk, struct socket *parent)
-{
-}
-
-static int cap_inet_conn_request(struct sock *sk, struct sk_buff *skb,
- struct request_sock *req)
-{
- return 0;
-}
-
-static void cap_inet_csk_clone(struct sock *newsk,
- const struct request_sock *req)
-{
-}
-
-static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
-{
-}
-
-static int cap_secmark_relabel_packet(u32 secid)
-{
- return 0;
-}
-
-static void cap_secmark_refcount_inc(void)
-{
-}
-
-static void cap_secmark_refcount_dec(void)
-{
-}
-
-static void cap_req_classify_flow(const struct request_sock *req,
- struct flowi *fl)
-{
-}
-
-static int cap_tun_dev_alloc_security(void **security)
-{
- return 0;
-}
-
-static void cap_tun_dev_free_security(void *security)
-{
-}
-
-static int cap_tun_dev_create(void)
-{
- return 0;
-}
-
-static int cap_tun_dev_attach_queue(void *security)
-{
- return 0;
-}
-
-static int cap_tun_dev_attach(struct sock *sk, void *security)
-{
- return 0;
-}
-
-static int cap_tun_dev_open(void *security)
-{
- return 0;
-}
-#endif /* CONFIG_SECURITY_NETWORK */
-
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
-static int cap_xfrm_policy_alloc_security(struct xfrm_sec_ctx **ctxp,
- struct xfrm_user_sec_ctx *sec_ctx,
- gfp_t gfp)
-{
- return 0;
-}
-
-static int cap_xfrm_policy_clone_security(struct xfrm_sec_ctx *old_ctx,
- struct xfrm_sec_ctx **new_ctxp)
-{
- return 0;
-}
-
-static void cap_xfrm_policy_free_security(struct xfrm_sec_ctx *ctx)
-{
-}
-
-static int cap_xfrm_policy_delete_security(struct xfrm_sec_ctx *ctx)
-{
- return 0;
-}
-
-static int cap_xfrm_state_alloc(struct xfrm_state *x,
- struct xfrm_user_sec_ctx *sec_ctx)
-{
- return 0;
-}
-
-static int cap_xfrm_state_alloc_acquire(struct xfrm_state *x,
- struct xfrm_sec_ctx *polsec,
- u32 secid)
-{
- return 0;
-}
-
-static void cap_xfrm_state_free_security(struct xfrm_state *x)
-{
-}
-
-static int cap_xfrm_state_delete_security(struct xfrm_state *x)
-{
- return 0;
-}
-
-static int cap_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 sk_sid, u8 dir)
-{
- return 0;
-}
-
-static int cap_xfrm_state_pol_flow_match(struct xfrm_state *x,
- struct xfrm_policy *xp,
- const struct flowi *fl)
-{
- return 1;
-}
-
-static int cap_xfrm_decode_session(struct sk_buff *skb, u32 *fl, int ckall)
-{
- return 0;
-}
-
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-static void cap_d_instantiate(struct dentry *dentry, struct inode *inode)
-{
-}
-
-static int cap_getprocattr(struct task_struct *p, char *name, char **value)
-{
- return -EINVAL;
-}
-
-static int cap_setprocattr(struct task_struct *p, char *name, void *value,
- size_t size)
-{
- return -EINVAL;
-}
-
-static int cap_ismaclabel(const char *name)
-{
- return 0;
-}
-
-static int cap_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
-{
- return -EOPNOTSUPP;
-}
-
-static int cap_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
-{
- *secid = 0;
- return 0;
-}
-
-static void cap_release_secctx(char *secdata, u32 seclen)
-{
-}
-
-static int cap_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
-{
- return 0;
-}
-
-static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
-{
- return 0;
-}
-
-static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
-{
- return -EOPNOTSUPP;
-}
-#ifdef CONFIG_KEYS
-static int cap_key_alloc(struct key *key, const struct cred *cred,
- unsigned long flags)
-{
- return 0;
-}
-
-static void cap_key_free(struct key *key)
-{
-}
-
-static int cap_key_permission(key_ref_t key_ref, const struct cred *cred,
- unsigned perm)
-{
- return 0;
-}
-
-static int cap_key_getsecurity(struct key *key, char **_buffer)
-{
- *_buffer = NULL;
- return 0;
-}
-
-#endif /* CONFIG_KEYS */
-
-#ifdef CONFIG_AUDIT
-static int cap_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
-{
- return 0;
-}
-
-static int cap_audit_rule_known(struct audit_krule *krule)
-{
- return 0;
-}
-
-static int cap_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
- struct audit_context *actx)
-{
- return 0;
-}
-
-static void cap_audit_rule_free(void *lsmrule)
-{
-}
-#endif /* CONFIG_AUDIT */
-
-#define set_to_cap_if_null(ops, function) \
- do { \
- if (!ops->function) { \
- ops->function = cap_##function; \
- pr_debug("Had to override the " #function \
- " security operation with the default.\n");\
- } \
- } while (0)
-
-void __init security_fixup_ops(struct security_operations *ops)
-{
- set_to_cap_if_null(ops, binder_set_context_mgr);
- set_to_cap_if_null(ops, binder_transaction);
- set_to_cap_if_null(ops, binder_transfer_binder);
- set_to_cap_if_null(ops, binder_transfer_file);
- set_to_cap_if_null(ops, ptrace_access_check);
- set_to_cap_if_null(ops, ptrace_traceme);
- set_to_cap_if_null(ops, capget);
- set_to_cap_if_null(ops, capset);
- set_to_cap_if_null(ops, capable);
- set_to_cap_if_null(ops, quotactl);
- set_to_cap_if_null(ops, quota_on);
- set_to_cap_if_null(ops, syslog);
- set_to_cap_if_null(ops, settime);
- set_to_cap_if_null(ops, vm_enough_memory);
- set_to_cap_if_null(ops, bprm_set_creds);
- set_to_cap_if_null(ops, bprm_committing_creds);
- set_to_cap_if_null(ops, bprm_committed_creds);
- set_to_cap_if_null(ops, bprm_check_security);
- set_to_cap_if_null(ops, bprm_secureexec);
- set_to_cap_if_null(ops, sb_alloc_security);
- set_to_cap_if_null(ops, sb_free_security);
- set_to_cap_if_null(ops, sb_copy_data);
- set_to_cap_if_null(ops, sb_remount);
- set_to_cap_if_null(ops, sb_kern_mount);
- set_to_cap_if_null(ops, sb_show_options);
- set_to_cap_if_null(ops, sb_statfs);
- set_to_cap_if_null(ops, sb_mount);
- set_to_cap_if_null(ops, sb_umount);
- set_to_cap_if_null(ops, sb_pivotroot);
- set_to_cap_if_null(ops, sb_set_mnt_opts);
- set_to_cap_if_null(ops, sb_clone_mnt_opts);
- set_to_cap_if_null(ops, sb_parse_opts_str);
- set_to_cap_if_null(ops, dentry_init_security);
- set_to_cap_if_null(ops, inode_alloc_security);
- set_to_cap_if_null(ops, inode_free_security);
- set_to_cap_if_null(ops, inode_init_security);
- set_to_cap_if_null(ops, inode_create);
- set_to_cap_if_null(ops, inode_link);
- set_to_cap_if_null(ops, inode_unlink);
- set_to_cap_if_null(ops, inode_symlink);
- set_to_cap_if_null(ops, inode_mkdir);
- set_to_cap_if_null(ops, inode_rmdir);
- set_to_cap_if_null(ops, inode_mknod);
- set_to_cap_if_null(ops, inode_rename);
- set_to_cap_if_null(ops, inode_readlink);
- set_to_cap_if_null(ops, inode_follow_link);
- set_to_cap_if_null(ops, inode_permission);
- set_to_cap_if_null(ops, inode_setattr);
- set_to_cap_if_null(ops, inode_getattr);
- set_to_cap_if_null(ops, inode_setxattr);
- set_to_cap_if_null(ops, inode_post_setxattr);
- set_to_cap_if_null(ops, inode_getxattr);
- set_to_cap_if_null(ops, inode_listxattr);
- set_to_cap_if_null(ops, inode_removexattr);
- set_to_cap_if_null(ops, inode_need_killpriv);
- set_to_cap_if_null(ops, inode_killpriv);
- set_to_cap_if_null(ops, inode_getsecurity);
- set_to_cap_if_null(ops, inode_setsecurity);
- set_to_cap_if_null(ops, inode_listsecurity);
- set_to_cap_if_null(ops, inode_getsecid);
-#ifdef CONFIG_SECURITY_PATH
- set_to_cap_if_null(ops, path_mknod);
- set_to_cap_if_null(ops, path_mkdir);
- set_to_cap_if_null(ops, path_rmdir);
- set_to_cap_if_null(ops, path_unlink);
- set_to_cap_if_null(ops, path_symlink);
- set_to_cap_if_null(ops, path_link);
- set_to_cap_if_null(ops, path_rename);
- set_to_cap_if_null(ops, path_truncate);
- set_to_cap_if_null(ops, path_chmod);
- set_to_cap_if_null(ops, path_chown);
- set_to_cap_if_null(ops, path_chroot);
-#endif
- set_to_cap_if_null(ops, file_permission);
- set_to_cap_if_null(ops, file_alloc_security);
- set_to_cap_if_null(ops, file_free_security);
- set_to_cap_if_null(ops, file_ioctl);
- set_to_cap_if_null(ops, mmap_addr);
- set_to_cap_if_null(ops, mmap_file);
- set_to_cap_if_null(ops, file_mprotect);
- set_to_cap_if_null(ops, file_lock);
- set_to_cap_if_null(ops, file_fcntl);
- set_to_cap_if_null(ops, file_set_fowner);
- set_to_cap_if_null(ops, file_send_sigiotask);
- set_to_cap_if_null(ops, file_receive);
- set_to_cap_if_null(ops, file_open);
- set_to_cap_if_null(ops, task_create);
- set_to_cap_if_null(ops, task_free);
- set_to_cap_if_null(ops, cred_alloc_blank);
- set_to_cap_if_null(ops, cred_free);
- set_to_cap_if_null(ops, cred_prepare);
- set_to_cap_if_null(ops, cred_transfer);
- set_to_cap_if_null(ops, kernel_act_as);
- set_to_cap_if_null(ops, kernel_create_files_as);
- set_to_cap_if_null(ops, kernel_fw_from_file);
- set_to_cap_if_null(ops, kernel_module_request);
- set_to_cap_if_null(ops, kernel_module_from_file);
- set_to_cap_if_null(ops, task_fix_setuid);
- set_to_cap_if_null(ops, task_setpgid);
- set_to_cap_if_null(ops, task_getpgid);
- set_to_cap_if_null(ops, task_getsid);
- set_to_cap_if_null(ops, task_getsecid);
- set_to_cap_if_null(ops, task_setnice);
- set_to_cap_if_null(ops, task_setioprio);
- set_to_cap_if_null(ops, task_getioprio);
- set_to_cap_if_null(ops, task_setrlimit);
- set_to_cap_if_null(ops, task_setscheduler);
- set_to_cap_if_null(ops, task_getscheduler);
- set_to_cap_if_null(ops, task_movememory);
- set_to_cap_if_null(ops, task_wait);
- set_to_cap_if_null(ops, task_kill);
- set_to_cap_if_null(ops, task_prctl);
- set_to_cap_if_null(ops, task_to_inode);
- set_to_cap_if_null(ops, ipc_permission);
- set_to_cap_if_null(ops, ipc_getsecid);
- set_to_cap_if_null(ops, msg_msg_alloc_security);
- set_to_cap_if_null(ops, msg_msg_free_security);
- set_to_cap_if_null(ops, msg_queue_alloc_security);
- set_to_cap_if_null(ops, msg_queue_free_security);
- set_to_cap_if_null(ops, msg_queue_associate);
- set_to_cap_if_null(ops, msg_queue_msgctl);
- set_to_cap_if_null(ops, msg_queue_msgsnd);
- set_to_cap_if_null(ops, msg_queue_msgrcv);
- set_to_cap_if_null(ops, shm_alloc_security);
- set_to_cap_if_null(ops, shm_free_security);
- set_to_cap_if_null(ops, shm_associate);
- set_to_cap_if_null(ops, shm_shmctl);
- set_to_cap_if_null(ops, shm_shmat);
- set_to_cap_if_null(ops, sem_alloc_security);
- set_to_cap_if_null(ops, sem_free_security);
- set_to_cap_if_null(ops, sem_associate);
- set_to_cap_if_null(ops, sem_semctl);
- set_to_cap_if_null(ops, sem_semop);
- set_to_cap_if_null(ops, netlink_send);
- set_to_cap_if_null(ops, d_instantiate);
- set_to_cap_if_null(ops, getprocattr);
- set_to_cap_if_null(ops, setprocattr);
- set_to_cap_if_null(ops, ismaclabel);
- set_to_cap_if_null(ops, secid_to_secctx);
- set_to_cap_if_null(ops, secctx_to_secid);
- set_to_cap_if_null(ops, release_secctx);
- set_to_cap_if_null(ops, inode_notifysecctx);
- set_to_cap_if_null(ops, inode_setsecctx);
- set_to_cap_if_null(ops, inode_getsecctx);
-#ifdef CONFIG_SECURITY_NETWORK
- set_to_cap_if_null(ops, unix_stream_connect);
- set_to_cap_if_null(ops, unix_may_send);
- set_to_cap_if_null(ops, socket_create);
- set_to_cap_if_null(ops, socket_post_create);
- set_to_cap_if_null(ops, socket_bind);
- set_to_cap_if_null(ops, socket_connect);
- set_to_cap_if_null(ops, socket_listen);
- set_to_cap_if_null(ops, socket_accept);
- set_to_cap_if_null(ops, socket_sendmsg);
- set_to_cap_if_null(ops, socket_recvmsg);
- set_to_cap_if_null(ops, socket_getsockname);
- set_to_cap_if_null(ops, socket_getpeername);
- set_to_cap_if_null(ops, socket_setsockopt);
- set_to_cap_if_null(ops, socket_getsockopt);
- set_to_cap_if_null(ops, socket_shutdown);
- set_to_cap_if_null(ops, socket_sock_rcv_skb);
- set_to_cap_if_null(ops, socket_getpeersec_stream);
- set_to_cap_if_null(ops, socket_getpeersec_dgram);
- set_to_cap_if_null(ops, sk_alloc_security);
- set_to_cap_if_null(ops, sk_free_security);
- set_to_cap_if_null(ops, sk_clone_security);
- set_to_cap_if_null(ops, sk_getsecid);
- set_to_cap_if_null(ops, sock_graft);
- set_to_cap_if_null(ops, inet_conn_request);
- set_to_cap_if_null(ops, inet_csk_clone);
- set_to_cap_if_null(ops, inet_conn_established);
- set_to_cap_if_null(ops, secmark_relabel_packet);
- set_to_cap_if_null(ops, secmark_refcount_inc);
- set_to_cap_if_null(ops, secmark_refcount_dec);
- set_to_cap_if_null(ops, req_classify_flow);
- set_to_cap_if_null(ops, tun_dev_alloc_security);
- set_to_cap_if_null(ops, tun_dev_free_security);
- set_to_cap_if_null(ops, tun_dev_create);
- set_to_cap_if_null(ops, tun_dev_open);
- set_to_cap_if_null(ops, tun_dev_attach_queue);
- set_to_cap_if_null(ops, tun_dev_attach);
-#endif /* CONFIG_SECURITY_NETWORK */
-#ifdef CONFIG_SECURITY_NETWORK_XFRM
- set_to_cap_if_null(ops, xfrm_policy_alloc_security);
- set_to_cap_if_null(ops, xfrm_policy_clone_security);
- set_to_cap_if_null(ops, xfrm_policy_free_security);
- set_to_cap_if_null(ops, xfrm_policy_delete_security);
- set_to_cap_if_null(ops, xfrm_state_alloc);
- set_to_cap_if_null(ops, xfrm_state_alloc_acquire);
- set_to_cap_if_null(ops, xfrm_state_free_security);
- set_to_cap_if_null(ops, xfrm_state_delete_security);
- set_to_cap_if_null(ops, xfrm_policy_lookup);
- set_to_cap_if_null(ops, xfrm_state_pol_flow_match);
- set_to_cap_if_null(ops, xfrm_decode_session);
-#endif /* CONFIG_SECURITY_NETWORK_XFRM */
-#ifdef CONFIG_KEYS
- set_to_cap_if_null(ops, key_alloc);
- set_to_cap_if_null(ops, key_free);
- set_to_cap_if_null(ops, key_permission);
- set_to_cap_if_null(ops, key_getsecurity);
-#endif /* CONFIG_KEYS */
-#ifdef CONFIG_AUDIT
- set_to_cap_if_null(ops, audit_rule_init);
- set_to_cap_if_null(ops, audit_rule_known);
- set_to_cap_if_null(ops, audit_rule_match);
- set_to_cap_if_null(ops, audit_rule_free);
-#endif
-}
diff --git a/kernel/security/commoncap.c b/kernel/security/commoncap.c
index f2875cd9f..48071ed7c 100644
--- a/kernel/security/commoncap.c
+++ b/kernel/security/commoncap.c
@@ -12,7 +12,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/mman.h>
@@ -53,11 +53,6 @@ static void warn_setuid_and_fcaps_mixed(const char *fname)
}
}
-int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
-{
- return 0;
-}
-
/**
* cap_capable - Determine whether a task has a particular effective capability
* @cred: The credentials to use
@@ -142,12 +137,17 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
int ret = 0;
const struct cred *cred, *child_cred;
+ const kernel_cap_t *caller_caps;
rcu_read_lock();
cred = current_cred();
child_cred = __task_cred(child);
+ if (mode & PTRACE_MODE_FSCREDS)
+ caller_caps = &cred->cap_effective;
+ else
+ caller_caps = &cred->cap_permitted;
if (cred->user_ns == child_cred->user_ns &&
- cap_issubset(child_cred->cap_permitted, cred->cap_permitted))
+ cap_issubset(child_cred->cap_permitted, *caller_caps))
goto out;
if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE))
goto out;
@@ -272,6 +272,16 @@ int cap_capset(struct cred *new,
new->cap_effective = *effective;
new->cap_inheritable = *inheritable;
new->cap_permitted = *permitted;
+
+ /*
+ * Mask off ambient bits that are no longer both permitted and
+ * inheritable.
+ */
+ new->cap_ambient = cap_intersect(new->cap_ambient,
+ cap_intersect(*permitted,
+ *inheritable));
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EINVAL;
return 0;
}
@@ -352,6 +362,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
/*
* pP' = (X & fP) | (pI & fI)
+ * The addition of pA' is handled later.
*/
new->cap_permitted.cap[i] =
(new->cap_bset.cap[i] & permitted) |
@@ -479,10 +490,13 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
{
const struct cred *old = current_cred();
struct cred *new = bprm->cred;
- bool effective, has_cap = false;
+ bool effective, has_cap = false, is_setid;
int ret;
kuid_t root_uid;
+ if (WARN_ON(!cap_ambient_invariant_ok(old)))
+ return -EPERM;
+
effective = false;
ret = get_file_caps(bprm, &effective, &has_cap);
if (ret < 0)
@@ -527,8 +541,9 @@ skip:
*
* In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
- if ((!uid_eq(new->euid, old->uid) ||
- !gid_eq(new->egid, old->gid) ||
+ is_setid = !uid_eq(new->euid, old->uid) || !gid_eq(new->egid, old->gid);
+
+ if ((is_setid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
/* downgrade; they get no more than they had, and maybe less */
@@ -544,10 +559,28 @@ skip:
new->suid = new->fsuid = new->euid;
new->sgid = new->fsgid = new->egid;
+ /* File caps or setid cancels ambient. */
+ if (has_cap || is_setid)
+ cap_clear(new->cap_ambient);
+
+ /*
+ * Now that we've computed pA', update pP' to give:
+ * pP' = (X & fP) | (pI & fI) | pA'
+ */
+ new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);
+
+ /*
+ * Set pE' = (fE ? pP' : pA'). Because pA' is zero if fE is set,
+ * this is the same as pE' = (fE ? pP' : 0) | pA'.
+ */
if (effective)
new->cap_effective = new->cap_permitted;
else
- cap_clear(new->cap_effective);
+ new->cap_effective = new->cap_ambient;
+
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EPERM;
+
bprm->cap_effective = effective;
/*
@@ -562,7 +595,7 @@ skip:
* Number 1 above might fail if you don't have a full bset, but I think
* that is interesting information to audit.
*/
- if (!cap_isclear(new->cap_effective)) {
+ if (!cap_issubset(new->cap_effective, new->cap_ambient)) {
if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
!uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) ||
issecure(SECURE_NOROOT)) {
@@ -573,6 +606,10 @@ skip:
}
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EPERM;
+
return 0;
}
@@ -594,7 +631,7 @@ int cap_bprm_secureexec(struct linux_binprm *bprm)
if (!uid_eq(cred->uid, root_uid)) {
if (bprm->cap_effective)
return 1;
- if (!cap_isclear(cred->cap_permitted))
+ if (!cap_issubset(cred->cap_permitted, cred->cap_ambient))
return 1;
}
@@ -696,10 +733,18 @@ static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
uid_eq(old->suid, root_uid)) &&
(!uid_eq(new->uid, root_uid) &&
!uid_eq(new->euid, root_uid) &&
- !uid_eq(new->suid, root_uid)) &&
- !issecure(SECURE_KEEP_CAPS)) {
- cap_clear(new->cap_permitted);
- cap_clear(new->cap_effective);
+ !uid_eq(new->suid, root_uid))) {
+ if (!issecure(SECURE_KEEP_CAPS)) {
+ cap_clear(new->cap_permitted);
+ cap_clear(new->cap_effective);
+ }
+
+ /*
+ * Pre-ambient programs expect setresuid to nonroot followed
+ * by exec to drop capabilities. We should make sure that
+ * this remains the case.
+ */
+ cap_clear(new->cap_ambient);
}
if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
cap_clear(new->cap_effective);
@@ -929,6 +974,44 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
return commit_creds(new);
+ case PR_CAP_AMBIENT:
+ if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) {
+ if (arg3 | arg4 | arg5)
+ return -EINVAL;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ cap_clear(new->cap_ambient);
+ return commit_creds(new);
+ }
+
+ if (((!cap_valid(arg3)) | arg4 | arg5))
+ return -EINVAL;
+
+ if (arg2 == PR_CAP_AMBIENT_IS_SET) {
+ return !!cap_raised(current_cred()->cap_ambient, arg3);
+ } else if (arg2 != PR_CAP_AMBIENT_RAISE &&
+ arg2 != PR_CAP_AMBIENT_LOWER) {
+ return -EINVAL;
+ } else {
+ if (arg2 == PR_CAP_AMBIENT_RAISE &&
+ (!cap_raised(current_cred()->cap_permitted, arg3) ||
+ !cap_raised(current_cred()->cap_inheritable,
+ arg3) ||
+ issecure(SECURE_NO_CAP_AMBIENT_RAISE)))
+ return -EPERM;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ if (arg2 == PR_CAP_AMBIENT_RAISE)
+ cap_raise(new->cap_ambient, arg3);
+ else
+ cap_lower(new->cap_ambient, arg3);
+ return commit_creds(new);
+ }
+
default:
/* No functionality available - continue with default */
return -ENOSYS;
@@ -941,7 +1024,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* @pages: The size of the mapping
*
* Determine whether the allocation of a new virtual mapping by the current
- * task is permitted, returning 0 if permission is granted, -ve if not.
+ * task is permitted, returning 1 if permission is granted, 0 if not.
*/
int cap_vm_enough_memory(struct mm_struct *mm, long pages)
{
@@ -950,7 +1033,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
SECURITY_CAP_NOAUDIT) == 0)
cap_sys_admin = 1;
- return __vm_enough_memory(mm, pages, cap_sys_admin);
+ return cap_sys_admin;
}
/*
@@ -981,3 +1064,33 @@ int cap_mmap_file(struct file *file, unsigned long reqprot,
{
return 0;
}
+
+#ifdef CONFIG_SECURITY
+
+struct security_hook_list capability_hooks[] = {
+ LSM_HOOK_INIT(capable, cap_capable),
+ LSM_HOOK_INIT(settime, cap_settime),
+ LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
+ LSM_HOOK_INIT(capget, cap_capget),
+ LSM_HOOK_INIT(capset, cap_capset),
+ LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds),
+ LSM_HOOK_INIT(bprm_secureexec, cap_bprm_secureexec),
+ LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
+ LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
+ LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
+ LSM_HOOK_INIT(mmap_file, cap_mmap_file),
+ LSM_HOOK_INIT(task_fix_setuid, cap_task_fix_setuid),
+ LSM_HOOK_INIT(task_prctl, cap_task_prctl),
+ LSM_HOOK_INIT(task_setscheduler, cap_task_setscheduler),
+ LSM_HOOK_INIT(task_setioprio, cap_task_setioprio),
+ LSM_HOOK_INIT(task_setnice, cap_task_setnice),
+ LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
+};
+
+void __init capability_add_hooks(void)
+{
+ security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks));
+}
+
+#endif /* CONFIG_SECURITY */
diff --git a/kernel/security/device_cgroup.c b/kernel/security/device_cgroup.c
index 188c1d263..03c1652c9 100644
--- a/kernel/security/device_cgroup.c
+++ b/kernel/security/device_cgroup.c
@@ -400,9 +400,9 @@ static bool verify_new_ex(struct dev_cgroup *dev_cgroup,
{
bool match = false;
- rcu_lockdep_assert(rcu_read_lock_held() ||
- lockdep_is_held(&devcgroup_mutex),
- "device_cgroup:verify_new_ex called without proper synchronization");
+ RCU_LOCKDEP_WARN(!rcu_read_lock_held() &&
+ !lockdep_is_held(&devcgroup_mutex),
+ "device_cgroup:verify_new_ex called without proper synchronization");
if (dev_cgroup->behavior == DEVCG_DEFAULT_ALLOW) {
if (behavior == DEVCG_DEFAULT_ALLOW) {
diff --git a/kernel/security/inode.c b/kernel/security/inode.c
index 0e37e4fba..16622aef9 100644
--- a/kernel/security/inode.c
+++ b/kernel/security/inode.c
@@ -25,11 +25,6 @@
static struct vfsmount *mount;
static int mount_count;
-static inline int positive(struct dentry *dentry)
-{
- return d_really_is_positive(dentry) && !d_unhashed(dentry);
-}
-
static int fill_super(struct super_block *sb, void *data, int silent)
{
static struct tree_descr files[] = {{""}};
@@ -201,14 +196,12 @@ void securityfs_remove(struct dentry *dentry)
return;
mutex_lock(&d_inode(parent)->i_mutex);
- if (positive(dentry)) {
- if (d_really_is_positive(dentry)) {
- if (d_is_dir(dentry))
- simple_rmdir(d_inode(parent), dentry);
- else
- simple_unlink(d_inode(parent), dentry);
- dput(dentry);
- }
+ if (simple_positive(dentry)) {
+ if (d_is_dir(dentry))
+ simple_rmdir(d_inode(parent), dentry);
+ else
+ simple_unlink(d_inode(parent), dentry);
+ dput(dentry);
}
mutex_unlock(&d_inode(parent)->i_mutex);
simple_release_fs(&mount, &mount_count);
diff --git a/kernel/security/integrity/digsig.c b/kernel/security/integrity/digsig.c
index 5e3bd72b2..5be9ffbe9 100644
--- a/kernel/security/integrity/digsig.c
+++ b/kernel/security/integrity/digsig.c
@@ -85,7 +85,7 @@ int __init integrity_init_keyring(const unsigned int id)
return err;
}
-int __init integrity_load_x509(const unsigned int id, char *path)
+int __init integrity_load_x509(const unsigned int id, const char *path)
{
key_ref_t key;
char *data;
@@ -105,7 +105,7 @@ int __init integrity_load_x509(const unsigned int id, char *path)
rc,
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
KEY_USR_VIEW | KEY_USR_READ),
- KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_TRUSTED);
+ KEY_ALLOC_NOT_IN_QUOTA);
if (IS_ERR(key)) {
rc = PTR_ERR(key);
pr_err("Problem loading X.509 certificate (%d): %s\n",
diff --git a/kernel/security/integrity/evm/evm_crypto.c b/kernel/security/integrity/evm/evm_crypto.c
index 159ef3ea4..461f8d891 100644
--- a/kernel/security/integrity/evm/evm_crypto.c
+++ b/kernel/security/integrity/evm/evm_crypto.c
@@ -247,7 +247,7 @@ int evm_init_key(void)
return -ENOENT;
down_read(&evm_key->sem);
- ekp = evm_key->payload.data;
+ ekp = evm_key->payload.data[0];
if (ekp->decrypted_datalen > MAX_KEY_SIZE) {
rc = -EINVAL;
goto out;
diff --git a/kernel/security/integrity/evm/evm_main.c b/kernel/security/integrity/evm/evm_main.c
index 582091498..3d145a3ff 100644
--- a/kernel/security/integrity/evm/evm_main.c
+++ b/kernel/security/integrity/evm/evm_main.c
@@ -23,6 +23,7 @@
#include <linux/integrity.h>
#include <linux/evm.h>
#include <crypto/hash.h>
+#include <crypto/algapi.h>
#include "evm.h"
int evm_initialized;
@@ -148,7 +149,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
xattr_value_len, calc.digest);
if (rc)
break;
- rc = memcmp(xattr_data->digest, calc.digest,
+ rc = crypto_memneq(xattr_data->digest, calc.digest,
sizeof(calc.digest));
if (rc)
rc = -EINVAL;
@@ -387,17 +388,16 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name,
* @xattr_name: pointer to the affected extended attribute name
*
* Update the HMAC stored in 'security.evm' to reflect removal of the xattr.
+ *
+ * No need to take the i_mutex lock here, as this function is called from
+ * vfs_removexattr() which takes the i_mutex.
*/
void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name)
{
- struct inode *inode = d_backing_inode(dentry);
-
if (!evm_initialized || !evm_protected_xattr(xattr_name))
return;
- mutex_lock(&inode->i_mutex);
evm_update_evmxattr(dentry, xattr_name, NULL, 0);
- mutex_unlock(&inode->i_mutex);
}
/**
diff --git a/kernel/security/integrity/iint.c b/kernel/security/integrity/iint.c
index dbb6d141c..3d2f5b45c 100644
--- a/kernel/security/integrity/iint.c
+++ b/kernel/security/integrity/iint.c
@@ -213,6 +213,9 @@ int __init integrity_read_file(const char *path, char **data)
char *buf;
int rc = -EINVAL;
+ if (!path || !*path)
+ return -EINVAL;
+
file = filp_open(path, O_RDONLY, 0);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
diff --git a/kernel/security/integrity/ima/ima.h b/kernel/security/integrity/ima/ima.h
index fc56d4dfa..e2a60c30d 100644
--- a/kernel/security/integrity/ima/ima.h
+++ b/kernel/security/integrity/ima/ima.h
@@ -52,6 +52,16 @@ extern int ima_used_chip;
extern int ima_hash_algo;
extern int ima_appraise;
+/* IMA event related data */
+struct ima_event_data {
+ struct integrity_iint_cache *iint;
+ struct file *file;
+ const unsigned char *filename;
+ struct evm_ima_xattr_data *xattr_value;
+ int xattr_len;
+ const char *violation;
+};
+
/* IMA template field data definition */
struct ima_field_data {
u8 *data;
@@ -61,12 +71,10 @@ struct ima_field_data {
/* IMA template field definition */
struct ima_template_field {
const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
- int (*field_init) (struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value,
- int xattr_len, struct ima_field_data *field_data);
- void (*field_show) (struct seq_file *m, enum ima_show_type show,
- struct ima_field_data *field_data);
+ int (*field_init)(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+ void (*field_show)(struct seq_file *m, enum ima_show_type show,
+ struct ima_field_data *field_data);
};
/* IMA template descriptor definition */
@@ -103,6 +111,7 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
struct ima_digest_data *hash);
int __init ima_calc_boot_aggregate(struct ima_digest_data *hash);
void ima_add_violation(struct file *file, const unsigned char *filename,
+ struct integrity_iint_cache *iint,
const char *op, const char *cause);
int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
@@ -140,10 +149,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
int xattr_len);
void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename);
-int ima_alloc_init_template(struct integrity_iint_cache *iint,
- struct file *file, const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value,
- int xattr_len, struct ima_template_entry **entry);
+int ima_alloc_init_template(struct ima_event_data *event_data,
+ struct ima_template_entry **entry);
int ima_store_template(struct ima_template_entry *entry, int violation,
struct inode *inode, const unsigned char *filename);
void ima_free_template_entry(struct ima_template_entry *entry);
diff --git a/kernel/security/integrity/ima/ima_api.c b/kernel/security/integrity/ima/ima_api.c
index b8a27c505..1d950fbb2 100644
--- a/kernel/security/integrity/ima/ima_api.c
+++ b/kernel/security/integrity/ima/ima_api.c
@@ -37,10 +37,8 @@ void ima_free_template_entry(struct ima_template_entry *entry)
/*
* ima_alloc_init_template - create and initialize a new template entry
*/
-int ima_alloc_init_template(struct integrity_iint_cache *iint,
- struct file *file, const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value,
- int xattr_len, struct ima_template_entry **entry)
+int ima_alloc_init_template(struct ima_event_data *event_data,
+ struct ima_template_entry **entry)
{
struct ima_template_desc *template_desc = ima_template_desc_current();
int i, result = 0;
@@ -55,8 +53,7 @@ int ima_alloc_init_template(struct integrity_iint_cache *iint,
struct ima_template_field *field = template_desc->fields[i];
u32 len;
- result = field->field_init(iint, file, filename,
- xattr_value, xattr_len,
+ result = field->field_init(event_data,
&((*entry)->template_data[i]));
if (result != 0)
goto out;
@@ -129,18 +126,20 @@ int ima_store_template(struct ima_template_entry *entry,
* value is invalidated.
*/
void ima_add_violation(struct file *file, const unsigned char *filename,
+ struct integrity_iint_cache *iint,
const char *op, const char *cause)
{
struct ima_template_entry *entry;
struct inode *inode = file_inode(file);
+ struct ima_event_data event_data = {iint, file, filename, NULL, 0,
+ cause};
int violation = 1;
int result;
/* can overflow, only indicator */
atomic_long_inc(&ima_htable.violations);
- result = ima_alloc_init_template(NULL, file, filename,
- NULL, 0, &entry);
+ result = ima_alloc_init_template(&event_data, &entry);
if (result < 0) {
result = -ENOMEM;
goto err_out;
@@ -267,13 +266,14 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
int result = -ENOMEM;
struct inode *inode = file_inode(file);
struct ima_template_entry *entry;
+ struct ima_event_data event_data = {iint, file, filename, xattr_value,
+ xattr_len, NULL};
int violation = 0;
if (iint->flags & IMA_MEASURED)
return;
- result = ima_alloc_init_template(iint, file, filename,
- xattr_value, xattr_len, &entry);
+ result = ima_alloc_init_template(&event_data, &entry);
if (result < 0) {
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
op, audit_cause, result, 0);
diff --git a/kernel/security/integrity/ima/ima_appraise.c b/kernel/security/integrity/ima/ima_appraise.c
index 4df493e4b..1873b5536 100644
--- a/kernel/security/integrity/ima/ima_appraise.c
+++ b/kernel/security/integrity/ima/ima_appraise.c
@@ -378,10 +378,14 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len);
if (result == 1) {
+ bool digsig;
+
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL;
- ima_reset_appraise_flags(d_backing_inode(dentry),
- (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
+ digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
+ if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE))
+ return -EPERM;
+ ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
result = 0;
}
return result;
diff --git a/kernel/security/integrity/ima/ima_crypto.c b/kernel/security/integrity/ima/ima_crypto.c
index 686355fea..6eb62936c 100644
--- a/kernel/security/integrity/ima/ima_crypto.c
+++ b/kernel/security/integrity/ima/ima_crypto.c
@@ -55,7 +55,7 @@ static int param_set_bufsize(const char *val, const struct kernel_param *kp)
return 0;
}
-static struct kernel_param_ops param_ops_bufsize = {
+static const struct kernel_param_ops param_ops_bufsize = {
.set = param_set_bufsize,
.get = param_get_uint,
};
@@ -126,7 +126,7 @@ static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size,
{
void *ptr;
int order = ima_maxorder;
- gfp_t gfp_mask = __GFP_WAIT | __GFP_NOWARN | __GFP_NORETRY;
+ gfp_t gfp_mask = __GFP_RECLAIM | __GFP_NOWARN | __GFP_NORETRY;
if (order)
order = min(get_order(max_size), order);
diff --git a/kernel/security/integrity/ima/ima_init.c b/kernel/security/integrity/ima/ima_init.c
index 5e4c29d17..e600cadd2 100644
--- a/kernel/security/integrity/ima/ima_init.c
+++ b/kernel/security/integrity/ima/ima_init.c
@@ -24,12 +24,6 @@
#include <crypto/hash_info.h>
#include "ima.h"
-#ifdef CONFIG_IMA_X509_PATH
-#define IMA_X509_PATH CONFIG_IMA_X509_PATH
-#else
-#define IMA_X509_PATH "/etc/keys/x509_ima.der"
-#endif
-
/* name for boot aggregate entry */
static const char *boot_aggregate_name = "boot_aggregate";
int ima_used_chip;
@@ -55,6 +49,8 @@ static int __init ima_add_boot_aggregate(void)
const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry;
struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
+ struct ima_event_data event_data = {iint, NULL, boot_aggregate_name,
+ NULL, 0, NULL};
int result = -ENOMEM;
int violation = 0;
struct {
@@ -76,8 +72,7 @@ static int __init ima_add_boot_aggregate(void)
}
}
- result = ima_alloc_init_template(iint, NULL, boot_aggregate_name,
- NULL, 0, &entry);
+ result = ima_alloc_init_template(&event_data, &entry);
if (result < 0) {
audit_cause = "alloc_entry";
goto err_out;
@@ -103,7 +98,7 @@ void __init ima_load_x509(void)
int unset_flags = ima_policy_flag & IMA_APPRAISE;
ima_policy_flag &= ~unset_flags;
- integrity_load_x509(INTEGRITY_KEYRING_IMA, IMA_X509_PATH);
+ integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
ima_policy_flag |= unset_flags;
}
#endif
diff --git a/kernel/security/integrity/ima/ima_main.c b/kernel/security/integrity/ima/ima_main.c
index eeee00dce..c21f09bf8 100644
--- a/kernel/security/integrity/ima/ima_main.c
+++ b/kernel/security/integrity/ima/ima_main.c
@@ -106,9 +106,10 @@ static void ima_rdwr_violation_check(struct file *file,
*pathname = ima_d_path(&file->f_path, pathbuf);
if (send_tomtou)
- ima_add_violation(file, *pathname, "invalid_pcr", "ToMToU");
+ ima_add_violation(file, *pathname, iint,
+ "invalid_pcr", "ToMToU");
if (send_writers)
- ima_add_violation(file, *pathname,
+ ima_add_violation(file, *pathname, iint,
"invalid_pcr", "open_writers");
}
diff --git a/kernel/security/integrity/ima/ima_template_lib.c b/kernel/security/integrity/ima/ima_template_lib.c
index 61fbd0c0d..2934e3d37 100644
--- a/kernel/security/integrity/ima/ima_template_lib.c
+++ b/kernel/security/integrity/ima/ima_template_lib.c
@@ -196,9 +196,7 @@ static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo,
/*
* This function writes the digest of an event (with size limit).
*/
-int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
struct {
@@ -212,25 +210,25 @@ int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
memset(&hash, 0, sizeof(hash));
- if (!iint) /* recording a violation. */
+ if (event_data->violation) /* recording a violation. */
goto out;
- if (ima_template_hash_algo_allowed(iint->ima_hash->algo)) {
- cur_digest = iint->ima_hash->digest;
- cur_digestsize = iint->ima_hash->length;
+ if (ima_template_hash_algo_allowed(event_data->iint->ima_hash->algo)) {
+ cur_digest = event_data->iint->ima_hash->digest;
+ cur_digestsize = event_data->iint->ima_hash->length;
goto out;
}
- if (!file) /* missing info to re-calculate the digest */
+ if (!event_data->file) /* missing info to re-calculate the digest */
return -EINVAL;
- inode = file_inode(file);
+ inode = file_inode(event_data->file);
hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
ima_hash_algo : HASH_ALGO_SHA1;
- result = ima_calc_file_hash(file, &hash.hdr);
+ result = ima_calc_file_hash(event_data->file, &hash.hdr);
if (result) {
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
- filename, "collect_data",
+ event_data->filename, "collect_data",
"failed", result, 0);
return result;
}
@@ -244,48 +242,43 @@ out:
/*
* This function writes the digest of an event (without size limit).
*/
-int ima_eventdigest_ng_init(struct integrity_iint_cache *iint,
- struct file *file, const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value,
- int xattr_len, struct ima_field_data *field_data)
+int ima_eventdigest_ng_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data)
{
u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1;
u32 cur_digestsize = 0;
- /* If iint is NULL, we are recording a violation. */
- if (!iint)
+ if (event_data->violation) /* recording a violation. */
goto out;
- cur_digest = iint->ima_hash->digest;
- cur_digestsize = iint->ima_hash->length;
+ cur_digest = event_data->iint->ima_hash->digest;
+ cur_digestsize = event_data->iint->ima_hash->length;
- hash_algo = iint->ima_hash->algo;
+ hash_algo = event_data->iint->ima_hash->algo;
out:
return ima_eventdigest_init_common(cur_digest, cur_digestsize,
hash_algo, field_data);
}
-static int ima_eventname_init_common(struct integrity_iint_cache *iint,
- struct file *file,
- const unsigned char *filename,
+static int ima_eventname_init_common(struct ima_event_data *event_data,
struct ima_field_data *field_data,
bool size_limit)
{
const char *cur_filename = NULL;
u32 cur_filename_len = 0;
- BUG_ON(filename == NULL && file == NULL);
+ BUG_ON(event_data->filename == NULL && event_data->file == NULL);
- if (filename) {
- cur_filename = filename;
- cur_filename_len = strlen(filename);
+ if (event_data->filename) {
+ cur_filename = event_data->filename;
+ cur_filename_len = strlen(event_data->filename);
if (!size_limit || cur_filename_len <= IMA_EVENT_NAME_LEN_MAX)
goto out;
}
- if (file) {
- cur_filename = file->f_path.dentry->d_name.name;
+ if (event_data->file) {
+ cur_filename = event_data->file->f_path.dentry->d_name.name;
cur_filename_len = strlen(cur_filename);
} else
/*
@@ -301,36 +294,30 @@ out:
/*
* This function writes the name of an event (with size limit).
*/
-int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventname_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
- return ima_eventname_init_common(iint, file, filename,
- field_data, true);
+ return ima_eventname_init_common(event_data, field_data, true);
}
/*
* This function writes the name of an event (without size limit).
*/
-int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
- return ima_eventname_init_common(iint, file, filename,
- field_data, false);
+ return ima_eventname_init_common(event_data, field_data, false);
}
/*
* ima_eventsig_init - include the file signature as part of the template data
*/
-int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data)
{
enum data_formats fmt = DATA_FMT_HEX;
+ struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
+ int xattr_len = event_data->xattr_len;
int rc = 0;
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
diff --git a/kernel/security/integrity/ima/ima_template_lib.h b/kernel/security/integrity/ima/ima_template_lib.h
index 63f6b52cb..c344530c1 100644
--- a/kernel/security/integrity/ima/ima_template_lib.h
+++ b/kernel/security/integrity/ima/ima_template_lib.h
@@ -26,24 +26,14 @@ void ima_show_template_string(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
void ima_show_template_sig(struct seq_file *m, enum ima_show_type show,
struct ima_field_data *field_data);
-int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventdigest_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
-int ima_eventname_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventname_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
-int ima_eventdigest_ng_init(struct integrity_iint_cache *iint,
- struct file *file, const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value,
- int xattr_len, struct ima_field_data *field_data);
-int ima_eventname_ng_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventdigest_ng_init(struct ima_event_data *event_data,
+ struct ima_field_data *field_data);
+int ima_eventname_ng_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
-int ima_eventsig_init(struct integrity_iint_cache *iint, struct file *file,
- const unsigned char *filename,
- struct evm_ima_xattr_data *xattr_value, int xattr_len,
+int ima_eventsig_init(struct ima_event_data *event_data,
struct ima_field_data *field_data);
#endif /* __LINUX_IMA_TEMPLATE_LIB_H */
diff --git a/kernel/security/integrity/integrity.h b/kernel/security/integrity/integrity.h
index 0fc9519fe..9c6168709 100644
--- a/kernel/security/integrity/integrity.h
+++ b/kernel/security/integrity/integrity.h
@@ -135,7 +135,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen);
int __init integrity_init_keyring(const unsigned int id);
-int __init integrity_load_x509(const unsigned int id, char *path);
+int __init integrity_load_x509(const unsigned int id, const char *path);
#else
static inline int integrity_digsig_verify(const unsigned int id,
diff --git a/kernel/security/keys/big_key.c b/kernel/security/keys/big_key.c
index b6adb94f6..907c1522e 100644
--- a/kernel/security/keys/big_key.c
+++ b/kernel/security/keys/big_key.c
@@ -21,6 +21,16 @@
MODULE_LICENSE("GPL");
/*
+ * Layout of key payload words.
+ */
+enum {
+ big_key_data,
+ big_key_path,
+ big_key_path_2nd_part,
+ big_key_len,
+};
+
+/*
* If the data is under this limit, there's no point creating a shm file to
* hold it as the permanently resident metadata for the shmem fs will be at
* least as large as the data.
@@ -47,7 +57,7 @@ struct key_type key_type_big_key = {
*/
int big_key_preparse(struct key_preparsed_payload *prep)
{
- struct path *path = (struct path *)&prep->payload;
+ struct path *path = (struct path *)&prep->payload.data[big_key_path];
struct file *file;
ssize_t written;
size_t datalen = prep->datalen;
@@ -60,7 +70,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
/* Set an arbitrary quota */
prep->quotalen = 16;
- prep->type_data[1] = (void *)(unsigned long)datalen;
+ prep->payload.data[big_key_len] = (void *)(unsigned long)datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) {
/* Create a shmem file to store the data in. This will permit the data
@@ -94,7 +104,8 @@ int big_key_preparse(struct key_preparsed_payload *prep)
if (!data)
return -ENOMEM;
- prep->payload[0] = memcpy(data, prep->data, prep->datalen);
+ prep->payload.data[big_key_data] = data;
+ memcpy(data, prep->data, prep->datalen);
}
return 0;
@@ -110,10 +121,10 @@ error:
void big_key_free_preparse(struct key_preparsed_payload *prep)
{
if (prep->datalen > BIG_KEY_FILE_THRESHOLD) {
- struct path *path = (struct path *)&prep->payload;
+ struct path *path = (struct path *)&prep->payload.data[big_key_path];
path_put(path);
} else {
- kfree(prep->payload[0]);
+ kfree(prep->payload.data[big_key_data]);
}
}
@@ -123,11 +134,12 @@ void big_key_free_preparse(struct key_preparsed_payload *prep)
*/
void big_key_revoke(struct key *key)
{
- struct path *path = (struct path *)&key->payload.data2;
+ struct path *path = (struct path *)&key->payload.data[big_key_path];
/* clear the quota */
key_payload_reserve(key, 0);
- if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD)
+ if (key_is_instantiated(key) &&
+ (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
vfs_truncate(path, 0);
}
@@ -136,14 +148,16 @@ void big_key_revoke(struct key *key)
*/
void big_key_destroy(struct key *key)
{
- if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) {
- struct path *path = (struct path *)&key->payload.data2;
+ size_t datalen = (size_t)key->payload.data[big_key_len];
+
+ if (datalen) {
+ struct path *path = (struct path *)&key->payload.data[big_key_path];
path_put(path);
path->mnt = NULL;
path->dentry = NULL;
} else {
- kfree(key->payload.data);
- key->payload.data = NULL;
+ kfree(key->payload.data[big_key_data]);
+ key->payload.data[big_key_data] = NULL;
}
}
@@ -152,12 +166,12 @@ void big_key_destroy(struct key *key)
*/
void big_key_describe(const struct key *key, struct seq_file *m)
{
- unsigned long datalen = key->type_data.x[1];
+ size_t datalen = (size_t)key->payload.data[big_key_len];
seq_puts(m, key->description);
if (key_is_instantiated(key))
- seq_printf(m, ": %lu [%s]",
+ seq_printf(m, ": %zu [%s]",
datalen,
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
}
@@ -168,14 +182,14 @@ void big_key_describe(const struct key *key, struct seq_file *m)
*/
long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
{
- unsigned long datalen = key->type_data.x[1];
+ size_t datalen = (size_t)key->payload.data[big_key_len];
long ret;
if (!buffer || buflen < datalen)
return datalen;
if (datalen > BIG_KEY_FILE_THRESHOLD) {
- struct path *path = (struct path *)&key->payload.data2;
+ struct path *path = (struct path *)&key->payload.data[big_key_path];
struct file *file;
loff_t pos;
@@ -190,7 +204,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen)
ret = -EIO;
} else {
ret = datalen;
- if (copy_to_user(buffer, key->payload.data, datalen) != 0)
+ if (copy_to_user(buffer, key->payload.data[big_key_data],
+ datalen) != 0)
ret = -EFAULT;
}
diff --git a/kernel/security/keys/encrypted-keys/encrypted.c b/kernel/security/keys/encrypted-keys/encrypted.c
index 7bed4ad7c..696ccfa08 100644
--- a/kernel/security/keys/encrypted-keys/encrypted.c
+++ b/kernel/security/keys/encrypted-keys/encrypted.c
@@ -303,10 +303,10 @@ out:
*
* Use a user provided key to encrypt/decrypt an encrypted-key.
*/
-static struct key *request_user_key(const char *master_desc, u8 **master_key,
+static struct key *request_user_key(const char *master_desc, const u8 **master_key,
size_t *master_keylen)
{
- struct user_key_payload *upayload;
+ const struct user_key_payload *upayload;
struct key *ukey;
ukey = request_key(&key_type_user, master_desc, NULL);
@@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, u8 **master_key,
goto error;
down_read(&ukey->sem);
- upayload = ukey->payload.data;
+ upayload = user_key_payload(ukey);
*master_key = upayload->data;
*master_keylen = upayload->datalen;
error:
@@ -426,7 +426,7 @@ static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key,
}
static struct key *request_master_key(struct encrypted_key_payload *epayload,
- u8 **master_key, size_t *master_keylen)
+ const u8 **master_key, size_t *master_keylen)
{
struct key *mkey = NULL;
@@ -653,7 +653,7 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload,
{
struct key *mkey;
u8 derived_key[HASH_SIZE];
- u8 *master_key;
+ const u8 *master_key;
u8 *hmac;
const char *hex_encoded_data;
unsigned int encrypted_datalen;
@@ -837,7 +837,7 @@ static void encrypted_rcu_free(struct rcu_head *rcu)
*/
static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
{
- struct encrypted_key_payload *epayload = key->payload.data;
+ struct encrypted_key_payload *epayload = key->payload.data[0];
struct encrypted_key_payload *new_epayload;
char *buf;
char *new_master_desc = NULL;
@@ -845,6 +845,8 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
size_t datalen = prep->datalen;
int ret = 0;
+ if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ return -ENOKEY;
if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;
@@ -896,7 +898,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
{
struct encrypted_key_payload *epayload;
struct key *mkey;
- u8 *master_key;
+ const u8 *master_key;
size_t master_keylen;
char derived_key[HASH_SIZE];
char *ascii_buf;
@@ -957,13 +959,13 @@ out:
*/
static void encrypted_destroy(struct key *key)
{
- struct encrypted_key_payload *epayload = key->payload.data;
+ struct encrypted_key_payload *epayload = key->payload.data[0];
if (!epayload)
return;
memset(epayload->decrypted_data, 0, epayload->decrypted_datalen);
- kfree(key->payload.data);
+ kfree(key->payload.data[0]);
}
struct key_type key_type_encrypted = {
diff --git a/kernel/security/keys/encrypted-keys/encrypted.h b/kernel/security/keys/encrypted-keys/encrypted.h
index 8136a2d44..47802c0de 100644
--- a/kernel/security/keys/encrypted-keys/encrypted.h
+++ b/kernel/security/keys/encrypted-keys/encrypted.h
@@ -5,10 +5,10 @@
#if defined(CONFIG_TRUSTED_KEYS) || \
(defined(CONFIG_TRUSTED_KEYS_MODULE) && defined(CONFIG_ENCRYPTED_KEYS_MODULE))
extern struct key *request_trusted_key(const char *trusted_desc,
- u8 **master_key, size_t *master_keylen);
+ const u8 **master_key, size_t *master_keylen);
#else
static inline struct key *request_trusted_key(const char *trusted_desc,
- u8 **master_key,
+ const u8 **master_key,
size_t *master_keylen)
{
return ERR_PTR(-EOPNOTSUPP);
diff --git a/kernel/security/keys/encrypted-keys/masterkey_trusted.c b/kernel/security/keys/encrypted-keys/masterkey_trusted.c
index 013f7e5d3..b5b4812db 100644
--- a/kernel/security/keys/encrypted-keys/masterkey_trusted.c
+++ b/kernel/security/keys/encrypted-keys/masterkey_trusted.c
@@ -29,7 +29,7 @@
* data, trusted key type data is not visible decrypted from userspace.
*/
struct key *request_trusted_key(const char *trusted_desc,
- u8 **master_key, size_t *master_keylen)
+ const u8 **master_key, size_t *master_keylen)
{
struct trusted_key_payload *tpayload;
struct key *tkey;
@@ -39,7 +39,7 @@ struct key *request_trusted_key(const char *trusted_desc,
goto error;
down_read(&tkey->sem);
- tpayload = tkey->payload.data;
+ tpayload = tkey->payload.data[0];
*master_key = tpayload->key;
*master_keylen = tpayload->key_len;
error:
diff --git a/kernel/security/keys/gc.c b/kernel/security/keys/gc.c
index c7952375a..addf06039 100644
--- a/kernel/security/keys/gc.c
+++ b/kernel/security/keys/gc.c
@@ -134,6 +134,12 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
kdebug("- %u", key->serial);
key_check(key);
+ /* Throw away the key data if the key is instantiated */
+ if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
+ !test_bit(KEY_FLAG_NEGATIVE, &key->flags) &&
+ key->type->destroy)
+ key->type->destroy(key);
+
security_key_free(key);
/* deal with the user's key tracking and quota */
@@ -148,10 +154,6 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
atomic_dec(&key->user->nikeys);
- /* now throw away the key memory */
- if (key->type->destroy)
- key->type->destroy(key);
-
key_user_put(key->user);
kfree(key->description);
diff --git a/kernel/security/keys/key.c b/kernel/security/keys/key.c
index aee2ec5a1..ab7997ded 100644
--- a/kernel/security/keys/key.c
+++ b/kernel/security/keys/key.c
@@ -278,7 +278,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
key->index_key.desc_len = desclen;
key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
- if (!key->description)
+ if (!key->index_key.description)
goto no_memory_3;
atomic_set(&key->usage, 1);
@@ -554,7 +554,7 @@ int key_reject_and_link(struct key *key,
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
/* mark the key as being negatively instantiated */
atomic_inc(&key->user->nikeys);
- key->type_data.reject_error = -error;
+ key->reject_error = -error;
smp_wmb();
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
@@ -1046,14 +1046,14 @@ int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
ret = key_payload_reserve(key, prep->quotalen);
if (ret == 0) {
- key->type_data.p[0] = prep->type_data[0];
- key->type_data.p[1] = prep->type_data[1];
- rcu_assign_keypointer(key, prep->payload[0]);
- key->payload.data2[1] = prep->payload[1];
- prep->type_data[0] = NULL;
- prep->type_data[1] = NULL;
- prep->payload[0] = NULL;
- prep->payload[1] = NULL;
+ rcu_assign_keypointer(key, prep->payload.data[0]);
+ key->payload.data[1] = prep->payload.data[1];
+ key->payload.data[2] = prep->payload.data[2];
+ key->payload.data[3] = prep->payload.data[3];
+ prep->payload.data[0] = NULL;
+ prep->payload.data[1] = NULL;
+ prep->payload.data[2] = NULL;
+ prep->payload.data[3] = NULL;
}
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
diff --git a/kernel/security/keys/keyctl.c b/kernel/security/keys/keyctl.c
index 0b9ec78a7..1c3872aee 100644
--- a/kernel/security/keys/keyctl.c
+++ b/kernel/security/keys/keyctl.c
@@ -67,7 +67,6 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
char type[32], *description;
void *payload;
long ret;
- bool vm;
ret = -EINVAL;
if (plen > 1024 * 1024 - 1)
@@ -98,14 +97,12 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
/* pull the payload in if one was supplied */
payload = NULL;
- vm = false;
if (_payload) {
ret = -ENOMEM;
payload = kmalloc(plen, GFP_KERNEL | __GFP_NOWARN);
if (!payload) {
if (plen <= PAGE_SIZE)
goto error2;
- vm = true;
payload = vmalloc(plen);
if (!payload)
goto error2;
@@ -138,10 +135,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
key_ref_put(keyring_ref);
error3:
- if (!vm)
- kfree(payload);
- else
- vfree(payload);
+ kvfree(payload);
error2:
kfree(description);
error:
@@ -757,16 +751,16 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
/* the key is probably readable - now try to read it */
can_read_key:
- ret = key_validate(key);
- if (ret == 0) {
- ret = -EOPNOTSUPP;
- if (key->type->read) {
- /* read the data with the semaphore held (since we
- * might sleep) */
- down_read(&key->sem);
+ ret = -EOPNOTSUPP;
+ if (key->type->read) {
+ /* Read the data with the semaphore held (since we might sleep)
+ * to protect against the key being updated or revoked.
+ */
+ down_read(&key->sem);
+ ret = key_validate(key);
+ if (ret == 0)
ret = key->type->read(key, buffer, buflen);
- up_read(&key->sem);
- }
+ up_read(&key->sem);
}
error2:
@@ -1033,7 +1027,7 @@ long keyctl_instantiate_key_common(key_serial_t id,
if (!instkey)
goto error;
- rka = instkey->payload.data;
+ rka = instkey->payload.data[0];
if (rka->target_key->serial != id)
goto error;
@@ -1200,7 +1194,7 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
if (!instkey)
goto error;
- rka = instkey->payload.data;
+ rka = instkey->payload.data[0];
if (rka->target_key->serial != id)
goto error;
diff --git a/kernel/security/keys/keyring.c b/kernel/security/keys/keyring.c
index d33437007..f931ccfee 100644
--- a/kernel/security/keys/keyring.c
+++ b/kernel/security/keys/keyring.c
@@ -118,7 +118,7 @@ static void keyring_publish_name(struct key *keyring)
if (!keyring_name_hash[bucket].next)
INIT_LIST_HEAD(&keyring_name_hash[bucket]);
- list_add_tail(&keyring->type_data.link,
+ list_add_tail(&keyring->name_link,
&keyring_name_hash[bucket]);
write_unlock(&keyring_name_lock);
@@ -387,9 +387,9 @@ static void keyring_destroy(struct key *keyring)
if (keyring->description) {
write_lock(&keyring_name_lock);
- if (keyring->type_data.link.next != NULL &&
- !list_empty(&keyring->type_data.link))
- list_del(&keyring->type_data.link);
+ if (keyring->name_link.next != NULL &&
+ !list_empty(&keyring->name_link))
+ list_del(&keyring->name_link);
write_unlock(&keyring_name_lock);
}
@@ -572,7 +572,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
/* we set a different error code if we pass a negative key */
if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
smp_rmb();
- ctx->result = ERR_PTR(key->type_data.reject_error);
+ ctx->result = ERR_PTR(key->reject_error);
kleave(" = %d [neg]", ctx->skipped_ret);
goto skipped;
}
@@ -990,7 +990,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
* that's readable and that hasn't been revoked */
list_for_each_entry(keyring,
&keyring_name_hash[bucket],
- type_data.link
+ name_link
) {
if (!kuid_has_mapping(current_user_ns(), keyring->user->uid))
continue;
diff --git a/kernel/security/keys/process_keys.c b/kernel/security/keys/process_keys.c
index bd536cb22..e6d501728 100644
--- a/kernel/security/keys/process_keys.c
+++ b/kernel/security/keys/process_keys.c
@@ -457,7 +457,7 @@ key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
down_read(&cred->request_key_auth->sem);
if (key_validate(ctx->cred->request_key_auth) == 0) {
- rka = ctx->cred->request_key_auth->payload.data;
+ rka = ctx->cred->request_key_auth->payload.data[0];
ctx->cred = rka->cred;
key_ref = search_process_keyrings(ctx);
@@ -647,7 +647,7 @@ try_again:
key_ref = ERR_PTR(-EKEYREVOKED);
key = NULL;
} else {
- rka = ctx.cred->request_key_auth->payload.data;
+ rka = ctx.cred->request_key_auth->payload.data[0];
key = rka->dest_keyring;
__key_get(key);
}
@@ -794,6 +794,7 @@ long join_session_keyring(const char *name)
ret = PTR_ERR(keyring);
goto error2;
} else if (keyring == new->session_keyring) {
+ key_put(keyring);
ret = 0;
goto error2;
}
@@ -848,6 +849,7 @@ void key_change_session_keyring(struct callback_head *twork)
new->cap_inheritable = old->cap_inheritable;
new->cap_permitted = old->cap_permitted;
new->cap_effective = old->cap_effective;
+ new->cap_ambient = old->cap_ambient;
new->cap_bset = old->cap_bset;
new->jit_keyring = old->jit_keyring;
diff --git a/kernel/security/keys/request_key.c b/kernel/security/keys/request_key.c
index 486ef6fa3..c7a117c9a 100644
--- a/kernel/security/keys/request_key.c
+++ b/kernel/security/keys/request_key.c
@@ -271,7 +271,7 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)
if (cred->request_key_auth) {
authkey = cred->request_key_auth;
down_read(&authkey->sem);
- rka = authkey->payload.data;
+ rka = authkey->payload.data[0];
if (!test_bit(KEY_FLAG_REVOKED,
&authkey->flags))
dest_keyring =
@@ -440,6 +440,9 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
kenter("");
+ if (ctx->index_key.type == &key_type_keyring)
+ return ERR_PTR(-EPERM);
+
user = key_user_lookup(current_fsuid());
if (!user)
return ERR_PTR(-ENOMEM);
@@ -593,7 +596,7 @@ int wait_for_key_construction(struct key *key, bool intr)
return -ERESTARTSYS;
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
smp_rmb();
- return key->type_data.reject_error;
+ return key->reject_error;
}
return key_validate(key);
}
diff --git a/kernel/security/keys/request_key_auth.c b/kernel/security/keys/request_key_auth.c
index 5d672f758..4f0f112fe 100644
--- a/kernel/security/keys/request_key_auth.c
+++ b/kernel/security/keys/request_key_auth.c
@@ -59,7 +59,7 @@ static void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
static int request_key_auth_instantiate(struct key *key,
struct key_preparsed_payload *prep)
{
- key->payload.data = (struct request_key_auth *)prep->data;
+ key->payload.data[0] = (struct request_key_auth *)prep->data;
return 0;
}
@@ -69,7 +69,7 @@ static int request_key_auth_instantiate(struct key *key,
static void request_key_auth_describe(const struct key *key,
struct seq_file *m)
{
- struct request_key_auth *rka = key->payload.data;
+ struct request_key_auth *rka = key->payload.data[0];
seq_puts(m, "key:");
seq_puts(m, key->description);
@@ -84,7 +84,7 @@ static void request_key_auth_describe(const struct key *key,
static long request_key_auth_read(const struct key *key,
char __user *buffer, size_t buflen)
{
- struct request_key_auth *rka = key->payload.data;
+ struct request_key_auth *rka = key->payload.data[0];
size_t datalen;
long ret;
@@ -110,7 +110,7 @@ static long request_key_auth_read(const struct key *key,
*/
static void request_key_auth_revoke(struct key *key)
{
- struct request_key_auth *rka = key->payload.data;
+ struct request_key_auth *rka = key->payload.data[0];
kenter("{%d}", key->serial);
@@ -125,7 +125,7 @@ static void request_key_auth_revoke(struct key *key)
*/
static void request_key_auth_destroy(struct key *key)
{
- struct request_key_auth *rka = key->payload.data;
+ struct request_key_auth *rka = key->payload.data[0];
kenter("{%d}", key->serial);
@@ -179,7 +179,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags))
goto auth_key_revoked;
- irka = cred->request_key_auth->payload.data;
+ irka = cred->request_key_auth->payload.data[0];
rka->cred = get_cred(irka->cred);
rka->pid = irka->pid;
diff --git a/kernel/security/keys/trusted.c b/kernel/security/keys/trusted.c
index c0594cb07..16dec5318 100644
--- a/kernel/security/keys/trusted.c
+++ b/kernel/security/keys/trusted.c
@@ -862,12 +862,19 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p,
static struct trusted_key_options *trusted_options_alloc(void)
{
struct trusted_key_options *options;
+ int tpm2;
+
+ tpm2 = tpm_is_tpm2(TPM_ANY_NUM);
+ if (tpm2 < 0)
+ return NULL;
options = kzalloc(sizeof *options, GFP_KERNEL);
if (options) {
/* set any non-zero defaults */
options->keytype = SRK_keytype;
- options->keyhandle = SRKHANDLE;
+
+ if (!tpm2)
+ options->keyhandle = SRKHANDLE;
}
return options;
}
@@ -905,6 +912,11 @@ static int trusted_instantiate(struct key *key,
int ret = 0;
int key_cmd;
size_t key_len;
+ int tpm2;
+
+ tpm2 = tpm_is_tpm2(TPM_ANY_NUM);
+ if (tpm2 < 0)
+ return tpm2;
if (datalen <= 0 || datalen > 32767 || !prep->data)
return -EINVAL;
@@ -932,12 +944,20 @@ static int trusted_instantiate(struct key *key,
goto out;
}
+ if (!options->keyhandle) {
+ ret = -EINVAL;
+ goto out;
+ }
+
dump_payload(payload);
dump_options(options);
switch (key_cmd) {
case Opt_load:
- ret = key_unseal(payload, options);
+ if (tpm2)
+ ret = tpm_unseal_trusted(TPM_ANY_NUM, payload, options);
+ else
+ ret = key_unseal(payload, options);
dump_payload(payload);
dump_options(options);
if (ret < 0)
@@ -950,7 +970,10 @@ static int trusted_instantiate(struct key *key,
pr_info("trusted_key: key_create failed (%d)\n", ret);
goto out;
}
- ret = key_seal(payload, options);
+ if (tpm2)
+ ret = tpm_seal_trusted(TPM_ANY_NUM, payload, options);
+ else
+ ret = key_seal(payload, options);
if (ret < 0)
pr_info("trusted_key: key_seal failed (%d)\n", ret);
break;
@@ -984,13 +1007,16 @@ static void trusted_rcu_free(struct rcu_head *rcu)
*/
static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
{
- struct trusted_key_payload *p = key->payload.data;
+ struct trusted_key_payload *p;
struct trusted_key_payload *new_p;
struct trusted_key_options *new_o;
size_t datalen = prep->datalen;
char *datablob;
int ret = 0;
+ if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ return -ENOKEY;
+ p = key->payload.data[0];
if (!p->migratable)
return -EPERM;
if (datalen <= 0 || datalen > 32767 || !prep->data)
@@ -1018,6 +1044,13 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep)
kfree(new_p);
goto out;
}
+
+ if (!new_o->keyhandle) {
+ ret = -EINVAL;
+ kfree(new_p);
+ goto out;
+ }
+
/* copy old key values, and reseal with new pcrs */
new_p->migratable = p->migratable;
new_p->key_len = p->key_len;
@@ -1084,12 +1117,12 @@ static long trusted_read(const struct key *key, char __user *buffer,
*/
static void trusted_destroy(struct key *key)
{
- struct trusted_key_payload *p = key->payload.data;
+ struct trusted_key_payload *p = key->payload.data[0];
if (!p)
return;
memset(p->key, 0, p->key_len);
- kfree(key->payload.data);
+ kfree(key->payload.data[0]);
}
struct key_type key_type_trusted = {
diff --git a/kernel/security/keys/trusted.h b/kernel/security/keys/trusted.h
index 3249fbd2b..ff001a5dc 100644
--- a/kernel/security/keys/trusted.h
+++ b/kernel/security/keys/trusted.h
@@ -2,7 +2,6 @@
#define __TRUSTED_KEY_H
/* implementation specific TPM constants */
-#define MAX_PCRINFO_SIZE 64
#define MAX_BUF_SIZE 512
#define TPM_GETRANDOM_SIZE 14
#define TPM_OSAP_SIZE 36
@@ -36,16 +35,6 @@ enum {
SRK_keytype = 4
};
-struct trusted_key_options {
- uint16_t keytype;
- uint32_t keyhandle;
- unsigned char keyauth[SHA1_DIGEST_SIZE];
- unsigned char blobauth[SHA1_DIGEST_SIZE];
- uint32_t pcrinfo_len;
- unsigned char pcrinfo[MAX_PCRINFO_SIZE];
- int pcrlock;
-};
-
#define TPM_DEBUG 0
#if TPM_DEBUG
diff --git a/kernel/security/keys/user_defined.c b/kernel/security/keys/user_defined.c
index 36b47bbd3..8705d79b2 100644
--- a/kernel/security/keys/user_defined.c
+++ b/kernel/security/keys/user_defined.c
@@ -74,7 +74,7 @@ int user_preparse(struct key_preparsed_payload *prep)
/* attach the data */
prep->quotalen = datalen;
- prep->payload[0] = upayload;
+ prep->payload.data[0] = upayload;
upayload->datalen = datalen;
memcpy(upayload->data, prep->data, datalen);
return 0;
@@ -86,7 +86,7 @@ EXPORT_SYMBOL_GPL(user_preparse);
*/
void user_free_preparse(struct key_preparsed_payload *prep)
{
- kfree(prep->payload[0]);
+ kfree(prep->payload.data[0]);
}
EXPORT_SYMBOL_GPL(user_free_preparse);
@@ -120,7 +120,10 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
if (ret == 0) {
/* attach the new data, displacing the old */
- zap = key->payload.data;
+ if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+ zap = key->payload.data[0];
+ else
+ zap = NULL;
rcu_assign_keypointer(key, upayload);
key->expiry = 0;
}
@@ -140,7 +143,7 @@ EXPORT_SYMBOL_GPL(user_update);
*/
void user_revoke(struct key *key)
{
- struct user_key_payload *upayload = key->payload.data;
+ struct user_key_payload *upayload = key->payload.data[0];
/* clear the quota */
key_payload_reserve(key, 0);
@@ -158,7 +161,7 @@ EXPORT_SYMBOL(user_revoke);
*/
void user_destroy(struct key *key)
{
- struct user_key_payload *upayload = key->payload.data;
+ struct user_key_payload *upayload = key->payload.data[0];
kfree(upayload);
}
@@ -183,10 +186,10 @@ EXPORT_SYMBOL_GPL(user_describe);
*/
long user_read(const struct key *key, char __user *buffer, size_t buflen)
{
- struct user_key_payload *upayload;
+ const struct user_key_payload *upayload;
long ret;
- upayload = rcu_dereference_key(key);
+ upayload = user_key_payload(key);
ret = upayload->datalen;
/* we can return the data as is */
diff --git a/kernel/security/lsm_audit.c b/kernel/security/lsm_audit.c
index 1d34277dc..cccbf3068 100644
--- a/kernel/security/lsm_audit.c
+++ b/kernel/security/lsm_audit.c
@@ -245,6 +245,21 @@ static void dump_common_audit_data(struct audit_buffer *ab,
}
break;
}
+ case LSM_AUDIT_DATA_IOCTL_OP: {
+ struct inode *inode;
+
+ audit_log_d_path(ab, " path=", &a->u.op->path);
+
+ inode = a->u.op->path.dentry->d_inode;
+ if (inode) {
+ audit_log_format(ab, " dev=");
+ audit_log_untrustedstring(ab, inode->i_sb->s_id);
+ audit_log_format(ab, " ino=%lu", inode->i_ino);
+ }
+
+ audit_log_format(ab, " ioctlcmd=%hx", a->u.op->cmd);
+ break;
+ }
case LSM_AUDIT_DATA_DENTRY: {
struct inode *inode;
@@ -282,7 +297,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
pid_t pid = task_pid_nr(tsk);
if (pid) {
char comm[sizeof(tsk->comm)];
- audit_log_format(ab, " pid=%d comm=", pid);
+ audit_log_format(ab, " opid=%d ocomm=", pid);
audit_log_untrustedstring(ab,
memcpy(comm, tsk->comm, sizeof(comm)));
}
diff --git a/kernel/security/security.c b/kernel/security/security.c
index 8e9b1f4b9..46f405ce6 100644
--- a/kernel/security/security.c
+++ b/kernel/security/security.c
@@ -16,7 +16,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/integrity.h>
#include <linux/ima.h>
#include <linux/evm.h>
@@ -29,24 +29,13 @@
#define MAX_LSM_EVM_XATTR 2
+/* Maximum number of letters for an LSM name string */
+#define SECURITY_NAME_MAX 10
+
/* Boot-time LSM user choice */
static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
CONFIG_DEFAULT_SECURITY;
-static struct security_operations *security_ops;
-static struct security_operations default_security_ops = {
- .name = "default",
-};
-
-static inline int __init verify(struct security_operations *ops)
-{
- /* verify the security_operations structure exists */
- if (!ops)
- return -EINVAL;
- security_fixup_ops(ops);
- return 0;
-}
-
static void __init do_security_initcalls(void)
{
initcall_t *call;
@@ -64,20 +53,22 @@ static void __init do_security_initcalls(void)
*/
int __init security_init(void)
{
- printk(KERN_INFO "Security Framework initialized\n");
+ pr_info("Security Framework initialized\n");
+
+ /*
+ * Load minor LSMs, with the capability module always first.
+ */
+ capability_add_hooks();
+ yama_add_hooks();
- security_fixup_ops(&default_security_ops);
- security_ops = &default_security_ops;
+ /*
+ * Load all the remaining security modules.
+ */
do_security_initcalls();
return 0;
}
-void reset_security_ops(void)
-{
- security_ops = &default_security_ops;
-}
-
/* Save user chosen LSM */
static int __init choose_lsm(char *str)
{
@@ -88,7 +79,7 @@ __setup("security=", choose_lsm);
/**
* security_module_enable - Load given security module on boot ?
- * @ops: a pointer to the struct security_operations that is to be checked.
+ * @module: the name of the module
*
* Each LSM must pass this method before registering its own operations
* to avoid security registration races. This method may also be used
@@ -100,84 +91,76 @@ __setup("security=", choose_lsm);
* choose an alternate LSM at boot time.
* Otherwise, return false.
*/
-int __init security_module_enable(struct security_operations *ops)
+int __init security_module_enable(const char *module)
{
- return !strcmp(ops->name, chosen_lsm);
+ return !strcmp(module, chosen_lsm);
}
-/**
- * register_security - registers a security framework with the kernel
- * @ops: a pointer to the struct security_options that is to be registered
+/*
+ * Hook list operation macros.
*
- * This function allows a security module to register itself with the
- * kernel security subsystem. Some rudimentary checking is done on the @ops
- * value passed to this function. You'll need to check first if your LSM
- * is allowed to register its @ops by calling security_module_enable(@ops).
+ * call_void_hook:
+ * This is a hook that does not return a value.
*
- * If there is already a security module registered with the kernel,
- * an error will be returned. Otherwise %0 is returned on success.
+ * call_int_hook:
+ * This is a hook that returns a value.
*/
-int __init register_security(struct security_operations *ops)
-{
- if (verify(ops)) {
- printk(KERN_DEBUG "%s could not verify "
- "security_operations structure.\n", __func__);
- return -EINVAL;
- }
-
- if (security_ops != &default_security_ops)
- return -EAGAIN;
- security_ops = ops;
-
- return 0;
-}
+#define call_void_hook(FUNC, ...) \
+ do { \
+ struct security_hook_list *P; \
+ \
+ list_for_each_entry(P, &security_hook_heads.FUNC, list) \
+ P->hook.FUNC(__VA_ARGS__); \
+ } while (0)
+
+#define call_int_hook(FUNC, IRC, ...) ({ \
+ int RC = IRC; \
+ do { \
+ struct security_hook_list *P; \
+ \
+ list_for_each_entry(P, &security_hook_heads.FUNC, list) { \
+ RC = P->hook.FUNC(__VA_ARGS__); \
+ if (RC != 0) \
+ break; \
+ } \
+ } while (0); \
+ RC; \
+})
/* Security operations */
int security_binder_set_context_mgr(struct task_struct *mgr)
{
- return security_ops->binder_set_context_mgr(mgr);
+ return call_int_hook(binder_set_context_mgr, 0, mgr);
}
int security_binder_transaction(struct task_struct *from,
struct task_struct *to)
{
- return security_ops->binder_transaction(from, to);
+ return call_int_hook(binder_transaction, 0, from, to);
}
int security_binder_transfer_binder(struct task_struct *from,
struct task_struct *to)
{
- return security_ops->binder_transfer_binder(from, to);
+ return call_int_hook(binder_transfer_binder, 0, from, to);
}
int security_binder_transfer_file(struct task_struct *from,
struct task_struct *to, struct file *file)
{
- return security_ops->binder_transfer_file(from, to, file);
+ return call_int_hook(binder_transfer_file, 0, from, to, file);
}
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
-#ifdef CONFIG_SECURITY_YAMA_STACKED
- int rc;
- rc = yama_ptrace_access_check(child, mode);
- if (rc)
- return rc;
-#endif
- return security_ops->ptrace_access_check(child, mode);
+ return call_int_hook(ptrace_access_check, 0, child, mode);
}
int security_ptrace_traceme(struct task_struct *parent)
{
-#ifdef CONFIG_SECURITY_YAMA_STACKED
- int rc;
- rc = yama_ptrace_traceme(parent);
- if (rc)
- return rc;
-#endif
- return security_ops->ptrace_traceme(parent);
+ return call_int_hook(ptrace_traceme, 0, parent);
}
int security_capget(struct task_struct *target,
@@ -185,7 +168,8 @@ int security_capget(struct task_struct *target,
kernel_cap_t *inheritable,
kernel_cap_t *permitted)
{
- return security_ops->capget(target, effective, inheritable, permitted);
+ return call_int_hook(capget, 0, target,
+ effective, inheritable, permitted);
}
int security_capset(struct cred *new, const struct cred *old,
@@ -193,57 +177,75 @@ int security_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
- return security_ops->capset(new, old,
- effective, inheritable, permitted);
+ return call_int_hook(capset, 0, new, old,
+ effective, inheritable, permitted);
}
int security_capable(const struct cred *cred, struct user_namespace *ns,
int cap)
{
- return security_ops->capable(cred, ns, cap, SECURITY_CAP_AUDIT);
+ return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_AUDIT);
}
int security_capable_noaudit(const struct cred *cred, struct user_namespace *ns,
int cap)
{
- return security_ops->capable(cred, ns, cap, SECURITY_CAP_NOAUDIT);
+ return call_int_hook(capable, 0, cred, ns, cap, SECURITY_CAP_NOAUDIT);
}
int security_quotactl(int cmds, int type, int id, struct super_block *sb)
{
- return security_ops->quotactl(cmds, type, id, sb);
+ return call_int_hook(quotactl, 0, cmds, type, id, sb);
}
int security_quota_on(struct dentry *dentry)
{
- return security_ops->quota_on(dentry);
+ return call_int_hook(quota_on, 0, dentry);
}
int security_syslog(int type)
{
- return security_ops->syslog(type);
+ return call_int_hook(syslog, 0, type);
}
int security_settime(const struct timespec *ts, const struct timezone *tz)
{
- return security_ops->settime(ts, tz);
+ return call_int_hook(settime, 0, ts, tz);
}
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
- return security_ops->vm_enough_memory(mm, pages);
+ struct security_hook_list *hp;
+ int cap_sys_admin = 1;
+ int rc;
+
+ /*
+ * The module will respond with a positive value if
+ * it thinks the __vm_enough_memory() call should be
+ * made with the cap_sys_admin set. If all of the modules
+ * agree that it should be set it will. If any module
+ * thinks it should not be set it won't.
+ */
+ list_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
+ rc = hp->hook.vm_enough_memory(mm, pages);
+ if (rc <= 0) {
+ cap_sys_admin = 0;
+ break;
+ }
+ }
+ return __vm_enough_memory(mm, pages, cap_sys_admin);
}
int security_bprm_set_creds(struct linux_binprm *bprm)
{
- return security_ops->bprm_set_creds(bprm);
+ return call_int_hook(bprm_set_creds, 0, bprm);
}
int security_bprm_check(struct linux_binprm *bprm)
{
int ret;
- ret = security_ops->bprm_check_security(bprm);
+ ret = call_int_hook(bprm_check_security, 0, bprm);
if (ret)
return ret;
return ima_bprm_check(bprm);
@@ -251,69 +253,69 @@ int security_bprm_check(struct linux_binprm *bprm)
void security_bprm_committing_creds(struct linux_binprm *bprm)
{
- security_ops->bprm_committing_creds(bprm);
+ call_void_hook(bprm_committing_creds, bprm);
}
void security_bprm_committed_creds(struct linux_binprm *bprm)
{
- security_ops->bprm_committed_creds(bprm);
+ call_void_hook(bprm_committed_creds, bprm);
}
int security_bprm_secureexec(struct linux_binprm *bprm)
{
- return security_ops->bprm_secureexec(bprm);
+ return call_int_hook(bprm_secureexec, 0, bprm);
}
int security_sb_alloc(struct super_block *sb)
{
- return security_ops->sb_alloc_security(sb);
+ return call_int_hook(sb_alloc_security, 0, sb);
}
void security_sb_free(struct super_block *sb)
{
- security_ops->sb_free_security(sb);
+ call_void_hook(sb_free_security, sb);
}
int security_sb_copy_data(char *orig, char *copy)
{
- return security_ops->sb_copy_data(orig, copy);
+ return call_int_hook(sb_copy_data, 0, orig, copy);
}
EXPORT_SYMBOL(security_sb_copy_data);
int security_sb_remount(struct super_block *sb, void *data)
{
- return security_ops->sb_remount(sb, data);
+ return call_int_hook(sb_remount, 0, sb, data);
}
int security_sb_kern_mount(struct super_block *sb, int flags, void *data)
{
- return security_ops->sb_kern_mount(sb, flags, data);
+ return call_int_hook(sb_kern_mount, 0, sb, flags, data);
}
int security_sb_show_options(struct seq_file *m, struct super_block *sb)
{
- return security_ops->sb_show_options(m, sb);
+ return call_int_hook(sb_show_options, 0, m, sb);
}
int security_sb_statfs(struct dentry *dentry)
{
- return security_ops->sb_statfs(dentry);
+ return call_int_hook(sb_statfs, 0, dentry);
}
int security_sb_mount(const char *dev_name, struct path *path,
const char *type, unsigned long flags, void *data)
{
- return security_ops->sb_mount(dev_name, path, type, flags, data);
+ return call_int_hook(sb_mount, 0, dev_name, path, type, flags, data);
}
int security_sb_umount(struct vfsmount *mnt, int flags)
{
- return security_ops->sb_umount(mnt, flags);
+ return call_int_hook(sb_umount, 0, mnt, flags);
}
int security_sb_pivotroot(struct path *old_path, struct path *new_path)
{
- return security_ops->sb_pivotroot(old_path, new_path);
+ return call_int_hook(sb_pivotroot, 0, old_path, new_path);
}
int security_sb_set_mnt_opts(struct super_block *sb,
@@ -321,42 +323,43 @@ int security_sb_set_mnt_opts(struct super_block *sb,
unsigned long kern_flags,
unsigned long *set_kern_flags)
{
- return security_ops->sb_set_mnt_opts(sb, opts, kern_flags,
- set_kern_flags);
+ return call_int_hook(sb_set_mnt_opts,
+ opts->num_mnt_opts ? -EOPNOTSUPP : 0, sb,
+ opts, kern_flags, set_kern_flags);
}
EXPORT_SYMBOL(security_sb_set_mnt_opts);
int security_sb_clone_mnt_opts(const struct super_block *oldsb,
struct super_block *newsb)
{
- return security_ops->sb_clone_mnt_opts(oldsb, newsb);
+ return call_int_hook(sb_clone_mnt_opts, 0, oldsb, newsb);
}
EXPORT_SYMBOL(security_sb_clone_mnt_opts);
int security_sb_parse_opts_str(char *options, struct security_mnt_opts *opts)
{
- return security_ops->sb_parse_opts_str(options, opts);
+ return call_int_hook(sb_parse_opts_str, 0, options, opts);
}
EXPORT_SYMBOL(security_sb_parse_opts_str);
int security_inode_alloc(struct inode *inode)
{
inode->i_security = NULL;
- return security_ops->inode_alloc_security(inode);
+ return call_int_hook(inode_alloc_security, 0, inode);
}
void security_inode_free(struct inode *inode)
{
integrity_inode_free(inode);
- security_ops->inode_free_security(inode);
+ call_void_hook(inode_free_security, inode);
}
int security_dentry_init_security(struct dentry *dentry, int mode,
struct qstr *name, void **ctx,
u32 *ctxlen)
{
- return security_ops->dentry_init_security(dentry, mode, name,
- ctx, ctxlen);
+ return call_int_hook(dentry_init_security, -EOPNOTSUPP, dentry, mode,
+ name, ctx, ctxlen);
}
EXPORT_SYMBOL(security_dentry_init_security);
@@ -372,11 +375,11 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
return 0;
if (!initxattrs)
- return security_ops->inode_init_security(inode, dir, qstr,
- NULL, NULL, NULL);
+ return call_int_hook(inode_init_security, -EOPNOTSUPP, inode,
+ dir, qstr, NULL, NULL, NULL);
memset(new_xattrs, 0, sizeof(new_xattrs));
lsm_xattr = new_xattrs;
- ret = security_ops->inode_init_security(inode, dir, qstr,
+ ret = call_int_hook(inode_init_security, -EOPNOTSUPP, inode, dir, qstr,
&lsm_xattr->name,
&lsm_xattr->value,
&lsm_xattr->value_len);
@@ -401,8 +404,8 @@ int security_old_inode_init_security(struct inode *inode, struct inode *dir,
{
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
- return security_ops->inode_init_security(inode, dir, qstr, name, value,
- len);
+ return call_int_hook(inode_init_security, -EOPNOTSUPP, inode, dir,
+ qstr, name, value, len);
}
EXPORT_SYMBOL(security_old_inode_init_security);
@@ -412,7 +415,7 @@ int security_path_mknod(struct path *dir, struct dentry *dentry, umode_t mode,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return security_ops->path_mknod(dir, dentry, mode, dev);
+ return call_int_hook(path_mknod, 0, dir, dentry, mode, dev);
}
EXPORT_SYMBOL(security_path_mknod);
@@ -420,7 +423,7 @@ int security_path_mkdir(struct path *dir, struct dentry *dentry, umode_t mode)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return security_ops->path_mkdir(dir, dentry, mode);
+ return call_int_hook(path_mkdir, 0, dir, dentry, mode);
}
EXPORT_SYMBOL(security_path_mkdir);
@@ -428,14 +431,14 @@ int security_path_rmdir(struct path *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return security_ops->path_rmdir(dir, dentry);
+ return call_int_hook(path_rmdir, 0, dir, dentry);
}
int security_path_unlink(struct path *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return security_ops->path_unlink(dir, dentry);
+ return call_int_hook(path_unlink, 0, dir, dentry);
}
EXPORT_SYMBOL(security_path_unlink);
@@ -444,7 +447,7 @@ int security_path_symlink(struct path *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
return 0;
- return security_ops->path_symlink(dir, dentry, old_name);
+ return call_int_hook(path_symlink, 0, dir, dentry, old_name);
}
int security_path_link(struct dentry *old_dentry, struct path *new_dir,
@@ -452,7 +455,7 @@ int security_path_link(struct dentry *old_dentry, struct path *new_dir,
{
if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
return 0;
- return security_ops->path_link(old_dentry, new_dir, new_dentry);
+ return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry);
}
int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
@@ -464,14 +467,14 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry,
return 0;
if (flags & RENAME_EXCHANGE) {
- int err = security_ops->path_rename(new_dir, new_dentry,
- old_dir, old_dentry);
+ int err = call_int_hook(path_rename, 0, new_dir, new_dentry,
+ old_dir, old_dentry);
if (err)
return err;
}
- return security_ops->path_rename(old_dir, old_dentry, new_dir,
- new_dentry);
+ return call_int_hook(path_rename, 0, old_dir, old_dentry, new_dir,
+ new_dentry);
}
EXPORT_SYMBOL(security_path_rename);
@@ -479,26 +482,26 @@ int security_path_truncate(struct path *path)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return security_ops->path_truncate(path);
+ return call_int_hook(path_truncate, 0, path);
}
int security_path_chmod(struct path *path, umode_t mode)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return security_ops->path_chmod(path, mode);
+ return call_int_hook(path_chmod, 0, path, mode);
}
int security_path_chown(struct path *path, kuid_t uid, kgid_t gid)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return security_ops->path_chown(path, uid, gid);
+ return call_int_hook(path_chown, 0, path, uid, gid);
}
int security_path_chroot(struct path *path)
{
- return security_ops->path_chroot(path);
+ return call_int_hook(path_chroot, 0, path);
}
#endif
@@ -506,7 +509,7 @@ int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_create(dir, dentry, mode);
+ return call_int_hook(inode_create, 0, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_create);
@@ -515,14 +518,14 @@ int security_inode_link(struct dentry *old_dentry, struct inode *dir,
{
if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
return 0;
- return security_ops->inode_link(old_dentry, dir, new_dentry);
+ return call_int_hook(inode_link, 0, old_dentry, dir, new_dentry);
}
int security_inode_unlink(struct inode *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return security_ops->inode_unlink(dir, dentry);
+ return call_int_hook(inode_unlink, 0, dir, dentry);
}
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
@@ -530,14 +533,14 @@ int security_inode_symlink(struct inode *dir, struct dentry *dentry,
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_symlink(dir, dentry, old_name);
+ return call_int_hook(inode_symlink, 0, dir, dentry, old_name);
}
int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_mkdir(dir, dentry, mode);
+ return call_int_hook(inode_mkdir, 0, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_mkdir);
@@ -545,14 +548,14 @@ int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return security_ops->inode_rmdir(dir, dentry);
+ return call_int_hook(inode_rmdir, 0, dir, dentry);
}
int security_inode_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_mknod(dir, dentry, mode, dev);
+ return call_int_hook(inode_mknod, 0, dir, dentry, mode, dev);
}
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -564,13 +567,13 @@ int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
return 0;
if (flags & RENAME_EXCHANGE) {
- int err = security_ops->inode_rename(new_dir, new_dentry,
+ int err = call_int_hook(inode_rename, 0, new_dir, new_dentry,
old_dir, old_dentry);
if (err)
return err;
}
- return security_ops->inode_rename(old_dir, old_dentry,
+ return call_int_hook(inode_rename, 0, old_dir, old_dentry,
new_dir, new_dentry);
}
@@ -578,21 +581,22 @@ int security_inode_readlink(struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return security_ops->inode_readlink(dentry);
+ return call_int_hook(inode_readlink, 0, dentry);
}
-int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
+int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
+ bool rcu)
{
- if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
+ if (unlikely(IS_PRIVATE(inode)))
return 0;
- return security_ops->inode_follow_link(dentry, nd);
+ return call_int_hook(inode_follow_link, 0, dentry, inode, rcu);
}
int security_inode_permission(struct inode *inode, int mask)
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return security_ops->inode_permission(inode, mask);
+ return call_int_hook(inode_permission, 0, inode, mask);
}
int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
@@ -601,7 +605,7 @@ int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = security_ops->inode_setattr(dentry, attr);
+ ret = call_int_hook(inode_setattr, 0, dentry, attr);
if (ret)
return ret;
return evm_inode_setattr(dentry, attr);
@@ -612,7 +616,7 @@ int security_inode_getattr(const struct path *path)
{
if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
return 0;
- return security_ops->inode_getattr(path);
+ return call_int_hook(inode_getattr, 0, path);
}
int security_inode_setxattr(struct dentry *dentry, const char *name,
@@ -622,7 +626,15 @@ int security_inode_setxattr(struct dentry *dentry, const char *name,
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+ /*
+ * SELinux and Smack integrate the cap call,
+ * so assume that all LSMs supplying this call do so.
+ */
+ ret = call_int_hook(inode_setxattr, 1, dentry, name, value, size,
+ flags);
+
+ if (ret == 1)
+ ret = cap_inode_setxattr(dentry, name, value, size, flags);
if (ret)
return ret;
ret = ima_inode_setxattr(dentry, name, value, size);
@@ -636,7 +648,7 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name,
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return;
- security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+ call_void_hook(inode_post_setxattr, dentry, name, value, size, flags);
evm_inode_post_setxattr(dentry, name, value, size);
}
@@ -644,14 +656,14 @@ int security_inode_getxattr(struct dentry *dentry, const char *name)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return security_ops->inode_getxattr(dentry, name);
+ return call_int_hook(inode_getxattr, 0, dentry, name);
}
int security_inode_listxattr(struct dentry *dentry)
{
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- return security_ops->inode_listxattr(dentry);
+ return call_int_hook(inode_listxattr, 0, dentry);
}
int security_inode_removexattr(struct dentry *dentry, const char *name)
@@ -660,7 +672,13 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
return 0;
- ret = security_ops->inode_removexattr(dentry, name);
+ /*
+ * SELinux and Smack integrate the cap call,
+ * so assume that all LSMs supplying this call do so.
+ */
+ ret = call_int_hook(inode_removexattr, 1, dentry, name);
+ if (ret == 1)
+ ret = cap_inode_removexattr(dentry, name);
if (ret)
return ret;
ret = ima_inode_removexattr(dentry, name);
@@ -671,46 +689,48 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
int security_inode_need_killpriv(struct dentry *dentry)
{
- return security_ops->inode_need_killpriv(dentry);
+ return call_int_hook(inode_need_killpriv, 0, dentry);
}
int security_inode_killpriv(struct dentry *dentry)
{
- return security_ops->inode_killpriv(dentry);
+ return call_int_hook(inode_killpriv, 0, dentry);
}
int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
{
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
- return security_ops->inode_getsecurity(inode, name, buffer, alloc);
+ return call_int_hook(inode_getsecurity, -EOPNOTSUPP, inode, name,
+ buffer, alloc);
}
int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
{
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
- return security_ops->inode_setsecurity(inode, name, value, size, flags);
+ return call_int_hook(inode_setsecurity, -EOPNOTSUPP, inode, name,
+ value, size, flags);
}
int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
{
if (unlikely(IS_PRIVATE(inode)))
return 0;
- return security_ops->inode_listsecurity(inode, buffer, buffer_size);
+ return call_int_hook(inode_listsecurity, 0, inode, buffer, buffer_size);
}
EXPORT_SYMBOL(security_inode_listsecurity);
void security_inode_getsecid(const struct inode *inode, u32 *secid)
{
- security_ops->inode_getsecid(inode, secid);
+ call_void_hook(inode_getsecid, inode, secid);
}
int security_file_permission(struct file *file, int mask)
{
int ret;
- ret = security_ops->file_permission(file, mask);
+ ret = call_int_hook(file_permission, 0, file, mask);
if (ret)
return ret;
@@ -719,17 +739,17 @@ int security_file_permission(struct file *file, int mask)
int security_file_alloc(struct file *file)
{
- return security_ops->file_alloc_security(file);
+ return call_int_hook(file_alloc_security, 0, file);
}
void security_file_free(struct file *file)
{
- security_ops->file_free_security(file);
+ call_void_hook(file_free_security, file);
}
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return security_ops->file_ioctl(file, cmd, arg);
+ return call_int_hook(file_ioctl, 0, file, cmd, arg);
}
static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
@@ -751,7 +771,7 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot)
* ditto if it's not on noexec mount, except that on !MMU we need
* NOMMU_MAP_EXEC (== VM_MAYEXEC) in this case
*/
- if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) {
+ if (!path_noexec(&file->f_path)) {
#ifndef CONFIG_MMU
if (file->f_op->mmap_capabilities) {
unsigned caps = file->f_op->mmap_capabilities(file);
@@ -769,7 +789,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
unsigned long flags)
{
int ret;
- ret = security_ops->mmap_file(file, prot,
+ ret = call_int_hook(mmap_file, 0, file, prot,
mmap_prot(file, prot), flags);
if (ret)
return ret;
@@ -778,46 +798,46 @@ int security_mmap_file(struct file *file, unsigned long prot,
int security_mmap_addr(unsigned long addr)
{
- return security_ops->mmap_addr(addr);
+ return call_int_hook(mmap_addr, 0, addr);
}
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
unsigned long prot)
{
- return security_ops->file_mprotect(vma, reqprot, prot);
+ return call_int_hook(file_mprotect, 0, vma, reqprot, prot);
}
int security_file_lock(struct file *file, unsigned int cmd)
{
- return security_ops->file_lock(file, cmd);
+ return call_int_hook(file_lock, 0, file, cmd);
}
int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
{
- return security_ops->file_fcntl(file, cmd, arg);
+ return call_int_hook(file_fcntl, 0, file, cmd, arg);
}
void security_file_set_fowner(struct file *file)
{
- security_ops->file_set_fowner(file);
+ call_void_hook(file_set_fowner, file);
}
int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig)
{
- return security_ops->file_send_sigiotask(tsk, fown, sig);
+ return call_int_hook(file_send_sigiotask, 0, tsk, fown, sig);
}
int security_file_receive(struct file *file)
{
- return security_ops->file_receive(file);
+ return call_int_hook(file_receive, 0, file);
}
int security_file_open(struct file *file, const struct cred *cred)
{
int ret;
- ret = security_ops->file_open(file, cred);
+ ret = call_int_hook(file_open, 0, file, cred);
if (ret)
return ret;
@@ -826,52 +846,49 @@ int security_file_open(struct file *file, const struct cred *cred)
int security_task_create(unsigned long clone_flags)
{
- return security_ops->task_create(clone_flags);
+ return call_int_hook(task_create, 0, clone_flags);
}
void security_task_free(struct task_struct *task)
{
-#ifdef CONFIG_SECURITY_YAMA_STACKED
- yama_task_free(task);
-#endif
- security_ops->task_free(task);
+ call_void_hook(task_free, task);
}
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
- return security_ops->cred_alloc_blank(cred, gfp);
+ return call_int_hook(cred_alloc_blank, 0, cred, gfp);
}
void security_cred_free(struct cred *cred)
{
- security_ops->cred_free(cred);
+ call_void_hook(cred_free, cred);
}
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
{
- return security_ops->cred_prepare(new, old, gfp);
+ return call_int_hook(cred_prepare, 0, new, old, gfp);
}
void security_transfer_creds(struct cred *new, const struct cred *old)
{
- security_ops->cred_transfer(new, old);
+ call_void_hook(cred_transfer, new, old);
}
int security_kernel_act_as(struct cred *new, u32 secid)
{
- return security_ops->kernel_act_as(new, secid);
+ return call_int_hook(kernel_act_as, 0, new, secid);
}
int security_kernel_create_files_as(struct cred *new, struct inode *inode)
{
- return security_ops->kernel_create_files_as(new, inode);
+ return call_int_hook(kernel_create_files_as, 0, new, inode);
}
int security_kernel_fw_from_file(struct file *file, char *buf, size_t size)
{
int ret;
- ret = security_ops->kernel_fw_from_file(file, buf, size);
+ ret = call_int_hook(kernel_fw_from_file, 0, file, buf, size);
if (ret)
return ret;
return ima_fw_from_file(file, buf, size);
@@ -880,14 +897,14 @@ EXPORT_SYMBOL_GPL(security_kernel_fw_from_file);
int security_kernel_module_request(char *kmod_name)
{
- return security_ops->kernel_module_request(kmod_name);
+ return call_int_hook(kernel_module_request, 0, kmod_name);
}
int security_kernel_module_from_file(struct file *file)
{
int ret;
- ret = security_ops->kernel_module_from_file(file);
+ ret = call_int_hook(kernel_module_from_file, 0, file);
if (ret)
return ret;
return ima_module_check(file);
@@ -896,259 +913,269 @@ int security_kernel_module_from_file(struct file *file)
int security_task_fix_setuid(struct cred *new, const struct cred *old,
int flags)
{
- return security_ops->task_fix_setuid(new, old, flags);
+ return call_int_hook(task_fix_setuid, 0, new, old, flags);
}
int security_task_setpgid(struct task_struct *p, pid_t pgid)
{
- return security_ops->task_setpgid(p, pgid);
+ return call_int_hook(task_setpgid, 0, p, pgid);
}
int security_task_getpgid(struct task_struct *p)
{
- return security_ops->task_getpgid(p);
+ return call_int_hook(task_getpgid, 0, p);
}
int security_task_getsid(struct task_struct *p)
{
- return security_ops->task_getsid(p);
+ return call_int_hook(task_getsid, 0, p);
}
void security_task_getsecid(struct task_struct *p, u32 *secid)
{
- security_ops->task_getsecid(p, secid);
+ *secid = 0;
+ call_void_hook(task_getsecid, p, secid);
}
EXPORT_SYMBOL(security_task_getsecid);
int security_task_setnice(struct task_struct *p, int nice)
{
- return security_ops->task_setnice(p, nice);
+ return call_int_hook(task_setnice, 0, p, nice);
}
int security_task_setioprio(struct task_struct *p, int ioprio)
{
- return security_ops->task_setioprio(p, ioprio);
+ return call_int_hook(task_setioprio, 0, p, ioprio);
}
int security_task_getioprio(struct task_struct *p)
{
- return security_ops->task_getioprio(p);
+ return call_int_hook(task_getioprio, 0, p);
}
int security_task_setrlimit(struct task_struct *p, unsigned int resource,
struct rlimit *new_rlim)
{
- return security_ops->task_setrlimit(p, resource, new_rlim);
+ return call_int_hook(task_setrlimit, 0, p, resource, new_rlim);
}
int security_task_setscheduler(struct task_struct *p)
{
- return security_ops->task_setscheduler(p);
+ return call_int_hook(task_setscheduler, 0, p);
}
int security_task_getscheduler(struct task_struct *p)
{
- return security_ops->task_getscheduler(p);
+ return call_int_hook(task_getscheduler, 0, p);
}
int security_task_movememory(struct task_struct *p)
{
- return security_ops->task_movememory(p);
+ return call_int_hook(task_movememory, 0, p);
}
int security_task_kill(struct task_struct *p, struct siginfo *info,
int sig, u32 secid)
{
- return security_ops->task_kill(p, info, sig, secid);
+ return call_int_hook(task_kill, 0, p, info, sig, secid);
}
int security_task_wait(struct task_struct *p)
{
- return security_ops->task_wait(p);
+ return call_int_hook(task_wait, 0, p);
}
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
-#ifdef CONFIG_SECURITY_YAMA_STACKED
- int rc;
- rc = yama_task_prctl(option, arg2, arg3, arg4, arg5);
- if (rc != -ENOSYS)
- return rc;
-#endif
- return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
+ int thisrc;
+ int rc = -ENOSYS;
+ struct security_hook_list *hp;
+
+ list_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
+ thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
+ if (thisrc != -ENOSYS) {
+ rc = thisrc;
+ if (thisrc != 0)
+ break;
+ }
+ }
+ return rc;
}
void security_task_to_inode(struct task_struct *p, struct inode *inode)
{
- security_ops->task_to_inode(p, inode);
+ call_void_hook(task_to_inode, p, inode);
}
int security_ipc_permission(struct kern_ipc_perm *ipcp, short flag)
{
- return security_ops->ipc_permission(ipcp, flag);
+ return call_int_hook(ipc_permission, 0, ipcp, flag);
}
void security_ipc_getsecid(struct kern_ipc_perm *ipcp, u32 *secid)
{
- security_ops->ipc_getsecid(ipcp, secid);
+ *secid = 0;
+ call_void_hook(ipc_getsecid, ipcp, secid);
}
int security_msg_msg_alloc(struct msg_msg *msg)
{
- return security_ops->msg_msg_alloc_security(msg);
+ return call_int_hook(msg_msg_alloc_security, 0, msg);
}
void security_msg_msg_free(struct msg_msg *msg)
{
- security_ops->msg_msg_free_security(msg);
+ call_void_hook(msg_msg_free_security, msg);
}
int security_msg_queue_alloc(struct msg_queue *msq)
{
- return security_ops->msg_queue_alloc_security(msq);
+ return call_int_hook(msg_queue_alloc_security, 0, msq);
}
void security_msg_queue_free(struct msg_queue *msq)
{
- security_ops->msg_queue_free_security(msq);
+ call_void_hook(msg_queue_free_security, msq);
}
int security_msg_queue_associate(struct msg_queue *msq, int msqflg)
{
- return security_ops->msg_queue_associate(msq, msqflg);
+ return call_int_hook(msg_queue_associate, 0, msq, msqflg);
}
int security_msg_queue_msgctl(struct msg_queue *msq, int cmd)
{
- return security_ops->msg_queue_msgctl(msq, cmd);
+ return call_int_hook(msg_queue_msgctl, 0, msq, cmd);
}
int security_msg_queue_msgsnd(struct msg_queue *msq,
struct msg_msg *msg, int msqflg)
{
- return security_ops->msg_queue_msgsnd(msq, msg, msqflg);
+ return call_int_hook(msg_queue_msgsnd, 0, msq, msg, msqflg);
}
int security_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
struct task_struct *target, long type, int mode)
{
- return security_ops->msg_queue_msgrcv(msq, msg, target, type, mode);
+ return call_int_hook(msg_queue_msgrcv, 0, msq, msg, target, type, mode);
}
int security_shm_alloc(struct shmid_kernel *shp)
{
- return security_ops->shm_alloc_security(shp);
+ return call_int_hook(shm_alloc_security, 0, shp);
}
void security_shm_free(struct shmid_kernel *shp)
{
- security_ops->shm_free_security(shp);
+ call_void_hook(shm_free_security, shp);
}
int security_shm_associate(struct shmid_kernel *shp, int shmflg)
{
- return security_ops->shm_associate(shp, shmflg);
+ return call_int_hook(shm_associate, 0, shp, shmflg);
}
int security_shm_shmctl(struct shmid_kernel *shp, int cmd)
{
- return security_ops->shm_shmctl(shp, cmd);
+ return call_int_hook(shm_shmctl, 0, shp, cmd);
}
int security_shm_shmat(struct shmid_kernel *shp, char __user *shmaddr, int shmflg)
{
- return security_ops->shm_shmat(shp, shmaddr, shmflg);
+ return call_int_hook(shm_shmat, 0, shp, shmaddr, shmflg);
}
int security_sem_alloc(struct sem_array *sma)
{
- return security_ops->sem_alloc_security(sma);
+ return call_int_hook(sem_alloc_security, 0, sma);
}
void security_sem_free(struct sem_array *sma)
{
- security_ops->sem_free_security(sma);
+ call_void_hook(sem_free_security, sma);
}
int security_sem_associate(struct sem_array *sma, int semflg)
{
- return security_ops->sem_associate(sma, semflg);
+ return call_int_hook(sem_associate, 0, sma, semflg);
}
int security_sem_semctl(struct sem_array *sma, int cmd)
{
- return security_ops->sem_semctl(sma, cmd);
+ return call_int_hook(sem_semctl, 0, sma, cmd);
}
int security_sem_semop(struct sem_array *sma, struct sembuf *sops,
unsigned nsops, int alter)
{
- return security_ops->sem_semop(sma, sops, nsops, alter);
+ return call_int_hook(sem_semop, 0, sma, sops, nsops, alter);
}
void security_d_instantiate(struct dentry *dentry, struct inode *inode)
{
if (unlikely(inode && IS_PRIVATE(inode)))
return;
- security_ops->d_instantiate(dentry, inode);
+ call_void_hook(d_instantiate, dentry, inode);
}
EXPORT_SYMBOL(security_d_instantiate);
int security_getprocattr(struct task_struct *p, char *name, char **value)
{
- return security_ops->getprocattr(p, name, value);
+ return call_int_hook(getprocattr, -EINVAL, p, name, value);
}
int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size)
{
- return security_ops->setprocattr(p, name, value, size);
+ return call_int_hook(setprocattr, -EINVAL, p, name, value, size);
}
int security_netlink_send(struct sock *sk, struct sk_buff *skb)
{
- return security_ops->netlink_send(sk, skb);
+ return call_int_hook(netlink_send, 0, sk, skb);
}
int security_ismaclabel(const char *name)
{
- return security_ops->ismaclabel(name);
+ return call_int_hook(ismaclabel, 0, name);
}
EXPORT_SYMBOL(security_ismaclabel);
int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen)
{
- return security_ops->secid_to_secctx(secid, secdata, seclen);
+ return call_int_hook(secid_to_secctx, -EOPNOTSUPP, secid, secdata,
+ seclen);
}
EXPORT_SYMBOL(security_secid_to_secctx);
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
{
- return security_ops->secctx_to_secid(secdata, seclen, secid);
+ *secid = 0;
+ return call_int_hook(secctx_to_secid, 0, secdata, seclen, secid);
}
EXPORT_SYMBOL(security_secctx_to_secid);
void security_release_secctx(char *secdata, u32 seclen)
{
- security_ops->release_secctx(secdata, seclen);
+ call_void_hook(release_secctx, secdata, seclen);
}
EXPORT_SYMBOL(security_release_secctx);
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
{
- return security_ops->inode_notifysecctx(inode, ctx, ctxlen);
+ return call_int_hook(inode_notifysecctx, 0, inode, ctx, ctxlen);
}
EXPORT_SYMBOL(security_inode_notifysecctx);
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
{
- return security_ops->inode_setsecctx(dentry, ctx, ctxlen);
+ return call_int_hook(inode_setsecctx, 0, dentry, ctx, ctxlen);
}
EXPORT_SYMBOL(security_inode_setsecctx);
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
{
- return security_ops->inode_getsecctx(inode, ctx, ctxlen);
+ return call_int_hook(inode_getsecctx, -EOPNOTSUPP, inode, ctx, ctxlen);
}
EXPORT_SYMBOL(security_inode_getsecctx);
@@ -1156,206 +1183,208 @@ EXPORT_SYMBOL(security_inode_getsecctx);
int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk)
{
- return security_ops->unix_stream_connect(sock, other, newsk);
+ return call_int_hook(unix_stream_connect, 0, sock, other, newsk);
}
EXPORT_SYMBOL(security_unix_stream_connect);
int security_unix_may_send(struct socket *sock, struct socket *other)
{
- return security_ops->unix_may_send(sock, other);
+ return call_int_hook(unix_may_send, 0, sock, other);
}
EXPORT_SYMBOL(security_unix_may_send);
int security_socket_create(int family, int type, int protocol, int kern)
{
- return security_ops->socket_create(family, type, protocol, kern);
+ return call_int_hook(socket_create, 0, family, type, protocol, kern);
}
int security_socket_post_create(struct socket *sock, int family,
int type, int protocol, int kern)
{
- return security_ops->socket_post_create(sock, family, type,
+ return call_int_hook(socket_post_create, 0, sock, family, type,
protocol, kern);
}
int security_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{
- return security_ops->socket_bind(sock, address, addrlen);
+ return call_int_hook(socket_bind, 0, sock, address, addrlen);
}
int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
- return security_ops->socket_connect(sock, address, addrlen);
+ return call_int_hook(socket_connect, 0, sock, address, addrlen);
}
int security_socket_listen(struct socket *sock, int backlog)
{
- return security_ops->socket_listen(sock, backlog);
+ return call_int_hook(socket_listen, 0, sock, backlog);
}
int security_socket_accept(struct socket *sock, struct socket *newsock)
{
- return security_ops->socket_accept(sock, newsock);
+ return call_int_hook(socket_accept, 0, sock, newsock);
}
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
- return security_ops->socket_sendmsg(sock, msg, size);
+ return call_int_hook(socket_sendmsg, 0, sock, msg, size);
}
int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags)
{
- return security_ops->socket_recvmsg(sock, msg, size, flags);
+ return call_int_hook(socket_recvmsg, 0, sock, msg, size, flags);
}
int security_socket_getsockname(struct socket *sock)
{
- return security_ops->socket_getsockname(sock);
+ return call_int_hook(socket_getsockname, 0, sock);
}
int security_socket_getpeername(struct socket *sock)
{
- return security_ops->socket_getpeername(sock);
+ return call_int_hook(socket_getpeername, 0, sock);
}
int security_socket_getsockopt(struct socket *sock, int level, int optname)
{
- return security_ops->socket_getsockopt(sock, level, optname);
+ return call_int_hook(socket_getsockopt, 0, sock, level, optname);
}
int security_socket_setsockopt(struct socket *sock, int level, int optname)
{
- return security_ops->socket_setsockopt(sock, level, optname);
+ return call_int_hook(socket_setsockopt, 0, sock, level, optname);
}
int security_socket_shutdown(struct socket *sock, int how)
{
- return security_ops->socket_shutdown(sock, how);
+ return call_int_hook(socket_shutdown, 0, sock, how);
}
int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
- return security_ops->socket_sock_rcv_skb(sk, skb);
+ return call_int_hook(socket_sock_rcv_skb, 0, sk, skb);
}
EXPORT_SYMBOL(security_sock_rcv_skb);
int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
int __user *optlen, unsigned len)
{
- return security_ops->socket_getpeersec_stream(sock, optval, optlen, len);
+ return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock,
+ optval, optlen, len);
}
int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)
{
- return security_ops->socket_getpeersec_dgram(sock, skb, secid);
+ return call_int_hook(socket_getpeersec_dgram, -ENOPROTOOPT, sock,
+ skb, secid);
}
EXPORT_SYMBOL(security_socket_getpeersec_dgram);
int security_sk_alloc(struct sock *sk, int family, gfp_t priority)
{
- return security_ops->sk_alloc_security(sk, family, priority);
+ return call_int_hook(sk_alloc_security, 0, sk, family, priority);
}
void security_sk_free(struct sock *sk)
{
- security_ops->sk_free_security(sk);
+ call_void_hook(sk_free_security, sk);
}
void security_sk_clone(const struct sock *sk, struct sock *newsk)
{
- security_ops->sk_clone_security(sk, newsk);
+ call_void_hook(sk_clone_security, sk, newsk);
}
EXPORT_SYMBOL(security_sk_clone);
void security_sk_classify_flow(struct sock *sk, struct flowi *fl)
{
- security_ops->sk_getsecid(sk, &fl->flowi_secid);
+ call_void_hook(sk_getsecid, sk, &fl->flowi_secid);
}
EXPORT_SYMBOL(security_sk_classify_flow);
void security_req_classify_flow(const struct request_sock *req, struct flowi *fl)
{
- security_ops->req_classify_flow(req, fl);
+ call_void_hook(req_classify_flow, req, fl);
}
EXPORT_SYMBOL(security_req_classify_flow);
void security_sock_graft(struct sock *sk, struct socket *parent)
{
- security_ops->sock_graft(sk, parent);
+ call_void_hook(sock_graft, sk, parent);
}
EXPORT_SYMBOL(security_sock_graft);
int security_inet_conn_request(struct sock *sk,
struct sk_buff *skb, struct request_sock *req)
{
- return security_ops->inet_conn_request(sk, skb, req);
+ return call_int_hook(inet_conn_request, 0, sk, skb, req);
}
EXPORT_SYMBOL(security_inet_conn_request);
void security_inet_csk_clone(struct sock *newsk,
const struct request_sock *req)
{
- security_ops->inet_csk_clone(newsk, req);
+ call_void_hook(inet_csk_clone, newsk, req);
}
void security_inet_conn_established(struct sock *sk,
struct sk_buff *skb)
{
- security_ops->inet_conn_established(sk, skb);
+ call_void_hook(inet_conn_established, sk, skb);
}
int security_secmark_relabel_packet(u32 secid)
{
- return security_ops->secmark_relabel_packet(secid);
+ return call_int_hook(secmark_relabel_packet, 0, secid);
}
EXPORT_SYMBOL(security_secmark_relabel_packet);
void security_secmark_refcount_inc(void)
{
- security_ops->secmark_refcount_inc();
+ call_void_hook(secmark_refcount_inc);
}
EXPORT_SYMBOL(security_secmark_refcount_inc);
void security_secmark_refcount_dec(void)
{
- security_ops->secmark_refcount_dec();
+ call_void_hook(secmark_refcount_dec);
}
EXPORT_SYMBOL(security_secmark_refcount_dec);
int security_tun_dev_alloc_security(void **security)
{
- return security_ops->tun_dev_alloc_security(security);
+ return call_int_hook(tun_dev_alloc_security, 0, security);
}
EXPORT_SYMBOL(security_tun_dev_alloc_security);
void security_tun_dev_free_security(void *security)
{
- security_ops->tun_dev_free_security(security);
+ call_void_hook(tun_dev_free_security, security);
}
EXPORT_SYMBOL(security_tun_dev_free_security);
int security_tun_dev_create(void)
{
- return security_ops->tun_dev_create();
+ return call_int_hook(tun_dev_create, 0);
}
EXPORT_SYMBOL(security_tun_dev_create);
int security_tun_dev_attach_queue(void *security)
{
- return security_ops->tun_dev_attach_queue(security);
+ return call_int_hook(tun_dev_attach_queue, 0, security);
}
EXPORT_SYMBOL(security_tun_dev_attach_queue);
int security_tun_dev_attach(struct sock *sk, void *security)
{
- return security_ops->tun_dev_attach(sk, security);
+ return call_int_hook(tun_dev_attach, 0, sk, security);
}
EXPORT_SYMBOL(security_tun_dev_attach);
int security_tun_dev_open(void *security)
{
- return security_ops->tun_dev_open(security);
+ return call_int_hook(tun_dev_open, 0, security);
}
EXPORT_SYMBOL(security_tun_dev_open);
@@ -1367,71 +1396,89 @@ int security_xfrm_policy_alloc(struct xfrm_sec_ctx **ctxp,
struct xfrm_user_sec_ctx *sec_ctx,
gfp_t gfp)
{
- return security_ops->xfrm_policy_alloc_security(ctxp, sec_ctx, gfp);
+ return call_int_hook(xfrm_policy_alloc_security, 0, ctxp, sec_ctx, gfp);
}
EXPORT_SYMBOL(security_xfrm_policy_alloc);
int security_xfrm_policy_clone(struct xfrm_sec_ctx *old_ctx,
struct xfrm_sec_ctx **new_ctxp)
{
- return security_ops->xfrm_policy_clone_security(old_ctx, new_ctxp);
+ return call_int_hook(xfrm_policy_clone_security, 0, old_ctx, new_ctxp);
}
void security_xfrm_policy_free(struct xfrm_sec_ctx *ctx)
{
- security_ops->xfrm_policy_free_security(ctx);
+ call_void_hook(xfrm_policy_free_security, ctx);
}
EXPORT_SYMBOL(security_xfrm_policy_free);
int security_xfrm_policy_delete(struct xfrm_sec_ctx *ctx)
{
- return security_ops->xfrm_policy_delete_security(ctx);
+ return call_int_hook(xfrm_policy_delete_security, 0, ctx);
}
int security_xfrm_state_alloc(struct xfrm_state *x,
struct xfrm_user_sec_ctx *sec_ctx)
{
- return security_ops->xfrm_state_alloc(x, sec_ctx);
+ return call_int_hook(xfrm_state_alloc, 0, x, sec_ctx);
}
EXPORT_SYMBOL(security_xfrm_state_alloc);
int security_xfrm_state_alloc_acquire(struct xfrm_state *x,
struct xfrm_sec_ctx *polsec, u32 secid)
{
- return security_ops->xfrm_state_alloc_acquire(x, polsec, secid);
+ return call_int_hook(xfrm_state_alloc_acquire, 0, x, polsec, secid);
}
int security_xfrm_state_delete(struct xfrm_state *x)
{
- return security_ops->xfrm_state_delete_security(x);
+ return call_int_hook(xfrm_state_delete_security, 0, x);
}
EXPORT_SYMBOL(security_xfrm_state_delete);
void security_xfrm_state_free(struct xfrm_state *x)
{
- security_ops->xfrm_state_free_security(x);
+ call_void_hook(xfrm_state_free_security, x);
}
int security_xfrm_policy_lookup(struct xfrm_sec_ctx *ctx, u32 fl_secid, u8 dir)
{
- return security_ops->xfrm_policy_lookup(ctx, fl_secid, dir);
+ return call_int_hook(xfrm_policy_lookup, 0, ctx, fl_secid, dir);
}
int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
struct xfrm_policy *xp,
const struct flowi *fl)
{
- return security_ops->xfrm_state_pol_flow_match(x, xp, fl);
+ struct security_hook_list *hp;
+ int rc = 1;
+
+ /*
+ * Since this function is expected to return 0 or 1, the judgment
+ * becomes difficult if multiple LSMs supply this call. Fortunately,
+ * we can use the first LSM's judgment because currently only SELinux
+ * supplies this call.
+ *
+ * For speed optimization, we explicitly break the loop rather than
+ * using the macro
+ */
+ list_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
+ list) {
+ rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl);
+ break;
+ }
+ return rc;
}
int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
{
- return security_ops->xfrm_decode_session(skb, secid, 1);
+ return call_int_hook(xfrm_decode_session, 0, skb, secid, 1);
}
void security_skb_classify_flow(struct sk_buff *skb, struct flowi *fl)
{
- int rc = security_ops->xfrm_decode_session(skb, &fl->flowi_secid, 0);
+ int rc = call_int_hook(xfrm_decode_session, 0, skb, &fl->flowi_secid,
+ 0);
BUG_ON(rc);
}
@@ -1444,23 +1491,24 @@ EXPORT_SYMBOL(security_skb_classify_flow);
int security_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags)
{
- return security_ops->key_alloc(key, cred, flags);
+ return call_int_hook(key_alloc, 0, key, cred, flags);
}
void security_key_free(struct key *key)
{
- security_ops->key_free(key);
+ call_void_hook(key_free, key);
}
int security_key_permission(key_ref_t key_ref,
const struct cred *cred, unsigned perm)
{
- return security_ops->key_permission(key_ref, cred, perm);
+ return call_int_hook(key_permission, 0, key_ref, cred, perm);
}
int security_key_getsecurity(struct key *key, char **_buffer)
{
- return security_ops->key_getsecurity(key, _buffer);
+ *_buffer = NULL;
+ return call_int_hook(key_getsecurity, 0, key, _buffer);
}
#endif /* CONFIG_KEYS */
@@ -1469,23 +1517,369 @@ int security_key_getsecurity(struct key *key, char **_buffer)
int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
{
- return security_ops->audit_rule_init(field, op, rulestr, lsmrule);
+ return call_int_hook(audit_rule_init, 0, field, op, rulestr, lsmrule);
}
int security_audit_rule_known(struct audit_krule *krule)
{
- return security_ops->audit_rule_known(krule);
+ return call_int_hook(audit_rule_known, 0, krule);
}
void security_audit_rule_free(void *lsmrule)
{
- security_ops->audit_rule_free(lsmrule);
+ call_void_hook(audit_rule_free, lsmrule);
}
int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule,
struct audit_context *actx)
{
- return security_ops->audit_rule_match(secid, field, op, lsmrule, actx);
+ return call_int_hook(audit_rule_match, 0, secid, field, op, lsmrule,
+ actx);
}
+#endif /* CONFIG_AUDIT */
+struct security_hook_heads security_hook_heads = {
+ .binder_set_context_mgr =
+ LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr),
+ .binder_transaction =
+ LIST_HEAD_INIT(security_hook_heads.binder_transaction),
+ .binder_transfer_binder =
+ LIST_HEAD_INIT(security_hook_heads.binder_transfer_binder),
+ .binder_transfer_file =
+ LIST_HEAD_INIT(security_hook_heads.binder_transfer_file),
+
+ .ptrace_access_check =
+ LIST_HEAD_INIT(security_hook_heads.ptrace_access_check),
+ .ptrace_traceme =
+ LIST_HEAD_INIT(security_hook_heads.ptrace_traceme),
+ .capget = LIST_HEAD_INIT(security_hook_heads.capget),
+ .capset = LIST_HEAD_INIT(security_hook_heads.capset),
+ .capable = LIST_HEAD_INIT(security_hook_heads.capable),
+ .quotactl = LIST_HEAD_INIT(security_hook_heads.quotactl),
+ .quota_on = LIST_HEAD_INIT(security_hook_heads.quota_on),
+ .syslog = LIST_HEAD_INIT(security_hook_heads.syslog),
+ .settime = LIST_HEAD_INIT(security_hook_heads.settime),
+ .vm_enough_memory =
+ LIST_HEAD_INIT(security_hook_heads.vm_enough_memory),
+ .bprm_set_creds =
+ LIST_HEAD_INIT(security_hook_heads.bprm_set_creds),
+ .bprm_check_security =
+ LIST_HEAD_INIT(security_hook_heads.bprm_check_security),
+ .bprm_secureexec =
+ LIST_HEAD_INIT(security_hook_heads.bprm_secureexec),
+ .bprm_committing_creds =
+ LIST_HEAD_INIT(security_hook_heads.bprm_committing_creds),
+ .bprm_committed_creds =
+ LIST_HEAD_INIT(security_hook_heads.bprm_committed_creds),
+ .sb_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.sb_alloc_security),
+ .sb_free_security =
+ LIST_HEAD_INIT(security_hook_heads.sb_free_security),
+ .sb_copy_data = LIST_HEAD_INIT(security_hook_heads.sb_copy_data),
+ .sb_remount = LIST_HEAD_INIT(security_hook_heads.sb_remount),
+ .sb_kern_mount =
+ LIST_HEAD_INIT(security_hook_heads.sb_kern_mount),
+ .sb_show_options =
+ LIST_HEAD_INIT(security_hook_heads.sb_show_options),
+ .sb_statfs = LIST_HEAD_INIT(security_hook_heads.sb_statfs),
+ .sb_mount = LIST_HEAD_INIT(security_hook_heads.sb_mount),
+ .sb_umount = LIST_HEAD_INIT(security_hook_heads.sb_umount),
+ .sb_pivotroot = LIST_HEAD_INIT(security_hook_heads.sb_pivotroot),
+ .sb_set_mnt_opts =
+ LIST_HEAD_INIT(security_hook_heads.sb_set_mnt_opts),
+ .sb_clone_mnt_opts =
+ LIST_HEAD_INIT(security_hook_heads.sb_clone_mnt_opts),
+ .sb_parse_opts_str =
+ LIST_HEAD_INIT(security_hook_heads.sb_parse_opts_str),
+ .dentry_init_security =
+ LIST_HEAD_INIT(security_hook_heads.dentry_init_security),
+#ifdef CONFIG_SECURITY_PATH
+ .path_unlink = LIST_HEAD_INIT(security_hook_heads.path_unlink),
+ .path_mkdir = LIST_HEAD_INIT(security_hook_heads.path_mkdir),
+ .path_rmdir = LIST_HEAD_INIT(security_hook_heads.path_rmdir),
+ .path_mknod = LIST_HEAD_INIT(security_hook_heads.path_mknod),
+ .path_truncate =
+ LIST_HEAD_INIT(security_hook_heads.path_truncate),
+ .path_symlink = LIST_HEAD_INIT(security_hook_heads.path_symlink),
+ .path_link = LIST_HEAD_INIT(security_hook_heads.path_link),
+ .path_rename = LIST_HEAD_INIT(security_hook_heads.path_rename),
+ .path_chmod = LIST_HEAD_INIT(security_hook_heads.path_chmod),
+ .path_chown = LIST_HEAD_INIT(security_hook_heads.path_chown),
+ .path_chroot = LIST_HEAD_INIT(security_hook_heads.path_chroot),
+#endif
+ .inode_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.inode_alloc_security),
+ .inode_free_security =
+ LIST_HEAD_INIT(security_hook_heads.inode_free_security),
+ .inode_init_security =
+ LIST_HEAD_INIT(security_hook_heads.inode_init_security),
+ .inode_create = LIST_HEAD_INIT(security_hook_heads.inode_create),
+ .inode_link = LIST_HEAD_INIT(security_hook_heads.inode_link),
+ .inode_unlink = LIST_HEAD_INIT(security_hook_heads.inode_unlink),
+ .inode_symlink =
+ LIST_HEAD_INIT(security_hook_heads.inode_symlink),
+ .inode_mkdir = LIST_HEAD_INIT(security_hook_heads.inode_mkdir),
+ .inode_rmdir = LIST_HEAD_INIT(security_hook_heads.inode_rmdir),
+ .inode_mknod = LIST_HEAD_INIT(security_hook_heads.inode_mknod),
+ .inode_rename = LIST_HEAD_INIT(security_hook_heads.inode_rename),
+ .inode_readlink =
+ LIST_HEAD_INIT(security_hook_heads.inode_readlink),
+ .inode_follow_link =
+ LIST_HEAD_INIT(security_hook_heads.inode_follow_link),
+ .inode_permission =
+ LIST_HEAD_INIT(security_hook_heads.inode_permission),
+ .inode_setattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_setattr),
+ .inode_getattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_getattr),
+ .inode_setxattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_setxattr),
+ .inode_post_setxattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_post_setxattr),
+ .inode_getxattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_getxattr),
+ .inode_listxattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_listxattr),
+ .inode_removexattr =
+ LIST_HEAD_INIT(security_hook_heads.inode_removexattr),
+ .inode_need_killpriv =
+ LIST_HEAD_INIT(security_hook_heads.inode_need_killpriv),
+ .inode_killpriv =
+ LIST_HEAD_INIT(security_hook_heads.inode_killpriv),
+ .inode_getsecurity =
+ LIST_HEAD_INIT(security_hook_heads.inode_getsecurity),
+ .inode_setsecurity =
+ LIST_HEAD_INIT(security_hook_heads.inode_setsecurity),
+ .inode_listsecurity =
+ LIST_HEAD_INIT(security_hook_heads.inode_listsecurity),
+ .inode_getsecid =
+ LIST_HEAD_INIT(security_hook_heads.inode_getsecid),
+ .file_permission =
+ LIST_HEAD_INIT(security_hook_heads.file_permission),
+ .file_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.file_alloc_security),
+ .file_free_security =
+ LIST_HEAD_INIT(security_hook_heads.file_free_security),
+ .file_ioctl = LIST_HEAD_INIT(security_hook_heads.file_ioctl),
+ .mmap_addr = LIST_HEAD_INIT(security_hook_heads.mmap_addr),
+ .mmap_file = LIST_HEAD_INIT(security_hook_heads.mmap_file),
+ .file_mprotect =
+ LIST_HEAD_INIT(security_hook_heads.file_mprotect),
+ .file_lock = LIST_HEAD_INIT(security_hook_heads.file_lock),
+ .file_fcntl = LIST_HEAD_INIT(security_hook_heads.file_fcntl),
+ .file_set_fowner =
+ LIST_HEAD_INIT(security_hook_heads.file_set_fowner),
+ .file_send_sigiotask =
+ LIST_HEAD_INIT(security_hook_heads.file_send_sigiotask),
+ .file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive),
+ .file_open = LIST_HEAD_INIT(security_hook_heads.file_open),
+ .task_create = LIST_HEAD_INIT(security_hook_heads.task_create),
+ .task_free = LIST_HEAD_INIT(security_hook_heads.task_free),
+ .cred_alloc_blank =
+ LIST_HEAD_INIT(security_hook_heads.cred_alloc_blank),
+ .cred_free = LIST_HEAD_INIT(security_hook_heads.cred_free),
+ .cred_prepare = LIST_HEAD_INIT(security_hook_heads.cred_prepare),
+ .cred_transfer =
+ LIST_HEAD_INIT(security_hook_heads.cred_transfer),
+ .kernel_act_as =
+ LIST_HEAD_INIT(security_hook_heads.kernel_act_as),
+ .kernel_create_files_as =
+ LIST_HEAD_INIT(security_hook_heads.kernel_create_files_as),
+ .kernel_fw_from_file =
+ LIST_HEAD_INIT(security_hook_heads.kernel_fw_from_file),
+ .kernel_module_request =
+ LIST_HEAD_INIT(security_hook_heads.kernel_module_request),
+ .kernel_module_from_file =
+ LIST_HEAD_INIT(security_hook_heads.kernel_module_from_file),
+ .task_fix_setuid =
+ LIST_HEAD_INIT(security_hook_heads.task_fix_setuid),
+ .task_setpgid = LIST_HEAD_INIT(security_hook_heads.task_setpgid),
+ .task_getpgid = LIST_HEAD_INIT(security_hook_heads.task_getpgid),
+ .task_getsid = LIST_HEAD_INIT(security_hook_heads.task_getsid),
+ .task_getsecid =
+ LIST_HEAD_INIT(security_hook_heads.task_getsecid),
+ .task_setnice = LIST_HEAD_INIT(security_hook_heads.task_setnice),
+ .task_setioprio =
+ LIST_HEAD_INIT(security_hook_heads.task_setioprio),
+ .task_getioprio =
+ LIST_HEAD_INIT(security_hook_heads.task_getioprio),
+ .task_setrlimit =
+ LIST_HEAD_INIT(security_hook_heads.task_setrlimit),
+ .task_setscheduler =
+ LIST_HEAD_INIT(security_hook_heads.task_setscheduler),
+ .task_getscheduler =
+ LIST_HEAD_INIT(security_hook_heads.task_getscheduler),
+ .task_movememory =
+ LIST_HEAD_INIT(security_hook_heads.task_movememory),
+ .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill),
+ .task_wait = LIST_HEAD_INIT(security_hook_heads.task_wait),
+ .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl),
+ .task_to_inode =
+ LIST_HEAD_INIT(security_hook_heads.task_to_inode),
+ .ipc_permission =
+ LIST_HEAD_INIT(security_hook_heads.ipc_permission),
+ .ipc_getsecid = LIST_HEAD_INIT(security_hook_heads.ipc_getsecid),
+ .msg_msg_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.msg_msg_alloc_security),
+ .msg_msg_free_security =
+ LIST_HEAD_INIT(security_hook_heads.msg_msg_free_security),
+ .msg_queue_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.msg_queue_alloc_security),
+ .msg_queue_free_security =
+ LIST_HEAD_INIT(security_hook_heads.msg_queue_free_security),
+ .msg_queue_associate =
+ LIST_HEAD_INIT(security_hook_heads.msg_queue_associate),
+ .msg_queue_msgctl =
+ LIST_HEAD_INIT(security_hook_heads.msg_queue_msgctl),
+ .msg_queue_msgsnd =
+ LIST_HEAD_INIT(security_hook_heads.msg_queue_msgsnd),
+ .msg_queue_msgrcv =
+ LIST_HEAD_INIT(security_hook_heads.msg_queue_msgrcv),
+ .shm_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.shm_alloc_security),
+ .shm_free_security =
+ LIST_HEAD_INIT(security_hook_heads.shm_free_security),
+ .shm_associate =
+ LIST_HEAD_INIT(security_hook_heads.shm_associate),
+ .shm_shmctl = LIST_HEAD_INIT(security_hook_heads.shm_shmctl),
+ .shm_shmat = LIST_HEAD_INIT(security_hook_heads.shm_shmat),
+ .sem_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.sem_alloc_security),
+ .sem_free_security =
+ LIST_HEAD_INIT(security_hook_heads.sem_free_security),
+ .sem_associate =
+ LIST_HEAD_INIT(security_hook_heads.sem_associate),
+ .sem_semctl = LIST_HEAD_INIT(security_hook_heads.sem_semctl),
+ .sem_semop = LIST_HEAD_INIT(security_hook_heads.sem_semop),
+ .netlink_send = LIST_HEAD_INIT(security_hook_heads.netlink_send),
+ .d_instantiate =
+ LIST_HEAD_INIT(security_hook_heads.d_instantiate),
+ .getprocattr = LIST_HEAD_INIT(security_hook_heads.getprocattr),
+ .setprocattr = LIST_HEAD_INIT(security_hook_heads.setprocattr),
+ .ismaclabel = LIST_HEAD_INIT(security_hook_heads.ismaclabel),
+ .secid_to_secctx =
+ LIST_HEAD_INIT(security_hook_heads.secid_to_secctx),
+ .secctx_to_secid =
+ LIST_HEAD_INIT(security_hook_heads.secctx_to_secid),
+ .release_secctx =
+ LIST_HEAD_INIT(security_hook_heads.release_secctx),
+ .inode_notifysecctx =
+ LIST_HEAD_INIT(security_hook_heads.inode_notifysecctx),
+ .inode_setsecctx =
+ LIST_HEAD_INIT(security_hook_heads.inode_setsecctx),
+ .inode_getsecctx =
+ LIST_HEAD_INIT(security_hook_heads.inode_getsecctx),
+#ifdef CONFIG_SECURITY_NETWORK
+ .unix_stream_connect =
+ LIST_HEAD_INIT(security_hook_heads.unix_stream_connect),
+ .unix_may_send =
+ LIST_HEAD_INIT(security_hook_heads.unix_may_send),
+ .socket_create =
+ LIST_HEAD_INIT(security_hook_heads.socket_create),
+ .socket_post_create =
+ LIST_HEAD_INIT(security_hook_heads.socket_post_create),
+ .socket_bind = LIST_HEAD_INIT(security_hook_heads.socket_bind),
+ .socket_connect =
+ LIST_HEAD_INIT(security_hook_heads.socket_connect),
+ .socket_listen =
+ LIST_HEAD_INIT(security_hook_heads.socket_listen),
+ .socket_accept =
+ LIST_HEAD_INIT(security_hook_heads.socket_accept),
+ .socket_sendmsg =
+ LIST_HEAD_INIT(security_hook_heads.socket_sendmsg),
+ .socket_recvmsg =
+ LIST_HEAD_INIT(security_hook_heads.socket_recvmsg),
+ .socket_getsockname =
+ LIST_HEAD_INIT(security_hook_heads.socket_getsockname),
+ .socket_getpeername =
+ LIST_HEAD_INIT(security_hook_heads.socket_getpeername),
+ .socket_getsockopt =
+ LIST_HEAD_INIT(security_hook_heads.socket_getsockopt),
+ .socket_setsockopt =
+ LIST_HEAD_INIT(security_hook_heads.socket_setsockopt),
+ .socket_shutdown =
+ LIST_HEAD_INIT(security_hook_heads.socket_shutdown),
+ .socket_sock_rcv_skb =
+ LIST_HEAD_INIT(security_hook_heads.socket_sock_rcv_skb),
+ .socket_getpeersec_stream =
+ LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_stream),
+ .socket_getpeersec_dgram =
+ LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_dgram),
+ .sk_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.sk_alloc_security),
+ .sk_free_security =
+ LIST_HEAD_INIT(security_hook_heads.sk_free_security),
+ .sk_clone_security =
+ LIST_HEAD_INIT(security_hook_heads.sk_clone_security),
+ .sk_getsecid = LIST_HEAD_INIT(security_hook_heads.sk_getsecid),
+ .sock_graft = LIST_HEAD_INIT(security_hook_heads.sock_graft),
+ .inet_conn_request =
+ LIST_HEAD_INIT(security_hook_heads.inet_conn_request),
+ .inet_csk_clone =
+ LIST_HEAD_INIT(security_hook_heads.inet_csk_clone),
+ .inet_conn_established =
+ LIST_HEAD_INIT(security_hook_heads.inet_conn_established),
+ .secmark_relabel_packet =
+ LIST_HEAD_INIT(security_hook_heads.secmark_relabel_packet),
+ .secmark_refcount_inc =
+ LIST_HEAD_INIT(security_hook_heads.secmark_refcount_inc),
+ .secmark_refcount_dec =
+ LIST_HEAD_INIT(security_hook_heads.secmark_refcount_dec),
+ .req_classify_flow =
+ LIST_HEAD_INIT(security_hook_heads.req_classify_flow),
+ .tun_dev_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.tun_dev_alloc_security),
+ .tun_dev_free_security =
+ LIST_HEAD_INIT(security_hook_heads.tun_dev_free_security),
+ .tun_dev_create =
+ LIST_HEAD_INIT(security_hook_heads.tun_dev_create),
+ .tun_dev_attach_queue =
+ LIST_HEAD_INIT(security_hook_heads.tun_dev_attach_queue),
+ .tun_dev_attach =
+ LIST_HEAD_INIT(security_hook_heads.tun_dev_attach),
+ .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open),
+ .skb_owned_by = LIST_HEAD_INIT(security_hook_heads.skb_owned_by),
+#endif /* CONFIG_SECURITY_NETWORK */
+#ifdef CONFIG_SECURITY_NETWORK_XFRM
+ .xfrm_policy_alloc_security =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_policy_alloc_security),
+ .xfrm_policy_clone_security =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_policy_clone_security),
+ .xfrm_policy_free_security =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_policy_free_security),
+ .xfrm_policy_delete_security =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_policy_delete_security),
+ .xfrm_state_alloc =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc),
+ .xfrm_state_alloc_acquire =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc_acquire),
+ .xfrm_state_free_security =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_state_free_security),
+ .xfrm_state_delete_security =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_state_delete_security),
+ .xfrm_policy_lookup =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_policy_lookup),
+ .xfrm_state_pol_flow_match =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_state_pol_flow_match),
+ .xfrm_decode_session =
+ LIST_HEAD_INIT(security_hook_heads.xfrm_decode_session),
+#endif /* CONFIG_SECURITY_NETWORK_XFRM */
+#ifdef CONFIG_KEYS
+ .key_alloc = LIST_HEAD_INIT(security_hook_heads.key_alloc),
+ .key_free = LIST_HEAD_INIT(security_hook_heads.key_free),
+ .key_permission =
+ LIST_HEAD_INIT(security_hook_heads.key_permission),
+ .key_getsecurity =
+ LIST_HEAD_INIT(security_hook_heads.key_getsecurity),
+#endif /* CONFIG_KEYS */
+#ifdef CONFIG_AUDIT
+ .audit_rule_init =
+ LIST_HEAD_INIT(security_hook_heads.audit_rule_init),
+ .audit_rule_known =
+ LIST_HEAD_INIT(security_hook_heads.audit_rule_known),
+ .audit_rule_match =
+ LIST_HEAD_INIT(security_hook_heads.audit_rule_match),
+ .audit_rule_free =
+ LIST_HEAD_INIT(security_hook_heads.audit_rule_free),
#endif /* CONFIG_AUDIT */
+};
diff --git a/kernel/security/selinux/Kconfig b/kernel/security/selinux/Kconfig
index bca1b74a4..8691e92f2 100644
--- a/kernel/security/selinux/Kconfig
+++ b/kernel/security/selinux/Kconfig
@@ -78,7 +78,7 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE
int "NSA SELinux checkreqprot default value"
depends on SECURITY_SELINUX
range 0 1
- default 1
+ default 0
help
This option sets the default value for the 'checkreqprot' flag
that determines whether SELinux checks the protection requested
@@ -92,7 +92,7 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE
'checkreqprot=' boot parameter. It may also be changed at runtime
via /selinux/checkreqprot if authorized by policy.
- If you are unsure how to answer this question, answer 1.
+ If you are unsure how to answer this question, answer 0.
config SECURITY_SELINUX_POLICYDB_VERSION_MAX
bool "NSA SELinux maximum supported policy format version"
diff --git a/kernel/security/selinux/avc.c b/kernel/security/selinux/avc.c
index 3c17dda95..e60c79de1 100644
--- a/kernel/security/selinux/avc.c
+++ b/kernel/security/selinux/avc.c
@@ -22,6 +22,7 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/percpu.h>
+#include <linux/list.h>
#include <net/sock.h>
#include <linux/un.h>
#include <net/af_unix.h>
@@ -48,6 +49,7 @@ struct avc_entry {
u32 tsid;
u16 tclass;
struct av_decision avd;
+ struct avc_xperms_node *xp_node;
};
struct avc_node {
@@ -56,6 +58,16 @@ struct avc_node {
struct rcu_head rhead;
};
+struct avc_xperms_decision_node {
+ struct extended_perms_decision xpd;
+ struct list_head xpd_list; /* list of extended_perms_decision */
+};
+
+struct avc_xperms_node {
+ struct extended_perms xp;
+ struct list_head xpd_head; /* list head of extended_perms_decision */
+};
+
struct avc_cache {
struct hlist_head slots[AVC_CACHE_SLOTS]; /* head for avc_node->list */
spinlock_t slots_lock[AVC_CACHE_SLOTS]; /* lock for writes */
@@ -80,6 +92,9 @@ DEFINE_PER_CPU(struct avc_cache_stats, avc_cache_stats) = { 0 };
static struct avc_cache avc_cache;
static struct avc_callback_node *avc_callbacks;
static struct kmem_cache *avc_node_cachep;
+static struct kmem_cache *avc_xperms_data_cachep;
+static struct kmem_cache *avc_xperms_decision_cachep;
+static struct kmem_cache *avc_xperms_cachep;
static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
@@ -101,6 +116,7 @@ static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
return;
}
+ BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
perms = secclass_map[tclass-1].perms;
audit_log_format(ab, " {");
@@ -149,7 +165,7 @@ static void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tcla
kfree(scontext);
}
- BUG_ON(tclass >= ARRAY_SIZE(secclass_map));
+ BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name);
}
@@ -170,7 +186,17 @@ void __init avc_init(void)
atomic_set(&avc_cache.lru_hint, 0);
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
- 0, SLAB_PANIC, NULL);
+ 0, SLAB_PANIC, NULL);
+ avc_xperms_cachep = kmem_cache_create("avc_xperms_node",
+ sizeof(struct avc_xperms_node),
+ 0, SLAB_PANIC, NULL);
+ avc_xperms_decision_cachep = kmem_cache_create(
+ "avc_xperms_decision_node",
+ sizeof(struct avc_xperms_decision_node),
+ 0, SLAB_PANIC, NULL);
+ avc_xperms_data_cachep = kmem_cache_create("avc_xperms_data",
+ sizeof(struct extended_perms_data),
+ 0, SLAB_PANIC, NULL);
audit_log(current->audit_context, GFP_KERNEL, AUDIT_KERNEL, "AVC INITIALIZED\n");
}
@@ -205,9 +231,261 @@ int avc_get_hash_stats(char *page)
slots_used, AVC_CACHE_SLOTS, max_chain_len);
}
+/*
+ * using a linked list for extended_perms_decision lookup because the list is
+ * always small. i.e. less than 5, typically 1
+ */
+static struct extended_perms_decision *avc_xperms_decision_lookup(u8 driver,
+ struct avc_xperms_node *xp_node)
+{
+ struct avc_xperms_decision_node *xpd_node;
+
+ list_for_each_entry(xpd_node, &xp_node->xpd_head, xpd_list) {
+ if (xpd_node->xpd.driver == driver)
+ return &xpd_node->xpd;
+ }
+ return NULL;
+}
+
+static inline unsigned int
+avc_xperms_has_perm(struct extended_perms_decision *xpd,
+ u8 perm, u8 which)
+{
+ unsigned int rc = 0;
+
+ if ((which == XPERMS_ALLOWED) &&
+ (xpd->used & XPERMS_ALLOWED))
+ rc = security_xperm_test(xpd->allowed->p, perm);
+ else if ((which == XPERMS_AUDITALLOW) &&
+ (xpd->used & XPERMS_AUDITALLOW))
+ rc = security_xperm_test(xpd->auditallow->p, perm);
+ else if ((which == XPERMS_DONTAUDIT) &&
+ (xpd->used & XPERMS_DONTAUDIT))
+ rc = security_xperm_test(xpd->dontaudit->p, perm);
+ return rc;
+}
+
+static void avc_xperms_allow_perm(struct avc_xperms_node *xp_node,
+ u8 driver, u8 perm)
+{
+ struct extended_perms_decision *xpd;
+ security_xperm_set(xp_node->xp.drivers.p, driver);
+ xpd = avc_xperms_decision_lookup(driver, xp_node);
+ if (xpd && xpd->allowed)
+ security_xperm_set(xpd->allowed->p, perm);
+}
+
+static void avc_xperms_decision_free(struct avc_xperms_decision_node *xpd_node)
+{
+ struct extended_perms_decision *xpd;
+
+ xpd = &xpd_node->xpd;
+ if (xpd->allowed)
+ kmem_cache_free(avc_xperms_data_cachep, xpd->allowed);
+ if (xpd->auditallow)
+ kmem_cache_free(avc_xperms_data_cachep, xpd->auditallow);
+ if (xpd->dontaudit)
+ kmem_cache_free(avc_xperms_data_cachep, xpd->dontaudit);
+ kmem_cache_free(avc_xperms_decision_cachep, xpd_node);
+}
+
+static void avc_xperms_free(struct avc_xperms_node *xp_node)
+{
+ struct avc_xperms_decision_node *xpd_node, *tmp;
+
+ if (!xp_node)
+ return;
+
+ list_for_each_entry_safe(xpd_node, tmp, &xp_node->xpd_head, xpd_list) {
+ list_del(&xpd_node->xpd_list);
+ avc_xperms_decision_free(xpd_node);
+ }
+ kmem_cache_free(avc_xperms_cachep, xp_node);
+}
+
+static void avc_copy_xperms_decision(struct extended_perms_decision *dest,
+ struct extended_perms_decision *src)
+{
+ dest->driver = src->driver;
+ dest->used = src->used;
+ if (dest->used & XPERMS_ALLOWED)
+ memcpy(dest->allowed->p, src->allowed->p,
+ sizeof(src->allowed->p));
+ if (dest->used & XPERMS_AUDITALLOW)
+ memcpy(dest->auditallow->p, src->auditallow->p,
+ sizeof(src->auditallow->p));
+ if (dest->used & XPERMS_DONTAUDIT)
+ memcpy(dest->dontaudit->p, src->dontaudit->p,
+ sizeof(src->dontaudit->p));
+}
+
+/*
+ * similar to avc_copy_xperms_decision, but only copy decision
+ * information relevant to this perm
+ */
+static inline void avc_quick_copy_xperms_decision(u8 perm,
+ struct extended_perms_decision *dest,
+ struct extended_perms_decision *src)
+{
+ /*
+ * compute index of the u32 of the 256 bits (8 u32s) that contain this
+ * command permission
+ */
+ u8 i = perm >> 5;
+
+ dest->used = src->used;
+ if (dest->used & XPERMS_ALLOWED)
+ dest->allowed->p[i] = src->allowed->p[i];
+ if (dest->used & XPERMS_AUDITALLOW)
+ dest->auditallow->p[i] = src->auditallow->p[i];
+ if (dest->used & XPERMS_DONTAUDIT)
+ dest->dontaudit->p[i] = src->dontaudit->p[i];
+}
+
+static struct avc_xperms_decision_node
+ *avc_xperms_decision_alloc(u8 which)
+{
+ struct avc_xperms_decision_node *xpd_node;
+ struct extended_perms_decision *xpd;
+
+ xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
+ GFP_ATOMIC | __GFP_NOMEMALLOC);
+ if (!xpd_node)
+ return NULL;
+
+ xpd = &xpd_node->xpd;
+ if (which & XPERMS_ALLOWED) {
+ xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
+ GFP_ATOMIC | __GFP_NOMEMALLOC);
+ if (!xpd->allowed)
+ goto error;
+ }
+ if (which & XPERMS_AUDITALLOW) {
+ xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
+ GFP_ATOMIC | __GFP_NOMEMALLOC);
+ if (!xpd->auditallow)
+ goto error;
+ }
+ if (which & XPERMS_DONTAUDIT) {
+ xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
+ GFP_ATOMIC | __GFP_NOMEMALLOC);
+ if (!xpd->dontaudit)
+ goto error;
+ }
+ return xpd_node;
+error:
+ avc_xperms_decision_free(xpd_node);
+ return NULL;
+}
+
+static int avc_add_xperms_decision(struct avc_node *node,
+ struct extended_perms_decision *src)
+{
+ struct avc_xperms_decision_node *dest_xpd;
+
+ node->ae.xp_node->xp.len++;
+ dest_xpd = avc_xperms_decision_alloc(src->used);
+ if (!dest_xpd)
+ return -ENOMEM;
+ avc_copy_xperms_decision(&dest_xpd->xpd, src);
+ list_add(&dest_xpd->xpd_list, &node->ae.xp_node->xpd_head);
+ return 0;
+}
+
+static struct avc_xperms_node *avc_xperms_alloc(void)
+{
+ struct avc_xperms_node *xp_node;
+
+ xp_node = kmem_cache_zalloc(avc_xperms_cachep,
+ GFP_ATOMIC|__GFP_NOMEMALLOC);
+ if (!xp_node)
+ return xp_node;
+ INIT_LIST_HEAD(&xp_node->xpd_head);
+ return xp_node;
+}
+
+static int avc_xperms_populate(struct avc_node *node,
+ struct avc_xperms_node *src)
+{
+ struct avc_xperms_node *dest;
+ struct avc_xperms_decision_node *dest_xpd;
+ struct avc_xperms_decision_node *src_xpd;
+
+ if (src->xp.len == 0)
+ return 0;
+ dest = avc_xperms_alloc();
+ if (!dest)
+ return -ENOMEM;
+
+ memcpy(dest->xp.drivers.p, src->xp.drivers.p, sizeof(dest->xp.drivers.p));
+ dest->xp.len = src->xp.len;
+
+ /* for each source xpd allocate a destination xpd and copy */
+ list_for_each_entry(src_xpd, &src->xpd_head, xpd_list) {
+ dest_xpd = avc_xperms_decision_alloc(src_xpd->xpd.used);
+ if (!dest_xpd)
+ goto error;
+ avc_copy_xperms_decision(&dest_xpd->xpd, &src_xpd->xpd);
+ list_add(&dest_xpd->xpd_list, &dest->xpd_head);
+ }
+ node->ae.xp_node = dest;
+ return 0;
+error:
+ avc_xperms_free(dest);
+ return -ENOMEM;
+
+}
+
+static inline u32 avc_xperms_audit_required(u32 requested,
+ struct av_decision *avd,
+ struct extended_perms_decision *xpd,
+ u8 perm,
+ int result,
+ u32 *deniedp)
+{
+ u32 denied, audited;
+
+ denied = requested & ~avd->allowed;
+ if (unlikely(denied)) {
+ audited = denied & avd->auditdeny;
+ if (audited && xpd) {
+ if (avc_xperms_has_perm(xpd, perm, XPERMS_DONTAUDIT))
+ audited &= ~requested;
+ }
+ } else if (result) {
+ audited = denied = requested;
+ } else {
+ audited = requested & avd->auditallow;
+ if (audited && xpd) {
+ if (!avc_xperms_has_perm(xpd, perm, XPERMS_AUDITALLOW))
+ audited &= ~requested;
+ }
+ }
+
+ *deniedp = denied;
+ return audited;
+}
+
+static inline int avc_xperms_audit(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct av_decision *avd,
+ struct extended_perms_decision *xpd,
+ u8 perm, int result,
+ struct common_audit_data *ad)
+{
+ u32 audited, denied;
+
+ audited = avc_xperms_audit_required(
+ requested, avd, xpd, perm, result, &denied);
+ if (likely(!audited))
+ return 0;
+ return slow_avc_audit(ssid, tsid, tclass, requested,
+ audited, denied, result, ad, 0);
+}
+
static void avc_node_free(struct rcu_head *rhead)
{
struct avc_node *node = container_of(rhead, struct avc_node, rhead);
+ avc_xperms_free(node->ae.xp_node);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
}
@@ -221,6 +499,7 @@ static void avc_node_delete(struct avc_node *node)
static void avc_node_kill(struct avc_node *node)
{
+ avc_xperms_free(node->ae.xp_node);
kmem_cache_free(avc_node_cachep, node);
avc_cache_stats_incr(frees);
atomic_dec(&avc_cache.active_nodes);
@@ -367,6 +646,7 @@ static int avc_latest_notif_update(int seqno, int is_insert)
* @tsid: target security identifier
* @tclass: target security class
* @avd: resulting av decision
+ * @xp_node: resulting extended permissions
*
* Insert an AVC entry for the SID pair
* (@ssid, @tsid) and class @tclass.
@@ -378,7 +658,9 @@ static int avc_latest_notif_update(int seqno, int is_insert)
* the access vectors into a cache entry, returns
* avc_node inserted. Otherwise, this function returns NULL.
*/
-static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_decision *avd)
+static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass,
+ struct av_decision *avd,
+ struct avc_xperms_node *xp_node)
{
struct avc_node *pos, *node = NULL;
int hvalue;
@@ -391,10 +673,15 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec
if (node) {
struct hlist_head *head;
spinlock_t *lock;
+ int rc = 0;
hvalue = avc_hash(ssid, tsid, tclass);
avc_node_populate(node, ssid, tsid, tclass, avd);
-
+ rc = avc_xperms_populate(node, xp_node);
+ if (rc) {
+ kmem_cache_free(avc_node_cachep, node);
+ return NULL;
+ }
head = &avc_cache.slots[hvalue];
lock = &avc_cache.slots_lock[hvalue];
@@ -523,14 +810,17 @@ out:
* @perms : Permission mask bits
* @ssid,@tsid,@tclass : identifier of an AVC entry
* @seqno : sequence number when decision was made
+ * @xpd: extended_perms_decision to be added to the node
*
* if a valid AVC entry doesn't exist,this function returns -ENOENT.
* if kmalloc() called internal returns NULL, this function returns -ENOMEM.
* otherwise, this function updates the AVC entry. The original AVC-entry object
* will release later by RCU.
*/
-static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
- u32 seqno)
+static int avc_update_node(u32 event, u32 perms, u8 driver, u8 xperm, u32 ssid,
+ u32 tsid, u16 tclass, u32 seqno,
+ struct extended_perms_decision *xpd,
+ u32 flags)
{
int hvalue, rc = 0;
unsigned long flag;
@@ -574,9 +864,19 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
avc_node_populate(node, ssid, tsid, tclass, &orig->ae.avd);
+ if (orig->ae.xp_node) {
+ rc = avc_xperms_populate(node, orig->ae.xp_node);
+ if (rc) {
+ kmem_cache_free(avc_node_cachep, node);
+ goto out_unlock;
+ }
+ }
+
switch (event) {
case AVC_CALLBACK_GRANT:
node->ae.avd.allowed |= perms;
+ if (node->ae.xp_node && (flags & AVC_EXTENDED_PERMS))
+ avc_xperms_allow_perm(node->ae.xp_node, driver, xperm);
break;
case AVC_CALLBACK_TRY_REVOKE:
case AVC_CALLBACK_REVOKE:
@@ -594,6 +894,9 @@ static int avc_update_node(u32 event, u32 perms, u32 ssid, u32 tsid, u16 tclass,
case AVC_CALLBACK_AUDITDENY_DISABLE:
node->ae.avd.auditdeny &= ~perms;
break;
+ case AVC_CALLBACK_ADD_XPERMS:
+ avc_add_xperms_decision(node, xpd);
+ break;
}
avc_node_replace(node, orig);
out_unlock:
@@ -665,18 +968,20 @@ int avc_ss_reset(u32 seqno)
* results in a bigger stack frame.
*/
static noinline struct avc_node *avc_compute_av(u32 ssid, u32 tsid,
- u16 tclass, struct av_decision *avd)
+ u16 tclass, struct av_decision *avd,
+ struct avc_xperms_node *xp_node)
{
rcu_read_unlock();
- security_compute_av(ssid, tsid, tclass, avd);
+ INIT_LIST_HEAD(&xp_node->xpd_head);
+ security_compute_av(ssid, tsid, tclass, avd, &xp_node->xp);
rcu_read_lock();
- return avc_insert(ssid, tsid, tclass, avd);
+ return avc_insert(ssid, tsid, tclass, avd, xp_node);
}
static noinline int avc_denied(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- unsigned flags,
- struct av_decision *avd)
+ u16 tclass, u32 requested,
+ u8 driver, u8 xperm, unsigned flags,
+ struct av_decision *avd)
{
if (flags & AVC_STRICT)
return -EACCES;
@@ -684,11 +989,91 @@ static noinline int avc_denied(u32 ssid, u32 tsid,
if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
return -EACCES;
- avc_update_node(AVC_CALLBACK_GRANT, requested, ssid,
- tsid, tclass, avd->seqno);
+ avc_update_node(AVC_CALLBACK_GRANT, requested, driver, xperm, ssid,
+ tsid, tclass, avd->seqno, NULL, flags);
return 0;
}
+/*
+ * The avc extended permissions logic adds an additional 256 bits of
+ * permissions to an avc node when extended permissions for that node are
+ * specified in the avtab. If the additional 256 permissions is not adequate,
+ * as-is the case with ioctls, then multiple may be chained together and the
+ * driver field is used to specify which set contains the permission.
+ */
+int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ u8 driver, u8 xperm, struct common_audit_data *ad)
+{
+ struct avc_node *node;
+ struct av_decision avd;
+ u32 denied;
+ struct extended_perms_decision local_xpd;
+ struct extended_perms_decision *xpd = NULL;
+ struct extended_perms_data allowed;
+ struct extended_perms_data auditallow;
+ struct extended_perms_data dontaudit;
+ struct avc_xperms_node local_xp_node;
+ struct avc_xperms_node *xp_node;
+ int rc = 0, rc2;
+
+ xp_node = &local_xp_node;
+ BUG_ON(!requested);
+
+ rcu_read_lock();
+
+ node = avc_lookup(ssid, tsid, tclass);
+ if (unlikely(!node)) {
+ node = avc_compute_av(ssid, tsid, tclass, &avd, xp_node);
+ } else {
+ memcpy(&avd, &node->ae.avd, sizeof(avd));
+ xp_node = node->ae.xp_node;
+ }
+ /* if extended permissions are not defined, only consider av_decision */
+ if (!xp_node || !xp_node->xp.len)
+ goto decision;
+
+ local_xpd.allowed = &allowed;
+ local_xpd.auditallow = &auditallow;
+ local_xpd.dontaudit = &dontaudit;
+
+ xpd = avc_xperms_decision_lookup(driver, xp_node);
+ if (unlikely(!xpd)) {
+ /*
+ * Compute the extended_perms_decision only if the driver
+ * is flagged
+ */
+ if (!security_xperm_test(xp_node->xp.drivers.p, driver)) {
+ avd.allowed &= ~requested;
+ goto decision;
+ }
+ rcu_read_unlock();
+ security_compute_xperms_decision(ssid, tsid, tclass, driver,
+ &local_xpd);
+ rcu_read_lock();
+ avc_update_node(AVC_CALLBACK_ADD_XPERMS, requested, driver, xperm,
+ ssid, tsid, tclass, avd.seqno, &local_xpd, 0);
+ } else {
+ avc_quick_copy_xperms_decision(xperm, &local_xpd, xpd);
+ }
+ xpd = &local_xpd;
+
+ if (!avc_xperms_has_perm(xpd, xperm, XPERMS_ALLOWED))
+ avd.allowed &= ~requested;
+
+decision:
+ denied = requested & ~(avd.allowed);
+ if (unlikely(denied))
+ rc = avc_denied(ssid, tsid, tclass, requested, driver, xperm,
+ AVC_EXTENDED_PERMS, &avd);
+
+ rcu_read_unlock();
+
+ rc2 = avc_xperms_audit(ssid, tsid, tclass, requested,
+ &avd, xpd, xperm, rc, ad);
+ if (rc2)
+ return rc2;
+ return rc;
+}
/**
* avc_has_perm_noaudit - Check permissions but perform no auditing.
@@ -716,6 +1101,7 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
struct av_decision *avd)
{
struct avc_node *node;
+ struct avc_xperms_node xp_node;
int rc = 0;
u32 denied;
@@ -725,13 +1111,13 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
node = avc_lookup(ssid, tsid, tclass);
if (unlikely(!node))
- node = avc_compute_av(ssid, tsid, tclass, avd);
+ node = avc_compute_av(ssid, tsid, tclass, avd, &xp_node);
else
memcpy(avd, &node->ae.avd, sizeof(*avd));
denied = requested & ~(avd->allowed);
if (unlikely(denied))
- rc = avc_denied(ssid, tsid, tclass, requested, flags, avd);
+ rc = avc_denied(ssid, tsid, tclass, requested, 0, 0, flags, avd);
rcu_read_unlock();
return rc;
@@ -761,7 +1147,23 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
- rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
+ rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata, 0);
+ if (rc2)
+ return rc2;
+ return rc;
+}
+
+int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
+ u32 requested, struct common_audit_data *auditdata,
+ int flags)
+{
+ struct av_decision avd;
+ int rc, rc2;
+
+ rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
+
+ rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc,
+ auditdata, flags);
if (rc2)
return rc2;
return rc;
diff --git a/kernel/security/selinux/hooks.c b/kernel/security/selinux/hooks.c
index 280235cc3..d0cfaa9f1 100644
--- a/kernel/security/selinux/hooks.c
+++ b/kernel/security/selinux/hooks.c
@@ -29,7 +29,7 @@
#include <linux/tracehook.h>
#include <linux/errno.h>
#include <linux/sched.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/xattr.h>
#include <linux/capability.h>
#include <linux/unistd.h>
@@ -126,6 +126,7 @@ int selinux_enabled = 1;
#endif
static struct kmem_cache *sel_inode_cache;
+static struct kmem_cache *file_security_cache;
/**
* selinux_secmark_enabled - Check to see if SECMARK is currently enabled
@@ -254,10 +255,21 @@ static void inode_free_security(struct inode *inode)
struct inode_security_struct *isec = inode->i_security;
struct superblock_security_struct *sbsec = inode->i_sb->s_security;
- spin_lock(&sbsec->isec_lock);
- if (!list_empty(&isec->list))
+ /*
+ * As not all inode security structures are in a list, we check for
+ * empty list outside of the lock to make sure that we won't waste
+ * time taking a lock doing nothing.
+ *
+ * The list_del_init() function can be safely called more than once.
+ * It should not be possible for this function to be called with
+ * concurrent list_add(), but for better safety against future changes
+ * in the code, we use list_empty_careful() here.
+ */
+ if (!list_empty_careful(&isec->list)) {
+ spin_lock(&sbsec->isec_lock);
list_del_init(&isec->list);
- spin_unlock(&sbsec->isec_lock);
+ spin_unlock(&sbsec->isec_lock);
+ }
/*
* The inode may still be referenced in a path walk and
@@ -276,7 +288,7 @@ static int file_alloc_security(struct file *file)
struct file_security_struct *fsec;
u32 sid = current_sid();
- fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);
+ fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL);
if (!fsec)
return -ENOMEM;
@@ -291,7 +303,7 @@ static void file_free_security(struct file *file)
{
struct file_security_struct *fsec = file->f_security;
file->f_security = NULL;
- kfree(fsec);
+ kmem_cache_free(file_security_cache, fsec);
}
static int superblock_alloc_security(struct super_block *sb)
@@ -663,10 +675,9 @@ static int selinux_set_mnt_opts(struct super_block *sb,
if (flags[i] == SBLABEL_MNT)
continue;
- rc = security_context_to_sid(mount_options[i],
- strlen(mount_options[i]), &sid, GFP_KERNEL);
+ rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL);
if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
+ printk(KERN_WARNING "SELinux: security_context_str_to_sid"
"(%s) failed for (dev %s, type %s) errno=%d\n",
mount_options[i], sb->s_id, name, rc);
goto out;
@@ -725,7 +736,12 @@ static int selinux_set_mnt_opts(struct super_block *sb,
}
if (strcmp(sb->s_type->name, "proc") == 0)
- sbsec->flags |= SE_SBPROC;
+ sbsec->flags |= SE_SBPROC | SE_SBGENFS;
+
+ if (!strcmp(sb->s_type->name, "debugfs") ||
+ !strcmp(sb->s_type->name, "sysfs") ||
+ !strcmp(sb->s_type->name, "pstore"))
+ sbsec->flags |= SE_SBGENFS;
if (!sbsec->behavior) {
/*
@@ -1189,8 +1205,6 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
switch (protocol) {
case NETLINK_ROUTE:
return SECCLASS_NETLINK_ROUTE_SOCKET;
- case NETLINK_FIREWALL:
- return SECCLASS_NETLINK_FIREWALL_SOCKET;
case NETLINK_SOCK_DIAG:
return SECCLASS_NETLINK_TCPDIAG_SOCKET;
case NETLINK_NFLOG:
@@ -1199,14 +1213,28 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_NETLINK_XFRM_SOCKET;
case NETLINK_SELINUX:
return SECCLASS_NETLINK_SELINUX_SOCKET;
+ case NETLINK_ISCSI:
+ return SECCLASS_NETLINK_ISCSI_SOCKET;
case NETLINK_AUDIT:
return SECCLASS_NETLINK_AUDIT_SOCKET;
- case NETLINK_IP6_FW:
- return SECCLASS_NETLINK_IP6FW_SOCKET;
+ case NETLINK_FIB_LOOKUP:
+ return SECCLASS_NETLINK_FIB_LOOKUP_SOCKET;
+ case NETLINK_CONNECTOR:
+ return SECCLASS_NETLINK_CONNECTOR_SOCKET;
+ case NETLINK_NETFILTER:
+ return SECCLASS_NETLINK_NETFILTER_SOCKET;
case NETLINK_DNRTMSG:
return SECCLASS_NETLINK_DNRT_SOCKET;
case NETLINK_KOBJECT_UEVENT:
return SECCLASS_NETLINK_KOBJECT_UEVENT_SOCKET;
+ case NETLINK_GENERIC:
+ return SECCLASS_NETLINK_GENERIC_SOCKET;
+ case NETLINK_SCSITRANSPORT:
+ return SECCLASS_NETLINK_SCSITRANSPORT_SOCKET;
+ case NETLINK_RDMA:
+ return SECCLASS_NETLINK_RDMA_SOCKET;
+ case NETLINK_CRYPTO:
+ return SECCLASS_NETLINK_CRYPTO_SOCKET;
default:
return SECCLASS_NETLINK_SOCKET;
}
@@ -1221,12 +1249,13 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc
return SECCLASS_SOCKET;
}
-#ifdef CONFIG_PROC_FS
-static int selinux_proc_get_sid(struct dentry *dentry,
- u16 tclass,
- u32 *sid)
+static int selinux_genfs_get_sid(struct dentry *dentry,
+ u16 tclass,
+ u16 flags,
+ u32 *sid)
{
int rc;
+ struct super_block *sb = dentry->d_inode->i_sb;
char *buffer, *path;
buffer = (char *)__get_free_page(GFP_KERNEL);
@@ -1237,26 +1266,20 @@ static int selinux_proc_get_sid(struct dentry *dentry,
if (IS_ERR(path))
rc = PTR_ERR(path);
else {
- /* each process gets a /proc/PID/ entry. Strip off the
- * PID part to get a valid selinux labeling.
- * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */
- while (path[1] >= '0' && path[1] <= '9') {
- path[1] = '/';
- path++;
+ if (flags & SE_SBPROC) {
+ /* each process gets a /proc/PID/ entry. Strip off the
+ * PID part to get a valid selinux labeling.
+ * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */
+ while (path[1] >= '0' && path[1] <= '9') {
+ path[1] = '/';
+ path++;
+ }
}
- rc = security_genfs_sid("proc", path, tclass, sid);
+ rc = security_genfs_sid(sb->s_type->name, path, tclass, sid);
}
free_page((unsigned long)buffer);
return rc;
}
-#else
-static int selinux_proc_get_sid(struct dentry *dentry,
- u16 tclass,
- u32 *sid)
-{
- return -EINVAL;
-}
-#endif
/* The inode's security attributes must be initialized before first use. */
static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry)
@@ -1413,7 +1436,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/* Default to the fs superblock SID. */
isec->sid = sbsec->sid;
- if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
+ if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) {
/* We must have a dentry to determine the label on
* procfs inodes */
if (opt_dentry)
@@ -1436,7 +1459,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
if (!dentry)
goto out_unlock;
isec->sclass = inode_mode_to_security_class(inode->i_mode);
- rc = selinux_proc_get_sid(dentry, isec->sclass, &sid);
+ rc = selinux_genfs_get_sid(dentry, isec->sclass,
+ sbsec->flags, &sid);
dput(dentry);
if (rc)
goto out_unlock;
@@ -1565,7 +1589,7 @@ static int cred_has_capability(const struct cred *cred,
rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
if (audit == SECURITY_CAP_AUDIT) {
- int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
+ int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
if (rc2)
return rc2;
}
@@ -1685,6 +1709,32 @@ out:
return rc;
}
+/*
+ * Determine the label for an inode that might be unioned.
+ */
+static int selinux_determine_inode_label(const struct inode *dir,
+ const struct qstr *name,
+ u16 tclass,
+ u32 *_new_isid)
+{
+ const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
+ const struct inode_security_struct *dsec = dir->i_security;
+ const struct task_security_struct *tsec = current_security();
+
+ if ((sbsec->flags & SE_SBINITIALIZED) &&
+ (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
+ *_new_isid = sbsec->mntpoint_sid;
+ } else if ((sbsec->flags & SBLABEL_MNT) &&
+ tsec->create_sid) {
+ *_new_isid = tsec->create_sid;
+ } else {
+ return security_transition_sid(tsec->sid, dsec->sid, tclass,
+ name, _new_isid);
+ }
+
+ return 0;
+}
+
/* Check whether a task can create a file. */
static int may_create(struct inode *dir,
struct dentry *dentry,
@@ -1701,7 +1751,6 @@ static int may_create(struct inode *dir,
sbsec = dir->i_sb->s_security;
sid = tsec->sid;
- newsid = tsec->create_sid;
ad.type = LSM_AUDIT_DATA_DENTRY;
ad.u.dentry = dentry;
@@ -1712,12 +1761,10 @@ static int may_create(struct inode *dir,
if (rc)
return rc;
- if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
- rc = security_transition_sid(sid, dsec->sid, tclass,
- &dentry->d_name, &newsid);
- if (rc)
- return rc;
- }
+ rc = selinux_determine_inode_label(dir, &dentry->d_name, tclass,
+ &newsid);
+ if (rc)
+ return rc;
rc = avc_has_perm(sid, newsid, tclass, FILE__CREATE, &ad);
if (rc)
@@ -1991,12 +2038,6 @@ static int selinux_binder_transfer_file(struct task_struct *from,
static int selinux_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
- int rc;
-
- rc = cap_ptrace_access_check(child, mode);
- if (rc)
- return rc;
-
if (mode & PTRACE_MODE_READ) {
u32 sid = current_sid();
u32 csid = task_sid(child);
@@ -2008,25 +2049,13 @@ static int selinux_ptrace_access_check(struct task_struct *child,
static int selinux_ptrace_traceme(struct task_struct *parent)
{
- int rc;
-
- rc = cap_ptrace_traceme(parent);
- if (rc)
- return rc;
-
return task_has_perm(parent, current, PROCESS__PTRACE);
}
static int selinux_capget(struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- int error;
-
- error = current_has_perm(target, PROCESS__GETCAP);
- if (error)
- return error;
-
- return cap_capget(target, effective, inheritable, permitted);
+ return current_has_perm(target, PROCESS__GETCAP);
}
static int selinux_capset(struct cred *new, const struct cred *old,
@@ -2034,13 +2063,6 @@ static int selinux_capset(struct cred *new, const struct cred *old,
const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
{
- int error;
-
- error = cap_capset(new, old,
- effective, inheritable, permitted);
- if (error)
- return error;
-
return cred_has_perm(old, new, PROCESS__SETCAP);
}
@@ -2057,12 +2079,6 @@ static int selinux_capset(struct cred *new, const struct cred *old,
static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
- int rc;
-
- rc = cap_capable(cred, ns, cap, audit);
- if (rc)
- return rc;
-
return cred_has_capability(cred, cap, audit);
}
@@ -2140,12 +2156,12 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
{
int rc, cap_sys_admin = 0;
- rc = selinux_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
- SECURITY_CAP_NOAUDIT);
+ rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
+ SECURITY_CAP_NOAUDIT);
if (rc == 0)
cap_sys_admin = 1;
- return __vm_enough_memory(mm, pages, cap_sys_admin);
+ return cap_sys_admin;
}
/* binprm security operations */
@@ -2194,10 +2210,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
struct inode *inode = file_inode(bprm->file);
int rc;
- rc = cap_bprm_set_creds(bprm);
- if (rc)
- return rc;
-
/* SELinux context only depends on initial program or script and not
* the script interpreter */
if (bprm->cred_prepared)
@@ -2321,7 +2333,7 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm)
PROCESS__NOATSECURE, NULL);
}
- return (atsecure || cap_bprm_secureexec(bprm));
+ return !!atsecure;
}
static int match_file(const void *p, struct file *file, unsigned fd)
@@ -2452,10 +2464,12 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
for (i = 0; i < 3; i++)
do_setitimer(i, &itimer, NULL);
spin_lock_irq(&current->sighand->siglock);
- if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) {
- __flush_signals(current);
+ if (!fatal_signal_pending(current)) {
+ flush_sigqueue(&current->pending);
+ flush_sigqueue(&current->signal->shared_pending);
flush_signal_handlers(current, 1);
sigemptyset(&current->blocked);
+ recalc_sigpending();
}
spin_unlock_irq(&current->sighand->siglock);
}
@@ -2603,15 +2617,12 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
for (i = 0; i < opts.num_mnt_opts; i++) {
u32 sid;
- size_t len;
if (flags[i] == SBLABEL_MNT)
continue;
- len = strlen(mount_options[i]);
- rc = security_context_to_sid(mount_options[i], len, &sid,
- GFP_KERNEL);
+ rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL);
if (rc) {
- printk(KERN_WARNING "SELinux: security_context_to_sid"
+ printk(KERN_WARNING "SELinux: security_context_str_to_sid"
"(%s) failed for (dev %s, type %s) errno=%d\n",
mount_options[i], sb->s_id, sb->s_type->name, rc);
goto out_free_opts;
@@ -2724,32 +2735,14 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
struct qstr *name, void **ctx,
u32 *ctxlen)
{
- const struct cred *cred = current_cred();
- struct task_security_struct *tsec;
- struct inode_security_struct *dsec;
- struct superblock_security_struct *sbsec;
- struct inode *dir = d_backing_inode(dentry->d_parent);
u32 newsid;
int rc;
- tsec = cred->security;
- dsec = dir->i_security;
- sbsec = dir->i_sb->s_security;
-
- if (tsec->create_sid && sbsec->behavior != SECURITY_FS_USE_MNTPOINT) {
- newsid = tsec->create_sid;
- } else {
- rc = security_transition_sid(tsec->sid, dsec->sid,
- inode_mode_to_security_class(mode),
- name,
- &newsid);
- if (rc) {
- printk(KERN_WARNING
- "%s: security_transition_sid failed, rc=%d\n",
- __func__, -rc);
- return rc;
- }
- }
+ rc = selinux_determine_inode_label(d_inode(dentry->d_parent), name,
+ inode_mode_to_security_class(mode),
+ &newsid);
+ if (rc)
+ return rc;
return security_sid_to_context(newsid, (char **)ctx, ctxlen);
}
@@ -2772,22 +2765,12 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
sid = tsec->sid;
newsid = tsec->create_sid;
- if ((sbsec->flags & SE_SBINITIALIZED) &&
- (sbsec->behavior == SECURITY_FS_USE_MNTPOINT))
- newsid = sbsec->mntpoint_sid;
- else if (!newsid || !(sbsec->flags & SBLABEL_MNT)) {
- rc = security_transition_sid(sid, dsec->sid,
- inode_mode_to_security_class(inode->i_mode),
- qstr, &newsid);
- if (rc) {
- printk(KERN_WARNING "%s: "
- "security_transition_sid failed, rc=%d (dev=%s "
- "ino=%ld)\n",
- __func__,
- -rc, inode->i_sb->s_id, inode->i_ino);
- return rc;
- }
- }
+ rc = selinux_determine_inode_label(
+ dir, qstr,
+ inode_mode_to_security_class(inode->i_mode),
+ &newsid);
+ if (rc)
+ return rc;
/* Possibly defer initialization to selinux_complete_init. */
if (sbsec->flags & SE_SBINITIALIZED) {
@@ -2862,11 +2845,23 @@ static int selinux_inode_readlink(struct dentry *dentry)
return dentry_has_perm(cred, dentry, FILE__READ);
}
-static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
+static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
+ bool rcu)
{
const struct cred *cred = current_cred();
+ struct common_audit_data ad;
+ struct inode_security_struct *isec;
+ u32 sid;
- return dentry_has_perm(cred, dentry, FILE__READ);
+ validate_creds(cred);
+
+ ad.type = LSM_AUDIT_DATA_DENTRY;
+ ad.u.dentry = dentry;
+ sid = cred_sid(cred);
+ isec = inode->i_security;
+
+ return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
+ rcu ? MAY_NOT_BLOCK : 0);
}
static noinline int audit_inode_permission(struct inode *inode,
@@ -2948,7 +2943,8 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, dentry, FILE__SETATTR);
- if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE))
+ if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE)
+ && !(ia_valid & ATTR_FILE))
av |= FILE__OPEN;
return dentry_has_perm(cred, dentry, av);
@@ -3133,8 +3129,11 @@ static int selinux_inode_getsecurity(const struct inode *inode, const char *name
* and lack of permission just means that we fall back to the
* in-core context value, not a denial.
*/
- error = selinux_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
- SECURITY_CAP_NOAUDIT);
+ error = cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
+ SECURITY_CAP_NOAUDIT);
+ if (!error)
+ error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
+ SECURITY_CAP_NOAUDIT);
if (!error)
error = security_sid_to_context_force(isec->sid, &context,
&size);
@@ -3165,7 +3164,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
if (!value || !size)
return -EACCES;
- rc = security_context_to_sid((void *)value, size, &newsid, GFP_KERNEL);
+ rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL);
if (rc)
return rc;
@@ -3233,6 +3232,46 @@ static void selinux_file_free_security(struct file *file)
file_free_security(file);
}
+/*
+ * Check whether a task has the ioctl permission and cmd
+ * operation to an inode.
+ */
+static int ioctl_has_perm(const struct cred *cred, struct file *file,
+ u32 requested, u16 cmd)
+{
+ struct common_audit_data ad;
+ struct file_security_struct *fsec = file->f_security;
+ struct inode *inode = file_inode(file);
+ struct inode_security_struct *isec = inode->i_security;
+ struct lsm_ioctlop_audit ioctl;
+ u32 ssid = cred_sid(cred);
+ int rc;
+ u8 driver = cmd >> 8;
+ u8 xperm = cmd & 0xff;
+
+ ad.type = LSM_AUDIT_DATA_IOCTL_OP;
+ ad.u.op = &ioctl;
+ ad.u.op->cmd = cmd;
+ ad.u.op->path = file->f_path;
+
+ if (ssid != fsec->sid) {
+ rc = avc_has_perm(ssid, fsec->sid,
+ SECCLASS_FD,
+ FD__USE,
+ &ad);
+ if (rc)
+ goto out;
+ }
+
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
+ rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
+ requested, driver, xperm, &ad);
+out:
+ return rc;
+}
+
static int selinux_file_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -3275,7 +3314,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
* to the file's ioctl() function.
*/
default:
- error = file_has_perm(cred, file, FILE__IOCTL);
+ error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
}
return error;
}
@@ -3320,12 +3359,7 @@ error:
static int selinux_mmap_addr(unsigned long addr)
{
- int rc;
-
- /* do DAC check on address space usage */
- rc = cap_mmap_addr(addr);
- if (rc)
- return rc;
+ int rc = 0;
if (addr < CONFIG_LSM_MMAP_MIN_ADDR) {
u32 sid = current_sid();
@@ -3641,23 +3675,11 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
static int selinux_task_setnice(struct task_struct *p, int nice)
{
- int rc;
-
- rc = cap_task_setnice(p, nice);
- if (rc)
- return rc;
-
return current_has_perm(p, PROCESS__SETSCHED);
}
static int selinux_task_setioprio(struct task_struct *p, int ioprio)
{
- int rc;
-
- rc = cap_task_setioprio(p, ioprio);
- if (rc)
- return rc;
-
return current_has_perm(p, PROCESS__SETSCHED);
}
@@ -3683,12 +3705,6 @@ static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
static int selinux_task_setscheduler(struct task_struct *p)
{
- int rc;
-
- rc = cap_task_setscheduler(p);
- if (rc)
- return rc;
-
return current_has_perm(p, PROCESS__SETSCHED);
}
@@ -4548,6 +4564,7 @@ static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority
sksec->peer_sid = SECINITSID_UNLABELED;
sksec->sid = SECINITSID_UNLABELED;
+ sksec->sclass = SECCLASS_SOCKET;
selinux_netlbl_sk_security_reset(sksec);
sk->sk_security = sksec;
@@ -4770,8 +4787,9 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
if (err == -EINVAL) {
printk(KERN_WARNING
"SELinux: unrecognized netlink message:"
- " protocol=%hu nlmsg_type=%hu sclass=%hu\n",
- sk->sk_protocol, nlh->nlmsg_type, sksec->sclass);
+ " protocol=%hu nlmsg_type=%hu sclass=%s\n",
+ sk->sk_protocol, nlh->nlmsg_type,
+ secclass_map[sksec->sclass - 1].name);
if (!selinux_enforcing || security_get_allow_unknown())
err = 0;
}
@@ -4846,7 +4864,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv4_forward(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
@@ -4854,7 +4872,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv6_forward(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
@@ -4878,7 +4896,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
if (sk) {
struct sk_security_struct *sksec;
- if (sk->sk_state == TCP_LISTEN)
+ if (sk_listener(sk))
/* if the socket is the listening state then this
* packet is a SYN-ACK packet which means it needs to
* be labeled based on the connection/request_sock and
@@ -4904,7 +4922,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb,
return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv4_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
@@ -4915,7 +4933,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
int ifindex,
u16 family)
{
- struct sock *sk = skb->sk;
+ struct sock *sk = skb_to_full_sk(skb);
struct sk_security_struct *sksec;
struct common_audit_data ad;
struct lsm_network_audit net = {0,};
@@ -4970,7 +4988,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
if (!secmark_active && !peerlbl_active)
return NF_ACCEPT;
- sk = skb->sk;
+ sk = skb_to_full_sk(skb);
#ifdef CONFIG_XFRM
/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
@@ -4985,7 +5003,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
* unfortunately, this means more work, but it is only once per
* connection. */
if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL &&
- !(sk != NULL && sk->sk_state == TCP_LISTEN))
+ !(sk && sk_listener(sk)))
return NF_ACCEPT;
#endif
@@ -5002,7 +5020,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
secmark_perm = PACKET__SEND;
peer_sid = SECINITSID_KERNEL;
}
- } else if (sk->sk_state == TCP_LISTEN) {
+ } else if (sk_listener(sk)) {
/* Locally generated packet but the associated socket is in the
* listening state which means this is a SYN-ACK packet. In
* this particular case the correct security label is assigned
@@ -5013,7 +5031,9 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
* selinux_inet_conn_request(). See also selinux_ip_output()
* for similar problems. */
u32 skb_sid;
- struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *sksec;
+
+ sksec = sk->sk_security;
if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
return NF_DROP;
/* At this point, if the returned skb peerlbl is SECSID_NULL
@@ -5079,7 +5099,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
return NF_ACCEPT;
}
-static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv4_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
@@ -5087,7 +5107,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
+static unsigned int selinux_ipv6_postroute(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
@@ -5099,12 +5119,6 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
{
- int err;
-
- err = cap_netlink_send(sk, skb);
- if (err)
- return err;
-
return selinux_nlmsg_perm(sk, skb);
}
@@ -5842,218 +5856,220 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
#endif
-static struct security_operations selinux_ops = {
- .name = "selinux",
-
- .binder_set_context_mgr = selinux_binder_set_context_mgr,
- .binder_transaction = selinux_binder_transaction,
- .binder_transfer_binder = selinux_binder_transfer_binder,
- .binder_transfer_file = selinux_binder_transfer_file,
-
- .ptrace_access_check = selinux_ptrace_access_check,
- .ptrace_traceme = selinux_ptrace_traceme,
- .capget = selinux_capget,
- .capset = selinux_capset,
- .capable = selinux_capable,
- .quotactl = selinux_quotactl,
- .quota_on = selinux_quota_on,
- .syslog = selinux_syslog,
- .vm_enough_memory = selinux_vm_enough_memory,
-
- .netlink_send = selinux_netlink_send,
-
- .bprm_set_creds = selinux_bprm_set_creds,
- .bprm_committing_creds = selinux_bprm_committing_creds,
- .bprm_committed_creds = selinux_bprm_committed_creds,
- .bprm_secureexec = selinux_bprm_secureexec,
-
- .sb_alloc_security = selinux_sb_alloc_security,
- .sb_free_security = selinux_sb_free_security,
- .sb_copy_data = selinux_sb_copy_data,
- .sb_remount = selinux_sb_remount,
- .sb_kern_mount = selinux_sb_kern_mount,
- .sb_show_options = selinux_sb_show_options,
- .sb_statfs = selinux_sb_statfs,
- .sb_mount = selinux_mount,
- .sb_umount = selinux_umount,
- .sb_set_mnt_opts = selinux_set_mnt_opts,
- .sb_clone_mnt_opts = selinux_sb_clone_mnt_opts,
- .sb_parse_opts_str = selinux_parse_opts_str,
-
- .dentry_init_security = selinux_dentry_init_security,
-
- .inode_alloc_security = selinux_inode_alloc_security,
- .inode_free_security = selinux_inode_free_security,
- .inode_init_security = selinux_inode_init_security,
- .inode_create = selinux_inode_create,
- .inode_link = selinux_inode_link,
- .inode_unlink = selinux_inode_unlink,
- .inode_symlink = selinux_inode_symlink,
- .inode_mkdir = selinux_inode_mkdir,
- .inode_rmdir = selinux_inode_rmdir,
- .inode_mknod = selinux_inode_mknod,
- .inode_rename = selinux_inode_rename,
- .inode_readlink = selinux_inode_readlink,
- .inode_follow_link = selinux_inode_follow_link,
- .inode_permission = selinux_inode_permission,
- .inode_setattr = selinux_inode_setattr,
- .inode_getattr = selinux_inode_getattr,
- .inode_setxattr = selinux_inode_setxattr,
- .inode_post_setxattr = selinux_inode_post_setxattr,
- .inode_getxattr = selinux_inode_getxattr,
- .inode_listxattr = selinux_inode_listxattr,
- .inode_removexattr = selinux_inode_removexattr,
- .inode_getsecurity = selinux_inode_getsecurity,
- .inode_setsecurity = selinux_inode_setsecurity,
- .inode_listsecurity = selinux_inode_listsecurity,
- .inode_getsecid = selinux_inode_getsecid,
-
- .file_permission = selinux_file_permission,
- .file_alloc_security = selinux_file_alloc_security,
- .file_free_security = selinux_file_free_security,
- .file_ioctl = selinux_file_ioctl,
- .mmap_file = selinux_mmap_file,
- .mmap_addr = selinux_mmap_addr,
- .file_mprotect = selinux_file_mprotect,
- .file_lock = selinux_file_lock,
- .file_fcntl = selinux_file_fcntl,
- .file_set_fowner = selinux_file_set_fowner,
- .file_send_sigiotask = selinux_file_send_sigiotask,
- .file_receive = selinux_file_receive,
-
- .file_open = selinux_file_open,
-
- .task_create = selinux_task_create,
- .cred_alloc_blank = selinux_cred_alloc_blank,
- .cred_free = selinux_cred_free,
- .cred_prepare = selinux_cred_prepare,
- .cred_transfer = selinux_cred_transfer,
- .kernel_act_as = selinux_kernel_act_as,
- .kernel_create_files_as = selinux_kernel_create_files_as,
- .kernel_module_request = selinux_kernel_module_request,
- .task_setpgid = selinux_task_setpgid,
- .task_getpgid = selinux_task_getpgid,
- .task_getsid = selinux_task_getsid,
- .task_getsecid = selinux_task_getsecid,
- .task_setnice = selinux_task_setnice,
- .task_setioprio = selinux_task_setioprio,
- .task_getioprio = selinux_task_getioprio,
- .task_setrlimit = selinux_task_setrlimit,
- .task_setscheduler = selinux_task_setscheduler,
- .task_getscheduler = selinux_task_getscheduler,
- .task_movememory = selinux_task_movememory,
- .task_kill = selinux_task_kill,
- .task_wait = selinux_task_wait,
- .task_to_inode = selinux_task_to_inode,
-
- .ipc_permission = selinux_ipc_permission,
- .ipc_getsecid = selinux_ipc_getsecid,
-
- .msg_msg_alloc_security = selinux_msg_msg_alloc_security,
- .msg_msg_free_security = selinux_msg_msg_free_security,
-
- .msg_queue_alloc_security = selinux_msg_queue_alloc_security,
- .msg_queue_free_security = selinux_msg_queue_free_security,
- .msg_queue_associate = selinux_msg_queue_associate,
- .msg_queue_msgctl = selinux_msg_queue_msgctl,
- .msg_queue_msgsnd = selinux_msg_queue_msgsnd,
- .msg_queue_msgrcv = selinux_msg_queue_msgrcv,
-
- .shm_alloc_security = selinux_shm_alloc_security,
- .shm_free_security = selinux_shm_free_security,
- .shm_associate = selinux_shm_associate,
- .shm_shmctl = selinux_shm_shmctl,
- .shm_shmat = selinux_shm_shmat,
-
- .sem_alloc_security = selinux_sem_alloc_security,
- .sem_free_security = selinux_sem_free_security,
- .sem_associate = selinux_sem_associate,
- .sem_semctl = selinux_sem_semctl,
- .sem_semop = selinux_sem_semop,
-
- .d_instantiate = selinux_d_instantiate,
-
- .getprocattr = selinux_getprocattr,
- .setprocattr = selinux_setprocattr,
-
- .ismaclabel = selinux_ismaclabel,
- .secid_to_secctx = selinux_secid_to_secctx,
- .secctx_to_secid = selinux_secctx_to_secid,
- .release_secctx = selinux_release_secctx,
- .inode_notifysecctx = selinux_inode_notifysecctx,
- .inode_setsecctx = selinux_inode_setsecctx,
- .inode_getsecctx = selinux_inode_getsecctx,
-
- .unix_stream_connect = selinux_socket_unix_stream_connect,
- .unix_may_send = selinux_socket_unix_may_send,
-
- .socket_create = selinux_socket_create,
- .socket_post_create = selinux_socket_post_create,
- .socket_bind = selinux_socket_bind,
- .socket_connect = selinux_socket_connect,
- .socket_listen = selinux_socket_listen,
- .socket_accept = selinux_socket_accept,
- .socket_sendmsg = selinux_socket_sendmsg,
- .socket_recvmsg = selinux_socket_recvmsg,
- .socket_getsockname = selinux_socket_getsockname,
- .socket_getpeername = selinux_socket_getpeername,
- .socket_getsockopt = selinux_socket_getsockopt,
- .socket_setsockopt = selinux_socket_setsockopt,
- .socket_shutdown = selinux_socket_shutdown,
- .socket_sock_rcv_skb = selinux_socket_sock_rcv_skb,
- .socket_getpeersec_stream = selinux_socket_getpeersec_stream,
- .socket_getpeersec_dgram = selinux_socket_getpeersec_dgram,
- .sk_alloc_security = selinux_sk_alloc_security,
- .sk_free_security = selinux_sk_free_security,
- .sk_clone_security = selinux_sk_clone_security,
- .sk_getsecid = selinux_sk_getsecid,
- .sock_graft = selinux_sock_graft,
- .inet_conn_request = selinux_inet_conn_request,
- .inet_csk_clone = selinux_inet_csk_clone,
- .inet_conn_established = selinux_inet_conn_established,
- .secmark_relabel_packet = selinux_secmark_relabel_packet,
- .secmark_refcount_inc = selinux_secmark_refcount_inc,
- .secmark_refcount_dec = selinux_secmark_refcount_dec,
- .req_classify_flow = selinux_req_classify_flow,
- .tun_dev_alloc_security = selinux_tun_dev_alloc_security,
- .tun_dev_free_security = selinux_tun_dev_free_security,
- .tun_dev_create = selinux_tun_dev_create,
- .tun_dev_attach_queue = selinux_tun_dev_attach_queue,
- .tun_dev_attach = selinux_tun_dev_attach,
- .tun_dev_open = selinux_tun_dev_open,
+static struct security_hook_list selinux_hooks[] = {
+ LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
+ LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
+ LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder),
+ LSM_HOOK_INIT(binder_transfer_file, selinux_binder_transfer_file),
+
+ LSM_HOOK_INIT(ptrace_access_check, selinux_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, selinux_ptrace_traceme),
+ LSM_HOOK_INIT(capget, selinux_capget),
+ LSM_HOOK_INIT(capset, selinux_capset),
+ LSM_HOOK_INIT(capable, selinux_capable),
+ LSM_HOOK_INIT(quotactl, selinux_quotactl),
+ LSM_HOOK_INIT(quota_on, selinux_quota_on),
+ LSM_HOOK_INIT(syslog, selinux_syslog),
+ LSM_HOOK_INIT(vm_enough_memory, selinux_vm_enough_memory),
+
+ LSM_HOOK_INIT(netlink_send, selinux_netlink_send),
+
+ LSM_HOOK_INIT(bprm_set_creds, selinux_bprm_set_creds),
+ LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
+ LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
+ LSM_HOOK_INIT(bprm_secureexec, selinux_bprm_secureexec),
+
+ LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
+ LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
+ LSM_HOOK_INIT(sb_copy_data, selinux_sb_copy_data),
+ LSM_HOOK_INIT(sb_remount, selinux_sb_remount),
+ LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
+ LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
+ LSM_HOOK_INIT(sb_statfs, selinux_sb_statfs),
+ LSM_HOOK_INIT(sb_mount, selinux_mount),
+ LSM_HOOK_INIT(sb_umount, selinux_umount),
+ LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
+ LSM_HOOK_INIT(sb_clone_mnt_opts, selinux_sb_clone_mnt_opts),
+ LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str),
+
+ LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
+
+ LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security),
+ LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security),
+ LSM_HOOK_INIT(inode_init_security, selinux_inode_init_security),
+ LSM_HOOK_INIT(inode_create, selinux_inode_create),
+ LSM_HOOK_INIT(inode_link, selinux_inode_link),
+ LSM_HOOK_INIT(inode_unlink, selinux_inode_unlink),
+ LSM_HOOK_INIT(inode_symlink, selinux_inode_symlink),
+ LSM_HOOK_INIT(inode_mkdir, selinux_inode_mkdir),
+ LSM_HOOK_INIT(inode_rmdir, selinux_inode_rmdir),
+ LSM_HOOK_INIT(inode_mknod, selinux_inode_mknod),
+ LSM_HOOK_INIT(inode_rename, selinux_inode_rename),
+ LSM_HOOK_INIT(inode_readlink, selinux_inode_readlink),
+ LSM_HOOK_INIT(inode_follow_link, selinux_inode_follow_link),
+ LSM_HOOK_INIT(inode_permission, selinux_inode_permission),
+ LSM_HOOK_INIT(inode_setattr, selinux_inode_setattr),
+ LSM_HOOK_INIT(inode_getattr, selinux_inode_getattr),
+ LSM_HOOK_INIT(inode_setxattr, selinux_inode_setxattr),
+ LSM_HOOK_INIT(inode_post_setxattr, selinux_inode_post_setxattr),
+ LSM_HOOK_INIT(inode_getxattr, selinux_inode_getxattr),
+ LSM_HOOK_INIT(inode_listxattr, selinux_inode_listxattr),
+ LSM_HOOK_INIT(inode_removexattr, selinux_inode_removexattr),
+ LSM_HOOK_INIT(inode_getsecurity, selinux_inode_getsecurity),
+ LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
+ LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
+ LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
+
+ LSM_HOOK_INIT(file_permission, selinux_file_permission),
+ LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
+ LSM_HOOK_INIT(file_free_security, selinux_file_free_security),
+ LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
+ LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
+ LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
+ LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
+ LSM_HOOK_INIT(file_lock, selinux_file_lock),
+ LSM_HOOK_INIT(file_fcntl, selinux_file_fcntl),
+ LSM_HOOK_INIT(file_set_fowner, selinux_file_set_fowner),
+ LSM_HOOK_INIT(file_send_sigiotask, selinux_file_send_sigiotask),
+ LSM_HOOK_INIT(file_receive, selinux_file_receive),
+
+ LSM_HOOK_INIT(file_open, selinux_file_open),
+
+ LSM_HOOK_INIT(task_create, selinux_task_create),
+ LSM_HOOK_INIT(cred_alloc_blank, selinux_cred_alloc_blank),
+ LSM_HOOK_INIT(cred_free, selinux_cred_free),
+ LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
+ LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
+ LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
+ LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
+ LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
+ LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
+ LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
+ LSM_HOOK_INIT(task_getsid, selinux_task_getsid),
+ LSM_HOOK_INIT(task_getsecid, selinux_task_getsecid),
+ LSM_HOOK_INIT(task_setnice, selinux_task_setnice),
+ LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio),
+ LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio),
+ LSM_HOOK_INIT(task_setrlimit, selinux_task_setrlimit),
+ LSM_HOOK_INIT(task_setscheduler, selinux_task_setscheduler),
+ LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler),
+ LSM_HOOK_INIT(task_movememory, selinux_task_movememory),
+ LSM_HOOK_INIT(task_kill, selinux_task_kill),
+ LSM_HOOK_INIT(task_wait, selinux_task_wait),
+ LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode),
+
+ LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission),
+ LSM_HOOK_INIT(ipc_getsecid, selinux_ipc_getsecid),
+
+ LSM_HOOK_INIT(msg_msg_alloc_security, selinux_msg_msg_alloc_security),
+ LSM_HOOK_INIT(msg_msg_free_security, selinux_msg_msg_free_security),
+
+ LSM_HOOK_INIT(msg_queue_alloc_security,
+ selinux_msg_queue_alloc_security),
+ LSM_HOOK_INIT(msg_queue_free_security, selinux_msg_queue_free_security),
+ LSM_HOOK_INIT(msg_queue_associate, selinux_msg_queue_associate),
+ LSM_HOOK_INIT(msg_queue_msgctl, selinux_msg_queue_msgctl),
+ LSM_HOOK_INIT(msg_queue_msgsnd, selinux_msg_queue_msgsnd),
+ LSM_HOOK_INIT(msg_queue_msgrcv, selinux_msg_queue_msgrcv),
+
+ LSM_HOOK_INIT(shm_alloc_security, selinux_shm_alloc_security),
+ LSM_HOOK_INIT(shm_free_security, selinux_shm_free_security),
+ LSM_HOOK_INIT(shm_associate, selinux_shm_associate),
+ LSM_HOOK_INIT(shm_shmctl, selinux_shm_shmctl),
+ LSM_HOOK_INIT(shm_shmat, selinux_shm_shmat),
+
+ LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security),
+ LSM_HOOK_INIT(sem_free_security, selinux_sem_free_security),
+ LSM_HOOK_INIT(sem_associate, selinux_sem_associate),
+ LSM_HOOK_INIT(sem_semctl, selinux_sem_semctl),
+ LSM_HOOK_INIT(sem_semop, selinux_sem_semop),
+
+ LSM_HOOK_INIT(d_instantiate, selinux_d_instantiate),
+
+ LSM_HOOK_INIT(getprocattr, selinux_getprocattr),
+ LSM_HOOK_INIT(setprocattr, selinux_setprocattr),
+
+ LSM_HOOK_INIT(ismaclabel, selinux_ismaclabel),
+ LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
+ LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid),
+ LSM_HOOK_INIT(release_secctx, selinux_release_secctx),
+ LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx),
+ LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
+ LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
+
+ LSM_HOOK_INIT(unix_stream_connect, selinux_socket_unix_stream_connect),
+ LSM_HOOK_INIT(unix_may_send, selinux_socket_unix_may_send),
+
+ LSM_HOOK_INIT(socket_create, selinux_socket_create),
+ LSM_HOOK_INIT(socket_post_create, selinux_socket_post_create),
+ LSM_HOOK_INIT(socket_bind, selinux_socket_bind),
+ LSM_HOOK_INIT(socket_connect, selinux_socket_connect),
+ LSM_HOOK_INIT(socket_listen, selinux_socket_listen),
+ LSM_HOOK_INIT(socket_accept, selinux_socket_accept),
+ LSM_HOOK_INIT(socket_sendmsg, selinux_socket_sendmsg),
+ LSM_HOOK_INIT(socket_recvmsg, selinux_socket_recvmsg),
+ LSM_HOOK_INIT(socket_getsockname, selinux_socket_getsockname),
+ LSM_HOOK_INIT(socket_getpeername, selinux_socket_getpeername),
+ LSM_HOOK_INIT(socket_getsockopt, selinux_socket_getsockopt),
+ LSM_HOOK_INIT(socket_setsockopt, selinux_socket_setsockopt),
+ LSM_HOOK_INIT(socket_shutdown, selinux_socket_shutdown),
+ LSM_HOOK_INIT(socket_sock_rcv_skb, selinux_socket_sock_rcv_skb),
+ LSM_HOOK_INIT(socket_getpeersec_stream,
+ selinux_socket_getpeersec_stream),
+ LSM_HOOK_INIT(socket_getpeersec_dgram, selinux_socket_getpeersec_dgram),
+ LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security),
+ LSM_HOOK_INIT(sk_free_security, selinux_sk_free_security),
+ LSM_HOOK_INIT(sk_clone_security, selinux_sk_clone_security),
+ LSM_HOOK_INIT(sk_getsecid, selinux_sk_getsecid),
+ LSM_HOOK_INIT(sock_graft, selinux_sock_graft),
+ LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request),
+ LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone),
+ LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established),
+ LSM_HOOK_INIT(secmark_relabel_packet, selinux_secmark_relabel_packet),
+ LSM_HOOK_INIT(secmark_refcount_inc, selinux_secmark_refcount_inc),
+ LSM_HOOK_INIT(secmark_refcount_dec, selinux_secmark_refcount_dec),
+ LSM_HOOK_INIT(req_classify_flow, selinux_req_classify_flow),
+ LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security),
+ LSM_HOOK_INIT(tun_dev_free_security, selinux_tun_dev_free_security),
+ LSM_HOOK_INIT(tun_dev_create, selinux_tun_dev_create),
+ LSM_HOOK_INIT(tun_dev_attach_queue, selinux_tun_dev_attach_queue),
+ LSM_HOOK_INIT(tun_dev_attach, selinux_tun_dev_attach),
+ LSM_HOOK_INIT(tun_dev_open, selinux_tun_dev_open),
#ifdef CONFIG_SECURITY_NETWORK_XFRM
- .xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
- .xfrm_policy_clone_security = selinux_xfrm_policy_clone,
- .xfrm_policy_free_security = selinux_xfrm_policy_free,
- .xfrm_policy_delete_security = selinux_xfrm_policy_delete,
- .xfrm_state_alloc = selinux_xfrm_state_alloc,
- .xfrm_state_alloc_acquire = selinux_xfrm_state_alloc_acquire,
- .xfrm_state_free_security = selinux_xfrm_state_free,
- .xfrm_state_delete_security = selinux_xfrm_state_delete,
- .xfrm_policy_lookup = selinux_xfrm_policy_lookup,
- .xfrm_state_pol_flow_match = selinux_xfrm_state_pol_flow_match,
- .xfrm_decode_session = selinux_xfrm_decode_session,
+ LSM_HOOK_INIT(xfrm_policy_alloc_security, selinux_xfrm_policy_alloc),
+ LSM_HOOK_INIT(xfrm_policy_clone_security, selinux_xfrm_policy_clone),
+ LSM_HOOK_INIT(xfrm_policy_free_security, selinux_xfrm_policy_free),
+ LSM_HOOK_INIT(xfrm_policy_delete_security, selinux_xfrm_policy_delete),
+ LSM_HOOK_INIT(xfrm_state_alloc, selinux_xfrm_state_alloc),
+ LSM_HOOK_INIT(xfrm_state_alloc_acquire,
+ selinux_xfrm_state_alloc_acquire),
+ LSM_HOOK_INIT(xfrm_state_free_security, selinux_xfrm_state_free),
+ LSM_HOOK_INIT(xfrm_state_delete_security, selinux_xfrm_state_delete),
+ LSM_HOOK_INIT(xfrm_policy_lookup, selinux_xfrm_policy_lookup),
+ LSM_HOOK_INIT(xfrm_state_pol_flow_match,
+ selinux_xfrm_state_pol_flow_match),
+ LSM_HOOK_INIT(xfrm_decode_session, selinux_xfrm_decode_session),
#endif
#ifdef CONFIG_KEYS
- .key_alloc = selinux_key_alloc,
- .key_free = selinux_key_free,
- .key_permission = selinux_key_permission,
- .key_getsecurity = selinux_key_getsecurity,
+ LSM_HOOK_INIT(key_alloc, selinux_key_alloc),
+ LSM_HOOK_INIT(key_free, selinux_key_free),
+ LSM_HOOK_INIT(key_permission, selinux_key_permission),
+ LSM_HOOK_INIT(key_getsecurity, selinux_key_getsecurity),
#endif
#ifdef CONFIG_AUDIT
- .audit_rule_init = selinux_audit_rule_init,
- .audit_rule_known = selinux_audit_rule_known,
- .audit_rule_match = selinux_audit_rule_match,
- .audit_rule_free = selinux_audit_rule_free,
+ LSM_HOOK_INIT(audit_rule_init, selinux_audit_rule_init),
+ LSM_HOOK_INIT(audit_rule_known, selinux_audit_rule_known),
+ LSM_HOOK_INIT(audit_rule_match, selinux_audit_rule_match),
+ LSM_HOOK_INIT(audit_rule_free, selinux_audit_rule_free),
#endif
};
static __init int selinux_init(void)
{
- if (!security_module_enable(&selinux_ops)) {
+ if (!security_module_enable("selinux")) {
selinux_enabled = 0;
return 0;
}
@@ -6073,10 +6089,12 @@ static __init int selinux_init(void)
sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),
0, SLAB_PANIC, NULL);
+ file_security_cache = kmem_cache_create("selinux_file_security",
+ sizeof(struct file_security_struct),
+ 0, SLAB_PANIC, NULL);
avc_init();
- if (register_security(&selinux_ops))
- panic("SELinux: Unable to register with kernel.\n");
+ security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC netcache callback\n");
@@ -6112,21 +6130,18 @@ security_initcall(selinux_init);
static struct nf_hook_ops selinux_nf_ops[] = {
{
.hook = selinux_ipv4_postroute,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_SELINUX_LAST,
},
{
.hook = selinux_ipv4_forward,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SELINUX_FIRST,
},
{
.hook = selinux_ipv4_output,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST,
@@ -6134,14 +6149,12 @@ static struct nf_hook_ops selinux_nf_ops[] = {
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
{
.hook = selinux_ipv6_postroute,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_SELINUX_LAST,
},
{
.hook = selinux_ipv6_forward,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_SELINUX_FIRST,
@@ -6204,7 +6217,7 @@ int selinux_disable(void)
selinux_disabled = 1;
selinux_enabled = 0;
- reset_security_ops();
+ security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
/* Try to destroy the avc node cache */
avc_disable();
diff --git a/kernel/security/selinux/include/avc.h b/kernel/security/selinux/include/avc.h
index ddf8eec03..0999df03a 100644
--- a/kernel/security/selinux/include/avc.h
+++ b/kernel/security/selinux/include/avc.h
@@ -130,7 +130,8 @@ static inline int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
- struct common_audit_data *a)
+ struct common_audit_data *a,
+ int flags)
{
u32 audited, denied;
audited = avc_audit_required(requested, avd, result, 0, &denied);
@@ -138,10 +139,11 @@ static inline int avc_audit(u32 ssid, u32 tsid,
return 0;
return slow_avc_audit(ssid, tsid, tclass,
requested, audited, denied, result,
- a, 0);
+ a, flags);
}
#define AVC_STRICT 1 /* Ignore permissive mode. */
+#define AVC_EXTENDED_PERMS 2 /* update extended permissions */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
unsigned flags,
@@ -150,6 +152,14 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct common_audit_data *auditdata);
+int avc_has_perm_flags(u32 ssid, u32 tsid,
+ u16 tclass, u32 requested,
+ struct common_audit_data *auditdata,
+ int flags);
+
+int avc_has_extended_perms(u32 ssid, u32 tsid, u16 tclass, u32 requested,
+ u8 driver, u8 perm, struct common_audit_data *ad);
+
u32 avc_policy_seqno(void);
@@ -161,6 +171,7 @@ u32 avc_policy_seqno(void);
#define AVC_CALLBACK_AUDITALLOW_DISABLE 32
#define AVC_CALLBACK_AUDITDENY_ENABLE 64
#define AVC_CALLBACK_AUDITDENY_DISABLE 128
+#define AVC_CALLBACK_ADD_XPERMS 256
int avc_add_callback(int (*callback)(u32 event), u32 events);
diff --git a/kernel/security/selinux/include/classmap.h b/kernel/security/selinux/include/classmap.h
index eccd61b3d..5a4eef59a 100644
--- a/kernel/security/selinux/include/classmap.h
+++ b/kernel/security/selinux/include/classmap.h
@@ -2,12 +2,12 @@
"getattr", "setattr", "lock", "relabelfrom", "relabelto", "append"
#define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \
- "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \
+ "rename", "execute", "quotaon", "mounton", "audit_access", \
"open", "execmod"
#define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \
"listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \
- "sendto", "recv_msg", "send_msg", "name_bind"
+ "sendto", "name_bind"
#define COMMON_IPC_PERMS "create", "destroy", "getattr", "setattr", "read", \
"write", "associate", "unix_read", "unix_write"
@@ -44,7 +44,7 @@ struct security_class_mapping secclass_map[] = {
"audit_control", "setfcap", NULL } },
{ "filesystem",
{ "mount", "remount", "unmount", "getattr",
- "relabelfrom", "relabelto", "transition", "associate", "quotamod",
+ "relabelfrom", "relabelto", "associate", "quotamod",
"quotaget", NULL } },
{ "file",
{ COMMON_FILE_PERMS,
@@ -67,7 +67,7 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS, NULL } },
{ "tcp_socket",
{ COMMON_SOCK_PERMS,
- "connectto", "newconn", "acceptfrom", "node_bind", "name_connect",
+ "node_bind", "name_connect",
NULL } },
{ "udp_socket",
{ COMMON_SOCK_PERMS,
@@ -76,13 +76,9 @@ struct security_class_mapping secclass_map[] = {
{ COMMON_SOCK_PERMS,
"node_bind", NULL } },
{ "node",
- { "tcp_recv", "tcp_send", "udp_recv", "udp_send",
- "rawip_recv", "rawip_send", "enforce_dest",
- "dccp_recv", "dccp_send", "recvfrom", "sendto", NULL } },
+ { "recvfrom", "sendto", NULL } },
{ "netif",
- { "tcp_recv", "tcp_send", "udp_recv", "udp_send",
- "rawip_recv", "rawip_send", "dccp_recv", "dccp_send",
- "ingress", "egress", NULL } },
+ { "ingress", "egress", NULL } },
{ "netlink_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "packet_socket",
@@ -90,11 +86,9 @@ struct security_class_mapping secclass_map[] = {
{ "key_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "unix_stream_socket",
- { COMMON_SOCK_PERMS, "connectto", "newconn", "acceptfrom", NULL
- } },
+ { COMMON_SOCK_PERMS, "connectto", NULL } },
{ "unix_dgram_socket",
- { COMMON_SOCK_PERMS, NULL
- } },
+ { COMMON_SOCK_PERMS, NULL } },
{ "sem",
{ COMMON_IPC_PERMS, NULL } },
{ "msg", { "send", "receive", NULL } },
@@ -107,9 +101,6 @@ struct security_class_mapping secclass_map[] = {
{ "netlink_route_socket",
{ COMMON_SOCK_PERMS,
"nlmsg_read", "nlmsg_write", NULL } },
- { "netlink_firewall_socket",
- { COMMON_SOCK_PERMS,
- "nlmsg_read", "nlmsg_write", NULL } },
{ "netlink_tcpdiag_socket",
{ COMMON_SOCK_PERMS,
"nlmsg_read", "nlmsg_write", NULL } },
@@ -120,19 +111,32 @@ struct security_class_mapping secclass_map[] = {
"nlmsg_read", "nlmsg_write", NULL } },
{ "netlink_selinux_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "netlink_iscsi_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ "netlink_audit_socket",
{ COMMON_SOCK_PERMS,
"nlmsg_read", "nlmsg_write", "nlmsg_relay", "nlmsg_readpriv",
"nlmsg_tty_audit", NULL } },
- { "netlink_ip6fw_socket",
- { COMMON_SOCK_PERMS,
- "nlmsg_read", "nlmsg_write", NULL } },
+ { "netlink_fib_lookup_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "netlink_connector_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "netlink_netfilter_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ "netlink_dnrt_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "association",
{ "sendto", "recvfrom", "setcontext", "polmatch", NULL } },
{ "netlink_kobject_uevent_socket",
{ COMMON_SOCK_PERMS, NULL } },
+ { "netlink_generic_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "netlink_scsitransport_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "netlink_rdma_socket",
+ { COMMON_SOCK_PERMS, NULL } },
+ { "netlink_crypto_socket",
+ { COMMON_SOCK_PERMS, NULL } },
{ "appletalk_socket",
{ COMMON_SOCK_PERMS, NULL } },
{ "packet",
diff --git a/kernel/security/selinux/include/security.h b/kernel/security/selinux/include/security.h
index d1e0b239b..223e9fd15 100644
--- a/kernel/security/selinux/include/security.h
+++ b/kernel/security/selinux/include/security.h
@@ -35,13 +35,14 @@
#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27
#define POLICYDB_VERSION_DEFAULT_TYPE 28
#define POLICYDB_VERSION_CONSTRAINT_NAMES 29
+#define POLICYDB_VERSION_XPERMS_IOCTL 30
/* Range of policy versions we understand*/
#define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE
#ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX
#define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE
#else
-#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES
+#define POLICYDB_VERSION_MAX POLICYDB_VERSION_XPERMS_IOCTL
#endif
/* Mask for just the mount related flags */
@@ -56,6 +57,7 @@
/* Non-mount related flags */
#define SE_SBINITIALIZED 0x0100
#define SE_SBPROC 0x0200
+#define SE_SBGENFS 0x0400
#define CONTEXT_STR "context="
#define FSCONTEXT_STR "fscontext="
@@ -108,11 +110,38 @@ struct av_decision {
u32 flags;
};
+#define XPERMS_ALLOWED 1
+#define XPERMS_AUDITALLOW 2
+#define XPERMS_DONTAUDIT 4
+
+#define security_xperm_set(perms, x) (perms[x >> 5] |= 1 << (x & 0x1f))
+#define security_xperm_test(perms, x) (1 & (perms[x >> 5] >> (x & 0x1f)))
+struct extended_perms_data {
+ u32 p[8];
+};
+
+struct extended_perms_decision {
+ u8 used;
+ u8 driver;
+ struct extended_perms_data *allowed;
+ struct extended_perms_data *auditallow;
+ struct extended_perms_data *dontaudit;
+};
+
+struct extended_perms {
+ u16 len; /* length associated decision chain */
+ struct extended_perms_data drivers; /* flag drivers that are used */
+};
+
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE 0x0001
void security_compute_av(u32 ssid, u32 tsid,
- u16 tclass, struct av_decision *avd);
+ u16 tclass, struct av_decision *avd,
+ struct extended_perms *xperms);
+
+void security_compute_xperms_decision(u32 ssid, u32 tsid, u16 tclass,
+ u8 driver, struct extended_perms_decision *xpermd);
void security_compute_av_user(u32 ssid, u32 tsid,
u16 tclass, struct av_decision *avd);
@@ -137,6 +166,8 @@ int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len);
int security_context_to_sid(const char *scontext, u32 scontext_len,
u32 *out_sid, gfp_t gfp);
+int security_context_str_to_sid(const char *scontext, u32 *out_sid, gfp_t gfp);
+
int security_context_to_sid_default(const char *scontext, u32 scontext_len,
u32 *out_sid, u32 def_sid, gfp_t gfp_flags);
diff --git a/kernel/security/selinux/netlabel.c b/kernel/security/selinux/netlabel.c
index 0364120d1..1f989a539 100644
--- a/kernel/security/selinux/netlabel.c
+++ b/kernel/security/selinux/netlabel.c
@@ -245,7 +245,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb,
/* if this is a locally generated packet check to see if it is already
* being labeled by it's parent socket, if it is just exit */
- sk = skb->sk;
+ sk = skb_to_full_sk(skb);
if (sk != NULL) {
struct sk_security_struct *sksec = sk->sk_security;
if (sksec->nlbl_state != NLBL_REQSKB)
diff --git a/kernel/security/selinux/selinuxfs.c b/kernel/security/selinux/selinuxfs.c
index 3d2201413..c02da25d7 100644
--- a/kernel/security/selinux/selinuxfs.c
+++ b/kernel/security/selinux/selinuxfs.c
@@ -472,7 +472,7 @@ static int sel_mmap_policy_fault(struct vm_area_struct *vma,
return 0;
}
-static struct vm_operations_struct sel_mmap_policy_ops = {
+static const struct vm_operations_struct sel_mmap_policy_ops = {
.fault = sel_mmap_policy_fault,
.page_mkwrite = sel_mmap_policy_fault,
};
@@ -731,13 +731,11 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out;
- length = security_context_to_sid(scon, strlen(scon) + 1, &ssid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
@@ -819,13 +817,11 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
objname = namebuf;
}
- length = security_context_to_sid(scon, strlen(scon) + 1, &ssid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
@@ -882,13 +878,11 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out;
- length = security_context_to_sid(scon, strlen(scon) + 1, &ssid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
@@ -940,7 +934,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s", con, user) != 2)
goto out;
- length = security_context_to_sid(con, strlen(con) + 1, &sid, GFP_KERNEL);
+ length = security_context_str_to_sid(con, &sid, GFP_KERNEL);
if (length)
goto out;
@@ -1000,13 +994,11 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out;
- length = security_context_to_sid(scon, strlen(scon) + 1, &ssid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL);
if (length)
goto out;
- length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid,
- GFP_KERNEL);
+ length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL);
if (length)
goto out;
diff --git a/kernel/security/selinux/ss/avtab.c b/kernel/security/selinux/ss/avtab.c
index b64f2772b..3628d3a86 100644
--- a/kernel/security/selinux/ss/avtab.c
+++ b/kernel/security/selinux/ss/avtab.c
@@ -24,6 +24,7 @@
#include "policydb.h"
static struct kmem_cache *avtab_node_cachep;
+static struct kmem_cache *avtab_xperms_cachep;
/* Based on MurmurHash3, written by Austin Appleby and placed in the
* public domain.
@@ -70,11 +71,24 @@ avtab_insert_node(struct avtab *h, int hvalue,
struct avtab_key *key, struct avtab_datum *datum)
{
struct avtab_node *newnode;
+ struct avtab_extended_perms *xperms;
newnode = kmem_cache_zalloc(avtab_node_cachep, GFP_KERNEL);
if (newnode == NULL)
return NULL;
newnode->key = *key;
- newnode->datum = *datum;
+
+ if (key->specified & AVTAB_XPERMS) {
+ xperms = kmem_cache_zalloc(avtab_xperms_cachep, GFP_KERNEL);
+ if (xperms == NULL) {
+ kmem_cache_free(avtab_node_cachep, newnode);
+ return NULL;
+ }
+ *xperms = *(datum->u.xperms);
+ newnode->datum.u.xperms = xperms;
+ } else {
+ newnode->datum.u.data = datum->u.data;
+ }
+
if (prev) {
newnode->next = prev->next;
prev->next = newnode;
@@ -107,8 +121,12 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat
if (key->source_type == cur->key.source_type &&
key->target_type == cur->key.target_type &&
key->target_class == cur->key.target_class &&
- (specified & cur->key.specified))
+ (specified & cur->key.specified)) {
+ /* extended perms may not be unique */
+ if (specified & AVTAB_XPERMS)
+ break;
return -EEXIST;
+ }
if (key->source_type < cur->key.source_type)
break;
if (key->source_type == cur->key.source_type &&
@@ -271,6 +289,9 @@ void avtab_destroy(struct avtab *h)
while (cur) {
temp = cur;
cur = cur->next;
+ if (temp->key.specified & AVTAB_XPERMS)
+ kmem_cache_free(avtab_xperms_cachep,
+ temp->datum.u.xperms);
kmem_cache_free(avtab_node_cachep, temp);
}
}
@@ -359,7 +380,10 @@ static uint16_t spec_order[] = {
AVTAB_AUDITALLOW,
AVTAB_TRANSITION,
AVTAB_CHANGE,
- AVTAB_MEMBER
+ AVTAB_MEMBER,
+ AVTAB_XPERMS_ALLOWED,
+ AVTAB_XPERMS_AUDITALLOW,
+ AVTAB_XPERMS_DONTAUDIT
};
int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
@@ -369,10 +393,11 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
{
__le16 buf16[4];
u16 enabled;
- __le32 buf32[7];
u32 items, items2, val, vers = pol->policyvers;
struct avtab_key key;
struct avtab_datum datum;
+ struct avtab_extended_perms xperms;
+ __le32 buf32[ARRAY_SIZE(xperms.perms.p)];
int i, rc;
unsigned set;
@@ -429,11 +454,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n");
return -EINVAL;
}
+ if (val & AVTAB_XPERMS) {
+ printk(KERN_ERR "SELinux: avtab: entry has extended permissions\n");
+ return -EINVAL;
+ }
for (i = 0; i < ARRAY_SIZE(spec_order); i++) {
if (val & spec_order[i]) {
key.specified = spec_order[i] | enabled;
- datum.data = le32_to_cpu(buf32[items++]);
+ datum.u.data = le32_to_cpu(buf32[items++]);
rc = insertf(a, &key, &datum, p);
if (rc)
return rc;
@@ -476,14 +505,42 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol,
return -EINVAL;
}
- rc = next_entry(buf32, fp, sizeof(u32));
- if (rc) {
- printk(KERN_ERR "SELinux: avtab: truncated entry\n");
- return rc;
+ if ((vers < POLICYDB_VERSION_XPERMS_IOCTL) &&
+ (key.specified & AVTAB_XPERMS)) {
+ printk(KERN_ERR "SELinux: avtab: policy version %u does not "
+ "support extended permissions rules and one "
+ "was specified\n", vers);
+ return -EINVAL;
+ } else if (key.specified & AVTAB_XPERMS) {
+ memset(&xperms, 0, sizeof(struct avtab_extended_perms));
+ rc = next_entry(&xperms.specified, fp, sizeof(u8));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ rc = next_entry(&xperms.driver, fp, sizeof(u8));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ rc = next_entry(buf32, fp, sizeof(u32)*ARRAY_SIZE(xperms.perms.p));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ for (i = 0; i < ARRAY_SIZE(xperms.perms.p); i++)
+ xperms.perms.p[i] = le32_to_cpu(buf32[i]);
+ datum.u.xperms = &xperms;
+ } else {
+ rc = next_entry(buf32, fp, sizeof(u32));
+ if (rc) {
+ printk(KERN_ERR "SELinux: avtab: truncated entry\n");
+ return rc;
+ }
+ datum.u.data = le32_to_cpu(*buf32);
}
- datum.data = le32_to_cpu(*buf32);
if ((key.specified & AVTAB_TYPE) &&
- !policydb_type_isvalid(pol, datum.data)) {
+ !policydb_type_isvalid(pol, datum.u.data)) {
printk(KERN_ERR "SELinux: avtab: invalid type\n");
return -EINVAL;
}
@@ -543,8 +600,9 @@ bad:
int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
{
__le16 buf16[4];
- __le32 buf32[1];
+ __le32 buf32[ARRAY_SIZE(cur->datum.u.xperms->perms.p)];
int rc;
+ unsigned int i;
buf16[0] = cpu_to_le16(cur->key.source_type);
buf16[1] = cpu_to_le16(cur->key.target_type);
@@ -553,8 +611,22 @@ int avtab_write_item(struct policydb *p, struct avtab_node *cur, void *fp)
rc = put_entry(buf16, sizeof(u16), 4, fp);
if (rc)
return rc;
- buf32[0] = cpu_to_le32(cur->datum.data);
- rc = put_entry(buf32, sizeof(u32), 1, fp);
+
+ if (cur->key.specified & AVTAB_XPERMS) {
+ rc = put_entry(&cur->datum.u.xperms->specified, sizeof(u8), 1, fp);
+ if (rc)
+ return rc;
+ rc = put_entry(&cur->datum.u.xperms->driver, sizeof(u8), 1, fp);
+ if (rc)
+ return rc;
+ for (i = 0; i < ARRAY_SIZE(cur->datum.u.xperms->perms.p); i++)
+ buf32[i] = cpu_to_le32(cur->datum.u.xperms->perms.p[i]);
+ rc = put_entry(buf32, sizeof(u32),
+ ARRAY_SIZE(cur->datum.u.xperms->perms.p), fp);
+ } else {
+ buf32[0] = cpu_to_le32(cur->datum.u.data);
+ rc = put_entry(buf32, sizeof(u32), 1, fp);
+ }
if (rc)
return rc;
return 0;
@@ -588,9 +660,13 @@ void avtab_cache_init(void)
avtab_node_cachep = kmem_cache_create("avtab_node",
sizeof(struct avtab_node),
0, SLAB_PANIC, NULL);
+ avtab_xperms_cachep = kmem_cache_create("avtab_extended_perms",
+ sizeof(struct avtab_extended_perms),
+ 0, SLAB_PANIC, NULL);
}
void avtab_cache_destroy(void)
{
kmem_cache_destroy(avtab_node_cachep);
+ kmem_cache_destroy(avtab_xperms_cachep);
}
diff --git a/kernel/security/selinux/ss/avtab.h b/kernel/security/selinux/ss/avtab.h
index adb451cd4..d946c9dc3 100644
--- a/kernel/security/selinux/ss/avtab.h
+++ b/kernel/security/selinux/ss/avtab.h
@@ -23,6 +23,7 @@
#ifndef _SS_AVTAB_H_
#define _SS_AVTAB_H_
+#include "security.h"
#include <linux/flex_array.h>
struct avtab_key {
@@ -37,13 +38,43 @@ struct avtab_key {
#define AVTAB_MEMBER 0x0020
#define AVTAB_CHANGE 0x0040
#define AVTAB_TYPE (AVTAB_TRANSITION | AVTAB_MEMBER | AVTAB_CHANGE)
+/* extended permissions */
+#define AVTAB_XPERMS_ALLOWED 0x0100
+#define AVTAB_XPERMS_AUDITALLOW 0x0200
+#define AVTAB_XPERMS_DONTAUDIT 0x0400
+#define AVTAB_XPERMS (AVTAB_XPERMS_ALLOWED | \
+ AVTAB_XPERMS_AUDITALLOW | \
+ AVTAB_XPERMS_DONTAUDIT)
#define AVTAB_ENABLED_OLD 0x80000000 /* reserved for used in cond_avtab */
#define AVTAB_ENABLED 0x8000 /* reserved for used in cond_avtab */
u16 specified; /* what field is specified */
};
+/*
+ * For operations that require more than the 32 permissions provided by the avc
+ * extended permissions may be used to provide 256 bits of permissions.
+ */
+struct avtab_extended_perms {
+/* These are not flags. All 256 values may be used */
+#define AVTAB_XPERMS_IOCTLFUNCTION 0x01
+#define AVTAB_XPERMS_IOCTLDRIVER 0x02
+ /* extension of the avtab_key specified */
+ u8 specified; /* ioctl, netfilter, ... */
+ /*
+ * if 256 bits is not adequate as is often the case with ioctls, then
+ * multiple extended perms may be used and the driver field
+ * specifies which permissions are included.
+ */
+ u8 driver;
+ /* 256 bits of permissions */
+ struct extended_perms_data perms;
+};
+
struct avtab_datum {
- u32 data; /* access vector or type value */
+ union {
+ u32 data; /* access vector or type value */
+ struct avtab_extended_perms *xperms;
+ } u;
};
struct avtab_node {
diff --git a/kernel/security/selinux/ss/conditional.c b/kernel/security/selinux/ss/conditional.c
index 62c6773be..456e1a9bc 100644
--- a/kernel/security/selinux/ss/conditional.c
+++ b/kernel/security/selinux/ss/conditional.c
@@ -15,6 +15,7 @@
#include "security.h"
#include "conditional.h"
+#include "services.h"
/*
* cond_evaluate_expr evaluates a conditional expr
@@ -612,10 +613,28 @@ int cond_write_list(struct policydb *p, struct cond_node *list, void *fp)
return 0;
}
+
+void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
+ struct extended_perms_decision *xpermd)
+{
+ struct avtab_node *node;
+
+ if (!ctab || !key || !xpermd)
+ return;
+
+ for (node = avtab_search_node(ctab, key); node;
+ node = avtab_search_node_next(node, key->specified)) {
+ if (node->key.specified & AVTAB_ENABLED)
+ services_compute_xperms_decision(xpermd, node);
+ }
+ return;
+
+}
/* Determine whether additional permissions are granted by the conditional
* av table, and if so, add them to the result
*/
-void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd)
+void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
+ struct av_decision *avd, struct extended_perms *xperms)
{
struct avtab_node *node;
@@ -626,7 +645,7 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
node = avtab_search_node_next(node, key->specified)) {
if ((u16)(AVTAB_ALLOWED|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_ALLOWED|AVTAB_ENABLED)))
- avd->allowed |= node->datum.data;
+ avd->allowed |= node->datum.u.data;
if ((u16)(AVTAB_AUDITDENY|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITDENY|AVTAB_ENABLED)))
/* Since a '0' in an auditdeny mask represents a
@@ -634,10 +653,13 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decisi
* the '&' operand to ensure that all '0's in the mask
* are retained (much unlike the allow and auditallow cases).
*/
- avd->auditdeny &= node->datum.data;
+ avd->auditdeny &= node->datum.u.data;
if ((u16)(AVTAB_AUDITALLOW|AVTAB_ENABLED) ==
(node->key.specified & (AVTAB_AUDITALLOW|AVTAB_ENABLED)))
- avd->auditallow |= node->datum.data;
+ avd->auditallow |= node->datum.u.data;
+ if (xperms && (node->key.specified & AVTAB_ENABLED) &&
+ (node->key.specified & AVTAB_XPERMS))
+ services_compute_xperms_drivers(xperms, node);
}
return;
}
diff --git a/kernel/security/selinux/ss/conditional.h b/kernel/security/selinux/ss/conditional.h
index 4d1f87466..ddb43e7e1 100644
--- a/kernel/security/selinux/ss/conditional.h
+++ b/kernel/security/selinux/ss/conditional.h
@@ -73,8 +73,10 @@ int cond_read_list(struct policydb *p, void *fp);
int cond_write_bool(void *key, void *datum, void *ptr);
int cond_write_list(struct policydb *p, struct cond_node *list, void *fp);
-void cond_compute_av(struct avtab *ctab, struct avtab_key *key, struct av_decision *avd);
-
+void cond_compute_av(struct avtab *ctab, struct avtab_key *key,
+ struct av_decision *avd, struct extended_perms *xperms);
+void cond_compute_xperms(struct avtab *ctab, struct avtab_key *key,
+ struct extended_perms_decision *xpermd);
int evaluate_cond_node(struct policydb *p, struct cond_node *node);
#endif /* _CONDITIONAL_H_ */
diff --git a/kernel/security/selinux/ss/policydb.c b/kernel/security/selinux/ss/policydb.c
index 74aa22426..992a31530 100644
--- a/kernel/security/selinux/ss/policydb.c
+++ b/kernel/security/selinux/ss/policydb.c
@@ -148,6 +148,11 @@ static struct policydb_compat_info policydb_compat[] = {
.sym_num = SYM_NUM,
.ocon_num = OCON_NUM,
},
+ {
+ .version = POLICYDB_VERSION_XPERMS_IOCTL,
+ .sym_num = SYM_NUM,
+ .ocon_num = OCON_NUM,
+ },
};
static struct policydb_compat_info *policydb_lookup_compat(int version)
diff --git a/kernel/security/selinux/ss/services.c b/kernel/security/selinux/ss/services.c
index 9e2d82070..ebb5eb3c3 100644
--- a/kernel/security/selinux/ss/services.c
+++ b/kernel/security/selinux/ss/services.c
@@ -93,9 +93,10 @@ static int context_struct_to_string(struct context *context, char **scontext,
u32 *scontext_len);
static void context_struct_compute_av(struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- struct av_decision *avd);
+ struct context *tcontext,
+ u16 tclass,
+ struct av_decision *avd,
+ struct extended_perms *xperms);
struct selinux_mapping {
u16 value; /* policy value */
@@ -565,7 +566,8 @@ static void type_attribute_bounds_av(struct context *scontext,
context_struct_compute_av(&lo_scontext,
tcontext,
tclass,
- &lo_avd);
+ &lo_avd,
+ NULL);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
masked = ~lo_avd.allowed & avd->allowed;
@@ -580,7 +582,8 @@ static void type_attribute_bounds_av(struct context *scontext,
context_struct_compute_av(scontext,
&lo_tcontext,
tclass,
- &lo_avd);
+ &lo_avd,
+ NULL);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
masked = ~lo_avd.allowed & avd->allowed;
@@ -596,7 +599,8 @@ static void type_attribute_bounds_av(struct context *scontext,
context_struct_compute_av(&lo_scontext,
&lo_tcontext,
tclass,
- &lo_avd);
+ &lo_avd,
+ NULL);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
masked = ~lo_avd.allowed & avd->allowed;
@@ -613,13 +617,39 @@ static void type_attribute_bounds_av(struct context *scontext,
}
/*
- * Compute access vectors based on a context structure pair for
- * the permissions in a particular class.
+ * flag which drivers have permissions
+ * only looking for ioctl based extended permssions
+ */
+void services_compute_xperms_drivers(
+ struct extended_perms *xperms,
+ struct avtab_node *node)
+{
+ unsigned int i;
+
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ /* if one or more driver has all permissions allowed */
+ for (i = 0; i < ARRAY_SIZE(xperms->drivers.p); i++)
+ xperms->drivers.p[i] |= node->datum.u.xperms->perms.p[i];
+ } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
+ /* if allowing permissions within a driver */
+ security_xperm_set(xperms->drivers.p,
+ node->datum.u.xperms->driver);
+ }
+
+ /* If no ioctl commands are allowed, ignore auditallow and auditdeny */
+ if (node->key.specified & AVTAB_XPERMS_ALLOWED)
+ xperms->len = 1;
+}
+
+/*
+ * Compute access vectors and extended permissions based on a context
+ * structure pair for the permissions in a particular class.
*/
static void context_struct_compute_av(struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- struct av_decision *avd)
+ struct context *tcontext,
+ u16 tclass,
+ struct av_decision *avd,
+ struct extended_perms *xperms)
{
struct constraint_node *constraint;
struct role_allow *ra;
@@ -633,6 +663,10 @@ static void context_struct_compute_av(struct context *scontext,
avd->allowed = 0;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
+ if (xperms) {
+ memset(&xperms->drivers, 0, sizeof(xperms->drivers));
+ xperms->len = 0;
+ }
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
if (printk_ratelimit())
@@ -647,7 +681,7 @@ static void context_struct_compute_av(struct context *scontext,
* this permission check, then use it.
*/
avkey.target_class = tclass;
- avkey.specified = AVTAB_AV;
+ avkey.specified = AVTAB_AV | AVTAB_XPERMS;
sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1);
BUG_ON(!sattr);
tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1);
@@ -660,15 +694,18 @@ static void context_struct_compute_av(struct context *scontext,
node;
node = avtab_search_node_next(node, avkey.specified)) {
if (node->key.specified == AVTAB_ALLOWED)
- avd->allowed |= node->datum.data;
+ avd->allowed |= node->datum.u.data;
else if (node->key.specified == AVTAB_AUDITALLOW)
- avd->auditallow |= node->datum.data;
+ avd->auditallow |= node->datum.u.data;
else if (node->key.specified == AVTAB_AUDITDENY)
- avd->auditdeny &= node->datum.data;
+ avd->auditdeny &= node->datum.u.data;
+ else if (xperms && (node->key.specified & AVTAB_XPERMS))
+ services_compute_xperms_drivers(xperms, node);
}
/* Check conditional av table for additional permissions */
- cond_compute_av(&policydb.te_cond_avtab, &avkey, avd);
+ cond_compute_av(&policydb.te_cond_avtab, &avkey,
+ avd, xperms);
}
}
@@ -899,6 +936,139 @@ static void avd_init(struct av_decision *avd)
avd->flags = 0;
}
+void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
+ struct avtab_node *node)
+{
+ unsigned int i;
+
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
+ if (xpermd->driver != node->datum.u.xperms->driver)
+ return;
+ } else if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ if (!security_xperm_test(node->datum.u.xperms->perms.p,
+ xpermd->driver))
+ return;
+ } else {
+ BUG();
+ }
+
+ if (node->key.specified == AVTAB_XPERMS_ALLOWED) {
+ xpermd->used |= XPERMS_ALLOWED;
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ memset(xpermd->allowed->p, 0xff,
+ sizeof(xpermd->allowed->p));
+ }
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
+ for (i = 0; i < ARRAY_SIZE(xpermd->allowed->p); i++)
+ xpermd->allowed->p[i] |=
+ node->datum.u.xperms->perms.p[i];
+ }
+ } else if (node->key.specified == AVTAB_XPERMS_AUDITALLOW) {
+ xpermd->used |= XPERMS_AUDITALLOW;
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ memset(xpermd->auditallow->p, 0xff,
+ sizeof(xpermd->auditallow->p));
+ }
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
+ for (i = 0; i < ARRAY_SIZE(xpermd->auditallow->p); i++)
+ xpermd->auditallow->p[i] |=
+ node->datum.u.xperms->perms.p[i];
+ }
+ } else if (node->key.specified == AVTAB_XPERMS_DONTAUDIT) {
+ xpermd->used |= XPERMS_DONTAUDIT;
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ memset(xpermd->dontaudit->p, 0xff,
+ sizeof(xpermd->dontaudit->p));
+ }
+ if (node->datum.u.xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) {
+ for (i = 0; i < ARRAY_SIZE(xpermd->dontaudit->p); i++)
+ xpermd->dontaudit->p[i] |=
+ node->datum.u.xperms->perms.p[i];
+ }
+ } else {
+ BUG();
+ }
+}
+
+void security_compute_xperms_decision(u32 ssid,
+ u32 tsid,
+ u16 orig_tclass,
+ u8 driver,
+ struct extended_perms_decision *xpermd)
+{
+ u16 tclass;
+ struct context *scontext, *tcontext;
+ struct avtab_key avkey;
+ struct avtab_node *node;
+ struct ebitmap *sattr, *tattr;
+ struct ebitmap_node *snode, *tnode;
+ unsigned int i, j;
+
+ xpermd->driver = driver;
+ xpermd->used = 0;
+ memset(xpermd->allowed->p, 0, sizeof(xpermd->allowed->p));
+ memset(xpermd->auditallow->p, 0, sizeof(xpermd->auditallow->p));
+ memset(xpermd->dontaudit->p, 0, sizeof(xpermd->dontaudit->p));
+
+ read_lock(&policy_rwlock);
+ if (!ss_initialized)
+ goto allow;
+
+ scontext = sidtab_search(&sidtab, ssid);
+ if (!scontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, ssid);
+ goto out;
+ }
+
+ tcontext = sidtab_search(&sidtab, tsid);
+ if (!tcontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, tsid);
+ goto out;
+ }
+
+ tclass = unmap_class(orig_tclass);
+ if (unlikely(orig_tclass && !tclass)) {
+ if (policydb.allow_unknown)
+ goto allow;
+ goto out;
+ }
+
+
+ if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
+ pr_warn_ratelimited("SELinux: Invalid class %hu\n", tclass);
+ goto out;
+ }
+
+ avkey.target_class = tclass;
+ avkey.specified = AVTAB_XPERMS;
+ sattr = flex_array_get(policydb.type_attr_map_array,
+ scontext->type - 1);
+ BUG_ON(!sattr);
+ tattr = flex_array_get(policydb.type_attr_map_array,
+ tcontext->type - 1);
+ BUG_ON(!tattr);
+ ebitmap_for_each_positive_bit(sattr, snode, i) {
+ ebitmap_for_each_positive_bit(tattr, tnode, j) {
+ avkey.source_type = i + 1;
+ avkey.target_type = j + 1;
+ for (node = avtab_search_node(&policydb.te_avtab, &avkey);
+ node;
+ node = avtab_search_node_next(node, avkey.specified))
+ services_compute_xperms_decision(xpermd, node);
+
+ cond_compute_xperms(&policydb.te_cond_avtab,
+ &avkey, xpermd);
+ }
+ }
+out:
+ read_unlock(&policy_rwlock);
+ return;
+allow:
+ memset(xpermd->allowed->p, 0xff, sizeof(xpermd->allowed->p));
+ goto out;
+}
/**
* security_compute_av - Compute access vector decisions.
@@ -906,6 +1076,7 @@ static void avd_init(struct av_decision *avd)
* @tsid: target security identifier
* @tclass: target security class
* @avd: access vector decisions
+ * @xperms: extended permissions
*
* Compute a set of access vector decisions based on the
* SID pair (@ssid, @tsid) for the permissions in @tclass.
@@ -913,13 +1084,15 @@ static void avd_init(struct av_decision *avd)
void security_compute_av(u32 ssid,
u32 tsid,
u16 orig_tclass,
- struct av_decision *avd)
+ struct av_decision *avd,
+ struct extended_perms *xperms)
{
u16 tclass;
struct context *scontext = NULL, *tcontext = NULL;
read_lock(&policy_rwlock);
avd_init(avd);
+ xperms->len = 0;
if (!ss_initialized)
goto allow;
@@ -947,7 +1120,7 @@ void security_compute_av(u32 ssid,
goto allow;
goto out;
}
- context_struct_compute_av(scontext, tcontext, tclass, avd);
+ context_struct_compute_av(scontext, tcontext, tclass, avd, xperms);
map_decision(orig_tclass, avd, policydb.allow_unknown);
out:
read_unlock(&policy_rwlock);
@@ -993,7 +1166,7 @@ void security_compute_av_user(u32 ssid,
goto out;
}
- context_struct_compute_av(scontext, tcontext, tclass, avd);
+ context_struct_compute_av(scontext, tcontext, tclass, avd, NULL);
out:
read_unlock(&policy_rwlock);
return;
@@ -1045,13 +1218,10 @@ static int context_struct_to_string(struct context *context, char **scontext, u3
/*
* Copy the user name, role name and type name into the context.
*/
- sprintf(scontextp, "%s:%s:%s",
+ scontextp += sprintf(scontextp, "%s:%s:%s",
sym_name(&policydb, SYM_USERS, context->user - 1),
sym_name(&policydb, SYM_ROLES, context->role - 1),
sym_name(&policydb, SYM_TYPES, context->type - 1));
- scontextp += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) +
- 1 + strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) +
- 1 + strlen(sym_name(&policydb, SYM_TYPES, context->type - 1));
mls_sid_to_context(context, &scontextp);
@@ -1086,12 +1256,12 @@ static int security_sid_to_context_core(u32 sid, char **scontext,
*scontext_len = strlen(initial_sid_to_string[sid]) + 1;
if (!scontext)
goto out;
- scontextp = kmalloc(*scontext_len, GFP_ATOMIC);
+ scontextp = kmemdup(initial_sid_to_string[sid],
+ *scontext_len, GFP_ATOMIC);
if (!scontextp) {
rc = -ENOMEM;
goto out;
}
- strcpy(scontextp, initial_sid_to_string[sid]);
*scontext = scontextp;
goto out;
}
@@ -1303,6 +1473,11 @@ int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid,
sid, SECSID_NULL, gfp, 0);
}
+int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp)
+{
+ return security_context_to_sid(scontext, strlen(scontext), sid, gfp);
+}
+
/**
* security_context_to_sid_default - Obtain a SID for a given security context,
* falling back to specified default if needed.
@@ -1515,7 +1690,7 @@ static int security_compute_sid(u32 ssid,
if (avdatum) {
/* Use the type from the type transition/member/change rule. */
- newcontext.type = avdatum->data;
+ newcontext.type = avdatum->u.data;
}
/* if we have a objname this is a file trans check so check those rules */
@@ -2431,18 +2606,12 @@ int security_get_bools(int *len, char ***names, int **values)
goto err;
for (i = 0; i < *len; i++) {
- size_t name_len;
-
(*values)[i] = policydb.bool_val_to_struct[i]->state;
- name_len = strlen(sym_name(&policydb, SYM_BOOLS, i)) + 1;
rc = -ENOMEM;
- (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC);
+ (*names)[i] = kstrdup(sym_name(&policydb, SYM_BOOLS, i), GFP_ATOMIC);
if (!(*names)[i])
goto err;
-
- strncpy((*names)[i], sym_name(&policydb, SYM_BOOLS, i), name_len);
- (*names)[i][name_len - 1] = 0;
}
rc = 0;
out:
diff --git a/kernel/security/selinux/ss/services.h b/kernel/security/selinux/ss/services.h
index e8d907e90..6abcd8729 100644
--- a/kernel/security/selinux/ss/services.h
+++ b/kernel/security/selinux/ss/services.h
@@ -11,5 +11,11 @@
extern struct policydb policydb;
+void services_compute_xperms_drivers(struct extended_perms *xperms,
+ struct avtab_node *node);
+
+void services_compute_xperms_decision(struct extended_perms_decision *xpermd,
+ struct avtab_node *node);
+
#endif /* _SS_SERVICES_H_ */
diff --git a/kernel/security/selinux/xfrm.c b/kernel/security/selinux/xfrm.c
index 98b042630..56e354fcd 100644
--- a/kernel/security/selinux/xfrm.c
+++ b/kernel/security/selinux/xfrm.c
@@ -35,9 +35,6 @@
#include <linux/init.h>
#include <linux/security.h>
#include <linux/types.h>
-#include <linux/netfilter.h>
-#include <linux/netfilter_ipv4.h>
-#include <linux/netfilter_ipv6.h>
#include <linux/slab.h>
#include <linux/ip.h>
#include <linux/tcp.h>
diff --git a/kernel/security/smack/smack.h b/kernel/security/smack/smack.h
index 49eada626..6c91156ae 100644
--- a/kernel/security/smack/smack.h
+++ b/kernel/security/smack/smack.h
@@ -15,14 +15,29 @@
#include <linux/capability.h>
#include <linux/spinlock.h>
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/in.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/in6.h>
+#endif /* CONFIG_IPV6 */
#include <net/netlabel.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/lsm_audit.h>
/*
+ * Use IPv6 port labeling if IPv6 is enabled and secmarks
+ * are not being used.
+ */
+#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#define SMACK_IPV6_PORT_LABELING 1
+#endif
+
+#if IS_ENABLED(CONFIG_IPV6) && defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#define SMACK_IPV6_SECMARK_LABELING 1
+#endif
+
+/*
* Smack labels were limited to 23 characters for a long time.
*/
#define SMK_LABELLEN 24
@@ -100,6 +115,7 @@ struct task_smack {
struct smack_known *smk_forked; /* label when forked */
struct list_head smk_rules; /* per task access rules */
struct mutex smk_rules_lock; /* lock for the rules */
+ struct list_head smk_relabel; /* transit allowed labels */
};
#define SMK_INODE_INSTANT 0x01 /* inode is instantiated */
@@ -118,15 +134,30 @@ struct smack_rule {
};
/*
- * An entry in the table identifying hosts.
+ * An entry in the table identifying IPv4 hosts.
*/
-struct smk_netlbladdr {
+struct smk_net4addr {
struct list_head list;
- struct sockaddr_in smk_host; /* network address */
+ struct in_addr smk_host; /* network address */
struct in_addr smk_mask; /* network mask */
+ int smk_masks; /* mask size */
struct smack_known *smk_label; /* label */
};
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * An entry in the table identifying IPv6 hosts.
+ */
+struct smk_net6addr {
+ struct list_head list;
+ struct in6_addr smk_host; /* network address */
+ struct in6_addr smk_mask; /* network mask */
+ int smk_masks; /* mask size */
+ struct smack_known *smk_label; /* label */
+};
+#endif /* CONFIG_IPV6 */
+
+#ifdef SMACK_IPV6_PORT_LABELING
/*
* An entry in the table identifying ports.
*/
@@ -137,6 +168,30 @@ struct smk_port_label {
struct smack_known *smk_in; /* inbound label */
struct smack_known *smk_out; /* outgoing label */
};
+#endif /* SMACK_IPV6_PORT_LABELING */
+
+struct smack_known_list_elem {
+ struct list_head list;
+ struct smack_known *smk_label;
+};
+
+/* Super block security struct flags for mount options */
+#define FSDEFAULT_MNT 0x01
+#define FSFLOOR_MNT 0x02
+#define FSHAT_MNT 0x04
+#define FSROOT_MNT 0x08
+#define FSTRANS_MNT 0x10
+
+#define NUM_SMK_MNT_OPTS 5
+
+enum {
+ Opt_error = -1,
+ Opt_fsdefault = 1,
+ Opt_fsfloor = 2,
+ Opt_fshat = 3,
+ Opt_fsroot = 4,
+ Opt_fstransmute = 5,
+};
/*
* Mount options
@@ -147,6 +202,7 @@ struct smk_port_label {
#define SMK_FSROOT "smackfsroot="
#define SMK_FSTRANS "smackfstransmute="
+#define SMACK_DELETE_OPTION "-DELETE"
#define SMACK_CIPSO_OPTION "-CIPSO"
/*
@@ -229,10 +285,6 @@ struct smk_audit_info {
struct smack_audit_data sad;
#endif
};
-/*
- * These functions are in smack_lsm.c
- */
-struct inode_smack *new_inode_smack(struct smack_known *);
/*
* These functions are in smack_access.c
@@ -249,6 +301,8 @@ int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int);
struct smack_known *smk_import_entry(const char *, int);
void smk_insert_entry(struct smack_known *skp);
struct smack_known *smk_find_entry(const char *);
+int smack_privileged(int cap);
+void smk_destroy_label_list(struct list_head *list);
/*
* Shared data.
@@ -257,12 +311,10 @@ extern int smack_enabled;
extern int smack_cipso_direct;
extern int smack_cipso_mapped;
extern struct smack_known *smack_net_ambient;
-extern struct smack_known *smack_onlycap;
extern struct smack_known *smack_syslog_label;
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
extern struct smack_known *smack_unconfined;
#endif
-extern struct smack_known smack_cipso_option;
extern int smack_ptrace_rule;
extern struct smack_known smack_known_floor;
@@ -274,9 +326,13 @@ extern struct smack_known smack_known_web;
extern struct mutex smack_known_lock;
extern struct list_head smack_known_list;
-extern struct list_head smk_netlbladdr_list;
+extern struct list_head smk_net4addr_list;
+#if IS_ENABLED(CONFIG_IPV6)
+extern struct list_head smk_net6addr_list;
+#endif /* CONFIG_IPV6 */
-extern struct security_operations smack_ops;
+extern struct mutex smack_onlycap_lock;
+extern struct list_head smack_onlycap_list;
#define SMACK_HASH_SLOTS 16
extern struct hlist_head smack_known_hash[SMACK_HASH_SLOTS];
@@ -334,21 +390,6 @@ static inline struct smack_known *smk_of_current(void)
}
/*
- * Is the task privileged and allowed to be privileged
- * by the onlycap rule.
- */
-static inline int smack_privileged(int cap)
-{
- struct smack_known *skp = smk_of_current();
-
- if (!capable(cap))
- return 0;
- if (smack_onlycap == NULL || smack_onlycap == skp)
- return 1;
- return 0;
-}
-
-/*
* logging functions
*/
#define SMACK_AUDIT_DENIED 0x1
diff --git a/kernel/security/smack/smack_access.c b/kernel/security/smack/smack_access.c
index 0f410fc56..a283f9e79 100644
--- a/kernel/security/smack/smack_access.c
+++ b/kernel/security/smack/smack_access.c
@@ -425,7 +425,7 @@ void smk_insert_entry(struct smack_known *skp)
* @string: a text string that might be a Smack label
*
* Returns a pointer to the entry in the label list that
- * matches the passed string.
+ * matches the passed string or NULL if not found.
*/
struct smack_known *smk_find_entry(const char *string)
{
@@ -448,7 +448,7 @@ struct smack_known *smk_find_entry(const char *string)
* @string: a text string that might contain a Smack label
* @len: the maximum size, or zero if it is NULL terminated.
*
- * Returns a pointer to the clean label, or NULL
+ * Returns a pointer to the clean label or an error code.
*/
char *smk_parse_smack(const char *string, int len)
{
@@ -464,7 +464,7 @@ char *smk_parse_smack(const char *string, int len)
* including /smack/cipso and /smack/cipso2
*/
if (string[0] == '-')
- return NULL;
+ return ERR_PTR(-EINVAL);
for (i = 0; i < len; i++)
if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' ||
@@ -472,11 +472,13 @@ char *smk_parse_smack(const char *string, int len)
break;
if (i == 0 || i >= SMK_LONGLABEL)
- return NULL;
+ return ERR_PTR(-EINVAL);
smack = kzalloc(i + 1, GFP_KERNEL);
- if (smack != NULL)
- strncpy(smack, string, i);
+ if (smack == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ strncpy(smack, string, i);
return smack;
}
@@ -523,7 +525,8 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap,
* @len: the maximum size, or zero if it is NULL terminated.
*
* Returns a pointer to the entry in the label list that
- * matches the passed string, adding it if necessary.
+ * matches the passed string, adding it if necessary,
+ * or an error code.
*/
struct smack_known *smk_import_entry(const char *string, int len)
{
@@ -533,8 +536,8 @@ struct smack_known *smk_import_entry(const char *string, int len)
int rc;
smack = smk_parse_smack(string, len);
- if (smack == NULL)
- return NULL;
+ if (IS_ERR(smack))
+ return ERR_CAST(smack);
mutex_lock(&smack_known_lock);
@@ -543,8 +546,10 @@ struct smack_known *smk_import_entry(const char *string, int len)
goto freeout;
skp = kzalloc(sizeof(*skp), GFP_KERNEL);
- if (skp == NULL)
+ if (skp == NULL) {
+ skp = ERR_PTR(-ENOMEM);
goto freeout;
+ }
skp->smk_known = smack;
skp->smk_secid = smack_next_secid++;
@@ -577,7 +582,7 @@ struct smack_known *smk_import_entry(const char *string, int len)
* smk_netlbl_mls failed.
*/
kfree(skp);
- skp = NULL;
+ skp = ERR_PTR(rc);
freeout:
kfree(smack);
unlockout:
@@ -612,3 +617,50 @@ struct smack_known *smack_from_secid(const u32 secid)
rcu_read_unlock();
return &smack_known_invalid;
}
+
+/*
+ * Unless a process is running with one of these labels
+ * even having CAP_MAC_OVERRIDE isn't enough to grant
+ * privilege to violate MAC policy. If no labels are
+ * designated (the empty list case) capabilities apply to
+ * everyone.
+ */
+LIST_HEAD(smack_onlycap_list);
+DEFINE_MUTEX(smack_onlycap_lock);
+
+/*
+ * Is the task privileged and allowed to be privileged
+ * by the onlycap rule.
+ *
+ * Returns 1 if the task is allowed to be privileged, 0 if it's not.
+ */
+int smack_privileged(int cap)
+{
+ struct smack_known *skp = smk_of_current();
+ struct smack_known_list_elem *sklep;
+
+ /*
+ * All kernel tasks are privileged
+ */
+ if (unlikely(current->flags & PF_KTHREAD))
+ return 1;
+
+ if (!capable(cap))
+ return 0;
+
+ rcu_read_lock();
+ if (list_empty(&smack_onlycap_list)) {
+ rcu_read_unlock();
+ return 1;
+ }
+
+ list_for_each_entry_rcu(sklep, &smack_onlycap_list, list) {
+ if (sklep->smk_label == skp) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
+
+ return 0;
+}
diff --git a/kernel/security/smack/smack_lsm.c b/kernel/security/smack/smack_lsm.c
index b64475788..7c57c7fcf 100644
--- a/kernel/security/smack/smack_lsm.c
+++ b/kernel/security/smack/smack_lsm.c
@@ -41,6 +41,7 @@
#include <linux/msg.h>
#include <linux/shm.h>
#include <linux/binfmts.h>
+#include <linux/parser.h>
#include "smack.h"
#define TRANS_TRUE "TRUE"
@@ -50,12 +51,21 @@
#define SMK_RECEIVING 1
#define SMK_SENDING 2
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
-LIST_HEAD(smk_ipv6_port_list);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#ifdef SMACK_IPV6_PORT_LABELING
+static LIST_HEAD(smk_ipv6_port_list);
+#endif
static struct kmem_cache *smack_inode_cache;
int smack_enabled;
+static const match_table_t smk_mount_tokens = {
+ {Opt_fsdefault, SMK_FSDEFAULT "%s"},
+ {Opt_fsfloor, SMK_FSFLOOR "%s"},
+ {Opt_fshat, SMK_FSHAT "%s"},
+ {Opt_fsroot, SMK_FSROOT "%s"},
+ {Opt_fstransmute, SMK_FSTRANS "%s"},
+ {Opt_error, NULL},
+};
+
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
static char *smk_bu_mess[] = {
"Bringup Error", /* Unused */
@@ -245,8 +255,8 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file,
* @ip: a pointer to the inode
* @dp: a pointer to the dentry
*
- * Returns a pointer to the master list entry for the Smack label
- * or NULL if there was no label to fetch.
+ * Returns a pointer to the master list entry for the Smack label,
+ * NULL if there was no label to fetch, or an error code.
*/
static struct smack_known *smk_fetch(const char *name, struct inode *ip,
struct dentry *dp)
@@ -256,14 +266,18 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
struct smack_known *skp = NULL;
if (ip->i_op->getxattr == NULL)
- return NULL;
+ return ERR_PTR(-EOPNOTSUPP);
buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL);
if (buffer == NULL)
- return NULL;
+ return ERR_PTR(-ENOMEM);
rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL);
- if (rc > 0)
+ if (rc < 0)
+ skp = ERR_PTR(rc);
+ else if (rc == 0)
+ skp = NULL;
+ else
skp = smk_import_entry(buffer, rc);
kfree(buffer);
@@ -277,7 +291,7 @@ static struct smack_known *smk_fetch(const char *name, struct inode *ip,
*
* Returns the new blob or NULL if there's no memory available
*/
-struct inode_smack *new_inode_smack(struct smack_known *skp)
+static struct inode_smack *new_inode_smack(struct smack_known *skp)
{
struct inode_smack *isp;
@@ -312,6 +326,7 @@ static struct task_smack *new_task_smack(struct smack_known *task,
tsp->smk_task = task;
tsp->smk_forked = forked;
INIT_LIST_HEAD(&tsp->smk_rules);
+ INIT_LIST_HEAD(&tsp->smk_relabel);
mutex_init(&tsp->smk_rules_lock);
return tsp;
@@ -347,6 +362,35 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
}
/**
+ * smk_copy_relabel - copy smk_relabel labels list
+ * @nhead: new rules header pointer
+ * @ohead: old rules header pointer
+ * @gfp: type of the memory for the allocation
+ *
+ * Returns 0 on success, -ENOMEM on error
+ */
+static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead,
+ gfp_t gfp)
+{
+ struct smack_known_list_elem *nklep;
+ struct smack_known_list_elem *oklep;
+
+ INIT_LIST_HEAD(nhead);
+
+ list_for_each_entry(oklep, ohead, list) {
+ nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp);
+ if (nklep == NULL) {
+ smk_destroy_label_list(nhead);
+ return -ENOMEM;
+ }
+ nklep->smk_label = oklep->smk_label;
+ list_add(&nklep->list, nhead);
+ }
+
+ return 0;
+}
+
+/**
* smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_*
* @mode - input mode in form of PTRACE_MODE_*
*
@@ -354,12 +398,10 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead,
*/
static inline unsigned int smk_ptrace_mode(unsigned int mode)
{
- switch (mode) {
- case PTRACE_MODE_READ:
- return MAY_READ;
- case PTRACE_MODE_ATTACH:
+ if (mode & PTRACE_MODE_ATTACH)
return MAY_READWRITE;
- }
+ if (mode & PTRACE_MODE_READ)
+ return MAY_READ;
return 0;
}
@@ -436,17 +478,11 @@ static int smk_ptrace_rule_check(struct task_struct *tracer,
*/
static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
{
- int rc;
struct smack_known *skp;
- rc = cap_ptrace_access_check(ctp, mode);
- if (rc != 0)
- return rc;
-
skp = smk_of_task_struct(ctp);
- rc = smk_ptrace_rule_check(current, skp, mode, __func__);
- return rc;
+ return smk_ptrace_rule_check(current, skp, mode, __func__);
}
/**
@@ -462,10 +498,6 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
int rc;
struct smack_known *skp;
- rc = cap_ptrace_traceme(ptp);
- if (rc != 0)
- return rc;
-
skp = smk_of_task(current_security());
rc = smk_ptrace_rule_check(ptp, skp, PTRACE_MODE_ATTACH, __func__);
@@ -583,72 +615,197 @@ static int smack_sb_copy_data(char *orig, char *smackopts)
}
/**
- * smack_sb_kern_mount - Smack specific mount processing
+ * smack_parse_opts_str - parse Smack specific mount options
+ * @options: mount options string
+ * @opts: where to store converted mount opts
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ *
+ * converts Smack specific mount options to generic security option format
+ */
+static int smack_parse_opts_str(char *options,
+ struct security_mnt_opts *opts)
+{
+ char *p;
+ char *fsdefault = NULL;
+ char *fsfloor = NULL;
+ char *fshat = NULL;
+ char *fsroot = NULL;
+ char *fstransmute = NULL;
+ int rc = -ENOMEM;
+ int num_mnt_opts = 0;
+ int token;
+
+ opts->num_mnt_opts = 0;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, smk_mount_tokens, args);
+
+ switch (token) {
+ case Opt_fsdefault:
+ if (fsdefault)
+ goto out_opt_err;
+ fsdefault = match_strdup(&args[0]);
+ if (!fsdefault)
+ goto out_err;
+ break;
+ case Opt_fsfloor:
+ if (fsfloor)
+ goto out_opt_err;
+ fsfloor = match_strdup(&args[0]);
+ if (!fsfloor)
+ goto out_err;
+ break;
+ case Opt_fshat:
+ if (fshat)
+ goto out_opt_err;
+ fshat = match_strdup(&args[0]);
+ if (!fshat)
+ goto out_err;
+ break;
+ case Opt_fsroot:
+ if (fsroot)
+ goto out_opt_err;
+ fsroot = match_strdup(&args[0]);
+ if (!fsroot)
+ goto out_err;
+ break;
+ case Opt_fstransmute:
+ if (fstransmute)
+ goto out_opt_err;
+ fstransmute = match_strdup(&args[0]);
+ if (!fstransmute)
+ goto out_err;
+ break;
+ default:
+ rc = -EINVAL;
+ pr_warn("Smack: unknown mount option\n");
+ goto out_err;
+ }
+ }
+
+ opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_ATOMIC);
+ if (!opts->mnt_opts)
+ goto out_err;
+
+ opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
+ GFP_ATOMIC);
+ if (!opts->mnt_opts_flags) {
+ kfree(opts->mnt_opts);
+ goto out_err;
+ }
+
+ if (fsdefault) {
+ opts->mnt_opts[num_mnt_opts] = fsdefault;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSDEFAULT_MNT;
+ }
+ if (fsfloor) {
+ opts->mnt_opts[num_mnt_opts] = fsfloor;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSFLOOR_MNT;
+ }
+ if (fshat) {
+ opts->mnt_opts[num_mnt_opts] = fshat;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSHAT_MNT;
+ }
+ if (fsroot) {
+ opts->mnt_opts[num_mnt_opts] = fsroot;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSROOT_MNT;
+ }
+ if (fstransmute) {
+ opts->mnt_opts[num_mnt_opts] = fstransmute;
+ opts->mnt_opts_flags[num_mnt_opts++] = FSTRANS_MNT;
+ }
+
+ opts->num_mnt_opts = num_mnt_opts;
+ return 0;
+
+out_opt_err:
+ rc = -EINVAL;
+ pr_warn("Smack: duplicate mount options\n");
+
+out_err:
+ kfree(fsdefault);
+ kfree(fsfloor);
+ kfree(fshat);
+ kfree(fsroot);
+ kfree(fstransmute);
+ return rc;
+}
+
+/**
+ * smack_set_mnt_opts - set Smack specific mount options
* @sb: the file system superblock
- * @flags: the mount flags
- * @data: the smack mount options
+ * @opts: Smack mount options
+ * @kern_flags: mount option from kernel space or user space
+ * @set_kern_flags: where to store converted mount opts
*
* Returns 0 on success, an error code on failure
+ *
+ * Allow filesystems with binary mount data to explicitly set Smack mount
+ * labels.
*/
-static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
+static int smack_set_mnt_opts(struct super_block *sb,
+ struct security_mnt_opts *opts,
+ unsigned long kern_flags,
+ unsigned long *set_kern_flags)
{
struct dentry *root = sb->s_root;
struct inode *inode = d_backing_inode(root);
struct superblock_smack *sp = sb->s_security;
struct inode_smack *isp;
struct smack_known *skp;
- char *op;
- char *commap;
+ int i;
+ int num_opts = opts->num_mnt_opts;
int transmute = 0;
- int specified = 0;
if (sp->smk_initialized)
return 0;
sp->smk_initialized = 1;
- for (op = data; op != NULL; op = commap) {
- commap = strchr(op, ',');
- if (commap != NULL)
- *commap++ = '\0';
-
- if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) {
- op += strlen(SMK_FSHAT);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_hat = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) {
- op += strlen(SMK_FSFLOOR);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_floor = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSDEFAULT,
- strlen(SMK_FSDEFAULT)) == 0) {
- op += strlen(SMK_FSDEFAULT);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_default = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) {
- op += strlen(SMK_FSROOT);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_root = skp;
- specified = 1;
- }
- } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) {
- op += strlen(SMK_FSTRANS);
- skp = smk_import_entry(op, 0);
- if (skp != NULL) {
- sp->smk_root = skp;
- transmute = 1;
- specified = 1;
- }
+ for (i = 0; i < num_opts; i++) {
+ switch (opts->mnt_opts_flags[i]) {
+ case FSDEFAULT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_default = skp;
+ break;
+ case FSFLOOR_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_floor = skp;
+ break;
+ case FSHAT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_hat = skp;
+ break;
+ case FSROOT_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_root = skp;
+ break;
+ case FSTRANS_MNT:
+ skp = smk_import_entry(opts->mnt_opts[i], 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+ sp->smk_root = skp;
+ transmute = 1;
+ break;
+ default:
+ break;
}
}
@@ -656,7 +813,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
/*
* Unprivileged mounts don't get to specify Smack values.
*/
- if (specified)
+ if (num_opts)
return -EPERM;
/*
* Unprivileged mounts get root and default from the caller.
@@ -665,6 +822,7 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
sp->smk_root = skp;
sp->smk_default = skp;
}
+
/*
* Initialize the root inode.
*/
@@ -684,6 +842,37 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
}
/**
+ * smack_sb_kern_mount - Smack specific mount processing
+ * @sb: the file system superblock
+ * @flags: the mount flags
+ * @data: the smack mount options
+ *
+ * Returns 0 on success, an error code on failure
+ */
+static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data)
+{
+ int rc = 0;
+ char *options = data;
+ struct security_mnt_opts opts;
+
+ security_init_mnt_opts(&opts);
+
+ if (!options)
+ goto out;
+
+ rc = smack_parse_opts_str(options, &opts);
+ if (rc)
+ goto out_err;
+
+out:
+ rc = smack_set_mnt_opts(sb, &opts, 0, NULL);
+
+out_err:
+ security_free_mnt_opts(&opts);
+ return rc;
+}
+
+/**
* smack_sb_statfs - Smack check on statfs
* @dentry: identifies the file system in question
*
@@ -721,10 +910,6 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
struct inode_smack *isp;
int rc;
- rc = cap_bprm_set_creds(bprm);
- if (rc != 0)
- return rc;
-
if (bprm->cred_prepared)
return 0;
@@ -779,12 +964,11 @@ static void smack_bprm_committing_creds(struct linux_binprm *bprm)
static int smack_bprm_secureexec(struct linux_binprm *bprm)
{
struct task_smack *tsp = current_security();
- int ret = cap_bprm_secureexec(bprm);
- if (!ret && (tsp->smk_task != tsp->smk_forked))
- ret = 1;
+ if (tsp->smk_task != tsp->smk_forked)
+ return 1;
- return ret;
+ return 0;
}
/*
@@ -1133,7 +1317,9 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name,
if (rc == 0 && check_import) {
skp = size ? smk_import_entry(value, size) : NULL;
- if (skp == NULL || (check_star &&
+ if (IS_ERR(skp))
+ rc = PTR_ERR(skp);
+ else if (skp == NULL || (check_star &&
(skp == &smack_known_star || skp == &smack_known_web)))
rc = -EINVAL;
}
@@ -1173,19 +1359,19 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
if (strcmp(name, XATTR_NAME_SMACK) == 0) {
skp = smk_import_entry(value, size);
- if (skp != NULL)
+ if (!IS_ERR(skp))
isp->smk_inode = skp;
else
isp->smk_inode = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
skp = smk_import_entry(value, size);
- if (skp != NULL)
+ if (!IS_ERR(skp))
isp->smk_task = skp;
else
isp->smk_task = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
skp = smk_import_entry(value, size);
- if (skp != NULL)
+ if (!IS_ERR(skp))
isp->smk_mmap = skp;
else
isp->smk_mmap = &smack_known_invalid;
@@ -1673,6 +1859,9 @@ static int smack_file_receive(struct file *file)
struct smk_audit_info ad;
struct inode *inode = file_inode(file);
+ if (unlikely(IS_PRIVATE(inode)))
+ return 0;
+
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH);
smk_ad_setfield_u_fs_path(&ad, file->f_path);
/*
@@ -1761,6 +1950,8 @@ static void smack_cred_free(struct cred *cred)
return;
cred->security = NULL;
+ smk_destroy_label_list(&tsp->smk_relabel);
+
list_for_each_safe(l, n, &tsp->smk_rules) {
rp = list_entry(l, struct smack_rule, list);
list_del(&rp->list);
@@ -1792,6 +1983,11 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
if (rc != 0)
return rc;
+ rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel,
+ gfp);
+ if (rc != 0)
+ return rc;
+
new->security = new_tsp;
return 0;
}
@@ -1934,12 +2130,7 @@ static void smack_task_getsecid(struct task_struct *p, u32 *secid)
*/
static int smack_task_setnice(struct task_struct *p, int nice)
{
- int rc;
-
- rc = cap_task_setnice(p, nice);
- if (rc == 0)
- rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
- return rc;
+ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/**
@@ -1951,12 +2142,7 @@ static int smack_task_setnice(struct task_struct *p, int nice)
*/
static int smack_task_setioprio(struct task_struct *p, int ioprio)
{
- int rc;
-
- rc = cap_task_setioprio(p, ioprio);
- if (rc == 0)
- rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
- return rc;
+ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/**
@@ -1980,12 +2166,7 @@ static int smack_task_getioprio(struct task_struct *p)
*/
static int smack_task_setscheduler(struct task_struct *p)
{
- int rc;
-
- rc = cap_task_setscheduler(p);
- if (rc == 0)
- rc = smk_curacc_on_task(p, MAY_WRITE, __func__);
- return rc;
+ return smk_curacc_on_task(p, MAY_WRITE, __func__);
}
/**
@@ -2130,7 +2311,7 @@ static void smack_sk_free_security(struct sock *sk)
}
/**
-* smack_host_label - check host based restrictions
+* smack_ipv4host_label - check host based restrictions
* @sip: the object end
*
* looks for host based access restrictions
@@ -2141,30 +2322,96 @@ static void smack_sk_free_security(struct sock *sk)
*
* Returns the label of the far end or NULL if it's not special.
*/
-static struct smack_known *smack_host_label(struct sockaddr_in *sip)
+static struct smack_known *smack_ipv4host_label(struct sockaddr_in *sip)
{
- struct smk_netlbladdr *snp;
+ struct smk_net4addr *snp;
struct in_addr *siap = &sip->sin_addr;
if (siap->s_addr == 0)
return NULL;
- list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list)
+ list_for_each_entry_rcu(snp, &smk_net4addr_list, list)
+ /*
+ * we break after finding the first match because
+ * the list is sorted from longest to shortest mask
+ * so we have found the most specific match
+ */
+ if (snp->smk_host.s_addr ==
+ (siap->s_addr & snp->smk_mask.s_addr))
+ return snp->smk_label;
+
+ return NULL;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * smk_ipv6_localhost - Check for local ipv6 host address
+ * @sip: the address
+ *
+ * Returns boolean true if this is the localhost address
+ */
+static bool smk_ipv6_localhost(struct sockaddr_in6 *sip)
+{
+ __be16 *be16p = (__be16 *)&sip->sin6_addr;
+ __be32 *be32p = (__be32 *)&sip->sin6_addr;
+
+ if (be32p[0] == 0 && be32p[1] == 0 && be32p[2] == 0 && be16p[6] == 0 &&
+ ntohs(be16p[7]) == 1)
+ return true;
+ return false;
+}
+
+/**
+* smack_ipv6host_label - check host based restrictions
+* @sip: the object end
+*
+* looks for host based access restrictions
+*
+* This version will only be appropriate for really small sets of single label
+* hosts. The caller is responsible for ensuring that the RCU read lock is
+* taken before calling this function.
+*
+* Returns the label of the far end or NULL if it's not special.
+*/
+static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
+{
+ struct smk_net6addr *snp;
+ struct in6_addr *sap = &sip->sin6_addr;
+ int i;
+ int found = 0;
+
+ /*
+ * It's local. Don't look for a host label.
+ */
+ if (smk_ipv6_localhost(sip))
+ return NULL;
+
+ list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
/*
* we break after finding the first match because
* the list is sorted from longest to shortest mask
* so we have found the most specific match
*/
- if ((&snp->smk_host.sin_addr)->s_addr ==
- (siap->s_addr & (&snp->smk_mask)->s_addr)) {
- /* we have found the special CIPSO option */
- if (snp->smk_label == &smack_cipso_option)
- return NULL;
- return snp->smk_label;
+ for (found = 1, i = 0; i < 8; i++) {
+ /*
+ * If the label is NULL the entry has
+ * been renounced. Ignore it.
+ */
+ if (snp->smk_label == NULL)
+ continue;
+ if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
+ snp->smk_host.s6_addr16[i]) {
+ found = 0;
+ break;
+ }
}
+ if (found)
+ return snp->smk_label;
+ }
return NULL;
}
+#endif /* CONFIG_IPV6 */
/**
* smack_netlabel - Set the secattr on a socket
@@ -2228,7 +2475,7 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
struct smk_audit_info ad;
rcu_read_lock();
- hkp = smack_host_label(sap);
+ hkp = smack_ipv4host_label(sap);
if (hkp != NULL) {
#ifdef CONFIG_AUDIT
struct lsm_network_audit net;
@@ -2253,7 +2500,42 @@ static int smack_netlabel_send(struct sock *sk, struct sockaddr_in *sap)
return smack_netlabel(sk, sk_lbl);
}
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#if IS_ENABLED(CONFIG_IPV6)
+/**
+ * smk_ipv6_check - check Smack access
+ * @subject: subject Smack label
+ * @object: object Smack label
+ * @address: address
+ * @act: the action being taken
+ *
+ * Check an IPv6 access
+ */
+static int smk_ipv6_check(struct smack_known *subject,
+ struct smack_known *object,
+ struct sockaddr_in6 *address, int act)
+{
+#ifdef CONFIG_AUDIT
+ struct lsm_network_audit net;
+#endif
+ struct smk_audit_info ad;
+ int rc;
+
+#ifdef CONFIG_AUDIT
+ smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
+ ad.a.u.net->family = PF_INET6;
+ ad.a.u.net->dport = ntohs(address->sin6_port);
+ if (act == SMK_RECEIVING)
+ ad.a.u.net->v6info.saddr = address->sin6_addr;
+ else
+ ad.a.u.net->v6info.daddr = address->sin6_addr;
+#endif
+ rc = smk_access(subject, object, MAY_WRITE, &ad);
+ rc = smk_bu_note("IPv6 check", subject, object, MAY_WRITE, rc);
+ return rc;
+}
+#endif /* CONFIG_IPV6 */
+
+#ifdef SMACK_IPV6_PORT_LABELING
/**
* smk_ipv6_port_label - Smack port access table management
* @sock: socket
@@ -2337,48 +2619,43 @@ static void smk_ipv6_port_label(struct socket *sock, struct sockaddr *address)
static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
int act)
{
- __be16 *bep;
- __be32 *be32p;
struct smk_port_label *spp;
struct socket_smack *ssp = sk->sk_security;
- struct smack_known *skp;
- unsigned short port = 0;
+ struct smack_known *skp = NULL;
+ unsigned short port;
struct smack_known *object;
- struct smk_audit_info ad;
- int rc;
-#ifdef CONFIG_AUDIT
- struct lsm_network_audit net;
-#endif
if (act == SMK_RECEIVING) {
- skp = smack_net_ambient;
+ skp = smack_ipv6host_label(address);
object = ssp->smk_in;
} else {
skp = ssp->smk_out;
- object = smack_net_ambient;
+ object = smack_ipv6host_label(address);
}
/*
- * Get the IP address and port from the address.
+ * The other end is a single label host.
*/
- port = ntohs(address->sin6_port);
- bep = (__be16 *)(&address->sin6_addr);
- be32p = (__be32 *)(&address->sin6_addr);
+ if (skp != NULL && object != NULL)
+ return smk_ipv6_check(skp, object, address, act);
+ if (skp == NULL)
+ skp = smack_net_ambient;
+ if (object == NULL)
+ object = smack_net_ambient;
/*
* It's remote, so port lookup does no good.
*/
- if (be32p[0] || be32p[1] || be32p[2] || bep[6] || ntohs(bep[7]) != 1)
- goto auditout;
+ if (!smk_ipv6_localhost(address))
+ return smk_ipv6_check(skp, object, address, act);
/*
* It's local so the send check has to have passed.
*/
- if (act == SMK_RECEIVING) {
- skp = &smack_known_web;
- goto auditout;
- }
+ if (act == SMK_RECEIVING)
+ return 0;
+ port = ntohs(address->sin6_port);
list_for_each_entry(spp, &smk_ipv6_port_list, list) {
if (spp->smk_port != port)
continue;
@@ -2388,22 +2665,9 @@ static int smk_ipv6_port_check(struct sock *sk, struct sockaddr_in6 *address,
break;
}
-auditout:
-
-#ifdef CONFIG_AUDIT
- smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
- ad.a.u.net->family = sk->sk_family;
- ad.a.u.net->dport = port;
- if (act == SMK_RECEIVING)
- ad.a.u.net->v6info.saddr = address->sin6_addr;
- else
- ad.a.u.net->v6info.daddr = address->sin6_addr;
-#endif
- rc = smk_access(skp, object, MAY_WRITE, &ad);
- rc = smk_bu_note("IPv6 port check", skp, object, MAY_WRITE, rc);
- return rc;
+ return smk_ipv6_check(skp, object, address, act);
}
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
/**
* smack_inode_setsecurity - set smack xattrs
@@ -2430,8 +2694,8 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
return -EINVAL;
skp = smk_import_entry(value, size);
- if (skp == NULL)
- return -EINVAL;
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
nsp->smk_inode = skp;
@@ -2464,10 +2728,10 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
} else
return -EOPNOTSUPP;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_PORT_LABELING
if (sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, NULL);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
return 0;
}
@@ -2509,7 +2773,7 @@ static int smack_socket_post_create(struct socket *sock, int family,
return smack_netlabel(sock->sk, SMACK_CIPSO_SOCKET);
}
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_PORT_LABELING
/**
* smack_socket_bind - record port binding information.
* @sock: the socket
@@ -2523,14 +2787,11 @@ static int smack_socket_post_create(struct socket *sock, int family,
static int smack_socket_bind(struct socket *sock, struct sockaddr *address,
int addrlen)
{
-#if IS_ENABLED(CONFIG_IPV6)
if (sock->sk != NULL && sock->sk->sk_family == PF_INET6)
smk_ipv6_port_label(sock, address);
-#endif
-
return 0;
}
-#endif /* !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
/**
* smack_socket_connect - connect access check
@@ -2546,6 +2807,13 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
int addrlen)
{
int rc = 0;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ struct smack_known *rsp;
+ struct socket_smack *ssp = sock->sk->sk_security;
+#endif
if (sock->sk == NULL)
return 0;
@@ -2559,10 +2827,15 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
case PF_INET6:
if (addrlen < sizeof(struct sockaddr_in6))
return -EINVAL;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
- rc = smk_ipv6_port_check(sock->sk, (struct sockaddr_in6 *)sap,
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ rsp = smack_ipv6host_label(sip);
+ if (rsp != NULL)
+ rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
SMK_CONNECTING);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
+ rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING);
+#endif
break;
}
return rc;
@@ -3116,6 +3389,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
isp->smk_inode = smk_of_current();
break;
+ case PIPEFS_MAGIC:
+ isp->smk_inode = smk_of_current();
+ break;
default:
isp->smk_inode = sbsp->smk_root;
break;
@@ -3204,7 +3480,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
*/
dp = dget(opt_dentry);
skp = smk_fetch(XATTR_NAME_SMACK, inode, dp);
- if (skp != NULL)
+ if (!IS_ERR_OR_NULL(skp))
final = skp;
/*
@@ -3241,11 +3517,14 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
* Don't let the exec or mmap label be "*" or "@".
*/
skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp);
- if (skp == &smack_known_star || skp == &smack_known_web)
+ if (IS_ERR(skp) || skp == &smack_known_star ||
+ skp == &smack_known_web)
skp = NULL;
isp->smk_task = skp;
+
skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp);
- if (skp == &smack_known_star || skp == &smack_known_web)
+ if (IS_ERR(skp) || skp == &smack_known_star ||
+ skp == &smack_known_web)
skp = NULL;
isp->smk_mmap = skp;
@@ -3308,9 +3587,11 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
static int smack_setprocattr(struct task_struct *p, char *name,
void *value, size_t size)
{
- struct task_smack *tsp;
+ struct task_smack *tsp = current_security();
struct cred *new;
struct smack_known *skp;
+ struct smack_known_list_elem *sklep;
+ int rc;
/*
* Changing another process' Smack value is too dangerous
@@ -3319,7 +3600,7 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (p != current)
return -EPERM;
- if (!smack_privileged(CAP_MAC_ADMIN))
+ if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel))
return -EPERM;
if (value == NULL || size == 0 || size >= SMK_LONGLABEL)
@@ -3329,8 +3610,8 @@ static int smack_setprocattr(struct task_struct *p, char *name,
return -EINVAL;
skp = smk_import_entry(value, size);
- if (skp == NULL)
- return -EINVAL;
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
/*
* No process is ever allowed the web ("@") label.
@@ -3338,12 +3619,27 @@ static int smack_setprocattr(struct task_struct *p, char *name,
if (skp == &smack_known_web)
return -EPERM;
+ if (!smack_privileged(CAP_MAC_ADMIN)) {
+ rc = -EPERM;
+ list_for_each_entry(sklep, &tsp->smk_relabel, list)
+ if (sklep->smk_label == skp) {
+ rc = 0;
+ break;
+ }
+ if (rc)
+ return rc;
+ }
+
new = prepare_creds();
if (new == NULL)
return -ENOMEM;
tsp = new->security;
tsp->smk_task = skp;
+ /*
+ * process can change its label only once
+ */
+ smk_destroy_label_list(&tsp->smk_relabel);
commit_creds(new);
return size;
@@ -3445,9 +3741,13 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#if IS_ENABLED(CONFIG_IPV6)
struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ struct socket_smack *ssp = sock->sk->sk_security;
+ struct smack_known *rsp;
+#endif
int rc = 0;
/*
@@ -3461,9 +3761,15 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
rc = smack_netlabel_send(sock->sk, sip);
break;
case AF_INET6:
-#if IS_ENABLED(CONFIG_IPV6) && !defined(CONFIG_SECURITY_SMACK_NETFILTER)
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ rsp = smack_ipv6host_label(sap);
+ if (rsp != NULL)
+ rc = smk_ipv6_check(ssp->smk_out, rsp, sap,
+ SMK_CONNECTING);
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
-#endif /* CONFIG_IPV6 && !CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
break;
}
return rc;
@@ -3677,10 +3983,12 @@ access_check:
proto = smk_skb_to_addr_ipv6(skb, &sadd);
if (proto != IPPROTO_UDP && proto != IPPROTO_TCP)
break;
-#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_SECMARK_LABELING
if (skb && skb->secmark != 0)
skp = smack_from_secid(skb->secmark);
else
+ skp = smack_ipv6host_label(&sadd);
+ if (skp == NULL)
skp = smack_net_ambient;
#ifdef CONFIG_AUDIT
smk_ad_init_net(&ad, __func__, LSM_AUDIT_DATA_NET, &net);
@@ -3691,9 +3999,10 @@ access_check:
rc = smk_access(skp, ssp->smk_in, MAY_WRITE, &ad);
rc = smk_bu_note("IPv6 delivery", skp, ssp->smk_in,
MAY_WRITE, rc);
-#else /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_SECMARK_LABELING */
+#ifdef SMACK_IPV6_PORT_LABELING
rc = smk_ipv6_port_check(sk, &sadd, SMK_RECEIVING);
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif /* SMACK_IPV6_PORT_LABELING */
break;
#endif /* CONFIG_IPV6 */
}
@@ -3791,13 +4100,11 @@ static int smack_socket_getpeersec_dgram(struct socket *sock,
}
netlbl_secattr_destroy(&secattr);
break;
-#if IS_ENABLED(CONFIG_IPV6)
case PF_INET6:
-#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+#ifdef SMACK_IPV6_SECMARK_LABELING
s = skb->secmark;
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
+#endif
break;
-#endif /* CONFIG_IPV6 */
}
*secid = s;
if (s == 0)
@@ -3920,7 +4227,7 @@ access_check:
hdr = ip_hdr(skb);
addr.sin_addr.s_addr = hdr->saddr;
rcu_read_lock();
- hskp = smack_host_label(&addr);
+ hskp = smack_ipv4host_label(&addr);
rcu_read_unlock();
if (hskp == NULL)
@@ -4105,8 +4412,10 @@ static int smack_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule)
return -EINVAL;
skp = smk_import_entry(rulestr, 0);
- if (skp)
- *rule = skp->smk_known;
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
+
+ *rule = skp->smk_known;
return 0;
}
@@ -4266,147 +4575,147 @@ static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
return 0;
}
-struct security_operations smack_ops = {
- .name = "smack",
-
- .ptrace_access_check = smack_ptrace_access_check,
- .ptrace_traceme = smack_ptrace_traceme,
- .syslog = smack_syslog,
-
- .sb_alloc_security = smack_sb_alloc_security,
- .sb_free_security = smack_sb_free_security,
- .sb_copy_data = smack_sb_copy_data,
- .sb_kern_mount = smack_sb_kern_mount,
- .sb_statfs = smack_sb_statfs,
-
- .bprm_set_creds = smack_bprm_set_creds,
- .bprm_committing_creds = smack_bprm_committing_creds,
- .bprm_secureexec = smack_bprm_secureexec,
-
- .inode_alloc_security = smack_inode_alloc_security,
- .inode_free_security = smack_inode_free_security,
- .inode_init_security = smack_inode_init_security,
- .inode_link = smack_inode_link,
- .inode_unlink = smack_inode_unlink,
- .inode_rmdir = smack_inode_rmdir,
- .inode_rename = smack_inode_rename,
- .inode_permission = smack_inode_permission,
- .inode_setattr = smack_inode_setattr,
- .inode_getattr = smack_inode_getattr,
- .inode_setxattr = smack_inode_setxattr,
- .inode_post_setxattr = smack_inode_post_setxattr,
- .inode_getxattr = smack_inode_getxattr,
- .inode_removexattr = smack_inode_removexattr,
- .inode_getsecurity = smack_inode_getsecurity,
- .inode_setsecurity = smack_inode_setsecurity,
- .inode_listsecurity = smack_inode_listsecurity,
- .inode_getsecid = smack_inode_getsecid,
-
- .file_permission = smack_file_permission,
- .file_alloc_security = smack_file_alloc_security,
- .file_free_security = smack_file_free_security,
- .file_ioctl = smack_file_ioctl,
- .file_lock = smack_file_lock,
- .file_fcntl = smack_file_fcntl,
- .mmap_file = smack_mmap_file,
- .mmap_addr = cap_mmap_addr,
- .file_set_fowner = smack_file_set_fowner,
- .file_send_sigiotask = smack_file_send_sigiotask,
- .file_receive = smack_file_receive,
-
- .file_open = smack_file_open,
-
- .cred_alloc_blank = smack_cred_alloc_blank,
- .cred_free = smack_cred_free,
- .cred_prepare = smack_cred_prepare,
- .cred_transfer = smack_cred_transfer,
- .kernel_act_as = smack_kernel_act_as,
- .kernel_create_files_as = smack_kernel_create_files_as,
- .task_setpgid = smack_task_setpgid,
- .task_getpgid = smack_task_getpgid,
- .task_getsid = smack_task_getsid,
- .task_getsecid = smack_task_getsecid,
- .task_setnice = smack_task_setnice,
- .task_setioprio = smack_task_setioprio,
- .task_getioprio = smack_task_getioprio,
- .task_setscheduler = smack_task_setscheduler,
- .task_getscheduler = smack_task_getscheduler,
- .task_movememory = smack_task_movememory,
- .task_kill = smack_task_kill,
- .task_wait = smack_task_wait,
- .task_to_inode = smack_task_to_inode,
-
- .ipc_permission = smack_ipc_permission,
- .ipc_getsecid = smack_ipc_getsecid,
-
- .msg_msg_alloc_security = smack_msg_msg_alloc_security,
- .msg_msg_free_security = smack_msg_msg_free_security,
-
- .msg_queue_alloc_security = smack_msg_queue_alloc_security,
- .msg_queue_free_security = smack_msg_queue_free_security,
- .msg_queue_associate = smack_msg_queue_associate,
- .msg_queue_msgctl = smack_msg_queue_msgctl,
- .msg_queue_msgsnd = smack_msg_queue_msgsnd,
- .msg_queue_msgrcv = smack_msg_queue_msgrcv,
-
- .shm_alloc_security = smack_shm_alloc_security,
- .shm_free_security = smack_shm_free_security,
- .shm_associate = smack_shm_associate,
- .shm_shmctl = smack_shm_shmctl,
- .shm_shmat = smack_shm_shmat,
-
- .sem_alloc_security = smack_sem_alloc_security,
- .sem_free_security = smack_sem_free_security,
- .sem_associate = smack_sem_associate,
- .sem_semctl = smack_sem_semctl,
- .sem_semop = smack_sem_semop,
-
- .d_instantiate = smack_d_instantiate,
-
- .getprocattr = smack_getprocattr,
- .setprocattr = smack_setprocattr,
-
- .unix_stream_connect = smack_unix_stream_connect,
- .unix_may_send = smack_unix_may_send,
-
- .socket_post_create = smack_socket_post_create,
-#ifndef CONFIG_SECURITY_SMACK_NETFILTER
- .socket_bind = smack_socket_bind,
-#endif /* CONFIG_SECURITY_SMACK_NETFILTER */
- .socket_connect = smack_socket_connect,
- .socket_sendmsg = smack_socket_sendmsg,
- .socket_sock_rcv_skb = smack_socket_sock_rcv_skb,
- .socket_getpeersec_stream = smack_socket_getpeersec_stream,
- .socket_getpeersec_dgram = smack_socket_getpeersec_dgram,
- .sk_alloc_security = smack_sk_alloc_security,
- .sk_free_security = smack_sk_free_security,
- .sock_graft = smack_sock_graft,
- .inet_conn_request = smack_inet_conn_request,
- .inet_csk_clone = smack_inet_csk_clone,
+static struct security_hook_list smack_hooks[] = {
+ LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
+ LSM_HOOK_INIT(syslog, smack_syslog),
+
+ LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
+ LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
+ LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),
+ LSM_HOOK_INIT(sb_kern_mount, smack_sb_kern_mount),
+ LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
+ LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
+ LSM_HOOK_INIT(sb_parse_opts_str, smack_parse_opts_str),
+
+ LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
+ LSM_HOOK_INIT(bprm_committing_creds, smack_bprm_committing_creds),
+ LSM_HOOK_INIT(bprm_secureexec, smack_bprm_secureexec),
+
+ LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
+ LSM_HOOK_INIT(inode_free_security, smack_inode_free_security),
+ LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
+ LSM_HOOK_INIT(inode_link, smack_inode_link),
+ LSM_HOOK_INIT(inode_unlink, smack_inode_unlink),
+ LSM_HOOK_INIT(inode_rmdir, smack_inode_rmdir),
+ LSM_HOOK_INIT(inode_rename, smack_inode_rename),
+ LSM_HOOK_INIT(inode_permission, smack_inode_permission),
+ LSM_HOOK_INIT(inode_setattr, smack_inode_setattr),
+ LSM_HOOK_INIT(inode_getattr, smack_inode_getattr),
+ LSM_HOOK_INIT(inode_setxattr, smack_inode_setxattr),
+ LSM_HOOK_INIT(inode_post_setxattr, smack_inode_post_setxattr),
+ LSM_HOOK_INIT(inode_getxattr, smack_inode_getxattr),
+ LSM_HOOK_INIT(inode_removexattr, smack_inode_removexattr),
+ LSM_HOOK_INIT(inode_getsecurity, smack_inode_getsecurity),
+ LSM_HOOK_INIT(inode_setsecurity, smack_inode_setsecurity),
+ LSM_HOOK_INIT(inode_listsecurity, smack_inode_listsecurity),
+ LSM_HOOK_INIT(inode_getsecid, smack_inode_getsecid),
+
+ LSM_HOOK_INIT(file_permission, smack_file_permission),
+ LSM_HOOK_INIT(file_alloc_security, smack_file_alloc_security),
+ LSM_HOOK_INIT(file_free_security, smack_file_free_security),
+ LSM_HOOK_INIT(file_ioctl, smack_file_ioctl),
+ LSM_HOOK_INIT(file_lock, smack_file_lock),
+ LSM_HOOK_INIT(file_fcntl, smack_file_fcntl),
+ LSM_HOOK_INIT(mmap_file, smack_mmap_file),
+ LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
+ LSM_HOOK_INIT(file_set_fowner, smack_file_set_fowner),
+ LSM_HOOK_INIT(file_send_sigiotask, smack_file_send_sigiotask),
+ LSM_HOOK_INIT(file_receive, smack_file_receive),
+
+ LSM_HOOK_INIT(file_open, smack_file_open),
+
+ LSM_HOOK_INIT(cred_alloc_blank, smack_cred_alloc_blank),
+ LSM_HOOK_INIT(cred_free, smack_cred_free),
+ LSM_HOOK_INIT(cred_prepare, smack_cred_prepare),
+ LSM_HOOK_INIT(cred_transfer, smack_cred_transfer),
+ LSM_HOOK_INIT(kernel_act_as, smack_kernel_act_as),
+ LSM_HOOK_INIT(kernel_create_files_as, smack_kernel_create_files_as),
+ LSM_HOOK_INIT(task_setpgid, smack_task_setpgid),
+ LSM_HOOK_INIT(task_getpgid, smack_task_getpgid),
+ LSM_HOOK_INIT(task_getsid, smack_task_getsid),
+ LSM_HOOK_INIT(task_getsecid, smack_task_getsecid),
+ LSM_HOOK_INIT(task_setnice, smack_task_setnice),
+ LSM_HOOK_INIT(task_setioprio, smack_task_setioprio),
+ LSM_HOOK_INIT(task_getioprio, smack_task_getioprio),
+ LSM_HOOK_INIT(task_setscheduler, smack_task_setscheduler),
+ LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler),
+ LSM_HOOK_INIT(task_movememory, smack_task_movememory),
+ LSM_HOOK_INIT(task_kill, smack_task_kill),
+ LSM_HOOK_INIT(task_wait, smack_task_wait),
+ LSM_HOOK_INIT(task_to_inode, smack_task_to_inode),
+
+ LSM_HOOK_INIT(ipc_permission, smack_ipc_permission),
+ LSM_HOOK_INIT(ipc_getsecid, smack_ipc_getsecid),
+
+ LSM_HOOK_INIT(msg_msg_alloc_security, smack_msg_msg_alloc_security),
+ LSM_HOOK_INIT(msg_msg_free_security, smack_msg_msg_free_security),
+
+ LSM_HOOK_INIT(msg_queue_alloc_security, smack_msg_queue_alloc_security),
+ LSM_HOOK_INIT(msg_queue_free_security, smack_msg_queue_free_security),
+ LSM_HOOK_INIT(msg_queue_associate, smack_msg_queue_associate),
+ LSM_HOOK_INIT(msg_queue_msgctl, smack_msg_queue_msgctl),
+ LSM_HOOK_INIT(msg_queue_msgsnd, smack_msg_queue_msgsnd),
+ LSM_HOOK_INIT(msg_queue_msgrcv, smack_msg_queue_msgrcv),
+
+ LSM_HOOK_INIT(shm_alloc_security, smack_shm_alloc_security),
+ LSM_HOOK_INIT(shm_free_security, smack_shm_free_security),
+ LSM_HOOK_INIT(shm_associate, smack_shm_associate),
+ LSM_HOOK_INIT(shm_shmctl, smack_shm_shmctl),
+ LSM_HOOK_INIT(shm_shmat, smack_shm_shmat),
+
+ LSM_HOOK_INIT(sem_alloc_security, smack_sem_alloc_security),
+ LSM_HOOK_INIT(sem_free_security, smack_sem_free_security),
+ LSM_HOOK_INIT(sem_associate, smack_sem_associate),
+ LSM_HOOK_INIT(sem_semctl, smack_sem_semctl),
+ LSM_HOOK_INIT(sem_semop, smack_sem_semop),
+
+ LSM_HOOK_INIT(d_instantiate, smack_d_instantiate),
+
+ LSM_HOOK_INIT(getprocattr, smack_getprocattr),
+ LSM_HOOK_INIT(setprocattr, smack_setprocattr),
+
+ LSM_HOOK_INIT(unix_stream_connect, smack_unix_stream_connect),
+ LSM_HOOK_INIT(unix_may_send, smack_unix_may_send),
+
+ LSM_HOOK_INIT(socket_post_create, smack_socket_post_create),
+#ifdef SMACK_IPV6_PORT_LABELING
+ LSM_HOOK_INIT(socket_bind, smack_socket_bind),
+#endif
+ LSM_HOOK_INIT(socket_connect, smack_socket_connect),
+ LSM_HOOK_INIT(socket_sendmsg, smack_socket_sendmsg),
+ LSM_HOOK_INIT(socket_sock_rcv_skb, smack_socket_sock_rcv_skb),
+ LSM_HOOK_INIT(socket_getpeersec_stream, smack_socket_getpeersec_stream),
+ LSM_HOOK_INIT(socket_getpeersec_dgram, smack_socket_getpeersec_dgram),
+ LSM_HOOK_INIT(sk_alloc_security, smack_sk_alloc_security),
+ LSM_HOOK_INIT(sk_free_security, smack_sk_free_security),
+ LSM_HOOK_INIT(sock_graft, smack_sock_graft),
+ LSM_HOOK_INIT(inet_conn_request, smack_inet_conn_request),
+ LSM_HOOK_INIT(inet_csk_clone, smack_inet_csk_clone),
/* key management security hooks */
#ifdef CONFIG_KEYS
- .key_alloc = smack_key_alloc,
- .key_free = smack_key_free,
- .key_permission = smack_key_permission,
- .key_getsecurity = smack_key_getsecurity,
+ LSM_HOOK_INIT(key_alloc, smack_key_alloc),
+ LSM_HOOK_INIT(key_free, smack_key_free),
+ LSM_HOOK_INIT(key_permission, smack_key_permission),
+ LSM_HOOK_INIT(key_getsecurity, smack_key_getsecurity),
#endif /* CONFIG_KEYS */
/* Audit hooks */
#ifdef CONFIG_AUDIT
- .audit_rule_init = smack_audit_rule_init,
- .audit_rule_known = smack_audit_rule_known,
- .audit_rule_match = smack_audit_rule_match,
- .audit_rule_free = smack_audit_rule_free,
+ LSM_HOOK_INIT(audit_rule_init, smack_audit_rule_init),
+ LSM_HOOK_INIT(audit_rule_known, smack_audit_rule_known),
+ LSM_HOOK_INIT(audit_rule_match, smack_audit_rule_match),
+ LSM_HOOK_INIT(audit_rule_free, smack_audit_rule_free),
#endif /* CONFIG_AUDIT */
- .ismaclabel = smack_ismaclabel,
- .secid_to_secctx = smack_secid_to_secctx,
- .secctx_to_secid = smack_secctx_to_secid,
- .release_secctx = smack_release_secctx,
- .inode_notifysecctx = smack_inode_notifysecctx,
- .inode_setsecctx = smack_inode_setsecctx,
- .inode_getsecctx = smack_inode_getsecctx,
+ LSM_HOOK_INIT(ismaclabel, smack_ismaclabel),
+ LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx),
+ LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid),
+ LSM_HOOK_INIT(release_secctx, smack_release_secctx),
+ LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx),
+ LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx),
+ LSM_HOOK_INIT(inode_getsecctx, smack_inode_getsecctx),
};
@@ -4451,11 +4760,9 @@ static __init int smack_init(void)
struct cred *cred;
struct task_smack *tsp;
- if (!security_module_enable(&smack_ops))
+ if (!security_module_enable("smack"))
return 0;
- smack_enabled = 1;
-
smack_inode_cache = KMEM_CACHE(inode_smack, 0);
if (!smack_inode_cache)
return -ENOMEM;
@@ -4467,7 +4774,18 @@ static __init int smack_init(void)
return -ENOMEM;
}
- printk(KERN_INFO "Smack: Initializing.\n");
+ smack_enabled = 1;
+
+ pr_info("Smack: Initializing.\n");
+#ifdef CONFIG_SECURITY_SMACK_NETFILTER
+ pr_info("Smack: Netfilter enabled.\n");
+#endif
+#ifdef SMACK_IPV6_PORT_LABELING
+ pr_info("Smack: IPv6 port labeling enabled.\n");
+#endif
+#ifdef SMACK_IPV6_SECMARK_LABELING
+ pr_info("Smack: IPv6 Netfilter enabled.\n");
+#endif
/*
* Set the security state for the initial task.
@@ -4481,8 +4799,7 @@ static __init int smack_init(void)
/*
* Register with LSM
*/
- if (register_security(&smack_ops))
- panic("smack: Unable to register with kernel.\n");
+ security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks));
return 0;
}
diff --git a/kernel/security/smack/smack_netfilter.c b/kernel/security/smack/smack_netfilter.c
index a455cfc9e..aa6bf1b22 100644
--- a/kernel/security/smack/smack_netfilter.c
+++ b/kernel/security/smack/smack_netfilter.c
@@ -17,19 +17,21 @@
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netdevice.h>
+#include <net/inet_sock.h>
#include "smack.h"
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
-static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
+static unsigned int smack_ipv6_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
+ struct sock *sk = skb_to_full_sk(skb);
struct socket_smack *ssp;
struct smack_known *skp;
- if (skb && skb->sk && skb->sk->sk_security) {
- ssp = skb->sk->sk_security;
+ if (sk && sk->sk_security) {
+ ssp = sk->sk_security;
skp = ssp->smk_out;
skb->secmark = skp->smk_secid;
}
@@ -38,15 +40,16 @@ static unsigned int smack_ipv6_output(const struct nf_hook_ops *ops,
}
#endif /* IPV6 */
-static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
+static unsigned int smack_ipv4_output(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
+ struct sock *sk = skb_to_full_sk(skb);
struct socket_smack *ssp;
struct smack_known *skp;
- if (skb && skb->sk && skb->sk->sk_security) {
- ssp = skb->sk->sk_security;
+ if (sk && sk->sk_security) {
+ ssp = sk->sk_security;
skp = ssp->smk_out;
skb->secmark = skp->smk_secid;
}
@@ -57,7 +60,6 @@ static unsigned int smack_ipv4_output(const struct nf_hook_ops *ops,
static struct nf_hook_ops smack_nf_ops[] = {
{
.hook = smack_ipv4_output,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SELINUX_FIRST,
@@ -65,7 +67,6 @@ static struct nf_hook_ops smack_nf_ops[] = {
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
{
.hook = smack_ipv6_output,
- .owner = THIS_MODULE,
.pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_SELINUX_FIRST,
diff --git a/kernel/security/smack/smackfs.c b/kernel/security/smack/smackfs.c
index ac4cac7c6..94bd9e41c 100644
--- a/kernel/security/smack/smackfs.c
+++ b/kernel/security/smack/smackfs.c
@@ -29,6 +29,7 @@
#include <linux/magic.h>
#include "smack.h"
+#define BEBITS (sizeof(__be32) * 8)
/*
* smackfs pseudo filesystem.
*/
@@ -40,7 +41,7 @@ enum smk_inos {
SMK_DOI = 5, /* CIPSO DOI */
SMK_DIRECT = 6, /* CIPSO level indicating direct label */
SMK_AMBIENT = 7, /* internet ambient label */
- SMK_NETLBLADDR = 8, /* single label hosts */
+ SMK_NET4ADDR = 8, /* single label hosts */
SMK_ONLYCAP = 9, /* the only "capable" label */
SMK_LOGGING = 10, /* logging */
SMK_LOAD_SELF = 11, /* task specific rules */
@@ -57,6 +58,10 @@ enum smk_inos {
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
SMK_UNCONFINED = 22, /* define an unconfined label */
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ SMK_NET6ADDR = 23, /* single label IPv6 hosts */
+#endif /* CONFIG_IPV6 */
+ SMK_RELABEL_SELF = 24, /* relabel possible without CAP_MAC_ADMIN */
};
/*
@@ -64,7 +69,10 @@ enum smk_inos {
*/
static DEFINE_MUTEX(smack_cipso_lock);
static DEFINE_MUTEX(smack_ambient_lock);
-static DEFINE_MUTEX(smk_netlbladdr_lock);
+static DEFINE_MUTEX(smk_net4addr_lock);
+#if IS_ENABLED(CONFIG_IPV6)
+static DEFINE_MUTEX(smk_net6addr_lock);
+#endif /* CONFIG_IPV6 */
/*
* This is the "ambient" label for network traffic.
@@ -87,16 +95,6 @@ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT;
*/
int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT;
-/*
- * Unless a process is running with this label even
- * having CAP_MAC_OVERRIDE isn't enough to grant
- * privilege to violate MAC policy. If no label is
- * designated (the NULL case) capabilities apply to
- * everyone. It is expected that the hat (^) label
- * will be used if any label is used.
- */
-struct smack_known *smack_onlycap;
-
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
/*
* Allow one label to be unconfined. This is for
@@ -128,7 +126,10 @@ int smack_ptrace_rule = SMACK_PTRACE_DEFAULT;
* can write to the specified label.
*/
-LIST_HEAD(smk_netlbladdr_list);
+LIST_HEAD(smk_net4addr_list);
+#if IS_ENABLED(CONFIG_IPV6)
+LIST_HEAD(smk_net6addr_list);
+#endif /* CONFIG_IPV6 */
/*
* Rule lists are maintained for each label.
@@ -139,7 +140,7 @@ struct smack_master_list {
struct smack_rule *smk_rule;
};
-LIST_HEAD(smack_rule_list);
+static LIST_HEAD(smack_rule_list);
struct smack_parsed_rule {
struct smack_known *smk_subject;
@@ -150,11 +151,6 @@ struct smack_parsed_rule {
static int smk_cipso_doi_value = SMACK_CIPSO_DOI_DEFAULT;
-struct smack_known smack_cipso_option = {
- .smk_known = SMACK_CIPSO_OPTION,
- .smk_secid = 0,
-};
-
/*
* Values for parsing cipso rules
* SMK_DIGITLEN: Length of a digit field in a rule.
@@ -338,8 +334,7 @@ static int smk_perm_from_str(const char *string)
* @import: if non-zero, import labels
* @len: label length limit
*
- * Returns 0 on success, -EINVAL on failure and -ENOENT when either subject
- * or object is missing.
+ * Returns 0 on success, appropriate error code on failure.
*/
static int smk_fill_rule(const char *subject, const char *object,
const char *access1, const char *access2,
@@ -351,16 +346,16 @@ static int smk_fill_rule(const char *subject, const char *object,
if (import) {
rule->smk_subject = smk_import_entry(subject, len);
- if (rule->smk_subject == NULL)
- return -EINVAL;
+ if (IS_ERR(rule->smk_subject))
+ return PTR_ERR(rule->smk_subject);
rule->smk_object = smk_import_entry(object, len);
- if (rule->smk_object == NULL)
- return -EINVAL;
+ if (IS_ERR(rule->smk_object))
+ return PTR_ERR(rule->smk_object);
} else {
cp = smk_parse_smack(subject, len);
- if (cp == NULL)
- return -EINVAL;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
skp = smk_find_entry(cp);
kfree(cp);
if (skp == NULL)
@@ -368,8 +363,8 @@ static int smk_fill_rule(const char *subject, const char *object,
rule->smk_subject = skp;
cp = smk_parse_smack(object, len);
- if (cp == NULL)
- return -EINVAL;
+ if (IS_ERR(cp))
+ return PTR_ERR(cp);
skp = smk_find_entry(cp);
kfree(cp);
if (skp == NULL)
@@ -412,7 +407,7 @@ static int smk_parse_rule(const char *data, struct smack_parsed_rule *rule,
* @import: if non-zero, import labels
* @tokens: numer of substrings expected in data
*
- * Returns number of processed bytes on success, -1 on failure.
+ * Returns number of processed bytes on success, -ERRNO on failure.
*/
static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
int import, int tokens)
@@ -431,7 +426,7 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule,
if (data[cnt] == '\0')
/* Unexpected end of data */
- return -1;
+ return -EINVAL;
tok[i] = data + cnt;
@@ -529,14 +524,14 @@ static ssize_t smk_write_rules_list(struct file *file, const char __user *buf,
while (cnt < count) {
if (format == SMK_FIXED24_FMT) {
rc = smk_parse_rule(data, &rule, 1);
- if (rc != 0) {
- rc = -EINVAL;
+ if (rc < 0)
goto out;
- }
cnt = count;
} else {
rc = smk_parse_long_rule(data + cnt, &rule, 1, tokens);
- if (rc <= 0) {
+ if (rc < 0)
+ goto out;
+ if (rc == 0) {
rc = -EINVAL;
goto out;
}
@@ -567,23 +562,17 @@ static void *smk_seq_start(struct seq_file *s, loff_t *pos,
struct list_head *head)
{
struct list_head *list;
+ int i = *pos;
+
+ rcu_read_lock();
+ for (list = rcu_dereference(list_next_rcu(head));
+ list != head;
+ list = rcu_dereference(list_next_rcu(list))) {
+ if (i-- == 0)
+ return list;
+ }
- /*
- * This is 0 the first time through.
- */
- if (s->index == 0)
- s->private = head;
-
- if (s->private == NULL)
- return NULL;
-
- list = s->private;
- if (list_empty(list))
- return NULL;
-
- if (s->index == 0)
- return list->next;
- return list;
+ return NULL;
}
static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
@@ -591,17 +580,15 @@ static void *smk_seq_next(struct seq_file *s, void *v, loff_t *pos,
{
struct list_head *list = v;
- if (list_is_last(list, head)) {
- s->private = NULL;
- return NULL;
- }
- s->private = list->next;
- return list->next;
+ ++*pos;
+ list = rcu_dereference(list_next_rcu(list));
+
+ return (list == head) ? NULL : list;
}
static void smk_seq_stop(struct seq_file *s, void *v)
{
- /* No-op */
+ rcu_read_unlock();
}
static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max)
@@ -661,7 +648,7 @@ static int load_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_master_list *smlp =
- list_entry(list, struct smack_master_list, list);
+ list_entry_rcu(list, struct smack_master_list, list);
smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN);
@@ -809,7 +796,7 @@ static int cipso_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_known *skp =
- list_entry(list, struct smack_known, list);
+ list_entry_rcu(list, struct smack_known, list);
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
char sep = '/';
int i;
@@ -915,8 +902,10 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf,
mutex_lock(&smack_cipso_lock);
skp = smk_import_entry(rule, 0);
- if (skp == NULL)
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
goto out;
+ }
if (format == SMK_FIXED24_FMT)
rule += SMK_LABELLEN;
@@ -998,7 +987,7 @@ static int cipso2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_known *skp =
- list_entry(list, struct smack_known, list);
+ list_entry_rcu(list, struct smack_known, list);
struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat;
char sep = '/';
int i;
@@ -1064,92 +1053,90 @@ static const struct file_operations smk_cipso2_ops = {
* Seq_file read operations for /smack/netlabel
*/
-static void *netlbladdr_seq_start(struct seq_file *s, loff_t *pos)
+static void *net4addr_seq_start(struct seq_file *s, loff_t *pos)
{
- return smk_seq_start(s, pos, &smk_netlbladdr_list);
+ return smk_seq_start(s, pos, &smk_net4addr_list);
}
-static void *netlbladdr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+static void *net4addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
- return smk_seq_next(s, v, pos, &smk_netlbladdr_list);
+ return smk_seq_next(s, v, pos, &smk_net4addr_list);
}
-#define BEBITS (sizeof(__be32) * 8)
/*
* Print host/label pairs
*/
-static int netlbladdr_seq_show(struct seq_file *s, void *v)
+static int net4addr_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
- struct smk_netlbladdr *skp =
- list_entry(list, struct smk_netlbladdr, list);
- unsigned char *hp = (char *) &skp->smk_host.sin_addr.s_addr;
- int maskn;
- u32 temp_mask = be32_to_cpu(skp->smk_mask.s_addr);
+ struct smk_net4addr *skp =
+ list_entry_rcu(list, struct smk_net4addr, list);
+ char *kp = SMACK_CIPSO_OPTION;
- for (maskn = 0; temp_mask; temp_mask <<= 1, maskn++);
-
- seq_printf(s, "%u.%u.%u.%u/%d %s\n",
- hp[0], hp[1], hp[2], hp[3], maskn, skp->smk_label->smk_known);
+ if (skp->smk_label != NULL)
+ kp = skp->smk_label->smk_known;
+ seq_printf(s, "%pI4/%d %s\n", &skp->smk_host.s_addr,
+ skp->smk_masks, kp);
return 0;
}
-static const struct seq_operations netlbladdr_seq_ops = {
- .start = netlbladdr_seq_start,
- .next = netlbladdr_seq_next,
- .show = netlbladdr_seq_show,
+static const struct seq_operations net4addr_seq_ops = {
+ .start = net4addr_seq_start,
+ .next = net4addr_seq_next,
+ .show = net4addr_seq_show,
.stop = smk_seq_stop,
};
/**
- * smk_open_netlbladdr - open() for /smack/netlabel
+ * smk_open_net4addr - open() for /smack/netlabel
* @inode: inode structure representing file
* @file: "netlabel" file pointer
*
- * Connect our netlbladdr_seq_* operations with /smack/netlabel
+ * Connect our net4addr_seq_* operations with /smack/netlabel
* file_operations
*/
-static int smk_open_netlbladdr(struct inode *inode, struct file *file)
+static int smk_open_net4addr(struct inode *inode, struct file *file)
{
- return seq_open(file, &netlbladdr_seq_ops);
+ return seq_open(file, &net4addr_seq_ops);
}
/**
- * smk_netlbladdr_insert
+ * smk_net4addr_insert
* @new : netlabel to insert
*
- * This helper insert netlabel in the smack_netlbladdrs list
+ * This helper insert netlabel in the smack_net4addrs list
* sorted by netmask length (longest to smallest)
- * locked by &smk_netlbladdr_lock in smk_write_netlbladdr
+ * locked by &smk_net4addr_lock in smk_write_net4addr
*
*/
-static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
+static void smk_net4addr_insert(struct smk_net4addr *new)
{
- struct smk_netlbladdr *m, *m_next;
+ struct smk_net4addr *m;
+ struct smk_net4addr *m_next;
- if (list_empty(&smk_netlbladdr_list)) {
- list_add_rcu(&new->list, &smk_netlbladdr_list);
+ if (list_empty(&smk_net4addr_list)) {
+ list_add_rcu(&new->list, &smk_net4addr_list);
return;
}
- m = list_entry_rcu(smk_netlbladdr_list.next,
- struct smk_netlbladdr, list);
+ m = list_entry_rcu(smk_net4addr_list.next,
+ struct smk_net4addr, list);
/* the comparison '>' is a bit hacky, but works */
- if (new->smk_mask.s_addr > m->smk_mask.s_addr) {
- list_add_rcu(&new->list, &smk_netlbladdr_list);
+ if (new->smk_masks > m->smk_masks) {
+ list_add_rcu(&new->list, &smk_net4addr_list);
return;
}
- list_for_each_entry_rcu(m, &smk_netlbladdr_list, list) {
- if (list_is_last(&m->list, &smk_netlbladdr_list)) {
+ list_for_each_entry_rcu(m, &smk_net4addr_list, list) {
+ if (list_is_last(&m->list, &smk_net4addr_list)) {
list_add_rcu(&new->list, &m->list);
return;
}
m_next = list_entry_rcu(m->list.next,
- struct smk_netlbladdr, list);
- if (new->smk_mask.s_addr > m_next->smk_mask.s_addr) {
+ struct smk_net4addr, list);
+ if (new->smk_masks > m_next->smk_masks) {
list_add_rcu(&new->list, &m->list);
return;
}
@@ -1158,28 +1145,29 @@ static void smk_netlbladdr_insert(struct smk_netlbladdr *new)
/**
- * smk_write_netlbladdr - write() for /smack/netlabel
+ * smk_write_net4addr - write() for /smack/netlabel
* @file: file pointer, not actually used
* @buf: where to get the data from
* @count: bytes sent
* @ppos: where to start
*
- * Accepts only one netlbladdr per write call.
+ * Accepts only one net4addr per write call.
* Returns number of bytes written or error code, as appropriate
*/
-static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
+static ssize_t smk_write_net4addr(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- struct smk_netlbladdr *snp;
+ struct smk_net4addr *snp;
struct sockaddr_in newname;
char *smack;
- struct smack_known *skp;
+ struct smack_known *skp = NULL;
char *data;
char *host = (char *)&newname.sin_addr.s_addr;
int rc;
struct netlbl_audit audit_info;
struct in_addr mask;
unsigned int m;
+ unsigned int masks;
int found;
u32 mask_bits = (1<<31);
__be32 nsa;
@@ -1217,7 +1205,7 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
data[count] = '\0';
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd/%u %s",
- &host[0], &host[1], &host[2], &host[3], &m, smack);
+ &host[0], &host[1], &host[2], &host[3], &masks, smack);
if (rc != 6) {
rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s",
&host[0], &host[1], &host[2], &host[3], smack);
@@ -1226,8 +1214,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
goto free_out;
}
m = BEBITS;
+ masks = 32;
}
- if (m > BEBITS) {
+ if (masks > BEBITS) {
rc = -EINVAL;
goto free_out;
}
@@ -1237,21 +1226,21 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
*/
if (smack[0] != '-') {
skp = smk_import_entry(smack, 0);
- if (skp == NULL) {
- rc = -EINVAL;
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
goto free_out;
}
} else {
- /* check known options */
- if (strcmp(smack, smack_cipso_option.smk_known) == 0)
- skp = &smack_cipso_option;
- else {
+ /*
+ * Only the -CIPSO option is supported for IPv4
+ */
+ if (strcmp(smack, SMACK_CIPSO_OPTION) != 0) {
rc = -EINVAL;
goto free_out;
}
}
- for (temp_mask = 0; m > 0; m--) {
+ for (m = masks, temp_mask = 0; m > 0; m--) {
temp_mask |= mask_bits;
mask_bits >>= 1;
}
@@ -1262,14 +1251,13 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* Only allow one writer at a time. Writes should be
* quite rare and small in any case.
*/
- mutex_lock(&smk_netlbladdr_lock);
+ mutex_lock(&smk_net4addr_lock);
nsa = newname.sin_addr.s_addr;
/* try to find if the prefix is already in the list */
found = 0;
- list_for_each_entry_rcu(snp, &smk_netlbladdr_list, list) {
- if (snp->smk_host.sin_addr.s_addr == nsa &&
- snp->smk_mask.s_addr == mask.s_addr) {
+ list_for_each_entry_rcu(snp, &smk_net4addr_list, list) {
+ if (snp->smk_host.s_addr == nsa && snp->smk_masks == masks) {
found = 1;
break;
}
@@ -1282,17 +1270,20 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
rc = -ENOMEM;
else {
rc = 0;
- snp->smk_host.sin_addr.s_addr = newname.sin_addr.s_addr;
+ snp->smk_host.s_addr = newname.sin_addr.s_addr;
snp->smk_mask.s_addr = mask.s_addr;
snp->smk_label = skp;
- smk_netlbladdr_insert(snp);
+ snp->smk_masks = masks;
+ smk_net4addr_insert(snp);
}
} else {
- /* we delete the unlabeled entry, only if the previous label
- * wasn't the special CIPSO option */
- if (snp->smk_label != &smack_cipso_option)
+ /*
+ * Delete the unlabeled entry, only if the previous label
+ * wasn't the special CIPSO option
+ */
+ if (snp->smk_label != NULL)
rc = netlbl_cfg_unlbl_static_del(&init_net, NULL,
- &snp->smk_host.sin_addr, &snp->smk_mask,
+ &snp->smk_host, &snp->smk_mask,
PF_INET, &audit_info);
else
rc = 0;
@@ -1304,15 +1295,15 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf,
* this host so that incoming packets get labeled.
* but only if we didn't get the special CIPSO option
*/
- if (rc == 0 && skp != &smack_cipso_option)
+ if (rc == 0 && skp != NULL)
rc = netlbl_cfg_unlbl_static_add(&init_net, NULL,
- &snp->smk_host.sin_addr, &snp->smk_mask, PF_INET,
+ &snp->smk_host, &snp->smk_mask, PF_INET,
snp->smk_label->smk_secid, &audit_info);
if (rc == 0)
rc = count;
- mutex_unlock(&smk_netlbladdr_lock);
+ mutex_unlock(&smk_net4addr_lock);
free_out:
kfree(smack);
@@ -1322,14 +1313,279 @@ free_data_out:
return rc;
}
-static const struct file_operations smk_netlbladdr_ops = {
- .open = smk_open_netlbladdr,
+static const struct file_operations smk_net4addr_ops = {
+ .open = smk_open_net4addr,
.read = seq_read,
.llseek = seq_lseek,
- .write = smk_write_netlbladdr,
+ .write = smk_write_net4addr,
.release = seq_release,
};
+#if IS_ENABLED(CONFIG_IPV6)
+/*
+ * Seq_file read operations for /smack/netlabel6
+ */
+
+static void *net6addr_seq_start(struct seq_file *s, loff_t *pos)
+{
+ return smk_seq_start(s, pos, &smk_net6addr_list);
+}
+
+static void *net6addr_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return smk_seq_next(s, v, pos, &smk_net6addr_list);
+}
+
+/*
+ * Print host/label pairs
+ */
+static int net6addr_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smk_net6addr *skp =
+ list_entry(list, struct smk_net6addr, list);
+
+ if (skp->smk_label != NULL)
+ seq_printf(s, "%pI6/%d %s\n", &skp->smk_host, skp->smk_masks,
+ skp->smk_label->smk_known);
+
+ return 0;
+}
+
+static const struct seq_operations net6addr_seq_ops = {
+ .start = net6addr_seq_start,
+ .next = net6addr_seq_next,
+ .show = net6addr_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_net6addr - open() for /smack/netlabel
+ * @inode: inode structure representing file
+ * @file: "netlabel" file pointer
+ *
+ * Connect our net6addr_seq_* operations with /smack/netlabel
+ * file_operations
+ */
+static int smk_open_net6addr(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &net6addr_seq_ops);
+}
+
+/**
+ * smk_net6addr_insert
+ * @new : entry to insert
+ *
+ * This inserts an entry in the smack_net6addrs list
+ * sorted by netmask length (longest to smallest)
+ * locked by &smk_net6addr_lock in smk_write_net6addr
+ *
+ */
+static void smk_net6addr_insert(struct smk_net6addr *new)
+{
+ struct smk_net6addr *m_next;
+ struct smk_net6addr *m;
+
+ if (list_empty(&smk_net6addr_list)) {
+ list_add_rcu(&new->list, &smk_net6addr_list);
+ return;
+ }
+
+ m = list_entry_rcu(smk_net6addr_list.next,
+ struct smk_net6addr, list);
+
+ if (new->smk_masks > m->smk_masks) {
+ list_add_rcu(&new->list, &smk_net6addr_list);
+ return;
+ }
+
+ list_for_each_entry_rcu(m, &smk_net6addr_list, list) {
+ if (list_is_last(&m->list, &smk_net6addr_list)) {
+ list_add_rcu(&new->list, &m->list);
+ return;
+ }
+ m_next = list_entry_rcu(m->list.next,
+ struct smk_net6addr, list);
+ if (new->smk_masks > m_next->smk_masks) {
+ list_add_rcu(&new->list, &m->list);
+ return;
+ }
+ }
+}
+
+
+/**
+ * smk_write_net6addr - write() for /smack/netlabel
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start
+ *
+ * Accepts only one net6addr per write call.
+ * Returns number of bytes written or error code, as appropriate
+ */
+static ssize_t smk_write_net6addr(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct smk_net6addr *snp;
+ struct in6_addr newname;
+ struct in6_addr fullmask;
+ struct smack_known *skp = NULL;
+ char *smack;
+ char *data;
+ int rc = 0;
+ int found = 0;
+ int i;
+ unsigned int scanned[8];
+ unsigned int m;
+ unsigned int mask = 128;
+
+ /*
+ * Must have privilege.
+ * No partial writes.
+ * Enough data must be present.
+ * "<addr/mask, as a:b:c:d:e:f:g:h/e><space><label>"
+ * "<addr, as a:b:c:d:e:f:g:h><space><label>"
+ */
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
+ if (*ppos != 0)
+ return -EINVAL;
+ if (count < SMK_NETLBLADDRMIN)
+ return -EINVAL;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto free_data_out;
+ }
+
+ smack = kzalloc(count + 1, GFP_KERNEL);
+ if (smack == NULL) {
+ rc = -ENOMEM;
+ goto free_data_out;
+ }
+
+ data[count] = '\0';
+
+ i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x/%u %s",
+ &scanned[0], &scanned[1], &scanned[2], &scanned[3],
+ &scanned[4], &scanned[5], &scanned[6], &scanned[7],
+ &mask, smack);
+ if (i != 10) {
+ i = sscanf(data, "%x:%x:%x:%x:%x:%x:%x:%x %s",
+ &scanned[0], &scanned[1], &scanned[2],
+ &scanned[3], &scanned[4], &scanned[5],
+ &scanned[6], &scanned[7], smack);
+ if (i != 9) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ }
+ if (mask > 128) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ for (i = 0; i < 8; i++) {
+ if (scanned[i] > 0xffff) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ newname.s6_addr16[i] = htons(scanned[i]);
+ }
+
+ /*
+ * If smack begins with '-', it is an option, don't import it
+ */
+ if (smack[0] != '-') {
+ skp = smk_import_entry(smack, 0);
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
+ goto free_out;
+ }
+ } else {
+ /*
+ * Only -DELETE is supported for IPv6
+ */
+ if (strcmp(smack, SMACK_DELETE_OPTION) != 0) {
+ rc = -EINVAL;
+ goto free_out;
+ }
+ }
+
+ for (i = 0, m = mask; i < 8; i++) {
+ if (m >= 16) {
+ fullmask.s6_addr16[i] = 0xffff;
+ m -= 16;
+ } else if (m > 0) {
+ fullmask.s6_addr16[i] = (1 << m) - 1;
+ m = 0;
+ } else
+ fullmask.s6_addr16[i] = 0;
+ newname.s6_addr16[i] &= fullmask.s6_addr16[i];
+ }
+
+ /*
+ * Only allow one writer at a time. Writes should be
+ * quite rare and small in any case.
+ */
+ mutex_lock(&smk_net6addr_lock);
+ /*
+ * Try to find the prefix in the list
+ */
+ list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
+ if (mask != snp->smk_masks)
+ continue;
+ for (found = 1, i = 0; i < 8; i++) {
+ if (newname.s6_addr16[i] !=
+ snp->smk_host.s6_addr16[i]) {
+ found = 0;
+ break;
+ }
+ }
+ if (found == 1)
+ break;
+ }
+ if (found == 0) {
+ snp = kzalloc(sizeof(*snp), GFP_KERNEL);
+ if (snp == NULL)
+ rc = -ENOMEM;
+ else {
+ snp->smk_host = newname;
+ snp->smk_mask = fullmask;
+ snp->smk_masks = mask;
+ snp->smk_label = skp;
+ smk_net6addr_insert(snp);
+ }
+ } else {
+ snp->smk_label = skp;
+ }
+
+ if (rc == 0)
+ rc = count;
+
+ mutex_unlock(&smk_net6addr_lock);
+
+free_out:
+ kfree(smack);
+free_data_out:
+ kfree(data);
+
+ return rc;
+}
+
+static const struct file_operations smk_net6addr_ops = {
+ .open = smk_open_net6addr,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_net6addr,
+ .release = seq_release,
+};
+#endif /* CONFIG_IPV6 */
+
/**
* smk_read_doi - read() for /smack/doi
* @filp: file pointer, not actually used
@@ -1619,8 +1875,8 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf,
}
skp = smk_import_entry(data, count);
- if (skp == NULL) {
- rc = -EINVAL;
+ if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
goto out;
}
@@ -1643,34 +1899,127 @@ static const struct file_operations smk_ambient_ops = {
.llseek = default_llseek,
};
+/*
+ * Seq_file operations for /smack/onlycap
+ */
+static void *onlycap_seq_start(struct seq_file *s, loff_t *pos)
+{
+ return smk_seq_start(s, pos, &smack_onlycap_list);
+}
+
+static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ return smk_seq_next(s, v, pos, &smack_onlycap_list);
+}
+
+static int onlycap_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_known_list_elem *sklep =
+ list_entry_rcu(list, struct smack_known_list_elem, list);
+
+ seq_puts(s, sklep->smk_label->smk_known);
+ seq_putc(s, ' ');
+
+ return 0;
+}
+
+static const struct seq_operations onlycap_seq_ops = {
+ .start = onlycap_seq_start,
+ .next = onlycap_seq_next,
+ .show = onlycap_seq_show,
+ .stop = smk_seq_stop,
+};
+
+static int smk_open_onlycap(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &onlycap_seq_ops);
+}
+
/**
- * smk_read_onlycap - read() for smackfs/onlycap
- * @filp: file pointer, not actually used
- * @buf: where to put the result
- * @cn: maximum to send along
- * @ppos: where to start
+ * smk_list_swap_rcu - swap public list with a private one in RCU-safe way
+ * The caller must hold appropriate mutex to prevent concurrent modifications
+ * to the public list.
+ * Private list is assumed to be not accessible to other threads yet.
*
- * Returns number of bytes read or error code, as appropriate
+ * @public: public list
+ * @private: private list
*/
-static ssize_t smk_read_onlycap(struct file *filp, char __user *buf,
- size_t cn, loff_t *ppos)
+static void smk_list_swap_rcu(struct list_head *public,
+ struct list_head *private)
{
- char *smack = "";
- ssize_t rc = -EINVAL;
- int asize;
+ struct list_head *first, *last;
- if (*ppos != 0)
- return 0;
+ if (list_empty(public)) {
+ list_splice_init_rcu(private, public, synchronize_rcu);
+ } else {
+ /* Remember public list before replacing it */
+ first = public->next;
+ last = public->prev;
+
+ /* Publish private list in place of public in RCU-safe way */
+ private->prev->next = public;
+ private->next->prev = public;
+ rcu_assign_pointer(public->next, private->next);
+ public->prev = private->prev;
+
+ synchronize_rcu();
+
+ /* When all readers are done with the old public list,
+ * attach it in place of private */
+ private->next = first;
+ private->prev = last;
+ first->prev = private;
+ last->next = private;
+ }
+}
+
+/**
+ * smk_parse_label_list - parse list of Smack labels, separated by spaces
+ *
+ * @data: the string to parse
+ * @private: destination list
+ *
+ * Returns zero on success or error code, as appropriate
+ */
+static int smk_parse_label_list(char *data, struct list_head *list)
+{
+ char *tok;
+ struct smack_known *skp;
+ struct smack_known_list_elem *sklep;
- if (smack_onlycap != NULL)
- smack = smack_onlycap->smk_known;
+ while ((tok = strsep(&data, " ")) != NULL) {
+ if (!*tok)
+ continue;
- asize = strlen(smack) + 1;
+ skp = smk_import_entry(tok, 0);
+ if (IS_ERR(skp))
+ return PTR_ERR(skp);
- if (cn >= asize)
- rc = simple_read_from_buffer(buf, cn, ppos, smack, asize);
+ sklep = kzalloc(sizeof(*sklep), GFP_KERNEL);
+ if (sklep == NULL)
+ return -ENOMEM;
- return rc;
+ sklep->smk_label = skp;
+ list_add(&sklep->list, list);
+ }
+
+ return 0;
+}
+
+/**
+ * smk_destroy_label_list - destroy a list of smack_known_list_elem
+ * @head: header pointer of the list to destroy
+ */
+void smk_destroy_label_list(struct list_head *list)
+{
+ struct smack_known_list_elem *sklep;
+ struct smack_known_list_elem *sklep2;
+
+ list_for_each_entry_safe(sklep, sklep2, list, list)
+ kfree(sklep);
+
+ INIT_LIST_HEAD(list);
}
/**
@@ -1686,47 +2035,52 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *data;
- struct smack_known *skp = smk_of_task(current->cred->security);
- int rc = count;
+ LIST_HEAD(list_tmp);
+ int rc;
if (!smack_privileged(CAP_MAC_ADMIN))
return -EPERM;
- /*
- * This can be done using smk_access() but is done
- * explicitly for clarity. The smk_access() implementation
- * would use smk_access(smack_onlycap, MAY_WRITE)
- */
- if (smack_onlycap != NULL && smack_onlycap != skp)
- return -EPERM;
-
data = kzalloc(count + 1, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ rc = smk_parse_label_list(data, &list_tmp);
+ kfree(data);
+
/*
- * Should the null string be passed in unset the onlycap value.
- * This seems like something to be careful with as usually
- * smk_import only expects to return NULL for errors. It
- * is usually the case that a nullstring or "\n" would be
- * bad to pass to smk_import but in fact this is useful here.
+ * Clear the smack_onlycap on invalid label errors. This means
+ * that we can pass a null string to unset the onlycap value.
*
- * smk_import will also reject a label beginning with '-',
+ * Importing will also reject a label beginning with '-',
* so "-usecapabilities" will also work.
+ *
+ * But do so only on invalid label, not on system errors.
+ * The invalid label must be first to count as clearing attempt.
*/
- if (copy_from_user(data, buf, count) != 0)
- rc = -EFAULT;
- else
- smack_onlycap = smk_import_entry(data, count);
+ if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
+ mutex_lock(&smack_onlycap_lock);
+ smk_list_swap_rcu(&smack_onlycap_list, &list_tmp);
+ mutex_unlock(&smack_onlycap_lock);
+ rc = count;
+ }
+
+ smk_destroy_label_list(&list_tmp);
- kfree(data);
return rc;
}
static const struct file_operations smk_onlycap_ops = {
- .read = smk_read_onlycap,
+ .open = smk_open_onlycap,
+ .read = seq_read,
.write = smk_write_onlycap,
- .llseek = default_llseek,
+ .llseek = seq_lseek,
+ .release = seq_release,
};
#ifdef CONFIG_SECURITY_SMACK_BRINGUP
@@ -1773,6 +2127,7 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *data;
+ struct smack_known *skp;
int rc = count;
if (!smack_privileged(CAP_MAC_ADMIN))
@@ -1782,21 +2137,31 @@ static ssize_t smk_write_unconfined(struct file *file, const char __user *buf,
if (data == NULL)
return -ENOMEM;
+ if (copy_from_user(data, buf, count) != 0) {
+ rc = -EFAULT;
+ goto freeout;
+ }
+
/*
- * Should the null string be passed in unset the unconfined value.
- * This seems like something to be careful with as usually
- * smk_import only expects to return NULL for errors. It
- * is usually the case that a nullstring or "\n" would be
- * bad to pass to smk_import but in fact this is useful here.
+ * Clear the smack_unconfined on invalid label errors. This means
+ * that we can pass a null string to unset the unconfined value.
*
- * smk_import will also reject a label beginning with '-',
+ * Importing will also reject a label beginning with '-',
* so "-confine" will also work.
+ *
+ * But do so only on invalid label, not on system errors.
*/
- if (copy_from_user(data, buf, count) != 0)
- rc = -EFAULT;
- else
- smack_unconfined = smk_import_entry(data, count);
+ skp = smk_import_entry(data, count);
+ if (PTR_ERR(skp) == -EINVAL)
+ skp = NULL;
+ else if (IS_ERR(skp)) {
+ rc = PTR_ERR(skp);
+ goto freeout;
+ }
+
+ smack_unconfined = skp;
+freeout:
kfree(data);
return rc;
}
@@ -1895,7 +2260,7 @@ static int load_self_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_rule *srp =
- list_entry(list, struct smack_rule, list);
+ list_entry_rcu(list, struct smack_rule, list);
smk_rule_show(s, srp, SMK_LABELLEN);
@@ -1980,7 +2345,7 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf,
res = smk_access(rule.smk_subject, rule.smk_object,
rule.smk_access1, NULL);
else if (res != -ENOENT)
- return -EINVAL;
+ return res;
/*
* smk_access() can return a value > 0 in the "bringup" case.
@@ -2024,7 +2389,7 @@ static int load2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_master_list *smlp =
- list_entry(list, struct smack_master_list, list);
+ list_entry_rcu(list, struct smack_master_list, list);
smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL);
@@ -2101,7 +2466,7 @@ static int load_self2_seq_show(struct seq_file *s, void *v)
{
struct list_head *list = v;
struct smack_rule *srp =
- list_entry(list, struct smack_rule, list);
+ list_entry_rcu(list, struct smack_rule, list);
smk_rule_show(s, srp, SMK_LONGLABEL);
@@ -2182,8 +2547,8 @@ static const struct file_operations smk_access2_ops = {
static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
- char *data = NULL;
- const char *cp = NULL;
+ char *data;
+ const char *cp;
struct smack_known *skp;
struct smack_rule *sp;
struct list_head *rule_list;
@@ -2205,18 +2570,18 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
if (copy_from_user(data, buf, count) != 0) {
rc = -EFAULT;
- goto free_out;
+ goto out_data;
}
cp = smk_parse_smack(data, count);
- if (cp == NULL) {
- rc = -EINVAL;
- goto free_out;
+ if (IS_ERR(cp)) {
+ rc = PTR_ERR(cp);
+ goto out_data;
}
skp = smk_find_entry(cp);
if (skp == NULL)
- goto free_out;
+ goto out_cp;
rule_list = &skp->smk_rules;
rule_lock = &skp->smk_rules_lock;
@@ -2228,9 +2593,11 @@ static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
mutex_unlock(rule_lock);
-free_out:
- kfree(data);
+out_cp:
kfree(cp);
+out_data:
+ kfree(data);
+
return rc;
}
@@ -2247,11 +2614,7 @@ static const struct file_operations smk_revoke_subj_ops = {
*/
static int smk_init_sysfs(void)
{
- int err;
- err = sysfs_create_mount_point(fs_kobj, "smackfs");
- if (err)
- return err;
- return 0;
+ return sysfs_create_mount_point(fs_kobj, "smackfs");
}
/**
@@ -2341,10 +2704,10 @@ static ssize_t smk_write_syslog(struct file *file, const char __user *buf,
rc = -EFAULT;
else {
skp = smk_import_entry(data, count);
- if (skp == NULL)
- rc = -EINVAL;
+ if (IS_ERR(skp))
+ rc = PTR_ERR(skp);
else
- smack_syslog_label = smk_import_entry(data, count);
+ smack_syslog_label = skp;
}
kfree(data);
@@ -2357,6 +2720,113 @@ static const struct file_operations smk_syslog_ops = {
.llseek = default_llseek,
};
+/*
+ * Seq_file read operations for /smack/relabel-self
+ */
+
+static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_seq_start(s, pos, &tsp->smk_relabel);
+}
+
+static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos)
+{
+ struct task_smack *tsp = current_security();
+
+ return smk_seq_next(s, v, pos, &tsp->smk_relabel);
+}
+
+static int relabel_self_seq_show(struct seq_file *s, void *v)
+{
+ struct list_head *list = v;
+ struct smack_known_list_elem *sklep =
+ list_entry(list, struct smack_known_list_elem, list);
+
+ seq_puts(s, sklep->smk_label->smk_known);
+ seq_putc(s, ' ');
+
+ return 0;
+}
+
+static const struct seq_operations relabel_self_seq_ops = {
+ .start = relabel_self_seq_start,
+ .next = relabel_self_seq_next,
+ .show = relabel_self_seq_show,
+ .stop = smk_seq_stop,
+};
+
+/**
+ * smk_open_relabel_self - open() for /smack/relabel-self
+ * @inode: inode structure representing file
+ * @file: "relabel-self" file pointer
+ *
+ * Connect our relabel_self_seq_* operations with /smack/relabel-self
+ * file_operations
+ */
+static int smk_open_relabel_self(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &relabel_self_seq_ops);
+}
+
+/**
+ * smk_write_relabel_self - write() for /smack/relabel-self
+ * @file: file pointer, not actually used
+ * @buf: where to get the data from
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ *
+ */
+static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_smack *tsp = current_security();
+ char *data;
+ int rc;
+ LIST_HEAD(list_tmp);
+
+ /*
+ * Must have privilege.
+ */
+ if (!smack_privileged(CAP_MAC_ADMIN))
+ return -EPERM;
+
+ /*
+ * Enough data must be present.
+ */
+ if (*ppos != 0)
+ return -EINVAL;
+
+ data = kzalloc(count + 1, GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, count) != 0) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ rc = smk_parse_label_list(data, &list_tmp);
+ kfree(data);
+
+ if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) {
+ smk_destroy_label_list(&tsp->smk_relabel);
+ list_splice(&list_tmp, &tsp->smk_relabel);
+ return count;
+ }
+
+ smk_destroy_label_list(&list_tmp);
+ return rc;
+}
+
+static const struct file_operations smk_relabel_self_ops = {
+ .open = smk_open_relabel_self,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = smk_write_relabel_self,
+ .release = seq_release,
+};
/**
* smk_read_ptrace - read() for /smack/ptrace
@@ -2446,8 +2916,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
"direct", &smk_direct_ops, S_IRUGO|S_IWUSR},
[SMK_AMBIENT] = {
"ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR},
- [SMK_NETLBLADDR] = {
- "netlabel", &smk_netlbladdr_ops, S_IRUGO|S_IWUSR},
+ [SMK_NET4ADDR] = {
+ "netlabel", &smk_net4addr_ops, S_IRUGO|S_IWUSR},
[SMK_ONLYCAP] = {
"onlycap", &smk_onlycap_ops, S_IRUGO|S_IWUSR},
[SMK_LOGGING] = {
@@ -2479,6 +2949,13 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
[SMK_UNCONFINED] = {
"unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR},
#endif
+#if IS_ENABLED(CONFIG_IPV6)
+ [SMK_NET6ADDR] = {
+ "ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR},
+#endif /* CONFIG_IPV6 */
+ [SMK_RELABEL_SELF] = {
+ "relabel-self", &smk_relabel_self_ops,
+ S_IRUGO|S_IWUGO},
/* last one */
{""}
};
@@ -2547,7 +3024,7 @@ static int __init init_smk_fs(void)
int err;
int rc;
- if (!security_module_enable(&smack_ops))
+ if (smack_enabled == 0)
return 0;
err = smk_init_sysfs();
diff --git a/kernel/security/tomoyo/tomoyo.c b/kernel/security/tomoyo/tomoyo.c
index 57c88d52f..cbf3df422 100644
--- a/kernel/security/tomoyo/tomoyo.c
+++ b/kernel/security/tomoyo/tomoyo.c
@@ -4,7 +4,7 @@
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include "common.h"
/**
@@ -72,12 +72,6 @@ static void tomoyo_cred_free(struct cred *cred)
*/
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{
- int rc;
-
- rc = cap_bprm_set_creds(bprm);
- if (rc)
- return rc;
-
/*
* Do only if this function is called for the first time of an execve
* operation.
@@ -502,36 +496,35 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
* tomoyo_security_ops is a "struct security_operations" which is used for
* registering TOMOYO.
*/
-static struct security_operations tomoyo_security_ops = {
- .name = "tomoyo",
- .cred_alloc_blank = tomoyo_cred_alloc_blank,
- .cred_prepare = tomoyo_cred_prepare,
- .cred_transfer = tomoyo_cred_transfer,
- .cred_free = tomoyo_cred_free,
- .bprm_set_creds = tomoyo_bprm_set_creds,
- .bprm_check_security = tomoyo_bprm_check_security,
- .file_fcntl = tomoyo_file_fcntl,
- .file_open = tomoyo_file_open,
- .path_truncate = tomoyo_path_truncate,
- .path_unlink = tomoyo_path_unlink,
- .path_mkdir = tomoyo_path_mkdir,
- .path_rmdir = tomoyo_path_rmdir,
- .path_symlink = tomoyo_path_symlink,
- .path_mknod = tomoyo_path_mknod,
- .path_link = tomoyo_path_link,
- .path_rename = tomoyo_path_rename,
- .inode_getattr = tomoyo_inode_getattr,
- .file_ioctl = tomoyo_file_ioctl,
- .path_chmod = tomoyo_path_chmod,
- .path_chown = tomoyo_path_chown,
- .path_chroot = tomoyo_path_chroot,
- .sb_mount = tomoyo_sb_mount,
- .sb_umount = tomoyo_sb_umount,
- .sb_pivotroot = tomoyo_sb_pivotroot,
- .socket_bind = tomoyo_socket_bind,
- .socket_connect = tomoyo_socket_connect,
- .socket_listen = tomoyo_socket_listen,
- .socket_sendmsg = tomoyo_socket_sendmsg,
+static struct security_hook_list tomoyo_hooks[] = {
+ LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank),
+ LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
+ LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer),
+ LSM_HOOK_INIT(cred_free, tomoyo_cred_free),
+ LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds),
+ LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security),
+ LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl),
+ LSM_HOOK_INIT(file_open, tomoyo_file_open),
+ LSM_HOOK_INIT(path_truncate, tomoyo_path_truncate),
+ LSM_HOOK_INIT(path_unlink, tomoyo_path_unlink),
+ LSM_HOOK_INIT(path_mkdir, tomoyo_path_mkdir),
+ LSM_HOOK_INIT(path_rmdir, tomoyo_path_rmdir),
+ LSM_HOOK_INIT(path_symlink, tomoyo_path_symlink),
+ LSM_HOOK_INIT(path_mknod, tomoyo_path_mknod),
+ LSM_HOOK_INIT(path_link, tomoyo_path_link),
+ LSM_HOOK_INIT(path_rename, tomoyo_path_rename),
+ LSM_HOOK_INIT(inode_getattr, tomoyo_inode_getattr),
+ LSM_HOOK_INIT(file_ioctl, tomoyo_file_ioctl),
+ LSM_HOOK_INIT(path_chmod, tomoyo_path_chmod),
+ LSM_HOOK_INIT(path_chown, tomoyo_path_chown),
+ LSM_HOOK_INIT(path_chroot, tomoyo_path_chroot),
+ LSM_HOOK_INIT(sb_mount, tomoyo_sb_mount),
+ LSM_HOOK_INIT(sb_umount, tomoyo_sb_umount),
+ LSM_HOOK_INIT(sb_pivotroot, tomoyo_sb_pivotroot),
+ LSM_HOOK_INIT(socket_bind, tomoyo_socket_bind),
+ LSM_HOOK_INIT(socket_connect, tomoyo_socket_connect),
+ LSM_HOOK_INIT(socket_listen, tomoyo_socket_listen),
+ LSM_HOOK_INIT(socket_sendmsg, tomoyo_socket_sendmsg),
};
/* Lock for GC. */
@@ -546,11 +539,10 @@ static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
- if (!security_module_enable(&tomoyo_security_ops))
+ if (!security_module_enable("tomoyo"))
return 0;
/* register ourselves with the security framework */
- if (register_security(&tomoyo_security_ops))
- panic("Failure registering TOMOYO Linux");
+ security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks));
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
tomoyo_mm_init();
diff --git a/kernel/security/yama/Kconfig b/kernel/security/yama/Kconfig
index 3123e1da2..90c605eea 100644
--- a/kernel/security/yama/Kconfig
+++ b/kernel/security/yama/Kconfig
@@ -6,14 +6,7 @@ config SECURITY_YAMA
This selects Yama, which extends DAC support with additional
system-wide security settings beyond regular Linux discretionary
access controls. Currently available is ptrace scope restriction.
+ Like capabilities, this security module stacks with other LSMs.
Further information can be found in Documentation/security/Yama.txt.
If you are unsure how to answer this question, answer N.
-
-config SECURITY_YAMA_STACKED
- bool "Yama stacked with other LSMs"
- depends on SECURITY_YAMA
- default n
- help
- When Yama is built into the kernel, force it to stack with the
- selected primary LSM.
diff --git a/kernel/security/yama/yama_lsm.c b/kernel/security/yama/yama_lsm.c
index 24aae2ae2..cb6ed1081 100644
--- a/kernel/security/yama/yama_lsm.c
+++ b/kernel/security/yama/yama_lsm.c
@@ -12,7 +12,7 @@
*
*/
-#include <linux/security.h>
+#include <linux/lsm_hooks.h>
#include <linux/sysctl.h>
#include <linux/ptrace.h>
#include <linux/prctl.h>
@@ -154,13 +154,9 @@ void yama_task_free(struct task_struct *task)
int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
- int rc;
+ int rc = -ENOSYS;
struct task_struct *myself = current;
- rc = cap_task_prctl(option, arg2, arg3, arg4, arg5);
- if (rc != -ENOSYS)
- return rc;
-
switch (option) {
case PR_SET_PTRACER:
/* Since a thread can call prctl(), find the group leader
@@ -279,20 +275,13 @@ static int ptracer_exception_found(struct task_struct *tracer,
*
* Returns 0 if following the ptrace is allowed, -ve on error.
*/
-int yama_ptrace_access_check(struct task_struct *child,
+static int yama_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
- int rc;
-
- /* If standard caps disallows it, so does Yama. We should
- * only tighten restrictions further.
- */
- rc = cap_ptrace_access_check(child, mode);
- if (rc)
- return rc;
+ int rc = 0;
/* require ptrace target be a child of ptracer on attach */
- if (mode == PTRACE_MODE_ATTACH) {
+ if (mode & PTRACE_MODE_ATTACH) {
switch (ptrace_scope) {
case YAMA_SCOPE_DISABLED:
/* No additional restrictions. */
@@ -318,7 +307,7 @@ int yama_ptrace_access_check(struct task_struct *child,
}
}
- if (rc) {
+ if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) {
printk_ratelimited(KERN_NOTICE
"ptrace of pid %d was attempted by: %s (pid %d)\n",
child->pid, current->comm, current->pid);
@@ -335,14 +324,7 @@ int yama_ptrace_access_check(struct task_struct *child,
*/
int yama_ptrace_traceme(struct task_struct *parent)
{
- int rc;
-
- /* If standard caps disallows it, so does Yama. We should
- * only tighten restrictions further.
- */
- rc = cap_ptrace_traceme(parent);
- if (rc)
- return rc;
+ int rc = 0;
/* Only disallow PTRACE_TRACEME on more aggressive settings. */
switch (ptrace_scope) {
@@ -364,16 +346,12 @@ int yama_ptrace_traceme(struct task_struct *parent)
return rc;
}
-#ifndef CONFIG_SECURITY_YAMA_STACKED
-static struct security_operations yama_ops = {
- .name = "yama",
-
- .ptrace_access_check = yama_ptrace_access_check,
- .ptrace_traceme = yama_ptrace_traceme,
- .task_prctl = yama_task_prctl,
- .task_free = yama_task_free,
+static struct security_hook_list yama_hooks[] = {
+ LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check),
+ LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme),
+ LSM_HOOK_INIT(task_prctl, yama_task_prctl),
+ LSM_HOOK_INIT(task_free, yama_task_free),
};
-#endif
#ifdef CONFIG_SYSCTL
static int yama_dointvec_minmax(struct ctl_table *table, int write,
@@ -413,28 +391,18 @@ static struct ctl_table yama_sysctl_table[] = {
},
{ }
};
-#endif /* CONFIG_SYSCTL */
-
-static __init int yama_init(void)
+static void __init yama_init_sysctl(void)
{
-#ifndef CONFIG_SECURITY_YAMA_STACKED
- if (!security_module_enable(&yama_ops))
- return 0;
-#endif
-
- printk(KERN_INFO "Yama: becoming mindful.\n");
-
-#ifndef CONFIG_SECURITY_YAMA_STACKED
- if (register_security(&yama_ops))
- panic("Yama: kernel registration failed.\n");
-#endif
-
-#ifdef CONFIG_SYSCTL
if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))
panic("Yama: sysctl registration failed.\n");
-#endif
-
- return 0;
}
+#else
+static inline void yama_init_sysctl(void) { }
+#endif /* CONFIG_SYSCTL */
-security_initcall(yama_init);
+void __init yama_add_hooks(void)
+{
+ pr_info("Yama: becoming mindful.\n");
+ security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks));
+ yama_init_sysctl();
+}