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