Add CONFIG_SECURITY_WRITABLE_HOOKS=n check.
@@ -1107,6 +1107,71 @@ | ||
1107 | 1107 | } |
1108 | 1108 | } |
1109 | 1109 | |
1110 | +#if defined(CONFIG_STRICT_KERNEL_RWX) && !defined(SECURITY_WRITABLE_HOOKS) | |
1111 | +#include <linux/uaccess.h> /* probe_kernel_write() */ | |
1112 | +#define NEED_TO_CHECK_HOOKS_ARE_WRITABLE | |
1113 | + | |
1114 | +#if defined(CONFIG_X86) | |
1115 | +#define MAX_RO_PAGES 1024 | |
1116 | +static struct page *ro_pages[MAX_RO_PAGES] __initdata; | |
1117 | +static unsigned int ro_pages_len __initdata; | |
1118 | + | |
1119 | +static bool __init lsm_test_page_ro(void *addr) | |
1120 | +{ | |
1121 | + unsigned int i; | |
1122 | + int unused; | |
1123 | + struct page *page; | |
1124 | + | |
1125 | + page = (struct page *) lookup_address((unsigned long) addr, &unused); | |
1126 | + if (!page) | |
1127 | + return false; | |
1128 | + if (test_bit(_PAGE_BIT_RW, &(page->flags))) | |
1129 | + return true; | |
1130 | + for (i = 0; i < ro_pages_len; i++) | |
1131 | + if (page == ro_pages[i]) | |
1132 | + return true; | |
1133 | + if (ro_pages_len == MAX_RO_PAGES) | |
1134 | + return false; | |
1135 | + ro_pages[ro_pages_len++] = page; | |
1136 | + return true; | |
1137 | +} | |
1138 | + | |
1139 | +static bool __init check_ro_pages(struct security_hook_heads *hooks) | |
1140 | +{ | |
1141 | + int i; | |
1142 | + struct list_head *list = (struct list_head *) hooks; | |
1143 | + | |
1144 | + if (!probe_kernel_write(&list->next, list->next, sizeof(void *))) | |
1145 | + return true; | |
1146 | + for (i = 0; i < ARRAY_SIZE(caitsith_hooks); i++) { | |
1147 | + const unsigned int idx = | |
1148 | + ((unsigned long) caitsith_hooks[i].head | |
1149 | + - (unsigned long) hooks) | |
1150 | + / sizeof(struct list_head); | |
1151 | + struct list_head *self = &list[idx]; | |
1152 | + struct list_head *prev = self->prev; | |
1153 | + | |
1154 | + if (!lsm_test_page_ro(&prev->next) || | |
1155 | + !lsm_test_page_ro(&self->prev)) | |
1156 | + return false; | |
1157 | + if (!list_empty(self) && | |
1158 | + !lsm_test_page_ro(&list_last_entry | |
1159 | + (self, struct security_hook_list, | |
1160 | + list)->hook)) | |
1161 | + return false; | |
1162 | + } | |
1163 | + return true; | |
1164 | +} | |
1165 | +#else | |
1166 | +static bool __init check_ro_pages(struct security_hook_heads *hooks) | |
1167 | +{ | |
1168 | + return !probe_kernel_write(&((struct list_head *) hooks)->next, | |
1169 | + ((struct list_head *) hooks)->next, | |
1170 | + sizeof(void *)); | |
1171 | +} | |
1172 | +#endif | |
1173 | +#endif | |
1174 | + | |
1110 | 1175 | /** |
1111 | 1176 | * cs_init - Initialize this module. |
1112 | 1177 | * |
@@ -1123,6 +1188,12 @@ | ||
1123 | 1188 | caitsith_hooks[idx].head = ((void *) hooks) |
1124 | 1189 | + ((unsigned long) caitsith_hooks[idx].head) |
1125 | 1190 | - ((unsigned long) &probe_dummy_security_hook_heads); |
1191 | +#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) | |
1192 | + if (!check_ro_pages(hooks)) { | |
1193 | + printk(KERN_INFO "Can't update security_hook_heads due to write protected. Retry with rodata=0 kernel command line option added.\n"); | |
1194 | + return -EINVAL; | |
1195 | + } | |
1196 | +#endif | |
1126 | 1197 | caitsith_exports.find_task_by_vpid = probe_find_task_by_vpid(); |
1127 | 1198 | if (!caitsith_exports.find_task_by_vpid) |
1128 | 1199 | goto out; |
@@ -1135,11 +1206,19 @@ | ||
1135 | 1206 | for (idx = 0; idx < CS_MAX_TASK_SECURITY_HASH; idx++) |
1136 | 1207 | INIT_LIST_HEAD(&cs_task_security_list[idx]); |
1137 | 1208 | cs_init_module(); |
1209 | +#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86) | |
1210 | + for (idx = 0; idx < ro_pages_len; idx++) | |
1211 | + set_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags)); | |
1212 | +#endif | |
1138 | 1213 | swap_hook(&caitsith_hooks[0], &original_task_free); |
1139 | 1214 | swap_hook(&caitsith_hooks[1], &original_cred_prepare); |
1140 | 1215 | swap_hook(&caitsith_hooks[2], &original_task_alloc); |
1141 | 1216 | for (idx = 3; idx < ARRAY_SIZE(caitsith_hooks); idx++) |
1142 | 1217 | add_hook(&caitsith_hooks[idx]); |
1218 | +#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86) | |
1219 | + for (idx = 0; idx < ro_pages_len; idx++) | |
1220 | + clear_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags)); | |
1221 | +#endif | |
1143 | 1222 | return 0; |
1144 | 1223 | out: |
1145 | 1224 | return -EINVAL; |