diff options
author | Kyungwook Tak <k.tak@samsung.com> | 2016-07-11 18:30:51 +0900 |
---|---|---|
committer | kyungwook tak <k.tak@samsung.com> | 2016-07-18 18:01:20 -0700 |
commit | fac6b36b70367c5600cced824289d65529d705f0 (patch) | |
tree | ce32a1c50a0e5d51fc4b1470750d505127853ff1 /src/manager/service/ss-migrate.cpp | |
parent | a3740cdb1f6f2a2cf161453637a178237adc0ed3 (diff) | |
download | key-manager-fac6b36b70367c5600cced824289d65529d705f0.tar.gz key-manager-fac6b36b70367c5600cced824289d65529d705f0.tar.bz2 key-manager-fac6b36b70367c5600cced824289d65529d705f0.zip |
Migrate secure-storage data
Change-Id: Ifa89e9086a40f8dcbd82bdbc26fe14a7dcc1c8c1
Signed-off-by: Kyungwook Tak <k.tak@samsung.com>
Diffstat (limited to 'src/manager/service/ss-migrate.cpp')
-rw-r--r-- | src/manager/service/ss-migrate.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/manager/service/ss-migrate.cpp b/src/manager/service/ss-migrate.cpp new file mode 100644 index 00000000..5bf01792 --- /dev/null +++ b/src/manager/service/ss-migrate.cpp @@ -0,0 +1,269 @@ +/* + * 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 + * + * + * @file ss-migrate.cpp + * @author Kyungwook Tak (k.tak@samsung.com) + * @version 1.0 + * @brief Deprecated secure-storage data migration + */ +#include <ss-migrate.h> + +#include <fstream> +#include <memory> +#include <cstring> +#include <cerrno> +#include <cstddef> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> + +#include <openssl/sha.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> + +#include <dpl/log/log.h> + +namespace CKM { + +namespace { + +const std::string OLD_SS_DIR = RW_DATA_DIR "/secure-storage"; +const std::string OLD_SS_GROUP_PREFIX = "secure-storage::"; +const int SALT_SIZE = 32; +const int KEY_SIZE = 16; + +std::unique_ptr<char[]> get_key(const std::string &id, size_t len) +{ + unsigned char salt[SALT_SIZE]; + + ::memset(salt, 0xFF, SALT_SIZE); + + std::unique_ptr<char[]> duk(new char[len + 1]); + + ::PKCS5_PBKDF2_HMAC_SHA1(id.c_str(), id.length(), salt, SALT_SIZE, 1, + len, reinterpret_cast<unsigned char *>(duk.get())); + + duk[len] = '\0'; + + return duk; +} + +RawBuffer read_data(const std::string &filepath, const std::string &seed) +{ + std::ifstream f(filepath.c_str(), std::ios::binary); + + if (!f.is_open()) { + LogError("Failed to open file: " << filepath); + return RawBuffer(); + } + + f.seekg(0, f.end); + auto ciphertext_len = f.tellg(); + if (ciphertext_len == -1) { + LogError("Failed to get file length: " << filepath); + return RawBuffer(); + } + + f.seekg(0, f.beg); + + RawBuffer ciphertext(ciphertext_len, 0); + + f.read(reinterpret_cast<char *>(ciphertext.data()), ciphertext.size()); + if (!f) { + LogError("Failed to read file: " << filepath); + return RawBuffer(); + } + + auto key = get_key(seed, KEY_SIZE); + std::unique_ptr<char[]> iv(new char[KEY_SIZE]); + size_t ivlen = 0; + if (::EVP_Digest(key.get(), KEY_SIZE, reinterpret_cast<unsigned char *>(iv.get()), + &ivlen, ::EVP_sha1(), nullptr) != 1) { + LogError("Failed to get iv"); + return RawBuffer(); + } + + // start decrypt data with key and iv + auto algo = ::EVP_aes_128_cbc(); + int tmp_len = (ciphertext.size() / algo->block_size + 1) * algo->block_size; + RawBuffer plaintext(tmp_len, 0); + + std::unique_ptr<EVP_CIPHER_CTX, void(*)(EVP_CIPHER_CTX *)> ctxptr( + ::EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free); + + if (ctxptr == nullptr) + throw std::bad_alloc(); + + auto ctx = ctxptr.get(); + + int ec = ::EVP_CIPHER_CTX_set_padding(ctx, 1); + if (ec != 1) { + LogError("Failed to evp ctx set padding. ec: " << ec); + return RawBuffer(); + } + + ec = ::EVP_CipherInit(ctx, algo, reinterpret_cast<unsigned char *>(key.get()), + reinterpret_cast<unsigned char *>(iv.get()), 0 /* decrypt flag */); + if (ec != 1) { + LogError("Failed to evp cipher init. ec: " << ec); + return RawBuffer(); + } + + int plaintext_len = 0; + ec = ::EVP_CipherUpdate(ctx, plaintext.data(), &plaintext_len, + ciphertext.data(), ciphertext.size()); + if (ec != 1) { + LogError("Failed to evp cipher update. ec: " << ec); + return RawBuffer(); + } + + int final_len = 0; + ec = EVP_CipherFinal(ctx, plaintext.data() + plaintext_len, &final_len); + if (ec != 1) { + LogError("Failed to evp cipher final. ec: " << ec); + return RawBuffer(); + } + + plaintext_len += final_len; + + plaintext.resize(plaintext_len); + + return plaintext; +} + +inline void remove_path(const std::string &path) +{ + if (::remove(path.c_str()) == -1) + LogError("Failed to remove path: " << path << " with errno: " << errno); +} + +// depth 0 -> OLD_SS_DIR +// 1 -> group dir in OLD_SS_DIR +void visit_dir(const std::string &dirpath, struct dirent *buf, size_t depth, + const Saver &saver) +{ + if (depth > 1) { + LogError("Invalid depth in secure-storage subdir... dirpath: " << dirpath); + return; + } + + std::unique_ptr<DIR, int(*)(DIR *)> dirptr(::opendir(dirpath.c_str()), ::closedir); + if (dirptr == nullptr) { + LogError("Failed to open dir: " << dirpath << " with errno: " << errno); + return; + } + + while (true) { + struct dirent *result = nullptr; + auto ret = ::readdir_r(dirptr.get(), buf, &result); + if (ret != 0) { + LogError("readdir_r error on secure-storage dir: " << dirpath << + " with errno: " << errno); + break; + } else if (result == nullptr) { + remove_path(dirpath); + break; + } + + const auto &name = result->d_name; + auto name_size = ::strlen(name); + + std::string path = dirpath + "/" + name; + if (result->d_type == DT_DIR) { + if ((name_size == 1 && name[0] == '.') || + (name_size == 2 && name[0] == '.' && name[1] == '.')) { + continue; + } else if (depth == 1) { + LogError("Invalid hierarchy of secure-storage dir... " + "Directory(" << name << ") cannot be in " + "group storage: " << dirpath); + remove_path(path); + } else { + std::string subdir = dirpath + "/" + name; + visit_dir(subdir, buf, depth + 1, saver); + } + } else if (result->d_type == DT_REG) { + if (depth == 0) { + LogError("Invalid hierarchy of secure-storage dir... " + "File(" << name << ") cannot be in secure-storage top dir"); + remove_path(path); + } else { + LogInfo("Meet file(" << path << ") in secure-storage! " + "Let's save it into key-manager."); + + auto storage_name = dirpath.substr(OLD_SS_DIR.length() + 1); + + Crypto::Data data; + data.type = DataType::BINARY_DATA; + data.data = read_data(path, storage_name); + + if (data.data.empty()) { + LogError("Failed to read data from file: " << path); + } else if (storage_name == "secure-storage") { + LogInfo("Meet secure-storage storage which contains SALT! skip it!"); + } else if (storage_name.rfind(OLD_SS_GROUP_PREFIX) == std::string::npos) { + LogInfo("data file(" << path << ") is not in group! " + "smack-label is used as storage name"); + saver(storage_name, name, data); + } else { + LogInfo("data file(" << path << ") is in group! " + "group id is extracted from dir path"); + saver(storage_name.substr(OLD_SS_GROUP_PREFIX.length()), name, data); + } + + remove_path(path); + } + } else { + LogError("Invalid type(" << result->d_type << ") of file(" << path << ") "); + remove_path(path); + } + } +} + +} // namespace anonymous + +bool hasMigratableData(void) +{ + if (::access(OLD_SS_DIR.c_str(), R_OK | X_OK) == -1) { + const int err = errno; + + if (err != ENOENT) + LogError("Failed to access old secure-storage dir. errno: " << err); + + return false; + } else { + return true; + } +} + +void migrateData(const Saver &saver) +{ + if (saver == nullptr) { + LogError("saver cannot be null"); + return; + } + + std::unique_ptr<struct dirent, void(*)(void *)> bufptr( + static_cast<struct dirent *>(::malloc( + offsetof(struct dirent, d_name) + NAME_MAX + 1)), ::free); + + if (bufptr == nullptr) + throw std::bad_alloc(); + + visit_dir(OLD_SS_DIR, bufptr.get(), 0, saver); +} + +} |