diff options
author | Paul Lawrence <paullawrence@google.com> | 2015-03-16 15:35:55 -0700 |
---|---|---|
committer | JP Abgrall <jpa@google.com> | 2015-04-01 19:25:51 +0000 |
commit | 61980269c14ae78a9ca893a56df67cf475cdfafd (patch) | |
tree | 67b35eb8531604b94fec5cb50e7b96bbb911850c | |
parent | 17fb21d013743a93bab3cdfc899e952184cae58c (diff) | |
download | extras-61980269c14ae78a9ca893a56df67cf475cdfafd.tar.gz |
ext4_utils: Abstracting ext4 encryption property system
Change-Id: Iddca42328e9249eb09eb68e6d5339a02cfebecd0
-rw-r--r-- | ext4_utils/Android.mk | 3 | ||||
-rw-r--r-- | ext4_utils/ext4_crypt.cpp | 46 | ||||
-rw-r--r-- | ext4_utils/ext4_crypt_init_extensions.cpp | 94 | ||||
-rw-r--r-- | ext4_utils/unencrypted_properties.cpp | 86 | ||||
-rw-r--r-- | ext4_utils/unencrypted_properties.h | 70 |
5 files changed, 218 insertions, 81 deletions
diff --git a/ext4_utils/Android.mk b/ext4_utils/Android.mk index 964f6668..76dd4534 100644 --- a/ext4_utils/Android.mk +++ b/ext4_utils/Android.mk @@ -54,7 +54,8 @@ include $(BUILD_HOST_EXECUTABLE) libext4_utils_src_files += \ ext4_crypt.cpp \ - e4crypt_static.c + e4crypt_static.c \ + unencrypted_properties.cpp ifneq ($(HOST_OS),windows) diff --git a/ext4_utils/ext4_crypt.cpp b/ext4_utils/ext4_crypt.cpp index 70ad070d..bb573323 100644 --- a/ext4_utils/ext4_crypt.cpp +++ b/ext4_utils/ext4_crypt.cpp @@ -12,35 +12,29 @@ #include <cutils/klog.h> #include <cutils/properties.h> -// ext4enc::TODO remove this duplicated const -static const std::string unencrypted_path = "/unencrypted"; +#include "unencrypted_properties.h" -static std::map<std::string, std::string> s_password_store; +namespace { + std::map<std::string, std::string> s_password_store; +} bool e4crypt_non_default_key(const char* dir) { int type = e4crypt_get_password_type(dir); + + // ext4enc:TODO Use consts, not 1 here return type != -1 && type != 1; } int e4crypt_get_password_type(const char* path) { - auto full_path = std::string() + path + unencrypted_path; - if (!std::ifstream(full_path + "/key")) { + UnencryptedProperties props(path); + if (props.Get<std::string>(properties::key).empty()) { KLOG_INFO(TAG, "No master key, so not ext4enc\n"); return -1; } - std::ifstream type(full_path + "/type"); - if (!type) { - KLOG_INFO(TAG, "No password type so default\n"); - return 1; // Default - } - - int value = 0; - type >> value; - KLOG_INFO(TAG, "Password type is %d\n", value); - return value; + return props.Get<int>(properties::type, 1); } int e4crypt_change_password(const char* path, int crypt_type, @@ -48,18 +42,17 @@ int e4crypt_change_password(const char* path, int crypt_type, { // ext4enc:TODO Encrypt master key with password securely. Store hash of // master key for validation - auto full_path = std::string() + path + unencrypted_path; - std::ofstream(full_path + "/password") << password; - std::ofstream(full_path + "/type") << crypt_type; - return 0; + UnencryptedProperties props(path); + if ( props.Set(properties::password, password) + && props.Set(properties::type, crypt_type)) + return 0; + return -1; } -int e4crypt_crypto_complete(const char* path) +int e4crypt_crypto_complete(const char* path) { KLOG_INFO(TAG, "ext4 crypto complete called on %s\n", path); - - auto full_path = std::string() + path + unencrypted_path; - if (!std::ifstream(full_path + "/key")) { + if (UnencryptedProperties(path).Get<std::string>(properties::key).empty()) { KLOG_INFO(TAG, "No master key, so not ext4enc\n"); return -1; } @@ -69,14 +62,13 @@ int e4crypt_crypto_complete(const char* path) int e4crypt_check_passwd(const char* path, const char* password) { - auto full_path = std::string() + path + unencrypted_path; - if (!std::ifstream(full_path + "/key")) { + UnencryptedProperties props(path); + if (props.Get<std::string>(properties::key).empty()) { KLOG_INFO(TAG, "No master key, so not ext4enc\n"); return -1; } - std::string actual_password; - std::ifstream(full_path + "/password") >> actual_password; + auto actual_password = props.Get<std::string>(properties::password); if (actual_password == password) { s_password_store[path] = password; diff --git a/ext4_utils/ext4_crypt_init_extensions.cpp b/ext4_utils/ext4_crypt_init_extensions.cpp index 16d333e9..6d73fb2f 100644 --- a/ext4_utils/ext4_crypt_init_extensions.cpp +++ b/ext4_utils/ext4_crypt_init_extensions.cpp @@ -15,6 +15,8 @@ #include <cutils/properties.h> #include <cutils/sockets.h> +#include "unencrypted_properties.h" + // ext4enc:TODO Include structure from somewhere sensible // MUST be in sync with ext4_crypto.c in kernel #define EXT4_MAX_KEY_SIZE 76 @@ -24,7 +26,6 @@ struct ext4_encryption_key { uint32_t size; }; -static const std::string unencrypted_path = "/unencrypted"; static const std::string keyring = "@s"; static const std::string arbitrary_sequence_number = "42"; @@ -104,46 +105,40 @@ int e4crypt_create_device_key(const char* dir, { // Make sure folder exists. Use make_dir to set selinux permissions. KLOG_INFO(TAG, "Creating test device key\n"); - std::string path = std::string() + dir + unencrypted_path; - if (ensure_dir_exists(path.c_str())) { + UnencryptedProperties props(dir); + if (ensure_dir_exists(props.GetPath().c_str())) { KLOG_ERROR(TAG, "Failed to create %s with error %s\n", - path.c_str(), strerror(errno)); + props.GetPath().c_str(), strerror(errno)); return -1; } - // Open key if it exists - std::string key_path = path + "/key"; - std::ifstream key(key_path.c_str(), std::ifstream::binary); - - if (!key.good()) { - // Create new key if it doesn't - std::ofstream new_key(key_path.c_str(), std::ofstream::binary); - if (!new_key) { - KLOG_ERROR(TAG, "Failed to open %s\n", key_path.c_str()); - return -1; - } - + if (props.Get<std::string>(properties::key).empty()) { + // Create new key since it doesn't already exist std::ifstream urandom("/dev/urandom", std::ifstream::binary); if (!urandom) { KLOG_ERROR(TAG, "Failed to open /dev/urandom\n"); return -1; } - char key_material[32]; - urandom.read(key_material, 32); + // ext4enc:TODO Don't hardcode 32 + std::string key_material(32, '\0'); + urandom.read(&key_material[0], key_material.length()); if (!urandom) { KLOG_ERROR(TAG, "Failed to read random bytes\n"); return -1; } - new_key.write(key_material, 32); - if (!new_key) { + if (!props.Set(properties::key, key_material)) { KLOG_ERROR(TAG, "Failed to write key material"); return -1; } } - remove((std::string(dir) + "/ref").c_str()); + if (!props.Remove(properties::ref)) { + KLOG_ERROR(TAG, "Failed to remove key ref\n"); + return -1; + } + return 0; } @@ -160,7 +155,8 @@ int e4crypt_install_keyring() return -1; } - KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n", device_keyring, getpid()); + KLOG_INFO(TAG, "Keyring created wth id %d in process %d\n", + device_keyring, getpid()); // ext4enc:TODO set correct permissions long result = keyctl_setperm(device_keyring, 0x3f3f3f3f); @@ -174,23 +170,8 @@ int e4crypt_install_keyring() int e4crypt_install_key(const char* dir) { - std::string path = std::string() + dir + unencrypted_path; - - // Open key if it exists - std::string key_path = path + "/key"; - std::ifstream key(key_path.c_str(), std::ifstream::binary); - if (!key.good()) { - KLOG_ERROR(TAG, "Failed to open key %s\n", key_path.c_str()); - return -1; - } - - char keyblob[256]; - key.read(keyblob, sizeof(keyblob)); - std::streamsize keyblob_size = key.gcount(); - if (keyblob_size <= 0) { - KLOG_ERROR(TAG, "Failed to read key data\n"); - return -1; - } + UnencryptedProperties props(dir); + auto key = props.Get<std::string>(properties::key); // Get password to decrypt as needed if (e4crypt_non_default_key(dir)) { @@ -225,8 +206,14 @@ int e4crypt_install_key(const char* dir) // Add key to keyring ext4_encryption_key ext4_key = {0, {0}, 0}; - memcpy(ext4_key.raw, keyblob, keyblob_size); - ext4_key.size = keyblob_size; + if (key.length() > sizeof(ext4_key.raw)) { + KLOG_ERROR(TAG, "Key too long\n"); + return -1; + } + + ext4_key.mode = 0; + memcpy(ext4_key.raw, &key[0], key.length()); + ext4_key.size = key.length(); // ext4enc:TODO Use better reference not 1234567890 key_serial_t key_id = add_key("logon", "ext4-key:1234567890", @@ -250,29 +237,30 @@ int e4crypt_install_key(const char* dir) } // Save reference to key so we can set policy later - std::ofstream(path + "/ref") << "ext4-key:1234567890"; + if (!props.Set(properties::ref, "ext4-key:1234567890")) { + KLOG_ERROR(TAG, "Cannot save key reference\n"); + return -1; + } + return 0; } int e4crypt_set_directory_policy(const char* dir) { // Only set policy on first level /data directories - // ext4enc:TODO don't hard code /data/ + // To make this less restrictive, consider using a policy file. + // However this is overkill for as long as the policy is simply + // to apply a global policy to all /data folders created via makedir if (!dir || strncmp(dir, "/data/", 6) || strchr(dir + 6, '/')) { return 0; } - std::ifstream ref_file("/data/unencrypted/ref"); - if (!ref_file) { - KLOG_ERROR(TAG, "Cannot open key reference file\n"); - return -1; - } - - std::string ref; - std::getline(ref_file, ref); - std::string policy = std::string() + keyring + "." + ref; + UnencryptedProperties props("/data"); + std::string ref = props.Get<std::string>(properties::ref); + std::string policy = keyring + "." + ref; KLOG_INFO(TAG, "Setting policy %s\n", policy.c_str()); - if (do_policy_set(dir, policy.c_str())) { + int result = do_policy_set(dir, policy.c_str()); + if (result) { KLOG_ERROR(TAG, "Setting policy on %s failed!", dir); return -1; } diff --git a/ext4_utils/unencrypted_properties.cpp b/ext4_utils/unencrypted_properties.cpp new file mode 100644 index 00000000..bef7c57b --- /dev/null +++ b/ext4_utils/unencrypted_properties.cpp @@ -0,0 +1,86 @@ +#include "unencrypted_properties.h" + +#include <sys/stat.h> + +namespace properties { + const char* key = "key"; + const char* ref = "ref"; + const char* type = "type"; + const char* password = "password"; +} + +namespace +{ + const char* unencrypted_folder = "unencrypted"; +} + +UnencryptedProperties::UnencryptedProperties(const char* device) + : folder_(std::string() + device + "/" + unencrypted_folder) +{ +} + +UnencryptedProperties::UnencryptedProperties() +{ +} + +template<> std::string UnencryptedProperties::Get(const char* name, + std::string default_value) +{ + if (!OK()) return default_value; + std::ifstream i(folder_ + "/" + name, std::ios::binary); + if (!i) { + return default_value; + } + + i.seekg(0, std::ios::end); + int length = i.tellg(); + i.seekg(0, std::ios::beg); + if (length == -1) { + return default_value; + } + + std::string s(length, 0); + i.read(&s[0], length); + if (!i) { + return default_value; + } + + return s; +} + +template<> bool UnencryptedProperties::Set(const char* name, std::string const& value) +{ + if (!OK()) return false; + std::ofstream o(folder_ + "/" + name, std::ios::binary); + o << value; + return !o.fail(); +} + +UnencryptedProperties UnencryptedProperties::GetChild(const char* name) +{ + UnencryptedProperties e4p; + if (!OK()) return e4p; + + std::string directory(folder_ + "/" + name); + if (mkdir(directory.c_str(), 700) == -1 && errno != EEXIST) { + return e4p; + } + + e4p.folder_ = directory; + return e4p; +} + +bool UnencryptedProperties::Remove(const char* name) +{ + if (remove((folder_ + "/" + name).c_str()) + && errno != ENOENT) { + return false; + } + + return true; +} + +bool UnencryptedProperties::OK() const +{ + return !folder_.empty(); +} diff --git a/ext4_utils/unencrypted_properties.h b/ext4_utils/unencrypted_properties.h new file mode 100644 index 00000000..80f41df4 --- /dev/null +++ b/ext4_utils/unencrypted_properties.h @@ -0,0 +1,70 @@ +#include <string> +#include <fstream> + +// key names for properties we use +namespace properties { + extern const char* key; + extern const char* ref; + extern const char* type; + extern const char* password; +} + +/** + * Class to store data on the unencrypted folder of a device. + * Note that the folder must exist before this class is constructed. + * All names must be valid single level (no '/') file or directory names + * Data is organized hierarchically so we can get a child folder + */ +class UnencryptedProperties +{ +public: + // Opens properties folder on named device. + // If folder does not exist, construction will succeed, but all + // getters will return default properties and setters will fail. + UnencryptedProperties(const char* device); + + // Get named object. Return default if object does not exist or error. + template<typename t> t Get(const char* name, t default_value = t()); + + // Set named object. Return true if success, false otherwise + template<typename t> bool Set(const char* name, t const& value); + + // Get child properties + UnencryptedProperties GetChild(const char* name); + + // Remove named object + bool Remove(const char* name); + + // Get path of folder + std::string const& GetPath() const {return folder_;} +private: + UnencryptedProperties(); + bool OK() const; + std::string folder_; +}; + + +template<typename t> t UnencryptedProperties::Get(const char* name, + t default_value) +{ + if (!OK()) return default_value; + t value = default_value; + std::ifstream(folder_ + "/" + name) >> value; + return value; +} + +template<typename t> bool UnencryptedProperties::Set(const char* name, + t const& value) +{ + if (!OK()) return false; + std::ofstream o(folder_ + "/" + name); + o << value; + return !o.fail(); +} + +// Specialized getters/setters for strings +template<> std::string UnencryptedProperties::Get(const char* name, + std::string default_value); + +template<> bool UnencryptedProperties::Set(const char* name, + std::string const& value); |