* Check permissions on incoming network packets. This hook is distinct
* from Netfilter's IP input hooks since it is the first time that the
* incoming sk_buff @skb has been associated with a particular socket, @sk.
+ * Must not sleep inside this hook because some callers hold spinlocks.
* @sk contains the sock (not socket) associated with the incoming sk_buff.
* @skb contains the incoming network data.
* @socket_getpeersec_stream:
if (pid && (pid != task_pid_vnr(current))) {
struct task_struct *target;
- read_lock(&tasklist_lock);
+ rcu_read_lock();
target = find_task_by_vpid(pid);
if (!target)
else
ret = security_capget(target, pEp, pIp, pPp);
- read_unlock(&tasklist_lock);
+ rcu_read_unlock();
} else
ret = security_capget(current, pEp, pIp, pPp);
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,
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);
}
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 {
}
rcu_read_unlock();
-out:
return rc;
}
/* 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);
char *scon, *tcon;
u32 ssid, tsid;
u16 tclass;
- u32 req;
struct av_decision avd;
ssize_t length;
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);
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",
case AVTAB_MEMBER:
/* Use the process effective MLS attributes. */
return mls_context_cpy_low(newcontext, scontext);
- default:
- return -EINVAL;
+
+ /* fall through */
}
return -EINVAL;
}
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 */
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)
{
static void type_attribute_bounds_av(struct context *scontext,
struct context *tcontext,
u16 tclass,
- u32 requested,
struct av_decision *avd)
{
struct context lo_scontext;
context_struct_compute_av(&lo_scontext,
tcontext,
tclass,
- requested,
&lo_avd);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
context_struct_compute_av(scontext,
&lo_tcontext,
tclass,
- requested,
&lo_avd);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
context_struct_compute_av(&lo_scontext,
&lo_tcontext,
tclass,
- requested,
&lo_avd);
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
return; /* no masked permission */
* 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;
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];
* 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,
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;
}
/*
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;
}
*nperms = match->permissions.nprim;
- *perms = kcalloc(*nperms, sizeof(*perms), GFP_ATOMIC);
+ *perms = kcalloc(*nperms, sizeof(**perms), GFP_ATOMIC);
if (!*perms)
goto out;
#include "common.h"
#include "tomoyo.h"
+/* Lock for protecting policy. */
+DEFINE_MUTEX(tomoyo_policy_lock);
+
/* Has loading policy done? */
bool tomoyo_policy_loaded;
*
* @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)
{
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;
* @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)
{
if (!domain)
return true;
- down_read(&tomoyo_domain_acl_info_list_lock);
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(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;
+ struct tomoyo_single_path_acl_record *acl;
+ u32 perm;
+ u8 i;
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++;
+ acl = container_of(ptr,
+ struct tomoyo_single_path_acl_record,
+ head);
+ perm = acl->perm | (((u32) acl->perm_high) << 16);
+ for (i = 0; i < TOMOYO_MAX_SINGLE_PATH_OPERATION; i++)
+ if (perm & (1 << i))
+ count++;
+ if (perm & (1 << TOMOYO_TYPE_READ_WRITE_ACL))
+ count -= 2;
break;
case TOMOYO_TYPE_DOUBLE_PATH_ACL:
- acl2 = container_of(ptr,
+ perm = 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++;
+ head)->perm;
+ for (i = 0; i < TOMOYO_MAX_DOUBLE_PATH_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) {
ptr = tomoyo_profile_ptr[profile];
if (ptr)
goto ok;
- ptr = tomoyo_alloc_element(sizeof(*ptr));
- if (!ptr)
+ ptr = kmalloc(sizeof(*ptr), GFP_KERNEL);
+ if (!tomoyo_memory_ok(ptr)) {
+ kfree(ptr);
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. */
* # cat /sys/kernel/security/tomoyo/manager
*/
static LIST_HEAD(tomoyo_policy_manager_list);
-static DECLARE_RWSEM(tomoyo_policy_manager_list_lock);
/**
* tomoyo_update_manager_entry - Add a manager entry.
* @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)
saved_manager = tomoyo_save_name(manager);
if (!saved_manager)
return -ENOMEM;
- down_write(&tomoyo_policy_manager_list_lock);
- list_for_each_entry(ptr, &tomoyo_policy_manager_list, list) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) {
if (ptr->manager != saved_manager)
continue;
ptr->is_deleted = is_delete;
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(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);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_policy_manager_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_policy_manager_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
* @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)
{
* @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)
{
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;
if (!done)
break;
}
- up_read(&tomoyo_policy_manager_list_lock);
head->read_eof = done;
return 0;
}
*
* 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)
{
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;
* @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)
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
} 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;
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;
* @domainname: The name of domain.
*
* Returns 0.
+ *
+ * Caller holds tomoyo_read_lock().
*/
static int tomoyo_delete_domain(char *domainname)
{
name.name = domainname;
tomoyo_fill_path_info(&name);
- down_write(&tomoyo_domain_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
/* 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;
domain->is_deleted = true;
break;
}
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return 0;
}
* @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)
{
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;
u8 bit;
const char *atmark = "";
const char *filename;
- const u16 perm = ptr->perm;
+ const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16);
filename = ptr->filename->name;
for (bit = head->read_bit; bit < TOMOYO_MAX_SINGLE_PATH_OPERATION;
* @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)
{
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 = "";
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
if (!done)
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
if (!done)
break;
head->read_step = 3;
if (head->read_single_domain)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
*
* ( 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)
{
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
* 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)
{
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);
if (!done)
break;
}
- up_read(&tomoyo_domain_list_lock);
head->read_eof = done;
return 0;
}
* @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)
{
* @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)
{
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);
}
}
* @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)
{
return -ENOMEM;
}
}
+ head->reader_idx = tomoyo_read_lock();
file->private_data = head;
/*
* Call the handler now if the file is
* @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)
* @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)
* @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;
+ tomoyo_read_unlock(head->reader_idx);
/* Release memory used for policy I/O. */
tomoyo_free(head->read_buf);
head->read_buf = NULL;
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.
*
* (b) type & 0x80 : whether the entry is marked as "deleted".
*
* Packing "struct tomoyo_acl_info" allows
- * "struct tomoyo_single_path_acl_record" to embed "u16" and
+ * "struct tomoyo_single_path_acl_record" to embed "u8" + "u16" and
* "struct tomoyo_double_path_acl_record" to embed "u8"
* without enlarging their structure size.
*/
* 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 */
+ u8 perm_high;
u16 perm;
/* Pointer to single pathname. */
const struct tomoyo_path_info *filename;
/*
* tomoyo_double_path_acl_record is a structure which is used for holding an
- * entry with two pathnames operation (i.e. link() and rename()).
+ * 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".
* (3) "filename1" is the source/old pathname.
* (4) "filename2" 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 */
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. */
/* 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. */
/* The list for "struct tomoyo_domain_info". */
extern struct list_head tomoyo_domain_list;
-extern struct rw_semaphore tomoyo_domain_list_lock;
-/* Lock for domain->acl_info_list. */
-extern struct rw_semaphore tomoyo_domain_acl_info_list_lock;
+/* Lock for protecting policy. */
+extern struct mutex tomoyo_policy_lock;
/* Has /sbin/init started? */
extern bool tomoyo_policy_loaded;
* @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))
+
+extern struct srcu_struct tomoyo_ss;
+
+static inline int tomoyo_read_lock(void)
+{
+ return srcu_read_lock(&tomoyo_ss);
+}
+
+static inline void tomoyo_read_unlock(int idx)
+{
+ srcu_read_unlock(&tomoyo_ss, idx);
+}
#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */
* exceptions.
*/
LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
/*
* tomoyo_domain_initializer_entry is a structure which is used for holding
* 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);
/**
* tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
* @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,
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) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(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);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_domain_initializer_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
* @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;
if (!done)
break;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return done;
}
* @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)
*
* 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,
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) {
}
flag = true;
}
- up_read(&tomoyo_domain_initializer_list_lock);
return flag;
}
* explicitly specified by "initialize_domain".
*/
static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
/**
* tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
* @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,
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) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
if (ptr->is_not != is_not ||
ptr->domainname != saved_domainname ||
ptr->program != saved_program)
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(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);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_domain_keeper_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
* @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)
* @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;
if (!done)
break;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return done;
}
*
* 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,
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) {
}
flag = true;
}
- up_read(&tomoyo_domain_keeper_list_lock);
return flag;
}
* execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
*/
static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
/**
* tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
* @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,
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) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
if (ptr->original_name != saved_original_name ||
ptr->aliased_name != saved_aliased_name)
continue;
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(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);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_alias_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
* @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;
if (!done)
break;
}
- up_read(&tomoyo_alias_list_lock);
return done;
}
* @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)
{
* @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 *domain = NULL;
+ struct tomoyo_domain_info *domain;
const struct tomoyo_path_info *saved_domainname;
- down_write(&tomoyo_domain_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
domain = tomoyo_find_domain(domainname);
if (domain)
goto out;
saved_domainname = tomoyo_save_name(domainname);
if (!saved_domainname)
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)
- 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;
- }
- /* No memory reusable. Create using new memory. */
- domain = tomoyo_alloc_element(sizeof(*domain));
- if (domain) {
+ domain = kmalloc(sizeof(*domain), GFP_KERNEL);
+ if (tomoyo_memory_ok(domain)) {
INIT_LIST_HEAD(&domain->acl_info_list);
domain->domainname = saved_domainname;
domain->profile = profile;
- list_add_tail(&domain->list, &tomoyo_domain_list);
+ list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
+ } else {
+ kfree(domain);
+ domain = NULL;
}
+
out:
- up_write(&tomoyo_domain_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return domain;
}
* @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)
{
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))
tomoyo_fill_path_info(&r);
break;
}
- up_read(&tomoyo_alias_list_lock);
}
/* Check execute permission. */
}
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)
[TOMOYO_TYPE_TRUNCATE_ACL] = "truncate",
[TOMOYO_TYPE_SYMLINK_ACL] = "symlink",
[TOMOYO_TYPE_REWRITE_ACL] = "rewrite",
+ [TOMOYO_TYPE_IOCTL_ACL] = "ioctl",
+ [TOMOYO_TYPE_CHMOD_ACL] = "chmod",
+ [TOMOYO_TYPE_CHOWN_ACL] = "chown",
+ [TOMOYO_TYPE_CHGRP_ACL] = "chgrp",
+ [TOMOYO_TYPE_CHROOT_ACL] = "chroot",
+ [TOMOYO_TYPE_MOUNT_ACL] = "mount",
+ [TOMOYO_TYPE_UMOUNT_ACL] = "unmount",
};
/* 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",
+ [TOMOYO_TYPE_PIVOT_ROOT_ACL] = "pivot_root",
};
/**
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 *
* belongs to.
*/
static LIST_HEAD(tomoyo_globally_readable_list);
-static DECLARE_RWSEM(tomoyo_globally_readable_list_lock);
/**
* tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list.
* @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)
saved_filename = tomoyo_save_name(filename);
if (!saved_filename)
return -ENOMEM;
- down_write(&tomoyo_globally_readable_list_lock);
- list_for_each_entry(ptr, &tomoyo_globally_readable_list, list) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) {
if (ptr->filename != saved_filename)
continue;
ptr->is_deleted = is_delete;
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(new_entry))
goto out;
new_entry->filename = saved_filename;
- list_add_tail(&new_entry->list, &tomoyo_globally_readable_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_globally_readable_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_globally_readable_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
* @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;
}
* @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)
{
* @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;
if (!done)
break;
}
- up_read(&tomoyo_globally_readable_list_lock);
return done;
}
* current process from accessing other process's information.
*/
static LIST_HEAD(tomoyo_pattern_list);
-static DECLARE_RWSEM(tomoyo_pattern_list_lock);
/**
* tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list.
* @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)
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) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) {
if (saved_pattern != ptr->pattern)
continue;
ptr->is_deleted = is_delete;
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(new_entry))
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_pattern_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_pattern_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_pattern_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
* @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)
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))
break;
}
}
- up_read(&tomoyo_pattern_list_lock);
if (pattern)
filename = pattern;
return 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)
{
* @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);
if (!done)
break;
}
- up_read(&tomoyo_pattern_list_lock);
return done;
}
* 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);
/**
* tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list.
* @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)
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) {
+ new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) {
if (ptr->pattern != saved_pattern)
continue;
ptr->is_deleted = is_delete;
error = -ENOENT;
goto out;
}
- new_entry = tomoyo_alloc_element(sizeof(*new_entry));
- if (!new_entry)
+ if (!tomoyo_memory_ok(new_entry))
goto out;
new_entry->pattern = saved_pattern;
- list_add_tail(&new_entry->list, &tomoyo_no_rewrite_list);
+ list_add_tail_rcu(&new_entry->list, &tomoyo_no_rewrite_list);
+ new_entry = NULL;
error = 0;
out:
- up_write(&tomoyo_no_rewrite_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
+ kfree(new_entry);
return error;
}
*
* 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))
found = true;
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return found;
}
* @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)
{
* @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);
if (!done)
break;
}
- up_read(&tomoyo_no_rewrite_list_lock);
return done;
}
* 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,
* @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 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) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_single_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_SINGLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_single_path_acl_record,
head);
- if (!(acl->perm & perm))
- continue;
+ if (perm <= 0xFFFF) {
+ if (!(acl->perm & perm))
+ continue;
+ } else {
+ if (!(acl->perm_high & (perm >> 16)))
+ continue;
+ }
if (may_use_pattern || !acl->filename->is_patterned) {
if (!tomoyo_path_matches_pattern(filename,
acl->filename))
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
* @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;
* @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,
* @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)
* @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 const u16 rw_mask =
+ static const u32 rw_mask =
(1 << TOMOYO_TYPE_READ_ACL) | (1 << TOMOYO_TYPE_WRITE_ACL);
const struct tomoyo_path_info *saved_filename;
struct tomoyo_acl_info *ptr;
struct tomoyo_single_path_acl_record *acl;
int error = -ENOMEM;
- const u16 perm = 1 << type;
+ const u32 perm = 1 << type;
if (!domain)
return -EINVAL;
saved_filename = tomoyo_save_name(filename);
if (!saved_filename)
return -ENOMEM;
- down_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(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,
/* Special case. Clear all bits if marked as deleted. */
if (ptr->type & TOMOYO_ACL_DELETED)
acl->perm = 0;
- acl->perm |= perm;
+ if (perm <= 0xFFFF)
+ acl->perm |= perm;
+ else
+ acl->perm_high |= (perm >> 16);
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))
goto out;
}
/* Not found. Append it to the tail. */
- acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_SINGLE_PATH_ACL);
- if (!acl)
+ acl = kmalloc(sizeof(*acl), GFP_KERNEL);
+ if (!tomoyo_memory_ok(acl)) {
+ kfree(acl);
+ acl = NULL;
goto out;
- acl->perm = perm;
+ }
+ acl->head.type = TOMOYO_TYPE_SINGLE_PATH_ACL;
+ if (perm <= 0xFFFF)
+ acl->perm = perm;
+ else
+ acl->perm_high = (perm >> 16);
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);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(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)
continue;
- acl->perm &= ~perm;
+ if (perm <= 0xFFFF)
+ acl->perm &= ~perm;
+ else
+ acl->perm_high &= ~(perm >> 16);
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)
+ if (!acl->perm && !acl->perm_high)
ptr->type |= TOMOYO_ACL_DELETED;
error = 0;
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return error;
}
* @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,
saved_filename2 = tomoyo_save_name(filename2);
if (!saved_filename1 || !saved_filename2)
return -ENOMEM;
- down_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_lock(&tomoyo_policy_lock);
if (is_delete)
goto delete;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(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,
goto out;
}
/* Not found. Append it to the tail. */
- acl = tomoyo_alloc_acl_element(TOMOYO_TYPE_DOUBLE_PATH_ACL);
- if (!acl)
+ acl = kmalloc(sizeof(*acl), GFP_KERNEL);
+ if (!tomoyo_memory_ok(acl)) {
+ kfree(acl);
+ acl = NULL;
goto out;
+ }
+ acl->head.type = TOMOYO_TYPE_DOUBLE_PATH_ACL;
acl->perm = perm;
acl->filename1 = saved_filename1;
acl->filename2 = saved_filename2;
- list_add_tail(&acl->head.list, &domain->acl_info_list);
+ list_add_tail_rcu(&acl->head.list, &domain->acl_info_list);
error = 0;
goto out;
delete:
error = -ENOENT;
- list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
acl = container_of(ptr, struct tomoyo_double_path_acl_record,
break;
}
out:
- up_write(&tomoyo_domain_acl_info_list_lock);
+ mutex_unlock(&tomoyo_policy_lock);
return error;
}
* @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,
* @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,
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) {
+ list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) {
struct tomoyo_double_path_acl_record *acl;
if (tomoyo_acl_type2(ptr) != TOMOYO_TYPE_DOUBLE_PATH_ACL)
continue;
error = 0;
break;
}
- up_read(&tomoyo_domain_acl_info_list_lock);
return error;
}
* @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,
* @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)
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;
* don't call me.
*/
return 0;
+ idx = tomoyo_read_lock();
buf = tomoyo_get_path(path);
if (!buf)
goto out;
buf, mode);
out:
tomoyo_free(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_check_1path_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.
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;
+ 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_CHROOT_ACL:
if (!buf->is_dir) {
/*
* tomoyo_get_path() reserves space for appending "/."
mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
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;
buf, mode);
out:
tomoyo_free(buf);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
}
/**
- * tomoyo_check_2path_perm - Check permission for "rename" and "link".
+ * tomoyo_check_2path_perm - Check permission for "rename", "link" and "pivot_root".
*
* @domain: Pointer to "struct tomoyo_domain_info".
* @operation: Type of operation.
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)
out:
tomoyo_free(buf1);
tomoyo_free(buf2);
+ tomoyo_read_unlock(idx);
if (!is_enforce)
error = 0;
return error;
#include <linux/mnt_namespace.h>
#include <linux/fs_struct.h>
#include <linux/hash.h>
+#include <linux/magic.h>
#include "common.h"
#include "realpath.h"
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)) {
+ (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) {
sp -= 5;
if (sp >= newname)
memcpy(sp, "/proc", 5);
static unsigned int tomoyo_quota_for_elements;
/**
- * 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;
+ bool result = false;
+ if (!ptr || (tomoyo_quota_for_elements &&
+ tomoyo_allocated_memory_for_elements
+ + allocated_len > tomoyo_quota_for_elements)) {
+ printk(KERN_WARNING "ERROR: Out of memory "
+ "for tomoyo_alloc_element().\n");
+ if (!tomoyo_policy_loaded)
+ panic("MAC Initialization failed.\n");
+ } else {
+ result = true;
+ tomoyo_allocated_memory_for_elements += allocated_len;
+ memset(ptr, 0, allocated_len);
}
- mutex_unlock(&lock);
- return ptr;
+ return result;
}
/* Memory allocated for string data in bytes. */
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. */
-};
-
/*
* tomoyo_name_list is used for holding string data used by TOMOYO.
* Since same string data is likely used for multiple times (e.g.
* @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)
{
- 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);
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 (!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_KERNEL);
+ allocated_len = ptr ? ksize(ptr) : 0;
+ if (!ptr || (tomoyo_quota_for_savename &&
+ tomoyo_allocated_memory_for_savename + allocated_len
+ > tomoyo_quota_for_savename)) {
+ kfree(ptr);
printk(KERN_WARNING "ERROR: Out of memory "
"for tomoyo_save_name().\n");
if (!tomoyo_policy_loaded)
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);
+ tomoyo_allocated_memory_for_savename += allocated_len;
+ ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
+ memmove((char *) ptr->entry.name, name, len);
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);
return ptr ? &ptr->entry : NULL;
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_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. */
/* 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);
+/* Check memory quota. */
+bool tomoyo_memory_ok(void *ptr);
/*
* Keep the given name on the RAM.
* Execute permission is checked against pathname passed to do_execve()
* using current domain.
*/
- if (!domain)
- return tomoyo_find_next_domain(bprm);
+ if (!domain) {
+ /*
+ * We will need to protect whole execve() operation when GC
+ * starts kfree()ing "struct tomoyo_domain_info" because
+ * bprm->cred->security points to "struct tomoyo_domain_info"
+ * but "struct tomoyo_domain_info" does not have a refcounter.
+ */
+ 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.
* '1' is the result of open_to_namei_flags(O_RDONLY).
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_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_IOCTL_ACL,
+ &file->f_path);
+}
+
+static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt,
+ mode_t mode)
+{
+ struct path path = { mnt, dentry };
+ return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_CHMOD_ACL,
+ &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_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_CHOWN_ACL, path);
+ if (!error && gid != (gid_t) -1)
+ error = tomoyo_check_1path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_CHGRP_ACL, path);
+ return error;
+}
+
+static int tomoyo_path_chroot(struct path *path)
+{
+ return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_CHROOT_ACL,
+ path);
+}
+
+static int tomoyo_sb_mount(char *dev_name, struct path *path,
+ char *type, unsigned long flags, void *data)
+{
+ return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_MOUNT_ACL,
+ path);
+}
+
+static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
+{
+ struct path path = { mnt, mnt->mnt_root };
+ return tomoyo_check_1path_perm(tomoyo_domain(), TOMOYO_TYPE_UMOUNT_ACL,
+ &path);
+}
+
+static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path)
+{
+ return tomoyo_check_2path_perm(tomoyo_domain(),
+ TOMOYO_TYPE_PIVOT_ROOT_ACL,
+ new_path, old_path);
+}
+
/*
* tomoyo_security_ops is a "struct security_operations" which is used for
* registering TOMOYO.
.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();
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;
#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_IOCTL_ACL 15
+#define TOMOYO_TYPE_CHMOD_ACL 16
+#define TOMOYO_TYPE_CHOWN_ACL 17
+#define TOMOYO_TYPE_CHGRP_ACL 18
+#define TOMOYO_TYPE_CHROOT_ACL 19
+#define TOMOYO_TYPE_MOUNT_ACL 20
+#define TOMOYO_TYPE_UMOUNT_ACL 21
+#define TOMOYO_MAX_SINGLE_PATH_OPERATION 22
#define TOMOYO_TYPE_LINK_ACL 0
#define TOMOYO_TYPE_RENAME_ACL 1
-#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 2
+#define TOMOYO_TYPE_PIVOT_ROOT_ACL 2
+#define TOMOYO_MAX_DOUBLE_PATH_OPERATION 3
#define TOMOYO_DOMAINPOLICY 0
#define TOMOYO_EXCEPTIONPOLICY 1