1 /*- 2 * Copyright (c) 2017 Martin Matuska 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 #if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL 29 static const acl_perm_t acl_perms[] = { 30 #if ARCHIVE_ACL_DARWIN 31 ACL_READ_DATA, 32 ACL_LIST_DIRECTORY, 33 ACL_WRITE_DATA, 34 ACL_ADD_FILE, 35 ACL_EXECUTE, 36 ACL_SEARCH, 37 ACL_DELETE, 38 ACL_APPEND_DATA, 39 ACL_ADD_SUBDIRECTORY, 40 ACL_DELETE_CHILD, 41 ACL_READ_ATTRIBUTES, 42 ACL_WRITE_ATTRIBUTES, 43 ACL_READ_EXTATTRIBUTES, 44 ACL_WRITE_EXTATTRIBUTES, 45 ACL_READ_SECURITY, 46 ACL_WRITE_SECURITY, 47 ACL_CHANGE_OWNER, 48 ACL_SYNCHRONIZE 49 #else /* !ARCHIVE_ACL_DARWIN */ 50 ACL_EXECUTE, 51 ACL_WRITE, 52 ACL_READ, 53 #if ARCHIVE_ACL_FREEBSD_NFS4 54 ACL_READ_DATA, 55 ACL_LIST_DIRECTORY, 56 ACL_WRITE_DATA, 57 ACL_ADD_FILE, 58 ACL_APPEND_DATA, 59 ACL_ADD_SUBDIRECTORY, 60 ACL_READ_NAMED_ATTRS, 61 ACL_WRITE_NAMED_ATTRS, 62 ACL_DELETE_CHILD, 63 ACL_READ_ATTRIBUTES, 64 ACL_WRITE_ATTRIBUTES, 65 ACL_DELETE, 66 ACL_READ_ACL, 67 ACL_WRITE_ACL, 68 ACL_WRITE_OWNER, 69 ACL_SYNCHRONIZE 70 #endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ 71 #endif /* !ARCHIVE_ACL_DARWIN */ 72 }; 73 #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 74 static const acl_flag_t acl_flags[] = { 75 #if ARCHIVE_ACL_DARWIN 76 ACL_ENTRY_INHERITED, 77 ACL_ENTRY_FILE_INHERIT, 78 ACL_ENTRY_DIRECTORY_INHERIT, 79 ACL_ENTRY_LIMIT_INHERIT, 80 ACL_ENTRY_ONLY_INHERIT 81 #else /* ARCHIVE_ACL_FREEBSD_NFS4 */ 82 ACL_ENTRY_FILE_INHERIT, 83 ACL_ENTRY_DIRECTORY_INHERIT, 84 ACL_ENTRY_NO_PROPAGATE_INHERIT, 85 ACL_ENTRY_INHERIT_ONLY, 86 ACL_ENTRY_SUCCESSFUL_ACCESS, 87 ACL_ENTRY_FAILED_ACCESS, 88 ACL_ENTRY_INHERITED 89 #endif /* ARCHIVE_ACL_FREEBSD_NFS4 */ 90 }; 91 #endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 */ 92 93 /* 94 * Compare two ACL entries on FreeBSD or on Mac OS X 95 */ 96 static int 97 compare_acl_entry(acl_entry_t ae_a, acl_entry_t ae_b, int is_nfs4) 98 { 99 acl_tag_t tag_a, tag_b; 100 acl_permset_t permset_a, permset_b; 101 int perm_a, perm_b, perm_start, perm_end; 102 void *qual_a, *qual_b; 103 #if ARCHIVE_ACL_FREEBSD_NFS4 104 acl_entry_type_t type_a, type_b; 105 #endif 106 #if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN 107 acl_flagset_t flagset_a, flagset_b; 108 int flag_a, flag_b; 109 #endif 110 int i, r; 111 112 113 /* Compare ACL tag */ 114 r = acl_get_tag_type(ae_a, &tag_a); 115 failure("acl_get_tag_type() error: %s", strerror(errno)); 116 if (assertEqualInt(r, 0) == 0) 117 return (-1); 118 r = acl_get_tag_type(ae_b, &tag_b); 119 failure("acl_get_tag_type() error: %s", strerror(errno)); 120 if (assertEqualInt(r, 0) == 0) 121 return (-1); 122 if (tag_a != tag_b) 123 return (0); 124 125 /* Compare ACL qualifier */ 126 #if ARCHIVE_ACL_DARWIN 127 if (tag_a == ACL_EXTENDED_ALLOW || tag_b == ACL_EXTENDED_DENY) 128 #else 129 if (tag_a == ACL_USER || tag_a == ACL_GROUP) 130 #endif 131 { 132 qual_a = acl_get_qualifier(ae_a); 133 failure("acl_get_qualifier() error: %s", strerror(errno)); 134 if (assert(qual_a != NULL) == 0) 135 return (-1); 136 qual_b = acl_get_qualifier(ae_b); 137 failure("acl_get_qualifier() error: %s", strerror(errno)); 138 if (assert(qual_b != NULL) == 0) { 139 acl_free(qual_a); 140 return (-1); 141 } 142 #if ARCHIVE_ACL_DARWIN 143 if (memcmp(((guid_t *)qual_a)->g_guid, 144 ((guid_t *)qual_b)->g_guid, KAUTH_GUID_SIZE) != 0) 145 #else 146 if ((tag_a == ACL_USER && 147 (*(uid_t *)qual_a != *(uid_t *)qual_b)) || 148 (tag_a == ACL_GROUP && 149 (*(gid_t *)qual_a != *(gid_t *)qual_b))) 150 #endif 151 { 152 acl_free(qual_a); 153 acl_free(qual_b); 154 return (0); 155 } 156 acl_free(qual_a); 157 acl_free(qual_b); 158 } 159 160 #if ARCHIVE_ACL_FREEBSD_NFS4 161 if (is_nfs4) { 162 /* Compare NFS4 ACL type */ 163 r = acl_get_entry_type_np(ae_a, &type_a); 164 failure("acl_get_entry_type_np() error: %s", strerror(errno)); 165 if (assertEqualInt(r, 0) == 0) 166 return (-1); 167 r = acl_get_entry_type_np(ae_b, &type_b); 168 failure("acl_get_entry_type_np() error: %s", strerror(errno)); 169 if (assertEqualInt(r, 0) == 0) 170 return (-1); 171 if (type_a != type_b) 172 return (0); 173 } 174 #endif 175 176 /* Compare ACL perms */ 177 r = acl_get_permset(ae_a, &permset_a); 178 failure("acl_get_permset() error: %s", strerror(errno)); 179 if (assertEqualInt(r, 0) == 0) 180 return (-1); 181 r = acl_get_permset(ae_b, &permset_b); 182 failure("acl_get_permset() error: %s", strerror(errno)); 183 if (assertEqualInt(r, 0) == 0) 184 return (-1); 185 186 perm_start = 0; 187 perm_end = (int)(sizeof(acl_perms) / sizeof(acl_perms[0])); 188 #if ARCHIVE_ACL_FREEBSD_NFS4 189 if (is_nfs4) 190 perm_start = 3; 191 else 192 perm_end = 3; 193 #endif 194 /* Cycle through all perms and compare their value */ 195 for (i = perm_start; i < perm_end; i++) { 196 #if ARCHIVE_ACL_LIBACL 197 perm_a = acl_get_perm(permset_a, acl_perms[i]); 198 perm_b = acl_get_perm(permset_b, acl_perms[i]); 199 #else 200 perm_a = acl_get_perm_np(permset_a, acl_perms[i]); 201 perm_b = acl_get_perm_np(permset_b, acl_perms[i]); 202 #endif 203 if (perm_a == -1 || perm_b == -1) 204 return (-1); 205 if (perm_a != perm_b) 206 return (0); 207 } 208 209 #if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN 210 if (is_nfs4) { 211 r = acl_get_flagset_np(ae_a, &flagset_a); 212 failure("acl_get_flagset_np() error: %s", strerror(errno)); 213 if (assertEqualInt(r, 0) == 0) 214 return (-1); 215 r = acl_get_flagset_np(ae_b, &flagset_b); 216 failure("acl_get_flagset_np() error: %s", strerror(errno)); 217 if (assertEqualInt(r, 0) == 0) 218 return (-1); 219 /* Cycle through all flags and compare their status */ 220 for (i = 0; i < (int)(sizeof(acl_flags) / sizeof(acl_flags[0])); 221 i++) { 222 flag_a = acl_get_flag_np(flagset_a, acl_flags[i]); 223 flag_b = acl_get_flag_np(flagset_b, acl_flags[i]); 224 if (flag_a == -1 || flag_b == -1) 225 return (-1); 226 if (flag_a != flag_b) 227 return (0); 228 } 229 } 230 #else /* ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN */ 231 (void)is_nfs4; /* UNUSED */ 232 #endif 233 return (1); 234 } 235 #endif /* ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL */ 236 237 #if ARCHIVE_ACL_SUPPORT 238 /* 239 * Clear default ACLs or inheritance flags 240 */ 241 static void 242 clear_inheritance_flags(const char *path, int type) 243 { 244 switch (type) { 245 case ARCHIVE_TEST_ACL_TYPE_POSIX1E: 246 #if ARCHIVE_ACL_POSIX1E 247 #if !ARCHIVE_ACL_SUNOS 248 acl_delete_def_file(path); 249 #else 250 /* Solaris */ 251 setTestAcl(path); 252 #endif 253 #endif /* ARCHIVE_ACL_POSIX1E */ 254 break; 255 case ARCHIVE_TEST_ACL_TYPE_NFS4: 256 #if ARCHIVE_ACL_NFS4 257 setTestAcl(path); 258 #endif 259 break; 260 default: 261 (void)path; /* UNUSED */ 262 break; 263 } 264 } 265 266 static int 267 compare_acls(const char *path_a, const char *path_b) 268 { 269 int ret = 1; 270 int is_nfs4 = 0; 271 #if ARCHIVE_ACL_SUNOS 272 void *acl_a, *acl_b; 273 int aclcnt_a, aclcnt_b; 274 aclent_t *aclent_a, *aclent_b; 275 ace_t *ace_a, *ace_b; 276 int e; 277 #elif ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL 278 acl_t acl_a, acl_b; 279 acl_entry_t aclent_a, aclent_b; 280 int a, b, r; 281 #endif 282 #if ARCHIVE_ACL_LIBRICHACL 283 struct richacl *richacl_a, *richacl_b; 284 285 richacl_a = NULL; 286 richacl_b = NULL; 287 #endif 288 289 #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL || \ 290 ARCHIVE_ACL_SUNOS 291 acl_a = NULL; 292 acl_b = NULL; 293 #endif 294 #if ARCHIVE_ACL_SUNOS 295 acl_a = sunacl_get(GETACL, &aclcnt_a, 0, path_a); 296 if (acl_a == NULL) { 297 #if ARCHIVE_ACL_SUNOS_NFS4 298 is_nfs4 = 1; 299 acl_a = sunacl_get(ACE_GETACL, &aclcnt_a, 0, path_a); 300 #endif 301 failure("acl_get() error: %s", strerror(errno)); 302 if (assert(acl_a != NULL) == 0) 303 return (-1); 304 #if ARCHIVE_ACL_SUNOS_NFS4 305 acl_b = sunacl_get(ACE_GETACL, &aclcnt_b, 0, path_b); 306 #endif 307 } else 308 acl_b = sunacl_get(GETACL, &aclcnt_b, 0, path_b); 309 if (acl_b == NULL && (errno == ENOSYS || errno == ENOTSUP)) { 310 free(acl_a); 311 return (0); 312 } 313 failure("acl_get() error: %s", strerror(errno)); 314 if (assert(acl_b != NULL) == 0) { 315 free(acl_a); 316 return (-1); 317 } 318 319 if (aclcnt_a != aclcnt_b) { 320 ret = 0; 321 goto exit_free; 322 } 323 324 for (e = 0; e < aclcnt_a; e++) { 325 if (!is_nfs4) { 326 aclent_a = &((aclent_t *)acl_a)[e]; 327 aclent_b = &((aclent_t *)acl_b)[e]; 328 if (aclent_a->a_type != aclent_b->a_type || 329 aclent_a->a_id != aclent_b->a_id || 330 aclent_a->a_perm != aclent_b->a_perm) { 331 ret = 0; 332 goto exit_free; 333 } 334 } 335 #if ARCHIVE_ACL_SUNOS_NFS4 336 else { 337 ace_a = &((ace_t *)acl_a)[e]; 338 ace_b = &((ace_t *)acl_b)[e]; 339 if (ace_a->a_who != ace_b->a_who || 340 ace_a->a_access_mask != ace_b->a_access_mask || 341 ace_a->a_flags != ace_b->a_flags || 342 ace_a->a_type != ace_b->a_type) { 343 ret = 0; 344 goto exit_free; 345 } 346 } 347 #endif 348 } 349 #else /* !ARCHIVE_ACL_SUNOS */ 350 #if ARCHIVE_ACL_LIBRICHACL 351 richacl_a = richacl_get_file(path_a); 352 #if !ARCHIVE_ACL_LIBACL 353 if (richacl_a == NULL && 354 (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) 355 return (0); 356 failure("richacl_get_file() error: %s (%s)", path_a, strerror(errno)); 357 if (assert(richacl_a != NULL) == 0) 358 return (-1); 359 #endif 360 if (richacl_a != NULL) { 361 richacl_b = richacl_get_file(path_b); 362 if (richacl_b == NULL && 363 (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) { 364 richacl_free(richacl_a); 365 return (0); 366 } 367 failure("richacl_get_file() error: %s (%s)", path_b, 368 strerror(errno)); 369 if (assert(richacl_b != NULL) == 0) { 370 richacl_free(richacl_a); 371 return (-1); 372 } 373 if (richacl_compare(richacl_a, richacl_b) == 0) 374 ret = 0; 375 richacl_free(richacl_a); 376 richacl_free(richacl_b); 377 return (ret); 378 } 379 #endif /* ARCHIVE_ACL_LIBRICHACL */ 380 #if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL 381 #if ARCHIVE_ACL_DARWIN 382 is_nfs4 = 1; 383 acl_a = acl_get_file(path_a, ACL_TYPE_EXTENDED); 384 #elif ARCHIVE_ACL_FREEBSD_NFS4 385 acl_a = acl_get_file(path_a, ACL_TYPE_NFS4); 386 if (acl_a != NULL) 387 is_nfs4 = 1; 388 #endif 389 if (acl_a == NULL) 390 acl_a = acl_get_file(path_a, ACL_TYPE_ACCESS); 391 failure("acl_get_file() error: %s (%s)", path_a, strerror(errno)); 392 if (assert(acl_a != NULL) == 0) 393 return (-1); 394 #if ARCHIVE_ACL_DARWIN 395 acl_b = acl_get_file(path_b, ACL_TYPE_EXTENDED); 396 #elif ARCHIVE_ACL_FREEBSD_NFS4 397 acl_b = acl_get_file(path_b, ACL_TYPE_NFS4); 398 #endif 399 #if !ARCHIVE_ACL_DARWIN 400 if (acl_b == NULL) { 401 #if ARCHIVE_ACL_FREEBSD_NFS4 402 if (is_nfs4) { 403 acl_free(acl_a); 404 return (0); 405 } 406 #endif 407 acl_b = acl_get_file(path_b, ACL_TYPE_ACCESS); 408 } 409 failure("acl_get_file() error: %s (%s)", path_b, strerror(errno)); 410 if (assert(acl_b != NULL) == 0) { 411 acl_free(acl_a); 412 return (-1); 413 } 414 #endif 415 a = acl_get_entry(acl_a, ACL_FIRST_ENTRY, &aclent_a); 416 if (a == -1) { 417 ret = 0; 418 goto exit_free; 419 } 420 b = acl_get_entry(acl_b, ACL_FIRST_ENTRY, &aclent_b); 421 if (b == -1) { 422 ret = 0; 423 goto exit_free; 424 } 425 #if ARCHIVE_ACL_DARWIN 426 while (a == 0 && b == 0) 427 #else /* FreeBSD, Linux */ 428 while (a == 1 && b == 1) 429 #endif 430 { 431 r = compare_acl_entry(aclent_a, aclent_b, is_nfs4); 432 if (r != 1) { 433 ret = r; 434 goto exit_free; 435 } 436 a = acl_get_entry(acl_a, ACL_NEXT_ENTRY, &aclent_a); 437 b = acl_get_entry(acl_b, ACL_NEXT_ENTRY, &aclent_b); 438 } 439 /* Entry count must match */ 440 if (a != b) 441 ret = 0; 442 #endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL */ 443 #endif /* !ARCHIVE_ACL_SUNOS */ 444 exit_free: 445 #if ARCHIVE_ACL_SUNOS 446 free(acl_a); 447 free(acl_b); 448 #else 449 acl_free(acl_a); 450 acl_free(acl_b); 451 #endif 452 return (ret); 453 } 454 #endif /* ARCHIVE_ACL_SUPPORT */ 455 456 DEFINE_TEST(test_option_acls) 457 { 458 #if !ARCHIVE_ACL_SUPPORT 459 skipping("ACLs are not supported on this platform"); 460 #else /* ARCHIVE_ACL_SUPPORT */ 461 int acltype, r; 462 463 assertMakeFile("f", 0644, "a"); 464 acltype = setTestAcl("f"); 465 if (acltype == 0) { 466 skipping("Can't write ACLs on the filesystem"); 467 return; 468 } 469 470 /* Archive it with acls */ 471 r = systemf("%s -c --no-mac-metadata --acls -f acls.tar f >acls.out 2>acls.err", testprog); 472 assertEqualInt(r, 0); 473 474 /* Archive it without acls */ 475 r = systemf("%s -c --no-mac-metadata --no-acls -f noacls.tar f >noacls.out 2>noacls.err", testprog); 476 assertEqualInt(r, 0); 477 478 /* Extract acls with acls */ 479 assertMakeDir("acls_acls", 0755); 480 clear_inheritance_flags("acls_acls", acltype); 481 r = systemf("%s -x -C acls_acls --no-same-permissions --acls -f acls.tar >acls_acls.out 2>acls_acls.err", testprog); 482 assertEqualInt(r, 0); 483 r = compare_acls("f", "acls_acls/f"); 484 assertEqualInt(r, 1); 485 486 /* Extract acls without acls */ 487 assertMakeDir("acls_noacls", 0755); 488 clear_inheritance_flags("acls_noacls", acltype); 489 r = systemf("%s -x -C acls_noacls -p --no-acls -f acls.tar >acls_noacls.out 2>acls_noacls.err", testprog); 490 assertEqualInt(r, 0); 491 r = compare_acls("f", "acls_noacls/f"); 492 assertEqualInt(r, 0); 493 494 /* Extract noacls with acls flag */ 495 assertMakeDir("noacls_acls", 0755); 496 clear_inheritance_flags("noacls_acls", acltype); 497 r = systemf("%s -x -C noacls_acls --no-same-permissions --acls -f noacls.tar >noacls_acls.out 2>noacls_acls.err", testprog); 498 assertEqualInt(r, 0); 499 r = compare_acls("f", "noacls_acls/f"); 500 assertEqualInt(r, 0); 501 502 /* Extract noacls with noacls */ 503 assertMakeDir("noacls_noacls", 0755); 504 clear_inheritance_flags("noacls_noacls", acltype); 505 r = systemf("%s -x -C noacls_noacls -p --no-acls -f noacls.tar >noacls_noacls.out 2>noacls_noacls.err", testprog); 506 assertEqualInt(r, 0); 507 r = compare_acls("f", "noacls_noacls/f"); 508 assertEqualInt(r, 0); 509 #endif /* ARCHIVE_ACL_SUPPORT */ 510 } 511