test_write_disk_perms.c revision 328827
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_perms.c 328827 2018-02-03 02:17:04Z mm $"); 27 28#if !defined(_WIN32) || defined(__CYGWIN__) 29 30#define UMASK 022 31 32static long _default_gid = -1; 33static long _invalid_gid = -1; 34static long _alt_gid = -1; 35 36/* 37 * To fully test SGID restores, we need three distinct GIDs to work 38 * with: 39 * * the GID that files are created with by default (for the 40 * current user in the current directory) 41 * * An "alt gid" that this user can create files with 42 * * An "invalid gid" that this user is not permitted to create 43 * files with. 44 * The second fails if this user doesn't belong to at least two groups; 45 * the third fails if the current user is root. 46 */ 47static void 48searchgid(void) 49{ 50 static int _searched = 0; 51 uid_t uid = getuid(); 52 gid_t gid = 0; 53 unsigned int n; 54 struct stat st; 55 int fd; 56 57 /* If we've already looked this up, we're done. */ 58 if (_searched) 59 return; 60 _searched = 1; 61 62 /* Create a file on disk in the current default dir. */ 63 fd = open("test_gid", O_CREAT | O_BINARY, 0664); 64 failure("Couldn't create a file for gid testing."); 65 assert(fd > 0); 66 67 /* See what GID it ended up with. This is our "valid" GID. */ 68 assert(fstat(fd, &st) == 0); 69 _default_gid = st.st_gid; 70 71 /* Find a GID for which fchown() fails. This is our "invalid" GID. */ 72 _invalid_gid = -1; 73 /* This loop stops when we wrap the gid or examine 10,000 gids. */ 74 for (gid = 1, n = 1; gid == n && n < 10000 ; n++, gid++) { 75 if (fchown(fd, uid, gid) != 0) { 76 _invalid_gid = gid; 77 break; 78 } 79 } 80 81 /* 82 * Find a GID for which fchown() succeeds, but which isn't the 83 * default. This is the "alternate" gid. 84 */ 85 _alt_gid = -1; 86 for (gid = 0, n = 0; gid == n && n < 10000 ; n++, gid++) { 87 /* _alt_gid must be different than _default_gid */ 88 if (gid == (gid_t)_default_gid) 89 continue; 90 if (fchown(fd, uid, gid) == 0) { 91 _alt_gid = gid; 92 break; 93 } 94 } 95 close(fd); 96} 97 98static int 99altgid(void) 100{ 101 searchgid(); 102 return (_alt_gid); 103} 104 105static int 106invalidgid(void) 107{ 108 searchgid(); 109 return (_invalid_gid); 110} 111 112static int 113defaultgid(void) 114{ 115 searchgid(); 116 return (_default_gid); 117} 118#endif 119 120/* 121 * Exercise permission and ownership restores. 122 * In particular, try to exercise a bunch of border cases related 123 * to files/dirs that already exist, SUID/SGID bits, etc. 124 */ 125 126DEFINE_TEST(test_write_disk_perms) 127{ 128#if defined(_WIN32) && !defined(__CYGWIN__) 129 skipping("archive_write_disk interface"); 130#else 131 struct archive *a; 132 struct archive_entry *ae; 133 struct stat st; 134 uid_t original_uid; 135 uid_t try_to_change_uid; 136 137 assertUmask(UMASK); 138 139 /* 140 * Set ownership of the current directory to the group of this 141 * process. Otherwise, the SGID tests below fail if the 142 * /tmp directory is owned by a group to which we don't belong 143 * and we're on a system where group ownership is inherited. 144 * (Because we're not allowed to SGID files with defaultgid().) 145 */ 146 assertEqualInt(0, chown(".", getuid(), getgid())); 147 148 /* Create an archive_write_disk object. */ 149 assert((a = archive_write_disk_new()) != NULL); 150 151 /* Write a regular file to it. */ 152 assert((ae = archive_entry_new()) != NULL); 153 archive_entry_copy_pathname(ae, "file_0755"); 154 archive_entry_set_mode(ae, S_IFREG | 0777); 155 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 156 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 157 archive_entry_free(ae); 158 159 /* Write a regular file, then write over it. */ 160 /* For files, the perms should get updated. */ 161 assert((ae = archive_entry_new()) != NULL); 162 archive_entry_copy_pathname(ae, "file_overwrite_0144"); 163 archive_entry_set_mode(ae, S_IFREG | 0777); 164 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 165 archive_entry_free(ae); 166 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 167 /* Check that file was created with different perms. */ 168 assertEqualInt(0, stat("file_overwrite_0144", &st)); 169 failure("file_overwrite_0144: st.st_mode=%o", st.st_mode); 170 assert((st.st_mode & 07777) != 0144); 171 /* Overwrite, this should change the perms. */ 172 assert((ae = archive_entry_new()) != NULL); 173 archive_entry_copy_pathname(ae, "file_overwrite_0144"); 174 archive_entry_set_mode(ae, S_IFREG | 0144); 175 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 176 archive_entry_free(ae); 177 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 178 179 /* Write a regular dir. */ 180 assert((ae = archive_entry_new()) != NULL); 181 archive_entry_copy_pathname(ae, "dir_0514"); 182 archive_entry_set_mode(ae, S_IFDIR | 0514); 183 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 184 archive_entry_free(ae); 185 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 186 187 /* Overwrite an existing dir. */ 188 /* For dir, the first perms should get left. */ 189 assertMakeDir("dir_overwrite_0744", 0744); 190 /* Check original perms. */ 191 assertEqualInt(0, stat("dir_overwrite_0744", &st)); 192 failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); 193 assertEqualInt(st.st_mode & 0777, 0744); 194 /* Overwrite shouldn't edit perms. */ 195 assert((ae = archive_entry_new()) != NULL); 196 archive_entry_copy_pathname(ae, "dir_overwrite_0744"); 197 archive_entry_set_mode(ae, S_IFDIR | 0777); 198 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 199 archive_entry_free(ae); 200 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 201 /* Make sure they're unchanged. */ 202 assertEqualInt(0, stat("dir_overwrite_0744", &st)); 203 failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); 204 assertEqualInt(st.st_mode & 0777, 0744); 205 206 /* For dir, the owner should get left when not overwritting. */ 207 assertMakeDir("dir_owner", 0744); 208 209 if (getuid() == 0) { 210 original_uid = getuid() + 1; 211 try_to_change_uid = getuid(); 212 assertEqualInt(0, chown("dir_owner", original_uid, getgid())); 213 } else { 214 original_uid = getuid(); 215 try_to_change_uid = getuid() + 1; 216 } 217 218 /* Check original owner. */ 219 assertEqualInt(0, stat("dir_owner", &st)); 220 failure("dir_owner: st.st_uid=%d", st.st_uid); 221 assertEqualInt(st.st_uid, original_uid); 222 /* Shouldn't try to edit the owner when no overwrite option is set. */ 223 assert((ae = archive_entry_new()) != NULL); 224 archive_entry_copy_pathname(ae, "dir_owner"); 225 archive_entry_set_mode(ae, S_IFDIR | 0744); 226 archive_entry_set_uid(ae, try_to_change_uid); 227 archive_write_disk_set_options(a, 228 ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_NO_OVERWRITE); 229 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 230 archive_entry_free(ae); 231 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 232 /* Make sure they're unchanged. */ 233 assertEqualInt(0, stat("dir_owner", &st)); 234 failure("dir_owner: st.st_uid=%d", st.st_uid); 235 assertEqualInt(st.st_uid, original_uid); 236 237 /* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */ 238 assert((ae = archive_entry_new()) != NULL); 239 archive_entry_copy_pathname(ae, "file_no_suid"); 240 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0777); 241 archive_write_disk_set_options(a, 0); 242 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 243 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 244 245 /* Write a regular file with ARCHIVE_EXTRACT_PERM. */ 246 assert(archive_entry_clear(ae) != NULL); 247 archive_entry_copy_pathname(ae, "file_0777"); 248 archive_entry_set_mode(ae, S_IFREG | 0777); 249 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 250 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 251 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 252 253 /* Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit */ 254 assert(archive_entry_clear(ae) != NULL); 255 archive_entry_copy_pathname(ae, "file_4742"); 256 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); 257 archive_entry_set_uid(ae, getuid()); 258 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 259 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 260 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 261 262 /* 263 * Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit, 264 * but wrong uid. POSIX says you shouldn't restore SUID bit 265 * unless the UID could be restored. 266 */ 267 assert(archive_entry_clear(ae) != NULL); 268 archive_entry_copy_pathname(ae, "file_bad_suid"); 269 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); 270 archive_entry_set_uid(ae, getuid() + 1); 271 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 272 assertA(0 == archive_write_header(a, ae)); 273 /* 274 * Because we didn't ask for owner, the failure to 275 * restore SUID shouldn't return a failure. 276 * We check below to make sure SUID really wasn't set. 277 * See more detailed comments below. 278 */ 279 failure("Opportunistic SUID failure shouldn't return error."); 280 assertEqualInt(0, archive_write_finish_entry(a)); 281 282 if (getuid() != 0) { 283 assert(archive_entry_clear(ae) != NULL); 284 archive_entry_copy_pathname(ae, "file_bad_suid2"); 285 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); 286 archive_entry_set_uid(ae, getuid() + 1); 287 archive_write_disk_set_options(a, 288 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); 289 assertA(0 == archive_write_header(a, ae)); 290 /* Owner change should fail here. */ 291 failure("Non-opportunistic SUID failure should return error."); 292 assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a)); 293 } 294 295 /* Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit */ 296 assert(archive_entry_clear(ae) != NULL); 297 archive_entry_copy_pathname(ae, "file_perm_sgid"); 298 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 299 archive_entry_set_gid(ae, defaultgid()); 300 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 301 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 302 failure("Setting SGID bit should succeed here."); 303 assertEqualIntA(a, 0, archive_write_finish_entry(a)); 304 305 if (altgid() == -1) { 306 /* 307 * Current user must belong to at least two groups or 308 * else we can't test setting the GID to another group. 309 */ 310 skipping("Current user can't test gid restore: must belong to more than one group."); 311 } else { 312 /* 313 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit 314 * but without ARCHIVE_EXTRACT_OWNER. 315 */ 316 /* 317 * This is a weird case: The user has asked for permissions to 318 * be restored but not asked for ownership to be restored. As 319 * a result, the default file creation will create a file with 320 * the wrong group. There are several possible behaviors for 321 * libarchive in this scenario: 322 * = Set the SGID bit. It is wrong and a security hole to 323 * set SGID with the wrong group. Even POSIX thinks so. 324 * = Implicitly set the group. I don't like this. 325 * = drop the SGID bit and warn (the old libarchive behavior) 326 * = drop the SGID bit and don't warn (the current libarchive 327 * behavior). 328 * The current behavior sees SGID/SUID restore when you 329 * don't ask for owner restore as an "opportunistic" 330 * action. That is, libarchive should do it if it can, 331 * but if it can't, it's not an error. 332 */ 333 assert(archive_entry_clear(ae) != NULL); 334 archive_entry_copy_pathname(ae, "file_alt_sgid"); 335 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 336 archive_entry_set_uid(ae, getuid()); 337 archive_entry_set_gid(ae, altgid()); 338 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 339 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 340 failure("Setting SGID bit should fail because of group mismatch but the failure should be silent because we didn't ask for the group to be set."); 341 assertEqualIntA(a, 0, archive_write_finish_entry(a)); 342 343 /* 344 * As above, but add _EXTRACT_OWNER to verify that it 345 * does succeed. 346 */ 347 assert(archive_entry_clear(ae) != NULL); 348 archive_entry_copy_pathname(ae, "file_alt_sgid_owner"); 349 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 350 archive_entry_set_uid(ae, getuid()); 351 archive_entry_set_gid(ae, altgid()); 352 archive_write_disk_set_options(a, 353 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); 354 assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); 355 failure("Setting SGID bit should succeed here."); 356 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 357 } 358 359 /* 360 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit, 361 * but wrong GID. POSIX says you shouldn't restore SGID bit 362 * unless the GID could be restored. 363 */ 364 if (invalidgid() == -1) { 365 /* This test always fails for root. */ 366 printf("Running as root: Can't test SGID failures.\n"); 367 } else { 368 assert(archive_entry_clear(ae) != NULL); 369 archive_entry_copy_pathname(ae, "file_bad_sgid"); 370 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 371 archive_entry_set_gid(ae, invalidgid()); 372 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 373 assertA(0 == archive_write_header(a, ae)); 374 failure("This SGID restore should fail without an error."); 375 assertEqualIntA(a, 0, archive_write_finish_entry(a)); 376 377 assert(archive_entry_clear(ae) != NULL); 378 archive_entry_copy_pathname(ae, "file_bad_sgid2"); 379 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 380 archive_entry_set_gid(ae, invalidgid()); 381 archive_write_disk_set_options(a, 382 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); 383 assertA(0 == archive_write_header(a, ae)); 384 failure("This SGID restore should fail with an error."); 385 assertEqualIntA(a, ARCHIVE_WARN, archive_write_finish_entry(a)); 386 } 387 388 /* Set ownership should fail if we're not root. */ 389 if (getuid() == 0) { 390 printf("Running as root: Can't test setuid failures.\n"); 391 } else { 392 assert(archive_entry_clear(ae) != NULL); 393 archive_entry_copy_pathname(ae, "file_bad_owner"); 394 archive_entry_set_mode(ae, S_IFREG | 0744); 395 archive_entry_set_uid(ae, getuid() + 1); 396 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_OWNER); 397 assertA(0 == archive_write_header(a, ae)); 398 assertEqualIntA(a,ARCHIVE_WARN,archive_write_finish_entry(a)); 399 } 400 401 assertEqualInt(ARCHIVE_OK, archive_write_free(a)); 402 archive_entry_free(ae); 403 404 /* Test the entries on disk. */ 405 assertEqualInt(0, stat("file_0755", &st)); 406 failure("file_0755: st.st_mode=%o", st.st_mode); 407 assertEqualInt(st.st_mode & 07777, 0755); 408 409 assertEqualInt(0, stat("file_overwrite_0144", &st)); 410 failure("file_overwrite_0144: st.st_mode=%o", st.st_mode); 411 assertEqualInt(st.st_mode & 07777, 0144); 412 413 assertEqualInt(0, stat("dir_0514", &st)); 414 failure("dir_0514: st.st_mode=%o", st.st_mode); 415 assertEqualInt(st.st_mode & 07777, 0514); 416 417 assertEqualInt(0, stat("dir_overwrite_0744", &st)); 418 failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); 419 assertEqualInt(st.st_mode & 0777, 0744); 420 421 assertEqualInt(0, stat("file_no_suid", &st)); 422 failure("file_0755: st.st_mode=%o", st.st_mode); 423 assertEqualInt(st.st_mode & 07777, 0755); 424 425 assertEqualInt(0, stat("file_0777", &st)); 426 failure("file_0777: st.st_mode=%o", st.st_mode); 427 assertEqualInt(st.st_mode & 07777, 0777); 428 429 /* SUID bit should get set here. */ 430 assertEqualInt(0, stat("file_4742", &st)); 431 failure("file_4742: st.st_mode=%o", st.st_mode); 432 assertEqualInt(st.st_mode & 07777, S_ISUID | 0742); 433 434 /* SUID bit should NOT have been set here. */ 435 assertEqualInt(0, stat("file_bad_suid", &st)); 436 failure("file_bad_suid: st.st_mode=%o", st.st_mode); 437 assertEqualInt(st.st_mode & 07777, 0742); 438 439 /* Some things don't fail if you're root, so suppress this. */ 440 if (getuid() != 0) { 441 /* SUID bit should NOT have been set here. */ 442 assertEqualInt(0, stat("file_bad_suid2", &st)); 443 failure("file_bad_suid2: st.st_mode=%o", st.st_mode); 444 assertEqualInt(st.st_mode & 07777, 0742); 445 } 446 447 /* SGID should be set here. */ 448 assertEqualInt(0, stat("file_perm_sgid", &st)); 449 failure("file_perm_sgid: st.st_mode=%o", st.st_mode); 450 assertEqualInt(st.st_mode & 07777, S_ISGID | 0742); 451 452 if (altgid() != -1) { 453 /* SGID should not be set here. */ 454 assertEqualInt(0, stat("file_alt_sgid", &st)); 455 failure("file_alt_sgid: st.st_mode=%o", st.st_mode); 456 assertEqualInt(st.st_mode & 07777, 0742); 457 458 /* SGID should be set here. */ 459 assertEqualInt(0, stat("file_alt_sgid_owner", &st)); 460 failure("file_alt_sgid: st.st_mode=%o", st.st_mode); 461 assertEqualInt(st.st_mode & 07777, S_ISGID | 0742); 462 } 463 464 if (invalidgid() != -1) { 465 /* SGID should NOT be set here. */ 466 assertEqualInt(0, stat("file_bad_sgid", &st)); 467 failure("file_bad_sgid: st.st_mode=%o", st.st_mode); 468 assertEqualInt(st.st_mode & 07777, 0742); 469 /* SGID should NOT be set here. */ 470 assertEqualInt(0, stat("file_bad_sgid2", &st)); 471 failure("file_bad_sgid2: st.st_mode=%o", st.st_mode); 472 assertEqualInt(st.st_mode & 07777, 0742); 473 } 474 475 if (getuid() != 0) { 476 assertEqualInt(0, stat("file_bad_owner", &st)); 477 failure("file_bad_owner: st.st_mode=%o", st.st_mode); 478 assertEqualInt(st.st_mode & 07777, 0744); 479 failure("file_bad_owner: st.st_uid=%d getuid()=%d", 480 st.st_uid, getuid()); 481 /* The entry had getuid()+1, but because we're 482 * not root, we should not have been able to set that. */ 483 assertEqualInt(st.st_uid, getuid()); 484 } 485#endif 486} 487