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