mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
74
security/tomoyo/Kconfig
Normal file
74
security/tomoyo/Kconfig
Normal file
|
@ -0,0 +1,74 @@
|
|||
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.
|
||||
Required userspace tools and further information may be
|
||||
found at <http://tomoyo.sourceforge.jp/>.
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
|
||||
int "Default maximal count for learning mode"
|
||||
default 2048
|
||||
range 0 2147483647
|
||||
depends on SECURITY_TOMOYO
|
||||
help
|
||||
This is the default value for maximal ACL entries
|
||||
that are automatically appended into policy at "learning mode".
|
||||
Some programs access thousands of objects, so running
|
||||
such programs in "learning mode" dulls the system response
|
||||
and consumes much memory.
|
||||
This is the safeguard for such programs.
|
||||
|
||||
config SECURITY_TOMOYO_MAX_AUDIT_LOG
|
||||
int "Default maximal count for audit log"
|
||||
default 1024
|
||||
range 0 2147483647
|
||||
depends on SECURITY_TOMOYO
|
||||
help
|
||||
This is the default value for maximal entries for
|
||||
audit logs that the kernel can hold on memory.
|
||||
You can read the log via /sys/kernel/security/tomoyo/audit.
|
||||
If you don't need audit logs, you may set this value to 0.
|
||||
|
||||
config SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
bool "Activate without calling userspace policy loader."
|
||||
default n
|
||||
depends on SECURITY_TOMOYO
|
||||
---help---
|
||||
Say Y here if you want to activate access control as soon as built-in
|
||||
policy was loaded. This option will be useful for systems where
|
||||
operations which can lead to the hijacking of the boot sequence are
|
||||
needed before loading the policy. For example, you can activate
|
||||
immediately after loading the fixed part of policy which will allow
|
||||
only operations needed for mounting a partition which contains the
|
||||
variant part of policy and verifying (e.g. running GPG check) and
|
||||
loading the variant part of policy. Since you can start using
|
||||
enforcing mode from the beginning, you can reduce the possibility of
|
||||
hijacking the boot sequence.
|
||||
|
||||
config SECURITY_TOMOYO_POLICY_LOADER
|
||||
string "Location of userspace policy loader"
|
||||
default "/sbin/tomoyo-init"
|
||||
depends on SECURITY_TOMOYO
|
||||
depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
---help---
|
||||
This is the default pathname of policy loader which is called before
|
||||
activation. You can override this setting via TOMOYO_loader= kernel
|
||||
command line option.
|
||||
|
||||
config SECURITY_TOMOYO_ACTIVATION_TRIGGER
|
||||
string "Trigger for calling userspace policy loader"
|
||||
default "/sbin/init"
|
||||
depends on SECURITY_TOMOYO
|
||||
depends on !SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
---help---
|
||||
This is the default pathname of activation trigger.
|
||||
You can override this setting via TOMOYO_trigger= kernel command line
|
||||
option. For example, if you pass init=/bin/systemd option, you may
|
||||
want to also pass TOMOYO_trigger=/bin/systemd option.
|
48
security/tomoyo/Makefile
Normal file
48
security/tomoyo/Makefile
Normal file
|
@ -0,0 +1,48 @@
|
|||
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/
|
||||
@echo Creating an empty policy/profile.conf
|
||||
@touch $@
|
||||
|
||||
$(obj)/policy/exception_policy.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating a default policy/exception_policy.conf
|
||||
@echo initialize_domain /sbin/modprobe from any >> $@
|
||||
@echo initialize_domain /sbin/hotplug from any >> $@
|
||||
|
||||
$(obj)/policy/domain_policy.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty policy/domain_policy.conf
|
||||
@touch $@
|
||||
|
||||
$(obj)/policy/manager.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty policy/manager.conf
|
||||
@touch $@
|
||||
|
||||
$(obj)/policy/stat.conf:
|
||||
@mkdir -p $(obj)/policy/
|
||||
@echo Creating an empty 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.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
|
||||
@echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp
|
||||
@sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp
|
||||
@echo "\"\";" >> $@.tmp
|
||||
@mv $@.tmp $@
|
||||
|
||||
$(obj)/common.o: $(obj)/builtin-policy.h
|
468
security/tomoyo/audit.c
Normal file
468
security/tomoyo/audit.c
Normal file
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* security/tomoyo/audit.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* tomoyo_print_bprm - Print "struct linux_binprm" for auditing.
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
* @dump: Pointer to "struct tomoyo_page_dump".
|
||||
*
|
||||
* Returns the contents of @bprm on success, NULL otherwise.
|
||||
*
|
||||
* This function uses kzalloc(), so caller must kfree() if this function
|
||||
* didn't return NULL.
|
||||
*/
|
||||
static char *tomoyo_print_bprm(struct linux_binprm *bprm,
|
||||
struct tomoyo_page_dump *dump)
|
||||
{
|
||||
static const int tomoyo_buffer_len = 4096 * 2;
|
||||
char *buffer = kzalloc(tomoyo_buffer_len, GFP_NOFS);
|
||||
char *cp;
|
||||
char *last_start;
|
||||
int len;
|
||||
unsigned long pos = bprm->p;
|
||||
int offset = pos % PAGE_SIZE;
|
||||
int argv_count = bprm->argc;
|
||||
int envp_count = bprm->envc;
|
||||
bool truncated = false;
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
len = snprintf(buffer, tomoyo_buffer_len - 1, "argv[]={ ");
|
||||
cp = buffer + len;
|
||||
if (!argv_count) {
|
||||
memmove(cp, "} envp[]={ ", 11);
|
||||
cp += 11;
|
||||
}
|
||||
last_start = cp;
|
||||
while (argv_count || envp_count) {
|
||||
if (!tomoyo_dump_page(bprm, pos, dump))
|
||||
goto out;
|
||||
pos += PAGE_SIZE - offset;
|
||||
/* Read. */
|
||||
while (offset < PAGE_SIZE) {
|
||||
const char *kaddr = dump->data;
|
||||
const unsigned char c = kaddr[offset++];
|
||||
if (cp == last_start)
|
||||
*cp++ = '"';
|
||||
if (cp >= buffer + tomoyo_buffer_len - 32) {
|
||||
/* Reserve some room for "..." string. */
|
||||
truncated = true;
|
||||
} else if (c == '\\') {
|
||||
*cp++ = '\\';
|
||||
*cp++ = '\\';
|
||||
} else if (c > ' ' && c < 127) {
|
||||
*cp++ = c;
|
||||
} else if (!c) {
|
||||
*cp++ = '"';
|
||||
*cp++ = ' ';
|
||||
last_start = cp;
|
||||
} else {
|
||||
*cp++ = '\\';
|
||||
*cp++ = (c >> 6) + '0';
|
||||
*cp++ = ((c >> 3) & 7) + '0';
|
||||
*cp++ = (c & 7) + '0';
|
||||
}
|
||||
if (c)
|
||||
continue;
|
||||
if (argv_count) {
|
||||
if (--argv_count == 0) {
|
||||
if (truncated) {
|
||||
cp = last_start;
|
||||
memmove(cp, "... ", 4);
|
||||
cp += 4;
|
||||
}
|
||||
memmove(cp, "} envp[]={ ", 11);
|
||||
cp += 11;
|
||||
last_start = cp;
|
||||
truncated = false;
|
||||
}
|
||||
} else if (envp_count) {
|
||||
if (--envp_count == 0) {
|
||||
if (truncated) {
|
||||
cp = last_start;
|
||||
memmove(cp, "... ", 4);
|
||||
cp += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!argv_count && !envp_count)
|
||||
break;
|
||||
}
|
||||
offset = 0;
|
||||
}
|
||||
*cp++ = '}';
|
||||
*cp = '\0';
|
||||
return buffer;
|
||||
out:
|
||||
snprintf(buffer, tomoyo_buffer_len - 1,
|
||||
"argv[]={ ... } envp[]= { ... }");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_filetype - Get string representation of file type.
|
||||
*
|
||||
* @mode: Mode value for stat().
|
||||
*
|
||||
* Returns file type string.
|
||||
*/
|
||||
static inline const char *tomoyo_filetype(const umode_t mode)
|
||||
{
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
case 0:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FILE];
|
||||
case S_IFDIR:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_DIRECTORY];
|
||||
case S_IFLNK:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SYMLINK];
|
||||
case S_IFIFO:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_FIFO];
|
||||
case S_IFSOCK:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_SOCKET];
|
||||
case S_IFBLK:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_BLOCK_DEV];
|
||||
case S_IFCHR:
|
||||
return tomoyo_condition_keyword[TOMOYO_TYPE_IS_CHAR_DEV];
|
||||
}
|
||||
return "unknown"; /* This should not happen. */
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_print_header - Get header line of audit log.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
*
|
||||
* Returns string representation.
|
||||
*
|
||||
* This function uses kmalloc(), so caller must kfree() if this function
|
||||
* didn't return NULL.
|
||||
*/
|
||||
static char *tomoyo_print_header(struct tomoyo_request_info *r)
|
||||
{
|
||||
struct tomoyo_time stamp;
|
||||
const pid_t gpid = task_pid_nr(current);
|
||||
struct tomoyo_obj_info *obj = r->obj;
|
||||
static const int tomoyo_buffer_len = 4096;
|
||||
char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS);
|
||||
int pos;
|
||||
u8 i;
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
tomoyo_convert_time(get_seconds(), &stamp);
|
||||
|
||||
pos = snprintf(buffer, tomoyo_buffer_len - 1,
|
||||
"#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s "
|
||||
"granted=%s (global-pid=%u) task={ pid=%u ppid=%u "
|
||||
"uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u "
|
||||
"fsuid=%u fsgid=%u }", stamp.year, stamp.month,
|
||||
stamp.day, stamp.hour, stamp.min, stamp.sec, r->profile,
|
||||
tomoyo_mode[r->mode], tomoyo_yesno(r->granted), gpid,
|
||||
tomoyo_sys_getpid(), tomoyo_sys_getppid(),
|
||||
from_kuid(&init_user_ns, current_uid()),
|
||||
from_kgid(&init_user_ns, current_gid()),
|
||||
from_kuid(&init_user_ns, current_euid()),
|
||||
from_kgid(&init_user_ns, current_egid()),
|
||||
from_kuid(&init_user_ns, current_suid()),
|
||||
from_kgid(&init_user_ns, current_sgid()),
|
||||
from_kuid(&init_user_ns, current_fsuid()),
|
||||
from_kgid(&init_user_ns, current_fsgid()));
|
||||
if (!obj)
|
||||
goto no_obj_info;
|
||||
if (!obj->validate_done) {
|
||||
tomoyo_get_attributes(obj);
|
||||
obj->validate_done = true;
|
||||
}
|
||||
for (i = 0; i < TOMOYO_MAX_PATH_STAT; i++) {
|
||||
struct tomoyo_mini_stat *stat;
|
||||
unsigned int dev;
|
||||
umode_t mode;
|
||||
if (!obj->stat_valid[i])
|
||||
continue;
|
||||
stat = &obj->stat[i];
|
||||
dev = stat->dev;
|
||||
mode = stat->mode;
|
||||
if (i & 1) {
|
||||
pos += snprintf(buffer + pos,
|
||||
tomoyo_buffer_len - 1 - pos,
|
||||
" path%u.parent={ uid=%u gid=%u "
|
||||
"ino=%lu perm=0%o }", (i >> 1) + 1,
|
||||
from_kuid(&init_user_ns, stat->uid),
|
||||
from_kgid(&init_user_ns, stat->gid),
|
||||
(unsigned long)stat->ino,
|
||||
stat->mode & S_IALLUGO);
|
||||
continue;
|
||||
}
|
||||
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
|
||||
" path%u={ uid=%u gid=%u ino=%lu major=%u"
|
||||
" minor=%u perm=0%o type=%s", (i >> 1) + 1,
|
||||
from_kuid(&init_user_ns, stat->uid),
|
||||
from_kgid(&init_user_ns, stat->gid),
|
||||
(unsigned long)stat->ino,
|
||||
MAJOR(dev), MINOR(dev),
|
||||
mode & S_IALLUGO, tomoyo_filetype(mode));
|
||||
if (S_ISCHR(mode) || S_ISBLK(mode)) {
|
||||
dev = stat->rdev;
|
||||
pos += snprintf(buffer + pos,
|
||||
tomoyo_buffer_len - 1 - pos,
|
||||
" dev_major=%u dev_minor=%u",
|
||||
MAJOR(dev), MINOR(dev));
|
||||
}
|
||||
pos += snprintf(buffer + pos, tomoyo_buffer_len - 1 - pos,
|
||||
" }");
|
||||
}
|
||||
no_obj_info:
|
||||
if (pos < tomoyo_buffer_len - 1)
|
||||
return buffer;
|
||||
kfree(buffer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_init_log - Allocate buffer for audit logs.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
* @len: Buffer size needed for @fmt and @args.
|
||||
* @fmt: The printf()'s format string.
|
||||
* @args: va_list structure for @fmt.
|
||||
*
|
||||
* Returns pointer to allocated memory.
|
||||
*
|
||||
* This function uses kzalloc(), so caller must kfree() if this function
|
||||
* didn't return NULL.
|
||||
*/
|
||||
char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
char *buf = NULL;
|
||||
char *bprm_info = NULL;
|
||||
const char *header = NULL;
|
||||
char *realpath = NULL;
|
||||
const char *symlink = NULL;
|
||||
int pos;
|
||||
const char *domainname = r->domain->domainname->name;
|
||||
header = tomoyo_print_header(r);
|
||||
if (!header)
|
||||
return NULL;
|
||||
/* +10 is for '\n' etc. and '\0'. */
|
||||
len += strlen(domainname) + strlen(header) + 10;
|
||||
if (r->ee) {
|
||||
struct file *file = r->ee->bprm->file;
|
||||
realpath = tomoyo_realpath_from_path(&file->f_path);
|
||||
bprm_info = tomoyo_print_bprm(r->ee->bprm, &r->ee->dump);
|
||||
if (!realpath || !bprm_info)
|
||||
goto out;
|
||||
/* +80 is for " exec={ realpath=\"%s\" argc=%d envc=%d %s }" */
|
||||
len += strlen(realpath) + 80 + strlen(bprm_info);
|
||||
} else if (r->obj && r->obj->symlink_target) {
|
||||
symlink = r->obj->symlink_target->name;
|
||||
/* +18 is for " symlink.target=\"%s\"" */
|
||||
len += 18 + strlen(symlink);
|
||||
}
|
||||
len = tomoyo_round2(len);
|
||||
buf = kzalloc(len, GFP_NOFS);
|
||||
if (!buf)
|
||||
goto out;
|
||||
len--;
|
||||
pos = snprintf(buf, len, "%s", header);
|
||||
if (realpath) {
|
||||
struct linux_binprm *bprm = r->ee->bprm;
|
||||
pos += snprintf(buf + pos, len - pos,
|
||||
" exec={ realpath=\"%s\" argc=%d envc=%d %s }",
|
||||
realpath, bprm->argc, bprm->envc, bprm_info);
|
||||
} else if (symlink)
|
||||
pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
|
||||
symlink);
|
||||
pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
|
||||
vsnprintf(buf + pos, len - pos, fmt, args);
|
||||
out:
|
||||
kfree(realpath);
|
||||
kfree(bprm_info);
|
||||
kfree(header);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Wait queue for /sys/kernel/security/tomoyo/audit. */
|
||||
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait);
|
||||
|
||||
/* Structure for audit log. */
|
||||
struct tomoyo_log {
|
||||
struct list_head list;
|
||||
char *log;
|
||||
int size;
|
||||
};
|
||||
|
||||
/* The list for "struct tomoyo_log". */
|
||||
static LIST_HEAD(tomoyo_log);
|
||||
|
||||
/* Lock for "struct list_head tomoyo_log". */
|
||||
static DEFINE_SPINLOCK(tomoyo_log_lock);
|
||||
|
||||
/* Length of "stuct list_head tomoyo_log". */
|
||||
static unsigned int tomoyo_log_count;
|
||||
|
||||
/**
|
||||
* tomoyo_get_audit - Get audit mode.
|
||||
*
|
||||
* @ns: Pointer to "struct tomoyo_policy_namespace".
|
||||
* @profile: Profile number.
|
||||
* @index: Index number of functionality.
|
||||
* @is_granted: True if granted log, false otherwise.
|
||||
*
|
||||
* Returns true if this request should be audited, false otherwise.
|
||||
*/
|
||||
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;
|
||||
const u8 category = tomoyo_index2category[index] +
|
||||
TOMOYO_MAX_MAC_INDEX;
|
||||
struct tomoyo_profile *p;
|
||||
if (!tomoyo_policy_loaded)
|
||||
return false;
|
||||
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];
|
||||
if (mode == TOMOYO_CONFIG_USE_DEFAULT)
|
||||
mode = p->default_config;
|
||||
if (is_granted)
|
||||
return mode & TOMOYO_CONFIG_WANT_GRANT_LOG;
|
||||
return mode & TOMOYO_CONFIG_WANT_REJECT_LOG;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_write_log2 - Write an audit log.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
* @len: Buffer size needed for @fmt and @args.
|
||||
* @fmt: The printf()'s format string.
|
||||
* @args: va_list structure for @fmt.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
char *buf;
|
||||
struct tomoyo_log *entry;
|
||||
bool quota_exceeded = false;
|
||||
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)
|
||||
goto out;
|
||||
entry = kzalloc(sizeof(*entry), GFP_NOFS);
|
||||
if (!entry) {
|
||||
kfree(buf);
|
||||
goto out;
|
||||
}
|
||||
entry->log = buf;
|
||||
len = tomoyo_round2(strlen(buf) + 1);
|
||||
/*
|
||||
* The entry->size is used for memory quota checks.
|
||||
* Don't go beyond strlen(entry->log).
|
||||
*/
|
||||
entry->size = len + tomoyo_round2(sizeof(*entry));
|
||||
spin_lock(&tomoyo_log_lock);
|
||||
if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] &&
|
||||
tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >=
|
||||
tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) {
|
||||
quota_exceeded = true;
|
||||
} else {
|
||||
tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size;
|
||||
list_add_tail(&entry->list, &tomoyo_log);
|
||||
tomoyo_log_count++;
|
||||
}
|
||||
spin_unlock(&tomoyo_log_lock);
|
||||
if (quota_exceeded) {
|
||||
kfree(buf);
|
||||
kfree(entry);
|
||||
goto out;
|
||||
}
|
||||
wake_up(&tomoyo_log_wait);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_write_log - Write an audit log.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
* @fmt: The printf()'s format string, followed by parameters.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int len;
|
||||
va_start(args, fmt);
|
||||
len = vsnprintf((char *) &len, 1, fmt, args) + 1;
|
||||
va_end(args);
|
||||
va_start(args, fmt);
|
||||
tomoyo_write_log2(r, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_read_log - Read an audit log.
|
||||
*
|
||||
* @head: Pointer to "struct tomoyo_io_buffer".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void tomoyo_read_log(struct tomoyo_io_buffer *head)
|
||||
{
|
||||
struct tomoyo_log *ptr = NULL;
|
||||
if (head->r.w_pos)
|
||||
return;
|
||||
kfree(head->read_buf);
|
||||
head->read_buf = NULL;
|
||||
spin_lock(&tomoyo_log_lock);
|
||||
if (!list_empty(&tomoyo_log)) {
|
||||
ptr = list_entry(tomoyo_log.next, typeof(*ptr), list);
|
||||
list_del(&ptr->list);
|
||||
tomoyo_log_count--;
|
||||
tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size;
|
||||
}
|
||||
spin_unlock(&tomoyo_log_lock);
|
||||
if (ptr) {
|
||||
head->read_buf = ptr->log;
|
||||
head->r.w[head->r.w_pos++] = head->read_buf;
|
||||
kfree(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_poll_log - Wait for an audit log.
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
* @wait: Pointer to "poll_table". Maybe NULL.
|
||||
*
|
||||
* Returns POLLIN | POLLRDNORM when ready to read an audit log.
|
||||
*/
|
||||
unsigned int tomoyo_poll_log(struct file *file, poll_table *wait)
|
||||
{
|
||||
if (tomoyo_log_count)
|
||||
return POLLIN | POLLRDNORM;
|
||||
poll_wait(file, &tomoyo_log_wait, wait);
|
||||
if (tomoyo_log_count)
|
||||
return POLLIN | POLLRDNORM;
|
||||
return 0;
|
||||
}
|
2789
security/tomoyo/common.c
Normal file
2789
security/tomoyo/common.c
Normal file
File diff suppressed because it is too large
Load diff
1330
security/tomoyo/common.h
Normal file
1330
security/tomoyo/common.h
Normal file
File diff suppressed because it is too large
Load diff
1094
security/tomoyo/condition.c
Normal file
1094
security/tomoyo/condition.c
Normal file
File diff suppressed because it is too large
Load diff
901
security/tomoyo/domain.c
Normal file
901
security/tomoyo/domain.c
Normal file
|
@ -0,0 +1,901 @@
|
|||
/*
|
||||
* security/tomoyo/domain.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Variables definitions.*/
|
||||
|
||||
/* The initial domain. */
|
||||
struct tomoyo_domain_info tomoyo_kernel_domain;
|
||||
|
||||
/**
|
||||
* tomoyo_update_policy - Update an entry for exception policy.
|
||||
*
|
||||
* @new_entry: Pointer to "struct tomoyo_acl_info".
|
||||
* @size: Size of @new_entry in bytes.
|
||||
* @param: Pointer to "struct tomoyo_acl_param".
|
||||
* @check_duplicate: Callback function to find duplicated entry.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
|
||||
struct tomoyo_acl_param *param,
|
||||
bool (*check_duplicate) (const struct tomoyo_acl_head
|
||||
*,
|
||||
const struct tomoyo_acl_head
|
||||
*))
|
||||
{
|
||||
int error = param->is_delete ? -ENOENT : -ENOMEM;
|
||||
struct tomoyo_acl_head *entry;
|
||||
struct list_head *list = param->list;
|
||||
|
||||
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;
|
||||
error = 0;
|
||||
break;
|
||||
}
|
||||
if (error && !param->is_delete) {
|
||||
entry = tomoyo_commit_ok(new_entry, size);
|
||||
if (entry) {
|
||||
list_add_tail_rcu(&entry->list, list);
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_same_acl_head - Check for duplicated "struct tomoyo_acl_info" entry.
|
||||
*
|
||||
* @a: Pointer to "struct tomoyo_acl_info".
|
||||
* @b: Pointer to "struct tomoyo_acl_info".
|
||||
*
|
||||
* Returns true if @a == @b, false otherwise.
|
||||
*/
|
||||
static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
|
||||
const struct tomoyo_acl_info *b)
|
||||
{
|
||||
return a->type == b->type && a->cond == b->cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_update_domain - Update an entry for domain policy.
|
||||
*
|
||||
* @new_entry: Pointer to "struct tomoyo_acl_info".
|
||||
* @size: Size of @new_entry in bytes.
|
||||
* @param: Pointer to "struct tomoyo_acl_param".
|
||||
* @check_duplicate: Callback function to find duplicated entry.
|
||||
* @merge_duplicate: Callback function to merge duplicated entry.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
|
||||
struct tomoyo_acl_param *param,
|
||||
bool (*check_duplicate) (const struct tomoyo_acl_info
|
||||
*,
|
||||
const struct tomoyo_acl_info
|
||||
*),
|
||||
bool (*merge_duplicate) (struct tomoyo_acl_info *,
|
||||
struct tomoyo_acl_info *,
|
||||
const bool))
|
||||
{
|
||||
const bool is_delete = param->is_delete;
|
||||
int error = is_delete ? -ENOENT : -ENOMEM;
|
||||
struct tomoyo_acl_info *entry;
|
||||
struct list_head * const list = param->list;
|
||||
|
||||
if (param->data[0]) {
|
||||
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;
|
||||
if (merge_duplicate)
|
||||
entry->is_deleted = merge_duplicate(entry, new_entry,
|
||||
is_delete);
|
||||
else
|
||||
entry->is_deleted = is_delete;
|
||||
error = 0;
|
||||
break;
|
||||
}
|
||||
if (error && !is_delete) {
|
||||
entry = tomoyo_commit_ok(new_entry, size);
|
||||
if (entry) {
|
||||
list_add_tail_rcu(&entry->list, list);
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
out:
|
||||
tomoyo_put_condition(new_entry->cond);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_check_acl - Do permission check.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
* @check_entry: Callback function to check type specific parameters.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
void tomoyo_check_acl(struct tomoyo_request_info *r,
|
||||
bool (*check_entry) (struct tomoyo_request_info *,
|
||||
const struct tomoyo_acl_info *))
|
||||
{
|
||||
const struct tomoyo_domain_info *domain = r->domain;
|
||||
struct tomoyo_acl_info *ptr;
|
||||
bool retried = false;
|
||||
const struct list_head *list = &domain->acl_info_list;
|
||||
|
||||
retry:
|
||||
list_for_each_entry_rcu(ptr, list, list) {
|
||||
if (ptr->is_deleted || ptr->type != r->param_type)
|
||||
continue;
|
||||
if (!check_entry(r, ptr))
|
||||
continue;
|
||||
if (!tomoyo_condition(r, ptr->cond))
|
||||
continue;
|
||||
r->matched_acl = ptr;
|
||||
r->granted = true;
|
||||
return;
|
||||
}
|
||||
if (!retried) {
|
||||
retried = true;
|
||||
list = &domain->ns->acl_group[domain->group];
|
||||
goto retry;
|
||||
}
|
||||
r->granted = false;
|
||||
}
|
||||
|
||||
/* The list for "struct tomoyo_domain_info". */
|
||||
LIST_HEAD(tomoyo_domain_list);
|
||||
|
||||
/**
|
||||
* tomoyo_last_word - Get last component of a domainname.
|
||||
*
|
||||
* @name: Domainname to check.
|
||||
*
|
||||
* Returns the last word of @domainname.
|
||||
*/
|
||||
static const char *tomoyo_last_word(const char *name)
|
||||
{
|
||||
const char *cp = strrchr(name, ' ');
|
||||
if (cp)
|
||||
return cp + 1;
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_same_transition_control - Check for duplicated "struct tomoyo_transition_control" 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_transition_control(const struct tomoyo_acl_head *a,
|
||||
const struct tomoyo_acl_head *b)
|
||||
{
|
||||
const struct tomoyo_transition_control *p1 = container_of(a,
|
||||
typeof(*p1),
|
||||
head);
|
||||
const struct tomoyo_transition_control *p2 = container_of(b,
|
||||
typeof(*p2),
|
||||
head);
|
||||
return p1->type == p2->type && p1->is_last_name == p2->is_last_name
|
||||
&& p1->domainname == p2->domainname
|
||||
&& p1->program == p2->program;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list.
|
||||
*
|
||||
* @param: Pointer to "struct tomoyo_acl_param".
|
||||
* @type: Type of this entry.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
int tomoyo_write_transition_control(struct tomoyo_acl_param *param,
|
||||
const u8 type)
|
||||
{
|
||||
struct tomoyo_transition_control e = { .type = type };
|
||||
int error = param->is_delete ? -ENOENT : -ENOMEM;
|
||||
char *program = param->data;
|
||||
char *domainname = strstr(program, " from ");
|
||||
if (domainname) {
|
||||
*domainname = '\0';
|
||||
domainname += 6;
|
||||
} else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP ||
|
||||
type == TOMOYO_TRANSITION_CONTROL_KEEP) {
|
||||
domainname = program;
|
||||
program = NULL;
|
||||
}
|
||||
if (program && strcmp(program, "any")) {
|
||||
if (!tomoyo_correct_path(program))
|
||||
return -EINVAL;
|
||||
e.program = tomoyo_get_name(program);
|
||||
if (!e.program)
|
||||
goto out;
|
||||
}
|
||||
if (domainname && strcmp(domainname, "any")) {
|
||||
if (!tomoyo_correct_domain(domainname)) {
|
||||
if (!tomoyo_correct_path(domainname))
|
||||
goto out;
|
||||
e.is_last_name = true;
|
||||
}
|
||||
e.domainname = tomoyo_get_name(domainname);
|
||||
if (!e.domainname)
|
||||
goto out;
|
||||
}
|
||||
param->list = ¶m->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
|
||||
error = tomoyo_update_policy(&e.head, sizeof(e), param,
|
||||
tomoyo_same_transition_control);
|
||||
out:
|
||||
tomoyo_put_name(e.domainname);
|
||||
tomoyo_put_name(e.program);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_scan_transition - Try to find specific domain transition type.
|
||||
*
|
||||
* @list: Pointer to "struct list_head".
|
||||
* @domainname: The name of current domain.
|
||||
* @program: The name of requested program.
|
||||
* @last_name: The last component of @domainname.
|
||||
* @type: One of values in "enum tomoyo_transition_type".
|
||||
*
|
||||
* Returns true if found one, false otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
static inline bool tomoyo_scan_transition
|
||||
(const struct list_head *list, const struct tomoyo_path_info *domainname,
|
||||
const struct tomoyo_path_info *program, const char *last_name,
|
||||
const enum tomoyo_transition_type type)
|
||||
{
|
||||
const struct tomoyo_transition_control *ptr;
|
||||
list_for_each_entry_rcu(ptr, list, head.list) {
|
||||
if (ptr->head.is_deleted || ptr->type != type)
|
||||
continue;
|
||||
if (ptr->domainname) {
|
||||
if (!ptr->is_last_name) {
|
||||
if (ptr->domainname != domainname)
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
* Use direct strcmp() since this is
|
||||
* unlikely used.
|
||||
*/
|
||||
if (strcmp(ptr->domainname->name, last_name))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ptr->program && tomoyo_pathcmp(ptr->program, program))
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_transition_type - Get domain transition type.
|
||||
*
|
||||
* @ns: Pointer to "struct tomoyo_policy_namespace".
|
||||
* @domainname: The name of current domain.
|
||||
* @program: The name of requested program.
|
||||
*
|
||||
* Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes
|
||||
* domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if
|
||||
* executing @program reinitializes domain transition within that namespace,
|
||||
* TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname ,
|
||||
* others otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
static enum tomoyo_transition_type tomoyo_transition_type
|
||||
(const struct tomoyo_policy_namespace *ns,
|
||||
const struct tomoyo_path_info *domainname,
|
||||
const struct tomoyo_path_info *program)
|
||||
{
|
||||
const char *last_name = tomoyo_last_word(domainname->name);
|
||||
enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET;
|
||||
while (type < TOMOYO_MAX_TRANSITION_TYPE) {
|
||||
const struct list_head * const list =
|
||||
&ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL];
|
||||
if (!tomoyo_scan_transition(list, domainname, program,
|
||||
last_name, type)) {
|
||||
type++;
|
||||
continue;
|
||||
}
|
||||
if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET &&
|
||||
type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE)
|
||||
break;
|
||||
/*
|
||||
* Do not check for reset_domain if no_reset_domain matched.
|
||||
* Do not check for initialize_domain if no_initialize_domain
|
||||
* matched.
|
||||
*/
|
||||
type++;
|
||||
type++;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_same_aggregator - Check for duplicated "struct tomoyo_aggregator" 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_aggregator(const struct tomoyo_acl_head *a,
|
||||
const struct tomoyo_acl_head *b)
|
||||
{
|
||||
const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1),
|
||||
head);
|
||||
const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2),
|
||||
head);
|
||||
return p1->original_name == p2->original_name &&
|
||||
p1->aggregated_name == p2->aggregated_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list.
|
||||
*
|
||||
* @param: Pointer to "struct tomoyo_acl_param".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
int tomoyo_write_aggregator(struct tomoyo_acl_param *param)
|
||||
{
|
||||
struct tomoyo_aggregator e = { };
|
||||
int error = param->is_delete ? -ENOENT : -ENOMEM;
|
||||
const char *original_name = tomoyo_read_token(param);
|
||||
const char *aggregated_name = tomoyo_read_token(param);
|
||||
if (!tomoyo_correct_word(original_name) ||
|
||||
!tomoyo_correct_path(aggregated_name))
|
||||
return -EINVAL;
|
||||
e.original_name = tomoyo_get_name(original_name);
|
||||
e.aggregated_name = tomoyo_get_name(aggregated_name);
|
||||
if (!e.original_name || !e.aggregated_name ||
|
||||
e.aggregated_name->is_patterned) /* No patterns allowed. */
|
||||
goto out;
|
||||
param->list = ¶m->ns->policy_list[TOMOYO_ID_AGGREGATOR];
|
||||
error = tomoyo_update_policy(&e.head, sizeof(e), param,
|
||||
tomoyo_same_aggregator);
|
||||
out:
|
||||
tomoyo_put_name(e.original_name);
|
||||
tomoyo_put_name(e.aggregated_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_find_namespace - Find specified namespace.
|
||||
*
|
||||
* @name: Name of namespace to find.
|
||||
* @len: Length of @name.
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_policy_namespace" if found,
|
||||
* NULL otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
static struct tomoyo_policy_namespace *tomoyo_find_namespace
|
||||
(const char *name, const unsigned int len)
|
||||
{
|
||||
struct tomoyo_policy_namespace *ns;
|
||||
list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
|
||||
if (strncmp(name, ns->name, len) ||
|
||||
(name[len] && name[len] != ' '))
|
||||
continue;
|
||||
return ns;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_assign_namespace - Create a new namespace.
|
||||
*
|
||||
* @domainname: Name of namespace to create.
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_policy_namespace" on success,
|
||||
* NULL otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname)
|
||||
{
|
||||
struct tomoyo_policy_namespace *ptr;
|
||||
struct tomoyo_policy_namespace *entry;
|
||||
const char *cp = domainname;
|
||||
unsigned int len = 0;
|
||||
while (*cp && *cp++ != ' ')
|
||||
len++;
|
||||
ptr = tomoyo_find_namespace(domainname, len);
|
||||
if (ptr)
|
||||
return ptr;
|
||||
if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname))
|
||||
return NULL;
|
||||
entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS);
|
||||
if (!entry)
|
||||
return NULL;
|
||||
if (mutex_lock_interruptible(&tomoyo_policy_lock))
|
||||
goto out;
|
||||
ptr = tomoyo_find_namespace(domainname, len);
|
||||
if (!ptr && tomoyo_memory_ok(entry)) {
|
||||
char *name = (char *) (entry + 1);
|
||||
ptr = entry;
|
||||
memmove(name, domainname, len);
|
||||
name[len] = '\0';
|
||||
entry->name = name;
|
||||
tomoyo_init_policy_namespace(entry);
|
||||
entry = NULL;
|
||||
}
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
out:
|
||||
kfree(entry);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_namespace_jump - Check for namespace jump.
|
||||
*
|
||||
* @domainname: Name of domain.
|
||||
*
|
||||
* Returns true if namespace differs, false otherwise.
|
||||
*/
|
||||
static bool tomoyo_namespace_jump(const char *domainname)
|
||||
{
|
||||
const char *namespace = tomoyo_current_namespace()->name;
|
||||
const int len = strlen(namespace);
|
||||
return strncmp(domainname, namespace, len) ||
|
||||
(domainname[len] && domainname[len] != ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_assign_domain - Create a domain or a namespace.
|
||||
*
|
||||
* @domainname: The name of domain.
|
||||
* @transit: True if transit to domain found or created.
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname,
|
||||
const bool transit)
|
||||
{
|
||||
struct tomoyo_domain_info e = { };
|
||||
struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname);
|
||||
bool created = false;
|
||||
if (entry) {
|
||||
if (transit) {
|
||||
/*
|
||||
* Since namespace is created at runtime, profiles may
|
||||
* not be created by the moment the process transits to
|
||||
* that domain. Do not perform domain transition if
|
||||
* profile for that domain is not yet created.
|
||||
*/
|
||||
if (tomoyo_policy_loaded &&
|
||||
!entry->ns->profile_ptr[entry->profile])
|
||||
return NULL;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
/* Requested domain does not exist. */
|
||||
/* Don't create requested domain if domainname is invalid. */
|
||||
if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 ||
|
||||
!tomoyo_correct_domain(domainname))
|
||||
return NULL;
|
||||
/*
|
||||
* Since definition of profiles and acl_groups may differ across
|
||||
* namespaces, do not inherit "use_profile" and "use_group" settings
|
||||
* by automatically creating requested domain upon domain transition.
|
||||
*/
|
||||
if (transit && tomoyo_namespace_jump(domainname))
|
||||
return NULL;
|
||||
e.ns = tomoyo_assign_namespace(domainname);
|
||||
if (!e.ns)
|
||||
return NULL;
|
||||
/*
|
||||
* "use_profile" and "use_group" settings for automatically created
|
||||
* domains are inherited from current domain. These are 0 for manually
|
||||
* created domains.
|
||||
*/
|
||||
if (transit) {
|
||||
const struct tomoyo_domain_info *domain = tomoyo_domain();
|
||||
e.profile = domain->profile;
|
||||
e.group = domain->group;
|
||||
}
|
||||
e.domainname = tomoyo_get_name(domainname);
|
||||
if (!e.domainname)
|
||||
return NULL;
|
||||
if (mutex_lock_interruptible(&tomoyo_policy_lock))
|
||||
goto out;
|
||||
entry = tomoyo_find_domain(domainname);
|
||||
if (!entry) {
|
||||
entry = tomoyo_commit_ok(&e, sizeof(e));
|
||||
if (entry) {
|
||||
INIT_LIST_HEAD(&entry->acl_info_list);
|
||||
list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
|
||||
created = true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
out:
|
||||
tomoyo_put_name(e.domainname);
|
||||
if (entry && transit) {
|
||||
if (created) {
|
||||
struct tomoyo_request_info r;
|
||||
tomoyo_init_request_info(&r, entry,
|
||||
TOMOYO_MAC_FILE_EXECUTE);
|
||||
r.granted = false;
|
||||
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.
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
int tomoyo_find_next_domain(struct linux_binprm *bprm)
|
||||
{
|
||||
struct tomoyo_domain_info *old_domain = tomoyo_domain();
|
||||
struct tomoyo_domain_info *domain = NULL;
|
||||
const char *original_name = bprm->filename;
|
||||
int retval = -ENOMEM;
|
||||
bool reject_on_transition_failure = false;
|
||||
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);
|
||||
if (!ee->tmp) {
|
||||
kfree(ee);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* ee->dump->data is allocated by tomoyo_dump_page(). */
|
||||
tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE);
|
||||
ee->r.ee = ee;
|
||||
ee->bprm = bprm;
|
||||
ee->r.obj = &ee->obj;
|
||||
ee->obj.path1 = bprm->file->f_path;
|
||||
/* Get symlink's pathname of program. */
|
||||
retval = -ENOENT;
|
||||
exename.name = tomoyo_realpath_nofollow(original_name);
|
||||
if (!exename.name)
|
||||
goto out;
|
||||
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(&exename,
|
||||
ptr->original_name))
|
||||
continue;
|
||||
candidate = ptr->aggregated_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check execute permission. */
|
||||
retval = tomoyo_execute_permission(&ee->r, candidate);
|
||||
if (retval == TOMOYO_RETRY_REQUEST)
|
||||
goto retry;
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
/*
|
||||
* To be able to specify domainnames with wildcards, use the
|
||||
* pathname specified in the policy (which may contain
|
||||
* wildcard) rather than the pathname passed to execve()
|
||||
* (which never contains wildcard).
|
||||
*/
|
||||
if (ee->r.param.path.matched_path)
|
||||
candidate = ee->r.param.path.matched_path;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
candidate)) {
|
||||
case TOMOYO_TRANSITION_CONTROL_RESET:
|
||||
force_reset_domain:
|
||||
/* Transit to the root of specified namespace. */
|
||||
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
|
||||
candidate->name);
|
||||
/*
|
||||
* Make do_execve() fail if domain transition across namespaces
|
||||
* has failed.
|
||||
*/
|
||||
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, candidate->name);
|
||||
break;
|
||||
case TOMOYO_TRANSITION_CONTROL_KEEP:
|
||||
force_keep_domain:
|
||||
/* Keep current domain. */
|
||||
domain = old_domain;
|
||||
break;
|
||||
default:
|
||||
if (old_domain == &tomoyo_kernel_domain &&
|
||||
!tomoyo_policy_loaded) {
|
||||
/*
|
||||
* Needn't to transit from kernel domain before
|
||||
* starting /sbin/init. But transit from kernel domain
|
||||
* if executing initializers because they might start
|
||||
* before /sbin/init.
|
||||
*/
|
||||
domain = old_domain;
|
||||
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)
|
||||
retval = 0;
|
||||
else if (reject_on_transition_failure) {
|
||||
printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
|
||||
ee->tmp);
|
||||
retval = -ENOMEM;
|
||||
} else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
|
||||
retval = -ENOMEM;
|
||||
else {
|
||||
retval = 0;
|
||||
if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) {
|
||||
old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true;
|
||||
ee->r.granted = false;
|
||||
tomoyo_write_log(&ee->r, "%s", tomoyo_dif
|
||||
[TOMOYO_DIF_TRANSITION_FAILED]);
|
||||
printk(KERN_WARNING
|
||||
"ERROR: Domain '%s' not defined.\n", ee->tmp);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (!domain)
|
||||
domain = old_domain;
|
||||
/* Update reference count on "struct tomoyo_domain_info". */
|
||||
atomic_inc(&domain->users);
|
||||
bprm->cred->security = domain;
|
||||
kfree(exename.name);
|
||||
if (!retval) {
|
||||
ee->r.domain = domain;
|
||||
retval = tomoyo_environ(ee);
|
||||
}
|
||||
kfree(ee->tmp);
|
||||
kfree(ee->dump.data);
|
||||
kfree(ee);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_dump_page - Dump a page to buffer.
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
* @pos: Location to dump.
|
||||
* @dump: Poiner to "struct tomoyo_page_dump".
|
||||
*
|
||||
* Returns true on success, false otherwise.
|
||||
*/
|
||||
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_find_next_domain(). */
|
||||
if (!dump->data) {
|
||||
dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
|
||||
if (!dump->data)
|
||||
return false;
|
||||
}
|
||||
/* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
|
||||
#ifdef CONFIG_MMU
|
||||
if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
|
||||
return false;
|
||||
#else
|
||||
page = bprm->page[pos / PAGE_SIZE];
|
||||
#endif
|
||||
if (page != dump->page) {
|
||||
const unsigned int offset = pos % PAGE_SIZE;
|
||||
/*
|
||||
* Maybe kmap()/kunmap() should be used here.
|
||||
* But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
|
||||
* So do I.
|
||||
*/
|
||||
char *kaddr = kmap_atomic(page);
|
||||
|
||||
dump->page = page;
|
||||
memcpy(dump->data + offset, kaddr + offset,
|
||||
PAGE_SIZE - offset);
|
||||
kunmap_atomic(kaddr);
|
||||
}
|
||||
/* Same with put_arg_page(page) in fs/exec.c */
|
||||
#ifdef CONFIG_MMU
|
||||
put_page(page);
|
||||
#endif
|
||||
return true;
|
||||
}
|
122
security/tomoyo/environ.c
Normal file
122
security/tomoyo/environ.c
Normal 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(¶m->data, "env "))
|
||||
return tomoyo_write_env(param);
|
||||
return -EINVAL;
|
||||
}
|
1026
security/tomoyo/file.c
Normal file
1026
security/tomoyo/file.c
Normal file
File diff suppressed because it is too large
Load diff
655
security/tomoyo/gc.c
Normal file
655
security/tomoyo/gc.c
Normal file
|
@ -0,0 +1,655 @@
|
|||
/*
|
||||
* security/tomoyo/gc.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#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);
|
||||
|
||||
/**
|
||||
* tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not.
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns true if @element is used by /sys/kernel/security/tomoyo/ users,
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element)
|
||||
{
|
||||
struct tomoyo_io_buffer *head;
|
||||
bool in_use = false;
|
||||
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
|
||||
head->users++;
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
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);
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
head->users--;
|
||||
if (in_use)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
return in_use;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not.
|
||||
*
|
||||
* @string: String to check.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
struct tomoyo_io_buffer *head;
|
||||
const size_t size = strlen(string) + 1;
|
||||
bool in_use = false;
|
||||
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
list_for_each_entry(head, &tomoyo_io_buffer_list, list) {
|
||||
int i;
|
||||
head->users++;
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
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)
|
||||
continue;
|
||||
in_use = true;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&head->io_sem);
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
head->users--;
|
||||
if (in_use)
|
||||
break;
|
||||
}
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
return in_use;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static inline void tomoyo_del_transition_control(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_transition_control *ptr =
|
||||
container_of(element, typeof(*ptr), head.list);
|
||||
tomoyo_put_name(ptr->domainname);
|
||||
tomoyo_put_name(ptr->program);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_aggregator - Delete members in "struct tomoyo_aggregator".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static inline void tomoyo_del_aggregator(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_aggregator *ptr =
|
||||
container_of(element, typeof(*ptr), head.list);
|
||||
tomoyo_put_name(ptr->original_name);
|
||||
tomoyo_put_name(ptr->aggregated_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_manager - Delete members in "struct tomoyo_manager".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static inline void tomoyo_del_manager(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_manager *ptr =
|
||||
container_of(element, typeof(*ptr), head.list);
|
||||
tomoyo_put_name(ptr->manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_acl - Delete members in "struct tomoyo_acl_info".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void tomoyo_del_acl(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_acl_info *acl =
|
||||
container_of(element, typeof(*acl), list);
|
||||
tomoyo_put_condition(acl->cond);
|
||||
switch (acl->type) {
|
||||
case TOMOYO_TYPE_PATH_ACL:
|
||||
{
|
||||
struct tomoyo_path_acl *entry
|
||||
= container_of(acl, typeof(*entry), head);
|
||||
tomoyo_put_name_union(&entry->name);
|
||||
}
|
||||
break;
|
||||
case TOMOYO_TYPE_PATH2_ACL:
|
||||
{
|
||||
struct tomoyo_path2_acl *entry
|
||||
= container_of(acl, typeof(*entry), head);
|
||||
tomoyo_put_name_union(&entry->name1);
|
||||
tomoyo_put_name_union(&entry->name2);
|
||||
}
|
||||
break;
|
||||
case TOMOYO_TYPE_PATH_NUMBER_ACL:
|
||||
{
|
||||
struct tomoyo_path_number_acl *entry
|
||||
= container_of(acl, typeof(*entry), head);
|
||||
tomoyo_put_name_union(&entry->name);
|
||||
tomoyo_put_number_union(&entry->number);
|
||||
}
|
||||
break;
|
||||
case TOMOYO_TYPE_MKDEV_ACL:
|
||||
{
|
||||
struct tomoyo_mkdev_acl *entry
|
||||
= container_of(acl, typeof(*entry), head);
|
||||
tomoyo_put_name_union(&entry->name);
|
||||
tomoyo_put_number_union(&entry->mode);
|
||||
tomoyo_put_number_union(&entry->major);
|
||||
tomoyo_put_number_union(&entry->minor);
|
||||
}
|
||||
break;
|
||||
case TOMOYO_TYPE_MOUNT_ACL:
|
||||
{
|
||||
struct tomoyo_mount_acl *entry
|
||||
= container_of(acl, typeof(*entry), head);
|
||||
tomoyo_put_name_union(&entry->dev_name);
|
||||
tomoyo_put_name_union(&entry->dir_name);
|
||||
tomoyo_put_name_union(&entry->fs_type);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_domain - Delete members in "struct tomoyo_domain_info".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*
|
||||
* Caller holds tomoyo_policy_lock mutex.
|
||||
*/
|
||||
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 this domain is referenced from neither
|
||||
* "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete
|
||||
* elements without checking for is_deleted flag.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_condition - Delete members in "struct tomoyo_condition".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void tomoyo_del_condition(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_condition *cond = container_of(element, typeof(*cond),
|
||||
head.list);
|
||||
const u16 condc = cond->condc;
|
||||
const u16 numbers_count = cond->numbers_count;
|
||||
const u16 names_count = cond->names_count;
|
||||
const u16 argc = cond->argc;
|
||||
const u16 envc = cond->envc;
|
||||
unsigned int i;
|
||||
const struct tomoyo_condition_element *condp
|
||||
= (const struct tomoyo_condition_element *) (cond + 1);
|
||||
struct tomoyo_number_union *numbers_p
|
||||
= (struct tomoyo_number_union *) (condp + condc);
|
||||
struct tomoyo_name_union *names_p
|
||||
= (struct tomoyo_name_union *) (numbers_p + numbers_count);
|
||||
const struct tomoyo_argv *argv
|
||||
= (const struct tomoyo_argv *) (names_p + names_count);
|
||||
const struct tomoyo_envp *envp
|
||||
= (const struct tomoyo_envp *) (argv + argc);
|
||||
for (i = 0; i < numbers_count; i++)
|
||||
tomoyo_put_number_union(numbers_p++);
|
||||
for (i = 0; i < names_count; i++)
|
||||
tomoyo_put_name_union(names_p++);
|
||||
for (i = 0; i < argc; argv++, i++)
|
||||
tomoyo_put_name(argv->value);
|
||||
for (i = 0; i < envc; envp++, i++) {
|
||||
tomoyo_put_name(envp->name);
|
||||
tomoyo_put_name(envp->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_name - Delete members in "struct tomoyo_name".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static inline void tomoyo_del_name(struct list_head *element)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_path_group - Delete members in "struct tomoyo_path_group".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static inline void tomoyo_del_path_group(struct list_head *element)
|
||||
{
|
||||
struct tomoyo_path_group *member =
|
||||
container_of(element, typeof(*member), head.list);
|
||||
tomoyo_put_name(member->member_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_del_group - Delete "struct tomoyo_group".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
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".
|
||||
*
|
||||
* @element: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static inline void tomoyo_del_number_group(struct list_head *element)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_collect_member - Delete elements with "struct tomoyo_acl_head".
|
||||
*
|
||||
* @id: One of values in "enum tomoyo_policy_id".
|
||||
* @member_list: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void tomoyo_collect_member(const enum tomoyo_policy_id id,
|
||||
struct list_head *member_list)
|
||||
{
|
||||
struct tomoyo_acl_head *member;
|
||||
struct tomoyo_acl_head *tmp;
|
||||
list_for_each_entry_safe(member, tmp, member_list, list) {
|
||||
if (!member->is_deleted)
|
||||
continue;
|
||||
member->is_deleted = TOMOYO_GC_IN_PROGRESS;
|
||||
tomoyo_try_to_gc(id, &member->list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_collect_acl - Delete elements in "struct tomoyo_domain_info".
|
||||
*
|
||||
* @list: Pointer to "struct list_head".
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void tomoyo_collect_acl(struct list_head *list)
|
||||
{
|
||||
struct tomoyo_acl_info *acl;
|
||||
struct tomoyo_acl_info *tmp;
|
||||
list_for_each_entry_safe(acl, tmp, list, list) {
|
||||
if (!acl->is_deleted)
|
||||
continue;
|
||||
acl->is_deleted = TOMOYO_GC_IN_PROGRESS;
|
||||
tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_collect_entry - Try to kfree() deleted elements.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void tomoyo_collect_entry(void)
|
||||
{
|
||||
int i;
|
||||
enum tomoyo_policy_id id;
|
||||
struct tomoyo_policy_namespace *ns;
|
||||
mutex_lock(&tomoyo_policy_lock);
|
||||
{
|
||||
struct tomoyo_domain_info *domain;
|
||||
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;
|
||||
tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list);
|
||||
}
|
||||
}
|
||||
list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) {
|
||||
for (id = 0; id < TOMOYO_MAX_POLICY; id++)
|
||||
tomoyo_collect_member(id, &ns->policy_list[id]);
|
||||
for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++)
|
||||
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;
|
||||
case 1:
|
||||
id = TOMOYO_ID_NUMBER_GROUP;
|
||||
break;
|
||||
default:
|
||||
id = TOMOYO_ID_ADDRESS_GROUP;
|
||||
break;
|
||||
}
|
||||
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) > 0)
|
||||
continue;
|
||||
atomic_set(&group->head.users,
|
||||
TOMOYO_GC_IN_PROGRESS);
|
||||
tomoyo_try_to_gc(TOMOYO_ID_GROUP,
|
||||
&group->head.list);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < TOMOYO_MAX_HASH; i++) {
|
||||
struct list_head *list = &tomoyo_name_list[i];
|
||||
struct tomoyo_shared_acl_head *ptr;
|
||||
struct tomoyo_shared_acl_head *tmp;
|
||||
list_for_each_entry_safe(ptr, tmp, list, list) {
|
||||
if (atomic_read(&ptr->users) > 0)
|
||||
continue;
|
||||
atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS);
|
||||
tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_gc_thread - Garbage collector thread function.
|
||||
*
|
||||
* @unused: Unused.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int tomoyo_gc_thread(void *unused)
|
||||
{
|
||||
/* Garbage collector thread is exclusive. */
|
||||
static DEFINE_MUTEX(tomoyo_gc_mutex);
|
||||
if (!mutex_trylock(&tomoyo_gc_mutex))
|
||||
goto out;
|
||||
tomoyo_collect_entry();
|
||||
{
|
||||
struct tomoyo_io_buffer *head;
|
||||
struct tomoyo_io_buffer *tmp;
|
||||
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
list_for_each_entry_safe(head, tmp, &tomoyo_io_buffer_list,
|
||||
list) {
|
||||
if (head->users)
|
||||
continue;
|
||||
list_del(&head->list);
|
||||
kfree(head->read_buf);
|
||||
kfree(head->write_buf);
|
||||
kfree(head);
|
||||
}
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
}
|
||||
mutex_unlock(&tomoyo_gc_mutex);
|
||||
out:
|
||||
/* This acts as do_exit(0). */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_notify_gc - Register/unregister /sys/kernel/security/tomoyo/ users.
|
||||
*
|
||||
* @head: Pointer to "struct tomoyo_io_buffer".
|
||||
* @is_register: True if register, false if unregister.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register)
|
||||
{
|
||||
bool is_write = false;
|
||||
|
||||
spin_lock(&tomoyo_io_buffer_list_lock);
|
||||
if (is_register) {
|
||||
head->users = 1;
|
||||
list_add(&head->list, &tomoyo_io_buffer_list);
|
||||
} else {
|
||||
is_write = head->write_buf != NULL;
|
||||
if (!--head->users) {
|
||||
list_del(&head->list);
|
||||
kfree(head->read_buf);
|
||||
kfree(head->write_buf);
|
||||
kfree(head);
|
||||
}
|
||||
}
|
||||
spin_unlock(&tomoyo_io_buffer_list_lock);
|
||||
if (is_write) {
|
||||
struct task_struct *task = kthread_create(tomoyo_gc_thread,
|
||||
NULL,
|
||||
"GC for TOMOYO");
|
||||
if (!IS_ERR(task))
|
||||
wake_up_process(task);
|
||||
}
|
||||
}
|
198
security/tomoyo/group.c
Normal file
198
security/tomoyo/group.c
Normal file
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* security/tomoyo/group.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* tomoyo_same_path_group - Check for duplicated "struct tomoyo_path_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_path_group(const struct tomoyo_acl_head *a,
|
||||
const struct tomoyo_acl_head *b)
|
||||
{
|
||||
return container_of(a, struct tomoyo_path_group, head)->member_name ==
|
||||
container_of(b, struct tomoyo_path_group, head)->member_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_same_number_group - Check for duplicated "struct tomoyo_number_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_number_group(const struct tomoyo_acl_head *a,
|
||||
const struct tomoyo_acl_head *b)
|
||||
{
|
||||
return !memcmp(&container_of(a, struct tomoyo_number_group, head)
|
||||
->number,
|
||||
&container_of(b, struct tomoyo_number_group, head)
|
||||
->number,
|
||||
sizeof(container_of(a, struct tomoyo_number_group, head)
|
||||
->number));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type)
|
||||
{
|
||||
struct tomoyo_group *group = tomoyo_get_group(param, type);
|
||||
int error = -EINVAL;
|
||||
if (!group)
|
||||
return -ENOMEM;
|
||||
param->list = &group->member_list;
|
||||
if (type == TOMOYO_PATH_GROUP) {
|
||||
struct tomoyo_path_group e = { };
|
||||
e.member_name = tomoyo_get_name(tomoyo_read_token(param));
|
||||
if (!e.member_name) {
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
error = tomoyo_update_policy(&e.head, sizeof(e), param,
|
||||
tomoyo_same_path_group);
|
||||
tomoyo_put_name(e.member_name);
|
||||
} else if (type == TOMOYO_NUMBER_GROUP) {
|
||||
struct tomoyo_number_group e = { };
|
||||
if (param->data[0] == '@' ||
|
||||
!tomoyo_parse_number_union(param, &e.number))
|
||||
goto out;
|
||||
error = tomoyo_update_policy(&e.head, sizeof(e), param,
|
||||
tomoyo_same_number_group);
|
||||
/*
|
||||
* 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);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group.
|
||||
*
|
||||
* @pathname: The name of pathname.
|
||||
* @group: Pointer to "struct tomoyo_path_group".
|
||||
*
|
||||
* Returns matched member's pathname if @pathname matches pathnames in @group,
|
||||
* NULL otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
const struct tomoyo_path_info *
|
||||
tomoyo_path_matches_group(const struct tomoyo_path_info *pathname,
|
||||
const struct tomoyo_group *group)
|
||||
{
|
||||
struct tomoyo_path_group *member;
|
||||
list_for_each_entry_rcu(member, &group->member_list, head.list) {
|
||||
if (member->head.is_deleted)
|
||||
continue;
|
||||
if (!tomoyo_path_matches_pattern(pathname, member->member_name))
|
||||
continue;
|
||||
return member->member_name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_number_matches_group - Check whether the given number matches members of the given number group.
|
||||
*
|
||||
* @min: Min number.
|
||||
* @max: Max number.
|
||||
* @group: Pointer to "struct tomoyo_number_group".
|
||||
*
|
||||
* Returns true if @min and @max partially overlaps @group, false otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
bool tomoyo_number_matches_group(const unsigned long min,
|
||||
const unsigned long max,
|
||||
const struct tomoyo_group *group)
|
||||
{
|
||||
struct tomoyo_number_group *member;
|
||||
bool matched = false;
|
||||
list_for_each_entry_rcu(member, &group->member_list, head.list) {
|
||||
if (member->head.is_deleted)
|
||||
continue;
|
||||
if (min > member->number.values[1] ||
|
||||
max < member->number.values[0])
|
||||
continue;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
109
security/tomoyo/load_policy.c
Normal file
109
security/tomoyo/load_policy.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* security/tomoyo/load_policy.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
|
||||
/*
|
||||
* Path to the policy loader. (default = CONFIG_SECURITY_TOMOYO_POLICY_LOADER)
|
||||
*/
|
||||
static const char *tomoyo_loader;
|
||||
|
||||
/**
|
||||
* tomoyo_loader_setup - Set policy loader.
|
||||
*
|
||||
* @str: Program to use as a policy loader (e.g. /sbin/tomoyo-init ).
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int __init tomoyo_loader_setup(char *str)
|
||||
{
|
||||
tomoyo_loader = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("TOMOYO_loader=", tomoyo_loader_setup);
|
||||
|
||||
/**
|
||||
* tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists.
|
||||
*
|
||||
* Returns true if /sbin/tomoyo-init exists, false otherwise.
|
||||
*/
|
||||
static bool tomoyo_policy_loader_exists(void)
|
||||
{
|
||||
struct path path;
|
||||
if (!tomoyo_loader)
|
||||
tomoyo_loader = CONFIG_SECURITY_TOMOYO_POLICY_LOADER;
|
||||
if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) {
|
||||
printk(KERN_INFO "Not activating Mandatory Access Control "
|
||||
"as %s does not exist.\n", tomoyo_loader);
|
||||
return false;
|
||||
}
|
||||
path_put(&path);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Path to the trigger. (default = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER)
|
||||
*/
|
||||
static const char *tomoyo_trigger;
|
||||
|
||||
/**
|
||||
* tomoyo_trigger_setup - Set trigger for activation.
|
||||
*
|
||||
* @str: Program to use as an activation trigger (e.g. /sbin/init ).
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int __init tomoyo_trigger_setup(char *str)
|
||||
{
|
||||
tomoyo_trigger = str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__setup("TOMOYO_trigger=", tomoyo_trigger_setup);
|
||||
|
||||
/**
|
||||
* tomoyo_load_policy - Run external policy loader to load policy.
|
||||
*
|
||||
* @filename: The program about to start.
|
||||
*
|
||||
* This function checks whether @filename is /sbin/init , and if so
|
||||
* invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init
|
||||
* and then continues invocation of /sbin/init.
|
||||
* /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and
|
||||
* writes to /sys/kernel/security/tomoyo/ interfaces.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
void tomoyo_load_policy(const char *filename)
|
||||
{
|
||||
static bool done;
|
||||
char *argv[2];
|
||||
char *envp[3];
|
||||
|
||||
if (tomoyo_policy_loaded || done)
|
||||
return;
|
||||
if (!tomoyo_trigger)
|
||||
tomoyo_trigger = CONFIG_SECURITY_TOMOYO_ACTIVATION_TRIGGER;
|
||||
if (strcmp(filename, tomoyo_trigger))
|
||||
return;
|
||||
if (!tomoyo_policy_loader_exists())
|
||||
return;
|
||||
done = true;
|
||||
printk(KERN_INFO "Calling %s to load policy. Please wait.\n",
|
||||
tomoyo_loader);
|
||||
argv[0] = (char *) tomoyo_loader;
|
||||
argv[1] = NULL;
|
||||
envp[0] = "HOME=/";
|
||||
envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
|
||||
envp[2] = NULL;
|
||||
call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
|
||||
tomoyo_check_profile();
|
||||
}
|
||||
|
||||
#endif
|
201
security/tomoyo/memory.c
Normal file
201
security/tomoyo/memory.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* security/tomoyo/memory.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include <linux/hash.h>
|
||||
#include <linux/slab.h>
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* tomoyo_warn_oom - Print out of memory warning message.
|
||||
*
|
||||
* @function: Function's name.
|
||||
*/
|
||||
void tomoyo_warn_oom(const char *function)
|
||||
{
|
||||
/* Reduce error messages. */
|
||||
static pid_t tomoyo_last_pid;
|
||||
const pid_t pid = current->pid;
|
||||
if (tomoyo_last_pid != pid) {
|
||||
printk(KERN_WARNING "ERROR: Out of memory at %s.\n",
|
||||
function);
|
||||
tomoyo_last_pid = pid;
|
||||
}
|
||||
if (!tomoyo_policy_loaded)
|
||||
panic("MAC Initialization failed.\n");
|
||||
}
|
||||
|
||||
/* Memoy currently used by policy/audit log/query. */
|
||||
unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT];
|
||||
/* Memory quota for "policy"/"audit log"/"query". */
|
||||
unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT];
|
||||
|
||||
/**
|
||||
* tomoyo_memory_ok - Check memory quota.
|
||||
*
|
||||
* @ptr: Pointer to allocated memory.
|
||||
*
|
||||
* 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);
|
||||
tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_commit_ok - Check memory quota.
|
||||
*
|
||||
* @data: Data to copy from.
|
||||
* @size: Size in byte.
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
void *ptr = kzalloc(size, GFP_NOFS);
|
||||
if (tomoyo_memory_ok(ptr)) {
|
||||
memmove(ptr, data, size);
|
||||
memset(data, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
kfree(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group".
|
||||
*
|
||||
* @param: Pointer to "struct tomoyo_acl_param".
|
||||
* @idx: Index number.
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_group" on success, NULL otherwise.
|
||||
*/
|
||||
struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param,
|
||||
const u8 idx)
|
||||
{
|
||||
struct tomoyo_group e = { };
|
||||
struct tomoyo_group *group = NULL;
|
||||
struct list_head *list;
|
||||
const char *group_name = tomoyo_read_token(param);
|
||||
bool found = false;
|
||||
if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP)
|
||||
return NULL;
|
||||
e.group_name = tomoyo_get_name(group_name);
|
||||
if (!e.group_name)
|
||||
return NULL;
|
||||
if (mutex_lock_interruptible(&tomoyo_policy_lock))
|
||||
goto out;
|
||||
list = ¶m->ns->group_list[idx];
|
||||
list_for_each_entry(group, list, head.list) {
|
||||
if (e.group_name != group->group_name ||
|
||||
atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS)
|
||||
continue;
|
||||
atomic_inc(&group->head.users);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found) {
|
||||
struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e));
|
||||
if (entry) {
|
||||
INIT_LIST_HEAD(&entry->member_list);
|
||||
atomic_set(&entry->head.users, 1);
|
||||
list_add_tail_rcu(&entry->head.list, list);
|
||||
group = entry;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
out:
|
||||
tomoyo_put_name(e.group_name);
|
||||
return found ? group : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* tomoyo_name_list is used for holding string data used by TOMOYO.
|
||||
* Since same string data is likely used for multiple times (e.g.
|
||||
* "/lib/libc-2.5.so"), TOMOYO shares string data in the form of
|
||||
* "const struct tomoyo_path_info *".
|
||||
*/
|
||||
struct list_head tomoyo_name_list[TOMOYO_MAX_HASH];
|
||||
|
||||
/**
|
||||
* tomoyo_get_name - Allocate permanent memory for string data.
|
||||
*
|
||||
* @name: The string to store into the permernent memory.
|
||||
*
|
||||
* Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
|
||||
*/
|
||||
const struct tomoyo_path_info *tomoyo_get_name(const char *name)
|
||||
{
|
||||
struct tomoyo_name *ptr;
|
||||
unsigned int hash;
|
||||
int len;
|
||||
struct list_head *head;
|
||||
|
||||
if (!name)
|
||||
return NULL;
|
||||
len = strlen(name) + 1;
|
||||
hash = full_name_hash((const unsigned char *) name, len - 1);
|
||||
head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)];
|
||||
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) ||
|
||||
atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS)
|
||||
continue;
|
||||
atomic_inc(&ptr->head.users);
|
||||
goto out;
|
||||
}
|
||||
ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS);
|
||||
if (tomoyo_memory_ok(ptr)) {
|
||||
ptr->entry.name = ((char *) ptr) + sizeof(*ptr);
|
||||
memmove((char *) ptr->entry.name, name, len);
|
||||
atomic_set(&ptr->head.users, 1);
|
||||
tomoyo_fill_path_info(&ptr->entry);
|
||||
list_add_tail(&ptr->head.list, head);
|
||||
} else {
|
||||
kfree(ptr);
|
||||
ptr = NULL;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&tomoyo_policy_lock);
|
||||
return ptr ? &ptr->entry : NULL;
|
||||
}
|
||||
|
||||
/* Initial namespace.*/
|
||||
struct tomoyo_policy_namespace tomoyo_kernel_namespace;
|
||||
|
||||
/**
|
||||
* tomoyo_mm_init - Initialize mm related code.
|
||||
*/
|
||||
void __init tomoyo_mm_init(void)
|
||||
{
|
||||
int idx;
|
||||
for (idx = 0; idx < TOMOYO_MAX_HASH; idx++)
|
||||
INIT_LIST_HEAD(&tomoyo_name_list[idx]);
|
||||
tomoyo_kernel_namespace.name = "<kernel>";
|
||||
tomoyo_init_policy_namespace(&tomoyo_kernel_namespace);
|
||||
tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace;
|
||||
INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list);
|
||||
tomoyo_kernel_domain.domainname = tomoyo_get_name("<kernel>");
|
||||
list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list);
|
||||
}
|
236
security/tomoyo/mount.c
Normal file
236
security/tomoyo/mount.c
Normal file
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* security/tomoyo/mount.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "common.h"
|
||||
|
||||
/* String table for special mount operations. */
|
||||
static const char * const tomoyo_mounts[TOMOYO_MAX_SPECIAL_MOUNT] = {
|
||||
[TOMOYO_MOUNT_BIND] = "--bind",
|
||||
[TOMOYO_MOUNT_MOVE] = "--move",
|
||||
[TOMOYO_MOUNT_REMOUNT] = "--remount",
|
||||
[TOMOYO_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable",
|
||||
[TOMOYO_MOUNT_MAKE_PRIVATE] = "--make-private",
|
||||
[TOMOYO_MOUNT_MAKE_SLAVE] = "--make-slave",
|
||||
[TOMOYO_MOUNT_MAKE_SHARED] = "--make-shared",
|
||||
};
|
||||
|
||||
/**
|
||||
* tomoyo_audit_mount_log - Audit mount log.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_audit_mount_log(struct tomoyo_request_info *r)
|
||||
{
|
||||
return tomoyo_supervisor(r, "file mount %s %s %s 0x%lX\n",
|
||||
r->param.mount.dev->name,
|
||||
r->param.mount.dir->name,
|
||||
r->param.mount.type->name,
|
||||
r->param.mount.flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_check_mount_acl - Check permission for path path path number 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_mount_acl(struct tomoyo_request_info *r,
|
||||
const struct tomoyo_acl_info *ptr)
|
||||
{
|
||||
const struct tomoyo_mount_acl *acl =
|
||||
container_of(ptr, typeof(*acl), head);
|
||||
return tomoyo_compare_number_union(r->param.mount.flags,
|
||||
&acl->flags) &&
|
||||
tomoyo_compare_name_union(r->param.mount.type,
|
||||
&acl->fs_type) &&
|
||||
tomoyo_compare_name_union(r->param.mount.dir,
|
||||
&acl->dir_name) &&
|
||||
(!r->param.mount.need_dev ||
|
||||
tomoyo_compare_name_union(r->param.mount.dev,
|
||||
&acl->dev_name));
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_mount_acl - Check permission for mount() operation.
|
||||
*
|
||||
* @r: Pointer to "struct tomoyo_request_info".
|
||||
* @dev_name: Name of device file. Maybe NULL.
|
||||
* @dir: Pointer to "struct path".
|
||||
* @type: Name of filesystem type.
|
||||
* @flags: Mount options.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*
|
||||
* Caller holds tomoyo_read_lock().
|
||||
*/
|
||||
static int tomoyo_mount_acl(struct tomoyo_request_info *r,
|
||||
const char *dev_name,
|
||||
struct path *dir, const char *type,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct tomoyo_obj_info obj = { };
|
||||
struct path path;
|
||||
struct file_system_type *fstype = NULL;
|
||||
const char *requested_type = NULL;
|
||||
const char *requested_dir_name = NULL;
|
||||
const char *requested_dev_name = NULL;
|
||||
struct tomoyo_path_info rtype;
|
||||
struct tomoyo_path_info rdev;
|
||||
struct tomoyo_path_info rdir;
|
||||
int need_dev = 0;
|
||||
int error = -ENOMEM;
|
||||
r->obj = &obj;
|
||||
|
||||
/* Get fstype. */
|
||||
requested_type = tomoyo_encode(type);
|
||||
if (!requested_type)
|
||||
goto out;
|
||||
rtype.name = requested_type;
|
||||
tomoyo_fill_path_info(&rtype);
|
||||
|
||||
/* Get mount point. */
|
||||
obj.path2 = *dir;
|
||||
requested_dir_name = tomoyo_realpath_from_path(dir);
|
||||
if (!requested_dir_name) {
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
rdir.name = requested_dir_name;
|
||||
tomoyo_fill_path_info(&rdir);
|
||||
|
||||
/* Compare fs name. */
|
||||
if (type == tomoyo_mounts[TOMOYO_MOUNT_REMOUNT]) {
|
||||
/* dev_name is ignored. */
|
||||
} else if (type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE] ||
|
||||
type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE] ||
|
||||
type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE] ||
|
||||
type == tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED]) {
|
||||
/* dev_name is ignored. */
|
||||
} else if (type == tomoyo_mounts[TOMOYO_MOUNT_BIND] ||
|
||||
type == tomoyo_mounts[TOMOYO_MOUNT_MOVE]) {
|
||||
need_dev = -1; /* dev_name is a directory */
|
||||
} else {
|
||||
fstype = get_fs_type(type);
|
||||
if (!fstype) {
|
||||
error = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
if (fstype->fs_flags & FS_REQUIRES_DEV)
|
||||
/* dev_name is a block device file. */
|
||||
need_dev = 1;
|
||||
}
|
||||
if (need_dev) {
|
||||
/* Get mount point or device file. */
|
||||
if (!dev_name || kern_path(dev_name, LOOKUP_FOLLOW, &path)) {
|
||||
error = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
obj.path1 = path;
|
||||
requested_dev_name = tomoyo_realpath_from_path(&path);
|
||||
if (!requested_dev_name) {
|
||||
error = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* Map dev_name to "<NULL>" if no dev_name given. */
|
||||
if (!dev_name)
|
||||
dev_name = "<NULL>";
|
||||
requested_dev_name = tomoyo_encode(dev_name);
|
||||
if (!requested_dev_name) {
|
||||
error = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rdev.name = requested_dev_name;
|
||||
tomoyo_fill_path_info(&rdev);
|
||||
r->param_type = TOMOYO_TYPE_MOUNT_ACL;
|
||||
r->param.mount.need_dev = need_dev;
|
||||
r->param.mount.dev = &rdev;
|
||||
r->param.mount.dir = &rdir;
|
||||
r->param.mount.type = &rtype;
|
||||
r->param.mount.flags = flags;
|
||||
do {
|
||||
tomoyo_check_acl(r, tomoyo_check_mount_acl);
|
||||
error = tomoyo_audit_mount_log(r);
|
||||
} while (error == TOMOYO_RETRY_REQUEST);
|
||||
out:
|
||||
kfree(requested_dev_name);
|
||||
kfree(requested_dir_name);
|
||||
if (fstype)
|
||||
put_filesystem(fstype);
|
||||
kfree(requested_type);
|
||||
/* Drop refcount obtained by kern_path(). */
|
||||
if (obj.path1.dentry)
|
||||
path_put(&obj.path1);
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_mount_permission - Check permission for mount() operation.
|
||||
*
|
||||
* @dev_name: Name of device file. Maybe NULL.
|
||||
* @path: Pointer to "struct path".
|
||||
* @type: Name of filesystem type. Maybe NULL.
|
||||
* @flags: Mount options.
|
||||
* @data_page: Optional data. Maybe NULL.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
int tomoyo_mount_permission(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags,
|
||||
void *data_page)
|
||||
{
|
||||
struct tomoyo_request_info r;
|
||||
int error;
|
||||
int idx;
|
||||
|
||||
if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT)
|
||||
== TOMOYO_CONFIG_DISABLED)
|
||||
return 0;
|
||||
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
|
||||
flags &= ~MS_MGC_MSK;
|
||||
if (flags & MS_REMOUNT) {
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_REMOUNT];
|
||||
flags &= ~MS_REMOUNT;
|
||||
} else if (flags & MS_BIND) {
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_BIND];
|
||||
flags &= ~MS_BIND;
|
||||
} else if (flags & MS_SHARED) {
|
||||
if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
|
||||
return -EINVAL;
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SHARED];
|
||||
flags &= ~MS_SHARED;
|
||||
} else if (flags & MS_PRIVATE) {
|
||||
if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE))
|
||||
return -EINVAL;
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_PRIVATE];
|
||||
flags &= ~MS_PRIVATE;
|
||||
} else if (flags & MS_SLAVE) {
|
||||
if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE))
|
||||
return -EINVAL;
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_SLAVE];
|
||||
flags &= ~MS_SLAVE;
|
||||
} else if (flags & MS_UNBINDABLE) {
|
||||
if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE))
|
||||
return -EINVAL;
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_MAKE_UNBINDABLE];
|
||||
flags &= ~MS_UNBINDABLE;
|
||||
} else if (flags & MS_MOVE) {
|
||||
type = tomoyo_mounts[TOMOYO_MOUNT_MOVE];
|
||||
flags &= ~MS_MOVE;
|
||||
}
|
||||
if (!type)
|
||||
type = "<NULL>";
|
||||
idx = tomoyo_read_lock();
|
||||
error = tomoyo_mount_acl(&r, dev_name, path, type, flags);
|
||||
tomoyo_read_unlock(idx);
|
||||
return error;
|
||||
}
|
771
security/tomoyo/network.c
Normal file
771
security/tomoyo/network.c
Normal 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);
|
||||
}
|
329
security/tomoyo/realpath.c
Normal file
329
security/tomoyo/realpath.c
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
* security/tomoyo/realpath.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <linux/magic.h>
|
||||
|
||||
/**
|
||||
* tomoyo_encode2 - Encode binary string to ascii string.
|
||||
*
|
||||
* @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_encode2(const char *str, int str_len)
|
||||
{
|
||||
int i;
|
||||
int len = 0;
|
||||
const char *p = str;
|
||||
char *cp;
|
||||
char *cp0;
|
||||
|
||||
if (!p)
|
||||
return NULL;
|
||||
for (i = 0; i < str_len; i++) {
|
||||
const unsigned char c = p[i];
|
||||
|
||||
if (c == '\\')
|
||||
len += 2;
|
||||
else if (c > ' ' && c < 127)
|
||||
len++;
|
||||
else
|
||||
len += 4;
|
||||
}
|
||||
len++;
|
||||
/* Reserve space for appending "/". */
|
||||
cp = kzalloc(len + 10, GFP_NOFS);
|
||||
if (!cp)
|
||||
return NULL;
|
||||
cp0 = cp;
|
||||
p = str;
|
||||
for (i = 0; i < str_len; i++) {
|
||||
const unsigned char c = p[i];
|
||||
|
||||
if (c == '\\') {
|
||||
*cp++ = '\\';
|
||||
*cp++ = '\\';
|
||||
} else if (c > ' ' && c < 127) {
|
||||
*cp++ = c;
|
||||
} else {
|
||||
*cp++ = '\\';
|
||||
*cp++ = (c >> 6) + '0';
|
||||
*cp++ = ((c >> 3) & 7) + '0';
|
||||
*cp++ = (c & 7) + '0';
|
||||
}
|
||||
}
|
||||
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.
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
* @buffer: Pointer to buffer to return value in.
|
||||
* @buflen: Sizeof @buffer.
|
||||
*
|
||||
* Returns the buffer on success, an error code otherwise.
|
||||
*
|
||||
* If dentry is a directory, trailing '/' is appended.
|
||||
*/
|
||||
static char *tomoyo_get_absolute_path(struct path *path, char * const buffer,
|
||||
const int buflen)
|
||||
{
|
||||
char *pos = ERR_PTR(-ENOMEM);
|
||||
if (buflen >= 256) {
|
||||
/* go to whatever namespace root we are under */
|
||||
pos = d_absolute_path(path, buffer, buflen - 1);
|
||||
if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
if (inode && S_ISDIR(inode->i_mode)) {
|
||||
buffer[buflen - 2] = '/';
|
||||
buffer[buflen - 1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_get_dentry_path - Get the path of a dentry.
|
||||
*
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
* @buffer: Pointer to buffer to return value in.
|
||||
* @buflen: Sizeof @buffer.
|
||||
*
|
||||
* Returns the buffer on success, an error code otherwise.
|
||||
*
|
||||
* If dentry is a directory, trailing '/' is appended.
|
||||
*/
|
||||
static char *tomoyo_get_dentry_path(struct dentry *dentry, char * const buffer,
|
||||
const int buflen)
|
||||
{
|
||||
char *pos = ERR_PTR(-ENOMEM);
|
||||
if (buflen >= 256) {
|
||||
pos = dentry_path_raw(dentry, buffer, buflen - 1);
|
||||
if (!IS_ERR(pos) && *pos == '/' && pos[1]) {
|
||||
struct inode *inode = dentry->d_inode;
|
||||
if (inode && S_ISDIR(inode->i_mode)) {
|
||||
buffer[buflen - 2] = '/';
|
||||
buffer[buflen - 1] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_get_local_path - Get the path of a dentry.
|
||||
*
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
* @buffer: Pointer to buffer to return value in.
|
||||
* @buflen: Sizeof @buffer.
|
||||
*
|
||||
* Returns the buffer on success, an error code otherwise.
|
||||
*/
|
||||
static char *tomoyo_get_local_path(struct dentry *dentry, char * const buffer,
|
||||
const int buflen)
|
||||
{
|
||||
struct super_block *sb = dentry->d_sb;
|
||||
char *pos = tomoyo_get_dentry_path(dentry, buffer, buflen);
|
||||
if (IS_ERR(pos))
|
||||
return pos;
|
||||
/* Convert from $PID to self if $PID is current thread. */
|
||||
if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') {
|
||||
char *ep;
|
||||
const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10);
|
||||
if (*ep == '/' && pid && pid ==
|
||||
task_tgid_nr_ns(current, sb->s_fs_info)) {
|
||||
pos = ep - 5;
|
||||
if (pos < buffer)
|
||||
goto out;
|
||||
memmove(pos, "/self", 5);
|
||||
}
|
||||
goto prepend_filesystem_name;
|
||||
}
|
||||
/* Use filesystem name for unnamed devices. */
|
||||
if (!MAJOR(sb->s_dev))
|
||||
goto prepend_filesystem_name;
|
||||
{
|
||||
struct inode *inode = sb->s_root->d_inode;
|
||||
/*
|
||||
* Use filesystem name if filesystem does not support rename()
|
||||
* operation.
|
||||
*/
|
||||
if (!inode->i_op->rename && !inode->i_op->rename2)
|
||||
goto prepend_filesystem_name;
|
||||
}
|
||||
/* Prepend device name. */
|
||||
{
|
||||
char name[64];
|
||||
int name_len;
|
||||
const dev_t dev = sb->s_dev;
|
||||
name[sizeof(name) - 1] = '\0';
|
||||
snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev),
|
||||
MINOR(dev));
|
||||
name_len = strlen(name);
|
||||
pos -= name_len;
|
||||
if (pos < buffer)
|
||||
goto out;
|
||||
memmove(pos, name, name_len);
|
||||
return pos;
|
||||
}
|
||||
/* Prepend filesystem name. */
|
||||
prepend_filesystem_name:
|
||||
{
|
||||
const char *name = sb->s_type->name;
|
||||
const int name_len = strlen(name);
|
||||
pos -= name_len + 1;
|
||||
if (pos < buffer)
|
||||
goto out;
|
||||
memmove(pos, name, name_len);
|
||||
pos[name_len] = ':';
|
||||
}
|
||||
return pos;
|
||||
out:
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_get_socket_name - Get the name of a socket.
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
* @buffer: Pointer to buffer to return value in.
|
||||
* @buflen: Sizeof @buffer.
|
||||
*
|
||||
* Returns the buffer.
|
||||
*/
|
||||
static char *tomoyo_get_socket_name(struct path *path, char * const buffer,
|
||||
const int buflen)
|
||||
{
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
struct socket *sock = inode ? SOCKET_I(inode) : NULL;
|
||||
struct sock *sk = sock ? sock->sk : NULL;
|
||||
if (sk) {
|
||||
snprintf(buffer, buflen, "socket:[family=%u:type=%u:"
|
||||
"protocol=%u]", sk->sk_family, sk->sk_type,
|
||||
sk->sk_protocol);
|
||||
} else {
|
||||
snprintf(buffer, buflen, "socket:[unknown]");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root.
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
*
|
||||
* Returns the realpath of the given @path on success, NULL otherwise.
|
||||
*
|
||||
* If dentry is a directory, trailing '/' is appended.
|
||||
* Characters out of 0x20 < c < 0x7F range are converted to
|
||||
* \ooo style octal string.
|
||||
* Character \ is converted to \\ string.
|
||||
*
|
||||
* These functions use kzalloc(), so the caller must call kfree()
|
||||
* if these functions didn't return NULL.
|
||||
*/
|
||||
char *tomoyo_realpath_from_path(struct path *path)
|
||||
{
|
||||
char *buf = NULL;
|
||||
char *name = NULL;
|
||||
unsigned int buf_len = PAGE_SIZE / 2;
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct super_block *sb;
|
||||
if (!dentry)
|
||||
return NULL;
|
||||
sb = dentry->d_sb;
|
||||
while (1) {
|
||||
char *pos;
|
||||
struct inode *inode;
|
||||
buf_len <<= 1;
|
||||
kfree(buf);
|
||||
buf = kmalloc(buf_len, GFP_NOFS);
|
||||
if (!buf)
|
||||
break;
|
||||
/* To make sure that pos is '\0' terminated. */
|
||||
buf[buf_len - 1] = '\0';
|
||||
/* Get better name for socket. */
|
||||
if (sb->s_magic == SOCKFS_MAGIC) {
|
||||
pos = tomoyo_get_socket_name(path, buf, buf_len - 1);
|
||||
goto encode;
|
||||
}
|
||||
/* For "pipe:[\$]". */
|
||||
if (dentry->d_op && dentry->d_op->d_dname) {
|
||||
pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1);
|
||||
goto encode;
|
||||
}
|
||||
inode = sb->s_root->d_inode;
|
||||
/*
|
||||
* Get local name for filesystems without rename() operation
|
||||
* or dentry without vfsmount.
|
||||
*/
|
||||
if (!path->mnt ||
|
||||
(!inode->i_op->rename && !inode->i_op->rename2))
|
||||
pos = tomoyo_get_local_path(path->dentry, buf,
|
||||
buf_len - 1);
|
||||
/* Get absolute name for the rest. */
|
||||
else {
|
||||
pos = tomoyo_get_absolute_path(path, buf, buf_len - 1);
|
||||
/*
|
||||
* Fall back to local name if absolute name is not
|
||||
* available.
|
||||
*/
|
||||
if (pos == ERR_PTR(-EINVAL))
|
||||
pos = tomoyo_get_local_path(path->dentry, buf,
|
||||
buf_len - 1);
|
||||
}
|
||||
encode:
|
||||
if (IS_ERR(pos))
|
||||
continue;
|
||||
name = tomoyo_encode(pos);
|
||||
break;
|
||||
}
|
||||
kfree(buf);
|
||||
if (!name)
|
||||
tomoyo_warn_oom(__func__);
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_realpath_nofollow - Get realpath of a pathname.
|
||||
*
|
||||
* @pathname: The pathname to solve.
|
||||
*
|
||||
* Returns the realpath of @pathname on success, NULL otherwise.
|
||||
*/
|
||||
char *tomoyo_realpath_nofollow(const char *pathname)
|
||||
{
|
||||
struct path path;
|
||||
|
||||
if (pathname && kern_path(pathname, 0, &path) == 0) {
|
||||
char *buf = tomoyo_realpath_from_path(&path);
|
||||
path_put(&path);
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
272
security/tomoyo/securityfs_if.c
Normal file
272
security/tomoyo/securityfs_if.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* security/tomoyo/securityfs_if.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#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.
|
||||
*
|
||||
* @inode: Pointer to "struct inode".
|
||||
* @file: Pointer to "struct file".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
const int key = ((u8 *) file_inode(file)->i_private)
|
||||
- ((u8 *) NULL);
|
||||
return tomoyo_open_control(key, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
*
|
||||
*/
|
||||
static int tomoyo_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
tomoyo_close_control(file->private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_poll - poll() for /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
* @wait: Pointer to "poll_table". Maybe NULL.
|
||||
*
|
||||
* Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write,
|
||||
* POLLOUT | POLLWRNORM otherwise.
|
||||
*/
|
||||
static unsigned int tomoyo_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
return tomoyo_poll_control(file, wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
* @buf: Pointer to buffer.
|
||||
* @count: Size of @buf.
|
||||
* @ppos: Unused.
|
||||
*
|
||||
* Returns bytes read on success, negative value otherwise.
|
||||
*/
|
||||
static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
return tomoyo_read_control(file->private_data, buf, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
* @buf: Pointer to buffer.
|
||||
* @count: Size of @buf.
|
||||
* @ppos: Unused.
|
||||
*
|
||||
* Returns @count on success, negative value otherwise.
|
||||
*/
|
||||
static ssize_t tomoyo_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
return tomoyo_write_control(file->private_data, buf, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* tomoyo_operations is a "struct file_operations" which is used for handling
|
||||
* /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR).
|
||||
* See tomoyo_io_buffer for internals.
|
||||
*/
|
||||
static const struct file_operations tomoyo_operations = {
|
||||
.open = tomoyo_open,
|
||||
.release = tomoyo_release,
|
||||
.poll = tomoyo_poll,
|
||||
.read = tomoyo_read,
|
||||
.write = tomoyo_write,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory.
|
||||
*
|
||||
* @name: The name of the interface file.
|
||||
* @mode: The permission of the interface file.
|
||||
* @parent: The parent directory.
|
||||
* @key: Type of interface.
|
||||
*
|
||||
* Returns nothing.
|
||||
*/
|
||||
static void __init tomoyo_create_entry(const char *name, const umode_t mode,
|
||||
struct dentry *parent, const u8 key)
|
||||
{
|
||||
securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key,
|
||||
&tomoyo_operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int __init tomoyo_initerface_init(void)
|
||||
{
|
||||
struct dentry *tomoyo_dir;
|
||||
|
||||
/* Don't create securityfs entries unless registered. */
|
||||
if (current_cred()->security != &tomoyo_kernel_domain)
|
||||
return 0;
|
||||
|
||||
tomoyo_dir = securityfs_create_dir("tomoyo", NULL);
|
||||
tomoyo_create_entry("query", 0600, tomoyo_dir,
|
||||
TOMOYO_QUERY);
|
||||
tomoyo_create_entry("domain_policy", 0600, tomoyo_dir,
|
||||
TOMOYO_DOMAINPOLICY);
|
||||
tomoyo_create_entry("exception_policy", 0600, tomoyo_dir,
|
||||
TOMOYO_EXCEPTIONPOLICY);
|
||||
tomoyo_create_entry("audit", 0400, tomoyo_dir,
|
||||
TOMOYO_AUDIT);
|
||||
tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
|
||||
TOMOYO_PROCESS_STATUS);
|
||||
tomoyo_create_entry("stat", 0644, tomoyo_dir,
|
||||
TOMOYO_STAT);
|
||||
tomoyo_create_entry("profile", 0600, tomoyo_dir,
|
||||
TOMOYO_PROFILE);
|
||||
tomoyo_create_entry("manager", 0600, tomoyo_dir,
|
||||
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;
|
||||
}
|
||||
|
||||
fs_initcall(tomoyo_initerface_init);
|
561
security/tomoyo/tomoyo.c
Normal file
561
security/tomoyo/tomoyo.c
Normal file
|
@ -0,0 +1,561 @@
|
|||
/*
|
||||
* security/tomoyo/tomoyo.c
|
||||
*
|
||||
* Copyright (C) 2005-2011 NTT DATA CORPORATION
|
||||
*/
|
||||
|
||||
#include <linux/security.h>
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* tomoyo_cred_alloc_blank - Target for security_cred_alloc_blank().
|
||||
*
|
||||
* @new: Pointer to "struct cred".
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
|
||||
{
|
||||
new->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_cred_prepare - Target for security_prepare_creds().
|
||||
*
|
||||
* @new: Pointer to "struct cred".
|
||||
* @old: Pointer to "struct cred".
|
||||
* @gfp: Memory allocation flags.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct tomoyo_domain_info *domain = old->security;
|
||||
new->security = domain;
|
||||
if (domain)
|
||||
atomic_inc(&domain->users);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_cred_transfer - Target for security_transfer_creds().
|
||||
*
|
||||
* @new: Pointer to "struct cred".
|
||||
* @old: Pointer to "struct cred".
|
||||
*/
|
||||
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
tomoyo_cred_prepare(new, old, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_cred_free - Target for security_cred_free().
|
||||
*
|
||||
* @cred: Pointer to "struct cred".
|
||||
*/
|
||||
static void tomoyo_cred_free(struct cred *cred)
|
||||
{
|
||||
struct tomoyo_domain_info *domain = cred->security;
|
||||
if (domain)
|
||||
atomic_dec(&domain->users);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = cap_bprm_set_creds(bprm);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Do only if this function is called for the first time of an execve
|
||||
* operation.
|
||||
*/
|
||||
if (bprm->cred_prepared)
|
||||
return 0;
|
||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||
/*
|
||||
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
|
||||
* for the first time.
|
||||
*/
|
||||
if (!tomoyo_policy_loaded)
|
||||
tomoyo_load_policy(bprm->filename);
|
||||
#endif
|
||||
/*
|
||||
* Release reference to "struct tomoyo_domain_info" stored inside
|
||||
* "bprm->cred->security". New reference to "struct tomoyo_domain_info"
|
||||
* stored inside "bprm->cred->security" will be acquired later inside
|
||||
* tomoyo_find_next_domain().
|
||||
*/
|
||||
atomic_dec(&((struct tomoyo_domain_info *)
|
||||
bprm->cred->security)->users);
|
||||
/*
|
||||
* Tell tomoyo_bprm_check_security() is called for the first time of an
|
||||
* execve operation.
|
||||
*/
|
||||
bprm->cred->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_bprm_check_security - Target for security_bprm_check().
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
|
||||
{
|
||||
struct tomoyo_domain_info *domain = bprm->cred->security;
|
||||
|
||||
/*
|
||||
* Execute permission is checked against pathname passed to do_execve()
|
||||
* using current domain.
|
||||
*/
|
||||
if (!domain) {
|
||||
const int idx = tomoyo_read_lock();
|
||||
const int err = tomoyo_find_next_domain(bprm);
|
||||
tomoyo_read_unlock(idx);
|
||||
return err;
|
||||
}
|
||||
/*
|
||||
* Read permission is checked against interpreters using next domain.
|
||||
*/
|
||||
return tomoyo_check_open_permission(domain, &bprm->file->f_path,
|
||||
O_RDONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_inode_getattr - Target for security_inode_getattr().
|
||||
*
|
||||
* @mnt: Pointer to "struct vfsmount".
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { mnt, dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_GETATTR, &path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_truncate - Target for security_path_truncate().
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_truncate(struct path *path)
|
||||
{
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_unlink - Target for security_path_unlink().
|
||||
*
|
||||
* @parent: Pointer to "struct path".
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_unlink(struct path *parent, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_mkdir - Target for security_path_mkdir().
|
||||
*
|
||||
* @parent: Pointer to "struct path".
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
* @mode: DAC permission mode.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry,
|
||||
umode_t mode)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path,
|
||||
mode & S_IALLUGO);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_rmdir - Target for security_path_rmdir().
|
||||
*
|
||||
* @parent: Pointer to "struct path".
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_symlink - Target for security_path_symlink().
|
||||
*
|
||||
* @parent: Pointer to "struct path".
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
* @old_name: Symlink's content.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_symlink(struct path *parent, struct dentry *dentry,
|
||||
const char *old_name)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_mknod - Target for security_path_mknod().
|
||||
*
|
||||
* @parent: Pointer to "struct path".
|
||||
* @dentry: Pointer to "struct dentry".
|
||||
* @mode: DAC permission mode.
|
||||
* @dev: Device attributes.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry,
|
||||
umode_t mode, unsigned int dev)
|
||||
{
|
||||
struct path path = { parent->mnt, dentry };
|
||||
int type = TOMOYO_TYPE_CREATE;
|
||||
const unsigned int perm = mode & S_IALLUGO;
|
||||
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFCHR:
|
||||
type = TOMOYO_TYPE_MKCHAR;
|
||||
break;
|
||||
case S_IFBLK:
|
||||
type = TOMOYO_TYPE_MKBLOCK;
|
||||
break;
|
||||
default:
|
||||
goto no_dev;
|
||||
}
|
||||
return tomoyo_mkdev_perm(type, &path, perm, dev);
|
||||
no_dev:
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFIFO:
|
||||
type = TOMOYO_TYPE_MKFIFO;
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
type = TOMOYO_TYPE_MKSOCK;
|
||||
break;
|
||||
}
|
||||
return tomoyo_path_number_perm(type, &path, perm);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_link - Target for security_path_link().
|
||||
*
|
||||
* @old_dentry: Pointer to "struct dentry".
|
||||
* @new_dir: Pointer to "struct path".
|
||||
* @new_dentry: Pointer to "struct dentry".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
struct path path1 = { new_dir->mnt, old_dentry };
|
||||
struct path path2 = { new_dir->mnt, new_dentry };
|
||||
return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_rename - Target for security_path_rename().
|
||||
*
|
||||
* @old_parent: Pointer to "struct path".
|
||||
* @old_dentry: Pointer to "struct dentry".
|
||||
* @new_parent: Pointer to "struct path".
|
||||
* @new_dentry: Pointer to "struct dentry".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_rename(struct path *old_parent,
|
||||
struct dentry *old_dentry,
|
||||
struct path *new_parent,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
struct path path1 = { old_parent->mnt, old_dentry };
|
||||
struct path path2 = { new_parent->mnt, new_dentry };
|
||||
return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_file_fcntl - Target for security_file_fcntl().
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
* @cmd: Command for fcntl().
|
||||
* @arg: Argument for @cmd.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_file_fcntl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)))
|
||||
return 0;
|
||||
return tomoyo_check_open_permission(tomoyo_domain(), &file->f_path,
|
||||
O_WRONLY | (arg & O_APPEND));
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_file_open - Target for security_file_open().
|
||||
*
|
||||
* @f: Pointer to "struct file".
|
||||
* @cred: Pointer to "struct cred".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_file_open(struct file *f, const struct cred *cred)
|
||||
{
|
||||
int flags = f->f_flags;
|
||||
/* Don't check read permission here if called from do_execve(). */
|
||||
if (current->in_execve)
|
||||
return 0;
|
||||
return tomoyo_check_open_permission(tomoyo_domain(), &f->f_path, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_file_ioctl - Target for security_file_ioctl().
|
||||
*
|
||||
* @file: Pointer to "struct file".
|
||||
* @cmd: Command for ioctl().
|
||||
* @arg: Argument for @cmd.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_chmod - Target for security_path_chmod().
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
* @mode: DAC permission mode.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_chmod(struct path *path, umode_t mode)
|
||||
{
|
||||
return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, path,
|
||||
mode & S_IALLUGO);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_chown - Target for security_path_chown().
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
* @uid: Owner ID.
|
||||
* @gid: Group ID.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_chown(struct path *path, kuid_t uid, kgid_t gid)
|
||||
{
|
||||
int error = 0;
|
||||
if (uid_valid(uid))
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path,
|
||||
from_kuid(&init_user_ns, uid));
|
||||
if (!error && gid_valid(gid))
|
||||
error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path,
|
||||
from_kgid(&init_user_ns, gid));
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_path_chroot - Target for security_path_chroot().
|
||||
*
|
||||
* @path: Pointer to "struct path".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_path_chroot(struct path *path)
|
||||
{
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_CHROOT, path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_sb_mount - Target for security_sb_mount().
|
||||
*
|
||||
* @dev_name: Name of device file. Maybe NULL.
|
||||
* @path: Pointer to "struct path".
|
||||
* @type: Name of filesystem type. Maybe NULL.
|
||||
* @flags: Mount options.
|
||||
* @data: Optional data. Maybe NULL.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_sb_mount(const char *dev_name, struct path *path,
|
||||
const char *type, unsigned long flags, void *data)
|
||||
{
|
||||
return tomoyo_mount_permission(dev_name, path, type, flags, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_sb_umount - Target for security_sb_umount().
|
||||
*
|
||||
* @mnt: Pointer to "struct vfsmount".
|
||||
* @flags: Unmount options.
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
static int tomoyo_sb_umount(struct vfsmount *mnt, int flags)
|
||||
{
|
||||
struct path path = { mnt, mnt->mnt_root };
|
||||
return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_sb_pivotroot - Target for security_sb_pivotroot().
|
||||
*
|
||||
* @old_path: Pointer to "struct path".
|
||||
* @new_path: Pointer to "struct path".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
static struct security_operations tomoyo_security_ops = {
|
||||
.name = "tomoyo",
|
||||
.cred_alloc_blank = tomoyo_cred_alloc_blank,
|
||||
.cred_prepare = tomoyo_cred_prepare,
|
||||
.cred_transfer = tomoyo_cred_transfer,
|
||||
.cred_free = tomoyo_cred_free,
|
||||
.bprm_set_creds = tomoyo_bprm_set_creds,
|
||||
.bprm_check_security = tomoyo_bprm_check_security,
|
||||
.file_fcntl = tomoyo_file_fcntl,
|
||||
.file_open = tomoyo_file_open,
|
||||
.path_truncate = tomoyo_path_truncate,
|
||||
.path_unlink = tomoyo_path_unlink,
|
||||
.path_mkdir = tomoyo_path_mkdir,
|
||||
.path_rmdir = tomoyo_path_rmdir,
|
||||
.path_symlink = tomoyo_path_symlink,
|
||||
.path_mknod = tomoyo_path_mknod,
|
||||
.path_link = tomoyo_path_link,
|
||||
.path_rename = tomoyo_path_rename,
|
||||
.inode_getattr = tomoyo_inode_getattr,
|
||||
.file_ioctl = tomoyo_file_ioctl,
|
||||
.path_chmod = tomoyo_path_chmod,
|
||||
.path_chown = tomoyo_path_chown,
|
||||
.path_chroot = tomoyo_path_chroot,
|
||||
.sb_mount = tomoyo_sb_mount,
|
||||
.sb_umount = tomoyo_sb_umount,
|
||||
.sb_pivotroot = tomoyo_sb_pivotroot,
|
||||
.socket_bind = tomoyo_socket_bind,
|
||||
.socket_connect = tomoyo_socket_connect,
|
||||
.socket_listen = tomoyo_socket_listen,
|
||||
.socket_sendmsg = tomoyo_socket_sendmsg,
|
||||
};
|
||||
|
||||
/* Lock for GC. */
|
||||
DEFINE_SRCU(tomoyo_ss);
|
||||
|
||||
/**
|
||||
* tomoyo_init - Register TOMOYO Linux as a LSM module.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int __init tomoyo_init(void)
|
||||
{
|
||||
struct cred *cred = (struct cred *) current_cred();
|
||||
|
||||
if (!security_module_enable(&tomoyo_security_ops))
|
||||
return 0;
|
||||
/* register ourselves with the security framework */
|
||||
if (register_security(&tomoyo_security_ops))
|
||||
panic("Failure registering TOMOYO Linux");
|
||||
printk(KERN_INFO "TOMOYO Linux initialized\n");
|
||||
cred->security = &tomoyo_kernel_domain;
|
||||
tomoyo_mm_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
security_initcall(tomoyo_init);
|
1085
security/tomoyo/util.c
Normal file
1085
security/tomoyo/util.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue