1/*
2 * Copyright (c) 2000-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
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>
38
39#include <sys/fcntl.h>
40#include <sys/file.h>
41#include <sys/file_internal.h>
42#include <sys/kauth.h>
43#include <sys/mount.h>
44#include <sys/msg.h>
45#include <sys/proc.h>
46#include <sys/socketvar.h>
47#include <sys/vnode.h>
48#include <sys/vnode_internal.h>
49
50#include <sys/ubc.h>
51#include <sys/ubc_internal.h>
52
53#include <security/mac.h>
54#include <security/mac_policy.h>
55#include <security/mac_framework.h>
56
57#include <mach/mach_types.h>
58#include <mach/vm_map.h>
59#include <mach/mach_vm.h>
60
61#include <kern/kern_types.h>
62#include <kern/task.h>
63
64#include <vm/vm_map.h>
65#include <vm/vm_kern.h>
66
67
68#include <kern/assert.h>
69
70#include <pexpert/pexpert.h>
71
72#include <mach/shared_region.h>
73
74#include <libkern/section_keywords.h>
75
76unsigned long cs_procs_killed = 0;
77unsigned long cs_procs_invalidated = 0;
78
79int cs_force_kill = 0;
80int cs_force_hard = 0;
81int cs_debug = 0;
82// If set, AMFI will error out early on unsigned code, before evaluation the normal policy.
83int cs_debug_fail_on_unsigned_code = 0;
84// If the previous mode is enabled, we count the resulting failures here.
85unsigned int cs_debug_unsigned_exec_failures = 0;
86unsigned int cs_debug_unsigned_mmap_failures = 0;
87
88#if SECURE_KERNEL
89/*
90Here we split cs_enforcement_enable into cs_system_enforcement_enable and cs_process_enforcement_enable
91
92cs_system_enforcement_enable governs whether or not system level code signing enforcement mechanisms
93are applied on the system. Today, the only such mechanism is code signing enforcement of the dyld shared
94cache.
95
96cs_process_enforcement_enable governs whether code signing enforcement mechanisms are applied to all
97processes or only those that opt into such enforcement.
98
99(On iOS and related, both of these are set by default. On macOS, only cs_system_enforcement_enable
100is set by default. Processes can then be opted into code signing enforcement on a case by case basis.)
101 */
102const int cs_system_enforcement_enable = 1;
103const int cs_process_enforcement_enable = 1;
104const int cs_library_val_enable = 1;
105#else /* !SECURE_KERNEL */
106int cs_enforcement_panic=0;
107int cs_relax_platform_task_ports = 0;
108
109#if CONFIG_ENFORCE_SIGNED_CODE
110#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1
111#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 1
112#else
113#define DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE 1
114#define DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE 0
115#endif
116SECURITY_READ_ONLY_LATE(int) cs_system_enforcement_enable = DEFAULT_CS_SYSTEM_ENFORCEMENT_ENABLE;
117SECURITY_READ_ONLY_LATE(int) cs_process_enforcement_enable = DEFAULT_CS_PROCESS_ENFORCEMENT_ENABLE;
118
119#if CONFIG_ENFORCE_LIBRARY_VALIDATION
120#define DEFAULT_CS_LIBRARY_VA_ENABLE 1
121#else
122#define DEFAULT_CS_LIBRARY_VA_ENABLE 0
123#endif
124SECURITY_READ_ONLY_LATE(int) cs_library_val_enable = DEFAULT_CS_LIBRARY_VA_ENABLE;
125
126#endif /* !SECURE_KERNEL */
127int cs_all_vnodes = 0;
128
129static lck_grp_t *cs_lockgrp;
130
131SYSCTL_INT(_vm, OID_AUTO, cs_force_kill, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_kill, 0, "");
132SYSCTL_INT(_vm, OID_AUTO, cs_force_hard, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_hard, 0, "");
133SYSCTL_INT(_vm, OID_AUTO, cs_debug, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_debug, 0, "");
134SYSCTL_INT(_vm, OID_AUTO, cs_debug_fail_on_unsigned_code, CTLFLAG_RW | CTLFLAG_LOCKED,
135 &cs_debug_fail_on_unsigned_code, 0, "");
136SYSCTL_UINT(_vm, OID_AUTO, cs_debug_unsigned_exec_failures, CTLFLAG_RD | CTLFLAG_LOCKED,
137 &cs_debug_unsigned_exec_failures, 0, "");
138SYSCTL_UINT(_vm, OID_AUTO, cs_debug_unsigned_mmap_failures, CTLFLAG_RD | CTLFLAG_LOCKED,
139 &cs_debug_unsigned_mmap_failures, 0, "");
140
141SYSCTL_INT(_vm, OID_AUTO, cs_all_vnodes, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_all_vnodes, 0, "");
142
143#if !SECURE_KERNEL
144SYSCTL_INT(_vm, OID_AUTO, cs_system_enforcement, CTLFLAG_RD | CTLFLAG_LOCKED, &cs_system_enforcement_enable, 0, "");
145SYSCTL_INT(_vm, OID_AUTO, cs_process_enforcement, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_process_enforcement_enable, 0, "");
146SYSCTL_INT(_vm, OID_AUTO, cs_enforcement_panic, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_panic, 0, "");
147
148#if !CONFIG_ENFORCE_LIBRARY_VALIDATION
149SYSCTL_INT(_vm, OID_AUTO, cs_library_validation, CTLFLAG_RD | CTLFLAG_LOCKED, &cs_library_val_enable, 0, "");
150#endif
151#endif /* !SECURE_KERNEL */
152
153int panic_on_cs_killed = 0;
154
155void
156cs_init(void)
157{
158#if MACH_ASSERT
159#if PLATFORM_WatchOS || __x86_64__
160 panic_on_cs_killed = 1;
161#endif /* watchos || x86_64 */
162#endif /* MACH_ASSERT */
163 PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed,
164 sizeof (panic_on_cs_killed));
165#if !SECURE_KERNEL
166 int disable_cs_enforcement = 0;
167 PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement,
168 sizeof (disable_cs_enforcement));
169 if (disable_cs_enforcement && PE_i_can_has_debugger(NULL) != 0) {
170 cs_system_enforcement_enable = 0;
171 cs_process_enforcement_enable = 0;
172 } else {
173 int panic = 0;
174 PE_parse_boot_argn("cs_enforcement_panic", &panic, sizeof(panic));
175 cs_enforcement_panic = (panic != 0);
176 }
177
178 PE_parse_boot_argn("cs_relax_platform_task_ports",
179 &cs_relax_platform_task_ports,
180 sizeof(cs_relax_platform_task_ports));
181
182 PE_parse_boot_argn("cs_debug", &cs_debug, sizeof (cs_debug));
183
184#if !CONFIG_ENFORCE_LIBRARY_VALIDATION
185 PE_parse_boot_argn("cs_library_val_enable", &cs_library_val_enable,
186 sizeof (cs_library_val_enable));
187#endif
188#endif /* !SECURE_KERNEL */
189
190 lck_grp_attr_t *attr = lck_grp_attr_alloc_init();
191 cs_lockgrp = lck_grp_alloc_init("KERNCS", attr);
192 lck_grp_attr_free(attr);
193}
194
195int
196cs_allow_invalid(struct proc *p)
197{
198#if MACH_ASSERT
199 lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED);
200#endif
201#if CONFIG_MACF
202 /* There needs to be a MAC policy to implement this hook, or else the
203 * kill bits will be cleared here every time. If we have
204 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
205 * implementing the hook.
206 */
207 if( 0 != mac_proc_check_run_cs_invalid(p)) {
208 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
209 "not allowed: pid %d\n",
210 p->p_pid);
211 return 0;
212 }
213 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
214 "allowed: pid %d\n",
215 p->p_pid);
216 proc_lock(p);
217 p->p_csflags &= ~(CS_KILL | CS_HARD);
218 if (p->p_csflags & CS_VALID)
219 {
220 p->p_csflags |= CS_DEBUGGED;
221 }
222
223 proc_unlock(p);
224
225 vm_map_switch_protect(get_task_map(p->task), FALSE);
226#endif
227 return (p->p_csflags & (CS_KILL | CS_HARD)) == 0;
228}
229
230int
231cs_invalid_page(addr64_t vaddr, boolean_t *cs_killed)
232{
233 struct proc *p;
234 int send_kill = 0, retval = 0, verbose = cs_debug;
235 uint32_t csflags;
236
237 p = current_proc();
238
239 if (verbose)
240 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
241 vaddr, p->p_pid, p->p_comm);
242
243 proc_lock(p);
244
245 /* XXX for testing */
246 if (cs_force_kill)
247 p->p_csflags |= CS_KILL;
248 if (cs_force_hard)
249 p->p_csflags |= CS_HARD;
250
251 /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
252 if (p->p_csflags & CS_KILL) {
253 p->p_csflags |= CS_KILLED;
254 cs_procs_killed++;
255 send_kill = 1;
256 retval = 1;
257 }
258
259 /* CS_HARD means fail the mapping operation so the process stays valid. */
260 if (p->p_csflags & CS_HARD) {
261 retval = 1;
262 } else {
263 if (p->p_csflags & CS_VALID) {
264 p->p_csflags &= ~CS_VALID;
265 cs_procs_invalidated++;
266 verbose = 1;
267 }
268 }
269 csflags = p->p_csflags;
270 proc_unlock(p);
271
272 if (verbose)
273 printf("CODE SIGNING: cs_invalid_page(0x%llx): "
274 "p=%d[%s] final status 0x%x, %s page%s\n",
275 vaddr, p->p_pid, p->p_comm, p->p_csflags,
276 retval ? "denying" : "allowing (remove VALID)",
277 send_kill ? " sending SIGKILL" : "");
278
279 if (send_kill) {
280 /* We will set the exit reason for the thread later */
281 threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS, FALSE);
282 if (cs_killed) {
283 *cs_killed = TRUE;
284 }
285 } else if (cs_killed) {
286 *cs_killed = FALSE;
287 }
288
289
290 return retval;
291}
292
293/*
294 * Assumes p (if passed in) is locked with proc_lock().
295 */
296
297int
298cs_process_enforcement(struct proc *p)
299{
300
301 if (cs_process_enforcement_enable)
302 return 1;
303
304 if (p == NULL)
305 p = current_proc();
306
307 if (p != NULL && (p->p_csflags & CS_ENFORCEMENT))
308 return 1;
309
310 return 0;
311}
312
313int
314cs_process_global_enforcement(void)
315{
316 return cs_process_enforcement_enable ? 1 : 0;
317}
318
319int
320cs_system_enforcement(void)
321{
322 return cs_system_enforcement_enable ? 1 : 0;
323}
324
325/*
326 * Returns whether a given process is still valid.
327 */
328int
329cs_valid(struct proc *p)
330{
331
332 if (p == NULL)
333 p = current_proc();
334
335 if (p != NULL && (p->p_csflags & CS_VALID))
336 return 1;
337
338 return 0;
339}
340
341/*
342 * Library validation functions
343 */
344int
345cs_require_lv(struct proc *p)
346{
347
348 if (cs_library_val_enable)
349 return 1;
350
351 if (p == NULL)
352 p = current_proc();
353
354 if (p != NULL && (p->p_csflags & CS_REQUIRE_LV))
355 return 1;
356
357 return 0;
358}
359
360int
361csproc_forced_lv(struct proc* p)
362{
363 if (p == NULL) {
364 p = current_proc();
365 }
366 if (p != NULL && (p->p_csflags & CS_FORCED_LV)) {
367 return 1;
368 }
369 return 0;
370}
371
372/*
373 * <rdar://problem/24634089> added to allow system level library
374 * validation check at mac_cred_label_update_execve time
375 */
376int
377cs_system_require_lv(void)
378{
379 return cs_library_val_enable ? 1 : 0;
380}
381
382/*
383 * Function: csblob_get_base_offset
384 *
385 * Description: This function returns the base offset into the (possibly universal) binary
386 * for a given blob.
387*/
388
389off_t
390csblob_get_base_offset(struct cs_blob *blob)
391{
392 return blob->csb_base_offset;
393}
394
395/*
396 * Function: csblob_get_size
397 *
398 * Description: This function returns the size of a given blob.
399*/
400
401vm_size_t
402csblob_get_size(struct cs_blob *blob)
403{
404 return blob->csb_mem_size;
405}
406
407/*
408 * Function: csblob_get_addr
409 *
410 * Description: This function returns the address of a given blob.
411*/
412
413vm_address_t
414csblob_get_addr(struct cs_blob *blob)
415{
416 return blob->csb_mem_kaddr;
417}
418
419/*
420 * Function: csblob_get_platform_binary
421 *
422 * Description: This function returns true if the binary is
423 * in the trust cache.
424*/
425
426int
427csblob_get_platform_binary(struct cs_blob *blob)
428{
429 if (blob && blob->csb_platform_binary)
430 return 1;
431 return 0;
432}
433
434/*
435 * Function: csblob_get_flags
436 *
437 * Description: This function returns the flags for a given blob
438*/
439
440unsigned int
441csblob_get_flags(struct cs_blob *blob)
442{
443 return blob->csb_flags;
444}
445
446/*
447 * Function: csblob_get_hashtype
448 *
449 * Description: This function returns the hash type for a given blob
450*/
451
452uint8_t
453csblob_get_hashtype(struct cs_blob const * const blob)
454{
455 return blob->csb_hashtype != NULL ? cs_hash_type(blob->csb_hashtype) : 0;
456}
457
458/*
459 * Function: csproc_get_blob
460 *
461 * Description: This function returns the cs_blob
462 * for the process p
463 */
464struct cs_blob *
465csproc_get_blob(struct proc *p)
466{
467 if (NULL == p)
468 return NULL;
469
470 if (NULL == p->p_textvp)
471 return NULL;
472
473 if ((p->p_csflags & CS_SIGNED) == 0) {
474 return NULL;
475 }
476
477 return ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff);
478}
479
480/*
481 * Function: csvnode_get_blob
482 *
483 * Description: This function returns the cs_blob
484 * for the vnode vp
485 */
486struct cs_blob *
487csvnode_get_blob(struct vnode *vp, off_t offset)
488{
489 return ubc_cs_blob_get(vp, -1, offset);
490}
491
492/*
493 * Function: csblob_get_teamid
494 *
495 * Description: This function returns a pointer to the
496 * team id of csblob
497*/
498const char *
499csblob_get_teamid(struct cs_blob *csblob)
500{
501 return csblob->csb_teamid;
502}
503
504/*
505 * Function: csblob_get_identity
506 *
507 * Description: This function returns a pointer to the
508 * identity string
509 */
510const char *
511csblob_get_identity(struct cs_blob *csblob)
512{
513 const CS_CodeDirectory *cd;
514
515 cd = (const CS_CodeDirectory *)csblob_find_blob(csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY);
516 if (cd == NULL)
517 return NULL;
518
519 if (cd->identOffset == 0)
520 return NULL;
521
522 return ((const char *)cd) + ntohl(cd->identOffset);
523}
524
525/*
526 * Function: csblob_get_cdhash
527 *
528 * Description: This function returns a pointer to the
529 * cdhash of csblob (20 byte array)
530 */
531const uint8_t *
532csblob_get_cdhash(struct cs_blob *csblob)
533{
534 return csblob->csb_cdhash;
535}
536
537/*
538 * Function: csblob_get_signer_type
539 *
540 * Description: This function returns the signer type
541 * as an integer
542 */
543unsigned int
544csblob_get_signer_type(struct cs_blob *csblob)
545{
546 return csblob->csb_signer_type;
547}
548
549void *
550csblob_entitlements_dictionary_copy(struct cs_blob *csblob)
551{
552 if (!csblob->csb_entitlements) return NULL;
553 osobject_retain(csblob->csb_entitlements);
554 return csblob->csb_entitlements;
555}
556
557void
558csblob_entitlements_dictionary_set(struct cs_blob *csblob, void * entitlements)
559{
560 assert(csblob->csb_entitlements == NULL);
561 if (entitlements) osobject_retain(entitlements);
562 csblob->csb_entitlements = entitlements;
563}
564
565/*
566 * Function: csproc_get_teamid
567 *
568 * Description: This function returns a pointer to the
569 * team id of the process p
570*/
571const char *
572csproc_get_teamid(struct proc *p)
573{
574 struct cs_blob *csblob;
575
576 csblob = csproc_get_blob(p);
577 if (csblob == NULL)
578 return NULL;
579
580 return csblob_get_teamid(csblob);
581}
582
583/*
584 * Function: csproc_get_signer_type
585 *
586 * Description: This function returns the signer type
587 * of the process p
588*/
589unsigned int
590csproc_get_signer_type(struct proc *p)
591{
592 struct cs_blob *csblob;
593
594 csblob = csproc_get_blob(p);
595 if (csblob == NULL)
596 return CS_SIGNER_TYPE_UNKNOWN;
597
598 return csblob_get_signer_type(csblob);
599}
600
601/*
602 * Function: csvnode_get_teamid
603 *
604 * Description: This function returns a pointer to the
605 * team id of the binary at the given offset in vnode vp
606*/
607const char *
608csvnode_get_teamid(struct vnode *vp, off_t offset)
609{
610 struct cs_blob *csblob;
611
612 if (vp == NULL)
613 return NULL;
614
615 csblob = ubc_cs_blob_get(vp, -1, offset);
616 if (csblob == NULL)
617 return NULL;
618
619 return csblob_get_teamid(csblob);
620}
621
622/*
623 * Function: csproc_get_platform_binary
624 *
625 * Description: This function returns the value
626 * of the platform_binary field for proc p
627 */
628int
629csproc_get_platform_binary(struct proc *p)
630{
631 struct cs_blob *csblob;
632
633 csblob = csproc_get_blob(p);
634
635 /* If there is no csblob this returns 0 because
636 it is true that it is not a platform binary */
637 return (csblob == NULL) ? 0 : csblob->csb_platform_binary;
638}
639
640int
641csproc_get_platform_path(struct proc *p)
642{
643 struct cs_blob *csblob;
644
645 csblob = csproc_get_blob(p);
646
647 return (csblob == NULL) ? 0 : csblob->csb_platform_path;
648}
649
650#if DEVELOPMENT || DEBUG
651void
652csproc_clear_platform_binary(struct proc *p)
653{
654 struct cs_blob *csblob = csproc_get_blob(p);
655
656 if (csblob == NULL) {
657 return;
658 }
659
660 if (cs_debug) {
661 printf("clearing platform binary on proc/task: pid = %d\n", p->p_pid);
662 }
663
664 csblob->csb_platform_binary = 0;
665 csblob->csb_platform_path = 0;
666 task_set_platform_binary(proc_task(p), FALSE);
667}
668#endif
669
670void
671csproc_disable_enforcement(struct proc* __unused p)
672{
673#if !CONFIG_ENFORCE_SIGNED_CODE
674 if (p != NULL) {
675 proc_lock(p);
676 p->p_csflags &= (~CS_ENFORCEMENT);
677 proc_unlock(p);
678 }
679#endif
680}
681
682/* Function: csproc_mark_invalid_allowed
683 *
684 * Description: Mark the process as being allowed to go invalid. Called as part of
685 * task_for_pid and ptrace policy. Note CS_INVALID_ALLOWED only matters for
686 * processes that have been opted into CS_ENFORCEMENT.
687 */
688void
689csproc_mark_invalid_allowed(struct proc* __unused p)
690{
691#if !CONFIG_ENFORCE_SIGNED_CODE
692 if (p != NULL) {
693 proc_lock(p);
694 p->p_csflags |= CS_INVALID_ALLOWED;
695 proc_unlock(p);
696 }
697#endif
698}
699
700/*
701 * Function: csproc_check_invalid_allowed
702 *
703 * Description: Returns 1 if the process has been marked as allowed to go invalid
704 * because it gave its task port to an allowed process.
705 */
706int
707csproc_check_invalid_allowed(struct proc* __unused p)
708{
709#if !CONFIG_ENFORCE_SIGNED_CODE
710 if (p == NULL) {
711 p = current_proc();
712 }
713
714 if (p != NULL && (p->p_csflags & CS_INVALID_ALLOWED))
715 return 1;
716#endif
717 return 0;
718}
719
720/*
721 * Function: csproc_get_prod_signed
722 *
723 * Description: Returns 1 if process is not signed with a developer identity.
724 * Note the inverted meaning from the cs_flag to make the error case safer.
725 * Will go away with rdar://problem/28322552.
726 */
727int
728csproc_get_prod_signed(struct proc *p)
729{
730 return ((p->p_csflags & CS_DEV_CODE) == 0);
731}
732
733
734/*
735 * Function: csfg_get_platform_binary
736 *
737 * Description: This function returns the
738 * platform binary field for the
739 * fileglob fg
740 */
741int
742csfg_get_platform_binary(struct fileglob *fg)
743{
744 int platform_binary = 0;
745 struct ubc_info *uip;
746 vnode_t vp;
747
748 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
749 return 0;
750
751 vp = (struct vnode *)fg->fg_data;
752 if (vp == NULL)
753 return 0;
754
755 vnode_lock(vp);
756 if (!UBCINFOEXISTS(vp))
757 goto out;
758
759 uip = vp->v_ubcinfo;
760 if (uip == NULL)
761 goto out;
762
763 if (uip->cs_blobs == NULL)
764 goto out;
765
766 /* It is OK to extract the teamid from the first blob
767 because all blobs of a vnode must have the same teamid */
768 platform_binary = uip->cs_blobs->csb_platform_binary;
769out:
770 vnode_unlock(vp);
771
772 return platform_binary;
773}
774
775uint8_t *
776csfg_get_cdhash(struct fileglob *fg, uint64_t offset, size_t *cdhash_size)
777{
778 vnode_t vp;
779
780 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
781 return NULL;
782
783 vp = (struct vnode *)fg->fg_data;
784 if (vp == NULL)
785 return NULL;
786
787 struct cs_blob *csblob = NULL;
788 if ((csblob = ubc_cs_blob_get(vp, -1, offset)) == NULL)
789 return NULL;
790
791 if (cdhash_size)
792 *cdhash_size = CS_CDHASH_LEN;
793
794 return csblob->csb_cdhash;
795}
796
797/*
798 * Function: csfg_get_signer_type
799 *
800 * Description: This returns the signer type
801 * for the fileglob fg
802 */
803unsigned int
804csfg_get_signer_type(struct fileglob *fg)
805{
806 struct ubc_info *uip;
807 unsigned int signer_type = CS_SIGNER_TYPE_UNKNOWN;
808 vnode_t vp;
809
810 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
811 return CS_SIGNER_TYPE_UNKNOWN;
812
813 vp = (struct vnode *)fg->fg_data;
814 if (vp == NULL)
815 return CS_SIGNER_TYPE_UNKNOWN;
816
817 vnode_lock(vp);
818 if (!UBCINFOEXISTS(vp))
819 goto out;
820
821 uip = vp->v_ubcinfo;
822 if (uip == NULL)
823 goto out;
824
825 if (uip->cs_blobs == NULL)
826 goto out;
827
828 /* It is OK to extract the signer type from the first blob,
829 because all blobs of a vnode must have the same signer type. */
830 signer_type = uip->cs_blobs->csb_signer_type;
831out:
832 vnode_unlock(vp);
833
834 return signer_type;
835}
836
837/*
838 * Function: csfg_get_teamid
839 *
840 * Description: This returns a pointer to
841 * the teamid for the fileglob fg
842 */
843const char *
844csfg_get_teamid(struct fileglob *fg)
845{
846 struct ubc_info *uip;
847 const char *str = NULL;
848 vnode_t vp;
849
850 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
851 return NULL;
852
853 vp = (struct vnode *)fg->fg_data;
854 if (vp == NULL)
855 return NULL;
856
857 vnode_lock(vp);
858 if (!UBCINFOEXISTS(vp))
859 goto out;
860
861 uip = vp->v_ubcinfo;
862 if (uip == NULL)
863 goto out;
864
865 if (uip->cs_blobs == NULL)
866 goto out;
867
868 /* It is OK to extract the teamid from the first blob
869 because all blobs of a vnode must have the same teamid */
870 str = uip->cs_blobs->csb_teamid;
871out:
872 vnode_unlock(vp);
873
874 return str;
875}
876
877/*
878 * Function: csfg_get_prod_signed
879 *
880 * Description: Returns 1 if code is not signed with a developer identity.
881 * Note the inverted meaning from the cs_flag to make the error case safer.
882 * Will go away with rdar://problem/28322552.
883 */
884int
885csfg_get_prod_signed(struct fileglob *fg)
886{
887 struct ubc_info *uip;
888 vnode_t vp;
889 int prod_signed = 0;
890
891 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
892 return 0;
893
894 vp = (struct vnode *)fg->fg_data;
895 if (vp == NULL)
896 return 0;
897
898 vnode_lock(vp);
899 if (!UBCINFOEXISTS(vp))
900 goto out;
901
902 uip = vp->v_ubcinfo;
903 if (uip == NULL)
904 goto out;
905
906 if (uip->cs_blobs == NULL)
907 goto out;
908
909 /* It is OK to extract the flag from the first blob
910 because all blobs of a vnode must have the same cs_flags */
911 prod_signed = (uip->cs_blobs->csb_flags & CS_DEV_CODE) == 0;
912out:
913 vnode_unlock(vp);
914
915 return prod_signed;
916}
917
918/*
919 * Function: csfg_get_identity
920 *
921 * Description: This function returns the codesign identity
922 * for the fileglob
923 */
924const char *
925csfg_get_identity(struct fileglob *fg, off_t offset)
926{
927 vnode_t vp;
928 struct cs_blob *csblob = NULL;
929
930 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
931 return NULL;
932
933 vp = (struct vnode *)fg->fg_data;
934 if (vp == NULL)
935 return NULL;
936
937 csblob = ubc_cs_blob_get(vp, -1, offset);
938 if (csblob == NULL)
939 return NULL;
940
941 return csblob_get_identity(csblob);
942}
943
944/*
945 * Function: csfg_get_platform_identifier
946 *
947 * Description: This function returns the codesign platform
948 * identifier for the fileglob. Assumes the fileproc
949 * is being held busy to keep the fileglob consistent.
950 */
951uint8_t
952csfg_get_platform_identifier(struct fileglob *fg, off_t offset)
953{
954 vnode_t vp;
955
956 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
957 return 0;
958
959 vp = (struct vnode *)fg->fg_data;
960 if (vp == NULL)
961 return 0;
962
963 return csvnode_get_platform_identifier(vp, offset);
964}
965
966/*
967 * Function: csvnode_get_platform_identifier
968 *
969 * Description: This function returns the codesign platform
970 * identifier for the vnode. Assumes a vnode reference
971 * is held.
972 */
973uint8_t
974csvnode_get_platform_identifier(struct vnode *vp, off_t offset)
975{
976 struct cs_blob *csblob;
977 const CS_CodeDirectory *code_dir;
978
979 csblob = ubc_cs_blob_get(vp, -1, offset);
980 if (csblob == NULL)
981 return 0;
982
983 code_dir = csblob->csb_cd;
984 if (code_dir == NULL || ntohl(code_dir->length) < 8)
985 return 0;
986
987 return code_dir->platform;
988}
989
990/*
991 * Function: csproc_get_platform_identifier
992 *
993 * Description: This function returns the codesign platform
994 * identifier for the proc. Assumes proc will remain
995 * valid through call.
996 */
997uint8_t
998csproc_get_platform_identifier(struct proc *p)
999{
1000 if (NULL == p->p_textvp)
1001 return 0;
1002
1003 return csvnode_get_platform_identifier(p->p_textvp, p->p_textoff);
1004}
1005
1006uint32_t
1007cs_entitlement_flags(struct proc *p)
1008{
1009 return (p->p_csflags & CS_ENTITLEMENT_FLAGS);
1010}
1011
1012int
1013cs_restricted(struct proc *p)
1014{
1015 return (p->p_csflags & CS_RESTRICT) ? 1 : 0;
1016}
1017
1018int
1019csproc_hardened_runtime(struct proc* p)
1020{
1021 return (p->p_csflags & CS_RUNTIME) ? 1 : 0;
1022}
1023
1024/*
1025 * Function: csfg_get_path
1026 *
1027 * Description: This populates the buffer passed in
1028 * with the path of the vnode
1029 * When calling this, the fileglob
1030 * cannot go away. The caller must have a
1031 * a reference on the fileglob or fileproc
1032 */
1033int
1034csfg_get_path(struct fileglob *fg, char *path, int *len)
1035{
1036 vnode_t vp = NULL;
1037
1038 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
1039 return -1;
1040
1041 vp = (struct vnode *)fg->fg_data;
1042
1043 /* vn_getpath returns 0 for success,
1044 or an error code */
1045 return vn_getpath(vp, path, len);
1046}
1047
1048/* Retrieve the entitlements blob for a process.
1049 * Returns:
1050 * EINVAL no text vnode associated with the process
1051 * EBADEXEC invalid code signing data
1052 * 0 no error occurred
1053 *
1054 * On success, out_start and out_length will point to the
1055 * entitlements blob if found; or will be set to NULL/zero
1056 * if there were no entitlements.
1057 */
1058
1059int
1060cs_entitlements_blob_get(proc_t p, void **out_start, size_t *out_length)
1061{
1062 struct cs_blob *csblob;
1063
1064 *out_start = NULL;
1065 *out_length = 0;
1066
1067 if ((p->p_csflags & CS_SIGNED) == 0) {
1068 return 0;
1069 }
1070
1071 if (NULL == p->p_textvp)
1072 return EINVAL;
1073
1074 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
1075 return 0;
1076
1077 return csblob_get_entitlements(csblob, out_start, out_length);
1078}
1079
1080/* Retrieve the codesign identity for a process.
1081 * Returns:
1082 * NULL an error occured
1083 * string the cs_identity
1084 */
1085
1086const char *
1087cs_identity_get(proc_t p)
1088{
1089 struct cs_blob *csblob;
1090
1091 if ((p->p_csflags & CS_SIGNED) == 0) {
1092 return NULL;
1093 }
1094
1095 if (NULL == p->p_textvp)
1096 return NULL;
1097
1098 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
1099 return NULL;
1100
1101 return csblob_get_identity(csblob);
1102}
1103
1104/*
1105 * DO NOT USE THIS FUNCTION!
1106 * Use the properly guarded csproc_get_blob instead.
1107 *
1108 * This is currently here to allow detached signatures to work
1109 * properly. The only user of this function is also checking
1110 * for CS_VALID.
1111 */
1112
1113int
1114cs_blob_get(proc_t p, void **out_start, size_t *out_length)
1115{
1116 struct cs_blob *csblob;
1117
1118 *out_start = NULL;
1119 *out_length = 0;
1120
1121 if (NULL == p->p_textvp)
1122 return EINVAL;
1123
1124 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
1125 return 0;
1126
1127 *out_start = (void *)csblob->csb_mem_kaddr;
1128 *out_length = csblob->csb_mem_size;
1129
1130 return 0;
1131}
1132
1133/*
1134 * return cshash of a process, cdhash is of size CS_CDHASH_LEN
1135 */
1136
1137uint8_t *
1138cs_get_cdhash(struct proc *p)
1139{
1140 struct cs_blob *csblob;
1141
1142 if ((p->p_csflags & CS_SIGNED) == 0) {
1143 return NULL;
1144 }
1145
1146 if (NULL == p->p_textvp)
1147 return NULL;
1148
1149 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
1150 return NULL;
1151
1152 return csblob->csb_cdhash;
1153}
1154