mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-08 09:20:11 +01:00
On some systems the requested amount of memory causes OOM killer to kill the process (instead of returning ENOMEM). For now, we never try to use more than half of available physical memory.
253 lines
6.7 KiB
C
253 lines
6.7 KiB
C
/*
|
|
* utils_pbkdf - PBKDF ssettings for libcryptsetup
|
|
*
|
|
* Copyright (C) 2009-2017, Red Hat, Inc. All rights reserved.
|
|
* Copyright (C) 2009-2017, Milan Broz
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
#include "internal.h"
|
|
|
|
const struct crypt_pbkdf_type default_luks2 = {
|
|
.type = DEFAULT_LUKS2_PBKDF,
|
|
.hash = DEFAULT_LUKS1_HASH,
|
|
.time_ms = DEFAULT_LUKS2_ITER_TIME,
|
|
.max_memory_kb = DEFAULT_LUKS2_MEMORY_KB,
|
|
.parallel_threads = DEFAULT_LUKS2_PARALLEL_THREADS
|
|
};
|
|
|
|
const struct crypt_pbkdf_type default_luks1 = {
|
|
.type = CRYPT_KDF_PBKDF2,
|
|
.hash = DEFAULT_LUKS1_HASH,
|
|
.time_ms = DEFAULT_LUKS1_ITER_TIME
|
|
};
|
|
|
|
static uint32_t adjusted_pbkdf_memory(void)
|
|
{
|
|
uint64_t memory_kb = crypt_getphysmemory_kb();
|
|
|
|
/* Ignore bogus value */
|
|
if (memory_kb < (128 * 1024))
|
|
return DEFAULT_LUKS2_MEMORY_KB;
|
|
|
|
/*
|
|
* Never use more than half of physical memory.
|
|
* OOM killer is too clever...
|
|
*/
|
|
memory_kb /= 2;
|
|
|
|
if (memory_kb < DEFAULT_LUKS2_MEMORY_KB)
|
|
return (uint32_t)memory_kb;
|
|
|
|
return DEFAULT_LUKS2_MEMORY_KB;
|
|
}
|
|
|
|
/*
|
|
* PBKDF configuration interface
|
|
*/
|
|
int verify_pbkdf_params(struct crypt_device *cd,
|
|
const struct crypt_pbkdf_type *pbkdf)
|
|
{
|
|
const char *pbkdf_type;
|
|
int r = 0;
|
|
|
|
if (!pbkdf->type || !pbkdf->hash || !pbkdf->time_ms)
|
|
return -EINVAL;
|
|
|
|
/* TODO: initialise crypto and check the hash and pbkdf are both available */
|
|
r = crypt_parse_pbkdf(pbkdf->type, &pbkdf_type);
|
|
if (r < 0) {
|
|
log_err(cd, _("Unknown PBKDF type %s.\n"), pbkdf->type);
|
|
return r;
|
|
}
|
|
|
|
if (crypt_get_type(cd) &&
|
|
!strcmp(crypt_get_type(cd), CRYPT_LUKS1) &&
|
|
strcmp(pbkdf_type, CRYPT_KDF_PBKDF2)) {
|
|
log_err(cd, _("Requested PBKDF type is not supported for LUKS1.\n"));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!strcmp(pbkdf_type, CRYPT_KDF_PBKDF2)) {
|
|
if (pbkdf->max_memory_kb || pbkdf->parallel_threads) {
|
|
log_err(cd, _("PBKDF max memory or parallel threads must not be set with pbkdf2.\n"));
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (pbkdf->max_memory_kb > MAX_PBKDF_MEMORY) {
|
|
log_err(cd, _("Requested maximum PBKDF memory cost is too high (maximum is %d kilobytes).\n"),
|
|
MAX_PBKDF_MEMORY);
|
|
r = -EINVAL;
|
|
}
|
|
if (!pbkdf->max_memory_kb) {
|
|
log_err(cd, _("Requested maximum PBKDF memory can not be zero.\n"));
|
|
r = -EINVAL;
|
|
}
|
|
if (!pbkdf->parallel_threads) {
|
|
log_err(cd, _("Requested PBKDF parallel threads can not be zero.\n"));
|
|
r = -EINVAL;
|
|
}
|
|
if (!pbkdf->time_ms) {
|
|
log_err(cd, _("Requested PBKDF target time can not be zero.\n"));
|
|
r = -EINVAL;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
int init_pbkdf_type(struct crypt_device *cd,
|
|
const struct crypt_pbkdf_type *pbkdf,
|
|
const char *dev_type)
|
|
{
|
|
struct crypt_pbkdf_type *cd_pbkdf = crypt_get_pbkdf(cd);
|
|
const char *hash, *type;
|
|
unsigned cpus;
|
|
uint32_t old_flags, memory_kb;
|
|
int r;
|
|
|
|
if (!pbkdf && dev_type && !strcmp(dev_type, CRYPT_LUKS2))
|
|
pbkdf = &default_luks2;
|
|
else if (!pbkdf)
|
|
pbkdf = &default_luks1;
|
|
|
|
r = verify_pbkdf_params(cd, pbkdf);
|
|
if (r)
|
|
return r;
|
|
|
|
/*
|
|
* Crypto backend may be not initialized here,
|
|
* cannot check if algorithms are really available.
|
|
* It will fail later anyway :-)
|
|
*/
|
|
type = strdup(pbkdf->type);
|
|
hash = strdup(pbkdf->hash);
|
|
|
|
if (!type || !hash) {
|
|
free(CONST_CAST(void*)type);
|
|
free(CONST_CAST(void*)hash);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
free(CONST_CAST(void*)cd_pbkdf->type);
|
|
free(CONST_CAST(void*)cd_pbkdf->hash);
|
|
cd_pbkdf->type = type;
|
|
cd_pbkdf->hash = hash;
|
|
|
|
old_flags = cd_pbkdf->flags;
|
|
cd_pbkdf->flags = pbkdf->flags;
|
|
|
|
/* Reset iteration count so benchmark must run again. */
|
|
if (cd_pbkdf->flags & CRYPT_PBKDF_NO_BENCHMARK)
|
|
cd_pbkdf->iterations = pbkdf->iterations;
|
|
else
|
|
cd_pbkdf->iterations = 0;
|
|
|
|
if (old_flags & CRYPT_PBKDF_ITER_TIME_SET)
|
|
cd_pbkdf->flags |= CRYPT_PBKDF_ITER_TIME_SET;
|
|
else
|
|
cd_pbkdf->time_ms = pbkdf->time_ms;
|
|
|
|
cd_pbkdf->max_memory_kb = pbkdf->max_memory_kb;
|
|
cd_pbkdf->parallel_threads = pbkdf->parallel_threads;
|
|
|
|
if (cd_pbkdf->parallel_threads > MAX_PBKDF_THREADS) {
|
|
log_dbg("Maximum PBKDF threads is %d (requested %d).",
|
|
MAX_PBKDF_THREADS, cd_pbkdf->parallel_threads);
|
|
cd_pbkdf->parallel_threads = MAX_PBKDF_THREADS;
|
|
}
|
|
|
|
if (cd_pbkdf->parallel_threads) {
|
|
cpus = crypt_cpusonline();
|
|
if (cd_pbkdf->parallel_threads > cpus) {
|
|
log_dbg("Only %u active CPUs detected, "
|
|
"PBKDF threads decreased from %d to %d.",
|
|
cpus, cd_pbkdf->parallel_threads, cpus);
|
|
cd_pbkdf->parallel_threads = cpus;
|
|
}
|
|
}
|
|
|
|
if (cd_pbkdf->max_memory_kb) {
|
|
memory_kb = adjusted_pbkdf_memory();
|
|
if (cd_pbkdf->max_memory_kb > memory_kb) {
|
|
log_dbg("Not enough physical memory detected, "
|
|
"PBKDF max memory decreased from %dkB to %dkB.",
|
|
cd_pbkdf->max_memory_kb, memory_kb);
|
|
cd_pbkdf->max_memory_kb = memory_kb;
|
|
}
|
|
}
|
|
|
|
log_dbg("PBKDF %s, hash %s, time_ms %u (iterations %u), max_memory_kb %u, parallel_threads %u.",
|
|
cd_pbkdf->type ?: "(none)", cd_pbkdf->hash ?: "(none)", cd_pbkdf->time_ms,
|
|
cd_pbkdf->iterations, cd_pbkdf->max_memory_kb, cd_pbkdf->parallel_threads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Libcryptsetup API */
|
|
|
|
int crypt_set_pbkdf_type(struct crypt_device *cd, const struct crypt_pbkdf_type *pbkdf)
|
|
{
|
|
if (!cd)
|
|
return -EINVAL;
|
|
|
|
if (!pbkdf)
|
|
log_dbg("Resetting pbkdf type to default");
|
|
|
|
crypt_get_pbkdf(cd)->flags = 0;
|
|
|
|
return init_pbkdf_type(cd, pbkdf, crypt_get_type(cd));
|
|
}
|
|
|
|
const struct crypt_pbkdf_type *crypt_get_pbkdf_type(struct crypt_device *cd)
|
|
{
|
|
if (!cd)
|
|
return NULL;
|
|
|
|
return crypt_get_pbkdf(cd)->type ? crypt_get_pbkdf(cd) : NULL;
|
|
}
|
|
|
|
void crypt_set_iteration_time(struct crypt_device *cd, uint64_t iteration_time_ms)
|
|
{
|
|
struct crypt_pbkdf_type *pbkdf;
|
|
uint32_t old_time_ms;
|
|
|
|
if (!cd || iteration_time_ms > UINT32_MAX)
|
|
return;
|
|
|
|
pbkdf = crypt_get_pbkdf(cd);
|
|
old_time_ms = pbkdf->time_ms;
|
|
pbkdf->time_ms = (uint32_t)iteration_time_ms;
|
|
|
|
if (pbkdf->type && verify_pbkdf_params(cd, pbkdf)) {
|
|
pbkdf->time_ms = old_time_ms;
|
|
log_dbg("Invalid iteration time.");
|
|
return;
|
|
}
|
|
|
|
pbkdf->flags |= CRYPT_PBKDF_ITER_TIME_SET;
|
|
|
|
/* iterations must be benchmarked now */
|
|
pbkdf->flags &= ~(CRYPT_PBKDF_NO_BENCHMARK);
|
|
pbkdf->iterations = 0;
|
|
|
|
log_dbg("Iteration time set to %" PRIu64 " milliseconds.", iteration_time_ms);
|
|
}
|