1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2017, Fedor Uporov 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 #include <sys/vnode.h> 36 #include <sys/bio.h> 37 #include <sys/buf.h> 38 #include <sys/endian.h> 39 #include <sys/conf.h> 40 #include <sys/mount.h> 41 #include <sys/extattr.h> 42 43 #include <fs/ext2fs/fs.h> 44 #include <fs/ext2fs/ext2fs.h> 45 #include <fs/ext2fs/inode.h> 46 #include <fs/ext2fs/ext2_acl.h> 47 #include <fs/ext2fs/ext2_extattr.h> 48 #include <fs/ext2fs/ext2_extern.h> 49 #include <fs/ext2fs/ext2_dinode.h> 50 #include <fs/ext2fs/ext2_mount.h> 51 52 #ifdef UFS_ACL 53 54 void 55 ext2_sync_acl_from_inode(struct inode *ip, struct acl *acl) 56 { 57 struct acl_entry *acl_mask, *acl_group_obj; 58 int i; 59 60 /* 61 * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK 62 * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is 63 * present. 64 */ 65 acl_mask = NULL; 66 acl_group_obj = NULL; 67 for (i = 0; i < acl->acl_cnt; i++) { 68 switch (acl->acl_entry[i].ae_tag) { 69 case ACL_USER_OBJ: 70 acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 71 ACL_USER_OBJ, ip->i_mode); 72 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 73 break; 74 75 case ACL_GROUP_OBJ: 76 acl_group_obj = &acl->acl_entry[i]; 77 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 78 break; 79 80 case ACL_OTHER: 81 acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 82 ACL_OTHER, ip->i_mode); 83 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 84 break; 85 86 case ACL_MASK: 87 acl_mask = &acl->acl_entry[i]; 88 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 89 break; 90 91 case ACL_USER: 92 case ACL_GROUP: 93 break; 94 95 default: 96 panic("ext2_sync_acl_from_inode(): bad ae_tag"); 97 } 98 } 99 100 if (acl_group_obj == NULL) 101 panic("ext2_sync_acl_from_inode(): no ACL_GROUP_OBJ"); 102 103 if (acl_mask == NULL) { 104 /* 105 * There is no ACL_MASK, so update ACL_GROUP_OBJ. 106 */ 107 acl_group_obj->ae_perm = acl_posix1e_mode_to_perm( 108 ACL_GROUP_OBJ, ip->i_mode); 109 } else { 110 /* 111 * Update the ACL_MASK entry instead of ACL_GROUP_OBJ. 112 */ 113 acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ, 114 ip->i_mode); 115 } 116 } 117 118 static void 119 ext2_sync_inode_from_acl(struct acl *acl, struct inode *ip) 120 { 121 122 ip->i_mode &= ACL_PRESERVE_MASK; 123 ip->i_mode |= acl_posix1e_acl_to_mode(acl); 124 } 125 126 /* 127 * Convert from filesystem to in-memory representation. 128 */ 129 static int 130 ext4_acl_from_disk(char *value, size_t size, struct acl *acl) 131 { 132 const char *end; 133 int n, count, s; 134 135 if (value == NULL) 136 return (EINVAL); 137 138 end = value + size; 139 140 if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION) 141 return (EINVAL); 142 143 if (size < sizeof(struct ext2_acl_header)) 144 return (EINVAL); 145 146 s = size - sizeof(struct ext2_acl_header); 147 s -= 4 * sizeof(struct ext2_acl_entry_short); 148 if (s < 0) 149 if ((size - sizeof(struct ext2_acl_header)) % 150 sizeof(struct ext2_acl_entry_short)) 151 count = -1; 152 else 153 count = (size - sizeof(struct ext2_acl_header)) / 154 sizeof(struct ext2_acl_entry_short); 155 else 156 if (s % sizeof(struct ext2_acl_entry)) 157 count = -1; 158 else 159 count = s / sizeof(struct ext2_acl_entry) + 4; 160 161 if (count <= 0 || count > acl->acl_maxcnt) 162 return (EINVAL); 163 164 value = value + sizeof(struct ext2_acl_header); 165 166 for (n = 0; n < count; n++) { 167 struct ext2_acl_entry *entry = (struct ext2_acl_entry *)value; 168 if ((char *)value + sizeof(struct ext2_acl_entry_short) > end) 169 return (EINVAL); 170 171 acl->acl_entry[n].ae_tag = entry->ae_tag; 172 acl->acl_entry[n].ae_perm = entry->ae_perm; 173 174 switch (acl->acl_entry[n].ae_tag) { 175 case ACL_USER_OBJ: 176 case ACL_GROUP_OBJ: 177 case ACL_MASK: 178 case ACL_OTHER: 179 value = (char *)value + sizeof(struct ext2_acl_entry_short); 180 break; 181 182 case ACL_USER: 183 value = (char *)value + sizeof(struct ext2_acl_entry); 184 if ((char *)value > end) 185 return (EINVAL); 186 187 acl->acl_entry[n].ae_id = entry->ae_id; 188 break; 189 190 case ACL_GROUP: 191 value = (char *)value + sizeof(struct ext2_acl_entry); 192 if ((char *)value > end) 193 return (EINVAL); 194 195 acl->acl_entry[n].ae_id = entry->ae_id; 196 break; 197 198 default: 199 return (EINVAL); 200 } 201 } 202 203 if (value != end) 204 return (EINVAL); 205 206 acl->acl_cnt = count; 207 208 return (0); 209 } 210 211 static int 212 ext2_getacl_posix1e(struct vop_getacl_args *ap) 213 { 214 int attrnamespace; 215 const char *attrname; 216 char *value; 217 int len; 218 int error; 219 220 switch (ap->a_type) { 221 case ACL_TYPE_DEFAULT: 222 attrnamespace = POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE; 223 attrname = POSIX1E_ACL_DEFAULT_EXTATTR_NAME; 224 break; 225 case ACL_TYPE_ACCESS: 226 attrnamespace = POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE; 227 attrname = POSIX1E_ACL_ACCESS_EXTATTR_NAME; 228 break; 229 default: 230 return (EINVAL); 231 } 232 233 len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 234 value = malloc(len, M_ACL, M_WAITOK); 235 236 error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, attrnamespace, attrname, 237 &len, value, ap->a_td); 238 if (error == ENOATTR) { 239 switch (ap->a_type) { 240 case ACL_TYPE_ACCESS: 241 ap->a_aclp->acl_cnt = 3; 242 ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ; 243 ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID; 244 ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE; 245 ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ; 246 ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID; 247 ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE; 248 ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER; 249 ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID; 250 ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE; 251 break; 252 253 case ACL_TYPE_DEFAULT: 254 ap->a_aclp->acl_cnt = 0; 255 break; 256 } 257 } else if (error != 0) 258 goto out; 259 260 if (!error) { 261 error = ext4_acl_from_disk(value, len, ap->a_aclp); 262 if (error) 263 goto out; 264 } 265 266 if (error == ENOATTR) 267 error = 0; 268 269 if (ap->a_type == ACL_TYPE_ACCESS) 270 ext2_sync_acl_from_inode(VTOI(ap->a_vp), ap->a_aclp); 271 272 out: 273 free(value, M_TEMP); 274 return (error); 275 } 276 277 int 278 ext2_getacl(struct vop_getacl_args *ap) 279 { 280 281 if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || 282 ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)) 283 return (EOPNOTSUPP); 284 285 if (ap->a_type == ACL_TYPE_NFS4) 286 return (ENOTSUP); 287 288 return (ext2_getacl_posix1e(ap)); 289 } 290 291 /* 292 * Convert from in-memory to filesystem representation. 293 */ 294 static int 295 ext4_acl_to_disk(const struct acl *acl, size_t *size, char *value) 296 { 297 struct ext2_acl_header *ext_acl; 298 int disk_size; 299 char *e; 300 size_t n; 301 302 if (acl->acl_cnt <= 4) 303 disk_size = sizeof(struct ext2_acl_header) + 304 acl->acl_cnt * sizeof(struct ext2_acl_entry_short); 305 else 306 disk_size = sizeof(struct ext2_acl_header) + 307 4 * sizeof(struct ext2_acl_entry_short) + 308 (acl->acl_cnt - 4) * sizeof(struct ext2_acl_entry); 309 310 if (disk_size > *size) 311 return (EINVAL); 312 313 *size = disk_size; 314 ext_acl = (struct ext2_acl_header *)value; 315 316 ext_acl->a_version = EXT4_ACL_VERSION; 317 e = (char *)ext_acl + sizeof(struct ext2_acl_header); 318 for (n = 0; n < acl->acl_cnt; n++) { 319 const struct acl_entry *acl_e = &acl->acl_entry[n]; 320 struct ext2_acl_entry *entry = (struct ext2_acl_entry *)e; 321 entry->ae_tag = acl_e->ae_tag; 322 entry->ae_perm = acl_e->ae_perm; 323 switch (acl_e->ae_tag) { 324 case ACL_USER: 325 entry->ae_id = acl_e->ae_id; 326 e += sizeof(struct ext2_acl_entry); 327 break; 328 329 case ACL_GROUP: 330 entry->ae_id = acl_e->ae_id; 331 e += sizeof(struct ext2_acl_entry); 332 break; 333 334 case ACL_USER_OBJ: 335 case ACL_GROUP_OBJ: 336 case ACL_MASK: 337 case ACL_OTHER: 338 e += sizeof(struct ext2_acl_entry_short); 339 break; 340 341 default: 342 return (EINVAL); 343 } 344 } 345 346 return (0); 347 } 348 349 static int 350 ext2_setacl_posix1e(struct vop_setacl_args *ap) 351 { 352 struct inode *ip = VTOI(ap->a_vp); 353 char *value; 354 size_t len; 355 int error; 356 357 if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 358 return (EINVAL); 359 360 /* 361 * If this is a set operation rather than a delete operation, 362 * invoke VOP_ACLCHECK() on the passed ACL to determine if it is 363 * valid for the target. This will include a check on ap->a_type. 364 */ 365 if (ap->a_aclp != NULL) { 366 /* 367 * Set operation. 368 */ 369 error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, 370 ap->a_cred, ap->a_td); 371 if (error) 372 return (error); 373 } else { 374 /* 375 * Delete operation. 376 * POSIX.1e allows only deletion of the default ACL on a 377 * directory (ACL_TYPE_DEFAULT). 378 */ 379 if (ap->a_type != ACL_TYPE_DEFAULT) 380 return (EINVAL); 381 if (ap->a_vp->v_type != VDIR) 382 return (ENOTDIR); 383 } 384 385 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 386 return (EROFS); 387 388 /* 389 * Authorize the ACL operation. 390 */ 391 if (ip->i_flags & (IMMUTABLE | APPEND)) 392 return (EPERM); 393 394 /* 395 * Must hold VADMIN (be file owner) or have appropriate privilege. 396 */ 397 if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td))) 398 return (error); 399 400 switch (ap->a_type) { 401 case ACL_TYPE_ACCESS: 402 len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 403 value = malloc(len, M_ACL, M_WAITOK | M_ZERO); 404 error = ext4_acl_to_disk(ap->a_aclp, &len, value); 405 if (error == 0) 406 error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 407 POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, 408 POSIX1E_ACL_ACCESS_EXTATTR_NAME, len, 409 value, ap->a_td); 410 411 free(value, M_ACL); 412 break; 413 414 case ACL_TYPE_DEFAULT: 415 if (ap->a_aclp == NULL) { 416 error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, 417 POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 418 POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td); 419 420 /* 421 * Attempting to delete a non-present default ACL 422 * will return success for portability purposes. 423 * (TRIX) 424 * 425 * XXX: Note that since we can't distinguish 426 * "that EA is not supported" from "that EA is not 427 * defined", the success case here overlaps the 428 * the ENOATTR->EOPNOTSUPP case below. 429 */ 430 if (error == ENOATTR) 431 error = 0; 432 } else { 433 len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 434 value = malloc(len, M_ACL, M_WAITOK | M_ZERO); 435 error = ext4_acl_to_disk(ap->a_aclp, &len, value); 436 if (error == 0) 437 error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 438 POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 439 POSIX1E_ACL_DEFAULT_EXTATTR_NAME, len, 440 value, ap->a_td); 441 442 free(value, M_ACL); 443 } 444 break; 445 446 default: 447 error = EINVAL; 448 } 449 450 /* 451 * Map lack of attribute definition in UFS_EXTATTR into lack of 452 * support for ACLs on the filesystem. 453 */ 454 if (error == ENOATTR) 455 return (EOPNOTSUPP); 456 457 if (error != 0) 458 return (error); 459 460 if (ap->a_type == ACL_TYPE_ACCESS) { 461 /* 462 * Now that the EA is successfully updated, update the 463 * inode and mark it as changed. 464 */ 465 ext2_sync_inode_from_acl(ap->a_aclp, ip); 466 ip->i_flag |= IN_CHANGE; 467 error = ext2_update(ip->i_vnode, 1); 468 } 469 470 VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); 471 472 return (error); 473 } 474 475 int 476 ext2_setacl(struct vop_setacl_args *ap) 477 { 478 if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || 479 ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)) 480 return (EOPNOTSUPP); 481 482 if (ap->a_type == ACL_TYPE_NFS4) 483 return (ENOTSUP); 484 485 return (ext2_setacl_posix1e(ap)); 486 } 487 488 /* 489 * Check the validity of an ACL for a file. 490 */ 491 int 492 ext2_aclcheck(struct vop_aclcheck_args *ap) 493 { 494 495 if (((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) || 496 ((ap->a_vp->v_mount->mnt_flag & MNT_NFS4ACLS) != 0)) 497 return (EOPNOTSUPP); 498 499 if (ap->a_type == ACL_TYPE_NFS4) 500 return (ENOTSUP); 501 502 if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 503 return (EINVAL); 504 505 /* 506 * Verify we understand this type of ACL, and that it applies 507 * to this kind of object. 508 * Rely on the acl_posix1e_check() routine to verify the contents. 509 */ 510 switch (ap->a_type) { 511 case ACL_TYPE_ACCESS: 512 break; 513 514 case ACL_TYPE_DEFAULT: 515 if (ap->a_vp->v_type != VDIR) 516 return (EINVAL); 517 break; 518 519 default: 520 return (EINVAL); 521 } 522 523 return (acl_posix1e_check(ap->a_aclp)); 524 } 525 526 #endif /* UFS_ACL */ 527