]>
git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_cs.c
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;
83 #else /* !SECURE_KERNEL */
84 int cs_enforcement_panic
=0;
86 #if CONFIG_ENFORCE_SIGNED_CODE
87 int cs_enforcement_enable
= 1;
89 int cs_enforcement_enable
= 0;
92 #if CONFIG_ENFORCE_LIBRARY_VALIDATION
93 int cs_library_val_enable
= 1;
95 int cs_library_val_enable
= 0;
98 #endif /* !SECURE_KERNEL */
99 int cs_all_vnodes
= 0;
101 static lck_grp_t
*cs_lockgrp
;
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, "");
113 #if !CONFIG_ENFORCE_LIBRARY_VALIDATION
114 SYSCTL_INT(_vm
, OID_AUTO
, cs_library_validation
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &cs_library_val_enable
, 0, "");
116 #endif /* !SECURE_KERNEL */
118 int panic_on_cs_killed
= 0;
122 #if MACH_ASSERT && __x86_64__
123 panic_on_cs_killed
= 1;
124 #endif /* MACH_ASSERT && __x86_64__ */
125 PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed
,
126 sizeof (panic_on_cs_killed
));
128 int disable_cs_enforcement
= 0;
129 PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement
,
130 sizeof (disable_cs_enforcement
));
131 if (disable_cs_enforcement
) {
132 cs_enforcement_enable
= 0;
135 PE_parse_boot_argn("cs_enforcement_panic", &panic
, sizeof(panic
));
136 cs_enforcement_panic
= (panic
!= 0);
139 PE_parse_boot_argn("cs_debug", &cs_debug
, sizeof (cs_debug
));
141 #if !CONFIG_ENFORCE_LIBRARY_VALIDATION
142 PE_parse_boot_argn("cs_library_val_enable", &cs_library_val_enable
,
143 sizeof (cs_library_val_enable
));
145 #endif /* !SECURE_KERNEL */
147 lck_grp_attr_t
*attr
= lck_grp_attr_alloc_init();
148 cs_lockgrp
= lck_grp_alloc_init("KERNCS", attr
);
152 cs_allow_invalid(struct proc
*p
)
155 lck_mtx_assert(&p
->p_mlock
, LCK_MTX_ASSERT_NOTOWNED
);
157 #if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE
158 /* There needs to be a MAC policy to implement this hook, or else the
159 * kill bits will be cleared here every time. If we have
160 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
161 * implementing the hook.
163 if( 0 != mac_proc_check_run_cs_invalid(p
)) {
164 if(cs_debug
) printf("CODE SIGNING: cs_allow_invalid() "
165 "not allowed: pid %d\n",
169 if(cs_debug
) printf("CODE SIGNING: cs_allow_invalid() "
173 p
->p_csflags
&= ~(CS_KILL
| CS_HARD
);
175 vm_map_switch_protect(get_task_map(p
->task
), FALSE
);
177 return (p
->p_csflags
& (CS_KILL
| CS_HARD
)) == 0;
185 int send_kill
= 0, retval
= 0, verbose
= cs_debug
;
191 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
192 vaddr
, p
->p_pid
, p
->p_comm
);
196 /* XXX for testing */
198 p
->p_csflags
|= CS_KILL
;
200 p
->p_csflags
|= CS_HARD
;
202 /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
203 if (p
->p_csflags
& CS_KILL
) {
204 if (panic_on_cs_killed
&&
205 vaddr
>= SHARED_REGION_BASE
&&
206 vaddr
< SHARED_REGION_BASE
+ SHARED_REGION_SIZE
) {
207 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr
, p
);
209 p
->p_csflags
|= CS_KILLED
;
216 if (panic_on_cs_killed
&&
217 vaddr
>= SHARED_REGION_BASE
&&
218 vaddr
< SHARED_REGION_BASE
+ SHARED_REGION_SIZE
) {
219 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr
, p
);
221 #endif /* __x86_64__ */
223 /* CS_HARD means fail the mapping operation so the process stays valid. */
224 if (p
->p_csflags
& CS_HARD
) {
227 if (p
->p_csflags
& CS_VALID
) {
228 p
->p_csflags
&= ~CS_VALID
;
229 cs_procs_invalidated
++;
233 csflags
= p
->p_csflags
;
237 printf("CODE SIGNING: cs_invalid_page(0x%llx): "
238 "p=%d[%s] final status 0x%x, %s page%s\n",
239 vaddr
, p
->p_pid
, p
->p_comm
, p
->p_csflags
,
240 retval
? "denying" : "allowing (remove VALID)",
241 send_kill
? " sending SIGKILL" : "");
244 threadsignal(current_thread(), SIGKILL
, EXC_BAD_ACCESS
);
251 * Assumes p (if passed in) is locked with proc_lock().
255 cs_enforcement(struct proc
*p
)
258 if (cs_enforcement_enable
)
264 if (p
!= NULL
&& (p
->p_csflags
& CS_ENFORCEMENT
))
271 * Library validation functions
274 cs_require_lv(struct proc
*p
)
277 if (cs_library_val_enable
)
283 if (p
!= NULL
&& (p
->p_csflags
& CS_REQUIRE_LV
))
290 * Function: csblob_get_platform_binary
292 * Description: This function returns true if the binary is
293 * in the trust cache.
297 csblob_get_platform_binary(struct cs_blob
*blob
)
299 if (blob
&& blob
->csb_platform_binary
)
305 * Function: csblob_get_flags
307 * Description: This function returns the flags for a given blob
311 csblob_get_flags(struct cs_blob
*blob
)
314 return blob
->csb_flags
;
319 * Function: csproc_get_blob
321 * Description: This function returns the cs_blob
325 csproc_get_blob(struct proc
*p
)
330 if (NULL
== p
->p_textvp
)
333 return ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
);
337 * Function: csproc_get_blob
339 * Description: This function returns the cs_blob
343 csvnode_get_blob(struct vnode
*vp
, off_t offset
)
345 return ubc_cs_blob_get(vp
, -1, offset
);
349 * Function: csblob_get_teamid
351 * Description: This function returns a pointer to the
355 csblob_get_teamid(struct cs_blob
*csblob
)
357 return csblob
->csb_teamid
;
361 * Function: csblob_get_identity
363 * Description: This function returns a pointer to the
367 csblob_get_identity(struct cs_blob
*csblob
)
369 const CS_CodeDirectory
*cd
;
371 cd
= (const CS_CodeDirectory
*)csblob_find_blob(csblob
, CSSLOT_CODEDIRECTORY
, CSMAGIC_CODEDIRECTORY
);
375 if (cd
->identOffset
== 0)
378 return ((const char *)cd
) + ntohl(cd
->identOffset
);
382 * Function: csblob_get_cdhash
384 * Description: This function returns a pointer to the
385 * cdhash of csblob (20 byte array)
388 csblob_get_cdhash(struct cs_blob
*csblob
)
390 return csblob
->csb_cdhash
;
394 * Function: csproc_get_teamid
396 * Description: This function returns a pointer to the
397 * team id of the process p
400 csproc_get_teamid(struct proc
*p
)
402 struct cs_blob
*csblob
;
404 csblob
= csproc_get_blob(p
);
408 return csblob_get_teamid(csblob
);
412 * Function: csvnode_get_teamid
414 * Description: This function returns a pointer to the
415 * team id of the binary at the given offset in vnode vp
418 csvnode_get_teamid(struct vnode
*vp
, off_t offset
)
420 struct cs_blob
*csblob
;
425 csblob
= ubc_cs_blob_get(vp
, -1, offset
);
429 return csblob_get_teamid(csblob
);
433 * Function: csproc_get_platform_binary
435 * Description: This function returns the value
436 * of the platform_binary field for proc p
439 csproc_get_platform_binary(struct proc
*p
)
441 struct cs_blob
*csblob
;
443 csblob
= csproc_get_blob(p
);
445 /* If there is no csblob this returns 0 because
446 it is true that it is not a platform binary */
447 return (csblob
== NULL
) ? 0 : csblob
->csb_platform_binary
;
451 csproc_get_platform_path(struct proc
*p
)
453 struct cs_blob
*csblob
= csproc_get_blob(p
);
455 return (csblob
== NULL
) ? 0 : csblob
->csb_platform_path
;
459 * Function: csfg_get_platform_binary
461 * Description: This function returns the
462 * platform binary field for the
466 csfg_get_platform_binary(struct fileglob
*fg
)
468 int platform_binary
= 0;
469 struct ubc_info
*uip
;
472 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
475 vp
= (struct vnode
*)fg
->fg_data
;
480 if (!UBCINFOEXISTS(vp
))
487 if (uip
->cs_blobs
== NULL
)
490 /* It is OK to extract the teamid from the first blob
491 because all blobs of a vnode must have the same teamid */
492 platform_binary
= uip
->cs_blobs
->csb_platform_binary
;
496 return platform_binary
;
500 csfg_get_cdhash(struct fileglob
*fg
, uint64_t offset
, size_t *cdhash_size
)
504 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
507 vp
= (struct vnode
*)fg
->fg_data
;
511 struct cs_blob
*csblob
= NULL
;
512 if ((csblob
= ubc_cs_blob_get(vp
, -1, offset
)) == NULL
)
516 *cdhash_size
= CS_CDHASH_LEN
;
518 return csblob
->csb_cdhash
;
522 * Function: csfg_get_teamid
524 * Description: This returns a pointer to
525 * the teamid for the fileglob fg
528 csfg_get_teamid(struct fileglob
*fg
)
530 struct ubc_info
*uip
;
531 const char *str
= NULL
;
534 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
537 vp
= (struct vnode
*)fg
->fg_data
;
542 if (!UBCINFOEXISTS(vp
))
549 if (uip
->cs_blobs
== NULL
)
552 /* It is OK to extract the teamid from the first blob
553 because all blobs of a vnode must have the same teamid */
554 str
= uip
->cs_blobs
->csb_teamid
;
562 cs_entitlement_flags(struct proc
*p
)
564 return (p
->p_csflags
& CS_ENTITLEMENT_FLAGS
);
568 cs_restricted(struct proc
*p
)
570 return (p
->p_csflags
& CS_RESTRICT
) ? 1 : 0;
574 * Function: csfg_get_path
576 * Description: This populates the buffer passed in
577 * with the path of the vnode
578 * When calling this, the fileglob
579 * cannot go away. The caller must have a
580 * a reference on the fileglob or fileproc
583 csfg_get_path(struct fileglob
*fg
, char *path
, int *len
)
587 if (FILEGLOB_DTYPE(fg
) != DTYPE_VNODE
)
590 vp
= (struct vnode
*)fg
->fg_data
;
592 /* vn_getpath returns 0 for success,
594 return vn_getpath(vp
, path
, len
);
597 /* Retrieve the entitlements blob for a process.
599 * EINVAL no text vnode associated with the process
600 * EBADEXEC invalid code signing data
601 * 0 no error occurred
603 * On success, out_start and out_length will point to the
604 * entitlements blob if found; or will be set to NULL/zero
605 * if there were no entitlements.
609 cs_entitlements_blob_get(proc_t p
, void **out_start
, size_t *out_length
)
611 struct cs_blob
*csblob
;
616 if (NULL
== p
->p_textvp
)
619 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
622 return csblob_get_entitlements(csblob
, out_start
, out_length
);
625 /* Retrieve the codesign identity for a process.
627 * NULL an error occured
628 * string the cs_identity
632 cs_identity_get(proc_t p
)
634 struct cs_blob
*csblob
;
636 if (NULL
== p
->p_textvp
)
639 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
642 return csblob_get_identity(csblob
);
646 /* Retrieve the codesign blob for a process.
648 * EINVAL no text vnode associated with the process
649 * 0 no error occurred
651 * On success, out_start and out_length will point to the
652 * cms blob if found; or will be set to NULL/zero
653 * if there were no blob.
657 cs_blob_get(proc_t p
, void **out_start
, size_t *out_length
)
659 struct cs_blob
*csblob
;
664 if (NULL
== p
->p_textvp
)
667 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
670 *out_start
= (void *)csblob
->csb_mem_kaddr
;
671 *out_length
= csblob
->csb_mem_size
;
677 * return cshash of a process, cdhash is of size CS_CDHASH_LEN
681 cs_get_cdhash(struct proc
*p
)
683 struct cs_blob
*csblob
;
685 if (NULL
== p
->p_textvp
)
688 if ((csblob
= ubc_cs_blob_get(p
->p_textvp
, -1, p
->p_textoff
)) == NULL
)
691 return csblob
->csb_cdhash
;