2
* Copyright (c) 2003-2007 Tim Kientzle
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
26
__FBSDID("$FreeBSD: head/lib/libarchive/test/test_write_disk_secure.c 201247 2009-12-30 05:59:21Z kientzle $");
31
* Exercise security checks that should prevent certain
35
DEFINE_TEST(test_write_disk_secure)
37
#if ARCHIVE_VERSION_NUMBER < 1009000
38
skipping("archive_write_disk interface");
39
#elif !defined(_WIN32) || defined(__CYGWIN__)
41
struct archive_entry *ae;
44
/* Start with a known umask. */
47
/* Create an archive_write_disk object. */
48
assert((a = archive_write_disk_new()) != NULL);
50
/* Write a regular dir to it. */
51
assert((ae = archive_entry_new()) != NULL);
52
archive_entry_copy_pathname(ae, "dir");
53
archive_entry_set_mode(ae, S_IFDIR | 0777);
54
assert(0 == archive_write_header(a, ae));
55
archive_entry_free(ae);
56
assert(0 == archive_write_finish_entry(a));
58
/* Write a symlink to the dir above. */
59
assert((ae = archive_entry_new()) != NULL);
60
archive_entry_copy_pathname(ae, "link_to_dir");
61
archive_entry_set_mode(ae, S_IFLNK | 0777);
62
archive_entry_set_symlink(ae, "dir");
63
archive_write_disk_set_options(a, 0);
64
assert(0 == archive_write_header(a, ae));
65
assert(0 == archive_write_finish_entry(a));
68
* Without security checks, we should be able to
69
* extract a file through the link.
71
assert(archive_entry_clear(ae) != NULL);
72
archive_entry_copy_pathname(ae, "link_to_dir/filea");
73
archive_entry_set_mode(ae, S_IFREG | 0777);
74
assert(0 == archive_write_header(a, ae));
75
assert(0 == archive_write_finish_entry(a));
77
/* But with security checks enabled, this should fail. */
78
assert(archive_entry_clear(ae) != NULL);
79
archive_entry_copy_pathname(ae, "link_to_dir/fileb");
80
archive_entry_set_mode(ae, S_IFREG | 0777);
81
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS);
82
failure("Extracting a file through a symlink should fail here.");
83
assertEqualInt(ARCHIVE_FAILED, archive_write_header(a, ae));
84
archive_entry_free(ae);
85
assert(0 == archive_write_finish_entry(a));
87
/* Create another link. */
88
assert((ae = archive_entry_new()) != NULL);
89
archive_entry_copy_pathname(ae, "link_to_dir2");
90
archive_entry_set_mode(ae, S_IFLNK | 0777);
91
archive_entry_set_symlink(ae, "dir");
92
archive_write_disk_set_options(a, 0);
93
assert(0 == archive_write_header(a, ae));
94
assert(0 == archive_write_finish_entry(a));
97
* With symlink check and unlink option, it should remove
98
* the link and create the dir.
100
assert(archive_entry_clear(ae) != NULL);
101
archive_entry_copy_pathname(ae, "link_to_dir2/filec");
102
archive_entry_set_mode(ae, S_IFREG | 0777);
103
archive_write_disk_set_options(a, ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_UNLINK);
104
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae));
105
archive_entry_free(ae);
106
assert(0 == archive_write_finish_entry(a));
109
* Without security checks, extracting a dir over a link to a
110
* dir should follow the link.
112
/* Create a symlink to a dir. */
113
assert((ae = archive_entry_new()) != NULL);
114
archive_entry_copy_pathname(ae, "link_to_dir3");
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
/* Extract a dir whose name matches the symlink. */
121
assert(archive_entry_clear(ae) != NULL);
122
archive_entry_copy_pathname(ae, "link_to_dir3");
123
archive_entry_set_mode(ae, S_IFDIR | 0777);
124
assert(0 == archive_write_header(a, ae));
125
assert(0 == archive_write_finish_entry(a));
126
/* Verify link was followed. */
127
assertEqualInt(0, lstat("link_to_dir3", &st));
128
assert(S_ISLNK(st.st_mode));
129
archive_entry_free(ae);
132
* As above, but a broken link, so the link should get replaced.
134
/* Create a symlink to a dir. */
135
assert((ae = archive_entry_new()) != NULL);
136
archive_entry_copy_pathname(ae, "link_to_dir4");
137
archive_entry_set_mode(ae, S_IFLNK | 0777);
138
archive_entry_set_symlink(ae, "nonexistent_dir");
139
archive_write_disk_set_options(a, 0);
140
assert(0 == archive_write_header(a, ae));
141
assert(0 == archive_write_finish_entry(a));
142
/* Extract a dir whose name matches the symlink. */
143
assert(archive_entry_clear(ae) != NULL);
144
archive_entry_copy_pathname(ae, "link_to_dir4");
145
archive_entry_set_mode(ae, S_IFDIR | 0777);
146
assert(0 == archive_write_header(a, ae));
147
assert(0 == archive_write_finish_entry(a));
148
/* Verify link was replaced. */
149
assertEqualInt(0, lstat("link_to_dir4", &st));
150
assert(S_ISDIR(st.st_mode));
151
archive_entry_free(ae);
154
* As above, but a link to a non-dir, so the link should get replaced.
156
/* Create a regular file and a symlink to it */
157
assert((ae = archive_entry_new()) != NULL);
158
archive_entry_copy_pathname(ae, "non_dir");
159
archive_entry_set_mode(ae, S_IFREG | 0777);
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
/* Create symlink to the file. */
164
archive_entry_copy_pathname(ae, "link_to_dir5");
165
archive_entry_set_mode(ae, S_IFLNK | 0777);
166
archive_entry_set_symlink(ae, "non_dir");
167
archive_write_disk_set_options(a, 0);
168
assert(0 == archive_write_header(a, ae));
169
assert(0 == archive_write_finish_entry(a));
170
/* Extract a dir whose name matches the symlink. */
171
assert(archive_entry_clear(ae) != NULL);
172
archive_entry_copy_pathname(ae, "link_to_dir5");
173
archive_entry_set_mode(ae, S_IFDIR | 0777);
174
assert(0 == archive_write_header(a, ae));
175
assert(0 == archive_write_finish_entry(a));
176
/* Verify link was replaced. */
177
assertEqualInt(0, lstat("link_to_dir5", &st));
178
assert(S_ISDIR(st.st_mode));
179
archive_entry_free(ae);
181
assert(0 == archive_write_finish(a));
183
/* Test the entries on disk. */
184
assert(0 == lstat("dir", &st));
185
failure("dir: st.st_mode=%o", st.st_mode);
186
assert((st.st_mode & 0777) == 0755);
188
assert(0 == lstat("link_to_dir", &st));
189
failure("link_to_dir: st.st_mode=%o", st.st_mode);
190
assert(S_ISLNK(st.st_mode));
192
/* Systems that lack lchmod() can't set symlink perms, so skip this. */
193
failure("link_to_dir: st.st_mode=%o", st.st_mode);
194
assert((st.st_mode & 07777) == 0755);
197
assert(0 == lstat("dir/filea", &st));
198
failure("dir/filea: st.st_mode=%o", st.st_mode);
199
assert((st.st_mode & 07777) == 0755);
201
failure("dir/fileb: This file should not have been created");
202
assert(0 != lstat("dir/fileb", &st));
204
assert(0 == lstat("link_to_dir2", &st));
205
failure("link_to_dir2 should have been re-created as a true dir");
206
assert(S_ISDIR(st.st_mode));
207
failure("link_to_dir2: Implicit dir creation should obey umask, but st.st_mode=%o", st.st_mode);
208
assert((st.st_mode & 0777) == 0755);
210
assert(0 == lstat("link_to_dir2/filec", &st));
211
assert(S_ISREG(st.st_mode));
212
failure("link_to_dir2/filec: st.st_mode=%o", st.st_mode);
213
assert((st.st_mode & 07777) == 0755);