From 9efcb91aed4e7365aa945fc9c6ffe2d111ca1496 Mon Sep 17 00:00:00 2001 From: Bartlomiej Kunikowski Date: Mon, 5 Dec 2016 08:49:59 +0100 Subject: Fix for wae_initializer There is a bug in if statement, it don't allow to properly work of this initializer if there are no --reload flag. Other way to do it is simply use wae_initializer always as it is with --reload flag. Change-Id: I9622373c914f8c0f1f22d3f810417251cced81ba Signed-off-by: Bartlomiej Kunikowski (cherry picked from commit 1fcac8945420537f89d24a0e8e52ec99cc432674) --- srcs/key_handler.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srcs/key_handler.c b/srcs/key_handler.c index 4607fdd..905538e 100644 --- a/srcs/key_handler.c +++ b/srcs/key_handler.c @@ -539,8 +539,8 @@ int load_preloaded_app_deks(bool reload) // check if all deks were already loaded into key-manager. ret = is_app_deks_loaded_in_key_manager(); - if (ret == WAE_ERROR_NONE) - return ret; + if (ret == true) + return WAE_ERROR_NONE; } raw_buffer_s *prikey = NULL; -- cgit v1.2.3 From 66f4515064566676869fd3c3a8970fcf24b00b5f Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Wed, 7 Dec 2016 17:22:40 +0900 Subject: Remove reload option to wae initializer service Reload option is not needed anymore. To be secure, remove all KEKs from dek store (also adek) after loading preloaded adeks once. Loaded adeks are stored in key-manager so they're useless. Related test cases are added. (load preloaded app deks) (TODO) To use key-manager initial value feature is highly considered to store KEK private key more securely. Change-Id: I2f6c645398277968cd7d480236d1802a07fa33df Signed-off-by: Kyungwook Tak --- packaging/libwebappenc.spec | 1 + srcs/key_handler.c | 164 +++++++++++++++++++++++++++++++---------- srcs/key_handler.h | 6 +- srcs/key_manager.c | 61 --------------- srcs/wae_initializer.c | 9 +-- tests/CMakeLists.txt | 2 + tests/internals.cpp | 103 ++++++++++++++++++++++---- tests/resources/CMakeLists.txt | 19 +++++ tests/resources/prikey.pem | 30 ++++++++ tests/resources/pubkey.pem | 9 +++ tests/test-helper.cpp | 72 ++++++++++++++++-- tests/test-helper.h | 3 + 12 files changed, 352 insertions(+), 127 deletions(-) create mode 100644 tests/resources/CMakeLists.txt create mode 100644 tests/resources/prikey.pem create mode 100644 tests/resources/pubkey.pem diff --git a/packaging/libwebappenc.spec b/packaging/libwebappenc.spec index 760f3e2..4184b84 100644 --- a/packaging/libwebappenc.spec +++ b/packaging/libwebappenc.spec @@ -117,3 +117,4 @@ fi %license LICENSE.BSL-1.0 %{bin_dir}/wae_tests %{_libdir}/libwae_tests_common.so* +%attr(660, %user_name, %group_name) %{rw_share_dir}/wae/test/app_dek/* diff --git a/srcs/key_handler.c b/srcs/key_handler.c index 905538e..d77016f 100644 --- a/srcs/key_handler.c +++ b/srcs/key_handler.c @@ -103,17 +103,17 @@ int _get_random(raw_buffer_s *rb) return WAE_ERROR_NONE; } -static const char *_get_dek_kek_pub_key_path() +const char *_get_dek_kek_pub_key_path() { return tzplatform_mkpath4(TZ_SYS_SHARE, "wae", "app_dek", "WAE_APPDEK_KEK_PublicKey.pem"); } -static const char *_get_dek_kek_pri_key_path() +const char *_get_dek_kek_pri_key_path() { return tzplatform_mkpath4(TZ_SYS_SHARE, "wae", "app_dek", "WAE_APPDEK_KEK_PrivateKey.pem"); } -static const char *_get_dek_store_path() +const char *_get_dek_store_path() { return tzplatform_mkpath3(TZ_SYS_SHARE, "wae", "app_dek"); } @@ -188,6 +188,52 @@ error: return ret; } +static void _remove_file(const char *path) +{ + unlink(path); +} + +void _remove_directory(const char *path) +{ + char file_path_buff[MAX_PATH_LEN] = {0, }; + DIR *dir = opendir(path); + if (dir == NULL) { + if (errno == ENOENT) + WAE_SLOGI("directory is not exist already(%s)", path); + else + WAE_SLOGE("Failed to open dir(%s)", path); + + return; + } + + struct dirent entry; + struct dirent *result = NULL; + while (true) { + if (readdir_r(dir, &entry, &result) != 0) { + break; + } else if (result == NULL) { + break; + } else if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) { + continue; + } + + if (snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", path, entry.d_name) + < 0) + continue; + + if (entry.d_type == DT_DIR) { + _remove_directory(file_path_buff); + } else { + WAE_SLOGD("remove file(%s)", file_path_buff); + _remove_file(file_path_buff); + } + } + + WAE_SLOGD("remove directory(%s)", path); + closedir(dir); + rmdir(path); +} + int _get_preloaded_app_dek_file_path(const char *pkg_id, size_t size, char *path) { int ret = snprintf(path, size, "%s/%s_%s.adek", @@ -528,43 +574,53 @@ int _get_app_dek_kek(raw_buffer_s **pdek_kek) #endif } -int load_preloaded_app_deks(bool reload) +int load_preloaded_app_deks() { - int ret = WAE_ERROR_NONE; + WAE_SLOGI("load_preloaded_app_deks start"); - char pkg_id[MAX_PKGID_LEN] = {0, }; - char file_path_buff[MAX_PATH_LEN]; + int global_ret = WAE_ERROR_NONE; - if (!reload) { - // check if all deks were already loaded into key-manager. - ret = is_app_deks_loaded_in_key_manager(); + char pkg_id[MAX_PKGID_LEN] = {0, }; + char file_path_buff[MAX_PATH_LEN] = {0, }; - if (ret == true) - return WAE_ERROR_NONE; - } + const char *dek_store_path = _get_dek_store_path(); + const char *dek_kek_pub_key_path = _get_dek_kek_pub_key_path(); + const char *dek_kek_pri_key_path = _get_dek_kek_pri_key_path(); raw_buffer_s *prikey = NULL; - ret = _get_app_dek_kek(&prikey); + DIR *dir = NULL; - if (ret != WAE_ERROR_NONE) { - WAE_SLOGE("Fail to get APP_DEK_KEK Private Key"); - return ret; + // check if all deks were already loaded into key-manager. + // TODO: instead of checking key-manager, check based on file existance + dir = opendir(dek_store_path); + if (dir == NULL) { + if (errno == ENOENT) { + WAE_SLOGI( + "dek store doesn't exist. " + "It might be loading preloaded app deks already done"); + + return WAE_ERROR_NONE; + } else { + WAE_SLOGE("Fail to open dir. dir=%s", dek_store_path); + global_ret = WAE_ERROR_FILE; + goto out; + } } - DIR *dir = opendir(_get_dek_store_path()); + global_ret = _get_app_dek_kek(&prikey); - if (dir == NULL) { - WAE_SLOGE("Fail to open dir. dir=%s", _get_dek_store_path()); - buffer_destroy(prikey); - return WAE_ERROR_FILE; + if (global_ret != WAE_ERROR_NONE) { + WAE_SLOGE("Fail to get APP_DEK_KEK Private Key"); + goto out; } struct dirent entry; struct dirent *result = NULL; while (true) { + int ret = WAE_ERROR_NONE; if (readdir_r(dir, &entry, &result) != 0) { - ret = WAE_ERROR_FILE; + global_ret = WAE_ERROR_FILE; break; } @@ -573,43 +629,77 @@ int load_preloaded_app_deks(bool reload) if (result == NULL) break; - // regular file && start with KEY_MANAGER_INITIAL_VALUE_FILE_PFX - if (entry.d_type != DT_REG || strstr(entry.d_name, APP_DEK_FILE_PFX) == NULL) + if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) continue; - ret = snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", - _get_dek_store_path(), entry.d_name); - - if (ret < 0) { + if (snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", + dek_store_path, entry.d_name) < 0) { WAE_SLOGE("Failed to make file path by snprintf."); - ret = WAE_ERROR_INVALID_PARAMETER; /* buffer size too small */ + global_ret = WAE_ERROR_INVALID_PARAMETER; /* buffer size too small */ break; } + // skip for KEKs. They'll be deleted after all akek loaded to key-manager. + if (strcmp(file_path_buff, dek_kek_pub_key_path) == 0 || + strcmp(file_path_buff, dek_kek_pri_key_path) == 0) { + WAE_SLOGD("Skip KEK file...(%s)", file_path_buff); + continue; + } + + // regular file && start with KEY_MANAGER_INITIAL_VALUE_FILE_PFX + // clear all invalid cases silently + if (entry.d_type != DT_REG || strstr(entry.d_name, APP_DEK_FILE_PFX) == NULL) { + if (entry.d_type == DT_DIR) { + WAE_SLOGW( + "Invalid file in dek store(%s). Directory shouldn't be here.", + dek_store_path); + global_ret = WAE_ERROR_FILE; + } else { + WAE_SLOGW( + "Invalid file in dek store(%s). " + "Not regular file or prefix(%s) is invalid.", + dek_store_path, APP_DEK_FILE_PFX); + global_ret = WAE_ERROR_FILE; + } + + continue; + } + ret = _extract_pkg_id_from_file_name(entry.d_name, pkg_id); if (ret != WAE_ERROR_NONE) { WAE_SLOGW("Failed to extract pkgid from file. It will be ignored. file=%s", file_path_buff); + global_ret = ret; continue; } ret = _load_preloaded_app_dek(prikey, file_path_buff, pkg_id); if (ret != WAE_ERROR_NONE && ret != WAE_ERROR_KEY_EXISTS) { WAE_SLOGW("Failed to load app dek(%s) ret(%d)", file_path_buff, ret); + global_ret = ret; } else { WAE_SLOGI("Successfully load app dek(%s)", file_path_buff); - ret = WAE_ERROR_NONE; } } - buffer_destroy(prikey); - closedir(dir); +out: + if (prikey != NULL) + buffer_destroy(prikey); - if (ret != WAE_ERROR_NONE) - return ret; - else - return set_app_deks_loaded_to_key_manager(); + if (dir != NULL) + closedir(dir); + + // remove dek store after loade done even though it's partially failed + // because malware can still put the file in dek store if it still system service's + // ownership and they can break this logic by inserting any file to dek store path. + // If KEK private key is inserted to key-manager with initial-value feature, malware + // cannot insert/encrypt/decrypt app dek so it's fine on preloaded app security but + // if we handle errors related loading file, malware can at least occur webappenc + // initializer service failure. + _remove_directory(dek_store_path); + + return global_ret; } int remove_app_ce(uid_t uid, const char *pkg_id, wae_app_type_e app_type) diff --git a/srcs/key_handler.h b/srcs/key_handler.h index 791e149..f5ce3e4 100644 --- a/srcs/key_handler.h +++ b/srcs/key_handler.h @@ -41,6 +41,10 @@ int _get_random(raw_buffer_s *rb); int _get_preloaded_app_dek_file_path(const char *pkg_id, size_t size, char *path); int _read_encrypted_app_dek_from_file(const char *pkg_id, raw_buffer_s **pencrypted); int _write_encrypted_app_dek_to_file(const char *pkg_id, const raw_buffer_s *encrypted); +void _remove_directory(const char *path); +const char *_get_dek_kek_pub_key_path(); +const char *_get_dek_kek_pri_key_path(); +const char *_get_dek_store_path(); /* functions for interface */ int get_app_ce(uid_t uid, const char *pkg_id, wae_app_type_e app_type, @@ -51,7 +55,7 @@ int remove_app_ce(uid_t uid, const char *pkg_id, wae_app_type_e app_type); int get_preloaded_app_ce(const char *pkg_id, const crypto_element_s **pce); int create_preloaded_app_ce(const char *pkg_id, const crypto_element_s **pce); -int load_preloaded_app_deks(bool reload); +int load_preloaded_app_deks(); #ifdef __cplusplus } diff --git a/srcs/key_manager.c b/srcs/key_manager.c index aeee748..ac42db2 100644 --- a/srcs/key_manager.c +++ b/srcs/key_manager.c @@ -32,7 +32,6 @@ #define MAX_ALIAS_LEN 256 #define APP_DEK_ALIAS_PFX "APP_DEK_" -#define APP_DEK_LOADING_DONE_ALIAS "APP_DEKS_LOADING_FINISHED" #define APP_DEK_KEK_ALIAS "WAE_APP_DEK_KEK" static int _to_wae_error(int key_manager_error) @@ -203,66 +202,6 @@ static void _get_alias(const char *name, UNUSED wae_app_type_e type, UNUSED bool name); } -static void _get_dek_loading_done_alias(char *alias, size_t buff_len) -{ - snprintf(alias, buff_len, "%s%s%s", - ckmc_owner_id_system, - ckmc_owner_id_separator, - APP_DEK_LOADING_DONE_ALIAS); -} - -bool is_app_deks_loaded_in_key_manager() -{ - char alias[MAX_ALIAS_LEN] = {0, }; - - _get_dek_loading_done_alias(alias, sizeof(alias)); - - ckmc_raw_buffer_s *buf = NULL; - int ret = _to_wae_error(ckmc_get_data(alias, NULL, &buf)); - - ckmc_buffer_free(buf); - - switch (ret) { - case WAE_ERROR_NONE: - return true; - case WAE_ERROR_NO_KEY: - WAE_SLOGI("app dek loading isn't done yet"); - return false; - default: - WAE_SLOGE("Failed to get dek loading flag data from key-manager. ret(%d)", ret); - return false; - } -} - -int set_app_deks_loaded_to_key_manager() -{ - unsigned char dummy_data[1] = {0}; - ckmc_raw_buffer_s buf; - buf.data = dummy_data; - buf.size = sizeof(dummy_data); - - ckmc_policy_s policy; - policy.password = NULL; - policy.extractable = true; - - char alias[MAX_ALIAS_LEN] = {0, }; - _get_dek_loading_done_alias(alias, sizeof(alias)); - - int ret = _to_wae_error(ckmc_save_data(alias, buf, policy)); - if (ret == WAE_ERROR_KEY_EXISTS) - ret = WAE_ERROR_NONE; - - return ret; -} - -int clear_app_deks_loaded_from_key_manager() -{ - char alias[MAX_ALIAS_LEN] = {0, }; - _get_dek_loading_done_alias(alias, sizeof(alias)); - - return _to_wae_error(ckmc_remove_alias(alias)); -} - int save_to_key_manager(const char *name, const char *pkg_id, wae_app_type_e type, const crypto_element_s *ce) { diff --git a/srcs/wae_initializer.c b/srcs/wae_initializer.c index 956b04d..f01c8e8 100644 --- a/srcs/wae_initializer.c +++ b/srcs/wae_initializer.c @@ -23,14 +23,9 @@ #include "web_app_enc.h" #include "wae_log.h" -int main(int argc, char *argv[]) +int main() { - bool reload = false; - - if (argc == 2 && strcmp(argv[1], "--reload") == 0) - reload = true; - - int ret = load_preloaded_app_deks(reload); + int ret = load_preloaded_app_deks(); if (ret == WAE_ERROR_NONE) { WAE_SLOGI("WAE INITIALIZER was finished successfully."); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7c9e2c9..07d2082 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -97,3 +97,5 @@ INSTALL(TARGETS ${TARGET_WAE_TEST} WORLD_READ WORLD_EXECUTE ) + +ADD_SUBDIRECTORY(resources) diff --git a/tests/internals.cpp b/tests/internals.cpp index 1fa9aff..7a13df7 100644 --- a/tests/internals.cpp +++ b/tests/internals.cpp @@ -24,7 +24,12 @@ #include #include +#include +#include +#include #include +#include +#include #include @@ -32,6 +37,7 @@ #include "crypto_service.h" #include "test-common.h" +#include "test-helper.h" namespace { @@ -76,7 +82,7 @@ crypto_element_s *_create_ce(void) return ce; } -} +} // namespace anonymous BOOST_AUTO_TEST_SUITE(SYSTEM) @@ -261,6 +267,15 @@ BOOST_AUTO_TEST_CASE(read_write_encrypted_app_dek) BOOST_REQUIRE(dek != nullptr); BOOST_REQUIRE(_get_random(dek) == WAE_ERROR_NONE); + // precondition + // dek store is removed after preloaded app deks loaded so dek store + // does not exists as default. To test write/read app dek test(they're working on + // dek store), dek store directory should be made + Wae::Test::restore_dek_store(); + // make unique_ptr to remove directory automatically + std::unique_ptr> scoped_store( + reinterpret_cast(1), [](void *) { Wae::Test::remove_dek_store(); }); + int ret = _write_encrypted_app_dek_to_file(pkg_id, dek); BOOST_REQUIRE_MESSAGE(ret == WAE_ERROR_NONE, "Failed to write_encrypted_app_dek_to_file. ec: " << ret); @@ -276,7 +291,7 @@ BOOST_AUTO_TEST_CASE(read_write_encrypted_app_dek) "readed(" << Wae::Test::bytes_to_hex(readed) << ")"); } -BOOST_AUTO_TEST_CASE(get_create_preloaded_app_dek_1) +BOOST_AUTO_TEST_CASE(cache_create_preloaded_app_dek) { const char *pkg_id = "TEST_PKG_ID_FOR_CREATE"; @@ -287,7 +302,22 @@ BOOST_AUTO_TEST_CASE(get_create_preloaded_app_dek_1) "preloaded app ce to create is already exist. ec: " << ret); const crypto_element_s *ce = nullptr; - ret = create_preloaded_app_ce(pkg_id, &ce); + + { + // precondition: + // for create_preloaded_app_ce, public key(kek) is needed + Wae::Test::restore_dummy_preloaded_app_dek_keks(); + // postcondition: + // get_preloaded_app_ce retrieves app ce from cache which is created on + // create_preloaded_app_ce so private key in dek store shouldn't be needed + // make unique_ptr to remove directory automatically + std::unique_ptr> scoped_store( + reinterpret_cast(1), [](void *) { Wae::Test::remove_dek_store(); }); + + // created preloaded app ce is just written in file, not into key-manager repo so + // no need to call remove_app_ce. + ret = create_preloaded_app_ce(pkg_id, &ce); + } BOOST_REQUIRE_MESSAGE(ret == WAE_ERROR_NONE, "Failed to create_preloaded_app_ce. ec: " << ret); @@ -299,22 +329,21 @@ BOOST_AUTO_TEST_CASE(get_create_preloaded_app_dek_1) BOOST_REQUIRE_MESSAGE(readed == ce, "cached ce address and actual is different!"); } -BOOST_AUTO_TEST_CASE(get_create_preloaded_app_dek_2) +BOOST_AUTO_TEST_CASE(load_preloaded_app_dek) { + // steps + // 1) restore KEKs : restore_dummy_preloaded_app_dek_keks + // 2) create app deks based on KEK (public key) : create_preloaded_app_ce + // -> originally this step runs in image server so result(adek) is written to file + // 3) load preloaded app deks (.adek) in file : load_preloaded_app_deks + // -> After load, pri/pub key pair and adek in file is no longer needed so they're + // automatically cleared by load_preloaded_app_deks() + // 4) clear app deks from key-manager for remove it (associated to TEST_PKG_ID_*) + Wae::Test::restore_dummy_preloaded_app_dek_keks(); + const char *pkg_id1 = "TEST_PKGID_1"; const char *pkg_id2 = "TEST_PKGID_2"; - char path1[MAX_PATH_LEN] = {0, }; - char path2[MAX_PATH_LEN] = {0, }; - _get_preloaded_app_dek_file_path(pkg_id1, sizeof(path1), path1); - _get_preloaded_app_dek_file_path(pkg_id2, sizeof(path2), path2); - - // remove old test data - remove_app_ce(0, pkg_id1, WAE_PRELOADED_APP); - remove_app_ce(0, pkg_id2, WAE_PRELOADED_APP); - unlink(path1); - unlink(path2); - // create 2 ces for preloaded app const crypto_element_s *ce1 = nullptr; int ret = create_preloaded_app_ce(pkg_id1, &ce1); @@ -326,7 +355,7 @@ BOOST_AUTO_TEST_CASE(get_create_preloaded_app_dek_2) BOOST_REQUIRE_MESSAGE(ret == WAE_ERROR_NONE, "Failed to create_preloaded_app_ce. ec: " << ret); - ret = load_preloaded_app_deks(true); + ret = load_preloaded_app_deks(); BOOST_REQUIRE_MESSAGE(ret == WAE_ERROR_NONE, "Failed to load_preloaded_app_deks. ec: " << ret); @@ -348,6 +377,48 @@ BOOST_AUTO_TEST_CASE(get_create_preloaded_app_dek_2) BOOST_REQUIRE_MESSAGE(ret == WAE_ERROR_NONE, "Failed remove app ce. ec: " << ret); } +BOOST_AUTO_TEST_CASE(load_preloaded_app_dek_tolerances) +{ + std::function does_dek_store_exist = []() { + if (DIR *dir = opendir(_get_dek_store_path())) { + closedir(dir); + return true; + } else if (errno != ENOENT) { + return true; + } else { + return false; + } + }; + + // without dek store directory + BOOST_REQUIRE(load_preloaded_app_deks() == WAE_ERROR_NONE); + BOOST_REQUIRE(does_dek_store_exist() == false); + + // without kek(private key) + Wae::Test::restore_dek_store(); + BOOST_REQUIRE(load_preloaded_app_deks() == WAE_ERROR_FILE); + BOOST_REQUIRE(does_dek_store_exist() == false); + + // with invalid file in dek store + Wae::Test::restore_dummy_preloaded_app_dek_keks(); + std::ofstream dst; + dst.exceptions(std::ofstream::failbit | std::ofstream::badbit); + dst.open(std::string(_get_dek_store_path()) + "/invalids", std::ofstream::binary); + dst << "touch invalid file to dek store"; + // std::ofstream destructor will call close automatically so no need to handle + // close in the exception cases + dst.close(); + BOOST_REQUIRE(load_preloaded_app_deks() == WAE_ERROR_FILE); + BOOST_REQUIRE(does_dek_store_exist() == false); + + // with invalid directory in dek store + Wae::Test::restore_dummy_preloaded_app_dek_keks(); + std::string invalid_dir = std::string(_get_dek_store_path()) + "/invalid_dir"; + mkdir(invalid_dir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR); + BOOST_REQUIRE(load_preloaded_app_deks() == WAE_ERROR_FILE); + BOOST_REQUIRE(does_dek_store_exist() == false); +} + BOOST_AUTO_TEST_SUITE_END() // INTERNALS BOOST_AUTO_TEST_SUITE_END() // SYSTEM diff --git a/tests/resources/CMakeLists.txt b/tests/resources/CMakeLists.txt new file mode 100644 index 0000000..fa0856f --- /dev/null +++ b/tests/resources/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +INSTALL( + FILES pubkey.pem prikey.pem + DESTINATION ${RW_SHARE_DIR}/wae/test/app_dek + PERMISSIONS OWNER_READ +) diff --git a/tests/resources/prikey.pem b/tests/resources/prikey.pem new file mode 100644 index 0000000..e27950c --- /dev/null +++ b/tests/resources/prikey.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,F4C783D75B0679F29398E9A3CAB4733D + +kxgW1wGX3TZZ/wtv3g4AOLlZCHoQ6uXVQ0h2ofWjnJs8tas/alR6o8UBRIqCw44t +znUvQ8HlThvzhGgxje/yDDSxCy9mqhgsi2XeTtAeUbMhFL6UArb3cs6M4a37lYoT +llZdFyYkRWJ3vRS33TDrhXDV6GjZWQ05SJ0OYdPJsmA1ENwdH+5NE/xLnqLdTtWr +O3Mn2vi6P9CVqZroCvYBzUaypGcmFhjTIbWmB6inXjoXyddzerh7PTDBDWWacBab +C7gcZC5SrK5YOt6f54ANsVQO8jnkLDx95gUSHYthX1hrQ3Da5Gb6nfYP9RNrHCum +O8RKxSOvv8zwbMlzqtld8xCOb7Nh04f8bofrzZVLZ0T92FcyFQmt1F4U6DNQqHsn +AAqxRxUWsC5k2dX9uZ6RCpEzNYWyPvNe24I/Kt01Geoh1NtCns8CVZcrxyMMtZRK +ZJnYhvNDXDQCDtMJjRBiEXXE++AdA2O6uFoGX3alKwtxAIjGI++pSRlz1GTps26x +5mmLil5wb3KGBfMN4L0R0heDOeiPQrNv7CwX8OlHtA1OKFBtViWdd/uZ2hAko1Tz +YkoYpHPQOV5LZ7dem/XNnwwel9g6AkHhLNJv5ih4Y0CQfPBSs+iiLbMHh/NaGDD9 ++kbcf5Lk4FQGVbJDW9nDAXT6jjMyliTI+hIh5fM2k22qbq6OqBkW6EbOQDMP/R2P +LhFqTgHceNt0mqpcDJdJQ0YKbxVpdkv5f1C4rW+pgUEeHDCQ7vPe4p44xQJ/Z/7Y +AtPwPKzPPJze2cfoUkZd9jXN9g2v2555xnQZU78IEm1nPVBA+hLIaqN1hu1Lkzxy +CwFNo7bMVh3FSBmZVtJlcLsyLxZ9UdoaSr+anfA0lWJPiBzE0whQljZp56l1rL1V +1K8m/dc9rLJ3uDQmYoSRmBZG5zZlVWCip+R9VAHMxRi1x29dFk1jbtQscr63dMI8 +0eOUf28Mw719WWUZVzD08b431DPqWiqrpexUKEXPW8EsrINPfIg180QYt1VUoshs +Tqi/LKM0OV6nlMGh9ieCK8WzVDW8F16krSLo6eJpIPYPZgkHE7fC7Jws1kpUrSnF +GgT6rBA97tJ0EalinuFXbip1X087Quz5USURq18f7/B6nFu0Kd4GhlICsR24j3eB +75SsTNmfUcko8s5QT4rwONEwtRffkGbbNEisCPcleJV68zHvN58mfD7Dl8W3zIO4 +Qk6B1Xy0C4EEniKFfjxIaMEaxrqntBIc+nZE6/+UoGp/Hj9r5ZdzQX2j4837IIdR +CxT4tjXiWBA6u3WaLAZUSM0W0SEORUF9NwzlId1b8A3WxA8XewhAKPaJEr677vzZ +083+neUOuXqqs597romLH1omuffxmHxBzmP+koUtemP78XxCBVWUAB1T+fBRJMz6 +9ZEgDWrMntJ1IaFoGdOWZELgwcXJ0KwWFuk+sieZ5WCCzNmFli9WPN/xSqwmdYw6 +RK9er5Vc8D9mAlmGlz2mpAmzNJHH30zYKT/d0XzBS8z6WBRthaTS3NLsiSeWdELH +b5+WEMOiKvZ19AXU2unHw/XpeVnAISOHhumAqFCwXkjVoMt8LMDawt6ra8N8G+gD +-----END RSA PRIVATE KEY----- diff --git a/tests/resources/pubkey.pem b/tests/resources/pubkey.pem new file mode 100644 index 0000000..f0dfcea --- /dev/null +++ b/tests/resources/pubkey.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0kWtjpRO7Zh2KX2naVE/ +BDJdrfwK9xexfNA0MkY2VJ4J2AKMYTj1D1jntceryupCEHOvP3rum+WsFvPXduz9 ++VKnSsSqj4jcTUubtpDUGA5G79IqLEPFuSBaqI8Uwkzd08pE+s30oaJDnNazMhSq +8JkqBPoCCwtUs73ruE9VbtsBO/kTlASIAfe8nXqcJLcDQgWYhizjJw0Pi6d74oCw +S2OTvQDNvsXfFnA0ZJEEYw/rZLirj7OHoOjz+Sh5N+1uA3Up6SPPEbHuP6L12Yxq +Hdy7gnJXodLhvE/cR4SN9VW7+qmCMBjmLkBejGrEX3STS9sLI7MZHu9Y26dwuYb4 ++wIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/test-helper.cpp b/tests/test-helper.cpp index d0ca263..b7fdf6a 100644 --- a/tests/test-helper.cpp +++ b/tests/test-helper.cpp @@ -22,6 +22,9 @@ #include #include +#include +#include +#include #include "web_app_enc.h" #include "key_handler.h" @@ -37,6 +40,25 @@ namespace { const uid_t UID_OWNER = 5001; +void copy_file(const char *src_path, const char *dst_path) +{ + std::ifstream src; + std::ofstream dst; + + src.exceptions(std::ifstream::failbit | std::ifstream::badbit); + dst.exceptions(std::ofstream::failbit | std::ofstream::badbit); + + src.open(src_path, std::ifstream::binary); + dst.open(dst_path, std::ofstream::binary); + + dst << src.rdbuf(); + + // std::ofstream destructor will call close automatically so no need to handle + // close in the exception cases + src.close(); + dst.close(); +} + } // namespace anonymous void add_get_remove_ce(wae_app_type_e app_type) @@ -123,15 +145,29 @@ void encrypt_decrypt_web_app(wae_app_type_e app_type) else wae_remove_global_app_dek(pkg_id, app_type == WAE_PRELOADED_APP); - if (app_type == WAE_PRELOADED_APP) - clear_app_deks_loaded_from_key_manager(); - std::vector plaintext = { 'a', 'b', 'c', 'a', 'b', 'c', 'x', 'y', 'o', 'q', '2', 'e', 'v', '0', '1', 'x' }; - // test for downloaded web application + // precondition for preloaded app: + // for preloaded app encryption, preloaded app dek kek(pub) is needed. + // dek store is removed after preloaded app deks loaded so dek store + // does not exists as default. To test encrypt/decrypt(write/read ce) app test, + // dek store directory should be made. + std::unique_ptr> scoped_store( + reinterpret_cast(1), [](void *ptr) { + if (ptr == reinterpret_cast(1)) + return; + else + remove_dek_store(); // remove dek store automatically in case of error + }); + + if (app_type == WAE_PRELOADED_APP) { + restore_dummy_preloaded_app_dek_keks(); + scoped_store.reset(reinterpret_cast(2)); + } + unsigned char *_encrypted = nullptr; size_t _enc_len = 0; int tmp = 0; @@ -171,7 +207,7 @@ void encrypt_decrypt_web_app(wae_app_type_e app_type) } if (app_type == WAE_PRELOADED_APP) - load_preloaded_app_deks(true); + load_preloaded_app_deks(); unsigned char *_decrypted = nullptr; size_t _dec_len = 0; @@ -201,5 +237,31 @@ void encrypt_decrypt_web_app(wae_app_type_e app_type) "Failed to wae_remove_app_dek. ec: " << tmp); } +void restore_dek_store() +{ + mkdir( + _get_dek_store_path(), + S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP); +} + +void remove_dek_store() +{ + _remove_directory(_get_dek_store_path()); +} + +void restore_dummy_preloaded_app_dek_keks() +{ + // Generate pri/pub key pair. Private key is protected + // with assigned password: APP_DEK_KEK_PRIKEY_PASSWORD) which is same to password + // of real private key because it's built in source of srcs/key_handler.c + // It should be removed after private key goes into key-manager initial-value. + restore_dek_store(); + + copy_file("/opt/share/wae/test/app_dek/prikey.pem", _get_dek_kek_pri_key_path()); + copy_file("/opt/share/wae/test/app_dek/pubkey.pem", _get_dek_kek_pub_key_path()); + + BOOST_MESSAGE("copying dummy pri/pub key pair to dek store done"); +} + } // namespace Test } // namespace Wae diff --git a/tests/test-helper.h b/tests/test-helper.h index b4d1f42..cd2fae2 100644 --- a/tests/test-helper.h +++ b/tests/test-helper.h @@ -29,5 +29,8 @@ void add_get_remove_ce(wae_app_type_e app_type); void create_app_ce(wae_app_type_e app_type); void encrypt_decrypt_web_app(wae_app_type_e app_type); +void remove_dek_store(); +void restore_dek_store(); +void restore_dummy_preloaded_app_dek_keks(); } // namespace Test } // namespace Wae -- cgit v1.2.3 From 9fddab9e58ccd9acdecdbfcf12770c50feac4d2b Mon Sep 17 00:00:00 2001 From: Kyungwook Tak Date: Thu, 8 Dec 2016 10:12:11 +0900 Subject: Refine directory traversing: registering callback There's two part of directory traversing(removing all files in directory and loading preloaded app deks). So duplicated code can be compressed to traverse_directory with entry callback registered. Change-Id: I654bed7f3b4efff75b2853fceb3f9d97b51a85b5 Signed-off-by: Kyungwook Tak --- srcs/key_handler.c | 218 +++++++++++++++++++++++++++-------------------------- 1 file changed, 110 insertions(+), 108 deletions(-) diff --git a/srcs/key_handler.c b/srcs/key_handler.c index d77016f..cdc96af 100644 --- a/srcs/key_handler.c +++ b/srcs/key_handler.c @@ -188,61 +188,87 @@ error: return ret; } -static void _remove_file(const char *path) +typedef int(*entry_callback)(const char *path, const struct dirent *entry, void *user_data); +static int traverse_directory(const char *path, entry_callback ecb, void *user_data) { - unlink(path); -} - -void _remove_directory(const char *path) -{ - char file_path_buff[MAX_PATH_LEN] = {0, }; DIR *dir = opendir(path); if (dir == NULL) { - if (errno == ENOENT) - WAE_SLOGI("directory is not exist already(%s)", path); - else + if (errno == ENOENT) { + // it's not error for current cases of using traverse_directory. + // To open dek store directory for load/remove can be occured in some + // exception(or attacked) cases but we can just ignore it if it isn't the + // first time call load_preloaded_app_deks. + WAE_SLOGI("directory isn't exist(%s).", path); + return WAE_ERROR_NONE; + } else { WAE_SLOGE("Failed to open dir(%s)", path); - - return; + return WAE_ERROR_FILE; + } } + int ret = WAE_ERROR_NONE; struct dirent entry; struct dirent *result = NULL; while (true) { if (readdir_r(dir, &entry, &result) != 0) { + WAE_SLOGE("readdir_r error on dir(%s) errno(%d)", path, errno); break; } else if (result == NULL) { - break; + break; // end of directory } else if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) { continue; } - if (snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", path, entry.d_name) - < 0) - continue; + int _ret = ecb(path, result, user_data); + if (_ret != WAE_ERROR_NONE) + ret = _ret; + } - if (entry.d_type == DT_DIR) { - _remove_directory(file_path_buff); - } else { - WAE_SLOGD("remove file(%s)", file_path_buff); - _remove_file(file_path_buff); - } + closedir(dir); + return ret; +} + +static void _remove_file(const char *path) +{ + unlink(path); +} + +static int _entry_callback_remove_all( + const char *path, const struct dirent *entry, void *user_data) +{ + (void) user_data; // TODO: use UNUSED macro + + char file_path_buff[MAX_PATH_LEN] = {0, }; + if (snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", path, entry->d_name) < 0) + return WAE_ERROR_INVALID_PARAMETER; /* buffer size too small */ + + int ret = WAE_ERROR_NONE; + if (entry->d_type == DT_DIR) { + int _ret = traverse_directory(file_path_buff, _entry_callback_remove_all, NULL); + if (_ret != WAE_ERROR_NONE) + ret = _ret; + rmdir(file_path_buff); + } else { + _remove_file(file_path_buff); } + return ret; +} + +void _remove_directory(const char *path) +{ + traverse_directory(path, _entry_callback_remove_all, NULL); WAE_SLOGD("remove directory(%s)", path); - closedir(dir); rmdir(path); } int _get_preloaded_app_dek_file_path(const char *pkg_id, size_t size, char *path) { - int ret = snprintf(path, size, "%s/%s_%s.adek", - _get_dek_store_path(), APP_DEK_FILE_PFX, pkg_id); - - if (ret < 0) + if (snprintf(path, size, "%s/%s_%s.adek", + _get_dek_store_path(), APP_DEK_FILE_PFX, pkg_id) < 0) return WAE_ERROR_INVALID_PARAMETER; /* buffer size too small */ - - return WAE_ERROR_NONE; + else + return WAE_ERROR_NONE; } static int _extract_pkg_id_from_file_name(const char *file_name, char *pkg_id) @@ -574,18 +600,56 @@ int _get_app_dek_kek(raw_buffer_s **pdek_kek) #endif } -int load_preloaded_app_deks() +static int _entry_callback_load_preloaded_adeks( + const char *path, const struct dirent *entry, void *prikey) { - WAE_SLOGI("load_preloaded_app_deks start"); + const char *pub_key_path = _get_dek_kek_pub_key_path(); + const char *pri_key_path = _get_dek_kek_pri_key_path(); + + char file_path_buff[MAX_PATH_LEN] = {0, }; + if (snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", path, entry->d_name) < 0) + return WAE_ERROR_INVALID_PARAMETER; /* buffer size too small */ + + if (strcmp(file_path_buff, pub_key_path) == 0 || + strcmp(file_path_buff, pri_key_path) == 0) + return WAE_ERROR_NONE; /* skip KEK files */ - int global_ret = WAE_ERROR_NONE; + if (entry->d_type != DT_REG || strstr(entry->d_name, APP_DEK_FILE_PFX) == NULL) { + if (entry->d_type == DT_DIR) + WAE_SLOGW( + "Invalid file in dek store(%s). Directory shouldn't be here.", path); + else + WAE_SLOGW( + "Invalid file in dek store(%s). " + "Not regular file or prefix(%s) is invalid.", path, APP_DEK_FILE_PFX); + + return WAE_ERROR_FILE; + } char pkg_id[MAX_PKGID_LEN] = {0, }; - char file_path_buff[MAX_PATH_LEN] = {0, }; + int ret = _extract_pkg_id_from_file_name(entry->d_name, pkg_id); + if (ret != WAE_ERROR_NONE) { + WAE_SLOGW("Failed to extract pkgid from file(%s). It will be ignored.", file_path_buff); + return ret; + } + + ret = _load_preloaded_app_dek((raw_buffer_s *)prikey, file_path_buff, pkg_id); + if (ret == WAE_ERROR_NONE || ret == WAE_ERROR_KEY_EXISTS) { + WAE_SLOGI("Successfully load app dek(%s)", file_path_buff); + return WAE_ERROR_NONE; + } else { + WAE_SLOGW("Failed to load app dek(%s) ret(%d)", file_path_buff, ret); + return ret; + } +} + +int load_preloaded_app_deks() +{ + WAE_SLOGI("load_preloaded_app_deks start"); + + int ret = WAE_ERROR_NONE; const char *dek_store_path = _get_dek_store_path(); - const char *dek_kek_pub_key_path = _get_dek_kek_pub_key_path(); - const char *dek_kek_pri_key_path = _get_dek_kek_pri_key_path(); raw_buffer_s *prikey = NULL; DIR *dir = NULL; @@ -602,86 +666,24 @@ int load_preloaded_app_deks() return WAE_ERROR_NONE; } else { WAE_SLOGE("Fail to open dir. dir=%s", dek_store_path); - global_ret = WAE_ERROR_FILE; + ret = WAE_ERROR_FILE; goto out; } } - global_ret = _get_app_dek_kek(&prikey); - - if (global_ret != WAE_ERROR_NONE) { - WAE_SLOGE("Fail to get APP_DEK_KEK Private Key"); + ret = _get_app_dek_kek(&prikey); + if (ret != WAE_ERROR_NONE) { + WAE_SLOGE("Fail to get APP_DEK_KEK Private Key. ret(%d)", ret); goto out; } - struct dirent entry; - struct dirent *result = NULL; - - while (true) { - int ret = WAE_ERROR_NONE; - if (readdir_r(dir, &entry, &result) != 0) { - global_ret = WAE_ERROR_FILE; - break; - } - - // readdir_r returns NULL in *result if the end - // of the directory stream is reached - if (result == NULL) - break; - - if (strcmp(entry.d_name, ".") == 0 || strcmp(entry.d_name, "..") == 0) - continue; - - if (snprintf(file_path_buff, sizeof(file_path_buff), "%s/%s", - dek_store_path, entry.d_name) < 0) { - WAE_SLOGE("Failed to make file path by snprintf."); - global_ret = WAE_ERROR_INVALID_PARAMETER; /* buffer size too small */ - break; - } - - // skip for KEKs. They'll be deleted after all akek loaded to key-manager. - if (strcmp(file_path_buff, dek_kek_pub_key_path) == 0 || - strcmp(file_path_buff, dek_kek_pri_key_path) == 0) { - WAE_SLOGD("Skip KEK file...(%s)", file_path_buff); - continue; - } - - // regular file && start with KEY_MANAGER_INITIAL_VALUE_FILE_PFX - // clear all invalid cases silently - if (entry.d_type != DT_REG || strstr(entry.d_name, APP_DEK_FILE_PFX) == NULL) { - if (entry.d_type == DT_DIR) { - WAE_SLOGW( - "Invalid file in dek store(%s). Directory shouldn't be here.", - dek_store_path); - global_ret = WAE_ERROR_FILE; - } else { - WAE_SLOGW( - "Invalid file in dek store(%s). " - "Not regular file or prefix(%s) is invalid.", - dek_store_path, APP_DEK_FILE_PFX); - global_ret = WAE_ERROR_FILE; - } - - continue; - } - - ret = _extract_pkg_id_from_file_name(entry.d_name, pkg_id); - - if (ret != WAE_ERROR_NONE) { - WAE_SLOGW("Failed to extract pkgid from file. It will be ignored. file=%s", - file_path_buff); - global_ret = ret; - continue; - } + // close dek store dir fd not to affect the traverse_directory call + closedir(dir); + dir = NULL; - ret = _load_preloaded_app_dek(prikey, file_path_buff, pkg_id); - if (ret != WAE_ERROR_NONE && ret != WAE_ERROR_KEY_EXISTS) { - WAE_SLOGW("Failed to load app dek(%s) ret(%d)", file_path_buff, ret); - global_ret = ret; - } else { - WAE_SLOGI("Successfully load app dek(%s)", file_path_buff); - } - } + ret = traverse_directory(dek_store_path, _entry_callback_load_preloaded_adeks, prikey); + if (ret != WAE_ERROR_NONE) + WAE_SLOGE("Fail when traverse dek store directory. ret(%d)", ret); out: if (prikey != NULL) @@ -699,7 +701,7 @@ out: // initializer service failure. _remove_directory(dek_store_path); - return global_ret; + return ret; } int remove_app_ce(uid_t uid, const char *pkg_id, wae_app_type_e app_type) -- cgit v1.2.3