genode/repos/dde_rump/src/server/rump_cgd/cgd.cc

512 lines
12 KiB
C++

/**
* \brief RUMP cgd
* \author Josef Soentgen
* \date 2014-04-11
*/
/*
* Copyright (C) 2014 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <os/config.h>
/* repo includes */
#include <rump_cgd/cgd.h>
/* local includes */
#include "cgd.h"
/* rump includes */
extern "C" {
#include <sys/cdefs.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <sys/dirent.h>
#include <sys/disklabel.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sys/reboot.h>
#include <dev/cgdvar.h>
#include <rump/rump.h>
#include <rump/rump_syscalls.h>
}
static const bool verbose = false;
#define PDBGV(...) if (verbose) PDBG(__VA_ARGS__)
/**
* Miscellaneous methods used for converting the key string
*/
namespace {
/**
* Decode base64 encoding
*/
class Base64
{
private:
/* initialize the lookup table */
static constexpr const unsigned char _ascii_to_value[256]
{
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
};
public:
static size_t decode(char *dst, char const *src, size_t src_len)
{
unsigned char const *usrc = reinterpret_cast<unsigned char const*>(src);
size_t i;
for (i = 0; i < src_len; i += 4) {
*(dst++) = _ascii_to_value[usrc[i]] << 2 | _ascii_to_value[usrc[i + 1]] >> 4;
*(dst++) = _ascii_to_value[usrc[i + 1]] << 4 | _ascii_to_value[usrc[i + 2]] >> 2;
*(dst++) = _ascii_to_value[usrc[i + 2]] << 6 | _ascii_to_value[usrc[i + 3]];
}
return src_len * 3/4;
}
};
/* lookup table instance */
constexpr unsigned char Base64::_ascii_to_value[256];
/**
* Convert unsigned big endian value to host endianess
*/
unsigned read_big_endian(unsigned val)
{
unsigned char *p = reinterpret_cast<unsigned char*>(&val);
return (p[3]<<0) | (p[2]<<8) | (p[1]<<16) | (p[0]<<24);
}
}
/**
* cgd(4) wrapper
*/
namespace Cgd {
struct Params
{
enum {
ALGORITHM_LEN = 16,
IVMETHOD_LEN = 16,
KEY_LEN = 64,
PASSPHRASE_LEN = 64,
SALT_LEN = 29,
};
char algorithm[ALGORITHM_LEN];
char ivmethod[IVMETHOD_LEN];
char key[KEY_LEN];
Genode::size_t keylen = 0;
Genode::size_t blocksize = 0;
Params()
{
Genode::memset(algorithm, 0, sizeof(*algorithm));
Genode::memset(ivmethod, 0, sizeof(*ivmethod));
Genode::memset(key, 0, sizeof(*key));
}
static Params *generate()
{
return 0;
}
};
/**
* Configuration of the cgd device
*
* cgdconfig(8) saves the key as base 64 encoded string. The first 4 bytes
* of this string are the length of the key in bits stored as big endian.
*/
class Config
{
public:
/* same as in cgdconfig(8) */
typedef enum { ACTION_INVALID, ACTION_CONFIGURE, ACTION_GENERATE } Action;
private:
enum { ACTION_VALUE_LEN = 16, /* action attribute value length */
ENCODED_KEY_LEN = 64, /* encoded key string buffer */
DECODED_KEY_LEN = 36, /* decoded key string buffer */
VALID_ENCODED_KEY_LEN = 48, /* length of 256Bit key */
};
Genode::Config &_cfg;
Action _action;
Params *_params;
/**
* Get action from config attribute
*
* \return action
*/
Action _get_action()
{
char action_val[ACTION_VALUE_LEN];
if (_cfg.xml_node().has_attribute("action"))
_cfg.xml_node().attribute("action").value(action_val, sizeof (action_val));
else
return ACTION_INVALID;
if (Genode::strcmp(action_val, "configure") == 0)
return ACTION_CONFIGURE;
if (Genode::strcmp(action_val, "generate") == 0)
return ACTION_GENERATE;
return ACTION_INVALID;
}
/**
* Decode key string in cgdconfig(8) format
*
* \param dst pointer to buffer to store decoded key
* \param src pointer to buffer with encoded key string
* \param src_len length of encoded key string buffer
*
* \return true if decoding was successful and false otherwise
*/
bool _decode_key_string(char *dst, char const *src, size_t src_len)
{
char dec_key[DECODED_KEY_LEN];
/* check that buffer is large enough */
if (sizeof (dec_key) < (src_len*3/4))
return false;
(void) Base64::decode(dec_key, src, src_len);
unsigned *p = reinterpret_cast<unsigned *>(dec_key);
unsigned bits = read_big_endian(*p);
Genode::memcpy(dst, p + 1, bits / 8);
return true;
}
/**
* Parse given configuration
*
* This method throws a Genode::Exception if an error in the given
* configuration is found.
*/
void _parse_config()
{
if (_cfg.xml_node().has_sub_node("params")) {
Genode::Xml_node pnode = _cfg.xml_node().sub_node("params");
char method_val[4];
pnode.sub_node("method").value(method_val, sizeof (method_val));
bool use_key = Genode::strcmp(method_val, "key") == 0 ? true : false;
if (!use_key) {
PERR("no valid method specified.");
throw Genode::Exception();
}
_params = new (Genode::env()->heap()) Params();
Genode::strncpy(_params->algorithm, CGD_ALGORITHM,
Params::ALGORITHM_LEN);
Genode::strncpy(_params->ivmethod, CGD_IVMETHOD,
Params::IVMETHOD_LEN);
char enc_key[ENCODED_KEY_LEN];
pnode.sub_node("key").value(enc_key, sizeof (enc_key));
size_t enc_key_len = Genode::strlen(enc_key);
if (enc_key_len != VALID_ENCODED_KEY_LEN) {
PERR("incorrect encoded key found.");
throw Genode::Exception();
}
if (!_decode_key_string(_params->key, enc_key, enc_key_len)) {
PERR("could not decode key string.");
throw Genode::Exception();
}
_params->keylen = CGD_KEYLEN;
/* let cgd(4) figure out the right blocksize */
_params->blocksize = -1;
} else {
PERR("no <params> node found.");
throw Genode::Exception();
}
}
public:
Config()
:
_cfg(*Genode::config()),
_action(_get_action()),
_params(0)
{
_parse_config();
}
~Config() { destroy(Genode::env()->heap(), _params); }
Action action() { return _action; }
Params* params() { return _params; }
};
}
/**
* Constructor
*
* \param fd used by the rumpkernel to execute syscalls on
*/
Cgd::Device::Device(int fd)
:
_fd(fd),
_blk_sz(0), _blk_cnt(0)
{
/**
* We query the disklabel(5) on CGD_RAW_DEVICE to get the
* actual block size and the number of blocks in the
* first partition. Though there may be more partitions
* we always use the first one.
*/
disklabel dl;
Genode::memset(&dl, 0, sizeof (dl));
int err = rump_sys_ioctl(_fd, DIOCGDINFO, &dl);
if (err) {
cgd_ioctl ci;
rump_sys_ioctl(_fd, CGDIOCCLR, &ci);
rump_sys_close(_fd);
PERR("could not read geometry of '%s'", CGD_RAW_DEVICE);
throw Genode::Exception();
}
_blk_sz = dl.d_secsize;
_blk_cnt = dl.d_partitions[0].p_size;
}
/**
* Destructor
*/
Cgd::Device::~Device()
{
/* unconfigure cgd(4) device to explicitly clean up buffers */
cgd_ioctl ci;
rump_sys_ioctl(_fd, CGDIOCCLR, &ci);
rump_sys_close(_fd);
}
/**
* Get name of the actual cgd device
*
* \return name
*/
char const *Cgd::Device::name() const { return CGD_RAW_DEVICE; }
/**
* Read from the cgd device
*
* \param dst pointer to buffer
* \param len size of the buffer
* \param seek_offset offset from where to start reading
*
* \return bytes read
*/
size_t Cgd::Device::read(char *dst, size_t len, seek_off_t seek_offset)
{
ssize_t ret = rump_sys_pread(_fd, dst, len, seek_offset);
return ret == -1 ? 0 : ret;
}
/**
* Write to cgd device
*
* \param src pointer to buffer
* \param len size of the buffer
* \param seek_offset offset from where to start writing
*
* \return bytes written
*/
size_t Cgd::Device::write(char const *src, size_t len, seek_off_t seek_offset)
{
/* should we append? */
if (seek_offset == ~0ULL) {
off_t off = rump_sys_lseek(_fd, 0, SEEK_END);
if (off == -1)
return 0;
seek_offset = off;
}
ssize_t ret = rump_sys_pwrite(_fd, src, len, seek_offset);
return ret == -1 ? 0 : ret;
}
/**
* Configure the actual cgd device
*
* \param alloc memory allocator for metadata
* \param p pointer to parameters
* \param dev name of the block device used as backing device for cgd
*
* \return configured cgd device
*/
Cgd::Device *Cgd::Device::configure(Genode::Allocator *alloc, Cgd::Params const *p,
char const *dev)
{
int fd = rump_sys_open(CGD_RAW_DEVICE, O_RDWR);
if (fd == -1) {
PERR("could not open '%s'", CGD_RAW_DEVICE);
throw Genode::Exception();
}
PDBGV("dev: '%s' alg: '%s' ivmethod: '%s' blocksize: %zu keylen: %zu",
dev, p->algorithm, p->ivmethod, p->blocksize, p->keylen);
/* perform configuration of cgd device */
cgd_ioctl ci;
Genode::memset(&ci, 0, sizeof (ci));
ci.ci_disk = dev;
ci.ci_alg = p->algorithm;
ci.ci_ivmethod = p->ivmethod;
ci.ci_key = p->key;
ci.ci_keylen = p->keylen;
ci.ci_blocksize = p->blocksize;
int err = rump_sys_ioctl(fd, CGDIOCSET, &ci);
if (err == -1) {
rump_sys_close(fd);
PERR("could not configure '%s'", CGD_RAW_DEVICE);
throw Genode::Exception();
}
cgd_user cu;
Genode::memset(&cu, 0, sizeof (cu));
err = rump_sys_ioctl(fd, CGDIOCGET, &cu);
if (err == -1) { /* unlikely */
/**
* Reuse former cgd_ioctl struct because it is not used by this
* ioctl() anyway.
*/
rump_sys_ioctl(fd, CGDIOCCLR, &ci);
rump_sys_close(fd);
PERR("could not get cgd information.");
throw Genode::Exception();
}
Cgd::Device *cgd = new (alloc) Cgd::Device(fd);
return cgd;
}
/**
* Initialize a new Cgd::Device
*/
Cgd::Device *Cgd::init(Genode::Allocator *alloc, Server::Entrypoint &ep)
{
/* start rumpkernel */
rump_init();
/* register block device */
if (rump_pub_etfs_register(GENODE_DEVICE, GENODE_BLOCK_SESSION,
RUMP_ETFS_BLK)) {
PERR("could not register '%s' within rumpkernel", GENODE_DEVICE);
throw Genode::Exception();
}
Cgd::Config cfg;
Cgd::Config::Action action = cfg.action();
Cgd::Device *cgd_dev = 0;
switch (action) {
case Cgd::Config::ACTION_CONFIGURE:
{
Cgd::Params *p = cfg.params();
if (!p)
throw Genode::Exception();
cgd_dev = Cgd::Device::configure(alloc, p, GENODE_DEVICE);
break;
}
case Cgd::Config::ACTION_GENERATE:
{
Cgd::Params *params = Cgd::Params::generate();
(void)params;
break;
}
case Cgd::Config::ACTION_INVALID:
PERR("invalid action declared");
throw Genode::Exception();
break;
}
PINF("exporting '%s' as Block_session", cgd_dev->name());
return cgd_dev;
}
/**
* Deinitialize a Cgd::Device
*/
void Cgd::deinit(Genode::Allocator *alloc, Cgd::Device *dev)
{
destroy(alloc, dev);
/* halt rumpkernel */
rump_sys_reboot(RB_HALT, 0);
}