(メッセージはありません)
@@ -1,4401 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/policy_io.c | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#include "internal.h" | |
10 | - | |
11 | -/***** SECTION1: Constants definition *****/ | |
12 | - | |
13 | -/* Define this to enable debug mode. */ | |
14 | -/* #define DEBUG_CONDITION */ | |
15 | - | |
16 | -#ifdef DEBUG_CONDITION | |
17 | -#define dprintk printk | |
18 | -#else | |
19 | -#define dprintk(...) do { } while (0) | |
20 | -#endif | |
21 | - | |
22 | -/* String table for operation. */ | |
23 | -static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX] = { | |
24 | - [CCS_MAC_EXECUTE] = "execute", | |
25 | - [CCS_MAC_READ] = "read", | |
26 | - [CCS_MAC_WRITE] = "write", | |
27 | - [CCS_MAC_APPEND] = "append", | |
28 | - [CCS_MAC_CREATE] = "create", | |
29 | - [CCS_MAC_UNLINK] = "unlink", | |
30 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
31 | - [CCS_MAC_GETATTR] = "getattr", | |
32 | -#endif | |
33 | - [CCS_MAC_MKDIR] = "mkdir", | |
34 | - [CCS_MAC_RMDIR] = "rmdir", | |
35 | - [CCS_MAC_MKFIFO] = "mkfifo", | |
36 | - [CCS_MAC_MKSOCK] = "mksock", | |
37 | - [CCS_MAC_TRUNCATE] = "truncate", | |
38 | - [CCS_MAC_SYMLINK] = "symlink", | |
39 | - [CCS_MAC_MKBLOCK] = "mkblock", | |
40 | - [CCS_MAC_MKCHAR] = "mkchar", | |
41 | - [CCS_MAC_LINK] = "link", | |
42 | - [CCS_MAC_RENAME] = "rename", | |
43 | - [CCS_MAC_CHMOD] = "chmod", | |
44 | - [CCS_MAC_CHOWN] = "chown", | |
45 | - [CCS_MAC_CHGRP] = "chgrp", | |
46 | - [CCS_MAC_IOCTL] = "ioctl", | |
47 | - [CCS_MAC_CHROOT] = "chroot", | |
48 | - [CCS_MAC_MOUNT] = "mount", | |
49 | - [CCS_MAC_UMOUNT] = "unmount", | |
50 | - [CCS_MAC_PIVOT_ROOT] = "pivot_root", | |
51 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
52 | - [CCS_MAC_INET_STREAM_BIND] = "inet_stream_bind", | |
53 | - [CCS_MAC_INET_STREAM_LISTEN] = "inet_stream_listen", | |
54 | - [CCS_MAC_INET_STREAM_CONNECT] = "inet_stream_connect", | |
55 | - [CCS_MAC_INET_STREAM_ACCEPT] = "inet_stream_accept", | |
56 | - [CCS_MAC_INET_DGRAM_BIND] = "inet_dgram_bind", | |
57 | - [CCS_MAC_INET_DGRAM_SEND] = "inet_dgram_send", | |
58 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
59 | - [CCS_MAC_INET_DGRAM_RECV] = "inet_dgram_recv", | |
60 | -#endif | |
61 | - [CCS_MAC_INET_RAW_BIND] = "inet_raw_bind", | |
62 | - [CCS_MAC_INET_RAW_SEND] = "inet_raw_send", | |
63 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
64 | - [CCS_MAC_INET_RAW_RECV] = "inet_raw_recv", | |
65 | -#endif | |
66 | - [CCS_MAC_UNIX_STREAM_BIND] = "unix_stream_bind", | |
67 | - [CCS_MAC_UNIX_STREAM_LISTEN] = "unix_stream_listen", | |
68 | - [CCS_MAC_UNIX_STREAM_CONNECT] = "unix_stream_connect", | |
69 | - [CCS_MAC_UNIX_STREAM_ACCEPT] = "unix_stream_accept", | |
70 | - [CCS_MAC_UNIX_DGRAM_BIND] = "unix_dgram_bind", | |
71 | - [CCS_MAC_UNIX_DGRAM_SEND] = "unix_dgram_send", | |
72 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
73 | - [CCS_MAC_UNIX_DGRAM_RECV] = "unix_dgram_recv", | |
74 | -#endif | |
75 | - [CCS_MAC_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", | |
76 | - [CCS_MAC_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", | |
77 | - [CCS_MAC_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", | |
78 | - [CCS_MAC_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", | |
79 | -#endif | |
80 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
81 | - [CCS_MAC_ENVIRON] = "environ", | |
82 | -#endif | |
83 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
84 | - [CCS_MAC_PTRACE] = "ptrace", | |
85 | -#endif | |
86 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
87 | - [CCS_MAC_SIGNAL] = "signal", | |
88 | -#endif | |
89 | - [CCS_MAC_MODIFY_POLICY] = "modify_policy", | |
90 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
91 | - [CCS_MAC_USE_NETLINK_SOCKET] = "use_netlink_socket", | |
92 | - [CCS_MAC_USE_PACKET_SOCKET] = "use_packet_socket", | |
93 | - [CCS_MAC_USE_REBOOT] = "use_reboot", | |
94 | - [CCS_MAC_USE_VHANGUP] = "use_vhangup", | |
95 | - [CCS_MAC_SET_TIME] = "set_time", | |
96 | - [CCS_MAC_SET_PRIORITY] = "set_priority", | |
97 | - [CCS_MAC_SET_HOSTNAME] = "set_hostname", | |
98 | - [CCS_MAC_USE_KERNEL_MODULE] = "use_kernel_module", | |
99 | - [CCS_MAC_USE_NEW_KERNEL] = "use_new_kernel", | |
100 | -#endif | |
101 | -#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
102 | - [CCS_MAC_AUTO_DOMAIN_TRANSITION] = "auto_domain_transition", | |
103 | -#endif | |
104 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
105 | - [CCS_MAC_MANUAL_DOMAIN_TRANSITION] = "manual_domain_transition", | |
106 | -#endif | |
107 | -}; | |
108 | - | |
109 | -/* String table for conditions. */ | |
110 | -static const char *const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { | |
111 | - [CCS_SELF_UID] = "uid", | |
112 | - [CCS_SELF_EUID] = "euid", | |
113 | - [CCS_SELF_SUID] = "suid", | |
114 | - [CCS_SELF_FSUID] = "fsuid", | |
115 | - [CCS_SELF_GID] = "gid", | |
116 | - [CCS_SELF_EGID] = "egid", | |
117 | - [CCS_SELF_SGID] = "sgid", | |
118 | - [CCS_SELF_FSGID] = "fsgid", | |
119 | - [CCS_SELF_PID] = "pid", | |
120 | - [CCS_SELF_PPID] = "ppid", | |
121 | - [CCS_TASK_TYPE] = "type", | |
122 | - [CCS_SELF_DOMAIN] = "domain", | |
123 | - [CCS_SELF_EXE] = "exe", | |
124 | - [CCS_EXEC_ARGC] = "argc", | |
125 | - [CCS_EXEC_ENVC] = "envc", | |
126 | - [CCS_OBJ_IS_SOCKET] = "socket", | |
127 | - [CCS_OBJ_IS_SYMLINK] = "symlink", | |
128 | - [CCS_OBJ_IS_FILE] = "file", | |
129 | - [CCS_OBJ_IS_BLOCK_DEV] = "block", | |
130 | - [CCS_OBJ_IS_DIRECTORY] = "directory", | |
131 | - [CCS_OBJ_IS_CHAR_DEV] = "char", | |
132 | - [CCS_OBJ_IS_FIFO] = "fifo", | |
133 | - [CCS_MODE_SETUID] = "setuid", | |
134 | - [CCS_MODE_SETGID] = "setgid", | |
135 | - [CCS_MODE_STICKY] = "sticky", | |
136 | - [CCS_MODE_OWNER_READ] = "owner_read", | |
137 | - [CCS_MODE_OWNER_WRITE] = "owner_write", | |
138 | - [CCS_MODE_OWNER_EXECUTE] = "owner_execute", | |
139 | - [CCS_MODE_GROUP_READ] = "group_read", | |
140 | - [CCS_MODE_GROUP_WRITE] = "group_write", | |
141 | - [CCS_MODE_GROUP_EXECUTE] = "group_execute", | |
142 | - [CCS_MODE_OTHERS_READ] = "others_read", | |
143 | - [CCS_MODE_OTHERS_WRITE] = "others_write", | |
144 | - [CCS_MODE_OTHERS_EXECUTE] = "others_execute", | |
145 | - [CCS_TASK_EXECUTE_HANDLER] = "execute_handler", | |
146 | - [CCS_HANDLER_PATH] = "handler", | |
147 | - [CCS_TRANSIT_DOMAIN] = "transition", | |
148 | -}; | |
149 | - | |
150 | -/* String table for file attributes. */ | |
151 | -static const char *const ccs_path_attribute[CCS_MAX_PATH_ATTRIBUTE] = { | |
152 | - [CCS_PATH_ATTRIBUTE_UID] = "uid", | |
153 | - [CCS_PATH_ATTRIBUTE_GID] = "gid", | |
154 | - [CCS_PATH_ATTRIBUTE_INO] = "ino", | |
155 | - [CCS_PATH_ATTRIBUTE_MAJOR] = "major", | |
156 | - [CCS_PATH_ATTRIBUTE_MINOR] = "minor", | |
157 | - [CCS_PATH_ATTRIBUTE_PERM] = "perm", | |
158 | - [CCS_PATH_ATTRIBUTE_TYPE] = "type", | |
159 | - [CCS_PATH_ATTRIBUTE_DEV_MAJOR] = "dev_major", | |
160 | - [CCS_PATH_ATTRIBUTE_DEV_MINOR] = "dev_minor", | |
161 | - [CCS_PATH_ATTRIBUTE_FSMAGIC] = "fsmagic", | |
162 | -}; | |
163 | - | |
164 | -/* String table for grouping keywords. */ | |
165 | -static const char * const ccs_group_name[CCS_MAX_GROUP] = { | |
166 | - [CCS_STRING_GROUP] = "string_group", | |
167 | - [CCS_NUMBER_GROUP] = "number_group", | |
168 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
169 | - [CCS_IP_GROUP] = "ip_group", | |
170 | -#endif | |
171 | -}; | |
172 | - | |
173 | -/* String table for stat info. */ | |
174 | -static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { | |
175 | - [CCS_MEMORY_POLICY] = "policy", | |
176 | - [CCS_MEMORY_AUDIT] = "audit", | |
177 | - [CCS_MEMORY_QUERY] = "query", | |
178 | -}; | |
179 | - | |
180 | -/***** SECTION2: Structure definition *****/ | |
181 | - | |
182 | -struct iattr; | |
183 | - | |
184 | -/* Structure for query. */ | |
185 | -struct ccs_query { | |
186 | - struct list_head list; | |
187 | - struct ccs_acl_info *acl; | |
188 | - char *query; | |
189 | - size_t query_len; | |
190 | - unsigned int serial; | |
191 | - u8 timer; | |
192 | - u8 answer; | |
193 | - u8 retry; | |
194 | -}; | |
195 | - | |
196 | -/* Structure for audit log. */ | |
197 | -struct ccs_log { | |
198 | - struct list_head list; | |
199 | - char *log; | |
200 | - int size; | |
201 | - enum ccs_matching_result result; | |
202 | -}; | |
203 | - | |
204 | -/* Structure for holding single condition component. */ | |
205 | -struct ccs_cond_tmp { | |
206 | - u8 left; | |
207 | - u8 right; | |
208 | - bool is_not; | |
209 | - u8 radix; | |
210 | - struct ccs_group *group; | |
211 | - const struct ccs_path_info *path; | |
212 | - struct in6_addr ipv6[2]; | |
213 | - unsigned long value[2]; | |
214 | - unsigned long argv; | |
215 | - const struct ccs_path_info *envp; | |
216 | -}; | |
217 | - | |
218 | -/***** SECTION3: Prototype definition section *****/ | |
219 | - | |
220 | -static bool ccs_correct_domain(const unsigned char *domainname); | |
221 | -static bool ccs_correct_word(const char *string); | |
222 | -static bool ccs_flush(struct ccs_io_buffer *head); | |
223 | -static bool ccs_print_condition(struct ccs_io_buffer *head, | |
224 | - const struct ccs_condition *cond); | |
225 | -static bool ccs_memory_ok(const void *ptr, const unsigned int size); | |
226 | -static bool ccs_read_acl(struct ccs_io_buffer *head, | |
227 | - const struct ccs_acl_info *acl); | |
228 | -static bool ccs_read_group(struct ccs_io_buffer *head); | |
229 | -static bool ccs_select_acl(struct ccs_io_buffer *head, const char *data); | |
230 | -static bool ccs_set_lf(struct ccs_io_buffer *head); | |
231 | -static bool ccs_str_starts(char **src, const char *find); | |
232 | -static char *ccs_init_log(struct ccs_request_info *r); | |
233 | -static char *ccs_print_bprm(struct linux_binprm *bprm, | |
234 | - struct ccs_page_dump *dump); | |
235 | -static char *ccs_print_trailer(struct ccs_request_info *r); | |
236 | -static char *ccs_read_token(struct ccs_io_buffer *head); | |
237 | -static const char *ccs_yesno(const unsigned int value); | |
238 | -static const struct ccs_path_info *ccs_get_dqword(char *start); | |
239 | -static const struct ccs_path_info *ccs_get_name(const char *name); | |
240 | -static int __init ccs_init_module(void); | |
241 | -static int ccs_open(struct inode *inode, struct file *file); | |
242 | -static int ccs_parse_policy(struct ccs_io_buffer *head, char *line); | |
243 | -static int ccs_release(struct inode *inode, struct file *file); | |
244 | -static int ccs_supervisor(struct ccs_request_info *r); | |
245 | -static int ccs_update_group(struct ccs_io_buffer *head, | |
246 | - const enum ccs_group_id type); | |
247 | -static int ccs_write_answer(struct ccs_io_buffer *head); | |
248 | -static int ccs_write_audit_quota(char *data); | |
249 | -static int ccs_write_memory_quota(char *data); | |
250 | -static int ccs_write_pid(struct ccs_io_buffer *head); | |
251 | -static int ccs_write_policy(struct ccs_io_buffer *head); | |
252 | -static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | |
253 | - loff_t *ppos); | |
254 | -static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | |
255 | - loff_t *ppos); | |
256 | -static ssize_t ccs_write(struct file *file, const char __user *buf, | |
257 | - size_t count, loff_t *ppos); | |
258 | -static struct ccs_condition *ccs_get_condition(struct ccs_io_buffer *head); | |
259 | -static struct ccs_domain_info *ccs_find_domain(const char *domainname); | |
260 | -static struct ccs_acl_info *ccs_find_acl_by_qid(unsigned int serial); | |
261 | -static struct ccs_group *ccs_get_group(struct ccs_io_buffer *head, | |
262 | - const enum ccs_group_id idx); | |
263 | -static enum ccs_value_type ccs_parse_ulong(unsigned long *result, char **str); | |
264 | -static unsigned int ccs_poll(struct file *file, poll_table *wait); | |
265 | -static void __init ccs_create_entry(const char *name, const umode_t mode, | |
266 | - struct proc_dir_entry *parent, | |
267 | - const u8 key); | |
268 | -static void __init ccs_load_builtin_policy(void); | |
269 | -static void __init ccs_policy_io_init(void); | |
270 | -static void __init ccs_proc_init(void); | |
271 | -static void ccs_check_profile(void); | |
272 | -static void ccs_convert_time(time_t time, struct ccs_time *stamp); | |
273 | -static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | |
274 | - __printf(2, 3); | |
275 | -static void ccs_normalize_line(unsigned char *buffer); | |
276 | -static void ccs_read_log(struct ccs_io_buffer *head); | |
277 | -static void ccs_read_pid(struct ccs_io_buffer *head); | |
278 | -static void ccs_read_policy(struct ccs_io_buffer *head); | |
279 | -static void ccs_read_query(struct ccs_io_buffer *head); | |
280 | -static void *ccs_commit_ok(void *data, const unsigned int size); | |
281 | -static bool ccs_read_quota(struct ccs_io_buffer *head); | |
282 | -static void ccs_read_stat(struct ccs_io_buffer *head); | |
283 | -static void ccs_read_version(struct ccs_io_buffer *head); | |
284 | -static void ccs_set_space(struct ccs_io_buffer *head); | |
285 | -static void ccs_set_string(struct ccs_io_buffer *head, const char *string); | |
286 | -static void ccs_update_stat(const u8 index); | |
287 | -static void ccs_write_log(struct ccs_request_info *r); | |
288 | - | |
289 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
290 | -static enum ccs_ipaddr_type ccs_parse_ipaddr(char *address, | |
291 | - struct in6_addr ipv6[2]); | |
292 | -static void ccs_print_ipv4(struct ccs_io_buffer *head, const u32 *ip); | |
293 | -static void ccs_print_ipv6(struct ccs_io_buffer *head, | |
294 | - const struct in6_addr *ip); | |
295 | -static void ccs_print_ip(struct ccs_io_buffer *head, | |
296 | - struct ccs_ip_group *member); | |
297 | -#endif | |
298 | - | |
299 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
300 | -static ssize_t ccs_write_self(struct file *file, const char __user *buf, | |
301 | - size_t count, loff_t *ppos); | |
302 | -#endif | |
303 | - | |
304 | -/***** SECTION4: Standalone functions section *****/ | |
305 | - | |
306 | -/** | |
307 | - * ccs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. | |
308 | - * | |
309 | - * @time: Seconds since 1970/01/01 00:00:00. | |
310 | - * @stamp: Pointer to "struct ccs_time". | |
311 | - * | |
312 | - * Returns nothing. | |
313 | - * | |
314 | - * This function does not handle Y2038 problem. | |
315 | - */ | |
316 | -static void ccs_convert_time(time_t time, struct ccs_time *stamp) | |
317 | -{ | |
318 | - static const u16 ccs_eom[2][12] = { | |
319 | - { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | |
320 | - { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | |
321 | - }; | |
322 | - u16 y; | |
323 | - u8 m; | |
324 | - bool r; | |
325 | - stamp->sec = time % 60; | |
326 | - time /= 60; | |
327 | - stamp->min = time % 60; | |
328 | - time /= 60; | |
329 | - stamp->hour = time % 24; | |
330 | - time /= 24; | |
331 | - for (y = 1970; ; y++) { | |
332 | - const unsigned short days = (y & 3) ? 365 : 366; | |
333 | - if (time < days) | |
334 | - break; | |
335 | - time -= days; | |
336 | - } | |
337 | - r = (y & 3) == 0; | |
338 | - for (m = 0; m < 11 && time >= ccs_eom[r][m]; m++); | |
339 | - if (m) | |
340 | - time -= ccs_eom[r][m - 1]; | |
341 | - stamp->year = y; | |
342 | - stamp->month = ++m; | |
343 | - stamp->day = ++time; | |
344 | -} | |
345 | - | |
346 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
347 | - | |
348 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) | |
349 | - | |
350 | -/* | |
351 | - * Routines for printing IPv4 or IPv6 address. | |
352 | - * These are copied from include/linux/kernel.h include/net/ipv6.h | |
353 | - * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. | |
354 | - */ | |
355 | -static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) | |
356 | -{ | |
357 | - return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); | |
358 | -} | |
359 | - | |
360 | -static char *ip4_string(char *p, const u8 *addr) | |
361 | -{ | |
362 | - /* | |
363 | - * Since this function is called outside vsnprintf(), I can use | |
364 | - * sprintf() here. | |
365 | - */ | |
366 | - return p + | |
367 | - sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); | |
368 | -} | |
369 | - | |
370 | -static char *ip6_compressed_string(char *p, const char *addr) | |
371 | -{ | |
372 | - int i, j, range; | |
373 | - unsigned char zerolength[8]; | |
374 | - int longest = 1; | |
375 | - int colonpos = -1; | |
376 | - u16 word; | |
377 | - u8 hi, lo; | |
378 | - bool needcolon = false; | |
379 | - bool useIPv4; | |
380 | - struct in6_addr in6; | |
381 | - | |
382 | - memcpy(&in6, addr, sizeof(struct in6_addr)); | |
383 | - | |
384 | - useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); | |
385 | - | |
386 | - memset(zerolength, 0, sizeof(zerolength)); | |
387 | - | |
388 | - if (useIPv4) | |
389 | - range = 6; | |
390 | - else | |
391 | - range = 8; | |
392 | - | |
393 | - /* find position of longest 0 run */ | |
394 | - for (i = 0; i < range; i++) { | |
395 | - for (j = i; j < range; j++) { | |
396 | - if (in6.s6_addr16[j] != 0) | |
397 | - break; | |
398 | - zerolength[i]++; | |
399 | - } | |
400 | - } | |
401 | - for (i = 0; i < range; i++) { | |
402 | - if (zerolength[i] > longest) { | |
403 | - longest = zerolength[i]; | |
404 | - colonpos = i; | |
405 | - } | |
406 | - } | |
407 | - if (longest == 1) /* don't compress a single 0 */ | |
408 | - colonpos = -1; | |
409 | - | |
410 | - /* emit address */ | |
411 | - for (i = 0; i < range; i++) { | |
412 | - if (i == colonpos) { | |
413 | - if (needcolon || i == 0) | |
414 | - *p++ = ':'; | |
415 | - *p++ = ':'; | |
416 | - needcolon = false; | |
417 | - i += longest - 1; | |
418 | - continue; | |
419 | - } | |
420 | - if (needcolon) { | |
421 | - *p++ = ':'; | |
422 | - needcolon = false; | |
423 | - } | |
424 | - /* hex u16 without leading 0s */ | |
425 | - word = ntohs(in6.s6_addr16[i]); | |
426 | - hi = word >> 8; | |
427 | - lo = word & 0xff; | |
428 | - if (hi) { | |
429 | - if (hi > 0x0f) | |
430 | - p = pack_hex_byte(p, hi); | |
431 | - else | |
432 | - *p++ = hex_asc_lo(hi); | |
433 | - p = pack_hex_byte(p, lo); | |
434 | - } else if (lo > 0x0f) | |
435 | - p = pack_hex_byte(p, lo); | |
436 | - else | |
437 | - *p++ = hex_asc_lo(lo); | |
438 | - needcolon = true; | |
439 | - } | |
440 | - | |
441 | - if (useIPv4) { | |
442 | - if (needcolon) | |
443 | - *p++ = ':'; | |
444 | - p = ip4_string(p, &in6.s6_addr[12]); | |
445 | - } | |
446 | - *p = '\0'; | |
447 | - | |
448 | - return p; | |
449 | -} | |
450 | -#endif | |
451 | - | |
452 | -/** | |
453 | - * ccs_print_ipv4 - Print an IPv4 address. | |
454 | - * | |
455 | - * @head: Pointer to "struct ccs_io_buffer". | |
456 | - * @ip: Pointer to "u32" in network byte order. | |
457 | - * | |
458 | - * Returns nothing. | |
459 | - */ | |
460 | -static void ccs_print_ipv4(struct ccs_io_buffer *head, const u32 *ip) | |
461 | -{ | |
462 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
463 | - ccs_io_printf(head, "%pI4", ip); | |
464 | -#else | |
465 | - char addr[sizeof("255.255.255.255")]; | |
466 | - ip4_string(addr, (const u8 *) ip); | |
467 | - ccs_io_printf(head, "%s", addr); | |
468 | -#endif | |
469 | -} | |
470 | - | |
471 | -/** | |
472 | - * ccs_print_ipv6 - Print an IPv6 address. | |
473 | - * | |
474 | - * @head: Pointer to "struct ccs_io_buffer". | |
475 | - * @ip: Pointer to "struct in6_addr". | |
476 | - * | |
477 | - * Returns nothing. | |
478 | - */ | |
479 | -static void ccs_print_ipv6(struct ccs_io_buffer *head, | |
480 | - const struct in6_addr *ip) | |
481 | -{ | |
482 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
483 | - ccs_io_printf(head, "%pI6c", ip); | |
484 | -#else | |
485 | - char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:" | |
486 | - "255.255.255.255")]; | |
487 | - ip6_compressed_string(addr, (const u8 *) ip); | |
488 | - ccs_io_printf(head, "%s", addr); | |
489 | -#endif | |
490 | -} | |
491 | - | |
492 | -/** | |
493 | - * ccs_print_ip - Print an IP address. | |
494 | - * | |
495 | - * @head: Pointer to "struct ccs_io_buffer". | |
496 | - * @member: Pointer to "struct ccs_ip_group". | |
497 | - * | |
498 | - * Returns nothing. | |
499 | - */ | |
500 | -static void ccs_print_ip(struct ccs_io_buffer *head, | |
501 | - struct ccs_ip_group *member) | |
502 | -{ | |
503 | - u8 i; | |
504 | - for (i = 0; i < 2; i++) { | |
505 | - if (member->is_ipv6) | |
506 | - ccs_print_ipv6(head, &member->ip[i]); | |
507 | - else | |
508 | - ccs_print_ipv4(head, (const u32 *) &member->ip[i]); | |
509 | - if (i) | |
510 | - break; | |
511 | - if (!memcmp(&member->ip[0], &member->ip[1], 16)) | |
512 | - break; | |
513 | - ccs_set_string(head, "-"); | |
514 | - } | |
515 | -} | |
516 | - | |
517 | -#endif | |
518 | - | |
519 | -/** | |
520 | - * ccs_get_sarg - Get attribute name of CCS_SARG argument. | |
521 | - * | |
522 | - * @type: One of values in "enum ccs_mac_index". | |
523 | - * @index: Index to return. | |
524 | - * | |
525 | - * Returns attribute name. | |
526 | - */ | |
527 | -static const char *ccs_get_sarg(const enum ccs_mac_index type, const u8 index) | |
528 | -{ | |
529 | - switch (type) { | |
530 | - case CCS_MAC_LINK: | |
531 | - case CCS_MAC_RENAME: | |
532 | - if (index == 0) | |
533 | - return "old_path"; | |
534 | - if (index == 1) | |
535 | - return "new_path"; | |
536 | - break; | |
537 | - case CCS_MAC_MOUNT: | |
538 | - if (index == 0) | |
539 | - return "source"; | |
540 | - if (index == 1) | |
541 | - return "target"; | |
542 | - if (index == 2) | |
543 | - return "fstype"; | |
544 | - if (index == 3) | |
545 | - return "data"; | |
546 | - break; | |
547 | - case CCS_MAC_PIVOT_ROOT: | |
548 | - if (index == 0) | |
549 | - return "new_root"; | |
550 | - if (index == 1) | |
551 | - return "put_old"; | |
552 | - break; | |
553 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
554 | - case CCS_MAC_ENVIRON: | |
555 | - if (index == 2) | |
556 | - return "name"; | |
557 | - if (index == 3) | |
558 | - return "value"; | |
559 | - /* fall through */ | |
560 | -#endif | |
561 | - case CCS_MAC_EXECUTE: | |
562 | - if (index == 0) | |
563 | - return "path"; | |
564 | - if (index == 1) | |
565 | - return "exec"; | |
566 | - break; | |
567 | - case CCS_MAC_SYMLINK: | |
568 | - if (index == 0) | |
569 | - return "path"; | |
570 | - if (index == 1) | |
571 | - return "target"; | |
572 | - break; | |
573 | - case CCS_MAC_READ: | |
574 | - case CCS_MAC_WRITE: | |
575 | - case CCS_MAC_APPEND: | |
576 | - case CCS_MAC_UNLINK: | |
577 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
578 | - case CCS_MAC_GETATTR: | |
579 | -#endif | |
580 | - case CCS_MAC_RMDIR: | |
581 | - case CCS_MAC_TRUNCATE: | |
582 | - case CCS_MAC_CHROOT: | |
583 | - case CCS_MAC_CHMOD: | |
584 | - case CCS_MAC_CHOWN: | |
585 | - case CCS_MAC_CHGRP: | |
586 | - case CCS_MAC_IOCTL: | |
587 | - case CCS_MAC_MKDIR: | |
588 | - case CCS_MAC_CREATE: | |
589 | - case CCS_MAC_MKFIFO: | |
590 | - case CCS_MAC_MKSOCK: | |
591 | - case CCS_MAC_MKBLOCK: | |
592 | - case CCS_MAC_MKCHAR: | |
593 | - case CCS_MAC_UMOUNT: | |
594 | - if (index == 0) | |
595 | - return "path"; | |
596 | - break; | |
597 | - case CCS_MAC_MODIFY_POLICY: | |
598 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
599 | - case CCS_MAC_USE_NETLINK_SOCKET: | |
600 | - case CCS_MAC_USE_PACKET_SOCKET: | |
601 | - case CCS_MAC_USE_REBOOT: | |
602 | - case CCS_MAC_USE_VHANGUP: | |
603 | - case CCS_MAC_SET_TIME: | |
604 | - case CCS_MAC_SET_PRIORITY: | |
605 | - case CCS_MAC_SET_HOSTNAME: | |
606 | - case CCS_MAC_USE_KERNEL_MODULE: | |
607 | - case CCS_MAC_USE_NEW_KERNEL: | |
608 | -#endif | |
609 | - break; | |
610 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
611 | - case CCS_MAC_INET_STREAM_BIND: | |
612 | - case CCS_MAC_INET_STREAM_LISTEN: | |
613 | - case CCS_MAC_INET_STREAM_CONNECT: | |
614 | - case CCS_MAC_INET_STREAM_ACCEPT: | |
615 | - case CCS_MAC_INET_DGRAM_BIND: | |
616 | - case CCS_MAC_INET_DGRAM_SEND: | |
617 | - case CCS_MAC_INET_DGRAM_RECV: | |
618 | - case CCS_MAC_INET_RAW_BIND: | |
619 | - case CCS_MAC_INET_RAW_SEND: | |
620 | - case CCS_MAC_INET_RAW_RECV: | |
621 | - if (index == 0) | |
622 | - return "ip"; | |
623 | - break; | |
624 | - case CCS_MAC_UNIX_STREAM_BIND: | |
625 | - case CCS_MAC_UNIX_STREAM_LISTEN: | |
626 | - case CCS_MAC_UNIX_STREAM_CONNECT: | |
627 | - case CCS_MAC_UNIX_STREAM_ACCEPT: | |
628 | - case CCS_MAC_UNIX_DGRAM_BIND: | |
629 | - case CCS_MAC_UNIX_DGRAM_SEND: | |
630 | - case CCS_MAC_UNIX_DGRAM_RECV: | |
631 | - case CCS_MAC_UNIX_SEQPACKET_BIND: | |
632 | - case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
633 | - case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
634 | - case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
635 | - if (index == 0) | |
636 | - return "addr"; | |
637 | - break; | |
638 | -#endif | |
639 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
640 | - case CCS_MAC_PTRACE: | |
641 | - if (index == 0) | |
642 | - return "domain"; | |
643 | - break; | |
644 | -#endif | |
645 | - default: | |
646 | - break; | |
647 | - } | |
648 | - return "unknown"; /* This should not happen. */ | |
649 | -} | |
650 | - | |
651 | -/** | |
652 | - * ccs_get_narg - Get attribute name of CCS_NARG argument. | |
653 | - * | |
654 | - * @type: One of values in "enum ccs_mac_index". | |
655 | - * @index: Index to return. | |
656 | - * | |
657 | - * Returns attribute name. | |
658 | - */ | |
659 | -static const char *ccs_get_narg(const enum ccs_mac_index type, const u8 index) | |
660 | -{ | |
661 | - switch (type) { | |
662 | - case CCS_MAC_MOUNT: | |
663 | - case CCS_MAC_UMOUNT: | |
664 | - if (index == 0) | |
665 | - return "flags"; | |
666 | - break; | |
667 | - case CCS_MAC_CHMOD: | |
668 | - if (index == 0) | |
669 | - return "perm"; | |
670 | - break; | |
671 | - case CCS_MAC_CHOWN: | |
672 | - if (index == 0) | |
673 | - return "uid"; | |
674 | - break; | |
675 | - case CCS_MAC_CHGRP: | |
676 | - if (index == 0) | |
677 | - return "gid"; | |
678 | - break; | |
679 | - case CCS_MAC_IOCTL: | |
680 | - if (index == 0) | |
681 | - return "cmd"; | |
682 | - break; | |
683 | - case CCS_MAC_MKDIR: | |
684 | - case CCS_MAC_CREATE: | |
685 | - case CCS_MAC_MKFIFO: | |
686 | - case CCS_MAC_MKSOCK: | |
687 | - if (index == 0) | |
688 | - return "perm"; | |
689 | - break; | |
690 | - case CCS_MAC_MKBLOCK: | |
691 | - case CCS_MAC_MKCHAR: | |
692 | - if (index == 0) | |
693 | - return "perm"; | |
694 | - if (index == 1) | |
695 | - return "dev_major"; | |
696 | - if (index == 2) | |
697 | - return "dev_minor"; | |
698 | - break; | |
699 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
700 | - case CCS_MAC_INET_STREAM_BIND: | |
701 | - case CCS_MAC_INET_STREAM_LISTEN: | |
702 | - case CCS_MAC_INET_STREAM_CONNECT: | |
703 | - case CCS_MAC_INET_STREAM_ACCEPT: | |
704 | - case CCS_MAC_INET_DGRAM_BIND: | |
705 | - case CCS_MAC_INET_DGRAM_SEND: | |
706 | - case CCS_MAC_INET_DGRAM_RECV: | |
707 | - if (index == 0) | |
708 | - return "port"; | |
709 | - break; | |
710 | - case CCS_MAC_INET_RAW_BIND: | |
711 | - case CCS_MAC_INET_RAW_SEND: | |
712 | - case CCS_MAC_INET_RAW_RECV: | |
713 | - if (index == 0) | |
714 | - return "proto"; | |
715 | - break; | |
716 | -#endif | |
717 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
718 | - case CCS_MAC_PTRACE: | |
719 | - if (index == 0) | |
720 | - return "cmd"; | |
721 | - break; | |
722 | -#endif | |
723 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
724 | - case CCS_MAC_SIGNAL: | |
725 | - if (index == 0) | |
726 | - return "cmd"; | |
727 | - break; | |
728 | -#endif | |
729 | - default: | |
730 | - break; | |
731 | - } | |
732 | - return "unknown"; /* This should not happen. */ | |
733 | -} | |
734 | - | |
735 | -/***** SECTION5: Variables definition section *****/ | |
736 | - | |
737 | -/* Lock for protecting policy. */ | |
738 | -DEFINE_MUTEX(ccs_policy_lock); | |
739 | - | |
740 | -/* Has /sbin/init started? */ | |
741 | -bool ccs_policy_loaded; | |
742 | - | |
743 | -/* List of "struct ccs_group". */ | |
744 | -struct list_head ccs_group_list[CCS_MAX_GROUP]; | |
745 | -/* Policy version. Currently only 20120401 is defined. */ | |
746 | -static unsigned int ccs_policy_version = 20120401; | |
747 | - | |
748 | -/* List of "struct ccs_condition". */ | |
749 | -LIST_HEAD(ccs_condition_list); | |
750 | - | |
751 | -/* Wait queue for kernel -> userspace notification. */ | |
752 | -static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); | |
753 | -/* Wait queue for userspace -> kernel notification. */ | |
754 | -static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); | |
755 | - | |
756 | -/* The list for "struct ccs_query". */ | |
757 | -static LIST_HEAD(ccs_query_list); | |
758 | - | |
759 | -/* Lock for manipulating ccs_query_list. */ | |
760 | -static DEFINE_SPINLOCK(ccs_query_list_lock); | |
761 | - | |
762 | -/* Number of "struct file" referring /proc/ccs/query interface. */ | |
763 | -static atomic_t ccs_query_observers = ATOMIC_INIT(0); | |
764 | - | |
765 | -/* Wait queue for /proc/ccs/audit. */ | |
766 | -static DECLARE_WAIT_QUEUE_HEAD(ccs_log_wait); | |
767 | - | |
768 | -/* The list for "struct ccs_log". */ | |
769 | -static LIST_HEAD(ccs_log); | |
770 | - | |
771 | -/* Lock for "struct list_head ccs_log". */ | |
772 | -static DEFINE_SPINLOCK(ccs_log_lock); | |
773 | - | |
774 | -/* Length of "stuct list_head ccs_log". */ | |
775 | -static unsigned int ccs_log_count[CCS_MAX_MATCHING]; | |
776 | -/* Quota for audit logs. */ | |
777 | -static unsigned int ccs_log_quota[CCS_MAX_LOG_QUOTA][CCS_MAX_MATCHING]; | |
778 | - | |
779 | -/* Memoy currently used by policy/audit log/query. */ | |
780 | -unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | |
781 | - | |
782 | -/* Memory quota for "policy"/"audit log"/"query". */ | |
783 | -static unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; | |
784 | - | |
785 | -/* The list for "struct ccs_name". */ | |
786 | -struct list_head ccs_name_list[CCS_MAX_HASH]; | |
787 | - | |
788 | -/* Timestamp counter for last updated. */ | |
789 | -static unsigned int ccs_stat_updated[CCS_MAX_POLICY_STAT]; | |
790 | - | |
791 | -/* Counter for number of updates. */ | |
792 | -static unsigned int ccs_stat_modified[CCS_MAX_POLICY_STAT]; | |
793 | - | |
794 | -/* Operations for /proc/ccs/self_domain interface. */ | |
795 | -static const struct file_operations ccs_self_operations = { | |
796 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
797 | - .write = ccs_write_self, | |
798 | -#endif | |
799 | - .read = ccs_read_self, | |
800 | -}; | |
801 | - | |
802 | -/* Operations for /proc/ccs/ interface. */ | |
803 | -static const struct file_operations ccs_operations = { | |
804 | - .open = ccs_open, | |
805 | - .release = ccs_release, | |
806 | - .poll = ccs_poll, | |
807 | - .read = ccs_read, | |
808 | - .write = ccs_write, | |
809 | -}; | |
810 | - | |
811 | -/***** SECTION6: Dependent functions section *****/ | |
812 | - | |
813 | -/** | |
814 | - * list_for_each_cookie - iterate over a list with cookie. | |
815 | - * | |
816 | - * @pos: Pointer to "struct list_head". | |
817 | - * @head: Pointer to "struct list_head". | |
818 | - */ | |
819 | -#define list_for_each_cookie(pos, head) \ | |
820 | - for (pos = pos ? pos : srcu_dereference((head)->next, &ccs_ss); \ | |
821 | - pos != (head); pos = srcu_dereference(pos->next, &ccs_ss)) | |
822 | - | |
823 | -/** | |
824 | - * ccs_warn_oom - Print out of memory warning message. | |
825 | - * | |
826 | - * @function: Function's name. | |
827 | - * | |
828 | - * Returns nothing. | |
829 | - */ | |
830 | -void ccs_warn_oom(const char *function) | |
831 | -{ | |
832 | - /* Reduce error messages. */ | |
833 | - static pid_t ccs_last_pid; | |
834 | - const pid_t pid = current->pid; | |
835 | - if (ccs_last_pid != pid) { | |
836 | - printk(KERN_WARNING "ERROR: Out of memory at %s.\n", | |
837 | - function); | |
838 | - ccs_last_pid = pid; | |
839 | - } | |
840 | - if (!ccs_policy_loaded) | |
841 | - panic("MAC Initialization failed.\n"); | |
842 | -} | |
843 | - | |
844 | -/** | |
845 | - * ccs_memory_ok - Check memory quota. | |
846 | - * | |
847 | - * @ptr: Pointer to allocated memory. Maybe NULL. | |
848 | - * @size: Size in byte. Not used if @ptr is NULL. | |
849 | - * | |
850 | - * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | |
851 | - * | |
852 | - * Caller holds ccs_policy_lock mutex. | |
853 | - */ | |
854 | -static bool ccs_memory_ok(const void *ptr, const unsigned int size) | |
855 | -{ | |
856 | - if (ptr) { | |
857 | - const size_t s = ccs_round2(size); | |
858 | - ccs_memory_used[CCS_MEMORY_POLICY] += s; | |
859 | - if (!ccs_memory_quota[CCS_MEMORY_POLICY] || | |
860 | - ccs_memory_used[CCS_MEMORY_POLICY] <= | |
861 | - ccs_memory_quota[CCS_MEMORY_POLICY]) | |
862 | - return true; | |
863 | - ccs_memory_used[CCS_MEMORY_POLICY] -= s; | |
864 | - } | |
865 | - ccs_warn_oom(__func__); | |
866 | - return false; | |
867 | -} | |
868 | - | |
869 | -/** | |
870 | - * ccs_get_name - Allocate memory for string data. | |
871 | - * | |
872 | - * @name: The string to store into the permernent memory. Maybe NULL. | |
873 | - * | |
874 | - * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | |
875 | - */ | |
876 | -static const struct ccs_path_info *ccs_get_name(const char *name) | |
877 | -{ | |
878 | - struct ccs_name *ptr; | |
879 | - unsigned int hash; | |
880 | - int len; | |
881 | - int allocated_len; | |
882 | - struct list_head *head; | |
883 | - | |
884 | - if (!name) | |
885 | - return NULL; | |
886 | - len = strlen(name) + 1; | |
887 | - hash = full_name_hash((const unsigned char *) name, len - 1); | |
888 | - head = &ccs_name_list[hash_long(hash, CCS_HASH_BITS)]; | |
889 | - if (mutex_lock_interruptible(&ccs_policy_lock)) | |
890 | - return NULL; | |
891 | - list_for_each_entry(ptr, head, head.list) { | |
892 | - if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || | |
893 | - atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | |
894 | - continue; | |
895 | - atomic_inc(&ptr->head.users); | |
896 | - goto out; | |
897 | - } | |
898 | - allocated_len = sizeof(*ptr) + len; | |
899 | - ptr = kzalloc(allocated_len, GFP_NOFS); | |
900 | - if (ccs_memory_ok(ptr, allocated_len)) { | |
901 | - ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | |
902 | - memmove((char *) ptr->entry.name, name, len); | |
903 | - atomic_set(&ptr->head.users, 1); | |
904 | - ccs_fill_path_info(&ptr->entry); | |
905 | - ptr->size = allocated_len; | |
906 | - list_add_tail(&ptr->head.list, head); | |
907 | - } else { | |
908 | - kfree(ptr); | |
909 | - ptr = NULL; | |
910 | - } | |
911 | -out: | |
912 | - mutex_unlock(&ccs_policy_lock); | |
913 | - return ptr ? &ptr->entry : NULL; | |
914 | -} | |
915 | - | |
916 | -/** | |
917 | - * ccs_read_token - Read a word from a line. | |
918 | - * | |
919 | - * @head: Pointer to "struct ccs_io_buffer". | |
920 | - * | |
921 | - * Returns a word on success, "" otherwise. | |
922 | - * | |
923 | - * To allow the caller to skip NULL check, this function returns "" rather than | |
924 | - * NULL if there is no more words to read. | |
925 | - */ | |
926 | -static char *ccs_read_token(struct ccs_io_buffer *head) | |
927 | -{ | |
928 | - char *pos = head->w.data; | |
929 | - char *del = strchr(pos, ' '); | |
930 | - if (del) | |
931 | - *del++ = '\0'; | |
932 | - else | |
933 | - del = pos + strlen(pos); | |
934 | - head->w.data = del; | |
935 | - return pos; | |
936 | -} | |
937 | - | |
938 | -/** | |
939 | - * ccs_correct_word - Check whether the given string follows the naming rules. | |
940 | - * | |
941 | - * @string: The string to check. | |
942 | - * | |
943 | - * Returns true if @string follows the naming rules, false otherwise. | |
944 | - */ | |
945 | -static bool ccs_correct_word(const char *string) | |
946 | -{ | |
947 | - const char *const start = string; | |
948 | - u8 in_repetition = 0; | |
949 | - if (!*string) | |
950 | - goto out; | |
951 | - while (*string) { | |
952 | - unsigned char c = *string++; | |
953 | - if (in_repetition && c == '/') | |
954 | - goto out; | |
955 | - if (c <= ' ' || c >= 127) | |
956 | - goto out; | |
957 | - if (c != '\\') | |
958 | - continue; | |
959 | - c = *string++; | |
960 | - if (c >= '0' && c <= '3') { | |
961 | - unsigned char d; | |
962 | - unsigned char e; | |
963 | - d = *string++; | |
964 | - if (d < '0' || d > '7') | |
965 | - goto out; | |
966 | - e = *string++; | |
967 | - if (e < '0' || e > '7') | |
968 | - goto out; | |
969 | - c = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
970 | - if (c <= ' ' || c >= 127 || c == '\\') | |
971 | - continue; | |
972 | - goto out; | |
973 | - } | |
974 | - switch (c) { | |
975 | - case '$': /* "\$" */ | |
976 | - case '+': /* "\+" */ | |
977 | - case '?': /* "\?" */ | |
978 | - case '*': /* "\*" */ | |
979 | - case '@': /* "\@" */ | |
980 | - case 'x': /* "\x" */ | |
981 | - case 'X': /* "\X" */ | |
982 | - case 'a': /* "\a" */ | |
983 | - case 'A': /* "\A" */ | |
984 | - case '-': /* "\-" */ | |
985 | - continue; | |
986 | - case '{': /* "/\{" */ | |
987 | - if (string - 3 < start || *(string - 3) != '/') | |
988 | - goto out; | |
989 | - in_repetition = 1; | |
990 | - continue; | |
991 | - case '}': /* "\}/" */ | |
992 | - if (in_repetition != 1 || *string++ != '/') | |
993 | - goto out; | |
994 | - in_repetition = 0; | |
995 | - continue; | |
996 | - case '(': /* "/\(" */ | |
997 | - if (string - 3 < start || *(string - 3) != '/') | |
998 | - goto out; | |
999 | - in_repetition = 2; | |
1000 | - continue; | |
1001 | - case ')': /* "\)/" */ | |
1002 | - if (in_repetition != 2 || *string++ != '/') | |
1003 | - goto out; | |
1004 | - in_repetition = 0; | |
1005 | - continue; | |
1006 | - } | |
1007 | - goto out; | |
1008 | - } | |
1009 | - if (in_repetition) | |
1010 | - goto out; | |
1011 | - return true; | |
1012 | -out: | |
1013 | - return false; | |
1014 | -} | |
1015 | - | |
1016 | -/** | |
1017 | - * ccs_commit_ok - Allocate memory and check memory quota. | |
1018 | - * | |
1019 | - * @data: Data to copy from. | |
1020 | - * @size: Size in byte. | |
1021 | - * | |
1022 | - * Returns pointer to allocated memory on success, NULL otherwise. | |
1023 | - * @data is zero-cleared on success. | |
1024 | - * | |
1025 | - * Caller holds ccs_policy_lock mutex. | |
1026 | - */ | |
1027 | -static void *ccs_commit_ok(void *data, const unsigned int size) | |
1028 | -{ | |
1029 | - void *ptr = kmalloc(size, GFP_NOFS); | |
1030 | - if (ccs_memory_ok(ptr, size)) { | |
1031 | - memmove(ptr, data, size); | |
1032 | - memset(data, 0, size); | |
1033 | - return ptr; | |
1034 | - } | |
1035 | - kfree(ptr); | |
1036 | - return NULL; | |
1037 | -} | |
1038 | - | |
1039 | -/** | |
1040 | - * ccs_get_group - Allocate memory for "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group". | |
1041 | - * | |
1042 | - * @head: Pointer to "struct ccs_io_buffer". | |
1043 | - * @idx: Index number. | |
1044 | - * | |
1045 | - * Returns pointer to "struct ccs_group" on success, NULL otherwise. | |
1046 | - */ | |
1047 | -static struct ccs_group *ccs_get_group(struct ccs_io_buffer *head, | |
1048 | - const enum ccs_group_id idx) | |
1049 | -{ | |
1050 | - struct ccs_group e = { }; | |
1051 | - struct ccs_group *group = NULL; | |
1052 | - struct list_head *list; | |
1053 | - const char *group_name = ccs_read_token(head); | |
1054 | - bool found = false; | |
1055 | - if (!ccs_correct_word(group_name) || idx >= CCS_MAX_GROUP) | |
1056 | - return NULL; | |
1057 | - e.group_name = ccs_get_name(group_name); | |
1058 | - if (!e.group_name) | |
1059 | - return NULL; | |
1060 | - if (mutex_lock_interruptible(&ccs_policy_lock)) | |
1061 | - goto out; | |
1062 | - list = &ccs_group_list[idx]; | |
1063 | - list_for_each_entry(group, list, head.list) { | |
1064 | - if (e.group_name != group->group_name || | |
1065 | - atomic_read(&group->head.users) == CCS_GC_IN_PROGRESS) | |
1066 | - continue; | |
1067 | - atomic_inc(&group->head.users); | |
1068 | - found = true; | |
1069 | - break; | |
1070 | - } | |
1071 | - if (!found) { | |
1072 | - struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); | |
1073 | - if (entry) { | |
1074 | - INIT_LIST_HEAD(&entry->member_list); | |
1075 | - atomic_set(&entry->head.users, 1); | |
1076 | - list_add_tail_rcu(&entry->head.list, list); | |
1077 | - group = entry; | |
1078 | - found = true; | |
1079 | - } | |
1080 | - } | |
1081 | - mutex_unlock(&ccs_policy_lock); | |
1082 | -out: | |
1083 | - ccs_put_name(e.group_name); | |
1084 | - return found ? group : NULL; | |
1085 | -} | |
1086 | - | |
1087 | -/** | |
1088 | - * ccs_parse_ulong - Parse an "unsigned long" value. | |
1089 | - * | |
1090 | - * @result: Pointer to "unsigned long". | |
1091 | - * @str: Pointer to string to parse. | |
1092 | - * | |
1093 | - * Returns one of values in "enum ccs_value_type". | |
1094 | - * | |
1095 | - * The @src is updated to point the first character after the value | |
1096 | - * on success. | |
1097 | - */ | |
1098 | -static enum ccs_value_type ccs_parse_ulong(unsigned long *result, char **str) | |
1099 | -{ | |
1100 | - const char *cp = *str; | |
1101 | - char *ep; | |
1102 | - int base = 10; | |
1103 | - if (*cp == '0') { | |
1104 | - char c = *(cp + 1); | |
1105 | - if (c == 'x' || c == 'X') { | |
1106 | - base = 16; | |
1107 | - cp += 2; | |
1108 | - } else if (c >= '0' && c <= '7') { | |
1109 | - base = 8; | |
1110 | - cp++; | |
1111 | - } | |
1112 | - } | |
1113 | - *result = simple_strtoul(cp, &ep, base); | |
1114 | - if (cp == ep) | |
1115 | - return CCS_VALUE_TYPE_INVALID; | |
1116 | - *str = ep; | |
1117 | - switch (base) { | |
1118 | - case 16: | |
1119 | - return CCS_VALUE_TYPE_HEXADECIMAL; | |
1120 | - case 8: | |
1121 | - return CCS_VALUE_TYPE_OCTAL; | |
1122 | - default: | |
1123 | - return CCS_VALUE_TYPE_DECIMAL; | |
1124 | - } | |
1125 | -} | |
1126 | - | |
1127 | -/** | |
1128 | - * ccs_get_dqword - ccs_get_name() for a quoted string. | |
1129 | - * | |
1130 | - * @start: String to parse. | |
1131 | - * | |
1132 | - * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | |
1133 | - */ | |
1134 | -static const struct ccs_path_info *ccs_get_dqword(char *start) | |
1135 | -{ | |
1136 | - char *cp = start + strlen(start) - 1; | |
1137 | - if (cp == start || *start++ != '"' || *cp != '"') | |
1138 | - return NULL; | |
1139 | - *cp = '\0'; | |
1140 | - if (*start && !ccs_correct_word(start)) | |
1141 | - return NULL; | |
1142 | - return ccs_get_name(start); | |
1143 | -} | |
1144 | - | |
1145 | -/** | |
1146 | - * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. | |
1147 | - * | |
1148 | - * @a: Pointer to "struct ccs_condition". | |
1149 | - * @b: Pointer to "struct ccs_condition". | |
1150 | - * | |
1151 | - * Returns true if @a == @b, false otherwise. | |
1152 | - */ | |
1153 | -static inline bool ccs_same_condition(const struct ccs_condition *a, | |
1154 | - const struct ccs_condition *b) | |
1155 | -{ | |
1156 | - return a->size == b->size && | |
1157 | - !memcmp(a + 1, b + 1, a->size - sizeof(*a)); | |
1158 | -} | |
1159 | - | |
1160 | -/** | |
1161 | - * ccs_commit_condition - Commit "struct ccs_condition". | |
1162 | - * | |
1163 | - * @entry: Pointer to "struct ccs_condition". | |
1164 | - * | |
1165 | - * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | |
1166 | - * | |
1167 | - * This function merges duplicated entries. This function returns NULL if | |
1168 | - * @entry is not duplicated but memory quota for policy has exceeded. | |
1169 | - */ | |
1170 | -static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) | |
1171 | -{ | |
1172 | - struct ccs_condition *ptr; | |
1173 | - bool found = false; | |
1174 | - if (mutex_lock_interruptible(&ccs_policy_lock)) { | |
1175 | - dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); | |
1176 | - ptr = NULL; | |
1177 | - found = true; | |
1178 | - goto out; | |
1179 | - } | |
1180 | - list_for_each_entry(ptr, &ccs_condition_list, head.list) { | |
1181 | - if (!ccs_same_condition(ptr, entry) || | |
1182 | - atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | |
1183 | - continue; | |
1184 | - /* Same entry found. Share this entry. */ | |
1185 | - atomic_inc(&ptr->head.users); | |
1186 | - found = true; | |
1187 | - break; | |
1188 | - } | |
1189 | - if (!found) { | |
1190 | - if (ccs_memory_ok(entry, entry->size)) { | |
1191 | - atomic_set(&entry->head.users, 1); | |
1192 | - list_add(&entry->head.list, &ccs_condition_list); | |
1193 | - } else { | |
1194 | - found = true; | |
1195 | - ptr = NULL; | |
1196 | - } | |
1197 | - } | |
1198 | - mutex_unlock(&ccs_policy_lock); | |
1199 | -out: | |
1200 | - if (found) { | |
1201 | - ccs_del_condition(&entry->head.list); | |
1202 | - kfree(entry); | |
1203 | - entry = ptr; | |
1204 | - } | |
1205 | - return entry; | |
1206 | -} | |
1207 | - | |
1208 | -/** | |
1209 | - * ccs_correct_domain - Check whether the given domainname follows the naming rules. | |
1210 | - * | |
1211 | - * @domainname: The domainname to check. | |
1212 | - * | |
1213 | - * Returns true if @domainname follows the naming rules, false otherwise. | |
1214 | - */ | |
1215 | -static bool ccs_correct_domain(const unsigned char *domainname) | |
1216 | -{ | |
1217 | - if (!ccs_correct_word(domainname)) | |
1218 | - return false; | |
1219 | - while (*domainname) { | |
1220 | - if (*domainname++ != '\\') | |
1221 | - continue; | |
1222 | - if (*domainname < '0' || *domainname++ > '3') | |
1223 | - return false; | |
1224 | - } | |
1225 | - return true; | |
1226 | -} | |
1227 | - | |
1228 | -/** | |
1229 | - * ccs_normalize_line - Format string. | |
1230 | - * | |
1231 | - * @buffer: The line to normalize. | |
1232 | - * | |
1233 | - * Returns nothing. | |
1234 | - * | |
1235 | - * Leading and trailing whitespaces are removed. | |
1236 | - * Multiple whitespaces are packed into single space. | |
1237 | - */ | |
1238 | -static void ccs_normalize_line(unsigned char *buffer) | |
1239 | -{ | |
1240 | - unsigned char *sp = buffer; | |
1241 | - unsigned char *dp = buffer; | |
1242 | - bool first = true; | |
1243 | - while (*sp && (*sp <= ' ' || *sp >= 127)) | |
1244 | - sp++; | |
1245 | - while (*sp) { | |
1246 | - if (!first) | |
1247 | - *dp++ = ' '; | |
1248 | - first = false; | |
1249 | - while (*sp > ' ' && *sp < 127) | |
1250 | - *dp++ = *sp++; | |
1251 | - while (*sp && (*sp <= ' ' || *sp >= 127)) | |
1252 | - sp++; | |
1253 | - } | |
1254 | - *dp = '\0'; | |
1255 | -} | |
1256 | - | |
1257 | -/** | |
1258 | - * ccs_parse_values - Parse an numeric argument. | |
1259 | - * | |
1260 | - * @value: Values to parse. | |
1261 | - * @v: Pointer to "unsigned long". | |
1262 | - * | |
1263 | - * Returns "enum ccs_value_type" if @value is a single value, bitwise-OR-ed | |
1264 | - * value if @value is value range. | |
1265 | - */ | |
1266 | -static u8 ccs_parse_values(char *value, unsigned long v[2]) | |
1267 | -{ | |
1268 | - enum ccs_value_type radix1 = ccs_parse_ulong(&v[0], &value); | |
1269 | - enum ccs_value_type radix2; | |
1270 | - if (radix1 == CCS_VALUE_TYPE_INVALID) | |
1271 | - return CCS_VALUE_TYPE_INVALID; | |
1272 | - if (!*value) { | |
1273 | - v[1] = v[0]; | |
1274 | - return radix1; | |
1275 | - } | |
1276 | - if (*value++ != '-') | |
1277 | - return CCS_VALUE_TYPE_INVALID; | |
1278 | - radix2 = ccs_parse_ulong(&v[1], &value); | |
1279 | - if (radix2 == CCS_VALUE_TYPE_INVALID || *value || v[0] > v[1]) | |
1280 | - return CCS_VALUE_TYPE_INVALID; | |
1281 | - return radix1 | (radix2 << 2); | |
1282 | -} | |
1283 | - | |
1284 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1285 | - | |
1286 | -/** | |
1287 | - * ccs_parse_ipaddr - Parse an IP address. | |
1288 | - * | |
1289 | - * @address: Address to parse. | |
1290 | - * @ipv6: Pointer to "struct in6_addr". | |
1291 | - * | |
1292 | - * Returns one of values in "enum ccs_ipaddr_type". | |
1293 | - */ | |
1294 | -/* Index numbers for type of IP addresses. */ | |
1295 | -static enum ccs_ipaddr_type ccs_parse_ipaddr(char *address, | |
1296 | - struct in6_addr ipv6[2]) | |
1297 | -{ | |
1298 | - const char *end; | |
1299 | - if (!strchr(address, ':') && | |
1300 | - in4_pton(address, -1, ipv6[0].s6_addr, '-', &end) > 0) { | |
1301 | - if (!*end) { | |
1302 | - ipv6[0].s6_addr32[0] = ipv6[0].s6_addr32[0]; | |
1303 | - ipv6[1].s6_addr32[0] = ipv6[0].s6_addr32[0]; | |
1304 | - return CCS_ADDRESS_TYPE_IPV4; | |
1305 | - } | |
1306 | - if (*end++ != '-' || | |
1307 | - in4_pton(end, -1, ipv6[1].s6_addr, '\0', &end) <= 0 || | |
1308 | - *end || memcmp(&ipv6[0], &ipv6[1], 4) >= 0) | |
1309 | - return CCS_ADDRESS_TYPE_INVALID; | |
1310 | - return CCS_ADDRESS_TYPE_IPV4_RANGE; | |
1311 | - } | |
1312 | - if (in6_pton(address, -1, ipv6[0].s6_addr, '-', &end) > 0) { | |
1313 | - if (!*end) { | |
1314 | - ipv6[1] = ipv6[0]; | |
1315 | - return CCS_ADDRESS_TYPE_IPV6; | |
1316 | - } | |
1317 | - if (*end++ != '-' || | |
1318 | - in6_pton(end, -1, ipv6[1].s6_addr, '\0', &end) <= 0 || | |
1319 | - *end || memcmp(&ipv6[0], &ipv6[1], 16) >= 0) | |
1320 | - return CCS_ADDRESS_TYPE_INVALID; | |
1321 | - return CCS_ADDRESS_TYPE_IPV6_RANGE; | |
1322 | - } | |
1323 | - return CCS_ADDRESS_TYPE_INVALID; | |
1324 | -} | |
1325 | - | |
1326 | -#endif | |
1327 | - | |
1328 | -/** | |
1329 | - * ccs_parse_task_cond - Find index for variable's name. | |
1330 | - * | |
1331 | - * @word: Keyword to search. | |
1332 | - * | |
1333 | - * Returns one of "ccs_conditions_index" value. | |
1334 | - */ | |
1335 | -static enum ccs_conditions_index ccs_parse_task_cond(const char *word) | |
1336 | -{ | |
1337 | - if (!strncmp(word, "task.", 5)) { | |
1338 | - word += 5; | |
1339 | - if (!strcmp(word, "uid")) | |
1340 | - return CCS_SELF_UID; | |
1341 | - if (!strcmp(word, "euid")) | |
1342 | - return CCS_SELF_EUID; | |
1343 | - if (!strcmp(word, "suid")) | |
1344 | - return CCS_SELF_SUID; | |
1345 | - if (!strcmp(word, "fsuid")) | |
1346 | - return CCS_SELF_FSUID; | |
1347 | - if (!strcmp(word, "gid")) | |
1348 | - return CCS_SELF_GID; | |
1349 | - if (!strcmp(word, "egid")) | |
1350 | - return CCS_SELF_EGID; | |
1351 | - if (!strcmp(word, "sgid")) | |
1352 | - return CCS_SELF_SGID; | |
1353 | - if (!strcmp(word, "fsgid")) | |
1354 | - return CCS_SELF_FSGID; | |
1355 | - if (!strcmp(word, "pid")) | |
1356 | - return CCS_SELF_PID; | |
1357 | - if (!strcmp(word, "ppid")) | |
1358 | - return CCS_SELF_PPID; | |
1359 | - if (!strcmp(word, "type")) | |
1360 | - return CCS_TASK_TYPE; | |
1361 | - if (!strcmp(word, "domain")) | |
1362 | - return CCS_SELF_DOMAIN; | |
1363 | - if (!strcmp(word, "exe")) | |
1364 | - return CCS_SELF_EXE; | |
1365 | - } | |
1366 | - return CCS_MAX_CONDITION_KEYWORD; | |
1367 | -} | |
1368 | - | |
1369 | -/** | |
1370 | - * ccs_parse_syscall_arg - Find index for variable's name. | |
1371 | - * | |
1372 | - * @word: Keyword to search. | |
1373 | - * @type: One of values in "enum ccs_mac_index". | |
1374 | - * | |
1375 | - * Returns one of "ccs_conditions_index" value. | |
1376 | - */ | |
1377 | -static enum ccs_conditions_index ccs_parse_syscall_arg | |
1378 | -(const char *word, const enum ccs_mac_index type) | |
1379 | -{ | |
1380 | - switch (type) { | |
1381 | - case CCS_MAC_READ: | |
1382 | - case CCS_MAC_WRITE: | |
1383 | - case CCS_MAC_APPEND: | |
1384 | - case CCS_MAC_UNLINK: | |
1385 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
1386 | - case CCS_MAC_GETATTR: | |
1387 | -#endif | |
1388 | - case CCS_MAC_RMDIR: | |
1389 | - case CCS_MAC_TRUNCATE: | |
1390 | - case CCS_MAC_CHROOT: | |
1391 | - case CCS_MAC_CHOWN: | |
1392 | - case CCS_MAC_CHGRP: | |
1393 | - case CCS_MAC_IOCTL: | |
1394 | - case CCS_MAC_EXECUTE: | |
1395 | - case CCS_MAC_SYMLINK: | |
1396 | - if (!strcmp(word, "path")) | |
1397 | - return CCS_COND_SARG0; | |
1398 | - if (type == CCS_MAC_CHOWN && !strcmp(word, "uid")) | |
1399 | - return CCS_COND_NARG0; | |
1400 | - if (type == CCS_MAC_CHGRP && !strcmp(word, "gid")) | |
1401 | - return CCS_COND_NARG0; | |
1402 | - if (type == CCS_MAC_IOCTL && !strcmp(word, "cmd")) | |
1403 | - return CCS_COND_NARG0; | |
1404 | - if (type == CCS_MAC_EXECUTE && !strcmp(word, "exec")) | |
1405 | - return CCS_COND_SARG1; | |
1406 | - if (type == CCS_MAC_SYMLINK && !strcmp(word, "target")) | |
1407 | - return CCS_COND_SARG1; | |
1408 | - break; | |
1409 | - case CCS_MAC_CHMOD: | |
1410 | - case CCS_MAC_MKDIR: | |
1411 | - case CCS_MAC_CREATE: | |
1412 | - case CCS_MAC_MKFIFO: | |
1413 | - case CCS_MAC_MKSOCK: | |
1414 | - case CCS_MAC_MKBLOCK: | |
1415 | - case CCS_MAC_MKCHAR: | |
1416 | - if (!strcmp(word, "path")) | |
1417 | - return CCS_COND_SARG0; | |
1418 | - if (!strcmp(word, "perm")) | |
1419 | - return CCS_COND_NARG0; | |
1420 | - if (type == CCS_MAC_MKBLOCK || type == CCS_MAC_MKCHAR) { | |
1421 | - if (!strcmp(word, "dev_major")) | |
1422 | - return CCS_COND_NARG1; | |
1423 | - if (!strcmp(word, "dev_minor")) | |
1424 | - return CCS_COND_NARG2; | |
1425 | - } | |
1426 | - break; | |
1427 | - case CCS_MAC_LINK: | |
1428 | - case CCS_MAC_RENAME: | |
1429 | - if (!strcmp(word, "old_path")) | |
1430 | - return CCS_COND_SARG0; | |
1431 | - if (!strcmp(word, "new_path")) | |
1432 | - return CCS_COND_SARG1; | |
1433 | - break; | |
1434 | - case CCS_MAC_MOUNT: | |
1435 | - if (!strcmp(word, "source")) | |
1436 | - return CCS_COND_SARG0; | |
1437 | - if (!strcmp(word, "target")) | |
1438 | - return CCS_COND_SARG1; | |
1439 | - if (!strcmp(word, "fstype")) | |
1440 | - return CCS_COND_SARG2; | |
1441 | - if (!strcmp(word, "data")) | |
1442 | - return CCS_COND_SARG3; | |
1443 | - if (!strcmp(word, "flags")) | |
1444 | - return CCS_COND_NARG0; | |
1445 | - break; | |
1446 | - case CCS_MAC_UMOUNT: | |
1447 | - if (!strcmp(word, "path")) | |
1448 | - return CCS_COND_SARG0; | |
1449 | - if (!strcmp(word, "flags")) | |
1450 | - return CCS_COND_NARG0; | |
1451 | - break; | |
1452 | - case CCS_MAC_PIVOT_ROOT: | |
1453 | - if (!strcmp(word, "new_root")) | |
1454 | - return CCS_COND_SARG0; | |
1455 | - if (!strcmp(word, "put_old")) | |
1456 | - return CCS_COND_SARG1; | |
1457 | - break; | |
1458 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1459 | - case CCS_MAC_INET_STREAM_BIND: | |
1460 | - case CCS_MAC_INET_STREAM_LISTEN: | |
1461 | - case CCS_MAC_INET_STREAM_CONNECT: | |
1462 | - case CCS_MAC_INET_STREAM_ACCEPT: | |
1463 | - case CCS_MAC_INET_DGRAM_BIND: | |
1464 | - case CCS_MAC_INET_DGRAM_SEND: | |
1465 | - case CCS_MAC_INET_DGRAM_RECV: | |
1466 | - if (!strcmp(word, "ip")) | |
1467 | - return CCS_COND_IPARG; | |
1468 | - if (!strcmp(word, "port")) | |
1469 | - return CCS_COND_NARG0; | |
1470 | - break; | |
1471 | - case CCS_MAC_INET_RAW_BIND: | |
1472 | - case CCS_MAC_INET_RAW_SEND: | |
1473 | - case CCS_MAC_INET_RAW_RECV: | |
1474 | - if (!strcmp(word, "ip")) | |
1475 | - return CCS_COND_IPARG; | |
1476 | - if (!strcmp(word, "proto")) | |
1477 | - return CCS_COND_NARG0; | |
1478 | - break; | |
1479 | - case CCS_MAC_UNIX_STREAM_BIND: | |
1480 | - case CCS_MAC_UNIX_STREAM_LISTEN: | |
1481 | - case CCS_MAC_UNIX_STREAM_CONNECT: | |
1482 | - case CCS_MAC_UNIX_STREAM_ACCEPT: | |
1483 | - case CCS_MAC_UNIX_DGRAM_BIND: | |
1484 | - case CCS_MAC_UNIX_DGRAM_SEND: | |
1485 | - case CCS_MAC_UNIX_DGRAM_RECV: | |
1486 | - case CCS_MAC_UNIX_SEQPACKET_BIND: | |
1487 | - case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
1488 | - case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
1489 | - case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
1490 | - if (!strcmp(word, "addr")) | |
1491 | - return CCS_COND_SARG0; | |
1492 | - break; | |
1493 | -#endif | |
1494 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
1495 | - case CCS_MAC_ENVIRON: | |
1496 | - if (!strcmp(word, "path")) | |
1497 | - return CCS_COND_SARG0; | |
1498 | - if (!strcmp(word, "exec")) | |
1499 | - return CCS_COND_SARG1; | |
1500 | - if (!strcmp(word, "name")) | |
1501 | - return CCS_COND_SARG2; | |
1502 | - if (!strcmp(word, "value")) | |
1503 | - return CCS_COND_SARG3; | |
1504 | - break; | |
1505 | -#endif | |
1506 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
1507 | - case CCS_MAC_PTRACE: | |
1508 | - if (!strcmp(word, "domain")) | |
1509 | - return CCS_COND_DOMAIN; | |
1510 | - if (!strcmp(word, "cmd")) | |
1511 | - return CCS_COND_NARG0; | |
1512 | - break; | |
1513 | -#endif | |
1514 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
1515 | - case CCS_MAC_SIGNAL: | |
1516 | - if (!strcmp(word, "sig")) | |
1517 | - return CCS_COND_NARG0; | |
1518 | - break; | |
1519 | -#endif | |
1520 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
1521 | - case CCS_MAC_MANUAL_DOMAIN_TRANSITION: | |
1522 | - if (!strcmp(word, "domain")) | |
1523 | - return CCS_COND_DOMAIN; | |
1524 | - break; | |
1525 | -#endif | |
1526 | - default: | |
1527 | - break; | |
1528 | - } | |
1529 | - return CCS_MAX_CONDITION_KEYWORD; | |
1530 | -} | |
1531 | - | |
1532 | -/** | |
1533 | - * ccs_parse_path_attributes - Find index for variable's name. | |
1534 | - * | |
1535 | - * @word: Keyword to search. | |
1536 | - * @type: One of values in "enum ccs_mac_index". | |
1537 | - * | |
1538 | - * Returns one of "ccs_conditions_index" value. | |
1539 | - */ | |
1540 | -static enum ccs_conditions_index ccs_parse_path_attribute | |
1541 | -(char *word, const enum ccs_mac_index type) | |
1542 | -{ | |
1543 | - u8 i; | |
1544 | - enum ccs_conditions_index start; | |
1545 | - switch (type) { | |
1546 | - case CCS_MAC_READ: | |
1547 | - case CCS_MAC_WRITE: | |
1548 | - case CCS_MAC_APPEND: | |
1549 | - case CCS_MAC_UNLINK: | |
1550 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
1551 | - case CCS_MAC_GETATTR: | |
1552 | -#endif | |
1553 | - case CCS_MAC_RMDIR: | |
1554 | - case CCS_MAC_TRUNCATE: | |
1555 | - case CCS_MAC_CHROOT: | |
1556 | - case CCS_MAC_CHMOD: | |
1557 | - case CCS_MAC_CHOWN: | |
1558 | - case CCS_MAC_CHGRP: | |
1559 | - case CCS_MAC_IOCTL: | |
1560 | - case CCS_MAC_EXECUTE: | |
1561 | - case CCS_MAC_UMOUNT: | |
1562 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
1563 | - case CCS_MAC_ENVIRON: | |
1564 | -#endif | |
1565 | - if (ccs_str_starts(&word, "path")) | |
1566 | - goto path1; | |
1567 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
1568 | - if ((type == CCS_MAC_EXECUTE || type == CCS_MAC_ENVIRON) && | |
1569 | - ccs_str_starts(&word, "exec")) | |
1570 | - goto path2; | |
1571 | -#else | |
1572 | - if (type == CCS_MAC_EXECUTE && | |
1573 | - ccs_str_starts(&word, "exec")) | |
1574 | - goto path2; | |
1575 | -#endif | |
1576 | - break; | |
1577 | - case CCS_MAC_MKDIR: | |
1578 | - case CCS_MAC_CREATE: | |
1579 | - case CCS_MAC_MKFIFO: | |
1580 | - case CCS_MAC_MKSOCK: | |
1581 | - case CCS_MAC_MKBLOCK: | |
1582 | - case CCS_MAC_MKCHAR: | |
1583 | - case CCS_MAC_SYMLINK: | |
1584 | - if (ccs_str_starts(&word, "path")) | |
1585 | - goto path1_parent; | |
1586 | - break; | |
1587 | - case CCS_MAC_LINK: | |
1588 | - case CCS_MAC_RENAME: | |
1589 | - if (ccs_str_starts(&word, "old_path")) | |
1590 | - goto path1; | |
1591 | - if (ccs_str_starts(&word, "new_path")) | |
1592 | - goto path2_parent; | |
1593 | - break; | |
1594 | - case CCS_MAC_MOUNT: | |
1595 | - if (ccs_str_starts(&word, "source")) | |
1596 | - goto path1; | |
1597 | - if (ccs_str_starts(&word, "target")) | |
1598 | - goto path2; | |
1599 | - break; | |
1600 | - case CCS_MAC_PIVOT_ROOT: | |
1601 | - if (ccs_str_starts(&word, "new_root")) | |
1602 | - goto path1; | |
1603 | - if (ccs_str_starts(&word, "put_old")) | |
1604 | - goto path2; | |
1605 | - break; | |
1606 | - default: | |
1607 | - break; | |
1608 | - } | |
1609 | - goto out; | |
1610 | -path1_parent: | |
1611 | - if (strncmp(word, ".parent", 7)) | |
1612 | - goto out; | |
1613 | -path1: | |
1614 | - start = CCS_PATH_ATTRIBUTE_START; | |
1615 | - goto check; | |
1616 | -path2_parent: | |
1617 | - if (strncmp(word, ".parent", 7)) | |
1618 | - goto out; | |
1619 | -path2: | |
1620 | - start = CCS_PATH_ATTRIBUTE_START + 32; | |
1621 | -check: | |
1622 | - if (ccs_str_starts(&word, ".parent")) | |
1623 | - start += 16; | |
1624 | - if (*word++ == '.') | |
1625 | - for (i = 0; i < CCS_MAX_PATH_ATTRIBUTE; i++) | |
1626 | - if (!strcmp(word, ccs_path_attribute[i])) | |
1627 | - return start + i; | |
1628 | -out: | |
1629 | - return CCS_MAX_CONDITION_KEYWORD; | |
1630 | -} | |
1631 | - | |
1632 | -/** | |
1633 | - * ccs_find_pathtype - Find index for file's type. | |
1634 | - * | |
1635 | - * @word: Keyword to search. | |
1636 | - * | |
1637 | - * Returns one of "ccs_conditions_index" value. | |
1638 | - */ | |
1639 | -static enum ccs_conditions_index ccs_find_path_type(const char *word) | |
1640 | -{ | |
1641 | - if (!strcmp(word, "socket")) | |
1642 | - return CCS_OBJ_IS_SOCKET; | |
1643 | - if (!strcmp(word, "symlink")) | |
1644 | - return CCS_OBJ_IS_SYMLINK; | |
1645 | - if (!strcmp(word, "file")) | |
1646 | - return CCS_OBJ_IS_FILE; | |
1647 | - if (!strcmp(word, "block")) | |
1648 | - return CCS_OBJ_IS_BLOCK_DEV; | |
1649 | - if (!strcmp(word, "directory")) | |
1650 | - return CCS_OBJ_IS_DIRECTORY; | |
1651 | - if (!strcmp(word, "char")) | |
1652 | - return CCS_OBJ_IS_CHAR_DEV; | |
1653 | - if (!strcmp(word, "fifo")) | |
1654 | - return CCS_OBJ_IS_FIFO; | |
1655 | - return CCS_MAX_CONDITION_KEYWORD; | |
1656 | -} | |
1657 | - | |
1658 | -/** | |
1659 | - * ccs_find_path_perm - Find index for file's DAC attribute. | |
1660 | - * | |
1661 | - * @word: Keyword to search. | |
1662 | - * | |
1663 | - * Returns one of "ccs_conditions_index" value. | |
1664 | - */ | |
1665 | -static enum ccs_conditions_index ccs_find_path_perm(const char *word) | |
1666 | -{ | |
1667 | - if (!strcmp(word, "setuid")) | |
1668 | - return CCS_MODE_SETUID; | |
1669 | - if (!strcmp(word, "setgid")) | |
1670 | - return CCS_MODE_SETGID; | |
1671 | - if (!strcmp(word, "sticky")) | |
1672 | - return CCS_MODE_STICKY; | |
1673 | - if (!strcmp(word, "owner_read")) | |
1674 | - return CCS_MODE_OWNER_READ; | |
1675 | - if (!strcmp(word, "owner_write")) | |
1676 | - return CCS_MODE_OWNER_WRITE; | |
1677 | - if (!strcmp(word, "owner_execute")) | |
1678 | - return CCS_MODE_OWNER_EXECUTE; | |
1679 | - if (!strcmp(word, "group_read")) | |
1680 | - return CCS_MODE_GROUP_READ; | |
1681 | - if (!strcmp(word, "group_write")) | |
1682 | - return CCS_MODE_GROUP_WRITE; | |
1683 | - if (!strcmp(word, "group_execute")) | |
1684 | - return CCS_MODE_GROUP_EXECUTE; | |
1685 | - if (!strcmp(word, "others_read")) | |
1686 | - return CCS_MODE_OTHERS_READ; | |
1687 | - if (!strcmp(word, "others_write")) | |
1688 | - return CCS_MODE_OTHERS_WRITE; | |
1689 | - if (!strcmp(word, "others_execute")) | |
1690 | - return CCS_MODE_OTHERS_EXECUTE; | |
1691 | - return CCS_MAX_CONDITION_KEYWORD; | |
1692 | -} | |
1693 | - | |
1694 | -/** | |
1695 | - * ccs_parse_cond - Parse single condition. | |
1696 | - * | |
1697 | - * @tmp: Pointer to "struct ccs_cond_tmp". | |
1698 | - * @head: Pointer to "struct ccs_io_buffer". | |
1699 | - * | |
1700 | - * Returns true on success, false otherwise. | |
1701 | - */ | |
1702 | -static bool ccs_parse_cond(struct ccs_cond_tmp *tmp, | |
1703 | - struct ccs_io_buffer *head) | |
1704 | -{ | |
1705 | - enum ccs_group_id g; | |
1706 | - char *left = head->w.data; | |
1707 | - char *right; | |
1708 | - const enum ccs_mac_index type = head->w.acl_index; | |
1709 | - right = strchr(left, '='); | |
1710 | - if (!right || right == left) | |
1711 | - return false; | |
1712 | - *right++ = '\0'; | |
1713 | - tmp->is_not = (*(right - 2) == '!'); | |
1714 | - if (tmp->is_not) | |
1715 | - *(right - 2) = '\0'; | |
1716 | - if (!*left || !*right) | |
1717 | - return false; | |
1718 | - if (type == CCS_MAC_EXECUTE | |
1719 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
1720 | - || type == CCS_MAC_ENVIRON | |
1721 | -#endif | |
1722 | - ) { | |
1723 | - if (ccs_str_starts(&left, "argv[")) { | |
1724 | - tmp->left = CCS_ARGV_ENTRY; | |
1725 | - if (ccs_parse_ulong(&tmp->argv, &left) != | |
1726 | - CCS_VALUE_TYPE_DECIMAL || *left++ != ']' || *left) | |
1727 | - return false; | |
1728 | - } else if (ccs_str_starts(&left, "envp[")) { | |
1729 | - char *cp = left + strlen(left) - 1; | |
1730 | - tmp->left = CCS_ENVP_ENTRY; | |
1731 | - if (*cp != ']') | |
1732 | - return false; | |
1733 | - *cp = '\0'; | |
1734 | - tmp->envp = ccs_get_dqword(left); | |
1735 | - if (!tmp->envp) | |
1736 | - return false; | |
1737 | - } else if (!strcmp(left, "argc")) | |
1738 | - tmp->left = CCS_EXEC_ARGC; | |
1739 | - else if (!strcmp(left, "envc")) | |
1740 | - tmp->left = CCS_EXEC_ENVC; | |
1741 | - } | |
1742 | - if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1743 | - tmp->left = ccs_parse_syscall_arg(left, type); | |
1744 | - if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1745 | - tmp->left = ccs_parse_task_cond(left); | |
1746 | - if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1747 | - tmp->left = ccs_parse_path_attribute(left, type); | |
1748 | - if (tmp->left == CCS_MAX_CONDITION_KEYWORD) { | |
1749 | - /* | |
1750 | - * CCS_HANDLER_PATH and CCS_TRANSIT_DOMAIN are not for | |
1751 | - * comparison. | |
1752 | - */ | |
1753 | - if (tmp->is_not) | |
1754 | - return false; | |
1755 | - if (!strcmp(left, "handler")) | |
1756 | - tmp->left = CCS_HANDLER_PATH; | |
1757 | - else if (!strcmp(left, "transition")) | |
1758 | - tmp->left = CCS_TRANSIT_DOMAIN; | |
1759 | - else | |
1760 | - return false; | |
1761 | - tmp->right = CCS_IMM_NAME_ENTRY; | |
1762 | - if (!strcmp(right, "NULL")) { | |
1763 | - tmp->path = &ccs_null_name; | |
1764 | - } else { | |
1765 | - tmp->path = ccs_get_dqword(right); | |
1766 | - if (!tmp->path || | |
1767 | - tmp->path->const_len != tmp->path->total_len) | |
1768 | - return false; | |
1769 | - } | |
1770 | - return true; | |
1771 | - } | |
1772 | - switch (tmp->left) { | |
1773 | - case CCS_COND_DOMAIN: | |
1774 | - case CCS_SELF_DOMAIN: | |
1775 | - case CCS_ARGV_ENTRY: | |
1776 | - case CCS_ENVP_ENTRY: | |
1777 | - case CCS_COND_SARG0: | |
1778 | - case CCS_COND_SARG1: | |
1779 | - case CCS_COND_SARG2: | |
1780 | - case CCS_COND_SARG3: | |
1781 | - case CCS_SELF_EXE: | |
1782 | - g = CCS_STRING_GROUP; | |
1783 | - break; | |
1784 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1785 | - case CCS_COND_IPARG: | |
1786 | - g = CCS_IP_GROUP; | |
1787 | - break; | |
1788 | -#endif | |
1789 | - case CCS_TASK_TYPE: | |
1790 | - tmp->right = CCS_TASK_EXECUTE_HANDLER; | |
1791 | - return !strcmp(right, "execute_handler"); | |
1792 | - case CCS_PATH_ATTRIBUTE_START + CCS_PATH_ATTRIBUTE_TYPE: | |
1793 | - case CCS_PATH_ATTRIBUTE_START + 16 + CCS_PATH_ATTRIBUTE_TYPE: | |
1794 | - case CCS_PATH_ATTRIBUTE_START + 32 + CCS_PATH_ATTRIBUTE_TYPE: | |
1795 | - case CCS_PATH_ATTRIBUTE_START + 48 + CCS_PATH_ATTRIBUTE_TYPE: | |
1796 | - tmp->right = ccs_find_path_type(right); | |
1797 | - return tmp->right != CCS_MAX_CONDITION_KEYWORD; | |
1798 | - case CCS_PATH_ATTRIBUTE_START + CCS_PATH_ATTRIBUTE_PERM: | |
1799 | - case CCS_PATH_ATTRIBUTE_START + 16 + CCS_PATH_ATTRIBUTE_PERM: | |
1800 | - case CCS_PATH_ATTRIBUTE_START + 32 + CCS_PATH_ATTRIBUTE_PERM: | |
1801 | - case CCS_PATH_ATTRIBUTE_START + 48 + CCS_PATH_ATTRIBUTE_PERM: | |
1802 | - tmp->right = ccs_find_path_perm(right); | |
1803 | - if (tmp->right != CCS_MAX_CONDITION_KEYWORD) | |
1804 | - return true; | |
1805 | - /* fall through */ | |
1806 | - default: | |
1807 | - g = CCS_NUMBER_GROUP; | |
1808 | - } | |
1809 | - if (*right == '@') { | |
1810 | - tmp->right = CCS_IMM_GROUP; | |
1811 | - head->w.data = ++right; | |
1812 | - tmp->group = ccs_get_group(head, g); | |
1813 | - return tmp->group != NULL; | |
1814 | - } | |
1815 | - if (*right == '"') { | |
1816 | - if (g != CCS_STRING_GROUP) | |
1817 | - return false; | |
1818 | - tmp->right = CCS_IMM_NAME_ENTRY; | |
1819 | - tmp->path = ccs_get_dqword(right); | |
1820 | - return tmp->path != NULL; | |
1821 | - } | |
1822 | - if (tmp->left == CCS_ENVP_ENTRY) { | |
1823 | - tmp->right = CCS_IMM_NAME_ENTRY; | |
1824 | - tmp->path = &ccs_null_name; | |
1825 | - return !strcmp(right, "NULL"); | |
1826 | - } | |
1827 | - if (g == CCS_NUMBER_GROUP) { | |
1828 | - tmp->right = ccs_parse_task_cond(right); | |
1829 | - if (tmp->right == CCS_SELF_DOMAIN || | |
1830 | - tmp->right == CCS_SELF_EXE) | |
1831 | - return false; | |
1832 | - if (tmp->right == CCS_MAX_CONDITION_KEYWORD) | |
1833 | - tmp->right = ccs_parse_path_attribute(right, type); | |
1834 | - if (tmp->right != CCS_MAX_CONDITION_KEYWORD) | |
1835 | - return true; | |
1836 | - tmp->radix = ccs_parse_values(right, tmp->value); | |
1837 | - if (tmp->radix == CCS_VALUE_TYPE_INVALID) | |
1838 | - return false; | |
1839 | - if (tmp->radix >> 2) | |
1840 | - tmp->right = CCS_IMM_NUMBER_ENTRY2; | |
1841 | - else | |
1842 | - tmp->right = CCS_IMM_NUMBER_ENTRY1; | |
1843 | - return true; | |
1844 | - } | |
1845 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1846 | - if (g == CCS_IP_GROUP) { | |
1847 | - switch (ccs_parse_ipaddr(right, tmp->ipv6)) { | |
1848 | - case CCS_ADDRESS_TYPE_IPV4: | |
1849 | - tmp->right = CCS_IMM_IPV4ADDR_ENTRY1; | |
1850 | - break; | |
1851 | - case CCS_ADDRESS_TYPE_IPV4_RANGE: | |
1852 | - tmp->right = CCS_IMM_IPV4ADDR_ENTRY2; | |
1853 | - break; | |
1854 | - case CCS_ADDRESS_TYPE_IPV6: | |
1855 | - tmp->right = CCS_IMM_IPV6ADDR_ENTRY1; | |
1856 | - break; | |
1857 | - case CCS_ADDRESS_TYPE_IPV6_RANGE: | |
1858 | - tmp->right = CCS_IMM_IPV6ADDR_ENTRY2; | |
1859 | - break; | |
1860 | - default: | |
1861 | - return false; | |
1862 | - } | |
1863 | - return true; | |
1864 | - } | |
1865 | -#endif | |
1866 | - return false; | |
1867 | -} | |
1868 | - | |
1869 | -/** | |
1870 | - * ccs_get_condition - Parse condition part. | |
1871 | - * | |
1872 | - * @head: Pointer to "struct ccs_io_buffer". | |
1873 | - * | |
1874 | - * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | |
1875 | - */ | |
1876 | -struct ccs_condition *ccs_get_condition(struct ccs_io_buffer *head) | |
1877 | -{ | |
1878 | - struct ccs_condition *entry = kmalloc(PAGE_SIZE, GFP_NOFS); | |
1879 | - union ccs_condition_element *condp; | |
1880 | - struct ccs_cond_tmp tmp; | |
1881 | - const enum ccs_mac_index type = head->w.acl_index; | |
1882 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
1883 | - bool handler_path_done = head->w.is_deny || | |
1884 | - type != CCS_MAC_EXECUTE; | |
1885 | -#else | |
1886 | - bool handler_path_done = true; | |
1887 | -#endif | |
1888 | - bool transit_domain_done = head->w.is_deny || | |
1889 | - (type != CCS_MAC_EXECUTE | |
1890 | -#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
1891 | - && type != CCS_MAC_AUTO_DOMAIN_TRANSITION | |
1892 | -#endif | |
1893 | - ); | |
1894 | - char *pos = head->w.data; | |
1895 | - if (!entry) | |
1896 | - return NULL; | |
1897 | - condp = (union ccs_condition_element *) (entry + 1); | |
1898 | - while (1) { | |
1899 | - memset(&tmp, 0, sizeof(tmp)); | |
1900 | - tmp.left = CCS_MAX_CONDITION_KEYWORD; | |
1901 | - tmp.right = CCS_MAX_CONDITION_KEYWORD; | |
1902 | - while (*pos == ' ') | |
1903 | - pos++; | |
1904 | - if (!*pos) | |
1905 | - break; | |
1906 | - if ((u8 *) condp >= ((u8 *) entry) + PAGE_SIZE | |
1907 | - - (sizeof(*condp) + sizeof(struct in6_addr) * 2)) | |
1908 | - goto out; | |
1909 | - { | |
1910 | - char *next = strchr(pos, ' '); | |
1911 | - if (next) | |
1912 | - *next++ = '\0'; | |
1913 | - else | |
1914 | - next = ""; | |
1915 | - head->w.data = pos; | |
1916 | - pos = next; | |
1917 | - } | |
1918 | - if (!ccs_parse_cond(&tmp, head)) | |
1919 | - goto out; | |
1920 | - if (tmp.left == CCS_HANDLER_PATH) { | |
1921 | - if (handler_path_done) | |
1922 | - goto out; | |
1923 | - handler_path_done = true; | |
1924 | - } | |
1925 | - if (tmp.left == CCS_TRANSIT_DOMAIN) { | |
1926 | - if (transit_domain_done) | |
1927 | - goto out; | |
1928 | - transit_domain_done = true; | |
1929 | - } | |
1930 | - condp->is_not = tmp.is_not; | |
1931 | - condp->left = tmp.left; | |
1932 | - condp->right = tmp.right; | |
1933 | - condp->radix = tmp.radix; | |
1934 | - condp++; | |
1935 | - if (tmp.left == CCS_ARGV_ENTRY) { | |
1936 | - condp->value = tmp.argv; | |
1937 | - condp++; | |
1938 | - } else if (tmp.left == CCS_ENVP_ENTRY) { | |
1939 | - condp->path = tmp.envp; | |
1940 | - condp++; | |
1941 | - } | |
1942 | - if (tmp.right == CCS_IMM_GROUP) { | |
1943 | - condp->group = tmp.group; | |
1944 | - condp++; | |
1945 | - } else if (tmp.right == CCS_IMM_NAME_ENTRY) { | |
1946 | - condp->path = tmp.path; | |
1947 | - condp++; | |
1948 | - } else if (tmp.right == CCS_IMM_NUMBER_ENTRY1 || | |
1949 | - tmp.right == CCS_IMM_NUMBER_ENTRY2) { | |
1950 | - condp->value = tmp.value[0]; | |
1951 | - condp++; | |
1952 | - if (tmp.right == CCS_IMM_NUMBER_ENTRY2) { | |
1953 | - condp->value = tmp.value[1]; | |
1954 | - condp++; | |
1955 | - } | |
1956 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1957 | - } else if (tmp.right == CCS_IMM_IPV4ADDR_ENTRY1 || | |
1958 | - tmp.right == CCS_IMM_IPV4ADDR_ENTRY2) { | |
1959 | - condp->ip = * (u32 *) &tmp.ipv6[0]; | |
1960 | - condp++; | |
1961 | - if (tmp.right == CCS_IMM_IPV4ADDR_ENTRY2) { | |
1962 | - condp->ip = * (u32 *) &tmp.ipv6[1]; | |
1963 | - condp++; | |
1964 | - } | |
1965 | - } else if (tmp.right == CCS_IMM_IPV6ADDR_ENTRY1 || | |
1966 | - tmp.right == CCS_IMM_IPV6ADDR_ENTRY2) { | |
1967 | - * (struct in6_addr *) condp = tmp.ipv6[0]; | |
1968 | - condp = (void *) (((u8 *) condp) + | |
1969 | - sizeof(struct in6_addr)); | |
1970 | - if (tmp.right == CCS_IMM_IPV6ADDR_ENTRY2) { | |
1971 | - * (struct in6_addr *) condp = tmp.ipv6[1]; | |
1972 | - condp = (void *) (((u8 *) condp) + | |
1973 | - sizeof(struct in6_addr)); | |
1974 | - } | |
1975 | -#endif | |
1976 | - } | |
1977 | - } | |
1978 | -#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
1979 | - if (!transit_domain_done && type == CCS_MAC_AUTO_DOMAIN_TRANSITION) | |
1980 | - goto out; | |
1981 | -#endif | |
1982 | - entry->size = (void *) condp - (void *) entry; | |
1983 | - return ccs_commit_condition(entry); | |
1984 | -out: | |
1985 | - dprintk(KERN_WARNING "%u: type=%u env='%s' path='%s' group='%s'\n", | |
1986 | - __LINE__, type, tmp.envp ? tmp.envp->name : "", | |
1987 | - tmp.path ? tmp.path->name : "", | |
1988 | - tmp.group ? tmp.group->group_name->name : ""); | |
1989 | - ccs_put_name(tmp.envp); | |
1990 | - if (tmp.path != &ccs_null_name) | |
1991 | - ccs_put_name(tmp.path); | |
1992 | - ccs_put_group(tmp.group); | |
1993 | - entry->size = (void *) condp - (void *) entry; | |
1994 | - ccs_del_condition(&entry->head.list); | |
1995 | - kfree(entry); | |
1996 | - return NULL; | |
1997 | -} | |
1998 | - | |
1999 | -/** | |
2000 | - * ccs_yesno - Return "yes" or "no". | |
2001 | - * | |
2002 | - * @value: Bool value. | |
2003 | - * | |
2004 | - * Returns "yes" if @value is not 0, "no" otherwise. | |
2005 | - */ | |
2006 | -static const char *ccs_yesno(const unsigned int value) | |
2007 | -{ | |
2008 | - return value ? "yes" : "no"; | |
2009 | -} | |
2010 | - | |
2011 | -/** | |
2012 | - * ccs_flush - Flush queued string to userspace's buffer. | |
2013 | - * | |
2014 | - * @head: Pointer to "struct ccs_io_buffer". | |
2015 | - * | |
2016 | - * Returns true if all data was flushed, false otherwise. | |
2017 | - */ | |
2018 | -static bool ccs_flush(struct ccs_io_buffer *head) | |
2019 | -{ | |
2020 | - while (head->r.w_pos) { | |
2021 | - const char *w = head->r.w[0]; | |
2022 | - size_t len = strlen(w); | |
2023 | - if (len) { | |
2024 | - if (len > head->read_user_buf_avail) | |
2025 | - len = head->read_user_buf_avail; | |
2026 | - if (!len) | |
2027 | - return false; | |
2028 | - if (copy_to_user(head->read_user_buf, w, len)) | |
2029 | - return false; | |
2030 | - head->read_user_buf_avail -= len; | |
2031 | - head->read_user_buf += len; | |
2032 | - w += len; | |
2033 | - } | |
2034 | - head->r.w[0] = w; | |
2035 | - if (*w) | |
2036 | - return false; | |
2037 | - /* Add '\0' for audit logs and query. */ | |
2038 | - if (head->type == CCS_AUDIT || head->type == CCS_QUERY) { | |
2039 | - if (!head->read_user_buf_avail || | |
2040 | - copy_to_user(head->read_user_buf, "", 1)) | |
2041 | - return false; | |
2042 | - head->read_user_buf_avail--; | |
2043 | - head->read_user_buf++; | |
2044 | - } | |
2045 | - head->r.w_pos--; | |
2046 | - for (len = 0; len < head->r.w_pos; len++) | |
2047 | - head->r.w[len] = head->r.w[len + 1]; | |
2048 | - } | |
2049 | - head->r.avail = 0; | |
2050 | - return true; | |
2051 | -} | |
2052 | - | |
2053 | -/** | |
2054 | - * ccs_set_string - Queue string to "struct ccs_io_buffer" structure. | |
2055 | - * | |
2056 | - * @head: Pointer to "struct ccs_io_buffer". | |
2057 | - * @string: String to print. | |
2058 | - * | |
2059 | - * Returns nothing. | |
2060 | - * | |
2061 | - * Note that @string has to be kept valid until @head is kfree()d. | |
2062 | - * This means that char[] allocated on stack memory cannot be passed to | |
2063 | - * this function. Use ccs_io_printf() for char[] allocated on stack memory. | |
2064 | - */ | |
2065 | -static void ccs_set_string(struct ccs_io_buffer *head, const char *string) | |
2066 | -{ | |
2067 | - if (head->r.w_pos < CCS_MAX_IO_READ_QUEUE) { | |
2068 | - head->r.w[head->r.w_pos++] = string; | |
2069 | - ccs_flush(head); | |
2070 | - } else | |
2071 | - printk(KERN_WARNING "Too many words in a line.\n"); | |
2072 | -} | |
2073 | - | |
2074 | -/** | |
2075 | - * ccs_io_printf - printf() to "struct ccs_io_buffer" structure. | |
2076 | - * | |
2077 | - * @head: Pointer to "struct ccs_io_buffer". | |
2078 | - * @fmt: The printf()'s format string, followed by parameters. | |
2079 | - * | |
2080 | - * Returns nothing. | |
2081 | - */ | |
2082 | -static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | |
2083 | -{ | |
2084 | - va_list args; | |
2085 | - size_t len; | |
2086 | - size_t pos = head->r.avail; | |
2087 | - int size = head->readbuf_size - pos; | |
2088 | - if (size <= 0) | |
2089 | - return; | |
2090 | - va_start(args, fmt); | |
2091 | - len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; | |
2092 | - va_end(args); | |
2093 | - if (pos + len >= head->readbuf_size) { | |
2094 | - printk(KERN_WARNING "Too many words in a line.\n"); | |
2095 | - return; | |
2096 | - } | |
2097 | - head->r.avail += len; | |
2098 | - ccs_set_string(head, head->read_buf + pos); | |
2099 | -} | |
2100 | - | |
2101 | -/** | |
2102 | - * ccs_set_space - Put a space to "struct ccs_io_buffer" structure. | |
2103 | - * | |
2104 | - * @head: Pointer to "struct ccs_io_buffer". | |
2105 | - * | |
2106 | - * Returns nothing. | |
2107 | - */ | |
2108 | -static void ccs_set_space(struct ccs_io_buffer *head) | |
2109 | -{ | |
2110 | - ccs_set_string(head, " "); | |
2111 | -} | |
2112 | - | |
2113 | -/** | |
2114 | - * ccs_set_lf - Put a line feed to "struct ccs_io_buffer" structure. | |
2115 | - * | |
2116 | - * @head: Pointer to "struct ccs_io_buffer". | |
2117 | - * | |
2118 | - * Returns true if all data was flushed, false otherwise. | |
2119 | - */ | |
2120 | -static bool ccs_set_lf(struct ccs_io_buffer *head) | |
2121 | -{ | |
2122 | - ccs_set_string(head, "\n"); | |
2123 | - return !head->r.w_pos; | |
2124 | -} | |
2125 | - | |
2126 | -/** | |
2127 | - * ccs_check_profile - Check policy is loaded. | |
2128 | - * | |
2129 | - * Returns nothing. | |
2130 | - */ | |
2131 | -static void ccs_check_profile(void) | |
2132 | -{ | |
2133 | - ccs_policy_loaded = true; | |
2134 | - printk(KERN_INFO "CaitSith: 0.1 2012/04/01\n"); | |
2135 | - if (ccs_policy_version == 20120401) { | |
2136 | - printk(KERN_INFO "CaitSith module activated.\n"); | |
2137 | - return; | |
2138 | - } | |
2139 | - printk(KERN_ERR "Policy version %u is not supported.\n", | |
2140 | - ccs_policy_version); | |
2141 | - printk(KERN_ERR "Userland tools for CaitSith must be installed and " | |
2142 | - "policy must be initialized.\n"); | |
2143 | - printk(KERN_ERR "Please see http://caitsith.sourceforge.jp/ " | |
2144 | - "for more information.\n"); | |
2145 | - panic("STOP!"); | |
2146 | -} | |
2147 | - | |
2148 | -/** | |
2149 | - * ccs_str_starts - Check whether the given string starts with the given keyword. | |
2150 | - * | |
2151 | - * @src: Pointer to pointer to the string. | |
2152 | - * @find: Pointer to the keyword. | |
2153 | - * | |
2154 | - * Returns true if @src starts with @find, false otherwise. | |
2155 | - * | |
2156 | - * The @src is updated to point the first character after the @find | |
2157 | - * if @src starts with @find. | |
2158 | - */ | |
2159 | -static bool ccs_str_starts(char **src, const char *find) | |
2160 | -{ | |
2161 | - const int len = strlen(find); | |
2162 | - char *tmp = *src; | |
2163 | - if (strncmp(tmp, find, len)) | |
2164 | - return false; | |
2165 | - tmp += len; | |
2166 | - *src = tmp; | |
2167 | - return true; | |
2168 | -} | |
2169 | - | |
2170 | -/** | |
2171 | - * ccs_find_domain - Find a domain by the given name. | |
2172 | - * | |
2173 | - * @domainname: The domainname to find. | |
2174 | - * | |
2175 | - * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. | |
2176 | - * | |
2177 | - * Caller holds ccs_read_lock(). | |
2178 | - */ | |
2179 | -static struct ccs_domain_info *ccs_find_domain(const char *domainname) | |
2180 | -{ | |
2181 | - struct ccs_domain_info *domain; | |
2182 | - struct ccs_path_info name; | |
2183 | - name.name = domainname; | |
2184 | - ccs_fill_path_info(&name); | |
2185 | - list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { | |
2186 | - if (!ccs_pathcmp(&name, domain->domainname)) | |
2187 | - return domain; | |
2188 | - } | |
2189 | - return NULL; | |
2190 | -} | |
2191 | - | |
2192 | -/** | |
2193 | - * ccs_select_acl - Parse select command. | |
2194 | - * | |
2195 | - * @head: Pointer to "struct ccs_io_buffer". | |
2196 | - * @data: String to parse. | |
2197 | - * | |
2198 | - * Returns true on success, false otherwise. | |
2199 | - * | |
2200 | - * Caller holds ccs_read_lock(). | |
2201 | - */ | |
2202 | -static bool ccs_select_acl(struct ccs_io_buffer *head, const char *data) | |
2203 | -{ | |
2204 | - unsigned int qid; | |
2205 | - struct ccs_acl_info *acl; | |
2206 | - if (sscanf(data, "Q=%u", &qid) != 1) | |
2207 | - return false; | |
2208 | - acl = ccs_find_acl_by_qid(qid); | |
2209 | - head->w.acl = acl; | |
2210 | - /* Accessing read_buf is safe because head->io_sem is held. */ | |
2211 | - if (!head->read_buf) | |
2212 | - return true; /* Do nothing if open(O_WRONLY). */ | |
2213 | - memset(&head->r, 0, sizeof(head->r)); | |
2214 | - head->r.print_this_acl_only = true; | |
2215 | - if (acl) | |
2216 | - head->r.acl = &acl->list; | |
2217 | - else | |
2218 | - head->r.eof = true; | |
2219 | - ccs_io_printf(head, "# Q=%u\n", qid); | |
2220 | - return true; | |
2221 | -} | |
2222 | - | |
2223 | -/** | |
2224 | - * ccs_update_acl - Update "struct ccs_acl_info" entry. | |
2225 | - * | |
2226 | - * @list: Pointer to "struct list_head". | |
2227 | - * @head: Pointer to "struct ccs_io_buffer". | |
2228 | - * @update: True to store matching entry, false otherwise. | |
2229 | - * | |
2230 | - * Returns 0 on success, negative value otherwise. | |
2231 | - * | |
2232 | - * Caller holds ccs_read_lock(). | |
2233 | - */ | |
2234 | -static int ccs_update_acl(struct list_head * const list, | |
2235 | - struct ccs_io_buffer *head, const bool update) | |
2236 | -{ | |
2237 | - struct ccs_acl_info *ptr; | |
2238 | - struct ccs_acl_info new_entry = { }; | |
2239 | - const bool is_delete = head->w.is_delete; | |
2240 | - int error = is_delete ? -ENOENT : -ENOMEM; | |
2241 | - new_entry.priority = head->w.priority; | |
2242 | - new_entry.is_deny = head->w.is_deny; | |
2243 | - if (head->w.data[0]) { | |
2244 | - new_entry.cond = ccs_get_condition(head); | |
2245 | - if (!new_entry.cond) | |
2246 | - return -EINVAL; | |
2247 | - } | |
2248 | - if (mutex_lock_interruptible(&ccs_policy_lock)) | |
2249 | - goto out; | |
2250 | - list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | |
2251 | - if (ptr->priority > new_entry.priority) | |
2252 | - break; | |
2253 | - /* | |
2254 | - * We cannot reuse deleted "struct ccs_acl_info" entry because | |
2255 | - * somebody might be referencing children of this deleted entry | |
2256 | - * from srcu section. We cannot delete children of this deleted | |
2257 | - * entry until all children are no longer referenced. Thus, let | |
2258 | - * the garbage collector wait and delete rather than trying to | |
2259 | - * reuse this deleted entry. | |
2260 | - */ | |
2261 | - if (ptr->is_deleted || ptr->cond != new_entry.cond || | |
2262 | - ptr->priority != new_entry.priority || | |
2263 | - ptr->is_deny != new_entry.is_deny) | |
2264 | - continue; | |
2265 | - ptr->is_deleted = is_delete; | |
2266 | - if (!is_delete && update) | |
2267 | - head->w.acl = ptr; | |
2268 | - error = 0; | |
2269 | - break; | |
2270 | - } | |
2271 | - if (error && !is_delete) { | |
2272 | - struct ccs_acl_info *entry = | |
2273 | - ccs_commit_ok(&new_entry, sizeof(new_entry)); | |
2274 | - if (entry) { | |
2275 | - INIT_LIST_HEAD(&entry->acl_info_list); | |
2276 | - list_add_tail_rcu(&entry->list, &ptr->list); | |
2277 | - if (update) | |
2278 | - head->w.acl = entry; | |
2279 | - } | |
2280 | - } | |
2281 | - mutex_unlock(&ccs_policy_lock); | |
2282 | -out: | |
2283 | - ccs_put_condition(new_entry.cond); | |
2284 | - return error; | |
2285 | -} | |
2286 | - | |
2287 | -/** | |
2288 | - * ccs_parse_entry - Update ACL entry. | |
2289 | - * | |
2290 | - * @head: Pointer to "struct ccs_io_buffer". | |
2291 | - * | |
2292 | - * Returns 0 on success, negative value otherwise. | |
2293 | - * | |
2294 | - * Caller holds ccs_read_lock(). | |
2295 | - */ | |
2296 | -static int ccs_parse_entry(struct ccs_io_buffer *head) | |
2297 | -{ | |
2298 | - enum ccs_mac_index type; | |
2299 | - const char *operation = ccs_read_token(head); | |
2300 | - for (type = 0; type < CCS_MAX_MAC_INDEX; type++) { | |
2301 | - if (strcmp(operation, ccs_mac_keywords[type])) | |
2302 | - continue; | |
2303 | - head->w.acl_index = type; | |
2304 | - /* | |
2305 | - * This is_deny is for rejecting handler= and transition= | |
2306 | - * arguments in "acl" line. handler= and transition= arguments | |
2307 | - * are accepted for only "allow" line. | |
2308 | - */ | |
2309 | - head->w.is_deny = true; | |
2310 | - return ccs_update_acl(&ccs_acl_list[type], head, true); | |
2311 | - } | |
2312 | - return -EINVAL; | |
2313 | -} | |
2314 | - | |
2315 | -/** | |
2316 | - * ccs_print_number - Print number argument. | |
2317 | - * | |
2318 | - * @head: Pointer to "struct ccs_io_buffer". | |
2319 | - * @radix: One of values in "enum ccs_value_type". | |
2320 | - * @value: Value to print. | |
2321 | - * | |
2322 | - * Returns nothing. | |
2323 | - */ | |
2324 | -static void ccs_print_number(struct ccs_io_buffer *head, | |
2325 | - const enum ccs_value_type radix, | |
2326 | - const unsigned long value) | |
2327 | -{ | |
2328 | - switch (radix) { | |
2329 | - case CCS_VALUE_TYPE_HEXADECIMAL: | |
2330 | - ccs_io_printf(head, "0x%lX", value); | |
2331 | - break; | |
2332 | - case CCS_VALUE_TYPE_OCTAL: | |
2333 | - ccs_io_printf(head, "0%lo", value); | |
2334 | - break; | |
2335 | - default: | |
2336 | - ccs_io_printf(head, "%lu", value); | |
2337 | - } | |
2338 | -} | |
2339 | - | |
2340 | -/** | |
2341 | - * ccs_print_misc_attribute - Print misc attribute's name. | |
2342 | - * | |
2343 | - * @head: Pointer to "struct ccs_io_buffer". | |
2344 | - * @type: One of values in "enum ccs_mac_index". | |
2345 | - * @cond: One of values in "enum ccs_condition_index". | |
2346 | - * | |
2347 | - * Returns nothing. | |
2348 | - */ | |
2349 | -static void ccs_print_misc_attribute(struct ccs_io_buffer *head, | |
2350 | - const enum ccs_mac_index type, | |
2351 | - const enum ccs_conditions_index cond) | |
2352 | -{ | |
2353 | - if (cond >= CCS_PATH_ATTRIBUTE_START) { | |
2354 | - const u8 pos = cond - CCS_PATH_ATTRIBUTE_START; | |
2355 | - ccs_io_printf(head, "%s.%s%s", ccs_get_sarg(type, pos >= 32), | |
2356 | - pos % 32 >= 16 ? "parent." : "", | |
2357 | - ccs_path_attribute[pos % 16]); | |
2358 | - return; | |
2359 | - } | |
2360 | - if (cond == CCS_COND_DOMAIN) { | |
2361 | - ccs_set_string(head, "domain"); | |
2362 | - return; | |
2363 | - } | |
2364 | - switch (cond) { | |
2365 | - case CCS_SELF_UID: | |
2366 | - case CCS_SELF_EUID: | |
2367 | - case CCS_SELF_SUID: | |
2368 | - case CCS_SELF_FSUID: | |
2369 | - case CCS_SELF_GID: | |
2370 | - case CCS_SELF_EGID: | |
2371 | - case CCS_SELF_SGID: | |
2372 | - case CCS_SELF_FSGID: | |
2373 | - case CCS_SELF_PID: | |
2374 | - case CCS_SELF_PPID: | |
2375 | - case CCS_TASK_TYPE: | |
2376 | - case CCS_SELF_DOMAIN: | |
2377 | - case CCS_SELF_EXE: | |
2378 | - ccs_set_string(head, "task."); | |
2379 | - /* fall through */ | |
2380 | - default: | |
2381 | - if (cond < CCS_MAX_CONDITION_KEYWORD) | |
2382 | - ccs_set_string(head, ccs_condition_keyword[cond]); | |
2383 | - else | |
2384 | - ccs_io_printf(head, "unknown(%u)", cond); | |
2385 | - } | |
2386 | -} | |
2387 | - | |
2388 | -/** | |
2389 | - * ccs_print_condition_loop - Print condition part. | |
2390 | - * | |
2391 | - * @head: Pointer to "struct ccs_io_buffer". | |
2392 | - * @cond: Pointer to "struct ccs_condition". | |
2393 | - * | |
2394 | - * Returns true on success, false otherwise. | |
2395 | - */ | |
2396 | -static bool ccs_print_condition_loop(struct ccs_io_buffer *head, | |
2397 | - const struct ccs_condition *cond) | |
2398 | -{ | |
2399 | - const enum ccs_mac_index type = head->r.acl_index; | |
2400 | - const union ccs_condition_element *condp = head->r.cond; | |
2401 | - while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
2402 | - const bool is_not = condp->is_not; | |
2403 | - const enum ccs_conditions_index left = condp->left; | |
2404 | - const enum ccs_conditions_index right = condp->right; | |
2405 | - const u8 radix = condp->radix; | |
2406 | - if (!ccs_flush(head)) { | |
2407 | - head->r.cond = condp; | |
2408 | - return false; | |
2409 | - } | |
2410 | - condp++; | |
2411 | - ccs_set_space(head); | |
2412 | - switch (left) { | |
2413 | - case CCS_ARGV_ENTRY: | |
2414 | - ccs_io_printf(head, "argv[%lu]", condp->value); | |
2415 | - condp++; | |
2416 | - break; | |
2417 | - case CCS_ENVP_ENTRY: | |
2418 | - ccs_set_string(head, "envp[\""); | |
2419 | - ccs_set_string(head, condp->path->name); | |
2420 | - condp++; | |
2421 | - ccs_set_string(head, "\"]"); | |
2422 | - break; | |
2423 | - case CCS_COND_SARG0: | |
2424 | - ccs_set_string(head, ccs_get_sarg(type, 0)); | |
2425 | - break; | |
2426 | - case CCS_COND_SARG1: | |
2427 | - ccs_set_string(head, ccs_get_sarg(type, 1)); | |
2428 | - break; | |
2429 | - case CCS_COND_SARG2: | |
2430 | - ccs_set_string(head, ccs_get_sarg(type, 2)); | |
2431 | - break; | |
2432 | - case CCS_COND_SARG3: | |
2433 | - ccs_set_string(head, ccs_get_sarg(type, 3)); | |
2434 | - break; | |
2435 | - case CCS_COND_NARG0: | |
2436 | - ccs_set_string(head, ccs_get_narg(type, 0)); | |
2437 | - break; | |
2438 | - case CCS_COND_NARG1: | |
2439 | - ccs_set_string(head, ccs_get_narg(type, 1)); | |
2440 | - break; | |
2441 | - case CCS_COND_NARG2: | |
2442 | - ccs_set_string(head, ccs_get_narg(type, 2)); | |
2443 | - break; | |
2444 | - case CCS_COND_IPARG: | |
2445 | - ccs_set_string(head, "ip"); | |
2446 | - break; | |
2447 | - default: | |
2448 | - ccs_print_misc_attribute(head, type, left); | |
2449 | - } | |
2450 | - ccs_set_string(head, is_not ? "!=" : "="); | |
2451 | - switch (right) { | |
2452 | - case CCS_IMM_GROUP: | |
2453 | - ccs_set_string(head, "@"); | |
2454 | - ccs_set_string(head, condp->group->group_name->name); | |
2455 | - condp++; | |
2456 | - break; | |
2457 | - case CCS_IMM_NAME_ENTRY: | |
2458 | - if (condp->path != &ccs_null_name) { | |
2459 | - ccs_set_string(head, "\""); | |
2460 | - ccs_set_string(head, condp->path->name); | |
2461 | - ccs_set_string(head, "\""); | |
2462 | - } else { | |
2463 | - ccs_set_string(head, "NULL"); | |
2464 | - } | |
2465 | - condp++; | |
2466 | - break; | |
2467 | - case CCS_IMM_NUMBER_ENTRY1: | |
2468 | - case CCS_IMM_NUMBER_ENTRY2: | |
2469 | - ccs_print_number(head, radix & 3, condp->value); | |
2470 | - condp++; | |
2471 | - if (right == CCS_IMM_NUMBER_ENTRY1) | |
2472 | - break; | |
2473 | - ccs_set_string(head, "-"); | |
2474 | - ccs_print_number(head, (radix >> 2) & 3, condp->value); | |
2475 | - condp++; | |
2476 | - break; | |
2477 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
2478 | - case CCS_IMM_IPV4ADDR_ENTRY1: | |
2479 | - case CCS_IMM_IPV4ADDR_ENTRY2: | |
2480 | - ccs_print_ipv4(head, &condp->ip); | |
2481 | - condp++; | |
2482 | - if (right == CCS_IMM_IPV4ADDR_ENTRY1) | |
2483 | - break; | |
2484 | - ccs_set_string(head, "-"); | |
2485 | - ccs_print_ipv4(head, &condp->ip); | |
2486 | - condp++; | |
2487 | - break; | |
2488 | - case CCS_IMM_IPV6ADDR_ENTRY1: | |
2489 | - case CCS_IMM_IPV6ADDR_ENTRY2: | |
2490 | - ccs_print_ipv6(head, (const struct in6_addr *) condp); | |
2491 | - condp = (void *) | |
2492 | - ((u8 *) condp) + sizeof(struct in6_addr); | |
2493 | - if (right == CCS_IMM_IPV6ADDR_ENTRY1) | |
2494 | - break; | |
2495 | - ccs_set_string(head, "-"); | |
2496 | - ccs_print_ipv6(head, (const struct in6_addr *) condp); | |
2497 | - condp = (void *) | |
2498 | - ((u8 *) condp) + sizeof(struct in6_addr); | |
2499 | - break; | |
2500 | -#endif | |
2501 | - default: | |
2502 | - ccs_print_misc_attribute(head, type, right); | |
2503 | - } | |
2504 | - } | |
2505 | - head->r.cond = NULL; | |
2506 | - return true; | |
2507 | -} | |
2508 | - | |
2509 | -/** | |
2510 | - * ccs_print_condition - Print condition part. | |
2511 | - * | |
2512 | - * @head: Pointer to "struct ccs_io_buffer". | |
2513 | - * @cond: Pointer to "struct ccs_condition". | |
2514 | - * | |
2515 | - * Returns true on success, false otherwise. | |
2516 | - */ | |
2517 | -static bool ccs_print_condition(struct ccs_io_buffer *head, | |
2518 | - const struct ccs_condition *cond) | |
2519 | -{ | |
2520 | - switch (head->r.cond_step) { | |
2521 | - case 0: | |
2522 | - head->r.cond = (const union ccs_condition_element *) | |
2523 | - (cond + 1); | |
2524 | - head->r.cond_step++; | |
2525 | - /* fall through */ | |
2526 | - case 1: | |
2527 | - if (!ccs_print_condition_loop(head, cond)) | |
2528 | - return false; | |
2529 | - head->r.cond_step++; | |
2530 | - /* fall through */ | |
2531 | - case 2: | |
2532 | - head->r.cond = NULL; | |
2533 | - return true; | |
2534 | - } | |
2535 | - return false; | |
2536 | -} | |
2537 | - | |
2538 | -/** | |
2539 | - * ccs_read_acl - Print an ACL entry. | |
2540 | - * | |
2541 | - * @head: Pointer to "struct ccs_io_buffer". | |
2542 | - * @acl: Pointer to an ACL entry. | |
2543 | - * | |
2544 | - * Returns true on success, false otherwise. | |
2545 | - */ | |
2546 | -static bool ccs_read_acl(struct ccs_io_buffer *head, | |
2547 | - const struct ccs_acl_info *acl) | |
2548 | -{ | |
2549 | - const enum ccs_mac_index type = head->r.acl_index; | |
2550 | - if (head->r.cond) | |
2551 | - goto print_cond_part; | |
2552 | - if (acl->is_deleted) | |
2553 | - return true; | |
2554 | - if (!ccs_flush(head)) | |
2555 | - return false; | |
2556 | - ccs_io_printf(head, "%u ", acl->priority); | |
2557 | - ccs_set_string(head, "acl "); | |
2558 | - ccs_set_string(head, ccs_mac_keywords[type]); | |
2559 | - if (acl->cond) { | |
2560 | - head->r.cond_step = 0; | |
2561 | -print_cond_part: | |
2562 | - if (!ccs_print_condition(head, acl->cond)) | |
2563 | - return false; | |
2564 | - } | |
2565 | - ccs_set_lf(head); | |
2566 | - return true; | |
2567 | -} | |
2568 | - | |
2569 | -/** | |
2570 | - * ccs_write_pid - Specify PID to obtain domainname. | |
2571 | - * | |
2572 | - * @head: Pointer to "struct ccs_io_buffer". | |
2573 | - * | |
2574 | - * Returns 0. | |
2575 | - */ | |
2576 | -static int ccs_write_pid(struct ccs_io_buffer *head) | |
2577 | -{ | |
2578 | - head->r.eof = false; | |
2579 | - return 0; | |
2580 | -} | |
2581 | - | |
2582 | -/** | |
2583 | - * ccs_read_pid - Read information of a process. | |
2584 | - * | |
2585 | - * @head: Pointer to "struct ccs_io_buffer". | |
2586 | - * | |
2587 | - * Returns nothing. | |
2588 | - * | |
2589 | - * Reads the domainname which the specified PID is in or | |
2590 | - * process information of the specified PID on success. | |
2591 | - * | |
2592 | - * Caller holds ccs_read_lock(). | |
2593 | - */ | |
2594 | -static void ccs_read_pid(struct ccs_io_buffer *head) | |
2595 | -{ | |
2596 | - char *buf = head->write_buf; | |
2597 | - bool task_info = false; | |
2598 | - bool global_pid = false; | |
2599 | - unsigned int pid; | |
2600 | - struct task_struct *p; | |
2601 | - struct ccs_domain_info *domain = NULL; | |
2602 | - u32 ccs_flags = 0; | |
2603 | - /* Accessing write_buf is safe because head->io_sem is held. */ | |
2604 | - if (!buf) { | |
2605 | - head->r.eof = true; | |
2606 | - return; /* Do nothing if open(O_RDONLY). */ | |
2607 | - } | |
2608 | - if (head->r.w_pos || head->r.eof) | |
2609 | - return; | |
2610 | - head->r.eof = true; | |
2611 | - if (ccs_str_starts(&buf, "info ")) | |
2612 | - task_info = true; | |
2613 | - if (ccs_str_starts(&buf, "global-pid ")) | |
2614 | - global_pid = true; | |
2615 | - pid = (unsigned int) simple_strtoul(buf, NULL, 10); | |
2616 | - ccs_tasklist_lock(); | |
2617 | - if (global_pid) | |
2618 | - p = ccsecurity_exports.find_task_by_pid_ns(pid, &init_pid_ns); | |
2619 | - else | |
2620 | - p = ccsecurity_exports.find_task_by_vpid(pid); | |
2621 | - if (p) { | |
2622 | - domain = ccs_task_domain(p); | |
2623 | - ccs_flags = ccs_task_flags(p); | |
2624 | - } | |
2625 | - ccs_tasklist_unlock(); | |
2626 | - if (!domain) | |
2627 | - return; | |
2628 | - if (!task_info) { | |
2629 | - ccs_io_printf(head, "%u ", pid); | |
2630 | - ccs_set_string(head, domain->domainname->name); | |
2631 | - } else { | |
2632 | - ccs_io_printf(head, "%u manager=%s execute_handler=%s ", pid, | |
2633 | - ccs_yesno(ccs_flags & | |
2634 | - CCS_TASK_IS_MANAGER), | |
2635 | - ccs_yesno(ccs_flags & | |
2636 | - CCS_TASK_IS_EXECUTE_HANDLER)); | |
2637 | - } | |
2638 | -} | |
2639 | - | |
2640 | -/** | |
2641 | - * ccs_update_group - Update "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2642 | - * | |
2643 | - * @head: Pointer to "struct ccs_io_buffer". | |
2644 | - * @type: Type of this group. | |
2645 | - * | |
2646 | - * Returns 0 on success, negative value otherwise. | |
2647 | - */ | |
2648 | -static int ccs_update_group(struct ccs_io_buffer *head, | |
2649 | - const enum ccs_group_id type) | |
2650 | -{ | |
2651 | - u8 size; | |
2652 | - const bool is_delete = head->w.is_delete; | |
2653 | - int error = is_delete ? -ENOENT : -ENOMEM; | |
2654 | - struct ccs_group *group = ccs_get_group(head, type); | |
2655 | - char *word = ccs_read_token(head); | |
2656 | - union { | |
2657 | - struct ccs_acl_head head; | |
2658 | - struct ccs_string_group path; | |
2659 | - struct ccs_number_group number; | |
2660 | - struct ccs_ip_group address; | |
2661 | - } e = { }; | |
2662 | - if (!group) | |
2663 | - return -ENOMEM; | |
2664 | - if (!*word) { | |
2665 | - error = -EINVAL; | |
2666 | - goto out; | |
2667 | - } | |
2668 | - if (type == CCS_STRING_GROUP) { | |
2669 | - if (!ccs_correct_word(word)) { | |
2670 | - error = -EINVAL; | |
2671 | - goto out; | |
2672 | - } | |
2673 | - e.path.member_name = ccs_get_name(word); | |
2674 | - if (!e.path.member_name) { | |
2675 | - error = -ENOMEM; | |
2676 | - goto out; | |
2677 | - } | |
2678 | - size = sizeof(e.path); | |
2679 | - } else if (type == CCS_NUMBER_GROUP) { | |
2680 | - e.number.radix = ccs_parse_values(word, e.number.value); | |
2681 | - if (e.number.radix == CCS_VALUE_TYPE_INVALID) | |
2682 | - goto out; | |
2683 | - size = sizeof(e.number); | |
2684 | - } else { | |
2685 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
2686 | - switch (ccs_parse_ipaddr(word, e.address.ip)) { | |
2687 | - case CCS_ADDRESS_TYPE_IPV4: | |
2688 | - case CCS_ADDRESS_TYPE_IPV4_RANGE: | |
2689 | - e.address.is_ipv6 = false; | |
2690 | - break; | |
2691 | - case CCS_ADDRESS_TYPE_IPV6: | |
2692 | - case CCS_ADDRESS_TYPE_IPV6_RANGE: | |
2693 | - e.address.is_ipv6 = true; | |
2694 | - break; | |
2695 | - default: | |
2696 | - goto out; | |
2697 | - } | |
2698 | - size = sizeof(e.address); | |
2699 | -#else | |
2700 | - goto out; | |
2701 | -#endif | |
2702 | - } | |
2703 | - if (mutex_lock_interruptible(&ccs_policy_lock) == 0) { | |
2704 | - struct ccs_acl_head *entry; | |
2705 | - list_for_each_entry_srcu(entry, &group->member_list, | |
2706 | - list, &ccs_ss) { | |
2707 | - if (entry->is_deleted == CCS_GC_IN_PROGRESS || | |
2708 | - memcmp(entry + 1, &e.head + 1, | |
2709 | - size - sizeof(*entry))) | |
2710 | - continue; | |
2711 | - entry->is_deleted = is_delete; | |
2712 | - error = 0; | |
2713 | - break; | |
2714 | - } | |
2715 | - if (error && !is_delete) { | |
2716 | - entry = ccs_commit_ok(&e, size); | |
2717 | - if (entry) { | |
2718 | - list_add_tail_rcu(&entry->list, | |
2719 | - &group->member_list); | |
2720 | - error = 0; | |
2721 | - } | |
2722 | - } | |
2723 | - mutex_unlock(&ccs_policy_lock); | |
2724 | - } | |
2725 | - if (type == CCS_STRING_GROUP) | |
2726 | - ccs_put_name(e.path.member_name); | |
2727 | -out: | |
2728 | - ccs_put_group(group); | |
2729 | - return error; | |
2730 | -} | |
2731 | - | |
2732 | -/** | |
2733 | - * ccs_write_policy - Write policy. | |
2734 | - * | |
2735 | - * @head: Pointer to "struct ccs_io_buffer". | |
2736 | - * | |
2737 | - * Returns 0 on success, negative value otherwise. | |
2738 | - */ | |
2739 | -static int ccs_write_policy(struct ccs_io_buffer *head) | |
2740 | -{ | |
2741 | - enum ccs_group_id i; | |
2742 | - unsigned int priority; | |
2743 | - char *word = ccs_read_token(head); | |
2744 | - if (sscanf(word, "%u", &priority) == 1) | |
2745 | - word = ccs_read_token(head); | |
2746 | - else | |
2747 | - priority = 1000; | |
2748 | - if (priority >= 65536 || !*word) | |
2749 | - return -EINVAL; | |
2750 | - head->w.priority = priority; | |
2751 | - if (!head->w.acl) | |
2752 | - goto no_acl_selected; | |
2753 | - head->w.is_deny = !strcmp(word, "deny"); | |
2754 | - if (head->w.is_deny || !strcmp(word, "allow")) | |
2755 | - return ccs_update_acl(&head->w.acl->acl_info_list, head, | |
2756 | - false); | |
2757 | - if (!strcmp(word, "audit")) { | |
2758 | - head->w.acl->audit = simple_strtoul(head->w.data, NULL, 10); | |
2759 | - return 0; | |
2760 | - } | |
2761 | - head->w.acl = NULL; | |
2762 | -no_acl_selected: | |
2763 | - if (ccs_select_acl(head, word)) | |
2764 | - return 0; | |
2765 | - if (!strcmp(word, "acl")) | |
2766 | - return ccs_parse_entry(head); | |
2767 | - for (i = 0; i < CCS_MAX_GROUP; i++) | |
2768 | - if (!strcmp(word, ccs_group_name[i])) | |
2769 | - return ccs_update_group(head, i); | |
2770 | - if (sscanf(word, "POLICY_VERSION=%u", &ccs_policy_version) == 1) | |
2771 | - return 0; | |
2772 | - if (strcmp(word, "quota")) | |
2773 | - return -EINVAL; | |
2774 | - if (ccs_str_starts(&head->w.data, "memory ")) | |
2775 | - return ccs_write_memory_quota(head->w.data); | |
2776 | - return ccs_write_audit_quota(head->w.data); | |
2777 | -} | |
2778 | - | |
2779 | -/** | |
2780 | - * ccs_read_subgroup - Read "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2781 | - * | |
2782 | - * @head: Pointer to "struct ccs_io_buffer". | |
2783 | - * @group: Pointer to "struct ccs_group". | |
2784 | - * @idx: One of values in "enum ccs_group_id". | |
2785 | - * | |
2786 | - * Returns true on success, false otherwise. | |
2787 | - * | |
2788 | - * Caller holds ccs_read_lock(). | |
2789 | - */ | |
2790 | -static bool ccs_read_subgroup(struct ccs_io_buffer *head, | |
2791 | - struct ccs_group *group, | |
2792 | - const enum ccs_group_id idx) | |
2793 | -{ | |
2794 | - list_for_each_cookie(head->r.acl, &group->member_list) { | |
2795 | - struct ccs_acl_head *ptr = | |
2796 | - list_entry(head->r.acl, typeof(*ptr), list); | |
2797 | - if (ptr->is_deleted) | |
2798 | - continue; | |
2799 | - if (!ccs_flush(head)) | |
2800 | - return false; | |
2801 | - ccs_set_string(head, ccs_group_name[idx]); | |
2802 | - ccs_set_space(head); | |
2803 | - ccs_set_string(head, group->group_name->name); | |
2804 | - ccs_set_space(head); | |
2805 | - if (idx == CCS_STRING_GROUP) { | |
2806 | - ccs_set_string(head, container_of | |
2807 | - (ptr, struct ccs_string_group, | |
2808 | - head)->member_name->name); | |
2809 | - } else if (idx == CCS_NUMBER_GROUP) { | |
2810 | - struct ccs_number_group *e = | |
2811 | - container_of(ptr, typeof(*e), head); | |
2812 | - ccs_print_number(head, e->radix & 3, e->value[0]); | |
2813 | - if (e->radix >> 2) { | |
2814 | - ccs_set_string(head, "-"); | |
2815 | - ccs_print_number(head, (e->radix >> 2) & 3, | |
2816 | - e->value[1]); | |
2817 | - } | |
2818 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
2819 | - } else if (idx == CCS_IP_GROUP) { | |
2820 | - ccs_print_ip(head, container_of | |
2821 | - (ptr, struct ccs_ip_group, head)); | |
2822 | -#endif | |
2823 | - } | |
2824 | - ccs_set_lf(head); | |
2825 | - } | |
2826 | - head->r.acl = NULL; | |
2827 | - return true; | |
2828 | -} | |
2829 | - | |
2830 | -/** | |
2831 | - * ccs_read_group - Read "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2832 | - * | |
2833 | - * @head: Pointer to "struct ccs_io_buffer". | |
2834 | - * | |
2835 | - * Returns true on success, false otherwise. | |
2836 | - * | |
2837 | - * Caller holds ccs_read_lock(). | |
2838 | - */ | |
2839 | -static bool ccs_read_group(struct ccs_io_buffer *head) | |
2840 | -{ | |
2841 | - while (head->r.step < CCS_MAX_GROUP) { | |
2842 | - const enum ccs_group_id idx = head->r.step; | |
2843 | - struct list_head *list = &ccs_group_list[idx]; | |
2844 | - list_for_each_cookie(head->r.group, list) { | |
2845 | - struct ccs_group *group = | |
2846 | - list_entry(head->r.group, typeof(*group), | |
2847 | - head.list); | |
2848 | - if (!ccs_read_subgroup(head, group, idx)) | |
2849 | - return false; | |
2850 | - } | |
2851 | - head->r.group = NULL; | |
2852 | - head->r.step++; | |
2853 | - } | |
2854 | - head->r.step = 0; | |
2855 | - return true; | |
2856 | -} | |
2857 | - | |
2858 | -/** | |
2859 | - * ccs_supervisor - Ask for the supervisor's decision. | |
2860 | - * | |
2861 | - * @r: Pointer to "struct ccs_request_info". | |
2862 | - * | |
2863 | - * Returns 0 if the supervisor decided to permit the access request, | |
2864 | - * CCS_RETRY_REQUEST if the supervisor decided to retry the access request, | |
2865 | - * -EPERM otherwise. | |
2866 | - */ | |
2867 | -static int ccs_supervisor(struct ccs_request_info *r) | |
2868 | -{ | |
2869 | - int error = -EPERM; | |
2870 | - int len; | |
2871 | - static unsigned int ccs_serial; | |
2872 | - struct ccs_query entry = { }; | |
2873 | - bool quota_exceeded = false; | |
2874 | - if (!r->matched_acl) | |
2875 | - return -EPERM; | |
2876 | - /* Get message. */ | |
2877 | - entry.query = ccs_init_log(r); | |
2878 | - if (!entry.query) | |
2879 | - return -EPERM; | |
2880 | - entry.query_len = strlen(entry.query) + 1; | |
2881 | - len = ccs_round2(entry.query_len); | |
2882 | - entry.acl = r->matched_acl; | |
2883 | - spin_lock(&ccs_query_list_lock); | |
2884 | - if (ccs_memory_quota[CCS_MEMORY_QUERY] && | |
2885 | - ccs_memory_used[CCS_MEMORY_QUERY] + len | |
2886 | - >= ccs_memory_quota[CCS_MEMORY_QUERY]) { | |
2887 | - quota_exceeded = true; | |
2888 | - } else { | |
2889 | - entry.serial = ccs_serial++; | |
2890 | - entry.retry = r->retry; | |
2891 | - ccs_memory_used[CCS_MEMORY_QUERY] += len; | |
2892 | - list_add_tail(&entry.list, &ccs_query_list); | |
2893 | - } | |
2894 | - spin_unlock(&ccs_query_list_lock); | |
2895 | - if (quota_exceeded) | |
2896 | - goto out; | |
2897 | - /* Give 10 seconds for supervisor's opinion. */ | |
2898 | - while (entry.timer < 10) { | |
2899 | - wake_up_all(&ccs_query_wait); | |
2900 | - if (wait_event_interruptible_timeout | |
2901 | - (ccs_answer_wait, entry.answer || | |
2902 | - !atomic_read(&ccs_query_observers), HZ)) | |
2903 | - break; | |
2904 | - else | |
2905 | - entry.timer++; | |
2906 | - } | |
2907 | - spin_lock(&ccs_query_list_lock); | |
2908 | - list_del(&entry.list); | |
2909 | - ccs_memory_used[CCS_MEMORY_QUERY] -= len; | |
2910 | - spin_unlock(&ccs_query_list_lock); | |
2911 | - switch (entry.answer) { | |
2912 | - case 3: /* Asked to retry by administrator. */ | |
2913 | - error = CCS_RETRY_REQUEST; | |
2914 | - r->retry++; | |
2915 | - break; | |
2916 | - case 1: | |
2917 | - /* Granted by administrator. */ | |
2918 | - error = 0; | |
2919 | - break; | |
2920 | - default: | |
2921 | - /* Timed out or rejected by administrator. */ | |
2922 | - break; | |
2923 | - } | |
2924 | -out: | |
2925 | - kfree(entry.query); | |
2926 | - return error; | |
2927 | -} | |
2928 | - | |
2929 | -/** | |
2930 | - * ccs_audit_log - Audit permission check log. | |
2931 | - * | |
2932 | - * @r: Pointer to "struct ccs_request_info". | |
2933 | - * | |
2934 | - * Returns 0 to grant the request, CCS_RETRY_REQUEST to retry the permission | |
2935 | - * check, -EPERM otherwise. | |
2936 | - */ | |
2937 | -int ccs_audit_log(struct ccs_request_info *r) | |
2938 | -{ | |
2939 | - /* Do not reject if not yet activated. */ | |
2940 | - if (!ccs_policy_loaded) | |
2941 | - return 0; | |
2942 | - /* Write /proc/ccs/audit unless quota exceeded. */ | |
2943 | - if (ccs_log_count[r->result] < ccs_log_quota[r->audit][r->result]) | |
2944 | - ccs_write_log(r); | |
2945 | - /* Nothing more to do unless denied. */ | |
2946 | - if (r->result != CCS_MATCHING_DENIED) | |
2947 | - return 0; | |
2948 | - /* Update policy violation counter if denied. */ | |
2949 | - ccs_update_stat(CCS_STAT_REQUEST_DENIED); | |
2950 | - /* Nothing more to do unless ccs-queryd is running. */ | |
2951 | - if (!atomic_read(&ccs_query_observers)) | |
2952 | - return -EPERM; | |
2953 | - /* Ask the ccs-queryd for decision. */ | |
2954 | - return ccs_supervisor(r); | |
2955 | -} | |
2956 | - | |
2957 | -/** | |
2958 | - * ccs_find_acl_by_qid - Get ACL by query id. | |
2959 | - * | |
2960 | - * @serial: Query ID assigned by ccs_supervisor(). | |
2961 | - * | |
2962 | - * Returns pointer to "struct ccs_acl_info" if found, NULL otherwise. | |
2963 | - */ | |
2964 | -static struct ccs_acl_info *ccs_find_acl_by_qid(unsigned int serial) | |
2965 | -{ | |
2966 | - struct ccs_query *ptr; | |
2967 | - struct ccs_acl_info *acl = NULL; | |
2968 | - spin_lock(&ccs_query_list_lock); | |
2969 | - list_for_each_entry(ptr, &ccs_query_list, list) { | |
2970 | - if (ptr->serial != serial) | |
2971 | - continue; | |
2972 | - acl = ptr->acl; | |
2973 | - break; | |
2974 | - } | |
2975 | - spin_unlock(&ccs_query_list_lock); | |
2976 | - return acl; | |
2977 | -} | |
2978 | - | |
2979 | -/** | |
2980 | - * ccs_read_query - Read access requests which violated policy in enforcing mode. | |
2981 | - * | |
2982 | - * @head: Pointer to "struct ccs_io_buffer". | |
2983 | - * | |
2984 | - * Returns nothing. | |
2985 | - */ | |
2986 | -static void ccs_read_query(struct ccs_io_buffer *head) | |
2987 | -{ | |
2988 | - struct list_head *tmp; | |
2989 | - unsigned int pos = 0; | |
2990 | - size_t len = 0; | |
2991 | - char *buf; | |
2992 | - if (head->r.w_pos) | |
2993 | - return; | |
2994 | - kfree(head->read_buf); | |
2995 | - head->read_buf = NULL; | |
2996 | - spin_lock(&ccs_query_list_lock); | |
2997 | - list_for_each(tmp, &ccs_query_list) { | |
2998 | - struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
2999 | - if (pos++ != head->r.query_index) | |
3000 | - continue; | |
3001 | - len = ptr->query_len; | |
3002 | - break; | |
3003 | - } | |
3004 | - spin_unlock(&ccs_query_list_lock); | |
3005 | - if (!len) { | |
3006 | - head->r.query_index = 0; | |
3007 | - return; | |
3008 | - } | |
3009 | - buf = kzalloc(len + 32, GFP_NOFS); | |
3010 | - if (!buf) | |
3011 | - return; | |
3012 | - pos = 0; | |
3013 | - spin_lock(&ccs_query_list_lock); | |
3014 | - list_for_each(tmp, &ccs_query_list) { | |
3015 | - struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3016 | - if (pos++ != head->r.query_index) | |
3017 | - continue; | |
3018 | - /* | |
3019 | - * Some query can be skipped because ccs_query_list | |
3020 | - * can change, but I don't care. | |
3021 | - */ | |
3022 | - if (len == ptr->query_len) | |
3023 | - snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, | |
3024 | - ptr->retry, ptr->query); | |
3025 | - break; | |
3026 | - } | |
3027 | - spin_unlock(&ccs_query_list_lock); | |
3028 | - if (buf[0]) { | |
3029 | - head->read_buf = buf; | |
3030 | - head->r.w[head->r.w_pos++] = buf; | |
3031 | - head->r.query_index++; | |
3032 | - } else { | |
3033 | - kfree(buf); | |
3034 | - } | |
3035 | -} | |
3036 | - | |
3037 | -/** | |
3038 | - * ccs_write_answer - Write the supervisor's decision. | |
3039 | - * | |
3040 | - * @head: Pointer to "struct ccs_io_buffer". | |
3041 | - * | |
3042 | - * Returns 0 on success, -EINVAL otherwise. | |
3043 | - */ | |
3044 | -static int ccs_write_answer(struct ccs_io_buffer *head) | |
3045 | -{ | |
3046 | - char *data = head->write_buf; | |
3047 | - struct list_head *tmp; | |
3048 | - unsigned int serial; | |
3049 | - unsigned int answer; | |
3050 | - spin_lock(&ccs_query_list_lock); | |
3051 | - list_for_each(tmp, &ccs_query_list) { | |
3052 | - struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3053 | - ptr->timer = 0; | |
3054 | - } | |
3055 | - spin_unlock(&ccs_query_list_lock); | |
3056 | - if (sscanf(data, "A%u=%u", &serial, &answer) != 2) | |
3057 | - return -EINVAL; | |
3058 | - spin_lock(&ccs_query_list_lock); | |
3059 | - list_for_each(tmp, &ccs_query_list) { | |
3060 | - struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3061 | - if (ptr->serial != serial) | |
3062 | - continue; | |
3063 | - ptr->answer = (u8) answer; | |
3064 | - /* Remove from ccs_query_list. */ | |
3065 | - if (ptr->answer) { | |
3066 | - list_del(&ptr->list); | |
3067 | - INIT_LIST_HEAD(&ptr->list); | |
3068 | - } | |
3069 | - break; | |
3070 | - } | |
3071 | - spin_unlock(&ccs_query_list_lock); | |
3072 | - wake_up_all(&ccs_answer_wait); | |
3073 | - return 0; | |
3074 | -} | |
3075 | - | |
3076 | -/** | |
3077 | - * ccs_read_version - Get version. | |
3078 | - * | |
3079 | - * @head: Pointer to "struct ccs_io_buffer". | |
3080 | - * | |
3081 | - * Returns nothing. | |
3082 | - */ | |
3083 | -static void ccs_read_version(struct ccs_io_buffer *head) | |
3084 | -{ | |
3085 | - if (head->r.eof) | |
3086 | - return; | |
3087 | - ccs_set_string(head, "1.8.3"); | |
3088 | - head->r.eof = true; | |
3089 | -} | |
3090 | - | |
3091 | -/** | |
3092 | - * ccs_update_stat - Update statistic counters. | |
3093 | - * | |
3094 | - * @index: Index for policy type. | |
3095 | - * | |
3096 | - * Returns nothing. | |
3097 | - */ | |
3098 | -static void ccs_update_stat(const u8 index) | |
3099 | -{ | |
3100 | - struct timeval tv; | |
3101 | - do_gettimeofday(&tv); | |
3102 | - /* | |
3103 | - * I don't use atomic operations because race condition is not fatal. | |
3104 | - */ | |
3105 | - ccs_stat_updated[index]++; | |
3106 | - ccs_stat_modified[index] = tv.tv_sec; | |
3107 | -} | |
3108 | - | |
3109 | -/** | |
3110 | - * ccs_read_stat - Read statistic data. | |
3111 | - * | |
3112 | - * @head: Pointer to "struct ccs_io_buffer". | |
3113 | - * | |
3114 | - * Returns nothing. | |
3115 | - */ | |
3116 | -static void ccs_read_stat(struct ccs_io_buffer *head) | |
3117 | -{ | |
3118 | - u8 i; | |
3119 | - for (i = 0; i < CCS_MAX_POLICY_STAT; i++) { | |
3120 | - static const char * const k[CCS_MAX_POLICY_STAT] = { | |
3121 | - [CCS_STAT_POLICY_UPDATES] = "Policy updated:", | |
3122 | - [CCS_STAT_REQUEST_DENIED] = "Requests denied:", | |
3123 | - }; | |
3124 | - ccs_io_printf(head, "stat %s %u", k[i], ccs_stat_updated[i]); | |
3125 | - if (ccs_stat_modified[i]) { | |
3126 | - struct ccs_time stamp; | |
3127 | - ccs_convert_time(ccs_stat_modified[i], &stamp); | |
3128 | - ccs_io_printf(head, " (Last: %04u/%02u/%02u " | |
3129 | - "%02u:%02u:%02u)", | |
3130 | - stamp.year, stamp.month, stamp.day, | |
3131 | - stamp.hour, stamp.min, stamp.sec); | |
3132 | - } | |
3133 | - ccs_set_lf(head); | |
3134 | - } | |
3135 | - for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | |
3136 | - ccs_io_printf(head, "stat Memory used by %s: %u\n", | |
3137 | - ccs_memory_headers[i], ccs_memory_used[i]); | |
3138 | -} | |
3139 | - | |
3140 | -/** | |
3141 | - * ccs_read_quota - Read quota data. | |
3142 | - * | |
3143 | - * @head: Pointer to "struct ccs_io_buffer". | |
3144 | - * | |
3145 | - * Returns true on success, false otherwise. | |
3146 | - */ | |
3147 | -static bool ccs_read_quota(struct ccs_io_buffer *head) | |
3148 | -{ | |
3149 | - unsigned int i; | |
3150 | - while (head->r.step < CCS_MAX_MEMORY_STAT) { | |
3151 | - i = head->r.step++; | |
3152 | - if (!ccs_memory_quota[i]) | |
3153 | - continue; | |
3154 | - ccs_io_printf(head, "quota memory %s %u\n", | |
3155 | - ccs_memory_headers[i], ccs_memory_quota[i]); | |
3156 | - } | |
3157 | - while (head->r.step < CCS_MAX_GROUP + CCS_MAX_MEMORY_STAT) { | |
3158 | - unsigned int a; | |
3159 | - unsigned int d; | |
3160 | - unsigned int u; | |
3161 | - if (!ccs_flush(head)) | |
3162 | - return false; | |
3163 | - i = head->r.step - CCS_MAX_MEMORY_STAT; | |
3164 | - a = ccs_log_quota[i][CCS_MATCHING_ALLOWED]; | |
3165 | - d = ccs_log_quota[i][CCS_MATCHING_DENIED]; | |
3166 | - u = ccs_log_quota[i][CCS_MATCHING_UNMATCHED]; | |
3167 | - if (a || d || u) | |
3168 | - ccs_io_printf(head, "quota audit[%u] allowed=%u" | |
3169 | - " denied=%u unmatched=%u\n", i, a, d, u); | |
3170 | - head->r.step++; | |
3171 | - } | |
3172 | - head->r.step = 0; | |
3173 | - return true; | |
3174 | -} | |
3175 | - | |
3176 | -/** | |
3177 | - * ccs_write_memory_quota - Set memory quota. | |
3178 | - * | |
3179 | - * @data: Line to parse. | |
3180 | - * | |
3181 | - * Returns 0 on success, -EINVAL otherwise. | |
3182 | - */ | |
3183 | -static int ccs_write_memory_quota(char *data) | |
3184 | -{ | |
3185 | - u8 i; | |
3186 | - for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | |
3187 | - if (ccs_str_starts(&data, ccs_memory_headers[i])) { | |
3188 | - if (*data == ' ') | |
3189 | - data++; | |
3190 | - ccs_memory_quota[i] = | |
3191 | - simple_strtoul(data, NULL, 10); | |
3192 | - return 0; | |
3193 | - } | |
3194 | - return -EINVAL; | |
3195 | -} | |
3196 | - | |
3197 | -/** | |
3198 | - * ccs_write_audit_quota - Set audit log quota. | |
3199 | - * | |
3200 | - * @data: Line to parse. | |
3201 | - * | |
3202 | - * Returns 0 on success, -EINVAL otherwise. | |
3203 | - */ | |
3204 | -static int ccs_write_audit_quota(char *data) | |
3205 | -{ | |
3206 | - unsigned int i; | |
3207 | - if (sscanf(data, "audit[%u]", &i) != 1 || i >= CCS_MAX_LOG_QUOTA) | |
3208 | - return -EINVAL; | |
3209 | - data = strchr(data, ' '); | |
3210 | - if (!data++) | |
3211 | - return -EINVAL; | |
3212 | - while (1) { | |
3213 | - unsigned int logs; | |
3214 | - char *cp = strchr(data, ' '); | |
3215 | - if (cp) | |
3216 | - *cp++ = '\0'; | |
3217 | - if (sscanf(data, "allowed=%u", &logs) == 1) | |
3218 | - ccs_log_quota[i][CCS_MATCHING_ALLOWED] = logs; | |
3219 | - else if (sscanf(data, "denied=%u", &logs) == 1) | |
3220 | - ccs_log_quota[i][CCS_MATCHING_DENIED] = logs; | |
3221 | - else if (sscanf(data, "unmatched=%u", &logs) == 1) | |
3222 | - ccs_log_quota[i][CCS_MATCHING_UNMATCHED] = logs; | |
3223 | - if (!cp) | |
3224 | - break; | |
3225 | - data = cp; | |
3226 | - } | |
3227 | - return 0; | |
3228 | -} | |
3229 | - | |
3230 | -/** | |
3231 | - * ccs_print_bprm - Print "struct linux_binprm" for auditing. | |
3232 | - * | |
3233 | - * @bprm: Pointer to "struct linux_binprm". | |
3234 | - * @dump: Pointer to "struct ccs_page_dump". | |
3235 | - * | |
3236 | - * Returns the contents of @bprm on success, NULL otherwise. | |
3237 | - * | |
3238 | - * This function uses kzalloc(), so caller must kfree() if this function | |
3239 | - * didn't return NULL. | |
3240 | - */ | |
3241 | -static char *ccs_print_bprm(struct linux_binprm *bprm, | |
3242 | - struct ccs_page_dump *dump) | |
3243 | -{ | |
3244 | - static const int ccs_buffer_len = 4096 * 2; | |
3245 | - char *buffer = kzalloc(ccs_buffer_len, GFP_NOFS); | |
3246 | - char *cp; | |
3247 | - char *last_start; | |
3248 | - unsigned long pos = bprm->p; | |
3249 | - int offset = pos % PAGE_SIZE; | |
3250 | - int argv_count = bprm->argc; | |
3251 | - int envp_count = bprm->envc; | |
3252 | - bool skip = false; | |
3253 | - bool env_value = false; | |
3254 | - if (!buffer) | |
3255 | - return NULL; | |
3256 | - cp = buffer + snprintf(buffer, ccs_buffer_len - 1, " argc=%d envc=%d", | |
3257 | - argv_count, envp_count); | |
3258 | - last_start = cp; | |
3259 | - while (argv_count || envp_count) { | |
3260 | - if (!ccs_dump_page(bprm, pos, dump)) { | |
3261 | - kfree(buffer); | |
3262 | - return NULL; | |
3263 | - } | |
3264 | - pos += PAGE_SIZE - offset; | |
3265 | - /* Read. */ | |
3266 | - while (offset < PAGE_SIZE) { | |
3267 | - const char *kaddr = dump->data; | |
3268 | - const unsigned char c = kaddr[offset++]; | |
3269 | - int len; | |
3270 | - /* Check for end of buffer. */ | |
3271 | - if (skip) { | |
3272 | - if (c) | |
3273 | - continue; | |
3274 | - goto reset; | |
3275 | - } | |
3276 | - len = buffer + ccs_buffer_len - cp - 1; | |
3277 | - if (len <= 32 && c) { | |
3278 | - cp = last_start; | |
3279 | - skip = true; | |
3280 | - continue; | |
3281 | - } | |
3282 | - /* Print argv[$index]=" or envp[" part. */ | |
3283 | - if (cp == last_start) { | |
3284 | - int l; | |
3285 | - if (argv_count) | |
3286 | - l = snprintf(cp, len, " argv[%u]=\"", | |
3287 | - bprm->argc - argv_count); | |
3288 | - else | |
3289 | - l = snprintf(cp, len, " envp[\""); | |
3290 | - cp += l; | |
3291 | - len -= l; | |
3292 | - } | |
3293 | - if (c > ' ' && c < 127 && c != '\\') { | |
3294 | - /* Print "]=" part if printing environ. */ | |
3295 | - if (c == '=' && !argv_count && !env_value) { | |
3296 | - cp += snprintf(cp, len, "\"]=\""); | |
3297 | - env_value = true; | |
3298 | - } else | |
3299 | - *cp++ = c; | |
3300 | - continue; | |
3301 | - } | |
3302 | - if (c) { | |
3303 | - *cp++ = '\\'; | |
3304 | - *cp++ = (c >> 6) + '0'; | |
3305 | - *cp++ = ((c >> 3) & 7) + '0'; | |
3306 | - *cp++ = (c & 7) + '0'; | |
3307 | - continue; | |
3308 | - } | |
3309 | - /* Print "]=" part if not yet printed. */ | |
3310 | - if (!argv_count && !env_value) | |
3311 | - cp += snprintf(cp, len, "\"]=\""); | |
3312 | - *cp++ = '"'; | |
3313 | - last_start = cp; | |
3314 | -reset: | |
3315 | - skip = false; | |
3316 | - env_value = false; | |
3317 | - if (argv_count) | |
3318 | - argv_count--; | |
3319 | - else if (envp_count) | |
3320 | - envp_count--; | |
3321 | - if (!argv_count && !envp_count) | |
3322 | - break; | |
3323 | - } | |
3324 | - offset = 0; | |
3325 | - } | |
3326 | - *cp = '\0'; | |
3327 | - return buffer; | |
3328 | -} | |
3329 | - | |
3330 | -/** | |
3331 | - * ccs_filetype - Get string representation of file type. | |
3332 | - * | |
3333 | - * @mode: Mode value for stat(). | |
3334 | - * | |
3335 | - * Returns file type string. | |
3336 | - */ | |
3337 | -static inline const char *ccs_filetype(const umode_t mode) | |
3338 | -{ | |
3339 | - switch (mode & S_IFMT) { | |
3340 | - case S_IFREG: | |
3341 | - case 0: | |
3342 | - return "file"; | |
3343 | - case S_IFDIR: | |
3344 | - return "directory"; | |
3345 | - case S_IFLNK: | |
3346 | - return "symlink"; | |
3347 | - case S_IFIFO: | |
3348 | - return "fifo"; | |
3349 | - case S_IFSOCK: | |
3350 | - return "socket"; | |
3351 | - case S_IFBLK: | |
3352 | - return "block"; | |
3353 | - case S_IFCHR: | |
3354 | - return "char"; | |
3355 | - } | |
3356 | - return "unknown"; /* This should not happen. */ | |
3357 | -} | |
3358 | - | |
3359 | -/** | |
3360 | - * ccs_print_trailer - Get misc info of audit log. | |
3361 | - * | |
3362 | - * @r: Pointer to "struct ccs_request_info". | |
3363 | - * | |
3364 | - * Returns string representation. | |
3365 | - * | |
3366 | - * This function uses kmalloc(), so caller must kfree() if this function | |
3367 | - * didn't return NULL. | |
3368 | - */ | |
3369 | -static char *ccs_print_trailer(struct ccs_request_info *r) | |
3370 | -{ | |
3371 | - const char *handler = | |
3372 | - ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER ? "" : "!"; | |
3373 | - const char *exe = r->exename.name; | |
3374 | - const char *domain = ccs_current_domain()->domainname->name; | |
3375 | - const int ccs_buffer_len = 2000 + strlen(exe) + strlen(domain); | |
3376 | - char *buffer = kmalloc(ccs_buffer_len, GFP_NOFS); | |
3377 | - int pos; | |
3378 | - u8 i; | |
3379 | - if (!buffer) | |
3380 | - return NULL; | |
3381 | - pos = snprintf(buffer, ccs_buffer_len - 1, " task.pid=%u task.ppid=%u" | |
3382 | - " task.uid=%u task.gid=%u task.euid=%u task.egid=%u" | |
3383 | - " task.suid=%u task.sgid=%u task.fsuid=%u task.fsgid=%u" | |
3384 | - " task.type%s=execute_handler task.exe=\"%s\"" | |
3385 | - " task.domain=\"%s\"", ccs_sys_getpid(), | |
3386 | - ccs_sys_getppid(), current_uid(), current_gid(), | |
3387 | - current_euid(), current_egid(), current_suid(), | |
3388 | - current_sgid(), current_fsuid(), current_fsgid(), | |
3389 | - handler, exe, domain); | |
3390 | - if (!r->obj.path[0].dentry && !r->obj.path[1].dentry) | |
3391 | - goto no_obj_info; | |
3392 | - ccs_get_attributes(r); | |
3393 | - for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | |
3394 | - char objname[32]; | |
3395 | - struct ccs_mini_stat *stat; | |
3396 | - unsigned int dev; | |
3397 | - umode_t mode; | |
3398 | - if (!r->obj.stat_valid[i]) | |
3399 | - continue; | |
3400 | - stat = &r->obj.stat[i]; | |
3401 | - dev = stat->dev; | |
3402 | - mode = stat->mode; | |
3403 | - memset(objname, 0, sizeof(objname)); | |
3404 | - snprintf(objname, sizeof(objname) - 1, "%s%s.", | |
3405 | - ccs_get_sarg(r->type, (i >> 1)), | |
3406 | - i & 1 ? ".parent" : ""); | |
3407 | - pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | |
3408 | - " %suid=%u %sgid=%u %sino=%lu %smajor=%u" | |
3409 | - " %sminor=%u %sperm=0%o %stype=%s" | |
3410 | - " %sfsmagic=0x%lX", objname, stat->uid, | |
3411 | - objname, stat->gid, objname, (unsigned long) | |
3412 | - stat->ino, objname, MAJOR(dev), objname, | |
3413 | - MINOR(dev), objname, mode & S_IALLUGO, objname, | |
3414 | - ccs_filetype(mode), objname, stat->fsmagic); | |
3415 | - if (S_ISCHR(mode) || S_ISBLK(mode)) { | |
3416 | - dev = stat->rdev; | |
3417 | - pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | |
3418 | - " %sdev_major=%u %sdev_minor=%u", | |
3419 | - objname, MAJOR(dev), objname, | |
3420 | - MINOR(dev)); | |
3421 | - } | |
3422 | - } | |
3423 | -no_obj_info: | |
3424 | - if (pos < ccs_buffer_len - 1) | |
3425 | - return buffer; | |
3426 | - kfree(buffer); | |
3427 | - return NULL; | |
3428 | -} | |
3429 | - | |
3430 | -/** | |
3431 | - * ccs_print_param - Get arg info of audit log. | |
3432 | - * | |
3433 | - * @r: Pointer to "struct ccs_request_info". | |
3434 | - * @buf: Buffer to write. | |
3435 | - * @len: Size of @buf in bytes. | |
3436 | - */ | |
3437 | -static int ccs_print_param(struct ccs_request_info *r, char *buf, int len) | |
3438 | -{ | |
3439 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
3440 | - /* Make sure that IP address argument is ready. */ | |
3441 | - char ip[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; | |
3442 | - switch (r->type) { | |
3443 | - case CCS_MAC_INET_STREAM_BIND: | |
3444 | - case CCS_MAC_INET_STREAM_LISTEN: | |
3445 | - case CCS_MAC_INET_STREAM_CONNECT: | |
3446 | - case CCS_MAC_INET_STREAM_ACCEPT: | |
3447 | - case CCS_MAC_INET_DGRAM_BIND: | |
3448 | - case CCS_MAC_INET_DGRAM_SEND: | |
3449 | - case CCS_MAC_INET_RAW_BIND: | |
3450 | - case CCS_MAC_INET_RAW_SEND: | |
3451 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3452 | - case CCS_MAC_INET_DGRAM_RECV: | |
3453 | - case CCS_MAC_INET_RAW_RECV: | |
3454 | -#endif | |
3455 | - if (!r->param.ip) | |
3456 | - return 0; | |
3457 | - if (r->param.is_ipv6) { | |
3458 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
3459 | - snprintf(ip, sizeof(ip), "%pI6c", | |
3460 | - (const struct in6_addr *) r->param.ip); | |
3461 | -#else | |
3462 | - ip6_compressed_string(ip, (const u8 *) r->param.ip); | |
3463 | -#endif | |
3464 | - } else { | |
3465 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
3466 | - snprintf(ip, sizeof(ip), "%pI4", r->param.ip); | |
3467 | -#else | |
3468 | - ip4_string(ip, r->param.ip); | |
3469 | -#endif | |
3470 | - } | |
3471 | - break; | |
3472 | - default: | |
3473 | - break; | |
3474 | - } | |
3475 | -#endif | |
3476 | - /* Make sure that string arguments are ready. */ | |
3477 | - if (!r->param.s[0] && r->obj.path[0].dentry) { | |
3478 | - ccs_populate_patharg(r, true); | |
3479 | - if (!r->param.s[0]) | |
3480 | - return 0; | |
3481 | - } | |
3482 | - if (!r->param.s[1] && r->obj.path[1].dentry) { | |
3483 | - ccs_populate_patharg(r, false); | |
3484 | - if (!r->param.s[1]) | |
3485 | - return 0; | |
3486 | - } | |
3487 | - switch (r->type) { | |
3488 | - int pos; | |
3489 | - u8 i; | |
3490 | - case CCS_MAC_EXECUTE: | |
3491 | - return snprintf(buf, len, " exec=\"%s\" path=\"%s\"", | |
3492 | - r->param.s[1]->name, r->param.s[0]->name); | |
3493 | - case CCS_MAC_READ: | |
3494 | - case CCS_MAC_WRITE: | |
3495 | - case CCS_MAC_APPEND: | |
3496 | - case CCS_MAC_UNLINK: | |
3497 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
3498 | - case CCS_MAC_GETATTR: | |
3499 | -#endif | |
3500 | - case CCS_MAC_RMDIR: | |
3501 | - case CCS_MAC_TRUNCATE: | |
3502 | - case CCS_MAC_CHROOT: | |
3503 | - return snprintf(buf, len, " path=\"%s\"", r->param.s[0]->name); | |
3504 | - case CCS_MAC_CREATE: | |
3505 | - case CCS_MAC_MKDIR: | |
3506 | - case CCS_MAC_MKFIFO: | |
3507 | - case CCS_MAC_MKSOCK: | |
3508 | - return snprintf(buf, len, " path=\"%s\" perm=0%lo", | |
3509 | - r->param.s[0]->name, r->param.i[0]); | |
3510 | - case CCS_MAC_SYMLINK: | |
3511 | - return snprintf(buf, len, " path=\"%s\" target=\"%s\"", | |
3512 | - r->param.s[0]->name, r->param.s[1]->name); | |
3513 | - case CCS_MAC_MKBLOCK: | |
3514 | - case CCS_MAC_MKCHAR: | |
3515 | - return snprintf(buf, len, " path=\"%s\" perm=0%lo " | |
3516 | - "dev_major=%lu dev_minor=%lu", | |
3517 | - r->param.s[0]->name, r->param.i[0], | |
3518 | - r->param.i[1], r->param.i[2]); | |
3519 | - case CCS_MAC_LINK: | |
3520 | - case CCS_MAC_RENAME: | |
3521 | - return snprintf(buf, len, " old_path=\"%s\" new_path=\"%s\"", | |
3522 | - r->param.s[0]->name, r->param.s[1]->name); | |
3523 | - case CCS_MAC_CHMOD: | |
3524 | - return snprintf(buf, len, " path=\"%s\" perm=0%lo", | |
3525 | - r->param.s[0]->name, r->param.i[0]); | |
3526 | - case CCS_MAC_CHOWN: | |
3527 | - return snprintf(buf, len, " path=\"%s\" uid=%lu", | |
3528 | - r->param.s[0]->name, r->param.i[0]); | |
3529 | - case CCS_MAC_CHGRP: | |
3530 | - return snprintf(buf, len, " path=\"%s\" gid=%lu", | |
3531 | - r->param.s[0]->name, r->param.i[0]); | |
3532 | - case CCS_MAC_IOCTL: | |
3533 | - return snprintf(buf, len, " path=\"%s\" cmd=0x%lX", | |
3534 | - r->param.s[0]->name, r->param.i[0]); | |
3535 | - case CCS_MAC_MOUNT: | |
3536 | - pos = 0; | |
3537 | - for (i = 0; i < 4; i++) { | |
3538 | - if (i == 3) | |
3539 | - pos += snprintf(buf + pos, pos < len ? | |
3540 | - len - pos : 0, " flags=0x%lX", | |
3541 | - r->param.i[0]); | |
3542 | - if (!r->param.s[i]) | |
3543 | - continue; | |
3544 | - pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3545 | - " %s=\"%s\"", | |
3546 | - ccs_get_sarg(CCS_MAC_MOUNT, i), | |
3547 | - r->param.s[i]->name); | |
3548 | - } | |
3549 | - return pos; | |
3550 | - case CCS_MAC_UMOUNT: | |
3551 | - return snprintf(buf, len, " path=\"%s\" flags=0x%lX", | |
3552 | - r->param.s[0]->name, r->param.i[0]); | |
3553 | - case CCS_MAC_PIVOT_ROOT: | |
3554 | - return snprintf(buf, len, " new_root=\"%s\" put_old=\"%s\"", | |
3555 | - r->param.s[0]->name, r->param.s[1]->name); | |
3556 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
3557 | - case CCS_MAC_ENVIRON: | |
3558 | - return snprintf(buf, len, " name=\"%s\" value=\"%s\"" | |
3559 | - " exec=\"%s\" path=\"%s\"", | |
3560 | - r->param.s[2]->name, r->param.s[3]->name, | |
3561 | - r->param.s[1]->name, r->param.s[0]->name); | |
3562 | -#endif | |
3563 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
3564 | - case CCS_MAC_INET_STREAM_BIND: | |
3565 | - case CCS_MAC_INET_STREAM_LISTEN: | |
3566 | - case CCS_MAC_INET_STREAM_CONNECT: | |
3567 | - case CCS_MAC_INET_STREAM_ACCEPT: | |
3568 | - case CCS_MAC_INET_DGRAM_BIND: | |
3569 | - case CCS_MAC_INET_DGRAM_SEND: | |
3570 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3571 | - case CCS_MAC_INET_DGRAM_RECV: | |
3572 | -#endif | |
3573 | - return snprintf(buf, len, " ip=%s port=%lu", ip, | |
3574 | - r->param.i[0]); | |
3575 | - case CCS_MAC_INET_RAW_BIND: | |
3576 | - case CCS_MAC_INET_RAW_SEND: | |
3577 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3578 | - case CCS_MAC_INET_RAW_RECV: | |
3579 | -#endif | |
3580 | - return snprintf(buf, len, " ip=%s proto=%lu", ip, | |
3581 | - r->param.i[0]); | |
3582 | - case CCS_MAC_UNIX_STREAM_BIND: | |
3583 | - case CCS_MAC_UNIX_STREAM_LISTEN: | |
3584 | - case CCS_MAC_UNIX_STREAM_CONNECT: | |
3585 | - case CCS_MAC_UNIX_STREAM_ACCEPT: | |
3586 | - case CCS_MAC_UNIX_DGRAM_BIND: | |
3587 | - case CCS_MAC_UNIX_DGRAM_SEND: | |
3588 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3589 | - case CCS_MAC_UNIX_DGRAM_RECV: | |
3590 | -#endif | |
3591 | - case CCS_MAC_UNIX_SEQPACKET_BIND: | |
3592 | - case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
3593 | - case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
3594 | - case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
3595 | - return snprintf(buf, len, " addr=\"%s\"", r->param.s[0]->name); | |
3596 | -#endif | |
3597 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
3598 | - case CCS_MAC_PTRACE: | |
3599 | - return snprintf(buf, len, " cmd=%lu domain=\"%s\"", | |
3600 | - r->param.i[0], r->param.s[0]->name); | |
3601 | -#endif | |
3602 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
3603 | - case CCS_MAC_SIGNAL: | |
3604 | - return snprintf(buf, len, " sig=%lu", r->param.i[0]); | |
3605 | -#endif | |
3606 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
3607 | - case CCS_MAC_MANUAL_DOMAIN_TRANSITION: | |
3608 | - return snprintf(buf, len, " domain=\"%s\"", | |
3609 | - r->param.s[0]->name); | |
3610 | -#endif | |
3611 | - default: | |
3612 | - break; | |
3613 | - } | |
3614 | - return 0; | |
3615 | -} | |
3616 | - | |
3617 | -/** | |
3618 | - * ccs_init_log - Allocate buffer for audit logs. | |
3619 | - * | |
3620 | - * @r: Pointer to "struct ccs_request_info". | |
3621 | - * | |
3622 | - * Returns pointer to allocated memory. | |
3623 | - * | |
3624 | - * This function uses kzalloc(), so caller must kfree() if this function | |
3625 | - * didn't return NULL. | |
3626 | - */ | |
3627 | -static char *ccs_init_log(struct ccs_request_info *r) | |
3628 | -{ | |
3629 | - const pid_t gpid = task_pid_nr(current); | |
3630 | - struct timeval tv; | |
3631 | - struct ccs_time stamp; | |
3632 | - static const char * const k[CCS_MAX_MATCHING] = { | |
3633 | - [CCS_MATCHING_UNMATCHED] = "unmatched", | |
3634 | - [CCS_MATCHING_ALLOWED] = "allowed", | |
3635 | - [CCS_MATCHING_DENIED] = "denied", | |
3636 | - }; | |
3637 | - char *buf; | |
3638 | - const char *bprm_info; | |
3639 | - const char *trailer; | |
3640 | - int len; | |
3641 | - if (!r->exename.name && !ccs_get_exename(&r->exename)) | |
3642 | - return NULL; | |
3643 | - do_gettimeofday(&tv); | |
3644 | - ccs_convert_time(tv.tv_sec, &stamp); | |
3645 | - trailer = ccs_print_trailer(r); | |
3646 | - if (r->bprm) | |
3647 | - bprm_info = ccs_print_bprm(r->bprm, &r->dump); | |
3648 | - else | |
3649 | - bprm_info = NULL; | |
3650 | - len = 0; | |
3651 | - while (1) { | |
3652 | - int pos; | |
3653 | - buf = kzalloc(len, GFP_NOFS); | |
3654 | - if (!buf) | |
3655 | - break; | |
3656 | - pos = snprintf(buf, len, "#%04u/%02u/%02u %02u:%02u:%02u# " | |
3657 | - "global-pid=%u result=%s priority=%u / %s", | |
3658 | - stamp.year, stamp.month, stamp.day, stamp.hour, | |
3659 | - stamp.min, stamp.sec, gpid, k[r->result], | |
3660 | - r->matched_acl ? r->matched_acl->priority : 0, | |
3661 | - ccs_mac_keywords[r->type]); | |
3662 | - pos += ccs_print_param(r, buf + pos, | |
3663 | - pos < len ? len - pos : 0); | |
3664 | - if (bprm_info) | |
3665 | - pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3666 | - "%s", bprm_info); | |
3667 | - if (trailer) | |
3668 | - pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3669 | - "%s", trailer); | |
3670 | - pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3671 | - "\n") + 1; | |
3672 | - if (pos <= len) | |
3673 | - break; | |
3674 | - kfree(buf); | |
3675 | - len = pos; | |
3676 | - } | |
3677 | - kfree(bprm_info); | |
3678 | - kfree(trailer); | |
3679 | - return buf; | |
3680 | -} | |
3681 | - | |
3682 | -/** | |
3683 | - * ccs_write_log - Write an audit log. | |
3684 | - * | |
3685 | - * @r: Pointer to "struct ccs_request_info". | |
3686 | - * | |
3687 | - * Returns nothing. | |
3688 | - */ | |
3689 | -static void ccs_write_log(struct ccs_request_info *r) | |
3690 | -{ | |
3691 | - struct ccs_log *entry; | |
3692 | - bool quota_exceeded = false; | |
3693 | - int len; | |
3694 | - char *buf = ccs_init_log(r); | |
3695 | - if (!buf) | |
3696 | - return; | |
3697 | - entry = kzalloc(sizeof(*entry), GFP_NOFS); | |
3698 | - if (!entry) { | |
3699 | - kfree(buf); | |
3700 | - return; | |
3701 | - } | |
3702 | - entry->log = buf; | |
3703 | - len = ccs_round2(strlen(buf) + 1); | |
3704 | - /* | |
3705 | - * The entry->size is used for memory quota checks. | |
3706 | - * Don't go beyond strlen(entry->log). | |
3707 | - */ | |
3708 | - entry->size = len + ccs_round2(sizeof(*entry)); | |
3709 | - entry->result = r->result; | |
3710 | - spin_lock(&ccs_log_lock); | |
3711 | - if (ccs_memory_quota[CCS_MEMORY_AUDIT] && | |
3712 | - ccs_memory_used[CCS_MEMORY_AUDIT] + entry->size >= | |
3713 | - ccs_memory_quota[CCS_MEMORY_AUDIT]) { | |
3714 | - quota_exceeded = true; | |
3715 | - } else { | |
3716 | - ccs_memory_used[CCS_MEMORY_AUDIT] += entry->size; | |
3717 | - list_add_tail(&entry->list, &ccs_log); | |
3718 | - ccs_log_count[entry->result]++; | |
3719 | - } | |
3720 | - spin_unlock(&ccs_log_lock); | |
3721 | - if (quota_exceeded) { | |
3722 | - kfree(buf); | |
3723 | - kfree(entry); | |
3724 | - return; | |
3725 | - } | |
3726 | - wake_up(&ccs_log_wait); | |
3727 | -} | |
3728 | - | |
3729 | -/** | |
3730 | - * ccs_read_log - Read an audit log. | |
3731 | - * | |
3732 | - * @head: Pointer to "struct ccs_io_buffer". | |
3733 | - * | |
3734 | - * Returns nothing. | |
3735 | - */ | |
3736 | -static void ccs_read_log(struct ccs_io_buffer *head) | |
3737 | -{ | |
3738 | - struct ccs_log *ptr = NULL; | |
3739 | - if (head->r.w_pos) | |
3740 | - return; | |
3741 | - kfree(head->read_buf); | |
3742 | - head->read_buf = NULL; | |
3743 | - spin_lock(&ccs_log_lock); | |
3744 | - if (!list_empty(&ccs_log)) { | |
3745 | - ptr = list_entry(ccs_log.next, typeof(*ptr), list); | |
3746 | - list_del(&ptr->list); | |
3747 | - ccs_log_count[ptr->result]--; | |
3748 | - ccs_memory_used[CCS_MEMORY_AUDIT] -= ptr->size; | |
3749 | - } | |
3750 | - spin_unlock(&ccs_log_lock); | |
3751 | - if (ptr) { | |
3752 | - head->read_buf = ptr->log; | |
3753 | - head->r.w[head->r.w_pos++] = head->read_buf; | |
3754 | - kfree(ptr); | |
3755 | - } | |
3756 | -} | |
3757 | - | |
3758 | -/** | |
3759 | - * ccs_transit_domain - Transit to other domain. | |
3760 | - * | |
3761 | - * @domainname: The name of domain. | |
3762 | - * | |
3763 | - * Returns true on success, false otherwise. | |
3764 | - * | |
3765 | - * Caller holds ccs_read_lock(). | |
3766 | - */ | |
3767 | -bool ccs_transit_domain(const char *domainname) | |
3768 | -{ | |
3769 | - struct ccs_security *security = ccs_current_security(); | |
3770 | - struct ccs_domain_info e = { }; | |
3771 | - struct ccs_domain_info *entry = ccs_find_domain(domainname); | |
3772 | - if (entry) { | |
3773 | - security->ccs_domain_info = entry; | |
3774 | - return true; | |
3775 | - } | |
3776 | - /* Requested domain does not exist. */ | |
3777 | - /* Don't create requested domain if domainname is invalid. */ | |
3778 | - if (!ccs_correct_domain(domainname)) | |
3779 | - return false; | |
3780 | - e.domainname = ccs_get_name(domainname); | |
3781 | - if (!e.domainname) | |
3782 | - return false; | |
3783 | - if (mutex_lock_interruptible(&ccs_policy_lock)) | |
3784 | - goto out; | |
3785 | - entry = ccs_find_domain(domainname); | |
3786 | - if (entry) | |
3787 | - goto done; | |
3788 | - entry = ccs_commit_ok(&e, sizeof(e)); | |
3789 | - if (!entry) | |
3790 | - goto done; | |
3791 | - list_add_tail_rcu(&entry->list, &ccs_domain_list); | |
3792 | -done: | |
3793 | - mutex_unlock(&ccs_policy_lock); | |
3794 | -out: | |
3795 | - ccs_put_name(e.domainname); | |
3796 | - if (entry) | |
3797 | - security->ccs_domain_info = entry; | |
3798 | - return entry != NULL; | |
3799 | -} | |
3800 | - | |
3801 | -/** | |
3802 | - * ccs_parse_policy - Parse a policy line. | |
3803 | - * | |
3804 | - * @head: Poiter to "struct ccs_io_buffer". | |
3805 | - * @line: Line to parse. | |
3806 | - * | |
3807 | - * Returns 0 on success, negative value otherwise. | |
3808 | - * | |
3809 | - * Caller holds ccs_read_lock(). | |
3810 | - */ | |
3811 | -static int ccs_parse_policy(struct ccs_io_buffer *head, char *line) | |
3812 | -{ | |
3813 | - /* Set current line's content. */ | |
3814 | - head->w.data = line; | |
3815 | - head->w.is_deny = false; | |
3816 | - head->w.priority = 0; | |
3817 | - /* Delete request? */ | |
3818 | - head->w.is_delete = !strncmp(line, "delete ", 7); | |
3819 | - if (head->w.is_delete) | |
3820 | - memmove(line, line + 7, strlen(line + 7) + 1); | |
3821 | - /* Do the update. */ | |
3822 | - switch (head->type) { | |
3823 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
3824 | - case CCS_EXECUTE_HANDLER: | |
3825 | -#endif | |
3826 | - case CCS_PROCESS_STATUS: | |
3827 | - return ccs_write_pid(head); | |
3828 | - case CCS_QUERY: | |
3829 | - return ccs_write_answer(head); | |
3830 | - case CCS_POLICY: | |
3831 | - return ccs_write_policy(head); | |
3832 | - default: | |
3833 | - return -ENOSYS; | |
3834 | - } | |
3835 | -} | |
3836 | - | |
3837 | -/** | |
3838 | - * ccs_policy_io_init - Register hooks for policy I/O. | |
3839 | - * | |
3840 | - * Returns nothing. | |
3841 | - */ | |
3842 | -static void __init ccs_policy_io_init(void) | |
3843 | -{ | |
3844 | - ccsecurity_ops.check_profile = ccs_check_profile; | |
3845 | -} | |
3846 | - | |
3847 | -/** | |
3848 | - * ccs_load_builtin_policy - Load built-in policy. | |
3849 | - * | |
3850 | - * Returns nothing. | |
3851 | - */ | |
3852 | -static void __init ccs_load_builtin_policy(void) | |
3853 | -{ | |
3854 | - /* | |
3855 | - * This include file is manually created and contains built-in policy. | |
3856 | - * | |
3857 | - * static char [] __initdata ccs_builtin_policy = { ... }; | |
3858 | - */ | |
3859 | -#include "builtin-policy.h" | |
3860 | - const int idx = ccs_read_lock(); | |
3861 | - struct ccs_io_buffer head = { }; | |
3862 | - char *start = ccs_builtin_policy; | |
3863 | - head.type = CCS_POLICY; | |
3864 | - while (1) { | |
3865 | - char *end = strchr(start, '\n'); | |
3866 | - if (!end) | |
3867 | - break; | |
3868 | - *end = '\0'; | |
3869 | - ccs_normalize_line(start); | |
3870 | - head.write_buf = start; | |
3871 | - ccs_parse_policy(&head, start); | |
3872 | - start = end + 1; | |
3873 | - } | |
3874 | - ccs_read_unlock(idx); | |
3875 | -#ifdef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
3876 | - ccs_check_profile(); | |
3877 | -#endif | |
3878 | -} | |
3879 | - | |
3880 | -/** | |
3881 | - * ccs_read_self - read() for /proc/ccs/self_domain interface. | |
3882 | - * | |
3883 | - * @file: Pointer to "struct file". | |
3884 | - * @buf: Domainname which current thread belongs to. | |
3885 | - * @count: Size of @buf. | |
3886 | - * @ppos: Bytes read by now. | |
3887 | - * | |
3888 | - * Returns read size on success, negative value otherwise. | |
3889 | - */ | |
3890 | -static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | |
3891 | - loff_t *ppos) | |
3892 | -{ | |
3893 | - const char *domain = ccs_current_domain()->domainname->name; | |
3894 | - loff_t len = strlen(domain); | |
3895 | - loff_t pos = *ppos; | |
3896 | - if (pos >= len || !count) | |
3897 | - return 0; | |
3898 | - len -= pos; | |
3899 | - if (count < len) | |
3900 | - len = count; | |
3901 | - if (copy_to_user(buf, domain + pos, len)) | |
3902 | - return -EFAULT; | |
3903 | - *ppos += len; | |
3904 | - return len; | |
3905 | -} | |
3906 | - | |
3907 | -/** | |
3908 | - * ccs_read_subacl - Read sub ACL in ACL entry. | |
3909 | - * | |
3910 | - * @head: Pointer to "struct ccs_io_buffer". | |
3911 | - * @list: Pointer to "struct list_head". | |
3912 | - * | |
3913 | - * Returns true on success, false otherwise. | |
3914 | - * | |
3915 | - * Caller holds ccs_read_lock(). | |
3916 | - */ | |
3917 | -static bool ccs_read_subacl(struct ccs_io_buffer *head, | |
3918 | - const struct list_head *list) | |
3919 | -{ | |
3920 | - list_for_each_cookie(head->r.subacl, list) { | |
3921 | - struct ccs_acl_info *acl = | |
3922 | - list_entry(head->r.subacl, typeof(*acl), list); | |
3923 | - switch (head->r.step) { | |
3924 | - case 3: | |
3925 | - if (acl->is_deleted) | |
3926 | - continue; | |
3927 | - if (!ccs_flush(head)) | |
3928 | - return false; | |
3929 | - ccs_io_printf(head, " %u ", acl->priority); | |
3930 | - if (acl->is_deny) | |
3931 | - ccs_set_string(head, "deny"); | |
3932 | - else | |
3933 | - ccs_set_string(head, "allow"); | |
3934 | - head->r.cond_step = 0; | |
3935 | - head->r.step++; | |
3936 | - /* fall through */ | |
3937 | - case 4: | |
3938 | - if (!ccs_flush(head)) | |
3939 | - return false; | |
3940 | - if (acl->cond && | |
3941 | - !ccs_print_condition(head, acl->cond)) | |
3942 | - return false; | |
3943 | - ccs_set_lf(head); | |
3944 | - head->r.step--; | |
3945 | - } | |
3946 | - } | |
3947 | - head->r.subacl = NULL; | |
3948 | - return true; | |
3949 | -} | |
3950 | - | |
3951 | -/** | |
3952 | - * ccs_read_policy - Read policy. | |
3953 | - * | |
3954 | - * @head: Pointer to "struct ccs_io_buffer". | |
3955 | - * | |
3956 | - * Caller holds ccs_read_lock(). | |
3957 | - */ | |
3958 | -static void ccs_read_policy(struct ccs_io_buffer *head) | |
3959 | -{ | |
3960 | - if (head->r.eof) | |
3961 | - return; | |
3962 | - if (head->r.print_this_acl_only) | |
3963 | - goto skip; | |
3964 | - if (!head->r.version_done) { | |
3965 | - ccs_io_printf(head, "POLICY_VERSION=%u\n", ccs_policy_version); | |
3966 | - head->r.version_done = true; | |
3967 | - } | |
3968 | - if (!head->r.stat_done) { | |
3969 | - ccs_read_stat(head); | |
3970 | - head->r.stat_done = true; | |
3971 | - } | |
3972 | - if (!head->r.quota_done) { | |
3973 | - if (!ccs_read_quota(head)) | |
3974 | - return; | |
3975 | - head->r.quota_done = true; | |
3976 | - } | |
3977 | - if (!head->r.group_done) { | |
3978 | - if (!ccs_read_group(head)) | |
3979 | - return; | |
3980 | - head->r.group_done = true; | |
3981 | - ccs_set_lf(head); | |
3982 | - } | |
3983 | - while (head->r.acl_index < CCS_MAX_MAC_INDEX) { | |
3984 | - struct list_head * const list = | |
3985 | - &ccs_acl_list[head->r.acl_index]; | |
3986 | - list_for_each_cookie(head->r.acl, list) { | |
3987 | - struct ccs_acl_info *ptr; | |
3988 | -skip: | |
3989 | - ptr = list_entry(head->r.acl, typeof(*ptr), list); | |
3990 | - switch (head->r.step) { | |
3991 | - case 0: | |
3992 | - if (ptr->is_deleted && | |
3993 | - !head->r.print_this_acl_only) | |
3994 | - continue; | |
3995 | - head->r.step++; | |
3996 | - /* fall through */ | |
3997 | - case 1: | |
3998 | - if (!ccs_read_acl(head, ptr)) | |
3999 | - return; | |
4000 | - head->r.step++; | |
4001 | - /* fall through */ | |
4002 | - case 2: | |
4003 | - if (!ccs_flush(head)) | |
4004 | - return; | |
4005 | - ccs_io_printf(head, " audit %u\n", | |
4006 | - ptr->audit); | |
4007 | - head->r.step++; | |
4008 | - /* fall through */ | |
4009 | - case 3: | |
4010 | - case 4: | |
4011 | - if (!ccs_read_subacl(head, | |
4012 | - &ptr->acl_info_list)) | |
4013 | - return; | |
4014 | - head->r.step = 5; | |
4015 | - /* fall through */ | |
4016 | - case 5: | |
4017 | - if (!ccs_flush(head)) | |
4018 | - return; | |
4019 | - ccs_set_lf(head); | |
4020 | - head->r.step = 0; | |
4021 | - if (head->r.print_this_acl_only) | |
4022 | - goto done; | |
4023 | - } | |
4024 | - } | |
4025 | - head->r.acl = NULL; | |
4026 | - head->r.acl_index++; | |
4027 | - } | |
4028 | -done: | |
4029 | - head->r.eof = true; | |
4030 | -} | |
4031 | - | |
4032 | -/** | |
4033 | - * ccs_open - open() for /proc/ccs/ interface. | |
4034 | - * | |
4035 | - * @inode: Pointer to "struct inode". | |
4036 | - * @file: Pointer to "struct file". | |
4037 | - * | |
4038 | - * Returns 0 on success, negative value otherwise. | |
4039 | - */ | |
4040 | -static int ccs_open(struct inode *inode, struct file *file) | |
4041 | -{ | |
4042 | - const u8 type = (unsigned long) PDE(inode)->data; | |
4043 | - struct ccs_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); | |
4044 | - if (!head) | |
4045 | - return -ENOMEM; | |
4046 | - mutex_init(&head->io_sem); | |
4047 | - head->type = type; | |
4048 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4049 | - if (type == CCS_EXECUTE_HANDLER) { | |
4050 | - /* Allow execute_handler to read process's status. */ | |
4051 | - if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | |
4052 | - kfree(head); | |
4053 | - return -EPERM; | |
4054 | - } | |
4055 | - } | |
4056 | -#endif | |
4057 | - if ((file->f_mode & FMODE_READ) && type != CCS_AUDIT && | |
4058 | - type != CCS_QUERY) { | |
4059 | - /* Don't allocate read_buf for poll() access. */ | |
4060 | - head->readbuf_size = 4096; | |
4061 | - head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); | |
4062 | - if (!head->read_buf) { | |
4063 | - kfree(head); | |
4064 | - return -ENOMEM; | |
4065 | - } | |
4066 | - } | |
4067 | - if (file->f_mode & FMODE_WRITE) { | |
4068 | - head->writebuf_size = 4096; | |
4069 | - head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS); | |
4070 | - if (!head->write_buf) { | |
4071 | - kfree(head->read_buf); | |
4072 | - kfree(head); | |
4073 | - return -ENOMEM; | |
4074 | - } | |
4075 | - } | |
4076 | - /* | |
4077 | - * If the file is /proc/ccs/query, increment the observer counter. | |
4078 | - * The obserber counter is used by ccs_supervisor() to see if | |
4079 | - * there is some process monitoring /proc/ccs/query. | |
4080 | - */ | |
4081 | - if (type == CCS_QUERY) | |
4082 | - atomic_inc(&ccs_query_observers); | |
4083 | - file->private_data = head; | |
4084 | - ccs_notify_gc(head, true); | |
4085 | - return 0; | |
4086 | -} | |
4087 | - | |
4088 | -/** | |
4089 | - * ccs_release - close() for /proc/ccs/ interface. | |
4090 | - * | |
4091 | - * @inode: Pointer to "struct inode". | |
4092 | - * @file: Pointer to "struct file". | |
4093 | - * | |
4094 | - * Returns 0. | |
4095 | - */ | |
4096 | -static int ccs_release(struct inode *inode, struct file *file) | |
4097 | -{ | |
4098 | - struct ccs_io_buffer *head = file->private_data; | |
4099 | - /* | |
4100 | - * If the file is /proc/ccs/query, decrement the observer counter. | |
4101 | - */ | |
4102 | - if (head->type == CCS_QUERY && | |
4103 | - atomic_dec_and_test(&ccs_query_observers)) | |
4104 | - wake_up_all(&ccs_answer_wait); | |
4105 | - ccs_notify_gc(head, false); | |
4106 | - return 0; | |
4107 | -} | |
4108 | - | |
4109 | -/** | |
4110 | - * ccs_poll - poll() for /proc/ccs/ interface. | |
4111 | - * | |
4112 | - * @file: Pointer to "struct file". | |
4113 | - * @wait: Pointer to "poll_table". Maybe NULL. | |
4114 | - * | |
4115 | - * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, | |
4116 | - * POLLOUT | POLLWRNORM otherwise. | |
4117 | - */ | |
4118 | -static unsigned int ccs_poll(struct file *file, poll_table *wait) | |
4119 | -{ | |
4120 | - struct ccs_io_buffer *head = file->private_data; | |
4121 | - if (head->type == CCS_AUDIT) { | |
4122 | - if (!ccs_memory_used[CCS_MEMORY_AUDIT]) { | |
4123 | - poll_wait(file, &ccs_log_wait, wait); | |
4124 | - if (!ccs_memory_used[CCS_MEMORY_AUDIT]) | |
4125 | - return POLLOUT | POLLWRNORM; | |
4126 | - } | |
4127 | - } else if (head->type == CCS_QUERY) { | |
4128 | - if (list_empty(&ccs_query_list)) { | |
4129 | - poll_wait(file, &ccs_query_wait, wait); | |
4130 | - if (list_empty(&ccs_query_list)) | |
4131 | - return POLLOUT | POLLWRNORM; | |
4132 | - } | |
4133 | - } | |
4134 | - return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; | |
4135 | -} | |
4136 | - | |
4137 | -/** | |
4138 | - * ccs_read - read() for /proc/ccs/ interface. | |
4139 | - * | |
4140 | - * @file: Pointer to "struct file". | |
4141 | - * @buf: Pointer to buffer. | |
4142 | - * @count: Size of @buf. | |
4143 | - * @ppos: Unused. | |
4144 | - * | |
4145 | - * Returns bytes read on success, negative value otherwise. | |
4146 | - */ | |
4147 | -static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | |
4148 | - loff_t *ppos) | |
4149 | -{ | |
4150 | - struct ccs_io_buffer *head = file->private_data; | |
4151 | - int len; | |
4152 | - int idx; | |
4153 | - if (mutex_lock_interruptible(&head->io_sem)) | |
4154 | - return -EINTR; | |
4155 | - head->read_user_buf = buf; | |
4156 | - head->read_user_buf_avail = count; | |
4157 | - idx = ccs_read_lock(); | |
4158 | - if (ccs_flush(head)) { | |
4159 | - /* Call the policy handler. */ | |
4160 | - switch (head->type) { | |
4161 | - case CCS_AUDIT: | |
4162 | - ccs_read_log(head); | |
4163 | - break; | |
4164 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4165 | - case CCS_EXECUTE_HANDLER: | |
4166 | -#endif | |
4167 | - case CCS_PROCESS_STATUS: | |
4168 | - ccs_read_pid(head); | |
4169 | - break; | |
4170 | - case CCS_VERSION: | |
4171 | - ccs_read_version(head); | |
4172 | - break; | |
4173 | - case CCS_QUERY: | |
4174 | - ccs_read_query(head); | |
4175 | - break; | |
4176 | - case CCS_POLICY: | |
4177 | - ccs_read_policy(head); | |
4178 | - break; | |
4179 | - } | |
4180 | - ccs_flush(head); | |
4181 | - } | |
4182 | - ccs_read_unlock(idx); | |
4183 | - len = head->read_user_buf - buf; | |
4184 | - mutex_unlock(&head->io_sem); | |
4185 | - return len; | |
4186 | -} | |
4187 | - | |
4188 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
4189 | - | |
4190 | -/** | |
4191 | - * ccs_write_self - write() for /proc/ccs/self_domain interface. | |
4192 | - * | |
4193 | - * @file: Pointer to "struct file". | |
4194 | - * @buf: Domainname to transit to. | |
4195 | - * @count: Size of @buf. | |
4196 | - * @ppos: Unused. | |
4197 | - * | |
4198 | - * Returns @count on success, negative value otherwise. | |
4199 | - * | |
4200 | - * If domain transition was permitted but the domain transition failed, this | |
4201 | - * function returns error rather than terminating current thread with SIGKILL. | |
4202 | - */ | |
4203 | -static ssize_t ccs_write_self(struct file *file, const char __user *buf, | |
4204 | - size_t count, loff_t *ppos) | |
4205 | -{ | |
4206 | - char *data; | |
4207 | - int error; | |
4208 | - if (!count || count >= CCS_EXEC_TMPSIZE - 10) | |
4209 | - return -ENOMEM; | |
4210 | - data = kzalloc(count + 1, GFP_NOFS); | |
4211 | - if (!data) | |
4212 | - return -ENOMEM; | |
4213 | - if (copy_from_user(data, buf, count)) { | |
4214 | - error = -EFAULT; | |
4215 | - goto out; | |
4216 | - } | |
4217 | - ccs_normalize_line(data); | |
4218 | - if (ccs_correct_domain(data)) { | |
4219 | - const int idx = ccs_read_lock(); | |
4220 | - struct ccs_path_info name; | |
4221 | - struct ccs_request_info r = { }; | |
4222 | - name.name = data; | |
4223 | - ccs_fill_path_info(&name); | |
4224 | - /* Check "manual_domain_transition" permission. */ | |
4225 | - r.type = CCS_MAC_MANUAL_DOMAIN_TRANSITION; | |
4226 | - r.param.s[0] = &name; | |
4227 | - ccs_check_acl(&r, true); | |
4228 | - if (r.result != CCS_MATCHING_ALLOWED) | |
4229 | - error = -EPERM; | |
4230 | - else | |
4231 | - error = ccs_transit_domain(data) ? 0 : -ENOENT; | |
4232 | - ccs_read_unlock(idx); | |
4233 | - } else | |
4234 | - error = -EINVAL; | |
4235 | -out: | |
4236 | - kfree(data); | |
4237 | - return error ? error : count; | |
4238 | -} | |
4239 | - | |
4240 | -#endif | |
4241 | - | |
4242 | -/** | |
4243 | - * ccs_write - write() for /proc/ccs/ interface. | |
4244 | - * | |
4245 | - * @file: Pointer to "struct file". | |
4246 | - * @buf: Pointer to buffer. | |
4247 | - * @count: Size of @buf. | |
4248 | - * @ppos: Unused. | |
4249 | - * | |
4250 | - * Returns @count on success, negative value otherwise. | |
4251 | - */ | |
4252 | -static ssize_t ccs_write(struct file *file, const char __user *buf, | |
4253 | - size_t count, loff_t *ppos) | |
4254 | -{ | |
4255 | - struct ccs_io_buffer *head = file->private_data; | |
4256 | - int error = count; | |
4257 | - char *cp0 = head->write_buf; | |
4258 | - int idx; | |
4259 | - if (mutex_lock_interruptible(&head->io_sem)) | |
4260 | - return -EINTR; | |
4261 | - head->read_user_buf_avail = 0; | |
4262 | - idx = ccs_read_lock(); | |
4263 | - /* Read a line and dispatch it to the policy handler. */ | |
4264 | - while (count) { | |
4265 | - char c; | |
4266 | - if (head->w.avail >= head->writebuf_size - 1) { | |
4267 | - const int len = head->writebuf_size * 2; | |
4268 | - char *cp = kzalloc(len, GFP_NOFS); | |
4269 | - if (!cp) { | |
4270 | - error = -ENOMEM; | |
4271 | - break; | |
4272 | - } | |
4273 | - memmove(cp, cp0, head->w.avail); | |
4274 | - kfree(cp0); | |
4275 | - head->write_buf = cp; | |
4276 | - cp0 = cp; | |
4277 | - head->writebuf_size = len; | |
4278 | - } | |
4279 | - if (get_user(c, buf)) { | |
4280 | - error = -EFAULT; | |
4281 | - break; | |
4282 | - } | |
4283 | - buf++; | |
4284 | - count--; | |
4285 | - cp0[head->w.avail++] = c; | |
4286 | - if (c != '\n') | |
4287 | - continue; | |
4288 | - cp0[head->w.avail - 1] = '\0'; | |
4289 | - head->w.avail = 0; | |
4290 | - ccs_normalize_line(cp0); | |
4291 | - /* Don't allow updating policies by non manager programs. */ | |
4292 | - if (head->type != CCS_PROCESS_STATUS && !ccs_manager()) { | |
4293 | - error = -EPERM; | |
4294 | - goto out; | |
4295 | - } | |
4296 | - switch (ccs_parse_policy(head, cp0)) { | |
4297 | - case -EPERM: | |
4298 | - error = -EPERM; | |
4299 | - goto out; | |
4300 | - case 0: | |
4301 | - /* Update statistics. */ | |
4302 | - if (head->type == CCS_POLICY) | |
4303 | - ccs_update_stat(CCS_STAT_POLICY_UPDATES); | |
4304 | - break; | |
4305 | - } | |
4306 | - } | |
4307 | -out: | |
4308 | - ccs_read_unlock(idx); | |
4309 | - mutex_unlock(&head->io_sem); | |
4310 | - return error; | |
4311 | -} | |
4312 | - | |
4313 | -/** | |
4314 | - * ccs_create_entry - Create interface files under /proc/ccs/ directory. | |
4315 | - * | |
4316 | - * @name: The name of the interface file. | |
4317 | - * @mode: The permission of the interface file. | |
4318 | - * @parent: The parent directory. | |
4319 | - * @key: Type of interface. | |
4320 | - * | |
4321 | - * Returns nothing. | |
4322 | - */ | |
4323 | -static void __init ccs_create_entry(const char *name, const umode_t mode, | |
4324 | - struct proc_dir_entry *parent, | |
4325 | - const u8 key) | |
4326 | -{ | |
4327 | - struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); | |
4328 | - if (entry) { | |
4329 | - entry->proc_fops = &ccs_operations; | |
4330 | - entry->data = ((u8 *) NULL) + key; | |
4331 | - } | |
4332 | -} | |
4333 | - | |
4334 | -/** | |
4335 | - * ccs_proc_init - Initialize /proc/ccs/ interface. | |
4336 | - * | |
4337 | - * Returns nothing. | |
4338 | - */ | |
4339 | -static void __init ccs_proc_init(void) | |
4340 | -{ | |
4341 | - struct proc_dir_entry *ccs_dir = proc_mkdir("ccs", NULL); | |
4342 | - ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); | |
4343 | - ccs_create_entry("audit", 0400, ccs_dir, CCS_AUDIT); | |
4344 | - ccs_create_entry(".process_status", 0600, ccs_dir, | |
4345 | - CCS_PROCESS_STATUS); | |
4346 | - ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); | |
4347 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4348 | - ccs_create_entry(".execute_handler", 0666, ccs_dir, | |
4349 | - CCS_EXECUTE_HANDLER); | |
4350 | -#endif | |
4351 | - ccs_create_entry("policy", 0600, ccs_dir, CCS_POLICY); | |
4352 | - { | |
4353 | - struct proc_dir_entry *e = create_proc_entry("self_domain", | |
4354 | - 0666, ccs_dir); | |
4355 | - if (e) | |
4356 | - e->proc_fops = &ccs_self_operations; | |
4357 | - } | |
4358 | -} | |
4359 | - | |
4360 | -/** | |
4361 | - * ccs_init_module - Initialize this module. | |
4362 | - * | |
4363 | - * Returns 0 on success, negative value otherwise. | |
4364 | - */ | |
4365 | -static int __init ccs_init_module(void) | |
4366 | -{ | |
4367 | - u16 idx; | |
4368 | - if (ccsecurity_ops.disabled) | |
4369 | - return -EINVAL; | |
4370 | -#ifdef DEBUG_CONDITION | |
4371 | - for (idx = 0; idx < CCS_MAX_MAC_INDEX; idx++) { | |
4372 | - if (ccs_mac_keywords[idx]) | |
4373 | - continue; | |
4374 | - printk(KERN_INFO "ccs_mac_keywords[%u]==NULL\n", idx); | |
4375 | - return -EINVAL; | |
4376 | - } | |
4377 | -#endif | |
4378 | - if (init_srcu_struct(&ccs_ss)) | |
4379 | - panic("Out of memory."); | |
4380 | - for (idx = 0; idx < CCS_MAX_MAC_INDEX; idx++) | |
4381 | - INIT_LIST_HEAD(&ccs_acl_list[idx]); | |
4382 | - for (idx = 0; idx < CCS_MAX_GROUP; idx++) | |
4383 | - INIT_LIST_HEAD(&ccs_group_list[idx]); | |
4384 | - for (idx = 0; idx < CCS_MAX_HASH; idx++) | |
4385 | - INIT_LIST_HEAD(&ccs_name_list[idx]); | |
4386 | -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
4387 | - ccs_mm_init(); | |
4388 | -#endif | |
4389 | - ccs_null_name.name = "NULL"; | |
4390 | - ccs_fill_path_info(&ccs_null_name); | |
4391 | - ccs_kernel_domain.domainname = ccs_get_name("<kernel>"); | |
4392 | - list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list); | |
4393 | - ccs_policy_io_init(); | |
4394 | - ccs_permission_init(); | |
4395 | - ccs_proc_init(); | |
4396 | - ccs_load_builtin_policy(); | |
4397 | - return 0; | |
4398 | -} | |
4399 | - | |
4400 | -MODULE_LICENSE("GPL"); | |
4401 | -module_init(ccs_init_module); |
@@ -1,3752 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/permission.c | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#include "internal.h" | |
10 | - | |
11 | -/***** SECTION1: Constants definition *****/ | |
12 | - | |
13 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
14 | - | |
15 | -/* | |
16 | - * may_open() receives open flags modified by open_to_namei_flags() until | |
17 | - * 2.6.32. We stop here in case some distributions backported ACC_MODE changes, | |
18 | - * for we can't determine whether may_open() receives open flags modified by | |
19 | - * open_to_namei_flags() or not. | |
20 | - */ | |
21 | -#ifdef ACC_MODE | |
22 | -#error ACC_MODE already defined. | |
23 | -#endif | |
24 | -#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) | |
25 | - | |
26 | -#if defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | |
27 | -/* RHEL6 passes unmodified flags since 2.6.32-71.14.1.el6 . */ | |
28 | -#undef ACC_MODE | |
29 | -#define ACC_MODE(x) ("\004\002\006"[(x)&O_ACCMODE]) | |
30 | -#endif | |
31 | - | |
32 | -#endif | |
33 | - | |
34 | -/* String table for special mount operations. */ | |
35 | -static const char * const ccs_mounts[CCS_MAX_SPECIAL_MOUNT] = { | |
36 | - [CCS_MOUNT_BIND] = "--bind", | |
37 | - [CCS_MOUNT_MOVE] = "--move", | |
38 | - [CCS_MOUNT_REMOUNT] = "--remount", | |
39 | - [CCS_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", | |
40 | - [CCS_MOUNT_MAKE_PRIVATE] = "--make-private", | |
41 | - [CCS_MOUNT_MAKE_SLAVE] = "--make-slave", | |
42 | - [CCS_MOUNT_MAKE_SHARED] = "--make-shared", | |
43 | -}; | |
44 | - | |
45 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
46 | - | |
47 | -/* | |
48 | - * Mapping table from "enum ccs_capability_acl_index" to "enum ccs_mac_index". | |
49 | - */ | |
50 | -static const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX] = { | |
51 | - [CCS_USE_ROUTE_SOCKET] = CCS_MAC_USE_NETLINK_SOCKET, | |
52 | - [CCS_USE_PACKET_SOCKET] = CCS_MAC_USE_PACKET_SOCKET, | |
53 | - [CCS_SYS_REBOOT] = CCS_MAC_USE_REBOOT, | |
54 | - [CCS_SYS_VHANGUP] = CCS_MAC_USE_VHANGUP, | |
55 | - [CCS_SYS_SETTIME] = CCS_MAC_SET_TIME, | |
56 | - [CCS_SYS_NICE] = CCS_MAC_SET_PRIORITY, | |
57 | - [CCS_SYS_SETHOSTNAME] = CCS_MAC_SET_HOSTNAME, | |
58 | - [CCS_USE_KERNEL_MODULE] = CCS_MAC_USE_KERNEL_MODULE, | |
59 | - [CCS_SYS_KEXEC_LOAD] = CCS_MAC_USE_NEW_KERNEL, | |
60 | -}; | |
61 | - | |
62 | -#endif | |
63 | - | |
64 | -/* Type of condition argument. */ | |
65 | -enum ccs_arg_type { | |
66 | - CCS_ARG_TYPE_NONE, | |
67 | - CCS_ARG_TYPE_NUMBER, | |
68 | - CCS_ARG_TYPE_NAME, | |
69 | - CCS_ARG_TYPE_GROUP, | |
70 | - CCS_ARG_TYPE_BITOP, | |
71 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
72 | - CCS_ARG_TYPE_IPV4ADDR, | |
73 | - CCS_ARG_TYPE_IPV6ADDR, | |
74 | -#endif | |
75 | -} __packed; | |
76 | - | |
77 | -/***** SECTION2: Structure definition *****/ | |
78 | - | |
79 | -/* Structure for holding inet domain socket's address. */ | |
80 | -struct ccs_inet_addr_info { | |
81 | - u16 port; /* In network byte order. */ | |
82 | - const u8 *address; /* In network byte order. */ | |
83 | - bool is_ipv6; | |
84 | -}; | |
85 | - | |
86 | -/* Structure for holding unix domain socket's address. */ | |
87 | -struct ccs_unix_addr_info { | |
88 | - u8 *addr; /* This may not be '\0' terminated string. */ | |
89 | - unsigned int addr_len; | |
90 | -}; | |
91 | - | |
92 | -/* Structure for holding socket address. */ | |
93 | -struct ccs_addr_info { | |
94 | - u8 operation; | |
95 | - struct ccs_inet_addr_info inet; | |
96 | - struct ccs_unix_addr_info unix0; | |
97 | -}; | |
98 | - | |
99 | -/* Structure for holding single condition component. */ | |
100 | -struct ccs_cond_arg { | |
101 | - enum ccs_arg_type type; | |
102 | - unsigned long value[2]; | |
103 | - const struct ccs_path_info *name; | |
104 | - const struct ccs_group *group; | |
105 | - struct in6_addr ip[2]; | |
106 | -}; | |
107 | - | |
108 | -/***** SECTION3: Prototype definition section *****/ | |
109 | - | |
110 | -static bool ccs_alphabet_char(const char c); | |
111 | -static bool ccs_byte_range(const char *str); | |
112 | -static bool ccs_check_entry(struct ccs_request_info *r, | |
113 | - const struct ccs_acl_info *ptr); | |
114 | -static bool ccs_condition(struct ccs_request_info *r, | |
115 | - const struct ccs_condition *cond); | |
116 | -static bool ccs_decimal(const char c); | |
117 | -static bool ccs_file_matches_pattern(const char *filename, | |
118 | - const char *filename_end, | |
119 | - const char *pattern, | |
120 | - const char *pattern_end); | |
121 | -static bool ccs_file_matches_pattern2(const char *filename, | |
122 | - const char *filename_end, | |
123 | - const char *pattern, | |
124 | - const char *pattern_end); | |
125 | -static bool ccs_hexadecimal(const char c); | |
126 | -static bool ccs_number_matches_group(const unsigned long min, | |
127 | - const unsigned long max, | |
128 | - const struct ccs_group *group); | |
129 | -static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | |
130 | - const struct ccs_path_info *pattern); | |
131 | -static bool ccs_path_matches_pattern2(const char *f, const char *p); | |
132 | -static bool ccs_path_matches_group(const struct ccs_path_info *pathname, | |
133 | - const struct ccs_group *group); | |
134 | -static int __ccs_chmod_permission(struct dentry *dentry, | |
135 | - struct vfsmount *vfsmnt, mode_t mode); | |
136 | -static int __ccs_chown_permission(struct dentry *dentry, | |
137 | - struct vfsmount *vfsmnt, uid_t user, | |
138 | - gid_t group); | |
139 | -static int __ccs_chroot_permission(struct path *path); | |
140 | -static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
141 | - unsigned long arg); | |
142 | -static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
143 | - unsigned long arg); | |
144 | -static int __ccs_link_permission(struct dentry *old_dentry, | |
145 | - struct dentry *new_dentry, | |
146 | - struct vfsmount *mnt); | |
147 | -static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | |
148 | - unsigned int mode); | |
149 | -static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | |
150 | - const unsigned int mode, unsigned int dev); | |
151 | -static int __ccs_mount_permission(char *dev_name, struct path *path, | |
152 | - const char *type, unsigned long flags, | |
153 | - void *data_page); | |
154 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
155 | -static int __ccs_open_exec_permission(struct dentry *dentry, | |
156 | - struct vfsmount *mnt); | |
157 | -#endif | |
158 | -static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | |
159 | - const int flag); | |
160 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
161 | -static int ccs_sysctl_permission(enum ccs_mac_index type, | |
162 | - const struct ccs_path_info *filename); | |
163 | -static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | |
164 | - void __user *newval, struct ctl_table *table); | |
165 | -#endif | |
166 | -static int __ccs_pivot_root_permission(struct path *old_path, | |
167 | - struct path *new_path); | |
168 | -static int __ccs_rename_permission(struct dentry *old_dentry, | |
169 | - struct dentry *new_dentry, | |
170 | - struct vfsmount *mnt); | |
171 | -static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt); | |
172 | -static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
173 | - struct pt_regs *regs); | |
174 | -static int __ccs_symlink_permission(struct dentry *dentry, | |
175 | - struct vfsmount *mnt, const char *from); | |
176 | -static int __ccs_truncate_permission(struct dentry *dentry, | |
177 | - struct vfsmount *mnt); | |
178 | -static int __ccs_umount_permission(struct vfsmount *mnt, int flags); | |
179 | -static int __ccs_unlink_permission(struct dentry *dentry, | |
180 | - struct vfsmount *mnt); | |
181 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
182 | -static int __ccs_uselib_permission(struct dentry *dentry, | |
183 | - struct vfsmount *mnt); | |
184 | -#endif | |
185 | -static int ccs_execute_path(struct linux_binprm *bprm, struct path *path); | |
186 | -static int ccs_execute(struct ccs_request_info *r); | |
187 | -static int ccs_kern_path(const char *pathname, int flags, struct path *path); | |
188 | -static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | |
189 | - struct vfsmount *mnt, const unsigned int mode, | |
190 | - unsigned int dev); | |
191 | -static int ccs_mount_acl(const char *dev_name, struct path *dir, | |
192 | - const char *type, unsigned long flags, | |
193 | - const char *data); | |
194 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
195 | -static int ccs_new_open_permission(struct file *filp); | |
196 | -#endif | |
197 | -static int ccs_path2_perm(const enum ccs_mac_index operation, | |
198 | - struct dentry *dentry1, struct vfsmount *mnt1, | |
199 | - struct dentry *dentry2, struct vfsmount *mnt2); | |
200 | -static int ccs_path_number_perm(const enum ccs_mac_index type, | |
201 | - struct dentry *dentry, struct vfsmount *vfsmnt, | |
202 | - unsigned long number); | |
203 | -static int ccs_path_perm(const enum ccs_mac_index operation, | |
204 | - struct dentry *dentry, struct vfsmount *mnt); | |
205 | -static int ccs_start_execve(struct linux_binprm *bprm, | |
206 | - struct ccs_request_info **rp); | |
207 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
208 | -static void __ccs_clear_open_mode(void); | |
209 | -static void __ccs_save_open_mode(int mode); | |
210 | -#endif | |
211 | -static void ccs_check_auto_domain_transition(void); | |
212 | -static void ccs_clear_request_info(struct ccs_request_info *r); | |
213 | -static void ccs_finish_execve(int retval, struct ccs_request_info *r); | |
214 | - | |
215 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
216 | -static int ccs_env_perm(struct ccs_request_info *r, const char *name, | |
217 | - const char *value); | |
218 | -static int ccs_environ(struct ccs_request_info *r); | |
219 | -#endif | |
220 | - | |
221 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
222 | -static bool __ccs_capable(const u8 operation); | |
223 | -static bool ccs_kernel_service(void); | |
224 | -static int __ccs_socket_create_permission(int family, int type, int protocol); | |
225 | -#endif | |
226 | - | |
227 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
228 | -static bool ccs_ip_matches_group(const bool is_ipv6, const u8 *address, | |
229 | - const struct ccs_group *group); | |
230 | -static bool ccs_kernel_service(void); | |
231 | -static int __ccs_socket_bind_permission(struct socket *sock, | |
232 | - struct sockaddr *addr, int addr_len); | |
233 | -static int __ccs_socket_connect_permission(struct socket *sock, | |
234 | - struct sockaddr *addr, | |
235 | - int addr_len); | |
236 | -static int __ccs_socket_listen_permission(struct socket *sock); | |
237 | -static int __ccs_socket_post_accept_permission(struct socket *sock, | |
238 | - struct socket *newsock); | |
239 | -static int __ccs_socket_sendmsg_permission(struct socket *sock, | |
240 | - struct msghdr *msg, int size); | |
241 | -static int ccs_check_inet_address(const struct sockaddr *addr, | |
242 | - const unsigned int addr_len, const u16 port, | |
243 | - struct ccs_addr_info *address); | |
244 | -static int ccs_check_unix_address(struct sockaddr *addr, | |
245 | - const unsigned int addr_len, | |
246 | - struct ccs_addr_info *address); | |
247 | -static int ccs_inet_entry(const struct ccs_addr_info *address); | |
248 | -static int ccs_unix_entry(const struct ccs_addr_info *address); | |
249 | -static u8 ccs_sock_family(struct sock *sk); | |
250 | -#endif | |
251 | - | |
252 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
253 | -static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | |
254 | - struct sk_buff *skb, | |
255 | - int flags); | |
256 | -#endif | |
257 | - | |
258 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
259 | -static int __ccs_ptrace_permission(long request, long pid); | |
260 | -#endif | |
261 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
262 | -static int __ccs_signal_permission(const int sig); | |
263 | -static int ccs_signal_permission0(const int pid, const int sig); | |
264 | -static int ccs_signal_permission1(pid_t tgid, pid_t pid, int sig); | |
265 | -#endif | |
266 | - | |
267 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
268 | -static int __ccs_getattr_permission(struct vfsmount *mnt, | |
269 | - struct dentry *dentry); | |
270 | -#endif | |
271 | - | |
272 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
273 | -static int ccs_try_alt_exec(struct ccs_request_info *r); | |
274 | -static void ccs_unescape(unsigned char *dest); | |
275 | -#endif | |
276 | - | |
277 | -/***** SECTION4: Standalone functions section *****/ | |
278 | - | |
279 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
280 | - | |
281 | -/** | |
282 | - * ccs_copy_argv - Wrapper for copy_strings_kernel(). | |
283 | - * | |
284 | - * @arg: String to copy. | |
285 | - * @bprm: Pointer to "struct linux_binprm". | |
286 | - * | |
287 | - * Returns return value of copy_strings_kernel(). | |
288 | - */ | |
289 | -static inline int ccs_copy_argv(const char *arg, struct linux_binprm *bprm) | |
290 | -{ | |
291 | - const int ret = copy_strings_kernel(1, &arg, bprm); | |
292 | - if (ret >= 0) | |
293 | - bprm->argc++; | |
294 | - return ret; | |
295 | -} | |
296 | - | |
297 | -#else | |
298 | - | |
299 | -/** | |
300 | - * ccs_copy_argv - Wrapper for copy_strings_kernel(). | |
301 | - * | |
302 | - * @arg: String to copy. | |
303 | - * @bprm: Pointer to "struct linux_binprm". | |
304 | - * | |
305 | - * Returns return value of copy_strings_kernel(). | |
306 | - */ | |
307 | -static inline int ccs_copy_argv(char *arg, struct linux_binprm *bprm) | |
308 | -{ | |
309 | - const int ret = copy_strings_kernel(1, &arg, bprm); | |
310 | - if (ret >= 0) | |
311 | - bprm->argc++; | |
312 | - return ret; | |
313 | -} | |
314 | - | |
315 | -#endif | |
316 | - | |
317 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | |
318 | - | |
319 | -/** | |
320 | - * get_fs_root - Get reference on root directory. | |
321 | - * | |
322 | - * @fs: Pointer to "struct fs_struct". | |
323 | - * @root: Pointer to "struct path". | |
324 | - * | |
325 | - * Returns nothing. | |
326 | - * | |
327 | - * This is for compatibility with older kernels. | |
328 | - */ | |
329 | -static inline void get_fs_root(struct fs_struct *fs, struct path *root) | |
330 | -{ | |
331 | - read_lock(&fs->lock); | |
332 | - *root = fs->root; | |
333 | - path_get(root); | |
334 | - read_unlock(&fs->lock); | |
335 | -} | |
336 | - | |
337 | -#endif | |
338 | - | |
339 | -/** | |
340 | - * ccs_put_filesystem - Wrapper for put_filesystem(). | |
341 | - * | |
342 | - * @fstype: Pointer to "struct file_system_type". | |
343 | - * | |
344 | - * Returns nothing. | |
345 | - * | |
346 | - * Since put_filesystem() is not exported, I embed put_filesystem() here. | |
347 | - */ | |
348 | -static inline void ccs_put_filesystem(struct file_system_type *fstype) | |
349 | -{ | |
350 | - module_put(fstype->owner); | |
351 | -} | |
352 | - | |
353 | -/***** SECTION5: Variables definition section *****/ | |
354 | - | |
355 | -/* The initial domain. */ | |
356 | -struct ccs_domain_info ccs_kernel_domain; | |
357 | - | |
358 | -/* The list for "struct ccs_domain_info". */ | |
359 | -LIST_HEAD(ccs_domain_list); | |
360 | - | |
361 | -/* The list for ACL policy. */ | |
362 | -struct list_head ccs_acl_list[CCS_MAX_MAC_INDEX]; | |
363 | - | |
364 | -/* NULL value. */ | |
365 | -struct ccs_path_info ccs_null_name; | |
366 | - | |
367 | -/***** SECTION6: Dependent functions section *****/ | |
368 | - | |
369 | -/** | |
370 | - * ccs_path_matches_group - Check whether the given pathname matches members of the given pathname group. | |
371 | - * | |
372 | - * @pathname: The name of pathname. | |
373 | - * @group: Pointer to "struct ccs_string_group". | |
374 | - * | |
375 | - * Returns true if @pathname matches pathnames in @group, false otherwise. | |
376 | - * | |
377 | - * Caller holds ccs_read_lock(). | |
378 | - */ | |
379 | -static bool ccs_path_matches_group(const struct ccs_path_info *pathname, | |
380 | - const struct ccs_group *group) | |
381 | -{ | |
382 | - struct ccs_string_group *member; | |
383 | - list_for_each_entry_srcu(member, &group->member_list, head.list, | |
384 | - &ccs_ss) { | |
385 | - if (member->head.is_deleted) | |
386 | - continue; | |
387 | - if (!ccs_path_matches_pattern(pathname, member->member_name)) | |
388 | - continue; | |
389 | - return true; | |
390 | - } | |
391 | - return false; | |
392 | -} | |
393 | - | |
394 | -/** | |
395 | - * ccs_number_matches_group - Check whether the given number matches members of the given number group. | |
396 | - * | |
397 | - * @min: Min number. | |
398 | - * @max: Max number. | |
399 | - * @group: Pointer to "struct ccs_number_group". | |
400 | - * | |
401 | - * Returns true if @min and @max partially overlaps @group, false otherwise. | |
402 | - * | |
403 | - * Caller holds ccs_read_lock(). | |
404 | - */ | |
405 | -static bool ccs_number_matches_group(const unsigned long min, | |
406 | - const unsigned long max, | |
407 | - const struct ccs_group *group) | |
408 | -{ | |
409 | - struct ccs_number_group *member; | |
410 | - bool matched = false; | |
411 | - list_for_each_entry_srcu(member, &group->member_list, head.list, | |
412 | - &ccs_ss) { | |
413 | - if (member->head.is_deleted) | |
414 | - continue; | |
415 | - if (min > member->value[1] || max < member->value[0]) | |
416 | - continue; | |
417 | - matched = true; | |
418 | - break; | |
419 | - } | |
420 | - return matched; | |
421 | -} | |
422 | - | |
423 | -/** | |
424 | - * ccs_check_entry - Do permission check. | |
425 | - * | |
426 | - * @r: Pointer to "struct ccs_request_info". | |
427 | - * @ptr: Pointer to "struct ccs_acl_info". | |
428 | - * | |
429 | - * Returns true on match, false otherwise. | |
430 | - * | |
431 | - * Caller holds ccs_read_lock(). | |
432 | - */ | |
433 | -static bool ccs_check_entry(struct ccs_request_info *r, | |
434 | - const struct ccs_acl_info *ptr) | |
435 | -{ | |
436 | - return !ptr->is_deleted && ccs_condition(r, ptr->cond); | |
437 | -} | |
438 | - | |
439 | -/** | |
440 | - * ccs_check_acl_list - Do permission check. | |
441 | - * | |
442 | - * @r: Pointer to "struct ccs_request_info". | |
443 | - * | |
444 | - * Returns 0 on success, negative value otherwise. | |
445 | - * | |
446 | - * Caller holds ccs_read_lock(). | |
447 | - */ | |
448 | -static int ccs_check_acl_list(struct ccs_request_info *r) | |
449 | -{ | |
450 | - struct ccs_acl_info *ptr; | |
451 | - int error = 0; | |
452 | - struct list_head * const list = &ccs_acl_list[r->type]; | |
453 | - r->matched_acl = NULL; | |
454 | - list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | |
455 | - struct ccs_acl_info *ptr2; | |
456 | -retry: | |
457 | - if (!ccs_check_entry(r, ptr)) { | |
458 | - if (unlikely(r->failed_by_oom)) | |
459 | - goto oom; | |
460 | - continue; | |
461 | - } | |
462 | - r->matched_acl = ptr; | |
463 | - r->audit = ptr->audit; | |
464 | - r->result = CCS_MATCHING_UNMATCHED; | |
465 | - list_for_each_entry_srcu(ptr2, &ptr->acl_info_list, list, | |
466 | - &ccs_ss) { | |
467 | - r->transition_candidate = NULL; | |
468 | - r->handler_path_candidate = NULL; | |
469 | - if (!ccs_check_entry(r, ptr2)) { | |
470 | - if (unlikely(r->failed_by_oom)) | |
471 | - goto oom; | |
472 | - continue; | |
473 | - } | |
474 | - if (ptr2->is_deny) { | |
475 | - r->result = CCS_MATCHING_DENIED; | |
476 | - break; | |
477 | - } | |
478 | - r->result = CCS_MATCHING_ALLOWED; | |
479 | - /* Set the first matching domain transition entry. */ | |
480 | - if (r->transition_candidate && !r->transition) | |
481 | - r->transition = r->transition_candidate; | |
482 | - /* Set the first matching execute handler entry. */ | |
483 | - if (r->handler_path_candidate && !r->handler_path) | |
484 | - r->handler_path = r->handler_path_candidate; | |
485 | - break; | |
486 | - } | |
487 | - error = ccs_audit_log(r); | |
488 | - /* Ignore out of memory during audit. */ | |
489 | - r->failed_by_oom = false; | |
490 | - if (!error) | |
491 | - continue; | |
492 | - if (error == CCS_RETRY_REQUEST) | |
493 | - goto retry; | |
494 | - break; | |
495 | - } | |
496 | - return error; | |
497 | -oom: | |
498 | - /* | |
499 | - * If conditions could not be checked due to out of memory, | |
500 | - * reject the request with -ENOMEM, for we don't know whether | |
501 | - * there was a possibility of matching "deny" lines or not. | |
502 | - */ | |
503 | - { | |
504 | - static struct timeval ccs_last_tv; | |
505 | - struct timeval tv; | |
506 | - do_gettimeofday(&tv); | |
507 | - if (tv.tv_sec != ccs_last_tv.tv_sec) { | |
508 | - ccs_last_tv = tv; | |
509 | - printk(KERN_INFO "CaitSith: Rejecting access " | |
510 | - "request due to out of memory.\n"); | |
511 | - } | |
512 | - } | |
513 | - return -ENOMEM; | |
514 | -} | |
515 | - | |
516 | -/** | |
517 | - * ccs_check_acl - Do permission check. | |
518 | - * | |
519 | - * @r: Pointer to "struct ccs_request_info". | |
520 | - * @clear: True to cleanup @r before return, false otherwise. | |
521 | - * | |
522 | - * Returns 0 on success, negative value otherwise. | |
523 | - */ | |
524 | -int ccs_check_acl(struct ccs_request_info *r, const bool clear) | |
525 | -{ | |
526 | - int error; | |
527 | - const int idx = ccs_read_lock(); | |
528 | - error = ccs_check_acl_list(r); | |
529 | - ccs_read_unlock(idx); | |
530 | - if (clear) | |
531 | - ccs_clear_request_info(r); | |
532 | - return error; | |
533 | -} | |
534 | - | |
535 | -/** | |
536 | - * ccs_execute - Check permission for "execute". | |
537 | - * | |
538 | - * @r: Pointer to "struct ccs_request_info". | |
539 | - * | |
540 | - * Returns 0 on success, negative value otherwise. | |
541 | - * | |
542 | - * Caller holds ccs_read_lock(). | |
543 | - */ | |
544 | -static int ccs_execute(struct ccs_request_info *r) | |
545 | -{ | |
546 | - int retval; | |
547 | - | |
548 | - /* Get symlink's dentry/vfsmount. */ | |
549 | - retval = ccs_execute_path(r->bprm, &r->obj.path[1]); | |
550 | - if (retval < 0) | |
551 | - return retval; | |
552 | - ccs_populate_patharg(r, false); | |
553 | - if (!r->param.s[1]) | |
554 | - return -ENOMEM; | |
555 | - | |
556 | - /* Check execute permission. */ | |
557 | - r->type = CCS_MAC_EXECUTE; | |
558 | - retval = ccs_check_acl(r, false); | |
559 | - if (retval < 0) | |
560 | - return retval; | |
561 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
562 | - /* | |
563 | - * Switch to execute handler if matched. To avoid infinite execute | |
564 | - * handler loop, don't use execute handler if the current process is | |
565 | - * marked as execute handler. | |
566 | - */ | |
567 | - if (r->handler_path && r->handler_path != &ccs_null_name && | |
568 | - !(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | |
569 | - retval = ccs_try_alt_exec(r); | |
570 | - if (retval < 0) | |
571 | - return retval; | |
572 | - } | |
573 | -#endif | |
574 | - /* | |
575 | - * Tell GC that I started execve(). | |
576 | - * Also, tell open_exec() to check read permission. | |
577 | - */ | |
578 | - ccs_current_security()->ccs_flags |= CCS_TASK_IS_IN_EXECVE; | |
579 | - if (!r->transition || r->transition == &ccs_null_name) | |
580 | - /* Keep current domain. */ | |
581 | - return 0; | |
582 | - /* | |
583 | - * Make ccs_current_security()->ccs_flags visible to GC before changing | |
584 | - * ccs_current_security()->ccs_domain_info. | |
585 | - */ | |
586 | - smp_wmb(); | |
587 | - /* | |
588 | - * Transit to the specified domain. | |
589 | - * It will be reverted if execve() failed. | |
590 | - */ | |
591 | - if (ccs_transit_domain(r->transition->name)) | |
592 | - return 0; | |
593 | - printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", | |
594 | - r->transition->name); | |
595 | - return -ENOMEM; | |
596 | -} | |
597 | - | |
598 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
599 | - | |
600 | -/** | |
601 | - * ccs_unescape - Unescape escaped string. | |
602 | - * | |
603 | - * @dest: String to unescape. | |
604 | - * | |
605 | - * Returns nothing. | |
606 | - */ | |
607 | -static void ccs_unescape(unsigned char *dest) | |
608 | -{ | |
609 | - unsigned char *src = dest; | |
610 | - unsigned char c; | |
611 | - unsigned char d; | |
612 | - unsigned char e; | |
613 | - while (1) { | |
614 | - c = *src++; | |
615 | - if (!c) | |
616 | - break; | |
617 | - if (c != '\\') { | |
618 | - *dest++ = c; | |
619 | - continue; | |
620 | - } | |
621 | - c = *src++; | |
622 | - if (c < '0' || c > '3') | |
623 | - break; | |
624 | - d = *src++; | |
625 | - if (d < '0' || d > '7') | |
626 | - break; | |
627 | - e = *src++; | |
628 | - if (e < '0' || e > '7') | |
629 | - break; | |
630 | - *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
631 | - } | |
632 | - *dest = '\0'; | |
633 | -} | |
634 | - | |
635 | -/** | |
636 | - * ccs_try_alt_exec - Try to start execute handler. | |
637 | - * | |
638 | - * @r: Pointer to "struct ccs_request_info". | |
639 | - * | |
640 | - * Returns 0 on success, negative value otherwise. | |
641 | - */ | |
642 | -static int ccs_try_alt_exec(struct ccs_request_info *r) | |
643 | -{ | |
644 | - /* | |
645 | - * Contents of modified bprm. | |
646 | - * The envp[] in original bprm is moved to argv[] so that | |
647 | - * the alternatively executed program won't be affected by | |
648 | - * some dangerous environment variables like LD_PRELOAD. | |
649 | - * | |
650 | - * modified bprm->argc | |
651 | - * = original bprm->argc + original bprm->envc + 7 | |
652 | - * modified bprm->envc | |
653 | - * = 0 | |
654 | - * | |
655 | - * modified bprm->argv[0] | |
656 | - * = the program's name specified by *_execute_handler | |
657 | - * modified bprm->argv[1] | |
658 | - * = ccs_current_domain()->domainname->name | |
659 | - * modified bprm->argv[2] | |
660 | - * = the current process's name | |
661 | - * modified bprm->argv[3] | |
662 | - * = the current process's information (e.g. uid/gid). | |
663 | - * modified bprm->argv[4] | |
664 | - * = original bprm->filename | |
665 | - * modified bprm->argv[5] | |
666 | - * = original bprm->argc in string expression | |
667 | - * modified bprm->argv[6] | |
668 | - * = original bprm->envc in string expression | |
669 | - * modified bprm->argv[7] | |
670 | - * = original bprm->argv[0] | |
671 | - * ... | |
672 | - * modified bprm->argv[bprm->argc + 6] | |
673 | - * = original bprm->argv[bprm->argc - 1] | |
674 | - * modified bprm->argv[bprm->argc + 7] | |
675 | - * = original bprm->envp[0] | |
676 | - * ... | |
677 | - * modified bprm->argv[bprm->envc + bprm->argc + 6] | |
678 | - * = original bprm->envp[bprm->envc - 1] | |
679 | - */ | |
680 | - struct linux_binprm *bprm = r->bprm; | |
681 | - struct file *filp; | |
682 | - int retval; | |
683 | - const int original_argc = bprm->argc; | |
684 | - const int original_envc = bprm->envc; | |
685 | - | |
686 | - ccs_clear_request_info(r); | |
687 | - | |
688 | - /* Close the requested program's dentry. */ | |
689 | - r->obj.path[0].dentry = NULL; | |
690 | - r->obj.path[0].mnt = NULL; | |
691 | - r->obj.validate_done = false; | |
692 | - allow_write_access(bprm->file); | |
693 | - fput(bprm->file); | |
694 | - bprm->file = NULL; | |
695 | - | |
696 | - /* Invalidate page dump cache. */ | |
697 | - r->dump.page = NULL; | |
698 | - | |
699 | - /* Move envp[] to argv[] */ | |
700 | - bprm->argc += bprm->envc; | |
701 | - bprm->envc = 0; | |
702 | - | |
703 | - /* Set argv[6] */ | |
704 | - { | |
705 | - snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc); | |
706 | - retval = ccs_copy_argv(r->tmp, bprm); | |
707 | - if (retval < 0) | |
708 | - goto out; | |
709 | - } | |
710 | - | |
711 | - /* Set argv[5] */ | |
712 | - { | |
713 | - snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc); | |
714 | - retval = ccs_copy_argv(r->tmp, bprm); | |
715 | - if (retval < 0) | |
716 | - goto out; | |
717 | - } | |
718 | - | |
719 | - /* Set argv[4] */ | |
720 | - { | |
721 | - retval = ccs_copy_argv(bprm->filename, bprm); | |
722 | - if (retval < 0) | |
723 | - goto out; | |
724 | - } | |
725 | - | |
726 | - /* Set argv[3] */ | |
727 | - { | |
728 | - snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, | |
729 | - "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " | |
730 | - "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), | |
731 | - current_uid(), current_gid(), current_euid(), | |
732 | - current_egid(), current_suid(), current_sgid(), | |
733 | - current_fsuid(), current_fsgid()); | |
734 | - retval = ccs_copy_argv(r->tmp, bprm); | |
735 | - if (retval < 0) | |
736 | - goto out; | |
737 | - } | |
738 | - | |
739 | - /* Set argv[2] */ | |
740 | - { | |
741 | - char *exe = ccs_get_exe(); | |
742 | - if (exe) { | |
743 | - retval = ccs_copy_argv(exe, bprm); | |
744 | - kfree(exe); | |
745 | - } else { | |
746 | - retval = -ENOMEM; | |
747 | - } | |
748 | - if (retval < 0) | |
749 | - goto out; | |
750 | - } | |
751 | - | |
752 | - /* Set argv[1] */ | |
753 | - { | |
754 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
755 | - retval = ccs_copy_argv(ccs_current_domain()->domainname->name, | |
756 | - bprm); | |
757 | -#else | |
758 | - snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%s", | |
759 | - ccs_current_domain()->domainname->name); | |
760 | - retval = ccs_copy_argv(r->tmp, bprm); | |
761 | -#endif | |
762 | - if (retval < 0) | |
763 | - goto out; | |
764 | - } | |
765 | - | |
766 | - /* Set argv[0] */ | |
767 | - { | |
768 | - struct path root; | |
769 | - char *cp; | |
770 | - int root_len; | |
771 | - int handler_len; | |
772 | - get_fs_root(current->fs, &root); | |
773 | - cp = ccs_realpath(&root); | |
774 | - path_put(&root); | |
775 | - if (!cp) { | |
776 | - retval = -ENOMEM; | |
777 | - goto out; | |
778 | - } | |
779 | - root_len = strlen(cp); | |
780 | - retval = strncmp(r->handler_path->name, cp, root_len); | |
781 | - root_len--; | |
782 | - kfree(cp); | |
783 | - if (retval) { | |
784 | - retval = -ENOENT; | |
785 | - goto out; | |
786 | - } | |
787 | - handler_len = r->handler_path->total_len + 1; | |
788 | - /* r->handler is released by ccs_finish_execve(). */ | |
789 | - r->handler = kmalloc(handler_len, GFP_NOFS); | |
790 | - if (!r->handler) { | |
791 | - retval = -ENOMEM; | |
792 | - goto out; | |
793 | - } | |
794 | - /* Adjust root directory for open_exec(). */ | |
795 | - memmove(r->handler, r->handler_path->name + root_len, | |
796 | - handler_len - root_len); | |
797 | - ccs_unescape(r->handler); | |
798 | - retval = -ENOENT; | |
799 | - if (*r->handler != '/') | |
800 | - goto out; | |
801 | - retval = ccs_copy_argv(r->handler, bprm); | |
802 | - if (retval < 0) | |
803 | - goto out; | |
804 | - } | |
805 | - | |
806 | - /* | |
807 | - * OK, now restart the process with execute handler program's dentry. | |
808 | - */ | |
809 | - filp = open_exec(r->handler); | |
810 | - if (IS_ERR(filp)) { | |
811 | - retval = PTR_ERR(filp); | |
812 | - goto out; | |
813 | - } | |
814 | - r->obj.path[0].dentry = filp->f_dentry; | |
815 | - r->obj.path[0].mnt = filp->f_vfsmnt; | |
816 | - bprm->file = filp; | |
817 | - bprm->filename = r->handler; | |
818 | - bprm->interp = bprm->filename; | |
819 | - retval = prepare_binprm(bprm); | |
820 | - if (retval < 0) | |
821 | - goto out; | |
822 | - ccs_populate_patharg(r, true); | |
823 | - if (!r->param.s[0]) | |
824 | - retval = -ENOMEM; | |
825 | - else if (ccs_pathcmp(r->param.s[0], r->handler_path)) { | |
826 | - /* Failed to verify execute handler. */ | |
827 | - static u8 counter = 20; | |
828 | - if (counter) { | |
829 | - counter--; | |
830 | - printk(KERN_WARNING "Failed to verify: %s\n", | |
831 | - r->handler_path->name); | |
832 | - } | |
833 | - retval = -EINVAL; | |
834 | - } | |
835 | -out: | |
836 | - return retval; | |
837 | -} | |
838 | - | |
839 | -#endif | |
840 | - | |
841 | -/** | |
842 | - * ccs_dump_page - Dump a page to buffer. | |
843 | - * | |
844 | - * @bprm: Pointer to "struct linux_binprm". | |
845 | - * @pos: Location to dump. | |
846 | - * @dump: Poiner to "struct ccs_page_dump". | |
847 | - * | |
848 | - * Returns true on success, false otherwise. | |
849 | - */ | |
850 | -bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | |
851 | - struct ccs_page_dump *dump) | |
852 | -{ | |
853 | - struct page *page; | |
854 | - /* dump->data is released by ccs_start_execve(). */ | |
855 | - if (!dump->data) { | |
856 | - dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); | |
857 | - if (!dump->data) | |
858 | - return false; | |
859 | - } | |
860 | - /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ | |
861 | -#ifdef CONFIG_MMU | |
862 | - if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) | |
863 | - return false; | |
864 | -#else | |
865 | - page = bprm->page[pos / PAGE_SIZE]; | |
866 | -#endif | |
867 | - if (page != dump->page) { | |
868 | - const unsigned int offset = pos % PAGE_SIZE; | |
869 | - /* | |
870 | - * Maybe kmap()/kunmap() should be used here. | |
871 | - * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). | |
872 | - * So do I. | |
873 | - */ | |
874 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
875 | - char *kaddr = kmap_atomic(page); | |
876 | -#else | |
877 | - char *kaddr = kmap_atomic(page, KM_USER0); | |
878 | -#endif | |
879 | - dump->page = page; | |
880 | - memcpy(dump->data + offset, kaddr + offset, | |
881 | - PAGE_SIZE - offset); | |
882 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
883 | - kunmap_atomic(kaddr); | |
884 | -#else | |
885 | - kunmap_atomic(kaddr, KM_USER0); | |
886 | -#endif | |
887 | - } | |
888 | - /* Same with put_arg_page(page) in fs/exec.c */ | |
889 | -#ifdef CONFIG_MMU | |
890 | - put_page(page); | |
891 | -#endif | |
892 | - return true; | |
893 | -} | |
894 | - | |
895 | -/** | |
896 | - * ccs_start_execve - Prepare for execve() operation. | |
897 | - * | |
898 | - * @bprm: Pointer to "struct linux_binprm". | |
899 | - * @rp: Pointer to "struct ccs_request_info *". | |
900 | - * | |
901 | - * Returns 0 on success, negative value otherwise. | |
902 | - */ | |
903 | -static int ccs_start_execve(struct linux_binprm *bprm, | |
904 | - struct ccs_request_info **rp) | |
905 | -{ | |
906 | - int retval; | |
907 | - struct ccs_security *task = ccs_current_security(); | |
908 | - struct ccs_request_info *r; | |
909 | - int idx; | |
910 | - *rp = NULL; | |
911 | - r = kzalloc(sizeof(*r), GFP_NOFS); | |
912 | - if (!r) | |
913 | - return -ENOMEM; | |
914 | - r->tmp = kzalloc(CCS_EXEC_TMPSIZE, GFP_NOFS); | |
915 | - if (!r->tmp) { | |
916 | - kfree(r); | |
917 | - return -ENOMEM; | |
918 | - } | |
919 | - idx = ccs_read_lock(); | |
920 | - /* r->dump->data is allocated by ccs_dump_page(). */ | |
921 | - r->previous_domain = task->ccs_domain_info; | |
922 | - /* Clear manager flag. */ | |
923 | - task->ccs_flags &= ~CCS_TASK_IS_MANAGER; | |
924 | - *rp = r; | |
925 | - r->bprm = bprm; | |
926 | - r->obj.path[0].dentry = bprm->file->f_dentry; | |
927 | - r->obj.path[0].mnt = bprm->file->f_vfsmnt; | |
928 | - retval = ccs_execute(r); | |
929 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
930 | - if (!retval && bprm->envc) | |
931 | - retval = ccs_environ(r); | |
932 | -#endif | |
933 | - ccs_clear_request_info(r); | |
934 | - /* Drop refcount obtained by ccs_execute_path(). */ | |
935 | - if (r->obj.path[1].dentry) { | |
936 | - path_put(&r->obj.path[1]); | |
937 | - r->obj.path[1].dentry = NULL; | |
938 | - } | |
939 | - ccs_read_unlock(idx); | |
940 | - kfree(r->tmp); | |
941 | - r->tmp = NULL; | |
942 | - kfree(r->dump.data); | |
943 | - r->dump.data = NULL; | |
944 | - return retval; | |
945 | -} | |
946 | - | |
947 | -/** | |
948 | - * ccs_finish_execve - Clean up execve() operation. | |
949 | - * | |
950 | - * @retval: Return code of an execve() operation. | |
951 | - * @r: Pointer to "struct ccs_request_info". | |
952 | - * | |
953 | - * Returns nothing. | |
954 | - */ | |
955 | -static void ccs_finish_execve(int retval, struct ccs_request_info *r) | |
956 | -{ | |
957 | - struct ccs_security *task; | |
958 | - if (!r) | |
959 | - return; | |
960 | - task = ccs_current_security(); | |
961 | - if (retval < 0) { | |
962 | - task->ccs_domain_info = r->previous_domain; | |
963 | - /* | |
964 | - * Make task->ccs_domain_info visible to GC before changing | |
965 | - * task->ccs_flags. | |
966 | - */ | |
967 | - smp_wmb(); | |
968 | - } else { | |
969 | - /* Mark the current process as execute handler. */ | |
970 | - if (r->handler) | |
971 | - task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER; | |
972 | - /* Mark the current process as normal process. */ | |
973 | - else | |
974 | - task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER; | |
975 | - } | |
976 | - /* Tell GC that I finished execve(). */ | |
977 | - task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE; | |
978 | - ccs_clear_request_info(r); | |
979 | - kfree(r->handler); | |
980 | - kfree(r); | |
981 | -} | |
982 | - | |
983 | -/** | |
984 | - * __ccs_search_binary_handler - Main routine for do_execve(). | |
985 | - * | |
986 | - * @bprm: Pointer to "struct linux_binprm". | |
987 | - * @regs: Pointer to "struct pt_regs". | |
988 | - * | |
989 | - * Returns 0 on success, negative value otherwise. | |
990 | - * | |
991 | - * Performs permission checks for do_execve() and domain transition. | |
992 | - * Domain transition by "struct ccs_acl_info" will be reverted | |
993 | - * if do_execve() failed. | |
994 | - * Garbage collector does not remove "struct ccs_domain_info" from | |
995 | - * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is | |
996 | - * marked as CCS_TASK_IS_IN_EXECVE. | |
997 | - */ | |
998 | -static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
999 | - struct pt_regs *regs) | |
1000 | -{ | |
1001 | - struct ccs_request_info *r; | |
1002 | - int retval; | |
1003 | -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
1004 | - if (!ccs_policy_loaded) | |
1005 | - ccsecurity_exports.load_policy(bprm->filename); | |
1006 | -#endif | |
1007 | - retval = ccs_start_execve(bprm, &r); | |
1008 | - if (!retval) | |
1009 | - retval = search_binary_handler(bprm, regs); | |
1010 | - ccs_finish_execve(retval, r); | |
1011 | - return retval; | |
1012 | -} | |
1013 | - | |
1014 | -/** | |
1015 | - * ccs_permission_init - Register permission check hooks. | |
1016 | - * | |
1017 | - * Returns nothing. | |
1018 | - */ | |
1019 | -void __init ccs_permission_init(void) | |
1020 | -{ | |
1021 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1022 | - ccsecurity_ops.save_open_mode = __ccs_save_open_mode; | |
1023 | - ccsecurity_ops.clear_open_mode = __ccs_clear_open_mode; | |
1024 | - ccsecurity_ops.open_permission = __ccs_open_permission; | |
1025 | -#else | |
1026 | - ccsecurity_ops.open_permission = ccs_new_open_permission; | |
1027 | -#endif | |
1028 | - ccsecurity_ops.fcntl_permission = __ccs_fcntl_permission; | |
1029 | - ccsecurity_ops.ioctl_permission = __ccs_ioctl_permission; | |
1030 | - ccsecurity_ops.chmod_permission = __ccs_chmod_permission; | |
1031 | - ccsecurity_ops.chown_permission = __ccs_chown_permission; | |
1032 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
1033 | - ccsecurity_ops.getattr_permission = __ccs_getattr_permission; | |
1034 | -#endif | |
1035 | - ccsecurity_ops.pivot_root_permission = __ccs_pivot_root_permission; | |
1036 | - ccsecurity_ops.chroot_permission = __ccs_chroot_permission; | |
1037 | - ccsecurity_ops.umount_permission = __ccs_umount_permission; | |
1038 | - ccsecurity_ops.mknod_permission = __ccs_mknod_permission; | |
1039 | - ccsecurity_ops.mkdir_permission = __ccs_mkdir_permission; | |
1040 | - ccsecurity_ops.rmdir_permission = __ccs_rmdir_permission; | |
1041 | - ccsecurity_ops.unlink_permission = __ccs_unlink_permission; | |
1042 | - ccsecurity_ops.symlink_permission = __ccs_symlink_permission; | |
1043 | - ccsecurity_ops.truncate_permission = __ccs_truncate_permission; | |
1044 | - ccsecurity_ops.rename_permission = __ccs_rename_permission; | |
1045 | - ccsecurity_ops.link_permission = __ccs_link_permission; | |
1046 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
1047 | - ccsecurity_ops.open_exec_permission = __ccs_open_exec_permission; | |
1048 | - ccsecurity_ops.uselib_permission = __ccs_uselib_permission; | |
1049 | -#endif | |
1050 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
1051 | - ccsecurity_ops.parse_table = __ccs_parse_table; | |
1052 | -#endif | |
1053 | - ccsecurity_ops.mount_permission = __ccs_mount_permission; | |
1054 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
1055 | - ccsecurity_ops.capable = __ccs_capable; | |
1056 | - ccsecurity_ops.socket_create_permission = | |
1057 | - __ccs_socket_create_permission; | |
1058 | -#endif | |
1059 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1060 | - ccsecurity_ops.socket_listen_permission = | |
1061 | - __ccs_socket_listen_permission; | |
1062 | - ccsecurity_ops.socket_connect_permission = | |
1063 | - __ccs_socket_connect_permission; | |
1064 | - ccsecurity_ops.socket_bind_permission = __ccs_socket_bind_permission; | |
1065 | - ccsecurity_ops.socket_post_accept_permission = | |
1066 | - __ccs_socket_post_accept_permission; | |
1067 | - ccsecurity_ops.socket_sendmsg_permission = | |
1068 | - __ccs_socket_sendmsg_permission; | |
1069 | -#endif | |
1070 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
1071 | - ccsecurity_ops.socket_post_recvmsg_permission = | |
1072 | - __ccs_socket_post_recvmsg_permission; | |
1073 | -#endif | |
1074 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
1075 | - ccsecurity_ops.ptrace_permission = __ccs_ptrace_permission; | |
1076 | -#endif | |
1077 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
1078 | - ccsecurity_ops.kill_permission = ccs_signal_permission0; | |
1079 | - ccsecurity_ops.tgkill_permission = ccs_signal_permission1; | |
1080 | - ccsecurity_ops.tkill_permission = ccs_signal_permission0; | |
1081 | - ccsecurity_ops.sigqueue_permission = ccs_signal_permission0; | |
1082 | - ccsecurity_ops.tgsigqueue_permission = ccs_signal_permission1; | |
1083 | -#endif | |
1084 | - ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler; | |
1085 | -} | |
1086 | - | |
1087 | -/** | |
1088 | - * ccs_kern_path - Wrapper for kern_path(). | |
1089 | - * | |
1090 | - * @pathname: Pathname to resolve. Maybe NULL. | |
1091 | - * @flags: Lookup flags. | |
1092 | - * @path: Pointer to "struct path". | |
1093 | - * | |
1094 | - * Returns 0 on success, negative value otherwise. | |
1095 | - */ | |
1096 | -static int ccs_kern_path(const char *pathname, int flags, struct path *path) | |
1097 | -{ | |
1098 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
1099 | - if (!pathname || kern_path(pathname, flags, path)) | |
1100 | - return -ENOENT; | |
1101 | -#else | |
1102 | - struct nameidata nd; | |
1103 | - if (!pathname || path_lookup(pathname, flags, &nd)) | |
1104 | - return -ENOENT; | |
1105 | - *path = nd.path; | |
1106 | -#endif | |
1107 | - return 0; | |
1108 | -} | |
1109 | - | |
1110 | -/** | |
1111 | - * ccs_execute_path - Get dentry/vfsmount of a program. | |
1112 | - * | |
1113 | - * @bprm: Pointer to "struct linux_binprm". | |
1114 | - * @path: Pointer to "struct path". | |
1115 | - * | |
1116 | - * Returns 0 on success, negative value otherwise. | |
1117 | - */ | |
1118 | -static int ccs_execute_path(struct linux_binprm *bprm, struct path *path) | |
1119 | -{ | |
1120 | - /* | |
1121 | - * Follow symlinks if the requested pathname is on procfs, for | |
1122 | - * /proc/\$/exe is meaningless. | |
1123 | - */ | |
1124 | - const unsigned int follow = | |
1125 | - (bprm->file->f_dentry->d_sb->s_magic == PROC_SUPER_MAGIC) ? | |
1126 | - LOOKUP_FOLLOW : 0; | |
1127 | - if (ccs_kern_path(bprm->filename, follow, path)) | |
1128 | - return -ENOENT; | |
1129 | - return 0; | |
1130 | -} | |
1131 | - | |
1132 | -/** | |
1133 | - * ccs_mount_acl - Check permission for mount() operation. | |
1134 | - * | |
1135 | - * @dev_name: Name of device file or mount source. Maybe NULL. | |
1136 | - * @dir: Pointer to "struct path". | |
1137 | - * @type: Name of filesystem type. Maybe NULL. | |
1138 | - * @flags: Mount options. | |
1139 | - * @data: Mount options not in @flags. Maybe NULL. | |
1140 | - * | |
1141 | - * Returns 0 on success, negative value otherwise. | |
1142 | - */ | |
1143 | -static int ccs_mount_acl(const char *dev_name, struct path *dir, | |
1144 | - const char *type, unsigned long flags, | |
1145 | - const char *data) | |
1146 | -{ | |
1147 | - struct ccs_request_info r = { }; | |
1148 | - struct ccs_path_info rtype = { }; | |
1149 | - struct ccs_path_info rdata = { }; | |
1150 | - bool check_dev = false; | |
1151 | - bool check_data = false; | |
1152 | - int error; | |
1153 | - | |
1154 | - /* Compare fstype in order to determine type of dev_name argument. */ | |
1155 | - if (type == ccs_mounts[CCS_MOUNT_REMOUNT]) { | |
1156 | - /* do_remount() case. */ | |
1157 | - if (data && !(dir->mnt->mnt_sb->s_type->fs_flags & | |
1158 | - FS_BINARY_MOUNTDATA)) | |
1159 | - check_data = true; | |
1160 | - } else if (type == ccs_mounts[CCS_MOUNT_BIND]) { | |
1161 | - /* do_loopback() case. */ | |
1162 | - check_dev = true; | |
1163 | - } else if (type == ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE] || | |
1164 | - type == ccs_mounts[CCS_MOUNT_MAKE_PRIVATE] || | |
1165 | - type == ccs_mounts[CCS_MOUNT_MAKE_SLAVE] || | |
1166 | - type == ccs_mounts[CCS_MOUNT_MAKE_SHARED]) { | |
1167 | - /* do_change_type() case. */ | |
1168 | - } else if (type == ccs_mounts[CCS_MOUNT_MOVE]) { | |
1169 | - /* do_move_mount() case. */ | |
1170 | - check_dev = true; | |
1171 | - } else { | |
1172 | - /* do_new_mount() case. */ | |
1173 | - struct file_system_type *fstype; | |
1174 | - if (!type) | |
1175 | - return -EINVAL; | |
1176 | - fstype = get_fs_type(type); | |
1177 | - if (!fstype) | |
1178 | - return -ENODEV; | |
1179 | - if (fstype->fs_flags & FS_REQUIRES_DEV) | |
1180 | - check_dev = true; | |
1181 | - if (data && !(fstype->fs_flags & FS_BINARY_MOUNTDATA)) | |
1182 | - check_data = true; | |
1183 | - ccs_put_filesystem(fstype); | |
1184 | - } | |
1185 | - /* Start filling arguments. */ | |
1186 | - r.type = CCS_MAC_MOUNT; | |
1187 | - /* Remember mount options. */ | |
1188 | - r.param.i[0] = flags; | |
1189 | - /* | |
1190 | - * Remember mount point. | |
1191 | - * r.param.s[1] is calculated from r.obj.path[1] as needed. | |
1192 | - */ | |
1193 | - r.obj.path[1] = *dir; | |
1194 | - /* Remember fstype. */ | |
1195 | - rtype.name = ccs_encode(type); | |
1196 | - if (!rtype.name) | |
1197 | - return -ENOMEM; | |
1198 | - ccs_fill_path_info(&rtype); | |
1199 | - r.param.s[2] = &rtype; | |
1200 | - if (check_data) { | |
1201 | - /* Remember data argument. */ | |
1202 | - rdata.name = ccs_encode(data); | |
1203 | - if (!rdata.name) { | |
1204 | - error = -ENOMEM; | |
1205 | - goto out; | |
1206 | - } | |
1207 | - ccs_fill_path_info(&rdata); | |
1208 | - r.param.s[3] = &rdata; | |
1209 | - } | |
1210 | - if (check_dev) { | |
1211 | - /* | |
1212 | - * Remember device file or mount source. | |
1213 | - * r.param.s[0] is calculated from r.obj.path[0] as needed. | |
1214 | - */ | |
1215 | - if (ccs_kern_path(dev_name, LOOKUP_FOLLOW, &r.obj.path[0])) { | |
1216 | - error = -ENOENT; | |
1217 | - goto out; | |
1218 | - } | |
1219 | - } | |
1220 | - error = ccs_check_acl(&r, false); | |
1221 | - /* Drop refcount obtained by ccs_kern_path(). */ | |
1222 | - if (check_dev) | |
1223 | - path_put(&r.obj.path[0]); | |
1224 | -out: | |
1225 | - kfree(rtype.name); | |
1226 | - kfree(rdata.name); | |
1227 | - ccs_clear_request_info(&r); | |
1228 | - return error; | |
1229 | -} | |
1230 | - | |
1231 | -/** | |
1232 | - * __ccs_mount_permission - Check permission for mount() operation. | |
1233 | - * | |
1234 | - * @dev_name: Name of device file. Maybe NULL. | |
1235 | - * @path: Pointer to "struct path". | |
1236 | - * @type: Name of filesystem type. Maybe NULL. | |
1237 | - * @flags: Mount options. | |
1238 | - * @data_page: Mount options not in @flags. Maybe NULL. | |
1239 | - * | |
1240 | - * Returns 0 on success, negative value otherwise. | |
1241 | - */ | |
1242 | -static int __ccs_mount_permission(char *dev_name, struct path *path, | |
1243 | - const char *type, unsigned long flags, | |
1244 | - void *data_page) | |
1245 | -{ | |
1246 | - if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
1247 | - flags &= ~MS_MGC_MSK; | |
1248 | - if (flags & MS_REMOUNT) { | |
1249 | - type = ccs_mounts[CCS_MOUNT_REMOUNT]; | |
1250 | - flags &= ~MS_REMOUNT; | |
1251 | - } else if (flags & MS_BIND) { | |
1252 | - type = ccs_mounts[CCS_MOUNT_BIND]; | |
1253 | - flags &= ~MS_BIND; | |
1254 | - } else if (flags & MS_SHARED) { | |
1255 | - if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) | |
1256 | - return -EINVAL; | |
1257 | - type = ccs_mounts[CCS_MOUNT_MAKE_SHARED]; | |
1258 | - flags &= ~MS_SHARED; | |
1259 | - } else if (flags & MS_PRIVATE) { | |
1260 | - if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) | |
1261 | - return -EINVAL; | |
1262 | - type = ccs_mounts[CCS_MOUNT_MAKE_PRIVATE]; | |
1263 | - flags &= ~MS_PRIVATE; | |
1264 | - } else if (flags & MS_SLAVE) { | |
1265 | - if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) | |
1266 | - return -EINVAL; | |
1267 | - type = ccs_mounts[CCS_MOUNT_MAKE_SLAVE]; | |
1268 | - flags &= ~MS_SLAVE; | |
1269 | - } else if (flags & MS_UNBINDABLE) { | |
1270 | - if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) | |
1271 | - return -EINVAL; | |
1272 | - type = ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE]; | |
1273 | - flags &= ~MS_UNBINDABLE; | |
1274 | - } else if (flags & MS_MOVE) { | |
1275 | - type = ccs_mounts[CCS_MOUNT_MOVE]; | |
1276 | - flags &= ~MS_MOVE; | |
1277 | - } | |
1278 | - /* | |
1279 | - * do_mount() terminates data_page with '\0' if data_page != NULL. | |
1280 | - * Therefore, it is safe to pass data_page argument to ccs_mount_acl() | |
1281 | - * as "const char *" rather than "void *". | |
1282 | - */ | |
1283 | - ccs_check_auto_domain_transition(); | |
1284 | - return ccs_mount_acl(dev_name, path, type, flags, data_page); | |
1285 | -} | |
1286 | - | |
1287 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1288 | - | |
1289 | -/** | |
1290 | - * __ccs_save_open_mode - Remember original flags passed to sys_open(). | |
1291 | - * | |
1292 | - * @mode: Flags passed to sys_open(). | |
1293 | - * | |
1294 | - * Returns nothing. | |
1295 | - * | |
1296 | - * TOMOYO does not check "file write" if open(path, O_TRUNC | O_RDONLY) was | |
1297 | - * requested because write() is not permitted. Instead, TOMOYO checks | |
1298 | - * "file truncate" if O_TRUNC is passed. | |
1299 | - * | |
1300 | - * TOMOYO does not check "file read" and "file write" if open(path, 3) was | |
1301 | - * requested because read()/write() are not permitted. Instead, TOMOYO checks | |
1302 | - * "file ioctl" when ioctl() is requested. | |
1303 | - */ | |
1304 | -static void __ccs_save_open_mode(int mode) | |
1305 | -{ | |
1306 | - if ((mode & 3) == 3) | |
1307 | - ccs_current_security()->ccs_flags |= CCS_OPEN_FOR_IOCTL_ONLY; | |
1308 | -} | |
1309 | - | |
1310 | -/** | |
1311 | - * __ccs_clear_open_mode - Forget original flags passed to sys_open(). | |
1312 | - * | |
1313 | - * Returns nothing. | |
1314 | - */ | |
1315 | -static void __ccs_clear_open_mode(void) | |
1316 | -{ | |
1317 | - ccs_current_security()->ccs_flags &= ~CCS_OPEN_FOR_IOCTL_ONLY; | |
1318 | -} | |
1319 | - | |
1320 | -#endif | |
1321 | - | |
1322 | -/** | |
1323 | - * __ccs_open_permission - Check permission for "read" and "write". | |
1324 | - * | |
1325 | - * @dentry: Pointer to "struct dentry". | |
1326 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1327 | - * @flag: Flags for open(). | |
1328 | - * | |
1329 | - * Returns 0 on success, negative value otherwise. | |
1330 | - */ | |
1331 | -static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1332 | - const int flag) | |
1333 | -{ | |
1334 | - struct ccs_request_info r = { }; | |
1335 | - const u32 ccs_flags = ccs_current_flags(); | |
1336 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1337 | - const u8 acc_mode = (flag & 3) == 3 ? 0 : ACC_MODE(flag); | |
1338 | -#else | |
1339 | - const u8 acc_mode = (ccs_flags & CCS_OPEN_FOR_IOCTL_ONLY) ? 0 : | |
1340 | - ACC_MODE(flag); | |
1341 | -#endif | |
1342 | - int error = 0; | |
1343 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | |
1344 | - if (current->in_execve && !(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | |
1345 | - return 0; | |
1346 | -#endif | |
1347 | -#ifndef CONFIG_CCSECURITY_GETATTR | |
1348 | - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | |
1349 | - return 0; | |
1350 | -#endif | |
1351 | - r.obj.path[0].dentry = dentry; | |
1352 | - r.obj.path[0].mnt = mnt; | |
1353 | - if (!(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | |
1354 | - ccs_check_auto_domain_transition(); | |
1355 | - if (acc_mode & MAY_READ) { | |
1356 | - r.type = CCS_MAC_READ; | |
1357 | - error = ccs_check_acl(&r, false); | |
1358 | - } | |
1359 | - if (!error && (acc_mode & MAY_WRITE)) { | |
1360 | - r.type = (flag & O_APPEND) ? CCS_MAC_APPEND : | |
1361 | - CCS_MAC_WRITE; | |
1362 | - error = ccs_check_acl(&r, false); | |
1363 | - } | |
1364 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1365 | - if (!error && (flag & O_TRUNC)) { | |
1366 | - r.type = CCS_MAC_TRUNCATE; | |
1367 | - error = ccs_check_acl(&r, false); | |
1368 | - } | |
1369 | -#endif | |
1370 | - ccs_clear_request_info(&r); | |
1371 | - return error; | |
1372 | -} | |
1373 | - | |
1374 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1375 | - | |
1376 | -/** | |
1377 | - * ccs_new_open_permission - Check permission for "read" and "write". | |
1378 | - * | |
1379 | - * @filp: Pointer to "struct file". | |
1380 | - * | |
1381 | - * Returns 0 on success, negative value otherwise. | |
1382 | - */ | |
1383 | -static int ccs_new_open_permission(struct file *filp) | |
1384 | -{ | |
1385 | - return __ccs_open_permission(filp->f_path.dentry, filp->f_path.mnt, | |
1386 | - filp->f_flags); | |
1387 | -} | |
1388 | - | |
1389 | -#endif | |
1390 | - | |
1391 | -/** | |
1392 | - * ccs_path_perm - Check permission for "unlink", "rmdir", "truncate", "append", "getattr" and "chroot". | |
1393 | - * | |
1394 | - * @operation: One of values in "enum ccs_mac_index". | |
1395 | - * @dentry: Pointer to "struct dentry". | |
1396 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1397 | - * | |
1398 | - * Returns 0 on success, negative value otherwise. | |
1399 | - */ | |
1400 | -static int ccs_path_perm(const enum ccs_mac_index operation, | |
1401 | - struct dentry *dentry, struct vfsmount *mnt) | |
1402 | -{ | |
1403 | - struct ccs_request_info r = { }; | |
1404 | - ccs_check_auto_domain_transition(); | |
1405 | - r.type = operation; | |
1406 | - r.obj.path[0].dentry = dentry; | |
1407 | - r.obj.path[0].mnt = mnt; | |
1408 | - return ccs_check_acl(&r, true); | |
1409 | -} | |
1410 | - | |
1411 | -/** | |
1412 | - * ccs_mkdev_perm - Check permission for "mkblock" and "mkchar". | |
1413 | - * | |
1414 | - * @operation: Type of operation. (CCS_MAC_MKCHAR or CCS_MAC_MKBLOCK) | |
1415 | - * @dentry: Pointer to "struct dentry". | |
1416 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1417 | - * @mode: Create mode. | |
1418 | - * @dev: Device number. | |
1419 | - * | |
1420 | - * Returns 0 on success, negative value otherwise. | |
1421 | - */ | |
1422 | -static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | |
1423 | - struct vfsmount *mnt, const unsigned int mode, | |
1424 | - unsigned int dev) | |
1425 | -{ | |
1426 | - struct ccs_request_info r = { }; | |
1427 | - ccs_check_auto_domain_transition(); | |
1428 | - r.obj.path[0].dentry = dentry; | |
1429 | - r.obj.path[0].mnt = mnt; | |
1430 | - dev = new_decode_dev(dev); | |
1431 | - r.type = operation; | |
1432 | - r.param.i[0] = mode; | |
1433 | - r.param.i[1] = MAJOR(dev); | |
1434 | - r.param.i[2] = MINOR(dev); | |
1435 | - return ccs_check_acl(&r, true); | |
1436 | -} | |
1437 | - | |
1438 | -/** | |
1439 | - * ccs_path2_perm - Check permission for "rename", "link" and "pivot_root". | |
1440 | - * | |
1441 | - * @operation: One of values in "enum ccs_mac_index". | |
1442 | - * @dentry1: Pointer to "struct dentry". | |
1443 | - * @mnt1: Pointer to "struct vfsmount". Maybe NULL. | |
1444 | - * @dentry2: Pointer to "struct dentry". | |
1445 | - * @mnt2: Pointer to "struct vfsmount". Maybe NULL. | |
1446 | - * | |
1447 | - * Returns 0 on success, negative value otherwise. | |
1448 | - */ | |
1449 | -static int ccs_path2_perm(const enum ccs_mac_index operation, | |
1450 | - struct dentry *dentry1, struct vfsmount *mnt1, | |
1451 | - struct dentry *dentry2, struct vfsmount *mnt2) | |
1452 | -{ | |
1453 | - struct ccs_request_info r = { }; | |
1454 | - ccs_check_auto_domain_transition(); | |
1455 | - r.type = operation; | |
1456 | - r.obj.path[0].dentry = dentry1; | |
1457 | - r.obj.path[0].mnt = mnt1; | |
1458 | - r.obj.path[1].dentry = dentry2; | |
1459 | - r.obj.path[1].mnt = mnt2; | |
1460 | - return ccs_check_acl(&r, true); | |
1461 | -} | |
1462 | - | |
1463 | -/** | |
1464 | - * __ccs_symlink_permission - Check permission for "symlink". | |
1465 | - * | |
1466 | - * @dentry: Pointer to "struct dentry". | |
1467 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1468 | - * @target: Content of symlink. | |
1469 | - * | |
1470 | - * Returns 0 on success, negative value otherwise. | |
1471 | - */ | |
1472 | -static int __ccs_symlink_permission(struct dentry *dentry, | |
1473 | - struct vfsmount *mnt, const char *target) | |
1474 | -{ | |
1475 | - struct ccs_request_info r = { }; | |
1476 | - ccs_check_auto_domain_transition(); | |
1477 | - r.type = CCS_MAC_SYMLINK; | |
1478 | - r.obj.path[0].dentry = dentry; | |
1479 | - r.obj.path[0].mnt = mnt; | |
1480 | - r.obj.pathname[1].name = ccs_encode(target); | |
1481 | - if (!r.obj.pathname[1].name) | |
1482 | - return -ENOMEM; | |
1483 | - ccs_fill_path_info(&r.obj.pathname[1]); | |
1484 | - r.param.s[1] = &r.obj.pathname[1]; | |
1485 | - return ccs_check_acl(&r, true); | |
1486 | -} | |
1487 | - | |
1488 | -/** | |
1489 | - * ccs_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp" and "unmount". | |
1490 | - * | |
1491 | - * @type: One of values in "enum ccs_mac_index". | |
1492 | - * @dentry: Pointer to "struct dentry". | |
1493 | - * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1494 | - * @number: Number. | |
1495 | - * | |
1496 | - * Returns 0 on success, negative value otherwise. | |
1497 | - */ | |
1498 | -static int ccs_path_number_perm(const enum ccs_mac_index type, | |
1499 | - struct dentry *dentry, struct vfsmount *vfsmnt, | |
1500 | - unsigned long number) | |
1501 | -{ | |
1502 | - struct ccs_request_info r = { }; | |
1503 | - ccs_check_auto_domain_transition(); | |
1504 | - r.type = type; | |
1505 | - r.obj.path[0].dentry = dentry; | |
1506 | - r.obj.path[0].mnt = vfsmnt; | |
1507 | - r.param.i[0] = number; | |
1508 | - return ccs_check_acl(&r, true); | |
1509 | -} | |
1510 | - | |
1511 | -/** | |
1512 | - * __ccs_ioctl_permission - Check permission for "ioctl". | |
1513 | - * | |
1514 | - * @filp: Pointer to "struct file". | |
1515 | - * @cmd: Ioctl command number. | |
1516 | - * @arg: Param for @cmd. | |
1517 | - * | |
1518 | - * Returns 0 on success, negative value otherwise. | |
1519 | - */ | |
1520 | -static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
1521 | - unsigned long arg) | |
1522 | -{ | |
1523 | - return ccs_path_number_perm(CCS_MAC_IOCTL, filp->f_dentry, | |
1524 | - filp->f_vfsmnt, cmd); | |
1525 | -} | |
1526 | - | |
1527 | -/** | |
1528 | - * __ccs_chmod_permission - Check permission for "chmod". | |
1529 | - * | |
1530 | - * @dentry: Pointer to "struct dentry". | |
1531 | - * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1532 | - * @mode: Mode. | |
1533 | - * | |
1534 | - * Returns 0 on success, negative value otherwise. | |
1535 | - */ | |
1536 | -static int __ccs_chmod_permission(struct dentry *dentry, | |
1537 | - struct vfsmount *vfsmnt, mode_t mode) | |
1538 | -{ | |
1539 | - if (mode == (mode_t) -1) | |
1540 | - return 0; | |
1541 | - return ccs_path_number_perm(CCS_MAC_CHMOD, dentry, vfsmnt, | |
1542 | - mode & S_IALLUGO); | |
1543 | -} | |
1544 | - | |
1545 | -/** | |
1546 | - * __ccs_chown_permission - Check permission for "chown/chgrp". | |
1547 | - * | |
1548 | - * @dentry: Pointer to "struct dentry". | |
1549 | - * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1550 | - * @user: User ID. | |
1551 | - * @group: Group ID. | |
1552 | - * | |
1553 | - * Returns 0 on success, negative value otherwise. | |
1554 | - */ | |
1555 | -static int __ccs_chown_permission(struct dentry *dentry, | |
1556 | - struct vfsmount *vfsmnt, uid_t user, | |
1557 | - gid_t group) | |
1558 | -{ | |
1559 | - int error = 0; | |
1560 | - if (user == (uid_t) -1 && group == (gid_t) -1) | |
1561 | - return 0; | |
1562 | - if (user != (uid_t) -1) | |
1563 | - error = ccs_path_number_perm(CCS_MAC_CHOWN, dentry, | |
1564 | - vfsmnt, user); | |
1565 | - if (!error && group != (gid_t) -1) | |
1566 | - error = ccs_path_number_perm(CCS_MAC_CHGRP, dentry, | |
1567 | - vfsmnt, group); | |
1568 | - return error; | |
1569 | -} | |
1570 | - | |
1571 | -/** | |
1572 | - * __ccs_fcntl_permission - Check permission for changing O_APPEND flag. | |
1573 | - * | |
1574 | - * @file: Pointer to "struct file". | |
1575 | - * @cmd: Command number. | |
1576 | - * @arg: Value for @cmd. | |
1577 | - * | |
1578 | - * Returns 0 on success, negative value otherwise. | |
1579 | - */ | |
1580 | -static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
1581 | - unsigned long arg) | |
1582 | -{ | |
1583 | - if (!(cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND))) | |
1584 | - return 0; | |
1585 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1586 | - return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | |
1587 | - O_WRONLY | (arg & O_APPEND)); | |
1588 | -#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | |
1589 | - return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | |
1590 | - O_WRONLY | (arg & O_APPEND)); | |
1591 | -#else | |
1592 | - return __ccs_open_permission(file->f_dentry, file->f_vfsmnt, | |
1593 | - (O_WRONLY + 1) | (arg & O_APPEND)); | |
1594 | -#endif | |
1595 | -} | |
1596 | - | |
1597 | -/** | |
1598 | - * __ccs_pivot_root_permission - Check permission for pivot_root(). | |
1599 | - * | |
1600 | - * @old_path: Pointer to "struct path". | |
1601 | - * @new_path: Pointer to "struct path". | |
1602 | - * | |
1603 | - * Returns 0 on success, negative value otherwise. | |
1604 | - */ | |
1605 | -static int __ccs_pivot_root_permission(struct path *old_path, | |
1606 | - struct path *new_path) | |
1607 | -{ | |
1608 | - return ccs_path2_perm(CCS_MAC_PIVOT_ROOT, new_path->dentry, | |
1609 | - new_path->mnt, old_path->dentry, old_path->mnt); | |
1610 | -} | |
1611 | - | |
1612 | -/** | |
1613 | - * __ccs_chroot_permission - Check permission for chroot(). | |
1614 | - * | |
1615 | - * @path: Pointer to "struct path". | |
1616 | - * | |
1617 | - * Returns 0 on success, negative value otherwise. | |
1618 | - */ | |
1619 | -static int __ccs_chroot_permission(struct path *path) | |
1620 | -{ | |
1621 | - return ccs_path_perm(CCS_MAC_CHROOT, path->dentry, path->mnt); | |
1622 | -} | |
1623 | - | |
1624 | -/** | |
1625 | - * __ccs_umount_permission - Check permission for unmount. | |
1626 | - * | |
1627 | - * @mnt: Pointer to "struct vfsmount". | |
1628 | - * @flags: Unmount flags. | |
1629 | - * | |
1630 | - * Returns 0 on success, negative value otherwise. | |
1631 | - */ | |
1632 | -static int __ccs_umount_permission(struct vfsmount *mnt, int flags) | |
1633 | -{ | |
1634 | - return ccs_path_number_perm(CCS_MAC_UMOUNT, mnt->mnt_root, mnt, flags); | |
1635 | -} | |
1636 | - | |
1637 | -/** | |
1638 | - * __ccs_mknod_permission - Check permission for vfs_mknod(). | |
1639 | - * | |
1640 | - * @dentry: Pointer to "struct dentry". | |
1641 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1642 | - * @mode: Device type and permission. | |
1643 | - * @dev: Device number for block or character device. | |
1644 | - * | |
1645 | - * Returns 0 on success, negative value otherwise. | |
1646 | - */ | |
1647 | -static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1648 | - const unsigned int mode, unsigned int dev) | |
1649 | -{ | |
1650 | - int error = 0; | |
1651 | - const unsigned int perm = mode & S_IALLUGO; | |
1652 | - switch (mode & S_IFMT) { | |
1653 | - case S_IFCHR: | |
1654 | - error = ccs_mkdev_perm(CCS_MAC_MKCHAR, dentry, mnt, perm, | |
1655 | - dev); | |
1656 | - break; | |
1657 | - case S_IFBLK: | |
1658 | - error = ccs_mkdev_perm(CCS_MAC_MKBLOCK, dentry, mnt, perm, | |
1659 | - dev); | |
1660 | - break; | |
1661 | - case S_IFIFO: | |
1662 | - error = ccs_path_number_perm(CCS_MAC_MKFIFO, dentry, mnt, | |
1663 | - perm); | |
1664 | - break; | |
1665 | - case S_IFSOCK: | |
1666 | - error = ccs_path_number_perm(CCS_MAC_MKSOCK, dentry, mnt, | |
1667 | - perm); | |
1668 | - break; | |
1669 | - case 0: | |
1670 | - case S_IFREG: | |
1671 | - error = ccs_path_number_perm(CCS_MAC_CREATE, dentry, mnt, | |
1672 | - perm); | |
1673 | - break; | |
1674 | - } | |
1675 | - return error; | |
1676 | -} | |
1677 | - | |
1678 | -/** | |
1679 | - * __ccs_mkdir_permission - Check permission for vfs_mkdir(). | |
1680 | - * | |
1681 | - * @dentry: Pointer to "struct dentry". | |
1682 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1683 | - * @mode: Create mode. | |
1684 | - * | |
1685 | - * Returns 0 on success, negative value otherwise. | |
1686 | - */ | |
1687 | -static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1688 | - unsigned int mode) | |
1689 | -{ | |
1690 | - return ccs_path_number_perm(CCS_MAC_MKDIR, dentry, mnt, mode); | |
1691 | -} | |
1692 | - | |
1693 | -/** | |
1694 | - * __ccs_rmdir_permission - Check permission for vfs_rmdir(). | |
1695 | - * | |
1696 | - * @dentry: Pointer to "struct dentry". | |
1697 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1698 | - * | |
1699 | - * Returns 0 on success, negative value otherwise. | |
1700 | - */ | |
1701 | -static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt) | |
1702 | -{ | |
1703 | - return ccs_path_perm(CCS_MAC_RMDIR, dentry, mnt); | |
1704 | -} | |
1705 | - | |
1706 | -/** | |
1707 | - * __ccs_unlink_permission - Check permission for vfs_unlink(). | |
1708 | - * | |
1709 | - * @dentry: Pointer to "struct dentry". | |
1710 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1711 | - * | |
1712 | - * Returns 0 on success, negative value otherwise. | |
1713 | - */ | |
1714 | -static int __ccs_unlink_permission(struct dentry *dentry, struct vfsmount *mnt) | |
1715 | -{ | |
1716 | - return ccs_path_perm(CCS_MAC_UNLINK, dentry, mnt); | |
1717 | -} | |
1718 | - | |
1719 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
1720 | - | |
1721 | -/** | |
1722 | - * __ccs_getattr_permission - Check permission for vfs_getattr(). | |
1723 | - * | |
1724 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1725 | - * @dentry: Pointer to "struct dentry". | |
1726 | - * | |
1727 | - * Returns 0 on success, negative value otherwise. | |
1728 | - */ | |
1729 | -static int __ccs_getattr_permission(struct vfsmount *mnt, | |
1730 | - struct dentry *dentry) | |
1731 | -{ | |
1732 | - return ccs_path_perm(CCS_MAC_GETATTR, dentry, mnt); | |
1733 | -} | |
1734 | - | |
1735 | -#endif | |
1736 | - | |
1737 | -/** | |
1738 | - * __ccs_truncate_permission - Check permission for notify_change(). | |
1739 | - * | |
1740 | - * @dentry: Pointer to "struct dentry". | |
1741 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1742 | - * | |
1743 | - * Returns 0 on success, negative value otherwise. | |
1744 | - */ | |
1745 | -static int __ccs_truncate_permission(struct dentry *dentry, | |
1746 | - struct vfsmount *mnt) | |
1747 | -{ | |
1748 | - return ccs_path_perm(CCS_MAC_TRUNCATE, dentry, mnt); | |
1749 | -} | |
1750 | - | |
1751 | -/** | |
1752 | - * __ccs_rename_permission - Check permission for vfs_rename(). | |
1753 | - * | |
1754 | - * @old_dentry: Pointer to "struct dentry". | |
1755 | - * @new_dentry: Pointer to "struct dentry". | |
1756 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1757 | - * | |
1758 | - * Returns 0 on success, negative value otherwise. | |
1759 | - */ | |
1760 | -static int __ccs_rename_permission(struct dentry *old_dentry, | |
1761 | - struct dentry *new_dentry, | |
1762 | - struct vfsmount *mnt) | |
1763 | -{ | |
1764 | - return ccs_path2_perm(CCS_MAC_RENAME, old_dentry, mnt, new_dentry, | |
1765 | - mnt); | |
1766 | -} | |
1767 | - | |
1768 | -/** | |
1769 | - * __ccs_link_permission - Check permission for vfs_link(). | |
1770 | - * | |
1771 | - * @old_dentry: Pointer to "struct dentry". | |
1772 | - * @new_dentry: Pointer to "struct dentry". | |
1773 | - * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1774 | - * | |
1775 | - * Returns 0 on success, negative value otherwise. | |
1776 | - */ | |
1777 | -static int __ccs_link_permission(struct dentry *old_dentry, | |
1778 | - struct dentry *new_dentry, | |
1779 | - struct vfsmount *mnt) | |
1780 | -{ | |
1781 | - return ccs_path2_perm(CCS_MAC_LINK, old_dentry, mnt, new_dentry, | |
1782 | - mnt); | |
1783 | -} | |
1784 | - | |
1785 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
1786 | - | |
1787 | -/** | |
1788 | - * __ccs_open_exec_permission - Check permission for open_exec(). | |
1789 | - * | |
1790 | - * @dentry: Pointer to "struct dentry". | |
1791 | - * @mnt: Pointer to "struct vfsmount". | |
1792 | - * | |
1793 | - * Returns 0 on success, negative value otherwise. | |
1794 | - */ | |
1795 | -static int __ccs_open_exec_permission(struct dentry *dentry, | |
1796 | - struct vfsmount *mnt) | |
1797 | -{ | |
1798 | - return (ccs_current_flags() & CCS_TASK_IS_IN_EXECVE) ? | |
1799 | - __ccs_open_permission(dentry, mnt, O_RDONLY + 1) : 0; | |
1800 | -} | |
1801 | - | |
1802 | -/** | |
1803 | - * __ccs_uselib_permission - Check permission for sys_uselib(). | |
1804 | - * | |
1805 | - * @dentry: Pointer to "struct dentry". | |
1806 | - * @mnt: Pointer to "struct vfsmount". | |
1807 | - * | |
1808 | - * Returns 0 on success, negative value otherwise. | |
1809 | - */ | |
1810 | -static int __ccs_uselib_permission(struct dentry *dentry, struct vfsmount *mnt) | |
1811 | -{ | |
1812 | - return __ccs_open_permission(dentry, mnt, O_RDONLY + 1); | |
1813 | -} | |
1814 | - | |
1815 | -#endif | |
1816 | - | |
1817 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
1818 | - | |
1819 | -/** | |
1820 | - * ccs_sysctl_permission - Check permission for sysctl operation. | |
1821 | - * | |
1822 | - * @type: One of values in "enum ccs_mac_index". | |
1823 | - * @filename: Filename to check. | |
1824 | - * | |
1825 | - * Returns 0 on success, negative value otherwise. | |
1826 | - */ | |
1827 | -static int ccs_sysctl_permission(enum ccs_mac_index type, | |
1828 | - const struct ccs_path_info *filename) | |
1829 | -{ | |
1830 | - struct ccs_request_info r = { }; | |
1831 | - r.type = type; | |
1832 | - r.param.s[0] = filename; | |
1833 | - return ccs_check_acl(&r, true); | |
1834 | -} | |
1835 | - | |
1836 | -/** | |
1837 | - * __ccs_parse_table - Check permission for parse_table(). | |
1838 | - * | |
1839 | - * @name: Pointer to "int __user". | |
1840 | - * @nlen: Number of elements in @name. | |
1841 | - * @oldval: Pointer to "void __user". | |
1842 | - * @newval: Pointer to "void __user". | |
1843 | - * @table: Pointer to "struct ctl_table". | |
1844 | - * | |
1845 | - * Returns 0 on success, negative value otherwise. | |
1846 | - * | |
1847 | - * Note that this function is racy because this function checks values in | |
1848 | - * userspace memory which could be changed after permission check. | |
1849 | - */ | |
1850 | -static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | |
1851 | - void __user *newval, struct ctl_table *table) | |
1852 | -{ | |
1853 | - int n; | |
1854 | - int error = -ENOMEM; | |
1855 | - int op = 0; | |
1856 | - struct ccs_path_info buf; | |
1857 | - char *buffer = NULL; | |
1858 | - if (oldval) | |
1859 | - op |= 004; | |
1860 | - if (newval) | |
1861 | - op |= 002; | |
1862 | - if (!op) /* Neither read nor write */ | |
1863 | - return 0; | |
1864 | - buffer = kmalloc(PAGE_SIZE, GFP_NOFS); | |
1865 | - if (!buffer) | |
1866 | - goto out; | |
1867 | - snprintf(buffer, PAGE_SIZE - 1, "proc:/sys"); | |
1868 | -repeat: | |
1869 | - if (!nlen) { | |
1870 | - error = -ENOTDIR; | |
1871 | - goto out; | |
1872 | - } | |
1873 | - if (get_user(n, name)) { | |
1874 | - error = -EFAULT; | |
1875 | - goto out; | |
1876 | - } | |
1877 | - for ( ; table->ctl_name || table->procname; table++) { | |
1878 | - int pos; | |
1879 | - const char *cp; | |
1880 | - if (!n || n != table->ctl_name) | |
1881 | - continue; | |
1882 | - pos = strlen(buffer); | |
1883 | - cp = table->procname; | |
1884 | - error = -ENOMEM; | |
1885 | - if (cp) { | |
1886 | - int len = strlen(cp); | |
1887 | - if (len + 2 > PAGE_SIZE - 1) | |
1888 | - goto out; | |
1889 | - buffer[pos++] = '/'; | |
1890 | - memmove(buffer + pos, cp, len + 1); | |
1891 | - } else { | |
1892 | - /* Assume nobody assigns "=\$=" for procname. */ | |
1893 | - snprintf(buffer + pos, PAGE_SIZE - pos - 1, | |
1894 | - "/=%d=", table->ctl_name); | |
1895 | - if (!memchr(buffer, '\0', PAGE_SIZE - 2)) | |
1896 | - goto out; | |
1897 | - } | |
1898 | - if (!table->child) | |
1899 | - goto no_child; | |
1900 | - name++; | |
1901 | - nlen--; | |
1902 | - table = table->child; | |
1903 | - goto repeat; | |
1904 | -no_child: | |
1905 | - /* printk("sysctl='%s'\n", buffer); */ | |
1906 | - buf.name = ccs_encode(buffer); | |
1907 | - if (!buf.name) | |
1908 | - goto out; | |
1909 | - ccs_fill_path_info(&buf); | |
1910 | - if (op & MAY_READ) | |
1911 | - error = ccs_sysctl_permission(CCS_MAC_READ, &buf); | |
1912 | - else | |
1913 | - error = 0; | |
1914 | - if (!error && (op & MAY_WRITE)) | |
1915 | - error = ccs_sysctl_permission(CCS_MAC_WRITE, | |
1916 | - &buf); | |
1917 | - kfree(buf.name); | |
1918 | - goto out; | |
1919 | - } | |
1920 | - error = -ENOTDIR; | |
1921 | -out: | |
1922 | - kfree(buffer); | |
1923 | - return error; | |
1924 | -} | |
1925 | - | |
1926 | -#endif | |
1927 | - | |
1928 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
1929 | - | |
1930 | -/** | |
1931 | - * ccs_ip_matches_group - Check whether the given IP address matches members of the given IP group. | |
1932 | - * | |
1933 | - * @is_ipv6: True if @address is an IPv6 address. | |
1934 | - * @address: An IPv4 or IPv6 address. | |
1935 | - * @group: Pointer to "struct ccs_ip_group". | |
1936 | - * | |
1937 | - * Returns true if @address matches addresses in @group group, false otherwise. | |
1938 | - * | |
1939 | - * Caller holds ccs_read_lock(). | |
1940 | - */ | |
1941 | -static bool ccs_ip_matches_group(const bool is_ipv6, const u8 *address, | |
1942 | - const struct ccs_group *group) | |
1943 | -{ | |
1944 | - struct ccs_ip_group *member; | |
1945 | - bool matched = false; | |
1946 | - const u8 size = is_ipv6 ? 16 : 4; | |
1947 | - list_for_each_entry_srcu(member, &group->member_list, head.list, | |
1948 | - &ccs_ss) { | |
1949 | - if (member->head.is_deleted) | |
1950 | - continue; | |
1951 | - if (member->is_ipv6 != is_ipv6) | |
1952 | - continue; | |
1953 | - if (memcmp(&member->ip[0], address, size) > 0 || | |
1954 | - memcmp(address, &member->ip[1], size) > 0) | |
1955 | - continue; | |
1956 | - matched = true; | |
1957 | - break; | |
1958 | - } | |
1959 | - return matched; | |
1960 | -} | |
1961 | - | |
1962 | -/** | |
1963 | - * ccs_inet_entry - Check permission for INET network operation. | |
1964 | - * | |
1965 | - * @address: Pointer to "struct ccs_addr_info". | |
1966 | - * | |
1967 | - * Returns 0 on success, negative value otherwise. | |
1968 | - */ | |
1969 | -static int ccs_inet_entry(const struct ccs_addr_info *address) | |
1970 | -{ | |
1971 | - struct ccs_request_info r = { }; | |
1972 | - ccs_check_auto_domain_transition(); | |
1973 | - r.type = address->operation; | |
1974 | - r.param.is_ipv6 = address->inet.is_ipv6; | |
1975 | - r.param.ip = address->inet.address; | |
1976 | - r.param.i[0] = ntohs(address->inet.port); | |
1977 | - return ccs_check_acl(&r, true); | |
1978 | -} | |
1979 | - | |
1980 | -/** | |
1981 | - * ccs_check_inet_address - Check permission for inet domain socket's operation. | |
1982 | - * | |
1983 | - * @addr: Pointer to "struct sockaddr". | |
1984 | - * @addr_len: Size of @addr. | |
1985 | - * @port: Port number. | |
1986 | - * @address: Pointer to "struct ccs_addr_info". | |
1987 | - * | |
1988 | - * Returns 0 on success, negative value otherwise. | |
1989 | - */ | |
1990 | -static int ccs_check_inet_address(const struct sockaddr *addr, | |
1991 | - const unsigned int addr_len, const u16 port, | |
1992 | - struct ccs_addr_info *address) | |
1993 | -{ | |
1994 | - struct ccs_inet_addr_info *i = &address->inet; | |
1995 | - switch (addr->sa_family) { | |
1996 | - case AF_INET6: | |
1997 | - if (addr_len < SIN6_LEN_RFC2133) | |
1998 | - goto skip; | |
1999 | - i->is_ipv6 = true; | |
2000 | - i->address = | |
2001 | - ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; | |
2002 | - i->port = ((struct sockaddr_in6 *) addr)->sin6_port; | |
2003 | - break; | |
2004 | - case AF_INET: | |
2005 | - if (addr_len < sizeof(struct sockaddr_in)) | |
2006 | - goto skip; | |
2007 | - i->is_ipv6 = false; | |
2008 | - i->address = (u8 *) &((struct sockaddr_in *) addr)->sin_addr; | |
2009 | - i->port = ((struct sockaddr_in *) addr)->sin_port; | |
2010 | - break; | |
2011 | - default: | |
2012 | - goto skip; | |
2013 | - } | |
2014 | - if (address->operation == CCS_MAC_INET_RAW_BIND || | |
2015 | - address->operation == CCS_MAC_INET_RAW_SEND || | |
2016 | - address->operation == CCS_MAC_INET_RAW_RECV) | |
2017 | - i->port = htons(port); | |
2018 | - return ccs_inet_entry(address); | |
2019 | -skip: | |
2020 | - return 0; | |
2021 | -} | |
2022 | - | |
2023 | -/** | |
2024 | - * ccs_unix_entry - Check permission for UNIX network operation. | |
2025 | - * | |
2026 | - * @address: Pointer to "struct ccs_addr_info". | |
2027 | - * | |
2028 | - * Returns 0 on success, negative value otherwise. | |
2029 | - */ | |
2030 | -static int ccs_unix_entry(const struct ccs_addr_info *address) | |
2031 | -{ | |
2032 | - int error; | |
2033 | - char *buf = address->unix0.addr; | |
2034 | - int len = address->unix0.addr_len - sizeof(sa_family_t); | |
2035 | - if (len <= 0) { | |
2036 | - buf = "anonymous"; | |
2037 | - len = 9; | |
2038 | - } else if (buf[0]) { | |
2039 | - len = strnlen(buf, len); | |
2040 | - } | |
2041 | - buf = ccs_encode2(buf, len); | |
2042 | - if (buf) { | |
2043 | - struct ccs_path_info addr; | |
2044 | - struct ccs_request_info r = { }; | |
2045 | - addr.name = buf; | |
2046 | - ccs_fill_path_info(&addr); | |
2047 | - r.type = address->operation; | |
2048 | - r.param.s[0] = &addr; | |
2049 | - error = ccs_check_acl(&r, true); | |
2050 | - kfree(buf); | |
2051 | - } else | |
2052 | - error = -ENOMEM; | |
2053 | - return error; | |
2054 | -} | |
2055 | - | |
2056 | -/** | |
2057 | - * ccs_check_unix_address - Check permission for unix domain socket's operation. | |
2058 | - * | |
2059 | - * @addr: Pointer to "struct sockaddr". | |
2060 | - * @addr_len: Size of @addr. | |
2061 | - * @address: Pointer to "struct ccs_addr_info". | |
2062 | - * | |
2063 | - * Returns 0 on success, negative value otherwise. | |
2064 | - */ | |
2065 | -static int ccs_check_unix_address(struct sockaddr *addr, | |
2066 | - const unsigned int addr_len, | |
2067 | - struct ccs_addr_info *address) | |
2068 | -{ | |
2069 | - struct ccs_unix_addr_info *u = &address->unix0; | |
2070 | - if (addr->sa_family != AF_UNIX) | |
2071 | - return 0; | |
2072 | - u->addr = ((struct sockaddr_un *) addr)->sun_path; | |
2073 | - u->addr_len = addr_len; | |
2074 | - return ccs_unix_entry(address); | |
2075 | -} | |
2076 | - | |
2077 | -/** | |
2078 | - * ccs_sock_family - Get socket's family. | |
2079 | - * | |
2080 | - * @sk: Pointer to "struct sock". | |
2081 | - * | |
2082 | - * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. | |
2083 | - */ | |
2084 | -static u8 ccs_sock_family(struct sock *sk) | |
2085 | -{ | |
2086 | - u8 family; | |
2087 | - if (ccs_kernel_service()) | |
2088 | - return 0; | |
2089 | - family = sk->sk_family; | |
2090 | - switch (family) { | |
2091 | - case PF_INET: | |
2092 | - case PF_INET6: | |
2093 | - case PF_UNIX: | |
2094 | - return family; | |
2095 | - default: | |
2096 | - return 0; | |
2097 | - } | |
2098 | -} | |
2099 | - | |
2100 | -/** | |
2101 | - * __ccs_socket_listen_permission - Check permission for listening a socket. | |
2102 | - * | |
2103 | - * @sock: Pointer to "struct socket". | |
2104 | - * | |
2105 | - * Returns 0 on success, negative value otherwise. | |
2106 | - */ | |
2107 | -static int __ccs_socket_listen_permission(struct socket *sock) | |
2108 | -{ | |
2109 | - struct ccs_addr_info address; | |
2110 | - const u8 family = ccs_sock_family(sock->sk); | |
2111 | - const unsigned int type = sock->type; | |
2112 | - struct sockaddr_storage addr; | |
2113 | - int addr_len; | |
2114 | - if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | |
2115 | - return 0; | |
2116 | - { | |
2117 | - const int error = sock->ops->getname(sock, (struct sockaddr *) | |
2118 | - &addr, &addr_len, 0); | |
2119 | - if (error) | |
2120 | - return error; | |
2121 | - } | |
2122 | - if (family == PF_INET || family == PF_INET6) | |
2123 | - address.operation = CCS_MAC_INET_STREAM_LISTEN; | |
2124 | - else if (type == SOCK_STREAM) | |
2125 | - address.operation = CCS_MAC_UNIX_STREAM_LISTEN; | |
2126 | - else | |
2127 | - address.operation = CCS_MAC_UNIX_SEQPACKET_LISTEN; | |
2128 | - if (family == PF_UNIX) | |
2129 | - return ccs_check_unix_address((struct sockaddr *) &addr, | |
2130 | - addr_len, &address); | |
2131 | - return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, | |
2132 | - &address); | |
2133 | -} | |
2134 | - | |
2135 | -/** | |
2136 | - * __ccs_socket_connect_permission - Check permission for setting the remote address of a socket. | |
2137 | - * | |
2138 | - * @sock: Pointer to "struct socket". | |
2139 | - * @addr: Pointer to "struct sockaddr". | |
2140 | - * @addr_len: Size of @addr. | |
2141 | - * | |
2142 | - * Returns 0 on success, negative value otherwise. | |
2143 | - */ | |
2144 | -static int __ccs_socket_connect_permission(struct socket *sock, | |
2145 | - struct sockaddr *addr, int addr_len) | |
2146 | -{ | |
2147 | - struct ccs_addr_info address; | |
2148 | - const u8 family = ccs_sock_family(sock->sk); | |
2149 | - if (!family) | |
2150 | - return 0; | |
2151 | - switch (sock->type) { | |
2152 | - case SOCK_DGRAM: | |
2153 | - address.operation = family == PF_UNIX ? | |
2154 | - CCS_MAC_UNIX_DGRAM_SEND : | |
2155 | - CCS_MAC_INET_DGRAM_SEND; | |
2156 | - break; | |
2157 | - case SOCK_RAW: | |
2158 | - address.operation = CCS_MAC_INET_RAW_SEND; | |
2159 | - break; | |
2160 | - case SOCK_STREAM: | |
2161 | - address.operation = family == PF_UNIX ? | |
2162 | - CCS_MAC_UNIX_STREAM_CONNECT : | |
2163 | - CCS_MAC_INET_STREAM_CONNECT; | |
2164 | - break; | |
2165 | - case SOCK_SEQPACKET: | |
2166 | - address.operation = CCS_MAC_UNIX_SEQPACKET_CONNECT; | |
2167 | - break; | |
2168 | - default: | |
2169 | - return 0; | |
2170 | - } | |
2171 | - if (family == PF_UNIX) | |
2172 | - return ccs_check_unix_address(addr, addr_len, &address); | |
2173 | - return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | |
2174 | - &address); | |
2175 | -} | |
2176 | - | |
2177 | -/** | |
2178 | - * __ccs_socket_bind_permission - Check permission for setting the local address of a socket. | |
2179 | - * | |
2180 | - * @sock: Pointer to "struct socket". | |
2181 | - * @addr: Pointer to "struct sockaddr". | |
2182 | - * @addr_len: Size of @addr. | |
2183 | - * | |
2184 | - * Returns 0 on success, negative value otherwise. | |
2185 | - */ | |
2186 | -static int __ccs_socket_bind_permission(struct socket *sock, | |
2187 | - struct sockaddr *addr, int addr_len) | |
2188 | -{ | |
2189 | - struct ccs_addr_info address; | |
2190 | - const u8 family = ccs_sock_family(sock->sk); | |
2191 | - const unsigned int type = sock->type; | |
2192 | - if (!family) | |
2193 | - return 0; | |
2194 | - switch (type) { | |
2195 | - case SOCK_STREAM: | |
2196 | - address.operation = family == PF_UNIX ? | |
2197 | - CCS_MAC_UNIX_STREAM_BIND : | |
2198 | - CCS_MAC_INET_STREAM_BIND; | |
2199 | - break; | |
2200 | - case SOCK_DGRAM: | |
2201 | - address.operation = family == PF_UNIX ? | |
2202 | - CCS_MAC_UNIX_DGRAM_BIND : | |
2203 | - CCS_MAC_INET_DGRAM_BIND; | |
2204 | - break; | |
2205 | - case SOCK_RAW: | |
2206 | - address.operation = CCS_MAC_INET_RAW_BIND; | |
2207 | - break; | |
2208 | - case SOCK_SEQPACKET: | |
2209 | - address.operation = CCS_MAC_UNIX_SEQPACKET_BIND; | |
2210 | - break; | |
2211 | - default: | |
2212 | - return 0; | |
2213 | - } | |
2214 | - if (family == PF_UNIX) | |
2215 | - return ccs_check_unix_address(addr, addr_len, &address); | |
2216 | - return ccs_check_inet_address(addr, addr_len, sock->sk->sk_protocol, | |
2217 | - &address); | |
2218 | -} | |
2219 | - | |
2220 | -/** | |
2221 | - * __ccs_socket_sendmsg_permission - Check permission for sending a datagram. | |
2222 | - * | |
2223 | - * @sock: Pointer to "struct socket". | |
2224 | - * @msg: Pointer to "struct msghdr". | |
2225 | - * @size: Unused. | |
2226 | - * | |
2227 | - * Returns 0 on success, negative value otherwise. | |
2228 | - */ | |
2229 | -static int __ccs_socket_sendmsg_permission(struct socket *sock, | |
2230 | - struct msghdr *msg, int size) | |
2231 | -{ | |
2232 | - struct ccs_addr_info address; | |
2233 | - const u8 family = ccs_sock_family(sock->sk); | |
2234 | - const unsigned int type = sock->type; | |
2235 | - if (!msg->msg_name || !family || | |
2236 | - (type != SOCK_DGRAM && type != SOCK_RAW)) | |
2237 | - return 0; | |
2238 | - if (family == PF_UNIX) | |
2239 | - address.operation = CCS_MAC_UNIX_DGRAM_SEND; | |
2240 | - else if (type == SOCK_DGRAM) | |
2241 | - address.operation = CCS_MAC_INET_DGRAM_SEND; | |
2242 | - else | |
2243 | - address.operation = CCS_MAC_INET_RAW_SEND; | |
2244 | - if (family == PF_UNIX) | |
2245 | - return ccs_check_unix_address((struct sockaddr *) | |
2246 | - msg->msg_name, msg->msg_namelen, | |
2247 | - &address); | |
2248 | - return ccs_check_inet_address((struct sockaddr *) msg->msg_name, | |
2249 | - msg->msg_namelen, sock->sk->sk_protocol, | |
2250 | - &address); | |
2251 | -} | |
2252 | - | |
2253 | -/** | |
2254 | - * __ccs_socket_post_accept_permission - Check permission for accepting a socket. | |
2255 | - * | |
2256 | - * @sock: Pointer to "struct socket". | |
2257 | - * @newsock: Pointer to "struct socket". | |
2258 | - * | |
2259 | - * Returns 0 on success, negative value otherwise. | |
2260 | - */ | |
2261 | -static int __ccs_socket_post_accept_permission(struct socket *sock, | |
2262 | - struct socket *newsock) | |
2263 | -{ | |
2264 | - struct ccs_addr_info address; | |
2265 | - const u8 family = ccs_sock_family(sock->sk); | |
2266 | - const unsigned int type = sock->type; | |
2267 | - struct sockaddr_storage addr; | |
2268 | - int addr_len; | |
2269 | - if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) | |
2270 | - return 0; | |
2271 | - { | |
2272 | - const int error = newsock->ops->getname(newsock, | |
2273 | - (struct sockaddr *) | |
2274 | - &addr, &addr_len, 2); | |
2275 | - if (error) | |
2276 | - return error; | |
2277 | - } | |
2278 | - if (family == PF_INET || family == PF_INET6) | |
2279 | - address.operation = CCS_MAC_INET_STREAM_ACCEPT; | |
2280 | - else if (type == SOCK_STREAM) | |
2281 | - address.operation = CCS_MAC_UNIX_STREAM_ACCEPT; | |
2282 | - else | |
2283 | - address.operation = CCS_MAC_UNIX_SEQPACKET_ACCEPT; | |
2284 | - if (family == PF_UNIX) | |
2285 | - return ccs_check_unix_address((struct sockaddr *) &addr, | |
2286 | - addr_len, &address); | |
2287 | - return ccs_check_inet_address((struct sockaddr *) &addr, addr_len, 0, | |
2288 | - &address); | |
2289 | -} | |
2290 | - | |
2291 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
2292 | - | |
2293 | -/** | |
2294 | - * __ccs_socket_post_recvmsg_permission - Check permission for receiving a datagram. | |
2295 | - * | |
2296 | - * @sk: Pointer to "struct sock". | |
2297 | - * @skb: Pointer to "struct sk_buff". | |
2298 | - * @flags: Flags passed to skb_recv_datagram(). | |
2299 | - * | |
2300 | - * Returns 0 on success, negative value otherwise. | |
2301 | - */ | |
2302 | -static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | |
2303 | - struct sk_buff *skb, int flags) | |
2304 | -{ | |
2305 | - struct ccs_addr_info address; | |
2306 | - const u8 family = ccs_sock_family(sk); | |
2307 | - const unsigned int type = sk->sk_type; | |
2308 | - struct sockaddr_storage addr; | |
2309 | - if (!family || (type != SOCK_DGRAM && type != SOCK_RAW)) | |
2310 | - return 0; | |
2311 | - if (family == PF_UNIX) | |
2312 | - address.operation = CCS_MAC_UNIX_DGRAM_RECV; | |
2313 | - else if (type == SOCK_DGRAM) | |
2314 | - address.operation = CCS_MAC_INET_DGRAM_RECV; | |
2315 | - else | |
2316 | - address.operation = CCS_MAC_INET_RAW_RECV; | |
2317 | - switch (family) { | |
2318 | - case PF_INET6: | |
2319 | - { | |
2320 | - struct in6_addr *sin6 = (struct in6_addr *) &addr; | |
2321 | - address.inet.is_ipv6 = true; | |
2322 | - if (type == SOCK_DGRAM && | |
2323 | - skb->protocol == htons(ETH_P_IP)) | |
2324 | - ipv6_addr_set(sin6, 0, 0, htonl(0xffff), | |
2325 | - ip_hdr(skb)->saddr); | |
2326 | - else | |
2327 | - *sin6 = ipv6_hdr(skb)->saddr; | |
2328 | - break; | |
2329 | - } | |
2330 | - case PF_INET: | |
2331 | - { | |
2332 | - struct in_addr *sin4 = (struct in_addr *) &addr; | |
2333 | - address.inet.is_ipv6 = false; | |
2334 | - sin4->s_addr = ip_hdr(skb)->saddr; | |
2335 | - break; | |
2336 | - } | |
2337 | - default: /* == PF_UNIX */ | |
2338 | - { | |
2339 | - struct unix_address *u = unix_sk(skb->sk)->addr; | |
2340 | - unsigned int addr_len; | |
2341 | - if (u && u->len <= sizeof(addr)) { | |
2342 | - addr_len = u->len; | |
2343 | - memcpy(&addr, u->name, addr_len); | |
2344 | - } else { | |
2345 | - addr_len = 0; | |
2346 | - addr.ss_family = AF_UNIX; | |
2347 | - } | |
2348 | - if (ccs_check_unix_address((struct sockaddr *) &addr, | |
2349 | - addr_len, &address)) | |
2350 | - goto out; | |
2351 | - return 0; | |
2352 | - } | |
2353 | - } | |
2354 | - address.inet.address = (u8 *) &addr; | |
2355 | - if (type == SOCK_DGRAM) | |
2356 | - address.inet.port = udp_hdr(skb)->source; | |
2357 | - else | |
2358 | - address.inet.port = htons(sk->sk_protocol); | |
2359 | - if (ccs_inet_entry(&address)) | |
2360 | - goto out; | |
2361 | - return 0; | |
2362 | -out: | |
2363 | - /* | |
2364 | - * Remove from queue if MSG_PEEK is used so that | |
2365 | - * the head message from unwanted source in receive queue will not | |
2366 | - * prevent the caller from picking up next message from wanted source | |
2367 | - * when the caller is using MSG_PEEK flag for picking up. | |
2368 | - */ | |
2369 | - { | |
2370 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
2371 | - bool slow = false; | |
2372 | - if (type == SOCK_DGRAM && family != PF_UNIX) | |
2373 | - slow = lock_sock_fast(sk); | |
2374 | -#else | |
2375 | - if (type == SOCK_DGRAM && family != PF_UNIX) | |
2376 | - lock_sock(sk); | |
2377 | -#endif | |
2378 | - skb_kill_datagram(sk, skb, flags); | |
2379 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) | |
2380 | - if (type == SOCK_DGRAM && family != PF_UNIX) | |
2381 | - unlock_sock_fast(sk, slow); | |
2382 | -#else | |
2383 | - if (type == SOCK_DGRAM && family != PF_UNIX) | |
2384 | - release_sock(sk); | |
2385 | -#endif | |
2386 | - } | |
2387 | - return -EPERM; | |
2388 | -} | |
2389 | - | |
2390 | -#endif | |
2391 | - | |
2392 | -#endif | |
2393 | - | |
2394 | -#if defined(CONFIG_CCSECURITY_CAPABILITY) || defined(CONFIG_CCSECURITY_NETWORK) | |
2395 | - | |
2396 | -/** | |
2397 | - * ccs_kernel_service - Check whether I'm kernel service or not. | |
2398 | - * | |
2399 | - * Returns true if I'm kernel service, false otherwise. | |
2400 | - */ | |
2401 | -static bool ccs_kernel_service(void) | |
2402 | -{ | |
2403 | - /* Nothing to do if I am a kernel service. */ | |
2404 | - return segment_eq(get_fs(), KERNEL_DS); | |
2405 | -} | |
2406 | - | |
2407 | -#endif | |
2408 | - | |
2409 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
2410 | - | |
2411 | -/** | |
2412 | - * ccs_capable - Check permission for capability. | |
2413 | - * | |
2414 | - * @operation: Type of operation. | |
2415 | - * | |
2416 | - * Returns true on success, false otherwise. | |
2417 | - */ | |
2418 | -static bool __ccs_capable(const u8 operation) | |
2419 | -{ | |
2420 | - struct ccs_request_info r = { }; | |
2421 | - r.type = ccs_c2mac[operation]; | |
2422 | - return !ccs_check_acl(&r, true); | |
2423 | -} | |
2424 | - | |
2425 | -/** | |
2426 | - * __ccs_socket_create_permission - Check permission for creating a socket. | |
2427 | - * | |
2428 | - * @family: Protocol family. | |
2429 | - * @type: Unused. | |
2430 | - * @protocol: Unused. | |
2431 | - * | |
2432 | - * Returns 0 on success, negative value otherwise. | |
2433 | - */ | |
2434 | -static int __ccs_socket_create_permission(int family, int type, int protocol) | |
2435 | -{ | |
2436 | - if (ccs_kernel_service()) | |
2437 | - return 0; | |
2438 | - if (family == PF_PACKET && !ccs_capable(CCS_USE_PACKET_SOCKET)) | |
2439 | - return -EPERM; | |
2440 | - if (family == PF_NETLINK && !ccs_capable(CCS_USE_ROUTE_SOCKET)) | |
2441 | - return -EPERM; | |
2442 | - return 0; | |
2443 | -} | |
2444 | - | |
2445 | -#endif | |
2446 | - | |
2447 | -/** | |
2448 | - * ccs_manager - Check whether the current process is a policy manager. | |
2449 | - * | |
2450 | - * Returns true if the current process is permitted to modify policy | |
2451 | - * via /proc/ccs/ interface. | |
2452 | - * | |
2453 | - * Caller holds ccs_read_lock(). | |
2454 | - */ | |
2455 | -bool ccs_manager(void) | |
2456 | -{ | |
2457 | - struct ccs_security *task; | |
2458 | - if (!ccs_policy_loaded) | |
2459 | - return true; | |
2460 | - task = ccs_current_security(); | |
2461 | - if (task->ccs_flags & CCS_TASK_IS_MANAGER) | |
2462 | - return true; | |
2463 | - { | |
2464 | - struct ccs_request_info r = { }; | |
2465 | - r.type = CCS_MAC_MODIFY_POLICY; | |
2466 | - if (ccs_check_acl(&r, true) == 0) { | |
2467 | - /* Set manager flag. */ | |
2468 | - task->ccs_flags |= CCS_TASK_IS_MANAGER; | |
2469 | - return true; | |
2470 | - } | |
2471 | - } | |
2472 | - { /* Reduce error messages. */ | |
2473 | - static pid_t ccs_last_pid; | |
2474 | - const pid_t pid = current->pid; | |
2475 | - if (ccs_last_pid != pid) { | |
2476 | - const char *exe = ccs_get_exe(); | |
2477 | - printk(KERN_WARNING "'%s' (pid=%u domain='%s') is" | |
2478 | - " not permitted to update policies.\n", exe, | |
2479 | - pid, task->ccs_domain_info->domainname->name); | |
2480 | - ccs_last_pid = pid; | |
2481 | - kfree(exe); | |
2482 | - } | |
2483 | - } | |
2484 | - return false; | |
2485 | -} | |
2486 | - | |
2487 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
2488 | - | |
2489 | -/** | |
2490 | - * __ccs_ptrace_permission - Check permission for ptrace(). | |
2491 | - * | |
2492 | - * @request: Command number. | |
2493 | - * @pid: Target's PID. | |
2494 | - * | |
2495 | - * Returns 0 on success, negative value otherwise. | |
2496 | - */ | |
2497 | -static int __ccs_ptrace_permission(long request, long pid) | |
2498 | -{ | |
2499 | - struct ccs_domain_info *dest; | |
2500 | - int error = -ESRCH; | |
2501 | - const int idx = ccs_read_lock(); | |
2502 | - ccs_check_auto_domain_transition(); | |
2503 | - if (request == PTRACE_TRACEME) { | |
2504 | - dest = ccs_current_domain(); | |
2505 | - } else { | |
2506 | - struct task_struct *p; | |
2507 | - ccs_tasklist_lock(); | |
2508 | - p = ccsecurity_exports.find_task_by_vpid((pid_t) pid); | |
2509 | - if (p) | |
2510 | - dest = ccs_task_domain(p); | |
2511 | - else | |
2512 | - dest = NULL; | |
2513 | - ccs_tasklist_unlock(); | |
2514 | - } | |
2515 | - if (dest) { | |
2516 | - struct ccs_request_info r = { }; | |
2517 | - r.type = CCS_MAC_PTRACE; | |
2518 | - r.param.i[0] = request; | |
2519 | - r.param.s[0] = dest->domainname; | |
2520 | - error = ccs_check_acl(&r, true); | |
2521 | - } | |
2522 | - ccs_read_unlock(idx); | |
2523 | - return error; | |
2524 | -} | |
2525 | - | |
2526 | -#endif | |
2527 | - | |
2528 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
2529 | - | |
2530 | -/** | |
2531 | - * __ccs_signal_permission - Check permission for signal. | |
2532 | - * | |
2533 | - * @sig: Signal number. | |
2534 | - * | |
2535 | - * Returns 0 on success, negative value otherwise. | |
2536 | - * | |
2537 | - * Caller holds ccs_read_lock(). | |
2538 | - */ | |
2539 | -static int __ccs_signal_permission(const int sig) | |
2540 | -{ | |
2541 | - struct ccs_request_info r = { }; | |
2542 | - const int idx = ccs_read_lock(); | |
2543 | - int error; | |
2544 | - ccs_check_auto_domain_transition(); | |
2545 | - r.type = CCS_MAC_SIGNAL; | |
2546 | - r.param.i[0] = sig; | |
2547 | - error = ccs_check_acl(&r, true); | |
2548 | - ccs_read_unlock(idx); | |
2549 | - return error; | |
2550 | -} | |
2551 | - | |
2552 | -/** | |
2553 | - * ccs_signal_permission0 - Check permission for signal. | |
2554 | - * | |
2555 | - * @pid: Unused. | |
2556 | - * @sig: Signal number. | |
2557 | - * | |
2558 | - * Returns 0 on success, negative value otherwise. | |
2559 | - */ | |
2560 | -static int ccs_signal_permission0(const int pid, const int sig) | |
2561 | -{ | |
2562 | - return __ccs_signal_permission(sig); | |
2563 | -} | |
2564 | - | |
2565 | -/** | |
2566 | - * ccs_signal_permission1 - Permission check for signal(). | |
2567 | - * | |
2568 | - * @tgid: Unused. | |
2569 | - * @pid: Unused. | |
2570 | - * @sig: Signal number. | |
2571 | - * | |
2572 | - * Returns 0 on success, negative value otherwise. | |
2573 | - */ | |
2574 | -static int ccs_signal_permission1(pid_t tgid, pid_t pid, int sig) | |
2575 | -{ | |
2576 | - return __ccs_signal_permission(sig); | |
2577 | -} | |
2578 | - | |
2579 | -#endif | |
2580 | - | |
2581 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
2582 | - | |
2583 | -/** | |
2584 | - * ccs_env_perm - Check permission for environment variable's name. | |
2585 | - * | |
2586 | - * @r: Pointer to "struct ccs_request_info". | |
2587 | - * @name: Name of environment variable. Maybe "". | |
2588 | - * @value: Value of environment variable. Maybe "". | |
2589 | - * | |
2590 | - * Returns 0 on success, negative value otherwise. | |
2591 | - */ | |
2592 | -static int ccs_env_perm(struct ccs_request_info *r, const char *name, | |
2593 | - const char *value) | |
2594 | -{ | |
2595 | - struct ccs_path_info n; | |
2596 | - struct ccs_path_info v; | |
2597 | - n.name = name; | |
2598 | - ccs_fill_path_info(&n); | |
2599 | - v.name = value; | |
2600 | - ccs_fill_path_info(&v); | |
2601 | - r->type = CCS_MAC_ENVIRON; | |
2602 | - r->param.s[2] = &n; | |
2603 | - r->param.s[3] = &v; | |
2604 | - return ccs_check_acl(r, false); | |
2605 | -} | |
2606 | - | |
2607 | -/** | |
2608 | - * ccs_environ - Check permission for environment variable names. | |
2609 | - * | |
2610 | - * @r: Pointer to "struct ccs_request_info". | |
2611 | - * | |
2612 | - * Returns 0 on success, negative value otherwise. | |
2613 | - */ | |
2614 | -static int ccs_environ(struct ccs_request_info *r) | |
2615 | -{ | |
2616 | - struct linux_binprm *bprm = r->bprm; | |
2617 | - /* env_page.data is allocated by ccs_dump_page(). */ | |
2618 | - struct ccs_page_dump env_page = { }; | |
2619 | - char *arg_ptr; /* Size is CCS_EXEC_TMPSIZE bytes */ | |
2620 | - int arg_len = 0; | |
2621 | - unsigned long pos = bprm->p; | |
2622 | - int offset = pos % PAGE_SIZE; | |
2623 | - int argv_count = bprm->argc; | |
2624 | - int envp_count = bprm->envc; | |
2625 | - int error = -ENOMEM; | |
2626 | - arg_ptr = kzalloc(CCS_EXEC_TMPSIZE, GFP_NOFS); | |
2627 | - if (!arg_ptr) { | |
2628 | - r->failed_by_oom = true; | |
2629 | - goto out; | |
2630 | - } | |
2631 | - while (error == -ENOMEM) { | |
2632 | - if (!ccs_dump_page(bprm, pos, &env_page)) { | |
2633 | - r->failed_by_oom = true; | |
2634 | - goto out; | |
2635 | - } | |
2636 | - pos += PAGE_SIZE - offset; | |
2637 | - /* Read. */ | |
2638 | - while (argv_count && offset < PAGE_SIZE) { | |
2639 | - if (!env_page.data[offset++]) | |
2640 | - argv_count--; | |
2641 | - } | |
2642 | - if (argv_count) { | |
2643 | - offset = 0; | |
2644 | - continue; | |
2645 | - } | |
2646 | - while (offset < PAGE_SIZE) { | |
2647 | - char *value; | |
2648 | - const unsigned char c = env_page.data[offset++]; | |
2649 | - if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | |
2650 | - if (c > ' ' && c < 127 && c != '\\') { | |
2651 | - arg_ptr[arg_len++] = c; | |
2652 | - } else { | |
2653 | - arg_ptr[arg_len++] = '\\'; | |
2654 | - arg_ptr[arg_len++] = (c >> 6) + '0'; | |
2655 | - arg_ptr[arg_len++] | |
2656 | - = ((c >> 3) & 7) + '0'; | |
2657 | - arg_ptr[arg_len++] = (c & 7) + '0'; | |
2658 | - } | |
2659 | - } else { | |
2660 | - arg_ptr[arg_len] = '\0'; | |
2661 | - } | |
2662 | - if (c) | |
2663 | - continue; | |
2664 | - value = strchr(arg_ptr, '='); | |
2665 | - if (value) | |
2666 | - *value++ = '\0'; | |
2667 | - else | |
2668 | - value = ""; | |
2669 | - if (ccs_env_perm(r, arg_ptr, value)) { | |
2670 | - error = -EPERM; | |
2671 | - break; | |
2672 | - } | |
2673 | - if (!--envp_count) { | |
2674 | - error = 0; | |
2675 | - break; | |
2676 | - } | |
2677 | - arg_len = 0; | |
2678 | - } | |
2679 | - offset = 0; | |
2680 | - } | |
2681 | -out: | |
2682 | - kfree(env_page.data); | |
2683 | - kfree(arg_ptr); | |
2684 | - return error; | |
2685 | -} | |
2686 | - | |
2687 | -#endif | |
2688 | - | |
2689 | -/** | |
2690 | - * ccs_path_matches_group_or_pattern - Check whether the given pathname matches the given group or the given pattern. | |
2691 | - * | |
2692 | - * @path: Pointer to "struct ccs_path_info". | |
2693 | - * @group: Pointer to "struct ccs_group". Maybe NULL. | |
2694 | - * @pattern: Poiner to "struct ccs_path_info". Maybe NULL. | |
2695 | - * @match: True if positive match, false othwerwise. | |
2696 | - * | |
2697 | - * Returns true on success, false otherwise. | |
2698 | - */ | |
2699 | -static bool ccs_path_matches_group_or_pattern | |
2700 | -(const struct ccs_path_info *path, const struct ccs_group *group, | |
2701 | - const struct ccs_path_info *pattern, const bool match) | |
2702 | -{ | |
2703 | - if (group) | |
2704 | - return ccs_path_matches_group(path, group) == match; | |
2705 | - else if (pattern != &ccs_null_name) | |
2706 | - return ccs_path_matches_pattern(path, pattern) == match; | |
2707 | - else | |
2708 | - return !match; | |
2709 | -} | |
2710 | - | |
2711 | -/** | |
2712 | - * ccs_check_argv - Check argv[] in "struct linux_binbrm". | |
2713 | - * | |
2714 | - * @r: Pointer to "struct ccs_request_info". | |
2715 | - * @index: Index number to check. | |
2716 | - * @group: Pointer to "struct ccs_group". Maybe NULL. | |
2717 | - * @value: Poiner to "struct ccs_path_info". NULL if @group != NULL. | |
2718 | - * @match: True if positive match, false othwerwise. | |
2719 | - * | |
2720 | - * Returns true on success, false otherwise. | |
2721 | - */ | |
2722 | -static bool ccs_check_argv(struct ccs_request_info *r, unsigned long index, | |
2723 | - const struct ccs_group *group, | |
2724 | - const struct ccs_path_info *value, | |
2725 | - const bool match) | |
2726 | -{ | |
2727 | - struct linux_binprm *bprm = r->bprm; | |
2728 | - struct ccs_page_dump *dump = &r->dump; | |
2729 | - char *arg_ptr = r->tmp; | |
2730 | - int arg_len = 0; | |
2731 | - unsigned long pos = bprm->p; | |
2732 | - int offset = pos % PAGE_SIZE; | |
2733 | - struct ccs_path_info arg; | |
2734 | - if (index > bprm->argc) | |
2735 | - return false; | |
2736 | - while (1) { | |
2737 | - if (!ccs_dump_page(bprm, pos, dump)) { | |
2738 | - r->failed_by_oom = true; | |
2739 | - return false; | |
2740 | - } | |
2741 | - pos += PAGE_SIZE - offset; | |
2742 | - while (offset < PAGE_SIZE) { | |
2743 | - const unsigned char c = dump->data[offset++]; | |
2744 | - if (index) { | |
2745 | - if (!c) | |
2746 | - index--; | |
2747 | - continue; | |
2748 | - } | |
2749 | - if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | |
2750 | - if (c > ' ' && c < 127 && c != '\\') { | |
2751 | - arg_ptr[arg_len++] = c; | |
2752 | - } else { | |
2753 | - arg_ptr[arg_len++] = '\\'; | |
2754 | - arg_ptr[arg_len++] = (c >> 6) + '0'; | |
2755 | - arg_ptr[arg_len++] = | |
2756 | - ((c >> 3) & 7) + '0'; | |
2757 | - arg_ptr[arg_len++] = (c & 7) + '0'; | |
2758 | - } | |
2759 | - continue; | |
2760 | - } | |
2761 | - arg_ptr[arg_len] = '\0'; | |
2762 | - arg.name = arg_ptr; | |
2763 | - ccs_fill_path_info(&arg); | |
2764 | - return ccs_path_matches_group_or_pattern | |
2765 | - (&arg, group, value, match); | |
2766 | - } | |
2767 | - offset = 0; | |
2768 | - } | |
2769 | -} | |
2770 | - | |
2771 | -/** | |
2772 | - * ccs_check_envp - Check envp[] in "struct linux_binbrm". | |
2773 | - * | |
2774 | - * @r: Pointer to "struct ccs_request_info". | |
2775 | - * @name: Pointer to "struct ccs_path_info". | |
2776 | - * @group: Pointer to "struct ccs_group". Maybe NULL. | |
2777 | - * @value: Pointer to "struct ccs_path_info". NULL if @group != NULL. | |
2778 | - * @match: True if positive match, false othwerwise. | |
2779 | - * | |
2780 | - * Returns true on success, false otherwise. | |
2781 | - */ | |
2782 | -static bool ccs_check_envp(struct ccs_request_info *r, | |
2783 | - const struct ccs_path_info *name, | |
2784 | - const struct ccs_group *group, | |
2785 | - const struct ccs_path_info *value, | |
2786 | - const bool match) | |
2787 | -{ | |
2788 | - struct linux_binprm *bprm = r->bprm; | |
2789 | - struct ccs_page_dump *dump = &r->dump; | |
2790 | - char *arg_ptr = r->tmp; | |
2791 | - int arg_len = 0; | |
2792 | - unsigned long pos = bprm->p; | |
2793 | - int offset = pos % PAGE_SIZE; | |
2794 | - int argv_count = bprm->argc; | |
2795 | - int envp_count = bprm->envc; | |
2796 | - bool result = (value != &ccs_null_name) == match; | |
2797 | - struct ccs_path_info env; | |
2798 | - char *cp; | |
2799 | - while (envp_count) { | |
2800 | - if (!ccs_dump_page(bprm, pos, dump)) { | |
2801 | - r->failed_by_oom = true; | |
2802 | - return false; | |
2803 | - } | |
2804 | - pos += PAGE_SIZE - offset; | |
2805 | - while (envp_count && offset < PAGE_SIZE) { | |
2806 | - const unsigned char c = dump->data[offset++]; | |
2807 | - if (argv_count) { | |
2808 | - if (!c) | |
2809 | - argv_count--; | |
2810 | - continue; | |
2811 | - } | |
2812 | - if (c && arg_len < CCS_EXEC_TMPSIZE - 10) { | |
2813 | - if (c > ' ' && c < 127 && c != '\\') { | |
2814 | - arg_ptr[arg_len++] = c; | |
2815 | - } else { | |
2816 | - arg_ptr[arg_len++] = '\\'; | |
2817 | - arg_ptr[arg_len++] = (c >> 6) + '0'; | |
2818 | - arg_ptr[arg_len++] = | |
2819 | - ((c >> 3) & 7) + '0'; | |
2820 | - arg_ptr[arg_len++] = (c & 7) + '0'; | |
2821 | - } | |
2822 | - } else { | |
2823 | - arg_ptr[arg_len] = '\0'; | |
2824 | - } | |
2825 | - if (c) | |
2826 | - continue; | |
2827 | - arg_len = 0; | |
2828 | - envp_count--; | |
2829 | - /* Check. */ | |
2830 | - cp = strchr(arg_ptr, '='); | |
2831 | - if (!cp) | |
2832 | - cp = ""; | |
2833 | - else | |
2834 | - *cp++ = '\0'; | |
2835 | - env.name = arg_ptr; | |
2836 | - ccs_fill_path_info(&env); | |
2837 | - if (!ccs_path_matches_pattern(&env, name)) | |
2838 | - continue; | |
2839 | - result = true; | |
2840 | - env.name = cp; | |
2841 | - ccs_fill_path_info(&env); | |
2842 | - if (ccs_path_matches_group_or_pattern | |
2843 | - (&env, group, value, match)) | |
2844 | - continue; | |
2845 | - return false; | |
2846 | - } | |
2847 | - offset = 0; | |
2848 | - } | |
2849 | - return result; | |
2850 | -} | |
2851 | - | |
2852 | -/** | |
2853 | - * ccs_get_attributes - Revalidate "struct inode". | |
2854 | - * | |
2855 | - * @r: Pointer to "struct ccs_request_info". | |
2856 | - * | |
2857 | - * Returns nothing. | |
2858 | - */ | |
2859 | -void ccs_get_attributes(struct ccs_request_info *r) | |
2860 | -{ | |
2861 | - u8 i; | |
2862 | - struct dentry *dentry = NULL; | |
2863 | - | |
2864 | - if (r->obj.validate_done) | |
2865 | - return; | |
2866 | - for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | |
2867 | - struct inode *inode; | |
2868 | - switch (i) { | |
2869 | - case CCS_PATH1: | |
2870 | - dentry = r->obj.path[0].dentry; | |
2871 | - if (!dentry) | |
2872 | - continue; | |
2873 | - break; | |
2874 | - case CCS_PATH2: | |
2875 | - dentry = r->obj.path[1].dentry; | |
2876 | - if (!dentry) | |
2877 | - continue; | |
2878 | - break; | |
2879 | - default: | |
2880 | - if (!dentry) | |
2881 | - continue; | |
2882 | - dentry = dget_parent(dentry); | |
2883 | - break; | |
2884 | - } | |
2885 | - inode = dentry->d_inode; | |
2886 | - if (inode) { | |
2887 | - struct ccs_mini_stat *stat = &r->obj.stat[i]; | |
2888 | - stat->uid = inode->i_uid; | |
2889 | - stat->gid = inode->i_gid; | |
2890 | - stat->ino = inode->i_ino; | |
2891 | - stat->mode = inode->i_mode; | |
2892 | - stat->dev = inode->i_sb->s_dev; | |
2893 | - stat->rdev = inode->i_rdev; | |
2894 | - stat->fsmagic = dentry->d_sb->s_magic; | |
2895 | - r->obj.stat_valid[i] = true; | |
2896 | - } | |
2897 | - if (i & 1) /* parent directory */ | |
2898 | - dput(dentry); | |
2899 | - } | |
2900 | - r->obj.validate_done = true; | |
2901 | -} | |
2902 | - | |
2903 | -/** | |
2904 | - * ccs_populate_patharg - Calculate pathname for permission check and audit logs. | |
2905 | - * | |
2906 | - * @r: Pointer to "struct ccs_request_info". | |
2907 | - * @first: True for first pathname, false for second pathname. | |
2908 | - * | |
2909 | - * Returns nothing. | |
2910 | - */ | |
2911 | -void ccs_populate_patharg(struct ccs_request_info *r, const bool first) | |
2912 | -{ | |
2913 | - struct ccs_path_info *buf = &r->obj.pathname[!first]; | |
2914 | - struct path *path = &r->obj.path[!first]; | |
2915 | - if (!buf->name && path->dentry) { | |
2916 | - buf->name = ccs_realpath(path); | |
2917 | - /* Set OOM flag if failed. */ | |
2918 | - if (!buf->name) { | |
2919 | - r->failed_by_oom = true; | |
2920 | - return; | |
2921 | - } | |
2922 | - ccs_fill_path_info(buf); | |
2923 | - } | |
2924 | - if (!r->param.s[!first] && buf->name) | |
2925 | - r->param.s[!first] = buf; | |
2926 | -} | |
2927 | - | |
2928 | -/** | |
2929 | - * ccs_cond2arg - Assign values to condition variables. | |
2930 | - * | |
2931 | - * @arg: Pointer to "struct ccs_cond_arg". | |
2932 | - * @cmd: One of values in "enum ccs_conditions_index". | |
2933 | - * @condp: Pointer to "union ccs_condition_element *". | |
2934 | - * @r: Pointer to "struct ccs_request_info". | |
2935 | - * | |
2936 | - * Returns true on success, false othwerwise. | |
2937 | - * | |
2938 | - * This function should not fail. But it can fail if (for example) out of | |
2939 | - * memory has occured while calculating ccs_populate_patharg() or | |
2940 | - * ccs_get_exename(). | |
2941 | - */ | |
2942 | -static bool ccs_cond2arg(struct ccs_cond_arg *arg, | |
2943 | - const enum ccs_conditions_index cmd, | |
2944 | - const union ccs_condition_element **condp, | |
2945 | - struct ccs_request_info *r) | |
2946 | -{ | |
2947 | - struct ccs_mini_stat *stat; | |
2948 | - unsigned long value; | |
2949 | - const struct linux_binprm *bprm = r->bprm; | |
2950 | - const struct ccs_request_param *param = &r->param; | |
2951 | - arg->type = CCS_ARG_TYPE_NUMBER; | |
2952 | - switch (cmd) { | |
2953 | - case CCS_SELF_UID: | |
2954 | - value = current_uid(); | |
2955 | - break; | |
2956 | - case CCS_SELF_EUID: | |
2957 | - value = current_euid(); | |
2958 | - break; | |
2959 | - case CCS_SELF_SUID: | |
2960 | - value = current_suid(); | |
2961 | - break; | |
2962 | - case CCS_SELF_FSUID: | |
2963 | - value = current_fsuid(); | |
2964 | - break; | |
2965 | - case CCS_SELF_GID: | |
2966 | - value = current_gid(); | |
2967 | - break; | |
2968 | - case CCS_SELF_EGID: | |
2969 | - value = current_egid(); | |
2970 | - break; | |
2971 | - case CCS_SELF_SGID: | |
2972 | - value = current_sgid(); | |
2973 | - break; | |
2974 | - case CCS_SELF_FSGID: | |
2975 | - value = current_fsgid(); | |
2976 | - break; | |
2977 | - case CCS_SELF_PID: | |
2978 | - value = ccs_sys_getpid(); | |
2979 | - break; | |
2980 | - case CCS_SELF_PPID: | |
2981 | - value = ccs_sys_getppid(); | |
2982 | - break; | |
2983 | - case CCS_OBJ_IS_SOCKET: | |
2984 | - value = S_IFSOCK; | |
2985 | - break; | |
2986 | - case CCS_OBJ_IS_SYMLINK: | |
2987 | - value = S_IFLNK; | |
2988 | - break; | |
2989 | - case CCS_OBJ_IS_FILE: | |
2990 | - value = S_IFREG; | |
2991 | - break; | |
2992 | - case CCS_OBJ_IS_BLOCK_DEV: | |
2993 | - value = S_IFBLK; | |
2994 | - break; | |
2995 | - case CCS_OBJ_IS_DIRECTORY: | |
2996 | - value = S_IFDIR; | |
2997 | - break; | |
2998 | - case CCS_OBJ_IS_CHAR_DEV: | |
2999 | - value = S_IFCHR; | |
3000 | - break; | |
3001 | - case CCS_OBJ_IS_FIFO: | |
3002 | - value = S_IFIFO; | |
3003 | - break; | |
3004 | - case CCS_EXEC_ARGC: | |
3005 | - if (!bprm) | |
3006 | - return false; | |
3007 | - value = bprm->argc; | |
3008 | - break; | |
3009 | - case CCS_EXEC_ENVC: | |
3010 | - if (!bprm) | |
3011 | - return false; | |
3012 | - value = bprm->envc; | |
3013 | - break; | |
3014 | - case CCS_TASK_TYPE: | |
3015 | - value = ((u8) ccs_current_flags()) & | |
3016 | - CCS_TASK_IS_EXECUTE_HANDLER; | |
3017 | - break; | |
3018 | - case CCS_TASK_EXECUTE_HANDLER: | |
3019 | - value = CCS_TASK_IS_EXECUTE_HANDLER; | |
3020 | - break; | |
3021 | - case CCS_ARGV_ENTRY: | |
3022 | - case CCS_IMM_NUMBER_ENTRY1: | |
3023 | - value = (*condp)->value; | |
3024 | - (*condp)++; | |
3025 | - break; | |
3026 | - case CCS_COND_NARG0: | |
3027 | - value = param->i[0]; | |
3028 | - break; | |
3029 | - case CCS_COND_NARG1: | |
3030 | - value = param->i[1]; | |
3031 | - break; | |
3032 | - case CCS_COND_NARG2: | |
3033 | - value = param->i[2]; | |
3034 | - break; | |
3035 | - case CCS_HANDLER_PATH: | |
3036 | - case CCS_TRANSIT_DOMAIN: | |
3037 | - case CCS_COND_IPARG: | |
3038 | - /* Values are loaded by caller. Just return a dummy. */ | |
3039 | - arg->type = CCS_ARG_TYPE_NONE; | |
3040 | - value = 0; | |
3041 | - break; | |
3042 | - default: | |
3043 | - goto not_single_value; | |
3044 | - } | |
3045 | - arg->value[0] = value; | |
3046 | - arg->value[1] = value; | |
3047 | - return true; | |
3048 | -not_single_value: | |
3049 | - if (cmd == CCS_IMM_NUMBER_ENTRY2) { | |
3050 | - arg->value[0] = (*condp)->value; | |
3051 | - (*condp)++; | |
3052 | - arg->value[1] = (*condp)->value; | |
3053 | - (*condp)++; | |
3054 | - return true; | |
3055 | - } | |
3056 | - switch (cmd) { | |
3057 | - case CCS_COND_SARG0: | |
3058 | - if (!r->param.s[0]) | |
3059 | - ccs_populate_patharg(r, true); | |
3060 | - arg->name = r->param.s[0]; | |
3061 | - break; | |
3062 | - case CCS_COND_SARG1: | |
3063 | - if (!r->param.s[1]) | |
3064 | - ccs_populate_patharg(r, false); | |
3065 | - arg->name = r->param.s[1]; | |
3066 | - break; | |
3067 | - case CCS_COND_SARG2: | |
3068 | - arg->name = r->param.s[2]; | |
3069 | - break; | |
3070 | - case CCS_COND_SARG3: | |
3071 | - arg->name = r->param.s[3]; | |
3072 | - break; | |
3073 | - case CCS_ENVP_ENTRY: | |
3074 | - case CCS_IMM_NAME_ENTRY: | |
3075 | - arg->name = (*condp)->path; | |
3076 | - (*condp)++; | |
3077 | - break; | |
3078 | - case CCS_SELF_EXE: | |
3079 | - if (!r->exename.name) { | |
3080 | - ccs_get_exename(&r->exename); | |
3081 | - /* Set OOM flag if failed. */ | |
3082 | - if (!r->exename.name) | |
3083 | - r->failed_by_oom = true; | |
3084 | - } | |
3085 | - arg->name = &r->exename; | |
3086 | - break; | |
3087 | - case CCS_COND_DOMAIN: | |
3088 | - arg->name = r->param.s[0]; | |
3089 | - break; | |
3090 | - case CCS_SELF_DOMAIN: | |
3091 | - arg->name = ccs_current_domain()->domainname; | |
3092 | - break; | |
3093 | - default: | |
3094 | - goto not_single_name; | |
3095 | - } | |
3096 | - if (!arg->name) | |
3097 | - return false; | |
3098 | - arg->type = CCS_ARG_TYPE_NAME; | |
3099 | - return true; | |
3100 | -not_single_name: | |
3101 | - if (cmd == CCS_IMM_GROUP) { | |
3102 | - arg->type = CCS_ARG_TYPE_GROUP; | |
3103 | - arg->group = (*condp)->group; | |
3104 | - (*condp)++; | |
3105 | - return true; | |
3106 | - } | |
3107 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
3108 | - if (cmd == CCS_IMM_IPV4ADDR_ENTRY1) { | |
3109 | - arg->type = CCS_ARG_TYPE_IPV4ADDR; | |
3110 | - memmove(&arg->ip[0], &(*condp)->ip, 4); | |
3111 | - memmove(&arg->ip[1], &(*condp)->ip, 4); | |
3112 | - (*condp)++; | |
3113 | - return true; | |
3114 | - } | |
3115 | - if (cmd == CCS_IMM_IPV4ADDR_ENTRY2) { | |
3116 | - arg->type = CCS_ARG_TYPE_IPV4ADDR; | |
3117 | - memmove(&arg->ip[0], &(*condp)->ip, 4); | |
3118 | - (*condp)++; | |
3119 | - memmove(&arg->ip[1], &(*condp)->ip, 4); | |
3120 | - (*condp)++; | |
3121 | - return true; | |
3122 | - } | |
3123 | - if (cmd == CCS_IMM_IPV6ADDR_ENTRY1) { | |
3124 | - arg->type = CCS_ARG_TYPE_IPV6ADDR; | |
3125 | - memmove(&arg->ip[0], &(*condp)->ip, 16); | |
3126 | - memmove(&arg->ip[1], &(*condp)->ip, 16); | |
3127 | - *condp = (void *) | |
3128 | - (((u8 *) *condp) + sizeof(struct in6_addr)); | |
3129 | - return true; | |
3130 | - } | |
3131 | - if (cmd == CCS_IMM_IPV6ADDR_ENTRY2) { | |
3132 | - arg->type = CCS_ARG_TYPE_IPV6ADDR; | |
3133 | - memmove(&arg->ip[0], &(*condp)->ip, 16); | |
3134 | - *condp = (void *) | |
3135 | - (((u8 *) *condp) + sizeof(struct in6_addr)); | |
3136 | - memmove(&arg->ip[1], &(*condp)->ip, 16); | |
3137 | - *condp = (void *) | |
3138 | - (((u8 *) *condp) + sizeof(struct in6_addr)); | |
3139 | - return true; | |
3140 | - } | |
3141 | -#endif | |
3142 | - switch (cmd) { | |
3143 | - case CCS_MODE_SETUID: | |
3144 | - value = S_ISUID; | |
3145 | - break; | |
3146 | - case CCS_MODE_SETGID: | |
3147 | - value = S_ISGID; | |
3148 | - break; | |
3149 | - case CCS_MODE_STICKY: | |
3150 | - value = S_ISVTX; | |
3151 | - break; | |
3152 | - case CCS_MODE_OWNER_READ: | |
3153 | - value = S_IRUSR; | |
3154 | - break; | |
3155 | - case CCS_MODE_OWNER_WRITE: | |
3156 | - value = S_IWUSR; | |
3157 | - break; | |
3158 | - case CCS_MODE_OWNER_EXECUTE: | |
3159 | - value = S_IXUSR; | |
3160 | - break; | |
3161 | - case CCS_MODE_GROUP_READ: | |
3162 | - value = S_IRGRP; | |
3163 | - break; | |
3164 | - case CCS_MODE_GROUP_WRITE: | |
3165 | - value = S_IWGRP; | |
3166 | - break; | |
3167 | - case CCS_MODE_GROUP_EXECUTE: | |
3168 | - value = S_IXGRP; | |
3169 | - break; | |
3170 | - case CCS_MODE_OTHERS_READ: | |
3171 | - value = S_IROTH; | |
3172 | - break; | |
3173 | - case CCS_MODE_OTHERS_WRITE: | |
3174 | - value = S_IWOTH; | |
3175 | - break; | |
3176 | - case CCS_MODE_OTHERS_EXECUTE: | |
3177 | - value = S_IXOTH; | |
3178 | - break; | |
3179 | - default: | |
3180 | - goto not_bitop; | |
3181 | - } | |
3182 | - arg->type = CCS_ARG_TYPE_BITOP; | |
3183 | - arg->value[0] = value; | |
3184 | - return true; | |
3185 | -not_bitop: | |
3186 | - arg->type = CCS_ARG_TYPE_NUMBER; | |
3187 | - if (!r->obj.path[0].dentry && !r->obj.path[1].dentry) | |
3188 | - return false; | |
3189 | - ccs_get_attributes(r); | |
3190 | - value = (cmd - CCS_PATH_ATTRIBUTE_START) >> 4; | |
3191 | - if (value > 3) | |
3192 | - return false; | |
3193 | - stat = &r->obj.stat[value]; | |
3194 | - if (!stat) | |
3195 | - return false; | |
3196 | - switch ((cmd - CCS_PATH_ATTRIBUTE_START) & 0xF) { | |
3197 | - case CCS_PATH_ATTRIBUTE_UID: | |
3198 | - value = stat->uid; | |
3199 | - break; | |
3200 | - case CCS_PATH_ATTRIBUTE_GID: | |
3201 | - value = stat->gid; | |
3202 | - break; | |
3203 | - case CCS_PATH_ATTRIBUTE_INO: | |
3204 | - value = stat->ino; | |
3205 | - break; | |
3206 | - case CCS_PATH_ATTRIBUTE_MAJOR: | |
3207 | - value = MAJOR(stat->dev); | |
3208 | - break; | |
3209 | - case CCS_PATH_ATTRIBUTE_MINOR: | |
3210 | - value = MINOR(stat->dev); | |
3211 | - break; | |
3212 | - case CCS_PATH_ATTRIBUTE_TYPE: | |
3213 | - value = stat->mode & S_IFMT; | |
3214 | - break; | |
3215 | - case CCS_PATH_ATTRIBUTE_DEV_MAJOR: | |
3216 | - value = MAJOR(stat->rdev); | |
3217 | - break; | |
3218 | - case CCS_PATH_ATTRIBUTE_DEV_MINOR: | |
3219 | - value = MINOR(stat->rdev); | |
3220 | - break; | |
3221 | - case CCS_PATH_ATTRIBUTE_PERM: | |
3222 | - value = stat->mode & S_IALLUGO; | |
3223 | - break; | |
3224 | - case CCS_PATH_ATTRIBUTE_FSMAGIC: | |
3225 | - value = stat->fsmagic; | |
3226 | - break; | |
3227 | - default: | |
3228 | - return false; | |
3229 | - } | |
3230 | - arg->value[0] = value; | |
3231 | - arg->value[1] = value; | |
3232 | - return true; | |
3233 | -} | |
3234 | - | |
3235 | -/** | |
3236 | - * ccs_condition - Check condition part. | |
3237 | - * | |
3238 | - * @r: Pointer to "struct ccs_request_info". | |
3239 | - * @cond: Pointer to "struct ccs_condition". Maybe NULL. | |
3240 | - * | |
3241 | - * Returns true on success, false otherwise. | |
3242 | - * | |
3243 | - * Caller holds ccs_read_lock(). | |
3244 | - */ | |
3245 | -static bool ccs_condition(struct ccs_request_info *r, | |
3246 | - const struct ccs_condition *cond) | |
3247 | -{ | |
3248 | - const union ccs_condition_element *condp; | |
3249 | - if (!cond) | |
3250 | - return true; | |
3251 | - condp = (typeof(condp)) (cond + 1); | |
3252 | - while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
3253 | - struct ccs_cond_arg left; | |
3254 | - struct ccs_cond_arg right; | |
3255 | - const enum ccs_conditions_index left_op = condp->left; | |
3256 | - const enum ccs_conditions_index right_op = condp->right; | |
3257 | - const bool match = !condp->is_not; | |
3258 | - condp++; | |
3259 | - if (!ccs_cond2arg(&left, left_op, &condp, r) || | |
3260 | - !ccs_cond2arg(&right, right_op, &condp, r)) { | |
3261 | - /* | |
3262 | - * Something wrong (e.g. out of memory or invalid | |
3263 | - * argument) occured. We can't check permission. | |
3264 | - */ | |
3265 | - return false; | |
3266 | - } | |
3267 | - if (left.type == CCS_ARG_TYPE_NUMBER) { | |
3268 | - if (left_op == CCS_ARGV_ENTRY) { | |
3269 | - if (!r->bprm) | |
3270 | - return false; | |
3271 | - else if (right.type == CCS_ARG_TYPE_NAME) | |
3272 | - right.group = NULL; | |
3273 | - else if (right.type == CCS_ARG_TYPE_GROUP) | |
3274 | - right.name = NULL; | |
3275 | - else | |
3276 | - return false; | |
3277 | - if (ccs_check_argv(r, left.value[0], | |
3278 | - right.group, right.name, | |
3279 | - match)) | |
3280 | - continue; | |
3281 | - return false; | |
3282 | - } | |
3283 | - if (right.type == CCS_ARG_TYPE_NUMBER) { | |
3284 | - if ((left.value[0] <= right.value[1] && | |
3285 | - left.value[1] >= right.value[0]) == match) | |
3286 | - continue; | |
3287 | - return false; | |
3288 | - } | |
3289 | - if (right.type == CCS_ARG_TYPE_GROUP) { | |
3290 | - if (ccs_number_matches_group | |
3291 | - (left.value[0], left.value[1], right.group) | |
3292 | - == match) | |
3293 | - continue; | |
3294 | - return false; | |
3295 | - } | |
3296 | - if (right.type == CCS_ARG_TYPE_BITOP) { | |
3297 | - if (!(left.value[0] & right.value[0]) == | |
3298 | - !match) | |
3299 | - continue; | |
3300 | - return false; | |
3301 | - } | |
3302 | - return false; | |
3303 | - } | |
3304 | - if (left.type == CCS_ARG_TYPE_NAME) { | |
3305 | - if (right.type == CCS_ARG_TYPE_NAME) | |
3306 | - right.group = NULL; | |
3307 | - else if (right.type == CCS_ARG_TYPE_GROUP) | |
3308 | - right.name = NULL; | |
3309 | - else | |
3310 | - return false; | |
3311 | - if (left_op == CCS_ENVP_ENTRY) { | |
3312 | - if (r->bprm && ccs_check_envp | |
3313 | - (r, left.name, right.group, right.name, | |
3314 | - match)) | |
3315 | - continue; | |
3316 | - } else if (ccs_path_matches_group_or_pattern | |
3317 | - (left.name, right.group, right.name, match)) | |
3318 | - continue; | |
3319 | - return false; | |
3320 | - } | |
3321 | - if (left.type != CCS_ARG_TYPE_NONE) | |
3322 | - return false; | |
3323 | - /* Check IPv4 or IPv6 address expressions. */ | |
3324 | - if (left_op == CCS_COND_IPARG) { | |
3325 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
3326 | - if (right.type == CCS_ARG_TYPE_GROUP) { | |
3327 | - if (ccs_ip_matches_group | |
3328 | - (r->param.is_ipv6, r->param.ip, | |
3329 | - right.group) == match) | |
3330 | - continue; | |
3331 | - } else if (right.type == CCS_ARG_TYPE_IPV6ADDR) { | |
3332 | - if (r->param.is_ipv6 && | |
3333 | - (memcmp(r->param.ip, &right.ip[0], | |
3334 | - 16) >= 0 && | |
3335 | - memcmp(r->param.ip, &right.ip[1], | |
3336 | - 16) <= 0) == match) | |
3337 | - continue; | |
3338 | - } else if (right.type == CCS_ARG_TYPE_IPV4ADDR) { | |
3339 | - if (!r->param.is_ipv6 && | |
3340 | - (memcmp(r->param.ip, &right.ip[0], | |
3341 | - 4) >= 0 && | |
3342 | - memcmp(r->param.ip, &right.ip[1], | |
3343 | - 4) <= 0) == match) | |
3344 | - continue; | |
3345 | - } | |
3346 | -#endif | |
3347 | - return false; | |
3348 | - } | |
3349 | - if (left_op == CCS_HANDLER_PATH) { | |
3350 | - r->handler_path_candidate = right.name; | |
3351 | - continue; | |
3352 | - } | |
3353 | - if (left_op == CCS_TRANSIT_DOMAIN) { | |
3354 | - r->transition_candidate = right.name; | |
3355 | - continue; | |
3356 | - } | |
3357 | - return false; | |
3358 | - } | |
3359 | - return true; | |
3360 | -} | |
3361 | - | |
3362 | -/** | |
3363 | - * ccs_check_auto_domain_transition - Check "auto_domain_transition" entry. | |
3364 | - * | |
3365 | - * Returns nothing. | |
3366 | - * | |
3367 | - * If "auto_domain_transition" keyword was specified and transition to that | |
3368 | - * domain failed, the current thread will be killed by SIGKILL. | |
3369 | - */ | |
3370 | -static void ccs_check_auto_domain_transition(void) | |
3371 | -{ | |
3372 | -#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
3373 | - struct ccs_request_info r = { }; | |
3374 | - const int idx = ccs_read_lock(); | |
3375 | - r.type = CCS_MAC_AUTO_DOMAIN_TRANSITION; | |
3376 | - ccs_check_acl(&r, true); | |
3377 | - if (r.result != CCS_MATCHING_ALLOWED || | |
3378 | - ccs_transit_domain(r.transition->name)) | |
3379 | - goto done; | |
3380 | - printk(KERN_WARNING "ERROR: Unable to transit to '%s' domain.\n", | |
3381 | - r.transition->name); | |
3382 | - force_sig(SIGKILL, current); | |
3383 | -done: | |
3384 | - ccs_read_unlock(idx); | |
3385 | -#endif | |
3386 | -} | |
3387 | - | |
3388 | -/** | |
3389 | - * ccs_byte_range - Check whether the string is a \ooo style octal value. | |
3390 | - * | |
3391 | - * @str: Pointer to the string. | |
3392 | - * | |
3393 | - * Returns true if @str is a \ooo style octal value, false otherwise. | |
3394 | - */ | |
3395 | -static bool ccs_byte_range(const char *str) | |
3396 | -{ | |
3397 | - return *str >= '0' && *str++ <= '3' && | |
3398 | - *str >= '0' && *str++ <= '7' && | |
3399 | - *str >= '0' && *str <= '7'; | |
3400 | -} | |
3401 | - | |
3402 | -/** | |
3403 | - * ccs_decimal - Check whether the character is a decimal character. | |
3404 | - * | |
3405 | - * @c: The character to check. | |
3406 | - * | |
3407 | - * Returns true if @c is a decimal character, false otherwise. | |
3408 | - */ | |
3409 | -static bool ccs_decimal(const char c) | |
3410 | -{ | |
3411 | - return c >= '0' && c <= '9'; | |
3412 | -} | |
3413 | - | |
3414 | -/** | |
3415 | - * ccs_hexadecimal - Check whether the character is a hexadecimal character. | |
3416 | - * | |
3417 | - * @c: The character to check. | |
3418 | - * | |
3419 | - * Returns true if @c is a hexadecimal character, false otherwise. | |
3420 | - */ | |
3421 | -static bool ccs_hexadecimal(const char c) | |
3422 | -{ | |
3423 | - return (c >= '0' && c <= '9') || | |
3424 | - (c >= 'A' && c <= 'F') || | |
3425 | - (c >= 'a' && c <= 'f'); | |
3426 | -} | |
3427 | - | |
3428 | -/** | |
3429 | - * ccs_alphabet_char - Check whether the character is an alphabet. | |
3430 | - * | |
3431 | - * @c: The character to check. | |
3432 | - * | |
3433 | - * Returns true if @c is an alphabet character, false otherwise. | |
3434 | - */ | |
3435 | -static bool ccs_alphabet_char(const char c) | |
3436 | -{ | |
3437 | - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | |
3438 | -} | |
3439 | - | |
3440 | -/** | |
3441 | - * ccs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | |
3442 | - * | |
3443 | - * @filename: The start of string to check. | |
3444 | - * @filename_end: The end of string to check. | |
3445 | - * @pattern: The start of pattern to compare. | |
3446 | - * @pattern_end: The end of pattern to compare. | |
3447 | - * | |
3448 | - * Returns true if @filename matches @pattern, false otherwise. | |
3449 | - */ | |
3450 | -static bool ccs_file_matches_pattern2(const char *filename, | |
3451 | - const char *filename_end, | |
3452 | - const char *pattern, | |
3453 | - const char *pattern_end) | |
3454 | -{ | |
3455 | - while (filename < filename_end && pattern < pattern_end) { | |
3456 | - char c; | |
3457 | - if (*pattern != '\\') { | |
3458 | - if (*filename++ != *pattern++) | |
3459 | - return false; | |
3460 | - continue; | |
3461 | - } | |
3462 | - c = *filename; | |
3463 | - pattern++; | |
3464 | - switch (*pattern) { | |
3465 | - int i; | |
3466 | - int j; | |
3467 | - case '?': | |
3468 | - if (c == '/') { | |
3469 | - return false; | |
3470 | - } else if (c == '\\') { | |
3471 | - if (ccs_byte_range(filename + 1)) | |
3472 | - filename += 3; | |
3473 | - else | |
3474 | - return false; | |
3475 | - } | |
3476 | - break; | |
3477 | - case '+': | |
3478 | - if (!ccs_decimal(c)) | |
3479 | - return false; | |
3480 | - break; | |
3481 | - case 'x': | |
3482 | - if (!ccs_hexadecimal(c)) | |
3483 | - return false; | |
3484 | - break; | |
3485 | - case 'a': | |
3486 | - if (!ccs_alphabet_char(c)) | |
3487 | - return false; | |
3488 | - break; | |
3489 | - case '0': | |
3490 | - case '1': | |
3491 | - case '2': | |
3492 | - case '3': | |
3493 | - if (c == '\\' && ccs_byte_range(filename + 1) | |
3494 | - && !strncmp(filename + 1, pattern, 3)) { | |
3495 | - filename += 3; | |
3496 | - pattern += 2; | |
3497 | - break; | |
3498 | - } | |
3499 | - return false; /* Not matched. */ | |
3500 | - case '*': | |
3501 | - case '@': | |
3502 | - for (i = 0; i <= filename_end - filename; i++) { | |
3503 | - if (ccs_file_matches_pattern2(filename + i, | |
3504 | - filename_end, | |
3505 | - pattern + 1, | |
3506 | - pattern_end)) | |
3507 | - return true; | |
3508 | - c = filename[i]; | |
3509 | - if (c == '.' && *pattern == '@') | |
3510 | - break; | |
3511 | - if (c != '\\') | |
3512 | - continue; | |
3513 | - if (ccs_byte_range(filename + i + 1)) | |
3514 | - i += 3; | |
3515 | - else | |
3516 | - break; /* Bad pattern. */ | |
3517 | - } | |
3518 | - return false; /* Not matched. */ | |
3519 | - default: | |
3520 | - j = 0; | |
3521 | - c = *pattern; | |
3522 | - if (c == '$') { | |
3523 | - while (ccs_decimal(filename[j])) | |
3524 | - j++; | |
3525 | - } else if (c == 'X') { | |
3526 | - while (ccs_hexadecimal(filename[j])) | |
3527 | - j++; | |
3528 | - } else if (c == 'A') { | |
3529 | - while (ccs_alphabet_char(filename[j])) | |
3530 | - j++; | |
3531 | - } | |
3532 | - for (i = 1; i <= j; i++) { | |
3533 | - if (ccs_file_matches_pattern2(filename + i, | |
3534 | - filename_end, | |
3535 | - pattern + 1, | |
3536 | - pattern_end)) | |
3537 | - return true; | |
3538 | - } | |
3539 | - return false; /* Not matched or bad pattern. */ | |
3540 | - } | |
3541 | - filename++; | |
3542 | - pattern++; | |
3543 | - } | |
3544 | - /* Ignore trailing "\*" and "\@" in @pattern. */ | |
3545 | - while (*pattern == '\\' && | |
3546 | - (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | |
3547 | - pattern += 2; | |
3548 | - return filename == filename_end && pattern == pattern_end; | |
3549 | -} | |
3550 | - | |
3551 | -/** | |
3552 | - * ccs_file_matches_pattern - Pattern matching without '/' character. | |
3553 | - * | |
3554 | - * @filename: The start of string to check. | |
3555 | - * @filename_end: The end of string to check. | |
3556 | - * @pattern: The start of pattern to compare. | |
3557 | - * @pattern_end: The end of pattern to compare. | |
3558 | - * | |
3559 | - * Returns true if @filename matches @pattern, false otherwise. | |
3560 | - */ | |
3561 | -static bool ccs_file_matches_pattern(const char *filename, | |
3562 | - const char *filename_end, | |
3563 | - const char *pattern, | |
3564 | - const char *pattern_end) | |
3565 | -{ | |
3566 | - const char *pattern_start = pattern; | |
3567 | - bool first = true; | |
3568 | - bool result; | |
3569 | - while (pattern < pattern_end - 1) { | |
3570 | - /* Split at "\-" pattern. */ | |
3571 | - if (*pattern++ != '\\' || *pattern++ != '-') | |
3572 | - continue; | |
3573 | - result = ccs_file_matches_pattern2(filename, filename_end, | |
3574 | - pattern_start, pattern - 2); | |
3575 | - if (first) | |
3576 | - result = !result; | |
3577 | - if (result) | |
3578 | - return false; | |
3579 | - first = false; | |
3580 | - pattern_start = pattern; | |
3581 | - } | |
3582 | - result = ccs_file_matches_pattern2(filename, filename_end, | |
3583 | - pattern_start, pattern_end); | |
3584 | - return first ? result : !result; | |
3585 | -} | |
3586 | - | |
3587 | -/** | |
3588 | - * ccs_path_matches_pattern2 - Do pathname pattern matching. | |
3589 | - * | |
3590 | - * @f: The start of string to check. | |
3591 | - * @p: The start of pattern to compare. | |
3592 | - * | |
3593 | - * Returns true if @f matches @p, false otherwise. | |
3594 | - */ | |
3595 | -static bool ccs_path_matches_pattern2(const char *f, const char *p) | |
3596 | -{ | |
3597 | - const char *f_delimiter; | |
3598 | - const char *p_delimiter; | |
3599 | - char recursive_end; | |
3600 | - while (*f && *p) { | |
3601 | - f_delimiter = strchr(f, '/'); | |
3602 | - if (!f_delimiter) | |
3603 | - f_delimiter = f + strlen(f); | |
3604 | - p_delimiter = strchr(p, '/'); | |
3605 | - if (!p_delimiter) | |
3606 | - p_delimiter = p + strlen(p); | |
3607 | - if (*p == '\\') { | |
3608 | - if (*(p + 1) == '{') { | |
3609 | - recursive_end = '}'; | |
3610 | - goto recursive; | |
3611 | - } | |
3612 | - if (*(p + 1) == '(') { | |
3613 | - recursive_end = ')'; | |
3614 | - goto recursive; | |
3615 | - } | |
3616 | - } | |
3617 | - if (!ccs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) | |
3618 | - return false; | |
3619 | - f = f_delimiter; | |
3620 | - if (*f) | |
3621 | - f++; | |
3622 | - p = p_delimiter; | |
3623 | - if (*p) | |
3624 | - p++; | |
3625 | - } | |
3626 | - /* Ignore trailing "\*" and "\@" in @pattern. */ | |
3627 | - while (*p == '\\' && | |
3628 | - (*(p + 1) == '*' || *(p + 1) == '@')) | |
3629 | - p += 2; | |
3630 | - return !*f && !*p; | |
3631 | -recursive: | |
3632 | - /* | |
3633 | - * The "\{" or "\(" pattern is permitted only after '/' character. | |
3634 | - * This guarantees that below "*(p - 1)" is safe. | |
3635 | - * Also, the "\}" or "\)" pattern is permitted only before '/' | |
3636 | - * character so that "\{" + "\}" or "\(" + "\)" pair will not break | |
3637 | - * the "\-" operator. | |
3638 | - */ | |
3639 | - if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || | |
3640 | - *(p_delimiter - 1) != recursive_end || *(p_delimiter - 2) != '\\') | |
3641 | - return false; /* Bad pattern. */ | |
3642 | - if (recursive_end == ')') { | |
3643 | - /* Check zero repetition. */ | |
3644 | - if (ccs_path_matches_pattern2(f, p_delimiter + 1)) | |
3645 | - return true; | |
3646 | - /* Fall back to one or more repetition. */ | |
3647 | - } | |
3648 | - do { | |
3649 | - /* Compare current component with pattern. */ | |
3650 | - if (!ccs_file_matches_pattern(f, f_delimiter, p + 2, | |
3651 | - p_delimiter - 2)) | |
3652 | - break; | |
3653 | - /* Proceed to next component. */ | |
3654 | - f = f_delimiter; | |
3655 | - if (!*f) | |
3656 | - break; | |
3657 | - f++; | |
3658 | - /* Continue comparison. */ | |
3659 | - if (ccs_path_matches_pattern2(f, p_delimiter + 1)) | |
3660 | - return true; | |
3661 | - f_delimiter = strchr(f, '/'); | |
3662 | - } while (f_delimiter); | |
3663 | - return false; /* Not matched. */ | |
3664 | -} | |
3665 | - | |
3666 | -/** | |
3667 | - * ccs_path_matches_pattern - Check whether the given filename matches the given pattern. | |
3668 | - * | |
3669 | - * @filename: The filename to check. | |
3670 | - * @pattern: The pattern to compare. | |
3671 | - * | |
3672 | - * Returns true if matches, false otherwise. | |
3673 | - * | |
3674 | - * The following patterns are available. | |
3675 | - * \ooo Octal representation of a byte. | |
3676 | - * \* Zero or more repetitions of characters other than '/'. | |
3677 | - * \@ Zero or more repetitions of characters other than '/' or '.'. | |
3678 | - * \? 1 byte character other than '/'. | |
3679 | - * \$ One or more repetitions of decimal digits. | |
3680 | - * \+ 1 decimal digit. | |
3681 | - * \X One or more repetitions of hexadecimal digits. | |
3682 | - * \x 1 hexadecimal digit. | |
3683 | - * \A One or more repetitions of alphabet characters. | |
3684 | - * \a 1 alphabet character. | |
3685 | - * | |
3686 | - * \- Subtraction operator. | |
3687 | - * | |
3688 | - * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | |
3689 | - * /dir/dir/dir/ ). | |
3690 | - * | |
3691 | - * /\(dir\)/ '/' + 'Zero or more repetitions of dir/' (e.g. / /dir/ | |
3692 | - * /dir/dir/ ). | |
3693 | - */ | |
3694 | -static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | |
3695 | - const struct ccs_path_info *pattern) | |
3696 | -{ | |
3697 | - const char *f = filename->name; | |
3698 | - const char *p = pattern->name; | |
3699 | - const int len = pattern->const_len; | |
3700 | - /* If @pattern doesn't contain pattern, I can use strcmp(). */ | |
3701 | - if (len == pattern->total_len) | |
3702 | - return !ccs_pathcmp(filename, pattern); | |
3703 | - /* Compare the initial length without patterns. */ | |
3704 | - if (strncmp(f, p, len)) | |
3705 | - return false; | |
3706 | - f += len; | |
3707 | - p += len; | |
3708 | - /* Compare the last component first. */ | |
3709 | - { | |
3710 | - const char *f2 = strrchr(f, '/'); | |
3711 | - const char *p2 = strrchr(p, '/'); | |
3712 | - if (!f2++) | |
3713 | - f2 = f; | |
3714 | - if (!p2++) | |
3715 | - p2 = p; | |
3716 | - if (!ccs_file_matches_pattern(f2, filename->name | |
3717 | - + filename->total_len, | |
3718 | - p2, pattern->name | |
3719 | - + pattern->total_len)) | |
3720 | - return false; | |
3721 | - } | |
3722 | - return ccs_path_matches_pattern2(f, p); | |
3723 | -} | |
3724 | - | |
3725 | -/** | |
3726 | - * ccs_clear_request_info - Release memory allocated during permission check. | |
3727 | - * | |
3728 | - * @r: Pointer to "struct ccs_request_info". | |
3729 | - * | |
3730 | - * Returns nothing. | |
3731 | - */ | |
3732 | -static void ccs_clear_request_info(struct ccs_request_info *r) | |
3733 | -{ | |
3734 | - u8 i; | |
3735 | - /* | |
3736 | - * r->obj.pathname[0] (which is referenced by r->obj.s[0]) and | |
3737 | - * r->obj.pathname[1] (which is referenced by r->obj.s[1]) may contain | |
3738 | - * pathnames allocated using ccs_populate_patharg() or ccs_mount_acl(). | |
3739 | - * Their callers do not allocate memory until pathnames becomes needed | |
3740 | - * for checking condition or auditing requests. | |
3741 | - * | |
3742 | - * r->obj.s[2] and r->obj.s[3] are used by | |
3743 | - * ccs_mount_acl()/ccs_env_perm() and are allocated/released by their | |
3744 | - * callers. | |
3745 | - */ | |
3746 | - for (i = 0; i < 2; i++) { | |
3747 | - kfree(r->obj.pathname[i].name); | |
3748 | - r->obj.pathname[i].name = NULL; | |
3749 | - } | |
3750 | - kfree(r->exename.name); | |
3751 | - r->exename.name = NULL; | |
3752 | -} |
@@ -1,179 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/memory.c | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#include "internal.h" | |
10 | - | |
11 | -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
12 | - | |
13 | -/***** SECTION1: Constants definition *****/ | |
14 | - | |
15 | -/***** SECTION2: Structure definition *****/ | |
16 | - | |
17 | -/***** SECTION3: Prototype definition section *****/ | |
18 | - | |
19 | -struct ccs_security *ccs_find_task_security(const struct task_struct *task); | |
20 | -void __init ccs_mm_init(void); | |
21 | - | |
22 | -static int __ccs_alloc_task_security(const struct task_struct *task); | |
23 | -static void __ccs_free_task_security(const struct task_struct *task); | |
24 | -static void ccs_add_task_security(struct ccs_security *ptr, | |
25 | - struct list_head *list); | |
26 | -static void ccs_rcu_free(struct rcu_head *rcu); | |
27 | - | |
28 | -/***** SECTION4: Standalone functions section *****/ | |
29 | - | |
30 | -/***** SECTION5: Variables definition section *****/ | |
31 | - | |
32 | -/* Dummy security context for avoiding NULL pointer dereference. */ | |
33 | -static struct ccs_security ccs_oom_security = { | |
34 | - .ccs_domain_info = &ccs_kernel_domain | |
35 | -}; | |
36 | - | |
37 | -/* Dummy security context for avoiding NULL pointer dereference. */ | |
38 | -static struct ccs_security ccs_default_security = { | |
39 | - .ccs_domain_info = &ccs_kernel_domain | |
40 | -}; | |
41 | - | |
42 | -/* List of "struct ccs_security". */ | |
43 | -struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | |
44 | -/* Lock for protecting ccs_task_security_list[]. */ | |
45 | -static DEFINE_SPINLOCK(ccs_task_security_list_lock); | |
46 | - | |
47 | -/***** SECTION6: Dependent functions section *****/ | |
48 | - | |
49 | -/** | |
50 | - * ccs_add_task_security - Add "struct ccs_security" to list. | |
51 | - * | |
52 | - * @ptr: Pointer to "struct ccs_security". | |
53 | - * @list: Pointer to "struct list_head". | |
54 | - * | |
55 | - * Returns nothing. | |
56 | - */ | |
57 | -static void ccs_add_task_security(struct ccs_security *ptr, | |
58 | - struct list_head *list) | |
59 | -{ | |
60 | - unsigned long flags; | |
61 | - spin_lock_irqsave(&ccs_task_security_list_lock, flags); | |
62 | - list_add_rcu(&ptr->list, list); | |
63 | - spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | |
64 | -} | |
65 | - | |
66 | -/** | |
67 | - * __ccs_alloc_task_security - Allocate memory for new tasks. | |
68 | - * | |
69 | - * @task: Pointer to "struct task_struct". | |
70 | - * | |
71 | - * Returns 0 on success, negative value otherwise. | |
72 | - */ | |
73 | -static int __ccs_alloc_task_security(const struct task_struct *task) | |
74 | -{ | |
75 | - struct ccs_security *old_security = ccs_current_security(); | |
76 | - struct ccs_security *new_security = kzalloc(sizeof(*new_security), | |
77 | - GFP_KERNEL); | |
78 | - struct list_head *list = &ccs_task_security_list | |
79 | - [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | |
80 | - if (!new_security) | |
81 | - return -ENOMEM; | |
82 | - *new_security = *old_security; | |
83 | - new_security->task = task; | |
84 | - ccs_add_task_security(new_security, list); | |
85 | - return 0; | |
86 | -} | |
87 | - | |
88 | -/** | |
89 | - * ccs_find_task_security - Find "struct ccs_security" for given task. | |
90 | - * | |
91 | - * @task: Pointer to "struct task_struct". | |
92 | - * | |
93 | - * Returns pointer to "struct ccs_security" on success, &ccs_oom_security on | |
94 | - * out of memory, &ccs_default_security otherwise. | |
95 | - * | |
96 | - * If @task is current thread and "struct ccs_security" for current thread was | |
97 | - * not found, I try to allocate it. But if allocation failed, current thread | |
98 | - * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL | |
99 | - * won't work. | |
100 | - */ | |
101 | -struct ccs_security *ccs_find_task_security(const struct task_struct *task) | |
102 | -{ | |
103 | - struct ccs_security *ptr; | |
104 | - struct list_head *list = &ccs_task_security_list | |
105 | - [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | |
106 | - /* Make sure INIT_LIST_HEAD() in ccs_mm_init() takes effect. */ | |
107 | - while (!list->next); | |
108 | - rcu_read_lock(); | |
109 | - list_for_each_entry_rcu(ptr, list, list) { | |
110 | - if (ptr->task != task) | |
111 | - continue; | |
112 | - rcu_read_unlock(); | |
113 | - return ptr; | |
114 | - } | |
115 | - rcu_read_unlock(); | |
116 | - if (task != current) | |
117 | - return &ccs_default_security; | |
118 | - /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ | |
119 | - ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); | |
120 | - if (!ptr) { | |
121 | - printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", | |
122 | - task->pid); | |
123 | - send_sig(SIGKILL, current, 0); | |
124 | - return &ccs_oom_security; | |
125 | - } | |
126 | - *ptr = ccs_default_security; | |
127 | - ptr->task = task; | |
128 | - ccs_add_task_security(ptr, list); | |
129 | - return ptr; | |
130 | -} | |
131 | - | |
132 | -/** | |
133 | - * ccs_rcu_free - RCU callback for releasing "struct ccs_security". | |
134 | - * | |
135 | - * @rcu: Pointer to "struct rcu_head". | |
136 | - * | |
137 | - * Returns nothing. | |
138 | - */ | |
139 | -static void ccs_rcu_free(struct rcu_head *rcu) | |
140 | -{ | |
141 | - struct ccs_security *ptr = container_of(rcu, typeof(*ptr), rcu); | |
142 | - kfree(ptr); | |
143 | -} | |
144 | - | |
145 | -/** | |
146 | - * __ccs_free_task_security - Release memory associated with "struct task_struct". | |
147 | - * | |
148 | - * @task: Pointer to "struct task_struct". | |
149 | - * | |
150 | - * Returns nothing. | |
151 | - */ | |
152 | -static void __ccs_free_task_security(const struct task_struct *task) | |
153 | -{ | |
154 | - unsigned long flags; | |
155 | - struct ccs_security *ptr = ccs_find_task_security(task); | |
156 | - if (ptr == &ccs_default_security || ptr == &ccs_oom_security) | |
157 | - return; | |
158 | - spin_lock_irqsave(&ccs_task_security_list_lock, flags); | |
159 | - list_del_rcu(&ptr->list); | |
160 | - spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | |
161 | - call_rcu(&ptr->rcu, ccs_rcu_free); | |
162 | -} | |
163 | - | |
164 | -/** | |
165 | - * ccs_mm_init - Initialize mm related code. | |
166 | - * | |
167 | - * Returns nothing. | |
168 | - */ | |
169 | -void __init ccs_mm_init(void) | |
170 | -{ | |
171 | - int idx; | |
172 | - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) | |
173 | - INIT_LIST_HEAD(&ccs_task_security_list[idx]); | |
174 | - smp_wmb(); /* Avoid out of order execution. */ | |
175 | - ccsecurity_ops.alloc_task_security = __ccs_alloc_task_security; | |
176 | - ccsecurity_ops.free_task_security = __ccs_free_task_security; | |
177 | -} | |
178 | - | |
179 | -#endif |
@@ -1,581 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/gc.c | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#include "internal.h" | |
10 | - | |
11 | -/***** SECTION1: Constants definition *****/ | |
12 | - | |
13 | -/* For compatibility with older kernels. */ | |
14 | -#ifndef for_each_process | |
15 | -#define for_each_process for_each_task | |
16 | -#endif | |
17 | - | |
18 | -/* The list for "struct ccs_io_buffer". */ | |
19 | -static LIST_HEAD(ccs_io_buffer_list); | |
20 | -/* Lock for protecting ccs_io_buffer_list. */ | |
21 | -static DEFINE_SPINLOCK(ccs_io_buffer_list_lock); | |
22 | - | |
23 | -/***** SECTION2: Structure definition *****/ | |
24 | - | |
25 | -/***** SECTION3: Prototype definition section *****/ | |
26 | - | |
27 | -static bool ccs_domain_used_by_task(struct ccs_domain_info *domain); | |
28 | -static bool ccs_name_used_by_io_buffer(const char *string, const size_t size); | |
29 | -static bool ccs_struct_used_by_io_buffer(const struct list_head *element); | |
30 | -static int ccs_gc_thread(void *unused); | |
31 | -static void ccs_collect_acl(struct list_head *list); | |
32 | -static void ccs_collect_entry(void); | |
33 | -static void ccs_collect_member(const enum ccs_policy_id id, | |
34 | - struct list_head *member_list); | |
35 | -static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type); | |
36 | -static void ccs_try_to_gc(const enum ccs_policy_id type, | |
37 | - struct list_head *element); | |
38 | - | |
39 | -/***** SECTION4: Standalone functions section *****/ | |
40 | - | |
41 | -/***** SECTION5: Variables definition section *****/ | |
42 | - | |
43 | -/* | |
44 | - * Lock for syscall users. | |
45 | - * | |
46 | - * This lock is held for only protecting single SRCU section. | |
47 | - */ | |
48 | -struct srcu_struct ccs_ss; | |
49 | - | |
50 | -/***** SECTION6: Dependent functions section *****/ | |
51 | - | |
52 | -/** | |
53 | - * ccs_memory_free - Free memory for elements. | |
54 | - * | |
55 | - * @ptr: Pointer to allocated memory. | |
56 | - * @type: One of values in "enum ccs_policy_id". | |
57 | - * | |
58 | - * Returns nothing. | |
59 | - * | |
60 | - * Caller holds ccs_policy_lock mutex. | |
61 | - */ | |
62 | -static void ccs_memory_free(const void *ptr, const enum ccs_policy_id type) | |
63 | -{ | |
64 | - /* Size of an element. */ | |
65 | - static const u8 e[CCS_MAX_POLICY] = { | |
66 | - [CCS_ID_GROUP] = sizeof(struct ccs_group), | |
67 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
68 | - [CCS_ID_IP_GROUP] = sizeof(struct ccs_ip_group), | |
69 | -#endif | |
70 | - [CCS_ID_STRING_GROUP] = sizeof(struct ccs_string_group), | |
71 | - [CCS_ID_NUMBER_GROUP] = sizeof(struct ccs_number_group), | |
72 | - /* [CCS_ID_CONDITION] = "struct ccs_condition"->size, */ | |
73 | - /* [CCS_ID_NAME] = "struct ccs_name"->size, */ | |
74 | - /* [CCS_ID_ACL] = sizeof(struct ccs_acl_info), */ | |
75 | - [CCS_ID_DOMAIN] = sizeof(struct ccs_domain_info), | |
76 | - }; | |
77 | - size_t size; | |
78 | - if (type == CCS_ID_ACL) | |
79 | - size = sizeof(struct ccs_acl_info); | |
80 | - else if (type == CCS_ID_NAME) | |
81 | - size = container_of(ptr, typeof(struct ccs_name), | |
82 | - head.list)->size; | |
83 | - else if (type == CCS_ID_CONDITION) | |
84 | - size = container_of(ptr, typeof(struct ccs_condition), | |
85 | - head.list)->size; | |
86 | - else | |
87 | - size = e[type]; | |
88 | - ccs_memory_used[CCS_MEMORY_POLICY] -= ccs_round2(size); | |
89 | - kfree(ptr); | |
90 | -} | |
91 | - | |
92 | -/** | |
93 | - * ccs_struct_used_by_io_buffer - Check whether the list element is used by /proc/ccs/ users or not. | |
94 | - * | |
95 | - * @element: Pointer to "struct list_head". | |
96 | - * | |
97 | - * Returns true if @element is used by /proc/ccs/ users, false otherwise. | |
98 | - */ | |
99 | -static bool ccs_struct_used_by_io_buffer(const struct list_head *element) | |
100 | -{ | |
101 | - struct ccs_io_buffer *head; | |
102 | - bool in_use = false; | |
103 | - spin_lock(&ccs_io_buffer_list_lock); | |
104 | - list_for_each_entry(head, &ccs_io_buffer_list, list) { | |
105 | - head->users++; | |
106 | - spin_unlock(&ccs_io_buffer_list_lock); | |
107 | - mutex_lock(&head->io_sem); | |
108 | - if (head->r.acl == element || head->r.subacl == element || | |
109 | - head->r.group == element || &head->w.acl->list == element) | |
110 | - in_use = true; | |
111 | - mutex_unlock(&head->io_sem); | |
112 | - spin_lock(&ccs_io_buffer_list_lock); | |
113 | - head->users--; | |
114 | - if (in_use) | |
115 | - break; | |
116 | - } | |
117 | - spin_unlock(&ccs_io_buffer_list_lock); | |
118 | - return in_use; | |
119 | -} | |
120 | - | |
121 | -/** | |
122 | - * ccs_name_used_by_io_buffer - Check whether the string is used by /proc/ccs/ users or not. | |
123 | - * | |
124 | - * @string: String to check. | |
125 | - * @size: Memory allocated for @string . | |
126 | - * | |
127 | - * Returns true if @string is used by /proc/ccs/ users, false otherwise. | |
128 | - */ | |
129 | -static bool ccs_name_used_by_io_buffer(const char *string, const size_t size) | |
130 | -{ | |
131 | - struct ccs_io_buffer *head; | |
132 | - bool in_use = false; | |
133 | - spin_lock(&ccs_io_buffer_list_lock); | |
134 | - list_for_each_entry(head, &ccs_io_buffer_list, list) { | |
135 | - int i; | |
136 | - head->users++; | |
137 | - spin_unlock(&ccs_io_buffer_list_lock); | |
138 | - mutex_lock(&head->io_sem); | |
139 | - for (i = 0; i < CCS_MAX_IO_READ_QUEUE; i++) { | |
140 | - const char *w = head->r.w[i]; | |
141 | - if (w < string || w > string + size) | |
142 | - continue; | |
143 | - in_use = true; | |
144 | - break; | |
145 | - } | |
146 | - mutex_unlock(&head->io_sem); | |
147 | - spin_lock(&ccs_io_buffer_list_lock); | |
148 | - head->users--; | |
149 | - if (in_use) | |
150 | - break; | |
151 | - } | |
152 | - spin_unlock(&ccs_io_buffer_list_lock); | |
153 | - return in_use; | |
154 | -} | |
155 | - | |
156 | -/** | |
157 | - * ccs_domain_used_by_task - Check whether the given pointer is referenced by a task. | |
158 | - * | |
159 | - * @domain: Pointer to "struct ccs_domain_info". | |
160 | - * | |
161 | - * Returns true if @domain is in use, false otherwise. | |
162 | - */ | |
163 | -static bool ccs_domain_used_by_task(struct ccs_domain_info *domain) | |
164 | -{ | |
165 | - bool in_use = false; | |
166 | - /* | |
167 | - * Don't delete this domain if somebody is doing execve(). | |
168 | - * | |
169 | - * Since ccs_finish_execve() first reverts ccs_domain_info and then | |
170 | - * updates ccs_flags, we need smp_rmb() to make sure that GC first | |
171 | - * checks ccs_flags and then checks ccs_domain_info. | |
172 | - */ | |
173 | -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
174 | - int idx; | |
175 | - rcu_read_lock(); | |
176 | - for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) { | |
177 | - struct ccs_security *ptr; | |
178 | - struct list_head *list = &ccs_task_security_list[idx]; | |
179 | - list_for_each_entry_rcu(ptr, list, list) { | |
180 | - if (!(ptr->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | |
181 | - smp_rmb(); /* Avoid out of order execution. */ | |
182 | - if (ptr->ccs_domain_info != domain) | |
183 | - continue; | |
184 | - } | |
185 | - in_use = true; | |
186 | - goto out; | |
187 | - } | |
188 | - } | |
189 | -out: | |
190 | - rcu_read_unlock(); | |
191 | -#else | |
192 | - struct task_struct *g; | |
193 | - struct task_struct *t; | |
194 | - ccs_tasklist_lock(); | |
195 | - do_each_thread(g, t) { | |
196 | - if (!(t->ccs_flags & CCS_TASK_IS_IN_EXECVE)) { | |
197 | - smp_rmb(); /* Avoid out of order execution. */ | |
198 | - if (t->ccs_domain_info != domain) | |
199 | - continue; | |
200 | - } | |
201 | - in_use = true; | |
202 | - goto out; | |
203 | - } while_each_thread(g, t); | |
204 | -out: | |
205 | - ccs_tasklist_unlock(); | |
206 | -#endif | |
207 | - return in_use; | |
208 | -} | |
209 | - | |
210 | -/** | |
211 | - * ccs_del_acl - Delete members in "struct ccs_acl_info". | |
212 | - * | |
213 | - * @element: Pointer to "struct list_head". | |
214 | - * | |
215 | - * Returns nothing. | |
216 | - */ | |
217 | -static inline void ccs_del_acl(struct list_head *element) | |
218 | -{ | |
219 | - struct ccs_acl_info *acl = container_of(element, typeof(*acl), list); | |
220 | - ccs_put_condition(acl->cond); | |
221 | -} | |
222 | - | |
223 | -/** | |
224 | - * ccs_del_domain - Delete members in "struct ccs_domain_info". | |
225 | - * | |
226 | - * @element: Pointer to "struct list_head". | |
227 | - * | |
228 | - * Returns nothing. | |
229 | - * | |
230 | - * Caller holds ccs_policy_lock mutex. | |
231 | - */ | |
232 | -static inline void ccs_del_domain(struct list_head *element) | |
233 | -{ | |
234 | - struct ccs_domain_info *domain = | |
235 | - container_of(element, typeof(*domain), list); | |
236 | - ccs_put_name(domain->domainname); | |
237 | -} | |
238 | - | |
239 | -/** | |
240 | - * ccs_del_string_group - Delete members in "struct ccs_string_group". | |
241 | - * | |
242 | - * @element: Pointer to "struct list_head". | |
243 | - * | |
244 | - * Returns nothing. | |
245 | - */ | |
246 | -static inline void ccs_del_string_group(struct list_head *element) | |
247 | -{ | |
248 | - struct ccs_string_group *member = | |
249 | - container_of(element, typeof(*member), head.list); | |
250 | - ccs_put_name(member->member_name); | |
251 | -} | |
252 | - | |
253 | -/** | |
254 | - * ccs_del_group - Delete "struct ccs_group". | |
255 | - * | |
256 | - * @element: Pointer to "struct list_head". | |
257 | - * | |
258 | - * Returns nothing. | |
259 | - */ | |
260 | -static inline void ccs_del_group(struct list_head *element) | |
261 | -{ | |
262 | - struct ccs_group *group = | |
263 | - container_of(element, typeof(*group), head.list); | |
264 | - ccs_put_name(group->group_name); | |
265 | -} | |
266 | - | |
267 | -/** | |
268 | - * ccs_del_condition - Delete members in "struct ccs_condition". | |
269 | - * | |
270 | - * @element: Pointer to "struct list_head". | |
271 | - * | |
272 | - * Returns nothing. | |
273 | - */ | |
274 | -void ccs_del_condition(struct list_head *element) | |
275 | -{ | |
276 | - struct ccs_condition *cond = container_of(element, typeof(*cond), | |
277 | - head.list); | |
278 | - const union ccs_condition_element *condp = (typeof(condp)) (cond + 1); | |
279 | - while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
280 | - const enum ccs_conditions_index left = condp->left; | |
281 | - const enum ccs_conditions_index right = condp->right; | |
282 | - condp++; | |
283 | - if (left == CCS_ARGV_ENTRY) | |
284 | - condp++; | |
285 | - else if (left == CCS_ENVP_ENTRY) { | |
286 | - ccs_put_name(condp->path); | |
287 | - condp++; | |
288 | - } | |
289 | - if (right == CCS_IMM_GROUP) { | |
290 | - ccs_put_group(condp->group); | |
291 | - condp++; | |
292 | - } else if (right == CCS_IMM_NAME_ENTRY) { | |
293 | - if (condp->path != &ccs_null_name) | |
294 | - ccs_put_name(condp->path); | |
295 | - condp++; | |
296 | - } else if (right == CCS_IMM_NUMBER_ENTRY1) | |
297 | - condp++; | |
298 | - else if (right == CCS_IMM_NUMBER_ENTRY2) | |
299 | - condp += 2; | |
300 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
301 | - else if (right == CCS_IMM_IPV6ADDR_ENTRY1) | |
302 | - condp = (void *) | |
303 | - (((u8 *) condp) + sizeof(struct in6_addr)); | |
304 | - else if (right == CCS_IMM_IPV6ADDR_ENTRY2) | |
305 | - condp = (void *) | |
306 | - (((u8 *) condp) + sizeof(struct in6_addr) * 2); | |
307 | -#endif | |
308 | - } | |
309 | -} | |
310 | - | |
311 | -/** | |
312 | - * ccs_try_to_gc - Try to kfree() an entry. | |
313 | - * | |
314 | - * @type: One of values in "enum ccs_policy_id". | |
315 | - * @element: Pointer to "struct list_head". | |
316 | - * | |
317 | - * Returns nothing. | |
318 | - * | |
319 | - * Caller holds ccs_policy_lock mutex. | |
320 | - */ | |
321 | -static void ccs_try_to_gc(const enum ccs_policy_id type, | |
322 | - struct list_head *element) | |
323 | -{ | |
324 | - /* | |
325 | - * __list_del_entry() guarantees that the list element became no longer | |
326 | - * reachable from the list which the element was originally on (e.g. | |
327 | - * ccs_domain_list). Also, synchronize_srcu() guarantees that the list | |
328 | - * element became no longer referenced by syscall users. | |
329 | - */ | |
330 | - __list_del_entry(element); | |
331 | - mutex_unlock(&ccs_policy_lock); | |
332 | - synchronize_srcu(&ccs_ss); | |
333 | - /* | |
334 | - * However, there are two users which may still be using the list | |
335 | - * element. We need to defer until both users forget this element. | |
336 | - * | |
337 | - * Don't kfree() until "struct ccs_io_buffer"->r.{group,acl,subacl} and | |
338 | - * "struct ccs_io_buffer"->w.acl forget this element. | |
339 | - */ | |
340 | - if (ccs_struct_used_by_io_buffer(element)) | |
341 | - goto reinject; | |
342 | - switch (type) { | |
343 | - case CCS_ID_GROUP: | |
344 | - ccs_del_group(element); | |
345 | - break; | |
346 | - case CCS_ID_STRING_GROUP: | |
347 | - ccs_del_string_group(element); | |
348 | - break; | |
349 | - case CCS_ID_CONDITION: | |
350 | - ccs_del_condition(element); | |
351 | - break; | |
352 | - case CCS_ID_NAME: | |
353 | - /* | |
354 | - * Don't kfree() until all "struct ccs_io_buffer"->r.w[] forget | |
355 | - * this element. | |
356 | - */ | |
357 | - if (ccs_name_used_by_io_buffer | |
358 | - (container_of(element, typeof(struct ccs_name), | |
359 | - head.list)->entry.name, | |
360 | - container_of(element, typeof(struct ccs_name), | |
361 | - head.list)->size)) | |
362 | - goto reinject; | |
363 | - break; | |
364 | - case CCS_ID_ACL: | |
365 | - ccs_del_acl(element); | |
366 | - break; | |
367 | - case CCS_ID_DOMAIN: | |
368 | - /* | |
369 | - * Don't kfree() until all "struct task_struct" forget this | |
370 | - * element. | |
371 | - */ | |
372 | - if (ccs_domain_used_by_task | |
373 | - (container_of(element, typeof(struct ccs_domain_info), | |
374 | - list))) | |
375 | - goto reinject; | |
376 | - ccs_del_domain(element); | |
377 | - break; | |
378 | - default: | |
379 | - break; | |
380 | - } | |
381 | - mutex_lock(&ccs_policy_lock); | |
382 | - ccs_memory_free(element, type); | |
383 | - return; | |
384 | -reinject: | |
385 | - /* | |
386 | - * We can safely reinject this element here bacause | |
387 | - * (1) Appending list elements and removing list elements are protected | |
388 | - * by ccs_policy_lock mutex. | |
389 | - * (2) Only this function removes list elements and this function is | |
390 | - * exclusively executed by ccs_gc_mutex mutex. | |
391 | - * are true. | |
392 | - */ | |
393 | - mutex_lock(&ccs_policy_lock); | |
394 | - list_add_rcu(element, element->prev); | |
395 | -} | |
396 | - | |
397 | -/** | |
398 | - * ccs_collect_member - Delete elements with "struct ccs_acl_head". | |
399 | - * | |
400 | - * @id: One of values in "enum ccs_policy_id". | |
401 | - * @member_list: Pointer to "struct list_head". | |
402 | - * | |
403 | - * Returns nothing. | |
404 | - * | |
405 | - * Caller holds ccs_policy_lock mutex. | |
406 | - */ | |
407 | -static void ccs_collect_member(const enum ccs_policy_id id, | |
408 | - struct list_head *member_list) | |
409 | -{ | |
410 | - struct ccs_acl_head *member; | |
411 | - struct ccs_acl_head *tmp; | |
412 | - list_for_each_entry_safe(member, tmp, member_list, list) { | |
413 | - if (!member->is_deleted) | |
414 | - continue; | |
415 | - member->is_deleted = CCS_GC_IN_PROGRESS; | |
416 | - ccs_try_to_gc(id, &member->list); | |
417 | - } | |
418 | -} | |
419 | - | |
420 | -/** | |
421 | - * ccs_collect_acl - Delete elements in "struct ccs_acl_info". | |
422 | - * | |
423 | - * @list: Pointer to "struct list_head". | |
424 | - * | |
425 | - * Returns nothing. | |
426 | - * | |
427 | - * Caller holds ccs_policy_lock mutex. | |
428 | - */ | |
429 | -static void ccs_collect_acl(struct list_head *list) | |
430 | -{ | |
431 | - struct ccs_acl_info *acl; | |
432 | - struct ccs_acl_info *tmp; | |
433 | - list_for_each_entry_safe(acl, tmp, list, list) { | |
434 | - if (!acl->is_deleted) | |
435 | - continue; | |
436 | - ccs_try_to_gc(CCS_ID_ACL, &acl->list); | |
437 | - } | |
438 | -} | |
439 | - | |
440 | -/** | |
441 | - * ccs_collect_entry - Try to kfree() deleted elements. | |
442 | - * | |
443 | - * Returns nothing. | |
444 | - */ | |
445 | -static void ccs_collect_entry(void) | |
446 | -{ | |
447 | - int i; | |
448 | - mutex_lock(&ccs_policy_lock); | |
449 | - { | |
450 | - struct ccs_domain_info *domain; | |
451 | - struct ccs_domain_info *tmp; | |
452 | - list_for_each_entry_safe(domain, tmp, &ccs_domain_list, list) { | |
453 | - if (ccs_domain_used_by_task(domain)) | |
454 | - continue; | |
455 | - ccs_try_to_gc(CCS_ID_DOMAIN, &domain->list); | |
456 | - } | |
457 | - } | |
458 | - for (i = 0; i < CCS_MAX_MAC_INDEX; i++) { | |
459 | - struct ccs_acl_info *ptr; | |
460 | - struct ccs_acl_info *tmp; | |
461 | - struct list_head * const list = &ccs_acl_list[i]; | |
462 | - list_for_each_entry_safe(ptr, tmp, list, list) { | |
463 | - ccs_collect_acl(&ptr->acl_info_list); | |
464 | - if (!ptr->is_deleted || | |
465 | - !list_empty(&ptr->acl_info_list)) | |
466 | - continue; | |
467 | - /* ptr->is_deleted = CCS_GC_IN_PROGRESS; */ | |
468 | - ccs_try_to_gc(CCS_ID_ACL, &ptr->list); | |
469 | - } | |
470 | - } | |
471 | - { | |
472 | - struct ccs_shared_acl_head *ptr; | |
473 | - struct ccs_shared_acl_head *tmp; | |
474 | - list_for_each_entry_safe(ptr, tmp, &ccs_condition_list, list) { | |
475 | - if (atomic_read(&ptr->users) > 0) | |
476 | - continue; | |
477 | - atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); | |
478 | - ccs_try_to_gc(CCS_ID_CONDITION, &ptr->list); | |
479 | - } | |
480 | - } | |
481 | - for (i = 0; i < CCS_MAX_GROUP; i++) { | |
482 | - struct list_head *list = &ccs_group_list[i]; | |
483 | - struct ccs_group *group; | |
484 | - struct ccs_group *tmp; | |
485 | - enum ccs_policy_id id = CCS_ID_STRING_GROUP; | |
486 | - if (i == CCS_NUMBER_GROUP) | |
487 | - id = CCS_ID_NUMBER_GROUP; | |
488 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
489 | - else if (i == CCS_IP_GROUP) | |
490 | - id = CCS_ID_IP_GROUP; | |
491 | -#endif | |
492 | - list_for_each_entry_safe(group, tmp, list, head.list) { | |
493 | - ccs_collect_member(id, &group->member_list); | |
494 | - if (!list_empty(&group->member_list) || | |
495 | - atomic_read(&group->head.users) > 0) | |
496 | - continue; | |
497 | - atomic_set(&group->head.users, CCS_GC_IN_PROGRESS); | |
498 | - ccs_try_to_gc(CCS_ID_GROUP, &group->head.list); | |
499 | - } | |
500 | - } | |
501 | - for (i = 0; i < CCS_MAX_HASH; i++) { | |
502 | - struct list_head *list = &ccs_name_list[i]; | |
503 | - struct ccs_shared_acl_head *ptr; | |
504 | - struct ccs_shared_acl_head *tmp; | |
505 | - list_for_each_entry_safe(ptr, tmp, list, list) { | |
506 | - if (atomic_read(&ptr->users) > 0) | |
507 | - continue; | |
508 | - atomic_set(&ptr->users, CCS_GC_IN_PROGRESS); | |
509 | - ccs_try_to_gc(CCS_ID_NAME, &ptr->list); | |
510 | - } | |
511 | - } | |
512 | - mutex_unlock(&ccs_policy_lock); | |
513 | -} | |
514 | - | |
515 | -/** | |
516 | - * ccs_gc_thread - Garbage collector thread function. | |
517 | - * | |
518 | - * @unused: Unused. | |
519 | - * | |
520 | - * Returns 0. | |
521 | - */ | |
522 | -static int ccs_gc_thread(void *unused) | |
523 | -{ | |
524 | - /* Garbage collector thread is exclusive. */ | |
525 | - static DEFINE_MUTEX(ccs_gc_mutex); | |
526 | - if (!mutex_trylock(&ccs_gc_mutex)) | |
527 | - goto out; | |
528 | - ccs_collect_entry(); | |
529 | - { | |
530 | - struct ccs_io_buffer *head; | |
531 | - struct ccs_io_buffer *tmp; | |
532 | - spin_lock(&ccs_io_buffer_list_lock); | |
533 | - list_for_each_entry_safe(head, tmp, &ccs_io_buffer_list, | |
534 | - list) { | |
535 | - if (head->users) | |
536 | - continue; | |
537 | - list_del(&head->list); | |
538 | - kfree(head->read_buf); | |
539 | - kfree(head->write_buf); | |
540 | - kfree(head); | |
541 | - } | |
542 | - spin_unlock(&ccs_io_buffer_list_lock); | |
543 | - } | |
544 | - mutex_unlock(&ccs_gc_mutex); | |
545 | -out: | |
546 | - /* This acts as do_exit(0). */ | |
547 | - return 0; | |
548 | -} | |
549 | - | |
550 | -/** | |
551 | - * ccs_notify_gc - Register/unregister /proc/ccs/ users. | |
552 | - * | |
553 | - * @head: Pointer to "struct ccs_io_buffer". | |
554 | - * @is_register: True if register, false if unregister. | |
555 | - * | |
556 | - * Returns nothing. | |
557 | - */ | |
558 | -void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register) | |
559 | -{ | |
560 | - bool is_write = false; | |
561 | - spin_lock(&ccs_io_buffer_list_lock); | |
562 | - if (is_register) { | |
563 | - head->users = 1; | |
564 | - list_add(&head->list, &ccs_io_buffer_list); | |
565 | - } else { | |
566 | - is_write = head->write_buf != NULL; | |
567 | - if (!--head->users) { | |
568 | - list_del(&head->list); | |
569 | - kfree(head->read_buf); | |
570 | - kfree(head->write_buf); | |
571 | - kfree(head); | |
572 | - } | |
573 | - } | |
574 | - spin_unlock(&ccs_io_buffer_list_lock); | |
575 | - if (is_write) { | |
576 | - struct task_struct *task = kthread_create(ccs_gc_thread, NULL, | |
577 | - "GC for CCS"); | |
578 | - if (!IS_ERR(task)) | |
579 | - wake_up_process(task); | |
580 | - } | |
581 | -} |
@@ -1,22 +0,0 @@ | ||
1 | -ccsecurity-objs := permission.o gc.o memory.o policy_io.o realpath.o | |
2 | - | |
3 | -obj-y += load_policy.o | |
4 | -ifdef CONFIG_CCSECURITY_LKM | |
5 | -obj-m += ccsecurity.o | |
6 | -else | |
7 | -obj-y += ccsecurity.o | |
8 | -endif | |
9 | - | |
10 | -$(obj)/policy/policy.conf: | |
11 | - @mkdir -p $(obj)/policy/ | |
12 | - @echo Creating an empty policy/policy.conf | |
13 | - @touch $@ | |
14 | - | |
15 | -$(obj)/builtin-policy.h: $(obj)/policy/policy.conf | |
16 | - @echo Generating built-in policy for CaitSith 0.1. | |
17 | - @echo "static char ccs_builtin_policy[] __initdata =" > $@.tmp | |
18 | - @sed -e 's/\\/\\134/g' -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/policy.conf >> $@.tmp | |
19 | - @echo "\"\";" >> $@.tmp | |
20 | - @mv $@.tmp $@ | |
21 | - | |
22 | -$(obj)/policy_io.o: $(obj)/builtin-policy.h |
@@ -1,229 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/load_policy.c | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#include <linux/version.h> | |
10 | -#include <linux/module.h> | |
11 | -#include <linux/init.h> | |
12 | -#include <linux/binfmts.h> | |
13 | -#include <linux/sched.h> | |
14 | -#include <linux/fs.h> | |
15 | -#include <linux/namei.h> | |
16 | -#ifndef LOOKUP_POSITIVE | |
17 | -#define LOOKUP_POSITIVE 0 | |
18 | -#endif | |
19 | - | |
20 | -/* | |
21 | - * TOMOYO specific part start. | |
22 | - */ | |
23 | - | |
24 | -#include <linux/caitsith.h> | |
25 | - | |
26 | -/** | |
27 | - * ccs_setup - Set enable/disable upon boot. | |
28 | - * | |
29 | - * @str: "off" to disable, "on" to enable. | |
30 | - * | |
31 | - * Returns 0. | |
32 | - */ | |
33 | -static int __init ccs_setup(char *str) | |
34 | -{ | |
35 | - if (!strcmp(str, "off")) | |
36 | - ccsecurity_ops.disabled = 1; | |
37 | - else if (!strcmp(str, "on")) | |
38 | - ccsecurity_ops.disabled = 0; | |
39 | - return 0; | |
40 | -} | |
41 | - | |
42 | -__setup("caitsith=", ccs_setup); | |
43 | - | |
44 | -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
45 | - | |
46 | -/* Path to the policy loader. (default = CONFIG_CCSECURITY_POLICY_LOADER) */ | |
47 | -static const char *ccs_loader; | |
48 | - | |
49 | -/** | |
50 | - * ccs_loader_setup - Set policy loader. | |
51 | - * | |
52 | - * @str: Program to use as a policy loader (e.g. /sbin/caitsith-init ). | |
53 | - * | |
54 | - * Returns 0. | |
55 | - */ | |
56 | -static int __init ccs_loader_setup(char *str) | |
57 | -{ | |
58 | - ccs_loader = str; | |
59 | - return 0; | |
60 | -} | |
61 | - | |
62 | -__setup("CCS_loader=", ccs_loader_setup); | |
63 | - | |
64 | -/** | |
65 | - * ccs_policy_loader_exists - Check whether /sbin/caitsith-init exists. | |
66 | - * | |
67 | - * Returns true if /sbin/caitsith-init exists, false otherwise. | |
68 | - */ | |
69 | -static _Bool ccs_policy_loader_exists(void) | |
70 | -{ | |
71 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
72 | - struct path path; | |
73 | - if (!ccs_loader) | |
74 | - ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | |
75 | - if (kern_path(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | |
76 | - &path) == 0) { | |
77 | - path_put(&path); | |
78 | - return 1; | |
79 | - } | |
80 | -#else | |
81 | - struct nameidata nd; | |
82 | - if (!ccs_loader) | |
83 | - ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | |
84 | - if (path_lookup(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | |
85 | - &nd) == 0) { | |
86 | - path_put(&nd.path); | |
87 | - return 1; | |
88 | - } | |
89 | -#endif | |
90 | - printk(KERN_INFO "Not activating Mandatory Access Control " | |
91 | - "as %s does not exist.\n", ccs_loader); | |
92 | - return 0; | |
93 | -} | |
94 | - | |
95 | -/* Path to the trigger. (default = CONFIG_CCSECURITY_ACTIVATION_TRIGGER) */ | |
96 | -static const char *ccs_trigger; | |
97 | - | |
98 | -/** | |
99 | - * ccs_trigger_setup - Set trigger for activation. | |
100 | - * | |
101 | - * @str: Program to use as an activation trigger (e.g. /sbin/init ). | |
102 | - * | |
103 | - * Returns 0. | |
104 | - */ | |
105 | -static int __init ccs_trigger_setup(char *str) | |
106 | -{ | |
107 | - ccs_trigger = str; | |
108 | - return 0; | |
109 | -} | |
110 | - | |
111 | -__setup("CCS_trigger=", ccs_trigger_setup); | |
112 | - | |
113 | -/** | |
114 | - * ccs_load_policy - Run external policy loader to load policy. | |
115 | - * | |
116 | - * @filename: The program about to start. | |
117 | - * | |
118 | - * Returns nothing. | |
119 | - * | |
120 | - * This function checks whether @filename is /sbin/init, and if so | |
121 | - * invoke /sbin/caitsith-init and wait for the termination of /sbin/caitsith-init | |
122 | - * and then continues invocation of /sbin/init. | |
123 | - * /sbin/caitsith-init reads policy files in /etc/ccs/ directory and | |
124 | - * writes to /proc/ccs/ interfaces. | |
125 | - */ | |
126 | -static void ccs_load_policy(const char *filename) | |
127 | -{ | |
128 | - static _Bool done; | |
129 | - if (ccsecurity_ops.disabled || done) | |
130 | - return; | |
131 | - if (!ccs_trigger) | |
132 | - ccs_trigger = CONFIG_CCSECURITY_ACTIVATION_TRIGGER; | |
133 | - if (strcmp(filename, ccs_trigger)) | |
134 | - return; | |
135 | - if (!ccs_policy_loader_exists()) | |
136 | - return; | |
137 | - done = 1; | |
138 | - { | |
139 | - char *argv[2]; | |
140 | - char *envp[3]; | |
141 | - printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | |
142 | - ccs_loader); | |
143 | - argv[0] = (char *) ccs_loader; | |
144 | - argv[1] = NULL; | |
145 | - envp[0] = "HOME=/"; | |
146 | - envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
147 | - envp[2] = NULL; | |
148 | - call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); | |
149 | - } | |
150 | - if (ccsecurity_ops.check_profile) | |
151 | - ccsecurity_ops.check_profile(); | |
152 | - else | |
153 | - panic("Failed to load policy."); | |
154 | -} | |
155 | - | |
156 | -#endif | |
157 | - | |
158 | -/** | |
159 | - * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). | |
160 | - * | |
161 | - * @bprm: Pointer to "struct linux_binprm". | |
162 | - * @regs: Pointer to "struct pt_regs". | |
163 | - * | |
164 | - * Returns 0 on success, negative value otherwise. | |
165 | - */ | |
166 | -static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
167 | - struct pt_regs *regs) | |
168 | -{ | |
169 | -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
170 | - ccs_load_policy(bprm->filename); | |
171 | -#endif | |
172 | - /* | |
173 | - * ccs_load_policy() executes /sbin/caitsith-init if bprm->filename is | |
174 | - * /sbin/init. /sbin/caitsith-init executes | |
175 | - * /etc/ccs/caitsith-load-module to load loadable kernel module. | |
176 | - * The loadable kernel module modifies "struct ccsecurity_ops". Thus, | |
177 | - * we need to transfer control to __ccs_search_binary_handler() in | |
178 | - * security/caitsith/permission.c if "struct ccsecurity_ops" was | |
179 | - * modified. | |
180 | - */ | |
181 | - if (ccsecurity_ops.search_binary_handler | |
182 | - != __ccs_search_binary_handler) | |
183 | - return ccsecurity_ops.search_binary_handler(bprm, regs); | |
184 | - return search_binary_handler(bprm, regs); | |
185 | -} | |
186 | - | |
187 | -/* | |
188 | - * Some exports for loadable kernel module part. | |
189 | - * | |
190 | - * Although scripts/checkpatch.pl complains about use of "extern" in C file, | |
191 | - * we don't put these into security/caitsith/internal.h because we want to | |
192 | - * split built-in part and loadable kernel module part. | |
193 | - */ | |
194 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | |
195 | -extern spinlock_t vfsmount_lock; | |
196 | -#endif | |
197 | - | |
198 | -/* For exporting variables and functions. */ | |
199 | -const struct ccsecurity_exports ccsecurity_exports = { | |
200 | -#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
201 | - .load_policy = ccs_load_policy, | |
202 | -#endif | |
203 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
204 | - .d_absolute_path = d_absolute_path, | |
205 | -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
206 | - .__d_path = __d_path, | |
207 | -#else | |
208 | - .vfsmount_lock = &vfsmount_lock, | |
209 | -#endif | |
210 | - .find_task_by_vpid = find_task_by_vpid, | |
211 | - .find_task_by_pid_ns = find_task_by_pid_ns, | |
212 | -}; | |
213 | -#ifdef CONFIG_CCSECURITY_LKM | |
214 | -/* Only ccsecurity module need to access this struct. */ | |
215 | -EXPORT_SYMBOL_GPL(ccsecurity_exports); | |
216 | -#endif | |
217 | - | |
218 | -/* Members are updated by loadable kernel module. */ | |
219 | -struct ccsecurity_operations ccsecurity_ops = { | |
220 | - .search_binary_handler = __ccs_search_binary_handler, | |
221 | -#ifdef CONFIG_CCSECURITY_DISABLE_BY_DEFAULT | |
222 | - .disabled = 1, | |
223 | -#endif | |
224 | -}; | |
225 | -/* | |
226 | - * Non-GPL modules might need to access this struct via inlined functions | |
227 | - * embedded into include/linux/security.h and include/net/ip.h | |
228 | - */ | |
229 | -EXPORT_SYMBOL(ccsecurity_ops); |
@@ -1,644 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/realpath.c | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#include "internal.h" | |
10 | - | |
11 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
12 | -#include <linux/nsproxy.h> | |
13 | -#include <linux/mnt_namespace.h> | |
14 | -#endif | |
15 | - | |
16 | -/***** SECTION1: Constants definition *****/ | |
17 | - | |
18 | -#define SOCKFS_MAGIC 0x534F434B | |
19 | - | |
20 | -/***** SECTION2: Structure definition *****/ | |
21 | - | |
22 | -/***** SECTION3: Prototype definition section *****/ | |
23 | - | |
24 | -static char *ccs_get_absolute_path(struct path *path, char * const buffer, | |
25 | - const int buflen); | |
26 | -static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | |
27 | - const int buflen); | |
28 | -static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | |
29 | - const int buflen); | |
30 | -static char *ccs_get_socket_name(struct path *path, char * const buffer, | |
31 | - const int buflen); | |
32 | -static int ccs_const_part_length(const char *filename); | |
33 | - | |
34 | -/***** SECTION4: Standalone functions section *****/ | |
35 | - | |
36 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
37 | - | |
38 | -/** | |
39 | - * ccs_realpath_lock - Take locks for __d_path(). | |
40 | - * | |
41 | - * Returns nothing. | |
42 | - */ | |
43 | -static inline void ccs_realpath_lock(void) | |
44 | -{ | |
45 | - /* dcache_lock is locked by __d_path(). */ | |
46 | - /* vfsmount_lock is locked by __d_path(). */ | |
47 | -} | |
48 | - | |
49 | -/** | |
50 | - * ccs_realpath_unlock - Release locks for __d_path(). | |
51 | - * | |
52 | - * Returns nothing. | |
53 | - */ | |
54 | -static inline void ccs_realpath_unlock(void) | |
55 | -{ | |
56 | - /* vfsmount_lock is unlocked by __d_path(). */ | |
57 | - /* dcache_lock is unlocked by __d_path(). */ | |
58 | -} | |
59 | - | |
60 | -#elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) | |
61 | - | |
62 | -/** | |
63 | - * ccs_realpath_lock - Take locks for __d_path(). | |
64 | - * | |
65 | - * Returns nothing. | |
66 | - */ | |
67 | -static inline void ccs_realpath_lock(void) | |
68 | -{ | |
69 | - spin_lock(&dcache_lock); | |
70 | - /* vfsmount_lock is locked by __d_path(). */ | |
71 | -} | |
72 | - | |
73 | -/** | |
74 | - * ccs_realpath_unlock - Release locks for __d_path(). | |
75 | - * | |
76 | - * Returns nothing. | |
77 | - */ | |
78 | -static inline void ccs_realpath_unlock(void) | |
79 | -{ | |
80 | - /* vfsmount_lock is unlocked by __d_path(). */ | |
81 | - spin_unlock(&dcache_lock); | |
82 | -} | |
83 | - | |
84 | -#elif defined(D_PATH_DISCONNECT) && !defined(CONFIG_SUSE_KERNEL) | |
85 | - | |
86 | -/** | |
87 | - * ccs_realpath_lock - Take locks for __d_path(). | |
88 | - * | |
89 | - * Returns nothing. | |
90 | - * | |
91 | - * Original unambiguous-__d_path.diff in patches.apparmor.tar.bz2 inversed the | |
92 | - * order of holding dcache_lock and vfsmount_lock. That patch was applied on | |
93 | - * (at least) SUSE 11.1 and Ubuntu 8.10 and Ubuntu 9.04 kernels. | |
94 | - * | |
95 | - * However, that patch was updated to use original order and the updated patch | |
96 | - * is applied to (as far as I know) only SUSE kernels. | |
97 | - * | |
98 | - * Therefore, I need to use original order for SUSE 11.1 kernels and inversed | |
99 | - * order for other kernels. I detect it by checking D_PATH_DISCONNECT and | |
100 | - * CONFIG_SUSE_KERNEL. I don't know whether other distributions are using the | |
101 | - * updated patch or not. If you got deadlock, check fs/dcache.c for locking | |
102 | - * order, and add " && 0" to this "#elif " block if fs/dcache.c uses original | |
103 | - * order. | |
104 | - */ | |
105 | -static inline void ccs_realpath_lock(void) | |
106 | -{ | |
107 | - spin_lock(ccsecurity_exports.vfsmount_lock); | |
108 | - spin_lock(&dcache_lock); | |
109 | -} | |
110 | - | |
111 | -/** | |
112 | - * ccs_realpath_unlock - Release locks for __d_path(). | |
113 | - * | |
114 | - * Returns nothing. | |
115 | - */ | |
116 | -static inline void ccs_realpath_unlock(void) | |
117 | -{ | |
118 | - spin_unlock(&dcache_lock); | |
119 | - spin_unlock(ccsecurity_exports.vfsmount_lock); | |
120 | -} | |
121 | - | |
122 | -#else | |
123 | - | |
124 | -/** | |
125 | - * ccs_realpath_lock - Take locks for __d_path(). | |
126 | - * | |
127 | - * Returns nothing. | |
128 | - */ | |
129 | -static inline void ccs_realpath_lock(void) | |
130 | -{ | |
131 | - spin_lock(&dcache_lock); | |
132 | - spin_lock(ccsecurity_exports.vfsmount_lock); | |
133 | -} | |
134 | - | |
135 | -/** | |
136 | - * ccs_realpath_unlock - Release locks for __d_path(). | |
137 | - * | |
138 | - * Returns nothing. | |
139 | - */ | |
140 | -static inline void ccs_realpath_unlock(void) | |
141 | -{ | |
142 | - spin_unlock(ccsecurity_exports.vfsmount_lock); | |
143 | - spin_unlock(&dcache_lock); | |
144 | -} | |
145 | - | |
146 | -#endif | |
147 | - | |
148 | -/***** SECTION5: Variables definition section *****/ | |
149 | - | |
150 | -/***** SECTION6: Dependent functions section *****/ | |
151 | - | |
152 | -/** | |
153 | - * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. | |
154 | - * | |
155 | - * @path: Pointer to "struct path". | |
156 | - * @buffer: Pointer to buffer to return value in. | |
157 | - * @buflen: Sizeof @buffer. | |
158 | - * | |
159 | - * Returns the buffer on success, an error code otherwise. | |
160 | - * | |
161 | - * Caller holds the dcache_lock and vfsmount_lock. | |
162 | - * Based on __d_path() in fs/dcache.c | |
163 | - */ | |
164 | -static char *ccs_get_absolute_path(struct path *path, char * const buffer, | |
165 | - const int buflen) | |
166 | -{ | |
167 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
168 | - if (buflen < 256) | |
169 | - return ERR_PTR(-ENOMEM); | |
170 | - return ccsecurity_exports.d_absolute_path(path, buffer, buflen - 1); | |
171 | -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
172 | - /* | |
173 | - * __d_path() will start returning NULL by backporting commit 02125a82 | |
174 | - * "fix apparmor dereferencing potentially freed dentry, sanitize | |
175 | - * __d_path() API". | |
176 | - * | |
177 | - * Unfortunately, __d_path() after applying that commit always returns | |
178 | - * NULL when root is empty. d_absolute_path() is provided for TOMOYO | |
179 | - * 2.x and AppArmor but TOMOYO 1.x does not use it, for TOMOYO 1.x | |
180 | - * might be built as a loadable kernel module and there is no warrantee | |
181 | - * that TOMOYO 1.x is recompiled after applying that commit. Also, | |
182 | - * I don't want to search /proc/kallsyms for d_absolute_path() because | |
183 | - * I want to keep TOMOYO 1.x architecture independent. Thus, supply | |
184 | - * non empty root like AppArmor's d_namespace_path() did. | |
185 | - */ | |
186 | - static bool ccs_no_empty; | |
187 | - char *pos; | |
188 | - if (buflen < 256) | |
189 | - return ERR_PTR(-ENOMEM); | |
190 | - if (!ccs_no_empty) { | |
191 | - struct path root = { }; | |
192 | - pos = ccsecurity_exports.__d_path(path, &root, buffer, | |
193 | - buflen - 1); | |
194 | - } else { | |
195 | - pos = NULL; | |
196 | - } | |
197 | - if (!pos) { | |
198 | - struct task_struct *task = current; | |
199 | - struct path root; | |
200 | - struct path tmp; | |
201 | - spin_lock(&task->fs->lock); | |
202 | - root.mnt = task->nsproxy->mnt_ns->root; | |
203 | - root.dentry = root.mnt->mnt_root; | |
204 | - path_get(&root); | |
205 | - spin_unlock(&task->fs->lock); | |
206 | - tmp = root; | |
207 | - pos = ccsecurity_exports.__d_path(path, &tmp, buffer, | |
208 | - buflen - 1); | |
209 | - path_put(&root); | |
210 | - if (pos) | |
211 | - return pos; | |
212 | - /* Remember if __d_path() needs non empty root. */ | |
213 | - ccs_no_empty = true; | |
214 | - pos = ERR_PTR(-EINVAL); | |
215 | - } | |
216 | - return pos; | |
217 | -#else | |
218 | - char *pos = buffer + buflen - 1; | |
219 | - struct dentry *dentry = path->dentry; | |
220 | - struct vfsmount *vfsmnt = path->mnt; | |
221 | - const char *name; | |
222 | - int len; | |
223 | - | |
224 | - if (buflen < 256) | |
225 | - goto out; | |
226 | - | |
227 | - *pos = '\0'; | |
228 | - for (;;) { | |
229 | - struct dentry *parent; | |
230 | - if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | |
231 | - if (vfsmnt->mnt_parent == vfsmnt) | |
232 | - break; | |
233 | - dentry = vfsmnt->mnt_mountpoint; | |
234 | - vfsmnt = vfsmnt->mnt_parent; | |
235 | - continue; | |
236 | - } | |
237 | - parent = dentry->d_parent; | |
238 | - name = dentry->d_name.name; | |
239 | - len = dentry->d_name.len; | |
240 | - pos -= len; | |
241 | - if (pos <= buffer) | |
242 | - goto out; | |
243 | - memmove(pos, name, len); | |
244 | - *--pos = '/'; | |
245 | - dentry = parent; | |
246 | - } | |
247 | - if (*pos == '/') | |
248 | - pos++; | |
249 | - len = dentry->d_name.len; | |
250 | - pos -= len; | |
251 | - if (pos < buffer) | |
252 | - goto out; | |
253 | - memmove(pos, dentry->d_name.name, len); | |
254 | - return pos; | |
255 | -out: | |
256 | - return ERR_PTR(-ENOMEM); | |
257 | -#endif | |
258 | -} | |
259 | - | |
260 | -/** | |
261 | - * ccs_get_dentry_path - Get the path of a dentry. | |
262 | - * | |
263 | - * @dentry: Pointer to "struct dentry". | |
264 | - * @buffer: Pointer to buffer to return value in. | |
265 | - * @buflen: Sizeof @buffer. | |
266 | - * | |
267 | - * Returns the buffer on success, an error code otherwise. | |
268 | - * | |
269 | - * Based on dentry_path() in fs/dcache.c | |
270 | - */ | |
271 | -static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | |
272 | - const int buflen) | |
273 | -{ | |
274 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) | |
275 | - if (buflen < 256) | |
276 | - return ERR_PTR(-ENOMEM); | |
277 | - /* rename_lock is locked/unlocked by dentry_path_raw(). */ | |
278 | - return dentry_path_raw(dentry, buffer, buflen - 1); | |
279 | -#else | |
280 | - char *pos = buffer + buflen - 1; | |
281 | - if (buflen < 256) | |
282 | - return ERR_PTR(-ENOMEM); | |
283 | - *pos = '\0'; | |
284 | - spin_lock(&dcache_lock); | |
285 | - while (!IS_ROOT(dentry)) { | |
286 | - struct dentry *parent = dentry->d_parent; | |
287 | - const char *name = dentry->d_name.name; | |
288 | - const int len = dentry->d_name.len; | |
289 | - pos -= len; | |
290 | - if (pos <= buffer) { | |
291 | - pos = ERR_PTR(-ENOMEM); | |
292 | - break; | |
293 | - } | |
294 | - memmove(pos, name, len); | |
295 | - *--pos = '/'; | |
296 | - dentry = parent; | |
297 | - } | |
298 | - spin_unlock(&dcache_lock); | |
299 | - if (pos == buffer + buflen - 1) | |
300 | - *--pos = '/'; | |
301 | - return pos; | |
302 | -#endif | |
303 | -} | |
304 | - | |
305 | -/** | |
306 | - * ccs_get_local_path - Get the path of a dentry. | |
307 | - * | |
308 | - * @dentry: Pointer to "struct dentry". | |
309 | - * @buffer: Pointer to buffer to return value in. | |
310 | - * @buflen: Sizeof @buffer. | |
311 | - * | |
312 | - * Returns the buffer on success, an error code otherwise. | |
313 | - */ | |
314 | -static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | |
315 | - const int buflen) | |
316 | -{ | |
317 | - struct super_block *sb = dentry->d_sb; | |
318 | - char *pos = ccs_get_dentry_path(dentry, buffer, buflen); | |
319 | - if (IS_ERR(pos)) | |
320 | - return pos; | |
321 | - /* Convert from $PID to self if $PID is current thread. */ | |
322 | - if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { | |
323 | - char *ep; | |
324 | - const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); | |
325 | - if (*ep == '/' && pid && pid == | |
326 | - task_tgid_nr_ns(current, sb->s_fs_info)) { | |
327 | - pos = ep - 5; | |
328 | - if (pos < buffer) | |
329 | - goto out; | |
330 | - memmove(pos, "/self", 5); | |
331 | - } | |
332 | - goto prepend_filesystem_name; | |
333 | - } | |
334 | - /* Use filesystem name for unnamed devices. */ | |
335 | - if (!MAJOR(sb->s_dev)) | |
336 | - goto prepend_filesystem_name; | |
337 | - { | |
338 | - struct inode *inode = sb->s_root->d_inode; | |
339 | - /* | |
340 | - * Use filesystem name if filesystems does not support rename() | |
341 | - * operation. | |
342 | - */ | |
343 | - if (inode->i_op && !inode->i_op->rename) | |
344 | - goto prepend_filesystem_name; | |
345 | - } | |
346 | - /* Prepend device name. */ | |
347 | - { | |
348 | - char name[64]; | |
349 | - int name_len; | |
350 | - const dev_t dev = sb->s_dev; | |
351 | - name[sizeof(name) - 1] = '\0'; | |
352 | - snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), | |
353 | - MINOR(dev)); | |
354 | - name_len = strlen(name); | |
355 | - pos -= name_len; | |
356 | - if (pos < buffer) | |
357 | - goto out; | |
358 | - memmove(pos, name, name_len); | |
359 | - return pos; | |
360 | - } | |
361 | - /* Prepend filesystem name. */ | |
362 | -prepend_filesystem_name: | |
363 | - { | |
364 | - const char *name = sb->s_type->name; | |
365 | - const int name_len = strlen(name); | |
366 | - pos -= name_len + 1; | |
367 | - if (pos < buffer) | |
368 | - goto out; | |
369 | - memmove(pos, name, name_len); | |
370 | - pos[name_len] = ':'; | |
371 | - } | |
372 | - return pos; | |
373 | -out: | |
374 | - return ERR_PTR(-ENOMEM); | |
375 | -} | |
376 | - | |
377 | -/** | |
378 | - * ccs_get_socket_name - Get the name of a socket. | |
379 | - * | |
380 | - * @path: Pointer to "struct path". | |
381 | - * @buffer: Pointer to buffer to return value in. | |
382 | - * @buflen: Sizeof @buffer. | |
383 | - * | |
384 | - * Returns the buffer. | |
385 | - */ | |
386 | -static char *ccs_get_socket_name(struct path *path, char * const buffer, | |
387 | - const int buflen) | |
388 | -{ | |
389 | - struct inode *inode = path->dentry->d_inode; | |
390 | - struct socket *sock = inode ? SOCKET_I(inode) : NULL; | |
391 | - struct sock *sk = sock ? sock->sk : NULL; | |
392 | - if (sk) { | |
393 | - snprintf(buffer, buflen, "socket:[family=%u:type=%u:" | |
394 | - "protocol=%u]", sk->sk_family, sk->sk_type, | |
395 | - sk->sk_protocol); | |
396 | - } else { | |
397 | - snprintf(buffer, buflen, "socket:[unknown]"); | |
398 | - } | |
399 | - return buffer; | |
400 | -} | |
401 | - | |
402 | -/** | |
403 | - * ccs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root. | |
404 | - * | |
405 | - * @path: Pointer to "struct path". | |
406 | - * | |
407 | - * Returns the realpath of the given @path on success, NULL otherwise. | |
408 | - * | |
409 | - * This function uses kzalloc(), so caller must kfree() if this function | |
410 | - * didn't return NULL. | |
411 | - */ | |
412 | -char *ccs_realpath(struct path *path) | |
413 | -{ | |
414 | - char *buf = NULL; | |
415 | - char *name = NULL; | |
416 | - unsigned int buf_len = PAGE_SIZE / 2; | |
417 | - struct dentry *dentry = path->dentry; | |
418 | - struct super_block *sb; | |
419 | - if (!dentry) | |
420 | - return NULL; | |
421 | - sb = dentry->d_sb; | |
422 | - while (1) { | |
423 | - char *pos; | |
424 | - struct inode *inode; | |
425 | - buf_len <<= 1; | |
426 | - kfree(buf); | |
427 | - buf = kmalloc(buf_len, GFP_NOFS); | |
428 | - if (!buf) | |
429 | - break; | |
430 | - /* To make sure that pos is '\0' terminated. */ | |
431 | - buf[buf_len - 1] = '\0'; | |
432 | - /* Get better name for socket. */ | |
433 | - if (sb->s_magic == SOCKFS_MAGIC) { | |
434 | - pos = ccs_get_socket_name(path, buf, buf_len - 1); | |
435 | - goto encode; | |
436 | - } | |
437 | - /* For "pipe:[\$]". */ | |
438 | - if (dentry->d_op && dentry->d_op->d_dname) { | |
439 | - pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); | |
440 | - goto encode; | |
441 | - } | |
442 | - inode = sb->s_root->d_inode; | |
443 | - /* | |
444 | - * Use local name for "filesystems without rename() operation" | |
445 | - * or "path without vfsmount" or "absolute name is unavailable" | |
446 | - * cases. | |
447 | - */ | |
448 | - if (!path->mnt || (inode->i_op && !inode->i_op->rename)) | |
449 | - pos = ERR_PTR(-EINVAL); | |
450 | - else { | |
451 | - /* Get absolute name for the rest. */ | |
452 | - ccs_realpath_lock(); | |
453 | - pos = ccs_get_absolute_path(path, buf, buf_len - 1); | |
454 | - ccs_realpath_unlock(); | |
455 | - } | |
456 | - if (pos == ERR_PTR(-EINVAL)) | |
457 | - pos = ccs_get_local_path(path->dentry, buf, | |
458 | - buf_len - 1); | |
459 | -encode: | |
460 | - if (IS_ERR(pos)) | |
461 | - continue; | |
462 | - name = ccs_encode(pos); | |
463 | - break; | |
464 | - } | |
465 | - kfree(buf); | |
466 | - if (!name) | |
467 | - ccs_warn_oom(__func__); | |
468 | - return name; | |
469 | -} | |
470 | - | |
471 | -/** | |
472 | - * ccs_encode2 - Encode binary string to ascii string. | |
473 | - * | |
474 | - * @str: String in binary format. Maybe NULL. | |
475 | - * @str_len: Size of @str in byte. | |
476 | - * | |
477 | - * Returns pointer to @str in ascii format on success, NULL otherwise. | |
478 | - * | |
479 | - * This function uses kzalloc(), so caller must kfree() if this function | |
480 | - * didn't return NULL. | |
481 | - */ | |
482 | -char *ccs_encode2(const char *str, int str_len) | |
483 | -{ | |
484 | - int i; | |
485 | - int len; | |
486 | - const char *p = str; | |
487 | - char *cp; | |
488 | - char *cp0; | |
489 | - if (!p) | |
490 | - return NULL; | |
491 | - len = str_len; | |
492 | - for (i = 0; i < str_len; i++) { | |
493 | - const unsigned char c = p[i]; | |
494 | - if (!(c > ' ' && c < 127 && c != '\\')) | |
495 | - len += 3; | |
496 | - } | |
497 | - len++; | |
498 | - cp = kzalloc(len, GFP_NOFS); | |
499 | - if (!cp) | |
500 | - return NULL; | |
501 | - cp0 = cp; | |
502 | - p = str; | |
503 | - for (i = 0; i < str_len; i++) { | |
504 | - const unsigned char c = p[i]; | |
505 | - if (c > ' ' && c < 127 && c != '\\') { | |
506 | - *cp++ = c; | |
507 | - } else { | |
508 | - *cp++ = '\\'; | |
509 | - *cp++ = (c >> 6) + '0'; | |
510 | - *cp++ = ((c >> 3) & 7) + '0'; | |
511 | - *cp++ = (c & 7) + '0'; | |
512 | - } | |
513 | - } | |
514 | - return cp0; | |
515 | -} | |
516 | - | |
517 | -/** | |
518 | - * ccs_encode - Encode binary string to ascii string. | |
519 | - * | |
520 | - * @str: String in binary format. Maybe NULL. | |
521 | - * | |
522 | - * Returns pointer to @str in ascii format on success, NULL otherwise. | |
523 | - * | |
524 | - * This function uses kzalloc(), so caller must kfree() if this function | |
525 | - * didn't return NULL. | |
526 | - */ | |
527 | -char *ccs_encode(const char *str) | |
528 | -{ | |
529 | - return str ? ccs_encode2(str, strlen(str)) : NULL; | |
530 | -} | |
531 | - | |
532 | -/** | |
533 | - * ccs_const_part_length - Evaluate the initial length without a pattern in a token. | |
534 | - * | |
535 | - * @filename: The string to evaluate. Maybe NULL. | |
536 | - * | |
537 | - * Returns the initial length without a pattern in @filename. | |
538 | - */ | |
539 | -static int ccs_const_part_length(const char *filename) | |
540 | -{ | |
541 | - char c; | |
542 | - int len = 0; | |
543 | - if (!filename) | |
544 | - return 0; | |
545 | - while (1) { | |
546 | - c = *filename++; | |
547 | - if (!c) | |
548 | - break; | |
549 | - if (c != '\\') { | |
550 | - len++; | |
551 | - continue; | |
552 | - } | |
553 | - c = *filename++; | |
554 | - switch (c) { | |
555 | - case '0': /* "\ooo" */ | |
556 | - case '1': | |
557 | - case '2': | |
558 | - case '3': | |
559 | - c = *filename++; | |
560 | - if (c < '0' || c > '7') | |
561 | - break; | |
562 | - c = *filename++; | |
563 | - if (c < '0' || c > '7') | |
564 | - break; | |
565 | - len += 4; | |
566 | - continue; | |
567 | - } | |
568 | - break; | |
569 | - } | |
570 | - return len; | |
571 | -} | |
572 | - | |
573 | -/** | |
574 | - * ccs_fill_path_info - Fill in "struct ccs_path_info" members. | |
575 | - * | |
576 | - * @ptr: Pointer to "struct ccs_path_info" to fill in. | |
577 | - * | |
578 | - * Returns nothing. | |
579 | - * | |
580 | - * The caller sets "struct ccs_path_info"->name. | |
581 | - */ | |
582 | -void ccs_fill_path_info(struct ccs_path_info *ptr) | |
583 | -{ | |
584 | - const char *name = ptr->name; | |
585 | - const int len = strlen(name); | |
586 | - ptr->total_len = len; | |
587 | - ptr->const_len = ccs_const_part_length(name); | |
588 | - ptr->hash = full_name_hash(name, len); | |
589 | -} | |
590 | - | |
591 | -/** | |
592 | - * ccs_get_exe - Get ccs_realpath() of current process. | |
593 | - * | |
594 | - * Returns the ccs_realpath() of current process on success, NULL otherwise. | |
595 | - * | |
596 | - * This function uses kzalloc(), so the caller must kfree() | |
597 | - * if this function didn't return NULL. | |
598 | - */ | |
599 | -char *ccs_get_exe(void) | |
600 | -{ | |
601 | - struct mm_struct *mm; | |
602 | - struct vm_area_struct *vma; | |
603 | - bool done = false; | |
604 | - char *cp = NULL; | |
605 | - if (current->flags & PF_KTHREAD) | |
606 | - return kstrdup("<kernel>", GFP_NOFS); | |
607 | - mm = current->mm; | |
608 | - if (!mm) | |
609 | - goto task_has_no_mm; | |
610 | - down_read(&mm->mmap_sem); | |
611 | - for (vma = mm->mmap; vma; vma = vma->vm_next) { | |
612 | - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | |
613 | - cp = ccs_realpath(&vma->vm_file->f_path); | |
614 | - done = true; | |
615 | - break; | |
616 | - } | |
617 | - } | |
618 | - up_read(&mm->mmap_sem); | |
619 | - if (done) | |
620 | - return cp; | |
621 | -task_has_no_mm: | |
622 | - /* I'don't know. */ | |
623 | - return kstrdup("<unknown>", GFP_NOFS); | |
624 | -} | |
625 | - | |
626 | -/** | |
627 | - * ccs_get_exename - Get ccs_realpath() of current process. | |
628 | - * | |
629 | - * @buf: Pointer to "struct ccs_path_info". | |
630 | - * | |
631 | - * Returns true on success, false otherwise. | |
632 | - * | |
633 | - * This function uses kzalloc(), so the caller must kfree() | |
634 | - * if this function returned true. | |
635 | - */ | |
636 | -bool ccs_get_exename(struct ccs_path_info *buf) | |
637 | -{ | |
638 | - buf->name = ccs_get_exe(); | |
639 | - if (buf->name) { | |
640 | - ccs_fill_path_info(buf); | |
641 | - return true; | |
642 | - } | |
643 | - return false; | |
644 | -} |
@@ -1,1162 +0,0 @@ | ||
1 | -/* | |
2 | - * security/caitsith/internal.h | |
3 | - * | |
4 | - * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | - * | |
6 | - * Version: 0.1 2012/04/01 | |
7 | - */ | |
8 | - | |
9 | -#ifndef _SECURITY_CCSECURITY_INTERNAL_H | |
10 | -#define _SECURITY_CCSECURITY_INTERNAL_H | |
11 | - | |
12 | -#include <linux/version.h> | |
13 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) | |
14 | -#error This module supports only 2.6.27 and later kernels. | |
15 | -#endif | |
16 | -#include <linux/types.h> | |
17 | -#include <linux/kernel.h> | |
18 | -#include <linux/string.h> | |
19 | -#include <linux/mm.h> | |
20 | -#include <linux/utime.h> | |
21 | -#include <linux/file.h> | |
22 | -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) | |
23 | -#include <linux/smp_lock.h> | |
24 | -#endif | |
25 | -#include <linux/module.h> | |
26 | -#include <linux/init.h> | |
27 | -#include <linux/slab.h> | |
28 | -#include <linux/highmem.h> | |
29 | -#include <linux/poll.h> | |
30 | -#include <linux/binfmts.h> | |
31 | -#include <linux/delay.h> | |
32 | -#include <linux/sched.h> | |
33 | -#include <linux/dcache.h> | |
34 | -#include <linux/mount.h> | |
35 | -#include <linux/net.h> | |
36 | -#include <linux/inet.h> | |
37 | -#include <linux/in.h> | |
38 | -#include <linux/in6.h> | |
39 | -#include <linux/un.h> | |
40 | -#include <linux/ptrace.h> | |
41 | -#include <linux/namei.h> | |
42 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | |
43 | -#include <linux/fs_struct.h> | |
44 | -#endif | |
45 | -#include <linux/proc_fs.h> | |
46 | -#include <linux/hash.h> | |
47 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
48 | -#include <linux/sysctl.h> | |
49 | -#endif | |
50 | -#include <linux/kthread.h> | |
51 | -#include <stdarg.h> | |
52 | -#include <asm/uaccess.h> | |
53 | -#include <net/sock.h> | |
54 | -#include <net/af_unix.h> | |
55 | -#include <net/ip.h> | |
56 | -#include <net/ipv6.h> | |
57 | -#include <net/udp.h> | |
58 | - | |
59 | -#ifndef __printf | |
60 | -#define __printf(a,b) __attribute__((format(printf,a,b))) | |
61 | -#endif | |
62 | -#ifndef __packed | |
63 | -#define __packed __attribute__((__packed__)) | |
64 | -#endif | |
65 | -#ifndef bool | |
66 | -#define bool _Bool | |
67 | -#endif | |
68 | -#ifndef false | |
69 | -#define false 0 | |
70 | -#endif | |
71 | -#ifndef true | |
72 | -#define true 1 | |
73 | -#endif | |
74 | - | |
75 | -#ifndef __user | |
76 | -#define __user | |
77 | -#endif | |
78 | - | |
79 | -#ifndef current_uid | |
80 | -#define current_uid() (current->uid) | |
81 | -#endif | |
82 | -#ifndef current_gid | |
83 | -#define current_gid() (current->gid) | |
84 | -#endif | |
85 | -#ifndef current_euid | |
86 | -#define current_euid() (current->euid) | |
87 | -#endif | |
88 | -#ifndef current_egid | |
89 | -#define current_egid() (current->egid) | |
90 | -#endif | |
91 | -#ifndef current_suid | |
92 | -#define current_suid() (current->suid) | |
93 | -#endif | |
94 | -#ifndef current_sgid | |
95 | -#define current_sgid() (current->sgid) | |
96 | -#endif | |
97 | -#ifndef current_fsuid | |
98 | -#define current_fsuid() (current->fsuid) | |
99 | -#endif | |
100 | -#ifndef current_fsgid | |
101 | -#define current_fsgid() (current->fsgid) | |
102 | -#endif | |
103 | - | |
104 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) | |
105 | - | |
106 | -/** | |
107 | - * __list_del_entry - Deletes entry from list without re-initialization. | |
108 | - * | |
109 | - * @entry: Pointer to "struct list_head". | |
110 | - * | |
111 | - * Returns nothing. | |
112 | - * | |
113 | - * This is for compatibility with older kernels. | |
114 | - */ | |
115 | -static inline void __list_del_entry(struct list_head *entry) | |
116 | -{ | |
117 | - __list_del(entry->prev, entry->next); | |
118 | -} | |
119 | - | |
120 | -#endif | |
121 | - | |
122 | -#ifndef list_for_each_entry_safe | |
123 | - | |
124 | -/** | |
125 | - * list_for_each_entry_safe - Iterate over list of given type safe against removal of list entry. | |
126 | - * | |
127 | - * @pos: The "type *" to use as a loop cursor. | |
128 | - * @n: Another "type *" to use as temporary storage. | |
129 | - * @head: Pointer to "struct list_head". | |
130 | - * @member: The name of the list_struct within the struct. | |
131 | - * | |
132 | - * This is for compatibility with older kernels. | |
133 | - */ | |
134 | -#define list_for_each_entry_safe(pos, n, head, member) \ | |
135 | - for (pos = list_entry((head)->next, typeof(*pos), member), \ | |
136 | - n = list_entry(pos->member.next, typeof(*pos), member); \ | |
137 | - &pos->member != (head); \ | |
138 | - pos = n, n = list_entry(n->member.next, typeof(*n), member)) | |
139 | - | |
140 | -#endif | |
141 | - | |
142 | -#ifndef srcu_dereference | |
143 | - | |
144 | -/** | |
145 | - * srcu_dereference - Fetch SRCU-protected pointer with checking. | |
146 | - * | |
147 | - * @p: The pointer to read, prior to dereferencing. | |
148 | - * @ss: Pointer to "struct srcu_struct". | |
149 | - * | |
150 | - * Returns @p. | |
151 | - * | |
152 | - * This is for compatibility with older kernels. | |
153 | - */ | |
154 | -#define srcu_dereference(p, ss) rcu_dereference(p) | |
155 | - | |
156 | -#endif | |
157 | - | |
158 | -#ifndef list_for_each_entry_srcu | |
159 | - | |
160 | -/** | |
161 | - * list_for_each_entry_srcu - Iterate over rcu list of given type. | |
162 | - * | |
163 | - * @pos: The type * to use as a loop cursor. | |
164 | - * @head: The head for your list. | |
165 | - * @member: The name of the list_struct within the struct. | |
166 | - * @ss: Pointer to "struct srcu_struct". | |
167 | - * | |
168 | - * As of 2.6.36, this macro is not provided because only TOMOYO wants it. | |
169 | - */ | |
170 | -#define list_for_each_entry_srcu(pos, head, member, ss) \ | |
171 | - for (pos = list_entry(srcu_dereference((head)->next, ss), \ | |
172 | - typeof(*pos), member); \ | |
173 | - prefetch(pos->member.next), &pos->member != (head); \ | |
174 | - pos = list_entry(srcu_dereference(pos->member.next, ss), \ | |
175 | - typeof(*pos), member)) | |
176 | - | |
177 | -#endif | |
178 | - | |
179 | -/* | |
180 | - * TOMOYO specific part start. | |
181 | - */ | |
182 | - | |
183 | -#include <linux/ccsecurity.h> | |
184 | - | |
185 | -/* Enumeration definition for internal use. */ | |
186 | - | |
187 | -/* Index numbers for "struct ccs_condition". */ | |
188 | -enum ccs_conditions_index { | |
189 | - /* 0 */ | |
190 | - CCS_SELF_UID, /* current_uid() */ | |
191 | - CCS_SELF_EUID, /* current_euid() */ | |
192 | - CCS_SELF_SUID, /* current_suid() */ | |
193 | - CCS_SELF_FSUID, /* current_fsuid() */ | |
194 | - CCS_SELF_GID, /* current_gid() */ | |
195 | - CCS_SELF_EGID, /* current_egid() */ | |
196 | - CCS_SELF_SGID, /* current_sgid() */ | |
197 | - CCS_SELF_FSGID, /* current_fsgid() */ | |
198 | - CCS_SELF_PID, /* sys_getpid() */ | |
199 | - CCS_SELF_PPID, /* sys_getppid() */ | |
200 | - /* 10 */ | |
201 | - CCS_TASK_TYPE, /* ((u8) task->ccs_flags) & | |
202 | - CCS_TASK_IS_EXECUTE_HANDLER */ | |
203 | - CCS_SELF_DOMAIN, | |
204 | - CCS_SELF_EXE, | |
205 | - CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ | |
206 | - CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ | |
207 | - CCS_OBJ_IS_SOCKET, /* S_IFSOCK */ | |
208 | - CCS_OBJ_IS_SYMLINK, /* S_IFLNK */ | |
209 | - CCS_OBJ_IS_FILE, /* S_IFREG */ | |
210 | - CCS_OBJ_IS_BLOCK_DEV, /* S_IFBLK */ | |
211 | - CCS_OBJ_IS_DIRECTORY, /* S_IFDIR */ | |
212 | - /* 20 */ | |
213 | - CCS_OBJ_IS_CHAR_DEV, /* S_IFCHR */ | |
214 | - CCS_OBJ_IS_FIFO, /* S_IFIFO */ | |
215 | - CCS_MODE_SETUID, /* S_ISUID */ | |
216 | - CCS_MODE_SETGID, /* S_ISGID */ | |
217 | - CCS_MODE_STICKY, /* S_ISVTX */ | |
218 | - CCS_MODE_OWNER_READ, /* S_IRUSR */ | |
219 | - CCS_MODE_OWNER_WRITE, /* S_IWUSR */ | |
220 | - CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ | |
221 | - CCS_MODE_GROUP_READ, /* S_IRGRP */ | |
222 | - CCS_MODE_GROUP_WRITE, /* S_IWGRP */ | |
223 | - /* 30 */ | |
224 | - CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ | |
225 | - CCS_MODE_OTHERS_READ, /* S_IROTH */ | |
226 | - CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ | |
227 | - CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ | |
228 | - CCS_TASK_EXECUTE_HANDLER, /* CCS_TASK_IS_EXECUTE_HANDLER */ | |
229 | - CCS_HANDLER_PATH, | |
230 | - CCS_TRANSIT_DOMAIN, | |
231 | - CCS_MAX_CONDITION_KEYWORD, | |
232 | - CCS_COND_SARG0, | |
233 | - CCS_COND_SARG1, | |
234 | - /* 40 */ | |
235 | - CCS_COND_SARG2, | |
236 | - CCS_COND_SARG3, | |
237 | - CCS_COND_NARG0, | |
238 | - CCS_COND_NARG1, | |
239 | - CCS_COND_NARG2, | |
240 | - CCS_COND_IPARG, | |
241 | - CCS_COND_DOMAIN, | |
242 | - CCS_IMM_GROUP, | |
243 | - CCS_IMM_NAME_ENTRY, | |
244 | - CCS_IMM_NUMBER_ENTRY1, | |
245 | - /* 50 */ | |
246 | - CCS_IMM_NUMBER_ENTRY2, | |
247 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
248 | - CCS_IMM_IPV4ADDR_ENTRY1, | |
249 | - CCS_IMM_IPV4ADDR_ENTRY2, | |
250 | - CCS_IMM_IPV6ADDR_ENTRY1, | |
251 | - CCS_IMM_IPV6ADDR_ENTRY2, | |
252 | -#endif | |
253 | - CCS_ARGV_ENTRY, | |
254 | - CCS_ENVP_ENTRY, | |
255 | - CCS_PATH_ATTRIBUTE_START = 192, | |
256 | - CCS_PATH_ATTRIBUTE_END = 255 | |
257 | -} __packed; | |
258 | - | |
259 | -enum ccs_path_attribute_index { | |
260 | - CCS_PATH_ATTRIBUTE_UID, | |
261 | - CCS_PATH_ATTRIBUTE_GID, | |
262 | - CCS_PATH_ATTRIBUTE_INO, | |
263 | - CCS_PATH_ATTRIBUTE_TYPE, | |
264 | - CCS_PATH_ATTRIBUTE_MAJOR, | |
265 | - CCS_PATH_ATTRIBUTE_MINOR, | |
266 | - CCS_PATH_ATTRIBUTE_PERM, | |
267 | - CCS_PATH_ATTRIBUTE_DEV_MAJOR, | |
268 | - CCS_PATH_ATTRIBUTE_DEV_MINOR, | |
269 | - CCS_PATH_ATTRIBUTE_FSMAGIC, | |
270 | - CCS_MAX_PATH_ATTRIBUTE | |
271 | -} __packed; | |
272 | - | |
273 | -/* Index numbers for group entries. */ | |
274 | -enum ccs_group_id { | |
275 | - CCS_STRING_GROUP, | |
276 | - CCS_NUMBER_GROUP, | |
277 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
278 | - CCS_IP_GROUP, | |
279 | -#endif | |
280 | - CCS_MAX_GROUP | |
281 | -} __packed; | |
282 | - | |
283 | -/* Index numbers for functionality. */ | |
284 | -enum ccs_mac_index { | |
285 | - CCS_MAC_EXECUTE, | |
286 | - CCS_MAC_READ, | |
287 | - CCS_MAC_WRITE, | |
288 | - CCS_MAC_APPEND, | |
289 | - CCS_MAC_CREATE, | |
290 | - CCS_MAC_UNLINK, | |
291 | -#ifdef CONFIG_CCSECURITY_GETATTR | |
292 | - CCS_MAC_GETATTR, | |
293 | -#endif | |
294 | - CCS_MAC_MKDIR, | |
295 | - CCS_MAC_RMDIR, | |
296 | - CCS_MAC_MKFIFO, | |
297 | - CCS_MAC_MKSOCK, | |
298 | - CCS_MAC_TRUNCATE, | |
299 | - CCS_MAC_SYMLINK, | |
300 | - CCS_MAC_MKBLOCK, | |
301 | - CCS_MAC_MKCHAR, | |
302 | - CCS_MAC_LINK, | |
303 | - CCS_MAC_RENAME, | |
304 | - CCS_MAC_CHMOD, | |
305 | - CCS_MAC_CHOWN, | |
306 | - CCS_MAC_CHGRP, | |
307 | - CCS_MAC_IOCTL, | |
308 | - CCS_MAC_CHROOT, | |
309 | - CCS_MAC_MOUNT, | |
310 | - CCS_MAC_UMOUNT, | |
311 | - CCS_MAC_PIVOT_ROOT, | |
312 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
313 | - CCS_MAC_INET_STREAM_BIND, | |
314 | - CCS_MAC_INET_STREAM_LISTEN, | |
315 | - CCS_MAC_INET_STREAM_CONNECT, | |
316 | - CCS_MAC_INET_STREAM_ACCEPT, | |
317 | - CCS_MAC_INET_DGRAM_BIND, | |
318 | - CCS_MAC_INET_DGRAM_SEND, | |
319 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
320 | - CCS_MAC_INET_DGRAM_RECV, | |
321 | -#endif | |
322 | - CCS_MAC_INET_RAW_BIND, | |
323 | - CCS_MAC_INET_RAW_SEND, | |
324 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
325 | - CCS_MAC_INET_RAW_RECV, | |
326 | -#endif | |
327 | - CCS_MAC_UNIX_STREAM_BIND, | |
328 | - CCS_MAC_UNIX_STREAM_LISTEN, | |
329 | - CCS_MAC_UNIX_STREAM_CONNECT, | |
330 | - CCS_MAC_UNIX_STREAM_ACCEPT, | |
331 | - CCS_MAC_UNIX_DGRAM_BIND, | |
332 | - CCS_MAC_UNIX_DGRAM_SEND, | |
333 | -#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
334 | - CCS_MAC_UNIX_DGRAM_RECV, | |
335 | -#endif | |
336 | - CCS_MAC_UNIX_SEQPACKET_BIND, | |
337 | - CCS_MAC_UNIX_SEQPACKET_LISTEN, | |
338 | - CCS_MAC_UNIX_SEQPACKET_CONNECT, | |
339 | - CCS_MAC_UNIX_SEQPACKET_ACCEPT, | |
340 | -#endif | |
341 | -#ifdef CONFIG_CCSECURITY_ENVIRON | |
342 | - CCS_MAC_ENVIRON, | |
343 | -#endif | |
344 | -#ifdef CONFIG_CCSECURITY_PTRACE | |
345 | - CCS_MAC_PTRACE, | |
346 | -#endif | |
347 | -#ifdef CONFIG_CCSECURITY_SIGNAL | |
348 | - CCS_MAC_SIGNAL, | |
349 | -#endif | |
350 | - CCS_MAC_MODIFY_POLICY, | |
351 | -#ifdef CONFIG_CCSECURITY_CAPABILITY | |
352 | - CCS_MAC_USE_NETLINK_SOCKET, | |
353 | - CCS_MAC_USE_PACKET_SOCKET, | |
354 | - CCS_MAC_USE_REBOOT, | |
355 | - CCS_MAC_USE_VHANGUP, | |
356 | - CCS_MAC_SET_TIME, | |
357 | - CCS_MAC_SET_PRIORITY, | |
358 | - CCS_MAC_SET_HOSTNAME, | |
359 | - CCS_MAC_USE_KERNEL_MODULE, | |
360 | - CCS_MAC_USE_NEW_KERNEL, | |
361 | -#endif | |
362 | -#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
363 | - CCS_MAC_AUTO_DOMAIN_TRANSITION, | |
364 | -#endif | |
365 | -#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
366 | - CCS_MAC_MANUAL_DOMAIN_TRANSITION, | |
367 | -#endif | |
368 | - CCS_MAX_MAC_INDEX | |
369 | -} __packed; | |
370 | - | |
371 | -/* Index numbers for statistic information. */ | |
372 | -enum ccs_memory_stat_type { | |
373 | - CCS_MEMORY_POLICY, | |
374 | - CCS_MEMORY_AUDIT, | |
375 | - CCS_MEMORY_QUERY, | |
376 | - CCS_MAX_MEMORY_STAT | |
377 | -} __packed; | |
378 | - | |
379 | -enum ccs_matching_result { | |
380 | - CCS_MATCHING_UNMATCHED, | |
381 | - CCS_MATCHING_ALLOWED, | |
382 | - CCS_MATCHING_DENIED, | |
383 | - CCS_MAX_MATCHING | |
384 | -} __packed; | |
385 | - | |
386 | -/* Index numbers for stat(). */ | |
387 | -enum ccs_path_stat_index { | |
388 | - /* Do not change this order. */ | |
389 | - CCS_PATH1, | |
390 | - CCS_PATH1_PARENT, | |
391 | - CCS_PATH2, | |
392 | - CCS_PATH2_PARENT, | |
393 | - CCS_MAX_PATH_STAT | |
394 | -} __packed; | |
395 | - | |
396 | -/* Index numbers for entry type. */ | |
397 | -enum ccs_policy_id { | |
398 | - CCS_ID_GROUP, | |
399 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
400 | - CCS_ID_IP_GROUP, | |
401 | -#endif | |
402 | - CCS_ID_STRING_GROUP, | |
403 | - CCS_ID_NUMBER_GROUP, | |
404 | - CCS_ID_CONDITION, | |
405 | - CCS_ID_NAME, | |
406 | - CCS_ID_ACL, | |
407 | - CCS_ID_DOMAIN, | |
408 | - CCS_MAX_POLICY | |
409 | -} __packed; | |
410 | - | |
411 | -/* Index numbers for statistic information. */ | |
412 | -enum ccs_policy_stat_type { | |
413 | - CCS_STAT_POLICY_UPDATES, | |
414 | - CCS_STAT_REQUEST_DENIED, | |
415 | - CCS_MAX_POLICY_STAT | |
416 | -} __packed; | |
417 | - | |
418 | -/* Index numbers for /proc/ccs/ interfaces. */ | |
419 | -enum ccs_proc_interface_index { | |
420 | - CCS_POLICY, | |
421 | - CCS_PROCESS_STATUS, | |
422 | - CCS_AUDIT, | |
423 | - CCS_VERSION, | |
424 | - CCS_QUERY, | |
425 | -#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
426 | - CCS_EXECUTE_HANDLER, | |
427 | -#endif | |
428 | -} __packed; | |
429 | - | |
430 | -/* Index numbers for special mount operations. */ | |
431 | -enum ccs_special_mount { | |
432 | - CCS_MOUNT_BIND, /* mount --bind /source /dest */ | |
433 | - CCS_MOUNT_MOVE, /* mount --move /old /new */ | |
434 | - CCS_MOUNT_REMOUNT, /* mount -o remount /dir */ | |
435 | - CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ | |
436 | - CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ | |
437 | - CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ | |
438 | - CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ | |
439 | - CCS_MAX_SPECIAL_MOUNT | |
440 | -} __packed; | |
441 | - | |
442 | -/* Index numbers for type of numeric values. */ | |
443 | -enum ccs_value_type { | |
444 | - CCS_VALUE_TYPE_INVALID, | |
445 | - CCS_VALUE_TYPE_DECIMAL, | |
446 | - CCS_VALUE_TYPE_OCTAL, | |
447 | - CCS_VALUE_TYPE_HEXADECIMAL, | |
448 | -} __packed; | |
449 | - | |
450 | -/* Index numbers for type of IP addresses. */ | |
451 | -enum ccs_ipaddr_type { | |
452 | - CCS_ADDRESS_TYPE_INVALID, | |
453 | - CCS_ADDRESS_TYPE_IPV4, | |
454 | - CCS_ADDRESS_TYPE_IPV4_RANGE, | |
455 | - CCS_ADDRESS_TYPE_IPV6, | |
456 | - CCS_ADDRESS_TYPE_IPV6_RANGE, | |
457 | -} __packed; | |
458 | - | |
459 | -/* Constants definition for internal use. */ | |
460 | - | |
461 | -/* | |
462 | - * TOMOYO uses this hash only when appending a string into the string table. | |
463 | - * Frequency of appending strings is very low. So we don't need large (e.g. | |
464 | - * 64k) hash size. 256 will be sufficient. | |
465 | - */ | |
466 | -#define CCS_HASH_BITS 8 | |
467 | -#define CCS_MAX_HASH (1u << CCS_HASH_BITS) | |
468 | - | |
469 | -/* | |
470 | - * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. | |
471 | - * Therefore, we don't need SOCK_MAX. | |
472 | - */ | |
473 | -#define CCS_SOCK_MAX 6 | |
474 | - | |
475 | -/* Size of temporary buffer for execve() operation. */ | |
476 | -#define CCS_EXEC_TMPSIZE 4096 | |
477 | - | |
478 | -/* Patterns for auditing logs quota. */ | |
479 | -#define CCS_MAX_LOG_QUOTA 256 | |
480 | - | |
481 | -/* Garbage collector is trying to kfree() this element. */ | |
482 | -#define CCS_GC_IN_PROGRESS -1 | |
483 | - | |
484 | -/* Current thread is doing open(3) ? */ | |
485 | -#define CCS_OPEN_FOR_IOCTL_ONLY 2 | |
486 | -/* Current thread is doing do_execve() ? */ | |
487 | -#define CCS_TASK_IS_IN_EXECVE 4 | |
488 | -/* Current thread is running as an execute handler program? */ | |
489 | -#define CCS_TASK_IS_EXECUTE_HANDLER 8 | |
490 | -/* Current thread is allowed to modify policy via /proc/ccs/ interface? */ | |
491 | -#define CCS_TASK_IS_MANAGER 16 | |
492 | - | |
493 | -/* | |
494 | - * Retry this request. Returned by ccs_supervisor() if policy violation has | |
495 | - * occurred in enforcing mode and the userspace daemon decided to retry. | |
496 | - * | |
497 | - * We must choose a positive value in order to distinguish "granted" (which is | |
498 | - * 0) and "rejected" (which is a negative value) and "retry". | |
499 | - */ | |
500 | -#define CCS_RETRY_REQUEST 1 | |
501 | - | |
502 | -/* Size of read buffer for /proc/ccs/ interface. */ | |
503 | -#define CCS_MAX_IO_READ_QUEUE 64 | |
504 | - | |
505 | -/* Structure definition for internal use. */ | |
506 | - | |
507 | -/* Common header for holding ACL entries. */ | |
508 | -struct ccs_acl_head { | |
509 | - struct list_head list; | |
510 | - s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ | |
511 | -} __packed; | |
512 | - | |
513 | -/* Common header for shared entries. */ | |
514 | -struct ccs_shared_acl_head { | |
515 | - struct list_head list; | |
516 | - atomic_t users; | |
517 | -} __packed; | |
518 | - | |
519 | -/* Common header for individual entries. */ | |
520 | -struct ccs_acl_info { | |
521 | - struct list_head list; | |
522 | - struct list_head acl_info_list; | |
523 | - struct ccs_condition *cond; /* Maybe NULL. */ | |
524 | - bool is_deleted; | |
525 | - bool is_deny; | |
526 | - u16 priority; | |
527 | - u8 audit; | |
528 | -}; | |
529 | - | |
530 | -/* Structure for "string_group"/"number_group"/"ip_group" directive. */ | |
531 | -struct ccs_group { | |
532 | - struct ccs_shared_acl_head head; | |
533 | - /* Name of group (without leading "@"). */ | |
534 | - const struct ccs_path_info *group_name; | |
535 | - /* | |
536 | - * List of "struct ccs_string_group" or "struct ccs_number_group" or | |
537 | - * "struct ccs_ip_group". | |
538 | - */ | |
539 | - struct list_head member_list; | |
540 | -}; | |
541 | - | |
542 | -/* Structure for "string_group" directive. */ | |
543 | -struct ccs_string_group { | |
544 | - struct ccs_acl_head head; | |
545 | - const struct ccs_path_info *member_name; | |
546 | -}; | |
547 | - | |
548 | -/* Structure for "number_group" directive. */ | |
549 | -struct ccs_number_group { | |
550 | - struct ccs_acl_head head; | |
551 | - u8 radix; | |
552 | - unsigned long value[2]; | |
553 | -}; | |
554 | - | |
555 | -/* Structure for "ip_group" directive. */ | |
556 | -struct ccs_ip_group { | |
557 | - struct ccs_acl_head head; | |
558 | - bool is_ipv6; | |
559 | - /* Structure for holding an IP address. */ | |
560 | - struct in6_addr ip[2]; /* Big endian. */ | |
561 | -}; | |
562 | - | |
563 | -/* Subset of "struct stat". Used by conditional ACL and audit logs. */ | |
564 | -struct ccs_mini_stat { | |
565 | - uid_t uid; | |
566 | - gid_t gid; | |
567 | - ino_t ino; | |
568 | - umode_t mode; | |
569 | - dev_t dev; | |
570 | - dev_t rdev; | |
571 | - unsigned long fsmagic; | |
572 | -}; | |
573 | - | |
574 | -/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */ | |
575 | -struct ccs_page_dump { | |
576 | - struct page *page; /* Previously dumped page. */ | |
577 | - char *data; /* Contents of "page". Size is PAGE_SIZE. */ | |
578 | -}; | |
579 | - | |
580 | -/* Structure for entries which follows "struct ccs_condition". */ | |
581 | -union ccs_condition_element { | |
582 | - struct { | |
583 | - enum ccs_conditions_index left; | |
584 | - enum ccs_conditions_index right; | |
585 | - bool is_not; | |
586 | - u8 radix; | |
587 | - }; | |
588 | - struct ccs_group *group; | |
589 | - const struct ccs_path_info *path; | |
590 | - u32 ip; /* Repeat 4 times if IPv6 address. */ | |
591 | - unsigned long value; | |
592 | -}; | |
593 | - | |
594 | -/* Structure for optional arguments. */ | |
595 | -struct ccs_condition { | |
596 | - struct ccs_shared_acl_head head; | |
597 | - u32 size; /* Memory size allocated for this entry. */ | |
598 | - /* union ccs_condition_element condition[]; */ | |
599 | -}; | |
600 | - | |
601 | -/* Structure for holding a token. */ | |
602 | -struct ccs_path_info { | |
603 | - const char *name; | |
604 | - u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
605 | - u32 total_len; /* = strlen(name) */ | |
606 | - u32 const_len; /* = ccs_const_part_length(name) */ | |
607 | -}; | |
608 | - | |
609 | -/* Structure for request info. */ | |
610 | -struct ccs_request_info { | |
611 | - /* For holding parameters. */ | |
612 | - struct ccs_request_param { | |
613 | - const struct ccs_path_info *s[4]; | |
614 | - unsigned long i[3]; | |
615 | -#ifdef CONFIG_CCSECURITY_NETWORK | |
616 | - const u8 *ip; /* Big endian. */ | |
617 | - bool is_ipv6; | |
618 | -#endif | |
619 | - } param; | |
620 | - /* For holding pathnames and attributes. */ | |
621 | - struct { | |
622 | - /* | |
623 | - * True if ccs_get_attributes() was already called, false | |
624 | - * otherwise. | |
625 | - */ | |
626 | - bool validate_done; | |
627 | - /* True if @stat[] is valid. */ | |
628 | - bool stat_valid[CCS_MAX_PATH_STAT]; | |
629 | - /* Pointer to file objects. */ | |
630 | - struct path path[2]; | |
631 | - /* | |
632 | - * Information on @path[0], @path[0]'s parent directory, | |
633 | - * @path[1] and @path[1]'s parent directory. | |
634 | - */ | |
635 | - struct ccs_mini_stat stat[CCS_MAX_PATH_STAT]; | |
636 | - /* | |
637 | - * Name of @path[0] and @path[1]. | |
638 | - * Cleared by ccs_crear_request_info(). | |
639 | - */ | |
640 | - struct ccs_path_info pathname[2]; | |
641 | - } obj; | |
642 | - struct { | |
643 | - struct linux_binprm *bprm; | |
644 | - struct ccs_domain_info *previous_domain; | |
645 | - /* For execute_handler. */ | |
646 | - char *handler; /* kstrdup(handler_path->name, GFP_NOFS) */ | |
647 | - /* For dumping argv[] and envp[]. */ | |
648 | - struct ccs_page_dump dump; | |
649 | - /* For temporary use. Size is CCS_EXEC_TMPSIZE bytes. */ | |
650 | - char *tmp; | |
651 | - }; | |
652 | - /* | |
653 | - * Name of current thread's executable. | |
654 | - * Cleared by ccs_crear_request_info(). | |
655 | - */ | |
656 | - struct ccs_path_info exename; | |
657 | - /* | |
658 | - * Matching "struct ccs_acl_info" is copied. Used for ccs-queryd. | |
659 | - * Valid until ccs_read_unlock(). | |
660 | - */ | |
661 | - struct ccs_acl_info *matched_acl; | |
662 | - /* | |
663 | - * Matching handler and domain transition are copied. | |
664 | - * Valid until ccs_read_unlock(). | |
665 | - */ | |
666 | - const struct ccs_path_info *handler_path; | |
667 | - const struct ccs_path_info *transition; | |
668 | - const struct ccs_path_info *handler_path_candidate; | |
669 | - const struct ccs_path_info *transition_candidate; | |
670 | - /* | |
671 | - * For holding operation index used for this request. | |
672 | - * One of values in "enum ccs_mac_index". | |
673 | - */ | |
674 | - enum ccs_mac_index type; | |
675 | - /* For holding matching result. */ | |
676 | - enum ccs_matching_result result; | |
677 | - /* | |
678 | - * For counting number of retries made for this request. | |
679 | - * This counter is incremented whenever ccs_supervisor() returned | |
680 | - * CCS_RETRY_REQUEST. | |
681 | - */ | |
682 | - u8 retry; | |
683 | - /* For holding max audit log count for this matching entry. */ | |
684 | - u8 audit; | |
685 | - /* | |
686 | - * Set to true if condition could not be checked due to out of memory. | |
687 | - * This flag is used for returning out of memory flag back to | |
688 | - * ccs_check_acl_list(). Thus, this flag will not be set if out of | |
689 | - * memory occurred before ccs_check_acl_list() is called. | |
690 | - */ | |
691 | - bool failed_by_oom; | |
692 | -}; | |
693 | - | |
694 | -/* Structure for domain information. */ | |
695 | -struct ccs_domain_info { | |
696 | - struct list_head list; | |
697 | - /* Name of this domain. Never NULL. */ | |
698 | - const struct ccs_path_info *domainname; | |
699 | -}; | |
700 | - | |
701 | -/* Structure for holding string data. */ | |
702 | -struct ccs_name { | |
703 | - struct ccs_shared_acl_head head; | |
704 | - int size; /* Memory size allocated for this entry. */ | |
705 | - struct ccs_path_info entry; | |
706 | -}; | |
707 | - | |
708 | -/* Structure for reading/writing policy via /proc/ccs/ interfaces. */ | |
709 | -struct ccs_io_buffer { | |
710 | - /* Exclusive lock for this structure. */ | |
711 | - struct mutex io_sem; | |
712 | - char __user *read_user_buf; | |
713 | - size_t read_user_buf_avail; | |
714 | - struct { | |
715 | - struct list_head *group; | |
716 | - struct list_head *acl; | |
717 | - struct list_head *subacl; | |
718 | - const union ccs_condition_element *cond; | |
719 | - size_t avail; | |
720 | - unsigned int step; | |
721 | - unsigned int query_index; | |
722 | - u16 index; | |
723 | - u8 cond_step; | |
724 | - u8 w_pos; | |
725 | - enum ccs_mac_index acl_index; | |
726 | - bool eof; | |
727 | - bool print_this_acl_only; | |
728 | - bool version_done; | |
729 | - bool stat_done; | |
730 | - bool quota_done; | |
731 | - bool group_done; | |
732 | - const char *w[CCS_MAX_IO_READ_QUEUE]; | |
733 | - } r; | |
734 | - struct { | |
735 | - char *data; | |
736 | - struct ccs_acl_info *acl; | |
737 | - size_t avail; | |
738 | - enum ccs_mac_index acl_index; | |
739 | - bool is_delete; | |
740 | - bool is_deny; | |
741 | - u16 priority; | |
742 | - } w; | |
743 | - /* Buffer for reading. */ | |
744 | - char *read_buf; | |
745 | - /* Size of read buffer. */ | |
746 | - size_t readbuf_size; | |
747 | - /* Buffer for writing. */ | |
748 | - char *write_buf; | |
749 | - /* Size of write buffer. */ | |
750 | - size_t writebuf_size; | |
751 | - /* Type of interface. */ | |
752 | - enum ccs_proc_interface_index type; | |
753 | - /* Users counter protected by ccs_io_buffer_list_lock. */ | |
754 | - u8 users; | |
755 | - /* List for telling GC not to kfree() elements. */ | |
756 | - struct list_head list; | |
757 | -}; | |
758 | - | |
759 | -/* Structure for representing YYYY/MM/DD hh/mm/ss. */ | |
760 | -struct ccs_time { | |
761 | - u16 year; | |
762 | - u8 month; | |
763 | - u8 day; | |
764 | - u8 hour; | |
765 | - u8 min; | |
766 | - u8 sec; | |
767 | -}; | |
768 | - | |
769 | -/* Prototype definition for "struct ccsecurity_operations". */ | |
770 | - | |
771 | -void __init ccs_permission_init(void); | |
772 | -void __init ccs_mm_init(void); | |
773 | - | |
774 | -/* Prototype definition for internal use. */ | |
775 | - | |
776 | -bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | |
777 | - struct ccs_page_dump *dump); | |
778 | -bool ccs_get_exename(struct ccs_path_info *buf); | |
779 | -bool ccs_manager(void); | |
780 | -bool ccs_transit_domain(const char *domainname); | |
781 | -char *ccs_encode(const char *str); | |
782 | -char *ccs_encode2(const char *str, int str_len); | |
783 | -char *ccs_realpath(struct path *path); | |
784 | -char *ccs_get_exe(void); | |
785 | -int ccs_audit_log(struct ccs_request_info *r); | |
786 | -int ccs_check_acl(struct ccs_request_info *r, const bool clear); | |
787 | -void ccs_del_condition(struct list_head *element); | |
788 | -void ccs_fill_path_info(struct ccs_path_info *ptr); | |
789 | -void ccs_get_attributes(struct ccs_request_info *r); | |
790 | -void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); | |
791 | -void ccs_populate_patharg(struct ccs_request_info *r, const bool first); | |
792 | -void ccs_transition_failed(const char *domainname); | |
793 | -void ccs_warn_oom(const char *function); | |
794 | - | |
795 | -/* Variable definition for internal use. */ | |
796 | - | |
797 | -extern bool ccs_policy_loaded; | |
798 | -extern struct ccs_domain_info ccs_kernel_domain; | |
799 | -extern struct ccs_path_info ccs_null_name; | |
800 | -extern struct list_head ccs_acl_list[CCS_MAX_MAC_INDEX]; | |
801 | -extern struct list_head ccs_condition_list; | |
802 | -extern struct list_head ccs_domain_list; | |
803 | -extern struct list_head ccs_group_list[CCS_MAX_GROUP]; | |
804 | -extern struct list_head ccs_name_list[CCS_MAX_HASH]; | |
805 | -extern struct mutex ccs_policy_lock; | |
806 | -extern struct srcu_struct ccs_ss; | |
807 | -extern unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | |
808 | - | |
809 | -/* Inlined functions for internal use. */ | |
810 | - | |
811 | -/** | |
812 | - * ccs_pathcmp - strcmp() for "struct ccs_path_info" structure. | |
813 | - * | |
814 | - * @a: Pointer to "struct ccs_path_info". | |
815 | - * @b: Pointer to "struct ccs_path_info". | |
816 | - * | |
817 | - * Returns true if @a != @b, false otherwise. | |
818 | - */ | |
819 | -static inline bool ccs_pathcmp(const struct ccs_path_info *a, | |
820 | - const struct ccs_path_info *b) | |
821 | -{ | |
822 | - return a->hash != b->hash || strcmp(a->name, b->name); | |
823 | -} | |
824 | - | |
825 | -/** | |
826 | - * ccs_read_lock - Take lock for protecting policy. | |
827 | - * | |
828 | - * Returns index number for ccs_read_unlock(). | |
829 | - */ | |
830 | -static inline int ccs_read_lock(void) | |
831 | -{ | |
832 | - return srcu_read_lock(&ccs_ss); | |
833 | -} | |
834 | - | |
835 | -/** | |
836 | - * ccs_read_unlock - Release lock for protecting policy. | |
837 | - * | |
838 | - * @idx: Index number returned by ccs_read_lock(). | |
839 | - * | |
840 | - * Returns nothing. | |
841 | - */ | |
842 | -static inline void ccs_read_unlock(const int idx) | |
843 | -{ | |
844 | - srcu_read_unlock(&ccs_ss, idx); | |
845 | -} | |
846 | - | |
847 | -/** | |
848 | - * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". | |
849 | - * | |
850 | - * Returns nothing. | |
851 | - */ | |
852 | -static inline void ccs_tasklist_lock(void) | |
853 | -{ | |
854 | - rcu_read_lock(); | |
855 | -} | |
856 | - | |
857 | -/** | |
858 | - * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". | |
859 | - * | |
860 | - * Returns nothing. | |
861 | - */ | |
862 | -static inline void ccs_tasklist_unlock(void) | |
863 | -{ | |
864 | - rcu_read_unlock(); | |
865 | -} | |
866 | - | |
867 | -/** | |
868 | - * ccs_sys_getppid - Copy of getppid(). | |
869 | - * | |
870 | - * Returns parent process's PID. | |
871 | - * | |
872 | - * Alpha does not have getppid() defined. To be able to build this module on | |
873 | - * Alpha, I have to copy getppid() from kernel/timer.c. | |
874 | - */ | |
875 | -static inline pid_t ccs_sys_getppid(void) | |
876 | -{ | |
877 | - pid_t pid; | |
878 | - rcu_read_lock(); | |
879 | - pid = task_tgid_vnr(rcu_dereference(current->real_parent)); | |
880 | - rcu_read_unlock(); | |
881 | - return pid; | |
882 | -} | |
883 | - | |
884 | -/** | |
885 | - * ccs_sys_getpid - Copy of getpid(). | |
886 | - * | |
887 | - * Returns current thread's PID. | |
888 | - * | |
889 | - * Alpha does not have getpid() defined. To be able to build this module on | |
890 | - * Alpha, I have to copy getpid() from kernel/timer.c. | |
891 | - */ | |
892 | -static inline pid_t ccs_sys_getpid(void) | |
893 | -{ | |
894 | - return task_tgid_vnr(current); | |
895 | -} | |
896 | - | |
897 | -#if defined(CONFIG_SLOB) | |
898 | - | |
899 | -/** | |
900 | - * ccs_round2 - Round up to power of 2 for calculating memory usage. | |
901 | - * | |
902 | - * @size: Size to be rounded up. | |
903 | - * | |
904 | - * Returns @size. | |
905 | - * | |
906 | - * Since SLOB does not round up, this function simply returns @size. | |
907 | - */ | |
908 | -static inline int ccs_round2(size_t size) | |
909 | -{ | |
910 | - return size; | |
911 | -} | |
912 | - | |
913 | -#else | |
914 | - | |
915 | -/** | |
916 | - * ccs_round2 - Round up to power of 2 for calculating memory usage. | |
917 | - * | |
918 | - * @size: Size to be rounded up. | |
919 | - * | |
920 | - * Returns rounded size. | |
921 | - * | |
922 | - * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of | |
923 | - * (e.g.) 128 bytes. | |
924 | - */ | |
925 | -static inline int ccs_round2(size_t size) | |
926 | -{ | |
927 | -#if PAGE_SIZE == 4096 | |
928 | - size_t bsize = 32; | |
929 | -#else | |
930 | - size_t bsize = 64; | |
931 | -#endif | |
932 | - if (!size) | |
933 | - return 0; | |
934 | - while (size > bsize) | |
935 | - bsize <<= 1; | |
936 | - return bsize; | |
937 | -} | |
938 | - | |
939 | -#endif | |
940 | - | |
941 | -/** | |
942 | - * ccs_put_condition - Drop reference on "struct ccs_condition". | |
943 | - * | |
944 | - * @cond: Pointer to "struct ccs_condition". Maybe NULL. | |
945 | - * | |
946 | - * Returns nothing. | |
947 | - */ | |
948 | -static inline void ccs_put_condition(struct ccs_condition *cond) | |
949 | -{ | |
950 | - if (cond) | |
951 | - atomic_dec(&cond->head.users); | |
952 | -} | |
953 | - | |
954 | -/** | |
955 | - * ccs_put_group - Drop reference on "struct ccs_group". | |
956 | - * | |
957 | - * @group: Pointer to "struct ccs_group". Maybe NULL. | |
958 | - * | |
959 | - * Returns nothing. | |
960 | - */ | |
961 | -static inline void ccs_put_group(struct ccs_group *group) | |
962 | -{ | |
963 | - if (group) | |
964 | - atomic_dec(&group->head.users); | |
965 | -} | |
966 | - | |
967 | -/** | |
968 | - * ccs_put_name - Drop reference on "struct ccs_name". | |
969 | - * | |
970 | - * @name: Pointer to "struct ccs_path_info". Maybe NULL. | |
971 | - * | |
972 | - * Returns nothing. | |
973 | - */ | |
974 | -static inline void ccs_put_name(const struct ccs_path_info *name) | |
975 | -{ | |
976 | - if (name) | |
977 | - atomic_dec(&container_of(name, struct ccs_name, entry)-> | |
978 | - head.users); | |
979 | -} | |
980 | - | |
981 | -/* For importing variables and functions. */ | |
982 | -extern const struct ccsecurity_exports ccsecurity_exports; | |
983 | - | |
984 | -#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
985 | - | |
986 | -/* | |
987 | - * Structure for holding "struct ccs_domain_info *" and "u32 ccs_flags" for | |
988 | - * each "struct task_struct". | |
989 | - * | |
990 | - * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | |
991 | - * are maintained outside that "struct task_struct". Therefore, ccs_security | |
992 | - * != task_struct . This keeps KABI for distributor's prebuilt kernels but | |
993 | - * entails slow access. | |
994 | - * | |
995 | - * Memory for this structure is allocated when current thread tries to access | |
996 | - * it. Therefore, if memory allocation failed, current thread will be killed by | |
997 | - * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. | |
998 | - */ | |
999 | -struct ccs_security { | |
1000 | - struct list_head list; | |
1001 | - const struct task_struct *task; | |
1002 | - struct ccs_domain_info *ccs_domain_info; | |
1003 | - u32 ccs_flags; | |
1004 | - struct rcu_head rcu; | |
1005 | -}; | |
1006 | - | |
1007 | -#define CCS_TASK_SECURITY_HASH_BITS 12 | |
1008 | -#define CCS_MAX_TASK_SECURITY_HASH (1u << CCS_TASK_SECURITY_HASH_BITS) | |
1009 | -extern struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | |
1010 | - | |
1011 | -struct ccs_security *ccs_find_task_security(const struct task_struct *task); | |
1012 | - | |
1013 | -/** | |
1014 | - * ccs_current_security - Get "struct ccs_security" for current thread. | |
1015 | - * | |
1016 | - * Returns pointer to "struct ccs_security" for current thread. | |
1017 | - */ | |
1018 | -static inline struct ccs_security *ccs_current_security(void) | |
1019 | -{ | |
1020 | - return ccs_find_task_security(current); | |
1021 | -} | |
1022 | - | |
1023 | -/** | |
1024 | - * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | |
1025 | - * | |
1026 | - * @task: Pointer to "struct task_struct". | |
1027 | - * | |
1028 | - * Returns pointer to "struct ccs_security" for specified thread. | |
1029 | - */ | |
1030 | -static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | |
1031 | -{ | |
1032 | - struct ccs_domain_info *domain; | |
1033 | - rcu_read_lock(); | |
1034 | - domain = ccs_find_task_security(task)->ccs_domain_info; | |
1035 | - rcu_read_unlock(); | |
1036 | - return domain; | |
1037 | -} | |
1038 | - | |
1039 | -/** | |
1040 | - * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | |
1041 | - * | |
1042 | - * Returns pointer to "struct ccs_domain_info" for current thread. | |
1043 | - */ | |
1044 | -static inline struct ccs_domain_info *ccs_current_domain(void) | |
1045 | -{ | |
1046 | - return ccs_find_task_security(current)->ccs_domain_info; | |
1047 | -} | |
1048 | - | |
1049 | -/** | |
1050 | - * ccs_task_flags - Get flags for specified thread. | |
1051 | - * | |
1052 | - * @task: Pointer to "struct task_struct". | |
1053 | - * | |
1054 | - * Returns flags for specified thread. | |
1055 | - */ | |
1056 | -static inline u32 ccs_task_flags(struct task_struct *task) | |
1057 | -{ | |
1058 | - u32 ccs_flags; | |
1059 | - rcu_read_lock(); | |
1060 | - ccs_flags = ccs_find_task_security(task)->ccs_flags; | |
1061 | - rcu_read_unlock(); | |
1062 | - return ccs_flags; | |
1063 | -} | |
1064 | - | |
1065 | -/** | |
1066 | - * ccs_current_flags - Get flags for current thread. | |
1067 | - * | |
1068 | - * Returns flags for current thread. | |
1069 | - */ | |
1070 | -static inline u32 ccs_current_flags(void) | |
1071 | -{ | |
1072 | - return ccs_find_task_security(current)->ccs_flags; | |
1073 | -} | |
1074 | - | |
1075 | -#else | |
1076 | - | |
1077 | -/* | |
1078 | - * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | |
1079 | - * are maintained inside that "struct task_struct". Therefore, ccs_security == | |
1080 | - * task_struct . This allows fast access but breaks KABI checks for | |
1081 | - * distributor's prebuilt kernels due to changes in "struct task_struct". | |
1082 | - */ | |
1083 | -#define ccs_security task_struct | |
1084 | - | |
1085 | -/** | |
1086 | - * ccs_find_task_security - Find "struct ccs_security" for given task. | |
1087 | - * | |
1088 | - * @task: Pointer to "struct task_struct". | |
1089 | - * | |
1090 | - * Returns pointer to "struct ccs_security". | |
1091 | - */ | |
1092 | -static inline struct ccs_security *ccs_find_task_security(struct task_struct * | |
1093 | - task) | |
1094 | -{ | |
1095 | - return task; | |
1096 | -} | |
1097 | - | |
1098 | -/** | |
1099 | - * ccs_current_security - Get "struct ccs_security" for current thread. | |
1100 | - * | |
1101 | - * Returns pointer to "struct ccs_security" for current thread. | |
1102 | - */ | |
1103 | -static inline struct ccs_security *ccs_current_security(void) | |
1104 | -{ | |
1105 | - return ccs_find_task_security(current); | |
1106 | -} | |
1107 | - | |
1108 | -/** | |
1109 | - * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | |
1110 | - * | |
1111 | - * @task: Pointer to "struct task_struct". | |
1112 | - * | |
1113 | - * Returns pointer to "struct ccs_security" for specified thread. | |
1114 | - */ | |
1115 | -static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | |
1116 | -{ | |
1117 | - struct ccs_domain_info *domain = task->ccs_domain_info; | |
1118 | - return domain ? domain : &ccs_kernel_domain; | |
1119 | -} | |
1120 | - | |
1121 | -/** | |
1122 | - * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | |
1123 | - * | |
1124 | - * Returns pointer to "struct ccs_domain_info" for current thread. | |
1125 | - * | |
1126 | - * If current thread does not belong to a domain (which is true for initial | |
1127 | - * init_task in order to hide ccs_kernel_domain from this module), | |
1128 | - * current thread enters into ccs_kernel_domain. | |
1129 | - */ | |
1130 | -static inline struct ccs_domain_info *ccs_current_domain(void) | |
1131 | -{ | |
1132 | - struct task_struct *task = current; | |
1133 | - if (!task->ccs_domain_info) | |
1134 | - task->ccs_domain_info = &ccs_kernel_domain; | |
1135 | - return task->ccs_domain_info; | |
1136 | -} | |
1137 | - | |
1138 | -/** | |
1139 | - * ccs_task_flags - Get flags for specified thread. | |
1140 | - * | |
1141 | - * @task: Pointer to "struct task_struct". | |
1142 | - * | |
1143 | - * Returns flags for specified thread. | |
1144 | - */ | |
1145 | -static inline u32 ccs_task_flags(struct task_struct *task) | |
1146 | -{ | |
1147 | - return ccs_find_task_security(task)->ccs_flags; | |
1148 | -} | |
1149 | - | |
1150 | -/** | |
1151 | - * ccs_current_flags - Get flags for current thread. | |
1152 | - * | |
1153 | - * Returns flags for current thread. | |
1154 | - */ | |
1155 | -static inline u32 ccs_current_flags(void) | |
1156 | -{ | |
1157 | - return ccs_find_task_security(current)->ccs_flags; | |
1158 | -} | |
1159 | - | |
1160 | -#endif | |
1161 | - | |
1162 | -#endif |
@@ -0,0 +1,229 @@ | ||
1 | +/* | |
2 | + * security/ccsecurity/load_policy.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include <linux/version.h> | |
10 | +#include <linux/module.h> | |
11 | +#include <linux/init.h> | |
12 | +#include <linux/binfmts.h> | |
13 | +#include <linux/sched.h> | |
14 | +#include <linux/fs.h> | |
15 | +#include <linux/namei.h> | |
16 | +#ifndef LOOKUP_POSITIVE | |
17 | +#define LOOKUP_POSITIVE 0 | |
18 | +#endif | |
19 | + | |
20 | +/* | |
21 | + * TOMOYO specific part start. | |
22 | + */ | |
23 | + | |
24 | +#include <linux/ccsecurity.h> | |
25 | + | |
26 | +/** | |
27 | + * ccs_setup - Set enable/disable upon boot. | |
28 | + * | |
29 | + * @str: "off" to disable, "on" to enable. | |
30 | + * | |
31 | + * Returns 0. | |
32 | + */ | |
33 | +static int __init ccs_setup(char *str) | |
34 | +{ | |
35 | + if (!strcmp(str, "off")) | |
36 | + ccsecurity_ops.disabled = 1; | |
37 | + else if (!strcmp(str, "on")) | |
38 | + ccsecurity_ops.disabled = 0; | |
39 | + return 0; | |
40 | +} | |
41 | + | |
42 | +__setup("caitsith=", ccs_setup); | |
43 | + | |
44 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
45 | + | |
46 | +/* Path to the policy loader. (default = CONFIG_CCSECURITY_POLICY_LOADER) */ | |
47 | +static const char *ccs_loader; | |
48 | + | |
49 | +/** | |
50 | + * ccs_loader_setup - Set policy loader. | |
51 | + * | |
52 | + * @str: Program to use as a policy loader (e.g. /sbin/caitsith-init ). | |
53 | + * | |
54 | + * Returns 0. | |
55 | + */ | |
56 | +static int __init ccs_loader_setup(char *str) | |
57 | +{ | |
58 | + ccs_loader = str; | |
59 | + return 0; | |
60 | +} | |
61 | + | |
62 | +__setup("CCS_loader=", ccs_loader_setup); | |
63 | + | |
64 | +/** | |
65 | + * ccs_policy_loader_exists - Check whether /sbin/caitsith-init exists. | |
66 | + * | |
67 | + * Returns true if /sbin/caitsith-init exists, false otherwise. | |
68 | + */ | |
69 | +static _Bool ccs_policy_loader_exists(void) | |
70 | +{ | |
71 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
72 | + struct path path; | |
73 | + if (!ccs_loader) | |
74 | + ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | |
75 | + if (kern_path(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | |
76 | + &path) == 0) { | |
77 | + path_put(&path); | |
78 | + return 1; | |
79 | + } | |
80 | +#else | |
81 | + struct nameidata nd; | |
82 | + if (!ccs_loader) | |
83 | + ccs_loader = CONFIG_CCSECURITY_POLICY_LOADER; | |
84 | + if (path_lookup(ccs_loader, LOOKUP_FOLLOW | LOOKUP_POSITIVE, | |
85 | + &nd) == 0) { | |
86 | + path_put(&nd.path); | |
87 | + return 1; | |
88 | + } | |
89 | +#endif | |
90 | + printk(KERN_INFO "Not activating Mandatory Access Control " | |
91 | + "as %s does not exist.\n", ccs_loader); | |
92 | + return 0; | |
93 | +} | |
94 | + | |
95 | +/* Path to the trigger. (default = CONFIG_CCSECURITY_ACTIVATION_TRIGGER) */ | |
96 | +static const char *ccs_trigger; | |
97 | + | |
98 | +/** | |
99 | + * ccs_trigger_setup - Set trigger for activation. | |
100 | + * | |
101 | + * @str: Program to use as an activation trigger (e.g. /sbin/init ). | |
102 | + * | |
103 | + * Returns 0. | |
104 | + */ | |
105 | +static int __init ccs_trigger_setup(char *str) | |
106 | +{ | |
107 | + ccs_trigger = str; | |
108 | + return 0; | |
109 | +} | |
110 | + | |
111 | +__setup("CCS_trigger=", ccs_trigger_setup); | |
112 | + | |
113 | +/** | |
114 | + * ccs_load_policy - Run external policy loader to load policy. | |
115 | + * | |
116 | + * @filename: The program about to start. | |
117 | + * | |
118 | + * Returns nothing. | |
119 | + * | |
120 | + * This function checks whether @filename is /sbin/init, and if so | |
121 | + * invoke /sbin/caitsith-init and wait for the termination of /sbin/caitsith-init | |
122 | + * and then continues invocation of /sbin/init. | |
123 | + * /sbin/caitsith-init reads policy files in /etc/caitsith/ directory and | |
124 | + * writes to /proc/caitsith/ interfaces. | |
125 | + */ | |
126 | +static void ccs_load_policy(const char *filename) | |
127 | +{ | |
128 | + static _Bool done; | |
129 | + if (ccsecurity_ops.disabled || done) | |
130 | + return; | |
131 | + if (!ccs_trigger) | |
132 | + ccs_trigger = CONFIG_CCSECURITY_ACTIVATION_TRIGGER; | |
133 | + if (strcmp(filename, ccs_trigger)) | |
134 | + return; | |
135 | + if (!ccs_policy_loader_exists()) | |
136 | + return; | |
137 | + done = 1; | |
138 | + { | |
139 | + char *argv[2]; | |
140 | + char *envp[3]; | |
141 | + printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | |
142 | + ccs_loader); | |
143 | + argv[0] = (char *) ccs_loader; | |
144 | + argv[1] = NULL; | |
145 | + envp[0] = "HOME=/"; | |
146 | + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
147 | + envp[2] = NULL; | |
148 | + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); | |
149 | + } | |
150 | + if (ccsecurity_ops.check_profile) | |
151 | + ccsecurity_ops.check_profile(); | |
152 | + else | |
153 | + panic("Failed to load policy."); | |
154 | +} | |
155 | + | |
156 | +#endif | |
157 | + | |
158 | +/** | |
159 | + * __ccs_search_binary_handler - Load policy before calling search_binary_handler(). | |
160 | + * | |
161 | + * @bprm: Pointer to "struct linux_binprm". | |
162 | + * @regs: Pointer to "struct pt_regs". | |
163 | + * | |
164 | + * Returns 0 on success, negative value otherwise. | |
165 | + */ | |
166 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
167 | + struct pt_regs *regs) | |
168 | +{ | |
169 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
170 | + ccs_load_policy(bprm->filename); | |
171 | +#endif | |
172 | + /* | |
173 | + * ccs_load_policy() executes /sbin/caitsith-init if bprm->filename is | |
174 | + * /sbin/init. /sbin/caitsith-init executes | |
175 | + * /etc/ccs/caitsith-load-module to load loadable kernel module. | |
176 | + * The loadable kernel module modifies "struct ccsecurity_ops". Thus, | |
177 | + * we need to transfer control to __ccs_search_binary_handler() in | |
178 | + * security/ccsecurity/permission.c if "struct ccsecurity_ops" was | |
179 | + * modified. | |
180 | + */ | |
181 | + if (ccsecurity_ops.search_binary_handler | |
182 | + != __ccs_search_binary_handler) | |
183 | + return ccsecurity_ops.search_binary_handler(bprm, regs); | |
184 | + return search_binary_handler(bprm, regs); | |
185 | +} | |
186 | + | |
187 | +/* | |
188 | + * Some exports for loadable kernel module part. | |
189 | + * | |
190 | + * Although scripts/checkpatch.pl complains about use of "extern" in C file, | |
191 | + * we don't put these into security/caitsith/internal.h because we want to | |
192 | + * split built-in part and loadable kernel module part. | |
193 | + */ | |
194 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | |
195 | +extern spinlock_t vfsmount_lock; | |
196 | +#endif | |
197 | + | |
198 | +/* For exporting variables and functions. */ | |
199 | +const struct ccsecurity_exports ccsecurity_exports = { | |
200 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
201 | + .load_policy = ccs_load_policy, | |
202 | +#endif | |
203 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
204 | + .d_absolute_path = d_absolute_path, | |
205 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
206 | + .__d_path = __d_path, | |
207 | +#else | |
208 | + .vfsmount_lock = &vfsmount_lock, | |
209 | +#endif | |
210 | + .find_task_by_vpid = find_task_by_vpid, | |
211 | + .find_task_by_pid_ns = find_task_by_pid_ns, | |
212 | +}; | |
213 | +#ifdef CONFIG_CCSECURITY_LKM | |
214 | +/* Only ccsecurity module need to access this struct. */ | |
215 | +EXPORT_SYMBOL_GPL(ccsecurity_exports); | |
216 | +#endif | |
217 | + | |
218 | +/* Members are updated by loadable kernel module. */ | |
219 | +struct ccsecurity_operations ccsecurity_ops = { | |
220 | + .search_binary_handler = __ccs_search_binary_handler, | |
221 | +#ifdef CONFIG_CCSECURITY_DISABLE_BY_DEFAULT | |
222 | + .disabled = 1, | |
223 | +#endif | |
224 | +}; | |
225 | +/* | |
226 | + * Non-GPL modules might need to access this struct via inlined functions | |
227 | + * embedded into include/linux/security.h and include/net/ip.h | |
228 | + */ | |
229 | +EXPORT_SYMBOL(ccsecurity_ops); |
@@ -0,0 +1,644 @@ | ||
1 | +/* | |
2 | + * security/ccsecurity/realpath.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
12 | +#include <linux/nsproxy.h> | |
13 | +#include <linux/mnt_namespace.h> | |
14 | +#endif | |
15 | + | |
16 | +/***** SECTION1: Constants definition *****/ | |
17 | + | |
18 | +#define SOCKFS_MAGIC 0x534F434B | |
19 | + | |
20 | +/***** SECTION2: Structure definition *****/ | |
21 | + | |
22 | +/***** SECTION3: Prototype definition section *****/ | |
23 | + | |
24 | +static char *ccs_get_absolute_path(struct path *path, char * const buffer, | |
25 | + const int buflen); | |
26 | +static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | |
27 | + const int buflen); | |
28 | +static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | |
29 | + const int buflen); | |
30 | +static char *ccs_get_socket_name(struct path *path, char * const buffer, | |
31 | + const int buflen); | |
32 | +static int ccs_const_part_length(const char *filename); | |
33 | + | |
34 | +/***** SECTION4: Standalone functions section *****/ | |
35 | + | |
36 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
37 | + | |
38 | +/** | |
39 | + * ccs_realpath_lock - Take locks for __d_path(). | |
40 | + * | |
41 | + * Returns nothing. | |
42 | + */ | |
43 | +static inline void ccs_realpath_lock(void) | |
44 | +{ | |
45 | + /* dcache_lock is locked by __d_path(). */ | |
46 | + /* vfsmount_lock is locked by __d_path(). */ | |
47 | +} | |
48 | + | |
49 | +/** | |
50 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
51 | + * | |
52 | + * Returns nothing. | |
53 | + */ | |
54 | +static inline void ccs_realpath_unlock(void) | |
55 | +{ | |
56 | + /* vfsmount_lock is unlocked by __d_path(). */ | |
57 | + /* dcache_lock is unlocked by __d_path(). */ | |
58 | +} | |
59 | + | |
60 | +#elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) | |
61 | + | |
62 | +/** | |
63 | + * ccs_realpath_lock - Take locks for __d_path(). | |
64 | + * | |
65 | + * Returns nothing. | |
66 | + */ | |
67 | +static inline void ccs_realpath_lock(void) | |
68 | +{ | |
69 | + spin_lock(&dcache_lock); | |
70 | + /* vfsmount_lock is locked by __d_path(). */ | |
71 | +} | |
72 | + | |
73 | +/** | |
74 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
75 | + * | |
76 | + * Returns nothing. | |
77 | + */ | |
78 | +static inline void ccs_realpath_unlock(void) | |
79 | +{ | |
80 | + /* vfsmount_lock is unlocked by __d_path(). */ | |
81 | + spin_unlock(&dcache_lock); | |
82 | +} | |
83 | + | |
84 | +#elif defined(D_PATH_DISCONNECT) && !defined(CONFIG_SUSE_KERNEL) | |
85 | + | |
86 | +/** | |
87 | + * ccs_realpath_lock - Take locks for __d_path(). | |
88 | + * | |
89 | + * Returns nothing. | |
90 | + * | |
91 | + * Original unambiguous-__d_path.diff in patches.apparmor.tar.bz2 inversed the | |
92 | + * order of holding dcache_lock and vfsmount_lock. That patch was applied on | |
93 | + * (at least) SUSE 11.1 and Ubuntu 8.10 and Ubuntu 9.04 kernels. | |
94 | + * | |
95 | + * However, that patch was updated to use original order and the updated patch | |
96 | + * is applied to (as far as I know) only SUSE kernels. | |
97 | + * | |
98 | + * Therefore, I need to use original order for SUSE 11.1 kernels and inversed | |
99 | + * order for other kernels. I detect it by checking D_PATH_DISCONNECT and | |
100 | + * CONFIG_SUSE_KERNEL. I don't know whether other distributions are using the | |
101 | + * updated patch or not. If you got deadlock, check fs/dcache.c for locking | |
102 | + * order, and add " && 0" to this "#elif " block if fs/dcache.c uses original | |
103 | + * order. | |
104 | + */ | |
105 | +static inline void ccs_realpath_lock(void) | |
106 | +{ | |
107 | + spin_lock(ccsecurity_exports.vfsmount_lock); | |
108 | + spin_lock(&dcache_lock); | |
109 | +} | |
110 | + | |
111 | +/** | |
112 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
113 | + * | |
114 | + * Returns nothing. | |
115 | + */ | |
116 | +static inline void ccs_realpath_unlock(void) | |
117 | +{ | |
118 | + spin_unlock(&dcache_lock); | |
119 | + spin_unlock(ccsecurity_exports.vfsmount_lock); | |
120 | +} | |
121 | + | |
122 | +#else | |
123 | + | |
124 | +/** | |
125 | + * ccs_realpath_lock - Take locks for __d_path(). | |
126 | + * | |
127 | + * Returns nothing. | |
128 | + */ | |
129 | +static inline void ccs_realpath_lock(void) | |
130 | +{ | |
131 | + spin_lock(&dcache_lock); | |
132 | + spin_lock(ccsecurity_exports.vfsmount_lock); | |
133 | +} | |
134 | + | |
135 | +/** | |
136 | + * ccs_realpath_unlock - Release locks for __d_path(). | |
137 | + * | |
138 | + * Returns nothing. | |
139 | + */ | |
140 | +static inline void ccs_realpath_unlock(void) | |
141 | +{ | |
142 | + spin_unlock(ccsecurity_exports.vfsmount_lock); | |
143 | + spin_unlock(&dcache_lock); | |
144 | +} | |
145 | + | |
146 | +#endif | |
147 | + | |
148 | +/***** SECTION5: Variables definition section *****/ | |
149 | + | |
150 | +/***** SECTION6: Dependent functions section *****/ | |
151 | + | |
152 | +/** | |
153 | + * ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. | |
154 | + * | |
155 | + * @path: Pointer to "struct path". | |
156 | + * @buffer: Pointer to buffer to return value in. | |
157 | + * @buflen: Sizeof @buffer. | |
158 | + * | |
159 | + * Returns the buffer on success, an error code otherwise. | |
160 | + * | |
161 | + * Caller holds the dcache_lock and vfsmount_lock. | |
162 | + * Based on __d_path() in fs/dcache.c | |
163 | + */ | |
164 | +static char *ccs_get_absolute_path(struct path *path, char * const buffer, | |
165 | + const int buflen) | |
166 | +{ | |
167 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
168 | + if (buflen < 256) | |
169 | + return ERR_PTR(-ENOMEM); | |
170 | + return ccsecurity_exports.d_absolute_path(path, buffer, buflen - 1); | |
171 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
172 | + /* | |
173 | + * __d_path() will start returning NULL by backporting commit 02125a82 | |
174 | + * "fix apparmor dereferencing potentially freed dentry, sanitize | |
175 | + * __d_path() API". | |
176 | + * | |
177 | + * Unfortunately, __d_path() after applying that commit always returns | |
178 | + * NULL when root is empty. d_absolute_path() is provided for TOMOYO | |
179 | + * 2.x and AppArmor but TOMOYO 1.x does not use it, for TOMOYO 1.x | |
180 | + * might be built as a loadable kernel module and there is no warrantee | |
181 | + * that TOMOYO 1.x is recompiled after applying that commit. Also, | |
182 | + * I don't want to search /proc/kallsyms for d_absolute_path() because | |
183 | + * I want to keep TOMOYO 1.x architecture independent. Thus, supply | |
184 | + * non empty root like AppArmor's d_namespace_path() did. | |
185 | + */ | |
186 | + static bool ccs_no_empty; | |
187 | + char *pos; | |
188 | + if (buflen < 256) | |
189 | + return ERR_PTR(-ENOMEM); | |
190 | + if (!ccs_no_empty) { | |
191 | + struct path root = { }; | |
192 | + pos = ccsecurity_exports.__d_path(path, &root, buffer, | |
193 | + buflen - 1); | |
194 | + } else { | |
195 | + pos = NULL; | |
196 | + } | |
197 | + if (!pos) { | |
198 | + struct task_struct *task = current; | |
199 | + struct path root; | |
200 | + struct path tmp; | |
201 | + spin_lock(&task->fs->lock); | |
202 | + root.mnt = task->nsproxy->mnt_ns->root; | |
203 | + root.dentry = root.mnt->mnt_root; | |
204 | + path_get(&root); | |
205 | + spin_unlock(&task->fs->lock); | |
206 | + tmp = root; | |
207 | + pos = ccsecurity_exports.__d_path(path, &tmp, buffer, | |
208 | + buflen - 1); | |
209 | + path_put(&root); | |
210 | + if (pos) | |
211 | + return pos; | |
212 | + /* Remember if __d_path() needs non empty root. */ | |
213 | + ccs_no_empty = true; | |
214 | + pos = ERR_PTR(-EINVAL); | |
215 | + } | |
216 | + return pos; | |
217 | +#else | |
218 | + char *pos = buffer + buflen - 1; | |
219 | + struct dentry *dentry = path->dentry; | |
220 | + struct vfsmount *vfsmnt = path->mnt; | |
221 | + const char *name; | |
222 | + int len; | |
223 | + | |
224 | + if (buflen < 256) | |
225 | + goto out; | |
226 | + | |
227 | + *pos = '\0'; | |
228 | + for (;;) { | |
229 | + struct dentry *parent; | |
230 | + if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | |
231 | + if (vfsmnt->mnt_parent == vfsmnt) | |
232 | + break; | |
233 | + dentry = vfsmnt->mnt_mountpoint; | |
234 | + vfsmnt = vfsmnt->mnt_parent; | |
235 | + continue; | |
236 | + } | |
237 | + parent = dentry->d_parent; | |
238 | + name = dentry->d_name.name; | |
239 | + len = dentry->d_name.len; | |
240 | + pos -= len; | |
241 | + if (pos <= buffer) | |
242 | + goto out; | |
243 | + memmove(pos, name, len); | |
244 | + *--pos = '/'; | |
245 | + dentry = parent; | |
246 | + } | |
247 | + if (*pos == '/') | |
248 | + pos++; | |
249 | + len = dentry->d_name.len; | |
250 | + pos -= len; | |
251 | + if (pos < buffer) | |
252 | + goto out; | |
253 | + memmove(pos, dentry->d_name.name, len); | |
254 | + return pos; | |
255 | +out: | |
256 | + return ERR_PTR(-ENOMEM); | |
257 | +#endif | |
258 | +} | |
259 | + | |
260 | +/** | |
261 | + * ccs_get_dentry_path - Get the path of a dentry. | |
262 | + * | |
263 | + * @dentry: Pointer to "struct dentry". | |
264 | + * @buffer: Pointer to buffer to return value in. | |
265 | + * @buflen: Sizeof @buffer. | |
266 | + * | |
267 | + * Returns the buffer on success, an error code otherwise. | |
268 | + * | |
269 | + * Based on dentry_path() in fs/dcache.c | |
270 | + */ | |
271 | +static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, | |
272 | + const int buflen) | |
273 | +{ | |
274 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) | |
275 | + if (buflen < 256) | |
276 | + return ERR_PTR(-ENOMEM); | |
277 | + /* rename_lock is locked/unlocked by dentry_path_raw(). */ | |
278 | + return dentry_path_raw(dentry, buffer, buflen - 1); | |
279 | +#else | |
280 | + char *pos = buffer + buflen - 1; | |
281 | + if (buflen < 256) | |
282 | + return ERR_PTR(-ENOMEM); | |
283 | + *pos = '\0'; | |
284 | + spin_lock(&dcache_lock); | |
285 | + while (!IS_ROOT(dentry)) { | |
286 | + struct dentry *parent = dentry->d_parent; | |
287 | + const char *name = dentry->d_name.name; | |
288 | + const int len = dentry->d_name.len; | |
289 | + pos -= len; | |
290 | + if (pos <= buffer) { | |
291 | + pos = ERR_PTR(-ENOMEM); | |
292 | + break; | |
293 | + } | |
294 | + memmove(pos, name, len); | |
295 | + *--pos = '/'; | |
296 | + dentry = parent; | |
297 | + } | |
298 | + spin_unlock(&dcache_lock); | |
299 | + if (pos == buffer + buflen - 1) | |
300 | + *--pos = '/'; | |
301 | + return pos; | |
302 | +#endif | |
303 | +} | |
304 | + | |
305 | +/** | |
306 | + * ccs_get_local_path - Get the path of a dentry. | |
307 | + * | |
308 | + * @dentry: Pointer to "struct dentry". | |
309 | + * @buffer: Pointer to buffer to return value in. | |
310 | + * @buflen: Sizeof @buffer. | |
311 | + * | |
312 | + * Returns the buffer on success, an error code otherwise. | |
313 | + */ | |
314 | +static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, | |
315 | + const int buflen) | |
316 | +{ | |
317 | + struct super_block *sb = dentry->d_sb; | |
318 | + char *pos = ccs_get_dentry_path(dentry, buffer, buflen); | |
319 | + if (IS_ERR(pos)) | |
320 | + return pos; | |
321 | + /* Convert from $PID to self if $PID is current thread. */ | |
322 | + if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { | |
323 | + char *ep; | |
324 | + const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); | |
325 | + if (*ep == '/' && pid && pid == | |
326 | + task_tgid_nr_ns(current, sb->s_fs_info)) { | |
327 | + pos = ep - 5; | |
328 | + if (pos < buffer) | |
329 | + goto out; | |
330 | + memmove(pos, "/self", 5); | |
331 | + } | |
332 | + goto prepend_filesystem_name; | |
333 | + } | |
334 | + /* Use filesystem name for unnamed devices. */ | |
335 | + if (!MAJOR(sb->s_dev)) | |
336 | + goto prepend_filesystem_name; | |
337 | + { | |
338 | + struct inode *inode = sb->s_root->d_inode; | |
339 | + /* | |
340 | + * Use filesystem name if filesystems does not support rename() | |
341 | + * operation. | |
342 | + */ | |
343 | + if (inode->i_op && !inode->i_op->rename) | |
344 | + goto prepend_filesystem_name; | |
345 | + } | |
346 | + /* Prepend device name. */ | |
347 | + { | |
348 | + char name[64]; | |
349 | + int name_len; | |
350 | + const dev_t dev = sb->s_dev; | |
351 | + name[sizeof(name) - 1] = '\0'; | |
352 | + snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), | |
353 | + MINOR(dev)); | |
354 | + name_len = strlen(name); | |
355 | + pos -= name_len; | |
356 | + if (pos < buffer) | |
357 | + goto out; | |
358 | + memmove(pos, name, name_len); | |
359 | + return pos; | |
360 | + } | |
361 | + /* Prepend filesystem name. */ | |
362 | +prepend_filesystem_name: | |
363 | + { | |
364 | + const char *name = sb->s_type->name; | |
365 | + const int name_len = strlen(name); | |
366 | + pos -= name_len + 1; | |
367 | + if (pos < buffer) | |
368 | + goto out; | |
369 | + memmove(pos, name, name_len); | |
370 | + pos[name_len] = ':'; | |
371 | + } | |
372 | + return pos; | |
373 | +out: | |
374 | + return ERR_PTR(-ENOMEM); | |
375 | +} | |
376 | + | |
377 | +/** | |
378 | + * ccs_get_socket_name - Get the name of a socket. | |
379 | + * | |
380 | + * @path: Pointer to "struct path". | |
381 | + * @buffer: Pointer to buffer to return value in. | |
382 | + * @buflen: Sizeof @buffer. | |
383 | + * | |
384 | + * Returns the buffer. | |
385 | + */ | |
386 | +static char *ccs_get_socket_name(struct path *path, char * const buffer, | |
387 | + const int buflen) | |
388 | +{ | |
389 | + struct inode *inode = path->dentry->d_inode; | |
390 | + struct socket *sock = inode ? SOCKET_I(inode) : NULL; | |
391 | + struct sock *sk = sock ? sock->sk : NULL; | |
392 | + if (sk) { | |
393 | + snprintf(buffer, buflen, "socket:[family=%u:type=%u:" | |
394 | + "protocol=%u]", sk->sk_family, sk->sk_type, | |
395 | + sk->sk_protocol); | |
396 | + } else { | |
397 | + snprintf(buffer, buflen, "socket:[unknown]"); | |
398 | + } | |
399 | + return buffer; | |
400 | +} | |
401 | + | |
402 | +/** | |
403 | + * ccs_realpath - Returns realpath(3) of the given pathname but ignores chroot'ed root. | |
404 | + * | |
405 | + * @path: Pointer to "struct path". | |
406 | + * | |
407 | + * Returns the realpath of the given @path on success, NULL otherwise. | |
408 | + * | |
409 | + * This function uses kzalloc(), so caller must kfree() if this function | |
410 | + * didn't return NULL. | |
411 | + */ | |
412 | +char *ccs_realpath(struct path *path) | |
413 | +{ | |
414 | + char *buf = NULL; | |
415 | + char *name = NULL; | |
416 | + unsigned int buf_len = PAGE_SIZE / 2; | |
417 | + struct dentry *dentry = path->dentry; | |
418 | + struct super_block *sb; | |
419 | + if (!dentry) | |
420 | + return NULL; | |
421 | + sb = dentry->d_sb; | |
422 | + while (1) { | |
423 | + char *pos; | |
424 | + struct inode *inode; | |
425 | + buf_len <<= 1; | |
426 | + kfree(buf); | |
427 | + buf = kmalloc(buf_len, GFP_NOFS); | |
428 | + if (!buf) | |
429 | + break; | |
430 | + /* To make sure that pos is '\0' terminated. */ | |
431 | + buf[buf_len - 1] = '\0'; | |
432 | + /* Get better name for socket. */ | |
433 | + if (sb->s_magic == SOCKFS_MAGIC) { | |
434 | + pos = ccs_get_socket_name(path, buf, buf_len - 1); | |
435 | + goto encode; | |
436 | + } | |
437 | + /* For "pipe:[\$]". */ | |
438 | + if (dentry->d_op && dentry->d_op->d_dname) { | |
439 | + pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); | |
440 | + goto encode; | |
441 | + } | |
442 | + inode = sb->s_root->d_inode; | |
443 | + /* | |
444 | + * Use local name for "filesystems without rename() operation" | |
445 | + * or "path without vfsmount" or "absolute name is unavailable" | |
446 | + * cases. | |
447 | + */ | |
448 | + if (!path->mnt || (inode->i_op && !inode->i_op->rename)) | |
449 | + pos = ERR_PTR(-EINVAL); | |
450 | + else { | |
451 | + /* Get absolute name for the rest. */ | |
452 | + ccs_realpath_lock(); | |
453 | + pos = ccs_get_absolute_path(path, buf, buf_len - 1); | |
454 | + ccs_realpath_unlock(); | |
455 | + } | |
456 | + if (pos == ERR_PTR(-EINVAL)) | |
457 | + pos = ccs_get_local_path(path->dentry, buf, | |
458 | + buf_len - 1); | |
459 | +encode: | |
460 | + if (IS_ERR(pos)) | |
461 | + continue; | |
462 | + name = ccs_encode(pos); | |
463 | + break; | |
464 | + } | |
465 | + kfree(buf); | |
466 | + if (!name) | |
467 | + ccs_warn_oom(__func__); | |
468 | + return name; | |
469 | +} | |
470 | + | |
471 | +/** | |
472 | + * ccs_encode2 - Encode binary string to ascii string. | |
473 | + * | |
474 | + * @str: String in binary format. Maybe NULL. | |
475 | + * @str_len: Size of @str in byte. | |
476 | + * | |
477 | + * Returns pointer to @str in ascii format on success, NULL otherwise. | |
478 | + * | |
479 | + * This function uses kzalloc(), so caller must kfree() if this function | |
480 | + * didn't return NULL. | |
481 | + */ | |
482 | +char *ccs_encode2(const char *str, int str_len) | |
483 | +{ | |
484 | + int i; | |
485 | + int len; | |
486 | + const char *p = str; | |
487 | + char *cp; | |
488 | + char *cp0; | |
489 | + if (!p) | |
490 | + return NULL; | |
491 | + len = str_len; | |
492 | + for (i = 0; i < str_len; i++) { | |
493 | + const unsigned char c = p[i]; | |
494 | + if (!(c > ' ' && c < 127 && c != '\\')) | |
495 | + len += 3; | |
496 | + } | |
497 | + len++; | |
498 | + cp = kzalloc(len, GFP_NOFS); | |
499 | + if (!cp) | |
500 | + return NULL; | |
501 | + cp0 = cp; | |
502 | + p = str; | |
503 | + for (i = 0; i < str_len; i++) { | |
504 | + const unsigned char c = p[i]; | |
505 | + if (c > ' ' && c < 127 && c != '\\') { | |
506 | + *cp++ = c; | |
507 | + } else { | |
508 | + *cp++ = '\\'; | |
509 | + *cp++ = (c >> 6) + '0'; | |
510 | + *cp++ = ((c >> 3) & 7) + '0'; | |
511 | + *cp++ = (c & 7) + '0'; | |
512 | + } | |
513 | + } | |
514 | + return cp0; | |
515 | +} | |
516 | + | |
517 | +/** | |
518 | + * ccs_encode - Encode binary string to ascii string. | |
519 | + * | |
520 | + * @str: String in binary format. Maybe NULL. | |
521 | + * | |
522 | + * Returns pointer to @str in ascii format on success, NULL otherwise. | |
523 | + * | |
524 | + * This function uses kzalloc(), so caller must kfree() if this function | |
525 | + * didn't return NULL. | |
526 | + */ | |
527 | +char *ccs_encode(const char *str) | |
528 | +{ | |
529 | + return str ? ccs_encode2(str, strlen(str)) : NULL; | |
530 | +} | |
531 | + | |
532 | +/** | |
533 | + * ccs_const_part_length - Evaluate the initial length without a pattern in a token. | |
534 | + * | |
535 | + * @filename: The string to evaluate. Maybe NULL. | |
536 | + * | |
537 | + * Returns the initial length without a pattern in @filename. | |
538 | + */ | |
539 | +static int ccs_const_part_length(const char *filename) | |
540 | +{ | |
541 | + char c; | |
542 | + int len = 0; | |
543 | + if (!filename) | |
544 | + return 0; | |
545 | + while (1) { | |
546 | + c = *filename++; | |
547 | + if (!c) | |
548 | + break; | |
549 | + if (c != '\\') { | |
550 | + len++; | |
551 | + continue; | |
552 | + } | |
553 | + c = *filename++; | |
554 | + switch (c) { | |
555 | + case '0': /* "\ooo" */ | |
556 | + case '1': | |
557 | + case '2': | |
558 | + case '3': | |
559 | + c = *filename++; | |
560 | + if (c < '0' || c > '7') | |
561 | + break; | |
562 | + c = *filename++; | |
563 | + if (c < '0' || c > '7') | |
564 | + break; | |
565 | + len += 4; | |
566 | + continue; | |
567 | + } | |
568 | + break; | |
569 | + } | |
570 | + return len; | |
571 | +} | |
572 | + | |
573 | +/** | |
574 | + * ccs_fill_path_info - Fill in "struct ccs_path_info" members. | |
575 | + * | |
576 | + * @ptr: Pointer to "struct ccs_path_info" to fill in. | |
577 | + * | |
578 | + * Returns nothing. | |
579 | + * | |
580 | + * The caller sets "struct ccs_path_info"->name. | |
581 | + */ | |
582 | +void ccs_fill_path_info(struct ccs_path_info *ptr) | |
583 | +{ | |
584 | + const char *name = ptr->name; | |
585 | + const int len = strlen(name); | |
586 | + ptr->total_len = len; | |
587 | + ptr->const_len = ccs_const_part_length(name); | |
588 | + ptr->hash = full_name_hash(name, len); | |
589 | +} | |
590 | + | |
591 | +/** | |
592 | + * ccs_get_exe - Get ccs_realpath() of current process. | |
593 | + * | |
594 | + * Returns the ccs_realpath() of current process on success, NULL otherwise. | |
595 | + * | |
596 | + * This function uses kzalloc(), so the caller must kfree() | |
597 | + * if this function didn't return NULL. | |
598 | + */ | |
599 | +char *ccs_get_exe(void) | |
600 | +{ | |
601 | + struct mm_struct *mm; | |
602 | + struct vm_area_struct *vma; | |
603 | + bool done = false; | |
604 | + char *cp = NULL; | |
605 | + if (current->flags & PF_KTHREAD) | |
606 | + return kstrdup("<kernel>", GFP_NOFS); | |
607 | + mm = current->mm; | |
608 | + if (!mm) | |
609 | + goto task_has_no_mm; | |
610 | + down_read(&mm->mmap_sem); | |
611 | + for (vma = mm->mmap; vma; vma = vma->vm_next) { | |
612 | + if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | |
613 | + cp = ccs_realpath(&vma->vm_file->f_path); | |
614 | + done = true; | |
615 | + break; | |
616 | + } | |
617 | + } | |
618 | + up_read(&mm->mmap_sem); | |
619 | + if (done) | |
620 | + return cp; | |
621 | +task_has_no_mm: | |
622 | + /* I'don't know. */ | |
623 | + return kstrdup("<unknown>", GFP_NOFS); | |
624 | +} | |
625 | + | |
626 | +/** | |
627 | + * ccs_get_exename - Get ccs_realpath() of current process. | |
628 | + * | |
629 | + * @buf: Pointer to "struct ccs_path_info". | |
630 | + * | |
631 | + * Returns true on success, false otherwise. | |
632 | + * | |
633 | + * This function uses kzalloc(), so the caller must kfree() | |
634 | + * if this function returned true. | |
635 | + */ | |
636 | +bool ccs_get_exename(struct ccs_path_info *buf) | |
637 | +{ | |
638 | + buf->name = ccs_get_exe(); | |
639 | + if (buf->name) { | |
640 | + ccs_fill_path_info(buf); | |
641 | + return true; | |
642 | + } | |
643 | + return false; | |
644 | +} |
@@ -0,0 +1,1162 @@ | ||
1 | +/* | |
2 | + * security/ccsecurity/internal.h | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#ifndef _SECURITY_CCSECURITY_INTERNAL_H | |
10 | +#define _SECURITY_CCSECURITY_INTERNAL_H | |
11 | + | |
12 | +#include <linux/version.h> | |
13 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) | |
14 | +#error This module supports only 2.6.27 and later kernels. | |
15 | +#endif | |
16 | +#include <linux/types.h> | |
17 | +#include <linux/kernel.h> | |
18 | +#include <linux/string.h> | |
19 | +#include <linux/mm.h> | |
20 | +#include <linux/utime.h> | |
21 | +#include <linux/file.h> | |
22 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 38) | |
23 | +#include <linux/smp_lock.h> | |
24 | +#endif | |
25 | +#include <linux/module.h> | |
26 | +#include <linux/init.h> | |
27 | +#include <linux/slab.h> | |
28 | +#include <linux/highmem.h> | |
29 | +#include <linux/poll.h> | |
30 | +#include <linux/binfmts.h> | |
31 | +#include <linux/delay.h> | |
32 | +#include <linux/sched.h> | |
33 | +#include <linux/dcache.h> | |
34 | +#include <linux/mount.h> | |
35 | +#include <linux/net.h> | |
36 | +#include <linux/inet.h> | |
37 | +#include <linux/in.h> | |
38 | +#include <linux/in6.h> | |
39 | +#include <linux/un.h> | |
40 | +#include <linux/ptrace.h> | |
41 | +#include <linux/namei.h> | |
42 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | |
43 | +#include <linux/fs_struct.h> | |
44 | +#endif | |
45 | +#include <linux/proc_fs.h> | |
46 | +#include <linux/hash.h> | |
47 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
48 | +#include <linux/sysctl.h> | |
49 | +#endif | |
50 | +#include <linux/kthread.h> | |
51 | +#include <stdarg.h> | |
52 | +#include <asm/uaccess.h> | |
53 | +#include <net/sock.h> | |
54 | +#include <net/af_unix.h> | |
55 | +#include <net/ip.h> | |
56 | +#include <net/ipv6.h> | |
57 | +#include <net/udp.h> | |
58 | + | |
59 | +#ifndef __printf | |
60 | +#define __printf(a,b) __attribute__((format(printf,a,b))) | |
61 | +#endif | |
62 | +#ifndef __packed | |
63 | +#define __packed __attribute__((__packed__)) | |
64 | +#endif | |
65 | +#ifndef bool | |
66 | +#define bool _Bool | |
67 | +#endif | |
68 | +#ifndef false | |
69 | +#define false 0 | |
70 | +#endif | |
71 | +#ifndef true | |
72 | +#define true 1 | |
73 | +#endif | |
74 | + | |
75 | +#ifndef __user | |
76 | +#define __user | |
77 | +#endif | |
78 | + | |
79 | +#ifndef current_uid | |
80 | +#define current_uid() (current->uid) | |
81 | +#endif | |
82 | +#ifndef current_gid | |
83 | +#define current_gid() (current->gid) | |
84 | +#endif | |
85 | +#ifndef current_euid | |
86 | +#define current_euid() (current->euid) | |
87 | +#endif | |
88 | +#ifndef current_egid | |
89 | +#define current_egid() (current->egid) | |
90 | +#endif | |
91 | +#ifndef current_suid | |
92 | +#define current_suid() (current->suid) | |
93 | +#endif | |
94 | +#ifndef current_sgid | |
95 | +#define current_sgid() (current->sgid) | |
96 | +#endif | |
97 | +#ifndef current_fsuid | |
98 | +#define current_fsuid() (current->fsuid) | |
99 | +#endif | |
100 | +#ifndef current_fsgid | |
101 | +#define current_fsgid() (current->fsgid) | |
102 | +#endif | |
103 | + | |
104 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) | |
105 | + | |
106 | +/** | |
107 | + * __list_del_entry - Deletes entry from list without re-initialization. | |
108 | + * | |
109 | + * @entry: Pointer to "struct list_head". | |
110 | + * | |
111 | + * Returns nothing. | |
112 | + * | |
113 | + * This is for compatibility with older kernels. | |
114 | + */ | |
115 | +static inline void __list_del_entry(struct list_head *entry) | |
116 | +{ | |
117 | + __list_del(entry->prev, entry->next); | |
118 | +} | |
119 | + | |
120 | +#endif | |
121 | + | |
122 | +#ifndef list_for_each_entry_safe | |
123 | + | |
124 | +/** | |
125 | + * list_for_each_entry_safe - Iterate over list of given type safe against removal of list entry. | |
126 | + * | |
127 | + * @pos: The "type *" to use as a loop cursor. | |
128 | + * @n: Another "type *" to use as temporary storage. | |
129 | + * @head: Pointer to "struct list_head". | |
130 | + * @member: The name of the list_struct within the struct. | |
131 | + * | |
132 | + * This is for compatibility with older kernels. | |
133 | + */ | |
134 | +#define list_for_each_entry_safe(pos, n, head, member) \ | |
135 | + for (pos = list_entry((head)->next, typeof(*pos), member), \ | |
136 | + n = list_entry(pos->member.next, typeof(*pos), member); \ | |
137 | + &pos->member != (head); \ | |
138 | + pos = n, n = list_entry(n->member.next, typeof(*n), member)) | |
139 | + | |
140 | +#endif | |
141 | + | |
142 | +#ifndef srcu_dereference | |
143 | + | |
144 | +/** | |
145 | + * srcu_dereference - Fetch SRCU-protected pointer with checking. | |
146 | + * | |
147 | + * @p: The pointer to read, prior to dereferencing. | |
148 | + * @ss: Pointer to "struct srcu_struct". | |
149 | + * | |
150 | + * Returns @p. | |
151 | + * | |
152 | + * This is for compatibility with older kernels. | |
153 | + */ | |
154 | +#define srcu_dereference(p, ss) rcu_dereference(p) | |
155 | + | |
156 | +#endif | |
157 | + | |
158 | +#ifndef list_for_each_entry_srcu | |
159 | + | |
160 | +/** | |
161 | + * list_for_each_entry_srcu - Iterate over rcu list of given type. | |
162 | + * | |
163 | + * @pos: The type * to use as a loop cursor. | |
164 | + * @head: The head for your list. | |
165 | + * @member: The name of the list_struct within the struct. | |
166 | + * @ss: Pointer to "struct srcu_struct". | |
167 | + * | |
168 | + * As of 2.6.36, this macro is not provided because only TOMOYO wants it. | |
169 | + */ | |
170 | +#define list_for_each_entry_srcu(pos, head, member, ss) \ | |
171 | + for (pos = list_entry(srcu_dereference((head)->next, ss), \ | |
172 | + typeof(*pos), member); \ | |
173 | + prefetch(pos->member.next), &pos->member != (head); \ | |
174 | + pos = list_entry(srcu_dereference(pos->member.next, ss), \ | |
175 | + typeof(*pos), member)) | |
176 | + | |
177 | +#endif | |
178 | + | |
179 | +/* | |
180 | + * TOMOYO specific part start. | |
181 | + */ | |
182 | + | |
183 | +#include <linux/ccsecurity.h> | |
184 | + | |
185 | +/* Enumeration definition for internal use. */ | |
186 | + | |
187 | +/* Index numbers for "struct ccs_condition". */ | |
188 | +enum ccs_conditions_index { | |
189 | + /* 0 */ | |
190 | + CCS_SELF_UID, /* current_uid() */ | |
191 | + CCS_SELF_EUID, /* current_euid() */ | |
192 | + CCS_SELF_SUID, /* current_suid() */ | |
193 | + CCS_SELF_FSUID, /* current_fsuid() */ | |
194 | + CCS_SELF_GID, /* current_gid() */ | |
195 | + CCS_SELF_EGID, /* current_egid() */ | |
196 | + CCS_SELF_SGID, /* current_sgid() */ | |
197 | + CCS_SELF_FSGID, /* current_fsgid() */ | |
198 | + CCS_SELF_PID, /* sys_getpid() */ | |
199 | + CCS_SELF_PPID, /* sys_getppid() */ | |
200 | + /* 10 */ | |
201 | + CCS_TASK_TYPE, /* ((u8) task->ccs_flags) & | |
202 | + CCS_TASK_IS_EXECUTE_HANDLER */ | |
203 | + CCS_SELF_DOMAIN, | |
204 | + CCS_SELF_EXE, | |
205 | + CCS_EXEC_ARGC, /* "struct linux_binprm *"->argc */ | |
206 | + CCS_EXEC_ENVC, /* "struct linux_binprm *"->envc */ | |
207 | + CCS_OBJ_IS_SOCKET, /* S_IFSOCK */ | |
208 | + CCS_OBJ_IS_SYMLINK, /* S_IFLNK */ | |
209 | + CCS_OBJ_IS_FILE, /* S_IFREG */ | |
210 | + CCS_OBJ_IS_BLOCK_DEV, /* S_IFBLK */ | |
211 | + CCS_OBJ_IS_DIRECTORY, /* S_IFDIR */ | |
212 | + /* 20 */ | |
213 | + CCS_OBJ_IS_CHAR_DEV, /* S_IFCHR */ | |
214 | + CCS_OBJ_IS_FIFO, /* S_IFIFO */ | |
215 | + CCS_MODE_SETUID, /* S_ISUID */ | |
216 | + CCS_MODE_SETGID, /* S_ISGID */ | |
217 | + CCS_MODE_STICKY, /* S_ISVTX */ | |
218 | + CCS_MODE_OWNER_READ, /* S_IRUSR */ | |
219 | + CCS_MODE_OWNER_WRITE, /* S_IWUSR */ | |
220 | + CCS_MODE_OWNER_EXECUTE, /* S_IXUSR */ | |
221 | + CCS_MODE_GROUP_READ, /* S_IRGRP */ | |
222 | + CCS_MODE_GROUP_WRITE, /* S_IWGRP */ | |
223 | + /* 30 */ | |
224 | + CCS_MODE_GROUP_EXECUTE, /* S_IXGRP */ | |
225 | + CCS_MODE_OTHERS_READ, /* S_IROTH */ | |
226 | + CCS_MODE_OTHERS_WRITE, /* S_IWOTH */ | |
227 | + CCS_MODE_OTHERS_EXECUTE, /* S_IXOTH */ | |
228 | + CCS_TASK_EXECUTE_HANDLER, /* CCS_TASK_IS_EXECUTE_HANDLER */ | |
229 | + CCS_HANDLER_PATH, | |
230 | + CCS_TRANSIT_DOMAIN, | |
231 | + CCS_MAX_CONDITION_KEYWORD, | |
232 | + CCS_COND_SARG0, | |
233 | + CCS_COND_SARG1, | |
234 | + /* 40 */ | |
235 | + CCS_COND_SARG2, | |
236 | + CCS_COND_SARG3, | |
237 | + CCS_COND_NARG0, | |
238 | + CCS_COND_NARG1, | |
239 | + CCS_COND_NARG2, | |
240 | + CCS_COND_IPARG, | |
241 | + CCS_COND_DOMAIN, | |
242 | + CCS_IMM_GROUP, | |
243 | + CCS_IMM_NAME_ENTRY, | |
244 | + CCS_IMM_NUMBER_ENTRY1, | |
245 | + /* 50 */ | |
246 | + CCS_IMM_NUMBER_ENTRY2, | |
247 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
248 | + CCS_IMM_IPV4ADDR_ENTRY1, | |
249 | + CCS_IMM_IPV4ADDR_ENTRY2, | |
250 | + CCS_IMM_IPV6ADDR_ENTRY1, | |
251 | + CCS_IMM_IPV6ADDR_ENTRY2, | |
252 | +#endif | |
253 | + CCS_ARGV_ENTRY, | |
254 | + CCS_ENVP_ENTRY, | |
255 | + CCS_PATH_ATTRIBUTE_START = 192, | |
256 | + CCS_PATH_ATTRIBUTE_END = 255 | |
257 | +} __packed; | |
258 | + | |
259 | +enum ccs_path_attribute_index { | |
260 | + CCS_PATH_ATTRIBUTE_UID, | |
261 | + CCS_PATH_ATTRIBUTE_GID, | |
262 | + CCS_PATH_ATTRIBUTE_INO, | |
263 | + CCS_PATH_ATTRIBUTE_TYPE, | |
264 | + CCS_PATH_ATTRIBUTE_MAJOR, | |
265 | + CCS_PATH_ATTRIBUTE_MINOR, | |
266 | + CCS_PATH_ATTRIBUTE_PERM, | |
267 | + CCS_PATH_ATTRIBUTE_DEV_MAJOR, | |
268 | + CCS_PATH_ATTRIBUTE_DEV_MINOR, | |
269 | + CCS_PATH_ATTRIBUTE_FSMAGIC, | |
270 | + CCS_MAX_PATH_ATTRIBUTE | |
271 | +} __packed; | |
272 | + | |
273 | +/* Index numbers for group entries. */ | |
274 | +enum ccs_group_id { | |
275 | + CCS_STRING_GROUP, | |
276 | + CCS_NUMBER_GROUP, | |
277 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
278 | + CCS_IP_GROUP, | |
279 | +#endif | |
280 | + CCS_MAX_GROUP | |
281 | +} __packed; | |
282 | + | |
283 | +/* Index numbers for functionality. */ | |
284 | +enum ccs_mac_index { | |
285 | + CCS_MAC_EXECUTE, | |
286 | + CCS_MAC_READ, | |
287 | + CCS_MAC_WRITE, | |
288 | + CCS_MAC_APPEND, | |
289 | + CCS_MAC_CREATE, | |
290 | + CCS_MAC_UNLINK, | |
291 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
292 | + CCS_MAC_GETATTR, | |
293 | +#endif | |
294 | + CCS_MAC_MKDIR, | |
295 | + CCS_MAC_RMDIR, | |
296 | + CCS_MAC_MKFIFO, | |
297 | + CCS_MAC_MKSOCK, | |
298 | + CCS_MAC_TRUNCATE, | |
299 | + CCS_MAC_SYMLINK, | |
300 | + CCS_MAC_MKBLOCK, | |
301 | + CCS_MAC_MKCHAR, | |
302 | + CCS_MAC_LINK, | |
303 | + CCS_MAC_RENAME, | |
304 | + CCS_MAC_CHMOD, | |
305 | + CCS_MAC_CHOWN, | |
306 | + CCS_MAC_CHGRP, | |
307 | + CCS_MAC_IOCTL, | |
308 | + CCS_MAC_CHROOT, | |
309 | + CCS_MAC_MOUNT, | |
310 | + CCS_MAC_UMOUNT, | |
311 | + CCS_MAC_PIVOT_ROOT, | |
312 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
313 | + CCS_MAC_INET_STREAM_BIND, | |
314 | + CCS_MAC_INET_STREAM_LISTEN, | |
315 | + CCS_MAC_INET_STREAM_CONNECT, | |
316 | + CCS_MAC_INET_STREAM_ACCEPT, | |
317 | + CCS_MAC_INET_DGRAM_BIND, | |
318 | + CCS_MAC_INET_DGRAM_SEND, | |
319 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
320 | + CCS_MAC_INET_DGRAM_RECV, | |
321 | +#endif | |
322 | + CCS_MAC_INET_RAW_BIND, | |
323 | + CCS_MAC_INET_RAW_SEND, | |
324 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
325 | + CCS_MAC_INET_RAW_RECV, | |
326 | +#endif | |
327 | + CCS_MAC_UNIX_STREAM_BIND, | |
328 | + CCS_MAC_UNIX_STREAM_LISTEN, | |
329 | + CCS_MAC_UNIX_STREAM_CONNECT, | |
330 | + CCS_MAC_UNIX_STREAM_ACCEPT, | |
331 | + CCS_MAC_UNIX_DGRAM_BIND, | |
332 | + CCS_MAC_UNIX_DGRAM_SEND, | |
333 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
334 | + CCS_MAC_UNIX_DGRAM_RECV, | |
335 | +#endif | |
336 | + CCS_MAC_UNIX_SEQPACKET_BIND, | |
337 | + CCS_MAC_UNIX_SEQPACKET_LISTEN, | |
338 | + CCS_MAC_UNIX_SEQPACKET_CONNECT, | |
339 | + CCS_MAC_UNIX_SEQPACKET_ACCEPT, | |
340 | +#endif | |
341 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
342 | + CCS_MAC_ENVIRON, | |
343 | +#endif | |
344 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
345 | + CCS_MAC_PTRACE, | |
346 | +#endif | |
347 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
348 | + CCS_MAC_SIGNAL, | |
349 | +#endif | |
350 | + CCS_MAC_MODIFY_POLICY, | |
351 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
352 | + CCS_MAC_USE_NETLINK_SOCKET, | |
353 | + CCS_MAC_USE_PACKET_SOCKET, | |
354 | + CCS_MAC_USE_REBOOT, | |
355 | + CCS_MAC_USE_VHANGUP, | |
356 | + CCS_MAC_SET_TIME, | |
357 | + CCS_MAC_SET_PRIORITY, | |
358 | + CCS_MAC_SET_HOSTNAME, | |
359 | + CCS_MAC_USE_KERNEL_MODULE, | |
360 | + CCS_MAC_USE_NEW_KERNEL, | |
361 | +#endif | |
362 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
363 | + CCS_MAC_AUTO_DOMAIN_TRANSITION, | |
364 | +#endif | |
365 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
366 | + CCS_MAC_MANUAL_DOMAIN_TRANSITION, | |
367 | +#endif | |
368 | + CCS_MAX_MAC_INDEX | |
369 | +} __packed; | |
370 | + | |
371 | +/* Index numbers for statistic information. */ | |
372 | +enum ccs_memory_stat_type { | |
373 | + CCS_MEMORY_POLICY, | |
374 | + CCS_MEMORY_AUDIT, | |
375 | + CCS_MEMORY_QUERY, | |
376 | + CCS_MAX_MEMORY_STAT | |
377 | +} __packed; | |
378 | + | |
379 | +enum ccs_matching_result { | |
380 | + CCS_MATCHING_UNMATCHED, | |
381 | + CCS_MATCHING_ALLOWED, | |
382 | + CCS_MATCHING_DENIED, | |
383 | + CCS_MAX_MATCHING | |
384 | +} __packed; | |
385 | + | |
386 | +/* Index numbers for stat(). */ | |
387 | +enum ccs_path_stat_index { | |
388 | + /* Do not change this order. */ | |
389 | + CCS_PATH1, | |
390 | + CCS_PATH1_PARENT, | |
391 | + CCS_PATH2, | |
392 | + CCS_PATH2_PARENT, | |
393 | + CCS_MAX_PATH_STAT | |
394 | +} __packed; | |
395 | + | |
396 | +/* Index numbers for entry type. */ | |
397 | +enum ccs_policy_id { | |
398 | + CCS_ID_GROUP, | |
399 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
400 | + CCS_ID_IP_GROUP, | |
401 | +#endif | |
402 | + CCS_ID_STRING_GROUP, | |
403 | + CCS_ID_NUMBER_GROUP, | |
404 | + CCS_ID_CONDITION, | |
405 | + CCS_ID_NAME, | |
406 | + CCS_ID_ACL, | |
407 | + CCS_ID_DOMAIN, | |
408 | + CCS_MAX_POLICY | |
409 | +} __packed; | |
410 | + | |
411 | +/* Index numbers for statistic information. */ | |
412 | +enum ccs_policy_stat_type { | |
413 | + CCS_STAT_POLICY_UPDATES, | |
414 | + CCS_STAT_REQUEST_DENIED, | |
415 | + CCS_MAX_POLICY_STAT | |
416 | +} __packed; | |
417 | + | |
418 | +/* Index numbers for /proc/ccs/ interfaces. */ | |
419 | +enum ccs_proc_interface_index { | |
420 | + CCS_POLICY, | |
421 | + CCS_PROCESS_STATUS, | |
422 | + CCS_AUDIT, | |
423 | + CCS_VERSION, | |
424 | + CCS_QUERY, | |
425 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
426 | + CCS_EXECUTE_HANDLER, | |
427 | +#endif | |
428 | +} __packed; | |
429 | + | |
430 | +/* Index numbers for special mount operations. */ | |
431 | +enum ccs_special_mount { | |
432 | + CCS_MOUNT_BIND, /* mount --bind /source /dest */ | |
433 | + CCS_MOUNT_MOVE, /* mount --move /old /new */ | |
434 | + CCS_MOUNT_REMOUNT, /* mount -o remount /dir */ | |
435 | + CCS_MOUNT_MAKE_UNBINDABLE, /* mount --make-unbindable /dir */ | |
436 | + CCS_MOUNT_MAKE_PRIVATE, /* mount --make-private /dir */ | |
437 | + CCS_MOUNT_MAKE_SLAVE, /* mount --make-slave /dir */ | |
438 | + CCS_MOUNT_MAKE_SHARED, /* mount --make-shared /dir */ | |
439 | + CCS_MAX_SPECIAL_MOUNT | |
440 | +} __packed; | |
441 | + | |
442 | +/* Index numbers for type of numeric values. */ | |
443 | +enum ccs_value_type { | |
444 | + CCS_VALUE_TYPE_INVALID, | |
445 | + CCS_VALUE_TYPE_DECIMAL, | |
446 | + CCS_VALUE_TYPE_OCTAL, | |
447 | + CCS_VALUE_TYPE_HEXADECIMAL, | |
448 | +} __packed; | |
449 | + | |
450 | +/* Index numbers for type of IP addresses. */ | |
451 | +enum ccs_ipaddr_type { | |
452 | + CCS_ADDRESS_TYPE_INVALID, | |
453 | + CCS_ADDRESS_TYPE_IPV4, | |
454 | + CCS_ADDRESS_TYPE_IPV4_RANGE, | |
455 | + CCS_ADDRESS_TYPE_IPV6, | |
456 | + CCS_ADDRESS_TYPE_IPV6_RANGE, | |
457 | +} __packed; | |
458 | + | |
459 | +/* Constants definition for internal use. */ | |
460 | + | |
461 | +/* | |
462 | + * TOMOYO uses this hash only when appending a string into the string table. | |
463 | + * Frequency of appending strings is very low. So we don't need large (e.g. | |
464 | + * 64k) hash size. 256 will be sufficient. | |
465 | + */ | |
466 | +#define CCS_HASH_BITS 8 | |
467 | +#define CCS_MAX_HASH (1u << CCS_HASH_BITS) | |
468 | + | |
469 | +/* | |
470 | + * TOMOYO checks only SOCK_STREAM, SOCK_DGRAM, SOCK_RAW, SOCK_SEQPACKET. | |
471 | + * Therefore, we don't need SOCK_MAX. | |
472 | + */ | |
473 | +#define CCS_SOCK_MAX 6 | |
474 | + | |
475 | +/* Size of temporary buffer for execve() operation. */ | |
476 | +#define CCS_EXEC_TMPSIZE 4096 | |
477 | + | |
478 | +/* Patterns for auditing logs quota. */ | |
479 | +#define CCS_MAX_LOG_QUOTA 256 | |
480 | + | |
481 | +/* Garbage collector is trying to kfree() this element. */ | |
482 | +#define CCS_GC_IN_PROGRESS -1 | |
483 | + | |
484 | +/* Current thread is doing open(3) ? */ | |
485 | +#define CCS_OPEN_FOR_IOCTL_ONLY 2 | |
486 | +/* Current thread is doing do_execve() ? */ | |
487 | +#define CCS_TASK_IS_IN_EXECVE 4 | |
488 | +/* Current thread is running as an execute handler program? */ | |
489 | +#define CCS_TASK_IS_EXECUTE_HANDLER 8 | |
490 | +/* Current thread is allowed to modify policy via /proc/ccs/ interface? */ | |
491 | +#define CCS_TASK_IS_MANAGER 16 | |
492 | + | |
493 | +/* | |
494 | + * Retry this request. Returned by ccs_supervisor() if policy violation has | |
495 | + * occurred in enforcing mode and the userspace daemon decided to retry. | |
496 | + * | |
497 | + * We must choose a positive value in order to distinguish "granted" (which is | |
498 | + * 0) and "rejected" (which is a negative value) and "retry". | |
499 | + */ | |
500 | +#define CCS_RETRY_REQUEST 1 | |
501 | + | |
502 | +/* Size of read buffer for /proc/ccs/ interface. */ | |
503 | +#define CCS_MAX_IO_READ_QUEUE 64 | |
504 | + | |
505 | +/* Structure definition for internal use. */ | |
506 | + | |
507 | +/* Common header for holding ACL entries. */ | |
508 | +struct ccs_acl_head { | |
509 | + struct list_head list; | |
510 | + s8 is_deleted; /* true or false or CCS_GC_IN_PROGRESS */ | |
511 | +} __packed; | |
512 | + | |
513 | +/* Common header for shared entries. */ | |
514 | +struct ccs_shared_acl_head { | |
515 | + struct list_head list; | |
516 | + atomic_t users; | |
517 | +} __packed; | |
518 | + | |
519 | +/* Common header for individual entries. */ | |
520 | +struct ccs_acl_info { | |
521 | + struct list_head list; | |
522 | + struct list_head acl_info_list; | |
523 | + struct ccs_condition *cond; /* Maybe NULL. */ | |
524 | + bool is_deleted; | |
525 | + bool is_deny; | |
526 | + u16 priority; | |
527 | + u8 audit; | |
528 | +}; | |
529 | + | |
530 | +/* Structure for "string_group"/"number_group"/"ip_group" directive. */ | |
531 | +struct ccs_group { | |
532 | + struct ccs_shared_acl_head head; | |
533 | + /* Name of group (without leading "@"). */ | |
534 | + const struct ccs_path_info *group_name; | |
535 | + /* | |
536 | + * List of "struct ccs_string_group" or "struct ccs_number_group" or | |
537 | + * "struct ccs_ip_group". | |
538 | + */ | |
539 | + struct list_head member_list; | |
540 | +}; | |
541 | + | |
542 | +/* Structure for "string_group" directive. */ | |
543 | +struct ccs_string_group { | |
544 | + struct ccs_acl_head head; | |
545 | + const struct ccs_path_info *member_name; | |
546 | +}; | |
547 | + | |
548 | +/* Structure for "number_group" directive. */ | |
549 | +struct ccs_number_group { | |
550 | + struct ccs_acl_head head; | |
551 | + u8 radix; | |
552 | + unsigned long value[2]; | |
553 | +}; | |
554 | + | |
555 | +/* Structure for "ip_group" directive. */ | |
556 | +struct ccs_ip_group { | |
557 | + struct ccs_acl_head head; | |
558 | + bool is_ipv6; | |
559 | + /* Structure for holding an IP address. */ | |
560 | + struct in6_addr ip[2]; /* Big endian. */ | |
561 | +}; | |
562 | + | |
563 | +/* Subset of "struct stat". Used by conditional ACL and audit logs. */ | |
564 | +struct ccs_mini_stat { | |
565 | + uid_t uid; | |
566 | + gid_t gid; | |
567 | + ino_t ino; | |
568 | + umode_t mode; | |
569 | + dev_t dev; | |
570 | + dev_t rdev; | |
571 | + unsigned long fsmagic; | |
572 | +}; | |
573 | + | |
574 | +/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */ | |
575 | +struct ccs_page_dump { | |
576 | + struct page *page; /* Previously dumped page. */ | |
577 | + char *data; /* Contents of "page". Size is PAGE_SIZE. */ | |
578 | +}; | |
579 | + | |
580 | +/* Structure for entries which follows "struct ccs_condition". */ | |
581 | +union ccs_condition_element { | |
582 | + struct { | |
583 | + enum ccs_conditions_index left; | |
584 | + enum ccs_conditions_index right; | |
585 | + bool is_not; | |
586 | + u8 radix; | |
587 | + }; | |
588 | + struct ccs_group *group; | |
589 | + const struct ccs_path_info *path; | |
590 | + u32 ip; /* Repeat 4 times if IPv6 address. */ | |
591 | + unsigned long value; | |
592 | +}; | |
593 | + | |
594 | +/* Structure for optional arguments. */ | |
595 | +struct ccs_condition { | |
596 | + struct ccs_shared_acl_head head; | |
597 | + u32 size; /* Memory size allocated for this entry. */ | |
598 | + /* union ccs_condition_element condition[]; */ | |
599 | +}; | |
600 | + | |
601 | +/* Structure for holding a token. */ | |
602 | +struct ccs_path_info { | |
603 | + const char *name; | |
604 | + u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
605 | + u32 total_len; /* = strlen(name) */ | |
606 | + u32 const_len; /* = ccs_const_part_length(name) */ | |
607 | +}; | |
608 | + | |
609 | +/* Structure for request info. */ | |
610 | +struct ccs_request_info { | |
611 | + /* For holding parameters. */ | |
612 | + struct ccs_request_param { | |
613 | + const struct ccs_path_info *s[4]; | |
614 | + unsigned long i[3]; | |
615 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
616 | + const u8 *ip; /* Big endian. */ | |
617 | + bool is_ipv6; | |
618 | +#endif | |
619 | + } param; | |
620 | + /* For holding pathnames and attributes. */ | |
621 | + struct { | |
622 | + /* | |
623 | + * True if ccs_get_attributes() was already called, false | |
624 | + * otherwise. | |
625 | + */ | |
626 | + bool validate_done; | |
627 | + /* True if @stat[] is valid. */ | |
628 | + bool stat_valid[CCS_MAX_PATH_STAT]; | |
629 | + /* Pointer to file objects. */ | |
630 | + struct path path[2]; | |
631 | + /* | |
632 | + * Information on @path[0], @path[0]'s parent directory, | |
633 | + * @path[1] and @path[1]'s parent directory. | |
634 | + */ | |
635 | + struct ccs_mini_stat stat[CCS_MAX_PATH_STAT]; | |
636 | + /* | |
637 | + * Name of @path[0] and @path[1]. | |
638 | + * Cleared by ccs_crear_request_info(). | |
639 | + */ | |
640 | + struct ccs_path_info pathname[2]; | |
641 | + } obj; | |
642 | + struct { | |
643 | + struct linux_binprm *bprm; | |
644 | + struct ccs_domain_info *previous_domain; | |
645 | + /* For execute_handler. */ | |
646 | + char *handler; /* kstrdup(handler_path->name, GFP_NOFS) */ | |
647 | + /* For dumping argv[] and envp[]. */ | |
648 | + struct ccs_page_dump dump; | |
649 | + /* For temporary use. Size is CCS_EXEC_TMPSIZE bytes. */ | |
650 | + char *tmp; | |
651 | + }; | |
652 | + /* | |
653 | + * Name of current thread's executable. | |
654 | + * Cleared by ccs_crear_request_info(). | |
655 | + */ | |
656 | + struct ccs_path_info exename; | |
657 | + /* | |
658 | + * Matching "struct ccs_acl_info" is copied. Used for ccs-queryd. | |
659 | + * Valid until ccs_read_unlock(). | |
660 | + */ | |
661 | + struct ccs_acl_info *matched_acl; | |
662 | + /* | |
663 | + * Matching handler and domain transition are copied. | |
664 | + * Valid until ccs_read_unlock(). | |
665 | + */ | |
666 | + const struct ccs_path_info *handler_path; | |
667 | + const struct ccs_path_info *transition; | |
668 | + const struct ccs_path_info *handler_path_candidate; | |
669 | + const struct ccs_path_info *transition_candidate; | |
670 | + /* | |
671 | + * For holding operation index used for this request. | |
672 | + * One of values in "enum ccs_mac_index". | |
673 | + */ | |
674 | + enum ccs_mac_index type; | |
675 | + /* For holding matching result. */ | |
676 | + enum ccs_matching_result result; | |
677 | + /* | |
678 | + * For counting number of retries made for this request. | |
679 | + * This counter is incremented whenever ccs_supervisor() returned | |
680 | + * CCS_RETRY_REQUEST. | |
681 | + */ | |
682 | + u8 retry; | |
683 | + /* For holding max audit log count for this matching entry. */ | |
684 | + u8 audit; | |
685 | + /* | |
686 | + * Set to true if condition could not be checked due to out of memory. | |
687 | + * This flag is used for returning out of memory flag back to | |
688 | + * ccs_check_acl_list(). Thus, this flag will not be set if out of | |
689 | + * memory occurred before ccs_check_acl_list() is called. | |
690 | + */ | |
691 | + bool failed_by_oom; | |
692 | +}; | |
693 | + | |
694 | +/* Structure for domain information. */ | |
695 | +struct ccs_domain_info { | |
696 | + struct list_head list; | |
697 | + /* Name of this domain. Never NULL. */ | |
698 | + const struct ccs_path_info *domainname; | |
699 | +}; | |
700 | + | |
701 | +/* Structure for holding string data. */ | |
702 | +struct ccs_name { | |
703 | + struct ccs_shared_acl_head head; | |
704 | + int size; /* Memory size allocated for this entry. */ | |
705 | + struct ccs_path_info entry; | |
706 | +}; | |
707 | + | |
708 | +/* Structure for reading/writing policy via /proc/ccs/ interfaces. */ | |
709 | +struct ccs_io_buffer { | |
710 | + /* Exclusive lock for this structure. */ | |
711 | + struct mutex io_sem; | |
712 | + char __user *read_user_buf; | |
713 | + size_t read_user_buf_avail; | |
714 | + struct { | |
715 | + struct list_head *group; | |
716 | + struct list_head *acl; | |
717 | + struct list_head *subacl; | |
718 | + const union ccs_condition_element *cond; | |
719 | + size_t avail; | |
720 | + unsigned int step; | |
721 | + unsigned int query_index; | |
722 | + u16 index; | |
723 | + u8 cond_step; | |
724 | + u8 w_pos; | |
725 | + enum ccs_mac_index acl_index; | |
726 | + bool eof; | |
727 | + bool print_this_acl_only; | |
728 | + bool version_done; | |
729 | + bool stat_done; | |
730 | + bool quota_done; | |
731 | + bool group_done; | |
732 | + const char *w[CCS_MAX_IO_READ_QUEUE]; | |
733 | + } r; | |
734 | + struct { | |
735 | + char *data; | |
736 | + struct ccs_acl_info *acl; | |
737 | + size_t avail; | |
738 | + enum ccs_mac_index acl_index; | |
739 | + bool is_delete; | |
740 | + bool is_deny; | |
741 | + u16 priority; | |
742 | + } w; | |
743 | + /* Buffer for reading. */ | |
744 | + char *read_buf; | |
745 | + /* Size of read buffer. */ | |
746 | + size_t readbuf_size; | |
747 | + /* Buffer for writing. */ | |
748 | + char *write_buf; | |
749 | + /* Size of write buffer. */ | |
750 | + size_t writebuf_size; | |
751 | + /* Type of interface. */ | |
752 | + enum ccs_proc_interface_index type; | |
753 | + /* Users counter protected by ccs_io_buffer_list_lock. */ | |
754 | + u8 users; | |
755 | + /* List for telling GC not to kfree() elements. */ | |
756 | + struct list_head list; | |
757 | +}; | |
758 | + | |
759 | +/* Structure for representing YYYY/MM/DD hh/mm/ss. */ | |
760 | +struct ccs_time { | |
761 | + u16 year; | |
762 | + u8 month; | |
763 | + u8 day; | |
764 | + u8 hour; | |
765 | + u8 min; | |
766 | + u8 sec; | |
767 | +}; | |
768 | + | |
769 | +/* Prototype definition for "struct ccsecurity_operations". */ | |
770 | + | |
771 | +void __init ccs_permission_init(void); | |
772 | +void __init ccs_mm_init(void); | |
773 | + | |
774 | +/* Prototype definition for internal use. */ | |
775 | + | |
776 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | |
777 | + struct ccs_page_dump *dump); | |
778 | +bool ccs_get_exename(struct ccs_path_info *buf); | |
779 | +bool ccs_manager(void); | |
780 | +bool ccs_transit_domain(const char *domainname); | |
781 | +char *ccs_encode(const char *str); | |
782 | +char *ccs_encode2(const char *str, int str_len); | |
783 | +char *ccs_realpath(struct path *path); | |
784 | +char *ccs_get_exe(void); | |
785 | +int ccs_audit_log(struct ccs_request_info *r); | |
786 | +int ccs_check_acl(struct ccs_request_info *r, const bool clear); | |
787 | +void ccs_del_condition(struct list_head *element); | |
788 | +void ccs_fill_path_info(struct ccs_path_info *ptr); | |
789 | +void ccs_get_attributes(struct ccs_request_info *r); | |
790 | +void ccs_notify_gc(struct ccs_io_buffer *head, const bool is_register); | |
791 | +void ccs_populate_patharg(struct ccs_request_info *r, const bool first); | |
792 | +void ccs_transition_failed(const char *domainname); | |
793 | +void ccs_warn_oom(const char *function); | |
794 | + | |
795 | +/* Variable definition for internal use. */ | |
796 | + | |
797 | +extern bool ccs_policy_loaded; | |
798 | +extern struct ccs_domain_info ccs_kernel_domain; | |
799 | +extern struct ccs_path_info ccs_null_name; | |
800 | +extern struct list_head ccs_acl_list[CCS_MAX_MAC_INDEX]; | |
801 | +extern struct list_head ccs_condition_list; | |
802 | +extern struct list_head ccs_domain_list; | |
803 | +extern struct list_head ccs_group_list[CCS_MAX_GROUP]; | |
804 | +extern struct list_head ccs_name_list[CCS_MAX_HASH]; | |
805 | +extern struct mutex ccs_policy_lock; | |
806 | +extern struct srcu_struct ccs_ss; | |
807 | +extern unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | |
808 | + | |
809 | +/* Inlined functions for internal use. */ | |
810 | + | |
811 | +/** | |
812 | + * ccs_pathcmp - strcmp() for "struct ccs_path_info" structure. | |
813 | + * | |
814 | + * @a: Pointer to "struct ccs_path_info". | |
815 | + * @b: Pointer to "struct ccs_path_info". | |
816 | + * | |
817 | + * Returns true if @a != @b, false otherwise. | |
818 | + */ | |
819 | +static inline bool ccs_pathcmp(const struct ccs_path_info *a, | |
820 | + const struct ccs_path_info *b) | |
821 | +{ | |
822 | + return a->hash != b->hash || strcmp(a->name, b->name); | |
823 | +} | |
824 | + | |
825 | +/** | |
826 | + * ccs_read_lock - Take lock for protecting policy. | |
827 | + * | |
828 | + * Returns index number for ccs_read_unlock(). | |
829 | + */ | |
830 | +static inline int ccs_read_lock(void) | |
831 | +{ | |
832 | + return srcu_read_lock(&ccs_ss); | |
833 | +} | |
834 | + | |
835 | +/** | |
836 | + * ccs_read_unlock - Release lock for protecting policy. | |
837 | + * | |
838 | + * @idx: Index number returned by ccs_read_lock(). | |
839 | + * | |
840 | + * Returns nothing. | |
841 | + */ | |
842 | +static inline void ccs_read_unlock(const int idx) | |
843 | +{ | |
844 | + srcu_read_unlock(&ccs_ss, idx); | |
845 | +} | |
846 | + | |
847 | +/** | |
848 | + * ccs_tasklist_lock - Take lock for reading list of "struct task_struct". | |
849 | + * | |
850 | + * Returns nothing. | |
851 | + */ | |
852 | +static inline void ccs_tasklist_lock(void) | |
853 | +{ | |
854 | + rcu_read_lock(); | |
855 | +} | |
856 | + | |
857 | +/** | |
858 | + * ccs_tasklist_unlock - Release lock for reading list of "struct task_struct". | |
859 | + * | |
860 | + * Returns nothing. | |
861 | + */ | |
862 | +static inline void ccs_tasklist_unlock(void) | |
863 | +{ | |
864 | + rcu_read_unlock(); | |
865 | +} | |
866 | + | |
867 | +/** | |
868 | + * ccs_sys_getppid - Copy of getppid(). | |
869 | + * | |
870 | + * Returns parent process's PID. | |
871 | + * | |
872 | + * Alpha does not have getppid() defined. To be able to build this module on | |
873 | + * Alpha, I have to copy getppid() from kernel/timer.c. | |
874 | + */ | |
875 | +static inline pid_t ccs_sys_getppid(void) | |
876 | +{ | |
877 | + pid_t pid; | |
878 | + rcu_read_lock(); | |
879 | + pid = task_tgid_vnr(rcu_dereference(current->real_parent)); | |
880 | + rcu_read_unlock(); | |
881 | + return pid; | |
882 | +} | |
883 | + | |
884 | +/** | |
885 | + * ccs_sys_getpid - Copy of getpid(). | |
886 | + * | |
887 | + * Returns current thread's PID. | |
888 | + * | |
889 | + * Alpha does not have getpid() defined. To be able to build this module on | |
890 | + * Alpha, I have to copy getpid() from kernel/timer.c. | |
891 | + */ | |
892 | +static inline pid_t ccs_sys_getpid(void) | |
893 | +{ | |
894 | + return task_tgid_vnr(current); | |
895 | +} | |
896 | + | |
897 | +#if defined(CONFIG_SLOB) | |
898 | + | |
899 | +/** | |
900 | + * ccs_round2 - Round up to power of 2 for calculating memory usage. | |
901 | + * | |
902 | + * @size: Size to be rounded up. | |
903 | + * | |
904 | + * Returns @size. | |
905 | + * | |
906 | + * Since SLOB does not round up, this function simply returns @size. | |
907 | + */ | |
908 | +static inline int ccs_round2(size_t size) | |
909 | +{ | |
910 | + return size; | |
911 | +} | |
912 | + | |
913 | +#else | |
914 | + | |
915 | +/** | |
916 | + * ccs_round2 - Round up to power of 2 for calculating memory usage. | |
917 | + * | |
918 | + * @size: Size to be rounded up. | |
919 | + * | |
920 | + * Returns rounded size. | |
921 | + * | |
922 | + * Strictly speaking, SLAB may be able to allocate (e.g.) 96 bytes instead of | |
923 | + * (e.g.) 128 bytes. | |
924 | + */ | |
925 | +static inline int ccs_round2(size_t size) | |
926 | +{ | |
927 | +#if PAGE_SIZE == 4096 | |
928 | + size_t bsize = 32; | |
929 | +#else | |
930 | + size_t bsize = 64; | |
931 | +#endif | |
932 | + if (!size) | |
933 | + return 0; | |
934 | + while (size > bsize) | |
935 | + bsize <<= 1; | |
936 | + return bsize; | |
937 | +} | |
938 | + | |
939 | +#endif | |
940 | + | |
941 | +/** | |
942 | + * ccs_put_condition - Drop reference on "struct ccs_condition". | |
943 | + * | |
944 | + * @cond: Pointer to "struct ccs_condition". Maybe NULL. | |
945 | + * | |
946 | + * Returns nothing. | |
947 | + */ | |
948 | +static inline void ccs_put_condition(struct ccs_condition *cond) | |
949 | +{ | |
950 | + if (cond) | |
951 | + atomic_dec(&cond->head.users); | |
952 | +} | |
953 | + | |
954 | +/** | |
955 | + * ccs_put_group - Drop reference on "struct ccs_group". | |
956 | + * | |
957 | + * @group: Pointer to "struct ccs_group". Maybe NULL. | |
958 | + * | |
959 | + * Returns nothing. | |
960 | + */ | |
961 | +static inline void ccs_put_group(struct ccs_group *group) | |
962 | +{ | |
963 | + if (group) | |
964 | + atomic_dec(&group->head.users); | |
965 | +} | |
966 | + | |
967 | +/** | |
968 | + * ccs_put_name - Drop reference on "struct ccs_name". | |
969 | + * | |
970 | + * @name: Pointer to "struct ccs_path_info". Maybe NULL. | |
971 | + * | |
972 | + * Returns nothing. | |
973 | + */ | |
974 | +static inline void ccs_put_name(const struct ccs_path_info *name) | |
975 | +{ | |
976 | + if (name) | |
977 | + atomic_dec(&container_of(name, struct ccs_name, entry)-> | |
978 | + head.users); | |
979 | +} | |
980 | + | |
981 | +/* For importing variables and functions. */ | |
982 | +extern const struct ccsecurity_exports ccsecurity_exports; | |
983 | + | |
984 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
985 | + | |
986 | +/* | |
987 | + * Structure for holding "struct ccs_domain_info *" and "u32 ccs_flags" for | |
988 | + * each "struct task_struct". | |
989 | + * | |
990 | + * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | |
991 | + * are maintained outside that "struct task_struct". Therefore, ccs_security | |
992 | + * != task_struct . This keeps KABI for distributor's prebuilt kernels but | |
993 | + * entails slow access. | |
994 | + * | |
995 | + * Memory for this structure is allocated when current thread tries to access | |
996 | + * it. Therefore, if memory allocation failed, current thread will be killed by | |
997 | + * SIGKILL. Note that if current->pid == 1, sending SIGKILL won't work. | |
998 | + */ | |
999 | +struct ccs_security { | |
1000 | + struct list_head list; | |
1001 | + const struct task_struct *task; | |
1002 | + struct ccs_domain_info *ccs_domain_info; | |
1003 | + u32 ccs_flags; | |
1004 | + struct rcu_head rcu; | |
1005 | +}; | |
1006 | + | |
1007 | +#define CCS_TASK_SECURITY_HASH_BITS 12 | |
1008 | +#define CCS_MAX_TASK_SECURITY_HASH (1u << CCS_TASK_SECURITY_HASH_BITS) | |
1009 | +extern struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | |
1010 | + | |
1011 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task); | |
1012 | + | |
1013 | +/** | |
1014 | + * ccs_current_security - Get "struct ccs_security" for current thread. | |
1015 | + * | |
1016 | + * Returns pointer to "struct ccs_security" for current thread. | |
1017 | + */ | |
1018 | +static inline struct ccs_security *ccs_current_security(void) | |
1019 | +{ | |
1020 | + return ccs_find_task_security(current); | |
1021 | +} | |
1022 | + | |
1023 | +/** | |
1024 | + * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | |
1025 | + * | |
1026 | + * @task: Pointer to "struct task_struct". | |
1027 | + * | |
1028 | + * Returns pointer to "struct ccs_security" for specified thread. | |
1029 | + */ | |
1030 | +static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | |
1031 | +{ | |
1032 | + struct ccs_domain_info *domain; | |
1033 | + rcu_read_lock(); | |
1034 | + domain = ccs_find_task_security(task)->ccs_domain_info; | |
1035 | + rcu_read_unlock(); | |
1036 | + return domain; | |
1037 | +} | |
1038 | + | |
1039 | +/** | |
1040 | + * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | |
1041 | + * | |
1042 | + * Returns pointer to "struct ccs_domain_info" for current thread. | |
1043 | + */ | |
1044 | +static inline struct ccs_domain_info *ccs_current_domain(void) | |
1045 | +{ | |
1046 | + return ccs_find_task_security(current)->ccs_domain_info; | |
1047 | +} | |
1048 | + | |
1049 | +/** | |
1050 | + * ccs_task_flags - Get flags for specified thread. | |
1051 | + * | |
1052 | + * @task: Pointer to "struct task_struct". | |
1053 | + * | |
1054 | + * Returns flags for specified thread. | |
1055 | + */ | |
1056 | +static inline u32 ccs_task_flags(struct task_struct *task) | |
1057 | +{ | |
1058 | + u32 ccs_flags; | |
1059 | + rcu_read_lock(); | |
1060 | + ccs_flags = ccs_find_task_security(task)->ccs_flags; | |
1061 | + rcu_read_unlock(); | |
1062 | + return ccs_flags; | |
1063 | +} | |
1064 | + | |
1065 | +/** | |
1066 | + * ccs_current_flags - Get flags for current thread. | |
1067 | + * | |
1068 | + * Returns flags for current thread. | |
1069 | + */ | |
1070 | +static inline u32 ccs_current_flags(void) | |
1071 | +{ | |
1072 | + return ccs_find_task_security(current)->ccs_flags; | |
1073 | +} | |
1074 | + | |
1075 | +#else | |
1076 | + | |
1077 | +/* | |
1078 | + * "struct ccs_domain_info *" and "u32 ccs_flags" for each "struct task_struct" | |
1079 | + * are maintained inside that "struct task_struct". Therefore, ccs_security == | |
1080 | + * task_struct . This allows fast access but breaks KABI checks for | |
1081 | + * distributor's prebuilt kernels due to changes in "struct task_struct". | |
1082 | + */ | |
1083 | +#define ccs_security task_struct | |
1084 | + | |
1085 | +/** | |
1086 | + * ccs_find_task_security - Find "struct ccs_security" for given task. | |
1087 | + * | |
1088 | + * @task: Pointer to "struct task_struct". | |
1089 | + * | |
1090 | + * Returns pointer to "struct ccs_security". | |
1091 | + */ | |
1092 | +static inline struct ccs_security *ccs_find_task_security(struct task_struct * | |
1093 | + task) | |
1094 | +{ | |
1095 | + return task; | |
1096 | +} | |
1097 | + | |
1098 | +/** | |
1099 | + * ccs_current_security - Get "struct ccs_security" for current thread. | |
1100 | + * | |
1101 | + * Returns pointer to "struct ccs_security" for current thread. | |
1102 | + */ | |
1103 | +static inline struct ccs_security *ccs_current_security(void) | |
1104 | +{ | |
1105 | + return ccs_find_task_security(current); | |
1106 | +} | |
1107 | + | |
1108 | +/** | |
1109 | + * ccs_task_domain - Get "struct ccs_domain_info" for specified thread. | |
1110 | + * | |
1111 | + * @task: Pointer to "struct task_struct". | |
1112 | + * | |
1113 | + * Returns pointer to "struct ccs_security" for specified thread. | |
1114 | + */ | |
1115 | +static inline struct ccs_domain_info *ccs_task_domain(struct task_struct *task) | |
1116 | +{ | |
1117 | + struct ccs_domain_info *domain = task->ccs_domain_info; | |
1118 | + return domain ? domain : &ccs_kernel_domain; | |
1119 | +} | |
1120 | + | |
1121 | +/** | |
1122 | + * ccs_current_domain - Get "struct ccs_domain_info" for current thread. | |
1123 | + * | |
1124 | + * Returns pointer to "struct ccs_domain_info" for current thread. | |
1125 | + * | |
1126 | + * If current thread does not belong to a domain (which is true for initial | |
1127 | + * init_task in order to hide ccs_kernel_domain from this module), | |
1128 | + * current thread enters into ccs_kernel_domain. | |
1129 | + */ | |
1130 | +static inline struct ccs_domain_info *ccs_current_domain(void) | |
1131 | +{ | |
1132 | + struct task_struct *task = current; | |
1133 | + if (!task->ccs_domain_info) | |
1134 | + task->ccs_domain_info = &ccs_kernel_domain; | |
1135 | + return task->ccs_domain_info; | |
1136 | +} | |
1137 | + | |
1138 | +/** | |
1139 | + * ccs_task_flags - Get flags for specified thread. | |
1140 | + * | |
1141 | + * @task: Pointer to "struct task_struct". | |
1142 | + * | |
1143 | + * Returns flags for specified thread. | |
1144 | + */ | |
1145 | +static inline u32 ccs_task_flags(struct task_struct *task) | |
1146 | +{ | |
1147 | + return ccs_find_task_security(task)->ccs_flags; | |
1148 | +} | |
1149 | + | |
1150 | +/** | |
1151 | + * ccs_current_flags - Get flags for current thread. | |
1152 | + * | |
1153 | + * Returns flags for current thread. | |
1154 | + */ | |
1155 | +static inline u32 ccs_current_flags(void) | |
1156 | +{ | |
1157 | + return ccs_find_task_security(current)->ccs_flags; | |
1158 | +} | |
1159 | + | |
1160 | +#endif | |
1161 | + | |
1162 | +#endif |
@@ -0,0 +1,4401 @@ | ||
1 | +/* | |
2 | + * security/ccsecurity/policy_io.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +/***** SECTION1: Constants definition *****/ | |
12 | + | |
13 | +/* Define this to enable debug mode. */ | |
14 | +/* #define DEBUG_CONDITION */ | |
15 | + | |
16 | +#ifdef DEBUG_CONDITION | |
17 | +#define dprintk printk | |
18 | +#else | |
19 | +#define dprintk(...) do { } while (0) | |
20 | +#endif | |
21 | + | |
22 | +/* String table for operation. */ | |
23 | +static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX] = { | |
24 | + [CCS_MAC_EXECUTE] = "execute", | |
25 | + [CCS_MAC_READ] = "read", | |
26 | + [CCS_MAC_WRITE] = "write", | |
27 | + [CCS_MAC_APPEND] = "append", | |
28 | + [CCS_MAC_CREATE] = "create", | |
29 | + [CCS_MAC_UNLINK] = "unlink", | |
30 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
31 | + [CCS_MAC_GETATTR] = "getattr", | |
32 | +#endif | |
33 | + [CCS_MAC_MKDIR] = "mkdir", | |
34 | + [CCS_MAC_RMDIR] = "rmdir", | |
35 | + [CCS_MAC_MKFIFO] = "mkfifo", | |
36 | + [CCS_MAC_MKSOCK] = "mksock", | |
37 | + [CCS_MAC_TRUNCATE] = "truncate", | |
38 | + [CCS_MAC_SYMLINK] = "symlink", | |
39 | + [CCS_MAC_MKBLOCK] = "mkblock", | |
40 | + [CCS_MAC_MKCHAR] = "mkchar", | |
41 | + [CCS_MAC_LINK] = "link", | |
42 | + [CCS_MAC_RENAME] = "rename", | |
43 | + [CCS_MAC_CHMOD] = "chmod", | |
44 | + [CCS_MAC_CHOWN] = "chown", | |
45 | + [CCS_MAC_CHGRP] = "chgrp", | |
46 | + [CCS_MAC_IOCTL] = "ioctl", | |
47 | + [CCS_MAC_CHROOT] = "chroot", | |
48 | + [CCS_MAC_MOUNT] = "mount", | |
49 | + [CCS_MAC_UMOUNT] = "unmount", | |
50 | + [CCS_MAC_PIVOT_ROOT] = "pivot_root", | |
51 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
52 | + [CCS_MAC_INET_STREAM_BIND] = "inet_stream_bind", | |
53 | + [CCS_MAC_INET_STREAM_LISTEN] = "inet_stream_listen", | |
54 | + [CCS_MAC_INET_STREAM_CONNECT] = "inet_stream_connect", | |
55 | + [CCS_MAC_INET_STREAM_ACCEPT] = "inet_stream_accept", | |
56 | + [CCS_MAC_INET_DGRAM_BIND] = "inet_dgram_bind", | |
57 | + [CCS_MAC_INET_DGRAM_SEND] = "inet_dgram_send", | |
58 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
59 | + [CCS_MAC_INET_DGRAM_RECV] = "inet_dgram_recv", | |
60 | +#endif | |
61 | + [CCS_MAC_INET_RAW_BIND] = "inet_raw_bind", | |
62 | + [CCS_MAC_INET_RAW_SEND] = "inet_raw_send", | |
63 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
64 | + [CCS_MAC_INET_RAW_RECV] = "inet_raw_recv", | |
65 | +#endif | |
66 | + [CCS_MAC_UNIX_STREAM_BIND] = "unix_stream_bind", | |
67 | + [CCS_MAC_UNIX_STREAM_LISTEN] = "unix_stream_listen", | |
68 | + [CCS_MAC_UNIX_STREAM_CONNECT] = "unix_stream_connect", | |
69 | + [CCS_MAC_UNIX_STREAM_ACCEPT] = "unix_stream_accept", | |
70 | + [CCS_MAC_UNIX_DGRAM_BIND] = "unix_dgram_bind", | |
71 | + [CCS_MAC_UNIX_DGRAM_SEND] = "unix_dgram_send", | |
72 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
73 | + [CCS_MAC_UNIX_DGRAM_RECV] = "unix_dgram_recv", | |
74 | +#endif | |
75 | + [CCS_MAC_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", | |
76 | + [CCS_MAC_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", | |
77 | + [CCS_MAC_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", | |
78 | + [CCS_MAC_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", | |
79 | +#endif | |
80 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
81 | + [CCS_MAC_ENVIRON] = "environ", | |
82 | +#endif | |
83 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
84 | + [CCS_MAC_PTRACE] = "ptrace", | |
85 | +#endif | |
86 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
87 | + [CCS_MAC_SIGNAL] = "signal", | |
88 | +#endif | |
89 | + [CCS_MAC_MODIFY_POLICY] = "modify_policy", | |
90 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
91 | + [CCS_MAC_USE_NETLINK_SOCKET] = "use_netlink_socket", | |
92 | + [CCS_MAC_USE_PACKET_SOCKET] = "use_packet_socket", | |
93 | + [CCS_MAC_USE_REBOOT] = "use_reboot", | |
94 | + [CCS_MAC_USE_VHANGUP] = "use_vhangup", | |
95 | + [CCS_MAC_SET_TIME] = "set_time", | |
96 | + [CCS_MAC_SET_PRIORITY] = "set_priority", | |
97 | + [CCS_MAC_SET_HOSTNAME] = "set_hostname", | |
98 | + [CCS_MAC_USE_KERNEL_MODULE] = "use_kernel_module", | |
99 | + [CCS_MAC_USE_NEW_KERNEL] = "use_new_kernel", | |
100 | +#endif | |
101 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
102 | + [CCS_MAC_AUTO_DOMAIN_TRANSITION] = "auto_domain_transition", | |
103 | +#endif | |
104 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
105 | + [CCS_MAC_MANUAL_DOMAIN_TRANSITION] = "manual_domain_transition", | |
106 | +#endif | |
107 | +}; | |
108 | + | |
109 | +/* String table for conditions. */ | |
110 | +static const char *const ccs_condition_keyword[CCS_MAX_CONDITION_KEYWORD] = { | |
111 | + [CCS_SELF_UID] = "uid", | |
112 | + [CCS_SELF_EUID] = "euid", | |
113 | + [CCS_SELF_SUID] = "suid", | |
114 | + [CCS_SELF_FSUID] = "fsuid", | |
115 | + [CCS_SELF_GID] = "gid", | |
116 | + [CCS_SELF_EGID] = "egid", | |
117 | + [CCS_SELF_SGID] = "sgid", | |
118 | + [CCS_SELF_FSGID] = "fsgid", | |
119 | + [CCS_SELF_PID] = "pid", | |
120 | + [CCS_SELF_PPID] = "ppid", | |
121 | + [CCS_TASK_TYPE] = "type", | |
122 | + [CCS_SELF_DOMAIN] = "domain", | |
123 | + [CCS_SELF_EXE] = "exe", | |
124 | + [CCS_EXEC_ARGC] = "argc", | |
125 | + [CCS_EXEC_ENVC] = "envc", | |
126 | + [CCS_OBJ_IS_SOCKET] = "socket", | |
127 | + [CCS_OBJ_IS_SYMLINK] = "symlink", | |
128 | + [CCS_OBJ_IS_FILE] = "file", | |
129 | + [CCS_OBJ_IS_BLOCK_DEV] = "block", | |
130 | + [CCS_OBJ_IS_DIRECTORY] = "directory", | |
131 | + [CCS_OBJ_IS_CHAR_DEV] = "char", | |
132 | + [CCS_OBJ_IS_FIFO] = "fifo", | |
133 | + [CCS_MODE_SETUID] = "setuid", | |
134 | + [CCS_MODE_SETGID] = "setgid", | |
135 | + [CCS_MODE_STICKY] = "sticky", | |
136 | + [CCS_MODE_OWNER_READ] = "owner_read", | |
137 | + [CCS_MODE_OWNER_WRITE] = "owner_write", | |
138 | + [CCS_MODE_OWNER_EXECUTE] = "owner_execute", | |
139 | + [CCS_MODE_GROUP_READ] = "group_read", | |
140 | + [CCS_MODE_GROUP_WRITE] = "group_write", | |
141 | + [CCS_MODE_GROUP_EXECUTE] = "group_execute", | |
142 | + [CCS_MODE_OTHERS_READ] = "others_read", | |
143 | + [CCS_MODE_OTHERS_WRITE] = "others_write", | |
144 | + [CCS_MODE_OTHERS_EXECUTE] = "others_execute", | |
145 | + [CCS_TASK_EXECUTE_HANDLER] = "execute_handler", | |
146 | + [CCS_HANDLER_PATH] = "handler", | |
147 | + [CCS_TRANSIT_DOMAIN] = "transition", | |
148 | +}; | |
149 | + | |
150 | +/* String table for file attributes. */ | |
151 | +static const char *const ccs_path_attribute[CCS_MAX_PATH_ATTRIBUTE] = { | |
152 | + [CCS_PATH_ATTRIBUTE_UID] = "uid", | |
153 | + [CCS_PATH_ATTRIBUTE_GID] = "gid", | |
154 | + [CCS_PATH_ATTRIBUTE_INO] = "ino", | |
155 | + [CCS_PATH_ATTRIBUTE_MAJOR] = "major", | |
156 | + [CCS_PATH_ATTRIBUTE_MINOR] = "minor", | |
157 | + [CCS_PATH_ATTRIBUTE_PERM] = "perm", | |
158 | + [CCS_PATH_ATTRIBUTE_TYPE] = "type", | |
159 | + [CCS_PATH_ATTRIBUTE_DEV_MAJOR] = "dev_major", | |
160 | + [CCS_PATH_ATTRIBUTE_DEV_MINOR] = "dev_minor", | |
161 | + [CCS_PATH_ATTRIBUTE_FSMAGIC] = "fsmagic", | |
162 | +}; | |
163 | + | |
164 | +/* String table for grouping keywords. */ | |
165 | +static const char * const ccs_group_name[CCS_MAX_GROUP] = { | |
166 | + [CCS_STRING_GROUP] = "string_group", | |
167 | + [CCS_NUMBER_GROUP] = "number_group", | |
168 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
169 | + [CCS_IP_GROUP] = "ip_group", | |
170 | +#endif | |
171 | +}; | |
172 | + | |
173 | +/* String table for stat info. */ | |
174 | +static const char * const ccs_memory_headers[CCS_MAX_MEMORY_STAT] = { | |
175 | + [CCS_MEMORY_POLICY] = "policy", | |
176 | + [CCS_MEMORY_AUDIT] = "audit", | |
177 | + [CCS_MEMORY_QUERY] = "query", | |
178 | +}; | |
179 | + | |
180 | +/***** SECTION2: Structure definition *****/ | |
181 | + | |
182 | +struct iattr; | |
183 | + | |
184 | +/* Structure for query. */ | |
185 | +struct ccs_query { | |
186 | + struct list_head list; | |
187 | + struct ccs_acl_info *acl; | |
188 | + char *query; | |
189 | + size_t query_len; | |
190 | + unsigned int serial; | |
191 | + u8 timer; | |
192 | + u8 answer; | |
193 | + u8 retry; | |
194 | +}; | |
195 | + | |
196 | +/* Structure for audit log. */ | |
197 | +struct ccs_log { | |
198 | + struct list_head list; | |
199 | + char *log; | |
200 | + int size; | |
201 | + enum ccs_matching_result result; | |
202 | +}; | |
203 | + | |
204 | +/* Structure for holding single condition component. */ | |
205 | +struct ccs_cond_tmp { | |
206 | + u8 left; | |
207 | + u8 right; | |
208 | + bool is_not; | |
209 | + u8 radix; | |
210 | + struct ccs_group *group; | |
211 | + const struct ccs_path_info *path; | |
212 | + struct in6_addr ipv6[2]; | |
213 | + unsigned long value[2]; | |
214 | + unsigned long argv; | |
215 | + const struct ccs_path_info *envp; | |
216 | +}; | |
217 | + | |
218 | +/***** SECTION3: Prototype definition section *****/ | |
219 | + | |
220 | +static bool ccs_correct_domain(const unsigned char *domainname); | |
221 | +static bool ccs_correct_word(const char *string); | |
222 | +static bool ccs_flush(struct ccs_io_buffer *head); | |
223 | +static bool ccs_print_condition(struct ccs_io_buffer *head, | |
224 | + const struct ccs_condition *cond); | |
225 | +static bool ccs_memory_ok(const void *ptr, const unsigned int size); | |
226 | +static bool ccs_read_acl(struct ccs_io_buffer *head, | |
227 | + const struct ccs_acl_info *acl); | |
228 | +static bool ccs_read_group(struct ccs_io_buffer *head); | |
229 | +static bool ccs_select_acl(struct ccs_io_buffer *head, const char *data); | |
230 | +static bool ccs_set_lf(struct ccs_io_buffer *head); | |
231 | +static bool ccs_str_starts(char **src, const char *find); | |
232 | +static char *ccs_init_log(struct ccs_request_info *r); | |
233 | +static char *ccs_print_bprm(struct linux_binprm *bprm, | |
234 | + struct ccs_page_dump *dump); | |
235 | +static char *ccs_print_trailer(struct ccs_request_info *r); | |
236 | +static char *ccs_read_token(struct ccs_io_buffer *head); | |
237 | +static const char *ccs_yesno(const unsigned int value); | |
238 | +static const struct ccs_path_info *ccs_get_dqword(char *start); | |
239 | +static const struct ccs_path_info *ccs_get_name(const char *name); | |
240 | +static int __init ccs_init_module(void); | |
241 | +static int ccs_open(struct inode *inode, struct file *file); | |
242 | +static int ccs_parse_policy(struct ccs_io_buffer *head, char *line); | |
243 | +static int ccs_release(struct inode *inode, struct file *file); | |
244 | +static int ccs_supervisor(struct ccs_request_info *r); | |
245 | +static int ccs_update_group(struct ccs_io_buffer *head, | |
246 | + const enum ccs_group_id type); | |
247 | +static int ccs_write_answer(struct ccs_io_buffer *head); | |
248 | +static int ccs_write_audit_quota(char *data); | |
249 | +static int ccs_write_memory_quota(char *data); | |
250 | +static int ccs_write_pid(struct ccs_io_buffer *head); | |
251 | +static int ccs_write_policy(struct ccs_io_buffer *head); | |
252 | +static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | |
253 | + loff_t *ppos); | |
254 | +static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | |
255 | + loff_t *ppos); | |
256 | +static ssize_t ccs_write(struct file *file, const char __user *buf, | |
257 | + size_t count, loff_t *ppos); | |
258 | +static struct ccs_condition *ccs_get_condition(struct ccs_io_buffer *head); | |
259 | +static struct ccs_domain_info *ccs_find_domain(const char *domainname); | |
260 | +static struct ccs_acl_info *ccs_find_acl_by_qid(unsigned int serial); | |
261 | +static struct ccs_group *ccs_get_group(struct ccs_io_buffer *head, | |
262 | + const enum ccs_group_id idx); | |
263 | +static enum ccs_value_type ccs_parse_ulong(unsigned long *result, char **str); | |
264 | +static unsigned int ccs_poll(struct file *file, poll_table *wait); | |
265 | +static void __init ccs_create_entry(const char *name, const umode_t mode, | |
266 | + struct proc_dir_entry *parent, | |
267 | + const u8 key); | |
268 | +static void __init ccs_load_builtin_policy(void); | |
269 | +static void __init ccs_policy_io_init(void); | |
270 | +static void __init ccs_proc_init(void); | |
271 | +static void ccs_check_profile(void); | |
272 | +static void ccs_convert_time(time_t time, struct ccs_time *stamp); | |
273 | +static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | |
274 | + __printf(2, 3); | |
275 | +static void ccs_normalize_line(unsigned char *buffer); | |
276 | +static void ccs_read_log(struct ccs_io_buffer *head); | |
277 | +static void ccs_read_pid(struct ccs_io_buffer *head); | |
278 | +static void ccs_read_policy(struct ccs_io_buffer *head); | |
279 | +static void ccs_read_query(struct ccs_io_buffer *head); | |
280 | +static void *ccs_commit_ok(void *data, const unsigned int size); | |
281 | +static bool ccs_read_quota(struct ccs_io_buffer *head); | |
282 | +static void ccs_read_stat(struct ccs_io_buffer *head); | |
283 | +static void ccs_read_version(struct ccs_io_buffer *head); | |
284 | +static void ccs_set_space(struct ccs_io_buffer *head); | |
285 | +static void ccs_set_string(struct ccs_io_buffer *head, const char *string); | |
286 | +static void ccs_update_stat(const u8 index); | |
287 | +static void ccs_write_log(struct ccs_request_info *r); | |
288 | + | |
289 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
290 | +static enum ccs_ipaddr_type ccs_parse_ipaddr(char *address, | |
291 | + struct in6_addr ipv6[2]); | |
292 | +static void ccs_print_ipv4(struct ccs_io_buffer *head, const u32 *ip); | |
293 | +static void ccs_print_ipv6(struct ccs_io_buffer *head, | |
294 | + const struct in6_addr *ip); | |
295 | +static void ccs_print_ip(struct ccs_io_buffer *head, | |
296 | + struct ccs_ip_group *member); | |
297 | +#endif | |
298 | + | |
299 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
300 | +static ssize_t ccs_write_self(struct file *file, const char __user *buf, | |
301 | + size_t count, loff_t *ppos); | |
302 | +#endif | |
303 | + | |
304 | +/***** SECTION4: Standalone functions section *****/ | |
305 | + | |
306 | +/** | |
307 | + * ccs_convert_time - Convert time_t to YYYY/MM/DD hh/mm/ss. | |
308 | + * | |
309 | + * @time: Seconds since 1970/01/01 00:00:00. | |
310 | + * @stamp: Pointer to "struct ccs_time". | |
311 | + * | |
312 | + * Returns nothing. | |
313 | + * | |
314 | + * This function does not handle Y2038 problem. | |
315 | + */ | |
316 | +static void ccs_convert_time(time_t time, struct ccs_time *stamp) | |
317 | +{ | |
318 | + static const u16 ccs_eom[2][12] = { | |
319 | + { 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | |
320 | + { 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | |
321 | + }; | |
322 | + u16 y; | |
323 | + u8 m; | |
324 | + bool r; | |
325 | + stamp->sec = time % 60; | |
326 | + time /= 60; | |
327 | + stamp->min = time % 60; | |
328 | + time /= 60; | |
329 | + stamp->hour = time % 24; | |
330 | + time /= 24; | |
331 | + for (y = 1970; ; y++) { | |
332 | + const unsigned short days = (y & 3) ? 365 : 366; | |
333 | + if (time < days) | |
334 | + break; | |
335 | + time -= days; | |
336 | + } | |
337 | + r = (y & 3) == 0; | |
338 | + for (m = 0; m < 11 && time >= ccs_eom[r][m]; m++); | |
339 | + if (m) | |
340 | + time -= ccs_eom[r][m - 1]; | |
341 | + stamp->year = y; | |
342 | + stamp->month = ++m; | |
343 | + stamp->day = ++time; | |
344 | +} | |
345 | + | |
346 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
347 | + | |
348 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) | |
349 | + | |
350 | +/* | |
351 | + * Routines for printing IPv4 or IPv6 address. | |
352 | + * These are copied from include/linux/kernel.h include/net/ipv6.h | |
353 | + * include/net/addrconf.h lib/hexdump.c lib/vsprintf.c and simplified. | |
354 | + */ | |
355 | +static inline int ipv6_addr_is_isatap(const struct in6_addr *addr) | |
356 | +{ | |
357 | + return (addr->s6_addr32[2] | htonl(0x02000000)) == htonl(0x02005EFE); | |
358 | +} | |
359 | + | |
360 | +static char *ip4_string(char *p, const u8 *addr) | |
361 | +{ | |
362 | + /* | |
363 | + * Since this function is called outside vsnprintf(), I can use | |
364 | + * sprintf() here. | |
365 | + */ | |
366 | + return p + | |
367 | + sprintf(p, "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); | |
368 | +} | |
369 | + | |
370 | +static char *ip6_compressed_string(char *p, const char *addr) | |
371 | +{ | |
372 | + int i, j, range; | |
373 | + unsigned char zerolength[8]; | |
374 | + int longest = 1; | |
375 | + int colonpos = -1; | |
376 | + u16 word; | |
377 | + u8 hi, lo; | |
378 | + bool needcolon = false; | |
379 | + bool useIPv4; | |
380 | + struct in6_addr in6; | |
381 | + | |
382 | + memcpy(&in6, addr, sizeof(struct in6_addr)); | |
383 | + | |
384 | + useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6); | |
385 | + | |
386 | + memset(zerolength, 0, sizeof(zerolength)); | |
387 | + | |
388 | + if (useIPv4) | |
389 | + range = 6; | |
390 | + else | |
391 | + range = 8; | |
392 | + | |
393 | + /* find position of longest 0 run */ | |
394 | + for (i = 0; i < range; i++) { | |
395 | + for (j = i; j < range; j++) { | |
396 | + if (in6.s6_addr16[j] != 0) | |
397 | + break; | |
398 | + zerolength[i]++; | |
399 | + } | |
400 | + } | |
401 | + for (i = 0; i < range; i++) { | |
402 | + if (zerolength[i] > longest) { | |
403 | + longest = zerolength[i]; | |
404 | + colonpos = i; | |
405 | + } | |
406 | + } | |
407 | + if (longest == 1) /* don't compress a single 0 */ | |
408 | + colonpos = -1; | |
409 | + | |
410 | + /* emit address */ | |
411 | + for (i = 0; i < range; i++) { | |
412 | + if (i == colonpos) { | |
413 | + if (needcolon || i == 0) | |
414 | + *p++ = ':'; | |
415 | + *p++ = ':'; | |
416 | + needcolon = false; | |
417 | + i += longest - 1; | |
418 | + continue; | |
419 | + } | |
420 | + if (needcolon) { | |
421 | + *p++ = ':'; | |
422 | + needcolon = false; | |
423 | + } | |
424 | + /* hex u16 without leading 0s */ | |
425 | + word = ntohs(in6.s6_addr16[i]); | |
426 | + hi = word >> 8; | |
427 | + lo = word & 0xff; | |
428 | + if (hi) { | |
429 | + if (hi > 0x0f) | |
430 | + p = pack_hex_byte(p, hi); | |
431 | + else | |
432 | + *p++ = hex_asc_lo(hi); | |
433 | + p = pack_hex_byte(p, lo); | |
434 | + } else if (lo > 0x0f) | |
435 | + p = pack_hex_byte(p, lo); | |
436 | + else | |
437 | + *p++ = hex_asc_lo(lo); | |
438 | + needcolon = true; | |
439 | + } | |
440 | + | |
441 | + if (useIPv4) { | |
442 | + if (needcolon) | |
443 | + *p++ = ':'; | |
444 | + p = ip4_string(p, &in6.s6_addr[12]); | |
445 | + } | |
446 | + *p = '\0'; | |
447 | + | |
448 | + return p; | |
449 | +} | |
450 | +#endif | |
451 | + | |
452 | +/** | |
453 | + * ccs_print_ipv4 - Print an IPv4 address. | |
454 | + * | |
455 | + * @head: Pointer to "struct ccs_io_buffer". | |
456 | + * @ip: Pointer to "u32" in network byte order. | |
457 | + * | |
458 | + * Returns nothing. | |
459 | + */ | |
460 | +static void ccs_print_ipv4(struct ccs_io_buffer *head, const u32 *ip) | |
461 | +{ | |
462 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
463 | + ccs_io_printf(head, "%pI4", ip); | |
464 | +#else | |
465 | + char addr[sizeof("255.255.255.255")]; | |
466 | + ip4_string(addr, (const u8 *) ip); | |
467 | + ccs_io_printf(head, "%s", addr); | |
468 | +#endif | |
469 | +} | |
470 | + | |
471 | +/** | |
472 | + * ccs_print_ipv6 - Print an IPv6 address. | |
473 | + * | |
474 | + * @head: Pointer to "struct ccs_io_buffer". | |
475 | + * @ip: Pointer to "struct in6_addr". | |
476 | + * | |
477 | + * Returns nothing. | |
478 | + */ | |
479 | +static void ccs_print_ipv6(struct ccs_io_buffer *head, | |
480 | + const struct in6_addr *ip) | |
481 | +{ | |
482 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
483 | + ccs_io_printf(head, "%pI6c", ip); | |
484 | +#else | |
485 | + char addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:" | |
486 | + "255.255.255.255")]; | |
487 | + ip6_compressed_string(addr, (const u8 *) ip); | |
488 | + ccs_io_printf(head, "%s", addr); | |
489 | +#endif | |
490 | +} | |
491 | + | |
492 | +/** | |
493 | + * ccs_print_ip - Print an IP address. | |
494 | + * | |
495 | + * @head: Pointer to "struct ccs_io_buffer". | |
496 | + * @member: Pointer to "struct ccs_ip_group". | |
497 | + * | |
498 | + * Returns nothing. | |
499 | + */ | |
500 | +static void ccs_print_ip(struct ccs_io_buffer *head, | |
501 | + struct ccs_ip_group *member) | |
502 | +{ | |
503 | + u8 i; | |
504 | + for (i = 0; i < 2; i++) { | |
505 | + if (member->is_ipv6) | |
506 | + ccs_print_ipv6(head, &member->ip[i]); | |
507 | + else | |
508 | + ccs_print_ipv4(head, (const u32 *) &member->ip[i]); | |
509 | + if (i) | |
510 | + break; | |
511 | + if (!memcmp(&member->ip[0], &member->ip[1], 16)) | |
512 | + break; | |
513 | + ccs_set_string(head, "-"); | |
514 | + } | |
515 | +} | |
516 | + | |
517 | +#endif | |
518 | + | |
519 | +/** | |
520 | + * ccs_get_sarg - Get attribute name of CCS_SARG argument. | |
521 | + * | |
522 | + * @type: One of values in "enum ccs_mac_index". | |
523 | + * @index: Index to return. | |
524 | + * | |
525 | + * Returns attribute name. | |
526 | + */ | |
527 | +static const char *ccs_get_sarg(const enum ccs_mac_index type, const u8 index) | |
528 | +{ | |
529 | + switch (type) { | |
530 | + case CCS_MAC_LINK: | |
531 | + case CCS_MAC_RENAME: | |
532 | + if (index == 0) | |
533 | + return "old_path"; | |
534 | + if (index == 1) | |
535 | + return "new_path"; | |
536 | + break; | |
537 | + case CCS_MAC_MOUNT: | |
538 | + if (index == 0) | |
539 | + return "source"; | |
540 | + if (index == 1) | |
541 | + return "target"; | |
542 | + if (index == 2) | |
543 | + return "fstype"; | |
544 | + if (index == 3) | |
545 | + return "data"; | |
546 | + break; | |
547 | + case CCS_MAC_PIVOT_ROOT: | |
548 | + if (index == 0) | |
549 | + return "new_root"; | |
550 | + if (index == 1) | |
551 | + return "put_old"; | |
552 | + break; | |
553 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
554 | + case CCS_MAC_ENVIRON: | |
555 | + if (index == 2) | |
556 | + return "name"; | |
557 | + if (index == 3) | |
558 | + return "value"; | |
559 | + /* fall through */ | |
560 | +#endif | |
561 | + case CCS_MAC_EXECUTE: | |
562 | + if (index == 0) | |
563 | + return "path"; | |
564 | + if (index == 1) | |
565 | + return "exec"; | |
566 | + break; | |
567 | + case CCS_MAC_SYMLINK: | |
568 | + if (index == 0) | |
569 | + return "path"; | |
570 | + if (index == 1) | |
571 | + return "target"; | |
572 | + break; | |
573 | + case CCS_MAC_READ: | |
574 | + case CCS_MAC_WRITE: | |
575 | + case CCS_MAC_APPEND: | |
576 | + case CCS_MAC_UNLINK: | |
577 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
578 | + case CCS_MAC_GETATTR: | |
579 | +#endif | |
580 | + case CCS_MAC_RMDIR: | |
581 | + case CCS_MAC_TRUNCATE: | |
582 | + case CCS_MAC_CHROOT: | |
583 | + case CCS_MAC_CHMOD: | |
584 | + case CCS_MAC_CHOWN: | |
585 | + case CCS_MAC_CHGRP: | |
586 | + case CCS_MAC_IOCTL: | |
587 | + case CCS_MAC_MKDIR: | |
588 | + case CCS_MAC_CREATE: | |
589 | + case CCS_MAC_MKFIFO: | |
590 | + case CCS_MAC_MKSOCK: | |
591 | + case CCS_MAC_MKBLOCK: | |
592 | + case CCS_MAC_MKCHAR: | |
593 | + case CCS_MAC_UMOUNT: | |
594 | + if (index == 0) | |
595 | + return "path"; | |
596 | + break; | |
597 | + case CCS_MAC_MODIFY_POLICY: | |
598 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
599 | + case CCS_MAC_USE_NETLINK_SOCKET: | |
600 | + case CCS_MAC_USE_PACKET_SOCKET: | |
601 | + case CCS_MAC_USE_REBOOT: | |
602 | + case CCS_MAC_USE_VHANGUP: | |
603 | + case CCS_MAC_SET_TIME: | |
604 | + case CCS_MAC_SET_PRIORITY: | |
605 | + case CCS_MAC_SET_HOSTNAME: | |
606 | + case CCS_MAC_USE_KERNEL_MODULE: | |
607 | + case CCS_MAC_USE_NEW_KERNEL: | |
608 | +#endif | |
609 | + break; | |
610 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
611 | + case CCS_MAC_INET_STREAM_BIND: | |
612 | + case CCS_MAC_INET_STREAM_LISTEN: | |
613 | + case CCS_MAC_INET_STREAM_CONNECT: | |
614 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
615 | + case CCS_MAC_INET_DGRAM_BIND: | |
616 | + case CCS_MAC_INET_DGRAM_SEND: | |
617 | + case CCS_MAC_INET_DGRAM_RECV: | |
618 | + case CCS_MAC_INET_RAW_BIND: | |
619 | + case CCS_MAC_INET_RAW_SEND: | |
620 | + case CCS_MAC_INET_RAW_RECV: | |
621 | + if (index == 0) | |
622 | + return "ip"; | |
623 | + break; | |
624 | + case CCS_MAC_UNIX_STREAM_BIND: | |
625 | + case CCS_MAC_UNIX_STREAM_LISTEN: | |
626 | + case CCS_MAC_UNIX_STREAM_CONNECT: | |
627 | + case CCS_MAC_UNIX_STREAM_ACCEPT: | |
628 | + case CCS_MAC_UNIX_DGRAM_BIND: | |
629 | + case CCS_MAC_UNIX_DGRAM_SEND: | |
630 | + case CCS_MAC_UNIX_DGRAM_RECV: | |
631 | + case CCS_MAC_UNIX_SEQPACKET_BIND: | |
632 | + case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
633 | + case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
634 | + case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
635 | + if (index == 0) | |
636 | + return "addr"; | |
637 | + break; | |
638 | +#endif | |
639 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
640 | + case CCS_MAC_PTRACE: | |
641 | + if (index == 0) | |
642 | + return "domain"; | |
643 | + break; | |
644 | +#endif | |
645 | + default: | |
646 | + break; | |
647 | + } | |
648 | + return "unknown"; /* This should not happen. */ | |
649 | +} | |
650 | + | |
651 | +/** | |
652 | + * ccs_get_narg - Get attribute name of CCS_NARG argument. | |
653 | + * | |
654 | + * @type: One of values in "enum ccs_mac_index". | |
655 | + * @index: Index to return. | |
656 | + * | |
657 | + * Returns attribute name. | |
658 | + */ | |
659 | +static const char *ccs_get_narg(const enum ccs_mac_index type, const u8 index) | |
660 | +{ | |
661 | + switch (type) { | |
662 | + case CCS_MAC_MOUNT: | |
663 | + case CCS_MAC_UMOUNT: | |
664 | + if (index == 0) | |
665 | + return "flags"; | |
666 | + break; | |
667 | + case CCS_MAC_CHMOD: | |
668 | + if (index == 0) | |
669 | + return "perm"; | |
670 | + break; | |
671 | + case CCS_MAC_CHOWN: | |
672 | + if (index == 0) | |
673 | + return "uid"; | |
674 | + break; | |
675 | + case CCS_MAC_CHGRP: | |
676 | + if (index == 0) | |
677 | + return "gid"; | |
678 | + break; | |
679 | + case CCS_MAC_IOCTL: | |
680 | + if (index == 0) | |
681 | + return "cmd"; | |
682 | + break; | |
683 | + case CCS_MAC_MKDIR: | |
684 | + case CCS_MAC_CREATE: | |
685 | + case CCS_MAC_MKFIFO: | |
686 | + case CCS_MAC_MKSOCK: | |
687 | + if (index == 0) | |
688 | + return "perm"; | |
689 | + break; | |
690 | + case CCS_MAC_MKBLOCK: | |
691 | + case CCS_MAC_MKCHAR: | |
692 | + if (index == 0) | |
693 | + return "perm"; | |
694 | + if (index == 1) | |
695 | + return "dev_major"; | |
696 | + if (index == 2) | |
697 | + return "dev_minor"; | |
698 | + break; | |
699 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
700 | + case CCS_MAC_INET_STREAM_BIND: | |
701 | + case CCS_MAC_INET_STREAM_LISTEN: | |
702 | + case CCS_MAC_INET_STREAM_CONNECT: | |
703 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
704 | + case CCS_MAC_INET_DGRAM_BIND: | |
705 | + case CCS_MAC_INET_DGRAM_SEND: | |
706 | + case CCS_MAC_INET_DGRAM_RECV: | |
707 | + if (index == 0) | |
708 | + return "port"; | |
709 | + break; | |
710 | + case CCS_MAC_INET_RAW_BIND: | |
711 | + case CCS_MAC_INET_RAW_SEND: | |
712 | + case CCS_MAC_INET_RAW_RECV: | |
713 | + if (index == 0) | |
714 | + return "proto"; | |
715 | + break; | |
716 | +#endif | |
717 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
718 | + case CCS_MAC_PTRACE: | |
719 | + if (index == 0) | |
720 | + return "cmd"; | |
721 | + break; | |
722 | +#endif | |
723 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
724 | + case CCS_MAC_SIGNAL: | |
725 | + if (index == 0) | |
726 | + return "cmd"; | |
727 | + break; | |
728 | +#endif | |
729 | + default: | |
730 | + break; | |
731 | + } | |
732 | + return "unknown"; /* This should not happen. */ | |
733 | +} | |
734 | + | |
735 | +/***** SECTION5: Variables definition section *****/ | |
736 | + | |
737 | +/* Lock for protecting policy. */ | |
738 | +DEFINE_MUTEX(ccs_policy_lock); | |
739 | + | |
740 | +/* Has /sbin/init started? */ | |
741 | +bool ccs_policy_loaded; | |
742 | + | |
743 | +/* List of "struct ccs_group". */ | |
744 | +struct list_head ccs_group_list[CCS_MAX_GROUP]; | |
745 | +/* Policy version. Currently only 20120401 is defined. */ | |
746 | +static unsigned int ccs_policy_version = 20120401; | |
747 | + | |
748 | +/* List of "struct ccs_condition". */ | |
749 | +LIST_HEAD(ccs_condition_list); | |
750 | + | |
751 | +/* Wait queue for kernel -> userspace notification. */ | |
752 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_query_wait); | |
753 | +/* Wait queue for userspace -> kernel notification. */ | |
754 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_answer_wait); | |
755 | + | |
756 | +/* The list for "struct ccs_query". */ | |
757 | +static LIST_HEAD(ccs_query_list); | |
758 | + | |
759 | +/* Lock for manipulating ccs_query_list. */ | |
760 | +static DEFINE_SPINLOCK(ccs_query_list_lock); | |
761 | + | |
762 | +/* Number of "struct file" referring /proc/caitsith/query interface. */ | |
763 | +static atomic_t ccs_query_observers = ATOMIC_INIT(0); | |
764 | + | |
765 | +/* Wait queue for /proc/caitsith/audit. */ | |
766 | +static DECLARE_WAIT_QUEUE_HEAD(ccs_log_wait); | |
767 | + | |
768 | +/* The list for "struct ccs_log". */ | |
769 | +static LIST_HEAD(ccs_log); | |
770 | + | |
771 | +/* Lock for "struct list_head ccs_log". */ | |
772 | +static DEFINE_SPINLOCK(ccs_log_lock); | |
773 | + | |
774 | +/* Length of "stuct list_head ccs_log". */ | |
775 | +static unsigned int ccs_log_count[CCS_MAX_MATCHING]; | |
776 | +/* Quota for audit logs. */ | |
777 | +static unsigned int ccs_log_quota[CCS_MAX_LOG_QUOTA][CCS_MAX_MATCHING]; | |
778 | + | |
779 | +/* Memoy currently used by policy/audit log/query. */ | |
780 | +unsigned int ccs_memory_used[CCS_MAX_MEMORY_STAT]; | |
781 | + | |
782 | +/* Memory quota for "policy"/"audit log"/"query". */ | |
783 | +static unsigned int ccs_memory_quota[CCS_MAX_MEMORY_STAT]; | |
784 | + | |
785 | +/* The list for "struct ccs_name". */ | |
786 | +struct list_head ccs_name_list[CCS_MAX_HASH]; | |
787 | + | |
788 | +/* Timestamp counter for last updated. */ | |
789 | +static unsigned int ccs_stat_updated[CCS_MAX_POLICY_STAT]; | |
790 | + | |
791 | +/* Counter for number of updates. */ | |
792 | +static unsigned int ccs_stat_modified[CCS_MAX_POLICY_STAT]; | |
793 | + | |
794 | +/* Operations for /proc/caitsith/self_domain interface. */ | |
795 | +static const struct file_operations ccs_self_operations = { | |
796 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
797 | + .write = ccs_write_self, | |
798 | +#endif | |
799 | + .read = ccs_read_self, | |
800 | +}; | |
801 | + | |
802 | +/* Operations for /proc/caitsith/ interface. */ | |
803 | +static const struct file_operations ccs_operations = { | |
804 | + .open = ccs_open, | |
805 | + .release = ccs_release, | |
806 | + .poll = ccs_poll, | |
807 | + .read = ccs_read, | |
808 | + .write = ccs_write, | |
809 | +}; | |
810 | + | |
811 | +/***** SECTION6: Dependent functions section *****/ | |
812 | + | |
813 | +/** | |
814 | + * list_for_each_cookie - iterate over a list with cookie. | |
815 | + * | |
816 | + * @pos: Pointer to "struct list_head". | |
817 | + * @head: Pointer to "struct list_head". | |
818 | + */ | |
819 | +#define list_for_each_cookie(pos, head) \ | |
820 | + for (pos = pos ? pos : srcu_dereference((head)->next, &ccs_ss); \ | |
821 | + pos != (head); pos = srcu_dereference(pos->next, &ccs_ss)) | |
822 | + | |
823 | +/** | |
824 | + * ccs_warn_oom - Print out of memory warning message. | |
825 | + * | |
826 | + * @function: Function's name. | |
827 | + * | |
828 | + * Returns nothing. | |
829 | + */ | |
830 | +void ccs_warn_oom(const char *function) | |
831 | +{ | |
832 | + /* Reduce error messages. */ | |
833 | + static pid_t ccs_last_pid; | |
834 | + const pid_t pid = current->pid; | |
835 | + if (ccs_last_pid != pid) { | |
836 | + printk(KERN_WARNING "ERROR: Out of memory at %s.\n", | |
837 | + function); | |
838 | + ccs_last_pid = pid; | |
839 | + } | |
840 | + if (!ccs_policy_loaded) | |
841 | + panic("MAC Initialization failed.\n"); | |
842 | +} | |
843 | + | |
844 | +/** | |
845 | + * ccs_memory_ok - Check memory quota. | |
846 | + * | |
847 | + * @ptr: Pointer to allocated memory. Maybe NULL. | |
848 | + * @size: Size in byte. Not used if @ptr is NULL. | |
849 | + * | |
850 | + * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. | |
851 | + * | |
852 | + * Caller holds ccs_policy_lock mutex. | |
853 | + */ | |
854 | +static bool ccs_memory_ok(const void *ptr, const unsigned int size) | |
855 | +{ | |
856 | + if (ptr) { | |
857 | + const size_t s = ccs_round2(size); | |
858 | + ccs_memory_used[CCS_MEMORY_POLICY] += s; | |
859 | + if (!ccs_memory_quota[CCS_MEMORY_POLICY] || | |
860 | + ccs_memory_used[CCS_MEMORY_POLICY] <= | |
861 | + ccs_memory_quota[CCS_MEMORY_POLICY]) | |
862 | + return true; | |
863 | + ccs_memory_used[CCS_MEMORY_POLICY] -= s; | |
864 | + } | |
865 | + ccs_warn_oom(__func__); | |
866 | + return false; | |
867 | +} | |
868 | + | |
869 | +/** | |
870 | + * ccs_get_name - Allocate memory for string data. | |
871 | + * | |
872 | + * @name: The string to store into the permernent memory. Maybe NULL. | |
873 | + * | |
874 | + * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | |
875 | + */ | |
876 | +static const struct ccs_path_info *ccs_get_name(const char *name) | |
877 | +{ | |
878 | + struct ccs_name *ptr; | |
879 | + unsigned int hash; | |
880 | + int len; | |
881 | + int allocated_len; | |
882 | + struct list_head *head; | |
883 | + | |
884 | + if (!name) | |
885 | + return NULL; | |
886 | + len = strlen(name) + 1; | |
887 | + hash = full_name_hash((const unsigned char *) name, len - 1); | |
888 | + head = &ccs_name_list[hash_long(hash, CCS_HASH_BITS)]; | |
889 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
890 | + return NULL; | |
891 | + list_for_each_entry(ptr, head, head.list) { | |
892 | + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || | |
893 | + atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | |
894 | + continue; | |
895 | + atomic_inc(&ptr->head.users); | |
896 | + goto out; | |
897 | + } | |
898 | + allocated_len = sizeof(*ptr) + len; | |
899 | + ptr = kzalloc(allocated_len, GFP_NOFS); | |
900 | + if (ccs_memory_ok(ptr, allocated_len)) { | |
901 | + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); | |
902 | + memmove((char *) ptr->entry.name, name, len); | |
903 | + atomic_set(&ptr->head.users, 1); | |
904 | + ccs_fill_path_info(&ptr->entry); | |
905 | + ptr->size = allocated_len; | |
906 | + list_add_tail(&ptr->head.list, head); | |
907 | + } else { | |
908 | + kfree(ptr); | |
909 | + ptr = NULL; | |
910 | + } | |
911 | +out: | |
912 | + mutex_unlock(&ccs_policy_lock); | |
913 | + return ptr ? &ptr->entry : NULL; | |
914 | +} | |
915 | + | |
916 | +/** | |
917 | + * ccs_read_token - Read a word from a line. | |
918 | + * | |
919 | + * @head: Pointer to "struct ccs_io_buffer". | |
920 | + * | |
921 | + * Returns a word on success, "" otherwise. | |
922 | + * | |
923 | + * To allow the caller to skip NULL check, this function returns "" rather than | |
924 | + * NULL if there is no more words to read. | |
925 | + */ | |
926 | +static char *ccs_read_token(struct ccs_io_buffer *head) | |
927 | +{ | |
928 | + char *pos = head->w.data; | |
929 | + char *del = strchr(pos, ' '); | |
930 | + if (del) | |
931 | + *del++ = '\0'; | |
932 | + else | |
933 | + del = pos + strlen(pos); | |
934 | + head->w.data = del; | |
935 | + return pos; | |
936 | +} | |
937 | + | |
938 | +/** | |
939 | + * ccs_correct_word - Check whether the given string follows the naming rules. | |
940 | + * | |
941 | + * @string: The string to check. | |
942 | + * | |
943 | + * Returns true if @string follows the naming rules, false otherwise. | |
944 | + */ | |
945 | +static bool ccs_correct_word(const char *string) | |
946 | +{ | |
947 | + const char *const start = string; | |
948 | + u8 in_repetition = 0; | |
949 | + if (!*string) | |
950 | + goto out; | |
951 | + while (*string) { | |
952 | + unsigned char c = *string++; | |
953 | + if (in_repetition && c == '/') | |
954 | + goto out; | |
955 | + if (c <= ' ' || c >= 127) | |
956 | + goto out; | |
957 | + if (c != '\\') | |
958 | + continue; | |
959 | + c = *string++; | |
960 | + if (c >= '0' && c <= '3') { | |
961 | + unsigned char d; | |
962 | + unsigned char e; | |
963 | + d = *string++; | |
964 | + if (d < '0' || d > '7') | |
965 | + goto out; | |
966 | + e = *string++; | |
967 | + if (e < '0' || e > '7') | |
968 | + goto out; | |
969 | + c = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
970 | + if (c <= ' ' || c >= 127 || c == '\\') | |
971 | + continue; | |
972 | + goto out; | |
973 | + } | |
974 | + switch (c) { | |
975 | + case '$': /* "\$" */ | |
976 | + case '+': /* "\+" */ | |
977 | + case '?': /* "\?" */ | |
978 | + case '*': /* "\*" */ | |
979 | + case '@': /* "\@" */ | |
980 | + case 'x': /* "\x" */ | |
981 | + case 'X': /* "\X" */ | |
982 | + case 'a': /* "\a" */ | |
983 | + case 'A': /* "\A" */ | |
984 | + case '-': /* "\-" */ | |
985 | + continue; | |
986 | + case '{': /* "/\{" */ | |
987 | + if (string - 3 < start || *(string - 3) != '/') | |
988 | + goto out; | |
989 | + in_repetition = 1; | |
990 | + continue; | |
991 | + case '}': /* "\}/" */ | |
992 | + if (in_repetition != 1 || *string++ != '/') | |
993 | + goto out; | |
994 | + in_repetition = 0; | |
995 | + continue; | |
996 | + case '(': /* "/\(" */ | |
997 | + if (string - 3 < start || *(string - 3) != '/') | |
998 | + goto out; | |
999 | + in_repetition = 2; | |
1000 | + continue; | |
1001 | + case ')': /* "\)/" */ | |
1002 | + if (in_repetition != 2 || *string++ != '/') | |
1003 | + goto out; | |
1004 | + in_repetition = 0; | |
1005 | + continue; | |
1006 | + } | |
1007 | + goto out; | |
1008 | + } | |
1009 | + if (in_repetition) | |
1010 | + goto out; | |
1011 | + return true; | |
1012 | +out: | |
1013 | + return false; | |
1014 | +} | |
1015 | + | |
1016 | +/** | |
1017 | + * ccs_commit_ok - Allocate memory and check memory quota. | |
1018 | + * | |
1019 | + * @data: Data to copy from. | |
1020 | + * @size: Size in byte. | |
1021 | + * | |
1022 | + * Returns pointer to allocated memory on success, NULL otherwise. | |
1023 | + * @data is zero-cleared on success. | |
1024 | + * | |
1025 | + * Caller holds ccs_policy_lock mutex. | |
1026 | + */ | |
1027 | +static void *ccs_commit_ok(void *data, const unsigned int size) | |
1028 | +{ | |
1029 | + void *ptr = kmalloc(size, GFP_NOFS); | |
1030 | + if (ccs_memory_ok(ptr, size)) { | |
1031 | + memmove(ptr, data, size); | |
1032 | + memset(data, 0, size); | |
1033 | + return ptr; | |
1034 | + } | |
1035 | + kfree(ptr); | |
1036 | + return NULL; | |
1037 | +} | |
1038 | + | |
1039 | +/** | |
1040 | + * ccs_get_group - Allocate memory for "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group". | |
1041 | + * | |
1042 | + * @head: Pointer to "struct ccs_io_buffer". | |
1043 | + * @idx: Index number. | |
1044 | + * | |
1045 | + * Returns pointer to "struct ccs_group" on success, NULL otherwise. | |
1046 | + */ | |
1047 | +static struct ccs_group *ccs_get_group(struct ccs_io_buffer *head, | |
1048 | + const enum ccs_group_id idx) | |
1049 | +{ | |
1050 | + struct ccs_group e = { }; | |
1051 | + struct ccs_group *group = NULL; | |
1052 | + struct list_head *list; | |
1053 | + const char *group_name = ccs_read_token(head); | |
1054 | + bool found = false; | |
1055 | + if (!ccs_correct_word(group_name) || idx >= CCS_MAX_GROUP) | |
1056 | + return NULL; | |
1057 | + e.group_name = ccs_get_name(group_name); | |
1058 | + if (!e.group_name) | |
1059 | + return NULL; | |
1060 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
1061 | + goto out; | |
1062 | + list = &ccs_group_list[idx]; | |
1063 | + list_for_each_entry(group, list, head.list) { | |
1064 | + if (e.group_name != group->group_name || | |
1065 | + atomic_read(&group->head.users) == CCS_GC_IN_PROGRESS) | |
1066 | + continue; | |
1067 | + atomic_inc(&group->head.users); | |
1068 | + found = true; | |
1069 | + break; | |
1070 | + } | |
1071 | + if (!found) { | |
1072 | + struct ccs_group *entry = ccs_commit_ok(&e, sizeof(e)); | |
1073 | + if (entry) { | |
1074 | + INIT_LIST_HEAD(&entry->member_list); | |
1075 | + atomic_set(&entry->head.users, 1); | |
1076 | + list_add_tail_rcu(&entry->head.list, list); | |
1077 | + group = entry; | |
1078 | + found = true; | |
1079 | + } | |
1080 | + } | |
1081 | + mutex_unlock(&ccs_policy_lock); | |
1082 | +out: | |
1083 | + ccs_put_name(e.group_name); | |
1084 | + return found ? group : NULL; | |
1085 | +} | |
1086 | + | |
1087 | +/** | |
1088 | + * ccs_parse_ulong - Parse an "unsigned long" value. | |
1089 | + * | |
1090 | + * @result: Pointer to "unsigned long". | |
1091 | + * @str: Pointer to string to parse. | |
1092 | + * | |
1093 | + * Returns one of values in "enum ccs_value_type". | |
1094 | + * | |
1095 | + * The @src is updated to point the first character after the value | |
1096 | + * on success. | |
1097 | + */ | |
1098 | +static enum ccs_value_type ccs_parse_ulong(unsigned long *result, char **str) | |
1099 | +{ | |
1100 | + const char *cp = *str; | |
1101 | + char *ep; | |
1102 | + int base = 10; | |
1103 | + if (*cp == '0') { | |
1104 | + char c = *(cp + 1); | |
1105 | + if (c == 'x' || c == 'X') { | |
1106 | + base = 16; | |
1107 | + cp += 2; | |
1108 | + } else if (c >= '0' && c <= '7') { | |
1109 | + base = 8; | |
1110 | + cp++; | |
1111 | + } | |
1112 | + } | |
1113 | + *result = simple_strtoul(cp, &ep, base); | |
1114 | + if (cp == ep) | |
1115 | + return CCS_VALUE_TYPE_INVALID; | |
1116 | + *str = ep; | |
1117 | + switch (base) { | |
1118 | + case 16: | |
1119 | + return CCS_VALUE_TYPE_HEXADECIMAL; | |
1120 | + case 8: | |
1121 | + return CCS_VALUE_TYPE_OCTAL; | |
1122 | + default: | |
1123 | + return CCS_VALUE_TYPE_DECIMAL; | |
1124 | + } | |
1125 | +} | |
1126 | + | |
1127 | +/** | |
1128 | + * ccs_get_dqword - ccs_get_name() for a quoted string. | |
1129 | + * | |
1130 | + * @start: String to parse. | |
1131 | + * | |
1132 | + * Returns pointer to "struct ccs_path_info" on success, NULL otherwise. | |
1133 | + */ | |
1134 | +static const struct ccs_path_info *ccs_get_dqword(char *start) | |
1135 | +{ | |
1136 | + char *cp = start + strlen(start) - 1; | |
1137 | + if (cp == start || *start++ != '"' || *cp != '"') | |
1138 | + return NULL; | |
1139 | + *cp = '\0'; | |
1140 | + if (*start && !ccs_correct_word(start)) | |
1141 | + return NULL; | |
1142 | + return ccs_get_name(start); | |
1143 | +} | |
1144 | + | |
1145 | +/** | |
1146 | + * ccs_same_condition - Check for duplicated "struct ccs_condition" entry. | |
1147 | + * | |
1148 | + * @a: Pointer to "struct ccs_condition". | |
1149 | + * @b: Pointer to "struct ccs_condition". | |
1150 | + * | |
1151 | + * Returns true if @a == @b, false otherwise. | |
1152 | + */ | |
1153 | +static inline bool ccs_same_condition(const struct ccs_condition *a, | |
1154 | + const struct ccs_condition *b) | |
1155 | +{ | |
1156 | + return a->size == b->size && | |
1157 | + !memcmp(a + 1, b + 1, a->size - sizeof(*a)); | |
1158 | +} | |
1159 | + | |
1160 | +/** | |
1161 | + * ccs_commit_condition - Commit "struct ccs_condition". | |
1162 | + * | |
1163 | + * @entry: Pointer to "struct ccs_condition". | |
1164 | + * | |
1165 | + * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | |
1166 | + * | |
1167 | + * This function merges duplicated entries. This function returns NULL if | |
1168 | + * @entry is not duplicated but memory quota for policy has exceeded. | |
1169 | + */ | |
1170 | +static struct ccs_condition *ccs_commit_condition(struct ccs_condition *entry) | |
1171 | +{ | |
1172 | + struct ccs_condition *ptr; | |
1173 | + bool found = false; | |
1174 | + if (mutex_lock_interruptible(&ccs_policy_lock)) { | |
1175 | + dprintk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__); | |
1176 | + ptr = NULL; | |
1177 | + found = true; | |
1178 | + goto out; | |
1179 | + } | |
1180 | + list_for_each_entry(ptr, &ccs_condition_list, head.list) { | |
1181 | + if (!ccs_same_condition(ptr, entry) || | |
1182 | + atomic_read(&ptr->head.users) == CCS_GC_IN_PROGRESS) | |
1183 | + continue; | |
1184 | + /* Same entry found. Share this entry. */ | |
1185 | + atomic_inc(&ptr->head.users); | |
1186 | + found = true; | |
1187 | + break; | |
1188 | + } | |
1189 | + if (!found) { | |
1190 | + if (ccs_memory_ok(entry, entry->size)) { | |
1191 | + atomic_set(&entry->head.users, 1); | |
1192 | + list_add(&entry->head.list, &ccs_condition_list); | |
1193 | + } else { | |
1194 | + found = true; | |
1195 | + ptr = NULL; | |
1196 | + } | |
1197 | + } | |
1198 | + mutex_unlock(&ccs_policy_lock); | |
1199 | +out: | |
1200 | + if (found) { | |
1201 | + ccs_del_condition(&entry->head.list); | |
1202 | + kfree(entry); | |
1203 | + entry = ptr; | |
1204 | + } | |
1205 | + return entry; | |
1206 | +} | |
1207 | + | |
1208 | +/** | |
1209 | + * ccs_correct_domain - Check whether the given domainname follows the naming rules. | |
1210 | + * | |
1211 | + * @domainname: The domainname to check. | |
1212 | + * | |
1213 | + * Returns true if @domainname follows the naming rules, false otherwise. | |
1214 | + */ | |
1215 | +static bool ccs_correct_domain(const unsigned char *domainname) | |
1216 | +{ | |
1217 | + if (!ccs_correct_word(domainname)) | |
1218 | + return false; | |
1219 | + while (*domainname) { | |
1220 | + if (*domainname++ != '\\') | |
1221 | + continue; | |
1222 | + if (*domainname < '0' || *domainname++ > '3') | |
1223 | + return false; | |
1224 | + } | |
1225 | + return true; | |
1226 | +} | |
1227 | + | |
1228 | +/** | |
1229 | + * ccs_normalize_line - Format string. | |
1230 | + * | |
1231 | + * @buffer: The line to normalize. | |
1232 | + * | |
1233 | + * Returns nothing. | |
1234 | + * | |
1235 | + * Leading and trailing whitespaces are removed. | |
1236 | + * Multiple whitespaces are packed into single space. | |
1237 | + */ | |
1238 | +static void ccs_normalize_line(unsigned char *buffer) | |
1239 | +{ | |
1240 | + unsigned char *sp = buffer; | |
1241 | + unsigned char *dp = buffer; | |
1242 | + bool first = true; | |
1243 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
1244 | + sp++; | |
1245 | + while (*sp) { | |
1246 | + if (!first) | |
1247 | + *dp++ = ' '; | |
1248 | + first = false; | |
1249 | + while (*sp > ' ' && *sp < 127) | |
1250 | + *dp++ = *sp++; | |
1251 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
1252 | + sp++; | |
1253 | + } | |
1254 | + *dp = '\0'; | |
1255 | +} | |
1256 | + | |
1257 | +/** | |
1258 | + * ccs_parse_values - Parse an numeric argument. | |
1259 | + * | |
1260 | + * @value: Values to parse. | |
1261 | + * @v: Pointer to "unsigned long". | |
1262 | + * | |
1263 | + * Returns "enum ccs_value_type" if @value is a single value, bitwise-OR-ed | |
1264 | + * value if @value is value range. | |
1265 | + */ | |
1266 | +static u8 ccs_parse_values(char *value, unsigned long v[2]) | |
1267 | +{ | |
1268 | + enum ccs_value_type radix1 = ccs_parse_ulong(&v[0], &value); | |
1269 | + enum ccs_value_type radix2; | |
1270 | + if (radix1 == CCS_VALUE_TYPE_INVALID) | |
1271 | + return CCS_VALUE_TYPE_INVALID; | |
1272 | + if (!*value) { | |
1273 | + v[1] = v[0]; | |
1274 | + return radix1; | |
1275 | + } | |
1276 | + if (*value++ != '-') | |
1277 | + return CCS_VALUE_TYPE_INVALID; | |
1278 | + radix2 = ccs_parse_ulong(&v[1], &value); | |
1279 | + if (radix2 == CCS_VALUE_TYPE_INVALID || *value || v[0] > v[1]) | |
1280 | + return CCS_VALUE_TYPE_INVALID; | |
1281 | + return radix1 | (radix2 << 2); | |
1282 | +} | |
1283 | + | |
1284 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1285 | + | |
1286 | +/** | |
1287 | + * ccs_parse_ipaddr - Parse an IP address. | |
1288 | + * | |
1289 | + * @address: Address to parse. | |
1290 | + * @ipv6: Pointer to "struct in6_addr". | |
1291 | + * | |
1292 | + * Returns one of values in "enum ccs_ipaddr_type". | |
1293 | + */ | |
1294 | +/* Index numbers for type of IP addresses. */ | |
1295 | +static enum ccs_ipaddr_type ccs_parse_ipaddr(char *address, | |
1296 | + struct in6_addr ipv6[2]) | |
1297 | +{ | |
1298 | + const char *end; | |
1299 | + if (!strchr(address, ':') && | |
1300 | + in4_pton(address, -1, ipv6[0].s6_addr, '-', &end) > 0) { | |
1301 | + if (!*end) { | |
1302 | + ipv6[0].s6_addr32[0] = ipv6[0].s6_addr32[0]; | |
1303 | + ipv6[1].s6_addr32[0] = ipv6[0].s6_addr32[0]; | |
1304 | + return CCS_ADDRESS_TYPE_IPV4; | |
1305 | + } | |
1306 | + if (*end++ != '-' || | |
1307 | + in4_pton(end, -1, ipv6[1].s6_addr, '\0', &end) <= 0 || | |
1308 | + *end || memcmp(&ipv6[0], &ipv6[1], 4) >= 0) | |
1309 | + return CCS_ADDRESS_TYPE_INVALID; | |
1310 | + return CCS_ADDRESS_TYPE_IPV4_RANGE; | |
1311 | + } | |
1312 | + if (in6_pton(address, -1, ipv6[0].s6_addr, '-', &end) > 0) { | |
1313 | + if (!*end) { | |
1314 | + ipv6[1] = ipv6[0]; | |
1315 | + return CCS_ADDRESS_TYPE_IPV6; | |
1316 | + } | |
1317 | + if (*end++ != '-' || | |
1318 | + in6_pton(end, -1, ipv6[1].s6_addr, '\0', &end) <= 0 || | |
1319 | + *end || memcmp(&ipv6[0], &ipv6[1], 16) >= 0) | |
1320 | + return CCS_ADDRESS_TYPE_INVALID; | |
1321 | + return CCS_ADDRESS_TYPE_IPV6_RANGE; | |
1322 | + } | |
1323 | + return CCS_ADDRESS_TYPE_INVALID; | |
1324 | +} | |
1325 | + | |
1326 | +#endif | |
1327 | + | |
1328 | +/** | |
1329 | + * ccs_parse_task_cond - Find index for variable's name. | |
1330 | + * | |
1331 | + * @word: Keyword to search. | |
1332 | + * | |
1333 | + * Returns one of "ccs_conditions_index" value. | |
1334 | + */ | |
1335 | +static enum ccs_conditions_index ccs_parse_task_cond(const char *word) | |
1336 | +{ | |
1337 | + if (!strncmp(word, "task.", 5)) { | |
1338 | + word += 5; | |
1339 | + if (!strcmp(word, "uid")) | |
1340 | + return CCS_SELF_UID; | |
1341 | + if (!strcmp(word, "euid")) | |
1342 | + return CCS_SELF_EUID; | |
1343 | + if (!strcmp(word, "suid")) | |
1344 | + return CCS_SELF_SUID; | |
1345 | + if (!strcmp(word, "fsuid")) | |
1346 | + return CCS_SELF_FSUID; | |
1347 | + if (!strcmp(word, "gid")) | |
1348 | + return CCS_SELF_GID; | |
1349 | + if (!strcmp(word, "egid")) | |
1350 | + return CCS_SELF_EGID; | |
1351 | + if (!strcmp(word, "sgid")) | |
1352 | + return CCS_SELF_SGID; | |
1353 | + if (!strcmp(word, "fsgid")) | |
1354 | + return CCS_SELF_FSGID; | |
1355 | + if (!strcmp(word, "pid")) | |
1356 | + return CCS_SELF_PID; | |
1357 | + if (!strcmp(word, "ppid")) | |
1358 | + return CCS_SELF_PPID; | |
1359 | + if (!strcmp(word, "type")) | |
1360 | + return CCS_TASK_TYPE; | |
1361 | + if (!strcmp(word, "domain")) | |
1362 | + return CCS_SELF_DOMAIN; | |
1363 | + if (!strcmp(word, "exe")) | |
1364 | + return CCS_SELF_EXE; | |
1365 | + } | |
1366 | + return CCS_MAX_CONDITION_KEYWORD; | |
1367 | +} | |
1368 | + | |
1369 | +/** | |
1370 | + * ccs_parse_syscall_arg - Find index for variable's name. | |
1371 | + * | |
1372 | + * @word: Keyword to search. | |
1373 | + * @type: One of values in "enum ccs_mac_index". | |
1374 | + * | |
1375 | + * Returns one of "ccs_conditions_index" value. | |
1376 | + */ | |
1377 | +static enum ccs_conditions_index ccs_parse_syscall_arg | |
1378 | +(const char *word, const enum ccs_mac_index type) | |
1379 | +{ | |
1380 | + switch (type) { | |
1381 | + case CCS_MAC_READ: | |
1382 | + case CCS_MAC_WRITE: | |
1383 | + case CCS_MAC_APPEND: | |
1384 | + case CCS_MAC_UNLINK: | |
1385 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1386 | + case CCS_MAC_GETATTR: | |
1387 | +#endif | |
1388 | + case CCS_MAC_RMDIR: | |
1389 | + case CCS_MAC_TRUNCATE: | |
1390 | + case CCS_MAC_CHROOT: | |
1391 | + case CCS_MAC_CHOWN: | |
1392 | + case CCS_MAC_CHGRP: | |
1393 | + case CCS_MAC_IOCTL: | |
1394 | + case CCS_MAC_EXECUTE: | |
1395 | + case CCS_MAC_SYMLINK: | |
1396 | + if (!strcmp(word, "path")) | |
1397 | + return CCS_COND_SARG0; | |
1398 | + if (type == CCS_MAC_CHOWN && !strcmp(word, "uid")) | |
1399 | + return CCS_COND_NARG0; | |
1400 | + if (type == CCS_MAC_CHGRP && !strcmp(word, "gid")) | |
1401 | + return CCS_COND_NARG0; | |
1402 | + if (type == CCS_MAC_IOCTL && !strcmp(word, "cmd")) | |
1403 | + return CCS_COND_NARG0; | |
1404 | + if (type == CCS_MAC_EXECUTE && !strcmp(word, "exec")) | |
1405 | + return CCS_COND_SARG1; | |
1406 | + if (type == CCS_MAC_SYMLINK && !strcmp(word, "target")) | |
1407 | + return CCS_COND_SARG1; | |
1408 | + break; | |
1409 | + case CCS_MAC_CHMOD: | |
1410 | + case CCS_MAC_MKDIR: | |
1411 | + case CCS_MAC_CREATE: | |
1412 | + case CCS_MAC_MKFIFO: | |
1413 | + case CCS_MAC_MKSOCK: | |
1414 | + case CCS_MAC_MKBLOCK: | |
1415 | + case CCS_MAC_MKCHAR: | |
1416 | + if (!strcmp(word, "path")) | |
1417 | + return CCS_COND_SARG0; | |
1418 | + if (!strcmp(word, "perm")) | |
1419 | + return CCS_COND_NARG0; | |
1420 | + if (type == CCS_MAC_MKBLOCK || type == CCS_MAC_MKCHAR) { | |
1421 | + if (!strcmp(word, "dev_major")) | |
1422 | + return CCS_COND_NARG1; | |
1423 | + if (!strcmp(word, "dev_minor")) | |
1424 | + return CCS_COND_NARG2; | |
1425 | + } | |
1426 | + break; | |
1427 | + case CCS_MAC_LINK: | |
1428 | + case CCS_MAC_RENAME: | |
1429 | + if (!strcmp(word, "old_path")) | |
1430 | + return CCS_COND_SARG0; | |
1431 | + if (!strcmp(word, "new_path")) | |
1432 | + return CCS_COND_SARG1; | |
1433 | + break; | |
1434 | + case CCS_MAC_MOUNT: | |
1435 | + if (!strcmp(word, "source")) | |
1436 | + return CCS_COND_SARG0; | |
1437 | + if (!strcmp(word, "target")) | |
1438 | + return CCS_COND_SARG1; | |
1439 | + if (!strcmp(word, "fstype")) | |
1440 | + return CCS_COND_SARG2; | |
1441 | + if (!strcmp(word, "data")) | |
1442 | + return CCS_COND_SARG3; | |
1443 | + if (!strcmp(word, "flags")) | |
1444 | + return CCS_COND_NARG0; | |
1445 | + break; | |
1446 | + case CCS_MAC_UMOUNT: | |
1447 | + if (!strcmp(word, "path")) | |
1448 | + return CCS_COND_SARG0; | |
1449 | + if (!strcmp(word, "flags")) | |
1450 | + return CCS_COND_NARG0; | |
1451 | + break; | |
1452 | + case CCS_MAC_PIVOT_ROOT: | |
1453 | + if (!strcmp(word, "new_root")) | |
1454 | + return CCS_COND_SARG0; | |
1455 | + if (!strcmp(word, "put_old")) | |
1456 | + return CCS_COND_SARG1; | |
1457 | + break; | |
1458 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1459 | + case CCS_MAC_INET_STREAM_BIND: | |
1460 | + case CCS_MAC_INET_STREAM_LISTEN: | |
1461 | + case CCS_MAC_INET_STREAM_CONNECT: | |
1462 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
1463 | + case CCS_MAC_INET_DGRAM_BIND: | |
1464 | + case CCS_MAC_INET_DGRAM_SEND: | |
1465 | + case CCS_MAC_INET_DGRAM_RECV: | |
1466 | + if (!strcmp(word, "ip")) | |
1467 | + return CCS_COND_IPARG; | |
1468 | + if (!strcmp(word, "port")) | |
1469 | + return CCS_COND_NARG0; | |
1470 | + break; | |
1471 | + case CCS_MAC_INET_RAW_BIND: | |
1472 | + case CCS_MAC_INET_RAW_SEND: | |
1473 | + case CCS_MAC_INET_RAW_RECV: | |
1474 | + if (!strcmp(word, "ip")) | |
1475 | + return CCS_COND_IPARG; | |
1476 | + if (!strcmp(word, "proto")) | |
1477 | + return CCS_COND_NARG0; | |
1478 | + break; | |
1479 | + case CCS_MAC_UNIX_STREAM_BIND: | |
1480 | + case CCS_MAC_UNIX_STREAM_LISTEN: | |
1481 | + case CCS_MAC_UNIX_STREAM_CONNECT: | |
1482 | + case CCS_MAC_UNIX_STREAM_ACCEPT: | |
1483 | + case CCS_MAC_UNIX_DGRAM_BIND: | |
1484 | + case CCS_MAC_UNIX_DGRAM_SEND: | |
1485 | + case CCS_MAC_UNIX_DGRAM_RECV: | |
1486 | + case CCS_MAC_UNIX_SEQPACKET_BIND: | |
1487 | + case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
1488 | + case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
1489 | + case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
1490 | + if (!strcmp(word, "addr")) | |
1491 | + return CCS_COND_SARG0; | |
1492 | + break; | |
1493 | +#endif | |
1494 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1495 | + case CCS_MAC_ENVIRON: | |
1496 | + if (!strcmp(word, "path")) | |
1497 | + return CCS_COND_SARG0; | |
1498 | + if (!strcmp(word, "exec")) | |
1499 | + return CCS_COND_SARG1; | |
1500 | + if (!strcmp(word, "name")) | |
1501 | + return CCS_COND_SARG2; | |
1502 | + if (!strcmp(word, "value")) | |
1503 | + return CCS_COND_SARG3; | |
1504 | + break; | |
1505 | +#endif | |
1506 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
1507 | + case CCS_MAC_PTRACE: | |
1508 | + if (!strcmp(word, "domain")) | |
1509 | + return CCS_COND_DOMAIN; | |
1510 | + if (!strcmp(word, "cmd")) | |
1511 | + return CCS_COND_NARG0; | |
1512 | + break; | |
1513 | +#endif | |
1514 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
1515 | + case CCS_MAC_SIGNAL: | |
1516 | + if (!strcmp(word, "sig")) | |
1517 | + return CCS_COND_NARG0; | |
1518 | + break; | |
1519 | +#endif | |
1520 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
1521 | + case CCS_MAC_MANUAL_DOMAIN_TRANSITION: | |
1522 | + if (!strcmp(word, "domain")) | |
1523 | + return CCS_COND_DOMAIN; | |
1524 | + break; | |
1525 | +#endif | |
1526 | + default: | |
1527 | + break; | |
1528 | + } | |
1529 | + return CCS_MAX_CONDITION_KEYWORD; | |
1530 | +} | |
1531 | + | |
1532 | +/** | |
1533 | + * ccs_parse_path_attributes - Find index for variable's name. | |
1534 | + * | |
1535 | + * @word: Keyword to search. | |
1536 | + * @type: One of values in "enum ccs_mac_index". | |
1537 | + * | |
1538 | + * Returns one of "ccs_conditions_index" value. | |
1539 | + */ | |
1540 | +static enum ccs_conditions_index ccs_parse_path_attribute | |
1541 | +(char *word, const enum ccs_mac_index type) | |
1542 | +{ | |
1543 | + u8 i; | |
1544 | + enum ccs_conditions_index start; | |
1545 | + switch (type) { | |
1546 | + case CCS_MAC_READ: | |
1547 | + case CCS_MAC_WRITE: | |
1548 | + case CCS_MAC_APPEND: | |
1549 | + case CCS_MAC_UNLINK: | |
1550 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1551 | + case CCS_MAC_GETATTR: | |
1552 | +#endif | |
1553 | + case CCS_MAC_RMDIR: | |
1554 | + case CCS_MAC_TRUNCATE: | |
1555 | + case CCS_MAC_CHROOT: | |
1556 | + case CCS_MAC_CHMOD: | |
1557 | + case CCS_MAC_CHOWN: | |
1558 | + case CCS_MAC_CHGRP: | |
1559 | + case CCS_MAC_IOCTL: | |
1560 | + case CCS_MAC_EXECUTE: | |
1561 | + case CCS_MAC_UMOUNT: | |
1562 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1563 | + case CCS_MAC_ENVIRON: | |
1564 | +#endif | |
1565 | + if (ccs_str_starts(&word, "path")) | |
1566 | + goto path1; | |
1567 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1568 | + if ((type == CCS_MAC_EXECUTE || type == CCS_MAC_ENVIRON) && | |
1569 | + ccs_str_starts(&word, "exec")) | |
1570 | + goto path2; | |
1571 | +#else | |
1572 | + if (type == CCS_MAC_EXECUTE && | |
1573 | + ccs_str_starts(&word, "exec")) | |
1574 | + goto path2; | |
1575 | +#endif | |
1576 | + break; | |
1577 | + case CCS_MAC_MKDIR: | |
1578 | + case CCS_MAC_CREATE: | |
1579 | + case CCS_MAC_MKFIFO: | |
1580 | + case CCS_MAC_MKSOCK: | |
1581 | + case CCS_MAC_MKBLOCK: | |
1582 | + case CCS_MAC_MKCHAR: | |
1583 | + case CCS_MAC_SYMLINK: | |
1584 | + if (ccs_str_starts(&word, "path")) | |
1585 | + goto path1_parent; | |
1586 | + break; | |
1587 | + case CCS_MAC_LINK: | |
1588 | + case CCS_MAC_RENAME: | |
1589 | + if (ccs_str_starts(&word, "old_path")) | |
1590 | + goto path1; | |
1591 | + if (ccs_str_starts(&word, "new_path")) | |
1592 | + goto path2_parent; | |
1593 | + break; | |
1594 | + case CCS_MAC_MOUNT: | |
1595 | + if (ccs_str_starts(&word, "source")) | |
1596 | + goto path1; | |
1597 | + if (ccs_str_starts(&word, "target")) | |
1598 | + goto path2; | |
1599 | + break; | |
1600 | + case CCS_MAC_PIVOT_ROOT: | |
1601 | + if (ccs_str_starts(&word, "new_root")) | |
1602 | + goto path1; | |
1603 | + if (ccs_str_starts(&word, "put_old")) | |
1604 | + goto path2; | |
1605 | + break; | |
1606 | + default: | |
1607 | + break; | |
1608 | + } | |
1609 | + goto out; | |
1610 | +path1_parent: | |
1611 | + if (strncmp(word, ".parent", 7)) | |
1612 | + goto out; | |
1613 | +path1: | |
1614 | + start = CCS_PATH_ATTRIBUTE_START; | |
1615 | + goto check; | |
1616 | +path2_parent: | |
1617 | + if (strncmp(word, ".parent", 7)) | |
1618 | + goto out; | |
1619 | +path2: | |
1620 | + start = CCS_PATH_ATTRIBUTE_START + 32; | |
1621 | +check: | |
1622 | + if (ccs_str_starts(&word, ".parent")) | |
1623 | + start += 16; | |
1624 | + if (*word++ == '.') | |
1625 | + for (i = 0; i < CCS_MAX_PATH_ATTRIBUTE; i++) | |
1626 | + if (!strcmp(word, ccs_path_attribute[i])) | |
1627 | + return start + i; | |
1628 | +out: | |
1629 | + return CCS_MAX_CONDITION_KEYWORD; | |
1630 | +} | |
1631 | + | |
1632 | +/** | |
1633 | + * ccs_find_pathtype - Find index for file's type. | |
1634 | + * | |
1635 | + * @word: Keyword to search. | |
1636 | + * | |
1637 | + * Returns one of "ccs_conditions_index" value. | |
1638 | + */ | |
1639 | +static enum ccs_conditions_index ccs_find_path_type(const char *word) | |
1640 | +{ | |
1641 | + if (!strcmp(word, "socket")) | |
1642 | + return CCS_OBJ_IS_SOCKET; | |
1643 | + if (!strcmp(word, "symlink")) | |
1644 | + return CCS_OBJ_IS_SYMLINK; | |
1645 | + if (!strcmp(word, "file")) | |
1646 | + return CCS_OBJ_IS_FILE; | |
1647 | + if (!strcmp(word, "block")) | |
1648 | + return CCS_OBJ_IS_BLOCK_DEV; | |
1649 | + if (!strcmp(word, "directory")) | |
1650 | + return CCS_OBJ_IS_DIRECTORY; | |
1651 | + if (!strcmp(word, "char")) | |
1652 | + return CCS_OBJ_IS_CHAR_DEV; | |
1653 | + if (!strcmp(word, "fifo")) | |
1654 | + return CCS_OBJ_IS_FIFO; | |
1655 | + return CCS_MAX_CONDITION_KEYWORD; | |
1656 | +} | |
1657 | + | |
1658 | +/** | |
1659 | + * ccs_find_path_perm - Find index for file's DAC attribute. | |
1660 | + * | |
1661 | + * @word: Keyword to search. | |
1662 | + * | |
1663 | + * Returns one of "ccs_conditions_index" value. | |
1664 | + */ | |
1665 | +static enum ccs_conditions_index ccs_find_path_perm(const char *word) | |
1666 | +{ | |
1667 | + if (!strcmp(word, "setuid")) | |
1668 | + return CCS_MODE_SETUID; | |
1669 | + if (!strcmp(word, "setgid")) | |
1670 | + return CCS_MODE_SETGID; | |
1671 | + if (!strcmp(word, "sticky")) | |
1672 | + return CCS_MODE_STICKY; | |
1673 | + if (!strcmp(word, "owner_read")) | |
1674 | + return CCS_MODE_OWNER_READ; | |
1675 | + if (!strcmp(word, "owner_write")) | |
1676 | + return CCS_MODE_OWNER_WRITE; | |
1677 | + if (!strcmp(word, "owner_execute")) | |
1678 | + return CCS_MODE_OWNER_EXECUTE; | |
1679 | + if (!strcmp(word, "group_read")) | |
1680 | + return CCS_MODE_GROUP_READ; | |
1681 | + if (!strcmp(word, "group_write")) | |
1682 | + return CCS_MODE_GROUP_WRITE; | |
1683 | + if (!strcmp(word, "group_execute")) | |
1684 | + return CCS_MODE_GROUP_EXECUTE; | |
1685 | + if (!strcmp(word, "others_read")) | |
1686 | + return CCS_MODE_OTHERS_READ; | |
1687 | + if (!strcmp(word, "others_write")) | |
1688 | + return CCS_MODE_OTHERS_WRITE; | |
1689 | + if (!strcmp(word, "others_execute")) | |
1690 | + return CCS_MODE_OTHERS_EXECUTE; | |
1691 | + return CCS_MAX_CONDITION_KEYWORD; | |
1692 | +} | |
1693 | + | |
1694 | +/** | |
1695 | + * ccs_parse_cond - Parse single condition. | |
1696 | + * | |
1697 | + * @tmp: Pointer to "struct ccs_cond_tmp". | |
1698 | + * @head: Pointer to "struct ccs_io_buffer". | |
1699 | + * | |
1700 | + * Returns true on success, false otherwise. | |
1701 | + */ | |
1702 | +static bool ccs_parse_cond(struct ccs_cond_tmp *tmp, | |
1703 | + struct ccs_io_buffer *head) | |
1704 | +{ | |
1705 | + enum ccs_group_id g; | |
1706 | + char *left = head->w.data; | |
1707 | + char *right; | |
1708 | + const enum ccs_mac_index type = head->w.acl_index; | |
1709 | + right = strchr(left, '='); | |
1710 | + if (!right || right == left) | |
1711 | + return false; | |
1712 | + *right++ = '\0'; | |
1713 | + tmp->is_not = (*(right - 2) == '!'); | |
1714 | + if (tmp->is_not) | |
1715 | + *(right - 2) = '\0'; | |
1716 | + if (!*left || !*right) | |
1717 | + return false; | |
1718 | + if (type == CCS_MAC_EXECUTE | |
1719 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
1720 | + || type == CCS_MAC_ENVIRON | |
1721 | +#endif | |
1722 | + ) { | |
1723 | + if (ccs_str_starts(&left, "argv[")) { | |
1724 | + tmp->left = CCS_ARGV_ENTRY; | |
1725 | + if (ccs_parse_ulong(&tmp->argv, &left) != | |
1726 | + CCS_VALUE_TYPE_DECIMAL || *left++ != ']' || *left) | |
1727 | + return false; | |
1728 | + } else if (ccs_str_starts(&left, "envp[")) { | |
1729 | + char *cp = left + strlen(left) - 1; | |
1730 | + tmp->left = CCS_ENVP_ENTRY; | |
1731 | + if (*cp != ']') | |
1732 | + return false; | |
1733 | + *cp = '\0'; | |
1734 | + tmp->envp = ccs_get_dqword(left); | |
1735 | + if (!tmp->envp) | |
1736 | + return false; | |
1737 | + } else if (!strcmp(left, "argc")) | |
1738 | + tmp->left = CCS_EXEC_ARGC; | |
1739 | + else if (!strcmp(left, "envc")) | |
1740 | + tmp->left = CCS_EXEC_ENVC; | |
1741 | + } | |
1742 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1743 | + tmp->left = ccs_parse_syscall_arg(left, type); | |
1744 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1745 | + tmp->left = ccs_parse_task_cond(left); | |
1746 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) | |
1747 | + tmp->left = ccs_parse_path_attribute(left, type); | |
1748 | + if (tmp->left == CCS_MAX_CONDITION_KEYWORD) { | |
1749 | + /* | |
1750 | + * CCS_HANDLER_PATH and CCS_TRANSIT_DOMAIN are not for | |
1751 | + * comparison. | |
1752 | + */ | |
1753 | + if (tmp->is_not) | |
1754 | + return false; | |
1755 | + if (!strcmp(left, "handler")) | |
1756 | + tmp->left = CCS_HANDLER_PATH; | |
1757 | + else if (!strcmp(left, "transition")) | |
1758 | + tmp->left = CCS_TRANSIT_DOMAIN; | |
1759 | + else | |
1760 | + return false; | |
1761 | + tmp->right = CCS_IMM_NAME_ENTRY; | |
1762 | + if (!strcmp(right, "NULL")) { | |
1763 | + tmp->path = &ccs_null_name; | |
1764 | + } else { | |
1765 | + tmp->path = ccs_get_dqword(right); | |
1766 | + if (!tmp->path || | |
1767 | + tmp->path->const_len != tmp->path->total_len) | |
1768 | + return false; | |
1769 | + } | |
1770 | + return true; | |
1771 | + } | |
1772 | + switch (tmp->left) { | |
1773 | + case CCS_COND_DOMAIN: | |
1774 | + case CCS_SELF_DOMAIN: | |
1775 | + case CCS_ARGV_ENTRY: | |
1776 | + case CCS_ENVP_ENTRY: | |
1777 | + case CCS_COND_SARG0: | |
1778 | + case CCS_COND_SARG1: | |
1779 | + case CCS_COND_SARG2: | |
1780 | + case CCS_COND_SARG3: | |
1781 | + case CCS_SELF_EXE: | |
1782 | + g = CCS_STRING_GROUP; | |
1783 | + break; | |
1784 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1785 | + case CCS_COND_IPARG: | |
1786 | + g = CCS_IP_GROUP; | |
1787 | + break; | |
1788 | +#endif | |
1789 | + case CCS_TASK_TYPE: | |
1790 | + tmp->right = CCS_TASK_EXECUTE_HANDLER; | |
1791 | + return !strcmp(right, "execute_handler"); | |
1792 | + case CCS_PATH_ATTRIBUTE_START + CCS_PATH_ATTRIBUTE_TYPE: | |
1793 | + case CCS_PATH_ATTRIBUTE_START + 16 + CCS_PATH_ATTRIBUTE_TYPE: | |
1794 | + case CCS_PATH_ATTRIBUTE_START + 32 + CCS_PATH_ATTRIBUTE_TYPE: | |
1795 | + case CCS_PATH_ATTRIBUTE_START + 48 + CCS_PATH_ATTRIBUTE_TYPE: | |
1796 | + tmp->right = ccs_find_path_type(right); | |
1797 | + return tmp->right != CCS_MAX_CONDITION_KEYWORD; | |
1798 | + case CCS_PATH_ATTRIBUTE_START + CCS_PATH_ATTRIBUTE_PERM: | |
1799 | + case CCS_PATH_ATTRIBUTE_START + 16 + CCS_PATH_ATTRIBUTE_PERM: | |
1800 | + case CCS_PATH_ATTRIBUTE_START + 32 + CCS_PATH_ATTRIBUTE_PERM: | |
1801 | + case CCS_PATH_ATTRIBUTE_START + 48 + CCS_PATH_ATTRIBUTE_PERM: | |
1802 | + tmp->right = ccs_find_path_perm(right); | |
1803 | + if (tmp->right != CCS_MAX_CONDITION_KEYWORD) | |
1804 | + return true; | |
1805 | + /* fall through */ | |
1806 | + default: | |
1807 | + g = CCS_NUMBER_GROUP; | |
1808 | + } | |
1809 | + if (*right == '@') { | |
1810 | + tmp->right = CCS_IMM_GROUP; | |
1811 | + head->w.data = ++right; | |
1812 | + tmp->group = ccs_get_group(head, g); | |
1813 | + return tmp->group != NULL; | |
1814 | + } | |
1815 | + if (*right == '"') { | |
1816 | + if (g != CCS_STRING_GROUP) | |
1817 | + return false; | |
1818 | + tmp->right = CCS_IMM_NAME_ENTRY; | |
1819 | + tmp->path = ccs_get_dqword(right); | |
1820 | + return tmp->path != NULL; | |
1821 | + } | |
1822 | + if (tmp->left == CCS_ENVP_ENTRY) { | |
1823 | + tmp->right = CCS_IMM_NAME_ENTRY; | |
1824 | + tmp->path = &ccs_null_name; | |
1825 | + return !strcmp(right, "NULL"); | |
1826 | + } | |
1827 | + if (g == CCS_NUMBER_GROUP) { | |
1828 | + tmp->right = ccs_parse_task_cond(right); | |
1829 | + if (tmp->right == CCS_SELF_DOMAIN || | |
1830 | + tmp->right == CCS_SELF_EXE) | |
1831 | + return false; | |
1832 | + if (tmp->right == CCS_MAX_CONDITION_KEYWORD) | |
1833 | + tmp->right = ccs_parse_path_attribute(right, type); | |
1834 | + if (tmp->right != CCS_MAX_CONDITION_KEYWORD) | |
1835 | + return true; | |
1836 | + tmp->radix = ccs_parse_values(right, tmp->value); | |
1837 | + if (tmp->radix == CCS_VALUE_TYPE_INVALID) | |
1838 | + return false; | |
1839 | + if (tmp->radix >> 2) | |
1840 | + tmp->right = CCS_IMM_NUMBER_ENTRY2; | |
1841 | + else | |
1842 | + tmp->right = CCS_IMM_NUMBER_ENTRY1; | |
1843 | + return true; | |
1844 | + } | |
1845 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1846 | + if (g == CCS_IP_GROUP) { | |
1847 | + switch (ccs_parse_ipaddr(right, tmp->ipv6)) { | |
1848 | + case CCS_ADDRESS_TYPE_IPV4: | |
1849 | + tmp->right = CCS_IMM_IPV4ADDR_ENTRY1; | |
1850 | + break; | |
1851 | + case CCS_ADDRESS_TYPE_IPV4_RANGE: | |
1852 | + tmp->right = CCS_IMM_IPV4ADDR_ENTRY2; | |
1853 | + break; | |
1854 | + case CCS_ADDRESS_TYPE_IPV6: | |
1855 | + tmp->right = CCS_IMM_IPV6ADDR_ENTRY1; | |
1856 | + break; | |
1857 | + case CCS_ADDRESS_TYPE_IPV6_RANGE: | |
1858 | + tmp->right = CCS_IMM_IPV6ADDR_ENTRY2; | |
1859 | + break; | |
1860 | + default: | |
1861 | + return false; | |
1862 | + } | |
1863 | + return true; | |
1864 | + } | |
1865 | +#endif | |
1866 | + return false; | |
1867 | +} | |
1868 | + | |
1869 | +/** | |
1870 | + * ccs_get_condition - Parse condition part. | |
1871 | + * | |
1872 | + * @head: Pointer to "struct ccs_io_buffer". | |
1873 | + * | |
1874 | + * Returns pointer to "struct ccs_condition" on success, NULL otherwise. | |
1875 | + */ | |
1876 | +struct ccs_condition *ccs_get_condition(struct ccs_io_buffer *head) | |
1877 | +{ | |
1878 | + struct ccs_condition *entry = kmalloc(PAGE_SIZE, GFP_NOFS); | |
1879 | + union ccs_condition_element *condp; | |
1880 | + struct ccs_cond_tmp tmp; | |
1881 | + const enum ccs_mac_index type = head->w.acl_index; | |
1882 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
1883 | + bool handler_path_done = head->w.is_deny || | |
1884 | + type != CCS_MAC_EXECUTE; | |
1885 | +#else | |
1886 | + bool handler_path_done = true; | |
1887 | +#endif | |
1888 | + bool transit_domain_done = head->w.is_deny || | |
1889 | + (type != CCS_MAC_EXECUTE | |
1890 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
1891 | + && type != CCS_MAC_AUTO_DOMAIN_TRANSITION | |
1892 | +#endif | |
1893 | + ); | |
1894 | + char *pos = head->w.data; | |
1895 | + if (!entry) | |
1896 | + return NULL; | |
1897 | + condp = (union ccs_condition_element *) (entry + 1); | |
1898 | + while (1) { | |
1899 | + memset(&tmp, 0, sizeof(tmp)); | |
1900 | + tmp.left = CCS_MAX_CONDITION_KEYWORD; | |
1901 | + tmp.right = CCS_MAX_CONDITION_KEYWORD; | |
1902 | + while (*pos == ' ') | |
1903 | + pos++; | |
1904 | + if (!*pos) | |
1905 | + break; | |
1906 | + if ((u8 *) condp >= ((u8 *) entry) + PAGE_SIZE | |
1907 | + - (sizeof(*condp) + sizeof(struct in6_addr) * 2)) | |
1908 | + goto out; | |
1909 | + { | |
1910 | + char *next = strchr(pos, ' '); | |
1911 | + if (next) | |
1912 | + *next++ = '\0'; | |
1913 | + else | |
1914 | + next = ""; | |
1915 | + head->w.data = pos; | |
1916 | + pos = next; | |
1917 | + } | |
1918 | + if (!ccs_parse_cond(&tmp, head)) | |
1919 | + goto out; | |
1920 | + if (tmp.left == CCS_HANDLER_PATH) { | |
1921 | + if (handler_path_done) | |
1922 | + goto out; | |
1923 | + handler_path_done = true; | |
1924 | + } | |
1925 | + if (tmp.left == CCS_TRANSIT_DOMAIN) { | |
1926 | + if (transit_domain_done) | |
1927 | + goto out; | |
1928 | + transit_domain_done = true; | |
1929 | + } | |
1930 | + condp->is_not = tmp.is_not; | |
1931 | + condp->left = tmp.left; | |
1932 | + condp->right = tmp.right; | |
1933 | + condp->radix = tmp.radix; | |
1934 | + condp++; | |
1935 | + if (tmp.left == CCS_ARGV_ENTRY) { | |
1936 | + condp->value = tmp.argv; | |
1937 | + condp++; | |
1938 | + } else if (tmp.left == CCS_ENVP_ENTRY) { | |
1939 | + condp->path = tmp.envp; | |
1940 | + condp++; | |
1941 | + } | |
1942 | + if (tmp.right == CCS_IMM_GROUP) { | |
1943 | + condp->group = tmp.group; | |
1944 | + condp++; | |
1945 | + } else if (tmp.right == CCS_IMM_NAME_ENTRY) { | |
1946 | + condp->path = tmp.path; | |
1947 | + condp++; | |
1948 | + } else if (tmp.right == CCS_IMM_NUMBER_ENTRY1 || | |
1949 | + tmp.right == CCS_IMM_NUMBER_ENTRY2) { | |
1950 | + condp->value = tmp.value[0]; | |
1951 | + condp++; | |
1952 | + if (tmp.right == CCS_IMM_NUMBER_ENTRY2) { | |
1953 | + condp->value = tmp.value[1]; | |
1954 | + condp++; | |
1955 | + } | |
1956 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1957 | + } else if (tmp.right == CCS_IMM_IPV4ADDR_ENTRY1 || | |
1958 | + tmp.right == CCS_IMM_IPV4ADDR_ENTRY2) { | |
1959 | + condp->ip = * (u32 *) &tmp.ipv6[0]; | |
1960 | + condp++; | |
1961 | + if (tmp.right == CCS_IMM_IPV4ADDR_ENTRY2) { | |
1962 | + condp->ip = * (u32 *) &tmp.ipv6[1]; | |
1963 | + condp++; | |
1964 | + } | |
1965 | + } else if (tmp.right == CCS_IMM_IPV6ADDR_ENTRY1 || | |
1966 | + tmp.right == CCS_IMM_IPV6ADDR_ENTRY2) { | |
1967 | + * (struct in6_addr *) condp = tmp.ipv6[0]; | |
1968 | + condp = (void *) (((u8 *) condp) + | |
1969 | + sizeof(struct in6_addr)); | |
1970 | + if (tmp.right == CCS_IMM_IPV6ADDR_ENTRY2) { | |
1971 | + * (struct in6_addr *) condp = tmp.ipv6[1]; | |
1972 | + condp = (void *) (((u8 *) condp) + | |
1973 | + sizeof(struct in6_addr)); | |
1974 | + } | |
1975 | +#endif | |
1976 | + } | |
1977 | + } | |
1978 | +#ifdef CONFIG_CCSECURITY_AUTO_DOMAIN_TRANSITION | |
1979 | + if (!transit_domain_done && type == CCS_MAC_AUTO_DOMAIN_TRANSITION) | |
1980 | + goto out; | |
1981 | +#endif | |
1982 | + entry->size = (void *) condp - (void *) entry; | |
1983 | + return ccs_commit_condition(entry); | |
1984 | +out: | |
1985 | + dprintk(KERN_WARNING "%u: type=%u env='%s' path='%s' group='%s'\n", | |
1986 | + __LINE__, type, tmp.envp ? tmp.envp->name : "", | |
1987 | + tmp.path ? tmp.path->name : "", | |
1988 | + tmp.group ? tmp.group->group_name->name : ""); | |
1989 | + ccs_put_name(tmp.envp); | |
1990 | + if (tmp.path != &ccs_null_name) | |
1991 | + ccs_put_name(tmp.path); | |
1992 | + ccs_put_group(tmp.group); | |
1993 | + entry->size = (void *) condp - (void *) entry; | |
1994 | + ccs_del_condition(&entry->head.list); | |
1995 | + kfree(entry); | |
1996 | + return NULL; | |
1997 | +} | |
1998 | + | |
1999 | +/** | |
2000 | + * ccs_yesno - Return "yes" or "no". | |
2001 | + * | |
2002 | + * @value: Bool value. | |
2003 | + * | |
2004 | + * Returns "yes" if @value is not 0, "no" otherwise. | |
2005 | + */ | |
2006 | +static const char *ccs_yesno(const unsigned int value) | |
2007 | +{ | |
2008 | + return value ? "yes" : "no"; | |
2009 | +} | |
2010 | + | |
2011 | +/** | |
2012 | + * ccs_flush - Flush queued string to userspace's buffer. | |
2013 | + * | |
2014 | + * @head: Pointer to "struct ccs_io_buffer". | |
2015 | + * | |
2016 | + * Returns true if all data was flushed, false otherwise. | |
2017 | + */ | |
2018 | +static bool ccs_flush(struct ccs_io_buffer *head) | |
2019 | +{ | |
2020 | + while (head->r.w_pos) { | |
2021 | + const char *w = head->r.w[0]; | |
2022 | + size_t len = strlen(w); | |
2023 | + if (len) { | |
2024 | + if (len > head->read_user_buf_avail) | |
2025 | + len = head->read_user_buf_avail; | |
2026 | + if (!len) | |
2027 | + return false; | |
2028 | + if (copy_to_user(head->read_user_buf, w, len)) | |
2029 | + return false; | |
2030 | + head->read_user_buf_avail -= len; | |
2031 | + head->read_user_buf += len; | |
2032 | + w += len; | |
2033 | + } | |
2034 | + head->r.w[0] = w; | |
2035 | + if (*w) | |
2036 | + return false; | |
2037 | + /* Add '\0' for audit logs and query. */ | |
2038 | + if (head->type == CCS_AUDIT || head->type == CCS_QUERY) { | |
2039 | + if (!head->read_user_buf_avail || | |
2040 | + copy_to_user(head->read_user_buf, "", 1)) | |
2041 | + return false; | |
2042 | + head->read_user_buf_avail--; | |
2043 | + head->read_user_buf++; | |
2044 | + } | |
2045 | + head->r.w_pos--; | |
2046 | + for (len = 0; len < head->r.w_pos; len++) | |
2047 | + head->r.w[len] = head->r.w[len + 1]; | |
2048 | + } | |
2049 | + head->r.avail = 0; | |
2050 | + return true; | |
2051 | +} | |
2052 | + | |
2053 | +/** | |
2054 | + * ccs_set_string - Queue string to "struct ccs_io_buffer" structure. | |
2055 | + * | |
2056 | + * @head: Pointer to "struct ccs_io_buffer". | |
2057 | + * @string: String to print. | |
2058 | + * | |
2059 | + * Returns nothing. | |
2060 | + * | |
2061 | + * Note that @string has to be kept valid until @head is kfree()d. | |
2062 | + * This means that char[] allocated on stack memory cannot be passed to | |
2063 | + * this function. Use ccs_io_printf() for char[] allocated on stack memory. | |
2064 | + */ | |
2065 | +static void ccs_set_string(struct ccs_io_buffer *head, const char *string) | |
2066 | +{ | |
2067 | + if (head->r.w_pos < CCS_MAX_IO_READ_QUEUE) { | |
2068 | + head->r.w[head->r.w_pos++] = string; | |
2069 | + ccs_flush(head); | |
2070 | + } else | |
2071 | + printk(KERN_WARNING "Too many words in a line.\n"); | |
2072 | +} | |
2073 | + | |
2074 | +/** | |
2075 | + * ccs_io_printf - printf() to "struct ccs_io_buffer" structure. | |
2076 | + * | |
2077 | + * @head: Pointer to "struct ccs_io_buffer". | |
2078 | + * @fmt: The printf()'s format string, followed by parameters. | |
2079 | + * | |
2080 | + * Returns nothing. | |
2081 | + */ | |
2082 | +static void ccs_io_printf(struct ccs_io_buffer *head, const char *fmt, ...) | |
2083 | +{ | |
2084 | + va_list args; | |
2085 | + size_t len; | |
2086 | + size_t pos = head->r.avail; | |
2087 | + int size = head->readbuf_size - pos; | |
2088 | + if (size <= 0) | |
2089 | + return; | |
2090 | + va_start(args, fmt); | |
2091 | + len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; | |
2092 | + va_end(args); | |
2093 | + if (pos + len >= head->readbuf_size) { | |
2094 | + printk(KERN_WARNING "Too many words in a line.\n"); | |
2095 | + return; | |
2096 | + } | |
2097 | + head->r.avail += len; | |
2098 | + ccs_set_string(head, head->read_buf + pos); | |
2099 | +} | |
2100 | + | |
2101 | +/** | |
2102 | + * ccs_set_space - Put a space to "struct ccs_io_buffer" structure. | |
2103 | + * | |
2104 | + * @head: Pointer to "struct ccs_io_buffer". | |
2105 | + * | |
2106 | + * Returns nothing. | |
2107 | + */ | |
2108 | +static void ccs_set_space(struct ccs_io_buffer *head) | |
2109 | +{ | |
2110 | + ccs_set_string(head, " "); | |
2111 | +} | |
2112 | + | |
2113 | +/** | |
2114 | + * ccs_set_lf - Put a line feed to "struct ccs_io_buffer" structure. | |
2115 | + * | |
2116 | + * @head: Pointer to "struct ccs_io_buffer". | |
2117 | + * | |
2118 | + * Returns true if all data was flushed, false otherwise. | |
2119 | + */ | |
2120 | +static bool ccs_set_lf(struct ccs_io_buffer *head) | |
2121 | +{ | |
2122 | + ccs_set_string(head, "\n"); | |
2123 | + return !head->r.w_pos; | |
2124 | +} | |
2125 | + | |
2126 | +/** | |
2127 | + * ccs_check_profile - Check policy is loaded. | |
2128 | + * | |
2129 | + * Returns nothing. | |
2130 | + */ | |
2131 | +static void ccs_check_profile(void) | |
2132 | +{ | |
2133 | + ccs_policy_loaded = true; | |
2134 | + printk(KERN_INFO "CaitSith: 0.1 2012/04/01\n"); | |
2135 | + if (ccs_policy_version == 20120401) { | |
2136 | + printk(KERN_INFO "CaitSith module activated.\n"); | |
2137 | + return; | |
2138 | + } | |
2139 | + printk(KERN_ERR "Policy version %u is not supported.\n", | |
2140 | + ccs_policy_version); | |
2141 | + printk(KERN_ERR "Userland tools for CaitSith must be installed and " | |
2142 | + "policy must be initialized.\n"); | |
2143 | + printk(KERN_ERR "Please see http://caitsith.sourceforge.jp/ " | |
2144 | + "for more information.\n"); | |
2145 | + panic("STOP!"); | |
2146 | +} | |
2147 | + | |
2148 | +/** | |
2149 | + * ccs_str_starts - Check whether the given string starts with the given keyword. | |
2150 | + * | |
2151 | + * @src: Pointer to pointer to the string. | |
2152 | + * @find: Pointer to the keyword. | |
2153 | + * | |
2154 | + * Returns true if @src starts with @find, false otherwise. | |
2155 | + * | |
2156 | + * The @src is updated to point the first character after the @find | |
2157 | + * if @src starts with @find. | |
2158 | + */ | |
2159 | +static bool ccs_str_starts(char **src, const char *find) | |
2160 | +{ | |
2161 | + const int len = strlen(find); | |
2162 | + char *tmp = *src; | |
2163 | + if (strncmp(tmp, find, len)) | |
2164 | + return false; | |
2165 | + tmp += len; | |
2166 | + *src = tmp; | |
2167 | + return true; | |
2168 | +} | |
2169 | + | |
2170 | +/** | |
2171 | + * ccs_find_domain - Find a domain by the given name. | |
2172 | + * | |
2173 | + * @domainname: The domainname to find. | |
2174 | + * | |
2175 | + * Returns pointer to "struct ccs_domain_info" if found, NULL otherwise. | |
2176 | + * | |
2177 | + * Caller holds ccs_read_lock(). | |
2178 | + */ | |
2179 | +static struct ccs_domain_info *ccs_find_domain(const char *domainname) | |
2180 | +{ | |
2181 | + struct ccs_domain_info *domain; | |
2182 | + struct ccs_path_info name; | |
2183 | + name.name = domainname; | |
2184 | + ccs_fill_path_info(&name); | |
2185 | + list_for_each_entry_srcu(domain, &ccs_domain_list, list, &ccs_ss) { | |
2186 | + if (!ccs_pathcmp(&name, domain->domainname)) | |
2187 | + return domain; | |
2188 | + } | |
2189 | + return NULL; | |
2190 | +} | |
2191 | + | |
2192 | +/** | |
2193 | + * ccs_select_acl - Parse select command. | |
2194 | + * | |
2195 | + * @head: Pointer to "struct ccs_io_buffer". | |
2196 | + * @data: String to parse. | |
2197 | + * | |
2198 | + * Returns true on success, false otherwise. | |
2199 | + * | |
2200 | + * Caller holds ccs_read_lock(). | |
2201 | + */ | |
2202 | +static bool ccs_select_acl(struct ccs_io_buffer *head, const char *data) | |
2203 | +{ | |
2204 | + unsigned int qid; | |
2205 | + struct ccs_acl_info *acl; | |
2206 | + if (sscanf(data, "Q=%u", &qid) != 1) | |
2207 | + return false; | |
2208 | + acl = ccs_find_acl_by_qid(qid); | |
2209 | + head->w.acl = acl; | |
2210 | + /* Accessing read_buf is safe because head->io_sem is held. */ | |
2211 | + if (!head->read_buf) | |
2212 | + return true; /* Do nothing if open(O_WRONLY). */ | |
2213 | + memset(&head->r, 0, sizeof(head->r)); | |
2214 | + head->r.print_this_acl_only = true; | |
2215 | + if (acl) | |
2216 | + head->r.acl = &acl->list; | |
2217 | + else | |
2218 | + head->r.eof = true; | |
2219 | + ccs_io_printf(head, "# Q=%u\n", qid); | |
2220 | + return true; | |
2221 | +} | |
2222 | + | |
2223 | +/** | |
2224 | + * ccs_update_acl - Update "struct ccs_acl_info" entry. | |
2225 | + * | |
2226 | + * @list: Pointer to "struct list_head". | |
2227 | + * @head: Pointer to "struct ccs_io_buffer". | |
2228 | + * @update: True to store matching entry, false otherwise. | |
2229 | + * | |
2230 | + * Returns 0 on success, negative value otherwise. | |
2231 | + * | |
2232 | + * Caller holds ccs_read_lock(). | |
2233 | + */ | |
2234 | +static int ccs_update_acl(struct list_head * const list, | |
2235 | + struct ccs_io_buffer *head, const bool update) | |
2236 | +{ | |
2237 | + struct ccs_acl_info *ptr; | |
2238 | + struct ccs_acl_info new_entry = { }; | |
2239 | + const bool is_delete = head->w.is_delete; | |
2240 | + int error = is_delete ? -ENOENT : -ENOMEM; | |
2241 | + new_entry.priority = head->w.priority; | |
2242 | + new_entry.is_deny = head->w.is_deny; | |
2243 | + if (head->w.data[0]) { | |
2244 | + new_entry.cond = ccs_get_condition(head); | |
2245 | + if (!new_entry.cond) | |
2246 | + return -EINVAL; | |
2247 | + } | |
2248 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
2249 | + goto out; | |
2250 | + list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | |
2251 | + if (ptr->priority > new_entry.priority) | |
2252 | + break; | |
2253 | + /* | |
2254 | + * We cannot reuse deleted "struct ccs_acl_info" entry because | |
2255 | + * somebody might be referencing children of this deleted entry | |
2256 | + * from srcu section. We cannot delete children of this deleted | |
2257 | + * entry until all children are no longer referenced. Thus, let | |
2258 | + * the garbage collector wait and delete rather than trying to | |
2259 | + * reuse this deleted entry. | |
2260 | + */ | |
2261 | + if (ptr->is_deleted || ptr->cond != new_entry.cond || | |
2262 | + ptr->priority != new_entry.priority || | |
2263 | + ptr->is_deny != new_entry.is_deny) | |
2264 | + continue; | |
2265 | + ptr->is_deleted = is_delete; | |
2266 | + if (!is_delete && update) | |
2267 | + head->w.acl = ptr; | |
2268 | + error = 0; | |
2269 | + break; | |
2270 | + } | |
2271 | + if (error && !is_delete) { | |
2272 | + struct ccs_acl_info *entry = | |
2273 | + ccs_commit_ok(&new_entry, sizeof(new_entry)); | |
2274 | + if (entry) { | |
2275 | + INIT_LIST_HEAD(&entry->acl_info_list); | |
2276 | + list_add_tail_rcu(&entry->list, &ptr->list); | |
2277 | + if (update) | |
2278 | + head->w.acl = entry; | |
2279 | + } | |
2280 | + } | |
2281 | + mutex_unlock(&ccs_policy_lock); | |
2282 | +out: | |
2283 | + ccs_put_condition(new_entry.cond); | |
2284 | + return error; | |
2285 | +} | |
2286 | + | |
2287 | +/** | |
2288 | + * ccs_parse_entry - Update ACL entry. | |
2289 | + * | |
2290 | + * @head: Pointer to "struct ccs_io_buffer". | |
2291 | + * | |
2292 | + * Returns 0 on success, negative value otherwise. | |
2293 | + * | |
2294 | + * Caller holds ccs_read_lock(). | |
2295 | + */ | |
2296 | +static int ccs_parse_entry(struct ccs_io_buffer *head) | |
2297 | +{ | |
2298 | + enum ccs_mac_index type; | |
2299 | + const char *operation = ccs_read_token(head); | |
2300 | + for (type = 0; type < CCS_MAX_MAC_INDEX; type++) { | |
2301 | + if (strcmp(operation, ccs_mac_keywords[type])) | |
2302 | + continue; | |
2303 | + head->w.acl_index = type; | |
2304 | + /* | |
2305 | + * This is_deny is for rejecting handler= and transition= | |
2306 | + * arguments in "acl" line. handler= and transition= arguments | |
2307 | + * are accepted for only "allow" line. | |
2308 | + */ | |
2309 | + head->w.is_deny = true; | |
2310 | + return ccs_update_acl(&ccs_acl_list[type], head, true); | |
2311 | + } | |
2312 | + return -EINVAL; | |
2313 | +} | |
2314 | + | |
2315 | +/** | |
2316 | + * ccs_print_number - Print number argument. | |
2317 | + * | |
2318 | + * @head: Pointer to "struct ccs_io_buffer". | |
2319 | + * @radix: One of values in "enum ccs_value_type". | |
2320 | + * @value: Value to print. | |
2321 | + * | |
2322 | + * Returns nothing. | |
2323 | + */ | |
2324 | +static void ccs_print_number(struct ccs_io_buffer *head, | |
2325 | + const enum ccs_value_type radix, | |
2326 | + const unsigned long value) | |
2327 | +{ | |
2328 | + switch (radix) { | |
2329 | + case CCS_VALUE_TYPE_HEXADECIMAL: | |
2330 | + ccs_io_printf(head, "0x%lX", value); | |
2331 | + break; | |
2332 | + case CCS_VALUE_TYPE_OCTAL: | |
2333 | + ccs_io_printf(head, "0%lo", value); | |
2334 | + break; | |
2335 | + default: | |
2336 | + ccs_io_printf(head, "%lu", value); | |
2337 | + } | |
2338 | +} | |
2339 | + | |
2340 | +/** | |
2341 | + * ccs_print_misc_attribute - Print misc attribute's name. | |
2342 | + * | |
2343 | + * @head: Pointer to "struct ccs_io_buffer". | |
2344 | + * @type: One of values in "enum ccs_mac_index". | |
2345 | + * @cond: One of values in "enum ccs_condition_index". | |
2346 | + * | |
2347 | + * Returns nothing. | |
2348 | + */ | |
2349 | +static void ccs_print_misc_attribute(struct ccs_io_buffer *head, | |
2350 | + const enum ccs_mac_index type, | |
2351 | + const enum ccs_conditions_index cond) | |
2352 | +{ | |
2353 | + if (cond >= CCS_PATH_ATTRIBUTE_START) { | |
2354 | + const u8 pos = cond - CCS_PATH_ATTRIBUTE_START; | |
2355 | + ccs_io_printf(head, "%s.%s%s", ccs_get_sarg(type, pos >= 32), | |
2356 | + pos % 32 >= 16 ? "parent." : "", | |
2357 | + ccs_path_attribute[pos % 16]); | |
2358 | + return; | |
2359 | + } | |
2360 | + if (cond == CCS_COND_DOMAIN) { | |
2361 | + ccs_set_string(head, "domain"); | |
2362 | + return; | |
2363 | + } | |
2364 | + switch (cond) { | |
2365 | + case CCS_SELF_UID: | |
2366 | + case CCS_SELF_EUID: | |
2367 | + case CCS_SELF_SUID: | |
2368 | + case CCS_SELF_FSUID: | |
2369 | + case CCS_SELF_GID: | |
2370 | + case CCS_SELF_EGID: | |
2371 | + case CCS_SELF_SGID: | |
2372 | + case CCS_SELF_FSGID: | |
2373 | + case CCS_SELF_PID: | |
2374 | + case CCS_SELF_PPID: | |
2375 | + case CCS_TASK_TYPE: | |
2376 | + case CCS_SELF_DOMAIN: | |
2377 | + case CCS_SELF_EXE: | |
2378 | + ccs_set_string(head, "task."); | |
2379 | + /* fall through */ | |
2380 | + default: | |
2381 | + if (cond < CCS_MAX_CONDITION_KEYWORD) | |
2382 | + ccs_set_string(head, ccs_condition_keyword[cond]); | |
2383 | + else | |
2384 | + ccs_io_printf(head, "unknown(%u)", cond); | |
2385 | + } | |
2386 | +} | |
2387 | + | |
2388 | +/** | |
2389 | + * ccs_print_condition_loop - Print condition part. | |
2390 | + * | |
2391 | + * @head: Pointer to "struct ccs_io_buffer". | |
2392 | + * @cond: Pointer to "struct ccs_condition". | |
2393 | + * | |
2394 | + * Returns true on success, false otherwise. | |
2395 | + */ | |
2396 | +static bool ccs_print_condition_loop(struct ccs_io_buffer *head, | |
2397 | + const struct ccs_condition *cond) | |
2398 | +{ | |
2399 | + const enum ccs_mac_index type = head->r.acl_index; | |
2400 | + const union ccs_condition_element *condp = head->r.cond; | |
2401 | + while ((void *) condp < (void *) ((u8 *) cond) + cond->size) { | |
2402 | + const bool is_not = condp->is_not; | |
2403 | + const enum ccs_conditions_index left = condp->left; | |
2404 | + const enum ccs_conditions_index right = condp->right; | |
2405 | + const u8 radix = condp->radix; | |
2406 | + if (!ccs_flush(head)) { | |
2407 | + head->r.cond = condp; | |
2408 | + return false; | |
2409 | + } | |
2410 | + condp++; | |
2411 | + ccs_set_space(head); | |
2412 | + switch (left) { | |
2413 | + case CCS_ARGV_ENTRY: | |
2414 | + ccs_io_printf(head, "argv[%lu]", condp->value); | |
2415 | + condp++; | |
2416 | + break; | |
2417 | + case CCS_ENVP_ENTRY: | |
2418 | + ccs_set_string(head, "envp[\""); | |
2419 | + ccs_set_string(head, condp->path->name); | |
2420 | + condp++; | |
2421 | + ccs_set_string(head, "\"]"); | |
2422 | + break; | |
2423 | + case CCS_COND_SARG0: | |
2424 | + ccs_set_string(head, ccs_get_sarg(type, 0)); | |
2425 | + break; | |
2426 | + case CCS_COND_SARG1: | |
2427 | + ccs_set_string(head, ccs_get_sarg(type, 1)); | |
2428 | + break; | |
2429 | + case CCS_COND_SARG2: | |
2430 | + ccs_set_string(head, ccs_get_sarg(type, 2)); | |
2431 | + break; | |
2432 | + case CCS_COND_SARG3: | |
2433 | + ccs_set_string(head, ccs_get_sarg(type, 3)); | |
2434 | + break; | |
2435 | + case CCS_COND_NARG0: | |
2436 | + ccs_set_string(head, ccs_get_narg(type, 0)); | |
2437 | + break; | |
2438 | + case CCS_COND_NARG1: | |
2439 | + ccs_set_string(head, ccs_get_narg(type, 1)); | |
2440 | + break; | |
2441 | + case CCS_COND_NARG2: | |
2442 | + ccs_set_string(head, ccs_get_narg(type, 2)); | |
2443 | + break; | |
2444 | + case CCS_COND_IPARG: | |
2445 | + ccs_set_string(head, "ip"); | |
2446 | + break; | |
2447 | + default: | |
2448 | + ccs_print_misc_attribute(head, type, left); | |
2449 | + } | |
2450 | + ccs_set_string(head, is_not ? "!=" : "="); | |
2451 | + switch (right) { | |
2452 | + case CCS_IMM_GROUP: | |
2453 | + ccs_set_string(head, "@"); | |
2454 | + ccs_set_string(head, condp->group->group_name->name); | |
2455 | + condp++; | |
2456 | + break; | |
2457 | + case CCS_IMM_NAME_ENTRY: | |
2458 | + if (condp->path != &ccs_null_name) { | |
2459 | + ccs_set_string(head, "\""); | |
2460 | + ccs_set_string(head, condp->path->name); | |
2461 | + ccs_set_string(head, "\""); | |
2462 | + } else { | |
2463 | + ccs_set_string(head, "NULL"); | |
2464 | + } | |
2465 | + condp++; | |
2466 | + break; | |
2467 | + case CCS_IMM_NUMBER_ENTRY1: | |
2468 | + case CCS_IMM_NUMBER_ENTRY2: | |
2469 | + ccs_print_number(head, radix & 3, condp->value); | |
2470 | + condp++; | |
2471 | + if (right == CCS_IMM_NUMBER_ENTRY1) | |
2472 | + break; | |
2473 | + ccs_set_string(head, "-"); | |
2474 | + ccs_print_number(head, (radix >> 2) & 3, condp->value); | |
2475 | + condp++; | |
2476 | + break; | |
2477 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
2478 | + case CCS_IMM_IPV4ADDR_ENTRY1: | |
2479 | + case CCS_IMM_IPV4ADDR_ENTRY2: | |
2480 | + ccs_print_ipv4(head, &condp->ip); | |
2481 | + condp++; | |
2482 | + if (right == CCS_IMM_IPV4ADDR_ENTRY1) | |
2483 | + break; | |
2484 | + ccs_set_string(head, "-"); | |
2485 | + ccs_print_ipv4(head, &condp->ip); | |
2486 | + condp++; | |
2487 | + break; | |
2488 | + case CCS_IMM_IPV6ADDR_ENTRY1: | |
2489 | + case CCS_IMM_IPV6ADDR_ENTRY2: | |
2490 | + ccs_print_ipv6(head, (const struct in6_addr *) condp); | |
2491 | + condp = (void *) | |
2492 | + ((u8 *) condp) + sizeof(struct in6_addr); | |
2493 | + if (right == CCS_IMM_IPV6ADDR_ENTRY1) | |
2494 | + break; | |
2495 | + ccs_set_string(head, "-"); | |
2496 | + ccs_print_ipv6(head, (const struct in6_addr *) condp); | |
2497 | + condp = (void *) | |
2498 | + ((u8 *) condp) + sizeof(struct in6_addr); | |
2499 | + break; | |
2500 | +#endif | |
2501 | + default: | |
2502 | + ccs_print_misc_attribute(head, type, right); | |
2503 | + } | |
2504 | + } | |
2505 | + head->r.cond = NULL; | |
2506 | + return true; | |
2507 | +} | |
2508 | + | |
2509 | +/** | |
2510 | + * ccs_print_condition - Print condition part. | |
2511 | + * | |
2512 | + * @head: Pointer to "struct ccs_io_buffer". | |
2513 | + * @cond: Pointer to "struct ccs_condition". | |
2514 | + * | |
2515 | + * Returns true on success, false otherwise. | |
2516 | + */ | |
2517 | +static bool ccs_print_condition(struct ccs_io_buffer *head, | |
2518 | + const struct ccs_condition *cond) | |
2519 | +{ | |
2520 | + switch (head->r.cond_step) { | |
2521 | + case 0: | |
2522 | + head->r.cond = (const union ccs_condition_element *) | |
2523 | + (cond + 1); | |
2524 | + head->r.cond_step++; | |
2525 | + /* fall through */ | |
2526 | + case 1: | |
2527 | + if (!ccs_print_condition_loop(head, cond)) | |
2528 | + return false; | |
2529 | + head->r.cond_step++; | |
2530 | + /* fall through */ | |
2531 | + case 2: | |
2532 | + head->r.cond = NULL; | |
2533 | + return true; | |
2534 | + } | |
2535 | + return false; | |
2536 | +} | |
2537 | + | |
2538 | +/** | |
2539 | + * ccs_read_acl - Print an ACL entry. | |
2540 | + * | |
2541 | + * @head: Pointer to "struct ccs_io_buffer". | |
2542 | + * @acl: Pointer to an ACL entry. | |
2543 | + * | |
2544 | + * Returns true on success, false otherwise. | |
2545 | + */ | |
2546 | +static bool ccs_read_acl(struct ccs_io_buffer *head, | |
2547 | + const struct ccs_acl_info *acl) | |
2548 | +{ | |
2549 | + const enum ccs_mac_index type = head->r.acl_index; | |
2550 | + if (head->r.cond) | |
2551 | + goto print_cond_part; | |
2552 | + if (acl->is_deleted) | |
2553 | + return true; | |
2554 | + if (!ccs_flush(head)) | |
2555 | + return false; | |
2556 | + ccs_io_printf(head, "%u ", acl->priority); | |
2557 | + ccs_set_string(head, "acl "); | |
2558 | + ccs_set_string(head, ccs_mac_keywords[type]); | |
2559 | + if (acl->cond) { | |
2560 | + head->r.cond_step = 0; | |
2561 | +print_cond_part: | |
2562 | + if (!ccs_print_condition(head, acl->cond)) | |
2563 | + return false; | |
2564 | + } | |
2565 | + ccs_set_lf(head); | |
2566 | + return true; | |
2567 | +} | |
2568 | + | |
2569 | +/** | |
2570 | + * ccs_write_pid - Specify PID to obtain domainname. | |
2571 | + * | |
2572 | + * @head: Pointer to "struct ccs_io_buffer". | |
2573 | + * | |
2574 | + * Returns 0. | |
2575 | + */ | |
2576 | +static int ccs_write_pid(struct ccs_io_buffer *head) | |
2577 | +{ | |
2578 | + head->r.eof = false; | |
2579 | + return 0; | |
2580 | +} | |
2581 | + | |
2582 | +/** | |
2583 | + * ccs_read_pid - Read information of a process. | |
2584 | + * | |
2585 | + * @head: Pointer to "struct ccs_io_buffer". | |
2586 | + * | |
2587 | + * Returns nothing. | |
2588 | + * | |
2589 | + * Reads the domainname which the specified PID is in or | |
2590 | + * process information of the specified PID on success. | |
2591 | + * | |
2592 | + * Caller holds ccs_read_lock(). | |
2593 | + */ | |
2594 | +static void ccs_read_pid(struct ccs_io_buffer *head) | |
2595 | +{ | |
2596 | + char *buf = head->write_buf; | |
2597 | + bool task_info = false; | |
2598 | + bool global_pid = false; | |
2599 | + unsigned int pid; | |
2600 | + struct task_struct *p; | |
2601 | + struct ccs_domain_info *domain = NULL; | |
2602 | + u32 ccs_flags = 0; | |
2603 | + /* Accessing write_buf is safe because head->io_sem is held. */ | |
2604 | + if (!buf) { | |
2605 | + head->r.eof = true; | |
2606 | + return; /* Do nothing if open(O_RDONLY). */ | |
2607 | + } | |
2608 | + if (head->r.w_pos || head->r.eof) | |
2609 | + return; | |
2610 | + head->r.eof = true; | |
2611 | + if (ccs_str_starts(&buf, "info ")) | |
2612 | + task_info = true; | |
2613 | + if (ccs_str_starts(&buf, "global-pid ")) | |
2614 | + global_pid = true; | |
2615 | + pid = (unsigned int) simple_strtoul(buf, NULL, 10); | |
2616 | + ccs_tasklist_lock(); | |
2617 | + if (global_pid) | |
2618 | + p = ccsecurity_exports.find_task_by_pid_ns(pid, &init_pid_ns); | |
2619 | + else | |
2620 | + p = ccsecurity_exports.find_task_by_vpid(pid); | |
2621 | + if (p) { | |
2622 | + domain = ccs_task_domain(p); | |
2623 | + ccs_flags = ccs_task_flags(p); | |
2624 | + } | |
2625 | + ccs_tasklist_unlock(); | |
2626 | + if (!domain) | |
2627 | + return; | |
2628 | + if (!task_info) { | |
2629 | + ccs_io_printf(head, "%u ", pid); | |
2630 | + ccs_set_string(head, domain->domainname->name); | |
2631 | + } else { | |
2632 | + ccs_io_printf(head, "%u manager=%s execute_handler=%s ", pid, | |
2633 | + ccs_yesno(ccs_flags & | |
2634 | + CCS_TASK_IS_MANAGER), | |
2635 | + ccs_yesno(ccs_flags & | |
2636 | + CCS_TASK_IS_EXECUTE_HANDLER)); | |
2637 | + } | |
2638 | +} | |
2639 | + | |
2640 | +/** | |
2641 | + * ccs_update_group - Update "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2642 | + * | |
2643 | + * @head: Pointer to "struct ccs_io_buffer". | |
2644 | + * @type: Type of this group. | |
2645 | + * | |
2646 | + * Returns 0 on success, negative value otherwise. | |
2647 | + */ | |
2648 | +static int ccs_update_group(struct ccs_io_buffer *head, | |
2649 | + const enum ccs_group_id type) | |
2650 | +{ | |
2651 | + u8 size; | |
2652 | + const bool is_delete = head->w.is_delete; | |
2653 | + int error = is_delete ? -ENOENT : -ENOMEM; | |
2654 | + struct ccs_group *group = ccs_get_group(head, type); | |
2655 | + char *word = ccs_read_token(head); | |
2656 | + union { | |
2657 | + struct ccs_acl_head head; | |
2658 | + struct ccs_string_group path; | |
2659 | + struct ccs_number_group number; | |
2660 | + struct ccs_ip_group address; | |
2661 | + } e = { }; | |
2662 | + if (!group) | |
2663 | + return -ENOMEM; | |
2664 | + if (!*word) { | |
2665 | + error = -EINVAL; | |
2666 | + goto out; | |
2667 | + } | |
2668 | + if (type == CCS_STRING_GROUP) { | |
2669 | + if (!ccs_correct_word(word)) { | |
2670 | + error = -EINVAL; | |
2671 | + goto out; | |
2672 | + } | |
2673 | + e.path.member_name = ccs_get_name(word); | |
2674 | + if (!e.path.member_name) { | |
2675 | + error = -ENOMEM; | |
2676 | + goto out; | |
2677 | + } | |
2678 | + size = sizeof(e.path); | |
2679 | + } else if (type == CCS_NUMBER_GROUP) { | |
2680 | + e.number.radix = ccs_parse_values(word, e.number.value); | |
2681 | + if (e.number.radix == CCS_VALUE_TYPE_INVALID) | |
2682 | + goto out; | |
2683 | + size = sizeof(e.number); | |
2684 | + } else { | |
2685 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
2686 | + switch (ccs_parse_ipaddr(word, e.address.ip)) { | |
2687 | + case CCS_ADDRESS_TYPE_IPV4: | |
2688 | + case CCS_ADDRESS_TYPE_IPV4_RANGE: | |
2689 | + e.address.is_ipv6 = false; | |
2690 | + break; | |
2691 | + case CCS_ADDRESS_TYPE_IPV6: | |
2692 | + case CCS_ADDRESS_TYPE_IPV6_RANGE: | |
2693 | + e.address.is_ipv6 = true; | |
2694 | + break; | |
2695 | + default: | |
2696 | + goto out; | |
2697 | + } | |
2698 | + size = sizeof(e.address); | |
2699 | +#else | |
2700 | + goto out; | |
2701 | +#endif | |
2702 | + } | |
2703 | + if (mutex_lock_interruptible(&ccs_policy_lock) == 0) { | |
2704 | + struct ccs_acl_head *entry; | |
2705 | + list_for_each_entry_srcu(entry, &group->member_list, | |
2706 | + list, &ccs_ss) { | |
2707 | + if (entry->is_deleted == CCS_GC_IN_PROGRESS || | |
2708 | + memcmp(entry + 1, &e.head + 1, | |
2709 | + size - sizeof(*entry))) | |
2710 | + continue; | |
2711 | + entry->is_deleted = is_delete; | |
2712 | + error = 0; | |
2713 | + break; | |
2714 | + } | |
2715 | + if (error && !is_delete) { | |
2716 | + entry = ccs_commit_ok(&e, size); | |
2717 | + if (entry) { | |
2718 | + list_add_tail_rcu(&entry->list, | |
2719 | + &group->member_list); | |
2720 | + error = 0; | |
2721 | + } | |
2722 | + } | |
2723 | + mutex_unlock(&ccs_policy_lock); | |
2724 | + } | |
2725 | + if (type == CCS_STRING_GROUP) | |
2726 | + ccs_put_name(e.path.member_name); | |
2727 | +out: | |
2728 | + ccs_put_group(group); | |
2729 | + return error; | |
2730 | +} | |
2731 | + | |
2732 | +/** | |
2733 | + * ccs_write_policy - Write policy. | |
2734 | + * | |
2735 | + * @head: Pointer to "struct ccs_io_buffer". | |
2736 | + * | |
2737 | + * Returns 0 on success, negative value otherwise. | |
2738 | + */ | |
2739 | +static int ccs_write_policy(struct ccs_io_buffer *head) | |
2740 | +{ | |
2741 | + enum ccs_group_id i; | |
2742 | + unsigned int priority; | |
2743 | + char *word = ccs_read_token(head); | |
2744 | + if (sscanf(word, "%u", &priority) == 1) | |
2745 | + word = ccs_read_token(head); | |
2746 | + else | |
2747 | + priority = 1000; | |
2748 | + if (priority >= 65536 || !*word) | |
2749 | + return -EINVAL; | |
2750 | + head->w.priority = priority; | |
2751 | + if (!head->w.acl) | |
2752 | + goto no_acl_selected; | |
2753 | + head->w.is_deny = !strcmp(word, "deny"); | |
2754 | + if (head->w.is_deny || !strcmp(word, "allow")) | |
2755 | + return ccs_update_acl(&head->w.acl->acl_info_list, head, | |
2756 | + false); | |
2757 | + if (!strcmp(word, "audit")) { | |
2758 | + head->w.acl->audit = simple_strtoul(head->w.data, NULL, 10); | |
2759 | + return 0; | |
2760 | + } | |
2761 | + head->w.acl = NULL; | |
2762 | +no_acl_selected: | |
2763 | + if (ccs_select_acl(head, word)) | |
2764 | + return 0; | |
2765 | + if (!strcmp(word, "acl")) | |
2766 | + return ccs_parse_entry(head); | |
2767 | + for (i = 0; i < CCS_MAX_GROUP; i++) | |
2768 | + if (!strcmp(word, ccs_group_name[i])) | |
2769 | + return ccs_update_group(head, i); | |
2770 | + if (sscanf(word, "POLICY_VERSION=%u", &ccs_policy_version) == 1) | |
2771 | + return 0; | |
2772 | + if (strcmp(word, "quota")) | |
2773 | + return -EINVAL; | |
2774 | + if (ccs_str_starts(&head->w.data, "memory ")) | |
2775 | + return ccs_write_memory_quota(head->w.data); | |
2776 | + return ccs_write_audit_quota(head->w.data); | |
2777 | +} | |
2778 | + | |
2779 | +/** | |
2780 | + * ccs_read_subgroup - Read "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2781 | + * | |
2782 | + * @head: Pointer to "struct ccs_io_buffer". | |
2783 | + * @group: Pointer to "struct ccs_group". | |
2784 | + * @idx: One of values in "enum ccs_group_id". | |
2785 | + * | |
2786 | + * Returns true on success, false otherwise. | |
2787 | + * | |
2788 | + * Caller holds ccs_read_lock(). | |
2789 | + */ | |
2790 | +static bool ccs_read_subgroup(struct ccs_io_buffer *head, | |
2791 | + struct ccs_group *group, | |
2792 | + const enum ccs_group_id idx) | |
2793 | +{ | |
2794 | + list_for_each_cookie(head->r.acl, &group->member_list) { | |
2795 | + struct ccs_acl_head *ptr = | |
2796 | + list_entry(head->r.acl, typeof(*ptr), list); | |
2797 | + if (ptr->is_deleted) | |
2798 | + continue; | |
2799 | + if (!ccs_flush(head)) | |
2800 | + return false; | |
2801 | + ccs_set_string(head, ccs_group_name[idx]); | |
2802 | + ccs_set_space(head); | |
2803 | + ccs_set_string(head, group->group_name->name); | |
2804 | + ccs_set_space(head); | |
2805 | + if (idx == CCS_STRING_GROUP) { | |
2806 | + ccs_set_string(head, container_of | |
2807 | + (ptr, struct ccs_string_group, | |
2808 | + head)->member_name->name); | |
2809 | + } else if (idx == CCS_NUMBER_GROUP) { | |
2810 | + struct ccs_number_group *e = | |
2811 | + container_of(ptr, typeof(*e), head); | |
2812 | + ccs_print_number(head, e->radix & 3, e->value[0]); | |
2813 | + if (e->radix >> 2) { | |
2814 | + ccs_set_string(head, "-"); | |
2815 | + ccs_print_number(head, (e->radix >> 2) & 3, | |
2816 | + e->value[1]); | |
2817 | + } | |
2818 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
2819 | + } else if (idx == CCS_IP_GROUP) { | |
2820 | + ccs_print_ip(head, container_of | |
2821 | + (ptr, struct ccs_ip_group, head)); | |
2822 | +#endif | |
2823 | + } | |
2824 | + ccs_set_lf(head); | |
2825 | + } | |
2826 | + head->r.acl = NULL; | |
2827 | + return true; | |
2828 | +} | |
2829 | + | |
2830 | +/** | |
2831 | + * ccs_read_group - Read "struct ccs_string_group"/"struct ccs_number_group"/"struct ccs_ip_group" list. | |
2832 | + * | |
2833 | + * @head: Pointer to "struct ccs_io_buffer". | |
2834 | + * | |
2835 | + * Returns true on success, false otherwise. | |
2836 | + * | |
2837 | + * Caller holds ccs_read_lock(). | |
2838 | + */ | |
2839 | +static bool ccs_read_group(struct ccs_io_buffer *head) | |
2840 | +{ | |
2841 | + while (head->r.step < CCS_MAX_GROUP) { | |
2842 | + const enum ccs_group_id idx = head->r.step; | |
2843 | + struct list_head *list = &ccs_group_list[idx]; | |
2844 | + list_for_each_cookie(head->r.group, list) { | |
2845 | + struct ccs_group *group = | |
2846 | + list_entry(head->r.group, typeof(*group), | |
2847 | + head.list); | |
2848 | + if (!ccs_read_subgroup(head, group, idx)) | |
2849 | + return false; | |
2850 | + } | |
2851 | + head->r.group = NULL; | |
2852 | + head->r.step++; | |
2853 | + } | |
2854 | + head->r.step = 0; | |
2855 | + return true; | |
2856 | +} | |
2857 | + | |
2858 | +/** | |
2859 | + * ccs_supervisor - Ask for the supervisor's decision. | |
2860 | + * | |
2861 | + * @r: Pointer to "struct ccs_request_info". | |
2862 | + * | |
2863 | + * Returns 0 if the supervisor decided to permit the access request, | |
2864 | + * CCS_RETRY_REQUEST if the supervisor decided to retry the access request, | |
2865 | + * -EPERM otherwise. | |
2866 | + */ | |
2867 | +static int ccs_supervisor(struct ccs_request_info *r) | |
2868 | +{ | |
2869 | + int error = -EPERM; | |
2870 | + int len; | |
2871 | + static unsigned int ccs_serial; | |
2872 | + struct ccs_query entry = { }; | |
2873 | + bool quota_exceeded = false; | |
2874 | + if (!r->matched_acl) | |
2875 | + return -EPERM; | |
2876 | + /* Get message. */ | |
2877 | + entry.query = ccs_init_log(r); | |
2878 | + if (!entry.query) | |
2879 | + return -EPERM; | |
2880 | + entry.query_len = strlen(entry.query) + 1; | |
2881 | + len = ccs_round2(entry.query_len); | |
2882 | + entry.acl = r->matched_acl; | |
2883 | + spin_lock(&ccs_query_list_lock); | |
2884 | + if (ccs_memory_quota[CCS_MEMORY_QUERY] && | |
2885 | + ccs_memory_used[CCS_MEMORY_QUERY] + len | |
2886 | + >= ccs_memory_quota[CCS_MEMORY_QUERY]) { | |
2887 | + quota_exceeded = true; | |
2888 | + } else { | |
2889 | + entry.serial = ccs_serial++; | |
2890 | + entry.retry = r->retry; | |
2891 | + ccs_memory_used[CCS_MEMORY_QUERY] += len; | |
2892 | + list_add_tail(&entry.list, &ccs_query_list); | |
2893 | + } | |
2894 | + spin_unlock(&ccs_query_list_lock); | |
2895 | + if (quota_exceeded) | |
2896 | + goto out; | |
2897 | + /* Give 10 seconds for supervisor's opinion. */ | |
2898 | + while (entry.timer < 10) { | |
2899 | + wake_up_all(&ccs_query_wait); | |
2900 | + if (wait_event_interruptible_timeout | |
2901 | + (ccs_answer_wait, entry.answer || | |
2902 | + !atomic_read(&ccs_query_observers), HZ)) | |
2903 | + break; | |
2904 | + else | |
2905 | + entry.timer++; | |
2906 | + } | |
2907 | + spin_lock(&ccs_query_list_lock); | |
2908 | + list_del(&entry.list); | |
2909 | + ccs_memory_used[CCS_MEMORY_QUERY] -= len; | |
2910 | + spin_unlock(&ccs_query_list_lock); | |
2911 | + switch (entry.answer) { | |
2912 | + case 3: /* Asked to retry by administrator. */ | |
2913 | + error = CCS_RETRY_REQUEST; | |
2914 | + r->retry++; | |
2915 | + break; | |
2916 | + case 1: | |
2917 | + /* Granted by administrator. */ | |
2918 | + error = 0; | |
2919 | + break; | |
2920 | + default: | |
2921 | + /* Timed out or rejected by administrator. */ | |
2922 | + break; | |
2923 | + } | |
2924 | +out: | |
2925 | + kfree(entry.query); | |
2926 | + return error; | |
2927 | +} | |
2928 | + | |
2929 | +/** | |
2930 | + * ccs_audit_log - Audit permission check log. | |
2931 | + * | |
2932 | + * @r: Pointer to "struct ccs_request_info". | |
2933 | + * | |
2934 | + * Returns 0 to grant the request, CCS_RETRY_REQUEST to retry the permission | |
2935 | + * check, -EPERM otherwise. | |
2936 | + */ | |
2937 | +int ccs_audit_log(struct ccs_request_info *r) | |
2938 | +{ | |
2939 | + /* Do not reject if not yet activated. */ | |
2940 | + if (!ccs_policy_loaded) | |
2941 | + return 0; | |
2942 | + /* Write /proc/caitsith/audit unless quota exceeded. */ | |
2943 | + if (ccs_log_count[r->result] < ccs_log_quota[r->audit][r->result]) | |
2944 | + ccs_write_log(r); | |
2945 | + /* Nothing more to do unless denied. */ | |
2946 | + if (r->result != CCS_MATCHING_DENIED) | |
2947 | + return 0; | |
2948 | + /* Update policy violation counter if denied. */ | |
2949 | + ccs_update_stat(CCS_STAT_REQUEST_DENIED); | |
2950 | + /* Nothing more to do unless ccs-queryd is running. */ | |
2951 | + if (!atomic_read(&ccs_query_observers)) | |
2952 | + return -EPERM; | |
2953 | + /* Ask the ccs-queryd for decision. */ | |
2954 | + return ccs_supervisor(r); | |
2955 | +} | |
2956 | + | |
2957 | +/** | |
2958 | + * ccs_find_acl_by_qid - Get ACL by query id. | |
2959 | + * | |
2960 | + * @serial: Query ID assigned by ccs_supervisor(). | |
2961 | + * | |
2962 | + * Returns pointer to "struct ccs_acl_info" if found, NULL otherwise. | |
2963 | + */ | |
2964 | +static struct ccs_acl_info *ccs_find_acl_by_qid(unsigned int serial) | |
2965 | +{ | |
2966 | + struct ccs_query *ptr; | |
2967 | + struct ccs_acl_info *acl = NULL; | |
2968 | + spin_lock(&ccs_query_list_lock); | |
2969 | + list_for_each_entry(ptr, &ccs_query_list, list) { | |
2970 | + if (ptr->serial != serial) | |
2971 | + continue; | |
2972 | + acl = ptr->acl; | |
2973 | + break; | |
2974 | + } | |
2975 | + spin_unlock(&ccs_query_list_lock); | |
2976 | + return acl; | |
2977 | +} | |
2978 | + | |
2979 | +/** | |
2980 | + * ccs_read_query - Read access requests which violated policy in enforcing mode. | |
2981 | + * | |
2982 | + * @head: Pointer to "struct ccs_io_buffer". | |
2983 | + * | |
2984 | + * Returns nothing. | |
2985 | + */ | |
2986 | +static void ccs_read_query(struct ccs_io_buffer *head) | |
2987 | +{ | |
2988 | + struct list_head *tmp; | |
2989 | + unsigned int pos = 0; | |
2990 | + size_t len = 0; | |
2991 | + char *buf; | |
2992 | + if (head->r.w_pos) | |
2993 | + return; | |
2994 | + kfree(head->read_buf); | |
2995 | + head->read_buf = NULL; | |
2996 | + spin_lock(&ccs_query_list_lock); | |
2997 | + list_for_each(tmp, &ccs_query_list) { | |
2998 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
2999 | + if (pos++ != head->r.query_index) | |
3000 | + continue; | |
3001 | + len = ptr->query_len; | |
3002 | + break; | |
3003 | + } | |
3004 | + spin_unlock(&ccs_query_list_lock); | |
3005 | + if (!len) { | |
3006 | + head->r.query_index = 0; | |
3007 | + return; | |
3008 | + } | |
3009 | + buf = kzalloc(len + 32, GFP_NOFS); | |
3010 | + if (!buf) | |
3011 | + return; | |
3012 | + pos = 0; | |
3013 | + spin_lock(&ccs_query_list_lock); | |
3014 | + list_for_each(tmp, &ccs_query_list) { | |
3015 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3016 | + if (pos++ != head->r.query_index) | |
3017 | + continue; | |
3018 | + /* | |
3019 | + * Some query can be skipped because ccs_query_list | |
3020 | + * can change, but I don't care. | |
3021 | + */ | |
3022 | + if (len == ptr->query_len) | |
3023 | + snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, | |
3024 | + ptr->retry, ptr->query); | |
3025 | + break; | |
3026 | + } | |
3027 | + spin_unlock(&ccs_query_list_lock); | |
3028 | + if (buf[0]) { | |
3029 | + head->read_buf = buf; | |
3030 | + head->r.w[head->r.w_pos++] = buf; | |
3031 | + head->r.query_index++; | |
3032 | + } else { | |
3033 | + kfree(buf); | |
3034 | + } | |
3035 | +} | |
3036 | + | |
3037 | +/** | |
3038 | + * ccs_write_answer - Write the supervisor's decision. | |
3039 | + * | |
3040 | + * @head: Pointer to "struct ccs_io_buffer". | |
3041 | + * | |
3042 | + * Returns 0 on success, -EINVAL otherwise. | |
3043 | + */ | |
3044 | +static int ccs_write_answer(struct ccs_io_buffer *head) | |
3045 | +{ | |
3046 | + char *data = head->write_buf; | |
3047 | + struct list_head *tmp; | |
3048 | + unsigned int serial; | |
3049 | + unsigned int answer; | |
3050 | + spin_lock(&ccs_query_list_lock); | |
3051 | + list_for_each(tmp, &ccs_query_list) { | |
3052 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3053 | + ptr->timer = 0; | |
3054 | + } | |
3055 | + spin_unlock(&ccs_query_list_lock); | |
3056 | + if (sscanf(data, "A%u=%u", &serial, &answer) != 2) | |
3057 | + return -EINVAL; | |
3058 | + spin_lock(&ccs_query_list_lock); | |
3059 | + list_for_each(tmp, &ccs_query_list) { | |
3060 | + struct ccs_query *ptr = list_entry(tmp, typeof(*ptr), list); | |
3061 | + if (ptr->serial != serial) | |
3062 | + continue; | |
3063 | + ptr->answer = (u8) answer; | |
3064 | + /* Remove from ccs_query_list. */ | |
3065 | + if (ptr->answer) { | |
3066 | + list_del(&ptr->list); | |
3067 | + INIT_LIST_HEAD(&ptr->list); | |
3068 | + } | |
3069 | + break; | |
3070 | + } | |
3071 | + spin_unlock(&ccs_query_list_lock); | |
3072 | + wake_up_all(&ccs_answer_wait); | |
3073 | + return 0; | |
3074 | +} | |
3075 | + | |
3076 | +/** | |
3077 | + * ccs_read_version - Get version. | |
3078 | + * | |
3079 | + * @head: Pointer to "struct ccs_io_buffer". | |
3080 | + * | |
3081 | + * Returns nothing. | |
3082 | + */ | |
3083 | +static void ccs_read_version(struct ccs_io_buffer *head) | |
3084 | +{ | |
3085 | + if (head->r.eof) | |
3086 | + return; | |
3087 | + ccs_set_string(head, "1.8.3"); | |
3088 | + head->r.eof = true; | |
3089 | +} | |
3090 | + | |
3091 | +/** | |
3092 | + * ccs_update_stat - Update statistic counters. | |
3093 | + * | |
3094 | + * @index: Index for policy type. | |
3095 | + * | |
3096 | + * Returns nothing. | |
3097 | + */ | |
3098 | +static void ccs_update_stat(const u8 index) | |
3099 | +{ | |
3100 | + struct timeval tv; | |
3101 | + do_gettimeofday(&tv); | |
3102 | + /* | |
3103 | + * I don't use atomic operations because race condition is not fatal. | |
3104 | + */ | |
3105 | + ccs_stat_updated[index]++; | |
3106 | + ccs_stat_modified[index] = tv.tv_sec; | |
3107 | +} | |
3108 | + | |
3109 | +/** | |
3110 | + * ccs_read_stat - Read statistic data. | |
3111 | + * | |
3112 | + * @head: Pointer to "struct ccs_io_buffer". | |
3113 | + * | |
3114 | + * Returns nothing. | |
3115 | + */ | |
3116 | +static void ccs_read_stat(struct ccs_io_buffer *head) | |
3117 | +{ | |
3118 | + u8 i; | |
3119 | + for (i = 0; i < CCS_MAX_POLICY_STAT; i++) { | |
3120 | + static const char * const k[CCS_MAX_POLICY_STAT] = { | |
3121 | + [CCS_STAT_POLICY_UPDATES] = "Policy updated:", | |
3122 | + [CCS_STAT_REQUEST_DENIED] = "Requests denied:", | |
3123 | + }; | |
3124 | + ccs_io_printf(head, "stat %s %u", k[i], ccs_stat_updated[i]); | |
3125 | + if (ccs_stat_modified[i]) { | |
3126 | + struct ccs_time stamp; | |
3127 | + ccs_convert_time(ccs_stat_modified[i], &stamp); | |
3128 | + ccs_io_printf(head, " (Last: %04u/%02u/%02u " | |
3129 | + "%02u:%02u:%02u)", | |
3130 | + stamp.year, stamp.month, stamp.day, | |
3131 | + stamp.hour, stamp.min, stamp.sec); | |
3132 | + } | |
3133 | + ccs_set_lf(head); | |
3134 | + } | |
3135 | + for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | |
3136 | + ccs_io_printf(head, "stat Memory used by %s: %u\n", | |
3137 | + ccs_memory_headers[i], ccs_memory_used[i]); | |
3138 | +} | |
3139 | + | |
3140 | +/** | |
3141 | + * ccs_read_quota - Read quota data. | |
3142 | + * | |
3143 | + * @head: Pointer to "struct ccs_io_buffer". | |
3144 | + * | |
3145 | + * Returns true on success, false otherwise. | |
3146 | + */ | |
3147 | +static bool ccs_read_quota(struct ccs_io_buffer *head) | |
3148 | +{ | |
3149 | + unsigned int i; | |
3150 | + while (head->r.step < CCS_MAX_MEMORY_STAT) { | |
3151 | + i = head->r.step++; | |
3152 | + if (!ccs_memory_quota[i]) | |
3153 | + continue; | |
3154 | + ccs_io_printf(head, "quota memory %s %u\n", | |
3155 | + ccs_memory_headers[i], ccs_memory_quota[i]); | |
3156 | + } | |
3157 | + while (head->r.step < CCS_MAX_GROUP + CCS_MAX_MEMORY_STAT) { | |
3158 | + unsigned int a; | |
3159 | + unsigned int d; | |
3160 | + unsigned int u; | |
3161 | + if (!ccs_flush(head)) | |
3162 | + return false; | |
3163 | + i = head->r.step - CCS_MAX_MEMORY_STAT; | |
3164 | + a = ccs_log_quota[i][CCS_MATCHING_ALLOWED]; | |
3165 | + d = ccs_log_quota[i][CCS_MATCHING_DENIED]; | |
3166 | + u = ccs_log_quota[i][CCS_MATCHING_UNMATCHED]; | |
3167 | + if (a || d || u) | |
3168 | + ccs_io_printf(head, "quota audit[%u] allowed=%u" | |
3169 | + " denied=%u unmatched=%u\n", i, a, d, u); | |
3170 | + head->r.step++; | |
3171 | + } | |
3172 | + head->r.step = 0; | |
3173 | + return true; | |
3174 | +} | |
3175 | + | |
3176 | +/** | |
3177 | + * ccs_write_memory_quota - Set memory quota. | |
3178 | + * | |
3179 | + * @data: Line to parse. | |
3180 | + * | |
3181 | + * Returns 0 on success, -EINVAL otherwise. | |
3182 | + */ | |
3183 | +static int ccs_write_memory_quota(char *data) | |
3184 | +{ | |
3185 | + u8 i; | |
3186 | + for (i = 0; i < CCS_MAX_MEMORY_STAT; i++) | |
3187 | + if (ccs_str_starts(&data, ccs_memory_headers[i])) { | |
3188 | + if (*data == ' ') | |
3189 | + data++; | |
3190 | + ccs_memory_quota[i] = | |
3191 | + simple_strtoul(data, NULL, 10); | |
3192 | + return 0; | |
3193 | + } | |
3194 | + return -EINVAL; | |
3195 | +} | |
3196 | + | |
3197 | +/** | |
3198 | + * ccs_write_audit_quota - Set audit log quota. | |
3199 | + * | |
3200 | + * @data: Line to parse. | |
3201 | + * | |
3202 | + * Returns 0 on success, -EINVAL otherwise. | |
3203 | + */ | |
3204 | +static int ccs_write_audit_quota(char *data) | |
3205 | +{ | |
3206 | + unsigned int i; | |
3207 | + if (sscanf(data, "audit[%u]", &i) != 1 || i >= CCS_MAX_LOG_QUOTA) | |
3208 | + return -EINVAL; | |
3209 | + data = strchr(data, ' '); | |
3210 | + if (!data++) | |
3211 | + return -EINVAL; | |
3212 | + while (1) { | |
3213 | + unsigned int logs; | |
3214 | + char *cp = strchr(data, ' '); | |
3215 | + if (cp) | |
3216 | + *cp++ = '\0'; | |
3217 | + if (sscanf(data, "allowed=%u", &logs) == 1) | |
3218 | + ccs_log_quota[i][CCS_MATCHING_ALLOWED] = logs; | |
3219 | + else if (sscanf(data, "denied=%u", &logs) == 1) | |
3220 | + ccs_log_quota[i][CCS_MATCHING_DENIED] = logs; | |
3221 | + else if (sscanf(data, "unmatched=%u", &logs) == 1) | |
3222 | + ccs_log_quota[i][CCS_MATCHING_UNMATCHED] = logs; | |
3223 | + if (!cp) | |
3224 | + break; | |
3225 | + data = cp; | |
3226 | + } | |
3227 | + return 0; | |
3228 | +} | |
3229 | + | |
3230 | +/** | |
3231 | + * ccs_print_bprm - Print "struct linux_binprm" for auditing. | |
3232 | + * | |
3233 | + * @bprm: Pointer to "struct linux_binprm". | |
3234 | + * @dump: Pointer to "struct ccs_page_dump". | |
3235 | + * | |
3236 | + * Returns the contents of @bprm on success, NULL otherwise. | |
3237 | + * | |
3238 | + * This function uses kzalloc(), so caller must kfree() if this function | |
3239 | + * didn't return NULL. | |
3240 | + */ | |
3241 | +static char *ccs_print_bprm(struct linux_binprm *bprm, | |
3242 | + struct ccs_page_dump *dump) | |
3243 | +{ | |
3244 | + static const int ccs_buffer_len = 4096 * 2; | |
3245 | + char *buffer = kzalloc(ccs_buffer_len, GFP_NOFS); | |
3246 | + char *cp; | |
3247 | + char *last_start; | |
3248 | + unsigned long pos = bprm->p; | |
3249 | + int offset = pos % PAGE_SIZE; | |
3250 | + int argv_count = bprm->argc; | |
3251 | + int envp_count = bprm->envc; | |
3252 | + bool skip = false; | |
3253 | + bool env_value = false; | |
3254 | + if (!buffer) | |
3255 | + return NULL; | |
3256 | + cp = buffer + snprintf(buffer, ccs_buffer_len - 1, " argc=%d envc=%d", | |
3257 | + argv_count, envp_count); | |
3258 | + last_start = cp; | |
3259 | + while (argv_count || envp_count) { | |
3260 | + if (!ccs_dump_page(bprm, pos, dump)) { | |
3261 | + kfree(buffer); | |
3262 | + return NULL; | |
3263 | + } | |
3264 | + pos += PAGE_SIZE - offset; | |
3265 | + /* Read. */ | |
3266 | + while (offset < PAGE_SIZE) { | |
3267 | + const char *kaddr = dump->data; | |
3268 | + const unsigned char c = kaddr[offset++]; | |
3269 | + int len; | |
3270 | + /* Check for end of buffer. */ | |
3271 | + if (skip) { | |
3272 | + if (c) | |
3273 | + continue; | |
3274 | + goto reset; | |
3275 | + } | |
3276 | + len = buffer + ccs_buffer_len - cp - 1; | |
3277 | + if (len <= 32 && c) { | |
3278 | + cp = last_start; | |
3279 | + skip = true; | |
3280 | + continue; | |
3281 | + } | |
3282 | + /* Print argv[$index]=" or envp[" part. */ | |
3283 | + if (cp == last_start) { | |
3284 | + int l; | |
3285 | + if (argv_count) | |
3286 | + l = snprintf(cp, len, " argv[%u]=\"", | |
3287 | + bprm->argc - argv_count); | |
3288 | + else | |
3289 | + l = snprintf(cp, len, " envp[\""); | |
3290 | + cp += l; | |
3291 | + len -= l; | |
3292 | + } | |
3293 | + if (c > ' ' && c < 127 && c != '\\') { | |
3294 | + /* Print "]=" part if printing environ. */ | |
3295 | + if (c == '=' && !argv_count && !env_value) { | |
3296 | + cp += snprintf(cp, len, "\"]=\""); | |
3297 | + env_value = true; | |
3298 | + } else | |
3299 | + *cp++ = c; | |
3300 | + continue; | |
3301 | + } | |
3302 | + if (c) { | |
3303 | + *cp++ = '\\'; | |
3304 | + *cp++ = (c >> 6) + '0'; | |
3305 | + *cp++ = ((c >> 3) & 7) + '0'; | |
3306 | + *cp++ = (c & 7) + '0'; | |
3307 | + continue; | |
3308 | + } | |
3309 | + /* Print "]=" part if not yet printed. */ | |
3310 | + if (!argv_count && !env_value) | |
3311 | + cp += snprintf(cp, len, "\"]=\""); | |
3312 | + *cp++ = '"'; | |
3313 | + last_start = cp; | |
3314 | +reset: | |
3315 | + skip = false; | |
3316 | + env_value = false; | |
3317 | + if (argv_count) | |
3318 | + argv_count--; | |
3319 | + else if (envp_count) | |
3320 | + envp_count--; | |
3321 | + if (!argv_count && !envp_count) | |
3322 | + break; | |
3323 | + } | |
3324 | + offset = 0; | |
3325 | + } | |
3326 | + *cp = '\0'; | |
3327 | + return buffer; | |
3328 | +} | |
3329 | + | |
3330 | +/** | |
3331 | + * ccs_filetype - Get string representation of file type. | |
3332 | + * | |
3333 | + * @mode: Mode value for stat(). | |
3334 | + * | |
3335 | + * Returns file type string. | |
3336 | + */ | |
3337 | +static inline const char *ccs_filetype(const umode_t mode) | |
3338 | +{ | |
3339 | + switch (mode & S_IFMT) { | |
3340 | + case S_IFREG: | |
3341 | + case 0: | |
3342 | + return "file"; | |
3343 | + case S_IFDIR: | |
3344 | + return "directory"; | |
3345 | + case S_IFLNK: | |
3346 | + return "symlink"; | |
3347 | + case S_IFIFO: | |
3348 | + return "fifo"; | |
3349 | + case S_IFSOCK: | |
3350 | + return "socket"; | |
3351 | + case S_IFBLK: | |
3352 | + return "block"; | |
3353 | + case S_IFCHR: | |
3354 | + return "char"; | |
3355 | + } | |
3356 | + return "unknown"; /* This should not happen. */ | |
3357 | +} | |
3358 | + | |
3359 | +/** | |
3360 | + * ccs_print_trailer - Get misc info of audit log. | |
3361 | + * | |
3362 | + * @r: Pointer to "struct ccs_request_info". | |
3363 | + * | |
3364 | + * Returns string representation. | |
3365 | + * | |
3366 | + * This function uses kmalloc(), so caller must kfree() if this function | |
3367 | + * didn't return NULL. | |
3368 | + */ | |
3369 | +static char *ccs_print_trailer(struct ccs_request_info *r) | |
3370 | +{ | |
3371 | + const char *handler = | |
3372 | + ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER ? "" : "!"; | |
3373 | + const char *exe = r->exename.name; | |
3374 | + const char *domain = ccs_current_domain()->domainname->name; | |
3375 | + const int ccs_buffer_len = 2000 + strlen(exe) + strlen(domain); | |
3376 | + char *buffer = kmalloc(ccs_buffer_len, GFP_NOFS); | |
3377 | + int pos; | |
3378 | + u8 i; | |
3379 | + if (!buffer) | |
3380 | + return NULL; | |
3381 | + pos = snprintf(buffer, ccs_buffer_len - 1, " task.pid=%u task.ppid=%u" | |
3382 | + " task.uid=%u task.gid=%u task.euid=%u task.egid=%u" | |
3383 | + " task.suid=%u task.sgid=%u task.fsuid=%u task.fsgid=%u" | |
3384 | + " task.type%s=execute_handler task.exe=\"%s\"" | |
3385 | + " task.domain=\"%s\"", ccs_sys_getpid(), | |
3386 | + ccs_sys_getppid(), current_uid(), current_gid(), | |
3387 | + current_euid(), current_egid(), current_suid(), | |
3388 | + current_sgid(), current_fsuid(), current_fsgid(), | |
3389 | + handler, exe, domain); | |
3390 | + if (!r->obj.path[0].dentry && !r->obj.path[1].dentry) | |
3391 | + goto no_obj_info; | |
3392 | + ccs_get_attributes(r); | |
3393 | + for (i = 0; i < CCS_MAX_PATH_STAT; i++) { | |
3394 | + char objname[32]; | |
3395 | + struct ccs_mini_stat *stat; | |
3396 | + unsigned int dev; | |
3397 | + umode_t mode; | |
3398 | + if (!r->obj.stat_valid[i]) | |
3399 | + continue; | |
3400 | + stat = &r->obj.stat[i]; | |
3401 | + dev = stat->dev; | |
3402 | + mode = stat->mode; | |
3403 | + memset(objname, 0, sizeof(objname)); | |
3404 | + snprintf(objname, sizeof(objname) - 1, "%s%s.", | |
3405 | + ccs_get_sarg(r->type, (i >> 1)), | |
3406 | + i & 1 ? ".parent" : ""); | |
3407 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | |
3408 | + " %suid=%u %sgid=%u %sino=%lu %smajor=%u" | |
3409 | + " %sminor=%u %sperm=0%o %stype=%s" | |
3410 | + " %sfsmagic=0x%lX", objname, stat->uid, | |
3411 | + objname, stat->gid, objname, (unsigned long) | |
3412 | + stat->ino, objname, MAJOR(dev), objname, | |
3413 | + MINOR(dev), objname, mode & S_IALLUGO, objname, | |
3414 | + ccs_filetype(mode), objname, stat->fsmagic); | |
3415 | + if (S_ISCHR(mode) || S_ISBLK(mode)) { | |
3416 | + dev = stat->rdev; | |
3417 | + pos += snprintf(buffer + pos, ccs_buffer_len - 1 - pos, | |
3418 | + " %sdev_major=%u %sdev_minor=%u", | |
3419 | + objname, MAJOR(dev), objname, | |
3420 | + MINOR(dev)); | |
3421 | + } | |
3422 | + } | |
3423 | +no_obj_info: | |
3424 | + if (pos < ccs_buffer_len - 1) | |
3425 | + return buffer; | |
3426 | + kfree(buffer); | |
3427 | + return NULL; | |
3428 | +} | |
3429 | + | |
3430 | +/** | |
3431 | + * ccs_print_param - Get arg info of audit log. | |
3432 | + * | |
3433 | + * @r: Pointer to "struct ccs_request_info". | |
3434 | + * @buf: Buffer to write. | |
3435 | + * @len: Size of @buf in bytes. | |
3436 | + */ | |
3437 | +static int ccs_print_param(struct ccs_request_info *r, char *buf, int len) | |
3438 | +{ | |
3439 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
3440 | + /* Make sure that IP address argument is ready. */ | |
3441 | + char ip[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; | |
3442 | + switch (r->type) { | |
3443 | + case CCS_MAC_INET_STREAM_BIND: | |
3444 | + case CCS_MAC_INET_STREAM_LISTEN: | |
3445 | + case CCS_MAC_INET_STREAM_CONNECT: | |
3446 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
3447 | + case CCS_MAC_INET_DGRAM_BIND: | |
3448 | + case CCS_MAC_INET_DGRAM_SEND: | |
3449 | + case CCS_MAC_INET_RAW_BIND: | |
3450 | + case CCS_MAC_INET_RAW_SEND: | |
3451 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3452 | + case CCS_MAC_INET_DGRAM_RECV: | |
3453 | + case CCS_MAC_INET_RAW_RECV: | |
3454 | +#endif | |
3455 | + if (!r->param.ip) | |
3456 | + return 0; | |
3457 | + if (r->param.is_ipv6) { | |
3458 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
3459 | + snprintf(ip, sizeof(ip), "%pI6c", | |
3460 | + (const struct in6_addr *) r->param.ip); | |
3461 | +#else | |
3462 | + ip6_compressed_string(ip, (const u8 *) r->param.ip); | |
3463 | +#endif | |
3464 | + } else { | |
3465 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) | |
3466 | + snprintf(ip, sizeof(ip), "%pI4", r->param.ip); | |
3467 | +#else | |
3468 | + ip4_string(ip, r->param.ip); | |
3469 | +#endif | |
3470 | + } | |
3471 | + break; | |
3472 | + default: | |
3473 | + break; | |
3474 | + } | |
3475 | +#endif | |
3476 | + /* Make sure that string arguments are ready. */ | |
3477 | + if (!r->param.s[0] && r->obj.path[0].dentry) { | |
3478 | + ccs_populate_patharg(r, true); | |
3479 | + if (!r->param.s[0]) | |
3480 | + return 0; | |
3481 | + } | |
3482 | + if (!r->param.s[1] && r->obj.path[1].dentry) { | |
3483 | + ccs_populate_patharg(r, false); | |
3484 | + if (!r->param.s[1]) | |
3485 | + return 0; | |
3486 | + } | |
3487 | + switch (r->type) { | |
3488 | + int pos; | |
3489 | + u8 i; | |
3490 | + case CCS_MAC_EXECUTE: | |
3491 | + return snprintf(buf, len, " exec=\"%s\" path=\"%s\"", | |
3492 | + r->param.s[1]->name, r->param.s[0]->name); | |
3493 | + case CCS_MAC_READ: | |
3494 | + case CCS_MAC_WRITE: | |
3495 | + case CCS_MAC_APPEND: | |
3496 | + case CCS_MAC_UNLINK: | |
3497 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
3498 | + case CCS_MAC_GETATTR: | |
3499 | +#endif | |
3500 | + case CCS_MAC_RMDIR: | |
3501 | + case CCS_MAC_TRUNCATE: | |
3502 | + case CCS_MAC_CHROOT: | |
3503 | + return snprintf(buf, len, " path=\"%s\"", r->param.s[0]->name); | |
3504 | + case CCS_MAC_CREATE: | |
3505 | + case CCS_MAC_MKDIR: | |
3506 | + case CCS_MAC_MKFIFO: | |
3507 | + case CCS_MAC_MKSOCK: | |
3508 | + return snprintf(buf, len, " path=\"%s\" perm=0%lo", | |
3509 | + r->param.s[0]->name, r->param.i[0]); | |
3510 | + case CCS_MAC_SYMLINK: | |
3511 | + return snprintf(buf, len, " path=\"%s\" target=\"%s\"", | |
3512 | + r->param.s[0]->name, r->param.s[1]->name); | |
3513 | + case CCS_MAC_MKBLOCK: | |
3514 | + case CCS_MAC_MKCHAR: | |
3515 | + return snprintf(buf, len, " path=\"%s\" perm=0%lo " | |
3516 | + "dev_major=%lu dev_minor=%lu", | |
3517 | + r->param.s[0]->name, r->param.i[0], | |
3518 | + r->param.i[1], r->param.i[2]); | |
3519 | + case CCS_MAC_LINK: | |
3520 | + case CCS_MAC_RENAME: | |
3521 | + return snprintf(buf, len, " old_path=\"%s\" new_path=\"%s\"", | |
3522 | + r->param.s[0]->name, r->param.s[1]->name); | |
3523 | + case CCS_MAC_CHMOD: | |
3524 | + return snprintf(buf, len, " path=\"%s\" perm=0%lo", | |
3525 | + r->param.s[0]->name, r->param.i[0]); | |
3526 | + case CCS_MAC_CHOWN: | |
3527 | + return snprintf(buf, len, " path=\"%s\" uid=%lu", | |
3528 | + r->param.s[0]->name, r->param.i[0]); | |
3529 | + case CCS_MAC_CHGRP: | |
3530 | + return snprintf(buf, len, " path=\"%s\" gid=%lu", | |
3531 | + r->param.s[0]->name, r->param.i[0]); | |
3532 | + case CCS_MAC_IOCTL: | |
3533 | + return snprintf(buf, len, " path=\"%s\" cmd=0x%lX", | |
3534 | + r->param.s[0]->name, r->param.i[0]); | |
3535 | + case CCS_MAC_MOUNT: | |
3536 | + pos = 0; | |
3537 | + for (i = 0; i < 4; i++) { | |
3538 | + if (i == 3) | |
3539 | + pos += snprintf(buf + pos, pos < len ? | |
3540 | + len - pos : 0, " flags=0x%lX", | |
3541 | + r->param.i[0]); | |
3542 | + if (!r->param.s[i]) | |
3543 | + continue; | |
3544 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3545 | + " %s=\"%s\"", | |
3546 | + ccs_get_sarg(CCS_MAC_MOUNT, i), | |
3547 | + r->param.s[i]->name); | |
3548 | + } | |
3549 | + return pos; | |
3550 | + case CCS_MAC_UMOUNT: | |
3551 | + return snprintf(buf, len, " path=\"%s\" flags=0x%lX", | |
3552 | + r->param.s[0]->name, r->param.i[0]); | |
3553 | + case CCS_MAC_PIVOT_ROOT: | |
3554 | + return snprintf(buf, len, " new_root=\"%s\" put_old=\"%s\"", | |
3555 | + r->param.s[0]->name, r->param.s[1]->name); | |
3556 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
3557 | + case CCS_MAC_ENVIRON: | |
3558 | + return snprintf(buf, len, " name=\"%s\" value=\"%s\"" | |
3559 | + " exec=\"%s\" path=\"%s\"", | |
3560 | + r->param.s[2]->name, r->param.s[3]->name, | |
3561 | + r->param.s[1]->name, r->param.s[0]->name); | |
3562 | +#endif | |
3563 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
3564 | + case CCS_MAC_INET_STREAM_BIND: | |
3565 | + case CCS_MAC_INET_STREAM_LISTEN: | |
3566 | + case CCS_MAC_INET_STREAM_CONNECT: | |
3567 | + case CCS_MAC_INET_STREAM_ACCEPT: | |
3568 | + case CCS_MAC_INET_DGRAM_BIND: | |
3569 | + case CCS_MAC_INET_DGRAM_SEND: | |
3570 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3571 | + case CCS_MAC_INET_DGRAM_RECV: | |
3572 | +#endif | |
3573 | + return snprintf(buf, len, " ip=%s port=%lu", ip, | |
3574 | + r->param.i[0]); | |
3575 | + case CCS_MAC_INET_RAW_BIND: | |
3576 | + case CCS_MAC_INET_RAW_SEND: | |
3577 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3578 | + case CCS_MAC_INET_RAW_RECV: | |
3579 | +#endif | |
3580 | + return snprintf(buf, len, " ip=%s proto=%lu", ip, | |
3581 | + r->param.i[0]); | |
3582 | + case CCS_MAC_UNIX_STREAM_BIND: | |
3583 | + case CCS_MAC_UNIX_STREAM_LISTEN: | |
3584 | + case CCS_MAC_UNIX_STREAM_CONNECT: | |
3585 | + case CCS_MAC_UNIX_STREAM_ACCEPT: | |
3586 | + case CCS_MAC_UNIX_DGRAM_BIND: | |
3587 | + case CCS_MAC_UNIX_DGRAM_SEND: | |
3588 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
3589 | + case CCS_MAC_UNIX_DGRAM_RECV: | |
3590 | +#endif | |
3591 | + case CCS_MAC_UNIX_SEQPACKET_BIND: | |
3592 | + case CCS_MAC_UNIX_SEQPACKET_LISTEN: | |
3593 | + case CCS_MAC_UNIX_SEQPACKET_CONNECT: | |
3594 | + case CCS_MAC_UNIX_SEQPACKET_ACCEPT: | |
3595 | + return snprintf(buf, len, " addr=\"%s\"", r->param.s[0]->name); | |
3596 | +#endif | |
3597 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
3598 | + case CCS_MAC_PTRACE: | |
3599 | + return snprintf(buf, len, " cmd=%lu domain=\"%s\"", | |
3600 | + r->param.i[0], r->param.s[0]->name); | |
3601 | +#endif | |
3602 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
3603 | + case CCS_MAC_SIGNAL: | |
3604 | + return snprintf(buf, len, " sig=%lu", r->param.i[0]); | |
3605 | +#endif | |
3606 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
3607 | + case CCS_MAC_MANUAL_DOMAIN_TRANSITION: | |
3608 | + return snprintf(buf, len, " domain=\"%s\"", | |
3609 | + r->param.s[0]->name); | |
3610 | +#endif | |
3611 | + default: | |
3612 | + break; | |
3613 | + } | |
3614 | + return 0; | |
3615 | +} | |
3616 | + | |
3617 | +/** | |
3618 | + * ccs_init_log - Allocate buffer for audit logs. | |
3619 | + * | |
3620 | + * @r: Pointer to "struct ccs_request_info". | |
3621 | + * | |
3622 | + * Returns pointer to allocated memory. | |
3623 | + * | |
3624 | + * This function uses kzalloc(), so caller must kfree() if this function | |
3625 | + * didn't return NULL. | |
3626 | + */ | |
3627 | +static char *ccs_init_log(struct ccs_request_info *r) | |
3628 | +{ | |
3629 | + const pid_t gpid = task_pid_nr(current); | |
3630 | + struct timeval tv; | |
3631 | + struct ccs_time stamp; | |
3632 | + static const char * const k[CCS_MAX_MATCHING] = { | |
3633 | + [CCS_MATCHING_UNMATCHED] = "unmatched", | |
3634 | + [CCS_MATCHING_ALLOWED] = "allowed", | |
3635 | + [CCS_MATCHING_DENIED] = "denied", | |
3636 | + }; | |
3637 | + char *buf; | |
3638 | + const char *bprm_info; | |
3639 | + const char *trailer; | |
3640 | + int len; | |
3641 | + if (!r->exename.name && !ccs_get_exename(&r->exename)) | |
3642 | + return NULL; | |
3643 | + do_gettimeofday(&tv); | |
3644 | + ccs_convert_time(tv.tv_sec, &stamp); | |
3645 | + trailer = ccs_print_trailer(r); | |
3646 | + if (r->bprm) | |
3647 | + bprm_info = ccs_print_bprm(r->bprm, &r->dump); | |
3648 | + else | |
3649 | + bprm_info = NULL; | |
3650 | + len = 0; | |
3651 | + while (1) { | |
3652 | + int pos; | |
3653 | + buf = kzalloc(len, GFP_NOFS); | |
3654 | + if (!buf) | |
3655 | + break; | |
3656 | + pos = snprintf(buf, len, "#%04u/%02u/%02u %02u:%02u:%02u# " | |
3657 | + "global-pid=%u result=%s priority=%u / %s", | |
3658 | + stamp.year, stamp.month, stamp.day, stamp.hour, | |
3659 | + stamp.min, stamp.sec, gpid, k[r->result], | |
3660 | + r->matched_acl ? r->matched_acl->priority : 0, | |
3661 | + ccs_mac_keywords[r->type]); | |
3662 | + pos += ccs_print_param(r, buf + pos, | |
3663 | + pos < len ? len - pos : 0); | |
3664 | + if (bprm_info) | |
3665 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3666 | + "%s", bprm_info); | |
3667 | + if (trailer) | |
3668 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3669 | + "%s", trailer); | |
3670 | + pos += snprintf(buf + pos, pos < len ? len - pos : 0, | |
3671 | + "\n") + 1; | |
3672 | + if (pos <= len) | |
3673 | + break; | |
3674 | + kfree(buf); | |
3675 | + len = pos; | |
3676 | + } | |
3677 | + kfree(bprm_info); | |
3678 | + kfree(trailer); | |
3679 | + return buf; | |
3680 | +} | |
3681 | + | |
3682 | +/** | |
3683 | + * ccs_write_log - Write an audit log. | |
3684 | + * | |
3685 | + * @r: Pointer to "struct ccs_request_info". | |
3686 | + * | |
3687 | + * Returns nothing. | |
3688 | + */ | |
3689 | +static void ccs_write_log(struct ccs_request_info *r) | |
3690 | +{ | |
3691 | + struct ccs_log *entry; | |
3692 | + bool quota_exceeded = false; | |
3693 | + int len; | |
3694 | + char *buf = ccs_init_log(r); | |
3695 | + if (!buf) | |
3696 | + return; | |
3697 | + entry = kzalloc(sizeof(*entry), GFP_NOFS); | |
3698 | + if (!entry) { | |
3699 | + kfree(buf); | |
3700 | + return; | |
3701 | + } | |
3702 | + entry->log = buf; | |
3703 | + len = ccs_round2(strlen(buf) + 1); | |
3704 | + /* | |
3705 | + * The entry->size is used for memory quota checks. | |
3706 | + * Don't go beyond strlen(entry->log). | |
3707 | + */ | |
3708 | + entry->size = len + ccs_round2(sizeof(*entry)); | |
3709 | + entry->result = r->result; | |
3710 | + spin_lock(&ccs_log_lock); | |
3711 | + if (ccs_memory_quota[CCS_MEMORY_AUDIT] && | |
3712 | + ccs_memory_used[CCS_MEMORY_AUDIT] + entry->size >= | |
3713 | + ccs_memory_quota[CCS_MEMORY_AUDIT]) { | |
3714 | + quota_exceeded = true; | |
3715 | + } else { | |
3716 | + ccs_memory_used[CCS_MEMORY_AUDIT] += entry->size; | |
3717 | + list_add_tail(&entry->list, &ccs_log); | |
3718 | + ccs_log_count[entry->result]++; | |
3719 | + } | |
3720 | + spin_unlock(&ccs_log_lock); | |
3721 | + if (quota_exceeded) { | |
3722 | + kfree(buf); | |
3723 | + kfree(entry); | |
3724 | + return; | |
3725 | + } | |
3726 | + wake_up(&ccs_log_wait); | |
3727 | +} | |
3728 | + | |
3729 | +/** | |
3730 | + * ccs_read_log - Read an audit log. | |
3731 | + * | |
3732 | + * @head: Pointer to "struct ccs_io_buffer". | |
3733 | + * | |
3734 | + * Returns nothing. | |
3735 | + */ | |
3736 | +static void ccs_read_log(struct ccs_io_buffer *head) | |
3737 | +{ | |
3738 | + struct ccs_log *ptr = NULL; | |
3739 | + if (head->r.w_pos) | |
3740 | + return; | |
3741 | + kfree(head->read_buf); | |
3742 | + head->read_buf = NULL; | |
3743 | + spin_lock(&ccs_log_lock); | |
3744 | + if (!list_empty(&ccs_log)) { | |
3745 | + ptr = list_entry(ccs_log.next, typeof(*ptr), list); | |
3746 | + list_del(&ptr->list); | |
3747 | + ccs_log_count[ptr->result]--; | |
3748 | + ccs_memory_used[CCS_MEMORY_AUDIT] -= ptr->size; | |
3749 | + } | |
3750 | + spin_unlock(&ccs_log_lock); | |
3751 | + if (ptr) { | |
3752 | + head->read_buf = ptr->log; | |
3753 | + head->r.w[head->r.w_pos++] = head->read_buf; | |
3754 | + kfree(ptr); | |
3755 | + } | |
3756 | +} | |
3757 | + | |
3758 | +/** | |
3759 | + * ccs_transit_domain - Transit to other domain. | |
3760 | + * | |
3761 | + * @domainname: The name of domain. | |
3762 | + * | |
3763 | + * Returns true on success, false otherwise. | |
3764 | + * | |
3765 | + * Caller holds ccs_read_lock(). | |
3766 | + */ | |
3767 | +bool ccs_transit_domain(const char *domainname) | |
3768 | +{ | |
3769 | + struct ccs_security *security = ccs_current_security(); | |
3770 | + struct ccs_domain_info e = { }; | |
3771 | + struct ccs_domain_info *entry = ccs_find_domain(domainname); | |
3772 | + if (entry) { | |
3773 | + security->ccs_domain_info = entry; | |
3774 | + return true; | |
3775 | + } | |
3776 | + /* Requested domain does not exist. */ | |
3777 | + /* Don't create requested domain if domainname is invalid. */ | |
3778 | + if (!ccs_correct_domain(domainname)) | |
3779 | + return false; | |
3780 | + e.domainname = ccs_get_name(domainname); | |
3781 | + if (!e.domainname) | |
3782 | + return false; | |
3783 | + if (mutex_lock_interruptible(&ccs_policy_lock)) | |
3784 | + goto out; | |
3785 | + entry = ccs_find_domain(domainname); | |
3786 | + if (entry) | |
3787 | + goto done; | |
3788 | + entry = ccs_commit_ok(&e, sizeof(e)); | |
3789 | + if (!entry) | |
3790 | + goto done; | |
3791 | + list_add_tail_rcu(&entry->list, &ccs_domain_list); | |
3792 | +done: | |
3793 | + mutex_unlock(&ccs_policy_lock); | |
3794 | +out: | |
3795 | + ccs_put_name(e.domainname); | |
3796 | + if (entry) | |
3797 | + security->ccs_domain_info = entry; | |
3798 | + return entry != NULL; | |
3799 | +} | |
3800 | + | |
3801 | +/** | |
3802 | + * ccs_parse_policy - Parse a policy line. | |
3803 | + * | |
3804 | + * @head: Poiter to "struct ccs_io_buffer". | |
3805 | + * @line: Line to parse. | |
3806 | + * | |
3807 | + * Returns 0 on success, negative value otherwise. | |
3808 | + * | |
3809 | + * Caller holds ccs_read_lock(). | |
3810 | + */ | |
3811 | +static int ccs_parse_policy(struct ccs_io_buffer *head, char *line) | |
3812 | +{ | |
3813 | + /* Set current line's content. */ | |
3814 | + head->w.data = line; | |
3815 | + head->w.is_deny = false; | |
3816 | + head->w.priority = 0; | |
3817 | + /* Delete request? */ | |
3818 | + head->w.is_delete = !strncmp(line, "delete ", 7); | |
3819 | + if (head->w.is_delete) | |
3820 | + memmove(line, line + 7, strlen(line + 7) + 1); | |
3821 | + /* Do the update. */ | |
3822 | + switch (head->type) { | |
3823 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
3824 | + case CCS_EXECUTE_HANDLER: | |
3825 | +#endif | |
3826 | + case CCS_PROCESS_STATUS: | |
3827 | + return ccs_write_pid(head); | |
3828 | + case CCS_QUERY: | |
3829 | + return ccs_write_answer(head); | |
3830 | + case CCS_POLICY: | |
3831 | + return ccs_write_policy(head); | |
3832 | + default: | |
3833 | + return -ENOSYS; | |
3834 | + } | |
3835 | +} | |
3836 | + | |
3837 | +/** | |
3838 | + * ccs_policy_io_init - Register hooks for policy I/O. | |
3839 | + * | |
3840 | + * Returns nothing. | |
3841 | + */ | |
3842 | +static void __init ccs_policy_io_init(void) | |
3843 | +{ | |
3844 | + ccsecurity_ops.check_profile = ccs_check_profile; | |
3845 | +} | |
3846 | + | |
3847 | +/** | |
3848 | + * ccs_load_builtin_policy - Load built-in policy. | |
3849 | + * | |
3850 | + * Returns nothing. | |
3851 | + */ | |
3852 | +static void __init ccs_load_builtin_policy(void) | |
3853 | +{ | |
3854 | + /* | |
3855 | + * This include file is manually created and contains built-in policy. | |
3856 | + * | |
3857 | + * static char [] __initdata ccs_builtin_policy = { ... }; | |
3858 | + */ | |
3859 | +#include "builtin-policy.h" | |
3860 | + const int idx = ccs_read_lock(); | |
3861 | + struct ccs_io_buffer head = { }; | |
3862 | + char *start = ccs_builtin_policy; | |
3863 | + head.type = CCS_POLICY; | |
3864 | + while (1) { | |
3865 | + char *end = strchr(start, '\n'); | |
3866 | + if (!end) | |
3867 | + break; | |
3868 | + *end = '\0'; | |
3869 | + ccs_normalize_line(start); | |
3870 | + head.write_buf = start; | |
3871 | + ccs_parse_policy(&head, start); | |
3872 | + start = end + 1; | |
3873 | + } | |
3874 | + ccs_read_unlock(idx); | |
3875 | +#ifdef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
3876 | + ccs_check_profile(); | |
3877 | +#endif | |
3878 | +} | |
3879 | + | |
3880 | +/** | |
3881 | + * ccs_read_self - read() for /proc/caitsith/self_domain interface. | |
3882 | + * | |
3883 | + * @file: Pointer to "struct file". | |
3884 | + * @buf: Domainname which current thread belongs to. | |
3885 | + * @count: Size of @buf. | |
3886 | + * @ppos: Bytes read by now. | |
3887 | + * | |
3888 | + * Returns read size on success, negative value otherwise. | |
3889 | + */ | |
3890 | +static ssize_t ccs_read_self(struct file *file, char __user *buf, size_t count, | |
3891 | + loff_t *ppos) | |
3892 | +{ | |
3893 | + const char *domain = ccs_current_domain()->domainname->name; | |
3894 | + loff_t len = strlen(domain); | |
3895 | + loff_t pos = *ppos; | |
3896 | + if (pos >= len || !count) | |
3897 | + return 0; | |
3898 | + len -= pos; | |
3899 | + if (count < len) | |
3900 | + len = count; | |
3901 | + if (copy_to_user(buf, domain + pos, len)) | |
3902 | + return -EFAULT; | |
3903 | + *ppos += len; | |
3904 | + return len; | |
3905 | +} | |
3906 | + | |
3907 | +/** | |
3908 | + * ccs_read_subacl - Read sub ACL in ACL entry. | |
3909 | + * | |
3910 | + * @head: Pointer to "struct ccs_io_buffer". | |
3911 | + * @list: Pointer to "struct list_head". | |
3912 | + * | |
3913 | + * Returns true on success, false otherwise. | |
3914 | + * | |
3915 | + * Caller holds ccs_read_lock(). | |
3916 | + */ | |
3917 | +static bool ccs_read_subacl(struct ccs_io_buffer *head, | |
3918 | + const struct list_head *list) | |
3919 | +{ | |
3920 | + list_for_each_cookie(head->r.subacl, list) { | |
3921 | + struct ccs_acl_info *acl = | |
3922 | + list_entry(head->r.subacl, typeof(*acl), list); | |
3923 | + switch (head->r.step) { | |
3924 | + case 3: | |
3925 | + if (acl->is_deleted) | |
3926 | + continue; | |
3927 | + if (!ccs_flush(head)) | |
3928 | + return false; | |
3929 | + ccs_io_printf(head, " %u ", acl->priority); | |
3930 | + if (acl->is_deny) | |
3931 | + ccs_set_string(head, "deny"); | |
3932 | + else | |
3933 | + ccs_set_string(head, "allow"); | |
3934 | + head->r.cond_step = 0; | |
3935 | + head->r.step++; | |
3936 | + /* fall through */ | |
3937 | + case 4: | |
3938 | + if (!ccs_flush(head)) | |
3939 | + return false; | |
3940 | + if (acl->cond && | |
3941 | + !ccs_print_condition(head, acl->cond)) | |
3942 | + return false; | |
3943 | + ccs_set_lf(head); | |
3944 | + head->r.step--; | |
3945 | + } | |
3946 | + } | |
3947 | + head->r.subacl = NULL; | |
3948 | + return true; | |
3949 | +} | |
3950 | + | |
3951 | +/** | |
3952 | + * ccs_read_policy - Read policy. | |
3953 | + * | |
3954 | + * @head: Pointer to "struct ccs_io_buffer". | |
3955 | + * | |
3956 | + * Caller holds ccs_read_lock(). | |
3957 | + */ | |
3958 | +static void ccs_read_policy(struct ccs_io_buffer *head) | |
3959 | +{ | |
3960 | + if (head->r.eof) | |
3961 | + return; | |
3962 | + if (head->r.print_this_acl_only) | |
3963 | + goto skip; | |
3964 | + if (!head->r.version_done) { | |
3965 | + ccs_io_printf(head, "POLICY_VERSION=%u\n", ccs_policy_version); | |
3966 | + head->r.version_done = true; | |
3967 | + } | |
3968 | + if (!head->r.stat_done) { | |
3969 | + ccs_read_stat(head); | |
3970 | + head->r.stat_done = true; | |
3971 | + } | |
3972 | + if (!head->r.quota_done) { | |
3973 | + if (!ccs_read_quota(head)) | |
3974 | + return; | |
3975 | + head->r.quota_done = true; | |
3976 | + } | |
3977 | + if (!head->r.group_done) { | |
3978 | + if (!ccs_read_group(head)) | |
3979 | + return; | |
3980 | + head->r.group_done = true; | |
3981 | + ccs_set_lf(head); | |
3982 | + } | |
3983 | + while (head->r.acl_index < CCS_MAX_MAC_INDEX) { | |
3984 | + struct list_head * const list = | |
3985 | + &ccs_acl_list[head->r.acl_index]; | |
3986 | + list_for_each_cookie(head->r.acl, list) { | |
3987 | + struct ccs_acl_info *ptr; | |
3988 | +skip: | |
3989 | + ptr = list_entry(head->r.acl, typeof(*ptr), list); | |
3990 | + switch (head->r.step) { | |
3991 | + case 0: | |
3992 | + if (ptr->is_deleted && | |
3993 | + !head->r.print_this_acl_only) | |
3994 | + continue; | |
3995 | + head->r.step++; | |
3996 | + /* fall through */ | |
3997 | + case 1: | |
3998 | + if (!ccs_read_acl(head, ptr)) | |
3999 | + return; | |
4000 | + head->r.step++; | |
4001 | + /* fall through */ | |
4002 | + case 2: | |
4003 | + if (!ccs_flush(head)) | |
4004 | + return; | |
4005 | + ccs_io_printf(head, " audit %u\n", | |
4006 | + ptr->audit); | |
4007 | + head->r.step++; | |
4008 | + /* fall through */ | |
4009 | + case 3: | |
4010 | + case 4: | |
4011 | + if (!ccs_read_subacl(head, | |
4012 | + &ptr->acl_info_list)) | |
4013 | + return; | |
4014 | + head->r.step = 5; | |
4015 | + /* fall through */ | |
4016 | + case 5: | |
4017 | + if (!ccs_flush(head)) | |
4018 | + return; | |
4019 | + ccs_set_lf(head); | |
4020 | + head->r.step = 0; | |
4021 | + if (head->r.print_this_acl_only) | |
4022 | + goto done; | |
4023 | + } | |
4024 | + } | |
4025 | + head->r.acl = NULL; | |
4026 | + head->r.acl_index++; | |
4027 | + } | |
4028 | +done: | |
4029 | + head->r.eof = true; | |
4030 | +} | |
4031 | + | |
4032 | +/** | |
4033 | + * ccs_open - open() for /proc/caitsith/ interface. | |
4034 | + * | |
4035 | + * @inode: Pointer to "struct inode". | |
4036 | + * @file: Pointer to "struct file". | |
4037 | + * | |
4038 | + * Returns 0 on success, negative value otherwise. | |
4039 | + */ | |
4040 | +static int ccs_open(struct inode *inode, struct file *file) | |
4041 | +{ | |
4042 | + const u8 type = (unsigned long) PDE(inode)->data; | |
4043 | + struct ccs_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); | |
4044 | + if (!head) | |
4045 | + return -ENOMEM; | |
4046 | + mutex_init(&head->io_sem); | |
4047 | + head->type = type; | |
4048 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4049 | + if (type == CCS_EXECUTE_HANDLER) { | |
4050 | + /* Allow execute_handler to read process's status. */ | |
4051 | + if (!(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | |
4052 | + kfree(head); | |
4053 | + return -EPERM; | |
4054 | + } | |
4055 | + } | |
4056 | +#endif | |
4057 | + if ((file->f_mode & FMODE_READ) && type != CCS_AUDIT && | |
4058 | + type != CCS_QUERY) { | |
4059 | + /* Don't allocate read_buf for poll() access. */ | |
4060 | + head->readbuf_size = 4096; | |
4061 | + head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); | |
4062 | + if (!head->read_buf) { | |
4063 | + kfree(head); | |
4064 | + return -ENOMEM; | |
4065 | + } | |
4066 | + } | |
4067 | + if (file->f_mode & FMODE_WRITE) { | |
4068 | + head->writebuf_size = 4096; | |
4069 | + head->write_buf = kzalloc(head->writebuf_size, GFP_NOFS); | |
4070 | + if (!head->write_buf) { | |
4071 | + kfree(head->read_buf); | |
4072 | + kfree(head); | |
4073 | + return -ENOMEM; | |
4074 | + } | |
4075 | + } | |
4076 | + /* | |
4077 | + * If the file is /proc/caitsith/query, increment the observer counter. | |
4078 | + * The obserber counter is used by ccs_supervisor() to see if | |
4079 | + * there is some process monitoring /proc/caitsith/query. | |
4080 | + */ | |
4081 | + if (type == CCS_QUERY) | |
4082 | + atomic_inc(&ccs_query_observers); | |
4083 | + file->private_data = head; | |
4084 | + ccs_notify_gc(head, true); | |
4085 | + return 0; | |
4086 | +} | |
4087 | + | |
4088 | +/** | |
4089 | + * ccs_release - close() for /proc/caitsith/ interface. | |
4090 | + * | |
4091 | + * @inode: Pointer to "struct inode". | |
4092 | + * @file: Pointer to "struct file". | |
4093 | + * | |
4094 | + * Returns 0. | |
4095 | + */ | |
4096 | +static int ccs_release(struct inode *inode, struct file *file) | |
4097 | +{ | |
4098 | + struct ccs_io_buffer *head = file->private_data; | |
4099 | + /* | |
4100 | + * If the file is /proc/caitsith/query, decrement the observer counter. | |
4101 | + */ | |
4102 | + if (head->type == CCS_QUERY && | |
4103 | + atomic_dec_and_test(&ccs_query_observers)) | |
4104 | + wake_up_all(&ccs_answer_wait); | |
4105 | + ccs_notify_gc(head, false); | |
4106 | + return 0; | |
4107 | +} | |
4108 | + | |
4109 | +/** | |
4110 | + * ccs_poll - poll() for /proc/caitsith/ interface. | |
4111 | + * | |
4112 | + * @file: Pointer to "struct file". | |
4113 | + * @wait: Pointer to "poll_table". Maybe NULL. | |
4114 | + * | |
4115 | + * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, | |
4116 | + * POLLOUT | POLLWRNORM otherwise. | |
4117 | + */ | |
4118 | +static unsigned int ccs_poll(struct file *file, poll_table *wait) | |
4119 | +{ | |
4120 | + struct ccs_io_buffer *head = file->private_data; | |
4121 | + if (head->type == CCS_AUDIT) { | |
4122 | + if (!ccs_memory_used[CCS_MEMORY_AUDIT]) { | |
4123 | + poll_wait(file, &ccs_log_wait, wait); | |
4124 | + if (!ccs_memory_used[CCS_MEMORY_AUDIT]) | |
4125 | + return POLLOUT | POLLWRNORM; | |
4126 | + } | |
4127 | + } else if (head->type == CCS_QUERY) { | |
4128 | + if (list_empty(&ccs_query_list)) { | |
4129 | + poll_wait(file, &ccs_query_wait, wait); | |
4130 | + if (list_empty(&ccs_query_list)) | |
4131 | + return POLLOUT | POLLWRNORM; | |
4132 | + } | |
4133 | + } | |
4134 | + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; | |
4135 | +} | |
4136 | + | |
4137 | +/** | |
4138 | + * ccs_read - read() for /proc/caitsith/ interface. | |
4139 | + * | |
4140 | + * @file: Pointer to "struct file". | |
4141 | + * @buf: Pointer to buffer. | |
4142 | + * @count: Size of @buf. | |
4143 | + * @ppos: Unused. | |
4144 | + * | |
4145 | + * Returns bytes read on success, negative value otherwise. | |
4146 | + */ | |
4147 | +static ssize_t ccs_read(struct file *file, char __user *buf, size_t count, | |
4148 | + loff_t *ppos) | |
4149 | +{ | |
4150 | + struct ccs_io_buffer *head = file->private_data; | |
4151 | + int len; | |
4152 | + int idx; | |
4153 | + if (mutex_lock_interruptible(&head->io_sem)) | |
4154 | + return -EINTR; | |
4155 | + head->read_user_buf = buf; | |
4156 | + head->read_user_buf_avail = count; | |
4157 | + idx = ccs_read_lock(); | |
4158 | + if (ccs_flush(head)) { | |
4159 | + /* Call the policy handler. */ | |
4160 | + switch (head->type) { | |
4161 | + case CCS_AUDIT: | |
4162 | + ccs_read_log(head); | |
4163 | + break; | |
4164 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4165 | + case CCS_EXECUTE_HANDLER: | |
4166 | +#endif | |
4167 | + case CCS_PROCESS_STATUS: | |
4168 | + ccs_read_pid(head); | |
4169 | + break; | |
4170 | + case CCS_VERSION: | |
4171 | + ccs_read_version(head); | |
4172 | + break; | |
4173 | + case CCS_QUERY: | |
4174 | + ccs_read_query(head); | |
4175 | + break; | |
4176 | + case CCS_POLICY: | |
4177 | + ccs_read_policy(head); | |
4178 | + break; | |
4179 | + } | |
4180 | + ccs_flush(head); | |
4181 | + } | |
4182 | + ccs_read_unlock(idx); | |
4183 | + len = head->read_user_buf - buf; | |
4184 | + mutex_unlock(&head->io_sem); | |
4185 | + return len; | |
4186 | +} | |
4187 | + | |
4188 | +#ifdef CONFIG_CCSECURITY_MANUAL_DOMAIN_TRANSITION | |
4189 | + | |
4190 | +/** | |
4191 | + * ccs_write_self - write() for /proc/caitsith/self_domain interface. | |
4192 | + * | |
4193 | + * @file: Pointer to "struct file". | |
4194 | + * @buf: Domainname to transit to. | |
4195 | + * @count: Size of @buf. | |
4196 | + * @ppos: Unused. | |
4197 | + * | |
4198 | + * Returns @count on success, negative value otherwise. | |
4199 | + * | |
4200 | + * If domain transition was permitted but the domain transition failed, this | |
4201 | + * function returns error rather than terminating current thread with SIGKILL. | |
4202 | + */ | |
4203 | +static ssize_t ccs_write_self(struct file *file, const char __user *buf, | |
4204 | + size_t count, loff_t *ppos) | |
4205 | +{ | |
4206 | + char *data; | |
4207 | + int error; | |
4208 | + if (!count || count >= CCS_EXEC_TMPSIZE - 10) | |
4209 | + return -ENOMEM; | |
4210 | + data = kzalloc(count + 1, GFP_NOFS); | |
4211 | + if (!data) | |
4212 | + return -ENOMEM; | |
4213 | + if (copy_from_user(data, buf, count)) { | |
4214 | + error = -EFAULT; | |
4215 | + goto out; | |
4216 | + } | |
4217 | + ccs_normalize_line(data); | |
4218 | + if (ccs_correct_domain(data)) { | |
4219 | + const int idx = ccs_read_lock(); | |
4220 | + struct ccs_path_info name; | |
4221 | + struct ccs_request_info r = { }; | |
4222 | + name.name = data; | |
4223 | + ccs_fill_path_info(&name); | |
4224 | + /* Check "manual_domain_transition" permission. */ | |
4225 | + r.type = CCS_MAC_MANUAL_DOMAIN_TRANSITION; | |
4226 | + r.param.s[0] = &name; | |
4227 | + ccs_check_acl(&r, true); | |
4228 | + if (r.result != CCS_MATCHING_ALLOWED) | |
4229 | + error = -EPERM; | |
4230 | + else | |
4231 | + error = ccs_transit_domain(data) ? 0 : -ENOENT; | |
4232 | + ccs_read_unlock(idx); | |
4233 | + } else | |
4234 | + error = -EINVAL; | |
4235 | +out: | |
4236 | + kfree(data); | |
4237 | + return error ? error : count; | |
4238 | +} | |
4239 | + | |
4240 | +#endif | |
4241 | + | |
4242 | +/** | |
4243 | + * ccs_write - write() for /proc/caitsith/ interface. | |
4244 | + * | |
4245 | + * @file: Pointer to "struct file". | |
4246 | + * @buf: Pointer to buffer. | |
4247 | + * @count: Size of @buf. | |
4248 | + * @ppos: Unused. | |
4249 | + * | |
4250 | + * Returns @count on success, negative value otherwise. | |
4251 | + */ | |
4252 | +static ssize_t ccs_write(struct file *file, const char __user *buf, | |
4253 | + size_t count, loff_t *ppos) | |
4254 | +{ | |
4255 | + struct ccs_io_buffer *head = file->private_data; | |
4256 | + int error = count; | |
4257 | + char *cp0 = head->write_buf; | |
4258 | + int idx; | |
4259 | + if (mutex_lock_interruptible(&head->io_sem)) | |
4260 | + return -EINTR; | |
4261 | + head->read_user_buf_avail = 0; | |
4262 | + idx = ccs_read_lock(); | |
4263 | + /* Read a line and dispatch it to the policy handler. */ | |
4264 | + while (count) { | |
4265 | + char c; | |
4266 | + if (head->w.avail >= head->writebuf_size - 1) { | |
4267 | + const int len = head->writebuf_size * 2; | |
4268 | + char *cp = kzalloc(len, GFP_NOFS); | |
4269 | + if (!cp) { | |
4270 | + error = -ENOMEM; | |
4271 | + break; | |
4272 | + } | |
4273 | + memmove(cp, cp0, head->w.avail); | |
4274 | + kfree(cp0); | |
4275 | + head->write_buf = cp; | |
4276 | + cp0 = cp; | |
4277 | + head->writebuf_size = len; | |
4278 | + } | |
4279 | + if (get_user(c, buf)) { | |
4280 | + error = -EFAULT; | |
4281 | + break; | |
4282 | + } | |
4283 | + buf++; | |
4284 | + count--; | |
4285 | + cp0[head->w.avail++] = c; | |
4286 | + if (c != '\n') | |
4287 | + continue; | |
4288 | + cp0[head->w.avail - 1] = '\0'; | |
4289 | + head->w.avail = 0; | |
4290 | + ccs_normalize_line(cp0); | |
4291 | + /* Don't allow updating policies by non manager programs. */ | |
4292 | + if (head->type != CCS_PROCESS_STATUS && !ccs_manager()) { | |
4293 | + error = -EPERM; | |
4294 | + goto out; | |
4295 | + } | |
4296 | + switch (ccs_parse_policy(head, cp0)) { | |
4297 | + case -EPERM: | |
4298 | + error = -EPERM; | |
4299 | + goto out; | |
4300 | + case 0: | |
4301 | + /* Update statistics. */ | |
4302 | + if (head->type == CCS_POLICY) | |
4303 | + ccs_update_stat(CCS_STAT_POLICY_UPDATES); | |
4304 | + break; | |
4305 | + } | |
4306 | + } | |
4307 | +out: | |
4308 | + ccs_read_unlock(idx); | |
4309 | + mutex_unlock(&head->io_sem); | |
4310 | + return error; | |
4311 | +} | |
4312 | + | |
4313 | +/** | |
4314 | + * ccs_create_entry - Create interface files under /proc/caitsith/ directory. | |
4315 | + * | |
4316 | + * @name: The name of the interface file. | |
4317 | + * @mode: The permission of the interface file. | |
4318 | + * @parent: The parent directory. | |
4319 | + * @key: Type of interface. | |
4320 | + * | |
4321 | + * Returns nothing. | |
4322 | + */ | |
4323 | +static void __init ccs_create_entry(const char *name, const umode_t mode, | |
4324 | + struct proc_dir_entry *parent, | |
4325 | + const u8 key) | |
4326 | +{ | |
4327 | + struct proc_dir_entry *entry = create_proc_entry(name, mode, parent); | |
4328 | + if (entry) { | |
4329 | + entry->proc_fops = &ccs_operations; | |
4330 | + entry->data = ((u8 *) NULL) + key; | |
4331 | + } | |
4332 | +} | |
4333 | + | |
4334 | +/** | |
4335 | + * ccs_proc_init - Initialize /proc/caitsith/ interface. | |
4336 | + * | |
4337 | + * Returns nothing. | |
4338 | + */ | |
4339 | +static void __init ccs_proc_init(void) | |
4340 | +{ | |
4341 | + struct proc_dir_entry *ccs_dir = proc_mkdir("caitsith", NULL); | |
4342 | + ccs_create_entry("query", 0600, ccs_dir, CCS_QUERY); | |
4343 | + ccs_create_entry("audit", 0400, ccs_dir, CCS_AUDIT); | |
4344 | + ccs_create_entry(".process_status", 0600, ccs_dir, | |
4345 | + CCS_PROCESS_STATUS); | |
4346 | + ccs_create_entry("version", 0400, ccs_dir, CCS_VERSION); | |
4347 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
4348 | + ccs_create_entry(".execute_handler", 0666, ccs_dir, | |
4349 | + CCS_EXECUTE_HANDLER); | |
4350 | +#endif | |
4351 | + ccs_create_entry("policy", 0600, ccs_dir, CCS_POLICY); | |
4352 | + { | |
4353 | + struct proc_dir_entry *e = create_proc_entry("self_domain", | |
4354 | + 0666, ccs_dir); | |
4355 | + if (e) | |
4356 | + e->proc_fops = &ccs_self_operations; | |
4357 | + } | |
4358 | +} | |
4359 | + | |
4360 | +/** | |
4361 | + * ccs_init_module - Initialize this module. | |
4362 | + * | |
4363 | + * Returns 0 on success, negative value otherwise. | |
4364 | + */ | |
4365 | +static int __init ccs_init_module(void) | |
4366 | +{ | |
4367 | + u16 idx; | |
4368 | + if (ccsecurity_ops.disabled) | |
4369 | + return -EINVAL; | |
4370 | +#ifdef DEBUG_CONDITION | |
4371 | + for (idx = 0; idx < CCS_MAX_MAC_INDEX; idx++) { | |
4372 | + if (ccs_mac_keywords[idx]) | |
4373 | + continue; | |
4374 | + printk(KERN_INFO "ccs_mac_keywords[%u]==NULL\n", idx); | |
4375 | + return -EINVAL; | |
4376 | + } | |
4377 | +#endif | |
4378 | + if (init_srcu_struct(&ccs_ss)) | |
4379 | + panic("Out of memory."); | |
4380 | + for (idx = 0; idx < CCS_MAX_MAC_INDEX; idx++) | |
4381 | + INIT_LIST_HEAD(&ccs_acl_list[idx]); | |
4382 | + for (idx = 0; idx < CCS_MAX_GROUP; idx++) | |
4383 | + INIT_LIST_HEAD(&ccs_group_list[idx]); | |
4384 | + for (idx = 0; idx < CCS_MAX_HASH; idx++) | |
4385 | + INIT_LIST_HEAD(&ccs_name_list[idx]); | |
4386 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
4387 | + ccs_mm_init(); | |
4388 | +#endif | |
4389 | + ccs_null_name.name = "NULL"; | |
4390 | + ccs_fill_path_info(&ccs_null_name); | |
4391 | + ccs_kernel_domain.domainname = ccs_get_name("<kernel>"); | |
4392 | + list_add_tail_rcu(&ccs_kernel_domain.list, &ccs_domain_list); | |
4393 | + ccs_policy_io_init(); | |
4394 | + ccs_permission_init(); | |
4395 | + ccs_proc_init(); | |
4396 | + ccs_load_builtin_policy(); | |
4397 | + return 0; | |
4398 | +} | |
4399 | + | |
4400 | +MODULE_LICENSE("GPL"); | |
4401 | +module_init(ccs_init_module); |
@@ -0,0 +1,179 @@ | ||
1 | +/* | |
2 | + * security/ccsecurity/memory.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +#ifdef CONFIG_CCSECURITY_USE_EXTERNAL_TASK_SECURITY | |
12 | + | |
13 | +/***** SECTION1: Constants definition *****/ | |
14 | + | |
15 | +/***** SECTION2: Structure definition *****/ | |
16 | + | |
17 | +/***** SECTION3: Prototype definition section *****/ | |
18 | + | |
19 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task); | |
20 | +void __init ccs_mm_init(void); | |
21 | + | |
22 | +static int __ccs_alloc_task_security(const struct task_struct *task); | |
23 | +static void __ccs_free_task_security(const struct task_struct *task); | |
24 | +static void ccs_add_task_security(struct ccs_security *ptr, | |
25 | + struct list_head *list); | |
26 | +static void ccs_rcu_free(struct rcu_head *rcu); | |
27 | + | |
28 | +/***** SECTION4: Standalone functions section *****/ | |
29 | + | |
30 | +/***** SECTION5: Variables definition section *****/ | |
31 | + | |
32 | +/* Dummy security context for avoiding NULL pointer dereference. */ | |
33 | +static struct ccs_security ccs_oom_security = { | |
34 | + .ccs_domain_info = &ccs_kernel_domain | |
35 | +}; | |
36 | + | |
37 | +/* Dummy security context for avoiding NULL pointer dereference. */ | |
38 | +static struct ccs_security ccs_default_security = { | |
39 | + .ccs_domain_info = &ccs_kernel_domain | |
40 | +}; | |
41 | + | |
42 | +/* List of "struct ccs_security". */ | |
43 | +struct list_head ccs_task_security_list[CCS_MAX_TASK_SECURITY_HASH]; | |
44 | +/* Lock for protecting ccs_task_security_list[]. */ | |
45 | +static DEFINE_SPINLOCK(ccs_task_security_list_lock); | |
46 | + | |
47 | +/***** SECTION6: Dependent functions section *****/ | |
48 | + | |
49 | +/** | |
50 | + * ccs_add_task_security - Add "struct ccs_security" to list. | |
51 | + * | |
52 | + * @ptr: Pointer to "struct ccs_security". | |
53 | + * @list: Pointer to "struct list_head". | |
54 | + * | |
55 | + * Returns nothing. | |
56 | + */ | |
57 | +static void ccs_add_task_security(struct ccs_security *ptr, | |
58 | + struct list_head *list) | |
59 | +{ | |
60 | + unsigned long flags; | |
61 | + spin_lock_irqsave(&ccs_task_security_list_lock, flags); | |
62 | + list_add_rcu(&ptr->list, list); | |
63 | + spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | |
64 | +} | |
65 | + | |
66 | +/** | |
67 | + * __ccs_alloc_task_security - Allocate memory for new tasks. | |
68 | + * | |
69 | + * @task: Pointer to "struct task_struct". | |
70 | + * | |
71 | + * Returns 0 on success, negative value otherwise. | |
72 | + */ | |
73 | +static int __ccs_alloc_task_security(const struct task_struct *task) | |
74 | +{ | |
75 | + struct ccs_security *old_security = ccs_current_security(); | |
76 | + struct ccs_security *new_security = kzalloc(sizeof(*new_security), | |
77 | + GFP_KERNEL); | |
78 | + struct list_head *list = &ccs_task_security_list | |
79 | + [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | |
80 | + if (!new_security) | |
81 | + return -ENOMEM; | |
82 | + *new_security = *old_security; | |
83 | + new_security->task = task; | |
84 | + ccs_add_task_security(new_security, list); | |
85 | + return 0; | |
86 | +} | |
87 | + | |
88 | +/** | |
89 | + * ccs_find_task_security - Find "struct ccs_security" for given task. | |
90 | + * | |
91 | + * @task: Pointer to "struct task_struct". | |
92 | + * | |
93 | + * Returns pointer to "struct ccs_security" on success, &ccs_oom_security on | |
94 | + * out of memory, &ccs_default_security otherwise. | |
95 | + * | |
96 | + * If @task is current thread and "struct ccs_security" for current thread was | |
97 | + * not found, I try to allocate it. But if allocation failed, current thread | |
98 | + * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL | |
99 | + * won't work. | |
100 | + */ | |
101 | +struct ccs_security *ccs_find_task_security(const struct task_struct *task) | |
102 | +{ | |
103 | + struct ccs_security *ptr; | |
104 | + struct list_head *list = &ccs_task_security_list | |
105 | + [hash_ptr((void *) task, CCS_TASK_SECURITY_HASH_BITS)]; | |
106 | + /* Make sure INIT_LIST_HEAD() in ccs_mm_init() takes effect. */ | |
107 | + while (!list->next); | |
108 | + rcu_read_lock(); | |
109 | + list_for_each_entry_rcu(ptr, list, list) { | |
110 | + if (ptr->task != task) | |
111 | + continue; | |
112 | + rcu_read_unlock(); | |
113 | + return ptr; | |
114 | + } | |
115 | + rcu_read_unlock(); | |
116 | + if (task != current) | |
117 | + return &ccs_default_security; | |
118 | + /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ | |
119 | + ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); | |
120 | + if (!ptr) { | |
121 | + printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", | |
122 | + task->pid); | |
123 | + send_sig(SIGKILL, current, 0); | |
124 | + return &ccs_oom_security; | |
125 | + } | |
126 | + *ptr = ccs_default_security; | |
127 | + ptr->task = task; | |
128 | + ccs_add_task_security(ptr, list); | |
129 | + return ptr; | |
130 | +} | |
131 | + | |
132 | +/** | |
133 | + * ccs_rcu_free - RCU callback for releasing "struct ccs_security". | |
134 | + * | |
135 | + * @rcu: Pointer to "struct rcu_head". | |
136 | + * | |
137 | + * Returns nothing. | |
138 | + */ | |
139 | +static void ccs_rcu_free(struct rcu_head *rcu) | |
140 | +{ | |
141 | + struct ccs_security *ptr = container_of(rcu, typeof(*ptr), rcu); | |
142 | + kfree(ptr); | |
143 | +} | |
144 | + | |
145 | +/** | |
146 | + * __ccs_free_task_security - Release memory associated with "struct task_struct". | |
147 | + * | |
148 | + * @task: Pointer to "struct task_struct". | |
149 | + * | |
150 | + * Returns nothing. | |
151 | + */ | |
152 | +static void __ccs_free_task_security(const struct task_struct *task) | |
153 | +{ | |
154 | + unsigned long flags; | |
155 | + struct ccs_security *ptr = ccs_find_task_security(task); | |
156 | + if (ptr == &ccs_default_security || ptr == &ccs_oom_security) | |
157 | + return; | |
158 | + spin_lock_irqsave(&ccs_task_security_list_lock, flags); | |
159 | + list_del_rcu(&ptr->list); | |
160 | + spin_unlock_irqrestore(&ccs_task_security_list_lock, flags); | |
161 | + call_rcu(&ptr->rcu, ccs_rcu_free); | |
162 | +} | |
163 | + | |
164 | +/** | |
165 | + * ccs_mm_init - Initialize mm related code. | |
166 | + * | |
167 | + * Returns nothing. | |
168 | + */ | |
169 | +void __init ccs_mm_init(void) | |
170 | +{ | |
171 | + int idx; | |
172 | + for (idx = 0; idx < CCS_MAX_TASK_SECURITY_HASH; idx++) | |
173 | + INIT_LIST_HEAD(&ccs_task_security_list[idx]); | |
174 | + smp_wmb(); /* Avoid out of order execution. */ | |
175 | + ccsecurity_ops.alloc_task_security = __ccs_alloc_task_security; | |
176 | + ccsecurity_ops.free_task_security = __ccs_free_task_security; | |
177 | +} | |
178 | + | |
179 | +#endif |
@@ -0,0 +1,3752 @@ | ||
1 | +/* | |
2 | + * security/ccsecurity/permission.c | |
3 | + * | |
4 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
5 | + * | |
6 | + * Version: 0.1 2012/04/01 | |
7 | + */ | |
8 | + | |
9 | +#include "internal.h" | |
10 | + | |
11 | +/***** SECTION1: Constants definition *****/ | |
12 | + | |
13 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
14 | + | |
15 | +/* | |
16 | + * may_open() receives open flags modified by open_to_namei_flags() until | |
17 | + * 2.6.32. We stop here in case some distributions backported ACC_MODE changes, | |
18 | + * for we can't determine whether may_open() receives open flags modified by | |
19 | + * open_to_namei_flags() or not. | |
20 | + */ | |
21 | +#ifdef ACC_MODE | |
22 | +#error ACC_MODE already defined. | |
23 | +#endif | |
24 | +#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) | |
25 | + | |
26 | +#if defined(RHEL_MAJOR) && RHEL_MAJOR == 6 | |
27 | +/* RHEL6 passes unmodified flags since 2.6.32-71.14.1.el6 . */ | |
28 | +#undef ACC_MODE | |
29 | +#define ACC_MODE(x) ("\004\002\006"[(x)&O_ACCMODE]) | |
30 | +#endif | |
31 | + | |
32 | +#endif | |
33 | + | |
34 | +/* String table for special mount operations. */ | |
35 | +static const char * const ccs_mounts[CCS_MAX_SPECIAL_MOUNT] = { | |
36 | + [CCS_MOUNT_BIND] = "--bind", | |
37 | + [CCS_MOUNT_MOVE] = "--move", | |
38 | + [CCS_MOUNT_REMOUNT] = "--remount", | |
39 | + [CCS_MOUNT_MAKE_UNBINDABLE] = "--make-unbindable", | |
40 | + [CCS_MOUNT_MAKE_PRIVATE] = "--make-private", | |
41 | + [CCS_MOUNT_MAKE_SLAVE] = "--make-slave", | |
42 | + [CCS_MOUNT_MAKE_SHARED] = "--make-shared", | |
43 | +}; | |
44 | + | |
45 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
46 | + | |
47 | +/* | |
48 | + * Mapping table from "enum ccs_capability_acl_index" to "enum ccs_mac_index". | |
49 | + */ | |
50 | +static const u8 ccs_c2mac[CCS_MAX_CAPABILITY_INDEX] = { | |
51 | + [CCS_USE_ROUTE_SOCKET] = CCS_MAC_USE_NETLINK_SOCKET, | |
52 | + [CCS_USE_PACKET_SOCKET] = CCS_MAC_USE_PACKET_SOCKET, | |
53 | + [CCS_SYS_REBOOT] = CCS_MAC_USE_REBOOT, | |
54 | + [CCS_SYS_VHANGUP] = CCS_MAC_USE_VHANGUP, | |
55 | + [CCS_SYS_SETTIME] = CCS_MAC_SET_TIME, | |
56 | + [CCS_SYS_NICE] = CCS_MAC_SET_PRIORITY, | |
57 | + [CCS_SYS_SETHOSTNAME] = CCS_MAC_SET_HOSTNAME, | |
58 | + [CCS_USE_KERNEL_MODULE] = CCS_MAC_USE_KERNEL_MODULE, | |
59 | + [CCS_SYS_KEXEC_LOAD] = CCS_MAC_USE_NEW_KERNEL, | |
60 | +}; | |
61 | + | |
62 | +#endif | |
63 | + | |
64 | +/* Type of condition argument. */ | |
65 | +enum ccs_arg_type { | |
66 | + CCS_ARG_TYPE_NONE, | |
67 | + CCS_ARG_TYPE_NUMBER, | |
68 | + CCS_ARG_TYPE_NAME, | |
69 | + CCS_ARG_TYPE_GROUP, | |
70 | + CCS_ARG_TYPE_BITOP, | |
71 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
72 | + CCS_ARG_TYPE_IPV4ADDR, | |
73 | + CCS_ARG_TYPE_IPV6ADDR, | |
74 | +#endif | |
75 | +} __packed; | |
76 | + | |
77 | +/***** SECTION2: Structure definition *****/ | |
78 | + | |
79 | +/* Structure for holding inet domain socket's address. */ | |
80 | +struct ccs_inet_addr_info { | |
81 | + u16 port; /* In network byte order. */ | |
82 | + const u8 *address; /* In network byte order. */ | |
83 | + bool is_ipv6; | |
84 | +}; | |
85 | + | |
86 | +/* Structure for holding unix domain socket's address. */ | |
87 | +struct ccs_unix_addr_info { | |
88 | + u8 *addr; /* This may not be '\0' terminated string. */ | |
89 | + unsigned int addr_len; | |
90 | +}; | |
91 | + | |
92 | +/* Structure for holding socket address. */ | |
93 | +struct ccs_addr_info { | |
94 | + u8 operation; | |
95 | + struct ccs_inet_addr_info inet; | |
96 | + struct ccs_unix_addr_info unix0; | |
97 | +}; | |
98 | + | |
99 | +/* Structure for holding single condition component. */ | |
100 | +struct ccs_cond_arg { | |
101 | + enum ccs_arg_type type; | |
102 | + unsigned long value[2]; | |
103 | + const struct ccs_path_info *name; | |
104 | + const struct ccs_group *group; | |
105 | + struct in6_addr ip[2]; | |
106 | +}; | |
107 | + | |
108 | +/***** SECTION3: Prototype definition section *****/ | |
109 | + | |
110 | +static bool ccs_alphabet_char(const char c); | |
111 | +static bool ccs_byte_range(const char *str); | |
112 | +static bool ccs_check_entry(struct ccs_request_info *r, | |
113 | + const struct ccs_acl_info *ptr); | |
114 | +static bool ccs_condition(struct ccs_request_info *r, | |
115 | + const struct ccs_condition *cond); | |
116 | +static bool ccs_decimal(const char c); | |
117 | +static bool ccs_file_matches_pattern(const char *filename, | |
118 | + const char *filename_end, | |
119 | + const char *pattern, | |
120 | + const char *pattern_end); | |
121 | +static bool ccs_file_matches_pattern2(const char *filename, | |
122 | + const char *filename_end, | |
123 | + const char *pattern, | |
124 | + const char *pattern_end); | |
125 | +static bool ccs_hexadecimal(const char c); | |
126 | +static bool ccs_number_matches_group(const unsigned long min, | |
127 | + const unsigned long max, | |
128 | + const struct ccs_group *group); | |
129 | +static bool ccs_path_matches_pattern(const struct ccs_path_info *filename, | |
130 | + const struct ccs_path_info *pattern); | |
131 | +static bool ccs_path_matches_pattern2(const char *f, const char *p); | |
132 | +static bool ccs_path_matches_group(const struct ccs_path_info *pathname, | |
133 | + const struct ccs_group *group); | |
134 | +static int __ccs_chmod_permission(struct dentry *dentry, | |
135 | + struct vfsmount *vfsmnt, mode_t mode); | |
136 | +static int __ccs_chown_permission(struct dentry *dentry, | |
137 | + struct vfsmount *vfsmnt, uid_t user, | |
138 | + gid_t group); | |
139 | +static int __ccs_chroot_permission(struct path *path); | |
140 | +static int __ccs_fcntl_permission(struct file *file, unsigned int cmd, | |
141 | + unsigned long arg); | |
142 | +static int __ccs_ioctl_permission(struct file *filp, unsigned int cmd, | |
143 | + unsigned long arg); | |
144 | +static int __ccs_link_permission(struct dentry *old_dentry, | |
145 | + struct dentry *new_dentry, | |
146 | + struct vfsmount *mnt); | |
147 | +static int __ccs_mkdir_permission(struct dentry *dentry, struct vfsmount *mnt, | |
148 | + unsigned int mode); | |
149 | +static int __ccs_mknod_permission(struct dentry *dentry, struct vfsmount *mnt, | |
150 | + const unsigned int mode, unsigned int dev); | |
151 | +static int __ccs_mount_permission(char *dev_name, struct path *path, | |
152 | + const char *type, unsigned long flags, | |
153 | + void *data_page); | |
154 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
155 | +static int __ccs_open_exec_permission(struct dentry *dentry, | |
156 | + struct vfsmount *mnt); | |
157 | +#endif | |
158 | +static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | |
159 | + const int flag); | |
160 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
161 | +static int ccs_sysctl_permission(enum ccs_mac_index type, | |
162 | + const struct ccs_path_info *filename); | |
163 | +static int __ccs_parse_table(int __user *name, int nlen, void __user *oldval, | |
164 | + void __user *newval, struct ctl_table *table); | |
165 | +#endif | |
166 | +static int __ccs_pivot_root_permission(struct path *old_path, | |
167 | + struct path *new_path); | |
168 | +static int __ccs_rename_permission(struct dentry *old_dentry, | |
169 | + struct dentry *new_dentry, | |
170 | + struct vfsmount *mnt); | |
171 | +static int __ccs_rmdir_permission(struct dentry *dentry, struct vfsmount *mnt); | |
172 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
173 | + struct pt_regs *regs); | |
174 | +static int __ccs_symlink_permission(struct dentry *dentry, | |
175 | + struct vfsmount *mnt, const char *from); | |
176 | +static int __ccs_truncate_permission(struct dentry *dentry, | |
177 | + struct vfsmount *mnt); | |
178 | +static int __ccs_umount_permission(struct vfsmount *mnt, int flags); | |
179 | +static int __ccs_unlink_permission(struct dentry *dentry, | |
180 | + struct vfsmount *mnt); | |
181 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
182 | +static int __ccs_uselib_permission(struct dentry *dentry, | |
183 | + struct vfsmount *mnt); | |
184 | +#endif | |
185 | +static int ccs_execute_path(struct linux_binprm *bprm, struct path *path); | |
186 | +static int ccs_execute(struct ccs_request_info *r); | |
187 | +static int ccs_kern_path(const char *pathname, int flags, struct path *path); | |
188 | +static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | |
189 | + struct vfsmount *mnt, const unsigned int mode, | |
190 | + unsigned int dev); | |
191 | +static int ccs_mount_acl(const char *dev_name, struct path *dir, | |
192 | + const char *type, unsigned long flags, | |
193 | + const char *data); | |
194 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
195 | +static int ccs_new_open_permission(struct file *filp); | |
196 | +#endif | |
197 | +static int ccs_path2_perm(const enum ccs_mac_index operation, | |
198 | + struct dentry *dentry1, struct vfsmount *mnt1, | |
199 | + struct dentry *dentry2, struct vfsmount *mnt2); | |
200 | +static int ccs_path_number_perm(const enum ccs_mac_index type, | |
201 | + struct dentry *dentry, struct vfsmount *vfsmnt, | |
202 | + unsigned long number); | |
203 | +static int ccs_path_perm(const enum ccs_mac_index operation, | |
204 | + struct dentry *dentry, struct vfsmount *mnt); | |
205 | +static int ccs_start_execve(struct linux_binprm *bprm, | |
206 | + struct ccs_request_info **rp); | |
207 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
208 | +static void __ccs_clear_open_mode(void); | |
209 | +static void __ccs_save_open_mode(int mode); | |
210 | +#endif | |
211 | +static void ccs_check_auto_domain_transition(void); | |
212 | +static void ccs_clear_request_info(struct ccs_request_info *r); | |
213 | +static void ccs_finish_execve(int retval, struct ccs_request_info *r); | |
214 | + | |
215 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
216 | +static int ccs_env_perm(struct ccs_request_info *r, const char *name, | |
217 | + const char *value); | |
218 | +static int ccs_environ(struct ccs_request_info *r); | |
219 | +#endif | |
220 | + | |
221 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
222 | +static bool __ccs_capable(const u8 operation); | |
223 | +static bool ccs_kernel_service(void); | |
224 | +static int __ccs_socket_create_permission(int family, int type, int protocol); | |
225 | +#endif | |
226 | + | |
227 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
228 | +static bool ccs_ip_matches_group(const bool is_ipv6, const u8 *address, | |
229 | + const struct ccs_group *group); | |
230 | +static bool ccs_kernel_service(void); | |
231 | +static int __ccs_socket_bind_permission(struct socket *sock, | |
232 | + struct sockaddr *addr, int addr_len); | |
233 | +static int __ccs_socket_connect_permission(struct socket *sock, | |
234 | + struct sockaddr *addr, | |
235 | + int addr_len); | |
236 | +static int __ccs_socket_listen_permission(struct socket *sock); | |
237 | +static int __ccs_socket_post_accept_permission(struct socket *sock, | |
238 | + struct socket *newsock); | |
239 | +static int __ccs_socket_sendmsg_permission(struct socket *sock, | |
240 | + struct msghdr *msg, int size); | |
241 | +static int ccs_check_inet_address(const struct sockaddr *addr, | |
242 | + const unsigned int addr_len, const u16 port, | |
243 | + struct ccs_addr_info *address); | |
244 | +static int ccs_check_unix_address(struct sockaddr *addr, | |
245 | + const unsigned int addr_len, | |
246 | + struct ccs_addr_info *address); | |
247 | +static int ccs_inet_entry(const struct ccs_addr_info *address); | |
248 | +static int ccs_unix_entry(const struct ccs_addr_info *address); | |
249 | +static u8 ccs_sock_family(struct sock *sk); | |
250 | +#endif | |
251 | + | |
252 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
253 | +static int __ccs_socket_post_recvmsg_permission(struct sock *sk, | |
254 | + struct sk_buff *skb, | |
255 | + int flags); | |
256 | +#endif | |
257 | + | |
258 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
259 | +static int __ccs_ptrace_permission(long request, long pid); | |
260 | +#endif | |
261 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
262 | +static int __ccs_signal_permission(const int sig); | |
263 | +static int ccs_signal_permission0(const int pid, const int sig); | |
264 | +static int ccs_signal_permission1(pid_t tgid, pid_t pid, int sig); | |
265 | +#endif | |
266 | + | |
267 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
268 | +static int __ccs_getattr_permission(struct vfsmount *mnt, | |
269 | + struct dentry *dentry); | |
270 | +#endif | |
271 | + | |
272 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
273 | +static int ccs_try_alt_exec(struct ccs_request_info *r); | |
274 | +static void ccs_unescape(unsigned char *dest); | |
275 | +#endif | |
276 | + | |
277 | +/***** SECTION4: Standalone functions section *****/ | |
278 | + | |
279 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
280 | + | |
281 | +/** | |
282 | + * ccs_copy_argv - Wrapper for copy_strings_kernel(). | |
283 | + * | |
284 | + * @arg: String to copy. | |
285 | + * @bprm: Pointer to "struct linux_binprm". | |
286 | + * | |
287 | + * Returns return value of copy_strings_kernel(). | |
288 | + */ | |
289 | +static inline int ccs_copy_argv(const char *arg, struct linux_binprm *bprm) | |
290 | +{ | |
291 | + const int ret = copy_strings_kernel(1, &arg, bprm); | |
292 | + if (ret >= 0) | |
293 | + bprm->argc++; | |
294 | + return ret; | |
295 | +} | |
296 | + | |
297 | +#else | |
298 | + | |
299 | +/** | |
300 | + * ccs_copy_argv - Wrapper for copy_strings_kernel(). | |
301 | + * | |
302 | + * @arg: String to copy. | |
303 | + * @bprm: Pointer to "struct linux_binprm". | |
304 | + * | |
305 | + * Returns return value of copy_strings_kernel(). | |
306 | + */ | |
307 | +static inline int ccs_copy_argv(char *arg, struct linux_binprm *bprm) | |
308 | +{ | |
309 | + const int ret = copy_strings_kernel(1, &arg, bprm); | |
310 | + if (ret >= 0) | |
311 | + bprm->argc++; | |
312 | + return ret; | |
313 | +} | |
314 | + | |
315 | +#endif | |
316 | + | |
317 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35) | |
318 | + | |
319 | +/** | |
320 | + * get_fs_root - Get reference on root directory. | |
321 | + * | |
322 | + * @fs: Pointer to "struct fs_struct". | |
323 | + * @root: Pointer to "struct path". | |
324 | + * | |
325 | + * Returns nothing. | |
326 | + * | |
327 | + * This is for compatibility with older kernels. | |
328 | + */ | |
329 | +static inline void get_fs_root(struct fs_struct *fs, struct path *root) | |
330 | +{ | |
331 | + read_lock(&fs->lock); | |
332 | + *root = fs->root; | |
333 | + path_get(root); | |
334 | + read_unlock(&fs->lock); | |
335 | +} | |
336 | + | |
337 | +#endif | |
338 | + | |
339 | +/** | |
340 | + * ccs_put_filesystem - Wrapper for put_filesystem(). | |
341 | + * | |
342 | + * @fstype: Pointer to "struct file_system_type". | |
343 | + * | |
344 | + * Returns nothing. | |
345 | + * | |
346 | + * Since put_filesystem() is not exported, I embed put_filesystem() here. | |
347 | + */ | |
348 | +static inline void ccs_put_filesystem(struct file_system_type *fstype) | |
349 | +{ | |
350 | + module_put(fstype->owner); | |
351 | +} | |
352 | + | |
353 | +/***** SECTION5: Variables definition section *****/ | |
354 | + | |
355 | +/* The initial domain. */ | |
356 | +struct ccs_domain_info ccs_kernel_domain; | |
357 | + | |
358 | +/* The list for "struct ccs_domain_info". */ | |
359 | +LIST_HEAD(ccs_domain_list); | |
360 | + | |
361 | +/* The list for ACL policy. */ | |
362 | +struct list_head ccs_acl_list[CCS_MAX_MAC_INDEX]; | |
363 | + | |
364 | +/* NULL value. */ | |
365 | +struct ccs_path_info ccs_null_name; | |
366 | + | |
367 | +/***** SECTION6: Dependent functions section *****/ | |
368 | + | |
369 | +/** | |
370 | + * ccs_path_matches_group - Check whether the given pathname matches members of the given pathname group. | |
371 | + * | |
372 | + * @pathname: The name of pathname. | |
373 | + * @group: Pointer to "struct ccs_string_group". | |
374 | + * | |
375 | + * Returns true if @pathname matches pathnames in @group, false otherwise. | |
376 | + * | |
377 | + * Caller holds ccs_read_lock(). | |
378 | + */ | |
379 | +static bool ccs_path_matches_group(const struct ccs_path_info *pathname, | |
380 | + const struct ccs_group *group) | |
381 | +{ | |
382 | + struct ccs_string_group *member; | |
383 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | |
384 | + &ccs_ss) { | |
385 | + if (member->head.is_deleted) | |
386 | + continue; | |
387 | + if (!ccs_path_matches_pattern(pathname, member->member_name)) | |
388 | + continue; | |
389 | + return true; | |
390 | + } | |
391 | + return false; | |
392 | +} | |
393 | + | |
394 | +/** | |
395 | + * ccs_number_matches_group - Check whether the given number matches members of the given number group. | |
396 | + * | |
397 | + * @min: Min number. | |
398 | + * @max: Max number. | |
399 | + * @group: Pointer to "struct ccs_number_group". | |
400 | + * | |
401 | + * Returns true if @min and @max partially overlaps @group, false otherwise. | |
402 | + * | |
403 | + * Caller holds ccs_read_lock(). | |
404 | + */ | |
405 | +static bool ccs_number_matches_group(const unsigned long min, | |
406 | + const unsigned long max, | |
407 | + const struct ccs_group *group) | |
408 | +{ | |
409 | + struct ccs_number_group *member; | |
410 | + bool matched = false; | |
411 | + list_for_each_entry_srcu(member, &group->member_list, head.list, | |
412 | + &ccs_ss) { | |
413 | + if (member->head.is_deleted) | |
414 | + continue; | |
415 | + if (min > member->value[1] || max < member->value[0]) | |
416 | + continue; | |
417 | + matched = true; | |
418 | + break; | |
419 | + } | |
420 | + return matched; | |
421 | +} | |
422 | + | |
423 | +/** | |
424 | + * ccs_check_entry - Do permission check. | |
425 | + * | |
426 | + * @r: Pointer to "struct ccs_request_info". | |
427 | + * @ptr: Pointer to "struct ccs_acl_info". | |
428 | + * | |
429 | + * Returns true on match, false otherwise. | |
430 | + * | |
431 | + * Caller holds ccs_read_lock(). | |
432 | + */ | |
433 | +static bool ccs_check_entry(struct ccs_request_info *r, | |
434 | + const struct ccs_acl_info *ptr) | |
435 | +{ | |
436 | + return !ptr->is_deleted && ccs_condition(r, ptr->cond); | |
437 | +} | |
438 | + | |
439 | +/** | |
440 | + * ccs_check_acl_list - Do permission check. | |
441 | + * | |
442 | + * @r: Pointer to "struct ccs_request_info". | |
443 | + * | |
444 | + * Returns 0 on success, negative value otherwise. | |
445 | + * | |
446 | + * Caller holds ccs_read_lock(). | |
447 | + */ | |
448 | +static int ccs_check_acl_list(struct ccs_request_info *r) | |
449 | +{ | |
450 | + struct ccs_acl_info *ptr; | |
451 | + int error = 0; | |
452 | + struct list_head * const list = &ccs_acl_list[r->type]; | |
453 | + r->matched_acl = NULL; | |
454 | + list_for_each_entry_srcu(ptr, list, list, &ccs_ss) { | |
455 | + struct ccs_acl_info *ptr2; | |
456 | +retry: | |
457 | + if (!ccs_check_entry(r, ptr)) { | |
458 | + if (unlikely(r->failed_by_oom)) | |
459 | + goto oom; | |
460 | + continue; | |
461 | + } | |
462 | + r->matched_acl = ptr; | |
463 | + r->audit = ptr->audit; | |
464 | + r->result = CCS_MATCHING_UNMATCHED; | |
465 | + list_for_each_entry_srcu(ptr2, &ptr->acl_info_list, list, | |
466 | + &ccs_ss) { | |
467 | + r->transition_candidate = NULL; | |
468 | + r->handler_path_candidate = NULL; | |
469 | + if (!ccs_check_entry(r, ptr2)) { | |
470 | + if (unlikely(r->failed_by_oom)) | |
471 | + goto oom; | |
472 | + continue; | |
473 | + } | |
474 | + if (ptr2->is_deny) { | |
475 | + r->result = CCS_MATCHING_DENIED; | |
476 | + break; | |
477 | + } | |
478 | + r->result = CCS_MATCHING_ALLOWED; | |
479 | + /* Set the first matching domain transition entry. */ | |
480 | + if (r->transition_candidate && !r->transition) | |
481 | + r->transition = r->transition_candidate; | |
482 | + /* Set the first matching execute handler entry. */ | |
483 | + if (r->handler_path_candidate && !r->handler_path) | |
484 | + r->handler_path = r->handler_path_candidate; | |
485 | + break; | |
486 | + } | |
487 | + error = ccs_audit_log(r); | |
488 | + /* Ignore out of memory during audit. */ | |
489 | + r->failed_by_oom = false; | |
490 | + if (!error) | |
491 | + continue; | |
492 | + if (error == CCS_RETRY_REQUEST) | |
493 | + goto retry; | |
494 | + break; | |
495 | + } | |
496 | + return error; | |
497 | +oom: | |
498 | + /* | |
499 | + * If conditions could not be checked due to out of memory, | |
500 | + * reject the request with -ENOMEM, for we don't know whether | |
501 | + * there was a possibility of matching "deny" lines or not. | |
502 | + */ | |
503 | + { | |
504 | + static struct timeval ccs_last_tv; | |
505 | + struct timeval tv; | |
506 | + do_gettimeofday(&tv); | |
507 | + if (tv.tv_sec != ccs_last_tv.tv_sec) { | |
508 | + ccs_last_tv = tv; | |
509 | + printk(KERN_INFO "CaitSith: Rejecting access " | |
510 | + "request due to out of memory.\n"); | |
511 | + } | |
512 | + } | |
513 | + return -ENOMEM; | |
514 | +} | |
515 | + | |
516 | +/** | |
517 | + * ccs_check_acl - Do permission check. | |
518 | + * | |
519 | + * @r: Pointer to "struct ccs_request_info". | |
520 | + * @clear: True to cleanup @r before return, false otherwise. | |
521 | + * | |
522 | + * Returns 0 on success, negative value otherwise. | |
523 | + */ | |
524 | +int ccs_check_acl(struct ccs_request_info *r, const bool clear) | |
525 | +{ | |
526 | + int error; | |
527 | + const int idx = ccs_read_lock(); | |
528 | + error = ccs_check_acl_list(r); | |
529 | + ccs_read_unlock(idx); | |
530 | + if (clear) | |
531 | + ccs_clear_request_info(r); | |
532 | + return error; | |
533 | +} | |
534 | + | |
535 | +/** | |
536 | + * ccs_execute - Check permission for "execute". | |
537 | + * | |
538 | + * @r: Pointer to "struct ccs_request_info". | |
539 | + * | |
540 | + * Returns 0 on success, negative value otherwise. | |
541 | + * | |
542 | + * Caller holds ccs_read_lock(). | |
543 | + */ | |
544 | +static int ccs_execute(struct ccs_request_info *r) | |
545 | +{ | |
546 | + int retval; | |
547 | + | |
548 | + /* Get symlink's dentry/vfsmount. */ | |
549 | + retval = ccs_execute_path(r->bprm, &r->obj.path[1]); | |
550 | + if (retval < 0) | |
551 | + return retval; | |
552 | + ccs_populate_patharg(r, false); | |
553 | + if (!r->param.s[1]) | |
554 | + return -ENOMEM; | |
555 | + | |
556 | + /* Check execute permission. */ | |
557 | + r->type = CCS_MAC_EXECUTE; | |
558 | + retval = ccs_check_acl(r, false); | |
559 | + if (retval < 0) | |
560 | + return retval; | |
561 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
562 | + /* | |
563 | + * Switch to execute handler if matched. To avoid infinite execute | |
564 | + * handler loop, don't use execute handler if the current process is | |
565 | + * marked as execute handler. | |
566 | + */ | |
567 | + if (r->handler_path && r->handler_path != &ccs_null_name && | |
568 | + !(ccs_current_flags() & CCS_TASK_IS_EXECUTE_HANDLER)) { | |
569 | + retval = ccs_try_alt_exec(r); | |
570 | + if (retval < 0) | |
571 | + return retval; | |
572 | + } | |
573 | +#endif | |
574 | + /* | |
575 | + * Tell GC that I started execve(). | |
576 | + * Also, tell open_exec() to check read permission. | |
577 | + */ | |
578 | + ccs_current_security()->ccs_flags |= CCS_TASK_IS_IN_EXECVE; | |
579 | + if (!r->transition || r->transition == &ccs_null_name) | |
580 | + /* Keep current domain. */ | |
581 | + return 0; | |
582 | + /* | |
583 | + * Make ccs_current_security()->ccs_flags visible to GC before changing | |
584 | + * ccs_current_security()->ccs_domain_info. | |
585 | + */ | |
586 | + smp_wmb(); | |
587 | + /* | |
588 | + * Transit to the specified domain. | |
589 | + * It will be reverted if execve() failed. | |
590 | + */ | |
591 | + if (ccs_transit_domain(r->transition->name)) | |
592 | + return 0; | |
593 | + printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", | |
594 | + r->transition->name); | |
595 | + return -ENOMEM; | |
596 | +} | |
597 | + | |
598 | +#ifdef CONFIG_CCSECURITY_EXECUTE_HANDLER | |
599 | + | |
600 | +/** | |
601 | + * ccs_unescape - Unescape escaped string. | |
602 | + * | |
603 | + * @dest: String to unescape. | |
604 | + * | |
605 | + * Returns nothing. | |
606 | + */ | |
607 | +static void ccs_unescape(unsigned char *dest) | |
608 | +{ | |
609 | + unsigned char *src = dest; | |
610 | + unsigned char c; | |
611 | + unsigned char d; | |
612 | + unsigned char e; | |
613 | + while (1) { | |
614 | + c = *src++; | |
615 | + if (!c) | |
616 | + break; | |
617 | + if (c != '\\') { | |
618 | + *dest++ = c; | |
619 | + continue; | |
620 | + } | |
621 | + c = *src++; | |
622 | + if (c < '0' || c > '3') | |
623 | + break; | |
624 | + d = *src++; | |
625 | + if (d < '0' || d > '7') | |
626 | + break; | |
627 | + e = *src++; | |
628 | + if (e < '0' || e > '7') | |
629 | + break; | |
630 | + *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
631 | + } | |
632 | + *dest = '\0'; | |
633 | +} | |
634 | + | |
635 | +/** | |
636 | + * ccs_try_alt_exec - Try to start execute handler. | |
637 | + * | |
638 | + * @r: Pointer to "struct ccs_request_info". | |
639 | + * | |
640 | + * Returns 0 on success, negative value otherwise. | |
641 | + */ | |
642 | +static int ccs_try_alt_exec(struct ccs_request_info *r) | |
643 | +{ | |
644 | + /* | |
645 | + * Contents of modified bprm. | |
646 | + * The envp[] in original bprm is moved to argv[] so that | |
647 | + * the alternatively executed program won't be affected by | |
648 | + * some dangerous environment variables like LD_PRELOAD. | |
649 | + * | |
650 | + * modified bprm->argc | |
651 | + * = original bprm->argc + original bprm->envc + 7 | |
652 | + * modified bprm->envc | |
653 | + * = 0 | |
654 | + * | |
655 | + * modified bprm->argv[0] | |
656 | + * = the program's name specified by *_execute_handler | |
657 | + * modified bprm->argv[1] | |
658 | + * = ccs_current_domain()->domainname->name | |
659 | + * modified bprm->argv[2] | |
660 | + * = the current process's name | |
661 | + * modified bprm->argv[3] | |
662 | + * = the current process's information (e.g. uid/gid). | |
663 | + * modified bprm->argv[4] | |
664 | + * = original bprm->filename | |
665 | + * modified bprm->argv[5] | |
666 | + * = original bprm->argc in string expression | |
667 | + * modified bprm->argv[6] | |
668 | + * = original bprm->envc in string expression | |
669 | + * modified bprm->argv[7] | |
670 | + * = original bprm->argv[0] | |
671 | + * ... | |
672 | + * modified bprm->argv[bprm->argc + 6] | |
673 | + * = original bprm->argv[bprm->argc - 1] | |
674 | + * modified bprm->argv[bprm->argc + 7] | |
675 | + * = original bprm->envp[0] | |
676 | + * ... | |
677 | + * modified bprm->argv[bprm->envc + bprm->argc + 6] | |
678 | + * = original bprm->envp[bprm->envc - 1] | |
679 | + */ | |
680 | + struct linux_binprm *bprm = r->bprm; | |
681 | + struct file *filp; | |
682 | + int retval; | |
683 | + const int original_argc = bprm->argc; | |
684 | + const int original_envc = bprm->envc; | |
685 | + | |
686 | + ccs_clear_request_info(r); | |
687 | + | |
688 | + /* Close the requested program's dentry. */ | |
689 | + r->obj.path[0].dentry = NULL; | |
690 | + r->obj.path[0].mnt = NULL; | |
691 | + r->obj.validate_done = false; | |
692 | + allow_write_access(bprm->file); | |
693 | + fput(bprm->file); | |
694 | + bprm->file = NULL; | |
695 | + | |
696 | + /* Invalidate page dump cache. */ | |
697 | + r->dump.page = NULL; | |
698 | + | |
699 | + /* Move envp[] to argv[] */ | |
700 | + bprm->argc += bprm->envc; | |
701 | + bprm->envc = 0; | |
702 | + | |
703 | + /* Set argv[6] */ | |
704 | + { | |
705 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_envc); | |
706 | + retval = ccs_copy_argv(r->tmp, bprm); | |
707 | + if (retval < 0) | |
708 | + goto out; | |
709 | + } | |
710 | + | |
711 | + /* Set argv[5] */ | |
712 | + { | |
713 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%d", original_argc); | |
714 | + retval = ccs_copy_argv(r->tmp, bprm); | |
715 | + if (retval < 0) | |
716 | + goto out; | |
717 | + } | |
718 | + | |
719 | + /* Set argv[4] */ | |
720 | + { | |
721 | + retval = ccs_copy_argv(bprm->filename, bprm); | |
722 | + if (retval < 0) | |
723 | + goto out; | |
724 | + } | |
725 | + | |
726 | + /* Set argv[3] */ | |
727 | + { | |
728 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, | |
729 | + "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d " | |
730 | + "sgid=%d fsuid=%d fsgid=%d", ccs_sys_getpid(), | |
731 | + current_uid(), current_gid(), current_euid(), | |
732 | + current_egid(), current_suid(), current_sgid(), | |
733 | + current_fsuid(), current_fsgid()); | |
734 | + retval = ccs_copy_argv(r->tmp, bprm); | |
735 | + if (retval < 0) | |
736 | + goto out; | |
737 | + } | |
738 | + | |
739 | + /* Set argv[2] */ | |
740 | + { | |
741 | + char *exe = ccs_get_exe(); | |
742 | + if (exe) { | |
743 | + retval = ccs_copy_argv(exe, bprm); | |
744 | + kfree(exe); | |
745 | + } else { | |
746 | + retval = -ENOMEM; | |
747 | + } | |
748 | + if (retval < 0) | |
749 | + goto out; | |
750 | + } | |
751 | + | |
752 | + /* Set argv[1] */ | |
753 | + { | |
754 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
755 | + retval = ccs_copy_argv(ccs_current_domain()->domainname->name, | |
756 | + bprm); | |
757 | +#else | |
758 | + snprintf(r->tmp, CCS_EXEC_TMPSIZE - 1, "%s", | |
759 | + ccs_current_domain()->domainname->name); | |
760 | + retval = ccs_copy_argv(r->tmp, bprm); | |
761 | +#endif | |
762 | + if (retval < 0) | |
763 | + goto out; | |
764 | + } | |
765 | + | |
766 | + /* Set argv[0] */ | |
767 | + { | |
768 | + struct path root; | |
769 | + char *cp; | |
770 | + int root_len; | |
771 | + int handler_len; | |
772 | + get_fs_root(current->fs, &root); | |
773 | + cp = ccs_realpath(&root); | |
774 | + path_put(&root); | |
775 | + if (!cp) { | |
776 | + retval = -ENOMEM; | |
777 | + goto out; | |
778 | + } | |
779 | + root_len = strlen(cp); | |
780 | + retval = strncmp(r->handler_path->name, cp, root_len); | |
781 | + root_len--; | |
782 | + kfree(cp); | |
783 | + if (retval) { | |
784 | + retval = -ENOENT; | |
785 | + goto out; | |
786 | + } | |
787 | + handler_len = r->handler_path->total_len + 1; | |
788 | + /* r->handler is released by ccs_finish_execve(). */ | |
789 | + r->handler = kmalloc(handler_len, GFP_NOFS); | |
790 | + if (!r->handler) { | |
791 | + retval = -ENOMEM; | |
792 | + goto out; | |
793 | + } | |
794 | + /* Adjust root directory for open_exec(). */ | |
795 | + memmove(r->handler, r->handler_path->name + root_len, | |
796 | + handler_len - root_len); | |
797 | + ccs_unescape(r->handler); | |
798 | + retval = -ENOENT; | |
799 | + if (*r->handler != '/') | |
800 | + goto out; | |
801 | + retval = ccs_copy_argv(r->handler, bprm); | |
802 | + if (retval < 0) | |
803 | + goto out; | |
804 | + } | |
805 | + | |
806 | + /* | |
807 | + * OK, now restart the process with execute handler program's dentry. | |
808 | + */ | |
809 | + filp = open_exec(r->handler); | |
810 | + if (IS_ERR(filp)) { | |
811 | + retval = PTR_ERR(filp); | |
812 | + goto out; | |
813 | + } | |
814 | + r->obj.path[0].dentry = filp->f_dentry; | |
815 | + r->obj.path[0].mnt = filp->f_vfsmnt; | |
816 | + bprm->file = filp; | |
817 | + bprm->filename = r->handler; | |
818 | + bprm->interp = bprm->filename; | |
819 | + retval = prepare_binprm(bprm); | |
820 | + if (retval < 0) | |
821 | + goto out; | |
822 | + ccs_populate_patharg(r, true); | |
823 | + if (!r->param.s[0]) | |
824 | + retval = -ENOMEM; | |
825 | + else if (ccs_pathcmp(r->param.s[0], r->handler_path)) { | |
826 | + /* Failed to verify execute handler. */ | |
827 | + static u8 counter = 20; | |
828 | + if (counter) { | |
829 | + counter--; | |
830 | + printk(KERN_WARNING "Failed to verify: %s\n", | |
831 | + r->handler_path->name); | |
832 | + } | |
833 | + retval = -EINVAL; | |
834 | + } | |
835 | +out: | |
836 | + return retval; | |
837 | +} | |
838 | + | |
839 | +#endif | |
840 | + | |
841 | +/** | |
842 | + * ccs_dump_page - Dump a page to buffer. | |
843 | + * | |
844 | + * @bprm: Pointer to "struct linux_binprm". | |
845 | + * @pos: Location to dump. | |
846 | + * @dump: Poiner to "struct ccs_page_dump". | |
847 | + * | |
848 | + * Returns true on success, false otherwise. | |
849 | + */ | |
850 | +bool ccs_dump_page(struct linux_binprm *bprm, unsigned long pos, | |
851 | + struct ccs_page_dump *dump) | |
852 | +{ | |
853 | + struct page *page; | |
854 | + /* dump->data is released by ccs_start_execve(). */ | |
855 | + if (!dump->data) { | |
856 | + dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); | |
857 | + if (!dump->data) | |
858 | + return false; | |
859 | + } | |
860 | + /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */ | |
861 | +#ifdef CONFIG_MMU | |
862 | + if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0) | |
863 | + return false; | |
864 | +#else | |
865 | + page = bprm->page[pos / PAGE_SIZE]; | |
866 | +#endif | |
867 | + if (page != dump->page) { | |
868 | + const unsigned int offset = pos % PAGE_SIZE; | |
869 | + /* | |
870 | + * Maybe kmap()/kunmap() should be used here. | |
871 | + * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic(). | |
872 | + * So do I. | |
873 | + */ | |
874 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
875 | + char *kaddr = kmap_atomic(page); | |
876 | +#else | |
877 | + char *kaddr = kmap_atomic(page, KM_USER0); | |
878 | +#endif | |
879 | + dump->page = page; | |
880 | + memcpy(dump->data + offset, kaddr + offset, | |
881 | + PAGE_SIZE - offset); | |
882 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) | |
883 | + kunmap_atomic(kaddr); | |
884 | +#else | |
885 | + kunmap_atomic(kaddr, KM_USER0); | |
886 | +#endif | |
887 | + } | |
888 | + /* Same with put_arg_page(page) in fs/exec.c */ | |
889 | +#ifdef CONFIG_MMU | |
890 | + put_page(page); | |
891 | +#endif | |
892 | + return true; | |
893 | +} | |
894 | + | |
895 | +/** | |
896 | + * ccs_start_execve - Prepare for execve() operation. | |
897 | + * | |
898 | + * @bprm: Pointer to "struct linux_binprm". | |
899 | + * @rp: Pointer to "struct ccs_request_info *". | |
900 | + * | |
901 | + * Returns 0 on success, negative value otherwise. | |
902 | + */ | |
903 | +static int ccs_start_execve(struct linux_binprm *bprm, | |
904 | + struct ccs_request_info **rp) | |
905 | +{ | |
906 | + int retval; | |
907 | + struct ccs_security *task = ccs_current_security(); | |
908 | + struct ccs_request_info *r; | |
909 | + int idx; | |
910 | + *rp = NULL; | |
911 | + r = kzalloc(sizeof(*r), GFP_NOFS); | |
912 | + if (!r) | |
913 | + return -ENOMEM; | |
914 | + r->tmp = kzalloc(CCS_EXEC_TMPSIZE, GFP_NOFS); | |
915 | + if (!r->tmp) { | |
916 | + kfree(r); | |
917 | + return -ENOMEM; | |
918 | + } | |
919 | + idx = ccs_read_lock(); | |
920 | + /* r->dump->data is allocated by ccs_dump_page(). */ | |
921 | + r->previous_domain = task->ccs_domain_info; | |
922 | + /* Clear manager flag. */ | |
923 | + task->ccs_flags &= ~CCS_TASK_IS_MANAGER; | |
924 | + *rp = r; | |
925 | + r->bprm = bprm; | |
926 | + r->obj.path[0].dentry = bprm->file->f_dentry; | |
927 | + r->obj.path[0].mnt = bprm->file->f_vfsmnt; | |
928 | + retval = ccs_execute(r); | |
929 | +#ifdef CONFIG_CCSECURITY_ENVIRON | |
930 | + if (!retval && bprm->envc) | |
931 | + retval = ccs_environ(r); | |
932 | +#endif | |
933 | + ccs_clear_request_info(r); | |
934 | + /* Drop refcount obtained by ccs_execute_path(). */ | |
935 | + if (r->obj.path[1].dentry) { | |
936 | + path_put(&r->obj.path[1]); | |
937 | + r->obj.path[1].dentry = NULL; | |
938 | + } | |
939 | + ccs_read_unlock(idx); | |
940 | + kfree(r->tmp); | |
941 | + r->tmp = NULL; | |
942 | + kfree(r->dump.data); | |
943 | + r->dump.data = NULL; | |
944 | + return retval; | |
945 | +} | |
946 | + | |
947 | +/** | |
948 | + * ccs_finish_execve - Clean up execve() operation. | |
949 | + * | |
950 | + * @retval: Return code of an execve() operation. | |
951 | + * @r: Pointer to "struct ccs_request_info". | |
952 | + * | |
953 | + * Returns nothing. | |
954 | + */ | |
955 | +static void ccs_finish_execve(int retval, struct ccs_request_info *r) | |
956 | +{ | |
957 | + struct ccs_security *task; | |
958 | + if (!r) | |
959 | + return; | |
960 | + task = ccs_current_security(); | |
961 | + if (retval < 0) { | |
962 | + task->ccs_domain_info = r->previous_domain; | |
963 | + /* | |
964 | + * Make task->ccs_domain_info visible to GC before changing | |
965 | + * task->ccs_flags. | |
966 | + */ | |
967 | + smp_wmb(); | |
968 | + } else { | |
969 | + /* Mark the current process as execute handler. */ | |
970 | + if (r->handler) | |
971 | + task->ccs_flags |= CCS_TASK_IS_EXECUTE_HANDLER; | |
972 | + /* Mark the current process as normal process. */ | |
973 | + else | |
974 | + task->ccs_flags &= ~CCS_TASK_IS_EXECUTE_HANDLER; | |
975 | + } | |
976 | + /* Tell GC that I finished execve(). */ | |
977 | + task->ccs_flags &= ~CCS_TASK_IS_IN_EXECVE; | |
978 | + ccs_clear_request_info(r); | |
979 | + kfree(r->handler); | |
980 | + kfree(r); | |
981 | +} | |
982 | + | |
983 | +/** | |
984 | + * __ccs_search_binary_handler - Main routine for do_execve(). | |
985 | + * | |
986 | + * @bprm: Pointer to "struct linux_binprm". | |
987 | + * @regs: Pointer to "struct pt_regs". | |
988 | + * | |
989 | + * Returns 0 on success, negative value otherwise. | |
990 | + * | |
991 | + * Performs permission checks for do_execve() and domain transition. | |
992 | + * Domain transition by "struct ccs_acl_info" will be reverted | |
993 | + * if do_execve() failed. | |
994 | + * Garbage collector does not remove "struct ccs_domain_info" from | |
995 | + * ccs_domain_list nor kfree("struct ccs_domain_info") if the current thread is | |
996 | + * marked as CCS_TASK_IS_IN_EXECVE. | |
997 | + */ | |
998 | +static int __ccs_search_binary_handler(struct linux_binprm *bprm, | |
999 | + struct pt_regs *regs) | |
1000 | +{ | |
1001 | + struct ccs_request_info *r; | |
1002 | + int retval; | |
1003 | +#ifndef CONFIG_CCSECURITY_OMIT_USERSPACE_LOADER | |
1004 | + if (!ccs_policy_loaded) | |
1005 | + ccsecurity_exports.load_policy(bprm->filename); | |
1006 | +#endif | |
1007 | + retval = ccs_start_execve(bprm, &r); | |
1008 | + if (!retval) | |
1009 | + retval = search_binary_handler(bprm, regs); | |
1010 | + ccs_finish_execve(retval, r); | |
1011 | + return retval; | |
1012 | +} | |
1013 | + | |
1014 | +/** | |
1015 | + * ccs_permission_init - Register permission check hooks. | |
1016 | + * | |
1017 | + * Returns nothing. | |
1018 | + */ | |
1019 | +void __init ccs_permission_init(void) | |
1020 | +{ | |
1021 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1022 | + ccsecurity_ops.save_open_mode = __ccs_save_open_mode; | |
1023 | + ccsecurity_ops.clear_open_mode = __ccs_clear_open_mode; | |
1024 | + ccsecurity_ops.open_permission = __ccs_open_permission; | |
1025 | +#else | |
1026 | + ccsecurity_ops.open_permission = ccs_new_open_permission; | |
1027 | +#endif | |
1028 | + ccsecurity_ops.fcntl_permission = __ccs_fcntl_permission; | |
1029 | + ccsecurity_ops.ioctl_permission = __ccs_ioctl_permission; | |
1030 | + ccsecurity_ops.chmod_permission = __ccs_chmod_permission; | |
1031 | + ccsecurity_ops.chown_permission = __ccs_chown_permission; | |
1032 | +#ifdef CONFIG_CCSECURITY_GETATTR | |
1033 | + ccsecurity_ops.getattr_permission = __ccs_getattr_permission; | |
1034 | +#endif | |
1035 | + ccsecurity_ops.pivot_root_permission = __ccs_pivot_root_permission; | |
1036 | + ccsecurity_ops.chroot_permission = __ccs_chroot_permission; | |
1037 | + ccsecurity_ops.umount_permission = __ccs_umount_permission; | |
1038 | + ccsecurity_ops.mknod_permission = __ccs_mknod_permission; | |
1039 | + ccsecurity_ops.mkdir_permission = __ccs_mkdir_permission; | |
1040 | + ccsecurity_ops.rmdir_permission = __ccs_rmdir_permission; | |
1041 | + ccsecurity_ops.unlink_permission = __ccs_unlink_permission; | |
1042 | + ccsecurity_ops.symlink_permission = __ccs_symlink_permission; | |
1043 | + ccsecurity_ops.truncate_permission = __ccs_truncate_permission; | |
1044 | + ccsecurity_ops.rename_permission = __ccs_rename_permission; | |
1045 | + ccsecurity_ops.link_permission = __ccs_link_permission; | |
1046 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) | |
1047 | + ccsecurity_ops.open_exec_permission = __ccs_open_exec_permission; | |
1048 | + ccsecurity_ops.uselib_permission = __ccs_uselib_permission; | |
1049 | +#endif | |
1050 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && defined(CONFIG_SYSCTL_SYSCALL) | |
1051 | + ccsecurity_ops.parse_table = __ccs_parse_table; | |
1052 | +#endif | |
1053 | + ccsecurity_ops.mount_permission = __ccs_mount_permission; | |
1054 | +#ifdef CONFIG_CCSECURITY_CAPABILITY | |
1055 | + ccsecurity_ops.capable = __ccs_capable; | |
1056 | + ccsecurity_ops.socket_create_permission = | |
1057 | + __ccs_socket_create_permission; | |
1058 | +#endif | |
1059 | +#ifdef CONFIG_CCSECURITY_NETWORK | |
1060 | + ccsecurity_ops.socket_listen_permission = | |
1061 | + __ccs_socket_listen_permission; | |
1062 | + ccsecurity_ops.socket_connect_permission = | |
1063 | + __ccs_socket_connect_permission; | |
1064 | + ccsecurity_ops.socket_bind_permission = __ccs_socket_bind_permission; | |
1065 | + ccsecurity_ops.socket_post_accept_permission = | |
1066 | + __ccs_socket_post_accept_permission; | |
1067 | + ccsecurity_ops.socket_sendmsg_permission = | |
1068 | + __ccs_socket_sendmsg_permission; | |
1069 | +#endif | |
1070 | +#ifdef CONFIG_CCSECURITY_NETWORK_RECVMSG | |
1071 | + ccsecurity_ops.socket_post_recvmsg_permission = | |
1072 | + __ccs_socket_post_recvmsg_permission; | |
1073 | +#endif | |
1074 | +#ifdef CONFIG_CCSECURITY_PTRACE | |
1075 | + ccsecurity_ops.ptrace_permission = __ccs_ptrace_permission; | |
1076 | +#endif | |
1077 | +#ifdef CONFIG_CCSECURITY_SIGNAL | |
1078 | + ccsecurity_ops.kill_permission = ccs_signal_permission0; | |
1079 | + ccsecurity_ops.tgkill_permission = ccs_signal_permission1; | |
1080 | + ccsecurity_ops.tkill_permission = ccs_signal_permission0; | |
1081 | + ccsecurity_ops.sigqueue_permission = ccs_signal_permission0; | |
1082 | + ccsecurity_ops.tgsigqueue_permission = ccs_signal_permission1; | |
1083 | +#endif | |
1084 | + ccsecurity_ops.search_binary_handler = __ccs_search_binary_handler; | |
1085 | +} | |
1086 | + | |
1087 | +/** | |
1088 | + * ccs_kern_path - Wrapper for kern_path(). | |
1089 | + * | |
1090 | + * @pathname: Pathname to resolve. Maybe NULL. | |
1091 | + * @flags: Lookup flags. | |
1092 | + * @path: Pointer to "struct path". | |
1093 | + * | |
1094 | + * Returns 0 on success, negative value otherwise. | |
1095 | + */ | |
1096 | +static int ccs_kern_path(const char *pathname, int flags, struct path *path) | |
1097 | +{ | |
1098 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) | |
1099 | + if (!pathname || kern_path(pathname, flags, path)) | |
1100 | + return -ENOENT; | |
1101 | +#else | |
1102 | + struct nameidata nd; | |
1103 | + if (!pathname || path_lookup(pathname, flags, &nd)) | |
1104 | + return -ENOENT; | |
1105 | + *path = nd.path; | |
1106 | +#endif | |
1107 | + return 0; | |
1108 | +} | |
1109 | + | |
1110 | +/** | |
1111 | + * ccs_execute_path - Get dentry/vfsmount of a program. | |
1112 | + * | |
1113 | + * @bprm: Pointer to "struct linux_binprm". | |
1114 | + * @path: Pointer to "struct path". | |
1115 | + * | |
1116 | + * Returns 0 on success, negative value otherwise. | |
1117 | + */ | |
1118 | +static int ccs_execute_path(struct linux_binprm *bprm, struct path *path) | |
1119 | +{ | |
1120 | + /* | |
1121 | + * Follow symlinks if the requested pathname is on procfs, for | |
1122 | + * /proc/\$/exe is meaningless. | |
1123 | + */ | |
1124 | + const unsigned int follow = | |
1125 | + (bprm->file->f_dentry->d_sb->s_magic == PROC_SUPER_MAGIC) ? | |
1126 | + LOOKUP_FOLLOW : 0; | |
1127 | + if (ccs_kern_path(bprm->filename, follow, path)) | |
1128 | + return -ENOENT; | |
1129 | + return 0; | |
1130 | +} | |
1131 | + | |
1132 | +/** | |
1133 | + * ccs_mount_acl - Check permission for mount() operation. | |
1134 | + * | |
1135 | + * @dev_name: Name of device file or mount source. Maybe NULL. | |
1136 | + * @dir: Pointer to "struct path". | |
1137 | + * @type: Name of filesystem type. Maybe NULL. | |
1138 | + * @flags: Mount options. | |
1139 | + * @data: Mount options not in @flags. Maybe NULL. | |
1140 | + * | |
1141 | + * Returns 0 on success, negative value otherwise. | |
1142 | + */ | |
1143 | +static int ccs_mount_acl(const char *dev_name, struct path *dir, | |
1144 | + const char *type, unsigned long flags, | |
1145 | + const char *data) | |
1146 | +{ | |
1147 | + struct ccs_request_info r = { }; | |
1148 | + struct ccs_path_info rtype = { }; | |
1149 | + struct ccs_path_info rdata = { }; | |
1150 | + bool check_dev = false; | |
1151 | + bool check_data = false; | |
1152 | + int error; | |
1153 | + | |
1154 | + /* Compare fstype in order to determine type of dev_name argument. */ | |
1155 | + if (type == ccs_mounts[CCS_MOUNT_REMOUNT]) { | |
1156 | + /* do_remount() case. */ | |
1157 | + if (data && !(dir->mnt->mnt_sb->s_type->fs_flags & | |
1158 | + FS_BINARY_MOUNTDATA)) | |
1159 | + check_data = true; | |
1160 | + } else if (type == ccs_mounts[CCS_MOUNT_BIND]) { | |
1161 | + /* do_loopback() case. */ | |
1162 | + check_dev = true; | |
1163 | + } else if (type == ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE] || | |
1164 | + type == ccs_mounts[CCS_MOUNT_MAKE_PRIVATE] || | |
1165 | + type == ccs_mounts[CCS_MOUNT_MAKE_SLAVE] || | |
1166 | + type == ccs_mounts[CCS_MOUNT_MAKE_SHARED]) { | |
1167 | + /* do_change_type() case. */ | |
1168 | + } else if (type == ccs_mounts[CCS_MOUNT_MOVE]) { | |
1169 | + /* do_move_mount() case. */ | |
1170 | + check_dev = true; | |
1171 | + } else { | |
1172 | + /* do_new_mount() case. */ | |
1173 | + struct file_system_type *fstype; | |
1174 | + if (!type) | |
1175 | + return -EINVAL; | |
1176 | + fstype = get_fs_type(type); | |
1177 | + if (!fstype) | |
1178 | + return -ENODEV; | |
1179 | + if (fstype->fs_flags & FS_REQUIRES_DEV) | |
1180 | + check_dev = true; | |
1181 | + if (data && !(fstype->fs_flags & FS_BINARY_MOUNTDATA)) | |
1182 | + check_data = true; | |
1183 | + ccs_put_filesystem(fstype); | |
1184 | + } | |
1185 | + /* Start filling arguments. */ | |
1186 | + r.type = CCS_MAC_MOUNT; | |
1187 | + /* Remember mount options. */ | |
1188 | + r.param.i[0] = flags; | |
1189 | + /* | |
1190 | + * Remember mount point. | |
1191 | + * r.param.s[1] is calculated from r.obj.path[1] as needed. | |
1192 | + */ | |
1193 | + r.obj.path[1] = *dir; | |
1194 | + /* Remember fstype. */ | |
1195 | + rtype.name = ccs_encode(type); | |
1196 | + if (!rtype.name) | |
1197 | + return -ENOMEM; | |
1198 | + ccs_fill_path_info(&rtype); | |
1199 | + r.param.s[2] = &rtype; | |
1200 | + if (check_data) { | |
1201 | + /* Remember data argument. */ | |
1202 | + rdata.name = ccs_encode(data); | |
1203 | + if (!rdata.name) { | |
1204 | + error = -ENOMEM; | |
1205 | + goto out; | |
1206 | + } | |
1207 | + ccs_fill_path_info(&rdata); | |
1208 | + r.param.s[3] = &rdata; | |
1209 | + } | |
1210 | + if (check_dev) { | |
1211 | + /* | |
1212 | + * Remember device file or mount source. | |
1213 | + * r.param.s[0] is calculated from r.obj.path[0] as needed. | |
1214 | + */ | |
1215 | + if (ccs_kern_path(dev_name, LOOKUP_FOLLOW, &r.obj.path[0])) { | |
1216 | + error = -ENOENT; | |
1217 | + goto out; | |
1218 | + } | |
1219 | + } | |
1220 | + error = ccs_check_acl(&r, false); | |
1221 | + /* Drop refcount obtained by ccs_kern_path(). */ | |
1222 | + if (check_dev) | |
1223 | + path_put(&r.obj.path[0]); | |
1224 | +out: | |
1225 | + kfree(rtype.name); | |
1226 | + kfree(rdata.name); | |
1227 | + ccs_clear_request_info(&r); | |
1228 | + return error; | |
1229 | +} | |
1230 | + | |
1231 | +/** | |
1232 | + * __ccs_mount_permission - Check permission for mount() operation. | |
1233 | + * | |
1234 | + * @dev_name: Name of device file. Maybe NULL. | |
1235 | + * @path: Pointer to "struct path". | |
1236 | + * @type: Name of filesystem type. Maybe NULL. | |
1237 | + * @flags: Mount options. | |
1238 | + * @data_page: Mount options not in @flags. Maybe NULL. | |
1239 | + * | |
1240 | + * Returns 0 on success, negative value otherwise. | |
1241 | + */ | |
1242 | +static int __ccs_mount_permission(char *dev_name, struct path *path, | |
1243 | + const char *type, unsigned long flags, | |
1244 | + void *data_page) | |
1245 | +{ | |
1246 | + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | |
1247 | + flags &= ~MS_MGC_MSK; | |
1248 | + if (flags & MS_REMOUNT) { | |
1249 | + type = ccs_mounts[CCS_MOUNT_REMOUNT]; | |
1250 | + flags &= ~MS_REMOUNT; | |
1251 | + } else if (flags & MS_BIND) { | |
1252 | + type = ccs_mounts[CCS_MOUNT_BIND]; | |
1253 | + flags &= ~MS_BIND; | |
1254 | + } else if (flags & MS_SHARED) { | |
1255 | + if (flags & (MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) | |
1256 | + return -EINVAL; | |
1257 | + type = ccs_mounts[CCS_MOUNT_MAKE_SHARED]; | |
1258 | + flags &= ~MS_SHARED; | |
1259 | + } else if (flags & MS_PRIVATE) { | |
1260 | + if (flags & (MS_SHARED | MS_SLAVE | MS_UNBINDABLE)) | |
1261 | + return -EINVAL; | |
1262 | + type = ccs_mounts[CCS_MOUNT_MAKE_PRIVATE]; | |
1263 | + flags &= ~MS_PRIVATE; | |
1264 | + } else if (flags & MS_SLAVE) { | |
1265 | + if (flags & (MS_SHARED | MS_PRIVATE | MS_UNBINDABLE)) | |
1266 | + return -EINVAL; | |
1267 | + type = ccs_mounts[CCS_MOUNT_MAKE_SLAVE]; | |
1268 | + flags &= ~MS_SLAVE; | |
1269 | + } else if (flags & MS_UNBINDABLE) { | |
1270 | + if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE)) | |
1271 | + return -EINVAL; | |
1272 | + type = ccs_mounts[CCS_MOUNT_MAKE_UNBINDABLE]; | |
1273 | + flags &= ~MS_UNBINDABLE; | |
1274 | + } else if (flags & MS_MOVE) { | |
1275 | + type = ccs_mounts[CCS_MOUNT_MOVE]; | |
1276 | + flags &= ~MS_MOVE; | |
1277 | + } | |
1278 | + /* | |
1279 | + * do_mount() terminates data_page with '\0' if data_page != NULL. | |
1280 | + * Therefore, it is safe to pass data_page argument to ccs_mount_acl() | |
1281 | + * as "const char *" rather than "void *". | |
1282 | + */ | |
1283 | + ccs_check_auto_domain_transition(); | |
1284 | + return ccs_mount_acl(dev_name, path, type, flags, data_page); | |
1285 | +} | |
1286 | + | |
1287 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1288 | + | |
1289 | +/** | |
1290 | + * __ccs_save_open_mode - Remember original flags passed to sys_open(). | |
1291 | + * | |
1292 | + * @mode: Flags passed to sys_open(). | |
1293 | + * | |
1294 | + * Returns nothing. | |
1295 | + * | |
1296 | + * TOMOYO does not check "file write" if open(path, O_TRUNC | O_RDONLY) was | |
1297 | + * requested because write() is not permitted. Instead, TOMOYO checks | |
1298 | + * "file truncate" if O_TRUNC is passed. | |
1299 | + * | |
1300 | + * TOMOYO does not check "file read" and "file write" if open(path, 3) was | |
1301 | + * requested because read()/write() are not permitted. Instead, TOMOYO checks | |
1302 | + * "file ioctl" when ioctl() is requested. | |
1303 | + */ | |
1304 | +static void __ccs_save_open_mode(int mode) | |
1305 | +{ | |
1306 | + if ((mode & 3) == 3) | |
1307 | + ccs_current_security()->ccs_flags |= CCS_OPEN_FOR_IOCTL_ONLY; | |
1308 | +} | |
1309 | + | |
1310 | +/** | |
1311 | + * __ccs_clear_open_mode - Forget original flags passed to sys_open(). | |
1312 | + * | |
1313 | + * Returns nothing. | |
1314 | + */ | |
1315 | +static void __ccs_clear_open_mode(void) | |
1316 | +{ | |
1317 | + ccs_current_security()->ccs_flags &= ~CCS_OPEN_FOR_IOCTL_ONLY; | |
1318 | +} | |
1319 | + | |
1320 | +#endif | |
1321 | + | |
1322 | +/** | |
1323 | + * __ccs_open_permission - Check permission for "read" and "write". | |
1324 | + * | |
1325 | + * @dentry: Pointer to "struct dentry". | |
1326 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1327 | + * @flag: Flags for open(). | |
1328 | + * | |
1329 | + * Returns 0 on success, negative value otherwise. | |
1330 | + */ | |
1331 | +static int __ccs_open_permission(struct dentry *dentry, struct vfsmount *mnt, | |
1332 | + const int flag) | |
1333 | +{ | |
1334 | + struct ccs_request_info r = { }; | |
1335 | + const u32 ccs_flags = ccs_current_flags(); | |
1336 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1337 | + const u8 acc_mode = (flag & 3) == 3 ? 0 : ACC_MODE(flag); | |
1338 | +#else | |
1339 | + const u8 acc_mode = (ccs_flags & CCS_OPEN_FOR_IOCTL_ONLY) ? 0 : | |
1340 | + ACC_MODE(flag); | |
1341 | +#endif | |
1342 | + int error = 0; | |
1343 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) | |
1344 | + if (current->in_execve && !(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | |
1345 | + return 0; | |
1346 | +#endif | |
1347 | +#ifndef CONFIG_CCSECURITY_GETATTR | |
1348 | + if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) | |
1349 | + return 0; | |
1350 | +#endif | |
1351 | + r.obj.path[0].dentry = dentry; | |
1352 | + r.obj.path[0].mnt = mnt; | |
1353 | + if (!(ccs_flags & CCS_TASK_IS_IN_EXECVE)) | |
1354 | + ccs_check_auto_domain_transition(); | |
1355 | + if (acc_mode & MAY_READ) { | |
1356 | + r.type = CCS_MAC_READ; | |
1357 | + error = ccs_check_acl(&r, false); | |
1358 | + } | |
1359 | + if (!error && (acc_mode & MAY_WRITE)) { | |
1360 | + r.type = (flag & O_APPEND) ? CCS_MAC_APPEND : | |
1361 | + CCS_MAC_WRITE; | |
1362 | + error = ccs_check_acl(&r, false); | |
1363 | + } | |
1364 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32) | |
1365 | + if (!error && (flag & O_TRUNC)) { | |
1366 | + r.type = CCS_MAC_TRUNCATE; | |
1367 | + error = ccs_check_acl(&r, false); | |
1368 | + } | |
1369 | +#endif | |
1370 | + ccs_clear_request_info(&r); | |
1371 | + return error; | |
1372 | +} | |
1373 | + | |
1374 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) | |
1375 | + | |
1376 | +/** | |
1377 | + * ccs_new_open_permission - Check permission for "read" and "write". | |
1378 | + * | |
1379 | + * @filp: Pointer to "struct file". | |
1380 | + * | |
1381 | + * Returns 0 on success, negative value otherwise. | |
1382 | + */ | |
1383 | +static int ccs_new_open_permission(struct file *filp) | |
1384 | +{ | |
1385 | + return __ccs_open_permission(filp->f_path.dentry, filp->f_path.mnt, | |
1386 | + filp->f_flags); | |
1387 | +} | |
1388 | + | |
1389 | +#endif | |
1390 | + | |
1391 | +/** | |
1392 | + * ccs_path_perm - Check permission for "unlink", "rmdir", "truncate", "append", "getattr" and "chroot". | |
1393 | + * | |
1394 | + * @operation: One of values in "enum ccs_mac_index". | |
1395 | + * @dentry: Pointer to "struct dentry". | |
1396 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1397 | + * | |
1398 | + * Returns 0 on success, negative value otherwise. | |
1399 | + */ | |
1400 | +static int ccs_path_perm(const enum ccs_mac_index operation, | |
1401 | + struct dentry *dentry, struct vfsmount *mnt) | |
1402 | +{ | |
1403 | + struct ccs_request_info r = { }; | |
1404 | + ccs_check_auto_domain_transition(); | |
1405 | + r.type = operation; | |
1406 | + r.obj.path[0].dentry = dentry; | |
1407 | + r.obj.path[0].mnt = mnt; | |
1408 | + return ccs_check_acl(&r, true); | |
1409 | +} | |
1410 | + | |
1411 | +/** | |
1412 | + * ccs_mkdev_perm - Check permission for "mkblock" and "mkchar". | |
1413 | + * | |
1414 | + * @operation: Type of operation. (CCS_MAC_MKCHAR or CCS_MAC_MKBLOCK) | |
1415 | + * @dentry: Pointer to "struct dentry". | |
1416 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1417 | + * @mode: Create mode. | |
1418 | + * @dev: Device number. | |
1419 | + * | |
1420 | + * Returns 0 on success, negative value otherwise. | |
1421 | + */ | |
1422 | +static int ccs_mkdev_perm(const u8 operation, struct dentry *dentry, | |
1423 | + struct vfsmount *mnt, const unsigned int mode, | |
1424 | + unsigned int dev) | |
1425 | +{ | |
1426 | + struct ccs_request_info r = { }; | |
1427 | + ccs_check_auto_domain_transition(); | |
1428 | + r.obj.path[0].dentry = dentry; | |
1429 | + r.obj.path[0].mnt = mnt; | |
1430 | + dev = new_decode_dev(dev); | |
1431 | + r.type = operation; | |
1432 | + r.param.i[0] = mode; | |
1433 | + r.param.i[1] = MAJOR(dev); | |
1434 | + r.param.i[2] = MINOR(dev); | |
1435 | + return ccs_check_acl(&r, true); | |
1436 | +} | |
1437 | + | |
1438 | +/** | |
1439 | + * ccs_path2_perm - Check permission for "rename", "link" and "pivot_root". | |
1440 | + * | |
1441 | + * @operation: One of values in "enum ccs_mac_index". | |
1442 | + * @dentry1: Pointer to "struct dentry". | |
1443 | + * @mnt1: Pointer to "struct vfsmount". Maybe NULL. | |
1444 | + * @dentry2: Pointer to "struct dentry". | |
1445 | + * @mnt2: Pointer to "struct vfsmount". Maybe NULL. | |
1446 | + * | |
1447 | + * Returns 0 on success, negative value otherwise. | |
1448 | + */ | |
1449 | +static int ccs_path2_perm(const enum ccs_mac_index operation, | |
1450 | + struct dentry *dentry1, struct vfsmount *mnt1, | |
1451 | + struct dentry *dentry2, struct vfsmount *mnt2) | |
1452 | +{ | |
1453 | + struct ccs_request_info r = { }; | |
1454 | + ccs_check_auto_domain_transition(); | |
1455 | + r.type = operation; | |
1456 | + r.obj.path[0].dentry = dentry1; | |
1457 | + r.obj.path[0].mnt = mnt1; | |
1458 | + r.obj.path[1].dentry = dentry2; | |
1459 | + r.obj.path[1].mnt = mnt2; | |
1460 | + return ccs_check_acl(&r, true); | |
1461 | +} | |
1462 | + | |
1463 | +/** | |
1464 | + * __ccs_symlink_permission - Check permission for "symlink". | |
1465 | + * | |
1466 | + * @dentry: Pointer to "struct dentry". | |
1467 | + * @mnt: Pointer to "struct vfsmount". Maybe NULL. | |
1468 | + * @target: Content of symlink. | |
1469 | + * | |
1470 | + * Returns 0 on success, negative value otherwise. | |
1471 | + */ | |
1472 | +static int __ccs_symlink_permission(struct dentry *dentry, | |
1473 | + struct vfsmount *mnt, const char *target) | |
1474 | +{ | |
1475 | + struct ccs_request_info r = { }; | |
1476 | + ccs_check_auto_domain_transition(); | |
1477 | + r.type = CCS_MAC_SYMLINK; | |
1478 | + r.obj.path[0].dentry = dentry; | |
1479 | + r.obj.path[0].mnt = mnt; | |
1480 | + r.obj.pathname[1].name = ccs_encode(target); | |
1481 | + if (!r.obj.pathname[1].name) | |
1482 | + return -ENOMEM; | |
1483 | + ccs_fill_path_info(&r.obj.pathname[1]); | |
1484 | + r.param.s[1] = &r.obj.pathname[1]; | |
1485 | + return ccs_check_acl(&r, true); | |
1486 | +} | |
1487 | + | |
1488 | +/** | |
1489 | + * ccs_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp" and "unmount". | |
1490 | + * | |
1491 | + * @type: One of values in "enum ccs_mac_index". | |
1492 | + * @dentry: Pointer to "struct dentry". | |
1493 | + * @vfsmnt: Pointer to "struct vfsmount". Maybe NULL. | |
1494 | + * @number: Number. | |
1495 | + * | |
1496 | + * Returns 0 on success, negative value otherwise. | |
1497 | + */ | |
1498 | +static int ccs_path_number_perm(const enum ccs_mac_index type, | |
1499 | + struct dentry *dentry, struct vfsmount *vfsmnt, | |
1500 | + unsigned long number) | |
1501 | +{ | |
1502 | + struct ccs_request_info r = { }; | |
1503 | + ccs_check_ |
差分はサイズ制限により省略されました。全ての差分を見るためにはローカルクライアントを利用してください。