1 /*- 2 * Copyright (c) 1999-2006 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 * Developed by the TrustedBSD Project. 30 * 31 * ACL system calls and other functions common across different ACL types. 32 * Type-specific routines go into subr_acl_<type>.c. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/sysproto.h> 41 #include <sys/capsicum.h> 42 #include <sys/fcntl.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/mount.h> 46 #include <sys/vnode.h> 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/namei.h> 50 #include <sys/file.h> 51 #include <sys/filedesc.h> 52 #include <sys/proc.h> 53 #include <sys/sysent.h> 54 #include <sys/acl.h> 55 56 #include <security/mac/mac_framework.h> 57 58 CTASSERT(ACL_MAX_ENTRIES >= OLDACL_MAX_ENTRIES); 59 60 MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists"); 61 62 static int vacl_set_acl(struct thread *td, struct vnode *vp, 63 acl_type_t type, struct acl *aclp); 64 static int vacl_get_acl(struct thread *td, struct vnode *vp, 65 acl_type_t type, struct acl *aclp); 66 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 67 acl_type_t type, struct acl *aclp); 68 69 int 70 acl_copy_oldacl_into_acl(const struct oldacl *source, struct acl *dest) 71 { 72 int i; 73 74 if (source->acl_cnt < 0 || source->acl_cnt > OLDACL_MAX_ENTRIES) 75 return (EINVAL); 76 77 bzero(dest, sizeof(*dest)); 78 79 dest->acl_cnt = source->acl_cnt; 80 dest->acl_maxcnt = ACL_MAX_ENTRIES; 81 82 for (i = 0; i < dest->acl_cnt; i++) { 83 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 84 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 85 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 86 } 87 88 return (0); 89 } 90 91 int 92 acl_copy_acl_into_oldacl(const struct acl *source, struct oldacl *dest) 93 { 94 int i; 95 96 if (source->acl_cnt > OLDACL_MAX_ENTRIES) 97 return (EINVAL); 98 99 bzero(dest, sizeof(*dest)); 100 101 dest->acl_cnt = source->acl_cnt; 102 103 for (i = 0; i < dest->acl_cnt; i++) { 104 dest->acl_entry[i].ae_tag = source->acl_entry[i].ae_tag; 105 dest->acl_entry[i].ae_id = source->acl_entry[i].ae_id; 106 dest->acl_entry[i].ae_perm = source->acl_entry[i].ae_perm; 107 } 108 109 return (0); 110 } 111 112 /* 113 * At one time, "struct ACL" was extended in order to add support for NFSv4 114 * ACLs. Instead of creating compatibility versions of all the ACL-related 115 * syscalls, they were left intact. It's possible to find out what the code 116 * calling these syscalls (libc) expects basing on "type" argument - if it's 117 * either ACL_TYPE_ACCESS_OLD or ACL_TYPE_DEFAULT_OLD (which previously were 118 * known as ACL_TYPE_ACCESS and ACL_TYPE_DEFAULT), then it's the "struct 119 * oldacl". If it's something else, then it's the new "struct acl". In the 120 * latter case, the routines below just copyin/copyout the contents. In the 121 * former case, they copyin the "struct oldacl" and convert it to the new 122 * format. 123 */ 124 static int 125 acl_copyin(void *user_acl, struct acl *kernel_acl, acl_type_t type) 126 { 127 int error; 128 struct oldacl old; 129 130 switch (type) { 131 case ACL_TYPE_ACCESS_OLD: 132 case ACL_TYPE_DEFAULT_OLD: 133 error = copyin(user_acl, &old, sizeof(old)); 134 if (error != 0) 135 break; 136 acl_copy_oldacl_into_acl(&old, kernel_acl); 137 break; 138 139 default: 140 error = copyin(user_acl, kernel_acl, sizeof(*kernel_acl)); 141 if (kernel_acl->acl_maxcnt != ACL_MAX_ENTRIES) 142 return (EINVAL); 143 } 144 145 return (error); 146 } 147 148 static int 149 acl_copyout(struct acl *kernel_acl, void *user_acl, acl_type_t type) 150 { 151 uint32_t am; 152 int error; 153 struct oldacl old; 154 155 switch (type) { 156 case ACL_TYPE_ACCESS_OLD: 157 case ACL_TYPE_DEFAULT_OLD: 158 error = acl_copy_acl_into_oldacl(kernel_acl, &old); 159 if (error != 0) 160 break; 161 162 error = copyout(&old, user_acl, sizeof(old)); 163 break; 164 165 default: 166 error = fueword32((char *)user_acl + 167 offsetof(struct acl, acl_maxcnt), &am); 168 if (error == -1) 169 return (EFAULT); 170 if (am != ACL_MAX_ENTRIES) 171 return (EINVAL); 172 173 error = copyout(kernel_acl, user_acl, sizeof(*kernel_acl)); 174 } 175 176 return (error); 177 } 178 179 /* 180 * Convert "old" type - ACL_TYPE_{ACCESS,DEFAULT}_OLD - into its "new" 181 * counterpart. It's required for old (pre-NFSv4 ACLs) libc to work 182 * with new kernel. Fixing 'type' for old binaries with new libc 183 * is being done in lib/libc/posix1e/acl_support.c:_acl_type_unold(). 184 */ 185 static int 186 acl_type_unold(int type) 187 { 188 switch (type) { 189 case ACL_TYPE_ACCESS_OLD: 190 return (ACL_TYPE_ACCESS); 191 192 case ACL_TYPE_DEFAULT_OLD: 193 return (ACL_TYPE_DEFAULT); 194 195 default: 196 return (type); 197 } 198 } 199 200 /* 201 * These calls wrap the real vnode operations, and are called by the syscall 202 * code once the syscall has converted the path or file descriptor to a vnode 203 * (unlocked). The aclp pointer is assumed still to point to userland, so 204 * this should not be consumed within the kernel except by syscall code. 205 * Other code should directly invoke VOP_{SET,GET}ACL. 206 */ 207 208 /* 209 * Given a vnode, set its ACL. 210 */ 211 static int 212 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 213 struct acl *aclp) 214 { 215 struct acl *inkernelacl; 216 struct mount *mp; 217 int error; 218 219 inkernelacl = acl_alloc(M_WAITOK); 220 error = acl_copyin(aclp, inkernelacl, type); 221 if (error != 0) 222 goto out; 223 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 224 if (error != 0) 225 goto out; 226 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 227 #ifdef MAC 228 error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl); 229 if (error != 0) 230 goto out_unlock; 231 #endif 232 error = VOP_SETACL(vp, acl_type_unold(type), inkernelacl, 233 td->td_ucred, td); 234 #ifdef MAC 235 out_unlock: 236 #endif 237 VOP_UNLOCK(vp, 0); 238 vn_finished_write(mp); 239 out: 240 acl_free(inkernelacl); 241 return (error); 242 } 243 244 /* 245 * Given a vnode, get its ACL. 246 */ 247 static int 248 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 249 struct acl *aclp) 250 { 251 struct acl *inkernelacl; 252 int error; 253 254 inkernelacl = acl_alloc(M_WAITOK | M_ZERO); 255 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 256 #ifdef MAC 257 error = mac_vnode_check_getacl(td->td_ucred, vp, type); 258 if (error != 0) 259 goto out; 260 #endif 261 error = VOP_GETACL(vp, acl_type_unold(type), inkernelacl, 262 td->td_ucred, td); 263 264 #ifdef MAC 265 out: 266 #endif 267 VOP_UNLOCK(vp, 0); 268 if (error == 0) 269 error = acl_copyout(inkernelacl, aclp, type); 270 acl_free(inkernelacl); 271 return (error); 272 } 273 274 /* 275 * Given a vnode, delete its ACL. 276 */ 277 static int 278 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 279 { 280 struct mount *mp; 281 int error; 282 283 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 284 if (error != 0) 285 return (error); 286 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 287 #ifdef MAC 288 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); 289 if (error != 0) 290 goto out; 291 #endif 292 error = VOP_SETACL(vp, acl_type_unold(type), 0, td->td_ucred, td); 293 #ifdef MAC 294 out: 295 #endif 296 VOP_UNLOCK(vp, 0); 297 vn_finished_write(mp); 298 return (error); 299 } 300 301 /* 302 * Given a vnode, check whether an ACL is appropriate for it 303 */ 304 static int 305 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 306 struct acl *aclp) 307 { 308 struct acl *inkernelacl; 309 int error; 310 311 inkernelacl = acl_alloc(M_WAITOK); 312 error = acl_copyin(aclp, inkernelacl, type); 313 if (error != 0) 314 goto out; 315 error = VOP_ACLCHECK(vp, acl_type_unold(type), inkernelacl, 316 td->td_ucred, td); 317 out: 318 acl_free(inkernelacl); 319 return (error); 320 } 321 322 /* 323 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 324 * need to lock, as the vacl_ code will get/release any locks required. 325 */ 326 327 /* 328 * Given a file path, get an ACL for it 329 */ 330 int 331 sys___acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 332 { 333 struct nameidata nd; 334 int error; 335 336 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 337 error = namei(&nd); 338 if (error == 0) { 339 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 340 NDFREE(&nd, 0); 341 } 342 return (error); 343 } 344 345 /* 346 * Given a file path, get an ACL for it; don't follow links. 347 */ 348 int 349 sys___acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 350 { 351 struct nameidata nd; 352 int error; 353 354 NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); 355 error = namei(&nd); 356 if (error == 0) { 357 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 358 NDFREE(&nd, 0); 359 } 360 return (error); 361 } 362 363 /* 364 * Given a file path, set an ACL for it. 365 */ 366 int 367 sys___acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 368 { 369 struct nameidata nd; 370 int error; 371 372 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 373 error = namei(&nd); 374 if (error == 0) { 375 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 376 NDFREE(&nd, 0); 377 } 378 return (error); 379 } 380 381 /* 382 * Given a file path, set an ACL for it; don't follow links. 383 */ 384 int 385 sys___acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 386 { 387 struct nameidata nd; 388 int error; 389 390 NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); 391 error = namei(&nd); 392 if (error == 0) { 393 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 394 NDFREE(&nd, 0); 395 } 396 return (error); 397 } 398 399 /* 400 * Given a file descriptor, get an ACL for it. 401 */ 402 int 403 sys___acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 404 { 405 struct file *fp; 406 cap_rights_t rights; 407 int error; 408 409 error = getvnode(td, uap->filedes, 410 cap_rights_init(&rights, CAP_ACL_GET), &fp); 411 if (error == 0) { 412 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 413 fdrop(fp, td); 414 } 415 return (error); 416 } 417 418 /* 419 * Given a file descriptor, set an ACL for it. 420 */ 421 int 422 sys___acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 423 { 424 struct file *fp; 425 cap_rights_t rights; 426 int error; 427 428 error = getvnode(td, uap->filedes, 429 cap_rights_init(&rights, CAP_ACL_SET), &fp); 430 if (error == 0) { 431 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 432 fdrop(fp, td); 433 } 434 return (error); 435 } 436 437 /* 438 * Given a file path, delete an ACL from it. 439 */ 440 int 441 sys___acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 442 { 443 struct nameidata nd; 444 int error; 445 446 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 447 error = namei(&nd); 448 if (error == 0) { 449 error = vacl_delete(td, nd.ni_vp, uap->type); 450 NDFREE(&nd, 0); 451 } 452 return (error); 453 } 454 455 /* 456 * Given a file path, delete an ACL from it; don't follow links. 457 */ 458 int 459 sys___acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 460 { 461 struct nameidata nd; 462 int error; 463 464 NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); 465 error = namei(&nd); 466 if (error == 0) { 467 error = vacl_delete(td, nd.ni_vp, uap->type); 468 NDFREE(&nd, 0); 469 } 470 return (error); 471 } 472 473 /* 474 * Given a file path, delete an ACL from it. 475 */ 476 int 477 sys___acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 478 { 479 struct file *fp; 480 cap_rights_t rights; 481 int error; 482 483 error = getvnode(td, uap->filedes, 484 cap_rights_init(&rights, CAP_ACL_DELETE), &fp); 485 if (error == 0) { 486 error = vacl_delete(td, fp->f_vnode, uap->type); 487 fdrop(fp, td); 488 } 489 return (error); 490 } 491 492 /* 493 * Given a file path, check an ACL for it. 494 */ 495 int 496 sys___acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 497 { 498 struct nameidata nd; 499 int error; 500 501 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, uap->path, td); 502 error = namei(&nd); 503 if (error == 0) { 504 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 505 NDFREE(&nd, 0); 506 } 507 return (error); 508 } 509 510 /* 511 * Given a file path, check an ACL for it; don't follow links. 512 */ 513 int 514 sys___acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 515 { 516 struct nameidata nd; 517 int error; 518 519 NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, uap->path, td); 520 error = namei(&nd); 521 if (error == 0) { 522 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 523 NDFREE(&nd, 0); 524 } 525 return (error); 526 } 527 528 /* 529 * Given a file descriptor, check an ACL for it. 530 */ 531 int 532 sys___acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 533 { 534 struct file *fp; 535 cap_rights_t rights; 536 int error; 537 538 error = getvnode(td, uap->filedes, 539 cap_rights_init(&rights, CAP_ACL_CHECK), &fp); 540 if (error == 0) { 541 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 542 fdrop(fp, td); 543 } 544 return (error); 545 } 546 547 struct acl * 548 acl_alloc(int flags) 549 { 550 struct acl *aclp; 551 552 aclp = malloc(sizeof(*aclp), M_ACL, flags); 553 if (aclp == NULL) 554 return (NULL); 555 556 aclp->acl_maxcnt = ACL_MAX_ENTRIES; 557 558 return (aclp); 559 } 560 561 void 562 acl_free(struct acl *aclp) 563 { 564 565 free(aclp, M_ACL); 566 } 567