1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #include "test.h"
26 __FBSDID("$FreeBSD$");
27 
28 #define UMASK 022
29 
30 /*
31  * Exercise security checks that should prevent certain
32  * writes.
33  */
34 
35 DEFINE_TEST(test_write_disk_secure)
36 {
37 #if defined(_WIN32) && !defined(__CYGWIN__)
38 	skipping("archive_write_disk security checks not supported on Windows");
39 #else
40 	struct archive *a;
41 	struct archive_entry *ae;
42 	struct stat st;
43 #if defined(HAVE_LCHMOD) && defined(HAVE_SYMLINK) && \
44     defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
45 	int working_lchmod;
46 #endif
47 
48 	/* Start with a known umask. */
49 	assertUmask(UMASK);
50 
51 	/* Create an archive_write_disk object. */
52 	assert((a = archive_write_disk_new()) != NULL);
53 
54 	/* Write a regular dir to it. */
55 	assert((ae = archive_entry_new()) != NULL);
56 	archive_entry_copy_pathname(ae, "dir");
57 	archive_entry_set_mode(ae, S_IFDIR | 0777);
58 	assert(0 == archive_write_header(a, ae));
59 	archive_entry_free(ae);
60 	assert(0 == archive_write_finish_entry(a));
61 
62 	/* Write a symlink to the dir above. */
63 	assert((ae = archive_entry_new()) != NULL);
64 	archive_entry_copy_pathname(ae, "link_to_dir");
65 	archive_entry_set_mode(ae, S_IFLNK | 0777);
66 	archive_entry_set_symlink(ae, "dir");
67 	archive_write_disk_set_options(a, 0);
68 	assert(0 == archive_write_header(a, ae));
69 	assert(0 == archive_write_finish_entry(a));
70 
71 	/*
72 	 * Without security checks, we should be able to
73 	 * extract a file through the link.
74 	 */
75 	assert(archive_entry_clear(ae) != NULL);
76 	archive_entry_copy_pathname(ae, "link_to_dir/filea");
77 	archive_entry_set_mode(ae, S_IFREG | 0777);
78 	assert(0 == archive_write_header(a, ae));
79 	assert(0 == archive_write_finish_entry(a));
80 
81 	/* But with security checks enabled, this should fail. */
82 	assert(archive_entry_clear(ae) != NULL);
83 	archive_entry_copy_pathname(ae, "link_to_dir/fileb");
84 	archive_entry_set_mode(ae, S_IFREG | 0777);
85 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
86 	failure("Extracting a file through a symlink should fail here.");
87 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
88 	archive_entry_free(ae);
89 	assert(0 == archive_write_finish_entry(a));
90 
91 	/* Write an absolute symlink to /tmp. */
92 	assert((ae = archive_entry_new()) != NULL);
93 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_symlink");
94 	archive_entry_set_mode(ae, S_IFLNK | 0777);
95 	archive_entry_set_symlink(ae, "/tmp");
96 	archive_write_disk_set_options(a, 0);
97 	assert(0 == archive_write_header(a, ae));
98 	assert(0 == archive_write_finish_entry(a));
99 
100 	/* With security checks enabled, this should fail. */
101 	assert(archive_entry_clear(ae) != NULL);
102 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_symlink/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
103 	archive_entry_set_mode(ae, S_IFREG | 0777);
104 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
105 	failure("Extracting a file through an absolute symlink should fail here.");
106 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
107 	archive_entry_free(ae);
108 	assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
109 	assert(0 == unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink"));
110 	unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
111 
112 	/* Create another link. */
113 	assert((ae = archive_entry_new()) != NULL);
114 	archive_entry_copy_pathname(ae, "link_to_dir2");
115 	archive_entry_set_mode(ae, S_IFLNK | 0777);
116 	archive_entry_set_symlink(ae, "dir");
117 	archive_write_disk_set_options(a, 0);
118 	assert(0 == archive_write_header(a, ae));
119 	assert(0 == archive_write_finish_entry(a));
120 
121 	/*
122 	 * With symlink check and unlink option, it should remove
123 	 * the link and create the dir.
124 	 */
125 	assert(archive_entry_clear(ae) != NULL);
126 	archive_entry_copy_pathname(ae, "link_to_dir2/filec");
127 	archive_entry_set_mode(ae, S_IFREG | 0777);
128 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_UNLINK);
129 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
130 	archive_entry_free(ae);
131 	assert(0 == archive_write_finish_entry(a));
132 
133 	/* Create a nested symlink. */
134 	assert((ae = archive_entry_new()) != NULL);
135 	archive_entry_copy_pathname(ae, "dir/nested_link_to_dir");
136 	archive_entry_set_mode(ae, S_IFLNK | 0777);
137 	archive_entry_set_symlink(ae, "../dir");
138 	archive_write_disk_set_options(a, 0);
139 	assert(0 == archive_write_header(a, ae));
140 	assert(0 == archive_write_finish_entry(a));
141 
142 	/* But with security checks enabled, this should fail. */
143 	assert(archive_entry_clear(ae) != NULL);
144 	archive_entry_copy_pathname(ae, "dir/nested_link_to_dir/filed");
145 	archive_entry_set_mode(ae, S_IFREG | 0777);
146 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
147 	failure("Extracting a file through a symlink should fail here.");
148 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
149 	archive_entry_free(ae);
150 	assert(0 == archive_write_finish_entry(a));
151 
152 	/*
153 	 * Without security checks, extracting a dir over a link to a
154 	 * dir should follow the link.
155 	 */
156 	/* Create a symlink to a dir. */
157 	assert((ae = archive_entry_new()) != NULL);
158 	archive_entry_copy_pathname(ae, "link_to_dir3");
159 	archive_entry_set_mode(ae, S_IFLNK | 0777);
160 	archive_entry_set_symlink(ae, "dir");
161 	archive_write_disk_set_options(a, 0);
162 	assert(0 == archive_write_header(a, ae));
163 	assert(0 == archive_write_finish_entry(a));
164 	/* Extract a dir whose name matches the symlink. */
165 	assert(archive_entry_clear(ae) != NULL);
166 	archive_entry_copy_pathname(ae, "link_to_dir3");
167 	archive_entry_set_mode(ae, S_IFDIR | 0777);
168 	assert(0 == archive_write_header(a, ae));
169 	assert(0 == archive_write_finish_entry(a));
170 	/* Verify link was followed. */
171 	assertEqualInt(0, lstat("link_to_dir3", &st));
172 	assert(S_ISLNK(st.st_mode));
173 	archive_entry_free(ae);
174 
175 	/*
176 	 * As above, but a broken link, so the link should get replaced.
177 	 */
178 	/* Create a symlink to a dir. */
179 	assert((ae = archive_entry_new()) != NULL);
180 	archive_entry_copy_pathname(ae, "link_to_dir4");
181 	archive_entry_set_mode(ae, S_IFLNK | 0777);
182 	archive_entry_set_symlink(ae, "nonexistent_dir");
183 	archive_write_disk_set_options(a, 0);
184 	assert(0 == archive_write_header(a, ae));
185 	assert(0 == archive_write_finish_entry(a));
186 	/* Extract a dir whose name matches the symlink. */
187 	assert(archive_entry_clear(ae) != NULL);
188 	archive_entry_copy_pathname(ae, "link_to_dir4");
189 	archive_entry_set_mode(ae, S_IFDIR | 0777);
190 	assert(0 == archive_write_header(a, ae));
191 	assert(0 == archive_write_finish_entry(a));
192 	/* Verify link was replaced. */
193 	assertEqualInt(0, lstat("link_to_dir4", &st));
194 	assert(S_ISDIR(st.st_mode));
195 	archive_entry_free(ae);
196 
197 	/*
198 	 * As above, but a link to a non-dir, so the link should get replaced.
199 	 */
200 	/* Create a regular file and a symlink to it */
201 	assert((ae = archive_entry_new()) != NULL);
202 	archive_entry_copy_pathname(ae, "non_dir");
203 	archive_entry_set_mode(ae, S_IFREG | 0777);
204 	archive_write_disk_set_options(a, 0);
205 	assert(0 == archive_write_header(a, ae));
206 	assert(0 == archive_write_finish_entry(a));
207 	/* Create symlink to the file. */
208 	archive_entry_copy_pathname(ae, "link_to_dir5");
209 	archive_entry_set_mode(ae, S_IFLNK | 0777);
210 	archive_entry_set_symlink(ae, "non_dir");
211 	archive_write_disk_set_options(a, 0);
212 	assert(0 == archive_write_header(a, ae));
213 	assert(0 == archive_write_finish_entry(a));
214 	/* Extract a dir whose name matches the symlink. */
215 	assert(archive_entry_clear(ae) != NULL);
216 	archive_entry_copy_pathname(ae, "link_to_dir5");
217 	archive_entry_set_mode(ae, S_IFDIR | 0777);
218 	assert(0 == archive_write_header(a, ae));
219 	assert(0 == archive_write_finish_entry(a));
220 	/* Verify link was replaced. */
221 	assertEqualInt(0, lstat("link_to_dir5", &st));
222 	assert(S_ISDIR(st.st_mode));
223 	archive_entry_free(ae);
224 
225 	/*
226 	 * Without security checks, we should be able to
227 	 * extract an absolute path.
228 	 */
229 	assert((ae = archive_entry_new()) != NULL);
230 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
231 	archive_entry_set_mode(ae, S_IFREG | 0777);
232 	assert(0 == archive_write_header(a, ae));
233 	assert(0 == archive_write_finish_entry(a));
234 	assertFileExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
235 	assert(0 == unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp"));
236 
237 	/* But with security checks enabled, this should fail. */
238 	assert(archive_entry_clear(ae) != NULL);
239 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
240 	archive_entry_set_mode(ae, S_IFREG | 0777);
241 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS);
242 	failure("Extracting an absolute path should fail here.");
243 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
244 	archive_entry_free(ae);
245 	assert(0 == archive_write_finish_entry(a));
246 	assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
247 
248 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
249 
250 	/* Test the entries on disk. */
251 	assert(0 == lstat("dir", &st));
252 	failure("dir: st.st_mode=%o", st.st_mode);
253 	assert((st.st_mode & 0777) == 0755);
254 
255 	assert(0 == lstat("link_to_dir", &st));
256 	failure("link_to_dir: st.st_mode=%o", st.st_mode);
257 	assert(S_ISLNK(st.st_mode));
258 #if defined(HAVE_SYMLINK) && defined(HAVE_LCHMOD) && \
259     defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
260 	/* Verify if we are able to lchmod() */
261 	if (symlink("dir", "testlink_to_dir") == 0) {
262 		if (lchmod("testlink_to_dir",
263 		    S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
264 			switch (errno) {
265 				case ENOTSUP:
266 				case ENOSYS:
267 #if ENOTSUP != EOPNOTSUPP
268 				case EOPNOTSUPP:
269 #endif
270 					working_lchmod = 0;
271 					break;
272 				default:
273 					working_lchmod = 1;
274 			}
275 		} else
276 			working_lchmod = 1;
277 	} else
278 		working_lchmod = 0;
279 
280 	if (working_lchmod) {
281 		failure("link_to_dir: st.st_mode=%o", st.st_mode);
282 		assert((st.st_mode & 07777) == 0755);
283 	}
284 #endif
285 
286 	assert(0 == lstat("dir/filea", &st));
287 	failure("dir/filea: st.st_mode=%o", st.st_mode);
288 	assert((st.st_mode & 07777) == 0755);
289 
290 	failure("dir/fileb: This file should not have been created");
291 	assert(0 != lstat("dir/fileb", &st));
292 
293 	assert(0 == lstat("link_to_dir2", &st));
294 	failure("link_to_dir2 should have been re-created as a true dir");
295 	assert(S_ISDIR(st.st_mode));
296 	failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode);
297 	assert((st.st_mode & 0777) == 0755);
298 
299 	assert(0 == lstat("link_to_dir2/filec", &st));
300 	assert(S_ISREG(st.st_mode));
301 	failure("link_to_dir2/filec: st.st_mode=%o", st.st_mode);
302 	assert((st.st_mode & 07777) == 0755);
303 
304 	failure("dir/filed: This file should not have been created");
305 	assert(0 != lstat("dir/filed", &st));
306 #endif
307 }
308