summaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/capability.c80
-rw-r--r--security/commoncap.c15
-rw-r--r--security/device_cgroup.c3
-rw-r--r--security/inode.c4
-rw-r--r--security/integrity/ima/Kconfig5
-rw-r--r--security/integrity/ima/ima.h5
-rw-r--r--security/integrity/ima/ima_api.c5
-rw-r--r--security/integrity/ima/ima_audit.c3
-rw-r--r--security/integrity/ima/ima_crypto.c5
-rw-r--r--security/integrity/ima/ima_fs.c39
-rw-r--r--security/integrity/ima/ima_iint.c23
-rw-r--r--security/integrity/ima/ima_init.c3
-rw-r--r--security/integrity/ima/ima_main.c242
-rw-r--r--security/integrity/ima/ima_policy.c117
-rw-r--r--security/integrity/ima/ima_queue.c5
-rw-r--r--security/keys/gc.c6
-rw-r--r--security/keys/internal.h12
-rw-r--r--security/keys/key.c47
-rw-r--r--security/keys/keyctl.c71
-rw-r--r--security/keys/keyring.c316
-rw-r--r--security/keys/permission.c2
-rw-r--r--security/keys/proc.c3
-rw-r--r--security/keys/process_keys.c10
-rw-r--r--security/keys/request_key.c107
-rw-r--r--security/keys/user_defined.c3
-rw-r--r--security/lsm_audit.c3
-rw-r--r--security/min_addr.c2
-rw-r--r--security/security.c126
-rw-r--r--security/selinux/avc.c31
-rw-r--r--security/selinux/hooks.c162
-rw-r--r--security/selinux/include/initial_sid_to_string.h2
-rw-r--r--security/selinux/include/netlabel.h8
-rw-r--r--security/selinux/include/objsec.h1
-rw-r--r--security/selinux/include/security.h13
-rw-r--r--security/selinux/netif.c1
-rw-r--r--security/selinux/netlabel.c15
-rw-r--r--security/selinux/netlink.c2
-rw-r--r--security/selinux/netnode.c1
-rw-r--r--security/selinux/netport.c1
-rw-r--r--security/selinux/nlmsgtab.c1
-rw-r--r--security/selinux/selinuxfs.c56
-rw-r--r--security/selinux/ss/avtab.h2
-rw-r--r--security/selinux/ss/context.h12
-rw-r--r--security/selinux/ss/ebitmap.c2
-rw-r--r--security/selinux/ss/mls.c50
-rw-r--r--security/selinux/ss/mls.h2
-rw-r--r--security/selinux/ss/mls_types.h7
-rw-r--r--security/selinux/ss/policydb.c134
-rw-r--r--security/selinux/ss/policydb.h10
-rw-r--r--security/selinux/ss/services.c322
-rw-r--r--security/selinux/ss/symtab.c1
-rw-r--r--security/selinux/xfrm.c1
-rw-r--r--security/smack/smack_access.c1
-rw-r--r--security/smack/smack_lsm.c18
-rw-r--r--security/smack/smackfs.c1
-rw-r--r--security/tomoyo/Makefile2
-rw-r--r--security/tomoyo/common.c491
-rw-r--r--security/tomoyo/common.h647
-rw-r--r--security/tomoyo/domain.c405
-rw-r--r--security/tomoyo/file.c788
-rw-r--r--security/tomoyo/gc.c412
-rw-r--r--security/tomoyo/path_group.c172
-rw-r--r--security/tomoyo/realpath.c307
-rw-r--r--security/tomoyo/realpath.h66
-rw-r--r--security/tomoyo/tomoyo.c142
-rw-r--r--security/tomoyo/tomoyo.h94
66 files changed, 3133 insertions, 2512 deletions
diff --git a/security/capability.c b/security/capability.c
index 5c700e1a4fd3..8168e3ecd5bf 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -12,11 +12,6 @@
#include <linux/security.h>
-static int cap_acct(struct file *file)
-{
- return 0;
-}
-
static int cap_sysctl(ctl_table *table, int op)
{
return 0;
@@ -80,42 +75,16 @@ static int cap_sb_mount(char *dev_name, struct path *path, char *type,
return 0;
}
-static int cap_sb_check_sb(struct vfsmount *mnt, struct path *path)
-{
- return 0;
-}
-
static int cap_sb_umount(struct vfsmount *mnt, int flags)
{
return 0;
}
-static void cap_sb_umount_close(struct vfsmount *mnt)
-{
-}
-
-static void cap_sb_umount_busy(struct vfsmount *mnt)
-{
-}
-
-static void cap_sb_post_remount(struct vfsmount *mnt, unsigned long flags,
- void *data)
-{
-}
-
-static void cap_sb_post_addmount(struct vfsmount *mnt, struct path *path)
-{
-}
-
static int cap_sb_pivotroot(struct path *old_path, struct path *new_path)
{
return 0;
}
-static void cap_sb_post_pivotroot(struct path *old_path, struct path *new_path)
-{
-}
-
static int cap_sb_set_mnt_opts(struct super_block *sb,
struct security_mnt_opts *opts)
{
@@ -221,10 +190,6 @@ static int cap_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
return 0;
}
-static void cap_inode_delete(struct inode *ino)
-{
-}
-
static void cap_inode_post_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
@@ -403,10 +368,6 @@ static int cap_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp)
return 0;
}
-static void cap_cred_commit(struct cred *new, const struct cred *old)
-{
-}
-
static void cap_cred_transfer(struct cred *new, const struct cred *old)
{
}
@@ -426,16 +387,6 @@ static int cap_kernel_module_request(char *kmod_name)
return 0;
}
-static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
- return 0;
-}
-
-static int cap_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
-{
- return 0;
-}
-
static int cap_task_setpgid(struct task_struct *p, pid_t pgid)
{
return 0;
@@ -456,11 +407,6 @@ static void cap_task_getsecid(struct task_struct *p, u32 *secid)
*secid = 0;
}
-static int cap_task_setgroups(struct group_info *group_info)
-{
- return 0;
-}
-
static int cap_task_getioprio(struct task_struct *p)
{
return 0;
@@ -875,13 +821,6 @@ static int cap_key_getsecurity(struct key *key, char **_buffer)
return 0;
}
-static int cap_key_session_to_parent(const struct cred *cred,
- const struct cred *parent_cred,
- struct key *key)
-{
- return 0;
-}
-
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
@@ -906,10 +845,6 @@ static void cap_audit_rule_free(void *lsmrule)
}
#endif /* CONFIG_AUDIT */
-struct security_operations default_security_ops = {
- .name = "default",
-};
-
#define set_to_cap_if_null(ops, function) \
do { \
if (!ops->function) { \
@@ -919,13 +854,12 @@ struct security_operations default_security_ops = {
} \
} while (0)
-void security_fixup_ops(struct security_operations *ops)
+void __init security_fixup_ops(struct security_operations *ops)
{
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, acct);
set_to_cap_if_null(ops, capable);
set_to_cap_if_null(ops, quotactl);
set_to_cap_if_null(ops, quota_on);
@@ -945,14 +879,8 @@ void security_fixup_ops(struct security_operations *ops)
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_check_sb);
set_to_cap_if_null(ops, sb_umount);
- set_to_cap_if_null(ops, sb_umount_close);
- set_to_cap_if_null(ops, sb_umount_busy);
- set_to_cap_if_null(ops, sb_post_remount);
- set_to_cap_if_null(ops, sb_post_addmount);
set_to_cap_if_null(ops, sb_pivotroot);
- set_to_cap_if_null(ops, sb_post_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);
@@ -972,7 +900,6 @@ void security_fixup_ops(struct security_operations *ops)
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_delete);
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);
@@ -1013,19 +940,15 @@ void security_fixup_ops(struct security_operations *ops)
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_commit);
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_module_request);
- set_to_cap_if_null(ops, task_setuid);
set_to_cap_if_null(ops, task_fix_setuid);
- set_to_cap_if_null(ops, task_setgid);
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_setgroups);
set_to_cap_if_null(ops, task_setnice);
set_to_cap_if_null(ops, task_setioprio);
set_to_cap_if_null(ops, task_getioprio);
@@ -1117,7 +1040,6 @@ void security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, key_free);
set_to_cap_if_null(ops, key_permission);
set_to_cap_if_null(ops, key_getsecurity);
- set_to_cap_if_null(ops, key_session_to_parent);
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
set_to_cap_if_null(ops, audit_rule_init);
diff --git a/security/commoncap.c b/security/commoncap.c
index f800fdb3de94..4e015996dd4d 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -27,6 +27,7 @@
#include <linux/sched.h>
#include <linux/prctl.h>
#include <linux/securebits.h>
+#include <linux/syslog.h>
/*
* If a non-root user executes a setuid-root binary in
@@ -569,7 +570,7 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
}
if (!strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
@@ -595,7 +596,7 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name)
}
if (!strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
@@ -888,13 +889,17 @@ error:
/**
* cap_syslog - Determine whether syslog function is permitted
* @type: Function requested
+ * @from_file: Whether this request came from an open file (i.e. /proc)
*
* Determine whether the current process is permitted to use a particular
* syslog function, returning 0 if permission is granted, -ve if not.
*/
-int cap_syslog(int type)
+int cap_syslog(int type, bool from_file)
{
- if ((type != 3 && type != 10) && !capable(CAP_SYS_ADMIN))
+ if (type != SYSLOG_ACTION_OPEN && from_file)
+ return 0;
+ if ((type != SYSLOG_ACTION_READ_ALL &&
+ type != SYSLOG_ACTION_SIZE_BUFFER) && !capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
}
@@ -926,7 +931,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)
* @addr: address attempting to be mapped
* @addr_only: unused
*
- * If the process is attempting to map memory below mmap_min_addr they need
+ * If the process is attempting to map memory below dac_mmap_min_addr they need
* CAP_SYS_RAWIO. The other parameters to this function are unused by the
* capability security module. Returns 0 if this mapping should be allowed
* -EPERM if not.
diff --git a/security/device_cgroup.c b/security/device_cgroup.c
index 6cf8fd2b79e8..8d9c48f13774 100644
--- a/security/device_cgroup.c
+++ b/security/device_cgroup.c
@@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
+#include <linux/slab.h>
#include <linux/rcupdate.h>
#include <linux/mutex.h>
@@ -469,7 +470,7 @@ struct cgroup_subsys devices_subsys = {
.name = "devices",
.can_attach = devcgroup_can_attach,
.create = devcgroup_create,
- .destroy = devcgroup_destroy,
+ .destroy = devcgroup_destroy,
.populate = devcgroup_populate,
.subsys_id = devices_subsys_id,
};
diff --git a/security/inode.c b/security/inode.c
index c3a793881d04..1c812e874504 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -161,13 +161,13 @@ static int create_by_name(const char *name, mode_t mode,
mutex_lock(&parent->d_inode->i_mutex);
*dentry = lookup_one_len(name, parent, strlen(name));
- if (!IS_ERR(dentry)) {
+ if (!IS_ERR(*dentry)) {
if ((mode & S_IFMT) == S_IFDIR)
error = mkdir(parent->d_inode, *dentry, mode);
else
error = create(parent->d_inode, *dentry, mode);
} else
- error = PTR_ERR(dentry);
+ error = PTR_ERR(*dentry);
mutex_unlock(&parent->d_inode->i_mutex);
return error;
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 3d7846de8069..b6ecfd4d8d78 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -2,15 +2,14 @@
#
config IMA
bool "Integrity Measurement Architecture(IMA)"
- depends on ACPI
depends on SECURITY
select SECURITYFS
select CRYPTO
select CRYPTO_HMAC
select CRYPTO_MD5
select CRYPTO_SHA1
- select TCG_TPM
- select TCG_TIS
+ select TCG_TPM if !S390
+ select TCG_TIS if TCG_TPM
help
The Trusted Computing Group(TCG) runtime Integrity
Measurement Architecture(IMA) maintains a list of hash
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index c41afe6639a0..16d100d3fc38 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -65,7 +65,6 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
const char *cause, int result, int info);
/* Internal IMA function definitions */
-void ima_iintcache_init(void);
int ima_init(void);
void ima_cleanup(void);
int ima_fs_init(void);
@@ -131,12 +130,12 @@ void iint_free(struct kref *kref);
void iint_rcu_free(struct rcu_head *rcu);
/* IMA policy related functions */
-enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
void ima_init_policy(void);
void ima_update_policy(void);
-int ima_parse_add_rule(char *);
+ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
/* LSM based policy rules require audit */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 3cd58b60afd2..52015d098fdf 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -13,6 +13,7 @@
* and store_template.
*/
#include <linux/module.h>
+#include <linux/slab.h>
#include "ima.h"
static const char *IMA_TEMPLATE_NAME = "ima";
@@ -95,12 +96,12 @@ err_out:
* ima_must_measure - measure decision based on policy.
* @inode: pointer to inode to measure
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
- * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
+ * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
* subj,obj, and type: are LSM specific.
- * func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
+ * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP
* mask: contains the permission mask
* fsmagic: hex value
*
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
index ff513ff737f5..c5c5a72c30be 100644
--- a/security/integrity/ima/ima_audit.c
+++ b/security/integrity/ima/ima_audit.c
@@ -11,6 +11,7 @@
*/
#include <linux/fs.h>
+#include <linux/gfp.h>
#include <linux/audit.h>
#include "ima.h"
@@ -40,7 +41,7 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
return;
ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
- audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u ses=%u",
+ audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
current->pid, current_cred()->uid,
audit_get_loginuid(current),
audit_get_sessionid(current));
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 46642a19bc78..9b3ade7468b2 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -18,6 +18,7 @@
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/err.h>
+#include <linux/slab.h>
#include "ima.h"
static int init_desc(struct hash_desc *desc)
@@ -26,7 +27,7 @@ static int init_desc(struct hash_desc *desc)
desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(desc->tfm)) {
- pr_info("failed to load %s transform: %ld\n",
+ pr_info("IMA: failed to load %s transform: %ld\n",
ima_hash, PTR_ERR(desc->tfm));
rc = PTR_ERR(desc->tfm);
return rc;
@@ -111,7 +112,7 @@ static void __init ima_pcrread(int idx, u8 *pcr)
return;
if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
- pr_err("Error Communicating to TPM chip\n");
+ pr_err("IMA: Error Communicating to TPM chip\n");
}
/*
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 0c72c9c38956..8fe736aabe71 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -16,6 +16,7 @@
* current measurement list and IMA statistics
*/
#include <linux/fcntl.h>
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/rculist.h>
@@ -243,32 +244,34 @@ static const struct file_operations ima_ascii_measurements_ops = {
static ssize_t ima_write_policy(struct file *file, const char __user *buf,
size_t datalen, loff_t *ppos)
{
- char *data;
- int rc;
+ char *data = NULL;
+ ssize_t result;
if (datalen >= PAGE_SIZE)
- return -ENOMEM;
- if (*ppos != 0) {
- /* No partial writes. */
- return -EINVAL;
- }
+ datalen = PAGE_SIZE - 1;
+
+ /* No partial writes. */
+ result = -EINVAL;
+ if (*ppos != 0)
+ goto out;
+
+ result = -ENOMEM;
data = kmalloc(datalen + 1, GFP_KERNEL);
if (!data)
- return -ENOMEM;
+ goto out;
- if (copy_from_user(data, buf, datalen)) {
- kfree(data);
- return -EFAULT;
- }
*(data + datalen) = '\0';
- rc = ima_parse_add_rule(data);
- if (rc < 0) {
- datalen = -EINVAL;
- valid_policy = 0;
- }
+ result = -EFAULT;
+ if (copy_from_user(data, buf, datalen))
+ goto out;
+
+ result = ima_parse_add_rule(data);
+out:
+ if (result < 0)
+ valid_policy = 0;
kfree(data);
- return datalen;
+ return result;
}
static struct dentry *ima_dir;
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
index fa592ff1ac1c..7625b85c2274 100644
--- a/security/integrity/ima/ima_iint.c
+++ b/security/integrity/ima/ima_iint.c
@@ -14,6 +14,7 @@
* - cache integrity information associated with an inode
* using a radix tree.
*/
+#include <linux/slab.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/radix-tree.h>
@@ -52,9 +53,6 @@ int ima_inode_alloc(struct inode *inode)
struct ima_iint_cache *iint = NULL;
int rc = 0;
- if (!ima_initialized)
- return 0;
-
iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
if (!iint)
return -ENOMEM;
@@ -66,12 +64,11 @@ int ima_inode_alloc(struct inode *inode)
spin_lock(&ima_iint_lock);
rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
spin_unlock(&ima_iint_lock);
+ radix_tree_preload_end();
out:
if (rc < 0)
kmem_cache_free(iint_cache, iint);
- radix_tree_preload_end();
-
return rc;
}
@@ -83,21 +80,21 @@ void iint_free(struct kref *kref)
iint->version = 0;
iint->flags = 0UL;
if (iint->readcount != 0) {
- printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__,
+ printk(KERN_INFO "%s: readcount: %ld\n", __func__,
iint->readcount);
iint->readcount = 0;
}
if (iint->writecount != 0) {
- printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__,
+ printk(KERN_INFO "%s: writecount: %ld\n", __func__,
iint->writecount);
iint->writecount = 0;
}
if (iint->opencount != 0) {
- printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__,
+ printk(KERN_INFO "%s: opencount: %ld\n", __func__,
iint->opencount);
iint->opencount = 0;
}
- kref_set(&iint->refcount, 1);
+ kref_init(&iint->refcount);
kmem_cache_free(iint_cache, iint);
}
@@ -118,8 +115,6 @@ void ima_inode_free(struct inode *inode)
{
struct ima_iint_cache *iint;
- if (!ima_initialized)
- return;
spin_lock(&ima_iint_lock);
iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
spin_unlock(&ima_iint_lock);
@@ -138,12 +133,14 @@ static void init_once(void *foo)
iint->readcount = 0;
iint->writecount = 0;
iint->opencount = 0;
- kref_set(&iint->refcount, 1);
+ kref_init(&iint->refcount);
}
-void __init ima_iintcache_init(void)
+static int __init ima_iintcache_init(void)
{
iint_cache =
kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
SLAB_PANIC, init_once);
+ return 0;
}
+security_initcall(ima_iintcache_init);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index a40da7ae5900..17f1f060306f 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -16,6 +16,7 @@
*/
#include <linux/module.h>
#include <linux/scatterlist.h>
+#include <linux/slab.h>
#include <linux/err.h>
#include "ima.h"
@@ -82,7 +83,7 @@ int __init ima_init(void)
ima_used_chip = 1;
if (!ima_used_chip)
- pr_info("No TPM chip found, activating TPM-bypass!\n");
+ pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
ima_add_boot_aggregate(); /* boot aggregate must be first entry */
ima_init_policy();
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index a89f44d5e030..f93641382e9f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -14,13 +14,14 @@
*
* File: ima_main.c
* implements the IMA hooks: ima_bprm_check, ima_file_mmap,
- * and ima_path_check.
+ * and ima_file_check.
*/
#include <linux/module.h>
#include <linux/file.h>
#include <linux/binfmts.h>
#include <linux/mount.h>
#include <linux/mman.h>
+#include <linux/slab.h>
#include "ima.h"
@@ -84,6 +85,36 @@ out:
return found;
}
+/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
+ *
+ * When opening a file for read, if the file is already open for write,
+ * the file could change, resulting in a file measurement error.
+ *
+ * Opening a file for write, if the file is already open for read, results
+ * in a time of measure, time of use (ToMToU) error.
+ *
+ * In either case invalidate the PCR.
+ */
+enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
+static void ima_read_write_check(enum iint_pcr_error error,
+ struct ima_iint_cache *iint,
+ struct inode *inode,
+ const unsigned char *filename)
+{
+ switch (error) {
+ case TOMTOU:
+ if (iint->readcount > 0)
+ ima_add_violation(inode, filename, "invalid_pcr",
+ "ToMToU");
+ break;
+ case OPEN_WRITERS:
+ if (iint->writecount > 0)
+ ima_add_violation(inode, filename, "invalid_pcr",
+ "open_writers");
+ break;
+ }
+}
+
/*
* Update the counts given an fmode_t
*/
@@ -99,6 +130,47 @@ static void ima_inc_counts(struct ima_iint_cache *iint, fmode_t mode)
}
/*
+ * ima_counts_get - increment file counts
+ *
+ * Maintain read/write counters for all files, but only
+ * invalidate the PCR for measured files:
+ * - Opening a file for write when already open for read,
+ * results in a time of measure, time of use (ToMToU) error.
+ * - Opening a file for read when already open for write,
+ * could result in a file measurement error.
+ *
+ */
+void ima_counts_get(struct file *file)
+{
+ struct dentry *dentry = file->f_path.dentry;
+ struct inode *inode = dentry->d_inode;
+ fmode_t mode = file->f_mode;
+ struct ima_iint_cache *iint;
+ int rc;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return;
+ iint = ima_iint_find_get(inode);
+ if (!iint)
+ return;
+ mutex_lock(&iint->mutex);
+ rc = ima_must_measure(iint, inode, MAY_READ, FILE_CHECK);
+ if (rc < 0)
+ goto out;
+
+ if (mode & FMODE_WRITE) {
+ ima_read_write_check(TOMTOU, iint, inode, dentry->d_name.name);
+ goto out;
+ }
+ ima_read_write_check(OPEN_WRITERS, iint, inode, dentry->d_name.name);
+out:
+ ima_inc_counts(iint, file->f_mode);
+ mutex_unlock(&iint->mutex);
+
+ kref_put(&iint->refcount, iint_free);
+}
+
+/*
* Decrement ima counts
*/
static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode,
@@ -123,7 +195,7 @@ static void ima_dec_counts(struct ima_iint_cache *iint, struct inode *inode,
(iint->writecount < 0)) &&
!ima_limit_imbalance(file)) {
printk(KERN_INFO "%s: open/free imbalance (r:%ld w:%ld o:%ld)\n",
- __FUNCTION__, iint->readcount, iint->writecount,
+ __func__, iint->readcount, iint->writecount,
iint->opencount);
dump_stack();
}
@@ -153,123 +225,6 @@ void ima_file_free(struct file *file)
kref_put(&iint->refcount, iint_free);
}
-/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
- *
- * When opening a file for read, if the file is already open for write,
- * the file could change, resulting in a file measurement error.
- *
- * Opening a file for write, if the file is already open for read, results
- * in a time of measure, time of use (ToMToU) error.
- *
- * In either case invalidate the PCR.
- */
-enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
-static void ima_read_write_check(enum iint_pcr_error error,
- struct ima_iint_cache *iint,
- struct inode *inode,
- const unsigned char *filename)
-{
- switch (error) {
- case TOMTOU:
- if (iint->readcount > 0)
- ima_add_violation(inode, filename, "invalid_pcr",
- "ToMToU");
- break;
- case OPEN_WRITERS:
- if (iint->writecount > 0)
- ima_add_violation(inode, filename, "invalid_pcr",
- "open_writers");
- break;
- }
-}
-
-static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
- const unsigned char *filename)
-{
- int rc = 0;
-
- ima_inc_counts(iint, file->f_mode);
-
- rc = ima_collect_measurement(iint, file);
- if (!rc)
- ima_store_measurement(iint, file, filename);
- return rc;
-}
-
-/**
- * ima_path_check - based on policy, collect/store measurement.
- * @path: contains a pointer to the path to be measured
- * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
- *
- * Measure the file being open for readonly, based on the
- * ima_must_measure() policy decision.
- *
- * Keep read/write counters for all files, but only
- * invalidate the PCR for measured files:
- * - Opening a file for write when already open for read,
- * results in a time of measure, time of use (ToMToU) error.
- * - Opening a file for read when already open for write,
- * could result in a file measurement error.
- *
- * Always return 0 and audit dentry_open failures.
- * (Return code will be based upon measurement appraisal.)
- */
-int ima_path_check(struct path *path, int mask)
-{
- struct inode *inode = path->dentry->d_inode;
- struct ima_iint_cache *iint;
- struct file *file = NULL;
- int rc;
-
- if (!ima_initialized || !S_ISREG(inode->i_mode))
- return 0;
- iint = ima_iint_find_get(inode);
- if (!iint)
- return 0;
-
- mutex_lock(&iint->mutex);
-
- rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
- if (rc < 0)
- goto out;
-
- if ((mask & MAY_WRITE) || (mask == 0))
- ima_read_write_check(TOMTOU, iint, inode,
- path->dentry->d_name.name);
-
- if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
- goto out;
-
- ima_read_write_check(OPEN_WRITERS, iint, inode,
- path->dentry->d_name.name);
- if (!(iint->flags & IMA_MEASURED)) {
- struct dentry *dentry = dget(path->dentry);
- struct vfsmount *mnt = mntget(path->mnt);
-
- file = dentry_open(dentry, mnt, O_RDONLY | O_LARGEFILE,
- current_cred());
- if (IS_ERR(file)) {
- int audit_info = 0;
-
- integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
- dentry->d_name.name,
- "add_measurement",
- "dentry_open failed",
- 1, audit_info);
- file = NULL;
- goto out;
- }
- rc = get_path_measurement(iint, file, dentry->d_name.name);
- }
-out:
- mutex_unlock(&iint->mutex);
- if (file)
- fput(file);
- kref_put(&iint->refcount, iint_free);
- return 0;
-}
-EXPORT_SYMBOL_GPL(ima_path_check);
-
static int process_measurement(struct file *file, const unsigned char *filename,
int mask, int function)
{
@@ -297,33 +252,6 @@ out:
return rc;
}
-/*
- * ima_counts_get - increment file counts
- *
- * - for IPC shm and shmat file.
- * - for nfsd exported files.
- *
- * Increment the counts for these files to prevent unnecessary
- * imbalance messages.
- */
-void ima_counts_get(struct file *file)
-{
- struct inode *inode = file->f_dentry->d_inode;
- struct ima_iint_cache *iint;
-
- if (!ima_initialized || !S_ISREG(inode->i_mode))
- return;
- iint = ima_iint_find_get(inode);
- if (!iint)
- return;
- mutex_lock(&iint->mutex);
- ima_inc_counts(iint, file->f_mode);
- mutex_unlock(&iint->mutex);
-
- kref_put(&iint->refcount, iint_free);
-}
-EXPORT_SYMBOL_GPL(ima_counts_get);
-
/**
* ima_file_mmap - based on policy, collect/store measurement.
* @file: pointer to the file to be measured (May be NULL)
@@ -369,11 +297,31 @@ int ima_bprm_check(struct linux_binprm *bprm)
return 0;
}
+/**
+ * ima_path_check - based on policy, collect/store measurement.
+ * @file: pointer to the file to be measured
+ * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ *
+ * Measure files based on the ima_must_measure() policy decision.
+ *
+ * Always return 0 and audit dentry_open failures.
+ * (Return code will be based upon measurement appraisal.)
+ */
+int ima_file_check(struct file *file, int mask)
+{
+ int rc;
+
+ rc = process_measurement(file, file->f_dentry->d_name.name,
+ mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
+ FILE_CHECK);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ima_file_check);
+
static int __init init_ima(void)
{
int error;
- ima_iintcache_init();
error = ima_init();
ima_initialized = 1;
return error;
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index e1278399b345..aef8c0a923ab 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -15,6 +15,7 @@
#include <linux/security.h>
#include <linux/magic.h>
#include <linux/parser.h>
+#include <linux/slab.h>
#include "ima.h"
@@ -67,7 +68,7 @@ static struct ima_measure_rule_entry default_rules[] = {
.flags = IMA_FUNC | IMA_MASK},
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
.flags = IMA_FUNC | IMA_MASK},
- {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
+ {.action = MEASURE,.func = FILE_CHECK,.mask = MAY_READ,.uid = 0,
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
};
@@ -245,6 +246,9 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
{
int result;
+ if (entry->lsm[lsm_rule].rule)
+ return -EINVAL;
+
entry->lsm[lsm_rule].type = audit_type;
result = security_filter_rule_init(entry->lsm[lsm_rule].type,
Audit_equal, args,
@@ -252,6 +256,13 @@ static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
return result;
}
+static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
+{
+ audit_log_format(ab, "%s=", key);
+ audit_log_untrustedstring(ab, value);
+ audit_log_format(ab, " ");
+}
+
static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
{
struct audit_buffer *ab;
@@ -260,30 +271,46 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
- entry->action = -1;
- while ((p = strsep(&rule, " \n")) != NULL) {
+ entry->uid = -1;
+ entry->action = UNKNOWN;
+ while ((p = strsep(&rule, " \t")) != NULL) {
substring_t args[MAX_OPT_ARGS];
int token;
unsigned long lnum;
if (result < 0)
break;
- if (!*p)
+ if ((*p == '\0') || (*p == ' ') || (*p == '\t'))
continue;
token = match_token(p, policy_tokens, args);
switch (token) {
case Opt_measure:
- audit_log_format(ab, "%s ", "measure");
+ ima_log_string(ab, "action", "measure");
+
+ if (entry->action != UNKNOWN)
+ result = -EINVAL;
+
entry->action = MEASURE;
break;
case Opt_dont_measure:
- audit_log_format(ab, "%s ", "dont_measure");
+ ima_log_string(ab, "action", "dont_measure");
+
+ if (entry->action != UNKNOWN)
+ result = -EINVAL;
+
entry->action = DONT_MEASURE;
break;
case Opt_func:
- audit_log_format(ab, "func=%s ", args[0].from);
- if (strcmp(args[0].from, "PATH_CHECK") == 0)
- entry->func = PATH_CHECK;
+ ima_log_string(ab, "func", args[0].from);
+
+ if (entry->func)
+ result = -EINVAL;
+
+ if (strcmp(args[0].from, "FILE_CHECK") == 0)
+ entry->func = FILE_CHECK;
+ /* PATH_CHECK is for backwards compat */
+ else if (strcmp(args[0].from, "PATH_CHECK") == 0)
+ entry->func = FILE_CHECK;
else if (strcmp(args[0].from, "FILE_MMAP") == 0)
entry->func = FILE_MMAP;
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
@@ -294,7 +321,11 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
entry->flags |= IMA_FUNC;
break;
case Opt_mask:
- audit_log_format(ab, "mask=%s ", args[0].from);
+ ima_log_string(ab, "mask", args[0].from);
+
+ if (entry->mask)
+ result = -EINVAL;
+
if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
entry->mask = MAY_EXEC;
else if (strcmp(args[0].from, "MAY_WRITE") == 0)
@@ -309,14 +340,26 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
entry->flags |= IMA_MASK;
break;
case Opt_fsmagic:
- audit_log_format(ab, "fsmagic=%s ", args[0].from);
+ ima_log_string(ab, "fsmagic", args[0].from);
+
+ if (entry->fsmagic) {
+ result = -EINVAL;
+ break;
+ }
+
result = strict_strtoul(args[0].from, 16,
&entry->fsmagic);
if (!result)
entry->flags |= IMA_FSMAGIC;
break;
case Opt_uid:
- audit_log_format(ab, "uid=%s ", args[0].from);
+ ima_log_string(ab, "uid", args[0].from);
+
+ if (entry->uid != -1) {
+ result = -EINVAL;
+ break;
+ }
+
result = strict_strtoul(args[0].from, 10, &lnum);
if (!result) {
entry->uid = (uid_t) lnum;
@@ -327,50 +370,51 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
}
break;
case Opt_obj_user:
- audit_log_format(ab, "obj_user=%s ", args[0].from);
+ ima_log_string(ab, "obj_user", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
LSM_OBJ_USER,
AUDIT_OBJ_USER);
break;
case Opt_obj_role:
- audit_log_format(ab, "obj_role=%s ", args[0].from);
+ ima_log_string(ab, "obj_role", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
LSM_OBJ_ROLE,
AUDIT_OBJ_ROLE);
break;
case Opt_obj_type:
- audit_log_format(ab, "obj_type=%s ", args[0].from);
+ ima_log_string(ab, "obj_type", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
LSM_OBJ_TYPE,
AUDIT_OBJ_TYPE);
break;
case Opt_subj_user:
- audit_log_format(ab, "subj_user=%s ", args[0].from);
+ ima_log_string(ab, "subj_user", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
LSM_SUBJ_USER,
AUDIT_SUBJ_USER);
break;
case Opt_subj_role:
- audit_log_format(ab, "subj_role=%s ", args[0].from);
+ ima_log_string(ab, "subj_role", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
LSM_SUBJ_ROLE,
AUDIT_SUBJ_ROLE);
break;
case Opt_subj_type:
- audit_log_format(ab, "subj_type=%s ", args[0].from);
+ ima_log_string(ab, "subj_type", args[0].from);
result = ima_lsm_rule_init(entry, args[0].from,
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE);
break;
case Opt_err:
- audit_log_format(ab, "UNKNOWN=%s ", p);
+ ima_log_string(ab, "UNKNOWN", p);
+ result = -EINVAL;
break;
}
}
- if (entry->action == UNKNOWN)
+ if (!result && (entry->action == UNKNOWN))
result = -EINVAL;
- audit_log_format(ab, "res=%d", !result ? 0 : 1);
+ audit_log_format(ab, "res=%d", !!result);
audit_log_end(ab);
return result;
}
@@ -380,13 +424,14 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
* @rule - ima measurement policy rule
*
* Uses a mutex to protect the policy list from multiple concurrent writers.
- * Returns 0 on success, an error code on failure.
+ * Returns the length of the rule parsed, an error code on failure
*/
-int ima_parse_add_rule(char *rule)
+ssize_t ima_parse_add_rule(char *rule)
{
const char *op = "update_policy";
+ char *p;
struct ima_measure_rule_entry *entry;
- int result = 0;
+ ssize_t result, len;
int audit_info = 0;
/* Prevent installed policy from changing */
@@ -406,18 +451,28 @@ int ima_parse_add_rule(char *rule)
INIT_LIST_HEAD(&entry->list);
- result = ima_parse_rule(rule, entry);
- if (!result) {
- mutex_lock(&ima_measure_mutex);
- list_add_tail(&entry->list, &measure_policy_rules);
- mutex_unlock(&ima_measure_mutex);
- } else {
+ p = strsep(&rule, "\n");
+ len = strlen(p) + 1;
+
+ if (*p == '#') {
+ kfree(entry);
+ return len;
+ }
+
+ result = ima_parse_rule(p, entry);
+ if (result) {
kfree(entry);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
NULL, op, "invalid policy", result,
audit_info);
+ return result;
}
- return result;
+
+ mutex_lock(&ima_measure_mutex);
+ list_add_tail(&entry->list, &measure_policy_rules);
+ mutex_unlock(&ima_measure_mutex);
+
+ return len;
}
/* ima_delete_rules called to cleanup invalid policy */
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index a0880e9c8e05..8e28f04a5e2e 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -20,6 +20,7 @@
*/
#include <linux/module.h>
#include <linux/rculist.h>
+#include <linux/slab.h>
#include "ima.h"
LIST_HEAD(ima_measurements); /* list of all measurements */
@@ -70,7 +71,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry)
qe = kmalloc(sizeof(*qe), GFP_KERNEL);
if (qe == NULL) {
- pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
+ pr_err("IMA: OUT OF MEMORY ERROR creating queue entry.\n");
return -ENOMEM;
}
qe->entry = entry;
@@ -93,7 +94,7 @@ static int ima_pcr_extend(const u8 *hash)
result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
if (result != 0)
- pr_err("Error Communicating to TPM chip\n");
+ pr_err("IMA: Error Communicating to TPM chip\n");
return result;
}
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 4770be375ffe..a46e825cbf02 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -77,9 +77,10 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
goto dont_gc;
/* scan the keyring looking for dead keys */
+ rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
if (!klist)
- goto dont_gc;
+ goto unlock_dont_gc;
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
key = klist->keys[loop];
@@ -88,11 +89,14 @@ static bool key_gc_keyring(struct key *keyring, time_t limit)
goto do_gc;
}
+unlock_dont_gc:
+ rcu_read_unlock();
dont_gc:
kleave(" = false");
return false;
do_gc:
+ rcu_read_unlock();
key_gc_cursor = keyring->serial;
key_get(keyring);
spin_unlock(&key_serial_lock);
diff --git a/security/keys/internal.h b/security/keys/internal.h
index 24ba0307b7ad..38783dcf6c61 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -87,7 +87,16 @@ extern wait_queue_head_t request_key_conswq;
extern struct key_type *key_type_lookup(const char *type);
extern void key_type_put(struct key_type *ktype);
-extern int __key_link(struct key *keyring, struct key *key);
+extern int __key_link_begin(struct key *keyring,
+ const struct key_type *type,
+ const char *description,
+ struct keyring_list **_prealloc);
+extern int __key_link_check_live_key(struct key *keyring, struct key *key);
+extern void __key_link(struct key *keyring, struct key *key,
+ struct keyring_list **_prealloc);
+extern void __key_link_end(struct key *keyring,
+ struct key_type *type,
+ struct keyring_list *prealloc);
extern key_ref_t __keyring_search_one(key_ref_t keyring_ref,
const struct key_type *type,
@@ -115,6 +124,7 @@ extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
extern int install_user_keyrings(void);
extern int install_thread_keyring_to_cred(struct cred *);
extern int install_process_keyring_to_cred(struct cred *);
+extern int install_session_keyring_to_cred(struct cred *, struct key *);
extern struct key *request_key_and_link(struct key_type *type,
const char *description,
diff --git a/security/keys/key.c b/security/keys/key.c
index e50d264c9ad1..c1eac8084ade 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -355,7 +355,7 @@ EXPORT_SYMBOL(key_alloc);
*/
int key_payload_reserve(struct key *key, size_t datalen)
{
- int delta = (int) datalen - key->datalen;
+ int delta = (int)datalen - key->datalen;
int ret = 0;
key_check(key);
@@ -398,7 +398,8 @@ static int __key_instantiate_and_link(struct key *key,
const void *data,
size_t datalen,
struct key *keyring,
- struct key *authkey)
+ struct key *authkey,
+ struct keyring_list **_prealloc)
{
int ret, awaken;
@@ -425,7 +426,7 @@ static int __key_instantiate_and_link(struct key *key,
/* and link it into the destination keyring */
if (keyring)
- ret = __key_link(keyring, key);
+ __key_link(keyring, key, _prealloc);
/* disable the authorisation key */
if (authkey)
@@ -453,15 +454,21 @@ int key_instantiate_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
+ struct keyring_list *prealloc;
int ret;
- if (keyring)
- down_write(&keyring->sem);
+ if (keyring) {
+ ret = __key_link_begin(keyring, key->type, key->description,
+ &prealloc);
+ if (ret < 0)
+ return ret;
+ }
- ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey);
+ ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey,
+ &prealloc);
if (keyring)
- up_write(&keyring->sem);
+ __key_link_end(keyring, key->type, prealloc);
return ret;
@@ -478,8 +485,9 @@ int key_negate_and_link(struct key *key,
struct key *keyring,
struct key *authkey)
{
+ struct keyring_list *prealloc;
struct timespec now;
- int ret, awaken;
+ int ret, awaken, link_ret = 0;
key_check(key);
key_check(keyring);
@@ -488,7 +496,8 @@ int key_negate_and_link(struct key *key,
ret = -EBUSY;
if (keyring)
- down_write(&keyring->sem);
+ link_ret = __key_link_begin(keyring, key->type,
+ key->description, &prealloc);
mutex_lock(&key_construction_mutex);
@@ -508,8 +517,8 @@ int key_negate_and_link(struct key *key,
ret = 0;
/* and link it into the destination keyring */
- if (keyring)
- ret = __key_link(keyring, key);
+ if (keyring && link_ret == 0)
+ __key_link(keyring, key, &prealloc);
/* disable the authorisation key */
if (authkey)
@@ -519,13 +528,13 @@ int key_negate_and_link(struct key *key,
mutex_unlock(&key_construction_mutex);
if (keyring)
- up_write(&keyring->sem);
+ __key_link_end(keyring, key->type, prealloc);
/* wake up anyone waiting for a key to be constructed */
if (awaken)
wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);
- return ret;
+ return ret == 0 ? link_ret : ret;
} /* end key_negate_and_link() */
@@ -749,6 +758,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_perm_t perm,
unsigned long flags)
{
+ struct keyring_list *prealloc;
const struct cred *cred = current_cred();
struct key_type *ktype;
struct key *keyring, *key = NULL;
@@ -775,7 +785,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
if (keyring->type != &key_type_keyring)
goto error_2;
- down_write(&keyring->sem);
+ ret = __key_link_begin(keyring, ktype, description, &prealloc);
+ if (ret < 0)
+ goto error_2;
/* if we're going to allocate a new key, we're going to have
* to modify the keyring */
@@ -817,7 +829,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
}
/* instantiate it and link it into the target keyring */
- ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL);
+ ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL,
+ &prealloc);
if (ret < 0) {
key_put(key);
key_ref = ERR_PTR(ret);
@@ -827,7 +840,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
key_ref = make_key_ref(key, is_key_possessed(keyring_ref));
error_3:
- up_write(&keyring->sem);
+ __key_link_end(keyring, ktype, prealloc);
error_2:
key_type_put(ktype);
error:
@@ -837,7 +850,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
/* we found a matching key, so we're going to try to update it
* - we can drop the locks first as we have the key pinned
*/
- up_write(&keyring->sem);
+ __key_link_end(keyring, ktype, prealloc);
key_type_put(ktype);
key_ref = __key_update(key_ref, payload, plen);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index e9c2e7c584d9..6261745e4459 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -33,7 +33,7 @@ static int key_get_type_from_user(char *type,
ret = strncpy_from_user(type, _type, len);
if (ret < 0)
- return -EFAULT;
+ return ret;
if (ret == 0 || ret >= len)
return -EINVAL;
@@ -212,15 +212,15 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
ret = key->serial;
key_put(key);
- error5:
+error5:
key_type_put(ktype);
- error4:
+error4:
key_ref_put(dest_ref);
- error3:
+error3:
kfree(callout_info);
- error2:
+error2:
kfree(description);
- error:
+error:
return ret;
} /* end sys_request_key() */
@@ -246,7 +246,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)
ret = key_ref_to_ptr(key_ref)->serial;
key_ref_put(key_ref);
- error:
+error:
return ret;
} /* end keyctl_get_keyring_ID() */
@@ -275,7 +275,7 @@ long keyctl_join_session_keyring(const char __user *_name)
ret = join_session_keyring(name);
kfree(name);
- error:
+error:
return ret;
} /* end keyctl_join_session_keyring() */
@@ -322,9 +322,9 @@ long keyctl_update_key(key_serial_t id,
ret = key_update(key_ref, payload, plen);
key_ref_put(key_ref);
- error2:
+error2:
kfree(payload);
- error:
+error:
return ret;
} /* end keyctl_update_key() */
@@ -356,7 +356,7 @@ long keyctl_revoke_key(key_serial_t id)
ret = 0;
key_ref_put(key_ref);
- error:
+error:
return ret;
} /* end keyctl_revoke_key() */
@@ -381,7 +381,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
ret = keyring_clear(key_ref_to_ptr(keyring_ref));
key_ref_put(keyring_ref);
- error:
+error:
return ret;
} /* end keyctl_keyring_clear() */
@@ -413,9 +413,9 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
key_ref_put(key_ref);
- error2:
+error2:
key_ref_put(keyring_ref);
- error:
+error:
return ret;
} /* end keyctl_keyring_link() */
@@ -447,9 +447,9 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
ret = key_unlink(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
key_ref_put(key_ref);
- error2:
+error2:
key_ref_put(keyring_ref);
- error:
+error:
return ret;
} /* end keyctl_keyring_unlink() */
@@ -529,9 +529,9 @@ okay:
}
kfree(tmpbuf);
- error2:
+error2:
key_ref_put(key_ref);
- error:
+error:
return ret;
} /* end keyctl_describe_key() */
@@ -616,17 +616,17 @@ long keyctl_keyring_search(key_serial_t ringid,
ret = key_ref_to_ptr(key_ref)->serial;
- error6:
+error6:
key_ref_put(key_ref);
- error5:
+error5:
key_type_put(ktype);
- error4:
+error4:
key_ref_put(dest_ref);
- error3:
+error3:
key_ref_put(keyring_ref);
- error2:
+error2:
kfree(description);
- error:
+error:
return ret;
} /* end keyctl_keyring_search() */
@@ -673,7 +673,7 @@ 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:
+can_read_key:
ret = key_validate(key);
if (ret == 0) {
ret = -EOPNOTSUPP;
@@ -686,9 +686,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
}
}
- error2:
+error2:
key_put(key);
- error:
+error:
return ret;
} /* end keyctl_read_key() */
@@ -1080,7 +1080,7 @@ set:
return old_setting;
error:
abort_creds(new);
- return -EINVAL;
+ return ret;
} /* end keyctl_set_reqkey_keyring() */
@@ -1269,7 +1269,7 @@ long keyctl_session_to_parent(void)
goto not_permitted;
/* the parent must be single threaded */
- if (atomic_read(&parent->signal->count) != 1)
+ if (!thread_group_empty(parent))
goto not_permitted;
/* the parent and the child must have different session keyrings or
@@ -1282,26 +1282,19 @@ long keyctl_session_to_parent(void)
/* the parent must have the same effective ownership and mustn't be
* SUID/SGID */
- if (pcred-> uid != mycred->euid ||
+ if (pcred->uid != mycred->euid ||
pcred->euid != mycred->euid ||
pcred->suid != mycred->euid ||
- pcred-> gid != mycred->egid ||
+ pcred->gid != mycred->egid ||
pcred->egid != mycred->egid ||
pcred->sgid != mycred->egid)
goto not_permitted;
/* the keyrings must have the same UID */
- if (pcred ->tgcred->session_keyring->uid != mycred->euid ||
+ if (pcred->tgcred->session_keyring->uid != mycred->euid ||
mycred->tgcred->session_keyring->uid != mycred->euid)
goto not_permitted;
- /* the LSM must permit the replacement of the parent's keyring with the
- * keyring from this process */
- ret = security_key_session_to_parent(mycred, pcred,
- key_ref_to_ptr(keyring_r));
- if (ret < 0)
- goto not_permitted;
-
/* if there's an already pending keyring replacement, then we replace
* that */
oldcred = parent->replacement_session_keyring;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 8ec02746ca99..d37f713e73ce 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -17,9 +17,14 @@
#include <linux/seq_file.h>
#include <linux/err.h>
#include <keys/keyring-type.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include "internal.h"
+#define rcu_dereference_locked_keyring(keyring) \
+ (rcu_dereference_protected( \
+ (keyring)->payload.subscriptions, \
+ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
+
/*
* when plumbing the depths of the key tree, this sets a hard limit set on how
* deep we're willing to go
@@ -39,7 +44,7 @@ static inline unsigned keyring_hash(const char *desc)
unsigned bucket = 0;
for (; *desc; desc++)
- bucket += (unsigned char) *desc;
+ bucket += (unsigned char)*desc;
return bucket & (KEYRING_NAME_HASH_SIZE - 1);
}
@@ -151,7 +156,9 @@ static void keyring_destroy(struct key *keyring)
write_unlock(&keyring_name_lock);
}
- klist = rcu_dereference(keyring->payload.subscriptions);
+ klist = rcu_dereference_check(keyring->payload.subscriptions,
+ rcu_read_lock_held() ||
+ atomic_read(&keyring->usage) == 0);
if (klist) {
for (loop = klist->nkeys - 1; loop >= 0; loop--)
key_put(klist->keys[loop]);
@@ -168,12 +175,10 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
{
struct keyring_list *klist;
- if (keyring->description) {
+ if (keyring->description)
seq_puts(m, keyring->description);
- }
- else {
+ else
seq_puts(m, "[anon]");
- }
rcu_read_lock();
klist = rcu_dereference(keyring->payload.subscriptions);
@@ -199,8 +204,7 @@ static long keyring_read(const struct key *keyring,
int loop, ret;
ret = 0;
- klist = rcu_dereference(keyring->payload.subscriptions);
-
+ klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* calculate how much data we could return */
qty = klist->nkeys * sizeof(key_serial_t);
@@ -235,7 +239,7 @@ static long keyring_read(const struct key *keyring,
ret = qty;
}
- error:
+error:
return ret;
} /* end keyring_read() */
@@ -304,7 +308,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
key_check(keyring);
/* top keyring must have search permission to begin the search */
- err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
+ err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
if (err < 0) {
key_ref = ERR_PTR(err);
goto error;
@@ -506,7 +510,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
rcu_read_unlock();
return ERR_PTR(-ENOKEY);
- found:
+found:
atomic_inc(&key->usage);
rcu_read_unlock();
return make_key_ref(key, possessed);
@@ -524,9 +528,8 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
struct key *keyring;
int bucket;
- keyring = ERR_PTR(-EINVAL);
if (!name)
- goto error;
+ return ERR_PTR(-EINVAL);
bucket = keyring_hash(name);
@@ -553,17 +556,18 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
KEY_SEARCH) < 0)
continue;
- /* we've got a match */
- atomic_inc(&keyring->usage);
- read_unlock(&keyring_name_lock);
- goto error;
+ /* we've got a match but we might end up racing with
+ * key_cleanup() if the keyring is currently 'dead'
+ * (ie. it has a zero usage count) */
+ if (!atomic_inc_not_zero(&keyring->usage))
+ continue;
+ goto out;
}
}
- read_unlock(&keyring_name_lock);
keyring = ERR_PTR(-ENOKEY);
-
- error:
+out:
+ read_unlock(&keyring_name_lock);
return keyring;
} /* end find_keyring_by_name() */
@@ -596,7 +600,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
sp = 0;
/* start processing a new keyring */
- descend:
+descend:
if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
goto not_this_keyring;
@@ -605,7 +609,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
goto not_this_keyring;
kix = 0;
- ascend:
+ascend:
/* iterate through the remaining keys in this keyring */
for (; kix < keylist->nkeys; kix++) {
key = keylist->keys[kix];
@@ -631,7 +635,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
/* the keyring we're looking at was disqualified or didn't contain a
* matching key */
- not_this_keyring:
+not_this_keyring:
if (sp > 0) {
/* resume the checking of a keyring higher up in the tree */
sp--;
@@ -642,34 +646,20 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
ret = 0; /* no cycles detected */
- error:
+error:
rcu_read_unlock();
return ret;
- too_deep:
+too_deep:
ret = -ELOOP;
goto error;
- cycle_detected:
+cycle_detected:
ret = -EDEADLK;
goto error;
} /* end keyring_detect_cycle() */
-/*****************************************************************************/
-/*
- * dispose of a keyring list after the RCU grace period
- */
-static void keyring_link_rcu_disposal(struct rcu_head *rcu)
-{
- struct keyring_list *klist =
- container_of(rcu, struct keyring_list, rcu);
-
- kfree(klist);
-
-} /* end keyring_link_rcu_disposal() */
-
-/*****************************************************************************/
/*
* dispose of a keyring list after the RCU grace period, freeing the unlinked
* key
@@ -679,56 +669,51 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
struct keyring_list *klist =
container_of(rcu, struct keyring_list, rcu);
- key_put(klist->keys[klist->delkey]);
+ if (klist->delkey != USHRT_MAX)
+ key_put(klist->keys[klist->delkey]);
kfree(klist);
+}
-} /* end keyring_unlink_rcu_disposal() */
-
-/*****************************************************************************/
/*
- * link a key into to a keyring
- * - must be called with the keyring's semaphore write-locked
- * - discard already extant link to matching key if there is one
+ * preallocate memory so that a key can be linked into to a keyring
*/
-int __key_link(struct key *keyring, struct key *key)
+int __key_link_begin(struct key *keyring, const struct key_type *type,
+ const char *description,
+ struct keyring_list **_prealloc)
+ __acquires(&keyring->sem)
{
struct keyring_list *klist, *nklist;
unsigned max;
size_t size;
int loop, ret;
- ret = -EKEYREVOKED;
- if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
- goto error;
+ kenter("%d,%s,%s,", key_serial(keyring), type->name, description);
- ret = -ENOTDIR;
if (keyring->type != &key_type_keyring)
- goto error;
+ return -ENOTDIR;
- /* serialise link/link calls to prevent parallel calls causing a
- * cycle when applied to two keyring in opposite orders */
- down_write(&keyring_serialise_link_sem);
+ down_write(&keyring->sem);
- /* check that we aren't going to create a cycle adding one keyring to
- * another */
- if (key->type == &key_type_keyring) {
- ret = keyring_detect_cycle(keyring, key);
- if (ret < 0)
- goto error2;
- }
+ ret = -EKEYREVOKED;
+ if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
+ goto error_krsem;
- /* see if there's a matching key we can displace */
- klist = keyring->payload.subscriptions;
+ /* serialise link/link calls to prevent parallel calls causing a cycle
+ * when linking two keyring in opposite orders */
+ if (type == &key_type_keyring)
+ down_write(&keyring_serialise_link_sem);
- if (klist && klist->nkeys > 0) {
- struct key_type *type = key->type;
+ klist = rcu_dereference_locked_keyring(keyring);
+ /* see if there's a matching key we can displace */
+ if (klist && klist->nkeys > 0) {
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
if (klist->keys[loop]->type == type &&
strcmp(klist->keys[loop]->description,
- key->description) == 0
+ description) == 0
) {
- /* found a match - replace with new key */
+ /* found a match - we'll replace this one with
+ * the new key */
size = sizeof(struct key *) * klist->maxkeys;
size += sizeof(*klist);
BUG_ON(size > PAGE_SIZE);
@@ -736,22 +721,10 @@ int __key_link(struct key *keyring, struct key *key)
ret = -ENOMEM;
nklist = kmemdup(klist, size, GFP_KERNEL);
if (!nklist)
- goto error2;
-
- /* replace matched key */
- atomic_inc(&key->usage);
- nklist->keys[loop] = key;
-
- rcu_assign_pointer(
- keyring->payload.subscriptions,
- nklist);
-
- /* dispose of the old keyring list and the
- * displaced key */
- klist->delkey = loop;
- call_rcu(&klist->rcu,
- keyring_unlink_rcu_disposal);
+ goto error_sem;
+ /* note replacement slot */
+ klist->delkey = nklist->delkey = loop;
goto done;
}
}
@@ -761,90 +734,167 @@ int __key_link(struct key *keyring, struct key *key)
ret = key_payload_reserve(keyring,
keyring->datalen + KEYQUOTA_LINK_BYTES);
if (ret < 0)
- goto error2;
-
- klist = keyring->payload.subscriptions;
+ goto error_sem;
if (klist && klist->nkeys < klist->maxkeys) {
- /* there's sufficient slack space to add directly */
- atomic_inc(&key->usage);
-
- klist->keys[klist->nkeys] = key;
- smp_wmb();
- klist->nkeys++;
- smp_wmb();
- }
- else {
+ /* there's sufficient slack space to append directly */
+ nklist = NULL;
+ } else {
/* grow the key list */
max = 4;
if (klist)
max += klist->maxkeys;
ret = -ENFILE;
- if (max > 65535)
- goto error3;
+ if (max > USHRT_MAX - 1)
+ goto error_quota;
size = sizeof(*klist) + sizeof(struct key *) * max;
if (size > PAGE_SIZE)
- goto error3;
+ goto error_quota;
ret = -ENOMEM;
nklist = kmalloc(size, GFP_KERNEL);
if (!nklist)
- goto error3;
- nklist->maxkeys = max;
- nklist->nkeys = 0;
+ goto error_quota;
+ nklist->maxkeys = max;
if (klist) {
- nklist->nkeys = klist->nkeys;
- memcpy(nklist->keys,
- klist->keys,
+ memcpy(nklist->keys, klist->keys,
sizeof(struct key *) * klist->nkeys);
+ nklist->delkey = klist->nkeys;
+ nklist->nkeys = klist->nkeys + 1;
+ klist->delkey = USHRT_MAX;
+ } else {
+ nklist->nkeys = 1;
+ nklist->delkey = 0;
}
/* add the key into the new space */
- atomic_inc(&key->usage);
- nklist->keys[nklist->nkeys++] = key;
-
- rcu_assign_pointer(keyring->payload.subscriptions, nklist);
-
- /* dispose of the old keyring list */
- if (klist)
- call_rcu(&klist->rcu, keyring_link_rcu_disposal);
+ nklist->keys[nklist->delkey] = NULL;
}
done:
- ret = 0;
-error2:
- up_write(&keyring_serialise_link_sem);
-error:
- return ret;
+ *_prealloc = nklist;
+ kleave(" = 0");
+ return 0;
-error3:
+error_quota:
/* undo the quota changes */
key_payload_reserve(keyring,
keyring->datalen - KEYQUOTA_LINK_BYTES);
- goto error2;
+error_sem:
+ if (type == &key_type_keyring)
+ up_write(&keyring_serialise_link_sem);
+error_krsem:
+ up_write(&keyring->sem);
+ kleave(" = %d", ret);
+ return ret;
+}
-} /* end __key_link() */
+/*
+ * check already instantiated keys aren't going to be a problem
+ * - the caller must have called __key_link_begin()
+ * - don't need to call this for keys that were created since __key_link_begin()
+ * was called
+ */
+int __key_link_check_live_key(struct key *keyring, struct key *key)
+{
+ if (key->type == &key_type_keyring)
+ /* check that we aren't going to create a cycle by linking one
+ * keyring to another */
+ return keyring_detect_cycle(keyring, key);
+ return 0;
+}
+
+/*
+ * link a key into to a keyring
+ * - must be called with __key_link_begin() having being called
+ * - discard already extant link to matching key if there is one
+ */
+void __key_link(struct key *keyring, struct key *key,
+ struct keyring_list **_prealloc)
+{
+ struct keyring_list *klist, *nklist;
+
+ nklist = *_prealloc;
+ *_prealloc = NULL;
+
+ kenter("%d,%d,%p", keyring->serial, key->serial, nklist);
+
+ klist = rcu_dereference_protected(keyring->payload.subscriptions,
+ rwsem_is_locked(&keyring->sem));
+
+ atomic_inc(&key->usage);
+
+ /* there's a matching key we can displace or an empty slot in a newly
+ * allocated list we can fill */
+ if (nklist) {
+ kdebug("replace %hu/%hu/%hu",
+ nklist->delkey, nklist->nkeys, nklist->maxkeys);
+
+ nklist->keys[nklist->delkey] = key;
+
+ rcu_assign_pointer(keyring->payload.subscriptions, nklist);
+
+ /* dispose of the old keyring list and, if there was one, the
+ * displaced key */
+ if (klist) {
+ kdebug("dispose %hu/%hu/%hu",
+ klist->delkey, klist->nkeys, klist->maxkeys);
+ call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
+ }
+ } else {
+ /* there's sufficient slack space to append directly */
+ klist->keys[klist->nkeys] = key;
+ smp_wmb();
+ klist->nkeys++;
+ }
+}
+
+/*
+ * finish linking a key into to a keyring
+ * - must be called with __key_link_begin() having being called
+ */
+void __key_link_end(struct key *keyring, struct key_type *type,
+ struct keyring_list *prealloc)
+ __releases(&keyring->sem)
+{
+ BUG_ON(type == NULL);
+ BUG_ON(type->name == NULL);
+ kenter("%d,%s,%p", keyring->serial, type->name, prealloc);
+
+ if (type == &key_type_keyring)
+ up_write(&keyring_serialise_link_sem);
+
+ if (prealloc) {
+ kfree(prealloc);
+ key_payload_reserve(keyring,
+ keyring->datalen - KEYQUOTA_LINK_BYTES);
+ }
+ up_write(&keyring->sem);
+}
-/*****************************************************************************/
/*
* link a key to a keyring
*/
int key_link(struct key *keyring, struct key *key)
{
+ struct keyring_list *prealloc;
int ret;
key_check(keyring);
key_check(key);
- down_write(&keyring->sem);
- ret = __key_link(keyring, key);
- up_write(&keyring->sem);
+ ret = __key_link_begin(keyring, key->type, key->description, &prealloc);
+ if (ret == 0) {
+ ret = __key_link_check_live_key(keyring, key);
+ if (ret == 0)
+ __key_link(keyring, key, &prealloc);
+ __key_link_end(keyring, key->type, prealloc);
+ }
return ret;
-
-} /* end key_link() */
+}
EXPORT_SYMBOL(key_link);
@@ -866,7 +916,7 @@ int key_unlink(struct key *keyring, struct key *key)
down_write(&keyring->sem);
- klist = keyring->payload.subscriptions;
+ klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* search the keyring for the key */
for (loop = 0; loop < klist->nkeys; loop++)
@@ -957,7 +1007,7 @@ int keyring_clear(struct key *keyring)
/* detach the pointer block with the locks held */
down_write(&keyring->sem);
- klist = keyring->payload.subscriptions;
+ klist = rcu_dereference_locked_keyring(keyring);
if (klist) {
/* adjust the quota */
key_payload_reserve(keyring,
@@ -989,7 +1039,9 @@ EXPORT_SYMBOL(keyring_clear);
*/
static void keyring_revoke(struct key *keyring)
{
- struct keyring_list *klist = keyring->payload.subscriptions;
+ struct keyring_list *klist;
+
+ klist = rcu_dereference_locked_keyring(keyring);
/* adjust the quota */
key_payload_reserve(keyring, 0);
@@ -1023,7 +1075,7 @@ void keyring_gc(struct key *keyring, time_t limit)
down_write(&keyring->sem);
- klist = keyring->payload.subscriptions;
+ klist = rcu_dereference_locked_keyring(keyring);
if (!klist)
goto no_klist;
diff --git a/security/keys/permission.c b/security/keys/permission.c
index 0ed802c9e698..28645502cd0d 100644
--- a/security/keys/permission.c
+++ b/security/keys/permission.c
@@ -109,7 +109,7 @@ int key_validate(struct key *key)
}
}
- error:
+error:
return ret;
} /* end key_validate() */
diff --git a/security/keys/proc.c b/security/keys/proc.c
index 9d01021ca0c8..068b66ea2f1b 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
-#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -307,7 +306,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
{
(*_pos)++;
- return key_user_next((struct rb_node *) v);
+ return key_user_next((struct rb_node *)v);
}
static void proc_key_users_stop(struct seq_file *p, void *v)
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 5c23afb31ece..6b8e4ff4cc68 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -12,7 +12,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
-#include <linux/slab.h>
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
@@ -217,8 +216,7 @@ static int install_process_keyring(void)
/*
* install a session keyring directly to a credentials struct
*/
-static int install_session_keyring_to_cred(struct cred *cred,
- struct key *keyring)
+int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
{
unsigned long flags;
struct key *old;
@@ -509,7 +507,7 @@ try_again:
ret = install_thread_keyring();
if (ret < 0) {
- key = ERR_PTR(ret);
+ key_ref = ERR_PTR(ret);
goto error;
}
goto reget_creds;
@@ -527,7 +525,7 @@ try_again:
ret = install_process_keyring();
if (ret < 0) {
- key = ERR_PTR(ret);
+ key_ref = ERR_PTR(ret);
goto error;
}
goto reget_creds;
@@ -586,7 +584,7 @@ try_again:
case KEY_SPEC_GROUP_KEYRING:
/* group keyrings are not yet supported */
- key = ERR_PTR(-EINVAL);
+ key_ref = ERR_PTR(-EINVAL);
goto error;
case KEY_SPEC_REQKEY_AUTH_KEY:
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 03fe63ed55bd..f5ec9ac5d57c 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -58,6 +58,38 @@ void complete_request_key(struct key_construction *cons, int error)
}
EXPORT_SYMBOL(complete_request_key);
+static int umh_keys_init(struct subprocess_info *info)
+{
+ struct cred *cred = (struct cred*)current_cred();
+ struct key *keyring = info->data;
+ /*
+ * This is called in context of freshly forked kthread before
+ * kernel_execve(), we can just change our ->session_keyring.
+ */
+ return install_session_keyring_to_cred(cred, keyring);
+}
+
+static void umh_keys_cleanup(struct subprocess_info *info)
+{
+ struct key *keyring = info->data;
+ key_put(keyring);
+}
+
+static int call_usermodehelper_keys(char *path, char **argv, char **envp,
+ struct key *session_keyring, enum umh_wait wait)
+{
+ gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+ struct subprocess_info *info =
+ call_usermodehelper_setup(path, argv, envp, gfp_mask);
+
+ if (!info)
+ return -ENOMEM;
+
+ call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup,
+ key_get(session_keyring));
+ return call_usermodehelper_exec(info, wait);
+}
+
/*
* request userspace finish the construction of a key
* - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"
@@ -68,7 +100,8 @@ static int call_sbin_request_key(struct key_construction *cons,
{
const struct cred *cred = current_cred();
key_serial_t prkey, sskey;
- struct key *key = cons->key, *authkey = cons->authkey, *keyring;
+ struct key *key = cons->key, *authkey = cons->authkey, *keyring,
+ *session;
char *argv[9], *envp[3], uid_str[12], gid_str[12];
char key_str[12], keyring_str[3][12];
char desc[20];
@@ -93,7 +126,7 @@ static int call_sbin_request_key(struct key_construction *cons,
}
/* attach the auth key to the session keyring */
- ret = __key_link(keyring, authkey);
+ ret = key_link(keyring, authkey);
if (ret < 0)
goto error_link;
@@ -112,10 +145,12 @@ static int call_sbin_request_key(struct key_construction *cons,
if (cred->tgcred->process_keyring)
prkey = cred->tgcred->process_keyring->serial;
- if (cred->tgcred->session_keyring)
- sskey = rcu_dereference(cred->tgcred->session_keyring)->serial;
- else
- sskey = cred->user->session_keyring->serial;
+ rcu_read_lock();
+ session = rcu_dereference(cred->tgcred->session_keyring);
+ if (!session)
+ session = cred->user->session_keyring;
+ sskey = session->serial;
+ rcu_read_unlock();
sprintf(keyring_str[2], "%d", sskey);
@@ -296,12 +331,15 @@ static int construct_alloc_key(struct key_type *type,
struct key_user *user,
struct key **_key)
{
+ struct keyring_list *prealloc;
const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
+ int ret;
kenter("%s,%s,,,", type->name, description);
+ *_key = NULL;
mutex_lock(&user->cons_lock);
key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
@@ -311,8 +349,12 @@ static int construct_alloc_key(struct key_type *type,
set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);
- if (dest_keyring)
- down_write(&dest_keyring->sem);
+ if (dest_keyring) {
+ ret = __key_link_begin(dest_keyring, type, description,
+ &prealloc);
+ if (ret < 0)
+ goto link_prealloc_failed;
+ }
/* attach the key to the destination keyring under lock, but we do need
* to do another check just in case someone beat us to it whilst we
@@ -324,29 +366,49 @@ static int construct_alloc_key(struct key_type *type,
goto key_already_present;
if (dest_keyring)
- __key_link(dest_keyring, key);
+ __key_link(dest_keyring, key, &prealloc);
mutex_unlock(&key_construction_mutex);
if (dest_keyring)
- up_write(&dest_keyring->sem);
+ __key_link_end(dest_keyring, type, prealloc);
mutex_unlock(&user->cons_lock);
*_key = key;
kleave(" = 0 [%d]", key_serial(key));
return 0;
+ /* the key is now present - we tell the caller that we found it by
+ * returning -EINPROGRESS */
key_already_present:
+ key_put(key);
mutex_unlock(&key_construction_mutex);
- if (dest_keyring)
- up_write(&dest_keyring->sem);
+ key = key_ref_to_ptr(key_ref);
+ if (dest_keyring) {
+ ret = __key_link_check_live_key(dest_keyring, key);
+ if (ret == 0)
+ __key_link(dest_keyring, key, &prealloc);
+ __key_link_end(dest_keyring, type, prealloc);
+ if (ret < 0)
+ goto link_check_failed;
+ }
mutex_unlock(&user->cons_lock);
- key_put(key);
- *_key = key = key_ref_to_ptr(key_ref);
+ *_key = key;
kleave(" = -EINPROGRESS [%d]", key_serial(key));
return -EINPROGRESS;
+link_check_failed:
+ mutex_unlock(&user->cons_lock);
+ key_put(key);
+ kleave(" = %d [linkcheck]", ret);
+ return ret;
+
+link_prealloc_failed:
+ up_write(&dest_keyring->sem);
+ mutex_unlock(&user->cons_lock);
+ kleave(" = %d [prelink]", ret);
+ return ret;
+
alloc_failed:
mutex_unlock(&user->cons_lock);
- *_key = NULL;
kleave(" = %ld", PTR_ERR(key));
return PTR_ERR(key);
}
@@ -385,6 +447,10 @@ static struct key *construct_key_and_link(struct key_type *type,
kdebug("cons failed");
goto construction_failed;
}
+ } else if (ret == -EINPROGRESS) {
+ ret = 0;
+ } else {
+ key = ERR_PTR(ret);
}
key_put(dest_keyring);
@@ -417,6 +483,7 @@ struct key *request_key_and_link(struct key_type *type,
const struct cred *cred = current_cred();
struct key *key;
key_ref_t key_ref;
+ int ret;
kenter("%s,%s,%p,%zu,%p,%p,%lx",
type->name, description, callout_info, callout_len, aux,
@@ -428,6 +495,16 @@ struct key *request_key_and_link(struct key_type *type,
if (!IS_ERR(key_ref)) {
key = key_ref_to_ptr(key_ref);
+ if (dest_keyring) {
+ construct_get_dest_keyring(&dest_keyring);
+ ret = key_link(dest_keyring, key);
+ key_put(dest_keyring);
+ if (ret < 0) {
+ key_put(key);
+ key = ERR_PTR(ret);
+ goto error;
+ }
+ }
} else if (PTR_ERR(key_ref) != -EAGAIN) {
key = ERR_CAST(key_ref);
} else {
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 7c687d568221..e9aa07929656 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -199,7 +199,8 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
struct user_key_payload *upayload;
long ret;
- upayload = rcu_dereference(key->payload.data);
+ upayload = rcu_dereference_protected(
+ key->payload.data, rwsem_is_locked(&((struct key *)key)->sem));
ret = upayload->datalen;
/* we can return the data as is */
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index acba3dfc8d29..908aa712816a 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
+#include <linux/gfp.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <net/sock.h>
@@ -220,7 +221,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
}
switch (a->type) {
- case LSM_AUDIT_NO_AUDIT:
+ case LSM_AUDIT_DATA_NONE:
return;
case LSM_AUDIT_DATA_IPC:
audit_log_format(ab, " key=%d ", a->u.ipc_id);
diff --git a/security/min_addr.c b/security/min_addr.c
index e86f297522bf..f728728f193b 100644
--- a/security/min_addr.c
+++ b/security/min_addr.c
@@ -33,7 +33,7 @@ int mmap_min_addr_handler(struct ctl_table *table, int write,
{
int ret;
- if (!capable(CAP_SYS_RAWIO))
+ if (write && !capable(CAP_SYS_RAWIO))
return -EPERM;
ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
diff --git a/security/security.c b/security/security.c
index 24e060be9fa5..351942a4ca0e 100644
--- a/security/security.c
+++ b/security/security.c
@@ -23,12 +23,14 @@ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
CONFIG_DEFAULT_SECURITY;
/* things that live in capability.c */
-extern struct security_operations default_security_ops;
-extern void security_fixup_ops(struct security_operations *ops);
+extern void __init security_fixup_ops(struct security_operations *ops);
-struct security_operations *security_ops; /* Initialized to NULL */
+static struct security_operations *security_ops;
+static struct security_operations default_security_ops = {
+ .name = "default",
+};
-static inline int verify(struct security_operations *ops)
+static inline int __init verify(struct security_operations *ops)
{
/* verify the security_operations structure exists */
if (!ops)
@@ -63,6 +65,11 @@ int __init security_init(void)
return 0;
}
+void reset_security_ops(void)
+{
+ security_ops = &default_security_ops;
+}
+
/* Save user chosen LSM */
static int __init choose_lsm(char *str)
{
@@ -110,7 +117,7 @@ int __init security_module_enable(struct security_operations *ops)
* If there is already a security module registered with the kernel,
* an error will be returned. Otherwise %0 is returned on success.
*/
-int register_security(struct security_operations *ops)
+int __init register_security(struct security_operations *ops)
{
if (verify(ops)) {
printk(KERN_DEBUG "%s could not verify "
@@ -183,11 +190,6 @@ int security_real_capable_noaudit(struct task_struct *tsk, int cap)
return ret;
}
-int security_acct(struct file *file)
-{
- return security_ops->acct(file);
-}
-
int security_sysctl(struct ctl_table *table, int op)
{
return security_ops->sysctl(table, op);
@@ -203,9 +205,9 @@ int security_quota_on(struct dentry *dentry)
return security_ops->quota_on(dentry);
}
-int security_syslog(int type)
+int security_syslog(int type, bool from_file)
{
- return security_ops->syslog(type);
+ return security_ops->syslog(type, from_file);
}
int security_settime(struct timespec *ts, struct timezone *tz)
@@ -299,46 +301,16 @@ int security_sb_mount(char *dev_name, struct path *path,
return security_ops->sb_mount(dev_name, path, type, flags, data);
}
-int security_sb_check_sb(struct vfsmount *mnt, struct path *path)
-{
- return security_ops->sb_check_sb(mnt, path);
-}
-
int security_sb_umount(struct vfsmount *mnt, int flags)
{
return security_ops->sb_umount(mnt, flags);
}
-void security_sb_umount_close(struct vfsmount *mnt)
-{
- security_ops->sb_umount_close(mnt);
-}
-
-void security_sb_umount_busy(struct vfsmount *mnt)
-{
- security_ops->sb_umount_busy(mnt);
-}
-
-void security_sb_post_remount(struct vfsmount *mnt, unsigned long flags, void *data)
-{
- security_ops->sb_post_remount(mnt, flags, data);
-}
-
-void security_sb_post_addmount(struct vfsmount *mnt, struct path *mountpoint)
-{
- security_ops->sb_post_addmount(mnt, mountpoint);
-}
-
int security_sb_pivotroot(struct path *old_path, struct path *new_path)
{
return security_ops->sb_pivotroot(old_path, new_path);
}
-void security_sb_post_pivotroot(struct path *old_path, struct path *new_path)
-{
- security_ops->sb_post_pivotroot(old_path, new_path);
-}
-
int security_sb_set_mnt_opts(struct super_block *sb,
struct security_mnt_opts *opts)
{
@@ -389,42 +361,42 @@ int security_inode_init_security(struct inode *inode, struct inode *dir,
EXPORT_SYMBOL(security_inode_init_security);
#ifdef CONFIG_SECURITY_PATH
-int security_path_mknod(struct path *path, struct dentry *dentry, int mode,
+int security_path_mknod(struct path *dir, struct dentry *dentry, int mode,
unsigned int dev)
{
- if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
return 0;
- return security_ops->path_mknod(path, dentry, mode, dev);
+ return security_ops->path_mknod(dir, dentry, mode, dev);
}
EXPORT_SYMBOL(security_path_mknod);
-int security_path_mkdir(struct path *path, struct dentry *dentry, int mode)
+int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode)
{
- if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
return 0;
- return security_ops->path_mkdir(path, dentry, mode);
+ return security_ops->path_mkdir(dir, dentry, mode);
}
-int security_path_rmdir(struct path *path, struct dentry *dentry)
+int security_path_rmdir(struct path *dir, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
return 0;
- return security_ops->path_rmdir(path, dentry);
+ return security_ops->path_rmdir(dir, dentry);
}
-int security_path_unlink(struct path *path, struct dentry *dentry)
+int security_path_unlink(struct path *dir, struct dentry *dentry)
{
- if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
return 0;
- return security_ops->path_unlink(path, dentry);
+ return security_ops->path_unlink(dir, dentry);
}
-int security_path_symlink(struct path *path, struct dentry *dentry,
+int security_path_symlink(struct path *dir, struct dentry *dentry,
const char *old_name)
{
- if (unlikely(IS_PRIVATE(path->dentry->d_inode)))
+ if (unlikely(IS_PRIVATE(dir->dentry->d_inode)))
return 0;
- return security_ops->path_symlink(path, dentry, old_name);
+ return security_ops->path_symlink(dir, dentry, old_name);
}
int security_path_link(struct dentry *old_dentry, struct path *new_dir,
@@ -573,13 +545,6 @@ int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
return security_ops->inode_getattr(mnt, dentry);
}
-void security_inode_delete(struct inode *inode)
-{
- if (unlikely(IS_PRIVATE(inode)))
- return;
- security_ops->inode_delete(inode);
-}
-
int security_inode_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
@@ -630,14 +595,14 @@ int security_inode_killpriv(struct dentry *dentry)
int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
{
if (unlikely(IS_PRIVATE(inode)))
- return 0;
+ return -EOPNOTSUPP;
return security_ops->inode_getsecurity(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 0;
+ return -EOPNOTSUPP;
return security_ops->inode_setsecurity(inode, name, value, size, flags);
}
@@ -666,8 +631,6 @@ int security_file_alloc(struct file *file)
void security_file_free(struct file *file)
{
security_ops->file_free_security(file);
- if (file->f_dentry)
- ima_file_free(file);
}
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -744,11 +707,6 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
return security_ops->cred_prepare(new, old, gfp);
}
-void security_commit_creds(struct cred *new, const struct cred *old)
-{
- security_ops->cred_commit(new, old);
-}
-
void security_transfer_creds(struct cred *new, const struct cred *old)
{
security_ops->cred_transfer(new, old);
@@ -769,22 +727,12 @@ int security_kernel_module_request(char *kmod_name)
return security_ops->kernel_module_request(kmod_name);
}
-int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
- return security_ops->task_setuid(id0, id1, id2, flags);
-}
-
int security_task_fix_setuid(struct cred *new, const struct cred *old,
int flags)
{
return security_ops->task_fix_setuid(new, old, flags);
}
-int security_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
-{
- return security_ops->task_setgid(id0, id1, id2, flags);
-}
-
int security_task_setpgid(struct task_struct *p, pid_t pgid)
{
return security_ops->task_setpgid(p, pgid);
@@ -806,11 +754,6 @@ void security_task_getsecid(struct task_struct *p, u32 *secid)
}
EXPORT_SYMBOL(security_task_getsecid);
-int security_task_setgroups(struct group_info *group_info)
-{
- return security_ops->task_setgroups(group_info);
-}
-
int security_task_setnice(struct task_struct *p, int nice)
{
return security_ops->task_setnice(p, nice);
@@ -1314,13 +1257,6 @@ int security_key_getsecurity(struct key *key, char **_buffer)
return security_ops->key_getsecurity(key, _buffer);
}
-int security_key_session_to_parent(const struct cred *cred,
- const struct cred *parent_cred,
- struct key *key)
-{
- return security_ops->key_session_to_parent(cred, parent_cred, key);
-}
-
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index f2dde268165a..7f1a304712a9 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -337,7 +337,7 @@ static inline struct avc_node *avc_search_node(u32 ssid, u32 tsid, u16 tclass)
* Look up an AVC entry that is valid for the
* (@ssid, @tsid), interpreting the permissions
* based on @tclass. If a valid AVC entry exists,
- * then this function return the avc_node.
+ * then this function returns the avc_node.
* Otherwise, this function returns NULL.
*/
static struct avc_node *avc_lookup(u32 ssid, u32 tsid, u16 tclass)
@@ -489,21 +489,17 @@ void avc_audit(u32 ssid, u32 tsid,
struct common_audit_data stack_data;
u32 denied, audited;
denied = requested & ~avd->allowed;
- if (denied) {
- audited = denied;
- if (!(audited & avd->auditdeny))
- return;
- } else if (result) {
+ if (denied)
+ audited = denied & avd->auditdeny;
+ else if (result)
audited = denied = requested;
- } else {
- audited = requested;
- if (!(audited & avd->auditallow))
- return;
- }
+ else
+ audited = requested & avd->auditallow;
+ if (!audited)
+ return;
if (!a) {
a = &stack_data;
- memset(a, 0, sizeof(*a));
- a->type = LSM_AUDIT_NO_AUDIT;
+ COMMON_AUDIT_DATA_INIT(a, NONE);
}
a->selinux_audit_data.tclass = tclass;
a->selinux_audit_data.requested = requested;
@@ -526,7 +522,7 @@ void avc_audit(u32 ssid, u32 tsid,
* @perms: permissions
*
* Register a callback function for events in the set @events
- * related to the SID pair (@ssid, @tsid) and
+ * related to the SID pair (@ssid, @tsid)
* and the permissions @perms, interpreting
* @perms based on @tclass. Returns %0 on success or
* -%ENOMEM if insufficient memory exists to add the callback.
@@ -571,7 +567,7 @@ static inline int avc_sidcmp(u32 x, u32 y)
*
* 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 update the AVC entry. The original AVC-entry object
+ * 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,
@@ -746,9 +742,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
else
avd = &avd_entry;
- rc = security_compute_av(ssid, tsid, tclass, requested, avd);
- if (rc)
- goto out;
+ security_compute_av(ssid, tsid, tclass, avd);
rcu_read_lock();
node = avc_insert(ssid, tsid, tclass, avd);
} else {
@@ -770,7 +764,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
}
rcu_read_unlock();
-out:
return rc;
}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 9a2ee845e9d4..5c9f25ba1c95 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -76,6 +76,7 @@
#include <linux/selinux.h>
#include <linux/mutex.h>
#include <linux/posix-timers.h>
+#include <linux/syslog.h>
#include "avc.h"
#include "objsec.h"
@@ -125,18 +126,6 @@ __setup("selinux=", selinux_enabled_setup);
int selinux_enabled = 1;
#endif
-
-/*
- * Minimal support for a secondary security module,
- * just to allow the use of the capability module.
- */
-static struct security_operations *secondary_ops;
-
-/* Lists of inode and superblock security structures initialized
- before the policy was loaded. */
-static LIST_HEAD(superblock_security_head);
-static DEFINE_SPINLOCK(sb_security_lock);
-
static struct kmem_cache *sel_inode_cache;
/**
@@ -272,7 +261,6 @@ static int superblock_alloc_security(struct super_block *sb)
return -ENOMEM;
mutex_init(&sbsec->lock);
- INIT_LIST_HEAD(&sbsec->list);
INIT_LIST_HEAD(&sbsec->isec_head);
spin_lock_init(&sbsec->isec_lock);
sbsec->sb = sb;
@@ -287,40 +275,34 @@ static int superblock_alloc_security(struct super_block *sb)
static void superblock_free_security(struct super_block *sb)
{
struct superblock_security_struct *sbsec = sb->s_security;
-
- spin_lock(&sb_security_lock);
- if (!list_empty(&sbsec->list))
- list_del_init(&sbsec->list);
- spin_unlock(&sb_security_lock);
-
sb->s_security = NULL;
kfree(sbsec);
}
static int sk_alloc_security(struct sock *sk, int family, gfp_t priority)
{
- struct sk_security_struct *ssec;
+ struct sk_security_struct *sksec;
- ssec = kzalloc(sizeof(*ssec), priority);
- if (!ssec)
+ sksec = kzalloc(sizeof(*sksec), priority);
+ if (!sksec)
return -ENOMEM;
- ssec->peer_sid = SECINITSID_UNLABELED;
- ssec->sid = SECINITSID_UNLABELED;
- sk->sk_security = ssec;
+ sksec->peer_sid = SECINITSID_UNLABELED;
+ sksec->sid = SECINITSID_UNLABELED;
+ sk->sk_security = sksec;
- selinux_netlbl_sk_security_reset(ssec);
+ selinux_netlbl_sk_security_reset(sksec);
return 0;
}
static void sk_free_security(struct sock *sk)
{
- struct sk_security_struct *ssec = sk->sk_security;
+ struct sk_security_struct *sksec = sk->sk_security;
sk->sk_security = NULL;
- selinux_netlbl_sk_security_free(ssec);
- kfree(ssec);
+ selinux_netlbl_sk_security_free(sksec);
+ kfree(sksec);
}
/* The security server must be initialized before
@@ -329,7 +311,7 @@ extern int ss_initialized;
/* The file system's label must be initialized prior to use. */
-static char *labeling_behaviors[6] = {
+static const char *labeling_behaviors[6] = {
"uses xattr",
"uses transition SIDs",
"uses task SIDs",
@@ -618,10 +600,6 @@ static int selinux_set_mnt_opts(struct super_block *sb,
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
- spin_lock(&sb_security_lock);
- if (list_empty(&sbsec->list))
- list_add(&sbsec->list, &superblock_security_head);
- spin_unlock(&sb_security_lock);
goto out;
}
rc = -EINVAL;
@@ -812,16 +790,10 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
/*
* if the parent was able to be mounted it clearly had no special lsm
- * mount options. thus we can safely put this sb on the list and deal
- * with it later
+ * mount options. thus we can safely deal with this superblock later
*/
- if (!ss_initialized) {
- spin_lock(&sb_security_lock);
- if (list_empty(&newsbsec->list))
- list_add(&newsbsec->list, &superblock_security_head);
- spin_unlock(&sb_security_lock);
+ if (!ss_initialized)
return;
- }
/* how can we clone if the old one wasn't set up?? */
BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
@@ -2049,29 +2021,30 @@ static int selinux_quota_on(struct dentry *dentry)
return dentry_has_perm(cred, NULL, dentry, FILE__QUOTAON);
}
-static int selinux_syslog(int type)
+static int selinux_syslog(int type, bool from_file)
{
int rc;
- rc = cap_syslog(type);
+ rc = cap_syslog(type, from_file);
if (rc)
return rc;
switch (type) {
- case 3: /* Read last kernel messages */
- case 10: /* Return size of the log buffer */
+ case SYSLOG_ACTION_READ_ALL: /* Read last kernel messages */
+ case SYSLOG_ACTION_SIZE_BUFFER: /* Return size of the log buffer */
rc = task_has_system(current, SYSTEM__SYSLOG_READ);
break;
- case 6: /* Disable logging to console */
- case 7: /* Enable logging to console */
- case 8: /* Set level of messages printed to console */
+ case SYSLOG_ACTION_CONSOLE_OFF: /* Disable logging to console */
+ case SYSLOG_ACTION_CONSOLE_ON: /* Enable logging to console */
+ /* Set level of messages printed to console */
+ case SYSLOG_ACTION_CONSOLE_LEVEL:
rc = task_has_system(current, SYSTEM__SYSLOG_CONSOLE);
break;
- case 0: /* Close log */
- case 1: /* Open log */
- case 2: /* Read from log */
- case 4: /* Read/clear last kernel messages */
- case 5: /* Clear ring buffer */
+ case SYSLOG_ACTION_CLOSE: /* Close log */
+ case SYSLOG_ACTION_OPEN: /* Open log */
+ case SYSLOG_ACTION_READ: /* Read from log */
+ case SYSLOG_ACTION_READ_CLEAR: /* Read/clear last kernel messages */
+ case SYSLOG_ACTION_CLEAR: /* Clear ring buffer */
default:
rc = task_has_system(current, SYSTEM__SYSLOG_MOD);
break;
@@ -3004,13 +2977,15 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
return file_has_perm(cred, file, av);
}
+static int default_noexec;
+
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
{
const struct cred *cred = current_cred();
int rc = 0;
-#ifndef CONFIG_PPC32
- if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ if (default_noexec &&
+ (prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
/*
* We are making executable an anonymous mapping or a
* private file mapping that will also be writable.
@@ -3020,7 +2995,6 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
if (rc)
goto error;
}
-#endif
if (file) {
/* read access is always possible with a mapping */
@@ -3081,8 +3055,8 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (selinux_checkreqprot)
prot = reqprot;
-#ifndef CONFIG_PPC32
- if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ if (default_noexec &&
+ (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
@@ -3104,7 +3078,6 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (rc)
return rc;
}
-#endif
return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
}
@@ -3334,7 +3307,7 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
if (ret == 0)
tsec->create_sid = isec->sid;
- return 0;
+ return ret;
}
static int selinux_kernel_module_request(char *kmod_name)
@@ -4007,7 +3980,7 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
struct socket *other,
struct sock *newsk)
{
- struct sk_security_struct *ssec;
+ struct sk_security_struct *sksec;
struct inode_security_struct *isec;
struct inode_security_struct *other_isec;
struct common_audit_data ad;
@@ -4026,13 +3999,13 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
return err;
/* connecting socket */
- ssec = sock->sk->sk_security;
- ssec->peer_sid = other_isec->sid;
+ sksec = sock->sk->sk_security;
+ sksec->peer_sid = other_isec->sid;
/* server child socket */
- ssec = newsk->sk_security;
- ssec->peer_sid = isec->sid;
- err = security_sid_mls_copy(other_isec->sid, ssec->peer_sid, &ssec->sid);
+ sksec = newsk->sk_security;
+ sksec->peer_sid = isec->sid;
+ err = security_sid_mls_copy(other_isec->sid, sksec->peer_sid, &sksec->sid);
return err;
}
@@ -4195,7 +4168,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
int err = 0;
char *scontext;
u32 scontext_len;
- struct sk_security_struct *ssec;
+ struct sk_security_struct *sksec;
struct inode_security_struct *isec;
u32 peer_sid = SECSID_NULL;
@@ -4203,8 +4176,8 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
isec->sclass == SECCLASS_TCP_SOCKET) {
- ssec = sock->sk->sk_security;
- peer_sid = ssec->peer_sid;
+ sksec = sock->sk->sk_security;
+ peer_sid = sksec->peer_sid;
}
if (peer_sid == SECSID_NULL) {
err = -ENOPROTOOPT;
@@ -4271,14 +4244,14 @@ static void selinux_sk_free_security(struct sock *sk)
static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk)
{
- struct sk_security_struct *ssec = sk->sk_security;
- struct sk_security_struct *newssec = newsk->sk_security;
+ struct sk_security_struct *sksec = sk->sk_security;
+ struct sk_security_struct *newsksec = newsk->sk_security;
- newssec->sid = ssec->sid;
- newssec->peer_sid = ssec->peer_sid;
- newssec->sclass = ssec->sclass;
+ newsksec->sid = sksec->sid;
+ newsksec->peer_sid = sksec->peer_sid;
+ newsksec->sclass = sksec->sclass;
- selinux_netlbl_sk_security_reset(newssec);
+ selinux_netlbl_sk_security_reset(newsksec);
}
static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
@@ -5667,14 +5640,13 @@ static __init int selinux_init(void)
/* Set the security state for the initial task. */
cred_init_security();
+ default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
+
sel_inode_cache = kmem_cache_create("selinux_inode_security",
sizeof(struct inode_security_struct),
0, SLAB_PANIC, NULL);
avc_init();
- secondary_ops = security_ops;
- if (!secondary_ops)
- panic("SELinux: No initial security operations\n");
if (register_security(&selinux_ops))
panic("SELinux: Unable to register with kernel.\n");
@@ -5686,35 +5658,18 @@ static __init int selinux_init(void)
return 0;
}
+static void delayed_superblock_init(struct super_block *sb, void *unused)
+{
+ superblock_doinit(sb, NULL);
+}
+
void selinux_complete_init(void)
{
printk(KERN_DEBUG "SELinux: Completing initialization.\n");
/* Set up any superblocks initialized prior to the policy load. */
printk(KERN_DEBUG "SELinux: Setting up existing superblocks.\n");
- spin_lock(&sb_lock);
- spin_lock(&sb_security_lock);
-next_sb:
- if (!list_empty(&superblock_security_head)) {
- struct superblock_security_struct *sbsec =
- list_entry(superblock_security_head.next,
- struct superblock_security_struct,
- list);
- struct super_block *sb = sbsec->sb;
- sb->s_count++;
- spin_unlock(&sb_security_lock);
- spin_unlock(&sb_lock);
- down_read(&sb->s_umount);
- if (sb->s_root)
- superblock_doinit(sb, NULL);
- drop_super(sb);
- spin_lock(&sb_lock);
- spin_lock(&sb_security_lock);
- list_del_init(&sbsec->list);
- goto next_sb;
- }
- spin_unlock(&sb_security_lock);
- spin_unlock(&sb_lock);
+ iterate_supers(delayed_superblock_init, NULL);
}
/* SELinux requires early initialization in order to label
@@ -5835,8 +5790,7 @@ int selinux_disable(void)
selinux_disabled = 1;
selinux_enabled = 0;
- /* Reset security_ops to the secondary module, dummy or capability. */
- security_ops = secondary_ops;
+ reset_security_ops();
/* Try to destroy the avc node cache */
avc_disable();
diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h
index d4fac82793ae..a59b64e3fd02 100644
--- a/security/selinux/include/initial_sid_to_string.h
+++ b/security/selinux/include/initial_sid_to_string.h
@@ -1,5 +1,5 @@
/* This file is automatically generated. Do not edit. */
-static char *initial_sid_to_string[] =
+static const char *initial_sid_to_string[] =
{
"null",
"kernel",
diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h
index 8d7384280a7a..cf2f628e6e28 100644
--- a/security/selinux/include/netlabel.h
+++ b/security/selinux/include/netlabel.h
@@ -42,8 +42,8 @@ void selinux_netlbl_cache_invalidate(void);
void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway);
-void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec);
-void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec);
+void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec);
+void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec);
int selinux_netlbl_skbuff_getsid(struct sk_buff *skb,
u16 family,
@@ -79,13 +79,13 @@ static inline void selinux_netlbl_err(struct sk_buff *skb,
}
static inline void selinux_netlbl_sk_security_free(
- struct sk_security_struct *ssec)
+ struct sk_security_struct *sksec)
{
return;
}
static inline void selinux_netlbl_sk_security_reset(
- struct sk_security_struct *ssec)
+ struct sk_security_struct *sksec)
{
return;
}
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index c4e062336ef3..26c7eee1c309 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -55,7 +55,6 @@ struct file_security_struct {
struct superblock_security_struct {
struct super_block *sb; /* back pointer to sb object */
- struct list_head list; /* list of superblock_security_struct */
u32 sid; /* SID of file system superblock */
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 2553266ad793..1f7c2491d3dc 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -57,7 +57,6 @@
struct netlbl_lsm_secattr;
extern int selinux_enabled;
-extern int selinux_mls_enabled;
/* Policy capabilities */
enum {
@@ -80,6 +79,8 @@ extern int selinux_policycap_openperm;
/* limitation of boundary depth */
#define POLICYDB_BOUNDS_MAXDEPTH 4
+int security_mls_enabled(void);
+
int security_load_policy(void *data, size_t len);
int security_policycap_supported(unsigned int req_cap);
@@ -96,13 +97,11 @@ struct av_decision {
/* definitions of av_decision.flags */
#define AVD_FLAGS_PERMISSIVE 0x0001
-int security_compute_av(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd);
+void security_compute_av(u32 ssid, u32 tsid,
+ u16 tclass, struct av_decision *avd);
-int security_compute_av_user(u32 ssid, u32 tsid,
- u16 tclass, u32 requested,
- struct av_decision *avd);
+void security_compute_av_user(u32 ssid, u32 tsid,
+ u16 tclass, struct av_decision *avd);
int security_transition_sid(u32 ssid, u32 tsid,
u16 tclass, u32 *out_sid);
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index b4e14bc0bf32..d6095d63d831 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -16,6 +16,7 @@
*/
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
#include <linux/list.h>
diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c
index 2534400317c5..1c2fc46544bf 100644
--- a/security/selinux/netlabel.c
+++ b/security/selinux/netlabel.c
@@ -29,6 +29,7 @@
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
+#include <linux/gfp.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <net/sock.h>
@@ -131,21 +132,21 @@ void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway)
/**
* selinux_netlbl_sk_security_free - Free the NetLabel fields
- * @sssec: the sk_security_struct
+ * @sksec: the sk_security_struct
*
* Description:
* Free all of the memory in the NetLabel fields of a sk_security_struct.
*
*/
-void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
+void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec)
{
- if (ssec->nlbl_secattr != NULL)
- netlbl_secattr_free(ssec->nlbl_secattr);
+ if (sksec->nlbl_secattr != NULL)
+ netlbl_secattr_free(sksec->nlbl_secattr);
}
/**
* selinux_netlbl_sk_security_reset - Reset the NetLabel fields
- * @ssec: the sk_security_struct
+ * @sksec: the sk_security_struct
* @family: the socket family
*
* Description:
@@ -153,9 +154,9 @@ void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
* The caller is responsibile for all the NetLabel sk_security_struct locking.
*
*/
-void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec)
+void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec)
{
- ssec->nlbl_state = NLBL_UNSET;
+ sksec->nlbl_state = NLBL_UNSET;
}
/**
diff --git a/security/selinux/netlink.c b/security/selinux/netlink.c
index 1ae556446e65..36ac257cec9a 100644
--- a/security/selinux/netlink.c
+++ b/security/selinux/netlink.c
@@ -11,9 +11,9 @@
*/
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/stddef.h>
#include <linux/kernel.h>
-#include <linux/list.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/selinux_netlink.h>
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 7100072bb1b0..dc92792271f1 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/list.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/in.h>
#include <linux/in6.h>
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index fe7fba67f19f..cfe2d72d3fb7 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -30,6 +30,7 @@
#include <linux/types.h>
#include <linux/rcupdate.h>
#include <linux/list.h>
+#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/in.h>
#include <linux/in6.h>
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index dd7cc6de77f9..75ec0c6ebacd 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -11,7 +11,6 @@
*/
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/if.h>
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index fab36fdf2769..0293843f7eda 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -282,7 +282,8 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf,
char tmpbuf[TMPBUFLEN];
ssize_t length;
- length = scnprintf(tmpbuf, TMPBUFLEN, "%d", selinux_mls_enabled);
+ length = scnprintf(tmpbuf, TMPBUFLEN, "%d",
+ security_mls_enabled());
return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
}
@@ -494,7 +495,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
char *scon, *tcon;
u32 ssid, tsid;
u16 tclass;
- u32 req;
struct av_decision avd;
ssize_t length;
@@ -503,28 +503,26 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
return length;
length = -ENOMEM;
- scon = kzalloc(size+1, GFP_KERNEL);
+ scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
- tcon = kzalloc(size+1, GFP_KERNEL);
+ tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
length = -EINVAL;
- if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
+ if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
- length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
- length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
- length = security_compute_av_user(ssid, tsid, tclass, req, &avd);
- if (length < 0)
- goto out2;
+ security_compute_av_user(ssid, tsid, tclass, &avd);
length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
"%x %x %x %x %u %x",
@@ -552,11 +550,11 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
return length;
length = -ENOMEM;
- scon = kzalloc(size+1, GFP_KERNEL);
+ scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
- tcon = kzalloc(size+1, GFP_KERNEL);
+ tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
@@ -564,10 +562,10 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
goto out2;
- length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
- length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
@@ -611,11 +609,11 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size)
return length;
length = -ENOMEM;
- scon = kzalloc(size+1, GFP_KERNEL);
+ scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
- tcon = kzalloc(size+1, GFP_KERNEL);
+ tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
@@ -623,10 +621,10 @@ 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 out2;
- length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
- length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
@@ -668,11 +666,11 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
return length;
length = -ENOMEM;
- con = kzalloc(size+1, GFP_KERNEL);
+ con = kzalloc(size + 1, GFP_KERNEL);
if (!con)
return length;
- user = kzalloc(size+1, GFP_KERNEL);
+ user = kzalloc(size + 1, GFP_KERNEL);
if (!user)
goto out;
@@ -680,7 +678,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size)
if (sscanf(buf, "%s %s", con, user) != 2)
goto out2;
- length = security_context_to_sid(con, strlen(con)+1, &sid);
+ length = security_context_to_sid(con, strlen(con) + 1, &sid);
if (length < 0)
goto out2;
@@ -729,11 +727,11 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size)
return length;
length = -ENOMEM;
- scon = kzalloc(size+1, GFP_KERNEL);
+ scon = kzalloc(size + 1, GFP_KERNEL);
if (!scon)
return length;
- tcon = kzalloc(size+1, GFP_KERNEL);
+ tcon = kzalloc(size + 1, GFP_KERNEL);
if (!tcon)
goto out;
@@ -741,10 +739,10 @@ 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 out2;
- length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
+ length = security_context_to_sid(scon, strlen(scon) + 1, &ssid);
if (length < 0)
goto out2;
- length = security_context_to_sid(tcon, strlen(tcon)+1, &tsid);
+ length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid);
if (length < 0)
goto out2;
@@ -979,6 +977,8 @@ static int sel_make_bools(void)
u32 sid;
/* remove any existing files */
+ for (i = 0; i < bool_num; i++)
+ kfree(bool_pending_names[i]);
kfree(bool_pending_names);
kfree(bool_pending_values);
bool_pending_names = NULL;
@@ -1401,7 +1401,7 @@ static int sel_make_perm_files(char *objclass, int classvalue,
}
inode->i_fop = &sel_perm_ops;
/* i+1 since perm values are 1-indexed */
- inode->i_ino = sel_perm_to_ino(classvalue, i+1);
+ inode->i_ino = sel_perm_to_ino(classvalue, i + 1);
d_add(dentry, inode);
}
@@ -1489,7 +1489,7 @@ static int sel_make_classes(void)
goto out;
/* +2 since classes are 1-indexed */
- last_class_ino = sel_class_to_ino(nclasses+2);
+ last_class_ino = sel_class_to_ino(nclasses + 2);
for (i = 0; i < nclasses; i++) {
struct dentry *class_name_dir;
@@ -1506,7 +1506,7 @@ static int sel_make_classes(void)
goto out1;
/* i+1 since class values are 1-indexed */
- rc = sel_make_class_dir_entries(classes[i], i+1,
+ rc = sel_make_class_dir_entries(classes[i], i + 1,
class_name_dir);
if (rc)
goto out1;
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index 8da6a8428086..cd4f734e2749 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -82,7 +82,7 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified
void avtab_cache_init(void);
void avtab_cache_destroy(void);
-#define MAX_AVTAB_HASH_BITS 13
+#define MAX_AVTAB_HASH_BITS 11
#define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS)
#define MAX_AVTAB_HASH_MASK (MAX_AVTAB_HASH_BUCKETS-1)
#define MAX_AVTAB_SIZE MAX_AVTAB_HASH_BUCKETS
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index d9dd7a2f6a8a..45e8fb0515f8 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -41,9 +41,6 @@ static inline int mls_context_cpy(struct context *dst, struct context *src)
{
int rc;
- if (!selinux_mls_enabled)
- return 0;
-
dst->range.level[0].sens = src->range.level[0].sens;
rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
if (rc)
@@ -64,9 +61,6 @@ static inline int mls_context_cpy_low(struct context *dst, struct context *src)
{
int rc;
- if (!selinux_mls_enabled)
- return 0;
-
dst->range.level[0].sens = src->range.level[0].sens;
rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[0].cat);
if (rc)
@@ -82,9 +76,6 @@ out:
static inline int mls_context_cmp(struct context *c1, struct context *c2)
{
- if (!selinux_mls_enabled)
- return 1;
-
return ((c1->range.level[0].sens == c2->range.level[0].sens) &&
ebitmap_cmp(&c1->range.level[0].cat, &c2->range.level[0].cat) &&
(c1->range.level[1].sens == c2->range.level[1].sens) &&
@@ -93,9 +84,6 @@ static inline int mls_context_cmp(struct context *c1, struct context *c2)
static inline void mls_context_destroy(struct context *c)
{
- if (!selinux_mls_enabled)
- return;
-
ebitmap_destroy(&c->range.level[0].cat);
ebitmap_destroy(&c->range.level[1].cat);
mls_context_init(c);
diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c
index 68c7348d1acc..04b6145d767f 100644
--- a/security/selinux/ss/ebitmap.c
+++ b/security/selinux/ss/ebitmap.c
@@ -128,7 +128,7 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap,
cmap_idx = delta / NETLBL_CATMAP_MAPSIZE;
cmap_sft = delta % NETLBL_CATMAP_MAPSIZE;
c_iter->bitmap[cmap_idx]
- |= e_iter->maps[cmap_idx] << cmap_sft;
+ |= e_iter->maps[i] << cmap_sft;
}
e_iter = e_iter->next;
}
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 3f2b2706b5bb..b4eff7a60c50 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -39,7 +39,7 @@ int mls_compute_context_len(struct context *context)
struct ebitmap *e;
struct ebitmap_node *node;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return 0;
len = 1; /* for the beginning ":" */
@@ -93,7 +93,7 @@ void mls_sid_to_context(struct context *context,
struct ebitmap *e;
struct ebitmap_node *node;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return;
scontextp = *scontext;
@@ -200,7 +200,7 @@ int mls_context_isvalid(struct policydb *p, struct context *c)
{
struct user_datum *usrdatum;
- if (!selinux_mls_enabled)
+ if (!p->mls_enabled)
return 1;
if (!mls_range_isvalid(p, &c->range))
@@ -253,9 +253,9 @@ int mls_context_to_sid(struct policydb *pol,
struct cat_datum *catdatum, *rngdatum;
int l, rc = -EINVAL;
- if (!selinux_mls_enabled) {
+ if (!pol->mls_enabled) {
if (def_sid != SECSID_NULL && oldc)
- *scontext += strlen(*scontext)+1;
+ *scontext += strlen(*scontext) + 1;
return 0;
}
@@ -387,7 +387,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
char *tmpstr, *freestr;
int rc;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return -EINVAL;
/* we need freestr because mls_context_to_sid will change
@@ -407,7 +407,7 @@ int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
/*
* Copies the MLS range `range' into `context'.
*/
-static inline int mls_range_set(struct context *context,
+int mls_range_set(struct context *context,
struct mls_range *range)
{
int l, rc = 0;
@@ -427,7 +427,7 @@ static inline int mls_range_set(struct context *context,
int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
struct context *usercon)
{
- if (selinux_mls_enabled) {
+ if (policydb.mls_enabled) {
struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
struct mls_level *user_low = &(user->range.level[0]);
@@ -477,7 +477,7 @@ int mls_convert_context(struct policydb *oldp,
struct ebitmap_node *node;
int l, i;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return 0;
for (l = 0; l < 2; l++) {
@@ -513,23 +513,21 @@ int mls_compute_sid(struct context *scontext,
u32 specified,
struct context *newcontext)
{
- struct range_trans *rtr;
+ struct range_trans rtr;
+ struct mls_range *r;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return 0;
switch (specified) {
case AVTAB_TRANSITION:
/* Look for a range transition rule. */
- for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
- if (rtr->source_type == scontext->type &&
- rtr->target_type == tcontext->type &&
- rtr->target_class == tclass) {
- /* Set the range from the rule */
- return mls_range_set(newcontext,
- &rtr->target_range);
- }
- }
+ rtr.source_type = scontext->type;
+ rtr.target_type = tcontext->type;
+ rtr.target_class = tclass;
+ r = hashtab_search(policydb.range_tr, &rtr);
+ if (r)
+ return mls_range_set(newcontext, r);
/* Fallthrough */
case AVTAB_CHANGE:
if (tclass == policydb.process_class)
@@ -541,8 +539,8 @@ int mls_compute_sid(struct context *scontext,
case AVTAB_MEMBER:
/* Use the process effective MLS attributes. */
return mls_context_cpy_low(newcontext, scontext);
- default:
- return -EINVAL;
+
+ /* fall through */
}
return -EINVAL;
}
@@ -561,7 +559,7 @@ int mls_compute_sid(struct context *scontext,
void mls_export_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return;
secattr->attr.mls.lvl = context->range.level[0].sens - 1;
@@ -581,7 +579,7 @@ void mls_export_netlbl_lvl(struct context *context,
void mls_import_netlbl_lvl(struct context *context,
struct netlbl_lsm_secattr *secattr)
{
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return;
context->range.level[0].sens = secattr->attr.mls.lvl + 1;
@@ -603,7 +601,7 @@ int mls_export_netlbl_cat(struct context *context,
{
int rc;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return 0;
rc = ebitmap_netlbl_export(&context->range.level[0].cat,
@@ -631,7 +629,7 @@ int mls_import_netlbl_cat(struct context *context,
{
int rc;
- if (!selinux_mls_enabled)
+ if (!policydb.mls_enabled)
return 0;
rc = ebitmap_netlbl_import(&context->range.level[0].cat,
diff --git a/security/selinux/ss/mls.h b/security/selinux/ss/mls.h
index 1276715aaa8b..cd9152632e54 100644
--- a/security/selinux/ss/mls.h
+++ b/security/selinux/ss/mls.h
@@ -39,6 +39,8 @@ int mls_context_to_sid(struct policydb *p,
int mls_from_string(char *str, struct context *context, gfp_t gfp_mask);
+int mls_range_set(struct context *context, struct mls_range *range);
+
int mls_convert_context(struct policydb *oldp,
struct policydb *newp,
struct context *context);
diff --git a/security/selinux/ss/mls_types.h b/security/selinux/ss/mls_types.h
index b6e943a21061..03bed52a8052 100644
--- a/security/selinux/ss/mls_types.h
+++ b/security/selinux/ss/mls_types.h
@@ -15,6 +15,7 @@
#define _SS_MLS_TYPES_H_
#include "security.h"
+#include "ebitmap.h"
struct mls_level {
u32 sens; /* sensitivity */
@@ -27,18 +28,12 @@ struct mls_range {
static inline int mls_level_eq(struct mls_level *l1, struct mls_level *l2)
{
- if (!selinux_mls_enabled)
- return 1;
-
return ((l1->sens == l2->sens) &&
ebitmap_cmp(&l1->cat, &l2->cat));
}
static inline int mls_level_dom(struct mls_level *l1, struct mls_level *l2)
{
- if (!selinux_mls_enabled)
- return 1;
-
return ((l1->sens >= l2->sens) &&
ebitmap_contains(&l1->cat, &l2->cat));
}
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index f03667213ea8..c57802a164d5 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -40,7 +40,7 @@
#define _DEBUG_HASHES
#ifdef DEBUG_HASHES
-static char *symtab_name[SYM_NUM] = {
+static const char *symtab_name[SYM_NUM] = {
"common prefixes",
"classes",
"roles",
@@ -52,8 +52,6 @@ static char *symtab_name[SYM_NUM] = {
};
#endif
-int selinux_mls_enabled;
-
static unsigned int symtab_sizes[SYM_NUM] = {
2,
32,
@@ -158,12 +156,11 @@ static int roles_init(struct policydb *p)
rc = -EINVAL;
goto out_free_role;
}
- key = kmalloc(strlen(OBJECT_R)+1, GFP_KERNEL);
+ key = kstrdup(OBJECT_R, GFP_KERNEL);
if (!key) {
rc = -ENOMEM;
goto out_free_role;
}
- strcpy(key, OBJECT_R);
rc = hashtab_insert(p->p_roles.table, key, role);
if (rc)
goto out_free_key;
@@ -177,6 +174,21 @@ out_free_role:
goto out;
}
+static u32 rangetr_hash(struct hashtab *h, const void *k)
+{
+ const struct range_trans *key = k;
+ return (key->source_type + (key->target_type << 3) +
+ (key->target_class << 5)) & (h->size - 1);
+}
+
+static int rangetr_cmp(struct hashtab *h, const void *k1, const void *k2)
+{
+ const struct range_trans *key1 = k1, *key2 = k2;
+ return (key1->source_type != key2->source_type ||
+ key1->target_type != key2->target_type ||
+ key1->target_class != key2->target_class);
+}
+
/*
* Initialize a policy database structure.
*/
@@ -204,6 +216,10 @@ static int policydb_init(struct policydb *p)
if (rc)
goto out_free_symtab;
+ p->range_tr = hashtab_create(rangetr_hash, rangetr_cmp, 256);
+ if (!p->range_tr)
+ goto out_free_symtab;
+
ebitmap_init(&p->policycaps);
ebitmap_init(&p->permissive_map);
@@ -408,6 +424,20 @@ static void symtab_hash_eval(struct symtab *s)
info.slots_used, h->size, info.max_chain_len);
}
}
+
+static void rangetr_hash_eval(struct hashtab *h)
+{
+ struct hashtab_info info;
+
+ hashtab_stat(h, &info);
+ printk(KERN_DEBUG "SELinux: rangetr: %d entries and %d/%d buckets used, "
+ "longest chain length %d\n", h->nel,
+ info.slots_used, h->size, info.max_chain_len);
+}
+#else
+static inline void rangetr_hash_eval(struct hashtab *h)
+{
+}
#endif
/*
@@ -422,7 +452,7 @@ static int policydb_index_others(struct policydb *p)
printk(KERN_DEBUG "SELinux: %d users, %d roles, %d types, %d bools",
p->p_users.nprim, p->p_roles.nprim, p->p_types.nprim, p->p_bools.nprim);
- if (selinux_mls_enabled)
+ if (p->mls_enabled)
printk(", %d sens, %d cats", p->p_levels.nprim,
p->p_cats.nprim);
printk("\n");
@@ -612,6 +642,17 @@ static int (*destroy_f[SYM_NUM]) (void *key, void *datum, void *datap) =
cat_destroy,
};
+static int range_tr_destroy(void *key, void *datum, void *p)
+{
+ struct mls_range *rt = datum;
+ kfree(key);
+ ebitmap_destroy(&rt->level[0].cat);
+ ebitmap_destroy(&rt->level[1].cat);
+ kfree(datum);
+ cond_resched();
+ return 0;
+}
+
static void ocontext_destroy(struct ocontext *c, int i)
{
context_destroy(&c->context[0]);
@@ -632,7 +673,6 @@ void policydb_destroy(struct policydb *p)
int i;
struct role_allow *ra, *lra = NULL;
struct role_trans *tr, *ltr = NULL;
- struct range_trans *rt, *lrt = NULL;
for (i = 0; i < SYM_NUM; i++) {
cond_resched();
@@ -693,20 +733,8 @@ void policydb_destroy(struct policydb *p)
}
kfree(lra);
- for (rt = p->range_tr; rt; rt = rt->next) {
- cond_resched();
- if (lrt) {
- ebitmap_destroy(&lrt->target_range.level[0].cat);
- ebitmap_destroy(&lrt->target_range.level[1].cat);
- kfree(lrt);
- }
- lrt = rt;
- }
- if (lrt) {
- ebitmap_destroy(&lrt->target_range.level[0].cat);
- ebitmap_destroy(&lrt->target_range.level[1].cat);
- kfree(lrt);
- }
+ hashtab_map(p->range_tr, range_tr_destroy, NULL);
+ hashtab_destroy(p->range_tr);
if (p->type_attr_map) {
for (i = 0; i < p->p_types.nprim; i++)
@@ -1686,12 +1714,11 @@ int policydb_read(struct policydb *p, void *fp)
int i, j, rc;
__le32 buf[4];
u32 nodebuf[8];
- u32 len, len2, config, nprim, nel, nel2;
+ u32 len, len2, nprim, nel, nel2;
char *policydb_str;
struct policydb_compat_info *info;
- struct range_trans *rt, *lrt;
-
- config = 0;
+ struct range_trans *rt;
+ struct mls_range *r;
rc = policydb_init(p);
if (rc)
@@ -1740,7 +1767,7 @@ int policydb_read(struct policydb *p, void *fp)
kfree(policydb_str);
policydb_str = NULL;
- /* Read the version, config, and table sizes. */
+ /* Read the version and table sizes. */
rc = next_entry(buf, fp, sizeof(u32)*4);
if (rc < 0)
goto bad;
@@ -1755,13 +1782,7 @@ int policydb_read(struct policydb *p, void *fp)
}
if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_MLS)) {
- if (ss_initialized && !selinux_mls_enabled) {
- printk(KERN_ERR "SELinux: Cannot switch between non-MLS"
- " and MLS policies\n");
- goto bad;
- }
- selinux_mls_enabled = 1;
- config |= POLICYDB_CONFIG_MLS;
+ p->mls_enabled = 1;
if (p->policyvers < POLICYDB_VERSION_MLS) {
printk(KERN_ERR "SELinux: security policydb version %d "
@@ -1769,12 +1790,6 @@ int policydb_read(struct policydb *p, void *fp)
p->policyvers);
goto bad;
}
- } else {
- if (ss_initialized && selinux_mls_enabled) {
- printk(KERN_ERR "SELinux: Cannot switch between MLS and"
- " non-MLS policies\n");
- goto bad;
- }
}
p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
@@ -2122,47 +2137,64 @@ int policydb_read(struct policydb *p, void *fp)
if (rc < 0)
goto bad;
nel = le32_to_cpu(buf[0]);
- lrt = NULL;
for (i = 0; i < nel; i++) {
rt = kzalloc(sizeof(*rt), GFP_KERNEL);
if (!rt) {
rc = -ENOMEM;
goto bad;
}
- if (lrt)
- lrt->next = rt;
- else
- p->range_tr = rt;
rc = next_entry(buf, fp, (sizeof(u32) * 2));
- if (rc < 0)
+ if (rc < 0) {
+ kfree(rt);
goto bad;
+ }
rt->source_type = le32_to_cpu(buf[0]);
rt->target_type = le32_to_cpu(buf[1]);
if (new_rangetr) {
rc = next_entry(buf, fp, sizeof(u32));
- if (rc < 0)
+ if (rc < 0) {
+ kfree(rt);
goto bad;
+ }
rt->target_class = le32_to_cpu(buf[0]);
} else
rt->target_class = p->process_class;
if (!policydb_type_isvalid(p, rt->source_type) ||
!policydb_type_isvalid(p, rt->target_type) ||
!policydb_class_isvalid(p, rt->target_class)) {
+ kfree(rt);
rc = -EINVAL;
goto bad;
}
- rc = mls_read_range_helper(&rt->target_range, fp);
- if (rc)
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ kfree(rt);
+ rc = -ENOMEM;
goto bad;
- if (!mls_range_isvalid(p, &rt->target_range)) {
+ }
+ rc = mls_read_range_helper(r, fp);
+ if (rc) {
+ kfree(rt);
+ kfree(r);
+ goto bad;
+ }
+ if (!mls_range_isvalid(p, r)) {
printk(KERN_WARNING "SELinux: rangetrans: invalid range\n");
+ kfree(rt);
+ kfree(r);
+ goto bad;
+ }
+ rc = hashtab_insert(p->range_tr, rt, r);
+ if (rc) {
+ kfree(rt);
+ kfree(r);
goto bad;
}
- lrt = rt;
}
+ rangetr_hash_eval(p->range_tr);
}
- p->type_attr_map = kmalloc(p->p_types.nprim*sizeof(struct ebitmap), GFP_KERNEL);
+ p->type_attr_map = kmalloc(p->p_types.nprim * sizeof(struct ebitmap), GFP_KERNEL);
if (!p->type_attr_map)
goto bad;
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index cdcc5700946f..26d9adf8542b 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -27,6 +27,8 @@
#include "symtab.h"
#include "avtab.h"
#include "sidtab.h"
+#include "ebitmap.h"
+#include "mls_types.h"
#include "context.h"
#include "constraint.h"
@@ -113,8 +115,6 @@ struct range_trans {
u32 source_type;
u32 target_type;
u32 target_class;
- struct mls_range target_range;
- struct range_trans *next;
};
/* Boolean data type */
@@ -187,6 +187,8 @@ struct genfs {
/* The policy database */
struct policydb {
+ int mls_enabled;
+
/* symbol tables */
struct symtab symtab[SYM_NUM];
#define p_commons symtab[SYM_COMMONS]
@@ -240,8 +242,8 @@ struct policydb {
fixed labeling behavior. */
struct genfs *genfs;
- /* range transitions */
- struct range_trans *range_tr;
+ /* range transitions table (range_trans_key -> mls_range) */
+ struct hashtab *range_tr;
/* type -> attribute reverse mapping */
struct ebitmap *type_attr_map;
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index b3efae204ac7..1de60ce90d9a 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -26,6 +26,10 @@
*
* Added support for bounds domain and audit messaged on masked permissions
*
+ * Updated: Guido Trentalancia <guido@trentalancia.com>
+ *
+ * Added support for runtime switching of the policy type
+ *
* Copyright (C) 2008, 2009 NEC Corporation
* Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
@@ -87,11 +91,10 @@ static u32 latest_granting;
static int context_struct_to_string(struct context *context, char **scontext,
u32 *scontext_len);
-static int context_struct_compute_av(struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- u32 requested,
- struct av_decision *avd);
+static void context_struct_compute_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ struct av_decision *avd);
struct selinux_mapping {
u16 value; /* policy value */
@@ -196,23 +199,6 @@ static u16 unmap_class(u16 tclass)
return tclass;
}
-static u32 unmap_perm(u16 tclass, u32 tperm)
-{
- if (tclass < current_mapping_size) {
- unsigned i;
- u32 kperm = 0;
-
- for (i = 0; i < current_mapping[tclass].num_perms; i++)
- if (tperm & (1<<i)) {
- kperm |= current_mapping[tclass].perms[i];
- tperm &= ~(1<<i);
- }
- return kperm;
- }
-
- return tperm;
-}
-
static void map_decision(u16 tclass, struct av_decision *avd,
int allow_unknown)
{
@@ -250,6 +236,10 @@ static void map_decision(u16 tclass, struct av_decision *avd,
}
}
+int security_mls_enabled(void)
+{
+ return policydb.mls_enabled;
+}
/*
* Return the boolean value of a constraint expression
@@ -284,15 +274,15 @@ static int constraint_expr_eval(struct context *scontext,
case CEXPR_AND:
BUG_ON(sp < 1);
sp--;
- s[sp] &= s[sp+1];
+ s[sp] &= s[sp + 1];
break;
case CEXPR_OR:
BUG_ON(sp < 1);
sp--;
- s[sp] |= s[sp+1];
+ s[sp] |= s[sp + 1];
break;
case CEXPR_ATTR:
- if (sp == (CEXPR_MAXDEPTH-1))
+ if (sp == (CEXPR_MAXDEPTH - 1))
return 0;
switch (e->attr) {
case CEXPR_USER:
@@ -465,7 +455,8 @@ static void security_dump_masked_av(struct context *scontext,
char *scontext_name = NULL;
char *tcontext_name = NULL;
char *permission_names[32];
- int index, length;
+ int index;
+ u32 length;
bool need_comma = false;
if (!permissions)
@@ -532,7 +523,6 @@ out:
static void type_attribute_bounds_av(struct context *scontext,
struct context *tcontext,
u16 tclass,
- u32 requested,
struct av_decision *avd)
{
struct context lo_scontext;
@@ -553,7 +543,6 @@ static void type_attribute_bounds_av(struct context *scontext,
context_struct_compute_av(&lo_scontext,
tcontext,
tclass,
- requested,
&lo_avd);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
@@ -569,7 +558,6 @@ static void type_attribute_bounds_av(struct context *scontext,
context_struct_compute_av(scontext,
&lo_tcontext,
tclass,
- requested,
&lo_avd);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
@@ -586,7 +574,6 @@ static void type_attribute_bounds_av(struct context *scontext,
context_struct_compute_av(&lo_scontext,
&lo_tcontext,
tclass,
- requested,
&lo_avd);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
@@ -607,11 +594,10 @@ 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.
*/
-static int context_struct_compute_av(struct context *scontext,
- struct context *tcontext,
- u16 tclass,
- u32 requested,
- struct av_decision *avd)
+static void context_struct_compute_av(struct context *scontext,
+ struct context *tcontext,
+ u16 tclass,
+ struct av_decision *avd)
{
struct constraint_node *constraint;
struct role_allow *ra;
@@ -622,19 +608,14 @@ static int context_struct_compute_av(struct context *scontext,
struct ebitmap_node *snode, *tnode;
unsigned int i, j;
- /*
- * Initialize the access vectors to the default values.
- */
avd->allowed = 0;
avd->auditallow = 0;
avd->auditdeny = 0xffffffff;
- avd->seqno = latest_granting;
- avd->flags = 0;
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
if (printk_ratelimit())
printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass);
- return -EINVAL;
+ return;
}
tclass_datum = policydb.class_val_to_struct[tclass - 1];
@@ -705,9 +686,7 @@ static int context_struct_compute_av(struct context *scontext,
* permission and notice it to userspace via audit.
*/
type_attribute_bounds_av(scontext, tcontext,
- tclass, requested, avd);
-
- return 0;
+ tclass, avd);
}
static int security_validtrans_handle_fail(struct context *ocontext,
@@ -864,7 +843,7 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
if (rc) {
char *old_name = NULL;
char *new_name = NULL;
- int length;
+ u32 length;
if (!context_struct_to_string(old_context,
&old_name, &length) &&
@@ -886,110 +865,116 @@ out:
return rc;
}
-
-static int security_compute_av_core(u32 ssid,
- u32 tsid,
- u16 tclass,
- u32 requested,
- struct av_decision *avd)
+static void avd_init(struct av_decision *avd)
{
- struct context *scontext = NULL, *tcontext = NULL;
- int rc = 0;
-
- scontext = sidtab_search(&sidtab, ssid);
- if (!scontext) {
- printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
- __func__, ssid);
- return -EINVAL;
- }
- tcontext = sidtab_search(&sidtab, tsid);
- if (!tcontext) {
- printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
- __func__, tsid);
- return -EINVAL;
- }
-
- rc = context_struct_compute_av(scontext, tcontext, tclass,
- requested, avd);
-
- /* permissive domain? */
- if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
- avd->flags |= AVD_FLAGS_PERMISSIVE;
-
- return rc;
+ avd->allowed = 0;
+ avd->auditallow = 0;
+ avd->auditdeny = 0xffffffff;
+ avd->seqno = latest_granting;
+ avd->flags = 0;
}
+
/**
* security_compute_av - Compute access vector decisions.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
- * @requested: requested permissions
* @avd: access vector decisions
*
* Compute a set of access vector decisions based on the
* SID pair (@ssid, @tsid) for the permissions in @tclass.
- * Return -%EINVAL if any of the parameters are invalid or %0
- * if the access vector decisions were computed successfully.
*/
-int security_compute_av(u32 ssid,
- u32 tsid,
- u16 orig_tclass,
- u32 orig_requested,
- struct av_decision *avd)
+void security_compute_av(u32 ssid,
+ u32 tsid,
+ u16 orig_tclass,
+ struct av_decision *avd)
{
u16 tclass;
- u32 requested;
- int rc;
+ struct context *scontext = NULL, *tcontext = NULL;
read_lock(&policy_rwlock);
-
+ avd_init(avd);
if (!ss_initialized)
goto allow;
- requested = unmap_perm(orig_tclass, orig_requested);
+ scontext = sidtab_search(&sidtab, ssid);
+ if (!scontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, ssid);
+ goto out;
+ }
+
+ /* permissive domain? */
+ if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+ avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+ 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;
- rc = -EINVAL;
goto out;
}
- rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+ context_struct_compute_av(scontext, tcontext, tclass, avd);
map_decision(orig_tclass, avd, policydb.allow_unknown);
out:
read_unlock(&policy_rwlock);
- return rc;
+ return;
allow:
avd->allowed = 0xffffffff;
- avd->auditallow = 0;
- avd->auditdeny = 0xffffffff;
- avd->seqno = latest_granting;
- avd->flags = 0;
- rc = 0;
goto out;
}
-int security_compute_av_user(u32 ssid,
- u32 tsid,
- u16 tclass,
- u32 requested,
- struct av_decision *avd)
+void security_compute_av_user(u32 ssid,
+ u32 tsid,
+ u16 tclass,
+ struct av_decision *avd)
{
- int rc;
+ struct context *scontext = NULL, *tcontext = NULL;
- if (!ss_initialized) {
- avd->allowed = 0xffffffff;
- avd->auditallow = 0;
- avd->auditdeny = 0xffffffff;
- avd->seqno = latest_granting;
- return 0;
+ read_lock(&policy_rwlock);
+ avd_init(avd);
+ 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;
}
- read_lock(&policy_rwlock);
- rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
+ /* permissive domain? */
+ if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
+ avd->flags |= AVD_FLAGS_PERMISSIVE;
+
+ tcontext = sidtab_search(&sidtab, tsid);
+ if (!tcontext) {
+ printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
+ __func__, tsid);
+ goto out;
+ }
+
+ if (unlikely(!tclass)) {
+ if (policydb.allow_unknown)
+ goto allow;
+ goto out;
+ }
+
+ context_struct_compute_av(scontext, tcontext, tclass, avd);
+ out:
read_unlock(&policy_rwlock);
- return rc;
+ return;
+allow:
+ avd->allowed = 0xffffffff;
+ goto out;
}
/*
@@ -1231,7 +1216,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len,
*sid = SECSID_NULL;
/* Copy the string so that we can modify the copy as we parse it. */
- scontext2 = kmalloc(scontext_len+1, gfp_flags);
+ scontext2 = kmalloc(scontext_len + 1, gfp_flags);
if (!scontext2)
return -ENOMEM;
memcpy(scontext2, scontext, scontext_len);
@@ -1565,7 +1550,10 @@ static int clone_sid(u32 sid,
{
struct sidtab *s = arg;
- return sidtab_insert(s, sid, context);
+ if (sid > SECINITSID_NUM)
+ return sidtab_insert(s, sid, context);
+ else
+ return 0;
}
static inline int convert_context_handle_invalid_context(struct context *context)
@@ -1606,12 +1594,17 @@ static int convert_context(u32 key,
{
struct convert_context_args *args;
struct context oldc;
+ struct ocontext *oc;
+ struct mls_range *range;
struct role_datum *role;
struct type_datum *typdatum;
struct user_datum *usrdatum;
char *s;
u32 len;
- int rc;
+ int rc = 0;
+
+ if (key <= SECINITSID_NUM)
+ goto out;
args = p;
@@ -1673,9 +1666,39 @@ static int convert_context(u32 key,
goto bad;
c->type = typdatum->value;
- rc = mls_convert_context(args->oldp, args->newp, c);
- if (rc)
- goto bad;
+ /* Convert the MLS fields if dealing with MLS policies */
+ if (args->oldp->mls_enabled && args->newp->mls_enabled) {
+ rc = mls_convert_context(args->oldp, args->newp, c);
+ if (rc)
+ goto bad;
+ } else if (args->oldp->mls_enabled && !args->newp->mls_enabled) {
+ /*
+ * Switching between MLS and non-MLS policy:
+ * free any storage used by the MLS fields in the
+ * context for all existing entries in the sidtab.
+ */
+ mls_context_destroy(c);
+ } else if (!args->oldp->mls_enabled && args->newp->mls_enabled) {
+ /*
+ * Switching between non-MLS and MLS policy:
+ * ensure that the MLS fields of the context for all
+ * existing entries in the sidtab are filled in with a
+ * suitable default value, likely taken from one of the
+ * initial SIDs.
+ */
+ oc = args->newp->ocontexts[OCON_ISID];
+ while (oc && oc->sid[0] != SECINITSID_UNLABELED)
+ oc = oc->next;
+ if (!oc) {
+ printk(KERN_ERR "SELinux: unable to look up"
+ " the initial SIDs list\n");
+ goto bad;
+ }
+ range = &oc->context[0].range;
+ rc = mls_range_set(c, range);
+ if (rc)
+ goto bad;
+ }
/* Check the validity of the new context. */
if (!policydb_context_isvalid(args->newp, c)) {
@@ -1737,22 +1760,28 @@ int security_load_policy(void *data, size_t len)
if (!ss_initialized) {
avtab_cache_init();
- if (policydb_read(&policydb, fp)) {
+ rc = policydb_read(&policydb, fp);
+ if (rc) {
avtab_cache_destroy();
- return -EINVAL;
+ return rc;
}
- if (selinux_set_mapping(&policydb, secclass_map,
- &current_mapping,
- &current_mapping_size)) {
+
+ rc = selinux_set_mapping(&policydb, secclass_map,
+ &current_mapping,
+ &current_mapping_size);
+ if (rc) {
policydb_destroy(&policydb);
avtab_cache_destroy();
- return -EINVAL;
+ return rc;
}
- if (policydb_load_isids(&policydb, &sidtab)) {
+
+ rc = policydb_load_isids(&policydb, &sidtab);
+ if (rc) {
policydb_destroy(&policydb);
avtab_cache_destroy();
- return -EINVAL;
+ return rc;
}
+
security_load_policycaps();
ss_initialized = 1;
seqno = ++latest_granting;
@@ -1768,16 +1797,25 @@ int security_load_policy(void *data, size_t len)
sidtab_hash_eval(&sidtab, "sids");
#endif
- if (policydb_read(&newpolicydb, fp))
- return -EINVAL;
+ rc = policydb_read(&newpolicydb, fp);
+ if (rc)
+ return rc;
+
+ /* If switching between different policy types, log MLS status */
+ if (policydb.mls_enabled && !newpolicydb.mls_enabled)
+ printk(KERN_INFO "SELinux: Disabling MLS support...\n");
+ else if (!policydb.mls_enabled && newpolicydb.mls_enabled)
+ printk(KERN_INFO "SELinux: Enabling MLS support...\n");
- if (sidtab_init(&newsidtab)) {
+ rc = policydb_load_isids(&newpolicydb, &newsidtab);
+ if (rc) {
+ printk(KERN_ERR "SELinux: unable to load the initial SIDs\n");
policydb_destroy(&newpolicydb);
- return -ENOMEM;
+ return rc;
}
- if (selinux_set_mapping(&newpolicydb, secclass_map,
- &map, &map_size))
+ rc = selinux_set_mapping(&newpolicydb, secclass_map, &map, &map_size);
+ if (rc)
goto err;
rc = security_preserve_bools(&newpolicydb);
@@ -1788,10 +1826,10 @@ int security_load_policy(void *data, size_t len)
/* Clone the SID table. */
sidtab_shutdown(&sidtab);
- if (sidtab_map(&sidtab, clone_sid, &newsidtab)) {
- rc = -ENOMEM;
+
+ rc = sidtab_map(&sidtab, clone_sid, &newsidtab);
+ if (rc)
goto err;
- }
/*
* Convert the internal representations of contexts
@@ -1800,8 +1838,12 @@ int security_load_policy(void *data, size_t len)
args.oldp = &policydb;
args.newp = &newpolicydb;
rc = sidtab_map(&newsidtab, convert_context, &args);
- if (rc)
+ if (rc) {
+ printk(KERN_ERR "SELinux: unable to convert the internal"
+ " representation of contexts in the new SID"
+ " table\n");
goto err;
+ }
/* Save the old policydb and SID table to free later. */
memcpy(&oldpolicydb, &policydb, sizeof policydb);
@@ -2066,9 +2108,9 @@ int security_get_user_sids(u32 fromsid,
ebitmap_for_each_positive_bit(&user->roles, rnode, i) {
role = policydb.role_val_to_struct[i];
- usercon.role = i+1;
+ usercon.role = i + 1;
ebitmap_for_each_positive_bit(&role->types, tnode, j) {
- usercon.type = j+1;
+ usercon.type = j + 1;
if (mls_setup_user_range(fromcon, user, &usercon))
continue;
@@ -2397,7 +2439,7 @@ int security_sid_mls_copy(u32 sid, u32 mls_sid, u32 *new_sid)
u32 len;
int rc = 0;
- if (!ss_initialized || !selinux_mls_enabled) {
+ if (!ss_initialized || !policydb.mls_enabled) {
*new_sid = sid;
goto out;
}
@@ -2498,7 +2540,7 @@ int security_net_peersid_resolve(u32 nlbl_sid, u32 nlbl_type,
/* we don't need to check ss_initialized here since the only way both
* nlbl_sid and xfrm_sid are not equal to SECSID_NULL would be if the
* security server was initialized and ss_initialized was true */
- if (!selinux_mls_enabled) {
+ if (!policydb.mls_enabled) {
*peer_sid = SECSID_NULL;
return 0;
}
@@ -2555,7 +2597,7 @@ int security_get_classes(char ***classes, int *nclasses)
read_lock(&policy_rwlock);
*nclasses = policydb.p_classes.nprim;
- *classes = kcalloc(*nclasses, sizeof(*classes), GFP_ATOMIC);
+ *classes = kcalloc(*nclasses, sizeof(**classes), GFP_ATOMIC);
if (!*classes)
goto out;
@@ -2602,7 +2644,7 @@ int security_get_permissions(char *class, char ***perms, int *nperms)
}
*nperms = match->permissions.nprim;
- *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+ *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC);
if (!*perms)
goto out;
diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c
index 837658a98a54..bcf9f620426e 100644
--- a/security/selinux/ss/symtab.c
+++ b/security/selinux/ss/symtab.c
@@ -4,7 +4,6 @@
* Author : Stephen Smalley, <sds@epoch.ncsc.mil>
*/
#include <linux/kernel.h>
-#include <linux/slab.h>
#include <linux/string.h>
#include <linux/errno.h>
#include "symtab.h"
diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c
index f3cb9ed731a9..fff78d3b51a2 100644
--- a/security/selinux/xfrm.c
+++ b/security/selinux/xfrm.c
@@ -38,6 +38,7 @@
#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>
#include <linux/skbuff.h>
diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c
index 0f9ac8146900..f4fac64c4da8 100644
--- a/security/smack/smack_access.c
+++ b/security/smack/smack_access.c
@@ -11,6 +11,7 @@
*/
#include <linux/types.h>
+#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include "smack.h"
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 529c9ca65878..0f2fc480fc61 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -19,12 +19,12 @@
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/stat.h>
-#include <linux/ext2_fs.h>
#include <linux/kd.h>
#include <asm/ioctls.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
+#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/pipe_fs_i.h>
#include <net/netlabel.h>
@@ -157,12 +157,12 @@ static int smack_ptrace_traceme(struct task_struct *ptp)
*
* Returns 0 on success, error code otherwise.
*/
-static int smack_syslog(int type)
+static int smack_syslog(int type, bool from_file)
{
int rc;
char *sp = current_security();
- rc = cap_syslog(type);
+ rc = cap_syslog(type, from_file);
if (rc != 0)
return rc;
@@ -387,7 +387,7 @@ static int smack_sb_umount(struct vfsmount *mnt, int flags)
struct smk_audit_info ad;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_FS);
- smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_mountpoint);
+ smk_ad_setfield_u_fs_path_dentry(&ad, mnt->mnt_root);
smk_ad_setfield_u_fs_path_mnt(&ad, mnt);
sbp = mnt->mnt_sb->s_security;
@@ -1118,15 +1118,6 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
}
/**
- * smack_cred_commit - commit new credentials
- * @new: the new credentials
- * @old: the original credentials
- */
-static void smack_cred_commit(struct cred *new, const struct cred *old)
-{
-}
-
-/**
* smack_cred_transfer - Transfer the old credentials to the new credentials
* @new: the new credentials
* @old: the original credentials
@@ -3120,7 +3111,6 @@ struct security_operations smack_ops = {
.cred_alloc_blank = smack_cred_alloc_blank,
.cred_free = smack_cred_free,
.cred_prepare = smack_cred_prepare,
- .cred_commit = smack_cred_commit,
.cred_transfer = smack_cred_transfer,
.kernel_act_as = smack_kernel_act_as,
.kernel_create_files_as = smack_kernel_create_files_as,
diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c
index aeead7585093..a2b72d77f926 100644
--- a/security/smack/smackfs.c
+++ b/security/smack/smackfs.c
@@ -20,6 +20,7 @@
#include <linux/vmalloc.h>
#include <linux/security.h>
#include <linux/mutex.h>
+#include <linux/slab.h>
#include <net/net_namespace.h>
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index 10ccd686b290..4fb39030f6bd 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -1 +1 @@
-obj-y = common.o realpath.o tomoyo.o domain.o file.o
+obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index e0d0354008b7..b5dbdc9ff73c 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -10,11 +10,13 @@
*/
#include <linux/uaccess.h>
+#include <linux/slab.h>
#include <linux/security.h>
#include <linux/hardirq.h>
-#include "realpath.h"
#include "common.h"
-#include "tomoyo.h"
+
+/* Lock for protecting policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
/* Has loading policy done? */
bool tomoyo_policy_loaded;
@@ -74,6 +76,49 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
const int buffer_len);
/**
+ * tomoyo_parse_name_union - Parse a tomoyo_name_union.
+ *
+ * @filename: Name or name group.
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_parse_name_union(const char *filename,
+ struct tomoyo_name_union *ptr)
+{
+ if (!tomoyo_is_correct_path(filename, 0, 0, 0))
+ return false;
+ if (filename[0] == '@') {
+ ptr->group = tomoyo_get_path_group(filename + 1);
+ ptr->is_group = true;
+ return ptr->group != NULL;
+ }
+ ptr->filename = tomoyo_get_name(filename);
+ ptr->is_group = false;
+ return ptr->filename != NULL;
+}
+
+/**
+ * tomoyo_print_name_union - Print a tomoyo_name_union.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,
+ const struct tomoyo_name_union *ptr)
+{
+ int pos = head->read_avail;
+ if (pos && head->read_buf[pos - 1] == ' ')
+ head->read_avail--;
+ if (ptr->is_group)
+ return tomoyo_io_printf(head, " @%s",
+ ptr->group->group_name->name);
+ return tomoyo_io_printf(head, " %s", ptr->filename->name);
+}
+
+/**
* tomoyo_is_byte_range - Check whether the string isa \ooo style octal value.
*
* @str: Pointer to the string.
@@ -170,6 +215,33 @@ static void tomoyo_normalize_line(unsigned char *buffer)
}
/**
+ * tomoyo_tokenize - Tokenize string.
+ *
+ * @buffer: The line to tokenize.
+ * @w: Pointer to "char *".
+ * @size: Sizeof @w .
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size)
+{
+ int count = size / sizeof(char *);
+ int i;
+ for (i = 0; i < count; i++)
+ w[i] = "";
+ for (i = 0; i < count; i++) {
+ char *cp = strchr(buffer, ' ');
+ if (cp)
+ *cp = '\0';
+ w[i] = buffer;
+ if (!cp)
+ break;
+ buffer = cp + 1;
+ }
+ return i < count || !*buffer;
+}
+
+/**
* tomoyo_is_correct_path - Validate a pathname.
* @filename: The pathname to check.
* @start_type: Should the pathname start with '/'?
@@ -178,14 +250,12 @@ static void tomoyo_normalize_line(unsigned char *buffer)
* 1 = must / -1 = must not / 0 = don't care
* @end_type: Should the pathname end with '/'?
* 1 = must / -1 = must not / 0 = don't care
- * @function: The name of function calling me.
*
* Check whether the given filename follows the naming rules.
* Returns true if @filename follows the naming rules, false otherwise.
*/
bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
- const s8 pattern_type, const s8 end_type,
- const char *function)
+ const s8 pattern_type, const s8 end_type)
{
const char *const start = filename;
bool in_repetition = false;
@@ -193,7 +263,6 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
unsigned char c;
unsigned char d;
unsigned char e;
- const char *original_filename = filename;
if (!filename)
goto out;
@@ -282,25 +351,20 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
goto out;
return true;
out:
- printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function,
- original_filename);
return false;
}
/**
* tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules.
* @domainname: The domainname to check.
- * @function: The name of function calling me.
*
* Returns true if @domainname follows the naming rules, false otherwise.
*/
-bool tomoyo_is_correct_domain(const unsigned char *domainname,
- const char *function)
+bool tomoyo_is_correct_domain(const unsigned char *domainname)
{
unsigned char c;
unsigned char d;
unsigned char e;
- const char *org_domainname = domainname;
if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME,
TOMOYO_ROOT_NAME_LEN))
@@ -343,8 +407,6 @@ bool tomoyo_is_correct_domain(const unsigned char *domainname,
} while (*domainname);
return true;
out:
- printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function,
- org_domainname);
return false;
}
@@ -365,10 +427,9 @@ bool tomoyo_is_domain_def(const unsigned char *buffer)
*
* @domainname: The domainname to find.
*
- * Caller must call down_read(&tomoyo_domain_list_lock); or
- * down_write(&tomoyo_domain_list_lock); .
- *
* Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
{
@@ -377,7 +438,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname)
name.name = domainname;
tomoyo_fill_path_info(&name);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
if (!domain->is_deleted &&
!tomoyo_pathcmp(&name, domain->domainname))
return domain;
@@ -748,7 +809,7 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
*
* Returns the tomoyo_realpath() of current process on success, NULL otherwise.
*
- * This function uses tomoyo_alloc(), so the caller must call tomoyo_free()
+ * This function uses kzalloc(), so the caller must call kfree()
* if this function didn't return NULL.
*/
static const char *tomoyo_get_exe(void)
@@ -829,6 +890,8 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
* @domain: Pointer to "struct tomoyo_domain_info".
*
* Returns true if the domain is not exceeded quota, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
{
@@ -837,61 +900,29 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- if (ptr->type & TOMOYO_ACL_DELETED)
- continue;
- switch (tomoyo_acl_type2(ptr)) {
- struct tomoyo_single_path_acl_record *acl1;
- struct tomoyo_double_path_acl_record *acl2;
- u16 perm;
- case TOMOYO_TYPE_SINGLE_PATH_ACL:
- acl1 = container_of(ptr,
- struct tomoyo_single_path_acl_record,
- head);
- perm = acl1->perm;
- if (perm & (1 << TOMOYO_TYPE_EXECUTE_ACL))
- count++;
- if (perm &
- ((1 << TOMOYO_TYPE_READ_ACL) |
- (1 << TOMOYO_TYPE_WRITE_ACL)))
- count++;
- if (perm & (1 << TOMOYO_TYPE_CREATE_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_UNLINK_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_MKDIR_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_RMDIR_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_MKFIFO_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_MKSOCK_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_MKBLOCK_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_MKCHAR_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_TRUNCATE_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_SYMLINK_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_REWRITE_ACL))
- count++;
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ switch (ptr->type) {
+ struct tomoyo_path_acl *acl;
+ u32 perm;
+ u8 i;
+ case TOMOYO_TYPE_PATH_ACL:
+ acl = container_of(ptr, struct tomoyo_path_acl, head);
+ perm = acl->perm | (((u32) acl->perm_high) << 16);
+ for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_READ_WRITE))
+ count -= 2;
break;
- case TOMOYO_TYPE_DOUBLE_PATH_ACL:
- acl2 = container_of(ptr,
- struct tomoyo_double_path_acl_record,
- head);
- perm = acl2->perm;
- if (perm & (1 << TOMOYO_TYPE_LINK_ACL))
- count++;
- if (perm & (1 << TOMOYO_TYPE_RENAME_ACL))
- count++;
+ case TOMOYO_TYPE_PATH2_ACL:
+ perm = container_of(ptr, struct tomoyo_path2_acl, head)
+ ->perm;
+ for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
break;
}
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
return true;
if (!domain->quota_warned) {
@@ -913,25 +944,28 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain)
static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
int profile)
{
- static DEFINE_MUTEX(lock);
struct tomoyo_profile *ptr = NULL;
int i;
if (profile >= TOMOYO_MAX_PROFILES)
return NULL;
- mutex_lock(&lock);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return NULL;
ptr = tomoyo_profile_ptr[profile];
if (ptr)
goto ok;
- ptr = tomoyo_alloc_element(sizeof(*ptr));
- if (!ptr)
+ ptr = kmalloc(sizeof(*ptr), GFP_NOFS);
+ if (!tomoyo_memory_ok(ptr)) {
+ kfree(ptr);
+ ptr = NULL;
goto ok;
+ }
for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
ptr->value[i] = tomoyo_control_array[i].current_value;
mb(); /* Avoid out-of-order execution. */
tomoyo_profile_ptr[profile] = ptr;
ok:
- mutex_unlock(&lock);
+ mutex_unlock(&tomoyo_policy_lock);
return ptr;
}
@@ -966,7 +1000,9 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)
return -EINVAL;
*cp = '\0';
if (!strcmp(data, "COMMENT")) {
- profile->comment = tomoyo_save_name(cp + 1);
+ const struct tomoyo_path_info *old_comment = profile->comment;
+ profile->comment = tomoyo_get_name(cp + 1);
+ tomoyo_put_name(old_comment);
return 0;
}
for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
@@ -1061,27 +1097,6 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
}
/*
- * tomoyo_policy_manager_entry is a structure which is used for holding list of
- * domainnames or programs which are permitted to modify configuration via
- * /sys/kernel/security/tomoyo/ interface.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_policy_manager_list .
- * (2) "manager" is a domainname or a program's pathname.
- * (3) "is_domain" is a bool which is true if "manager" is a domainname, false
- * otherwise.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_policy_manager_entry {
- struct list_head list;
- /* A path to program or a domainname. */
- const struct tomoyo_path_info *manager;
- bool is_domain; /* True if manager is a domainname. */
- bool is_deleted; /* True if this entry is deleted. */
-};
-
-/*
* tomoyo_policy_manager_list is used for holding list of domainnames or
* programs which are permitted to modify configuration via
* /sys/kernel/security/tomoyo/ interface.
@@ -1111,8 +1126,7 @@ struct tomoyo_policy_manager_entry {
*
* # cat /sys/kernel/security/tomoyo/manager
*/
-static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
+LIST_HEAD(tomoyo_policy_manager_list);
/**
* tomoyo_update_manager_entry - Add a manager entry.
@@ -1121,48 +1135,48 @@ static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_manager_entry(const char *manager,
const bool is_delete)
{
- struct tomoyo_policy_manager_entry *new_entry;
struct tomoyo_policy_manager_entry *ptr;
- const struct tomoyo_path_info *saved_manager;
- int error = -ENOMEM;
- bool is_domain = false;
+ struct tomoyo_policy_manager_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
if (tomoyo_is_domain_def(manager)) {
- if (!tomoyo_is_correct_domain(manager, __func__))
+ if (!tomoyo_is_correct_domain(manager))
return -EINVAL;
- is_domain = true;
+ e.is_domain = true;
} else {
- if (!tomoyo_is_correct_path(manager, 1, -1, -1, __func__))
+ if (!tomoyo_is_correct_path(manager, 1, -1, -1))
return -EINVAL;
}
- saved_manager = tomoyo_save_name(manager);
- if (!saved_manager)
+ e.manager = tomoyo_get_name(manager);
+ if (!e.manager)
return -ENOMEM;
- down_write(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
- if (ptr->manager != saved_manager)
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
+ if (ptr->manager != e.manager)
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_policy_manager_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_policy_manager_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->manager = saved_manager;
- new_entry->is_domain = is_domain;
- list_add_tail(&new_entry->list, &tomoyo_policy_manager_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_policy_manager_list_lock);
+ tomoyo_put_name(e.manager);
return error;
}
@@ -1172,6 +1186,8 @@ static int tomoyo_update_manager_entry(const char *manager,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1191,6 +1207,8 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
{
@@ -1199,7 +1217,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
if (head->read_eof)
return 0;
- down_read(&tomoyo_policy_manager_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_policy_manager_list) {
struct tomoyo_policy_manager_entry *ptr;
@@ -1211,7 +1228,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
@@ -1221,6 +1237,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head)
*
* Returns true if the current process is permitted to modify policy
* via /sys/kernel/security/tomoyo/ interface.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_policy_manager(void)
{
@@ -1234,29 +1252,25 @@ static bool tomoyo_is_policy_manager(void)
return true;
if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && ptr->is_domain
&& !tomoyo_pathcmp(domainname, ptr->manager)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (found)
return true;
exe = tomoyo_get_exe();
if (!exe)
return false;
- down_read(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (!ptr->is_deleted && !ptr->is_domain
&& !strcmp(exe, ptr->manager->name)) {
found = true;
break;
}
}
- up_read(&tomoyo_policy_manager_list_lock);
if (!found) { /* Reduce error messages. */
static pid_t last_pid;
const pid_t pid = current->pid;
@@ -1266,7 +1280,7 @@ static bool tomoyo_is_policy_manager(void)
last_pid = pid;
}
}
- tomoyo_free(exe);
+ kfree(exe);
return found;
}
@@ -1277,6 +1291,8 @@ static bool tomoyo_is_policy_manager(void)
* @data: String to parse.
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
const char *data)
@@ -1286,17 +1302,16 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
if (sscanf(data, "pid=%u", &pid) == 1) {
struct task_struct *p;
+ rcu_read_lock();
read_lock(&tasklist_lock);
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
+ rcu_read_unlock();
} else if (!strncmp(data, "domain=", 7)) {
- if (tomoyo_is_domain_def(data + 7)) {
- down_read(&tomoyo_domain_list_lock);
+ if (tomoyo_is_domain_def(data + 7))
domain = tomoyo_find_domain(data + 7);
- up_read(&tomoyo_domain_list_lock);
- }
} else
return false;
head->write_var1 = domain;
@@ -1310,13 +1325,11 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
if (domain) {
struct tomoyo_domain_info *d;
head->read_var1 = NULL;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(d, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(d, &tomoyo_domain_list, list) {
if (d == domain)
break;
head->read_var1 = &d->list;
}
- up_read(&tomoyo_domain_list_lock);
head->read_var2 = NULL;
head->read_bit = 0;
head->read_step = 0;
@@ -1332,6 +1345,8 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
@@ -1340,9 +1355,10 @@ static int tomoyo_delete_domain(char *domainname)
name.name = domainname;
tomoyo_fill_path_info(&name);
- down_write(&tomoyo_domain_list_lock);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return 0;
/* Is there an active domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
/* Never delete tomoyo_kernel_domain */
if (domain == &tomoyo_kernel_domain)
continue;
@@ -1352,7 +1368,7 @@ static int tomoyo_delete_domain(char *domainname)
domain->is_deleted = true;
break;
}
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return 0;
}
@@ -1362,6 +1378,8 @@ static int tomoyo_delete_domain(char *domainname)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1384,11 +1402,9 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
domain = NULL;
if (is_delete)
tomoyo_delete_domain(data);
- else if (is_select) {
- down_read(&tomoyo_domain_list_lock);
+ else if (is_select)
domain = tomoyo_find_domain(data);
- up_read(&tomoyo_domain_list_lock);
- } else
+ else
domain = tomoyo_find_or_assign_new_domain(data, 0);
head->write_var1 = domain;
return 0;
@@ -1403,46 +1419,39 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head)
return 0;
}
if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) {
- tomoyo_set_domain_flag(domain, is_delete,
- TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ);
+ domain->ignore_global_allow_read = !is_delete;
return 0;
}
return tomoyo_write_file_policy(data, domain, is_delete);
}
/**
- * tomoyo_print_single_path_acl - Print a single path ACL entry.
+ * tomoyo_print_path_acl - Print a single path ACL entry.
*
* @head: Pointer to "struct tomoyo_io_buffer".
- * @ptr: Pointer to "struct tomoyo_single_path_acl_record".
+ * @ptr: Pointer to "struct tomoyo_path_acl".
*
* Returns true on success, false otherwise.
*/
-static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
- struct tomoyo_single_path_acl_record *
- ptr)
+static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path_acl *ptr)
{
int pos;
u8 bit;
- const char *atmark = "";
- const char *filename;
- const u16 perm = ptr->perm;
-
- filename = ptr->filename->name;
- for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
- bit++) {
- const char *msg;
+ const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);
+
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
/* Print "read/write" instead of "read" and "write". */
- if ((bit == TOMOYO_TYPE_READ_ACL ||
- bit == TOMOYO_TYPE_WRITE_ACL)
- && (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
+ if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE)
+ && (perm & (1 << TOMOYO_TYPE_READ_WRITE)))
continue;
- msg = tomoyo_sp2keyword(bit);
pos = head->read_avail;
- if (!tomoyo_io_printf(head, "allow_%s %s%s\n", msg,
- atmark, filename))
+ if (!tomoyo_io_printf(head, "allow_%s ",
+ tomoyo_path2keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name) ||
+ !tomoyo_io_printf(head, "\n"))
goto out;
}
head->read_bit = 0;
@@ -1454,36 +1463,29 @@ static bool tomoyo_print_single_path_acl(struct tomoyo_io_buffer *head,
}
/**
- * tomoyo_print_double_path_acl - Print a double path ACL entry.
+ * tomoyo_print_path2_acl - Print a double path ACL entry.
*
* @head: Pointer to "struct tomoyo_io_buffer".
- * @ptr: Pointer to "struct tomoyo_double_path_acl_record".
+ * @ptr: Pointer to "struct tomoyo_path2_acl".
*
* Returns true on success, false otherwise.
*/
-static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
- struct tomoyo_double_path_acl_record *
- ptr)
+static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head,
+ struct tomoyo_path2_acl *ptr)
{
int pos;
- const char *atmark1 = "";
- const char *atmark2 = "";
- const char *filename1;
- const char *filename2;
const u8 perm = ptr->perm;
u8 bit;
- filename1 = ptr->filename1->name;
- filename2 = ptr->filename2->name;
- for (bit = head->read_bit; bit < TOMOYO_MAX_DOUBLE_PATH_OPERATION;
- bit++) {
- const char *msg;
+ for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
- msg = tomoyo_dp2keyword(bit);
pos = head->read_avail;
- if (!tomoyo_io_printf(head, "allow_%s %s%s %s%s\n", msg,
- atmark1, filename1, atmark2, filename2))
+ if (!tomoyo_io_printf(head, "allow_%s ",
+ tomoyo_path22keyword(bit)) ||
+ !tomoyo_print_name_union(head, &ptr->name1) ||
+ !tomoyo_print_name_union(head, &ptr->name2) ||
+ !tomoyo_io_printf(head, "\n"))
goto out;
}
head->read_bit = 0;
@@ -1505,23 +1507,17 @@ static bool tomoyo_print_double_path_acl(struct tomoyo_io_buffer *head,
static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
struct tomoyo_acl_info *ptr)
{
- const u8 acl_type = tomoyo_acl_type2(ptr);
+ const u8 acl_type = ptr->type;
- if (acl_type & TOMOYO_ACL_DELETED)
- return true;
- if (acl_type == TOMOYO_TYPE_SINGLE_PATH_ACL) {
- struct tomoyo_single_path_acl_record *acl
- = container_of(ptr,
- struct tomoyo_single_path_acl_record,
- head);
- return tomoyo_print_single_path_acl(head, acl);
+ if (acl_type == TOMOYO_TYPE_PATH_ACL) {
+ struct tomoyo_path_acl *acl
+ = container_of(ptr, struct tomoyo_path_acl, head);
+ return tomoyo_print_path_acl(head, acl);
}
- if (acl_type == TOMOYO_TYPE_DOUBLE_PATH_ACL) {
- struct tomoyo_double_path_acl_record *acl
- = container_of(ptr,
- struct tomoyo_double_path_acl_record,
- head);
- return tomoyo_print_double_path_acl(head, acl);
+ if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
+ struct tomoyo_path2_acl *acl
+ = container_of(ptr, struct tomoyo_path2_acl, head);
+ return tomoyo_print_path2_acl(head, acl);
}
BUG(); /* This must not happen. */
return false;
@@ -1533,6 +1529,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
{
@@ -1544,7 +1542,6 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
return 0;
if (head->read_step == 0)
head->read_step = 1;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
const char *quota_exceeded = "";
@@ -1558,10 +1555,9 @@ static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head)
/* Print domainname and flags. */
if (domain->quota_warned)
quota_exceeded = "quota_exceeded\n";
- if (domain->flags & TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED)
+ if (domain->transition_failed)
transition_failed = "transition_failed\n";
- if (domain->flags &
- TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ)
+ if (domain->ignore_global_allow_read)
ignore_global_allow_read
= TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n";
done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE
@@ -1577,7 +1573,6 @@ acl_loop:
if (head->read_step == 3)
goto tail_mark;
/* Print ACL entries in the domain. */
- down_read(&tomoyo_domain_acl_info_list_lock);
list_for_each_cookie(apos, head->read_var2,
&domain->acl_info_list) {
struct tomoyo_acl_info *ptr
@@ -1587,7 +1582,6 @@ acl_loop:
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
@@ -1599,7 +1593,6 @@ tail_mark:
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1615,6 +1608,8 @@ tail_mark:
*
* ( echo "select " $domainname; echo "use_profile " $profile ) |
* /usr/lib/ccs/loadpolicy -d
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1626,9 +1621,7 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
if (!cp)
return -EINVAL;
*cp = '\0';
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(cp + 1);
- up_read(&tomoyo_domain_list_lock);
if (strict_strtoul(data, 10, &profile))
return -EINVAL;
if (domain && profile < TOMOYO_MAX_PROFILES
@@ -1650,6 +1643,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head)
* awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" )
* domainname = $0; } else if ( $1 == "use_profile" ) {
* print $2 " " domainname; domainname = ""; } } ; '
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
{
@@ -1658,7 +1653,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
if (head->read_eof)
return 0;
- down_read(&tomoyo_domain_list_lock);
list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) {
struct tomoyo_domain_info *domain;
domain = list_entry(pos, struct tomoyo_domain_info, list);
@@ -1669,7 +1663,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
@@ -1707,11 +1700,13 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
const int pid = head->read_step;
struct task_struct *p;
struct tomoyo_domain_info *domain = NULL;
+ rcu_read_lock();
read_lock(&tasklist_lock);
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
+ rcu_read_unlock();
if (domain)
tomoyo_io_printf(head, "%d %u %s", pid, domain->profile,
domain->domainname->name);
@@ -1726,6 +1721,8 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1751,6 +1748,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
return tomoyo_write_pattern_policy(data, is_delete);
if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE))
return tomoyo_write_no_rewrite_policy(data, is_delete);
+ if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP))
+ return tomoyo_write_path_group_policy(data, is_delete);
return -EINVAL;
}
@@ -1760,6 +1759,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns 0 on success, -EINVAL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
{
@@ -1805,6 +1806,12 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head)
head->read_var2 = NULL;
head->read_step = 9;
case 9:
+ if (!tomoyo_read_path_group_policy(head))
+ break;
+ head->read_var1 = NULL;
+ head->read_var2 = NULL;
+ head->read_step = 10;
+ case 10:
head->read_eof = true;
break;
default:
@@ -1889,15 +1896,13 @@ void tomoyo_load_policy(const char *filename)
tomoyo_policy_loaded = true;
{ /* Check all profiles currently assigned to domains are defined. */
struct tomoyo_domain_info *domain;
- down_read(&tomoyo_domain_list_lock);
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
if (tomoyo_profile_ptr[profile])
continue;
panic("Profile %u (used by '%s') not defined.\n",
profile, domain->domainname->name);
}
- up_read(&tomoyo_domain_list_lock);
}
}
@@ -1945,10 +1950,12 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
* @file: Pointer to "struct file".
*
* Associates policy handler and returns 0 on success, -ENOMEM otherwise.
+ *
+ * Caller acquires tomoyo_read_lock().
*/
static int tomoyo_open_control(const u8 type, struct file *file)
{
- struct tomoyo_io_buffer *head = tomoyo_alloc(sizeof(*head));
+ struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS);
if (!head)
return -ENOMEM;
@@ -2009,9 +2016,9 @@ static int tomoyo_open_control(const u8 type, struct file *file)
} else {
if (!head->readbuf_size)
head->readbuf_size = 4096 * 2;
- head->read_buf = tomoyo_alloc(head->readbuf_size);
+ head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS);
if (!head->read_buf) {
- tomoyo_free(head);
+ kfree(head);
return -ENOMEM;
}
}
@@ -2023,13 +2030,14 @@ static int tomoyo_open_control(const u8 type, struct file *file)
head->write = NULL;
} else if (head->write) {
head->writebuf_size = 4096 * 2;
- head->write_buf = tomoyo_alloc(head->writebuf_size);
+ head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS);
if (!head->write_buf) {
- tomoyo_free(head->read_buf);
- tomoyo_free(head);
+ kfree(head->read_buf);
+ kfree(head);
return -ENOMEM;
}
}
+ head->reader_idx = tomoyo_read_lock();
file->private_data = head;
/*
* Call the handler now if the file is
@@ -2051,6 +2059,8 @@ static int tomoyo_open_control(const u8 type, struct file *file)
* @buffer_len: Size of @buffer.
*
* Returns bytes read on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_read_control(struct file *file, char __user *buffer,
const int buffer_len)
@@ -2094,6 +2104,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer,
* @buffer_len: Size of @buffer.
*
* Returns @buffer_len on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_control(struct file *file, const char __user *buffer,
const int buffer_len)
@@ -2144,52 +2156,29 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer,
* @file: Pointer to "struct file".
*
* Releases memory and returns 0.
+ *
+ * Caller looses tomoyo_read_lock().
*/
static int tomoyo_close_control(struct file *file)
{
struct tomoyo_io_buffer *head = file->private_data;
+ const bool is_write = !!head->write_buf;
+ tomoyo_read_unlock(head->reader_idx);
/* Release memory used for policy I/O. */
- tomoyo_free(head->read_buf);
+ kfree(head->read_buf);
head->read_buf = NULL;
- tomoyo_free(head->write_buf);
+ kfree(head->write_buf);
head->write_buf = NULL;
- tomoyo_free(head);
+ kfree(head);
head = NULL;
file->private_data = NULL;
+ if (is_write)
+ tomoyo_run_gc();
return 0;
}
/**
- * tomoyo_alloc_acl_element - Allocate permanent memory for ACL entry.
- *
- * @acl_type: Type of ACL entry.
- *
- * Returns pointer to the ACL entry on success, NULL otherwise.
- */
-void *tomoyo_alloc_acl_element(const u8 acl_type)
-{
- int len;
- struct tomoyo_acl_info *ptr;
-
- switch (acl_type) {
- case TOMOYO_TYPE_SINGLE_PATH_ACL:
- len = sizeof(struct tomoyo_single_path_acl_record);
- break;
- case TOMOYO_TYPE_DOUBLE_PATH_ACL:
- len = sizeof(struct tomoyo_double_path_acl_record);
- break;
- default:
- return NULL;
- }
- ptr = tomoyo_alloc_element(len);
- if (!ptr)
- return NULL;
- ptr->type = acl_type;
- return ptr;
-}
-
-/**
* tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
*
* @inode: Pointer to "struct inode".
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 92169d29b2db..9f1ae5e3ba51 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -1,12 +1,9 @@
/*
* security/tomoyo/common.h
*
- * Common functions for TOMOYO.
- *
- * Copyright (C) 2005-2009 NTT DATA CORPORATION
- *
- * Version: 2.2.0 2009/04/01
+ * Header file for TOMOYO.
*
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
*/
#ifndef _SECURITY_TOMOYO_COMMON_H
@@ -22,9 +19,120 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/list.h>
+#include <linux/cred.h>
+struct linux_binprm;
+
+/********** Constants definitions. **********/
+
+/*
+ * TOMOYO uses this hash only when appending a string into the string
+ * table. Frequency of appending strings is very low. So we don't need
+ * large (e.g. 64k) hash size. 256 will be sufficient.
+ */
+#define TOMOYO_HASH_BITS 8
+#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
+
+/*
+ * This is the max length of a token.
+ *
+ * A token consists of only ASCII printable characters.
+ * Non printable characters in a token is represented in \ooo style
+ * octal string. Thus, \ itself is represented as \\.
+ */
+#define TOMOYO_MAX_PATHNAME_LEN 4000
+
+/* Profile number is an integer between 0 and 255. */
+#define TOMOYO_MAX_PROFILES 256
+
+/* Keywords for ACLs. */
+#define TOMOYO_KEYWORD_ALIAS "alias "
+#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
+#define TOMOYO_KEYWORD_DELETE "delete "
+#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
+#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
+#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
+#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
+#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
+#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
+#define TOMOYO_KEYWORD_PATH_GROUP "path_group "
+#define TOMOYO_KEYWORD_SELECT "select "
+#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
+#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
+/* A domain definition starts with <kernel>. */
+#define TOMOYO_ROOT_NAME "<kernel>"
+#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
+
+/* Index numbers for Access Controls. */
+enum tomoyo_mac_index {
+ TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */
+ TOMOYO_MAX_ACCEPT_ENTRY,
+ TOMOYO_VERBOSE,
+ TOMOYO_MAX_CONTROL_INDEX
+};
+
+/* Index numbers for Access Controls. */
+enum tomoyo_acl_entry_type_index {
+ TOMOYO_TYPE_PATH_ACL,
+ TOMOYO_TYPE_PATH2_ACL,
+};
+
+/* Index numbers for File Controls. */
-struct dentry;
-struct vfsmount;
+/*
+ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
+ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
+ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
+ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
+ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
+ * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
+ */
+
+enum tomoyo_path_acl_index {
+ TOMOYO_TYPE_READ_WRITE,
+ TOMOYO_TYPE_EXECUTE,
+ TOMOYO_TYPE_READ,
+ TOMOYO_TYPE_WRITE,
+ TOMOYO_TYPE_CREATE,
+ TOMOYO_TYPE_UNLINK,
+ TOMOYO_TYPE_MKDIR,
+ TOMOYO_TYPE_RMDIR,
+ TOMOYO_TYPE_MKFIFO,
+ TOMOYO_TYPE_MKSOCK,
+ TOMOYO_TYPE_MKBLOCK,
+ TOMOYO_TYPE_MKCHAR,
+ TOMOYO_TYPE_TRUNCATE,
+ TOMOYO_TYPE_SYMLINK,
+ TOMOYO_TYPE_REWRITE,
+ TOMOYO_TYPE_IOCTL,
+ TOMOYO_TYPE_CHMOD,
+ TOMOYO_TYPE_CHOWN,
+ TOMOYO_TYPE_CHGRP,
+ TOMOYO_TYPE_CHROOT,
+ TOMOYO_TYPE_MOUNT,
+ TOMOYO_TYPE_UMOUNT,
+ TOMOYO_MAX_PATH_OPERATION
+};
+
+enum tomoyo_path2_acl_index {
+ TOMOYO_TYPE_LINK,
+ TOMOYO_TYPE_RENAME,
+ TOMOYO_TYPE_PIVOT_ROOT,
+ TOMOYO_MAX_PATH2_OPERATION
+};
+
+enum tomoyo_securityfs_interface_index {
+ TOMOYO_DOMAINPOLICY,
+ TOMOYO_EXCEPTIONPOLICY,
+ TOMOYO_DOMAIN_STATUS,
+ TOMOYO_PROCESS_STATUS,
+ TOMOYO_MEMINFO,
+ TOMOYO_SELFDOMAIN,
+ TOMOYO_VERSION,
+ TOMOYO_PROFILE,
+ TOMOYO_MANAGER
+};
+
+/********** Structure definitions. **********/
/*
* tomoyo_page_buffer is a structure which is used for holding a pathname
@@ -66,13 +174,14 @@ struct tomoyo_path_info {
};
/*
- * This is the max length of a token.
- *
- * A token consists of only ASCII printable characters.
- * Non printable characters in a token is represented in \ooo style
- * octal string. Thus, \ itself is represented as \\.
+ * tomoyo_name_entry is a structure which is used for linking
+ * "struct tomoyo_path_info" into tomoyo_name_list .
*/
-#define TOMOYO_MAX_PATHNAME_LEN 4000
+struct tomoyo_name_entry {
+ struct list_head list;
+ atomic_t users;
+ struct tomoyo_path_info entry;
+};
/*
* tomoyo_path_info_with_data is a structure which is used for holding a
@@ -89,42 +198,52 @@ struct tomoyo_path_info {
* "struct tomoyo_path_info_with_data".
*/
struct tomoyo_path_info_with_data {
- /* Keep "head" first, for this pointer is passed to tomoyo_free(). */
+ /* Keep "head" first, for this pointer is passed to kfree(). */
struct tomoyo_path_info head;
char barrier1[16]; /* Safeguard for overrun. */
char body[TOMOYO_MAX_PATHNAME_LEN];
char barrier2[16]; /* Safeguard for overrun. */
};
+struct tomoyo_name_union {
+ const struct tomoyo_path_info *filename;
+ struct tomoyo_path_group *group;
+ u8 is_group;
+};
+
+/* Structure for "path_group" directive. */
+struct tomoyo_path_group {
+ struct list_head list;
+ const struct tomoyo_path_info *group_name;
+ struct list_head member_list;
+ atomic_t users;
+};
+
+/* Structure for "path_group" directive. */
+struct tomoyo_path_group_member {
+ struct list_head list;
+ bool is_deleted;
+ const struct tomoyo_path_info *member_name;
+};
+
/*
* tomoyo_acl_info is a structure which is used for holding
*
* (1) "list" which is linked to the ->acl_info_list of
* "struct tomoyo_domain_info"
- * (2) "type" which tells
- * (a) type & 0x7F : type of the entry (either
- * "struct tomoyo_single_path_acl_record" or
- * "struct tomoyo_double_path_acl_record")
- * (b) type & 0x80 : whether the entry is marked as "deleted".
+ * (2) "type" which tells type of the entry (either
+ * "struct tomoyo_path_acl" or "struct tomoyo_path2_acl").
*
* Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_single_path_acl_record" to embed "u16" and
- * "struct tomoyo_double_path_acl_record" to embed "u8"
+ * "struct tomoyo_path_acl" to embed "u8" + "u16" and
+ * "struct tomoyo_path2_acl" to embed "u8"
* without enlarging their structure size.
*/
struct tomoyo_acl_info {
struct list_head list;
- /*
- * Type of this ACL entry.
- *
- * MSB is is_deleted flag.
- */
u8 type;
} __packed;
-/* This ACL entry is deleted. */
-#define TOMOYO_ACL_DELETED 0x80
-
/*
* tomoyo_domain_info is a structure which is used for holding permissions
* (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
@@ -138,7 +257,17 @@ struct tomoyo_acl_info {
* "deleted", false otherwise.
* (6) "quota_warned" is a bool which is used for suppressing warning message
* when learning mode learned too much entries.
- * (7) "flags" which remembers this domain's attributes.
+ * (7) "ignore_global_allow_read" is a bool which is true if this domain
+ * should ignore "allow_read" directive in exception policy.
+ * (8) "transition_failed" is a bool which is set to true when this domain was
+ * unable to create a new domain at tomoyo_find_next_domain() because the
+ * name of the domain to be created was too long or it could not allocate
+ * memory. If set to true, more than one process continued execve()
+ * without domain transition.
+ * (9) "users" is an atomic_t that holds how many "struct cred"->security
+ * are referring this "struct tomoyo_domain_info". If is_deleted == true
+ * and users == 0, this struct will be kfree()d upon next garbage
+ * collection.
*
* A domain's lifecycle is an analogy of files on / directory.
* Multiple domains with the same domainname cannot be created (as with
@@ -155,88 +284,54 @@ struct tomoyo_domain_info {
u8 profile; /* Profile number to use. */
bool is_deleted; /* Delete flag. */
bool quota_warned; /* Quota warnning flag. */
- /* DOMAIN_FLAGS_*. Use tomoyo_set_domain_flag() to modify. */
- u8 flags;
+ bool ignore_global_allow_read; /* Ignore "allow_read" flag. */
+ bool transition_failed; /* Domain transition failed flag. */
+ atomic_t users; /* Number of referring credentials. */
};
-/* Profile number is an integer between 0 and 255. */
-#define TOMOYO_MAX_PROFILES 256
-
-/* Ignore "allow_read" directive in exception policy. */
-#define TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1
/*
- * This domain was unable to create a new domain at tomoyo_find_next_domain()
- * because the name of the domain to be created was too long or
- * it could not allocate memory.
- * More than one process continued execve() without domain transition.
- */
-#define TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED 2
-
-/*
- * tomoyo_single_path_acl_record is a structure which is used for holding an
+ * tomoyo_path_acl is a structure which is used for holding an
* entry with one pathname operation (e.g. open(), mkdir()).
* It has following fields.
*
* (1) "head" which is a "struct tomoyo_acl_info".
* (2) "perm" which is a bitmask of permitted operations.
- * (3) "filename" is the pathname.
+ * (3) "name" is the pathname.
*
* Directives held by this structure are "allow_read/write", "allow_execute",
* "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir",
* "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock",
- * "allow_mkchar", "allow_truncate", "allow_symlink" and "allow_rewrite".
+ * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite",
+ * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount"
+ * and "allow_unmount".
*/
-struct tomoyo_single_path_acl_record {
- struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_SINGLE_PATH_ACL */
+struct tomoyo_path_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */
+ u8 perm_high;
u16 perm;
- /* Pointer to single pathname. */
- const struct tomoyo_path_info *filename;
+ struct tomoyo_name_union name;
};
/*
- * tomoyo_double_path_acl_record is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link() and rename()).
+ * tomoyo_path2_acl is a structure which is used for holding an
+ * entry with two pathnames operation (i.e. link(), rename() and pivot_root()).
* It has following fields.
*
* (1) "head" which is a "struct tomoyo_acl_info".
* (2) "perm" which is a bitmask of permitted operations.
- * (3) "filename1" is the source/old pathname.
- * (4) "filename2" is the destination/new pathname.
+ * (3) "name1" is the source/old pathname.
+ * (4) "name2" is the destination/new pathname.
*
- * Directives held by this structure are "allow_rename" and "allow_link".
+ * Directives held by this structure are "allow_rename", "allow_link" and
+ * "allow_pivot_root".
*/
-struct tomoyo_double_path_acl_record {
- struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_DOUBLE_PATH_ACL */
+struct tomoyo_path2_acl {
+ struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH2_ACL */
u8 perm;
- /* Pointer to single pathname. */
- const struct tomoyo_path_info *filename1;
- /* Pointer to single pathname. */
- const struct tomoyo_path_info *filename2;
+ struct tomoyo_name_union name1;
+ struct tomoyo_name_union name2;
};
-/* Keywords for ACLs. */
-#define TOMOYO_KEYWORD_ALIAS "alias "
-#define TOMOYO_KEYWORD_ALLOW_READ "allow_read "
-#define TOMOYO_KEYWORD_DELETE "delete "
-#define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite "
-#define TOMOYO_KEYWORD_FILE_PATTERN "file_pattern "
-#define TOMOYO_KEYWORD_INITIALIZE_DOMAIN "initialize_domain "
-#define TOMOYO_KEYWORD_KEEP_DOMAIN "keep_domain "
-#define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain "
-#define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain "
-#define TOMOYO_KEYWORD_SELECT "select "
-#define TOMOYO_KEYWORD_USE_PROFILE "use_profile "
-#define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read"
-/* A domain definition starts with <kernel>. */
-#define TOMOYO_ROOT_NAME "<kernel>"
-#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1)
-
-/* Index numbers for Access Controls. */
-#define TOMOYO_MAC_FOR_FILE 0 /* domain_policy.conf */
-#define TOMOYO_MAX_ACCEPT_ENTRY 1
-#define TOMOYO_VERBOSE 2
-#define TOMOYO_MAX_CONTROL_INDEX 3
-
/*
* tomoyo_io_buffer is a structure which is used for reading and modifying
* configuration via /sys/kernel/security/tomoyo/ interface.
@@ -265,6 +360,8 @@ struct tomoyo_io_buffer {
int (*write) (struct tomoyo_io_buffer *);
/* Exclusive lock for this structure. */
struct mutex io_sem;
+ /* Index returned by tomoyo_read_lock(). */
+ int reader_idx;
/* The position currently reading from. */
struct list_head *read_var1;
/* Extra variables for reading. */
@@ -293,20 +390,170 @@ struct tomoyo_io_buffer {
int writebuf_size;
};
+/*
+ * tomoyo_globally_readable_file_entry is a structure which is used for holding
+ * "allow_read" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_globally_readable_list .
+ * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_globally_readable_file_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *filename;
+ bool is_deleted;
+};
+
+/*
+ * tomoyo_pattern_entry is a structure which is used for holding
+ * "tomoyo_pattern_list" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_pattern_list .
+ * (2) "pattern" is a pathname pattern which is used for converting pathnames
+ * to pathname patterns during learning mode.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_pattern_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *pattern;
+ bool is_deleted;
+};
+
+/*
+ * tomoyo_no_rewrite_entry is a structure which is used for holding
+ * "deny_rewrite" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_no_rewrite_list .
+ * (2) "pattern" is a pathname which is by default not permitted to modify
+ * already existing content.
+ * (3) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_no_rewrite_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *pattern;
+ bool is_deleted;
+};
+
+/*
+ * tomoyo_domain_initializer_entry is a structure which is used for holding
+ * "initialize_domain" and "no_initialize_domain" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_initializer_list .
+ * (2) "domainname" which is "a domainname" or "the last component of a
+ * domainname". This field is NULL if "from" clause is not specified.
+ * (3) "program" which is a program's pathname.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ * otherwise.
+ * (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_initializer_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *domainname; /* This may be NULL */
+ const struct tomoyo_path_info *program;
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_initialize_domain". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+};
+
+/*
+ * tomoyo_domain_keeper_entry is a structure which is used for holding
+ * "keep_domain" and "no_keep_domain" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_domain_keeper_list .
+ * (2) "domainname" which is "a domainname" or "the last component of a
+ * domainname".
+ * (3) "program" which is a program's pathname.
+ * This field is NULL if "from" clause is not specified.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ * (5) "is_not" is a bool which is true if "no_initialize_domain", false
+ * otherwise.
+ * (6) "is_last_name" is a bool which is true if "domainname" is "the last
+ * component of a domainname", false otherwise.
+ */
+struct tomoyo_domain_keeper_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *domainname;
+ const struct tomoyo_path_info *program; /* This may be NULL */
+ bool is_deleted;
+ bool is_not; /* True if this entry is "no_keep_domain". */
+ /* True if the domainname is tomoyo_get_last_name(). */
+ bool is_last_name;
+};
+
+/*
+ * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_alias_list .
+ * (2) "original_name" which is a dereferenced pathname.
+ * (3) "aliased_name" which is a symlink's pathname.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_alias_entry {
+ struct list_head list;
+ const struct tomoyo_path_info *original_name;
+ const struct tomoyo_path_info *aliased_name;
+ bool is_deleted;
+};
+
+/*
+ * tomoyo_policy_manager_entry is a structure which is used for holding list of
+ * domainnames or programs which are permitted to modify configuration via
+ * /sys/kernel/security/tomoyo/ interface.
+ * It has following fields.
+ *
+ * (1) "list" which is linked to tomoyo_policy_manager_list .
+ * (2) "manager" is a domainname or a program's pathname.
+ * (3) "is_domain" is a bool which is true if "manager" is a domainname, false
+ * otherwise.
+ * (4) "is_deleted" is a bool which is true if marked as deleted, false
+ * otherwise.
+ */
+struct tomoyo_policy_manager_entry {
+ struct list_head list;
+ /* A path to program or a domainname. */
+ const struct tomoyo_path_info *manager;
+ bool is_domain; /* True if manager is a domainname. */
+ bool is_deleted; /* True if this entry is deleted. */
+};
+
+/********** Function prototypes. **********/
+
+/* Check whether the given name matches the given name_union. */
+bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
+ const struct tomoyo_name_union *ptr);
/* Check whether the domain has too many ACL entries to hold. */
bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain);
/* Transactional sprintf() for policy dump. */
bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
/* Check whether the domainname is correct. */
-bool tomoyo_is_correct_domain(const unsigned char *domainname,
- const char *function);
+bool tomoyo_is_correct_domain(const unsigned char *domainname);
/* Check whether the token is correct. */
bool tomoyo_is_correct_path(const char *filename, const s8 start_type,
- const s8 pattern_type, const s8 end_type,
- const char *function);
+ const s8 pattern_type, const s8 end_type);
/* Check whether the token can be a domainname. */
bool tomoyo_is_domain_def(const unsigned char *buffer);
+bool tomoyo_parse_name_union(const char *filename,
+ struct tomoyo_name_union *ptr);
+/* Check whether the given filename matches the given path_group. */
+bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
+ const struct tomoyo_path_group *group,
+ const bool may_use_pattern);
/* Check whether the given filename matches the given pattern. */
bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
const struct tomoyo_path_info *pattern);
@@ -321,20 +568,24 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head);
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head);
/* Read "file_pattern" entry in exception policy. */
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head);
+/* Read "path_group" entry in exception policy. */
+bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head);
/* Read "allow_read" entry in exception policy. */
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head);
/* Read "deny_rewrite" entry in exception policy. */
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head);
+/* Tokenize a line. */
+bool tomoyo_tokenize(char *buffer, char *w[], size_t size);
/* Write domain policy violation warning message to console? */
bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain);
/* Convert double path operation to operation name. */
-const char *tomoyo_dp2keyword(const u8 operation);
+const char *tomoyo_path22keyword(const u8 operation);
/* Get the last component of the given domainname. */
const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
/* Get warning message. */
const char *tomoyo_get_msg(const bool is_enforce);
/* Convert single path operation to operation name. */
-const char *tomoyo_sp2keyword(const u8 operation);
+const char *tomoyo_path2keyword(const u8 operation);
/* Create "alias" entry in exception policy. */
int tomoyo_write_alias_policy(char *data, const bool is_delete);
/*
@@ -361,42 +612,126 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete);
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete);
/* Create "file_pattern" entry in exception policy. */
int tomoyo_write_pattern_policy(char *data, const bool is_delete);
+/* Create "path_group" entry in exception policy. */
+int tomoyo_write_path_group_policy(char *data, const bool is_delete);
/* Find a domain by the given name. */
struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname);
/* Find or create a domain by the given name. */
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
domainname,
const u8 profile);
+
+/* Allocate memory for "struct tomoyo_path_group". */
+struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name);
+
/* Check mode for specified functionality. */
unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
const u8 index);
-/* Allocate memory for structures. */
-void *tomoyo_alloc_acl_element(const u8 acl_type);
/* Fill in "struct tomoyo_path_info" members. */
void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
/* Run policy loader when /sbin/init starts. */
void tomoyo_load_policy(const char *filename);
-/* Change "struct tomoyo_domain_info"->flags. */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
- const bool is_delete, const u8 flags);
-/* strcmp() for "struct tomoyo_path_info" structure. */
-static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
- const struct tomoyo_path_info *b)
+/* Convert binary string to ascii string. */
+int tomoyo_encode(char *buffer, int buflen, const char *str);
+
+/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
+int tomoyo_realpath_from_path2(struct path *path, char *newname,
+ int newname_len);
+
+/*
+ * Returns realpath(3) of the given pathname but ignores chroot'ed root.
+ * These functions use kzalloc(), so the caller must call kfree()
+ * if these functions didn't return NULL.
+ */
+char *tomoyo_realpath(const char *pathname);
+/*
+ * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
+ */
+char *tomoyo_realpath_nofollow(const char *pathname);
+/* Same with tomoyo_realpath() except that the pathname is already solved. */
+char *tomoyo_realpath_from_path(struct path *path);
+
+/* Check memory quota. */
+bool tomoyo_memory_ok(void *ptr);
+void *tomoyo_commit_ok(void *data, const unsigned int size);
+
+/*
+ * Keep the given name on the RAM.
+ * The RAM is shared, so NEVER try to modify or kfree() the returned name.
+ */
+const struct tomoyo_path_info *tomoyo_get_name(const char *name);
+
+/* Check for memory usage. */
+int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
+
+/* Set memory quota. */
+int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
+
+/* Initialize realpath related code. */
+void __init tomoyo_realpath_init(void);
+int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+ const struct tomoyo_path_info *filename);
+int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
+ struct path *path, const int flag);
+int tomoyo_path_perm(const u8 operation, struct path *path);
+int tomoyo_path2_perm(const u8 operation, struct path *path1,
+ struct path *path2);
+int tomoyo_check_rewrite_permission(struct file *filp);
+int tomoyo_find_next_domain(struct linux_binprm *bprm);
+
+/* Drop refcount on tomoyo_name_union. */
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
+
+/* Run garbage collector. */
+void tomoyo_run_gc(void);
+
+void tomoyo_memory_free(void *ptr);
+
+/********** External variable definitions. **********/
+
+/* Lock for GC. */
+extern struct srcu_struct tomoyo_ss;
+
+/* The list for "struct tomoyo_domain_info". */
+extern struct list_head tomoyo_domain_list;
+
+extern struct list_head tomoyo_path_group_list;
+extern struct list_head tomoyo_domain_initializer_list;
+extern struct list_head tomoyo_domain_keeper_list;
+extern struct list_head tomoyo_alias_list;
+extern struct list_head tomoyo_globally_readable_list;
+extern struct list_head tomoyo_pattern_list;
+extern struct list_head tomoyo_no_rewrite_list;
+extern struct list_head tomoyo_policy_manager_list;
+extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+
+/* Lock for protecting policy. */
+extern struct mutex tomoyo_policy_lock;
+
+/* Has /sbin/init started? */
+extern bool tomoyo_policy_loaded;
+
+/* The kernel's domain. */
+extern struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/********** Inlined functions. **********/
+
+static inline int tomoyo_read_lock(void)
{
- return a->hash != b->hash || strcmp(a->name, b->name);
+ return srcu_read_lock(&tomoyo_ss);
}
-/* Get type of an ACL entry. */
-static inline u8 tomoyo_acl_type1(struct tomoyo_acl_info *ptr)
+static inline void tomoyo_read_unlock(int idx)
{
- return ptr->type & ~TOMOYO_ACL_DELETED;
+ srcu_read_unlock(&tomoyo_ss, idx);
}
-/* Get type of an ACL entry. */
-static inline u8 tomoyo_acl_type2(struct tomoyo_acl_info *ptr)
+/* strcmp() for "struct tomoyo_path_info" structure. */
+static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a,
+ const struct tomoyo_path_info *b)
{
- return ptr->type;
+ return a->hash != b->hash || strcmp(a->name, b->name);
}
/**
@@ -423,18 +758,84 @@ static inline bool tomoyo_is_invalid(const unsigned char c)
return c && (c <= ' ' || c >= 127);
}
-/* The list for "struct tomoyo_domain_info". */
-extern struct list_head tomoyo_domain_list;
-extern struct rw_semaphore tomoyo_domain_list_lock;
+static inline void tomoyo_put_name(const struct tomoyo_path_info *name)
+{
+ if (name) {
+ struct tomoyo_name_entry *ptr =
+ container_of(name, struct tomoyo_name_entry, entry);
+ atomic_dec(&ptr->users);
+ }
+}
-/* Lock for domain->acl_info_list. */
-extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+static inline void tomoyo_put_path_group(struct tomoyo_path_group *group)
+{
+ if (group)
+ atomic_dec(&group->users);
+}
-/* Has /sbin/init started? */
-extern bool tomoyo_policy_loaded;
+static inline struct tomoyo_domain_info *tomoyo_domain(void)
+{
+ return current_cred()->security;
+}
-/* The kernel's domain. */
-extern struct tomoyo_domain_info tomoyo_kernel_domain;
+static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
+ *task)
+{
+ return task_cred_xxx(task, security);
+}
+
+static inline bool tomoyo_is_same_acl_head(const struct tomoyo_acl_info *p1,
+ const struct tomoyo_acl_info *p2)
+{
+ return p1->type == p2->type;
+}
+
+static inline bool tomoyo_is_same_name_union
+(const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2)
+{
+ return p1->filename == p2->filename && p1->group == p2->group &&
+ p1->is_group == p2->is_group;
+}
+
+static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1,
+ const struct tomoyo_path_acl *p2)
+{
+ return tomoyo_is_same_acl_head(&p1->head, &p2->head) &&
+ tomoyo_is_same_name_union(&p1->name, &p2->name);
+}
+
+static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1,
+ const struct tomoyo_path2_acl *p2)
+{
+ return tomoyo_is_same_acl_head(&p1->head, &p2->head) &&
+ tomoyo_is_same_name_union(&p1->name1, &p2->name1) &&
+ tomoyo_is_same_name_union(&p1->name2, &p2->name2);
+}
+
+static inline bool tomoyo_is_same_domain_initializer_entry
+(const struct tomoyo_domain_initializer_entry *p1,
+ const struct tomoyo_domain_initializer_entry *p2)
+{
+ return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name
+ && p1->domainname == p2->domainname
+ && p1->program == p2->program;
+}
+
+static inline bool tomoyo_is_same_domain_keeper_entry
+(const struct tomoyo_domain_keeper_entry *p1,
+ const struct tomoyo_domain_keeper_entry *p2)
+{
+ return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name
+ && p1->domainname == p2->domainname
+ && p1->program == p2->program;
+}
+
+static inline bool tomoyo_is_same_alias_entry
+(const struct tomoyo_alias_entry *p1, const struct tomoyo_alias_entry *p2)
+{
+ return p1->original_name == p2->original_name &&
+ p1->aliased_name == p2->aliased_name;
+}
/**
* list_for_each_cookie - iterate over a list with cookie.
@@ -442,16 +843,16 @@ extern struct tomoyo_domain_info tomoyo_kernel_domain;
* @cookie: the &struct list_head to use as a cookie.
* @head: the head for your list.
*
- * Same with list_for_each() except that this primitive uses @cookie
+ * Same with list_for_each_rcu() except that this primitive uses @cookie
* so that we can continue iteration.
* @cookie must be NULL when iteration starts, and @cookie will become
* NULL when iteration finishes.
*/
-#define list_for_each_cookie(pos, cookie, head) \
- for (({ if (!cookie) \
- cookie = head; }), \
- pos = (cookie)->next; \
- prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
- (cookie) = pos, pos = pos->next)
+#define list_for_each_cookie(pos, cookie, head) \
+ for (({ if (!cookie) \
+ cookie = head; }), \
+ pos = rcu_dereference((cookie)->next); \
+ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \
+ (cookie) = pos, pos = rcu_dereference(pos->next))
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index fcf52accce2b..cd8ba4446763 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -10,9 +10,8 @@
*/
#include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
#include <linux/binfmts.h>
+#include <linux/slab.h>
/* Variables definitions.*/
@@ -58,99 +57,6 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
* exceptions.
*/
LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
-
-/*
- * tomoyo_domain_initializer_entry is a structure which is used for holding
- * "initialize_domain" and "no_initialize_domain" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_domain_initializer_list .
- * (2) "domainname" which is "a domainname" or "the last component of a
- * domainname". This field is NULL if "from" clause is not specified.
- * (3) "program" which is a program's pathname.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- * (5) "is_not" is a bool which is true if "no_initialize_domain", false
- * otherwise.
- * (6) "is_last_name" is a bool which is true if "domainname" is "the last
- * component of a domainname", false otherwise.
- */
-struct tomoyo_domain_initializer_entry {
- struct list_head list;
- const struct tomoyo_path_info *domainname; /* This may be NULL */
- const struct tomoyo_path_info *program;
- bool is_deleted;
- bool is_not; /* True if this entry is "no_initialize_domain". */
- /* True if the domainname is tomoyo_get_last_name(). */
- bool is_last_name;
-};
-
-/*
- * tomoyo_domain_keeper_entry is a structure which is used for holding
- * "keep_domain" and "no_keep_domain" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_domain_keeper_list .
- * (2) "domainname" which is "a domainname" or "the last component of a
- * domainname".
- * (3) "program" which is a program's pathname.
- * This field is NULL if "from" clause is not specified.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- * (5) "is_not" is a bool which is true if "no_initialize_domain", false
- * otherwise.
- * (6) "is_last_name" is a bool which is true if "domainname" is "the last
- * component of a domainname", false otherwise.
- */
-struct tomoyo_domain_keeper_entry {
- struct list_head list;
- const struct tomoyo_path_info *domainname;
- const struct tomoyo_path_info *program; /* This may be NULL */
- bool is_deleted;
- bool is_not; /* True if this entry is "no_keep_domain". */
- /* True if the domainname is tomoyo_get_last_name(). */
- bool is_last_name;
-};
-
-/*
- * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_alias_list .
- * (2) "original_name" which is a dereferenced pathname.
- * (3) "aliased_name" which is a symlink's pathname.
- * (4) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_alias_entry {
- struct list_head list;
- const struct tomoyo_path_info *original_name;
- const struct tomoyo_path_info *aliased_name;
- bool is_deleted;
-};
-
-/**
- * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
- *
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- * @flags: Flags to set or clear.
- *
- * Returns nothing.
- */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
- const bool is_delete, const u8 flags)
-{
- /* We need to serialize because this is bitfield operation. */
- static DEFINE_SPINLOCK(lock);
- spin_lock(&lock);
- if (!is_delete)
- domain->flags |= flags;
- else
- domain->flags &= ~flags;
- spin_unlock(&lock);
-}
/**
* tomoyo_get_last_name - Get last component of a domainname.
@@ -205,8 +111,7 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
* will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
* unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
*/
-static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+LIST_HEAD(tomoyo_domain_initializer_list);
/**
* tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -217,59 +122,55 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_initializer_entry(const char *domainname,
const char *program,
const bool is_not,
const bool is_delete)
{
- struct tomoyo_domain_initializer_entry *new_entry;
struct tomoyo_domain_initializer_entry *ptr;
- const struct tomoyo_path_info *saved_program;
- const struct tomoyo_path_info *saved_domainname = NULL;
- int error = -ENOMEM;
- bool is_last_name = false;
+ struct tomoyo_domain_initializer_entry e = { .is_not = is_not };
+ int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+ if (!tomoyo_is_correct_path(program, 1, -1, -1))
return -EINVAL; /* No patterns allowed. */
if (domainname) {
if (!tomoyo_is_domain_def(domainname) &&
- tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
- is_last_name = true;
- else if (!tomoyo_is_correct_domain(domainname, __func__))
+ tomoyo_is_correct_path(domainname, 1, -1, -1))
+ e.is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname))
return -EINVAL;
- saved_domainname = tomoyo_save_name(domainname);
- if (!saved_domainname)
- return -ENOMEM;
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
}
- saved_program = tomoyo_save_name(program);
- if (!saved_program)
- return -ENOMEM;
- down_write(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
- if (ptr->is_not != is_not ||
- ptr->domainname != saved_domainname ||
- ptr->program != saved_program)
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
+ if (!tomoyo_is_same_domain_initializer_entry(ptr, &e))
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_domain_initializer_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_domain_initializer_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->domainname = saved_domainname;
- new_entry->program = saved_program;
- new_entry->is_not = is_not;
- new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_domain_initializer_list_lock);
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
return error;
}
@@ -279,13 +180,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_initializer_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_initializer_list) {
const char *no;
@@ -308,7 +210,6 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
@@ -320,6 +221,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
const bool is_delete)
@@ -345,6 +248,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
*
* Returns true if executing @program reinitializes domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
domainname,
@@ -355,8 +260,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
struct tomoyo_domain_initializer_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_initializer_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_deleted)
continue;
if (ptr->domainname) {
@@ -376,7 +280,6 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
@@ -418,8 +321,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
* "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
* explicitly specified by "initialize_domain".
*/
-static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+LIST_HEAD(tomoyo_domain_keeper_list);
/**
* tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -430,59 +332,55 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_domain_keeper_entry(const char *domainname,
const char *program,
const bool is_not,
const bool is_delete)
{
- struct tomoyo_domain_keeper_entry *new_entry;
struct tomoyo_domain_keeper_entry *ptr;
- const struct tomoyo_path_info *saved_domainname;
- const struct tomoyo_path_info *saved_program = NULL;
- int error = -ENOMEM;
- bool is_last_name = false;
+ struct tomoyo_domain_keeper_entry e = { .is_not = is_not };
+ int error = is_delete ? -ENOENT : -ENOMEM;
if (!tomoyo_is_domain_def(domainname) &&
- tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
- is_last_name = true;
- else if (!tomoyo_is_correct_domain(domainname, __func__))
+ tomoyo_is_correct_path(domainname, 1, -1, -1))
+ e.is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname))
return -EINVAL;
if (program) {
- if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+ if (!tomoyo_is_correct_path(program, 1, -1, -1))
return -EINVAL;
- saved_program = tomoyo_save_name(program);
- if (!saved_program)
- return -ENOMEM;
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
}
- saved_domainname = tomoyo_save_name(domainname);
- if (!saved_domainname)
- return -ENOMEM;
- down_write(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
- if (ptr->is_not != is_not ||
- ptr->domainname != saved_domainname ||
- ptr->program != saved_program)
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+ if (!tomoyo_is_same_domain_keeper_entry(ptr, &e))
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_domain_keeper_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_domain_keeper_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->domainname = saved_domainname;
- new_entry->program = saved_program;
- new_entry->is_not = is_not;
- new_entry->is_last_name = is_last_name;
- list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_domain_keeper_list_lock);
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
return error;
}
@@ -493,6 +391,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
* @is_not: True if it is "no_keep_domain" entry.
* @is_delete: True if it is a delete request.
*
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
const bool is_delete)
@@ -513,13 +412,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_domain_keeper_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_domain_keeper_list) {
struct tomoyo_domain_keeper_entry *ptr;
@@ -542,7 +442,6 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
@@ -555,6 +454,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
*
* Returns true if executing @program supresses domain transition,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
const struct tomoyo_path_info *program,
@@ -563,8 +464,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
struct tomoyo_domain_keeper_entry *ptr;
bool flag = false;
- down_read(&tomoyo_domain_keeper_list_lock);
- list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_deleted)
continue;
if (!ptr->is_last_name) {
@@ -582,7 +482,6 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
@@ -616,8 +515,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
* /bin/busybox and domainname which the current process will belong to after
* execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
*/
-static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
+LIST_HEAD(tomoyo_alias_list);
/**
* tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -627,46 +525,45 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_alias_entry(const char *original_name,
const char *aliased_name,
const bool is_delete)
{
- struct tomoyo_alias_entry *new_entry;
struct tomoyo_alias_entry *ptr;
- const struct tomoyo_path_info *saved_original_name;
- const struct tomoyo_path_info *saved_aliased_name;
- int error = -ENOMEM;
+ struct tomoyo_alias_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
- !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+ if (!tomoyo_is_correct_path(original_name, 1, -1, -1) ||
+ !tomoyo_is_correct_path(aliased_name, 1, -1, -1))
return -EINVAL; /* No patterns allowed. */
- saved_original_name = tomoyo_save_name(original_name);
- saved_aliased_name = tomoyo_save_name(aliased_name);
- if (!saved_original_name || !saved_aliased_name)
- return -ENOMEM;
- down_write(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
- if (ptr->original_name != saved_original_name ||
- ptr->aliased_name != saved_aliased_name)
+ e.original_name = tomoyo_get_name(original_name);
+ e.aliased_name = tomoyo_get_name(aliased_name);
+ if (!e.original_name || !e.aliased_name)
+ goto out;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
+ if (!tomoyo_is_same_alias_entry(ptr, &e))
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_alias_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list, &tomoyo_alias_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->original_name = saved_original_name;
- new_entry->aliased_name = saved_aliased_name;
- list_add_tail(&new_entry->list, &tomoyo_alias_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_alias_list_lock);
+ tomoyo_put_name(e.original_name);
+ tomoyo_put_name(e.aliased_name);
return error;
}
@@ -676,13 +573,14 @@ static int tomoyo_update_alias_entry(const char *original_name,
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_alias_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
struct tomoyo_alias_entry *ptr;
@@ -695,7 +593,6 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
@@ -706,6 +603,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_alias_policy(char *data, const bool is_delete)
{
@@ -724,63 +623,48 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
* @profile: Profile number to assign if the domain was newly created.
*
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
domainname,
const u8 profile)
{
+ struct tomoyo_domain_info *entry;
struct tomoyo_domain_info *domain = NULL;
const struct tomoyo_path_info *saved_domainname;
+ bool found = false;
- down_write(&tomoyo_domain_list_lock);
- domain = tomoyo_find_domain(domainname);
- if (domain)
- goto out;
- if (!tomoyo_is_correct_domain(domainname, __func__))
- goto out;
- saved_domainname = tomoyo_save_name(domainname);
+ if (!tomoyo_is_correct_domain(domainname))
+ return NULL;
+ saved_domainname = tomoyo_get_name(domainname);
if (!saved_domainname)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- /* Can I reuse memory of deleted domain? */
- list_for_each_entry(domain, &tomoyo_domain_list, list) {
- struct task_struct *p;
- struct tomoyo_acl_info *ptr;
- bool flag;
- if (!domain->is_deleted ||
- domain->domainname != saved_domainname)
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ if (domain->is_deleted ||
+ tomoyo_pathcmp(saved_domainname, domain->domainname))
continue;
- flag = false;
- read_lock(&tasklist_lock);
- for_each_process(p) {
- if (tomoyo_real_domain(p) != domain)
- continue;
- flag = true;
- break;
- }
- read_unlock(&tasklist_lock);
- if (flag)
- continue;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- ptr->type |= TOMOYO_ACL_DELETED;
- }
- tomoyo_set_domain_flag(domain, true, domain->flags);
- domain->profile = profile;
- domain->quota_warned = false;
- mb(); /* Avoid out-of-order execution. */
- domain->is_deleted = false;
- goto out;
+ found = true;
+ break;
}
- /* No memory reusable. Create using new memory. */
- domain = tomoyo_alloc_element(sizeof(*domain));
- if (domain) {
- INIT_LIST_HEAD(&domain->acl_info_list);
- domain->domainname = saved_domainname;
- domain->profile = profile;
- list_add_tail(&domain->list, &tomoyo_domain_list);
+ if (!found && tomoyo_memory_ok(entry)) {
+ INIT_LIST_HEAD(&entry->acl_info_list);
+ entry->domainname = saved_domainname;
+ saved_domainname = NULL;
+ entry->profile = profile;
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+ domain = entry;
+ entry = NULL;
+ found = true;
}
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_domain_list_lock);
- return domain;
+ tomoyo_put_name(saved_domainname);
+ kfree(entry);
+ return found ? domain : NULL;
}
/**
@@ -789,6 +673,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
* @bprm: Pointer to "struct linux_binprm".
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_find_next_domain(struct linux_binprm *bprm)
{
@@ -796,7 +682,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
* This function assumes that the size of buffer returned by
* tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
*/
- struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+ struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS);
struct tomoyo_domain_info *old_domain = tomoyo_domain();
struct tomoyo_domain_info *domain = NULL;
const char *old_domain_name = old_domain->domainname->name;
@@ -849,8 +735,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
if (tomoyo_pathcmp(&r, &s)) {
struct tomoyo_alias_entry *ptr;
/* Is this program allowed to be called via symbolic links? */
- down_read(&tomoyo_alias_list_lock);
- list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->is_deleted ||
tomoyo_pathcmp(&r, ptr->original_name) ||
tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -861,7 +746,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
@@ -892,9 +776,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
}
if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
goto done;
- down_read(&tomoyo_domain_list_lock);
domain = tomoyo_find_domain(new_domain_name);
- up_read(&tomoyo_domain_list_lock);
if (domain)
goto done;
if (is_enforce)
@@ -909,14 +791,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
if (is_enforce)
retval = -EPERM;
else
- tomoyo_set_domain_flag(old_domain, false,
- TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+ old_domain->transition_failed = true;
out:
if (!domain)
domain = old_domain;
+ /* Update reference count on "struct tomoyo_domain_info". */
+ atomic_inc(&domain->users);
bprm->cred->security = domain;
- tomoyo_free(real_program_name);
- tomoyo_free(symlink_program_name);
- tomoyo_free(tmp);
+ kfree(real_program_name);
+ kfree(symlink_program_name);
+ kfree(tmp);
return retval;
}
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index 9a6c58881c0a..1c6f8238ec47 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -10,108 +10,96 @@
*/
#include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
+#include <linux/slab.h>
-/*
- * tomoyo_globally_readable_file_entry is a structure which is used for holding
- * "allow_read" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_globally_readable_list .
- * (2) "filename" is a pathname which is allowed to open(O_RDONLY).
- * (3) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_globally_readable_file_entry {
- struct list_head list;
- const struct tomoyo_path_info *filename;
- bool is_deleted;
+/* Keyword array for single path operations. */
+static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
+ [TOMOYO_TYPE_READ_WRITE] = "read/write",
+ [TOMOYO_TYPE_EXECUTE] = "execute",
+ [TOMOYO_TYPE_READ] = "read",
+ [TOMOYO_TYPE_WRITE] = "write",
+ [TOMOYO_TYPE_CREATE] = "create",
+ [TOMOYO_TYPE_UNLINK] = "unlink",
+ [TOMOYO_TYPE_MKDIR] = "mkdir",
+ [TOMOYO_TYPE_RMDIR] = "rmdir",
+ [TOMOYO_TYPE_MKFIFO] = "mkfifo",
+ [TOMOYO_TYPE_MKSOCK] = "mksock",
+ [TOMOYO_TYPE_MKBLOCK] = "mkblock",
+ [TOMOYO_TYPE_MKCHAR] = "mkchar",
+ [TOMOYO_TYPE_TRUNCATE] = "truncate",
+ [TOMOYO_TYPE_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_REWRITE] = "rewrite",
+ [TOMOYO_TYPE_IOCTL] = "ioctl",
+ [TOMOYO_TYPE_CHMOD] = "chmod",
+ [TOMOYO_TYPE_CHOWN] = "chown",
+ [TOMOYO_TYPE_CHGRP] = "chgrp",
+ [TOMOYO_TYPE_CHROOT] = "chroot",
+ [TOMOYO_TYPE_MOUNT] = "mount",
+ [TOMOYO_TYPE_UMOUNT] = "unmount",
};
-/*
- * tomoyo_pattern_entry is a structure which is used for holding
- * "tomoyo_pattern_list" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_pattern_list .
- * (2) "pattern" is a pathname pattern which is used for converting pathnames
- * to pathname patterns during learning mode.
- * (3) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_pattern_entry {
- struct list_head list;
- const struct tomoyo_path_info *pattern;
- bool is_deleted;
+/* Keyword array for double path operations. */
+static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = {
+ [TOMOYO_TYPE_LINK] = "link",
+ [TOMOYO_TYPE_RENAME] = "rename",
+ [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root",
};
-/*
- * tomoyo_no_rewrite_entry is a structure which is used for holding
- * "deny_rewrite" entries.
- * It has following fields.
- *
- * (1) "list" which is linked to tomoyo_no_rewrite_list .
- * (2) "pattern" is a pathname which is by default not permitted to modify
- * already existing content.
- * (3) "is_deleted" is a bool which is true if marked as deleted, false
- * otherwise.
- */
-struct tomoyo_no_rewrite_entry {
- struct list_head list;
- const struct tomoyo_path_info *pattern;
- bool is_deleted;
-};
+void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
+{
+ if (!ptr)
+ return;
+ if (ptr->is_group)
+ tomoyo_put_path_group(ptr->group);
+ else
+ tomoyo_put_name(ptr->filename);
+}
-/* Keyword array for single path operations. */
-static const char *tomoyo_sp_keyword[TOMOYO_MAX_SINGLE_PATH_OPERATION] = {
- [TOMOYO_TYPE_READ_WRITE_ACL] = "read/write",
- [TOMOYO_TYPE_EXECUTE_ACL] = "execute",
- [TOMOYO_TYPE_READ_ACL] = "read",
- [TOMOYO_TYPE_WRITE_ACL] = "write",
- [TOMOYO_TYPE_CREATE_ACL] = "create",
- [TOMOYO_TYPE_UNLINK_ACL] = "unlink",
- [TOMOYO_TYPE_MKDIR_ACL] = "mkdir",
- [TOMOYO_TYPE_RMDIR_ACL] = "rmdir",
- [TOMOYO_TYPE_MKFIFO_ACL] = "mkfifo",
- [TOMOYO_TYPE_MKSOCK_ACL] = "mksock",
- [TOMOYO_TYPE_MKBLOCK_ACL] = "mkblock",
- [TOMOYO_TYPE_MKCHAR_ACL] = "mkchar",
- [TOMOYO_TYPE_TRUNCATE_ACL] = "truncate",
- [TOMOYO_TYPE_SYMLINK_ACL] = "symlink",
- [TOMOYO_TYPE_REWRITE_ACL] = "rewrite",
-};
+bool tomoyo_compare_name_union(const struct tomoyo_path_info *name,
+ const struct tomoyo_name_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_path_matches_group(name, ptr->group, 1);
+ return tomoyo_path_matches_pattern(name, ptr->filename);
+}
-/* Keyword array for double path operations. */
-static const char *tomoyo_dp_keyword[TOMOYO_MAX_DOUBLE_PATH_OPERATION] = {
- [TOMOYO_TYPE_LINK_ACL] = "link",
- [TOMOYO_TYPE_RENAME_ACL] = "rename",
-};
+static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info
+ *name,
+ const struct tomoyo_name_union
+ *ptr, const bool may_use_pattern)
+{
+ if (ptr->is_group)
+ return tomoyo_path_matches_group(name, ptr->group,
+ may_use_pattern);
+ if (may_use_pattern || !ptr->filename->is_patterned)
+ return tomoyo_path_matches_pattern(name, ptr->filename);
+ return false;
+}
/**
- * tomoyo_sp2keyword - Get the name of single path operation.
+ * tomoyo_path2keyword - Get the name of single path operation.
*
* @operation: Type of operation.
*
* Returns the name of single path operation.
*/
-const char *tomoyo_sp2keyword(const u8 operation)
+const char *tomoyo_path2keyword(const u8 operation)
{
- return (operation < TOMOYO_MAX_SINGLE_PATH_OPERATION)
- ? tomoyo_sp_keyword[operation] : NULL;
+ return (operation < TOMOYO_MAX_PATH_OPERATION)
+ ? tomoyo_path_keyword[operation] : NULL;
}
/**
- * tomoyo_dp2keyword - Get the name of double path operation.
+ * tomoyo_path22keyword - Get the name of double path operation.
*
* @operation: Type of operation.
*
* Returns the name of double path operation.
*/
-const char *tomoyo_dp2keyword(const u8 operation)
+const char *tomoyo_path22keyword(const u8 operation)
{
- return (operation < TOMOYO_MAX_DOUBLE_PATH_OPERATION)
- ? tomoyo_dp_keyword[operation] : NULL;
+ return (operation < TOMOYO_MAX_PATH2_OPERATION)
+ ? tomoyo_path2_keyword[operation] : NULL;
}
/**
@@ -142,7 +130,8 @@ static bool tomoyo_strendswith(const char *name, const char *tail)
static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
{
int error;
- struct tomoyo_path_info_with_data *buf = tomoyo_alloc(sizeof(*buf));
+ struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf),
+ GFP_NOFS);
if (!buf)
return NULL;
@@ -154,20 +143,17 @@ static struct tomoyo_path_info *tomoyo_get_path(struct path *path)
tomoyo_fill_path_info(&buf->head);
return &buf->head;
}
- tomoyo_free(buf);
+ kfree(buf);
return NULL;
}
-/* Lock for domain->acl_info_list. */
-DECLARE_RWSEM(tomoyo_domain_acl_info_list_lock);
-
-static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
- const char *filename2,
- struct tomoyo_domain_info *
- const domain, const bool is_delete);
-static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
- struct tomoyo_domain_info *
- const domain, const bool is_delete);
+static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
+ const char *filename2,
+ struct tomoyo_domain_info *const domain,
+ const bool is_delete);
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info *const domain,
+ const bool is_delete);
/*
* tomoyo_globally_readable_list is used for holding list of pathnames which
@@ -194,8 +180,7 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
* given "allow_read /lib/libc-2.5.so" to the domain which current process
* belongs to.
*/
-static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
+LIST_HEAD(tomoyo_globally_readable_list);
/**
* tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
@@ -204,40 +189,42 @@ static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_globally_readable_entry(const char *filename,
const bool is_delete)
{
- struct tomoyo_globally_readable_file_entry *new_entry;
struct tomoyo_globally_readable_file_entry *ptr;
- const struct tomoyo_path_info *saved_filename;
- int error = -ENOMEM;
+ struct tomoyo_globally_readable_file_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_is_correct_path(filename, 1, 0, -1, __func__))
+ if (!tomoyo_is_correct_path(filename, 1, 0, -1))
return -EINVAL;
- saved_filename = tomoyo_save_name(filename);
- if (!saved_filename)
+ e.filename = tomoyo_get_name(filename);
+ if (!e.filename)
return -ENOMEM;
- down_write(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
- if (ptr->filename != saved_filename)
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
+ if (ptr->filename != e.filename)
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_globally_readable_file_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_globally_readable_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_globally_readable_list_lock);
+ tomoyo_put_name(e.filename);
return error;
}
@@ -247,21 +234,22 @@ static int tomoyo_update_globally_readable_entry(const char *filename,
* @filename: The filename to check.
*
* Returns true if any domain can open @filename for reading, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
filename)
{
struct tomoyo_globally_readable_file_entry *ptr;
bool found = false;
- down_read(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (!ptr->is_deleted &&
tomoyo_path_matches_pattern(filename, ptr->filename)) {
found = true;
break;
}
}
- up_read(&tomoyo_globally_readable_list_lock);
return found;
}
@@ -272,6 +260,8 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info *
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
{
@@ -284,13 +274,14 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_globally_readable_list_lock);
list_for_each_cookie(pos, head->read_var2,
&tomoyo_globally_readable_list) {
struct tomoyo_globally_readable_file_entry *ptr;
@@ -304,7 +295,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
@@ -337,8 +327,7 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head)
* which pretends as if /proc/self/ is not a symlink; so that we can forbid
* current process from accessing other process's information.
*/
-static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);
+LIST_HEAD(tomoyo_pattern_list);
/**
* tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
@@ -347,40 +336,40 @@ static DECLARE_RWSEM(tomoyo_pattern_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_pattern_entry(const char *pattern,
const bool is_delete)
{
- struct tomoyo_pattern_entry *new_entry;
struct tomoyo_pattern_entry *ptr;
- const struct tomoyo_path_info *saved_pattern;
- int error = -ENOMEM;
+ struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) };
+ int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_is_correct_path(pattern, 0, 1, 0, __func__))
- return -EINVAL;
- saved_pattern = tomoyo_save_name(pattern);
- if (!saved_pattern)
- return -ENOMEM;
- down_write(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
- if (saved_pattern != ptr->pattern)
+ if (!e.pattern)
+ return error;
+ if (!e.pattern->is_patterned)
+ goto out;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+ if (e.pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_pattern_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list, &tomoyo_pattern_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_pattern_list_lock);
+ tomoyo_put_name(e.pattern);
return error;
}
@@ -390,6 +379,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern,
* @filename: The filename to find patterned pathname.
*
* Returns pointer to pathname pattern if matched, @filename otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static const struct tomoyo_path_info *
tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
@@ -397,8 +388,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
struct tomoyo_pattern_entry *ptr;
const struct tomoyo_path_info *pattern = NULL;
- down_read(&tomoyo_pattern_list_lock);
- list_for_each_entry(ptr, &tomoyo_pattern_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -411,7 +401,6 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return filename;
@@ -424,6 +413,8 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_pattern_policy(char *data, const bool is_delete)
{
@@ -436,13 +427,14 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_pattern_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) {
struct tomoyo_pattern_entry *ptr;
ptr = list_entry(pos, struct tomoyo_pattern_entry, list);
@@ -453,7 +445,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
@@ -486,8 +477,7 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head)
* " (deleted)" suffix if the file is already unlink()ed; so that we don't
* need to worry whether the file is already unlink()ed or not.
*/
-static LIST_HEAD(tomoyo_no_rewrite_list);
-static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
+LIST_HEAD(tomoyo_no_rewrite_list);
/**
* tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
@@ -496,39 +486,42 @@ static DECLARE_RWSEM(tomoyo_no_rewrite_list_lock);
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_no_rewrite_entry(const char *pattern,
const bool is_delete)
{
- struct tomoyo_no_rewrite_entry *new_entry, *ptr;
- const struct tomoyo_path_info *saved_pattern;
- int error = -ENOMEM;
+ struct tomoyo_no_rewrite_entry *ptr;
+ struct tomoyo_no_rewrite_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
- if (!tomoyo_is_correct_path(pattern, 0, 0, 0, __func__))
+ if (!tomoyo_is_correct_path(pattern, 0, 0, 0))
return -EINVAL;
- saved_pattern = tomoyo_save_name(pattern);
- if (!saved_pattern)
- return -ENOMEM;
- down_write(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
- if (ptr->pattern != saved_pattern)
+ e.pattern = tomoyo_get_name(pattern);
+ if (!e.pattern)
+ return error;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+ if (ptr->pattern != e.pattern)
continue;
ptr->is_deleted = is_delete;
error = 0;
- goto out;
+ break;
}
- if (is_delete) {
- error = -ENOENT;
- goto out;
+ if (!is_delete && error) {
+ struct tomoyo_no_rewrite_entry *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_no_rewrite_list);
+ error = 0;
+ }
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
- goto out;
- new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
- error = 0;
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_no_rewrite_list_lock);
+ tomoyo_put_name(e.pattern);
return error;
}
@@ -539,14 +532,15 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern,
*
* Returns true if @filename is specified by "deny_rewrite" directive,
* false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
{
struct tomoyo_no_rewrite_entry *ptr;
bool found = false;
- down_read(&tomoyo_no_rewrite_list_lock);
- list_for_each_entry(ptr, &tomoyo_no_rewrite_list, list) {
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->is_deleted)
continue;
if (!tomoyo_path_matches_pattern(filename, ptr->pattern))
@@ -554,7 +548,6 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
@@ -565,6 +558,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename)
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
{
@@ -577,13 +572,14 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete)
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
{
struct list_head *pos;
bool done = true;
- down_read(&tomoyo_no_rewrite_list_lock);
list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) {
struct tomoyo_no_rewrite_entry *ptr;
ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list);
@@ -594,7 +590,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
@@ -612,6 +607,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head)
* Current policy syntax uses "allow_read/write" instead of "6",
* "allow_read" instead of "4", "allow_write" instead of "2",
* "allow_execute" instead of "1".
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_update_file_acl(const char *filename, u8 perm,
struct tomoyo_domain_info * const domain,
@@ -629,19 +626,19 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
*/
return 0;
if (perm & 4)
- tomoyo_update_single_path_acl(TOMOYO_TYPE_READ_ACL, filename,
- domain, is_delete);
+ tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain,
+ is_delete);
if (perm & 2)
- tomoyo_update_single_path_acl(TOMOYO_TYPE_WRITE_ACL, filename,
- domain, is_delete);
+ tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain,
+ is_delete);
if (perm & 1)
- tomoyo_update_single_path_acl(TOMOYO_TYPE_EXECUTE_ACL,
- filename, domain, is_delete);
+ tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain,
+ is_delete);
return 0;
}
/**
- * tomoyo_check_single_path_acl2 - Check permission for single path operation.
+ * tomoyo_path_acl2 - Check permission for single path operation.
*
* @domain: Pointer to "struct tomoyo_domain_info".
* @filename: Filename to check.
@@ -649,37 +646,34 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm,
* @may_use_pattern: True if patterned ACL is permitted.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
- domain,
- const struct tomoyo_path_info *
- filename,
- const u16 perm,
- const bool may_use_pattern)
+static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain,
+ const struct tomoyo_path_info *filename,
+ const u32 perm, const bool may_use_pattern)
{
struct tomoyo_acl_info *ptr;
int error = -EPERM;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- struct tomoyo_single_path_acl_record *acl;
- if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_acl *acl;
+ if (ptr->type != TOMOYO_TYPE_PATH_ACL)
continue;
- acl = container_of(ptr, struct tomoyo_single_path_acl_record,
- head);
- if (!(acl->perm & perm))
- continue;
- if (may_use_pattern || !acl->filename->is_patterned) {
- if (!tomoyo_path_matches_pattern(filename,
- acl->filename))
+ acl = container_of(ptr, struct tomoyo_path_acl, head);
+ if (perm <= 0xFFFF) {
+ if (!(acl->perm & perm))
continue;
} else {
- continue;
+ if (!(acl->perm_high & (perm >> 16)))
+ continue;
}
+ if (!tomoyo_compare_name_union_pattern(filename, &acl->name,
+ may_use_pattern))
+ continue;
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
@@ -691,27 +685,28 @@ static int tomoyo_check_single_path_acl2(const struct tomoyo_domain_info *
* @operation: Mode ("read" or "write" or "read/write" or "execute").
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename,
const u8 operation)
{
- u16 perm = 0;
+ u32 perm = 0;
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
if (operation == 6)
- perm = 1 << TOMOYO_TYPE_READ_WRITE_ACL;
+ perm = 1 << TOMOYO_TYPE_READ_WRITE;
else if (operation == 4)
- perm = 1 << TOMOYO_TYPE_READ_ACL;
+ perm = 1 << TOMOYO_TYPE_READ;
else if (operation == 2)
- perm = 1 << TOMOYO_TYPE_WRITE_ACL;
+ perm = 1 << TOMOYO_TYPE_WRITE;
else if (operation == 1)
- perm = 1 << TOMOYO_TYPE_EXECUTE_ACL;
+ perm = 1 << TOMOYO_TYPE_EXECUTE;
else
BUG();
- return tomoyo_check_single_path_acl2(domain, filename, perm,
- operation != 1);
+ return tomoyo_path_acl2(domain, filename, perm, operation != 1);
}
/**
@@ -724,6 +719,8 @@ static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain,
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
const struct tomoyo_path_info *filename,
@@ -737,18 +734,17 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
if (!filename)
return 0;
error = tomoyo_check_file_acl(domain, filename, perm);
- if (error && perm == 4 &&
- (domain->flags & TOMOYO_DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0
+ if (error && perm == 4 && !domain->ignore_global_allow_read
&& tomoyo_is_globally_readable_file(filename))
error = 0;
if (perm == 6)
- msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_WRITE_ACL);
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE);
else if (perm == 4)
- msg = tomoyo_sp2keyword(TOMOYO_TYPE_READ_ACL);
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_READ);
else if (perm == 2)
- msg = tomoyo_sp2keyword(TOMOYO_TYPE_WRITE_ACL);
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE);
else if (perm == 1)
- msg = tomoyo_sp2keyword(TOMOYO_TYPE_EXECUTE_ACL);
+ msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE);
else
BUG();
if (!error)
@@ -777,6 +773,8 @@ static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
const bool is_delete)
@@ -795,28 +793,28 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
if (strncmp(data, "allow_", 6))
goto out;
data += 6;
- for (type = 0; type < TOMOYO_MAX_SINGLE_PATH_OPERATION; type++) {
- if (strcmp(data, tomoyo_sp_keyword[type]))
+ for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) {
+ if (strcmp(data, tomoyo_path_keyword[type]))
continue;
- return tomoyo_update_single_path_acl(type, filename,
- domain, is_delete);
+ return tomoyo_update_path_acl(type, filename, domain,
+ is_delete);
}
filename2 = strchr(filename, ' ');
if (!filename2)
goto out;
*filename2++ = '\0';
- for (type = 0; type < TOMOYO_MAX_DOUBLE_PATH_OPERATION; type++) {
- if (strcmp(data, tomoyo_dp_keyword[type]))
+ for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) {
+ if (strcmp(data, tomoyo_path2_keyword[type]))
continue;
- return tomoyo_update_double_path_acl(type, filename, filename2,
- domain, is_delete);
+ return tomoyo_update_path2_acl(type, filename, filename2,
+ domain, is_delete);
}
out:
return -EINVAL;
}
/**
- * tomoyo_update_single_path_acl - Update "struct tomoyo_single_path_acl_record" list.
+ * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list.
*
* @type: Type of operation.
* @filename: Filename.
@@ -824,85 +822,76 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
- struct tomoyo_domain_info *
- const domain, const bool is_delete)
+static int tomoyo_update_path_acl(const u8 type, const char *filename,
+ struct tomoyo_domain_info *const domain,
+ const bool is_delete)
{
- static const u16 rw_mask =
- (1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
- const struct tomoyo_path_info *saved_filename;
+ static const u32 tomoyo_rw_mask =
+ (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE);
+ const u32 perm = 1 << type;
struct tomoyo_acl_info *ptr;
- struct tomoyo_single_path_acl_record *acl;
- int error = -ENOMEM;
- const u16 perm = 1 << type;
-
+ struct tomoyo_path_acl e = {
+ .head.type = TOMOYO_TYPE_PATH_ACL,
+ .perm_high = perm >> 16,
+ .perm = perm
+ };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+
+ if (type == TOMOYO_TYPE_READ_WRITE)
+ e.perm |= tomoyo_rw_mask;
if (!domain)
return -EINVAL;
- if (!tomoyo_is_correct_path(filename, 0, 0, 0, __func__))
+ if (!tomoyo_parse_name_union(filename, &e.name))
return -EINVAL;
- saved_filename = tomoyo_save_name(filename);
- if (!saved_filename)
- return -ENOMEM;
- down_write(&tomoyo_domain_acl_info_list_lock);
- if (is_delete)
- goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
- continue;
- acl = container_of(ptr, struct tomoyo_single_path_acl_record,
- head);
- if (acl->filename != saved_filename)
- continue;
- /* Special case. Clear all bits if marked as deleted. */
- if (ptr->type & TOMOYO_ACL_DELETED)
- acl->perm = 0;
- acl->perm |= perm;
- if ((acl->perm & rw_mask) == rw_mask)
- acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE_ACL;
- else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
- acl->perm |= rw_mask;
- ptr->type &= ~TOMOYO_ACL_DELETED;
- error = 0;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- }
- /* Not found. Append it to the tail. */
- acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
- if (!acl)
- goto out;
- acl->perm = perm;
- if (perm == (1 << TOMOYO_TYPE_READ_WRITE_ACL))
- acl->perm |= rw_mask;
- acl->filename = saved_filename;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
- error = 0;
- goto out;
- delete:
- error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
- continue;
- acl = container_of(ptr, struct tomoyo_single_path_acl_record,
- head);
- if (acl->filename != saved_filename)
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path_acl *acl =
+ container_of(ptr, struct tomoyo_path_acl, head);
+ if (!tomoyo_is_same_path_acl(acl, &e))
continue;
- acl->perm &= ~perm;
- if ((acl->perm & rw_mask) != rw_mask)
- acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE_ACL);
- else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL)))
- acl->perm &= ~rw_mask;
- if (!acl->perm)
- ptr->type |= TOMOYO_ACL_DELETED;
+ if (is_delete) {
+ if (perm <= 0xFFFF)
+ acl->perm &= ~perm;
+ else
+ acl->perm_high &= ~(perm >> 16);
+ if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask)
+ acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE);
+ else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)))
+ acl->perm &= ~tomoyo_rw_mask;
+ } else {
+ if (perm <= 0xFFFF)
+ acl->perm |= perm;
+ else
+ acl->perm_high |= (perm >> 16);
+ if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask)
+ acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE;
+ else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))
+ acl->perm |= tomoyo_rw_mask;
+ }
error = 0;
break;
}
+ if (!is_delete && error) {
+ struct tomoyo_path_acl *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->head.list,
+ &domain->acl_info_list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ tomoyo_put_name_union(&e.name);
return error;
}
/**
- * tomoyo_update_double_path_acl - Update "struct tomoyo_double_path_acl_record" list.
+ * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list.
*
* @type: Type of operation.
* @filename1: First filename.
@@ -911,98 +900,78 @@ static int tomoyo_update_single_path_acl(const u8 type, const char *filename,
* @is_delete: True if it is a delete request.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-static int tomoyo_update_double_path_acl(const u8 type, const char *filename1,
- const char *filename2,
- struct tomoyo_domain_info *
- const domain, const bool is_delete)
+static int tomoyo_update_path2_acl(const u8 type, const char *filename1,
+ const char *filename2,
+ struct tomoyo_domain_info *const domain,
+ const bool is_delete)
{
- const struct tomoyo_path_info *saved_filename1;
- const struct tomoyo_path_info *saved_filename2;
- struct tomoyo_acl_info *ptr;
- struct tomoyo_double_path_acl_record *acl;
- int error = -ENOMEM;
const u8 perm = 1 << type;
+ struct tomoyo_path2_acl e = {
+ .head.type = TOMOYO_TYPE_PATH2_ACL,
+ .perm = perm
+ };
+ struct tomoyo_acl_info *ptr;
+ int error = is_delete ? -ENOENT : -ENOMEM;
if (!domain)
return -EINVAL;
- if (!tomoyo_is_correct_path(filename1, 0, 0, 0, __func__) ||
- !tomoyo_is_correct_path(filename2, 0, 0, 0, __func__))
- return -EINVAL;
- saved_filename1 = tomoyo_save_name(filename1);
- saved_filename2 = tomoyo_save_name(filename2);
- if (!saved_filename1 || !saved_filename2)
- return -ENOMEM;
- down_write(&tomoyo_domain_acl_info_list_lock);
- if (is_delete)
- goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- if (tomoyo_acl_type1(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
- continue;
- acl = container_of(ptr, struct tomoyo_double_path_acl_record,
- head);
- if (acl->filename1 != saved_filename1 ||
- acl->filename2 != saved_filename2)
- continue;
- /* Special case. Clear all bits if marked as deleted. */
- if (ptr->type & TOMOYO_ACL_DELETED)
- acl->perm = 0;
- acl->perm |= perm;
- ptr->type &= ~TOMOYO_ACL_DELETED;
- error = 0;
+ if (!tomoyo_parse_name_union(filename1, &e.name1) ||
+ !tomoyo_parse_name_union(filename2, &e.name2))
goto out;
- }
- /* Not found. Append it to the tail. */
- acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
- if (!acl)
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
- acl->perm = perm;
- acl->filename1 = saved_filename1;
- acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
- error = 0;
- goto out;
- delete:
- error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path2_acl *acl =
+ container_of(ptr, struct tomoyo_path2_acl, head);
+ if (!tomoyo_is_same_path2_acl(acl, &e))
continue;
- acl = container_of(ptr, struct tomoyo_double_path_acl_record,
- head);
- if (acl->filename1 != saved_filename1 ||
- acl->filename2 != saved_filename2)
- continue;
- acl->perm &= ~perm;
- if (!acl->perm)
- ptr->type |= TOMOYO_ACL_DELETED;
+ if (is_delete)
+ acl->perm &= ~perm;
+ else
+ acl->perm |= perm;
error = 0;
break;
}
+ if (!is_delete && error) {
+ struct tomoyo_path2_acl *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->head.list,
+ &domain->acl_info_list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ tomoyo_put_name_union(&e.name1);
+ tomoyo_put_name_union(&e.name2);
return error;
}
/**
- * tomoyo_check_single_path_acl - Check permission for single path operation.
+ * tomoyo_path_acl - Check permission for single path operation.
*
* @domain: Pointer to "struct tomoyo_domain_info".
* @type: Type of operation.
* @filename: Filename to check.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
- const u8 type,
- const struct tomoyo_path_info *filename)
+static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type,
+ const struct tomoyo_path_info *filename)
{
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- return tomoyo_check_single_path_acl2(domain, filename, 1 << type, 1);
+ return tomoyo_path_acl2(domain, filename, 1 << type, 1);
}
/**
- * tomoyo_check_double_path_acl - Check permission for double path operation.
+ * tomoyo_path2_acl - Check permission for double path operation.
*
* @domain: Pointer to "struct tomoyo_domain_info".
* @type: Type of operation.
@@ -1010,13 +979,13 @@ static int tomoyo_check_single_path_acl(struct tomoyo_domain_info *domain,
* @filename2: Second filename to check.
*
* Returns 0 on success, -EPERM otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
- const u8 type,
- const struct tomoyo_path_info *
- filename1,
- const struct tomoyo_path_info *
- filename2)
+static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain,
+ const u8 type,
+ const struct tomoyo_path_info *filename1,
+ const struct tomoyo_path_info *filename2)
{
struct tomoyo_acl_info *ptr;
const u8 perm = 1 << type;
@@ -1024,28 +993,25 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE))
return 0;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
- struct tomoyo_double_path_acl_record *acl;
- if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_path2_acl *acl;
+ if (ptr->type != TOMOYO_TYPE_PATH2_ACL)
continue;
- acl = container_of(ptr, struct tomoyo_double_path_acl_record,
- head);
+ acl = container_of(ptr, struct tomoyo_path2_acl, head);
if (!(acl->perm & perm))
continue;
- if (!tomoyo_path_matches_pattern(filename1, acl->filename1))
+ if (!tomoyo_compare_name_union(filename1, &acl->name1))
continue;
- if (!tomoyo_path_matches_pattern(filename2, acl->filename2))
+ if (!tomoyo_compare_name_union(filename2, &acl->name2))
continue;
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
/**
- * tomoyo_check_single_path_permission2 - Check permission for single path operation.
+ * tomoyo_path_permission2 - Check permission for single path operation.
*
* @domain: Pointer to "struct tomoyo_domain_info".
* @operation: Type of operation.
@@ -1053,11 +1019,13 @@ static int tomoyo_check_double_path_acl(const struct tomoyo_domain_info *domain,
* @mode: Access control mode.
*
* Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
-static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
- const domain, u8 operation,
- const struct tomoyo_path_info *
- filename, const u8 mode)
+static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain,
+ u8 operation,
+ const struct tomoyo_path_info *filename,
+ const u8 mode)
{
const char *msg;
int error;
@@ -1066,8 +1034,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
if (!mode)
return 0;
next:
- error = tomoyo_check_single_path_acl(domain, operation, filename);
- msg = tomoyo_sp2keyword(operation);
+ error = tomoyo_path_acl(domain, operation, filename);
+ msg = tomoyo_path2keyword(operation);
if (!error)
goto ok;
if (tomoyo_verbose_mode(domain))
@@ -1076,7 +1044,7 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
tomoyo_get_last_name(domain));
if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
const char *name = tomoyo_get_file_pattern(filename)->name;
- tomoyo_update_single_path_acl(operation, name, domain, false);
+ tomoyo_update_path_acl(operation, name, domain, false);
}
if (!is_enforce)
error = 0;
@@ -1086,9 +1054,9 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
* we need to check "allow_rewrite" permission if the filename is
* specified by "deny_rewrite" keyword.
*/
- if (!error && operation == TOMOYO_TYPE_TRUNCATE_ACL &&
+ if (!error && operation == TOMOYO_TYPE_TRUNCATE &&
tomoyo_is_no_rewrite_file(filename)) {
- operation = TOMOYO_TYPE_REWRITE_ACL;
+ operation = TOMOYO_TYPE_REWRITE;
goto next;
}
return error;
@@ -1101,6 +1069,8 @@ static int tomoyo_check_single_path_permission2(struct tomoyo_domain_info *
* @filename: Check permission for "execute".
*
* Returns 0 on success, negativevalue otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
*/
int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
const struct tomoyo_path_info *filename)
@@ -1129,6 +1099,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
struct tomoyo_path_info *buf;
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;
if (!mode || !path->mnt)
return 0;
@@ -1140,6 +1111,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
* don't call me.
*/
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
@@ -1152,49 +1124,50 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
if ((acc_mode & MAY_WRITE) &&
((flag & O_TRUNC) || !(flag & O_APPEND)) &&
(tomoyo_is_no_rewrite_file(buf))) {
- error = tomoyo_check_single_path_permission2(domain,
- TOMOYO_TYPE_REWRITE_ACL,
- buf, mode);
+ error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE,
+ buf, mode);
}
if (!error)
error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open",
mode);
if (!error && (flag & O_TRUNC))
- error = tomoyo_check_single_path_permission2(domain,
- TOMOYO_TYPE_TRUNCATE_ACL,
- buf, mode);
+ error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE,
+ buf, mode);
out:
- tomoyo_free(buf);
+ kfree(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
}
/**
- * tomoyo_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink".
+ * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount".
*
- * @domain: Pointer to "struct tomoyo_domain_info".
* @operation: Type of operation.
* @path: Pointer to "struct path".
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
- const u8 operation, struct path *path)
+int tomoyo_path_perm(const u8 operation, struct path *path)
{
int error = -ENOMEM;
struct tomoyo_path_info *buf;
+ struct tomoyo_domain_info *domain = tomoyo_domain();
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
+ int idx;
if (!mode || !path->mnt)
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
switch (operation) {
- case TOMOYO_TYPE_MKDIR_ACL:
- case TOMOYO_TYPE_RMDIR_ACL:
+ case TOMOYO_TYPE_MKDIR:
+ case TOMOYO_TYPE_RMDIR:
+ case TOMOYO_TYPE_CHROOT:
if (!buf->is_dir) {
/*
* tomoyo_get_path() reserves space for appending "/."
@@ -1203,10 +1176,10 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
tomoyo_fill_path_info(buf);
}
}
- error = tomoyo_check_single_path_permission2(domain, operation, buf,
- mode);
+ error = tomoyo_path_permission2(domain, operation, buf, mode);
out:
- tomoyo_free(buf);
+ kfree(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
@@ -1215,21 +1188,23 @@ int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
/**
* tomoyo_check_rewrite_permission - Check permission for "rewrite".
*
- * @domain: Pointer to "struct tomoyo_domain_info".
* @filp: Pointer to "struct file".
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
- struct file *filp)
+int tomoyo_check_rewrite_permission(struct file *filp)
{
int error = -ENOMEM;
+ struct tomoyo_domain_info *domain = tomoyo_domain();
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
struct tomoyo_path_info *buf;
+ int idx;
if (!mode || !filp->f_path.mnt)
return 0;
+
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(&filp->f_path);
if (!buf)
goto out;
@@ -1237,38 +1212,38 @@ int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
error = 0;
goto out;
}
- error = tomoyo_check_single_path_permission2(domain,
- TOMOYO_TYPE_REWRITE_ACL,
- buf, mode);
+ error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode);
out:
- tomoyo_free(buf);
+ kfree(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
}
/**
- * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root".
*
- * @domain: Pointer to "struct tomoyo_domain_info".
* @operation: Type of operation.
* @path1: Pointer to "struct path".
* @path2: Pointer to "struct path".
*
* Returns 0 on success, negative value otherwise.
*/
-int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
- const u8 operation, struct path *path1,
- struct path *path2)
+int tomoyo_path2_perm(const u8 operation, struct path *path1,
+ struct path *path2)
{
int error = -ENOMEM;
struct tomoyo_path_info *buf1, *buf2;
+ struct tomoyo_domain_info *domain = tomoyo_domain();
const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
const bool is_enforce = (mode == 3);
const char *msg;
+ int idx;
if (!mode || !path1->mnt || !path2->mnt)
return 0;
+ idx = tomoyo_read_lock();
buf1 = tomoyo_get_path(path1);
buf2 = tomoyo_get_path(path2);
if (!buf1 || !buf2)
@@ -1289,8 +1264,8 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
}
}
}
- error = tomoyo_check_double_path_acl(domain, operation, buf1, buf2);
- msg = tomoyo_dp2keyword(operation);
+ error = tomoyo_path2_acl(domain, operation, buf1, buf2);
+ msg = tomoyo_path22keyword(operation);
if (!error)
goto out;
if (tomoyo_verbose_mode(domain))
@@ -1301,12 +1276,13 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info * const domain,
if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) {
const char *name1 = tomoyo_get_file_pattern(buf1)->name;
const char *name2 = tomoyo_get_file_pattern(buf2)->name;
- tomoyo_update_double_path_acl(operation, name1, name2, domain,
- false);
+ tomoyo_update_path2_acl(operation, name1, name2, domain,
+ false);
}
out:
- tomoyo_free(buf1);
- tomoyo_free(buf2);
+ kfree(buf1);
+ kfree(buf2);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c
new file mode 100644
index 000000000000..b9cc71b04314
--- /dev/null
+++ b/security/tomoyo/gc.c
@@ -0,0 +1,412 @@
+/*
+ * security/tomoyo/gc.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2010 NTT DATA CORPORATION
+ *
+ */
+
+#include "common.h"
+#include <linux/kthread.h>
+#include <linux/slab.h>
+
+enum tomoyo_gc_id {
+ TOMOYO_ID_PATH_GROUP,
+ TOMOYO_ID_PATH_GROUP_MEMBER,
+ TOMOYO_ID_DOMAIN_INITIALIZER,
+ TOMOYO_ID_DOMAIN_KEEPER,
+ TOMOYO_ID_ALIAS,
+ TOMOYO_ID_GLOBALLY_READABLE,
+ TOMOYO_ID_PATTERN,
+ TOMOYO_ID_NO_REWRITE,
+ TOMOYO_ID_MANAGER,
+ TOMOYO_ID_NAME,
+ TOMOYO_ID_ACL,
+ TOMOYO_ID_DOMAIN
+};
+
+struct tomoyo_gc_entry {
+ struct list_head list;
+ int type;
+ void *element;
+};
+static LIST_HEAD(tomoyo_gc_queue);
+static DEFINE_MUTEX(tomoyo_gc_mutex);
+
+/* Caller holds tomoyo_policy_lock mutex. */
+static bool tomoyo_add_to_gc(const int type, void *element)
+{
+ struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return false;
+ entry->type = type;
+ entry->element = element;
+ list_add(&entry->list, &tomoyo_gc_queue);
+ return true;
+}
+
+static void tomoyo_del_allow_read
+(struct tomoyo_globally_readable_file_entry *ptr)
+{
+ tomoyo_put_name(ptr->filename);
+}
+
+static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr)
+{
+ tomoyo_put_name(ptr->pattern);
+}
+
+static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr)
+{
+ tomoyo_put_name(ptr->pattern);
+}
+
+static void tomoyo_del_domain_initializer
+(struct tomoyo_domain_initializer_entry *ptr)
+{
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+}
+
+static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr)
+{
+ tomoyo_put_name(ptr->domainname);
+ tomoyo_put_name(ptr->program);
+}
+
+static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr)
+{
+ tomoyo_put_name(ptr->original_name);
+ tomoyo_put_name(ptr->aliased_name);
+}
+
+static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr)
+{
+ tomoyo_put_name(ptr->manager);
+}
+
+static void tomoyo_del_acl(struct tomoyo_acl_info *acl)
+{
+ switch (acl->type) {
+ case TOMOYO_TYPE_PATH_ACL:
+ {
+ struct tomoyo_path_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name);
+ }
+ break;
+ case TOMOYO_TYPE_PATH2_ACL:
+ {
+ struct tomoyo_path2_acl *entry
+ = container_of(acl, typeof(*entry), head);
+ tomoyo_put_name_union(&entry->name1);
+ tomoyo_put_name_union(&entry->name2);
+ }
+ break;
+ default:
+ printk(KERN_WARNING "Unknown type\n");
+ break;
+ }
+}
+
+static bool tomoyo_del_domain(struct tomoyo_domain_info *domain)
+{
+ struct tomoyo_acl_info *acl;
+ struct tomoyo_acl_info *tmp;
+ /*
+ * Since we don't protect whole execve() operation using SRCU,
+ * we need to recheck domain->users at this point.
+ *
+ * (1) Reader starts SRCU section upon execve().
+ * (2) Reader traverses tomoyo_domain_list and finds this domain.
+ * (3) Writer marks this domain as deleted.
+ * (4) Garbage collector removes this domain from tomoyo_domain_list
+ * because this domain is marked as deleted and used by nobody.
+ * (5) Reader saves reference to this domain into
+ * "struct linux_binprm"->cred->security .
+ * (6) Reader finishes SRCU section, although execve() operation has
+ * not finished yet.
+ * (7) Garbage collector waits for SRCU synchronization.
+ * (8) Garbage collector kfree() this domain because this domain is
+ * used by nobody.
+ * (9) Reader finishes execve() operation and restores this domain from
+ * "struct linux_binprm"->cred->security.
+ *
+ * By updating domain->users at (5), we can solve this race problem
+ * by rechecking domain->users at (8).
+ */
+ if (atomic_read(&domain->users))
+ return false;
+ list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
+ tomoyo_del_acl(acl);
+ tomoyo_memory_free(acl);
+ }
+ tomoyo_put_name(domain->domainname);
+ return true;
+}
+
+
+static void tomoyo_del_name(const struct tomoyo_name_entry *ptr)
+{
+}
+
+static void tomoyo_del_path_group_member(struct tomoyo_path_group_member
+ *member)
+{
+ tomoyo_put_name(member->member_name);
+}
+
+static void tomoyo_del_path_group(struct tomoyo_path_group *group)
+{
+ tomoyo_put_name(group->group_name);
+}
+
+static void tomoyo_collect_entry(void)
+{
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return;
+ {
+ struct tomoyo_globally_readable_file_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_pattern_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_no_rewrite_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_initializer_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_keeper_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_alias_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_policy_manager_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list,
+ list) {
+ if (!ptr->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr))
+ list_del_rcu(&ptr->list);
+ else
+ break;
+ }
+ }
+ {
+ struct tomoyo_domain_info *domain;
+ list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+ struct tomoyo_acl_info *acl;
+ list_for_each_entry_rcu(acl, &domain->acl_info_list,
+ list) {
+ switch (acl->type) {
+ case TOMOYO_TYPE_PATH_ACL:
+ if (container_of(acl,
+ struct tomoyo_path_acl,
+ head)->perm ||
+ container_of(acl,
+ struct tomoyo_path_acl,
+ head)->perm_high)
+ continue;
+ break;
+ case TOMOYO_TYPE_PATH2_ACL:
+ if (container_of(acl,
+ struct tomoyo_path2_acl,
+ head)->perm)
+ continue;
+ break;
+ default:
+ continue;
+ }
+ if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl))
+ list_del_rcu(&acl->list);
+ else
+ break;
+ }
+ if (!domain->is_deleted || atomic_read(&domain->users))
+ continue;
+ /*
+ * Nobody is referring this domain. But somebody may
+ * refer this domain after successful execve().
+ * We recheck domain->users after SRCU synchronization.
+ */
+ if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain))
+ list_del_rcu(&domain->list);
+ else
+ break;
+ }
+ }
+ {
+ int i;
+ for (i = 0; i < TOMOYO_MAX_HASH; i++) {
+ struct tomoyo_name_entry *ptr;
+ list_for_each_entry_rcu(ptr, &tomoyo_name_list[i],
+ list) {
+ if (atomic_read(&ptr->users))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr))
+ list_del_rcu(&ptr->list);
+ else {
+ i = TOMOYO_MAX_HASH;
+ break;
+ }
+ }
+ }
+ }
+ {
+ struct tomoyo_path_group *group;
+ list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) {
+ struct tomoyo_path_group_member *member;
+ list_for_each_entry_rcu(member, &group->member_list,
+ list) {
+ if (!member->is_deleted)
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER,
+ member))
+ list_del_rcu(&member->list);
+ else
+ break;
+ }
+ if (!list_empty(&group->member_list) ||
+ atomic_read(&group->users))
+ continue;
+ if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group))
+ list_del_rcu(&group->list);
+ else
+ break;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+}
+
+static void tomoyo_kfree_entry(void)
+{
+ struct tomoyo_gc_entry *p;
+ struct tomoyo_gc_entry *tmp;
+
+ list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) {
+ switch (p->type) {
+ case TOMOYO_ID_DOMAIN_INITIALIZER:
+ tomoyo_del_domain_initializer(p->element);
+ break;
+ case TOMOYO_ID_DOMAIN_KEEPER:
+ tomoyo_del_domain_keeper(p->element);
+ break;
+ case TOMOYO_ID_ALIAS:
+ tomoyo_del_alias(p->element);
+ break;
+ case TOMOYO_ID_GLOBALLY_READABLE:
+ tomoyo_del_allow_read(p->element);
+ break;
+ case TOMOYO_ID_PATTERN:
+ tomoyo_del_file_pattern(p->element);
+ break;
+ case TOMOYO_ID_NO_REWRITE:
+ tomoyo_del_no_rewrite(p->element);
+ break;
+ case TOMOYO_ID_MANAGER:
+ tomoyo_del_manager(p->element);
+ break;
+ case TOMOYO_ID_NAME:
+ tomoyo_del_name(p->element);
+ break;
+ case TOMOYO_ID_ACL:
+ tomoyo_del_acl(p->element);
+ break;
+ case TOMOYO_ID_DOMAIN:
+ if (!tomoyo_del_domain(p->element))
+ continue;
+ break;
+ case TOMOYO_ID_PATH_GROUP_MEMBER:
+ tomoyo_del_path_group_member(p->element);
+ break;
+ case TOMOYO_ID_PATH_GROUP:
+ tomoyo_del_path_group(p->element);
+ break;
+ default:
+ printk(KERN_WARNING "Unknown type\n");
+ break;
+ }
+ tomoyo_memory_free(p->element);
+ list_del(&p->list);
+ kfree(p);
+ }
+}
+
+static int tomoyo_gc_thread(void *unused)
+{
+ daemonize("GC for TOMOYO");
+ if (mutex_trylock(&tomoyo_gc_mutex)) {
+ int i;
+ for (i = 0; i < 10; i++) {
+ tomoyo_collect_entry();
+ if (list_empty(&tomoyo_gc_queue))
+ break;
+ synchronize_srcu(&tomoyo_ss);
+ tomoyo_kfree_entry();
+ }
+ mutex_unlock(&tomoyo_gc_mutex);
+ }
+ do_exit(0);
+}
+
+void tomoyo_run_gc(void)
+{
+ struct task_struct *task = kthread_create(tomoyo_gc_thread, NULL,
+ "GC for TOMOYO");
+ if (!IS_ERR(task))
+ wake_up_process(task);
+}
diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c
new file mode 100644
index 000000000000..c988041c8e1c
--- /dev/null
+++ b/security/tomoyo/path_group.c
@@ -0,0 +1,172 @@
+/*
+ * security/tomoyo/path_group.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+
+#include <linux/slab.h>
+#include "common.h"
+/* The list for "struct ccs_path_group". */
+LIST_HEAD(tomoyo_path_group_list);
+
+/**
+ * tomoyo_get_path_group - Allocate memory for "struct tomoyo_path_group".
+ *
+ * @group_name: The name of pathname group.
+ *
+ * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise.
+ */
+struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name)
+{
+ struct tomoyo_path_group *entry = NULL;
+ struct tomoyo_path_group *group = NULL;
+ const struct tomoyo_path_info *saved_group_name;
+ int error = -ENOMEM;
+ if (!tomoyo_is_correct_path(group_name, 0, 0, 0) ||
+ !group_name[0])
+ return NULL;
+ saved_group_name = tomoyo_get_name(group_name);
+ if (!saved_group_name)
+ return NULL;
+ entry = kzalloc(sizeof(*entry), GFP_NOFS);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) {
+ if (saved_group_name != group->group_name)
+ continue;
+ atomic_inc(&group->users);
+ error = 0;
+ break;
+ }
+ if (error && tomoyo_memory_ok(entry)) {
+ INIT_LIST_HEAD(&entry->member_list);
+ entry->group_name = saved_group_name;
+ saved_group_name = NULL;
+ atomic_set(&entry->users, 1);
+ list_add_tail_rcu(&entry->list, &tomoyo_path_group_list);
+ group = entry;
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(saved_group_name);
+ kfree(entry);
+ return !error ? group : NULL;
+}
+
+/**
+ * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, nagative value otherwise.
+ */
+int tomoyo_write_path_group_policy(char *data, const bool is_delete)
+{
+ struct tomoyo_path_group *group;
+ struct tomoyo_path_group_member *member;
+ struct tomoyo_path_group_member e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ char *w[2];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ group = tomoyo_get_path_group(w[0]);
+ if (!group)
+ return -ENOMEM;
+ e.member_name = tomoyo_get_name(w[1]);
+ if (!e.member_name)
+ goto out;
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ goto out;
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (member->member_name != e.member_name)
+ continue;
+ member->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error) {
+ struct tomoyo_path_group_member *entry =
+ tomoyo_commit_ok(&e, sizeof(e));
+ if (entry) {
+ list_add_tail_rcu(&entry->list, &group->member_list);
+ error = 0;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.member_name);
+ tomoyo_put_path_group(group);
+ return error;
+}
+
+/**
+ * tomoyo_read_path_group_policy - Read "struct tomoyo_path_group" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *gpos;
+ struct list_head *mpos;
+ list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) {
+ struct tomoyo_path_group *group;
+ group = list_entry(gpos, struct tomoyo_path_group, list);
+ list_for_each_cookie(mpos, head->read_var2,
+ &group->member_list) {
+ struct tomoyo_path_group_member *member;
+ member = list_entry(mpos,
+ struct tomoyo_path_group_member,
+ list);
+ if (member->is_deleted)
+ continue;
+ if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP
+ "%s %s\n",
+ group->group_name->name,
+ member->member_name->name))
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
+ *
+ * @pathname: The name of pathname.
+ * @group: Pointer to "struct tomoyo_path_group".
+ * @may_use_pattern: True if wild card is permitted.
+ *
+ * Returns true if @pathname matches pathnames in @group, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
+ const struct tomoyo_path_group *group,
+ const bool may_use_pattern)
+{
+ struct tomoyo_path_group_member *member;
+ bool matched = false;
+ list_for_each_entry_rcu(member, &group->member_list, list) {
+ if (member->is_deleted)
+ continue;
+ if (!member->member_name->is_patterned) {
+ if (tomoyo_pathcmp(pathname, member->member_name))
+ continue;
+ } else if (may_use_pattern) {
+ if (!tomoyo_path_matches_pattern(pathname,
+ member->member_name))
+ continue;
+ } else
+ continue;
+ matched = true;
+ break;
+ }
+ return matched;
+}
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c
index 18369d497eb8..d1b96f019621 100644
--- a/security/tomoyo/realpath.c
+++ b/security/tomoyo/realpath.c
@@ -14,9 +14,9 @@
#include <linux/mnt_namespace.h>
#include <linux/fs_struct.h>
#include <linux/hash.h>
-
+#include <linux/magic.h>
+#include <linux/slab.h>
#include "common.h"
-#include "realpath.h"
/**
* tomoyo_encode: Convert binary string to ascii string.
@@ -89,30 +89,15 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname,
sp = dentry->d_op->d_dname(dentry, newname + offset,
newname_len - offset);
} else {
- /* Taken from d_namespace_path(). */
- struct path root;
- struct path ns_root = { };
- struct path tmp;
+ struct path ns_root = {.mnt = NULL, .dentry = NULL};
- read_lock(&current->fs->lock);
- root = current->fs->root;
- path_get(&root);
- read_unlock(&current->fs->lock);
- spin_lock(&vfsmount_lock);
- if (root.mnt && root.mnt->mnt_ns)
- ns_root.mnt = mntget(root.mnt->mnt_ns->root);
- if (ns_root.mnt)
- ns_root.dentry = dget(ns_root.mnt->mnt_root);
- spin_unlock(&vfsmount_lock);
spin_lock(&dcache_lock);
- tmp = ns_root;
- sp = __d_path(path, &tmp, newname, newname_len);
+ /* go to whatever namespace root we are under */
+ sp = __d_path(path, &ns_root, newname, newname_len);
spin_unlock(&dcache_lock);
- path_put(&root);
- path_put(&ns_root);
/* Prepend "/proc" prefix if using internal proc vfs mount. */
- if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) &&
- (strcmp(path->mnt->mnt_sb->s_type->name, "proc") == 0)) {
+ if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) &&
+ (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
sp -= 5;
if (sp >= newname)
memcpy(sp, "/proc", 5);
@@ -149,12 +134,12 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname,
*
* Returns the realpath of the given @path on success, NULL otherwise.
*
- * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
+ * These functions use kzalloc(), so the caller must call kfree()
* if these functions didn't return NULL.
*/
char *tomoyo_realpath_from_path(struct path *path)
{
- char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer));
+ char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS);
BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer)
<= TOMOYO_MAX_PATHNAME_LEN - 1);
@@ -163,7 +148,7 @@ char *tomoyo_realpath_from_path(struct path *path)
if (tomoyo_realpath_from_path2(path, buf,
TOMOYO_MAX_PATHNAME_LEN - 1) == 0)
return buf;
- tomoyo_free(buf);
+ kfree(buf);
return NULL;
}
@@ -206,98 +191,66 @@ char *tomoyo_realpath_nofollow(const char *pathname)
}
/* Memory allocated for non-string data. */
-static unsigned int tomoyo_allocated_memory_for_elements;
-/* Quota for holding non-string data. */
-static unsigned int tomoyo_quota_for_elements;
+static atomic_t tomoyo_policy_memory_size;
+/* Quota for holding policy. */
+static unsigned int tomoyo_quota_for_policy;
/**
- * tomoyo_alloc_element - Allocate permanent memory for structures.
+ * tomoyo_memory_ok - Check memory quota.
*
- * @size: Size in bytes.
+ * @ptr: Pointer to allocated memory.
*
- * Returns pointer to allocated memory on success, NULL otherwise.
+ * Returns true on success, false otherwise.
*
- * Memory has to be zeroed.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
+ * Caller holds tomoyo_policy_lock.
+ * Memory pointed by @ptr will be zeroed on success.
*/
-void *tomoyo_alloc_element(const unsigned int size)
+bool tomoyo_memory_ok(void *ptr)
{
- static char *buf;
- static DEFINE_MUTEX(lock);
- static unsigned int buf_used_len = PATH_MAX;
- char *ptr = NULL;
- /*Assumes sizeof(void *) >= sizeof(long) is true. */
- const unsigned int word_aligned_size
- = roundup(size, max(sizeof(void *), sizeof(long)));
- if (word_aligned_size > PATH_MAX)
- return NULL;
- mutex_lock(&lock);
- if (buf_used_len + word_aligned_size > PATH_MAX) {
- if (!tomoyo_quota_for_elements ||
- tomoyo_allocated_memory_for_elements
- + PATH_MAX <= tomoyo_quota_for_elements)
- ptr = kzalloc(PATH_MAX, GFP_KERNEL);
- if (!ptr) {
- printk(KERN_WARNING "ERROR: Out of memory "
- "for tomoyo_alloc_element().\n");
- if (!tomoyo_policy_loaded)
- panic("MAC Initialization failed.\n");
- } else {
- buf = ptr;
- tomoyo_allocated_memory_for_elements += PATH_MAX;
- buf_used_len = word_aligned_size;
- ptr = buf;
- }
- } else if (word_aligned_size) {
- int i;
- ptr = buf + buf_used_len;
- buf_used_len += word_aligned_size;
- for (i = 0; i < word_aligned_size; i++) {
- if (!ptr[i])
- continue;
- printk(KERN_ERR "WARNING: Reserved memory was tainted! "
- "The system might go wrong.\n");
- ptr[i] = '\0';
- }
+ int allocated_len = ptr ? ksize(ptr) : 0;
+ atomic_add(allocated_len, &tomoyo_policy_memory_size);
+ if (ptr && (!tomoyo_quota_for_policy ||
+ atomic_read(&tomoyo_policy_memory_size)
+ <= tomoyo_quota_for_policy)) {
+ memset(ptr, 0, allocated_len);
+ return true;
}
- mutex_unlock(&lock);
- return ptr;
+ printk(KERN_WARNING "ERROR: Out of memory "
+ "for tomoyo_alloc_element().\n");
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+ return false;
}
-/* Memory allocated for string data in bytes. */
-static unsigned int tomoyo_allocated_memory_for_savename;
-/* Quota for holding string data in bytes. */
-static unsigned int tomoyo_quota_for_savename;
-
-/*
- * TOMOYO uses this hash only when appending a string into the string
- * table. Frequency of appending strings is very low. So we don't need
- * large (e.g. 64k) hash size. 256 will be sufficient.
+/**
+ * tomoyo_commit_ok - Check memory quota.
+ *
+ * @data: Data to copy from.
+ * @size: Size in byte.
+ *
+ * Returns pointer to allocated memory on success, NULL otherwise.
*/
-#define TOMOYO_HASH_BITS 8
-#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
+void *tomoyo_commit_ok(void *data, const unsigned int size)
+{
+ void *ptr = kzalloc(size, GFP_NOFS);
+ if (tomoyo_memory_ok(ptr)) {
+ memmove(ptr, data, size);
+ memset(data, 0, size);
+ return ptr;
+ }
+ return NULL;
+}
-/*
- * tomoyo_name_entry is a structure which is used for linking
- * "struct tomoyo_path_info" into tomoyo_name_list .
+/**
+ * tomoyo_memory_free - Free memory for elements.
*
- * Since tomoyo_name_list manages a list of strings which are shared by
- * multiple processes (whereas "struct tomoyo_path_info" inside
- * "struct tomoyo_path_info_with_data" is not shared), a reference counter will
- * be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info"
- * when TOMOYO starts supporting garbage collector.
+ * @ptr: Pointer to allocated memory.
*/
-struct tomoyo_name_entry {
- struct list_head list;
- struct tomoyo_path_info entry;
-};
-
-/* Structure for available memory region. */
-struct tomoyo_free_memory_block_list {
- struct list_head list;
- char *ptr; /* Pointer to a free area. */
- int len; /* Length of the area. */
-};
+void tomoyo_memory_free(void *ptr)
+{
+ atomic_sub(ksize(ptr), &tomoyo_policy_memory_size);
+ kfree(ptr);
+}
/*
* tomoyo_name_list is used for holding string data used by TOMOYO.
@@ -305,87 +258,57 @@ struct tomoyo_free_memory_block_list {
* "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
* "const struct tomoyo_path_info *".
*/
-static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
+struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
/**
- * tomoyo_save_name - Allocate permanent memory for string data.
+ * tomoyo_get_name - Allocate permanent memory for string data.
*
* @name: The string to store into the permernent memory.
*
* Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
- *
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
*/
-const struct tomoyo_path_info *tomoyo_save_name(const char *name)
+const struct tomoyo_path_info *tomoyo_get_name(const char *name)
{
- static LIST_HEAD(fmb_list);
- static DEFINE_MUTEX(lock);
struct tomoyo_name_entry *ptr;
unsigned int hash;
- /* fmb contains available size in bytes.
- fmb is removed from the fmb_list when fmb->len becomes 0. */
- struct tomoyo_free_memory_block_list *fmb;
int len;
- char *cp;
+ int allocated_len;
struct list_head *head;
if (!name)
return NULL;
len = strlen(name) + 1;
- if (len > TOMOYO_MAX_PATHNAME_LEN) {
- printk(KERN_WARNING "ERROR: Name too long "
- "for tomoyo_save_name().\n");
- return NULL;
- }
hash = full_name_hash((const unsigned char *) name, len - 1);
head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
-
- mutex_lock(&lock);
+ if (mutex_lock_interruptible(&tomoyo_policy_lock))
+ return NULL;
list_for_each_entry(ptr, head, list) {
- if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
- goto out;
- }
- list_for_each_entry(fmb, &fmb_list, list) {
- if (len <= fmb->len)
- goto ready;
+ if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
+ continue;
+ atomic_inc(&ptr->users);
+ goto out;
}
- if (!tomoyo_quota_for_savename ||
- tomoyo_allocated_memory_for_savename + PATH_MAX
- <= tomoyo_quota_for_savename)
- cp = kzalloc(PATH_MAX, GFP_KERNEL);
- else
- cp = NULL;
- fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
- if (!cp || !fmb) {
- kfree(cp);
- kfree(fmb);
+ ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
+ allocated_len = ptr ? ksize(ptr) : 0;
+ if (!ptr || (tomoyo_quota_for_policy &&
+ atomic_read(&tomoyo_policy_memory_size) + allocated_len
+ > tomoyo_quota_for_policy)) {
+ kfree(ptr);
printk(KERN_WARNING "ERROR: Out of memory "
- "for tomoyo_save_name().\n");
+ "for tomoyo_get_name().\n");
if (!tomoyo_policy_loaded)
panic("MAC Initialization failed.\n");
ptr = NULL;
goto out;
}
- tomoyo_allocated_memory_for_savename += PATH_MAX;
- list_add(&fmb->list, &fmb_list);
- fmb->ptr = cp;
- fmb->len = PATH_MAX;
- ready:
- ptr = tomoyo_alloc_element(sizeof(*ptr));
- if (!ptr)
- goto out;
- ptr->entry.name = fmb->ptr;
- memmove(fmb->ptr, name, len);
+ atomic_add(allocated_len, &tomoyo_policy_memory_size);
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
+ atomic_set(&ptr->users, 1);
tomoyo_fill_path_info(&ptr->entry);
- fmb->ptr += len;
- fmb->len -= len;
list_add_tail(&ptr->list, head);
- if (fmb->len == 0) {
- list_del(&fmb->list);
- kfree(fmb);
- }
out:
- mutex_unlock(&lock);
+ mutex_unlock(&tomoyo_policy_lock);
return ptr ? &ptr->entry : NULL;
}
@@ -400,45 +323,14 @@ void __init tomoyo_realpath_init(void)
for (i = 0; i < TOMOYO_MAX_HASH; i++)
INIT_LIST_HEAD(&tomoyo_name_list[i]);
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
- tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME);
- list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
- down_read(&tomoyo_domain_list_lock);
+ tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME);
+ /*
+ * tomoyo_read_lock() is not needed because this function is
+ * called before the first "delete" request.
+ */
+ list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain)
panic("Can't register tomoyo_kernel_domain");
- up_read(&tomoyo_domain_list_lock);
-}
-
-/* Memory allocated for temporary purpose. */
-static atomic_t tomoyo_dynamic_memory_size;
-
-/**
- * tomoyo_alloc - Allocate memory for temporary purpose.
- *
- * @size: Size in bytes.
- *
- * Returns pointer to allocated memory on success, NULL otherwise.
- */
-void *tomoyo_alloc(const size_t size)
-{
- void *p = kzalloc(size, GFP_KERNEL);
- if (p)
- atomic_add(ksize(p), &tomoyo_dynamic_memory_size);
- return p;
-}
-
-/**
- * tomoyo_free - Release memory allocated by tomoyo_alloc().
- *
- * @p: Pointer returned by tomoyo_alloc(). May be NULL.
- *
- * Returns nothing.
- */
-void tomoyo_free(const void *p)
-{
- if (p) {
- atomic_sub(ksize(p), &tomoyo_dynamic_memory_size);
- kfree(p);
- }
}
/**
@@ -451,32 +343,19 @@ void tomoyo_free(const void *p)
int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head)
{
if (!head->read_eof) {
- const unsigned int shared
- = tomoyo_allocated_memory_for_savename;
- const unsigned int private
- = tomoyo_allocated_memory_for_elements;
- const unsigned int dynamic
- = atomic_read(&tomoyo_dynamic_memory_size);
+ const unsigned int policy
+ = atomic_read(&tomoyo_policy_memory_size);
char buffer[64];
memset(buffer, 0, sizeof(buffer));
- if (tomoyo_quota_for_savename)
- snprintf(buffer, sizeof(buffer) - 1,
- " (Quota: %10u)",
- tomoyo_quota_for_savename);
- else
- buffer[0] = '\0';
- tomoyo_io_printf(head, "Shared: %10u%s\n", shared, buffer);
- if (tomoyo_quota_for_elements)
+ if (tomoyo_quota_for_policy)
snprintf(buffer, sizeof(buffer) - 1,
" (Quota: %10u)",
- tomoyo_quota_for_elements);
+ tomoyo_quota_for_policy);
else
buffer[0] = '\0';
- tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer);
- tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic);
- tomoyo_io_printf(head, "Total: %10u\n",
- shared + private + dynamic);
+ tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer);
+ tomoyo_io_printf(head, "Total: %10u\n", policy);
head->read_eof = true;
}
return 0;
@@ -494,9 +373,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head)
char *data = head->write_buf;
unsigned int size;
- if (sscanf(data, "Shared: %u", &size) == 1)
- tomoyo_quota_for_savename = size;
- else if (sscanf(data, "Private: %u", &size) == 1)
- tomoyo_quota_for_elements = size;
+ if (sscanf(data, "Policy: %u", &size) == 1)
+ tomoyo_quota_for_policy = size;
return 0;
}
diff --git a/security/tomoyo/realpath.h b/security/tomoyo/realpath.h
deleted file mode 100644
index 78217a37960b..000000000000
--- a/security/tomoyo/realpath.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * security/tomoyo/realpath.h
- *
- * Get the canonicalized absolute pathnames. The basis for TOMOYO.
- *
- * Copyright (C) 2005-2009 NTT DATA CORPORATION
- *
- * Version: 2.2.0 2009/04/01
- *
- */
-
-#ifndef _SECURITY_TOMOYO_REALPATH_H
-#define _SECURITY_TOMOYO_REALPATH_H
-
-struct path;
-struct tomoyo_path_info;
-struct tomoyo_io_buffer;
-
-/* Convert binary string to ascii string. */
-int tomoyo_encode(char *buffer, int buflen, const char *str);
-
-/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
-int tomoyo_realpath_from_path2(struct path *path, char *newname,
- int newname_len);
-
-/*
- * Returns realpath(3) of the given pathname but ignores chroot'ed root.
- * These functions use tomoyo_alloc(), so the caller must call tomoyo_free()
- * if these functions didn't return NULL.
- */
-char *tomoyo_realpath(const char *pathname);
-/*
- * Same with tomoyo_realpath() except that it doesn't follow the final symlink.
- */
-char *tomoyo_realpath_nofollow(const char *pathname);
-/* Same with tomoyo_realpath() except that the pathname is already solved. */
-char *tomoyo_realpath_from_path(struct path *path);
-
-/*
- * Allocate memory for ACL entry.
- * The RAM is chunked, so NEVER try to kfree() the returned pointer.
- */
-void *tomoyo_alloc_element(const unsigned int size);
-
-/*
- * Keep the given name on the RAM.
- * The RAM is shared, so NEVER try to modify or kfree() the returned name.
- */
-const struct tomoyo_path_info *tomoyo_save_name(const char *name);
-
-/* Allocate memory for temporary use (e.g. permission checks). */
-void *tomoyo_alloc(const size_t size);
-
-/* Free memory allocated by tomoyo_alloc(). */
-void tomoyo_free(const void *p);
-
-/* Check for memory usage. */
-int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head);
-
-/* Set memory quota. */
-int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head);
-
-/* Initialize realpath related code. */
-void __init tomoyo_realpath_init(void);
-
-#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */
diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c
index 2aceebf5f354..dedd97d0c163 100644
--- a/security/tomoyo/tomoyo.c
+++ b/security/tomoyo/tomoyo.c
@@ -11,8 +11,6 @@
#include <linux/security.h>
#include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
{
@@ -23,21 +21,23 @@ static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp)
{
- /*
- * Since "struct tomoyo_domain_info *" is a sharable pointer,
- * we don't need to duplicate.
- */
- new->security = old->security;
+ struct tomoyo_domain_info *domain = old->security;
+ new->security = domain;
+ if (domain)
+ atomic_inc(&domain->users);
return 0;
}
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
{
- /*
- * Since "struct tomoyo_domain_info *" is a sharable pointer,
- * we don't need to duplicate.
- */
- new->security = old->security;
+ tomoyo_cred_prepare(new, old, 0);
+}
+
+static void tomoyo_cred_free(struct cred *cred)
+{
+ struct tomoyo_domain_info *domain = cred->security;
+ if (domain)
+ atomic_dec(&domain->users);
}
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
@@ -61,6 +61,14 @@ static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
if (!tomoyo_policy_loaded)
tomoyo_load_policy(bprm->filename);
/*
+ * Release reference to "struct tomoyo_domain_info" stored inside
+ * "bprm->cred->security". New reference to "struct tomoyo_domain_info"
+ * stored inside "bprm->cred->security" will be acquired later inside
+ * tomoyo_find_next_domain().
+ */
+ atomic_dec(&((struct tomoyo_domain_info *)
+ bprm->cred->security)->users);
+ /*
* Tell tomoyo_bprm_check_security() is called for the first time of an
* execve operation.
*/
@@ -76,8 +84,12 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain)
- return tomoyo_find_next_domain(bprm);
+ if (!domain) {
+ const int idx = tomoyo_read_lock();
+ const int err = tomoyo_find_next_domain(bprm);
+ tomoyo_read_unlock(idx);
+ return err;
+ }
/*
* Read permission is checked against interpreters using next domain.
*/
@@ -87,67 +99,56 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
static int tomoyo_path_truncate(struct path *path, loff_t length,
unsigned int time_attrs)
{
- return tomoyo_check_1path_perm(tomoyo_domain(),
- TOMOYO_TYPE_TRUNCATE_ACL,
- path);
+ return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path);
}
static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_check_1path_perm(tomoyo_domain(),
- TOMOYO_TYPE_UNLINK_ACL,
- &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path);
}
static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
int mode)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_check_1path_perm(tomoyo_domain(),
- TOMOYO_TYPE_MKDIR_ACL,
- &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path);
}
static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_check_1path_perm(tomoyo_domain(),
- TOMOYO_TYPE_RMDIR_ACL,
- &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path);
}
static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
const char *old_name)
{
struct path path = { parent->mnt, dentry };
- return tomoyo_check_1path_perm(tomoyo_domain(),
- TOMOYO_TYPE_SYMLINK_ACL,
- &path);
+ return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path);
}
static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
int mode, unsigned int dev)
{
struct path path = { parent->mnt, dentry };
- int type = TOMOYO_TYPE_CREATE_ACL;
+ int type = TOMOYO_TYPE_CREATE;
switch (mode & S_IFMT) {
case S_IFCHR:
- type = TOMOYO_TYPE_MKCHAR_ACL;
+ type = TOMOYO_TYPE_MKCHAR;
break;
case S_IFBLK:
- type = TOMOYO_TYPE_MKBLOCK_ACL;
+ type = TOMOYO_TYPE_MKBLOCK;
break;
case S_IFIFO:
- type = TOMOYO_TYPE_MKFIFO_ACL;
+ type = TOMOYO_TYPE_MKFIFO;
break;
case S_IFSOCK:
- type = TOMOYO_TYPE_MKSOCK_ACL;
+ type = TOMOYO_TYPE_MKSOCK;
break;
}
- return tomoyo_check_1path_perm(tomoyo_domain(),
- type, &path);
+ return tomoyo_path_perm(type, &path);
}
static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
@@ -155,9 +156,7 @@ static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
{
struct path path1 = { new_dir->mnt, old_dentry };
struct path path2 = { new_dir->mnt, new_dentry };
- return tomoyo_check_2path_perm(tomoyo_domain(),
- TOMOYO_TYPE_LINK_ACL,
- &path1, &path2);
+ return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
}
static int tomoyo_path_rename(struct path *old_parent,
@@ -167,16 +166,14 @@ static int tomoyo_path_rename(struct path *old_parent,
{
struct path path1 = { old_parent->mnt, old_dentry };
struct path path2 = { new_parent->mnt, new_dentry };
- return tomoyo_check_2path_perm(tomoyo_domain(),
- TOMOYO_TYPE_RENAME_ACL,
- &path1, &path2);
+ return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
}
static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
unsigned long arg)
{
if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))
- return tomoyo_check_rewrite_permission(tomoyo_domain(), file);
+ return tomoyo_check_rewrite_permission(file);
return 0;
}
@@ -189,6 +186,51 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
}
+static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path);
+}
+
+static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ struct path path = { mnt, dentry };
+ return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path);
+}
+
+static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid)
+{
+ int error = 0;
+ if (uid != (uid_t) -1)
+ error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path);
+ if (!error && gid != (gid_t) -1)
+ error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path);
+ return error;
+}
+
+static int tomoyo_path_chroot(struct path *path)
+{
+ return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path);
+}
+
+static int tomoyo_sb_mount(char *dev_name, struct path *path,
+ char *type, unsigned long flags, void *data)
+{
+ return tomoyo_path_perm(TOMOYO_TYPE_MOUNT, path);
+}
+
+static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
+{
+ struct path path = { mnt, mnt->mnt_root };
+ return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path);
+}
+
+static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+ return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path);
+}
+
/*
* tomoyo_security_ops is a "struct security_operations" which is used for
* registering TOMOYO.
@@ -198,6 +240,7 @@ static struct security_operations tomoyo_security_ops = {
.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,
@@ -210,8 +253,18 @@ static struct security_operations tomoyo_security_ops = {
.path_mknod = tomoyo_path_mknod,
.path_link = tomoyo_path_link,
.path_rename = tomoyo_path_rename,
+ .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,
};
+/* Lock for GC. */
+struct srcu_struct tomoyo_ss;
+
static int __init tomoyo_init(void)
{
struct cred *cred = (struct cred *) current_cred();
@@ -219,7 +272,8 @@ static int __init tomoyo_init(void)
if (!security_module_enable(&tomoyo_security_ops))
return 0;
/* register ourselves with the security framework */
- if (register_security(&tomoyo_security_ops))
+ if (register_security(&tomoyo_security_ops) ||
+ init_srcu_struct(&tomoyo_ss))
panic("Failure registering TOMOYO Linux");
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
diff --git a/security/tomoyo/tomoyo.h b/security/tomoyo/tomoyo.h
deleted file mode 100644
index ed758325b1ae..000000000000
--- a/security/tomoyo/tomoyo.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * security/tomoyo/tomoyo.h
- *
- * Implementation of the Domain-Based Mandatory Access Control.
- *
- * Copyright (C) 2005-2009 NTT DATA CORPORATION
- *
- * Version: 2.2.0 2009/04/01
- *
- */
-
-#ifndef _SECURITY_TOMOYO_TOMOYO_H
-#define _SECURITY_TOMOYO_TOMOYO_H
-
-struct tomoyo_path_info;
-struct path;
-struct inode;
-struct linux_binprm;
-struct pt_regs;
-
-int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
- const struct tomoyo_path_info *filename);
-int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
- struct path *path, const int flag);
-int tomoyo_check_1path_perm(struct tomoyo_domain_info *domain,
- const u8 operation, struct path *path);
-int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
- const u8 operation, struct path *path1,
- struct path *path2);
-int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
- struct file *filp);
-int tomoyo_find_next_domain(struct linux_binprm *bprm);
-
-/* Index numbers for Access Controls. */
-
-#define TOMOYO_TYPE_SINGLE_PATH_ACL 0
-#define TOMOYO_TYPE_DOUBLE_PATH_ACL 1
-
-/* Index numbers for File Controls. */
-
-/*
- * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set
- * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and
- * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set.
- * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or
- * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are
- * automatically cleared if TYPE_READ_WRITE_ACL is cleared.
- */
-
-#define TOMOYO_TYPE_READ_WRITE_ACL 0
-#define TOMOYO_TYPE_EXECUTE_ACL 1
-#define TOMOYO_TYPE_READ_ACL 2
-#define TOMOYO_TYPE_WRITE_ACL 3
-#define TOMOYO_TYPE_CREATE_ACL 4
-#define TOMOYO_TYPE_UNLINK_ACL 5
-#define TOMOYO_TYPE_MKDIR_ACL 6
-#define TOMOYO_TYPE_RMDIR_ACL 7
-#define TOMOYO_TYPE_MKFIFO_ACL 8
-#define TOMOYO_TYPE_MKSOCK_ACL 9
-#define TOMOYO_TYPE_MKBLOCK_ACL 10
-#define TOMOYO_TYPE_MKCHAR_ACL 11
-#define TOMOYO_TYPE_TRUNCATE_ACL 12
-#define TOMOYO_TYPE_SYMLINK_ACL 13
-#define TOMOYO_TYPE_REWRITE_ACL 14
-#define TOMOYO_MAX_SINGLE_PATH_OPERATION 15
-
-#define TOMOYO_TYPE_LINK_ACL 0
-#define TOMOYO_TYPE_RENAME_ACL 1
-#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
-
-#define TOMOYO_DOMAINPOLICY 0
-#define TOMOYO_EXCEPTIONPOLICY 1
-#define TOMOYO_DOMAIN_STATUS 2
-#define TOMOYO_PROCESS_STATUS 3
-#define TOMOYO_MEMINFO 4
-#define TOMOYO_SELFDOMAIN 5
-#define TOMOYO_VERSION 6
-#define TOMOYO_PROFILE 7
-#define TOMOYO_MANAGER 8
-
-extern struct tomoyo_domain_info tomoyo_kernel_domain;
-
-static inline struct tomoyo_domain_info *tomoyo_domain(void)
-{
- return current_cred()->security;
-}
-
-static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct
- *task)
-{
- return task_cred_xxx(task, security);
-}
-
-#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */