/* * Copyright (C) 2014 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . * * Author: Vratislav Podzimek */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_BD_ESCROW #include #include #endif #include "crypto.h" #ifndef CRYPT_LUKS #define CRYPT_LUKS NULL #endif #ifdef __clang__ #define ZERO_INIT {} #else #define ZERO_INIT {0} #endif #define SECTOR_SIZE 512 #define UNUSED __attribute__((unused)) /** * SECTION: crypto * @short_description: plugin for operations with encrypted devices * @title: Crypto * @include: crypto.h * * A plugin for operations with encrypted devices. For now, only * LUKS devices are supported. * * Functions taking a parameter called "device" require the backing device to be * passed. On the other hand functions taking the "luks_device" parameter * require the LUKS device (/dev/mapper/SOMETHING"). * * Sizes are given in bytes unless stated otherwise. */ BDCryptoLUKSPBKDF* bd_crypto_luks_pbkdf_copy (BDCryptoLUKSPBKDF *pbkdf) { if (pbkdf == NULL) return NULL; BDCryptoLUKSPBKDF *new_pbkdf = g_new0 (BDCryptoLUKSPBKDF, 1); new_pbkdf->type = g_strdup (pbkdf->type); new_pbkdf->hash = g_strdup (pbkdf->hash); new_pbkdf->max_memory_kb = pbkdf->max_memory_kb; new_pbkdf->iterations = pbkdf->iterations; new_pbkdf->time_ms = pbkdf->time_ms; new_pbkdf->parallel_threads = pbkdf->parallel_threads; return new_pbkdf; } void bd_crypto_luks_pbkdf_free (BDCryptoLUKSPBKDF *pbkdf) { if (pbkdf == NULL) return; g_free (pbkdf->type); g_free (pbkdf->hash); g_free (pbkdf); } BDCryptoLUKSPBKDF* bd_crypto_luks_pbkdf_new (const gchar *type, const gchar *hash, guint32 max_memory_kb, guint32 iterations, guint32 time_ms, guint32 parallel_threads) { BDCryptoLUKSPBKDF *ret = g_new0 (BDCryptoLUKSPBKDF, 1); ret->type = g_strdup (type); ret->hash = g_strdup (hash); ret->max_memory_kb = max_memory_kb; ret->iterations = iterations; ret->time_ms = time_ms; ret->parallel_threads = parallel_threads; return ret; } BDCryptoLUKSExtra* bd_crypto_luks_extra_copy (BDCryptoLUKSExtra *extra) { if (extra == NULL) return NULL; BDCryptoLUKSExtra *new_extra = g_new0 (BDCryptoLUKSExtra, 1); new_extra->integrity = g_strdup (extra->integrity); new_extra->data_alignment = extra->data_alignment; new_extra->data_device = g_strdup (extra->data_device); new_extra->sector_size = extra->sector_size; new_extra->label = g_strdup (extra->label); new_extra->subsystem = g_strdup (extra->subsystem); new_extra->pbkdf = bd_crypto_luks_pbkdf_copy (extra->pbkdf); return new_extra; } void bd_crypto_luks_extra_free (BDCryptoLUKSExtra *extra) { if (extra == NULL) return; g_free (extra->integrity); g_free (extra->data_device); g_free (extra->label); g_free (extra->subsystem); bd_crypto_luks_pbkdf_free (extra->pbkdf); g_free (extra); } BDCryptoLUKSExtra* bd_crypto_luks_extra_new (guint64 data_alignment, const gchar *data_device, const gchar *integrity, guint64 sector_size, const gchar *label, const gchar *subsystem, BDCryptoLUKSPBKDF *pbkdf) { BDCryptoLUKSExtra *ret = g_new0 (BDCryptoLUKSExtra, 1); ret->integrity = g_strdup (integrity); ret->data_alignment = data_alignment; ret->data_device = g_strdup (data_device); ret->sector_size = sector_size; ret->label = g_strdup (label); ret->subsystem = g_strdup (subsystem); ret->pbkdf = bd_crypto_luks_pbkdf_copy (pbkdf); return ret; } void bd_crypto_luks_info_free (BDCryptoLUKSInfo *info) { if (info == NULL) return; g_free (info->cipher); g_free (info->mode); g_free (info->uuid); g_free (info->backing_device); g_free (info); } BDCryptoLUKSInfo* bd_crypto_luks_info_copy (BDCryptoLUKSInfo *info) { if (info == NULL) return NULL; BDCryptoLUKSInfo *new_info = g_new0 (BDCryptoLUKSInfo, 1); new_info->version = info->version; new_info->cipher = g_strdup (info->cipher); new_info->mode = g_strdup (info->mode); new_info->uuid = g_strdup (info->uuid); new_info->backing_device = g_strdup (info->backing_device); new_info->sector_size = info->sector_size; return new_info; } void bd_crypto_integrity_info_free (BDCryptoIntegrityInfo *info) { if (info == NULL) return; g_free (info->algorithm); g_free (info->journal_crypt); g_free (info->journal_integrity); g_free (info); } BDCryptoIntegrityInfo* bd_crypto_integrity_info_copy (BDCryptoIntegrityInfo *info) { if (info == NULL) return NULL; BDCryptoIntegrityInfo *new_info = g_new0 (BDCryptoIntegrityInfo, 1); new_info->algorithm = g_strdup (info->algorithm); new_info->key_size = info->key_size; new_info->sector_size = info->sector_size; new_info->tag_size = info->tag_size; new_info->interleave_sectors = info->interleave_sectors; new_info->journal_size = info->journal_size; new_info->journal_crypt = g_strdup (info->journal_crypt); new_info->journal_integrity = g_strdup (info->journal_integrity); return new_info; } /* "C" locale to get the locale-agnostic error messages */ static locale_t c_locale = (locale_t) 0; /** * bd_crypto_check_deps: * * Returns: whether the plugin's runtime dependencies are satisfied or not * * Function checking plugin's runtime dependencies. * */ gboolean bd_crypto_check_deps (void) { /* nothing to do here */ return TRUE; } static void crypto_log_redirect (gint level, const gchar *msg, void *usrptr __attribute__((unused))) { gchar *message = NULL; switch (level) { case CRYPT_LOG_DEBUG: case CRYPT_LOG_VERBOSE: message = g_strdup_printf ("[cryptsetup] %s", msg); bd_utils_log (LOG_DEBUG, message); g_free (message); break; case CRYPT_LOG_NORMAL: case CRYPT_LOG_ERROR: message = g_strdup_printf ("[cryptsetup] %s", msg); bd_utils_log (LOG_INFO, message); g_free (message); break; default: g_warning ("Unknown cryptsetup log level %d.", level); message = g_strdup_printf ("[cryptsetup] %s", msg); bd_utils_log (LOG_INFO, message); g_free (message); break; } } static void safe_zero (void *data, size_t len) { #ifdef HAVE_EXPLICIT_BZERO explicit_bzero (data, len); #else /* taken from glibc string/explicit_bzero.c */ memset (data, '\0', len); asm volatile ("" ::: "memory"); #endif } /** * bd_crypto_init: * * Initializes the plugin. **This function is called automatically by the * library's initialization functions.** * */ gboolean bd_crypto_init (void) { #ifdef DEBUG crypt_set_debug_level (CRYPT_DEBUG_ALL); #endif c_locale = newlocale (LC_ALL_MASK, "C", c_locale); crypt_set_log_callback (NULL, &crypto_log_redirect, NULL); return TRUE; } /** * bd_crypto_close: * * Cleans up after the plugin. **This function is called automatically by the * library's functions that unload it.** * */ void bd_crypto_close (void) { c_locale = (locale_t) 0; crypt_set_log_callback (NULL, NULL, NULL); crypt_set_debug_level (CRYPT_DEBUG_NONE); } /** * bd_crypto_is_tech_avail: * @tech: the queried tech * @mode: a bit mask of queried modes of operation (#BDCryptoTechMode) for @tech * @error: (out): place to store error (details about why the @tech-@mode combination is not available) * * Returns: whether the @tech-@mode combination is available -- supported by the * plugin implementation and having all the runtime dependencies available */ gboolean bd_crypto_is_tech_avail (BDCryptoTech tech, guint64 mode, GError **error) { guint64 ret = 0; switch (tech) { case BD_CRYPTO_TECH_LUKS: ret = mode & (BD_CRYPTO_TECH_MODE_CREATE|BD_CRYPTO_TECH_MODE_OPEN_CLOSE|BD_CRYPTO_TECH_MODE_QUERY| BD_CRYPTO_TECH_MODE_ADD_KEY|BD_CRYPTO_TECH_MODE_REMOVE_KEY|BD_CRYPTO_TECH_MODE_RESIZE| BD_CRYPTO_TECH_MODE_SUSPEND_RESUME|BD_CRYPTO_TECH_MODE_BACKUP_RESTORE); if (ret != mode) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Only 'create', 'open', 'query', 'add-key', 'remove-key', 'resize', 'suspend-resume', 'backup-restore' supported for LUKS"); return FALSE; } else return TRUE; case BD_CRYPTO_TECH_LUKS2: #ifndef LIBCRYPTSETUP_2 g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "LUKS 2 technology requires libcryptsetup >= 2.0"); return FALSE; #endif ret = mode & (BD_CRYPTO_TECH_MODE_CREATE|BD_CRYPTO_TECH_MODE_OPEN_CLOSE|BD_CRYPTO_TECH_MODE_QUERY| BD_CRYPTO_TECH_MODE_ADD_KEY|BD_CRYPTO_TECH_MODE_REMOVE_KEY|BD_CRYPTO_TECH_MODE_RESIZE| BD_CRYPTO_TECH_MODE_SUSPEND_RESUME|BD_CRYPTO_TECH_MODE_BACKUP_RESTORE); if (ret != mode) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Only 'create', 'open', 'query', 'add-key', 'remove-key', 'resize', 'suspend-resume', 'backup-restore' supported for LUKS 2"); return FALSE; } else return TRUE; case BD_CRYPTO_TECH_TRUECRYPT: ret = mode & BD_CRYPTO_TECH_MODE_OPEN_CLOSE; if (ret != mode) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Only 'open' supported for TrueCrypt"); return FALSE; } else return TRUE; case BD_CRYPTO_TECH_ESCROW: #ifndef WITH_BD_ESCROW g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Escrow technology is not available, libblockdev has been compiled without escrow support."); return FALSE; #endif ret = mode & BD_CRYPTO_TECH_MODE_CREATE; if (ret != mode) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Only 'create' supported for device escrow"); return FALSE; } else return TRUE; case BD_CRYPTO_TECH_INTEGRITY: #ifndef LIBCRYPTSETUP_2 g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Integrity technology requires libcryptsetup >= 2.0"); return FALSE; #endif ret = mode & (BD_CRYPTO_TECH_MODE_QUERY); if (ret != mode) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Only 'query' supported for Integrity"); return FALSE; } else return TRUE; case BD_CRYPTO_TECH_BITLK: #ifndef LIBCRYPTSETUP_BITLK g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "BITLK technology requires libcryptsetup >= 2.3.0"); return FALSE; #endif ret = mode & BD_CRYPTO_TECH_MODE_OPEN_CLOSE; if (ret != mode) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Only 'open' supported for BITLK"); return FALSE; } else return TRUE; default: g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Unknown technology"); return FALSE; } return TRUE; } /** * bd_crypto_error_quark: (skip) */ GQuark bd_crypto_error_quark (void) { return g_quark_from_static_string ("g-bd-crypto-error-quark"); } /** * bd_crypto_generate_backup_passphrase: * @error: (out): place to store error (if any) * * Returns: (transfer full): A newly generated %BD_CRYPTO_BACKUP_PASSPHRASE_LENGTH-long passphrase. * * See %BD_CRYPTO_BACKUP_PASSPHRASE_CHARSET for the definition of the charset used for the passphrase. * * Tech category: always available */ gchar* bd_crypto_generate_backup_passphrase(GError **error __attribute__((unused))) { guint8 i = 0; guint8 offset = 0; guint8 charset_length = strlen (BD_CRYPTO_BACKUP_PASSPHRASE_CHARSET); /* passphrase with groups of 5 characters separated with dashes, plus a null terminator */ gchar *ret = g_new0 (gchar, BD_CRYPTO_BACKUP_PASSPHRASE_LENGTH + (BD_CRYPTO_BACKUP_PASSPHRASE_LENGTH / 5)); for (i=0; i < BD_CRYPTO_BACKUP_PASSPHRASE_LENGTH; i++) { if (i > 0 && (i % 5 == 0)) { /* put a dash between every 5 characters */ ret[i+offset] = '-'; offset++; } ret[i+offset] = BD_CRYPTO_BACKUP_PASSPHRASE_CHARSET[g_random_int_range(0, charset_length)]; } return ret; } /** * bd_crypto_device_is_luks: * @device: the queried device * @error: (out): place to store error (if any) * * Returns: %TRUE if the given @device is a LUKS device or %FALSE if not or * failed to determine (the @error) is populated with the error in such * cases) * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_QUERY */ gboolean bd_crypto_device_is_luks (const gchar *device, GError **error) { blkid_probe probe = NULL; gint fd = 0; gint status = 0; const gchar *value = NULL; guint n_try = 0; probe = blkid_new_probe (); if (!probe) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to create a new probe"); return FALSE; } fd = open (device, O_RDONLY|O_CLOEXEC); if (fd == -1) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to open the device '%s'", device); blkid_free_probe (probe); return FALSE; } /* we may need to try mutliple times with some delays in case the device is busy at the very moment */ for (n_try=5, status=-1; (status != 0) && (n_try > 0); n_try--) { status = blkid_probe_set_device (probe, fd, 0, 0); if (status != 0) g_usleep (100 * 1000); /* microseconds */ } if (status != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to create a probe for the device '%s'", device); blkid_free_probe (probe); close (fd); return FALSE; } blkid_probe_enable_partitions (probe, 1); blkid_probe_set_partitions_flags (probe, BLKID_PARTS_MAGIC); blkid_probe_enable_superblocks (probe, 1); blkid_probe_set_superblocks_flags (probe, BLKID_SUBLKS_USAGE | BLKID_SUBLKS_TYPE | BLKID_SUBLKS_MAGIC | BLKID_SUBLKS_BADCSUM); /* we may need to try mutliple times with some delays in case the device is busy at the very moment */ for (n_try=5, status=-1; !(status == 0 || status == 1) && (n_try > 0); n_try--) { status = blkid_do_safeprobe (probe); if (status < 0) g_usleep (100 * 1000); /* microseconds */ } if (status < 0) { /* -1 or -2 = error during probing*/ g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to probe the device '%s'", device); blkid_free_probe (probe); close (fd); return FALSE; } else if (status == 1) { /* 1 = nothing detected */ blkid_free_probe (probe); close (fd); return FALSE; } status = blkid_probe_lookup_value (probe, "USAGE", &value, NULL); if (status != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to get usage for the device '%s'", device); blkid_free_probe (probe); close (fd); return FALSE; } if (g_strcmp0 (value, "crypto") != 0) { blkid_free_probe (probe); close (fd); return FALSE; } status = blkid_probe_lookup_value (probe, "TYPE", &value, NULL); if (status != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to get filesystem type for the device '%s'", device); blkid_free_probe (probe); close (fd); return FALSE; } if (g_strcmp0 (value, "crypto_LUKS") != 0) { blkid_free_probe (probe); close (fd); return FALSE; } blkid_free_probe (probe); close (fd); return TRUE; } /** * bd_crypto_luks_uuid: * @device: the queried device * @error: (out): place to store error (if any) * * Returns: (transfer full): UUID of the @device or %NULL if failed to determine (@error * is populated with the error in such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_QUERY */ gchar* bd_crypto_luks_uuid (const gchar *device, GError **error) { struct crypt_device *cd = NULL; gint ret_num; gchar *ret; ret_num = crypt_init (&cd, device); if (ret_num != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret_num, c_locale)); return NULL; } ret_num = crypt_load (cd, CRYPT_LUKS, NULL); if (ret_num != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device: %s", strerror_l(-ret_num, c_locale)); crypt_free (cd); return NULL; } ret = g_strdup (crypt_get_uuid (cd)); crypt_free (cd); return ret; } /** * bd_crypto_get_luks_metadata_size: * @device: the queried device * @error: (out): place to store error (if any) * * Returns: luks device metadata size of the @device * or 0 if failed to determine (@error is populated * with the error in such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_QUERY */ guint64 bd_crypto_luks_get_metadata_size (const gchar *device, GError **error) { struct crypt_device *cd = NULL; gint ret_num; guint64 ret; ret_num = crypt_init (&cd, device); if (ret_num != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret_num, c_locale)); return 0; } ret_num = crypt_load (cd, CRYPT_LUKS, NULL); if (ret_num != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device: %s", strerror_l(-ret_num, c_locale)); crypt_free (cd); return 0; } ret = SECTOR_SIZE * crypt_get_data_offset (cd); crypt_free (cd); return ret; } /** * bd_crypto_luks_status: * @luks_device: the queried LUKS device * @error: (out): place to store error (if any) * * Returns: (transfer none): one of "invalid", "inactive", "active" or "busy" or * %NULL if failed to determine (@error is populated with the error in * such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_QUERY */ gchar* bd_crypto_luks_status (const gchar *luks_device, GError **error) { struct crypt_device *cd = NULL; gint ret_num; const gchar *ret = NULL; crypt_status_info status; ret_num = crypt_init_by_name (&cd, luks_device); if (ret_num != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret_num, c_locale)); return NULL; } status = crypt_status (cd, luks_device); switch (status) { case CRYPT_INVALID: ret = "invalid"; break; case CRYPT_INACTIVE: ret = "inactive"; break; case CRYPT_ACTIVE: ret = "active"; break; case CRYPT_BUSY: ret = "busy"; break; default: ret = NULL; g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_STATE, "Unknown device's state"); } crypt_free (cd); /* cast the "const" away because this API requires returning a non-const string, though the caller isn't allowed to modify its contents */ return (gchar *)ret; } #ifdef LIBCRYPTSETUP_2 static struct crypt_pbkdf_type *get_pbkdf_params (BDCryptoLUKSPBKDF *user_pbkdf, GError **error) { const struct crypt_pbkdf_type *default_pbkdf = NULL; struct crypt_pbkdf_type *new_pbkdf = NULL; if (user_pbkdf == NULL) return NULL; /* crypt_get_pbkdf_default returns default pbkdf parameters only based on the luks version -- so for LUKS2 it returns default values for argon2 but we also need to be able to provide default values if user wants pbkdf2 and only specifies type -- we will use the defaults for argon2 and ignore parameters specific to it better API for this should be part of cryptsetup 2.0.4 */ default_pbkdf = crypt_get_pbkdf_default (CRYPT_LUKS2); if (!default_pbkdf) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_FORMAT_FAILED, "Failed to get default values for pbkdf."); return NULL; } new_pbkdf = g_new0 (struct crypt_pbkdf_type, 1); new_pbkdf->flags = default_pbkdf->flags; if (user_pbkdf->type) new_pbkdf->type = user_pbkdf->type; else new_pbkdf->type = default_pbkdf->type; if (user_pbkdf->hash) new_pbkdf->hash = user_pbkdf->hash; else new_pbkdf->hash = default_pbkdf->hash; if (user_pbkdf->time_ms) new_pbkdf->time_ms = user_pbkdf->time_ms; else new_pbkdf->time_ms = default_pbkdf->time_ms; if (user_pbkdf->iterations) { new_pbkdf->iterations = user_pbkdf->iterations; /* iterations set manually -> do not run benchmark */ new_pbkdf->flags = CRYPT_PBKDF_NO_BENCHMARK; } else new_pbkdf->iterations = default_pbkdf->iterations; /* 'max_memory_kb' and 'parallel_threads' are not used in pbkdf2 */ if (g_strcmp0 (user_pbkdf->type, CRYPT_KDF_PBKDF2) == 0) { if (user_pbkdf->max_memory_kb) bd_utils_log (LOG_WARNING, "'max_memory_kb' is not valid option for 'pbkdf2', ignoring."); new_pbkdf->max_memory_kb = 0; new_pbkdf->parallel_threads = 0; } else { if (user_pbkdf->max_memory_kb) new_pbkdf->max_memory_kb = user_pbkdf->max_memory_kb; else new_pbkdf->max_memory_kb = default_pbkdf->max_memory_kb; if (user_pbkdf->parallel_threads) new_pbkdf->parallel_threads = user_pbkdf->parallel_threads; else new_pbkdf->parallel_threads = default_pbkdf->parallel_threads; } return new_pbkdf; } #endif static gboolean luks_format (const gchar *device, const gchar *cipher, guint64 key_size, const guint8 *pass_data, gsize data_size, const gchar *key_file, guint64 min_entropy, BDCryptoLUKSVersion luks_version, BDCryptoLUKSExtra *extra, GError **error) { struct crypt_device *cd = NULL; gint ret; gchar **cipher_specs = NULL; guint32 current_entropy = 0; gint dev_random_fd = -1; gboolean success = FALSE; gchar *key_buffer = NULL; gsize buf_len = 0; guint64 progress_id = 0; gchar *msg = NULL; const gchar* crypt_version = NULL; msg = g_strdup_printf ("Started formatting '%s' as LUKS device", device); progress_id = bd_utils_report_started (msg); g_free (msg); if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS1) crypt_version = CRYPT_LUKS1; #ifdef LIBCRYPTSETUP_2 else if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS2) crypt_version = CRYPT_LUKS2; #endif else { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Unknown or unsupported LUKS version specified"); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } if ((data_size == 0) && !key_file) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "At least one of passphrase and key file have to be specified!"); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } cipher = cipher ? cipher : DEFAULT_LUKS_CIPHER; cipher_specs = g_strsplit (cipher, "-", 2); if (g_strv_length (cipher_specs) != 2) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_SPEC, "Invalid cipher specification: '%s'", cipher); crypt_free (cd); g_strfreev (cipher_specs); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } /* resolve requested/default key_size (should be in bytes) */ key_size = (key_size != 0) ? (key_size / 8) : (DEFAULT_LUKS_KEYSIZE_BITS / 8); /* wait for enough random data entropy (if requested) */ if (min_entropy > 0) { dev_random_fd = open ("/dev/random", O_RDONLY); if (dev_random_fd >= 0) { ioctl (dev_random_fd, RNDGETENTCNT, ¤t_entropy); while (current_entropy < min_entropy) { bd_utils_report_progress (progress_id, 0, "Waiting for enough random data entropy"); sleep (1); ioctl (dev_random_fd, RNDGETENTCNT, ¤t_entropy); } close (dev_random_fd); } else { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_FORMAT_FAILED, "Failed to check random data entropy level"); crypt_free (cd); g_strfreev (cipher_specs); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } } if (extra) { if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS1) { if (extra->integrity || extra->sector_size || extra->label || extra->subsystem || extra->pbkdf) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_PARAMS, "Invalid extra arguments specified. Only `data_alignment`" "and `data_device` are valid for LUKS 1."); crypt_free (cd); g_strfreev (cipher_specs); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } struct crypt_params_luks1 params = ZERO_INIT; params.data_alignment = extra->data_alignment; params.data_device = extra->data_device; ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1], NULL, NULL, key_size, ¶ms); } #ifdef LIBCRYPTSETUP_2 else if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS2) { GError *loc_error = NULL; struct crypt_params_luks2 params = ZERO_INIT; struct crypt_pbkdf_type *pbkdf = get_pbkdf_params (extra->pbkdf, &loc_error); if (pbkdf == NULL && loc_error != NULL) { crypt_free (cd); g_strfreev (cipher_specs); bd_utils_report_finished (progress_id, loc_error->message); g_propagate_prefixed_error (error, loc_error, "Failed to get PBKDF parameters for '%s'.", device); return FALSE; } params.pbkdf = pbkdf; params.integrity = extra->integrity; params.integrity_params = NULL; params.data_alignment = extra->data_alignment; params.data_device = extra->data_device; params.sector_size = extra->sector_size ? extra->sector_size : DEFAULT_LUKS2_SECTOR_SIZE; params.label = extra->label; params.subsystem = extra->subsystem; ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1], NULL, NULL, key_size, ¶ms); g_free (pbkdf); } #endif } else ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1], NULL, NULL, key_size, NULL); g_strfreev (cipher_specs); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_FORMAT_FAILED, "Failed to format device: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } bd_utils_report_progress (progress_id, ((data_size != 0) && key_file) ? 40 : 50, "Format created"); if (data_size != 0) { ret = crypt_keyslot_add_by_volume_key (cd, CRYPT_ANY_SLOT, NULL, 0, (const char*) pass_data, data_size); if (ret < 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ADD_KEY, "Failed to add passphrase: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } bd_utils_report_progress (progress_id, ((data_size != 0) && key_file) ? 70 : 100, "Added key"); } if (key_file) { success = g_file_get_contents (key_file, &key_buffer, &buf_len, error); if (!success) { g_prefix_error (error, "Failed to add key file: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_keyslot_add_by_volume_key (cd, CRYPT_ANY_SLOT, NULL, 0, (const char*) key_buffer, buf_len); safe_zero (key_buffer, buf_len); g_free (key_buffer); if (ret < 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ADD_KEY, "Failed to add key file: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_format: * @device: a device to format as LUKS * @cipher: (allow-none): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default * @key_size: size of the volume key in bits or 0 to use the default * @passphrase: (allow-none): a passphrase for the new LUKS device or %NULL if not requested * @key_file: (allow-none): a key file for the new LUKS device or %NULL if not requested * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS * @error: (out): place to store error (if any) * * Formats the given @device as LUKS according to the other parameters given. If * @min_entropy is specified (greater than 0), the function waits for enough * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE * FOREVER). * * Either @passhphrase or @key_file has to be != %NULL. * * Returns: whether the given @device was successfully formatted as LUKS or not * (the @error) contains the error in such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_CREATE */ gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint64 key_size, const gchar *passphrase, const gchar *key_file, guint64 min_entropy, GError **error) { return luks_format (device, cipher, key_size, (const guint8*) passphrase, passphrase ? strlen(passphrase) : 0, key_file, min_entropy, BD_CRYPTO_LUKS_VERSION_LUKS1, NULL, error); } /** * bd_crypto_luks_format_blob: * @device: a device to format as LUKS * @cipher: (allow-none): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default * @key_size: size of the volume key in bits or 0 to use the default * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) * @data_len: (allow-none): length of the @pass_data buffer * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS * @error: (out): place to store error (if any) * * Formats the given @device as LUKS according to the other parameters given. If * @min_entropy is specified (greater than 0), the function waits for enough * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE * FOREVER). * * Returns: whether the given @device was successfully formatted as LUKS or not * (the @error) contains the error in such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_CREATE */ gboolean bd_crypto_luks_format_blob (const gchar *device, const gchar *cipher, guint64 key_size, const guint8 *pass_data, gsize data_len, guint64 min_entropy, GError **error) { return luks_format (device, cipher, key_size, pass_data, data_len, NULL, min_entropy, BD_CRYPTO_LUKS_VERSION_LUKS1, NULL, error); } /** * bd_crypto_luks_format_luks2: * @device: a device to format as LUKS * @cipher: (allow-none): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default * @key_size: size of the volume key in bits or 0 to use the default * @passphrase: (allow-none): a passphrase for the new LUKS device or %NULL if not requested * @key_file: (allow-none): a key file for the new LUKS device or %NULL if not requested * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS * @luks_version: whether to use LUKS v1 or LUKS v2 * @extra: (allow-none): extra arguments for LUKS format creation * @error: (out): place to store error (if any) * * Formats the given @device as LUKS according to the other parameters given. If * @min_entropy is specified (greater than 0), the function waits for enough * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE * FOREVER). * * Either @passhphrase or @key_file has to be != %NULL. * * Using this function with @luks_version set to %BD_CRYPTO_LUKS_VERSION_LUKS1 and * @extra to %NULL is the same as calling %bd_crypto_luks_format. * * Returns: whether the given @device was successfully formatted as LUKS or not * (the @error) contains the error in such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS2-%BD_CRYPTO_TECH_MODE_CREATE */ gboolean bd_crypto_luks_format_luks2 (const gchar *device, const gchar *cipher, guint64 key_size, const gchar *passphrase, const gchar *key_file, guint64 min_entropy, BDCryptoLUKSVersion luks_version, BDCryptoLUKSExtra *extra,GError **error) { return luks_format (device, cipher, key_size, (const guint8*) passphrase, passphrase ? strlen(passphrase) : 0, key_file, min_entropy, luks_version, extra, error); } /** * bd_crypto_luks_format_luks2_blob: * @device: a device to format as LUKS * @cipher: (allow-none): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default * @key_size: size of the volume key in bits or 0 to use the default * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS * @luks_version: whether to use LUKS v1 or LUKS v2 * @extra: (allow-none): extra arguments for LUKS format creation * @error: (out): place to store error (if any) * * Formats the given @device as LUKS according to the other parameters given. If * @min_entropy is specified (greater than 0), the function waits for enough * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE * FOREVER). * * Using this function with @luks_version set to %BD_CRYPTO_LUKS_VERSION_LUKS1 and * @extra to %NULL is the same as calling %bd_crypto_luks_format_blob. * * Returns: whether the given @device was successfully formatted as LUKS or not * (the @error) contains the error in such cases) * * Tech category: %BD_CRYPTO_TECH_LUKS2-%BD_CRYPTO_TECH_MODE_CREATE */ gboolean bd_crypto_luks_format_luks2_blob (const gchar *device, const gchar *cipher, guint64 key_size, const guint8 *pass_data, gsize data_len, guint64 min_entropy, BDCryptoLUKSVersion luks_version, BDCryptoLUKSExtra *extra, GError **error) { return luks_format (device, cipher, key_size, pass_data, data_len, NULL, min_entropy, luks_version, extra, error); } static gboolean luks_open (const gchar *device, const gchar *name, const guint8 *pass_data, gsize data_len, const gchar *key_file, gboolean read_only, GError **error) { struct crypt_device *cd = NULL; gboolean success = FALSE; gchar *key_buffer = NULL; gsize buf_len = 0; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started opening '%s' LUKS device", device); progress_id = bd_utils_report_started (msg); g_free (msg); if ((data_len == 0) && !key_file) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "No passphrase nor key file specified, cannot open."); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } if (key_file) { success = g_file_get_contents (key_file, &key_buffer, &buf_len, error); if (!success) { g_prefix_error (error, "Failed to add key file: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } } else buf_len = data_len; ret = crypt_activate_by_passphrase (cd, name, CRYPT_ANY_SLOT, key_buffer ? key_buffer : (char*) pass_data, buf_len, read_only ? CRYPT_ACTIVATE_READONLY : 0); if (key_buffer) { safe_zero (key_buffer, buf_len); g_free (key_buffer); } if (ret < 0) { if (ret == -EPERM) g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: Incorrect passphrase."); else g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_open: * @device: the device to open * @name: name for the LUKS device * @passphrase: (allow-none): passphrase to open the @device or %NULL * @key_file: (allow-none): key file path to use for opening the @device or %NULL * @read_only: whether to open as read-only or not (meaning read-write) * @error: (out): place to store error (if any) * * Returns: whether the @device was successfully opened or not * * One of @passphrase, @key_file has to be != %NULL. * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ gboolean bd_crypto_luks_open (const gchar *device, const gchar *name, const gchar *passphrase, const gchar *key_file, gboolean read_only, GError **error) { return luks_open (device, name, (const guint8*) passphrase, passphrase ? strlen (passphrase) : 0, key_file, read_only, error); } /** * bd_crypto_luks_open_blob: * @device: the device to open * @name: name for the LUKS device * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @read_only: whether to open as read-only or not (meaning read-write) * @error: (out): place to store error (if any) * * Returns: whether the @device was successfully opened or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ gboolean bd_crypto_luks_open_blob (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, gboolean read_only, GError **error) { return luks_open (device, name, (const guint8*) pass_data, data_len, NULL, read_only, error); } static gboolean _crypto_close (const gchar *device, const gchar *tech_name, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started closing %s device '%s'", tech_name, device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init_by_name (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_deactivate (cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to deactivate device: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_close: * @luks_device: LUKS device to close * @error: (out): place to store error (if any) * * Returns: whether the given @luks_device was successfully closed or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ gboolean bd_crypto_luks_close (const gchar *luks_device, GError **error) { return _crypto_close (luks_device, "LUKS", error); } /** * bd_crypto_luks_add_key_blob: * @device: device to add new key to * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @npass_data: (array length=ndata_len): a new passphrase for the new LUKS device (may contain arbitrary binary data) * @ndata_len: length of the @npass_data buffer * @error: (out): place to store error (if any) * * Returns: whether the @npass_data was successfully added to @device or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_ADD_KEY */ gboolean bd_crypto_luks_add_key_blob (const gchar *device, const guint8 *pass_data, gsize data_len, const guint8 *npass_data, gsize ndata_len, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started adding key to the LUKS device '%s'", device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_keyslot_add_by_passphrase (cd, CRYPT_ANY_SLOT, (char*) pass_data, data_len, (char*) npass_data, ndata_len); if (ret < 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ADD_KEY, "Failed to add key: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_add_key: * @device: device to add new key to * @pass: (allow-none): passphrase for the @device or %NULL * @key_file: (allow-none): key file for the @device or %NULL * @npass: (allow-none): passphrase to add to @device or %NULL * @nkey_file: (allow-none): key file to add to @device or %NULL * @error: (out): place to store error (if any) * * Returns: whether the @npass or @nkey_file was successfully added to @device * or not * * One of @pass, @key_file has to be != %NULL and the same applies to @npass, * @nkey_file. * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_ADD_KEY */ gboolean bd_crypto_luks_add_key (const gchar *device, const gchar *pass, const gchar *key_file, const gchar *npass, const gchar *nkey_file, GError **error) { gboolean success = FALSE; gchar *key_buf = NULL; gsize buf_len = 0; gchar *nkey_buf = NULL; gsize nbuf_len = 0; if (!pass && !key_file) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "No passphrase nor key file given, cannot add key."); return FALSE; } if (!npass && !nkey_file) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "No new passphrase nor key file given, nothing to add."); return FALSE; } if (key_file) { success = g_file_get_contents (key_file, &key_buf, &buf_len, error); if (!success) { g_prefix_error (error, "Failed to load key from file '%s': ", key_file); return FALSE; } } else buf_len = strlen (pass); if (nkey_file) { success = g_file_get_contents (nkey_file, &nkey_buf, &nbuf_len, error); if (!success) { g_prefix_error (error, "Failed to load key from file '%s': ", nkey_file); return FALSE; } } else nbuf_len = strlen (npass); success = bd_crypto_luks_add_key_blob (device, key_buf ? (const guint8*) key_buf : (const guint8*) pass, buf_len, nkey_buf ? (const guint8*) nkey_buf : (const guint8*) npass, nbuf_len, error); if (key_buf) { safe_zero (key_buf, buf_len); g_free (key_buf); } if (nkey_buf) { safe_zero (nkey_buf, nbuf_len); g_free (nkey_buf); } return success; } /** * bd_crypto_luks_remove_key_blob: * @device: device to add new key to * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) to remove * @data_len: length of the @pass_data buffer * @error: (out): place to store error (if any) * * Returns: whether the key was successfully removed or not * * Either @pass or @key_file has to be != %NULL. * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_REMOVE_KEY */ gboolean bd_crypto_luks_remove_key_blob (const gchar *device, const guint8 *pass_data, gsize data_len, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started removing key from the LUKS device '%s'", device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_activate_by_passphrase (cd, NULL, CRYPT_ANY_SLOT, (char*) pass_data, data_len, 0); if (ret < 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_KEY_SLOT, "Failed to determine key slot: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_keyslot_destroy (cd, ret); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REMOVE_KEY, "Failed to remove key: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_remove_key: * @device: device to add new key to * @pass: (allow-none): passphrase for the @device or %NULL * @key_file: (allow-none): key file for the @device or %NULL * @error: (out): place to store error (if any) * * Returns: whether the key was successfully removed or not * * Either @pass or @key_file has to be != %NULL. * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_REMOVE_KEY */ gboolean bd_crypto_luks_remove_key (const gchar *device, const gchar *pass, const gchar *key_file, GError **error) { gboolean success = FALSE; gchar *key_buf = NULL; gsize buf_len = 0; if (!pass && !key_file) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_REMOVE_KEY, "No passphrase nor key file given, cannot remove key."); return FALSE; } if (key_file) { success = g_file_get_contents (key_file, &key_buf, &buf_len, error); if (!success) { g_prefix_error (error, "Failed to load key from file '%s': ", key_file); return FALSE; } } else buf_len = strlen (pass); success = bd_crypto_luks_remove_key_blob (device, key_buf ? (const guint8*) key_buf : (const guint8*) pass, buf_len, error); if (key_buf) { safe_zero (key_buf, buf_len); g_free (key_buf); } return success; } /** * bd_crypto_luks_change_key_blob: * @device: device to change key of * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @npass_data: (array length=ndata_len): a new passphrase for the new LUKS device (may contain arbitrary binary data) * @ndata_len: length of the @npass_data buffer * @error: (out): place to store error (if any) * * Returns: whether the key was successfully changed or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_ADD_KEY&%BD_CRYPTO_TECH_MODE_REMOVE_KEY */ gboolean bd_crypto_luks_change_key_blob (const gchar *device, const guint8 *pass_data, gsize data_len, const guint8 *npass_data, gsize ndata_len, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started changing key on the LUKS device '%s'", device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_keyslot_change_by_passphrase (cd, CRYPT_ANY_SLOT, CRYPT_ANY_SLOT, (char*) pass_data, data_len, (char*) npass_data, ndata_len); if (ret < 0) { if (ret == -EPERM) g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to change the passphrase: No keyslot with given passphrase found."); else g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ADD_KEY, "Failed to change the passphrase: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_change_key: * @device: device to change key of * @pass: old passphrase * @npass: new passphrase * @error: (out): place to store error (if any) * * Returns: whether the key was successfully changed or not * * No support for changing key files (yet). * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_ADD_KEY&%BD_CRYPTO_TECH_MODE_REMOVE_KEY */ gboolean bd_crypto_luks_change_key (const gchar *device, const gchar *pass, const gchar *npass, GError **error) { return bd_crypto_luks_change_key_blob (device, (guint8*) pass, strlen (pass), (guint8*) npass, strlen (npass), error); } static gboolean luks_resize (const gchar *luks_device, guint64 size, const guint8 *pass_data, gsize data_len, const gchar *key_file, GError **error) { struct crypt_device *cd = NULL; struct crypt_active_device cad; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; gboolean success = FALSE; gchar *key_buffer = NULL; gsize buf_len = 0; msg = g_strdup_printf ("Started resizing LUKS device '%s'", luks_device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init_by_name (&cd, luks_device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_get_active_device (cd, luks_device, &cad); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to get information about '%s': %s", luks_device, strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } if (pass_data || key_file) { if (key_file) { success = g_file_get_contents (key_file, &key_buffer, &buf_len, error); if (!success) { g_prefix_error (error, "Failed to add key file: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } } else buf_len = data_len; #ifdef LIBCRYPTSETUP_2 ret = crypt_activate_by_passphrase (cd, NULL, CRYPT_ANY_SLOT, key_buffer ? key_buffer : (char*) pass_data, buf_len, cad.flags & CRYPT_ACTIVATE_KEYRING_KEY); #else ret = crypt_activate_by_passphrase (cd, NULL, CRYPT_ANY_SLOT, key_buffer ? key_buffer : (char*) pass_data, buf_len, 0); #endif if (key_buffer) { safe_zero (key_buffer, buf_len); g_free (key_buffer); } if (ret < 0) { if (ret == -EPERM) g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: Incorrect passphrase."); else g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } } ret = crypt_resize (cd, luks_device, size); if (ret != 0) { #ifdef LIBCRYPTSETUP_2 if (ret == -EPERM && g_strcmp0 (crypt_get_type (cd), CRYPT_LUKS2) == 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_RESIZE_PERM, "Insufficient persmissions to resize device. You need to specify" " passphrase or keyfile to resize LUKS 2 devices that don't" " have verified key loaded in kernel."); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } #endif g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_RESIZE_FAILED, "Failed to resize device: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_resize: * @luks_device: opened LUKS device to resize * @size: requested size in sectors or 0 to adapt to the backing device * @error: (out): place to store error (if any) * * Returns: whether the @luks_device was successfully resized or not * * You need to specify passphrase when resizing LUKS 2 devices that don't have * verified key loaded in kernel. If you don't specify a passphrase, resize * will fail with %BD_CRYPTO_ERROR_RESIZE_PERM. Use %bd_crypto_luks_resize_luks2 * or %bd_crypto_luks_resize_luks2_blob for these devices. * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_RESIZE */ gboolean bd_crypto_luks_resize (const gchar *luks_device, guint64 size, GError **error) { return luks_resize (luks_device, size, NULL, 0, NULL, error); } /** * bd_crypto_luks_resize_luks2: * @luks_device: opened LUKS device to resize * @passphrase: (allow-none): passphrase to resize the @luks_device or %NULL * @key_file: (allow-none): key file path to use for resizinh the @luks_device or %NULL * @size: requested size in sectors or 0 to adapt to the backing device * @error: (out): place to store error (if any) * * Returns: whether the @luks_device was successfully resized or not * * You need to specify either @passphrase or @keyfile for LUKS 2 devices that * don't have verified key loaded in kernel. * For LUKS 1 devices you can set both @passphrase and @keyfile to %NULL to * achieve the same as calling %bd_crypto_luks_resize. * * Tech category: %BD_CRYPTO_TECH_LUKS2-%BD_CRYPTO_TECH_MODE_RESIZE */ gboolean bd_crypto_luks_resize_luks2 (const gchar *luks_device, guint64 size, const gchar *passphrase, const gchar *key_file, GError **error) { return luks_resize (luks_device, size, (const guint8*) passphrase, passphrase ? strlen (passphrase) : 0, key_file, error); } /** * bd_crypto_luks_resize_luks2_blob: * @luks_device: opened LUKS device to resize * @pass_data: (array length=data_len): a passphrase for the new LUKS device (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @size: requested size in sectors or 0 to adapt to the backing device * @error: (out): place to store error (if any) * * Returns: whether the @luks_device was successfully resized or not * * You need to specify @pass_data for LUKS 2 devices that don't have * verified key loaded in kernel. * * Tech category: %BD_CRYPTO_TECH_LUKS2-%BD_CRYPTO_TECH_MODE_RESIZE */ gboolean bd_crypto_luks_resize_luks2_blob (const gchar *luks_device, guint64 size, const guint8* pass_data, gsize data_len, GError **error) { return luks_resize (luks_device, size, pass_data, data_len, NULL, error); } /** * bd_crypto_luks_suspend: * @luks_device: LUKS device to suspend * @error: (out): place to store error (if any) * * Returns: whether the given @luks_device was successfully suspended or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_SUSPEND_RESUME */ gboolean bd_crypto_luks_suspend (const gchar *luks_device, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started suspending LUKS device '%s'", luks_device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init_by_name (&cd, luks_device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_suspend (cd, luks_device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to suspend device: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } static gboolean luks_resume (const gchar *luks_device, const guint8 *pass_data, gsize data_len, const gchar *key_file, GError **error) { struct crypt_device *cd = NULL; gboolean success = FALSE; gchar *key_buffer = NULL; gsize buf_len = 0; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started resuming '%s' LUKS device", luks_device); progress_id = bd_utils_report_started (msg); g_free (msg); if ((data_len == 0) && !key_file) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "No passphrase nor key file specified, cannot resume."); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_init_by_name (&cd, luks_device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } if (key_file) { success = g_file_get_contents (key_file, &key_buffer, &buf_len, error); if (!success) { g_prefix_error (error, "Failed to add key file: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } } else buf_len = data_len; ret = crypt_resume_by_passphrase (cd, luks_device, CRYPT_ANY_SLOT, key_buffer ? key_buffer : (char*) pass_data, buf_len); if (key_buffer) { safe_zero (key_buffer, buf_len); g_free (key_buffer); } if (ret < 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to resume device: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_resume_blob: * @luks_device: LUKS device to resume * @pass_data: (array length=data_len): a passphrase for the LUKS device (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @error: (out): place to store error (if any) * * Returns: whether the given @luks_device was successfully resumed or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_SUSPEND_RESUME */ gboolean bd_crypto_luks_resume_blob (const gchar *luks_device, const guint8 *pass_data, gsize data_len, GError **error) { return luks_resume (luks_device, pass_data, data_len, NULL, error); } /** * bd_crypto_luks_resume: * @luks_device: LUKS device to resume * @passphrase: (allow-none): passphrase to resume the @device or %NULL * @key_file: (allow-none): key file path to use for resuming the @device or %NULL * @error: (out): place to store error (if any) * * Returns: whether the give @luks_device was successfully resumed or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_SUSPEND_RESUME */ gboolean bd_crypto_luks_resume (const gchar *luks_device, const gchar *passphrase, const gchar *key_file, GError **error) { return luks_resume (luks_device, (guint8*) passphrase, passphrase ? strlen (passphrase) : 0, key_file, error); } /** * bd_crypto_luks_kill_slot: * @device: device to kill slot on * @slot: keyslot to destroy * @error: (out): place to store error (if any) * * Note: This can destroy last remaining keyslot without confirmation making * the LUKS device permanently inaccessible. * * Returns: whether the given @slot was successfully destroyed or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_REMOVE_KEY */ gboolean bd_crypto_luks_kill_slot (const gchar *device, gint slot, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started killing slot %d on LUKS device '%s'", slot, device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_keyslot_destroy (cd, slot); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to destroy keyslot: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_header_backup: * @device: device to backup the LUKS header * @backup_file: file to save the header backup to * @error: (out): place to store error (if any) * * Returns: whether the given backup of @device was successfully written to * @backup_file or not * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_BACKUP_RESTORE */ gboolean bd_crypto_luks_header_backup (const gchar *device, const gchar *backup_file, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started header backup of LUKS device '%s'", device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_LUKS, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_header_backup (cd, NULL, backup_file); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to backup LUKS header: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_header_restore: * @device: device to restore the LUKS header to * @backup_file: existing file with a LUKS header backup * @error: (out): place to store error (if any) * * Returns: whether the given @device LUKS header was successfully restored * from @backup_file * * * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_BACKUP_RESTORE */ gboolean bd_crypto_luks_header_restore (const gchar *device, const gchar *backup_file, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started LUKS header restore on device '%s'", device); progress_id = bd_utils_report_started (msg); g_free (msg); ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_header_restore (cd, NULL, backup_file); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to restore LUKS header: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_luks_info: * @luks_device: a device to get information about * @error: (out): place to store error (if any) * * Returns: information about the @luks_device or %NULL in case of error * * Tech category: %BD_CRYPTO_TECH_LUKS%BD_CRYPTO_TECH_MODE_QUERY */ BDCryptoLUKSInfo* bd_crypto_luks_info (const gchar *luks_device, GError **error) { struct crypt_device *cd = NULL; BDCryptoLUKSInfo *info = NULL; const gchar *version = NULL; gint ret; ret = crypt_init_by_name (&cd, luks_device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); return NULL; } info = g_new0 (BDCryptoLUKSInfo, 1); version = crypt_get_type (cd); if (g_strcmp0 (version, CRYPT_LUKS1) == 0) info->version = BD_CRYPTO_LUKS_VERSION_LUKS1; #ifdef LIBCRYPTSETUP_2 else if (g_strcmp0 (version, CRYPT_LUKS2) == 0) info->version = BD_CRYPTO_LUKS_VERSION_LUKS2; #endif else { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Unknown or unsupported LUKS version"); bd_crypto_luks_info_free (info); return NULL; } info->cipher = g_strdup (crypt_get_cipher (cd)); info->mode = g_strdup (crypt_get_cipher_mode (cd)); info->uuid = g_strdup (crypt_get_uuid (cd)); info->backing_device = g_strdup (crypt_get_device_name (cd)); #ifdef LIBCRYPTSETUP_2 info->sector_size = crypt_get_sector_size (cd); #else info->sector_size = 0; #endif crypt_free (cd); return info; } /** * bd_crypto_integrity_info: * @device: a device to get information about * @error: (out): place to store error (if any) * * Returns: information about the @device or %NULL in case of error * * Tech category: %BD_CRYPTO_TECH_INTEGRITY%BD_CRYPTO_TECH_MODE_QUERY */ #ifndef LIBCRYPTSETUP_2 BDCryptoIntegrityInfo* bd_crypto_integrity_info (const gchar *device __attribute__((unused)), GError **error) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Integrity technology requires libcryptsetup >= 2.0"); return NULL; } #else BDCryptoIntegrityInfo* bd_crypto_integrity_info (const gchar *device, GError **error) { struct crypt_device *cd = NULL; struct crypt_params_integrity ip = ZERO_INIT; BDCryptoIntegrityInfo *info = NULL; gint ret; ret = crypt_init_by_name (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); return NULL; } ret = crypt_get_integrity_info (cd, &ip); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to get information about device: %s", strerror_l (-ret, c_locale)); crypt_free (cd); return NULL; } info = g_new0 (BDCryptoIntegrityInfo, 1); info->algorithm = g_strdup (ip.integrity); info->key_size = ip.integrity_key_size; info->sector_size = ip.sector_size; info->tag_size = ip.tag_size; info->interleave_sectors = ip.interleave_sectors; info->journal_size = ip.journal_size; info->journal_crypt = g_strdup (ip.journal_crypt); info->journal_integrity = g_strdup (ip.journal_integrity); crypt_free (cd); return info; } #endif /** * bd_crypto_device_seems_encrypted: * @device: the queried device * @error: (out): place to store error (if any) * * Determines whether a block device seems to be encrypted. * * TCRYPT volumes are not easily identifiable, because they have no * cleartext header, but are completely encrypted. This function is * used to determine whether a block device is a candidate for being * TCRYPT encrypted. * * To achieve this, we calculate the chi square value of the first * 512 Bytes and treat devices with a chi square value between 136 * and 426 as candidates for being encrypted. * For the reasoning, see: https://tails.boum.org/blueprint/veracrypt/#detection * * Returns: %TRUE if the given @device seems to be encrypted or %FALSE if not or * failed to determine (the @error) is populated with the error in such * cases) * * Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_QUERY */ gboolean bd_crypto_device_seems_encrypted (const gchar *device, GError **error) { gint fd = -1; guchar buf[BD_CRYPTO_CHI_SQUARE_BYTES_TO_CHECK]; guint symbols[256] = {0}; gfloat chi_square = 0.0; gfloat e = (gfloat) sizeof(buf) / (gfloat) 256.0; guint i; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started determining if device '%s' seems to be encrypted", device); progress_id = bd_utils_report_started (msg); g_free (msg); fd = open (device, O_RDONLY); if (fd == -1) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to open device"); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } if (read (fd, buf, sizeof(buf)) != sizeof(buf)) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to read device"); bd_utils_report_finished (progress_id, (*error)->message); close(fd); return FALSE; } close(fd); /* Calculate Chi Square */ for (i = 0; i < sizeof(buf); i++) /* This is safe because the max value of buf[i] is < sizeof(symbols). */ symbols[buf[i]]++; for (i = 0; i < 256; i++) chi_square += (symbols[i] - e) * (symbols[i] - e); chi_square /= e; bd_utils_report_finished (progress_id, "Completed"); return BD_CRYPTO_CHI_SQUARE_LOWER_LIMIT < chi_square && chi_square < BD_CRYPTO_CHI_SQUARE_UPPER_LIMIT; } /** * bd_crypto_tc_open: * @device: the device to open * @name: name for the TrueCrypt/VeraCrypt device * @pass_data: (array length=data_len): a passphrase for the TrueCrypt/VeraCrypt volume (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @read_only: whether to open as read-only or not (meaning read-write) * @error: (out): place to store error (if any) * * Returns: whether the @device was successfully opened or not * * Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ gboolean bd_crypto_tc_open (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, gboolean read_only, GError **error) { return bd_crypto_tc_open_full (device, name, pass_data, data_len, NULL, FALSE, FALSE, FALSE, 0, read_only, error); } /** * bd_crypto_tc_open_full: * @device: the device to open * @name: name for the TrueCrypt/VeraCrypt device * @pass_data: (array length=data_len): a passphrase for the TrueCrypt/VeraCrypt volume (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @read_only: whether to open as read-only or not (meaning read-write) * @keyfiles: (allow-none) (array zero-terminated=1): paths to the keyfiles for the TrueCrypt/VeraCrypt volume * @hidden: whether a hidden volume inside the volume should be opened * @system: whether to try opening as an encrypted system (with boot loader) * @veracrypt: whether to try VeraCrypt modes (TrueCrypt modes are tried anyway) * @veracrypt_pim: VeraCrypt PIM value (only used if @veracrypt is %TRUE; only supported if compiled against libcryptsetup >= 2.0) * @error: (out): place to store error (if any) * * Returns: whether the @device was successfully opened or not * * Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ gboolean bd_crypto_tc_open_full (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, const gchar **keyfiles, gboolean hidden, gboolean system, gboolean veracrypt, guint32 veracrypt_pim, gboolean read_only, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; struct crypt_params_tcrypt params = ZERO_INIT; gsize keyfiles_count = 0; guint i; msg = g_strdup_printf ("Started opening '%s' TrueCrypt/VeraCrypt device", device); progress_id = bd_utils_report_started (msg); g_free (msg); if (keyfiles) { for (i=0; *(keyfiles + i); i++); keyfiles_count = i; } if ((data_len == 0) && (keyfiles_count == 0)) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "No passphrase nor key file specified, cannot open."); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l(-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } params.passphrase = (const char*) pass_data; params.passphrase_size = data_len; params.keyfiles = keyfiles; params.keyfiles_count = keyfiles_count; if (veracrypt) params.flags |= CRYPT_TCRYPT_VERA_MODES; if (hidden) params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER; if (system) params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER; #ifndef LIBCRYPTSETUP_2 if (veracrypt && veracrypt_pim != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Compiled against a version of libcryptsetup that does not support the VeraCrypt PIM setting."); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } #else if (veracrypt && veracrypt_pim != 0) params.veracrypt_pim = veracrypt_pim; #endif ret = crypt_load (cd, CRYPT_TCRYPT, ¶ms); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_activate_by_volume_key (cd, name, NULL, 0, read_only ? CRYPT_ACTIVATE_READONLY : 0); if (ret < 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: %s", strerror_l(-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; } /** * bd_crypto_tc_close: * @tc_device: TrueCrypt/VeraCrypt device to close * @error: (out): place to store error (if any) * * Returns: whether the given @tc_device was successfully closed or not * * Tech category: %BD_CRYPTO_TECH_TRUECRYPT-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ gboolean bd_crypto_tc_close (const gchar *tc_device, GError **error) { return _crypto_close (tc_device, "TrueCrypt/VeraCrypt", error); } #ifdef WITH_BD_ESCROW static gchar *always_fail_cb (gpointer data __attribute__((unused)), const gchar *prompt __attribute__((unused)), int echo __attribute__((unused))) { return NULL; } static gchar *give_passphrase_cb (gpointer data, const gchar *prompt __attribute__((unused)), unsigned failed_attempts) { if (failed_attempts == 0) /* Return a copy of the passphrase that will be freed by volume_key */ return g_strdup (data); return NULL; } static void free_passphrase_cb (gpointer data) { g_free (data); } /** * replace_char: * * Replaces all occurrences of @orig in @str with @new (in place). */ static gchar *replace_char (gchar *str, gchar orig, gchar new) { gchar *pos = str; if (!str) return str; for (pos=str; *pos; pos++) *pos = *pos == orig ? new : *pos; return str; } static gboolean write_escrow_data_file (struct libvk_volume *volume, struct libvk_ui *ui, enum libvk_secret secret_type, const gchar *out_path, CERTCertificate *cert, GError **error) { gpointer packet_data = NULL; gsize packet_data_size = 0; GIOChannel *out_file = NULL; GIOStatus status = G_IO_STATUS_ERROR; gsize bytes_written = 0; GError *tmp_error = NULL; packet_data = libvk_volume_create_packet_asymmetric_with_format (volume, &packet_data_size, secret_type, cert, ui, LIBVK_PACKET_FORMAT_ASYMMETRIC_WRAP_SECRET_ONLY, error); if (!packet_data) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_ESCROW_FAILED, "Failed to get escrow data"); libvk_volume_free (volume); return FALSE; } out_file = g_io_channel_new_file (out_path, "w", error); if (!out_file) { /* error is already populated */ g_free (packet_data); return FALSE; } status = g_io_channel_set_encoding (out_file, NULL, error); if (status != G_IO_STATUS_NORMAL) { g_free(packet_data); /* try to shutdown the channel, but if it fails, we cannot do anything about it here */ g_io_channel_shutdown (out_file, TRUE, &tmp_error); /* error is already populated */ g_io_channel_unref (out_file); return FALSE; } status = g_io_channel_write_chars (out_file, (const gchar *) packet_data, (gssize) packet_data_size, &bytes_written, error); g_free (packet_data); if (status != G_IO_STATUS_NORMAL) { /* try to shutdown the channel, but if it fails, we cannot do anything about it here */ g_io_channel_shutdown (out_file, TRUE, &tmp_error); /* error is already populated */ g_io_channel_unref (out_file); return FALSE; } if (g_io_channel_shutdown (out_file, TRUE, error) != G_IO_STATUS_NORMAL) { /* error is already populated */ g_io_channel_unref (out_file); return FALSE; } g_io_channel_unref (out_file); return TRUE; } #endif // WITH_BD_ESCROW /** * bd_crypto_escrow_device: * @device: path of the device to create escrow data for * @passphrase: passphrase used for the device * @cert_data: (array zero-terminated=1) (element-type gchar): certificate data to use for escrow * @directory: directory to put escrow data into * @backup_passphrase: (allow-none): backup passphrase for the device or %NULL * @error: (out): place to store error (if any) * * Returns: whether the escrow data was successfully created for @device or not * * Tech category: %BD_CRYPTO_TECH_ESCROW-%BD_CRYPTO_TECH_MODE_CREATE */ #ifndef WITH_BD_ESCROW gboolean bd_crypto_escrow_device (const gchar *device UNUSED, const gchar *passphrase UNUSED, const gchar *cert_data UNUSED, const gchar *directory UNUSED, const gchar *backup_passphrase UNUSED, GError **error) { /* this will return FALSE and set error, because escrow technology is not available */ return bd_crypto_is_tech_avail (BD_CRYPTO_TECH_ESCROW, BD_CRYPTO_TECH_MODE_CREATE, error); } #else gboolean bd_crypto_escrow_device (const gchar *device, const gchar *passphrase, const gchar *cert_data, const gchar *directory, const gchar *backup_passphrase, GError **error) { struct libvk_volume *volume = NULL; struct libvk_ui *ui = NULL; gchar *label = NULL; gchar *uuid = NULL; CERTCertificate *cert = NULL; gchar *volume_ident = NULL; gchar *out_path = NULL; gboolean ret = FALSE; gchar *passphrase_copy = NULL; gchar *cert_data_copy = NULL; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started creating escrow data for the LUKS device '%s'", device); progress_id = bd_utils_report_started (msg); g_free (msg); if (!NSS_IsInitialized()) if (NSS_NoDB_Init(NULL) != SECSuccess) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NSS_INIT_FAILED, "Failed to initialize NSS"); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } volume = libvk_volume_open (device, error); if (!volume) { /* error is already populated */ bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ui = libvk_ui_new (); /* not supposed to be called -> always fail */ libvk_ui_set_generic_cb (ui, always_fail_cb, NULL, NULL); /* Create a copy of the passphrase to be used by the passphrase callback. * The passphrase will be freed by volume_key via the free callback. */ passphrase_copy = g_strdup (passphrase); libvk_ui_set_passphrase_cb (ui, give_passphrase_cb, passphrase_copy, free_passphrase_cb); if (libvk_volume_get_secret (volume, LIBVK_SECRET_DEFAULT, ui, error) != 0) { /* error is already populated */ libvk_volume_free (volume); libvk_ui_free (ui); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } cert_data_copy = g_strdup(cert_data); cert = CERT_DecodeCertFromPackage (cert_data_copy, strlen(cert_data_copy)); if (!cert) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_CERT_DECODE, "Failed to decode the certificate data"); libvk_volume_free (volume); libvk_ui_free (ui); g_free(cert_data_copy); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } label = libvk_volume_get_label (volume); replace_char (label, '/', '_'); uuid = libvk_volume_get_uuid (volume); replace_char (uuid, '/', '_'); if (label && uuid) { volume_ident = g_strdup_printf ("%s-%s", label, uuid); g_free (label); g_free (uuid); } else if (uuid) volume_ident = uuid; else volume_ident = g_strdup ("_unknown"); out_path = g_strdup_printf ("%s/%s-escrow", directory, volume_ident); ret = write_escrow_data_file (volume, ui, LIBVK_SECRET_DEFAULT, out_path, cert, error); g_free (out_path); if (!ret) { CERT_DestroyCertificate (cert); libvk_volume_free (volume); libvk_ui_free (ui); g_free (volume_ident); g_free(cert_data_copy); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } if (backup_passphrase) { if (libvk_volume_add_secret (volume, LIBVK_SECRET_PASSPHRASE, backup_passphrase, strlen (backup_passphrase), error) != 0) { /* error is already populated */ CERT_DestroyCertificate (cert); libvk_volume_free (volume); libvk_ui_free (ui); g_free (volume_ident); g_free(cert_data_copy); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } out_path = g_strdup_printf ("%s/%s-escrow-backup-passphrase", directory, volume_ident); ret = write_escrow_data_file (volume, ui, LIBVK_SECRET_PASSPHRASE, out_path, cert, error); g_free (out_path); } CERT_DestroyCertificate (cert); libvk_volume_free (volume); libvk_ui_free (ui); g_free (volume_ident); g_free(cert_data_copy); bd_utils_report_finished (progress_id, "Completed"); return ret; } #endif // WITH_BD_ESCROW /** * bd_crypto_bitlk_open: * @device: the device to open * @name: name for the BITLK device * @pass_data: (array length=data_len): a passphrase for the BITLK volume (may contain arbitrary binary data) * @data_len: length of the @pass_data buffer * @read_only: whether to open as read-only or not (meaning read-write) * @error: (out): place to store error (if any) * * Returns: whether the @device was successfully opened or not * * Tech category: %BD_CRYPTO_TECH_BITLK-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ #ifndef LIBCRYPTSETUP_BITLK gboolean bd_crypto_bitlk_open (const gchar *device UNUSED, const gchar *name UNUSED, const guint8* pass_data UNUSED, gsize data_len UNUSED, gboolean read_only UNUSED, GError **error) { /* this will return FALSE and set error, because BITLK technology is not available */ return bd_crypto_is_tech_avail (BD_CRYPTO_TECH_BITLK, BD_CRYPTO_TECH_MODE_OPEN_CLOSE, error); #else gboolean bd_crypto_bitlk_open (const gchar *device, const gchar *name, const guint8* pass_data, gsize data_len, gboolean read_only, GError **error) { struct crypt_device *cd = NULL; gint ret = 0; guint64 progress_id = 0; gchar *msg = NULL; msg = g_strdup_printf ("Started opening '%s' BITLK device", device); progress_id = bd_utils_report_started (msg); g_free (msg); if (data_len == 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_NO_KEY, "No passphrase specified, cannot open."); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_init (&cd, device); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to initialize device: %s", strerror_l (-ret, c_locale)); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_load (cd, CRYPT_BITLK, NULL); if (ret != 0) { g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to load device's parameters: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } ret = crypt_activate_by_passphrase (cd, name, CRYPT_ANY_SLOT, (char*) pass_data, data_len, read_only ? CRYPT_ACTIVATE_READONLY : 0); if (ret < 0) { if (ret == -EPERM) g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: Incorrect passphrase."); else g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE, "Failed to activate device: %s", strerror_l (-ret, c_locale)); crypt_free (cd); bd_utils_report_finished (progress_id, (*error)->message); return FALSE; } crypt_free (cd); bd_utils_report_finished (progress_id, "Completed"); return TRUE; #endif } /** * bd_crypto_bitlk_close: * @bitlk_device: BITLK device to close * @error: (out): place to store error (if any) * * Returns: whether the given @bitlk_device was successfully closed or not * * Tech category: %BD_CRYPTO_TECH_BITLK-%BD_CRYPTO_TECH_MODE_OPEN_CLOSE */ #ifndef LIBCRYPTSETUP_BITLK gboolean bd_crypto_bitlk_close (const gchar *bitlk_device UNUSED, GError **error) { /* this will return FALSE and set error, because BITLK technology is not available */ return bd_crypto_is_tech_avail (BD_CRYPTO_TECH_BITLK, BD_CRYPTO_TECH_MODE_OPEN_CLOSE, error); #else gboolean bd_crypto_bitlk_close (const gchar *bitlk_device, GError **error) { return _crypto_close (bitlk_device, "BITLK", error); #endif }