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 #include <libkern/section_keywords.h>
76 unsigned long cs_procs_killed
= 0;
77 unsigned long cs_procs_invalidated
= 0;
79 int cs_force_kill
= 0;
80 int cs_force_hard
= 0;
83 const int cs_enforcement_enable
= 1;
84 const int cs_library_val_enable
= 1;
85 #else /* !SECURE_KERNEL */
86 int cs_enforcement_panic
=0;
88 #if CONFIG_ENFORCE_SIGNED_CODE
89 #define DEFAULT_CS_ENFORCEMENT_ENABLE 1
91 #define DEFAULT_CS_ENFORCEMENT_ENABLE 0
93 SECURITY_READ_ONLY_LATE(int) cs_enforcement_enable
= DEFAULT_CS_ENFORCEMENT_ENABLE
;
95 #if CONFIG_ENFORCE_LIBRARY_VALIDATION
96 #define DEFAULT_CS_LIBRARY_VA_ENABLE 1
98 #define DEFAULT_CS_LIBRARY_VA_ENABLE 0
100 SECURITY_READ_ONLY_LATE(int) cs_library_val_enable
= DEFAULT_CS_LIBRARY_VA_ENABLE
;
102 #endif /* !SECURE_KERNEL */
103 int cs_all_vnodes
= 0;
105 static lck_grp_t
*cs_lockgrp
;
107 SYSCTL_INT(_vm
, OID_AUTO
, cs_force_kill
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_force_kill
, 0, "");
108 SYSCTL_INT(_vm
, OID_AUTO
, cs_force_hard
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_force_hard
, 0, "");
109 SYSCTL_INT(_vm
, OID_AUTO
, cs_debug
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_debug
, 0, "");
111 SYSCTL_INT(_vm
, OID_AUTO
, cs_all_vnodes
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_all_vnodes
, 0, "");
114 SYSCTL_INT(_vm
, OID_AUTO
, cs_enforcement
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_enforcement_enable
, 0, "");
115 SYSCTL_INT(_vm
, OID_AUTO
, cs_enforcement_panic
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_enforcement_panic
, 0, "");
117 #if !CONFIG_ENFORCE_LIBRARY_VALIDATION
118 SYSCTL_INT(_vm
, OID_AUTO
, cs_library_validation
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_library_val_enable
, 0, "");
120 #endif /* !SECURE_KERNEL */
122 int panic_on_cs_killed
= 0;
126 #if MACH_ASSERT && __x86_64__
127 panic_on_cs_killed
= 1;
128 #endif /* MACH_ASSERT && __x86_64__ */
129 PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed
,
130 sizeof (panic_on_cs_killed
));
132 int disable_cs_enforcement
= 0;
133 PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement
,
134 sizeof (disable_cs_enforcement
));
135 if (disable_cs_enforcement
) {
136 cs_enforcement_enable
= 0;
139 PE_parse_boot_argn("cs_enforcement_panic", &panic
, sizeof(panic
));
140 cs_enforcement_panic
= (panic
!= 0);
143 PE_parse_boot_argn("cs_debug", &cs_debug
, sizeof (cs_debug
));
145 #if !CONFIG_ENFORCE_LIBRARY_VALIDATION
146 PE_parse_boot_argn("cs_library_val_enable", &cs_library_val_enable
,
147 sizeof (cs_library_val_enable
));
149 #endif /* !SECURE_KERNEL */
151 lck_grp_attr_t
*attr
= lck_grp_attr_alloc_init();
152 cs_lockgrp
= lck_grp_alloc_init("KERNCS", attr
);
153 lck_grp_attr_free(attr
);
157 cs_allow_invalid(struct proc
*p
)
160 lck_mtx_assert(&p
->p_mlock
, LCK_MTX_ASSERT_NOTOWNED
);
162 #if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE
163 /* There needs to be a MAC policy to implement this hook, or else the
164 * kill bits will be cleared here every time. If we have
165 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
166 * implementing the hook.
168 if( 0 != mac_proc_check_run_cs_invalid(p
)) {
169 if(cs_debug
) printf("CODE SIGNING: cs_allow_invalid() "
170 "not allowed: pid %d\n",
174 if(cs_debug
) printf("CODE SIGNING: cs_allow_invalid() "
178 p
->p_csflags
&= ~(CS_KILL
| CS_HARD
);
179 if (p
->p_csflags
& CS_VALID
)
181 p
->p_csflags
|= CS_DEBUGGED
;
184 vm_map_switch_protect(get_task_map(p
->task
), FALSE
);
186 return (p
->p_csflags
& (CS_KILL
| CS_HARD
)) == 0;
190 cs_invalid_page(addr64_t vaddr
, boolean_t
*cs_killed
)
193 int send_kill
= 0, retval
= 0, verbose
= cs_debug
;
199 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
200 vaddr
, p
->p_pid
, p
->p_comm
);
204 /* XXX for testing */
206 p
->p_csflags
|= CS_KILL
;
208 p
->p_csflags
|= CS_HARD
;
210 /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
211 if (p
->p_csflags
& CS_KILL
) {
212 p
->p_csflags
|= CS_KILLED
;
218 /* CS_HARD means fail the mapping operation so the process stays valid. */
219 if (p
->p_csflags
& CS_HARD
) {
222 if (p
->p_csflags
& CS_VALID
) {
223 p
->p_csflags
&= ~CS_VALID
;
224 cs_procs_invalidated
++;
228 csflags
= p
->p_csflags
;
232 printf("CODE SIGNING: cs_invalid_page(0x%llx): "
233 "p=%d[%s] final status 0x%x, %s page%s\n",
234 vaddr
, p
->p_pid
, p
->p_comm
, p
->p_csflags
,
235 retval
? "denying" : "allowing (remove VALID)",
236 send_kill
? " sending SIGKILL" : "");
239 /* We will set the exit reason for the thread later */
240 threadsignal(current_thread(), SIGKILL
, EXC_BAD_ACCESS
, FALSE
);
244 } else if (cs_killed
) {
253 * Assumes p (if passed in) is locked with proc_lock().
257 cs_enforcement(struct proc
*p
)
260 if (cs_enforcement_enable
)
266 if (p
!= NULL
&& (p
->p_csflags
& CS_ENFORCEMENT
))
273 * Returns whether a given process is still valid.
276 cs_valid(struct proc
*p
)
282 if (p
!= NULL
&& (p
->p_csflags
& CS_VALID
))
289 * Library validation functions
292 cs_require_lv(struct proc
*p
)
295 if (cs_library_val_enable
)
301 if (p
!= NULL
&& (p
->p_csflags
& CS_REQUIRE_LV
))
308 * <rdar://problem/24634089> added to allow system level library
309 * validation check at mac_cred_label_update_execve time
312 cs_system_require_lv(void)
314 return cs_library_val_enable
? 1 : 0;
318 * Function: csblob_get_base_offset
320 * Description: This function returns the base offset into the Mach-O binary
325 csblob_get_base_offset(struct cs_blob
*blob
)
327 return blob
->csb_base_offset
;
331 * Function: csblob_get_size
333 * Description: This function returns the size of a given blob.
337 csblob_get_size(struct cs_blob
*blob
)
339 return blob
->csb_mem_size
;
343 * Function: csblob_get_addr
345 * Description: This function returns the address of a given blob.
349 csblob_get_addr(struct cs_blob
*blob
)
351 return blob
->csb_mem_kaddr
;
355 * Function: csblob_get_platform_binary
357 * Description: This function returns true if the binary is
358 * in the trust cache.
362 csblob_get_platform_binary(struct cs_blob
*blob
)
364 if (blob
&& blob
->csb_platform_binary
)
370 * Function: csblob_get_flags
372 * Description: This function returns the flags for a given blob
376 csblob_get_flags(struct cs_blob
*blob
)
378 return blob
->csb_flags
;
382 * Function: csproc_get_blob
384 * Description: This function returns the cs_blob
388 csproc_get_blob(struct proc
*p
)
393 if (NULL
== p
->p_textvp
)
396 return ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
);
400 * Function: csproc_get_blob
402 * Description: This function returns the cs_blob
406 csvnode_get_blob(struct vnode
*vp
, off_t offset
)
408 return ubc_cs_blob_get(vp
, -1, offset
);
412 * Function: csblob_get_teamid
414 * Description: This function returns a pointer to the
418 csblob_get_teamid(struct cs_blob
*csblob
)
420 return csblob
->csb_teamid
;
424 * Function: csblob_get_identity
426 * Description: This function returns a pointer to the
430 csblob_get_identity(struct cs_blob
*csblob
)
432 const CS_CodeDirectory
*cd
;
434 cd
= (const CS_CodeDirectory
*)csblob_find_blob(csblob
, CSSLOT_CODEDIRECTORY
, CSMAGIC_CODEDIRECTORY
);
438 if (cd
->identOffset
== 0)
441 return ((const char *)cd
) + ntohl(cd
->identOffset
);
445 * Function: csblob_get_cdhash
447 * Description: This function returns a pointer to the
448 * cdhash of csblob (20 byte array)
451 csblob_get_cdhash(struct cs_blob
*csblob
)
453 return csblob
->csb_cdhash
;
457 csblob_entitlements_dictionary_copy(struct cs_blob
*csblob
)
459 if (!csblob
->csb_entitlements
) return NULL
;
460 osobject_retain(csblob
->csb_entitlements
);
461 return csblob
->csb_entitlements
;
465 csblob_entitlements_dictionary_set(struct cs_blob
*csblob
, void * entitlements
)
467 assert(csblob
->csb_entitlements
== NULL
);
468 if (entitlements
) osobject_retain(entitlements
);
469 csblob
->csb_entitlements
= entitlements
;
473 * Function: csproc_get_teamid
475 * Description: This function returns a pointer to the
476 * team id of the process p
479 csproc_get_teamid(struct proc
*p
)
481 struct cs_blob
*csblob
;
483 csblob
= csproc_get_blob(p
);
487 return csblob_get_teamid(csblob
);
491 * Function: csvnode_get_teamid
493 * Description: This function returns a pointer to the
494 * team id of the binary at the given offset in vnode vp
497 csvnode_get_teamid(struct vnode
*vp
, off_t offset
)
499 struct cs_blob
*csblob
;
504 csblob
= ubc_cs_blob_get(vp
, -1, offset
);
508 return csblob_get_teamid(csblob
);
512 * Function: csproc_get_platform_binary
514 * Description: This function returns the value
515 * of the platform_binary field for proc p
518 csproc_get_platform_binary(struct proc
*p
)
520 struct cs_blob
*csblob
;
522 csblob
= csproc_get_blob(p
);
524 /* If there is no csblob this returns 0 because
525 it is true that it is not a platform binary */
526 return (csblob
== NULL
) ? 0 : csblob
->csb_platform_binary
;
530 csproc_get_platform_path(struct proc
*p
)
532 struct cs_blob
*csblob
= csproc_get_blob(p
);
534 return (csblob
== NULL
) ? 0 : csblob
->csb_platform_path
;
538 * Function: csproc_get_prod_signed
540 * Description: Returns 1 if process is not signed with a developer identity.
541 * Note the inverted meaning from the cs_flag to make the error case safer.
542 * Will go away with rdar://problem/28322552.
545 csproc_get_prod_signed(struct proc
*p
)
547 return ((p
->p_csflags
& CS_DEV_CODE
) == 0);
552 * Function: csfg_get_platform_binary
554 * Description: This function returns the
555 * platform binary field for the
559 csfg_get_platform_binary(struct fileglob
*fg
)
561 int platform_binary
= 0;
562 struct ubc_info
*uip
;
565 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
568 vp
= (struct vnode
*)fg
->fg_data
;
573 if (!UBCINFOEXISTS(vp
))
580 if (uip
->cs_blobs
== NULL
)
583 /* It is OK to extract the teamid from the first blob
584 because all blobs of a vnode must have the same teamid */
585 platform_binary
= uip
->cs_blobs
->csb_platform_binary
;
589 return platform_binary
;
593 csfg_get_cdhash(struct fileglob
*fg
, uint64_t offset
, size_t *cdhash_size
)
597 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
600 vp
= (struct vnode
*)fg
->fg_data
;
604 struct cs_blob
*csblob
= NULL
;
605 if ((csblob
= ubc_cs_blob_get(vp
, -1, offset
)) == NULL
)
609 *cdhash_size
= CS_CDHASH_LEN
;
611 return csblob
->csb_cdhash
;
615 * Function: csfg_get_teamid
617 * Description: This returns a pointer to
618 * the teamid for the fileglob fg
621 csfg_get_teamid(struct fileglob
*fg
)
623 struct ubc_info
*uip
;
624 const char *str
= NULL
;
627 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
630 vp
= (struct vnode
*)fg
->fg_data
;
635 if (!UBCINFOEXISTS(vp
))
642 if (uip
->cs_blobs
== NULL
)
645 /* It is OK to extract the teamid from the first blob
646 because all blobs of a vnode must have the same teamid */
647 str
= uip
->cs_blobs
->csb_teamid
;
655 * Function: csfg_get_prod_signed
657 * Description: Returns 1 if code is not signed with a developer identity.
658 * Note the inverted meaning from the cs_flag to make the error case safer.
659 * Will go away with rdar://problem/28322552.
662 csfg_get_prod_signed(struct fileglob
*fg
)
664 struct ubc_info
*uip
;
668 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
671 vp
= (struct vnode
*)fg
->fg_data
;
676 if (!UBCINFOEXISTS(vp
))
683 if (uip
->cs_blobs
== NULL
)
686 /* It is OK to extract the flag from the first blob
687 because all blobs of a vnode must have the same cs_flags */
688 prod_signed
= (uip
->cs_blobs
->csb_flags
& CS_DEV_CODE
) == 0;
697 cs_entitlement_flags(struct proc
*p
)
699 return (p
->p_csflags
& CS_ENTITLEMENT_FLAGS
);
703 cs_restricted(struct proc
*p
)
705 return (p
->p_csflags
& CS_RESTRICT
) ? 1 : 0;
709 * Function: csfg_get_path
711 * Description: This populates the buffer passed in
712 * with the path of the vnode
713 * When calling this, the fileglob
714 * cannot go away. The caller must have a
715 * a reference on the fileglob or fileproc
718 csfg_get_path(struct fileglob
*fg
, char *path
, int *len
)
722 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
725 vp
= (struct vnode
*)fg
->fg_data
;
727 /* vn_getpath returns 0 for success,
729 return vn_getpath(vp
, path
, len
);
732 /* Retrieve the entitlements blob for a process.
734 * EINVAL no text vnode associated with the process
735 * EBADEXEC invalid code signing data
736 * 0 no error occurred
738 * On success, out_start and out_length will point to the
739 * entitlements blob if found; or will be set to NULL/zero
740 * if there were no entitlements.
744 cs_entitlements_blob_get(proc_t p
, void **out_start
, size_t *out_length
)
746 struct cs_blob
*csblob
;
751 if (NULL
== p
->p_textvp
)
754 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
757 return csblob_get_entitlements(csblob
, out_start
, out_length
);
760 /* Retrieve the codesign identity for a process.
762 * NULL an error occured
763 * string the cs_identity
767 cs_identity_get(proc_t p
)
769 struct cs_blob
*csblob
;
771 if (NULL
== p
->p_textvp
)
774 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
777 return csblob_get_identity(csblob
);
781 /* Retrieve the codesign blob for a process.
783 * EINVAL no text vnode associated with the process
784 * 0 no error occurred
786 * On success, out_start and out_length will point to the
787 * cms blob if found; or will be set to NULL/zero
788 * if there were no blob.
792 cs_blob_get(proc_t p
, void **out_start
, size_t *out_length
)
794 struct cs_blob
*csblob
;
799 if (NULL
== p
->p_textvp
)
802 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
805 *out_start
= (void *)csblob
->csb_mem_kaddr
;
806 *out_length
= csblob
->csb_mem_size
;
812 * return cshash of a process, cdhash is of size CS_CDHASH_LEN
816 cs_get_cdhash(struct proc
*p
)
818 struct cs_blob
*csblob
;
820 if (NULL
== p
->p_textvp
)
823 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
826 return csblob
->csb_cdhash
;