1 |
/* |
2 |
* fs/tomoyo_audit.c |
3 |
* |
4 |
* Implementation of the Domain-Based Mandatory Access Control. |
5 |
* |
6 |
* Copyright (C) 2005-2008 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.6.6-pre 2008/12/24 |
9 |
* |
10 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
11 |
* See README.ccs for ChangeLog. |
12 |
* |
13 |
*/ |
14 |
|
15 |
#include <linux/ccs_common.h> |
16 |
#include <linux/tomoyo.h> |
17 |
#include <linux/realpath.h> |
18 |
#include <linux/highmem.h> |
19 |
|
20 |
/** |
21 |
* ccs_print_bprm - Print "struct linux_binprm" for auditing. |
22 |
* |
23 |
* @bprm: Pointer to "struct linux_binprm". |
24 |
* |
25 |
* Returns the contents of @bprm on success, NULL otherwise. |
26 |
*/ |
27 |
static char *ccs_print_bprm(struct linux_binprm *bprm) |
28 |
{ |
29 |
static const int ccs_buffer_len = 4096 * 2; |
30 |
char *buffer = ccs_alloc(ccs_buffer_len, false); |
31 |
char *cp; |
32 |
char *last_start; |
33 |
int len; |
34 |
unsigned long pos = bprm->p; |
35 |
int offset = pos % PAGE_SIZE; |
36 |
int argv_count = bprm->argc; |
37 |
int envp_count = bprm->envc; |
38 |
bool truncated = false; |
39 |
if (!buffer) |
40 |
return NULL; |
41 |
len = snprintf(buffer, ccs_buffer_len - 1, |
42 |
"argc=%d envc=%d argv[]={ ", argv_count, envp_count); |
43 |
cp = buffer + len; |
44 |
if (!argv_count) { |
45 |
memmove(cp, "} envp[]={ ", 11); |
46 |
cp += 11; |
47 |
} |
48 |
last_start = cp; |
49 |
while (argv_count || envp_count) { |
50 |
struct page *page = ccs_get_arg_page(bprm, pos); |
51 |
const char *kaddr; |
52 |
if (!page) |
53 |
goto out; |
54 |
pos += PAGE_SIZE - offset; |
55 |
/* Map */ |
56 |
kaddr = kmap(page); |
57 |
/* Read. */ |
58 |
while (offset < PAGE_SIZE) { |
59 |
const unsigned char c = kaddr[offset++]; |
60 |
if (cp == last_start) |
61 |
*cp++ = '"'; |
62 |
if (cp >= buffer + ccs_buffer_len - 32) { |
63 |
/* Reserve some room for "..." string. */ |
64 |
truncated = true; |
65 |
} else if (c == '\\') { |
66 |
*cp++ = '\\'; |
67 |
*cp++ = '\\'; |
68 |
} else if (c > ' ' && c < 127) { |
69 |
*cp++ = c; |
70 |
} else if (!c) { |
71 |
*cp++ = '"'; |
72 |
*cp++ = ' '; |
73 |
last_start = cp; |
74 |
} else { |
75 |
*cp++ = '\\'; |
76 |
*cp++ = (c >> 6) + '0'; |
77 |
*cp++ = ((c >> 3) & 7) + '0'; |
78 |
*cp++ = (c & 7) + '0'; |
79 |
} |
80 |
if (c) |
81 |
continue; |
82 |
if (argv_count) { |
83 |
if (--argv_count == 0) { |
84 |
if (truncated) { |
85 |
cp = last_start; |
86 |
memmove(cp, "... ", 4); |
87 |
cp += 4; |
88 |
} |
89 |
memmove(cp, "} envp[]={ ", 11); |
90 |
cp += 11; |
91 |
last_start = cp; |
92 |
truncated = false; |
93 |
} |
94 |
} else if (envp_count) { |
95 |
if (--envp_count == 0) { |
96 |
if (truncated) { |
97 |
cp = last_start; |
98 |
memmove(cp, "... ", 4); |
99 |
cp += 4; |
100 |
} |
101 |
} |
102 |
} |
103 |
if (!argv_count && !envp_count) |
104 |
break; |
105 |
} |
106 |
/* Unmap. */ |
107 |
kunmap(page); |
108 |
ccs_put_arg_page(page); |
109 |
offset = 0; |
110 |
} |
111 |
*cp++ = '}'; |
112 |
*cp = '\0'; |
113 |
return buffer; |
114 |
out: |
115 |
snprintf(buffer, ccs_buffer_len - 1, |
116 |
"argc=%d envc=%d argv[]={ ... } envp[]= { ... }", |
117 |
argv_count, envp_count); |
118 |
return buffer; |
119 |
} |
120 |
|
121 |
static DECLARE_WAIT_QUEUE_HEAD(ccs_grant_log_wait); |
122 |
static DECLARE_WAIT_QUEUE_HEAD(ccs_reject_log_wait); |
123 |
|
124 |
static DEFINE_SPINLOCK(ccs_audit_log_lock); |
125 |
|
126 |
/* Structure for audit log. */ |
127 |
struct ccs_log_entry { |
128 |
struct list_head list; |
129 |
char *log; |
130 |
}; |
131 |
|
132 |
/* The list for "struct ccs_log_entry". */ |
133 |
static LIST_HEAD(ccs_grant_log); |
134 |
|
135 |
/* The list for "struct ccs_log_entry". */ |
136 |
static LIST_HEAD(ccs_reject_log); |
137 |
|
138 |
static int ccs_grant_log_count; |
139 |
static int ccs_reject_log_count; |
140 |
|
141 |
/** |
142 |
* ccs_init_audit_log - Allocate buffer for audit logs. |
143 |
* |
144 |
* @len: Required size. |
145 |
* @r: Pointer to "struct ccs_request_info". |
146 |
* |
147 |
* Returns pointer to allocated memory. |
148 |
* |
149 |
* The @len is updated to add the header lines' size on success. |
150 |
*/ |
151 |
char *ccs_init_audit_log(int *len, struct ccs_request_info *r) |
152 |
{ |
153 |
static const char *ccs_mode_4[4] = { |
154 |
"disabled", "learning", "permissive", "enforcing" |
155 |
}; |
156 |
char *buf; |
157 |
char *bprm_info = ""; |
158 |
struct timeval tv; |
159 |
u32 tomoyo_flags = current->tomoyo_flags; |
160 |
const char *domainname; |
161 |
if (!r->domain) |
162 |
r->domain = current->domain_info; |
163 |
domainname = r->domain->domainname->name; |
164 |
do_gettimeofday(&tv); |
165 |
*len += strlen(domainname) + 256; |
166 |
if (r->bprm) { |
167 |
bprm_info = ccs_print_bprm(r->bprm); |
168 |
if (!bprm_info) |
169 |
return NULL; |
170 |
*len += strlen(bprm_info); |
171 |
} |
172 |
buf = ccs_alloc(*len, true); |
173 |
if (buf) |
174 |
snprintf(buf, (*len) - 1, |
175 |
"#timestamp=%lu profile=%u mode=%s pid=%d uid=%d " |
176 |
"gid=%d euid=%d egid=%d suid=%d sgid=%d fsuid=%d " |
177 |
"fsgid=%d state[0]=%u state[1]=%u state[2]=%u %s\n" |
178 |
"%s\n", |
179 |
tv.tv_sec, r->profile, ccs_mode_4[r->mode], |
180 |
(pid_t) sys_getpid(), current_uid(), current_gid(), |
181 |
current_euid(), current_egid(), current_suid(), |
182 |
current_sgid(), current_fsuid(), current_fsgid(), |
183 |
(u8) (tomoyo_flags >> 24), (u8) (tomoyo_flags >> 16), |
184 |
(u8) (tomoyo_flags >> 8), bprm_info, domainname); |
185 |
if (r->bprm) |
186 |
ccs_free(bprm_info); |
187 |
return buf; |
188 |
} |
189 |
|
190 |
/** |
191 |
* ccs_can_save_audit_log - Check whether the kernel can save new audit log. |
192 |
* |
193 |
* @domain: Pointer to "struct domain_info". NULL for current->domain_info. |
194 |
* @is_granted: True if this is a granted log. |
195 |
* |
196 |
* Returns true if the kernel can save, false otherwise. |
197 |
*/ |
198 |
static bool ccs_can_save_audit_log(const struct domain_info *domain, |
199 |
const bool is_granted) |
200 |
{ |
201 |
if (is_granted) |
202 |
return ccs_grant_log_count |
203 |
< ccs_check_flags(domain, CCS_TOMOYO_MAX_GRANT_LOG); |
204 |
return ccs_reject_log_count |
205 |
< ccs_check_flags(domain, CCS_TOMOYO_MAX_REJECT_LOG); |
206 |
} |
207 |
|
208 |
/** |
209 |
* ccs_write_audit_log - Write audit log. |
210 |
* |
211 |
* @is_granted: True if this is a granted log. |
212 |
* @r: Pointer to "struct ccs_request_info". |
213 |
* @fmt: The printf()'s format string, followed by parameters. |
214 |
* |
215 |
* Returns 0 on success, -ENOMEM otherwise. |
216 |
*/ |
217 |
int ccs_write_audit_log(const bool is_granted, struct ccs_request_info *r, |
218 |
const char *fmt, ...) |
219 |
{ |
220 |
va_list args; |
221 |
int error = -ENOMEM; |
222 |
int pos; |
223 |
int len; |
224 |
char *buf; |
225 |
struct ccs_log_entry *new_entry; |
226 |
const struct ccs_condition_list *ptr; |
227 |
struct task_struct *task = current; |
228 |
if (!r->domain) |
229 |
r->domain = task->domain_info; |
230 |
if (!ccs_can_save_audit_log(r->domain, is_granted)) |
231 |
goto out; |
232 |
va_start(args, fmt); |
233 |
len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; |
234 |
va_end(args); |
235 |
buf = ccs_init_audit_log(&len, r); |
236 |
if (!buf) |
237 |
goto out; |
238 |
pos = strlen(buf); |
239 |
va_start(args, fmt); |
240 |
vsnprintf(buf + pos, len - pos - 1, fmt, args); |
241 |
va_end(args); |
242 |
new_entry = ccs_alloc(sizeof(*new_entry), true); |
243 |
if (!new_entry) { |
244 |
ccs_free(buf); |
245 |
goto out; |
246 |
} |
247 |
new_entry->log = buf; |
248 |
/***** CRITICAL SECTION START *****/ |
249 |
spin_lock(&ccs_audit_log_lock); |
250 |
if (is_granted) { |
251 |
list_add_tail(&new_entry->list, &ccs_grant_log); |
252 |
ccs_grant_log_count++; |
253 |
ccs_update_counter(CCS_UPDATES_COUNTER_GRANT_LOG); |
254 |
} else { |
255 |
list_add_tail(&new_entry->list, &ccs_reject_log); |
256 |
ccs_reject_log_count++; |
257 |
ccs_update_counter(CCS_UPDATES_COUNTER_REJECT_LOG); |
258 |
} |
259 |
spin_unlock(&ccs_audit_log_lock); |
260 |
/***** CRITICAL SECTION END *****/ |
261 |
if (is_granted) |
262 |
wake_up(&ccs_grant_log_wait); |
263 |
else |
264 |
wake_up(&ccs_reject_log_wait); |
265 |
error = 0; |
266 |
out: |
267 |
/* |
268 |
* Update task's state. |
269 |
* |
270 |
* Don't change the lowest byte because it is reserved for |
271 |
* TOMOYO_CHECK_READ_FOR_OPEN_EXEC / CCS_DONT_SLEEP_ON_ENFORCE_ERROR / |
272 |
* TOMOYO_TASK_IS_EXECUTE_HANDLER. |
273 |
*/ |
274 |
ptr = r->cond; |
275 |
if (ptr) { |
276 |
const u8 flags = ptr->post_state[3]; |
277 |
u32 tomoyo_flags = task->tomoyo_flags; |
278 |
if (flags & 1) { |
279 |
tomoyo_flags &= ~0xFF000000; |
280 |
tomoyo_flags |= ptr->post_state[0] << 24; |
281 |
} |
282 |
if (flags & 2) { |
283 |
tomoyo_flags &= ~0x00FF0000; |
284 |
tomoyo_flags |= ptr->post_state[1] << 16; |
285 |
} |
286 |
if (flags & 4) { |
287 |
tomoyo_flags &= ~0x0000FF00; |
288 |
tomoyo_flags |= ptr->post_state[2] << 8; |
289 |
} |
290 |
task->tomoyo_flags = tomoyo_flags; |
291 |
r->cond = NULL; |
292 |
} |
293 |
return error; |
294 |
} |
295 |
|
296 |
/** |
297 |
* ccs_read_grant_log - Read a grant log. |
298 |
* |
299 |
* @head: Pointer to "struct ccs_io_buffer". |
300 |
* |
301 |
* Returns 0. |
302 |
*/ |
303 |
int ccs_read_grant_log(struct ccs_io_buffer *head) |
304 |
{ |
305 |
struct ccs_log_entry *ptr = NULL; |
306 |
if (head->read_avail) |
307 |
return 0; |
308 |
if (head->read_buf) { |
309 |
ccs_free(head->read_buf); |
310 |
head->read_buf = NULL; |
311 |
head->readbuf_size = 0; |
312 |
} |
313 |
/***** CRITICAL SECTION START *****/ |
314 |
spin_lock(&ccs_audit_log_lock); |
315 |
if (!list_empty(&ccs_grant_log)) { |
316 |
ptr = list_entry(ccs_grant_log.next, struct ccs_log_entry, |
317 |
list); |
318 |
list_del(&ptr->list); |
319 |
ccs_grant_log_count--; |
320 |
} |
321 |
spin_unlock(&ccs_audit_log_lock); |
322 |
/***** CRITICAL SECTION END *****/ |
323 |
if (ptr) { |
324 |
head->read_buf = ptr->log; |
325 |
head->read_avail = strlen(ptr->log) + 1; |
326 |
head->readbuf_size = head->read_avail; |
327 |
ccs_free(ptr); |
328 |
} |
329 |
return 0; |
330 |
} |
331 |
|
332 |
/** |
333 |
* ccs_poll_grant_log - Wait for a grant log. |
334 |
* |
335 |
* @file: Pointer to "struct file". |
336 |
* @wait: Pointer to "poll_table". |
337 |
* |
338 |
* Returns POLLIN | POLLRDNORM when ready to read a grant log. |
339 |
*/ |
340 |
int ccs_poll_grant_log(struct file *file, poll_table *wait) |
341 |
{ |
342 |
if (ccs_grant_log_count) |
343 |
return POLLIN | POLLRDNORM; |
344 |
poll_wait(file, &ccs_grant_log_wait, wait); |
345 |
if (ccs_grant_log_count) |
346 |
return POLLIN | POLLRDNORM; |
347 |
return 0; |
348 |
} |
349 |
|
350 |
/** |
351 |
* ccs_read_reject_log - Read a reject log. |
352 |
* |
353 |
* @head: Pointer to "struct ccs_io_buffer". |
354 |
* |
355 |
* Returns 0. |
356 |
*/ |
357 |
int ccs_read_reject_log(struct ccs_io_buffer *head) |
358 |
{ |
359 |
struct ccs_log_entry *ptr = NULL; |
360 |
if (head->read_avail) |
361 |
return 0; |
362 |
if (head->read_buf) { |
363 |
ccs_free(head->read_buf); |
364 |
head->read_buf = NULL; |
365 |
head->readbuf_size = 0; |
366 |
} |
367 |
/***** CRITICAL SECTION START *****/ |
368 |
spin_lock(&ccs_audit_log_lock); |
369 |
if (!list_empty(&ccs_reject_log)) { |
370 |
ptr = list_entry(ccs_reject_log.next, struct ccs_log_entry, |
371 |
list); |
372 |
list_del(&ptr->list); |
373 |
ccs_reject_log_count--; |
374 |
} |
375 |
spin_unlock(&ccs_audit_log_lock); |
376 |
/***** CRITICAL SECTION END *****/ |
377 |
if (ptr) { |
378 |
head->read_buf = ptr->log; |
379 |
head->read_avail = strlen(ptr->log) + 1; |
380 |
head->readbuf_size = head->read_avail; |
381 |
ccs_free(ptr); |
382 |
} |
383 |
return 0; |
384 |
} |
385 |
|
386 |
/** |
387 |
* ccs_poll_reject_log - Wait for a reject log. |
388 |
* |
389 |
* @file: Pointer to "struct file". |
390 |
* @wait: Pointer to "poll_table". |
391 |
* |
392 |
* Returns POLLIN | POLLRDNORM when ready to read a reject log. |
393 |
*/ |
394 |
int ccs_poll_reject_log(struct file *file, poll_table *wait) |
395 |
{ |
396 |
if (ccs_reject_log_count) |
397 |
return POLLIN | POLLRDNORM; |
398 |
poll_wait(file, &ccs_reject_log_wait, wait); |
399 |
if (ccs_reject_log_count) |
400 |
return POLLIN | POLLRDNORM; |
401 |
return 0; |
402 |
} |