#include "../../file-footer.h"
#include "../../logger.h"
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+
#include <cctype>
#include <algorithm>
-#include <libcryptsetup.h>
-
#include <klay/exception.h>
namespace ode {
namespace {
-const char* DEFAULT_LUKS_CIPHER_NAME = "aes";
-const char* DEFAULT_LUKS_CIPHER_MODE = "xts-plain64";
-const char* DEFAULT_LUKS_HASH = "sha1";
-const size_t DEFAULT_LUKS_ALIGNMENT = 0;
+std::string mappingPath(const std::string& name)
+{
+ static std::string mappings_dir = "/dev/mapper/";
+ return mappings_dir + name;
+}
-#ifndef NDEBUG
-// log callback
-void log(int level, const char *msg, void*)
+void forkAndWrite(const char *const* argv, const CryptsetupEngine::data *d)
{
- std::string msgStr("[NULL]");
- if (msg) {
- msgStr = msg;
-
- // trim it
- auto it = find_if_not(msgStr.rbegin(),
- msgStr.rend(),
- [](char c){ return isspace(c); }).base();
- msgStr.erase(it, msgStr.end());
+ // pipe
+ int pipefd[2];
+ if (d != NULL) {
+ if (0 != pipe(pipefd))
+ throw runtime::Exception("pipe() failed:" + std::to_string(errno));
}
- switch (level)
- {
- case CRYPT_LOG_ERROR:
- ERROR(SINK, "[libcryptsetup] " + msgStr);
- break;
- case CRYPT_LOG_VERBOSE:
- INFO(SINK, "[libcryptsetup] " + msgStr);
- break;
- case CRYPT_LOG_DEBUG:
- case CRYPT_LOG_NORMAL:
- DEBUG(SINK, "[libcryptsetup] " + msgStr);
- break;
- default:
- ERROR(SINK, "[libcryptsetup] Unsupported log level. Msg: " + msgStr);
- }
-}
-#endif
+ pid_t pid = ::fork();
+ if (pid == -1)
+ throw runtime::Exception("fork() failed: " + std::to_string(errno));
-class Device
-{
-public:
- enum class InitMethod
- {
- BY_DEV_PATH, // requires device path as first ctor argument
- BY_MAP_NAME, // requires mapping name as first ctor argument
- };
+ if (pid == 0) {
+ if (d != NULL) {
+ ::close(pipefd[1]);
- explicit Device(const std::string &str, InitMethod method = InitMethod::BY_DEV_PATH)
- {
- int ret;
- if (str.empty())
- throw runtime::Exception("Empty argument");
-
- switch (method) {
- case InitMethod::BY_DEV_PATH:
- ret = crypt_init(&device, str.c_str());
- if (ret != 0)
- throw runtime::Exception("crypt_init() failed for " + str + ": "
- + std::to_string(ret));
- break;
- case InitMethod::BY_MAP_NAME:
- ret = crypt_init_by_name(&device, str.c_str());
- if (ret != 0)
- throw runtime::Exception("crypt_init_by_name() failed for " +
- str + ": " + std::to_string(ret));
- break;
- default:
- throw runtime::Exception("Unsupported init method " +
- std::to_string(static_cast<int>(method)));
+ int fd = TEMP_FAILURE_RETRY(dup2(pipefd[0], STDIN_FILENO));
+ if (fd == -1)
+ exit(EXIT_FAILURE);
+
+ ::close(pipefd[0]);
}
-#ifndef NDEBUG
- /*
- * This option causes debug logs to be printed to STDOUT. TODO execute it
- * once for all engines?
- */
- crypt_set_debug_level(CRYPT_DEBUG_ALL);
- crypt_set_log_callback(device, &log, NULL);
-#endif
-
- // Needs root
- if (1 != crypt_memory_lock(device, 1))
- throw runtime::Exception("Failed to lock memory");
+ ::execv(argv[0], const_cast<char *const*>(argv));
+ ::exit(EXIT_FAILURE);
}
- Device(const Device&) = delete;
- Device& operator=(const Device&) = delete;
+ if (d != NULL) {
+ ::close(pipefd[0]);
- ~Device()
- {
- if (0 != crypt_memory_lock(device, 0))
- ERROR(SINK, "Failed to unlock memory");
+ // write the pipefd[1]
+ ssize_t ret = TEMP_FAILURE_RETRY(write(pipefd[1], d->data(), d->size()));
+ if (ret < 0 || (size_t)ret != d->size())
+ throw runtime::Exception("write() failed");
- crypt_free(device);
+ ::close(pipefd[1]);
}
- crypt_device* get() noexcept { return device; }
-
-private:
- crypt_device *device;
-};
-
-std::string mappingPath(const std::string& name)
-{
- static std::string mappings_dir = std::string(crypt_get_dir()) + '/';
- return mappings_dir + name;
+ int status;
+ while (::waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR) {
+ throw runtime::Exception("waitpid() failed: " + std::to_string(errno));
+ }
+ }
}
} // anonymous namespace
{
switch (type) {
case DeviceType::LUKS:
- {
- Device d(devPath);
-
- // TODO support other formats? support other ciphers?
- crypt_params_luks1 params = {
- .hash = DEFAULT_LUKS_HASH,
- .data_alignment = DEFAULT_LUKS_ALIGNMENT,
- .data_device = NULL,
- };
-
- int ret = crypt_format(d.get(),
- CRYPT_LUKS1,
- DEFAULT_LUKS_CIPHER_NAME,
- DEFAULT_LUKS_CIPHER_MODE,
- NULL, // NULL uid
- reinterpret_cast<const char*>(key.data()),
- key.size(),
- ¶ms);
- if (ret != 0)
- throw runtime::Exception("crypt_format() failed: " + std::to_string(ret));
- break;
- }
+ {
+ // format
+ const char *const argv1[] = {
+ "/usr/sbin/cryptsetup",
+ "-q",
+ "luksFormat",
+ devPath.c_str(),
+ "--key-size=512",
+ "--master-key-file=/dev/stdin",
+ "--key-file=/usr/share/dummy_password",
+ NULL
+ };
+
+ forkAndWrite(argv1, &key);
+
+ // remove the key slot
+ const char *const argv2[] = {
+ "/usr/sbin/cryptsetup",
+ "-q",
+ "erase",
+ NULL
+ };
+
+ forkAndWrite(argv2, NULL);
+
+ break;
+ }
case DeviceType::PLAIN:
// TODO
default:
switch (type) {
case DeviceType::LUKS:
{
- Device d(devPath);
- int ret = crypt_load(d.get(), CRYPT_LUKS1, NULL);
- if (ret != 0)
- throw runtime::Exception("crypt_load() failed: " + std::to_string(ret));
-
- // TODO possible support for activation flags
- ret = crypt_activate_by_volume_key(d.get(),
- name.c_str(),
- reinterpret_cast<const char*>(key.data()),
- key.size(),
- 0);
- if (ret != 0)
- throw runtime::Exception("crypt_activate_by_volume_key() failed: " +
- std::to_string(ret));
+ const char *const argv[] = {
+ "/usr/sbin/cryptsetup",
+ "open",
+ devPath.c_str(),
+ name.c_str(),
+ "--master-key-file=/dev/stdin",
+ NULL
+ };
+
+ forkAndWrite(argv, &key);
+
break;
}
void CryptsetupEngine::close(const std::string &name)
{
- Device d(name, Device::InitMethod::BY_MAP_NAME);
+ const char *const argv[] = {
+ "/usr/sbin/cryptsetup",
+ "close",
+ name.c_str(),
+ NULL
+ };
- int ret = crypt_deactivate(d.get(), name.c_str());
- if (ret != 0)
- throw runtime::Exception("crypt_deactivate() failed for " + name +
- ": " + std::to_string(ret));
+ forkAndWrite(argv, NULL);
}
bool CryptsetupEngine::isKeyMetaSet()