diff --git a/Android.mk b/Android.mk index c913a1e..d4a178a 100644 --- a/Android.mk +++ b/Android.mk @@ -15,6 +15,7 @@ common_src_files := \ Devmapper.cpp \ ResponseCode.cpp \ CheckBattery.cpp \ + Ext4Crypt.cpp \ VoldUtil.c \ fstrim.c \ cryptfs.c diff --git a/CommandListener.cpp b/CommandListener.cpp index c66b9d2..73a26e4 100644 --- a/CommandListener.cpp +++ b/CommandListener.cpp @@ -619,6 +619,14 @@ int CommandListener::CryptfsCmd::runCommand(SocketClient *cli, Process::killProcessesWithOpenFiles(DATA_MNT_POINT, 2); } } + } else if (!strcmp(argv[1], "enablefilecrypto")) { + const char* syntax = "Usage: cryptfs enablefilecrypto"; + if (argc != 2) { + cli->sendMsg(ResponseCode::CommandSyntaxError, syntax, false); + return 0; + } + dumpArgs(argc, argv, -1); + rc = cryptfs_enable_file(); } else if (!strcmp(argv[1], "changepw")) { const char* syntax = "Usage: cryptfs changepw " "default|password|pin|pattern [newpasswd]"; diff --git a/Ext4Crypt.cpp b/Ext4Crypt.cpp new file mode 100644 index 0000000..84393c9 --- /dev/null +++ b/Ext4Crypt.cpp @@ -0,0 +1,377 @@ +#include "Ext4Crypt.h" + +#include +#include +#include + +#include +#include +#include + +#include "unencrypted_properties.h" +#include "key_control.h" +#include "cryptfs.h" + +#define LOG_TAG "Ext4Crypt" +#include "cutils/log.h" +#include + +namespace { + // Key length in bits + const int key_length = 128; + + // How is device encrypted + struct keys { + std::string master_key; + std::string password; + }; + std::map s_key_store; + + // ext4enc:TODO Include structure from somewhere sensible + // MUST be in sync with ext4_crypto.c in kernel + const int EXT4_MAX_KEY_SIZE = 76; + 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 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.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; + } + + 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"); + // ex4enc:TODO why is this failing? + //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"); + + UnencryptedProperties 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 + = reinterpret_cast(&mki->second.master_key[0]); + + if (cryptfs_set_password(&ftr, password, master_key)) { + 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); + UnencryptedProperties 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; +} + +int e4crypt_check_passwd(const char* path, const char* password) +{ + SLOGI("e4crypt_check_password"); + + // ext4enc:TODO once we have password checking, fix this to be + // GetKeyOrAltKey + UnencryptedProperties props = *password ? GetAltProps(path) + : GetProps(path); + UnencryptedProperties 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[key_length / 8]; + if (cryptfs_get_master_key (&ftr, password, master_key)){ + SLOGI("Incorrect password"); + return -1; + } + + s_key_store[path] = keys{std::string(reinterpret_cast(master_key), + sizeof(master_key)), + password}; + + // Install password into global keyring + ext4_encryption_key ext4_key = {0, {0}, key_length / 8}; + memcpy(ext4_key.raw, master_key, ext4_key.size); + + // ext4enc:TODO Use better reference not 1234567890 + key_serial_t device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, + "keyring", "e4crypt", 0); + + SLOGI("Found device_keyring - id is %d", device_keyring); + + key_serial_t key_id = add_key("logon", "ext4-key:1234567890", + (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 to keyring %d in process %d", + key_id, device_keyring, getpid()); + + // ext4enc:TODO set correct permissions + long result = keyctl_setperm(key_id, 0x3f3f3f3f); + if (result) { + SLOGE("KEYCTL_SETPERM failed with error %ld", result); + return -1; + } + + // Save reference to key so we can set policy later + if (!props.Set(properties::ref, "@s.ext4-key:1234567890")) { + SLOGE("Cannot save key reference"); + return -1; + } + + return 0; +} + +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"; + + // ext4enc:TODO add retry logic + rc = umount(tmp_path.c_str()); + if (rc) { + SLOGE("umount %s failed with rc %d, msg %s", + tmp_path.c_str(), rc, strerror(errno)); + return rc; + } + + // ext4enc:TODO add retry logic + rc = umount(path); + if (rc) { + SLOGE("umount %s failed with rc %d, msg %s", + path, rc, strerror(errno)); + return rc; + } + + return 0; +} + +const char* e4crypt_get_password(const char* path) +{ + SLOGI("e4crypt_get_password"); + + // ext4enc:TODO scrub password after timeout + auto i = s_key_store.find(path); + if (i == s_key_store.end()) { + return 0; + } else { + return i->second.password.c_str(); + } +} + +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); +} diff --git a/Ext4Crypt.h b/Ext4Crypt.h new file mode 100644 index 0000000..301639d --- /dev/null +++ b/Ext4Crypt.h @@ -0,0 +1,16 @@ +#include + +__BEGIN_DECLS + +// General functions +int e4crypt_enable(const char* path); +int e4crypt_main(int argc, char* argv[]); +int e4crypt_change_password(const char* path, int crypt_type, + const char* password); +int e4crypt_crypto_complete(const char* path); +int e4crypt_check_passwd(const char* path, const char* password); +int e4crypt_get_password_type(const char* path); +const char* e4crypt_get_password(const char* path); +int e4crypt_restart(const char* path); + +__END_DECLS diff --git a/cryptfs.c b/cryptfs.c index 91487ed..95b882f 100644 --- a/cryptfs.c +++ b/cryptfs.c @@ -53,7 +53,8 @@ #include "VolumeManager.h" #include "VoldUtil.h" #include "crypto_scrypt.h" -#include "ext4_crypt.h" +#include "Ext4Crypt.h" +#include "ext4_crypt_init_extensions.h" #include "ext4_utils.h" #include "f2fs_sparseblock.h" #include "CheckBattery.h" @@ -1310,7 +1311,7 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt, /* Encrypt the master key */ if (! EVP_EncryptUpdate(&e_ctx, encrypted_master_key, &encrypted_len, - decrypted_master_key, KEY_LEN_BYTES)) { + decrypted_master_key, KEY_LEN_BYTES)) { SLOGE("EVP_EncryptUpdate failed\n"); return -1; } @@ -1345,7 +1346,7 @@ static int encrypt_master_key(const char *passwd, const unsigned char *salt, return 0; } -static int decrypt_master_key_aux(char *passwd, unsigned char *salt, +static int decrypt_master_key_aux(const char *passwd, unsigned char *salt, unsigned char *encrypted_master_key, unsigned char *decrypted_master_key, kdf_func kdf, void *kdf_params, @@ -1410,7 +1411,7 @@ static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_pa } } -static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key, +static int decrypt_master_key(const char *passwd, unsigned char *decrypted_master_key, struct crypt_mnt_ftr *crypt_ftr, unsigned char** intermediate_key, size_t* intermediate_key_size) @@ -3804,3 +3805,46 @@ void cryptfs_clear_password() password_expiry_time = 0; } } + +int cryptfs_enable_file() +{ + return e4crypt_enable(DATA_MNT_POINT); +} + +int cryptfs_create_default_ftr(struct crypt_mnt_ftr* crypt_ftr, __attribute__((unused))int key_length) +{ + if (cryptfs_init_crypt_mnt_ftr(crypt_ftr)) { + SLOGE("Failed to initialize crypt_ftr"); + return -1; + } + + if (create_encrypted_random_key(DEFAULT_PASSWORD, crypt_ftr->master_key, + crypt_ftr->salt, crypt_ftr)) { + SLOGE("Cannot create encrypted master key\n"); + return -1; + } + + //crypt_ftr->keysize = key_length / 8; + return 0; +} + +int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password, + unsigned char* master_key) +{ + int rc; + + // ext4enc:TODO check intermediate_key to see if this is valid key + unsigned char* intermediate_key = 0; + size_t intermediate_key_size = 0; + rc = decrypt_master_key(password, master_key, ftr, &intermediate_key, + &intermediate_key_size); + + return rc; +} + +int cryptfs_set_password(struct crypt_mnt_ftr* ftr, const char* password, + const unsigned char* master_key) +{ + return encrypt_master_key(password, ftr->salt, master_key, ftr->master_key, + ftr); +} diff --git a/cryptfs.h b/cryptfs.h index 1da6f94..e149061 100644 --- a/cryptfs.h +++ b/cryptfs.h @@ -235,6 +235,7 @@ extern "C" { int cryptfs_enable(char *flag, int type, char *passwd, int allow_reboot); int cryptfs_changepw(int type, const char *newpw); int cryptfs_enable_default(char *flag, int allow_reboot); + int cryptfs_enable_file(); int cryptfs_setup_volume(const char *label, int major, int minor, char *crypto_dev_path, unsigned int max_pathlen, int *new_major, int *new_minor); @@ -245,6 +246,13 @@ extern "C" { int cryptfs_get_password_type(void); const char* cryptfs_get_password(void); void cryptfs_clear_password(void); + + // Functions for file encryption to use to inherit our encryption logic + int cryptfs_create_default_ftr(struct crypt_mnt_ftr* ftr, int key_length); + int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password, + unsigned char* master_key); + int cryptfs_set_password(struct crypt_mnt_ftr* ftr, const char* password, + const unsigned char* master_key); #ifdef __cplusplus } #endif