#include "Ext4Crypt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "unencrypted_properties.h" #include "key_control.h" #include "cryptfs.h" #include "ext4_crypt_init_extensions.h" #define LOG_TAG "Ext4Crypt" #include "cutils/log.h" #include #include #include namespace { // Key length in bits const int key_length = 128; static_assert(key_length % 8 == 0, "Key length must be multiple of 8 bits"); // How long do we store passwords for? const int password_max_age_seconds = 60; // How is device encrypted struct keys { std::string master_key; std::string password; time_t expiry_time; }; std::map s_key_store; // ext4enc:TODO get these consts from somewhere good const int SHA512_LENGTH = 64; const int EXT4_KEY_DESCRIPTOR_SIZE = 8; // ext4enc:TODO Include structure from somewhere sensible // MUST be in sync with ext4_crypto.c in kernel const int EXT4_MAX_KEY_SIZE = 64; const int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1; struct ext4_encryption_key { uint32_t mode; char raw[EXT4_MAX_KEY_SIZE]; uint32_t size; }; namespace tag { const char* magic = "magic"; const char* major_version = "major_version"; const char* minor_version = "minor_version"; const char* flags = "flags"; const char* crypt_type = "crypt_type"; const char* failed_decrypt_count = "failed_decrypt_count"; const char* crypto_type_name = "crypto_type_name"; const char* master_key = "master_key"; const char* salt = "salt"; const char* kdf_type = "kdf_type"; const char* N_factor = "N_factor"; const char* r_factor = "r_factor"; const char* p_factor = "p_factor"; const char* keymaster_blob = "keymaster_blob"; const char* scrypted_intermediate_key = "scrypted_intermediate_key"; } } static std::string e4crypt_install_key(const std::string &key); static int put_crypt_ftr_and_key(const crypt_mnt_ftr& crypt_ftr, UnencryptedProperties& props) { SLOGI("Putting crypt footer"); bool success = props.Set(tag::magic, crypt_ftr.magic) && props.Set(tag::major_version, crypt_ftr.major_version) && props.Set(tag::minor_version, crypt_ftr.minor_version) && props.Set(tag::flags, crypt_ftr.flags) && props.Set(tag::crypt_type, crypt_ftr.crypt_type) && props.Set(tag::failed_decrypt_count, crypt_ftr.failed_decrypt_count) && props.Set(tag::crypto_type_name, std::string(reinterpret_cast(crypt_ftr.crypto_type_name))) && props.Set(tag::master_key, std::string((const char*) crypt_ftr.master_key, crypt_ftr.keysize)) && props.Set(tag::salt, std::string((const char*) crypt_ftr.salt, SALT_LEN)) && props.Set(tag::kdf_type, crypt_ftr.kdf_type) && props.Set(tag::N_factor, crypt_ftr.N_factor) && props.Set(tag::r_factor, crypt_ftr.r_factor) && props.Set(tag::p_factor, crypt_ftr.p_factor) && props.Set(tag::keymaster_blob, std::string((const char*) crypt_ftr.keymaster_blob, crypt_ftr.keymaster_blob_size)) && props.Set(tag::scrypted_intermediate_key, std::string((const char*) crypt_ftr.scrypted_intermediate_key, SCRYPT_LEN)); return success ? 0 : -1; } static int get_crypt_ftr_and_key(crypt_mnt_ftr& crypt_ftr, const UnencryptedProperties& props) { memset(&crypt_ftr, 0, sizeof(crypt_ftr)); crypt_ftr.magic = props.Get(tag::magic); crypt_ftr.major_version = props.Get(tag::major_version); crypt_ftr.minor_version = props.Get(tag::minor_version); crypt_ftr.ftr_size = sizeof(crypt_ftr); crypt_ftr.flags = props.Get(tag::flags); crypt_ftr.crypt_type = props.Get(tag::crypt_type); crypt_ftr.failed_decrypt_count = props.Get(tag::failed_decrypt_count); std::string crypto_type_name = props.Get(tag::crypto_type_name); strlcpy(reinterpret_cast(crypt_ftr.crypto_type_name), crypto_type_name.c_str(), sizeof(crypt_ftr.crypto_type_name)); std::string master_key = props.Get(tag::master_key); crypt_ftr.keysize = master_key.size(); if (crypt_ftr.keysize > sizeof(crypt_ftr.master_key)) { SLOGE("Master key size too long"); return -1; } memcpy(crypt_ftr.master_key, &master_key[0], crypt_ftr.keysize); std::string salt = props.Get(tag::salt); if (salt.size() != SALT_LEN) { SLOGE("Salt wrong length"); return -1; } memcpy(crypt_ftr.salt, &salt[0], SALT_LEN); crypt_ftr.kdf_type = props.Get(tag::kdf_type); crypt_ftr.N_factor = props.Get(tag::N_factor); crypt_ftr.r_factor = props.Get(tag::r_factor); crypt_ftr.p_factor = props.Get(tag::p_factor); std::string keymaster_blob = props.Get(tag::keymaster_blob); crypt_ftr.keymaster_blob_size = keymaster_blob.size(); if (crypt_ftr.keymaster_blob_size > sizeof(crypt_ftr.keymaster_blob)) { SLOGE("Keymaster blob too long"); return -1; } memcpy(crypt_ftr.keymaster_blob, &keymaster_blob[0], crypt_ftr.keymaster_blob_size); std::string scrypted_intermediate_key = props.Get(tag::scrypted_intermediate_key); if (scrypted_intermediate_key.size() != SCRYPT_LEN) { SLOGE("scrypted intermediate key wrong length"); return -1; } memcpy(crypt_ftr.scrypted_intermediate_key, &scrypted_intermediate_key[0], SCRYPT_LEN); return 0; } static UnencryptedProperties GetProps(const char* path) { return UnencryptedProperties(path); } static UnencryptedProperties GetAltProps(const char* path) { return UnencryptedProperties((std::string() + path + "/tmp_mnt").c_str()); } static UnencryptedProperties GetPropsOrAltProps(const char* path) { UnencryptedProperties props = GetProps(path); if (props.OK()) { return props; } return GetAltProps(path); } int e4crypt_enable(const char* path) { // Already enabled? if (s_key_store.find(path) != s_key_store.end()) { return 0; } // Not an encryptable device? UnencryptedProperties key_props = GetProps(path).GetChild(properties::key); if (!key_props.OK()) { return 0; } if (key_props.Get(tag::master_key).empty()) { crypt_mnt_ftr ftr; if (cryptfs_create_default_ftr(&ftr, key_length)) { SLOGE("Failed to create crypto footer"); return -1; } // Scrub fields not used by ext4enc ftr.persist_data_offset[0] = 0; ftr.persist_data_offset[1] = 0; ftr.persist_data_size = 0; if (put_crypt_ftr_and_key(ftr, key_props)) { SLOGE("Failed to write crypto footer"); return -1; } crypt_mnt_ftr ftr2; if (get_crypt_ftr_and_key(ftr2, key_props)) { SLOGE("Failed to read crypto footer back"); return -1; } if (memcmp(&ftr, &ftr2, sizeof(ftr)) != 0) { SLOGE("Crypto footer not correctly written"); return -1; } } if (!UnencryptedProperties(path).Remove(properties::ref)) { SLOGE("Failed to remove key ref"); return -1; } return e4crypt_check_passwd(path, ""); } int e4crypt_change_password(const char* path, int crypt_type, const char* password) { SLOGI("e4crypt_change_password"); auto key_props = GetProps(path).GetChild(properties::key); crypt_mnt_ftr ftr; if (get_crypt_ftr_and_key(ftr, key_props)) { SLOGE("Failed to read crypto footer back"); return -1; } auto mki = s_key_store.find(path); if (mki == s_key_store.end()) { SLOGE("No stored master key - can't change password"); return -1; } const unsigned char* master_key_bytes = reinterpret_cast(&mki->second.master_key[0]); if (cryptfs_set_password(&ftr, password, master_key_bytes)) { SLOGE("Failed to set password"); return -1; } ftr.crypt_type = crypt_type; if (put_crypt_ftr_and_key(ftr, key_props)) { SLOGE("Failed to write crypto footer"); return -1; } if (!UnencryptedProperties(path).Set(properties::is_default, crypt_type == CRYPT_TYPE_DEFAULT)) { SLOGE("Failed to update default flag"); return -1; } return 0; } int e4crypt_crypto_complete(const char* path) { SLOGI("ext4 crypto complete called on %s", path); auto key_props = GetPropsOrAltProps(path).GetChild(properties::key); if (key_props.Get(tag::master_key).empty()) { SLOGI("No master key, so not ext4enc"); return -1; } return 0; } // Get raw keyref - used to make keyname and to pass to ioctl static std::string generate_key_ref(const char* key, int length) { SHA512_CTX c; SHA512_Init(&c); SHA512_Update(&c, key, length); unsigned char key_ref1[SHA512_LENGTH]; SHA512_Final(key_ref1, &c); SHA512_Init(&c); SHA512_Update(&c, key_ref1, SHA512_LENGTH); unsigned char key_ref2[SHA512_LENGTH]; SHA512_Final(key_ref2, &c); return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE); } int e4crypt_check_passwd(const char* path, const char* password) { SLOGI("e4crypt_check_password"); auto props = GetPropsOrAltProps(path); auto key_props = props.GetChild(properties::key); crypt_mnt_ftr ftr; if (get_crypt_ftr_and_key(ftr, key_props)) { SLOGE("Failed to read crypto footer back"); return -1; } unsigned char master_key_bytes[key_length / 8]; if (cryptfs_get_master_key (&ftr, password, master_key_bytes)){ SLOGI("Incorrect password"); ftr.failed_decrypt_count++; if (put_crypt_ftr_and_key(ftr, key_props)) { SLOGW("Failed to update failed_decrypt_count"); } return ftr.failed_decrypt_count; } if (ftr.failed_decrypt_count) { ftr.failed_decrypt_count = 0; if (put_crypt_ftr_and_key(ftr, key_props)) { SLOGW("Failed to reset failed_decrypt_count"); } } std::string master_key(reinterpret_cast(master_key_bytes), sizeof(master_key_bytes)); struct timespec now; clock_gettime(CLOCK_BOOTTIME, &now); s_key_store[path] = keys{master_key, password, now.tv_sec + password_max_age_seconds}; auto raw_ref = e4crypt_install_key(master_key); if (raw_ref.empty()) { return -1; } // Save reference to key so we can set policy later if (!props.Set(properties::ref, raw_ref)) { SLOGE("Cannot save key reference"); return -1; } return 0; } static ext4_encryption_key fill_key(const std::string &key) { // ext4enc:TODO Currently raw key is required to be of length // sizeof(ext4_key.raw) == EXT4_MAX_KEY_SIZE, so zero pad to // this length. Change when kernel bug is fixed. ext4_encryption_key ext4_key = {EXT4_ENCRYPTION_MODE_AES_256_XTS, {0}, sizeof(ext4_key.raw)}; memset(ext4_key.raw, 0, sizeof(ext4_key.raw)); static_assert(key_length / 8 <= sizeof(ext4_key.raw), "Key too long!"); memcpy(ext4_key.raw, &key[0], key.size()); return ext4_key; } static std::string keyname(const std::string &raw_ref) { std::ostringstream o; o << "ext4:"; for (auto i = raw_ref.begin(); i != raw_ref.end(); ++i) { o << std::hex << std::setw(2) << std::setfill('0') << (int)*i; } return o.str(); } // Get the keyring we store all keys in static key_serial_t e4crypt_keyring() { return keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0); } static int e4crypt_install_key(const ext4_encryption_key &ext4_key, const std::string &ref) { key_serial_t device_keyring = e4crypt_keyring(); SLOGI("Found device_keyring - id is %d", device_keyring); key_serial_t key_id = add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring); if (key_id == -1) { SLOGE("Failed to insert key into keyring with error %s", strerror(errno)); return -1; } SLOGI("Added key %d (%s) to keyring %d in process %d", key_id, ref.c_str(), device_keyring, getpid()); return 0; } // Install password into global keyring // Return raw key reference for use in policy static std::string e4crypt_install_key(const std::string &key) { auto ext4_key = fill_key(key); auto raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size); auto ref = keyname(raw_ref); if (e4crypt_install_key(ext4_key, ref) == -1) { return ""; } return raw_ref; } int e4crypt_restart(const char* path) { SLOGI("e4crypt_restart"); int rc = 0; SLOGI("ext4 restart called on %s", path); property_set("vold.decrypt", "trigger_reset_main"); SLOGI("Just asked init to shut down class main"); sleep(2); std::string tmp_path = std::string() + path + "/tmp_mnt"; rc = wait_and_unmount(tmp_path.c_str(), true); if (rc) { SLOGE("umount %s failed with rc %d, msg %s", tmp_path.c_str(), rc, strerror(errno)); return rc; } rc = wait_and_unmount(path, true); if (rc) { SLOGE("umount %s failed with rc %d, msg %s", path, rc, strerror(errno)); return rc; } return 0; } int e4crypt_get_password_type(const char* path) { SLOGI("e4crypt_get_password_type"); return GetPropsOrAltProps(path).GetChild(properties::key) .Get(tag::crypt_type, CRYPT_TYPE_DEFAULT); } const char* e4crypt_get_password(const char* path) { SLOGI("e4crypt_get_password"); auto i = s_key_store.find(path); if (i == s_key_store.end()) { return 0; } struct timespec now; clock_gettime(CLOCK_BOOTTIME, &now); if (i->second.expiry_time < now.tv_sec) { e4crypt_clear_password(path); return 0; } return i->second.password.c_str(); } void e4crypt_clear_password(const char* path) { SLOGI("e4crypt_clear_password"); auto i = s_key_store.find(path); if (i == s_key_store.end()) { return; } memset(&i->second.password[0], 0, i->second.password.size()); i->second.password = std::string(); } int e4crypt_get_field(const char* path, const char* fieldname, char* value, size_t len) { auto v = GetPropsOrAltProps(path).GetChild(properties::props) .Get(fieldname); if (v == "") { return CRYPTO_GETFIELD_ERROR_NO_FIELD; } if (v.length() >= len) { return CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL; } strlcpy(value, v.c_str(), len); return 0; } int e4crypt_set_field(const char* path, const char* fieldname, const char* value) { return GetPropsOrAltProps(path).GetChild(properties::props) .Set(fieldname, std::string(value)) ? 0 : -1; } static std::string get_key_path( const char *mount_path, const char *user_handle) { // ext4enc:TODO get the path properly auto key_dir = android::base::StringPrintf("%s/misc/vold/user_keys", mount_path); if (mkdir(key_dir.c_str(), 0700) < 0 && errno != EEXIST) { SLOGE("Unable to create %s (%s)", key_dir.c_str(), strerror(errno)); return ""; } return key_dir + "/" + user_handle; } // ext4enc:TODO this can't be the only place keys are read from /dev/urandom // we should unite those places. static std::string e4crypt_get_key( const std::string &key_path, bool create_if_absent) { std::string content; if (android::base::ReadFileToString(key_path, &content)) { if (content.size() != key_length/8) { SLOGE("Wrong size key %zu in %s", content.size(), key_path.c_str()); return ""; } return content; } if (!create_if_absent) { SLOGE("No key found in %s", key_path.c_str()); return ""; } std::ifstream urandom("/dev/urandom"); if (!urandom) { SLOGE("Unable to open /dev/urandom (%s)", strerror(errno)); return ""; } char key_bytes[key_length / 8]; errno = 0; urandom.read(key_bytes, sizeof(key_bytes)); if (!urandom) { SLOGE("Unable to read key from /dev/urandom (%s)", strerror(errno)); return ""; } std::string key(key_bytes, sizeof(key_bytes)); if (!android::base::WriteStringToFile(key, key_path)) { SLOGE("Unable to write key to %s (%s)", key_path.c_str(), strerror(errno)); return ""; } return key; } static int e4crypt_set_user_policy(const char *mount_path, const char *user_handle, const char *path, bool create_if_absent) { SLOGD("e4crypt_set_user_policy for %s", user_handle); auto user_key = e4crypt_get_key( get_key_path(mount_path, user_handle), create_if_absent); if (user_key.empty()) { return -1; } auto raw_ref = e4crypt_install_key(user_key); if (raw_ref.empty()) { return -1; } return do_policy_set(path, raw_ref.c_str(), raw_ref.size()); } int e4crypt_create_new_user_dir(const char *user_handle, const char *path) { SLOGD("e4crypt_create_new_user_dir(\"%s\", \"%s\")", user_handle, path); if (mkdir(path, S_IRWXU | S_IRWXG | S_IXOTH) < 0) { return -1; } if (chmod(path, S_IRWXU | S_IRWXG | S_IXOTH) < 0) { return -1; } if (chown(path, AID_SYSTEM, AID_SYSTEM) < 0) { return -1; } if (e4crypt_crypto_complete(DATA_MNT_POINT) == 0) { // ext4enc:TODO handle errors from this. e4crypt_set_user_policy(DATA_MNT_POINT, user_handle, path, true); } return 0; } static bool is_numeric(const char *name) { for (const char *p = name; *p != '\0'; p++) { if (!isdigit(*p)) return false; } return true; } int e4crypt_set_user_crypto_policies(const char *dir) { if (e4crypt_crypto_complete(DATA_MNT_POINT) != 0) { return 0; } SLOGD("e4crypt_set_user_crypto_policies"); std::unique_ptr dirp(opendir(dir), closedir); if (!dirp) { SLOGE("Unable to read directory %s, error %s\n", dir, strerror(errno)); return -1; } for (;;) { struct dirent *result = readdir(dirp.get()); if (!result) { // ext4enc:TODO check errno break; } if (result->d_type != DT_DIR || !is_numeric(result->d_name)) { continue; // skips user 0, which is a symlink } auto user_dir = std::string() + dir + "/" + result->d_name; // ext4enc:TODO don't hardcode /data if (e4crypt_set_user_policy("/data", result->d_name, user_dir.c_str(), false)) { // ext4enc:TODO If this function fails, stop the boot: we must // deliver on promised encryption. SLOGE("Unable to set policy on %s\n", user_dir.c_str()); } } return 0; } int e4crypt_delete_user_key(const char *user_handle) { SLOGD("e4crypt_delete_user_key(\"%s\")", user_handle); auto key_path = get_key_path(DATA_MNT_POINT, user_handle); auto key = e4crypt_get_key(key_path, false); auto ext4_key = fill_key(key); auto ref = keyname(generate_key_ref(ext4_key.raw, ext4_key.size)); auto key_serial = keyctl_search(e4crypt_keyring(), "logon", ref.c_str(), 0); if (keyctl_revoke(key_serial) == 0) { SLOGD("Revoked key with serial %ld ref %s\n", key_serial, ref.c_str()); } else { SLOGE("Failed to revoke key with serial %ld ref %s: %s\n", key_serial, ref.c_str(), strerror(errno)); } int pid = fork(); if (pid < 0) { SLOGE("Unable to fork: %s", strerror(errno)); return -1; } if (pid == 0) { SLOGD("Forked for secdiscard"); execl("/system/bin/secdiscard", "/system/bin/secdiscard", key_path.c_str(), NULL); SLOGE("Unable to launch secdiscard on %s: %s\n", key_path.c_str(), strerror(errno)); exit(-1); } // ext4enc:TODO reap the zombie return 0; }