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