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