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