Tetsuo Handa | eadd99c | 2011-06-26 23:18:58 +0900 | [diff] [blame^] | 1 | /* |
| 2 | * security/tomoyo/audit.c |
| 3 | * |
| 4 | * Pathname restriction functions. |
| 5 | * |
| 6 | * Copyright (C) 2005-2010 NTT DATA CORPORATION |
| 7 | */ |
| 8 | |
| 9 | #include "common.h" |
| 10 | #include <linux/slab.h> |
| 11 | |
| 12 | /** |
| 13 | * tomoyo_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. |
| 14 | * |
| 15 | * @time: Seconds since 1970/01/01 00:00:00. |
| 16 | * @stamp: Pointer to "struct tomoyo_time". |
| 17 | * |
| 18 | * Returns nothing. |
| 19 | * |
| 20 | * This function does not handle Y2038 problem. |
| 21 | */ |
| 22 | static void tomoyo_convert_time(time_t time, struct tomoyo_time *stamp) |
| 23 | { |
| 24 | static const u16 tomoyo_eom[2][12] = { |
| 25 | { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, |
| 26 | { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } |
| 27 | }; |
| 28 | u16 y; |
| 29 | u8 m; |
| 30 | bool r; |
| 31 | stamp->sec = time % 60; |
| 32 | time /= 60; |
| 33 | stamp->min = time % 60; |
| 34 | time /= 60; |
| 35 | stamp->hour = time % 24; |
| 36 | time /= 24; |
| 37 | for (y = 1970; ; y++) { |
| 38 | const unsigned short days = (y & 3) ? 365 : 366; |
| 39 | if (time < days) |
| 40 | break; |
| 41 | time -= days; |
| 42 | } |
| 43 | r = (y & 3) == 0; |
| 44 | for (m = 0; m < 11 && time >= tomoyo_eom[r][m]; m++) |
| 45 | ; |
| 46 | if (m) |
| 47 | time -= tomoyo_eom[r][m - 1]; |
| 48 | stamp->year = y; |
| 49 | stamp->month = ++m; |
| 50 | stamp->day = ++time; |
| 51 | } |
| 52 | |
| 53 | /** |
| 54 | * tomoyo_print_header - Get header line of audit log. |
| 55 | * |
| 56 | * @r: Pointer to "struct tomoyo_request_info". |
| 57 | * |
| 58 | * Returns string representation. |
| 59 | * |
| 60 | * This function uses kmalloc(), so caller must kfree() if this function |
| 61 | * didn't return NULL. |
| 62 | */ |
| 63 | static char *tomoyo_print_header(struct tomoyo_request_info *r) |
| 64 | { |
| 65 | struct tomoyo_time stamp; |
| 66 | const pid_t gpid = task_pid_nr(current); |
| 67 | static const int tomoyo_buffer_len = 4096; |
| 68 | char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); |
| 69 | pid_t ppid; |
| 70 | if (!buffer) |
| 71 | return NULL; |
| 72 | { |
| 73 | struct timeval tv; |
| 74 | do_gettimeofday(&tv); |
| 75 | tomoyo_convert_time(tv.tv_sec, &stamp); |
| 76 | } |
| 77 | rcu_read_lock(); |
| 78 | ppid = task_tgid_vnr(current->real_parent); |
| 79 | rcu_read_unlock(); |
| 80 | snprintf(buffer, tomoyo_buffer_len - 1, |
| 81 | "#%04u/%02u/%02u %02u:%02u:%02u# profile=%u mode=%s " |
| 82 | "granted=%s (global-pid=%u) task={ pid=%u ppid=%u " |
| 83 | "uid=%u gid=%u euid=%u egid=%u suid=%u sgid=%u " |
| 84 | "fsuid=%u fsgid=%u }", |
| 85 | stamp.year, stamp.month, stamp.day, stamp.hour, |
| 86 | stamp.min, stamp.sec, r->profile, tomoyo_mode[r->mode], |
| 87 | tomoyo_yesno(r->granted), gpid, task_tgid_vnr(current), ppid, |
| 88 | current_uid(), current_gid(), current_euid(), current_egid(), |
| 89 | current_suid(), current_sgid(), current_fsuid(), |
| 90 | current_fsgid()); |
| 91 | return buffer; |
| 92 | } |
| 93 | |
| 94 | /** |
| 95 | * tomoyo_init_log - Allocate buffer for audit logs. |
| 96 | * |
| 97 | * @r: Pointer to "struct tomoyo_request_info". |
| 98 | * @len: Buffer size needed for @fmt and @args. |
| 99 | * @fmt: The printf()'s format string. |
| 100 | * @args: va_list structure for @fmt. |
| 101 | * |
| 102 | * Returns pointer to allocated memory. |
| 103 | * |
| 104 | * This function uses kzalloc(), so caller must kfree() if this function |
| 105 | * didn't return NULL. |
| 106 | */ |
| 107 | char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt, |
| 108 | va_list args) |
| 109 | { |
| 110 | char *buf = NULL; |
| 111 | const char *header = NULL; |
| 112 | int pos; |
| 113 | const char *domainname = tomoyo_domain()->domainname->name; |
| 114 | header = tomoyo_print_header(r); |
| 115 | if (!header) |
| 116 | return NULL; |
| 117 | /* +10 is for '\n' etc. and '\0'. */ |
| 118 | len += strlen(domainname) + strlen(header) + 10; |
| 119 | len = tomoyo_round2(len); |
| 120 | buf = kzalloc(len, GFP_NOFS); |
| 121 | if (!buf) |
| 122 | goto out; |
| 123 | len--; |
| 124 | pos = snprintf(buf, len, "%s", header); |
| 125 | pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname); |
| 126 | vsnprintf(buf + pos, len - pos, fmt, args); |
| 127 | out: |
| 128 | kfree(header); |
| 129 | return buf; |
| 130 | } |
| 131 | |
| 132 | /* Wait queue for /sys/kernel/security/tomoyo/audit. */ |
| 133 | static DECLARE_WAIT_QUEUE_HEAD(tomoyo_log_wait); |
| 134 | |
| 135 | /* Structure for audit log. */ |
| 136 | struct tomoyo_log { |
| 137 | struct list_head list; |
| 138 | char *log; |
| 139 | int size; |
| 140 | }; |
| 141 | |
| 142 | /* The list for "struct tomoyo_log". */ |
| 143 | static LIST_HEAD(tomoyo_log); |
| 144 | |
| 145 | /* Lock for "struct list_head tomoyo_log". */ |
| 146 | static DEFINE_SPINLOCK(tomoyo_log_lock); |
| 147 | |
| 148 | /* Length of "stuct list_head tomoyo_log". */ |
| 149 | static unsigned int tomoyo_log_count; |
| 150 | |
| 151 | /** |
| 152 | * tomoyo_get_audit - Get audit mode. |
| 153 | * |
| 154 | * @profile: Profile number. |
| 155 | * @index: Index number of functionality. |
| 156 | * @is_granted: True if granted log, false otherwise. |
| 157 | * |
| 158 | * Returns true if this request should be audited, false otherwise. |
| 159 | */ |
| 160 | static bool tomoyo_get_audit(const u8 profile, const u8 index, |
| 161 | const bool is_granted) |
| 162 | { |
| 163 | u8 mode; |
| 164 | const u8 category = TOMOYO_MAC_CATEGORY_FILE + TOMOYO_MAX_MAC_INDEX; |
| 165 | struct tomoyo_profile *p; |
| 166 | if (!tomoyo_policy_loaded) |
| 167 | return false; |
| 168 | p = tomoyo_profile(profile); |
| 169 | if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG]) |
| 170 | return false; |
| 171 | mode = p->config[index]; |
| 172 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
| 173 | mode = p->config[category]; |
| 174 | if (mode == TOMOYO_CONFIG_USE_DEFAULT) |
| 175 | mode = p->default_config; |
| 176 | if (is_granted) |
| 177 | return mode & TOMOYO_CONFIG_WANT_GRANT_LOG; |
| 178 | return mode & TOMOYO_CONFIG_WANT_REJECT_LOG; |
| 179 | } |
| 180 | |
| 181 | /** |
| 182 | * tomoyo_write_log2 - Write an audit log. |
| 183 | * |
| 184 | * @r: Pointer to "struct tomoyo_request_info". |
| 185 | * @len: Buffer size needed for @fmt and @args. |
| 186 | * @fmt: The printf()'s format string. |
| 187 | * @args: va_list structure for @fmt. |
| 188 | * |
| 189 | * Returns nothing. |
| 190 | */ |
| 191 | void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, |
| 192 | va_list args) |
| 193 | { |
| 194 | char *buf; |
| 195 | struct tomoyo_log *entry; |
| 196 | bool quota_exceeded = false; |
| 197 | if (!tomoyo_get_audit(r->profile, r->type, r->granted)) |
| 198 | goto out; |
| 199 | buf = tomoyo_init_log(r, len, fmt, args); |
| 200 | if (!buf) |
| 201 | goto out; |
| 202 | entry = kzalloc(sizeof(*entry), GFP_NOFS); |
| 203 | if (!entry) { |
| 204 | kfree(buf); |
| 205 | goto out; |
| 206 | } |
| 207 | entry->log = buf; |
| 208 | len = tomoyo_round2(strlen(buf) + 1); |
| 209 | /* |
| 210 | * The entry->size is used for memory quota checks. |
| 211 | * Don't go beyond strlen(entry->log). |
| 212 | */ |
| 213 | entry->size = len + tomoyo_round2(sizeof(*entry)); |
| 214 | spin_lock(&tomoyo_log_lock); |
| 215 | if (tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT] && |
| 216 | tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] + entry->size >= |
| 217 | tomoyo_memory_quota[TOMOYO_MEMORY_AUDIT]) { |
| 218 | quota_exceeded = true; |
| 219 | } else { |
| 220 | tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] += entry->size; |
| 221 | list_add_tail(&entry->list, &tomoyo_log); |
| 222 | tomoyo_log_count++; |
| 223 | } |
| 224 | spin_unlock(&tomoyo_log_lock); |
| 225 | if (quota_exceeded) { |
| 226 | kfree(buf); |
| 227 | kfree(entry); |
| 228 | goto out; |
| 229 | } |
| 230 | wake_up(&tomoyo_log_wait); |
| 231 | out: |
| 232 | return; |
| 233 | } |
| 234 | |
| 235 | /** |
| 236 | * tomoyo_write_log - Write an audit log. |
| 237 | * |
| 238 | * @r: Pointer to "struct tomoyo_request_info". |
| 239 | * @fmt: The printf()'s format string, followed by parameters. |
| 240 | * |
| 241 | * Returns nothing. |
| 242 | */ |
| 243 | void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) |
| 244 | { |
| 245 | va_list args; |
| 246 | int len; |
| 247 | va_start(args, fmt); |
| 248 | len = vsnprintf((char *) &len, 1, fmt, args) + 1; |
| 249 | va_end(args); |
| 250 | va_start(args, fmt); |
| 251 | tomoyo_write_log2(r, len, fmt, args); |
| 252 | va_end(args); |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * tomoyo_read_log - Read an audit log. |
| 257 | * |
| 258 | * @head: Pointer to "struct tomoyo_io_buffer". |
| 259 | * |
| 260 | * Returns nothing. |
| 261 | */ |
| 262 | void tomoyo_read_log(struct tomoyo_io_buffer *head) |
| 263 | { |
| 264 | struct tomoyo_log *ptr = NULL; |
| 265 | if (head->r.w_pos) |
| 266 | return; |
| 267 | kfree(head->read_buf); |
| 268 | head->read_buf = NULL; |
| 269 | spin_lock(&tomoyo_log_lock); |
| 270 | if (!list_empty(&tomoyo_log)) { |
| 271 | ptr = list_entry(tomoyo_log.next, typeof(*ptr), list); |
| 272 | list_del(&ptr->list); |
| 273 | tomoyo_log_count--; |
| 274 | tomoyo_memory_used[TOMOYO_MEMORY_AUDIT] -= ptr->size; |
| 275 | } |
| 276 | spin_unlock(&tomoyo_log_lock); |
| 277 | if (ptr) { |
| 278 | head->read_buf = ptr->log; |
| 279 | head->r.w[head->r.w_pos++] = head->read_buf; |
| 280 | kfree(ptr); |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * tomoyo_poll_log - Wait for an audit log. |
| 286 | * |
| 287 | * @file: Pointer to "struct file". |
| 288 | * @wait: Pointer to "poll_table". |
| 289 | * |
| 290 | * Returns POLLIN | POLLRDNORM when ready to read an audit log. |
| 291 | */ |
| 292 | int tomoyo_poll_log(struct file *file, poll_table *wait) |
| 293 | { |
| 294 | if (tomoyo_log_count) |
| 295 | return POLLIN | POLLRDNORM; |
| 296 | poll_wait(file, &tomoyo_log_wait, wait); |
| 297 | if (tomoyo_log_count) |
| 298 | return POLLIN | POLLRDNORM; |
| 299 | return 0; |
| 300 | } |