dissect: add support for encrypted images
This adds support to the image dissector to deal with encrypted images (only LUKS). Given that we now have a neatly isolated image dissector codebase, let's add a new feature to it: support for automatically dealing with encrypted images. This is then exposed in systemd-dissect and nspawn. It's pretty basic: only support for passphrase-based encryption. In order to ensure that "systemd-dissect --mount" results in mount points whose backing LUKS DM devices are cleaned up automatically we use the DM_DEV_REMOVE ioctl() directly on the device (in DM_DEFERRED_REMOVE mode). libgcryptsetup at the moment doesn't provide a proper API for this. Thankfully, the ioctl() API is pretty easy to use.
This commit is contained in:
parent
cf139e6025
commit
18b5886e56
10
Makefile.am
10
Makefile.am
|
@ -1086,7 +1086,8 @@ libshared_la_CFLAGS = \
|
|||
$(ACL_CFLAGS) \
|
||||
$(LIBIDN_CFLAGS) \
|
||||
$(SECCOMP_CFLAGS) \
|
||||
$(BLKID_CFLAGS)
|
||||
$(BLKID_CFLAGS) \
|
||||
$(LIBCRYPTSETUP_CFLAGS)
|
||||
|
||||
libshared_la_LIBADD = \
|
||||
libsystemd-internal.la \
|
||||
|
@ -1096,7 +1097,8 @@ libshared_la_LIBADD = \
|
|||
$(ACL_LIBS) \
|
||||
$(LIBIDN_LIBS) \
|
||||
$(SECCOMP_LIBS) \
|
||||
$(BLKID_LIBS)
|
||||
$(BLKID_LIBS) \
|
||||
$(LIBCRYPTSETUP_LIBS)
|
||||
|
||||
rootlibexec_LTLIBRARIES += \
|
||||
libsystemd-shared.la
|
||||
|
@ -1119,6 +1121,7 @@ libsystemd_shared_la_CFLAGS = \
|
|||
$(LIBIDN_CFLAGS) \
|
||||
$(SECCOMP_CFLAGS) \
|
||||
$(BLKID_CFLAGS) \
|
||||
$(LIBCRYPTSETUP_CFLAGS) \
|
||||
-fvisibility=default
|
||||
|
||||
# We can't use libshared_la_LIBADD here because it would
|
||||
|
@ -1131,7 +1134,8 @@ libsystemd_shared_la_LIBADD = \
|
|||
$(ACL_LIBS) \
|
||||
$(LIBIDN_LIBS) \
|
||||
$(SECCOMP_LIBS) \
|
||||
$(BLKID_LIBS)
|
||||
$(BLKID_LIBS) \
|
||||
$(LIBCRYPTSETUP_LIBS)
|
||||
|
||||
libsystemd_shared_la_LDFLAGS = \
|
||||
$(AM_LDFLAGS) \
|
||||
|
|
|
@ -34,7 +34,7 @@ static enum {
|
|||
} arg_action = ACTION_DISSECT;
|
||||
static const char *arg_image = NULL;
|
||||
static const char *arg_path = NULL;
|
||||
static bool arg_read_only = false;
|
||||
static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP;
|
||||
|
||||
static void help(void) {
|
||||
printf("%s [OPTIONS...] IMAGE\n"
|
||||
|
@ -43,7 +43,8 @@ static void help(void) {
|
|||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
" -m --mount Mount the image to the specified directory\n"
|
||||
" -r --read-only Mount read-only\n",
|
||||
" -r --read-only Mount read-only\n"
|
||||
" --discard=MODE Choose 'discard' mode (disabled, loop, all, crypto)\n",
|
||||
program_invocation_short_name,
|
||||
program_invocation_short_name);
|
||||
}
|
||||
|
@ -52,6 +53,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_DISCARD,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
|
@ -59,6 +61,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "mount", no_argument, NULL, 'm' },
|
||||
{ "read-only", no_argument, NULL, 'r' },
|
||||
{ "discard", required_argument, NULL, ARG_DISCARD },
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -83,7 +86,23 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
break;
|
||||
|
||||
case 'r':
|
||||
arg_read_only = true;
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
||||
case ARG_DISCARD:
|
||||
if (streq(optarg, "disabled"))
|
||||
arg_flags &= ~(DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_DISCARD|DISSECT_IMAGE_DISCARD_ON_CRYPTO);
|
||||
else if (streq(optarg, "loop"))
|
||||
arg_flags = (arg_flags & ~(DISSECT_IMAGE_DISCARD|DISSECT_IMAGE_DISCARD_ON_CRYPTO)) | DISSECT_IMAGE_DISCARD_ON_LOOP;
|
||||
else if (streq(optarg, "all"))
|
||||
arg_flags = (arg_flags & ~(DISSECT_IMAGE_DISCARD_ON_CRYPTO)) | DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD;
|
||||
else if (streq(optarg, "crypt"))
|
||||
arg_flags |= DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD | DISSECT_IMAGE_DISCARD_ON_CRYPTO;
|
||||
else {
|
||||
log_error("Unknown --discard= parameter: %s", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
|
@ -104,7 +123,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_read_only = true;
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
break;
|
||||
|
||||
case ACTION_MOUNT:
|
||||
|
@ -126,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
|
||||
int r;
|
||||
|
||||
|
@ -136,7 +156,7 @@ int main(int argc, char *argv[]) {
|
|||
if (r <= 0)
|
||||
goto finish;
|
||||
|
||||
r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &d);
|
||||
r = loop_device_make_by_path(arg_image, (arg_flags & DISSECT_IMAGE_READ_ONLY) ? O_RDONLY : O_RDWR, &d);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set up loopback device: %m");
|
||||
goto finish;
|
||||
|
@ -186,14 +206,24 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
case ACTION_MOUNT:
|
||||
r = dissected_image_mount(m, arg_path,
|
||||
(arg_read_only ? DISSECTED_IMAGE_READ_ONLY : 0) |
|
||||
DISSECTED_IMAGE_DISCARD_ON_LOOP);
|
||||
r = dissected_image_decrypt_interactively(m, NULL, arg_flags, &di);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
r = dissected_image_mount(m, arg_path, arg_flags);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to mount image: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (di) {
|
||||
r = decrypted_image_relinquish(di);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to relinquish DM devices: %m");
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
loop_device_relinquish(d);
|
||||
break;
|
||||
|
||||
|
|
|
@ -358,7 +358,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err
|
|||
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
r = dissected_image_mount(m, t, DISSECTED_IMAGE_READ_ONLY);
|
||||
r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
|
|
|
@ -2365,7 +2365,7 @@ static int outer_child(
|
|||
return r;
|
||||
|
||||
if (dissected_image) {
|
||||
r = dissected_image_mount(dissected_image, directory, DISSECTED_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECTED_IMAGE_READ_ONLY : 0));
|
||||
r = dissected_image_mount(dissected_image, directory, DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : 0));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -3410,8 +3410,9 @@ int main(int argc, char *argv[]) {
|
|||
_cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
|
||||
bool interactive, veth_created = false, remove_tmprootdir = false;
|
||||
char tmprootdir[] = "/tmp/nspawn-root-XXXXXX";
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
|
||||
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
|
||||
|
||||
log_parse_environment();
|
||||
log_open();
|
||||
|
@ -3652,6 +3653,10 @@ int main(int argc, char *argv[]) {
|
|||
goto finish;
|
||||
}
|
||||
|
||||
r = dissected_image_decrypt_interactively(dissected_image, NULL, 0, &decrypted_image);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
/* Now that we mounted the image, let's try to remove it again, if it is ephemeral */
|
||||
if (remove_image && unlink(arg_image) >= 0)
|
||||
remove_image = false;
|
||||
|
|
|
@ -17,19 +17,73 @@
|
|||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
#include <libcryptsetup.h>
|
||||
#endif
|
||||
#include <linux/dm-ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "architecture.h"
|
||||
#include "ask-password-api.h"
|
||||
#include "blkid-util.h"
|
||||
#include "dissect-image.h"
|
||||
#include "fd-util.h"
|
||||
#include "gpt.h"
|
||||
#include "mount-util.h"
|
||||
#include "path-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "udev-util.h"
|
||||
|
||||
static int probe_filesystem(const char *node, char **ret_fstype) {
|
||||
#ifdef HAVE_BLKID
|
||||
_cleanup_blkid_free_probe_ blkid_probe b = NULL;
|
||||
const char *fstype;
|
||||
int r;
|
||||
|
||||
b = blkid_new_probe_from_filename(node);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
blkid_probe_enable_superblocks(b, 1);
|
||||
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
|
||||
|
||||
errno = 0;
|
||||
r = blkid_do_safeprobe(b);
|
||||
if (r == -2 || r == 1) {
|
||||
log_debug("Failed to identify any partition type on partition %s", node);
|
||||
goto not_found;
|
||||
}
|
||||
if (r != 0) {
|
||||
if (errno == 0)
|
||||
return -EIO;
|
||||
|
||||
return -errno;
|
||||
}
|
||||
|
||||
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
||||
|
||||
if (fstype) {
|
||||
char *t;
|
||||
|
||||
t = strdup(fstype);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret_fstype = t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
not_found:
|
||||
*ret_fstype = NULL;
|
||||
return 0;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int dissect_image(int fd, DissectedImage **ret) {
|
||||
|
||||
#ifdef HAVE_BLKID
|
||||
|
@ -96,7 +150,7 @@ int dissect_image(int fd, DissectedImage **ret) {
|
|||
return -ENOMEM;
|
||||
|
||||
(void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
|
||||
if (streq_ptr(usage, "filesystem")) {
|
||||
if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
|
||||
_cleanup_free_ char *t = NULL, *n = NULL;
|
||||
const char *fstype = NULL;
|
||||
|
||||
|
@ -123,6 +177,8 @@ int dissect_image(int fd, DissectedImage **ret) {
|
|||
|
||||
t = n = NULL;
|
||||
|
||||
m->encrypted = streq(fstype, "crypto_LUKS");
|
||||
|
||||
*ret = m;
|
||||
m = NULL;
|
||||
|
||||
|
@ -385,52 +441,24 @@ int dissect_image(int fd, DissectedImage **ret) {
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
blkid_free_probe(b);
|
||||
b = NULL;
|
||||
|
||||
/* Fill in file system types if we don't know them yet. */
|
||||
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
const char *fstype;
|
||||
DissectedPartition *p = m->partitions + i;
|
||||
|
||||
if (!m->partitions[i].found) /* not found? */
|
||||
if (!p->found)
|
||||
continue;
|
||||
|
||||
if (m->partitions[i].fstype) /* already know the type? */
|
||||
continue;
|
||||
|
||||
if (!m->partitions[i].node) /* have no device node for? */
|
||||
continue;
|
||||
|
||||
if (b)
|
||||
blkid_free_probe(b);
|
||||
|
||||
b = blkid_new_probe_from_filename(m->partitions[i].node);
|
||||
if (!b)
|
||||
return -ENOMEM;
|
||||
|
||||
blkid_probe_enable_superblocks(b, 1);
|
||||
blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
|
||||
|
||||
errno = 0;
|
||||
r = blkid_do_safeprobe(b);
|
||||
if (r == -2 || r == 1) {
|
||||
log_debug("Failed to identify any partition type on partition %i", m->partitions[i].partno);
|
||||
continue;
|
||||
}
|
||||
if (r != 0) {
|
||||
if (errno == 0)
|
||||
return -EIO;
|
||||
|
||||
return -errno;
|
||||
if (!p->fstype && p->node) {
|
||||
r = probe_filesystem(p->node, &p->fstype);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
|
||||
if (fstype) {
|
||||
char *t;
|
||||
|
||||
t = strdup(fstype);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
m->partitions[i].fstype = t;
|
||||
}
|
||||
if (streq_ptr(p->fstype, "crypto_LUKS"))
|
||||
m->encrypted = true;
|
||||
}
|
||||
|
||||
*ret = m;
|
||||
|
@ -451,48 +479,79 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
|
|||
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
free(m->partitions[i].fstype);
|
||||
free(m->partitions[i].node);
|
||||
free(m->partitions[i].decrypted_fstype);
|
||||
free(m->partitions[i].decrypted_node);
|
||||
}
|
||||
|
||||
free(m);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int mount_partition(DissectedPartition *m, const char *where, const char *directory, DissectedImageMountFlags flags) {
|
||||
const char *p, *options = NULL;
|
||||
static int is_loop_device(const char *path) {
|
||||
char s[strlen("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen("/../loop/")];
|
||||
struct stat st;
|
||||
|
||||
assert(path);
|
||||
|
||||
if (stat(path, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (!S_ISBLK(st.st_mode))
|
||||
return -ENOTBLK;
|
||||
|
||||
xsprintf(s, "/sys/dev/block/%u:%u/loop/", major(st.st_rdev), minor(st.st_rdev));
|
||||
if (access(s, F_OK) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
/* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
|
||||
xsprintf(s, "/sys/dev/block/%u:%u/../loop/", major(st.st_rdev), minor(st.st_rdev));
|
||||
if (access(s, F_OK) < 0)
|
||||
return errno == ENOENT ? false : -errno;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mount_partition(
|
||||
DissectedPartition *m,
|
||||
const char *where,
|
||||
const char *directory,
|
||||
DissectImageFlags flags) {
|
||||
|
||||
const char *p, *options = NULL, *node, *fstype;
|
||||
bool rw;
|
||||
|
||||
assert(m);
|
||||
assert(where);
|
||||
|
||||
if (!m->found || !m->node || !m->fstype)
|
||||
node = m->decrypted_node ?: m->node;
|
||||
fstype = m->decrypted_fstype ?: m->fstype;
|
||||
|
||||
if (!m->found || !node || !fstype)
|
||||
return 0;
|
||||
|
||||
rw = m->rw && !(flags & DISSECTED_IMAGE_READ_ONLY);
|
||||
/* Stacked encryption? Yuck */
|
||||
if (streq_ptr(fstype, "crypto_LUKS"))
|
||||
return -ELOOP;
|
||||
|
||||
rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
|
||||
|
||||
if (directory)
|
||||
p = strjoina(where, directory);
|
||||
else
|
||||
p = where;
|
||||
|
||||
/* Not supported for now. */
|
||||
if (streq(m->fstype, "crypto_LUKS"))
|
||||
return -EOPNOTSUPP;
|
||||
/* If requested, turn on discard support. */
|
||||
if (STR_IN_SET(fstype, "btrfs", "ext4", "vfat", "xfs") &&
|
||||
((flags & DISSECT_IMAGE_DISCARD) ||
|
||||
((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
|
||||
options = "discard";
|
||||
|
||||
/* If this is a loopback device then let's mount the image with discard, so that the underlying file remains
|
||||
* sparse when possible. */
|
||||
if ((flags & DISSECTED_IMAGE_DISCARD_ON_LOOP) &&
|
||||
STR_IN_SET(m->fstype, "btrfs", "ext4", "vfat", "xfs")) {
|
||||
const char *l;
|
||||
|
||||
l = path_startswith(m->node, "/dev");
|
||||
if (l && startswith(l, "loop"))
|
||||
options = "discard";
|
||||
}
|
||||
|
||||
return mount_verbose(LOG_DEBUG, m->node, p, m->fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||
return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
|
||||
}
|
||||
|
||||
int dissected_image_mount(DissectedImage *m, const char *where, DissectedImageMountFlags flags) {
|
||||
int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
|
@ -536,6 +595,284 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectedImageMo
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
typedef struct DecryptedPartition {
|
||||
struct crypt_device *device;
|
||||
char *name;
|
||||
bool relinquished;
|
||||
} DecryptedPartition;
|
||||
|
||||
struct DecryptedImage {
|
||||
DecryptedPartition *decrypted;
|
||||
size_t n_decrypted;
|
||||
size_t n_allocated;
|
||||
};
|
||||
#endif
|
||||
|
||||
DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
if (!d)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < d->n_decrypted; i++) {
|
||||
DecryptedPartition *p = d->decrypted + i;
|
||||
|
||||
if (p->device && p->name && !p->relinquished) {
|
||||
r = crypt_deactivate(p->device, p->name);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
|
||||
}
|
||||
|
||||
if (p->device)
|
||||
crypt_free(p->device);
|
||||
free(p->name);
|
||||
}
|
||||
|
||||
free(d);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
static int decrypt_partition(
|
||||
DissectedPartition *m,
|
||||
const char *passphrase,
|
||||
DissectImageFlags flags,
|
||||
DecryptedImage *d) {
|
||||
|
||||
_cleanup_free_ char *node = NULL, *name = NULL;
|
||||
struct crypt_device *cd;
|
||||
const char *suffix;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(d);
|
||||
|
||||
if (!m->found || !m->node || !m->fstype)
|
||||
return 0;
|
||||
|
||||
if (!streq(m->fstype, "crypto_LUKS"))
|
||||
return 0;
|
||||
|
||||
suffix = strrchr(m->node, '/');
|
||||
if (!suffix)
|
||||
return -EINVAL;
|
||||
suffix++;
|
||||
if (isempty(suffix))
|
||||
return -EINVAL;
|
||||
|
||||
name = strjoin(suffix, "-decrypted");
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
if (!filename_is_valid(name))
|
||||
return -EINVAL;
|
||||
|
||||
node = strjoin(crypt_get_dir(), "/", name);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
|
||||
return -ENOMEM;
|
||||
|
||||
r = crypt_init(&cd, m->node);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = crypt_load(cd, CRYPT_LUKS1, NULL);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
|
||||
((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
|
||||
((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
|
||||
if (r == -EPERM) {
|
||||
r = -EKEYREJECTED;
|
||||
goto fail;
|
||||
}
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
d->decrypted[d->n_decrypted].name = name;
|
||||
name = NULL;
|
||||
|
||||
d->decrypted[d->n_decrypted].device = cd;
|
||||
d->n_decrypted++;
|
||||
|
||||
m->decrypted_node = node;
|
||||
node = NULL;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
crypt_free(cd);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
int dissected_image_decrypt(
|
||||
DissectedImage *m,
|
||||
const char *passphrase,
|
||||
DissectImageFlags flags,
|
||||
DecryptedImage **ret) {
|
||||
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
unsigned i;
|
||||
int r;
|
||||
#endif
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Returns:
|
||||
*
|
||||
* = 0 → There was nothing to decrypt
|
||||
* > 0 → Decrypted successfully
|
||||
* -ENOKEY → There's some to decrypt but no key was supplied
|
||||
* -EKEYREJECTED → Passed key was not correct
|
||||
*/
|
||||
|
||||
if (!m->encrypted) {
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
if (!passphrase)
|
||||
return -ENOKEY;
|
||||
|
||||
d = new0(DecryptedImage, 1);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
DissectedPartition *p = m->partitions + i;
|
||||
|
||||
if (!p->found)
|
||||
continue;
|
||||
|
||||
r = decrypt_partition(p, passphrase, flags, d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!p->decrypted_fstype && p->decrypted_node) {
|
||||
r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = d;
|
||||
d = NULL;
|
||||
|
||||
return 1;
|
||||
#else
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
int dissected_image_decrypt_interactively(
|
||||
DissectedImage *m,
|
||||
const char *passphrase,
|
||||
DissectImageFlags flags,
|
||||
DecryptedImage **ret) {
|
||||
|
||||
_cleanup_strv_free_erase_ char **z = NULL;
|
||||
int n = 3, r;
|
||||
|
||||
if (passphrase)
|
||||
n--;
|
||||
|
||||
for (;;) {
|
||||
r = dissected_image_decrypt(m, passphrase, flags, ret);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
if (r == -EKEYREJECTED)
|
||||
log_error_errno(r, "Incorrect passphrase, try again!");
|
||||
else if (r != -ENOKEY) {
|
||||
log_error_errno(r, "Failed to decrypt image: %m");
|
||||
return r;
|
||||
}
|
||||
|
||||
if (--n < 0) {
|
||||
log_error("Too many retries.");
|
||||
return -EKEYREJECTED;
|
||||
}
|
||||
|
||||
z = strv_free(z);
|
||||
|
||||
r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to query for passphrase: %m");
|
||||
|
||||
passphrase = z[0];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
static int deferred_remove(DecryptedPartition *p) {
|
||||
|
||||
struct dm_ioctl dm = {
|
||||
.version = {
|
||||
DM_VERSION_MAJOR,
|
||||
DM_VERSION_MINOR,
|
||||
DM_VERSION_PATCHLEVEL
|
||||
},
|
||||
.data_size = sizeof(dm),
|
||||
.flags = DM_DEFERRED_REMOVE,
|
||||
};
|
||||
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
||||
assert(p);
|
||||
|
||||
/* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() directly. */
|
||||
|
||||
fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
|
||||
strncpy(dm.name, p->name, sizeof(dm.name));
|
||||
|
||||
if (ioctl(fd, DM_DEV_REMOVE, &dm))
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int decrypted_image_relinquish(DecryptedImage *d) {
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
size_t i;
|
||||
int r;
|
||||
#endif
|
||||
|
||||
assert(d);
|
||||
|
||||
/* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
|
||||
* that we don't clean it up ourselves either anymore */
|
||||
|
||||
#ifdef HAVE_LIBCRYPTSETUP
|
||||
for (i = 0; i < d->n_decrypted; i++) {
|
||||
DecryptedPartition *p = d->decrypted + i;
|
||||
|
||||
if (p->relinquished)
|
||||
continue;
|
||||
|
||||
r = deferred_remove(p);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
|
||||
|
||||
p->relinquished = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *const partition_designator_table[] = {
|
||||
[PARTITION_ROOT] = "root",
|
||||
[PARTITION_ROOT_SECONDARY] = "root-secondary",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
typedef struct DissectedImage DissectedImage;
|
||||
typedef struct DissectedPartition DissectedPartition;
|
||||
typedef struct DecryptedImage DecryptedImage;
|
||||
|
||||
struct DissectedPartition {
|
||||
bool found:1;
|
||||
|
@ -33,6 +34,8 @@ struct DissectedPartition {
|
|||
int architecture; /* Intended architecture: either native, secondary or unset (-1). */
|
||||
char *fstype;
|
||||
char *node;
|
||||
char *decrypted_node;
|
||||
char *decrypted_fstype;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -46,12 +49,15 @@ enum {
|
|||
_PARTITION_DESIGNATOR_INVALID = -1
|
||||
};
|
||||
|
||||
typedef enum DissectedImageMountFlags {
|
||||
DISSECTED_IMAGE_READ_ONLY = 1,
|
||||
DISSECTED_IMAGE_DISCARD_ON_LOOP = 2, /* Turn on "discard" if on loop device and file system supports it */
|
||||
} DissectedImageMountFlags;
|
||||
typedef enum DissectImageFlags {
|
||||
DISSECT_IMAGE_READ_ONLY = 1,
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP = 2, /* Turn on "discard" if on loop device and file system supports it */
|
||||
DISSECT_IMAGE_DISCARD = 4, /* Turn on "discard" if file system supports it, on all block devices */
|
||||
DISSECT_IMAGE_DISCARD_ON_CRYPTO = 8, /* Turn on "discard" also on crypto devices */
|
||||
} DissectImageFlags;
|
||||
|
||||
struct DissectedImage {
|
||||
bool encrypted;
|
||||
DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
|
||||
};
|
||||
|
||||
|
@ -60,7 +66,13 @@ int dissect_image(int fd, DissectedImage **ret);
|
|||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, DissectedImageMountFlags flags);
|
||||
int dissected_image_decrypt(DissectedImage *m, const char *passphrase, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, DissectImageFlags flags, DecryptedImage **ret);
|
||||
int dissected_image_mount(DissectedImage *m, const char *dest, DissectImageFlags flags);
|
||||
|
||||
DecryptedImage* decrypted_image_unref(DecryptedImage *p);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
|
||||
int decrypted_image_relinquish(DecryptedImage *d);
|
||||
|
||||
const char* partition_designator_to_string(int i) _const_;
|
||||
int partition_designator_from_string(const char *name) _pure_;
|
||||
|
|
Loading…
Reference in New Issue