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
DEFINE_TEST(test_write_disk_secure)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