Files
cryptsetup/lib/setup.c
Milan Broz ad224b479a Change in iterations counts:
* Fix key slot iteration count calculation (small -i value was the same as default).
  * The slot and key digest iteration minimun is now 1000.
  * The key digest iteration # is calculated from iteration time (approx 1/8 of that).

If something very strange happens, and the generated key is not completely random
(wrong RNG), attacker can skip the whole kesylot area processing and try to
brute force key according to limited set of keys.

The iteration time (default, inherited from slot iteration time, is cca 120ms)
can make this attack slower, if not impossible.

Note, that this is just theorethic problem, anyway it is better to be prepared
if possible:-)

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@159 36d66b0a-2a48-0410-832c-cd162a569da5
2009-12-30 19:02:44 +00:00

2062 lines
48 KiB
C

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
#include "libcryptsetup.h"
#include "luks.h"
#include "internal.h"
struct crypt_device {
char *type;
char *device;
struct luks_masterkey *volume_key;
uint64_t timeout;
uint64_t iteration_time;
int tries;
int password_verify;
/* used in CRYPT_LUKS1 */
struct luks_phdr hdr;
uint64_t PBKDF2_per_sec;
/* used in CRYPT_PLAIN */
struct crypt_params_plain plain_hdr;
char *plain_cipher;
char *plain_cipher_mode;
char *plain_uuid;
/* callbacks definitions */
void (*log)(int class, const char *msg, void *usrptr);
void *log_usrptr;
int (*confirm)(const char *msg, void *usrptr);
void *confirm_usrptr;
int (*password)(const char *msg, char *buf, size_t length, void *usrptr);
void *password_usrptr;
};
/* Log helper */
static void (*_default_log)(int class, const char *msg, void *usrptr) = NULL;
static int _debug_level = 0;
void crypt_set_debug_level(int level)
{
_debug_level = level;
}
int crypt_get_debug_level()
{
return _debug_level;
}
void crypt_log(struct crypt_device *cd, int class, const char *msg)
{
if (cd && cd->log)
cd->log(class, msg, cd->log_usrptr);
else if (_default_log)
_default_log(class, msg, NULL);
}
void logger(struct crypt_device *cd, int class, const char *file,
int line, const char *format, ...)
{
va_list argp;
char *target = NULL;
va_start(argp, format);
if (vasprintf(&target, format, argp) > 0) {
if (class >= 0) {
crypt_log(cd, class, target);
#ifdef CRYPT_DEBUG
} else if (_debug_level)
printf("# %s:%d %s\n", file ?: "?", line, target);
#else
} else if (_debug_level)
printf("# %s\n", target);
#endif
}
va_end(argp);
free(target);
}
/*
* Password processing behaviour matrix of process_key
*
* from binary file: check if there is sufficently large key material
* interactive & from fd: hash if requested, otherwise crop or pad with '0'
*/
static char *process_key(struct crypt_device *cd, const char *hash_name,
const char *key_file, size_t key_size,
const char *pass, size_t passLen)
{
char *key = safe_alloc(key_size);
memset(key, 0, key_size);
/* key is coming from binary file */
if (key_file && strcmp(key_file, "-")) {
if(passLen < key_size) {
log_err(cd, _("Cannot not read %d bytes from key file %s.\n"),
key_size, key_file);
safe_free(key);
return NULL;
}
memcpy(key, pass, key_size);
return key;
}
/* key is coming from tty, fd or binary stdin */
if (hash_name) {
if (hash(NULL, hash_name, key, key_size, pass, passLen) < 0) {
log_err(cd, _("Key processing error.\n"));
safe_free(key);
return NULL;
}
} else if (passLen > key_size) {
memcpy(key, pass, key_size);
} else {
memcpy(key, pass, passLen);
}
return key;
}
int parse_into_name_and_mode(const char *nameAndMode, char *name, char *mode)
{
/* Token content stringification, see info cpp/stringification */
#define str(s) #s
#define xstr(s) str(s)
#define scanpattern1 "%" xstr(LUKS_CIPHERNAME_L) "[^-]-%" xstr(LUKS_CIPHERMODE_L) "s"
#define scanpattern2 "%" xstr(LUKS_CIPHERNAME_L) "[^-]"
int r;
if(sscanf(nameAndMode,scanpattern1, name, mode) != 2) {
if((r = sscanf(nameAndMode,scanpattern2,name)) == 1)
strncpy(mode,"cbc-plain",10);
else
return -EINVAL;
}
return 0;
#undef scanpattern1
#undef scanpattern2
#undef str
#undef xstr
}
static int isPLAIN(const char *type)
{
return (type && !strcmp(CRYPT_PLAIN, type));
}
static int isLUKS(const char *type)
{
return (type && !strcmp(CRYPT_LUKS1, type));
}
/* keyslot helpers */
static int keyslot_verify_or_find_empty(struct crypt_device *cd, int *keyslot)
{
if (*keyslot == CRYPT_ANY_SLOT) {
*keyslot = LUKS_keyslot_find_empty(&cd->hdr);
if (*keyslot < 0) {
log_err(cd, _("All key slots full.\n"));
return -EINVAL;
}
}
switch (LUKS_keyslot_info(&cd->hdr, *keyslot)) {
case CRYPT_SLOT_INVALID:
log_err(cd, _("Key slot %d is invalid, please select between 0 and %d.\n"),
*keyslot, LUKS_NUMKEYS - 1);
return -EINVAL;
case CRYPT_SLOT_INACTIVE:
break;
default:
log_err(cd, _("Key slot %d is full, please select another one.\n"),
*keyslot);
return -EINVAL;
}
return 0;
}
static int verify_other_keyslot(struct crypt_device *cd,
const char *key_file,
unsigned int flags,
int keyIndex)
{
struct luks_masterkey *mk;
crypt_keyslot_info ki;
int openedIndex;
char *password = NULL;
unsigned int passwordLen;
get_key(_("Enter any remaining LUKS passphrase: "), &password,
&passwordLen, 0, key_file, cd->timeout, flags, cd);
if(!password)
return -EINVAL;
ki = crypt_keyslot_status(cd, keyIndex);
if (ki == CRYPT_SLOT_ACTIVE) /* Not last slot */
LUKS_keyslot_set(&cd->hdr, keyIndex, 0);
openedIndex = LUKS_open_key_with_hdr(cd->device, CRYPT_ANY_SLOT,
password, passwordLen,
&cd->hdr, &mk, cd);
if (ki == CRYPT_SLOT_ACTIVE)
LUKS_keyslot_set(&cd->hdr, keyIndex, 1);
LUKS_dealloc_masterkey(mk);
safe_free(password);
if (openedIndex < 0)
return -EPERM;
log_std(cd, _("Key slot %d verified.\n"), openedIndex);
return 0;
}
static int find_keyslot_by_passphrase(struct crypt_device *cd,
const char *key_file,
unsigned int flags,
char *message)
{
struct luks_masterkey *mk;
char *password = NULL;
unsigned int passwordLen;
int keyIndex;
get_key(message,&password,&passwordLen, 0, key_file,
cd->timeout, flags, cd);
if(!password)
return -EINVAL;
keyIndex = LUKS_open_key_with_hdr(cd->device, CRYPT_ANY_SLOT, password,
passwordLen, &cd->hdr, &mk, cd);
LUKS_dealloc_masterkey(mk);
safe_free(password);
return keyIndex;
}
static int device_check_and_adjust(struct crypt_device *cd,
const char *device,
uint64_t *size, uint64_t *offset,
int *read_only)
{
struct device_infos infos;
if (get_device_infos(device, &infos, cd) < 0) {
log_err(cd, _("Cannot get info about device %s.\n"), device);
return -ENOTBLK;
}
if (!*size) {
*size = infos.size;
if (!*size) {
log_err(cd, _("Device %s has zero size.\n"), device);
return -ENOTBLK;
}
if (*size < *offset) {
log_err(cd, _("Device %s is too small.\n"), device);
return -EINVAL;
}
*size -= *offset;
}
if (infos.readonly)
*read_only = 1;
log_dbg("Calculated device size is %" PRIu64 " sectors (%s), offset %" PRIu64 ".",
*size, *read_only ? "RO" : "RW", *offset);
return 0;
}
static int luks_remove_helper(struct crypt_device *cd,
int key_slot,
const char *other_key_file,
const char *key_file,
int verify)
{
crypt_keyslot_info ki;
int r = -EINVAL;
if (key_slot == CRYPT_ANY_SLOT) {
key_slot = find_keyslot_by_passphrase(cd, key_file, 0,
_("Enter LUKS passphrase to be deleted: "));
if(key_slot < 0) {
r = -EPERM;
goto out;
}
log_std(cd, _("key slot %d selected for deletion.\n"), key_slot);
}
ki = crypt_keyslot_status(cd, key_slot);
if (ki == CRYPT_SLOT_INVALID) {
log_err(cd, _("Key slot %d is invalid, please select between 0 and %d.\n"),
key_slot, LUKS_NUMKEYS - 1);
r = -EINVAL;
goto out;
}
if (ki <= CRYPT_SLOT_INACTIVE) {
log_err(cd, _("Key %d not active. Can't wipe.\n"), key_slot);
r = -EINVAL;
goto out;
}
if (ki == CRYPT_SLOT_ACTIVE_LAST && cd->confirm &&
!(cd->confirm(_("This is the last keyslot."
" Device will become unusable after purging this key."),
cd->confirm_usrptr))) {
r = -EINVAL;
goto out;
}
if(verify)
r = verify_other_keyslot(cd, other_key_file, 0, key_slot);
else
r = 0;
if (!r)
r = crypt_keyslot_destroy(cd, key_slot);
out:
return (r < 0) ? r : 0;
}
static int create_device_helper(struct crypt_device *cd,
const char *name,
const char *hash,
const char *cipher,
const char *cipher_mode,
const char *key_file,
const char *key,
unsigned int keyLen,
int key_size,
uint64_t size,
uint64_t skip,
uint64_t offset,
const char *uuid,
int read_only,
unsigned int flags,
int reload)
{
crypt_status_info ci;
char *dm_cipher = NULL;
char *processed_key = NULL;
int r;
ci = crypt_status(cd, name);
if (ci == CRYPT_INVALID)
return -EINVAL;
if (reload && ci < CRYPT_ACTIVE)
return -EINVAL;
if (!reload && ci >= CRYPT_ACTIVE) {
log_err(cd, _("Device %s already exists.\n"), name);
return -EEXIST;
}
if (key_size < 0 || key_size > 1024) {
log_err(cd, _("Invalid key size %d.\n"), key_size);
return -EINVAL;
}
r = device_check_and_adjust(cd, cd->device, &size, &offset, &read_only);
if (r)
return r;
if (cipher_mode && asprintf(&dm_cipher, "%s-%s", cipher, cipher_mode) < 0)
return -ENOMEM;
processed_key = process_key(cd, hash, key_file, key_size, key, keyLen);
if (!processed_key)
return -ENOENT;
r = dm_create_device(name, cd->device, dm_cipher ?: cipher, cd->type, uuid, size, skip, offset,
key_size, processed_key, read_only, reload);
free(dm_cipher);
safe_free(processed_key);
return r;
}
static int open_from_hdr_and_mk(struct crypt_device *cd,
struct luks_masterkey *mk,
const char *name,
uint32_t flags)
{
uint64_t size, offset;
char *cipher;
int read_only, no_uuid, r;
size = 0;
offset = crypt_get_data_offset(cd);
read_only = flags & CRYPT_ACTIVATE_READONLY;
no_uuid = flags & CRYPT_ACTIVATE_NO_UUID;
r = device_check_and_adjust(cd, cd->device, &size, &offset, &read_only);
if (r)
return r;
if (asprintf(&cipher, "%s-%s", crypt_get_cipher(cd),
crypt_get_cipher_mode(cd)) < 0)
r = -ENOMEM;
else
r = dm_create_device(name, cd->device, cipher, cd->type,
no_uuid ? NULL : crypt_get_uuid(cd),
size, 0, offset, mk->keyLength, mk->key,
read_only, 0);
free(cipher);
return r;
}
static void log_wrapper(int class, const char *msg, void *usrptr)
{
void (*xlog)(int class, char *msg) = usrptr;
xlog(class, (char *)msg);
}
static int yesDialog_wrapper(const char *msg, void *usrptr)
{
int (*xyesDialog)(char *msg) = usrptr;
return xyesDialog((char*)msg);
}
int crypt_confirm(struct crypt_device *cd, const char *msg)
{
if (!cd || !cd->confirm)
return 1;
else
return cd->confirm(msg, cd->confirm_usrptr);
}
static void key_from_terminal(struct crypt_device *cd, char *msg, char **key,
unsigned int *key_len, int force_verify)
{
int r, flags = 0;
if (cd->password) {
*key = safe_alloc(MAX_TTY_PASSWORD_LEN);
if (*key)
return;
r = cd->password(msg, *key, (size_t)key_len, cd->password_usrptr);
if (r < 0) {
safe_free(*key);
*key = NULL;
} else
*key_len = r;
} else {
if (force_verify || cd->password_verify)
flags |= CRYPT_FLAG_VERIFY_IF_POSSIBLE;
get_key(msg, key, key_len, 0, NULL, cd->timeout, flags, cd);
}
}
static int volume_key_by_terminal_passphrase(struct crypt_device *cd, int keyslot,
struct luks_masterkey **mk)
{
char *prompt = NULL, *passphrase_read = NULL;
unsigned int passphrase_size_read;
int r = -EINVAL, tries = cd->tries;
if(asprintf(&prompt, _("Enter passphrase for %s: "), cd->device) < 0)
return -ENOMEM;
*mk = NULL;
do {
if (*mk)
LUKS_dealloc_masterkey(*mk);
*mk = NULL;
key_from_terminal(cd, prompt, &passphrase_read,
&passphrase_size_read, 0);
if(!passphrase_read) {
r = -EINVAL;
break;
}
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase_read,
passphrase_size_read, &cd->hdr, mk, cd);
safe_free(passphrase_read);
passphrase_read = NULL;
} while (r == -EPERM && (--tries > 0));
if (r < 0 && *mk) {
LUKS_dealloc_masterkey(*mk);
*mk = NULL;
}
free(prompt);
return r;
}
static void key_from_file(struct crypt_device *cd, char *msg,
char **key, unsigned int *key_len,
const char *key_file, size_t key_size)
{
get_key(msg, key, key_len, key_size, key_file, cd->timeout, 0, cd);
}
static int _crypt_init(struct crypt_device **cd,
const char *type,
struct crypt_options *options,
int load, int need_dm)
{
int init_by_name, r;
/* if it is plain device and mapping table is being reloaded
initialize it by name*/
init_by_name = (type && !strcmp(type, CRYPT_PLAIN) && load);
/* Some of old API calls do not require DM in kernel,
fake initialisation by initialise it with kernel_check disabled */
if (!need_dm)
(void)dm_init(NULL, 0);
if (init_by_name)
r = crypt_init_by_name(cd, options->name);
else
r = crypt_init(cd, options->device);
if (!need_dm)
dm_exit();
if (r)
return -EINVAL;
crypt_set_log_callback(*cd, log_wrapper, options->icb->log);
crypt_set_confirm_callback(*cd, yesDialog_wrapper, options->icb->yesDialog);
crypt_set_timeout(*cd, options->timeout);
crypt_set_password_retry(*cd, options->tries);
crypt_set_iterarion_time(*cd, options->iteration_time ?: 1000);
crypt_set_password_verify(*cd, options->flags & CRYPT_FLAG_VERIFY);
if (load && !init_by_name)
r = crypt_load(*cd, type, NULL);
if (!r && type && !(*cd)->type) {
(*cd)->type = strdup(type);
if (!(*cd)->type)
r = -ENOMEM;
}
if (r)
crypt_free(*cd);
return r;
}
void crypt_set_log_callback(struct crypt_device *cd,
void (*log)(int class, const char *msg, void *usrptr),
void *usrptr)
{
if (!cd)
_default_log = log;
else {
cd->log = log;
cd->log_usrptr = usrptr;
}
}
void crypt_set_confirm_callback(struct crypt_device *cd,
int (*confirm)(const char *msg, void *usrptr),
void *usrptr)
{
cd->confirm = confirm;
cd->confirm_usrptr = usrptr;
}
void crypt_set_password_callback(struct crypt_device *cd,
int (*password)(const char *msg, char *buf, size_t length, void *usrptr),
void *usrptr)
{
cd->password = password;
cd->password_usrptr = usrptr;
}
/* OPTIONS: name, cipher, device, hash, key_file, key_size, key_slot,
* offset, size, skip, timeout, tries, passphrase_fd (ignored),
* flags, icb */
int crypt_create_device(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
char *key = NULL;
unsigned int keyLen;
int r;
r = _crypt_init(&cd, CRYPT_PLAIN, options, 0, 1);
if (r)
return r;
get_key(_("Enter passphrase: "), &key, &keyLen, options->key_size,
options->key_file, cd->timeout, options->flags, cd);
if (!key)
r = -ENOENT;
else
r = create_device_helper(cd, options->name, options->hash,
options->cipher, NULL, options->key_file, key, keyLen,
options->key_size, options->size, options->skip,
options->offset, NULL, options->flags & CRYPT_FLAG_READONLY,
options->flags, 0);
safe_free(key);
crypt_free(cd);
return r;
}
/* OPTIONS: same as create above */
int crypt_update_device(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
char *key = NULL;
unsigned int keyLen;
int r;
r = _crypt_init(&cd, CRYPT_PLAIN, options, 1, 1);
if (r)
return r;
get_key(_("Enter passphrase: "), &key, &keyLen, options->key_size,
options->key_file, cd->timeout, options->flags, cd);
if (!key)
r = -ENOENT;
else
r = create_device_helper(cd, options->name, options->hash,
options->cipher, NULL, options->key_file, key, keyLen,
options->key_size, options->size, options->skip,
options->offset, NULL, options->flags & CRYPT_FLAG_READONLY,
options->flags, 1);
safe_free(key);
crypt_free(cd);
return r;
}
/* OPTIONS: name, size, icb */
int crypt_resize_device(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
char *device = NULL, *cipher = NULL, *uuid = NULL, *key = NULL;
char *type = NULL;
uint64_t size, skip, offset;
int key_size, read_only, r;
log_dbg("Resizing device %s to %" PRIu64 " sectors.", options->name, options->size);
if (dm_init(NULL, 1) < 0)
return -ENOSYS;
r = dm_query_device(options->name, &device, &size, &skip, &offset,
&cipher, &key_size, &key, &read_only, NULL, &uuid);
if (r < 0) {
log_err(NULL, _("Device %s is not active.\n"), options->name);
goto out;
}
/* Try to determine type of device from UUID */
type = CRYPT_PLAIN;
if (uuid) {
if (!strncmp(uuid, CRYPT_PLAIN, strlen(CRYPT_PLAIN))) {
type = CRYPT_PLAIN;
free (uuid);
uuid = NULL;
} else if (!strncmp(uuid, CRYPT_LUKS1, strlen(CRYPT_LUKS1)))
type = CRYPT_LUKS1;
}
if (!options->device)
options->device = device;
r = _crypt_init(&cd, type, options, 1, 1);
if (r)
goto out;
size = options->size;
r = device_check_and_adjust(cd, device, &size, &offset, &read_only);
if (r)
goto out;
r = dm_create_device(options->name, device, cipher, type,
crypt_get_uuid(cd), size, skip, offset,
key_size, key, read_only, 1);
out:
safe_free(key);
free(cipher);
if (options->device == device)
options->device = NULL;
free(device);
free(uuid);
crypt_free(cd);
dm_exit();
return r;
}
/* OPTIONS: name, icb */
int crypt_query_device(struct crypt_options *options)
{
int read_only, r;
log_dbg("Query device %s.", options->name);
if (dm_init(NULL, 1) < 0)
return -ENOSYS;
r = dm_status_device(options->name);
if (r == -ENODEV) {
dm_exit();
return 0;
}
r = dm_query_device(options->name, (char **)&options->device, &options->size,
&options->skip, &options->offset, (char **)&options->cipher,
&options->key_size, NULL, &read_only, NULL, NULL);
dm_exit();
if (r < 0)
return r;
if (read_only)
options->flags |= CRYPT_FLAG_READONLY;
options->flags |= CRYPT_FLAG_FREE_DEVICE;
options->flags |= CRYPT_FLAG_FREE_CIPHER;
return 1;
}
/* OPTIONS: name, icb */
int crypt_remove_device(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
int r;
r = crypt_init_by_name(&cd, options->name);
if (r)
return r;
r = crypt_deactivate(cd, options->name);
crypt_free(cd);
return r;
}
/* OPTIONS: device, cipher, hash, align_payload, key_size (master key), key_slot
* new_key_file, iteration_time, timeout, flags, icb */
int crypt_luksFormat(struct crypt_options *options)
{
char cipherName[LUKS_CIPHERNAME_L];
char cipherMode[LUKS_CIPHERMODE_L];
char *password=NULL;
unsigned int passwordLen;
struct crypt_device *cd;
struct crypt_params_luks1 cp = {
.hash = options->hash,
.data_alignment = options->align_payload
};
int r;
r = parse_into_name_and_mode(options->cipher, cipherName, cipherMode);
if(r < 0) {
log_err(cd, _("No known cipher specification pattern detected.\n"));
return r;
}
if ((r = _crypt_init(&cd, CRYPT_LUKS1, options, 0, 1)))
return r;
if (options->key_slot >= LUKS_NUMKEYS && options->key_slot != CRYPT_ANY_SLOT) {
log_err(cd, _("Key slot %d is invalid, please select between 0 and %d.\n"),
options->key_slot, LUKS_NUMKEYS - 1);
r = -EINVAL;
goto out;
}
get_key(_("Enter LUKS passphrase: "), &password, &passwordLen, 0,
options->new_key_file, options->timeout, options->flags, cd);
if(!password) {
r = -EINVAL;
goto out;
}
r = crypt_format(cd, CRYPT_LUKS1, cipherName, cipherMode,
NULL, NULL, options->key_size, &cp);
if (r < 0)
goto out;
/* Add keyslot using internally stored volume key generated during format */
r = crypt_keyslot_add_by_volume_key(cd, options->key_slot, NULL, 0,
password, passwordLen);
out:
crypt_free(cd);
safe_free(password);
return (r < 0) ? r : 0;
}
/* OPTIONS: name, device, key_size, key_file, timeout, tries, flags, icb */
int crypt_luksOpen(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
uint32_t flags = 0;
int r;
if (!options->name)
return -EINVAL;
r = _crypt_init(&cd, CRYPT_LUKS1, options, 1, 1);
if (r)
return r;
if (options->flags & CRYPT_FLAG_READONLY)
flags |= CRYPT_ACTIVATE_READONLY;
if (options->flags & CRYPT_FLAG_NON_EXCLUSIVE_ACCESS)
flags |= CRYPT_ACTIVATE_NO_UUID;
if (options->key_file)
r = crypt_activate_by_keyfile(cd, options->name,
CRYPT_ANY_SLOT, options->key_file, options->key_size,
flags);
else
r = crypt_activate_by_passphrase(cd, options->name,
CRYPT_ANY_SLOT, options->passphrase,
options->passphrase ? strlen(options->passphrase) : 0,
flags);
crypt_free(cd);
return (r < 0) ? r : 0;
}
/* OPTIONS: device, keys_slot, key_file, timeout, flags, icb */
int crypt_luksKillSlot(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
int r;
r = _crypt_init(&cd, CRYPT_LUKS1, options, 1, 1);
if (r)
return r;
r = luks_remove_helper(cd, options->key_slot, options->key_file, NULL,
options->flags & CRYPT_FLAG_VERIFY_ON_DELKEY);
crypt_free(cd);
return (r < 0) ? r : 0;
}
/* OPTIONS: device, new_key_file, key_file, timeout, flags, icb */
int crypt_luksRemoveKey(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
int r;
r = _crypt_init(&cd, CRYPT_LUKS1, options, 1, 1);
if (r)
return r;
r = luks_remove_helper(cd, CRYPT_ANY_SLOT, options->key_file, options->new_key_file,
options->flags & CRYPT_FLAG_VERIFY_ON_DELKEY);
crypt_free(cd);
return (r < 0) ? r : 0;
}
/* OPTIONS: device, new_key_file, key_file, key_slot, flags,
iteration_time, timeout, icb */
int crypt_luksAddKey(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
int r = -EINVAL;
r = _crypt_init(&cd, CRYPT_LUKS1, options, 1, 1);
if (r)
return r;
if (options->key_file || options->new_key_file)
r = crypt_keyslot_add_by_keyfile(cd, options->key_slot,
options->key_file, 0,
options->new_key_file, 0);
else
r = crypt_keyslot_add_by_passphrase(cd, options->key_slot,
NULL, 0, NULL, 0);
crypt_free(cd);
return (r < 0) ? r : 0;
}
/* OPTIONS: device, icb */
int crypt_luksUUID(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
char *uuid;
int r;
r = _crypt_init(&cd, CRYPT_LUKS1, options, 1, 0);
if (r)
return r;
uuid = (char *)crypt_get_uuid(cd);
log_std(cd, uuid ?: "");
log_std(cd, "\n");
crypt_free(cd);
return 0;
}
/* OPTIONS: device, icb */
int crypt_isLuks(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
int r;
log_dbg("Check device %s for LUKS header.", options->device);
r = crypt_init(&cd, options->device);
if (r < 0)
return -EINVAL;
/* Do print fail here, no need to crypt_load() */
r = LUKS_read_phdr(cd->device, &cd->hdr, 0, cd) ? -EINVAL : 0;
crypt_free(cd);
return r;
}
/* OPTIONS: device, icb */
int crypt_luksDump(struct crypt_options *options)
{
struct crypt_device *cd = NULL;
int r;
r = _crypt_init(&cd, CRYPT_LUKS1, options, 1, 0);
if(r < 0)
return r;
r = crypt_dump(cd);
crypt_free(cd);
return 0;
}
void crypt_get_error(char *buf, size_t size)
{
const char *error = get_error();
if (!buf || size < 1)
set_error(NULL);
else if (error) {
strncpy(buf, error, size - 1);
buf[size - 1] = '\0';
set_error(NULL);
} else
buf[0] = '\0';
}
void crypt_put_options(struct crypt_options *options)
{
if (options->flags & CRYPT_FLAG_FREE_DEVICE) {
free((char *)options->device);
options->device = NULL;
options->flags &= ~CRYPT_FLAG_FREE_DEVICE;
}
if (options->flags & CRYPT_FLAG_FREE_CIPHER) {
free((char *)options->cipher);
options->cipher = NULL;
options->flags &= ~CRYPT_FLAG_FREE_CIPHER;
}
}
const char *crypt_get_dir(void)
{
return dm_get_dir();
}
/////////////////////////////////
//
// New API
//
int crypt_init(struct crypt_device **cd, const char *device)
{
struct crypt_device *h = NULL;
if (!cd)
return -EINVAL;
log_dbg("Allocating crypt device %s context.", device);
if (device && !device_ready(NULL, device, O_RDONLY))
return -ENOTBLK;
if (!(h = malloc(sizeof(struct crypt_device))))
return -ENOMEM;
memset(h, 0, sizeof(*h));
if (device) {
h->device = strdup(device);
if (!h->device) {
free(h);
return -ENOMEM;
}
} else
h->device = NULL;
if (dm_init(h, 1) < 0) {
free(h);
return -ENOSYS;
}
h->iteration_time = 1000;
h->password_verify = 0;
h->tries = 3;
*cd = h;
return 0;
}
int crypt_init_by_name(struct crypt_device **cd, const char *name)
{
crypt_status_info ci;
char *device = NULL;
int r;
log_dbg("Allocating crypt device context by device %s.", name);
ci = crypt_status(NULL, name);
if (ci == CRYPT_INVALID)
return -ENODEV;
if (ci < CRYPT_ACTIVE) {
log_err(NULL, _("Device %s is not active.\n"), name);
return -ENODEV;
}
r = dm_query_device(name, &device, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL);
if (r >= 0)
r = crypt_init(cd, device);
free(device);
return r;
}
static int _crypt_format_plain(struct crypt_device *cd,
const char *cipher,
const char *cipher_mode,
const char *uuid,
struct crypt_params_plain *params)
{
if (!cipher || !cipher_mode || !params || !params->hash) {
log_err(cd, _("Invalid plain crypt parameters.\n"));
return -EINVAL;
}
if (cd->volume_key->keyLength > 1024) {
log_err(cd, _("Invalid key size.\n"));
return -EINVAL;
}
cd->plain_cipher = strdup(cipher);
cd->plain_cipher_mode = strdup(cipher_mode);
if (uuid)
cd->plain_uuid = strdup(uuid);
if (params->hash)
cd->plain_hdr.hash = strdup(params->hash);
cd->plain_hdr.offset = params->offset;
cd->plain_hdr.skip = params->skip;
if ((params->hash && !cd->plain_hdr.hash) ||
!cd->plain_cipher || !cd->plain_cipher_mode)
return -ENOMEM;
return 0;
}
static int _crypt_format_luks1(struct crypt_device *cd,
const char *cipher,
const char *cipher_mode,
const char *uuid,
struct crypt_params_luks1 *params)
{
int r;
if (!cd->device) {
log_err(cd, _("Can't format LUKS without device.\n"));
return -EINVAL;
}
r = LUKS_generate_phdr(&cd->hdr, cd->volume_key, cipher, cipher_mode,
(params && params->hash) ? params->hash : "sha1",
uuid, LUKS_STRIPES,
params ? params->data_alignment: DEFAULT_ALIGNMENT,
cd->iteration_time, &cd->PBKDF2_per_sec, cd);
if(r < 0)
return r;
/* Wipe first 8 sectors - fs magic numbers etc. */
r = wipe_device_header(cd->device, 8);
if(r < 0) {
log_err(cd, _("Can't wipe header on device %s.\n"), cd->device);
return r;
}
r = LUKS_write_phdr(cd->device, &cd->hdr, cd);
return r;
}
int crypt_format(struct crypt_device *cd,
const char *type,
const char *cipher,
const char *cipher_mode,
const char *uuid,
const char *volume_key,
size_t volume_key_size,
void *params)
{
int r;
log_dbg("Formatting device %s as type %s.", cd->device ?: "(none)", cd->type ?: "(none)");
if (!type)
return -EINVAL;
if (volume_key)
cd->volume_key = LUKS_alloc_masterkey(volume_key_size,
volume_key);
else
cd->volume_key = LUKS_generate_masterkey(volume_key_size);
if(!cd->volume_key)
return -ENOMEM;
if (isPLAIN(type))
r = _crypt_format_plain(cd, cipher, cipher_mode,
uuid, params);
else if (isLUKS(type))
r = _crypt_format_luks1(cd, cipher, cipher_mode,
uuid, params);
else {
/* FIXME: allow plugins here? */
log_err(cd, _("Unknown crypt device type %s requested.\n"), type);
r = -EINVAL;
}
if (!r && !(cd->type = strdup(type)))
r = -ENOMEM;
if (r < 0) {
LUKS_dealloc_masterkey(cd->volume_key);
cd->volume_key = NULL;
}
return r;
}
int crypt_load(struct crypt_device *cd,
const char *requested_type,
void *params)
{
struct luks_phdr hdr;
int r;
log_dbg("Trying to load %s crypt type from device %s.",
requested_type ?: "any", cd->device ?: "(none)");
if (!cd->device)
return -EINVAL;
if (requested_type && !isLUKS(requested_type))
return -EINVAL;
/* Some hash functions need initialized gcrypt library */
if (init_crypto()) {
log_err(cd, _("Cannot initialize crypto backend.\n"));
return -ENOSYS;
}
r = LUKS_read_phdr(cd->device, &hdr, 1, cd);
if (!r) {
memcpy(&cd->hdr, &hdr, sizeof(hdr));
cd->type = strdup(requested_type);
if (!cd->type)
r = -ENOMEM;
}
return r;
}
int crypt_header_backup(struct crypt_device *cd,
const char *requested_type,
const char *backup_file)
{
if ((requested_type && !isLUKS(requested_type)) || !backup_file)
return -EINVAL;
log_dbg("Requested header backup of device %s (%s) to "
"file %s.", cd->device, requested_type, backup_file);
return LUKS_hdr_backup(backup_file, cd->device, &cd->hdr, cd);
}
int crypt_header_restore(struct crypt_device *cd,
const char *requested_type,
const char *backup_file)
{
if (requested_type && !isLUKS(requested_type))
return -EINVAL;
log_dbg("Requested header restore to device %s (%s) from "
"file %s.", cd->device, requested_type, backup_file);
return LUKS_hdr_restore(backup_file, cd->device, &cd->hdr, cd);
}
void crypt_free(struct crypt_device *cd)
{
if (cd) {
log_dbg("Releasing crypt device %s context.", cd->device);
dm_exit();
if (cd->volume_key)
LUKS_dealloc_masterkey(cd->volume_key);
free(cd->device);
free(cd->type);
/* used in plain device only */
free((char*)cd->plain_hdr.hash);
free(cd->plain_cipher);
free(cd->plain_cipher_mode);
free(cd->plain_uuid);
free(cd);
}
}
int crypt_suspend(struct crypt_device *cd,
const char *name)
{
crypt_status_info ci;
int r, suspended = 0;
log_dbg("Suspending volume %s.", name);
ci = crypt_status(NULL, name);
if (ci < CRYPT_ACTIVE) {
log_err(cd, _("Volume %s is not active.\n"), name);
return -EINVAL;
}
if (!cd && dm_init(NULL, 1) < 0)
return -ENOSYS;
r = dm_query_device(name, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &suspended, NULL);
if (r < 0)
goto out;
if (suspended) {
log_err(cd, _("Volume %s is already suspended.\n"), name);
r = -EINVAL;
goto out;
}
r = dm_suspend_and_wipe_key(name);
if (r)
log_err(cd, "Error during suspending device %s.\n", name);
out:
if (!cd)
dm_exit();
return r;
}
int crypt_resume_by_passphrase(struct crypt_device *cd,
const char *name,
int keyslot,
const char *passphrase,
size_t passphrase_size)
{
struct luks_masterkey *mk = NULL;
int r, suspended = 0;
log_dbg("Resuming volume %s.", name);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
r = -EINVAL;
goto out;
}
r = dm_query_device(name, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &suspended, NULL);
if (r < 0)
return r;
if (!suspended) {
log_err(cd, _("Volume %s is not suspended.\n"), name);
return -EINVAL;
}
if (passphrase) {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
} else
r = volume_key_by_terminal_passphrase(cd, keyslot, &mk);
if (r >= 0) {
keyslot = r;
r = dm_resume_and_reinstate_key(name, mk->keyLength, mk->key);
if (r)
log_err(cd, "Error during resuming device %s.\n", name);
} else
r = keyslot;
out:
LUKS_dealloc_masterkey(mk);
return r < 0 ? r : keyslot;
}
int crypt_resume_by_keyfile(struct crypt_device *cd,
const char *name,
int keyslot,
const char *keyfile,
size_t keyfile_size)
{
struct luks_masterkey *mk = NULL;
char *passphrase_read = NULL;
unsigned int passphrase_size_read;
int r, suspended = 0;
log_dbg("Resuming volume %s.", name);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
r = -EINVAL;
goto out;
}
r = dm_query_device(name, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, &suspended, NULL);
if (r < 0)
return r;
if (!suspended) {
log_err(cd, _("Volume %s is not suspended.\n"), name);
return -EINVAL;
}
if (!keyfile)
return -EINVAL;
key_from_file(cd, _("Enter passphrase: "), &passphrase_read,
&passphrase_size_read, keyfile, keyfile_size);
if(!passphrase_read)
r = -EINVAL;
else {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase_read,
passphrase_size_read, &cd->hdr, &mk, cd);
safe_free(passphrase_read);
}
if (r >= 0) {
keyslot = r;
r = dm_resume_and_reinstate_key(name, mk->keyLength, mk->key);
if (r)
log_err(cd, "Error during resuming device %s.\n", name);
} else
r = keyslot;
out:
LUKS_dealloc_masterkey(mk);
return r < 0 ? r : keyslot;
}
// slot manipulation
int crypt_keyslot_add_by_passphrase(struct crypt_device *cd,
int keyslot, // -1 any
const char *passphrase, // NULL -> terminal
size_t passphrase_size,
const char *new_passphrase, // NULL -> terminal
size_t new_passphrase_size)
{
struct luks_masterkey *mk = NULL;
char *password = NULL, *new_password = NULL;
unsigned int passwordLen, new_passwordLen;
int r;
log_dbg("Adding new keyslot, existing passphrase %sprovided,"
"new passphrase %sprovided.",
passphrase ? "" : "not ", new_passphrase ? "" : "not ");
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
r = keyslot_verify_or_find_empty(cd, &keyslot);
if (r)
return r;
if (!LUKS_keyslot_active_count(&cd->hdr)) {
/* No slots used, try to use pre-generated key in header */
if (cd->volume_key) {
mk = LUKS_alloc_masterkey(cd->volume_key->keyLength, cd->volume_key->key);
r = mk ? 0 : -ENOMEM;
} else {
log_err(cd, _("Cannot add key slot, all slots disabled and no volume key provided.\n"));
return -EINVAL;
}
} else if (passphrase) {
/* Passphrase provided, use it to unlock existing keyslot */
r = LUKS_open_key_with_hdr(cd->device, CRYPT_ANY_SLOT, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
} else {
/* Passphrase not provided, ask first and use it to unlock existing keyslot */
key_from_terminal(cd, _("Enter any passphrase: "),
&password, &passwordLen, 0);
if (!password) {
r = -EINVAL;
goto out;
}
r = LUKS_open_key_with_hdr(cd->device, CRYPT_ANY_SLOT, password,
passwordLen, &cd->hdr, &mk, cd);
safe_free(password);
}
if(r < 0)
goto out;
if (new_passphrase) {
new_password = (char *)new_passphrase;
new_passwordLen = new_passphrase_size;
} else {
key_from_terminal(cd, _("Enter new passphrase for key slot: "),
&new_password, &new_passwordLen, 1);
if(!new_password) {
r = -EINVAL;
goto out;
}
}
r = LUKS_set_key(cd->device, keyslot, new_password, new_passwordLen,
&cd->hdr, mk, cd->iteration_time, &cd->PBKDF2_per_sec, cd);
if(r < 0) goto out;
r = 0;
out:
if (!new_passphrase)
safe_free(new_password);
LUKS_dealloc_masterkey(mk);
return r ?: keyslot;
}
int crypt_keyslot_add_by_keyfile(struct crypt_device *cd,
int keyslot,
const char *keyfile,
size_t keyfile_size,
const char *new_keyfile,
size_t new_keyfile_size)
{
struct luks_masterkey *mk=NULL;
char *password=NULL; unsigned int passwordLen;
char *new_password = NULL; unsigned int new_passwordLen;
int r;
log_dbg("Adding new keyslot, existing keyfile %s, new keyfile %s.",
keyfile ?: "[none]", new_keyfile ?: "[none]");
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
r = keyslot_verify_or_find_empty(cd, &keyslot);
if (r)
return r;
if (!LUKS_keyslot_active_count(&cd->hdr)) {
/* No slots used, try to use pre-generated key in header */
if (cd->volume_key) {
mk = LUKS_alloc_masterkey(cd->volume_key->keyLength, cd->volume_key->key);
r = mk ? 0 : -ENOMEM;
} else {
log_err(cd, _("Cannot add key slot, all slots disabled and no volume key provided.\n"));
return -EINVAL;
}
} else {
/* Read password from file of (if NULL) from terminal */
if (keyfile)
key_from_file(cd, _("Enter any passphrase: "), &password, &passwordLen,
keyfile, keyfile_size);
else
key_from_terminal(cd, _("Enter any passphrase: "),
&password, &passwordLen, 1);
if (!password)
return -EINVAL;
r = LUKS_open_key_with_hdr(cd->device, CRYPT_ANY_SLOT, password, passwordLen,
&cd->hdr, &mk, cd);
safe_free(password);
}
if(r < 0)
goto out;
if (new_keyfile)
key_from_file(cd, _("Enter new passphrase for key slot: "),
&new_password, &new_passwordLen, new_keyfile,
new_keyfile_size);
else
key_from_terminal(cd, _("Enter new passphrase for key slot: "),
&new_password, &new_passwordLen, 1);
if(!new_password) {
r = -EINVAL;
goto out;
}
r = LUKS_set_key(cd->device, keyslot, new_password, new_passwordLen,
&cd->hdr, mk, cd->iteration_time, &cd->PBKDF2_per_sec, cd);
out:
safe_free(new_password);
LUKS_dealloc_masterkey(mk);
return r < 0 ? r : keyslot;
}
int crypt_keyslot_add_by_volume_key(struct crypt_device *cd,
int keyslot,
const char *volume_key,
size_t volume_key_size,
const char *passphrase,
size_t passphrase_size)
{
struct luks_masterkey *mk = NULL;
int r = -EINVAL;
char *new_password = NULL; unsigned int new_passwordLen;
log_dbg("Adding new keyslot %d using volume key.", keyslot);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
if (volume_key)
mk = LUKS_alloc_masterkey(volume_key_size, volume_key);
else if (cd->volume_key)
mk = LUKS_alloc_masterkey(cd->volume_key->keyLength, cd->volume_key->key);
if (!mk)
return -ENOMEM;
r = LUKS_verify_master_key(&cd->hdr, mk);
if (r < 0) {
log_err(cd, _("Volume key does not match the volume.\n"));
goto out;
}
r = keyslot_verify_or_find_empty(cd, &keyslot);
if (r)
goto out;
if (!passphrase) {
key_from_terminal(cd, _("Enter new passphrase for key slot: "),
&new_password, &new_passwordLen, 1);
passphrase = new_password;
passphrase_size = new_passwordLen;
}
r = LUKS_set_key(cd->device, keyslot, passphrase, passphrase_size,
&cd->hdr, mk, cd->iteration_time, &cd->PBKDF2_per_sec, cd);
out:
if (new_password)
safe_free(new_password);
LUKS_dealloc_masterkey(mk);
return r ?: keyslot;
}
int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot)
{
crypt_keyslot_info ki;
log_dbg("Destroying keyslot %d.", keyslot);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
ki = crypt_keyslot_status(cd, keyslot);
if (ki == CRYPT_SLOT_INVALID) {
log_err(cd, _("Key slot %d is invalid.\n"), keyslot);
return -EINVAL;
}
if (ki == CRYPT_SLOT_INACTIVE) {
log_err(cd, _("Key slot %d is not used.\n"), keyslot);
return -EINVAL;
}
return LUKS_del_key(cd->device, keyslot, &cd->hdr, cd);
}
// activation/deactivation of device mapping
int crypt_activate_by_passphrase(struct crypt_device *cd,
const char *name,
int keyslot,
const char *passphrase,
size_t passphrase_size,
uint32_t flags)
{
crypt_status_info ci;
struct luks_masterkey *mk = NULL;
char *prompt = NULL;
int r;
log_dbg("%s volume %s [keyslot %d] using %spassphrase.",
name ? "Activating" : "Checking", name ?: "",
keyslot, passphrase ? "" : "[none] ");
if (!name)
return -EINVAL;
/* plain, use hashed passphrase */
if (isPLAIN(cd->type))
return create_device_helper(cd, name, cd->plain_hdr.hash,
cd->plain_cipher, cd->plain_cipher_mode, NULL, passphrase, passphrase_size,
cd->volume_key->keyLength, 0, cd->plain_hdr.skip,
cd->plain_hdr.offset, cd->plain_uuid, flags & CRYPT_ACTIVATE_READONLY, 0, 0);
if (name) {
ci = crypt_status(NULL, name);
if (ci == CRYPT_INVALID)
return -EINVAL;
else if (ci >= CRYPT_ACTIVE) {
log_err(cd, _("Device %s already exists.\n"), name);
return -EEXIST;
}
}
if(asprintf(&prompt, _("Enter passphrase for %s: "), cd->device) < 0)
return -ENOMEM;
/* provided passphrase, do not retry */
if (passphrase) {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
} else
r = volume_key_by_terminal_passphrase(cd, keyslot, &mk);
if (r >= 0) {
keyslot = r;
if (name)
r = open_from_hdr_and_mk(cd, mk, name, flags);
}
LUKS_dealloc_masterkey(mk);
free(prompt);
return r < 0 ? r : keyslot;
}
int crypt_activate_by_keyfile(struct crypt_device *cd,
const char *name,
int keyslot,
const char *keyfile,
size_t keyfile_size,
uint32_t flags)
{
crypt_status_info ci;
struct luks_masterkey *mk = NULL;
char *passphrase_read = NULL;
unsigned int passphrase_size_read;
int r;
log_dbg("Activating volume %s [keyslot %d] using keyfile %s.",
name, keyslot, keyfile ?: "[none]");
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
if (name) {
ci = crypt_status(NULL, name);
if (ci == CRYPT_INVALID)
return -EINVAL;
else if (ci >= CRYPT_ACTIVE) {
log_err(cd, _("Device %s already exists.\n"), name);
return -EEXIST;
}
}
if (!keyfile)
return -EINVAL;
key_from_file(cd, _("Enter passphrase: "), &passphrase_read,
&passphrase_size_read, keyfile, keyfile_size);
if(!passphrase_read)
r = -EINVAL;
else {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase_read,
passphrase_size_read, &cd->hdr, &mk, cd);
safe_free(passphrase_read);
}
if (r >= 0) {
keyslot = r;
r = open_from_hdr_and_mk(cd, mk, name, flags);
}
LUKS_dealloc_masterkey(mk);
return r < 0 ? r : keyslot;
}
int crypt_activate_by_volume_key(struct crypt_device *cd,
const char *name,
const char *volume_key,
size_t volume_key_size,
uint32_t flags)
{
crypt_status_info ci;
struct luks_masterkey *mk;
int r;
log_dbg("Activating volume %s by volume key.", name);
/* use key directly, no hash */
if (isPLAIN(cd->type))
return create_device_helper(cd, name, NULL,
cd->plain_cipher, cd->plain_cipher_mode, NULL, volume_key, volume_key_size,
cd->volume_key->keyLength, 0, cd->plain_hdr.skip,
cd->plain_hdr.offset, cd->plain_uuid, flags & CRYPT_ACTIVATE_READONLY, 0, 0);
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
if (name) {
ci = crypt_status(NULL, name);
if (ci == CRYPT_INVALID)
return -EINVAL;
else if (ci >= CRYPT_ACTIVE) {
log_err(cd, _("Device %s already exists.\n"), name);
return -EEXIST;
}
}
mk = LUKS_alloc_masterkey(volume_key_size, volume_key);
if (!mk)
return -ENOMEM;
r = LUKS_verify_master_key(&cd->hdr, mk);
if (r == -EPERM)
log_err(cd, _("Volume key does not match the volume.\n"));
if (!r && name)
r = open_from_hdr_and_mk(cd, mk, name, flags);
LUKS_dealloc_masterkey(mk);
return r;
}
int crypt_deactivate(struct crypt_device *cd, const char *name)
{
int r;
if (!name)
return -EINVAL;
log_dbg("Deactivating volume %s.", name);
if (!cd && dm_init(NULL, 1) < 0)
return -ENOSYS;
switch (crypt_status(cd, name)) {
case CRYPT_ACTIVE:
r = dm_remove_device(name, 0, 0);
break;
case CRYPT_BUSY:
log_err(cd, _("Device %s is busy.\n"), name);
r = -EBUSY;
break;
case CRYPT_INACTIVE:
log_err(cd, _("Device %s is not active.\n"), name);
r = -ENODEV;
break;
default:
log_err(cd, _("Invalid device %s.\n"), name);
r = -EINVAL;
}
if (!cd)
dm_exit();
return r;
}
// misc helper functions
int crypt_volume_key_get(struct crypt_device *cd,
int keyslot,
char *volume_key,
size_t *volume_key_size,
const char *passphrase,
size_t passphrase_size)
{
struct luks_masterkey *mk;
char *processed_key = NULL;
int r, key_len;
key_len = crypt_get_volume_key_size(cd);
if (key_len > *volume_key_size) {
log_err(cd, _("Volume key buffer too small.\n"));
return -ENOMEM;
}
if (isPLAIN(cd->type)) {
processed_key = process_key(cd, cd->plain_hdr.hash, NULL, key_len,
passphrase, passphrase_size);
if (!processed_key) {
log_err(cd, _("Cannot retrieve volume key for plain device.\n"));
return -EINVAL;
}
memcpy(volume_key, processed_key, key_len);
*volume_key_size = key_len;
safe_free(processed_key);
return 0;
}
if (isLUKS(cd->type)) {
r = LUKS_open_key_with_hdr(cd->device, keyslot, passphrase,
passphrase_size, &cd->hdr, &mk, cd);
if (r >= 0) {
memcpy(volume_key, mk->key, mk->keyLength);
*volume_key_size = mk->keyLength;
}
LUKS_dealloc_masterkey(mk);
return r;
}
log_err(cd, _("This operation is not supported for %s crypt device.\n"), cd->type ?: "(none)");
return -EINVAL;
}
int crypt_volume_key_verify(struct crypt_device *cd,
const char *volume_key,
size_t volume_key_size)
{
struct luks_masterkey *mk;
int r;
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
mk = LUKS_alloc_masterkey(volume_key_size, volume_key);
if (!mk)
return -ENOMEM;
r = LUKS_verify_master_key(&cd->hdr, mk);
if (r == -EPERM)
log_err(cd, _("Volume key does not match the volume.\n"));
LUKS_dealloc_masterkey(mk);
return r;
}
void crypt_set_timeout(struct crypt_device *cd, uint64_t timeout_sec)
{
log_dbg("Timeout set to %" PRIu64 " miliseconds.", timeout_sec);
cd->timeout = timeout_sec;
}
void crypt_set_password_retry(struct crypt_device *cd, int tries)
{
log_dbg("Password retry count set to %d.", tries);
cd->tries = tries;
}
void crypt_set_iterarion_time(struct crypt_device *cd, uint64_t iteration_time_ms)
{
log_dbg("Iteration time set to %" PRIu64 " miliseconds.", iteration_time_ms);
cd->iteration_time = iteration_time_ms;
}
void crypt_set_password_verify(struct crypt_device *cd, int password_verify)
{
log_dbg("Password verification %s.", password_verify ? "enabled" : "disabled");
cd->password_verify = password_verify ? 1 : 0;
}
int crypt_memory_lock(struct crypt_device *cd, int lock)
{
return lock ? crypt_memlock_inc(cd) : crypt_memlock_dec(cd);
}
// reporting
crypt_status_info crypt_status(struct crypt_device *cd, const char *name)
{
int r;
if (!cd && dm_init(NULL, 1) < 0)
return CRYPT_INVALID;
r = dm_status_device(name);
if (!cd)
dm_exit();
if (r < 0 && r != -ENODEV)
return CRYPT_INVALID;
if (r == 0)
return CRYPT_ACTIVE;
if (r > 0)
return CRYPT_BUSY;
return CRYPT_INACTIVE;
}
static void hexprintICB(struct crypt_device *cd, char *d, int n)
{
int i;
for(i = 0; i < n; i++)
log_std(cd, "%02hhx ", (char)d[i]);
}
int crypt_dump(struct crypt_device *cd)
{
int i;
if (!isLUKS(cd->type)) { //FIXME
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return -EINVAL;
}
log_std(cd, "LUKS header information for %s\n\n", cd->device);
log_std(cd, "Version: \t%d\n", cd->hdr.version);
log_std(cd, "Cipher name: \t%s\n", cd->hdr.cipherName);
log_std(cd, "Cipher mode: \t%s\n", cd->hdr.cipherMode);
log_std(cd, "Hash spec: \t%s\n", cd->hdr.hashSpec);
log_std(cd, "Payload offset:\t%d\n", cd->hdr.payloadOffset);
log_std(cd, "MK bits: \t%d\n", cd->hdr.keyBytes * 8);
log_std(cd, "MK digest: \t");
hexprintICB(cd, cd->hdr.mkDigest, LUKS_DIGESTSIZE);
log_std(cd, "\n");
log_std(cd, "MK salt: \t");
hexprintICB(cd, cd->hdr.mkDigestSalt, LUKS_SALTSIZE/2);
log_std(cd, "\n \t");
hexprintICB(cd, cd->hdr.mkDigestSalt+LUKS_SALTSIZE/2, LUKS_SALTSIZE/2);
log_std(cd, "\n");
log_std(cd, "MK iterations: \t%d\n", cd->hdr.mkDigestIterations);
log_std(cd, "UUID: \t%s\n\n", cd->hdr.uuid);
for(i = 0; i < LUKS_NUMKEYS; i++) {
if(cd->hdr.keyblock[i].active == LUKS_KEY_ENABLED) {
log_std(cd, "Key Slot %d: ENABLED\n",i);
log_std(cd, "\tIterations: \t%d\n",
cd->hdr.keyblock[i].passwordIterations);
log_std(cd, "\tSalt: \t");
hexprintICB(cd, cd->hdr.keyblock[i].passwordSalt,
LUKS_SALTSIZE/2);
log_std(cd, "\n\t \t");
hexprintICB(cd, cd->hdr.keyblock[i].passwordSalt +
LUKS_SALTSIZE/2, LUKS_SALTSIZE/2);
log_std(cd, "\n");
log_std(cd, "\tKey material offset:\t%d\n",
cd->hdr.keyblock[i].keyMaterialOffset);
log_std(cd, "\tAF stripes: \t%d\n",
cd->hdr.keyblock[i].stripes);
}
else
log_std(cd, "Key Slot %d: DISABLED\n", i);
}
return 0;
}
const char *crypt_get_cipher(struct crypt_device *cd)
{
if (isPLAIN(cd->type))
return cd->plain_cipher;
if (isLUKS(cd->type))
return cd->hdr.cipherName;
return NULL;
}
const char *crypt_get_cipher_mode(struct crypt_device *cd)
{
if (isPLAIN(cd->type))
return cd->plain_cipher_mode;
if (isLUKS(cd->type))
return cd->hdr.cipherMode;
return NULL;
}
const char *crypt_get_uuid(struct crypt_device *cd)
{
if (isLUKS(cd->type))
return cd->hdr.uuid;
return NULL;
}
int crypt_get_volume_key_size(struct crypt_device *cd)
{
if (isPLAIN(cd->type))
return cd->volume_key->keyLength;
if (isLUKS(cd->type))
return cd->hdr.keyBytes;
return 0;
}
uint64_t crypt_get_data_offset(struct crypt_device *cd)
{
if (isPLAIN(cd->type))
return cd->plain_hdr.offset;
if (isLUKS(cd->type))
return cd->hdr.payloadOffset;
return 0;
}
crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot)
{
if (!isLUKS(cd->type)) {
log_err(cd, _("This operation is supported only for LUKS device.\n"));
return CRYPT_SLOT_INVALID;
}
return LUKS_keyslot_info(&cd->hdr, keyslot);
}