From d70782478e6645efb2d76fd0b8283292d67e1ca0 Mon Sep 17 00:00:00 2001 From: Milan Broz Date: Sun, 30 Aug 2009 18:09:53 +0000 Subject: [PATCH] * Add new libcryptsetup API (documented in libcryptsetup.h). Signed-off-by: Milan Broz git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@96 36d66b0a-2a48-0410-832c-cd162a569da5 --- ChangeLog | 1 + lib/libcryptsetup.h | 408 ++++++++++++++++++- lib/setup.c | 1080 ++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1470 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9839d3b..961a612 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,7 @@ * Add LUKS function to verify master key digest. * Move key slot manuipulation function into LUKS specific code. * Replace global options struct with separate parameters in helper functions. + * Add new libcryptsetup API (documented in libcryptsetup.h). 2009-08-17 Milan Broz * Fix PBKDF2 speed calculation for large passhrases. diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 7602136..daf425c 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -8,13 +8,411 @@ extern "C" { struct crypt_device; /* crypt device handle */ -#define CRYPT_ANY_SLOT -1 - -typedef enum { SLOT_INVALID, SLOT_INACTIVE, SLOT_ACTIVE, SLOT_ACTIVE_LAST } crypt_keyslot_info; +/** + * Initialise crypt device handle and check if provided device exists. + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - returns pointer to crypt device handle + * + * Note that logging is not initialized here, possible messages uses + * default log function. + */ +int crypt_init(struct crypt_device **cd, const char *device); +/** + * Set log function. + * + * @cd - crypt device handle + * @usrptr - provided identification in callback + * @class - log type below (debug messages can uses other levels) + * @msg - log message + */ #define CRYPT_LOG_NORMAL 0 #define CRYPT_LOG_ERROR 1 -#define CRYPT_LOG_DEBUG -1 /* always on stdout */ +#define CRYPT_LOG_DEBUG -1 /* always on stdout */ +void crypt_set_log_callback(struct crypt_device *cd, + void (*log)(int class, const char *msg, void *usrptr), + void *usrptr); + +/** + * Set confirmation callback (yes/no) + * + * If code need confirmation (like deleting last key slot) this function + * is called. If not defined, everything is confirmed. + * + * Calback should return 0 if operation is declined, other values mean accepted. + * + * @cd - crypt device handle + * @usrptr - provided identification in callback + * @msg - Message for user to confirm + */ +void crypt_set_confirm_callback(struct crypt_device *cd, + int (*confirm)(const char *msg, void *usrptr), + void *usrptr); + +/** + * Set password query callback. + * + * If code need _interactive_ query for password, this callback is called. + * If not defined, compiled-in default is called (uses terminal input). + * + * @cd - crypt device handle + * @usrptr - provided identification in callback + * @msg - Message for user + * @buf - buffer for password + * @length - size of buffer + * + * - Note that if this function is defined, verify option is ignored + * (caller whch provided callback is responsible fo password verification) + * - Only zero terminated passwords can be enteted this way, for complex + * API functions directly. + * - Maximal length of password is limited to @length-1 (minimal 511 chars) + */ +void crypt_set_password_callback(struct crypt_device *cd, + int (*password)(const char *msg, char *buf, size_t length, void *usrptr), + void *usrptr); + +/** + * Various crypt device parameters + * + * @cd - crypt device handle + * @timeout - timeout in secons for password entry if compiled-in function used + * @password_retry - number of tries for password if not verified + * @iteration_time - iteration time for LUKS header in miliseconds + * @password_verify - for compiled-in password query always verify passwords twice + */ +void crypt_set_timeout(struct crypt_device *cd, uint64_t timeout_sec); +void crypt_set_password_retry(struct crypt_device *cd, int tries); +void crypt_set_iterarion_time(struct crypt_device *cd, uint64_t iteration_time_ms); +void crypt_set_password_verify(struct crypt_device *cd, int password_verify); + +/** + * Helper to lock/unlock memory to avoid swapping sesitive data_alignment + * + * @cd - crypt device handle, can be NULL + * @lock - 0 to unloct otherwise lock memory + * + * Return value indicated that memory is locked (function can be called multiple times). + * Only root can do this. Note it locks/unlocks all process memory, not only crypt context. + */ +int crypt_memory_lock(struct crypt_device *cd, int lock); + +#define CRYPT_PLAIN "PLAIN" /* regular crypt device, no on-disk header */ +#define CRYPT_LUKS1 "LUKS1" /* LUKS version 1 header on-disk */ + +struct crypt_params_plain { + const char *hash; /* password hash function */ + uint64_t offset; /* offset in sectors */ + uint64_t skip; /* IV initilisation sector */ +}; + +struct crypt_params_luks1 { + const char *hash; /* hash used in LUKS header */ + size_t data_alignment; /* in sectors, data offset is multiple of this */ +}; + +/** + * Create (format) new crypt device (and possible header on-disk) but not activates it. + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @type - type of device (optional params struct must be of this type) + * @cipher - (e.g. "aes") + * @cipher_mode - including IV specification (e.g. "xts-plain") + * @uuid - requested UUID or NULL if it should be generated + * @volume_key - pre-generated volume key or NULL if it should be generated (only for LUKS) + * @volume_key_size - size og volume key in bytes. + * @params - crypt type specific parameters + * + * Note that crypt_format do not enable any keyslot, but it stores volume key internally + * and subsequent crypt_keyslot_add_* calls can be used. + * (It is the only situation when crypt_keyslot_add_* do not require active key slots.) + */ +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); + +/** + * Load crypt device parameters from on-disk header + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @requested_type - use NULL for all known + * @params - crypt type specific parameters + */ +int crypt_load(struct crypt_device *cd, + const char *requested_type, + void *params); + +/** + * Releases crypt device context and used memory. + * + * @cd - crypt device handle + */ +void crypt_free(struct crypt_device *cd); + +/** + * Add key slot using provided passphrase + * + * Returns allocated key slot number or negative errno otherwise. + * + * @cd - crypt device handle + * @keyslot - requested keyslot or CRYPT_ANY_SLOT + * @passphrase - passphrase used to unlock volume key, NULL for query + * @passphrase_size - size of @passphrase (binary data) + * @new_passphrase - passphrase for new keyslot, NULL for query + * @new_passphrase_size - size of @new_passphrase (binary data) + */ +#define CRYPT_ANY_SLOT -1 +int crypt_keyslot_add_by_passphrase(struct crypt_device *cd, + int keyslot, + const char *passphrase, + size_t passphrase_size, + const char *new_passphrase, + size_t new_passphrase_size); + +/** +* Add key slot using provided key file path + * + * Returns allocated key slot number or negative errno otherwise. + * + * @cd - crypt device handle + * @keyslot - requested keyslot or CRYPT_ANY_SLOT + * @keyfile - key file used to unlock volume key, NULL for passphrase query + * @keyfile_size - number of bytes to read from @keyfile, 0 is unlimited + * @new_keyfile - keyfile for new keyslot, NULL for passphrase query + * @new_keyfile_size - number of bytes to read from @new_keyfile, 0 is unlimited + * + * Note that @keyfile can be "-" for STDIN + */ +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); + +/** + * Add key slot using provided volume key + * + * Returns allocated key slot number or negative errno otherwise. + * + * @cd - crypt device handle + * @keyslot - requested keyslot or CRYPT_ANY_SLOT + * @volume_key - provided volume key or NULL if used after crypt_format + * @volume_key_size - size of @volume_key + * @passphrase - passphrase for new keyslot, NULL for query + * @passphrase_size - size of @passphrase + */ +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); + +/** + * Destroy (and disable) key slot + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @keyslot - requested key slot to destroy + * + * Note that there is no passphrase verification used. + */ +int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot); + +/** + * Activation flags + */ +#define CRYPT_ACTIVATE_READONLY (1 << 0) +#define CRYPT_ACTIVATE_NO_UUID (1 << 1) + +/** + * Activate device or check passphrase + * + * Returns unlocked key slot number or negative errno otherwise. + * + * @cd - crypt device handle + * @name - name of device to create, if NULL only check passphrase + * @keyslot - requested keyslot to check or CRYPT_ANY_SLOT + * @passphrase - passphrase used to unlock volume key, NULL for query + * @passphrase_size - size of @passphrase + * @flags - activation flags + */ +int crypt_activate_by_passphrase(struct crypt_device *cd, + const char *name, + int keyslot, + const char *passphrase, + size_t passphrase_size, + uint32_t flags); + +/** + * Activate device or check using key file + * + * Returns unlocked key slot number or negative errno otherwise. + * + * @cd - crypt device handle + * @name - name of device to create, if NULL only check keyfile + * @keyslot - requested keyslot to check or CRYPT_ANY_SLOT + * @keyfile - key file used to unlock volume key + * @keyfile_size - number of bytes to read from @keyfile, 0 is unlimited + * @flags - activation flags + */ +int crypt_activate_by_keyfile(struct crypt_device *cd, + const char *name, + int keyslot, + const char *keyfile, + size_t keyfile_size, + uint32_t flags); + +/** + * Activate device using provided volume key + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @name - name of device to create, if NULL only check volume key + * @volume_key - provided volume key + * @volume_key_size - size of @volume_key + * @flags - activation flags + */ +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); + +/** + * Deactivate crypt device + * + * @cd - crypt device handle, can be NULL + * @name - name of device to deactivate + */ +int crypt_deactivate(struct crypt_device *cd, const char *name); + +/** + * Get volume key from of crypt device + * + * Returns unlocked key slot number or negative errno otherwise. + * + * @cd - crypt device handle + * @keyslot - use this keyslot or CRYPT_ANY_SLOT + * @volume_key - buffer for volume key + * @volume_key_size - on input, size of buffer @volume_key, + * on output size of @volume_key + * @passphrase - passphrase used to unlock volume key, NULL for query + * @passphrase_size - size of @passphrase + */ +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); + +/** + * Verify that provided volume key is valid for crypt device + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle + * @volume_key - provided volume key + * @volume_key_size - size of @volume_key + */ +int crypt_volume_key_verify(struct crypt_device *cd, + const char *volume_key, + size_t volume_key_size); + +/** + * Get status info about device name + * + * Returns value defined by crypt_status_info. + * + * @cd - crypt device handle, can be NULL + * @name -crypt device name + * + * INACTIVE - no such mapped device + * ACTIVE - device is active + * BUSY - device is active and has open count > 0 + */ +typedef enum { INVALID, INACTIVE, ACTIVE, BUSY } crypt_status_info; +crypt_status_info crypt_status(struct crypt_device *cd, const char *name); + +/** + * Dump text-formatted information about crypt device to log output + * + * Returns 0 on success or negative errno value otherwise. + * + * @cd - crypt device handle, can be NULL + */ +int crypt_dump(struct crypt_device *cd); + +/** + * Various crypt device info functions + * + * @cd - crypt device handle + * + * cipher - used cipher, e.g. "aes" or NULL otherwise + * cipher_mode - used cipher mode including IV, e.g. "xts-plain" or NULL otherwise + * uuid - device UUID or NULL if not set + * data_offset - device offset in sectors where real data starts on underlying device) + * volume_key_size - size (in bytes) of volume key for crypt device + */ +const char *crypt_get_cipher(struct crypt_device *cd); +const char *crypt_get_cipher_mode(struct crypt_device *cd); +const char *crypt_get_uuid(struct crypt_device *cd); +uint64_t crypt_get_data_offset(struct crypt_device *cd); +int crypt_get_volume_key_size(struct crypt_device *cd); + +/** + * Get information about particular key slot + * + * Returns value defined by crypt_keyslot_info. + * + * @cd - crypt device handle + * @keyslot - requested keyslot to check or CRYPT_ANY_SLOT + */ +typedef enum { SLOT_INVALID, SLOT_INACTIVE, SLOT_ACTIVE, SLOT_ACTIVE_LAST } crypt_keyslot_info; +crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot); + +/** + * Receives last reported error + * + * @buf - buffef for message + * @size - size of buffer + * + * Note that this is old API function using global context. + * All error messages are reported also through log callback. + */ +void crypt_get_error(char *buf, size_t size); + +/** + * Get directory where mapped crypt devices are created + */ +const char *crypt_get_dir(void); + +/** + * Set library debug level + */ +#define CRYPT_DEBUG_ALL -1 +#define CRYPT_DEBUG_NONE 0 +void crypt_set_debug_level(int level); + +/** + * OLD DEPRECATED API ********************************** + * + * Provided only for backward compatibility. + */ struct interface_callbacks { int (*yesDialog)(char *msg); @@ -69,9 +467,7 @@ int crypt_luksUUID(struct crypt_options *options); int crypt_isLuks(struct crypt_options *options); int crypt_luksDump(struct crypt_options *options); -void crypt_get_error(char *buf, size_t size); void crypt_put_options(struct crypt_options *options); -const char *crypt_get_dir(void); #ifdef __cplusplus } diff --git a/lib/setup.c b/lib/setup.c index 2f97f60..8f3e4a0 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -10,9 +10,32 @@ #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 */ @@ -56,13 +79,6 @@ void logger(struct crypt_device *cd, int class, const char *file, free(target); } -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]); -} - /* * Password processing behaviour matrix of process_key * @@ -129,7 +145,43 @@ int parse_into_name_and_mode(const char *nameAndMode, char *name, char *mode) #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 SLOT_INVALID: + log_err(cd, _("Key slot %d is invalid, please select between 0 and %d.\n"), + *keyslot, LUKS_NUMKEYS - 1); + return -EINVAL; + case SLOT_INACTIVE: + break; + default: + log_err(cd, _("Key slot %d is full, please select another one.\n"), + *keyslot); + return -EINVAL; + } + + return 0; +} + static int keyslot_is_valid(struct crypt_device *cd, int keySlotIndex) { if(keySlotIndex >= LUKS_NUMKEYS || keySlotIndex < 0) { @@ -198,7 +250,7 @@ static int device_check_and_adjust(struct crypt_device *cd, return 0; } -static int create_device_helper(int reload, struct crypt_options *options) +static int create_device_helper_old(int reload, struct crypt_options *options) { struct crypt_device *cd = NULL; char *key = NULL; @@ -253,7 +305,7 @@ static int create_device_helper(int reload, struct crypt_options *options) return r; } -static int luks_remove_helper(struct crypt_device *cd, +static int luks_remove_helper_old(struct crypt_device *cd, struct crypt_options *options, int supply_it) { struct luks_masterkey *mk; @@ -341,18 +393,158 @@ out: return r; } +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 == INVALID) + return -EINVAL; + + if (reload && ci < ACTIVE) + return -EINVAL; + + if (!reload && ci >= 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, 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, no_uuid ? NULL : crypt_get_uuid(cd), + size, 0, offset, mk->keyLength, mk->key, + read_only, 0); + free(cipher); + return r; +} + +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 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); +} + +void crypt_set_log_callback(struct crypt_device *cd, + void (*log)(int class, const char *msg, void *usrptr), + void *usrptr) +{ + 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) { - return create_device_helper(0, options); + return create_device_helper_old(0, options); } /* OPTIONS: same as create above */ int crypt_update_device(struct crypt_options *options) { - return create_device_helper(1, options); + return create_device_helper_old(1, options); } /* OPTIONS: name, size, icb */ @@ -602,13 +794,13 @@ start: /* OPTIONS: device, keys_slot, key_file, timeout, flags, icb */ int crypt_luksKillSlot(struct crypt_options *options) { - return luks_remove_helper(NULL, options, 0); + return luks_remove_helper_old(NULL, options, 0); } /* OPTIONS: device, new_key_file, key_file, timeout, flags, icb */ int crypt_luksRemoveKey(struct crypt_options *options) { - return luks_remove_helper(NULL, options, 1); + return luks_remove_helper_old(NULL, options, 1); } /* OPTIONS: device, new_key_file, key_file, key_slot, flags, @@ -699,6 +891,7 @@ int crypt_isLuks(struct crypt_options *options) } /* OPTIONS: device, icb */ +static void hexprintICB(struct crypt_device *cd, char *d, int n); int crypt_luksDump(struct crypt_options *options) { struct crypt_device *cd = NULL; @@ -781,3 +974,864 @@ 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_ready(NULL, device, 0)) + return -ENOTBLK; + + if (!(h = malloc(sizeof(struct crypt_device)))) + return -ENOMEM; + + memset(h, 0, sizeof(*h)); + + h->device = strdup(device); + if (!h->device) { + free(h); + return -ENOMEM; + } + + 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; +} + +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; + + 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); + 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, 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, _("Unkown crypt device type %s requesed.\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); + + if (requested_type && !isPLAIN(requested_type) && !isLUKS(requested_type)) + return -EINVAL; + + r = LUKS_read_phdr(cd->device, &hdr, 0, cd); + + if (!r) { + memcpy(&cd->hdr, &hdr, sizeof(hdr)); + cd->type = strdup(requested_type); + if (!cd->type) + r = -ENOMEM; + } + + return r; +} + +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); + } +} + +// 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; + 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 == SLOT_INVALID) + return -EINVAL; + + if (ki == SLOT_INACTIVE) + return 0; + + return LUKS_del_key(cd->device, keyslot, 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, *passphrase_read = NULL; + unsigned int passphrase_size_read; + int r, tries = cd->tries; + + 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 == INVALID) + return -EINVAL; + else if (ci >= 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 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) { + 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 == INVALID) + return -EINVAL; + else if (ci >= 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 == INVALID) + return -EINVAL; + else if (ci >= 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) +{ + if (!name) + return -EINVAL; + + log_dbg("Deactivating volume %s.", name); + + switch (crypt_status(cd, name)) { + case ACTIVE: return dm_remove_device(name, 0, 0); + case BUSY: log_err(cd, _("Device %s is busy."), name); + return -EBUSY; + case INACTIVE: return -ENODEV; + default: log_err(cd, _("Invalid device %s."), name); + return -EINVAL; + } +} + +// 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 ? memlock_inc(cd) : memlock_dec(cd); +} + +// reporting +crypt_status_info crypt_status(struct crypt_device *cd, const char *name) +{ + int r; + + r = dm_status_device(name); + + if (r < 0 && r != -ENODEV) + return INVALID; + + if (r == 0) + return ACTIVE; + + if (r > 0) + return BUSY; + + return 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 SLOT_INVALID; + } + + return LUKS_keyslot_info(&cd->hdr, keyslot); +} -- 2.7.4