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 #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 if (!value) 236 return (ENOMEM); 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