Merge branch 'next' of git://selinuxproject.org/~jmorris/linux-security

* 'next' of git://selinuxproject.org/~jmorris/linux-security: (95 commits)
  TOMOYO: Fix incomplete read after seek.
  Smack: allow to access /smack/access as normal user
  TOMOYO: Fix unused kernel config option.
  Smack: fix: invalid length set for the result of /smack/access
  Smack: compilation fix
  Smack: fix for /smack/access output, use string instead of byte
  Smack: domain transition protections (v3)
  Smack: Provide information for UDS getsockopt(SO_PEERCRED)
  Smack: Clean up comments
  Smack: Repair processing of fcntl
  Smack: Rule list lookup performance
  Smack: check permissions from user space (v2)
  TOMOYO: Fix quota and garbage collector.
  TOMOYO: Remove redundant tasklist_lock.
  TOMOYO: Fix domain transition failure warning.
  TOMOYO: Remove tomoyo_policy_memory_lock spinlock.
  TOMOYO: Simplify garbage collector.
  TOMOYO: Fix make namespacecheck warnings.
  target: check hex2bin result
  encrypted-keys: check hex2bin result
  ...
This commit is contained in:
Linus Torvalds 2011-10-25 09:45:31 +02:00
commit 36b8d186e6
99 changed files with 4701 additions and 1432 deletions

View file

@ -1,8 +1,10 @@
config SECURITY_TOMOYO
bool "TOMOYO Linux Support"
depends on SECURITY
depends on NET
select SECURITYFS
select SECURITY_PATH
select SECURITY_NETWORK
default n
help
This selects TOMOYO Linux, pathname-based access control.

View file

@ -1,4 +1,4 @@
obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o
obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o
$(obj)/policy/profile.conf:
@mkdir -p $(obj)/policy/
@ -27,7 +27,7 @@ $(obj)/policy/stat.conf:
@touch $@
$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf
@echo Generating built-in policy for TOMOYO 2.4.x.
@echo Generating built-in policy for TOMOYO 2.5.x.
@echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp
@echo "\"\";" >> $@.tmp

View file

@ -313,6 +313,7 @@ static unsigned int tomoyo_log_count;
*/
static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
const u8 profile, const u8 index,
const struct tomoyo_acl_info *matched_acl,
const bool is_granted)
{
u8 mode;
@ -324,6 +325,9 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns,
p = tomoyo_profile(ns, profile);
if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG])
return false;
if (is_granted && matched_acl && matched_acl->cond &&
matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO)
return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES;
mode = p->config[index];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
mode = p->config[category];
@ -350,7 +354,8 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
char *buf;
struct tomoyo_log *entry;
bool quota_exceeded = false;
if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted))
if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type,
r->matched_acl, r->granted))
goto out;
buf = tomoyo_init_log(r, len, fmt, args);
if (!buf)

View file

@ -20,6 +20,7 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = {
/* String table for /sys/kernel/security/tomoyo/profile */
const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
/* CONFIG::file group */
[TOMOYO_MAC_FILE_EXECUTE] = "execute",
[TOMOYO_MAC_FILE_OPEN] = "open",
[TOMOYO_MAC_FILE_CREATE] = "create",
@ -43,7 +44,28 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
[TOMOYO_MAC_FILE_MOUNT] = "mount",
[TOMOYO_MAC_FILE_UMOUNT] = "unmount",
[TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root",
/* CONFIG::network group */
[TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind",
[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen",
[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect",
[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind",
[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send",
[TOMOYO_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind",
[TOMOYO_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send",
[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind",
[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen",
[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect",
[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind",
[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send",
[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind",
[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen",
[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect",
/* CONFIG::misc group */
[TOMOYO_MAC_ENVIRON] = "env",
/* CONFIG group */
[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network",
[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc",
};
/* String table for conditions. */
@ -130,10 +152,20 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = {
[TOMOYO_TYPE_UMOUNT] = "unmount",
};
/* String table for socket's operation. */
const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = {
[TOMOYO_NETWORK_BIND] = "bind",
[TOMOYO_NETWORK_LISTEN] = "listen",
[TOMOYO_NETWORK_CONNECT] = "connect",
[TOMOYO_NETWORK_SEND] = "send",
};
/* String table for categories. */
static const char * const tomoyo_category_keywords
[TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
[TOMOYO_MAC_CATEGORY_FILE] = "file",
[TOMOYO_MAC_CATEGORY_FILE] = "file",
[TOMOYO_MAC_CATEGORY_NETWORK] = "network",
[TOMOYO_MAC_CATEGORY_MISC] = "misc",
};
/* Permit policy management by non-root user? */
@ -230,13 +262,17 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)
WARN_ON(1);
}
static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
...) __printf(2, 3);
/**
* tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @fmt: The printf()'s format string, followed by parameters.
*/
void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt,
...)
{
va_list args;
size_t len;
@ -313,7 +349,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns)
INIT_LIST_HEAD(&ns->group_list[idx]);
for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++)
INIT_LIST_HEAD(&ns->policy_list[idx]);
ns->profile_version = 20100903;
ns->profile_version = 20110903;
tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list);
list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list);
}
@ -466,8 +502,10 @@ static struct tomoyo_profile *tomoyo_assign_profile
TOMOYO_CONFIG_WANT_REJECT_LOG;
memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
sizeof(ptr->config));
ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024;
ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;
ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] =
CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG;
ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] =
CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY;
mb(); /* Avoid out-of-order execution. */
ns->profile_ptr[profile] = ptr;
entry = NULL;
@ -951,14 +989,12 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
(global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {
struct task_struct *p;
rcu_read_lock();
read_lock(&tasklist_lock);
if (global_pid)
p = find_task_by_pid_ns(pid, &init_pid_ns);
else
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_domain_def(data + 7))
@ -981,6 +1017,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
return true;
}
/**
* tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
*
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
return p1->domainname == p2->domainname;
}
/**
* tomoyo_write_task - Update task related list.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_task(struct tomoyo_acl_param *param)
{
int error = -EINVAL;
if (tomoyo_str_starts(&param->data, "manual_domain_transition ")) {
struct tomoyo_task_acl e = {
.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
.domainname = tomoyo_get_domainname(param),
};
if (e.domainname)
error = tomoyo_update_domain(&e.head, sizeof(e), param,
tomoyo_same_task_acl,
NULL);
tomoyo_put_name(e.domainname);
}
return error;
}
/**
* tomoyo_delete_domain - Delete a domain.
*
@ -1039,11 +1117,16 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
static const struct {
const char *keyword;
int (*write) (struct tomoyo_acl_param *);
} tomoyo_callback[1] = {
} tomoyo_callback[5] = {
{ "file ", tomoyo_write_file },
{ "network inet ", tomoyo_write_inet_network },
{ "network unix ", tomoyo_write_unix_network },
{ "misc ", tomoyo_write_misc },
{ "task ", tomoyo_write_task },
};
u8 i;
for (i = 0; i < 1; i++) {
for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) {
if (!tomoyo_str_starts(&param.data,
tomoyo_callback[i].keyword))
continue;
@ -1127,6 +1210,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
case 0:
head->r.cond_index = 0;
head->r.cond_step++;
if (cond->transit) {
tomoyo_set_space(head);
tomoyo_set_string(head, cond->transit->name);
}
/* fall through */
case 1:
{
@ -1239,6 +1326,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
head->r.cond_step++;
/* fall through */
case 3:
if (cond->grant_log != TOMOYO_GRANTLOG_AUTO)
tomoyo_io_printf(head, " grant_log=%s",
tomoyo_yesno(cond->grant_log ==
TOMOYO_GRANTLOG_YES));
tomoyo_set_lf(head);
return true;
}
@ -1306,6 +1397,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
if (first)
return true;
tomoyo_print_name_union(head, &ptr->name);
} else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
struct tomoyo_task_acl *ptr =
container_of(acl, typeof(*ptr), head);
tomoyo_set_group(head, "task ");
tomoyo_set_string(head, "manual_domain_transition ");
tomoyo_set_string(head, ptr->domainname->name);
} else if (head->r.print_transition_related_only) {
return true;
} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
@ -1370,6 +1467,60 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
tomoyo_print_number_union(head, &ptr->mode);
tomoyo_print_number_union(head, &ptr->major);
tomoyo_print_number_union(head, &ptr->minor);
} else if (acl_type == TOMOYO_TYPE_INET_ACL) {
struct tomoyo_inet_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u8 perm = ptr->perm;
for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
if (first) {
tomoyo_set_group(head, "network inet ");
tomoyo_set_string(head, tomoyo_proto_keyword
[ptr->protocol]);
tomoyo_set_space(head);
first = false;
} else {
tomoyo_set_slash(head);
}
tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
}
if (first)
return true;
tomoyo_set_space(head);
if (ptr->address.group) {
tomoyo_set_string(head, "@");
tomoyo_set_string(head, ptr->address.group->group_name
->name);
} else {
char buf[128];
tomoyo_print_ip(buf, sizeof(buf), &ptr->address);
tomoyo_io_printf(head, "%s", buf);
}
tomoyo_print_number_union(head, &ptr->port);
} else if (acl_type == TOMOYO_TYPE_UNIX_ACL) {
struct tomoyo_unix_acl *ptr =
container_of(acl, typeof(*ptr), head);
const u8 perm = ptr->perm;
for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) {
if (!(perm & (1 << bit)))
continue;
if (first) {
tomoyo_set_group(head, "network unix ");
tomoyo_set_string(head, tomoyo_proto_keyword
[ptr->protocol]);
tomoyo_set_space(head);
first = false;
} else {
tomoyo_set_slash(head);
}
tomoyo_set_string(head, tomoyo_socket_keyword[bit]);
}
if (first)
return true;
tomoyo_print_name_union(head, &ptr->name);
} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {
struct tomoyo_mount_acl *ptr =
container_of(acl, typeof(*ptr), head);
@ -1378,6 +1529,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
tomoyo_print_name_union(head, &ptr->dir_name);
tomoyo_print_name_union(head, &ptr->fs_type);
tomoyo_print_number_union(head, &ptr->flags);
} else if (acl_type == TOMOYO_TYPE_ENV_ACL) {
struct tomoyo_env_acl *ptr =
container_of(acl, typeof(*ptr), head);
tomoyo_set_group(head, "misc env ");
tomoyo_set_string(head, ptr->env->name);
}
if (acl->cond) {
head->r.print_cond_part = true;
@ -1510,14 +1667,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)
global_pid = true;
pid = (unsigned int) simple_strtoul(buf, NULL, 10);
rcu_read_lock();
read_lock(&tasklist_lock);
if (global_pid)
p = find_task_by_pid_ns(pid, &init_pid_ns);
else
p = find_task_by_vpid(pid);
if (p)
domain = tomoyo_real_domain(p);
read_unlock(&tasklist_lock);
rcu_read_unlock();
if (!domain)
return;
@ -1537,8 +1692,9 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = {
/* String table for grouping keywords. */
static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {
[TOMOYO_PATH_GROUP] = "path_group ",
[TOMOYO_NUMBER_GROUP] = "number_group ",
[TOMOYO_PATH_GROUP] = "path_group ",
[TOMOYO_NUMBER_GROUP] = "number_group ",
[TOMOYO_ADDRESS_GROUP] = "address_group ",
};
/**
@ -1580,7 +1736,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)
}
/**
* tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
* tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
*
* @head: Pointer to "struct tomoyo_io_buffer".
* @idx: Index number.
@ -1617,6 +1773,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)
(ptr,
struct tomoyo_number_group,
head)->number);
} else if (idx == TOMOYO_ADDRESS_GROUP) {
char buffer[128];
struct tomoyo_address_group *member =
container_of(ptr, typeof(*member),
head);
tomoyo_print_ip(buffer, sizeof(buffer),
&member->address);
tomoyo_io_printf(head, " %s", buffer);
}
tomoyo_set_lf(head);
}
@ -2066,27 +2231,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)
static void tomoyo_read_version(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
tomoyo_io_printf(head, "2.4.0");
head->r.eof = true;
}
}
/**
* tomoyo_read_self_domain - Get the current process's domainname.
*
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns the current process's domainname.
*/
static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
/*
* tomoyo_domain()->domainname != NULL
* because every process belongs to a domain and
* the domain's name cannot be NULL.
*/
tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
tomoyo_io_printf(head, "2.5.0");
head->r.eof = true;
}
}
@ -2221,10 +2366,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
head->poll = tomoyo_poll_log;
head->read = tomoyo_read_log;
break;
case TOMOYO_SELFDOMAIN:
/* /sys/kernel/security/tomoyo/self_domain */
head->read = tomoyo_read_self_domain;
break;
case TOMOYO_PROCESS_STATUS:
/* /sys/kernel/security/tomoyo/.process_status */
head->write = tomoyo_write_pid;
@ -2453,6 +2594,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
return -EFAULT;
if (mutex_lock_interruptible(&head->io_sem))
return -EINTR;
head->read_user_buf_avail = 0;
idx = tomoyo_read_lock();
/* Read a line and dispatch it to the policy handler. */
while (avail_len > 0) {
@ -2562,11 +2704,11 @@ void tomoyo_check_profile(void)
struct tomoyo_domain_info *domain;
const int idx = tomoyo_read_lock();
tomoyo_policy_loaded = true;
printk(KERN_INFO "TOMOYO: 2.4.0\n");
printk(KERN_INFO "TOMOYO: 2.5.0\n");
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
const u8 profile = domain->profile;
const struct tomoyo_policy_namespace *ns = domain->ns;
if (ns->profile_version != 20100903)
if (ns->profile_version != 20110903)
printk(KERN_ERR
"Profile version %u is not supported.\n",
ns->profile_version);
@ -2577,9 +2719,9 @@ void tomoyo_check_profile(void)
else
continue;
printk(KERN_ERR
"Userland tools for TOMOYO 2.4 must be installed and "
"Userland tools for TOMOYO 2.5 must be installed and "
"policy must be initialized.\n");
printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "
printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ "
"for more information.\n");
panic("STOP!");
}

View file

@ -3,7 +3,7 @@
*
* Header file for TOMOYO.
*
* Copyright (C) 2005-2010 NTT DATA CORPORATION
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#ifndef _SECURITY_TOMOYO_COMMON_H
@ -23,6 +23,16 @@
#include <linux/poll.h>
#include <linux/binfmts.h>
#include <linux/highmem.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/un.h>
#include <net/sock.h>
#include <net/af_unix.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/udp.h>
/********** Constants definitions. **********/
@ -34,8 +44,17 @@
#define TOMOYO_HASH_BITS 8
#define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS)
/*
* TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET.
* Therefore, we don't need SOCK_MAX.
*/
#define TOMOYO_SOCK_MAX 6
#define TOMOYO_EXEC_TMPSIZE 4096
/* Garbage collector is trying to kfree() this element. */
#define TOMOYO_GC_IN_PROGRESS -1
/* Profile number is an integer between 0 and 255. */
#define TOMOYO_MAX_PROFILES 256
@ -136,6 +155,7 @@ enum tomoyo_mode_index {
/* Index numbers for entry type. */
enum tomoyo_policy_id {
TOMOYO_ID_GROUP,
TOMOYO_ID_ADDRESS_GROUP,
TOMOYO_ID_PATH_GROUP,
TOMOYO_ID_NUMBER_GROUP,
TOMOYO_ID_TRANSITION_CONTROL,
@ -162,10 +182,21 @@ enum tomoyo_domain_info_flags_index {
TOMOYO_MAX_DOMAIN_INFO_FLAGS
};
/* Index numbers for audit type. */
enum tomoyo_grant_log {
/* Follow profile's configuration. */
TOMOYO_GRANTLOG_AUTO,
/* Do not generate grant log. */
TOMOYO_GRANTLOG_NO,
/* Generate grant_log. */
TOMOYO_GRANTLOG_YES,
};
/* Index numbers for group entries. */
enum tomoyo_group_id {
TOMOYO_PATH_GROUP,
TOMOYO_NUMBER_GROUP,
TOMOYO_ADDRESS_GROUP,
TOMOYO_MAX_GROUP
};
@ -196,6 +227,10 @@ enum tomoyo_acl_entry_type_index {
TOMOYO_TYPE_PATH_NUMBER_ACL,
TOMOYO_TYPE_MKDEV_ACL,
TOMOYO_TYPE_MOUNT_ACL,
TOMOYO_TYPE_INET_ACL,
TOMOYO_TYPE_UNIX_ACL,
TOMOYO_TYPE_ENV_ACL,
TOMOYO_TYPE_MANUAL_TASK_ACL,
};
/* Index numbers for access controls with one pathname. */
@ -228,6 +263,15 @@ enum tomoyo_mkdev_acl_index {
TOMOYO_MAX_MKDEV_OPERATION
};
/* Index numbers for socket operations. */
enum tomoyo_network_acl_index {
TOMOYO_NETWORK_BIND, /* bind() operation. */
TOMOYO_NETWORK_LISTEN, /* listen() operation. */
TOMOYO_NETWORK_CONNECT, /* connect() operation. */
TOMOYO_NETWORK_SEND, /* send() operation. */
TOMOYO_MAX_NETWORK_OPERATION
};
/* Index numbers for access controls with two pathnames. */
enum tomoyo_path2_acl_index {
TOMOYO_TYPE_LINK,
@ -255,7 +299,6 @@ enum tomoyo_securityfs_interface_index {
TOMOYO_EXCEPTIONPOLICY,
TOMOYO_PROCESS_STATUS,
TOMOYO_STAT,
TOMOYO_SELFDOMAIN,
TOMOYO_AUDIT,
TOMOYO_VERSION,
TOMOYO_PROFILE,
@ -300,12 +343,30 @@ enum tomoyo_mac_index {
TOMOYO_MAC_FILE_MOUNT,
TOMOYO_MAC_FILE_UMOUNT,
TOMOYO_MAC_FILE_PIVOT_ROOT,
TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
TOMOYO_MAC_NETWORK_INET_RAW_BIND,
TOMOYO_MAC_NETWORK_INET_RAW_SEND,
TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
TOMOYO_MAC_ENVIRON,
TOMOYO_MAX_MAC_INDEX
};
/* Index numbers for category of functionality. */
enum tomoyo_mac_category_index {
TOMOYO_MAC_CATEGORY_FILE,
TOMOYO_MAC_CATEGORY_NETWORK,
TOMOYO_MAC_CATEGORY_MISC,
TOMOYO_MAX_MAC_CATEGORY_INDEX
};
@ -340,7 +401,7 @@ enum tomoyo_pref_index {
/* Common header for holding ACL entries. */
struct tomoyo_acl_head {
struct list_head list;
bool is_deleted;
s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */
} __packed;
/* Common header for shared entries. */
@ -396,6 +457,25 @@ struct tomoyo_request_info {
*/
u8 operation;
} path_number;
struct {
const struct tomoyo_path_info *name;
} environ;
struct {
const __be32 *address;
u16 port;
/* One of values smaller than TOMOYO_SOCK_MAX. */
u8 protocol;
/* One of values in "enum tomoyo_network_acl_index". */
u8 operation;
bool is_ipv6;
} inet_network;
struct {
const struct tomoyo_path_info *address;
/* One of values smaller than TOMOYO_SOCK_MAX. */
u8 protocol;
/* One of values in "enum tomoyo_network_acl_index". */
u8 operation;
} unix_network;
struct {
const struct tomoyo_path_info *type;
const struct tomoyo_path_info *dir;
@ -403,7 +483,11 @@ struct tomoyo_request_info {
unsigned long flags;
int need_dev;
} mount;
struct {
const struct tomoyo_path_info *domainname;
} task;
} param;
struct tomoyo_acl_info *matched_acl;
u8 param_type;
bool granted;
u8 retry;
@ -442,7 +526,14 @@ struct tomoyo_number_union {
u8 value_type[2];
};
/* Structure for "path_group"/"number_group" directive. */
/* Structure for holding an IP address. */
struct tomoyo_ipaddr_union {
struct in6_addr ip[2]; /* Big endian. */
struct tomoyo_group *group; /* Pointer to address group. */
bool is_ipv6; /* Valid only if @group == NULL. */
};
/* Structure for "path_group"/"number_group"/"address_group" directive. */
struct tomoyo_group {
struct tomoyo_shared_acl_head head;
const struct tomoyo_path_info *group_name;
@ -461,6 +552,13 @@ struct tomoyo_number_group {
struct tomoyo_number_union number;
};
/* Structure for "address_group" directive. */
struct tomoyo_address_group {
struct tomoyo_acl_head head;
/* Structure for holding an IP address. */
struct tomoyo_ipaddr_union address;
};
/* Subset of "struct stat". Used by conditional ACL and audit logs. */
struct tomoyo_mini_stat {
uid_t uid;
@ -520,6 +618,7 @@ struct tomoyo_execve {
struct tomoyo_request_info r;
struct tomoyo_obj_info obj;
struct linux_binprm *bprm;
const struct tomoyo_path_info *transition;
/* For dumping argv[] and envp[]. */
struct tomoyo_page_dump dump;
/* For temporary use. */
@ -554,6 +653,8 @@ struct tomoyo_condition {
u16 names_count; /* Number of "struct tomoyo_name_union names". */
u16 argc; /* Number of "struct tomoyo_argv". */
u16 envc; /* Number of "struct tomoyo_envp". */
u8 grant_log; /* One of values in "enum tomoyo_grant_log". */
const struct tomoyo_path_info *transit; /* Maybe NULL. */
/*
* struct tomoyo_condition_element condition[condc];
* struct tomoyo_number_union values[numbers_count];
@ -567,7 +668,7 @@ struct tomoyo_condition {
struct tomoyo_acl_info {
struct list_head list;
struct tomoyo_condition *cond; /* Maybe NULL. */
bool is_deleted;
s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */
u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */
} __packed;
@ -586,6 +687,15 @@ struct tomoyo_domain_info {
atomic_t users; /* Number of referring credentials. */
};
/*
* Structure for "task manual_domain_transition" directive.
*/
struct tomoyo_task_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */
/* Pointer to domainname. */
const struct tomoyo_path_info *domainname;
};
/*
* Structure for "file execute", "file read", "file write", "file append",
* "file unlink", "file getattr", "file rmdir", "file truncate",
@ -638,6 +748,29 @@ struct tomoyo_mount_acl {
struct tomoyo_number_union flags;
};
/* Structure for "misc env" directive in domain policy. */
struct tomoyo_env_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_ENV_ACL */
const struct tomoyo_path_info *env; /* environment variable */
};
/* Structure for "network inet" directive. */
struct tomoyo_inet_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_INET_ACL */
u8 protocol;
u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
struct tomoyo_ipaddr_union address;
struct tomoyo_number_union port;
};
/* Structure for "network unix" directive. */
struct tomoyo_unix_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_UNIX_ACL */
u8 protocol;
u8 perm; /* Bitmask of values in "enum tomoyo_network_acl_index" */
struct tomoyo_name_union name;
};
/* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */
struct tomoyo_acl_param {
char *data;
@ -773,7 +906,7 @@ struct tomoyo_policy_namespace {
struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS];
/* List for connecting to tomoyo_namespace_list list. */
struct list_head namespace_list;
/* Profile version. Currently only 20100903 is defined. */
/* Profile version. Currently only 20110903 is defined. */
unsigned int profile_version;
/* Name of this namespace (e.g. "<kernel>", "</usr/sbin/httpd>" ). */
const char *name;
@ -781,6 +914,8 @@ struct tomoyo_policy_namespace {
/********** Function prototypes. **********/
bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
const struct tomoyo_group *group);
bool tomoyo_compare_number_union(const unsigned long value,
const struct tomoyo_number_union *ptr);
bool tomoyo_condition(struct tomoyo_request_info *r,
@ -796,6 +931,8 @@ bool tomoyo_memory_ok(void *ptr);
bool tomoyo_number_matches_group(const unsigned long min,
const unsigned long max,
const struct tomoyo_group *group);
bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
struct tomoyo_ipaddr_union *ptr);
bool tomoyo_parse_name_union(struct tomoyo_acl_param *param,
struct tomoyo_name_union *ptr);
bool tomoyo_parse_number_union(struct tomoyo_acl_param *param,
@ -805,6 +942,7 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename,
bool tomoyo_permstr(const char *string, const char *keyword);
bool tomoyo_str_starts(char **src, const char *find);
char *tomoyo_encode(const char *str);
char *tomoyo_encode2(const char *str, int str_len);
char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
va_list args);
char *tomoyo_read_token(struct tomoyo_acl_param *param);
@ -814,12 +952,17 @@ const char *tomoyo_get_exe(void);
const char *tomoyo_yesno(const unsigned int value);
const struct tomoyo_path_info *tomoyo_compare_name_union
(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
const struct tomoyo_path_info *tomoyo_get_domainname
(struct tomoyo_acl_param *param);
const struct tomoyo_path_info *tomoyo_get_name(const char *name);
const struct tomoyo_path_info *tomoyo_path_matches_group
(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
struct path *path, const int flag);
int tomoyo_close_control(struct tomoyo_io_buffer *head);
int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env);
int tomoyo_execute_permission(struct tomoyo_request_info *r,
const struct tomoyo_path_info *filename);
int tomoyo_find_next_domain(struct linux_binprm *bprm);
int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
const u8 index);
@ -838,10 +981,15 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path,
unsigned long number);
int tomoyo_path_perm(const u8 operation, struct path *path,
const char *target);
int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
const struct tomoyo_path_info *filename);
int tomoyo_poll_control(struct file *file, poll_table *wait);
int tomoyo_poll_log(struct file *file, poll_table *wait);
int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
int addr_len);
int tomoyo_socket_connect_permission(struct socket *sock,
struct sockaddr *addr, int addr_len);
int tomoyo_socket_listen_permission(struct socket *sock);
int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
int size);
int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)
__printf(2, 3);
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
@ -860,8 +1008,11 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
int tomoyo_write_aggregator(struct tomoyo_acl_param *param);
int tomoyo_write_file(struct tomoyo_acl_param *param);
int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type);
int tomoyo_write_misc(struct tomoyo_acl_param *param);
int tomoyo_write_inet_network(struct tomoyo_acl_param *param);
int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
const u8 type);
int tomoyo_write_unix_network(struct tomoyo_acl_param *param);
ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer,
const int buffer_len);
ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head,
@ -891,12 +1042,11 @@ void tomoyo_del_condition(struct list_head *element);
void tomoyo_fill_path_info(struct tomoyo_path_info *ptr);
void tomoyo_get_attributes(struct tomoyo_obj_info *obj);
void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns);
void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
__printf(2, 3);
void tomoyo_load_policy(const char *filename);
void tomoyo_memory_free(void *ptr);
void tomoyo_normalize_line(unsigned char *buffer);
void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register);
void tomoyo_print_ip(char *buf, const unsigned int size,
const struct tomoyo_ipaddr_union *ptr);
void tomoyo_print_ulong(char *buffer, const int buffer_len,
const unsigned long value, const u8 type);
void tomoyo_put_name_union(struct tomoyo_name_union *ptr);
@ -919,6 +1069,8 @@ extern const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX];
extern const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE];
extern const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION];
extern const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX];
extern const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION];
extern const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX];
extern const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION];
extern const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION];
@ -1097,6 +1249,21 @@ static inline bool tomoyo_same_number_union
a->value_type[1] == b->value_type[1];
}
/**
* tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry.
*
* @a: Pointer to "struct tomoyo_ipaddr_union".
* @b: Pointer to "struct tomoyo_ipaddr_union".
*
* Returns true if @a == @b, false otherwise.
*/
static inline bool tomoyo_same_ipaddr_union
(const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b)
{
return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group &&
a->is_ipv6 == b->is_ipv6;
}
/**
* tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread.
*

View file

@ -348,6 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
a->numbers_count == b->numbers_count &&
a->names_count == b->names_count &&
a->argc == b->argc && a->envc == b->envc &&
a->grant_log == b->grant_log && a->transit == b->transit &&
!memcmp(a + 1, b + 1, a->size - sizeof(*a));
}
@ -399,8 +400,9 @@ static struct tomoyo_condition *tomoyo_commit_condition
found = true;
goto out;
}
list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) {
if (!tomoyo_same_condition(ptr, entry))
list_for_each_entry(ptr, &tomoyo_condition_list, head.list) {
if (!tomoyo_same_condition(ptr, entry) ||
atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
continue;
/* Same entry found. Share this entry. */
atomic_inc(&ptr->head.users);
@ -410,8 +412,7 @@ static struct tomoyo_condition *tomoyo_commit_condition
if (!found) {
if (tomoyo_memory_ok(entry)) {
atomic_set(&entry->head.users, 1);
list_add_rcu(&entry->head.list,
&tomoyo_condition_list);
list_add(&entry->head.list, &tomoyo_condition_list);
} else {
found = true;
ptr = NULL;
@ -427,6 +428,46 @@ out:
return entry;
}
/**
* tomoyo_get_transit_preference - Parse domain transition preference for execve().
*
* @param: Pointer to "struct tomoyo_acl_param".
* @e: Pointer to "struct tomoyo_condition".
*
* Returns the condition string part.
*/
static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param,
struct tomoyo_condition *e)
{
char * const pos = param->data;
bool flag;
if (*pos == '<') {
e->transit = tomoyo_get_domainname(param);
goto done;
}
{
char *cp = strchr(pos, ' ');
if (cp)
*cp = '\0';
flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") ||
!strcmp(pos, "initialize") || !strcmp(pos, "reset") ||
!strcmp(pos, "child") || !strcmp(pos, "parent");
if (cp)
*cp = ' ';
}
if (!flag)
return pos;
e->transit = tomoyo_get_name(tomoyo_read_token(param));
done:
if (e->transit)
return param->data;
/*
* Return a bad read-only condition string that will let
* tomoyo_get_condition() return NULL.
*/
return "/";
}
/**
* tomoyo_get_condition - Parse condition part.
*
@ -443,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
struct tomoyo_argv *argv = NULL;
struct tomoyo_envp *envp = NULL;
struct tomoyo_condition e = { };
char * const start_of_string = param->data;
char * const start_of_string =
tomoyo_get_transit_preference(param, &e);
char * const end_of_string = start_of_string + strlen(start_of_string);
char *pos;
rerun:
@ -486,6 +528,20 @@ rerun:
goto out;
dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word,
is_not ? "!" : "", right_word);
if (!strcmp(left_word, "grant_log")) {
if (entry) {
if (is_not ||
entry->grant_log != TOMOYO_GRANTLOG_AUTO)
goto out;
else if (!strcmp(right_word, "yes"))
entry->grant_log = TOMOYO_GRANTLOG_YES;
else if (!strcmp(right_word, "no"))
entry->grant_log = TOMOYO_GRANTLOG_NO;
else
goto out;
}
continue;
}
if (!strncmp(left_word, "exec.argv[", 10)) {
if (!argv) {
e.argc++;
@ -593,8 +649,9 @@ store_value:
+ e.envc * sizeof(struct tomoyo_envp);
entry = kzalloc(e.size, GFP_NOFS);
if (!entry)
return NULL;
goto out2;
*entry = e;
e.transit = NULL;
condp = (struct tomoyo_condition_element *) (entry + 1);
numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
@ -621,6 +678,8 @@ out:
tomoyo_del_condition(&entry->head.list);
kfree(entry);
}
out2:
tomoyo_put_name(e.transit);
return NULL;
}

View file

@ -39,6 +39,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return -ENOMEM;
list_for_each_entry_rcu(entry, list, list) {
if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
continue;
if (!check_duplicate(entry, new_entry))
continue;
entry->is_deleted = param->is_delete;
@ -102,10 +104,21 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
new_entry->cond = tomoyo_get_condition(param);
if (!new_entry->cond)
return -EINVAL;
/*
* Domain transition preference is allowed for only
* "file execute" entries.
*/
if (new_entry->cond->transit &&
!(new_entry->type == TOMOYO_TYPE_PATH_ACL &&
container_of(new_entry, struct tomoyo_path_acl, head)
->perm == 1 << TOMOYO_TYPE_EXECUTE))
goto out;
}
if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out;
list_for_each_entry_rcu(entry, list, list) {
if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS)
continue;
if (!tomoyo_same_acl_head(entry, new_entry) ||
!check_duplicate(entry, new_entry))
continue;
@ -157,6 +170,7 @@ retry:
continue;
if (!tomoyo_condition(r, ptr->cond))
continue;
r->matched_acl = ptr;
r->granted = true;
return;
}
@ -501,7 +515,8 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
* that domain. Do not perform domain transition if
* profile for that domain is not yet created.
*/
if (!entry->ns->profile_ptr[entry->profile])
if (tomoyo_policy_loaded &&
!entry->ns->profile_ptr[entry->profile])
return NULL;
}
return entry;
@ -557,11 +572,98 @@ out:
tomoyo_write_log(&r, "use_profile %u\n",
entry->profile);
tomoyo_write_log(&r, "use_group %u\n", entry->group);
tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
}
}
return entry;
}
/**
* tomoyo_environ - Check permission for environment variable names.
*
* @ee: Pointer to "struct tomoyo_execve".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_environ(struct tomoyo_execve *ee)
{
struct tomoyo_request_info *r = &ee->r;
struct linux_binprm *bprm = ee->bprm;
/* env_page.data is allocated by tomoyo_dump_page(). */
struct tomoyo_page_dump env_page = { };
char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
int arg_len = 0;
unsigned long pos = bprm->p;
int offset = pos % PAGE_SIZE;
int argv_count = bprm->argc;
int envp_count = bprm->envc;
int error = -ENOMEM;
ee->r.type = TOMOYO_MAC_ENVIRON;
ee->r.profile = r->domain->profile;
ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile,
TOMOYO_MAC_ENVIRON);
if (!r->mode || !envp_count)
return 0;
arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
if (!arg_ptr)
goto out;
while (error == -ENOMEM) {
if (!tomoyo_dump_page(bprm, pos, &env_page))
goto out;
pos += PAGE_SIZE - offset;
/* Read. */
while (argv_count && offset < PAGE_SIZE) {
if (!env_page.data[offset++])
argv_count--;
}
if (argv_count) {
offset = 0;
continue;
}
while (offset < PAGE_SIZE) {
const unsigned char c = env_page.data[offset++];
if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
if (c == '=') {
arg_ptr[arg_len++] = '\0';
} else if (c == '\\') {
arg_ptr[arg_len++] = '\\';
arg_ptr[arg_len++] = '\\';
} else if (c > ' ' && c < 127) {
arg_ptr[arg_len++] = c;
} else {
arg_ptr[arg_len++] = '\\';
arg_ptr[arg_len++] = (c >> 6) + '0';
arg_ptr[arg_len++]
= ((c >> 3) & 7) + '0';
arg_ptr[arg_len++] = (c & 7) + '0';
}
} else {
arg_ptr[arg_len] = '\0';
}
if (c)
continue;
if (tomoyo_env_perm(r, arg_ptr)) {
error = -EPERM;
break;
}
if (!--envp_count) {
error = 0;
break;
}
arg_len = 0;
}
offset = 0;
}
out:
if (r->mode != TOMOYO_CONFIG_ENFORCING)
error = 0;
kfree(env_page.data);
kfree(arg_ptr);
return error;
}
/**
* tomoyo_find_next_domain - Find a domain.
*
@ -577,10 +679,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
struct tomoyo_domain_info *domain = NULL;
const char *original_name = bprm->filename;
int retval = -ENOMEM;
bool need_kfree = false;
bool reject_on_transition_failure = false;
struct tomoyo_path_info rn = { }; /* real name */
const struct tomoyo_path_info *candidate;
struct tomoyo_path_info exename;
struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
if (!ee)
return -ENOMEM;
ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
@ -594,40 +697,32 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
ee->bprm = bprm;
ee->r.obj = &ee->obj;
ee->obj.path1 = bprm->file->f_path;
retry:
if (need_kfree) {
kfree(rn.name);
need_kfree = false;
}
/* Get symlink's pathname of program. */
retval = -ENOENT;
rn.name = tomoyo_realpath_nofollow(original_name);
if (!rn.name)
exename.name = tomoyo_realpath_nofollow(original_name);
if (!exename.name)
goto out;
tomoyo_fill_path_info(&rn);
need_kfree = true;
tomoyo_fill_path_info(&exename);
retry:
/* Check 'aggregator' directive. */
{
struct tomoyo_aggregator *ptr;
struct list_head *list =
&old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR];
/* Check 'aggregator' directive. */
candidate = &exename;
list_for_each_entry_rcu(ptr, list, head.list) {
if (ptr->head.is_deleted ||
!tomoyo_path_matches_pattern(&rn,
!tomoyo_path_matches_pattern(&exename,
ptr->original_name))
continue;
kfree(rn.name);
need_kfree = false;
/* This is OK because it is read only. */
rn = *ptr->aggregated_name;
candidate = ptr->aggregated_name;
break;
}
}
/* Check execute permission. */
retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
retval = tomoyo_execute_permission(&ee->r, candidate);
if (retval == TOMOYO_RETRY_REQUEST)
goto retry;
if (retval < 0)
@ -638,20 +733,51 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
* wildcard) rather than the pathname passed to execve()
* (which never contains wildcard).
*/
if (ee->r.param.path.matched_path) {
if (need_kfree)
kfree(rn.name);
need_kfree = false;
/* This is OK because it is read only. */
rn = *ee->r.param.path.matched_path;
}
if (ee->r.param.path.matched_path)
candidate = ee->r.param.path.matched_path;
/* Calculate domain to transit to. */
/*
* Check for domain transition preference if "file execute" matched.
* If preference is given, make do_execve() fail if domain transition
* has failed, for domain transition preference should be used with
* destination domain defined.
*/
if (ee->transition) {
const char *domainname = ee->transition->name;
reject_on_transition_failure = true;
if (!strcmp(domainname, "keep"))
goto force_keep_domain;
if (!strcmp(domainname, "child"))
goto force_child_domain;
if (!strcmp(domainname, "reset"))
goto force_reset_domain;
if (!strcmp(domainname, "initialize"))
goto force_initialize_domain;
if (!strcmp(domainname, "parent")) {
char *cp;
strncpy(ee->tmp, old_domain->domainname->name,
TOMOYO_EXEC_TMPSIZE - 1);
cp = strrchr(ee->tmp, ' ');
if (cp)
*cp = '\0';
} else if (*domainname == '<')
strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1);
else
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->domainname->name, domainname);
goto force_jump_domain;
}
/*
* No domain transition preference specified.
* Calculate domain to transit to.
*/
switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
&rn)) {
candidate)) {
case TOMOYO_TRANSITION_CONTROL_RESET:
force_reset_domain:
/* Transit to the root of specified namespace. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
candidate->name);
/*
* Make do_execve() fail if domain transition across namespaces
* has failed.
@ -659,11 +785,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
reject_on_transition_failure = true;
break;
case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
force_initialize_domain:
/* Transit to the child of current namespace's root. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->ns->name, rn.name);
old_domain->ns->name, candidate->name);
break;
case TOMOYO_TRANSITION_CONTROL_KEEP:
force_keep_domain:
/* Keep current domain. */
domain = old_domain;
break;
@ -677,13 +805,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
* before /sbin/init.
*/
domain = old_domain;
} else {
/* Normal domain transition. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->domainname->name, rn.name);
break;
}
force_child_domain:
/* Normal domain transition. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->domainname->name, candidate->name);
break;
}
force_jump_domain:
if (!domain)
domain = tomoyo_assign_domain(ee->tmp, true);
if (domain)
@ -711,8 +841,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
/* Update reference count on "struct tomoyo_domain_info". */
atomic_inc(&domain->users);
bprm->cred->security = domain;
if (need_kfree)
kfree(rn.name);
kfree(exename.name);
if (!retval) {
ee->r.domain = domain;
retval = tomoyo_environ(ee);
}
kfree(ee->tmp);
kfree(ee->dump.data);
kfree(ee);
@ -732,7 +865,8 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
struct tomoyo_page_dump *dump)
{
struct page *page;
/* dump->data is released by tomoyo_finish_execve(). */
/* dump->data is released by tomoyo_find_next_domain(). */
if (!dump->data) {
dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
if (!dump->data)
@ -753,6 +887,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
* So do I.
*/
char *kaddr = kmap_atomic(page, KM_USER0);
dump->page = page;
memcpy(dump->data + offset, kaddr + offset,
PAGE_SIZE - offset);

122
security/tomoyo/environ.c Normal file
View file

@ -0,0 +1,122 @@
/*
* security/tomoyo/environ.c
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
/**
* tomoyo_check_env_acl - Check permission for environment variable's name.
*
* @r: Pointer to "struct tomoyo_request_info".
* @ptr: Pointer to "struct tomoyo_acl_info".
*
* Returns true if granted, false otherwise.
*/
static bool tomoyo_check_env_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_env_acl *acl =
container_of(ptr, typeof(*acl), head);
return tomoyo_path_matches_pattern(r->param.environ.name, acl->env);
}
/**
* tomoyo_audit_env_log - Audit environment variable name log.
*
* @r: Pointer to "struct tomoyo_request_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_audit_env_log(struct tomoyo_request_info *r)
{
return tomoyo_supervisor(r, "misc env %s\n",
r->param.environ.name->name);
}
/**
* tomoyo_env_perm - Check permission for environment variable's name.
*
* @r: Pointer to "struct tomoyo_request_info".
* @env: The name of environment variable.
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env)
{
struct tomoyo_path_info environ;
int error;
if (!env || !*env)
return 0;
environ.name = env;
tomoyo_fill_path_info(&environ);
r->param_type = TOMOYO_TYPE_ENV_ACL;
r->param.environ.name = &environ;
do {
tomoyo_check_acl(r, tomoyo_check_env_acl);
error = tomoyo_audit_env_log(r);
} while (error == TOMOYO_RETRY_REQUEST);
return error;
}
/**
* tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
*
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head);
return p1->env == p2->env;
}
/**
* tomoyo_write_env - Write "struct tomoyo_env_acl" list.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_env(struct tomoyo_acl_param *param)
{
struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL };
int error = -ENOMEM;
const char *data = tomoyo_read_token(param);
if (!tomoyo_correct_word(data) || strchr(data, '='))
return -EINVAL;
e.env = tomoyo_get_name(data);
if (!e.env)
return error;
error = tomoyo_update_domain(&e.head, sizeof(e), param,
tomoyo_same_env_acl, NULL);
tomoyo_put_name(e.env);
return error;
}
/**
* tomoyo_write_misc - Update environment variable list.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_write_misc(struct tomoyo_acl_param *param)
{
if (tomoyo_str_starts(&param->data, "env "))
return tomoyo_write_env(param);
return -EINVAL;
}

View file

@ -555,8 +555,8 @@ static int tomoyo_update_path2_acl(const u8 perm,
*
* Caller holds tomoyo_read_lock().
*/
int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
const struct tomoyo_path_info *filename)
static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
const struct tomoyo_path_info *filename)
{
int error;
@ -570,15 +570,41 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
do {
tomoyo_check_acl(r, tomoyo_check_path_acl);
error = tomoyo_audit_path_log(r);
/*
* Do not retry for execute request, for alias may have
* changed.
*/
} while (error == TOMOYO_RETRY_REQUEST &&
operation != TOMOYO_TYPE_EXECUTE);
} while (error == TOMOYO_RETRY_REQUEST);
return error;
}
/**
* tomoyo_execute_permission - Check permission for execute operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @filename: Filename to check.
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
int tomoyo_execute_permission(struct tomoyo_request_info *r,
const struct tomoyo_path_info *filename)
{
/*
* Unlike other permission checks, this check is done regardless of
* profile mode settings in order to check for domain transition
* preference.
*/
r->type = TOMOYO_MAC_FILE_EXECUTE;
r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type);
r->param_type = TOMOYO_TYPE_PATH_ACL;
r->param.path.filename = filename;
r->param.path.operation = TOMOYO_TYPE_EXECUTE;
tomoyo_check_acl(r, tomoyo_check_path_acl);
r->ee->transition = r->matched_acl && r->matched_acl->cond ?
r->matched_acl->cond->transit : NULL;
if (r->mode != TOMOYO_CONFIG_DISABLED)
return tomoyo_audit_path_log(r);
return 0;
}
/**
* tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
*

View file

@ -8,36 +8,26 @@
#include <linux/kthread.h>
#include <linux/slab.h>
/**
* tomoyo_memory_free - Free memory for elements.
*
* @ptr: Pointer to allocated memory.
*
* Returns nothing.
*
* Caller holds tomoyo_policy_lock mutex.
*/
static inline void tomoyo_memory_free(void *ptr)
{
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr);
kfree(ptr);
}
/* The list for "struct tomoyo_io_buffer". */
static LIST_HEAD(tomoyo_io_buffer_list);
/* Lock for protecting tomoyo_io_buffer_list. */
static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock);
/* Size of an element. */
static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = {
[TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group),
[TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group),
[TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group),
[TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator),
[TOMOYO_ID_TRANSITION_CONTROL] =
sizeof(struct tomoyo_transition_control),
[TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager),
/* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */
/* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */
/* [TOMOYO_ID_ACL] =
tomoyo_acl_size["struct tomoyo_acl_info"->type], */
[TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info),
};
/* Size of a domain ACL element. */
static const u8 tomoyo_acl_size[] = {
[TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl),
[TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl),
[TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl),
[TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl),
[TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl),
};
/**
* tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
*
@ -55,15 +45,11 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
head->users++;
spin_unlock(&tomoyo_io_buffer_list_lock);
if (mutex_lock_interruptible(&head->io_sem)) {
in_use = true;
goto out;
}
mutex_lock(&head->io_sem);
if (head->r.domain == element || head->r.group == element ||
head->r.acl == element || &head->w.domain->list == element)
in_use = true;
mutex_unlock(&head->io_sem);
out:
spin_lock(&tomoyo_io_buffer_list_lock);
head->users--;
if (in_use)
@ -77,15 +63,14 @@ out:
* tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
*
* @string: String to check.
* @size: Memory allocated for @string .
*
* Returns true if @string is used by /sys/kernel/security/tomoyo/ users,
* false otherwise.
*/
static bool tomoyo_name_used_by_io_buffer(const char *string,
const size_t size)
static bool tomoyo_name_used_by_io_buffer(const char *string)
{
struct tomoyo_io_buffer *head;
const size_t size = strlen(string) + 1;
bool in_use = false;
spin_lock(&tomoyo_io_buffer_list_lock);
@ -93,10 +78,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string,
int i;
head->users++;
spin_unlock(&tomoyo_io_buffer_list_lock);
if (mutex_lock_interruptible(&head->io_sem)) {
in_use = true;
goto out;
}
mutex_lock(&head->io_sem);
for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) {
const char *w = head->r.w[i];
if (w < string || w > string + size)
@ -105,7 +87,6 @@ static bool tomoyo_name_used_by_io_buffer(const char *string,
break;
}
mutex_unlock(&head->io_sem);
out:
spin_lock(&tomoyo_io_buffer_list_lock);
head->users--;
if (in_use)
@ -115,84 +96,6 @@ out:
return in_use;
}
/* Structure for garbage collection. */
struct tomoyo_gc {
struct list_head list;
enum tomoyo_policy_id type;
size_t size;
struct list_head *element;
};
/* List of entries to be deleted. */
static LIST_HEAD(tomoyo_gc_list);
/* Length of tomoyo_gc_list. */
static int tomoyo_gc_list_len;
/**
* tomoyo_add_to_gc - Add an entry to to be deleted list.
*
* @type: One of values in "enum tomoyo_policy_id".
* @element: Pointer to "struct list_head".
*
* Returns true on success, false otherwise.
*
* Caller holds tomoyo_policy_lock mutex.
*
* Adding an entry needs kmalloc(). Thus, if we try to add thousands of
* entries at once, it will take too long time. Thus, do not add more than 128
* entries per a scan. But to be able to handle worst case where all entries
* are in-use, we accept one more entry per a scan.
*
* If we use singly linked list using "struct list_head"->prev (which is
* LIST_POISON2), we can avoid kmalloc().
*/
static bool tomoyo_add_to_gc(const int type, struct list_head *element)
{
struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return false;
entry->type = type;
if (type == TOMOYO_ID_ACL)
entry->size = tomoyo_acl_size[
container_of(element,
typeof(struct tomoyo_acl_info),
list)->type];
else if (type == TOMOYO_ID_NAME)
entry->size = strlen(container_of(element,
typeof(struct tomoyo_name),
head.list)->entry.name) + 1;
else if (type == TOMOYO_ID_CONDITION)
entry->size =
container_of(element, typeof(struct tomoyo_condition),
head.list)->size;
else
entry->size = tomoyo_element_size[type];
entry->element = element;
list_add(&entry->list, &tomoyo_gc_list);
list_del_rcu(element);
return tomoyo_gc_list_len++ < 128;
}
/**
* tomoyo_element_linked_by_gc - Validate next element of an entry.
*
* @element: Pointer to an element.
* @size: Size of @element in byte.
*
* Returns true if @element is linked by other elements in the garbage
* collector's queue, false otherwise.
*/
static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
{
struct tomoyo_gc *p;
list_for_each_entry(p, &tomoyo_gc_list, list) {
const u8 *ptr = (const u8 *) p->element->next;
if (ptr < element || element + size < ptr)
continue;
return true;
}
return false;
}
/**
* tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
*
@ -200,7 +103,7 @@ static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size)
*
* Returns nothing.
*/
static void tomoyo_del_transition_control(struct list_head *element)
static inline void tomoyo_del_transition_control(struct list_head *element)
{
struct tomoyo_transition_control *ptr =
container_of(element, typeof(*ptr), head.list);
@ -215,7 +118,7 @@ static void tomoyo_del_transition_control(struct list_head *element)
*
* Returns nothing.
*/
static void tomoyo_del_aggregator(struct list_head *element)
static inline void tomoyo_del_aggregator(struct list_head *element)
{
struct tomoyo_aggregator *ptr =
container_of(element, typeof(*ptr), head.list);
@ -230,7 +133,7 @@ static void tomoyo_del_aggregator(struct list_head *element)
*
* Returns nothing.
*/
static void tomoyo_del_manager(struct list_head *element)
static inline void tomoyo_del_manager(struct list_head *element)
{
struct tomoyo_manager *ptr =
container_of(element, typeof(*ptr), head.list);
@ -293,6 +196,38 @@ static void tomoyo_del_acl(struct list_head *element)
tomoyo_put_number_union(&entry->flags);
}
break;
case TOMOYO_TYPE_ENV_ACL:
{
struct tomoyo_env_acl *entry =
container_of(acl, typeof(*entry), head);
tomoyo_put_name(entry->env);
}
break;
case TOMOYO_TYPE_INET_ACL:
{
struct tomoyo_inet_acl *entry =
container_of(acl, typeof(*entry), head);
tomoyo_put_group(entry->address.group);
tomoyo_put_number_union(&entry->port);
}
break;
case TOMOYO_TYPE_UNIX_ACL:
{
struct tomoyo_unix_acl *entry =
container_of(acl, typeof(*entry), head);
tomoyo_put_name_union(&entry->name);
}
break;
case TOMOYO_TYPE_MANUAL_TASK_ACL:
{
struct tomoyo_task_acl *entry =
container_of(acl, typeof(*entry), head);
tomoyo_put_name(entry->domainname);
}
break;
}
}
@ -301,44 +236,26 @@ static void tomoyo_del_acl(struct list_head *element)
*
* @element: Pointer to "struct list_head".
*
* Returns true if deleted, false otherwise.
* Returns nothing.
*
* Caller holds tomoyo_policy_lock mutex.
*/
static bool tomoyo_del_domain(struct list_head *element)
static inline void tomoyo_del_domain(struct list_head *element)
{
struct tomoyo_domain_info *domain =
container_of(element, typeof(*domain), list);
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).
* Since this domain is referenced from neither
* "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete
* elements without checking for is_deleted flag.
*/
if (atomic_read(&domain->users))
return false;
list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) {
tomoyo_del_acl(&acl->list);
tomoyo_memory_free(acl);
}
tomoyo_put_name(domain->domainname);
return true;
}
/**
@ -387,10 +304,9 @@ void tomoyo_del_condition(struct list_head *element)
*
* Returns nothing.
*/
static void tomoyo_del_name(struct list_head *element)
static inline void tomoyo_del_name(struct list_head *element)
{
const struct tomoyo_name *ptr =
container_of(element, typeof(*ptr), head.list);
/* Nothing to do. */
}
/**
@ -400,7 +316,7 @@ static void tomoyo_del_name(struct list_head *element)
*
* Returns nothing.
*/
static void tomoyo_del_path_group(struct list_head *element)
static inline void tomoyo_del_path_group(struct list_head *element)
{
struct tomoyo_path_group *member =
container_of(element, typeof(*member), head.list);
@ -414,13 +330,25 @@ static void tomoyo_del_path_group(struct list_head *element)
*
* Returns nothing.
*/
static void tomoyo_del_group(struct list_head *element)
static inline void tomoyo_del_group(struct list_head *element)
{
struct tomoyo_group *group =
container_of(element, typeof(*group), head.list);
tomoyo_put_name(group->group_name);
}
/**
* tomoyo_del_address_group - Delete members in "struct tomoyo_address_group".
*
* @element: Pointer to "struct list_head".
*
* Returns nothing.
*/
static inline void tomoyo_del_address_group(struct list_head *element)
{
/* Nothing to do. */
}
/**
* tomoyo_del_number_group - Delete members in "struct tomoyo_number_group".
*
@ -428,10 +356,110 @@ static void tomoyo_del_group(struct list_head *element)
*
* Returns nothing.
*/
static void tomoyo_del_number_group(struct list_head *element)
static inline void tomoyo_del_number_group(struct list_head *element)
{
struct tomoyo_number_group *member =
container_of(element, typeof(*member), head.list);
/* Nothing to do. */
}
/**
* tomoyo_try_to_gc - Try to kfree() an entry.
*
* @type: One of values in "enum tomoyo_policy_id".
* @element: Pointer to "struct list_head".
*
* Returns nothing.
*
* Caller holds tomoyo_policy_lock mutex.
*/
static void tomoyo_try_to_gc(const enum tomoyo_policy_id type,
struct list_head *element)
{
/*
* __list_del_entry() guarantees that the list element became no longer
* reachable from the list which the element was originally on (e.g.
* tomoyo_domain_list). Also, synchronize_srcu() guarantees that the
* list element became no longer referenced by syscall users.
*/
__list_del_entry(element);
mutex_unlock(&tomoyo_policy_lock);
synchronize_srcu(&tomoyo_ss);
/*
* However, there are two users which may still be using the list
* element. We need to defer until both users forget this element.
*
* Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl}
* and "struct tomoyo_io_buffer"->w.domain forget this element.
*/
if (tomoyo_struct_used_by_io_buffer(element))
goto reinject;
switch (type) {
case TOMOYO_ID_TRANSITION_CONTROL:
tomoyo_del_transition_control(element);
break;
case TOMOYO_ID_MANAGER:
tomoyo_del_manager(element);
break;
case TOMOYO_ID_AGGREGATOR:
tomoyo_del_aggregator(element);
break;
case TOMOYO_ID_GROUP:
tomoyo_del_group(element);
break;
case TOMOYO_ID_PATH_GROUP:
tomoyo_del_path_group(element);
break;
case TOMOYO_ID_ADDRESS_GROUP:
tomoyo_del_address_group(element);
break;
case TOMOYO_ID_NUMBER_GROUP:
tomoyo_del_number_group(element);
break;
case TOMOYO_ID_CONDITION:
tomoyo_del_condition(element);
break;
case TOMOYO_ID_NAME:
/*
* Don't kfree() until all "struct tomoyo_io_buffer"->r.w[]
* forget this element.
*/
if (tomoyo_name_used_by_io_buffer
(container_of(element, typeof(struct tomoyo_name),
head.list)->entry.name))
goto reinject;
tomoyo_del_name(element);
break;
case TOMOYO_ID_ACL:
tomoyo_del_acl(element);
break;
case TOMOYO_ID_DOMAIN:
/*
* Don't kfree() until all "struct cred"->security forget this
* element.
*/
if (atomic_read(&container_of
(element, typeof(struct tomoyo_domain_info),
list)->users))
goto reinject;
break;
case TOMOYO_MAX_POLICY:
break;
}
mutex_lock(&tomoyo_policy_lock);
if (type == TOMOYO_ID_DOMAIN)
tomoyo_del_domain(element);
tomoyo_memory_free(element);
return;
reinject:
/*
* We can safely reinject this element here bacause
* (1) Appending list elements and removing list elements are protected
* by tomoyo_policy_lock mutex.
* (2) Only this function removes list elements and this function is
* exclusively executed by tomoyo_gc_mutex mutex.
* are true.
*/
mutex_lock(&tomoyo_policy_lock);
list_add_rcu(element, element->prev);
}
/**
@ -440,19 +468,19 @@ static void tomoyo_del_number_group(struct list_head *element)
* @id: One of values in "enum tomoyo_policy_id".
* @member_list: Pointer to "struct list_head".
*
* Returns true if some elements are deleted, false otherwise.
* Returns nothing.
*/
static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
static void tomoyo_collect_member(const enum tomoyo_policy_id id,
struct list_head *member_list)
{
struct tomoyo_acl_head *member;
list_for_each_entry(member, member_list, list) {
struct tomoyo_acl_head *tmp;
list_for_each_entry_safe(member, tmp, member_list, list) {
if (!member->is_deleted)
continue;
if (!tomoyo_add_to_gc(id, &member->list))
return false;
member->is_deleted = TOMOYO_GC_IN_PROGRESS;
tomoyo_try_to_gc(id, &member->list);
}
return true;
}
/**
@ -460,22 +488,22 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id,
*
* @list: Pointer to "struct list_head".
*
* Returns true if some elements are deleted, false otherwise.
* Returns nothing.
*/
static bool tomoyo_collect_acl(struct list_head *list)
static void tomoyo_collect_acl(struct list_head *list)
{
struct tomoyo_acl_info *acl;
list_for_each_entry(acl, list, list) {
struct tomoyo_acl_info *tmp;
list_for_each_entry_safe(acl, tmp, list, list) {
if (!acl->is_deleted)
continue;
if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list))
return false;
acl->is_deleted = TOMOYO_GC_IN_PROGRESS;
tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list);
}
return true;
}
/**
* tomoyo_collect_entry - Scan lists for deleted elements.
* tomoyo_collect_entry - Try to kfree() deleted elements.
*
* Returns nothing.
*/
@ -484,174 +512,82 @@ static void tomoyo_collect_entry(void)
int i;
enum tomoyo_policy_id id;
struct tomoyo_policy_namespace *ns;
int idx;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return;
idx = tomoyo_read_lock();
mutex_lock(&tomoyo_policy_lock);
{
struct tomoyo_domain_info *domain;
list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
if (!tomoyo_collect_acl(&domain->acl_info_list))
goto unlock;
struct tomoyo_domain_info *tmp;
list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list,
list) {
tomoyo_collect_acl(&domain->acl_info_list);
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))
goto unlock;
tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list);
}
}
list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) {
list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
for (id = 0; id < TOMOYO_MAX_POLICY; id++)
if (!tomoyo_collect_member(id, &ns->policy_list[id]))
goto unlock;
tomoyo_collect_member(id, &ns->policy_list[id]);
for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
if (!tomoyo_collect_acl(&ns->acl_group[i]))
goto unlock;
tomoyo_collect_acl(&ns->acl_group[i]);
}
{
struct tomoyo_shared_acl_head *ptr;
struct tomoyo_shared_acl_head *tmp;
list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list,
list) {
if (atomic_read(&ptr->users) > 0)
continue;
atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list);
}
}
list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
for (i = 0; i < TOMOYO_MAX_GROUP; i++) {
struct list_head *list = &ns->group_list[i];
struct tomoyo_group *group;
struct tomoyo_group *tmp;
switch (i) {
case 0:
id = TOMOYO_ID_PATH_GROUP;
break;
default:
case 1:
id = TOMOYO_ID_NUMBER_GROUP;
break;
default:
id = TOMOYO_ID_ADDRESS_GROUP;
break;
}
list_for_each_entry(group, list, head.list) {
if (!tomoyo_collect_member
(id, &group->member_list))
goto unlock;
list_for_each_entry_safe(group, tmp, list, head.list) {
tomoyo_collect_member(id, &group->member_list);
if (!list_empty(&group->member_list) ||
atomic_read(&group->head.users))
atomic_read(&group->head.users) > 0)
continue;
if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP,
&group->head.list))
goto unlock;
atomic_set(&group->head.users,
TOMOYO_GC_IN_PROGRESS);
tomoyo_try_to_gc(TOMOYO_ID_GROUP,
&group->head.list);
}
}
}
id = TOMOYO_ID_CONDITION;
for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) {
struct list_head *list = !i ?
&tomoyo_condition_list : &tomoyo_name_list[i - 1];
for (i = 0; i < TOMOYO_MAX_HASH; i++) {
struct list_head *list = &tomoyo_name_list[i];
struct tomoyo_shared_acl_head *ptr;
list_for_each_entry(ptr, list, list) {
if (atomic_read(&ptr->users))
struct tomoyo_shared_acl_head *tmp;
list_for_each_entry_safe(ptr, tmp, list, list) {
if (atomic_read(&ptr->users) > 0)
continue;
if (!tomoyo_add_to_gc(id, &ptr->list))
goto unlock;
atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list);
}
id = TOMOYO_ID_NAME;
}
unlock:
tomoyo_read_unlock(idx);
mutex_unlock(&tomoyo_policy_lock);
}
/**
* tomoyo_kfree_entry - Delete entries in tomoyo_gc_list.
*
* Returns true if some entries were kfree()d, false otherwise.
*/
static bool tomoyo_kfree_entry(void)
{
struct tomoyo_gc *p;
struct tomoyo_gc *tmp;
bool result = false;
list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) {
struct list_head *element = p->element;
/*
* list_del_rcu() in tomoyo_add_to_gc() guarantees that the
* list element became no longer reachable from the list which
* the element was originally on (e.g. tomoyo_domain_list).
* Also, synchronize_srcu() in tomoyo_gc_thread() guarantees
* that the list element became no longer referenced by syscall
* users.
*
* However, there are three users which may still be using the
* list element. We need to defer until all of these users
* forget the list element.
*
* Firstly, defer until "struct tomoyo_io_buffer"->r.{domain,
* group,acl} and "struct tomoyo_io_buffer"->w.domain forget
* the list element.
*/
if (tomoyo_struct_used_by_io_buffer(element))
continue;
/*
* Secondly, defer until all other elements in the
* tomoyo_gc_list list forget the list element.
*/
if (tomoyo_element_linked_by_gc((const u8 *) element, p->size))
continue;
switch (p->type) {
case TOMOYO_ID_TRANSITION_CONTROL:
tomoyo_del_transition_control(element);
break;
case TOMOYO_ID_AGGREGATOR:
tomoyo_del_aggregator(element);
break;
case TOMOYO_ID_MANAGER:
tomoyo_del_manager(element);
break;
case TOMOYO_ID_CONDITION:
tomoyo_del_condition(element);
break;
case TOMOYO_ID_NAME:
/*
* Thirdly, defer until all "struct tomoyo_io_buffer"
* ->r.w[] forget the list element.
*/
if (tomoyo_name_used_by_io_buffer(
container_of(element, typeof(struct tomoyo_name),
head.list)->entry.name, p->size))
continue;
tomoyo_del_name(element);
break;
case TOMOYO_ID_ACL:
tomoyo_del_acl(element);
break;
case TOMOYO_ID_DOMAIN:
if (!tomoyo_del_domain(element))
continue;
break;
case TOMOYO_ID_PATH_GROUP:
tomoyo_del_path_group(element);
break;
case TOMOYO_ID_GROUP:
tomoyo_del_group(element);
break;
case TOMOYO_ID_NUMBER_GROUP:
tomoyo_del_number_group(element);
break;
case TOMOYO_MAX_POLICY:
break;
}
tomoyo_memory_free(element);
list_del(&p->list);
kfree(p);
tomoyo_gc_list_len--;
result = true;
}
return result;
}
/**
* tomoyo_gc_thread - Garbage collector thread function.
*
* @unused: Unused.
*
* In case OOM-killer choose this thread for termination, we create this thread
* as a short live thread whenever /sys/kernel/security/tomoyo/ interface was
* close()d.
*
* Returns 0.
*/
static int tomoyo_gc_thread(void *unused)
@ -660,13 +596,7 @@ static int tomoyo_gc_thread(void *unused)
static DEFINE_MUTEX(tomoyo_gc_mutex);
if (!mutex_trylock(&tomoyo_gc_mutex))
goto out;
daemonize("GC for TOMOYO");
do {
tomoyo_collect_entry();
if (list_empty(&tomoyo_gc_list))
break;
synchronize_srcu(&tomoyo_ss);
} while (tomoyo_kfree_entry());
tomoyo_collect_entry();
{
struct tomoyo_io_buffer *head;
struct tomoyo_io_buffer *tmp;

View file

@ -42,7 +42,26 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a,
}
/**
* tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list.
* tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry.
*
* @a: Pointer to "struct tomoyo_acl_head".
* @b: Pointer to "struct tomoyo_acl_head".
*
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_address_group(const struct tomoyo_acl_head *a,
const struct tomoyo_acl_head *b)
{
const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1),
head);
const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2),
head);
return tomoyo_same_ipaddr_union(&p1->address, &p2->address);
}
/**
* tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list.
*
* @param: Pointer to "struct tomoyo_acl_param".
* @type: Type of this group.
@ -77,6 +96,14 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
* tomoyo_put_number_union() is not needed because
* param->data[0] != '@'.
*/
} else {
struct tomoyo_address_group e = { };
if (param->data[0] == '@' ||
!tomoyo_parse_ipaddr_union(param, &e.address))
goto out;
error = tomoyo_update_policy(&e.head, sizeof(e), param,
tomoyo_same_address_group);
}
out:
tomoyo_put_group(group);
@ -137,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min,
}
return matched;
}
/**
* tomoyo_address_matches_group - Check whether the given address matches members of the given address group.
*
* @is_ipv6: True if @address is an IPv6 address.
* @address: An IPv4 or IPv6 address.
* @group: Pointer to "struct tomoyo_address_group".
*
* Returns true if @address matches addresses in @group group, false otherwise.
*
* Caller holds tomoyo_read_lock().
*/
bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address,
const struct tomoyo_group *group)
{
struct tomoyo_address_group *member;
bool matched = false;
const u8 size = is_ipv6 ? 16 : 4;
list_for_each_entry_rcu(member, &group->member_list, head.list) {
if (member->head.is_deleted)
continue;
if (member->address.is_ipv6 != is_ipv6)
continue;
if (memcmp(&member->address.ip[0], address, size) > 0 ||
memcmp(address, &member->address.ip[1], size) > 0)
continue;
matched = true;
break;
}
return matched;
}

View file

@ -27,8 +27,6 @@ void tomoyo_warn_oom(const char *function)
panic("MAC Initialization failed.\n");
}
/* Lock for protecting tomoyo_memory_used. */
static DEFINE_SPINLOCK(tomoyo_policy_memory_lock);
/* Memoy currently used by policy/audit log/query. */
unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
/* Memory quota for "policy"/"audit log"/"query". */
@ -42,22 +40,19 @@ unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
* Returns true on success, false otherwise.
*
* Returns true if @ptr is not NULL and quota not exceeded, false otherwise.
*
* Caller holds tomoyo_policy_lock mutex.
*/
bool tomoyo_memory_ok(void *ptr)
{
if (ptr) {
const size_t s = ksize(ptr);
bool result;
spin_lock(&tomoyo_policy_memory_lock);
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
tomoyo_memory_quota[TOMOYO_MEMORY_POLICY];
if (!result)
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
spin_unlock(&tomoyo_policy_memory_lock);
if (result)
if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] ||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <=
tomoyo_memory_quota[TOMOYO_MEMORY_POLICY])
return true;
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
}
tomoyo_warn_oom(__func__);
return false;
@ -71,6 +66,8 @@ bool tomoyo_memory_ok(void *ptr)
*
* Returns pointer to allocated memory on success, NULL otherwise.
* @data is zero-cleared on success.
*
* Caller holds tomoyo_policy_lock mutex.
*/
void *tomoyo_commit_ok(void *data, const unsigned int size)
{
@ -84,20 +81,6 @@ void *tomoyo_commit_ok(void *data, const unsigned int size)
return NULL;
}
/**
* tomoyo_memory_free - Free memory for elements.
*
* @ptr: Pointer to allocated memory.
*/
void tomoyo_memory_free(void *ptr)
{
size_t s = ksize(ptr);
spin_lock(&tomoyo_policy_memory_lock);
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s;
spin_unlock(&tomoyo_policy_memory_lock);
kfree(ptr);
}
/**
* tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
*
@ -123,7 +106,8 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
goto out;
list = &param->ns->group_list[idx];
list_for_each_entry(group, list, head.list) {
if (e.group_name != group->group_name)
if (e.group_name != group->group_name ||
atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS)
continue;
atomic_inc(&group->head.users);
found = true;
@ -175,7 +159,8 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name)
if (mutex_lock_interruptible(&tomoyo_policy_lock))
return NULL;
list_for_each_entry(ptr, head, head.list) {
if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name))
if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) ||
atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
continue;
atomic_inc(&ptr->head.users);
goto out;

771
security/tomoyo/network.c Normal file
View file

@ -0,0 +1,771 @@
/*
* security/tomoyo/network.c
*
* Copyright (C) 2005-2011 NTT DATA CORPORATION
*/
#include "common.h"
#include <linux/slab.h>
/* Structure for holding inet domain socket's address. */
struct tomoyo_inet_addr_info {
__be16 port; /* In network byte order. */
const __be32 *address; /* In network byte order. */
bool is_ipv6;
};
/* Structure for holding unix domain socket's address. */
struct tomoyo_unix_addr_info {
u8 *addr; /* This may not be '\0' terminated string. */
unsigned int addr_len;
};
/* Structure for holding socket address. */
struct tomoyo_addr_info {
u8 protocol;
u8 operation;
struct tomoyo_inet_addr_info inet;
struct tomoyo_unix_addr_info unix0;
};
/* String table for socket's protocols. */
const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = {
[SOCK_STREAM] = "stream",
[SOCK_DGRAM] = "dgram",
[SOCK_RAW] = "raw",
[SOCK_SEQPACKET] = "seqpacket",
[0] = " ", /* Dummy for avoiding NULL pointer dereference. */
[4] = " ", /* Dummy for avoiding NULL pointer dereference. */
};
/**
* tomoyo_parse_ipaddr_union - Parse an IP address.
*
* @param: Pointer to "struct tomoyo_acl_param".
* @ptr: Pointer to "struct tomoyo_ipaddr_union".
*
* Returns true on success, false otherwise.
*/
bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param,
struct tomoyo_ipaddr_union *ptr)
{
u8 * const min = ptr->ip[0].in6_u.u6_addr8;
u8 * const max = ptr->ip[1].in6_u.u6_addr8;
char *address = tomoyo_read_token(param);
const char *end;
if (!strchr(address, ':') &&
in4_pton(address, -1, min, '-', &end) > 0) {
ptr->is_ipv6 = false;
if (!*end)
ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0];
else if (*end++ != '-' ||
in4_pton(end, -1, max, '\0', &end) <= 0 || *end)
return false;
return true;
}
if (in6_pton(address, -1, min, '-', &end) > 0) {
ptr->is_ipv6 = true;
if (!*end)
memmove(max, min, sizeof(u16) * 8);
else if (*end++ != '-' ||
in6_pton(end, -1, max, '\0', &end) <= 0 || *end)
return false;
return true;
}
return false;
}
/**
* tomoyo_print_ipv4 - Print an IPv4 address.
*
* @buffer: Buffer to write to.
* @buffer_len: Size of @buffer.
* @min_ip: Pointer to __be32.
* @max_ip: Pointer to __be32.
*
* Returns nothing.
*/
static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len,
const __be32 *min_ip, const __be32 *max_ip)
{
snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip,
*min_ip == *max_ip ? '\0' : '-', max_ip);
}
/**
* tomoyo_print_ipv6 - Print an IPv6 address.
*
* @buffer: Buffer to write to.
* @buffer_len: Size of @buffer.
* @min_ip: Pointer to "struct in6_addr".
* @max_ip: Pointer to "struct in6_addr".
*
* Returns nothing.
*/
static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len,
const struct in6_addr *min_ip,
const struct in6_addr *max_ip)
{
snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip,
!memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip);
}
/**
* tomoyo_print_ip - Print an IP address.
*
* @buf: Buffer to write to.
* @size: Size of @buf.
* @ptr: Pointer to "struct ipaddr_union".
*
* Returns nothing.
*/
void tomoyo_print_ip(char *buf, const unsigned int size,
const struct tomoyo_ipaddr_union *ptr)
{
if (ptr->is_ipv6)
tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]);
else
tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0],
&ptr->ip[1].s6_addr32[0]);
}
/*
* Mapping table from "enum tomoyo_network_acl_index" to
* "enum tomoyo_mac_index" for inet domain socket.
*/
static const u8 tomoyo_inet2mac
[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
[SOCK_STREAM] = {
[TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_STREAM_BIND,
[TOMOYO_NETWORK_LISTEN] =
TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN,
[TOMOYO_NETWORK_CONNECT] =
TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT,
},
[SOCK_DGRAM] = {
[TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND,
[TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND,
},
[SOCK_RAW] = {
[TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_RAW_BIND,
[TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_INET_RAW_SEND,
},
};
/*
* Mapping table from "enum tomoyo_network_acl_index" to
* "enum tomoyo_mac_index" for unix domain socket.
*/
static const u8 tomoyo_unix2mac
[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = {
[SOCK_STREAM] = {
[TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND,
[TOMOYO_NETWORK_LISTEN] =
TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN,
[TOMOYO_NETWORK_CONNECT] =
TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT,
},
[SOCK_DGRAM] = {
[TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND,
[TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND,
},
[SOCK_SEQPACKET] = {
[TOMOYO_NETWORK_BIND] =
TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND,
[TOMOYO_NETWORK_LISTEN] =
TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN,
[TOMOYO_NETWORK_CONNECT] =
TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT,
},
};
/**
* tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
*
* Returns true if @a == @b except permission bits, false otherwise.
*/
static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head);
return p1->protocol == p2->protocol &&
tomoyo_same_ipaddr_union(&p1->address, &p2->address) &&
tomoyo_same_number_union(&p1->port, &p2->port);
}
/**
* tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
*
* Returns true if @a == @b except permission bits, false otherwise.
*/
static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head);
return p1->protocol == p2->protocol &&
tomoyo_same_name_union(&p1->name, &p2->name);
}
/**
* tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
* @is_delete: True for @a &= ~@b, false for @a |= @b.
*
* Returns true if @a is empty, false otherwise.
*/
static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
{
u8 * const a_perm =
&container_of(a, struct tomoyo_inet_acl, head)->perm;
u8 perm = *a_perm;
const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm;
if (is_delete)
perm &= ~b_perm;
else
perm |= b_perm;
*a_perm = perm;
return !perm;
}
/**
* tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
* @is_delete: True for @a &= ~@b, false for @a |= @b.
*
* Returns true if @a is empty, false otherwise.
*/
static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a,
struct tomoyo_acl_info *b,
const bool is_delete)
{
u8 * const a_perm =
&container_of(a, struct tomoyo_unix_acl, head)->perm;
u8 perm = *a_perm;
const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm;
if (is_delete)
perm &= ~b_perm;
else
perm |= b_perm;
*a_perm = perm;
return !perm;
}
/**
* tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
int tomoyo_write_inet_network(struct tomoyo_acl_param *param)
{
struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL };
int error = -EINVAL;
u8 type;
const char *protocol = tomoyo_read_token(param);
const char *operation = tomoyo_read_token(param);
for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
break;
for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
e.perm |= 1 << type;
if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
return -EINVAL;
if (param->data[0] == '@') {
param->data++;
e.address.group =
tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP);
if (!e.address.group)
return -ENOMEM;
} else {
if (!tomoyo_parse_ipaddr_union(param, &e.address))
goto out;
}
if (!tomoyo_parse_number_union(param, &e.port) ||
e.port.values[1] > 65535)
goto out;
error = tomoyo_update_domain(&e.head, sizeof(e), param,
tomoyo_same_inet_acl,
tomoyo_merge_inet_acl);
out:
tomoyo_put_group(e.address.group);
tomoyo_put_number_union(&e.port);
return error;
}
/**
* tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_write_unix_network(struct tomoyo_acl_param *param)
{
struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL };
int error;
u8 type;
const char *protocol = tomoyo_read_token(param);
const char *operation = tomoyo_read_token(param);
for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++)
if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol]))
break;
for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++)
if (tomoyo_permstr(operation, tomoyo_socket_keyword[type]))
e.perm |= 1 << type;
if (e.protocol == TOMOYO_SOCK_MAX || !e.perm)
return -EINVAL;
if (!tomoyo_parse_name_union(param, &e.name))
return -EINVAL;
error = tomoyo_update_domain(&e.head, sizeof(e), param,
tomoyo_same_unix_acl,
tomoyo_merge_unix_acl);
tomoyo_put_name_union(&e.name);
return error;
}
/**
* tomoyo_audit_net_log - Audit network log.
*
* @r: Pointer to "struct tomoyo_request_info".
* @family: Name of socket family ("inet" or "unix").
* @protocol: Name of protocol in @family.
* @operation: Name of socket operation.
* @address: Name of address.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_audit_net_log(struct tomoyo_request_info *r,
const char *family, const u8 protocol,
const u8 operation, const char *address)
{
return tomoyo_supervisor(r, "network %s %s %s %s\n", family,
tomoyo_proto_keyword[protocol],
tomoyo_socket_keyword[operation], address);
}
/**
* tomoyo_audit_inet_log - Audit INET network log.
*
* @r: Pointer to "struct tomoyo_request_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_audit_inet_log(struct tomoyo_request_info *r)
{
char buf[128];
int len;
const __be32 *address = r->param.inet_network.address;
if (r->param.inet_network.is_ipv6)
tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *)
address, (const struct in6_addr *) address);
else
tomoyo_print_ipv4(buf, sizeof(buf), address, address);
len = strlen(buf);
snprintf(buf + len, sizeof(buf) - len, " %u",
r->param.inet_network.port);
return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol,
r->param.inet_network.operation, buf);
}
/**
* tomoyo_audit_unix_log - Audit UNIX network log.
*
* @r: Pointer to "struct tomoyo_request_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_audit_unix_log(struct tomoyo_request_info *r)
{
return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol,
r->param.unix_network.operation,
r->param.unix_network.address->name);
}
/**
* tomoyo_check_inet_acl - Check permission for inet domain socket operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @ptr: Pointer to "struct tomoyo_acl_info".
*
* Returns true if granted, false otherwise.
*/
static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_inet_acl *acl =
container_of(ptr, typeof(*acl), head);
const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4;
if (!(acl->perm & (1 << r->param.inet_network.operation)) ||
!tomoyo_compare_number_union(r->param.inet_network.port,
&acl->port))
return false;
if (acl->address.group)
return tomoyo_address_matches_group
(r->param.inet_network.is_ipv6,
r->param.inet_network.address, acl->address.group);
return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 &&
memcmp(&acl->address.ip[0],
r->param.inet_network.address, size) <= 0 &&
memcmp(r->param.inet_network.address,
&acl->address.ip[1], size) <= 0;
}
/**
* tomoyo_check_unix_acl - Check permission for unix domain socket operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @ptr: Pointer to "struct tomoyo_acl_info".
*
* Returns true if granted, false otherwise.
*/
static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_unix_acl *acl =
container_of(ptr, typeof(*acl), head);
return (acl->perm & (1 << r->param.unix_network.operation)) &&
tomoyo_compare_name_union(r->param.unix_network.address,
&acl->name);
}
/**
* tomoyo_inet_entry - Check permission for INET network operation.
*
* @address: Pointer to "struct tomoyo_addr_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_inet_entry(const struct tomoyo_addr_info *address)
{
const int idx = tomoyo_read_lock();
struct tomoyo_request_info r;
int error = 0;
const u8 type = tomoyo_inet2mac[address->protocol][address->operation];
if (type && tomoyo_init_request_info(&r, NULL, type)
!= TOMOYO_CONFIG_DISABLED) {
r.param_type = TOMOYO_TYPE_INET_ACL;
r.param.inet_network.protocol = address->protocol;
r.param.inet_network.operation = address->operation;
r.param.inet_network.is_ipv6 = address->inet.is_ipv6;
r.param.inet_network.address = address->inet.address;
r.param.inet_network.port = ntohs(address->inet.port);
do {
tomoyo_check_acl(&r, tomoyo_check_inet_acl);
error = tomoyo_audit_inet_log(&r);
} while (error == TOMOYO_RETRY_REQUEST);
}
tomoyo_read_unlock(idx);
return error;
}
/**
* tomoyo_check_inet_address - Check permission for inet domain socket's operation.
*
* @addr: Pointer to "struct sockaddr".
* @addr_len: Size of @addr.
* @port: Port number.
* @address: Pointer to "struct tomoyo_addr_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_check_inet_address(const struct sockaddr *addr,
const unsigned int addr_len,
const u16 port,
struct tomoyo_addr_info *address)
{
struct tomoyo_inet_addr_info *i = &address->inet;
switch (addr->sa_family) {
case AF_INET6:
if (addr_len < SIN6_LEN_RFC2133)
goto skip;
i->is_ipv6 = true;
i->address = (__be32 *)
((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr;
i->port = ((struct sockaddr_in6 *) addr)->sin6_port;
break;
case AF_INET:
if (addr_len < sizeof(struct sockaddr_in))
goto skip;
i->is_ipv6 = false;
i->address = (__be32 *)
&((struct sockaddr_in *) addr)->sin_addr;
i->port = ((struct sockaddr_in *) addr)->sin_port;
break;
default:
goto skip;
}
if (address->protocol == SOCK_RAW)
i->port = htons(port);
return tomoyo_inet_entry(address);
skip:
return 0;
}
/**
* tomoyo_unix_entry - Check permission for UNIX network operation.
*
* @address: Pointer to "struct tomoyo_addr_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_unix_entry(const struct tomoyo_addr_info *address)
{
const int idx = tomoyo_read_lock();
struct tomoyo_request_info r;
int error = 0;
const u8 type = tomoyo_unix2mac[address->protocol][address->operation];
if (type && tomoyo_init_request_info(&r, NULL, type)
!= TOMOYO_CONFIG_DISABLED) {
char *buf = address->unix0.addr;
int len = address->unix0.addr_len - sizeof(sa_family_t);
if (len <= 0) {
buf = "anonymous";
len = 9;
} else if (buf[0]) {
len = strnlen(buf, len);
}
buf = tomoyo_encode2(buf, len);
if (buf) {
struct tomoyo_path_info addr;
addr.name = buf;
tomoyo_fill_path_info(&addr);
r.param_type = TOMOYO_TYPE_UNIX_ACL;
r.param.unix_network.protocol = address->protocol;
r.param.unix_network.operation = address->operation;
r.param.unix_network.address = &addr;
do {
tomoyo_check_acl(&r, tomoyo_check_unix_acl);
error = tomoyo_audit_unix_log(&r);
} while (error == TOMOYO_RETRY_REQUEST);
kfree(buf);
} else
error = -ENOMEM;
}
tomoyo_read_unlock(idx);
return error;
}
/**
* tomoyo_check_unix_address - Check permission for unix domain socket's operation.
*
* @addr: Pointer to "struct sockaddr".
* @addr_len: Size of @addr.
* @address: Pointer to "struct tomoyo_addr_info".
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_check_unix_address(struct sockaddr *addr,
const unsigned int addr_len,
struct tomoyo_addr_info *address)
{
struct tomoyo_unix_addr_info *u = &address->unix0;
if (addr->sa_family != AF_UNIX)
return 0;
u->addr = ((struct sockaddr_un *) addr)->sun_path;
u->addr_len = addr_len;
return tomoyo_unix_entry(address);
}
/**
* tomoyo_kernel_service - Check whether I'm kernel service or not.
*
* Returns true if I'm kernel service, false otherwise.
*/
static bool tomoyo_kernel_service(void)
{
/* Nothing to do if I am a kernel service. */
return segment_eq(get_fs(), KERNEL_DS);
}
/**
* tomoyo_sock_family - Get socket's family.
*
* @sk: Pointer to "struct sock".
*
* Returns one of PF_INET, PF_INET6, PF_UNIX or 0.
*/
static u8 tomoyo_sock_family(struct sock *sk)
{
u8 family;
if (tomoyo_kernel_service())
return 0;
family = sk->sk_family;
switch (family) {
case PF_INET:
case PF_INET6:
case PF_UNIX:
return family;
default:
return 0;
}
}
/**
* tomoyo_socket_listen_permission - Check permission for listening a socket.
*
* @sock: Pointer to "struct socket".
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_socket_listen_permission(struct socket *sock)
{
struct tomoyo_addr_info address;
const u8 family = tomoyo_sock_family(sock->sk);
const unsigned int type = sock->type;
struct sockaddr_storage addr;
int addr_len;
if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET))
return 0;
{
const int error = sock->ops->getname(sock, (struct sockaddr *)
&addr, &addr_len, 0);
if (error)
return error;
}
address.protocol = type;
address.operation = TOMOYO_NETWORK_LISTEN;
if (family == PF_UNIX)
return tomoyo_check_unix_address((struct sockaddr *) &addr,
addr_len, &address);
return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len,
0, &address);
}
/**
* tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket.
*
* @sock: Pointer to "struct socket".
* @addr: Pointer to "struct sockaddr".
* @addr_len: Size of @addr.
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_socket_connect_permission(struct socket *sock,
struct sockaddr *addr, int addr_len)
{
struct tomoyo_addr_info address;
const u8 family = tomoyo_sock_family(sock->sk);
const unsigned int type = sock->type;
if (!family)
return 0;
address.protocol = type;
switch (type) {
case SOCK_DGRAM:
case SOCK_RAW:
address.operation = TOMOYO_NETWORK_SEND;
break;
case SOCK_STREAM:
case SOCK_SEQPACKET:
address.operation = TOMOYO_NETWORK_CONNECT;
break;
default:
return 0;
}
if (family == PF_UNIX)
return tomoyo_check_unix_address(addr, addr_len, &address);
return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
&address);
}
/**
* tomoyo_socket_bind_permission - Check permission for setting the local address of a socket.
*
* @sock: Pointer to "struct socket".
* @addr: Pointer to "struct sockaddr".
* @addr_len: Size of @addr.
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr,
int addr_len)
{
struct tomoyo_addr_info address;
const u8 family = tomoyo_sock_family(sock->sk);
const unsigned int type = sock->type;
if (!family)
return 0;
switch (type) {
case SOCK_STREAM:
case SOCK_DGRAM:
case SOCK_RAW:
case SOCK_SEQPACKET:
address.protocol = type;
address.operation = TOMOYO_NETWORK_BIND;
break;
default:
return 0;
}
if (family == PF_UNIX)
return tomoyo_check_unix_address(addr, addr_len, &address);
return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol,
&address);
}
/**
* tomoyo_socket_sendmsg_permission - Check permission for sending a datagram.
*
* @sock: Pointer to "struct socket".
* @msg: Pointer to "struct msghdr".
* @size: Unused.
*
* Returns 0 on success, negative value otherwise.
*/
int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg,
int size)
{
struct tomoyo_addr_info address;
const u8 family = tomoyo_sock_family(sock->sk);
const unsigned int type = sock->type;
if (!msg->msg_name || !family ||
(type != SOCK_DGRAM && type != SOCK_RAW))
return 0;
address.protocol = type;
address.operation = TOMOYO_NETWORK_SEND;
if (family == PF_UNIX)
return tomoyo_check_unix_address((struct sockaddr *)
msg->msg_name,
msg->msg_namelen, &address);
return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name,
msg->msg_namelen,
sock->sk->sk_protocol, &address);
}

View file

@ -15,17 +15,19 @@
#include "../../fs/internal.h"
/**
* tomoyo_encode: Convert binary string to ascii string.
* tomoyo_encode2 - Encode binary string to ascii string.
*
* @str: String in binary format.
* @str: String in binary format.
* @str_len: Size of @str in byte.
*
* Returns pointer to @str in ascii format on success, NULL otherwise.
*
* This function uses kzalloc(), so caller must kfree() if this function
* didn't return NULL.
*/
char *tomoyo_encode(const char *str)
char *tomoyo_encode2(const char *str, int str_len)
{
int i;
int len = 0;
const char *p = str;
char *cp;
@ -33,8 +35,9 @@ char *tomoyo_encode(const char *str)
if (!p)
return NULL;
while (*p) {
const unsigned char c = *p++;
for (i = 0; i < str_len; i++) {
const unsigned char c = p[i];
if (c == '\\')
len += 2;
else if (c > ' ' && c < 127)
@ -49,8 +52,8 @@ char *tomoyo_encode(const char *str)
return NULL;
cp0 = cp;
p = str;
while (*p) {
const unsigned char c = *p++;
for (i = 0; i < str_len; i++) {
const unsigned char c = p[i];
if (c == '\\') {
*cp++ = '\\';
@ -67,6 +70,21 @@ char *tomoyo_encode(const char *str)
return cp0;
}
/**
* tomoyo_encode - Encode binary string to ascii string.
*
* @str: String in binary format.
*
* Returns pointer to @str in ascii format on success, NULL otherwise.
*
* This function uses kzalloc(), so caller must kfree() if this function
* didn't return NULL.
*/
char *tomoyo_encode(const char *str)
{
return str ? tomoyo_encode2(str, strlen(str)) : NULL;
}
/**
* tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
*

View file

@ -7,6 +7,124 @@
#include <linux/security.h>
#include "common.h"
/**
* tomoyo_check_task_acl - Check permission for task operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @ptr: Pointer to "struct tomoyo_acl_info".
*
* Returns true if granted, false otherwise.
*/
static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
head);
return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
}
/**
* tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface.
*
* @file: Pointer to "struct file".
* @buf: Domainname to transit to.
* @count: Size of @buf.
* @ppos: Unused.
*
* Returns @count on success, negative value otherwise.
*
* If domain transition was permitted but the domain transition failed, this
* function returns error rather than terminating current thread with SIGKILL.
*/
static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *data;
int error;
if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
return -ENOMEM;
data = kzalloc(count + 1, GFP_NOFS);
if (!data)
return -ENOMEM;
if (copy_from_user(data, buf, count)) {
error = -EFAULT;
goto out;
}
tomoyo_normalize_line(data);
if (tomoyo_correct_domain(data)) {
const int idx = tomoyo_read_lock();
struct tomoyo_path_info name;
struct tomoyo_request_info r;
name.name = data;
tomoyo_fill_path_info(&name);
/* Check "task manual_domain_transition" permission. */
tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL;
r.param.task.domainname = &name;
tomoyo_check_acl(&r, tomoyo_check_task_acl);
if (!r.granted)
error = -EPERM;
else {
struct tomoyo_domain_info *new_domain =
tomoyo_assign_domain(data, true);
if (!new_domain) {
error = -ENOENT;
} else {
struct cred *cred = prepare_creds();
if (!cred) {
error = -ENOMEM;
} else {
struct tomoyo_domain_info *old_domain =
cred->security;
cred->security = new_domain;
atomic_inc(&new_domain->users);
atomic_dec(&old_domain->users);
commit_creds(cred);
error = 0;
}
}
}
tomoyo_read_unlock(idx);
} else
error = -EINVAL;
out:
kfree(data);
return error ? error : count;
}
/**
* tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface.
*
* @file: Pointer to "struct file".
* @buf: Domainname which current thread belongs to.
* @count: Size of @buf.
* @ppos: Bytes read by now.
*
* Returns read size on success, negative value otherwise.
*/
static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
const char *domain = tomoyo_domain()->domainname->name;
loff_t len = strlen(domain);
loff_t pos = *ppos;
if (pos >= len || !count)
return 0;
len -= pos;
if (count < len)
len = count;
if (copy_to_user(buf, domain + pos, len))
return -EFAULT;
*ppos += len;
return len;
}
/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */
static const struct file_operations tomoyo_self_operations = {
.write = tomoyo_write_self,
.read = tomoyo_read_self,
};
/**
* tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
*
@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void)
TOMOYO_EXCEPTIONPOLICY);
tomoyo_create_entry("audit", 0400, tomoyo_dir,
TOMOYO_AUDIT);
tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
TOMOYO_SELFDOMAIN);
tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
TOMOYO_PROCESS_STATUS);
tomoyo_create_entry("stat", 0644, tomoyo_dir,
@ -147,6 +263,9 @@ static int __init tomoyo_initerface_init(void)
TOMOYO_MANAGER);
tomoyo_create_entry("version", 0400, tomoyo_dir,
TOMOYO_VERSION);
securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL,
&tomoyo_self_operations);
tomoyo_load_builtin_policy();
return 0;
}

View file

@ -442,6 +442,64 @@ 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_socket_listen - Check permission for listen().
*
* @sock: Pointer to "struct socket".
* @backlog: Backlog parameter.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_socket_listen(struct socket *sock, int backlog)
{
return tomoyo_socket_listen_permission(sock);
}
/**
* tomoyo_socket_connect - Check permission for connect().
*
* @sock: Pointer to "struct socket".
* @addr: Pointer to "struct sockaddr".
* @addr_len: Size of @addr.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr,
int addr_len)
{
return tomoyo_socket_connect_permission(sock, addr, addr_len);
}
/**
* tomoyo_socket_bind - Check permission for bind().
*
* @sock: Pointer to "struct socket".
* @addr: Pointer to "struct sockaddr".
* @addr_len: Size of @addr.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr,
int addr_len)
{
return tomoyo_socket_bind_permission(sock, addr, addr_len);
}
/**
* tomoyo_socket_sendmsg - Check permission for sendmsg().
*
* @sock: Pointer to "struct socket".
* @msg: Pointer to "struct msghdr".
* @size: Size of message.
*
* Returns 0 on success, negative value otherwise.
*/
static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
return tomoyo_socket_sendmsg_permission(sock, msg, size);
}
/*
* tomoyo_security_ops is a "struct security_operations" which is used for
* registering TOMOYO.
@ -472,6 +530,10 @@ static struct security_operations tomoyo_security_ops = {
.sb_mount = tomoyo_sb_mount,
.sb_umount = tomoyo_sb_umount,
.sb_pivotroot = tomoyo_sb_pivotroot,
.socket_bind = tomoyo_socket_bind,
.socket_connect = tomoyo_socket_connect,
.socket_listen = tomoyo_socket_listen,
.socket_sendmsg = tomoyo_socket_sendmsg,
};
/* Lock for GC. */

View file

@ -42,6 +42,39 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = {
[TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE,
[TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE,
/* CONFIG::network group */
[TOMOYO_MAC_NETWORK_INET_STREAM_BIND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_INET_RAW_BIND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_INET_RAW_SEND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] =
TOMOYO_MAC_CATEGORY_NETWORK,
[TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] =
TOMOYO_MAC_CATEGORY_NETWORK,
/* CONFIG::misc group */
[TOMOYO_MAC_ENVIRON] = TOMOYO_MAC_CATEGORY_MISC,
};
/**
@ -125,6 +158,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
return pos;
}
/**
* tomoyo_get_domainname - Read a domainname from a line.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns a domainname on success, NULL otherwise.
*/
const struct tomoyo_path_info *tomoyo_get_domainname
(struct tomoyo_acl_param *param)
{
char *start = param->data;
char *pos = start;
while (*pos) {
if (*pos++ != ' ' || *pos++ == '/')
continue;
pos -= 2;
*pos++ = '\0';
break;
}
param->data = pos;
if (tomoyo_correct_domain(start))
return tomoyo_get_name(start);
return NULL;
}
/**
* tomoyo_parse_ulong - Parse an "unsigned long" value.
*
@ -920,14 +978,17 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
const u8 index)
{
u8 mode;
const u8 category = TOMOYO_MAC_CATEGORY_FILE;
struct tomoyo_profile *p;
if (!tomoyo_policy_loaded)
return TOMOYO_CONFIG_DISABLED;
mode = tomoyo_profile(ns, profile)->config[index];
p = tomoyo_profile(ns, profile);
mode = p->config[index];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
mode = tomoyo_profile(ns, profile)->config[category];
mode = p->config[tomoyo_index2category[index]
+ TOMOYO_MAX_MAC_INDEX];
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
mode = tomoyo_profile(ns, profile)->default_config;
mode = p->default_config;
return mode & 3;
}
@ -996,6 +1057,17 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r)
perm = container_of(ptr, struct tomoyo_mkdev_acl,
head)->perm;
break;
case TOMOYO_TYPE_INET_ACL:
perm = container_of(ptr, struct tomoyo_inet_acl,
head)->perm;
break;
case TOMOYO_TYPE_UNIX_ACL:
perm = container_of(ptr, struct tomoyo_unix_acl,
head)->perm;
break;
case TOMOYO_TYPE_MANUAL_TASK_ACL:
perm = 0;
break;
default:
perm = 1;
}