5 |
* |
* |
6 |
* Copyright (C) 2005-2007 NTT DATA CORPORATION |
* Copyright (C) 2005-2007 NTT DATA CORPORATION |
7 |
* |
* |
8 |
* Version: 1.5.0-pre 2007/08/06 |
* Version: 1.5.2-pre 2007/10/19 |
9 |
* |
* |
10 |
* 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. |
11 |
* See README.ccs for ChangeLog. |
* See README.ccs for ChangeLog. |
44 |
struct domain_initializer_entry *next; |
struct domain_initializer_entry *next; |
45 |
const struct path_info *domainname; /* This may be NULL */ |
const struct path_info *domainname; /* This may be NULL */ |
46 |
const struct path_info *program; |
const struct path_info *program; |
47 |
u8 is_deleted; |
bool is_deleted; |
48 |
u8 is_not; |
bool is_not; |
49 |
u8 is_last_name; |
bool is_last_name; |
|
u8 is_oldstyle; |
|
50 |
}; |
}; |
51 |
|
|
52 |
/***** The structure for domains to not to transit domains. *****/ |
/***** The structure for domains to not to transit domains. *****/ |
55 |
struct domain_keeper_entry *next; |
struct domain_keeper_entry *next; |
56 |
const struct path_info *domainname; |
const struct path_info *domainname; |
57 |
const struct path_info *program; /* This may be NULL */ |
const struct path_info *program; /* This may be NULL */ |
58 |
u8 is_deleted; |
bool is_deleted; |
59 |
u8 is_not; |
bool is_not; |
60 |
u8 is_last_name; |
bool is_last_name; |
61 |
}; |
}; |
62 |
|
|
63 |
/***** The structure for program files that should be aggregated. *****/ |
/***** The structure for program files that should be aggregated. *****/ |
66 |
struct aggregator_entry *next; |
struct aggregator_entry *next; |
67 |
const struct path_info *original_name; |
const struct path_info *original_name; |
68 |
const struct path_info *aggregated_name; |
const struct path_info *aggregated_name; |
69 |
int is_deleted; |
bool is_deleted; |
70 |
}; |
}; |
71 |
|
|
72 |
/***** The structure for program files that should be aliased. *****/ |
/***** The structure for program files that should be aliased. *****/ |
75 |
struct alias_entry *next; |
struct alias_entry *next; |
76 |
const struct path_info *original_name; |
const struct path_info *original_name; |
77 |
const struct path_info *aliased_name; |
const struct path_info *aliased_name; |
78 |
int is_deleted; |
bool is_deleted; |
79 |
}; |
}; |
80 |
|
|
81 |
/************************* VARIABLES *************************/ |
/************************* VARIABLES *************************/ |
85 |
|
|
86 |
/************************* UTILITY FUNCTIONS *************************/ |
/************************* UTILITY FUNCTIONS *************************/ |
87 |
|
|
|
int IsDomainDef(const unsigned char *buffer) |
|
|
{ |
|
|
/* while (*buffer && (*buffer <= ' ' || *buffer >= 127)) buffer++; */ |
|
|
return strncmp(buffer, ROOT_NAME, ROOT_NAME_LEN) == 0; |
|
|
} |
|
|
|
|
88 |
const char *GetLastName(const struct domain_info *domain) |
const char *GetLastName(const struct domain_info *domain) |
89 |
{ |
{ |
90 |
const char *cp0 = domain->domainname->name, *cp1; |
const char *cp0 = domain->domainname->name, *cp1; |
92 |
return cp0; |
return cp0; |
93 |
} |
} |
94 |
|
|
|
int ReadSelfDomain(struct io_buffer *head) |
|
|
{ |
|
|
if (!head->read_eof) { |
|
|
io_printf(head, "%s", current->domain_info->domainname->name); |
|
|
head->read_eof = 1; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
95 |
int AddDomainACL(struct acl_info *ptr, struct domain_info *domain, struct acl_info *new_ptr) |
int AddDomainACL(struct acl_info *ptr, struct domain_info *domain, struct acl_info *new_ptr) |
96 |
{ |
{ |
97 |
mb(); /* Instead of using spinlock. */ |
mb(); /* Instead of using spinlock. */ |
108 |
return 0; |
return 0; |
109 |
} |
} |
110 |
|
|
|
int TooManyDomainACL(struct domain_info * const domain) { |
|
|
unsigned int count = 0; |
|
|
struct acl_info *ptr; |
|
|
for (ptr = domain->first_acl_ptr; ptr; ptr = ptr->next) { |
|
|
if (!ptr->is_deleted) count++; |
|
|
} |
|
|
/* If there are so many entries, don't append if learning mode. */ |
|
|
if (count < CheckCCSFlags(CCS_TOMOYO_MAX_ACCEPT_ENTRY)) return 0; |
|
|
if (!domain->quota_warned) { |
|
|
printk("TOMOYO-WARNING: Domain '%s' has so many ACLs to hold. Stopped learning mode.\n", domain->domainname->name); |
|
|
domain->quota_warned = 1; |
|
|
} |
|
|
return 1; |
|
|
} |
|
|
|
|
|
|
|
111 |
/************************* DOMAIN INITIALIZER HANDLER *************************/ |
/************************* DOMAIN INITIALIZER HANDLER *************************/ |
112 |
|
|
113 |
static struct domain_initializer_entry *domain_initializer_list = NULL; |
static struct domain_initializer_entry *domain_initializer_list = NULL; |
114 |
|
|
115 |
static int AddDomainInitializerEntry(const char *domainname, const char *program, const int is_not, const int is_delete, const int is_oldstyle) |
static int AddDomainInitializerEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete) |
116 |
{ |
{ |
117 |
struct domain_initializer_entry *new_entry, *ptr; |
struct domain_initializer_entry *new_entry, *ptr; |
118 |
static DECLARE_MUTEX(lock); |
static DECLARE_MUTEX(lock); |
119 |
const struct path_info *saved_program, *saved_domainname = NULL; |
const struct path_info *saved_program, *saved_domainname = NULL; |
120 |
int error = -ENOMEM; |
int error = -ENOMEM; |
121 |
int is_last_name = 0; |
bool is_last_name = 0; |
122 |
if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */ |
if (!IsCorrectPath(program, 1, -1, -1, __FUNCTION__)) return -EINVAL; /* No patterns allowed. */ |
123 |
if (domainname) { |
if (domainname) { |
124 |
if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) { |
if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) { |
131 |
if ((saved_program = SaveName(program)) == NULL) return -ENOMEM; |
if ((saved_program = SaveName(program)) == NULL) return -ENOMEM; |
132 |
down(&lock); |
down(&lock); |
133 |
for (ptr = domain_initializer_list; ptr; ptr = ptr->next) { |
for (ptr = domain_initializer_list; ptr; ptr = ptr->next) { |
134 |
if (ptr->is_not == is_not && ptr->is_oldstyle == is_oldstyle && ptr->domainname == saved_domainname && ptr->program == saved_program) { |
if (ptr->is_not == is_not && ptr->domainname == saved_domainname && ptr->program == saved_program) { |
135 |
ptr->is_deleted = is_delete; |
ptr->is_deleted = is_delete; |
136 |
error = 0; |
error = 0; |
137 |
goto out; |
goto out; |
146 |
new_entry->program = saved_program; |
new_entry->program = saved_program; |
147 |
new_entry->is_not = is_not; |
new_entry->is_not = is_not; |
148 |
new_entry->is_last_name = is_last_name; |
new_entry->is_last_name = is_last_name; |
|
new_entry->is_oldstyle = is_oldstyle; |
|
149 |
mb(); /* Instead of using spinlock. */ |
mb(); /* Instead of using spinlock. */ |
150 |
if ((ptr = domain_initializer_list) != NULL) { |
if ((ptr = domain_initializer_list) != NULL) { |
151 |
while (ptr->next) ptr = ptr->next; ptr->next = new_entry; |
while (ptr->next) ptr = ptr->next; ptr->next = new_entry; |
166 |
head->read_var2 = ptr; |
head->read_var2 = ptr; |
167 |
if (!ptr->is_deleted) { |
if (!ptr->is_deleted) { |
168 |
if (ptr->domainname) { |
if (ptr->domainname) { |
169 |
if (io_printf(head, "%s%s%s from %s\n", ptr->is_not ? "no_" : "", ptr->is_oldstyle ? KEYWORD_INITIALIZER : KEYWORD_INITIALIZE_DOMAIN, ptr->program->name, ptr->domainname->name)) break; |
if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s from %s\n", ptr->is_not ? "no_" : "", ptr->program->name, ptr->domainname->name)) break; |
170 |
} else { |
} else { |
171 |
if (io_printf(head, "%s%s%s\n", ptr->is_not ? "no_" : "", ptr->is_oldstyle ? KEYWORD_INITIALIZER : KEYWORD_INITIALIZE_DOMAIN, ptr->program->name)) break; |
if (io_printf(head, "%s" KEYWORD_INITIALIZE_DOMAIN "%s\n", ptr->is_not ? "no_" : "", ptr->program->name)) break; |
172 |
} |
} |
173 |
} |
} |
174 |
ptr = ptr->next; |
ptr = ptr->next; |
176 |
return ptr ? -ENOMEM : 0; |
return ptr ? -ENOMEM : 0; |
177 |
} |
} |
178 |
|
|
179 |
int AddDomainInitializerPolicy(char *data, const int is_not, const int is_delete, const int is_oldstyle) |
int AddDomainInitializerPolicy(char *data, const bool is_not, const bool is_delete) |
180 |
{ |
{ |
181 |
char *cp = strstr(data, " from "); |
char *cp = strstr(data, " from "); |
182 |
if (cp) { |
if (cp) { |
183 |
*cp = '\0'; |
*cp = '\0'; |
184 |
return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete, is_oldstyle); |
return AddDomainInitializerEntry(cp + 6, data, is_not, is_delete); |
185 |
} else { |
} else { |
186 |
return AddDomainInitializerEntry(NULL, data, is_not, is_delete, is_oldstyle); |
return AddDomainInitializerEntry(NULL, data, is_not, is_delete); |
187 |
} |
} |
188 |
} |
} |
189 |
|
|
211 |
|
|
212 |
static struct domain_keeper_entry *domain_keeper_list = NULL; |
static struct domain_keeper_entry *domain_keeper_list = NULL; |
213 |
|
|
214 |
static int AddDomainKeeperEntry(const char *domainname, const char *program, const int is_not, const int is_delete) |
static int AddDomainKeeperEntry(const char *domainname, const char *program, const bool is_not, const bool is_delete) |
215 |
{ |
{ |
216 |
struct domain_keeper_entry *new_entry, *ptr; |
struct domain_keeper_entry *new_entry, *ptr; |
217 |
const struct path_info *saved_domainname, *saved_program = NULL; |
const struct path_info *saved_domainname, *saved_program = NULL; |
218 |
static DECLARE_MUTEX(lock); |
static DECLARE_MUTEX(lock); |
219 |
int error = -ENOMEM; |
int error = -ENOMEM; |
220 |
int is_last_name = 0; |
bool is_last_name = 0; |
221 |
if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) { |
if (!IsDomainDef(domainname) && IsCorrectPath(domainname, 1, -1, -1, __FUNCTION__)) { |
222 |
is_last_name = 1; |
is_last_name = 1; |
223 |
} else if (!IsCorrectDomain(domainname, __FUNCTION__)) { |
} else if (!IsCorrectDomain(domainname, __FUNCTION__)) { |
257 |
return error; |
return error; |
258 |
} |
} |
259 |
|
|
260 |
int AddDomainKeeperPolicy(char *data, const int is_not, const int is_delete) |
int AddDomainKeeperPolicy(char *data, const bool is_not, const bool is_delete) |
261 |
{ |
{ |
262 |
char *cp = strstr(data, " from "); |
char *cp = strstr(data, " from "); |
263 |
if (cp) { |
if (cp) { |
308 |
|
|
309 |
static struct alias_entry *alias_list = NULL; |
static struct alias_entry *alias_list = NULL; |
310 |
|
|
311 |
static int AddAliasEntry(const char *original_name, const char *aliased_name, const int is_delete) |
static int AddAliasEntry(const char *original_name, const char *aliased_name, const bool is_delete) |
312 |
{ |
{ |
313 |
struct alias_entry *new_entry, *ptr; |
struct alias_entry *new_entry, *ptr; |
314 |
static DECLARE_MUTEX(lock); |
static DECLARE_MUTEX(lock); |
355 |
return ptr ? -ENOMEM : 0; |
return ptr ? -ENOMEM : 0; |
356 |
} |
} |
357 |
|
|
358 |
int AddAliasPolicy(char *data, const int is_delete) |
int AddAliasPolicy(char *data, const bool is_delete) |
359 |
{ |
{ |
360 |
char *cp = strchr(data, ' '); |
char *cp = strchr(data, ' '); |
361 |
if (!cp) return -EINVAL; |
if (!cp) return -EINVAL; |
367 |
|
|
368 |
static struct aggregator_entry *aggregator_list = NULL; |
static struct aggregator_entry *aggregator_list = NULL; |
369 |
|
|
370 |
static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const int is_delete) |
static int AddAggregatorEntry(const char *original_name, const char *aggregated_name, const bool is_delete) |
371 |
{ |
{ |
372 |
struct aggregator_entry *new_entry, *ptr; |
struct aggregator_entry *new_entry, *ptr; |
373 |
static DECLARE_MUTEX(lock); |
static DECLARE_MUTEX(lock); |
414 |
return ptr ? -ENOMEM : 0; |
return ptr ? -ENOMEM : 0; |
415 |
} |
} |
416 |
|
|
417 |
int AddAggregatorPolicy(char *data, const int is_delete) |
int AddAggregatorPolicy(char *data, const bool is_delete) |
418 |
{ |
{ |
419 |
char *cp = strchr(data, ' '); |
char *cp = strchr(data, ' '); |
420 |
if (!cp) return -EINVAL; |
if (!cp) return -EINVAL; |
504 |
|
|
505 |
/************************* DOMAIN TRANSITION HANDLER *************************/ |
/************************* DOMAIN TRANSITION HANDLER *************************/ |
506 |
|
|
|
struct domain_info *FindDomain(const char *domainname0) |
|
|
{ |
|
|
struct domain_info *domain; |
|
|
static int first = 1; |
|
|
struct path_info domainname; |
|
|
domainname.name = domainname0; |
|
|
fill_path_info(&domainname); |
|
|
if (first) { |
|
|
KERNEL_DOMAIN.domainname = SaveName(ROOT_NAME); |
|
|
first = 0; |
|
|
} |
|
|
for (domain = &KERNEL_DOMAIN; domain; domain = domain->next) { |
|
|
if (!domain->is_deleted && !pathcmp(&domainname, domain->domainname)) return domain; |
|
|
} |
|
|
return NULL; |
|
|
} |
|
|
|
|
507 |
struct domain_info *FindOrAssignNewDomain(const char *domainname, const u8 profile) |
struct domain_info *FindOrAssignNewDomain(const char *domainname, const u8 profile) |
508 |
{ |
{ |
509 |
struct domain_info *domain = NULL; |
struct domain_info *domain = NULL; |
635 |
struct file *filp = bprm->file; |
struct file *filp = bprm->file; |
636 |
char *new_domain_name = NULL; |
char *new_domain_name = NULL; |
637 |
char *real_program_name = NULL, *symlink_program_name = NULL; |
char *real_program_name = NULL, *symlink_program_name = NULL; |
638 |
const int is_enforce = CheckCCSEnforce(CCS_TOMOYO_MAC_FOR_FILE); |
const bool is_enforce = CheckCCSEnforce(CCS_TOMOYO_MAC_FOR_FILE); |
639 |
int retval; |
int retval; |
640 |
struct path_info r, s, l; |
struct path_info r, s, l; |
641 |
|
|
645 |
*/ |
*/ |
646 |
static int first = 1; |
static int first = 1; |
647 |
if (first) { |
if (first) { |
648 |
AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0, 0); |
AddDomainInitializerEntry(NULL, "/sbin/hotplug", 0, 0); |
649 |
AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0, 0); |
AddDomainInitializerEntry(NULL, "/sbin/modprobe", 0, 0); |
650 |
first = 0; |
first = 0; |
651 |
} |
} |
652 |
} |
} |
756 |
return retval; |
return retval; |
757 |
} |
} |
758 |
|
|
759 |
|
static int CheckEnviron(struct linux_binprm *bprm) |
760 |
|
{ |
761 |
|
char *arg_ptr = ccs_alloc(CCS_MAX_PATHNAME_LEN); |
762 |
|
int arg_len = 0; |
763 |
|
unsigned long pos = bprm->p; |
764 |
|
int i = pos / PAGE_SIZE, offset = pos % PAGE_SIZE; |
765 |
|
int argv_count = bprm->argc; |
766 |
|
int envp_count = bprm->envc; |
767 |
|
//printk("start %d %d\n", argv_count, envp_count); |
768 |
|
int error = -ENOMEM; |
769 |
|
if (!arg_ptr) goto out; |
770 |
|
if (!envp_count) { |
771 |
|
error = 0; |
772 |
|
goto out; |
773 |
|
} |
774 |
|
while (error == -ENOMEM) { |
775 |
|
struct page *page; |
776 |
|
const char *kaddr; |
777 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU) |
778 |
|
if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) goto out; |
779 |
|
#else |
780 |
|
page = bprm->page[i]; |
781 |
|
#endif |
782 |
|
/* Map */ |
783 |
|
kaddr = kmap(page); |
784 |
|
if (!kaddr) { /* Mapping failed. */ |
785 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU) |
786 |
|
put_page(page); |
787 |
|
#endif |
788 |
|
goto out; |
789 |
|
} |
790 |
|
/* Read. */ |
791 |
|
while (argv_count && offset < PAGE_SIZE) { |
792 |
|
if (!kaddr[offset++]) argv_count--; |
793 |
|
} |
794 |
|
if (argv_count) goto unmap_page; |
795 |
|
while (offset < PAGE_SIZE) { |
796 |
|
const unsigned char c = kaddr[offset++]; |
797 |
|
if (arg_len < CCS_MAX_PATHNAME_LEN - 10) { |
798 |
|
if (c == '=') { |
799 |
|
arg_ptr[arg_len++] = '\0'; |
800 |
|
} else if (c == '\\') { |
801 |
|
arg_ptr[arg_len++] = '\\'; |
802 |
|
arg_ptr[arg_len++] = '\\'; |
803 |
|
} else if (c > ' ' && c < 127) { |
804 |
|
arg_ptr[arg_len++] = c; |
805 |
|
} else { |
806 |
|
arg_ptr[arg_len++] = '\\'; |
807 |
|
arg_ptr[arg_len++] = (c >> 6) + '0'; |
808 |
|
arg_ptr[arg_len++] = ((c >> 3) & 7) + '0'; |
809 |
|
arg_ptr[arg_len++] = (c & 7) + '0'; |
810 |
|
} |
811 |
|
} else { |
812 |
|
arg_ptr[arg_len] = '\0'; |
813 |
|
} |
814 |
|
if (c) continue; |
815 |
|
if (CheckEnvPerm(arg_ptr)) { |
816 |
|
error = -EPERM; |
817 |
|
break; |
818 |
|
} |
819 |
|
if (!--envp_count) { |
820 |
|
error = 0; |
821 |
|
break; |
822 |
|
} |
823 |
|
arg_len = 0; |
824 |
|
} |
825 |
|
unmap_page: |
826 |
|
/* Unmap. */ |
827 |
|
kunmap(page); |
828 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23) && defined(CONFIG_MMU) |
829 |
|
put_page(page); |
830 |
|
pos += PAGE_SIZE - offset; |
831 |
|
#endif |
832 |
|
i++; |
833 |
|
offset = 0; |
834 |
|
} |
835 |
|
out: |
836 |
|
ccs_free(arg_ptr); |
837 |
|
if (error && !CheckCCSEnforce(CCS_TOMOYO_MAC_FOR_ENV)) error = 0; |
838 |
|
return error; |
839 |
|
} |
840 |
|
|
841 |
#endif |
#endif |
842 |
|
|
843 |
int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs) |
int search_binary_handler_with_transition(struct linux_binprm *bprm, struct pt_regs *regs) |
854 |
retval = 0; next_domain = prev_domain; |
retval = 0; next_domain = prev_domain; |
855 |
#endif |
#endif |
856 |
if (retval == 0) { |
if (retval == 0) { |
|
current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
|
857 |
current->domain_info = next_domain; |
current->domain_info = next_domain; |
858 |
retval = search_binary_handler(bprm, regs); |
#if defined(CONFIG_TOMOYO) |
859 |
if (retval < 0) current->domain_info = prev_domain; |
if (CheckCCSFlags(CCS_TOMOYO_MAC_FOR_ENV)) retval = CheckEnviron(bprm); |
860 |
|
#endif |
861 |
|
current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
862 |
|
if (!retval) retval = search_binary_handler(bprm, regs); |
863 |
current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
864 |
|
if (retval < 0) current->domain_info = prev_domain; |
865 |
} |
} |
866 |
return retval; |
return retval; |
867 |
} |
} |