mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-06 00:10:04 +01:00
Process LUKS keyslots in userspace through kernel crypto wrapper.
This allow LUKS handling without requiring root privilege. The dmcrypt device-mapper is used only for device activation now.
This commit is contained in:
@@ -5,7 +5,7 @@ noinst_LTLIBRARIES = libcrypto_backend.la
|
|||||||
libcrypto_backend_la_CFLAGS = $(AM_CFLAGS) -Wall @CRYPTO_CFLAGS@
|
libcrypto_backend_la_CFLAGS = $(AM_CFLAGS) -Wall @CRYPTO_CFLAGS@
|
||||||
|
|
||||||
libcrypto_backend_la_SOURCES = crypto_backend.h \
|
libcrypto_backend_la_SOURCES = crypto_backend.h \
|
||||||
crypto_cipher_kernel.c pbkdf_check.c crc32.c
|
crypto_cipher_kernel.c crypto_storage.c pbkdf_check.c crc32.c
|
||||||
|
|
||||||
if CRYPTO_BACKEND_GCRYPT
|
if CRYPTO_BACKEND_GCRYPT
|
||||||
libcrypto_backend_la_SOURCES += crypto_gcrypt.c
|
libcrypto_backend_la_SOURCES += crypto_gcrypt.c
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ struct crypt_device;
|
|||||||
struct crypt_hash;
|
struct crypt_hash;
|
||||||
struct crypt_hmac;
|
struct crypt_hmac;
|
||||||
struct crypt_cipher;
|
struct crypt_cipher;
|
||||||
|
struct crypt_storage;
|
||||||
|
|
||||||
int crypt_backend_init(struct crypt_device *ctx);
|
int crypt_backend_init(struct crypt_device *ctx);
|
||||||
|
|
||||||
@@ -91,4 +92,14 @@ int crypt_cipher_decrypt(struct crypt_cipher *ctx,
|
|||||||
const char *in, char *out, size_t length,
|
const char *in, char *out, size_t length,
|
||||||
const char *iv, size_t iv_length);
|
const char *iv, size_t iv_length);
|
||||||
|
|
||||||
|
/* storage encryption wrappers */
|
||||||
|
int crypt_storage_init(struct crypt_storage **ctx, uint64_t sector_start,
|
||||||
|
const char *cipher, const char *cipher_mode,
|
||||||
|
char *key, size_t key_length);
|
||||||
|
int crypt_storage_destroy(struct crypt_storage *ctx);
|
||||||
|
int crypt_storage_decrypt(struct crypt_storage *ctx, uint64_t sector,
|
||||||
|
size_t count, char *buffer);
|
||||||
|
int crypt_storage_encrypt(struct crypt_storage *ctx, uint64_t sector,
|
||||||
|
size_t count, char *buffer);
|
||||||
|
|
||||||
#endif /* _CRYPTO_BACKEND_H */
|
#endif /* _CRYPTO_BACKEND_H */
|
||||||
|
|||||||
274
lib/crypto_backend/crypto_storage.c
Normal file
274
lib/crypto_backend/crypto_storage.c
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
* Generic wrapper for storage encryption modes and Initial Vectors
|
||||||
|
* (reimplementation of some functions from Linux dm-crypt kernel)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014, Milan Broz
|
||||||
|
*
|
||||||
|
* This file 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 file 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 file; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "bitops.h"
|
||||||
|
#include "crypto_backend.h"
|
||||||
|
|
||||||
|
#define SECTOR_SHIFT 9
|
||||||
|
#define SECTOR_SIZE (1 << SECTOR_SHIFT)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Internal IV helper
|
||||||
|
* IV documentation: https://code.google.com/p/cryptsetup/wiki/DMCrypt
|
||||||
|
*/
|
||||||
|
struct crypt_sector_iv {
|
||||||
|
enum { IV_NONE, IV_NULL, IV_PLAIN, IV_PLAIN64, IV_ESSIV, IV_BENBI } type;
|
||||||
|
int iv_size;
|
||||||
|
char *iv;
|
||||||
|
struct crypt_cipher *essiv_cipher;
|
||||||
|
int benbi_shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Block encryption storage context */
|
||||||
|
struct crypt_storage {
|
||||||
|
uint64_t sector_start;
|
||||||
|
struct crypt_cipher *cipher;
|
||||||
|
struct crypt_sector_iv cipher_iv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int int_log2(unsigned int x)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
for (x >>= 1; x > 0; x >>= 1)
|
||||||
|
r++;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int crypt_sector_iv_init(struct crypt_sector_iv *ctx,
|
||||||
|
const char *cipher_name, const char *iv_name,
|
||||||
|
char *key, size_t key_length)
|
||||||
|
{
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
|
||||||
|
if (!iv_name || !strcmp(cipher_name, "cipher_null")) {
|
||||||
|
ctx->type = IV_NONE;
|
||||||
|
ctx->iv_size = 0;
|
||||||
|
return 0;
|
||||||
|
} else if (!strncasecmp(iv_name, "null", 4)) {
|
||||||
|
ctx->type = IV_NULL;
|
||||||
|
} else if (!strncasecmp(iv_name, "plain64", 7)) {
|
||||||
|
ctx->type = IV_PLAIN64;
|
||||||
|
} else if (!strncasecmp(iv_name, "plain", 5)) {
|
||||||
|
ctx->type = IV_PLAIN;
|
||||||
|
} else if (!strncasecmp(iv_name, "essiv", 5)) {
|
||||||
|
struct crypt_hash *h = NULL;
|
||||||
|
char *hash_name = strchr(iv_name, ':');
|
||||||
|
char tmp[256];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (!hash_name || crypt_hash_size(++hash_name) > sizeof(tmp))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (crypt_hash_init(&h, hash_name))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
crypt_hash_write(h, key, key_length);
|
||||||
|
crypt_hash_final(h, tmp, crypt_hash_size(hash_name));
|
||||||
|
crypt_hash_destroy(h);
|
||||||
|
|
||||||
|
r = crypt_cipher_init(&ctx->essiv_cipher, cipher_name, "ecb",
|
||||||
|
tmp, crypt_hash_size(hash_name));
|
||||||
|
memset(tmp, 0, sizeof(tmp));
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
ctx->type = IV_ESSIV;
|
||||||
|
} else if (!strncasecmp(iv_name, "benbi", 5)) {
|
||||||
|
int log = int_log2(crypt_cipher_blocksize(cipher_name));
|
||||||
|
if (log > SECTOR_SHIFT)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ctx->type = IV_BENBI;
|
||||||
|
ctx->benbi_shift = SECTOR_SHIFT - log;
|
||||||
|
} else
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!ctx->iv_size)
|
||||||
|
ctx->iv_size = crypt_cipher_blocksize(cipher_name);
|
||||||
|
|
||||||
|
ctx->iv = malloc(ctx->iv_size);
|
||||||
|
if (!ctx->iv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int crypt_sector_iv_generate(struct crypt_sector_iv *ctx, uint64_t sector)
|
||||||
|
{
|
||||||
|
uint64_t val;
|
||||||
|
|
||||||
|
switch (ctx->type) {
|
||||||
|
case IV_NONE:
|
||||||
|
break;
|
||||||
|
case IV_NULL:
|
||||||
|
memset(ctx->iv, 0, ctx->iv_size);
|
||||||
|
break;
|
||||||
|
case IV_PLAIN:
|
||||||
|
memset(ctx->iv, 0, ctx->iv_size);
|
||||||
|
*(uint32_t *)ctx->iv = cpu_to_le32(sector & 0xffffffff);
|
||||||
|
break;
|
||||||
|
case IV_PLAIN64:
|
||||||
|
memset(ctx->iv, 0, ctx->iv_size);
|
||||||
|
*(uint64_t *)ctx->iv = cpu_to_le64(sector);
|
||||||
|
break;
|
||||||
|
case IV_ESSIV:
|
||||||
|
memset(ctx->iv, 0, ctx->iv_size);
|
||||||
|
*(uint64_t *)ctx->iv = cpu_to_le64(sector);
|
||||||
|
return crypt_cipher_encrypt(ctx->essiv_cipher,
|
||||||
|
ctx->iv, ctx->iv, ctx->iv_size, NULL, 0);
|
||||||
|
break;
|
||||||
|
case IV_BENBI:
|
||||||
|
memset(ctx->iv, 0, ctx->iv_size);
|
||||||
|
val = cpu_to_be64((sector << ctx->benbi_shift) + 1);
|
||||||
|
memcpy(ctx->iv + ctx->iv_size - sizeof(val), &val, sizeof(val));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int crypt_sector_iv_destroy(struct crypt_sector_iv *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->type == IV_ESSIV)
|
||||||
|
crypt_cipher_destroy(ctx->essiv_cipher);
|
||||||
|
|
||||||
|
if (ctx->iv) {
|
||||||
|
memset(ctx->iv, 0, ctx->iv_size);
|
||||||
|
free(ctx->iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Block encryption storage wrappers */
|
||||||
|
|
||||||
|
int crypt_storage_init(struct crypt_storage **ctx,
|
||||||
|
uint64_t sector_start,
|
||||||
|
const char *cipher,
|
||||||
|
const char *cipher_mode,
|
||||||
|
char *key, size_t key_length)
|
||||||
|
{
|
||||||
|
struct crypt_storage *s;
|
||||||
|
char mode_name[64];
|
||||||
|
char *cipher_iv = NULL;
|
||||||
|
int r = -EIO;
|
||||||
|
|
||||||
|
s = malloc(sizeof(*s));
|
||||||
|
if (!s)
|
||||||
|
return -ENOMEM;
|
||||||
|
memset(s, 0, sizeof(*s));
|
||||||
|
|
||||||
|
/* Remove IV if present */
|
||||||
|
strncpy(mode_name, cipher_mode, sizeof(mode_name));
|
||||||
|
mode_name[sizeof(mode_name) - 1] = 0;
|
||||||
|
cipher_iv = strchr(mode_name, '-');
|
||||||
|
if (cipher_iv) {
|
||||||
|
*cipher_iv = '\0';
|
||||||
|
cipher_iv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = crypt_cipher_init(&s->cipher, cipher, mode_name, key, key_length);
|
||||||
|
if (r) {
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = crypt_sector_iv_init(&s->cipher_iv, cipher, cipher_iv, key, key_length);
|
||||||
|
if (r) {
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->sector_start = sector_start;
|
||||||
|
|
||||||
|
*ctx = s;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int crypt_storage_decrypt(struct crypt_storage *ctx,
|
||||||
|
uint64_t sector, size_t count,
|
||||||
|
char *buffer)
|
||||||
|
{
|
||||||
|
int i, r = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
r = crypt_sector_iv_generate(&ctx->cipher_iv, sector + i);
|
||||||
|
if (r)
|
||||||
|
break;
|
||||||
|
r = crypt_cipher_decrypt(ctx->cipher,
|
||||||
|
&buffer[i * SECTOR_SIZE],
|
||||||
|
&buffer[i * SECTOR_SIZE],
|
||||||
|
SECTOR_SIZE,
|
||||||
|
ctx->cipher_iv.iv,
|
||||||
|
ctx->cipher_iv.iv_size);
|
||||||
|
if (r)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int crypt_storage_encrypt(struct crypt_storage *ctx,
|
||||||
|
uint64_t sector, size_t count,
|
||||||
|
char *buffer)
|
||||||
|
{
|
||||||
|
int i, r = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
r = crypt_sector_iv_generate(&ctx->cipher_iv, sector + i);
|
||||||
|
if (r)
|
||||||
|
break;
|
||||||
|
r = crypt_cipher_encrypt(ctx->cipher,
|
||||||
|
&buffer[i * SECTOR_SIZE],
|
||||||
|
&buffer[i * SECTOR_SIZE],
|
||||||
|
SECTOR_SIZE,
|
||||||
|
ctx->cipher_iv.iv,
|
||||||
|
ctx->cipher_iv.iv_size);
|
||||||
|
if (r)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int crypt_storage_destroy(struct crypt_storage *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
crypt_sector_iv_destroy(&ctx->cipher_iv);
|
||||||
|
|
||||||
|
if (ctx->cipher)
|
||||||
|
crypt_cipher_destroy(ctx->cipher);
|
||||||
|
|
||||||
|
memset(ctx, 0, sizeof(*ctx));
|
||||||
|
free(ctx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2004-2006, Clemens Fruhwirth <clemens@endorphin.org>
|
* Copyright (C) 2004-2006, Clemens Fruhwirth <clemens@endorphin.org>
|
||||||
* Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
|
* Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
|
||||||
|
* Copyright (C) 2012-2014, Milan Broz
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
@@ -125,8 +126,45 @@ int LUKS_encrypt_to_storage(char *src, size_t srcLength,
|
|||||||
unsigned int sector,
|
unsigned int sector,
|
||||||
struct crypt_device *ctx)
|
struct crypt_device *ctx)
|
||||||
{
|
{
|
||||||
return LUKS_endec_template(src, srcLength, cipher, cipher_mode,
|
|
||||||
vk, sector, write_blockwise, O_RDWR, ctx);
|
struct device *device = crypt_metadata_device(ctx);
|
||||||
|
struct crypt_storage *s;
|
||||||
|
int devfd, bsize, r = 0;
|
||||||
|
|
||||||
|
/* Only whole sector writes supported */
|
||||||
|
if (srcLength % SECTOR_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Encrypt buffer */
|
||||||
|
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
|
||||||
|
|
||||||
|
/* Fallback to old temporary dmcrypt device */
|
||||||
|
if (r == -ENOTSUP)
|
||||||
|
return LUKS_endec_template(src, srcLength, cipher, cipher_mode,
|
||||||
|
vk, sector, write_blockwise, O_RDWR, ctx);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
r = crypt_storage_encrypt(s, 0, srcLength / SECTOR_SIZE, src);
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Write buffer to device */
|
||||||
|
bsize = device_block_size(device);
|
||||||
|
if (bsize <= 0)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
devfd = open(device_path(device), O_RDWR | O_DIRECT);
|
||||||
|
if (devfd == -1)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
|
||||||
|
write_blockwise(devfd, bsize, src, srcLength) == -1)
|
||||||
|
r = -EIO;
|
||||||
|
|
||||||
|
close(devfd);
|
||||||
|
return r;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int LUKS_decrypt_from_storage(char *dst, size_t dstLength,
|
int LUKS_decrypt_from_storage(char *dst, size_t dstLength,
|
||||||
@@ -136,6 +174,48 @@ int LUKS_decrypt_from_storage(char *dst, size_t dstLength,
|
|||||||
unsigned int sector,
|
unsigned int sector,
|
||||||
struct crypt_device *ctx)
|
struct crypt_device *ctx)
|
||||||
{
|
{
|
||||||
return LUKS_endec_template(dst, dstLength, cipher, cipher_mode,
|
struct device *device = crypt_metadata_device(ctx);
|
||||||
vk, sector, read_blockwise, O_RDONLY, ctx);
|
struct crypt_storage *s;
|
||||||
|
int devfd, bsize, r = 0;
|
||||||
|
|
||||||
|
/* Only whole sector reads supported */
|
||||||
|
if (dstLength % SECTOR_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
r = crypt_storage_init(&s, 0, cipher, cipher_mode, vk->key, vk->keylength);
|
||||||
|
|
||||||
|
/* Fallback to old temporary dmcrypt device */
|
||||||
|
if (r == -ENOTSUP)
|
||||||
|
return LUKS_endec_template(dst, dstLength, cipher, cipher_mode,
|
||||||
|
vk, sector, read_blockwise, O_RDONLY, ctx);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Read buffer from device */
|
||||||
|
bsize = device_block_size(device);
|
||||||
|
if (bsize <= 0) {
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
devfd = open(device_path(device), O_RDONLY | O_DIRECT);
|
||||||
|
if (devfd == -1) {
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lseek(devfd, sector * SECTOR_SIZE, SEEK_SET) == -1 ||
|
||||||
|
read_blockwise(devfd, bsize, dst, dstLength) == -1) {
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
close(devfd);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(devfd);
|
||||||
|
|
||||||
|
/* Decrypt buffer */
|
||||||
|
r = crypt_storage_decrypt(s, 0, dstLength / SECTOR_SIZE, dst);
|
||||||
|
crypt_storage_destroy(s);
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user