1 /*
2 * Copyright (c) 2024 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 /* compile: xcrun -sdk macosx.internal clang -ldarwintest -o resolve_beneath resolve_beneath.c -g -Weverything */
30
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <sys/mount.h>
35 #include <sys/attr.h>
36 #include <sys/stat.h>
37 #include <sys/param.h>
38 #include <sys/xattr.h>
39 #include <sys/clonefile.h>
40 #include <pthread.h>
41 #include <time.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44
45 #include <darwintest.h>
46 #include <darwintest/utils.h>
47
48 static char template[MAXPATHLEN];
49 static char *testdir = NULL;
50 static int testdir_fd = -1, test_fd = -1;
51
52 #ifndef ENOTCAPABLE
53 #define ENOTCAPABLE 107
54 #endif
55
56 #ifndef O_RESOLVE_BENEATH
57 #define O_RESOLVE_BENEATH 0x1000
58 #endif
59
60 #ifndef AT_RESOLVE_BENEATH
61 #define AT_RESOLVE_BENEATH 0x2000
62 #endif
63
64 #ifndef XATTR_RESOLVE_BENEATH
65 #define XATTR_RESOLVE_BENEATH 0x0080
66 #endif
67
68 #ifndef CLONE_RESOLVE_BENEATH
69 #define CLONE_RESOLVE_BENEATH 0x0010
70 #endif
71
72 #ifndef RENAME_RESOLVE_BENEATH
73 #define RENAME_RESOLVE_BENEATH 0x0020
74 #endif
75
76 #ifndef FSOPT_RESOLVE_BENEATH
77 #define FSOPT_RESOLVE_BENEATH 0x1000
78 #endif
79
80 #define TEST_DIR "test_dir"
81 #define NESTED_DIR "test_dir/nested"
82 #define OUTSIDE_FILE "outside_file.txt"
83 #define INSIDE_FILE "test_dir/inside_file.txt"
84 #define NESTED_FILE "test_dir/nested/nested_file.txt"
85 #define SYMLINK "test_dir/symlink"
86 #define SYMLINK_TO_NESTED "test_dir/symlink_to_nested"
87 #define PARENT_SYMLINK "test_dir/parent_symlink"
88 #define CIRCULAR_SYMLINK "test_dir/circular_symlink"
89 #define SYMLINK_ABSOLUTE "test_dir/symlink_absolute"
90
91 #define SYMLINK_FROM "../outside_file.txt"
92 #define SYMLINK_TO_NESTED_FROM "nested/nested_file.txt"
93 #define PARENT_SYMLINK_FROM ".."
94 #define CIRCULAR_SYMLINK_FROM "circular_symlink"
95
96 T_GLOBAL_META(
97 T_META_NAMESPACE("xnu.vfs"),
98 T_META_RADAR_COMPONENT_NAME("xnu"),
99 T_META_RADAR_COMPONENT_VERSION("vfs"),
100 T_META_ASROOT(false),
101 T_META_CHECK_LEAKS(false));
102
103 static void
setup(const char * dirname)104 setup(const char *dirname)
105 {
106 int fd;
107
108 testdir_fd = test_fd = -1;
109
110 /* Create test root directory */
111 snprintf(template, sizeof(template), "%s/%s-XXXXXX", dt_tmpdir(), dirname);
112 T_ASSERT_POSIX_NOTNULL((testdir = mkdtemp(template)), "Creating test root directory");
113 T_ASSERT_POSIX_SUCCESS((testdir_fd = open(testdir, O_SEARCH, 0777)), "Opening test root directory %s", testdir);
114
115 /* Create test directories */
116 T_ASSERT_POSIX_SUCCESS(mkdirat(testdir_fd, TEST_DIR, 0777), "Creating %s/%s", testdir, TEST_DIR);
117 T_ASSERT_POSIX_SUCCESS((test_fd = openat(testdir_fd, TEST_DIR, O_SEARCH, 0777)), "Opening test directory %s/%s", testdir, TEST_DIR);
118 T_ASSERT_POSIX_SUCCESS(mkdirat(testdir_fd, NESTED_DIR, 0777), "Creating %s/%s", testdir, NESTED_DIR);
119
120 /* Create test files */
121 T_ASSERT_POSIX_SUCCESS((fd = openat(testdir_fd, OUTSIDE_FILE, O_CREAT | O_RDWR, 0777)), "Creating file %s/%s", testdir, OUTSIDE_FILE);
122 T_ASSERT_POSIX_SUCCESS(close(fd), "Closing %s", OUTSIDE_FILE);
123
124 T_ASSERT_POSIX_SUCCESS((fd = openat(testdir_fd, INSIDE_FILE, O_CREAT | O_RDWR, 0777)), "Creating file %s/%s", testdir, INSIDE_FILE);
125 T_ASSERT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
126
127 T_ASSERT_POSIX_SUCCESS((fd = openat(testdir_fd, NESTED_FILE, O_CREAT | O_RDWR, 0777)), "Creating file %s/%s", testdir, NESTED_FILE);
128 T_ASSERT_POSIX_SUCCESS(close(fd), "Closing %s", NESTED_FILE);
129
130 /* Create test symlinks */
131 T_ASSERT_POSIX_SUCCESS(symlinkat(SYMLINK_FROM, testdir_fd, SYMLINK), "Creating symlink %s/%s -> %s", testdir, SYMLINK, SYMLINK_FROM);
132 T_ASSERT_POSIX_SUCCESS(symlinkat(SYMLINK_TO_NESTED_FROM, testdir_fd, SYMLINK_TO_NESTED), "Creating symlink %s/%s -> %s", testdir, SYMLINK_TO_NESTED, SYMLINK_TO_NESTED_FROM);
133 T_ASSERT_POSIX_SUCCESS(symlinkat(PARENT_SYMLINK_FROM, testdir_fd, PARENT_SYMLINK), "Creating symlink %s/%s -> %s", testdir, PARENT_SYMLINK, PARENT_SYMLINK_FROM);
134 T_ASSERT_POSIX_SUCCESS(symlinkat(CIRCULAR_SYMLINK_FROM, testdir_fd, CIRCULAR_SYMLINK), "Creating symlink %s/%s -> %s", testdir, CIRCULAR_SYMLINK, CIRCULAR_SYMLINK_FROM);
135 T_ASSERT_POSIX_SUCCESS(symlinkat(testdir, testdir_fd, SYMLINK_ABSOLUTE), "Creating symlink %s/%s -> %s", testdir, SYMLINK_ABSOLUTE, testdir);
136 }
137
138 static void
cleanup(void)139 cleanup(void)
140 {
141 if (test_fd != -1) {
142 close(test_fd);
143 }
144 if (testdir_fd != -1) {
145 unlinkat(testdir_fd, SYMLINK_ABSOLUTE, 0);
146 unlinkat(testdir_fd, CIRCULAR_SYMLINK, 0);
147 unlinkat(testdir_fd, PARENT_SYMLINK, 0);
148 unlinkat(testdir_fd, SYMLINK_TO_NESTED, 0);
149 unlinkat(testdir_fd, SYMLINK, 0);
150 unlinkat(testdir_fd, NESTED_FILE, 0);
151 unlinkat(testdir_fd, NESTED_DIR, AT_REMOVEDIR);
152 unlinkat(testdir_fd, INSIDE_FILE, 0);
153 unlinkat(testdir_fd, TEST_DIR, AT_REMOVEDIR);
154 unlinkat(testdir_fd, OUTSIDE_FILE, 0);
155
156 close(testdir_fd);
157 rmdir(testdir);
158 }
159 }
160
161 T_DECL(resolve_beneath_open,
162 "test open()/openat() using the O_RESOLVE_BENEATH flag")
163 {
164 int fd, root_fd;
165 char path[MAXPATHLEN];
166
167 T_SETUPBEGIN;
168
169 T_ATEND(cleanup);
170 setup("resolve_beneath_open");
171
172 T_ASSERT_POSIX_SUCCESS((root_fd = open("/", O_SEARCH, 0777)), "Opening the root directory");
173
174 T_SETUPEND;
175
176 T_LOG("Testing the openat() syscall using O_RESOLVE_BENEATH");
177
178 /* Test Case 1: File within the directory */
179 T_EXPECT_POSIX_SUCCESS((fd = openat(test_fd, "inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 1: File within the directory");
180 if (fd >= 0) {
181 close(fd);
182 }
183
184 /* Test Case 2: File using a symlink pointing outside */
185 T_EXPECT_POSIX_FAILURE(openat(test_fd, "symlink", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
186
187 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
188 T_EXPECT_POSIX_FAILURE(openat(test_fd, "../outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
189
190 /* Test Case 4: File within a nested directory */
191 T_EXPECT_POSIX_SUCCESS((fd = openat(test_fd, "nested/nested_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 4: File within a nested directory");
192 if (fd >= 0) {
193 close(fd);
194 }
195
196 /* Test Case 5: Symlink to a file in a nested directory */
197 T_EXPECT_POSIX_SUCCESS((fd = openat(test_fd, "symlink_to_nested", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 5: Symlink to a file within the same directory");
198 if (fd >= 0) {
199 close(fd);
200 }
201
202 /* Test Case 6: File using an absolute path */
203 T_EXPECT_POSIX_FAILURE(openat(test_fd, "/etc/passwd", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
204
205 /* Test Case 7: Valid symlink to parent directory */
206 T_EXPECT_POSIX_FAILURE(openat(test_fd, "parent_symlink/outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
207
208 /* Test Case 8: Circular symlink within directory */
209 T_EXPECT_POSIX_FAILURE(openat(test_fd, "circular_symlink", O_RDONLY | O_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
210
211 /* Test Case 9: Path can not escape outside at any point of the resolution */
212 T_EXPECT_POSIX_FAILURE(openat(test_fd, "../test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
213
214 /* Test Case 10: File using a symlink pointing to absolute path */
215 T_EXPECT_POSIX_FAILURE(openat(test_fd, "symlink_absolute/test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
216
217 /* Test Case 11: Absolute path relative to the root directory */
218 T_EXPECT_POSIX_FAILURE(openat(root_fd, "/etc/passwd", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 11: Absolute path relative to the root directory");
219
220 /* Test Case 12: Path can not escape outside of the root directory using dotdot */
221 T_EXPECT_POSIX_FAILURE((fd = openat(root_fd, "../private", O_RESOLVE_BENEATH)), ENOTCAPABLE, "Test Case 12: Path can not escape outside of the root directory using dotdot");
222
223 /* Changing current directory to the test directory */
224 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
225
226 T_LOG("Testing the open() syscall using O_RESOLVE_BENEATH");
227
228 /* Test Case 13: Open a file within the directory */
229 T_EXPECT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 13: Open a file within the directory");
230 if (fd >= 0) {
231 close(fd);
232 }
233
234 /* Test Case 14: Attempt to open a file using a symlink pointing outside */
235 T_EXPECT_POSIX_FAILURE(open("symlink", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: Attempt to open a file using a symlink pointing outside");
236
237 /* Test Case 15: Attempt to open a file using ".." to navigate outside */
238 T_EXPECT_POSIX_FAILURE(open("../outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 15: Attempt to open a file using \"..\" to navigate outside");
239
240 /* Test Case 16: Open a file within a nested directory */
241 T_EXPECT_POSIX_SUCCESS((fd = open("nested/nested_file.txt", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 16: Open a file within a nested directory");
242 if (fd >= 0) {
243 close(fd);
244 }
245
246 /* Test Case 17: Symlink to a file in a nested directory */
247 T_EXPECT_POSIX_SUCCESS((fd = open("symlink_to_nested", O_RDONLY | O_RESOLVE_BENEATH, 0777)), "Test Case 17: Symlink to a file within the same directory");
248 if (fd >= 0) {
249 close(fd);
250 }
251
252 /* Test Case 18: Attempt to open a file using an absolute path */
253 T_EXPECT_POSIX_FAILURE(open("/etc/passwd", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: Attempt to open a file using an absolute path");
254
255 /* Test Case 19: Valid symlink to parent directory */
256 T_EXPECT_POSIX_FAILURE(open("parent_symlink/outside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 19: Valid symlink to parent directory");
257
258 /* Test Case 20: Circular symlink within directory */
259 T_EXPECT_POSIX_FAILURE(open("circular_symlink", O_RDONLY | O_RESOLVE_BENEATH), ELOOP, "Test Case 20: Circular symlink within directory");
260
261 /* Test Case 21: Path can not escape outside at any point of the resolution */
262 T_EXPECT_POSIX_FAILURE(open("../test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 21: Path can not escape outside at any point of the resolution");
263
264 /* Test Case 22: Attempt to open a file using a symlink pointing to absolute path */
265 T_EXPECT_POSIX_FAILURE(open("symlink_absolute/test_dir/inside_file.txt", O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 22: Attempt to open a file using a symlink pointing to absolute path");
266
267 /* Test Case 23: Path can not escape outside at any point of the resolution using absolute path */
268 snprintf(path, sizeof(path), "%s/%s", testdir, INSIDE_FILE);
269 T_EXPECT_POSIX_FAILURE(open(path, O_RDONLY | O_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 23: Path can not escape outside at any point of the resolution using absolute path");
270
271 T_EXPECT_POSIX_SUCCESS(close(root_fd), "Closing the root directory");
272 }
273
274 T_DECL(resolve_beneath_faccessat,
275 "test faccessat() using the AT_RESOLVE_BENEATH flag")
276 {
277 T_SETUPBEGIN;
278
279 T_ATEND(cleanup);
280 setup("resolve_beneath_faccessat");
281
282 T_SETUPEND;
283
284 T_LOG("Testing the faccessat() syscall using AT_RESOLVE_BENEATH");
285
286 /* Test Case 1: File within the directory */
287 T_EXPECT_POSIX_SUCCESS(faccessat(test_fd, "inside_file.txt", R_OK, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
288
289 /* Test Case 2: File using a symlink pointing outside */
290 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "symlink", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
291
292 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
293 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "../outside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
294
295 /* Test Case 4: File within a nested directory */
296 T_EXPECT_POSIX_SUCCESS(faccessat(test_fd, "nested/nested_file.txt", R_OK, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
297
298 /* Test Case 5: Symlink to a file in a nested directory */
299 T_EXPECT_POSIX_SUCCESS(faccessat(test_fd, "symlink_to_nested", R_OK, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
300
301 /* Test Case 6: File using an absolute path */
302 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "/etc/passwd", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
303
304 /* Test Case 7: Valid symlink to parent directory */
305 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "parent_symlink/outside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
306
307 /* Test Case 8: Circular symlink within directory */
308 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "circular_symlink", R_OK, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
309
310 /* Test Case 9: Path can not escape outside at any point of the resolution */
311 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "../test_dir/inside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
312
313 /* Test Case 10: File using a symlink pointing to absolute path */
314 T_EXPECT_POSIX_FAILURE(faccessat(test_fd, "symlink_absolute/test_dir/inside_file.txt", R_OK, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
315 }
316
317 T_DECL(resolve_beneath_fstatat,
318 "test fstatat() using the AT_RESOLVE_BENEATH flag")
319 {
320 struct stat buf;
321
322 T_SETUPBEGIN;
323
324 T_ATEND(cleanup);
325 setup("resolve_beneath_fstatat");
326
327 T_SETUPEND;
328
329 T_LOG("Testing the fstatat() syscall using AT_RESOLVE_BENEATH");
330
331 /* Test Case 1: File within the directory */
332 T_EXPECT_POSIX_SUCCESS(fstatat(test_fd, "inside_file.txt", &buf, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
333
334 /* Test Case 2: File using a symlink pointing outside */
335 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "symlink", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
336
337 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
338 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "../outside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
339
340 /* Test Case 4: File within a nested directory */
341 T_EXPECT_POSIX_SUCCESS(fstatat(test_fd, "nested/nested_file.txt", &buf, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
342
343 /* Test Case 5: Symlink to a file in a nested directory */
344 T_EXPECT_POSIX_SUCCESS(fstatat(test_fd, "symlink_to_nested", &buf, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
345
346 /* Test Case 6: File using an absolute path */
347 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "/etc/passwd", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
348
349 /* Test Case 7: Valid symlink to parent directory */
350 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "parent_symlink/outside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
351
352 /* Test Case 8: Circular symlink within directory */
353 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "circular_symlink", &buf, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
354
355 /* Test Case 9: Path can not escape outside at any point of the resolution */
356 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "../test_dir/inside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
357
358 /* Test Case 10: File using a symlink pointing to absolute path */
359 T_EXPECT_POSIX_FAILURE(fstatat(test_fd, "symlink_absolute/test_dir/inside_file.txt", &buf, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
360 }
361
362 T_DECL(resolve_beneath_fchmodat,
363 "test fchmodat() using the AT_RESOLVE_BENEATH flag")
364 {
365 T_SETUPBEGIN;
366
367 T_ATEND(cleanup);
368 setup("resolve_beneath_fchmodat");
369
370 T_SETUPEND;
371
372 T_LOG("Testing the fchmodat() syscall using AT_RESOLVE_BENEATH");
373
374 /* Test Case 1: File within the directory */
375 T_EXPECT_POSIX_SUCCESS(fchmodat(test_fd, "inside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
376
377 /* Test Case 2: File using a symlink pointing outside */
378 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "symlink", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
379
380 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
381 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "../outside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
382
383 /* Test Case 4: File within a nested directory */
384 T_EXPECT_POSIX_SUCCESS(fchmodat(test_fd, "nested/nested_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
385
386 /* Test Case 5: Symlink to a file in a nested directory */
387 T_EXPECT_POSIX_SUCCESS(fchmodat(test_fd, "symlink_to_nested", S_IRWXU, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
388
389 /* Test Case 6: File using an absolute path */
390 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "/etc/passwd", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
391
392 /* Test Case 7: Valid symlink to parent directory */
393 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "parent_symlink/outside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
394
395 /* Test Case 8: Circular symlink within directory */
396 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "circular_symlink", S_IRWXU, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
397
398 /* Test Case 9: Path can not escape outside at any point of the resolution */
399 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "../test_dir/inside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
400
401 /* Test Case 10: File using a symlink pointing to absolute path */
402 T_EXPECT_POSIX_FAILURE(fchmodat(test_fd, "symlink_absolute/test_dir/inside_file.txt", S_IRWXU, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
403 }
404
405 T_DECL(resolve_beneath_fchownat,
406 "test fchownat() using the AT_RESOLVE_BENEATH flag")
407 {
408 T_SETUPBEGIN;
409
410 T_ATEND(cleanup);
411 setup("resolve_beneath_fchownat");
412
413 T_SETUPEND;
414
415 T_LOG("Testing the fchownat() syscall using AT_RESOLVE_BENEATH");
416
417 /* Test Case 1: File within the directory */
418 T_EXPECT_POSIX_SUCCESS(fchownat(test_fd, "inside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
419
420 /* Test Case 2: File using a symlink pointing outside */
421 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "symlink", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
422
423 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
424 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "../outside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
425
426 /* Test Case 4: File within a nested directory */
427 T_EXPECT_POSIX_SUCCESS(fchownat(test_fd, "nested/nested_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
428
429 /* Test Case 5: Symlink to a file in a nested directory */
430 T_EXPECT_POSIX_SUCCESS(fchownat(test_fd, "symlink_to_nested", geteuid(), getgid(), AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
431
432 /* Test Case 6: File using an absolute path */
433 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "/etc/passwd", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
434
435 /* Test Case 7: Valid symlink to parent directory */
436 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "parent_symlink/outside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
437
438 /* Test Case 8: Circular symlink within directory */
439 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "circular_symlink", geteuid(), getgid(), AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
440
441 /* Test Case 9: Path can not escape outside at any point of the resolution */
442 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "../test_dir/inside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
443
444 /* Test Case 10: File using a symlink pointing to absolute path */
445 T_EXPECT_POSIX_FAILURE(fchownat(test_fd, "symlink_absolute/test_dir/inside_file.txt", geteuid(), getgid(), AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
446 }
447
448 T_DECL(resolve_beneath_linkat,
449 "test linkat() using the AT_RESOLVE_BENEATH flag")
450 {
451 T_SETUPBEGIN;
452
453 T_ATEND(cleanup);
454 setup("resolve_beneath_linkat");
455
456 T_SETUPEND;
457
458 T_LOG("Testing the linkat() syscall using AT_RESOLVE_BENEATH");
459
460 /* Test Case 1: File within the directory */
461 T_EXPECT_POSIX_SUCCESS(linkat(test_fd, "inside_file.txt", test_fd, "inside_file_2.txt", AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
462 unlinkat(test_fd, "inside_file_2.txt", 0);
463
464 /* Test Case 2: File using a symlink pointing outside */
465 T_EXPECT_POSIX_FAILURE(linkat(test_fd, "symlink/.", test_fd, "inside_file_2.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
466
467 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
468 T_EXPECT_POSIX_FAILURE(linkat(test_fd, "inside_file.txt", test_fd, "../outside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
469
470 /* Test Case 4: File within a nested directory */
471 T_EXPECT_POSIX_SUCCESS(linkat(test_fd, "nested/nested_file.txt", test_fd, "nested/nested_file_2.txt", AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
472 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
473
474 /* Test Case 5: Symlink to a file in a nested directory */
475 T_EXPECT_POSIX_SUCCESS(linkat(test_fd, "symlink_to_nested", test_fd, "nested/nested_file_2.txt", AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
476 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
477
478 /* Test Case 6: File using an absolute path */
479 T_EXPECT_POSIX_FAILURE(linkat(test_fd, "/etc/passwd", test_fd, "inside_file_2.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
480 }
481
482 T_DECL(resolve_beneath_unlinkat,
483 "test unlinkat() using the AT_RESOLVE_BENEATH flag")
484 {
485 int fd;
486
487 T_SETUPBEGIN;
488
489 T_ATEND(cleanup);
490 setup("resolve_beneath_unlinkat");
491
492 T_SETUPEND;
493
494 T_LOG("Testing the unlinkat() syscall using AT_RESOLVE_BENEATH");
495
496 /* Test Case 1: File within the directory */
497 T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "inside_file.txt", AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
498 if ((fd = openat(testdir_fd, INSIDE_FILE, O_CREAT | O_RDWR, 0777)) < 0) {
499 T_FAIL("Unable to recreate %s", INSIDE_FILE);
500 }
501 close(fd);
502
503 /* Test Case 2: File using a symlink pointing outside */
504 T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "symlink", AT_RESOLVE_BENEATH), "Test Case 2: File using a symlink pointing outside");
505 if (symlinkat(SYMLINK_FROM, testdir_fd, SYMLINK) < 0) {
506 T_FAIL("Unable to recreate %s", INSIDE_FILE);
507 }
508
509 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
510 T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "../outside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
511
512 /* Test Case 4: File within a nested directory */
513 T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "nested/nested_file.txt", AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
514 if ((fd = openat(testdir_fd, NESTED_FILE, O_CREAT | O_RDWR, 0777)) < 0) {
515 T_FAIL("Unable to recreate %s", NESTED_FILE);
516 }
517 close(fd);
518
519 /* Test Case 5: Symlink to a file in a nested directory */
520 T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "symlink_to_nested", AT_RESOLVE_BENEATH), "Test Case 5: Symlink //to a file within the same directory");
521 if (symlinkat(SYMLINK_TO_NESTED_FROM, testdir_fd, SYMLINK_TO_NESTED) < 0) {
522 T_FAIL("Unable to recreate %s", SYMLINK_TO_NESTED);
523 }
524
525 /* Test Case 6: File using an absolute path */
526 T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "/etc/passwd", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
527
528 /* Test Case 7: Valid symlink to parent directory */
529 T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "parent_symlink/outside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
530
531 /* Test Case 8: Circular symlink within directory */
532 T_EXPECT_POSIX_SUCCESS(unlinkat(test_fd, "circular_symlink", AT_RESOLVE_BENEATH), "Test Case 8: Circular symlink within directory");
533 if (symlinkat(CIRCULAR_SYMLINK_FROM, testdir_fd, CIRCULAR_SYMLINK) < 0) {
534 T_FAIL("Unable to recreate %s", CIRCULAR_SYMLINK);
535 }
536
537 /* Test Case 9: Path can not escape outside at any point of the resolution */
538 T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "../test_dir/inside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
539
540 /* Test Case 10: File using a symlink pointing to absolute path */
541 T_EXPECT_POSIX_FAILURE(unlinkat(test_fd, "symlink_absolute/test_dir/inside_file.txt", AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
542 }
543
544 T_DECL(resolve_beneath_utimensat,
545 "test utimensat() using the AT_RESOLVE_BENEATH flag")
546 {
547 static const struct timespec tptr[] = {
548 { 0x12345678, 987654321 },
549 { 0x15263748, 123456789 },
550 };
551
552 T_SETUPBEGIN;
553
554 T_ATEND(cleanup);
555 setup("resolve_beneath_utimensat");
556
557 T_SETUPEND;
558
559 T_LOG("Testing the utimensat() syscall using AT_RESOLVE_BENEATH");
560
561 /* Test Case 1: File within the directory */
562 T_EXPECT_POSIX_SUCCESS(utimensat(test_fd, "inside_file.txt", tptr, AT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
563
564 /* Test Case 2: File using a symlink pointing outside */
565 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "symlink", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
566
567 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
568 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "../outside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
569
570 /* Test Case 4: File within a nested directory */
571 T_EXPECT_POSIX_SUCCESS(utimensat(test_fd, "nested/nested_file.txt", tptr, AT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
572
573 /* Test Case 5: Symlink to a file in a nested directory */
574 T_EXPECT_POSIX_SUCCESS(utimensat(test_fd, "symlink_to_nested", tptr, AT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
575
576 /* Test Case 6: File using an absolute path */
577 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "/etc/passwd", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
578
579 /* Test Case 7: Valid symlink to parent directory */
580 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "parent_symlink/outside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
581
582 /* Test Case 8: Circular symlink within directory */
583 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "circular_symlink", tptr, AT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
584
585 /* Test Case 9: Path can not escape outside at any point of the resolution */
586 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "../test_dir/inside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
587
588 /* Test Case 10: File using a symlink pointing to absolute path */
589 T_EXPECT_POSIX_FAILURE(utimensat(test_fd, "symlink_absolute/test_dir/inside_file.txt", tptr, AT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
590 }
591
592 T_DECL(resolve_beneath_getxattr,
593 "test getxattr()/fgetxattr() using the XATTR_RESOLVE_BENEATH flag")
594 {
595 char xattr_buff[100];
596 const char *xattr = "test1234";
597 size_t xattr_len = strlen(xattr);
598
599 T_SETUPBEGIN;
600
601 T_ATEND(cleanup);
602 setup("resolve_beneath_getxattr");
603
604 /* Changing current directory to the test directory */
605 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
606
607 /* Setting extended attributes */
608 T_ASSERT_POSIX_SUCCESS(setxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to inside_file.txt");
609 T_ASSERT_POSIX_SUCCESS(setxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to outside_file.txt");
610 T_ASSERT_POSIX_SUCCESS(setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to nested_file.txt");
611
612 T_SETUPEND;
613
614 T_LOG("Testing the getxattr() syscall using XATTR_RESOLVE_BENEATH");
615
616 /* Test Case 1: File within the directory */
617 T_EXPECT_POSIX_SUCCESS(getxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
618
619 /* Test Case 2: File using a symlink pointing outside */
620 T_EXPECT_POSIX_FAILURE(getxattr("symlink", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
621
622 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
623 T_EXPECT_POSIX_FAILURE(getxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
624
625 /* Test Case 4: File within a nested directory */
626 T_EXPECT_POSIX_SUCCESS(getxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
627
628 /* Test Case 5: Symlink to a file in a nested directory */
629 T_EXPECT_POSIX_SUCCESS(getxattr("symlink_to_nested", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
630
631 /* Test Case 6: File using an absolute path */
632 T_EXPECT_POSIX_FAILURE(getxattr("/etc/passwd", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
633
634 /* Test Case 7: Valid symlink to parent directory */
635 T_EXPECT_POSIX_FAILURE(getxattr("parent_symlink/outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
636
637 /* Test Case 8: Circular symlink within directory */
638 T_EXPECT_POSIX_FAILURE(getxattr("circular_symlink", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
639
640 /* Test Case 9: Path can not escape outside at any point of the resolution */
641 T_EXPECT_POSIX_FAILURE(getxattr("../test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
642
643 /* Test Case 10: File using a symlink pointing to absolute path */
644 T_EXPECT_POSIX_FAILURE(getxattr("symlink_absolute/test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
645
646 T_LOG("Testing the fgetxattr() syscall using XATTR_RESOLVE_BENEATH");
647
648 /* Test Case 11: Verifying that fgetxattr() fails with EINVAL */
649 T_EXPECT_POSIX_FAILURE(fgetxattr(test_fd, XATTR_RESOURCEFORK_NAME, xattr_buff, sizeof(xattr_buff), 0, XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that fgetxattr() fails with EINVAL");
650 }
651
652 T_DECL(resolve_beneath_setxattr,
653 "test setxattr()/fsetxattr() using the XATTR_RESOLVE_BENEATH flag")
654 {
655 const char *xattr = "test1234";
656 size_t xattr_len = strlen(xattr);
657
658 T_SETUPBEGIN;
659
660 T_ATEND(cleanup);
661 setup("resolve_beneath_setxattr");
662
663 /* Changing current directory to the test directory */
664 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
665
666 T_SETUPEND;
667
668 T_LOG("Testing the setxattr() syscall using XATTR_RESOLVE_BENEATH");
669
670 /* Test Case 1: File within the directory */
671 T_EXPECT_POSIX_SUCCESS(setxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
672
673 /* Test Case 2: File using a symlink pointing outside */
674 T_EXPECT_POSIX_FAILURE(setxattr("symlink", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
675
676 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
677 T_EXPECT_POSIX_FAILURE(setxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
678
679 /* Test Case 4: File within a nested directory */
680 T_EXPECT_POSIX_SUCCESS(setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
681
682 /* Test Case 5: Symlink to a file in a nested directory */
683 T_EXPECT_POSIX_SUCCESS(setxattr("symlink_to_nested", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
684
685 /* Test Case 6: File using an absolute path */
686 T_EXPECT_POSIX_FAILURE(setxattr("/etc/passwd", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
687
688 /* Test Case 7: Valid symlink to parent directory */
689 T_EXPECT_POSIX_FAILURE(setxattr("parent_symlink/outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
690
691 /* Test Case 8: Circular symlink within directory */
692 T_EXPECT_POSIX_FAILURE(setxattr("circular_symlink", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
693
694 /* Test Case 9: Path can not escape outside at any point of the resolution */
695 T_EXPECT_POSIX_FAILURE(setxattr("../test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
696
697 /* Test Case 10: File using a symlink pointing to absolute path */
698 T_EXPECT_POSIX_FAILURE(setxattr("symlink_absolute/test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
699
700 T_LOG("Testing the fsetxattr() syscall using XATTR_RESOLVE_BENEATH");
701
702 /* Test Case 11: Verifying that fsetxattr() fails with EINVAL */
703 T_EXPECT_POSIX_FAILURE(fsetxattr(test_fd, XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that fsetxattr() fails with EINVAL");
704 }
705
706 T_DECL(resolve_beneath_listxattr,
707 "test listxattr()/flistxattr() using the XATTR_RESOLVE_BENEATH flag")
708 {
709 char xattr_buff[100];
710
711 T_SETUPBEGIN;
712
713 T_ATEND(cleanup);
714 setup("resolve_beneath_listxattr");
715
716 /* Changing current directory to the test directory */
717 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
718
719 T_SETUPEND;
720
721 T_LOG("Testing the listxattr() syscall using XATTR_RESOLVE_BENEATH");
722
723 /* Test Case 1: File within the directory */
724 T_EXPECT_POSIX_SUCCESS(listxattr("inside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
725
726 /* Test Case 2: File using a symlink pointing outside */
727 T_EXPECT_POSIX_FAILURE(listxattr("symlink", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
728
729 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
730 T_EXPECT_POSIX_FAILURE(listxattr("../outside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
731
732 /* Test Case 4: File within a nested directory */
733 T_EXPECT_POSIX_SUCCESS(listxattr("nested/nested_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
734
735 /* Test Case 5: Symlink to a file in a nested directory */
736 T_EXPECT_POSIX_SUCCESS(listxattr("symlink_to_nested", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
737
738 /* Test Case 6: File using an absolute path */
739 T_EXPECT_POSIX_FAILURE(listxattr("/etc/passwd", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
740
741 /* Test Case 7: Valid symlink to parent directory */
742 T_EXPECT_POSIX_FAILURE(listxattr("parent_symlink/outside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
743
744 /* Test Case 8: Circular symlink within directory */
745 T_EXPECT_POSIX_FAILURE(listxattr("circular_symlink", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
746
747 /* Test Case 9: Path can not escape outside at any point of the resolution */
748 T_EXPECT_POSIX_FAILURE(listxattr("../test_dir/inside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
749
750 /* Test Case 10: File using a symlink pointing to absolute path */
751 T_EXPECT_POSIX_FAILURE(listxattr("symlink_absolute/test_dir/inside_file.txt", xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
752
753 T_LOG("Testing the flistxattr() syscall using XATTR_RESOLVE_BENEATH");
754
755 /* Test Case 11: Verifying that flistxattr() fails with EINVAL */
756 T_EXPECT_POSIX_FAILURE(flistxattr(test_fd, xattr_buff, sizeof(xattr_buff), XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that flistxattr() fails with EINVAL");
757 }
758
759 T_DECL(resolve_beneath_removexattr,
760 "test removexattr()/fremovexattr() using the XATTR_RESOLVE_BENEATH flag")
761 {
762 const char *xattr = "test1234";
763 size_t xattr_len = strlen(xattr);
764
765 T_SETUPBEGIN;
766
767 T_ATEND(cleanup);
768 setup("resolve_beneath_removexattr");
769
770 /* Changing current directory to the test directory */
771 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
772
773 /* Setting extended attributes */
774 T_ASSERT_POSIX_SUCCESS(setxattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to inside_file.txt");
775 T_ASSERT_POSIX_SUCCESS(setxattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to outside_file.txt");
776 T_ASSERT_POSIX_SUCCESS(setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0), "Setting extended attributes to nested_file.txt");
777
778 T_SETUPEND;
779
780 T_LOG("Testing the removexattr() syscall using XATTR_RESOLVE_BENEATH");
781
782 /* Test Case 1: File within the directory */
783 T_EXPECT_POSIX_SUCCESS(removexattr("inside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), "Test Case 1: File within the directory");
784
785 /* Test Case 2: File using a symlink pointing outside */
786 T_EXPECT_POSIX_FAILURE(removexattr("symlink", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
787
788 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
789 T_EXPECT_POSIX_FAILURE(removexattr("../outside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
790
791 /* Test Case 4: File within a nested directory */
792 T_EXPECT_POSIX_SUCCESS(removexattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
793
794 if (setxattr("nested/nested_file.txt", XATTR_RESOURCEFORK_NAME, xattr, xattr_len, 0, 0) < 0) {
795 T_FAIL("Unable to setxattr to nested_file.txt");
796 }
797
798 /* Test Case 5: Symlink to a file in a nested directory */
799 T_EXPECT_POSIX_SUCCESS(removexattr("symlink_to_nested", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
800
801 /* Test Case 6: File using an absolute path */
802 T_EXPECT_POSIX_FAILURE(removexattr("/etc/passwd", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
803
804 /* Test Case 7: Valid symlink to parent directory */
805 T_EXPECT_POSIX_FAILURE(removexattr("parent_symlink/outside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
806
807 /* Test Case 8: Circular symlink within directory */
808 T_EXPECT_POSIX_FAILURE(removexattr("circular_symlink", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
809
810 /* Test Case 9: Path can not escape outside at any point of the resolution */
811 T_EXPECT_POSIX_FAILURE(removexattr("../test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
812
813 /* Test Case 10: File using a symlink pointing to absolute path */
814 T_EXPECT_POSIX_FAILURE(removexattr("symlink_absolute/test_dir/inside_file.txt", XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
815
816 T_LOG("Testing the fremovexattr() syscall using XATTR_RESOLVE_BENEATH");
817
818 /* Test Case 11: Verifying that fremovexattr() fails with EINVAL */
819 T_EXPECT_POSIX_FAILURE(fremovexattr(test_fd, XATTR_RESOURCEFORK_NAME, XATTR_RESOLVE_BENEATH), EINVAL, "Test Case 11: Verifying that fremovexattr() fails with EINVAL");
820 }
821
822 T_DECL(resolve_beneath_clonefile,
823 "test clonefile()/clonefileat()/fclonefileat() using the CLONE_RESOLVE_BENEATH flag")
824 {
825 int fd;
826 T_SETUPBEGIN;
827
828 T_ATEND(cleanup);
829 setup("resolve_beneath_clonefile");
830
831 /* Changing current directory to the test directory */
832 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
833
834 /* Open test file */
835 T_ASSERT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDWR, 0777)), "Opening %s", INSIDE_FILE);
836
837 T_SETUPEND;
838
839 T_LOG("Testing the clonefile() syscall using CLONE_RESOLVE_BENEATH");
840
841 /* Test Case 1: File within the directory */
842 T_EXPECT_POSIX_SUCCESS(clonefile("inside_file.txt", "inside_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 1: File within the directory");
843 unlink("inside_file_2.txt");
844
845 /* Test Case 2: File using a symlink pointing outside */
846 T_EXPECT_POSIX_FAILURE(clonefile("symlink", "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
847
848 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
849 T_EXPECT_POSIX_FAILURE(clonefile("inside_file.txt", "../outside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
850
851 /* Test Case 4: File within a nested directory */
852 T_EXPECT_POSIX_SUCCESS(clonefile("nested/nested_file.txt", "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
853 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
854
855 /* Test Case 5: Symlink to a file in a nested directory */
856 T_EXPECT_POSIX_SUCCESS(clonefile("symlink_to_nested", "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
857 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
858
859 /* Test Case 6: File using an absolute path */
860 T_EXPECT_POSIX_FAILURE(clonefile("/etc/passwd", "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
861
862 T_LOG("Testing the clonefileat() syscall using CLONE_RESOLVE_BENEATH");
863
864 /* Test Case 7: File within the directory */
865 T_EXPECT_POSIX_SUCCESS(clonefileat(test_fd, "inside_file.txt", test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 7: File within the directory");
866 unlinkat(test_fd, "inside_file_2.txt", 0);
867
868 /* Test Case 8: File using a symlink pointing outside */
869 T_EXPECT_POSIX_FAILURE(clonefileat(test_fd, "symlink", test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 8: File using a symlink pointing outside");
870
871 /* Test Case 9: Attempt to open a file using ".." to navigate outside */
872 T_EXPECT_POSIX_FAILURE(clonefileat(test_fd, "inside_file.txt", test_fd, "../outside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: File using \"..\" to navigate outside");
873
874 /* Test Case 10: File within a nested directory */
875 T_EXPECT_POSIX_SUCCESS(clonefileat(test_fd, "nested/nested_file.txt", test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 10: File within a nested directory");
876 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
877
878 /* Test Case 11: Symlink to a file in a nested directory */
879 T_EXPECT_POSIX_SUCCESS(clonefileat(test_fd, "symlink_to_nested", test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 11: Symlink to a file within the same directory");
880 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
881
882 /* Test Case 12: File using an absolute path */
883 T_EXPECT_POSIX_FAILURE(clonefileat(test_fd, "/etc/passwd", test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 12: File using an absolute path");
884
885 T_LOG("Testing the fclonefileat() syscall using CLONE_RESOLVE_BENEATH");
886
887 /* Test Case 13: File within the directory */
888 T_EXPECT_POSIX_SUCCESS(fclonefileat(fd, test_fd, "inside_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 13: File within the directory");
889 unlinkat(test_fd, "inside_file_2.txt", 0);
890
891 /* Test Case 14: File using a symlink pointing outside */
892 T_EXPECT_POSIX_FAILURE(fclonefileat(fd, test_fd, "symlink_absolute/test_dir/inside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: File using a symlink pointing outside");
893
894 /* Test Case 15: Attempt to open a file using ".." to navigate outside */
895 T_EXPECT_POSIX_FAILURE(fclonefileat(fd, test_fd, "../outside_file.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 15: File using \"..\" to navigate outside");
896
897 /* Test Case 16: File within a nested directory */
898 T_EXPECT_POSIX_SUCCESS(fclonefileat(fd, test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 16: File within a nested directory");
899 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
900
901 /* Test Case 17: Symlink to a file in a nested directory */
902 T_EXPECT_POSIX_SUCCESS(fclonefileat(fd, test_fd, "nested/nested_file_2.txt", CLONE_RESOLVE_BENEATH), "Test Case 17: Symlink to a file within the same directory");
903 unlinkat(test_fd, "nested/nested_file_2.txt", 0);
904
905 /* Test Case 18: File using an absolute path */
906 T_EXPECT_POSIX_FAILURE(fclonefileat(fd, test_fd, "/etc/inside_file_2.txt", CLONE_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: File using an absolute path");
907
908 T_EXPECT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
909 }
910
911 T_DECL(resolve_beneath_renamex_np,
912 "test renamex_np()/renameatx_np() using the RENAME_RESOLVE_BENEATH flag")
913 {
914 T_SETUPBEGIN;
915
916 T_ATEND(cleanup);
917 setup("resolve_beneath_renamex_np");
918
919 /* Changing current directory to the test directory */
920 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
921
922 T_SETUPEND;
923
924 T_LOG("Testing the renamex_np() syscall using RENAME_RESOLVE_BENEATH");
925
926 /* Test Case 1: File within the directory */
927 T_EXPECT_POSIX_SUCCESS(renamex_np("inside_file.txt", "inside_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 1: File within the directory");
928 if (renamex_np("inside_file_2.txt", "inside_file.txt", 0)) {
929 T_FAIL("Unable to rename inside_file_2.txt to inside_file.txt");
930 }
931
932 /* Test Case 2: File using a symlink pointing outside */
933 T_EXPECT_POSIX_FAILURE(renamex_np("symlink/.", "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
934
935 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
936 T_EXPECT_POSIX_FAILURE(renamex_np("inside_file.txt", "../outside_file.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
937
938 /* Test Case 4: File within a nested directory */
939 T_EXPECT_POSIX_SUCCESS(renamex_np("nested/nested_file.txt", "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
940 if (renamex_np("nested/nested_file_2.txt", "nested/nested_file.txt", 0)) {
941 T_FAIL("Unable to rename nested/nested_file_2.txt to nested/nested_file.txt");
942 }
943
944 /* Test Case 5: Symlink to a file in a nested directory */
945 T_EXPECT_POSIX_SUCCESS(renamex_np("symlink_to_nested", "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
946 if (renamex_np("nested/nested_file_2.txt", "symlink_to_nested", 0)) {
947 T_FAIL("Unable to rename nested/nested_file_2.txt to symlink_to_nested");
948 }
949
950 /* Test Case 6: File using an absolute path */
951 T_EXPECT_POSIX_FAILURE(renamex_np("/etc/passwd", "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
952
953 T_LOG("Testing the renameatx_np() syscall using RENAME_RESOLVE_BENEATH");
954
955 /* Test Case 7: File within the directory */
956 T_EXPECT_POSIX_SUCCESS(renameatx_np(test_fd, "inside_file.txt", test_fd, "inside_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 7: File within the directory");
957 if (renamex_np("inside_file_2.txt", "inside_file.txt", 0)) {
958 T_FAIL("Unable to rename inside_file_2.txt to inside_file.txt");
959 }
960
961 /* Test Case 8: File using a symlink pointing outside */
962 T_EXPECT_POSIX_FAILURE(renameatx_np(test_fd, "symlink/.", test_fd, "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 8: File using a symlink pointing outside");
963
964 /* Test Case 9: Attempt to open a file using ".." to navigate outside */
965 T_EXPECT_POSIX_FAILURE(renameatx_np(test_fd, "inside_file.txt", test_fd, "../outside_file.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: File using \"..\" to navigate outside");
966
967 /* Test Case 10: File within a nested directory */
968 T_EXPECT_POSIX_SUCCESS(renameatx_np(test_fd, "nested/nested_file.txt", test_fd, "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 10: File within a nested directory");
969 if (renamex_np("nested/nested_file_2.txt", "nested/nested_file.txt", 0)) {
970 T_FAIL("Unable to rename nested/nested_file_2.txt to nested/nested_file.txt");
971 }
972
973 /* Test Case 11: Symlink to a file in a nested directory */
974 T_EXPECT_POSIX_SUCCESS(renameatx_np(test_fd, "symlink_to_nested", test_fd, "nested/nested_file_2.txt", RENAME_RESOLVE_BENEATH), "Test Case 11: Symlink to a file within the same directory");
975 if (renamex_np("nested/nested_file_2.txt", "symlink_to_nested", 0)) {
976 T_FAIL("Unable to rename nested/nested_file_2.txt to symlink_to_nested");
977 }
978
979 /* Test Case 12: File using an absolute path */
980 T_EXPECT_POSIX_FAILURE(renameatx_np(test_fd, "/etc/passwd", test_fd, "inside_file_2.txt", RENAME_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 12: File using an absolute path");
981 }
982
983 T_DECL(resolve_beneath_getattrlist,
984 "test getattrlist()/fgetattrlist()/getattrlistat() using the FSOPT_RESOLVE_BENEATH flag")
985 {
986 int fd;
987
988 struct myattrbuf {
989 uint32_t length;
990 attribute_set_t returned_attrs;
991 vol_attributes_attr_t vol_attributes;
992 attrreference_t fstypename_ref;
993 uint32_t fssubtype;
994 char fstypename[MFSTYPENAMELEN];
995 } attrbuf;
996
997 struct attrlist attrs = {
998 .bitmapcount = ATTR_BIT_MAP_COUNT,
999 .commonattr = ATTR_CMN_RETURNED_ATTRS,
1000 /*
1001 * Request ATTR_VOL_ATTRIBUTES to ensure that
1002 * ATTR_VOL_FSTYPENAME and ATTR_VOL_FSSUBTYPE
1003 * are packed into the buffer *after*.
1004 */
1005 .volattr = ATTR_VOL_INFO | ATTR_VOL_ATTRIBUTES |
1006 ATTR_VOL_FSTYPENAME | ATTR_VOL_FSSUBTYPE,
1007 };
1008
1009 T_SETUPBEGIN;
1010
1011 T_ATEND(cleanup);
1012 setup("resolve_beneath_getattrlist");
1013
1014 /* Changing current directory to the test directory */
1015 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
1016
1017 /* Open test file */
1018 T_ASSERT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDWR, 0777)), "Opening %s", INSIDE_FILE);
1019
1020 T_SETUPEND;
1021
1022 T_LOG("Testing the getattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1023
1024 /* Test Case 1: File within the directory */
1025 T_EXPECT_POSIX_SUCCESS(getattrlist("inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
1026
1027 /* Test Case 2: File using a symlink pointing outside */
1028 T_EXPECT_POSIX_FAILURE(getattrlist("symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
1029
1030 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
1031 T_EXPECT_POSIX_FAILURE(getattrlist("../outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
1032
1033 /* Test Case 4: File within a nested directory */
1034 T_EXPECT_POSIX_SUCCESS(getattrlist("nested/nested_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
1035
1036 /* Test Case 5: Symlink to a file in a nested directory */
1037 T_EXPECT_POSIX_SUCCESS(getattrlist("symlink_to_nested", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
1038
1039 /* Test Case 6: File using an absolute path */
1040 T_EXPECT_POSIX_FAILURE(getattrlist("/etc/passwd", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
1041
1042 /* Test Case 7: Valid symlink to parent directory */
1043 T_EXPECT_POSIX_FAILURE(getattrlist("parent_symlink/outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
1044
1045 /* Test Case 8: Circular symlink within directory */
1046 T_EXPECT_POSIX_FAILURE(getattrlist("circular_symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
1047
1048 /* Test Case 9: Path can not escape outside at any point of the resolution */
1049 T_EXPECT_POSIX_FAILURE(getattrlist("../test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
1050
1051 /* Test Case 10: File using a symlink pointing to absolute path */
1052 T_EXPECT_POSIX_FAILURE(getattrlist("symlink_absolute/test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
1053
1054 T_LOG("Testing the fgetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1055
1056 /* Test Case 11: fgetattrlist() syscall using FSOPT_RESOLVE_BENEATH */
1057 T_EXPECT_POSIX_SUCCESS(fgetattrlist(fd, &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 11: fgetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1058
1059 T_LOG("Testing the getattrlistat() syscall using FSOPT_RESOLVE_BENEATH");
1060
1061 /* Test Case 12: File within the directory */
1062 T_EXPECT_POSIX_SUCCESS(getattrlistat(test_fd, "inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 12: File within the directory");
1063
1064 /* Test Case 13: File using a symlink pointing outside */
1065 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 13: File using a symlink pointing outside");
1066
1067 /* Test Case 14: Attempt to open a file using ".." to navigate outside */
1068 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "../outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: File using \"..\" to navigate outside");
1069
1070 /* Test Case 15: File within a nested directory */
1071 T_EXPECT_POSIX_SUCCESS(getattrlistat(test_fd, "nested/nested_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 15: File within a nested directory");
1072
1073 /* Test Case 16: Symlink to a file in a nested directory */
1074 T_EXPECT_POSIX_SUCCESS(getattrlistat(test_fd, "symlink_to_nested", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), "Test Case 16: Symlink to a file within the same directory");
1075
1076 /* Test Case 17: File using an absolute path */
1077 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "/etc/passwd", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 17: File using an absolute path");
1078
1079 /* Test Case 18: Valid symlink to parent directory */
1080 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "parent_symlink/outside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: Valid symlink to parent directory");
1081
1082 /* Test Case 19: Circular symlink within directory */
1083 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "circular_symlink", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 19: Circular symlink within directory");
1084
1085 /* Test Case 20: Path can not escape outside at any point of the resolution */
1086 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "../test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 20: Path can not escape outside at any point of the resolution");
1087
1088 /* Test Case 21: File using a symlink pointing to absolute path */
1089 T_EXPECT_POSIX_FAILURE(getattrlistat(test_fd, "symlink_absolute/test_dir/inside_file.txt", &attrs, &attrbuf, sizeof(attrbuf), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 21: File using a symlink pointing to absolute path");
1090
1091 T_EXPECT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
1092 }
1093
1094 T_DECL(resolve_beneath_setattrlist,
1095 "test setattrlist()/fsetattrlist()/setattrlistat() using the FSOPT_RESOLVE_BENEATH flag")
1096 {
1097 int fd;
1098 int flags;
1099 struct attrlist attrlist;
1100
1101 T_SETUPBEGIN;
1102
1103 flags = 0;
1104 memset(&attrlist, 0, sizeof(attrlist));
1105 attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
1106 attrlist.commonattr = ATTR_CMN_FLAGS;
1107
1108 T_ATEND(cleanup);
1109 setup("resolve_beneath_setattrlist");
1110
1111 /* Changing current directory to the test directory */
1112 T_ASSERT_POSIX_SUCCESS(fchdir(test_fd), "Changing directory to %s/%s", testdir, TEST_DIR);
1113
1114 /* Open test file */
1115 T_ASSERT_POSIX_SUCCESS((fd = open("inside_file.txt", O_RDWR, 0777)), "Opening %s", INSIDE_FILE);
1116
1117 T_SETUPEND;
1118
1119 T_LOG("Testing the setattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1120
1121 /* Test Case 1: File within the directory */
1122 T_EXPECT_POSIX_SUCCESS(setattrlist("inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 1: File within the directory");
1123
1124 /* Test Case 2: File using a symlink pointing outside */
1125 T_EXPECT_POSIX_FAILURE(setattrlist("symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 2: File using a symlink pointing outside");
1126
1127 /* Test Case 3: Attempt to open a file using ".." to navigate outside */
1128 T_EXPECT_POSIX_FAILURE(setattrlist("../outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 3: File using \"..\" to navigate outside");
1129
1130 /* Test Case 4: File within a nested directory */
1131 T_EXPECT_POSIX_SUCCESS(setattrlist("nested/nested_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 4: File within a nested directory");
1132
1133 /* Test Case 5: Symlink to a file in a nested directory */
1134 T_EXPECT_POSIX_SUCCESS(setattrlist("symlink_to_nested", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 5: Symlink to a file within the same directory");
1135
1136 /* Test Case 6: File using an absolute path */
1137 T_EXPECT_POSIX_FAILURE(setattrlist("/etc/passwd", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 6: File using an absolute path");
1138
1139 /* Test Case 7: Valid symlink to parent directory */
1140 T_EXPECT_POSIX_FAILURE(setattrlist("parent_symlink/outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 7: Valid symlink to parent directory");
1141
1142 /* Test Case 8: Circular symlink within directory */
1143 T_EXPECT_POSIX_FAILURE(setattrlist("circular_symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 8: Circular symlink within directory");
1144
1145 /* Test Case 9: Path can not escape outside at any point of the resolution */
1146 T_EXPECT_POSIX_FAILURE(setattrlist("../test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 9: Path can not escape outside at any point of the resolution");
1147
1148 /* Test Case 10: File using a symlink pointing to absolute path */
1149 T_EXPECT_POSIX_FAILURE(setattrlist("symlink_absolute/test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 10: File using a symlink pointing to absolute path");
1150
1151 T_LOG("Testing the fsetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1152
1153 /* Test Case 11: fsetattrlist() syscall using FSOPT_RESOLVE_BENEATH */
1154 T_EXPECT_POSIX_SUCCESS(fsetattrlist(fd, &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 11: fsetattrlist() syscall using FSOPT_RESOLVE_BENEATH");
1155
1156 T_LOG("Testing the setattrlistat() syscall using FSOPT_RESOLVE_BENEATH");
1157
1158 /* Test Case 12: File within the directory */
1159 T_EXPECT_POSIX_SUCCESS(setattrlistat(test_fd, "inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 12: File within the directory");
1160
1161 /* Test Case 13: File using a symlink pointing outside */
1162 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 13: File using a symlink pointing outside");
1163
1164 /* Test Case 14: Attempt to open a file using ".." to navigate outside */
1165 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "../outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 14: File using \"..\" to navigate outside");
1166
1167 /* Test Case 15: File within a nested directory */
1168 T_EXPECT_POSIX_SUCCESS(setattrlistat(test_fd, "nested/nested_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 15: File within a nested directory");
1169
1170 /* Test Case 16: Symlink to a file in a nested directory */
1171 T_EXPECT_POSIX_SUCCESS(setattrlistat(test_fd, "symlink_to_nested", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), "Test Case 16: Symlink to a file within the same directory");
1172
1173 /* Test Case 17: File using an absolute path */
1174 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "/etc/passwd", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 17: File using an absolute path");
1175
1176 /* Test Case 18: Valid symlink to parent directory */
1177 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "parent_symlink/outside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 18: Valid symlink to parent directory");
1178
1179 /* Test Case 19: Circular symlink within directory */
1180 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "circular_symlink", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ELOOP, "Test Case 19: Circular symlink within directory");
1181
1182 /* Test Case 20: Path can not escape outside at any point of the resolution */
1183 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "../test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 20: Path can not escape outside at any point of the resolution");
1184
1185 /* Test Case 21: File using a symlink pointing to absolute path */
1186 T_EXPECT_POSIX_FAILURE(setattrlistat(test_fd, "symlink_absolute/test_dir/inside_file.txt", &attrlist, &flags, sizeof(flags), FSOPT_RESOLVE_BENEATH), ENOTCAPABLE, "Test Case 21: File using a symlink pointing to absolute path");
1187
1188 T_EXPECT_POSIX_SUCCESS(close(fd), "Closing %s", INSIDE_FILE);
1189 }
1190
1191 static volatile int stop_race = 0;
1192 static char race_mount_path[MAXPATHLEN];
1193
1194 static void *
mounter_thread(void * arg)1195 mounter_thread(void *arg)
1196 {
1197 while (!stop_race) {
1198 /* Try to mount devfs over the directory */
1199 if (mount("devfs", race_mount_path, 0, NULL) == 0) {
1200 /* Immediately unmount it */
1201 unmount(race_mount_path, MNT_FORCE);
1202 }
1203 /* Small delay to avoid overwhelming the system */
1204 usleep(1000);
1205 }
1206
1207 unmount(race_mount_path, MNT_FORCE);
1208 return NULL;
1209 }
1210
1211 T_DECL(resolve_beneath_mount_race,
1212 "test O_RESOLVE_BENEATH with concurrent mount operations")
1213 {
1214 int underfd;
1215 char underlying_dir[MAXPATHLEN];
1216 pthread_t mounter;
1217 time_t start_time, current_time;
1218 int race_won = 0;
1219
1220 T_SETUPBEGIN;
1221
1222 T_ATEND(cleanup);
1223 setup("resolve_beneath_mount_race");
1224
1225 /* Create the mount point and underlying directory structure */
1226 snprintf(race_mount_path, sizeof(race_mount_path), "%s/beneath", testdir);
1227 snprintf(underlying_dir, sizeof(underlying_dir), "%s/beneath/underlying_dir", testdir);
1228
1229 T_ASSERT_POSIX_SUCCESS(mkdir(race_mount_path, 0777), "Creating mount point %s", race_mount_path);
1230 T_ASSERT_POSIX_SUCCESS(mkdir(underlying_dir, 0777), "Creating underlying directory %s", underlying_dir);
1231
1232 T_ASSERT_POSIX_SUCCESS((underfd = open(race_mount_path, O_SEARCH, 0777)), "Opening mount point %s", race_mount_path);
1233
1234 T_SETUPEND;
1235
1236 T_LOG("Testing O_RESOLVE_BENEATH with mount race condition for 5 seconds");
1237
1238 /* Start the mounter thread and immediately begin the race */
1239 stop_race = 0;
1240 start_time = time(NULL);
1241 T_ASSERT_POSIX_ZERO(pthread_create(&mounter, NULL, mounter_thread, NULL), "Creating mounter thread");
1242
1243 /* Try to win the race for 5 seconds while the mounter thread is running */
1244 while ((time(NULL) - start_time) < 5) {
1245 int dotdot_fd = openat(underfd, "underlying_dir/../../../../../..", O_RESOLVE_BENEATH);
1246 if (dotdot_fd != -1) {
1247 char path[1024];
1248 if (fcntl(dotdot_fd, F_GETPATH, path) == 0) {
1249 T_LOG("Race condition exploit attempt succeeded, got path: %s", path);
1250 /* If we got here, the old vulnerable code would have allowed this */
1251 race_won = 1;
1252 close(dotdot_fd);
1253 break;
1254 }
1255 close(dotdot_fd);
1256 }
1257 }
1258
1259 /* Stop the mounter thread */
1260 stop_race = 1;
1261 T_ASSERT_POSIX_ZERO(pthread_join(mounter, NULL), "Joining mounter thread");
1262
1263 /* With the fix, we should never win the race */
1264 T_EXPECT_EQ(race_won, 0, "Race condition should not succeed with the fix in place");
1265
1266 /* Clean up - only after the helper thread has finished */
1267 close(underfd);
1268 rmdir(underlying_dir);
1269 rmdir(race_mount_path);
1270 }
1271