You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
137 lines
3.6 KiB
C++
137 lines
3.6 KiB
C++
#include "security-token.hh"
|
|
#include "json_spirit/json_spirit_value.h"
|
|
#include "json_spirit/json_spirit_writer.h"
|
|
#include <random>
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
|
|
#include <cstdlib> // for getenv()
|
|
#include <boost/filesystem.hpp>
|
|
|
|
namespace fs = boost::filesystem;
|
|
|
|
namespace
|
|
{
|
|
// 36 alphanumeric characters
|
|
static const char token_alphabet[] = "qaywsxedcrfvtgbzhnujmikolp1234567890POIUZTREWQASDFGHJKLMNBVCXY";
|
|
|
|
std::string create_random_token(unsigned length=38)
|
|
{
|
|
static std::random_device rd;
|
|
static std::mt19937 gen(rd());
|
|
static std::uniform_int_distribution<> dis( 0, sizeof(token_alphabet)-2 ); // sizeof-2 because the range is a closed interval!
|
|
|
|
const unsigned left_len = length/2;
|
|
const unsigned right_len = length-left_len;
|
|
|
|
std::string ret; ret.reserve(length+1);
|
|
|
|
std::generate_n( std::back_inserter(ret), left_len, [&](){ return token_alphabet[dis(gen)]; } );
|
|
ret += '_';
|
|
std::generate_n( std::back_inserter(ret), right_len, [&](){ return token_alphabet[dis(gen)]; } );
|
|
return ret;
|
|
}
|
|
|
|
// platform dependent:
|
|
#ifdef _WIN32
|
|
|
|
fs::path get_token_filename()
|
|
{
|
|
// Windows guarantees that this directory is rw by the user only?
|
|
const char* const dir = getenv("LOCALAPPDATA");
|
|
return dir / fs::path("pEp") / fs::path("json-token");
|
|
}
|
|
|
|
void write_security_file(const std::string& content)
|
|
{
|
|
const fs::path filename = get_token_filename();
|
|
const fs::path parent = filename.parent_path();
|
|
fs::create_directories(parent);
|
|
fs::ofstream secfile(filename);
|
|
secfile << content;
|
|
secfile.close();
|
|
}
|
|
|
|
#else
|
|
|
|
// version for POSIX-compliant systems:
|
|
#include <sys/types.h> // for creat()
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
fs::path get_token_filename()
|
|
{
|
|
const char* const home_dir = getenv("HOME");
|
|
if(home_dir == nullptr)
|
|
{
|
|
throw std::runtime_error("Cannot get home directory. $HOME environment variable is not set.");
|
|
}
|
|
|
|
const fs::path pEp_dir = fs::path(home_dir) / ".pEp";
|
|
boost::system::error_code ec;
|
|
fs::create_directory( pEp_dir, ec );
|
|
if(ec)
|
|
{
|
|
throw boost::system::system_error(ec, "Cannot create pEp home directory" );
|
|
}
|
|
|
|
fs::permissions( pEp_dir, fs::perms(0700), ec);
|
|
if(ec)
|
|
{
|
|
throw boost::system::system_error(ec, "Cannot change permissions of pEp home directory to 0700 ");
|
|
}
|
|
|
|
return pEp_dir / "json-token";
|
|
}
|
|
|
|
|
|
void write_security_file(const std::string& content)
|
|
{
|
|
const fs::path filename = get_token_filename();
|
|
|
|
// TODO: how to atomically create a file with restricted permissions using boost::filesystem?
|
|
int fd = creat( filename.c_str(), S_IRUSR | S_IWUSR );
|
|
if(fd < 0)
|
|
{
|
|
throw std::runtime_error("Cannot create security token file \"" + filename.string() + "\": " + std::to_string(errno) );
|
|
}
|
|
|
|
const ssize_t ss = write(fd, content.data(), content.size());
|
|
if(ss<0 || uint64_t(ss)!=content.size())
|
|
{
|
|
throw std::runtime_error("Cannot write into security token file \"" + filename.string() + "\": " + std::to_string(errno));
|
|
}
|
|
close(fd);
|
|
}
|
|
|
|
#endif // ! _WIN32
|
|
|
|
} // end of anonymous namespace
|
|
|
|
|
|
namespace js = json_spirit;
|
|
|
|
// creates a file with restrictive access rights that contains a security token.
|
|
std::string create_security_token(const std::string& server_address, unsigned port_nr, const std::string& path)
|
|
{
|
|
const std::string sec_token = create_random_token();
|
|
|
|
js::Object o;
|
|
o.emplace_back("address", server_address);
|
|
o.emplace_back("port", uint64_t(port_nr));
|
|
o.emplace_back("path", path);
|
|
o.emplace_back("security_token", sec_token );
|
|
|
|
const std::string content = js::write( o, js::pretty_print | js::raw_utf8 ) + '\n';
|
|
write_security_file( content );
|
|
|
|
return sec_token;
|
|
}
|
|
|
|
|
|
void remove_token_file()
|
|
{
|
|
const fs::path filename = get_token_filename();
|
|
fs::remove(filename.c_str());
|
|
}
|