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; 131 int n, count, s; 132 133 if (value == NULL) 134 return (EINVAL); 135 136 end = value + size; 137 138 if (((struct ext2_acl_header *)value)->a_version != EXT4_ACL_VERSION) 139 return (EINVAL); 140 141 if (size < sizeof(struct ext2_acl_header)) 142 return (EINVAL); 143 144 s = size - sizeof(struct ext2_acl_header); 145 s -= 4 * sizeof(struct ext2_acl_entry_short); 146 if (s < 0) 147 if ((size - sizeof(struct ext2_acl_header)) % 148 sizeof(struct ext2_acl_entry_short)) 149 count = -1; 150 else 151 count = (size - sizeof(struct ext2_acl_header)) / 152 sizeof(struct ext2_acl_entry_short); 153 else 154 if (s % sizeof(struct ext2_acl_entry)) 155 count = -1; 156 else 157 count = s / sizeof(struct ext2_acl_entry) + 4; 158 159 if (count <= 0 || count > acl->acl_maxcnt) 160 return (EINVAL); 161 162 value = value + sizeof(struct ext2_acl_header); 163 164 for (n = 0; n < count; n++) { 165 struct ext2_acl_entry *entry = (struct ext2_acl_entry *)value; 166 if ((char *)value + sizeof(struct ext2_acl_entry_short) > end) 167 return (EINVAL); 168 169 acl->acl_entry[n].ae_tag = entry->ae_tag; 170 acl->acl_entry[n].ae_perm = entry->ae_perm; 171 172 switch (acl->acl_entry[n].ae_tag) { 173 case ACL_USER_OBJ: 174 case ACL_GROUP_OBJ: 175 case ACL_MASK: 176 case ACL_OTHER: 177 value = (char *)value + sizeof(struct ext2_acl_entry_short); 178 break; 179 180 case ACL_USER: 181 value = (char *)value + sizeof(struct ext2_acl_entry); 182 if ((char *)value > end) 183 return (EINVAL); 184 185 acl->acl_entry[n].ae_id = entry->ae_id; 186 break; 187 188 case ACL_GROUP: 189 value = (char *)value + sizeof(struct ext2_acl_entry); 190 if ((char *)value > end) 191 return (EINVAL); 192 193 acl->acl_entry[n].ae_id = entry->ae_id; 194 break; 195 196 default: 197 return (EINVAL); 198 } 199 } 200 201 if (value != end) 202 return (EINVAL); 203 204 acl->acl_cnt = count; 205 206 return (0); 207 } 208 209 static int 210 ext2_getacl_posix1e(struct vop_getacl_args *ap) 211 { 212 int attrnamespace; 213 const char *attrname; 214 char *value; 215 int len; 216 int error; 217 218 switch (ap->a_type) { 219 case ACL_TYPE_DEFAULT: 220 attrnamespace = POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE; 221 attrname = POSIX1E_ACL_DEFAULT_EXTATTR_NAME; 222 break; 223 case ACL_TYPE_ACCESS: 224 attrnamespace = POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE; 225 attrname = POSIX1E_ACL_ACCESS_EXTATTR_NAME; 226 break; 227 default: 228 return (EINVAL); 229 } 230 231 len = sizeof(*ap->a_aclp) + sizeof(struct ext2_acl_header); 232 value = malloc(len, M_ACL, M_WAITOK); 233 if (!value) 234 return (ENOMEM); 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