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 
27 #define UMASK 022
28 
29 /*
30  * Exercise security checks that should prevent certain
31  * writes.
32  */
33 
34 DEFINE_TEST(test_write_disk_secure)
35 {
36 #if defined(_WIN32) && !defined(__CYGWIN__)
37 	skipping("archive_write_disk security checks not supported on Windows");
38 #else
39 	struct archive *a;
40 	struct archive_entry *ae;
41 	struct stat st;
42 #if defined(HAVE_LCHMOD) && defined(HAVE_SYMLINK) && \
43     defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
44 	int working_lchmod;
45 #endif
46 
47 	/* Start with a known umask. */
48 	assertUmask(UMASK);
49 
50 	/* Create an archive_write_disk object. */
51 	assert((a = archive_write_disk_new()) != NULL);
52 
53 	/* Write a regular dir to it. */
54 	assert((ae = archive_entry_new()) != NULL);
55 	archive_entry_copy_pathname(ae, "dir");
56 	archive_entry_set_mode(ae, S_IFDIR | 0777);
57 	assert(0 == archive_write_header(a, ae));
58 	archive_entry_free(ae);
59 	assert(0 == archive_write_finish_entry(a));
60 
61 	/* Write a symlink to the dir above. */
62 	assert((ae = archive_entry_new()) != NULL);
63 	archive_entry_copy_pathname(ae, "link_to_dir");
64 	archive_entry_set_mode(ae, S_IFLNK | 0777);
65 	archive_entry_set_symlink(ae, "dir");
66 	archive_write_disk_set_options(a, 0);
67 	assert(0 == archive_write_header(a, ae));
68 	assert(0 == archive_write_finish_entry(a));
69 
70 	/*
71 	 * Without security checks, we should be able to
72 	 * extract a file through the link.
73 	 */
74 	assert(archive_entry_clear(ae) != NULL);
75 	archive_entry_copy_pathname(ae, "link_to_dir/filea");
76 	archive_entry_set_mode(ae, S_IFREG | 0777);
77 	assert(0 == archive_write_header(a, ae));
78 	assert(0 == archive_write_finish_entry(a));
79 
80 	/* But with security checks enabled, this should fail. */
81 	assert(archive_entry_clear(ae) != NULL);
82 	archive_entry_copy_pathname(ae, "link_to_dir/fileb");
83 	archive_entry_set_mode(ae, S_IFREG | 0777);
84 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
85 	failure("Extracting a file through a symlink should fail here.");
86 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
87 	archive_entry_free(ae);
88 	assert(0 == archive_write_finish_entry(a));
89 
90 	/* Write an absolute symlink to /tmp. */
91 	assert((ae = archive_entry_new()) != NULL);
92 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_symlink");
93 	archive_entry_set_mode(ae, S_IFLNK | 0777);
94 	archive_entry_set_symlink(ae, "/tmp");
95 	archive_write_disk_set_options(a, 0);
96 	assert(0 == archive_write_header(a, ae));
97 	assert(0 == archive_write_finish_entry(a));
98 
99 	/* With security checks enabled, this should fail. */
100 	assert(archive_entry_clear(ae) != NULL);
101 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_symlink/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
102 	archive_entry_set_mode(ae, S_IFREG | 0777);
103 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
104 	failure("Extracting a file through an absolute symlink should fail here.");
105 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
106 	archive_entry_free(ae);
107 	assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
108 	assert(0 == unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink"));
109 	unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_symlink_path.tmp");
110 
111 	/* Create another link. */
112 	assert((ae = archive_entry_new()) != NULL);
113 	archive_entry_copy_pathname(ae, "link_to_dir2");
114 	archive_entry_set_mode(ae, S_IFLNK | 0777);
115 	archive_entry_set_symlink(ae, "dir");
116 	archive_write_disk_set_options(a, 0);
117 	assert(0 == archive_write_header(a, ae));
118 	assert(0 == archive_write_finish_entry(a));
119 
120 	/*
121 	 * With symlink check and unlink option, it should remove
122 	 * the link and create the dir.
123 	 */
124 	assert(archive_entry_clear(ae) != NULL);
125 	archive_entry_copy_pathname(ae, "link_to_dir2/filec");
126 	archive_entry_set_mode(ae, S_IFREG | 0777);
127 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_UNLINK);
128 	assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
129 	archive_entry_free(ae);
130 	assert(0 == archive_write_finish_entry(a));
131 
132 	/* Create a nested symlink. */
133 	assert((ae = archive_entry_new()) != NULL);
134 	archive_entry_copy_pathname(ae, "dir/nested_link_to_dir");
135 	archive_entry_set_mode(ae, S_IFLNK | 0777);
136 	archive_entry_set_symlink(ae, "../dir");
137 	archive_write_disk_set_options(a, 0);
138 	assert(0 == archive_write_header(a, ae));
139 	assert(0 == archive_write_finish_entry(a));
140 
141 	/* But with security checks enabled, this should fail. */
142 	assert(archive_entry_clear(ae) != NULL);
143 	archive_entry_copy_pathname(ae, "dir/nested_link_to_dir/filed");
144 	archive_entry_set_mode(ae, S_IFREG | 0777);
145 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
146 	failure("Extracting a file through a symlink should fail here.");
147 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
148 	archive_entry_free(ae);
149 	assert(0 == archive_write_finish_entry(a));
150 
151 	/*
152 	 * Without security checks, extracting a dir over a link to a
153 	 * dir should follow the link.
154 	 */
155 	/* Create a symlink to a dir. */
156 	assert((ae = archive_entry_new()) != NULL);
157 	archive_entry_copy_pathname(ae, "link_to_dir3");
158 	archive_entry_set_mode(ae, S_IFLNK | 0777);
159 	archive_entry_set_symlink(ae, "dir");
160 	archive_write_disk_set_options(a, 0);
161 	assert(0 == archive_write_header(a, ae));
162 	assert(0 == archive_write_finish_entry(a));
163 	/* Extract a dir whose name matches the symlink. */
164 	assert(archive_entry_clear(ae) != NULL);
165 	archive_entry_copy_pathname(ae, "link_to_dir3");
166 	archive_entry_set_mode(ae, S_IFDIR | 0777);
167 	assert(0 == archive_write_header(a, ae));
168 	assert(0 == archive_write_finish_entry(a));
169 	/* Verify link was followed. */
170 	assertEqualInt(0, lstat("link_to_dir3", &st));
171 	assert(S_ISLNK(st.st_mode));
172 	archive_entry_free(ae);
173 
174 	/*
175 	 * As above, but a broken link, so the link should get replaced.
176 	 */
177 	/* Create a symlink to a dir. */
178 	assert((ae = archive_entry_new()) != NULL);
179 	archive_entry_copy_pathname(ae, "link_to_dir4");
180 	archive_entry_set_mode(ae, S_IFLNK | 0777);
181 	archive_entry_set_symlink(ae, "nonexistent_dir");
182 	archive_write_disk_set_options(a, 0);
183 	assert(0 == archive_write_header(a, ae));
184 	assert(0 == archive_write_finish_entry(a));
185 	/* Extract a dir whose name matches the symlink. */
186 	assert(archive_entry_clear(ae) != NULL);
187 	archive_entry_copy_pathname(ae, "link_to_dir4");
188 	archive_entry_set_mode(ae, S_IFDIR | 0777);
189 	assert(0 == archive_write_header(a, ae));
190 	assert(0 == archive_write_finish_entry(a));
191 	/* Verify link was replaced. */
192 	assertEqualInt(0, lstat("link_to_dir4", &st));
193 	assert(S_ISDIR(st.st_mode));
194 	archive_entry_free(ae);
195 
196 	/*
197 	 * As above, but a link to a non-dir, so the link should get replaced.
198 	 */
199 	/* Create a regular file and a symlink to it */
200 	assert((ae = archive_entry_new()) != NULL);
201 	archive_entry_copy_pathname(ae, "non_dir");
202 	archive_entry_set_mode(ae, S_IFREG | 0777);
203 	archive_write_disk_set_options(a, 0);
204 	assert(0 == archive_write_header(a, ae));
205 	assert(0 == archive_write_finish_entry(a));
206 	/* Create symlink to the file. */
207 	archive_entry_copy_pathname(ae, "link_to_dir5");
208 	archive_entry_set_mode(ae, S_IFLNK | 0777);
209 	archive_entry_set_symlink(ae, "non_dir");
210 	archive_write_disk_set_options(a, 0);
211 	assert(0 == archive_write_header(a, ae));
212 	assert(0 == archive_write_finish_entry(a));
213 	/* Extract a dir whose name matches the symlink. */
214 	assert(archive_entry_clear(ae) != NULL);
215 	archive_entry_copy_pathname(ae, "link_to_dir5");
216 	archive_entry_set_mode(ae, S_IFDIR | 0777);
217 	assert(0 == archive_write_header(a, ae));
218 	assert(0 == archive_write_finish_entry(a));
219 	/* Verify link was replaced. */
220 	assertEqualInt(0, lstat("link_to_dir5", &st));
221 	assert(S_ISDIR(st.st_mode));
222 	archive_entry_free(ae);
223 
224 	/*
225 	 * Without security checks, we should be able to
226 	 * extract an absolute path.
227 	 */
228 	assert((ae = archive_entry_new()) != NULL);
229 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
230 	archive_entry_set_mode(ae, S_IFREG | 0777);
231 	assert(0 == archive_write_header(a, ae));
232 	assert(0 == archive_write_finish_entry(a));
233 	assertFileExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
234 	assert(0 == unlink("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp"));
235 
236 	/* But with security checks enabled, this should fail. */
237 	assert(archive_entry_clear(ae) != NULL);
238 	archive_entry_copy_pathname(ae, "/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
239 	archive_entry_set_mode(ae, S_IFREG | 0777);
240 	archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS);
241 	failure("Extracting an absolute path should fail here.");
242 	assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
243 	archive_entry_free(ae);
244 	assert(0 == archive_write_finish_entry(a));
245 	assertFileNotExists("/tmp/libarchive_test-test_write_disk_secure-absolute_path.tmp");
246 
247 	assertEqualInt(ARCHIVE_OK, archive_write_free(a));
248 
249 	/* Test the entries on disk. */
250 	assert(0 == lstat("dir", &st));
251 	failure("dir: st.st_mode=%o", st.st_mode);
252 	assert((st.st_mode & 0777) == 0755);
253 
254 	assert(0 == lstat("link_to_dir", &st));
255 	failure("link_to_dir: st.st_mode=%o", st.st_mode);
256 	assert(S_ISLNK(st.st_mode));
257 #if defined(HAVE_SYMLINK) && defined(HAVE_LCHMOD) && \
258     defined(S_IRUSR) && defined(S_IWUSR) && defined(S_IXUSR)
259 	/* Verify if we are able to lchmod() */
260 	if (symlink("dir", "testlink_to_dir") == 0) {
261 		if (lchmod("testlink_to_dir",
262 		    S_IRUSR | S_IWUSR | S_IXUSR) != 0) {
263 			switch (errno) {
264 				case ENOTSUP:
265 				case ENOSYS:
266 #if ENOTSUP != EOPNOTSUPP
267 				case EOPNOTSUPP:
268 #endif
269 					working_lchmod = 0;
270 					break;
271 				default:
272 					working_lchmod = 1;
273 			}
274 		} else
275 			working_lchmod = 1;
276 	} else
277 		working_lchmod = 0;
278 
279 	if (working_lchmod) {
280 		failure("link_to_dir: st.st_mode=%o", st.st_mode);
281 		assert((st.st_mode & 07777) == 0755);
282 	}
283 #endif
284 
285 	assert(0 == lstat("dir/filea", &st));
286 	failure("dir/filea: st.st_mode=%o", st.st_mode);
287 	assert((st.st_mode & 07777) == 0755);
288 
289 	failure("dir/fileb: This file should not have been created");
290 	assert(0 != lstat("dir/fileb", &st));
291 
292 	assert(0 == lstat("link_to_dir2", &st));
293 	failure("link_to_dir2 should have been re-created as a true dir");
294 	assert(S_ISDIR(st.st_mode));
295 	failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode);
296 	assert((st.st_mode & 0777) == 0755);
297 
298 	assert(0 == lstat("link_to_dir2/filec", &st));
299 	assert(S_ISREG(st.st_mode));
300 	failure("link_to_dir2/filec: st.st_mode=%o", st.st_mode);
301 	assert((st.st_mode & 07777) == 0755);
302 
303 	failure("dir/filed: This file should not have been created");
304 	assert(0 != lstat("dir/filed", &st));
305 #endif
306 }
307