X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/813fb2f63a553c957e917ede5f119b021d6ce391..cb3231590a3c94ab4375e2228bd5e86b0cf1ad7e:/bsd/vfs/vfs_cprotect.c diff --git a/bsd/vfs/vfs_cprotect.c b/bsd/vfs/vfs_cprotect.c index d28679623..edb00a315 100644 --- a/bsd/vfs/vfs_cprotect.c +++ b/bsd/vfs/vfs_cprotect.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Apple Inc. All rights reserved. + * Copyright (c) 2015-2018 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -33,8 +33,11 @@ #include #include #include +//for write protection +#include +#include -#define PTR_ADD(type, base, offset) (type)((uintptr_t)(base) + (offset)) +#define PTR_ADD(type, base, offset) (type)((uintptr_t)(base) + (offset)) // -- struct cpx -- @@ -47,30 +50,34 @@ // cpx_flags typedef uint32_t cpx_flags_t; enum { - CPX_SEP_WRAPPEDKEY = 0x01, - CPX_IV_AES_CTX_INITIALIZED = 0x02, - CPX_USE_OFFSET_FOR_IV = 0x04, + CPX_SEP_WRAPPEDKEY = 0x01, + CPX_IV_AES_CTX_INITIALIZED = 0x02, + CPX_USE_OFFSET_FOR_IV = 0x04, // Using AES IV context generated from key - CPX_IV_AES_CTX_VFS = 0x08, + CPX_IV_AES_CTX_VFS = 0x08, CPX_SYNTHETIC_OFFSET_FOR_IV = 0x10, - CPX_COMPOSITEKEY = 0x20 + CPX_COMPOSITEKEY = 0x20, + + //write page protection + CPX_WRITE_PROTECTABLE = 0x40 }; struct cpx { #if DEBUG - uint32_t cpx_magic1; + uint32_t cpx_magic1; #endif - aes_encrypt_ctx cpx_iv_aes_ctx; // Context used for generating the IV - cpx_flags_t cpx_flags; - uint16_t cpx_max_key_len; - uint16_t cpx_key_len; - uint8_t cpx_cached_key[]; + aes_encrypt_ctx cpx_iv_aes_ctx; // Context used for generating the IV + cpx_flags_t cpx_flags; + uint16_t cpx_max_key_len; + uint16_t cpx_key_len; + uint8_t cpx_cached_key[]; }; // -- cpx_t accessors -- -size_t cpx_size(size_t key_size) +size_t +cpx_size(size_t key_size) { size_t size = sizeof(struct cpx) + key_size; @@ -81,38 +88,105 @@ size_t cpx_size(size_t key_size) return size; } -size_t cpx_sizex(const struct cpx *cpx) +size_t +cpx_sizex(const struct cpx *cpx) { return cpx_size(cpx->cpx_max_key_len); } -cpx_t cpx_alloc(size_t key_len) +cpx_t +cpx_alloc(size_t key_len) { - cpx_t cpx; + cpx_t cpx = NULL; +#if CONFIG_KEYPAGE_WP + /* + * Macs only use 1 key per volume, so force it into its own page. + * This way, we can write-protect as needed. + */ + size_t cpsize = cpx_size(key_len); + if (cpsize < PAGE_SIZE) { + /* + * Don't use MALLOC to allocate the page-sized structure. Instead, + * use kmem_alloc to bypass KASAN since we are supplying our own + * unilateral write protection on this page. Note that kmem_alloc + * can block. + */ + if (kmem_alloc(kernel_map, (vm_offset_t *)&cpx, PAGE_SIZE, VM_KERN_MEMORY_FILE)) { + /* + * returning NULL at this point (due to failed allocation) would just + * result in a panic. fall back to attempting a normal MALLOC, and don't + * let the cpx get marked PROTECTABLE. + */ + MALLOC(cpx, cpx_t, cpx_size(key_len), M_TEMP, M_WAITOK); + } else { + //mark the page as protectable, since kmem_alloc succeeded. + cpx->cpx_flags |= CPX_WRITE_PROTECTABLE; + } + } else { + panic("cpx_size too large ! (%lu)", cpsize); + } +#else + /* If key page write protection disabled, just switch to kernel MALLOC */ MALLOC(cpx, cpx_t, cpx_size(key_len), M_TEMP, M_WAITOK); - +#endif cpx_init(cpx, key_len); return cpx; } +/* this is really a void function */ +void +cpx_writeprotect(cpx_t cpx) +{ +#if CONFIG_KEYPAGE_WP + void *cpxstart = (void*)cpx; + void *cpxend = (void*)((uint8_t*)cpx + PAGE_SIZE); + if (cpx->cpx_flags & CPX_WRITE_PROTECTABLE) { + vm_map_protect(kernel_map, (vm_map_offset_t)cpxstart, (vm_map_offset_t)cpxend, (VM_PROT_READ), FALSE); + } +#else + (void) cpx; +#endif + return; +} + #if DEBUG -static const uint32_t cpx_magic1 = 0x7b787063; // cpx{ -static const uint32_t cpx_magic2 = 0x7870637d; // }cpx +static const uint32_t cpx_magic1 = 0x7b787063; // cpx{ +static const uint32_t cpx_magic2 = 0x7870637d; // }cpx #endif -void cpx_free(cpx_t cpx) +void +cpx_free(cpx_t cpx) { #if DEBUG assert(cpx->cpx_magic1 == cpx_magic1); assert(*PTR_ADD(uint32_t *, cpx, cpx_sizex(cpx) - 4) == cpx_magic2); #endif + +#if CONFIG_KEYPAGE_WP + /* unprotect the page before bzeroing */ + void *cpxstart = (void*)cpx; + void *cpxend = (void*)((uint8_t*)cpx + PAGE_SIZE); + if (cpx->cpx_flags & CPX_WRITE_PROTECTABLE) { + vm_map_protect(kernel_map, (vm_map_offset_t)cpxstart, (vm_map_offset_t)cpxend, (VM_PROT_DEFAULT), FALSE); + + //now zero the memory after un-protecting it + bzero(cpx->cpx_cached_key, cpx->cpx_max_key_len); + + //If we are here, then we used kmem_alloc to get the page. Must use kmem_free to drop it. + kmem_free(kernel_map, (vm_offset_t)cpx, PAGE_SIZE); + return; + } +#else bzero(cpx->cpx_cached_key, cpx->cpx_max_key_len); FREE(cpx, M_TEMP); + return; +#endif } -void cpx_init(cpx_t cpx, size_t key_len) +void +cpx_init(cpx_t cpx, size_t key_len) { #if DEBUG cpx->cpx_magic1 = cpx_magic1; @@ -123,69 +197,84 @@ void cpx_init(cpx_t cpx, size_t key_len) cpx->cpx_max_key_len = key_len; } -bool cpx_is_sep_wrapped_key(const struct cpx *cpx) +bool +cpx_is_sep_wrapped_key(const struct cpx *cpx) { return ISSET(cpx->cpx_flags, CPX_SEP_WRAPPEDKEY); } -void cpx_set_is_sep_wrapped_key(struct cpx *cpx, bool v) +void +cpx_set_is_sep_wrapped_key(struct cpx *cpx, bool v) { - if (v) - SET(cpx->cpx_flags, CPX_SEP_WRAPPEDKEY); - else - CLR(cpx->cpx_flags, CPX_SEP_WRAPPEDKEY); + if (v) { + SET(cpx->cpx_flags, CPX_SEP_WRAPPEDKEY); + } else { + CLR(cpx->cpx_flags, CPX_SEP_WRAPPEDKEY); + } } -bool cpx_is_composite_key(const struct cpx *cpx) +bool +cpx_is_composite_key(const struct cpx *cpx) { - return ISSET(cpx->cpx_flags, CPX_COMPOSITEKEY); + return ISSET(cpx->cpx_flags, CPX_COMPOSITEKEY); } -void cpx_set_is_composite_key(struct cpx *cpx, bool v) +void +cpx_set_is_composite_key(struct cpx *cpx, bool v) { - if (v) - SET(cpx->cpx_flags, CPX_COMPOSITEKEY); - else - CLR(cpx->cpx_flags, CPX_COMPOSITEKEY); + if (v) { + SET(cpx->cpx_flags, CPX_COMPOSITEKEY); + } else { + CLR(cpx->cpx_flags, CPX_COMPOSITEKEY); + } } -bool cpx_use_offset_for_iv(const struct cpx *cpx) +bool +cpx_use_offset_for_iv(const struct cpx *cpx) { return ISSET(cpx->cpx_flags, CPX_USE_OFFSET_FOR_IV); } -void cpx_set_use_offset_for_iv(struct cpx *cpx, bool v) +void +cpx_set_use_offset_for_iv(struct cpx *cpx, bool v) { - if (v) - SET(cpx->cpx_flags, CPX_USE_OFFSET_FOR_IV); - else - CLR(cpx->cpx_flags, CPX_USE_OFFSET_FOR_IV); + if (v) { + SET(cpx->cpx_flags, CPX_USE_OFFSET_FOR_IV); + } else { + CLR(cpx->cpx_flags, CPX_USE_OFFSET_FOR_IV); + } } -bool cpx_synthetic_offset_for_iv(const struct cpx *cpx) +bool +cpx_synthetic_offset_for_iv(const struct cpx *cpx) { return ISSET(cpx->cpx_flags, CPX_SYNTHETIC_OFFSET_FOR_IV); } -void cpx_set_synthetic_offset_for_iv(struct cpx *cpx, bool v) +void +cpx_set_synthetic_offset_for_iv(struct cpx *cpx, bool v) { - if (v) - SET(cpx->cpx_flags, CPX_SYNTHETIC_OFFSET_FOR_IV); - else - CLR(cpx->cpx_flags, CPX_SYNTHETIC_OFFSET_FOR_IV); + if (v) { + SET(cpx->cpx_flags, CPX_SYNTHETIC_OFFSET_FOR_IV); + } else { + CLR(cpx->cpx_flags, CPX_SYNTHETIC_OFFSET_FOR_IV); + } } -uint16_t cpx_max_key_len(const struct cpx *cpx) +uint16_t +cpx_max_key_len(const struct cpx *cpx) { return cpx->cpx_max_key_len; } -uint16_t cpx_key_len(const struct cpx *cpx) +uint16_t +cpx_key_len(const struct cpx *cpx) { return cpx->cpx_key_len; } -void cpx_set_key_len(struct cpx *cpx, uint16_t key_len) +void +cpx_set_key_len(struct cpx *cpx, uint16_t key_len) { cpx->cpx_key_len = key_len; @@ -200,30 +289,35 @@ void cpx_set_key_len(struct cpx *cpx, uint16_t key_len) } } -bool cpx_has_key(const struct cpx *cpx) +bool +cpx_has_key(const struct cpx *cpx) { return cpx->cpx_key_len > 0; } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-qual" -void *cpx_key(const struct cpx *cpx) +void * +cpx_key(const struct cpx *cpx) { return (void *)cpx->cpx_cached_key; } #pragma clang diagnostic pop -void cpx_set_aes_iv_key(struct cpx *cpx, void *iv_key) +void +cpx_set_aes_iv_key(struct cpx *cpx, void *iv_key) { aes_encrypt_key128(iv_key, &cpx->cpx_iv_aes_ctx); SET(cpx->cpx_flags, CPX_IV_AES_CTX_INITIALIZED | CPX_USE_OFFSET_FOR_IV); CLR(cpx->cpx_flags, CPX_IV_AES_CTX_VFS); } -aes_encrypt_ctx *cpx_iv_aes_ctx(struct cpx *cpx) +aes_encrypt_ctx * +cpx_iv_aes_ctx(struct cpx *cpx) { - if (ISSET(cpx->cpx_flags, CPX_IV_AES_CTX_INITIALIZED)) - return &cpx->cpx_iv_aes_ctx; + if (ISSET(cpx->cpx_flags, CPX_IV_AES_CTX_INITIALIZED)) { + return &cpx->cpx_iv_aes_ctx; + } SHA1_CTX sha1ctxt; uint8_t digest[SHA_DIGEST_LENGTH]; /* Kiv */ @@ -244,7 +338,8 @@ aes_encrypt_ctx *cpx_iv_aes_ctx(struct cpx *cpx) return &cpx->cpx_iv_aes_ctx; } -void cpx_flush(cpx_t cpx) +void +cpx_flush(cpx_t cpx) { bzero(cpx->cpx_cached_key, cpx->cpx_max_key_len); bzero(&cpx->cpx_iv_aes_ctx, sizeof(cpx->cpx_iv_aes_ctx)); @@ -252,25 +347,28 @@ void cpx_flush(cpx_t cpx) cpx->cpx_key_len = 0; } -bool cpx_can_copy(const struct cpx *src, const struct cpx *dst) +bool +cpx_can_copy(const struct cpx *src, const struct cpx *dst) { return src->cpx_key_len <= dst->cpx_max_key_len; } -void cpx_copy(const struct cpx *src, cpx_t dst) +void +cpx_copy(const struct cpx *src, cpx_t dst) { uint16_t key_len = cpx_key_len(src); cpx_set_key_len(dst, key_len); memcpy(cpx_key(dst), cpx_key(src), key_len); dst->cpx_flags = src->cpx_flags; - if (ISSET(dst->cpx_flags, CPX_IV_AES_CTX_INITIALIZED)) - dst->cpx_iv_aes_ctx = src->cpx_iv_aes_ctx; + if (ISSET(dst->cpx_flags, CPX_IV_AES_CTX_INITIALIZED)) { + dst->cpx_iv_aes_ctx = src->cpx_iv_aes_ctx; + } } typedef struct { cp_lock_state_t state; - int valid_uuid; - uuid_t volume_uuid; + int valid_uuid; + uuid_t volume_uuid; } cp_lock_vfs_callback_arg; static int @@ -278,19 +376,22 @@ cp_lock_vfs_callback(mount_t mp, void *arg) { cp_lock_vfs_callback_arg *callback_arg = (cp_lock_vfs_callback_arg *)arg; - if (callback_arg->valid_uuid) { + if (callback_arg->valid_uuid) { struct vfs_attr va; VFSATTR_INIT(&va); VFSATTR_WANTED(&va, f_uuid); - if (vfs_getattr(mp, &va, vfs_context_current())) + if (vfs_getattr(mp, &va, vfs_context_current())) { return 0; + } - if (!VFSATTR_IS_SUPPORTED(&va, f_uuid)) + if (!VFSATTR_IS_SUPPORTED(&va, f_uuid)) { return 0; + } - if(memcmp(va.f_uuid, callback_arg->volume_uuid, sizeof(uuid_t))) + if (memcmp(va.f_uuid, callback_arg->volume_uuid, sizeof(uuid_t))) { return 0; + } } VFS_IOCTL(mp, FIODEVICELOCKED, (void *)(uintptr_t)callback_arg->state, 0, vfs_context_kernel()); @@ -303,14 +404,14 @@ cp_key_store_action(cp_key_store_action_t action) cp_lock_vfs_callback_arg callback_arg; switch (action) { - case CP_ACTION_LOCKED: - case CP_ACTION_UNLOCKED: - callback_arg.state = (action == CP_ACTION_LOCKED ? CP_LOCKED_STATE : CP_UNLOCKED_STATE); - memset(callback_arg.volume_uuid, 0, sizeof(uuid_t)); - callback_arg.valid_uuid = 0; - return vfs_iterate(0, cp_lock_vfs_callback, (void *)&callback_arg); - default: - return -1; + case CP_ACTION_LOCKED: + case CP_ACTION_UNLOCKED: + callback_arg.state = (action == CP_ACTION_LOCKED ? CP_LOCKED_STATE : CP_UNLOCKED_STATE); + memset(callback_arg.volume_uuid, 0, sizeof(uuid_t)); + callback_arg.valid_uuid = 0; + return vfs_iterate(0, cp_lock_vfs_callback, (void *)&callback_arg); + default: + return -1; } } @@ -320,14 +421,14 @@ cp_key_store_action_for_volume(uuid_t volume_uuid, cp_key_store_action_t action) cp_lock_vfs_callback_arg callback_arg; switch (action) { - case CP_ACTION_LOCKED: - case CP_ACTION_UNLOCKED: - callback_arg.state = (action == CP_ACTION_LOCKED ? CP_LOCKED_STATE : CP_UNLOCKED_STATE); - memcpy(callback_arg.volume_uuid, volume_uuid, sizeof(uuid_t)); - callback_arg.valid_uuid = 1; - return vfs_iterate(0, cp_lock_vfs_callback, (void *)&callback_arg); - default: - return -1; + case CP_ACTION_LOCKED: + case CP_ACTION_UNLOCKED: + callback_arg.state = (action == CP_ACTION_LOCKED ? CP_LOCKED_STATE : CP_UNLOCKED_STATE); + memcpy(callback_arg.volume_uuid, volume_uuid, sizeof(uuid_t)); + callback_arg.valid_uuid = 1; + return vfs_iterate(0, cp_lock_vfs_callback, (void *)&callback_arg); + default: + return -1; } } @@ -341,12 +442,11 @@ cp_is_valid_class(int isdir, int32_t protectionclass) */ if (isdir) { /* Directories are not allowed to have F, but they can have "NONE" */ - return ((protectionclass >= PROTECTION_CLASS_DIR_NONE) && - (protectionclass <= PROTECTION_CLASS_D)); - } - else { - return ((protectionclass >= PROTECTION_CLASS_A) && - (protectionclass <= PROTECTION_CLASS_F)); + return (protectionclass >= PROTECTION_CLASS_DIR_NONE) && + (protectionclass <= PROTECTION_CLASS_D); + } else { + return (protectionclass >= PROTECTION_CLASS_A) && + (protectionclass <= PROTECTION_CLASS_F); } } @@ -366,12 +466,14 @@ parse_os_version(const char *vers) ++p; } - if (!a) + if (!a) { return 0; + } int b = *p++; - if (!b) + if (!b) { return 0; + } int c = 0; while (*p >= '0' && *p <= '9') { @@ -379,8 +481,9 @@ parse_os_version(const char *vers) ++p; } - if (!c) + if (!c) { return 0; + } return (a & 0xff) << 24 | b << 16 | (c & 0xffff); } @@ -390,11 +493,13 @@ cp_os_version(void) { static cp_key_os_version_t cp_os_version; - if (cp_os_version) + if (cp_os_version) { return cp_os_version; + } - if (!osversion[0]) + if (!osversion[0]) { return 0; + } cp_os_version = parse_os_version(osversion); if (!cp_os_version) {