/** * tomoyo_flush - Flush queued string to userspace's buffer. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns true if all data was flushed, false otherwise.
*/ staticbool tomoyo_flush(struct tomoyo_io_buffer *head)
{ while (head->r.w_pos) { constchar *w = head->r.w[0];
size_t len = strlen(w);
if (len) { if (len > head->read_user_buf_avail)
len = head->read_user_buf_avail; if (!len) returnfalse; if (copy_to_user(head->read_user_buf, w, len)) returnfalse;
head->read_user_buf_avail -= len;
head->read_user_buf += len;
w += len;
}
head->r.w[0] = w; if (*w) returnfalse; /* Add '\0' for audit logs and query. */ if (head->poll) { if (!head->read_user_buf_avail ||
copy_to_user(head->read_user_buf, "", 1)) returnfalse;
head->read_user_buf_avail--;
head->read_user_buf++;
}
head->r.w_pos--; for (len = 0; len < head->r.w_pos; len++)
head->r.w[len] = head->r.w[len + 1];
}
head->r.avail = 0; returntrue;
}
/** * tomoyo_set_string - Queue string to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * @string: String to print. * * Note that @string has to be kept valid until @head is kfree()d. * This means that char[] allocated on stack memory cannot be passed to * this function. Use tomoyo_io_printf() for char[] allocated on stack memory.
*/ staticvoid tomoyo_set_string(struct tomoyo_io_buffer *head, constchar *string)
{ if (head->r.w_pos < TOMOYO_MAX_IO_READ_QUEUE) {
head->r.w[head->r.w_pos++] = string;
tomoyo_flush(head);
} else
WARN_ON(1);
}
/** * 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.
*/ staticvoid tomoyo_io_printf(struct tomoyo_io_buffer *head, constchar *fmt,
...)
{
va_list args;
size_t len;
size_t pos = head->r.avail; int size = head->readbuf_size - pos;
if (size <= 0) return;
va_start(args, fmt);
len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1;
va_end(args); if (pos + len >= head->readbuf_size) {
WARN_ON(1); return;
}
head->r.avail += len;
tomoyo_set_string(head, head->read_buf + pos);
}
/** * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns nothing.
*/ staticvoid tomoyo_set_space(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, " ");
}
/** * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns nothing.
*/ staticbool tomoyo_set_lf(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, "\n"); return !head->r.w_pos;
}
/** * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns nothing.
*/ staticvoid tomoyo_set_slash(struct tomoyo_io_buffer *head)
{
tomoyo_set_string(head, "/");
}
/* List of namespaces. */
LIST_HEAD(tomoyo_namespace_list); /* True if namespace other than tomoyo_kernel_namespace is defined. */ staticbool tomoyo_namespace_enabled;
/** * tomoyo_set_uint - Set value for specified preference. * * @i: Pointer to "unsigned int". * @string: String to check. * @find: Name of keyword. * * Returns nothing.
*/ staticvoid tomoyo_set_uint(unsignedint *i, constchar *string, constchar *find)
{ constchar *cp = strstr(string, find);
if (cp)
sscanf(cp + strlen(find), "=%u", i);
}
/** * tomoyo_set_mode - Set mode for specified profile. * * @name: Name of functionality. * @value: Mode for @name. * @profile: Pointer to "struct tomoyo_profile". * * Returns 0 on success, negative value otherwise.
*/ staticint tomoyo_set_mode(char *name, constchar *value, struct tomoyo_profile *profile)
{
u8 i;
u8 config;
if (!strcmp(name, "CONFIG")) {
i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX;
config = profile->default_config;
} elseif (tomoyo_str_starts(&name, "CONFIG::")) {
config = 0; for (i = 0; i < TOMOYO_MAX_MAC_INDEX
+ TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { int len = 0;
if (i < TOMOYO_MAX_MAC_INDEX) { const u8 c = tomoyo_index2category[i]; constchar *category =
tomoyo_category_keywords[c];
len = strlen(category); if (strncmp(name, category, len) ||
name[len++] != ':' || name[len++] != ':') continue;
} if (strcmp(name + len, tomoyo_mac_keywords[i])) continue;
config = profile->config[i]; break;
} if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) return -EINVAL;
} else { return -EINVAL;
} if (strstr(value, "use_default")) {
config = TOMOYO_CONFIG_USE_DEFAULT;
} else {
u8 mode;
for (mode = 0; mode < 4; mode++) if (strstr(value, tomoyo_mode[mode])) /* * Update lower 3 bits in order to distinguish * 'config' from 'TOMOYO_CONFIG_USE_DEFAULT'.
*/
config = (config & ~7) | mode; if (config != TOMOYO_CONFIG_USE_DEFAULT) { switch (tomoyo_find_yesno(value, "grant_log")) { case 1:
config |= TOMOYO_CONFIG_WANT_GRANT_LOG; break; case 0:
config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG; break;
} switch (tomoyo_find_yesno(value, "reject_log")) { case 1:
config |= TOMOYO_CONFIG_WANT_REJECT_LOG; break; case 0:
config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG; break;
}
}
} if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
profile->config[i] = config; elseif (config != TOMOYO_CONFIG_USE_DEFAULT)
profile->default_config = config; return 0;
}
if (ptr->head.is_deleted) continue; if (!tomoyo_flush(head)) return;
tomoyo_set_string(head, ptr->manager->name);
tomoyo_set_lf(head);
}
head->r.eof = true;
}
/** * tomoyo_manager - Check whether the current process is a policy manager. * * Returns true if the current process is permitted to modify policy * via /sys/kernel/security/tomoyo/ interface. * * Caller holds tomoyo_read_lock().
*/ staticbool tomoyo_manager(void)
{ struct tomoyo_manager *ptr; constchar *exe; conststruct task_struct *task = current; conststruct tomoyo_path_info *domainname = tomoyo_domain()->domainname; bool found = IS_ENABLED(CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING);
if (!tomoyo_policy_loaded) returntrue; if (!tomoyo_manage_by_non_root &&
(!uid_eq(task->cred->uid, GLOBAL_ROOT_UID) ||
!uid_eq(task->cred->euid, GLOBAL_ROOT_UID))) returnfalse;
exe = tomoyo_get_exe(); if (!exe) returnfalse;
list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list,
srcu_read_lock_held(&tomoyo_ss)) { if (!ptr->head.is_deleted &&
(!tomoyo_pathcmp(domainname, ptr->manager) ||
!strcmp(exe, ptr->manager->name))) {
found = true; break;
}
} if (!found) { /* Reduce error messages. */ static pid_t last_pid; const pid_t pid = current->pid;
if (last_pid != pid) {
pr_warn("%s ( %s ) is not permitted to update policies.\n",
domainname->name, exe);
last_pid = pid;
}
}
kfree(exe); return found;
}
for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) { if (!tomoyo_str_starts(¶m.data,
tomoyo_callback[i].keyword)) continue; return tomoyo_callback[i].write(¶m);
} return -EINVAL;
}
/** * tomoyo_read_pid - Get domainname of the specified PID. * * @head: Pointer to "struct tomoyo_io_buffer". * * Returns the domainname which the specified PID is in on success, * empty string otherwise. * The PID is specified by tomoyo_write_pid() so that the user can obtain * using read()/write() interface rather than sysctl() interface.
*/ staticvoid tomoyo_read_pid(struct tomoyo_io_buffer *head)
{ char *buf = head->write_buf; bool global_pid = false; unsignedint pid; struct task_struct *p; struct tomoyo_domain_info *domain = NULL;
/* Accessing write_buf is safe because head->io_sem is held. */ if (!buf) {
head->r.eof = true; return; /* Do nothing if open(O_RDONLY). */
} if (head->r.w_pos || head->r.eof) return;
head->r.eof = true; if (tomoyo_str_starts(&buf, "global-pid "))
global_pid = true; if (kstrtouint(buf, 10, &pid)) return;
rcu_read_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_task(p)->domain_info;
rcu_read_unlock(); if (!domain) return;
tomoyo_io_printf(head, "%u %u ", pid, domain->profile);
tomoyo_set_string(head, domain->domainname->name);
}
if (tomoyo_str_starts(¶m.data, "aggregator ")) return tomoyo_write_aggregator(¶m); for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) if (tomoyo_str_starts(¶m.data, tomoyo_transition_type[i])) return tomoyo_write_transition_control(¶m, i); for (i = 0; i < TOMOYO_MAX_GROUP; i++) if (tomoyo_str_starts(¶m.data, tomoyo_group_name[i])) return tomoyo_write_group(¶m, i); if (tomoyo_str_starts(¶m.data, "acl_group ")) { unsignedint group; char *data;
/** * tomoyo_numscan - sscanf() which stores the length of a decimal integer value. * * @str: String to scan. * @head: Leading string that must start with. * @width: Pointer to "int" for storing length of a decimal integer value after @head. * @tail: Optional character that must match after a decimal integer value. * * Returns whether @str starts with @head and a decimal value follows @head.
*/ staticbool tomoyo_numscan(constchar *str, constchar *head, int *width, constchar tail)
{ constchar *cp; constint n = strlen(head);
/** * tomoyo_patternize_path - Make patterns for file path. Used by learning mode. * * @buffer: Destination buffer. * @len: Size of @buffer. * @entry: Original line. * * Returns nothing.
*/ staticvoid tomoyo_patternize_path(char *buffer, constint len, char *entry)
{ int width; char *cp = entry;
/* Nothing to do if this line is not for "file" related entry. */ if (strncmp(entry, "file ", 5)) goto flush; /* * Nothing to do if there is no colon in this line, for this rewriting * applies to only filesystems where numeric values in the path are volatile.
*/
cp = strchr(entry + 5, ':'); if (!cp) {
cp = entry; goto flush;
} /* Flush e.g. "file ioctl" part. */ while (*cp != ' ')
cp--;
*cp++ = '\0';
tomoyo_addprintf(buffer, len, "%s ", entry); /* e.g. file ioctl pipe:[$INO] $CMD */ if (tomoyo_numscan(cp, "pipe:[", &width, ']')) {
cp += width + 7;
tomoyo_addprintf(buffer, len, "pipe:[\\$]"); goto flush;
} /* e.g. file ioctl socket:[$INO] $CMD */ if (tomoyo_numscan(cp, "socket:[", &width, ']')) {
cp += width + 9;
tomoyo_addprintf(buffer, len, "socket:[\\$]"); goto flush;
} if (!strncmp(cp, "proc:/self", 10)) { /* e.g. file read proc:/self/task/$TID/fdinfo/$FD */
cp += 10;
tomoyo_addprintf(buffer, len, "proc:/self");
} elseif (tomoyo_numscan(cp, "proc:/", &width, 0)) { /* e.g. file read proc:/$PID/task/$TID/fdinfo/$FD */ /* * Don't patternize $PID part if $PID == 1, for several * programs access only files in /proc/1/ directory.
*/
cp += width + 6; if (width == 1 && *(cp - 1) == '1')
tomoyo_addprintf(buffer, len, "proc:/1"); else
tomoyo_addprintf(buffer, len, "proc:/\\$");
} else { goto flush;
} /* Patternize $TID part if "/task/" follows. */ if (tomoyo_numscan(cp, "/task/", &width, 0)) {
cp += width + 6;
tomoyo_addprintf(buffer, len, "/task/\\$");
} /* Patternize $FD part if "/fd/" or "/fdinfo/" follows. */ if (tomoyo_numscan(cp, "/fd/", &width, 0)) {
cp += width + 4;
tomoyo_addprintf(buffer, len, "/fd/\\$");
} elseif (tomoyo_numscan(cp, "/fdinfo/", &width, 0)) {
cp += width + 8;
tomoyo_addprintf(buffer, len, "/fdinfo/\\$");
}
flush: /* Flush remaining part if any. */ if (*cp)
tomoyo_addprintf(buffer, len, "%s", cp);
}
/** * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode. * * @domain: Pointer to "struct tomoyo_domain_info". * @header: Lines containing ACL. * * Returns nothing.
*/ staticvoid tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
{ char *buffer; char *realpath = NULL; char *argv0 = NULL; char *symlink = NULL; char *cp = strchr(header, '\n'); int len;
if (!cp) return;
cp = strchr(cp + 1, '\n'); if (!cp) return;
*cp++ = '\0'; /* Reserve some space for potentially using patterns. */
len = strlen(cp) + 16; /* strstr() will return NULL if ordering is wrong. */ if (*cp == 'f') {
argv0 = strstr(header, " argv[]={ \""); if (argv0) {
argv0 += 10;
len += tomoyo_truncate(argv0) + 14;
}
realpath = strstr(header, " exec={ realpath=\""); if (realpath) {
realpath += 8;
len += tomoyo_truncate(realpath) + 6;
}
symlink = strstr(header, " symlink.target=\""); if (symlink)
len += tomoyo_truncate(symlink + 1) + 1;
}
buffer = kmalloc(len, GFP_NOFS | __GFP_ZERO); if (!buffer) return;
tomoyo_patternize_path(buffer, len, cp); if (realpath)
tomoyo_addprintf(buffer, len, " exec.%s", realpath); if (argv0)
tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0); if (symlink)
tomoyo_addprintf(buffer, len, "%s", symlink);
tomoyo_normalize_line(buffer); if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, false))
tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES);
kfree(buffer);
}
/** * tomoyo_supervisor - Ask for the supervisor's decision. * * @r: Pointer to "struct tomoyo_request_info". * @fmt: The printf()'s format string, followed by parameters. * * Returns 0 if the supervisor decided to permit the access request which * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the * supervisor decided to retry the access request which violated the policy in * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise.
*/ int tomoyo_supervisor(struct tomoyo_request_info *r, constchar *fmt, ...)
{
va_list args; int error; int len; staticunsignedint tomoyo_serial; struct tomoyo_query entry = { }; bool quota_exceeded = false;
va_start(args, fmt);
len = vsnprintf(NULL, 0, fmt, args) + 1;
va_end(args); /* Write /sys/kernel/security/tomoyo/audit. */
va_start(args, fmt);
tomoyo_write_log2(r, len, fmt, args);
va_end(args); /* Nothing more to do if granted. */ if (r->granted) return 0; if (r->mode)
tomoyo_update_stat(r->mode); switch (r->mode) { case TOMOYO_CONFIG_ENFORCING:
error = -EPERM; if (atomic_read(&tomoyo_query_observers)) break; goto out; case TOMOYO_CONFIG_LEARNING:
error = 0; /* Check max_learning_entry parameter. */ if (tomoyo_domain_quota_is_ok(r)) break;
fallthrough; default: return 0;
} /* Get message. */
va_start(args, fmt);
entry.query = tomoyo_init_log(r, len, fmt, args);
va_end(args); if (!entry.query) goto out;
entry.query_len = strlen(entry.query) + 1; if (!error) {
tomoyo_add_entry(r->domain, entry.query); goto out;
}
len = kmalloc_size_roundup(entry.query_len);
entry.domain = r->domain;
spin_lock(&tomoyo_query_list_lock); if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] &&
tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len
>= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {
quota_exceeded = true;
} else {
entry.serial = tomoyo_serial++;
entry.retry = r->retry;
tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len;
list_add_tail(&entry.list, &tomoyo_query_list);
}
spin_unlock(&tomoyo_query_list_lock); if (quota_exceeded) goto out; /* Give 10 seconds for supervisor's opinion. */ while (entry.timer < 10) {
wake_up_all(&tomoyo_query_wait); if (wait_event_interruptible_timeout
(tomoyo_answer_wait, entry.answer ||
!atomic_read(&tomoyo_query_observers), HZ)) break;
entry.timer++;
}
spin_lock(&tomoyo_query_list_lock);
list_del(&entry.list);
tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;
spin_unlock(&tomoyo_query_list_lock); switch (entry.answer) { case 3: /* Asked to retry by administrator. */
error = TOMOYO_RETRY_REQUEST;
r->retry++; break; case 1: /* Granted by administrator. */
error = 0; break; default: /* Timed out or rejected by administrator. */ break;
}
out:
kfree(entry.query); return error;
}
/** * tomoyo_find_domain_by_qid - Get domain by query id. * * @serial: Query ID assigned by tomoyo_supervisor(). * * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise.
*/ staticstruct tomoyo_domain_info *tomoyo_find_domain_by_qid
(unsignedint serial)
{ struct tomoyo_query *ptr; struct tomoyo_domain_info *domain = NULL;
if (pos++ != head->r.query_index) continue; /* * Some query can be skipped because tomoyo_query_list * can change, but I don't care.
*/ if (len == ptr->query_len)
snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial,
ptr->retry, ptr->query); break;
}
spin_unlock(&tomoyo_query_list_lock); if (buf[0]) {
head->read_buf = buf;
head->r.w[head->r.w_pos++] = buf;
head->r.query_index++;
} else {
kfree(buf);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.