OUT-893 Local provisioning first version (incomplete)

OUT-893
Alex Sualdea 2022-06-20 16:34:48 +02:00
parent 300cbe221e
commit cfcd147e34
9 changed files with 684 additions and 0 deletions

20
LocalProvisioning.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "stdafx.h"
#include "unpack.hh"
#include "LocalProvisioning.h"
namespace pEp
{
}
//void install_if_location_empty(
// CryptoPP::PublicKey& deployment_key,
// CryptoPP::PrivateKey& provisioning_key,
// std::filesystem::path pkg_path,
// std::string location,
// std::filesystem::path target_path
//);

129
LocalProvisioning.h Normal file
View File

@ -0,0 +1,129 @@
#pragma once
#include "stdafx.h"
#include <boost/algorithm/string.hpp>
#include "unpack.hh"
namespace pEp
{
#define ProvisioningRegKey _T("Software\\pEp\\Provisioning")
#define ProvisioningLocalFolderRegKey _T("LocalFolder")
#define ProvisioningIsProvisionedRegKey _T("IsProvisioned")
#define ProvisioningFileNameRegKey _T("FileName")
#define DeploymentKeyFile L"deployment_key-pub.der"
#define ProvisioningKeyFile L"provisioning_key.der"
#define DefaultProvisionPackage L"pEp.pkk"
class LocalProvisioning
{
bool convert_bool(const std::wstring& in)
{
bool ret;
if (in.size() < 1)
return false;
std::wstring s = in;
boost::to_lower(s);
std::wistringstream(s) >> std::boolalpha >> ret;
return ret;
}
std::wstring defaultProvisioningPath()
{
std::wstring pep_user_path = SignedPackage::pEp_locations.at("PER_USER_DIRECTORY");
std::filesystem::path provisioning_path(pep_user_path);
provisioning_path /= "Provisioning";
return provisioning_path.c_str();
}
bool create_dir_if_not_exists(const std::filesystem::path& path)
{
if (!std::filesystem::exists(path))
{
std::filesystem::create_directories(path);
}
else
{
if (!std::filesystem::is_directory(path))
{
std::filesystem::remove(path);
std::filesystem::create_directories(path);
}
}
return std::filesystem::exists(path) && std::filesystem::is_directory(path);
}
public:
void Run()
{
pEp::utility::RegistryKey provisionRegKey(ProvisioningRegKey);
std::wstring isProvisioned = provisionRegKey.GetValue(ProvisioningIsProvisionedRegKey,
L"False");
std::wstring localFolder = provisionRegKey.GetValue(ProvisioningLocalFolderRegKey,
defaultProvisioningPath());
std::wstring provisioning_file_name = provisionRegKey.GetValue(ProvisioningFileNameRegKey,
DefaultProvisionPackage);
if (!convert_bool(isProvisioned))
{
std::filesystem::path pep_user_path(SignedPackage::pEp_locations.at("PER_USER_DIRECTORY"));
std::filesystem::path provisioning_path(localFolder);
std::filesystem::path pkg_path(provisioning_path / provisioning_file_name);
std::filesystem::path deployment_key_path = provisioning_path / DeploymentKeyFile;
std::filesystem::path provisioning_key_path = provisioning_path / ProvisioningKeyFile;
std::filesystem::path target_path = provisioning_path / L"package";
if (!std::filesystem::exists(provisioning_path))
create_dir_if_not_exists(provisioning_path);
if (!std::filesystem::exists(pkg_path))
return; // AQUI log?
if (!std::filesystem::exists(deployment_key_path))
return; // AQUI log
if (!std::filesystem::exists(provisioning_key_path))
return; // AQUI log
CryptoPP::ed25519PublicKey deployment_key;
CryptoPP::RSA::PrivateKey provisioning_key;
try
{
SignedPackage::LoadPublicKey(deployment_key_path, deployment_key);
SignedPackage::LoadPrivateKey(provisioning_key_path, provisioning_key);
SignedPackage::install_if_location_empty(deployment_key, provisioning_key,
pkg_path, "PER_USER_DIRECTORY", target_path);
for (auto const& direntry : std::filesystem::directory_iterator{ target_path }) {
std::filesystem::path source = direntry.path();
std::filesystem::rename(direntry.path(), pep_user_path / source.filename());
}
provisionRegKey.SetValue(ProvisioningIsProvisionedRegKey,L"True");
}
catch (std::runtime_error& e)
{
std::cout << e.what() << "\n";
}
catch (std::exception& e)
{
std::cout << e.what() << "\n";
}
}
}
};
}

View File

@ -9,6 +9,7 @@
#include "pEpCOMServerAdapter.h"
#include "LocalJSONAdapter.h"
#include "CMainWindow.h"
#include "LocalProvisioning.h"
#include <iostream>
@ -91,6 +92,13 @@ extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/
_AtlModule.hModule(hInstance);
_AtlModule.start_gatekeeper();
// AQUI provisioning
pEp::LocalProvisioning provisioning;
provisioning.Run();
// AQUI provisioning
PEP_SESSION first_session;
PEP_STATUS status = ::init(&first_session, NULL, NULL, pEp::Adapter::_ensure_passphrase);

View File

@ -73,6 +73,7 @@
<PreprocessorDefinitions>JSON_ADAPTER_LIBRARY;DEBUG_ENABLED;WIN32;_WINDOWS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<EnablePREfast>false</EnablePREfast>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Midl>
<MkTypLibCompatible>false</MkTypLibCompatible>
@ -109,6 +110,7 @@
<PreprocessorDefinitions>JSON_ADAPTER_LIBRARY;DEBUG_ENABLED;WIN32;_WINDOWS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<EnablePREfast>false</EnablePREfast>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Midl>
<MkTypLibCompatible>false</MkTypLibCompatible>
@ -151,10 +153,12 @@
</PrecompiledHeader>
</ClCompile>
<ClCompile Include="pEp_utility.cpp" />
<ClCompile Include="LocalProvisioning.cpp" />
<ClCompile Include="stdafx.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="unpack.cc" />
<ClCompile Include="utf8_helper.cpp" />
<ClCompile Include="xdlldata.c">
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
@ -171,12 +175,14 @@
<ClInclude Include="CpEpEngine.h" />
<ClInclude Include="GateKeeper.h" />
<ClInclude Include="LocalJSONAdapter.h" />
<ClInclude Include="LocalProvisioning.h" />
<ClInclude Include="pEpCOMServerAdapter.h" />
<ClInclude Include="pEpCOMServerAdapter_i.h" />
<ClInclude Include="pEp_utility.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="unpack.hh" />
<ClInclude Include="utf8_helper.h" />
<ClInclude Include="xdlldata.h" />
</ItemGroup>

View File

@ -49,6 +49,12 @@
<ClCompile Include="CMainWindow.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="unpack.cc">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LocalProvisioning.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
@ -90,6 +96,12 @@
<ClInclude Include="CMainWindow.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="unpack.hh">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LocalProvisioning.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="pEpCOMServerAdapter.rc">

View File

@ -594,5 +594,70 @@ namespace pEp {
*opt_field_array = NULL;
}
}
/** RegistryKey class **/
LONG RegistryKey::create_key(HKEY hk, const std::wstring& key, HKEY& hkKey) noexcept
{
return RegCreateKeyEx(hk, key.c_str(), 0, REG_NONE,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkKey, NULL);
}
RegistryKey::RegistryKey(const std::wstring& keyPath) noexcept
{
key_path = keyPath;
if (RegOpenCurrentUser(KEY_ALL_ACCESS, &cu) == ERROR_SUCCESS)
{
lResult = RegOpenKeyEx(cu, key_path.c_str(), 0, KEY_ALL_ACCESS, &hkKeyPath);
if (lResult == ERROR_SUCCESS)
{
opened = true;
}
else if (lResult == ERROR_FILE_NOT_FOUND)
{
if (create_key(cu, key_path, hkKeyPath) == ERROR_SUCCESS)
opened = true;
}
}
}
std::wstring RegistryKey::GetValue(const std::wstring& key, const std::wstring& default_value) noexcept
{
if (opened)
{
DWORD size;
lResult = RegGetValue(cu, key_path.c_str(), key.c_str(), RRF_RT_REG_SZ, NULL, NULL, &size);
if (lResult == ERROR_SUCCESS && size)
{
std::wstring ret;
std::unique_ptr<wchar_t[]> value(new wchar_t[size]);
lResult = RegGetValue(cu, key_path.c_str(), key.c_str(), RRF_RT_REG_SZ, NULL, value.get(), &size);
ret = value.get();
if (lResult == ERROR_SUCCESS)
return ret;
else
return default_value;
}
else if (lResult == ERROR_FILE_NOT_FOUND)
{
SetValue(key, default_value);
return default_value;
}
else
{
return default_value;
}
}
else
{
return default_value;
}
}
bool RegistryKey::SetValue(const std::wstring& key, const std::wstring& value) noexcept
{
return RegSetValueEx(hkKeyPath, key.c_str(), 0, REG_SZ, (BYTE*)value.c_str(), value.size() * 2) == ERROR_SUCCESS;
}
}
}

View File

@ -90,5 +90,48 @@ namespace pEp {
identity_list *identities(SAFEARRAY * sa);
::message * text_message_to_C(TextMessage *msg);
void text_message_from_C(TextMessage *msg2, const ::message *msg);
/// <summary>
/// Little utility class to read/write from the windows registry
/// </summary>
class RegistryKey
{
HKEY cu;
bool opened = false;
HKEY hkKeyPath = NULL;
LONG lResult;
std::wstring key_path;
LONG create_key(HKEY hk, const std::wstring& key, HKEY& hkKey) noexcept;
public:
/// <summary>
///
/// </summary>
/// <param name="keyPath">Key were the settings are to be used (e.g."Software\\pEp\\Something")</param>
RegistryKey(const std::wstring& keyPath) noexcept;
/// <summary>
/// Gets a value from a key.
/// </summary>
/// <param name="key">Relative (to the initialized one in constructor) key name </param>
/// <param name="default_value">
/// Value that will be returned in case the key
/// does not exist or any error is produced
/// </param>
/// <returns>The value retrieved</returns>
std::wstring GetValue(const std::wstring& key, const std::wstring& default_value) noexcept;
/// <summary>
/// Sets a value for a key
/// </summary>
/// <param name="key">Relative (to the initialized one in constructor) key name </param>
/// <param name="value">The value to be set</param>
bool SetValue(const std::wstring& key, const std::wstring& value) noexcept;
};
}
}

291
unpack.cc Normal file
View File

@ -0,0 +1,291 @@
#include "stdafx.h"
#include "unpack.hh"
#include <system_error>
#include <archive.h>
#include <archive_entry.h>
#ifdef WIN32
#include <io.h>
#include <direct.h>
// FIXME: name collision possible
static wchar_t *mkdtemp(wchar_t *_template, size_t len)
{
if (_wmktemp_s(_template, len+1))
return nullptr;
if (_wmkdir(_template))
return nullptr;
return _template;
}
#endif
namespace SignedPackage {
static bool path_empty(std::wstring path)
{
if (!std::filesystem::exists(path))
return true;
if (!std::filesystem::is_directory(path))
return false;
return std::filesystem::is_empty(path);
}
static void ensure_target_path(std::wstring path)
{
if (std::filesystem::exists(path)) {
if (!std::filesystem::is_directory(path))
std::filesystem::remove(path);
}
std::filesystem::create_directories(path);
}
static std::filesystem::path mktempdir()
{
std::filesystem::path _path = std::filesystem::temp_directory_path() / "spXXXXXXXXXXXX";
std::unique_ptr< wchar_t[] > buffer { ::_wcsdup(_path.c_str()) };
if (!buffer.get())
throw std::bad_alloc();
wchar_t *p = mkdtemp(buffer.get(), wcslen(buffer.get()));
if (!p) {
char err[512];
if (!::strerror_s(err, sizeof(err), errno))
throw std::filesystem::filesystem_error(err,
std::error_code(errno, std::system_category()));
else
throw std::filesystem::filesystem_error("Filesystem error",
std::error_code(errno, std::system_category()));
}
return buffer.get();
}
static void throw_archive_read_error(struct archive *a)
{
if (::archive_error_string(a)) {
std::string error_string = ::archive_error_string(a);
int e = ::archive_errno(a);
::archive_read_free(a);
throw std::filesystem::filesystem_error(error_string,
std::error_code(e, std::system_category()));
}
else if (errno) {
::archive_read_free(a);
char err[512];
if (!::strerror_s(err, sizeof(err), errno))
throw std::filesystem::filesystem_error(err,
std::error_code(errno, std::system_category()));
else
throw std::filesystem::filesystem_error("Filesystem error",
std::error_code(errno, std::system_category()));
}
}
static void Load(const std::wstring& filename, CryptoPP::BufferedTransformation& bt)
{
CryptoPP::FileSource file(filename.c_str(), true);
file.TransferTo(bt);
bt.MessageEnd();
}
void LoadPrivateKey(const std::wstring& filename, CryptoPP::PrivateKey& key)
{
CryptoPP::ByteQueue queue;
Load(filename, queue);
key.Load(queue);
}
void LoadPublicKey(const std::wstring& filename, CryptoPP::PublicKey& key)
{
CryptoPP::ByteQueue queue;
Load(filename, queue);
key.Load(queue);
}
void extract_archive(
std::wstring pkg_path,
std::wstring target_path
)
{
std::wstring cwd = std::filesystem::current_path();
struct archive *a = ::archive_read_new();
::archive_read_support_filter_all(a);
::archive_read_support_format_all(a);
int r = ::archive_read_open_filename_w(a, pkg_path.c_str(), 32768);
if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
std::filesystem::current_path(cwd);
throw_archive_read_error(a);
}
std::filesystem::current_path(target_path);
struct ::archive_entry *entry;
while (::archive_read_next_header(a, &entry) == ARCHIVE_OK) {
std::string pathname{::archive_entry_pathname(entry)};
do {
r = archive_read_extract(a, entry,
ARCHIVE_EXTRACT_ACL |
ARCHIVE_EXTRACT_FFLAGS |
ARCHIVE_EXTRACT_MAC_METADATA |
ARCHIVE_EXTRACT_NO_OVERWRITE |
ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS |
ARCHIVE_EXTRACT_SECURE_NODOTDOT |
ARCHIVE_EXTRACT_SECURE_SYMLINKS |
ARCHIVE_EXTRACT_TIME |
ARCHIVE_EXTRACT_XATTR
);
} while (r == ARCHIVE_RETRY);
if (r != ARCHIVE_OK && r != ARCHIVE_WARN) {
std::filesystem::current_path(cwd);
throw_archive_read_error(a);
}
}
::archive_read_free(a);
std::filesystem::current_path(cwd);
}
std::string decrypt_distribution_key(
std::filesystem::path key_path,
CryptoPP::PrivateKey& provisioning_key
)
{
CryptoPP::RSAES< CryptoPP::OAEP< CryptoPP::SHA256 > >::Decryptor
decryptor(provisioning_key);
CryptoPP::AutoSeededRandomPool rng;
std::string distribution_key;
CryptoPP::FileSource keySource(key_path.c_str(), true,
new CryptoPP::PK_DecryptorFilter(rng, decryptor,
new CryptoPP::StringSink(distribution_key)));
return distribution_key;
}
void verify_distribution_signature(
std::filesystem::path archive,
std::filesystem::path sigfile,
CryptoPP::PublicKey& deployment_key
)
{
CryptoPP::ed25519::Verifier verifier(
dynamic_cast< CryptoPP::X509PublicKey& >(deployment_key));
const size_t size = 64; // ed25519 signature size
char signature[size];
{
std::ifstream _sigfile(sigfile, std::ios_base::in |
std::ios_base::binary);
_sigfile.read(signature, size);
}
std::ifstream _archive(archive, std::ios_base::in | std::ios_base::binary);
assert(_archive.good());
bool valid = verifier.VerifyStream(_archive,
(unsigned char*) signature, size);
if (!valid)
throw std::runtime_error("signature does not match"); // AQUI
}
std::filesystem::path decrypt_distribution_archive(
std::filesystem::path archive,
std::filesystem::path key_path,
CryptoPP::PrivateKey& provisioning_key
)
{
std::string distribution_key = decrypt_distribution_key(key_path,
provisioning_key);
std::ifstream ifs(archive, std::ios_base::binary | std::ios_base::in);
char iv[12]; // 96 bit is GCM standard size
ifs.read(iv, sizeof(iv));
CryptoPP::GCM< CryptoPP::AES >::Decryption dec;
dec.SetKeyWithIV((const unsigned char*) distribution_key.c_str(),
distribution_key.size(), (const unsigned char*) iv, sizeof(iv));
std::filesystem::path decrypted_archive = archive.c_str() + std::wstring(L"D");
CryptoPP::FileSource archiveSource(ifs, true,
new CryptoPP::AuthenticatedDecryptionFilter(dec,
new CryptoPP::FileSink(decrypted_archive.c_str())));
return decrypted_archive;
}
std::filesystem::path extract_deployment_archive(
CryptoPP::PublicKey& deployment_key,
CryptoPP::PrivateKey& provisioning_key,
std::filesystem::path pkg_path
)
{
std::filesystem::path target_path = mktempdir();
extract_archive(pkg_path, target_path);
verify_distribution_signature(target_path / "DIST.A",
target_path / "DIST.SIG", deployment_key);
decrypt_distribution_archive(target_path / "DIST.A",
target_path / "DIST.KEY", provisioning_key);
return target_path;
}
void install_if_location_empty(
CryptoPP::PublicKey& deployment_key,
CryptoPP::PrivateKey& provisioning_key,
std::filesystem::path pkg_path,
std::string location,
std::filesystem::path target_path
)
{
if (path_empty(target_path)) {
std::filesystem::path tmp_path = extract_deployment_archive(deployment_key,
provisioning_key, pkg_path);
std::filesystem::path decrypted_archive =
decrypt_distribution_archive(tmp_path / "DIST.A",
tmp_path / "DIST.KEY", provisioning_key);
extract_archive(decrypted_archive, tmp_path);
ensure_target_path(target_path);
for (auto const& direntry : std::filesystem::directory_iterator{tmp_path / location}) {
std::filesystem::path source = direntry.path();
std::filesystem::rename(direntry.path(), target_path / source.filename());
}
std::filesystem::remove_all(tmp_path);
}
}
//void provision_user(
// pEp::UpdateClient::product p,
// pEp::UpdateClient::PublicKey update_key,
// CryptoPP::PublicKey& deployment_key,
// CryptoPP::PrivateKey& provisioning_key,
// pEp::notifyRead_t notifyRead,
// locations loc
// )
//{
// std::string target_path = loc["PER_USER_DIRECTORY"];
// // do not provision systems, which formerly were used
// if (!path_empty(target_path))
// return;
// std::string pkg_path = pEp::UpdateClient::update(p, update_key, notifyRead);
// install_if_location_empty(deployment_key, provisioning_key, pkg_path, "PER_USER_DIRECTORY", target_path);
// std::filesystem::remove(pkg_path);
//}
}

110
unpack.hh Normal file
View File

@ -0,0 +1,110 @@
#ifndef SP_UNPACK
#define SP_UNPACK
#include <string>
#include <map>
#include <filesystem>
#include <cryptopp/files.h>
#include <cryptopp/oaep.h>
#include <cryptopp/osrng.h>
#include <cryptopp/xed25519.h>
#include <cryptopp/rsa.h>
#include <cryptopp/gcm.h>
#include <codecvt>
#include <pEp/pEpEngine.h>
//#include <pEp/downloadclient.hh>
#include "utf8_helper.h"
namespace SignedPackage {
// locations maps installation location name to actual POSIX path
typedef std::map< std::string, std::wstring > locations;
const locations pEp_locations = {
{ "PER_USER_DIRECTORY", pEp::utility::utf16_string(::per_user_directory()) },
{ "PER_MACHINE_DIRECTORY", pEp::utility::utf16_string(::per_machine_directory()) }
};
void LoadPrivateKey(const std::wstring& filename, CryptoPP::PrivateKey& key);
void LoadPublicKey(const std::wstring& filename, CryptoPP::PublicKey& key);
void extract_archive(
std::wstring pkg_path,
std::wstring target_path
);
void decrypt_archive(
std::filesystem::path archive,
std::filesystem::path key,
CryptoPP::PrivateKey& provisioning_key
);
std::string decrypt_distribution_key(
std::filesystem::path key_path,
CryptoPP::PrivateKey& provisioning_key
);
void verify_distribution_signature(
std::filesystem::path archive,
std::filesystem::path sigfile,
CryptoPP::PublicKey& deployment_key
);
std::filesystem::path decrypt_distribution_archive(
std::filesystem::path archive,
std::filesystem::path key_path,
CryptoPP::PrivateKey& provisioning_key
);
std::filesystem::path extract_deployment_archive(
CryptoPP::PublicKey& deployment_key,
CryptoPP::PrivateKey& provisioning_key,
std::filesystem::path pkg_path
);
void install_if_location_empty(
CryptoPP::PublicKey& deployment_key,
CryptoPP::PrivateKey& provisioning_key,
std::filesystem::path pkg_path,
std::string location,
std::filesystem::path target_path
);
/* how to provision for a user and use this API
#include <pEp/unpack.hh>
void provision(std::string url)
{
pEp::UpdateClient::product p { "provisioning data", url };
UpdateClient::PublicKey update_key;
UpdateClient::load_key("update_key.der");
CryptoPP::ed25519PublicKey deployment_key;
SignedPackage::LoadPublicKey("deployment_key-pub.der", deployment_key);
CryptoPP::RSA::PrivateKey provisioning_key;
SignedPackage::LoadPrivateKey("provisioning_key.der", provisioning_key);
SignedPackage::provision_user(p, update_key, deployment_key,
provisioning_key);
}
*/
//void provision_user(
// pEp::UpdateClient::product p,
// pEp::UpdateClient::PublicKey update_key,
// CryptoPP::PublicKey& deployment_key,
// CryptoPP::PrivateKey& provisioning_key,
// pEp::notifyRead_t notifyRead = nullptr,
// locations loc = pEp_locations
// );
}
#endif // SP_UNPACK