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: stable/11/contrib/libarchive/libarchive/test/test_write_disk_secure.c 368707 2020-12-16 22:25:20Z mm $");
27
28#define UMASK 022
29
30/*
31 * Exercise security checks that should prevent certain
32 * writes.
33 */
34
35DEFINE_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