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 } 308