xref: /freebsd/contrib/libarchive/libarchive/test/test_write_disk_secure_noabsolutepaths.c (revision bd66c1b43e33540205dbc1187c2f2a15c58b57ba)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024 Mostyn Bramley-Moore <mostyn@antipode.se>
5  */
6 
7 #include "test.h"
8 
9 #include <stdlib.h>
10 
11 #if defined(_WIN32) && !defined(__CYGWIN__)
12 #define UNLINK _unlink
13 #else
14 #include <unistd.h>
15 #define UNLINK unlink
16 #endif
17 
18 /*
19  * Exercise security checks that should prevent writing absolute paths
20  * when extracting archives.
21  */
DEFINE_TEST(test_write_disk_secure_noabsolutepaths)22 DEFINE_TEST(test_write_disk_secure_noabsolutepaths)
23 {
24 	struct archive *a, *ad;
25 	struct archive_entry *ae;
26 
27 	char buff[10000];
28 
29 	size_t used;
30 
31 	// Create an archive_write object.
32 	assert((a = archive_write_new()) != NULL);
33 	assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_ustar(a));
34 	assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
35 	assertEqualIntA(a, ARCHIVE_OK, archive_write_open_memory(a, buff, sizeof(buff), &used));
36 
37 	// Create an absolute path for a test file inside testworkdir.
38 	const char *entry_suffix = "/badfile";
39 	size_t entry_suffix_length = strlen(entry_suffix);
40 	size_t testworkdir_length = strlen(testworkdir);
41 	size_t temp_absolute_file_name_length = testworkdir_length + entry_suffix_length;
42 	char *temp_absolute_file_name = calloc(1, temp_absolute_file_name_length + 1); // +1 for null character.
43 	assertEqualInt(snprintf(temp_absolute_file_name, temp_absolute_file_name_length + 1, "%s%s", testworkdir, entry_suffix),
44 		temp_absolute_file_name_length);
45 
46 	// Convert to a unix-style path, so we can compare it to the entry
47 	// path when reading back the archive.
48 	for (char *p = temp_absolute_file_name; *p != '\0'; p++)
49 		if (*p == '\\') *p = '/';
50 
51 	// Add a regular file entry with an absolute path.
52 	assert((ae = archive_entry_new()) != NULL);
53 	archive_entry_copy_pathname(ae, temp_absolute_file_name);
54 	archive_entry_set_mode(ae, S_IFREG | 0777);
55 	archive_entry_set_size(ae, 6);
56 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
57 	archive_entry_free(ae);
58 	assertEqualInt(6, archive_write_data(a, "hello", 6));
59 	assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
60 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
61 
62 	// Now try to extract the data.
63 	assert((a = archive_read_new()) != NULL);
64 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
65 	assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
66 	assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
67 
68 	assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
69 	assertEqualString(temp_absolute_file_name, archive_entry_pathname(ae));
70 	assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
71 	assertEqualInt(AE_IFREG | 0777, archive_entry_mode(ae));
72 	assertEqualInt(6, archive_entry_size(ae));
73 
74 	// This should succeed.
75 	assertEqualInt(ARCHIVE_OK, archive_read_extract(a, ae, 0));
76 	UNLINK(temp_absolute_file_name);
77 
78 	// This should fail, since the archive entry has an absolute path.
79 	assert(ARCHIVE_OK != archive_read_extract(a, ae, ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS));
80 
81 	// This should also fail.
82 	assert((ad = archive_write_new()) != NULL);
83 	assertEqualInt(ARCHIVE_OK, archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS));
84 	assert(ARCHIVE_OK != archive_read_extract2(a, ae, ad));
85 
86 	assertEqualInt(ARCHIVE_OK, archive_write_free(ad));
87 	assertEqualInt(ARCHIVE_OK, archive_read_free(a));
88 }
89