]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_cs.c
xnu-2782.30.5.tar.gz
[apple/xnu.git] / bsd / kern / kern_cs.c
CommitLineData
39236c6e
A
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>
fe8ab488 41#include <sys/file_internal.h>
39236c6e
A
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
39236c6e
A
67
68#include <kern/assert.h>
69
70#include <pexpert/pexpert.h>
71
15129b1c
A
72#include <mach/shared_region.h>
73
39236c6e
A
74unsigned long cs_procs_killed = 0;
75unsigned long cs_procs_invalidated = 0;
76
77int cs_force_kill = 0;
78int cs_force_hard = 0;
79int cs_debug = 0;
80#if SECURE_KERNEL
81const int cs_enforcement_enable=1;
fe8ab488 82const int cs_library_val_enable=1;
39236c6e
A
83#else
84#if CONFIG_ENFORCE_SIGNED_CODE
85int cs_enforcement_enable=1;
86#else
87int cs_enforcement_enable=0;
fe8ab488
A
88#endif /* CONFIG_ENFORCE_SIGNED_CODE */
89
90#if CONFIG_ENFORCE_LIBRARY_VALIDATION
91int cs_library_val_enable = 1;
92#else
93int cs_library_val_enable = 0;
94#endif /* CONFIG_ENFORCE_LIBRARY_VALIDATION */
95
39236c6e 96int cs_enforcement_panic=0;
fe8ab488 97#endif /* SECURE_KERNEL */
39236c6e
A
98int cs_all_vnodes = 0;
99
100static lck_grp_t *cs_lockgrp;
101static lck_rw_t * SigPUPLock;
102
103SYSCTL_INT(_vm, OID_AUTO, cs_force_kill, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_kill, 0, "");
104SYSCTL_INT(_vm, OID_AUTO, cs_force_hard, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_hard, 0, "");
105SYSCTL_INT(_vm, OID_AUTO, cs_debug, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_debug, 0, "");
106
107SYSCTL_INT(_vm, OID_AUTO, cs_all_vnodes, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_all_vnodes, 0, "");
108
109#if !SECURE_KERNEL
110SYSCTL_INT(_vm, OID_AUTO, cs_enforcement, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_enable, 0, "");
111SYSCTL_INT(_vm, OID_AUTO, cs_enforcement_panic, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_panic, 0, "");
112#endif
113
15129b1c 114int panic_on_cs_killed = 0;
39236c6e
A
115void
116cs_init(void)
117{
fe8ab488 118#if MACH_ASSERT && __x86_64__
15129b1c 119 panic_on_cs_killed = 1;
fe8ab488 120#endif /* MACH_ASSERT && __x86_64__ */
15129b1c
A
121 PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed,
122 sizeof (panic_on_cs_killed));
39236c6e
A
123#if !SECURE_KERNEL
124 int disable_cs_enforcement = 0;
125 PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement,
126 sizeof (disable_cs_enforcement));
127 if (disable_cs_enforcement) {
128 cs_enforcement_enable = 0;
129 } else {
130 int panic = 0;
131 PE_parse_boot_argn("cs_enforcement_panic", &panic, sizeof(panic));
132 cs_enforcement_panic = (panic != 0);
133 }
134
135 PE_parse_boot_argn("cs_debug", &cs_debug, sizeof (cs_debug));
136#endif
137 lck_grp_attr_t *attr = lck_grp_attr_alloc_init();
138 cs_lockgrp = lck_grp_alloc_init("KERNCS", attr);
139 SigPUPLock = lck_rw_alloc_init(cs_lockgrp, NULL);
140}
141
142int
143cs_allow_invalid(struct proc *p)
144{
145#if MACH_ASSERT
146 lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED);
147#endif
148#if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE
149 /* There needs to be a MAC policy to implement this hook, or else the
150 * kill bits will be cleared here every time. If we have
151 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy
152 * implementing the hook.
153 */
154 if( 0 != mac_proc_check_run_cs_invalid(p)) {
155 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
156 "not allowed: pid %d\n",
157 p->p_pid);
158 return 0;
159 }
160 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() "
161 "allowed: pid %d\n",
162 p->p_pid);
163 proc_lock(p);
164 p->p_csflags &= ~(CS_KILL | CS_HARD);
165 proc_unlock(p);
166 vm_map_switch_protect(get_task_map(p->task), FALSE);
167#endif
168 return (p->p_csflags & (CS_KILL | CS_HARD)) == 0;
169}
170
171int
172cs_invalid_page(
173 addr64_t vaddr)
174{
175 struct proc *p;
176 int send_kill = 0, retval = 0, verbose = cs_debug;
177 uint32_t csflags;
178
179 p = current_proc();
180
39236c6e
A
181 if (verbose)
182 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n",
183 vaddr, p->p_pid, p->p_comm);
184
185 proc_lock(p);
186
187 /* XXX for testing */
188 if (cs_force_kill)
189 p->p_csflags |= CS_KILL;
190 if (cs_force_hard)
191 p->p_csflags |= CS_HARD;
192
193 /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */
194 if (p->p_csflags & CS_KILL) {
15129b1c
A
195 if (panic_on_cs_killed &&
196 vaddr >= SHARED_REGION_BASE &&
197 vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) {
198 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr, p);
199 }
39236c6e
A
200 p->p_csflags |= CS_KILLED;
201 cs_procs_killed++;
202 send_kill = 1;
203 retval = 1;
204 }
205
15129b1c
A
206#if __x86_64__
207 if (panic_on_cs_killed &&
208 vaddr >= SHARED_REGION_BASE &&
209 vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) {
210 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr, p);
211 }
212#endif /* __x86_64__ */
213
39236c6e
A
214 /* CS_HARD means fail the mapping operation so the process stays valid. */
215 if (p->p_csflags & CS_HARD) {
216 retval = 1;
217 } else {
218 if (p->p_csflags & CS_VALID) {
219 p->p_csflags &= ~CS_VALID;
220 cs_procs_invalidated++;
221 verbose = 1;
222 }
223 }
224 csflags = p->p_csflags;
225 proc_unlock(p);
226
fe8ab488 227 if (verbose)
39236c6e 228 printf("CODE SIGNING: cs_invalid_page(0x%llx): "
15129b1c 229 "p=%d[%s] final status 0x%x, %s page%s\n",
39236c6e 230 vaddr, p->p_pid, p->p_comm, p->p_csflags,
15129b1c 231 retval ? "denying" : "allowing (remove VALID)",
39236c6e 232 send_kill ? " sending SIGKILL" : "");
39236c6e
A
233
234 if (send_kill)
15129b1c 235 threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS);
39236c6e
A
236
237
238 return retval;
239}
240
241/*
242 * Assumes p (if passed in) is locked with proc_lock().
243 */
244
245int
246cs_enforcement(struct proc *p)
247{
248
249 if (cs_enforcement_enable)
250 return 1;
251
252 if (p == NULL)
253 p = current_proc();
254
255 if (p != NULL && (p->p_csflags & CS_ENFORCEMENT))
256 return 1;
257
258 return 0;
259}
260
261static struct {
262 struct cscsr_functions *funcs;
263 vm_map_offset_t csr_map_base;
264 vm_map_size_t csr_map_size;
265 int inuse;
266 int disabled;
267} csr_state;
268
269SYSCTL_INT(_vm, OID_AUTO, sigpup_disable, CTLFLAG_RW | CTLFLAG_LOCKED, &csr_state.disabled, 0, "");
270
271static int
272vnsize(vfs_context_t vfs, vnode_t vp, uint64_t *size)
273{
274 struct vnode_attr va;
275 int error;
276
277 VATTR_INIT(&va);
278 VATTR_WANTED(&va, va_data_size);
279
280 error = vnode_getattr(vp, &va, vfs);
281 if (error)
282 return error;
283 *size = va.va_data_size;
284 return 0;
285}
286
287int
288sigpup_install(user_addr_t argsp)
289{
290 struct sigpup_install_table args;
291 memory_object_control_t control;
292 kern_return_t result;
293 vfs_context_t vfs = NULL;
294 struct vnode_attr va;
295 vnode_t vp = NULL;
296 char *buf = NULL;
297 uint64_t size;
298 size_t len = 0;
299 int error = 0;
300
301 if (!cs_enforcement_enable || csr_state.funcs == NULL)
302 return ENOTSUP;
303
304 lck_rw_lock_exclusive(SigPUPLock);
305
306 if (kauth_cred_issuser(kauth_cred_get()) == 0) {
307 error = EPERM;
308 goto cleanup;
309 }
310
311 if (cs_debug > 10)
312 printf("sigpup install\n");
313
314 if (csr_state.csr_map_base != 0 || csr_state.inuse) {
315 error = EPERM;
316 goto cleanup;
317 }
318
319 if (USER_ADDR_NULL == argsp) {
320 error = EINVAL;
321 goto cleanup;
322 }
323 if ((error = copyin(argsp, &args, sizeof(args))) != 0)
324 goto cleanup;
325
326 if (cs_debug > 10)
327 printf("sigpup install with args\n");
328
329 MALLOC(buf, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
330 if (buf == NULL) {
331 error = ENOMEM;
332 goto cleanup;
333 }
334 if ((error = copyinstr((user_addr_t)args.path, buf, MAXPATHLEN, &len)) != 0)
335 goto cleanup;
336
337 if ((vfs = vfs_context_create(NULL)) == NULL) {
338 error = ENOMEM;
339 goto cleanup;
340 }
341
342 if ((error = vnode_lookup(buf, VNODE_LOOKUP_NOFOLLOW, &vp, vfs)) != 0)
343 goto cleanup;
344
345 if (cs_debug > 10)
346 printf("sigpup found file: %s\n", buf);
347
348 /* make sure vnode is on the process's root volume */
349 if (rootvnode->v_mount != vp->v_mount) {
350 if (cs_debug) printf("sigpup csr no on root volume\n");
351 error = EPERM;
352 goto cleanup;
353 }
354
355 /* make sure vnode is owned by "root" */
356 VATTR_INIT(&va);
357 VATTR_WANTED(&va, va_uid);
358 error = vnode_getattr(vp, &va, vfs);
359 if (error)
360 goto cleanup;
361
362 if (va.va_uid != 0) {
363 if (cs_debug) printf("sigpup: csr file not owned by root\n");
364 error = EPERM;
365 goto cleanup;
366 }
367
368 error = vnsize(vfs, vp, &size);
369 if (error)
370 goto cleanup;
371
372 control = ubc_getobject(vp, 0);
373 if (control == MEMORY_OBJECT_CONTROL_NULL) {
374 error = EINVAL;
375 goto cleanup;
376 }
377
378 csr_state.csr_map_size = mach_vm_round_page(size);
379
380 if (cs_debug > 10)
381 printf("mmap!\n");
382
383 result = vm_map_enter_mem_object_control(kernel_map,
384 &csr_state.csr_map_base,
385 csr_state.csr_map_size,
386 0, VM_FLAGS_ANYWHERE,
387 control, 0 /* file offset */,
388 0 /* cow */,
389 VM_PROT_READ,
390 VM_PROT_READ,
391 VM_INHERIT_DEFAULT);
392 if (result != KERN_SUCCESS) {
393 error = EINVAL;
394 goto cleanup;
395 }
396
397 error = csr_state.funcs->csr_validate_header((const uint8_t *)csr_state.csr_map_base,
398 csr_state.csr_map_size);
399 if (error) {
400 if (cs_debug > 10)
401 printf("sigpup header invalid, dropping mapping");
402 sigpup_drop();
403 goto cleanup;
404 }
405
406 if (cs_debug > 10)
407 printf("table loaded %ld bytes\n", (long)csr_state.csr_map_size);
408
409cleanup:
410 lck_rw_unlock_exclusive(SigPUPLock);
411
412 if (buf)
413 FREE(buf, M_TEMP);
414 if (vp)
415 (void)vnode_put(vp);
416 if (vfs)
417 (void)vfs_context_rele(vfs);
418
419 if (error)
420 printf("sigpup: load failed with error: %d\n", error);
421
422
423 return error;
424}
425
426int
427sigpup_drop(void)
428{
429
430 if (kauth_cred_issuser(kauth_cred_get()) == 0)
431 return EPERM;
432
433 lck_rw_lock_exclusive(SigPUPLock);
434
435 if (csr_state.csr_map_base == 0 || csr_state.inuse) {
436 printf("failed to unload the sigpup database\n");
437 lck_rw_unlock_exclusive(SigPUPLock);
438 return EINVAL;
439 }
440
441 if (cs_debug > 10)
442 printf("sigpup: unloading\n");
443
444 (void)mach_vm_deallocate(kernel_map,
445 csr_state.csr_map_base, csr_state.csr_map_size);
446
447 csr_state.csr_map_base = 0;
448 csr_state.csr_map_size = 0;
449
450 lck_rw_unlock_exclusive(SigPUPLock);
451
452 return 0;
453}
454
455void sigpup_attach_vnode(vnode_t); /* XXX */
456
457void
458sigpup_attach_vnode(vnode_t vp)
459{
460 const void *csblob;
461 size_t cslen;
462
463 if (!cs_enforcement_enable || csr_state.funcs == NULL || csr_state.csr_map_base == 0 || csr_state.disabled)
464 return;
465
466 /* if the file is not on the root volumes or already been check, skip */
467 if (vp->v_mount != rootvnode->v_mount || (vp->v_flag & VNOCS))
468 return;
469
470 csblob = csr_state.funcs->csr_find_file_codedirectory(vp, (const uint8_t *)csr_state.csr_map_base,
471 (size_t)csr_state.csr_map_size, &cslen);
472 if (csblob) {
473 ubc_cs_sigpup_add(vp, (vm_address_t)csblob, (vm_size_t)cslen);
474 csr_state.inuse = 1;
475 }
476 vp->v_flag |= VNOCS;
477}
478
479void
480cs_register_cscsr(struct cscsr_functions *funcs)
481{
482 if (csr_state.funcs || funcs->csr_version < CSCSR_VERSION)
483 return;
484 csr_state.funcs = funcs;
485}
fe8ab488
A
486
487/*
488 * Library validation functions
489 */
490int
491cs_require_lv(struct proc *p)
492{
493
494 if (cs_library_val_enable)
495 return 1;
496
497 if (p == NULL)
498 p = current_proc();
499
500 if (p != NULL && (p->p_csflags & CS_REQUIRE_LV))
501 return 1;
502
503 return 0;
504}
505
506/*
507 * Function: csblob_get_teamid
508 *
509 * Description: This function returns a pointer to the team id
510 stored within the codedirectory of the csblob.
511 If the codedirectory predates team-ids, it returns
512 NULL.
513 This does not copy the name but returns a pointer to
514 it within the CD. Subsequently, the CD must be
515 available when this is used.
516*/
517const char *
518csblob_get_teamid(struct cs_blob *csblob)
519{
520 const CS_CodeDirectory *cd;
521
522 if ((cd = (const CS_CodeDirectory *)cs_find_blob(
523 csblob, CSSLOT_CODEDIRECTORY, CSMAGIC_CODEDIRECTORY)) == NULL)
524 return NULL;
525
526 if (ntohl(cd->version) < CS_SUPPORTSTEAMID)
527 return NULL;
528
529 if (ntohl(cd->teamOffset) == 0)
530 return NULL;
531
532 const char *name = ((const char *)cd) + ntohl(cd->teamOffset);
533 if (cs_debug > 1)
534 printf("found team-id %s in cdblob\n", name);
535
536 return name;
537}
538
539/*
540 * Function: csproc_get_blob
541 *
542 * Description: This function returns the cs_blob
543 * for the process p
544 */
545static struct cs_blob *
546csproc_get_blob(struct proc *p)
547{
548 if (NULL == p)
549 return NULL;
550
551 if (NULL == p->p_textvp)
552 return NULL;
553
554 return ubc_cs_blob_get(p->p_textvp, -1, p->p_textoff);
555}
556
557/*
558 * Function: csproc_get_teamid
559 *
560 * Description: This function returns a pointer to the
561 * team id of the process p
562*/
563const char *
564csproc_get_teamid(struct proc *p)
565{
566 struct cs_blob *csblob;
567
568 csblob = csproc_get_blob(p);
569
570 return (csblob == NULL) ? NULL : csblob->csb_teamid;
571}
572
573/*
574 * Function: csvnode_get_teamid
575 *
576 * Description: This function returns a pointer to the
577 * team id of the binary at the given offset in vnode vp
578*/
579const char *
580csvnode_get_teamid(struct vnode *vp, off_t offset)
581{
582 struct cs_blob *csblob;
583
584 if (vp == NULL)
585 return NULL;
586
587 csblob = ubc_cs_blob_get(vp, -1, offset);
588
589 return (csblob == NULL) ? NULL : csblob->csb_teamid;
590}
591
592/*
593 * Function: csproc_get_platform_binary
594 *
595 * Description: This function returns the value
596 * of the platform_binary field for proc p
597 */
598int
599csproc_get_platform_binary(struct proc *p)
600{
601 struct cs_blob *csblob;
602
603 csblob = csproc_get_blob(p);
604
605 /* If there is no csblob this returns 0 because
606 it is true that it is not a platform binary */
607 return (csblob == NULL) ? 0 : csblob->csb_platform_binary;
608}
609
610/*
611 * Function: csfg_get_platform_binary
612 *
613 * Description: This function returns the
614 * platform binary field for the
615 * fileglob fg
616 */
617int
618csfg_get_platform_binary(struct fileglob *fg)
619{
620 int platform_binary = 0;
621 struct ubc_info *uip;
622 vnode_t vp;
623
624 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
625 return 0;
626
627 vp = (struct vnode *)fg->fg_data;
628 if (vp == NULL)
629 return 0;
630
631 vnode_lock(vp);
632 if (!UBCINFOEXISTS(vp))
633 goto out;
634
635 uip = vp->v_ubcinfo;
636 if (uip == NULL)
637 goto out;
638
639 if (uip->cs_blobs == NULL)
640 goto out;
641
642 /* It is OK to extract the teamid from the first blob
643 because all blobs of a vnode must have the same teamid */
644 platform_binary = uip->cs_blobs->csb_platform_binary;
645out:
646 vnode_unlock(vp);
647
648 return platform_binary;
649}
650
651/*
652 * Function: csfg_get_teamid
653 *
654 * Description: This returns a pointer to
655 * the teamid for the fileglob fg
656 */
657const char *
658csfg_get_teamid(struct fileglob *fg)
659{
660 struct ubc_info *uip;
661 const char *str = NULL;
662 vnode_t vp;
663
664 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
665 return NULL;
666
667 vp = (struct vnode *)fg->fg_data;
668 if (vp == NULL)
669 return NULL;
670
671 vnode_lock(vp);
672 if (!UBCINFOEXISTS(vp))
673 goto out;
674
675 uip = vp->v_ubcinfo;
676 if (uip == NULL)
677 goto out;
678
679 if (uip->cs_blobs == NULL)
680 goto out;
681
682 /* It is OK to extract the teamid from the first blob
683 because all blobs of a vnode must have the same teamid */
684 str = uip->cs_blobs->csb_teamid;
685out:
686 vnode_unlock(vp);
687
688 return str;
689}
690
691uint32_t
692cs_entitlement_flags(struct proc *p)
693{
694 return (p->p_csflags & CS_ENTITLEMENT_FLAGS);
695}
696
697/*
698 * Function: csfg_get_path
699 *
700 * Description: This populates the buffer passed in
701 * with the path of the vnode
702 * When calling this, the fileglob
703 * cannot go away. The caller must have a
704 * a reference on the fileglob or fileproc
705 */
706int
707csfg_get_path(struct fileglob *fg, char *path, int *len)
708{
709 vnode_t vp = NULL;
710
711 if (FILEGLOB_DTYPE(fg) != DTYPE_VNODE)
712 return -1;
713
714 vp = (struct vnode *)fg->fg_data;
715
716 /* vn_getpath returns 0 for success,
717 or an error code */
718 return vn_getpath(vp, path, len);
719}