2 * Copyright (c) 2015-2018 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/cprotect.h>
30 #include <sys/malloc.h>
31 #include <sys/mount_internal.h>
32 #include <sys/filio.h>
33 #include <sys/content_protection.h>
34 #include <libkern/crypto/sha1.h>
35 #include <libkern/libkern.h>
36 //for write protection
37 #include <vm/vm_kern.h>
38 #include <vm/vm_map.h>
40 #define PTR_ADD(type, base, offset) (type)((uintptr_t)(base) + (offset))
45 * This structure contains the unwrapped key and is passed to the lower layers.
46 * It is private so users must use the accessors declared in sys/cprotect.h
50 // cpx_flags defined in cprotect.h
52 CPX_SEP_WRAPPEDKEY
= 0x01,
53 CPX_IV_AES_CTX_INITIALIZED
= 0x02,
54 CPX_USE_OFFSET_FOR_IV
= 0x04,
56 // Using AES IV context generated from key
57 CPX_IV_AES_CTX_VFS
= 0x08,
58 CPX_SYNTHETIC_OFFSET_FOR_IV
= 0x10,
59 CPX_COMPOSITEKEY
= 0x20,
61 //write page protection
62 CPX_WRITE_PROTECTABLE
= 0x40
66 * variable-length CPX structure. See fixed-length variant in cprotect.h
72 aes_encrypt_ctx
*cpx_iv_aes_ctx_ptr
;// Pointer to context used for generating the IV
73 cpx_flags_t cpx_flags
;
74 uint16_t cpx_max_key_len
;
76 //fixed length up to here. cpx_cached_key is variable-length
77 uint8_t cpx_cached_key
[];
80 /* Allows us to switch between CPX types */
81 typedef union cpxunion
{
86 ZONE_DECLARE(cpx_zone
, "cpx",
87 sizeof(struct fcpx
), ZC_ZFREE_CLEARMEM
);
88 ZONE_DECLARE(aes_ctz_zone
, "AES ctx",
89 sizeof(aes_encrypt_ctx
), ZC_ZFREE_CLEARMEM
| ZC_NOENCRYPT
);
91 // Note: see struct fcpx defined in sys/cprotect.h
93 // -- cpx_t accessors --
96 cpx_size(size_t key_len
)
98 // This should pick up the 'magic' word in DEBUG for free.
99 size_t size
= sizeof(struct cpx
) + key_len
;
105 cpx_sizex(const struct cpx
*cpx
)
107 return cpx_size(cpx
->cpx_max_key_len
);
111 cpx_alloc(size_t key_len
, bool needs_ctx
)
115 #if CONFIG_KEYPAGE_WP
117 * Macs only use 1 key per volume, so force it into its own page.
118 * This way, we can write-protect as needed.
120 size_t cpsize
= cpx_size(key_len
);
122 // silence warning for needs_ctx
125 if (cpsize
< PAGE_SIZE
) {
127 * Don't use MALLOC to allocate the page-sized structure. Instead,
128 * use kmem_alloc to bypass KASAN since we are supplying our own
129 * unilateral write protection on this page. Note that kmem_alloc
132 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&cpx
, PAGE_SIZE
, VM_KERN_MEMORY_FILE
)) {
134 * returning NULL at this point (due to failed allocation) would just
135 * result in a panic. fall back to attempting a normal MALLOC, and don't
136 * let the cpx get marked PROTECTABLE.
138 MALLOC(cpx
, cpx_t
, cpx_size(key_len
), M_TEMP
, M_WAITOK
);
140 //mark the page as protectable, since kmem_alloc succeeded.
141 cpx
->cpx_flags
|= CPX_WRITE_PROTECTABLE
;
144 panic("cpx_size too large ! (%lu)", cpsize
);
147 /* If key page write protection disabled, just switch to zalloc */
149 // error out if you try to request a key that's too big
150 if (key_len
> VFS_CP_MAX_CACHEBUFLEN
) {
154 // the actual key array is fixed-length, but the amount of usable content can vary, via 'key_len'
155 cpx
= zalloc_flags(cpx_zone
, Z_WAITOK
| Z_ZERO
);
157 // if our encryption type needs it, alloc the context
163 cpx_init(cpx
, key_len
);
169 cpx_alloc_ctx(cpx_t cpx
)
171 #if CONFIG_KEYPAGE_WP
174 if (cpx
->cpx_iv_aes_ctx_ptr
) {
175 // already allocated?
179 cpx
->cpx_iv_aes_ctx_ptr
= zalloc_flags(aes_ctz_zone
, Z_WAITOK
| Z_ZERO
);
180 #endif // CONFIG_KEYPAGE_WP
186 cpx_free_ctx(cpx_t cpx
)
188 #if CONFIG_KEYPAGE_WP
191 if (cpx
->cpx_iv_aes_ctx_ptr
) {
192 zfree(aes_ctz_zone
, cpx
->cpx_iv_aes_ctx_ptr
);
194 #endif // CONFIG_KEYPAGE_WP
198 cpx_writeprotect(cpx_t cpx
)
200 #if CONFIG_KEYPAGE_WP
201 void *cpxstart
= (void*)cpx
;
202 void *cpxend
= (void*)((uint8_t*)cpx
+ PAGE_SIZE
);
203 if (cpx
->cpx_flags
& CPX_WRITE_PROTECTABLE
) {
204 vm_map_protect(kernel_map
, (vm_map_offset_t
)cpxstart
, (vm_map_offset_t
)cpxend
, (VM_PROT_READ
), FALSE
);
213 static const uint32_t cpx_magic1
= 0x7b787063; // cpx{
214 static const uint32_t cpx_magic2
= 0x7870637d; // }cpx
221 assert(cpx
->cpx_magic1
== cpx_magic1
);
222 assert(*PTR_ADD(uint32_t *, cpx
, cpx_sizex(cpx
) - 4) == cpx_magic2
);
225 #if CONFIG_KEYPAGE_WP
226 /* unprotect the page before bzeroing */
227 void *cpxstart
= (void*)cpx
;
228 void *cpxend
= (void*)((uint8_t*)cpx
+ PAGE_SIZE
);
229 if (cpx
->cpx_flags
& CPX_WRITE_PROTECTABLE
) {
230 vm_map_protect(kernel_map
, (vm_map_offset_t
)cpxstart
, (vm_map_offset_t
)cpxend
, (VM_PROT_DEFAULT
), FALSE
);
232 //now zero the memory after un-protecting it
233 bzero(cpx
->cpx_cached_key
, cpx
->cpx_max_key_len
);
235 //If we are here, then we used kmem_alloc to get the page. Must use kmem_free to drop it.
236 kmem_free(kernel_map
, (vm_offset_t
)cpx
, PAGE_SIZE
);
240 // free the context if it wasn't already freed
242 zfree(cpx_zone
, cpx
);
248 cpx_init(cpx_t cpx
, size_t key_len
)
251 cpx
->cpx_magic1
= cpx_magic1
;
252 *PTR_ADD(uint32_t *, cpx
, cpx_size(key_len
) - 4) = cpx_magic2
;
255 cpx
->cpx_key_len
= 0;
256 assert(key_len
<= UINT16_MAX
);
257 cpx
->cpx_max_key_len
= (uint16_t)key_len
;
261 cpx_is_sep_wrapped_key(const struct cpx
*cpx
)
263 return ISSET(cpx
->cpx_flags
, CPX_SEP_WRAPPEDKEY
);
267 cpx_set_is_sep_wrapped_key(struct cpx
*cpx
, bool v
)
270 SET(cpx
->cpx_flags
, CPX_SEP_WRAPPEDKEY
);
272 CLR(cpx
->cpx_flags
, CPX_SEP_WRAPPEDKEY
);
277 cpx_is_composite_key(const struct cpx
*cpx
)
279 return ISSET(cpx
->cpx_flags
, CPX_COMPOSITEKEY
);
283 cpx_set_is_composite_key(struct cpx
*cpx
, bool v
)
286 SET(cpx
->cpx_flags
, CPX_COMPOSITEKEY
);
288 CLR(cpx
->cpx_flags
, CPX_COMPOSITEKEY
);
293 cpx_use_offset_for_iv(const struct cpx
*cpx
)
295 return ISSET(cpx
->cpx_flags
, CPX_USE_OFFSET_FOR_IV
);
299 cpx_set_use_offset_for_iv(struct cpx
*cpx
, bool v
)
302 SET(cpx
->cpx_flags
, CPX_USE_OFFSET_FOR_IV
);
304 CLR(cpx
->cpx_flags
, CPX_USE_OFFSET_FOR_IV
);
309 cpx_synthetic_offset_for_iv(const struct cpx
*cpx
)
311 return ISSET(cpx
->cpx_flags
, CPX_SYNTHETIC_OFFSET_FOR_IV
);
315 cpx_set_synthetic_offset_for_iv(struct cpx
*cpx
, bool v
)
318 SET(cpx
->cpx_flags
, CPX_SYNTHETIC_OFFSET_FOR_IV
);
320 CLR(cpx
->cpx_flags
, CPX_SYNTHETIC_OFFSET_FOR_IV
);
325 cpx_max_key_len(const struct cpx
*cpx
)
327 return cpx
->cpx_max_key_len
;
331 cpx_key_len(const struct cpx
*cpx
)
333 return cpx
->cpx_key_len
;
337 cpx_set_key_len(struct cpx
*cpx
, uint16_t key_len
)
339 cpx
->cpx_key_len
= key_len
;
341 if (ISSET(cpx
->cpx_flags
, CPX_IV_AES_CTX_VFS
)) {
343 * We assume that if the key length is being modified, the key
344 * has changed. As a result, un-set any bits related to the
345 * AES context, if needed. They should be re-generated
348 CLR(cpx
->cpx_flags
, CPX_IV_AES_CTX_INITIALIZED
| CPX_IV_AES_CTX_VFS
);
353 cpx_has_key(const struct cpx
*cpx
)
355 return cpx
->cpx_key_len
> 0;
358 #pragma clang diagnostic push
359 #pragma clang diagnostic ignored "-Wcast-qual"
361 cpx_key(const struct cpx
*cpx
)
363 return (void *)cpx
->cpx_cached_key
;
365 #pragma clang diagnostic pop
368 cpx_set_aes_iv_key(struct cpx
*cpx
, void *iv_key
)
370 if (cpx
->cpx_iv_aes_ctx_ptr
) {
371 aes_encrypt_key128(iv_key
, cpx
->cpx_iv_aes_ctx_ptr
);
372 SET(cpx
->cpx_flags
, CPX_IV_AES_CTX_INITIALIZED
| CPX_USE_OFFSET_FOR_IV
);
373 CLR(cpx
->cpx_flags
, CPX_IV_AES_CTX_VFS
);
378 cpx_iv_aes_ctx(struct cpx
*cpx
)
380 if (ISSET(cpx
->cpx_flags
, CPX_IV_AES_CTX_INITIALIZED
)) {
381 return cpx
->cpx_iv_aes_ctx_ptr
;
385 uint8_t digest
[SHA_DIGEST_LENGTH
]; /* Kiv */
387 /* First init the cp_cache_iv_key[] */
391 * We can only use this when the keys are generated in the AP; As a result
392 * we only use the first 32 bytes of key length in the cache key
394 SHA1Update(&sha1ctxt
, cpx
->cpx_cached_key
, cpx
->cpx_key_len
);
395 SHA1Final(digest
, &sha1ctxt
);
397 cpx_set_aes_iv_key(cpx
, digest
);
398 SET(cpx
->cpx_flags
, CPX_IV_AES_CTX_VFS
);
400 return cpx
->cpx_iv_aes_ctx_ptr
;
406 bzero(cpx
->cpx_cached_key
, cpx
->cpx_max_key_len
);
407 if (cpx
->cpx_iv_aes_ctx_ptr
) {
408 bzero(cpx
->cpx_iv_aes_ctx_ptr
, sizeof(aes_encrypt_ctx
));
411 cpx
->cpx_key_len
= 0;
415 cpx_can_copy(const struct cpx
*src
, const struct cpx
*dst
)
417 return src
->cpx_key_len
<= dst
->cpx_max_key_len
;
421 cpx_copy(const struct cpx
*src
, cpx_t dst
)
423 uint16_t key_len
= cpx_key_len(src
);
424 cpx_set_key_len(dst
, key_len
);
425 memcpy(cpx_key(dst
), cpx_key(src
), key_len
);
426 dst
->cpx_flags
= src
->cpx_flags
;
427 if (ISSET(dst
->cpx_flags
, CPX_IV_AES_CTX_INITIALIZED
)) {
428 *(dst
->cpx_iv_aes_ctx_ptr
) = *(src
->cpx_iv_aes_ctx_ptr
); // deep copy
433 cp_lock_state_t state
;
436 } cp_lock_vfs_callback_arg
;
439 cp_lock_vfs_callback(mount_t mp
, void *arg
)
441 cp_lock_vfs_callback_arg
*callback_arg
= (cp_lock_vfs_callback_arg
*)arg
;
443 if (callback_arg
->valid_uuid
) {
446 VFSATTR_WANTED(&va
, f_uuid
);
448 if (vfs_getattr(mp
, &va
, vfs_context_current())) {
452 if (!VFSATTR_IS_SUPPORTED(&va
, f_uuid
)) {
456 if (memcmp(va
.f_uuid
, callback_arg
->volume_uuid
, sizeof(uuid_t
))) {
461 VFS_IOCTL(mp
, FIODEVICELOCKED
, (void *)(uintptr_t)callback_arg
->state
, 0, vfs_context_kernel());
466 cp_key_store_action(cp_key_store_action_t action
)
468 cp_lock_vfs_callback_arg callback_arg
;
471 case CP_ACTION_LOCKED
:
472 case CP_ACTION_UNLOCKED
:
473 callback_arg
.state
= (action
== CP_ACTION_LOCKED
? CP_LOCKED_STATE
: CP_UNLOCKED_STATE
);
474 memset(callback_arg
.volume_uuid
, 0, sizeof(uuid_t
));
475 callback_arg
.valid_uuid
= 0;
476 return vfs_iterate(0, cp_lock_vfs_callback
, (void *)&callback_arg
);
483 cp_key_store_action_for_volume(uuid_t volume_uuid
, cp_key_store_action_t action
)
485 cp_lock_vfs_callback_arg callback_arg
;
488 case CP_ACTION_LOCKED
:
489 case CP_ACTION_UNLOCKED
:
490 callback_arg
.state
= (action
== CP_ACTION_LOCKED
? CP_LOCKED_STATE
: CP_UNLOCKED_STATE
);
491 memcpy(callback_arg
.volume_uuid
, volume_uuid
, sizeof(uuid_t
));
492 callback_arg
.valid_uuid
= 1;
493 return vfs_iterate(0, cp_lock_vfs_callback
, (void *)&callback_arg
);
500 cp_is_valid_class(int isdir
, int32_t protectionclass
)
503 * The valid protection classes are from 0 -> N
504 * We use a signed argument to detect unassigned values from
505 * directory entry creation time in HFS.
508 /* Directories are not allowed to have F, but they can have "NONE" */
509 return (protectionclass
>= PROTECTION_CLASS_DIR_NONE
) &&
510 (protectionclass
<= PROTECTION_CLASS_D
);
512 return (protectionclass
>= PROTECTION_CLASS_A
) &&
513 (protectionclass
<= PROTECTION_CLASS_F
);
518 * Parses versions of the form 12A316, i.e. <major><minor><revision> and
519 * returns a uint32_t in the form 0xaabbcccc where aa = <major>,
520 * bb = <ASCII char>, cccc = <revision>.
522 static cp_key_os_version_t
523 parse_os_version(const char *vers
)
525 const char *p
= vers
;
528 while (*p
>= '0' && *p
<= '9') {
529 a
= a
* 10 + *p
- '0';
543 while (*p
>= '0' && *p
<= '9') {
544 c
= c
* 10 + *p
- '0';
552 return (a
& 0xff) << 24 | b
<< 16 | (c
& 0xffff);
558 static cp_key_os_version_t cp_os_version
;
561 return cp_os_version
;
568 cp_os_version
= parse_os_version(osversion
);
569 if (!cp_os_version
) {
570 printf("cp_os_version: unable to parse osversion `%s'\n", osversion
);
574 return cp_os_version
;