3 |
* |
* |
4 |
* Copyright (C) 2005-2010 NTT DATA CORPORATION |
* Copyright (C) 2005-2010 NTT DATA CORPORATION |
5 |
* |
* |
6 |
* Version: 1.8.0-pre 2010/09/01 |
* Version: 1.8.0-pre 2010/10/05 |
7 |
* |
* |
8 |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
* This file is applicable to both 2.4.30 and 2.6.11 and later. |
9 |
* See README.ccs for ChangeLog. |
* See README.ccs for ChangeLog. |
12 |
|
|
13 |
#include "internal.h" |
#include "internal.h" |
14 |
|
|
|
struct ccs_preference ccs_preference = { |
|
|
#ifdef CONFIG_CCSECURITY_AUDIT |
|
|
.audit_max_grant_log = CONFIG_CCSECURITY_MAX_GRANT_LOG, |
|
|
.audit_max_reject_log = CONFIG_CCSECURITY_MAX_REJECT_LOG, |
|
|
#endif |
|
|
.audit_task_info = true, |
|
|
.audit_path_info = true, |
|
|
.enforcing_penalty = 0, |
|
|
.enforcing_verbose = true, |
|
|
.learning_max_entry = CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY, |
|
|
.learning_verbose = false, |
|
|
.learning_exec_realpath = true, |
|
|
.learning_exec_argv0 = true, |
|
|
.learning_symlink_target = true, |
|
|
.permissive_verbose = true, |
|
|
}; |
|
|
|
|
15 |
/* Profile version. Currently only 20100903 is defined. */ |
/* Profile version. Currently only 20100903 is defined. */ |
16 |
static unsigned int ccs_profile_version; |
static unsigned int ccs_profile_version; |
17 |
|
|
44 |
[CCS_MAC_FILE_LINK] = "link", |
[CCS_MAC_FILE_LINK] = "link", |
45 |
[CCS_MAC_FILE_RENAME] = "rename", |
[CCS_MAC_FILE_RENAME] = "rename", |
46 |
[CCS_MAC_FILE_CHMOD] = "chmod", |
[CCS_MAC_FILE_CHMOD] = "chmod", |
47 |
[CCS_MAC_FILE_CHOWN] = "chown", |
[CCS_MAC_FILE_CHOWN] = "chown", |
48 |
[CCS_MAC_FILE_CHGRP] = "chgrp", |
[CCS_MAC_FILE_CHGRP] = "chgrp", |
49 |
[CCS_MAC_FILE_IOCTL] = "ioctl", |
[CCS_MAC_FILE_IOCTL] = "ioctl", |
50 |
[CCS_MAC_FILE_CHROOT] = "chroot", |
[CCS_MAC_FILE_CHROOT] = "chroot", |
176 |
[CCS_PATH2_PARENT_PERM] = "path2.parent.perm", |
[CCS_PATH2_PARENT_PERM] = "path2.parent.perm", |
177 |
}; |
}; |
178 |
|
|
179 |
|
static const char * const ccs_pref_keywords[CCS_MAX_PREF] = { |
180 |
|
[CCS_PREF_MAX_GRANT_LOG] = "max_grant_log", |
181 |
|
[CCS_PREF_MAX_REJECT_LOG] = "max_reject_log", |
182 |
|
[CCS_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry", |
183 |
|
[CCS_PREF_ENFORCING_PENALTY] = "enforcing_penalty", |
184 |
|
}; |
185 |
|
|
186 |
/* Permit policy management by non-root user? */ |
/* Permit policy management by non-root user? */ |
187 |
static bool ccs_manage_by_non_root; |
static bool ccs_manage_by_non_root; |
188 |
|
|
197 |
} |
} |
198 |
|
|
199 |
static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) |
static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) |
200 |
|
__attribute__ ((format(printf, 3, 4))); |
201 |
|
|
202 |
|
static void ccs_addprintf(char *buffer, int len, const char *fmt, ...) |
203 |
{ |
{ |
204 |
va_list args; |
va_list args; |
205 |
const int pos = strlen(buffer); |
const int pos = strlen(buffer); |
332 |
CCS_CONFIG_WANT_GRANT_LOG | CCS_CONFIG_WANT_REJECT_LOG; |
CCS_CONFIG_WANT_GRANT_LOG | CCS_CONFIG_WANT_REJECT_LOG; |
333 |
memset(ptr->config, CCS_CONFIG_USE_DEFAULT, |
memset(ptr->config, CCS_CONFIG_USE_DEFAULT, |
334 |
sizeof(ptr->config)); |
sizeof(ptr->config)); |
335 |
|
ptr->pref[CCS_PREF_MAX_GRANT_LOG] = |
336 |
|
CONFIG_CCSECURITY_MAX_GRANT_LOG; |
337 |
|
ptr->pref[CCS_PREF_MAX_REJECT_LOG] = |
338 |
|
CONFIG_CCSECURITY_MAX_REJECT_LOG; |
339 |
|
ptr->pref[CCS_PREF_MAX_LEARNING_ENTRY] = |
340 |
|
CONFIG_CCSECURITY_MAX_ACCEPT_ENTRY; |
341 |
mb(); /* Avoid out-of-order execution. */ |
mb(); /* Avoid out-of-order execution. */ |
342 |
ccs_profile_ptr[profile] = ptr; |
ccs_profile_ptr[profile] = ptr; |
343 |
entry = NULL; |
entry = NULL; |
344 |
} |
} |
345 |
mutex_unlock(&ccs_policy_lock); |
mutex_unlock(&ccs_policy_lock); |
346 |
out: |
out: |
347 |
kfree(entry); |
kfree(entry); |
348 |
return ptr; |
return ptr; |
349 |
} |
} |
356 |
struct ccs_domain_info *domain; |
struct ccs_domain_info *domain; |
357 |
const int idx = ccs_read_lock(); |
const int idx = ccs_read_lock(); |
358 |
ccs_policy_loaded = true; |
ccs_policy_loaded = true; |
359 |
list_for_each_entry_rcu(domain, &ccs_domain_list, list) { |
list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { |
360 |
const u8 profile = domain->profile; |
const u8 profile = domain->profile; |
361 |
if (ccs_profile_ptr[profile]) |
if (ccs_profile_ptr[profile]) |
362 |
continue; |
continue; |
367 |
if (ccs_profile_version != 20100903) |
if (ccs_profile_version != 20100903) |
368 |
panic("Profile version %u is not supported.\n", |
panic("Profile version %u is not supported.\n", |
369 |
ccs_profile_version); |
ccs_profile_version); |
370 |
printk(KERN_INFO "CCSecurity: 1.8.0-pre 2010/09/01\n"); |
printk(KERN_INFO "CCSecurity: 1.8.0-pre 2010/10/05\n"); |
371 |
printk(KERN_INFO "Mandatory Access Control activated.\n"); |
printk(KERN_INFO "Mandatory Access Control activated.\n"); |
372 |
} |
} |
373 |
|
|
400 |
return -1; |
return -1; |
401 |
} |
} |
402 |
|
|
|
static void ccs_set_bool(bool *b, const char *string, const char *find) |
|
|
{ |
|
|
switch (ccs_find_yesno(string, find)) { |
|
|
case 1: |
|
|
*b = true; |
|
|
break; |
|
|
case 0: |
|
|
*b = false; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
403 |
static void ccs_set_uint(unsigned int *i, const char *string, const char *find) |
static void ccs_set_uint(unsigned int *i, const char *string, const char *find) |
404 |
{ |
{ |
405 |
const char *cp = strstr(string, find); |
const char *cp = strstr(string, find); |
407 |
sscanf(cp + strlen(find), "=%u", i); |
sscanf(cp + strlen(find), "=%u", i); |
408 |
} |
} |
409 |
|
|
|
static int ccs_set_pref(char *data) |
|
|
{ |
|
|
if (ccs_str_starts(&data, "audit")) { |
|
|
#ifdef CONFIG_CCSECURITY_AUDIT |
|
|
ccs_set_uint(&ccs_preference.audit_max_grant_log, data, |
|
|
"max_grant_log"); |
|
|
ccs_set_uint(&ccs_preference.audit_max_reject_log, data, |
|
|
"max_reject_log"); |
|
|
#endif |
|
|
ccs_set_bool(&ccs_preference.audit_task_info, data, |
|
|
"task_info"); |
|
|
ccs_set_bool(&ccs_preference.audit_path_info, data, |
|
|
"path_info"); |
|
|
} else if (ccs_str_starts(&data, "enforcing")) { |
|
|
ccs_set_bool(&ccs_preference.enforcing_verbose, data, |
|
|
"verbose"); |
|
|
ccs_set_uint(&ccs_preference.enforcing_penalty, data, |
|
|
"penalty"); |
|
|
} else if (ccs_str_starts(&data, "permissive")) { |
|
|
ccs_set_bool(&ccs_preference.permissive_verbose, data, |
|
|
"verbose"); |
|
|
} else if (ccs_str_starts(&data, "learning")) { |
|
|
ccs_set_bool(&ccs_preference.learning_verbose, data, |
|
|
"verbose"); |
|
|
ccs_set_uint(&ccs_preference.learning_max_entry, data, |
|
|
"max_entry"); |
|
|
ccs_set_bool(&ccs_preference.learning_exec_realpath, data, |
|
|
ccs_condition_keyword[CCS_EXEC_REALPATH]); |
|
|
ccs_set_bool(&ccs_preference.learning_exec_argv0, data, |
|
|
"exec.argv0"); |
|
|
ccs_set_bool(&ccs_preference.learning_symlink_target, data, |
|
|
ccs_condition_keyword[CCS_SYMLINK_TARGET]); |
|
|
} else |
|
|
return -EINVAL; |
|
|
return 0; |
|
|
} |
|
|
|
|
410 |
static int ccs_set_mode(char *name, const char *value, |
static int ccs_set_mode(char *name, const char *value, |
411 |
struct ccs_profile *profile) |
struct ccs_profile *profile) |
412 |
{ |
{ |
422 |
int len = 0; |
int len = 0; |
423 |
if (i < CCS_MAX_MAC_INDEX) { |
if (i < CCS_MAX_MAC_INDEX) { |
424 |
const u8 c = ccs_index2category[i]; |
const u8 c = ccs_index2category[i]; |
425 |
const char *category = ccs_category_keywords[c]; |
const char *category = |
426 |
|
ccs_category_keywords[c]; |
427 |
len = strlen(category); |
len = strlen(category); |
428 |
if (strncmp(name, category, len) || |
if (strncmp(name, category, len) || |
429 |
name[len++] != ':' || name[len++] != ':') |
name[len++] != ':' || name[len++] != ':') |
450 |
* 'config' from 'CCS_CONFIG_USE_DEAFULT'. |
* 'config' from 'CCS_CONFIG_USE_DEAFULT'. |
451 |
*/ |
*/ |
452 |
config = (config & ~7) | mode; |
config = (config & ~7) | mode; |
|
#ifdef CONFIG_CCSECURITY_AUDIT |
|
453 |
if (config != CCS_CONFIG_USE_DEFAULT) { |
if (config != CCS_CONFIG_USE_DEFAULT) { |
454 |
switch (ccs_find_yesno(value, "grant_log")) { |
switch (ccs_find_yesno(value, "grant_log")) { |
455 |
case 1: |
case 1: |
468 |
break; |
break; |
469 |
} |
} |
470 |
} |
} |
|
#endif |
|
471 |
} |
} |
472 |
if (i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) |
if (i < CCS_MAX_MAC_INDEX + CCS_MAX_MAC_CATEGORY_INDEX) |
473 |
profile->config[i] = config; |
profile->config[i] = config; |
491 |
struct ccs_profile *profile; |
struct ccs_profile *profile; |
492 |
if (sscanf(data, "PROFILE_VERSION=%u", &ccs_profile_version) == 1) |
if (sscanf(data, "PROFILE_VERSION=%u", &ccs_profile_version) == 1) |
493 |
return 0; |
return 0; |
|
if (ccs_str_starts(&data, "PREFERENCE::")) |
|
|
return ccs_set_pref(data); |
|
494 |
i = simple_strtoul(data, &cp, 10); |
i = simple_strtoul(data, &cp, 10); |
495 |
if (*cp != '-') |
if (*cp != '-') |
496 |
return -EINVAL; |
return -EINVAL; |
508 |
ccs_put_name(old_comment); |
ccs_put_name(old_comment); |
509 |
return 0; |
return 0; |
510 |
} |
} |
511 |
|
if (!strcmp(data, "PREFERENCE")) { |
512 |
|
for (i = 0; i < CCS_MAX_PREF; i++) |
513 |
|
ccs_set_uint(&profile->pref[i], cp, |
514 |
|
ccs_pref_keywords[i]); |
515 |
|
return 0; |
516 |
|
} |
517 |
return ccs_set_mode(data, cp, profile); |
return ccs_set_mode(data, cp, profile); |
518 |
} |
} |
519 |
|
|
|
static void ccs_print_preference(struct ccs_io_buffer *head) |
|
|
{ |
|
|
ccs_io_printf(head, "PREFERENCE::%s={ " |
|
|
#ifdef CONFIG_CCSECURITY_AUDIT |
|
|
"max_grant_log=%u max_reject_log=%u " |
|
|
#endif |
|
|
"task_info=%s path_info=%s }\n", "audit", |
|
|
#ifdef CONFIG_CCSECURITY_AUDIT |
|
|
ccs_preference.audit_max_grant_log, |
|
|
ccs_preference.audit_max_reject_log, |
|
|
#endif |
|
|
ccs_yesno(ccs_preference.audit_task_info), |
|
|
ccs_yesno(ccs_preference.audit_path_info)); |
|
|
ccs_io_printf(head, "PREFERENCE::%s={ verbose=%s max_entry=%u " |
|
|
"exec.realpath=%s exec.argv0=%s symlink.target=%s }\n", |
|
|
"learning", ccs_yesno(ccs_preference.learning_verbose), |
|
|
ccs_preference.learning_max_entry, |
|
|
ccs_yesno(ccs_preference.learning_exec_realpath), |
|
|
ccs_yesno(ccs_preference.learning_exec_argv0), |
|
|
ccs_yesno(ccs_preference.learning_symlink_target)); |
|
|
ccs_io_printf(head, "PREFERENCE::%s={ verbose=%s }\n", "permissive", |
|
|
ccs_yesno(ccs_preference.permissive_verbose)); |
|
|
ccs_io_printf(head, "PREFERENCE::%s={ verbose=%s penalty=%u }\n", |
|
|
"enforcing", ccs_yesno(ccs_preference.enforcing_verbose), |
|
|
ccs_preference.enforcing_penalty); |
|
|
} |
|
|
|
|
520 |
static void ccs_print_config(struct ccs_io_buffer *head, const u8 config) |
static void ccs_print_config(struct ccs_io_buffer *head, const u8 config) |
521 |
{ |
{ |
522 |
ccs_io_printf(head, "={ mode=%s", ccs_mode[config & 3]); |
ccs_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", |
523 |
#ifdef CONFIG_CCSECURITY_AUDIT |
ccs_mode[config & 3], |
|
ccs_io_printf(head, " grant_log=%s reject_log=%s", |
|
524 |
ccs_yesno(config & CCS_CONFIG_WANT_GRANT_LOG), |
ccs_yesno(config & CCS_CONFIG_WANT_GRANT_LOG), |
525 |
ccs_yesno(config & CCS_CONFIG_WANT_REJECT_LOG)); |
ccs_yesno(config & CCS_CONFIG_WANT_REJECT_LOG)); |
|
#endif |
|
|
ccs_set_string(head, " }\n"); |
|
526 |
} |
} |
527 |
|
|
528 |
/** |
/** |
534 |
{ |
{ |
535 |
u8 index; |
u8 index; |
536 |
const struct ccs_profile *profile; |
const struct ccs_profile *profile; |
537 |
next: |
next: |
538 |
index = head->r.index; |
index = head->r.index; |
539 |
profile = ccs_profile_ptr[index]; |
profile = ccs_profile_ptr[index]; |
540 |
switch (head->r.step) { |
switch (head->r.step) { |
541 |
case 0: |
case 0: |
542 |
ccs_io_printf(head, "PROFILE_VERSION=%s\n", "20100903"); |
ccs_io_printf(head, "PROFILE_VERSION=%s\n", "20100903"); |
|
ccs_print_preference(head); |
|
543 |
head->r.step++; |
head->r.step++; |
544 |
break; |
break; |
545 |
case 1: |
case 1: |
553 |
break; |
break; |
554 |
case 2: |
case 2: |
555 |
{ |
{ |
556 |
|
u8 i; |
557 |
const struct ccs_path_info *comment = profile->comment; |
const struct ccs_path_info *comment = profile->comment; |
558 |
ccs_io_printf(head, "%u-COMMENT=", index); |
ccs_io_printf(head, "%u-COMMENT=", index); |
559 |
ccs_set_string(head, comment ? comment->name : ""); |
ccs_set_string(head, comment ? comment->name : ""); |
560 |
ccs_set_lf(head); |
ccs_set_lf(head); |
561 |
|
ccs_io_printf(head, "%u-PREFERENCE={ ", index); |
562 |
|
for (i = 0; i < CCS_MAX_PREF; i++) |
563 |
|
ccs_io_printf(head, "%s=%u ", |
564 |
|
ccs_pref_keywords[i], |
565 |
|
profile->pref[i]); |
566 |
|
ccs_set_string(head, " }\n"); |
567 |
head->r.step++; |
head->r.step++; |
568 |
} |
} |
569 |
break; |
break; |
696 |
{ |
{ |
697 |
struct ccs_manager *ptr; |
struct ccs_manager *ptr; |
698 |
const char *exe; |
const char *exe; |
699 |
struct task_struct *task = current; |
struct ccs_security *task = ccs_current_security(); |
700 |
const struct ccs_path_info *domainname |
const struct ccs_path_info *domainname |
701 |
= ccs_current_domain()->domainname; |
= ccs_current_domain()->domainname; |
702 |
bool found = false; |
bool found = false; |
707 |
if (!ccs_manage_by_non_root && (current_uid() || current_euid())) |
if (!ccs_manage_by_non_root && (current_uid() || current_euid())) |
708 |
return false; |
return false; |
709 |
exe = ccs_get_exe(); |
exe = ccs_get_exe(); |
710 |
list_for_each_entry_rcu(ptr, &ccs_policy_list[CCS_ID_MANAGER], |
list_for_each_entry_srcu(ptr, &ccs_policy_list[CCS_ID_MANAGER], |
711 |
head.list) { |
head.list, &ccs_ss) { |
712 |
if (ptr->head.is_deleted) |
if (ptr->head.is_deleted) |
713 |
continue; |
continue; |
714 |
if (ptr->is_domain) { |
if (ptr->is_domain) { |
1198 |
goto print_cond_part; |
goto print_cond_part; |
1199 |
if (acl->is_deleted) |
if (acl->is_deleted) |
1200 |
return true; |
return true; |
1201 |
next: |
next: |
1202 |
bit = head->r.bit; |
bit = head->r.bit; |
1203 |
if (!ccs_flush(head)) |
if (!ccs_flush(head)) |
1204 |
return false; |
return false; |
1357 |
head->r.cond_step = 0; |
head->r.cond_step = 0; |
1358 |
if (!ccs_flush(head)) |
if (!ccs_flush(head)) |
1359 |
return false; |
return false; |
1360 |
print_cond_part: |
print_cond_part: |
1361 |
if (!ccs_print_condition(head, acl->cond)) |
if (!ccs_print_condition(head, acl->cond)) |
1362 |
return false; |
return false; |
1363 |
head->r.print_cond_part = false; |
head->r.print_cond_part = false; |
1373 |
case CCS_TYPE_UNIX_ACL: |
case CCS_TYPE_UNIX_ACL: |
1374 |
goto next; |
goto next; |
1375 |
} |
} |
1376 |
done: |
done: |
1377 |
head->r.bit = 0; |
head->r.bit = 0; |
1378 |
return true; |
return true; |
1379 |
} |
} |
1453 |
goto done; |
goto done; |
1454 |
} |
} |
1455 |
} |
} |
1456 |
done: |
done: |
1457 |
head->r.eof = true; |
head->r.eof = true; |
1458 |
} |
} |
1459 |
|
|
1578 |
#endif |
#endif |
1579 |
if (p) { |
if (p) { |
1580 |
domain = ccs_task_domain(p); |
domain = ccs_task_domain(p); |
1581 |
ccs_flags = p->ccs_flags; |
ccs_flags = ccs_task_flags(p); |
1582 |
} |
} |
1583 |
ccs_tasklist_unlock(); |
ccs_tasklist_unlock(); |
1584 |
if (!domain) |
if (!domain) |
1623 |
static const struct { |
static const struct { |
1624 |
const char *keyword; |
const char *keyword; |
1625 |
int (*write) (char *, const bool); |
int (*write) (char *, const bool); |
1626 |
} ccs_callback[3] = { |
} ccs_callback[2] = { |
1627 |
{ "aggregator ", ccs_write_aggregator }, |
{ "aggregator ", ccs_write_aggregator }, |
|
{ "file_pattern ", ccs_write_pattern }, |
|
1628 |
{ "deny_autobind ", ccs_write_reserved_port }, |
{ "deny_autobind ", ccs_write_reserved_port }, |
1629 |
}; |
}; |
1630 |
for (i = 0; i < 3; i++) |
for (i = 0; i < 2; i++) |
1631 |
if (ccs_str_starts(&data, ccs_callback[i].keyword)) |
if (ccs_str_starts(&data, ccs_callback[i].keyword)) |
1632 |
return ccs_callback[i].write(data, is_delete); |
return ccs_callback[i].write(data, is_delete); |
1633 |
for (i = 0; i < CCS_MAX_TRANSITION_TYPE; i++) |
for (i = 0; i < CCS_MAX_TRANSITION_TYPE; i++) |
1682 |
head)->member_name->name); |
head)->member_name->name); |
1683 |
} else if (idx == CCS_NUMBER_GROUP) { |
} else if (idx == CCS_NUMBER_GROUP) { |
1684 |
ccs_print_number_union(head, &container_of |
ccs_print_number_union(head, &container_of |
1685 |
(ptr, struct ccs_number_group, |
(ptr, struct ccs_number_group, |
1686 |
head)->number); |
head)->number); |
1687 |
} else if (idx == CCS_ADDRESS_GROUP) { |
} else if (idx == CCS_ADDRESS_GROUP) { |
1688 |
char buffer[128]; |
char buffer[128]; |
1689 |
struct ccs_address_group *member = |
struct ccs_address_group *member = |
1751 |
ptr->aggregated_name->name); |
ptr->aggregated_name->name); |
1752 |
} |
} |
1753 |
break; |
break; |
|
case CCS_ID_PATTERN: |
|
|
{ |
|
|
struct ccs_pattern *ptr = |
|
|
container_of(acl, typeof(*ptr), head); |
|
|
ccs_set_string(head, "file_pattern "); |
|
|
ccs_set_string(head, ptr->pattern->name); |
|
|
} |
|
|
break; |
|
1754 |
case CCS_ID_RESERVEDPORT: |
case CCS_ID_RESERVEDPORT: |
1755 |
{ |
{ |
1756 |
struct ccs_reserved *ptr = |
struct ccs_reserved *ptr = |
1808 |
|
|
1809 |
/* Wait queue for ccs_query_list. */ |
/* Wait queue for ccs_query_list. */ |
1810 |
static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); |
static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); |
1811 |
|
static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); |
1812 |
|
|
1813 |
/* Lock for manipulating ccs_query_list. */ |
/* Lock for manipulating ccs_query_list. */ |
1814 |
static DEFINE_SPINLOCK(ccs_query_list_lock); |
static DEFINE_SPINLOCK(ccs_query_list_lock); |
1821 |
unsigned int serial; |
unsigned int serial; |
1822 |
int timer; |
int timer; |
1823 |
int answer; |
int answer; |
1824 |
|
u8 retry; |
1825 |
}; |
}; |
1826 |
|
|
1827 |
/* The list for "struct ccs_query". */ |
/* The list for "struct ccs_query". */ |
1830 |
/* Number of "struct file" referring /proc/ccs/query interface. */ |
/* Number of "struct file" referring /proc/ccs/query interface. */ |
1831 |
static atomic_t ccs_query_observers = ATOMIC_INIT(0); |
static atomic_t ccs_query_observers = ATOMIC_INIT(0); |
1832 |
|
|
1833 |
static void ccs_truncate(char *str) |
static int ccs_truncate(char *str) |
1834 |
{ |
{ |
1835 |
while (* (unsigned char *) str > (unsigned char) ' ') |
char *start = str; |
1836 |
|
while (*(unsigned char *) str > (unsigned char) ' ') |
1837 |
str++; |
str++; |
1838 |
*str = '\0'; |
*str = '\0'; |
1839 |
|
return strlen(start) + 1; |
1840 |
|
} |
1841 |
|
|
1842 |
|
static void ccs_add_entry(char *header) |
1843 |
|
{ |
1844 |
|
char *buffer; |
1845 |
|
char *realpath = NULL; |
1846 |
|
char *argv0 = NULL; |
1847 |
|
char *symlink = NULL; |
1848 |
|
char *handler; |
1849 |
|
char *cp = strchr(header, '\n'); |
1850 |
|
int len; |
1851 |
|
if (!cp) |
1852 |
|
return; |
1853 |
|
cp = strchr(cp + 1, '\n'); |
1854 |
|
if (!cp) |
1855 |
|
return; |
1856 |
|
*cp++ = '\0'; |
1857 |
|
len = strlen(cp) + 1; |
1858 |
|
/* strstr() will return NULL if ordering is wrong. */ |
1859 |
|
if (*cp == 'f') { |
1860 |
|
argv0 = strstr(header, " argv[]={ \""); |
1861 |
|
if (argv0) { |
1862 |
|
argv0 += 10; |
1863 |
|
len += ccs_truncate(argv0) + 14; |
1864 |
|
} |
1865 |
|
realpath = strstr(header, " exec={ realpath=\""); |
1866 |
|
if (realpath) { |
1867 |
|
realpath += 8; |
1868 |
|
len += ccs_truncate(realpath) + 6; |
1869 |
|
} |
1870 |
|
symlink = strstr(header, " symlink.target=\""); |
1871 |
|
if (symlink) |
1872 |
|
len += ccs_truncate(symlink + 1) + 1; |
1873 |
|
} |
1874 |
|
handler = strstr(header, "type=execute_handler"); |
1875 |
|
if (handler) |
1876 |
|
len += ccs_truncate(handler) + 6; |
1877 |
|
buffer = kmalloc(len, CCS_GFP_FLAGS); |
1878 |
|
if (!buffer) |
1879 |
|
return; |
1880 |
|
snprintf(buffer, len - 1, "%s", cp); |
1881 |
|
if (handler) |
1882 |
|
ccs_addprintf(buffer, len, " task.%s", handler); |
1883 |
|
if (realpath) |
1884 |
|
ccs_addprintf(buffer, len, " exec.%s", realpath); |
1885 |
|
if (argv0) |
1886 |
|
ccs_addprintf(buffer, len, " exec.argv[0]=%s", argv0); |
1887 |
|
if (symlink) |
1888 |
|
ccs_addprintf(buffer, len, "%s", symlink); |
1889 |
|
ccs_normalize_line(buffer); |
1890 |
|
if (!ccs_write_domain2(buffer, ccs_current_domain(), false)) |
1891 |
|
ccs_update_stat(CCS_STAT_POLICY_UPDATES); |
1892 |
|
kfree(buffer); |
1893 |
} |
} |
1894 |
|
|
1895 |
/** |
/** |
1906 |
int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) |
int ccs_supervisor(struct ccs_request_info *r, const char *fmt, ...) |
1907 |
{ |
{ |
1908 |
va_list args; |
va_list args; |
1909 |
int error = -EPERM; |
int error; |
|
int pos; |
|
1910 |
int len; |
int len; |
1911 |
static unsigned int ccs_serial; |
static unsigned int ccs_serial; |
1912 |
struct ccs_query *entry = NULL; |
struct ccs_query entry = { }; |
1913 |
bool quota_exceeded = false; |
bool quota_exceeded = false; |
1914 |
char *header; |
/* Write /proc/ccs/grant_log or /proc/ccs/reject_log . */ |
|
struct ccs_domain_info * const domain = ccs_current_domain(); |
|
1915 |
va_start(args, fmt); |
va_start(args, fmt); |
1916 |
len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 80; |
ccs_write_log2(r, fmt, args); |
1917 |
va_end(args); |
va_end(args); |
1918 |
if (r->mode == CCS_CONFIG_LEARNING) { |
/* Nothing more to do if granted. */ |
1919 |
char *buffer; |
if (r->granted) |
|
char *realpath = NULL; |
|
|
char *argv0 = NULL; |
|
|
char *symlink = NULL; |
|
|
char *handler = NULL; |
|
|
if (!ccs_domain_quota_ok(r)) |
|
|
return 0; |
|
|
header = ccs_init_log(&len, r); |
|
|
if (!header) |
|
|
return 0; |
|
|
/* strstr() will return NULL if ordering is wrong. */ |
|
|
if (r->param_type == CCS_TYPE_PATH_ACL && |
|
|
r->param.path.operation == CCS_TYPE_EXECUTE) { |
|
|
if (ccs_preference.learning_exec_argv0) { |
|
|
argv0 = strstr(header, " argv[]={ \""); |
|
|
if (argv0) { |
|
|
argv0 += 10; |
|
|
ccs_truncate(argv0); |
|
|
} |
|
|
} |
|
|
if (ccs_preference.learning_exec_realpath) { |
|
|
realpath = strstr(header, |
|
|
" exec={ realpath=\""); |
|
|
if (realpath) { |
|
|
realpath += 8; |
|
|
ccs_truncate(realpath); |
|
|
} |
|
|
} |
|
|
} else if (r->param_type == CCS_TYPE_PATH_ACL && |
|
|
r->param.path.operation == CCS_TYPE_SYMLINK && |
|
|
ccs_preference.learning_symlink_target) { |
|
|
symlink = strstr(header, " symlink.target=\""); |
|
|
if (symlink) |
|
|
ccs_truncate(symlink + 1); |
|
|
} |
|
|
handler = strstr(header, "type=execute_handler"); |
|
|
if (handler) |
|
|
ccs_truncate(handler); |
|
|
buffer = kmalloc(len, CCS_GFP_FLAGS); |
|
|
if (buffer) { |
|
|
va_start(args, fmt); |
|
|
vsnprintf(buffer, len - 1, fmt, args); |
|
|
va_end(args); |
|
|
if (handler) |
|
|
ccs_addprintf(buffer, len, " task.%s", |
|
|
handler); |
|
|
if (realpath) |
|
|
ccs_addprintf(buffer, len, " exec.%s", |
|
|
realpath); |
|
|
if (argv0) |
|
|
ccs_addprintf(buffer, len, " exec.argv[0]=%s", |
|
|
argv0); |
|
|
if (symlink) |
|
|
ccs_addprintf(buffer, len, "%s", symlink); |
|
|
ccs_normalize_line(buffer); |
|
|
ccs_write_domain2(buffer, domain, false); |
|
|
kfree(buffer); |
|
|
} |
|
|
kfree(header); |
|
1920 |
return 0; |
return 0; |
1921 |
} |
if (r->mode) |
1922 |
if (r->mode != CCS_CONFIG_ENFORCING) |
ccs_update_stat(r->mode); |
1923 |
return 0; |
switch (r->mode) { |
|
if (!atomic_read(&ccs_query_observers)) { |
|
1924 |
int i; |
int i; |
1925 |
if (current->ccs_flags & CCS_DONT_SLEEP_ON_ENFORCE_ERROR) |
struct ccs_profile *p; |
1926 |
return -EPERM; |
case CCS_CONFIG_ENFORCING: |
1927 |
for (i = 0; i < ccs_preference.enforcing_penalty; i++) { |
error = -EPERM; |
1928 |
|
if (atomic_read(&ccs_query_observers)) |
1929 |
|
break; |
1930 |
|
if (ccs_current_flags() & CCS_DONT_SLEEP_ON_ENFORCE_ERROR) |
1931 |
|
goto out; |
1932 |
|
p = ccs_profile(r->profile); |
1933 |
|
/* Check enforcing_penalty parameter. */ |
1934 |
|
for (i = 0; i < p->pref[CCS_PREF_ENFORCING_PENALTY]; i++) { |
1935 |
set_current_state(TASK_INTERRUPTIBLE); |
set_current_state(TASK_INTERRUPTIBLE); |
1936 |
schedule_timeout(HZ / 10); |
schedule_timeout(HZ / 10); |
1937 |
} |
} |
|
return -EPERM; |
|
|
} |
|
|
header = ccs_init_log(&len, r); |
|
|
if (!header) |
|
1938 |
goto out; |
goto out; |
1939 |
entry = kzalloc(sizeof(*entry), CCS_GFP_FLAGS); |
case CCS_CONFIG_LEARNING: |
1940 |
if (!entry) |
error = 0; |
1941 |
|
/* Check mac_learning_entry parameter. */ |
1942 |
|
if (ccs_domain_quota_ok(r)) |
1943 |
|
break; |
1944 |
|
/* fall through */ |
1945 |
|
default: |
1946 |
|
return 0; |
1947 |
|
} |
1948 |
|
/* Get message. */ |
1949 |
|
va_start(args, fmt); |
1950 |
|
entry.query = ccs_init_log(&len, r, fmt, args); |
1951 |
|
va_end(args); |
1952 |
|
if (!entry.query) |
1953 |
goto out; |
goto out; |
1954 |
len = ccs_round2(len); |
entry.query_len = strlen(entry.query) + 1; |
1955 |
entry->query = kzalloc(len, CCS_GFP_FLAGS); |
if (!error) { |
1956 |
if (!entry->query) |
ccs_add_entry(entry.query); |
1957 |
goto out; |
goto out; |
1958 |
|
} |
1959 |
spin_lock(&ccs_query_list_lock); |
spin_lock(&ccs_query_list_lock); |
1960 |
if (ccs_quota_for_query && ccs_query_memory_size + len + |
if (ccs_memory_quota[CCS_MEMORY_QUERY] && |
1961 |
sizeof(*entry) >= ccs_quota_for_query) { |
ccs_memory_used[CCS_MEMORY_QUERY] + len |
1962 |
|
>= ccs_memory_quota[CCS_MEMORY_QUERY]) { |
1963 |
quota_exceeded = true; |
quota_exceeded = true; |
1964 |
} else { |
} else { |
1965 |
ccs_query_memory_size += len + sizeof(*entry); |
entry.serial = ccs_serial++; |
1966 |
entry->serial = ccs_serial++; |
entry.retry = r->retry; |
1967 |
|
ccs_memory_used[CCS_MEMORY_QUERY] += len; |
1968 |
|
list_add_tail(&entry.list, &ccs_query_list); |
1969 |
} |
} |
1970 |
spin_unlock(&ccs_query_list_lock); |
spin_unlock(&ccs_query_list_lock); |
1971 |
if (quota_exceeded) |
if (quota_exceeded) |
1972 |
goto out; |
goto out; |
|
pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", |
|
|
entry->serial, r->retry, header); |
|
|
kfree(header); |
|
|
header = NULL; |
|
|
va_start(args, fmt); |
|
|
vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); |
|
|
entry->query_len = strlen(entry->query) + 1; |
|
|
va_end(args); |
|
|
spin_lock(&ccs_query_list_lock); |
|
|
list_add_tail(&entry->list, &ccs_query_list); |
|
|
spin_unlock(&ccs_query_list_lock); |
|
1973 |
/* Give 10 seconds for supervisor's opinion. */ |
/* Give 10 seconds for supervisor's opinion. */ |
1974 |
for (entry->timer = 0; |
while (entry.timer < 10) { |
1975 |
atomic_read(&ccs_query_observers) && entry->timer < 100; |
wake_up_all(&ccs_query_wait); |
1976 |
entry->timer++) { |
if (wait_event_interruptible_timeout |
1977 |
wake_up(&ccs_query_wait); |
(ccs_answer_wait, entry.answer || |
1978 |
set_current_state(TASK_INTERRUPTIBLE); |
!atomic_read(&ccs_query_observers), HZ)) |
|
schedule_timeout(HZ / 10); |
|
|
if (entry->answer) |
|
1979 |
break; |
break; |
1980 |
|
else |
1981 |
|
entry.timer++; |
1982 |
} |
} |
1983 |
spin_lock(&ccs_query_list_lock); |
spin_lock(&ccs_query_list_lock); |
1984 |
list_del(&entry->list); |
list_del(&entry.list); |
1985 |
ccs_query_memory_size -= len + sizeof(*entry); |
ccs_memory_used[CCS_MEMORY_QUERY] -= len; |
1986 |
spin_unlock(&ccs_query_list_lock); |
spin_unlock(&ccs_query_list_lock); |
1987 |
switch (entry->answer) { |
switch (entry.answer) { |
1988 |
case 3: /* Asked to retry by administrator. */ |
case 3: /* Asked to retry by administrator. */ |
1989 |
error = CCS_RETRY_REQUEST; |
error = CCS_RETRY_REQUEST; |
1990 |
r->retry++; |
r->retry++; |
1993 |
/* Granted by administrator. */ |
/* Granted by administrator. */ |
1994 |
error = 0; |
error = 0; |
1995 |
break; |
break; |
|
case 0: |
|
|
/* Timed out. */ |
|
|
break; |
|
1996 |
default: |
default: |
1997 |
/* Rejected by administrator. */ |
/* Timed out or rejected by administrator. */ |
1998 |
break; |
break; |
1999 |
} |
} |
2000 |
out: |
out: |
2001 |
if (entry) |
kfree(entry.query); |
|
kfree(entry->query); |
|
|
kfree(entry); |
|
|
kfree(header); |
|
2002 |
return error; |
return error; |
2003 |
} |
} |
2004 |
|
|
2069 |
head->r.query_index = 0; |
head->r.query_index = 0; |
2070 |
return; |
return; |
2071 |
} |
} |
2072 |
buf = kzalloc(len, CCS_GFP_FLAGS); |
buf = kzalloc(len + 32, CCS_GFP_FLAGS); |
2073 |
if (!buf) |
if (!buf) |
2074 |
return; |
return; |
2075 |
pos = 0; |
pos = 0; |
2085 |
* can change, but I don't care. |
* can change, but I don't care. |
2086 |
*/ |
*/ |
2087 |
if (len == ptr->query_len) |
if (len == ptr->query_len) |
2088 |
memmove(buf, ptr->query, len); |
snprintf(buf, len + 32, "Q%u-%hu\n%s", ptr->serial, |
2089 |
|
ptr->retry, ptr->query); |
2090 |
break; |
break; |
2091 |
} |
} |
2092 |
spin_unlock(&ccs_query_list_lock); |
spin_unlock(&ccs_query_list_lock); |
2130 |
break; |
break; |
2131 |
} |
} |
2132 |
spin_unlock(&ccs_query_list_lock); |
spin_unlock(&ccs_query_list_lock); |
2133 |
|
wake_up_all(&ccs_answer_wait); |
2134 |
return 0; |
return 0; |
2135 |
} |
} |
2136 |
|
|
2148 |
} |
} |
2149 |
|
|
2150 |
/** |
/** |
|
* ccs_read_self_domain - Get the current process's domainname. |
|
|
* |
|
|
* @head: Pointer to "struct ccs_io_buffer". |
|
|
*/ |
|
|
static void ccs_read_self_domain(struct ccs_io_buffer *head) |
|
|
{ |
|
|
if (head->r.eof) |
|
|
return; |
|
|
/* |
|
|
* ccs_current_domain()->domainname != NULL because every process |
|
|
* belongs to a domain and the domain's name cannot be NULL. |
|
|
*/ |
|
|
ccs_io_printf(head, "%s", ccs_current_domain()->domainname->name); |
|
|
head->r.eof = true; |
|
|
} |
|
|
|
|
|
/** |
|
2151 |
* ccs_open_control - open() for /proc/ccs/ interface. |
* ccs_open_control - open() for /proc/ccs/ interface. |
2152 |
* |
* |
2153 |
* @type: Type of interface. |
* @type: Type of interface. |
2171 |
head->write = ccs_write_exception; |
head->write = ccs_write_exception; |
2172 |
head->read = ccs_read_exception; |
head->read = ccs_read_exception; |
2173 |
break; |
break; |
|
#ifdef CONFIG_CCSECURITY_AUDIT |
|
2174 |
case CCS_GRANTLOG: /* /proc/ccs/grant_log */ |
case CCS_GRANTLOG: /* /proc/ccs/grant_log */ |
2175 |
case CCS_REJECTLOG: /* /proc/ccs/reject_log */ |
case CCS_REJECTLOG: /* /proc/ccs/reject_log */ |
2176 |
head->poll = ccs_poll_log; |
head->poll = ccs_poll_log; |
2177 |
head->read = ccs_read_log; |
head->read = ccs_read_log; |
2178 |
break; |
break; |
|
#endif |
|
|
case CCS_SELFDOMAIN: /* /proc/ccs/self_domain */ |
|
|
head->read = ccs_read_self_domain; |
|
|
break; |
|
2179 |
case CCS_DOMAIN_STATUS: /* /proc/ccs/.domain_status */ |
case CCS_DOMAIN_STATUS: /* /proc/ccs/.domain_status */ |
2180 |
head->write = ccs_write_domain_profile; |
head->write = ccs_write_domain_profile; |
2181 |
head->read = ccs_read_domain_profile; |
head->read = ccs_read_domain_profile; |
2182 |
break; |
break; |
2183 |
case CCS_EXECUTE_HANDLER: /* /proc/ccs/.execute_handler */ |
case CCS_EXECUTE_HANDLER: /* /proc/ccs/.execute_handler */ |
2184 |
/* Allow execute_handler to read process's status. */ |
/* Allow execute_handler to read process's status. */ |
2185 |
if (!(current->ccs_flags & CCS_TASK_IS_EXECUTE_HANDLER)) { |
if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { |
2186 |
kfree(head); |
kfree(head); |
2187 |
return -EPERM; |
return -EPERM; |
2188 |
} |
} |
2246 |
return -ENOMEM; |
return -ENOMEM; |
2247 |
} |
} |
2248 |
} |
} |
|
if (type != CCS_QUERY && |
|
|
type != CCS_GRANTLOG && type != CCS_REJECTLOG) |
|
|
head->reader_idx = ccs_lock(); |
|
|
file->private_data = head; |
|
|
/* |
|
|
* Call the handler now if the file is /proc/ccs/self_domain |
|
|
* so that the user can use "cat < /proc/ccs/self_domain" to |
|
|
* know the current process's domainname. |
|
|
*/ |
|
|
if (type == CCS_SELFDOMAIN) |
|
|
ccs_read_control(file, NULL, 0); |
|
2249 |
/* |
/* |
2250 |
* If the file is /proc/ccs/query , increment the observer counter. |
* If the file is /proc/ccs/query , increment the observer counter. |
2251 |
* The obserber counter is used by ccs_supervisor() to see if |
* The obserber counter is used by ccs_supervisor() to see if |
2252 |
* there is some process monitoring /proc/ccs/query. |
* there is some process monitoring /proc/ccs/query. |
2253 |
*/ |
*/ |
2254 |
else if (type == CCS_QUERY) |
if (type == CCS_QUERY) |
2255 |
atomic_inc(&ccs_query_observers); |
atomic_inc(&ccs_query_observers); |
2256 |
|
else if (type != CCS_GRANTLOG && type != CCS_REJECTLOG) |
2257 |
|
head->reader_idx = ccs_lock(); |
2258 |
|
file->private_data = head; |
2259 |
return 0; |
return 0; |
2260 |
} |
} |
2261 |
|
|
2371 |
cp0[head->w.avail - 1] = '\0'; |
cp0[head->w.avail - 1] = '\0'; |
2372 |
head->w.avail = 0; |
head->w.avail = 0; |
2373 |
ccs_normalize_line(cp0); |
ccs_normalize_line(cp0); |
2374 |
head->write(head); |
if (head->write(head)) |
2375 |
|
continue; |
2376 |
|
switch (head->type) { |
2377 |
|
case CCS_DOMAINPOLICY: |
2378 |
|
case CCS_EXCEPTIONPOLICY: |
2379 |
|
case CCS_DOMAIN_STATUS: |
2380 |
|
case CCS_MEMINFO: |
2381 |
|
case CCS_PROFILE: |
2382 |
|
case CCS_MANAGER: |
2383 |
|
ccs_update_stat(CCS_STAT_POLICY_UPDATES); |
2384 |
|
} |
2385 |
} |
} |
2386 |
ccs_read_unlock(idx); |
ccs_read_unlock(idx); |
2387 |
mutex_unlock(&head->io_sem); |
mutex_unlock(&head->io_sem); |
2403 |
/* |
/* |
2404 |
* If the file is /proc/ccs/query , decrement the observer counter. |
* If the file is /proc/ccs/query , decrement the observer counter. |
2405 |
*/ |
*/ |
2406 |
if (type == CCS_QUERY) |
if (type == CCS_QUERY) { |
2407 |
atomic_dec(&ccs_query_observers); |
if (atomic_dec_and_test(&ccs_query_observers)) |
2408 |
if (type != CCS_QUERY && |
wake_up_all(&ccs_answer_wait); |
2409 |
type != CCS_GRANTLOG && type != CCS_REJECTLOG) |
} else if (type != CCS_GRANTLOG && type != CCS_REJECTLOG) |
2410 |
ccs_unlock(head->reader_idx); |
ccs_unlock(head->reader_idx); |
2411 |
/* Release memory used for policy I/O. */ |
/* Release memory used for policy I/O. */ |
2412 |
kfree(head->read_buf); |
kfree(head->read_buf); |