The Pedigree Project 0.1
fanotify-handle-contract-test/admission.c
1#define _GNU_SOURCE
2#include <grp.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/mman.h>
8#include <sys/stat.h>
9
10static int flags_and_paths(void) {
11 int failed = 0, group = -1, accepted = -1, memory = -1, ram = -1;
12 struct fh_file file = {.fd = -1};
13 struct fh_handle handle;
14 char symlink_path[224] = {0}, ram_path[192] = {0};
15 int mount_id;
16 CHECK(!fh_create(&file));
17 errno = 0;
18 CHECK(fanotify_init(FAN_REPORT_FID | 0x80000000U, O_RDONLY) == -1 && errno == EINVAL);
19 errno = 0;
20 CHECK(fanotify_init(FAN_REPORT_FID | FAN_CLASS_CONTENT | FAN_CLASS_PRE_CONTENT, O_RDONLY) == -1 &&
21 errno == EINVAL);
22 errno = 0;
23 CHECK(fanotify_init(0, O_RDONLY) == -1 && errno == EOPNOTSUPP);
24 errno = 0;
25 CHECK(fanotify_init(FAN_REPORT_FID | FAN_CLASS_CONTENT, O_RDONLY) == -1 && errno == EOPNOTSUPP);
26 errno = 0;
27 CHECK(fanotify_init(FAN_REPORT_FID, O_ACCMODE) == -1 && errno == EINVAL);
28 errno = 0;
29 CHECK(fanotify_init(FAN_REPORT_FID, 3) == -1 && errno == EINVAL);
30 errno = 0;
31 CHECK(fanotify_init(FAN_REPORT_FID, O_PATH) == -1 && errno == EINVAL);
32 errno = 0;
33 CHECK(fanotify_init(FAN_REPORT_FID, O_CREAT) == -1 && errno == EINVAL);
34 accepted = fanotify_init(FAN_REPORT_FID | FAN_NONBLOCK, O_RDWR | O_APPEND | O_NONBLOCK | O_SYNC |
35 O_DSYNC | O_CLOEXEC | O_LARGEFILE |
36 O_NOATIME);
37 CHECK(accepted >= 0);
38 close(accepted);
39 accepted = -1;
40 group = fh_group(1);
41 CHECK(group >= 0);
42 errno = 0;
43 CHECK(fh_mark(group, &file, FAN_MARK_ADD | FAN_MARK_REMOVE, FAN_MODIFY) == -1 && errno == EINVAL);
44 errno = 0;
45 CHECK(fh_mark(group, &file, FAN_MARK_ADD, FAN_OPEN_PERM) == -1 && errno == EINVAL);
46 errno = 0;
47 CHECK(fh_mark(group, &file, FAN_MARK_ADD, 1ULL << 63) == -1 && errno == EINVAL);
48 errno = 0;
49 CHECK(fh_mark(group, &file, FAN_MARK_ADD | FAN_MARK_MOUNT, FAN_MODIFY) == -1 &&
50 errno == EOPNOTSUPP);
51 errno = 0;
52 CHECK(fh_mark(group, &file, FAN_MARK_ADD | FAN_MARK_ONLYDIR, FAN_MODIFY) == -1 &&
53 errno == ENOTDIR);
54 errno = 0;
55 CHECK(fanotify_mark(group, FAN_MARK_ADD, FAN_MODIFY, file.fd, "") == -1 && errno == ENOENT);
56 CHECK(!fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
57 errno = 0;
58 CHECK(fanotify_mark(group, FAN_MARK_FLUSH | FAN_MARK_DONT_FOLLOW, 0, -1, NULL) == -1 &&
59 errno == EINVAL);
60 errno = 0;
61 CHECK(fanotify_mark(group, FAN_MARK_FLUSH | FAN_MARK_ONLYDIR, 0, -1, NULL) == -1 &&
62 errno == EINVAL);
63 CHECK(!fh_mark(group, &file, FAN_MARK_REMOVE, FAN_MODIFY));
64 CHECK(!fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
65 errno = 0;
66 CHECK(fanotify_mark(group, FAN_MARK_ADD, FAN_MODIFY, AT_FDCWD, NULL) == -1 && errno == EBADF);
67 CHECK(!fanotify_mark(group, FAN_MARK_FLUSH, UINT64_MAX, -1, (const char*)1));
68 snprintf(symlink_path, sizeof(symlink_path), "%s.link", file.path);
69 CHECK(!symlink(file.path, symlink_path));
70 handle.handle_bytes = sizeof(handle.bytes);
71 errno = 0;
72 CHECK(name_to_handle_at(AT_FDCWD, symlink_path, (struct file_handle*)&handle, &mount_id, 0) ==
73 -1 &&
74 errno == EOPNOTSUPP);
75 handle.handle_bytes = sizeof(handle.bytes);
76 CHECK(!name_to_handle_at(AT_FDCWD, symlink_path, (struct file_handle*)&handle, &mount_id,
77 AT_SYMLINK_FOLLOW));
78 CHECK(fh_equal(&handle, &file.handle));
79 errno = 0;
80 CHECK(fanotify_mark(group, FAN_MARK_ADD | FAN_MARK_DONT_FOLLOW, FAN_MODIFY, AT_FDCWD,
81 symlink_path) == -1 &&
82 errno == EOPNOTSUPP);
83 CHECK(!fanotify_mark(group, FAN_MARK_ADD, FAN_MODIFY, AT_FDCWD, symlink_path));
84 memory = memfd_create("fanotify-unsupported", MFD_CLOEXEC);
85 CHECK(memory >= 0);
86 errno = 0;
87 CHECK(fh_export(memory, &handle, &mount_id) == -1 && errno == EOPNOTSUPP);
88 errno = 0;
89 CHECK(fanotify_mark(group, FAN_MARK_ADD, FAN_MODIFY, memory, NULL) == -1 && errno == EOPNOTSUPP);
90 snprintf(ram_path, sizeof(ram_path), "/tmp/fanotify-unsupported-%ld", (long)getpid());
91 ram = open(ram_path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
92 CHECK(ram >= 0);
93 errno = 0;
94 CHECK(fh_export(ram, &handle, &mount_id) == -1 && errno == EOPNOTSUPP);
95 errno = 0;
96 CHECK(open_by_handle_at(file.fd, (struct file_handle*)&file.handle, O_PATH) == -1 &&
97 errno == EOPNOTSUPP);
98 errno = 0;
99 CHECK(open_by_handle_at(-1, (struct file_handle*)&file.handle, O_RDONLY) == -1 && errno == EBADF);
100out:
101 if (ram >= 0)
102 close(ram);
103 if (ram_path[0])
104 unlink(ram_path);
105 if (memory >= 0)
106 close(memory);
107 if (symlink_path[0])
108 unlink(symlink_path);
109 if (accepted >= 0)
110 close(accepted);
111 if (group >= 0)
112 close(group);
113 fh_close(&file);
114 return failed;
115}
116
117static int copies(void) {
118 int failed = 0, mount_id;
119 struct fh_file file = {.fd = -1};
120 struct fh_handle handle, saved;
121 void* page = MAP_FAILED;
122 CHECK(!fh_create(&file));
123 page = mmap(NULL, fh_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
124 CHECK(page != MAP_FAILED);
125 handle = file.handle;
126 handle.handle_bytes = 129;
127 errno = 0;
128 CHECK(name_to_handle_at(file.fd, "", (struct file_handle*)&handle, &mount_id, AT_EMPTY_PATH) ==
129 -1 &&
130 errno == EINVAL);
131 handle = file.handle;
132 errno = 0;
133 CHECK(name_to_handle_at(file.fd, "", (struct file_handle*)&handle, &mount_id,
134 AT_EMPTY_PATH | 0x40000000) == -1 &&
135 errno == EINVAL);
136 handle.handle_bytes = 0;
137 errno = 0;
138 CHECK(open_by_handle_at(file.fd, (struct file_handle*)&handle, O_RDONLY) == -1 &&
139 errno == EINVAL);
140 CHECK(!mprotect(page, fh_page, PROT_NONE));
141 errno = 0;
142 CHECK(name_to_handle_at(file.fd, "", page, &mount_id, AT_EMPTY_PATH) == -1 && errno == EFAULT);
143 errno = 0;
144 CHECK(open_by_handle_at(file.fd, page, O_RDONLY) == -1 && errno == EFAULT);
145 memset(&handle, 0x5a, sizeof(handle));
146 handle.handle_bytes = sizeof(handle.bytes);
147 saved = handle;
148 errno = 0;
149 CHECK(name_to_handle_at(file.fd, "", (struct file_handle*)&handle, page, AT_EMPTY_PATH) == -1 &&
150 errno == EFAULT);
151 CHECK(!memcmp(&handle, &saved, sizeof(handle)));
152 CHECK(!mprotect(page, fh_page, PROT_READ | PROT_WRITE));
153 memcpy(page, &saved, sizeof(saved));
154 CHECK(!mprotect(page, fh_page, PROT_READ));
155 mount_id = -123;
156 errno = 0;
157 CHECK(name_to_handle_at(file.fd, "", page, &mount_id, AT_EMPTY_PATH) == -1 && errno == EFAULT);
158 CHECK(mount_id == file.mount_id && !memcmp(page, &saved, sizeof(saved)));
159out:
160 if (page != MAP_FAILED)
161 munmap(page, fh_page);
162 fh_close(&file);
163 return failed;
164}
165
166static int permissions(void) {
167 int failed = 0, group = -1;
168 pid_t child = -1;
169 struct fh_file file = {.fd = -1};
170 CHECK(!fh_create(&file));
171 CHECK(!fchmod(file.fd, 0644));
172 group = fh_group(1);
173 CHECK(group >= 0);
174 child = fork();
175 CHECK(child >= 0);
176 if (!child) {
177 alarm(10);
178 struct fh_handle handle;
179 int mount_id;
180 if (setgroups(0, NULL) || setgid(65534) || setuid(65534))
181 _exit(10);
182 errno = 0;
183 if (fh_group(1) != -1 || errno != EPERM)
184 _exit(11);
185 handle.handle_bytes = sizeof(handle.bytes);
186 if (name_to_handle_at(AT_FDCWD, file.path, (struct file_handle*)&handle, &mount_id, 0) ||
187 !fh_equal(&handle, &file.handle))
188 _exit(12);
189 errno = 0;
190 if (open_by_handle_at(file.fd, (struct file_handle*)&handle, O_RDONLY) != -1 || errno != EPERM)
191 _exit(13);
192 if (fanotify_mark(group, FAN_MARK_ADD, FAN_MODIFY, AT_FDCWD, file.path))
193 _exit(14);
194 _exit(0);
195 }
196 CHECK(!fh_reap(child, 12000));
197 child = -1;
198 CHECK(!fh_mark(group, &file, FAN_MARK_REMOVE, FAN_MODIFY));
199 CHECK(!fchmod(file.fd, 0600));
200 child = fork();
201 CHECK(child >= 0);
202 if (!child) {
203 alarm(10);
204 if (setgroups(0, NULL) || setgid(65534) || setuid(65534))
205 _exit(20);
206 errno = 0;
207 _exit(fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY) == -1 && errno == EACCES ? 0 : 21);
208 }
209 CHECK(!fh_reap(child, 12000));
210 child = -1;
211out:
212 if (child > 0)
213 fh_reap(child, 100);
214 if (group >= 0)
215 close(group);
216 fh_close(&file);
217 return failed;
218}
219int fh_admission(void) {
220 return flags_and_paths() || copies() || permissions();
221}