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