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