ufs_acl.c revision 184629
1/*- 2 * Copyright (c) 1999-2003 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed by Robert Watson for the TrustedBSD Project. 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 AUTHOR 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 AUTHOR 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/* 30 * Support for POSIX.1e access control lists: UFS-specific support functions. 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: head/sys/ufs/ufs/ufs_acl.c 184629 2008-11-04 12:30:31Z trasz $"); 35 36#include "opt_ufs.h" 37#include "opt_quota.h" 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/stat.h> 42#include <sys/mount.h> 43#include <sys/vnode.h> 44#include <sys/types.h> 45#include <sys/acl.h> 46#include <sys/event.h> 47#include <sys/extattr.h> 48 49#include <ufs/ufs/quota.h> 50#include <ufs/ufs/inode.h> 51#include <ufs/ufs/acl.h> 52#include <ufs/ufs/extattr.h> 53#include <ufs/ufs/dir.h> 54#include <ufs/ufs/ufsmount.h> 55#include <ufs/ufs/ufs_extern.h> 56#include <ufs/ffs/fs.h> 57 58#ifdef UFS_ACL 59 60/* 61 * Synchronize an ACL and an inode by copying over appropriate inode fields 62 * to the passed ACL. Assumes an ACL that would satisfy acl_posix1e_check(), 63 * and may panic if not. 64 */ 65void 66ufs_sync_acl_from_inode(struct inode *ip, struct acl *acl) 67{ 68 struct acl_entry *acl_mask, *acl_group_obj; 69 int i; 70 71 /* 72 * Update ACL_USER_OBJ, ACL_OTHER, but simply identify ACL_MASK 73 * and ACL_GROUP_OBJ for use after we know whether ACL_MASK is 74 * present. 75 */ 76 acl_mask = NULL; 77 acl_group_obj = NULL; 78 for (i = 0; i < acl->acl_cnt; i++) { 79 switch (acl->acl_entry[i].ae_tag) { 80 case ACL_USER_OBJ: 81 acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 82 ACL_USER_OBJ, ip->i_mode); 83 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 84 break; 85 86 case ACL_GROUP_OBJ: 87 acl_group_obj = &acl->acl_entry[i]; 88 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 89 break; 90 91 case ACL_OTHER: 92 acl->acl_entry[i].ae_perm = acl_posix1e_mode_to_perm( 93 ACL_OTHER, ip->i_mode); 94 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 95 break; 96 97 case ACL_MASK: 98 acl_mask = &acl->acl_entry[i]; 99 acl->acl_entry[i].ae_id = ACL_UNDEFINED_ID; 100 break; 101 102 case ACL_USER: 103 case ACL_GROUP: 104 break; 105 106 default: 107 panic("ufs_sync_acl_from_inode(): bad ae_tag"); 108 } 109 } 110 111 if (acl_group_obj == NULL) 112 panic("ufs_sync_acl_from_inode(): no ACL_GROUP_OBJ"); 113 114 if (acl_mask == NULL) { 115 /* 116 * There is no ACL_MASK, so update ACL_GROUP_OBJ. 117 */ 118 acl_group_obj->ae_perm = acl_posix1e_mode_to_perm( 119 ACL_GROUP_OBJ, ip->i_mode); 120 } else { 121 /* 122 * Update the ACL_MASK entry instead of ACL_GROUP_OBJ. 123 */ 124 acl_mask->ae_perm = acl_posix1e_mode_to_perm(ACL_GROUP_OBJ, 125 ip->i_mode); 126 } 127} 128 129/* 130 * Calculate what the inode mode should look like based on an authoritative 131 * ACL for the inode. Replace only the fields in the inode that the ACL 132 * can represent. 133 */ 134void 135ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip) 136{ 137 138 ip->i_mode &= ACL_PRESERVE_MASK; 139 ip->i_mode |= acl_posix1e_acl_to_mode(acl); 140 DIP_SET(ip, i_mode, ip->i_mode); 141} 142 143/* 144 * Retrieve the ACL on a file. 145 * 146 * As part of the ACL is stored in the inode, and the rest in an EA, 147 * assemble both into a final ACL product. Right now this is not done 148 * very efficiently. 149 */ 150int 151ufs_getacl(ap) 152 struct vop_getacl_args /* { 153 struct vnode *vp; 154 struct acl_type_t type; 155 struct acl *aclp; 156 struct ucred *cred; 157 struct thread *td; 158 } */ *ap; 159{ 160 struct inode *ip = VTOI(ap->a_vp); 161 int error, len; 162 163 /* 164 * XXX: If ufs_getacl() should work on file systems not supporting 165 * ACLs, remove this check. 166 */ 167 if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 168 return (EOPNOTSUPP); 169 170 /* 171 * Attempt to retrieve the ACL based on the ACL type. 172 */ 173 bzero(ap->a_aclp, sizeof(*ap->a_aclp)); 174 len = sizeof(*ap->a_aclp); 175 switch(ap->a_type) { 176 case ACL_TYPE_ACCESS: 177 /* 178 * ACL_TYPE_ACCESS ACLs may or may not be stored in the 179 * EA, as they are in fact a combination of the inode 180 * ownership/permissions and the EA contents. If the 181 * EA is present, merge the two in a temporary ACL 182 * storage, otherwise just return the inode contents. 183 */ 184 error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, 185 POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, 186 POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) ap->a_aclp, 187 ap->a_td); 188 switch (error) { 189 /* XXX: If ufs_getacl() should work on filesystems without 190 * the EA configured, add case EOPNOTSUPP here. */ 191 case ENOATTR: 192 /* 193 * Legitimately no ACL set on object, purely 194 * emulate it through the inode. These fields will 195 * be updated when the ACL is synchronized with 196 * the inode later. 197 */ 198 ap->a_aclp->acl_cnt = 3; 199 ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ; 200 ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID; 201 ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE; 202 ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ; 203 ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID; 204 ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE; 205 ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER; 206 ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID; 207 ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE; 208 ufs_sync_acl_from_inode(ip, ap->a_aclp); 209 error = 0; 210 break; 211 212 case 0: 213 if (len != sizeof(*ap->a_aclp)) { 214 /* 215 * A short (or long) read, meaning that for 216 * some reason the ACL is corrupted. Return 217 * EPERM since the object DAC protections 218 * are unsafe. 219 */ 220 printf("ufs_getacl(): Loaded invalid ACL (" 221 "%d bytes), inumber %d on %s\n", len, 222 ip->i_number, ip->i_fs->fs_fsmnt); 223 return (EPERM); 224 } 225 ufs_sync_acl_from_inode(ip, ap->a_aclp); 226 break; 227 228 default: 229 break; 230 } 231 break; 232 233 case ACL_TYPE_DEFAULT: 234 if (ap->a_vp->v_type != VDIR) { 235 error = EINVAL; 236 break; 237 } 238 error = vn_extattr_get(ap->a_vp, IO_NODELOCKED, 239 POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 240 POSIX1E_ACL_DEFAULT_EXTATTR_NAME, &len, 241 (char *) ap->a_aclp, ap->a_td); 242 /* 243 * Unlike ACL_TYPE_ACCESS, there is no relationship between 244 * the inode contents and the ACL, and it is therefore 245 * possible for the request for the ACL to fail since the 246 * ACL is undefined. In this situation, return success 247 * and an empty ACL, as required by POSIX.1e. 248 */ 249 switch (error) { 250 /* XXX: If ufs_getacl() should work on filesystems without 251 * the EA configured, add case EOPNOTSUPP here. */ 252 case ENOATTR: 253 bzero(ap->a_aclp, sizeof(*ap->a_aclp)); 254 ap->a_aclp->acl_cnt = 0; 255 error = 0; 256 break; 257 258 case 0: 259 if (len != sizeof(*ap->a_aclp)) { 260 /* 261 * A short (or long) read, meaning that for 262 * some reason the ACL is corrupted. Return 263 * EPERM since the object default DAC 264 * protections are unsafe. 265 */ 266 printf("ufs_getacl(): Loaded invalid ACL (" 267 "%d bytes), inumber %d on %s\n", len, 268 ip->i_number, ip->i_fs->fs_fsmnt); 269 return (EPERM); 270 } 271 break; 272 273 default: 274 break; 275 } 276 break; 277 278 default: 279 error = EINVAL; 280 } 281 282 return (error); 283} 284 285/* 286 * Set the ACL on a file. 287 * 288 * As part of the ACL is stored in the inode, and the rest in an EA, 289 * this is necessarily non-atomic, and has complex authorization. 290 * As ufs_setacl() includes elements of ufs_chown() and ufs_chmod(), 291 * a fair number of different access checks may be required to go ahead 292 * with the operation at all. 293 */ 294int 295ufs_setacl(ap) 296 struct vop_setacl_args /* { 297 struct vnode *vp; 298 acl_type_t type; 299 struct acl *aclp; 300 struct ucred *cred; 301 struct proc *p; 302 } */ *ap; 303{ 304 struct inode *ip = VTOI(ap->a_vp); 305 int error; 306 307 if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 308 return (EOPNOTSUPP); 309 310 /* 311 * If this is a set operation rather than a delete operation, 312 * invoke VOP_ACLCHECK() on the passed ACL to determine if it is 313 * valid for the target. This will include a check on ap->a_type. 314 */ 315 if (ap->a_aclp != NULL) { 316 /* 317 * Set operation. 318 */ 319 error = VOP_ACLCHECK(ap->a_vp, ap->a_type, ap->a_aclp, 320 ap->a_cred, ap->a_td); 321 if (error != 0) 322 return (error); 323 } else { 324 /* 325 * Delete operation. 326 * POSIX.1e allows only deletion of the default ACL on a 327 * directory (ACL_TYPE_DEFAULT). 328 */ 329 if (ap->a_type != ACL_TYPE_DEFAULT) 330 return (EINVAL); 331 if (ap->a_vp->v_type != VDIR) 332 return (ENOTDIR); 333 } 334 335 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 336 return (EROFS); 337 338 /* 339 * Authorize the ACL operation. 340 */ 341 if (ip->i_flags & (IMMUTABLE | APPEND)) 342 return (EPERM); 343 344 /* 345 * Must hold VADMIN (be file owner) or have appropriate privilege. 346 */ 347 if ((error = VOP_ACCESS(ap->a_vp, VADMIN, ap->a_cred, ap->a_td))) 348 return (error); 349 350 switch(ap->a_type) { 351 case ACL_TYPE_ACCESS: 352 error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 353 POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE, 354 POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(*ap->a_aclp), 355 (char *) ap->a_aclp, ap->a_td); 356 break; 357 358 case ACL_TYPE_DEFAULT: 359 if (ap->a_aclp == NULL) { 360 error = vn_extattr_rm(ap->a_vp, IO_NODELOCKED, 361 POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 362 POSIX1E_ACL_DEFAULT_EXTATTR_NAME, ap->a_td); 363 /* 364 * Attempting to delete a non-present default ACL 365 * will return success for portability purposes. 366 * (TRIX) 367 * 368 * XXX: Note that since we can't distinguish 369 * "that EA is not supported" from "that EA is not 370 * defined", the success case here overlaps the 371 * the ENOATTR->EOPNOTSUPP case below. 372 */ 373 if (error == ENOATTR) 374 error = 0; 375 } else 376 error = vn_extattr_set(ap->a_vp, IO_NODELOCKED, 377 POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE, 378 POSIX1E_ACL_DEFAULT_EXTATTR_NAME, 379 sizeof(*ap->a_aclp), (char *) ap->a_aclp, ap->a_td); 380 break; 381 382 default: 383 error = EINVAL; 384 } 385 /* 386 * Map lack of attribute definition in UFS_EXTATTR into lack of 387 * support for ACLs on the filesystem. 388 */ 389 if (error == ENOATTR) 390 return (EOPNOTSUPP); 391 if (error != 0) 392 return (error); 393 394 if (ap->a_type == ACL_TYPE_ACCESS) { 395 /* 396 * Now that the EA is successfully updated, update the 397 * inode and mark it as changed. 398 */ 399 ufs_sync_inode_from_acl(ap->a_aclp, ip); 400 ip->i_flag |= IN_CHANGE; 401 } 402 403 VN_KNOTE_UNLOCKED(ap->a_vp, NOTE_ATTRIB); 404 return (0); 405} 406 407/* 408 * Check the validity of an ACL for a file. 409 */ 410int 411ufs_aclcheck(ap) 412 struct vop_aclcheck_args /* { 413 struct vnode *vp; 414 acl_type_t type; 415 struct acl *aclp; 416 struct ucred *cred; 417 struct thread *td; 418 } */ *ap; 419{ 420 421 if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0) 422 return (EOPNOTSUPP); 423 424 /* 425 * Verify we understand this type of ACL, and that it applies 426 * to this kind of object. 427 * Rely on the acl_posix1e_check() routine to verify the contents. 428 */ 429 switch(ap->a_type) { 430 case ACL_TYPE_ACCESS: 431 break; 432 433 case ACL_TYPE_DEFAULT: 434 if (ap->a_vp->v_type != VDIR) 435 return (EINVAL); 436 break; 437 438 default: 439 return (EINVAL); 440 } 441 return (acl_posix1e_check(ap->a_aclp)); 442} 443 444#endif /* !UFS_ACL */ 445