caitsith-tools 0.2.1
@@ -0,0 +1,77 @@ | ||
1 | +Summary: Userspace tools for CaitSith 0.2 | |
2 | + | |
3 | +Name: caitsith-tools | |
4 | +Version: 0.2 | |
5 | +Release: 2 | |
6 | +License: GPL | |
7 | +Group: System Environment/Kernel | |
8 | +ExclusiveOS: Linux | |
9 | +Autoreqprov: no | |
10 | +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot | |
11 | +## | |
12 | +## This spec file is intended to be distribution independent. | |
13 | +## I don't enable "BuildRequires:" line because rpmbuild will fail on | |
14 | +## environments where packages are managed by (e.g.) apt. | |
15 | +## | |
16 | +# BuildRequires: ncurses-devel | |
17 | +Requires: ncurses | |
18 | +Conflicts: caitsith-tools < 0.2-2 | |
19 | + | |
20 | +Source0: http://osdn.dl.osdn.jp/caitsith/55465/caitsith-tools-0.2-20161016.tar.gz | |
21 | + | |
22 | +%description | |
23 | +This package contains userspace tools for administrating CaitSith 0.2. | |
24 | +Please see http://caitsith.osdn.jp/ for documentation. | |
25 | + | |
26 | +%prep | |
27 | + | |
28 | +%setup -q -n caitsith-tools | |
29 | + | |
30 | +%build | |
31 | + | |
32 | +make USRLIBDIR=%_libdir CFLAGS="-Wall $RPM_OPT_FLAGS" | |
33 | + | |
34 | +%install | |
35 | + | |
36 | +rm -rf $RPM_BUILD_ROOT | |
37 | +make INSTALLDIR=$RPM_BUILD_ROOT USRLIBDIR=%_libdir install | |
38 | + | |
39 | +%clean | |
40 | + | |
41 | +rm -rf $RPM_BUILD_ROOT | |
42 | + | |
43 | +%post | |
44 | +ldconfig || true | |
45 | + | |
46 | +%files | |
47 | +%defattr(-,root,root) | |
48 | +/sbin/* | |
49 | +%_libdir/caitsith/* | |
50 | +%_libdir/libcaitsith* | |
51 | +/usr/sbin/* | |
52 | + | |
53 | +%changelog | |
54 | +* Sun Oct 16 2016 0.2-2 | |
55 | +- In order to cancel the effect of MS_REC|MS_SHARED done by systemd, | |
56 | + mark MS_REC|MS_PRIVATE before mounting securityfs. | |
57 | + | |
58 | +* Wed Oct 05 2016 0.2-1 | |
59 | +- Use /sys/kernel/security/caitsith/ by default and fall back to | |
60 | + /proc/caitsith/ . | |
61 | + | |
62 | +* Thu Jul 23 2015 0.1-5 | |
63 | +- caitsith-queryd: Copy a line to edit buffer correctly. | |
64 | + | |
65 | +* Sun Jan 05 2014 0.1-4 | |
66 | +- Let caitsith-queryd use poll() rather than select(). | |
67 | + | |
68 | +* Thu Feb 14 2013 0.1-3 | |
69 | +- Change Makefile's build flags, as suggested by Simon Ruderich and Hideki | |
70 | + Yamane. (Debian bug 674723) | |
71 | +- Change / to /* in rpm's %files section because Fedora 18 complains conflicts. | |
72 | + | |
73 | +* Sat May 05 2012 0.1-2 | |
74 | +- caitsith-init: Count number of ACL entries correctly. | |
75 | + | |
76 | +* Sun Apr 01 2012 0.1-1 | |
77 | +- First-release. |
@@ -0,0 +1,409 @@ | ||
1 | +/* | |
2 | + * caitsithtools.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/16 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include <unistd.h> | |
25 | +#include <sys/mount.h> | |
26 | +#include <sched.h> | |
27 | + | |
28 | +/* Use caitsith-agent process? */ | |
29 | +_Bool ccs_network_mode = false; | |
30 | +/* The IPv4 address of the remote host running the caitsith-agent . */ | |
31 | +u32 ccs_network_ip = INADDR_NONE; | |
32 | +/* The port number of the remote host running the caitsith-agent . */ | |
33 | +u16 ccs_network_port = 0; | |
34 | + | |
35 | +const char *CCS_PROC_POLICY_DIR = "/sys/kernel/security/caitsith"; | |
36 | +const char *CCS_PROC_POLICY_POLICY = "/sys/kernel/security/caitsith/policy"; | |
37 | + | |
38 | +/* Prototypes */ | |
39 | + | |
40 | +/* Utility functions */ | |
41 | + | |
42 | +/** | |
43 | + * ccs_out_of_memory - Print error message and abort. | |
44 | + * | |
45 | + * This function does not return. | |
46 | + */ | |
47 | +static void ccs_out_of_memory(void) | |
48 | +{ | |
49 | + fprintf(stderr, "Out of memory. Aborted.\n"); | |
50 | + exit(1); | |
51 | +} | |
52 | + | |
53 | +/** | |
54 | + * ccs_strdup - strdup() with abort on error. | |
55 | + * | |
56 | + * @string: String to duplicate. | |
57 | + * | |
58 | + * Returns copy of @string on success, abort otherwise. | |
59 | + */ | |
60 | +char *ccs_strdup(const char *string) | |
61 | +{ | |
62 | + char *cp = strdup(string); | |
63 | + if (!cp) | |
64 | + ccs_out_of_memory(); | |
65 | + return cp; | |
66 | +} | |
67 | + | |
68 | +/** | |
69 | + * ccs_realloc - realloc() with abort on error. | |
70 | + * | |
71 | + * @ptr: Pointer to void. | |
72 | + * @size: New size. | |
73 | + * | |
74 | + * Returns return value of realloc() on success, abort otherwise. | |
75 | + */ | |
76 | +void *ccs_realloc(void *ptr, const size_t size) | |
77 | +{ | |
78 | + void *vp = realloc(ptr, size); | |
79 | + if (!vp) | |
80 | + ccs_out_of_memory(); | |
81 | + return vp; | |
82 | +} | |
83 | + | |
84 | +/** | |
85 | + * ccs_malloc - malloc() with abort on error. | |
86 | + * | |
87 | + * @size: Size to allocate. | |
88 | + * | |
89 | + * Returns return value of malloc() on success, abort otherwise. | |
90 | + * | |
91 | + * Allocated memory is cleared with 0. | |
92 | + */ | |
93 | +void *ccs_malloc(const size_t size) | |
94 | +{ | |
95 | + void *vp = malloc(size); | |
96 | + if (!vp) | |
97 | + ccs_out_of_memory(); | |
98 | + memset(vp, 0, size); | |
99 | + return vp; | |
100 | +} | |
101 | + | |
102 | +/** | |
103 | + * ccs_str_starts - Check whether the given string starts with the given keyword. | |
104 | + * | |
105 | + * @str: Pointer to "char *". | |
106 | + * @begin: Pointer to "const char *". | |
107 | + * | |
108 | + * Returns true if @str starts with @begin, false otherwise. | |
109 | + * | |
110 | + * Note that @begin will be removed from @str before returning true. Therefore, | |
111 | + * @str must not be "const char *". | |
112 | + * | |
113 | + * Note that this function in kernel source has different arguments and behaves | |
114 | + * differently. | |
115 | + */ | |
116 | +_Bool ccs_str_starts(char *str, const char *begin) | |
117 | +{ | |
118 | + const int len = strlen(begin); | |
119 | + if (strncmp(str, begin, len)) | |
120 | + return false; | |
121 | + memmove(str, str + len, strlen(str + len) + 1); | |
122 | + return true; | |
123 | +} | |
124 | + | |
125 | +/** | |
126 | + * ccs_normalize_line - Format string. | |
127 | + * | |
128 | + * @buffer: The line to normalize. | |
129 | + * | |
130 | + * Returns nothing. | |
131 | + * | |
132 | + * Leading and trailing whitespaces are removed. | |
133 | + * Multiple whitespaces are packed into single space. | |
134 | + */ | |
135 | +void ccs_normalize_line(char *buffer) | |
136 | +{ | |
137 | + unsigned char *sp = (unsigned char *) buffer; | |
138 | + unsigned char *dp = (unsigned char *) buffer; | |
139 | + _Bool first = true; | |
140 | + while (*sp && (*sp <= ' ' || 127 <= *sp)) | |
141 | + sp++; | |
142 | + while (*sp) { | |
143 | + if (!first) | |
144 | + *dp++ = ' '; | |
145 | + first = false; | |
146 | + while (' ' < *sp && *sp < 127) | |
147 | + *dp++ = *sp++; | |
148 | + while (*sp && (*sp <= ' ' || 127 <= *sp)) | |
149 | + sp++; | |
150 | + } | |
151 | + *dp = '\0'; | |
152 | +} | |
153 | + | |
154 | +/** | |
155 | + * ccs_decode - Decode a string in CaitSith's rule to a string in C. | |
156 | + * | |
157 | + * @ascii: Pointer to "const char". | |
158 | + * @bin: Pointer to "char". Must not contain wildcards nor '\000'. | |
159 | + * | |
160 | + * Returns true if @ascii was successfully decoded, false otherwise. | |
161 | + * | |
162 | + * Note that it is legal to pass @ascii == @bin if the caller want to decode | |
163 | + * a string in a temporary buffer. | |
164 | + */ | |
165 | +_Bool ccs_decode(const char *ascii, char *bin) | |
166 | +{ | |
167 | + while (true) { | |
168 | + char c = *ascii++; | |
169 | + *bin++ = c; | |
170 | + if (!c) | |
171 | + break; | |
172 | + if (c == '\\') { | |
173 | + char d; | |
174 | + char e; | |
175 | + u8 f; | |
176 | + c = *ascii++; | |
177 | + switch (c) { | |
178 | + case '0': /* "\ooo" */ | |
179 | + case '1': | |
180 | + case '2': | |
181 | + case '3': | |
182 | + d = *ascii++; | |
183 | + if (d < '0' || d > '7') | |
184 | + break; | |
185 | + e = *ascii++; | |
186 | + if (e < '0' || e > '7') | |
187 | + break; | |
188 | + f = (u8) ((c - '0') << 6) + | |
189 | + (((u8) (d - '0')) << 3) + | |
190 | + (((u8) (e - '0'))); | |
191 | + if (f <= ' ' || f >= 127 || f == '\\') { | |
192 | + *(bin - 1) = f; | |
193 | + continue; | |
194 | + } | |
195 | + } | |
196 | + return false; | |
197 | + } else if (c <= ' ' || c >= 127) { | |
198 | + return false; | |
199 | + } | |
200 | + } | |
201 | + return true; | |
202 | +} | |
203 | + | |
204 | +/** | |
205 | + * ccs_open_read - Open a file for reading. | |
206 | + * | |
207 | + * @filename: String to send to remote caitsith-agent program if using | |
208 | + * network mode, file to open for reading otherwise. | |
209 | + * | |
210 | + * Returns pointer to "FILE" on success, NULL otherwise. | |
211 | + */ | |
212 | +FILE *ccs_open_read(const char *filename) | |
213 | +{ | |
214 | + if (ccs_network_mode) { | |
215 | + FILE *fp = ccs_open_write(filename); | |
216 | + if (fp) { | |
217 | + fputc(0, fp); | |
218 | + fflush(fp); | |
219 | + } | |
220 | + return fp; | |
221 | + } else { | |
222 | + return fopen(filename, "r"); | |
223 | + } | |
224 | +} | |
225 | + | |
226 | +/** | |
227 | + * ccs_open_stream - Establish IP connection. | |
228 | + * | |
229 | + * @filename: String to send to remote caitsith-agent program. | |
230 | + * | |
231 | + * Retruns file descriptor on success, EOF otherwise. | |
232 | + */ | |
233 | +int ccs_open_stream(const char *filename) | |
234 | +{ | |
235 | + const int fd = socket(AF_INET, SOCK_STREAM, 0); | |
236 | + struct sockaddr_in addr; | |
237 | + char c; | |
238 | + int len = strlen(filename) + 1; | |
239 | + memset(&addr, 0, sizeof(addr)); | |
240 | + addr.sin_family = AF_INET; | |
241 | + addr.sin_addr.s_addr = ccs_network_ip; | |
242 | + addr.sin_port = ccs_network_port; | |
243 | + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) || | |
244 | + write(fd, filename, len) != len || read(fd, &c, 1) != 1 || c) { | |
245 | + close(fd); | |
246 | + return EOF; | |
247 | + } | |
248 | + return fd; | |
249 | +} | |
250 | + | |
251 | +/** | |
252 | + * ccs_open_write - Open a file for writing. | |
253 | + * | |
254 | + * @filename: String to send to remote caitsith-agent program if using | |
255 | + * network mode, file to open for writing otherwise. | |
256 | + * | |
257 | + * Returns pointer to "FILE" on success, NULL otherwise. | |
258 | + */ | |
259 | +FILE *ccs_open_write(const char *filename) | |
260 | +{ | |
261 | + if (ccs_network_mode) { | |
262 | + const int fd = socket(AF_INET, SOCK_STREAM, 0); | |
263 | + struct sockaddr_in addr; | |
264 | + FILE *fp; | |
265 | + memset(&addr, 0, sizeof(addr)); | |
266 | + addr.sin_family = AF_INET; | |
267 | + addr.sin_addr.s_addr = ccs_network_ip; | |
268 | + addr.sin_port = ccs_network_port; | |
269 | + if (connect(fd, (struct sockaddr *) &addr, sizeof(addr))) { | |
270 | + close(fd); | |
271 | + return NULL; | |
272 | + } | |
273 | + fp = fdopen(fd, "r+"); | |
274 | + /* setbuf(fp, NULL); */ | |
275 | + fprintf(fp, "%s", filename); | |
276 | + fputc(0, fp); | |
277 | + fflush(fp); | |
278 | + if (fgetc(fp) != 0) { | |
279 | + fclose(fp); | |
280 | + return NULL; | |
281 | + } | |
282 | + return fp; | |
283 | + } else { | |
284 | + return fdopen(open(filename, O_WRONLY), "w"); | |
285 | + } | |
286 | +} | |
287 | + | |
288 | +/* Is the shared buffer for ccs_freadline() owned? */ | |
289 | +static _Bool ccs_buffer_locked = false; | |
290 | + | |
291 | +/** | |
292 | + * ccs_get - Mark the shared buffer for ccs_freadline() owned. | |
293 | + * | |
294 | + * Returns nothing. | |
295 | + * | |
296 | + * This is for avoiding accidental overwriting. | |
297 | + * ccs_freadline() have their own memory buffer. | |
298 | + */ | |
299 | +void ccs_get(void) | |
300 | +{ | |
301 | + if (ccs_buffer_locked) | |
302 | + ccs_out_of_memory(); | |
303 | + ccs_buffer_locked = true; | |
304 | +} | |
305 | + | |
306 | +/** | |
307 | + * ccs_put - Mark the shared buffer for ccs_freadline() no longer owned. | |
308 | + * | |
309 | + * Returns nothing. | |
310 | + * | |
311 | + * This is for avoiding accidental overwriting. | |
312 | + * ccs_freadline() have their own memory buffer. | |
313 | + */ | |
314 | +void ccs_put(void) | |
315 | +{ | |
316 | + if (!ccs_buffer_locked) | |
317 | + ccs_out_of_memory(); | |
318 | + ccs_buffer_locked = false; | |
319 | +} | |
320 | + | |
321 | +/** | |
322 | + * ccs_freadline - Read a line from file to dynamically allocated buffer. | |
323 | + * | |
324 | + * @fp: Pointer to "FILE". | |
325 | + * | |
326 | + * Returns pointer to dynamically allocated buffer on success, NULL otherwise. | |
327 | + * | |
328 | + * The caller must not free() the returned pointer. | |
329 | + */ | |
330 | +char *ccs_freadline(FILE *fp) | |
331 | +{ | |
332 | + static char *policy = NULL; | |
333 | + int pos = 0; | |
334 | + while (true) { | |
335 | + static int max_policy_len = 0; | |
336 | + const int c = fgetc(fp); | |
337 | + if (c == EOF) | |
338 | + return NULL; | |
339 | + if (ccs_network_mode && !c) | |
340 | + return NULL; | |
341 | + if (pos == max_policy_len) { | |
342 | + max_policy_len += 4096; | |
343 | + policy = ccs_realloc(policy, max_policy_len); | |
344 | + } | |
345 | + policy[pos++] = (char) c; | |
346 | + if (c == '\n') { | |
347 | + policy[--pos] = '\0'; | |
348 | + break; | |
349 | + } | |
350 | + } | |
351 | + ccs_normalize_line(policy); | |
352 | + return policy; | |
353 | +} | |
354 | + | |
355 | +/** | |
356 | + * ccs_check_remote_host - Check whether the remote host is running with the CaitSith kernel or not. | |
357 | + * | |
358 | + * Returns true if running with CaitSith kernel, false otherwise. | |
359 | + */ | |
360 | +_Bool ccs_check_remote_host(_Bool exit_on_failue) | |
361 | +{ | |
362 | + int major = 0; | |
363 | + int minor = 0; | |
364 | + FILE *fp = ccs_open_read("version"); | |
365 | + if (!fp || (fscanf(fp, "%u.%u", &major, &minor) < 2 && major < 2016)) { | |
366 | + const u32 ip = ntohl(ccs_network_ip); | |
367 | + fprintf(stderr, "Can't connect to %u.%u.%u.%u:%u\n", | |
368 | + (u8) (ip >> 24), (u8) (ip >> 16), | |
369 | + (u8) (ip >> 8), (u8) ip, ntohs(ccs_network_port)); | |
370 | + if (fp) | |
371 | + fclose(fp); | |
372 | + if (exit_on_failue) | |
373 | + exit(1); | |
374 | + return false; | |
375 | + } else if (major == 0 && minor == 1) { | |
376 | + CCS_PROC_POLICY_DIR = "/proc/caitsith"; | |
377 | + CCS_PROC_POLICY_POLICY = "/proc/caitsith/policy"; | |
378 | + } | |
379 | + fclose(fp); | |
380 | + return true; | |
381 | +} | |
382 | + | |
383 | +_Bool ccs_check_policy_dir(_Bool exit_on_failue) | |
384 | +{ | |
385 | + if (access("/proc/caitsith/", X_OK) == 0) { | |
386 | + CCS_PROC_POLICY_DIR = "/proc/caitsith"; | |
387 | + CCS_PROC_POLICY_POLICY = "/proc/caitsith/policy"; | |
388 | + } else if (access("/sys/kernel/security/caitsith/", X_OK) && | |
389 | + (unshare(CLONE_NEWNS) || | |
390 | + mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) || | |
391 | + mount("none", "/sys/kernel/security/", "securityfs", 0, | |
392 | + NULL))) { | |
393 | + if (errno != EBUSY) { | |
394 | + fprintf(stderr, "Please mount securityfs on " | |
395 | + "/sys/kernel/security/ .\n"); | |
396 | + if (exit_on_failue) | |
397 | + exit(1); | |
398 | + return false; | |
399 | + } | |
400 | + } | |
401 | + if (chdir(CCS_PROC_POLICY_DIR)) { | |
402 | + fprintf(stderr, | |
403 | + "You can't run this program for this kernel.\n"); | |
404 | + if (exit_on_failue) | |
405 | + exit(1); | |
406 | + return false; | |
407 | + } | |
408 | + return true; | |
409 | +} |
@@ -0,0 +1,460 @@ | ||
1 | +/* | |
2 | + * caitsith-auditd.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include <signal.h> | |
25 | +#include <syslog.h> | |
26 | +#include <poll.h> | |
27 | + | |
28 | +#define CCS_AUDITD_CONF "/etc/caitsith/tools/auditd.conf" | |
29 | + | |
30 | +struct ccs_destination { | |
31 | + const char *pathname; | |
32 | + int fd; | |
33 | +}; | |
34 | + | |
35 | +static struct ccs_destination *destination_list = NULL; | |
36 | +static unsigned int destination_list_len = 0; | |
37 | + | |
38 | +enum ccs_rule_types { | |
39 | + CCS_SORT_RULE_HEADER, | |
40 | + CCS_SORT_RULE_ACL, | |
41 | + CCS_SORT_RULE_DESTINATION, | |
42 | +}; | |
43 | + | |
44 | +enum ccs_operator_types { | |
45 | + CCS_SORT_OPERATOR_CONTAINS, | |
46 | + CCS_SORT_OPERATOR_EQUALS, | |
47 | + CCS_SORT_OPERATOR_STARTS, | |
48 | +}; | |
49 | + | |
50 | +struct ccs_sort_rules { | |
51 | + enum ccs_rule_types type; | |
52 | + enum ccs_operator_types operation; | |
53 | + unsigned int index; | |
54 | + const char *string; | |
55 | + unsigned int string_len; /* strlen(string). */ | |
56 | +}; | |
57 | + | |
58 | +static struct ccs_sort_rules *rules = NULL; | |
59 | +static unsigned int rules_len = 0; | |
60 | + | |
61 | +static void ccs_auditd_init_rules(const char *filename) | |
62 | +{ | |
63 | + static _Bool first = 1; | |
64 | + FILE *fp = fopen(filename, "r"); | |
65 | + unsigned int line_no = 0; | |
66 | + unsigned int i; | |
67 | + if (!first) { | |
68 | + for (i = 0; i < rules_len; i++) | |
69 | + free((void *) rules[i].string); | |
70 | + rules_len = 0; | |
71 | + for (i = 0; i < destination_list_len; i++) { | |
72 | + free((void *) destination_list[i].pathname); | |
73 | + close(destination_list[i].fd); | |
74 | + } | |
75 | + destination_list_len = 0; | |
76 | + } | |
77 | + if (!fp) { | |
78 | + if (first) | |
79 | + fprintf(stderr, "Can't open %s for reading.\n", | |
80 | + filename); | |
81 | + else | |
82 | + syslog(LOG_WARNING, "Can't open %s for reading.\n", | |
83 | + filename); | |
84 | + exit(1); | |
85 | + } | |
86 | + ccs_get(); | |
87 | + while (true) { | |
88 | + char *line = ccs_freadline(fp); | |
89 | + struct ccs_sort_rules *ptr; | |
90 | + unsigned char c; | |
91 | + if (!line) | |
92 | + break; | |
93 | + line_no++; | |
94 | + ccs_normalize_line(line); | |
95 | + if (*line == '#' || !*line) | |
96 | + continue; | |
97 | + rules = ccs_realloc(rules, sizeof(struct ccs_sort_rules) * | |
98 | + (rules_len + 1)); | |
99 | + ptr = &rules[rules_len++]; | |
100 | + memset(ptr, 0, sizeof(*ptr)); | |
101 | + if (ccs_str_starts(line, "destination ")) { | |
102 | + if (*line != '/') | |
103 | + goto invalid_rule; | |
104 | + for (i = 0; i < destination_list_len; i++) | |
105 | + if (!strcmp(destination_list[i].pathname, | |
106 | + line)) | |
107 | + break; | |
108 | + if (i < destination_list_len) | |
109 | + goto store_destination; | |
110 | + destination_list = | |
111 | + ccs_realloc(destination_list, | |
112 | + ++destination_list_len * | |
113 | + sizeof(struct ccs_destination)); | |
114 | + if (!ccs_decode(line, line)) | |
115 | + goto invalid_rule; | |
116 | + destination_list[i].pathname = ccs_strdup(line); | |
117 | + destination_list[i].fd = EOF; | |
118 | +store_destination: | |
119 | + ptr->type = CCS_SORT_RULE_DESTINATION; | |
120 | + ptr->index = i; | |
121 | + continue; | |
122 | + } | |
123 | + if (ccs_str_starts(line, "header")) | |
124 | + ptr->type = CCS_SORT_RULE_HEADER; | |
125 | + else if (ccs_str_starts(line, "acl")) | |
126 | + ptr->type = CCS_SORT_RULE_ACL; | |
127 | + else | |
128 | + goto invalid_rule; | |
129 | + switch (sscanf(line, "[%u%c", &ptr->index, &c)) { | |
130 | + case 0: | |
131 | + break; | |
132 | + case 2: | |
133 | + if (c == ']') { | |
134 | + char *cp = strchr(line, ']') + 1; | |
135 | + memmove(line, cp, strlen(cp) + 1); | |
136 | + break; | |
137 | + } | |
138 | + default: | |
139 | + goto invalid_rule; | |
140 | + } | |
141 | + if (ccs_str_starts(line, ".contains ")) | |
142 | + ptr->operation = CCS_SORT_OPERATOR_CONTAINS; | |
143 | + else if (ccs_str_starts(line, ".equals ")) | |
144 | + ptr->operation = CCS_SORT_OPERATOR_EQUALS; | |
145 | + else if (ccs_str_starts(line, ".starts ")) | |
146 | + ptr->operation = CCS_SORT_OPERATOR_STARTS; | |
147 | + else | |
148 | + goto invalid_rule; | |
149 | + if (!*line) | |
150 | + goto invalid_rule; | |
151 | + line = ccs_strdup(line); | |
152 | + ptr->string = line; | |
153 | + ptr->string_len = strlen(line); | |
154 | + } | |
155 | + ccs_put(); | |
156 | + fclose(fp); | |
157 | + if (!rules_len) { | |
158 | + if (first) | |
159 | + fprintf(stderr, "No rules defined in %s .\n", | |
160 | + filename); | |
161 | + else | |
162 | + syslog(LOG_WARNING, "No rules defined in %s .\n", | |
163 | + filename); | |
164 | + exit(1); | |
165 | + } | |
166 | + for (i = 0; i < destination_list_len; i++) { | |
167 | + struct ccs_destination *ptr = &destination_list[i]; | |
168 | + const char *path = ptr->pathname; | |
169 | + /* This is OK because path is a strdup()ed string. */ | |
170 | + char *pos = (char *) path; | |
171 | + while (*pos) { | |
172 | + int ret_ignored; | |
173 | + if (*pos++ != '/') | |
174 | + continue; | |
175 | + *(pos - 1) = '\0'; | |
176 | + ret_ignored = mkdir(path, 0700); | |
177 | + *(pos - 1) = '/'; | |
178 | + } | |
179 | + do { | |
180 | + ptr->fd = open(path, O_WRONLY | O_APPEND | O_CREAT, | |
181 | + 0600); | |
182 | + } while (ptr->fd == EOF && errno == EINTR); | |
183 | + if (ptr->fd == EOF) { | |
184 | + if (first) | |
185 | + fprintf(stderr, "Can't open %s for writing.\n", | |
186 | + path); | |
187 | + else | |
188 | + syslog(LOG_WARNING, | |
189 | + "Can't open %s for writing.\n", path); | |
190 | + exit(1); | |
191 | + } | |
192 | + } | |
193 | + first = 0; | |
194 | + return; | |
195 | +invalid_rule: | |
196 | + if (first) | |
197 | + fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, | |
198 | + filename); | |
199 | + else | |
200 | + syslog(LOG_WARNING, "Invalid rule at line %u in %s .\n", | |
201 | + line_no, filename); | |
202 | + exit(1); | |
203 | +} | |
204 | + | |
205 | +static int ccs_check_rules(char *header, char *acl) | |
206 | +{ | |
207 | + unsigned int i; | |
208 | + _Bool matched = true; | |
209 | + for (i = 0; i < rules_len; i++) { | |
210 | + const struct ccs_sort_rules *ptr = &rules[i]; | |
211 | + char *line; | |
212 | + unsigned int index = ptr->index; | |
213 | + const char *find = ptr->string; | |
214 | + unsigned int find_len = ptr->string_len; | |
215 | + switch (ptr->type) { | |
216 | + case CCS_SORT_RULE_HEADER: | |
217 | + line = header; | |
218 | + break; | |
219 | + case CCS_SORT_RULE_ACL: | |
220 | + line = acl; | |
221 | + break; | |
222 | + default: /* CCS_SORT_RULE_DESTINATION */ | |
223 | + if (matched) | |
224 | + return ptr->index; | |
225 | + matched = true; | |
226 | + continue; | |
227 | + } | |
228 | + if (!matched) | |
229 | + continue; | |
230 | + if (!index) { | |
231 | + switch (ptr->operation) { | |
232 | + case CCS_SORT_OPERATOR_CONTAINS: | |
233 | + while (1) { | |
234 | + char *cp = strstr(line, find); | |
235 | + if (!cp) { | |
236 | + matched = false; | |
237 | + break; | |
238 | + } | |
239 | + if ((cp == line || *(cp - 1) == ' ') && | |
240 | + (!cp[find_len] || | |
241 | + cp[find_len] == ' ')) | |
242 | + break; | |
243 | + line = cp + 1; | |
244 | + } | |
245 | + break; | |
246 | + case CCS_SORT_OPERATOR_EQUALS: | |
247 | + matched = !strcmp(line, find); | |
248 | + break; | |
249 | + default: /* CCS_SORT_OPERATOR_STARTS */ | |
250 | + matched = !strncmp(line, find, find_len) && | |
251 | + (!line[find_len] || | |
252 | + line[find_len] == ' '); | |
253 | + } | |
254 | + } else { | |
255 | + char *word = line; | |
256 | + char *word_end; | |
257 | + while (--index) { | |
258 | + char *cp = strchr(word, ' '); | |
259 | + if (!cp) { | |
260 | + matched = false; | |
261 | + break; | |
262 | + } | |
263 | + word = cp + 1; | |
264 | + } | |
265 | + if (!matched) | |
266 | + continue; | |
267 | + word_end = strchr(word, ' '); | |
268 | + if (word_end) | |
269 | + *word_end = '\0'; | |
270 | + switch (ptr->operation) { | |
271 | + case CCS_SORT_OPERATOR_CONTAINS: | |
272 | + matched = strstr(word, find) != NULL; | |
273 | + break; | |
274 | + case CCS_SORT_OPERATOR_EQUALS: | |
275 | + matched = !strcmp(word, find); | |
276 | + break; | |
277 | + default: /* CCS_SORT_OPERATOR_STARTS */ | |
278 | + matched = !strncmp(word, find, find_len); | |
279 | + break; | |
280 | + } | |
281 | + if (word_end) | |
282 | + *word_end = ' '; | |
283 | + } | |
284 | + } | |
285 | + return EOF; | |
286 | +} | |
287 | + | |
288 | +static _Bool ccs_write_log(const int i, char *buffer) | |
289 | +{ | |
290 | + int len = strlen(buffer); | |
291 | + int ret; | |
292 | + struct ccs_destination *ptr = &destination_list[i]; | |
293 | + /* Create destination file if needed. */ | |
294 | + if (access(ptr->pathname, F_OK)) { | |
295 | + close(ptr->fd); | |
296 | + do { | |
297 | + ptr->fd = open(ptr->pathname, | |
298 | + O_WRONLY | O_APPEND | O_CREAT, 0600); | |
299 | + } while (ptr->fd == EOF && errno == EINTR); | |
300 | + if (ptr->fd == EOF) { | |
301 | + syslog(LOG_WARNING, "Can't open %s for writing.\n", | |
302 | + ptr->pathname); | |
303 | + return 0; | |
304 | + } | |
305 | + } | |
306 | + do { | |
307 | + ret = write(ptr->fd, buffer, len); | |
308 | + if (ret == len) | |
309 | + return 1; | |
310 | + } while (ret == EOF && errno == EINTR); | |
311 | + syslog(LOG_WARNING, "Can't write to %s .\n", ptr->pathname); | |
312 | + return 0; | |
313 | +} | |
314 | + | |
315 | +static void block_sighup(const _Bool block) | |
316 | +{ | |
317 | + sigset_t sigset; | |
318 | + sigemptyset(&sigset); | |
319 | + sigaddset(&sigset, SIGHUP); | |
320 | + sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigset, NULL); | |
321 | +} | |
322 | + | |
323 | +static void ccs_reload_config(int sig) | |
324 | +{ | |
325 | + block_sighup(1); | |
326 | + syslog(LOG_WARNING, "Reloading configuration file.\n"); | |
327 | + ccs_auditd_init_rules(CCS_AUDITD_CONF); | |
328 | + block_sighup(0); | |
329 | +} | |
330 | + | |
331 | +int main(int argc, char *argv[]) | |
332 | +{ | |
333 | + unsigned int i; | |
334 | + int fd_in; | |
335 | + for (i = 1; i < argc; i++) { | |
336 | + char *ptr = argv[i]; | |
337 | + char *cp = strchr(ptr, ':'); | |
338 | + if (!cp) | |
339 | + goto usage; | |
340 | + *cp++ = '\0'; | |
341 | + if (ccs_network_mode) | |
342 | + goto usage; | |
343 | + ccs_network_ip = inet_addr(ptr); | |
344 | + ccs_network_port = htons(atoi(cp)); | |
345 | + ccs_network_mode = true; | |
346 | + } | |
347 | + ccs_auditd_init_rules(CCS_AUDITD_CONF); | |
348 | + if (!ccs_network_mode) { | |
349 | + /* Get exclusive lock. */ | |
350 | + int fd = open("/proc/self/exe", O_RDONLY); | |
351 | + if (flock(fd, LOCK_EX | LOCK_NB) == EOF) | |
352 | + return 0; | |
353 | + } | |
354 | + if (ccs_network_mode) { | |
355 | + ccs_check_remote_host(true); | |
356 | + fd_in = ccs_open_stream("proc:audit"); | |
357 | + } else { | |
358 | + ccs_check_policy_dir(true); | |
359 | + fd_in = open("audit", O_RDONLY); | |
360 | + } | |
361 | + if (fd_in == EOF) { | |
362 | + fprintf(stderr, "Can't open %s/audit for reading.\n", | |
363 | + CCS_PROC_POLICY_DIR); | |
364 | + return 1; | |
365 | + } | |
366 | + switch (fork()) { | |
367 | + case 0: | |
368 | + break; | |
369 | + case -1: | |
370 | + fprintf(stderr, "Can't fork()\n"); | |
371 | + return 1; | |
372 | + default: | |
373 | + return 0; | |
374 | + } | |
375 | + if (setsid() == EOF) { | |
376 | + fprintf(stderr, "Can't setsid()\n"); | |
377 | + return 1; | |
378 | + } | |
379 | + switch (fork()) { | |
380 | + case 0: | |
381 | + break; | |
382 | + case -1: | |
383 | + fprintf(stderr, "Can't fork()\n"); | |
384 | + return 1; | |
385 | + default: | |
386 | + return 0; | |
387 | + } | |
388 | + if (chdir("/")) { | |
389 | + fprintf(stderr, "Can't chdir()\n"); | |
390 | + return 1; | |
391 | + } | |
392 | + close(0); | |
393 | + close(1); | |
394 | + close(2); | |
395 | + openlog("caitsith-auditd", 0, LOG_USER); | |
396 | + syslog(LOG_WARNING, "Started.\n"); | |
397 | + signal(SIGHUP, ccs_reload_config); | |
398 | + while (true) { | |
399 | + static char buffer[32768]; | |
400 | + char *acl; | |
401 | + char *tail; | |
402 | + int ret; | |
403 | + memset(buffer, 0, sizeof(buffer)); | |
404 | + if (ccs_network_mode) { | |
405 | + int j; | |
406 | + for (j = 0; j < sizeof(buffer) - 1; j++) { | |
407 | + do { | |
408 | + ret = read(fd_in, buffer + j, 1); | |
409 | + } while (ret == EOF && errno == EINTR); | |
410 | + if (ret != 1) | |
411 | + goto out; | |
412 | + if (!buffer[j]) | |
413 | + break; | |
414 | + } | |
415 | + if (j == sizeof(buffer) - 1) | |
416 | + goto out; | |
417 | + } else { | |
418 | + while (read(fd_in, buffer, sizeof(buffer) - 1) <= 0) { | |
419 | + /* Wait for data. */ | |
420 | + struct pollfd pfd = { | |
421 | + .fd = fd_in, | |
422 | + .events = POLLIN, | |
423 | + }; | |
424 | + if (poll(&pfd, 1, -1) == EOF && errno != EINTR) | |
425 | + goto out; | |
426 | + } | |
427 | + } | |
428 | + /* Split into two parts. */ | |
429 | + acl = strstr(buffer, " / "); | |
430 | + if (!acl) | |
431 | + continue; | |
432 | + *acl = '\0'; | |
433 | + acl += 3; | |
434 | + tail = strchr(acl, '\n'); | |
435 | + if (!tail) | |
436 | + continue; | |
437 | + *tail = '\0'; | |
438 | + block_sighup(1); | |
439 | + /* Check for filtering rules. */ | |
440 | + i = ccs_check_rules(buffer, acl); | |
441 | + if (i != EOF) { | |
442 | + *tail = '\n'; | |
443 | + *(acl - 3) = ' '; | |
444 | + /* Write the audit log. */ | |
445 | + if (!ccs_write_log(i, buffer)) | |
446 | + break; | |
447 | + } | |
448 | + block_sighup(0); | |
449 | + } | |
450 | +out: | |
451 | + syslog(LOG_WARNING, "Terminated.\n"); | |
452 | + closelog(); | |
453 | + return 1; | |
454 | +usage: | |
455 | + printf("Usage: %s [remote_ip:remote_port]\n\n" | |
456 | + "remote_ip:remote_port : Read from caitsith-agent listening " | |
457 | + "at remote_ip:remote_port .\n\n" | |
458 | + "See %s for configuration.\n", argv[0], CCS_AUDITD_CONF); | |
459 | + return 1; | |
460 | +} |
@@ -0,0 +1,100 @@ | ||
1 | +/* | |
2 | + * caitsith-loadpolicy.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +/** | |
26 | + * ccs_close_write - Close stream opened by ccs_open_write(). | |
27 | + * | |
28 | + * @fp: Pointer to "FILE". | |
29 | + * | |
30 | + * Returns true on success, false otherwise. | |
31 | + */ | |
32 | +static _Bool ccs_close_write(FILE *fp) | |
33 | +{ | |
34 | + _Bool result = true; | |
35 | + if (ccs_network_mode) { | |
36 | + if (fputc(0, fp) == EOF) | |
37 | + result = false; | |
38 | + if (fflush(fp) == EOF) | |
39 | + result = false; | |
40 | + if (fgetc(fp) == EOF) | |
41 | + result = false; | |
42 | + } | |
43 | + if (fclose(fp) == EOF) | |
44 | + result = false; | |
45 | + return result; | |
46 | +} | |
47 | + | |
48 | +static _Bool ccs_move_file_to_proc(const char *dest) | |
49 | +{ | |
50 | + FILE *proc_fp = ccs_open_write(dest); | |
51 | + _Bool result = true; | |
52 | + if (!proc_fp) { | |
53 | + fprintf(stderr, "Can't open %s for writing.\n", dest); | |
54 | + return false; | |
55 | + } | |
56 | + ccs_get(); | |
57 | + while (true) { | |
58 | + char *line = ccs_freadline(stdin); | |
59 | + if (!line) | |
60 | + break; | |
61 | + if (line[0]) | |
62 | + if (fprintf(proc_fp, "%s\n", line) < 0) | |
63 | + result = false; | |
64 | + } | |
65 | + ccs_put(); | |
66 | + if (!ccs_close_write(proc_fp)) | |
67 | + result = false; | |
68 | + return result; | |
69 | +} | |
70 | + | |
71 | +int main(int argc, char *argv[]) | |
72 | +{ | |
73 | + int i; | |
74 | + for (i = 1; i < argc; i++) { | |
75 | + char *ptr = argv[i]; | |
76 | + char *cp = strchr(ptr, ':'); | |
77 | + if (!cp) | |
78 | + goto usage; | |
79 | + *cp++ = '\0'; | |
80 | + ccs_network_ip = inet_addr(ptr); | |
81 | + ccs_network_port = htons(atoi(cp)); | |
82 | + if (ccs_network_mode) { | |
83 | + fprintf(stderr, "You cannot specify multiple " | |
84 | + "%s at the same time.\n\n", | |
85 | + "remote agents"); | |
86 | + goto usage; | |
87 | + } | |
88 | + ccs_network_mode = true; | |
89 | + } | |
90 | + if (ccs_network_mode) | |
91 | + ccs_check_remote_host(true); | |
92 | + else | |
93 | + ccs_check_policy_dir(true); | |
94 | + return !ccs_move_file_to_proc(CCS_PROC_POLICY_POLICY); | |
95 | +usage: | |
96 | + printf("Usage: %s [remote_ip:remote_port]\n\n" | |
97 | + "remote_ip:remote_port : Write to caitsith-agent listening at " | |
98 | + "remote_ip:remote_port .\n", argv[0]); | |
99 | + return 1; | |
100 | +} |
@@ -0,0 +1,252 @@ | ||
1 | +/* | |
2 | + * caitsith-notifyd.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include <sys/wait.h> | |
25 | +#include <signal.h> | |
26 | +#include <syslog.h> | |
27 | +#include <poll.h> | |
28 | + | |
29 | +#define CCS_NOTIFYD_CONF "/etc/caitsith/tools/notifyd.conf" | |
30 | + | |
31 | +static int query_fd = EOF; | |
32 | +static int time_to_wait = 0; | |
33 | +static char **action_to_take = NULL; | |
34 | +static int minimal_interval = 0; | |
35 | + | |
36 | +static void ccs_notifyd_init_rules(const char *filename) | |
37 | +{ | |
38 | + static _Bool first = 1; | |
39 | + FILE *fp = fopen(filename, "r"); | |
40 | + unsigned int line_no = 0; | |
41 | + char *action = NULL; | |
42 | + if (!first) { | |
43 | + free(action_to_take); | |
44 | + action_to_take = NULL; | |
45 | + time_to_wait = 0; | |
46 | + minimal_interval = 0; | |
47 | + } | |
48 | + if (!fp) { | |
49 | + if (first) | |
50 | + fprintf(stderr, "Can't open %s for reading.\n", | |
51 | + filename); | |
52 | + else | |
53 | + syslog(LOG_WARNING, "Can't open %s for reading.\n", | |
54 | + filename); | |
55 | + exit(1); | |
56 | + } | |
57 | + ccs_get(); | |
58 | + while (true) { | |
59 | + char *line = ccs_freadline(fp); | |
60 | + if (!line) | |
61 | + break; | |
62 | + line_no++; | |
63 | + ccs_normalize_line(line); | |
64 | + if (*line == '#' || !*line) | |
65 | + continue; | |
66 | + if (sscanf(line, "time_to_wait %u", &time_to_wait) == 1 || | |
67 | + sscanf(line, "minimal_interval %u", &minimal_interval) | |
68 | + == 1) | |
69 | + continue; | |
70 | + if (!ccs_str_starts(line, "action_to_take ")) | |
71 | + continue; | |
72 | + if (!*line) | |
73 | + goto invalid_rule; | |
74 | + if (action) | |
75 | + goto invalid_rule; | |
76 | + action = ccs_strdup(line); | |
77 | + } | |
78 | + ccs_put(); | |
79 | + fclose(fp); | |
80 | + if (!action) { | |
81 | + if (first) | |
82 | + fprintf(stderr, "No actions defined in %s .\n", | |
83 | + filename); | |
84 | + else | |
85 | + syslog(LOG_WARNING, "No actions defined in %s .\n", | |
86 | + filename); | |
87 | + exit(1); | |
88 | + } | |
89 | + { | |
90 | + int count = 0; | |
91 | + char *sp = action; | |
92 | + while (true) { | |
93 | + char *cp = strsep(&sp, " "); | |
94 | + action_to_take = ccs_realloc(action_to_take, | |
95 | + sizeof(char *) * ++count); | |
96 | + action_to_take[count - 1] = cp; | |
97 | + if (!cp) | |
98 | + break; | |
99 | + if (!ccs_decode(cp, cp)) | |
100 | + goto invalid_rule; | |
101 | + } | |
102 | + } | |
103 | + first = 0; | |
104 | + return; | |
105 | +invalid_rule: | |
106 | + if (first) | |
107 | + fprintf(stderr, "Invalid rule at line %u in %s .\n", line_no, | |
108 | + filename); | |
109 | + else | |
110 | + syslog(LOG_WARNING, "Invalid rule at line %u in %s .\n", | |
111 | + line_no, filename); | |
112 | + exit(1); | |
113 | +} | |
114 | + | |
115 | +static void block_sighup(const _Bool block) | |
116 | +{ | |
117 | + sigset_t sigset; | |
118 | + sigemptyset(&sigset); | |
119 | + sigaddset(&sigset, SIGHUP); | |
120 | + sigprocmask(block ? SIG_BLOCK : SIG_UNBLOCK, &sigset, NULL); | |
121 | +} | |
122 | + | |
123 | +static void main_loop(void) | |
124 | +{ | |
125 | + static char buffer[32768]; | |
126 | + while (query_fd != EOF) { | |
127 | + int pipe_fd[2]; | |
128 | + pid_t pid; | |
129 | + memset(buffer, 0, sizeof(buffer)); | |
130 | + while (read(query_fd, buffer, sizeof(buffer) - 1) <= 0) { | |
131 | + /* Wait for data. */ | |
132 | + struct pollfd pfd = { | |
133 | + .fd = query_fd, | |
134 | + .events = POLLIN, | |
135 | + }; | |
136 | + if (poll(&pfd, 1, -1) == EOF && errno != EINTR) | |
137 | + return; | |
138 | + } | |
139 | + if (pipe(pipe_fd) == EOF) { | |
140 | + syslog(LOG_WARNING, "Can't create pipe.\n"); | |
141 | + return; | |
142 | + } | |
143 | + block_sighup(1); | |
144 | + pid = fork(); | |
145 | + if (pid == -1) { | |
146 | + syslog(LOG_WARNING, "Can't fork().\n"); | |
147 | + return; | |
148 | + } | |
149 | + if (!pid) { | |
150 | + int ret_ignored; | |
151 | + ret_ignored = close(query_fd); | |
152 | + ret_ignored = close(pipe_fd[1]); | |
153 | + ret_ignored = close(0); | |
154 | + ret_ignored = dup2(pipe_fd[0], 0); | |
155 | + ret_ignored = close(pipe_fd[0]); | |
156 | + execvp(action_to_take[0], action_to_take); | |
157 | + syslog(LOG_WARNING, "Can't execute %s\n", | |
158 | + action_to_take[0]); | |
159 | + closelog(); | |
160 | + _exit(1); | |
161 | + } else { | |
162 | + int ret_ignored; | |
163 | + int len = strlen(buffer); | |
164 | + close(pipe_fd[0]); | |
165 | + /* This is OK because read() < sizeof(buffer). */ | |
166 | + buffer[len++] = '\n'; | |
167 | + ret_ignored = write(pipe_fd[1], buffer, len); | |
168 | + close(pipe_fd[1]); | |
169 | + } | |
170 | + block_sighup(0); | |
171 | + while (time_to_wait-- > 0) { | |
172 | + int ret_ignored; | |
173 | + sleep(1); | |
174 | + ret_ignored = write(query_fd, "\n", 1); | |
175 | + } | |
176 | + close(query_fd); | |
177 | + while (waitpid(pid, NULL, __WALL) == EOF && errno == EINTR); | |
178 | + sleep(minimal_interval); | |
179 | + do { | |
180 | + query_fd = open("query", O_RDWR); | |
181 | + } while (query_fd == EOF && errno == EINTR); | |
182 | + } | |
183 | +} | |
184 | + | |
185 | +static void ccs_reload_config(int sig) | |
186 | +{ | |
187 | + block_sighup(1); | |
188 | + syslog(LOG_WARNING, "Reloading configuration file.\n"); | |
189 | + ccs_notifyd_init_rules(CCS_NOTIFYD_CONF); | |
190 | + block_sighup(0); | |
191 | +} | |
192 | + | |
193 | +int main(int argc, char *argv[]) | |
194 | +{ | |
195 | + unsetenv("SHELLOPTS"); /* Make sure popen() executes commands. */ | |
196 | + if (argc != 1) | |
197 | + goto usage; | |
198 | + ccs_notifyd_init_rules(CCS_NOTIFYD_CONF); | |
199 | + ccs_check_policy_dir(true); | |
200 | + query_fd = open("query", O_RDWR); | |
201 | + if (query_fd == EOF) { | |
202 | + fprintf(stderr, "You can't run this daemon for this kernel." | |
203 | + "\n"); | |
204 | + return 1; | |
205 | + } else if (time_to_wait && write(query_fd, "", 0) != 0) { | |
206 | + fprintf(stderr, "You need to give this program permission to " | |
207 | + "modify policy.\n"); | |
208 | + return 1; | |
209 | + } | |
210 | + umask(0); | |
211 | + switch (fork()) { | |
212 | + case 0: | |
213 | + break; | |
214 | + case -1: | |
215 | + fprintf(stderr, "Can't fork()\n"); | |
216 | + return 1; | |
217 | + default: | |
218 | + return 0; | |
219 | + } | |
220 | + if (setsid() == EOF) { | |
221 | + fprintf(stderr, "Can't setsid()\n"); | |
222 | + return 1; | |
223 | + } | |
224 | + switch (fork()) { | |
225 | + case 0: | |
226 | + break; | |
227 | + case -1: | |
228 | + fprintf(stderr, "Can't fork()\n"); | |
229 | + return 1; | |
230 | + default: | |
231 | + return 0; | |
232 | + } | |
233 | + { /* Get exclusive lock. */ | |
234 | + int fd = open("/proc/self/exe", O_RDONLY); | |
235 | + if (flock(fd, LOCK_EX | LOCK_NB) == EOF) | |
236 | + return 0; | |
237 | + } | |
238 | + close(0); | |
239 | + close(1); | |
240 | + close(2); | |
241 | + openlog("caitsith-notifyd", 0, LOG_USER); | |
242 | + syslog(LOG_WARNING, "Started.\n"); | |
243 | + signal(SIGHUP, ccs_reload_config); | |
244 | + main_loop(); | |
245 | + syslog(LOG_WARNING, "Terminated.\n"); | |
246 | + closelog(); | |
247 | + return 1; | |
248 | +usage: | |
249 | + printf("Usage: %s\n\nSee %s for configuration.\n", argv[0], | |
250 | + CCS_NOTIFYD_CONF); | |
251 | + return 1; | |
252 | +} |
@@ -0,0 +1,387 @@ | ||
1 | +/* | |
2 | + * caitsith-pstree.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +struct ccs_task_entry { | |
26 | + pid_t pid; | |
27 | + pid_t ppid; | |
28 | + char *name; | |
29 | + char *domain; | |
30 | + _Bool selected; | |
31 | + int index; | |
32 | + int depth; | |
33 | +}; | |
34 | + | |
35 | +/* The list of processes currently running. */ | |
36 | +static struct ccs_task_entry *ccs_task_list = NULL; | |
37 | +/* The length of ccs_task_list . */ | |
38 | +static int ccs_task_list_len = 0; | |
39 | + | |
40 | +/* Serial number for sorting ccs_task_list . */ | |
41 | +static int ccs_dump_index = 0; | |
42 | + | |
43 | +/** | |
44 | + * ccs_sort_process_entry - Sort ccs_tasklist list. | |
45 | + * | |
46 | + * @pid: Pid to search. | |
47 | + * @depth: Depth of the process for printing like pstree command. | |
48 | + * | |
49 | + * Returns nothing. | |
50 | + */ | |
51 | +static void ccs_sort_process_entry(const pid_t pid, const int depth) | |
52 | +{ | |
53 | + int i; | |
54 | + for (i = 0; i < ccs_task_list_len; i++) { | |
55 | + if (pid != ccs_task_list[i].pid) | |
56 | + continue; | |
57 | + ccs_task_list[i].index = ccs_dump_index++; | |
58 | + ccs_task_list[i].depth = depth; | |
59 | + ccs_task_list[i].selected = true; | |
60 | + } | |
61 | + for (i = 0; i < ccs_task_list_len; i++) { | |
62 | + if (pid != ccs_task_list[i].ppid) | |
63 | + continue; | |
64 | + ccs_sort_process_entry(ccs_task_list[i].pid, depth + 1); | |
65 | + } | |
66 | +} | |
67 | + | |
68 | +/** | |
69 | + * ccs_task_entry_compare - Compare routine for qsort() callback. | |
70 | + * | |
71 | + * @a: Pointer to "void". | |
72 | + * @b: Pointer to "void". | |
73 | + * | |
74 | + * Returns index diff value. | |
75 | + */ | |
76 | +static int ccs_task_entry_compare(const void *a, const void *b) | |
77 | +{ | |
78 | + const struct ccs_task_entry *a0 = (struct ccs_task_entry *) a; | |
79 | + const struct ccs_task_entry *b0 = (struct ccs_task_entry *) b; | |
80 | + return a0->index - b0->index; | |
81 | +} | |
82 | + | |
83 | +/** | |
84 | + * ccs_add_process_entry - Add entry for running processes. | |
85 | + * | |
86 | + * @line: A line containing PID and domainname. | |
87 | + * @ppid: Parent PID. | |
88 | + * @name: Comm name (allocated by strdup()). | |
89 | + * | |
90 | + * Returns nothing. | |
91 | + * | |
92 | + * @name is free()d on failure. | |
93 | + */ | |
94 | +static void ccs_add_process_entry(const char *line, const pid_t ppid, | |
95 | + char *name) | |
96 | +{ | |
97 | + int index; | |
98 | + unsigned int pid = 0; | |
99 | + char *domain; | |
100 | + if (!line || sscanf(line, "%u", &pid) != 1) { | |
101 | + free(name); | |
102 | + return; | |
103 | + } | |
104 | + domain = strchr(line, ' '); | |
105 | + if (domain++) | |
106 | + domain = ccs_strdup(domain); | |
107 | + else | |
108 | + domain = ccs_strdup("<UNKNOWN>"); | |
109 | + index = ccs_task_list_len++; | |
110 | + ccs_task_list = ccs_realloc(ccs_task_list, ccs_task_list_len * | |
111 | + sizeof(struct ccs_task_entry)); | |
112 | + memset(&ccs_task_list[index], 0, sizeof(ccs_task_list[0])); | |
113 | + ccs_task_list[index].pid = pid; | |
114 | + ccs_task_list[index].ppid = ppid; | |
115 | + ccs_task_list[index].name = name; | |
116 | + ccs_task_list[index].domain = domain; | |
117 | +} | |
118 | + | |
119 | +/** | |
120 | + * ccs_get_ppid - Get PPID of the given PID. | |
121 | + * | |
122 | + * @pid: A pid_t value. | |
123 | + * | |
124 | + * Returns PPID value. | |
125 | + */ | |
126 | +static pid_t ccs_get_ppid(const pid_t pid) | |
127 | +{ | |
128 | + char buffer[1024]; | |
129 | + FILE *fp; | |
130 | + pid_t ppid = 1; | |
131 | + memset(buffer, 0, sizeof(buffer)); | |
132 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); | |
133 | + fp = fopen(buffer, "r"); | |
134 | + if (fp) { | |
135 | + while (memset(buffer, 0, sizeof(buffer)) && | |
136 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
137 | + if (sscanf(buffer, "PPid: %u", &ppid) == 1) | |
138 | + break; | |
139 | + } | |
140 | + fclose(fp); | |
141 | + } | |
142 | + return ppid; | |
143 | +} | |
144 | + | |
145 | +/** | |
146 | + * ccs_get_name - Get comm name of the given PID. | |
147 | + * | |
148 | + * @pid: A pid_t value. | |
149 | + * | |
150 | + * Returns comm name using on success, NULL otherwise. | |
151 | + * | |
152 | + * The caller must free() the returned pointer. | |
153 | + */ | |
154 | +static char *ccs_get_name(const pid_t pid) | |
155 | +{ | |
156 | + char buffer[1024]; | |
157 | + FILE *fp; | |
158 | + memset(buffer, 0, sizeof(buffer)); | |
159 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); | |
160 | + fp = fopen(buffer, "r"); | |
161 | + if (fp) { | |
162 | + static const int offset = sizeof(buffer) / 6; | |
163 | + while (memset(buffer, 0, sizeof(buffer)) && | |
164 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
165 | + if (!strncmp(buffer, "Name:\t", 6)) { | |
166 | + char *cp = buffer + 6; | |
167 | + memmove(buffer, cp, strlen(cp) + 1); | |
168 | + cp = strchr(buffer, '\n'); | |
169 | + if (cp) | |
170 | + *cp = '\0'; | |
171 | + break; | |
172 | + } | |
173 | + } | |
174 | + fclose(fp); | |
175 | + if (buffer[0] && strlen(buffer) < offset - 1) { | |
176 | + const char *src = buffer; | |
177 | + char *dest = buffer + offset; | |
178 | + while (1) { | |
179 | + unsigned char c = *src++; | |
180 | + if (!c) { | |
181 | + *dest = '\0'; | |
182 | + break; | |
183 | + } | |
184 | + if (c == '\\') { | |
185 | + c = *src++; | |
186 | + if (c == '\\') { | |
187 | + memmove(dest, "\\134", 4); | |
188 | + dest += 4; | |
189 | + } else if (c == 'n') { | |
190 | + memmove(dest, "\\012", 4); | |
191 | + dest += 4; | |
192 | + } else { | |
193 | + break; | |
194 | + } | |
195 | + } else if (c > ' ' && c <= 126) { | |
196 | + *dest++ = c; | |
197 | + } else { | |
198 | + *dest++ = '\\'; | |
199 | + *dest++ = (c >> 6) + '0'; | |
200 | + *dest++ = ((c >> 3) & 7) + '0'; | |
201 | + *dest++ = (c & 7) + '0'; | |
202 | + } | |
203 | + } | |
204 | + return strdup(buffer + offset); | |
205 | + } | |
206 | + } | |
207 | + return NULL; | |
208 | +} | |
209 | + | |
210 | +/** | |
211 | + * ccs_read_process_list - Read all process's information. | |
212 | + * | |
213 | + * @show_all: Ture if kernel threads should be included, false otherwise. | |
214 | + * | |
215 | + * Returns nothing. | |
216 | + */ | |
217 | +static void ccs_read_process_list(_Bool show_all) | |
218 | +{ | |
219 | + int i; | |
220 | + while (ccs_task_list_len) { | |
221 | + ccs_task_list_len--; | |
222 | + free((void *) ccs_task_list[ccs_task_list_len].name); | |
223 | + free((void *) ccs_task_list[ccs_task_list_len].domain); | |
224 | + } | |
225 | + ccs_dump_index = 0; | |
226 | + if (ccs_network_mode) { | |
227 | + FILE *fp = ccs_open_write(show_all ? | |
228 | + "proc:all_process_status" : | |
229 | + "proc:process_status"); | |
230 | + if (!fp) | |
231 | + return; | |
232 | + ccs_get(); | |
233 | + while (true) { | |
234 | + char *line = ccs_freadline(fp); | |
235 | + unsigned int pid = 0; | |
236 | + unsigned int ppid = 0; | |
237 | + char *name; | |
238 | + if (!line) | |
239 | + break; | |
240 | + sscanf(line, "PID=%u PPID=%u", &pid, &ppid); | |
241 | + name = strstr(line, "NAME="); | |
242 | + if (name) | |
243 | + name = ccs_strdup(name + 5); | |
244 | + else | |
245 | + name = ccs_strdup("<UNKNOWN>"); | |
246 | + line = ccs_freadline(fp); | |
247 | + ccs_add_process_entry(line, ppid, name); | |
248 | + } | |
249 | + ccs_put(); | |
250 | + fclose(fp); | |
251 | + } else { | |
252 | + static const int line_len = 8192; | |
253 | + char *line; | |
254 | + int status_fd = open(".process_status", O_RDWR); | |
255 | + DIR *dir = opendir("/proc/"); | |
256 | + if (status_fd == EOF || !dir) { | |
257 | + if (status_fd != EOF) | |
258 | + close(status_fd); | |
259 | + if (dir) | |
260 | + closedir(dir); | |
261 | + return; | |
262 | + } | |
263 | + line = ccs_malloc(line_len); | |
264 | + while (1) { | |
265 | + char *name; | |
266 | + int ret_ignored; | |
267 | + unsigned int pid = 0; | |
268 | + char buffer[128]; | |
269 | + char test[16]; | |
270 | + struct dirent *dent = readdir(dir); | |
271 | + if (!dent) | |
272 | + break; | |
273 | + if (dent->d_type != DT_DIR || | |
274 | + sscanf(dent->d_name, "%u", &pid) != 1 || !pid) | |
275 | + continue; | |
276 | + memset(buffer, 0, sizeof(buffer)); | |
277 | + if (!show_all) { | |
278 | + snprintf(buffer, sizeof(buffer) - 1, | |
279 | + "/proc/%u/exe", pid); | |
280 | + if (readlink(buffer, test, sizeof(test)) <= 0) | |
281 | + continue; | |
282 | + } | |
283 | + name = ccs_get_name(pid); | |
284 | + if (!name) | |
285 | + name = ccs_strdup("<UNKNOWN>"); | |
286 | + snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid); | |
287 | + ret_ignored = write(status_fd, buffer, strlen(buffer)); | |
288 | + memset(line, 0, line_len); | |
289 | + ret_ignored = read(status_fd, line, line_len - 1); | |
290 | + ccs_add_process_entry(line, ccs_get_ppid(pid), name); | |
291 | + } | |
292 | + free(line); | |
293 | + closedir(dir); | |
294 | + close(status_fd); | |
295 | + } | |
296 | + ccs_sort_process_entry(1, 0); | |
297 | + for (i = 0; i < ccs_task_list_len; i++) { | |
298 | + if (ccs_task_list[i].selected) { | |
299 | + ccs_task_list[i].selected = false; | |
300 | + continue; | |
301 | + } | |
302 | + ccs_task_list[i].index = ccs_dump_index++; | |
303 | + ccs_task_list[i].depth = 0; | |
304 | + } | |
305 | + qsort(ccs_task_list, ccs_task_list_len, sizeof(struct ccs_task_entry), | |
306 | + ccs_task_entry_compare); | |
307 | +} | |
308 | + | |
309 | +static void ccs_dump(const pid_t pid, const int depth) | |
310 | +{ | |
311 | + int i; | |
312 | + for (i = 0; i < ccs_task_list_len; i++) { | |
313 | + int j; | |
314 | + if (pid != ccs_task_list[i].pid) | |
315 | + continue; | |
316 | + for (j = 0; j < depth - 1; j++) | |
317 | + printf(" "); | |
318 | + for (; j < depth; j++) | |
319 | + printf(" +-"); | |
320 | + printf(" %s (%u) %s\n", ccs_task_list[i].name, | |
321 | + ccs_task_list[i].pid, ccs_task_list[i].domain); | |
322 | + ccs_task_list[i].selected = true; | |
323 | + } | |
324 | + for (i = 0; i < ccs_task_list_len; i++) { | |
325 | + if (pid != ccs_task_list[i].ppid) | |
326 | + continue; | |
327 | + ccs_dump(ccs_task_list[i].pid, depth + 1); | |
328 | + } | |
329 | +} | |
330 | + | |
331 | +int main(int argc, char *argv[]) | |
332 | +{ | |
333 | + static _Bool show_all = false; | |
334 | + int i; | |
335 | + for (i = 1; i < argc; i++) { | |
336 | + char *ptr = argv[i]; | |
337 | + char *cp = strchr(ptr, ':'); | |
338 | + if (cp) { | |
339 | + *cp++ = '\0'; | |
340 | + if (ccs_network_mode) | |
341 | + goto usage; | |
342 | + ccs_network_ip = inet_addr(ptr); | |
343 | + ccs_network_port = htons(atoi(cp)); | |
344 | + ccs_network_mode = true; | |
345 | + } else if (!strcmp(ptr, "-a")) { | |
346 | + show_all = true; | |
347 | + } else | |
348 | + goto usage; | |
349 | + } | |
350 | + if (ccs_network_mode) | |
351 | + ccs_check_remote_host(true); | |
352 | + else | |
353 | + ccs_check_policy_dir(true); | |
354 | + ccs_read_process_list(show_all); | |
355 | + if (!ccs_task_list_len) { | |
356 | + if (ccs_network_mode) { | |
357 | + fprintf(stderr, "Can't connect.\n"); | |
358 | + return 1; | |
359 | + } else { | |
360 | + fprintf(stderr, "You can't use this command " | |
361 | + "for this kernel.\n"); | |
362 | + return 1; | |
363 | + } | |
364 | + } | |
365 | + ccs_dump(1, 0); | |
366 | + for (i = 0; i < ccs_task_list_len; i++) { | |
367 | + if (ccs_task_list[i].selected) | |
368 | + continue; | |
369 | + printf(" %s (%u) %s\n", ccs_task_list[i].name, | |
370 | + ccs_task_list[i].pid, ccs_task_list[i].domain); | |
371 | + ccs_task_list[i].selected = true; | |
372 | + } | |
373 | + while (ccs_task_list_len) { | |
374 | + ccs_task_list_len--; | |
375 | + free((void *) ccs_task_list[ccs_task_list_len].name); | |
376 | + free((void *) ccs_task_list[ccs_task_list_len].domain); | |
377 | + } | |
378 | + free(ccs_task_list); | |
379 | + ccs_task_list = NULL; | |
380 | + return 0; | |
381 | +usage: | |
382 | + printf("Usage: %s [-a] [remote_ip:remote_port]\n\n" | |
383 | + "-a : Print all processes including kernel threads.\n" | |
384 | + "remote_ip:remote_port : Read from caitsith-agent listening " | |
385 | + "at remote_ip:remote_port .\n", argv[0]); | |
386 | + return 0; | |
387 | +} |
@@ -0,0 +1,326 @@ | ||
1 | +/* | |
2 | + * caitsith-queryd.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | +#include "readline.h" | |
25 | + | |
26 | +/* Prototypes */ | |
27 | + | |
28 | +static void ccs_printw(const char *fmt, ...) | |
29 | + __attribute__ ((format(printf, 1, 2))); | |
30 | +static _Bool ccs_handle_query(unsigned int serial); | |
31 | + | |
32 | +/* Utility functions */ | |
33 | + | |
34 | +static void ccs_printw(const char *fmt, ...) | |
35 | +{ | |
36 | + va_list args; | |
37 | + int i; | |
38 | + int len; | |
39 | + char *buffer; | |
40 | + va_start(args, fmt); | |
41 | + len = vsnprintf((char *) &i, sizeof(i) - 1, fmt, args) + 16; | |
42 | + va_end(args); | |
43 | + buffer = ccs_malloc(len); | |
44 | + va_start(args, fmt); | |
45 | + len = vsnprintf(buffer, len, fmt, args); | |
46 | + va_end(args); | |
47 | + for (i = 0; i < len; i++) { | |
48 | + addch(buffer[i]); | |
49 | + refresh(); | |
50 | + } | |
51 | + free(buffer); | |
52 | +} | |
53 | + | |
54 | +static void ccs_send_keepalive(void) | |
55 | +{ | |
56 | + static time_t previous = 0; | |
57 | + time_t now = time(NULL); | |
58 | + if (previous != now || !previous) { | |
59 | + int ret_ignored; | |
60 | + previous = now; | |
61 | + ret_ignored = write(ccs_query_fd, "\n", 1); | |
62 | + } | |
63 | +} | |
64 | + | |
65 | +/* Variables */ | |
66 | + | |
67 | +static unsigned short int ccs_retries = 0; | |
68 | + | |
69 | +static FILE *ccs_policy_fp = NULL; | |
70 | +static int ccs_policy_fd = EOF; | |
71 | +#define CCS_MAX_READLINE_HISTORY 20 | |
72 | +static const char **ccs_readline_history = NULL; | |
73 | +static int ccs_readline_history_count = 0; | |
74 | +static char ccs_buffer[32768]; | |
75 | + | |
76 | +/* Main functions */ | |
77 | + | |
78 | +static _Bool ccs_handle_query(unsigned int serial) | |
79 | +{ | |
80 | + int c = 0; | |
81 | + int y; | |
82 | + int x; | |
83 | + int ret_ignored; | |
84 | + char *line = NULL; | |
85 | + static unsigned int prev_pid = 0; | |
86 | + unsigned int pid; | |
87 | + char qidbuf[128]; | |
88 | + char *cp = strstr(ccs_buffer, " global-pid="); | |
89 | + if (!cp || sscanf(cp + 13, "%u", &pid) != 1) { | |
90 | + ccs_printw("ERROR: Unsupported query.\n"); | |
91 | + return false; | |
92 | + } | |
93 | + cp = ccs_buffer + strlen(ccs_buffer); | |
94 | + if (*(cp - 1) != '\n') { | |
95 | + ccs_printw("ERROR: Unsupported query.\n"); | |
96 | + return false; | |
97 | + } | |
98 | + *(cp - 1) = '\0'; | |
99 | + if (pid != prev_pid) { | |
100 | + if (prev_pid) | |
101 | + ccs_printw("----------------------------------------" | |
102 | + "\n"); | |
103 | + prev_pid = pid; | |
104 | + } | |
105 | + ccs_printw("%s\n", ccs_buffer); | |
106 | + memset(qidbuf, 0, sizeof(qidbuf)); | |
107 | + snprintf(qidbuf, sizeof(qidbuf) - 1, "Q=%u\n", serial); | |
108 | + ccs_printw("Allow? ('Y'es/'N'o/'R'etry/'S'how policy/'A'dd to policy " | |
109 | + "and retry):"); | |
110 | + while (true) { | |
111 | + c = ccs_getch2(); | |
112 | + if (c == 'Y' || c == 'y' || c == 'N' || c == 'n' || c == 'R' || | |
113 | + c == 'r' || c == 'A' || c == 'a' || c == 'S' || c == 's') | |
114 | + break; | |
115 | + ccs_send_keepalive(); | |
116 | + } | |
117 | + ccs_printw("%c\n", c); | |
118 | + | |
119 | + if (c == 'S' || c == 's') { | |
120 | + if (ccs_network_mode) { | |
121 | + fprintf(ccs_policy_fp, "%s", qidbuf); | |
122 | + fputc(0, ccs_policy_fp); | |
123 | + fflush(ccs_policy_fp); | |
124 | + rewind(ccs_policy_fp); | |
125 | + while (1) { | |
126 | + char c; | |
127 | + if (fread(&c, 1, 1, ccs_policy_fp) != 1 || !c) | |
128 | + break; | |
129 | + addch(c); | |
130 | + refresh(); | |
131 | + ccs_send_keepalive(); | |
132 | + } | |
133 | + } else { | |
134 | + ret_ignored = write(ccs_policy_fd, qidbuf, | |
135 | + strlen(qidbuf)); | |
136 | + while (1) { | |
137 | + int i; | |
138 | + int len = read(ccs_policy_fd, ccs_buffer, | |
139 | + sizeof(ccs_buffer) - 1); | |
140 | + if (len <= 0) | |
141 | + break; | |
142 | + for (i = 0; i < len; i++) { | |
143 | + addch(ccs_buffer[i]); | |
144 | + refresh(); | |
145 | + } | |
146 | + ccs_send_keepalive(); | |
147 | + } | |
148 | + } | |
149 | + c = 'r'; | |
150 | + } | |
151 | + | |
152 | + /* Append to policy. */ | |
153 | + if (c != 'A' && c != 'a') | |
154 | + goto not_append; | |
155 | + c = 'r'; | |
156 | + getyx(stdscr, y, x); | |
157 | + cp = strstr(ccs_buffer, " / "); | |
158 | + if (!cp++) | |
159 | + return false; | |
160 | + cp = strchr(cp + 2, ' '); | |
161 | + if (!cp++) | |
162 | + return false; | |
163 | + ccs_initial_readline_data = NULL; | |
164 | + ccs_readline_history_count = | |
165 | + ccs_add_history(cp, ccs_readline_history, | |
166 | + ccs_readline_history_count, | |
167 | + CCS_MAX_READLINE_HISTORY); | |
168 | + line = ccs_readline(y, 0, "Enter new entry> ", ccs_readline_history, | |
169 | + ccs_readline_history_count, 128000, 8); | |
170 | + scrollok(stdscr, TRUE); | |
171 | + ccs_printw("\n"); | |
172 | + if (!line || !*line) { | |
173 | + ccs_printw("None added.\n"); | |
174 | + goto not_append; | |
175 | + } | |
176 | + ccs_readline_history_count = | |
177 | + ccs_add_history(line, ccs_readline_history, | |
178 | + ccs_readline_history_count, | |
179 | + CCS_MAX_READLINE_HISTORY); | |
180 | + if (ccs_network_mode) { | |
181 | + fprintf(ccs_policy_fp, "%s%s\n", qidbuf, line); | |
182 | + fflush(ccs_policy_fp); | |
183 | + } else { | |
184 | + ret_ignored = write(ccs_policy_fd, qidbuf, strlen(qidbuf)); | |
185 | + ret_ignored = write(ccs_policy_fd, line, strlen(line)); | |
186 | + ret_ignored = write(ccs_policy_fd, "\n", 1); | |
187 | + } | |
188 | + ccs_printw("Added '%s'.\n", line); | |
189 | +not_append: | |
190 | + free(line); | |
191 | + /* Write answer. */ | |
192 | + if (c == 'Y' || c == 'y' || c == 'A' || c == 'a') | |
193 | + c = 1; | |
194 | + else if (c == 'R' || c == 'r') | |
195 | + c = 3; | |
196 | + else | |
197 | + c = 2; | |
198 | + snprintf(ccs_buffer, sizeof(ccs_buffer) - 1, "A%u=%u\n", serial, c); | |
199 | + ret_ignored = write(ccs_query_fd, ccs_buffer, strlen(ccs_buffer)); | |
200 | + ccs_printw("\n"); | |
201 | + return true; | |
202 | +} | |
203 | + | |
204 | +int main(int argc, char *argv[]) | |
205 | +{ | |
206 | + if (argc == 1) | |
207 | + goto ok; | |
208 | + { | |
209 | + char *cp = strchr(argv[1], ':'); | |
210 | + if (cp) { | |
211 | + *cp++ = '\0'; | |
212 | + ccs_network_ip = inet_addr(argv[1]); | |
213 | + ccs_network_port = htons(atoi(cp)); | |
214 | + ccs_network_mode = true; | |
215 | + goto ok; | |
216 | + } | |
217 | + } | |
218 | + printf("Usage: %s [remote_ip:remote_port]\n\n" | |
219 | + "remote_ip:remote_port : Read from and reply to caitsith-agent " | |
220 | + "listening at remote_ip:remote_port .\n\n", argv[0]); | |
221 | + printf("This program is used for granting access requests manually." | |
222 | + "\n"); | |
223 | + printf("This program shows access requests that are about to be " | |
224 | + "rejected by the kernel's decision.\n"); | |
225 | + printf("If you answer before the kernel's decision takes effect, your " | |
226 | + "decision will take effect.\n"); | |
227 | + printf("You can use this program to respond to accidental access " | |
228 | + "requests triggered by non-routine tasks (such as restarting " | |
229 | + "daemons after updating).\n"); | |
230 | + printf("To terminate this program, use 'Ctrl-C'.\n"); | |
231 | + return 0; | |
232 | +ok: | |
233 | + if (ccs_network_mode) { | |
234 | + ccs_check_remote_host(true); | |
235 | + ccs_query_fd = ccs_open_stream("proc:query"); | |
236 | + ccs_policy_fp = ccs_open_write(CCS_PROC_POLICY_POLICY); | |
237 | + } else { | |
238 | + ccs_check_policy_dir(true); | |
239 | + ccs_query_fd = open("query", O_RDWR); | |
240 | + ccs_policy_fd = open("policy", O_RDWR); | |
241 | + } | |
242 | + if (ccs_query_fd == EOF) { | |
243 | + fprintf(stderr, | |
244 | + "You can't run this utility for this kernel.\n"); | |
245 | + return 1; | |
246 | + } else if (!ccs_network_mode && write(ccs_query_fd, "", 0) != 0) { | |
247 | + fprintf(stderr, "You need to allow this program in " | |
248 | + "%s/policy to run this program.\n", | |
249 | + CCS_PROC_POLICY_DIR); | |
250 | + return 1; | |
251 | + } | |
252 | + ccs_readline_history = ccs_malloc(CCS_MAX_READLINE_HISTORY * | |
253 | + sizeof(const char *)); | |
254 | + ccs_send_keepalive(); | |
255 | + initscr(); | |
256 | + cbreak(); | |
257 | + noecho(); | |
258 | + nonl(); | |
259 | + intrflush(stdscr, FALSE); | |
260 | + keypad(stdscr, TRUE); | |
261 | + clear(); | |
262 | + refresh(); | |
263 | + scrollok(stdscr, TRUE); | |
264 | + if (ccs_network_mode) { | |
265 | + const u32 ip = ntohl(ccs_network_ip); | |
266 | + ccs_printw("Monitoring %s/query via %u.%u.%u.%u:%u.", | |
267 | + CCS_PROC_POLICY_DIR, (u8) (ip >> 24), | |
268 | + (u8) (ip >> 16), (u8) (ip >> 8), (u8) ip, | |
269 | + ntohs(ccs_network_port)); | |
270 | + } else | |
271 | + ccs_printw("Monitoring %s/query .", CCS_PROC_POLICY_DIR); | |
272 | + ccs_printw(" Press Ctrl-C to terminate.\n\n"); | |
273 | + while (true) { | |
274 | + unsigned int serial; | |
275 | + char *cp; | |
276 | + /* Wait for query and read query. */ | |
277 | + memset(ccs_buffer, 0, sizeof(ccs_buffer)); | |
278 | + if (ccs_network_mode) { | |
279 | + int i; | |
280 | + int ret_ignored; | |
281 | + ret_ignored = write(ccs_query_fd, "", 1); | |
282 | + for (i = 0; i < sizeof(ccs_buffer) - 1; i++) { | |
283 | + if (read(ccs_query_fd, ccs_buffer + i, 1) != 1) | |
284 | + break; | |
285 | + if (!ccs_buffer[i]) | |
286 | + goto read_ok; | |
287 | + } | |
288 | + break; | |
289 | + } else { | |
290 | + struct pollfd pfd; | |
291 | + pfd.fd = ccs_query_fd; | |
292 | + pfd.events = POLLIN; | |
293 | + pfd.revents = 0; | |
294 | + poll(&pfd, 1, -1); | |
295 | + if (!(pfd.revents & POLLIN)) | |
296 | + continue; | |
297 | + if (read(ccs_query_fd, ccs_buffer, | |
298 | + sizeof(ccs_buffer) - 1) <= 0) | |
299 | + continue; | |
300 | + } | |
301 | +read_ok: | |
302 | + cp = strchr(ccs_buffer, '\n'); | |
303 | + if (!cp) | |
304 | + continue; | |
305 | + *cp = '\0'; | |
306 | + | |
307 | + /* Get query number. */ | |
308 | + if (sscanf(ccs_buffer, "Q%u-%hu", &serial, &ccs_retries) != 2) | |
309 | + continue; | |
310 | + memmove(ccs_buffer, cp + 1, strlen(cp + 1) + 1); | |
311 | + | |
312 | + /* Clear pending input. */; | |
313 | + timeout(0); | |
314 | + while (true) { | |
315 | + int c = ccs_getch2(); | |
316 | + if (c == EOF || c == ERR) | |
317 | + break; | |
318 | + } | |
319 | + timeout(1000); | |
320 | + if (ccs_handle_query(serial)) | |
321 | + continue; | |
322 | + break; | |
323 | + } | |
324 | + endwin(); | |
325 | + return 0; | |
326 | +} |
@@ -0,0 +1,167 @@ | ||
1 | +/* | |
2 | + * caitsith-savepolicy.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include "caitsithtools.h" | |
24 | + | |
25 | +/** | |
26 | + * ccs_move_proc_to_file - Save /sys/kernel/security/caitsith/ to /etc/caitsith/ . | |
27 | + * | |
28 | + * @src: Filename to save from. | |
29 | + * @dest: Filename to save to. | |
30 | + * | |
31 | + * Returns true on success, false otherwise. | |
32 | + */ | |
33 | +static _Bool ccs_move_proc_to_file(const char *src, const char *dest) | |
34 | +{ | |
35 | + FILE *proc_fp = ccs_open_read(src); | |
36 | + FILE *file_fp; | |
37 | + _Bool result = true; | |
38 | + if (!proc_fp) { | |
39 | + fprintf(stderr, "Can't open %s for reading.\n", src); | |
40 | + return false; | |
41 | + } | |
42 | + file_fp = dest ? fopen(dest, "w") : stdout; | |
43 | + if (!file_fp) { | |
44 | + fprintf(stderr, "Can't open %s for writing.\n", dest); | |
45 | + fclose(proc_fp); | |
46 | + return false; | |
47 | + } | |
48 | + while (true) { | |
49 | + const int c = fgetc(proc_fp); | |
50 | + if (ccs_network_mode && !c) | |
51 | + break; | |
52 | + if (c == EOF) | |
53 | + break; | |
54 | + if (fputc(c, file_fp) == EOF) | |
55 | + result = false; | |
56 | + } | |
57 | + fclose(proc_fp); | |
58 | + if (file_fp != stdout) | |
59 | + if (fclose(file_fp) == EOF) | |
60 | + result = false; | |
61 | + return result; | |
62 | +} | |
63 | + | |
64 | +static const char *ccs_policy_dir = NULL; | |
65 | + | |
66 | +static _Bool ccs_cat_file(const char *path) | |
67 | +{ | |
68 | + FILE *fp = ccs_open_read(path); | |
69 | + _Bool result = true; | |
70 | + if (!fp) { | |
71 | + fprintf(stderr, "Can't open %s\n", path); | |
72 | + return false; | |
73 | + } | |
74 | + while (true) { | |
75 | + int c = fgetc(fp); | |
76 | + if (ccs_network_mode && !c) | |
77 | + break; | |
78 | + if (c == EOF) | |
79 | + break; | |
80 | + if (putchar(c) == EOF) | |
81 | + result = false; | |
82 | + } | |
83 | + fclose(fp); | |
84 | + return result; | |
85 | +} | |
86 | + | |
87 | +static _Bool ccs_save_policy(void) | |
88 | +{ | |
89 | + time_t now = time(NULL); | |
90 | + char stamp[32] = { }; | |
91 | + while (1) { | |
92 | + struct tm *tm = localtime(&now); | |
93 | + snprintf(stamp, sizeof(stamp) - 1, | |
94 | + "%02d-%02d-%02d.%02d:%02d:%02d", | |
95 | + tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, | |
96 | + tm->tm_hour, tm->tm_min, tm->tm_sec); | |
97 | + if (access(stamp, F_OK)) | |
98 | + break; | |
99 | + else if (errno == EEXIST) | |
100 | + now++; | |
101 | + else { | |
102 | + fprintf(stderr, "Can't create %s/policy/%s .\n", | |
103 | + ccs_policy_dir, stamp); | |
104 | + return false; | |
105 | + } | |
106 | + } | |
107 | + if (!ccs_move_proc_to_file(CCS_PROC_POLICY_POLICY, stamp) || | |
108 | + (rename("current", "previous") && errno != ENOENT) || | |
109 | + symlink(stamp, "current")) { | |
110 | + fprintf(stderr, "Failed to save policy.\n"); | |
111 | + return false; | |
112 | + } | |
113 | + return true; | |
114 | +} | |
115 | + | |
116 | +int main(int argc, char *argv[]) | |
117 | +{ | |
118 | + _Bool use_stdout = false; | |
119 | + int i; | |
120 | + for (i = 1; i < argc; i++) { | |
121 | + char *ptr = argv[i]; | |
122 | + char *cp = strchr(ptr, ':'); | |
123 | + if (*ptr == '/') { | |
124 | + if (ccs_policy_dir || use_stdout) | |
125 | + goto usage; | |
126 | + ccs_policy_dir = ptr; | |
127 | + } else if (cp) { | |
128 | + *cp++ = '\0'; | |
129 | + ccs_network_ip = inet_addr(ptr); | |
130 | + ccs_network_port = htons(atoi(cp)); | |
131 | + if (ccs_network_mode) { | |
132 | + fprintf(stderr, "You cannot specify multiple " | |
133 | + "%s at the same time.\n\n", | |
134 | + "remote agents"); | |
135 | + goto usage; | |
136 | + } | |
137 | + ccs_network_mode = true; | |
138 | + } else if (*ptr++ == '-' && !*ptr) { | |
139 | + if (ccs_policy_dir || use_stdout) | |
140 | + goto usage; | |
141 | + use_stdout = true; | |
142 | + } else | |
143 | + goto usage; | |
144 | + } | |
145 | + if (ccs_network_mode) | |
146 | + ccs_check_remote_host(true); | |
147 | + else | |
148 | + ccs_check_policy_dir(true); | |
149 | + if (use_stdout) | |
150 | + return !ccs_cat_file(CCS_PROC_POLICY_POLICY); | |
151 | + if (!ccs_policy_dir) | |
152 | + ccs_policy_dir = "/etc/caitsith"; | |
153 | + if (chdir(ccs_policy_dir) || chdir("policy/")) { | |
154 | + fprintf(stderr, "Directory %s/policy/ doesn't exist.\n", | |
155 | + ccs_policy_dir); | |
156 | + return 1; | |
157 | + } | |
158 | + return !ccs_save_policy(); | |
159 | +usage: | |
160 | + printf("Usage: %s [policy_dir|-] [remote_ip:remote_port]\n\n" | |
161 | + "policy_dir : Use policy_dir rather than /etc/caitsith " | |
162 | + "directory.\n" | |
163 | + "- : Print policy to stdout rather than save as a file.\n" | |
164 | + "remote_ip:remote_port : Read from caitsith-agent listening at " | |
165 | + "remote_ip:remote_port .\n", argv[0]); | |
166 | + return 1; | |
167 | +} |
@@ -0,0 +1,84 @@ | ||
1 | +/* | |
2 | + * caitsithtools.h | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#define _FILE_OFFSET_BITS 64 | |
24 | +#define _LARGEFILE_SOURCE | |
25 | +#define _LARGEFILE64_SOURCE | |
26 | +#define _GNU_SOURCE | |
27 | +#include <arpa/inet.h> | |
28 | +#include <asm/types.h> | |
29 | +#include <dirent.h> | |
30 | +#include <errno.h> | |
31 | +#include <fcntl.h> | |
32 | +#include <limits.h> | |
33 | +#include <stdio.h> | |
34 | +#include <stdlib.h> | |
35 | +#include <string.h> | |
36 | +#include <sys/file.h> | |
37 | +#include <sys/socket.h> | |
38 | +#include <sys/stat.h> | |
39 | +#include <sys/types.h> | |
40 | +#include <sys/un.h> | |
41 | +#include <time.h> | |
42 | +#include <unistd.h> | |
43 | +#include <stdarg.h> | |
44 | +#include <poll.h> | |
45 | + | |
46 | +#define s8 __s8 | |
47 | +#define u8 __u8 | |
48 | +#define u16 __u16 | |
49 | +#define u32 __u32 | |
50 | +#define true 1 | |
51 | +#define false 0 | |
52 | + | |
53 | +/***** CONSTANTS DEFINITION START *****/ | |
54 | + | |
55 | +/***** CONSTANTS DEFINITION END *****/ | |
56 | + | |
57 | +/***** STRUCTURES DEFINITION START *****/ | |
58 | + | |
59 | +/***** STRUCTURES DEFINITION END *****/ | |
60 | + | |
61 | +/***** PROTOTYPES DEFINITION START *****/ | |
62 | + | |
63 | +FILE *ccs_open_read(const char *filename); | |
64 | +FILE *ccs_open_write(const char *filename); | |
65 | +_Bool ccs_check_policy_dir(_Bool exit_on_failue); | |
66 | +_Bool ccs_check_remote_host(_Bool exit_on_failue); | |
67 | +_Bool ccs_decode(const char *ascii, char *bin); | |
68 | +_Bool ccs_str_starts(char *str, const char *begin); | |
69 | +char *ccs_freadline(FILE *fp); | |
70 | +char *ccs_strdup(const char *string); | |
71 | +int ccs_open_stream(const char *filename); | |
72 | +void *ccs_malloc(const size_t size); | |
73 | +void *ccs_realloc(void *ptr, const size_t size); | |
74 | +void ccs_get(void); | |
75 | +void ccs_normalize_line(char *buffer); | |
76 | +void ccs_put(void); | |
77 | + | |
78 | +extern _Bool ccs_network_mode; | |
79 | +extern u16 ccs_network_port; | |
80 | +extern u32 ccs_network_ip; | |
81 | +extern const char *CCS_PROC_POLICY_DIR; | |
82 | +extern const char *CCS_PROC_POLICY_POLICY; | |
83 | + | |
84 | +/***** PROTOTYPES DEFINITION END *****/ |
@@ -0,0 +1,303 @@ | ||
1 | +/* | |
2 | + * readline.h | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include <ncurses.h> | |
24 | + | |
25 | +static int ccs_getch0(void) | |
26 | +{ | |
27 | + static int enter_key = EOF; | |
28 | + int c; | |
29 | +again: | |
30 | + c = getch(); | |
31 | + if (c == 127 || c == 8) | |
32 | + c = KEY_BACKSPACE; | |
33 | + /* syslog(LOG_INFO, "ccs_getch0='%c' (%d)\n", c, c); */ | |
34 | + if (c == '\r' || c == '\n') { | |
35 | + if (enter_key == EOF) | |
36 | + enter_key = c; | |
37 | + else if (c != enter_key) | |
38 | + goto again; | |
39 | + } | |
40 | + return c; | |
41 | +} | |
42 | + | |
43 | +static int ccs_getch2(void) | |
44 | +{ | |
45 | + static int c0 = 0; | |
46 | + static int c1 = 0; | |
47 | + static int c2 = 0; | |
48 | + static int c3 = 0; | |
49 | + static int len = 0; | |
50 | + if (len > 0) { | |
51 | + c0 = c1; | |
52 | + c1 = c2; | |
53 | + c2 = c3; | |
54 | + len--; | |
55 | + return c0; | |
56 | + } | |
57 | + c0 = ccs_getch0(); | |
58 | + if (c0 != 0x1B) | |
59 | + return c0; | |
60 | + c1 = ccs_getch0(); | |
61 | + if (c1 != '[') { | |
62 | + len = 1; | |
63 | + return c0; | |
64 | + } | |
65 | + c2 = ccs_getch0(); | |
66 | + if (c2 < '1' || c2 > '6') { | |
67 | + len = 2; | |
68 | + return c0; | |
69 | + } | |
70 | + c3 = ccs_getch0(); | |
71 | + if (c3 != '~') { | |
72 | + len = 3; | |
73 | + return c0; | |
74 | + } | |
75 | + /* syslog(LOG_INFO, "ccs_getch2='%c'\n", c2); */ | |
76 | + switch (c2) { | |
77 | + case '1': | |
78 | + return KEY_HOME; | |
79 | + case '2': | |
80 | + return KEY_IC; | |
81 | + case '3': | |
82 | + return KEY_DC; | |
83 | + case '4': | |
84 | + return KEY_END; | |
85 | + case '5': | |
86 | + return KEY_PPAGE; | |
87 | + case '6': | |
88 | + return KEY_NPAGE; | |
89 | + } | |
90 | + return 0; | |
91 | +} | |
92 | + | |
93 | +static int ccs_add_history(const char *buffer, const char **history, | |
94 | + const int history_count, const int max_history) | |
95 | +{ | |
96 | + char *cp = buffer ? strdup(buffer) : NULL; | |
97 | + if (!cp) | |
98 | + return history_count; | |
99 | + if (history_count && !strcmp(history[history_count - 1], cp)) { | |
100 | + free(cp); | |
101 | + return history_count; | |
102 | + } | |
103 | + if (history_count < max_history) { | |
104 | + history[history_count] = cp; | |
105 | + return history_count + 1; | |
106 | + } else if (max_history) { | |
107 | + int i; | |
108 | + free((char *) history[0]); | |
109 | + for (i = 0; i < history_count - 1; i++) | |
110 | + history[i] = history[i + 1]; | |
111 | + history[history_count - 1] = cp; | |
112 | + return history_count; | |
113 | + } | |
114 | + return 0; | |
115 | +} | |
116 | + | |
117 | +static int ccs_query_fd = EOF; | |
118 | +static char *ccs_initial_readline_data = NULL; | |
119 | + | |
120 | +static char *ccs_readline(const int start_y, const int start_x, | |
121 | + const char *prompt, const char *history[], | |
122 | + const int history_count, const int max_length, | |
123 | + const int scroll_width) | |
124 | +{ | |
125 | + const int prompt_len = prompt ? strlen(prompt) : 0; | |
126 | + int buffer_len = 0; | |
127 | + int line_pos = 0; | |
128 | + int cur_pos = 0; | |
129 | + int history_pos = 0; | |
130 | + _Bool tmp_saved = false; | |
131 | + static char *buffer = NULL; | |
132 | + static char *tmp_buffer = NULL; | |
133 | + { | |
134 | + int i; | |
135 | + for (i = 0; i < history_count; i++) | |
136 | + if (!history[i]) | |
137 | + return NULL; | |
138 | + } | |
139 | + { | |
140 | + char *tmp; | |
141 | + tmp = realloc(buffer, max_length + 1); | |
142 | + if (!tmp) | |
143 | + return NULL; | |
144 | + buffer = tmp; | |
145 | + tmp = realloc(tmp_buffer, max_length + 1); | |
146 | + if (!tmp) | |
147 | + return NULL; | |
148 | + tmp_buffer = tmp; | |
149 | + memset(buffer, 0, max_length + 1); | |
150 | + memset(tmp_buffer, 0, max_length + 1); | |
151 | + } | |
152 | + move(start_y, start_x); | |
153 | + history_pos = history_count; | |
154 | + if (ccs_initial_readline_data) { | |
155 | + strncpy(buffer, ccs_initial_readline_data, max_length); | |
156 | + buffer_len = strlen(buffer); | |
157 | + ungetch(KEY_END); | |
158 | + } | |
159 | + while (true) { | |
160 | + int window_width; | |
161 | + int window_height; | |
162 | + int c; | |
163 | + int x; | |
164 | + int y; | |
165 | + int i; | |
166 | + int ret_ignored; | |
167 | + getmaxyx(stdscr, window_height, window_width); | |
168 | + window_width -= prompt_len; | |
169 | + getyx(stdscr, y, x); | |
170 | + move(y, 0); | |
171 | + while (cur_pos > window_width - 1) { | |
172 | + cur_pos--; | |
173 | + line_pos++; | |
174 | + } | |
175 | + if (prompt_len) | |
176 | + printw("%s", prompt); | |
177 | + for (i = line_pos; i < line_pos + window_width; i++) { | |
178 | + if (i < buffer_len) | |
179 | + addch(buffer[i]); | |
180 | + else | |
181 | + break; | |
182 | + } | |
183 | + clrtoeol(); | |
184 | + move(y, cur_pos + prompt_len); | |
185 | + refresh(); | |
186 | + c = ccs_getch2(); | |
187 | + if (ccs_query_fd != EOF) | |
188 | + ret_ignored = write(ccs_query_fd, "\n", 1); | |
189 | + if (c == 4) { /* Ctrl-D */ | |
190 | + if (!buffer_len) | |
191 | + buffer_len = -1; | |
192 | + break; | |
193 | + } else if (c == KEY_IC) { | |
194 | + scrollok(stdscr, TRUE); | |
195 | + printw("\n"); | |
196 | + for (i = 0; i < history_count; i++) | |
197 | + printw("%d: '%s'\n", i, history[i]); | |
198 | + scrollok(stdscr, FALSE); | |
199 | + } else if (c >= 0x20 && c <= 0x7E && | |
200 | + buffer_len < max_length - 1) { | |
201 | + for (i = buffer_len - 1; i >= line_pos + cur_pos; i--) | |
202 | + buffer[i + 1] = buffer[i]; | |
203 | + buffer[line_pos + cur_pos] = c; | |
204 | + buffer[++buffer_len] = '\0'; | |
205 | + if (cur_pos < window_width - 1) | |
206 | + cur_pos++; | |
207 | + else | |
208 | + line_pos++; | |
209 | + } else if (c == '\r' || c == '\n') { | |
210 | + break; | |
211 | + } else if (c == KEY_BACKSPACE) { | |
212 | + if (line_pos + cur_pos) { | |
213 | + buffer_len--; | |
214 | + for (i = line_pos + cur_pos - 1; | |
215 | + i < buffer_len; i++) | |
216 | + buffer[i] = buffer[i + 1]; | |
217 | + buffer[buffer_len] = '\0'; | |
218 | + if (line_pos >= scroll_width && cur_pos == 0) { | |
219 | + line_pos -= scroll_width; | |
220 | + cur_pos += scroll_width - 1; | |
221 | + } else if (cur_pos) { | |
222 | + cur_pos--; | |
223 | + } else if (line_pos) { | |
224 | + line_pos--; | |
225 | + } | |
226 | + } | |
227 | + } else if (c == KEY_DC) { | |
228 | + if (line_pos + cur_pos < buffer_len) { | |
229 | + buffer_len--; | |
230 | + for (i = line_pos + cur_pos; i < buffer_len; | |
231 | + i++) | |
232 | + buffer[i] = buffer[i + 1]; | |
233 | + buffer[buffer_len] = '\0'; | |
234 | + } | |
235 | + } else if (c == KEY_UP) { | |
236 | + if (history_pos) { | |
237 | + if (!tmp_saved) { | |
238 | + tmp_saved = true; | |
239 | + strncpy(tmp_buffer, buffer, | |
240 | + max_length); | |
241 | + } | |
242 | + history_pos--; | |
243 | + strncpy(buffer, history[history_pos], | |
244 | + max_length); | |
245 | + buffer_len = strlen(buffer); | |
246 | + goto end_key; | |
247 | + } | |
248 | + } else if (c == KEY_DOWN) { | |
249 | + if (history_pos < history_count - 1) { | |
250 | + history_pos++; | |
251 | + strncpy(buffer, history[history_pos], | |
252 | + max_length); | |
253 | + buffer_len = strlen(buffer); | |
254 | + goto end_key; | |
255 | + } else if (tmp_saved) { | |
256 | + tmp_saved = false; | |
257 | + history_pos = history_count; | |
258 | + strncpy(buffer, tmp_buffer, max_length); | |
259 | + buffer_len = strlen(buffer); | |
260 | + goto end_key; | |
261 | + } | |
262 | + } else if (c == KEY_HOME) { | |
263 | + cur_pos = 0; | |
264 | + line_pos = 0; | |
265 | + } else if (c == KEY_END) { | |
266 | + goto end_key; | |
267 | + } else if (c == KEY_LEFT) { | |
268 | + if (line_pos >= scroll_width && cur_pos == 0) { | |
269 | + line_pos -= scroll_width; | |
270 | + cur_pos += scroll_width - 1; | |
271 | + } else if (cur_pos) { | |
272 | + cur_pos--; | |
273 | + } else if (line_pos) { | |
274 | + line_pos--; | |
275 | + } | |
276 | + } else if (c == KEY_RIGHT) { | |
277 | + if (line_pos + cur_pos < buffer_len) { | |
278 | + if (cur_pos < window_width - 1) | |
279 | + cur_pos++; | |
280 | + else if (line_pos + cur_pos < | |
281 | + buffer_len - scroll_width && | |
282 | + cur_pos >= scroll_width - 1) { | |
283 | + cur_pos -= scroll_width - 1; | |
284 | + line_pos += scroll_width; | |
285 | + } else { | |
286 | + line_pos++; | |
287 | + } | |
288 | + } | |
289 | + } | |
290 | + continue; | |
291 | +end_key: | |
292 | + cur_pos = buffer_len; | |
293 | + line_pos = 0; | |
294 | + if (cur_pos > window_width - 1) { | |
295 | + line_pos = buffer_len - (window_width - 1); | |
296 | + cur_pos = window_width - 1; | |
297 | + } | |
298 | + } | |
299 | + if (buffer_len == -1) | |
300 | + return NULL; | |
301 | + ccs_normalize_line(buffer); | |
302 | + return strdup(buffer); | |
303 | +} |
@@ -0,0 +1,42 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +BUILD_FILES := caitsith-auditd caitsith-loadpolicy caitsith-notifyd caitsith-pstree caitsith-queryd \ | |
4 | + caitsith-savepolicy | |
5 | + | |
6 | +all: libcaitsithtools.so $(BUILD_FILES) | |
7 | + | |
8 | +$(BUILD_FILES): libcaitsithtools.so | |
9 | + | |
10 | +/usr/include/curses.h: | |
11 | + @echo "/usr/include/curses.h is missing." | |
12 | + @echo "Run 'yum install ncurses-devel' or 'apt-get install libncurses-dev'" | |
13 | + sleep 10 | |
14 | + | |
15 | +# -fPIE conflicts with -fPIC, disable it for libraries. | |
16 | +CFLAGS_PIC := $(filter-out -fPIE,$(CFLAGS)) | |
17 | +LDFLAGS_PIC := $(filter-out -pie,$(filter-out -fPIE,$(LDFLAGS))) | |
18 | + | |
19 | +libcaitsithtools.so: caitsithtools.c caitsithtools.h | |
20 | + $(CC) $(CPPFLAGS) $(CFLAGS_PIC) $(LDFLAGS_PIC) -fPIC caitsithtools.c -shared -Wl,-soname,libcaitsithtools.so.2 -o libcaitsithtools.so.2.0.0 | |
21 | + ln -sf libcaitsithtools.so.2.0.0 libcaitsithtools.so | |
22 | + | |
23 | +.c: | |
24 | + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< -lcaitsithtools -L. | |
25 | + | |
26 | +caitsith-queryd: caitsithtools.h caitsith-queryd.c readline.h /usr/include/curses.h libcaitsithtools.so | |
27 | + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o caitsith-queryd caitsith-queryd.c -lncurses -lcaitsithtools -L. | |
28 | + | |
29 | +install: all | |
30 | + mkdir -p -m 0755 $(INSTALLDIR)$(USRLIBDIR) | |
31 | + $(INSTALL) -m 0755 libcaitsithtools.so.2.0.0 $(INSTALLDIR)$(USRLIBDIR) | |
32 | + ln -sf libcaitsithtools.so.2.0.0 $(INSTALLDIR)$(USRLIBDIR)/libcaitsithtools.so.2 | |
33 | +ifeq ($(INSTALLDIR),) | |
34 | + ldconfig || true | |
35 | +endif | |
36 | + mkdir -p -m 0755 $(INSTALLDIR)$(USRSBINDIR) | |
37 | + $(INSTALL) -m 0755 $(BUILD_FILES) $(INSTALLDIR)$(USRSBINDIR) | |
38 | + | |
39 | +clean: | |
40 | + rm -f -- $(BUILD_FILES) libcaitsithtools.so* | |
41 | + | |
42 | +.PHONY: clean install |
@@ -0,0 +1,1339 @@ | ||
1 | +/* | |
2 | + * caitsith_audit2cond_test.c | |
3 | + * | |
4 | + * Copyright (C) 2012-2013 Tetsuo Handa | |
5 | + * | |
6 | + * Version: 0.2 2016/10/05 | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License v2 as published by the | |
10 | + * Free Software Foundation. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | + * more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License along with | |
18 | + * this program; if not, write to the Free Software Foundation, Inc., | |
19 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
20 | + */ | |
21 | + | |
22 | +#include <errno.h> | |
23 | +#include <fcntl.h> | |
24 | +#include <linux/ip.h> | |
25 | +#include <linux/kdev_t.h> | |
26 | +#include <linux/reboot.h> | |
27 | +#include <netinet/in.h> | |
28 | +#include <poll.h> | |
29 | +#include <pty.h> | |
30 | +#include <sched.h> | |
31 | +#include <stdio.h> | |
32 | +#include <stdlib.h> | |
33 | +#include <string.h> | |
34 | +#include <signal.h> | |
35 | +#include <sys/mount.h> | |
36 | +#include <sys/ptrace.h> | |
37 | +#include <sys/socket.h> | |
38 | +#include <sys/stat.h> | |
39 | +#include <sys/syscall.h> | |
40 | +#include <sys/timex.h> | |
41 | +#include <sys/types.h> | |
42 | +#include <sys/un.h> | |
43 | +#include <sys/wait.h> | |
44 | +#include <time.h> | |
45 | +#include <unistd.h> | |
46 | +#ifndef CLONE_NEWNS | |
47 | +#include <linux/sched.h> | |
48 | +#endif | |
49 | +#ifndef PRIO_PROCESS | |
50 | +#include <sys/resource.h> | |
51 | +#endif | |
52 | +#ifndef MS_REC | |
53 | +#define MS_REC 16384 | |
54 | +#endif | |
55 | +#ifndef MS_PRIVATE | |
56 | +#define MS_PRIVATE (1 << 18) | |
57 | +#endif | |
58 | +int unshare(int flags); | |
59 | +int reboot(int cmd); | |
60 | +struct module; | |
61 | +int init_module(const char *name, struct module *image); | |
62 | +int delete_module(const char *name); | |
63 | +int pivot_root(const char *new_root, const char *put_old); | |
64 | +struct kexec_segment; | |
65 | +static inline long sys_kexec_load(unsigned long entry, | |
66 | + unsigned long nr_segments, | |
67 | + struct kexec_segment *segments, | |
68 | + unsigned long flags) | |
69 | +{ | |
70 | + return (long) syscall(__NR_kexec_load, entry, nr_segments, | |
71 | + segments, flags); | |
72 | +} | |
73 | + | |
74 | +static char *args[512]; | |
75 | +static int audit_fd = EOF; | |
76 | +static int policy_fd = EOF; | |
77 | +static int one = 1; | |
78 | + | |
79 | +static const char *make_variable(const char *name, const unsigned int type) | |
80 | +{ | |
81 | + const char *cp = name + strlen(name) + 1; | |
82 | + if (type == 0) | |
83 | + return cp; | |
84 | + if (*cp == '"') { | |
85 | + /* Name variables. */ | |
86 | + if (type == 1) | |
87 | + return "NULL"; | |
88 | + if (type == 2) | |
89 | + return "!@EMPTY_NAME_GROUP"; | |
90 | + //if (type == 3) | |
91 | + // return "task.exe"; // How can this match? | |
92 | + } else if (!strcmp(name, "ip")) { | |
93 | + /* IP address variables */ | |
94 | + if (type == 1) | |
95 | + return "!@EMPTY_IP_GROUP"; | |
96 | + } else if (strstr(name, ".type")) { | |
97 | + /* task.type or path.type */ | |
98 | + /* No variables supported. */ | |
99 | + } else { | |
100 | + /* Numeric variables */ | |
101 | + const int zero = !strcmp(cp, "0") || !strcmp(cp, "00") || | |
102 | + !strcmp(cp, "0x0"); | |
103 | + if (type == 1) | |
104 | + return "!@EMPTY_NUMBER_GROUP"; | |
105 | + if (type == 2) | |
106 | + return "!@ZERO" + zero; | |
107 | + if (type == 3) | |
108 | + return "!task.uid" + zero; | |
109 | + if (strstr(name, ".perm")) { | |
110 | + unsigned int perm = 0; | |
111 | + sscanf(cp, "%o", &perm); | |
112 | + if (type == 4) | |
113 | + return "!setuid" + ((perm & 04000) != 0); | |
114 | + if (type == 5) | |
115 | + return "!setgid" + ((perm & 02000) != 0); | |
116 | + if (type == 6) | |
117 | + return "!sticky" + ((perm & 01000) != 0); | |
118 | + if (type == 7) | |
119 | + return "!owner_read" + ((perm & 0400) != 0); | |
120 | + if (type == 8) | |
121 | + return "!owner_write" + ((perm & 0200) != 0); | |
122 | + if (type == 9) | |
123 | + return "!owner_execute" + ((perm & 0100) != 0); | |
124 | + if (type == 10) | |
125 | + return "!group_read" + ((perm & 040) != 0); | |
126 | + if (type == 11) | |
127 | + return "!group_write" + ((perm & 020) != 0); | |
128 | + if (type == 12) | |
129 | + return "!group_execute" + ((perm & 010) != 0); | |
130 | + if (type == 13) | |
131 | + return "!others_read" + ((perm & 04) != 0); | |
132 | + if (type == 14) | |
133 | + return "!others_write" + ((perm & 02) != 0); | |
134 | + if (type == 15) | |
135 | + return "!others_execute" + ((perm & 01) != 0); | |
136 | + } | |
137 | + } | |
138 | + return NULL; | |
139 | +} | |
140 | + | |
141 | +static int make_policy(const char *action, const unsigned int loop, | |
142 | + const int check, const int max) | |
143 | +{ | |
144 | + static char buffer[1048576]; | |
145 | + int pos; | |
146 | + int i; | |
147 | + const char *var = ""; | |
148 | + int not_equals = (loop & 1); | |
149 | + if (max) { | |
150 | + /* Nothing more to test if there is no more variables. */ | |
151 | + var = make_variable(args[check], loop >> 1); | |
152 | + if (!var) | |
153 | + return 0; | |
154 | + /* NULL has inverse semantics. */ | |
155 | + if (!strcmp(var, "NULL")) | |
156 | + not_equals = !not_equals; | |
157 | + /* Handle other inversed conditions. */ | |
158 | + else if (*var == '!') { | |
159 | + var++; | |
160 | + not_equals = !not_equals; | |
161 | + } | |
162 | + } | |
163 | + memset(buffer, 0, sizeof(buffer)); | |
164 | + pos = snprintf(buffer, sizeof(buffer) - 1, "0 acl %s task.ppid=%u\n" | |
165 | + "\t0 allow", action, getpid()); | |
166 | + for (i = 0; i < max; i++) { | |
167 | + /* Change only one argument at a time. */ | |
168 | + if (i != check) | |
169 | + pos += snprintf(buffer + pos, sizeof(buffer) - 1 - pos, | |
170 | + " %s=%s", args[i], | |
171 | + args[i] + strlen(args[i]) + 1); | |
172 | + else | |
173 | + pos += snprintf(buffer + pos, sizeof(buffer) - 1 - pos, | |
174 | + " %s%s=%s", args[i], | |
175 | + not_equals ? "!" : "", var); | |
176 | + } | |
177 | + pos += snprintf(buffer + pos, sizeof(buffer) - 1 - pos, "\n" | |
178 | + "\t1 deny\n"); | |
179 | + write(policy_fd, buffer, strlen(buffer)); | |
180 | + printf("Expecting %s: %s", (loop & 1) ? "denied" : "allowed", | |
181 | + buffer); | |
182 | + return 1; | |
183 | +} | |
184 | + | |
185 | +static int do_test(int (*func) (void)) | |
186 | +{ | |
187 | + int error = 0; | |
188 | + const pid_t pid = fork(); | |
189 | + switch (pid) { | |
190 | + case 0: | |
191 | + if (unshare(CLONE_NEWNS)) { | |
192 | + fprintf(stderr, "***** Can't unshare.\n"); | |
193 | + _exit(1); | |
194 | + } | |
195 | + errno = 0; | |
196 | + _exit(func()); | |
197 | + case -1: | |
198 | + fprintf(stderr, "***** Can't fork.\n"); | |
199 | + return 1; | |
200 | + } | |
201 | + while (waitpid(pid, &error, 0) == EOF && errno == EINTR); | |
202 | + return WIFEXITED(error) ? WEXITSTATUS(error) : -1; | |
203 | +} | |
204 | + | |
205 | +static int check_result(const char *action, int (*func) (void), | |
206 | + const unsigned int loop) | |
207 | +{ | |
208 | + static char buffer[1048576]; | |
209 | + char *cp1; | |
210 | + char *cp2; | |
211 | + int retries = 0; | |
212 | +retry: | |
213 | + do_test(func); | |
214 | + memset(buffer, 0, sizeof(buffer)); | |
215 | + read(audit_fd, buffer, sizeof(buffer) - 1); | |
216 | + cp1 = strchr(buffer, '\n'); | |
217 | + if (!cp1) { | |
218 | + if (retries++ < 100) | |
219 | + goto retry; | |
220 | + fprintf(stderr, "***** Missing audit log for '%s': %s\n", | |
221 | + action, buffer); | |
222 | + return 1; | |
223 | + } | |
224 | + *cp1 = '\0'; | |
225 | + cp1 = strstr(buffer, " / "); | |
226 | + if (!cp1) { | |
227 | + fprintf(stderr, "***** Corrupted audit log for '%s': %s\n", | |
228 | + action, buffer); | |
229 | + return 1; | |
230 | + } | |
231 | + cp1 += 3; | |
232 | + cp2 = strchr(cp1, ' '); | |
233 | + if (!cp2) { | |
234 | + fprintf(stderr, "***** Corrupted audit log for '%s': %s\n", | |
235 | + action, buffer); | |
236 | + return 1; | |
237 | + } | |
238 | + *cp2 = '\0'; | |
239 | + if (strcmp(cp1, action)) { | |
240 | + if (retries++ < 100) | |
241 | + goto retry; | |
242 | + *cp2 = ' '; | |
243 | + fprintf(stderr, "***** Unexpected audit log for '%s': %s\n", | |
244 | + action, buffer); | |
245 | + return 1; | |
246 | + } | |
247 | + *cp2 = ' '; | |
248 | + if (!(loop & 1)) { | |
249 | + if (!strstr(buffer, " result=allowed ")) { | |
250 | + fprintf(stderr, "***** result=allowed expected: %s\n", | |
251 | + buffer); | |
252 | + exit(1); | |
253 | + return 1; | |
254 | + } | |
255 | + } else { | |
256 | + if (!strstr(buffer, " result=denied ")) { | |
257 | + fprintf(stderr, "***** result=denied expected: %s\n", | |
258 | + buffer); | |
259 | + exit(1); | |
260 | + return 1; | |
261 | + } | |
262 | + } | |
263 | + snprintf(buffer, sizeof(buffer) - 1, "delete 0 acl %s task.ppid=%u\n", | |
264 | + action, getpid()); | |
265 | + write(policy_fd, buffer, strlen(buffer)); | |
266 | + while (read(audit_fd, buffer, sizeof(buffer) - 1) > 0); | |
267 | + return 0; | |
268 | +} | |
269 | + | |
270 | +static void test_action(const char *action, int (*func) (void)) | |
271 | +{ | |
272 | + static char buffer[1048576]; | |
273 | + int pos; | |
274 | + int i; | |
275 | + char *cp1; | |
276 | + char *cp2; | |
277 | + int retries = 0; | |
278 | + memset(args, 0, sizeof(args)); | |
279 | + make_policy(action, 0, 0, 0); | |
280 | +retry: | |
281 | + do_test(func); | |
282 | + memset(buffer, 0, sizeof(buffer)); | |
283 | + read(audit_fd, buffer, sizeof(buffer) - 1); | |
284 | + cp1 = strchr(buffer, '\n'); | |
285 | + if (!cp1) { | |
286 | + if (retries++ < 100) | |
287 | + goto retry; | |
288 | + fprintf(stderr, "+++++ Missing audit log for '%s': %s\n", | |
289 | + action, buffer); | |
290 | + return; | |
291 | + } | |
292 | + *cp1 = '\0'; | |
293 | + cp1 = strstr(buffer, " / "); | |
294 | + if (!cp1) { | |
295 | + fprintf(stderr, "+++++ Corrupted audit log for '%s': %s\n", | |
296 | + action, buffer); | |
297 | + return; | |
298 | + } | |
299 | + cp1 += 3; | |
300 | + cp2 = strchr(cp1, ' '); | |
301 | + if (!cp2) { | |
302 | + fprintf(stderr, "+++++ Corrupted audit log for '%s': %s\n", | |
303 | + action, buffer); | |
304 | + return; | |
305 | + } | |
306 | + *cp2++ = '\0'; | |
307 | + if (strcmp(cp1, action)) { | |
308 | + if (retries++ < 100) | |
309 | + goto retry; | |
310 | + fprintf(stderr, "+++++ Unexpected audit log for '%s': %s\n", | |
311 | + action, cp1); | |
312 | + return; | |
313 | + } | |
314 | + cp1 = cp2; | |
315 | + pos = 0; | |
316 | + while (pos < (sizeof(args) / sizeof(args[0]))) { | |
317 | + char *cp3; | |
318 | + args[pos++] = cp1; | |
319 | + cp2 = strchr(cp1, ' '); | |
320 | + if (cp2) | |
321 | + *cp2++ = '\0'; | |
322 | + cp3 = strchr(cp1, '='); | |
323 | + if (!cp3 || !*(cp3 + 1)) { | |
324 | + fprintf(stderr, "+++++ Corrupted audit log.\n"); | |
325 | + return; | |
326 | + } | |
327 | + *cp3 = '\0'; | |
328 | + /* | |
329 | + * Ignore task.pid which cannot be matched due to tesing under | |
330 | + * fork()ed process. | |
331 | + */ | |
332 | + if (!strcmp(cp1, "task.pid")) | |
333 | + pos--; | |
334 | + /* | |
335 | + * Ignore .ino which might change for each test. | |
336 | + */ | |
337 | + else if (strstr(cp1, ".ino")) | |
338 | + pos--; | |
339 | + /* | |
340 | + * Ignore .minor which might change for each test. | |
341 | + */ | |
342 | + else if (strstr(cp1, ".minor")) | |
343 | + pos--; | |
344 | + cp1 = cp2; | |
345 | + if (!cp1) | |
346 | + break; | |
347 | + } | |
348 | + if (pos == (sizeof(args) / sizeof(args[0]))) { | |
349 | + fprintf(stderr, "+++++ Line too long.\n"); | |
350 | + return; | |
351 | + } | |
352 | + { | |
353 | + static char buffer2[1024]; | |
354 | + while (read(audit_fd, buffer2, sizeof(buffer2)) > 0); | |
355 | + } | |
356 | + for (i = 0; i < pos; i++) { | |
357 | + unsigned int loop; | |
358 | + for (loop = 0; make_policy(action, loop, i, pos); loop++) | |
359 | + check_result(action, func, loop); | |
360 | + } | |
361 | + return; | |
362 | +} | |
363 | + | |
364 | +static void startup(void) | |
365 | +{ | |
366 | + int pipe_fd[2] = { EOF, EOF }; | |
367 | + static char buffer[1048576]; | |
368 | + FILE *fp = fopen(POLDIR "/policy", "r"); | |
369 | + policy_fd = open(POLDIR "/policy", O_WRONLY); | |
370 | + audit_fd = open(POLDIR "/audit", O_RDONLY); | |
371 | + if (!fp || policy_fd == EOF || audit_fd == EOF) { | |
372 | + fprintf(stderr, | |
373 | + "***** Can't open " POLDIR "/ interface.\n"); | |
374 | + exit(1); | |
375 | + } | |
376 | + if (pipe(pipe_fd)) { | |
377 | + fprintf(stderr, "***** Can't pipe.\n"); | |
378 | + exit(1); | |
379 | + } | |
380 | + mkdir("/tmp/caitsith.tmp", 0700); | |
381 | + if (chown("/tmp/caitsith.tmp", 0, 0) || | |
382 | + chmod("/tmp/caitsith.tmp", 0700)) { | |
383 | + fprintf(stderr, "***** Can't chown/chmod.\n"); | |
384 | + exit(1); | |
385 | + } | |
386 | + while (memset(buffer, 0, sizeof(buffer)), | |
387 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
388 | + if (!strchr(buffer, '\n')) { | |
389 | + fprintf(stderr, "***** Line too long.\n"); | |
390 | + exit(1); | |
391 | + } | |
392 | + write(policy_fd, "delete ", 7); | |
393 | + write(policy_fd, buffer, strlen(buffer)); | |
394 | + } | |
395 | + fclose(fp); | |
396 | + { | |
397 | + const char *config = "quota Memory used by audit: 1048576\n" | |
398 | + "quota audit[0] allowed=1024 denied=1024 " | |
399 | + "unmatched=1024\n" "number_group ZERO 0\n"; | |
400 | + write(policy_fd, config, strlen(config)); | |
401 | + } | |
402 | + while (read(audit_fd, buffer, sizeof(buffer)) > 0); | |
403 | + switch (fork()) { | |
404 | + int fd; | |
405 | + case 0: | |
406 | + close(policy_fd); | |
407 | + close(audit_fd); | |
408 | + close(pipe_fd[1]); | |
409 | + fd = open(POLDIR "/query", O_RDWR); | |
410 | + while (1) { | |
411 | + unsigned int serial; | |
412 | + unsigned int retry; | |
413 | + struct pollfd pfd[2] = { | |
414 | + { fd, POLLIN, 0 }, | |
415 | + { pipe_fd[0], POLLIN, 0 } | |
416 | + }; | |
417 | + poll(pfd, 2, -1); | |
418 | + if (pfd[0].revents & POLLIN) | |
419 | + break; | |
420 | + if (read(fd, buffer, sizeof(buffer) - 1) <= 0) | |
421 | + continue; | |
422 | + if (sscanf(buffer, "Q%u-%u", &serial, &retry) != 2) { | |
423 | + fprintf(stderr, "***** Corrupted query: %s\n", | |
424 | + buffer); | |
425 | + break; | |
426 | + } | |
427 | + snprintf(buffer, sizeof(buffer) - 1, "A%u=%u\n", | |
428 | + serial, | |
429 | + retry < 5 ? 3 /* Retry */ : 2 /* No */); | |
430 | + write(fd, buffer, strlen(buffer)); | |
431 | + } | |
432 | + _exit(0); | |
433 | + case -1: | |
434 | + fprintf(stderr, "***** Can't fork.\n"); | |
435 | + exit(1); | |
436 | + } | |
437 | + close(pipe_fd[0]); | |
438 | +} | |
439 | + | |
440 | +static int test_execute(void) | |
441 | +{ | |
442 | + execlp(BINDIR "/true", "true", NULL); | |
443 | + return errno; | |
444 | +} | |
445 | + | |
446 | +static int test_read(void) | |
447 | +{ | |
448 | + open("/dev/null", O_RDONLY); | |
449 | + return errno; | |
450 | +} | |
451 | + | |
452 | +static int test_write(void) | |
453 | +{ | |
454 | + open("/dev/null", O_WRONLY); | |
455 | + return errno; | |
456 | +} | |
457 | + | |
458 | +static int test_create_no_append(void) | |
459 | +{ | |
460 | + int err; | |
461 | + open("/tmp/caitsith.tmp/file", O_CREAT | O_RDWR | O_TRUNC, 0600); | |
462 | + err = errno; | |
463 | + unlink("/tmp/caitsith.tmp/file"); | |
464 | + return err; | |
465 | +} | |
466 | + | |
467 | +static int test_create_append(void) | |
468 | +{ | |
469 | + int err; | |
470 | + open("/tmp/caitsith.tmp/file", O_CREAT | O_RDWR | O_TRUNC | O_APPEND, | |
471 | + 0600); | |
472 | + err = errno; | |
473 | + unlink("/tmp/caitsith.tmp/file"); | |
474 | + return err; | |
475 | +} | |
476 | + | |
477 | +static int test_fcntl_clear_append(void) | |
478 | +{ | |
479 | + const int fd = open("/dev/null", O_WRONLY | O_APPEND); | |
480 | + const int flags = fcntl(fd, F_GETFL); | |
481 | + errno = 0; | |
482 | + fcntl(fd, F_SETFL, flags & ~O_APPEND); | |
483 | + return errno; | |
484 | +} | |
485 | + | |
486 | +static int test_fcntl_set_append(void) | |
487 | +{ | |
488 | + const int fd = open("/dev/null", O_WRONLY); | |
489 | + const int flags = fcntl(fd, F_GETFL); | |
490 | + errno = 0; | |
491 | + fcntl(fd, F_SETFL, flags | O_APPEND); | |
492 | + return errno; | |
493 | +} | |
494 | + | |
495 | +static int test_unlink(void) | |
496 | +{ | |
497 | + mknod("/tmp/caitsith.tmp/file", S_IFREG | 0600, 0); | |
498 | + errno = 0; | |
499 | + unlink("/tmp/caitsith.tmp/file"); | |
500 | + return errno; | |
501 | +} | |
502 | + | |
503 | +static int test_getattr(void) | |
504 | +{ | |
505 | + struct stat buf; | |
506 | + stat("/dev/null", &buf); | |
507 | + return errno; | |
508 | +} | |
509 | + | |
510 | +static int test_mkdir(void) | |
511 | +{ | |
512 | + int err; | |
513 | + mkdir("/tmp/caitsith.tmp/dir", 0755); | |
514 | + err = errno; | |
515 | + rmdir("/tmp/caitsith.tmp/dir"); | |
516 | + return err; | |
517 | +} | |
518 | + | |
519 | +static int test_rmdir(void) | |
520 | +{ | |
521 | + mkdir("/tmp/caitsith.tmp/dir", 0755); | |
522 | + errno = 0; | |
523 | + rmdir("/tmp/caitsith.tmp/dir"); | |
524 | + return errno; | |
525 | +} | |
526 | + | |
527 | +static int test_mkfifo(void) | |
528 | +{ | |
529 | + int err; | |
530 | + mknod("/tmp/caitsith.tmp/fifo", S_IFIFO | 0600, 0); | |
531 | + err = errno; | |
532 | + unlink("/tmp/caitsith.tmp/fifo"); | |
533 | + return err; | |
534 | +} | |
535 | + | |
536 | +static int test_mksock_by_mknod(void) | |
537 | +{ | |
538 | + int err; | |
539 | + mknod("/tmp/caitsith.tmp/sock", S_IFSOCK | 0600, 0); | |
540 | + err = errno; | |
541 | + unlink("/tmp/caitsith.tmp/sock"); | |
542 | + return err; | |
543 | +} | |
544 | + | |
545 | +static int test_mksock_by_bind(void) | |
546 | +{ | |
547 | + struct sockaddr_un addr; | |
548 | + int err; | |
549 | + int fd = socket(PF_UNIX, SOCK_STREAM, 0); | |
550 | + memset(&addr, 0, sizeof(addr)); | |
551 | + addr.sun_family = AF_UNIX; | |
552 | + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, | |
553 | + "/tmp/caitsith.tmp/sock"); | |
554 | + errno = 0; | |
555 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
556 | + err = errno; | |
557 | + unlink(addr.sun_path); | |
558 | + return err; | |
559 | +} | |
560 | + | |
561 | +static int test_truncate(void) | |
562 | +{ | |
563 | + mknod("/tmp/caitsith.tmp/truncate", S_IFREG | 0600, 0); | |
564 | + errno = 0; | |
565 | + truncate("/tmp/caitsith.tmp/truncate", 0); | |
566 | + return errno; | |
567 | +} | |
568 | + | |
569 | +static int test_symlink(void) | |
570 | +{ | |
571 | + int err; | |
572 | + symlink(".", "/tmp/caitsith.tmp/symlink"); | |
573 | + err = errno; | |
574 | + unlink("/tmp/caitsith.tmp/symlink"); | |
575 | + return err; | |
576 | +} | |
577 | + | |
578 | +static int test_mkchar(void) | |
579 | +{ | |
580 | + int err; | |
581 | + mknod("/tmp/caitsith.tmp/char", S_IFCHR | 0600, MKDEV(1, 3)); | |
582 | + err = errno; | |
583 | + unlink("/tmp/caitsith.tmp/char"); | |
584 | + return err; | |
585 | +} | |
586 | + | |
587 | +static int test_mkblock(void) | |
588 | +{ | |
589 | + int err; | |
590 | + mknod("/tmp/caitsith.tmp/block", S_IFBLK | 0600, MKDEV(1, 0)); | |
591 | + err = errno; | |
592 | + unlink("/tmp/caitsith.tmp/block"); | |
593 | + return err; | |
594 | +} | |
595 | + | |
596 | +static int test_link(void) | |
597 | +{ | |
598 | + int err; | |
599 | + mknod("/tmp/caitsith.tmp/old_path", S_IFREG | 0600, 0); | |
600 | + errno = 0; | |
601 | + link("/tmp/caitsith.tmp/old_path", "/tmp/caitsith.tmp/new_path"); | |
602 | + err = errno; | |
603 | + unlink("/tmp/caitsith.tmp/old_path"); | |
604 | + unlink("/tmp/caitsith.tmp/new_path"); | |
605 | + return err; | |
606 | +} | |
607 | + | |
608 | +static int test_rename(void) | |
609 | +{ | |
610 | + int err; | |
611 | + mknod("/tmp/caitsith.tmp/old_path", S_IFREG | 0600, 0); | |
612 | + errno = 0; | |
613 | + rename("/tmp/caitsith.tmp/old_path", "/tmp/caitsith.tmp/new_path"); | |
614 | + err = errno; | |
615 | + unlink("/tmp/caitsith.tmp/old_path"); | |
616 | + unlink("/tmp/caitsith.tmp/new_path"); | |
617 | + return err; | |
618 | +} | |
619 | + | |
620 | +static int test_chmod(void) | |
621 | +{ | |
622 | + chmod("/dev/null", 0666); | |
623 | + return errno; | |
624 | +} | |
625 | + | |
626 | +static int test_chown(void) | |
627 | +{ | |
628 | + chown("/dev/null", 0, -1); | |
629 | + return errno; | |
630 | +} | |
631 | + | |
632 | +static int test_chgrp(void) | |
633 | +{ | |
634 | + chown("/dev/null", -1, 0); | |
635 | + return errno; | |
636 | +} | |
637 | + | |
638 | +static int test_ioctl(void) | |
639 | +{ | |
640 | + const int fd = open("/dev/null", O_WRONLY); | |
641 | + errno = 0; | |
642 | + ioctl(fd, 0, 0); | |
643 | + return errno; | |
644 | +} | |
645 | + | |
646 | +static int test_chroot(void) | |
647 | +{ | |
648 | + chroot("/tmp/caitsith.tmp"); | |
649 | + return errno; | |
650 | +} | |
651 | + | |
652 | +static int test_mount(void) | |
653 | +{ | |
654 | + mount(NULL, "/tmp/caitsith.tmp", "tmpfs", 0, NULL); | |
655 | + return errno; | |
656 | +} | |
657 | + | |
658 | +static int test_unmount(void) | |
659 | +{ | |
660 | + mount(NULL, "/tmp/caitsith.tmp", "tmpfs", 0, NULL); | |
661 | + errno = 0; | |
662 | + umount("/tmp/caitsith.tmp"); | |
663 | + return errno; | |
664 | +} | |
665 | + | |
666 | +static int test_pivot_root(void) | |
667 | +{ | |
668 | + errno = 0; | |
669 | + pivot_root("/sys/kernel/security/", "/sys/kernel/security/caitsith/"); | |
670 | + if (errno == ENOENT) { | |
671 | + errno = 0; | |
672 | + pivot_root("/proc/", "/proc/caitsith/"); | |
673 | + } | |
674 | + return errno; | |
675 | +} | |
676 | + | |
677 | +static void inet_bind(const int fd, const unsigned short int port) | |
678 | +{ | |
679 | + struct sockaddr_in addr = { }; | |
680 | + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | |
681 | + addr.sin_family = AF_INET; | |
682 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
683 | + addr.sin_port = htons(port); | |
684 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
685 | +} | |
686 | + | |
687 | +static int test_inet_stream_bind(void) | |
688 | +{ | |
689 | + int fd = socket(PF_INET, SOCK_STREAM, 0); | |
690 | + struct sockaddr_in addr = { }; | |
691 | + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | |
692 | + addr.sin_family = AF_INET; | |
693 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
694 | + addr.sin_port = htons(10000); | |
695 | + errno = 0; | |
696 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
697 | + return errno; | |
698 | +} | |
699 | + | |
700 | +static int test_inet_stream_listen(void) | |
701 | +{ | |
702 | + int fd = socket(PF_INET, SOCK_STREAM, 0); | |
703 | + inet_bind(fd, 10000); | |
704 | + errno = 0; | |
705 | + listen(fd, 5); | |
706 | + return errno; | |
707 | +} | |
708 | + | |
709 | +static int test_inet_stream_connect(void) | |
710 | +{ | |
711 | + int fd1 = socket(PF_INET, SOCK_STREAM, 0); | |
712 | + int fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
713 | + struct sockaddr_in addr = { }; | |
714 | + socklen_t size = sizeof(addr); | |
715 | + inet_bind(fd1, 10000); | |
716 | + listen(fd1, 0); | |
717 | + inet_bind(fd2, 10001); | |
718 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
719 | + errno = 0; | |
720 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
721 | + return errno; | |
722 | +} | |
723 | + | |
724 | +static int test_inet_stream_accept(void) | |
725 | +{ | |
726 | + int fd1 = socket(PF_INET, SOCK_STREAM, 0); | |
727 | + int fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
728 | + struct sockaddr_in addr = { }; | |
729 | + socklen_t size = sizeof(addr); | |
730 | + inet_bind(fd1, 10000); | |
731 | + listen(fd1, 0); | |
732 | + inet_bind(fd2, 10001); | |
733 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
734 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
735 | + errno = 0; | |
736 | + accept(fd1, (struct sockaddr *) &addr, &size); | |
737 | + return errno; | |
738 | +} | |
739 | + | |
740 | +static int test_inet_dgram_bind(void) | |
741 | +{ | |
742 | + int fd = socket(PF_INET, SOCK_DGRAM, 0); | |
743 | + struct sockaddr_in addr = { }; | |
744 | + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | |
745 | + addr.sin_family = AF_INET; | |
746 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
747 | + addr.sin_port = htons(10000); | |
748 | + errno = 0; | |
749 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
750 | + return errno; | |
751 | +} | |
752 | + | |
753 | +static int test_inet_dgram_connect(void) | |
754 | +{ | |
755 | + int fd1 = socket(PF_INET, SOCK_DGRAM, 0); | |
756 | + int fd2 = socket(PF_INET, SOCK_DGRAM, 0); | |
757 | + struct sockaddr_in addr = { }; | |
758 | + socklen_t size = sizeof(addr); | |
759 | + inet_bind(fd1, 10000); | |
760 | + inet_bind(fd2, 10001); | |
761 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
762 | + errno = 0; | |
763 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
764 | + return errno; | |
765 | +} | |
766 | + | |
767 | +static int test_inet_dgram_send(void) | |
768 | +{ | |
769 | + int fd1 = socket(PF_INET, SOCK_DGRAM, 0); | |
770 | + int fd2 = socket(PF_INET, SOCK_DGRAM, 0); | |
771 | + struct sockaddr_in addr = { }; | |
772 | + socklen_t size = sizeof(addr); | |
773 | + inet_bind(fd1, 10000); | |
774 | + inet_bind(fd2, 10001); | |
775 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
776 | + errno = 0; | |
777 | + sendto(fd2, "", 1, 0, (struct sockaddr *) &addr, sizeof(addr)); | |
778 | + return errno; | |
779 | +} | |
780 | + | |
781 | +static int test_inet_dgram_recv(void) | |
782 | +{ | |
783 | + int fd1 = socket(PF_INET, SOCK_DGRAM, 0); | |
784 | + int fd2 = socket(PF_INET, SOCK_DGRAM, 0); | |
785 | + char c; | |
786 | + struct sockaddr_in addr = { }; | |
787 | + socklen_t size = sizeof(addr); | |
788 | + inet_bind(fd1, 10000); | |
789 | + inet_bind(fd2, 10001); | |
790 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
791 | + sendto(fd2, "", 1, 0, (struct sockaddr *) &addr, sizeof(addr)); | |
792 | + errno = 0; | |
793 | + recvfrom(fd1, &c, 1, MSG_DONTWAIT, (struct sockaddr *) &addr, &size); | |
794 | + return errno; | |
795 | +} | |
796 | + | |
797 | +static int test_inet_raw_bind(void) | |
798 | +{ | |
799 | + int fd = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
800 | + struct sockaddr_in addr = { }; | |
801 | + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | |
802 | + addr.sin_family = AF_INET; | |
803 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
804 | + addr.sin_port = htons(10000); | |
805 | + errno = 0; | |
806 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
807 | + return errno; | |
808 | +} | |
809 | + | |
810 | +static int test_inet_raw_connect(void) | |
811 | +{ | |
812 | + int fd1 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
813 | + int fd2 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
814 | + struct sockaddr_in addr = { }; | |
815 | + socklen_t size = sizeof(addr); | |
816 | + inet_bind(fd1, 10000); | |
817 | + inet_bind(fd2, 10001); | |
818 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
819 | + errno = 0; | |
820 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
821 | + return errno; | |
822 | +} | |
823 | + | |
824 | +static int test_inet_raw_send(void) | |
825 | +{ | |
826 | + int fd1 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
827 | + int fd2 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
828 | + struct sockaddr_in addr = { }; | |
829 | + socklen_t size = sizeof(addr); | |
830 | + static struct iphdr ip = { }; | |
831 | + inet_bind(fd1, 10000); | |
832 | + inet_bind(fd2, 10001); | |
833 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
834 | + ip.version = 4; | |
835 | + ip.ihl = sizeof(struct iphdr) / 4; | |
836 | + ip.protocol = IPPROTO_RAW; | |
837 | + ip.daddr = htonl(INADDR_LOOPBACK); | |
838 | + ip.saddr = ip.daddr; | |
839 | + errno = 0; | |
840 | + sendto(fd2, &ip, sizeof(ip), 0, (struct sockaddr *) &addr, | |
841 | + sizeof(addr)); | |
842 | + return errno; | |
843 | +} | |
844 | + | |
845 | +static int test_inet_raw_recv(void) | |
846 | +{ | |
847 | + int fd1 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
848 | + int fd2 = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); | |
849 | + struct sockaddr_in addr = { }; | |
850 | + socklen_t size = sizeof(addr); | |
851 | + static struct iphdr ip = { }; | |
852 | + inet_bind(fd1, 10000); | |
853 | + inet_bind(fd2, 10000); | |
854 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
855 | + ip.version = 4; | |
856 | + ip.ihl = sizeof(struct iphdr) / 4; | |
857 | + ip.protocol = IPPROTO_RAW; | |
858 | + ip.daddr = htonl(INADDR_LOOPBACK); | |
859 | + ip.saddr = ip.daddr; | |
860 | + sendto(fd2, &ip, sizeof(ip), 0, (struct sockaddr *) &addr, | |
861 | + sizeof(addr)); | |
862 | + errno = 0; | |
863 | + recvfrom(fd1, &ip, sizeof(ip), MSG_DONTWAIT, (struct sockaddr *) &addr, | |
864 | + &size); | |
865 | + return errno; | |
866 | +} | |
867 | + | |
868 | +static void unix_bind(const int fd, const _Bool listener) | |
869 | +{ | |
870 | + struct sockaddr_un addr = { }; | |
871 | + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); | |
872 | + addr.sun_family = AF_UNIX; | |
873 | + snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, | |
874 | + "/tmp/caitsith.tmp/%s", listener ? "server" : "client"); | |
875 | + unlink(addr.sun_path); | |
876 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
877 | +} | |
878 | + | |
879 | +static int test_unix_stream_bind(void) | |
880 | +{ | |
881 | + int fd = socket(PF_UNIX, SOCK_STREAM, 0); | |
882 | + struct sockaddr_un addr = { }; | |
883 | + addr.sun_family = AF_UNIX; | |
884 | + errno = 0; | |
885 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family)); | |
886 | + return errno; | |
887 | +} | |
888 | + | |
889 | +static int test_unix_stream_listen(void) | |
890 | +{ | |
891 | + int fd = socket(PF_UNIX, SOCK_STREAM, 0); | |
892 | + unix_bind(fd, 1); | |
893 | + errno = 0; | |
894 | + listen(fd, 5); | |
895 | + return errno; | |
896 | +} | |
897 | + | |
898 | +static int test_unix_stream_connect(void) | |
899 | +{ | |
900 | + int fd1 = socket(PF_UNIX, SOCK_STREAM, 0); | |
901 | + int fd2 = socket(PF_UNIX, SOCK_STREAM, 0); | |
902 | + struct sockaddr_un addr = { }; | |
903 | + socklen_t size = sizeof(addr); | |
904 | + unix_bind(fd1, 1); | |
905 | + unix_bind(fd2, 0); | |
906 | + listen(fd1, 0); | |
907 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
908 | + errno = 0; | |
909 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
910 | + return errno; | |
911 | +} | |
912 | + | |
913 | +static int test_unix_stream_accept(void) | |
914 | +{ | |
915 | + int fd1 = socket(PF_UNIX, SOCK_STREAM, 0); | |
916 | + int fd2 = socket(PF_UNIX, SOCK_STREAM, 0); | |
917 | + struct sockaddr_un addr = { }; | |
918 | + socklen_t size = sizeof(addr); | |
919 | + unix_bind(fd1, 1); | |
920 | + unix_bind(fd2, 0); | |
921 | + listen(fd1, 0); | |
922 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
923 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
924 | + errno = 0; | |
925 | + accept(fd1, (struct sockaddr *) &addr, &size); | |
926 | + return errno; | |
927 | +} | |
928 | + | |
929 | +static int test_unix_dgram_bind(void) | |
930 | +{ | |
931 | + int fd = socket(PF_UNIX, SOCK_DGRAM, 0); | |
932 | + int err; | |
933 | + struct sockaddr_un addr = { }; | |
934 | + addr.sun_family = AF_UNIX; | |
935 | + errno = 0; | |
936 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr)); | |
937 | + err = errno; | |
938 | + close(fd); | |
939 | + return err; | |
940 | +} | |
941 | + | |
942 | +static int test_unix_dgram_connect(void) | |
943 | +{ | |
944 | + int fd1 = socket(PF_UNIX, SOCK_DGRAM, 0); | |
945 | + int fd2 = socket(PF_UNIX, SOCK_DGRAM, 0); | |
946 | + int err; | |
947 | + struct sockaddr_un addr = { }; | |
948 | + socklen_t size = sizeof(addr); | |
949 | + unix_bind(fd1, 1); | |
950 | + unix_bind(fd2, 0); | |
951 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
952 | + errno = 0; | |
953 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
954 | + err = errno; | |
955 | + close(fd1); | |
956 | + close(fd2); | |
957 | + return err; | |
958 | +} | |
959 | + | |
960 | +static int test_unix_dgram_send(void) | |
961 | +{ | |
962 | + int fd1 = socket(PF_UNIX, SOCK_DGRAM, 0); | |
963 | + int fd2 = socket(PF_UNIX, SOCK_DGRAM, 0); | |
964 | + int err; | |
965 | + struct sockaddr_un addr = { }; | |
966 | + socklen_t size = sizeof(addr); | |
967 | + unix_bind(fd1, 1); | |
968 | + unix_bind(fd2, 0); | |
969 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
970 | + errno = 0; | |
971 | + sendto(fd2, "", 1, 0, (struct sockaddr *) &addr, sizeof(addr)); | |
972 | + err = errno; | |
973 | + close(fd1); | |
974 | + close(fd2); | |
975 | + return err; | |
976 | +} | |
977 | + | |
978 | +static int test_unix_dgram_recv(void) | |
979 | +{ | |
980 | + int fd1 = socket(PF_UNIX, SOCK_DGRAM, 0); | |
981 | + int fd2 = socket(PF_UNIX, SOCK_DGRAM, 0); | |
982 | + int err; | |
983 | + char c; | |
984 | + struct sockaddr_un addr = { }; | |
985 | + socklen_t size = sizeof(addr); | |
986 | + unix_bind(fd1, 1); | |
987 | + unix_bind(fd2, 0); | |
988 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
989 | + sendto(fd2, "", 1, 0, (struct sockaddr *) &addr, sizeof(addr)); | |
990 | + errno = 0; | |
991 | + recvfrom(fd1, &c, 1, MSG_DONTWAIT, (struct sockaddr *) &addr, &size); | |
992 | + err = errno; | |
993 | + close(fd1); | |
994 | + close(fd2); | |
995 | + return err; | |
996 | +} | |
997 | + | |
998 | +static int test_unix_seqpacket_bind(void) | |
999 | +{ | |
1000 | + int fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); | |
1001 | + int err; | |
1002 | + struct sockaddr_un addr = { }; | |
1003 | + addr.sun_family = AF_UNIX; | |
1004 | + errno = 0; | |
1005 | + bind(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family)); | |
1006 | + err = errno; | |
1007 | + close(fd); | |
1008 | + return err; | |
1009 | +} | |
1010 | + | |
1011 | +static int test_unix_seqpacket_listen(void) | |
1012 | +{ | |
1013 | + int fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); | |
1014 | + int err; | |
1015 | + unix_bind(fd, 1); | |
1016 | + errno = 0; | |
1017 | + listen(fd, 5); | |
1018 | + err = errno; | |
1019 | + close(fd); | |
1020 | + return err; | |
1021 | +} | |
1022 | + | |
1023 | +static int test_unix_seqpacket_connect(void) | |
1024 | +{ | |
1025 | + int fd1 = socket(PF_UNIX, SOCK_SEQPACKET, 0); | |
1026 | + int fd2 = socket(PF_UNIX, SOCK_SEQPACKET, 0); | |
1027 | + int err; | |
1028 | + struct sockaddr_un addr = { }; | |
1029 | + socklen_t size = sizeof(addr); | |
1030 | + unix_bind(fd1, 1); | |
1031 | + unix_bind(fd2, 0); | |
1032 | + listen(fd1, 0); | |
1033 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
1034 | + errno = 0; | |
1035 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
1036 | + err = errno; | |
1037 | + close(fd1); | |
1038 | + close(fd2); | |
1039 | + return err; | |
1040 | +} | |
1041 | + | |
1042 | +static int test_unix_seqpacket_accept(void) | |
1043 | +{ | |
1044 | + int fd1 = socket(PF_UNIX, SOCK_SEQPACKET, 0); | |
1045 | + int fd2 = socket(PF_UNIX, SOCK_SEQPACKET, 0); | |
1046 | + struct sockaddr_un addr = { }; | |
1047 | + socklen_t size = sizeof(addr); | |
1048 | + unix_bind(fd1, 1); | |
1049 | + unix_bind(fd2, 0); | |
1050 | + listen(fd1, 0); | |
1051 | + getsockname(fd1, (struct sockaddr *) &addr, &size); | |
1052 | + connect(fd2, (struct sockaddr *) &addr, sizeof(addr)); | |
1053 | + errno = 0; | |
1054 | + accept(fd1, (struct sockaddr *) &addr, &size); | |
1055 | + return errno; | |
1056 | +} | |
1057 | + | |
1058 | +static int test_environ(void) | |
1059 | +{ | |
1060 | + char * const argv[2] = { "true", NULL }; | |
1061 | + char * const envp[2] = { "HOME=/", NULL }; | |
1062 | + execve(BINDIR "/true", argv, envp); | |
1063 | + return errno; | |
1064 | +} | |
1065 | + | |
1066 | +static int test_ptrace(void) | |
1067 | +{ | |
1068 | + ptrace(PTRACE_ATTACH, 1, NULL, NULL); | |
1069 | + return errno; | |
1070 | +} | |
1071 | + | |
1072 | +static int test_signal(void) | |
1073 | +{ | |
1074 | + kill(1, 1); | |
1075 | + return errno; | |
1076 | +} | |
1077 | + | |
1078 | +static int test_modify_policy(void) | |
1079 | +{ | |
1080 | + const char *policy = "1 acl modify_policy\n"; | |
1081 | + /* | |
1082 | + * Try to execute a non-executable in order to clear cached policy | |
1083 | + * manager attribute. | |
1084 | + */ | |
1085 | + const int fd = open("/tmp/caitsith.tmp/exec", | |
1086 | + O_CREAT | O_TRUNC | O_WRONLY, 0600); | |
1087 | + fchmod(fd, 0700); | |
1088 | + close(fd); | |
1089 | + execl("/tmp/caitsith.tmp/exec", "exec", NULL); | |
1090 | + /* Now policy manager attribute was cleared. */ | |
1091 | + errno = 0; | |
1092 | + write(policy_fd, policy, strlen(policy)); | |
1093 | + return errno; | |
1094 | +} | |
1095 | + | |
1096 | +static int test_use_netlink_socket(void) | |
1097 | +{ | |
1098 | + const int fd = socket(AF_ROUTE, SOCK_RAW, 0); | |
1099 | + const int err = errno; | |
1100 | + close(fd); | |
1101 | + return err; | |
1102 | +} | |
1103 | + | |
1104 | +static int test_use_packet_socket(void) | |
1105 | +{ | |
1106 | + const int fd = socket(AF_PACKET, SOCK_RAW, 0); | |
1107 | + const int err = errno; | |
1108 | + close(fd); | |
1109 | + return err; | |
1110 | +} | |
1111 | + | |
1112 | +static int test_use_reboot(void) | |
1113 | +{ | |
1114 | + FILE *fp = fopen("/proc/sys/kernel/ctrl-alt-del", "a+"); | |
1115 | + unsigned int c; | |
1116 | + int err; | |
1117 | + if (fp && fscanf(fp, "%u", &c) == 1) { | |
1118 | + errno = 0; | |
1119 | + reboot(LINUX_REBOOT_CMD_CAD_ON); | |
1120 | + err = errno; | |
1121 | + rewind(fp); | |
1122 | + fprintf(fp, "%u\n", c); | |
1123 | + } else { | |
1124 | + errno = 0; | |
1125 | + /* Use invalid value */ | |
1126 | + reboot(0x0000C0DE); | |
1127 | + err = errno; | |
1128 | + } | |
1129 | + if (fp) | |
1130 | + fclose(fp); | |
1131 | + return err; | |
1132 | +} | |
1133 | + | |
1134 | +static int test_use_vhangup(void) | |
1135 | +{ | |
1136 | + setsid(); | |
1137 | + errno = 0; | |
1138 | + vhangup(); | |
1139 | + return errno; | |
1140 | +} | |
1141 | + | |
1142 | +static int test_set_time_by_stime(void) | |
1143 | +{ | |
1144 | + const time_t now = time(NULL); | |
1145 | + errno = 0; | |
1146 | + stime(&now); | |
1147 | + return errno; | |
1148 | +} | |
1149 | + | |
1150 | +static int test_set_time_by_settimeofday(void) | |
1151 | +{ | |
1152 | + struct timeval tv; | |
1153 | + struct timezone tz; | |
1154 | + gettimeofday(&tv, &tz); | |
1155 | + errno = 0; | |
1156 | + settimeofday(&tv, &tz); | |
1157 | + return errno; | |
1158 | +} | |
1159 | + | |
1160 | +static int test_set_time_by_adjtimex(void) | |
1161 | +{ | |
1162 | + struct timex buf = { }; | |
1163 | + buf.modes = 0x100; /* Use invalid value so that the clock | |
1164 | + won't change. */ | |
1165 | + adjtimex(&buf); | |
1166 | + return errno; | |
1167 | +} | |
1168 | + | |
1169 | +static int test_set_priority_by_nice(void) | |
1170 | +{ | |
1171 | + nice(0); | |
1172 | + return errno; | |
1173 | +} | |
1174 | + | |
1175 | +static int test_set_priority_by_setpriority(void) | |
1176 | +{ | |
1177 | + const int priority = getpriority(PRIO_PROCESS, getpid()); | |
1178 | + errno = 0; | |
1179 | + setpriority(PRIO_PROCESS, getpid(), priority); | |
1180 | + return errno; | |
1181 | +} | |
1182 | + | |
1183 | +static int test_set_hostname_by_sethostname(void) | |
1184 | +{ | |
1185 | + char buffer[4096] = { }; | |
1186 | + gethostname(buffer, sizeof(buffer) - 1); | |
1187 | + errno = 0; | |
1188 | + sethostname(buffer, strlen(buffer)); | |
1189 | + return errno; | |
1190 | +} | |
1191 | + | |
1192 | +static int test_set_hostname_by_setdomainname(void) | |
1193 | +{ | |
1194 | + char buffer[4096] = { }; | |
1195 | + getdomainname(buffer, sizeof(buffer) - 1); | |
1196 | + errno = 0; | |
1197 | + setdomainname(buffer, strlen(buffer)); | |
1198 | + return errno; | |
1199 | +} | |
1200 | + | |
1201 | +static int test_use_kernel_module_by_init_module(void) | |
1202 | +{ | |
1203 | + init_module("", NULL); | |
1204 | + return errno; | |
1205 | +} | |
1206 | + | |
1207 | +static int test_use_kernel_module_by_delete_module(void) | |
1208 | +{ | |
1209 | + delete_module(""); | |
1210 | + return errno; | |
1211 | +} | |
1212 | + | |
1213 | +static int test_use_new_kernel(void) | |
1214 | +{ | |
1215 | + sys_kexec_load(0, 0, NULL, 0); | |
1216 | + return errno; | |
1217 | +} | |
1218 | + | |
1219 | +static void reset_policy(void) | |
1220 | +{ | |
1221 | + FILE *fp2 = fopen(POLDIR "/policy", "r"); | |
1222 | + FILE *fp1 = fopen(POLDIR "/policy", "w"); | |
1223 | + if (!fp1 || !fp2) { | |
1224 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
1225 | + exit(1); | |
1226 | + } | |
1227 | + while (1) { | |
1228 | + const int c = fgetc(fp2); | |
1229 | + if (c == EOF) | |
1230 | + break; | |
1231 | + fputc(c, fp1); | |
1232 | + if (c == '\n') | |
1233 | + fprintf(fp1, "delete "); | |
1234 | + } | |
1235 | + fclose(fp2); | |
1236 | + fclose(fp1); | |
1237 | + | |
1238 | + /* Do not leave the init process in stopped state. */ | |
1239 | + kill(1, SIGCONT); | |
1240 | + | |
1241 | + /* Undo mount("/", MS_REC|MS_SHARED) made by systemd. */ | |
1242 | + mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL); | |
1243 | +} | |
1244 | + | |
1245 | +int main(int argc, char *argv[]) | |
1246 | +{ | |
1247 | + int i; | |
1248 | + static const struct { | |
1249 | + const char *action; | |
1250 | + int (*func) (void); | |
1251 | + } testcases[] = { | |
1252 | + { "execute", test_execute }, | |
1253 | + { "read", test_read }, | |
1254 | + { "write", test_write }, | |
1255 | + { "create", test_create_no_append }, | |
1256 | + { "read", test_create_no_append }, | |
1257 | + { "write", test_create_no_append }, | |
1258 | + /* { "truncate", test_create_no_append }, */ | |
1259 | + { "create", test_create_append }, | |
1260 | + { "read", test_create_append }, | |
1261 | + { "append", test_create_append }, | |
1262 | + /* { "truncate", test_create_append }, */ | |
1263 | + { "write", test_fcntl_clear_append }, | |
1264 | + { "append", test_fcntl_set_append }, | |
1265 | + { "unlink", test_unlink }, | |
1266 | + { "getattr", test_getattr }, | |
1267 | + { "mkdir", test_mkdir }, | |
1268 | + { "rmdir", test_rmdir }, | |
1269 | + { "mkfifo", test_mkfifo }, | |
1270 | + { "mksock", test_mksock_by_mknod }, | |
1271 | + { "mksock", test_mksock_by_bind }, | |
1272 | + { "truncate", test_truncate }, | |
1273 | + { "symlink", test_symlink }, | |
1274 | + { "mkchar", test_mkchar }, | |
1275 | + { "mkblock", test_mkblock }, | |
1276 | + { "link", test_link }, | |
1277 | + { "rename", test_rename }, | |
1278 | + { "chmod", test_chmod }, | |
1279 | + { "chown", test_chown }, | |
1280 | + { "chgrp", test_chgrp }, | |
1281 | + { "ioctl", test_ioctl }, | |
1282 | + { "chroot", test_chroot }, | |
1283 | + { "mount", test_mount }, | |
1284 | + { "unmount", test_unmount }, | |
1285 | + { "pivot_root", test_pivot_root }, | |
1286 | + { "inet_stream_bind", test_inet_stream_bind }, | |
1287 | + { "inet_stream_listen", test_inet_stream_listen }, | |
1288 | + { "inet_stream_connect", test_inet_stream_connect }, | |
1289 | + { "inet_stream_accept", test_inet_stream_accept }, | |
1290 | + { "inet_dgram_bind", test_inet_dgram_bind }, | |
1291 | + { "inet_dgram_send", test_inet_dgram_connect }, | |
1292 | + { "inet_dgram_send", test_inet_dgram_send }, | |
1293 | + { "inet_dgram_recv", test_inet_dgram_recv }, | |
1294 | + { "inet_raw_bind", test_inet_raw_bind }, | |
1295 | + { "inet_raw_send", test_inet_raw_connect }, | |
1296 | + { "inet_raw_send", test_inet_raw_send }, | |
1297 | + { "inet_raw_recv", test_inet_raw_recv }, | |
1298 | + { "unix_stream_bind", test_unix_stream_bind }, | |
1299 | + { "unix_stream_listen", test_unix_stream_listen }, | |
1300 | + { "unix_stream_connect", test_unix_stream_connect }, | |
1301 | + { "unix_stream_accept", test_unix_stream_accept }, | |
1302 | + { "unix_dgram_bind", test_unix_dgram_bind }, | |
1303 | + { "unix_dgram_send", test_unix_dgram_connect }, | |
1304 | + { "unix_dgram_send", test_unix_dgram_send }, | |
1305 | + { "unix_dgram_recv", test_unix_dgram_recv }, | |
1306 | + { "unix_seqpacket_bind", test_unix_seqpacket_bind }, | |
1307 | + { "unix_seqpacket_listen", test_unix_seqpacket_listen }, | |
1308 | + { "unix_seqpacket_connect", test_unix_seqpacket_connect }, | |
1309 | + { "unix_seqpacket_accept", test_unix_seqpacket_accept }, | |
1310 | + { "environ", test_environ }, | |
1311 | + { "ptrace", test_ptrace }, | |
1312 | + { "signal", test_signal }, | |
1313 | + { "modify_policy", test_modify_policy }, | |
1314 | + { "use_netlink_socket", test_use_netlink_socket }, | |
1315 | + { "use_packet_socket", test_use_packet_socket }, | |
1316 | + { "use_reboot", test_use_reboot }, | |
1317 | + { "use_vhangup", test_use_vhangup }, | |
1318 | + { "set_time", test_set_time_by_stime }, | |
1319 | + { "set_time", test_set_time_by_settimeofday }, | |
1320 | + { "set_time", test_set_time_by_adjtimex }, | |
1321 | + { "set_priority", test_set_priority_by_nice }, | |
1322 | + { "set_priority", test_set_priority_by_setpriority }, | |
1323 | + { "set_hostname", test_set_hostname_by_sethostname }, | |
1324 | + { "set_hostname", test_set_hostname_by_setdomainname }, | |
1325 | + { "use_kernel_module", test_use_kernel_module_by_init_module }, | |
1326 | + { "use_kernel_module", | |
1327 | + test_use_kernel_module_by_delete_module }, | |
1328 | + { "use_new_kernel", test_use_new_kernel }, | |
1329 | + { NULL, NULL } | |
1330 | + }; | |
1331 | + | |
1332 | + reset_policy(); | |
1333 | + | |
1334 | + startup(); | |
1335 | + for (i = 0; testcases[i].action; i++) | |
1336 | + test_action(testcases[i].action, testcases[i].func); | |
1337 | + fprintf(stderr, "Done.\n"); | |
1338 | + return 0; | |
1339 | +} |
@@ -0,0 +1,1843 @@ | ||
1 | +/* | |
2 | + * caitsith_lsm_test.c | |
3 | + * | |
4 | + * Copyright (C) 2012-2013 Tetsuo Handa | |
5 | + * | |
6 | + * Version: 0.2 2016/10/05 | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License v2 as published by the | |
10 | + * Free Software Foundation. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | + * more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License along with | |
18 | + * this program; if not, write to the Free Software Foundation, Inc., | |
19 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
20 | + */ | |
21 | + | |
22 | +#include <stdio.h> | |
23 | +#include <stdlib.h> | |
24 | +#include <string.h> | |
25 | +#include <unistd.h> | |
26 | +#include <sys/types.h> | |
27 | +#include <sys/stat.h> | |
28 | +#include <sys/socket.h> | |
29 | +#include <netinet/in.h> | |
30 | +#include <sys/wait.h> | |
31 | +#include <linux/ip.h> | |
32 | +#include <fcntl.h> | |
33 | +#include <errno.h> | |
34 | +#include <sys/mount.h> | |
35 | +#ifndef MS_REC | |
36 | +#define MS_REC 16384 | |
37 | +#endif | |
38 | +#ifndef MS_PRIVATE | |
39 | +#define MS_PRIVATE (1 << 18) | |
40 | +#endif | |
41 | + | |
42 | +static FILE *fp = NULL; | |
43 | + | |
44 | +static void set(const char *str) | |
45 | +{ | |
46 | + fprintf(fp, "%s\n", str); | |
47 | + fflush(fp); | |
48 | + errno = 0; | |
49 | +} | |
50 | + | |
51 | +static void unset(const char *str) | |
52 | +{ | |
53 | + fprintf(fp, "delete %s\n", str); | |
54 | + fflush(fp); | |
55 | + errno = 0; | |
56 | +} | |
57 | + | |
58 | +static void unset2(const char *str) | |
59 | +{ | |
60 | + const char *cp = str; | |
61 | + while (*cp) { | |
62 | + if (*cp++ != '\n') | |
63 | + continue; | |
64 | + fprintf(fp, "delete "); | |
65 | + fwrite(str, cp - str, 1, fp); | |
66 | + str = cp; | |
67 | + } | |
68 | + fprintf(fp, "delete %s\n", str); | |
69 | + fflush(fp); | |
70 | + errno = 0; | |
71 | +} | |
72 | + | |
73 | +static void check(const char *prompt, int result) | |
74 | +{ | |
75 | + int err = errno; | |
76 | + printf("%s%s\n", prompt, result ? "Success" : "Failed"); | |
77 | + if (!result) { | |
78 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
79 | + { | |
80 | + int fd2 = open(POLDIR "/self_domain", O_RDONLY); | |
81 | + char c; | |
82 | + fprintf(stderr, "task.domain=\""); | |
83 | + while (read(fd2, &c, 1) == 1) | |
84 | + fprintf(stderr, "%c", c); | |
85 | + close(fd2); | |
86 | + fprintf(stderr, "\"\n"); | |
87 | + } | |
88 | + kill(1, SIGCONT); | |
89 | + exit(1); | |
90 | + } | |
91 | + printf("\n"); | |
92 | + fflush(stdout); | |
93 | +} | |
94 | + | |
95 | +static void check_init(const char *prompt, const char *expected) | |
96 | +{ | |
97 | + int result; | |
98 | + int fd = open(POLDIR "/.process_status", O_RDWR); | |
99 | + char buffer[1024]; | |
100 | + char *cp; | |
101 | + memset(buffer, 0, sizeof(buffer)); | |
102 | + kill(1, SIGHUP); | |
103 | + sleep(1); | |
104 | + write(fd, "1\n", 2); | |
105 | + read(fd, buffer, sizeof(buffer) - 1); | |
106 | + close(fd); | |
107 | + cp = strchr(buffer, ' '); | |
108 | + if (cp++) | |
109 | + memmove(buffer, cp, strlen(cp) + 1); | |
110 | + result = !strcmp(buffer, expected); | |
111 | + printf("%s%s\n", prompt, result ? "Success" : "Failed"); | |
112 | + if (!result) { | |
113 | + fprintf(stderr, "Err: expected='%s' result='%s'\n", | |
114 | + expected, buffer); | |
115 | + kill(1, SIGCONT); | |
116 | + exit(1); | |
117 | + } | |
118 | + printf("\n"); | |
119 | + fflush(stdout); | |
120 | +} | |
121 | + | |
122 | +static void test_task_transition(void) | |
123 | +{ | |
124 | + int fd = open(POLDIR "/self_domain", O_WRONLY); | |
125 | + char *policy; | |
126 | + | |
127 | + policy = "100 acl manual_domain_transition\n" | |
128 | + "0 allow domain=\"domain\\$\"\n"; | |
129 | + set(policy); | |
130 | + check(policy, write(fd, "domain0", 7) != EOF); | |
131 | + check(policy, write(fd, "domain10", 8) != EOF); | |
132 | + check(policy, write(fd, "domainXYX", 9) == EOF); | |
133 | + check(policy, write(fd, "domain200", 9) != EOF); | |
134 | + unset(policy); | |
135 | + | |
136 | + policy = "100 acl auto_domain_transition\n" | |
137 | + "0 allow task.pid=1 transition=\"<init3>\"\n"; | |
138 | + set(policy); | |
139 | + check_init(policy, "<init3>"); | |
140 | + unset(policy); | |
141 | + | |
142 | + policy = "100 acl auto_domain_transition\n" | |
143 | + "0 allow task.pid=1 task.uid!=0 transition=\"<init2>\"\n"; | |
144 | + set(policy); | |
145 | + check_init(policy, "<init3>"); | |
146 | + unset(policy); | |
147 | + | |
148 | + policy = "100 acl auto_domain_transition\n" | |
149 | + "0 allow task.pid=1 transition=\"<init>\"\n"; | |
150 | + set(policy); | |
151 | + check_init(policy, "<init>"); | |
152 | + unset(policy); | |
153 | + | |
154 | + close(fd); | |
155 | +} | |
156 | + | |
157 | +static void test_file_read(void) | |
158 | +{ | |
159 | + int fd; | |
160 | + char *policy; | |
161 | + | |
162 | + policy = "100 acl read\n"; | |
163 | + set(policy); | |
164 | + fd = open("/dev/null", O_RDONLY); | |
165 | + check(policy, fd != EOF); | |
166 | + close(fd); | |
167 | + unset(policy); | |
168 | + | |
169 | + policy = "100 acl read\n" | |
170 | + "0 allow\n" | |
171 | + "1 deny\n"; | |
172 | + set(policy); | |
173 | + fd = open("/dev/null", O_RDONLY); | |
174 | + check(policy, fd != EOF); | |
175 | + close(fd); | |
176 | + unset(policy); | |
177 | + | |
178 | + policy = "100 acl read\n" | |
179 | + "0 deny\n" | |
180 | + "1 allow\n"; | |
181 | + set(policy); | |
182 | + fd = open("/dev/null", O_RDONLY); | |
183 | + check(policy, fd == EOF); | |
184 | + close(fd); | |
185 | + unset(policy); | |
186 | + | |
187 | + policy = "100 acl read path=\"/dev/null\"\n" | |
188 | + "0 allow\n" | |
189 | + "1 deny\n"; | |
190 | + set(policy); | |
191 | + fd = open("/dev/null", O_RDONLY); | |
192 | + check(policy, fd != EOF); | |
193 | + close(fd); | |
194 | + unset(policy); | |
195 | + | |
196 | + policy = "100 acl read path=\"/dev/null\"\n" | |
197 | + "0 deny\n" | |
198 | + "1 allow\n"; | |
199 | + set(policy); | |
200 | + fd = open("/dev/null", O_RDONLY); | |
201 | + check(policy, fd == EOF); | |
202 | + close(fd); | |
203 | + unset(policy); | |
204 | + | |
205 | + policy = "100 acl read\n" | |
206 | + "0 allow path=\"/dev/null\"\n" | |
207 | + "1 deny\n"; | |
208 | + set(policy); | |
209 | + fd = open("/dev/null", O_RDONLY); | |
210 | + check(policy, fd != EOF); | |
211 | + close(fd); | |
212 | + unset(policy); | |
213 | + | |
214 | + policy = "100 acl read\n" | |
215 | + "0 deny path=\"/dev/null\"\n" | |
216 | + "1 allow\n"; | |
217 | + set(policy); | |
218 | + fd = open("/dev/null", O_RDONLY); | |
219 | + check(policy, fd == EOF); | |
220 | + close(fd); | |
221 | + unset(policy); | |
222 | + | |
223 | + policy = "100 acl read\n" | |
224 | + "0 allow path.type=char path.dev_major=1 path.dev_minor=3\n" | |
225 | + "1 deny\n"; | |
226 | + set(policy); | |
227 | + fd = open("/dev/null", O_RDONLY); | |
228 | + check(policy, fd != EOF); | |
229 | + close(fd); | |
230 | + unset(policy); | |
231 | + | |
232 | + policy = "100 acl read\n" | |
233 | + "0 deny path.type=char path.dev_major=1 path.dev_minor=3\n" | |
234 | + "1 allow\n"; | |
235 | + set(policy); | |
236 | + fd = open("/dev/null", O_RDONLY); | |
237 | + check(policy, fd == EOF); | |
238 | + close(fd); | |
239 | + unset(policy); | |
240 | + | |
241 | + policy = "100 acl read\n" | |
242 | + "0 allow path.type=char path.dev_major=1 path.dev_minor!=3\n" | |
243 | + "1 deny\n"; | |
244 | + set(policy); | |
245 | + fd = open("/dev/null", O_RDONLY); | |
246 | + check(policy, fd == EOF); | |
247 | + close(fd); | |
248 | + unset(policy); | |
249 | + | |
250 | + policy = "100 acl read\n" | |
251 | + "0 deny path.type=char path.dev_major=1 path.dev_minor!=3\n" | |
252 | + "1 allow\n"; | |
253 | + set(policy); | |
254 | + fd = open("/dev/null", O_RDONLY); | |
255 | + check(policy, fd != EOF); | |
256 | + close(fd); | |
257 | + unset(policy); | |
258 | + | |
259 | + policy = "string_group GROUP1 /dev/null\n" | |
260 | + "100 acl read\n" | |
261 | + "0 allow path=@GROUP1\n" | |
262 | + "1 deny\n"; | |
263 | + set(policy); | |
264 | + fd = open("/dev/null", O_RDONLY); | |
265 | + check(policy, fd != EOF); | |
266 | + close(fd); | |
267 | + unset2(policy); | |
268 | + | |
269 | + policy = "string_group GROUP1 /dev/null\n" | |
270 | + "100 acl read\n" | |
271 | + "0 deny path=@GROUP1\n" | |
272 | + "1 allow\n"; | |
273 | + set(policy); | |
274 | + fd = open("/dev/null", O_RDONLY); | |
275 | + check(policy, fd == EOF); | |
276 | + close(fd); | |
277 | + unset2(policy); | |
278 | + | |
279 | + policy = "string_group GROUP1 /dev/null\n" | |
280 | + "100 acl read\n" | |
281 | + "0 allow path!=@GROUP1\n" | |
282 | + "1 deny\n"; | |
283 | + set(policy); | |
284 | + fd = open("/dev/null", O_RDONLY); | |
285 | + check(policy, fd == EOF); | |
286 | + close(fd); | |
287 | + unset2(policy); | |
288 | + | |
289 | + policy = "string_group GROUP1 /dev/null\n" | |
290 | + "100 acl read\n" | |
291 | + "0 deny path!=@GROUP1\n" | |
292 | + "1 allow\n"; | |
293 | + set(policy); | |
294 | + fd = open("/dev/null", O_RDONLY); | |
295 | + check(policy, fd != EOF); | |
296 | + close(fd); | |
297 | + unset2(policy); | |
298 | + | |
299 | + policy = "string_group GROUP1 /dev/null\n" | |
300 | + "number_group MAJOR 1\n" | |
301 | + "number_group MINOR 3\n" | |
302 | + "100 acl read\n" | |
303 | + "0 allow path=@GROUP1 path.dev_major=@MAJOR" | |
304 | + " path.dev_minor=@MINOR\n" | |
305 | + "1 deny\n"; | |
306 | + set(policy); | |
307 | + fd = open("/dev/null", O_RDONLY); | |
308 | + check(policy, fd != EOF); | |
309 | + close(fd); | |
310 | + unset2(policy); | |
311 | + | |
312 | + policy = "string_group GROUP1 /dev/null\n" | |
313 | + "number_group MAJOR 1\n" | |
314 | + "number_group MINOR 3\n" | |
315 | + "100 acl read\n" | |
316 | + "0 deny path=@GROUP1 path.dev_major=@MAJOR" | |
317 | + " path.dev_minor=@MINOR\n" | |
318 | + "1 allow\n"; | |
319 | + set(policy); | |
320 | + fd = open("/dev/null", O_RDONLY); | |
321 | + check(policy, fd == EOF); | |
322 | + close(fd); | |
323 | + unset2(policy); | |
324 | + | |
325 | + policy = "string_group GROUP1 /dev/zero\n" | |
326 | + "string_group GROUP1 /dev/null\n" | |
327 | + "string_group GROUP1 /dev/urandom\n" | |
328 | + "number_group MAJOR 0\n" | |
329 | + "number_group MAJOR 2-255\n" | |
330 | + "number_group MINOR 00-0x2\n" | |
331 | + "number_group MINOR 255\n" | |
332 | + "100 acl read\n" | |
333 | + "0 allow path=@GROUP1 path.dev_major=@MAJOR" | |
334 | + " path.dev_minor=@MINOR\n" | |
335 | + "1 deny\n"; | |
336 | + set(policy); | |
337 | + fd = open("/dev/null", O_RDONLY); | |
338 | + check(policy, fd == EOF); | |
339 | + close(fd); | |
340 | + unset2(policy); | |
341 | + | |
342 | + policy = "string_group GROUP1 /dev/zero\n" | |
343 | + "string_group GROUP1 /dev/null\n" | |
344 | + "string_group GROUP1 /dev/urandom\n" | |
345 | + "number_group MAJOR 0\n" | |
346 | + "number_group MAJOR 2-255\n" | |
347 | + "number_group MINOR 00-0x2\n" | |
348 | + "number_group MINOR 255\n" | |
349 | + "100 acl read\n" | |
350 | + "0 allow path=@GROUP1 path.dev_major!=@MAJOR" | |
351 | + " path.dev_minor!=@MINOR\n" | |
352 | + "1 deny\n"; | |
353 | + set(policy); | |
354 | + fd = open("/dev/null", O_RDONLY); | |
355 | + check(policy, fd != EOF); | |
356 | + close(fd); | |
357 | + unset2(policy); | |
358 | +} | |
359 | + | |
360 | +static void test_file_write(void) | |
361 | +{ | |
362 | + int fd; | |
363 | + char *policy; | |
364 | + | |
365 | + policy = "100 acl write\n" | |
366 | + "0 allow\n" | |
367 | + "100 acl append\n" | |
368 | + "0 deny\n"; | |
369 | + set(policy); | |
370 | + fd = open("/dev/null", O_WRONLY); | |
371 | + check(policy, fd != EOF); | |
372 | + close(fd); | |
373 | + unset2(policy); | |
374 | + | |
375 | + policy = "100 acl write\n" | |
376 | + "0 deny\n" | |
377 | + "100 acl append\n" | |
378 | + "0 allow\n"; | |
379 | + set(policy); | |
380 | + fd = open("/dev/null", O_WRONLY); | |
381 | + check(policy, fd == EOF); | |
382 | + close(fd); | |
383 | + unset2(policy); | |
384 | + | |
385 | + policy = "100 acl write\n" | |
386 | + "0 allow\n" | |
387 | + "100 acl append\n" | |
388 | + "0 deny\n"; | |
389 | + set(policy); | |
390 | + fd = open("/dev/null", O_WRONLY | O_APPEND); | |
391 | + check(policy, fd == EOF); | |
392 | + close(fd); | |
393 | + unset2(policy); | |
394 | + | |
395 | + policy = "100 acl write\n" | |
396 | + "0 deny\n" | |
397 | + "100 acl append\n" | |
398 | + "0 append\n"; | |
399 | + set(policy); | |
400 | + fd = open("/dev/null", O_WRONLY | O_APPEND); | |
401 | + check(policy, fd != EOF); | |
402 | + close(fd); | |
403 | + unset2(policy); | |
404 | + | |
405 | + policy = "100 acl write\n" | |
406 | + "0 allow path.type=char path.dev_major=1 path.dev_minor=3\n" | |
407 | + "1 deny\n"; | |
408 | + set(policy); | |
409 | + fd = open("/dev/null", O_WRONLY | O_TRUNC); | |
410 | + check(policy, fd != EOF); | |
411 | + close(fd); | |
412 | + unset(policy); | |
413 | + | |
414 | + policy = "100 acl write\n" | |
415 | + "0 allow path.type=char path.dev_major=1" | |
416 | + " path.dev_minor=@MINOR\n" | |
417 | + "1 deny\n"; | |
418 | + set(policy); | |
419 | + fd = open("/dev/null", O_WRONLY | O_TRUNC); | |
420 | + check(policy, fd == EOF); | |
421 | + close(fd); | |
422 | + unset(policy); | |
423 | + | |
424 | + policy = "100 acl write\n" | |
425 | + "0 allow path.parent.uid=0 path.parent.perm=0755\n" | |
426 | + "1 deny\n"; | |
427 | + set(policy); | |
428 | + fd = open("/dev/null", O_WRONLY); | |
429 | + check(policy, fd != EOF); | |
430 | + close(fd); | |
431 | + unset(policy); | |
432 | + | |
433 | + policy = "100 acl write\n" | |
434 | + "0 allow path.parent.uid=task.uid path.parent.gid=task.gid\n" | |
435 | + "1 deny\n"; | |
436 | + set(policy); | |
437 | + fd = open("/dev/null", O_WRONLY); | |
438 | + check(policy, fd != EOF); | |
439 | + close(fd); | |
440 | + unset(policy); | |
441 | + | |
442 | + policy = "100 acl write\n" | |
443 | + "0 allow task.uid=path.parent.uid task.gid=path.parent.gid\n" | |
444 | + "1 deny\n"; | |
445 | + set(policy); | |
446 | + fd = open("/dev/null", O_WRONLY); | |
447 | + check(policy, fd != EOF); | |
448 | + close(fd); | |
449 | + unset(policy); | |
450 | +} | |
451 | + | |
452 | +static void test_file_create(void) | |
453 | +{ | |
454 | + int fd; | |
455 | + char *policy; | |
456 | + | |
457 | + policy = "100 acl create\n" | |
458 | + "0 allow path.uid=0\n" | |
459 | + "1 deny\n"; | |
460 | + set(policy); | |
461 | + unlink("/tmp/file"); | |
462 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
463 | + check(policy, fd == EOF); | |
464 | + close(fd); | |
465 | + unset(policy); | |
466 | + | |
467 | + policy = "100 acl create\n" | |
468 | + "0 allow path=\"dev(¥$,¥$):/tmp/file\" path.parent.uid=0\n" | |
469 | + "0 allow path=\"/tmp/file\" path.parent.uid=0\n" | |
470 | + "0 allow path=\"dev(¥$,¥$):/file\" path.parent.uid=0\n" | |
471 | + "1 deny\n"; | |
472 | + set(policy); | |
473 | + unlink("/tmp/file"); | |
474 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
475 | + check(policy, fd != EOF); | |
476 | + close(fd); | |
477 | + unset(policy); | |
478 | + | |
479 | + policy = "number_group GROUP1 1-0xFFFFFFFF\n" | |
480 | + "100 acl create\n" | |
481 | + "0 allow path.parent.uid!=@GROUP1 perm=0600\n" | |
482 | + "1 deny\n"; | |
483 | + set(policy); | |
484 | + unlink("/tmp/file"); | |
485 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
486 | + check(policy, fd != EOF); | |
487 | + close(fd); | |
488 | + unset2(policy); | |
489 | + | |
490 | + policy = "number_group GROUP1 1-0xFFFFFFFF\n" | |
491 | + "100 acl create\n" | |
492 | + "0 allow path.parent.uid!=@GROUP1 perm!=0600\n" | |
493 | + "1 deny\n"; | |
494 | + set(policy); | |
495 | + unlink("/tmp/file"); | |
496 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
497 | + check(policy, fd == EOF); | |
498 | + close(fd); | |
499 | + unset2(policy); | |
500 | + | |
501 | + policy = "100 acl create\n" | |
502 | + "0 allow path.parent.uid=task.uid\n" | |
503 | + "1 deny\n"; | |
504 | + set(policy); | |
505 | + unlink("/tmp/file"); | |
506 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
507 | + check(policy, fd != EOF); | |
508 | + close(fd); | |
509 | + unset(policy); | |
510 | +} | |
511 | + | |
512 | +static void test_file_unlink(void) | |
513 | +{ | |
514 | + char *policy; | |
515 | + | |
516 | + policy = "100 acl unlink\n" | |
517 | + "0 allow path.uid=0 path.uid=path.parent.uid\n" | |
518 | + "1 deny\n"; | |
519 | + set(policy); | |
520 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
521 | + check(policy, unlink("/tmp/file") == 0); | |
522 | + unset(policy); | |
523 | + | |
524 | + policy = "100 acl unlink\n" | |
525 | + "0 deny path.uid=0 path.uid=path.parent.uid\n" | |
526 | + "1 allow\n"; | |
527 | + set(policy); | |
528 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
529 | + check(policy, unlink("/tmp/file") == EOF); | |
530 | + unset(policy); | |
531 | +} | |
532 | + | |
533 | +static void test_file_link(void) | |
534 | +{ | |
535 | + char *policy; | |
536 | + | |
537 | + policy = "100 acl link\n" | |
538 | + "0 allow old_path.uid=0 old_path.uid=old_path.parent.uid" | |
539 | + " old_path.parent.ino=new_path.parent.ino\n" | |
540 | + "1 deny\n"; | |
541 | + set(policy); | |
542 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
543 | + unlink("/tmp/file2"); | |
544 | + check(policy, link("/tmp/file", "/tmp/file2") == 0); | |
545 | + unset(policy); | |
546 | + | |
547 | + policy = "100 acl link\n" | |
548 | + "0 deny old_path.uid=0 old_path.uid=old_path.parent.uid\n" | |
549 | + "1 allow\n"; | |
550 | + set(policy); | |
551 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
552 | + unlink("/tmp/file2"); | |
553 | + check(policy, link("/tmp/file", "/tmp/file2") == EOF); | |
554 | + unset(policy); | |
555 | +} | |
556 | + | |
557 | +static void test_file_rename(void) | |
558 | +{ | |
559 | + char *policy; | |
560 | + | |
561 | + policy = "100 acl rename\n" | |
562 | + "0 allow old_path.uid=0 old_path.uid=old_path.parent.uid" | |
563 | + " old_path.parent.ino=new_path.parent.ino\n" | |
564 | + "1 deny\n"; | |
565 | + set(policy); | |
566 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
567 | + unlink("/tmp/file2"); | |
568 | + check(policy, rename("/tmp/file", "/tmp/file2") == 0); | |
569 | + unset(policy); | |
570 | + | |
571 | + policy = "100 acl rename\n" | |
572 | + "0 deny old_path.uid=0 old_path.uid=old_path.parent.uid\n" | |
573 | + "1 allow\n"; | |
574 | + set(policy); | |
575 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
576 | + unlink("/tmp/file2"); | |
577 | + check(policy, rename("/tmp/file", "/tmp/file2") == EOF); | |
578 | + unset(policy); | |
579 | +} | |
580 | + | |
581 | +static void test_network_inet_stream(void) | |
582 | +{ | |
583 | + struct sockaddr_in addr1 = { }; | |
584 | + struct sockaddr_in addr2 = { }; | |
585 | + socklen_t size = sizeof(addr1); | |
586 | + int fd1; | |
587 | + int fd2; | |
588 | + int fd3; | |
589 | + char *policy; | |
590 | + char buffer[1024]; | |
591 | + memset(buffer, 0, sizeof(buffer)); | |
592 | + | |
593 | + fd1 = socket(PF_INET, SOCK_STREAM, 0); | |
594 | + fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
595 | + addr1.sin_family = AF_INET; | |
596 | + addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
597 | + | |
598 | + policy = "100 acl inet_stream_bind\n" | |
599 | + "0 allow ip=127.0.0.1 port!=0\n" | |
600 | + "1 deny\n"; | |
601 | + set(policy); | |
602 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
603 | + EOF); | |
604 | + unset(policy); | |
605 | + | |
606 | + policy = "100 acl inet_stream_bind\n" | |
607 | + "0 allow ip!=127.0.0.1 port=0\n" | |
608 | + "1 deny\n"; | |
609 | + set(policy); | |
610 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
611 | + EOF); | |
612 | + unset(policy); | |
613 | + | |
614 | + policy = "100 acl inet_stream_bind\n" | |
615 | + "0 allow ip=127.0.0.1 port=0 path.uid=task.uid\n" | |
616 | + "1 deny\n"; | |
617 | + set(policy); | |
618 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
619 | + EOF); | |
620 | + unset(policy); | |
621 | + | |
622 | + policy = "100 acl inet_stream_bind\n" | |
623 | + "0 allow ip=127.0.0.1 port=0\n" | |
624 | + "1 deny\n"; | |
625 | + set(policy); | |
626 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
627 | + 0); | |
628 | + unset(policy); | |
629 | + | |
630 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
631 | + | |
632 | + snprintf(buffer, sizeof(buffer) - 1, | |
633 | + "100 acl inet_stream_listen\n" | |
634 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
635 | + "1 deny\n", ntohs(addr1.sin_port)); | |
636 | + policy = buffer; | |
637 | + set(policy); | |
638 | + check(policy, listen(fd1, 5) == EOF); | |
639 | + unset(policy); | |
640 | + | |
641 | + snprintf(buffer, sizeof(buffer) - 1, | |
642 | + "100 acl inet_stream_listen\n" | |
643 | + "0 allow ip=127.0.0.1 port=%u\n" | |
644 | + "1 deny\n", ntohs(addr1.sin_port)); | |
645 | + policy = buffer; | |
646 | + set(policy); | |
647 | + check(policy, listen(fd1, 5) == 0); | |
648 | + unset(policy); | |
649 | + | |
650 | + snprintf(buffer, sizeof(buffer) - 1, | |
651 | + "100 acl inet_stream_connect\n" | |
652 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
653 | + "1 deny\n", ntohs(addr1.sin_port)); | |
654 | + policy = buffer; | |
655 | + set(policy); | |
656 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
657 | + == EOF); | |
658 | + unset(policy); | |
659 | + | |
660 | + snprintf(buffer, sizeof(buffer) - 1, | |
661 | + "100 acl inet_stream_connect\n" | |
662 | + "0 allow ip=127.0.0.1 port=%u\n" | |
663 | + "1 deny\n", ntohs(addr1.sin_port)); | |
664 | + policy = buffer; | |
665 | + set(policy); | |
666 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
667 | + == 0); | |
668 | + unset(policy); | |
669 | + | |
670 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
671 | + | |
672 | + snprintf(buffer, sizeof(buffer) - 1, | |
673 | + "100 acl inet_stream_accept\n" | |
674 | + "0 allow ip=127.0.0.1 port=%u\n" | |
675 | + "1 deny\n", ntohs(addr2.sin_port)); | |
676 | + policy = buffer; | |
677 | + set(policy); | |
678 | + fd3 = accept(fd1, NULL, 0); | |
679 | + check(policy, write(fd3, "", 1) == 1); | |
680 | + close(fd3); | |
681 | + unset(policy); | |
682 | + | |
683 | + snprintf(buffer, sizeof(buffer) - 1, | |
684 | + "100 acl inet_stream_connect\n" | |
685 | + "0 allow ip=127.0.0.1 port=%u\n" | |
686 | + "1 deny\n", ntohs(addr1.sin_port)); | |
687 | + policy = buffer; | |
688 | + set(policy); | |
689 | + close(fd2); | |
690 | + fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
691 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
692 | + == 0); | |
693 | + unset(policy); | |
694 | + | |
695 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
696 | + | |
697 | + snprintf(buffer, sizeof(buffer) - 1, | |
698 | + "100 acl inet_stream_accept\n" | |
699 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
700 | + "1 deny\n", ntohs(addr2.sin_port)); | |
701 | + policy = buffer; | |
702 | + set(policy); | |
703 | + fd3 = accept(fd1, NULL, 0); | |
704 | + check(policy, write(fd3, "", 1) == EOF); | |
705 | + close(fd3); | |
706 | + unset(policy); | |
707 | + | |
708 | + close(fd1); | |
709 | + close(fd2); | |
710 | +} | |
711 | + | |
712 | +static void test_network_inet_dgram(void) | |
713 | +{ | |
714 | + struct sockaddr_in addr1 = { }; | |
715 | + struct sockaddr_in addr2 = { }; | |
716 | + socklen_t size = sizeof(addr1); | |
717 | + int fd1; | |
718 | + int fd2; | |
719 | + char *policy; | |
720 | + char buffer[1024]; | |
721 | + memset(buffer, 0, sizeof(buffer)); | |
722 | + | |
723 | + fd1 = socket(PF_INET, SOCK_DGRAM, 0); | |
724 | + fd2 = socket(PF_INET, SOCK_DGRAM, 0); | |
725 | + addr1.sin_family = AF_INET; | |
726 | + addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
727 | + | |
728 | + policy = "100 acl inet_dgram_bind\n" | |
729 | + "0 allow ip=127.0.0.1 port!=0\n" | |
730 | + "1 deny\n"; | |
731 | + set(policy); | |
732 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
733 | + EOF); | |
734 | + unset(policy); | |
735 | + | |
736 | + policy = "100 acl inet_dgram_bind\n" | |
737 | + "0 allow ip!=127.0.0.1 port=0\n" | |
738 | + "1 deny\n"; | |
739 | + set(policy); | |
740 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
741 | + EOF); | |
742 | + unset(policy); | |
743 | + | |
744 | + policy = "100 acl inet_dgram_bind\n" | |
745 | + "0 allow ip=127.0.0.1 port=0 path.uid=task.uid\n" | |
746 | + "1 deny\n"; | |
747 | + set(policy); | |
748 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
749 | + EOF); | |
750 | + unset(policy); | |
751 | + | |
752 | + policy = "100 acl inet_dgram_bind\n" | |
753 | + "0 allow ip=127.0.0.1 port=0\n" | |
754 | + "1 deny\n"; | |
755 | + set(policy); | |
756 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
757 | + 0); | |
758 | + unset(policy); | |
759 | + | |
760 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
761 | + | |
762 | + snprintf(buffer, sizeof(buffer) - 1, | |
763 | + "100 acl inet_dgram_send\n" | |
764 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
765 | + "1 deny\n", ntohs(addr1.sin_port)); | |
766 | + policy = buffer; | |
767 | + set(policy); | |
768 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
769 | + == EOF); | |
770 | + unset(policy); | |
771 | + | |
772 | + snprintf(buffer, sizeof(buffer) - 1, | |
773 | + "100 acl inet_dgram_send\n" | |
774 | + "0 allow ip=127.0.0.1 port=%u\n" | |
775 | + "1 deny\n", ntohs(addr1.sin_port)); | |
776 | + policy = buffer; | |
777 | + set(policy); | |
778 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
779 | + == 0); | |
780 | + unset(policy); | |
781 | + | |
782 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
783 | + | |
784 | + snprintf(buffer, sizeof(buffer) - 1, | |
785 | + "100 acl inet_dgram_send\n" | |
786 | + "0 allow ip=127.0.0.1 port=%u\n" | |
787 | + "1 deny\n", ntohs(addr1.sin_port)); | |
788 | + policy = buffer; | |
789 | + set(policy); | |
790 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
791 | + unset(policy); | |
792 | + | |
793 | + snprintf(buffer, sizeof(buffer) - 1, | |
794 | + "100 acl inet_dgram_send\n" | |
795 | + "0 allow ip=127.0.0.1 port=%u\n" | |
796 | + "1 deny\n", ntohs(addr1.sin_port)); | |
797 | + policy = buffer; | |
798 | + set(policy); | |
799 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
800 | + unset(policy); | |
801 | + | |
802 | + snprintf(buffer, sizeof(buffer) - 1, | |
803 | + "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
804 | + "100 acl inet_dgram_send\n" | |
805 | + "0 allow ip=@LOCALHOST port=%u\n" | |
806 | + "1 deny\n", ntohs(addr1.sin_port)); | |
807 | + policy = buffer; | |
808 | + set(policy); | |
809 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
810 | + unset2(policy); | |
811 | + | |
812 | + close(fd1); | |
813 | + close(fd2); | |
814 | +} | |
815 | + | |
816 | +static void test_network_inet_raw(void) | |
817 | +{ | |
818 | + struct sockaddr_in addr = { }; | |
819 | + static struct iphdr ip = { }; | |
820 | + int fd1; | |
821 | + int fd2; | |
822 | + char *policy; | |
823 | + fd1 = socket(PF_INET, SOCK_RAW, 1); | |
824 | + fd2 = socket(PF_INET, SOCK_RAW, 1); | |
825 | + addr.sin_family = AF_INET; | |
826 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
827 | + ip.version = 4; | |
828 | + ip.ihl = sizeof(struct iphdr) / 4; | |
829 | + ip.protocol = IPPROTO_RAW; | |
830 | + ip.daddr = htonl(INADDR_LOOPBACK); | |
831 | + ip.saddr = ip.daddr; | |
832 | + | |
833 | + policy = "100 acl inet_raw_bind\n" | |
834 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
835 | + "1 deny\n"; | |
836 | + set(policy); | |
837 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
838 | + EOF); | |
839 | + unset(policy); | |
840 | + | |
841 | + policy = "100 acl inet_raw_bind\n" | |
842 | + "0 allow ip!=127.0.0.1 proto=1\n" | |
843 | + "1 deny\n"; | |
844 | + set(policy); | |
845 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
846 | + EOF); | |
847 | + unset(policy); | |
848 | + | |
849 | + policy = "100 acl inet_raw_bind\n" | |
850 | + "0 allow ip=127.0.0.1 proto=1 path.uid=task.uid\n" | |
851 | + "1 deny\n"; | |
852 | + set(policy); | |
853 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
854 | + EOF); | |
855 | + unset(policy); | |
856 | + | |
857 | + policy = "100 acl inet_raw_bind\n" | |
858 | + "0 allow ip=127.0.0.1 proto=1\n" | |
859 | + "1 deny\n"; | |
860 | + set(policy); | |
861 | + check(policy, bind(fd2, (struct sockaddr *) &addr, sizeof(addr)) == | |
862 | + 0); | |
863 | + unset(policy); | |
864 | + | |
865 | + policy = "100 acl inet_raw_send\n" | |
866 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
867 | + "1 deny\n"; | |
868 | + set(policy); | |
869 | + check(policy, connect(fd2, (struct sockaddr *) &addr, sizeof(addr)) | |
870 | + == EOF); | |
871 | + unset(policy); | |
872 | + | |
873 | + policy = "100 acl inet_raw_send\n" | |
874 | + "0 allow ip=127.0.0.1 proto=1\n" | |
875 | + "1 deny\n"; | |
876 | + set(policy); | |
877 | + check(policy, connect(fd2, (struct sockaddr *) &addr, sizeof(addr)) | |
878 | + == 0); | |
879 | + unset(policy); | |
880 | + | |
881 | + policy = "100 acl inet_raw_send\n" | |
882 | + "0 allow ip=127.0.0.1 proto=1\n" | |
883 | + "1 deny\n"; | |
884 | + set(policy); | |
885 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
886 | + unset(policy); | |
887 | + | |
888 | + policy = "100 acl inet_raw_send\n" | |
889 | + "0 allow ip=127.0.0.1 proto=1\n" | |
890 | + "1 deny\n"; | |
891 | + set(policy); | |
892 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
893 | + unset(policy); | |
894 | + | |
895 | + policy = "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
896 | + "100 acl inet_raw_send\n" | |
897 | + "0 allow ip=@LOCALHOST proto=1\n" | |
898 | + "1 deny\n"; | |
899 | + set(policy); | |
900 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
901 | + unset2(policy); | |
902 | + | |
903 | + close(fd1); | |
904 | + close(fd2); | |
905 | +} | |
906 | + | |
907 | +static void test_network_inet6_stream(void) | |
908 | +{ | |
909 | + struct sockaddr_in6 addr1 = { }; | |
910 | + struct sockaddr_in6 addr2 = { }; | |
911 | + socklen_t size = sizeof(addr1); | |
912 | + int fd1; | |
913 | + int fd2; | |
914 | + int fd3; | |
915 | + char *policy; | |
916 | + char buffer[1024]; | |
917 | + memset(buffer, 0, sizeof(buffer)); | |
918 | + | |
919 | + fd1 = socket(PF_INET6, SOCK_STREAM, 0); | |
920 | + fd2 = socket(PF_INET6, SOCK_STREAM, 0); | |
921 | + addr1.sin6_family = AF_INET6; | |
922 | + addr1.sin6_addr = in6addr_loopback; | |
923 | + | |
924 | + policy = "100 acl inet_stream_bind\n" | |
925 | + "0 allow ip=::1 port!=0\n" | |
926 | + "1 deny\n"; | |
927 | + set(policy); | |
928 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
929 | + EOF); | |
930 | + unset(policy); | |
931 | + | |
932 | + policy = "100 acl inet_stream_bind\n" | |
933 | + "0 allow ip!=::1 port=0\n" | |
934 | + "1 deny\n"; | |
935 | + set(policy); | |
936 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
937 | + EOF); | |
938 | + unset(policy); | |
939 | + | |
940 | + policy = "100 acl inet_stream_bind\n" | |
941 | + "0 allow ip=::1 port=0 path.uid=task.uid\n" | |
942 | + "1 deny\n"; | |
943 | + set(policy); | |
944 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
945 | + EOF); | |
946 | + unset(policy); | |
947 | + | |
948 | + policy = "100 acl inet_stream_bind\n" | |
949 | + "0 allow ip=::1 port=0\n" | |
950 | + "1 deny\n"; | |
951 | + set(policy); | |
952 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
953 | + 0); | |
954 | + unset(policy); | |
955 | + | |
956 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
957 | + | |
958 | + snprintf(buffer, sizeof(buffer) - 1, | |
959 | + "100 acl inet_stream_listen\n" | |
960 | + "0 allow ip=::1 port!=%u\n" | |
961 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
962 | + policy = buffer; | |
963 | + set(policy); | |
964 | + check(policy, listen(fd1, 5) == EOF); | |
965 | + unset(policy); | |
966 | + | |
967 | + snprintf(buffer, sizeof(buffer) - 1, | |
968 | + "100 acl inet_stream_listen\n" | |
969 | + "0 allow ip=::1 port=%u\n" | |
970 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
971 | + policy = buffer; | |
972 | + set(policy); | |
973 | + check(policy, listen(fd1, 5) == 0); | |
974 | + unset(policy); | |
975 | + | |
976 | + snprintf(buffer, sizeof(buffer) - 1, | |
977 | + "100 acl inet_stream_connect\n" | |
978 | + "0 allow ip=::1 port!=%u\n" | |
979 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
980 | + policy = buffer; | |
981 | + set(policy); | |
982 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
983 | + == EOF); | |
984 | + unset(policy); | |
985 | + | |
986 | + snprintf(buffer, sizeof(buffer) - 1, | |
987 | + "100 acl inet_stream_connect\n" | |
988 | + "0 allow ip=::1 port=%u\n" | |
989 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
990 | + policy = buffer; | |
991 | + set(policy); | |
992 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
993 | + == 0); | |
994 | + unset(policy); | |
995 | + | |
996 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
997 | + | |
998 | + snprintf(buffer, sizeof(buffer) - 1, | |
999 | + "100 acl inet_stream_accept\n" | |
1000 | + "0 allow ip=::1 port=%u\n" | |
1001 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1002 | + policy = buffer; | |
1003 | + set(policy); | |
1004 | + fd3 = accept(fd1, NULL, 0); | |
1005 | + check(policy, write(fd3, "", 1) == 1); | |
1006 | + close(fd3); | |
1007 | + unset(policy); | |
1008 | + | |
1009 | + snprintf(buffer, sizeof(buffer) - 1, | |
1010 | + "100 acl inet_stream_connect\n" | |
1011 | + "0 allow ip=::1 port=%u\n" | |
1012 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1013 | + policy = buffer; | |
1014 | + set(policy); | |
1015 | + close(fd2); | |
1016 | + fd2 = socket(PF_INET6, SOCK_STREAM, 0); | |
1017 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1018 | + == 0); | |
1019 | + unset(policy); | |
1020 | + | |
1021 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1022 | + | |
1023 | + snprintf(buffer, sizeof(buffer) - 1, | |
1024 | + "100 acl inet_stream_accept\n" | |
1025 | + "0 allow ip=::1 port!=%u\n" | |
1026 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1027 | + policy = buffer; | |
1028 | + set(policy); | |
1029 | + fd3 = accept(fd1, NULL, 0); | |
1030 | + check(policy, write(fd3, "", 1) == EOF); | |
1031 | + close(fd3); | |
1032 | + unset(policy); | |
1033 | + | |
1034 | + close(fd1); | |
1035 | + close(fd2); | |
1036 | +} | |
1037 | + | |
1038 | +static void test_network_inet6_dgram(void) | |
1039 | +{ | |
1040 | + struct sockaddr_in6 addr1 = { }; | |
1041 | + struct sockaddr_in6 addr2 = { }; | |
1042 | + socklen_t size = sizeof(addr1); | |
1043 | + int fd1; | |
1044 | + int fd2; | |
1045 | + char *policy; | |
1046 | + char buffer[1024]; | |
1047 | + memset(buffer, 0, sizeof(buffer)); | |
1048 | + | |
1049 | + fd1 = socket(PF_INET6, SOCK_DGRAM, 0); | |
1050 | + fd2 = socket(PF_INET6, SOCK_DGRAM, 0); | |
1051 | + addr1.sin6_family = AF_INET6; | |
1052 | + addr1.sin6_addr = in6addr_loopback; | |
1053 | + | |
1054 | + policy = "100 acl inet_dgram_bind\n" | |
1055 | + "0 allow ip=::1 port!=0\n" | |
1056 | + "1 deny\n"; | |
1057 | + set(policy); | |
1058 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1059 | + EOF); | |
1060 | + unset(policy); | |
1061 | + | |
1062 | + policy = "100 acl inet_dgram_bind\n" | |
1063 | + "0 allow ip!=::1 port=0\n" | |
1064 | + "1 deny\n"; | |
1065 | + set(policy); | |
1066 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1067 | + EOF); | |
1068 | + unset(policy); | |
1069 | + | |
1070 | + policy = "100 acl inet_dgram_bind\n" | |
1071 | + "0 allow ip=::1 port=0 path.uid=task.uid\n" | |
1072 | + "1 deny\n"; | |
1073 | + set(policy); | |
1074 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1075 | + EOF); | |
1076 | + unset(policy); | |
1077 | + | |
1078 | + policy = "100 acl inet_dgram_bind\n" | |
1079 | + "0 allow ip=::1 port=0\n" | |
1080 | + "1 deny\n"; | |
1081 | + set(policy); | |
1082 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1083 | + 0); | |
1084 | + unset(policy); | |
1085 | + | |
1086 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
1087 | + | |
1088 | + snprintf(buffer, sizeof(buffer) - 1, | |
1089 | + "100 acl inet_dgram_send\n" | |
1090 | + "0 allow ip=::1 port!=%u\n" | |
1091 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1092 | + policy = buffer; | |
1093 | + set(policy); | |
1094 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1095 | + == EOF); | |
1096 | + unset(policy); | |
1097 | + | |
1098 | + snprintf(buffer, sizeof(buffer) - 1, | |
1099 | + "100 acl inet_dgram_send\n" | |
1100 | + "0 allow ip=::1 port=%u\n" | |
1101 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1102 | + policy = buffer; | |
1103 | + set(policy); | |
1104 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1105 | + == 0); | |
1106 | + unset(policy); | |
1107 | + | |
1108 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1109 | + | |
1110 | + snprintf(buffer, sizeof(buffer) - 1, | |
1111 | + "100 acl inet_dgram_send\n" | |
1112 | + "0 allow ip=::1 port=%u\n" | |
1113 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1114 | + policy = buffer; | |
1115 | + set(policy); | |
1116 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1117 | + unset(policy); | |
1118 | + | |
1119 | + snprintf(buffer, sizeof(buffer) - 1, | |
1120 | + "100 acl inet_dgram_send\n" | |
1121 | + "0 allow ip=::1 port=%u\n" | |
1122 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1123 | + policy = buffer; | |
1124 | + set(policy); | |
1125 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1126 | + unset(policy); | |
1127 | + | |
1128 | + snprintf(buffer, sizeof(buffer) - 1, | |
1129 | + "ip_group LOCALHOST ::-::ffff\n" | |
1130 | + "100 acl inet_dgram_send\n" | |
1131 | + "0 allow ip=@LOCALHOST port=%u\n" | |
1132 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1133 | + policy = buffer; | |
1134 | + set(policy); | |
1135 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1136 | + unset2(policy); | |
1137 | + | |
1138 | + close(fd1); | |
1139 | + close(fd2); | |
1140 | +} | |
1141 | + | |
1142 | +static int fork_exec(char *envp[]) | |
1143 | +{ | |
1144 | + int ret_ignored; | |
1145 | + int pipe_fd[2] = { EOF, EOF }; | |
1146 | + int err = 0; | |
1147 | + pid_t pid; | |
1148 | + if (pipe(pipe_fd)) { | |
1149 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
1150 | + exit(1); | |
1151 | + } | |
1152 | + pid = fork(); | |
1153 | + if (pid == 0) { | |
1154 | + char *argv[2] = { BINDIR "/true", NULL }; | |
1155 | + execve(BINDIR "/true", argv, envp); | |
1156 | + err = errno; | |
1157 | + ret_ignored = write(pipe_fd[1], &err, sizeof(err)); | |
1158 | + _exit(0); | |
1159 | + } | |
1160 | + close(pipe_fd[1]); | |
1161 | + ret_ignored = read(pipe_fd[0], &err, sizeof(err)); | |
1162 | + close(pipe_fd[0]); | |
1163 | + wait(NULL); | |
1164 | + errno = err; | |
1165 | + return err ? EOF : 0; | |
1166 | +} | |
1167 | + | |
1168 | +static void test_environ(void) | |
1169 | +{ | |
1170 | + char *policy; | |
1171 | + char *envp[2]; | |
1172 | + envp[1] = NULL; | |
1173 | + | |
1174 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1175 | + "0 allow value=\"/\"\n" | |
1176 | + "1 deny\n"; | |
1177 | + set(policy); | |
1178 | + envp[0] = "PATH2=/"; | |
1179 | + check(policy, fork_exec(envp) == 0); | |
1180 | + unset(policy); | |
1181 | + | |
1182 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1183 | + "0 allow value!=\"/\"\n" | |
1184 | + "1 deny\n"; | |
1185 | + set(policy); | |
1186 | + envp[0] = "PATH2=/"; | |
1187 | + check(policy, fork_exec(envp) == EOF); | |
1188 | + unset(policy); | |
1189 | + | |
1190 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1191 | + "0 deny value!=\"/\"\n" | |
1192 | + "1 allow\n"; | |
1193 | + set(policy); | |
1194 | + envp[0] = "PATH2=/"; | |
1195 | + check(policy, fork_exec(envp) == 0); | |
1196 | + unset(policy); | |
1197 | + | |
1198 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1199 | + "0 deny value=\"/\"\n" | |
1200 | + "1 allow\n"; | |
1201 | + set(policy); | |
1202 | + envp[0] = "PATH2=/"; | |
1203 | + check(policy, fork_exec(envp) == EOF); | |
1204 | + unset(policy); | |
1205 | + | |
1206 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1207 | + "0 allow envp[\"PATH2\"]=\"/\"\n" | |
1208 | + "1 deny\n"; | |
1209 | + set(policy); | |
1210 | + envp[0] = "PATH2=/"; | |
1211 | + check(policy, fork_exec(envp) == 0); | |
1212 | + unset(policy); | |
1213 | + | |
1214 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1215 | + "0 allow envp[\"PATH2\"]!=\"/\"\n" | |
1216 | + "1 deny\n"; | |
1217 | + set(policy); | |
1218 | + envp[0] = "PATH2=/"; | |
1219 | + check(policy, fork_exec(envp) == EOF); | |
1220 | + unset(policy); | |
1221 | + | |
1222 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1223 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1224 | + "1 deny\n"; | |
1225 | + set(policy); | |
1226 | + envp[0] = "PATH2"; | |
1227 | + check(policy, fork_exec(envp) == 0); | |
1228 | + unset(policy); | |
1229 | + | |
1230 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1231 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1232 | + "1 deny\n"; | |
1233 | + set(policy); | |
1234 | + envp[0] = "PATH2="; | |
1235 | + check(policy, fork_exec(envp) == 0); | |
1236 | + unset(policy); | |
1237 | + | |
1238 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1239 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1240 | + "1 deny\n"; | |
1241 | + set(policy); | |
1242 | + envp[0] = "PATH2=/"; | |
1243 | + check(policy, fork_exec(envp) == 0); | |
1244 | + unset(policy); | |
1245 | + | |
1246 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1247 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1248 | + "1 deny\n"; | |
1249 | + set(policy); | |
1250 | + envp[0] = "PATH2"; | |
1251 | + check(policy, fork_exec(envp) == EOF); | |
1252 | + unset(policy); | |
1253 | + | |
1254 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1255 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1256 | + "1 deny\n"; | |
1257 | + set(policy); | |
1258 | + envp[0] = "PATH2="; | |
1259 | + check(policy, fork_exec(envp) == EOF); | |
1260 | + unset(policy); | |
1261 | + | |
1262 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1263 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1264 | + "1 deny\n"; | |
1265 | + set(policy); | |
1266 | + envp[0] = "PATH2=/"; | |
1267 | + check(policy, fork_exec(envp) == EOF); | |
1268 | + unset(policy); | |
1269 | + | |
1270 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1271 | + "0 allow envp[\"\"]=NULL\n" | |
1272 | + "1 deny\n"; | |
1273 | + set(policy); | |
1274 | + envp[0] = ""; | |
1275 | + check(policy, fork_exec(envp) == EOF); | |
1276 | + unset(policy); | |
1277 | + | |
1278 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1279 | + "0 allow envp[\"\"]!=NULL\n" | |
1280 | + "1 deny\n"; | |
1281 | + set(policy); | |
1282 | + envp[0] = ""; | |
1283 | + check(policy, fork_exec(envp) == 0); | |
1284 | + unset(policy); | |
1285 | + | |
1286 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1287 | + "0 allow envp[\"\"]!=NULL\n" | |
1288 | + "1 deny\n"; | |
1289 | + set(policy); | |
1290 | + envp[0] = "="; | |
1291 | + check(policy, fork_exec(envp) == 0); | |
1292 | + unset(policy); | |
1293 | + | |
1294 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1295 | + "0 allow envp[\"\"]!=NULL\n" | |
1296 | + "1 deny\n"; | |
1297 | + set(policy); | |
1298 | + envp[0] = "=/"; | |
1299 | + check(policy, fork_exec(envp) == 0); | |
1300 | + unset(policy); | |
1301 | +} | |
1302 | + | |
1303 | +static int fork_exec2(char *argv[], char *envp[]) | |
1304 | +{ | |
1305 | + int ret_ignored; | |
1306 | + int pipe_fd[2] = { EOF, EOF }; | |
1307 | + int err = 0; | |
1308 | + pid_t pid; | |
1309 | + if (pipe(pipe_fd)) { | |
1310 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
1311 | + exit(1); | |
1312 | + } | |
1313 | + pid = fork(); | |
1314 | + if (pid == 0) { | |
1315 | + execve(BINDIR "/true", argv, envp); | |
1316 | + err = errno; | |
1317 | + ret_ignored = write(pipe_fd[1], &err, sizeof(err)); | |
1318 | + _exit(0); | |
1319 | + } | |
1320 | + close(pipe_fd[1]); | |
1321 | + ret_ignored = read(pipe_fd[0], &err, sizeof(err)); | |
1322 | + close(pipe_fd[0]); | |
1323 | + wait(NULL); | |
1324 | + errno = err; | |
1325 | + return err ? EOF : 0; | |
1326 | +} | |
1327 | + | |
1328 | +static void test_file_execute(void) | |
1329 | +{ | |
1330 | + char *policy; | |
1331 | + char *argv[5]; | |
1332 | + char *envp[5]; | |
1333 | + memset(argv, 0, sizeof(argv)); | |
1334 | + memset(envp, 0, sizeof(envp)); | |
1335 | + | |
1336 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1337 | + "0 allow argc=1\n" | |
1338 | + "1 deny\n"; | |
1339 | + set(policy); | |
1340 | + argv[0]="true"; | |
1341 | + check(policy, fork_exec2(argv, envp) == 0); | |
1342 | + unset(policy); | |
1343 | + | |
1344 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1345 | + "0 allow argc!=1\n" | |
1346 | + "1 deny\n"; | |
1347 | + set(policy); | |
1348 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1349 | + unset(policy); | |
1350 | + | |
1351 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1352 | + "0 deny argc!=1\n" | |
1353 | + "1 allow\n"; | |
1354 | + set(policy); | |
1355 | + check(policy, fork_exec2(argv, envp) == 0); | |
1356 | + unset(policy); | |
1357 | + | |
1358 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1359 | + "0 deny argc=1\n" | |
1360 | + "1 allow\n"; | |
1361 | + set(policy); | |
1362 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1363 | + unset(policy); | |
1364 | + | |
1365 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1366 | + "0 deny argv[0]!=\"true\"\n" | |
1367 | + "1 allow\n"; | |
1368 | + set(policy); | |
1369 | + check(policy, fork_exec2(argv, envp) == 0); | |
1370 | + unset(policy); | |
1371 | + | |
1372 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1373 | + "0 deny argv[0]=\"true\"\n" | |
1374 | + "1 allow\n"; | |
1375 | + set(policy); | |
1376 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1377 | + unset(policy); | |
1378 | + | |
1379 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1380 | + "0 allow argv[0]!=\"true\"\n" | |
1381 | + "1 deny\n"; | |
1382 | + set(policy); | |
1383 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1384 | + unset(policy); | |
1385 | + | |
1386 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1387 | + "0 allow argv[0]=\"true\"\n" | |
1388 | + "1 deny\n"; | |
1389 | + set(policy); | |
1390 | + check(policy, fork_exec2(argv, envp) == 0); | |
1391 | + unset(policy); | |
1392 | + | |
1393 | + policy = "string_group EXEC_ARGV0 false\n" | |
1394 | + "string_group EXEC_ARGV0 true\n" | |
1395 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1396 | + "0 deny argv[0]!=@EXEC_ARGV0\n" | |
1397 | + "1 allow\n"; | |
1398 | + set(policy); | |
1399 | + check(policy, fork_exec2(argv, envp) == 0); | |
1400 | + unset2(policy); | |
1401 | + | |
1402 | + policy = "string_group EXEC_ARGV0 false\n" | |
1403 | + "string_group EXEC_ARGV0 true\n" | |
1404 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1405 | + "0 deny argv[0]=@EXEC_ARGV0\n" | |
1406 | + "1 allow\n"; | |
1407 | + set(policy); | |
1408 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1409 | + unset2(policy); | |
1410 | + | |
1411 | + policy = "string_group EXEC_ARGV0 false\n" | |
1412 | + "string_group EXEC_ARGV0 true\n" | |
1413 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1414 | + "0 allow argv[0]!=@EXEC_ARGV0\n" | |
1415 | + "1 deny\n"; | |
1416 | + set(policy); | |
1417 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1418 | + unset2(policy); | |
1419 | + | |
1420 | + policy = "string_group EXEC_ARGV0 false\n" | |
1421 | + "string_group EXEC_ARGV0 true\n" | |
1422 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1423 | + "0 allow argv[0]=@EXEC_ARGV0\n" | |
1424 | + "1 deny\n"; | |
1425 | + set(policy); | |
1426 | + check(policy, fork_exec2(argv, envp) == 0); | |
1427 | + unset2(policy); | |
1428 | + | |
1429 | + | |
1430 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1431 | + "0 allow envc=1\n" | |
1432 | + "1 deny\n"; | |
1433 | + set(policy); | |
1434 | + envp[0]="PATH=/"; | |
1435 | + check(policy, fork_exec2(argv, envp) == 0); | |
1436 | + unset(policy); | |
1437 | + | |
1438 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1439 | + "0 allow envc!=1\n" | |
1440 | + "1 deny\n"; | |
1441 | + set(policy); | |
1442 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1443 | + unset(policy); | |
1444 | + | |
1445 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1446 | + "0 deny envc!=1\n" | |
1447 | + "1 allow\n"; | |
1448 | + set(policy); | |
1449 | + check(policy, fork_exec2(argv, envp) == 0); | |
1450 | + unset(policy); | |
1451 | + | |
1452 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1453 | + "0 deny envc=1\n" | |
1454 | + "1 allow\n"; | |
1455 | + set(policy); | |
1456 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1457 | + unset(policy); | |
1458 | + | |
1459 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1460 | + "0 deny envp[\"PATH\"]!=\"/\"\n" | |
1461 | + "1 allow\n"; | |
1462 | + set(policy); | |
1463 | + check(policy, fork_exec2(argv, envp) == 0); | |
1464 | + unset(policy); | |
1465 | + | |
1466 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1467 | + "0 deny envp[\"PATH\"]=\"/\"\n" | |
1468 | + "1 allow\n"; | |
1469 | + set(policy); | |
1470 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1471 | + unset(policy); | |
1472 | + | |
1473 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1474 | + "0 allow envp[\"PATH\"]!=\"/\"\n" | |
1475 | + "1 deny\n"; | |
1476 | + set(policy); | |
1477 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1478 | + unset(policy); | |
1479 | + | |
1480 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1481 | + "0 allow envp[\"PATH\"]=\"/\"\n" | |
1482 | + "1 deny\n"; | |
1483 | + set(policy); | |
1484 | + check(policy, fork_exec2(argv, envp) == 0); | |
1485 | + unset(policy); | |
1486 | + | |
1487 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1488 | + "string_group PATH_VALUES /\n" | |
1489 | + "string_group PATH_VALUES /sbin\n" | |
1490 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1491 | + "0 deny envp[\"PATH\"]!=@PATH_VALUES\n" | |
1492 | + "1 allow\n"; | |
1493 | + set(policy); | |
1494 | + check(policy, fork_exec2(argv, envp) == 0); | |
1495 | + unset2(policy); | |
1496 | + | |
1497 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1498 | + "string_group PATH_VALUES /\n" | |
1499 | + "string_group PATH_VALUES /sbin\n" | |
1500 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1501 | + "0 deny envp[\"PATH\"]=@PATH_VALUES\n" | |
1502 | + "1 allow\n"; | |
1503 | + set(policy); | |
1504 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1505 | + unset2(policy); | |
1506 | + | |
1507 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1508 | + "string_group PATH_VALUES /\n" | |
1509 | + "string_group PATH_VALUES /sbin\n" | |
1510 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1511 | + "0 allow envp[\"PATH\"]!=@PATH_VALUES\n" | |
1512 | + "1 deny\n"; | |
1513 | + set(policy); | |
1514 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1515 | + unset2(policy); | |
1516 | + | |
1517 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1518 | + "string_group PATH_VALUES /\n" | |
1519 | + "string_group PATH_VALUES /sbin\n" | |
1520 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1521 | + "0 allow envp[\"PATH\"]=@PATH_VALUES\n" | |
1522 | + "1 deny\n"; | |
1523 | + set(policy); | |
1524 | + check(policy, fork_exec2(argv, envp) == 0); | |
1525 | + unset2(policy); | |
1526 | + | |
1527 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1528 | + "0 deny envp[\"PATH\"]!=NULL\n" | |
1529 | + "1 allow\n"; | |
1530 | + set(policy); | |
1531 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1532 | + unset(policy); | |
1533 | + | |
1534 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1535 | + "0 deny envp[\"PATH\"]=NULL\n" | |
1536 | + "1 allow\n"; | |
1537 | + set(policy); | |
1538 | + check(policy, fork_exec2(argv, envp) == 0); | |
1539 | + unset(policy); | |
1540 | + | |
1541 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1542 | + "0 allow envp[\"PATH\"]!=NULL\n" | |
1543 | + "1 deny\n"; | |
1544 | + set(policy); | |
1545 | + check(policy, fork_exec2(argv, envp) == 0); | |
1546 | + unset(policy); | |
1547 | + | |
1548 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1549 | + "0 allow envp[\"PATH\"]=NULL\n" | |
1550 | + "1 deny\n"; | |
1551 | + set(policy); | |
1552 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1553 | + unset(policy); | |
1554 | +} | |
1555 | + | |
1556 | +static void test_file_misc(void) | |
1557 | +{ | |
1558 | + int fd; | |
1559 | + const pid_t pid = getpid(); | |
1560 | + char buffer[1024]; | |
1561 | + memset(buffer, 0, sizeof(buffer)); | |
1562 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1563 | + "10 allow path!=NULL\n" | |
1564 | + "20 deny\n", pid); | |
1565 | + set(buffer); | |
1566 | + fd = open("/dev/null", O_RDONLY); | |
1567 | + check(buffer, fd != EOF); | |
1568 | + close(fd); | |
1569 | + unset(buffer); | |
1570 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1571 | + "10 allow path=NULL\n" | |
1572 | + "20 deny\n", pid); | |
1573 | + set(buffer); | |
1574 | + fd = open("/dev/null", O_RDONLY); | |
1575 | + check(buffer, fd == EOF); | |
1576 | + close(fd); | |
1577 | + unset(buffer); | |
1578 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1579 | + "10 deny path=NULL\n" | |
1580 | + "20 allow\n", pid); | |
1581 | + set(buffer); | |
1582 | + fd = open("/dev/null", O_RDONLY); | |
1583 | + check(buffer, fd != EOF); | |
1584 | + close(fd); | |
1585 | + unset(buffer); | |
1586 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1587 | + "10 deny path!=NULL\n" | |
1588 | + "20 allow\n", pid); | |
1589 | + set(buffer); | |
1590 | + fd = open("/dev/null", O_RDONLY); | |
1591 | + check(buffer, fd == EOF); | |
1592 | + close(fd); | |
1593 | + unset(buffer); | |
1594 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1595 | + "10 allow path=path\n" | |
1596 | + "20 deny\n", pid); | |
1597 | + set(buffer); | |
1598 | + fd = open("/dev/null", O_RDONLY); | |
1599 | + check(buffer, fd != EOF); | |
1600 | + close(fd); | |
1601 | + unset(buffer); | |
1602 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1603 | + "10 allow path!=path\n" | |
1604 | + "20 deny\n", pid); | |
1605 | + set(buffer); | |
1606 | + fd = open("/dev/null", O_RDONLY); | |
1607 | + check(buffer, fd == EOF); | |
1608 | + close(fd); | |
1609 | + unset(buffer); | |
1610 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1611 | + "10 deny path!=path\n" | |
1612 | + "20 allow\n", pid); | |
1613 | + set(buffer); | |
1614 | + fd = open("/dev/null", O_RDONLY); | |
1615 | + check(buffer, fd != EOF); | |
1616 | + close(fd); | |
1617 | + unset(buffer); | |
1618 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1619 | + "10 deny path=path\n" | |
1620 | + "20 allow\n", pid); | |
1621 | + set(buffer); | |
1622 | + fd = open("/dev/null", O_RDONLY); | |
1623 | + check(buffer, fd == EOF); | |
1624 | + close(fd); | |
1625 | + unset(buffer); | |
1626 | + snprintf(buffer, sizeof(buffer) - 1, | |
1627 | + "string_group STRING_GROUP1 /dev/null\n" | |
1628 | + "100 acl read task.pid=%u\n" | |
1629 | + "10 allow path=@STRING_GROUP1\n" | |
1630 | + "20 deny\n", pid); | |
1631 | + set(buffer); | |
1632 | + fd = open("/dev/null", O_RDONLY); | |
1633 | + check(buffer, fd != EOF); | |
1634 | + close(fd); | |
1635 | + unset2(buffer); | |
1636 | + snprintf(buffer, sizeof(buffer) - 1, | |
1637 | + "string_group STRING_GROUP1 /dev/null\n" | |
1638 | + "100 acl read task.pid=%u\n" | |
1639 | + "10 allow path!=@STRING_GROUP1\n" | |
1640 | + "20 deny\n", pid); | |
1641 | + set(buffer); | |
1642 | + fd = open("/dev/null", O_RDONLY); | |
1643 | + check(buffer, fd == EOF); | |
1644 | + close(fd); | |
1645 | + unset2(buffer); | |
1646 | + snprintf(buffer, sizeof(buffer) - 1, | |
1647 | + "string_group STRING_GROUP1 /dev/null\n" | |
1648 | + "100 acl read task.pid=%u\n" | |
1649 | + "10 deny path!=@STRING_GROUP1\n" | |
1650 | + "20 allow\n", pid); | |
1651 | + set(buffer); | |
1652 | + fd = open("/dev/null", O_RDONLY); | |
1653 | + check(buffer, fd != EOF); | |
1654 | + close(fd); | |
1655 | + unset2(buffer); | |
1656 | + snprintf(buffer, sizeof(buffer) - 1, | |
1657 | + "string_group STRING_GROUP1 /dev/null\n" | |
1658 | + "100 acl read task.pid=%u\n" | |
1659 | + "10 deny path=@STRING_GROUP1\n" | |
1660 | + "20 allow\n", pid); | |
1661 | + set(buffer); | |
1662 | + fd = open("/dev/null", O_RDONLY); | |
1663 | + check(buffer, fd == EOF); | |
1664 | + close(fd); | |
1665 | + unset2(buffer); | |
1666 | + snprintf(buffer, sizeof(buffer) - 1, | |
1667 | + "number_group NUMBER_GROUP1 0666\n" | |
1668 | + "100 acl read task.pid=%u\n" | |
1669 | + "10 deny path.perm!=@NUMBER_GROUP1\n" | |
1670 | + "20 allow\n", pid); | |
1671 | + set(buffer); | |
1672 | + fd = open("/dev/null", O_RDONLY); | |
1673 | + check(buffer, fd != EOF); | |
1674 | + close(fd); | |
1675 | + unset2(buffer); | |
1676 | + snprintf(buffer, sizeof(buffer) - 1, | |
1677 | + "number_group NUMBER_GROUP1 0666\n" | |
1678 | + "100 acl read task.pid=%u\n" | |
1679 | + "10 deny path.perm=@NUMBER_GROUP1\n" | |
1680 | + "20 allow\n", pid); | |
1681 | + set(buffer); | |
1682 | + fd = open("/dev/null", O_RDONLY); | |
1683 | + check(buffer, fd == EOF); | |
1684 | + close(fd); | |
1685 | + unset2(buffer); | |
1686 | + snprintf(buffer, sizeof(buffer) - 1, | |
1687 | + "100 acl read task.pid=%u\n" | |
1688 | + "10 deny path.perm!=owner_read\n" | |
1689 | + "20 allow\n", pid); | |
1690 | + set(buffer); | |
1691 | + fd = open("/dev/null", O_RDONLY); | |
1692 | + check(buffer, fd != EOF); | |
1693 | + close(fd); | |
1694 | + unset2(buffer); | |
1695 | + snprintf(buffer, sizeof(buffer) - 1, | |
1696 | + "100 acl read task.pid=%u\n" | |
1697 | + "10 deny path.perm=owner_read\n" | |
1698 | + "20 allow\n", pid); | |
1699 | + set(buffer); | |
1700 | + fd = open("/dev/null", O_RDONLY); | |
1701 | + check(buffer, fd == EOF); | |
1702 | + close(fd); | |
1703 | + unset2(buffer); | |
1704 | + snprintf(buffer, sizeof(buffer) - 1, | |
1705 | + "100 acl read task.pid=%u\n" | |
1706 | + "10 deny path.perm!=group_write\n" | |
1707 | + "20 allow\n", pid); | |
1708 | + set(buffer); | |
1709 | + fd = open("/dev/null", O_RDONLY); | |
1710 | + check(buffer, fd != EOF); | |
1711 | + close(fd); | |
1712 | + unset2(buffer); | |
1713 | + snprintf(buffer, sizeof(buffer) - 1, | |
1714 | + "100 acl read task.pid=%u\n" | |
1715 | + "10 deny path.perm=group_write\n" | |
1716 | + "20 allow\n", pid); | |
1717 | + set(buffer); | |
1718 | + fd = open("/dev/null", O_RDONLY); | |
1719 | + check(buffer, fd == EOF); | |
1720 | + close(fd); | |
1721 | + unset2(buffer); | |
1722 | + snprintf(buffer, sizeof(buffer) - 1, | |
1723 | + "100 acl read task.pid=%u\n" | |
1724 | + "10 deny path.perm!=others_read\n" | |
1725 | + "20 allow\n", pid); | |
1726 | + set(buffer); | |
1727 | + fd = open("/dev/null", O_RDONLY); | |
1728 | + check(buffer, fd != EOF); | |
1729 | + close(fd); | |
1730 | + unset2(buffer); | |
1731 | + snprintf(buffer, sizeof(buffer) - 1, | |
1732 | + "100 acl read task.pid=%u\n" | |
1733 | + "10 deny path.perm=others_read\n" | |
1734 | + "20 allow\n", pid); | |
1735 | + set(buffer); | |
1736 | + fd = open("/dev/null", O_RDONLY); | |
1737 | + check(buffer, fd == EOF); | |
1738 | + close(fd); | |
1739 | + unset2(buffer); | |
1740 | + snprintf(buffer, sizeof(buffer) - 1, | |
1741 | + "100 acl read task.pid=%u\n" | |
1742 | + "10 deny path.perm=path.parent.perm\n" | |
1743 | + "20 allow\n", pid); | |
1744 | + set(buffer); | |
1745 | + fd = open("/dev/null", O_RDONLY); | |
1746 | + check(buffer, fd != EOF); | |
1747 | + close(fd); | |
1748 | + unset2(buffer); | |
1749 | + snprintf(buffer, sizeof(buffer) - 1, | |
1750 | + "100 acl read task.pid=%u\n" | |
1751 | + "10 deny path.perm!=path.parent.perm\n" | |
1752 | + "20 allow\n", pid); | |
1753 | + set(buffer); | |
1754 | + fd = open("/dev/null", O_RDONLY); | |
1755 | + check(buffer, fd == EOF); | |
1756 | + close(fd); | |
1757 | + unset2(buffer); | |
1758 | + snprintf(buffer, sizeof(buffer) - 1, | |
1759 | + "100 acl execute task.ppid=%u\n" | |
1760 | + "10 allow path=exec\n" | |
1761 | + "20 deny\n", pid); | |
1762 | + set(buffer); | |
1763 | + check(buffer, fork_exec(NULL) == 0); | |
1764 | + unset(buffer); | |
1765 | + snprintf(buffer, sizeof(buffer) - 1, | |
1766 | + "100 acl execute task.ppid=%u\n" | |
1767 | + "10 allow path!=exec\n" | |
1768 | + "20 deny\n", pid); | |
1769 | + set(buffer); | |
1770 | + check(buffer, fork_exec(NULL) == EOF); | |
1771 | + unset(buffer); | |
1772 | + snprintf(buffer, sizeof(buffer) - 1, | |
1773 | + "100 acl execute task.ppid=%u\n" | |
1774 | + "10 deny path=exec\n" | |
1775 | + "20 allow\n", pid); | |
1776 | + set(buffer); | |
1777 | + check(buffer, fork_exec(NULL) == EOF); | |
1778 | + unset(buffer); | |
1779 | + snprintf(buffer, sizeof(buffer) - 1, | |
1780 | + "100 acl execute task.ppid=%u\n" | |
1781 | + "10 deny path!=exec\n" | |
1782 | + "20 allow\n", pid); | |
1783 | + set(buffer); | |
1784 | + check(buffer, fork_exec(NULL) == 0); | |
1785 | + unset(buffer); | |
1786 | +} | |
1787 | + | |
1788 | +static void reset_policy(void) | |
1789 | +{ | |
1790 | + FILE *fp2 = fopen(POLDIR "/policy", "r"); | |
1791 | + FILE *fp1 = fopen(POLDIR "/policy", "w"); | |
1792 | + if (!fp1 || !fp2) { | |
1793 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
1794 | + exit(1); | |
1795 | + } | |
1796 | + while (1) { | |
1797 | + const int c = fgetc(fp2); | |
1798 | + if (c == EOF) | |
1799 | + break; | |
1800 | + fputc(c, fp1); | |
1801 | + if (c == '\n') | |
1802 | + fprintf(fp1, "delete "); | |
1803 | + } | |
1804 | + fclose(fp2); | |
1805 | + fclose(fp1); | |
1806 | + | |
1807 | + /* Do not leave the init process in stopped state. */ | |
1808 | + kill(1, SIGCONT); | |
1809 | + | |
1810 | + /* Undo mount("/", MS_REC|MS_SHARED) made by systemd. */ | |
1811 | + mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL); | |
1812 | +} | |
1813 | + | |
1814 | +int main(int argc, char *argv[]) | |
1815 | +{ | |
1816 | + reset_policy(); | |
1817 | + | |
1818 | + fp = fopen(POLDIR "/policy", "w"); | |
1819 | + if (!fp) { | |
1820 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
1821 | + return 1; | |
1822 | + } | |
1823 | + fprintf(fp, "quota audit[0]" | |
1824 | + " allowed=1024 unmatched=1024 denied=1024\n"); | |
1825 | + fflush(fp); | |
1826 | + | |
1827 | + test_task_transition(); | |
1828 | + test_file_read(); | |
1829 | + test_file_write(); | |
1830 | + test_file_create(); | |
1831 | + test_file_unlink(); | |
1832 | + test_file_link(); | |
1833 | + test_file_rename(); | |
1834 | + test_network_inet_stream(); | |
1835 | + test_network_inet_dgram(); | |
1836 | + test_network_inet_raw(); | |
1837 | + test_network_inet6_stream(); | |
1838 | + test_network_inet6_dgram(); | |
1839 | + test_environ(); | |
1840 | + test_file_execute(); | |
1841 | + test_file_misc(); | |
1842 | + return 0; | |
1843 | +} |
@@ -0,0 +1,953 @@ | ||
1 | +/* | |
2 | + * caitsith-parser-test.c | |
3 | + * | |
4 | + * Copyright (C) 2012-2013 Tetsuo Handa | |
5 | + * | |
6 | + * Version: 0.2 2016/10/05 | |
7 | + * | |
8 | + * This program is free software; you can redistribute it and/or modify it | |
9 | + * under the terms of the GNU General Public License v2 as published by the | |
10 | + * Free Software Foundation. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | + * more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License along with | |
18 | + * this program; if not, write to the Free Software Foundation, Inc., | |
19 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
20 | + */ | |
21 | + | |
22 | +#include <stdio.h> | |
23 | +#include <string.h> | |
24 | +#include <stdlib.h> | |
25 | +#include <sys/types.h> | |
26 | +#include <sys/stat.h> | |
27 | +#include <fcntl.h> | |
28 | +#include <unistd.h> | |
29 | +#include <time.h> | |
30 | +#include <signal.h> | |
31 | +#include <sys/mount.h> | |
32 | +#ifndef MS_REC | |
33 | +#define MS_REC 16384 | |
34 | +#endif | |
35 | +#ifndef MS_PRIVATE | |
36 | +#define MS_PRIVATE (1 << 18) | |
37 | +#endif | |
38 | + | |
39 | +enum ccs_mac_index { | |
40 | + CCS_MAC_EXECUTE, | |
41 | + CCS_MAC_READ, | |
42 | + CCS_MAC_WRITE, | |
43 | + CCS_MAC_APPEND, | |
44 | + CCS_MAC_CREATE, | |
45 | + CCS_MAC_UNLINK, | |
46 | + CCS_MAC_GETATTR, | |
47 | + CCS_MAC_MKDIR, | |
48 | + CCS_MAC_RMDIR, | |
49 | + CCS_MAC_MKFIFO, | |
50 | + CCS_MAC_MKSOCK, | |
51 | + CCS_MAC_TRUNCATE, | |
52 | + CCS_MAC_SYMLINK, | |
53 | + CCS_MAC_MKBLOCK, | |
54 | + CCS_MAC_MKCHAR, | |
55 | + CCS_MAC_LINK, | |
56 | + CCS_MAC_RENAME, | |
57 | + CCS_MAC_CHMOD, | |
58 | + CCS_MAC_CHOWN, | |
59 | + CCS_MAC_CHGRP, | |
60 | + CCS_MAC_IOCTL, | |
61 | + CCS_MAC_CHROOT, | |
62 | + CCS_MAC_MOUNT, | |
63 | + CCS_MAC_UMOUNT, | |
64 | + CCS_MAC_PIVOT_ROOT, | |
65 | + CCS_MAC_INET_STREAM_BIND, | |
66 | + CCS_MAC_INET_STREAM_LISTEN, | |
67 | + CCS_MAC_INET_STREAM_CONNECT, | |
68 | + CCS_MAC_INET_STREAM_ACCEPT, | |
69 | + CCS_MAC_INET_DGRAM_BIND, | |
70 | + CCS_MAC_INET_DGRAM_SEND, | |
71 | + CCS_MAC_INET_DGRAM_RECV, | |
72 | + CCS_MAC_INET_RAW_BIND, | |
73 | + CCS_MAC_INET_RAW_SEND, | |
74 | + CCS_MAC_INET_RAW_RECV, | |
75 | + CCS_MAC_UNIX_STREAM_BIND, | |
76 | + CCS_MAC_UNIX_STREAM_LISTEN, | |
77 | + CCS_MAC_UNIX_STREAM_CONNECT, | |
78 | + CCS_MAC_UNIX_STREAM_ACCEPT, | |
79 | + CCS_MAC_UNIX_DGRAM_BIND, | |
80 | + CCS_MAC_UNIX_DGRAM_SEND, | |
81 | + CCS_MAC_UNIX_DGRAM_RECV, | |
82 | + CCS_MAC_UNIX_SEQPACKET_BIND, | |
83 | + CCS_MAC_UNIX_SEQPACKET_LISTEN, | |
84 | + CCS_MAC_UNIX_SEQPACKET_CONNECT, | |
85 | + CCS_MAC_UNIX_SEQPACKET_ACCEPT, | |
86 | + CCS_MAC_ENVIRON, | |
87 | + CCS_MAC_PTRACE, | |
88 | + CCS_MAC_SIGNAL, | |
89 | + CCS_MAC_MODIFY_POLICY, | |
90 | + CCS_MAC_USE_NETLINK_SOCKET, | |
91 | + CCS_MAC_USE_PACKET_SOCKET, | |
92 | + CCS_MAC_USE_REBOOT, | |
93 | + CCS_MAC_USE_VHANGUP, | |
94 | + CCS_MAC_SET_TIME, | |
95 | + CCS_MAC_SET_PRIORITY, | |
96 | + CCS_MAC_SET_HOSTNAME, | |
97 | + CCS_MAC_USE_KERNEL_MODULE, | |
98 | + CCS_MAC_USE_NEW_KERNEL, | |
99 | + CCS_MAC_AUTO_DOMAIN_TRANSITION, | |
100 | + CCS_MAC_MANUAL_DOMAIN_TRANSITION, | |
101 | + CCS_MAX_MAC_INDEX | |
102 | +}; | |
103 | + | |
104 | +static const char * const ccs_mac_keywords[CCS_MAX_MAC_INDEX] = { | |
105 | + [CCS_MAC_EXECUTE] = "execute", | |
106 | + [CCS_MAC_READ] = "read", | |
107 | + [CCS_MAC_WRITE] = "write", | |
108 | + [CCS_MAC_APPEND] = "append", | |
109 | + [CCS_MAC_CREATE] = "create", | |
110 | + [CCS_MAC_UNLINK] = "unlink", | |
111 | + [CCS_MAC_GETATTR] = "getattr", | |
112 | + [CCS_MAC_MKDIR] = "mkdir", | |
113 | + [CCS_MAC_RMDIR] = "rmdir", | |
114 | + [CCS_MAC_MKFIFO] = "mkfifo", | |
115 | + [CCS_MAC_MKSOCK] = "mksock", | |
116 | + [CCS_MAC_TRUNCATE] = "truncate", | |
117 | + [CCS_MAC_SYMLINK] = "symlink", | |
118 | + [CCS_MAC_MKBLOCK] = "mkblock", | |
119 | + [CCS_MAC_MKCHAR] = "mkchar", | |
120 | + [CCS_MAC_LINK] = "link", | |
121 | + [CCS_MAC_RENAME] = "rename", | |
122 | + [CCS_MAC_CHMOD] = "chmod", | |
123 | + [CCS_MAC_CHOWN] = "chown", | |
124 | + [CCS_MAC_CHGRP] = "chgrp", | |
125 | + [CCS_MAC_IOCTL] = "ioctl", | |
126 | + [CCS_MAC_CHROOT] = "chroot", | |
127 | + [CCS_MAC_MOUNT] = "mount", | |
128 | + [CCS_MAC_UMOUNT] = "unmount", | |
129 | + [CCS_MAC_PIVOT_ROOT] = "pivot_root", | |
130 | + [CCS_MAC_INET_STREAM_BIND] = "inet_stream_bind", | |
131 | + [CCS_MAC_INET_STREAM_LISTEN] = "inet_stream_listen", | |
132 | + [CCS_MAC_INET_STREAM_CONNECT] = "inet_stream_connect", | |
133 | + [CCS_MAC_INET_STREAM_ACCEPT] = "inet_stream_accept", | |
134 | + [CCS_MAC_INET_DGRAM_BIND] = "inet_dgram_bind", | |
135 | + [CCS_MAC_INET_DGRAM_SEND] = "inet_dgram_send", | |
136 | + [CCS_MAC_INET_DGRAM_RECV] = "inet_dgram_recv", | |
137 | + [CCS_MAC_INET_RAW_BIND] = "inet_raw_bind", | |
138 | + [CCS_MAC_INET_RAW_SEND] = "inet_raw_send", | |
139 | + [CCS_MAC_INET_RAW_RECV] = "inet_raw_recv", | |
140 | + [CCS_MAC_UNIX_STREAM_BIND] = "unix_stream_bind", | |
141 | + [CCS_MAC_UNIX_STREAM_LISTEN] = "unix_stream_listen", | |
142 | + [CCS_MAC_UNIX_STREAM_CONNECT] = "unix_stream_connect", | |
143 | + [CCS_MAC_UNIX_STREAM_ACCEPT] = "unix_stream_accept", | |
144 | + [CCS_MAC_UNIX_DGRAM_BIND] = "unix_dgram_bind", | |
145 | + [CCS_MAC_UNIX_DGRAM_SEND] = "unix_dgram_send", | |
146 | + [CCS_MAC_UNIX_DGRAM_RECV] = "unix_dgram_recv", | |
147 | + [CCS_MAC_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", | |
148 | + [CCS_MAC_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", | |
149 | + [CCS_MAC_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", | |
150 | + [CCS_MAC_UNIX_SEQPACKET_ACCEPT] = "unix_seqpacket_accept", | |
151 | + [CCS_MAC_ENVIRON] = "environ", | |
152 | + [CCS_MAC_PTRACE] = "ptrace", | |
153 | + [CCS_MAC_SIGNAL] = "signal", | |
154 | + [CCS_MAC_MODIFY_POLICY] = "modify_policy", | |
155 | + [CCS_MAC_USE_NETLINK_SOCKET] = "use_netlink_socket", | |
156 | + [CCS_MAC_USE_PACKET_SOCKET] = "use_packet_socket", | |
157 | + [CCS_MAC_USE_REBOOT] = "use_reboot", | |
158 | + [CCS_MAC_USE_VHANGUP] = "use_vhangup", | |
159 | + [CCS_MAC_SET_TIME] = "set_time", | |
160 | + [CCS_MAC_SET_PRIORITY] = "set_priority", | |
161 | + [CCS_MAC_SET_HOSTNAME] = "set_hostname", | |
162 | + [CCS_MAC_USE_KERNEL_MODULE] = "use_kernel_module", | |
163 | + [CCS_MAC_USE_NEW_KERNEL] = "use_new_kernel", | |
164 | + [CCS_MAC_AUTO_DOMAIN_TRANSITION] = "auto_domain_transition", | |
165 | + [CCS_MAC_MANUAL_DOMAIN_TRANSITION] = "manual_domain_transition", | |
166 | +}; | |
167 | + | |
168 | +#define F(bit) (1ULL << bit) | |
169 | + | |
170 | +#define CCS_ALL_OK \ | |
171 | + (F(CCS_MAC_EXECUTE) | \ | |
172 | + F(CCS_MAC_READ) | \ | |
173 | + F(CCS_MAC_WRITE) | \ | |
174 | + F(CCS_MAC_APPEND) | \ | |
175 | + F(CCS_MAC_CREATE) | \ | |
176 | + F(CCS_MAC_UNLINK) | \ | |
177 | + F(CCS_MAC_GETATTR) | \ | |
178 | + F(CCS_MAC_MKDIR) | \ | |
179 | + F(CCS_MAC_RMDIR) | \ | |
180 | + F(CCS_MAC_MKFIFO) | \ | |
181 | + F(CCS_MAC_MKSOCK) | \ | |
182 | + F(CCS_MAC_TRUNCATE) | \ | |
183 | + F(CCS_MAC_SYMLINK) | \ | |
184 | + F(CCS_MAC_MKBLOCK) | \ | |
185 | + F(CCS_MAC_MKCHAR) | \ | |
186 | + F(CCS_MAC_LINK) | \ | |
187 | + F(CCS_MAC_RENAME) | \ | |
188 | + F(CCS_MAC_CHMOD) | \ | |
189 | + F(CCS_MAC_CHOWN) | \ | |
190 | + F(CCS_MAC_CHGRP) | \ | |
191 | + F(CCS_MAC_IOCTL) | \ | |
192 | + F(CCS_MAC_CHROOT) | \ | |
193 | + F(CCS_MAC_MOUNT) | \ | |
194 | + F(CCS_MAC_UMOUNT) | \ | |
195 | + F(CCS_MAC_PIVOT_ROOT) | \ | |
196 | + F(CCS_MAC_INET_STREAM_BIND) | \ | |
197 | + F(CCS_MAC_INET_STREAM_LISTEN) | \ | |
198 | + F(CCS_MAC_INET_STREAM_CONNECT) | \ | |
199 | + F(CCS_MAC_INET_STREAM_ACCEPT) | \ | |
200 | + F(CCS_MAC_INET_DGRAM_BIND) | \ | |
201 | + F(CCS_MAC_INET_DGRAM_SEND) | \ | |
202 | + F(CCS_MAC_INET_DGRAM_RECV) | \ | |
203 | + F(CCS_MAC_INET_RAW_BIND) | \ | |
204 | + F(CCS_MAC_INET_RAW_SEND) | \ | |
205 | + F(CCS_MAC_INET_RAW_RECV) | \ | |
206 | + F(CCS_MAC_UNIX_STREAM_BIND) | \ | |
207 | + F(CCS_MAC_UNIX_STREAM_LISTEN) | \ | |
208 | + F(CCS_MAC_UNIX_STREAM_CONNECT) | \ | |
209 | + F(CCS_MAC_UNIX_STREAM_ACCEPT) | \ | |
210 | + F(CCS_MAC_UNIX_DGRAM_BIND) | \ | |
211 | + F(CCS_MAC_UNIX_DGRAM_SEND) | \ | |
212 | + F(CCS_MAC_UNIX_DGRAM_RECV) | \ | |
213 | + F(CCS_MAC_UNIX_SEQPACKET_BIND) | \ | |
214 | + F(CCS_MAC_UNIX_SEQPACKET_LISTEN) | \ | |
215 | + F(CCS_MAC_UNIX_SEQPACKET_CONNECT) | \ | |
216 | + F(CCS_MAC_UNIX_SEQPACKET_ACCEPT) | \ | |
217 | + F(CCS_MAC_ENVIRON) | \ | |
218 | + F(CCS_MAC_PTRACE) | \ | |
219 | + F(CCS_MAC_SIGNAL) | \ | |
220 | + F(CCS_MAC_MODIFY_POLICY) | \ | |
221 | + F(CCS_MAC_USE_NETLINK_SOCKET) | \ | |
222 | + F(CCS_MAC_USE_PACKET_SOCKET) | \ | |
223 | + F(CCS_MAC_USE_REBOOT) | \ | |
224 | + F(CCS_MAC_USE_VHANGUP) | \ | |
225 | + F(CCS_MAC_SET_TIME) | \ | |
226 | + F(CCS_MAC_SET_PRIORITY) | \ | |
227 | + F(CCS_MAC_SET_HOSTNAME) | \ | |
228 | + F(CCS_MAC_USE_KERNEL_MODULE) | \ | |
229 | + F(CCS_MAC_USE_NEW_KERNEL) | \ | |
230 | + F(CCS_MAC_AUTO_DOMAIN_TRANSITION) | \ | |
231 | + F(CCS_MAC_MANUAL_DOMAIN_TRANSITION)) | |
232 | + | |
233 | +#define CCS_PATH_SELF_OK \ | |
234 | + (F(CCS_MAC_EXECUTE) | \ | |
235 | + F(CCS_MAC_ENVIRON) | \ | |
236 | + F(CCS_MAC_READ) | \ | |
237 | + F(CCS_MAC_WRITE) | \ | |
238 | + F(CCS_MAC_APPEND) | \ | |
239 | + F(CCS_MAC_UNLINK) | \ | |
240 | + F(CCS_MAC_GETATTR) | \ | |
241 | + F(CCS_MAC_RMDIR) | \ | |
242 | + F(CCS_MAC_TRUNCATE) | \ | |
243 | + F(CCS_MAC_CHMOD) | \ | |
244 | + F(CCS_MAC_CHOWN) | \ | |
245 | + F(CCS_MAC_CHGRP) | \ | |
246 | + F(CCS_MAC_IOCTL) | \ | |
247 | + F(CCS_MAC_CHROOT) | \ | |
248 | + F(CCS_MAC_UMOUNT)) | |
249 | + | |
250 | +#define CCS_PATH_PARENT_OK \ | |
251 | + (F(CCS_MAC_CREATE) | \ | |
252 | + F(CCS_MAC_MKDIR) | \ | |
253 | + F(CCS_MAC_MKFIFO) | \ | |
254 | + F(CCS_MAC_MKSOCK) | \ | |
255 | + F(CCS_MAC_SYMLINK) | \ | |
256 | + F(CCS_MAC_MKBLOCK) | \ | |
257 | + F(CCS_MAC_MKCHAR)) | |
258 | + | |
259 | +#define CCS_PATH_OK (CCS_PATH_SELF_OK | CCS_PATH_PARENT_OK) | |
260 | + | |
261 | +#define CCS_RENAME_OR_LINK_OK (F(CCS_MAC_LINK) | F(CCS_MAC_RENAME)) | |
262 | + | |
263 | +#define CCS_EXECUTE_OR_ENVIRON_OK (F(CCS_MAC_EXECUTE) | F(CCS_MAC_ENVIRON)) | |
264 | + | |
265 | +#define CCS_MKDEV_OK (F(CCS_MAC_MKBLOCK) | F(CCS_MAC_MKCHAR)) | |
266 | + | |
267 | +#define CCS_PATH_PERM_OK \ | |
268 | + (F(CCS_MAC_MKDIR) | \ | |
269 | + F(CCS_MAC_MKBLOCK) | \ | |
270 | + F(CCS_MAC_MKCHAR) | \ | |
271 | + F(CCS_MAC_MKFIFO) | \ | |
272 | + F(CCS_MAC_MKSOCK) | \ | |
273 | + F(CCS_MAC_CREATE) | \ | |
274 | + F(CCS_MAC_CHMOD)) | |
275 | + | |
276 | +#define CCS_IP_SOCKET_OK \ | |
277 | + (F(CCS_MAC_INET_STREAM_BIND) | \ | |
278 | + F(CCS_MAC_INET_STREAM_LISTEN) | \ | |
279 | + F(CCS_MAC_INET_STREAM_CONNECT) | \ | |
280 | + F(CCS_MAC_INET_STREAM_ACCEPT) | \ | |
281 | + F(CCS_MAC_INET_DGRAM_BIND) | \ | |
282 | + F(CCS_MAC_INET_DGRAM_SEND) | \ | |
283 | + F(CCS_MAC_INET_DGRAM_RECV)) | |
284 | + | |
285 | +#define CCS_RAW_SOCKET_OK \ | |
286 | + (F(CCS_MAC_INET_RAW_BIND) | \ | |
287 | + F(CCS_MAC_INET_RAW_SEND) | \ | |
288 | + F(CCS_MAC_INET_RAW_RECV)) | |
289 | + | |
290 | +#define CCS_INET_SOCKET_OK (CCS_IP_SOCKET_OK | CCS_RAW_SOCKET_OK) | |
291 | + | |
292 | +#define CCS_UNIX_SOCKET_OK \ | |
293 | + (F(CCS_MAC_UNIX_STREAM_BIND) | \ | |
294 | + F(CCS_MAC_UNIX_STREAM_LISTEN) | \ | |
295 | + F(CCS_MAC_UNIX_STREAM_CONNECT) | \ | |
296 | + F(CCS_MAC_UNIX_STREAM_ACCEPT) | \ | |
297 | + F(CCS_MAC_UNIX_DGRAM_BIND) | \ | |
298 | + F(CCS_MAC_UNIX_DGRAM_SEND) | \ | |
299 | + F(CCS_MAC_UNIX_DGRAM_RECV) | \ | |
300 | + F(CCS_MAC_UNIX_SEQPACKET_BIND) | \ | |
301 | + F(CCS_MAC_UNIX_SEQPACKET_LISTEN) | \ | |
302 | + F(CCS_MAC_UNIX_SEQPACKET_CONNECT) | \ | |
303 | + F(CCS_MAC_UNIX_SEQPACKET_ACCEPT)) | |
304 | + | |
305 | +enum ccs_var_type { | |
306 | + CCS_TYPE_INVALID, | |
307 | + CCS_TYPE_NUMBER, | |
308 | + CCS_TYPE_STRING, | |
309 | + CCS_TYPE_IPADDR, | |
310 | + CCS_TYPE_FILEPERM, | |
311 | + CCS_TYPE_FILETYPE, | |
312 | + CCS_TYPE_TASKTYPE, | |
313 | + CCS_TYPE_ASSIGN, | |
314 | +}; | |
315 | + | |
316 | +static const struct { | |
317 | + const char * const keyword; | |
318 | + const enum ccs_var_type left_type; | |
319 | + const enum ccs_var_type right_type; | |
320 | + const unsigned long long available; | |
321 | +} ccs_conditions[] = { | |
322 | + { "addr", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
323 | + CCS_UNIX_SOCKET_OK }, | |
324 | + { "argc", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
325 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
326 | + { "block", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
327 | + CCS_ALL_OK }, | |
328 | + { "char", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
329 | + CCS_ALL_OK }, | |
330 | + { "cmd", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
331 | + F(CCS_MAC_IOCTL) | F(CCS_MAC_PTRACE) }, | |
332 | + { "data", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
333 | + F(CCS_MAC_MOUNT) }, | |
334 | + { "dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
335 | + CCS_MKDEV_OK }, | |
336 | + { "dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
337 | + CCS_MKDEV_OK }, | |
338 | + { "directory", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
339 | + CCS_ALL_OK }, | |
340 | + { "domain", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
341 | + F(CCS_MAC_PTRACE) | F(CCS_MAC_MANUAL_DOMAIN_TRANSITION) }, | |
342 | + { "envc", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
343 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
344 | + { "exec", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
345 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
346 | + { "exec.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
347 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
348 | + { "exec.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
349 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
350 | + { "exec.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
351 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
352 | + { "exec.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
353 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
354 | + { "exec.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
355 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
356 | + { "exec.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
357 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
358 | + { "exec.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
359 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
360 | + { "exec.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
361 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
362 | + { "exec.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
363 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
364 | + { "exec.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
365 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
366 | + { "exec.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
367 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
368 | + { "exec.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
369 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
370 | + { "exec.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
371 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
372 | + { "exec.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
373 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
374 | + { "exec.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
375 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
376 | + { "execute_handler", CCS_TYPE_INVALID, CCS_TYPE_TASKTYPE, | |
377 | + CCS_ALL_OK }, | |
378 | + { "fifo", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
379 | + CCS_ALL_OK }, | |
380 | + { "file", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
381 | + CCS_ALL_OK }, | |
382 | + { "flags", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
383 | + F(CCS_MAC_MOUNT) | F(CCS_MAC_UMOUNT) }, | |
384 | + { "fstype", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
385 | + F(CCS_MAC_MOUNT) }, | |
386 | + { "gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
387 | + F(CCS_MAC_CHGRP) }, | |
388 | + { "group_execute", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
389 | + CCS_ALL_OK }, | |
390 | + { "group_read", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
391 | + CCS_ALL_OK }, | |
392 | + { "group_write", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
393 | + CCS_ALL_OK }, | |
394 | + { "handler", CCS_TYPE_ASSIGN, CCS_TYPE_INVALID, | |
395 | + F(CCS_MAC_EXECUTE) }, | |
396 | + { "ip", CCS_TYPE_IPADDR, CCS_TYPE_INVALID, | |
397 | + CCS_INET_SOCKET_OK }, | |
398 | + { "name", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
399 | + F(CCS_MAC_ENVIRON) }, | |
400 | + { "new_path", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
401 | + CCS_RENAME_OR_LINK_OK }, | |
402 | + { "new_path.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
403 | + F(CCS_MAC_RENAME) }, | |
404 | + { "new_path.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
405 | + F(CCS_MAC_RENAME) }, | |
406 | + { "new_path.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
407 | + F(CCS_MAC_RENAME) }, | |
408 | + { "new_path.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
409 | + F(CCS_MAC_RENAME) }, | |
410 | + { "new_path.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
411 | + F(CCS_MAC_RENAME) }, | |
412 | + { "new_path.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
413 | + F(CCS_MAC_RENAME) }, | |
414 | + { "new_path.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
415 | + F(CCS_MAC_RENAME) }, | |
416 | + { "new_path.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
417 | + CCS_RENAME_OR_LINK_OK }, | |
418 | + { "new_path.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
419 | + CCS_RENAME_OR_LINK_OK }, | |
420 | + { "new_path.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
421 | + CCS_RENAME_OR_LINK_OK }, | |
422 | + { "new_path.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
423 | + CCS_RENAME_OR_LINK_OK }, | |
424 | + { "new_path.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
425 | + CCS_RENAME_OR_LINK_OK }, | |
426 | + { "new_path.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
427 | + CCS_RENAME_OR_LINK_OK }, | |
428 | + { "new_path.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
429 | + CCS_RENAME_OR_LINK_OK }, | |
430 | + { "new_path.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
431 | + F(CCS_MAC_RENAME) }, | |
432 | + { "new_path.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
433 | + F(CCS_MAC_RENAME) }, | |
434 | + { "new_path.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
435 | + F(CCS_MAC_RENAME) }, | |
436 | + { "new_root", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
437 | + F(CCS_MAC_PIVOT_ROOT) }, | |
438 | + { "new_root.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
439 | + F(CCS_MAC_PIVOT_ROOT) }, | |
440 | + { "new_root.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
441 | + F(CCS_MAC_PIVOT_ROOT) }, | |
442 | + { "new_root.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
443 | + F(CCS_MAC_PIVOT_ROOT) }, | |
444 | + { "new_root.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
445 | + F(CCS_MAC_PIVOT_ROOT) }, | |
446 | + { "new_root.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
447 | + F(CCS_MAC_PIVOT_ROOT) }, | |
448 | + { "new_root.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
449 | + F(CCS_MAC_PIVOT_ROOT) }, | |
450 | + { "new_root.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
451 | + F(CCS_MAC_PIVOT_ROOT) }, | |
452 | + { "new_root.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
453 | + F(CCS_MAC_PIVOT_ROOT) }, | |
454 | + { "new_root.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
455 | + F(CCS_MAC_PIVOT_ROOT) }, | |
456 | + { "new_root.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
457 | + F(CCS_MAC_PIVOT_ROOT) }, | |
458 | + { "new_root.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
459 | + F(CCS_MAC_PIVOT_ROOT) }, | |
460 | + { "new_root.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
461 | + F(CCS_MAC_PIVOT_ROOT) }, | |
462 | + { "new_root.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
463 | + F(CCS_MAC_PIVOT_ROOT) }, | |
464 | + { "new_root.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
465 | + F(CCS_MAC_PIVOT_ROOT) }, | |
466 | + { "new_root.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
467 | + F(CCS_MAC_PIVOT_ROOT) }, | |
468 | + { "new_root.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
469 | + F(CCS_MAC_PIVOT_ROOT) }, | |
470 | + { "new_root.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
471 | + F(CCS_MAC_PIVOT_ROOT) }, | |
472 | + { "old_path", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
473 | + CCS_RENAME_OR_LINK_OK }, | |
474 | + { "old_path.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
475 | + CCS_RENAME_OR_LINK_OK }, | |
476 | + { "old_path.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
477 | + CCS_RENAME_OR_LINK_OK }, | |
478 | + { "old_path.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
479 | + CCS_RENAME_OR_LINK_OK }, | |
480 | + { "old_path.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
481 | + CCS_RENAME_OR_LINK_OK }, | |
482 | + { "old_path.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
483 | + CCS_RENAME_OR_LINK_OK }, | |
484 | + { "old_path.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
485 | + CCS_RENAME_OR_LINK_OK }, | |
486 | + { "old_path.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
487 | + CCS_RENAME_OR_LINK_OK }, | |
488 | + { "old_path.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
489 | + CCS_RENAME_OR_LINK_OK }, | |
490 | + { "old_path.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
491 | + CCS_RENAME_OR_LINK_OK }, | |
492 | + { "old_path.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
493 | + CCS_RENAME_OR_LINK_OK }, | |
494 | + { "old_path.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
495 | + CCS_RENAME_OR_LINK_OK }, | |
496 | + { "old_path.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
497 | + CCS_RENAME_OR_LINK_OK }, | |
498 | + { "old_path.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
499 | + CCS_RENAME_OR_LINK_OK }, | |
500 | + { "old_path.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
501 | + CCS_RENAME_OR_LINK_OK }, | |
502 | + { "old_path.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
503 | + CCS_RENAME_OR_LINK_OK }, | |
504 | + { "old_path.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
505 | + CCS_RENAME_OR_LINK_OK }, | |
506 | + { "old_path.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
507 | + CCS_RENAME_OR_LINK_OK }, | |
508 | + { "others_execute", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
509 | + CCS_ALL_OK }, | |
510 | + { "others_read", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
511 | + CCS_ALL_OK }, | |
512 | + { "others_write", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
513 | + CCS_ALL_OK }, | |
514 | + { "owner_execute", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
515 | + CCS_ALL_OK }, | |
516 | + { "owner_read", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
517 | + CCS_ALL_OK }, | |
518 | + { "owner_write", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
519 | + CCS_ALL_OK }, | |
520 | + { "path", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
521 | + CCS_PATH_OK }, | |
522 | + { "path.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
523 | + CCS_PATH_SELF_OK }, | |
524 | + { "path.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
525 | + CCS_PATH_SELF_OK }, | |
526 | + { "path.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
527 | + CCS_PATH_SELF_OK }, | |
528 | + { "path.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
529 | + CCS_PATH_SELF_OK }, | |
530 | + { "path.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
531 | + CCS_PATH_SELF_OK }, | |
532 | + { "path.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
533 | + CCS_PATH_SELF_OK }, | |
534 | + { "path.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
535 | + CCS_PATH_SELF_OK }, | |
536 | + { "path.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
537 | + CCS_PATH_OK }, | |
538 | + { "path.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
539 | + CCS_PATH_OK }, | |
540 | + { "path.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
541 | + CCS_PATH_OK }, | |
542 | + { "path.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
543 | + CCS_PATH_OK }, | |
544 | + { "path.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
545 | + CCS_PATH_OK }, | |
546 | + { "path.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
547 | + CCS_PATH_OK }, | |
548 | + { "path.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
549 | + CCS_PATH_OK }, | |
550 | + { "path.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
551 | + CCS_PATH_SELF_OK }, | |
552 | + { "path.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
553 | + CCS_PATH_SELF_OK }, | |
554 | + { "path.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
555 | + CCS_PATH_SELF_OK }, | |
556 | + { "perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
557 | + CCS_PATH_PERM_OK }, | |
558 | + { "port", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
559 | + CCS_IP_SOCKET_OK }, | |
560 | + { "proto", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
561 | + CCS_RAW_SOCKET_OK }, | |
562 | + { "put_old", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
563 | + F(CCS_MAC_PIVOT_ROOT) }, | |
564 | + { "put_old.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
565 | + F(CCS_MAC_PIVOT_ROOT) }, | |
566 | + { "put_old.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
567 | + F(CCS_MAC_PIVOT_ROOT) }, | |
568 | + { "put_old.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
569 | + F(CCS_MAC_PIVOT_ROOT) }, | |
570 | + { "put_old.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
571 | + F(CCS_MAC_PIVOT_ROOT) }, | |
572 | + { "put_old.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
573 | + F(CCS_MAC_PIVOT_ROOT) }, | |
574 | + { "put_old.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
575 | + F(CCS_MAC_PIVOT_ROOT) }, | |
576 | + { "put_old.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
577 | + F(CCS_MAC_PIVOT_ROOT) }, | |
578 | + { "put_old.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
579 | + F(CCS_MAC_PIVOT_ROOT) }, | |
580 | + { "put_old.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
581 | + F(CCS_MAC_PIVOT_ROOT) }, | |
582 | + { "put_old.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
583 | + F(CCS_MAC_PIVOT_ROOT) }, | |
584 | + { "put_old.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
585 | + F(CCS_MAC_PIVOT_ROOT) }, | |
586 | + { "put_old.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
587 | + F(CCS_MAC_PIVOT_ROOT) }, | |
588 | + { "put_old.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
589 | + F(CCS_MAC_PIVOT_ROOT) }, | |
590 | + { "put_old.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
591 | + F(CCS_MAC_PIVOT_ROOT) }, | |
592 | + { "put_old.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
593 | + F(CCS_MAC_PIVOT_ROOT) }, | |
594 | + { "put_old.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
595 | + F(CCS_MAC_PIVOT_ROOT) }, | |
596 | + { "put_old.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
597 | + F(CCS_MAC_PIVOT_ROOT) }, | |
598 | + { "setgid", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
599 | + CCS_ALL_OK }, | |
600 | + { "setuid", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
601 | + CCS_ALL_OK }, | |
602 | + { "sig", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
603 | + F(CCS_MAC_SIGNAL) }, | |
604 | + { "socket", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
605 | + CCS_ALL_OK }, | |
606 | + { "source", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
607 | + F(CCS_MAC_MOUNT) }, | |
608 | + { "source.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
609 | + F(CCS_MAC_MOUNT) }, | |
610 | + { "source.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
611 | + F(CCS_MAC_MOUNT) }, | |
612 | + { "source.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
613 | + F(CCS_MAC_MOUNT) }, | |
614 | + { "source.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
615 | + F(CCS_MAC_MOUNT) }, | |
616 | + { "source.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
617 | + F(CCS_MAC_MOUNT) }, | |
618 | + { "source.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
619 | + F(CCS_MAC_MOUNT) }, | |
620 | + { "source.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
621 | + F(CCS_MAC_MOUNT) }, | |
622 | + { "source.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
623 | + F(CCS_MAC_MOUNT) }, | |
624 | + { "source.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
625 | + F(CCS_MAC_MOUNT) }, | |
626 | + { "source.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
627 | + F(CCS_MAC_MOUNT) }, | |
628 | + { "source.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
629 | + F(CCS_MAC_MOUNT) }, | |
630 | + { "source.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
631 | + F(CCS_MAC_MOUNT) }, | |
632 | + { "source.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
633 | + F(CCS_MAC_MOUNT) }, | |
634 | + { "source.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
635 | + F(CCS_MAC_MOUNT) }, | |
636 | + { "source.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
637 | + F(CCS_MAC_MOUNT) }, | |
638 | + { "source.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
639 | + F(CCS_MAC_MOUNT) }, | |
640 | + { "source.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
641 | + F(CCS_MAC_MOUNT) }, | |
642 | + { "sticky", CCS_TYPE_INVALID, CCS_TYPE_FILEPERM, | |
643 | + CCS_ALL_OK }, | |
644 | + { "symlink", CCS_TYPE_INVALID, CCS_TYPE_FILETYPE, | |
645 | + CCS_ALL_OK }, | |
646 | + { "target", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
647 | + F(CCS_MAC_MOUNT) | F(CCS_MAC_SYMLINK) }, | |
648 | + { "target.dev_major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
649 | + F(CCS_MAC_MOUNT) }, | |
650 | + { "target.dev_minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
651 | + F(CCS_MAC_MOUNT) }, | |
652 | + { "target.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
653 | + F(CCS_MAC_MOUNT) }, | |
654 | + { "target.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
655 | + F(CCS_MAC_MOUNT) }, | |
656 | + { "target.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
657 | + F(CCS_MAC_MOUNT) }, | |
658 | + { "target.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
659 | + F(CCS_MAC_MOUNT) }, | |
660 | + { "target.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
661 | + F(CCS_MAC_MOUNT) }, | |
662 | + { "target.parent.fsmagic", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
663 | + F(CCS_MAC_MOUNT) }, | |
664 | + { "target.parent.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
665 | + F(CCS_MAC_MOUNT) }, | |
666 | + { "target.parent.ino", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
667 | + F(CCS_MAC_MOUNT) }, | |
668 | + { "target.parent.major", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
669 | + F(CCS_MAC_MOUNT) }, | |
670 | + { "target.parent.minor", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
671 | + F(CCS_MAC_MOUNT) }, | |
672 | + { "target.parent.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
673 | + F(CCS_MAC_MOUNT) }, | |
674 | + { "target.parent.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
675 | + F(CCS_MAC_MOUNT) }, | |
676 | + { "target.perm", CCS_TYPE_FILEPERM, CCS_TYPE_FILEPERM, | |
677 | + F(CCS_MAC_MOUNT) }, | |
678 | + { "target.type", CCS_TYPE_FILETYPE, CCS_TYPE_FILETYPE, | |
679 | + F(CCS_MAC_MOUNT) }, | |
680 | + { "target.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
681 | + F(CCS_MAC_MOUNT) }, | |
682 | + { "task.domain", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
683 | + CCS_ALL_OK }, | |
684 | + { "task.egid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
685 | + CCS_ALL_OK }, | |
686 | + { "task.euid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
687 | + CCS_ALL_OK }, | |
688 | + { "task.exe", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
689 | + CCS_ALL_OK }, | |
690 | + { "task.fsgid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
691 | + CCS_ALL_OK }, | |
692 | + { "task.fsuid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
693 | + CCS_ALL_OK }, | |
694 | + { "task.gid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
695 | + CCS_ALL_OK }, | |
696 | + { "task.pid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
697 | + CCS_ALL_OK }, | |
698 | + { "task.ppid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
699 | + CCS_ALL_OK }, | |
700 | + { "task.sgid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
701 | + CCS_ALL_OK }, | |
702 | + { "task.suid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
703 | + CCS_ALL_OK }, | |
704 | + { "task.type", CCS_TYPE_TASKTYPE, CCS_TYPE_INVALID, | |
705 | + CCS_ALL_OK }, | |
706 | + { "task.uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
707 | + CCS_ALL_OK }, | |
708 | + { "transition", CCS_TYPE_ASSIGN, CCS_TYPE_INVALID, | |
709 | + F(CCS_MAC_EXECUTE) | F(CCS_MAC_AUTO_DOMAIN_TRANSITION) }, | |
710 | + { "uid", CCS_TYPE_NUMBER, CCS_TYPE_NUMBER, | |
711 | + F(CCS_MAC_CHOWN) }, | |
712 | + { "value", CCS_TYPE_STRING, CCS_TYPE_STRING, | |
713 | + F(CCS_MAC_ENVIRON) }, | |
714 | + { "argv[0]", CCS_TYPE_STRING, CCS_TYPE_INVALID, | |
715 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
716 | + { "argv[4294967295]", CCS_TYPE_STRING, CCS_TYPE_INVALID, | |
717 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
718 | + { "envp[\"PATH\"]", CCS_TYPE_STRING, CCS_TYPE_INVALID, | |
719 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
720 | + { "envp[\"\\*\"]", CCS_TYPE_STRING, CCS_TYPE_INVALID, | |
721 | + CCS_EXECUTE_OR_ENVIRON_OK }, | |
722 | + { "NULL", CCS_TYPE_INVALID, CCS_TYPE_STRING, | |
723 | + CCS_ALL_OK }, | |
724 | + { "\"word\"", CCS_TYPE_INVALID, CCS_TYPE_ASSIGN, | |
725 | + CCS_ALL_OK }, | |
726 | + { "\"/\"", CCS_TYPE_INVALID, CCS_TYPE_STRING, | |
727 | + CCS_ALL_OK }, | |
728 | + { "\"/\\*\"", CCS_TYPE_INVALID, CCS_TYPE_STRING, | |
729 | + CCS_ALL_OK }, | |
730 | + { "@STRING_GROUP", CCS_TYPE_INVALID, CCS_TYPE_STRING, | |
731 | + CCS_ALL_OK }, | |
732 | + { "0", CCS_TYPE_INVALID, CCS_TYPE_NUMBER, | |
733 | + CCS_ALL_OK }, | |
734 | + { "0-4294967295", CCS_TYPE_INVALID, CCS_TYPE_NUMBER, | |
735 | + CCS_ALL_OK }, | |
736 | + { "@NUMBER_GROUP", CCS_TYPE_INVALID, CCS_TYPE_NUMBER, | |
737 | + CCS_ALL_OK }, | |
738 | + { "127.0.0.1", CCS_TYPE_INVALID, CCS_TYPE_IPADDR, | |
739 | + CCS_ALL_OK }, | |
740 | + { "0.0.0.0-0.0.0.1", CCS_TYPE_INVALID, CCS_TYPE_IPADDR, | |
741 | + CCS_ALL_OK }, | |
742 | + { "::1", CCS_TYPE_INVALID, CCS_TYPE_IPADDR, | |
743 | + CCS_ALL_OK }, | |
744 | + { "::-::1", CCS_TYPE_INVALID, CCS_TYPE_IPADDR, | |
745 | + CCS_ALL_OK }, | |
746 | + { "@IP_GROUP", CCS_TYPE_INVALID, CCS_TYPE_IPADDR, | |
747 | + CCS_ALL_OK }, | |
748 | +}; | |
749 | + | |
750 | +static char *policy = NULL; | |
751 | +static int policy_len = 0; | |
752 | + | |
753 | +static inline void truncate_policy(void) | |
754 | +{ | |
755 | + policy_len = 0; | |
756 | +} | |
757 | + | |
758 | +static void add_policy(const char *data) | |
759 | +{ | |
760 | + const int len = strlen(data); | |
761 | + char *tmp = realloc(policy, policy_len + len + 1); | |
762 | + if (!tmp) { | |
763 | + fprintf(stderr, "Out of memory\n"); | |
764 | + exit(1); | |
765 | + } | |
766 | + policy = tmp; | |
767 | + memmove(policy + policy_len, data, len + 1); | |
768 | + policy_len += len; | |
769 | +} | |
770 | + | |
771 | +static unsigned int tests_now = 0; | |
772 | + | |
773 | +static void check_policy_written(const _Bool valid) | |
774 | +{ | |
775 | + int fd1 = open(POLDIR "/policy", O_RDWR); | |
776 | + int fd2 = open(POLDIR "/policy", O_RDWR); | |
777 | + int len = strlen(policy); | |
778 | + int ret; | |
779 | + char *buffer; | |
780 | + if (fd1 == EOF || fd2 == EOF) { | |
781 | + printf("%s", policy); | |
782 | + return; | |
783 | + } | |
784 | + ret = write(fd1, policy, len); | |
785 | + if (ret != len) { | |
786 | + fprintf(stderr, "Write error (%d, %d)\n", ret, len); | |
787 | + exit(1); | |
788 | + } | |
789 | + buffer = calloc(len + 4096, 1); | |
790 | + if (!buffer) { | |
791 | + fprintf(stderr, "Out of memory\n"); | |
792 | + exit(1); | |
793 | + } | |
794 | + ret = read(fd1, buffer, len + 4095); | |
795 | + if (!strstr(buffer, policy) != !valid) { | |
796 | + fprintf(stderr, "Test %u: FAILED(add) '%s' : '%s' valid=%u\n", | |
797 | + tests_now - 1, buffer, policy, valid); | |
798 | + exit(1); | |
799 | + } | |
800 | + printf("Test %u: OK '%s' : '%s'\n", tests_now - 1, buffer, policy); | |
801 | + if (write(fd2, "delete ", 7) != 7 || write(fd2, policy, len) != len) { | |
802 | + fprintf(stderr, "Write error\n"); | |
803 | + exit(1); | |
804 | + } | |
805 | + memset(buffer, 0, len + 1024); | |
806 | + ret = read(fd2, buffer, len + 1023); | |
807 | + if (strstr(buffer, policy)) { | |
808 | + fprintf(stderr, "Test %u: FAILED(remove) '%s' : '%s'\n", | |
809 | + tests_now - 1, buffer, policy); | |
810 | + exit(1); | |
811 | + } | |
812 | + printf("Test %u: OK '%s' : '%s'\n", tests_now - 1, buffer, policy); | |
813 | + free(buffer); | |
814 | + close(fd1); | |
815 | + close(fd2); | |
816 | +} | |
817 | + | |
818 | +static inline void add_policy2(const char *data1, const char *data2) | |
819 | +{ | |
820 | + add_policy(data1); | |
821 | + add_policy(data2); | |
822 | +} | |
823 | + | |
824 | +static inline void add_policy3(const char *data1, const char *data2, | |
825 | + const char *data3) | |
826 | +{ | |
827 | + add_policy(data1); | |
828 | + add_policy(data2); | |
829 | + add_policy(data3); | |
830 | +} | |
831 | + | |
832 | +static _Bool adjust_exception(const int mac, const int p1, const int p2) | |
833 | +{ | |
834 | + if (ccs_conditions[p1].left_type == CCS_TYPE_ASSIGN) | |
835 | + return !strcmp(ccs_conditions[p2].keyword, "NULL") || | |
836 | + !strcmp(ccs_conditions[p2].keyword,"\"/\""); | |
837 | + if (ccs_conditions[p2].keyword[0] == '@') | |
838 | + return ccs_conditions[p1].left_type == CCS_TYPE_STRING || | |
839 | + ccs_conditions[p1].left_type == CCS_TYPE_NUMBER || | |
840 | + ccs_conditions[p1].left_type == CCS_TYPE_IPADDR || | |
841 | + ccs_conditions[p1].left_type == CCS_TYPE_FILEPERM; | |
842 | + if (ccs_conditions[p1].left_type == CCS_TYPE_STRING) | |
843 | + return !strcmp(ccs_conditions[p2].keyword, "\"word\""); | |
844 | + if (ccs_conditions[p1].left_type == CCS_TYPE_FILEPERM && | |
845 | + ccs_conditions[p2].right_type == CCS_TYPE_NUMBER) | |
846 | + return 1; | |
847 | + return 0; | |
848 | +} | |
849 | + | |
850 | +static unsigned int tests_start = 0; | |
851 | + | |
852 | +static void check_righthand(const int mac, const int p1, const _Bool org_valid) | |
853 | +{ | |
854 | + int p2; | |
855 | + const enum ccs_var_type type = ccs_conditions[p1].left_type; | |
856 | + for (p2 = 0; p2 < sizeof(ccs_conditions) / sizeof(ccs_conditions[0]); | |
857 | + p2++) { | |
858 | + _Bool valid = org_valid; | |
859 | + if (tests_now++ < tests_start) | |
860 | + continue; | |
861 | + if (valid) { | |
862 | + if (!(ccs_conditions[p2].available & F(mac))) | |
863 | + valid = 0; | |
864 | + else if (ccs_conditions[p2].right_type != type) | |
865 | + valid = adjust_exception(mac, p1, p2); | |
866 | + } | |
867 | + truncate_policy(); | |
868 | + add_policy3("0 acl ", ccs_mac_keywords[mac], "\n" | |
869 | + " audit 0\n 0 allow"); | |
870 | + add_policy2(" ", ccs_conditions[p1].keyword); | |
871 | + add_policy2("=", ccs_conditions[p2].keyword); | |
872 | + if (mac == CCS_MAC_AUTO_DOMAIN_TRANSITION && | |
873 | + strcmp(ccs_conditions[p1].keyword, "transition")) | |
874 | + add_policy(" transition=\"word\""); | |
875 | + add_policy("\n"); | |
876 | + check_policy_written(valid); | |
877 | + if (type == CCS_TYPE_ASSIGN) | |
878 | + valid = 0; | |
879 | + truncate_policy(); | |
880 | + add_policy3("0 acl ", ccs_mac_keywords[mac], "\n" | |
881 | + " audit 0\n 0 allow"); | |
882 | + add_policy2(" ", ccs_conditions[p1].keyword); | |
883 | + add_policy2("!=", ccs_conditions[p2].keyword); | |
884 | + if (mac == CCS_MAC_AUTO_DOMAIN_TRANSITION && | |
885 | + strcmp(ccs_conditions[p1].keyword, "transition")) | |
886 | + add_policy(" transition=\"word\""); | |
887 | + add_policy("\n"); | |
888 | + check_policy_written(valid); | |
889 | + { | |
890 | + static time_t last = 0; | |
891 | + time_t now = time(NULL); | |
892 | + if (now - last >= (time_t) 10) { | |
893 | + last = now; | |
894 | + fprintf(stderr, "Test %u passed\n", | |
895 | + tests_now - 1); | |
896 | + } | |
897 | + } | |
898 | + } | |
899 | +} | |
900 | + | |
901 | +static void reset_policy(void) | |
902 | +{ | |
903 | + FILE *fp2 = fopen(POLDIR "/policy", "r"); | |
904 | + FILE *fp1 = fopen(POLDIR "/policy", "w"); | |
905 | + if (!fp1 || !fp2) { | |
906 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
907 | + exit(1); | |
908 | + } | |
909 | + while (1) { | |
910 | + const int c = fgetc(fp2); | |
911 | + if (c == EOF) | |
912 | + break; | |
913 | + fputc(c, fp1); | |
914 | + if (c == '\n') | |
915 | + fprintf(fp1, "delete "); | |
916 | + } | |
917 | + fclose(fp2); | |
918 | + fclose(fp1); | |
919 | + | |
920 | + /* Do not leave the init process in stopped state. */ | |
921 | + kill(1, SIGCONT); | |
922 | + | |
923 | + /* Undo mount("/", MS_REC|MS_SHARED) made by systemd. */ | |
924 | + mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL); | |
925 | +} | |
926 | + | |
927 | +int main(int argc, char *argv[]) { | |
928 | + int mac; | |
929 | + | |
930 | + reset_policy(); | |
931 | + if (argc > 1) | |
932 | + tests_start = atoi(argv[1]); | |
933 | + for (mac = 0; mac < CCS_MAX_MAC_INDEX; mac++) { | |
934 | + truncate_policy(); | |
935 | + add_policy3("0 acl ", ccs_mac_keywords[mac], "\n" | |
936 | + " audit 0\n"); | |
937 | + check_policy_written(1); | |
938 | + } | |
939 | + for (mac = 0; mac < CCS_MAX_MAC_INDEX; mac++) { | |
940 | + int p1; | |
941 | + for (p1 = 0; | |
942 | + p1 < sizeof(ccs_conditions) / sizeof(ccs_conditions[0]); | |
943 | + p1++) { | |
944 | + _Bool valid = 1; | |
945 | + if (ccs_conditions[p1].left_type == CCS_TYPE_INVALID || | |
946 | + !(ccs_conditions[p1].available & F(mac))) | |
947 | + valid = 0; | |
948 | + check_righthand(mac, p1, valid); | |
949 | + } | |
950 | + } | |
951 | + fprintf(stderr, "Test %u passed\n", tests_now - 1); | |
952 | + return 0; | |
953 | +} |
@@ -0,0 +1,23 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +ALL_FILES = caitsith_param_test caitsith_log_test caitsith_parser_test caitsith_audit2cond_test caitsith_lsm_test caitsith_wildcard_test | |
4 | + | |
5 | +all: $(ALL_FILES) | |
6 | + | |
7 | +# | |
8 | +# Tools for kernel testing. | |
9 | +# | |
10 | + | |
11 | +BINDIR = '"'$(shell readlink -f /bin)'"' | |
12 | +POLDIR = '"/sys/kernel/security/caitsith"' | |
13 | +#POLDIR = '"/proc/caitsith"' | |
14 | + | |
15 | +.c: | |
16 | + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DBINDIR=$(BINDIR) -DPOLDIR=$(POLDIR) -o $@ $< | |
17 | + | |
18 | +# | |
19 | +# Delete all test programs. | |
20 | +# | |
21 | + | |
22 | +clean: | |
23 | + rm -f $(ALL_FILES) |
@@ -0,0 +1,440 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <string.h> | |
3 | +#include <stdlib.h> | |
4 | +#include <sys/types.h> | |
5 | +#include <sys/stat.h> | |
6 | +#include <fcntl.h> | |
7 | +#include <unistd.h> | |
8 | +#include <linux/kdev_t.h> | |
9 | +#include <signal.h> | |
10 | +#include <sys/mount.h> | |
11 | +#include <sys/ioctl.h> | |
12 | + | |
13 | +#ifndef MS_MOVE | |
14 | +#define MS_MOVE 8192 | |
15 | +#endif | |
16 | +#ifndef MS_REC | |
17 | +#define MS_REC 16384 | |
18 | +#endif | |
19 | +#ifndef MS_UNBINDABLE | |
20 | +#define MS_UNBINDABLE (1<<17) | |
21 | +#endif | |
22 | +#ifndef MS_PRIVATE | |
23 | +#define MS_PRIVATE (1<<18) | |
24 | +#endif | |
25 | +#ifndef MS_SLAVE | |
26 | +#define MS_SLAVE (1<<19) | |
27 | +#endif | |
28 | +#ifndef MS_SHARED | |
29 | +#define MS_SHARED (1<<20) | |
30 | +#endif | |
31 | + | |
32 | +#include <asm/unistd.h> | |
33 | +static inline int pivot_root(const char *new_root, const char *put_old) | |
34 | +{ | |
35 | + return syscall(__NR_pivot_root, new_root, put_old); | |
36 | +} | |
37 | + | |
38 | +static _Bool debug = 0; | |
39 | + | |
40 | +static char *read_log(const char *expected_result, const char *expected_action) | |
41 | +{ | |
42 | + static int fd = EOF; | |
43 | + static char buffer[16384]; | |
44 | + if (fd == EOF) | |
45 | + fd = open(POLDIR "/audit", O_RDONLY); | |
46 | + memset(buffer, 0, sizeof(buffer)); | |
47 | + while (buffer[0] = '\0', read(fd, buffer, sizeof(buffer) - 1) > 0) { | |
48 | + char *cp1; | |
49 | + char *cp2; | |
50 | + if (debug) | |
51 | + printf("Got '%s'\n", buffer); | |
52 | + cp1 = strstr(buffer, " / "); | |
53 | + if (buffer[0] != '#' || !cp1 || !strchr(buffer, '\n')) { | |
54 | + fprintf(stderr, | |
55 | + "Expected complete audit log, got '%s'\n", | |
56 | + buffer); | |
57 | + return NULL; | |
58 | + } | |
59 | + *cp1 = '\0'; | |
60 | + if (!strstr(buffer, expected_result)) | |
61 | + continue; | |
62 | + cp1 += 3; | |
63 | + cp2 = strchr(cp1, ' '); | |
64 | + if (!cp2) { | |
65 | + fprintf(stderr, | |
66 | + "Expected complete audit log, got '%s'\n", | |
67 | + cp1); | |
68 | + return NULL; | |
69 | + } | |
70 | + *cp2++ = '\0'; | |
71 | + if (strcmp(expected_action, cp1)) | |
72 | + continue; | |
73 | + return cp2; | |
74 | + } | |
75 | + fprintf(stderr, "Expected '%s' '%s', found none\n", | |
76 | + expected_result, expected_action); | |
77 | + return NULL; | |
78 | +} | |
79 | + | |
80 | +static void create_dummy(const char *path) | |
81 | +{ | |
82 | + close(open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600)); | |
83 | +} | |
84 | + | |
85 | +static void test_execute(void) | |
86 | +{ | |
87 | + char *args[3] = { "null", "--help", NULL }; | |
88 | + char *envs[3] = { "PATH=/", "HOME=/", NULL }; | |
89 | + create_dummy("/tmp/null"); | |
90 | + chmod("/tmp/null", 0700); | |
91 | + execve("/tmp/null", args, envs); | |
92 | +} | |
93 | + | |
94 | +static void test_read(void) | |
95 | +{ | |
96 | + close(open("/dev/null", O_RDONLY)); | |
97 | +} | |
98 | + | |
99 | +static void test_write(void) | |
100 | +{ | |
101 | + close(open("/dev/null", O_WRONLY)); | |
102 | +} | |
103 | + | |
104 | +static void test_append(void) | |
105 | +{ | |
106 | + close(open("/dev/null", O_WRONLY | O_APPEND)); | |
107 | +} | |
108 | + | |
109 | +static void test_create(void) | |
110 | +{ | |
111 | + unlink("/tmp/null"); | |
112 | + create_dummy("/tmp/null"); | |
113 | +} | |
114 | + | |
115 | +static void test_unlink(void) | |
116 | +{ | |
117 | + create_dummy("/tmp/null"); | |
118 | + unlink("/tmp/null"); | |
119 | +} | |
120 | + | |
121 | +static void test_getattr(void) | |
122 | +{ | |
123 | + struct stat buf; | |
124 | + create_dummy("/tmp/null"); | |
125 | + stat("/tmp/null", &buf); | |
126 | +} | |
127 | + | |
128 | +static void test_mkdir(void) | |
129 | +{ | |
130 | + rmdir("/tmp/nulldir"); | |
131 | + mkdir("/tmp/nulldir", 0755); | |
132 | +} | |
133 | + | |
134 | +static void test_rmdir(void) | |
135 | +{ | |
136 | + mkdir("/tmp/nulldir", 0755); | |
137 | + rmdir("/tmp/nulldir"); | |
138 | +} | |
139 | + | |
140 | +static void test_mkfifo(void) | |
141 | +{ | |
142 | + unlink("/tmp/null"); | |
143 | + mknod("/tmp/null", S_IFIFO, 0); | |
144 | +} | |
145 | + | |
146 | +static void test_mksock(void) | |
147 | +{ | |
148 | + unlink("/tmp/null"); | |
149 | + mknod("/tmp/null", S_IFSOCK, 0); | |
150 | +} | |
151 | + | |
152 | +static void test_truncate(void) | |
153 | +{ | |
154 | + create_dummy("/tmp/null"); | |
155 | + truncate("/tmp/null", 0); | |
156 | +} | |
157 | + | |
158 | +static void test_symlink(void) | |
159 | +{ | |
160 | + unlink("/tmp/null"); | |
161 | + symlink("symlink'starget", "/tmp/null"); | |
162 | +} | |
163 | + | |
164 | +static void test_mkblock(void) | |
165 | +{ | |
166 | + unlink("/tmp/null"); | |
167 | + mknod("/tmp/null", S_IFBLK, MKDEV(1, 0)); | |
168 | +} | |
169 | + | |
170 | +static void test_mkchar(void) | |
171 | +{ | |
172 | + unlink("/tmp/null"); | |
173 | + mknod("/tmp/null", S_IFCHR, MKDEV(1, 3)); | |
174 | +} | |
175 | + | |
176 | +static void test_link(void) | |
177 | +{ | |
178 | + create_dummy("/tmp/link"); | |
179 | + unlink("/tmp/newlink"); | |
180 | + link("/tmp/link", "/tmp/newlink"); | |
181 | +} | |
182 | + | |
183 | +static void test_rename(void) | |
184 | +{ | |
185 | + link("/dev/null", "/dev/null0"); | |
186 | + rename("/dev/null0", "/dev/null1"); | |
187 | + unlink("/dev/null1"); | |
188 | +} | |
189 | + | |
190 | +static void test_chmod(void) | |
191 | +{ | |
192 | + chmod("/dev/null", 0666); | |
193 | +} | |
194 | + | |
195 | +static void test_chown(void) | |
196 | +{ | |
197 | + chown("/dev/null", 0, -1); | |
198 | +} | |
199 | + | |
200 | +static void test_chgrp(void) | |
201 | +{ | |
202 | + chown("/dev/null", -1, 0); | |
203 | +} | |
204 | + | |
205 | +static void test_ioctl(void) | |
206 | +{ | |
207 | + int fd = open("/dev/null", 3); | |
208 | + ioctl(fd, 0); | |
209 | + close(fd); | |
210 | +} | |
211 | + | |
212 | +static void test_chroot(void) | |
213 | +{ | |
214 | + chroot("/"); | |
215 | +} | |
216 | + | |
217 | +static void test_mount1(void) | |
218 | +{ | |
219 | + mount(NULL, "/tmp", "tmpfs", 0, "size=10%,uid=0,gid=0"); | |
220 | + umount("/tmp"); | |
221 | +} | |
222 | + | |
223 | +static void test_mount2(void) | |
224 | +{ | |
225 | + mount("/", "/", NULL, MS_BIND, NULL); | |
226 | +} | |
227 | + | |
228 | +static void test_mount3(void) | |
229 | +{ | |
230 | + mount("/", "/", NULL, MS_MOVE, NULL); | |
231 | +} | |
232 | + | |
233 | +static void test_mount4(void) | |
234 | +{ | |
235 | + mount(NULL, "/", NULL, MS_REMOUNT | MS_NOATIME, NULL); | |
236 | +} | |
237 | + | |
238 | +static void test_unmount(void) | |
239 | +{ | |
240 | + umount2("/", 1); | |
241 | +} | |
242 | + | |
243 | +static void test_pivot_root(void) | |
244 | +{ | |
245 | + pivot_root("/", "/"); | |
246 | +} | |
247 | + | |
248 | +static _Bool check_policy(const char *policy, const char *decision, | |
249 | + const char *condition) | |
250 | +{ | |
251 | + static char buffer[16384]; | |
252 | + FILE *fp = fopen(POLDIR "/policy", "r"); | |
253 | + _Bool found = 0; | |
254 | + if (!fp) { | |
255 | + fprintf(stderr, "Can't read " POLDIR "/policy interface.\n"); | |
256 | + return 0; | |
257 | + } | |
258 | + memset(buffer, 0, sizeof(buffer)); | |
259 | + while (fgets(buffer, sizeof(buffer) - 1, fp)) { | |
260 | + if (strstr(buffer, decision) && strstr(buffer, condition)) { | |
261 | + found = 1; | |
262 | + break; | |
263 | + } | |
264 | + } | |
265 | + fclose(fp); | |
266 | + if (found) { | |
267 | + printf("%s %s%s\n", policy, decision, condition); | |
268 | + return 1; | |
269 | + } | |
270 | + fprintf(stderr, "Can't find %s %s%s\n", | |
271 | + policy, decision, condition); | |
272 | + return 0; | |
273 | +} | |
274 | + | |
275 | +static void reset_policy(void) | |
276 | +{ | |
277 | + FILE *fp2 = fopen(POLDIR "/policy", "r"); | |
278 | + FILE *fp1 = fopen(POLDIR "/policy", "w"); | |
279 | + if (!fp1 || !fp2) { | |
280 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
281 | + exit(1); | |
282 | + } | |
283 | + while (1) { | |
284 | + const int c = fgetc(fp2); | |
285 | + if (c == EOF) | |
286 | + break; | |
287 | + fputc(c, fp1); | |
288 | + if (c == '\n') | |
289 | + fprintf(fp1, "delete "); | |
290 | + } | |
291 | + fclose(fp2); | |
292 | + fclose(fp1); | |
293 | + | |
294 | + /* Do not leave the init process in stopped state. */ | |
295 | + kill(1, SIGCONT); | |
296 | + | |
297 | + /* Undo mount("/", MS_REC|MS_SHARED) made by systemd. */ | |
298 | + mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL); | |
299 | +} | |
300 | + | |
301 | +int main(int argc, char *argv[]) | |
302 | +{ | |
303 | + unsigned int i; | |
304 | + char buffer[16384]; | |
305 | + struct { | |
306 | + const char *action; | |
307 | + void (*func) (void); | |
308 | + } testcases[] = { | |
309 | + { "execute", test_execute }, | |
310 | + { "read", test_read }, | |
311 | + { "write", test_write }, | |
312 | + { "append", test_append }, | |
313 | + { "create", test_create }, | |
314 | + { "unlink", test_unlink }, | |
315 | + { "getattr", test_getattr }, | |
316 | + { "mkdir", test_mkdir }, | |
317 | + { "rmdir", test_rmdir }, | |
318 | + { "mkfifo", test_mkfifo }, | |
319 | + { "mksock", test_mksock }, | |
320 | + { "truncate", test_truncate }, | |
321 | + { "symlink", test_symlink }, | |
322 | + { "mkblock", test_mkblock }, | |
323 | + { "mkchar", test_mkchar }, | |
324 | + { "link", test_link }, | |
325 | + { "rename", test_rename }, | |
326 | + { "chmod", test_chmod }, | |
327 | + { "chown", test_chown }, | |
328 | + { "chgrp", test_chgrp }, | |
329 | + { "ioctl", test_ioctl }, | |
330 | + { "chroot", test_chroot }, | |
331 | + { "mount", test_mount1 }, | |
332 | + { "mount", test_mount2 }, | |
333 | + { "mount", test_mount3 }, | |
334 | + { "mount", test_mount4 }, | |
335 | + { "unmount", test_unmount }, | |
336 | + { "pivot_root", test_pivot_root }, | |
337 | + /* | |
338 | + acl inet_stream_bind | |
339 | + acl inet_stream_listen | |
340 | + acl inet_stream_connect | |
341 | + acl inet_stream_accept | |
342 | + acl inet_dgram_bind | |
343 | + acl inet_dgram_send | |
344 | + acl inet_dgram_recv | |
345 | + acl inet_raw_bind | |
346 | + acl inet_raw_send | |
347 | + acl inet_raw_recv | |
348 | + acl unix_stream_bind | |
349 | + acl unix_stream_listen | |
350 | + # acl unix_stream_connect | |
351 | + acl unix_stream_accept | |
352 | + acl unix_dgram_bind | |
353 | + acl unix_dgram_send | |
354 | + acl unix_dgram_recv | |
355 | + acl unix_seqpacket_bind | |
356 | + acl unix_seqpacket_listen | |
357 | + acl unix_seqpacket_connect | |
358 | + acl unix_seqpacket_accept | |
359 | + # acl environ | |
360 | + acl ptrace | |
361 | + acl signal | |
362 | + acl modify_policy | |
363 | + # acl use_netlink_socket | |
364 | + acl use_packet_socket | |
365 | + acl use_reboot | |
366 | + acl use_vhangup | |
367 | + acl set_time | |
368 | + acl set_priority | |
369 | + acl set_hostname | |
370 | + acl use_kernel_module | |
371 | + acl use_new_kernel | |
372 | + # acl auto_domain_transition | |
373 | + acl manual_domain_transition | |
374 | + */ | |
375 | + { NULL, NULL }, | |
376 | + }; | |
377 | + int fd_out = open(POLDIR "/policy", O_WRONLY); | |
378 | + char *cp1; | |
379 | + char *cp2; | |
380 | + | |
381 | + reset_policy(); | |
382 | + | |
383 | + memset(buffer, 0, sizeof(buffer)); | |
384 | + if (fd_out == EOF) { | |
385 | + fprintf(stderr, "Can't write " POLDIR "/policy interface.\n"); | |
386 | + goto out; | |
387 | + } | |
388 | + { | |
389 | + int fd = open(POLDIR "/audit", O_RDONLY); | |
390 | + char buffer[4096]; | |
391 | + while (read(fd, buffer, sizeof(buffer)) > 0); | |
392 | + close(fd); | |
393 | + } | |
394 | + cp1 = "POLICY_VERSION=20120401\n" | |
395 | + "quota memory audit 16777216\n" | |
396 | + "quota memory query 1048576\n" | |
397 | + "quota audit[1] allowed=1024 denied=1024 unmatched=1024\n"; | |
398 | + i = strlen(cp1); | |
399 | + if (write(fd_out, cp1, i) != i) { | |
400 | + fprintf(stderr, "Can't write " POLDIR "/policy interface.\n"); | |
401 | + goto out; | |
402 | + } | |
403 | + for (i = 0; testcases[i].action; i++) { | |
404 | + int fd_in = open(POLDIR "/policy", O_RDONLY); | |
405 | + if (fd_in == EOF) { | |
406 | + fprintf(stderr, | |
407 | + "Can't read " POLDIR "/policy interface.\n"); | |
408 | + goto out; | |
409 | + } | |
410 | + snprintf(buffer, sizeof(buffer) - 1, "0 acl %s task.pid=%u\n" | |
411 | + " audit 1\n", testcases[i].action, getpid()); | |
412 | + write(fd_out, buffer, strlen(buffer)); | |
413 | + testcases[i].func(); | |
414 | + cp2 = read_log("result=unmatched", testcases[i].action); | |
415 | + if (!cp2) | |
416 | + goto out; | |
417 | + cp1 = "0 deny "; | |
418 | + write(fd_out, cp1, strlen(cp1)); | |
419 | + if (!strcmp(testcases[i].action, "rmdir")) { | |
420 | + char *cp3 = strstr(cp2, " path.ino="); | |
421 | + char *cp4 = cp3 ? strchr(cp3 + 1, ' ') : NULL; | |
422 | + if (cp4) | |
423 | + memmove(cp3, cp4, strlen(cp4) + 1); | |
424 | + } | |
425 | + write(fd_out, cp2, strlen(cp2)); | |
426 | + if (!check_policy(buffer, cp1, cp2)) | |
427 | + goto out; | |
428 | + testcases[i].func(); | |
429 | + cp2 = read_log("result=denied", testcases[i].action); | |
430 | + if (!cp2) | |
431 | + goto out; | |
432 | + snprintf(buffer, sizeof(buffer) - 1, | |
433 | + "delete 0 acl %s task.pid=%u\n", | |
434 | + testcases[i].action, getpid()); | |
435 | + write(fd_out, buffer, strlen(buffer)); | |
436 | + } | |
437 | + return 0; | |
438 | +out: | |
439 | + return 1; | |
440 | +} |
@@ -0,0 +1,2040 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <stdlib.h> | |
3 | +#include <string.h> | |
4 | +#include <unistd.h> | |
5 | +#include <sys/types.h> | |
6 | +#include <sys/stat.h> | |
7 | +#include <sys/socket.h> | |
8 | +#include <netinet/in.h> | |
9 | +#include <sys/ptrace.h> | |
10 | +#include <sys/wait.h> | |
11 | +#include <linux/ip.h> | |
12 | +#include <fcntl.h> | |
13 | +#include <errno.h> | |
14 | +#include <sys/mount.h> | |
15 | +#ifndef MS_REC | |
16 | +#define MS_REC 16384 | |
17 | +#endif | |
18 | +#ifndef MS_PRIVATE | |
19 | +#define MS_PRIVATE (1 << 18) | |
20 | +#endif | |
21 | + | |
22 | +static FILE *fp = NULL; | |
23 | + | |
24 | +static void set(const char *str) | |
25 | +{ | |
26 | + fprintf(fp, "%s\n", str); | |
27 | + fflush(fp); | |
28 | + errno = 0; | |
29 | +} | |
30 | + | |
31 | +static void unset(const char *str) | |
32 | +{ | |
33 | + fprintf(fp, "delete %s\n", str); | |
34 | + fflush(fp); | |
35 | + errno = 0; | |
36 | +} | |
37 | + | |
38 | +static void unset2(const char *str) | |
39 | +{ | |
40 | + const char *cp = str; | |
41 | + while (*cp) { | |
42 | + if (*cp++ != '\n') | |
43 | + continue; | |
44 | + fprintf(fp, "delete "); | |
45 | + fwrite(str, cp - str, 1, fp); | |
46 | + str = cp; | |
47 | + } | |
48 | + fprintf(fp, "delete %s\n", str); | |
49 | + fflush(fp); | |
50 | + errno = 0; | |
51 | +} | |
52 | + | |
53 | +static void check(const char *prompt, int result) | |
54 | +{ | |
55 | + int err = errno; | |
56 | + printf("%s%s\n", prompt, result ? "Success" : "Failed"); | |
57 | + if (!result) { | |
58 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
59 | + { | |
60 | + int fd2 = open(POLDIR "/self_domain", O_RDONLY); | |
61 | + char c; | |
62 | + fprintf(stderr, "task.domain=\""); | |
63 | + while (read(fd2, &c, 1) == 1) | |
64 | + fprintf(stderr, "%c", c); | |
65 | + close(fd2); | |
66 | + fprintf(stderr, "\"\n"); | |
67 | + } | |
68 | + kill(1, SIGCONT); | |
69 | + exit(1); | |
70 | + } | |
71 | + printf("\n"); | |
72 | + fflush(stdout); | |
73 | +} | |
74 | + | |
75 | +static void check_init(const char *prompt, const char *expected) | |
76 | +{ | |
77 | + int result; | |
78 | + int fd = open(POLDIR "/.process_status", O_RDWR); | |
79 | + char buffer[1024]; | |
80 | + char *cp; | |
81 | + memset(buffer, 0, sizeof(buffer)); | |
82 | + kill(1, SIGHUP); | |
83 | + sleep(1); | |
84 | + write(fd, "1\n", 2); | |
85 | + read(fd, buffer, sizeof(buffer) - 1); | |
86 | + close(fd); | |
87 | + cp = strchr(buffer, ' '); | |
88 | + if (cp++) | |
89 | + memmove(buffer, cp, strlen(cp) + 1); | |
90 | + result = !strcmp(buffer, expected); | |
91 | + printf("%s%s\n", prompt, result ? "Success" : "Failed"); | |
92 | + if (!result) { | |
93 | + fprintf(stderr, "Err: expected='%s' result='%s'\n", | |
94 | + expected, buffer); | |
95 | + kill(1, SIGCONT); | |
96 | + exit(1); | |
97 | + } | |
98 | + printf("\n"); | |
99 | + fflush(stdout); | |
100 | +} | |
101 | + | |
102 | +static void test_task_transition(void) | |
103 | +{ | |
104 | + int fd = open(POLDIR "/self_domain", O_WRONLY); | |
105 | + char *policy; | |
106 | + | |
107 | + policy = "100 acl manual_domain_transition\n" | |
108 | + "0 allow domain=\"domain\\$\"\n"; | |
109 | + set(policy); | |
110 | + check(policy, write(fd, "domain0", 7) != EOF); | |
111 | + check(policy, write(fd, "domain10", 8) != EOF); | |
112 | + check(policy, write(fd, "domainXYX", 9) == EOF); | |
113 | + check(policy, write(fd, "domain200", 9) != EOF); | |
114 | + unset(policy); | |
115 | + | |
116 | + policy = "100 acl auto_domain_transition\n" | |
117 | + "0 allow task.pid=1 transition=\"<init3>\"\n"; | |
118 | + set(policy); | |
119 | + check_init(policy, "<init3>"); | |
120 | + unset(policy); | |
121 | + | |
122 | + policy = "100 acl auto_domain_transition\n" | |
123 | + "0 allow task.pid=1 task.uid!=0 transition=\"<init2>\"\n"; | |
124 | + set(policy); | |
125 | + check_init(policy, "<init3>"); | |
126 | + unset(policy); | |
127 | + | |
128 | + policy = "100 acl auto_domain_transition\n" | |
129 | + "0 allow task.pid=1 transition=\"<init>\"\n"; | |
130 | + set(policy); | |
131 | + check_init(policy, "<init>"); | |
132 | + unset(policy); | |
133 | + | |
134 | + close(fd); | |
135 | +} | |
136 | + | |
137 | +static void test_file_read(void) | |
138 | +{ | |
139 | + int fd; | |
140 | + char *policy; | |
141 | + | |
142 | + policy = "100 acl read\n"; | |
143 | + set(policy); | |
144 | + fd = open("/dev/null", O_RDONLY); | |
145 | + check(policy, fd != EOF); | |
146 | + close(fd); | |
147 | + unset(policy); | |
148 | + | |
149 | + policy = "100 acl read\n" | |
150 | + "0 allow\n" | |
151 | + "1 deny\n"; | |
152 | + set(policy); | |
153 | + fd = open("/dev/null", O_RDONLY); | |
154 | + check(policy, fd != EOF); | |
155 | + close(fd); | |
156 | + unset(policy); | |
157 | + | |
158 | + policy = "100 acl read\n" | |
159 | + "0 deny\n" | |
160 | + "1 allow\n"; | |
161 | + set(policy); | |
162 | + fd = open("/dev/null", O_RDONLY); | |
163 | + check(policy, fd == EOF); | |
164 | + close(fd); | |
165 | + unset(policy); | |
166 | + | |
167 | + policy = "100 acl read path=\"/dev/null\"\n" | |
168 | + "0 allow\n" | |
169 | + "1 deny\n"; | |
170 | + set(policy); | |
171 | + fd = open("/dev/null", O_RDONLY); | |
172 | + check(policy, fd != EOF); | |
173 | + close(fd); | |
174 | + unset(policy); | |
175 | + | |
176 | + policy = "100 acl read path=\"/dev/null\"\n" | |
177 | + "0 deny\n" | |
178 | + "1 allow\n"; | |
179 | + set(policy); | |
180 | + fd = open("/dev/null", O_RDONLY); | |
181 | + check(policy, fd == EOF); | |
182 | + close(fd); | |
183 | + unset(policy); | |
184 | + | |
185 | + policy = "100 acl read\n" | |
186 | + "0 allow path=\"/dev/null\"\n" | |
187 | + "1 deny\n"; | |
188 | + set(policy); | |
189 | + fd = open("/dev/null", O_RDONLY); | |
190 | + check(policy, fd != EOF); | |
191 | + close(fd); | |
192 | + unset(policy); | |
193 | + | |
194 | + policy = "100 acl read\n" | |
195 | + "0 deny path=\"/dev/null\"\n" | |
196 | + "1 allow\n"; | |
197 | + set(policy); | |
198 | + fd = open("/dev/null", O_RDONLY); | |
199 | + check(policy, fd == EOF); | |
200 | + close(fd); | |
201 | + unset(policy); | |
202 | + | |
203 | + policy = "100 acl read\n" | |
204 | + "0 allow path.type=char path.dev_major=1 path.dev_minor=3\n" | |
205 | + "1 deny\n"; | |
206 | + set(policy); | |
207 | + fd = open("/dev/null", O_RDONLY); | |
208 | + check(policy, fd != EOF); | |
209 | + close(fd); | |
210 | + unset(policy); | |
211 | + | |
212 | + policy = "100 acl read\n" | |
213 | + "0 deny path.type=char path.dev_major=1 path.dev_minor=3\n" | |
214 | + "1 allow\n"; | |
215 | + set(policy); | |
216 | + fd = open("/dev/null", O_RDONLY); | |
217 | + check(policy, fd == EOF); | |
218 | + close(fd); | |
219 | + unset(policy); | |
220 | + | |
221 | + policy = "100 acl read\n" | |
222 | + "0 allow path.type=char path.dev_major=1 path.dev_minor!=3\n" | |
223 | + "1 deny\n"; | |
224 | + set(policy); | |
225 | + fd = open("/dev/null", O_RDONLY); | |
226 | + check(policy, fd == EOF); | |
227 | + close(fd); | |
228 | + unset(policy); | |
229 | + | |
230 | + policy = "100 acl read\n" | |
231 | + "0 deny path.type=char path.dev_major=1 path.dev_minor!=3\n" | |
232 | + "1 allow\n"; | |
233 | + set(policy); | |
234 | + fd = open("/dev/null", O_RDONLY); | |
235 | + check(policy, fd != EOF); | |
236 | + close(fd); | |
237 | + unset(policy); | |
238 | + | |
239 | + policy = "string_group GROUP1 /dev/null\n" | |
240 | + "100 acl read\n" | |
241 | + "0 allow path=@GROUP1\n" | |
242 | + "1 deny\n"; | |
243 | + set(policy); | |
244 | + fd = open("/dev/null", O_RDONLY); | |
245 | + check(policy, fd != EOF); | |
246 | + close(fd); | |
247 | + unset2(policy); | |
248 | + | |
249 | + policy = "string_group GROUP1 /dev/null\n" | |
250 | + "100 acl read\n" | |
251 | + "0 deny path=@GROUP1\n" | |
252 | + "1 allow\n"; | |
253 | + set(policy); | |
254 | + fd = open("/dev/null", O_RDONLY); | |
255 | + check(policy, fd == EOF); | |
256 | + close(fd); | |
257 | + unset2(policy); | |
258 | + | |
259 | + policy = "string_group GROUP1 /dev/null\n" | |
260 | + "100 acl read\n" | |
261 | + "0 allow path!=@GROUP1\n" | |
262 | + "1 deny\n"; | |
263 | + set(policy); | |
264 | + fd = open("/dev/null", O_RDONLY); | |
265 | + check(policy, fd == EOF); | |
266 | + close(fd); | |
267 | + unset2(policy); | |
268 | + | |
269 | + policy = "string_group GROUP1 /dev/null\n" | |
270 | + "100 acl read\n" | |
271 | + "0 deny path!=@GROUP1\n" | |
272 | + "1 allow\n"; | |
273 | + set(policy); | |
274 | + fd = open("/dev/null", O_RDONLY); | |
275 | + check(policy, fd != EOF); | |
276 | + close(fd); | |
277 | + unset2(policy); | |
278 | + | |
279 | + policy = "string_group GROUP1 /dev/null\n" | |
280 | + "number_group MAJOR 1\n" | |
281 | + "number_group MINOR 3\n" | |
282 | + "100 acl read\n" | |
283 | + "0 allow path=@GROUP1 path.dev_major=@MAJOR" | |
284 | + " path.dev_minor=@MINOR\n" | |
285 | + "1 deny\n"; | |
286 | + set(policy); | |
287 | + fd = open("/dev/null", O_RDONLY); | |
288 | + check(policy, fd != EOF); | |
289 | + close(fd); | |
290 | + unset2(policy); | |
291 | + | |
292 | + policy = "string_group GROUP1 /dev/null\n" | |
293 | + "number_group MAJOR 1\n" | |
294 | + "number_group MINOR 3\n" | |
295 | + "100 acl read\n" | |
296 | + "0 deny path=@GROUP1 path.dev_major=@MAJOR" | |
297 | + " path.dev_minor=@MINOR\n" | |
298 | + "1 allow\n"; | |
299 | + set(policy); | |
300 | + fd = open("/dev/null", O_RDONLY); | |
301 | + check(policy, fd == EOF); | |
302 | + close(fd); | |
303 | + unset2(policy); | |
304 | + | |
305 | + policy = "string_group GROUP1 /dev/zero\n" | |
306 | + "string_group GROUP1 /dev/null\n" | |
307 | + "string_group GROUP1 /dev/urandom\n" | |
308 | + "number_group MAJOR 0\n" | |
309 | + "number_group MAJOR 2-255\n" | |
310 | + "number_group MINOR 00-0x2\n" | |
311 | + "number_group MINOR 255\n" | |
312 | + "100 acl read\n" | |
313 | + "0 allow path=@GROUP1 path.dev_major=@MAJOR" | |
314 | + " path.dev_minor=@MINOR\n" | |
315 | + "1 deny\n"; | |
316 | + set(policy); | |
317 | + fd = open("/dev/null", O_RDONLY); | |
318 | + check(policy, fd == EOF); | |
319 | + close(fd); | |
320 | + unset2(policy); | |
321 | + | |
322 | + policy = "string_group GROUP1 /dev/zero\n" | |
323 | + "string_group GROUP1 /dev/null\n" | |
324 | + "string_group GROUP1 /dev/urandom\n" | |
325 | + "number_group MAJOR 0\n" | |
326 | + "number_group MAJOR 2-255\n" | |
327 | + "number_group MINOR 00-0x2\n" | |
328 | + "number_group MINOR 255\n" | |
329 | + "100 acl read\n" | |
330 | + "0 allow path=@GROUP1 path.dev_major!=@MAJOR" | |
331 | + " path.dev_minor!=@MINOR\n" | |
332 | + "1 deny\n"; | |
333 | + set(policy); | |
334 | + fd = open("/dev/null", O_RDONLY); | |
335 | + check(policy, fd != EOF); | |
336 | + close(fd); | |
337 | + unset2(policy); | |
338 | +} | |
339 | + | |
340 | +static void test_file_write(void) | |
341 | +{ | |
342 | + int fd; | |
343 | + char *policy; | |
344 | + | |
345 | + policy = "100 acl write\n" | |
346 | + "0 allow\n" | |
347 | + "100 acl append\n" | |
348 | + "0 deny\n"; | |
349 | + set(policy); | |
350 | + fd = open("/dev/null", O_WRONLY); | |
351 | + check(policy, fd != EOF); | |
352 | + close(fd); | |
353 | + unset2(policy); | |
354 | + | |
355 | + policy = "100 acl write\n" | |
356 | + "0 deny\n" | |
357 | + "100 acl append\n" | |
358 | + "0 allow\n"; | |
359 | + set(policy); | |
360 | + fd = open("/dev/null", O_WRONLY); | |
361 | + check(policy, fd == EOF); | |
362 | + close(fd); | |
363 | + unset2(policy); | |
364 | + | |
365 | + policy = "100 acl write\n" | |
366 | + "0 allow\n" | |
367 | + "100 acl append\n" | |
368 | + "0 deny\n"; | |
369 | + set(policy); | |
370 | + fd = open("/dev/null", O_WRONLY | O_APPEND); | |
371 | + check(policy, fd == EOF); | |
372 | + close(fd); | |
373 | + unset2(policy); | |
374 | + | |
375 | + policy = "100 acl write\n" | |
376 | + "0 deny\n" | |
377 | + "100 acl append\n" | |
378 | + "0 append\n"; | |
379 | + set(policy); | |
380 | + fd = open("/dev/null", O_WRONLY | O_APPEND); | |
381 | + check(policy, fd != EOF); | |
382 | + close(fd); | |
383 | + unset2(policy); | |
384 | + | |
385 | + policy = "100 acl write\n" | |
386 | + "0 allow path.type=char path.dev_major=1 path.dev_minor=3\n" | |
387 | + "1 deny\n"; | |
388 | + set(policy); | |
389 | + fd = open("/dev/null", O_WRONLY | O_TRUNC); | |
390 | + check(policy, fd != EOF); | |
391 | + close(fd); | |
392 | + unset(policy); | |
393 | + | |
394 | + policy = "100 acl write\n" | |
395 | + "0 allow path.type=char path.dev_major=1" | |
396 | + " path.dev_minor=@MINOR\n" | |
397 | + "1 deny\n"; | |
398 | + set(policy); | |
399 | + fd = open("/dev/null", O_WRONLY | O_TRUNC); | |
400 | + check(policy, fd == EOF); | |
401 | + close(fd); | |
402 | + unset(policy); | |
403 | + | |
404 | + policy = "100 acl write\n" | |
405 | + "0 allow path.parent.uid=0 path.parent.perm=0755\n" | |
406 | + "1 deny\n"; | |
407 | + set(policy); | |
408 | + fd = open("/dev/null", O_WRONLY); | |
409 | + check(policy, fd != EOF); | |
410 | + close(fd); | |
411 | + unset(policy); | |
412 | + | |
413 | + policy = "100 acl write\n" | |
414 | + "0 allow path.parent.uid=task.uid path.parent.gid=task.gid\n" | |
415 | + "1 deny\n"; | |
416 | + set(policy); | |
417 | + fd = open("/dev/null", O_WRONLY); | |
418 | + check(policy, fd != EOF); | |
419 | + close(fd); | |
420 | + unset(policy); | |
421 | + | |
422 | + policy = "100 acl write\n" | |
423 | + "0 allow task.uid=path.parent.uid task.gid=path.parent.gid\n" | |
424 | + "1 deny\n"; | |
425 | + set(policy); | |
426 | + fd = open("/dev/null", O_WRONLY); | |
427 | + check(policy, fd != EOF); | |
428 | + close(fd); | |
429 | + unset(policy); | |
430 | +} | |
431 | + | |
432 | +static void test_file_create(void) | |
433 | +{ | |
434 | + int fd; | |
435 | + char *policy; | |
436 | + | |
437 | + policy = "100 acl create\n" | |
438 | + "0 allow path.uid=0\n" | |
439 | + "1 deny\n"; | |
440 | + set(policy); | |
441 | + unlink("/tmp/file"); | |
442 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
443 | + check(policy, fd == EOF); | |
444 | + close(fd); | |
445 | + unset(policy); | |
446 | + | |
447 | + policy = "100 acl create\n" | |
448 | + "0 allow path=\"/tmp/file\" path.parent.uid=0\n" | |
449 | + "1 deny\n"; | |
450 | + set(policy); | |
451 | + unlink("/tmp/file"); | |
452 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
453 | + check(policy, fd != EOF); | |
454 | + close(fd); | |
455 | + unset(policy); | |
456 | + | |
457 | + policy = "number_group GROUP1 1-0xFFFFFFFF\n" | |
458 | + "100 acl create\n" | |
459 | + "0 allow path.parent.uid!=@GROUP1 perm=0600\n" | |
460 | + "1 deny\n"; | |
461 | + set(policy); | |
462 | + unlink("/tmp/file"); | |
463 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
464 | + check(policy, fd != EOF); | |
465 | + close(fd); | |
466 | + unset2(policy); | |
467 | + | |
468 | + policy = "number_group GROUP1 1-0xFFFFFFFF\n" | |
469 | + "100 acl create\n" | |
470 | + "0 allow path.parent.uid!=@GROUP1 perm!=0600\n" | |
471 | + "1 deny\n"; | |
472 | + set(policy); | |
473 | + unlink("/tmp/file"); | |
474 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
475 | + check(policy, fd == EOF); | |
476 | + close(fd); | |
477 | + unset2(policy); | |
478 | + | |
479 | + policy = "100 acl create\n" | |
480 | + "0 allow path.parent.uid=task.uid\n" | |
481 | + "1 deny\n"; | |
482 | + set(policy); | |
483 | + unlink("/tmp/file"); | |
484 | + fd = open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600); | |
485 | + check(policy, fd != EOF); | |
486 | + close(fd); | |
487 | + unset(policy); | |
488 | +} | |
489 | + | |
490 | +static void test_file_unlink(void) | |
491 | +{ | |
492 | + char *policy; | |
493 | + | |
494 | + policy = "100 acl unlink\n" | |
495 | + "0 allow path.uid=0 path.uid=path.parent.uid\n" | |
496 | + "1 deny\n"; | |
497 | + set(policy); | |
498 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
499 | + check(policy, unlink("/tmp/file") == 0); | |
500 | + unset(policy); | |
501 | + | |
502 | + policy = "100 acl unlink\n" | |
503 | + "0 deny path.uid=0 path.uid=path.parent.uid\n" | |
504 | + "1 allow\n"; | |
505 | + set(policy); | |
506 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
507 | + check(policy, unlink("/tmp/file") == EOF); | |
508 | + unset(policy); | |
509 | +} | |
510 | + | |
511 | +static void test_file_link(void) | |
512 | +{ | |
513 | + char *policy; | |
514 | + | |
515 | + policy = "100 acl link\n" | |
516 | + "0 allow old_path.uid=0 old_path.uid=old_path.parent.uid" | |
517 | + " old_path.parent.ino=new_path.parent.ino\n" | |
518 | + "1 deny\n"; | |
519 | + set(policy); | |
520 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
521 | + unlink("/tmp/file2"); | |
522 | + check(policy, link("/tmp/file", "/tmp/file2") == 0); | |
523 | + unset(policy); | |
524 | + | |
525 | + policy = "100 acl link\n" | |
526 | + "0 deny old_path.uid=0 old_path.uid=old_path.parent.uid\n" | |
527 | + "1 allow\n"; | |
528 | + set(policy); | |
529 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
530 | + unlink("/tmp/file2"); | |
531 | + check(policy, link("/tmp/file", "/tmp/file2") == EOF); | |
532 | + unset(policy); | |
533 | +} | |
534 | + | |
535 | +static void test_file_rename(void) | |
536 | +{ | |
537 | + char *policy; | |
538 | + | |
539 | + policy = "100 acl rename\n" | |
540 | + "0 allow old_path.uid=0 old_path.uid=old_path.parent.uid" | |
541 | + " old_path.parent.ino=new_path.parent.ino\n" | |
542 | + "1 deny\n"; | |
543 | + set(policy); | |
544 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
545 | + unlink("/tmp/file2"); | |
546 | + check(policy, rename("/tmp/file", "/tmp/file2") == 0); | |
547 | + unset(policy); | |
548 | + | |
549 | + policy = "100 acl rename\n" | |
550 | + "0 deny old_path.uid=0 old_path.uid=old_path.parent.uid\n" | |
551 | + "1 allow\n"; | |
552 | + set(policy); | |
553 | + close(open("/tmp/file", O_CREAT | O_WRONLY | O_EXCL, 0600)); | |
554 | + unlink("/tmp/file2"); | |
555 | + check(policy, rename("/tmp/file", "/tmp/file2") == EOF); | |
556 | + unset(policy); | |
557 | +} | |
558 | + | |
559 | +static void test_network_inet_stream(void) | |
560 | +{ | |
561 | + struct sockaddr_in addr1 = { }; | |
562 | + struct sockaddr_in addr2 = { }; | |
563 | + socklen_t size = sizeof(addr1); | |
564 | + int fd1; | |
565 | + int fd2; | |
566 | + int fd3; | |
567 | + char *policy; | |
568 | + char buffer[1024]; | |
569 | + memset(buffer, 0, sizeof(buffer)); | |
570 | + | |
571 | + fd1 = socket(PF_INET, SOCK_STREAM, 0); | |
572 | + fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
573 | + addr1.sin_family = AF_INET; | |
574 | + addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
575 | + | |
576 | + policy = "100 acl inet_stream_bind\n" | |
577 | + "0 allow ip=127.0.0.1 port!=0\n" | |
578 | + "1 deny\n"; | |
579 | + set(policy); | |
580 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
581 | + EOF); | |
582 | + unset(policy); | |
583 | + | |
584 | + policy = "100 acl inet_stream_bind\n" | |
585 | + "0 allow ip!=127.0.0.1 port=0\n" | |
586 | + "1 deny\n"; | |
587 | + set(policy); | |
588 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
589 | + EOF); | |
590 | + unset(policy); | |
591 | + | |
592 | + policy = "100 acl inet_stream_bind\n" | |
593 | + "0 allow ip=127.0.0.1 port=0 path.uid=task.uid\n" | |
594 | + "1 deny\n"; | |
595 | + set(policy); | |
596 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
597 | + EOF); | |
598 | + unset(policy); | |
599 | + | |
600 | + policy = "100 acl inet_stream_bind\n" | |
601 | + "0 allow ip=127.0.0.1 port=0\n" | |
602 | + "1 deny\n"; | |
603 | + set(policy); | |
604 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
605 | + 0); | |
606 | + unset(policy); | |
607 | + | |
608 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
609 | + | |
610 | + snprintf(buffer, sizeof(buffer) - 1, | |
611 | + "100 acl inet_stream_listen\n" | |
612 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
613 | + "1 deny\n", ntohs(addr1.sin_port)); | |
614 | + policy = buffer; | |
615 | + set(policy); | |
616 | + check(policy, listen(fd1, 5) == EOF); | |
617 | + unset(policy); | |
618 | + | |
619 | + snprintf(buffer, sizeof(buffer) - 1, | |
620 | + "100 acl inet_stream_listen\n" | |
621 | + "0 allow ip=127.0.0.1 port=%u\n" | |
622 | + "1 deny\n", ntohs(addr1.sin_port)); | |
623 | + policy = buffer; | |
624 | + set(policy); | |
625 | + check(policy, listen(fd1, 5) == 0); | |
626 | + unset(policy); | |
627 | + | |
628 | + snprintf(buffer, sizeof(buffer) - 1, | |
629 | + "100 acl inet_stream_connect\n" | |
630 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
631 | + "1 deny\n", ntohs(addr1.sin_port)); | |
632 | + policy = buffer; | |
633 | + set(policy); | |
634 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
635 | + == EOF); | |
636 | + unset(policy); | |
637 | + | |
638 | + snprintf(buffer, sizeof(buffer) - 1, | |
639 | + "100 acl inet_stream_connect\n" | |
640 | + "0 allow ip=127.0.0.1 port=%u\n" | |
641 | + "1 deny\n", ntohs(addr1.sin_port)); | |
642 | + policy = buffer; | |
643 | + set(policy); | |
644 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
645 | + == 0); | |
646 | + unset(policy); | |
647 | + | |
648 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
649 | + | |
650 | + snprintf(buffer, sizeof(buffer) - 1, | |
651 | + "100 acl inet_stream_accept\n" | |
652 | + "0 allow ip=127.0.0.1 port=%u\n" | |
653 | + "1 deny\n", ntohs(addr2.sin_port)); | |
654 | + policy = buffer; | |
655 | + set(policy); | |
656 | + fd3 = accept(fd1, NULL, 0); | |
657 | + check(policy, fd3 != EOF); | |
658 | + close(fd3); | |
659 | + unset(policy); | |
660 | + | |
661 | + snprintf(buffer, sizeof(buffer) - 1, | |
662 | + "100 acl inet_stream_connect\n" | |
663 | + "0 allow ip=127.0.0.1 port=%u\n" | |
664 | + "1 deny\n", ntohs(addr1.sin_port)); | |
665 | + policy = buffer; | |
666 | + set(policy); | |
667 | + close(fd2); | |
668 | + fd2 = socket(PF_INET, SOCK_STREAM, 0); | |
669 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
670 | + == 0); | |
671 | + unset(policy); | |
672 | + | |
673 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
674 | + | |
675 | + snprintf(buffer, sizeof(buffer) - 1, | |
676 | + "100 acl inet_stream_accept\n" | |
677 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
678 | + "1 deny\n", ntohs(addr2.sin_port)); | |
679 | + policy = buffer; | |
680 | + set(policy); | |
681 | + fd3 = accept(fd1, NULL, 0); | |
682 | + check(policy, fd3 == EOF); | |
683 | + close(fd3); | |
684 | + unset(policy); | |
685 | + | |
686 | + close(fd1); | |
687 | + close(fd2); | |
688 | +} | |
689 | + | |
690 | +static void test_network_inet_dgram(void) | |
691 | +{ | |
692 | + struct sockaddr_in addr1 = { }; | |
693 | + struct sockaddr_in addr2 = { }; | |
694 | + socklen_t size = sizeof(addr1); | |
695 | + int fd1; | |
696 | + int fd2; | |
697 | + char c; | |
698 | + char *policy; | |
699 | + char buffer[1024]; | |
700 | + memset(buffer, 0, sizeof(buffer)); | |
701 | + | |
702 | + fd1 = socket(PF_INET, SOCK_DGRAM, 0); | |
703 | + fd2 = socket(PF_INET, SOCK_DGRAM, 0); | |
704 | + addr1.sin_family = AF_INET; | |
705 | + addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
706 | + | |
707 | + policy = "100 acl inet_dgram_bind\n" | |
708 | + "0 allow ip=127.0.0.1 port!=0\n" | |
709 | + "1 deny\n"; | |
710 | + set(policy); | |
711 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
712 | + EOF); | |
713 | + unset(policy); | |
714 | + | |
715 | + policy = "100 acl inet_dgram_bind\n" | |
716 | + "0 allow ip!=127.0.0.1 port=0\n" | |
717 | + "1 deny\n"; | |
718 | + set(policy); | |
719 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
720 | + EOF); | |
721 | + unset(policy); | |
722 | + | |
723 | + policy = "100 acl inet_dgram_bind\n" | |
724 | + "0 allow ip=127.0.0.1 port=0 path.uid=task.uid\n" | |
725 | + "1 deny\n"; | |
726 | + set(policy); | |
727 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
728 | + EOF); | |
729 | + unset(policy); | |
730 | + | |
731 | + policy = "100 acl inet_dgram_bind\n" | |
732 | + "0 allow ip=127.0.0.1 port=0\n" | |
733 | + "1 deny\n"; | |
734 | + set(policy); | |
735 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
736 | + 0); | |
737 | + unset(policy); | |
738 | + | |
739 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
740 | + | |
741 | + snprintf(buffer, sizeof(buffer) - 1, | |
742 | + "100 acl inet_dgram_send\n" | |
743 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
744 | + "1 deny\n", ntohs(addr1.sin_port)); | |
745 | + policy = buffer; | |
746 | + set(policy); | |
747 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
748 | + == EOF); | |
749 | + unset(policy); | |
750 | + | |
751 | + snprintf(buffer, sizeof(buffer) - 1, | |
752 | + "100 acl inet_dgram_send\n" | |
753 | + "0 allow ip=127.0.0.1 port=%u\n" | |
754 | + "1 deny\n", ntohs(addr1.sin_port)); | |
755 | + policy = buffer; | |
756 | + set(policy); | |
757 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
758 | + == 0); | |
759 | + unset(policy); | |
760 | + | |
761 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
762 | + | |
763 | + snprintf(buffer, sizeof(buffer) - 1, | |
764 | + "100 acl inet_dgram_send\n" | |
765 | + "0 allow ip=127.0.0.1 port=%u\n" | |
766 | + "1 deny\n", ntohs(addr1.sin_port)); | |
767 | + policy = buffer; | |
768 | + set(policy); | |
769 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
770 | + unset(policy); | |
771 | + | |
772 | + snprintf(buffer, sizeof(buffer) - 1, | |
773 | + "100 acl inet_dgram_recv\n" | |
774 | + "0 allow ip=127.0.0.1 port=%u\n" | |
775 | + "1 deny\n", ntohs(addr2.sin_port)); | |
776 | + policy = buffer; | |
777 | + set(policy); | |
778 | + check(policy, recv(fd1, &c, 1, 0) != EOF); | |
779 | + unset(policy); | |
780 | + | |
781 | + snprintf(buffer, sizeof(buffer) - 1, | |
782 | + "100 acl inet_dgram_send\n" | |
783 | + "0 allow ip=127.0.0.1 port=%u\n" | |
784 | + "1 deny\n", ntohs(addr1.sin_port)); | |
785 | + policy = buffer; | |
786 | + set(policy); | |
787 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
788 | + unset(policy); | |
789 | + | |
790 | + snprintf(buffer, sizeof(buffer) - 1, | |
791 | + "100 acl inet_dgram_recv\n" | |
792 | + "0 allow ip=127.0.0.1 port!=%u\n" | |
793 | + "1 deny\n", ntohs(addr2.sin_port)); | |
794 | + policy = buffer; | |
795 | + set(policy); | |
796 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
797 | + unset(policy); | |
798 | + | |
799 | + snprintf(buffer, sizeof(buffer) - 1, | |
800 | + "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
801 | + "100 acl inet_dgram_send\n" | |
802 | + "0 allow ip=@LOCALHOST port=%u\n" | |
803 | + "1 deny\n", ntohs(addr1.sin_port)); | |
804 | + policy = buffer; | |
805 | + set(policy); | |
806 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
807 | + unset2(policy); | |
808 | + | |
809 | + snprintf(buffer, sizeof(buffer) - 1, | |
810 | + "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
811 | + "100 acl inet_dgram_recv\n" | |
812 | + "0 allow ip!=@LOCALHOST port=%u\n" | |
813 | + "1 deny\n", ntohs(addr2.sin_port)); | |
814 | + policy = buffer; | |
815 | + set(policy); | |
816 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
817 | + unset2(policy); | |
818 | + | |
819 | + close(fd1); | |
820 | + close(fd2); | |
821 | +} | |
822 | + | |
823 | +static void test_network_inet_raw(void) | |
824 | +{ | |
825 | + struct sockaddr_in addr = { }; | |
826 | + static struct iphdr ip = { }; | |
827 | + int fd1; | |
828 | + int fd2; | |
829 | + char *policy; | |
830 | + fd1 = socket(PF_INET, SOCK_RAW, 1); | |
831 | + fd2 = socket(PF_INET, SOCK_RAW, 1); | |
832 | + addr.sin_family = AF_INET; | |
833 | + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
834 | + ip.version = 4; | |
835 | + ip.ihl = sizeof(struct iphdr) / 4; | |
836 | + ip.protocol = IPPROTO_RAW; | |
837 | + ip.daddr = htonl(INADDR_LOOPBACK); | |
838 | + ip.saddr = ip.daddr; | |
839 | + | |
840 | + policy = "100 acl inet_raw_bind\n" | |
841 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
842 | + "1 deny\n"; | |
843 | + set(policy); | |
844 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
845 | + EOF); | |
846 | + unset(policy); | |
847 | + | |
848 | + policy = "100 acl inet_raw_bind\n" | |
849 | + "0 allow ip!=127.0.0.1 proto=1\n" | |
850 | + "1 deny\n"; | |
851 | + set(policy); | |
852 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
853 | + EOF); | |
854 | + unset(policy); | |
855 | + | |
856 | + policy = "100 acl inet_raw_bind\n" | |
857 | + "0 allow ip=127.0.0.1 proto=1 path.uid=task.uid\n" | |
858 | + "1 deny\n"; | |
859 | + set(policy); | |
860 | + check(policy, bind(fd1, (struct sockaddr *) &addr, sizeof(addr)) == | |
861 | + EOF); | |
862 | + unset(policy); | |
863 | + | |
864 | + policy = "100 acl inet_raw_bind\n" | |
865 | + "0 allow ip=127.0.0.1 proto=1\n" | |
866 | + "1 deny\n"; | |
867 | + set(policy); | |
868 | + check(policy, bind(fd2, (struct sockaddr *) &addr, sizeof(addr)) == | |
869 | + 0); | |
870 | + unset(policy); | |
871 | + | |
872 | + policy = "100 acl inet_raw_send\n" | |
873 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
874 | + "1 deny\n"; | |
875 | + set(policy); | |
876 | + check(policy, connect(fd2, (struct sockaddr *) &addr, sizeof(addr)) | |
877 | + == EOF); | |
878 | + unset(policy); | |
879 | + | |
880 | + policy = "100 acl inet_raw_send\n" | |
881 | + "0 allow ip=127.0.0.1 proto=1\n" | |
882 | + "1 deny\n"; | |
883 | + set(policy); | |
884 | + check(policy, connect(fd2, (struct sockaddr *) &addr, sizeof(addr)) | |
885 | + == 0); | |
886 | + unset(policy); | |
887 | + | |
888 | + policy = "100 acl inet_raw_send\n" | |
889 | + "0 allow ip=127.0.0.1 proto=1\n" | |
890 | + "1 deny\n"; | |
891 | + set(policy); | |
892 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
893 | + unset(policy); | |
894 | + | |
895 | + policy = "100 acl inet_raw_recv\n" | |
896 | + "0 allow ip=127.0.0.1 proto=1\n" | |
897 | + "1 deny\n"; | |
898 | + set(policy); | |
899 | + check(policy, recv(fd1, &ip, sizeof(ip), MSG_DONTWAIT) != EOF); | |
900 | + unset(policy); | |
901 | + | |
902 | + policy = "100 acl inet_raw_send\n" | |
903 | + "0 allow ip=127.0.0.1 proto=1\n" | |
904 | + "1 deny\n"; | |
905 | + set(policy); | |
906 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
907 | + unset(policy); | |
908 | + | |
909 | + policy = "100 acl inet_raw_recv\n" | |
910 | + "0 allow ip=127.0.0.1 proto!=1\n" | |
911 | + "1 deny\n"; | |
912 | + set(policy); | |
913 | + check(policy, recv(fd1, &ip, sizeof(ip), MSG_DONTWAIT) == EOF); | |
914 | + unset(policy); | |
915 | + | |
916 | + policy = "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
917 | + "100 acl inet_raw_send\n" | |
918 | + "0 allow ip=@LOCALHOST proto=1\n" | |
919 | + "1 deny\n"; | |
920 | + set(policy); | |
921 | + check(policy, send(fd2, &ip, sizeof(ip), 0) != EOF); | |
922 | + unset2(policy); | |
923 | + | |
924 | + policy = "ip_group LOCALHOST 127.0.0.0-127.255.255.255\n" | |
925 | + "100 acl inet_raw_recv\n" | |
926 | + "0 allow ip!=@LOCALHOST proto=1\n" | |
927 | + "1 deny\n"; | |
928 | + set(policy); | |
929 | + check(policy, recv(fd1, &ip, sizeof(ip), MSG_DONTWAIT) == EOF); | |
930 | + unset2(policy); | |
931 | + | |
932 | + close(fd1); | |
933 | + close(fd2); | |
934 | +} | |
935 | + | |
936 | +static void test_network_inet6_stream(void) | |
937 | +{ | |
938 | + struct sockaddr_in6 addr1 = { }; | |
939 | + struct sockaddr_in6 addr2 = { }; | |
940 | + socklen_t size = sizeof(addr1); | |
941 | + int fd1; | |
942 | + int fd2; | |
943 | + int fd3; | |
944 | + char *policy; | |
945 | + char buffer[1024]; | |
946 | + memset(buffer, 0, sizeof(buffer)); | |
947 | + | |
948 | + fd1 = socket(PF_INET6, SOCK_STREAM, 0); | |
949 | + fd2 = socket(PF_INET6, SOCK_STREAM, 0); | |
950 | + addr1.sin6_family = AF_INET6; | |
951 | + addr1.sin6_addr = in6addr_loopback; | |
952 | + | |
953 | + policy = "100 acl inet_stream_bind\n" | |
954 | + "0 allow ip=::1 port!=0\n" | |
955 | + "1 deny\n"; | |
956 | + set(policy); | |
957 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
958 | + EOF); | |
959 | + unset(policy); | |
960 | + | |
961 | + policy = "100 acl inet_stream_bind\n" | |
962 | + "0 allow ip!=::1 port=0\n" | |
963 | + "1 deny\n"; | |
964 | + set(policy); | |
965 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
966 | + EOF); | |
967 | + unset(policy); | |
968 | + | |
969 | + policy = "100 acl inet_stream_bind\n" | |
970 | + "0 allow ip=::1 port=0 path.uid=task.uid\n" | |
971 | + "1 deny\n"; | |
972 | + set(policy); | |
973 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
974 | + EOF); | |
975 | + unset(policy); | |
976 | + | |
977 | + policy = "100 acl inet_stream_bind\n" | |
978 | + "0 allow ip=::1 port=0\n" | |
979 | + "1 deny\n"; | |
980 | + set(policy); | |
981 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
982 | + 0); | |
983 | + unset(policy); | |
984 | + | |
985 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
986 | + | |
987 | + snprintf(buffer, sizeof(buffer) - 1, | |
988 | + "100 acl inet_stream_listen\n" | |
989 | + "0 allow ip=::1 port!=%u\n" | |
990 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
991 | + policy = buffer; | |
992 | + set(policy); | |
993 | + check(policy, listen(fd1, 5) == EOF); | |
994 | + unset(policy); | |
995 | + | |
996 | + snprintf(buffer, sizeof(buffer) - 1, | |
997 | + "100 acl inet_stream_listen\n" | |
998 | + "0 allow ip=::1 port=%u\n" | |
999 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1000 | + policy = buffer; | |
1001 | + set(policy); | |
1002 | + check(policy, listen(fd1, 5) == 0); | |
1003 | + unset(policy); | |
1004 | + | |
1005 | + snprintf(buffer, sizeof(buffer) - 1, | |
1006 | + "100 acl inet_stream_connect\n" | |
1007 | + "0 allow ip=::1 port!=%u\n" | |
1008 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1009 | + policy = buffer; | |
1010 | + set(policy); | |
1011 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1012 | + == EOF); | |
1013 | + unset(policy); | |
1014 | + | |
1015 | + snprintf(buffer, sizeof(buffer) - 1, | |
1016 | + "100 acl inet_stream_connect\n" | |
1017 | + "0 allow ip=::1 port=%u\n" | |
1018 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1019 | + policy = buffer; | |
1020 | + set(policy); | |
1021 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1022 | + == 0); | |
1023 | + unset(policy); | |
1024 | + | |
1025 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1026 | + | |
1027 | + snprintf(buffer, sizeof(buffer) - 1, | |
1028 | + "100 acl inet_stream_accept\n" | |
1029 | + "0 allow ip=::1 port=%u\n" | |
1030 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1031 | + policy = buffer; | |
1032 | + set(policy); | |
1033 | + fd3 = accept(fd1, NULL, 0); | |
1034 | + check(policy, fd3 != EOF); | |
1035 | + close(fd3); | |
1036 | + unset(policy); | |
1037 | + | |
1038 | + snprintf(buffer, sizeof(buffer) - 1, | |
1039 | + "100 acl inet_stream_connect\n" | |
1040 | + "0 allow ip=::1 port=%u\n" | |
1041 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1042 | + policy = buffer; | |
1043 | + set(policy); | |
1044 | + close(fd2); | |
1045 | + fd2 = socket(PF_INET6, SOCK_STREAM, 0); | |
1046 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1047 | + == 0); | |
1048 | + unset(policy); | |
1049 | + | |
1050 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1051 | + | |
1052 | + snprintf(buffer, sizeof(buffer) - 1, | |
1053 | + "100 acl inet_stream_accept\n" | |
1054 | + "0 allow ip=::1 port!=%u\n" | |
1055 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1056 | + policy = buffer; | |
1057 | + set(policy); | |
1058 | + fd3 = accept(fd1, NULL, 0); | |
1059 | + check(policy, fd3 == EOF); | |
1060 | + close(fd3); | |
1061 | + unset(policy); | |
1062 | + | |
1063 | + close(fd1); | |
1064 | + close(fd2); | |
1065 | +} | |
1066 | + | |
1067 | +static void test_network_inet6_dgram(void) | |
1068 | +{ | |
1069 | + struct sockaddr_in6 addr1 = { }; | |
1070 | + struct sockaddr_in6 addr2 = { }; | |
1071 | + socklen_t size = sizeof(addr1); | |
1072 | + int fd1; | |
1073 | + int fd2; | |
1074 | + char c; | |
1075 | + char *policy; | |
1076 | + char buffer[1024]; | |
1077 | + memset(buffer, 0, sizeof(buffer)); | |
1078 | + | |
1079 | + fd1 = socket(PF_INET6, SOCK_DGRAM, 0); | |
1080 | + fd2 = socket(PF_INET6, SOCK_DGRAM, 0); | |
1081 | + addr1.sin6_family = AF_INET6; | |
1082 | + addr1.sin6_addr = in6addr_loopback; | |
1083 | + | |
1084 | + policy = "100 acl inet_dgram_bind\n" | |
1085 | + "0 allow ip=::1 port!=0\n" | |
1086 | + "1 deny\n"; | |
1087 | + set(policy); | |
1088 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1089 | + EOF); | |
1090 | + unset(policy); | |
1091 | + | |
1092 | + policy = "100 acl inet_dgram_bind\n" | |
1093 | + "0 allow ip!=::1 port=0\n" | |
1094 | + "1 deny\n"; | |
1095 | + set(policy); | |
1096 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1097 | + EOF); | |
1098 | + unset(policy); | |
1099 | + | |
1100 | + policy = "100 acl inet_dgram_bind\n" | |
1101 | + "0 allow ip=::1 port=0 path.uid=task.uid\n" | |
1102 | + "1 deny\n"; | |
1103 | + set(policy); | |
1104 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1105 | + EOF); | |
1106 | + unset(policy); | |
1107 | + | |
1108 | + policy = "100 acl inet_dgram_bind\n" | |
1109 | + "0 allow ip=::1 port=0\n" | |
1110 | + "1 deny\n"; | |
1111 | + set(policy); | |
1112 | + check(policy, bind(fd1, (struct sockaddr *) &addr1, sizeof(addr1)) == | |
1113 | + 0); | |
1114 | + unset(policy); | |
1115 | + | |
1116 | + getsockname(fd1, (struct sockaddr *) &addr1, &size); | |
1117 | + | |
1118 | + snprintf(buffer, sizeof(buffer) - 1, | |
1119 | + "100 acl inet_dgram_send\n" | |
1120 | + "0 allow ip=::1 port!=%u\n" | |
1121 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1122 | + policy = buffer; | |
1123 | + set(policy); | |
1124 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1125 | + == EOF); | |
1126 | + unset(policy); | |
1127 | + | |
1128 | + snprintf(buffer, sizeof(buffer) - 1, | |
1129 | + "100 acl inet_dgram_send\n" | |
1130 | + "0 allow ip=::1 port=%u\n" | |
1131 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1132 | + policy = buffer; | |
1133 | + set(policy); | |
1134 | + check(policy, connect(fd2, (struct sockaddr *) &addr1, sizeof(addr1)) | |
1135 | + == 0); | |
1136 | + unset(policy); | |
1137 | + | |
1138 | + getsockname(fd2, (struct sockaddr *) &addr2, &size); | |
1139 | + | |
1140 | + snprintf(buffer, sizeof(buffer) - 1, | |
1141 | + "100 acl inet_dgram_send\n" | |
1142 | + "0 allow ip=::1 port=%u\n" | |
1143 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1144 | + policy = buffer; | |
1145 | + set(policy); | |
1146 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1147 | + unset(policy); | |
1148 | + | |
1149 | + snprintf(buffer, sizeof(buffer) - 1, | |
1150 | + "100 acl inet_dgram_recv\n" | |
1151 | + "0 allow ip=::1 port=%u\n" | |
1152 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1153 | + policy = buffer; | |
1154 | + set(policy); | |
1155 | + check(policy, recv(fd1, &c, 1, 0) != EOF); | |
1156 | + unset(policy); | |
1157 | + | |
1158 | + snprintf(buffer, sizeof(buffer) - 1, | |
1159 | + "100 acl inet_dgram_send\n" | |
1160 | + "0 allow ip=::1 port=%u\n" | |
1161 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1162 | + policy = buffer; | |
1163 | + set(policy); | |
1164 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1165 | + unset(policy); | |
1166 | + | |
1167 | + snprintf(buffer, sizeof(buffer) - 1, | |
1168 | + "100 acl inet_dgram_recv\n" | |
1169 | + "0 allow ip=::1 port!=%u\n" | |
1170 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1171 | + policy = buffer; | |
1172 | + set(policy); | |
1173 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
1174 | + unset(policy); | |
1175 | + | |
1176 | + snprintf(buffer, sizeof(buffer) - 1, | |
1177 | + "ip_group LOCALHOST ::-::ffff\n" | |
1178 | + "100 acl inet_dgram_send\n" | |
1179 | + "0 allow ip=@LOCALHOST port=%u\n" | |
1180 | + "1 deny\n", ntohs(addr1.sin6_port)); | |
1181 | + policy = buffer; | |
1182 | + set(policy); | |
1183 | + check(policy, send(fd2, "", 1, 0) != EOF); | |
1184 | + unset2(policy); | |
1185 | + | |
1186 | + snprintf(buffer, sizeof(buffer) - 1, | |
1187 | + "ip_group LOCALHOST ::-::ffff\n" | |
1188 | + "100 acl inet_dgram_recv\n" | |
1189 | + "0 allow ip!=@LOCALHOST port=%u\n" | |
1190 | + "1 deny\n", ntohs(addr2.sin6_port)); | |
1191 | + policy = buffer; | |
1192 | + set(policy); | |
1193 | + check(policy, recv(fd1, &c, 1, 0) == EOF); | |
1194 | + unset2(policy); | |
1195 | + | |
1196 | + close(fd1); | |
1197 | + close(fd2); | |
1198 | +} | |
1199 | + | |
1200 | +static void test_capability(void) | |
1201 | +{ | |
1202 | + char *policy; | |
1203 | + | |
1204 | + policy = "100 acl set_priority\n" | |
1205 | + "0 allow task.uid=0\n" | |
1206 | + "1 deny\n"; | |
1207 | + set(policy); | |
1208 | + check(policy, nice(0) == 0); | |
1209 | + unset(policy); | |
1210 | + | |
1211 | + policy = "100 acl set_priority\n" | |
1212 | + "0 allow task.uid=task.gid task.type!=execute_handler\n" | |
1213 | + "1 deny\n"; | |
1214 | + set(policy); | |
1215 | + check(policy, nice(0) == 0); | |
1216 | + unset(policy); | |
1217 | + | |
1218 | + policy = "100 acl set_priority\n" | |
1219 | + "0 deny task.uid=0\n" | |
1220 | + "1 allow\n"; | |
1221 | + set(policy); | |
1222 | + check(policy, nice(0) == EOF); | |
1223 | + unset(policy); | |
1224 | + | |
1225 | + policy = "100 acl set_priority\n" | |
1226 | + "0 allow task.uid=task.gid task.type=execute_handler\n" | |
1227 | + "1 deny\n"; | |
1228 | + set(policy); | |
1229 | + check(policy, nice(0) == EOF); | |
1230 | + unset(policy); | |
1231 | +} | |
1232 | + | |
1233 | +static void detach_init(void) | |
1234 | +{ | |
1235 | + int i; | |
1236 | + for (i = 0; i < 2; i++) { | |
1237 | + ptrace(PTRACE_DETACH, 1, NULL, NULL); | |
1238 | + kill(1, SIGCONT); | |
1239 | + sleep(1); | |
1240 | + } | |
1241 | +} | |
1242 | + | |
1243 | +static void test_ptrace(void) | |
1244 | +{ | |
1245 | + char *policy; | |
1246 | + | |
1247 | + policy = "100 acl ptrace\n" | |
1248 | + "0 allow cmd=1 domain!=\"foo\"\n" | |
1249 | + "0 allow cmd=17\n" | |
1250 | + "1 deny\n"; | |
1251 | + set(policy); | |
1252 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == EOF); | |
1253 | + unset(policy); | |
1254 | + detach_init(); | |
1255 | + | |
1256 | + policy = "100 acl ptrace\n" | |
1257 | + "0 allow cmd=16 domain=\"foo\"\n" | |
1258 | + "0 allow cmd=17\n" | |
1259 | + "1 deny\n"; | |
1260 | + set(policy); | |
1261 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == EOF); | |
1262 | + unset(policy); | |
1263 | + detach_init(); | |
1264 | + | |
1265 | + policy = "100 acl ptrace\n" | |
1266 | + "0 allow cmd=16 domain!=\"foo\"\n" | |
1267 | + "0 allow cmd=17\n" | |
1268 | + "1 deny\n"; | |
1269 | + set(policy); | |
1270 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == 0); | |
1271 | + unset(policy); | |
1272 | + detach_init(); | |
1273 | + | |
1274 | + policy = "string_group DOMAINS <init>\n" | |
1275 | + "100 acl ptrace\n" | |
1276 | + "0 allow cmd=16 domain=@DOMAINS\n" | |
1277 | + "0 allow cmd=17\n" | |
1278 | + "1 deny\n"; | |
1279 | + set(policy); | |
1280 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == 0); | |
1281 | + unset2(policy); | |
1282 | + detach_init(); | |
1283 | + | |
1284 | + policy = "string_group DOMAINS <init>\n" | |
1285 | + "100 acl ptrace\n" | |
1286 | + "0 allow cmd=16 domain!=@DOMAINS\n" | |
1287 | + "0 allow cmd=17\n" | |
1288 | + "1 deny\n"; | |
1289 | + set(policy); | |
1290 | + check(policy, ptrace(PTRACE_ATTACH, 1, NULL, NULL) == EOF); | |
1291 | + unset2(policy); | |
1292 | + detach_init(); | |
1293 | +} | |
1294 | + | |
1295 | +static void test_signal(void) | |
1296 | +{ | |
1297 | + char *policy; | |
1298 | + | |
1299 | + policy = "100 acl signal task.domain=\"domain200\"\n" | |
1300 | + "0 allow sig=1 task.uid=0\n" | |
1301 | + "1 deny\n"; | |
1302 | + set(policy); | |
1303 | + check(policy, kill(1, 1) == 0); | |
1304 | + unset(policy); | |
1305 | + | |
1306 | + policy = "100 acl signal task.domain=\"domain200\"\n" | |
1307 | + "0 allow sig!=1 task.uid=0\n" | |
1308 | + "1 deny\n"; | |
1309 | + set(policy); | |
1310 | + check(policy, kill(1, 1) == EOF); | |
1311 | + unset(policy); | |
1312 | + | |
1313 | + policy = "100 acl signal\n" | |
1314 | + "0 allow task.domain!=\"domain200\"\n" | |
1315 | + "0 allow sig=1\n" | |
1316 | + "1 deny\n"; | |
1317 | + set(policy); | |
1318 | + check(policy, kill(1, 1) == 0); | |
1319 | + unset(policy); | |
1320 | + | |
1321 | + policy = "100 acl signal\n" | |
1322 | + "0 deny task.domain=\"domain200\"\n" | |
1323 | + "0 allow\n"; | |
1324 | + set(policy); | |
1325 | + check(policy, kill(1, 1) == EOF); | |
1326 | + unset(policy); | |
1327 | + | |
1328 | + policy = "100 acl signal\n" | |
1329 | + "0 deny sig=1 task.domain=\"domain200\"\n" | |
1330 | + "0 allow\n"; | |
1331 | + set(policy); | |
1332 | + check(policy, kill(1, 1) == EOF); | |
1333 | + unset(policy); | |
1334 | +} | |
1335 | + | |
1336 | +static int fork_exec(char *envp[]) | |
1337 | +{ | |
1338 | + int ret_ignored; | |
1339 | + int pipe_fd[2] = { EOF, EOF }; | |
1340 | + int err = 0; | |
1341 | + pid_t pid; | |
1342 | + if (pipe(pipe_fd)) { | |
1343 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
1344 | + exit(1); | |
1345 | + } | |
1346 | + pid = fork(); | |
1347 | + if (pid == 0) { | |
1348 | + char *argv[2] = { BINDIR "/true", NULL }; | |
1349 | + execve(BINDIR "/true", argv, envp); | |
1350 | + err = errno; | |
1351 | + ret_ignored = write(pipe_fd[1], &err, sizeof(err)); | |
1352 | + _exit(0); | |
1353 | + } | |
1354 | + close(pipe_fd[1]); | |
1355 | + ret_ignored = read(pipe_fd[0], &err, sizeof(err)); | |
1356 | + close(pipe_fd[0]); | |
1357 | + wait(NULL); | |
1358 | + errno = err; | |
1359 | + return err ? EOF : 0; | |
1360 | +} | |
1361 | + | |
1362 | +static void test_environ(void) | |
1363 | +{ | |
1364 | + char *policy; | |
1365 | + char *envp[2]; | |
1366 | + envp[1] = NULL; | |
1367 | + | |
1368 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1369 | + "0 allow value=\"/\"\n" | |
1370 | + "1 deny\n"; | |
1371 | + set(policy); | |
1372 | + envp[0] = "PATH2=/"; | |
1373 | + check(policy, fork_exec(envp) == 0); | |
1374 | + unset(policy); | |
1375 | + | |
1376 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1377 | + "0 allow value!=\"/\"\n" | |
1378 | + "1 deny\n"; | |
1379 | + set(policy); | |
1380 | + envp[0] = "PATH2=/"; | |
1381 | + check(policy, fork_exec(envp) == EOF); | |
1382 | + unset(policy); | |
1383 | + | |
1384 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1385 | + "0 deny value!=\"/\"\n" | |
1386 | + "1 allow\n"; | |
1387 | + set(policy); | |
1388 | + envp[0] = "PATH2=/"; | |
1389 | + check(policy, fork_exec(envp) == 0); | |
1390 | + unset(policy); | |
1391 | + | |
1392 | + policy = "100 acl environ name=\"PATH2\"\n" | |
1393 | + "0 deny value=\"/\"\n" | |
1394 | + "1 allow\n"; | |
1395 | + set(policy); | |
1396 | + envp[0] = "PATH2=/"; | |
1397 | + check(policy, fork_exec(envp) == EOF); | |
1398 | + unset(policy); | |
1399 | + | |
1400 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1401 | + "0 allow envp[\"PATH2\"]=\"/\"\n" | |
1402 | + "1 deny\n"; | |
1403 | + set(policy); | |
1404 | + envp[0] = "PATH2=/"; | |
1405 | + check(policy, fork_exec(envp) == 0); | |
1406 | + unset(policy); | |
1407 | + | |
1408 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1409 | + "0 allow envp[\"PATH2\"]!=\"/\"\n" | |
1410 | + "1 deny\n"; | |
1411 | + set(policy); | |
1412 | + envp[0] = "PATH2=/"; | |
1413 | + check(policy, fork_exec(envp) == EOF); | |
1414 | + unset(policy); | |
1415 | + | |
1416 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1417 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1418 | + "1 deny\n"; | |
1419 | + set(policy); | |
1420 | + envp[0] = "PATH2"; | |
1421 | + check(policy, fork_exec(envp) == 0); | |
1422 | + unset(policy); | |
1423 | + | |
1424 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1425 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1426 | + "1 deny\n"; | |
1427 | + set(policy); | |
1428 | + envp[0] = "PATH2="; | |
1429 | + check(policy, fork_exec(envp) == 0); | |
1430 | + unset(policy); | |
1431 | + | |
1432 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1433 | + "0 allow envp[\"PATH2\"]!=NULL\n" | |
1434 | + "1 deny\n"; | |
1435 | + set(policy); | |
1436 | + envp[0] = "PATH2=/"; | |
1437 | + check(policy, fork_exec(envp) == 0); | |
1438 | + unset(policy); | |
1439 | + | |
1440 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1441 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1442 | + "1 deny\n"; | |
1443 | + set(policy); | |
1444 | + envp[0] = "PATH2"; | |
1445 | + check(policy, fork_exec(envp) == EOF); | |
1446 | + unset(policy); | |
1447 | + | |
1448 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1449 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1450 | + "1 deny\n"; | |
1451 | + set(policy); | |
1452 | + envp[0] = "PATH2="; | |
1453 | + check(policy, fork_exec(envp) == EOF); | |
1454 | + unset(policy); | |
1455 | + | |
1456 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1457 | + "0 allow envp[\"PATH2\"]=NULL\n" | |
1458 | + "1 deny\n"; | |
1459 | + set(policy); | |
1460 | + envp[0] = "PATH2=/"; | |
1461 | + check(policy, fork_exec(envp) == EOF); | |
1462 | + unset(policy); | |
1463 | + | |
1464 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1465 | + "0 allow envp[\"\"]=NULL\n" | |
1466 | + "1 deny\n"; | |
1467 | + set(policy); | |
1468 | + envp[0] = ""; | |
1469 | + check(policy, fork_exec(envp) == EOF); | |
1470 | + unset(policy); | |
1471 | + | |
1472 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1473 | + "0 allow envp[\"\"]!=NULL\n" | |
1474 | + "1 deny\n"; | |
1475 | + set(policy); | |
1476 | + envp[0] = ""; | |
1477 | + check(policy, fork_exec(envp) == 0); | |
1478 | + unset(policy); | |
1479 | + | |
1480 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1481 | + "0 allow envp[\"\"]!=NULL\n" | |
1482 | + "1 deny\n"; | |
1483 | + set(policy); | |
1484 | + envp[0] = "="; | |
1485 | + check(policy, fork_exec(envp) == 0); | |
1486 | + unset(policy); | |
1487 | + | |
1488 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1489 | + "0 allow envp[\"\"]!=NULL\n" | |
1490 | + "1 deny\n"; | |
1491 | + set(policy); | |
1492 | + envp[0] = "=/"; | |
1493 | + check(policy, fork_exec(envp) == 0); | |
1494 | + unset(policy); | |
1495 | +} | |
1496 | + | |
1497 | +static int fork_exec2(char *argv[], char *envp[]) | |
1498 | +{ | |
1499 | + int ret_ignored; | |
1500 | + int pipe_fd[2] = { EOF, EOF }; | |
1501 | + int err = 0; | |
1502 | + pid_t pid; | |
1503 | + if (pipe(pipe_fd)) { | |
1504 | + fprintf(stderr, "Err: %s(%d)\n", strerror(err), err); | |
1505 | + exit(1); | |
1506 | + } | |
1507 | + pid = fork(); | |
1508 | + if (pid == 0) { | |
1509 | + execve(BINDIR "/true", argv, envp); | |
1510 | + err = errno; | |
1511 | + ret_ignored = write(pipe_fd[1], &err, sizeof(err)); | |
1512 | + _exit(0); | |
1513 | + } | |
1514 | + close(pipe_fd[1]); | |
1515 | + ret_ignored = read(pipe_fd[0], &err, sizeof(err)); | |
1516 | + close(pipe_fd[0]); | |
1517 | + wait(NULL); | |
1518 | + errno = err; | |
1519 | + return err ? EOF : 0; | |
1520 | +} | |
1521 | + | |
1522 | +static void test_file_execute(void) | |
1523 | +{ | |
1524 | + char *policy; | |
1525 | + char *argv[5]; | |
1526 | + char *envp[5]; | |
1527 | + memset(argv, 0, sizeof(argv)); | |
1528 | + memset(envp, 0, sizeof(envp)); | |
1529 | + | |
1530 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1531 | + "0 allow argc=1\n" | |
1532 | + "1 deny\n"; | |
1533 | + set(policy); | |
1534 | + argv[0]="true"; | |
1535 | + check(policy, fork_exec2(argv, envp) == 0); | |
1536 | + unset(policy); | |
1537 | + | |
1538 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1539 | + "0 allow argc!=1\n" | |
1540 | + "1 deny\n"; | |
1541 | + set(policy); | |
1542 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1543 | + unset(policy); | |
1544 | + | |
1545 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1546 | + "0 deny argc!=1\n" | |
1547 | + "1 allow\n"; | |
1548 | + set(policy); | |
1549 | + check(policy, fork_exec2(argv, envp) == 0); | |
1550 | + unset(policy); | |
1551 | + | |
1552 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1553 | + "0 deny argc=1\n" | |
1554 | + "1 allow\n"; | |
1555 | + set(policy); | |
1556 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1557 | + unset(policy); | |
1558 | + | |
1559 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1560 | + "0 deny argv[0]!=\"true\"\n" | |
1561 | + "1 allow\n"; | |
1562 | + set(policy); | |
1563 | + check(policy, fork_exec2(argv, envp) == 0); | |
1564 | + unset(policy); | |
1565 | + | |
1566 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1567 | + "0 deny argv[0]=\"true\"\n" | |
1568 | + "1 allow\n"; | |
1569 | + set(policy); | |
1570 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1571 | + unset(policy); | |
1572 | + | |
1573 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1574 | + "0 allow argv[0]!=\"true\"\n" | |
1575 | + "1 deny\n"; | |
1576 | + set(policy); | |
1577 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1578 | + unset(policy); | |
1579 | + | |
1580 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1581 | + "0 allow argv[0]=\"true\"\n" | |
1582 | + "1 deny\n"; | |
1583 | + set(policy); | |
1584 | + check(policy, fork_exec2(argv, envp) == 0); | |
1585 | + unset(policy); | |
1586 | + | |
1587 | + policy = "string_group EXEC_ARGV0 false\n" | |
1588 | + "string_group EXEC_ARGV0 true\n" | |
1589 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1590 | + "0 deny argv[0]!=@EXEC_ARGV0\n" | |
1591 | + "1 allow\n"; | |
1592 | + set(policy); | |
1593 | + check(policy, fork_exec2(argv, envp) == 0); | |
1594 | + unset2(policy); | |
1595 | + | |
1596 | + policy = "string_group EXEC_ARGV0 false\n" | |
1597 | + "string_group EXEC_ARGV0 true\n" | |
1598 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1599 | + "0 deny argv[0]=@EXEC_ARGV0\n" | |
1600 | + "1 allow\n"; | |
1601 | + set(policy); | |
1602 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1603 | + unset2(policy); | |
1604 | + | |
1605 | + policy = "string_group EXEC_ARGV0 false\n" | |
1606 | + "string_group EXEC_ARGV0 true\n" | |
1607 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1608 | + "0 allow argv[0]!=@EXEC_ARGV0\n" | |
1609 | + "1 deny\n"; | |
1610 | + set(policy); | |
1611 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1612 | + unset2(policy); | |
1613 | + | |
1614 | + policy = "string_group EXEC_ARGV0 false\n" | |
1615 | + "string_group EXEC_ARGV0 true\n" | |
1616 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1617 | + "0 allow argv[0]=@EXEC_ARGV0\n" | |
1618 | + "1 deny\n"; | |
1619 | + set(policy); | |
1620 | + check(policy, fork_exec2(argv, envp) == 0); | |
1621 | + unset2(policy); | |
1622 | + | |
1623 | + | |
1624 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1625 | + "0 allow envc=1\n" | |
1626 | + "1 deny\n"; | |
1627 | + set(policy); | |
1628 | + envp[0]="PATH=/"; | |
1629 | + check(policy, fork_exec2(argv, envp) == 0); | |
1630 | + unset(policy); | |
1631 | + | |
1632 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1633 | + "0 allow envc!=1\n" | |
1634 | + "1 deny\n"; | |
1635 | + set(policy); | |
1636 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1637 | + unset(policy); | |
1638 | + | |
1639 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1640 | + "0 deny envc!=1\n" | |
1641 | + "1 allow\n"; | |
1642 | + set(policy); | |
1643 | + check(policy, fork_exec2(argv, envp) == 0); | |
1644 | + unset(policy); | |
1645 | + | |
1646 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1647 | + "0 deny envc=1\n" | |
1648 | + "1 allow\n"; | |
1649 | + set(policy); | |
1650 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1651 | + unset(policy); | |
1652 | + | |
1653 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1654 | + "0 deny envp[\"PATH\"]!=\"/\"\n" | |
1655 | + "1 allow\n"; | |
1656 | + set(policy); | |
1657 | + check(policy, fork_exec2(argv, envp) == 0); | |
1658 | + unset(policy); | |
1659 | + | |
1660 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1661 | + "0 deny envp[\"PATH\"]=\"/\"\n" | |
1662 | + "1 allow\n"; | |
1663 | + set(policy); | |
1664 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1665 | + unset(policy); | |
1666 | + | |
1667 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1668 | + "0 allow envp[\"PATH\"]!=\"/\"\n" | |
1669 | + "1 deny\n"; | |
1670 | + set(policy); | |
1671 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1672 | + unset(policy); | |
1673 | + | |
1674 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1675 | + "0 allow envp[\"PATH\"]=\"/\"\n" | |
1676 | + "1 deny\n"; | |
1677 | + set(policy); | |
1678 | + check(policy, fork_exec2(argv, envp) == 0); | |
1679 | + unset(policy); | |
1680 | + | |
1681 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1682 | + "string_group PATH_VALUES /\n" | |
1683 | + "string_group PATH_VALUES /sbin\n" | |
1684 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1685 | + "0 deny envp[\"PATH\"]!=@PATH_VALUES\n" | |
1686 | + "1 allow\n"; | |
1687 | + set(policy); | |
1688 | + check(policy, fork_exec2(argv, envp) == 0); | |
1689 | + unset2(policy); | |
1690 | + | |
1691 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1692 | + "string_group PATH_VALUES /\n" | |
1693 | + "string_group PATH_VALUES /sbin\n" | |
1694 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1695 | + "0 deny envp[\"PATH\"]=@PATH_VALUES\n" | |
1696 | + "1 allow\n"; | |
1697 | + set(policy); | |
1698 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1699 | + unset2(policy); | |
1700 | + | |
1701 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1702 | + "string_group PATH_VALUES /\n" | |
1703 | + "string_group PATH_VALUES /sbin\n" | |
1704 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1705 | + "0 allow envp[\"PATH\"]!=@PATH_VALUES\n" | |
1706 | + "1 deny\n"; | |
1707 | + set(policy); | |
1708 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1709 | + unset2(policy); | |
1710 | + | |
1711 | + policy = "string_group PATH_VALUES " BINDIR "\n" | |
1712 | + "string_group PATH_VALUES /\n" | |
1713 | + "string_group PATH_VALUES /sbin\n" | |
1714 | + "100 acl execute path=\"" BINDIR "/true\"\n" | |
1715 | + "0 allow envp[\"PATH\"]=@PATH_VALUES\n" | |
1716 | + "1 deny\n"; | |
1717 | + set(policy); | |
1718 | + check(policy, fork_exec2(argv, envp) == 0); | |
1719 | + unset2(policy); | |
1720 | + | |
1721 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1722 | + "0 deny envp[\"PATH\"]!=NULL\n" | |
1723 | + "1 allow\n"; | |
1724 | + set(policy); | |
1725 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1726 | + unset(policy); | |
1727 | + | |
1728 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1729 | + "0 deny envp[\"PATH\"]=NULL\n" | |
1730 | + "1 allow\n"; | |
1731 | + set(policy); | |
1732 | + check(policy, fork_exec2(argv, envp) == 0); | |
1733 | + unset(policy); | |
1734 | + | |
1735 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1736 | + "0 allow envp[\"PATH\"]!=NULL\n" | |
1737 | + "1 deny\n"; | |
1738 | + set(policy); | |
1739 | + check(policy, fork_exec2(argv, envp) == 0); | |
1740 | + unset(policy); | |
1741 | + | |
1742 | + policy = "100 acl execute path=\"" BINDIR "/true\"\n" | |
1743 | + "0 allow envp[\"PATH\"]=NULL\n" | |
1744 | + "1 deny\n"; | |
1745 | + set(policy); | |
1746 | + check(policy, fork_exec2(argv, envp) == EOF); | |
1747 | + unset(policy); | |
1748 | +} | |
1749 | + | |
1750 | +static void test_file_misc(void) | |
1751 | +{ | |
1752 | + int fd; | |
1753 | + const pid_t pid = getpid(); | |
1754 | + char buffer[1024]; | |
1755 | + memset(buffer, 0, sizeof(buffer)); | |
1756 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1757 | + "10 allow path!=NULL\n" | |
1758 | + "20 deny\n", pid); | |
1759 | + set(buffer); | |
1760 | + fd = open("/dev/null", O_RDONLY); | |
1761 | + check(buffer, fd != EOF); | |
1762 | + close(fd); | |
1763 | + unset(buffer); | |
1764 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1765 | + "10 allow path=NULL\n" | |
1766 | + "20 deny\n", pid); | |
1767 | + set(buffer); | |
1768 | + fd = open("/dev/null", O_RDONLY); | |
1769 | + check(buffer, fd == EOF); | |
1770 | + close(fd); | |
1771 | + unset(buffer); | |
1772 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1773 | + "10 deny path=NULL\n" | |
1774 | + "20 allow\n", pid); | |
1775 | + set(buffer); | |
1776 | + fd = open("/dev/null", O_RDONLY); | |
1777 | + check(buffer, fd != EOF); | |
1778 | + close(fd); | |
1779 | + unset(buffer); | |
1780 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1781 | + "10 deny path!=NULL\n" | |
1782 | + "20 allow\n", pid); | |
1783 | + set(buffer); | |
1784 | + fd = open("/dev/null", O_RDONLY); | |
1785 | + check(buffer, fd == EOF); | |
1786 | + close(fd); | |
1787 | + unset(buffer); | |
1788 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1789 | + "10 allow path=path\n" | |
1790 | + "20 deny\n", pid); | |
1791 | + set(buffer); | |
1792 | + fd = open("/dev/null", O_RDONLY); | |
1793 | + check(buffer, fd != EOF); | |
1794 | + close(fd); | |
1795 | + unset(buffer); | |
1796 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1797 | + "10 allow path!=path\n" | |
1798 | + "20 deny\n", pid); | |
1799 | + set(buffer); | |
1800 | + fd = open("/dev/null", O_RDONLY); | |
1801 | + check(buffer, fd == EOF); | |
1802 | + close(fd); | |
1803 | + unset(buffer); | |
1804 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1805 | + "10 deny path!=path\n" | |
1806 | + "20 allow\n", pid); | |
1807 | + set(buffer); | |
1808 | + fd = open("/dev/null", O_RDONLY); | |
1809 | + check(buffer, fd != EOF); | |
1810 | + close(fd); | |
1811 | + unset(buffer); | |
1812 | + snprintf(buffer, sizeof(buffer) - 1, "100 acl read task.pid=%u\n" | |
1813 | + "10 deny path=path\n" | |
1814 | + "20 allow\n", pid); | |
1815 | + set(buffer); | |
1816 | + fd = open("/dev/null", O_RDONLY); | |
1817 | + check(buffer, fd == EOF); | |
1818 | + close(fd); | |
1819 | + unset(buffer); | |
1820 | + snprintf(buffer, sizeof(buffer) - 1, | |
1821 | + "string_group STRING_GROUP1 /dev/null\n" | |
1822 | + "100 acl read task.pid=%u\n" | |
1823 | + "10 allow path=@STRING_GROUP1\n" | |
1824 | + "20 deny\n", pid); | |
1825 | + set(buffer); | |
1826 | + fd = open("/dev/null", O_RDONLY); | |
1827 | + check(buffer, fd != EOF); | |
1828 | + close(fd); | |
1829 | + unset2(buffer); | |
1830 | + snprintf(buffer, sizeof(buffer) - 1, | |
1831 | + "string_group STRING_GROUP1 /dev/null\n" | |
1832 | + "100 acl read task.pid=%u\n" | |
1833 | + "10 allow path!=@STRING_GROUP1\n" | |
1834 | + "20 deny\n", pid); | |
1835 | + set(buffer); | |
1836 | + fd = open("/dev/null", O_RDONLY); | |
1837 | + check(buffer, fd == EOF); | |
1838 | + close(fd); | |
1839 | + unset2(buffer); | |
1840 | + snprintf(buffer, sizeof(buffer) - 1, | |
1841 | + "string_group STRING_GROUP1 /dev/null\n" | |
1842 | + "100 acl read task.pid=%u\n" | |
1843 | + "10 deny path!=@STRING_GROUP1\n" | |
1844 | + "20 allow\n", pid); | |
1845 | + set(buffer); | |
1846 | + fd = open("/dev/null", O_RDONLY); | |
1847 | + check(buffer, fd != EOF); | |
1848 | + close(fd); | |
1849 | + unset2(buffer); | |
1850 | + snprintf(buffer, sizeof(buffer) - 1, | |
1851 | + "string_group STRING_GROUP1 /dev/null\n" | |
1852 | + "100 acl read task.pid=%u\n" | |
1853 | + "10 deny path=@STRING_GROUP1\n" | |
1854 | + "20 allow\n", pid); | |
1855 | + set(buffer); | |
1856 | + fd = open("/dev/null", O_RDONLY); | |
1857 | + check(buffer, fd == EOF); | |
1858 | + close(fd); | |
1859 | + unset2(buffer); | |
1860 | + snprintf(buffer, sizeof(buffer) - 1, | |
1861 | + "number_group NUMBER_GROUP1 0666\n" | |
1862 | + "100 acl read task.pid=%u\n" | |
1863 | + "10 deny path.perm!=@NUMBER_GROUP1\n" | |
1864 | + "20 allow\n", pid); | |
1865 | + set(buffer); | |
1866 | + fd = open("/dev/null", O_RDONLY); | |
1867 | + check(buffer, fd != EOF); | |
1868 | + close(fd); | |
1869 | + unset2(buffer); | |
1870 | + snprintf(buffer, sizeof(buffer) - 1, | |
1871 | + "number_group NUMBER_GROUP1 0666\n" | |
1872 | + "100 acl read task.pid=%u\n" | |
1873 | + "10 deny path.perm=@NUMBER_GROUP1\n" | |
1874 | + "20 allow\n", pid); | |
1875 | + set(buffer); | |
1876 | + fd = open("/dev/null", O_RDONLY); | |
1877 | + check(buffer, fd == EOF); | |
1878 | + close(fd); | |
1879 | + unset2(buffer); | |
1880 | + snprintf(buffer, sizeof(buffer) - 1, | |
1881 | + "100 acl read task.pid=%u\n" | |
1882 | + "10 deny path.perm!=owner_read\n" | |
1883 | + "20 allow\n", pid); | |
1884 | + set(buffer); | |
1885 | + fd = open("/dev/null", O_RDONLY); | |
1886 | + check(buffer, fd != EOF); | |
1887 | + close(fd); | |
1888 | + unset2(buffer); | |
1889 | + snprintf(buffer, sizeof(buffer) - 1, | |
1890 | + "100 acl read task.pid=%u\n" | |
1891 | + "10 deny path.perm=owner_read\n" | |
1892 | + "20 allow\n", pid); | |
1893 | + set(buffer); | |
1894 | + fd = open("/dev/null", O_RDONLY); | |
1895 | + check(buffer, fd == EOF); | |
1896 | + close(fd); | |
1897 | + unset2(buffer); | |
1898 | + snprintf(buffer, sizeof(buffer) - 1, | |
1899 | + "100 acl read task.pid=%u\n" | |
1900 | + "10 deny path.perm!=group_write\n" | |
1901 | + "20 allow\n", pid); | |
1902 | + set(buffer); | |
1903 | + fd = open("/dev/null", O_RDONLY); | |
1904 | + check(buffer, fd != EOF); | |
1905 | + close(fd); | |
1906 | + unset2(buffer); | |
1907 | + snprintf(buffer, sizeof(buffer) - 1, | |
1908 | + "100 acl read task.pid=%u\n" | |
1909 | + "10 deny path.perm=group_write\n" | |
1910 | + "20 allow\n", pid); | |
1911 | + set(buffer); | |
1912 | + fd = open("/dev/null", O_RDONLY); | |
1913 | + check(buffer, fd == EOF); | |
1914 | + close(fd); | |
1915 | + unset2(buffer); | |
1916 | + snprintf(buffer, sizeof(buffer) - 1, | |
1917 | + "100 acl read task.pid=%u\n" | |
1918 | + "10 deny path.perm!=others_read\n" | |
1919 | + "20 allow\n", pid); | |
1920 | + set(buffer); | |
1921 | + fd = open("/dev/null", O_RDONLY); | |
1922 | + check(buffer, fd != EOF); | |
1923 | + close(fd); | |
1924 | + unset2(buffer); | |
1925 | + snprintf(buffer, sizeof(buffer) - 1, | |
1926 | + "100 acl read task.pid=%u\n" | |
1927 | + "10 deny path.perm=others_read\n" | |
1928 | + "20 allow\n", pid); | |
1929 | + set(buffer); | |
1930 | + fd = open("/dev/null", O_RDONLY); | |
1931 | + check(buffer, fd == EOF); | |
1932 | + close(fd); | |
1933 | + unset2(buffer); | |
1934 | + snprintf(buffer, sizeof(buffer) - 1, | |
1935 | + "100 acl read task.pid=%u\n" | |
1936 | + "10 deny path.perm=path.parent.perm\n" | |
1937 | + "20 allow\n", pid); | |
1938 | + set(buffer); | |
1939 | + fd = open("/dev/null", O_RDONLY); | |
1940 | + check(buffer, fd != EOF); | |
1941 | + close(fd); | |
1942 | + unset2(buffer); | |
1943 | + snprintf(buffer, sizeof(buffer) - 1, | |
1944 | + "100 acl read task.pid=%u\n" | |
1945 | + "10 deny path.perm!=path.parent.perm\n" | |
1946 | + "20 allow\n", pid); | |
1947 | + set(buffer); | |
1948 | + fd = open("/dev/null", O_RDONLY); | |
1949 | + check(buffer, fd == EOF); | |
1950 | + close(fd); | |
1951 | + unset2(buffer); | |
1952 | + snprintf(buffer, sizeof(buffer) - 1, | |
1953 | + "100 acl execute task.ppid=%u\n" | |
1954 | + "10 allow path=exec\n" | |
1955 | + "20 deny\n", pid); | |
1956 | + set(buffer); | |
1957 | + check(buffer, fork_exec(NULL) == 0); | |
1958 | + unset(buffer); | |
1959 | + snprintf(buffer, sizeof(buffer) - 1, | |
1960 | + "100 acl execute task.ppid=%u\n" | |
1961 | + "10 allow path!=exec\n" | |
1962 | + "20 deny\n", pid); | |
1963 | + set(buffer); | |
1964 | + check(buffer, fork_exec(NULL) == EOF); | |
1965 | + unset(buffer); | |
1966 | + snprintf(buffer, sizeof(buffer) - 1, | |
1967 | + "100 acl execute task.ppid=%u\n" | |
1968 | + "10 deny path=exec\n" | |
1969 | + "20 allow\n", pid); | |
1970 | + set(buffer); | |
1971 | + check(buffer, fork_exec(NULL) == EOF); | |
1972 | + unset(buffer); | |
1973 | + snprintf(buffer, sizeof(buffer) - 1, | |
1974 | + "100 acl execute task.ppid=%u\n" | |
1975 | + "10 deny path!=exec\n" | |
1976 | + "20 allow\n", pid); | |
1977 | + set(buffer); | |
1978 | + check(buffer, fork_exec(NULL) == 0); | |
1979 | + unset(buffer); | |
1980 | +} | |
1981 | + | |
1982 | +static void reset_policy(void) | |
1983 | +{ | |
1984 | + FILE *fp2 = fopen(POLDIR "/policy", "r"); | |
1985 | + FILE *fp1 = fopen(POLDIR "/policy", "w"); | |
1986 | + if (!fp1 || !fp2) { | |
1987 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
1988 | + exit(1); | |
1989 | + } | |
1990 | + while (1) { | |
1991 | + const int c = fgetc(fp2); | |
1992 | + if (c == EOF) | |
1993 | + break; | |
1994 | + fputc(c, fp1); | |
1995 | + if (c == '\n') | |
1996 | + fprintf(fp1, "delete "); | |
1997 | + } | |
1998 | + fclose(fp2); | |
1999 | + fclose(fp1); | |
2000 | + | |
2001 | + /* Do not leave the init process in stopped state. */ | |
2002 | + kill(1, SIGCONT); | |
2003 | + | |
2004 | + /* Undo mount("/", MS_REC|MS_SHARED) made by systemd. */ | |
2005 | + mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL); | |
2006 | +} | |
2007 | + | |
2008 | +int main(int argc, char *argv[]) | |
2009 | +{ | |
2010 | + reset_policy(); | |
2011 | + | |
2012 | + fp = fopen(POLDIR "/policy", "w"); | |
2013 | + if (!fp) { | |
2014 | + fprintf(stderr, " Can't open " POLDIR "/policy\n"); | |
2015 | + return 1; | |
2016 | + } | |
2017 | + fprintf(fp, "quota audit[0]" | |
2018 | + " allowed=1024 unmatched=1024 denied=1024\n"); | |
2019 | + fflush(fp); | |
2020 | + | |
2021 | + test_task_transition(); | |
2022 | + test_file_read(); | |
2023 | + test_file_write(); | |
2024 | + test_file_create(); | |
2025 | + test_file_unlink(); | |
2026 | + test_file_link(); | |
2027 | + test_file_rename(); | |
2028 | + test_network_inet_stream(); | |
2029 | + test_network_inet_dgram(); | |
2030 | + test_network_inet_raw(); | |
2031 | + test_network_inet6_stream(); | |
2032 | + test_network_inet6_dgram(); | |
2033 | + test_capability(); | |
2034 | + test_ptrace(); | |
2035 | + test_signal(); | |
2036 | + test_environ(); | |
2037 | + test_file_execute(); | |
2038 | + test_file_misc(); | |
2039 | + return 0; | |
2040 | +} |
@@ -0,0 +1,692 @@ | ||
1 | +#include <stdio.h> | |
2 | +#include <string.h> | |
3 | +#include <stdlib.h> | |
4 | + | |
5 | +#define bool _Bool | |
6 | +#define true 1 | |
7 | +#define false 0 | |
8 | +#define u32 unsigned int | |
9 | +#define u8 unsigned char | |
10 | + | |
11 | +struct cs_path_info { | |
12 | + const char *name; | |
13 | + u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
14 | + u32 total_len; /* = strlen(name) */ | |
15 | + u32 const_len; /* = cs_const_part_length(name) */ | |
16 | +}; | |
17 | + | |
18 | +static void out_of_memory(void) | |
19 | +{ | |
20 | + fprintf(stderr, "Out of memory\n"); | |
21 | + exit(1); | |
22 | +} | |
23 | + | |
24 | +/* Copied from Linux kernel source code. */ | |
25 | +static unsigned int full_name_hash(const unsigned char *name, unsigned int len) | |
26 | +{ | |
27 | + unsigned long hash = 0; | |
28 | + while (len--) { | |
29 | + unsigned long c = *name++; | |
30 | + hash = (hash + (c << 4) + (c >> 4)) * 11; | |
31 | + } | |
32 | + return (unsigned int) hash; | |
33 | +} | |
34 | + | |
35 | +/** | |
36 | + * cs_pathcmp - strcmp() for "struct cs_path_info" structure. | |
37 | + * | |
38 | + * @a: Pointer to "struct cs_path_info". | |
39 | + * @b: Pointer to "struct cs_path_info". | |
40 | + * | |
41 | + * Returns true if @a != @b, false otherwise. | |
42 | + */ | |
43 | +static inline bool cs_pathcmp(const struct cs_path_info *a, | |
44 | + const struct cs_path_info *b) | |
45 | +{ | |
46 | + return a->hash != b->hash || strcmp(a->name, b->name); | |
47 | +} | |
48 | + | |
49 | +/** | |
50 | + * cs_const_part_length - Evaluate the initial length without a pattern in a token. | |
51 | + * | |
52 | + * @filename: The string to evaluate. Maybe NULL. | |
53 | + * | |
54 | + * Returns the initial length without a pattern in @filename. | |
55 | + */ | |
56 | +static int cs_const_part_length(const char *filename) | |
57 | +{ | |
58 | + char c; | |
59 | + int len = 0; | |
60 | + if (!filename) | |
61 | + return 0; | |
62 | + while (1) { | |
63 | + c = *filename++; | |
64 | + if (!c) | |
65 | + break; | |
66 | + if (c != '\\') { | |
67 | + len++; | |
68 | + continue; | |
69 | + } | |
70 | + c = *filename++; | |
71 | + switch (c) { | |
72 | + case '0': /* "\ooo" */ | |
73 | + case '1': | |
74 | + case '2': | |
75 | + case '3': | |
76 | + c = *filename++; | |
77 | + if (c < '0' || c > '7') | |
78 | + break; | |
79 | + c = *filename++; | |
80 | + if (c < '0' || c > '7') | |
81 | + break; | |
82 | + len += 4; | |
83 | + continue; | |
84 | + } | |
85 | + break; | |
86 | + } | |
87 | + return len; | |
88 | +} | |
89 | + | |
90 | +/** | |
91 | + * cs_fill_path_info - Fill in "struct cs_path_info" members. | |
92 | + * | |
93 | + * @ptr: Pointer to "struct cs_path_info" to fill in. | |
94 | + * | |
95 | + * Returns nothing. | |
96 | + * | |
97 | + * The caller sets "struct cs_path_info"->name. | |
98 | + */ | |
99 | +static void cs_fill_path_info(struct cs_path_info *ptr) | |
100 | +{ | |
101 | + const char *name = ptr->name; | |
102 | + const int len = strlen(name); | |
103 | + ptr->total_len = len; | |
104 | + ptr->const_len = cs_const_part_length(name); | |
105 | + ptr->hash = full_name_hash((const unsigned char *) name, len); | |
106 | +} | |
107 | + | |
108 | +/** | |
109 | + * cs_byte_range - Check whether the string is a \ooo style octal value. | |
110 | + * | |
111 | + * @str: Pointer to the string. | |
112 | + * | |
113 | + * Returns true if @str is a \ooo style octal value, false otherwise. | |
114 | + */ | |
115 | +static bool cs_byte_range(const char *str) | |
116 | +{ | |
117 | + return *str >= '0' && *str++ <= '3' && | |
118 | + *str >= '0' && *str++ <= '7' && | |
119 | + *str >= '0' && *str <= '7'; | |
120 | +} | |
121 | + | |
122 | +/** | |
123 | + * cs_decimal - Check whether the character is a decimal character. | |
124 | + * | |
125 | + * @c: The character to check. | |
126 | + * | |
127 | + * Returns true if @c is a decimal character, false otherwise. | |
128 | + */ | |
129 | +static bool cs_decimal(const char c) | |
130 | +{ | |
131 | + return c >= '0' && c <= '9'; | |
132 | +} | |
133 | + | |
134 | +/** | |
135 | + * cs_hexadecimal - Check whether the character is a hexadecimal character. | |
136 | + * | |
137 | + * @c: The character to check. | |
138 | + * | |
139 | + * Returns true if @c is a hexadecimal character, false otherwise. | |
140 | + */ | |
141 | +static bool cs_hexadecimal(const char c) | |
142 | +{ | |
143 | + return (c >= '0' && c <= '9') || | |
144 | + (c >= 'A' && c <= 'F') || | |
145 | + (c >= 'a' && c <= 'f'); | |
146 | +} | |
147 | + | |
148 | +/** | |
149 | + * cs_alphabet_char - Check whether the character is an alphabet. | |
150 | + * | |
151 | + * @c: The character to check. | |
152 | + * | |
153 | + * Returns true if @c is an alphabet character, false otherwise. | |
154 | + */ | |
155 | +static bool cs_alphabet_char(const char c) | |
156 | +{ | |
157 | + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); | |
158 | +} | |
159 | + | |
160 | +/** | |
161 | + * cs_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. | |
162 | + * | |
163 | + * @filename: The start of string to check. | |
164 | + * @filename_end: The end of string to check. | |
165 | + * @pattern: The start of pattern to compare. | |
166 | + * @pattern_end: The end of pattern to compare. | |
167 | + * | |
168 | + * Returns true if @filename matches @pattern, false otherwise. | |
169 | + */ | |
170 | +static bool cs_file_matches_pattern2(const char *filename, | |
171 | + const char *filename_end, | |
172 | + const char *pattern, | |
173 | + const char *pattern_end) | |
174 | +{ | |
175 | + while (filename < filename_end && pattern < pattern_end) { | |
176 | + char c; | |
177 | + if (*pattern != '\\') { | |
178 | + if (*filename++ != *pattern++) | |
179 | + return false; | |
180 | + continue; | |
181 | + } | |
182 | + c = *filename; | |
183 | + pattern++; | |
184 | + switch (*pattern) { | |
185 | + int i; | |
186 | + int j; | |
187 | + case '?': | |
188 | + if (c == '/') { | |
189 | + return false; | |
190 | + } else if (c == '\\') { | |
191 | + if (cs_byte_range(filename + 1)) | |
192 | + filename += 3; | |
193 | + else | |
194 | + return false; | |
195 | + } | |
196 | + break; | |
197 | + case '+': | |
198 | + if (!cs_decimal(c)) | |
199 | + return false; | |
200 | + break; | |
201 | + case 'x': | |
202 | + if (!cs_hexadecimal(c)) | |
203 | + return false; | |
204 | + break; | |
205 | + case 'a': | |
206 | + if (!cs_alphabet_char(c)) | |
207 | + return false; | |
208 | + break; | |
209 | + case '0': | |
210 | + case '1': | |
211 | + case '2': | |
212 | + case '3': | |
213 | + if (c == '\\' && cs_byte_range(filename + 1) | |
214 | + && !strncmp(filename + 1, pattern, 3)) { | |
215 | + filename += 3; | |
216 | + pattern += 2; | |
217 | + break; | |
218 | + } | |
219 | + return false; /* Not matched. */ | |
220 | + case '*': | |
221 | + case '@': | |
222 | + for (i = 0; i <= filename_end - filename; i++) { | |
223 | + if (cs_file_matches_pattern2(filename + i, | |
224 | + filename_end, | |
225 | + pattern + 1, | |
226 | + pattern_end)) | |
227 | + return true; | |
228 | + c = filename[i]; | |
229 | + if (c == '.' && *pattern == '@') | |
230 | + break; | |
231 | + if (c != '\\') | |
232 | + continue; | |
233 | + if (cs_byte_range(filename + i + 1)) | |
234 | + i += 3; | |
235 | + else | |
236 | + break; /* Bad pattern. */ | |
237 | + } | |
238 | + return false; /* Not matched. */ | |
239 | + default: | |
240 | + j = 0; | |
241 | + c = *pattern; | |
242 | + if (c == '$') { | |
243 | + while (cs_decimal(filename[j])) | |
244 | + j++; | |
245 | + } else if (c == 'X') { | |
246 | + while (cs_hexadecimal(filename[j])) | |
247 | + j++; | |
248 | + } else if (c == 'A') { | |
249 | + while (cs_alphabet_char(filename[j])) | |
250 | + j++; | |
251 | + } | |
252 | + for (i = 1; i <= j; i++) { | |
253 | + if (cs_file_matches_pattern2(filename + i, | |
254 | + filename_end, | |
255 | + pattern + 1, | |
256 | + pattern_end)) | |
257 | + return true; | |
258 | + } | |
259 | + return false; /* Not matched or bad pattern. */ | |
260 | + } | |
261 | + filename++; | |
262 | + pattern++; | |
263 | + } | |
264 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | |
265 | + while (*pattern == '\\' && | |
266 | + (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | |
267 | + pattern += 2; | |
268 | + return filename == filename_end && pattern == pattern_end; | |
269 | +} | |
270 | + | |
271 | +/** | |
272 | + * cs_file_matches_pattern - Pattern matching without '/' character. | |
273 | + * | |
274 | + * @filename: The start of string to check. | |
275 | + * @filename_end: The end of string to check. | |
276 | + * @pattern: The start of pattern to compare. | |
277 | + * @pattern_end: The end of pattern to compare. | |
278 | + * | |
279 | + * Returns true if @filename matches @pattern, false otherwise. | |
280 | + */ | |
281 | +static bool cs_file_matches_pattern(const char *filename, | |
282 | + const char *filename_end, | |
283 | + const char *pattern, | |
284 | + const char *pattern_end) | |
285 | +{ | |
286 | + const char *pattern_start = pattern; | |
287 | + bool first = true; | |
288 | + bool result; | |
289 | + if (filename_end > filename && memchr(filename, '/', filename_end - filename)) { | |
290 | + printf("'"); | |
291 | + fwrite(filename, 1, filename_end - filename, stdout); | |
292 | + printf("', '"); | |
293 | + fwrite(pattern, 1, pattern_end - pattern, stdout); | |
294 | + printf("'\n"); | |
295 | + } | |
296 | + if (pattern_end > pattern && memchr(pattern, '/', pattern_end - pattern)) { | |
297 | + printf("'"); | |
298 | + fwrite(filename, 1, filename_end - filename, stdout); | |
299 | + printf("', '"); | |
300 | + fwrite(pattern, 1, pattern_end - pattern, stdout); | |
301 | + printf("'\n"); | |
302 | + } | |
303 | + while (pattern < pattern_end - 1) { | |
304 | + /* Split at "\-" pattern. */ | |
305 | + if (*pattern++ != '\\' || *pattern++ != '-') | |
306 | + continue; | |
307 | + result = cs_file_matches_pattern2(filename, filename_end, | |
308 | + pattern_start, pattern - 2); | |
309 | + if (first) | |
310 | + result = !result; | |
311 | + if (result) | |
312 | + return false; | |
313 | + first = false; | |
314 | + pattern_start = pattern; | |
315 | + } | |
316 | + result = cs_file_matches_pattern2(filename, filename_end, | |
317 | + pattern_start, pattern_end); | |
318 | + return first ? result : !result; | |
319 | +} | |
320 | + | |
321 | +/** | |
322 | + * cs_path_matches_pattern2 - Do pathname pattern matching. | |
323 | + * | |
324 | + * @f: The start of string to check. | |
325 | + * @p: The start of pattern to compare. | |
326 | + * | |
327 | + * Returns true if @f matches @p, false otherwise. | |
328 | + */ | |
329 | +static bool cs_path_matches_pattern2(const char *f, const char *p) | |
330 | +{ | |
331 | + const char *f_delimiter; | |
332 | + const char *p_delimiter; | |
333 | + while (*f && *p) { | |
334 | + f_delimiter = strchr(f + 1, '/'); | |
335 | + if (!f_delimiter) | |
336 | + f_delimiter = f + strlen(f); | |
337 | + p_delimiter = strchr(p + 1, '/'); | |
338 | + if (!p_delimiter) | |
339 | + p_delimiter = p + strlen(p); | |
340 | + if (*p == '/' && *(p + 1) == '\\') { | |
341 | + if (*(p + 2) == '(') { | |
342 | + /* Check zero repetition. */ | |
343 | + if (cs_path_matches_pattern2(f, p_delimiter)) | |
344 | + return true; | |
345 | + /* Check one or more repetition. */ | |
346 | + goto repetition; | |
347 | + } | |
348 | + if (*(p + 2) == '{') | |
349 | + goto repetition; | |
350 | + } | |
351 | + if ((*f == '/' || *p == '/') && *f++ != *p++) | |
352 | + return false; | |
353 | + if (!cs_file_matches_pattern(f, f_delimiter, p, p_delimiter)) | |
354 | + return false; | |
355 | + f = f_delimiter; | |
356 | + p = p_delimiter; | |
357 | + } | |
358 | + /* Ignore trailing "\*" and "\@" in @pattern. */ | |
359 | + while (*p == '\\' && (*(p + 1) == '*' || *(p + 1) == '@')) | |
360 | + p += 2; | |
361 | + return !*f && !*p; | |
362 | +repetition: | |
363 | + do { | |
364 | + /* Compare current component with pattern. */ | |
365 | + if (!cs_file_matches_pattern(f + 1, f_delimiter, | |
366 | + p + 3, p_delimiter - 2)) | |
367 | + break; | |
368 | + /* Proceed to next component. */ | |
369 | + f = f_delimiter; | |
370 | + if (!*f) | |
371 | + break; | |
372 | + /* Continue comparison. */ | |
373 | + if (cs_path_matches_pattern2(f, p_delimiter)) | |
374 | + return true; | |
375 | + f_delimiter = strchr(f + 1, '/'); | |
376 | + } while (f_delimiter); | |
377 | + return false; /* Not matched. */ | |
378 | +} | |
379 | + | |
380 | +/** | |
381 | + * cs_path_matches_pattern - Check whether the given filename matches the given pattern. | |
382 | + * | |
383 | + * @filename: The filename to check. | |
384 | + * @pattern: The pattern to compare. | |
385 | + * | |
386 | + * Returns true if matches, false otherwise. | |
387 | + * | |
388 | + * The following patterns are available. | |
389 | + * \ooo Octal representation of a byte. | |
390 | + * \* Zero or more repetitions of characters other than '/'. | |
391 | + * \@ Zero or more repetitions of characters other than '/' or '.'. | |
392 | + * \? 1 byte character other than '/'. | |
393 | + * \$ One or more repetitions of decimal digits. | |
394 | + * \+ 1 decimal digit. | |
395 | + * \X One or more repetitions of hexadecimal digits. | |
396 | + * \x 1 hexadecimal digit. | |
397 | + * \A One or more repetitions of alphabet characters. | |
398 | + * \a 1 alphabet character. | |
399 | + * | |
400 | + * \- Subtraction operator. | |
401 | + * | |
402 | + * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ | |
403 | + * /dir/dir/dir/ ). | |
404 | + * | |
405 | + * /\(dir\)/ '/' + 'Zero or more repetitions of dir/' (e.g. / /dir/ | |
406 | + * /dir/dir/ ). | |
407 | + */ | |
408 | +static bool cs_path_matches_pattern(const struct cs_path_info *filename, | |
409 | + const struct cs_path_info *pattern) | |
410 | +{ | |
411 | + const char *f = filename->name; | |
412 | + const char *p = pattern->name; | |
413 | + const int len = pattern->const_len; | |
414 | + /* If @pattern doesn't contain pattern, I can use strcmp(). */ | |
415 | + if (len == pattern->total_len) | |
416 | + return !cs_pathcmp(filename, pattern); | |
417 | + /* Compare the initial length without patterns. */ | |
418 | + if (len) { | |
419 | + if (strncmp(f, p, len)) | |
420 | + return false; | |
421 | + f += len - 1; | |
422 | + p += len - 1; | |
423 | + } | |
424 | + return cs_path_matches_pattern2(f, p); | |
425 | +} | |
426 | + | |
427 | +/** | |
428 | + * cs_correct_word - Check whether the given string follows the naming rules. | |
429 | + * | |
430 | + * @string: The string to check. | |
431 | + * @allow_pattern: True if allow use of patterns, false otherwise. | |
432 | + * | |
433 | + * Returns true if @string follows the naming rules, false otherwise. | |
434 | + */ | |
435 | +static bool cs_correct_word(const char *string, bool allow_pattern) | |
436 | +{ | |
437 | + u8 recursion = 20; | |
438 | + const char *const start = string; | |
439 | + u8 in_repetition = 0; | |
440 | + if (!*string) | |
441 | + goto out; | |
442 | + while (*string) { | |
443 | + unsigned char c = *string++; | |
444 | + if (in_repetition && c == '/') | |
445 | + goto out; | |
446 | + if (c <= ' ' || c >= 127) | |
447 | + goto out; | |
448 | + if (c != '\\') | |
449 | + continue; | |
450 | + c = *string++; | |
451 | + if (c >= '0' && c <= '3') { | |
452 | + unsigned char d; | |
453 | + unsigned char e; | |
454 | + d = *string++; | |
455 | + if (d < '0' || d > '7') | |
456 | + goto out; | |
457 | + e = *string++; | |
458 | + if (e < '0' || e > '7') | |
459 | + goto out; | |
460 | + c = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0'); | |
461 | + if (c <= ' ' || c >= 127 || c == '\\') | |
462 | + continue; | |
463 | + goto out; | |
464 | + } | |
465 | + if (!allow_pattern) | |
466 | + goto out; | |
467 | + switch (c) { | |
468 | + case '+': /* "\+" */ | |
469 | + case '?': /* "\?" */ | |
470 | + case 'x': /* "\x" */ | |
471 | + case 'a': /* "\a" */ | |
472 | + case '-': /* "\-" */ | |
473 | + continue; | |
474 | + } | |
475 | + /* Reject too deep wildcard that consumes too much stack. */ | |
476 | + if (!recursion--) | |
477 | + goto out; | |
478 | + switch (c) { | |
479 | + case '*': /* "\*" */ | |
480 | + case '@': /* "\@" */ | |
481 | + case '$': /* "\$" */ | |
482 | + case 'X': /* "\X" */ | |
483 | + case 'A': /* "\A" */ | |
484 | + continue; | |
485 | + case '{': /* "/\{" */ | |
486 | + if (string - 3 < start || *(string - 3) != '/') | |
487 | + goto out; | |
488 | + in_repetition = 1; | |
489 | + continue; | |
490 | + case '}': /* "\}/" */ | |
491 | + if (in_repetition != 1 || *string++ != '/') | |
492 | + goto out; | |
493 | + in_repetition = 0; | |
494 | + continue; | |
495 | + case '(': /* "/\(" */ | |
496 | + if (string - 3 < start || *(string - 3) != '/') | |
497 | + goto out; | |
498 | + in_repetition = 2; | |
499 | + continue; | |
500 | + case ')': /* "\)/" */ | |
501 | + if (in_repetition != 2 || *string++ != '/') | |
502 | + goto out; | |
503 | + in_repetition = 0; | |
504 | + continue; | |
505 | + } | |
506 | + goto out; | |
507 | + } | |
508 | + if (in_repetition) | |
509 | + goto out; | |
510 | + return true; | |
511 | +out: | |
512 | + return false; | |
513 | +} | |
514 | + | |
515 | +/** | |
516 | + * cs_get_name - Allocate memory for string data. | |
517 | + * | |
518 | + * @name: The string to save. | |
519 | + * | |
520 | + * Returns pointer to "struct cs_path_info" on success, NULL otherwise. | |
521 | + */ | |
522 | +static struct cs_path_info *cs_get_name(const char *name) | |
523 | +{ | |
524 | + struct cs_path_info *ptr = | |
525 | + (struct cs_path_info *) malloc(sizeof(struct cs_path_info)); | |
526 | + if (!ptr) | |
527 | + out_of_memory(); | |
528 | + ptr->name = strdup(name); | |
529 | + if (!ptr->name) | |
530 | + out_of_memory(); | |
531 | + cs_fill_path_info(ptr); | |
532 | + return ptr; | |
533 | +} | |
534 | + | |
535 | +/** | |
536 | + * cs_put_name - Free memory for string data. | |
537 | + * | |
538 | + * @name: Pointer to "struct cs_path_info". Maybe NULL. | |
539 | + * | |
540 | + * Returns nothing. | |
541 | + */ | |
542 | +static void cs_put_name(struct cs_path_info *name) | |
543 | +{ | |
544 | + if (name) { | |
545 | + free((void *) name->name); | |
546 | + free(name); | |
547 | + } | |
548 | +} | |
549 | + | |
550 | +/** | |
551 | + * cs_normalize_line - Format string. | |
552 | + * | |
553 | + * @buffer: The line to normalize. | |
554 | + * | |
555 | + * Returns nothing. | |
556 | + * | |
557 | + * Leading and trailing whitespaces are removed. | |
558 | + * Multiple whitespaces are packed into single space. | |
559 | + */ | |
560 | +static void cs_normalize_line(char *buffer) | |
561 | +{ | |
562 | + unsigned char *sp = (unsigned char *) buffer; | |
563 | + unsigned char *dp = (unsigned char *) buffer; | |
564 | + bool first = true; | |
565 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
566 | + sp++; | |
567 | + while (*sp) { | |
568 | + if (!first) | |
569 | + *dp++ = ' '; | |
570 | + first = false; | |
571 | + while (*sp > ' ' && *sp < 127) | |
572 | + *dp++ = *sp++; | |
573 | + while (*sp && (*sp <= ' ' || *sp >= 127)) | |
574 | + sp++; | |
575 | + } | |
576 | + *dp = '\0'; | |
577 | +} | |
578 | + | |
579 | +static struct testcase { | |
580 | + const char *pathname; | |
581 | + const char *pattern; | |
582 | + _Bool match; | |
583 | +} testcases[] = { | |
584 | + { "/tmp/000", "/tmp/\\*", 1 }, | |
585 | + { "/tmp/000", "/tmp/\\@", 1 }, | |
586 | + { "/tmp/000", "/tmp/\\?\\?\\?", 1 }, | |
587 | + { "/tmp/000", "/tmp/\\*\\$\\@\\$", 1 }, | |
588 | + { "/tmp/000\\040111", "/tmp/\\*", 1 }, | |
589 | + { "/tmp/000\\040111", "/tmp/\\X", 0 }, | |
590 | + { "/tmp/000\\040111", "/tmp/\\$\\?\\$", 1 }, | |
591 | + { "/tmp/000111", "/tmp/\\(\\*\\)/\\*", 1 }, | |
592 | + { "/tmp/000/111", "/tmp/\\(\\*\\)/\\*", 1 }, | |
593 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\(\\*\\)/\\*", 1 }, | |
594 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\$\\}/\\$", 1 }, | |
595 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\a\\}/\\$", 0 }, | |
596 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\$\\-\\a\\}/\\$", 1 }, | |
597 | + { "/tmp/0/0/0/1/1/1", "/tmp/\\{\\x\\-\\a\\}/\\$", 1 }, | |
598 | + { "/tmp/\\001/\\002/\\040/^/$", "/tmp/\\{\\*\\-\\a\\}/\\?", 1 }, | |
599 | + { "/tmp/\\001/\\002/\\040/^/$", "/tmp/\\{\\*\\-\\a\\-\\x\\}/\\?", 1 }, | |
600 | + { "/tmp/$", "/tmp/\\*\\-\\a\\-\\x", 1 }, | |
601 | + { "/bin/true", "/bin/\\*", 1 }, | |
602 | + { "/bin/true", "/bin\\@\\*/\\*", 1 }, | |
603 | + { "/usr/local/", "/usr/\\*/", 1 }, | |
604 | + { "/usr/local/", "/usr/\\*\\*\\@\\*/", 1 }, | |
605 | + { "pipe:[12345]", "pipe:[\\$]", 1 }, | |
606 | + { "socket:[family=1:type=2:protocol=3]", "socket:[family=1:type=2:protocol=\\$]", 1 }, | |
607 | + { "http://tomoyo.osdn.jp/", "\\*/\\*/\\*/", 1 }, | |
608 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\*", 1 }, | |
609 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\*\\*\\@\\*\\@", 1 }, | |
610 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\@\\*/\\*\\@/\\*\\@\\*\\@\\*", 1 }, | |
611 | + { "http://tomoyo.osdn.jp/1.8/index.html", "http://\\{\\*\\}/\\@.html", 1 }, | |
612 | + { "http://tomoyo.osdn.jp/index.html", "\\*://\\@.osdn.jp/\\*", 1 }, | |
613 | + { "http://tomoyo.osdn.jp/index.html", "\\*://\\@.osdn.jp/\\*", 1 }, | |
614 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-patch/security/ccsecurity/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, | |
615 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-patch/security/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, | |
616 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x/ccs-patch/?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\}/?root=tomoyo", 1 }, | |
617 | + { "http://osdn.jp/projects/tomoyo/svn/view/trunk/1.8.x//ccs-patch///security//ccsecurity///?root=tomoyo", "\\*://\\@osdn.jp/\\{\\*\\-.\\-..\\-\\*%\\*\\}/?root=tomoyo\\*\\*", 1 }, | |
618 | + { "/var/www/html/test/test/test/index.html", "/var/www/html/\\{test\\}/\\*.html", 1 }, | |
619 | + { "/etc/skel/", "/etc/\\{\\*\\}/\\*/", 0 }, | |
620 | + { "/etc/passwd", "/etc/\\{\\*\\}/\\*", 0 }, | |
621 | + { "/bin/true", "/bin/\\*/", 0 }, | |
622 | + { "/bin/", "/bin/\\*", 1 }, | |
623 | + { "/bin/", "/bin/\\@", 1 }, | |
624 | + { "/bin/", "/bin/\\@\\@", 1 }, | |
625 | + { "http://tomoyo.osdn.jp/", "\\*/\\*/\\*/\\?", 0 }, | |
626 | + { "http://tomoyo.osdn.jp/index.html", "\\*/\\*/\\*/\\@", 0 }, | |
627 | + { "http://tomoyo.osdn.jp/index.html", "http://\\*/\\@", 0 }, | |
628 | + { "socket:[family=1:type=2:protocol=3]", "/\\{\\*\\}/socket:[\\*]", 0 }, | |
629 | + { "/", "/\\(\\*\\)/\\*", 1 }, | |
630 | + { "/", "/\\{\\*\\}/\\*", 0 }, | |
631 | + { "/foo/", "/foo/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\*", 1 }, | |
632 | + { "/foo/", "/foo/\\{\\*\\}/\\*", 0 }, | |
633 | + { "/foo/bar/", "/foo/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\*", 1 }, | |
634 | + { "/foo/bar/", "/foo/\\{\\*\\}/\\*", 1 }, | |
635 | + { "/foo/bar", "/foo/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/bar", 1 }, | |
636 | + { "/foo/bar", "/foo/\\{\\*\\}/bar", 0 }, | |
637 | + { "/foo/bar", "/foo/\\*/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\(\\*\\)/\\*", 0 }, | |
638 | + { "/foo/bar/", "/foo/\\(\\*\\)/", 1 }, | |
639 | + { "/foo/bar/", "/foo/\\*/\\(\\*\\)/", 1 }, | |
640 | + { "/foo/bar/", "/foo/\\*/\\*/\\(\\*\\)/", 0 }, | |
641 | + { "/foo/bar/", "/foo/\\*/\\(\\*\\)/\\*/", 0 }, | |
642 | + { "/foo/", "/foo/\\(\\*\\)/", 1 }, | |
643 | + { "abc", "abc", 1 }, | |
644 | + { "abc", "\\A\\A\\A", 1 }, | |
645 | + { "abc", "\\X\\X\\X", 1 }, | |
646 | + { "abc", "\\*\\*\\*", 1 }, | |
647 | + { "abc", "\\@\\@\\@", 1 }, | |
648 | + { "abc", "\\a\\@\\x", 1 }, | |
649 | + { "abc", "\\?\\?\\?", 1 }, | |
650 | + { "abc", "\\?\\@\\?\\*", 1 }, | |
651 | + { "abc", "\\?\\@\\?\\*\\?", 1 }, | |
652 | + { "abc", "def", 0 }, | |
653 | + { "abc/def", "\\*/\\(\\X\\)/\\X", 1 }, | |
654 | + { "abc/def", "\\*/\\{\\X\\}/\\X", 0 }, | |
655 | + { "abc/def/012", "\\*/\\(\\X\\)/\\X", 1 }, | |
656 | + { "abc/def/012", "\\*/\\{\\X\\}/\\X", 1 }, | |
657 | + { "abc/def/012/345/6789", "\\*/\\(\\X\\)/\\X", 1 }, | |
658 | + { "abc/def/012/345/6789", "\\*/\\{\\X\\}/\\X", 1 }, | |
659 | + { "abc/345/012/def/6789", "\\*/\\(\\$\\)/\\X", 0 }, | |
660 | + { "abc/345/012/def/6789", "\\*/\\(\\*\\)/\\*/", 0 }, | |
661 | + { "abc/345/012/def/6789/////1//23///", "\\*/\\(\\*\\)/\\*/", 1 }, | |
662 | + { "abc/345/012/def/6789/////1//23//./", "\\*/\\(\\*\\)/\\?/", 1 }, | |
663 | + { "abc/345/012/def/6789//1//23//.", "\\*/\\(\\*\\)/\\?/", 0 }, | |
664 | + { "abc/345/012/def/6789//1//23//", "\\*/\\(\\*\\)/\\?", 0 }, | |
665 | + { "abc", "abc/\\*", 0 }, | |
666 | + { "abc/", "abc/\\*", 1 }, | |
667 | + { NULL, NULL, 0 }, | |
668 | +}; | |
669 | + | |
670 | +int main(int argc, char *argv[]) | |
671 | +{ | |
672 | + struct testcase *ptr; | |
673 | + struct cs_path_info *path; | |
674 | + struct cs_path_info *pattern; | |
675 | + for (ptr = testcases; ptr->pathname; ptr++) { | |
676 | + if (!cs_correct_word(ptr->pathname, 0)) { | |
677 | + printf("Bad path: %s\n", ptr->pathname); | |
678 | + continue; | |
679 | + } else if (!cs_correct_word(ptr->pattern, 1)) { | |
680 | + printf("Bad pattern: %s\n", ptr->pattern); | |
681 | + continue; | |
682 | + } | |
683 | + path = cs_get_name(ptr->pathname); | |
684 | + pattern = cs_get_name(ptr->pattern); | |
685 | + if (cs_path_matches_pattern(path, pattern) != ptr->match) | |
686 | + printf("Failed (\"%s\", \"%s\") == %d\n", | |
687 | + ptr->pathname, ptr->pattern, ptr->match); | |
688 | + cs_put_name(path); | |
689 | + cs_put_name(pattern); | |
690 | + } | |
691 | + return 0; | |
692 | +} |
@@ -0,0 +1,218 @@ | ||
1 | +/* | |
2 | + * caitsith-init.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is executed automatically by kernel | |
11 | + * when execution of /sbin/init is requested. | |
12 | + * | |
13 | + * This program is free software; you can redistribute it and/or modify it | |
14 | + * under the terms of the GNU General Public License v2 as published by the | |
15 | + * Free Software Foundation. | |
16 | + * | |
17 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
18 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
20 | + * more details. | |
21 | + * | |
22 | + * You should have received a copy of the GNU General Public License along with | |
23 | + * this program; if not, write to the Free Software Foundation, Inc., | |
24 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
25 | + */ | |
26 | +#define _FILE_OFFSET_BITS 64 | |
27 | +#define _LARGEFILE_SOURCE | |
28 | +#define _LARGEFILE64_SOURCE | |
29 | +#include <stdio.h> | |
30 | +#include <string.h> | |
31 | +#include <stdlib.h> | |
32 | +#include <sys/types.h> | |
33 | +#include <sys/stat.h> | |
34 | +#include <sys/wait.h> | |
35 | +#include <sys/mount.h> | |
36 | +#include <fcntl.h> | |
37 | +#include <unistd.h> | |
38 | +#include <dirent.h> | |
39 | +#include <limits.h> | |
40 | +#include <sys/vfs.h> | |
41 | +#include <errno.h> | |
42 | + | |
43 | +static void panic(void) | |
44 | +{ | |
45 | + printf("Fatal error while loading policy.\n"); | |
46 | + fflush(stdout); | |
47 | + while (1) | |
48 | + sleep(100); | |
49 | +} | |
50 | + | |
51 | +#define policy_dir "/etc/caitsith/policy" | |
52 | +static const char *proc_policy = "/sys/kernel/security/caitsith/policy"; | |
53 | +static _Bool proc_unmount = 0; | |
54 | +static _Bool sys_unmount = 0; | |
55 | +static _Bool security_unmount = 0; | |
56 | + | |
57 | +static char buffer[8192]; | |
58 | + | |
59 | +static void copy_files(const char *src, const char *dest) | |
60 | +{ | |
61 | + int sfd; | |
62 | + int dfd = open(dest, O_WRONLY); | |
63 | + if (dfd == EOF) { | |
64 | + if (errno != ENOENT) | |
65 | + panic(); | |
66 | + return; | |
67 | + } | |
68 | + sfd = open(src, O_RDONLY); | |
69 | + if (sfd != EOF) { | |
70 | + while (1) { | |
71 | + int ret_ignored; | |
72 | + int len = read(sfd, buffer, sizeof(buffer)); | |
73 | + if (len <= 0) | |
74 | + break; | |
75 | + ret_ignored = write(dfd, buffer, len); | |
76 | + } | |
77 | + close(sfd); | |
78 | + } | |
79 | + close(dfd); | |
80 | +} | |
81 | + | |
82 | +static void show_stat(void) | |
83 | +{ | |
84 | + unsigned int acl = 0; | |
85 | + unsigned int size = -1; | |
86 | + FILE *fp = fopen(proc_policy, "r"); | |
87 | + if (!fp) | |
88 | + return; | |
89 | + while (memset(buffer, 0, sizeof(buffer)) && | |
90 | + fgets(buffer, sizeof(buffer) - 1, fp)) { | |
91 | + unsigned int priority; | |
92 | + unsigned char operation; | |
93 | + if (sscanf(buffer, "%u acl %c", &priority, &operation) == 2) | |
94 | + acl++; | |
95 | + else if (size == -1) | |
96 | + sscanf(buffer, "stat Memory used by policy: %u", | |
97 | + &size); | |
98 | + } | |
99 | + fclose(fp); | |
100 | + printf("%u ACL entr%s.\n", acl, acl > 1 ? "ies" : "y"); | |
101 | + if (size != -1) | |
102 | + printf("%u KB used by policy.\n", (size + 1023) / 1024); | |
103 | +} | |
104 | + | |
105 | +int main(int argc, char *argv[]) | |
106 | +{ | |
107 | + struct stat buf; | |
108 | + | |
109 | + /* Mount /proc if not mounted. */ | |
110 | + if (lstat("/proc/self/", &buf) || !S_ISDIR(buf.st_mode)) | |
111 | + proc_unmount = !mount("/proc", "/proc/", "proc", 0, NULL); | |
112 | + /* Mount /sys if not mounted. */ | |
113 | + if (lstat("/sys/kernel/security/", &buf) || !S_ISDIR(buf.st_mode)) | |
114 | + sys_unmount = !mount("/sys", "/sys", "sysfs", 0, NULL); | |
115 | + /* Mount /sys/kernel/security if not mounted. */ | |
116 | + if (lstat("/sys/kernel/security/caitsith/", &buf) || | |
117 | + !S_ISDIR(buf.st_mode)) | |
118 | + security_unmount = !mount("none", "/sys/kernel/security", | |
119 | + "securityfs", 0, NULL); | |
120 | + | |
121 | + /* | |
122 | + * Open /dev/console if stdio are not connected. | |
123 | + * | |
124 | + * WARNING: Don't let this program be invoked implicitly | |
125 | + * if you are not operating from console. | |
126 | + * Otherwise, you will get unable to respond to prompt | |
127 | + * if something went wrong. | |
128 | + */ | |
129 | + if (access("/proc/self/fd/0", R_OK)) { | |
130 | + close(0); | |
131 | + close(1); | |
132 | + close(2); | |
133 | + open("/dev/console", O_RDONLY); | |
134 | + open("/dev/console", O_WRONLY); | |
135 | + open("/dev/console", O_WRONLY); | |
136 | + } | |
137 | + | |
138 | + /* Load kernel module if needed. */ | |
139 | + if (lstat(proc_policy, &buf) && lstat("/proc/caitsith", &buf)) { | |
140 | + if (!access("/etc/caitsith/caitsith-load-module", X_OK)) { | |
141 | + const pid_t pid = fork(); | |
142 | + switch (pid) { | |
143 | + case 0: | |
144 | + execl("/etc/caitsith/caitsith-load-module", | |
145 | + "/etc/caitsith/caitsith-load-module", | |
146 | + NULL); | |
147 | + _exit(0); | |
148 | + case -1: | |
149 | + panic(); | |
150 | + } | |
151 | + while (waitpid(pid, NULL, __WALL) == EOF && | |
152 | + errno == EINTR); | |
153 | + } | |
154 | + } | |
155 | + | |
156 | + /* Try proc interface if securityfs interface does not exist. */ | |
157 | + if (lstat(proc_policy, &buf) || !S_ISREG(buf.st_mode)) | |
158 | + proc_policy = "/proc/caitsith/policy"; | |
159 | + | |
160 | + /* Stop if policy interface doesn't exist. */ | |
161 | + if (lstat(proc_policy, &buf) || !S_ISREG(buf.st_mode)) { | |
162 | + printf("FATAL: Policy interface %s does not exist.\n", | |
163 | + proc_policy); | |
164 | + fflush(stdout); | |
165 | + while (1) | |
166 | + sleep(100); | |
167 | + } | |
168 | + | |
169 | + /* | |
170 | + * Unmount and execute /sbin/init if this program was executed by | |
171 | + * passing init=/sbin/caitsith-init . The kernel will try to execute | |
172 | + * this program again with getpid() != 1 when /sbin/init starts. | |
173 | + */ | |
174 | + if (getpid() == 1) { | |
175 | + if (security_unmount) | |
176 | + umount("/sys/kernel/security/"); | |
177 | + if (sys_unmount) | |
178 | + umount("/sys/"); | |
179 | + if (proc_unmount) | |
180 | + umount("/proc/"); | |
181 | + argv[0] = "/sbin/init"; | |
182 | + execv(argv[0], argv); | |
183 | + printf("FATAL: Failed to execute %s\n", argv[0]); | |
184 | + fflush(stdout); | |
185 | + while (1) | |
186 | + sleep(100); | |
187 | + } | |
188 | + | |
189 | + /* Load policy. */ | |
190 | + if (!chdir(policy_dir)) | |
191 | + copy_files("current", proc_policy); | |
192 | + | |
193 | + /* Do additional initialization. */ | |
194 | + if (!access("/etc/caitsith/caitsith-post-init", X_OK)) { | |
195 | + const pid_t pid = fork(); | |
196 | + switch (pid) { | |
197 | + case 0: | |
198 | + execl("/etc/caitsith/caitsith-post-init", | |
199 | + "/etc/caitsith/caitsith-post-init", NULL); | |
200 | + _exit(0); | |
201 | + case -1: | |
202 | + panic(); | |
203 | + } | |
204 | + while (waitpid(pid, NULL, __WALL) == EOF && | |
205 | + errno == EINTR); | |
206 | + } | |
207 | + | |
208 | + show_stat(); | |
209 | + | |
210 | + if (security_unmount) | |
211 | + umount("/sys/kernel/security/"); | |
212 | + if (sys_unmount) | |
213 | + umount("/sys/"); | |
214 | + if (proc_unmount) | |
215 | + umount("/proc"); | |
216 | + | |
217 | + return 0; | |
218 | +} |
@@ -0,0 +1,17 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +BUILD_FILES = caitsith-init | |
4 | + | |
5 | +all: $(BUILD_FILES) | |
6 | + | |
7 | +install: all | |
8 | + mkdir -p -m 0755 $(INSTALLDIR)$(SBINDIR) | |
9 | + $(INSTALL) -m 0700 $(BUILD_FILES) $(INSTALLDIR)$(SBINDIR) | |
10 | + | |
11 | +.c: | |
12 | + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< | |
13 | + | |
14 | +clean: | |
15 | + rm -f -- $(BUILD_FILES) | |
16 | + | |
17 | +.PHONY: clean install |
@@ -0,0 +1,87 @@ | ||
1 | +/* | |
2 | + * audit-exec-param.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#include <stdio.h> | |
24 | +#include <stdlib.h> | |
25 | +#include <unistd.h> | |
26 | +#include <syslog.h> | |
27 | +#include <string.h> | |
28 | +#include <sys/types.h> | |
29 | +#include <sys/stat.h> | |
30 | +#include <fcntl.h> | |
31 | +#include <errno.h> | |
32 | + | |
33 | +int main(int raw_argc, char *raw_argv[]) | |
34 | +{ | |
35 | + int i; | |
36 | + int argc; | |
37 | + int envc; | |
38 | + char *filename; | |
39 | + char **argv; | |
40 | + char **envp; | |
41 | + if (1) { | |
42 | + int fd = open("/proc/caitsith/.execute_handler", 0); | |
43 | + close(fd); | |
44 | + if (fd == EOF && errno != ENOENT) { | |
45 | + fprintf(stderr, "FATAL: I'm not execute_handler.\n"); | |
46 | + return 1; | |
47 | + } | |
48 | + } | |
49 | + if (raw_argc < 7) | |
50 | + return 1; | |
51 | + filename = raw_argv[4]; | |
52 | + argc = atoi(raw_argv[5]); | |
53 | + envc = atoi(raw_argv[6]); | |
54 | + if (raw_argc != argc + envc + 7) | |
55 | + return 1; | |
56 | + for (i = 5; i < argc + 5; i++) | |
57 | + raw_argv[i] = raw_argv[i + 2]; | |
58 | + raw_argv[argc + 5] = NULL; | |
59 | + for (i = argc + 6; i < argc + envc + 6; i++) | |
60 | + raw_argv[i] = raw_argv[i + 1]; | |
61 | + raw_argv[argc + envc + 6] = NULL; | |
62 | + argv = raw_argv + 5; | |
63 | + envp = raw_argv + argc + 6; | |
64 | + /* | |
65 | + * Check parameters passed to execve() request. | |
66 | + */ | |
67 | + if (1) { | |
68 | + openlog(raw_argv[0], LOG_NDELAY, LOG_USER); | |
69 | + syslog(LOG_INFO, "Domain = %s\n", raw_argv[1]); | |
70 | + syslog(LOG_INFO, "Caller Program = %s\n", raw_argv[2]); | |
71 | + syslog(LOG_INFO, "Process Status = %s\n", raw_argv[3]); | |
72 | + syslog(LOG_INFO, "Requested Program = %s\n", filename); | |
73 | + syslog(LOG_INFO, "argc=%d\n", argc); | |
74 | + syslog(LOG_INFO, "envc=%d\n", envc); | |
75 | + for (i = 0; i < argc; i++) | |
76 | + syslog(LOG_INFO, "argv[%d] = %s\n", i, argv[i]); | |
77 | + for (i = 0; i < envc; i++) | |
78 | + syslog(LOG_INFO, "envp[%d] = %s\n", i, envp[i]); | |
79 | + closelog(); | |
80 | + } | |
81 | + /* | |
82 | + * Continue if filename and argv[] and envp[] are appropriate. | |
83 | + */ | |
84 | + if (1) | |
85 | + execve(filename, argv, envp); | |
86 | + return 1; | |
87 | +} |
@@ -0,0 +1,385 @@ | ||
1 | +/* | |
2 | + * caitsith-agent.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#define _GNU_SOURCE | |
24 | +#include <stdio.h> | |
25 | +#include <string.h> | |
26 | +#include <stdlib.h> | |
27 | +#include <sys/types.h> | |
28 | +#include <sys/socket.h> | |
29 | +#include <netinet/in.h> | |
30 | +#include <arpa/inet.h> | |
31 | +#include <sys/stat.h> | |
32 | +#include <fcntl.h> | |
33 | +#include <unistd.h> | |
34 | +#include <poll.h> | |
35 | +#include <signal.h> | |
36 | +#include <dirent.h> | |
37 | +#include <sys/mount.h> | |
38 | +#include <sched.h> | |
39 | + | |
40 | +static _Bool wait_data(const int fd) | |
41 | +{ | |
42 | + struct pollfd pfd = { .fd = fd, .events = POLLIN}; | |
43 | + poll(&pfd, 1, -1); | |
44 | + return 1; | |
45 | +} | |
46 | + | |
47 | +static void show_tasklist(FILE *fp, const _Bool show_all) | |
48 | +{ | |
49 | + int status_fd = open(".process_status", O_RDWR); | |
50 | + DIR *dir = opendir("/proc/"); | |
51 | + if (status_fd == EOF || !dir) { | |
52 | + if (status_fd != EOF) | |
53 | + close(status_fd); | |
54 | + if (dir) | |
55 | + closedir(dir); | |
56 | + return; | |
57 | + } | |
58 | + fputc(0, fp); | |
59 | + while (1) { | |
60 | + int ret_ignored; | |
61 | + FILE *status_fp; | |
62 | + pid_t ppid = 1; | |
63 | + char *name = NULL; | |
64 | + char buffer[1024]; | |
65 | + char test[16]; | |
66 | + unsigned int pid; | |
67 | + struct dirent *dent = readdir(dir); | |
68 | + if (!dent) | |
69 | + break; | |
70 | + if (dent->d_type != DT_DIR || | |
71 | + sscanf(dent->d_name, "%u", &pid) != 1 || !pid) | |
72 | + continue; | |
73 | + memset(buffer, 0, sizeof(buffer)); | |
74 | + if (!show_all) { | |
75 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/exe", | |
76 | + pid); | |
77 | + if (readlink(buffer, test, sizeof(test)) <= 0) | |
78 | + continue; | |
79 | + } | |
80 | + snprintf(buffer, sizeof(buffer) - 1, "/proc/%u/status", pid); | |
81 | + status_fp = fopen(buffer, "r"); | |
82 | + if (status_fp) { | |
83 | + while (memset(buffer, 0, sizeof(buffer)) && | |
84 | + fgets(buffer, sizeof(buffer) - 1, status_fp)) { | |
85 | + if (!strncmp(buffer, "Name:\t", 6)) { | |
86 | + char *cp = buffer + 6; | |
87 | + memmove(buffer, cp, strlen(cp) + 1); | |
88 | + cp = strchr(buffer, '\n'); | |
89 | + if (cp) | |
90 | + *cp = '\0'; | |
91 | + name = strdup(buffer); | |
92 | + } | |
93 | + if (sscanf(buffer, "PPid: %u", &ppid) == 1) | |
94 | + break; | |
95 | + } | |
96 | + fclose(status_fp); | |
97 | + } | |
98 | + snprintf(buffer, sizeof(buffer) - 1, "%u\n", pid); | |
99 | + ret_ignored = write(status_fd, buffer, strlen(buffer)); | |
100 | + memset(buffer, 0, sizeof(buffer)); | |
101 | + ret_ignored = read(status_fd, buffer, sizeof(buffer)); | |
102 | + if (!buffer[0]) | |
103 | + continue; | |
104 | + fprintf(fp, "PID=%u PPID=%u NAME=", pid, ppid); | |
105 | + if (name) { | |
106 | + const char *cp = name; | |
107 | + while (1) { | |
108 | + unsigned char c = *cp++; | |
109 | + if (!c) | |
110 | + break; | |
111 | + if (c == '\\') { | |
112 | + c = *cp++; | |
113 | + if (c == '\\') | |
114 | + fprintf(fp, "\\\\"); | |
115 | + else if (c == 'n') | |
116 | + fprintf(fp, "\\012"); | |
117 | + else | |
118 | + break; | |
119 | + } else if (c > ' ' && c <= 126) { | |
120 | + fputc(c, fp); | |
121 | + } else { | |
122 | + fprintf(fp, "\\%c%c%c", | |
123 | + (c >> 6) + '0', | |
124 | + ((c >> 3) & 7) + '0', | |
125 | + (c & 7) + '0'); | |
126 | + } | |
127 | + } | |
128 | + free(name); | |
129 | + } else { | |
130 | + fprintf(fp, "<UNKNOWN>"); | |
131 | + } | |
132 | + fputc('\n', fp); | |
133 | + ret_ignored = fwrite(buffer, strlen(buffer), 1, fp); | |
134 | + while (1) { | |
135 | + int len = read(status_fd, buffer, sizeof(buffer)); | |
136 | + if (len <= 0) | |
137 | + break; | |
138 | + ret_ignored = fwrite(buffer, len, 1, fp); | |
139 | + } | |
140 | + fputc('\n', fp); | |
141 | + } | |
142 | + fputc(0, fp); | |
143 | + closedir(dir); | |
144 | + close(status_fd); | |
145 | + fflush(fp); | |
146 | +} | |
147 | + | |
148 | +static void handle_audit(const int client) | |
149 | +{ | |
150 | + int ret_ignored; | |
151 | + const int fd = open("audit", O_RDONLY); | |
152 | + if (fd == EOF) | |
153 | + return; | |
154 | + /* Return \0 to indicate success. */ | |
155 | + ret_ignored = write(client, "", 1); | |
156 | + while (wait_data(fd)) { | |
157 | + char buffer[4096]; | |
158 | + const int len = read(fd, buffer, sizeof(buffer)); | |
159 | + if (!len) | |
160 | + continue; | |
161 | + if (len == EOF || write(client, buffer, len) != len) | |
162 | + break; | |
163 | + } | |
164 | + close(fd); | |
165 | +} | |
166 | + | |
167 | +static void handle_query(const int client) | |
168 | +{ | |
169 | + int ret_ignored; | |
170 | + const int fd = open("query", O_RDWR); | |
171 | + if (fd == EOF) | |
172 | + return; | |
173 | + /* Return \0 to indicate success. */ | |
174 | + ret_ignored = write(client, "", 1); | |
175 | + while (wait_data(client)) { | |
176 | + char buffer[4096]; | |
177 | + int len = recv(client, buffer, sizeof(buffer), MSG_DONTWAIT); | |
178 | + int nonzero_len; | |
179 | + if (len <= 0) | |
180 | + break; | |
181 | +restart: | |
182 | + for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) | |
183 | + if (!buffer[nonzero_len]) | |
184 | + break; | |
185 | + if (nonzero_len) { | |
186 | + if (write(fd, buffer, nonzero_len) != nonzero_len) | |
187 | + break; | |
188 | + } else { | |
189 | + while (wait_data(fd)) { | |
190 | + char buffer2[4096]; | |
191 | + const int len = read(fd, buffer2, | |
192 | + sizeof(buffer2)); | |
193 | + if (!len) | |
194 | + continue; | |
195 | + if (len == EOF || | |
196 | + write(client, buffer2, len) != len) { | |
197 | + shutdown(client, SHUT_RDWR); | |
198 | + break; | |
199 | + } | |
200 | + if (!buffer2[len - 1]) | |
201 | + break; | |
202 | + } | |
203 | + nonzero_len = 1; | |
204 | + } | |
205 | + len -= nonzero_len; | |
206 | + memmove(buffer, buffer + nonzero_len, len); | |
207 | + if (len) | |
208 | + goto restart; | |
209 | + } | |
210 | + close(fd); | |
211 | +} | |
212 | + | |
213 | +static _Bool verbose = 0; | |
214 | + | |
215 | +static void handle_policy(const int client, const char *filename) | |
216 | +{ | |
217 | + int ret_ignored; | |
218 | + char *cp = strrchr(filename, '/'); | |
219 | + int fd = open(cp ? cp + 1 : filename, O_RDWR); | |
220 | + if (fd == EOF) | |
221 | + goto out; | |
222 | + /* Return \0 to indicate success. */ | |
223 | + if (write(client, "", 1) != 1) | |
224 | + goto out; | |
225 | + if (verbose) { | |
226 | + ret_ignored = write(2, "opened ", 7); | |
227 | + ret_ignored = write(2, filename, strlen(filename)); | |
228 | + ret_ignored = write(2, "\n", 1); | |
229 | + } | |
230 | + while (wait_data(client)) { | |
231 | + char buffer[4096]; | |
232 | + int len = recv(client, buffer, sizeof(buffer), MSG_DONTWAIT); | |
233 | + int nonzero_len; | |
234 | + if (len <= 0) | |
235 | + break; | |
236 | +restart: | |
237 | + for (nonzero_len = 0 ; nonzero_len < len; nonzero_len++) | |
238 | + if (!buffer[nonzero_len]) | |
239 | + break; | |
240 | + if (nonzero_len) { | |
241 | + if (write(fd, buffer, nonzero_len) != nonzero_len) | |
242 | + break; | |
243 | + if (verbose) | |
244 | + ret_ignored = write(1, buffer, nonzero_len); | |
245 | + } else { | |
246 | + while (1) { | |
247 | + char buffer2[4096]; | |
248 | + const int len = read(fd, buffer2, | |
249 | + sizeof(buffer2)); | |
250 | + if (len == 0) | |
251 | + break; | |
252 | + /* Don't send \0 because it is EOF marker. */ | |
253 | + if (len < 0 || memchr(buffer2, '\0', len) || | |
254 | + write(client, buffer2, len) != len) | |
255 | + goto out; | |
256 | + } | |
257 | + /* Return \0 to indicate EOF. */ | |
258 | + if (write(client, "", 1) != 1) | |
259 | + goto out; | |
260 | + nonzero_len = 1; | |
261 | + } | |
262 | + len -= nonzero_len; | |
263 | + memmove(buffer, buffer + nonzero_len, len); | |
264 | + if (len) | |
265 | + goto restart; | |
266 | + } | |
267 | +out: | |
268 | + if (verbose) | |
269 | + ret_ignored = write(2, "disconnected\n", 13); | |
270 | +} | |
271 | + | |
272 | +static void do_child(const int client) | |
273 | +{ | |
274 | + int i; | |
275 | + char buffer[1024]; | |
276 | + /* Read filename. */ | |
277 | + for (i = 0; i < sizeof(buffer); i++) { | |
278 | + if (read(client, buffer + i, 1) != 1) | |
279 | + goto out; | |
280 | + if (!buffer[i]) | |
281 | + break; | |
282 | + } | |
283 | + if (!memchr(buffer, '\0', sizeof(buffer))) | |
284 | + goto out; | |
285 | + if (!strcmp(buffer, "proc:query")) | |
286 | + handle_query(client); | |
287 | + else if (!strcmp(buffer, "proc:audit")) | |
288 | + handle_audit(client); | |
289 | + else if (!strncmp(buffer, "proc:", 5)) { | |
290 | + /* Open /proc/\$/ for reading. */ | |
291 | + FILE *fp = fdopen(client, "w"); | |
292 | + if (fp) { | |
293 | + show_tasklist(fp, !strcmp(buffer + 5, | |
294 | + "all_process_status")); | |
295 | + fclose(fp); | |
296 | + } | |
297 | + } else | |
298 | + handle_policy(client, buffer); | |
299 | +out: | |
300 | + close(client); | |
301 | +} | |
302 | + | |
303 | +int main(int argc, char *argv[]) | |
304 | +{ | |
305 | + const int listener = socket(AF_INET, SOCK_STREAM, 0); | |
306 | + struct sockaddr_in addr; | |
307 | + socklen_t size = sizeof(addr); | |
308 | + char *port; | |
309 | + if (chdir("/proc/caitsith/") && | |
310 | + chdir("/sys/kernel/security/caitsith/") && | |
311 | + (unshare(CLONE_NEWNS) || | |
312 | + mount("none", "/sys/kernel/security/", "securityfs", 0, NULL) || | |
313 | + chdir("/sys/kernel/security/caitsith/"))) | |
314 | + return 1; | |
315 | + { | |
316 | + int i; | |
317 | + for (i = 1; i < argc; i++) { | |
318 | + if (strcmp(argv[i], "--verbose")) | |
319 | + continue; | |
320 | + verbose = 1; | |
321 | + argc--; | |
322 | + for (; i < argc; i++) | |
323 | + argv[i] = argv[i + 1]; | |
324 | + break; | |
325 | + } | |
326 | + } | |
327 | + if (argc != 2) { | |
328 | +usage: | |
329 | + fprintf(stderr, "%s listen_address:listen_port\n", argv[0]); | |
330 | + return 1; | |
331 | + } | |
332 | + port = strchr(argv[1], ':'); | |
333 | + if (!port) | |
334 | + goto usage; | |
335 | + *port++ = '\0'; | |
336 | + memset(&addr, 0, sizeof(addr)); | |
337 | + addr.sin_family = AF_INET; | |
338 | + addr.sin_addr.s_addr = inet_addr(argv[1]); | |
339 | + addr.sin_port = htons(atoi(port)); | |
340 | + if (bind(listener, (struct sockaddr *) &addr, sizeof(addr)) || | |
341 | + listen(listener, 5) || | |
342 | + getsockname(listener, (struct sockaddr *) &addr, &size)) { | |
343 | + close(listener); | |
344 | + return 1; | |
345 | + } | |
346 | + { | |
347 | + const unsigned int ip = ntohl(addr.sin_addr.s_addr); | |
348 | + printf("Listening at %u.%u.%u.%u:%u\n", | |
349 | + (unsigned char) (ip >> 24), (unsigned char) (ip >> 16), | |
350 | + (unsigned char) (ip >> 8), (unsigned char) ip, | |
351 | + ntohs(addr.sin_port)); | |
352 | + fflush(stdout); | |
353 | + } | |
354 | + close(0); | |
355 | + if (!verbose) { | |
356 | + close(1); | |
357 | + close(2); | |
358 | + } | |
359 | + signal(SIGCHLD, SIG_IGN); | |
360 | + while (1) { | |
361 | + socklen_t size = sizeof(addr); | |
362 | + const int client = accept(listener, (struct sockaddr *) &addr, | |
363 | + &size); | |
364 | + if (client == EOF) { | |
365 | + if (verbose) | |
366 | + fprintf(stderr, "accept() failed\n"); | |
367 | + continue; | |
368 | + } | |
369 | + switch (fork()) { | |
370 | + case 0: | |
371 | + close(listener); | |
372 | + do_child(client); | |
373 | + _exit(0); | |
374 | + case -1: | |
375 | + if (verbose) | |
376 | + fprintf(stderr, "fork() failed\n"); | |
377 | + close(client); | |
378 | + break; | |
379 | + default: | |
380 | + close(client); | |
381 | + } | |
382 | + } | |
383 | + close(listener); | |
384 | + return 1; | |
385 | +} |
@@ -0,0 +1,709 @@ | ||
1 | +/* | |
2 | + * init_policy.c | |
3 | + * | |
4 | + * CaitSith's utilities. | |
5 | + * | |
6 | + * Copyright (C) 2005-2012 NTT DATA CORPORATION | |
7 | + * | |
8 | + * Version: 0.2 2016/10/05 | |
9 | + * | |
10 | + * This program is free software; you can redistribute it and/or modify it | |
11 | + * under the terms of the GNU General Public License v2 as published by the | |
12 | + * Free Software Foundation. | |
13 | + * | |
14 | + * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | + * more details. | |
18 | + * | |
19 | + * You should have received a copy of the GNU General Public License along with | |
20 | + * this program; if not, write to the Free Software Foundation, Inc., | |
21 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA | |
22 | + */ | |
23 | +#define _FILE_OFFSET_BITS 64 | |
24 | +#define _LARGEFILE_SOURCE | |
25 | +#define _LARGEFILE64_SOURCE | |
26 | +#include <stdio.h> | |
27 | +#include <string.h> | |
28 | +#include <stdlib.h> | |
29 | +#include <sys/types.h> | |
30 | +#include <sys/stat.h> | |
31 | +#include <unistd.h> | |
32 | +#include <dirent.h> | |
33 | +#include <limits.h> | |
34 | +#include <sys/vfs.h> | |
35 | +#include <time.h> | |
36 | +#include <errno.h> | |
37 | + | |
38 | +#if defined(__GLIBC__) | |
39 | +/** | |
40 | + * get_realpath - Wrapper for realpath(3). | |
41 | + * | |
42 | + * @path: Pathname to resolve. | |
43 | + * | |
44 | + * Returns realpath of @path on success, NULL otherwise. | |
45 | + * | |
46 | + * Caller must free() the returned pointer if this function did not return | |
47 | + * NULL. | |
48 | + */ | |
49 | +static inline char *get_realpath(const char *path) | |
50 | +{ | |
51 | + return realpath(path, NULL); | |
52 | +} | |
53 | +#else | |
54 | +/** | |
55 | + * get_realpath - Fallback routine for realpath(3). | |
56 | + * | |
57 | + * @path: Pathname to resolve. | |
58 | + * | |
59 | + * Returns realpath of @path on success, NULL otherwise. | |
60 | + * | |
61 | + * realpath(@path, NULL) works on GLIBC, but will SIGSEGV on others. | |
62 | + * | |
63 | + * Caller must free() the returned pointer if this function did not return | |
64 | + * NULL. | |
65 | + */ | |
66 | +static char *get_realpath(const char *path) | |
67 | +{ | |
68 | + struct stat buf; | |
69 | + static const int pwd_len = PATH_MAX * 2; | |
70 | + char *dir = strdup(path); | |
71 | + char *pwd = malloc(pwd_len); | |
72 | + char *basename = NULL; | |
73 | + int len; | |
74 | + if (!dir || !pwd) | |
75 | + goto out; | |
76 | + if (stat(dir, &buf)) | |
77 | + goto out; | |
78 | + len = strlen(dir); | |
79 | + while (len > 1 && dir[len - 1] == '/') | |
80 | + dir[--len] = '\0'; | |
81 | + while (!lstat(dir, &buf) && S_ISLNK(buf.st_mode)) { | |
82 | + char *new_dir; | |
83 | + char *old_dir = dir; | |
84 | + memset(pwd, 0, pwd_len); | |
85 | + if (readlink(dir, pwd, pwd_len - 1) < 1) | |
86 | + goto out; | |
87 | + if (pwd[0] == '/') { | |
88 | + dir[0] = '\0'; | |
89 | + } else { | |
90 | + char *cp = strrchr(dir, '/'); | |
91 | + if (cp) | |
92 | + *cp = '\0'; | |
93 | + } | |
94 | + len = strlen(dir) + strlen(pwd) + 4; | |
95 | + new_dir = malloc(len); | |
96 | + if (new_dir) | |
97 | + snprintf(new_dir, len - 1, "%s/%s", dir, pwd); | |
98 | + dir = new_dir; | |
99 | + free(old_dir); | |
100 | + if (!dir) | |
101 | + goto out; | |
102 | + } | |
103 | + if (!dir) | |
104 | + goto out; | |
105 | + basename = strrchr(dir, '/'); | |
106 | + if (basename) | |
107 | + *basename++ = '\0'; | |
108 | + else | |
109 | + basename = ""; | |
110 | + if (chdir(dir)) | |
111 | + goto out; | |
112 | + memset(pwd, 0, pwd_len); | |
113 | + if (!getcwd(pwd, pwd_len - 1)) | |
114 | + goto out; | |
115 | + if (strcmp(pwd, "/")) | |
116 | + len = strlen(pwd); | |
117 | + else | |
118 | + len = 0; | |
119 | + snprintf(pwd + len, pwd_len - len - 1, "/%s", basename); | |
120 | + free(dir); | |
121 | + return pwd; | |
122 | +out: | |
123 | + free(dir); | |
124 | + free(pwd); | |
125 | + return NULL; | |
126 | +} | |
127 | +#endif | |
128 | + | |
129 | +#define elementof(x) (sizeof(x) / sizeof(x[0])) | |
130 | + | |
131 | +/** | |
132 | + * scandir_file_filter - Callback for scandir(). | |
133 | + * | |
134 | + * @buf: Pointer to "const struct dirent". | |
135 | + * | |
136 | + * Returns non 0 if @buf seems to be a file, 0 otherwise. | |
137 | + * | |
138 | + * Since several kernels have a bug that leaves @buf->d_type == DT_UNKNOWN, | |
139 | + * we allow it for now and recheck it later. | |
140 | + */ | |
141 | +static int scandir_file_filter(const struct dirent *buf) | |
142 | +{ | |
143 | + return (buf->d_type == DT_REG || buf->d_type == DT_UNKNOWN) && | |
144 | + strcmp(buf->d_name, ".") && strcmp(buf->d_name, ".."); | |
145 | +} | |
146 | + | |
147 | +/** | |
148 | + * revalidate_path - Recheck file's attribute. | |
149 | + * | |
150 | + * @path: Pathname to check. | |
151 | + * | |
152 | + * Returns type of @path. | |
153 | + * | |
154 | + * This is needed by buggy kernels that report DT_UNKNOWN upon scandir(). | |
155 | + */ | |
156 | +static unsigned char revalidate_path(const char *path) | |
157 | +{ | |
158 | + struct stat buf; | |
159 | + unsigned char type = DT_UNKNOWN; | |
160 | + if (!lstat(path, &buf)) { | |
161 | + if (S_ISREG(buf.st_mode)) | |
162 | + type = DT_REG; | |
163 | + else if (S_ISDIR(buf.st_mode)) | |
164 | + type = DT_DIR; | |
165 | + else if (S_ISLNK(buf.st_mode)) | |
166 | + type = DT_LNK; | |
167 | + } | |
168 | + return type; | |
169 | +} | |
170 | + | |
171 | +/* File handle to /etc/caitsith/policy/current . */ | |
172 | +static FILE *filp = NULL; | |
173 | + | |
174 | +/** | |
175 | + * printf_encoded - Print a word to the policy file, with escaping as needed. | |
176 | + * | |
177 | + * @str: Word to print. Needn't to follow CaitSith's escape rules. | |
178 | + * | |
179 | + * Returns nothing. | |
180 | + * | |
181 | + * If @str starts with "/proc/", it is converted with "proc:/". | |
182 | + */ | |
183 | +static void printf_encoded(const char *str) | |
184 | +{ | |
185 | + if (!strncmp(str, "/proc/", 6)) { | |
186 | + fprintf(filp, "proc:"); | |
187 | + str += 5; | |
188 | + } | |
189 | + while (1) { | |
190 | + const char c = *str++; | |
191 | + if (!c) | |
192 | + break; | |
193 | + if (c > ' ' && c < 127 && c != '\\') | |
194 | + fputc(c, filp); | |
195 | + else | |
196 | + fprintf(filp, "\\%c%c%c", (c >> 6) + '0', | |
197 | + ((c >> 3) & 7) + '0', (c & 7) + '0'); | |
198 | + } | |
199 | +} | |
200 | + | |
201 | +static void make_default_domain_transition(const char *path) | |
202 | +{ | |
203 | + fprintf(filp, " 10 allow path=\""); | |
204 | + printf_encoded(path); | |
205 | + fprintf(filp, "\" transition=\""); | |
206 | + printf_encoded(path); | |
207 | + fprintf(filp, "\"\n"); | |
208 | +} | |
209 | + | |
210 | + | |
211 | +/* Shared buffer for scandir(). */ | |
212 | +static char path[8192]; | |
213 | + | |
214 | +/** | |
215 | + * scan_executable_files - Find executable files in the specific directory. | |
216 | + * | |
217 | + * @dir: Directory name to scan. | |
218 | + * | |
219 | + * Returns nothing. | |
220 | + */ | |
221 | +static void scan_executable_files(const char *dir) | |
222 | +{ | |
223 | + struct dirent **namelist; | |
224 | + int n = scandir(dir, &namelist, scandir_file_filter, 0); | |
225 | + int i; | |
226 | + if (n < 0) | |
227 | + return; | |
228 | + for (i = 0; i < n; i++) { | |
229 | + unsigned char type = namelist[i]->d_type; | |
230 | + snprintf(path, sizeof(path) - 1, "%s/%s", dir, | |
231 | + namelist[i]->d_name); | |
232 | + if (type == DT_UNKNOWN) | |
233 | + type = revalidate_path(path); | |
234 | + if (type == DT_REG && !access(path, X_OK)) | |
235 | + make_default_domain_transition(path); | |
236 | + free(namelist[i]); | |
237 | + } | |
238 | + free(namelist); | |
239 | +} | |
240 | + | |
241 | +/** | |
242 | + * scan_modprobe_and_hotplug - Mark modprobe and hotplug as domain_transition entries. | |
243 | + * | |
244 | + * Returns nothing. | |
245 | + */ | |
246 | +static void scan_modprobe_and_hotplug(void) | |
247 | +{ | |
248 | + static const char * const files[2] = { | |
249 | + "/proc/sys/kernel/modprobe", "/proc/sys/kernel/hotplug" | |
250 | + }; | |
251 | + int i; | |
252 | + for (i = 0; i < elementof(files); i++) { | |
253 | + char *ret_ignored; | |
254 | + char buffer[PATH_MAX + 1]; | |
255 | + char *cp; | |
256 | + FILE *fp = fopen(files[i], "r"); | |
257 | + if (!fp) | |
258 | + continue; | |
259 | + memset(buffer, 0, sizeof(buffer)); | |
260 | + ret_ignored = fgets(buffer, sizeof(buffer) - 1, fp); | |
261 | + fclose(fp); | |
262 | + cp = strrchr(buffer, '\n'); | |
263 | + if (cp) | |
264 | + *cp = '\0'; | |
265 | + if (!buffer[0]) | |
266 | + continue; | |
267 | + cp = get_realpath(buffer); | |
268 | + if (!cp) | |
269 | + continue; | |
270 | + /* We ignore /bin/true if /proc/sys/kernel/modprobe said so. */ | |
271 | + if (strcmp(cp, "/bin/true") && !access(cp, X_OK)) | |
272 | + make_default_domain_transition(cp); | |
273 | + free(cp); | |
274 | + } | |
275 | +} | |
276 | + | |
277 | +/** | |
278 | + * scan_init_dir - Mark programs under /etc/init.d/ directory as default domain transition entries. | |
279 | + * | |
280 | + * Returns nothing. | |
281 | + */ | |
282 | +static void scan_init_dir(void) | |
283 | +{ | |
284 | + char *dir = get_realpath("/etc/init.d/"); | |
285 | + if (!dir) | |
286 | + return; | |
287 | + scan_executable_files(dir); | |
288 | + free(dir); | |
289 | +} | |
290 | + | |
291 | +/** | |
292 | + * scan_daemons - Mark daemon programs as default domain transition entries. | |
293 | + * | |
294 | + * Returns nothing. | |
295 | + */ | |
296 | +static void scan_daemons(void) | |
297 | +{ | |
298 | + static const char * const files[] = { | |
299 | + "/sbin/cardmgr", | |
300 | + "/sbin/getty", | |
301 | + "/sbin/init", | |
302 | + "/sbin/klogd", | |
303 | + "/sbin/mingetty", | |
304 | + "/sbin/portmap", | |
305 | + "/sbin/rpc.statd", | |
306 | + "/sbin/syslogd", | |
307 | + "/sbin/udevd", | |
308 | + "/usr/X11R6/bin/xfs", | |
309 | + "/usr/bin/dbus-daemon", | |
310 | + "/usr/bin/dbus-daemon-1", | |
311 | + "/usr/bin/jserver", | |
312 | + "/usr/bin/mDNSResponder", | |
313 | + "/usr/bin/nifd", | |
314 | + "/usr/bin/spamd", | |
315 | + "/usr/sbin/acpid", | |
316 | + "/usr/sbin/afpd", | |
317 | + "/usr/sbin/anacron", | |
318 | + "/usr/sbin/apache2", | |
319 | + "/usr/sbin/apmd", | |
320 | + "/usr/sbin/atalkd", | |
321 | + "/usr/sbin/atd", | |
322 | + "/usr/sbin/cannaserver", | |
323 | + "/usr/sbin/cpuspeed", | |
324 | + "/usr/sbin/cron", | |
325 | + "/usr/sbin/crond", | |
326 | + "/usr/sbin/cupsd", | |
327 | + "/usr/sbin/dhcpd", | |
328 | + "/usr/sbin/exim4", | |
329 | + "/usr/sbin/gpm", | |
330 | + "/usr/sbin/hald", | |
331 | + "/usr/sbin/htt", | |
332 | + "/usr/sbin/httpd", | |
333 | + "/usr/sbin/inetd", | |
334 | + "/usr/sbin/logrotate", | |
335 | + "/usr/sbin/lpd", | |
336 | + "/usr/sbin/nmbd", | |
337 | + "/usr/sbin/papd", | |
338 | + "/usr/sbin/rpc.idmapd", | |
339 | + "/usr/sbin/rpc.mountd", | |
340 | + "/usr/sbin/rpc.rquotad", | |
341 | + "/usr/sbin/sendmail.sendmail", | |
342 | + "/usr/sbin/smartd", | |
343 | + "/usr/sbin/smbd", | |
344 | + "/usr/sbin/squid", | |
345 | + "/usr/sbin/sshd", | |
346 | + "/usr/sbin/vmware-guestd", | |
347 | + "/usr/sbin/vsftpd", | |
348 | + "/usr/sbin/xinetd" | |
349 | + }; | |
350 | + int i; | |
351 | + for (i = 0; i < elementof(files); i++) { | |
352 | + char *cp = get_realpath(files[i]); | |
353 | + if (!cp) | |
354 | + continue; | |
355 | + if (!access(cp, X_OK)) | |
356 | + make_default_domain_transition(cp); | |
357 | + free(cp); | |
358 | + } | |
359 | +} | |
360 | + | |
361 | +/** | |
362 | + * mkdir2 - mkdir() with ignoring EEXIST error. | |
363 | + * | |
364 | + * @dir: Directory to create. | |
365 | + * @mode: Create mode. | |
366 | + * | |
367 | + * Returns 0 on success, EOF otehrwise. | |
368 | + */ | |
369 | +static int mkdir2(const char *dir, int mode) | |
370 | +{ | |
371 | + return mkdir(dir, mode) == 0 || errno == EEXIST ? 0 : EOF; | |
372 | +} | |
373 | + | |
374 | +/* Policy directory. Default is "/etc/caitsith/". */ | |
375 | +static char *policy_dir = NULL; | |
376 | + | |
377 | +/** | |
378 | + * make_policy_dir - Create policy directories and tools directories. | |
379 | + * | |
380 | + * Returns nothing. | |
381 | + */ | |
382 | +static void make_policy_dir(void) | |
383 | +{ | |
384 | + char *dir = policy_dir; | |
385 | + const time_t now = time(NULL); | |
386 | + struct tm *tm = localtime(&now); | |
387 | + char stamp[20] = { }; | |
388 | + snprintf(stamp, sizeof(stamp) - 1, "%02d-%02d-%02d.%02d:%02d:%02d", | |
389 | + tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, | |
390 | + tm->tm_min, tm->tm_sec); | |
391 | + if (!chdir(policy_dir) && !chdir("policy")) | |
392 | + goto tools_dir; | |
393 | + fprintf(stderr, "Creating policy directory... "); | |
394 | + while (1) { | |
395 | + const char c = *dir++; | |
396 | + if (!c) | |
397 | + break; | |
398 | + if (c != '/') | |
399 | + continue; | |
400 | + *(dir - 1) = '\0'; | |
401 | + mkdir(policy_dir, 0700); | |
402 | + *(dir - 1) = '/'; | |
403 | + } | |
404 | + if (mkdir2(policy_dir, 0700) || chdir(policy_dir) || | |
405 | + mkdir2("policy", 0700) || chdir("policy")) { | |
406 | + fprintf(stderr, "failed.\n"); | |
407 | + exit(1); | |
408 | + } else { | |
409 | + fprintf(stderr, "OK\n"); | |
410 | + } | |
411 | +tools_dir: | |
412 | + if (!chdir(policy_dir) && !chdir("tools")) | |
413 | + return; | |
414 | + fprintf(stderr, "Creating configuration directory... "); | |
415 | + mkdir("tools", 0700); | |
416 | + if (!chdir("tools")) | |
417 | + fprintf(stderr, "OK\n"); | |
418 | + else { | |
419 | + fprintf(stderr, "failed.\n"); | |
420 | + exit(1); | |
421 | + } | |
422 | +} | |
423 | + | |
424 | +/** | |
425 | + * chdir_policy - Change to policy directory. | |
426 | + * | |
427 | + * Returns 1 on success, 0 otherwise. | |
428 | + */ | |
429 | +static _Bool chdir_policy(void) | |
430 | +{ | |
431 | + if (chdir(policy_dir) || chdir("policy")) { | |
432 | + fprintf(stderr, "ERROR: Can't chdir to %s/policy/ " | |
433 | + "directory.\n", policy_dir); | |
434 | + return 0; | |
435 | + } | |
436 | + return 1; | |
437 | +} | |
438 | + | |
439 | +/** | |
440 | + * close_file - Close file and rename. | |
441 | + * | |
442 | + * @fp: Pointer to "FILE". | |
443 | + * @condition: Preconditions before rename(). | |
444 | + * @old: Temporary file's pathname. | |
445 | + * @new: Final file's pathname. | |
446 | + * | |
447 | + * Returns nothing. | |
448 | + */ | |
449 | +static void close_file(FILE *fp, _Bool condition, const char *old, | |
450 | + const char *new) | |
451 | +{ | |
452 | + if (fsync(fileno(fp)) || fclose(fp) || !condition || rename(old, new)) | |
453 | + fprintf(stderr, "failed.\n"); | |
454 | + else | |
455 | + fprintf(stderr, "OK.\n"); | |
456 | +} | |
457 | + | |
458 | +/** | |
459 | + * make_policy - Make /etc/caitsith/policy/current . | |
460 | + * | |
461 | + * Returns nothing. | |
462 | + */ | |
463 | +static void make_policy(void) | |
464 | +{ | |
465 | + if (!chdir_policy()) | |
466 | + return; | |
467 | + if (!access("current", R_OK)) | |
468 | + return; | |
469 | + filp = fopen("current.tmp", "w"); | |
470 | + if (!filp) { | |
471 | + fprintf(stderr, "ERROR: Can't create policy.\n"); | |
472 | + return; | |
473 | + } | |
474 | + fprintf(stderr, "Creating default policy... "); | |
475 | + fprintf(filp, "POLICY_VERSION=20120401\n"); | |
476 | + fprintf(filp, "\n"); | |
477 | + fprintf(filp, "quota memory audit 16777216\n"); | |
478 | + fprintf(filp, "quota memory query 1048576\n"); | |
479 | + fprintf(filp, "quota audit[1] allowed=0 denied=1024 unmatched=1024\n"); | |
480 | + fprintf(filp, "\n"); | |
481 | + fprintf(filp, "10000 acl execute\n" | |
482 | + " audit 0\n"); | |
483 | + scan_modprobe_and_hotplug(); | |
484 | + scan_daemons(); | |
485 | + scan_init_dir(); | |
486 | + fprintf(filp, "\n"); | |
487 | + { | |
488 | + char *tools_dir = get_realpath("/usr/sbin"); | |
489 | + fprintf(filp, "0 acl modify_policy\n" | |
490 | + " audit 1\n" | |
491 | + " 1 deny task.uid!=0\n" | |
492 | + " 1 deny task.euid!=0\n" | |
493 | + " 100 allow task.exe=\"%s/caitsith-loadpolicy\"\n" | |
494 | + " 100 allow task.exe=\"%s/caitsith-queryd\"\n" | |
495 | + " 10000 deny\n", tools_dir, tools_dir); | |
496 | + } | |
497 | + close_file(filp, chdir_policy(), "current.tmp", "current"); | |
498 | + filp = NULL; | |
499 | +} | |
500 | + | |
501 | +/* The name of loadable kernel module to load. */ | |
502 | +static const char *module_name = "caitsith"; | |
503 | + | |
504 | +/** | |
505 | + * make_module_loader - Make /etc/caitsith/caitsith-load-module . | |
506 | + * | |
507 | + * Returns nothing. | |
508 | + */ | |
509 | +static void make_module_loader(void) | |
510 | +{ | |
511 | + FILE *fp; | |
512 | + if (chdir(policy_dir) || !access("caitsith-load-module", X_OK) | |
513 | + || !module_name[0]) | |
514 | + return; | |
515 | + fp = fopen("caitsith-load-module.tmp", "w"); | |
516 | + if (!fp) { | |
517 | + fprintf(stderr, "ERROR: Can't create module loader.\n"); | |
518 | + return; | |
519 | + } | |
520 | + fprintf(stderr, "Creating module loader... "); | |
521 | + fprintf(fp, "#! /bin/sh\n"); | |
522 | + fprintf(fp, "export PATH=$PATH:/sbin:/bin\n"); | |
523 | + fprintf(fp, "exec modprobe %s\n", module_name); | |
524 | + close_file(fp, !chmod("caitsith-load-module.tmp", 0700), | |
525 | + "caitsith-load-module.tmp", "caitsith-load-module"); | |
526 | +} | |
527 | + | |
528 | +/* Content of /etc/caitsith/tools/auditd.conf . */ | |
529 | +static const char auditd_data[] = | |
530 | +"# This file contains sorting rules used by caitsith-auditd command.\n" | |
531 | +"\n" | |
532 | +"# An audit log consists with two parts delimited by \" / \" sequence.\n" | |
533 | +"# You can refer the former part using 'header' keyword, the latter part\n" | |
534 | +"# using 'acl' keyword.\n" | |
535 | +"#\n" | |
536 | +"# Words in each part are separated by a space character. Therefore, you can\n" | |
537 | +"# use 'header[index]', 'acl[index]' for referring index'th word of the\n" | |
538 | +"# part.\n" | |
539 | +"# The index starts from 1, and 0 refers the whole line\n" | |
540 | +"# (i.e. 'header[0]' = 'header', 'acl[0]' = 'acl').\n" | |
541 | +"#\n" | |
542 | +"# Three operators are provided for conditional sorting.\n" | |
543 | +"# '.contains' is for 'fgrep keyword' match.\n" | |
544 | +"# '.equals' is for 'grep ^keyword$' match.\n" | |
545 | +"# '.starts' is for 'grep ^keyword' match.\n" | |
546 | +"#\n" | |
547 | +"# Sorting rules are defined using multi-lined chunks. A chunk is terminated\n" | |
548 | +"# by a 'destination' line which specifies the pathname to write the audit\n" | |
549 | +"# log. A 'destination' line is processed only when all preceding 'header'\n" | |
550 | +"# and 'acl' lines in that chunk have matched.\n" | |
551 | +"# Evaluation stops at the first processed 'destination' line.\n" | |
552 | +"# Therefore, no audit logs are written more than once.\n" | |
553 | +"#\n" | |
554 | +"# More specific matches should be placed before less specific matches.\n" | |
555 | +"# For example:\n" | |
556 | +"#\n" | |
557 | +"# header.contains result=denied\n" | |
558 | +"# acl.contains task.domain=\"/usr/sbin/httpd\"\n" | |
559 | +"# destination /var/log/caitsith/httpd_denied.log\n" | |
560 | +"#\n" | |
561 | +"# This chunk should be placed before the chunk that matches logs with\n" | |
562 | +"# result=denied. If placed after, the audit logs for /usr/sbin/httpd will\n" | |
563 | +"# be sent to /var/log/caitsith/denied.log .\n" | |
564 | +"\n" | |
565 | +"# Please use CaitSith's escape rule (e.g. '\\040' rather than '\\ ' for\n" | |
566 | +"# representing a ' ' in a word).\n" | |
567 | +"\n" | |
568 | +"# Send all allowed logs to /dev/null.\n" | |
569 | +"header.contains result=allowed\n" | |
570 | +"destination /dev/null\n" | |
571 | +"\n" | |
572 | +"# Send all unmatched logs to /var/log/caitsith/unmatched.log\n" | |
573 | +"header.contains result=unmatched\n" | |
574 | +"destination /var/log/caitsith/unmatched.log\n" | |
575 | +"\n" | |
576 | +"# Send all denied logs to /var/log/caitsith/denied.log\n" | |
577 | +"header.contains result=denied\n" | |
578 | +"destination /var/log/caitsith/denied.log\n" | |
579 | +"\n"; | |
580 | + | |
581 | +/** | |
582 | + * make_auditd_conf - Make /etc/caitsith/tools/auditd.conf . | |
583 | + * | |
584 | + * Returns nothing. | |
585 | + */ | |
586 | +static void make_auditd_conf(void) | |
587 | +{ | |
588 | + FILE *fp; | |
589 | + if (chdir(policy_dir) || chdir("tools") || | |
590 | + !access("auditd.conf", R_OK)) | |
591 | + return; | |
592 | + fp = fopen("auditd.tmp", "w"); | |
593 | + if (!fp) { | |
594 | + fprintf(stderr, "ERROR: Can't create configuration file.\n"); | |
595 | + return; | |
596 | + } | |
597 | + fprintf(stderr, "Creating configuration file for caitsith-auditd ... "); | |
598 | + fprintf(fp, "%s", auditd_data); | |
599 | + close_file(fp, !chmod("auditd.tmp", 0644), "auditd.tmp", | |
600 | + "auditd.conf"); | |
601 | +} | |
602 | + | |
603 | +/* Content of /etc/caitsith/tools/notifyd.conf . */ | |
604 | +static const char notifyd_data[] = | |
605 | +"# This file contains configuration used by caitsith-notifyd command.\n" | |
606 | +"\n" | |
607 | +"# caitsith-notifyd is a daemon that notifies the occurrence of an access\n" | |
608 | +"# request that is about to be rejected by CaitSith.\n" | |
609 | +"#\n" | |
610 | +"# time_to_wait is grace time in second before rejecting the request.\n" | |
611 | +"# For example, if you specify 30, you will be given 30 seconds for starting\n" | |
612 | +"# caitsith-queryd command and responding to the policy violation event.\n" | |
613 | +"# You should avoid specifying too large value (e.g. 3600) because\n" | |
614 | +"# the request will remain pending for that period if you can't respond.\n" | |
615 | +"#\n" | |
616 | +"# action_to_take is a command line you want to use for notification.\n" | |
617 | +"# The command specified by this parameter must read the policy violation\n" | |
618 | +"# notification from standard input. For example, mail, curl and xmessage\n" | |
619 | +"# commands can read from standard input.\n" | |
620 | +"# This parameter is passed to execve(). Thus, please use a wrapper program\n" | |
621 | +"# if you need shell processing (e.g. wildcard expansion, environment\n" | |
622 | +"# variables).\n" | |
623 | +"#\n" | |
624 | +"# minimal_interval is grace time in second before re-notifying the next\n" | |
625 | +"# occurrence of policy violation. You can specify 60 to limit notification\n" | |
626 | +"# to once per a minute, 3600 to limit notification to once per an hour.\n" | |
627 | +"# You can specify 0 to unlimit, but notifying of every policy violation\n" | |
628 | +"# events (e.g. sending a mail) might annoy you because policy violation\n" | |
629 | +"# can occur in clusters if once occurred.\n" | |
630 | +"\n" | |
631 | +"# Please use CaitSith's escape rule (e.g. '\\040' rather than '\\ ' for\n" | |
632 | +"# representing a ' ' in a word).\n" | |
633 | +"\n" | |
634 | +"# Examples:\n" | |
635 | +"#\n" | |
636 | +"# time_to_wait 180\n" | |
637 | +"# action_to_take mail admin@example.com\n" | |
638 | +"#\n" | |
639 | +"# Wait for 180 seconds before rejecting the request.\n" | |
640 | +"# The occurrence is notified by sending mail to admin@example.com\n" | |
641 | +"# (if SMTP service is available).\n" | |
642 | +"#\n" | |
643 | +"# time_to_wait 0\n" | |
644 | +"# action_to_take curl --data-binary @- https://your.server/path_to_cgi\n" | |
645 | +"#\n" | |
646 | +"# Reject the request immediately.\n" | |
647 | +"# The occurrence is notified by executing curl command.\n" | |
648 | +"#\n" | |
649 | +"time_to_wait 0\n" | |
650 | +"action_to_take mail -s Notification\\040from\\040caitsith-notifyd root@localhost\n" | |
651 | +"minimal_interval 60\n" | |
652 | +"\n"; | |
653 | + | |
654 | +/** | |
655 | + * make_notifyd_conf - Make /etc/caitsith/tools/notifyd.conf . | |
656 | + * | |
657 | + * Returns nothing. | |
658 | + */ | |
659 | +static void make_notifyd_conf(void) | |
660 | +{ | |
661 | + FILE *fp; | |
662 | + if (chdir(policy_dir) || chdir("tools") || | |
663 | + !access("notifyd.conf", R_OK)) | |
664 | + return; | |
665 | + fp = fopen("notifyd.tmp", "w"); | |
666 | + if (!fp) { | |
667 | + fprintf(stderr, "ERROR: Can't create configuration file.\n"); | |
668 | + return; | |
669 | + } | |
670 | + fprintf(stderr, "Creating configuration file for caitsith-notifyd ... "); | |
671 | + fprintf(fp, "%s", notifyd_data); | |
672 | + close_file(fp, !chmod("notifyd.tmp", 0644), "notifyd.tmp", | |
673 | + "notifyd.conf"); | |
674 | +} | |
675 | + | |
676 | +int main(int argc, char *argv[]) | |
677 | +{ | |
678 | + int i; | |
679 | + const char *dir = NULL; | |
680 | + for (i = 1; i < argc; i++) { | |
681 | + char *arg = argv[i]; | |
682 | + if (*arg == '-' && *(arg + 1) == '-') | |
683 | + arg += 2; | |
684 | + if (!strncmp(arg, "root=", 5)) { | |
685 | + if (chroot(arg + 5) || chdir("/")) { | |
686 | + fprintf(stderr, "Can't chroot to '%s'\n", | |
687 | + arg + 5); | |
688 | + return 1; | |
689 | + } | |
690 | + } else if (!strncmp(arg, "policy_dir=", 11)) { | |
691 | + dir = arg + 11; | |
692 | + } else if (!strncmp(arg, "module_name=", 12)) { | |
693 | + module_name = arg + 12; | |
694 | + } else { | |
695 | + fprintf(stderr, "Unknown option: '%s'\n", argv[i]); | |
696 | + return 1; | |
697 | + } | |
698 | + } | |
699 | + if (!dir) | |
700 | + dir = "/etc/caitsith"; | |
701 | + policy_dir = strdup(dir); | |
702 | + memset(path, 0, sizeof(path)); | |
703 | + make_policy_dir(); | |
704 | + make_policy(); | |
705 | + make_module_loader(); | |
706 | + make_auditd_conf(); | |
707 | + make_notifyd_conf(); | |
708 | + return 0; | |
709 | +} |
@@ -0,0 +1,18 @@ | ||
1 | +include ../Include.make | |
2 | + | |
3 | +BUILD_FILES = audit-exec-param caitsith-agent init_policy | |
4 | + | |
5 | +all: $(BUILD_FILES) | |
6 | + | |
7 | +install: all | |
8 | + mkdir -p -m 0755 $(INSTALLDIR)/$(USRLIBDIR)/caitsith | |
9 | + $(INSTALL) -m 0755 $(BUILD_FILES) $(INSTALLDIR)/$(USRLIBDIR)/caitsith/ | |
10 | + $(INSTALL) -m 0644 ../README.caitsith ../COPYING.caitsith $(INSTALLDIR)/$(USRLIBDIR)/caitsith/ | |
11 | + | |
12 | +.c: | |
13 | + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< | |
14 | + | |
15 | +clean: | |
16 | + rm -f -- $(BUILD_FILES) | |
17 | + | |
18 | +.PHONY: clean install |
@@ -0,0 +1,21 @@ | ||
1 | +all: | |
2 | + $(MAKE) -C sbin all | |
3 | + $(MAKE) -C usr_sbin all | |
4 | + $(MAKE) -C usr_lib_caitsith all | |
5 | + | |
6 | +install: all | |
7 | + $(MAKE) -C sbin install | |
8 | + $(MAKE) -C usr_sbin install | |
9 | + $(MAKE) -C usr_lib_caitsith install | |
10 | + | |
11 | +clean: | |
12 | +## | |
13 | +## I don't enable "find" line because older versions does not support -delete | |
14 | +## action. | |
15 | +## | |
16 | +# find -name '*~' -delete | |
17 | + $(MAKE) -C sbin clean | |
18 | + $(MAKE) -C usr_sbin clean | |
19 | + $(MAKE) -C usr_lib_caitsith clean | |
20 | + | |
21 | +.PHONY: clean install |