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: src/lib/libarchive/test/test_write_disk_perms.c,v 1.9 2008/06/15 10:35:22 kientzle Exp $"); 27 28 #if ARCHIVE_VERSION_STAMP >= 1009000 29 30 #define UMASK 022 31 32 static long _default_gid = -1; 33 static long _invalid_gid = -1; 34 static 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 */ 47 static void 48 searchgid(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, 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 98 static int 99 altgid(void) 100 { 101 searchgid(); 102 return (_alt_gid); 103 } 104 105 static int 106 invalidgid(void) 107 { 108 searchgid(); 109 return (_invalid_gid); 110 } 111 112 static int 113 defaultgid(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 126 DEFINE_TEST(test_write_disk_perms) 127 { 128 #if ARCHIVE_VERSION_STAMP < 1009000 129 skipping("archive_write_disk interface"); 130 #else 131 struct archive *a; 132 struct archive_entry *ae; 133 struct stat st; 134 135 /* 136 * Set ownership of the current directory to the group of this 137 * process. Otherwise, the SGID tests below fail if the 138 * /tmp directory is owned by a group to which we don't belong 139 * and we're on a system where group ownership is inherited. 140 * (Because we're not allowed to SGID files with defaultgid().) 141 */ 142 assertEqualInt(0, chown(".", getuid(), getgid())); 143 144 /* Create an archive_write_disk object. */ 145 assert((a = archive_write_disk_new()) != NULL); 146 147 /* Write a regular file to it. */ 148 assert((ae = archive_entry_new()) != NULL); 149 archive_entry_copy_pathname(ae, "file_0755"); 150 archive_entry_set_mode(ae, S_IFREG | 0777); 151 assert(0 == archive_write_header(a, ae)); 152 assert(0 == archive_write_finish_entry(a)); 153 archive_entry_free(ae); 154 155 /* Write a regular file, then write over it. */ 156 /* For files, the perms should get updated. */ 157 assert((ae = archive_entry_new()) != NULL); 158 archive_entry_copy_pathname(ae, "file_overwrite_0144"); 159 archive_entry_set_mode(ae, S_IFREG | 0777); 160 assert(0 == archive_write_header(a, ae)); 161 archive_entry_free(ae); 162 assert(0 == archive_write_finish_entry(a)); 163 /* Check that file was created with different perms. */ 164 assert(0 == stat("file_overwrite_0144", &st)); 165 failure("file_overwrite_0144: st.st_mode=%o", st.st_mode); 166 assert((st.st_mode & 07777) != 0144); 167 /* Overwrite, this should change the perms. */ 168 assert((ae = archive_entry_new()) != NULL); 169 archive_entry_copy_pathname(ae, "file_overwrite_0144"); 170 archive_entry_set_mode(ae, S_IFREG | 0144); 171 assert(0 == archive_write_header(a, ae)); 172 archive_entry_free(ae); 173 assert(0 == archive_write_finish_entry(a)); 174 175 /* Write a regular dir. */ 176 assert((ae = archive_entry_new()) != NULL); 177 archive_entry_copy_pathname(ae, "dir_0514"); 178 archive_entry_set_mode(ae, S_IFDIR | 0514); 179 assert(0 == archive_write_header(a, ae)); 180 archive_entry_free(ae); 181 assert(0 == archive_write_finish_entry(a)); 182 183 /* Overwrite an existing dir. */ 184 /* For dir, the first perms should get left. */ 185 assert(mkdir("dir_overwrite_0744", 0744) == 0); 186 /* Check original perms. */ 187 assert(0 == stat("dir_overwrite_0744", &st)); 188 failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); 189 assert((st.st_mode & 0777) == 0744); 190 /* Overwrite shouldn't edit perms. */ 191 assert((ae = archive_entry_new()) != NULL); 192 archive_entry_copy_pathname(ae, "dir_overwrite_0744"); 193 archive_entry_set_mode(ae, S_IFDIR | 0777); 194 assert(0 == archive_write_header(a, ae)); 195 archive_entry_free(ae); 196 assert(0 == archive_write_finish_entry(a)); 197 /* Make sure they're unchanged. */ 198 assert(0 == stat("dir_overwrite_0744", &st)); 199 failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); 200 assert((st.st_mode & 0777) == 0744); 201 202 /* Write a regular file with SUID bit, but don't use _EXTRACT_PERM. */ 203 assert((ae = archive_entry_new()) != NULL); 204 archive_entry_copy_pathname(ae, "file_no_suid"); 205 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0777); 206 archive_write_disk_set_options(a, 0); 207 assert(0 == archive_write_header(a, ae)); 208 assert(0 == archive_write_finish_entry(a)); 209 210 /* Write a regular file with ARCHIVE_EXTRACT_PERM. */ 211 assert(archive_entry_clear(ae) != NULL); 212 archive_entry_copy_pathname(ae, "file_0777"); 213 archive_entry_set_mode(ae, S_IFREG | 0777); 214 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 215 assert(0 == archive_write_header(a, ae)); 216 assert(0 == archive_write_finish_entry(a)); 217 218 /* Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit */ 219 assert(archive_entry_clear(ae) != NULL); 220 archive_entry_copy_pathname(ae, "file_4742"); 221 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); 222 archive_entry_set_uid(ae, getuid()); 223 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 224 assert(0 == archive_write_header(a, ae)); 225 assert(0 == archive_write_finish_entry(a)); 226 227 /* 228 * Write a regular file with ARCHIVE_EXTRACT_PERM & SUID bit, 229 * but wrong uid. POSIX says you shouldn't restore SUID bit 230 * unless the UID could be restored. 231 */ 232 assert(archive_entry_clear(ae) != NULL); 233 archive_entry_copy_pathname(ae, "file_bad_suid"); 234 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); 235 archive_entry_set_uid(ae, getuid() + 1); 236 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 237 assertA(0 == archive_write_header(a, ae)); 238 /* 239 * Because we didn't ask for owner, the failure to 240 * restore SUID shouldn't return a failure. 241 * We check below to make sure SUID really wasn't set. 242 * See more detailed comments below. 243 */ 244 failure("Opportunistic SUID failure shouldn't return error."); 245 assertEqualInt(0, archive_write_finish_entry(a)); 246 247 if (getuid() != 0) { 248 assert(archive_entry_clear(ae) != NULL); 249 archive_entry_copy_pathname(ae, "file_bad_suid2"); 250 archive_entry_set_mode(ae, S_IFREG | S_ISUID | 0742); 251 archive_entry_set_uid(ae, getuid() + 1); 252 archive_write_disk_set_options(a, 253 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); 254 assertA(0 == archive_write_header(a, ae)); 255 /* Owner change should fail here. */ 256 failure("Non-opportunistic SUID failure should return error."); 257 assertEqualInt(ARCHIVE_WARN, archive_write_finish_entry(a)); 258 } 259 260 /* Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit */ 261 assert(archive_entry_clear(ae) != NULL); 262 archive_entry_copy_pathname(ae, "file_perm_sgid"); 263 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 264 archive_entry_set_gid(ae, defaultgid()); 265 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 266 assert(0 == archive_write_header(a, ae)); 267 failure("Setting SGID bit should succeed here."); 268 assertEqualIntA(a, 0, archive_write_finish_entry(a)); 269 270 if (altgid() == -1) { 271 /* 272 * Current user must belong to at least two groups or 273 * else we can't test setting the GID to another group. 274 */ 275 printf("Current user can't test gid restore: must belong to more than one group.\n"); 276 } else { 277 /* 278 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit 279 * but without ARCHIVE_EXTRACT_OWNER. 280 */ 281 /* 282 * This is a weird case: The user has asked for permissions to 283 * be restored but not asked for ownership to be restored. As 284 * a result, the default file creation will create a file with 285 * the wrong group. There are several possible behaviors for 286 * libarchive in this scenario: 287 * = Set the SGID bit. It is wrong and a security hole to 288 * set SGID with the wrong group. Even POSIX thinks so. 289 * = Implicitly set the group. I don't like this. 290 * = drop the SGID bit and warn (the old libarchive behavior) 291 * = drop the SGID bit and don't warn (the current libarchive 292 * behavior). 293 * The current behavior sees SGID/SUID restore when you 294 * don't ask for owner restore as an "opportunistic" 295 * action. That is, libarchive should do it if it can, 296 * but if it can't, it's not an error. 297 */ 298 assert(archive_entry_clear(ae) != NULL); 299 archive_entry_copy_pathname(ae, "file_alt_sgid"); 300 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 301 archive_entry_set_uid(ae, getuid()); 302 archive_entry_set_gid(ae, altgid()); 303 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 304 assert(0 == archive_write_header(a, ae)); 305 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."); 306 assertEqualIntA(a, 0, archive_write_finish_entry(a)); 307 308 /* 309 * As above, but add _EXTRACT_OWNER to verify that it 310 * does succeed. 311 */ 312 assert(archive_entry_clear(ae) != NULL); 313 archive_entry_copy_pathname(ae, "file_alt_sgid_owner"); 314 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 315 archive_entry_set_uid(ae, getuid()); 316 archive_entry_set_gid(ae, altgid()); 317 archive_write_disk_set_options(a, 318 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); 319 assert(0 == archive_write_header(a, ae)); 320 failure("Setting SGID bit should succeed here."); 321 assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a)); 322 } 323 324 /* 325 * Write a regular file with ARCHIVE_EXTRACT_PERM & SGID bit, 326 * but wrong GID. POSIX says you shouldn't restore SGID bit 327 * unless the GID could be restored. 328 */ 329 if (invalidgid() == -1) { 330 /* This test always fails for root. */ 331 printf("Running as root: Can't test SGID failures.\n"); 332 } else { 333 assert(archive_entry_clear(ae) != NULL); 334 archive_entry_copy_pathname(ae, "file_bad_sgid"); 335 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 336 archive_entry_set_gid(ae, invalidgid()); 337 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_PERM); 338 assertA(0 == archive_write_header(a, ae)); 339 failure("This SGID restore should fail without an error."); 340 assertEqualIntA(a, 0, archive_write_finish_entry(a)); 341 342 assert(archive_entry_clear(ae) != NULL); 343 archive_entry_copy_pathname(ae, "file_bad_sgid2"); 344 archive_entry_set_mode(ae, S_IFREG | S_ISGID | 0742); 345 archive_entry_set_gid(ae, invalidgid()); 346 archive_write_disk_set_options(a, 347 ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_OWNER); 348 assertA(0 == archive_write_header(a, ae)); 349 failure("This SGID restore should fail with an error."); 350 assertEqualIntA(a, ARCHIVE_WARN, archive_write_finish_entry(a)); 351 } 352 353 /* Set ownership should fail if we're not root. */ 354 if (getuid() == 0) { 355 printf("Running as root: Can't test setuid failures.\n"); 356 } else { 357 assert(archive_entry_clear(ae) != NULL); 358 archive_entry_copy_pathname(ae, "file_bad_owner"); 359 archive_entry_set_mode(ae, S_IFREG | 0744); 360 archive_entry_set_uid(ae, getuid() + 1); 361 archive_write_disk_set_options(a, ARCHIVE_EXTRACT_OWNER); 362 assertA(0 == archive_write_header(a, ae)); 363 assertEqualIntA(a,ARCHIVE_WARN,archive_write_finish_entry(a)); 364 } 365 366 #if ARCHIVE_API_VERSION > 1 367 assert(0 == archive_write_finish(a)); 368 #else 369 archive_write_finish(a); 370 #endif 371 archive_entry_free(ae); 372 373 /* Test the entries on disk. */ 374 assert(0 == stat("file_0755", &st)); 375 failure("file_0755: st.st_mode=%o", st.st_mode); 376 assert((st.st_mode & 07777) == 0755); 377 378 assert(0 == stat("file_overwrite_0144", &st)); 379 failure("file_overwrite_0144: st.st_mode=%o", st.st_mode); 380 assert((st.st_mode & 07777) == 0144); 381 382 assert(0 == stat("dir_0514", &st)); 383 failure("dir_0514: st.st_mode=%o", st.st_mode); 384 assert((st.st_mode & 07777) == 0514); 385 386 assert(0 == stat("dir_overwrite_0744", &st)); 387 failure("dir_overwrite_0744: st.st_mode=%o", st.st_mode); 388 assert((st.st_mode & 0777) == 0744); 389 390 assert(0 == stat("file_no_suid", &st)); 391 failure("file_0755: st.st_mode=%o", st.st_mode); 392 assert((st.st_mode & 07777) == 0755); 393 394 assert(0 == stat("file_0777", &st)); 395 failure("file_0777: st.st_mode=%o", st.st_mode); 396 assert((st.st_mode & 07777) == 0777); 397 398 /* SUID bit should get set here. */ 399 assert(0 == stat("file_4742", &st)); 400 failure("file_4742: st.st_mode=%o", st.st_mode); 401 assert((st.st_mode & 07777) == (S_ISUID | 0742)); 402 403 /* SUID bit should NOT have been set here. */ 404 assert(0 == stat("file_bad_suid", &st)); 405 failure("file_bad_suid: st.st_mode=%o", st.st_mode); 406 assert((st.st_mode & 07777) == (0742)); 407 408 /* Some things don't fail if you're root, so suppress this. */ 409 if (getuid() != 0) { 410 /* SUID bit should NOT have been set here. */ 411 assert(0 == stat("file_bad_suid2", &st)); 412 failure("file_bad_suid2: st.st_mode=%o", st.st_mode); 413 assert((st.st_mode & 07777) == (0742)); 414 } 415 416 /* SGID should be set here. */ 417 assert(0 == stat("file_perm_sgid", &st)); 418 failure("file_perm_sgid: st.st_mode=%o", st.st_mode); 419 assert((st.st_mode & 07777) == (S_ISGID | 0742)); 420 421 if (altgid() != -1) { 422 /* SGID should not be set here. */ 423 assert(0 == stat("file_alt_sgid", &st)); 424 failure("file_alt_sgid: st.st_mode=%o", st.st_mode); 425 assert((st.st_mode & 07777) == (0742)); 426 427 /* SGID should be set here. */ 428 assert(0 == stat("file_alt_sgid_owner", &st)); 429 failure("file_alt_sgid: st.st_mode=%o", st.st_mode); 430 assert((st.st_mode & 07777) == (S_ISGID | 0742)); 431 } 432 433 if (invalidgid() != -1) { 434 /* SGID should NOT be set here. */ 435 assert(0 == stat("file_bad_sgid", &st)); 436 failure("file_bad_sgid: st.st_mode=%o", st.st_mode); 437 assert((st.st_mode & 07777) == (0742)); 438 /* SGID should NOT be set here. */ 439 assert(0 == stat("file_bad_sgid2", &st)); 440 failure("file_bad_sgid2: st.st_mode=%o", st.st_mode); 441 assert((st.st_mode & 07777) == (0742)); 442 } 443 444 if (getuid() != 0) { 445 assert(0 == stat("file_bad_owner", &st)); 446 failure("file_bad_owner: st.st_mode=%o", st.st_mode); 447 assert((st.st_mode & 07777) == (0744)); 448 failure("file_bad_owner: st.st_uid=%d getuid()=%d", 449 st.st_uid, getuid()); 450 /* The entry had getuid()+1, but because we're 451 * not root, we should not have been able to set that. */ 452 assert(st.st_uid == getuid()); 453 } 454 #endif 455 } 456