5 |
* |
* |
6 |
* Copyright (C) 2005-2008 NTT DATA CORPORATION |
* Copyright (C) 2005-2008 NTT DATA CORPORATION |
7 |
* |
* |
8 |
* Version: 1.6.0-pre 2008/03/04 |
* Version: 1.6.0-pre 2008/03/10 |
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. |
85 |
|
|
86 |
/************************* UTILITY FUNCTIONS *************************/ |
/************************* UTILITY FUNCTIONS *************************/ |
87 |
|
|
88 |
|
void SetDomainFlag(struct domain_info *domain, const bool is_delete, const u8 flags) |
89 |
|
{ |
90 |
|
mutex_lock(&new_domain_assign_lock); |
91 |
|
if (!is_delete) domain->flags |= flags; |
92 |
|
else domain->flags &= ~flags; |
93 |
|
mutex_unlock(&new_domain_assign_lock); |
94 |
|
} |
95 |
|
|
96 |
const char *GetLastName(const struct domain_info *domain) |
const char *GetLastName(const struct domain_info *domain) |
97 |
{ |
{ |
98 |
const char *cp0 = domain->domainname->name, *cp1; |
const char *cp0 = domain->domainname->name, *cp1; |
610 |
return NULL; |
return NULL; |
611 |
} |
} |
612 |
|
|
613 |
static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const u8 do_perm_check) |
static int FindNextDomain(struct linux_binprm *bprm, struct domain_info **next_domain, const struct path_info *path_to_verify) |
614 |
{ |
{ |
615 |
/* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */ |
/* This function assumes that the size of buffer returned by realpath() = CCS_MAX_PATHNAME_LEN. */ |
616 |
struct domain_info *old_domain = current->domain_info, *domain = NULL; |
struct domain_info *old_domain = current->domain_info, *domain = NULL; |
648 |
else l.name = old_domain_name; |
else l.name = old_domain_name; |
649 |
fill_path_info(&l); |
fill_path_info(&l); |
650 |
|
|
651 |
if (!do_perm_check) goto ok; |
if (path_to_verify) { |
652 |
|
if (pathcmp(&r, path_to_verify)) { |
653 |
|
static u8 counter = 20; |
654 |
|
if (counter) { |
655 |
|
counter--; |
656 |
|
printk("Failed to verify: %s\n", path_to_verify->name); |
657 |
|
} |
658 |
|
goto out; |
659 |
|
} |
660 |
|
goto ok; |
661 |
|
} |
662 |
|
|
663 |
/* Check 'alias' directive. */ |
/* Check 'alias' directive. */ |
664 |
if (pathcmp(&r, &s)) { |
if (pathcmp(&r, &s)) { |
857 |
*dest = '\0'; |
*dest = '\0'; |
858 |
} |
} |
859 |
|
|
860 |
static int try_alt_exec(struct linux_binprm *bprm, char **work) |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
861 |
|
#include <linux/namei.h> |
862 |
|
#include <linux/mount.h> |
863 |
|
#endif |
864 |
|
|
865 |
|
/* |
866 |
|
* GetRootDepth - return the depth of root directory. |
867 |
|
*/ |
868 |
|
static int GetRootDepth(void) |
869 |
|
{ |
870 |
|
int depth = 0; |
871 |
|
struct dentry *dentry; |
872 |
|
struct vfsmount *vfsmnt; |
873 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) |
874 |
|
struct path root; |
875 |
|
#endif |
876 |
|
read_lock(¤t->fs->lock); |
877 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) |
878 |
|
root = current->fs->root; |
879 |
|
path_get(¤t->fs->root); |
880 |
|
dentry = root.dentry; |
881 |
|
vfsmnt = root.mnt; |
882 |
|
#else |
883 |
|
dentry = dget(current->fs->root); |
884 |
|
vfsmnt = mntget(current->fs->rootmnt); |
885 |
|
#endif |
886 |
|
read_unlock(¤t->fs->lock); |
887 |
|
/***** CRITICAL SECTION START *****/ |
888 |
|
spin_lock(&dcache_lock); |
889 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
890 |
|
spin_lock(&vfsmount_lock); |
891 |
|
#endif |
892 |
|
for (;;) { |
893 |
|
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { |
894 |
|
/* Global root? */ |
895 |
|
if (vfsmnt->mnt_parent == vfsmnt) break; |
896 |
|
dentry = vfsmnt->mnt_mountpoint; |
897 |
|
vfsmnt = vfsmnt->mnt_parent; |
898 |
|
continue; |
899 |
|
} |
900 |
|
dentry = dentry->d_parent; |
901 |
|
depth++; |
902 |
|
} |
903 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
904 |
|
spin_unlock(&vfsmount_lock); |
905 |
|
#endif |
906 |
|
spin_unlock(&dcache_lock); |
907 |
|
/***** CRITICAL SECTION END *****/ |
908 |
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) |
909 |
|
path_put(&root); |
910 |
|
#else |
911 |
|
dput(dentry); |
912 |
|
mntput(vfsmnt); |
913 |
|
#endif |
914 |
|
return depth; |
915 |
|
} |
916 |
|
|
917 |
|
static int try_alt_exec(struct linux_binprm *bprm, const struct path_info *filename, char **work, struct domain_info **next_domain) |
918 |
{ |
{ |
919 |
/* |
/* |
920 |
* Contents of modified bprm. |
* Contents of modified bprm. |
928 |
* = 0 |
* = 0 |
929 |
* |
* |
930 |
* modified bprm->argv[0] |
* modified bprm->argv[0] |
931 |
* = the program's name specified by alt_exec |
* = the program's name specified by execute_handler |
932 |
* modified bprm->argv[1] |
* modified bprm->argv[1] |
933 |
* = current->domain_info->domainname->name |
* = current->domain_info->domainname->name |
934 |
* modified bprm->argv[2] |
* modified bprm->argv[2] |
957 |
const int original_argc = bprm->argc; |
const int original_argc = bprm->argc; |
958 |
const int original_envc = bprm->envc; |
const int original_envc = bprm->envc; |
959 |
struct task_struct *task = current; |
struct task_struct *task = current; |
960 |
static const int buffer_len = PAGE_SIZE; |
static const int buffer_len = CCS_MAX_PATHNAME_LEN; |
961 |
char *buffer = NULL; |
char *buffer = NULL; |
962 |
char *alt_exec; |
char *execute_handler; |
|
const char *alt_exec1 = GetAltExec(); |
|
|
if (!alt_exec1 || *alt_exec1 != '/') return -EINVAL; |
|
|
retval = strlen(alt_exec1) + 1; |
|
|
alt_exec = ccs_alloc(retval); |
|
|
if (!alt_exec) return -ENOMEM; |
|
|
*work = alt_exec; |
|
|
memmove(alt_exec, alt_exec1, retval); |
|
|
UnEscape(alt_exec); |
|
963 |
|
|
964 |
/* Close the rejected program's dentry. */ |
/* Close the requested program's dentry. */ |
965 |
allow_write_access(bprm->file); |
allow_write_access(bprm->file); |
966 |
fput(bprm->file); |
fput(bprm->file); |
967 |
bprm->file = NULL; |
bprm->file = NULL; |
968 |
|
|
969 |
|
/* Allocate memory for execute handler's pathname. */ |
970 |
|
execute_handler = ccs_alloc(buffer_len); |
971 |
|
*work = execute_handler; |
972 |
|
if (!execute_handler) return -ENOMEM; |
973 |
|
strncpy(execute_handler, filename->name, buffer_len - 1); |
974 |
|
UnEscape(execute_handler); |
975 |
|
|
976 |
|
if (1) { /* Adjust root directory for open_exec(). */ |
977 |
|
int depth = GetRootDepth(); |
978 |
|
char *cp = execute_handler; |
979 |
|
if (!*cp || *cp != '/') return -ENOENT; |
980 |
|
while (depth) { |
981 |
|
cp = strchr(cp + 1, '/'); |
982 |
|
if (!cp) return -ENOENT; |
983 |
|
depth--; |
984 |
|
} |
985 |
|
memmove(execute_handler, cp, strlen(cp) + 1); |
986 |
|
} |
987 |
|
|
988 |
/* Allocate buffer. */ |
/* Allocate buffer. */ |
989 |
buffer = ccs_alloc(buffer_len); |
buffer = ccs_alloc(buffer_len); |
990 |
if (!buffer) return -ENOMEM; |
if (!buffer) return -ENOMEM; |
1049 |
|
|
1050 |
/* Set argv[0] */ |
/* Set argv[0] */ |
1051 |
{ |
{ |
1052 |
retval = copy_strings_kernel(1, &alt_exec, bprm); |
retval = copy_strings_kernel(1, &execute_handler, bprm); |
1053 |
if (retval < 0) goto out; |
if (retval < 0) goto out; |
1054 |
bprm->argc++; |
bprm->argc++; |
1055 |
} |
} |
1057 |
bprm->argv_len = bprm->exec - bprm->p; |
bprm->argv_len = bprm->exec - bprm->p; |
1058 |
#endif |
#endif |
1059 |
|
|
1060 |
/* OK, now restart the process with the alternative program's dentry. */ |
/* OK, now restart the process with execute handler program's dentry. */ |
1061 |
filp = open_exec(alt_exec); |
filp = open_exec(execute_handler); |
1062 |
if (IS_ERR(filp)) { |
if (IS_ERR(filp)) { |
1063 |
retval = PTR_ERR(filp); |
retval = PTR_ERR(filp); |
1064 |
goto out; |
goto out; |
1065 |
} |
} |
1066 |
bprm->file= filp; |
bprm->file= filp; |
1067 |
bprm->filename = alt_exec; |
bprm->filename = execute_handler; |
1068 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
1069 |
bprm->interp = alt_exec; |
bprm->interp = execute_handler; |
1070 |
#endif |
#endif |
1071 |
retval = 0; |
retval = prepare_binprm(bprm); |
1072 |
|
if (retval < 0) goto out; |
1073 |
|
task->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR; |
1074 |
|
retval = FindNextDomain(bprm, next_domain, filename); |
1075 |
|
task->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR; |
1076 |
out: |
out: |
1077 |
/* Free buffer. */ |
/* Free buffer. */ |
1078 |
ccs_free(buffer); |
ccs_free(buffer); |
1079 |
return retval; |
return retval; |
1080 |
} |
} |
1081 |
|
|
1082 |
|
static const struct path_info *FindExecuteHandler(bool is_preferred_handler) |
1083 |
|
{ |
1084 |
|
const struct domain_info *domain = current->domain_info; |
1085 |
|
struct acl_info *ptr; |
1086 |
|
const u8 type = is_preferred_handler ? TYPE_PREFERRED_EXECUTE_HANDLER : TYPE_DEFAULT_EXECUTE_HANDLER; |
1087 |
|
list1_for_each_entry(ptr, &domain->acl_info_list, list) { |
1088 |
|
struct execute_handler_record *acl; |
1089 |
|
if (ptr->type != type) continue; |
1090 |
|
acl = container_of(ptr, struct execute_handler_record, head); |
1091 |
|
return acl->handler; |
1092 |
|
} |
1093 |
|
return NULL; |
1094 |
|
} |
1095 |
|
|
1096 |
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) |
1097 |
{ |
{ |
1098 |
struct domain_info *next_domain = NULL, *prev_domain = current->domain_info; |
struct task_struct *task = current; |
1099 |
|
struct domain_info *next_domain = NULL, *prev_domain = task->domain_info; |
1100 |
|
const struct path_info *handler; |
1101 |
int retval; |
int retval; |
1102 |
char *work = NULL; /* Keep valid until search_binary_handler() finishes. */ |
char *work = NULL; /* Keep valid until search_binary_handler() finishes. */ |
1103 |
CCS_LoadPolicy(bprm->filename); |
CCS_LoadPolicy(bprm->filename); |
1104 |
if (prev_domain->flags & DOMAIN_FLAGS_FORCE_ALT_EXEC) retval = -EPERM; |
//printk("rootdepth=%d\n", GetRootDepth()); |
1105 |
else retval = FindNextDomain(bprm, &next_domain, 1); |
handler = FindExecuteHandler(true); |
1106 |
if (retval == -EPERM && try_alt_exec(bprm, &work) == 0 && prepare_binprm(bprm) >= 0) { |
if (handler) { |
1107 |
current->tomoyo_flags |= CCS_DONT_SLEEP_ON_ENFORCE_ERROR; |
retval = try_alt_exec(bprm, handler, &work, &next_domain); |
1108 |
retval = FindNextDomain(bprm, &next_domain, 0); |
} else if ((retval = FindNextDomain(bprm, &next_domain, NULL)) == -EPERM) { |
1109 |
current->tomoyo_flags &= ~CCS_DONT_SLEEP_ON_ENFORCE_ERROR; |
handler = FindExecuteHandler(false); |
1110 |
} |
if (handler) retval = try_alt_exec(bprm, handler, &work, &next_domain); |
1111 |
if (retval == 0) { |
} |
1112 |
current->domain_info = next_domain; |
if (retval) goto out; |
1113 |
retval = CheckEnviron(bprm); |
task->domain_info = next_domain; |
1114 |
current->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
retval = CheckEnviron(bprm); |
1115 |
if (!retval) retval = search_binary_handler(bprm, regs); |
if (retval) goto out; |
1116 |
current->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
task->tomoyo_flags |= TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
1117 |
if (retval < 0) current->domain_info = prev_domain; |
retval = search_binary_handler(bprm, regs); |
1118 |
} |
task->tomoyo_flags &= ~TOMOYO_CHECK_READ_FOR_OPEN_EXEC; |
1119 |
|
out: |
1120 |
|
if (retval < 0) task->domain_info = prev_domain; |
1121 |
ccs_free(work); |
ccs_free(work); |
1122 |
return retval; |
return retval; |
1123 |
} |
} |