]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_cs.c
c15dd4f118ed0f1df620e90bf8b4b219b45be412
[apple/xnu.git] / bsd / kern / kern_cs.c
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
76 unsigned long cs_procs_killed = 0;
77 unsigned long cs_procs_invalidated = 0;
78
79 int cs_force_kill = 0;
80 int cs_force_hard = 0;
81 int cs_debug = 0;
82 #if SECURE_KERNEL
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;
87
88 #if CONFIG_ENFORCE_SIGNED_CODE
89 #define DEFAULT_CS_ENFORCEMENT_ENABLE 1
90 #else
91 #define DEFAULT_CS_ENFORCEMENT_ENABLE 0
92 #endif
93 SECURITY_READ_ONLY_LATE(int) cs_enforcement_enable = DEFAULT_CS_ENFORCEMENT_ENABLE;
94
95 #if CONFIG_ENFORCE_LIBRARY_VALIDATION
96 #define DEFAULT_CS_LIBRARY_VA_ENABLE 1
97 #else
98 #define DEFAULT_CS_LIBRARY_VA_ENABLE 0
99 #endif
100 SECURITY_READ_ONLY_LATE(int) cs_library_val_enable = DEFAULT_CS_LIBRARY_VA_ENABLE;
101
102 #endif /* !SECURE_KERNEL */
103 int cs_all_vnodes = 0;
104
105 static lck_grp_t *cs_lockgrp;
106
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, "");
110
111 SYSCTL_INT(_vm, OID_AUTO, cs_all_vnodes, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_all_vnodes, 0, "");
112
113 #if !SECURE_KERNEL
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, "");
116
117 #if !CONFIG_ENFORCE_LIBRARY_VALIDATION
118 SYSCTL_INT(_vm, OID_AUTO, cs_library_validation, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_library_val_enable, 0, "");
119 #endif
120 #endif /* !SECURE_KERNEL */
121
122 int panic_on_cs_killed = 0;
123 void
124 cs_init(void)
125 {
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));
131 #if !SECURE_KERNEL
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;
137 } else {
138 int panic = 0;
139 PE_parse_boot_argn("cs_enforcement_panic", &panic, sizeof(panic));
140 cs_enforcement_panic = (panic != 0);
141 }
142
143 PE_parse_boot_argn("cs_debug", &cs_debug, sizeof (cs_debug));
144
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));
148 #endif
149 #endif /* !SECURE_KERNEL */
150
151 lck_grp_attr_t *attr = lck_grp_attr_alloc_init();
152 cs_lockgrp = lck_grp_alloc_init("KERNCS", attr);
153 }
154
155 int
156 cs_allow_invalid(struct proc *p)
157 {
158 #if MACH_ASSERT
159 lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED);
160 #endif
161 #if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE
162 /* There needs to be a MAC policy to implement this hook, or else the
163 * kill bits will be cleared here every time. If we have
164 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
165 * implementing the hook.
166 */
167 if( 0 != mac_proc_check_run_cs_invalid(p)) {
168 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
169 "not allowed: pid %d\n",
170 p->p_pid);
171 return 0;
172 }
173 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
174 "allowed: pid %d\n",
175 p->p_pid);
176 proc_lock(p);
177 p->p_csflags &= ~(CS_KILL | CS_HARD);
178 if (p->p_csflags & CS_VALID)
179 {
180 p->p_csflags |= CS_DEBUGGED;
181 }
182 proc_unlock(p);
183 vm_map_switch_protect(get_task_map(p->task), FALSE);
184 #endif
185 return (p->p_csflags & (CS_KILL | CS_HARD)) == 0;
186 }
187
188 int
189 cs_invalid_page(
190 addr64_t vaddr)
191 {
192 struct proc *p;
193 int send_kill = 0, retval = 0, verbose = cs_debug;
194 uint32_t csflags;
195
196 p = current_proc();
197
198 if (verbose)
199 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
200 vaddr, p->p_pid, p->p_comm);
201
202 proc_lock(p);
203
204 /* XXX for testing */
205 if (cs_force_kill)
206 p->p_csflags |= CS_KILL;
207 if (cs_force_hard)
208 p->p_csflags |= CS_HARD;
209
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 if (panic_on_cs_killed &&
213 vaddr >= SHARED_REGION_BASE &&
214 vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) {
215 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr, p);
216 }
217 p->p_csflags |= CS_KILLED;
218 cs_procs_killed++;
219 send_kill = 1;
220 retval = 1;
221 }
222
223 #if __x86_64__
224 if (panic_on_cs_killed &&
225 vaddr >= SHARED_REGION_BASE &&
226 vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) {
227 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr, p);
228 }
229 #endif /* __x86_64__ */
230
231 /* CS_HARD means fail the mapping operation so the process stays valid. */
232 if (p->p_csflags & CS_HARD) {
233 retval = 1;
234 } else {
235 if (p->p_csflags & CS_VALID) {
236 p->p_csflags &= ~CS_VALID;
237 cs_procs_invalidated++;
238 verbose = 1;
239 }
240 }
241 csflags = p->p_csflags;
242 proc_unlock(p);
243
244 if (verbose)
245 printf("CODE SIGNING: cs_invalid_page(0x%llx): "
246 "p=%d[%s] final status 0x%x, %s page%s\n",
247 vaddr, p->p_pid, p->p_comm, p->p_csflags,
248 retval ? "denying" : "allowing (remove VALID)",
249 send_kill ? " sending SIGKILL" : "");
250
251 if (send_kill)
252 threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS);
253
254
255 return retval;
256 }
257
258 /*
259 * Assumes p (if passed in) is locked with proc_lock().
260 */
261
262 int
263 cs_enforcement(struct proc *p)
264 {
265
266 if (cs_enforcement_enable)
267 return 1;
268
269 if (p == NULL)
270 p = current_proc();
271
272 if (p != NULL && (p->p_csflags & CS_ENFORCEMENT))
273 return 1;
274
275 return 0;
276 }
277
278 /*
279 * Library validation functions
280 */
281 int
282 cs_require_lv(struct proc *p)
283 {
284
285 if (cs_library_val_enable)
286 return 1;
287
288 if (p == NULL)
289 p = current_proc();
290
291 if (p != NULL && (p->p_csflags & CS_REQUIRE_LV))
292 return 1;
293
294 return 0;
295 }
296
297 /*
298 * Function: csblob_get_platform_binary
299 *
300 * Description: This function returns true if the binary is
301 * in the trust cache.
302 */
303
304 int
305 csblob_get_platform_binary(struct cs_blob *blob)
306 {
307 if (blob && blob->csb_platform_binary)
308 return 1;
309 return 0;
310 }
311
312 /*
313 * Function: csblob_get_flags
314 *
315 * Description: This function returns the flags for a given blob
316 */
317
318 unsigned int
319 csblob_get_flags(struct cs_blob *blob)
320 {
321 if (blob)
322 return blob->csb_flags;
323 return 0;
324 }
325
326 /*
327 * Function: csproc_get_blob
328 *
329 * Description: This function returns the cs_blob
330 * for the process p
331 */
332 struct cs_blob *
333 csproc_get_blob(struct proc *p)
334 {
335 if (NULL == p)
336 return NULL;
337
338 if (NULL == p->p_textvp)
339 return NULL;
340
341 return ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff);
342 }
343
344 /*
345 * Function: csproc_get_blob
346 *
347 * Description: This function returns the cs_blob
348 * for the vnode vp
349 */
350 struct cs_blob *
351 csvnode_get_blob(struct vnode *vp, off_t offset)
352 {
353 return ubc_cs_blob_get(vp, -1, offset);
354 }
355
356 /*
357 * Function: csblob_get_teamid
358 *
359 * Description: This function returns a pointer to the
360 * team id of csblob
361 */
362 const char *
363 csblob_get_teamid(struct cs_blob *csblob)
364 {
365 return csblob->csb_teamid;
366 }
367
368 /*
369 * Function: csblob_get_identity
370 *
371 * Description: This function returns a pointer to the
372 * identity string
373 */
374 const char *
375 csblob_get_identity(struct cs_blob *csblob)
376 {
377 const CS_CodeDirectory *cd;
378
379 cd = (const CS_CodeDirectory *)csblob_find_blob(csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY);
380 if (cd == NULL)
381 return NULL;
382
383 if (cd->identOffset == 0)
384 return NULL;
385
386 return ((const char *)cd) + ntohl(cd->identOffset);
387 }
388
389 /*
390 * Function: csblob_get_cdhash
391 *
392 * Description: This function returns a pointer to the
393 * cdhash of csblob (20 byte array)
394 */
395 const uint8_t *
396 csblob_get_cdhash(struct cs_blob *csblob)
397 {
398 return csblob->csb_cdhash;
399 }
400
401 /*
402 * Function: csproc_get_teamid
403 *
404 * Description: This function returns a pointer to the
405 * team id of the process p
406 */
407 const char *
408 csproc_get_teamid(struct proc *p)
409 {
410 struct cs_blob *csblob;
411
412 csblob = csproc_get_blob(p);
413 if (csblob == NULL)
414 return NULL;
415
416 return csblob_get_teamid(csblob);
417 }
418
419 /*
420 * Function: csvnode_get_teamid
421 *
422 * Description: This function returns a pointer to the
423 * team id of the binary at the given offset in vnode vp
424 */
425 const char *
426 csvnode_get_teamid(struct vnode *vp, off_t offset)
427 {
428 struct cs_blob *csblob;
429
430 if (vp == NULL)
431 return NULL;
432
433 csblob = ubc_cs_blob_get(vp, -1, offset);
434 if (csblob == NULL)
435 return NULL;
436
437 return csblob_get_teamid(csblob);
438 }
439
440 /*
441 * Function: csproc_get_platform_binary
442 *
443 * Description: This function returns the value
444 * of the platform_binary field for proc p
445 */
446 int
447 csproc_get_platform_binary(struct proc *p)
448 {
449 struct cs_blob *csblob;
450
451 csblob = csproc_get_blob(p);
452
453 /* If there is no csblob this returns 0 because
454 it is true that it is not a platform binary */
455 return (csblob == NULL) ? 0 : csblob->csb_platform_binary;
456 }
457
458 int
459 csproc_get_platform_path(struct proc *p)
460 {
461 struct cs_blob *csblob = csproc_get_blob(p);
462
463 return (csblob == NULL) ? 0 : csblob->csb_platform_path;
464 }
465
466 /*
467 * Function: csfg_get_platform_binary
468 *
469 * Description: This function returns the
470 * platform binary field for the
471 * fileglob fg
472 */
473 int
474 csfg_get_platform_binary(struct fileglob *fg)
475 {
476 int platform_binary = 0;
477 struct ubc_info *uip;
478 vnode_t vp;
479
480 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
481 return 0;
482
483 vp = (struct vnode *)fg->fg_data;
484 if (vp == NULL)
485 return 0;
486
487 vnode_lock(vp);
488 if (!UBCINFOEXISTS(vp))
489 goto out;
490
491 uip = vp->v_ubcinfo;
492 if (uip == NULL)
493 goto out;
494
495 if (uip->cs_blobs == NULL)
496 goto out;
497
498 /* It is OK to extract the teamid from the first blob
499 because all blobs of a vnode must have the same teamid */
500 platform_binary = uip->cs_blobs->csb_platform_binary;
501 out:
502 vnode_unlock(vp);
503
504 return platform_binary;
505 }
506
507 uint8_t *
508 csfg_get_cdhash(struct fileglob *fg, uint64_t offset, size_t *cdhash_size)
509 {
510 vnode_t vp;
511
512 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
513 return NULL;
514
515 vp = (struct vnode *)fg->fg_data;
516 if (vp == NULL)
517 return NULL;
518
519 struct cs_blob *csblob = NULL;
520 if ((csblob = ubc_cs_blob_get(vp, -1, offset)) == NULL)
521 return NULL;
522
523 if (cdhash_size)
524 *cdhash_size = CS_CDHASH_LEN;
525
526 return csblob->csb_cdhash;
527 }
528
529 /*
530 * Function: csfg_get_teamid
531 *
532 * Description: This returns a pointer to
533 * the teamid for the fileglob fg
534 */
535 const char *
536 csfg_get_teamid(struct fileglob *fg)
537 {
538 struct ubc_info *uip;
539 const char *str = NULL;
540 vnode_t vp;
541
542 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
543 return NULL;
544
545 vp = (struct vnode *)fg->fg_data;
546 if (vp == NULL)
547 return NULL;
548
549 vnode_lock(vp);
550 if (!UBCINFOEXISTS(vp))
551 goto out;
552
553 uip = vp->v_ubcinfo;
554 if (uip == NULL)
555 goto out;
556
557 if (uip->cs_blobs == NULL)
558 goto out;
559
560 /* It is OK to extract the teamid from the first blob
561 because all blobs of a vnode must have the same teamid */
562 str = uip->cs_blobs->csb_teamid;
563 out:
564 vnode_unlock(vp);
565
566 return str;
567 }
568
569 uint32_t
570 cs_entitlement_flags(struct proc *p)
571 {
572 return (p->p_csflags & CS_ENTITLEMENT_FLAGS);
573 }
574
575 int
576 cs_restricted(struct proc *p)
577 {
578 return (p->p_csflags & CS_RESTRICT) ? 1 : 0;
579 }
580
581 /*
582 * Function: csfg_get_path
583 *
584 * Description: This populates the buffer passed in
585 * with the path of the vnode
586 * When calling this, the fileglob
587 * cannot go away. The caller must have a
588 * a reference on the fileglob or fileproc
589 */
590 int
591 csfg_get_path(struct fileglob *fg, char *path, int *len)
592 {
593 vnode_t vp = NULL;
594
595 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
596 return -1;
597
598 vp = (struct vnode *)fg->fg_data;
599
600 /* vn_getpath returns 0 for success,
601 or an error code */
602 return vn_getpath(vp, path, len);
603 }
604
605 /* Retrieve the entitlements blob for a process.
606 * Returns:
607 * EINVAL no text vnode associated with the process
608 * EBADEXEC invalid code signing data
609 * 0 no error occurred
610 *
611 * On success, out_start and out_length will point to the
612 * entitlements blob if found; or will be set to NULL/zero
613 * if there were no entitlements.
614 */
615
616 int
617 cs_entitlements_blob_get(proc_t p, void **out_start, size_t *out_length)
618 {
619 struct cs_blob *csblob;
620
621 *out_start = NULL;
622 *out_length = 0;
623
624 if (NULL == p->p_textvp)
625 return EINVAL;
626
627 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
628 return 0;
629
630 return csblob_get_entitlements(csblob, out_start, out_length);
631 }
632
633 /* Retrieve the codesign identity for a process.
634 * Returns:
635 * NULL an error occured
636 * string the cs_identity
637 */
638
639 const char *
640 cs_identity_get(proc_t p)
641 {
642 struct cs_blob *csblob;
643
644 if (NULL == p->p_textvp)
645 return NULL;
646
647 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
648 return NULL;
649
650 return csblob_get_identity(csblob);
651 }
652
653
654 /* Retrieve the codesign blob for a process.
655 * Returns:
656 * EINVAL no text vnode associated with the process
657 * 0 no error occurred
658 *
659 * On success, out_start and out_length will point to the
660 * cms blob if found; or will be set to NULL/zero
661 * if there were no blob.
662 */
663
664 int
665 cs_blob_get(proc_t p, void **out_start, size_t *out_length)
666 {
667 struct cs_blob *csblob;
668
669 *out_start = NULL;
670 *out_length = 0;
671
672 if (NULL == p->p_textvp)
673 return EINVAL;
674
675 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
676 return 0;
677
678 *out_start = (void *)csblob->csb_mem_kaddr;
679 *out_length = csblob->csb_mem_size;
680
681 return 0;
682 }
683
684 /*
685 * return cshash of a process, cdhash is of size CS_CDHASH_LEN
686 */
687
688 uint8_t *
689 cs_get_cdhash(struct proc *p)
690 {
691 struct cs_blob *csblob;
692
693 if (NULL == p->p_textvp)
694 return NULL;
695
696 if ((csblob = ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff)) == NULL)
697 return NULL;
698
699 return csblob->csb_cdhash;
700 }