2 * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/proc_internal.h>
34 #include <sys/sysctl.h>
35 #include <sys/signal.h>
36 #include <sys/signalvar.h>
37 #include <sys/codesign.h>
39 #include <sys/fcntl.h>
41 #include <sys/file_internal.h>
42 #include <sys/kauth.h>
43 #include <sys/mount.h>
46 #include <sys/socketvar.h>
47 #include <sys/vnode.h>
48 #include <sys/vnode_internal.h>
51 #include <sys/ubc_internal.h>
53 #include <security/mac.h>
54 #include <security/mac_policy.h>
55 #include <security/mac_framework.h>
57 #include <mach/mach_types.h>
58 #include <mach/vm_map.h>
59 #include <mach/mach_vm.h>
61 #include <kern/kern_types.h>
62 #include <kern/task.h>
64 #include <vm/vm_map.h>
65 #include <vm/vm_kern.h>
68 #include <kern/assert.h>
70 #include <pexpert/pexpert.h>
72 #include <mach/shared_region.h>
74 unsigned long cs_procs_killed
= 0;
75 unsigned long cs_procs_invalidated
= 0;
77 int cs_force_kill
= 0;
78 int cs_force_hard
= 0;
81 const int cs_enforcement_enable
=1;
82 const int cs_library_val_enable
=1;
84 #if CONFIG_ENFORCE_SIGNED_CODE
85 int cs_enforcement_enable
=1;
87 int cs_enforcement_enable
=0;
88 #endif /* CONFIG_ENFORCE_SIGNED_CODE */
90 #if CONFIG_ENFORCE_LIBRARY_VALIDATION
91 int cs_library_val_enable
= 1;
93 int cs_library_val_enable
= 0;
94 #endif /* CONFIG_ENFORCE_LIBRARY_VALIDATION */
96 int cs_enforcement_panic
=0;
97 #endif /* SECURE_KERNEL */
98 int cs_all_vnodes
= 0;
100 static lck_grp_t
*cs_lockgrp
;
101 static lck_rw_t
* SigPUPLock
;
103 SYSCTL_INT(_vm
, OID_AUTO
, cs_force_kill
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_force_kill
, 0, "");
104 SYSCTL_INT(_vm
, OID_AUTO
, cs_force_hard
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_force_hard
, 0, "");
105 SYSCTL_INT(_vm
, OID_AUTO
, cs_debug
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_debug
, 0, "");
107 SYSCTL_INT(_vm
, OID_AUTO
, cs_all_vnodes
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_all_vnodes
, 0, "");
110 SYSCTL_INT(_vm
, OID_AUTO
, cs_enforcement
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_enforcement_enable
, 0, "");
111 SYSCTL_INT(_vm
, OID_AUTO
, cs_enforcement_panic
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_enforcement_panic
, 0, "");
114 int panic_on_cs_killed
= 0;
118 #if MACH_ASSERT && __x86_64__
119 panic_on_cs_killed
= 1;
120 #endif /* MACH_ASSERT && __x86_64__ */
121 PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed
,
122 sizeof (panic_on_cs_killed
));
124 int disable_cs_enforcement
= 0;
125 PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement
,
126 sizeof (disable_cs_enforcement
));
127 if (disable_cs_enforcement
) {
128 cs_enforcement_enable
= 0;
131 PE_parse_boot_argn("cs_enforcement_panic", &panic
, sizeof(panic
));
132 cs_enforcement_panic
= (panic
!= 0);
135 PE_parse_boot_argn("cs_debug", &cs_debug
, sizeof (cs_debug
));
137 lck_grp_attr_t
*attr
= lck_grp_attr_alloc_init();
138 cs_lockgrp
= lck_grp_alloc_init("KERNCS", attr
);
139 SigPUPLock
= lck_rw_alloc_init(cs_lockgrp
, NULL
);
143 cs_allow_invalid(struct proc
*p
)
146 lck_mtx_assert(&p
->p_mlock
, LCK_MTX_ASSERT_NOTOWNED
);
148 #if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE
149 /* There needs to be a MAC policy to implement this hook, or else the
150 * kill bits will be cleared here every time. If we have
151 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
152 * implementing the hook.
154 if( 0 != mac_proc_check_run_cs_invalid(p
)) {
155 if(cs_debug
) printf("CODE SIGNING: cs_allow_invalid() "
156 "not allowed: pid %d\n",
160 if(cs_debug
) printf("CODE SIGNING: cs_allow_invalid() "
164 p
->p_csflags
&= ~(CS_KILL
| CS_HARD
);
166 vm_map_switch_protect(get_task_map(p
->task
), FALSE
);
168 return (p
->p_csflags
& (CS_KILL
| CS_HARD
)) == 0;
176 int send_kill
= 0, retval
= 0, verbose
= cs_debug
;
182 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
183 vaddr
, p
->p_pid
, p
->p_comm
);
187 /* XXX for testing */
189 p
->p_csflags
|= CS_KILL
;
191 p
->p_csflags
|= CS_HARD
;
193 /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
194 if (p
->p_csflags
& CS_KILL
) {
195 if (panic_on_cs_killed
&&
196 vaddr
>= SHARED_REGION_BASE
&&
197 vaddr
< SHARED_REGION_BASE
+ SHARED_REGION_SIZE
) {
198 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr
, p
);
200 p
->p_csflags
|= CS_KILLED
;
207 if (panic_on_cs_killed
&&
208 vaddr
>= SHARED_REGION_BASE
&&
209 vaddr
< SHARED_REGION_BASE
+ SHARED_REGION_SIZE
) {
210 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr
, p
);
212 #endif /* __x86_64__ */
214 /* CS_HARD means fail the mapping operation so the process stays valid. */
215 if (p
->p_csflags
& CS_HARD
) {
218 if (p
->p_csflags
& CS_VALID
) {
219 p
->p_csflags
&= ~CS_VALID
;
220 cs_procs_invalidated
++;
224 csflags
= p
->p_csflags
;
228 printf("CODE SIGNING: cs_invalid_page(0x%llx): "
229 "p=%d[%s] final status 0x%x, %s page%s\n",
230 vaddr
, p
->p_pid
, p
->p_comm
, p
->p_csflags
,
231 retval
? "denying" : "allowing (remove VALID)",
232 send_kill
? " sending SIGKILL" : "");
235 threadsignal(current_thread(), SIGKILL
, EXC_BAD_ACCESS
);
242 * Assumes p (if passed in) is locked with proc_lock().
246 cs_enforcement(struct proc
*p
)
249 if (cs_enforcement_enable
)
255 if (p
!= NULL
&& (p
->p_csflags
& CS_ENFORCEMENT
))
262 struct cscsr_functions
*funcs
;
263 vm_map_offset_t csr_map_base
;
264 vm_map_size_t csr_map_size
;
269 SYSCTL_INT(_vm
, OID_AUTO
, sigpup_disable
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &csr_state
.disabled
, 0, "");
272 vnsize(vfs_context_t vfs
, vnode_t vp
, uint64_t *size
)
274 struct vnode_attr va
;
278 VATTR_WANTED(&va
, va_data_size
);
280 error
= vnode_getattr(vp
, &va
, vfs
);
283 *size
= va
.va_data_size
;
288 sigpup_install(user_addr_t argsp
)
290 struct sigpup_install_table args
;
291 memory_object_control_t control
;
292 kern_return_t result
;
293 vfs_context_t vfs
= NULL
;
294 struct vnode_attr va
;
301 if (!cs_enforcement_enable
|| csr_state
.funcs
== NULL
)
304 lck_rw_lock_exclusive(SigPUPLock
);
306 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
312 printf("sigpup install\n");
314 if (csr_state
.csr_map_base
!= 0 || csr_state
.inuse
) {
319 if (USER_ADDR_NULL
== argsp
) {
323 if ((error
= copyin(argsp
, &args
, sizeof(args
))) != 0)
327 printf("sigpup install with args\n");
329 MALLOC(buf
, char *, MAXPATHLEN
, M_TEMP
, M_WAITOK
);
334 if ((error
= copyinstr((user_addr_t
)args
.path
, buf
, MAXPATHLEN
, &len
)) != 0)
337 if ((vfs
= vfs_context_create(NULL
)) == NULL
) {
342 if ((error
= vnode_lookup(buf
, VNODE_LOOKUP_NOFOLLOW
, &vp
, vfs
)) != 0)
346 printf("sigpup found file: %s\n", buf
);
348 /* make sure vnode is on the process's root volume */
349 if (rootvnode
->v_mount
!= vp
->v_mount
) {
350 if (cs_debug
) printf("sigpup csr no on root volume\n");
355 /* make sure vnode is owned by "root" */
357 VATTR_WANTED(&va
, va_uid
);
358 error
= vnode_getattr(vp
, &va
, vfs
);
362 if (va
.va_uid
!= 0) {
363 if (cs_debug
) printf("sigpup: csr file not owned by root\n");
368 error
= vnsize(vfs
, vp
, &size
);
372 control
= ubc_getobject(vp
, 0);
373 if (control
== MEMORY_OBJECT_CONTROL_NULL
) {
378 csr_state
.csr_map_size
= mach_vm_round_page(size
);
383 result
= vm_map_enter_mem_object_control(kernel_map
,
384 &csr_state
.csr_map_base
,
385 csr_state
.csr_map_size
,
386 0, VM_FLAGS_ANYWHERE
,
387 control
, 0 /* file offset */,
392 if (result
!= KERN_SUCCESS
) {
397 error
= csr_state
.funcs
->csr_validate_header((const uint8_t *)csr_state
.csr_map_base
,
398 csr_state
.csr_map_size
);
401 printf("sigpup header invalid, dropping mapping");
407 printf("table loaded %ld bytes\n", (long)csr_state
.csr_map_size
);
410 lck_rw_unlock_exclusive(SigPUPLock
);
417 (void)vfs_context_rele(vfs
);
420 printf("sigpup: load failed with error: %d\n", error
);
430 if (kauth_cred_issuser(kauth_cred_get()) == 0)
433 lck_rw_lock_exclusive(SigPUPLock
);
435 if (csr_state
.csr_map_base
== 0 || csr_state
.inuse
) {
436 printf("failed to unload the sigpup database\n");
437 lck_rw_unlock_exclusive(SigPUPLock
);
442 printf("sigpup: unloading\n");
444 (void)mach_vm_deallocate(kernel_map
,
445 csr_state
.csr_map_base
, csr_state
.csr_map_size
);
447 csr_state
.csr_map_base
= 0;
448 csr_state
.csr_map_size
= 0;
450 lck_rw_unlock_exclusive(SigPUPLock
);
455 void sigpup_attach_vnode(vnode_t
); /* XXX */
458 sigpup_attach_vnode(vnode_t vp
)
463 if (!cs_enforcement_enable
|| csr_state
.funcs
== NULL
|| csr_state
.csr_map_base
== 0 || csr_state
.disabled
)
466 /* if the file is not on the root volumes or already been check, skip */
467 if (vp
->v_mount
!= rootvnode
->v_mount
|| (vp
->v_flag
& VNOCS
))
470 csblob
= csr_state
.funcs
->csr_find_file_codedirectory(vp
, (const uint8_t *)csr_state
.csr_map_base
,
471 (size_t)csr_state
.csr_map_size
, &cslen
);
473 ubc_cs_sigpup_add(vp
, (vm_address_t
)csblob
, (vm_size_t
)cslen
);
480 cs_register_cscsr(struct cscsr_functions
*funcs
)
482 if (csr_state
.funcs
|| funcs
->csr_version
< CSCSR_VERSION
)
484 csr_state
.funcs
= funcs
;
488 * Library validation functions
491 cs_require_lv(struct proc
*p
)
494 if (cs_library_val_enable
)
500 if (p
!= NULL
&& (p
->p_csflags
& CS_REQUIRE_LV
))
507 * Function: csblob_get_teamid
509 * Description: This function returns a pointer to the team id
510 stored within the codedirectory of the csblob.
511 If the codedirectory predates team-ids, it returns
513 This does not copy the name but returns a pointer to
514 it within the CD. Subsequently, the CD must be
515 available when this is used.
518 csblob_get_teamid(struct cs_blob
*csblob
)
520 const CS_CodeDirectory
*cd
;
522 if ((cd
= (const CS_CodeDirectory
*)cs_find_blob(
523 csblob
, CSSLOT_CODEDIRECTORY
, CSMAGIC_CODEDIRECTORY
)) == NULL
)
526 if (ntohl(cd
->version
) < CS_SUPPORTSTEAMID
)
529 if (ntohl(cd
->teamOffset
) == 0)
532 const char *name
= ((const char *)cd
) + ntohl(cd
->teamOffset
);
534 printf("found team-id %s in cdblob\n", name
);
540 * Function: csproc_get_blob
542 * Description: This function returns the cs_blob
545 static struct cs_blob
*
546 csproc_get_blob(struct proc
*p
)
551 if (NULL
== p
->p_textvp
)
554 return ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
);
558 * Function: csproc_get_teamid
560 * Description: This function returns a pointer to the
561 * team id of the process p
564 csproc_get_teamid(struct proc
*p
)
566 struct cs_blob
*csblob
;
568 csblob
= csproc_get_blob(p
);
570 return (csblob
== NULL
) ? NULL
: csblob
->csb_teamid
;
574 * Function: csvnode_get_teamid
576 * Description: This function returns a pointer to the
577 * team id of the binary at the given offset in vnode vp
580 csvnode_get_teamid(struct vnode
*vp
, off_t offset
)
582 struct cs_blob
*csblob
;
587 csblob
= ubc_cs_blob_get(vp
, -1, offset
);
589 return (csblob
== NULL
) ? NULL
: csblob
->csb_teamid
;
593 * Function: csproc_get_platform_binary
595 * Description: This function returns the value
596 * of the platform_binary field for proc p
599 csproc_get_platform_binary(struct proc
*p
)
601 struct cs_blob
*csblob
;
603 csblob
= csproc_get_blob(p
);
605 /* If there is no csblob this returns 0 because
606 it is true that it is not a platform binary */
607 return (csblob
== NULL
) ? 0 : csblob
->csb_platform_binary
;
611 * Function: csfg_get_platform_binary
613 * Description: This function returns the
614 * platform binary field for the
618 csfg_get_platform_binary(struct fileglob
*fg
)
620 int platform_binary
= 0;
621 struct ubc_info
*uip
;
624 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
627 vp
= (struct vnode
*)fg
->fg_data
;
632 if (!UBCINFOEXISTS(vp
))
639 if (uip
->cs_blobs
== NULL
)
642 /* It is OK to extract the teamid from the first blob
643 because all blobs of a vnode must have the same teamid */
644 platform_binary
= uip
->cs_blobs
->csb_platform_binary
;
648 return platform_binary
;
652 * Function: csfg_get_teamid
654 * Description: This returns a pointer to
655 * the teamid for the fileglob fg
658 csfg_get_teamid(struct fileglob
*fg
)
660 struct ubc_info
*uip
;
661 const char *str
= NULL
;
664 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
667 vp
= (struct vnode
*)fg
->fg_data
;
672 if (!UBCINFOEXISTS(vp
))
679 if (uip
->cs_blobs
== NULL
)
682 /* It is OK to extract the teamid from the first blob
683 because all blobs of a vnode must have the same teamid */
684 str
= uip
->cs_blobs
->csb_teamid
;
692 cs_entitlement_flags(struct proc
*p
)
694 return (p
->p_csflags
& CS_ENTITLEMENT_FLAGS
);
698 * Function: csfg_get_path
700 * Description: This populates the buffer passed in
701 * with the path of the vnode
702 * When calling this, the fileglob
703 * cannot go away. The caller must have a
704 * a reference on the fileglob or fileproc
707 csfg_get_path(struct fileglob
*fg
, char *path
, int *len
)
711 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
714 vp
= (struct vnode
*)fg
->fg_data
;
716 /* vn_getpath returns 0 for success,
718 return vn_getpath(vp
, path
, len
);