mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-05 16:00:05 +01:00
Add libLUKS2.
This commit is contained in:
19
configure.ac
19
configure.ac
@@ -34,6 +34,8 @@ AC_ENABLE_STATIC(no)
|
||||
LT_INIT
|
||||
PKG_PROG_PKG_CONFIG
|
||||
|
||||
AC_C_RESTRICT
|
||||
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(fcntl.h malloc.h inttypes.h sys/ioctl.h sys/mman.h \
|
||||
@@ -328,6 +330,9 @@ if test "x$enable_udev" = xyes; then
|
||||
fi
|
||||
LIBS=$saved_LIBS
|
||||
|
||||
dnl Check for JSON-C used in LUKS2
|
||||
PKG_CHECK_MODULES([JSON_C], [json-c])
|
||||
|
||||
dnl Crypto backend configuration.
|
||||
AC_ARG_WITH([crypto_backend],
|
||||
AS_HELP_STRING([--with-crypto_backend=BACKEND], [crypto backend (gcrypt/openssl/nss/kernel/nettle) [gcrypt]]),
|
||||
@@ -363,8 +368,8 @@ AM_CONDITIONAL(CRYPTO_BACKEND_NETTLE, test $with_crypto_backend = nettle)
|
||||
AM_CONDITIONAL(CRYPTO_INTERNAL_PBKDF2, test $use_internal_pbkdf2 = 1)
|
||||
AC_DEFINE_UNQUOTED(USE_INTERNAL_PBKDF2, [$use_internal_pbkdf2], [Use internal PBKDF2])
|
||||
|
||||
AC_ARG_ENABLE(argon2, AS_HELP_STRING([--enable-argon2],
|
||||
[enable internal implementation of Argon2 PBKDF]))
|
||||
AC_ARG_ENABLE(argon2, AS_HELP_STRING([--disable-argon2],
|
||||
[disable internal implementation of Argon2 PBKDF]),[], [enable_argon2=yes])
|
||||
AM_CONDITIONAL(CRYPTO_INTERNAL_ARGON2, test x$enable_argon2 = xyes)
|
||||
if test x$enable_argon2 = xyes ; then
|
||||
AC_DEFINE(USE_INTERNAL_ARGON2, 1, [Use internal Argon2])
|
||||
@@ -414,6 +419,8 @@ AC_SUBST([CRYPTO_CFLAGS])
|
||||
AC_SUBST([CRYPTO_LIBS])
|
||||
AC_SUBST([CRYPTO_STATIC_LIBS])
|
||||
|
||||
AC_SUBST([JSON_C_LIBS])
|
||||
|
||||
AC_SUBST([LIBCRYPTSETUP_VERSION])
|
||||
AC_SUBST([LIBCRYPTSETUP_VERSION_INFO])
|
||||
|
||||
@@ -480,7 +487,12 @@ CS_STR_WITH([luks1-hash], [hash function for LUKS1 header], [sha256])
|
||||
CS_STR_WITH([luks1-cipher], [cipher for LUKS1], [aes])
|
||||
CS_STR_WITH([luks1-mode], [cipher mode for LUKS1], [xts-plain64])
|
||||
CS_NUM_WITH([luks1-keybits],[key length in bits for LUKS1], [256])
|
||||
CS_NUM_WITH([luks1-iter-time],[PBKDF2 iteration time for LUKS1 (in ms)], [2000])
|
||||
|
||||
CS_STR_WITH([luks2-pbkdf], [Default PBKDF algorithm (pbkdf2 or argon2i/argon2id) for LUKS2], [argon2i])
|
||||
CS_NUM_WITH([luks1-iter-time], [PBKDF2 iteration time for LUKS1 (in ms)], [2000])
|
||||
CS_NUM_WITH([luks2-iter-time], [Argon2 PBKDF iteration time for LUKS2 (in ms)], [800])
|
||||
CS_NUM_WITH([luks2-memory-kb], [Argon2 PBKDF memory cost for LUKS2 (in kB)], [1024])
|
||||
CS_NUM_WITH([luks2-parallel-threads],[Argon2 PBKDF max parallel cost for LUKS2 (if CPUs available)], [4])
|
||||
|
||||
CS_STR_WITH([loopaes-cipher], [cipher for loop-AES mode], [aes])
|
||||
CS_NUM_WITH([loopaes-keybits],[key length in bits for loop-AES mode], [256])
|
||||
@@ -513,6 +525,7 @@ lib/libcryptsetup.pc
|
||||
lib/crypto_backend/Makefile
|
||||
lib/crypto_backend/argon2/Makefile
|
||||
lib/luks1/Makefile
|
||||
lib/luks2/Makefile
|
||||
lib/loopaes/Makefile
|
||||
lib/verity/Makefile
|
||||
lib/tcrypt/Makefile
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SUBDIRS = crypto_backend luks1 loopaes verity tcrypt integrity
|
||||
SUBDIRS = crypto_backend luks1 luks2 loopaes verity tcrypt integrity
|
||||
|
||||
moduledir = $(libdir)/cryptsetup
|
||||
|
||||
@@ -9,6 +9,7 @@ AM_CPPFLAGS = -include config.h \
|
||||
-I$(top_srcdir) \
|
||||
-I$(top_srcdir)/lib/crypto_backend \
|
||||
-I$(top_srcdir)/lib/luks1 \
|
||||
-I$(top_srcdir)/lib/luks2 \
|
||||
-I$(top_srcdir)/lib/loopaes \
|
||||
-I$(top_srcdir)/lib/verity \
|
||||
-I$(top_srcdir)/lib/tcrypt \
|
||||
@@ -24,6 +25,7 @@ lib_LTLIBRARIES = libcryptsetup.la
|
||||
common_ldadd = \
|
||||
crypto_backend/libcrypto_backend.la \
|
||||
luks1/libluks1.la \
|
||||
luks2/libluks2.la \
|
||||
loopaes/libloopaes.la \
|
||||
verity/libverity.la \
|
||||
tcrypt/libtcrypt.la \
|
||||
@@ -41,6 +43,7 @@ libcryptsetup_la_LIBADD = \
|
||||
@UUID_LIBS@ \
|
||||
@DEVMAPPER_LIBS@ \
|
||||
@CRYPTO_LIBS@ \
|
||||
@JSON_C_LIBS@ \
|
||||
$(common_ldadd)
|
||||
|
||||
|
||||
|
||||
@@ -253,9 +253,9 @@ int INTEGRITY_format(struct crypt_device *cd,
|
||||
dmdi.u.integrity.journal_commit_time = params->journal_commit_time;
|
||||
dmdi.u.integrity.interleave_sectors = params->interleave_sectors;
|
||||
dmdi.u.integrity.buffer_sectors = params->buffer_sectors;
|
||||
dmdi.u.integrity.integrity = params->integrity;
|
||||
dmdi.u.integrity.journal_integrity = params->journal_integrity;
|
||||
dmdi.u.integrity.journal_crypt = params->journal_crypt;
|
||||
dmdi.u.integrity.integrity = params->integrity;
|
||||
}
|
||||
|
||||
uuid_generate(tmp_uuid_bin);
|
||||
|
||||
@@ -323,6 +323,8 @@ int crypt_volume_key_keyring(struct crypt_device *cd, int enable);
|
||||
#define CRYPT_PLAIN "PLAIN"
|
||||
/** LUKS version 1 header on-disk */
|
||||
#define CRYPT_LUKS1 "LUKS1"
|
||||
/** LUKS version 2 header on-disk */
|
||||
#define CRYPT_LUKS2 "LUKS2"
|
||||
/** loop-AES compatibility mode */
|
||||
#define CRYPT_LOOPAES "LOOPAES"
|
||||
/** dm-verity mode */
|
||||
@@ -464,8 +466,8 @@ struct crypt_params_integrity {
|
||||
uint32_t tag_size; /**< tag size per-sector in bytes */
|
||||
uint32_t sector_size; /**< sector size in bytes */
|
||||
uint32_t buffer_sectors; /**< number of sectors in one buffer */
|
||||
const char *integrity; /**< integrity algorithm */
|
||||
uint32_t integrity_key_size; /**< integrity key size, info only */
|
||||
const char *integrity; /**< integrity algorithm, NULL for LUKS2 */
|
||||
uint32_t integrity_key_size; /**< integrity key size, info only, 0 for LUKS2 */
|
||||
|
||||
const char *journal_integrity; /**< journal integrity algorithm */
|
||||
const char *journal_integrity_key; /**< journal integrity key, only for crypt_load */
|
||||
@@ -476,6 +478,26 @@ struct crypt_params_integrity {
|
||||
uint32_t journal_crypt_key_size; /**< journal crypt key size, only for crypt_load */
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure used as parameter for LUKS2 device type.
|
||||
*
|
||||
* @see crypt_format, crypt_load
|
||||
*
|
||||
* @note during crypt_format @e data_device attribute determines
|
||||
* if the LUKS2 header is separated from encrypted payload device
|
||||
*
|
||||
*/
|
||||
struct crypt_params_luks2 {
|
||||
const struct crypt_pbkdf_type *pbkdf; /**< PBKDF (and hash) parameters or @e NULL*/
|
||||
const char *integrity; /**< integrity algorithm or @e NULL */
|
||||
const struct crypt_params_integrity *integrity_params; /**< Data integrity parameters or @e NULL*/
|
||||
size_t data_alignment; /**< data alignment in sectors, data offset is multiple of this */
|
||||
const char *data_device; /**< detached encrypted data device or @e NULL */
|
||||
uint32_t sector_size; /**< encryption sector size */
|
||||
const char *label; /**< header label or @e NULL*/
|
||||
const char *subsystem; /**< header subsystem label or @e NULL*/
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@@ -508,6 +530,21 @@ int crypt_format(struct crypt_device *cd,
|
||||
size_t volume_key_size,
|
||||
void *params);
|
||||
|
||||
/**
|
||||
* Convert to new type for already existing device.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param type type of device (optional params struct must be of this type)
|
||||
* @param params crypt type specific parameters (see @link crypt_type @endlink)
|
||||
*
|
||||
* @returns 0 on success or negative errno value otherwise.
|
||||
*
|
||||
* @note Currently, only LUKS1->LUKS2 conversion is supported
|
||||
*/
|
||||
int crypt_convert(struct crypt_device *cd,
|
||||
const char *type,
|
||||
void *params);
|
||||
|
||||
/**
|
||||
* Set new UUID for already existing device.
|
||||
*
|
||||
@@ -521,6 +558,21 @@ int crypt_format(struct crypt_device *cd,
|
||||
int crypt_set_uuid(struct crypt_device *cd,
|
||||
const char *uuid);
|
||||
|
||||
/**
|
||||
* Set new labels (label and subsystem) for already existing device.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param label requested label or @e NULL
|
||||
* @param subsystem requested subsystem label or @e NULL
|
||||
*
|
||||
* @returns 0 on success or negative errno value otherwise.
|
||||
*
|
||||
* @note Currently, only LUKS2 device type is supported
|
||||
*/
|
||||
int crypt_set_label(struct crypt_device *cd,
|
||||
const char *label,
|
||||
const char *subsystem);
|
||||
|
||||
/**
|
||||
* Load crypt device parameters from on-disk header.
|
||||
*
|
||||
@@ -754,6 +806,37 @@ int crypt_keyslot_add_by_volume_key(struct crypt_device *cd,
|
||||
const char *passphrase,
|
||||
size_t passphrase_size);
|
||||
|
||||
/* create keyslot with volume key not associated with current dm-crypt segment */
|
||||
#define CRYPT_VOLUME_KEY_NO_SEGMENT (1 << 0)
|
||||
|
||||
/**
|
||||
* Add key slot using provided key.
|
||||
*
|
||||
* @pre @e cd contains initialized and formatted LUKS2 device context
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param keyslot requested keyslot or CRYPT_ANY_SLOT
|
||||
* @param volume_key provided volume key or @e NULL (see note below)
|
||||
* @param volume_key_size size of volume_key
|
||||
* @param passphrase passphrase for new keyslot, @e NULL for query
|
||||
* @param passphrase_size size of passphrase
|
||||
*
|
||||
* @return allocated key slot number or negative errno otherwise.
|
||||
*
|
||||
* @note in case volume_key is @e NULL following first matching rule will apply:
|
||||
* a) if cd is device handle used in crypt_format() by current process, the volume
|
||||
* key generated (passed) to crypt_format() will be stored in keyslot.
|
||||
* b) if CRYPT_VOLUME_KEY_NO_SEGMENT flag is raised the new volume_key will be
|
||||
* generated and stored in keyslot.
|
||||
*/
|
||||
int crypt_keyslot_add_by_key(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
const char *volume_key,
|
||||
size_t volume_key_size,
|
||||
const char *passphrase,
|
||||
size_t passphrase_size,
|
||||
uint32_t flags);
|
||||
|
||||
/**
|
||||
* Destroy (and disable) key slot.
|
||||
*
|
||||
@@ -770,6 +853,20 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup areqs LUKS2 header requirements
|
||||
*
|
||||
* LUKS2 requirements flags
|
||||
*
|
||||
* @addtogroup areqs
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define CRYPT_REQUIREMENT_UNKNOWN (1 << 31)
|
||||
/** unknown requirement in header (output only) */
|
||||
|
||||
/* @} */
|
||||
|
||||
/**
|
||||
* @defgroup aflags Device runtime attributes
|
||||
*
|
||||
@@ -807,6 +904,8 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot);
|
||||
#define CRYPT_ACTIVATE_NO_JOURNAL (1 << 12)
|
||||
/** dm-integrity: recovery mode - no journal, no integrity checks */
|
||||
#define CRYPT_ACTIVATE_RECOVERY (1 << 13)
|
||||
/** ignore persistently stored flags */
|
||||
#define CRYPT_ACTIVATE_IGNORE_PERSISTENT (1 << 14)
|
||||
|
||||
/**
|
||||
* Active device runtime attributes
|
||||
@@ -832,6 +931,49 @@ int crypt_get_active_device(struct crypt_device *cd,
|
||||
const char *name,
|
||||
struct crypt_active_device *cad);
|
||||
|
||||
/**
|
||||
* Persistent flags type
|
||||
*/
|
||||
typedef enum {
|
||||
CRYPT_FLAGS_ACTIVATION, /**< activation flags */
|
||||
CRYPT_FLAGS_REQUIREMENTS /**< requirements flags */
|
||||
} crypt_flags_type;
|
||||
|
||||
/**
|
||||
* Set persistent flags.
|
||||
*
|
||||
* @param cd crypt device handle (can be @e NULL)
|
||||
* @param type type to set (CRYPT_FLAGS_ACTIVATION or CRYPT_FLAGS_REQUIREMENTS)
|
||||
* @param flags flags to set
|
||||
*
|
||||
* @return @e 0 on success or negative errno value otherwise
|
||||
*
|
||||
* @note Valid only for LUKS2.
|
||||
*
|
||||
* @note Not all activation flags can be stored. Only ALLOW_DISCARD,
|
||||
* SAME_CPU_CRYPT, SUBMIT_FROM_CRYPT_CPU and NO_JOURNAL can be
|
||||
* stored persistently.
|
||||
*
|
||||
* @note Only requirements flags recognised by current library may be set.
|
||||
* CRYPT_REQUIREMENT_FLAG is illegal (output only) in set operation.
|
||||
*/
|
||||
int crypt_persistent_flags_set(struct crypt_device *cd,
|
||||
crypt_flags_type type,
|
||||
uint32_t flags);
|
||||
|
||||
/**
|
||||
* Get persistent flags stored in header.
|
||||
*
|
||||
* @param cd crypt device handle (can be @e NULL)
|
||||
* @param type flags type to retrieve (CRYPT_FLAGS_ACTIVATION or CRYPT_FLAGS_REQUIREMENTS)
|
||||
* @param flags reference to output variable
|
||||
*
|
||||
* @return @e 0 on success or negative errno value otherwise
|
||||
*/
|
||||
int crypt_persistent_flags_get(struct crypt_device *cd,
|
||||
crypt_flags_type type,
|
||||
uint32_t *flags);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@@ -1226,7 +1368,37 @@ typedef enum {
|
||||
*
|
||||
*/
|
||||
crypt_keyslot_info crypt_keyslot_status(struct crypt_device *cd, int keyslot);
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Crypt keyslot priority
|
||||
*/
|
||||
typedef enum {
|
||||
CRYPT_SLOT_PRIORITY_INVALID =-1, /**< no such slot */
|
||||
CRYPT_SLOT_PRIORITY_IGNORE = 0, /**< CRYPT_ANY_SLOT will ignore it for open */
|
||||
CRYPT_SLOT_PRIORITY_NORMAL = 1, /**< default priority, tried after preferred */
|
||||
CRYPT_SLOT_PRIORITY_PREFER = 2, /**< will try to open first */
|
||||
} crypt_keyslot_priority;
|
||||
|
||||
/**
|
||||
* Get keyslot priority (LUKS2)
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param keyslot keyslot number
|
||||
*
|
||||
* @return value defined by crypt_keyslot_priority
|
||||
*/
|
||||
crypt_keyslot_priority crypt_keyslot_get_priority(struct crypt_device *cd, int keyslot);
|
||||
|
||||
/**
|
||||
* Set keyslot priority (LUKS2)
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param keyslot keyslot number
|
||||
* @param priority priority defined in crypt_keyslot_priority
|
||||
*
|
||||
* @return @e 0 on success or negative errno value otherwise.
|
||||
*/
|
||||
int crypt_keyslot_set_priority(struct crypt_device *cd, int keyslot, crypt_keyslot_priority priority);
|
||||
|
||||
/**
|
||||
* Get number of keyslots supported for device type.
|
||||
@@ -1253,6 +1425,8 @@ int crypt_keyslot_area(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
uint64_t *offset,
|
||||
uint64_t *length);
|
||||
/** @} */
|
||||
|
||||
|
||||
/**
|
||||
* Backup header and keyslots to file.
|
||||
@@ -1314,6 +1488,16 @@ void crypt_set_debug_level(int level);
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup keyfiles Keyfile utilities
|
||||
*
|
||||
* Utilities to handle keyfiles
|
||||
*
|
||||
* @addtogroup keyfiles
|
||||
* @{
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Read keyfile
|
||||
*
|
||||
@@ -1383,6 +1567,238 @@ int crypt_wipe(struct crypt_device *cd,
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup tokens LUKS2 token wrapper access
|
||||
*
|
||||
* Utilities for handling tokens LUKS2
|
||||
* Token is a device or a method how to read password for particular keyslot
|
||||
* automatically. It can be chunk of data stored on hardware token or
|
||||
* just a metadata how to generate password.
|
||||
*
|
||||
* @addtogroup tokens
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** iterate through all tokens */
|
||||
#define CRYPT_ANY_TOKEN -1
|
||||
|
||||
/**
|
||||
* Get content of a token definition in JSON format.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token token id
|
||||
* @param json buffer with JSON
|
||||
*
|
||||
* @return allocated token id or negative errno otherwise.
|
||||
*/
|
||||
int crypt_token_json_get(struct crypt_device *cd,
|
||||
int token,
|
||||
const char **json);
|
||||
|
||||
/**
|
||||
* Store content of a token definition in JSON format.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token token id or @e CRYPT_ANY_TOKEN to allocate new one
|
||||
* @param json buffer with JSON or @e NULL to remove token
|
||||
*
|
||||
* @return allocated token id or negative errno otherwise.
|
||||
*
|
||||
* @note The buffer must be in proper JSON format and must contain at least
|
||||
* string "type" with slot type and array of string names "keyslots".
|
||||
* Keyslots array contains assignments to particular slots and can be empty.
|
||||
*/
|
||||
int crypt_token_json_set(struct crypt_device *cd,
|
||||
int token,
|
||||
const char *json);
|
||||
|
||||
typedef enum {
|
||||
CRYPT_TOKEN_INVALID, /**< token is invalid */
|
||||
CRYPT_TOKEN_INACTIVE, /**< token is empty (free) */
|
||||
CRYPT_TOKEN_INTERNAL, /**< active internal token with driver */
|
||||
CRYPT_TOKEN_INTERNAL_UNKNOWN, /**< active internal token (reserved name) with missing token driver */
|
||||
CRYPT_TOKEN_EXTERNAL, /**< active external (user defined) token with driver */
|
||||
CRYPT_TOKEN_EXTERNAL_UNKNOWN, /**< active external (user defined) token with missing token driver */
|
||||
} crypt_token_info;
|
||||
|
||||
/**
|
||||
* Get info for specific token.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token existing token id
|
||||
* @param type pointer for returned type string
|
||||
*
|
||||
* @return token status info. For any returned status (besides CRYPT_TOKEN_INVALID
|
||||
* and CRYPT_TOKEN_INACTIVE) and if type parameter is not NULL it will
|
||||
* contain address of type string.
|
||||
*
|
||||
* @note if required, create a copy of string referenced in *type before calling next
|
||||
* libcryptsetup API function. The reference may become invalid.
|
||||
*/
|
||||
crypt_token_info crypt_token_status(struct crypt_device *cd, int token, const char **type);
|
||||
|
||||
/**
|
||||
* LUKS2 keyring token paramaters.
|
||||
*
|
||||
* @see crypt_token_builtin_set
|
||||
*
|
||||
*/
|
||||
struct crypt_token_params_luks2_keyring {
|
||||
const char *key_description;
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a new luks2 keyring token.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token token id or @e CRYPT_ANY_TOKEN to allocate new one
|
||||
* @param params luks2 keyring token params
|
||||
*
|
||||
* @return allocated token id or negative errno otherwise.
|
||||
*
|
||||
*/
|
||||
int crypt_token_luks2_keyring_set(struct crypt_device *cd,
|
||||
int token,
|
||||
const struct crypt_token_params_luks2_keyring *params);
|
||||
|
||||
/*
|
||||
* Get LUKS2 keyring token params
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token existing luks2 keyring token id
|
||||
* @param params returned luks2 keyring token params
|
||||
*
|
||||
* @return allocated token id or negative errno otherwise.
|
||||
*
|
||||
* @note do not call free() on params members. Members are valid only
|
||||
* until next libcryptsetup function is called.
|
||||
*/
|
||||
int crypt_token_luks2_keyring_get(struct crypt_device *cd,
|
||||
int token,
|
||||
struct crypt_token_params_luks2_keyring *params);
|
||||
|
||||
/**
|
||||
* Assign a token to particular keyslot.
|
||||
* (There can be more keyslots assigned to one token id.)
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token token id
|
||||
* @param keyslot keyslot to be assigned to token (CRYPT_ANY SLOT
|
||||
* assigns all active keyslots to token)
|
||||
*
|
||||
* @return allocated token id or negative errno otherwise.
|
||||
*/
|
||||
int crypt_token_assign_keyslot(struct crypt_device *cd,
|
||||
int token,
|
||||
int keyslot);
|
||||
|
||||
/**
|
||||
* Unassign a token from particular keyslot.
|
||||
* (There can be more keyslots assigned to one token id.)
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token token id
|
||||
* @param keyslot keyslot to be unassigned from token (CRYPT_ANY SLOT
|
||||
* unassigns all active keyslots from token)
|
||||
*
|
||||
* @return allocated token id or negative errno otherwise.
|
||||
*/
|
||||
int crypt_token_unassign_keyslot(struct crypt_device *cd,
|
||||
int token,
|
||||
int keyslot);
|
||||
|
||||
/**
|
||||
* Token handler open function prototype.
|
||||
* This fuction retrieves password from a token and return allocated buffer
|
||||
* containing this password. This buffer has to be deallocated by calling
|
||||
* free() function and content should be wiped before deallocation.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param token token id
|
||||
* @param buffer returned allocated buffer with password
|
||||
* @param buffer_len length of the buffer
|
||||
* @param usrptr user data in @link crypt_activate_by_token @endlink
|
||||
*/
|
||||
typedef int (*crypt_token_open_func) (
|
||||
struct crypt_device *cd,
|
||||
int token,
|
||||
char **buffer,
|
||||
size_t *buffer_len,
|
||||
void *usrptr);
|
||||
|
||||
/**
|
||||
* Token handler buffer free function prototype.
|
||||
* This function is used by library to free the buffer with keyslot
|
||||
* passphrase when it's no longer needed. If not defined the library
|
||||
* overwrites buffer with zeroes and call free().
|
||||
*
|
||||
* @param buffer the buffer with keyslot passphrase
|
||||
* @param buffer_len the buffer length
|
||||
*/
|
||||
typedef void (*crypt_token_buffer_free_func) (void *buffer, size_t buffer_len);
|
||||
|
||||
/**
|
||||
* Token handler validate function prototype.
|
||||
* This fuction validates JSON representation of user defined token for additional data
|
||||
* specific for its token type. If defined in the handler, it's called
|
||||
* during @link crypt_activate_by_token @endlink. It may also be called during
|
||||
* @link crypt_token_set @endlink when appropriate token handler was registered before
|
||||
* with @link crypt_token_register @endlink.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param json buffer with JSON
|
||||
*/
|
||||
typedef int (*crypt_token_validate_func) (struct crypt_device *cd, const char *json);
|
||||
|
||||
/**
|
||||
* Token handler dump function prototype.
|
||||
* This fuction is supposed to print token implementation specific details. It gets
|
||||
* called during @link crypt_dump @endlink if token handler was registered before.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param json buffer with token JSON
|
||||
*
|
||||
* @note dump implementations are advised to use @link crypt_log @endlink function
|
||||
* to dump token details.
|
||||
*/
|
||||
typedef void (*crypt_token_dump_func) (struct crypt_device *cd, const char *json);
|
||||
|
||||
typedef struct {
|
||||
const char *name; /**< token handler name */
|
||||
crypt_token_open_func open; /**< token handler open function */
|
||||
crypt_token_buffer_free_func buffer_free; /**< token handler buffer_free function (optional) */
|
||||
crypt_token_validate_func validate; /**< token handler validate function (optional) */
|
||||
crypt_token_dump_func dump; /**< token handler dump function (optional) */
|
||||
} crypt_token_handler;
|
||||
|
||||
/**
|
||||
* Register token handler
|
||||
*
|
||||
* @param handler token handler to register
|
||||
*
|
||||
* @return @e 0 on success or negative errno value otherwise.
|
||||
*/
|
||||
int crypt_token_register(const crypt_token_handler *handler);
|
||||
|
||||
/**
|
||||
* Activate device or check key using a token.
|
||||
*
|
||||
* @param cd crypt device handle
|
||||
* @param name name of device to create, if @e NULL only check token
|
||||
* @param token requested token to check or CRYPT_ANY_TOKEN to check all
|
||||
* @param usrptr provided identification in callback
|
||||
* @param flags activation flags
|
||||
*
|
||||
* @return unlocked key slot number or negative errno otherwise.
|
||||
*/
|
||||
int crypt_activate_by_token(struct crypt_device *cd,
|
||||
const char *name,
|
||||
int token,
|
||||
void *usrptr,
|
||||
uint32_t flags);
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -8,11 +8,13 @@ CRYPTSETUP_2.0 {
|
||||
crypt_set_confirm_callback;
|
||||
crypt_set_iteration_time;
|
||||
crypt_set_uuid;
|
||||
crypt_set_label;
|
||||
crypt_set_data_device;
|
||||
|
||||
crypt_memory_lock;
|
||||
crypt_metadata_locking;
|
||||
crypt_format;
|
||||
crypt_convert;
|
||||
crypt_load;
|
||||
crypt_repair;
|
||||
crypt_resize;
|
||||
@@ -27,6 +29,22 @@ CRYPTSETUP_2.0 {
|
||||
crypt_keyslot_add_by_keyfile;
|
||||
crypt_keyslot_add_by_keyfile_offset;
|
||||
crypt_keyslot_add_by_volume_key;
|
||||
crypt_keyslot_add_by_key;
|
||||
|
||||
crypt_keyslot_set_priority;
|
||||
crypt_keyslot_get_priority;
|
||||
|
||||
crypt_token_json_get;
|
||||
crypt_token_json_set;
|
||||
crypt_token_status;
|
||||
crypt_token_luks2_keyring_get;
|
||||
crypt_token_luks2_keyring_set;
|
||||
crypt_token_assign_keyslot;
|
||||
crypt_token_unassign_keyslot;
|
||||
crypt_token_register;
|
||||
|
||||
crypt_activate_by_token;
|
||||
|
||||
crypt_keyslot_destroy;
|
||||
crypt_activate_by_passphrase;
|
||||
crypt_activate_by_keyfile;
|
||||
@@ -55,6 +73,8 @@ CRYPTSETUP_2.0 {
|
||||
|
||||
crypt_get_type;
|
||||
crypt_get_active_device;
|
||||
crypt_persistent_flags_set;
|
||||
crypt_persistent_flags_get;
|
||||
|
||||
crypt_set_rng_type;
|
||||
crypt_get_rng_type;
|
||||
|
||||
23
lib/luks2/Makefile.am
Normal file
23
lib/luks2/Makefile.am
Normal file
@@ -0,0 +1,23 @@
|
||||
moduledir = $(libdir)/cryptsetup
|
||||
|
||||
noinst_LTLIBRARIES = libluks2.la
|
||||
|
||||
libluks2_la_CFLAGS = -Wall $(AM_CFLAGS) @CRYPTO_CFLAGS@
|
||||
|
||||
libluks2_la_SOURCES = \
|
||||
luks2_disk_metadata.c \
|
||||
luks2_json_format.c \
|
||||
luks2_json_metadata.c \
|
||||
luks2_luks1_convert.c \
|
||||
luks2_digest.c \
|
||||
luks2_digest_pbkdf2.c \
|
||||
luks2_keyslot.c \
|
||||
luks2_keyslot_luks2.c \
|
||||
luks2_token_keyring.c \
|
||||
luks2_token.c \
|
||||
luks2_internal.h \
|
||||
luks2.h
|
||||
|
||||
AM_CPPFLAGS = -include config.h \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/lib/crypto_backend
|
||||
353
lib/luks2/luks2.h
Normal file
353
lib/luks2/luks2.h
Normal file
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTSETUP_LUKS2_ONDISK_H
|
||||
#define _CRYPTSETUP_LUKS2_ONDISK_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define LUKS2_MAGIC_1ST "LUKS\xba\xbe"
|
||||
#define LUKS2_MAGIC_2ND "SKUL\xba\xbe"
|
||||
#define LUKS2_MAGIC_L 6
|
||||
#define LUKS2_UUID_L 40
|
||||
#define LUKS2_LABEL_L 48
|
||||
#define LUKS2_SALT_L 64
|
||||
#define LUKS2_CHECKSUM_ALG_L 32
|
||||
#define LUKS2_CHECKSUM_L 64
|
||||
|
||||
#define LUKS2_KEYSLOTS_MAX 32
|
||||
#define LUKS2_TOKENS_MAX 32
|
||||
|
||||
#define LUKS2_BUILTIN_TOKEN_PREFIX "luks2-"
|
||||
#define LUKS2_BUILTIN_TOKEN_PREFIX_LEN 6
|
||||
|
||||
#define LUKS2_TOKEN_KEYRING LUKS2_BUILTIN_TOKEN_PREFIX "keyring"
|
||||
|
||||
#define LUKS2_DIGEST_MAX 8
|
||||
|
||||
typedef int digests_t[LUKS2_DIGEST_MAX];
|
||||
|
||||
#define CRYPT_ANY_SEGMENT -1
|
||||
#define CRYPT_DEFAULT_SEGMENT 0
|
||||
#define CRYPT_DEFAULT_SEGMENT_STR "0"
|
||||
|
||||
/*
|
||||
* LUKS2 header on-disk.
|
||||
*
|
||||
* Binary header is followed by JSON area.
|
||||
* JSON area is followed by keyslot area and data area,
|
||||
* these are described in JSON metadata.
|
||||
*
|
||||
* Note: uuid, csum_alg are intentionally on the same offset as LUKS1
|
||||
* (checksum alg replaces hash in LUKS1)
|
||||
*
|
||||
* String (char) should be zero terminated.
|
||||
* Padding should be wiped.
|
||||
* Checksum is calculated with csum zeroed (+ full JSON area).
|
||||
*/
|
||||
struct luks2_hdr_disk {
|
||||
char magic[LUKS2_MAGIC_L];
|
||||
uint16_t version; /* Version 2 */
|
||||
uint64_t hdr_size; /* in bytes, including JSON area */
|
||||
uint64_t seqid; /* increased on every update */
|
||||
char label[LUKS2_LABEL_L];
|
||||
char checksum_alg[LUKS2_CHECKSUM_ALG_L];
|
||||
uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
|
||||
char uuid[LUKS2_UUID_L];
|
||||
char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
|
||||
uint64_t hdr_offset; /* offset from device start in bytes */
|
||||
char _padding[184];
|
||||
uint8_t csum[LUKS2_CHECKSUM_L];
|
||||
char _padding4096[7*512];
|
||||
/* JSON area starts here */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* LUKS2 header in-memory.
|
||||
*/
|
||||
typedef struct json_object json_object;
|
||||
struct luks2_hdr {
|
||||
size_t hdr_size;
|
||||
uint64_t seqid;
|
||||
unsigned int version;
|
||||
char label[LUKS2_LABEL_L];
|
||||
char subsystem[LUKS2_LABEL_L];
|
||||
char checksum_alg[LUKS2_CHECKSUM_ALG_L];
|
||||
uint8_t salt1[LUKS2_SALT_L];
|
||||
uint8_t salt2[LUKS2_SALT_L];
|
||||
char uuid[LUKS2_UUID_L];
|
||||
json_object *jobj;
|
||||
};
|
||||
|
||||
/*
|
||||
* Supportable header sizes (hdr_disk + JSON area)
|
||||
* Also used as offset for the 2nd header.
|
||||
*/
|
||||
#define LUKS2_HDR_16K_LEN 0x4000
|
||||
|
||||
#define LUKS2_HDR_BIN_LEN sizeof(struct luks2_hdr_disk)
|
||||
|
||||
#define LUKS2_HDR_DEFAULT_LEN 0x400000 /* 4 MiB */
|
||||
|
||||
#define LUKS2_MAX_KEYSLOTS_SIZE 0x8000000 /* 128 MiB */
|
||||
|
||||
int LUKS2_hdr_version_unlocked(struct crypt_device *cd);
|
||||
|
||||
int LUKS2_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr);
|
||||
int LUKS2_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr);
|
||||
int LUKS2_hdr_dump(struct crypt_device *cd, struct luks2_hdr *hdr);
|
||||
|
||||
int LUKS2_hdr_uuid(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *uuid);
|
||||
|
||||
int LUKS2_hdr_labels(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *label,
|
||||
const char *subsystem,
|
||||
int commit);
|
||||
|
||||
void LUKS2_hdr_free(struct luks2_hdr *hdr);
|
||||
|
||||
int LUKS2_hdr_backup(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *backup_file);
|
||||
int LUKS2_hdr_restore(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *backup_file);
|
||||
|
||||
uint64_t LUKS2_hdr_and_areas_size(json_object *jobj);
|
||||
uint64_t LUKS2_keyslots_size(json_object *jobj);
|
||||
|
||||
/*
|
||||
* Generic LUKS2 keyslot
|
||||
*/
|
||||
int LUKS2_keyslot_open(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
int segment,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
struct volume_key **vk);
|
||||
|
||||
int LUKS2_keyslot_store(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
const struct volume_key *vk);
|
||||
|
||||
int LUKS2_keyslot_wipe(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int wipe_area_only);
|
||||
|
||||
int LUKS2_keyslot_dump(struct crypt_device *cd,
|
||||
int keyslot);
|
||||
|
||||
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot);
|
||||
|
||||
int LUKS2_keyslot_priority_set(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
crypt_keyslot_priority priority,
|
||||
int commit);
|
||||
|
||||
/*
|
||||
* Generic LUKS2 token
|
||||
*/
|
||||
int LUKS2_token_json_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char **json);
|
||||
|
||||
int LUKS2_token_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int token,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *json,
|
||||
int commit);
|
||||
|
||||
crypt_token_info LUKS2_token_status(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char **type);
|
||||
|
||||
int LUKS2_builtin_token_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
void *params);
|
||||
|
||||
int LUKS2_builtin_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
const void *params,
|
||||
int commit);
|
||||
|
||||
int LUKS2_token_open_and_activate(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *name,
|
||||
uint32_t flags,
|
||||
void *usrptr);
|
||||
|
||||
int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *name,
|
||||
uint32_t flags);
|
||||
|
||||
/*
|
||||
* Generic LUKS2 digest
|
||||
*/
|
||||
int LUKS2_digests_verify_by_segment(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
const struct volume_key *vk,
|
||||
digests_t digests);
|
||||
|
||||
void LUKS2_digests_erase_unused(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr);
|
||||
|
||||
int LUKS2_digest_verify(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
struct volume_key *vk,
|
||||
int keyslot);
|
||||
|
||||
int LUKS2_digest_dump(struct crypt_device *cd,
|
||||
int digest);
|
||||
|
||||
int LUKS2_digest_json_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int digest,
|
||||
const char **json);
|
||||
|
||||
int LUKS2_digest_json_set(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int digest,
|
||||
const char *json);
|
||||
|
||||
int LUKS2_digests_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
digests_t digests,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_digest_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int digest,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_digest_segment_assign(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
int digest,
|
||||
int assign,
|
||||
int commit);
|
||||
|
||||
int LUKS2_digests_by_keyslot(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
digests_t digests);
|
||||
|
||||
int LUKS2_digest_create(struct crypt_device *cd,
|
||||
const char *type,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk);
|
||||
|
||||
/*
|
||||
* LUKS2 generic
|
||||
*/
|
||||
int LUKS2_activate(struct crypt_device *cd,
|
||||
const char *name,
|
||||
struct volume_key *vk,
|
||||
uint32_t flags);
|
||||
|
||||
int LUKS2_keyslot_luks2_format(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
const char *cipher,
|
||||
size_t keylength);
|
||||
|
||||
int LUKS2_generate_hdr(
|
||||
struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk,
|
||||
const char *cipherName,
|
||||
const char *cipherMode,
|
||||
const char *integrity,
|
||||
const char *uuid,
|
||||
unsigned int sector_size,
|
||||
unsigned int alignPayload,
|
||||
unsigned int alignOffset,
|
||||
int detached_metadata_device);
|
||||
|
||||
uint64_t LUKS2_get_data_offset(struct luks2_hdr *hdr);
|
||||
int LUKS2_get_sector_size(struct luks2_hdr *hdr);
|
||||
const char *LUKS2_get_cipher(struct luks2_hdr *hdr, int segment);
|
||||
const char *LUKS2_get_integrity(struct luks2_hdr *hdr, int segment);
|
||||
int LUKS2_get_volume_key_size(struct luks2_hdr *hdr, int segment);
|
||||
int LUKS2_get_keyslot_key_size(struct luks2_hdr *hdr, int keyslot);
|
||||
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type);
|
||||
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment);
|
||||
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment);
|
||||
crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot);
|
||||
int LUKS2_keyslot_area(struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
uint64_t *offset,
|
||||
uint64_t *length);
|
||||
/*
|
||||
* Permanent activation flags stored in header
|
||||
*/
|
||||
int LUKS2_config_get_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *flags);
|
||||
int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t flags);
|
||||
|
||||
/*
|
||||
* Requirements for device activation or header modification
|
||||
*/
|
||||
int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *requirements);
|
||||
int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t requirements);
|
||||
|
||||
int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, int quiet);
|
||||
|
||||
int crypt_use_keyring_for_vk(const struct crypt_device *cd);
|
||||
int crypt_volume_key_load_in_keyring(struct crypt_device *cd, struct volume_key *vk);
|
||||
|
||||
struct luks_phdr;
|
||||
int LUKS2_luks1_to_luks2(struct crypt_device *cd,
|
||||
struct luks_phdr *hdr1,
|
||||
struct luks2_hdr *hdr2);
|
||||
int LUKS2_luks2_to_luks1(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr2,
|
||||
struct luks_phdr *hdr1);
|
||||
|
||||
#endif
|
||||
391
lib/luks2/luks2_digest.c
Normal file
391
lib/luks2/luks2_digest.c
Normal file
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, digest handling
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
extern const digest_handler PBKDF2_digest;
|
||||
|
||||
static const digest_handler *digest_handlers[LUKS2_DIGEST_MAX] = {
|
||||
&PBKDF2_digest,
|
||||
NULL
|
||||
};
|
||||
|
||||
int crypt_digest_register(const digest_handler *handler)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
|
||||
if (!strcmp(digest_handlers[i]->name, handler->name))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (i == LUKS2_DIGEST_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
digest_handlers[i] = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digest_handlers[i]; i++) {
|
||||
if (!strcmp(digest_handlers[i]->name, type))
|
||||
return digest_handlers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const digest_handler *LUKS2_digest_handler(struct crypt_device *cd, int digest)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj1, *jobj2;
|
||||
|
||||
if (digest < 0)
|
||||
return NULL;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return NULL;
|
||||
|
||||
if (!(jobj1 = LUKS2_get_digest_jobj(hdr, digest)))
|
||||
return NULL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
|
||||
return NULL;
|
||||
|
||||
return LUKS2_digest_handler_type(cd, json_object_get_string(jobj2));
|
||||
}
|
||||
|
||||
static int LUKS2_digest_find_free(struct crypt_device *cd, struct luks2_hdr *hdr)
|
||||
{
|
||||
int digest = 0;
|
||||
|
||||
while (LUKS2_get_digest_jobj(hdr, digest) && digest < LUKS2_DIGEST_MAX)
|
||||
digest++;
|
||||
|
||||
return digest < LUKS2_DIGEST_MAX ? digest : -1;
|
||||
}
|
||||
|
||||
int LUKS2_digest_create(struct crypt_device *cd,
|
||||
const char *type,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk)
|
||||
{
|
||||
int digest;
|
||||
const digest_handler *dh;
|
||||
|
||||
dh = LUKS2_digest_handler_type(cd, type);
|
||||
if (!dh)
|
||||
return -EINVAL;
|
||||
|
||||
digest = LUKS2_digest_find_free(cd, hdr);
|
||||
if (digest < 0)
|
||||
return -EINVAL;
|
||||
|
||||
log_dbg("Creating new digest %d (%s).", digest, type);
|
||||
|
||||
return dh->store(cd, digest, vk->key, vk->keylength) ?: digest;
|
||||
}
|
||||
|
||||
int LUKS2_digests_by_keyslot(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
digests_t digests)
|
||||
{
|
||||
char keyslot_name[16];
|
||||
int i = 0;
|
||||
json_object *jobj_digests, *jobj_digest_keyslots;
|
||||
|
||||
if (snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
|
||||
return -ENOMEM;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
json_object_object_get_ex(val, "keyslots", &jobj_digest_keyslots);
|
||||
if (LUKS2_array_jobj(jobj_digest_keyslots, keyslot_name))
|
||||
digests[i++] = atoi(key);
|
||||
}
|
||||
|
||||
if (i < LUKS2_DIGEST_MAX)
|
||||
digests[i] = -1;
|
||||
|
||||
return i ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
int LUKS2_digest_verify(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
struct volume_key *vk,
|
||||
int keyslot)
|
||||
{
|
||||
const digest_handler *h;
|
||||
digests_t digests;
|
||||
int i, r;
|
||||
|
||||
r = LUKS2_digests_by_keyslot(cd, hdr, keyslot, digests);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digests[i] != -1 ; i++) {
|
||||
log_dbg("Verifying key from keyslot %d, digest %d.",
|
||||
keyslot, digests[i]);
|
||||
h = LUKS2_digest_handler(cd, digests[i]);
|
||||
if (!h)
|
||||
return -EINVAL;
|
||||
|
||||
r = h->verify(cd, digests[i], vk->key, vk->keylength);
|
||||
if (r < 0) {
|
||||
log_dbg("Digest %d (%s) verify failed with %d.",
|
||||
digests[i], h->name, r);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_digest_dump(struct crypt_device *cd, int digest)
|
||||
{
|
||||
const digest_handler *h;
|
||||
|
||||
if (!(h = LUKS2_digest_handler(cd, digest)))
|
||||
return -EINVAL;
|
||||
|
||||
return h->dump(cd, digest);
|
||||
}
|
||||
|
||||
int LUKS2_digests_verify_by_segment(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int segment,
|
||||
const struct volume_key *vk,
|
||||
digests_t digests)
|
||||
{
|
||||
char segment_name[16];
|
||||
const digest_handler *h;
|
||||
json_object *jobj_digests, *jobj_digest_segments;
|
||||
int digest, r, i = 0;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
json_object_object_get_ex(val, "segments", &jobj_digest_segments);
|
||||
if (!LUKS2_array_jobj(jobj_digest_segments, segment_name))
|
||||
continue;
|
||||
|
||||
digest = atoi(key);
|
||||
log_dbg("Verifying key digest %d.", digest);
|
||||
|
||||
h = LUKS2_digest_handler(cd, digest);
|
||||
if (!h)
|
||||
return -EINVAL;
|
||||
|
||||
r = h->verify(cd, digest, vk->key, vk->keylength);
|
||||
if (r < 0) {
|
||||
log_dbg("Digest %d (%s) verify failed with %d.", digest, h->name, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (digests)
|
||||
digests[i] = digest;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (digests && i < LUKS2_DIGEST_MAX)
|
||||
digests[i] = -1;
|
||||
|
||||
return i ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
int LUKS2_digest_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int digest, const char **json)
|
||||
{
|
||||
json_object *jobj_digest;
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
*json = json_object_to_json_string_ext(jobj_digest, JSON_C_TO_STRING_PLAIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int assign_one_digest(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int digest, int assign)
|
||||
{
|
||||
json_object *jobj1, *jobj_digest, *jobj_digest_keyslots;
|
||||
char num[16];
|
||||
|
||||
log_dbg("Keyslot %i %s digest %i.", keyslot, assign ? "assigned to" : "unassigned from", digest);
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "keyslots", &jobj_digest_keyslots);
|
||||
if (!jobj_digest_keyslots)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
if (assign) {
|
||||
jobj1 = LUKS2_array_jobj(jobj_digest_keyslots, num);
|
||||
if (!jobj1)
|
||||
json_object_array_add(jobj_digest_keyslots, json_object_new_string(num));
|
||||
} else {
|
||||
jobj1 = LUKS2_array_remove(jobj_digest_keyslots, num);
|
||||
if (jobj1)
|
||||
json_object_object_add(jobj_digest, "keyslots", jobj1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_digests_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, digests_t digests, int assign, int commit)
|
||||
{
|
||||
int i, r;
|
||||
|
||||
for (i = 0; i < LUKS2_DIGEST_MAX && digests[i] != -1; i++) {
|
||||
r = LUKS2_digest_assign(cd, hdr, keyslot, digests[i], assign, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
|
||||
int LUKS2_digest_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int digest, int assign, int commit)
|
||||
{
|
||||
json_object *jobj_digests;
|
||||
int r = 0;
|
||||
|
||||
if (digest == CRYPT_ANY_DIGEST) {
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_digest(cd, hdr, keyslot, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_digest(cd, hdr, keyslot, digest, assign);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// FIXME: do not write header in nothing changed
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
|
||||
static int assign_one_segment(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int segment, int digest, int assign)
|
||||
{
|
||||
json_object *jobj1, *jobj_digest, *jobj_digest_segments;
|
||||
char num[16];
|
||||
|
||||
log_dbg("Segment %i %s digest %i.", segment, assign ? "assigned to" : "unassigned from", digest);
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr, digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "segments", &jobj_digest_segments);
|
||||
if (!jobj_digest_segments)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", segment);
|
||||
if (assign) {
|
||||
jobj1 = LUKS2_array_jobj(jobj_digest_segments, num);
|
||||
if (!jobj1)
|
||||
json_object_array_add(jobj_digest_segments, json_object_new_string(num));
|
||||
} else {
|
||||
jobj1 = LUKS2_array_remove(jobj_digest_segments, num);
|
||||
if (jobj1)
|
||||
json_object_object_add(jobj_digest, "segments", jobj1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_digest_segment_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int segment, int digest, int assign, int commit)
|
||||
{
|
||||
json_object *jobj_digests;
|
||||
int r = 0;
|
||||
|
||||
if (digest == CRYPT_ANY_DIGEST) {
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_segment(cd, hdr, segment, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_segment(cd, hdr, segment, digest, assign);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// FIXME: do not write header in nothing changed
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
|
||||
static int digest_unused(json_object *jobj_digest)
|
||||
{
|
||||
json_object *jobj;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "segments", &jobj);
|
||||
if (!jobj || !json_object_is_type(jobj, json_type_array) || json_object_array_length(jobj))
|
||||
return 0;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "keyslots", &jobj);
|
||||
if (!jobj || !json_object_is_type(jobj, json_type_array))
|
||||
return 0;
|
||||
|
||||
return json_object_array_length(jobj) ? 0 : 1;
|
||||
}
|
||||
|
||||
void LUKS2_digests_erase_unused(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr)
|
||||
{
|
||||
json_object *jobj_digests;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
if (!jobj_digests || !json_object_is_type(jobj_digests, json_type_object))
|
||||
return;
|
||||
|
||||
json_object_object_foreach(jobj_digests, key, val) {
|
||||
if (digest_unused(val)) {
|
||||
log_dbg("Erasing unused digest %d.", atoi(key));
|
||||
json_object_object_del(jobj_digests, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
197
lib/luks2/luks2_digest_pbkdf2.c
Normal file
197
lib/luks2/luks2_digest_pbkdf2.c
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, PBKDF2 digest handler (LUKS1 compatible)
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
#define LUKS_DIGESTSIZE 20 // since SHA1
|
||||
#define LUKS_SALTSIZE 32
|
||||
#define LUKS_MKD_ITERATIONS_MS 125
|
||||
|
||||
static int PBKDF2_digest_verify(struct crypt_device *cd,
|
||||
int digest,
|
||||
const char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
char checkHashBuf[64];
|
||||
json_object *jobj_digest, *jobj1;
|
||||
const char *hashSpec;
|
||||
char *mkDigest = NULL, mkDigestSalt[LUKS_SALTSIZE];
|
||||
unsigned int mkDigestIterations;
|
||||
size_t len;
|
||||
int r;
|
||||
|
||||
/* This can be done only for internally linked digests */
|
||||
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "hash", &jobj1))
|
||||
return -EINVAL;
|
||||
hashSpec = json_object_get_string(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
|
||||
return -EINVAL;
|
||||
mkDigestIterations = json_object_get_int64(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
|
||||
return -EINVAL;
|
||||
len = sizeof(mkDigestSalt);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), mkDigestSalt, &len))
|
||||
return -EINVAL;
|
||||
if (len != LUKS_SALTSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
|
||||
return -EINVAL;
|
||||
len = 0;
|
||||
if (!base64_decode_alloc(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), &mkDigest, &len))
|
||||
return -EINVAL;
|
||||
if (len < LUKS_DIGESTSIZE ||
|
||||
len > sizeof(checkHashBuf) ||
|
||||
(len != LUKS_DIGESTSIZE && len != (size_t)crypt_hash_size(hashSpec))) {
|
||||
free(mkDigest);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = -EPERM;
|
||||
if (crypt_pbkdf(CRYPT_KDF_PBKDF2, hashSpec, volume_key, volume_key_len,
|
||||
mkDigestSalt, LUKS_SALTSIZE,
|
||||
checkHashBuf, len,
|
||||
mkDigestIterations, 0, 0) < 0) {
|
||||
r = -EINVAL;
|
||||
} else {
|
||||
if (memcmp(checkHashBuf, mkDigest, len) == 0)
|
||||
r = 0;
|
||||
}
|
||||
|
||||
free(mkDigest);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int PBKDF2_digest_store(struct crypt_device *cd,
|
||||
int digest,
|
||||
const char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
json_object *jobj_digest, *jobj_digests;
|
||||
char salt[LUKS_SALTSIZE], digest_raw[128], num[16];
|
||||
int r;
|
||||
char *base64_str;
|
||||
struct luks2_hdr *hdr;
|
||||
struct crypt_pbkdf_type pbkdf = {
|
||||
.type = CRYPT_KDF_PBKDF2,
|
||||
.hash = "sha256",
|
||||
.time_ms = LUKS_MKD_ITERATIONS_MS,
|
||||
};
|
||||
|
||||
log_dbg("Setting PBKDF2 type key digest %d.", digest);
|
||||
|
||||
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (crypt_get_pbkdf(cd)->flags & CRYPT_PBKDF_NO_BENCHMARK)
|
||||
pbkdf.iterations = MIN_PBKDF2_ITERATIONS;
|
||||
else {
|
||||
r = crypt_benchmark_pbkdf_internal(cd, &pbkdf, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = crypt_pbkdf(CRYPT_KDF_PBKDF2, pbkdf.hash, volume_key, volume_key_len,
|
||||
salt, LUKS_SALTSIZE, digest_raw, crypt_hmac_size(pbkdf.hash),
|
||||
pbkdf.iterations, 0, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
|
||||
jobj_digests = NULL;
|
||||
if (!jobj_digest) {
|
||||
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
jobj_digest = json_object_new_object();
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_digest, "type", json_object_new_string("pbkdf2"));
|
||||
json_object_object_add(jobj_digest, "keyslots", json_object_new_array());
|
||||
json_object_object_add(jobj_digest, "segments", json_object_new_array());
|
||||
json_object_object_add(jobj_digest, "hash", json_object_new_string(pbkdf.hash));
|
||||
json_object_object_add(jobj_digest, "iterations", json_object_new_int(pbkdf.iterations));
|
||||
|
||||
base64_encode_alloc(salt, LUKS_SALTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(jobj_digest);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_digest, "salt", json_object_new_string(base64_str));
|
||||
free(base64_str);
|
||||
|
||||
base64_encode_alloc(digest_raw, crypt_hmac_size(pbkdf.hash), &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(jobj_digest);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_digest, "digest", json_object_new_string(base64_str));
|
||||
free(base64_str);
|
||||
|
||||
if (jobj_digests) {
|
||||
snprintf(num, sizeof(num), "%d", digest);
|
||||
json_object_object_add(jobj_digests, num, jobj_digest);
|
||||
}
|
||||
|
||||
JSON_DBG(jobj_digest, "Digest JSON");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int PBKDF2_digest_dump(struct crypt_device *cd, int digest)
|
||||
{
|
||||
json_object *jobj_digest, *jobj1;
|
||||
|
||||
/* This can be done only for internally linked digests */
|
||||
jobj_digest = LUKS2_get_digest_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), digest);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "hash", &jobj1);
|
||||
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "iterations", &jobj1);
|
||||
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "salt", &jobj1);
|
||||
log_std(cd, "\tSalt: ");
|
||||
hexprint_base64(cd, jobj1, " ", " ");
|
||||
|
||||
json_object_object_get_ex(jobj_digest, "digest", &jobj1);
|
||||
log_std(cd, "\tDigest: ");
|
||||
hexprint_base64(cd, jobj1, " ", " ");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const digest_handler PBKDF2_digest = {
|
||||
.name = "pbkdf2",
|
||||
.verify = PBKDF2_digest_verify,
|
||||
.store = PBKDF2_digest_store,
|
||||
.dump = PBKDF2_digest_dump,
|
||||
};
|
||||
695
lib/luks2/luks2_disk_metadata.c
Normal file
695
lib/luks2/luks2_disk_metadata.c
Normal file
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
json_object *parse_json_len(const char *json_area, int length, int *end_offset)
|
||||
{
|
||||
json_object *jobj;
|
||||
struct json_tokener *jtok;
|
||||
|
||||
if (!json_area || length <= 0)
|
||||
return NULL;
|
||||
|
||||
jtok = json_tokener_new();
|
||||
if (!jtok) {
|
||||
log_dbg("ERROR: Failed to init json tokener");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobj = json_tokener_parse_ex(jtok, json_area, length);
|
||||
if (!jobj)
|
||||
log_dbg("ERROR: Failed to parse json data (%d): %s",
|
||||
json_tokener_get_error(jtok),
|
||||
json_tokener_error_desc(json_tokener_get_error(jtok)));
|
||||
else
|
||||
*end_offset = jtok->char_offset;
|
||||
|
||||
json_tokener_free(jtok);
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
static void log_dbg_checksum(const uint8_t *csum, const char *csum_alg, const char *info)
|
||||
{
|
||||
char csum_txt[2*LUKS2_CHECKSUM_L+1];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < crypt_hash_size(csum_alg); i++)
|
||||
snprintf(&csum_txt[i*2], 3, "%02hhx", (const char)csum[i]);
|
||||
csum_txt[i*2+1] = '\0'; /* Just to be safe, sprintf should write \0 there. */
|
||||
|
||||
log_dbg("Checksum:%s (%s)", &csum_txt[0], info);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate hash (checksum) of |LUKS2_bin|LUKS2_JSON_area| from in-memory structs.
|
||||
* LUKS2 on-disk header contains uniques salt both for primary and secondary header.
|
||||
* Checksum is always calculated with zeroed checksum field in binary header.
|
||||
*/
|
||||
static int hdr_checksum_calculate(const char *alg, struct luks2_hdr_disk *hdr_disk,
|
||||
const char *json_area, size_t json_len)
|
||||
{
|
||||
struct crypt_hash *hd = NULL;
|
||||
int r;
|
||||
|
||||
if (crypt_hash_init(&hd, alg))
|
||||
return -EINVAL;
|
||||
|
||||
/* Binary header, csum zeroed. */
|
||||
r = crypt_hash_write(hd, (char*)hdr_disk, LUKS2_HDR_BIN_LEN);
|
||||
|
||||
/* JSON area (including unused space) */
|
||||
if (!r)
|
||||
r = crypt_hash_write(hd, json_area, json_len);
|
||||
|
||||
if (!r)
|
||||
r = crypt_hash_final(hd, (char*)hdr_disk->csum, crypt_hash_size(alg));
|
||||
|
||||
crypt_hash_destroy(hd);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare hash (checksum) of on-disk and in-memory header.
|
||||
*/
|
||||
static int hdr_checksum_check(const char *alg, struct luks2_hdr_disk *hdr_disk,
|
||||
const char *json_area, size_t json_len)
|
||||
{
|
||||
struct luks2_hdr_disk hdr_tmp;
|
||||
int r;
|
||||
|
||||
/* Copy header and zero checksum. */
|
||||
memcpy(&hdr_tmp, hdr_disk, LUKS2_HDR_BIN_LEN);
|
||||
memset(&hdr_tmp.csum, 0, sizeof(hdr_tmp.csum));
|
||||
|
||||
r = hdr_checksum_calculate(alg, &hdr_tmp, json_area, json_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_dbg_checksum(hdr_disk->csum, alg, "on-disk");
|
||||
log_dbg_checksum(hdr_tmp.csum, alg, "in-memory");
|
||||
|
||||
if (memcmp(hdr_tmp.csum, hdr_disk->csum, crypt_hash_size(alg)))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert header from on-disk format to in-memory struct
|
||||
*/
|
||||
static void hdr_from_disk(struct luks2_hdr_disk *hdr_disk1,
|
||||
struct luks2_hdr_disk *hdr_disk2,
|
||||
struct luks2_hdr *hdr,
|
||||
int secondary)
|
||||
{
|
||||
hdr->version = be16_to_cpu(hdr_disk1->version);
|
||||
hdr->hdr_size = be64_to_cpu(hdr_disk1->hdr_size);
|
||||
hdr->seqid = be64_to_cpu(hdr_disk1->seqid);
|
||||
|
||||
memcpy(hdr->label, hdr_disk1->label, LUKS2_LABEL_L);
|
||||
hdr->label[LUKS2_LABEL_L - 1] = '\0';
|
||||
memcpy(hdr->subsystem, hdr_disk1->subsystem, LUKS2_LABEL_L);
|
||||
hdr->subsystem[LUKS2_LABEL_L - 1] = '\0';
|
||||
memcpy(hdr->checksum_alg, hdr_disk1->checksum_alg, LUKS2_CHECKSUM_ALG_L);
|
||||
hdr->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
|
||||
memcpy(hdr->uuid, hdr_disk1->uuid, LUKS2_UUID_L);
|
||||
hdr->uuid[LUKS2_UUID_L - 1] = '\0';
|
||||
|
||||
if (secondary) {
|
||||
memcpy(hdr->salt1, hdr_disk2->salt, LUKS2_SALT_L);
|
||||
memcpy(hdr->salt2, hdr_disk1->salt, LUKS2_SALT_L);
|
||||
} else {
|
||||
memcpy(hdr->salt1, hdr_disk1->salt, LUKS2_SALT_L);
|
||||
memcpy(hdr->salt2, hdr_disk2->salt, LUKS2_SALT_L);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert header from in-memory struct to on-disk format
|
||||
*/
|
||||
static void hdr_to_disk(struct luks2_hdr *hdr,
|
||||
struct luks2_hdr_disk *hdr_disk,
|
||||
int secondary, uint64_t offset)
|
||||
{
|
||||
assert(((char*)&(hdr_disk->_padding4096) - (char*)&(hdr_disk->magic)) == 512);
|
||||
|
||||
memset(hdr_disk, 0, LUKS2_HDR_BIN_LEN);
|
||||
|
||||
memcpy(&hdr_disk->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
|
||||
hdr_disk->version = cpu_to_be16(hdr->version);
|
||||
hdr_disk->hdr_size = cpu_to_be64(hdr->hdr_size);
|
||||
hdr_disk->hdr_offset = cpu_to_be64(offset);
|
||||
hdr_disk->seqid = cpu_to_be64(hdr->seqid);
|
||||
|
||||
strncpy(hdr_disk->label, hdr->label, LUKS2_LABEL_L);
|
||||
hdr_disk->label[LUKS2_LABEL_L - 1] = '\0';
|
||||
strncpy(hdr_disk->subsystem, hdr->subsystem, LUKS2_LABEL_L);
|
||||
hdr_disk->subsystem[LUKS2_LABEL_L - 1] = '\0';
|
||||
strncpy(hdr_disk->checksum_alg, hdr->checksum_alg, LUKS2_CHECKSUM_ALG_L);
|
||||
hdr_disk->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
|
||||
strncpy(hdr_disk->uuid, hdr->uuid, LUKS2_UUID_L);
|
||||
hdr_disk->uuid[LUKS2_UUID_L - 1] = '\0';
|
||||
|
||||
memcpy(hdr_disk->salt, secondary ? hdr->salt2 : hdr->salt1, LUKS2_SALT_L);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity checks before checkum is validated
|
||||
*/
|
||||
static int hdr_disk_sanity_check_pre(struct luks2_hdr_disk *hdr,
|
||||
size_t *hdr_json_size, int secondary,
|
||||
uint64_t offset)
|
||||
{
|
||||
if (memcmp(hdr->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
|
||||
return -EINVAL;
|
||||
|
||||
if (be16_to_cpu(hdr->version) != 2) {
|
||||
log_dbg("Unsupported LUKS2 header version %u.", be16_to_cpu(hdr->version));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (offset != be64_to_cpu(hdr->hdr_offset)) {
|
||||
log_dbg("LUKS2 offset 0x%04x on device differs to expected offset 0x%04x.",
|
||||
(unsigned)be64_to_cpu(hdr->hdr_offset), (unsigned)offset);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* FIXME: sanity check checksum alg. */
|
||||
|
||||
log_dbg("LUKS2 header version %u of size %u bytes, checksum %s.",
|
||||
(unsigned)be16_to_cpu(hdr->version), (unsigned)be64_to_cpu(hdr->hdr_size),
|
||||
hdr->checksum_alg);
|
||||
|
||||
*hdr_json_size = be64_to_cpu(hdr->hdr_size) - LUKS2_HDR_BIN_LEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read LUKS2 header from disk at specific offset.
|
||||
*/
|
||||
static int hdr_read_disk(struct device *device, struct luks2_hdr_disk *hdr_disk,
|
||||
char **json_area, uint64_t offset, int secondary)
|
||||
{
|
||||
size_t hdr_json_size = 0;
|
||||
int devfd = -1, r;
|
||||
|
||||
log_dbg("Trying to read %s LUKS2 header at offset %" PRIu64 ".",
|
||||
secondary ? "secondary" : "primary", offset);
|
||||
|
||||
devfd = device_open_locked(device, O_RDONLY);
|
||||
if (devfd < 0)
|
||||
return devfd == -1 ? -EIO : devfd;
|
||||
|
||||
/*
|
||||
* Read binary header and run sanity check before reading
|
||||
* JSON area and validating checksum.
|
||||
*/
|
||||
if (read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), hdr_disk,
|
||||
LUKS2_HDR_BIN_LEN, offset) != LUKS2_HDR_BIN_LEN) {
|
||||
close(devfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
r = hdr_disk_sanity_check_pre(hdr_disk, &hdr_json_size, secondary, offset);
|
||||
if (r < 0) {
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and read JSON area. Always the whole area must be read.
|
||||
*/
|
||||
*json_area = malloc(hdr_json_size);
|
||||
if (!*json_area) {
|
||||
close(devfd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), *json_area, hdr_json_size,
|
||||
offset + LUKS2_HDR_BIN_LEN) != (ssize_t)hdr_json_size) {
|
||||
close(devfd);
|
||||
free(*json_area);
|
||||
*json_area = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
close(devfd);
|
||||
|
||||
/*
|
||||
* Calculate and validate checksum and zero it afterwards.
|
||||
*/
|
||||
if (hdr_checksum_check(hdr_disk->checksum_alg, hdr_disk,
|
||||
*json_area, hdr_json_size)) {
|
||||
log_dbg("LUKS2 header checksum error (offset %" PRIu64 ").", offset);
|
||||
r = -EINVAL;
|
||||
}
|
||||
memset(hdr_disk->csum, 0, LUKS2_CHECKSUM_L);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write LUKS2 header to disk at specific offset.
|
||||
*/
|
||||
static int hdr_write_disk(struct device *device, struct luks2_hdr *hdr,
|
||||
const char *json_area, int secondary)
|
||||
{
|
||||
struct luks2_hdr_disk hdr_disk;
|
||||
uint64_t offset = secondary ? hdr->hdr_size : 0;
|
||||
size_t hdr_json_len;
|
||||
int devfd = -1, r;
|
||||
|
||||
log_dbg("Trying to write LUKS2 header (%zu bytes) at offset %" PRIu64 ".",
|
||||
hdr->hdr_size, offset);
|
||||
|
||||
/* FIXME: read-only device silent fail? */
|
||||
|
||||
devfd = device_open_locked(device, O_RDWR);
|
||||
if (devfd < 0)
|
||||
return devfd == -1 ? -EINVAL : devfd;
|
||||
|
||||
hdr_json_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
|
||||
|
||||
hdr_to_disk(hdr, &hdr_disk, secondary, offset);
|
||||
|
||||
/*
|
||||
* Write header without checksum but with proper seqid.
|
||||
*/
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), (char *)&hdr_disk,
|
||||
LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN) {
|
||||
close(devfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write json area.
|
||||
*/
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device),
|
||||
CONST_CAST(char*)json_area, hdr_json_len,
|
||||
LUKS2_HDR_BIN_LEN + offset) < (ssize_t)hdr_json_len) {
|
||||
close(devfd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate checksum and write header with checkum.
|
||||
*/
|
||||
r = hdr_checksum_calculate(hdr_disk.checksum_alg, &hdr_disk,
|
||||
json_area, hdr_json_len);
|
||||
if (r < 0) {
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
log_dbg_checksum(hdr_disk.csum, hdr_disk.checksum_alg, "in-memory");
|
||||
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), (char *)&hdr_disk,
|
||||
LUKS2_HDR_BIN_LEN, offset) < (ssize_t)LUKS2_HDR_BIN_LEN)
|
||||
r = -EIO;
|
||||
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int LUKS2_check_device_size(struct crypt_device *cd, struct device *device, uint64_t hdr_size)
|
||||
{
|
||||
uint64_t dev_size;
|
||||
|
||||
if (device_size(device, &dev_size)) {
|
||||
log_dbg("Cannot get device size for device %s.", device_path(device));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
log_dbg("Device size %" PRIu64 ", header size %"
|
||||
PRIu64 ".", dev_size, hdr_size);
|
||||
|
||||
if (hdr_size > dev_size) {
|
||||
log_err(cd, _("Device %s is too small. (LUKS2 requires at least %" PRIu64 " bytes.)\n"),
|
||||
device_path(device), hdr_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert in-memory LUKS2 header and write it to disk.
|
||||
* This will increase sequence id, write both header copies and calculate checksum.
|
||||
*/
|
||||
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr, struct device *device)
|
||||
{
|
||||
char *json_area;
|
||||
const char *json_text;
|
||||
size_t json_area_len;
|
||||
int r;
|
||||
|
||||
if (hdr->version != 2) {
|
||||
log_dbg("Unsupported LUKS2 header version (%u).", hdr->version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->hdr_size != LUKS2_HDR_16K_LEN) {
|
||||
log_dbg("Unsupported LUKS2 header size (%zu).", hdr->hdr_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = LUKS2_check_device_size(cd, crypt_metadata_device(cd), LUKS2_hdr_and_areas_size(hdr->jobj));
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* Allocate and zero JSON area (of proper header size).
|
||||
*/
|
||||
json_area_len = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
|
||||
json_area = malloc(json_area_len);
|
||||
if (!json_area)
|
||||
return -ENOMEM;
|
||||
memset(json_area, 0, json_area_len);
|
||||
|
||||
/*
|
||||
* Generate text space-efficient JSON representation to json area.
|
||||
*/
|
||||
json_text = json_object_to_json_string_ext(hdr->jobj, JSON_C_TO_STRING_PLAIN);
|
||||
if (!json_text || !*json_text) {
|
||||
log_dbg("Cannot parse JSON object to text representation.");
|
||||
free(json_area);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (strlen(json_text) > (json_area_len - 1)) {
|
||||
log_dbg("JSON is too large (%zu > %zu).", strlen(json_text), json_area_len);
|
||||
free(json_area);
|
||||
return -EINVAL;
|
||||
}
|
||||
strncpy(json_area, json_text, json_area_len);
|
||||
|
||||
/* Increase sequence id before writing it to disk. */
|
||||
hdr->seqid++;
|
||||
|
||||
r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write device lock.\n"));
|
||||
free(json_area);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Write primary and secondary header */
|
||||
r = hdr_write_disk(device, hdr, json_area, 0);
|
||||
if (!r)
|
||||
r = hdr_write_disk(device, hdr, json_area, 1);
|
||||
|
||||
if (r)
|
||||
log_dbg("LUKS2 header write failed (%d).", r);
|
||||
|
||||
device_write_unlock(device);
|
||||
|
||||
/* FIXME: try recovery here? */
|
||||
|
||||
free(json_area);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int validate_json_area(const char *json_area, int start, int length)
|
||||
{
|
||||
char c;
|
||||
|
||||
/* Enforce there are no needless opening bytes */
|
||||
if (*json_area != '{') {
|
||||
log_dbg("ERROR: Opening character must be left curly bracket: '{'.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (start >= length) {
|
||||
log_dbg("ERROR: Missing trailing null byte beyond parsed json data string.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* validate there are legal json format characters between
|
||||
* 'json_area' and 'json_area + start'
|
||||
*/
|
||||
|
||||
do {
|
||||
c = *(json_area + start);
|
||||
if (c != '\0') {
|
||||
log_dbg("ERROR: Forbidden ascii code 0x%02hhx found beyond json data string at offset %d.",
|
||||
c, start);
|
||||
return -EINVAL;
|
||||
}
|
||||
} while (++start < length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_luks2_json_object(json_object *jobj_hdr)
|
||||
{
|
||||
int r;
|
||||
|
||||
/* we require top level object to be of json_type_object */
|
||||
r = !json_object_is_type(jobj_hdr, json_type_object);
|
||||
if (r) {
|
||||
log_dbg("ERROR: Resulting object is not a json object type");
|
||||
return r;
|
||||
}
|
||||
|
||||
r = LUKS2_hdr_validate(jobj_hdr);
|
||||
if (r)
|
||||
log_dbg("ERROR: LUKS2 validation failed");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static json_object *parse_and_validate_json(const char *json_area, int length)
|
||||
{
|
||||
int offset, r;
|
||||
json_object *jobj = parse_json_len(json_area, length, &offset);
|
||||
|
||||
if (!jobj)
|
||||
return NULL;
|
||||
|
||||
/* successfull parse_json_len must not return offset <= 0 */
|
||||
assert(offset > 0);
|
||||
|
||||
r = validate_json_area(json_area, offset, length);
|
||||
if (!r)
|
||||
r = validate_luks2_json_object(jobj);
|
||||
|
||||
if (r) {
|
||||
json_object_put(jobj);
|
||||
jobj = NULL;
|
||||
}
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and convert on-disk LUKS2 header to in-memory representation..
|
||||
* Try to do recovery if on-disk state is not consistent.
|
||||
*/
|
||||
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
struct device *device, int do_recovery)
|
||||
{
|
||||
enum { HDR_OK, HDR_OBSOLETE, HDR_FAIL, HDR_FAIL_IO } state_hdr1, state_hdr2;
|
||||
struct luks2_hdr_disk hdr_disk1, hdr_disk2;
|
||||
char *json_area1 = NULL, *json_area2 = NULL;
|
||||
json_object *jobj_hdr1 = NULL, *jobj_hdr2 = NULL;
|
||||
int i, r;
|
||||
uint64_t hdr_size;
|
||||
|
||||
if (do_recovery && !crypt_metadata_locking_enabled()) {
|
||||
do_recovery = 0;
|
||||
log_dbg("Disabling header auto-recovery due to locking being disabled.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Read primary LUKS2 header (offset 0).
|
||||
*/
|
||||
state_hdr1 = HDR_FAIL;
|
||||
r = hdr_read_disk(device, &hdr_disk1, &json_area1, 0, 0);
|
||||
if (r == 0) {
|
||||
jobj_hdr1 = parse_and_validate_json(json_area1, be64_to_cpu(hdr_disk1.hdr_size) - LUKS2_HDR_BIN_LEN);
|
||||
state_hdr1 = jobj_hdr1 ? HDR_OK : HDR_OBSOLETE;
|
||||
} else if (r == -EIO)
|
||||
state_hdr1 = HDR_FAIL_IO;
|
||||
|
||||
/*
|
||||
* Read secondary LUKS2 header (follows primary).
|
||||
*/
|
||||
state_hdr2 = HDR_FAIL;
|
||||
if (state_hdr1 != HDR_FAIL && state_hdr1 != HDR_FAIL_IO) {
|
||||
r = hdr_read_disk(device, &hdr_disk2, &json_area2, be64_to_cpu(hdr_disk1.hdr_size), 1);
|
||||
if (r == 0) {
|
||||
jobj_hdr2 = parse_and_validate_json(json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
|
||||
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
|
||||
} else if (r == -EIO)
|
||||
state_hdr2 = HDR_FAIL_IO;
|
||||
} else {
|
||||
/*
|
||||
* No header size, check all known offsets.
|
||||
*/
|
||||
for (r = -EINVAL,i = 2; r < 0 && i <= 1024; i <<= 1)
|
||||
r = hdr_read_disk(device, &hdr_disk2, &json_area2, i * 4096, 1);
|
||||
|
||||
if (r == 0) {
|
||||
jobj_hdr2 = parse_and_validate_json(json_area2, be64_to_cpu(hdr_disk2.hdr_size) - LUKS2_HDR_BIN_LEN);
|
||||
state_hdr2 = jobj_hdr2 ? HDR_OK : HDR_OBSOLETE;
|
||||
} else if (r == -EIO)
|
||||
state_hdr2 = HDR_FAIL_IO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check sequence id if both headers are read correctly.
|
||||
*/
|
||||
if (state_hdr1 == HDR_OK && state_hdr2 == HDR_OK) {
|
||||
if (be64_to_cpu(hdr_disk1.seqid) > be64_to_cpu(hdr_disk2.seqid))
|
||||
state_hdr2 = HDR_OBSOLETE;
|
||||
else if (be64_to_cpu(hdr_disk1.seqid) < be64_to_cpu(hdr_disk2.seqid))
|
||||
state_hdr1 = HDR_OBSOLETE;
|
||||
}
|
||||
|
||||
/* check header with keyslots fit the device */
|
||||
if (state_hdr1 == HDR_OK)
|
||||
hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr1);
|
||||
else if (state_hdr2 == HDR_OK)
|
||||
hdr_size = LUKS2_hdr_and_areas_size(jobj_hdr2);
|
||||
else {
|
||||
r = (state_hdr1 == HDR_FAIL_IO && state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
r = LUKS2_check_device_size(cd, device, hdr_size);
|
||||
if (r)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Try to rewrite (recover) bad header. Always regenerate salt for bad header.
|
||||
*/
|
||||
if (state_hdr1 == HDR_OK && state_hdr2 != HDR_OK) {
|
||||
log_dbg("Secondary LUKS2 header requires recovery.");
|
||||
|
||||
if (do_recovery) {
|
||||
memcpy(&hdr_disk2, &hdr_disk1, LUKS2_HDR_BIN_LEN);
|
||||
r = crypt_random_get(NULL, (char*)hdr_disk2.salt, sizeof(hdr_disk2.salt), CRYPT_RND_SALT);
|
||||
if (r)
|
||||
log_dbg("Cannot generate master salt.");
|
||||
else {
|
||||
hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
|
||||
r = hdr_write_disk(device, hdr, json_area1, 1);
|
||||
}
|
||||
if (r)
|
||||
log_dbg("Secondary LUKS2 header recovery failed.");
|
||||
}
|
||||
} else if (state_hdr1 != HDR_OK && state_hdr2 == HDR_OK) {
|
||||
log_dbg("Primary LUKS2 header requires recovery.");
|
||||
|
||||
if (do_recovery) {
|
||||
memcpy(&hdr_disk1, &hdr_disk2, LUKS2_HDR_BIN_LEN);
|
||||
r = crypt_random_get(NULL, (char*)hdr_disk1.salt, sizeof(hdr_disk1.salt), CRYPT_RND_SALT);
|
||||
if (r)
|
||||
log_dbg("Cannot generate master salt.");
|
||||
else {
|
||||
hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
|
||||
r = hdr_write_disk(device, hdr, json_area2, 0);
|
||||
}
|
||||
if (r)
|
||||
log_dbg("Primary LUKS2 header recovery failed.");
|
||||
}
|
||||
}
|
||||
|
||||
free(json_area1);
|
||||
json_area1 = NULL;
|
||||
free(json_area2);
|
||||
json_area2 = NULL;
|
||||
|
||||
/* wrong lock for write mode during recovery attempt */
|
||||
if (r == -EAGAIN)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Even if status is failed, the second header includes salt.
|
||||
*/
|
||||
if (state_hdr1 == HDR_OK) {
|
||||
hdr_from_disk(&hdr_disk1, &hdr_disk2, hdr, 0);
|
||||
hdr->jobj = jobj_hdr1;
|
||||
json_object_put(jobj_hdr2);
|
||||
return 0;
|
||||
} else if (state_hdr2 == HDR_OK) {
|
||||
hdr_from_disk(&hdr_disk2, &hdr_disk1, hdr, 1);
|
||||
hdr->jobj = jobj_hdr2;
|
||||
json_object_put(jobj_hdr1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = (state_hdr1 == HDR_FAIL_IO || state_hdr2 == HDR_FAIL_IO) ? -EIO : -EINVAL;
|
||||
|
||||
err:
|
||||
log_dbg("LUKS2 header read failed (%d).", r);
|
||||
|
||||
free(json_area1);
|
||||
free(json_area2);
|
||||
json_object_put(jobj_hdr1);
|
||||
json_object_put(jobj_hdr2);
|
||||
hdr->jobj = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_hdr_version_unlocked(struct crypt_device *cd)
|
||||
{
|
||||
struct {
|
||||
char magic[LUKS2_MAGIC_L];
|
||||
uint16_t version;
|
||||
} __attribute__ ((packed)) hdr;
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
int r, devfd, flags;
|
||||
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
flags = O_RDONLY;
|
||||
if (device_direct_io(device))
|
||||
flags |= O_DIRECT;
|
||||
|
||||
devfd = open(device_path(device), flags);
|
||||
if (devfd < 0)
|
||||
return 0;
|
||||
|
||||
if ((read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), &hdr, sizeof(hdr), 0)
|
||||
!= sizeof(hdr)) ||
|
||||
memcmp(hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
|
||||
r = 0;
|
||||
else
|
||||
r = (int)be16_to_cpu(hdr.version);
|
||||
|
||||
close(devfd);
|
||||
return r;
|
||||
}
|
||||
154
lib/luks2/luks2_internal.h
Normal file
154
lib/luks2/luks2_internal.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _CRYPTSETUP_LUKS2_INTERNAL_H
|
||||
#define _CRYPTSETUP_LUKS2_INTERNAL_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "internal.h"
|
||||
#include "base64.h"
|
||||
#include "luks2.h"
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
/*
|
||||
* On-disk access function prototypes
|
||||
*/
|
||||
int LUKS2_disk_hdr_read(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
struct device *device, int do_recovery);
|
||||
int LUKS2_disk_hdr_write(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
struct device *device);
|
||||
|
||||
/*
|
||||
* JSON struct access helpers
|
||||
*/
|
||||
json_object *LUKS2_get_keyslot_jobj(struct luks2_hdr *hdr, int keyslot);
|
||||
json_object *LUKS2_get_token_jobj(struct luks2_hdr *hdr, int token);
|
||||
json_object *LUKS2_get_digest_jobj(struct luks2_hdr *hdr, int keyslot);
|
||||
json_object *LUKS2_get_segment_jobj(struct luks2_hdr *hdr, int segment);
|
||||
|
||||
void hexprint_base64(struct crypt_device *cd, json_object *jobj,
|
||||
const char *sep, const char *line_sep);
|
||||
|
||||
json_object *parse_json_len(const char *json_area, int length, int *end_offset);
|
||||
uint64_t json_object_get_uint64(json_object *jobj);
|
||||
uint32_t json_object_get_uint32(json_object *jobj);
|
||||
|
||||
void JSON_DBG(json_object *jobj, const char *desc);
|
||||
|
||||
/*
|
||||
* LUKS2 JSON validation
|
||||
*/
|
||||
|
||||
int LUKS2_hdr_validate(json_object *hdr_jobj);
|
||||
int LUKS2_keyslot_validate(json_object *hdr_jobj, json_object *hdr_keyslot, const char *key);
|
||||
int LUKS2_check_json_size(const struct luks2_hdr *hdr);
|
||||
int LUKS2_token_validate(json_object *hdr_jobj, json_object *jobj_token, const char *key);
|
||||
void LUKS2_token_dump(struct crypt_device *cd, int token);
|
||||
|
||||
/*
|
||||
* JSON array helpers
|
||||
*/
|
||||
struct json_object *LUKS2_array_jobj(struct json_object *array, const char *num);
|
||||
struct json_object *LUKS2_array_remove(struct json_object *array, const char *num);
|
||||
|
||||
/*
|
||||
* Plugins API
|
||||
*/
|
||||
|
||||
/**
|
||||
* LUKS2 keyslots handlers (EXPERIMENTAL)
|
||||
*/
|
||||
typedef int (*keyslot_alloc_func)(struct crypt_device *cd, int keyslot,
|
||||
size_t volume_key_len);
|
||||
typedef int (*keyslot_open_func) (struct crypt_device *cd, int keyslot,
|
||||
const char *password, size_t password_len,
|
||||
char *volume_key, size_t volume_key_len);
|
||||
typedef int (*keyslot_store_func)(struct crypt_device *cd, int keyslot,
|
||||
const char *password, size_t password_len,
|
||||
const char *volume_key, size_t volume_key_len);
|
||||
typedef int (*keyslot_wipe_func) (struct crypt_device *cd, int keyslot);
|
||||
typedef int (*keyslot_dump_func) (struct crypt_device *cd, int keyslot);
|
||||
typedef int (*keyslot_validate_func) (struct crypt_device *cd, int keyslot);
|
||||
|
||||
int luks2_keyslot_alloc(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
size_t volume_key_len);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
keyslot_alloc_func alloc;
|
||||
keyslot_open_func open;
|
||||
keyslot_store_func store;
|
||||
keyslot_wipe_func wipe;
|
||||
keyslot_dump_func dump;
|
||||
keyslot_validate_func validate;
|
||||
} keyslot_handler;
|
||||
|
||||
/**
|
||||
* LUKS2 digest handlers (EXPERIMENTAL)
|
||||
*/
|
||||
typedef int (*digest_verify_func)(struct crypt_device *cd, int digest,
|
||||
const char *volume_key, size_t volume_key_len);
|
||||
typedef int (*digest_store_func) (struct crypt_device *cd, int digest,
|
||||
const char *volume_key, size_t volume_key_len);
|
||||
typedef int (*digest_dump_func) (struct crypt_device *cd, int digest);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
digest_verify_func verify;
|
||||
digest_store_func store;
|
||||
digest_dump_func dump;
|
||||
} digest_handler;
|
||||
|
||||
int crypt_digest_register(const digest_handler *handler);
|
||||
const digest_handler *LUKS2_digest_handler_type(struct crypt_device *cd, const char *type);
|
||||
|
||||
#define CRYPT_ANY_DIGEST -1
|
||||
int crypt_keyslot_assign_digest(struct crypt_device *cd, int keyslot, int digest);
|
||||
int crypt_keyslot_unassign_digest(struct crypt_device *cd, int keyslot, int digest);
|
||||
|
||||
/**
|
||||
* LUKS2 token handlers (internal use only)
|
||||
*/
|
||||
typedef int (*builtin_token_get_func) (json_object *jobj_token, void *params);
|
||||
typedef int (*builtin_token_set_func) (json_object **jobj_token, const void *params);
|
||||
|
||||
typedef struct {
|
||||
/* internal only section used by builtin tokens */
|
||||
builtin_token_get_func get;
|
||||
builtin_token_set_func set;
|
||||
/* public token handler */
|
||||
const crypt_token_handler *h;
|
||||
} token_handler;
|
||||
|
||||
int token_keyring_set(json_object **, const void *);
|
||||
int token_keyring_get(json_object *, void *);
|
||||
|
||||
#define CRYPT_ANY_TOKEN -1
|
||||
int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
size_t keylength, uint64_t *area_offset, uint64_t *area_length);
|
||||
|
||||
#endif
|
||||
233
lib/luks2/luks2_json_format.c
Normal file
233
lib/luks2/luks2_json_format.c
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, LUKS2 header format code
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luks2_internal.h"
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
struct area {
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
};
|
||||
|
||||
static size_t get_area_size(size_t keylength)
|
||||
{
|
||||
//FIXME: calculate this properly, for now it is AF_split_sectors
|
||||
return size_round_up(keylength * 4000, 4096);
|
||||
}
|
||||
|
||||
static size_t get_min_offset(struct luks2_hdr *hdr)
|
||||
{
|
||||
return 2 * hdr->hdr_size;
|
||||
}
|
||||
|
||||
static size_t get_max_offset(struct crypt_device *cd)
|
||||
{
|
||||
return crypt_get_data_offset(cd) * SECTOR_SIZE;
|
||||
}
|
||||
|
||||
int LUKS2_find_area_gap(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
size_t keylength, uint64_t *area_offset, uint64_t *area_length)
|
||||
{
|
||||
struct area areas[LUKS2_KEYSLOTS_MAX], sorted_areas[LUKS2_KEYSLOTS_MAX] = {};
|
||||
int i, j, k, area_i;
|
||||
size_t offset, length;
|
||||
|
||||
/* fill area offset + length table */
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
if (!LUKS2_keyslot_area(hdr, i, &areas[i].offset, &areas[i].length))
|
||||
continue;
|
||||
areas[i].length = 0;
|
||||
areas[i].offset = 0;
|
||||
}
|
||||
|
||||
/* sort table */
|
||||
k = 0; /* index in sorted table */
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
offset = get_max_offset(cd) ?: UINT64_MAX;
|
||||
area_i = -1;
|
||||
/* search for the smallest offset in table */
|
||||
for (j = 0; j < LUKS2_KEYSLOTS_MAX; j++)
|
||||
if (areas[j].offset && areas[j].offset <= offset) {
|
||||
area_i = j;
|
||||
offset = areas[j].offset;
|
||||
}
|
||||
|
||||
if (area_i >= 0) {
|
||||
sorted_areas[k].length = areas[area_i].length;
|
||||
sorted_areas[k].offset = areas[area_i].offset;
|
||||
areas[area_i].length = 0;
|
||||
areas[area_i].offset = 0;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
/* search for the gap we can use */
|
||||
offset = get_min_offset(hdr);
|
||||
length = get_area_size(keylength);
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
/* skip empty */
|
||||
if (sorted_areas[i].offset == 0 || sorted_areas[i].length == 0)
|
||||
continue;
|
||||
|
||||
/* enough space before the used area */
|
||||
if ((offset < sorted_areas[i].offset) && ((offset + length) <= sorted_areas[i].offset))
|
||||
break;
|
||||
|
||||
/* both offset and length are already aligned to 4096 bytes */
|
||||
offset = sorted_areas[i].offset + sorted_areas[i].length;
|
||||
}
|
||||
|
||||
if (get_max_offset(cd) && (offset + length) > get_max_offset(cd)) {
|
||||
log_err(cd, _("No space for new keyslot.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_dbg("Found area %zu -> %zu", offset, length + offset);
|
||||
/*
|
||||
log_dbg("Area offset min: %zu, max %zu, slots max %u",
|
||||
get_min_offset(hdr), get_max_offset(cd), LUKS2_KEYSLOTS_MAX);
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
|
||||
log_dbg("SLOT[%02i]: %-8" PRIu64 " -> %-8" PRIu64, i,
|
||||
sorted_areas[i].offset,
|
||||
sorted_areas[i].length + sorted_areas[i].offset);
|
||||
*/
|
||||
*area_offset = offset;
|
||||
*area_length = length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUKS2_generate_hdr(
|
||||
struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const struct volume_key *vk,
|
||||
const char *cipherName,
|
||||
const char *cipherMode,
|
||||
const char *integrity,
|
||||
const char *uuid,
|
||||
unsigned int sector_size,
|
||||
unsigned int alignPayload,
|
||||
unsigned int alignOffset,
|
||||
int detached_metadata_device)
|
||||
{
|
||||
struct json_object *jobj_segment, *jobj_integrity, *jobj_keyslots, *jobj_segments, *jobj_config;
|
||||
char num[24], cipher[128];
|
||||
uint64_t offset, json_size, keyslots_size;
|
||||
uuid_t partitionUuid;
|
||||
int digest;
|
||||
|
||||
hdr->hdr_size = LUKS2_HDR_16K_LEN;
|
||||
hdr->seqid = 1;
|
||||
hdr->version = 2;
|
||||
memset(hdr->label, 0, LUKS2_LABEL_L);
|
||||
strcpy(hdr->checksum_alg, "sha256");
|
||||
crypt_random_get(NULL, (char*)hdr->salt1, LUKS2_SALT_L, CRYPT_RND_SALT);
|
||||
crypt_random_get(NULL, (char*)hdr->salt2, LUKS2_SALT_L, CRYPT_RND_SALT);
|
||||
|
||||
if (uuid && uuid_parse(uuid, partitionUuid) == -1) {
|
||||
log_err(cd, _("Wrong LUKS UUID format provided.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!uuid)
|
||||
uuid_generate(partitionUuid);
|
||||
|
||||
uuid_unparse(partitionUuid, hdr->uuid);
|
||||
|
||||
if (*cipherMode != '\0')
|
||||
snprintf(cipher, sizeof(cipher), "%s-%s", cipherName, cipherMode);
|
||||
else
|
||||
snprintf(cipher, sizeof(cipher), "%s", cipherName);
|
||||
|
||||
hdr->jobj = json_object_new_object();
|
||||
|
||||
jobj_keyslots = json_object_new_object();
|
||||
json_object_object_add(hdr->jobj, "keyslots", jobj_keyslots);
|
||||
json_object_object_add(hdr->jobj, "tokens", json_object_new_object());
|
||||
jobj_segments = json_object_new_object();
|
||||
json_object_object_add(hdr->jobj, "segments", jobj_segments);
|
||||
json_object_object_add(hdr->jobj, "digests", json_object_new_object());
|
||||
jobj_config = json_object_new_object();
|
||||
json_object_object_add(hdr->jobj, "config", jobj_config);
|
||||
|
||||
digest = LUKS2_digest_create(cd, "pbkdf2", hdr, vk);
|
||||
if (digest < 0) {
|
||||
json_object_put(hdr->jobj);
|
||||
hdr->jobj = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (LUKS2_digest_segment_assign(cd, hdr, CRYPT_DEFAULT_SEGMENT, digest, 1, 0) < 0) {
|
||||
json_object_put(hdr->jobj);
|
||||
hdr->jobj = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
jobj_segment = json_object_new_object();
|
||||
json_object_object_add(jobj_segment, "type", json_object_new_string("crypt"));
|
||||
if (detached_metadata_device)
|
||||
offset = alignPayload * sector_size;
|
||||
else {
|
||||
//FIXME
|
||||
//offset = size_round_up(areas[7].offset + areas[7].length, alignPayload * SECTOR_SIZE);
|
||||
offset = size_round_up(LUKS2_HDR_DEFAULT_LEN, alignPayload * sector_size);
|
||||
offset += alignOffset;
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_segment, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
|
||||
json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string("0"));
|
||||
json_object_object_add(jobj_segment, "size", json_object_new_string("dynamic"));
|
||||
json_object_object_add(jobj_segment, "encryption", json_object_new_string(cipher));
|
||||
json_object_object_add(jobj_segment, "sector_size", json_object_new_int(sector_size));
|
||||
|
||||
if (integrity) {
|
||||
jobj_integrity = json_object_new_object();
|
||||
json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
|
||||
json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
|
||||
json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
|
||||
json_object_object_add(jobj_segment, "integrity", jobj_integrity);
|
||||
}
|
||||
|
||||
snprintf(num, sizeof(num), "%u", CRYPT_DEFAULT_SEGMENT);
|
||||
json_object_object_add(jobj_segments, num, jobj_segment);
|
||||
|
||||
json_size = hdr->hdr_size - LUKS2_HDR_BIN_LEN;
|
||||
json_object_object_add(jobj_config, "json_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &json_size)));
|
||||
|
||||
/* for detached metadata device compute reasonable keyslot areas size */
|
||||
// FIXME: this is coupled with FIXME above
|
||||
if (detached_metadata_device)
|
||||
keyslots_size = LUKS2_HDR_DEFAULT_LEN - get_min_offset(hdr);
|
||||
else
|
||||
keyslots_size = offset - get_min_offset(hdr);
|
||||
|
||||
/* keep keyslots_size reasonable for custom data alignments */
|
||||
if (keyslots_size > LUKS2_MAX_KEYSLOTS_SIZE)
|
||||
keyslots_size = LUKS2_MAX_KEYSLOTS_SIZE;
|
||||
|
||||
/* keyslots size has to be 4 KiB aligned */
|
||||
keyslots_size -= (keyslots_size % 4096);
|
||||
|
||||
json_object_object_add(jobj_config, "keyslots_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &keyslots_size)));
|
||||
|
||||
JSON_DBG(hdr->jobj, "Header JSON");
|
||||
return 0;
|
||||
}
|
||||
1810
lib/luks2/luks2_json_metadata.c
Normal file
1810
lib/luks2/luks2_json_metadata.c
Normal file
File diff suppressed because it is too large
Load Diff
477
lib/luks2/luks2_keyslot.c
Normal file
477
lib/luks2/luks2_keyslot.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, keyslot handling
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
/* Internal implementations */
|
||||
extern const keyslot_handler luks2_keyslot;
|
||||
|
||||
static const keyslot_handler *keyslot_handlers[LUKS2_KEYSLOTS_MAX] = {
|
||||
&luks2_keyslot,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const keyslot_handler
|
||||
*LUKS2_keyslot_handler_type(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX && keyslot_handlers[i]; i++) {
|
||||
if (!strcmp(keyslot_handlers[i]->name, type))
|
||||
return keyslot_handlers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const keyslot_handler
|
||||
*LUKS2_keyslot_handler(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj1, *jobj2;
|
||||
|
||||
if (keyslot < 0)
|
||||
return NULL;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return NULL;
|
||||
|
||||
if (!(jobj1 = LUKS2_get_keyslot_jobj(hdr, keyslot)))
|
||||
return NULL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
|
||||
return NULL;
|
||||
|
||||
return LUKS2_keyslot_handler_type(cd, json_object_get_string(jobj2));
|
||||
}
|
||||
|
||||
static crypt_keyslot_info LUKS2_keyslot_active(struct luks2_hdr *hdr, int keyslot)
|
||||
{
|
||||
if (keyslot >= LUKS2_KEYSLOTS_MAX)
|
||||
return CRYPT_SLOT_INVALID;
|
||||
|
||||
return LUKS2_get_keyslot_jobj(hdr, keyslot) ? CRYPT_SLOT_ACTIVE : CRYPT_SLOT_INACTIVE;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_find_empty(struct luks2_hdr *hdr, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++)
|
||||
if (!LUKS2_get_keyslot_jobj(hdr, i))
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int digests_by_segment(json_object *jobj_digests, const char *segment,
|
||||
digests_t digests)
|
||||
{
|
||||
json_object *jobj_segs;
|
||||
int i = 0;
|
||||
|
||||
json_object_object_foreach(jobj_digests, dig, val) {
|
||||
json_object_object_get_ex(val, "segments", &jobj_segs);
|
||||
if (LUKS2_array_jobj(jobj_segs, segment))
|
||||
digests[i++] = atoi(dig);
|
||||
}
|
||||
|
||||
if (i < LUKS2_DIGEST_MAX)
|
||||
digests[i] = -1;
|
||||
|
||||
return i ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
static int is_in(const int super[], int super_size, int elem)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < super_size && super[i] != -1; i++)
|
||||
if (super[i] == elem)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_subset(const int super[], const int sub[], int super_size)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < super_size && sub[i] != -1; i++)
|
||||
if (!is_in(super, super_size, sub[i]))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_for_segment(struct luks2_hdr *hdr, int keyslot, int segment)
|
||||
{
|
||||
char keyslot_name[16], segment_name[16];
|
||||
digests_t keyslot_digests, segment_digests;
|
||||
json_object *jobj_digests;
|
||||
int r = -ENOENT;
|
||||
|
||||
/* no need to check anything */
|
||||
if (segment == CRYPT_ANY_SEGMENT)
|
||||
return 0;
|
||||
|
||||
if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1 ||
|
||||
snprintf(keyslot_name, sizeof(keyslot_name), "%u", keyslot) < 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* empty set is subset of any set and it'd be wrong */
|
||||
json_object_object_get_ex(hdr->jobj, "digests", &jobj_digests);
|
||||
r = LUKS2_digests_by_keyslot(NULL, hdr, keyslot, keyslot_digests);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/* empty set can't be superset of non-empty one */
|
||||
if (digests_by_segment(jobj_digests, segment_name, segment_digests))
|
||||
return r;
|
||||
|
||||
/*
|
||||
* keyslot may activate segment if set of digests for keyslot
|
||||
* is actually subset of set of digests for segment
|
||||
*/
|
||||
return is_subset(segment_digests, keyslot_digests, LUKS2_DIGEST_MAX) ? 0 : -ENOENT;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_active_count(struct luks2_hdr *hdr, int segment)
|
||||
{
|
||||
int num = 0;
|
||||
json_object *jobj_keyslots;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
|
||||
|
||||
/* keyslot digests must be subset of segment digests */
|
||||
json_object_object_foreach(jobj_keyslots, slot, val) {
|
||||
UNUSED(val);
|
||||
if (!LUKS2_keyslot_for_segment(hdr, atoi(slot), segment))
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot)
|
||||
{
|
||||
crypt_keyslot_info ki;
|
||||
|
||||
if(keyslot >= LUKS2_KEYSLOTS_MAX || keyslot < 0)
|
||||
return CRYPT_SLOT_INVALID;
|
||||
|
||||
ki = LUKS2_keyslot_active(hdr, keyslot);
|
||||
if (ki != CRYPT_SLOT_ACTIVE)
|
||||
return ki;
|
||||
|
||||
if (LUKS2_keyslot_active_count(hdr, CRYPT_DEFAULT_SEGMENT) == 1 && !LUKS2_keyslot_for_segment(hdr, keyslot, CRYPT_DEFAULT_SEGMENT))
|
||||
return CRYPT_SLOT_ACTIVE_LAST;
|
||||
|
||||
return CRYPT_SLOT_ACTIVE;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_area(struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
uint64_t *offset,
|
||||
uint64_t *length)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj_area, *jobj;
|
||||
|
||||
if(LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID)
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -ENOENT;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj))
|
||||
return -EINVAL;
|
||||
*offset = json_object_get_int64(jobj);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "size", &jobj))
|
||||
return -EINVAL;
|
||||
*length = json_object_get_int64(jobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int LUKS2_open_and_verify(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int segment,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
struct volume_key **vk)
|
||||
{
|
||||
const keyslot_handler *h;
|
||||
int key_size, r;
|
||||
|
||||
if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
|
||||
return -ENOENT;
|
||||
|
||||
r = LUKS2_keyslot_for_segment(hdr, keyslot, segment);
|
||||
if (r) {
|
||||
if (r == -ENOENT)
|
||||
log_dbg("Keyslot %d unusable for segment %d.", keyslot, segment);
|
||||
return r;
|
||||
}
|
||||
|
||||
key_size = LUKS2_get_volume_key_size(hdr, segment);
|
||||
if (key_size < 0)
|
||||
key_size = LUKS2_get_keyslot_key_size(hdr, keyslot);
|
||||
if (key_size < 0)
|
||||
return -EINVAL;
|
||||
|
||||
*vk = crypt_alloc_volume_key(key_size, NULL);
|
||||
if (!*vk)
|
||||
return -ENOMEM;
|
||||
|
||||
r = h->open(cd, keyslot, password, password_len, (*vk)->key, (*vk)->keylength);
|
||||
if (r < 0)
|
||||
log_dbg("Keyslot %d (%s) open failed with %d.", keyslot, h->name, r);
|
||||
else
|
||||
r = LUKS2_digest_verify(cd, hdr, *vk, keyslot);
|
||||
|
||||
if (r < 0) {
|
||||
crypt_free_volume_key(*vk);
|
||||
*vk = NULL;
|
||||
}
|
||||
|
||||
return r < 0 ? r : keyslot;
|
||||
}
|
||||
|
||||
static int LUKS2_keyslot_open_priority(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
crypt_keyslot_priority priority,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
int segment,
|
||||
struct volume_key **vk)
|
||||
{
|
||||
json_object *jobj_keyslots, *jobj;
|
||||
crypt_keyslot_priority slot_priority;
|
||||
int keyslot, r = -ENOENT;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
|
||||
|
||||
json_object_object_foreach(jobj_keyslots, slot, val) {
|
||||
if (!json_object_object_get_ex(val, "priority", &jobj))
|
||||
slot_priority = CRYPT_SLOT_PRIORITY_NORMAL;
|
||||
else
|
||||
slot_priority = json_object_get_int(jobj);
|
||||
|
||||
keyslot = atoi(slot);
|
||||
if (slot_priority != priority) {
|
||||
log_dbg("Keyslot %d priority %d != %d (required), skipped.",
|
||||
keyslot, slot_priority, priority);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk);
|
||||
|
||||
/* Do not retry for errors that are no -EPERM or -ENOENT,
|
||||
former meaning password wrong, latter key slot unusable for segment */
|
||||
if ((r != -EPERM) && (r != -ENOENT))
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_open(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
int segment,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
struct volume_key **vk)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
int r_prio, r = -EINVAL;
|
||||
|
||||
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT) {
|
||||
r_prio = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER,
|
||||
password, password_len, segment, vk);
|
||||
if (r_prio >= 0)
|
||||
r = r_prio;
|
||||
else if (r_prio < 0 && (r_prio != -EPERM) && (r_prio != -ENOENT))
|
||||
r = r_prio;
|
||||
else
|
||||
r = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_NORMAL,
|
||||
password, password_len, segment, vk);
|
||||
/* Prefer password wrong to no entry from priority slot */
|
||||
if (r_prio == -EPERM && r == -ENOENT)
|
||||
r = r_prio;
|
||||
} else
|
||||
r = LUKS2_open_and_verify(cd, hdr, keyslot, segment, password, password_len, vk);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_store(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
const struct volume_key *vk)
|
||||
{
|
||||
const keyslot_handler *h;
|
||||
int r;
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!LUKS2_get_keyslot_jobj(hdr, keyslot)) {
|
||||
/* Try to allocate default and empty keyslot type */
|
||||
h = LUKS2_keyslot_handler_type(cd, "luks2");
|
||||
if (!h)
|
||||
return -EINVAL;
|
||||
|
||||
r = h->alloc(cd, keyslot, vk->keylength);
|
||||
if (r)
|
||||
return r;
|
||||
} else if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
|
||||
return -EINVAL;
|
||||
|
||||
r = h->validate(cd, keyslot);
|
||||
if (r) {
|
||||
log_dbg("Keyslot validation failed.");
|
||||
return r;
|
||||
}
|
||||
|
||||
return h->store(cd, keyslot, password, password_len,
|
||||
vk->key, vk->keylength);
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_wipe(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int keyslot,
|
||||
int wipe_area_only)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
uint64_t area_offset, area_length;
|
||||
char num[16];
|
||||
int r;
|
||||
json_object *jobj_keyslot, *jobj_keyslots;
|
||||
const keyslot_handler *h;
|
||||
|
||||
h = LUKS2_keyslot_handler(cd, keyslot);
|
||||
|
||||
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -ENOENT;
|
||||
|
||||
if (wipe_area_only)
|
||||
log_dbg("Wiping keyslot %d area only.", keyslot);
|
||||
|
||||
/* Just check that nobody uses the metadata now */
|
||||
r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write lock on device %s.\n"),
|
||||
device_path(device));
|
||||
return r;
|
||||
}
|
||||
device_write_unlock(device);
|
||||
|
||||
/* secure deletion of possible key material in keyslot area */
|
||||
r = crypt_keyslot_area(cd, keyslot, &area_offset, &area_length);
|
||||
if (r && r != -ENOENT)
|
||||
return r;
|
||||
|
||||
/* We can destroy the binary keyslot area now without lock */
|
||||
if (!r) {
|
||||
r = crypt_wipe_device(cd, device, CRYPT_WIPE_SPECIAL, area_offset,
|
||||
area_length, area_length, NULL, NULL);
|
||||
if (r) {
|
||||
if (r == -EACCES) {
|
||||
log_err(cd, _("Cannot write to device %s, permission denied.\n"),
|
||||
device_path(device));
|
||||
r = -EINVAL;
|
||||
} else
|
||||
log_err(cd, _("Cannot wipe device %s.\n"), device_path(device));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
if (wipe_area_only)
|
||||
return r;
|
||||
|
||||
/* Slot specific wipe */
|
||||
if (h) {
|
||||
r = h->wipe(cd, keyslot);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
log_dbg("Wiping keyslot %d without specific-slot handler loaded.", keyslot);
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
json_object_object_del(jobj_keyslots, num);
|
||||
|
||||
return LUKS2_hdr_write(cd, hdr);
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_dump(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
const keyslot_handler *h;
|
||||
|
||||
if (!(h = LUKS2_keyslot_handler(cd, keyslot)))
|
||||
return -EINVAL;
|
||||
|
||||
return h->dump(cd, keyslot);
|
||||
}
|
||||
|
||||
crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr, int keyslot)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj_priority;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return CRYPT_SLOT_PRIORITY_INVALID;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "priority", &jobj_priority))
|
||||
return CRYPT_SLOT_PRIORITY_NORMAL;
|
||||
|
||||
return json_object_get_int(jobj_priority);
|
||||
}
|
||||
|
||||
int LUKS2_keyslot_priority_set(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, crypt_keyslot_priority priority, int commit)
|
||||
{
|
||||
json_object *jobj_keyslot;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
if (priority == CRYPT_SLOT_PRIORITY_NORMAL)
|
||||
json_object_object_del(jobj_keyslot, "priority");
|
||||
else
|
||||
json_object_object_add(jobj_keyslot, "priority", json_object_new_int(priority));
|
||||
|
||||
return commit ? LUKS2_hdr_write(cd, hdr) : 0;
|
||||
}
|
||||
687
lib/luks2/luks2_keyslot_luks2.c
Normal file
687
lib/luks2/luks2_keyslot_luks2.c
Normal file
@@ -0,0 +1,687 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, LUKS2 type keyslot handler
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
/* FIXME: move keyslot encryption to crypto backend */
|
||||
#include "../luks1/af.h"
|
||||
|
||||
#define LUKS_SALTSIZE 32
|
||||
#define LUKS_SLOT_ITERATIONS_MIN 1000
|
||||
#define LUKS_STRIPES 4000
|
||||
|
||||
static int luks2_encrypt_to_storage(char *src, size_t srcLength,
|
||||
const char *cipher, const char *cipher_mode,
|
||||
struct volume_key *vk, unsigned int sector,
|
||||
struct crypt_device *cd)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
|
||||
int r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write lock on device %s.\n"), device_path(device));
|
||||
return r;
|
||||
}
|
||||
r = LUKS_encrypt_to_storage(src, srcLength, cipher, cipher_mode, vk, sector, cd);
|
||||
device_write_unlock(crypt_metadata_device(cd));
|
||||
return r;
|
||||
#else
|
||||
struct crypt_storage *s;
|
||||
int devfd = -1, r;
|
||||
|
||||
/* Only whole sector writes supported */
|
||||
if (srcLength % SECTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Encrypt buffer */
|
||||
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
|
||||
if (r) {
|
||||
log_dbg("Userspace crypto wrapper cannot use %s-%s (%d).",
|
||||
cipher, cipher_mode, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = crypt_storage_encrypt(s, 0, srcLength / SECTOR_SIZE, src);
|
||||
crypt_storage_destroy(s);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = device_write_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire write lock on device %s.\n"),
|
||||
device_path(device));
|
||||
return r;
|
||||
}
|
||||
|
||||
devfd = device_open_locked(device, O_RDWR);
|
||||
if (devfd >= 0) {
|
||||
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
|
||||
write_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), src,
|
||||
srcLength) == -1)
|
||||
r = -EIO;
|
||||
else
|
||||
r = 0;
|
||||
close(devfd);
|
||||
} else
|
||||
r = -EIO;
|
||||
|
||||
device_write_unlock(device);
|
||||
|
||||
if (r)
|
||||
log_err(cd, _("IO error while encrypting keyslot.\n"));
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int luks2_decrypt_from_storage(char *dst, size_t dstLength,
|
||||
const char *cipher, const char *cipher_mode, struct volume_key *vk,
|
||||
unsigned int sector, struct crypt_device *cd)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
#ifndef ENABLE_AF_ALG /* Support for old kernel without Crypto API */
|
||||
int r = device_read_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire read lock on device %s.\n"), device_path(device));
|
||||
return r;
|
||||
}
|
||||
r = LUKS_decrypt_from_storage(dst, dstLength, cipher, cipher_mode, vk, sector, cd);
|
||||
device_read_unlock(crypt_metadata_device(cd));
|
||||
return r;
|
||||
#else
|
||||
struct crypt_storage *s;
|
||||
int devfd = -1, r;
|
||||
|
||||
/* Only whole sector writes supported */
|
||||
if (dstLength % SECTOR_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
|
||||
if (r) {
|
||||
log_dbg("Userspace crypto wrapper cannot use %s-%s (%d).",
|
||||
cipher, cipher_mode, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = device_read_lock(cd, device);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to acquire read lock on device %s.\n"),
|
||||
device_path(device));
|
||||
return r;
|
||||
}
|
||||
|
||||
devfd = device_open_locked(device, O_RDONLY);
|
||||
if (devfd >= 0) {
|
||||
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
|
||||
read_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), dst, dstLength) == -1)
|
||||
r = -EIO;
|
||||
else
|
||||
r = 0;
|
||||
close(devfd);
|
||||
} else
|
||||
r = -EIO;
|
||||
|
||||
device_read_unlock(device);
|
||||
|
||||
if (r) {
|
||||
log_err(cd, _("IO error while decrypting keyslot.\n"));
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Decrypt buffer */
|
||||
r = crypt_storage_decrypt(s, 0, dstLength / SECTOR_SIZE, dst);
|
||||
crypt_storage_destroy(s);
|
||||
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int luks2_keyslot_set_key(struct crypt_device *cd,
|
||||
json_object *jobj_keyslot,
|
||||
const char *password, size_t passwordLen,
|
||||
const char *volume_key, size_t volume_key_len)
|
||||
{
|
||||
struct volume_key *derived_key;
|
||||
char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
|
||||
char *AfKey = NULL, *salt_base64 = NULL;
|
||||
size_t AFEKSize, keyslot_key_len;
|
||||
json_object *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
uint64_t area_offset;
|
||||
const struct crypt_pbkdf_type *pbkdf;
|
||||
int r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
|
||||
return -EINVAL;
|
||||
area_offset = json_object_get_uint64(jobj2);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
|
||||
return -EINVAL;
|
||||
r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
|
||||
return -EINVAL;
|
||||
keyslot_key_len = json_object_get_int(jobj2);
|
||||
|
||||
pbkdf = crypt_get_pbkdf_type(cd);
|
||||
if (!pbkdf)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
|
||||
json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
|
||||
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
|
||||
} else {
|
||||
json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
|
||||
json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
|
||||
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
|
||||
|
||||
/*
|
||||
* Get salt and allocate derived key storage.
|
||||
*/
|
||||
r = crypt_random_get(cd, salt, LUKS_SALTSIZE, CRYPT_RND_SALT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
base64_encode_alloc(salt, LUKS_SALTSIZE, &salt_base64);
|
||||
if (!salt_base64)
|
||||
return -ENOMEM;
|
||||
json_object_object_add(jobj_kdf, "salt", json_object_new_string(salt_base64));
|
||||
free(salt_base64);
|
||||
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
|
||||
|
||||
json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash));
|
||||
|
||||
derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
|
||||
if (!derived_key)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Calculate keyslot content, split and store it to keyslot area.
|
||||
*/
|
||||
r = crypt_pbkdf(pbkdf->type, pbkdf->hash, password, passwordLen,
|
||||
salt, LUKS_SALTSIZE,
|
||||
derived_key->key, derived_key->keylength,
|
||||
pbkdf->iterations, pbkdf->max_memory_kb,
|
||||
pbkdf->parallel_threads);
|
||||
if (r < 0) {
|
||||
crypt_free_volume_key(derived_key);
|
||||
return r;
|
||||
}
|
||||
|
||||
// FIXME: verity key_size to AFEKSize
|
||||
AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
|
||||
AfKey = crypt_safe_alloc(AFEKSize);
|
||||
if (!AfKey) {
|
||||
crypt_free_volume_key(derived_key);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = AF_split(volume_key, AfKey, volume_key_len, LUKS_STRIPES, pbkdf->hash);
|
||||
|
||||
if (r == 0) {
|
||||
log_dbg("Updating keyslot area [0x%04x].", (unsigned)area_offset);
|
||||
/* FIXME: sector_offset should be size_t, fix LUKS_encrypt... accordingly */
|
||||
r = luks2_encrypt_to_storage(AfKey, AFEKSize, cipher, cipher_mode,
|
||||
derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
|
||||
}
|
||||
|
||||
crypt_safe_free(AfKey);
|
||||
crypt_free_volume_key(derived_key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
JSON_DBG(jobj_keyslot, "Keyslot JSON");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_get_key(struct crypt_device *cd,
|
||||
json_object *jobj_keyslot,
|
||||
const char *password, size_t passwordLen,
|
||||
char *volume_key, size_t volume_key_len)
|
||||
{
|
||||
struct volume_key *derived_key;
|
||||
char *AfKey;
|
||||
size_t AFEKSize;
|
||||
const char *hash = NULL, *af_hash = NULL, *kdf;
|
||||
char salt[LUKS_SALTSIZE], cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
|
||||
json_object *jobj1, *jobj2, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
uint32_t iterations, memory, parallel;
|
||||
uint64_t area_offset;
|
||||
size_t salt_len, keyslot_key_len;
|
||||
int r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
kdf = json_object_get_string(jobj1);
|
||||
if (!strcmp(kdf, CRYPT_KDF_PBKDF2)) {
|
||||
if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj2))
|
||||
return -EINVAL;
|
||||
hash = json_object_get_string(jobj2);
|
||||
if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj2))
|
||||
return -EINVAL;
|
||||
iterations = json_object_get_int(jobj2);
|
||||
memory = 0;
|
||||
parallel = 0;
|
||||
} else {
|
||||
if (!json_object_object_get_ex(jobj_kdf, "time", &jobj2))
|
||||
return -EINVAL;
|
||||
iterations = json_object_get_int(jobj2);
|
||||
if (!json_object_object_get_ex(jobj_kdf, "memory", &jobj2))
|
||||
return -EINVAL;
|
||||
memory = json_object_get_int(jobj2);
|
||||
if (!json_object_object_get_ex(jobj_kdf, "cpus", &jobj2))
|
||||
return -EINVAL;
|
||||
parallel = json_object_get_int(jobj2);
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj2))
|
||||
return -EINVAL;
|
||||
salt_len = LUKS_SALTSIZE;
|
||||
base64_decode(json_object_get_string(jobj2),
|
||||
json_object_get_string_len(jobj2),
|
||||
salt, &salt_len);
|
||||
if (salt_len != LUKS_SALTSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_af, "hash", &jobj2))
|
||||
return -EINVAL;
|
||||
af_hash = json_object_get_string(jobj2);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj2))
|
||||
return -EINVAL;
|
||||
area_offset = json_object_get_uint64(jobj2);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj2))
|
||||
return -EINVAL;
|
||||
r = crypt_parse_name_and_mode(json_object_get_string(jobj2), cipher, NULL, cipher_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_area, "key_size", &jobj2))
|
||||
return -EINVAL;
|
||||
keyslot_key_len = json_object_get_int(jobj2);
|
||||
|
||||
/*
|
||||
* Allocate derived key storage space.
|
||||
*/
|
||||
derived_key = crypt_alloc_volume_key(keyslot_key_len, NULL);
|
||||
if (!derived_key)
|
||||
return -ENOMEM;
|
||||
|
||||
AFEKSize = AF_split_sectors(volume_key_len, LUKS_STRIPES) * SECTOR_SIZE;
|
||||
AfKey = crypt_safe_alloc(AFEKSize);
|
||||
if (!AfKey) {
|
||||
crypt_free_volume_key(derived_key);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/*
|
||||
* Calculate derived key, decrypt keyslot content and merge it.
|
||||
*/
|
||||
r = crypt_pbkdf(kdf, hash, password, passwordLen,
|
||||
salt, LUKS_SALTSIZE,
|
||||
derived_key->key, derived_key->keylength,
|
||||
iterations, memory, parallel);
|
||||
|
||||
if (r == 0) {
|
||||
log_dbg("Reading keyslot area [0x%04x].", (unsigned)area_offset);
|
||||
/* FIXME: sector_offset should be size_t, fix LUKS_decrypt... accordingly */
|
||||
r = luks2_decrypt_from_storage(AfKey, AFEKSize, cipher, cipher_mode,
|
||||
derived_key, (unsigned)(area_offset / SECTOR_SIZE), cd);
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
r = AF_merge(AfKey, volume_key, volume_key_len, LUKS_STRIPES, af_hash);
|
||||
|
||||
crypt_free_volume_key(derived_key);
|
||||
crypt_safe_free(AfKey);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int luks2_keyslot_alloc(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
const struct crypt_pbkdf_type *pbkdf;
|
||||
char area_offset_string[24], area_length_string[24];
|
||||
char cipher[2 * MAX_CIPHER_LEN + 1], num[16];
|
||||
uint64_t area_offset, area_length;
|
||||
json_object *jobj_keyslots, *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
size_t keyslot_key_len;
|
||||
int r;
|
||||
|
||||
log_dbg("Trying to allocate LUKS2 keyslot %d.", keyslot);
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT)
|
||||
keyslot = LUKS2_keyslot_find_empty(hdr, "luks2");
|
||||
|
||||
if (keyslot < 0 || keyslot > LUKS2_KEYSLOTS_MAX)
|
||||
return -ENOMEM;
|
||||
|
||||
if (LUKS2_get_keyslot_jobj(hdr, keyslot)) {
|
||||
log_dbg("Cannot modify already active keyslot %d.", keyslot);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
|
||||
return -EINVAL;
|
||||
|
||||
r = LUKS2_find_area_gap(cd, hdr, volume_key_len, &area_offset, &area_length);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
pbkdf = crypt_get_pbkdf_type(cd);
|
||||
if (!pbkdf)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_benchmark_pbkdf_internal(cd, CONST_CAST(struct crypt_pbkdf_type *)pbkdf, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
jobj_keyslot = json_object_new_object();
|
||||
json_object_object_add(jobj_keyslot, "type", json_object_new_string("luks2"));
|
||||
json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(volume_key_len));
|
||||
|
||||
/* PBKDF object */
|
||||
jobj_kdf = json_object_new_object();
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(pbkdf->type));
|
||||
if (!strcmp(pbkdf->type, CRYPT_KDF_PBKDF2)) {
|
||||
json_object_object_add(jobj_kdf, "iterations", json_object_new_int(pbkdf->iterations));
|
||||
json_object_object_add(jobj_kdf, "hash", json_object_new_string(pbkdf->hash));
|
||||
json_object_object_add(jobj_kdf, "salt", json_object_new_string(""));
|
||||
} else {
|
||||
json_object_object_add(jobj_kdf, "time", json_object_new_int(pbkdf->iterations));
|
||||
json_object_object_add(jobj_kdf, "memory", json_object_new_int(pbkdf->max_memory_kb));
|
||||
json_object_object_add(jobj_kdf, "cpus", json_object_new_int(pbkdf->parallel_threads));
|
||||
json_object_object_add(jobj_kdf, "salt", json_object_new_string(""));
|
||||
}
|
||||
json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
|
||||
|
||||
/* AF object */
|
||||
jobj_af = json_object_new_object();
|
||||
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
|
||||
json_object_object_add(jobj_af, "hash", json_object_new_string(pbkdf->hash));
|
||||
json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
|
||||
json_object_object_add(jobj_keyslot, "af", jobj_af);
|
||||
|
||||
/* Area object */
|
||||
jobj_area = json_object_new_object();
|
||||
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
|
||||
|
||||
/* Slot encryption tries to use the same key size as fot the main algorithm */
|
||||
keyslot_key_len = volume_key_len - crypt_get_integrity_key_size(cd);
|
||||
|
||||
/* Cannot use metadata tags in keyslot */
|
||||
if (crypt_get_integrity_tag_size(cd)) {
|
||||
snprintf(cipher, sizeof(cipher), "aes-xts-plain64"); // FIXME: fixed cipher and key size can be wrong
|
||||
keyslot_key_len = 32;
|
||||
} else if (crypt_get_cipher_mode(cd))
|
||||
snprintf(cipher, sizeof(cipher), "%s-%s", crypt_get_cipher(cd), crypt_get_cipher_mode(cd));
|
||||
else
|
||||
snprintf(cipher, sizeof(cipher), "%s", crypt_get_cipher(cd));
|
||||
|
||||
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
|
||||
json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_key_len));
|
||||
uint64_to_str(area_offset_string, sizeof(area_offset_string), &area_offset);
|
||||
json_object_object_add(jobj_area, "offset", json_object_new_string(area_offset_string));
|
||||
uint64_to_str(area_length_string, sizeof(area_length_string), &area_length);
|
||||
json_object_object_add(jobj_area, "size", json_object_new_string(area_length_string));
|
||||
json_object_object_add(jobj_keyslot, "area", jobj_area);
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
|
||||
json_object_object_add(jobj_keyslots, num, jobj_keyslot);
|
||||
if (LUKS2_check_json_size(hdr)) {
|
||||
log_dbg("Not enough space in header json area for new keyslot.");
|
||||
json_object_object_del(jobj_keyslots, num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_open(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj_keyslot;
|
||||
|
||||
log_dbg("Trying to open LUKS2 keyslot %d.", keyslot);
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
return luks2_keyslot_get_key(cd, jobj_keyslot,
|
||||
password, password_len,
|
||||
volume_key, volume_key_len);
|
||||
}
|
||||
|
||||
static int luks2_keyslot_store(struct crypt_device *cd,
|
||||
int keyslot,
|
||||
const char *password,
|
||||
size_t password_len,
|
||||
const char *volume_key,
|
||||
size_t volume_key_len)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj_keyslot;
|
||||
int r;
|
||||
|
||||
log_dbg("Calculating attributes for LUKS2 keyslot %d.", keyslot);
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
r = luks2_keyslot_set_key(cd, jobj_keyslot,
|
||||
password, password_len,
|
||||
volume_key, volume_key_len);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = LUKS2_hdr_write(cd, hdr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return keyslot;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_wipe(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Remove any reference of deleted keyslot from digests and tokens */
|
||||
LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0);
|
||||
LUKS2_token_assign(cd, hdr, keyslot, CRYPT_ANY_TOKEN, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_dump(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj1, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_area, "encryption", &jobj1);
|
||||
log_std(cd, "\tCipher: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "type", &jobj1);
|
||||
log_std(cd, "\tPBKDF: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
|
||||
json_object_object_get_ex(jobj_kdf, "hash", &jobj1);
|
||||
log_std(cd, "\tHash: %s\n", json_object_get_string(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "iterations", &jobj1);
|
||||
log_std(cd, "\tIterations: %" PRIu64 "\n", json_object_get_uint64(jobj1));
|
||||
} else {
|
||||
json_object_object_get_ex(jobj_kdf, "time", &jobj1);
|
||||
log_std(cd, "\tTime: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "memory", &jobj1);
|
||||
log_std(cd, "\tMemory: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_kdf, "cpus", &jobj1);
|
||||
log_std(cd, "\tThreads: %" PRIu64 "\n", json_object_get_int64(jobj1));
|
||||
}
|
||||
json_object_object_get_ex(jobj_kdf, "salt", &jobj1);
|
||||
log_std(cd, "\tSalt: ");
|
||||
hexprint_base64(cd, jobj1, " ", " ");
|
||||
|
||||
|
||||
json_object_object_get_ex(jobj_af, "stripes", &jobj1);
|
||||
log_std(cd, "\tAF stripes: %u\n", json_object_get_int(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_area, "offset", &jobj1);
|
||||
log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
|
||||
|
||||
json_object_object_get_ex(jobj_area, "size", &jobj1);
|
||||
log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", json_object_get_uint64(jobj1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int contains(json_object *jobj, const char *key, json_type type)
|
||||
{
|
||||
json_object *sobj;
|
||||
|
||||
if (!json_object_object_get_ex(jobj, key, &sobj) ||
|
||||
!json_object_is_type(sobj, type))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luks2_keyslot_validate(struct crypt_device *cd, int keyslot)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj_keyslot, *jobj_kdf, *jobj_af, *jobj_area, *jobj1;
|
||||
char num[16];
|
||||
|
||||
hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
if (LUKS2_keyslot_validate(hdr->jobj, jobj_keyslot, num))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(json_object_get_string(jobj1), CRYPT_KDF_PBKDF2)) {
|
||||
if (!contains(jobj_kdf, "hash", json_type_string) ||
|
||||
!contains(jobj_kdf, "iterations", json_type_int) ||
|
||||
!contains(jobj_kdf, "salt", json_type_string))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (!contains(jobj_kdf, "time", json_type_int) ||
|
||||
!contains(jobj_kdf, "memory", json_type_int) ||
|
||||
!contains(jobj_kdf, "cpus", json_type_int) ||
|
||||
!contains(jobj_kdf, "salt", json_type_string))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_af, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
if (!strcmp(json_object_get_string(jobj1), "luks1")) {
|
||||
if (!contains(jobj_af, "hash", json_type_string) ||
|
||||
!contains(jobj_af, "stripes", json_type_int))
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
// FIXME check numbered
|
||||
if (!json_object_object_get_ex(jobj_area, "type", &jobj1))
|
||||
return -EINVAL;
|
||||
if (!strcmp(json_object_get_string(jobj1), "raw")) {
|
||||
if (!contains(jobj_area, "encryption", json_type_string) ||
|
||||
!contains(jobj_area, "key_size", json_type_int) ||
|
||||
!contains(jobj_area, "offset", json_type_string) ||
|
||||
!contains(jobj_area, "size", json_type_string))
|
||||
return -EINVAL;
|
||||
} else
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const keyslot_handler luks2_keyslot = {
|
||||
.name = "luks2",
|
||||
.alloc = luks2_keyslot_alloc,
|
||||
.open = luks2_keyslot_open,
|
||||
.store = luks2_keyslot_store,
|
||||
.wipe = luks2_keyslot_wipe,
|
||||
.dump = luks2_keyslot_dump,
|
||||
.validate = luks2_keyslot_validate,
|
||||
};
|
||||
794
lib/luks2/luks2_luks1_convert.c
Normal file
794
lib/luks2/luks2_luks1_convert.c
Normal file
@@ -0,0 +1,794 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, LUKS1 conversion code
|
||||
*
|
||||
* Copyright (C) 2015-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Ondrej Kozina. All rights reserved.
|
||||
* Copyright (C) 2015-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "luks2_internal.h"
|
||||
#include "../luks1/luks.h"
|
||||
#include "../luks1/af.h"
|
||||
|
||||
static int json_luks1_keyslot(const struct luks_phdr *hdr_v1, int keyslot, struct json_object **keyslot_object)
|
||||
{
|
||||
char *base64_str, cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L], num[24];
|
||||
size_t base64_len;
|
||||
struct json_object *keyslot_obj, *field, *jobj_kdf, *jobj_af, *jobj_area;
|
||||
uint64_t offset, area_size, offs_a, offs_b, length;
|
||||
|
||||
keyslot_obj = json_object_new_object();
|
||||
json_object_object_add(keyslot_obj, "type", json_object_new_string("luks2"));
|
||||
json_object_object_add(keyslot_obj, "key_size", json_object_new_int64(hdr_v1->keyBytes));
|
||||
|
||||
/* KDF */
|
||||
jobj_kdf = json_object_new_object();
|
||||
json_object_object_add(jobj_kdf, "type", json_object_new_string(CRYPT_KDF_PBKDF2));
|
||||
json_object_object_add(jobj_kdf, "hash", json_object_new_string(hdr_v1->hashSpec));
|
||||
json_object_object_add(jobj_kdf, "iterations", json_object_new_int64(hdr_v1->keyblock[keyslot].passwordIterations));
|
||||
/* salt field */
|
||||
base64_len = base64_encode_alloc(hdr_v1->keyblock[keyslot].passwordSalt, LUKS_SALTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(keyslot_obj);
|
||||
json_object_put(jobj_kdf);
|
||||
if (!base64_len)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
field = json_object_new_string_len(base64_str, base64_len);
|
||||
free(base64_str);
|
||||
json_object_object_add(jobj_kdf, "salt", field);
|
||||
json_object_object_add(keyslot_obj, "kdf", jobj_kdf);
|
||||
|
||||
/* AF */
|
||||
jobj_af = json_object_new_object();
|
||||
json_object_object_add(jobj_af, "type", json_object_new_string("luks1"));
|
||||
json_object_object_add(jobj_af, "hash", json_object_new_string(hdr_v1->hashSpec));
|
||||
/* stripes field ignored, fixed to LUKS_STRIPES (4000) */
|
||||
json_object_object_add(jobj_af, "stripes", json_object_new_int(4000));
|
||||
json_object_object_add(keyslot_obj, "af", jobj_af);
|
||||
|
||||
/* Area */
|
||||
jobj_area = json_object_new_object();
|
||||
json_object_object_add(jobj_area, "type", json_object_new_string("raw"));
|
||||
|
||||
/* encryption algorithm field */
|
||||
if (*hdr_v1->cipherMode != '\0') {
|
||||
(void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
|
||||
json_object_object_add(jobj_area, "encryption", json_object_new_string(cipher));
|
||||
} else
|
||||
json_object_object_add(jobj_area, "encryption", json_object_new_string(hdr_v1->cipherName));
|
||||
|
||||
/* area */
|
||||
if (LUKS_keyslot_area(hdr_v1, 0, &offs_a, &length) ||
|
||||
LUKS_keyslot_area(hdr_v1, 1, &offs_b, &length) ||
|
||||
LUKS_keyslot_area(hdr_v1, keyslot, &offset, &length)) {
|
||||
json_object_put(keyslot_obj);
|
||||
json_object_put(jobj_area);
|
||||
return -EINVAL;
|
||||
}
|
||||
area_size = offs_b - offs_a;
|
||||
json_object_object_add(jobj_area, "key_size", json_object_new_int(hdr_v1->keyBytes));
|
||||
json_object_object_add(jobj_area, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
|
||||
json_object_object_add(jobj_area, "size", json_object_new_string(uint64_to_str(num, sizeof(num), &area_size)));
|
||||
json_object_object_add(keyslot_obj, "area", jobj_area);
|
||||
|
||||
*keyslot_object = keyslot_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_keyslots(const struct luks_phdr *hdr_v1, struct json_object **keyslots_object)
|
||||
{
|
||||
char keyslot_str[2];
|
||||
int key_slot, r;
|
||||
struct json_object *keyslot_obj, *field;
|
||||
|
||||
keyslot_obj = json_object_new_object();
|
||||
if (!keyslot_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
for (key_slot = 0; key_slot < LUKS_NUMKEYS; key_slot++) {
|
||||
if (hdr_v1->keyblock[key_slot].active != LUKS_KEY_ENABLED)
|
||||
continue;
|
||||
r = json_luks1_keyslot(hdr_v1, key_slot, &field);
|
||||
if (r) {
|
||||
json_object_put(keyslot_obj);
|
||||
return r;
|
||||
}
|
||||
(void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", key_slot);
|
||||
json_object_object_add(keyslot_obj, keyslot_str, field);
|
||||
}
|
||||
|
||||
*keyslots_object = keyslot_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_segment(const struct luks_phdr *hdr_v1, struct json_object **segment_object)
|
||||
{
|
||||
const char *c;
|
||||
char cipher[LUKS_CIPHERNAME_L+LUKS_CIPHERMODE_L];
|
||||
char num[24]; /* uint64_t in string */
|
||||
struct json_object *segment_obj, *field;
|
||||
uint64_t number;
|
||||
|
||||
segment_obj = json_object_new_object();
|
||||
if (!segment_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* type field */
|
||||
field = json_object_new_string("crypt");
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "type", field);
|
||||
|
||||
/* offset field */
|
||||
number = hdr_v1->payloadOffset * SECTOR_SIZE;
|
||||
|
||||
field = json_object_new_string(uint64_to_str(num, sizeof(num), &number));
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "offset", field);
|
||||
|
||||
/* iv_tweak field */
|
||||
field = json_object_new_string("0");
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "iv_tweak", field);
|
||||
|
||||
/* length field */
|
||||
field = json_object_new_string("dynamic");
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "size", field);
|
||||
|
||||
/* cipher field */
|
||||
if (*hdr_v1->cipherMode != '\0') {
|
||||
(void) snprintf(cipher, sizeof(cipher), "%s-%s", hdr_v1->cipherName, hdr_v1->cipherMode);
|
||||
c = cipher;
|
||||
} else
|
||||
c = hdr_v1->cipherName;
|
||||
|
||||
field = json_object_new_string(c);
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "encryption", field);
|
||||
|
||||
/* block field */
|
||||
field = json_object_new_int(SECTOR_SIZE);
|
||||
if (!field) {
|
||||
json_object_put(segment_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(segment_obj, "sector_size", field);
|
||||
|
||||
*segment_object = segment_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_segments(const struct luks_phdr *hdr_v1, struct json_object **segments_object)
|
||||
{
|
||||
char num[16];
|
||||
int r;
|
||||
struct json_object *segments_obj, *field;
|
||||
|
||||
segments_obj = json_object_new_object();
|
||||
if (!segments_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
r = json_luks1_segment(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(segments_obj);
|
||||
return r;
|
||||
}
|
||||
snprintf(num, sizeof(num), "%u", CRYPT_DEFAULT_SEGMENT);
|
||||
json_object_object_add(segments_obj, num, field);
|
||||
|
||||
*segments_object = segments_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_digest(const struct luks_phdr *hdr_v1, struct json_object **digest_object)
|
||||
{
|
||||
char keyslot_str[2], *base64_str;
|
||||
int ks;
|
||||
size_t base64_len;
|
||||
struct json_object *digest_obj, *array, *field;
|
||||
|
||||
digest_obj = json_object_new_object();
|
||||
if (!digest_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* type field */
|
||||
field = json_object_new_string("pbkdf2");
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "type", field);
|
||||
|
||||
/* keyslots array */
|
||||
array = json_object_new_array();
|
||||
if (!array) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "keyslots", json_object_get(array));
|
||||
|
||||
for (ks = 0; ks < LUKS_NUMKEYS; ks++) {
|
||||
if (hdr_v1->keyblock[ks].active != LUKS_KEY_ENABLED)
|
||||
continue;
|
||||
(void) snprintf(keyslot_str, sizeof(keyslot_str), "%d", ks);
|
||||
|
||||
field = json_object_new_string(keyslot_str);
|
||||
if (!field || json_object_array_add(array, field) < 0) {
|
||||
json_object_put(field);
|
||||
json_object_put(array);
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(array);
|
||||
|
||||
/* segments array */
|
||||
array = json_object_new_array();
|
||||
if (!array) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "segments", json_object_get(array));
|
||||
|
||||
field = json_object_new_string("0");
|
||||
if (!field || json_object_array_add(array, field) < 0) {
|
||||
json_object_put(field);
|
||||
json_object_put(array);
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
json_object_put(array);
|
||||
|
||||
/* hash field */
|
||||
field = json_object_new_string(hdr_v1->hashSpec);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "hash", field);
|
||||
|
||||
/* salt field */
|
||||
base64_len = base64_encode_alloc(hdr_v1->mkDigestSalt, LUKS_SALTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(digest_obj);
|
||||
if (!base64_len)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
field = json_object_new_string_len(base64_str, base64_len);
|
||||
free(base64_str);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "salt", field);
|
||||
|
||||
/* digest field */
|
||||
base64_len = base64_encode_alloc(hdr_v1->mkDigest, LUKS_DIGESTSIZE, &base64_str);
|
||||
if (!base64_str) {
|
||||
json_object_put(digest_obj);
|
||||
if (!base64_len)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
field = json_object_new_string_len(base64_str, base64_len);
|
||||
free(base64_str);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "digest", field);
|
||||
|
||||
/* iterations field */
|
||||
field = json_object_new_int64(hdr_v1->mkDigestIterations);
|
||||
if (!field) {
|
||||
json_object_put(digest_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(digest_obj, "iterations", field);
|
||||
|
||||
*digest_object = digest_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_digests(const struct luks_phdr *hdr_v1, struct json_object **digests_object)
|
||||
{
|
||||
int r;
|
||||
struct json_object *digests_obj, *field;
|
||||
|
||||
digests_obj = json_object_new_object();
|
||||
if (!digests_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
r = json_luks1_digest(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(digests_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(digests_obj, "0", field);
|
||||
|
||||
*digests_object = digests_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_luks1_object(struct luks_phdr *hdr_v1, struct json_object **luks1_object, uint64_t keyslots_size)
|
||||
{
|
||||
char num[24];
|
||||
int r;
|
||||
struct json_object *luks1_obj, *field;
|
||||
uint64_t json_size;
|
||||
|
||||
luks1_obj = json_object_new_object();
|
||||
if (!luks1_obj)
|
||||
return -ENOMEM;
|
||||
|
||||
/* keyslots field */
|
||||
r = json_luks1_keyslots(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(luks1_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "keyslots", field);
|
||||
|
||||
/* tokens field */
|
||||
field = json_object_new_object();
|
||||
if (!field) {
|
||||
json_object_put(luks1_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "tokens", field);
|
||||
|
||||
/* segments field */
|
||||
r = json_luks1_segments(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(luks1_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "segments", field);
|
||||
|
||||
/* digests field */
|
||||
r = json_luks1_digests(hdr_v1, &field);
|
||||
if (r) {
|
||||
json_object_put(luks1_obj);
|
||||
return r;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "digests", field);
|
||||
|
||||
/* config field */
|
||||
/* anything else? */
|
||||
field = json_object_new_object();
|
||||
if (!field) {
|
||||
json_object_put(luks1_obj);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(luks1_obj, "config", field);
|
||||
|
||||
json_size = LUKS2_HDR_16K_LEN - LUKS2_HDR_BIN_LEN;
|
||||
json_object_object_add(field, "json_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &json_size)));
|
||||
json_object_object_add(field, "keyslots_size",
|
||||
json_object_new_string(uint64_to_str(num, sizeof(num), &keyslots_size)));
|
||||
|
||||
*luks1_object = luks1_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void move_keyslot_offset(json_object *jobj, int offset_add)
|
||||
{
|
||||
char num[24];
|
||||
json_object *jobj1, *jobj2, *jobj_area;
|
||||
uint64_t offset = 0;
|
||||
|
||||
json_object_object_get_ex(jobj, "keyslots", &jobj1);
|
||||
json_object_object_foreach(jobj1, key, val) {
|
||||
UNUSED(key);
|
||||
json_object_object_get_ex(val, "area", &jobj_area);
|
||||
json_object_object_get_ex(jobj_area, "offset", &jobj2);
|
||||
offset = json_object_get_uint64(jobj2) + offset_add;
|
||||
json_object_object_add(jobj_area, "offset", json_object_new_string(uint64_to_str(num, sizeof(num), &offset)));
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: return specific error code for partial write error (aka keyslots are gone) */
|
||||
static int move_keyslot_areas(struct crypt_device *cd, off_t offset_from,
|
||||
off_t offset_to, size_t buf_size)
|
||||
{
|
||||
struct device *device = crypt_metadata_device(cd);
|
||||
void *buf = NULL;
|
||||
int devfd = -1;
|
||||
|
||||
log_dbg("Moving keyslot areas of size %zu from %jd to %jd.",
|
||||
buf_size, (intmax_t)offset_from, (intmax_t)offset_to);
|
||||
|
||||
// FIXME: export aligned_malloc from utils
|
||||
if (posix_memalign(&buf, crypt_getpagesize(), buf_size))
|
||||
return -ENOMEM;
|
||||
|
||||
devfd = device_open(device, O_RDWR);
|
||||
if (devfd == -1) {
|
||||
log_dbg("Cannot open device %s.", device_path(device));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (read_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), buf, buf_size,
|
||||
offset_from)!= (ssize_t)buf_size) {
|
||||
close(devfd);
|
||||
free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (write_lseek_blockwise(devfd, device_block_size(device),
|
||||
device_alignment(device), buf, buf_size,
|
||||
offset_to) != (ssize_t)buf_size) {
|
||||
close(devfd);
|
||||
free(buf);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
close(devfd);
|
||||
crypt_memzero(buf, buf_size);
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luks_header_in_use(struct crypt_device *cd)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = lookup_dm_dev_by_uuid(crypt_get_uuid(cd), crypt_get_type(cd));
|
||||
if (r < 0)
|
||||
log_err(cd, _("Can not check status of device with uuid: %s.\n"), crypt_get_uuid(cd));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Convert LUKS1 -> LUKS2 */
|
||||
int LUKS2_luks1_to_luks2(struct crypt_device *cd, struct luks_phdr *hdr1, struct luks2_hdr *hdr2)
|
||||
{
|
||||
int r;
|
||||
json_object *jobj = NULL;
|
||||
size_t buf_size, buf_offset, luks1_size, luks1_shift = 2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS;
|
||||
uint64_t max_size = crypt_get_data_offset(cd) * SECTOR_SIZE;
|
||||
|
||||
/* for detached headers max size == device size */
|
||||
if (!max_size && (r = device_size(crypt_metadata_device(cd), &max_size)))
|
||||
return r;
|
||||
|
||||
luks1_size = LUKS_device_sectors(hdr1) << SECTOR_SHIFT;
|
||||
if (!luks1_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (LUKS_keyslots_offset(hdr1) != (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
|
||||
log_dbg("Unsupported keyslots material offset: %zu.", LUKS_keyslots_offset(hdr1));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_dbg("Max size: %" PRIu64 ", LUKS1 (full) header size %zu , required shift: %zu",
|
||||
max_size, luks1_size, luks1_shift);
|
||||
if ((max_size - luks1_size) < luks1_shift) {
|
||||
log_err(cd, _("Unable to move keyslot materials. Not enough space\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = json_luks1_object(hdr1, &jobj, max_size - 2 * LUKS2_HDR_16K_LEN);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
move_keyslot_offset(jobj, luks1_shift);
|
||||
|
||||
// fill hdr2
|
||||
memset(hdr2, 0, sizeof(*hdr2));
|
||||
hdr2->hdr_size = LUKS2_HDR_16K_LEN;
|
||||
hdr2->seqid = 1;
|
||||
hdr2->version = 2;
|
||||
strncpy(hdr2->checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
|
||||
crypt_random_get(cd, (char*)hdr2->salt1, sizeof(hdr2->salt1), CRYPT_RND_SALT);
|
||||
crypt_random_get(cd, (char*)hdr2->salt2, sizeof(hdr2->salt2), CRYPT_RND_SALT);
|
||||
strncpy(hdr2->uuid, crypt_get_uuid(cd), LUKS2_UUID_L);
|
||||
hdr2->jobj = jobj;
|
||||
|
||||
/*
|
||||
* It duplicates check in LUKS2_hdr_write() but we don't want to move
|
||||
* keyslot areas in case it would fail later
|
||||
*/
|
||||
if (max_size < LUKS2_hdr_and_areas_size(hdr2->jobj)) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((r = luks_header_in_use(cd))) {
|
||||
if (r > 0)
|
||||
r = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
// move keyslots 4k -> 32k offset
|
||||
buf_offset = 2 * LUKS2_HDR_16K_LEN;
|
||||
buf_size = luks1_size - LUKS_ALIGN_KEYSLOTS;
|
||||
if ((r = move_keyslot_areas(cd, 8 * SECTOR_SIZE, buf_offset, buf_size)) < 0)
|
||||
goto out;
|
||||
|
||||
// Write JSON hdr2
|
||||
r = LUKS2_hdr_write(cd, hdr2);
|
||||
out:
|
||||
LUKS2_hdr_free(hdr2);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int keyslot_LUKS1_compatible(struct luks2_hdr *hdr, int keyslot, uint32_t key_size)
|
||||
{
|
||||
json_object *jobj_keyslot, *jobj, *jobj_kdf, *jobj_af;
|
||||
uint64_t l2_offset, l2_length;
|
||||
int ks_key_size;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
|
||||
if (!jobj_keyslot)
|
||||
return 1;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "type", &jobj) ||
|
||||
strcmp(json_object_get_string(jobj), "luks2"))
|
||||
return 0;
|
||||
|
||||
/* Using PBKDF2, this implies memory and parallel is not used. */
|
||||
jobj = NULL;
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf) ||
|
||||
!json_object_object_get_ex(jobj_kdf, "type", &jobj) ||
|
||||
strcmp(json_object_get_string(jobj), CRYPT_KDF_PBKDF2))
|
||||
return 0;
|
||||
|
||||
jobj = NULL;
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "af", &jobj_af) ||
|
||||
!json_object_object_get_ex(jobj_af, "stripes", &jobj) ||
|
||||
json_object_get_int(jobj) != LUKS_STRIPES)
|
||||
return 0;
|
||||
|
||||
jobj = NULL;
|
||||
if (!json_object_object_get_ex(jobj_af, "hash", &jobj) ||
|
||||
crypt_hash_size(json_object_get_string(jobj)) < 0)
|
||||
return 0;
|
||||
|
||||
/* FIXME: should this go to validation code instead (aka invalid luks2 header if assigned to segment 0)? */
|
||||
ks_key_size = LUKS2_get_keyslot_key_size(hdr, keyslot);
|
||||
if (ks_key_size < 0 || (int)key_size != LUKS2_get_keyslot_key_size(hdr, keyslot)) {
|
||||
log_dbg("Key length in keyslot %d is different from volume key length", keyslot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LUKS2_keyslot_area(hdr, keyslot, &l2_offset, &l2_length))
|
||||
return 0;
|
||||
|
||||
if (l2_length != (size_round_up(AF_split_sectors(key_size, LUKS_STRIPES) * SECTOR_SIZE, 4096))) {
|
||||
log_dbg("Area length in LUKS2 keyslot (%d) is not compatible with LUKS1", keyslot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Convert LUKS2 -> LUKS1 */
|
||||
int LUKS2_luks2_to_luks1(struct crypt_device *cd, struct luks2_hdr *hdr2, struct luks_phdr *hdr1)
|
||||
{
|
||||
size_t buf_size, buf_offset;
|
||||
char cipher[LUKS_CIPHERNAME_L], cipher_mode[LUKS_CIPHERMODE_L];
|
||||
char digest[LUKS_DIGESTSIZE], digest_salt[LUKS_SALTSIZE];
|
||||
size_t len;
|
||||
json_object *jobj_keyslot, *jobj_digest, *jobj_segment, *jobj_kdf, *jobj_area, *jobj1, *jobj2;
|
||||
uint32_t key_size;
|
||||
int i, r, last_active = 0;
|
||||
uint64_t offset, area_length;
|
||||
char buf[256], luksMagic[] = LUKS_MAGIC;
|
||||
|
||||
jobj_digest = LUKS2_get_digest_jobj(hdr2, 0);
|
||||
if (!jobj_digest)
|
||||
return -EINVAL;
|
||||
|
||||
jobj_segment = LUKS2_get_segment_jobj(hdr2, CRYPT_DEFAULT_SEGMENT);
|
||||
if (!jobj_segment)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(hdr2->jobj, "digests", &jobj1);
|
||||
if (!json_object_object_get_ex(jobj_digest, "type", &jobj2) ||
|
||||
strcmp(json_object_get_string(jobj2), "pbkdf2") ||
|
||||
json_object_object_length(jobj1) != 1) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - key slot digests are not LUKS1 compatible.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key_size = r = LUKS2_get_volume_key_size(hdr2, 0);
|
||||
if (r < 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < LUKS2_KEYSLOTS_MAX; i++) {
|
||||
if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INACTIVE)
|
||||
continue;
|
||||
|
||||
if (LUKS2_keyslot_info(hdr2, i) == CRYPT_SLOT_INVALID) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is in invalid state.\n"), i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (i >= LUKS_NUMKEYS) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - slot %u (over maximum slots) is still active .\n"), i);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!keyslot_LUKS1_compatible(hdr2, i, key_size)) {
|
||||
log_err(cd, _("Cannot convert to LUKS1 format - keyslot %u is not LUKS1 compatible.\n"), i);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
memset(hdr1, 0, sizeof(*hdr1));
|
||||
|
||||
for (i = 0; i < LUKS_NUMKEYS; i++) {
|
||||
hdr1->keyblock[i].active = LUKS_KEY_DISABLED;
|
||||
hdr1->keyblock[i].stripes = LUKS_STRIPES;
|
||||
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, i);
|
||||
|
||||
if (jobj_keyslot) {
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
if (!json_object_object_get_ex(jobj_area, "offset", &jobj1))
|
||||
return -EINVAL;
|
||||
offset = json_object_get_uint64(jobj1);
|
||||
} else {
|
||||
if (LUKS2_find_area_gap(cd, hdr2, key_size, &offset, &area_length))
|
||||
return -EINVAL;
|
||||
/* FIXME: luks2 reload is required! */
|
||||
if (luks2_keyslot_alloc(cd, i, key_size))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset /= SECTOR_SIZE;
|
||||
if (offset > UINT32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
hdr1->keyblock[i].keyMaterialOffset = offset;
|
||||
hdr1->keyblock[i].keyMaterialOffset -=
|
||||
((2 * LUKS2_HDR_16K_LEN - LUKS_ALIGN_KEYSLOTS) / SECTOR_SIZE);
|
||||
|
||||
if (!jobj_keyslot)
|
||||
continue;
|
||||
|
||||
hdr1->keyblock[i].active = LUKS_KEY_ENABLED;
|
||||
last_active = i;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
|
||||
continue;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "iterations", &jobj1))
|
||||
continue;
|
||||
hdr1->keyblock[i].passwordIterations = json_object_get_uint32(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_kdf, "salt", &jobj1))
|
||||
continue;
|
||||
len = sizeof(buf);
|
||||
memset(buf, 0, len);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), buf, &len))
|
||||
continue;
|
||||
if (len > 0 && len != LUKS_SALTSIZE)
|
||||
continue;
|
||||
memcpy(hdr1->keyblock[i].passwordSalt, buf, LUKS_SALTSIZE);
|
||||
}
|
||||
|
||||
if (!jobj_keyslot) {
|
||||
jobj_keyslot = LUKS2_get_keyslot_jobj(hdr2, last_active);
|
||||
if (!jobj_keyslot)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area))
|
||||
return -EINVAL;
|
||||
if (!json_object_object_get_ex(jobj_area, "encryption", &jobj1))
|
||||
return -EINVAL;
|
||||
r = crypt_parse_name_and_mode(json_object_get_string(jobj1), cipher, NULL, cipher_mode);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
strncpy(hdr1->cipherName, cipher, sizeof(hdr1->cipherName));
|
||||
strncpy(hdr1->cipherMode, cipher_mode, sizeof(hdr1->cipherMode));
|
||||
|
||||
if (!json_object_object_get_ex(jobj_keyslot, "kdf", &jobj_kdf))
|
||||
return -EINVAL;
|
||||
if (!json_object_object_get_ex(jobj_kdf, "hash", &jobj1))
|
||||
return -EINVAL;
|
||||
strncpy(hdr1->hashSpec, json_object_get_string(jobj1), sizeof(hdr1->hashSpec));
|
||||
|
||||
hdr1->keyBytes = key_size;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "iterations", &jobj1))
|
||||
return -EINVAL;
|
||||
hdr1->mkDigestIterations = json_object_get_uint32(jobj1);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "digest", &jobj1))
|
||||
return -EINVAL;
|
||||
len = sizeof(digest);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), digest, &len))
|
||||
return -EINVAL;
|
||||
/* We can store full digest here, not only sha1 length */
|
||||
if (len < LUKS_DIGESTSIZE)
|
||||
return -EINVAL;
|
||||
memcpy(hdr1->mkDigest, digest, LUKS_DIGESTSIZE);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_digest, "salt", &jobj1))
|
||||
return -EINVAL;
|
||||
len = sizeof(digest_salt);
|
||||
if (!base64_decode(json_object_get_string(jobj1),
|
||||
json_object_get_string_len(jobj1), digest_salt, &len))
|
||||
return -EINVAL;
|
||||
if (len != LUKS_SALTSIZE)
|
||||
return -EINVAL;
|
||||
memcpy(hdr1->mkDigestSalt, digest_salt, LUKS_SALTSIZE);
|
||||
|
||||
if (!json_object_object_get_ex(jobj_segment, "offset", &jobj1))
|
||||
return -EINVAL;
|
||||
offset = json_object_get_uint64(jobj1) / SECTOR_SIZE;
|
||||
if (offset > UINT32_MAX)
|
||||
return -EINVAL;
|
||||
/* FIXME: LUKS1 requires offset == 0 || offset >= luks1_hdr_size */
|
||||
hdr1->payloadOffset = offset;
|
||||
|
||||
strncpy(hdr1->uuid, hdr2->uuid, UUID_STRING_L);
|
||||
|
||||
memcpy(hdr1->magic, luksMagic, LUKS_MAGIC_L);
|
||||
|
||||
hdr1->version = 1;
|
||||
|
||||
r = luks_header_in_use(cd);
|
||||
if (r)
|
||||
return r > 0 ? -EBUSY : r;
|
||||
|
||||
// move keyslots 32k -> 4k offset
|
||||
buf_offset = 2 * LUKS2_HDR_16K_LEN;
|
||||
buf_size = LUKS2_keyslots_size(hdr2->jobj);
|
||||
r = move_keyslot_areas(cd, buf_offset, 8 * SECTOR_SIZE, buf_size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
crypt_wipe_device(cd, crypt_metadata_device(cd), CRYPT_WIPE_ZERO, 0,
|
||||
8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
|
||||
|
||||
// Write LUKS1 hdr
|
||||
return LUKS_write_phdr(hdr1, cd);
|
||||
}
|
||||
560
lib/luks2/luks2_token.c
Normal file
560
lib/luks2/luks2_token.c
Normal file
@@ -0,0 +1,560 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, token handling
|
||||
*
|
||||
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2016-2017, Milan Broz. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
/* Builtin tokens */
|
||||
extern const crypt_token_handler keyring_handler;
|
||||
|
||||
static token_handler token_handlers[LUKS2_TOKENS_MAX] = {
|
||||
/* keyring builtin token */
|
||||
{
|
||||
.get = token_keyring_get,
|
||||
.set = token_keyring_set,
|
||||
.h = &keyring_handler
|
||||
},
|
||||
};
|
||||
|
||||
static int is_builtin_candidate(const char *type)
|
||||
{
|
||||
return !strncmp(type, LUKS2_BUILTIN_TOKEN_PREFIX, LUKS2_BUILTIN_TOKEN_PREFIX_LEN);
|
||||
}
|
||||
|
||||
int crypt_token_register(const crypt_token_handler *handler)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (is_builtin_candidate(handler->name)) {
|
||||
log_dbg("'" LUKS2_BUILTIN_TOKEN_PREFIX "' is reserved prefix for builtin tokens.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++) {
|
||||
if (!strcmp(token_handlers[i].h->name, handler->name)) {
|
||||
log_dbg("Keyslot handler %s is already registered.", handler->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == LUKS2_TOKENS_MAX) {
|
||||
log_dbg("No more space for another token handler.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
token_handlers[i].h = handler;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const token_handler
|
||||
*LUKS2_token_handler_type_internal(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_TOKENS_MAX && token_handlers[i].h; i++)
|
||||
if (!strcmp(token_handlers[i].h->name, type))
|
||||
return token_handlers + i;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const crypt_token_handler
|
||||
*LUKS2_token_handler_type(struct crypt_device *cd, const char *type)
|
||||
{
|
||||
const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
|
||||
|
||||
return th ? th->h : NULL;
|
||||
}
|
||||
|
||||
static const token_handler
|
||||
*LUKS2_token_handler_internal(struct crypt_device *cd, int token)
|
||||
{
|
||||
struct luks2_hdr *hdr;
|
||||
json_object *jobj1, *jobj2;
|
||||
|
||||
if (token < 0)
|
||||
return NULL;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return NULL;
|
||||
|
||||
if (!(jobj1 = LUKS2_get_token_jobj(hdr, token)))
|
||||
return NULL;
|
||||
|
||||
if (!json_object_object_get_ex(jobj1, "type", &jobj2))
|
||||
return NULL;
|
||||
|
||||
return LUKS2_token_handler_type_internal(cd, json_object_get_string(jobj2));
|
||||
}
|
||||
|
||||
static const crypt_token_handler
|
||||
*LUKS2_token_handler(struct crypt_device *cd, int token)
|
||||
{
|
||||
const token_handler *th = LUKS2_token_handler_internal(cd, token);
|
||||
|
||||
return th ? th->h : NULL;
|
||||
}
|
||||
|
||||
static int LUKS2_token_find_free(struct luks2_hdr *hdr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LUKS2_TOKENS_MAX; i++)
|
||||
if (!LUKS2_get_token_jobj(hdr, i))
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int LUKS2_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *json,
|
||||
int commit)
|
||||
{
|
||||
const crypt_token_handler *h;
|
||||
json_object *jobj_tokens, *jobj_type, *jobj;
|
||||
enum json_tokener_error jerr;
|
||||
char num[16];
|
||||
|
||||
if (token == CRYPT_ANY_TOKEN) {
|
||||
if (!json)
|
||||
return -EINVAL;
|
||||
token = LUKS2_token_find_free(hdr);
|
||||
}
|
||||
|
||||
if (token < 0 || token >= LUKS2_TOKENS_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (!json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens))
|
||||
return -EINVAL;
|
||||
|
||||
/* Remove token */
|
||||
if (!json) {
|
||||
snprintf(num, sizeof(num), "%d", token);
|
||||
json_object_object_del(jobj_tokens, num);
|
||||
} else {
|
||||
|
||||
jobj = json_tokener_parse_verbose(json, &jerr);
|
||||
if (!jobj) {
|
||||
log_dbg("Token JSON parse failed.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
snprintf(num, sizeof(num), "%d", token);
|
||||
|
||||
if (LUKS2_token_validate(hdr->jobj, jobj, num)) {
|
||||
json_object_put(jobj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
json_object_object_get_ex(jobj, "type", &jobj_type);
|
||||
if (is_builtin_candidate(json_object_get_string(jobj_type))) {
|
||||
log_dbg("%s is builtin token candidate", json_object_get_string(jobj_type));
|
||||
json_object_put(jobj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
h = LUKS2_token_handler_type(cd, json_object_get_string(jobj_type));
|
||||
if (h && h->validate && h->validate(cd, json)) {
|
||||
json_object_put(jobj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
json_object_object_add(jobj_tokens, num, jobj);
|
||||
if (LUKS2_check_json_size(hdr)) {
|
||||
log_dbg("Not enough space in header json area for new token.");
|
||||
json_object_object_del(jobj_tokens, num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
if (commit)
|
||||
return LUKS2_hdr_write(cd, hdr) ?: token;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
crypt_token_info LUKS2_token_status(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char **type)
|
||||
{
|
||||
const char *tmp;
|
||||
const token_handler *th;
|
||||
json_object *jobj_type, *jobj_token;
|
||||
|
||||
if (token < 0 || token >= LUKS2_TOKENS_MAX)
|
||||
return CRYPT_TOKEN_INVALID;
|
||||
|
||||
if (!(jobj_token = LUKS2_get_token_jobj(hdr, token)))
|
||||
return CRYPT_TOKEN_INACTIVE;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "type", &jobj_type);
|
||||
tmp = json_object_get_string(jobj_type);
|
||||
|
||||
if ((th = LUKS2_token_handler_type_internal(cd, tmp))) {
|
||||
if (type)
|
||||
*type = th->h->name;
|
||||
return th->set ? CRYPT_TOKEN_INTERNAL : CRYPT_TOKEN_EXTERNAL;
|
||||
}
|
||||
|
||||
if (type)
|
||||
*type = tmp;
|
||||
|
||||
return is_builtin_candidate(tmp) ? CRYPT_TOKEN_INTERNAL_UNKNOWN : CRYPT_TOKEN_EXTERNAL_UNKNOWN;
|
||||
}
|
||||
|
||||
int LUKS2_builtin_token_get(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
void *params)
|
||||
{
|
||||
const token_handler *th = LUKS2_token_handler_type_internal(cd, type);
|
||||
|
||||
// internal error
|
||||
assert(th && th->get);
|
||||
|
||||
return th->get(LUKS2_get_token_jobj(hdr, token), params) ?: token;
|
||||
}
|
||||
|
||||
int LUKS2_builtin_token_create(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *type,
|
||||
const void *params,
|
||||
int commit)
|
||||
{
|
||||
const token_handler *th;
|
||||
char num[16];
|
||||
int r;
|
||||
json_object *jobj_token, *jobj_tokens;
|
||||
|
||||
th = LUKS2_token_handler_type_internal(cd, type);
|
||||
|
||||
// at this point all builtin handlers must exist and have validate fn defined
|
||||
assert(th && th->set && th->h->validate);
|
||||
|
||||
if (token == CRYPT_ANY_TOKEN) {
|
||||
if ((token = LUKS2_token_find_free(hdr)) < 0)
|
||||
log_err(cd, _("No free token slot\n"));
|
||||
}
|
||||
if (token < 0 || token >= LUKS2_TOKENS_MAX)
|
||||
return -EINVAL;
|
||||
snprintf(num, sizeof(num), "%u", token);
|
||||
|
||||
r = th->set(&jobj_token, params);
|
||||
if (r) {
|
||||
log_err(cd, _("Failed to create builtin token %s\n"), type);
|
||||
return r;
|
||||
}
|
||||
|
||||
// builtin tokens must produce valid json
|
||||
r = LUKS2_token_validate(hdr->jobj, jobj_token, "new");
|
||||
assert(!r);
|
||||
r = th->h->validate(cd, json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN));
|
||||
assert(!r);
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
|
||||
json_object_object_add(jobj_tokens, num, jobj_token);
|
||||
if (LUKS2_check_json_size(hdr)) {
|
||||
log_dbg("Not enough space in header json area for new %s token.", type);
|
||||
json_object_object_del(jobj_tokens, num);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (commit)
|
||||
return LUKS2_hdr_write(cd, hdr) ?: token;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static int LUKS2_token_open(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
char **buffer,
|
||||
size_t *buffer_len,
|
||||
void *usrptr)
|
||||
{
|
||||
const char *json;
|
||||
const crypt_token_handler *h;
|
||||
int r;
|
||||
|
||||
if (!(h = LUKS2_token_handler(cd, token)))
|
||||
return -ENOENT;
|
||||
|
||||
if (h->validate) {
|
||||
if (LUKS2_token_json_get(cd, hdr, token, &json))
|
||||
return -EINVAL;
|
||||
|
||||
if (h->validate(cd, json)) {
|
||||
log_dbg("Token %d (%s) validation failed.", token, h->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
r = h->open(cd, token, buffer, buffer_len, usrptr);
|
||||
if (r < 0)
|
||||
log_dbg("Token %d (%s) open failed with %d.", token, h->name, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void LUKS2_token_buffer_free(struct crypt_device *cd,
|
||||
int token,
|
||||
void *buffer,
|
||||
size_t buffer_len)
|
||||
{
|
||||
const crypt_token_handler *h = LUKS2_token_handler(cd, token);
|
||||
|
||||
if (h->buffer_free)
|
||||
h->buffer_free(buffer, buffer_len);
|
||||
else {
|
||||
crypt_memzero(buffer, buffer_len);
|
||||
free(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
static int LUKS2_keyslot_open_by_token(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
int segment,
|
||||
const char *buffer,
|
||||
size_t buffer_len,
|
||||
struct volume_key **vk)
|
||||
{
|
||||
const crypt_token_handler *h;
|
||||
json_object *jobj_token, *jobj_token_keyslots, *jobj;
|
||||
const char *num;
|
||||
int i, r;
|
||||
|
||||
if (!(h = LUKS2_token_handler(cd, token)))
|
||||
return -ENOENT;
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
|
||||
if (!jobj_token_keyslots)
|
||||
return -EINVAL;
|
||||
|
||||
/* Try to open keyslot referenced in token */
|
||||
r = -EINVAL;
|
||||
for (i = 0; i < json_object_array_length(jobj_token_keyslots) && r < 0; i++) {
|
||||
jobj = json_object_array_get_idx(jobj_token_keyslots, i);
|
||||
num = json_object_get_string(jobj);
|
||||
log_dbg("Trying to open keyslot %s with token %d (type %s).", num, token, h->name);
|
||||
r = LUKS2_keyslot_open(cd, atoi(num), segment, buffer, buffer_len, vk);
|
||||
}
|
||||
|
||||
return r < 0 ? r : atoi(num);
|
||||
}
|
||||
|
||||
int LUKS2_token_open_and_activate(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
int token,
|
||||
const char *name,
|
||||
uint32_t flags,
|
||||
void *usrptr)
|
||||
{
|
||||
int keyslot, r;
|
||||
char *buffer;
|
||||
size_t buffer_len;
|
||||
struct volume_key *vk = NULL;
|
||||
|
||||
r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, usrptr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = LUKS2_keyslot_open_by_token(cd, hdr, token,
|
||||
name ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
|
||||
buffer, buffer_len, &vk);
|
||||
|
||||
LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
keyslot = r;
|
||||
|
||||
if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
|
||||
r = crypt_volume_key_load_in_keyring(cd, vk);
|
||||
|
||||
if (r >= 0 && name)
|
||||
r = LUKS2_activate(cd, name, vk, flags);
|
||||
|
||||
crypt_free_volume_key(vk);
|
||||
|
||||
return r < 0 ? r : keyslot;
|
||||
}
|
||||
|
||||
int LUKS2_token_open_and_activate_any(struct crypt_device *cd,
|
||||
struct luks2_hdr *hdr,
|
||||
const char *name,
|
||||
uint32_t flags)
|
||||
{
|
||||
char *buffer;
|
||||
json_object *tokens_jobj;
|
||||
size_t buffer_len;
|
||||
int keyslot, token, r = -EINVAL;
|
||||
struct volume_key *vk = NULL;
|
||||
|
||||
json_object_object_get_ex(hdr->jobj, "tokens", &tokens_jobj);
|
||||
|
||||
json_object_object_foreach(tokens_jobj, slot, val) {
|
||||
UNUSED(val);
|
||||
token = atoi(slot);
|
||||
|
||||
r = LUKS2_token_open(cd, hdr, token, &buffer, &buffer_len, NULL);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
r = LUKS2_keyslot_open_by_token(cd, hdr, token,
|
||||
name ? CRYPT_DEFAULT_SEGMENT : CRYPT_ANY_SEGMENT,
|
||||
buffer, buffer_len, &vk);
|
||||
LUKS2_token_buffer_free(cd, token, buffer, buffer_len);
|
||||
if (r >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
keyslot = r;
|
||||
|
||||
if (r >= 0 && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd))
|
||||
r = crypt_volume_key_load_in_keyring(cd, vk);
|
||||
|
||||
if (r >= 0 && name)
|
||||
r = LUKS2_activate(cd, name, vk, flags);
|
||||
|
||||
crypt_free_volume_key(vk);
|
||||
|
||||
return r < 0 ? r : keyslot;
|
||||
}
|
||||
|
||||
void LUKS2_token_dump(struct crypt_device *cd, int token)
|
||||
{
|
||||
const crypt_token_handler *h;
|
||||
json_object *jobj_token;
|
||||
|
||||
h = LUKS2_token_handler(cd, token);
|
||||
if (h && h->dump) {
|
||||
jobj_token = LUKS2_get_token_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), token);
|
||||
if (jobj_token)
|
||||
h->dump(cd, json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN));
|
||||
}
|
||||
}
|
||||
|
||||
int LUKS2_token_json_get(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int token, const char **json)
|
||||
{
|
||||
json_object *jobj_token;
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
*json = json_object_to_json_string_ext(jobj_token, JSON_C_TO_STRING_PLAIN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int assign_one_keyslot(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int token, int keyslot, int assign)
|
||||
{
|
||||
json_object *jobj1, *jobj_token, *jobj_token_keyslots;
|
||||
char num[16];
|
||||
|
||||
log_dbg("Keyslot %i %s token %i.", keyslot, assign ? "assigned to" : "unassigned from", token);
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "keyslots", &jobj_token_keyslots);
|
||||
if (!jobj_token_keyslots)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(num, sizeof(num), "%d", keyslot);
|
||||
if (assign) {
|
||||
jobj1 = LUKS2_array_jobj(jobj_token_keyslots, num);
|
||||
if (!jobj1)
|
||||
json_object_array_add(jobj_token_keyslots, json_object_new_string(num));
|
||||
} else {
|
||||
jobj1 = LUKS2_array_remove(jobj_token_keyslots, num);
|
||||
if (jobj1)
|
||||
json_object_object_add(jobj_token, "keyslots", jobj1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int assign_one_token(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int token, int assign)
|
||||
{
|
||||
json_object *jobj_keyslots;
|
||||
int r = 0;
|
||||
|
||||
if (!LUKS2_get_token_jobj(hdr, token))
|
||||
return -EINVAL;
|
||||
|
||||
if (keyslot == CRYPT_ANY_SLOT) {
|
||||
json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots);
|
||||
|
||||
json_object_object_foreach(jobj_keyslots, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_keyslot(cd, hdr, token, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_keyslot(cd, hdr, token, keyslot, assign);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int LUKS2_token_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
|
||||
int keyslot, int token, int assign, int commit)
|
||||
{
|
||||
json_object *jobj_tokens;
|
||||
int r = 0;
|
||||
|
||||
if (token == CRYPT_ANY_TOKEN) {
|
||||
json_object_object_get_ex(hdr->jobj, "tokens", &jobj_tokens);
|
||||
|
||||
json_object_object_foreach(jobj_tokens, key, val) {
|
||||
UNUSED(val);
|
||||
r = assign_one_token(cd, hdr, keyslot, atoi(key), assign);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
} else
|
||||
r = assign_one_token(cd, hdr, keyslot, token, assign);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
// FIXME: do not write header in nothing changed
|
||||
if (commit)
|
||||
return LUKS2_hdr_write(cd, hdr) ?: token;
|
||||
|
||||
return token;
|
||||
}
|
||||
159
lib/luks2/luks2_token_keyring.c
Normal file
159
lib/luks2/luks2_token_keyring.c
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* LUKS - Linux Unified Key Setup v2, kernel keyring token
|
||||
*
|
||||
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2016-2017, Ondrej Kozina. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "luks2_internal.h"
|
||||
|
||||
static int keyring_open(struct crypt_device *cd,
|
||||
int token,
|
||||
char **buffer,
|
||||
size_t *buffer_len,
|
||||
void *usrptr __attribute__((unused)))
|
||||
{
|
||||
json_object *jobj_token, *jobj_key;
|
||||
struct luks2_hdr *hdr;
|
||||
|
||||
if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
|
||||
return -EINVAL;
|
||||
|
||||
jobj_token = LUKS2_get_token_jobj(hdr, token);
|
||||
if (!jobj_token)
|
||||
return -EINVAL;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "key_description", &jobj_key);
|
||||
|
||||
/* TODO: if r == -ENOKEY then instantiate the key? */
|
||||
if (keyring_get_passphrase(json_object_get_string(jobj_key), buffer, buffer_len))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int keyring_validate(struct crypt_device *cd __attribute__((unused)),
|
||||
const char *json)
|
||||
{
|
||||
enum json_tokener_error jerr;
|
||||
json_object *jobj_token, *jobj_key;
|
||||
int r = 1;
|
||||
|
||||
log_dbg("Validating keyring token json");
|
||||
|
||||
jobj_token = json_tokener_parse_verbose(json, &jerr);
|
||||
if (!jobj_token) {
|
||||
log_dbg("Keyring token JSON parse failed.");
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
|
||||
log_dbg("missing key_description field.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!json_object_is_type(jobj_key, json_type_string)) {
|
||||
log_dbg("key_description is not a string.");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: perhaps check that key description is in '%s:%s'
|
||||
* format where both strings are not empty */
|
||||
r = !strlen(json_object_get_string(jobj_key));
|
||||
out:
|
||||
json_object_put(jobj_token);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void keyring_dump(struct crypt_device *cd, const char *json)
|
||||
{
|
||||
enum json_tokener_error jerr;
|
||||
json_object *jobj_token, *jobj_key;
|
||||
|
||||
jobj_token = json_tokener_parse_verbose(json, &jerr);
|
||||
if (!jobj_token)
|
||||
return;
|
||||
|
||||
if (!json_object_object_get_ex(jobj_token, "key_description", &jobj_key)) {
|
||||
json_object_put(jobj_token);
|
||||
return;
|
||||
}
|
||||
|
||||
log_std(cd, "\tKey description: %s\n", json_object_get_string(jobj_key));
|
||||
|
||||
json_object_put(jobj_token);
|
||||
}
|
||||
|
||||
int token_keyring_set(json_object **jobj_builtin_token,
|
||||
const void *params)
|
||||
{
|
||||
json_object *jobj_token, *jobj;
|
||||
const struct crypt_token_params_luks2_keyring *keyring_params = (const struct crypt_token_params_luks2_keyring *) params;
|
||||
|
||||
jobj_token = json_object_new_object();
|
||||
if (!jobj_token)
|
||||
return -ENOMEM;
|
||||
|
||||
jobj = json_object_new_string(LUKS2_TOKEN_KEYRING);
|
||||
if (!jobj) {
|
||||
json_object_put(jobj_token);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_token, "type", jobj);
|
||||
|
||||
jobj = json_object_new_array();
|
||||
if (!jobj) {
|
||||
json_object_put(jobj_token);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_token, "keyslots", jobj);
|
||||
|
||||
jobj = json_object_new_string(keyring_params->key_description);
|
||||
if (!jobj) {
|
||||
json_object_put(jobj_token);
|
||||
return -ENOMEM;
|
||||
}
|
||||
json_object_object_add(jobj_token, "key_description", jobj);
|
||||
|
||||
*jobj_builtin_token = jobj_token;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int token_keyring_get(json_object *jobj_token,
|
||||
void *params)
|
||||
{
|
||||
json_object *jobj;
|
||||
struct crypt_token_params_luks2_keyring *keyring_params = (struct crypt_token_params_luks2_keyring *) params;
|
||||
|
||||
json_object_object_get_ex(jobj_token, "type", &jobj);
|
||||
assert(!strcmp(json_object_get_string(jobj), LUKS2_TOKEN_KEYRING));
|
||||
|
||||
json_object_object_get_ex(jobj_token, "key_description", &jobj);
|
||||
|
||||
keyring_params->key_description = json_object_get_string(jobj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const crypt_token_handler keyring_handler = {
|
||||
.name = LUKS2_TOKEN_KEYRING,
|
||||
.open = keyring_open,
|
||||
.validate = keyring_validate,
|
||||
.dump = keyring_dump
|
||||
};
|
||||
1697
lib/setup.c
1697
lib/setup.c
File diff suppressed because it is too large
Load Diff
@@ -403,6 +403,9 @@ int crypt_keyfile_read(struct crypt_device *cd, const char *keyfile,
|
||||
size_t buflen, i, file_read_size;
|
||||
struct stat st;
|
||||
|
||||
if (!key || !key_size_read)
|
||||
return -EINVAL;
|
||||
|
||||
*key = NULL;
|
||||
*key_size_read = 0;
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ int crypt_benchmark(struct crypt_device *cd,
|
||||
char *c;
|
||||
int r;
|
||||
|
||||
if (!cipher || !cipher_mode || !volume_key_size)
|
||||
if (!cipher || !cipher_mode || !volume_key_size || !encryption_mbs || !decryption_mbs)
|
||||
return -EINVAL;
|
||||
|
||||
r = init_crypto(cd);
|
||||
@@ -244,6 +244,9 @@ int crypt_benchmark_pbkdf(struct crypt_device *cd,
|
||||
int r;
|
||||
const char *kdf_opt;
|
||||
|
||||
if (!pbkdf || (!password && password_size))
|
||||
return -EINVAL;
|
||||
|
||||
r = init_crypto(cd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
const struct crypt_pbkdf_type default_luks2 = {
|
||||
.type = DEFAULT_LUKS2_PBKDF,
|
||||
.hash = DEFAULT_LUKS1_HASH,
|
||||
.time_ms = DEFAULT_LUKS2_ITER_TIME,
|
||||
.max_memory_kb = DEFAULT_LUKS2_MEMORY_KB,
|
||||
.parallel_threads = DEFAULT_LUKS2_PARALLEL_THREADS
|
||||
};
|
||||
|
||||
const struct crypt_pbkdf_type default_luks1 = {
|
||||
.type = CRYPT_KDF_PBKDF2,
|
||||
.hash = DEFAULT_LUKS1_HASH,
|
||||
@@ -95,7 +103,9 @@ int init_pbkdf_type(struct crypt_device *cd,
|
||||
uint32_t old_flags;
|
||||
int r;
|
||||
|
||||
if (!pbkdf)
|
||||
if (!pbkdf && dev_type && !strcmp(dev_type, CRYPT_LUKS2))
|
||||
pbkdf = &default_luks2;
|
||||
else if (!pbkdf)
|
||||
pbkdf = &default_luks1;
|
||||
|
||||
r = verify_pbkdf_params(cd, pbkdf);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH CRYPTSETUP-REENCRYPT "8" "March 2017" "cryptsetup-reencrypt" "Maintenance Commands"
|
||||
.TH CRYPTSETUP-REENCRYPT "8" "September 2017" "cryptsetup-reencrypt" "Maintenance Commands"
|
||||
.SH NAME
|
||||
cryptsetup-reencrypt - tool for offline LUKS device re-encryption
|
||||
.SH SYNOPSIS
|
||||
@@ -14,6 +14,8 @@ unclocked by passphrase), \fBcipher\fR, \fBcipher mode\fR.
|
||||
Cryptsetup-reencrypt reencrypts data on LUKS device in-place. During
|
||||
reencryption process the LUKS device is marked unavailable.
|
||||
|
||||
\fBNOTE:\fR LUKS2 format is not yet supported.
|
||||
|
||||
\fIWARNING\fR: The cryptsetup-reencrypt program is not resistant to hardware
|
||||
or kernel failures during reencryption (you can lose you data in this case).
|
||||
|
||||
|
||||
421
man/cryptsetup.8
421
man/cryptsetup.8
@@ -1,4 +1,4 @@
|
||||
.TH CRYPTSETUP "8" "March 2017" "cryptsetup" "Maintenance Commands"
|
||||
.TH CRYPTSETUP "8" "September 2017" "cryptsetup" "Maintenance Commands"
|
||||
.SH NAME
|
||||
cryptsetup - manage plain dm-crypt and LUKS encrypted volumes
|
||||
.SH SYNOPSIS
|
||||
@@ -12,7 +12,7 @@ and can hence offer more features than plain dm-crypt. On the other
|
||||
hand, the header is visible and vulnerable to damage.
|
||||
|
||||
In addition, cryptsetup provides limited support for the use of
|
||||
historic loopaes volumes and for TrueCrypt compatible volumes.
|
||||
loop-AES volumes and for TrueCrypt compatible volumes.
|
||||
|
||||
.SH PLAIN DM-CRYPT OR LUKS?
|
||||
.PP
|
||||
@@ -29,12 +29,12 @@ to be mentioned here.
|
||||
|
||||
\fBBackup:\fR Storage media die. Encryption has no influence on that.
|
||||
Backup is mandatory for encrypted data as well, if the data has any
|
||||
worth. See the Cryptsetup FAQ for advice on how to do backup of an
|
||||
worth. See the Cryptsetup FAQ for advice on how to do a backup of an
|
||||
encrypted volume.
|
||||
|
||||
\fBCharacter encoding:\fR If you enter a
|
||||
passphrase with special symbols, the passphrase can change
|
||||
depending character encoding. Keyboard settings can also change,
|
||||
depending on character encoding. Keyboard settings can also change,
|
||||
which can make blind input hard or impossible. For
|
||||
example, switching from some ASCII 8-bit variant to UTF-8
|
||||
can lead to a different binary encoding and hence different
|
||||
@@ -57,10 +57,10 @@ secure wiping by just overwriting header and key-slot area.
|
||||
it is a very good idea to wipe filesystem signatures, data, etc. before
|
||||
creating a LUKS or plain dm-crypt container on it.
|
||||
For a quick removal of filesystem signatures, use "wipefs". Take care
|
||||
though that this may not remove everything. In particular md (RAID)
|
||||
though that this may not remove everything. In particular, MD RAID
|
||||
signatures at the end of a device may survive. It also does not
|
||||
remove data. For a full wipe, overwrite the whole partition before
|
||||
container creation. If you do not know how to to that, the
|
||||
container creation. If you do not know how to do that, the
|
||||
cryptsetup FAQ describes several options.
|
||||
|
||||
.SH BASIC COMMANDS
|
||||
@@ -98,6 +98,9 @@ For backward compatibility there are \fBclose\fR command aliases:
|
||||
\fBremove\fR, \fBplainClose\fR, \fBluksClose\fR, \fBloopaesClose\fR,
|
||||
\fBtcryptClose\fR (all behaves exactly the same, device type is
|
||||
determined automatically from active device).
|
||||
|
||||
\fB<options>\fR can be [\-\-deferred]
|
||||
|
||||
.PP
|
||||
\fIstatus\fR <name>
|
||||
.IP
|
||||
@@ -111,11 +114,22 @@ If \-\-size (in sectors) is not specified, the size is computed
|
||||
from the underlying device. For LUKS it is the size of the
|
||||
underlying device without the area reserved for LUKS header
|
||||
(see data payload offset in \fBluksDump\fR command).
|
||||
For plain crypt device the whole device size is used.
|
||||
For plain crypt device, the whole device size is used.
|
||||
|
||||
Note that this does not change the raw device geometry, it just
|
||||
changes how many sectors of the raw device are represented
|
||||
in the mapped device.
|
||||
|
||||
If cryptsetup detected volume key for active device loaded in kernel keyring
|
||||
service, resize action would first try to retrieve
|
||||
the key using a token and only if it failed it'd ask for a passphrase
|
||||
to unlock a keyslot (LUKS) or to derive a volume key again (plain mode).
|
||||
The kernel keyring is used by default for LUKS2 devices.
|
||||
|
||||
With LUKS2 device additional \fB<options>\fR can be [\-\-token\-id, \-\-token\-only,
|
||||
\-\-key\-slot, \-\-key\-file, \-\-keyfile\-size, \-\-keyfile\-offset, \-\-timeout,
|
||||
\-\-disable\-locks, \-\-disable\-keyring].
|
||||
|
||||
.SH PLAIN MODE
|
||||
Plain dm-crypt encrypts the device sector-by-sector with a
|
||||
single, non-salted hash of the passphrase. No checks
|
||||
@@ -134,7 +148,7 @@ Opens (creates a mapping with) <name> backed by device <device>.
|
||||
|
||||
\fB<options>\fR can be [\-\-hash, \-\-cipher, \-\-verify-passphrase,
|
||||
\-\-key-file, \-\-keyfile-offset, \-\-key-size, \-\-offset, \-\-skip, \-\-size,
|
||||
\-\-readonly, \-\-shared, \-\-allow-discards]
|
||||
\-\-readonly, \-\-shared, \-\-allow\-discards]
|
||||
|
||||
Example: 'cryptsetup open \-\-type plain /dev/sda10 e1' maps the raw
|
||||
encrypted device /dev/sda10 to the mapped (decrypted) device
|
||||
@@ -146,7 +160,7 @@ It adds a standardized header at the start of the device,
|
||||
a key-slot area directly behind the header and the bulk
|
||||
data area behind that. The whole set is called a 'LUKS container'.
|
||||
The device that a LUKS container resides on is called a 'LUKS device'.
|
||||
For most purposes both terms can be used interchangeably. But
|
||||
For most purposes, both terms can be used interchangeably. But
|
||||
note that when the LUKS header is at a nonzero offset
|
||||
in a device, then the device is not a LUKS device anymore, but
|
||||
has a LUKS container stored in it at an offset.
|
||||
@@ -158,6 +172,12 @@ are protected against brute-force and dictionary
|
||||
attacks by PBKDF2, which implements hash iteration and salting
|
||||
in one function.
|
||||
|
||||
LUKS2 is a new version of header format that allows additional
|
||||
extensions like different PBKDF algorithm or authenticated encryption.
|
||||
You can format device with LUKS2 header if you specify
|
||||
\fI\-\-type luks2\fR in \fIluksFormat\fR command.
|
||||
For activation, the format is already recognized automatically.
|
||||
|
||||
Each passphrase, also called a
|
||||
.B key
|
||||
in this document, is associated with one of 8 key-slots.
|
||||
@@ -165,13 +185,13 @@ Key operations that do not specify a slot affect the first slot
|
||||
that matches the supplied passphrase or the first empty slot if
|
||||
a new passphrase is added.
|
||||
|
||||
The \fB<device>\fR parameter can be also specified by a LUKS UUID in the
|
||||
The \fB<device>\fR parameter can also be specified by a LUKS UUID in the
|
||||
format UUID=<uuid>. Translation to real device name uses symlinks
|
||||
in /dev/disk/by-uuid directory.
|
||||
|
||||
To specify a detached header, the \fB\-\-header\fR parameter can be used
|
||||
in all LUKS commands and always takes precedence over positional \fB<device>\fR
|
||||
parameter.
|
||||
in all LUKS commands and always takes precedence over the positional
|
||||
\fB<device>\fR parameter.
|
||||
|
||||
The following are valid LUKS actions:
|
||||
|
||||
@@ -183,21 +203,30 @@ either via prompting or via <key file>. Note that
|
||||
if the second argument is present, then the passphrase
|
||||
is taken from the file given there, without the need
|
||||
to use the \-\-key-file option. Also note that for both forms
|
||||
of reading the passphrase from file you can
|
||||
of reading the passphrase from a file you can
|
||||
give '-' as file name, which results in the passphrase being read
|
||||
from stdin and the safety-question being skipped.
|
||||
|
||||
You can only call luksFormat on a LUKS device that is not mapped.
|
||||
|
||||
To use LUKS2, specify \fI\-\-type luks2\fR.
|
||||
|
||||
\fB<options>\fR can be [\-\-hash, \-\-cipher, \-\-verify\-passphrase,
|
||||
\-\-key\-size, \-\-key\-slot,
|
||||
\-\-key\-file (takes precedence over optional second argument),
|
||||
\-\-keyfile\-offset, \-\-keyfile\-size, \-\-use\-random | \-\-use\-urandom,
|
||||
\-\-uuid, \-\-master\-key\-file, \-\-iter\-time, \-\-header,
|
||||
\-\-force\-password].
|
||||
\-\-pbkdf\-force\-iterations,
|
||||
\-\-force\-password, \-\-disable-locks].
|
||||
|
||||
For LUKS2, additional \fB<options>\fR can be
|
||||
[\-\-integrity, \-\-integrity\-no\-wipe, \-\-sector\-size,
|
||||
\-\-label, \-\-subsystem,
|
||||
\-\-pbkdf, \-\-pbkdf\-memory, \-\-pbkdf\-parallel,
|
||||
\-\-disable\-locks, \-\-disable\-keyring].
|
||||
|
||||
\fBWARNING:\fR Doing a luksFormat on an existing LUKS container will
|
||||
make all data the old container permanently irretrievable, unless
|
||||
make all data the old container permanently irretrievable unless
|
||||
you have a header backup.
|
||||
.PP
|
||||
\fIopen\fR \-\-type luks <device> <name>
|
||||
@@ -206,16 +235,19 @@ you have a header backup.
|
||||
.IP
|
||||
Opens the LUKS device <device> and sets up a mapping <name> after
|
||||
successful verification of the supplied passphrase.
|
||||
If the passphrase is not supplied via \-\-key-file, the command
|
||||
prompts for it interactively.
|
||||
|
||||
First, the passphrase is searched in LUKS tokens. If it's not
|
||||
found in any token and also the passphrase is not supplied via \-\-key-file,
|
||||
the command prompts for it interactively.
|
||||
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
|
||||
\-\-keyfile\-size, \-\-readonly, \-\-test\-passphrase,
|
||||
\-\-allow\-discards, \-\-header, \-\-key-slot, \-\-master\-key\-file].
|
||||
\-\-allow\-discards, \-\-header, \-\-key-slot, \-\-master\-key\-file, \-\-token\-id,
|
||||
\-\-token\-only, \-\-disable\-keyring, \-\-disable\-locks].
|
||||
.PP
|
||||
\fIluksSuspend\fR <name>
|
||||
.IP
|
||||
Suspends an active device (all IO operations will blocked
|
||||
Suspends an active device (all IO operations will block
|
||||
and accesses to the device will wait indefinitely)
|
||||
and wipes the encryption
|
||||
key from kernel memory. Needs kernel 2.6.19 or later.
|
||||
@@ -226,14 +258,15 @@ the mapped device.
|
||||
|
||||
\fBWARNING:\fR never suspend the device on which the cryptsetup binary resides.
|
||||
|
||||
\fB<options>\fR can be [\-\-header].
|
||||
\fB<options>\fR can be [\-\-header, \-\-disable\-locks].
|
||||
.PP
|
||||
\fIluksResume\fR <name>
|
||||
.IP
|
||||
Resumes a suspended device and reinstates the encryption key.
|
||||
Prompts interactively for a passphrase if \-\-key-file is not given.
|
||||
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-size, \-\-header]
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-size, \-\-header,
|
||||
\-\-disable\-keyring,\-\-disable\-locks]
|
||||
.PP
|
||||
\fIluksAddKey\fR <device> [<key file with new key>]
|
||||
.IP
|
||||
@@ -245,19 +278,19 @@ or read from the file given as positional argument.
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
|
||||
\-\-keyfile\-size, \-\-new\-keyfile\-offset,
|
||||
\-\-new\-keyfile\-size, \-\-key\-slot, \-\-master\-key\-file,
|
||||
\-\-iter\-time, \-\-force\-password, \-\-header].
|
||||
\-\-iter\-time, \-\-force\-password, \-\-header, \-\-disable\-locks].
|
||||
.PP
|
||||
\fIluksRemoveKey\fR <device> [<key file with passphrase to be removed>]
|
||||
.IP
|
||||
Removes the supplied passphrase from the LUKS device. The
|
||||
passphrase to be removed can be specified interactively,
|
||||
as positional argument or via \-\-key-file.
|
||||
as the positional argument or via \-\-key-file.
|
||||
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
|
||||
\-\-keyfile\-size, \-\-header]
|
||||
\-\-keyfile\-size, \-\-header, \-\-disable\-locks]
|
||||
|
||||
\fBWARNING:\fR If you read the passphrase from stdin
|
||||
(without further argument or with '-' as argument
|
||||
(without further argument or with '-' as an argument
|
||||
to \-\-key\-file), batch-mode (\-q) will be implicitly
|
||||
switched on and no warning will be given when you remove the
|
||||
last remaining passphrase from a LUKS container. Removing
|
||||
@@ -287,7 +320,8 @@ inaccessible.
|
||||
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
|
||||
\-\-keyfile\-size, \-\-new\-keyfile\-offset,
|
||||
\-\-new\-keyfile\-size, \-\-key\-slot, \-\-force\-password, \-\-header].
|
||||
\-\-new\-keyfile\-size, \-\-key\-slot, \-\-force\-password, \-\-header,
|
||||
\-\-disable\-locks].
|
||||
.PP
|
||||
\fIluksKillSlot\fR <device> <key slot number>
|
||||
.IP
|
||||
@@ -299,10 +333,10 @@ an interactive confirmation when doing so. Removing the last
|
||||
passphrase makes a LUKS container permanently inaccessible.
|
||||
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-keyfile\-offset,
|
||||
\-\-keyfile\-size, \-\-header].
|
||||
\-\-keyfile\-size, \-\-header, \-\-disable\-locks].
|
||||
|
||||
\fBWARNING:\fR If you read the passphrase from stdin
|
||||
(without further argument or with '-' as argument
|
||||
(without further argument or with '-' as an argument
|
||||
to \-\-key-file), batch-mode (\-q) will be implicitly
|
||||
switched on and no warning will be given when you remove the
|
||||
last remaining passphrase from a LUKS container. Removing
|
||||
@@ -346,11 +380,11 @@ without a passphrase and even without the LUKS header. This means
|
||||
that if the master key is compromised, the whole device has to be
|
||||
erased to prevent further access. Use this option carefully.
|
||||
|
||||
In order to dump the master key, a passphrase has to be supplied,
|
||||
To dump the master key, a passphrase has to be supplied,
|
||||
either interactively or via \-\-key\-file.
|
||||
|
||||
\fB<options>\fR can be [\-\-dump\-master\-key, \-\-key\-file,
|
||||
\-\-keyfile\-offset, \-\-keyfile\-size, \-\-header].
|
||||
\-\-keyfile\-offset, \-\-keyfile\-size, \-\-header, \-\-disable\-locks].
|
||||
|
||||
\fBWARNING:\fR If \-\-dump\-master\-key is used with \-\-key\-file
|
||||
and the argument to \-\-key\-file is '-', no validation question
|
||||
@@ -381,12 +415,56 @@ from the specified file.
|
||||
Note: Using '-' as filename reads the header backup from a file named '-'.
|
||||
|
||||
\fBWARNING:\fR Header and keyslots will be replaced, only
|
||||
the passphrases from the backup will work afterwards.
|
||||
the passphrases from the backup will work afterward.
|
||||
|
||||
This command requires that the master key size and data offset
|
||||
of the LUKS header already on the device and of the header backup
|
||||
match. Alternatively, if there is no LUKS header on the device,
|
||||
the backup will also be written to it.
|
||||
.PP
|
||||
\fItoken\fR <add|remove> <device>
|
||||
.IP
|
||||
Adds a new keyring token to enable auto-activation of the device.
|
||||
For the auto-activation, the passphrase must be stored in keyring with the specified
|
||||
description. Usually, the passphrase should be stored in \fIuser\fR or
|
||||
\fIuser-session\fR keyring.
|
||||
The \fItoken\fR command is supported only for LUKS2.
|
||||
|
||||
For adding new keyring token, option \-\-key\-description is mandatory.
|
||||
Also, new token is assigned to key slot specified with \-\-key\-slot option or to all
|
||||
active key slots in the case \-\-key\-slot option is ommited.
|
||||
|
||||
To remove existing token, specify the token ID which should be removed with
|
||||
\-\-token\-id option.
|
||||
|
||||
\fBWARNING:\fR The action \fItoken remove\fR removes any token type, not just \fIkeyring\fR
|
||||
type from token slot specified by \-\-token\-id option.
|
||||
|
||||
\fB<options>\fR can be [\-\-header, \-\-token\-id, \-\-key-slot, \-\-key\-description,
|
||||
\-\-disable\-locks, \-\-disable\-keyring].
|
||||
.PP
|
||||
\fIconvert\fR <device> \-\-type <format>
|
||||
.IP
|
||||
Converts the device between LUKS and LUKS2 format (if possible).
|
||||
The conversion will not be performed if there is an additional LUKS2 feature or LUKS has
|
||||
unsupported header size.
|
||||
|
||||
\fBWARNING:\fR The \fIconvert\fR action can destroy the LUKS header in the case of a crash
|
||||
during conversion or if a media error occurs.
|
||||
Always create a header backup before performing this operation!
|
||||
|
||||
\fB<options>\fR can be [\-\-header, \-\-type].
|
||||
.PP
|
||||
\fIconfig\fR <device>
|
||||
.IP
|
||||
Set permanent configuration options (store to LUKS header).
|
||||
The \fIconfig\fR command is supported only for LUKS2.
|
||||
|
||||
The permanent options can be \fI\-\-priority\fR to set priority (normal, prefer, ignore)
|
||||
for keyslot (specified by \fI\-\-key\-slot\fR) or \fI\-\-label\fR and \fI\-\-subsystem\fR.
|
||||
|
||||
\fB<options>\fR can be [\-\-priority, \-\-label, \-\-subsystem, \-\-key\-slot, \-\-header].
|
||||
|
||||
.SH loop-AES EXTENSION
|
||||
cryptsetup supports mapping loop-AES encrypted partition using
|
||||
a compatibility mode.
|
||||
@@ -403,7 +481,7 @@ If the key file is encrypted with GnuPG, then you have to use
|
||||
gpg \-\-decrypt <keyfile> | cryptsetup loopaesOpen \-\-key\-file=\-
|
||||
<device> <name>
|
||||
|
||||
\fBWARNING:\fR The loop-AES extension cannot use direct input of key file
|
||||
\fBWARNING:\fR The loop-AES extension cannot use the direct input of key file
|
||||
on real terminal because the keys are separated by end-of-line and only part
|
||||
of the multi-key file would be read.
|
||||
.br
|
||||
@@ -456,16 +534,14 @@ iteration count so unlocking can take quite a lot of time (in comparison
|
||||
with TCRYPT device).
|
||||
|
||||
To open a VeraCrypt device with a custom Personal Iteration Multiplier (PIM)
|
||||
value, \fBadditionally to --veracrypt \fR use either the
|
||||
\fB--veracrypt-pim=<PIM>\fR option to directly specify the PIM on the command-
|
||||
line or use \fB--veracrypt-query-pim\fR to be prompted for the PIM.
|
||||
value, \fBadditionally to \-\-veracrypt \fR use either the
|
||||
\fB\-\-veracrypt\-pim=<PIM>\fR option to directly specify the PIM on the command-
|
||||
line or use \fB\-\-veracrypt\-query\-pim\fR to be prompted for the PIM.
|
||||
|
||||
The PIM value affects the number of iterations applied during key derivation. Please refer to
|
||||
\fBhttps://veracrypt.codeplex.com/wikipage?title=Personal%20Iterations%20Multiplier%20(PIM)\fR
|
||||
\fBhttps://www.veracrypt.fr/en/Personal%20Iterations%20Multiplier%20(PIM).html\fR
|
||||
for more detailed information.
|
||||
|
||||
|
||||
|
||||
\fBNOTE:\fR Activation with \fBtcryptOpen\fR is supported only for cipher chains
|
||||
using LRW or XTS encryption modes.
|
||||
|
||||
@@ -475,17 +551,17 @@ and doesn't require superuser privilege.
|
||||
To map system device (device with boot loader where the whole encrypted
|
||||
system resides) use \fB\-\-tcrypt\-system\fR option.
|
||||
You can use partition device as the parameter (parameter must be real partition
|
||||
device, not image in file), then only this partition is mapped.
|
||||
device, not an image in a file), then only this partition is mapped.
|
||||
|
||||
If you have whole TCRYPT device as a file image and you want to map multiple
|
||||
If you have the whole TCRYPT device as a file image and you want to map multiple
|
||||
partition encrypted with system encryption, please create loopback mapping
|
||||
with partitions first (\fBlosetup \-P\fR, see \fPlosetup(8)\fR man page for more info),
|
||||
and use loop partition as the device parameter.
|
||||
|
||||
If you use whole base device as parameter, one device for the whole system
|
||||
If you use the whole base device as a parameter, one device for the whole system
|
||||
encryption is mapped. This mode is available only for backward compatibility
|
||||
with older cryptsetup versions which mapped TCRYPT system encryption
|
||||
using whole device.
|
||||
using the whole device.
|
||||
|
||||
To use hidden header (and map hidden device, if available),
|
||||
use \fB\-\-tcrypt\-hidden\fR option.
|
||||
@@ -509,15 +585,15 @@ a mapping <name>.
|
||||
|
||||
\fB<options>\fR can be [\-\-key\-file, \-\-tcrypt\-hidden,
|
||||
\-\-tcrypt\-system, \-\-tcrypt\-backup, \-\-readonly, \-\-test\-passphrase,
|
||||
\-\-allow-discards].
|
||||
\-\-allow-discards, \-\-vracrypt, \-\-veracrypt\-pim, \-\-veracrypt\-querry\-pim].
|
||||
|
||||
The keyfile parameter allows combination of file content with the
|
||||
The keyfile parameter allows a combination of file content with the
|
||||
passphrase and can be repeated. Note that using keyfiles is compatible
|
||||
with TCRYPT and is different from LUKS keyfile logic.
|
||||
|
||||
\fBWARNING:\fR Option \fB\-\-allow\-discards\fR cannot be combined with
|
||||
option \fB\-\-tcrypt\-hidden\fR. For normal mapping it can cause
|
||||
\fBdestruction of hidden volume\fR (hidden volume appears as unused space
|
||||
option \fB\-\-tcrypt\-hidden\fR. For normal mapping, it can cause
|
||||
the \fBdestruction of hidden volume\fR (hidden volume appears as unused space
|
||||
for outer volume so this space can be discarded).
|
||||
|
||||
.PP
|
||||
@@ -536,14 +612,14 @@ to be erased to prevent further access. Use this option carefully.
|
||||
\fB<options>\fR can be [\-\-dump\-master\-key, \-\-key\-file,
|
||||
\-\-tcrypt\-hidden, \-\-tcrypt\-system, \-\-tcrypt\-backup].
|
||||
|
||||
The keyfile parameter allows combination of file content with the
|
||||
The keyfile parameter allows a combination of file content with the
|
||||
passphrase and can be repeated.
|
||||
.PP
|
||||
See also \fBhttps://en.wikipedia.org/wiki/TrueCrypt\fR for more information regarding
|
||||
TrueCrypt.
|
||||
|
||||
Please note that cryptsetup does not use TrueCrypt code, please report
|
||||
all problems related to this compatibility extension to cryptsetup project.
|
||||
all problems related to this compatibility extension to the cryptsetup project.
|
||||
.SH MISCELLANEOUS
|
||||
.PP
|
||||
\fIrepair\fR <device>
|
||||
@@ -562,7 +638,7 @@ header before calling this command.
|
||||
\fIbenchmark\fR <options>
|
||||
.IP
|
||||
Benchmarks ciphers and KDF (key derivation function).
|
||||
Without parameters it tries to measure few common configurations.
|
||||
Without parameters, it tries to measure few common configurations.
|
||||
|
||||
To benchmark other ciphers or modes, you need to specify \fB\-\-cipher\fR
|
||||
and \fB\-\-key\-size\fR options or \fB\-\-hash\fR for KDF test.
|
||||
@@ -586,6 +662,10 @@ Print more information on command execution.
|
||||
Run in debug mode with full diagnostic logs. Debug output
|
||||
lines are always prefixed by '#'.
|
||||
.TP
|
||||
.B "\-\-type <device-type>
|
||||
Specifies required device type, for more info
|
||||
read \fIBASIC COMMANDS\fR section.
|
||||
.TP
|
||||
.B "\-\-hash, \-h \fI<hash\-spec>\fR"
|
||||
Specifies the passphrase hash for \fIopen\fR (for plain and
|
||||
loopaes device types).
|
||||
@@ -652,18 +732,18 @@ See section \fBNOTES ON PASSPHRASE PROCESSING\fR for more information.
|
||||
.TP
|
||||
.B "\-\-keyfile\-offset \fIvalue\fR"
|
||||
Skip \fIvalue\fR bytes at the beginning of the key file.
|
||||
Works with all commands that accepts key files.
|
||||
Works with all commands that accept key files.
|
||||
.TP
|
||||
.B "\-\-keyfile\-size, \-l \fIvalue\fR"
|
||||
Read a maximum of \fIvalue\fR bytes from the key file.
|
||||
Default is to read the whole file up to the compiled-in
|
||||
The default is to read the whole file up to the compiled-in
|
||||
maximum that can be queried with \-\-help. Supplying more
|
||||
data than the compiled-in maximum aborts the operation.
|
||||
|
||||
This option is useful
|
||||
to cut trailing newlines, for example. If \-\-keyfile\-offset
|
||||
is also given, the size count starts after the offset.
|
||||
Works with all commands that accepts key files.
|
||||
Works with all commands that accept key files.
|
||||
.TP
|
||||
.B "\-\-new\-keyfile\-offset \fIvalue\fR"
|
||||
Skip \fIvalue\fR bytes at the start when
|
||||
@@ -673,7 +753,7 @@ adding a new passphrase from key file with
|
||||
.B "\-\-new\-keyfile\-size \fIvalue\fR"
|
||||
Read a maximum of \fIvalue\fR bytes when adding
|
||||
a new passphrase from key file with \fIluksAddKey\fR.
|
||||
Default is to read the whole file up to the compiled-in
|
||||
The default is to read the whole file up to the compiled-in
|
||||
maximum length that can be queried with \-\-help.
|
||||
Supplying more than the compiled in maximum aborts the
|
||||
operation.
|
||||
@@ -691,7 +771,7 @@ then the new header decrypts the data encrypted with the
|
||||
header the master key was taken from.
|
||||
|
||||
\fBWARNING:\fR If you create your own master key, you
|
||||
need to make sure to do it right. Otherwise you can end
|
||||
need to make sure to do it right. Otherwise, you can end
|
||||
up with a low-entropy or otherwise partially predictable
|
||||
master key which will compromise security.
|
||||
|
||||
@@ -764,7 +844,7 @@ This option is only relevant for the \fIopen\fR action with plain
|
||||
or loopaes device types.
|
||||
|
||||
Hence, if \-\-offset \fIn\fR, and \-\-skip \fIs\fR, sector \fIn\fR
|
||||
(the first sector of encrypted device) will get a sector number
|
||||
(the first sector of the encrypted device) will get a sector number
|
||||
of \fIs\fR for the IV calculation.
|
||||
.TP
|
||||
.B "\-\-readonly, \-r"
|
||||
@@ -777,12 +857,67 @@ This option is only relevant for the
|
||||
\fIopen \-\-type plain\fR action. Use \-\-offset, \-\-size and \-\-skip to
|
||||
specify the mapped area.
|
||||
.TP
|
||||
.B "\-\-pbkdf <PBKDF spec>"
|
||||
Set Password-Based Key Derivation Function (PBKDF) algorithm for LUKS keyslot.
|
||||
The PBKDF can be: \fIpbkdf2\fR (for PBKDF2 according to RFC2898),
|
||||
\fIargon2i\fR for Argon2i or \fIargon2id\fR for Argon2id
|
||||
(see https://www.cryptolux.org/index.php/Argon2 for more info).
|
||||
|
||||
For LUKS1, only PBKDF2 is accepted (no need to use this option).
|
||||
The default PBKDF2 for LUKS2 is set during compilation time
|
||||
and is available in \fIcryptsetup \-\-help\fR output.
|
||||
|
||||
A PBKDF is used for increasing dictionary and brute-force attack cost
|
||||
for keyslot passwords. The parameters can be time, memory and parallel cost.
|
||||
|
||||
For PBKDF2, only time cost (number of iterations) applies.
|
||||
For Argon2i/id, there is also memory cost (memory required during
|
||||
the process of key derivation) and parallel cost (number of threads
|
||||
that run in parallel during the key derivation.
|
||||
|
||||
Note that increasing memory cost also increases time, so the final
|
||||
parameter values are measured by a benchmark. The benchmark
|
||||
tries to find iteration time (\fI\-\-iter\-time\fR) with required
|
||||
memory cost \fI\-\-pbkdf\-memory\fR. If it is not possible,
|
||||
the memory cost is decreased as well.
|
||||
The parallel cost \fI\-\-pbkdf\-parallel\fR is constant, is is checked
|
||||
against available CPU cores (if not available, it is decreased) and the maximum
|
||||
parallel cost is 4.
|
||||
|
||||
You can see all PBKDF parameters for particular LUKS2 keyslot with
|
||||
\fIluksDump\fR command.
|
||||
|
||||
\fBNOTE:\fR If you do not want to use benchmark and want to specify
|
||||
all parameters directly, use \fI\-\-pbkdf\-force\-iterations\fR with
|
||||
\fI\-\-pbkdf\-memory\fR and \fI\-\-pbkdf\-parallel\fR.
|
||||
This will override the values without benchmarking.
|
||||
Note it can cause extremely long unlocking time. Use only is specified
|
||||
cases, for example, if you know that the formatted device will
|
||||
be used on some small embedded system.
|
||||
In this case, the LUKS PBKDF2 digest will be set to the minimum iteration count.
|
||||
.TP
|
||||
.B "\-\-iter\-time, \-i <number of milliseconds>"
|
||||
The number of milliseconds to spend with PBKDF2 passphrase processing.
|
||||
The number of milliseconds to spend with PBKDF passphrase processing.
|
||||
This option is only relevant for LUKS operations that set or change
|
||||
passphrases, such as \fIluksFormat\fR or \fIluksAddKey\fR.
|
||||
Specifying 0 as parameter selects the compiled-in default.
|
||||
.TP
|
||||
.B "\-\-pbkdf\-memory <number>"
|
||||
Set the memory cost for PBKDF (for Argon2i/id the number represents kilobytes).
|
||||
Note that it is maximal value, PBKDF benchmark can decrease it.
|
||||
This option is not available for PBKDF2.
|
||||
.TP
|
||||
.B "\-\-pbkdf\-paralell <number>"
|
||||
Set the parallel cost for PBKDF (number of threads, up to 4).
|
||||
Note that it is maximal value, it is decreased automatically if
|
||||
CPU online count is lower.
|
||||
This option is not available for PBKDF2.
|
||||
.TP
|
||||
.B "\-\-pbkdf\-force\-iterations <num>"
|
||||
Avoid PBKDF benchmark and set time cost (iterations) directly.
|
||||
It can be used for LUKS/LUKS2 device only.
|
||||
See \fI\-\-pbkdf\fR option for more info.
|
||||
.TP
|
||||
.B "\-\-batch\-mode, \-q"
|
||||
Suppresses all confirmation questions. Use with care!
|
||||
|
||||
@@ -812,23 +947,23 @@ Align payload at a boundary of \fIvalue\fR 512-byte sectors.
|
||||
This option is relevant for \fIluksFormat\fR.
|
||||
|
||||
If not specified, cryptsetup tries to use the topology info
|
||||
provided by kernel for the underlying device to get optimal alignment.
|
||||
provided by the kernel for the underlying device to get the optimal alignment.
|
||||
If not available (or the calculated value is a multiple of the default)
|
||||
data is by default aligned to a 1MiB boundary (i.e. 2048 512-byte sectors).
|
||||
|
||||
For a detached LUKS header this option specifies the offset on the
|
||||
For a detached LUKS header, this option specifies the offset on the
|
||||
data device. See also the \-\-header option.
|
||||
.TP
|
||||
.B "\-\-uuid=\fIUUID\fR"
|
||||
Use the provided \fIUUID\fR for the \fIluksFormat\fR command
|
||||
instead of generating new one. Changes the existing UUID when
|
||||
instead of generating a new one. Changes the existing UUID when
|
||||
used with the \fIluksUUID\fR command.
|
||||
|
||||
The UUID must be provided in the standard UUID format,
|
||||
e.g. 12345678-1234-1234-1234-123456789abc.
|
||||
.TP
|
||||
.B "\-\-allow\-discards\fR"
|
||||
Allow the use of discard (TRIM) requests for device.
|
||||
Allow the use of discard (TRIM) requests for the device.
|
||||
This option is only relevant for \fIopen\fR action.
|
||||
|
||||
\fBWARNING:\fR This command can have a negative security impact
|
||||
@@ -838,7 +973,7 @@ filesystem type, used space, etc. may be extractable from
|
||||
the physical device if the discarded blocks can be located
|
||||
later. If in doubt, do not use it.
|
||||
|
||||
A kernel version of 3.1 or later is needed. For earlier kernels
|
||||
A kernel version of 3.1 or later is needed. For earlier kernels,
|
||||
this option is ignored.
|
||||
.TP
|
||||
.B "\-\-perf\-same_cpu_crypt\fR"
|
||||
@@ -864,20 +999,20 @@ performance tuning, use only if you need a change to default dm-crypt
|
||||
behaviour. Needs kernel 4.0 or later.
|
||||
.TP
|
||||
.B "\-\-test\-passphrase\fR"
|
||||
Do not activate device, just verify passphrase.
|
||||
Do not activate the device, just verify passphrase.
|
||||
This option is only relevant for \fIopen\fR action (the device
|
||||
mapping name is not mandatory if this option is used).
|
||||
.TP
|
||||
.B "\-\-header\fR <device or file storing the LUKS header>"
|
||||
Use a detached (separated) metadata device or file where the
|
||||
LUKS header is stored. This options allows one to store ciphertext
|
||||
LUKS header is stored. This option allows one to store ciphertext
|
||||
and LUKS header on different devices.
|
||||
|
||||
This option is only relevant for LUKS devices and can be
|
||||
used with the \fIluksFormat\fR, \fIopen\fR, \fIluksSuspend\fR,
|
||||
\fIluksResume\fR, \fIstatus\fR and \fIresize\fR commands.
|
||||
|
||||
For \fIluksFormat\fR with a file name as argument to \-\-header,
|
||||
For \fIluksFormat\fR with a file name as the argument to \-\-header,
|
||||
it has to exist and be large enough to contain the LUKS header.
|
||||
See the cryptsetup FAQ for header size calculation.
|
||||
|
||||
@@ -889,23 +1024,129 @@ If used with \fIluksFormat\fR, the \-\-align\-payload option is taken
|
||||
as absolute sector alignment on ciphertext device and can be zero.
|
||||
|
||||
\fBWARNING:\fR There is no check whether the ciphertext device specified
|
||||
actually belongs to the header given. In fact you can specify an
|
||||
actually belongs to the header given. In fact, you can specify an
|
||||
arbitrary device as the ciphertext device for \fIopen\fR
|
||||
with the \-\-header option. Use with care.
|
||||
.TP
|
||||
.B "\-\-force\-password\fR"
|
||||
.B "\-\-header\-backup\-file <file>"
|
||||
Specify file with header backup for \fIluksHeaderBackup\fR or
|
||||
\fIluksHeaderBackup\fR actions.
|
||||
.TP
|
||||
.B "\-\-force\-password"
|
||||
Do not use password quality checking for new LUKS passwords.
|
||||
|
||||
This option applies only to \fIluksFormat\fR, \fIluksAddKey\fR and
|
||||
\fIluksChangeKey\fR and is ignored if cryptsetup is built without
|
||||
password quality checking support.
|
||||
|
||||
For more info about password quality check, see manual page
|
||||
For more info about password quality check, see the manual page
|
||||
for \fBpwquality.conf(5)\fR and \fBpasswdqc.conf(5)\fR.
|
||||
.TP
|
||||
.B "\-\-deferred"
|
||||
Defers device removal in \fIclose\fR command until the last user closes it.
|
||||
.TP
|
||||
.B "\-\-disable\-locks"
|
||||
Disable lock protection for metadata on disk.
|
||||
This option is valid only for LUKS2 and ignored for other formats.
|
||||
|
||||
\fBWARNING:\fR Do not use this option unless you run cryptsetup in
|
||||
a restricted environment where locking is impossible to perform
|
||||
(where /run directory cannot be used).
|
||||
.TP
|
||||
.B "\-\-disable\-keyring"
|
||||
Do not load volume key in kernel keyring but use store key directly
|
||||
in the dm-crypt target.
|
||||
This option is supported only for the LUKS2 format.
|
||||
.TP
|
||||
.B "\-\-key\-description <text>"
|
||||
Set key description in keyring for use with \fItoken\fR command.
|
||||
.TP
|
||||
.B "\-\-priority <normal|prefer|ignore>"
|
||||
Set a priority for LUKS2 keyslot.
|
||||
The \fIprefer\fR priority marked slots are tried before \fInormal\fR priority.
|
||||
The \fIignored\fR priority means, that slot is never used, if not explicitly
|
||||
requested by \fI\-\-key\-slot\fR option.
|
||||
.TP
|
||||
.B "\-\-token\-id"
|
||||
Specify what token to use in actions \fItoken\fR, \fIopen\fR or \fIresize\fR.
|
||||
If omitted, all available tokens will be checked before proceeding further with
|
||||
passphrase prompt.
|
||||
.TP
|
||||
.B "\-\-token\-only"
|
||||
Do not proceed further with action (any of \fItoken\fR, \fIopen\fR or
|
||||
\fIresize\fR) if token activation failed. Without the option,
|
||||
action asks for passphrase to proceed further.
|
||||
.TP
|
||||
.B "\-\-sector\-size <bytes>"
|
||||
Set sector size for use with disk encryption. It must be power of two
|
||||
and in range 512 - 4096 bytes. The default is 512 bytes sectors.
|
||||
This option is available only in the LUKS2 mode.
|
||||
|
||||
Note that if sector size is higher than underlying device hardware sector
|
||||
and there is not integrity protection that uses data journal, using
|
||||
this option can increase risk on incomplete sector writes during a power fail.
|
||||
|
||||
If used together with \fI\-\-integrity\fR option and dm-integrity journal,
|
||||
the atomicity of writes is guaranteed in all cases (but it cost write
|
||||
performance - data has to be written twice).
|
||||
|
||||
Increasing sector size from 512 bytes to 4096 bytes can provide better
|
||||
performance on most of the modern storage devices and also with some
|
||||
hw encryption accelerators.
|
||||
.TP
|
||||
.B "\-\-persistent"
|
||||
If used with LUKS2 devices and activation commands like \fIopen\fR,
|
||||
the specified activation flags are persistently written into metadata
|
||||
and used next time automatically even for normal activation.
|
||||
(No need to use cryptab or other system configuration files.)
|
||||
Only \fI\-\-allow-discards\fR, \fI\-\-perf\-same_cpu_crypt\fR,
|
||||
\fI\-\-perf\-submit_from_crypt_cpus\fR and \fI\-\-integrity\-no\-journal\fR
|
||||
can be stored persistently.
|
||||
.TP
|
||||
.B "\-\-label <LABEL>"
|
||||
.B "\-\-subsystem <SUBSYSTEM>"
|
||||
Set label and subsystem description for LUKS2 device, can be used
|
||||
in \fIconfig\fR and \fIformat\fR actions.
|
||||
The label and subsystem are optional fields and can be later used in udev scripts
|
||||
for triggering user actions once device marked by these labels is detected.
|
||||
.TP
|
||||
.B "\-\-integrity <integrity algorithm>"
|
||||
Specify integrity algorithm to be used for authenticated disk encryption in LUKS2.
|
||||
|
||||
\fBWARNING: This extension is EXPERIMENTAL\fR and requires dm-integrity
|
||||
kernel target (available since kernel version 4.12).
|
||||
For more info, see \fIAUTHENTICATED DISK ENCRYPTION\fR section.
|
||||
.TP
|
||||
.B "\-\-integrity\-\no\-journal"
|
||||
Activate device with integrity protection without using data journal (direct
|
||||
write of data and integrity tags).
|
||||
Note that without journal power fail can cause non-atomic write and data corruption.
|
||||
Use only if journalling is performed on a different storage layer.
|
||||
.TP
|
||||
.B "\-\-integrity\-\no\-wipe"
|
||||
Skip wiping of device authentication (integrity) tags. If you skip this
|
||||
step, sectors will report invalid integrity tag until an application write
|
||||
to the sector.
|
||||
|
||||
\fBNOTE:\fR Even some writes to the device can fail if the write is not
|
||||
aligned to page size and page-cache initiates read of a sector with invalid
|
||||
integrity tag.
|
||||
.TP
|
||||
.B "\-\-tcrypt\-hidden"
|
||||
.B "\-\-tcrypt\-system"
|
||||
.B "\-\-tcrypt\-backup"
|
||||
Specify which TrueCrypt on-disk header will be used to open the device.
|
||||
See \fITCRYPT\fR section for more info.
|
||||
.TP
|
||||
.B "\-\-veracrypt"
|
||||
Allow VeraCrypt compatible mode. Only for TCRYPT extension.
|
||||
See \fITCRYPT\fR section for more info.
|
||||
.TP
|
||||
.B "\-\-veracrypt\-pim"
|
||||
.B "\-\-veracrypt\-query\-pim"
|
||||
Use a custom Personal Iteration Multiplier (PIM) for VeraCrypt device.
|
||||
See \fITCRYPT\fR section for more info.
|
||||
.TP
|
||||
.B "\-\-version"
|
||||
Show the program version.
|
||||
.TP
|
||||
@@ -956,7 +1197,7 @@ less than the key size.
|
||||
|
||||
\fBFrom a key file\fR: It will be truncated to the
|
||||
key size of the used cipher or the size given by \-s
|
||||
and directly used as binary key.
|
||||
and directly used as a binary key.
|
||||
|
||||
\fBWARNING\fR: The \-\-hash argument is being ignored.
|
||||
The \-\-hash option is usable only for stdin input in plain mode.
|
||||
@@ -998,10 +1239,10 @@ low-entropy passphrases, but open will take longer to
|
||||
complete. For passphrases that have entropy higher than the
|
||||
used key length, higher iteration times will not increase security.
|
||||
|
||||
The default setting of one second is sufficient for most
|
||||
The default setting of one or two seconds is sufficient for most
|
||||
practical cases. The only exception is a low-entropy
|
||||
passphrase used on a device with a slow CPU, as this will
|
||||
result in a low iteration count. On a slow device it may
|
||||
result in a low iteration count. On a slow device, it may
|
||||
be advisable to increase the iteration time using the
|
||||
\-\-iter\-time option in order to obtain a higher
|
||||
iteration count. This does slow down all later luksOpen
|
||||
@@ -1021,7 +1262,7 @@ in order to get more options.
|
||||
|
||||
For the \-\-hash option, if the crypto backend is libgcrypt,
|
||||
then all algorithms supported by the gcrypt library are available.
|
||||
For other crypto backends some algorithms may be missing.
|
||||
For other crypto backends, some algorithms may be missing.
|
||||
.SH NOTES ON PASSPHRASES
|
||||
Mathematics can't be bribed. Make sure you keep your passphrases safe.
|
||||
There are a few nice tricks for constructing a fallback, when suddenly
|
||||
@@ -1050,13 +1291,49 @@ time, using /dev/urandom in a low-entropy situation will
|
||||
produce low-quality keys. This is a serious problem, but solving
|
||||
it is out of scope for a mere man-page.
|
||||
See \fPurandom(4)\fR for more information.
|
||||
.SH AUTHENTICATED DISK ENCRYPTION (EXPERIMENTAL)
|
||||
Since Linux kernel version 4.12 dm-crypt supports authenticated
|
||||
disk encryption.
|
||||
|
||||
Normal disk encryption modes are length-preserving (plaintext sector
|
||||
is of the same size as a ciphertext sector) and can provide only
|
||||
confidentiality protection, but not cryptographically sound
|
||||
data integrity protection.
|
||||
|
||||
Authenticated modes require additional space per-sector for
|
||||
authentication tag and use Authenticated Encryption with Additional
|
||||
Data (AEAD) algorithms.
|
||||
|
||||
If you configure LUKS2 device with data integrity protection,
|
||||
there will be an underlying dm-integrity device, which provides
|
||||
additional per-sector metadata space and also provide data
|
||||
journal protection to ensure atomicity of data and metadata update.
|
||||
Because there must be additional space for metadata and journal,
|
||||
the available space for the device will be smaller than for
|
||||
length-preserving modes.
|
||||
|
||||
The dm-crypt device then resides on top of such a dm-integrity device.
|
||||
All activation and deactivation of this device stack is performed
|
||||
by cryptsetup, there is no difference in using \fIluksOpen\fR
|
||||
for integrity protected devices.
|
||||
If you want to format LUKS2 device with data integrity protection,
|
||||
use \fI\-\-integrity\fR option.
|
||||
|
||||
Some integrity modes requires two independent keys (key for encryption
|
||||
and for authentication). Both these keys are stored in one LUKS keyslot.
|
||||
|
||||
\fBWARNING:\fR All support for authenticated modes is experimental
|
||||
and there are only some modes available for now. Note that there
|
||||
are a very few authenticated encryption algorithms that are suitable
|
||||
for disk encryption.
|
||||
|
||||
.SH NOTES ON LOOPBACK DEVICE USE
|
||||
Cryptsetup is usually used directly on a block device (disk
|
||||
partition or LVM volume). However, if the device argument is a
|
||||
file, cryptsetup tries to allocate a loopback device
|
||||
and map it into this file. This mode requires Linux kernel 2.6.25
|
||||
or more recent which supports the loop autoclear flag (loop device is
|
||||
cleared on last close automatically). Of course, you can
|
||||
cleared on the last close automatically). Of course, you can
|
||||
always map a file to a loop-device manually. See the
|
||||
cryptsetup FAQ for an example.
|
||||
|
||||
|
||||
@@ -23,6 +23,16 @@ lib/verity/verity.c
|
||||
lib/verity/verity_hash.c
|
||||
lib/verity/verity_fec.c
|
||||
lib/integrity/integrity.c
|
||||
lib/luks2/luks2_digest.c
|
||||
lib/luks2/luks2_digest_pbkdf2.c
|
||||
lib/luks2/luks2_disk_metadata.c
|
||||
lib/luks2/luks2_json_format.c
|
||||
lib/luks2/luks2_json_metadata.c
|
||||
lib/luks2/luks2_keyslot.c
|
||||
lib/luks2/luks2_keyslot_luks2.c
|
||||
lib/luks2/luks2_luks1_convert.c
|
||||
lib/luks2/luks2_token.c
|
||||
lib/luks2/luks2_token_keyring.c
|
||||
src/cryptsetup.c
|
||||
src/veritysetup.c
|
||||
src/integritysetup.c
|
||||
|
||||
@@ -21,7 +21,8 @@ cryptsetup_LDADD = -lm \
|
||||
$(top_builddir)/lib/libcryptsetup.la \
|
||||
@POPT_LIBS@ \
|
||||
@PWQUALITY_LIBS@ \
|
||||
@PASSWDQC_LIBS@
|
||||
@PASSWDQC_LIBS@ \
|
||||
@UUID_LIBS@
|
||||
|
||||
cryptsetup_CFLAGS = $(AM_CFLAGS) -Wall
|
||||
|
||||
|
||||
529
src/cryptsetup.c
529
src/cryptsetup.c
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
|
||||
#include "cryptsetup.h"
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
static const char *opt_cipher = NULL;
|
||||
static const char *opt_hash = NULL;
|
||||
@@ -43,6 +44,8 @@ static long opt_new_keyfile_size = 0;
|
||||
static long opt_keyfile_offset = 0;
|
||||
static long opt_new_keyfile_offset = 0;
|
||||
static int opt_key_slot = CRYPT_ANY_SLOT;
|
||||
static int opt_token = CRYPT_ANY_TOKEN;
|
||||
static int opt_token_only = 0;
|
||||
static uint64_t opt_size = 0;
|
||||
static uint64_t opt_offset = 0;
|
||||
static uint64_t opt_skip = 0;
|
||||
@@ -69,10 +72,21 @@ static int opt_veracrypt_query_pim = 0;
|
||||
static int opt_deferred_remove = 0;
|
||||
//FIXME: check uint32 overflow for long type
|
||||
static const char *opt_pbkdf = NULL;
|
||||
static long opt_pbkdf_memory = 1024;
|
||||
static long opt_pbkdf_parallel = 2;
|
||||
static long opt_pbkdf_memory = DEFAULT_LUKS2_MEMORY_KB;
|
||||
static long opt_pbkdf_parallel = DEFAULT_LUKS2_PARALLEL_THREADS;
|
||||
static long opt_pbkdf_iterations = 0;
|
||||
static int opt_iteration_time = 0;
|
||||
static int opt_disable_locks = 0;
|
||||
static int opt_disable_keyring = 0;
|
||||
static const char *opt_priority = NULL; /* normal */
|
||||
static const char *opt_integrity = NULL; /* none */
|
||||
static int opt_integrity_nojournal = 0;
|
||||
static int opt_integrity_no_wipe = 0;
|
||||
static const char *opt_key_description = NULL;
|
||||
static int opt_sector_size = 512;
|
||||
static int opt_persistent = 0;
|
||||
static const char *opt_label = NULL;
|
||||
static const char *opt_subsystem = NULL;
|
||||
|
||||
static const char **action_argv;
|
||||
static int action_argc;
|
||||
@@ -86,6 +100,23 @@ static const char *uuid_or_device_header(const char **data_device)
|
||||
return uuid_or_device(opt_header_device ?: action_argv[0]);
|
||||
}
|
||||
|
||||
static const char *luksType(const char *type)
|
||||
{
|
||||
if (type && !strcmp(type, "luks2"))
|
||||
return CRYPT_LUKS2;
|
||||
|
||||
if (type && !strcmp(type, "luks1"))
|
||||
return CRYPT_LUKS1;
|
||||
|
||||
if (type && !strcmp(type, "luks"))
|
||||
return CRYPT_LUKS; /* NULL */
|
||||
|
||||
if (type && *type)
|
||||
return type;
|
||||
|
||||
return CRYPT_LUKS; /* NULL */
|
||||
}
|
||||
|
||||
static int _verify_passphrase(int def)
|
||||
{
|
||||
/* Batch mode switch off verify - if not overrided by -y */
|
||||
@@ -117,6 +148,13 @@ static void _set_activation_flags(uint32_t *flags)
|
||||
|
||||
if (opt_perf_submit_from_crypt_cpus)
|
||||
*flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS;
|
||||
|
||||
if (opt_integrity_nojournal)
|
||||
*flags |= CRYPT_ACTIVATE_NO_JOURNAL;
|
||||
|
||||
/* In persistent mode, we use what is set on command line */
|
||||
if (opt_persistent)
|
||||
*flags |= CRYPT_ACTIVATE_IGNORE_PERSISTENT;
|
||||
}
|
||||
|
||||
static int action_open_plain(void)
|
||||
@@ -449,12 +487,45 @@ static int action_close(void)
|
||||
|
||||
static int action_resize(void)
|
||||
{
|
||||
struct crypt_device *cd = NULL;
|
||||
int r;
|
||||
size_t passwordLen;
|
||||
struct crypt_active_device cad;
|
||||
char *password = NULL;
|
||||
struct crypt_device *cd = NULL;
|
||||
|
||||
r = crypt_init_by_name_and_header(&cd, action_argv[0], opt_header_device);
|
||||
if (r == 0)
|
||||
r = crypt_resize(cd, action_argv[0], opt_size);
|
||||
if (r == 0) {
|
||||
r = crypt_get_active_device(cd, action_argv[0], &cad);
|
||||
|
||||
if (!r && (cad.flags & CRYPT_ACTIVATE_KEYRING_KEY)) {
|
||||
if (opt_disable_keyring) {
|
||||
r = -EINVAL;
|
||||
log_err(_("Resize of active device requires volume key in keyring but --disable-keyring option is set.\n"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* try load VK in kernel keyring using token */
|
||||
r = crypt_activate_by_token(cd, NULL, opt_token, NULL, CRYPT_ACTIVATE_KEYRING_KEY);
|
||||
if (r < 0) {
|
||||
if (opt_token_only)
|
||||
goto out;
|
||||
|
||||
r = tools_get_key(NULL, &password, &passwordLen,
|
||||
opt_keyfile_offset, opt_keyfile_size, opt_key_file,
|
||||
opt_timeout, _verify_passphrase(0), 0, cd);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = crypt_activate_by_passphrase(cd, NULL, opt_key_slot, password, passwordLen, CRYPT_ACTIVATE_KEYRING_KEY);
|
||||
|
||||
crypt_safe_free(password);
|
||||
}
|
||||
}
|
||||
|
||||
if (r >= 0)
|
||||
r = crypt_resize(cd, action_argv[0], opt_size);
|
||||
}
|
||||
out:
|
||||
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
@@ -464,6 +535,7 @@ static int action_status(void)
|
||||
{
|
||||
crypt_status_info ci;
|
||||
struct crypt_active_device cad;
|
||||
struct crypt_params_integrity ip = {};
|
||||
struct crypt_device *cd = NULL;
|
||||
char *backing_file;
|
||||
const char *device;
|
||||
@@ -504,8 +576,17 @@ static int action_status(void)
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
r = crypt_get_integrity_info(cd, &ip);
|
||||
if (r < 0 && r != -ENOTSUP)
|
||||
goto out;
|
||||
|
||||
log_std(" cipher: %s-%s\n", crypt_get_cipher(cd), crypt_get_cipher_mode(cd));
|
||||
log_std(" keysize: %d bits\n", crypt_get_volume_key_size(cd) * 8);
|
||||
log_std(" key location: %s\n", (cad.flags & CRYPT_ACTIVATE_KEYRING_KEY) ? "keyring" : "dm-crypt");
|
||||
if (ip.integrity)
|
||||
log_std(" integrity: %s\n", ip.integrity);
|
||||
if (ip.integrity_key_size)
|
||||
log_std(" integrity keysize: %d bits\n", ip.integrity_key_size * 8);
|
||||
device = crypt_get_device_name(cd);
|
||||
log_std(" device: %s\n", device);
|
||||
if (crypt_loop_device(device)) {
|
||||
@@ -513,6 +594,7 @@ static int action_status(void)
|
||||
log_std(" loop: %s\n", backing_file);
|
||||
free(backing_file);
|
||||
}
|
||||
log_std(" sector size: %d\n", crypt_get_sector_size(cd));
|
||||
log_std(" offset: %" PRIu64 " sectors\n", cad.offset);
|
||||
log_std(" size: %" PRIu64 " sectors\n", cad.size);
|
||||
if (cad.iv_offset)
|
||||
@@ -569,7 +651,7 @@ static int action_benchmark_kdf(const char *kdf, const char *hash, size_t key_si
|
||||
} else {
|
||||
struct crypt_pbkdf_type pbkdf = {
|
||||
.type = kdf,
|
||||
.time_ms = opt_iteration_time ?: 800,
|
||||
.time_ms = opt_iteration_time ?: DEFAULT_LUKS2_ITER_TIME,
|
||||
.max_memory_kb = opt_pbkdf_memory,
|
||||
.parallel_threads = opt_pbkdf_parallel,
|
||||
};
|
||||
@@ -765,6 +847,14 @@ static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type)
|
||||
pbkdf.type = CRYPT_KDF_PBKDF2;
|
||||
pbkdf.hash = opt_hash ?: DEFAULT_LUKS1_HASH;
|
||||
pbkdf.time_ms = opt_iteration_time ?: DEFAULT_LUKS1_ITER_TIME;
|
||||
} else if (!strcmp(dev_type, CRYPT_LUKS2)) {
|
||||
pbkdf.type = opt_pbkdf ?: DEFAULT_LUKS2_PBKDF;
|
||||
pbkdf.hash = opt_hash ?: DEFAULT_LUKS1_HASH;
|
||||
pbkdf.time_ms = opt_iteration_time ?: DEFAULT_LUKS2_ITER_TIME;
|
||||
if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) {
|
||||
pbkdf.max_memory_kb = opt_pbkdf_memory;
|
||||
pbkdf.parallel_threads = opt_pbkdf_parallel;
|
||||
}
|
||||
} else
|
||||
return 0;
|
||||
|
||||
@@ -784,9 +874,8 @@ static int action_luksRepair(void)
|
||||
if ((r = crypt_init(&cd, action_argv[0])))
|
||||
goto out;
|
||||
|
||||
/* Currently only LUKS1 allows repair */
|
||||
crypt_set_log_callback(cd, quiet_log, NULL);
|
||||
r = crypt_load(cd, CRYPT_LUKS1, NULL);
|
||||
r = crypt_load(cd, luksType(opt_type), NULL);
|
||||
crypt_set_log_callback(cd, tool_log, NULL);
|
||||
if (r == 0) {
|
||||
log_verbose(_("No known problems detected for LUKS header.\n"));
|
||||
@@ -796,18 +885,53 @@ static int action_luksRepair(void)
|
||||
r = yesDialog(_("Really try to repair LUKS device header?"),
|
||||
NULL) ? 0 : -EINVAL;
|
||||
if (r == 0)
|
||||
r = crypt_repair(cd, CRYPT_LUKS1, NULL);
|
||||
r = crypt_repair(cd, luksType(opt_type), NULL);
|
||||
out:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _wipe_data_device(struct crypt_device *cd)
|
||||
{
|
||||
char tmp_name[64], tmp_path[128], tmp_uuid[40];
|
||||
uuid_t tmp_uuid_bin;
|
||||
int r;
|
||||
|
||||
if (!opt_batch_mode)
|
||||
log_std(_("Wiping device to initialize integrity checksum.\n"
|
||||
"You can interrupt this by pressing CTRL+c "
|
||||
"(rest of not wiped device will contain invalid checksum).\n"));
|
||||
|
||||
/* Activate the device a temporary one */
|
||||
uuid_generate(tmp_uuid_bin);
|
||||
uuid_unparse(tmp_uuid_bin, tmp_uuid);
|
||||
if (snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid) < 0)
|
||||
return -EINVAL;
|
||||
if (snprintf(tmp_path, sizeof(tmp_path), "%s/%s", crypt_get_dir(), tmp_name) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = crypt_activate_by_volume_key(cd, tmp_name, NULL, 0,
|
||||
CRYPT_ACTIVATE_PRIVATE | CRYPT_ACTIVATE_NO_JOURNAL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Wipe the device */
|
||||
set_int_handler(0);
|
||||
r = crypt_wipe(cd, tmp_path, CRYPT_WIPE_ZERO, 0, 0, DEFAULT_WIPE_BLOCK,
|
||||
0, &tools_wipe_progress, NULL);
|
||||
if (crypt_deactivate(cd, tmp_name))
|
||||
log_err(_("Cannot deactivate temporary device %s.\n"), tmp_path);
|
||||
set_int_block(0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int action_luksFormat(void)
|
||||
{
|
||||
int r = -EINVAL, keysize;
|
||||
int r = -EINVAL, keysize, integrity_keysize = 0, luks_version;
|
||||
const char *header_device;
|
||||
char *msg = NULL, *key = NULL, cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
|
||||
char *password = NULL;
|
||||
char *msg = NULL, *key = NULL, *password = NULL;
|
||||
char cipher [MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN], integrity[MAX_CIPHER_LEN];
|
||||
size_t passwordLen;
|
||||
struct crypt_device *cd = NULL;
|
||||
struct crypt_params_luks1 params = {
|
||||
@@ -815,6 +939,25 @@ static int action_luksFormat(void)
|
||||
.data_alignment = opt_align_payload,
|
||||
.data_device = opt_header_device ? action_argv[0] : NULL,
|
||||
};
|
||||
struct crypt_params_luks2 params2 = {
|
||||
.data_alignment = params.data_alignment,
|
||||
.data_device = params.data_device,
|
||||
.sector_size = opt_sector_size,
|
||||
.label = opt_label,
|
||||
.subsystem = opt_subsystem
|
||||
};
|
||||
|
||||
if (!opt_type)
|
||||
return -EINVAL;
|
||||
else if (!strcmp(opt_type, "luks2"))
|
||||
luks_version = 2;
|
||||
else
|
||||
luks_version = 1;
|
||||
|
||||
if (opt_sector_size > 512 && luks_version == 1) {
|
||||
log_err(_("Unsupported encryption sector size.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
header_device = opt_header_device ?: action_argv[0];
|
||||
|
||||
@@ -836,6 +979,16 @@ static int action_luksFormat(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (luks_version == 2 && opt_integrity) {
|
||||
r = crypt_parse_integrity_mode(opt_integrity, integrity, &integrity_keysize);
|
||||
if (r < 0) {
|
||||
log_err(_("No known integrity specification pattern detected.\n"));
|
||||
goto out;
|
||||
}
|
||||
params2.integrity = integrity;
|
||||
/* FIXME: we use default integrity_params (set to NULL) */
|
||||
}
|
||||
|
||||
/* Never call pwquality if using null cipher */
|
||||
if (tools_is_cipher_null(cipher))
|
||||
opt_force_password = 1;
|
||||
@@ -846,7 +999,7 @@ static int action_luksFormat(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8;
|
||||
keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8 + integrity_keysize;
|
||||
|
||||
if (opt_random)
|
||||
crypt_set_rng_type(cd, CRYPT_RNG_RANDOM);
|
||||
@@ -865,14 +1018,21 @@ static int action_luksFormat(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = set_pbkdf_params(cd, CRYPT_LUKS1);
|
||||
if (luks_version == 1)
|
||||
r = set_pbkdf_params(cd, CRYPT_LUKS1);
|
||||
else
|
||||
r = set_pbkdf_params(cd, CRYPT_LUKS2);
|
||||
if (r) {
|
||||
log_err(_("Failed to set pbkdf parameters.\n"));
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode,
|
||||
opt_uuid, key, keysize, ¶ms);
|
||||
if (luks_version == 1)
|
||||
r = crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode,
|
||||
opt_uuid, key, keysize, ¶ms);
|
||||
else
|
||||
r = crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode,
|
||||
opt_uuid, key, keysize, ¶ms2);
|
||||
check_signal(&r);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
@@ -880,6 +1040,11 @@ static int action_luksFormat(void)
|
||||
r = crypt_keyslot_add_by_volume_key(cd, opt_key_slot,
|
||||
key, keysize,
|
||||
password, passwordLen);
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
if (opt_integrity && !opt_integrity_no_wipe)
|
||||
r = _wipe_data_device(cd);
|
||||
out:
|
||||
crypt_free(cd);
|
||||
crypt_safe_free(key);
|
||||
@@ -905,7 +1070,7 @@ static int action_open_luks(void)
|
||||
if ((r = crypt_init(&cd, header_device)))
|
||||
goto out;
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
if (data_device &&
|
||||
@@ -928,6 +1093,10 @@ static int action_open_luks(void)
|
||||
r = crypt_activate_by_volume_key(cd, activated_name,
|
||||
key, keysize, activate_flags);
|
||||
} else {
|
||||
r = crypt_activate_by_token(cd, activated_name, opt_token, NULL, activate_flags);
|
||||
if (r >= 0 || opt_token_only)
|
||||
goto out;
|
||||
|
||||
tries = (opt_key_file && !tools_is_stdin(opt_key_file)) ? 1 : opt_tries;
|
||||
do {
|
||||
r = tools_get_key(NULL, &password, &passwordLen,
|
||||
@@ -945,6 +1114,11 @@ static int action_open_luks(void)
|
||||
} while ((r == -EPERM || r == -ERANGE) && (--tries > 0));
|
||||
}
|
||||
out:
|
||||
if (r >= 0 && opt_persistent &&
|
||||
crypt_persistent_flags_set(cd, CRYPT_FLAGS_ACTIVATION, activate_flags))
|
||||
log_err(_("Device activated but cannot make flags persistent.\n"));
|
||||
|
||||
|
||||
crypt_safe_free(key);
|
||||
crypt_safe_free(password);
|
||||
crypt_free(cd);
|
||||
@@ -959,7 +1133,7 @@ static int verify_keyslot(struct crypt_device *cd, int key_slot,
|
||||
crypt_keyslot_info ki;
|
||||
char *password = NULL;
|
||||
size_t passwordLen;
|
||||
int i, r;
|
||||
int i, max, r;
|
||||
|
||||
ki = crypt_keyslot_status(cd, key_slot);
|
||||
if (ki == CRYPT_SLOT_ACTIVE_LAST && !opt_batch_mode && !key_file &&
|
||||
@@ -969,7 +1143,7 @@ static int verify_keyslot(struct crypt_device *cd, int key_slot,
|
||||
r = tools_get_key(msg_pass, &password, &passwordLen,
|
||||
keyfile_offset, keyfile_size, key_file, opt_timeout,
|
||||
_verify_passphrase(0), 0, cd);
|
||||
if(r < 0)
|
||||
if (r < 0)
|
||||
goto out;
|
||||
|
||||
if (ki == CRYPT_SLOT_ACTIVE_LAST) {
|
||||
@@ -978,13 +1152,18 @@ static int verify_keyslot(struct crypt_device *cd, int key_slot,
|
||||
password, passwordLen, 0);
|
||||
} else {
|
||||
/* try all other keyslots */
|
||||
for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) {
|
||||
r = crypt_keyslot_max(crypt_get_type(cd));
|
||||
if (r < 0)
|
||||
goto out;
|
||||
max = r;
|
||||
|
||||
for (i = 0; i < max ; i++) {
|
||||
if (i == key_slot)
|
||||
continue;
|
||||
ki = crypt_keyslot_status(cd, key_slot);
|
||||
if (ki == CRYPT_SLOT_ACTIVE)
|
||||
r = crypt_activate_by_passphrase(cd, NULL, i,
|
||||
password, passwordLen, 0);
|
||||
ki = crypt_keyslot_status(cd, i);
|
||||
if (ki == CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_ACTIVE_LAST)
|
||||
r = crypt_activate_by_passphrase(cd, NULL, i,
|
||||
password, passwordLen, 0);
|
||||
if (r == i)
|
||||
break;
|
||||
}
|
||||
@@ -1011,21 +1190,9 @@ static int action_luksKillSlot(void)
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
switch (crypt_keyslot_status(cd, opt_key_slot)) {
|
||||
case CRYPT_SLOT_ACTIVE_LAST:
|
||||
case CRYPT_SLOT_ACTIVE:
|
||||
log_verbose(_("Key slot %d selected for deletion.\n"), opt_key_slot);
|
||||
break;
|
||||
case CRYPT_SLOT_INACTIVE:
|
||||
log_err(_("Key %d not active. Can't wipe.\n"), opt_key_slot);
|
||||
case CRYPT_SLOT_INVALID:
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!opt_batch_mode || opt_key_file || !isatty(STDIN_FILENO)) {
|
||||
r = verify_keyslot(cd, opt_key_slot,
|
||||
_("This is the last keyslot. Device will become unusable after purging this key."),
|
||||
@@ -1059,7 +1226,7 @@ static int action_luksRemoveKey(void)
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
r = tools_get_key(_("Enter passphrase to be deleted: "),
|
||||
@@ -1109,7 +1276,7 @@ static int action_luksAddKey(void)
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
/* Never call pwquality if using null cipher */
|
||||
@@ -1194,7 +1361,7 @@ static int action_luksChangeKey(void)
|
||||
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
|
||||
goto out;
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
/* Never call pwquality if using null cipher */
|
||||
@@ -1253,7 +1420,7 @@ static int action_isLuks(void)
|
||||
goto out;
|
||||
|
||||
crypt_set_log_callback(cd, quiet_log, NULL);
|
||||
r = crypt_load(cd, CRYPT_LUKS1, NULL);
|
||||
r = crypt_load(cd, luksType(opt_type), NULL);
|
||||
out:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
@@ -1270,7 +1437,7 @@ static int action_luksUUID(void)
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
if (opt_uuid)
|
||||
@@ -1347,7 +1514,7 @@ static int action_luksDump(void)
|
||||
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
|
||||
goto out;
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
if (opt_dump_master_key)
|
||||
@@ -1382,7 +1549,7 @@ static int action_luksResume(void)
|
||||
if ((r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device))))
|
||||
goto out;
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
tries = (opt_key_file && !tools_is_stdin(opt_key_file)) ? 1 : opt_tries;
|
||||
@@ -1421,7 +1588,7 @@ static int action_luksBackup(void)
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
r = crypt_header_backup(cd, CRYPT_LUKS1, opt_header_backup_file);
|
||||
r = crypt_header_backup(cd, NULL, opt_header_backup_file);
|
||||
out:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
@@ -1441,7 +1608,7 @@ static int action_luksRestore(void)
|
||||
goto out;
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
r = crypt_header_restore(cd, CRYPT_LUKS1, opt_header_backup_file);
|
||||
r = crypt_header_restore(cd, NULL, opt_header_backup_file);
|
||||
out:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
@@ -1452,7 +1619,9 @@ static int action_open(void)
|
||||
if (!opt_type)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strcmp(opt_type, "luks") || !strcmp(opt_type, "luks1")) {
|
||||
if (!strcmp(opt_type, "luks") ||
|
||||
!strcmp(opt_type, "luks1") ||
|
||||
!strcmp(opt_type, "luks2")) {
|
||||
if (action_argc < 2 && !opt_test_passphrase)
|
||||
goto args;
|
||||
return action_open_luks();
|
||||
@@ -1482,14 +1651,14 @@ static int action_luksErase(void)
|
||||
struct crypt_device *cd = NULL;
|
||||
crypt_keyslot_info ki;
|
||||
char *msg = NULL;
|
||||
int i, r;
|
||||
int i, max, r;
|
||||
|
||||
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
|
||||
goto out;
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS1, NULL)))
|
||||
if ((r = crypt_load(cd, luksType(opt_type), NULL)))
|
||||
goto out;
|
||||
|
||||
if(asprintf(&msg, _("This operation will erase all keyslots on device %s.\n"
|
||||
@@ -1504,7 +1673,12 @@ static int action_luksErase(void)
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < crypt_keyslot_max(CRYPT_LUKS1); i++) {
|
||||
/* Safety check */
|
||||
max = crypt_keyslot_max(crypt_get_type(cd));
|
||||
if (max <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
ki = crypt_keyslot_status(cd, i);
|
||||
if (ki == CRYPT_SLOT_ACTIVE || ki == CRYPT_SLOT_ACTIVE_LAST) {
|
||||
r = crypt_keyslot_destroy(cd, i);
|
||||
@@ -1518,6 +1692,182 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int action_luksConvert(void)
|
||||
{
|
||||
struct crypt_device *cd = NULL;
|
||||
char *msg = NULL;
|
||||
const char *to_type, *from_type;
|
||||
int r;
|
||||
|
||||
if (!strcmp(opt_type, "luks2")) {
|
||||
to_type = CRYPT_LUKS2;
|
||||
} else if (!strcmp(opt_type, "luks1")) {
|
||||
to_type = CRYPT_LUKS1;
|
||||
} else {
|
||||
log_err(_("Missing LUKS target type, option --type is required.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
|
||||
return r;
|
||||
|
||||
crypt_set_confirm_callback(cd, yesDialog, NULL);
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS, NULL)) ||
|
||||
!(from_type = crypt_get_type(cd))) {
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!strcmp(from_type, to_type)) {
|
||||
log_err(_("Device is already %s type.\n"), to_type);
|
||||
crypt_free(cd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (asprintf(&msg, _("This operation will convert %s to %s format.\n"),
|
||||
uuid_or_device_header(NULL), to_type) == -1) {
|
||||
crypt_free(cd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (yesDialog(msg, NULL))
|
||||
r = crypt_convert(cd, to_type, NULL);
|
||||
else
|
||||
r = -EPERM;
|
||||
|
||||
free(msg);
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _config_priority(struct crypt_device *cd)
|
||||
{
|
||||
crypt_keyslot_info cs;
|
||||
crypt_keyslot_priority priority = CRYPT_SLOT_PRIORITY_INVALID;
|
||||
|
||||
if (!strcmp("normal", opt_priority))
|
||||
priority = CRYPT_SLOT_PRIORITY_NORMAL;
|
||||
else if (!strcmp("prefer", opt_priority))
|
||||
priority = CRYPT_SLOT_PRIORITY_PREFER;
|
||||
else if (!strcmp("ignore", opt_priority))
|
||||
priority = CRYPT_SLOT_PRIORITY_IGNORE;
|
||||
|
||||
cs = crypt_keyslot_status(cd, opt_key_slot);
|
||||
if (cs != CRYPT_SLOT_INVALID)
|
||||
return crypt_keyslot_set_priority(cd, opt_key_slot, priority);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int _config_labels(struct crypt_device *cd)
|
||||
{
|
||||
return crypt_set_label(cd, opt_label, opt_subsystem);
|
||||
}
|
||||
|
||||
static int action_luksConfig(void)
|
||||
{
|
||||
struct crypt_device *cd = NULL;
|
||||
int r;
|
||||
|
||||
if (!opt_priority && !opt_label && !opt_subsystem) {
|
||||
log_err(_("Option --priority, --label or --subsystem is missing.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
|
||||
return r;
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS2, NULL)))
|
||||
goto out;
|
||||
|
||||
if (opt_priority && (r = _config_priority(cd)))
|
||||
goto out;
|
||||
|
||||
if ((opt_label || opt_subsystem) && (r = _config_labels(cd)))
|
||||
goto out;
|
||||
out:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _token_add(struct crypt_device *cd)
|
||||
{
|
||||
int r, token;
|
||||
crypt_token_info token_info;
|
||||
const struct crypt_token_params_luks2_keyring params = {
|
||||
.key_description = opt_key_description
|
||||
};
|
||||
|
||||
if (opt_token != CRYPT_ANY_TOKEN) {
|
||||
token_info = crypt_token_status(cd, opt_token, NULL);
|
||||
if (token_info < CRYPT_TOKEN_INACTIVE) {
|
||||
log_err(_("Token %d is invalid.\n"), opt_token);
|
||||
return -EINVAL;
|
||||
} else if (token_info > CRYPT_TOKEN_INACTIVE) {
|
||||
log_err(_("Token %d in use.\n"), opt_token);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
r = crypt_token_luks2_keyring_set(cd, opt_token, ¶ms);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
token = r;
|
||||
|
||||
r = crypt_token_assign_keyslot(cd, token, opt_key_slot);
|
||||
if (r < 0) {
|
||||
log_err(_("Failed to assign token %d to keyslot %d.\n"), token, opt_key_slot);
|
||||
(void) crypt_token_json_set(cd, token, NULL);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int action_token(void)
|
||||
{
|
||||
int add, r;
|
||||
struct crypt_device *cd = NULL;
|
||||
|
||||
if (!strcmp(action_argv[0], "add")) {
|
||||
if (!opt_key_description) {
|
||||
log_err(_("--key-description parameter is mandatory for token add action.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
add = 1;
|
||||
} else if (!strcmp(action_argv[0], "remove")) {
|
||||
if (opt_token == CRYPT_ANY_TOKEN) {
|
||||
log_err(_("Missing --token option specifying token for removal.\n"));
|
||||
return -EINVAL;
|
||||
}
|
||||
add = 0;
|
||||
} else {
|
||||
log_err(_("Invalid token operation %s.\n"), action_argv[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((r = crypt_init(&cd, uuid_or_device(opt_header_device ?: action_argv[1]))))
|
||||
return r;
|
||||
|
||||
if ((r = crypt_load(cd, CRYPT_LUKS2, NULL))) {
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = add ? _token_add(cd) : crypt_token_json_set(cd, opt_token, NULL);
|
||||
if (r < 0) {
|
||||
if (add)
|
||||
log_err(_("Failed to add keyring token %d.\n"), opt_token);
|
||||
else
|
||||
log_err(_("Failed to remove token %d.\n"), opt_token);
|
||||
}
|
||||
|
||||
crypt_free(cd);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static struct action_type {
|
||||
const char *type;
|
||||
int (*handler)(void);
|
||||
@@ -1533,6 +1883,8 @@ static struct action_type {
|
||||
{ "benchmark", action_benchmark, 0, 0, N_("[--cipher <cipher>]"), N_("benchmark cipher") },
|
||||
{ "repair", action_luksRepair, 1, 1, N_("<device>"), N_("try to repair on-disk metadata") },
|
||||
{ "erase", action_luksErase , 1, 1, N_("<device>"), N_("erase all keyslots (remove encryption key)") },
|
||||
{ "convert", action_luksConvert, 1, 1, N_("<device>"), N_("convert LUKS from/to LUKS2 format") },
|
||||
{ "config", action_luksConfig, 1, 1, N_("<device>"), N_("set permanent configuration options for LUKS2") },
|
||||
{ "luksFormat", action_luksFormat, 1, 1, N_("<device> [<new key file>]"), N_("formats a LUKS device") },
|
||||
{ "luksAddKey", action_luksAddKey, 1, 1, N_("<device> [<new key file>]"), N_("add key to LUKS device") },
|
||||
{ "luksRemoveKey",action_luksRemoveKey,1, 1, N_("<device> [<key file>]"), N_("removes supplied key or key file from LUKS device") },
|
||||
@@ -1546,6 +1898,7 @@ static struct action_type {
|
||||
{ "luksResume", action_luksResume, 1, 1, N_("<device>"), N_("Resume suspended LUKS device.") },
|
||||
{ "luksHeaderBackup", action_luksBackup,1,1, N_("<device>"), N_("Backup LUKS device header and keyslots") },
|
||||
{ "luksHeaderRestore",action_luksRestore,1,1,N_("<device>"), N_("Restore LUKS device header and keyslots") },
|
||||
{ "token", action_token, 2, 0, N_("<add|remove> <device>"), N_("Add or remove keyring token") },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -1582,9 +1935,12 @@ static void help(poptContext popt_context,
|
||||
log_std(_("\nDefault compiled-in key and passphrase parameters:\n"
|
||||
"\tMaximum keyfile size: %dkB, "
|
||||
"Maximum interactive passphrase length %d (characters)\n"
|
||||
"Default PBKDF2 iteration time for LUKS: %d (ms)\n"),
|
||||
"Default PBKDF2 iteration time for LUKS: %d (ms)\n"
|
||||
"Default PBKDF for LUKS2: %s\n"
|
||||
"\tIteration time: %d, Memory required: %dkB, Parallel threads: %d\n"),
|
||||
DEFAULT_KEYFILE_SIZE_MAXKB, DEFAULT_PASSPHRASE_SIZE_MAX,
|
||||
DEFAULT_LUKS1_ITER_TIME);
|
||||
DEFAULT_LUKS1_ITER_TIME, DEFAULT_LUKS2_PBKDF, DEFAULT_LUKS2_ITER_TIME,
|
||||
DEFAULT_LUKS2_MEMORY_KB, DEFAULT_LUKS2_PARALLEL_THREADS);
|
||||
|
||||
log_std(_("\nDefault compiled-in device cipher parameters:\n"
|
||||
"\tloop-AES: %s, Key %d bits\n"
|
||||
@@ -1689,6 +2045,19 @@ int main(int argc, const char **argv)
|
||||
{ "pbkdf-memory", '\0', POPT_ARG_LONG, &opt_pbkdf_memory, 0, N_("PBKDF memory cost limit"), N_("kilobytes") },
|
||||
{ "pbkdf-parallel", '\0', POPT_ARG_LONG, &opt_pbkdf_parallel, 0, N_("PBKDF parallel cost "), N_("threads") },
|
||||
{ "pbkdf-force-iterations",'\0',POPT_ARG_LONG, &opt_pbkdf_iterations, 0, N_("PBKDF iterations cost (forced, disables benchmark)"), NULL },
|
||||
{ "priority", '\0', POPT_ARG_STRING, &opt_priority, 0, N_("Keyslot priority (ignore/normal/prefer)"), NULL },
|
||||
{ "disable-locks", '\0', POPT_ARG_NONE, &opt_disable_locks, 0, N_("Disable locking of on-disk metadata"), NULL },
|
||||
{ "disable-keyring", '\0', POPT_ARG_NONE, &opt_disable_keyring, 0, N_("Disable loading volume keys via kernel keyring"), NULL },
|
||||
{ "integrity", 'I', POPT_ARG_STRING, &opt_integrity, 0, N_("Data integrity algorithm (LUKS2 only)"), NULL },
|
||||
{ "integrity-no-journal",'\0',POPT_ARG_NONE, &opt_integrity_nojournal, 0, N_("Disable journal for integrity device."), NULL },
|
||||
{ "integrity-no-wipe", '\0', POPT_ARG_NONE, &opt_integrity_no_wipe, 0, N_("Do not wipe device after format"), NULL },
|
||||
{ "token-only", '\0', POPT_ARG_NONE, &opt_token_only, 0, N_("Do not ask for passphrase if activation by token fails"), NULL },
|
||||
{ "token-id", '\0', POPT_ARG_INT, &opt_token, 0, N_("Token number (default: any)"), NULL },
|
||||
{ "key-description", '\0', POPT_ARG_STRING, &opt_key_description, 0, N_("Key description"), NULL },
|
||||
{ "sector-size", '\0', POPT_ARG_INT, &opt_sector_size, 0, N_("Encryption sector size (default: 512 bytes)"), NULL },
|
||||
{ "persistent", '\0', POPT_ARG_NONE, &opt_persistent, 0, N_("Set activation flags persistent for device."), NULL },
|
||||
{ "label", '\0', POPT_ARG_STRING, &opt_label, 0, N_("Set label for the LUKS2 device."), NULL },
|
||||
{ "subsystem", '\0', POPT_ARG_STRING, &opt_subsystem, 0, N_("Set subsystem label for the LUKS2 device."), NULL },
|
||||
POPT_TABLEEND
|
||||
};
|
||||
poptContext popt_context;
|
||||
@@ -1801,6 +2170,9 @@ int main(int argc, const char **argv)
|
||||
} else if (!strcmp(aname, "luksErase")) {
|
||||
aname = "erase";
|
||||
opt_type = "luks";
|
||||
} else if (!strcmp(aname, "luksConfig")) {
|
||||
aname = "config";
|
||||
opt_type = "luks2";
|
||||
}
|
||||
|
||||
for(action = action_types; action->type; action++)
|
||||
@@ -1831,6 +2203,11 @@ int main(int argc, const char **argv)
|
||||
_("Option --allow-discards is allowed only for open operation.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_persistent && strcmp(aname, "open"))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Option --persistent is allowed only for open operation.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_key_size &&
|
||||
strcmp(aname, "luksFormat") &&
|
||||
strcmp(aname, "open") &&
|
||||
@@ -1840,8 +2217,24 @@ int main(int argc, const char **argv)
|
||||
"To limit read from keyfile use --keyfile-size=(bytes)."),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_integrity && strcmp(aname, "luksFormat"))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Option --integrity is allowed only for luksFormat (LUKS2).\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_integrity_no_wipe && !opt_integrity)
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Option --integrity-no-wipe"
|
||||
" can be used only for format action with integrity extension.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if ((opt_label || opt_subsystem) && strcmp(aname, "luksFormat") && strcmp(aname, "config"))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Options --label and --subsystem are allowed only for luksFormat and config LUKS2 operations.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_test_passphrase && (strcmp(aname, "open") ||
|
||||
(strcmp(opt_type, "luks") && strcmp(opt_type, "tcrypt"))))
|
||||
(strncmp(opt_type, "luks", 4) && strcmp(opt_type, "tcrypt"))))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Option --test-passphrase is allowed only for open of LUKS and TCRYPT devices.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
@@ -1853,8 +2246,7 @@ int main(int argc, const char **argv)
|
||||
|
||||
if (!strcmp(aname, "luksKillSlot") && action_argc > 1)
|
||||
opt_key_slot = atoi(action_argv[1]);
|
||||
if (opt_key_slot != CRYPT_ANY_SLOT &&
|
||||
(opt_key_slot < 0 || opt_key_slot >= crypt_keyslot_max(CRYPT_LUKS1)))
|
||||
if (opt_key_slot != CRYPT_ANY_SLOT && opt_key_slot < 0)
|
||||
usage(popt_context, EXIT_FAILURE, _("Key slot is invalid."),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
@@ -1945,9 +2337,19 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (opt_priority && strcmp(opt_priority, "normal") && strcmp(opt_priority, "prefer") && strcmp(opt_priority, "ignore"))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Option --priority can be only ignore/normal/prefer.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (!strcmp(aname, "config") && opt_priority && opt_key_slot == CRYPT_ANY_SLOT)
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Keyslot specification is required.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &opt_pbkdf))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("PBKDF can be only pbkdf2 or argon2i/argon2id.\n"),
|
||||
_("Password-based key derivation function (PBKDF) can be only pbkdf2 or argon2i/argon2id.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_pbkdf_iterations && opt_iteration_time)
|
||||
@@ -1955,12 +2357,27 @@ int main(int argc, const char **argv)
|
||||
_("PBKDF forced iterations cannot be combined with iteration time option.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if ((opt_sector_size != 512 && strcmp(aname, "luksFormat")) || opt_sector_size < 512
|
||||
|| opt_sector_size > 4096 || (opt_sector_size & (opt_sector_size - 1)))
|
||||
usage(popt_context, EXIT_FAILURE,
|
||||
_("Unsupported encryption sector size.\n"),
|
||||
poptGetInvocationName(popt_context));
|
||||
|
||||
if (opt_debug) {
|
||||
opt_verbose = 1;
|
||||
crypt_set_debug_level(-1);
|
||||
dbg_version_and_cmd(argc, argv);
|
||||
}
|
||||
|
||||
if (opt_disable_locks && crypt_metadata_locking(NULL, 0)) {
|
||||
log_std(_("Cannot disable metadata locking.\n"));
|
||||
poptFreeContext(popt_context);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (opt_disable_keyring)
|
||||
(void) crypt_volume_key_keyring(NULL, 0);
|
||||
|
||||
r = run_action(action);
|
||||
poptFreeContext(popt_context);
|
||||
return r;
|
||||
|
||||
Reference in New Issue
Block a user