1 /*- 2 * Copyright (c) 1999-2002, 2006, 2009 Robert N. M. Watson 3 * Copyright (c) 2001 Ilmar S. Habibulin 4 * Copyright (c) 2001-2005 Networks Associates Technology, Inc. 5 * Copyright (c) 2005-2006 SPARTA, Inc. 6 * Copyright (c) 2008 Apple Inc. 7 * All rights reserved. 8 * 9 * This software was developed by Robert Watson and Ilmar Habibulin for the 10 * TrustedBSD Project. 11 * 12 * This software was developed for the FreeBSD Project in part by Network 13 * Associates Laboratories, the Security Research Division of Network 14 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 15 * as part of the DARPA CHATS research program. 16 * 17 * This software was enhanced by SPARTA ISSO under SPAWAR contract 18 * N66001-04-C-6019 ("SEFOS"). 19 * 20 * This software was developed at the University of Cambridge Computer 21 * Laboratory with support from a grant from Google, Inc. 22 * 23 * Redistribution and use in source and binary forms, with or without 24 * modification, are permitted provided that the following conditions 25 * are met: 26 * 1. Redistributions of source code must retain the above copyright 27 * notice, this list of conditions and the following disclaimer. 28 * 2. Redistributions in binary form must reproduce the above copyright 29 * notice, this list of conditions and the following disclaimer in the 30 * documentation and/or other materials provided with the distribution. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 */ 44 45 #include <sys/cdefs.h> 46 #include "opt_mac.h" 47 48 #include <sys/param.h> 49 #include <sys/abi_compat.h> 50 #include <sys/capsicum.h> 51 #include <sys/fcntl.h> 52 #include <sys/kernel.h> 53 #include <sys/lock.h> 54 #include <sys/malloc.h> 55 #include <sys/mutex.h> 56 #include <sys/mac.h> 57 #include <sys/proc.h> 58 #include <sys/systm.h> 59 #include <sys/sysctl.h> 60 #include <sys/sysent.h> 61 #include <sys/sysproto.h> 62 #include <sys/vnode.h> 63 #include <sys/mount.h> 64 #include <sys/file.h> 65 #include <sys/namei.h> 66 #include <sys/socket.h> 67 #include <sys/pipe.h> 68 #include <sys/socketvar.h> 69 70 #include <security/mac/mac_framework.h> 71 #include <security/mac/mac_internal.h> 72 #include <security/mac/mac_policy.h> 73 #include <security/mac/mac_syscalls.h> 74 75 #ifdef MAC 76 77 FEATURE(security_mac, "Mandatory Access Control Framework support"); 78 79 static int kern___mac_get_path(struct thread *td, const char *path_p, 80 struct mac *mac_p, int follow); 81 static int kern___mac_set_path(struct thread *td, const char *path_p, 82 struct mac *mac_p, int follow); 83 84 #ifdef COMPAT_FREEBSD32 85 struct mac32 { 86 uint32_t m_buflen; /* size_t */ 87 uint32_t m_string; /* char * */ 88 }; 89 #endif 90 91 /* 92 * Copyin a 'struct mac', including the string pointed to by 'm_string'. 93 * 94 * On success (0 returned), fills '*mac', whose associated storage must be freed 95 * after use by calling free_copied_label() (which see). On success, 'u_string' 96 * if not NULL is filled with the userspace address for 'u_mac->m_string'. 97 */ 98 int 99 mac_label_copyin(const void *const u_mac, struct mac *const mac, 100 char **const u_string) 101 { 102 char *buffer; 103 int error; 104 105 #ifdef COMPAT_FREEBSD32 106 if (SV_CURPROC_FLAG(SV_ILP32)) { 107 struct mac32 mac32; 108 109 error = copyin(u_mac, &mac32, sizeof(mac32)); 110 if (error != 0) 111 return (error); 112 113 CP(mac32, *mac, m_buflen); 114 PTRIN_CP(mac32, *mac, m_string); 115 } else 116 #endif 117 { 118 error = copyin(u_mac, mac, sizeof(*mac)); 119 if (error != 0) 120 return (error); 121 } 122 123 error = mac_check_structmac_consistent(mac); 124 if (error != 0) 125 return (error); 126 127 /* 'm_buflen' not too big checked by function call above. */ 128 buffer = malloc(mac->m_buflen, M_MACTEMP, M_WAITOK); 129 error = copyinstr(mac->m_string, buffer, mac->m_buflen, NULL); 130 if (error != 0) { 131 free(buffer, M_MACTEMP); 132 return (error); 133 } 134 135 MPASS(error == 0); 136 if (u_string != NULL) 137 *u_string = mac->m_string; 138 mac->m_string = buffer; 139 return (0); 140 } 141 142 void 143 free_copied_label(const struct mac *const mac) 144 { 145 free(mac->m_string, M_MACTEMP); 146 } 147 148 int 149 sys___mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) 150 { 151 char *buffer, *u_buffer; 152 struct mac mac; 153 struct proc *tproc; 154 struct ucred *tcred; 155 int error; 156 157 error = mac_label_copyin(uap->mac_p, &mac, &u_buffer); 158 if (error) 159 return (error); 160 161 tproc = pfind(uap->pid); 162 if (tproc == NULL) { 163 error = ESRCH; 164 goto free_mac_and_exit; 165 } 166 167 tcred = NULL; /* Satisfy gcc. */ 168 error = p_cansee(td, tproc); 169 if (error == 0) 170 tcred = crhold(tproc->p_ucred); 171 PROC_UNLOCK(tproc); 172 if (error) 173 goto free_mac_and_exit; 174 175 buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); 176 error = mac_cred_externalize_label(tcred->cr_label, mac.m_string, 177 buffer, mac.m_buflen); 178 if (error == 0) 179 error = copyout(buffer, u_buffer, strlen(buffer)+1); 180 free(buffer, M_MACTEMP); 181 crfree(tcred); 182 183 free_mac_and_exit: 184 free_copied_label(&mac); 185 return (error); 186 } 187 188 int 189 sys___mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) 190 { 191 char *buffer, *u_buffer; 192 struct mac mac; 193 int error; 194 195 error = mac_label_copyin(uap->mac_p, &mac, &u_buffer); 196 if (error) 197 return (error); 198 199 buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); 200 error = mac_cred_externalize_label(td->td_ucred->cr_label, 201 mac.m_string, buffer, mac.m_buflen); 202 if (error == 0) 203 error = copyout(buffer, u_buffer, strlen(buffer)+1); 204 205 free(buffer, M_MACTEMP); 206 free_copied_label(&mac); 207 return (error); 208 } 209 210 /* 211 * Performs preparation (including allocations) for mac_set_proc(). 212 * 213 * No lock should be held while calling this function. On success, 214 * mac_set_proc_finish() must be called to free the data associated to 215 * 'mac_set_proc_data', even if mac_set_proc_core() fails. 'mac_set_proc_data' 216 * is not set in case of error, and is set to a non-NULL value on success. 217 */ 218 int 219 mac_set_proc_prepare(struct thread *const td, const struct mac *const mac, 220 void **const mac_set_proc_data) 221 { 222 struct label *intlabel; 223 int error; 224 225 PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED); 226 227 if (!(mac_labeled & MPC_OBJECT_CRED)) 228 return (EINVAL); 229 230 intlabel = mac_cred_label_alloc(); 231 error = mac_cred_internalize_label(intlabel, mac->m_string); 232 if (error) { 233 mac_cred_label_free(intlabel); 234 return (error); 235 } 236 237 *mac_set_proc_data = intlabel; 238 return (0); 239 } 240 241 /* 242 * Actually sets the MAC label on 'newcred'. 243 * 244 * The current process' lock *must* be held. This function only sets the label 245 * on 'newcred', but does not put 'newcred' in place on the current process' 246 * (consequently, it also does not call setsugid()). 'mac_set_proc_data' must 247 * be the pointer returned by mac_set_proc_prepare(). If called, this function 248 * must be so between a successful call to mac_set_proc_prepare() and 249 * mac_set_proc_finish(), but calling it is not mandatory (e.g., if some other 250 * error occured under the process lock that obsoletes setting the MAC label). 251 */ 252 int 253 mac_set_proc_core(struct thread *const td, struct ucred *const newcred, 254 void *const mac_set_proc_data) 255 { 256 struct label *const intlabel = mac_set_proc_data; 257 struct proc *const p = td->td_proc; 258 int error; 259 260 MPASS(td == curthread); 261 PROC_LOCK_ASSERT(p, MA_OWNED); 262 263 error = mac_cred_check_relabel(p->p_ucred, intlabel); 264 if (error) 265 return (error); 266 267 mac_cred_relabel(newcred, intlabel); 268 return (0); 269 } 270 271 /* 272 * Performs mac_set_proc() last operations, without the process lock. 273 * 274 * 'proc_label_set' indicates whether the label was actually set by a call to 275 * mac_set_proc_core() that succeeded. 'mac_set_proc_data' must be the pointer 276 * returned by mac_set_proc_prepare(), and its associated data will be freed. 277 */ 278 void 279 mac_set_proc_finish(struct thread *const td, bool proc_label_set, 280 void *const mac_set_proc_data) 281 { 282 struct label *const intlabel = mac_set_proc_data; 283 284 PROC_LOCK_ASSERT(td->td_proc, MA_NOTOWNED); 285 286 if (proc_label_set) 287 mac_proc_vm_revoke(td); 288 mac_cred_label_free(intlabel); 289 } 290 291 int 292 sys___mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) 293 { 294 struct ucred *newcred, *oldcred; 295 void *intlabel; 296 struct proc *const p = td->td_proc; 297 struct mac mac; 298 int error; 299 300 error = mac_label_copyin(uap->mac_p, &mac, NULL); 301 if (error) 302 return (error); 303 304 error = mac_set_proc_prepare(td, &mac, &intlabel); 305 if (error) 306 goto free_label; 307 308 newcred = crget(); 309 310 PROC_LOCK(p); 311 oldcred = p->p_ucred; 312 crcopy(newcred, oldcred); 313 314 error = mac_set_proc_core(td, newcred, intlabel); 315 if (error) { 316 PROC_UNLOCK(p); 317 crfree(newcred); 318 goto finish; 319 } 320 321 setsugid(p); 322 proc_set_cred(p, newcred); 323 PROC_UNLOCK(p); 324 325 crfree(oldcred); 326 finish: 327 mac_set_proc_finish(td, error == 0, intlabel); 328 free_label: 329 free_copied_label(&mac); 330 return (error); 331 } 332 333 int 334 sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) 335 { 336 char *u_buffer, *buffer; 337 struct label *intlabel; 338 struct file *fp; 339 struct mac mac; 340 struct vnode *vp; 341 struct pipe *pipe; 342 struct socket *so; 343 cap_rights_t rights; 344 int error; 345 346 error = mac_label_copyin(uap->mac_p, &mac, &u_buffer); 347 if (error) 348 return (error); 349 350 buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); 351 error = fget(td, uap->fd, cap_rights_init_one(&rights, CAP_MAC_GET), 352 &fp); 353 if (error) 354 goto out; 355 356 switch (fp->f_type) { 357 case DTYPE_FIFO: 358 case DTYPE_VNODE: 359 if (!(mac_labeled & MPC_OBJECT_VNODE)) { 360 error = EINVAL; 361 goto out_fdrop; 362 } 363 vp = fp->f_vnode; 364 intlabel = mac_vnode_label_alloc(); 365 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 366 mac_vnode_copy_label(vp->v_label, intlabel); 367 VOP_UNLOCK(vp); 368 error = mac_vnode_externalize_label(intlabel, mac.m_string, 369 buffer, mac.m_buflen); 370 mac_vnode_label_free(intlabel); 371 break; 372 373 case DTYPE_PIPE: 374 if (!(mac_labeled & MPC_OBJECT_PIPE)) { 375 error = EINVAL; 376 goto out_fdrop; 377 } 378 pipe = fp->f_data; 379 intlabel = mac_pipe_label_alloc(); 380 PIPE_LOCK(pipe); 381 mac_pipe_copy_label(pipe->pipe_pair->pp_label, intlabel); 382 PIPE_UNLOCK(pipe); 383 error = mac_pipe_externalize_label(intlabel, mac.m_string, 384 buffer, mac.m_buflen); 385 mac_pipe_label_free(intlabel); 386 break; 387 388 case DTYPE_SOCKET: 389 if (!(mac_labeled & MPC_OBJECT_SOCKET)) { 390 error = EINVAL; 391 goto out_fdrop; 392 } 393 so = fp->f_data; 394 intlabel = mac_socket_label_alloc(M_WAITOK); 395 SOCK_LOCK(so); 396 mac_socket_copy_label(so->so_label, intlabel); 397 SOCK_UNLOCK(so); 398 error = mac_socket_externalize_label(intlabel, mac.m_string, 399 buffer, mac.m_buflen); 400 mac_socket_label_free(intlabel); 401 break; 402 403 default: 404 error = EINVAL; 405 } 406 if (error == 0) 407 error = copyout(buffer, u_buffer, strlen(buffer)+1); 408 out_fdrop: 409 fdrop(fp, td); 410 out: 411 free(buffer, M_MACTEMP); 412 free_copied_label(&mac); 413 return (error); 414 } 415 416 int 417 sys___mac_get_file(struct thread *td, struct __mac_get_file_args *uap) 418 { 419 420 return (kern___mac_get_path(td, uap->path_p, uap->mac_p, FOLLOW)); 421 } 422 423 int 424 sys___mac_get_link(struct thread *td, struct __mac_get_link_args *uap) 425 { 426 427 return (kern___mac_get_path(td, uap->path_p, uap->mac_p, NOFOLLOW)); 428 } 429 430 static int 431 kern___mac_get_path(struct thread *td, const char *path_p, struct mac *mac_p, 432 int follow) 433 { 434 char *u_buffer, *buffer; 435 struct nameidata nd; 436 struct label *intlabel; 437 struct mac mac; 438 int error; 439 440 if (!(mac_labeled & MPC_OBJECT_VNODE)) 441 return (EINVAL); 442 443 error = mac_label_copyin(mac_p, &mac, &u_buffer); 444 if (error) 445 return (error); 446 447 buffer = malloc(mac.m_buflen, M_MACTEMP, M_WAITOK | M_ZERO); 448 NDINIT(&nd, LOOKUP, LOCKLEAF | follow, UIO_USERSPACE, path_p); 449 error = namei(&nd); 450 if (error) 451 goto out; 452 453 intlabel = mac_vnode_label_alloc(); 454 mac_vnode_copy_label(nd.ni_vp->v_label, intlabel); 455 error = mac_vnode_externalize_label(intlabel, mac.m_string, buffer, 456 mac.m_buflen); 457 vput(nd.ni_vp); 458 NDFREE_PNBUF(&nd); 459 mac_vnode_label_free(intlabel); 460 461 if (error == 0) 462 error = copyout(buffer, u_buffer, strlen(buffer)+1); 463 464 out: 465 free(buffer, M_MACTEMP); 466 free_copied_label(&mac); 467 468 return (error); 469 } 470 471 int 472 sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) 473 { 474 struct label *intlabel; 475 struct pipe *pipe; 476 struct socket *so; 477 struct file *fp; 478 struct mount *mp; 479 struct vnode *vp; 480 struct mac mac; 481 cap_rights_t rights; 482 int error; 483 484 error = mac_label_copyin(uap->mac_p, &mac, NULL); 485 if (error) 486 return (error); 487 488 error = fget(td, uap->fd, cap_rights_init_one(&rights, CAP_MAC_SET), 489 &fp); 490 if (error) 491 goto out; 492 493 switch (fp->f_type) { 494 case DTYPE_FIFO: 495 case DTYPE_VNODE: 496 if (!(mac_labeled & MPC_OBJECT_VNODE)) { 497 error = EINVAL; 498 goto out_fdrop; 499 } 500 intlabel = mac_vnode_label_alloc(); 501 error = mac_vnode_internalize_label(intlabel, mac.m_string); 502 if (error) { 503 mac_vnode_label_free(intlabel); 504 break; 505 } 506 vp = fp->f_vnode; 507 error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH); 508 if (error != 0) { 509 mac_vnode_label_free(intlabel); 510 break; 511 } 512 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 513 error = vn_setlabel(vp, intlabel, td->td_ucred); 514 VOP_UNLOCK(vp); 515 vn_finished_write(mp); 516 mac_vnode_label_free(intlabel); 517 break; 518 519 case DTYPE_PIPE: 520 if (!(mac_labeled & MPC_OBJECT_PIPE)) { 521 error = EINVAL; 522 goto out_fdrop; 523 } 524 intlabel = mac_pipe_label_alloc(); 525 error = mac_pipe_internalize_label(intlabel, mac.m_string); 526 if (error == 0) { 527 pipe = fp->f_data; 528 PIPE_LOCK(pipe); 529 error = mac_pipe_label_set(td->td_ucred, 530 pipe->pipe_pair, intlabel); 531 PIPE_UNLOCK(pipe); 532 } 533 mac_pipe_label_free(intlabel); 534 break; 535 536 case DTYPE_SOCKET: 537 if (!(mac_labeled & MPC_OBJECT_SOCKET)) { 538 error = EINVAL; 539 goto out_fdrop; 540 } 541 intlabel = mac_socket_label_alloc(M_WAITOK); 542 error = mac_socket_internalize_label(intlabel, mac.m_string); 543 if (error == 0) { 544 so = fp->f_data; 545 error = mac_socket_label_set(td->td_ucred, so, 546 intlabel); 547 } 548 mac_socket_label_free(intlabel); 549 break; 550 551 default: 552 error = EINVAL; 553 } 554 out_fdrop: 555 fdrop(fp, td); 556 out: 557 free_copied_label(&mac); 558 return (error); 559 } 560 561 int 562 sys___mac_set_file(struct thread *td, struct __mac_set_file_args *uap) 563 { 564 565 return (kern___mac_set_path(td, uap->path_p, uap->mac_p, FOLLOW)); 566 } 567 568 int 569 sys___mac_set_link(struct thread *td, struct __mac_set_link_args *uap) 570 { 571 572 return (kern___mac_set_path(td, uap->path_p, uap->mac_p, NOFOLLOW)); 573 } 574 575 static int 576 kern___mac_set_path(struct thread *td, const char *path_p, struct mac *mac_p, 577 int follow) 578 { 579 struct label *intlabel; 580 struct nameidata nd; 581 struct mount *mp; 582 struct mac mac; 583 int error; 584 585 if (!(mac_labeled & MPC_OBJECT_VNODE)) 586 return (EINVAL); 587 588 error = mac_label_copyin(mac_p, &mac, NULL); 589 if (error) 590 return (error); 591 592 intlabel = mac_vnode_label_alloc(); 593 error = mac_vnode_internalize_label(intlabel, mac.m_string); 594 free_copied_label(&mac); 595 if (error) 596 goto out; 597 598 NDINIT(&nd, LOOKUP, LOCKLEAF | follow, UIO_USERSPACE, path_p); 599 error = namei(&nd); 600 if (error == 0) { 601 error = vn_start_write(nd.ni_vp, &mp, V_WAIT | V_PCATCH); 602 if (error == 0) { 603 error = vn_setlabel(nd.ni_vp, intlabel, 604 td->td_ucred); 605 vn_finished_write(mp); 606 } 607 vput(nd.ni_vp); 608 NDFREE_PNBUF(&nd); 609 } 610 out: 611 mac_vnode_label_free(intlabel); 612 return (error); 613 } 614 615 int 616 sys_mac_syscall(struct thread *td, struct mac_syscall_args *uap) 617 { 618 struct mac_policy_conf *mpc; 619 char target[MAC_MAX_POLICY_NAME]; 620 int error; 621 622 error = copyinstr(uap->policy, target, sizeof(target), NULL); 623 if (error) 624 return (error); 625 626 error = ENOSYS; 627 LIST_FOREACH(mpc, &mac_static_policy_list, mpc_list) { 628 if (strcmp(mpc->mpc_name, target) == 0 && 629 mpc->mpc_ops->mpo_syscall != NULL) { 630 error = mpc->mpc_ops->mpo_syscall(td, 631 uap->call, uap->arg); 632 goto out; 633 } 634 } 635 636 if (!LIST_EMPTY(&mac_policy_list)) { 637 mac_policy_slock_sleep(); 638 LIST_FOREACH(mpc, &mac_policy_list, mpc_list) { 639 if (strcmp(mpc->mpc_name, target) == 0 && 640 mpc->mpc_ops->mpo_syscall != NULL) { 641 error = mpc->mpc_ops->mpo_syscall(td, 642 uap->call, uap->arg); 643 break; 644 } 645 } 646 mac_policy_sunlock_sleep(); 647 } 648 out: 649 return (error); 650 } 651 652 #else /* !MAC */ 653 654 int 655 sys___mac_get_pid(struct thread *td, struct __mac_get_pid_args *uap) 656 { 657 658 return (ENOSYS); 659 } 660 661 int 662 sys___mac_get_proc(struct thread *td, struct __mac_get_proc_args *uap) 663 { 664 665 return (ENOSYS); 666 } 667 668 int 669 sys___mac_set_proc(struct thread *td, struct __mac_set_proc_args *uap) 670 { 671 672 return (ENOSYS); 673 } 674 675 int 676 sys___mac_get_fd(struct thread *td, struct __mac_get_fd_args *uap) 677 { 678 679 return (ENOSYS); 680 } 681 682 int 683 sys___mac_get_file(struct thread *td, struct __mac_get_file_args *uap) 684 { 685 686 return (ENOSYS); 687 } 688 689 int 690 sys___mac_get_link(struct thread *td, struct __mac_get_link_args *uap) 691 { 692 693 return (ENOSYS); 694 } 695 696 int 697 sys___mac_set_fd(struct thread *td, struct __mac_set_fd_args *uap) 698 { 699 700 return (ENOSYS); 701 } 702 703 int 704 sys___mac_set_file(struct thread *td, struct __mac_set_file_args *uap) 705 { 706 707 return (ENOSYS); 708 } 709 710 int 711 sys___mac_set_link(struct thread *td, struct __mac_set_link_args *uap) 712 { 713 714 return (ENOSYS); 715 } 716 717 int 718 sys_mac_syscall(struct thread *td, struct mac_syscall_args *uap) 719 { 720 721 return (ENOSYS); 722 } 723 724 #endif /* !MAC */ 725