This file handles audit logs for TOMOYO. TOMOYO uses /proc/ccs/ interface for reporting access logs in domain policy format. One is 'grant_log', used for auditing accesses which are granted by the domain policy. The other is 'reject_log', used for auditing accesses which are not granted by the domain policy. The userland daemon /usr/lib/ccs/ccs-auditd will save these logs. By the way, copy_strings() seems to assume that kmap() never fails. Does get_user_pages() guarantee that the kmap() never fails? Signed-off-by: Kentaro Takeda Signed-off-by: Tetsuo Handa Signed-off-by: Toshiharu Harada --- fs/tomoyo_audit.c | 393 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) --- /dev/null +++ linux-2.6.25-rc8-mm1/fs/tomoyo_audit.c @@ -0,0 +1,393 @@ +/* + * fs/tomoyo_audit.c + * + * Implementation of the Domain-Based Mandatory Access Control. + * + * Copyright (C) 2005-2008 NTT DATA CORPORATION + * + * Version: 1.6.0 2008/04/01 + * + */ + +#include +#include +#include +#include +#include + +/** + * ccs_print_bprm - Print "struct linux_binprm" for auditing. + * + * @bprm: Pointer to "struct linux_binprm". + * + * Returns the contents of @bprm on success, NULL otherwise. + */ +static char *ccs_print_bprm(struct linux_binprm *bprm) +{ + static const int buffer_len = 4096 * 2; + char *buffer = ccs_alloc(buffer_len); + char *cp; + char *last_start; + int len; + unsigned long pos = bprm->p; + int i = pos / PAGE_SIZE; + 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, buffer_len - 1, + "argc=%d envc=%d argv[]={ ", argv_count, envp_count); + cp = buffer + len; + if (!argv_count) { + memmove(cp, "} envp[]={ ", 11); + cp += 11; + } + last_start = cp; + while (argv_count || envp_count) { + struct page *page; + const char *kaddr; +#ifdef CONFIG_MMU + if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, + NULL) <= 0) + goto out; + pos += PAGE_SIZE - offset; +#else + page = bprm->page[i]; +#endif + /* Map */ + kaddr = kmap(page); + if (!kaddr) { /* Mapping failed. */ +#ifdef CONFIG_MMU + put_page(page); +#endif + goto out; + } + /* Read. */ + while (offset < PAGE_SIZE) { + const unsigned char c = kaddr[offset++]; + if (cp == last_start) + *cp++ = '"'; + if (cp >= buffer + buffer_len - 32) { + /* Preserve 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; + } + /* Unmap. */ + kunmap(page); +#ifdef CONFIG_MMU + put_page(page); +#endif + i++; + offset = 0; + } + *cp++ = '}'; + *cp = '\0'; + return buffer; + out: + snprintf(buffer, buffer_len - 1, + "argc=%d envc=%d argv[]={ ... } envp[]= { ... }", + argv_count, envp_count); + return buffer; +} + +static DECLARE_WAIT_QUEUE_HEAD(grant_log_wait); +static DECLARE_WAIT_QUEUE_HEAD(reject_log_wait); + +static DEFINE_SPINLOCK(audit_log_lock); + +/* Structure for audit log. */ +struct log_entry { + struct list_head list; + char *log; +}; + +/* The list for "struct log_entry". */ +static LIST_HEAD(grant_log); + +/* The list for "struct log_entry". */ +static LIST_HEAD(reject_log); + +static int grant_log_count; +static int reject_log_count; + +/** + * ccs_init_audit_log - Allocate buffer for audit logs. + * + * @len: Required size. + * @profile: Profile number. + * @mode: Access control mode. + * @bprm: Pointer to "struct linux_binprm". May be NULL. + * + * Returns pointer to allocated memory. + * + * The @len is updated to add the header lines' size on success. + */ +char *ccs_init_audit_log(int *len, const u8 profile, const u8 mode, + struct linux_binprm *bprm) +{ + static const char *mode_4[4] = { + "disabled", "learning", "permissive", "enforcing" + }; + char *buf; + char *bprm_info = ""; + struct timeval tv; + struct task_struct *task = current; + u32 tomoyo_flags = task->tomoyo_flags; + const char *domainname = current->domain_info->domainname->name; + do_gettimeofday(&tv); + *len += strlen(domainname) + 256; + if (bprm) { + bprm_info = ccs_print_bprm(bprm); + if (!bprm_info) + return NULL; + *len += strlen(bprm_info); + } + buf = ccs_alloc(*len); + if (buf) + snprintf(buf, (*len) - 1, + "#timestamp=%lu profile=%u mode=%s pid=%d uid=%d " + "gid=%d euid=%d egid=%d suid=%d sgid=%d fsuid=%d " + "fsgid=%d state[0]=%u state[1]=%u state[2]=%u %s\n" + "%s\n", tv.tv_sec, profile, mode_4[mode], task->pid, + task->uid, task->gid, task->euid, task->egid, + task->suid, task->sgid, task->fsuid, task->fsgid, + (u8) (tomoyo_flags >> 24), (u8) (tomoyo_flags >> 16), + (u8) (tomoyo_flags >> 8), bprm_info, domainname); + if (bprm) + ccs_free(bprm_info); + return buf; +} + +/** + * get_max_grant_log - Get max number of spoolable grant logs. + * + * Returns max number of spoolable grant logs. + */ +static unsigned int get_max_grant_log(void) +{ + return ccs_check_flags(CCS_TOMOYO_MAX_GRANT_LOG); +} + +/** + * get_max_reject_log - Get max number of spoolable reject logs. + * + * Returns max number of spoolable reject logs. + */ +static unsigned int get_max_reject_log(void) +{ + return ccs_check_flags(CCS_TOMOYO_MAX_REJECT_LOG); +} + +/** + * ccs_write_audit_log - Write audit log. + * + * @buf: Pointer to audit log. + * @is_granted: True if this is a granted log. + * + * Returns 0 on success, -ENOMEM otherwise. + * + * Caller must allocate @buf with ccs_init_audit_log(). + */ +int ccs_write_audit_log(char *buf, const bool is_granted) +{ + struct log_entry *new_entry = ccs_alloc(sizeof(*new_entry)); + if (!new_entry) + goto out; + INIT_LIST_HEAD(&new_entry->list); + new_entry->log = buf; + /***** CRITICAL SECTION START *****/ + spin_lock(&audit_log_lock); + if (is_granted) { + if (grant_log_count < get_max_grant_log()) { + list_add_tail(&new_entry->list, &grant_log); + grant_log_count++; + buf = NULL; + ccs_update_counter(CCS_UPDATES_COUNTER_GRANT_LOG); + } + } else { + if (reject_log_count < get_max_reject_log()) { + list_add_tail(&new_entry->list, &reject_log); + reject_log_count++; + buf = NULL; + ccs_update_counter(CCS_UPDATES_COUNTER_REJECT_LOG); + } + } + spin_unlock(&audit_log_lock); + /***** CRITICAL SECTION END *****/ + if (is_granted) + wake_up(&grant_log_wait); + else + wake_up(&reject_log_wait); + if (!buf) + return 0; + ccs_free(new_entry); + out: + ccs_free(buf); + return -ENOMEM; +} + +/** + * ccs_can_save_audit_log - Check whether the kernel can save new audit log. + * + * @is_granted: True if this is a granted log. + * + * Returns 0 if the kernel can save, -ENOMEM otherwise. + */ +int ccs_can_save_audit_log(const bool is_granted) +{ + if (is_granted) { + if (grant_log_count < get_max_grant_log()) + return 0; + } else { + if (reject_log_count < get_max_reject_log()) + return 0; + } + return -ENOMEM; +} + +/** + * ccs_read_grant_log - Read a grant log. + * + * @head: Pointer to "struct ccs_io_buffer". + * + * Returns 0. + */ +int ccs_read_grant_log(struct ccs_io_buffer *head) +{ + struct log_entry *ptr = NULL; + if (head->read_avail) + return 0; + if (head->read_buf) { + ccs_free(head->read_buf); + head->read_buf = NULL; + head->readbuf_size = 0; + } + /***** CRITICAL SECTION START *****/ + spin_lock(&audit_log_lock); + if (!list_empty(&grant_log)) { + ptr = list_entry(grant_log.next, struct log_entry, list); + list_del(&ptr->list); + grant_log_count--; + } + spin_unlock(&audit_log_lock); + /***** CRITICAL SECTION END *****/ + if (ptr) { + head->read_buf = ptr->log; + head->read_avail = strlen(ptr->log) + 1; + head->readbuf_size = head->read_avail; + ccs_free(ptr); + } + return 0; +} + +/** + * ccs_poll_grant_log - Wait for a grant log. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Returns POLLIN | POLLRDNORM when ready to read a grant log. + */ +int ccs_poll_grant_log(struct file *file, poll_table *wait) +{ + if (grant_log_count) + return POLLIN | POLLRDNORM; + poll_wait(file, &grant_log_wait, wait); + if (grant_log_count) + return POLLIN | POLLRDNORM; + return 0; +} + +/** + * ccs_read_reject_log - Read a reject log. + * + * @head: Pointer to "struct ccs_io_buffer". + * + * Returns 0. + */ +int ccs_read_reject_log(struct ccs_io_buffer *head) +{ + struct log_entry *ptr = NULL; + if (head->read_avail) + return 0; + if (head->read_buf) { + ccs_free(head->read_buf); + head->read_buf = NULL; + head->readbuf_size = 0; + } + /***** CRITICAL SECTION START *****/ + spin_lock(&audit_log_lock); + if (!list_empty(&reject_log)) { + ptr = list_entry(reject_log.next, struct log_entry, list); + list_del(&ptr->list); + reject_log_count--; + } + spin_unlock(&audit_log_lock); + /***** CRITICAL SECTION END *****/ + if (ptr) { + head->read_buf = ptr->log; + head->read_avail = strlen(ptr->log) + 1; + head->readbuf_size = head->read_avail; + ccs_free(ptr); + } + return 0; +} + +/** + * ccs_poll_reject_log - Wait for a reject log. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Returns POLLIN | POLLRDNORM when ready to read a reject log. + */ +int ccs_poll_reject_log(struct file *file, poll_table *wait) +{ + if (reject_log_count) + return POLLIN | POLLRDNORM; + poll_wait(file, &reject_log_wait, wait); + if (reject_log_count) + return POLLIN | POLLRDNORM; + return 0; +} -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/