1 /*
2 Copyright 2020 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <test.h>
26
27 #include <definitions.h>
28 #include <file_lib.h>
29 #include <stdbool.h>
30
31 #define TEMP_DIR "/tmp/file_lib_test"
32 #define TEST_FILE "file_lib_test.txt"
33 #define TEST_LINK "file_lib_test.link"
34 #define TEST_DIR "file_lib_test.dir"
35 #define TEST_SUBDIR "file_lib_test.sub"
36 #define TEST_SUBSUBDIR "file_lib_test.sub/sub"
37 #define TEST_STRING "BLUE balloon"
38 #define TEST_SUBSTRING "YELLOW balloon"
39 #define TEST_SUBSUBSTRING "RED balloon"
40
41 // These are just a way to pass parameters into switch_symlink_hook().
42 // Since it can be called from CFEngine code, we need to do it like this.
43 // The way COUNTDOWN works is that it counts down towards zero for each
44 // component in the path passed to safe_open(). When it reaches zero,
45 // the symlink will be inserted at that moment.
46 int TEST_SYMLINK_COUNTDOWN = 0;
47 const char *TEST_SYMLINK_NAME = "";
48 const char *TEST_SYMLINK_TARGET = "";
49 // If this is true, when the countdown has been reached, we alternate
50 // between deleting and creating the link. This is to test the race condition
51 // when creating files. Defaults to false.
52 bool TEST_SYMLINK_ALTERNATE = false;
53
54 static int ORIG_DIR = -1;
55
switch_symlink_hook(void)56 void switch_symlink_hook(void)
57 {
58 if (--TEST_SYMLINK_COUNTDOWN <= 0) {
59 if (TEST_SYMLINK_COUNTDOWN == 0
60 || (TEST_SYMLINK_ALTERNATE && (TEST_SYMLINK_COUNTDOWN & 1)))
61 {
62 rmdir(TEST_SYMLINK_NAME);
63 unlink(TEST_SYMLINK_NAME);
64 }
65 if (TEST_SYMLINK_COUNTDOWN == 0
66 || (TEST_SYMLINK_ALTERNATE && !(TEST_SYMLINK_COUNTDOWN & 1)))
67 {
68 assert_int_equal(symlink(TEST_SYMLINK_TARGET, TEST_SYMLINK_NAME), 0);
69 // If we already are root, we must force the link to be non-root,
70 // otherwise the test may have no purpose.
71 if (getuid() == 0)
72 {
73 // 100 exists in most installations, but it doesn't really matter.
74 assert_int_equal(lchown(TEST_SYMLINK_NAME, 100, 100), 0);
75 }
76 }
77 }
78 }
79
complain_missing_sudo(const char * function)80 static void complain_missing_sudo(const char *function)
81 {
82 printf("WARNING!!! %s will not run without root privileges.\n"
83 "Tried using sudo with no luck.\n", function);
84 }
85
chdir_or_exit(const char * path)86 static void chdir_or_exit(const char *path)
87 {
88 if (chdir(path) < 0)
89 {
90 // Don't risk writing into folders we shouldn't. Just bail.
91 exit(EXIT_FAILURE);
92 }
93 }
94
save_test_dir(void)95 static void save_test_dir(void)
96 {
97 ORIG_DIR = open(".", O_RDONLY);
98 assert_true(ORIG_DIR >= 0);
99 }
100
close_test_dir(void)101 static void close_test_dir(void)
102 {
103 close(ORIG_DIR);
104 }
105
clear_tempfiles(void)106 static void clear_tempfiles(void)
107 {
108 unlink(TEMP_DIR "/" TEST_FILE);
109 unlink(TEMP_DIR "/" TEST_LINK);
110 unlink(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE);
111 unlink(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE);
112 rmdir(TEMP_DIR "/" TEST_SUBSUBDIR);
113 rmdir(TEMP_DIR "/" TEST_SUBDIR);
114 rmdir(TEMP_DIR);
115 }
116
setup_tempfiles(void)117 static void setup_tempfiles(void)
118 {
119 clear_tempfiles();
120
121 mkdir(TEMP_DIR, 0755);
122 chdir_or_exit(TEMP_DIR);
123 mkdir(TEST_SUBDIR, 0755);
124 mkdir(TEST_SUBSUBDIR, 0755);
125 int fd = open(TEMP_DIR "/" TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
126 int result = write(fd, TEST_STRING, strlen(TEST_STRING));
127 close(fd);
128 fd = open(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
129 result = write(fd, TEST_SUBSTRING, strlen(TEST_SUBSTRING));
130 close(fd);
131 fd = open(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
132 result = write(fd, TEST_SUBSUBSTRING, strlen(TEST_SUBSUBSTRING));
133 close(fd);
134
135 if (getuid() == 0)
136 {
137 // 100 exists in most installations, but it doesn't really matter.
138 result = chown(TEMP_DIR "/" TEST_FILE, 100, 100);
139 result = chown(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, 100, 100);
140 result = chown(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, 100, 100);
141 result = chown(TEMP_DIR "/" TEST_SUBDIR, 100, 100);
142 result = chown(TEMP_DIR "/" TEST_SUBSUBDIR, 100, 100);
143 }
144
145 (void)result;
146
147 TEST_SYMLINK_ALTERNATE = false;
148 }
149
return_to_test_dir(void)150 static void return_to_test_dir(void)
151 {
152 if (fchdir(ORIG_DIR) < 0)
153 {
154 // Don't risk writing into folders we shouldn't. Just bail.
155 exit(EXIT_FAILURE);
156 }
157 }
158
check_contents(int fd,const char * str)159 static void check_contents(int fd, const char *str)
160 {
161 char buf[strlen(str) + 1];
162 assert_int_equal(read(fd, buf, strlen(str)), strlen(str));
163 buf[strlen(str)] = '\0';
164 assert_string_equal(buf, str);
165 }
166
test_safe_open_currentdir(void)167 static void test_safe_open_currentdir(void)
168 {
169 setup_tempfiles();
170
171 int fd;
172 assert_true((fd = safe_open(TEST_FILE, O_RDONLY)) >= 0);
173 check_contents(fd, TEST_STRING);
174 close(fd);
175
176 return_to_test_dir();
177 }
178
test_safe_open_subdir(void)179 static void test_safe_open_subdir(void)
180 {
181 setup_tempfiles();
182
183 int fd;
184 assert_true((fd = safe_open(TEST_SUBDIR "/" TEST_FILE, O_RDONLY)) >= 0);
185 check_contents(fd, TEST_SUBSTRING);
186 close(fd);
187
188 return_to_test_dir();
189 }
190
test_safe_open_subsubdir(void)191 static void test_safe_open_subsubdir(void)
192 {
193 setup_tempfiles();
194
195 int fd;
196 assert_true((fd = safe_open(TEST_SUBSUBDIR "/" TEST_FILE, O_RDONLY)) >= 0);
197 check_contents(fd, TEST_SUBSUBSTRING);
198 close(fd);
199
200 return_to_test_dir();
201 }
202
test_safe_open_updir(void)203 static void test_safe_open_updir(void)
204 {
205 setup_tempfiles();
206
207 chdir_or_exit(TEST_SUBDIR);
208
209 int fd;
210 assert_true((fd = safe_open("../" TEST_FILE, O_RDONLY)) >= 0);
211 check_contents(fd, TEST_STRING);
212 close(fd);
213
214 return_to_test_dir();
215 }
216
test_safe_open_upupdir(void)217 static void test_safe_open_upupdir(void)
218 {
219 setup_tempfiles();
220
221 chdir_or_exit(TEST_SUBSUBDIR);
222
223 int fd;
224 assert_true((fd = safe_open("../../" TEST_FILE, O_RDONLY)) >= 0);
225 check_contents(fd, TEST_STRING);
226 close(fd);
227
228 return_to_test_dir();
229 }
230
test_safe_open_generic_relative_dir(void)231 static void test_safe_open_generic_relative_dir(void)
232 {
233 setup_tempfiles();
234
235 int fd;
236 assert_true((fd = safe_open(TEST_SUBSUBDIR "/../" TEST_FILE, O_RDONLY)) >= 0);
237 check_contents(fd, TEST_SUBSTRING);
238 close(fd);
239
240 return_to_test_dir();
241 }
242
test_safe_open_generic_absolute_dir(void)243 static void test_safe_open_generic_absolute_dir(void)
244 {
245 setup_tempfiles();
246
247 int fd;
248 assert_true((fd = safe_open(TEMP_DIR "/"
249 TEST_SUBDIR "/../"
250 TEST_SUBSUBDIR "/../"
251 TEST_FILE, O_RDONLY)) >= 0);
252 check_contents(fd, TEST_SUBSTRING);
253 close(fd);
254
255 return_to_test_dir();
256 }
257
test_safe_open_extra_slashes_relative(void)258 static void test_safe_open_extra_slashes_relative(void)
259 {
260 setup_tempfiles();
261
262 int fd;
263 assert_true((fd = safe_open(TEST_SUBSUBDIR "//..////" TEST_FILE, O_RDONLY)) >= 0);
264 check_contents(fd, TEST_SUBSTRING);
265 close(fd);
266
267 return_to_test_dir();
268 }
269
test_safe_open_extra_slashes_absolute(void)270 static void test_safe_open_extra_slashes_absolute(void)
271 {
272 setup_tempfiles();
273
274 chdir_or_exit(TEST_SUBSUBDIR);
275
276 int fd;
277 assert_true((fd = safe_open("/" TEMP_DIR "/"
278 TEST_SUBDIR "//..//"
279 TEST_SUBSUBDIR "/..//"
280 TEST_FILE, O_RDONLY)) >= 0);
281 check_contents(fd, TEST_SUBSTRING);
282 close(fd);
283
284 return_to_test_dir();
285 }
286
test_safe_open_unsafe_symlink(void)287 static void test_safe_open_unsafe_symlink(void)
288 {
289 setup_tempfiles();
290
291 TEST_SYMLINK_COUNTDOWN = 1;
292 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
293 TEST_SYMLINK_TARGET = "/etc/passwd";
294 switch_symlink_hook();
295
296 assert_true(safe_open(TEMP_DIR "/" TEST_LINK, O_RDONLY) < 0);
297 assert_int_equal(errno, ENOLINK);
298
299 return_to_test_dir();
300 }
301
test_safe_open_safe_symlink(void)302 static void test_safe_open_safe_symlink(void)
303 {
304 setup_tempfiles();
305
306 TEST_SYMLINK_COUNTDOWN = 1;
307 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
308 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_FILE;
309 switch_symlink_hook();
310
311 int fd;
312 assert_true((fd = safe_open(TEMP_DIR "/" TEST_LINK, O_RDONLY)) >= 0);
313 check_contents(fd, TEST_STRING);
314 close(fd);
315
316 return_to_test_dir();
317 }
318
test_safe_open_unsafe_inserted_symlink(void)319 static void test_safe_open_unsafe_inserted_symlink(void)
320 {
321 setup_tempfiles();
322
323 TEST_SYMLINK_COUNTDOWN = 1;
324 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
325 TEST_SYMLINK_TARGET = "/etc/passwd";
326 // Not calling this function will call it right in the middle of the
327 // safe_open() instead.
328 //switch_symlink_hook();
329
330 assert_true(safe_open(TEST_LINK, O_RDONLY) < 0);
331 assert_int_equal(errno, ENOENT);
332
333 return_to_test_dir();
334 }
335
test_safe_open_safe_inserted_symlink(void)336 static void test_safe_open_safe_inserted_symlink(void)
337 {
338 setup_tempfiles();
339
340 TEST_SYMLINK_COUNTDOWN = 1;
341 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
342 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_FILE;
343 // Not calling this function will call it right in the middle of the
344 // safe_open() instead.
345 //switch_symlink_hook();
346
347 assert_true(safe_open(TEST_LINK, O_RDONLY) < 0);
348 assert_int_equal(errno, ENOENT);
349
350 return_to_test_dir();
351 }
352
test_safe_open_unsafe_switched_symlink(void)353 static void test_safe_open_unsafe_switched_symlink(void)
354 {
355 setup_tempfiles();
356
357 TEST_SYMLINK_COUNTDOWN = 1;
358 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
359 TEST_SYMLINK_TARGET = "/etc/passwd";
360 // Not calling this function will call it right in the middle of the
361 // safe_open() instead.
362 //switch_symlink_hook();
363
364 assert_true(safe_open(TEST_FILE, O_RDONLY) < 0);
365 assert_int_equal(errno, ENOLINK);
366
367 return_to_test_dir();
368 }
369
test_safe_open_safe_switched_symlink(void)370 static void test_safe_open_safe_switched_symlink(void)
371 {
372 setup_tempfiles();
373
374 TEST_SYMLINK_COUNTDOWN = 3;
375 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
376 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE;
377 // Not calling this function will call it right in the middle of the
378 // safe_open() instead.
379 //switch_symlink_hook();
380
381 int fd;
382 assert_true((fd = safe_open(TEMP_DIR "/" TEST_FILE, O_RDONLY)) >= 0);
383 check_contents(fd, TEST_SUBSTRING);
384 close(fd);
385
386 return_to_test_dir();
387 }
388
test_safe_open_unsafe_dir_symlink(void)389 static void test_safe_open_unsafe_dir_symlink(void)
390 {
391 setup_tempfiles();
392
393 TEST_SYMLINK_COUNTDOWN = 1;
394 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
395 TEST_SYMLINK_TARGET = "/etc";
396 switch_symlink_hook();
397
398 assert_true(safe_open(TEMP_DIR "/" TEST_LINK "/passwd", O_RDONLY) < 0);
399 assert_int_equal(errno, ENOLINK);
400
401 return_to_test_dir();
402 }
403
test_safe_open_safe_dir_symlink(void)404 static void test_safe_open_safe_dir_symlink(void)
405 {
406 setup_tempfiles();
407
408 TEST_SYMLINK_COUNTDOWN = 1;
409 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
410 TEST_SYMLINK_TARGET = TEST_SUBDIR;
411 switch_symlink_hook();
412
413 int fd;
414 assert_true((fd = safe_open(TEST_LINK "/" TEST_FILE, O_RDONLY)) >= 0);
415 check_contents(fd, TEST_SUBSTRING);
416 close(fd);
417
418 return_to_test_dir();
419 }
420
test_safe_open_unsafe_inserted_dir_symlink(void)421 static void test_safe_open_unsafe_inserted_dir_symlink(void)
422 {
423 setup_tempfiles();
424
425 TEST_SYMLINK_COUNTDOWN = 1;
426 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
427 TEST_SYMLINK_TARGET = "/etc";
428 // Not calling this function will call it right in the middle of the
429 // safe_open() instead.
430 //switch_symlink_hook();
431
432 assert_true(safe_open(TEST_LINK "/passwd", O_RDONLY) < 0);
433 assert_int_equal(errno, ENOENT);
434
435 return_to_test_dir();
436 }
437
test_safe_open_safe_inserted_dir_symlink(void)438 static void test_safe_open_safe_inserted_dir_symlink(void)
439 {
440 setup_tempfiles();
441
442 TEST_SYMLINK_COUNTDOWN = 1;
443 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
444 TEST_SYMLINK_TARGET = TEST_SUBDIR;
445 // Not calling this function will call it right in the middle of the
446 // safe_open() instead.
447 //switch_symlink_hook();
448
449 assert_true(safe_open(TEST_LINK "/" TEST_FILE, O_RDONLY) < 0);
450 assert_int_equal(errno, ENOENT);
451
452 return_to_test_dir();
453 }
454
test_safe_open_unsafe_switched_dir_symlink(void)455 static void test_safe_open_unsafe_switched_dir_symlink(void)
456 {
457 setup_tempfiles();
458
459 assert_int_equal(mkdir(TEMP_DIR "/" TEST_LINK, 0755), 0);
460 if (getuid() == 0)
461 {
462 assert_int_equal(chown(TEMP_DIR "/" TEST_LINK, 100, 100), 0);
463 }
464
465 TEST_SYMLINK_COUNTDOWN = 1;
466 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
467 TEST_SYMLINK_TARGET = "/etc";
468 // Not calling this function will call it right in the middle of the
469 // safe_open() instead.
470 //switch_symlink_hook();
471
472 assert_true(safe_open(TEST_LINK "/passwd", O_RDONLY) < 0);
473 assert_int_equal(errno, ENOLINK);
474
475 return_to_test_dir();
476 }
477
test_safe_open_safe_switched_dir_symlink(void)478 static void test_safe_open_safe_switched_dir_symlink(void)
479 {
480 setup_tempfiles();
481
482 assert_int_equal(mkdir(TEMP_DIR "/" TEST_LINK, 0755), 0);
483 if (getuid() == 0)
484 {
485 assert_int_equal(chown(TEMP_DIR "/" TEST_LINK, 100, 100), 0);
486 }
487
488 TEST_SYMLINK_COUNTDOWN = 1;
489 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
490 TEST_SYMLINK_TARGET = TEST_SUBDIR;
491 // Not calling this function will call it right in the middle of the
492 // safe_open() instead.
493 //switch_symlink_hook();
494
495 int fd;
496 assert_true((fd = safe_open(TEST_LINK "/" TEST_FILE, O_RDONLY)) >= 0);
497 check_contents(fd, TEST_SUBSTRING);
498 close(fd);
499
500 return_to_test_dir();
501 }
502
test_safe_open_create_safe_inserted_symlink(void)503 static void test_safe_open_create_safe_inserted_symlink(void)
504 {
505 setup_tempfiles();
506
507 TEST_SYMLINK_COUNTDOWN = 1;
508 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
509 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_FILE;
510 // Not calling this function will call it right in the middle of the
511 // safe_open() instead.
512 //switch_symlink_hook();
513
514 int fd = safe_open_create_perms(
515 TEST_LINK, (O_RDONLY | O_CREAT), CF_PERMS_SHARED);
516 assert_true(fd >= 0);
517 check_contents(fd, TEST_STRING);
518 close(fd);
519
520 return_to_test_dir();
521 }
522
test_safe_open_create_alternating_symlink(void)523 static void test_safe_open_create_alternating_symlink(void)
524 {
525 setup_tempfiles();
526
527 TEST_SYMLINK_COUNTDOWN = 1;
528 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
529 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_FILE;
530 TEST_SYMLINK_ALTERNATE = true;
531 // Not calling this function will call it right in the middle of the
532 // safe_open() instead.
533 //switch_symlink_hook();
534
535 assert_true(safe_open_create_perms(TEST_LINK, O_RDONLY | O_CREAT,
536 CF_PERMS_SHARED) < 0);
537 assert_int_equal(errno, EACCES);
538
539 return_to_test_dir();
540 }
541
test_safe_open_create_unsafe_switched_symlink(void)542 static void test_safe_open_create_unsafe_switched_symlink(void)
543 {
544 setup_tempfiles();
545
546 TEST_SYMLINK_COUNTDOWN = 1;
547 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
548 TEST_SYMLINK_TARGET = "/etc/passwd";
549 // Not calling this function will call it right in the middle of the
550 // safe_open() instead.
551 //switch_symlink_hook();
552
553 assert_true(safe_open_create_perms(TEST_FILE, O_RDONLY | O_CREAT,
554 CF_PERMS_SHARED) < 0);
555 assert_int_equal(errno, ENOLINK);
556
557 return_to_test_dir();
558 }
559
test_safe_open_create_switched_dangling_symlink(void)560 static void test_safe_open_create_switched_dangling_symlink(void)
561 {
562 setup_tempfiles();
563
564 TEST_SYMLINK_COUNTDOWN = 1;
565 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
566 TEST_SYMLINK_TARGET = "/etc/file-that-for-sure-does-not-exist";
567 // Not calling this function will call it right in the middle of the
568 // safe_open() instead.
569 //switch_symlink_hook();
570
571 assert_true(safe_open_create_perms(TEST_FILE, O_RDONLY | O_CREAT,
572 CF_PERMS_SHARED) < 0);
573 assert_int_equal(errno, EACCES);
574
575 return_to_test_dir();
576 }
577
test_safe_open_create_switched_dangling_symlink_exclusively(void)578 static void test_safe_open_create_switched_dangling_symlink_exclusively(void)
579 {
580 setup_tempfiles();
581
582 TEST_SYMLINK_COUNTDOWN = 1;
583 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
584 TEST_SYMLINK_TARGET = "/etc/file-that-for-sure-does-not-exist";
585 // Not calling this function will call it right in the middle of the
586 // safe_open() instead.
587 //switch_symlink_hook();
588
589 assert_true(safe_open_create_perms(TEST_FILE, O_WRONLY | O_CREAT | O_EXCL,
590 CF_PERMS_SHARED) < 0);
591 assert_int_equal(errno, EEXIST);
592
593 return_to_test_dir();
594 }
595
test_safe_open_create_dangling_symlink_exclusively(void)596 static void test_safe_open_create_dangling_symlink_exclusively(void)
597 {
598 setup_tempfiles();
599
600 TEST_SYMLINK_COUNTDOWN = 1;
601 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
602 TEST_SYMLINK_TARGET = "/etc/file-that-for-sure-does-not-exist";
603 switch_symlink_hook();
604
605 assert_true(safe_open_create_perms(TEST_FILE, O_WRONLY | O_CREAT | O_EXCL,
606 CF_PERMS_SHARED) < 0);
607 assert_int_equal(errno, EEXIST);
608
609 return_to_test_dir();
610 }
611
test_safe_open_switched_dangling_symlink(void)612 static void test_safe_open_switched_dangling_symlink(void)
613 {
614 setup_tempfiles();
615
616 TEST_SYMLINK_COUNTDOWN = 1;
617 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
618 TEST_SYMLINK_TARGET = "/etc/file-that-for-sure-does-not-exist";
619 // Not calling this function will call it right in the middle of the
620 // safe_open() instead.
621 //switch_symlink_hook();
622
623 assert_true(safe_open(TEST_FILE, O_RDONLY) < 0);
624 assert_int_equal(errno, ENOENT);
625
626 return_to_test_dir();
627 }
628
test_safe_open_root(void)629 static void test_safe_open_root(void)
630 {
631 int fd;
632 struct stat statbuf;
633 assert_true((fd = safe_open("/", O_RDONLY)) >= 0);
634 assert_int_equal(fchdir(fd), 0);
635 assert_int_equal(stat("etc", &statbuf), 0);
636 close(fd);
637
638 return_to_test_dir();
639 }
640
test_safe_open_ending_slashes(void)641 static void test_safe_open_ending_slashes(void)
642 {
643 setup_tempfiles();
644
645 int fd;
646 // Whether a regular file with ending slash fails to open is platform dependent,
647 // so should be the same as open().
648 fd = open(TEMP_DIR "/" TEST_FILE "///", O_RDONLY);
649 bool ending_file_slash_ok;
650 if (fd >= 0)
651 {
652 close(fd);
653 ending_file_slash_ok = true;
654 }
655 else
656 {
657 ending_file_slash_ok = false;
658 }
659 fd = safe_open(TEMP_DIR "/" TEST_FILE "///", O_RDONLY);
660 assert_true(ending_file_slash_ok ? (fd >= 0) : (fd < 0));
661 if (fd >= 0)
662 {
663 close(fd);
664 }
665 else
666 {
667 assert_int_equal(errno, ENOTDIR);
668 }
669
670 assert_true((fd = safe_open(TEMP_DIR "/", O_RDONLY)) >= 0);
671 close(fd);
672
673 return_to_test_dir();
674 }
675
test_safe_open_null(void)676 static void test_safe_open_null(void)
677 {
678 setup_tempfiles();
679
680 int fd;
681 assert_false((fd = safe_open(NULL, O_RDONLY)) >= 0);
682 assert_int_equal(errno, EINVAL);
683
684 return_to_test_dir();
685 }
686
test_safe_open_empty(void)687 static void test_safe_open_empty(void)
688 {
689 setup_tempfiles();
690
691 int fd;
692 assert_false((fd = safe_open("", O_RDONLY)) >= 0);
693 assert_int_equal(errno, ENOENT);
694
695 return_to_test_dir();
696 }
697
698 /* *********** HELPERS ********************************************* */
699
GetFileSize(const char * filename)700 static size_t GetFileSize(const char *filename)
701 {
702 struct stat statbuf;
703 int st_ret = lstat(filename, &statbuf);
704 assert_int_not_equal(st_ret, -1);
705 return (size_t) statbuf.st_size;
706 }
assert_file_not_exists(const char * filename)707 static void assert_file_not_exists(const char *filename)
708 {
709 int acc_ret = access(filename, F_OK);
710 assert_int_equal(acc_ret, -1);
711 assert_int_equal(errno, ENOENT);
712 }
create_test_file(bool empty)713 static void create_test_file(bool empty)
714 {
715 unlink(TEST_FILE);
716
717 int fd = open(TEST_FILE, O_WRONLY|O_CREAT, 0644);
718 assert_int_not_equal(fd, -1);
719
720 if (!empty)
721 {
722 ssize_t w_ret = write(fd, TEST_STRING, strlen(TEST_STRING));
723 assert_int_equal(w_ret, strlen(TEST_STRING));
724 }
725
726 int cl_ret = close(fd);
727 assert_int_not_equal(cl_ret, -1);
728
729 assert_int_equal(GetFileSize(TEST_FILE),
730 empty ? 0 : strlen(TEST_STRING));
731 }
732
733 /* ****************************************************************** */
734
735 /* Make sure that opening a file with O_TRUNC always truncates it, even if
736 * opening is tried several times (there is a loop in the code that resets the
737 * "trunc" flag on retry, and this test simulates retrying by changing the
738 * file in the middle of the operation). */
test_safe_open_TRUNC_safe_switched_symlink(void)739 static void test_safe_open_TRUNC_safe_switched_symlink(void)
740 {
741 setup_tempfiles();
742
743 TEST_SYMLINK_COUNTDOWN = 3;
744 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
745 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE;
746 // Not calling this function will call it right in the middle of the
747 // safe_open() instead.
748 //switch_symlink_hook();
749
750 int fd = safe_open(TEMP_DIR "/" TEST_FILE, O_WRONLY | O_TRUNC);
751 assert_int_not_equal(fd, -1);
752
753 int cl_ret = close(fd);
754 assert_int_not_equal(cl_ret, -1);
755
756 size_t link_target_size =
757 GetFileSize(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE);
758
759 /* TRUNCATION SHOULD HAVE HAPPENED. */
760 assert_int_equal(link_target_size, 0);
761
762 return_to_test_dir();
763 }
test_safe_open_TRUNC_unsafe_switched_symlink(void)764 static void test_safe_open_TRUNC_unsafe_switched_symlink(void)
765 {
766 if (getuid() != 0)
767 {
768 complain_missing_sudo(__FUNCTION__);
769 return;
770 }
771
772 setup_tempfiles();
773
774 TEST_SYMLINK_COUNTDOWN = 2;
775 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
776 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE;
777 // Not calling this function will call it right in the middle of the
778 // safe_open() instead.
779 //switch_symlink_hook();
780
781 /* Since this test runs as root, we simulate an attack where the user
782 * overwrites the root-owned file with a symlink. The symlink target must
783 * *not* be truncated. */
784
785 /* 1. target is owned by root */
786 assert_int_equal(chown(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, 0, 0), 0);
787
788 /* 2. TEST, but with a user-owned symlink being injected
789 * in place of the file. */
790 int fd = safe_open(TEMP_DIR "/" TEST_FILE, O_WRONLY | O_TRUNC);
791 assert_int_equal(fd, -1);
792
793 size_t link_target_size =
794 GetFileSize(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE);
795
796 /* TRUNCATION MUST NOT HAPPEN. */
797 assert_int_not_equal(link_target_size, 0);
798
799 return_to_test_dir();
800 }
801
test_safe_open_TRUNC_existing_nonempty(void)802 static void test_safe_open_TRUNC_existing_nonempty(void)
803 {
804 setup_tempfiles();
805 create_test_file(false);
806
807 /* TEST: O_TRUNC */
808 int fd = safe_open(TEST_FILE, O_WRONLY | O_TRUNC);
809 assert_int_not_equal(fd, -1);
810
811 int cl_ret = close(fd);
812 assert_int_not_equal(cl_ret, -1);
813
814 assert_int_equal(GetFileSize(TEST_FILE), 0);
815
816 return_to_test_dir();
817 }
test_safe_open_TRUNC_existing_empty(void)818 static void test_safe_open_TRUNC_existing_empty(void)
819 {
820 setup_tempfiles();
821 create_test_file(true);
822
823 /* TEST: O_TRUNC */
824 int fd = safe_open(TEST_FILE, O_WRONLY | O_TRUNC);
825 assert_int_not_equal(fd, -1);
826
827 int cl_ret = close(fd);
828 assert_int_not_equal(cl_ret, -1);
829
830 assert_int_equal(GetFileSize(TEST_FILE), 0);
831
832 return_to_test_dir();
833 }
test_safe_open_TRUNC_nonexisting(void)834 static void test_safe_open_TRUNC_nonexisting(void)
835 {
836 setup_tempfiles();
837 unlink(TEST_FILE);
838
839 /* TEST: O_TRUNC */
840 int fd = safe_open(TEST_FILE, O_WRONLY | O_TRUNC);
841 assert_int_equal(fd, -1);
842 assert_int_equal(errno, ENOENT);
843
844 assert_file_not_exists(TEST_FILE);
845
846 return_to_test_dir();
847 }
test_safe_open_CREAT_TRUNC_existing_nonempty(void)848 static void test_safe_open_CREAT_TRUNC_existing_nonempty(void)
849 {
850 setup_tempfiles();
851 create_test_file(false);
852
853 /* TEST: O_CREAT | O_TRUNC */
854 int fd = safe_open(TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC);
855 assert_int_not_equal(fd, -1);
856
857 int cl_ret = close(fd);
858 assert_int_not_equal(cl_ret, -1);
859
860 assert_int_equal(GetFileSize(TEST_FILE), 0);
861
862 return_to_test_dir();
863 }
test_safe_open_CREAT_TRUNC_existing_empty(void)864 static void test_safe_open_CREAT_TRUNC_existing_empty(void)
865 {
866 setup_tempfiles();
867 create_test_file(true);
868
869 /* TEST: O_CREAT | O_TRUNC */
870 int fd = safe_open(TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC);
871 assert_int_not_equal(fd, -1);
872
873 int cl_ret = close(fd);
874 assert_int_not_equal(cl_ret, -1);
875
876 assert_int_equal(GetFileSize(TEST_FILE), 0);
877
878 return_to_test_dir();
879 }
test_safe_open_CREAT_TRUNC_nonexisting(void)880 static void test_safe_open_CREAT_TRUNC_nonexisting(void)
881 {
882 setup_tempfiles();
883 unlink(TEST_FILE);
884
885 /* TEST: O_TRUNC */
886 int fd = safe_open(TEST_FILE, O_WRONLY | O_CREAT | O_TRUNC);
887 assert_int_not_equal(fd, -1);
888
889 int cl_ret = close(fd);
890 assert_int_not_equal(cl_ret, -1);
891
892 assert_int_equal(GetFileSize(TEST_FILE), 0);
893
894 return_to_test_dir();
895 }
896
test_safe_fopen(void)897 static void test_safe_fopen(void)
898 {
899 setup_tempfiles();
900
901 FILE *fptr;
902
903 char buf = 'a';
904
905 assert_true(fptr = safe_fopen(TEST_FILE, "r"));
906 assert_int_equal(fread(&buf, 1, 1, fptr), 1);
907 assert_false(ferror(fptr));
908 clearerr(fptr);
909 assert_int_not_equal(fwrite(&buf, 1, 1, fptr), 1);
910 assert_true(ferror(fptr));
911 clearerr(fptr);
912 fclose(fptr);
913
914 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "a",
915 CF_PERMS_DEFAULT));
916 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
917 assert_true(ferror(fptr));
918 clearerr(fptr);
919 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
920 assert_false(ferror(fptr));
921 clearerr(fptr);
922 fclose(fptr);
923
924 assert_true(fptr = safe_fopen(TEST_FILE, "r+"));
925 assert_int_equal(fread(&buf, 1, 1, fptr), 1);
926 assert_false(ferror(fptr));
927 clearerr(fptr);
928 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
929 assert_false(ferror(fptr));
930 clearerr(fptr);
931 fclose(fptr);
932
933 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "a+",
934 CF_PERMS_DEFAULT));
935 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
936 assert_false(ferror(fptr));
937 clearerr(fptr);
938 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
939 assert_false(ferror(fptr));
940 clearerr(fptr);
941 fclose(fptr);
942
943 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "w",
944 CF_PERMS_DEFAULT));
945 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
946 assert_true(ferror(fptr));
947 clearerr(fptr);
948 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
949 assert_false(ferror(fptr));
950 clearerr(fptr);
951 fclose(fptr);
952
953 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "w+",
954 CF_PERMS_DEFAULT));
955 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
956 assert_false(ferror(fptr));
957 clearerr(fptr);
958 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
959 assert_false(ferror(fptr));
960 clearerr(fptr);
961 fclose(fptr);
962
963 unlink(TEST_FILE);
964 assert_false(fptr = safe_fopen(TEST_FILE, "r"));
965
966 unlink(TEST_FILE);
967 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "a",
968 CF_PERMS_DEFAULT));
969 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
970 assert_true(ferror(fptr));
971 clearerr(fptr);
972 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
973 assert_false(ferror(fptr));
974 clearerr(fptr);
975 fclose(fptr);
976
977 unlink(TEST_FILE);
978 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "w",
979 CF_PERMS_DEFAULT));
980 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
981 assert_true(ferror(fptr));
982 clearerr(fptr);
983 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
984 assert_false(ferror(fptr));
985 clearerr(fptr);
986 fclose(fptr);
987
988 unlink(TEST_FILE);
989 assert_false(fptr = safe_fopen(TEST_FILE, "r+"));
990
991 unlink(TEST_FILE);
992 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "a+",
993 CF_PERMS_DEFAULT));
994 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
995 assert_false(ferror(fptr));
996 clearerr(fptr);
997 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
998 assert_false(ferror(fptr));
999 clearerr(fptr);
1000 fclose(fptr);
1001
1002 unlink(TEST_FILE);
1003 assert_true(fptr = safe_fopen_create_perms(TEST_FILE, "w+",
1004 CF_PERMS_DEFAULT));
1005 assert_int_not_equal(fread(&buf, 1, 1, fptr), 1);
1006 assert_false(ferror(fptr));
1007 clearerr(fptr);
1008 assert_int_equal(fwrite(&buf, 1, 1, fptr), 1);
1009 assert_false(ferror(fptr));
1010 clearerr(fptr);
1011 fclose(fptr);
1012
1013 return_to_test_dir();
1014 }
1015
test_safe_chown_plain_file(void)1016 static void test_safe_chown_plain_file(void)
1017 {
1018 if (getuid() != 0)
1019 {
1020 complain_missing_sudo(__FUNCTION__);
1021 return;
1022 }
1023
1024 setup_tempfiles();
1025
1026 struct stat statbuf;
1027
1028 assert_int_equal(chown(TEST_FILE, 100, 100), 0);
1029 assert_int_equal(stat(TEST_FILE, &statbuf), 0);
1030 assert_int_equal(statbuf.st_uid, 100);
1031 assert_int_equal(statbuf.st_gid, 100);
1032 assert_int_equal(safe_chown(TEST_FILE, 0, 0), 0);
1033 assert_int_equal(stat(TEST_FILE, &statbuf), 0);
1034 assert_int_equal(statbuf.st_uid, 0);
1035 assert_int_equal(statbuf.st_gid, 0);
1036
1037 return_to_test_dir();
1038 }
1039
test_safe_chown_relative_file(void)1040 static void test_safe_chown_relative_file(void)
1041 {
1042 if (getuid() != 0)
1043 {
1044 complain_missing_sudo(__FUNCTION__);
1045 return;
1046 }
1047
1048 setup_tempfiles();
1049
1050 struct stat statbuf;
1051
1052 assert_int_equal(chown(TEST_SUBSUBDIR "/" TEST_FILE, 100, 100), 0);
1053 assert_int_equal(stat(TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1054 assert_int_equal(statbuf.st_uid, 100);
1055 assert_int_equal(statbuf.st_gid, 100);
1056 assert_int_equal(safe_chown(TEST_SUBSUBDIR "/" TEST_FILE, 0, 0), 0);
1057 assert_int_equal(stat(TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1058 assert_int_equal(statbuf.st_uid, 0);
1059 assert_int_equal(statbuf.st_gid, 0);
1060
1061 return_to_test_dir();
1062 }
1063
test_safe_chown_absolute_file(void)1064 static void test_safe_chown_absolute_file(void)
1065 {
1066 if (getuid() != 0)
1067 {
1068 complain_missing_sudo(__FUNCTION__);
1069 return;
1070 }
1071
1072 setup_tempfiles();
1073
1074 struct stat statbuf;
1075
1076 assert_int_equal(chown(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, 100, 100), 0);
1077 assert_int_equal(stat(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1078 assert_int_equal(statbuf.st_uid, 100);
1079 assert_int_equal(statbuf.st_gid, 100);
1080 assert_int_equal(safe_chown(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, 0, 0), 0);
1081 assert_int_equal(stat(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1082 assert_int_equal(statbuf.st_uid, 0);
1083 assert_int_equal(statbuf.st_gid, 0);
1084
1085 return_to_test_dir();
1086 }
1087
test_safe_chown_file_extra_slashes(void)1088 static void test_safe_chown_file_extra_slashes(void)
1089 {
1090 if (getuid() != 0)
1091 {
1092 complain_missing_sudo(__FUNCTION__);
1093 return;
1094 }
1095
1096 setup_tempfiles();
1097
1098 struct stat statbuf;
1099
1100 assert_int_equal(chown("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, 100, 100), 0);
1101 assert_int_equal(stat("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, &statbuf), 0);
1102 assert_int_equal(statbuf.st_uid, 100);
1103 assert_int_equal(statbuf.st_gid, 100);
1104 assert_int_equal(safe_chown("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, 0, 0), 0);
1105 assert_int_equal(stat("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, &statbuf), 0);
1106 assert_int_equal(statbuf.st_uid, 0);
1107 assert_int_equal(statbuf.st_gid, 0);
1108
1109 return_to_test_dir();
1110 }
1111
test_safe_chown_plain_directory(void)1112 static void test_safe_chown_plain_directory(void)
1113 {
1114 if (getuid() != 0)
1115 {
1116 complain_missing_sudo(__FUNCTION__);
1117 return;
1118 }
1119
1120 setup_tempfiles();
1121
1122 struct stat statbuf;
1123
1124 assert_int_equal(chown(TEST_SUBDIR, 100, 100), 0);
1125 assert_int_equal(stat(TEST_SUBDIR, &statbuf), 0);
1126 assert_int_equal(statbuf.st_uid, 100);
1127 assert_int_equal(statbuf.st_gid, 100);
1128 assert_int_equal(safe_chown(TEST_SUBDIR, 0, 0), 0);
1129 assert_int_equal(stat(TEST_SUBDIR, &statbuf), 0);
1130 assert_int_equal(statbuf.st_uid, 0);
1131 assert_int_equal(statbuf.st_gid, 0);
1132
1133 return_to_test_dir();
1134 }
1135
test_safe_chown_unsafe_link(void)1136 static void test_safe_chown_unsafe_link(void)
1137 {
1138 if (getuid() != 0)
1139 {
1140 complain_missing_sudo(__FUNCTION__);
1141 return;
1142 }
1143
1144 setup_tempfiles();
1145
1146 struct stat statbuf;
1147
1148 TEST_SYMLINK_COUNTDOWN = 1;
1149 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
1150 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE;
1151 // Not calling this function will call it right in the middle of the
1152 // safe_open() instead.
1153 //switch_symlink_hook();
1154
1155 assert_int_equal(chown(TEST_SUBDIR "/" TEST_FILE, 0, 0), 0);
1156 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1157 assert_int_equal(statbuf.st_uid, 0);
1158 assert_int_equal(statbuf.st_gid, 0);
1159 assert_int_equal(safe_chown(TEST_FILE, 100, 100), -1);
1160 assert_int_equal(errno, ENOLINK);
1161 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1162 assert_int_equal(statbuf.st_uid, 0);
1163 assert_int_equal(statbuf.st_gid, 0);
1164
1165 return_to_test_dir();
1166 }
1167
test_safe_lchown_plain_file(void)1168 static void test_safe_lchown_plain_file(void)
1169 {
1170 if (getuid() != 0)
1171 {
1172 complain_missing_sudo(__FUNCTION__);
1173 return;
1174 }
1175
1176 setup_tempfiles();
1177
1178 struct stat statbuf;
1179
1180 assert_int_equal(lchown(TEST_FILE, 100, 100), 0);
1181 assert_int_equal(stat(TEST_FILE, &statbuf), 0);
1182 assert_int_equal(statbuf.st_uid, 100);
1183 assert_int_equal(statbuf.st_gid, 100);
1184 assert_int_equal(safe_lchown(TEST_FILE, 0, 0), 0);
1185 assert_int_equal(stat(TEST_FILE, &statbuf), 0);
1186 assert_int_equal(statbuf.st_uid, 0);
1187 assert_int_equal(statbuf.st_gid, 0);
1188
1189 return_to_test_dir();
1190 }
1191
test_safe_lchown_relative_file(void)1192 static void test_safe_lchown_relative_file(void)
1193 {
1194 if (getuid() != 0)
1195 {
1196 complain_missing_sudo(__FUNCTION__);
1197 return;
1198 }
1199
1200 setup_tempfiles();
1201
1202 struct stat statbuf;
1203
1204 assert_int_equal(lchown(TEST_SUBSUBDIR "/" TEST_FILE, 100, 100), 0);
1205 assert_int_equal(stat(TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1206 assert_int_equal(statbuf.st_uid, 100);
1207 assert_int_equal(statbuf.st_gid, 100);
1208 assert_int_equal(safe_lchown(TEST_SUBSUBDIR "/" TEST_FILE, 0, 0), 0);
1209 assert_int_equal(stat(TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1210 assert_int_equal(statbuf.st_uid, 0);
1211 assert_int_equal(statbuf.st_gid, 0);
1212
1213 return_to_test_dir();
1214 }
1215
test_safe_lchown_absolute_file(void)1216 static void test_safe_lchown_absolute_file(void)
1217 {
1218 if (getuid() != 0)
1219 {
1220 complain_missing_sudo(__FUNCTION__);
1221 return;
1222 }
1223
1224 setup_tempfiles();
1225
1226 struct stat statbuf;
1227
1228 assert_int_equal(lchown(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, 100, 100), 0);
1229 assert_int_equal(stat(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1230 assert_int_equal(statbuf.st_uid, 100);
1231 assert_int_equal(statbuf.st_gid, 100);
1232 assert_int_equal(safe_lchown(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, 0, 0), 0);
1233 assert_int_equal(stat(TEMP_DIR "/" TEST_SUBSUBDIR "/" TEST_FILE, &statbuf), 0);
1234 assert_int_equal(statbuf.st_uid, 0);
1235 assert_int_equal(statbuf.st_gid, 0);
1236
1237 return_to_test_dir();
1238 }
1239
test_safe_lchown_file_extra_slashes(void)1240 static void test_safe_lchown_file_extra_slashes(void)
1241 {
1242 if (getuid() != 0)
1243 {
1244 complain_missing_sudo(__FUNCTION__);
1245 return;
1246 }
1247
1248 setup_tempfiles();
1249
1250 struct stat statbuf;
1251
1252 assert_int_equal(lchown("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, 100, 100), 0);
1253 assert_int_equal(stat("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, &statbuf), 0);
1254 assert_int_equal(statbuf.st_uid, 100);
1255 assert_int_equal(statbuf.st_gid, 100);
1256 assert_int_equal(safe_lchown("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, 0, 0), 0);
1257 assert_int_equal(stat("/" TEMP_DIR "////" TEST_SUBSUBDIR "//" TEST_FILE, &statbuf), 0);
1258 assert_int_equal(statbuf.st_uid, 0);
1259 assert_int_equal(statbuf.st_gid, 0);
1260
1261 return_to_test_dir();
1262 }
1263
test_safe_lchown_plain_directory(void)1264 static void test_safe_lchown_plain_directory(void)
1265 {
1266 if (getuid() != 0)
1267 {
1268 complain_missing_sudo(__FUNCTION__);
1269 return;
1270 }
1271
1272 setup_tempfiles();
1273
1274 struct stat statbuf;
1275
1276 assert_int_equal(lchown(TEST_SUBDIR, 100, 100), 0);
1277 assert_int_equal(stat(TEST_SUBDIR, &statbuf), 0);
1278 assert_int_equal(statbuf.st_uid, 100);
1279 assert_int_equal(statbuf.st_gid, 100);
1280 assert_int_equal(safe_lchown(TEST_SUBDIR, 0, 0), 0);
1281 assert_int_equal(stat(TEST_SUBDIR, &statbuf), 0);
1282 assert_int_equal(statbuf.st_uid, 0);
1283 assert_int_equal(statbuf.st_gid, 0);
1284
1285 return_to_test_dir();
1286 }
1287
test_safe_lchown_unsafe_link(void)1288 static void test_safe_lchown_unsafe_link(void)
1289 {
1290 if (getuid() != 0)
1291 {
1292 complain_missing_sudo(__FUNCTION__);
1293 return;
1294 }
1295
1296 setup_tempfiles();
1297
1298 struct stat statbuf;
1299
1300 TEST_SYMLINK_COUNTDOWN = 1;
1301 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
1302 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE;
1303 // Not calling this function will call it right in the middle of the
1304 // safe_open() instead.
1305 //switch_symlink_hook();
1306
1307 assert_int_equal(lchown(TEST_SUBDIR "/" TEST_FILE, 0, 0), 0);
1308 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1309 assert_int_equal(statbuf.st_uid, 0);
1310 assert_int_equal(statbuf.st_gid, 0);
1311 // Unsafe links should succeed, because we are operating on the *link*, not the target.
1312 assert_int_equal(safe_lchown(TEST_FILE, 100, 100), 0);
1313 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1314 assert_int_equal(statbuf.st_uid, 0);
1315 assert_int_equal(statbuf.st_gid, 0);
1316
1317 return_to_test_dir();
1318 }
1319
test_safe_lchown_unsafe_link_to_directory(void)1320 static void test_safe_lchown_unsafe_link_to_directory(void)
1321 {
1322 if (getuid() != 0)
1323 {
1324 complain_missing_sudo(__FUNCTION__);
1325 return;
1326 }
1327
1328 setup_tempfiles();
1329
1330 struct stat statbuf;
1331
1332 TEST_SYMLINK_COUNTDOWN = 1;
1333 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_LINK;
1334 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR;
1335 switch_symlink_hook();
1336
1337 assert_int_equal(lchown(TEST_SUBDIR "/" TEST_FILE, 0, 0), 0);
1338 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1339 assert_int_equal(statbuf.st_uid, 0);
1340 assert_int_equal(statbuf.st_gid, 0);
1341 assert_int_equal(lchown(TEST_SUBDIR, 0, 0), 0);
1342 assert_int_equal(stat(TEST_SUBDIR, &statbuf), 0);
1343 assert_int_equal(statbuf.st_uid, 0);
1344 assert_int_equal(statbuf.st_gid, 0);
1345 assert_int_equal(safe_lchown(TEST_LINK "/" TEST_FILE, 100, 100), -1);
1346 assert_int_equal(errno, ENOLINK);
1347
1348 assert_int_equal(lchown(TEST_SUBDIR "/" TEST_FILE, 100, 100), 0);
1349 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1350 assert_int_equal(statbuf.st_uid, 100);
1351 assert_int_equal(statbuf.st_gid, 100);
1352 assert_int_equal(lchown(TEST_SUBDIR, 100, 100), 0);
1353 assert_int_equal(stat(TEST_SUBDIR, &statbuf), 0);
1354 assert_int_equal(statbuf.st_uid, 100);
1355 assert_int_equal(statbuf.st_gid, 100);
1356 assert_int_equal(safe_lchown(TEST_LINK "/" TEST_FILE, 100, 100), 0);
1357 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1358 assert_int_equal(statbuf.st_uid, 100);
1359 assert_int_equal(statbuf.st_gid, 100);
1360
1361 return_to_test_dir();
1362 }
1363
test_safe_chmod_plain_file(void)1364 static void test_safe_chmod_plain_file(void)
1365 {
1366 setup_tempfiles();
1367
1368 struct stat statbuf;
1369
1370 assert_int_equal(chmod(TEST_FILE, 0777), 0);
1371 assert_int_equal(stat(TEST_FILE, &statbuf), 0);
1372 assert_int_equal(statbuf.st_mode & 0777, 0777);
1373 assert_int_equal(safe_chmod(TEST_FILE, 0644), 0);
1374 assert_int_equal(stat(TEST_FILE, &statbuf), 0);
1375 assert_int_equal(statbuf.st_mode & 0777, 0644);
1376
1377 return_to_test_dir();
1378 }
1379
test_safe_chmod_relative_file(void)1380 static void test_safe_chmod_relative_file(void)
1381 {
1382 setup_tempfiles();
1383
1384 struct stat statbuf;
1385
1386 assert_int_equal(chmod(TEST_SUBDIR "/" TEST_FILE, 0777), 0);
1387 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1388 assert_int_equal(statbuf.st_mode & 0777, 0777);
1389 assert_int_equal(safe_chmod(TEST_SUBDIR "/" TEST_FILE, 0644), 0);
1390 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1391 assert_int_equal(statbuf.st_mode & 0777, 0644);
1392
1393 return_to_test_dir();
1394 }
1395
test_safe_chmod_absolute_file(void)1396 static void test_safe_chmod_absolute_file(void)
1397 {
1398 setup_tempfiles();
1399
1400 struct stat statbuf;
1401
1402 assert_int_equal(chmod(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, 0777), 0);
1403 assert_int_equal(stat(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1404 assert_int_equal(statbuf.st_mode & 0777, 0777);
1405 assert_int_equal(safe_chmod(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, 0644), 0);
1406 assert_int_equal(stat(TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1407 assert_int_equal(statbuf.st_mode & 0777, 0644);
1408
1409 return_to_test_dir();
1410 }
1411
test_safe_chmod_extra_slashes(void)1412 static void test_safe_chmod_extra_slashes(void)
1413 {
1414 setup_tempfiles();
1415
1416 struct stat statbuf;
1417
1418 assert_int_equal(chmod("/" TEMP_DIR "///" TEST_SUBDIR "//" TEST_FILE, 0777), 0);
1419 assert_int_equal(stat("/" TEMP_DIR "///" TEST_SUBDIR "//" TEST_FILE, &statbuf), 0);
1420 assert_int_equal(statbuf.st_mode & 0777, 0777);
1421 assert_int_equal(safe_chmod("/" TEMP_DIR "///" TEST_SUBDIR "//" TEST_FILE, 0644), 0);
1422 assert_int_equal(stat("/" TEMP_DIR "///" TEST_SUBDIR "//" TEST_FILE, &statbuf), 0);
1423 assert_int_equal(statbuf.st_mode & 0777, 0644);
1424
1425 return_to_test_dir();
1426 }
1427
test_safe_chmod_unsafe_link(void)1428 static void test_safe_chmod_unsafe_link(void)
1429 {
1430 if (getuid() != 0)
1431 {
1432 complain_missing_sudo(__FUNCTION__);
1433 return;
1434 }
1435
1436 setup_tempfiles();
1437
1438 struct stat statbuf;
1439
1440 TEST_SYMLINK_COUNTDOWN = 1;
1441 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
1442 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_SUBDIR "/" TEST_FILE;
1443 // Not calling this function will call it right in the middle of the
1444 // safe_open() instead.
1445 //switch_symlink_hook();
1446
1447 assert_int_equal(chown(TEST_SUBDIR "/" TEST_FILE, 0, 0), 0);
1448
1449 assert_int_equal(chmod(TEST_SUBDIR "/" TEST_FILE, 0777), 0);
1450 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1451 assert_int_equal(statbuf.st_mode & 0777, 0777);
1452 assert_int_equal(safe_chmod(TEST_FILE, 0644), -1);
1453 assert_int_equal(errno, ENOLINK);
1454 assert_int_equal(stat(TEST_SUBDIR "/" TEST_FILE, &statbuf), 0);
1455 assert_int_equal(statbuf.st_mode & 0777, 0777);
1456
1457 return_to_test_dir();
1458 }
1459
test_safe_creat_exists(void)1460 static void test_safe_creat_exists(void)
1461 {
1462 setup_tempfiles();
1463
1464 int fd;
1465 struct stat buf;
1466 assert_true((fd = safe_creat(TEST_FILE, 0644)) >= 0);
1467 assert_int_equal(fstat(fd, &buf), 0);
1468 assert_int_equal(buf.st_size, 0);
1469 close(fd);
1470
1471 return_to_test_dir();
1472 }
1473
test_safe_creat_doesnt_exist(void)1474 static void test_safe_creat_doesnt_exist(void)
1475 {
1476 setup_tempfiles();
1477
1478 int fd;
1479 struct stat buf;
1480 unlink(TEST_FILE);
1481 assert_true((fd = safe_creat(TEST_FILE, 0644)) >= 0);
1482 assert_int_equal(fstat(fd, &buf), 0);
1483 assert_int_equal(buf.st_size, 0);
1484 close(fd);
1485
1486 return_to_test_dir();
1487 }
1488
test_symlink_loop(void)1489 static void test_symlink_loop(void)
1490 {
1491 if (getuid() != 0)
1492 {
1493 complain_missing_sudo(__FUNCTION__);
1494 return;
1495 }
1496
1497 setup_tempfiles();
1498
1499 TEST_SYMLINK_COUNTDOWN = 1;
1500 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
1501 TEST_SYMLINK_TARGET = TEMP_DIR "/" TEST_FILE;
1502 switch_symlink_hook();
1503
1504 assert_int_equal(safe_open(TEST_FILE, O_RDONLY), -1);
1505 assert_int_equal(errno, ELOOP);
1506 assert_int_equal(safe_chown(TEST_FILE, 100, 100), -1);
1507 assert_int_equal(errno, ELOOP);
1508 assert_int_equal(safe_chmod(TEST_FILE, 0644), -1);
1509 assert_int_equal(errno, ELOOP);
1510 assert_int_equal(safe_lchown(TEST_FILE, 100, 100), 0);
1511
1512 return_to_test_dir();
1513 }
1514
test_safe_chmod_chown_fifos(void)1515 static void test_safe_chmod_chown_fifos(void)
1516 {
1517 if (getuid() != 0)
1518 {
1519 complain_missing_sudo(__FUNCTION__);
1520 return;
1521 }
1522
1523 setup_tempfiles();
1524
1525 TEST_SYMLINK_COUNTDOWN = 1;
1526 TEST_SYMLINK_NAME = TEMP_DIR "/" TEST_FILE;
1527 TEST_SYMLINK_TARGET = TEST_SUBDIR "/" TEST_FILE;
1528 switch_symlink_hook();
1529
1530 unlink(TEST_SUBDIR "/" TEST_FILE);
1531 assert_int_equal(mkfifo(TEST_SUBDIR "/" TEST_FILE, 0644), 0);
1532
1533 // Link owner != target owner
1534 assert_int_equal(safe_chown(TEST_FILE, 100, 100), -1);
1535 assert_int_equal(errno, ENOLINK);
1536 assert_int_equal(safe_chmod(TEST_FILE, 0755), -1);
1537 assert_int_equal(errno, ENOLINK);
1538 assert_int_equal(safe_chown(TEST_SUBDIR "/" TEST_FILE, 100, 100), 0);
1539
1540 // Now the owner is correct
1541 assert_int_equal(safe_chmod(TEST_FILE, 0755), 0);
1542 assert_int_equal(safe_chown(TEST_FILE, 0, 0), 0);
1543 assert_int_equal(safe_chmod(TEST_SUBDIR "/" TEST_FILE, 0644), 0);
1544
1545 return_to_test_dir();
1546 }
1547
test_file_can_open(void)1548 static void test_file_can_open(void)
1549 {
1550 setup_tempfiles();
1551
1552 assert_true(FileCanOpen(TEST_FILE, "r"));
1553 assert_false(FileCanOpen("no_such_file", "r"));
1554
1555 return_to_test_dir();
1556 }
1557
test_file_copy(void)1558 static void test_file_copy(void)
1559 {
1560 setup_tempfiles();
1561
1562 assert_true(File_Copy(TEST_FILE, "new_file"));
1563
1564 int fd = safe_open("new_file", O_RDONLY);
1565 assert_true(fd >= 0);
1566 check_contents(fd, TEST_STRING);
1567 close(fd);
1568
1569 assert_int_equal(unlink("new_file"), 0);
1570
1571 return_to_test_dir();
1572 }
1573
test_file_copy_to_dir(void)1574 static void test_file_copy_to_dir(void)
1575 {
1576 setup_tempfiles();
1577
1578 assert_false(File_CopyToDir(TEST_FILE, "no/such/dir/"));
1579 assert_true(File_CopyToDir(TEST_FILE, TEST_SUBDIR "/"));
1580
1581 const char * new_path = TEST_SUBDIR "/" TEST_FILE;
1582 int fd = safe_open(new_path, O_RDONLY);
1583 assert_true(fd >= 0);
1584 check_contents(fd, TEST_STRING);
1585 close(fd);
1586
1587 assert_int_equal(unlink(new_path), 0);
1588
1589 return_to_test_dir();
1590 }
1591
test_file_read(void)1592 static void test_file_read(void)
1593 {
1594 setup_tempfiles();
1595
1596 {
1597 bool truncated = true;
1598 Writer *w = FileRead(TEST_FILE, 1024, &truncated);
1599 assert_false(truncated);
1600 char *data = StringWriterClose(w);
1601 assert_string_equal(TEST_STRING, data);
1602 free(data);
1603 }
1604
1605 {
1606 bool truncated = false;
1607 Writer *w = FileRead(TEST_FILE, 4, &truncated);
1608 assert_true(truncated);
1609 char *data = StringWriterClose(w);
1610 assert_string_equal("BLUE", data);
1611 free(data);
1612 }
1613
1614 return_to_test_dir();
1615 }
1616
test_read_file_stream_to_buffer(void)1617 static void test_read_file_stream_to_buffer(void)
1618 {
1619 setup_tempfiles();
1620
1621 {
1622 const size_t length = strlen(TEST_STRING);
1623 char buf[1024] = {0};
1624 FILE *const file = safe_fopen(TEST_FILE, "r");
1625 assert_true(file != NULL);
1626 const ssize_t bytes_read = ReadFileStreamToBuffer(file, length, buf);
1627 fclose(file);
1628 assert_int_equal(bytes_read, length);
1629 assert_string_equal(TEST_STRING, buf);
1630 }
1631
1632 return_to_test_dir();
1633 }
1634
test_full_read_write(void)1635 static void test_full_read_write(void)
1636 {
1637 setup_tempfiles();
1638
1639 // Write test string to new_file, don't include NUL-byte
1640 int fd = safe_open_create_perms("new_file", O_WRONLY | O_CREAT, 0777);
1641 const size_t length = strlen(TEST_STRING);
1642 assert_int_equal(FullWrite(fd, TEST_STRING, length), length);
1643 assert_int_equal(close(fd), 0);
1644
1645 {
1646 // Read the same length back from file:
1647 fd = safe_open("new_file", O_RDONLY);
1648 char buf[length];
1649 assert_int_equal(FullRead(fd, buf, length), length);
1650 assert_true(memcmp(buf, TEST_STRING, length) == 0);
1651 assert_int_equal(close(fd), 0);
1652 }
1653
1654 {
1655 // Try to read twice as much:
1656 const size_t twice = length * 2;
1657 fd = safe_open("new_file", O_RDONLY);
1658 char buf[length]; // ASAN should panic if we overflow
1659 assert_int_equal(FullRead(fd, buf, twice), length);
1660 assert_true(memcmp(buf, TEST_STRING, length) == 0);
1661 assert_int_equal(close(fd), 0);
1662 }
1663
1664 {
1665 // Read about half of the file:
1666 const size_t half = length / 2;
1667 fd = safe_open("new_file", O_RDONLY);
1668 char buf[half];
1669 assert_int_equal(FullRead(fd, buf, half), half);
1670 assert_true(memcmp(buf, TEST_STRING, half) == 0);
1671 assert_int_equal(close(fd), 0);
1672 }
1673
1674 assert_int_equal(unlink("new_file"), 0);
1675 return_to_test_dir();
1676 }
1677
test_is_dir_real(void)1678 static void test_is_dir_real(void)
1679 {
1680 setup_tempfiles();
1681
1682 assert_true(IsDirReal("/"));
1683 assert_true(IsDirReal(".."));
1684 assert_false(IsDirReal(TEST_FILE));
1685 assert_false(IsDirReal("no/such/dir/"));
1686
1687 // TODO: Test that IsDirReal() returns false for symlinks
1688
1689 return_to_test_dir();
1690 }
1691
test_file_locking(void)1692 static void test_file_locking(void)
1693 {
1694 /* see file_lock_test.c for some more test cases */
1695 setup_tempfiles();
1696
1697 /** TEST CASE 1 -- open, nowait excl. lock, unlock, close **/
1698 int fd = open(TEMP_DIR "/" TEST_FILE, O_CREAT | O_RDWR, 0644);
1699 FileLock lock = EMPTY_FILE_LOCK;
1700 lock.fd = fd;
1701
1702 /* lock without waiting */
1703 assert_int_equal(ExclusiveFileLock(&lock, false), 0);
1704
1705 /* FD should not be changed */
1706 assert_int_equal(lock.fd, fd);
1707
1708 /* unlock, but keep the FD open */
1709 assert_int_equal(ExclusiveFileUnlock(&lock, false), 0);
1710
1711 /* should be able to close */
1712 assert_int_equal(close(lock.fd), 0);
1713
1714
1715 /** TEST CASE 2 -- open, wait excl. lock, unlock+close **/
1716 fd = open(TEMP_DIR "/" TEST_FILE, O_CREAT | O_RDWR, 0644);
1717 lock.fd = fd;
1718
1719 /* lock trying to wait */
1720 assert_int_equal(ExclusiveFileLock(&lock, true), 0);
1721
1722 /* FD should not be changed */
1723 assert_int_equal(lock.fd, fd);
1724
1725 /* try to lock again without waiting (we already have the lock so it's a
1726 * no-op)*/
1727 assert_int_equal(ExclusiveFileLock(&lock, false), 0);
1728
1729 /* unlock and close the FD */
1730 assert_int_equal(ExclusiveFileUnlock(&lock, true), 0);
1731
1732 /* should be already closed */
1733 assert_int_equal(close(lock.fd), -1);
1734
1735 /* FD should be reset to -1 */
1736 assert_int_equal(lock.fd, -1);
1737
1738
1739 /** TEST CASE 3 -- open, wait shared lock, wait excl. lock, unlock, close **/
1740 fd = open(TEMP_DIR "/" TEST_FILE, O_CREAT | O_RDWR, 0644);
1741 lock.fd = fd;
1742
1743 /* SHARED lock trying to wait */
1744 assert_int_equal(SharedFileLock(&lock, true), 0);
1745
1746 /* FD should not be changed */
1747 assert_int_equal(lock.fd, fd);
1748
1749 /* we are holding a shared lock so WE should be able to get an exclusive
1750 * lock */
1751 assert_true(ExclusiveFileLockCheck(&lock));
1752
1753 /* upgrade the lock to an exclusive one */
1754 assert_int_equal(ExclusiveFileLock(&lock, true), 0);
1755
1756 /* unlock, but keep the FD open */
1757 assert_int_equal(ExclusiveFileUnlock(&lock, false), 0);
1758
1759 /* should be able to close the FD */
1760 assert_int_equal(close(lock.fd), 0);
1761
1762
1763 /** TEST CASE 4 -- open, unlock, wait excl. lock, unlock, excl. lock again,
1764 * unlock+close **/
1765 fd = open(TEMP_DIR "/" TEST_FILE, O_CREAT | O_RDWR, 0644);
1766 lock.fd = fd;
1767
1768 /* unlock, but keep the FD open (we are NOT holding the lock so this should
1769 * be no-op)*/
1770 assert_int_equal(ExclusiveFileUnlock(&lock, false), 0);
1771
1772 /* FD should not be changed */
1773 assert_int_equal(lock.fd, fd);
1774
1775 /* we should be able to get an exclusive lock */
1776 assert_true(ExclusiveFileLockCheck(&lock));
1777
1778 /* get an exclusive lock */
1779 assert_int_equal(ExclusiveFileLock(&lock, true), 0);
1780
1781 /* unlock, but keep the FD open */
1782 assert_int_equal(ExclusiveFileUnlock(&lock, false), 0);
1783
1784 /* get an exclusive lock again */
1785 assert_int_equal(ExclusiveFileLock(&lock, true), 0);
1786
1787 /* unlock and close the FD */
1788 assert_int_equal(ExclusiveFileUnlock(&lock, true), 0);
1789
1790
1791 return_to_test_dir();
1792 }
1793
test_file_locking_with_path(void)1794 static void test_file_locking_with_path(void)
1795 {
1796 /* see file_lock_test.c for some more test cases */
1797 setup_tempfiles();
1798
1799 FileLock lock = EMPTY_FILE_LOCK;
1800
1801 /** TEST CASE 1 -- nowait excl. lock, unlock, close **/
1802 /* lock without waiting */
1803 assert_int_equal(ExclusiveFileLockPath(&lock, TEMP_DIR "/" TEST_FILE, false), 0);
1804
1805 /* FD should be changed */
1806 assert_int_not_equal(lock.fd, -1);
1807
1808 /* unlock, but keep the FD open */
1809 assert_int_equal(ExclusiveFileUnlock(&lock, false), 0);
1810
1811 /* should be able to close */
1812 assert_int_equal(close(lock.fd), 0);
1813 lock.fd = -1;
1814
1815
1816 /** TEST CASE 2 -- open, wait excl. lock, unlock+close **/
1817 /* lock trying to wait */
1818 assert_int_equal(ExclusiveFileLockPath(&lock, TEMP_DIR "/" TEST_FILE, true), 0);
1819
1820 /* FD should be changed */
1821 assert_int_not_equal(lock.fd, -1);
1822
1823 /* try to lock again without waiting (we already have the lock so it's a
1824 * no-op)*/
1825 assert_int_equal(ExclusiveFileLock(&lock, false), 0);
1826
1827 /* unlock and close the FD */
1828 assert_int_equal(ExclusiveFileUnlock(&lock, true), 0);
1829
1830 /* should be already closed */
1831 assert_int_equal(close(lock.fd), -1);
1832
1833 /* FD should be reset to -1 */
1834 assert_int_equal(lock.fd, -1);
1835
1836
1837 /** TEST CASE 3 -- open, wait shared lock, wait excl. lock, unlock, close **/
1838 /* SHARED lock trying to wait */
1839 assert_int_equal(SharedFileLockPath(&lock, TEMP_DIR "/" TEST_FILE, true), 0);
1840
1841 /* FD should be changed */
1842 assert_int_not_equal(lock.fd, -1);
1843
1844 /* we are holding a shared lock so WE should be able to get an exclusive
1845 * lock */
1846 assert_true(ExclusiveFileLockCheck(&lock));
1847
1848 /* SharedFileLockPath opens the file as RDONLY. For an exclusive lock, we
1849 * need RDWR. */
1850 assert_int_equal(ExclusiveFileLock(&lock, true), -1);
1851
1852 /* upgrade the lock to an exclusive one */
1853 FileLock lock2 = EMPTY_FILE_LOCK;
1854 assert_int_equal(ExclusiveFileLockPath(&lock2, TEMP_DIR "/" TEST_FILE, true), 0);
1855
1856 /* unlock, but keep the FD open */
1857 assert_int_equal(ExclusiveFileUnlock(&lock, false), 0);
1858
1859 /* should be able to close both FDs */
1860 assert_int_equal(close(lock.fd), 0);
1861 lock.fd = -1;
1862 assert_int_equal(close(lock2.fd), 0);
1863 lock2.fd = -1;
1864
1865
1866 /* TEST CASE 4 -- try to use lock file in non-existing directory */
1867 assert_int_equal(ExclusiveFileLockPath(&lock, "non-existing-dir/" TEST_FILE, true), -2);
1868
1869
1870 return_to_test_dir();
1871 }
1872
try_gaining_root_privileges(ARG_UNUSED int argc,char ** argv)1873 static void try_gaining_root_privileges(ARG_UNUSED int argc, char **argv)
1874 {
1875 if (system("sudo -n /bin/true") == 0)
1876 {
1877 execlp("sudo", "sudo", "-n", argv[0], NULL);
1878 // Should never get here.
1879 }
1880 }
1881
main(int argc,char ** argv)1882 int main(int argc, char **argv)
1883 {
1884 if (getuid() != 0)
1885 {
1886 try_gaining_root_privileges(argc, argv);
1887 }
1888
1889 PRINT_TEST_BANNER();
1890
1891 const UnitTest tests[] =
1892 {
1893 unit_test(save_test_dir),
1894
1895 unit_test(test_safe_open_currentdir),
1896 unit_test(test_safe_open_subdir),
1897 unit_test(test_safe_open_subsubdir),
1898 unit_test(test_safe_open_updir),
1899 unit_test(test_safe_open_upupdir),
1900 unit_test(test_safe_open_generic_relative_dir),
1901 unit_test(test_safe_open_generic_absolute_dir),
1902 unit_test(test_safe_open_extra_slashes_relative),
1903 unit_test(test_safe_open_extra_slashes_absolute),
1904 unit_test(test_safe_open_unsafe_symlink),
1905 unit_test(test_safe_open_safe_symlink),
1906 unit_test(test_safe_open_unsafe_inserted_symlink),
1907 unit_test(test_safe_open_safe_inserted_symlink),
1908 unit_test(test_safe_open_unsafe_switched_symlink),
1909 unit_test(test_safe_open_safe_switched_symlink),
1910 unit_test(test_safe_open_unsafe_dir_symlink),
1911 unit_test(test_safe_open_safe_dir_symlink),
1912 unit_test(test_safe_open_unsafe_inserted_dir_symlink),
1913 unit_test(test_safe_open_safe_inserted_dir_symlink),
1914 unit_test(test_safe_open_unsafe_switched_dir_symlink),
1915 unit_test(test_safe_open_safe_switched_dir_symlink),
1916 unit_test(test_safe_open_create_safe_inserted_symlink),
1917 unit_test(test_safe_open_create_alternating_symlink),
1918 unit_test(test_safe_open_create_unsafe_switched_symlink),
1919 unit_test(test_safe_open_create_switched_dangling_symlink),
1920 unit_test(test_safe_open_create_switched_dangling_symlink_exclusively),
1921 unit_test(test_safe_open_create_dangling_symlink_exclusively),
1922 unit_test(test_safe_open_switched_dangling_symlink),
1923 unit_test(test_safe_open_root),
1924 unit_test(test_safe_open_ending_slashes),
1925 unit_test(test_safe_open_null),
1926 unit_test(test_safe_open_empty),
1927
1928 unit_test(test_safe_open_TRUNC_safe_switched_symlink),
1929 unit_test(test_safe_open_TRUNC_unsafe_switched_symlink),
1930 unit_test(test_safe_open_TRUNC_existing_nonempty),
1931 unit_test(test_safe_open_TRUNC_existing_empty),
1932 unit_test(test_safe_open_TRUNC_nonexisting),
1933 unit_test(test_safe_open_CREAT_TRUNC_existing_nonempty),
1934 unit_test(test_safe_open_CREAT_TRUNC_existing_empty),
1935 unit_test(test_safe_open_CREAT_TRUNC_nonexisting),
1936
1937 unit_test(test_safe_fopen),
1938
1939 unit_test(test_safe_chown_plain_file),
1940 unit_test(test_safe_chown_relative_file),
1941 unit_test(test_safe_chown_absolute_file),
1942 unit_test(test_safe_chown_file_extra_slashes),
1943 unit_test(test_safe_chown_plain_directory),
1944 unit_test(test_safe_chown_unsafe_link),
1945
1946 unit_test(test_safe_lchown_plain_file),
1947 unit_test(test_safe_lchown_relative_file),
1948 unit_test(test_safe_lchown_absolute_file),
1949 unit_test(test_safe_lchown_file_extra_slashes),
1950 unit_test(test_safe_lchown_plain_directory),
1951 unit_test(test_safe_lchown_unsafe_link),
1952 unit_test(test_safe_lchown_unsafe_link_to_directory),
1953
1954 unit_test(test_safe_chmod_plain_file),
1955 unit_test(test_safe_chmod_relative_file),
1956 unit_test(test_safe_chmod_absolute_file),
1957 unit_test(test_safe_chmod_extra_slashes),
1958 unit_test(test_safe_chmod_unsafe_link),
1959
1960 unit_test(test_safe_creat_exists),
1961 unit_test(test_safe_creat_doesnt_exist),
1962
1963 unit_test(test_symlink_loop),
1964
1965 unit_test(test_safe_chmod_chown_fifos),
1966
1967 unit_test(test_file_can_open),
1968 unit_test(test_file_copy),
1969 unit_test(test_file_copy_to_dir),
1970 unit_test(test_file_read),
1971 unit_test(test_read_file_stream_to_buffer),
1972 unit_test(test_full_read_write),
1973 unit_test(test_is_dir_real),
1974
1975 unit_test(test_file_locking),
1976 unit_test(test_file_locking_with_path),
1977
1978 unit_test(close_test_dir),
1979 unit_test(clear_tempfiles),
1980 };
1981
1982 int ret = run_tests(tests);
1983
1984 return ret;
1985 }
1986