]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/test/test_write_disk_secure.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / test / test_write_disk_secure.c
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 }