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