Files
cryptsetup/src/utils_password.c
Milan Broz bf4a039d50 Add a debug info if maximum interactive passphrase was read (possible trimmed).
If passphrase is read from a real terminal, there is maximum
interactive input length applied. This means that passphrase
can be trimmed in this case.

This patch adds debug log warning, if read does not detect
end of input (EOL or EOF) and the maximal input read is achieved.

We cannot say for sure if the next character is EOL without
actually reading it, debug warning should be enough in this case.

Fixes: #699
2022-02-25 14:14:03 +01:00

333 lines
8.1 KiB
C

/*
* Password quality check wrapper
*
* Copyright (C) 2012-2022 Red Hat, Inc. All rights reserved.
* Copyright (C) 2012-2022 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 "cryptsetup.h"
#include <termios.h>
#if defined ENABLE_PWQUALITY
#include <pwquality.h>
static int tools_check_pwquality(const char *password)
{
int r;
void *auxerror;
pwquality_settings_t *pwq;
log_dbg("Checking new password using default pwquality settings.");
pwq = pwquality_default_settings();
if (!pwq)
return -EINVAL;
r = pwquality_read_config(pwq, NULL, &auxerror);
if (r) {
log_err(_("Cannot check password quality: %s"),
pwquality_strerror(NULL, 0, r, auxerror));
pwquality_free_settings(pwq);
return -EINVAL;
}
r = pwquality_check(pwq, password, NULL, NULL, &auxerror);
if (r < 0) {
log_err(_("Password quality check failed:\n %s"),
pwquality_strerror(NULL, 0, r, auxerror));
r = -EPERM;
} else {
log_dbg("New password libpwquality score is %d.", r);
r = 0;
}
pwquality_free_settings(pwq);
return r;
}
#elif defined ENABLE_PASSWDQC
#include <passwdqc.h>
static int tools_check_passwdqc(const char *password)
{
passwdqc_params_t params;
char *parse_reason = NULL;
const char *check_reason;
const char *config = PASSWDQC_CONFIG_FILE;
int r = -EINVAL;
passwdqc_params_reset(&params);
if (*config && passwdqc_params_load(&params, &parse_reason, config)) {
log_err(_("Cannot check password quality: %s"),
(parse_reason ? parse_reason : "Out of memory"));
goto out;
}
check_reason = passwdqc_check(&params.qc, password, NULL, NULL);
if (check_reason) {
log_err(_("Password quality check failed: Bad passphrase (%s)"),
check_reason);
r = -EPERM;
} else
r = 0;
out:
#if HAVE_PASSWDQC_PARAMS_FREE
passwdqc_params_free(&params);
#endif
free(parse_reason);
return r;
}
#endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */
/* coverity[ +tainted_string_sanitize_content : arg-0 ] */
static int tools_check_password(const char *password)
{
#if defined ENABLE_PWQUALITY
return tools_check_pwquality(password);
#elif defined ENABLE_PASSWDQC
return tools_check_passwdqc(password);
#else
return 0;
#endif
}
/* Password reading helpers */
static ssize_t read_tty_eol(int fd, char *pass, size_t maxlen)
{
bool eol = false;
size_t read_size = 0;
ssize_t r;
do {
r = read(fd, pass, maxlen - read_size);
if ((r == -1 && errno != EINTR) || quit)
return -1;
if (r >= 0) {
if (!r || pass[r-1] == '\n')
eol = true;
read_size += (size_t)r;
pass = pass + r;
}
} while (!eol && read_size != maxlen);
return (ssize_t)read_size;
}
/* The pass buffer is zeroed and has trailing \0 already " */
static int untimed_read(int fd, char *pass, size_t maxlen, size_t *realsize)
{
ssize_t i;
i = read_tty_eol(fd, pass, maxlen);
if (i > 0) {
if (pass[i-1] == '\n') {
pass[i-1] = '\0';
*realsize = i - 1;
} else
*realsize = i;
i = 0;
} else if (i == 0) /* empty input */
i = -1;
return i;
}
static int timed_read(int fd, char *pass, size_t maxlen, size_t *realsize, long timeout)
{
struct timeval t;
fd_set fds = {}; /* Just to avoid scan-build false report for FD_SET */
int failed = -1;
FD_ZERO(&fds);
FD_SET(fd, &fds);
t.tv_sec = timeout;
t.tv_usec = 0;
if (select(fd+1, &fds, NULL, NULL, &t) > 0)
failed = untimed_read(fd, pass, maxlen, realsize);
return failed;
}
static int interactive_pass(const char *prompt, char *pass, size_t maxlen,
long timeout)
{
struct termios orig, tmp;
int failed = -1;
int infd, outfd;
size_t realsize = 0;
if (maxlen < 1)
return failed;
/* Read and write to /dev/tty if available */
infd = open("/dev/tty", O_RDWR);
if (infd == -1) {
infd = STDIN_FILENO;
outfd = STDERR_FILENO;
} else
outfd = infd;
if (tcgetattr(infd, &orig))
goto out;
memcpy(&tmp, &orig, sizeof(tmp));
tmp.c_lflag &= ~ECHO;
if (prompt && write(outfd, prompt, strlen(prompt)) < 0)
goto out;
tcsetattr(infd, TCSAFLUSH, &tmp);
if (timeout)
failed = timed_read(infd, pass, maxlen, &realsize, timeout);
else
failed = untimed_read(infd, pass, maxlen, &realsize);
tcsetattr(infd, TCSAFLUSH, &orig);
out:
if (!failed && write(outfd, "\n", 1)) {};
if (realsize == maxlen)
log_dbg("Read stopped at maximal interactive input length, passphrase can be trimmed.");
if (infd != STDIN_FILENO)
close(infd);
return failed;
}
static int crypt_get_key_tty(const char *prompt,
char **key, size_t *key_size,
int timeout, int verify)
{
int key_size_max = DEFAULT_PASSPHRASE_SIZE_MAX;
int r = -EINVAL;
char *pass = NULL, *pass_verify = NULL;
*key = NULL;
*key_size = 0;
log_dbg("Interactive passphrase entry requested.");
pass = crypt_safe_alloc(key_size_max + 1);
if (!pass) {
log_err( _("Out of memory while reading passphrase."));
return -ENOMEM;
}
if (interactive_pass(prompt, pass, key_size_max, timeout)) {
log_err(_("Error reading passphrase from terminal."));
goto out;
}
if (verify) {
pass_verify = crypt_safe_alloc(key_size_max + 1);
if (!pass_verify) {
log_err(_("Out of memory while reading passphrase."));
r = -ENOMEM;
goto out;
}
if (interactive_pass(_("Verify passphrase: "),
pass_verify, key_size_max, timeout)) {
log_err(_("Error reading passphrase from terminal."));
goto out;
}
if (strncmp(pass, pass_verify, key_size_max)) {
log_err(_("Passphrases do not match."));
r = -EPERM;
goto out;
}
}
*key = pass;
/* coverity[string_null] (crypt_safe_alloc wipes string with additional \0) */
*key_size = strlen(pass);
r = 0;
out:
crypt_safe_free(pass_verify);
if (r)
crypt_safe_free(pass);
return r;
}
/*
* Note: --key-file=- is interpreted as a read from a binary file (stdin)
* key_size_max == 0 means detect maximum according to input type (tty/file)
*/
int tools_get_key(const char *prompt,
char **key, size_t *key_size,
uint64_t keyfile_offset, size_t keyfile_size_max,
const char *key_file,
int timeout, int verify, int pwquality,
struct crypt_device *cd)
{
char tmp[PATH_MAX], *backing_file;
int r = -EINVAL, block;
block = tools_signals_blocked();
if (block)
set_int_block(0);
if (tools_is_stdin(key_file)) {
if (isatty(STDIN_FILENO)) {
if (keyfile_offset) {
log_err(_("Cannot use offset with terminal input."));
} else {
r = 0;
if (!prompt && !crypt_get_device_name(cd))
r = snprintf(tmp, sizeof(tmp), _("Enter passphrase: "));
else if (!prompt) {
backing_file = crypt_loop_backing_file(crypt_get_device_name(cd));
r = snprintf(tmp, sizeof(tmp), _("Enter passphrase for %s: "), backing_file ?: crypt_get_device_name(cd));
free(backing_file);
}
if (r >= 0)
r = crypt_get_key_tty(prompt ?: tmp, key, key_size, timeout, verify);
else
r = -EINVAL;
}
} else {
log_dbg("STDIN descriptor passphrase entry requested.");
/* No keyfile means STDIN with EOL handling (\n will end input)). */
r = crypt_keyfile_device_read(cd, NULL, key, key_size,
keyfile_offset, keyfile_size_max,
key_file ? 0 : CRYPT_KEYFILE_STOP_EOL);
}
} else {
log_dbg("File descriptor passphrase entry requested.");
r = crypt_keyfile_device_read(cd, key_file, key, key_size,
keyfile_offset, keyfile_size_max, 0);
}
if (block && !quit)
set_int_block(1);
/* Check pwquality for password (not keyfile) */
if (pwquality && !key_file && !r)
r = tools_check_password(*key);
return r;
}
void tools_passphrase_msg(int r)
{
if (r == -EPERM)
log_err(_("No key available with this passphrase."));
else if (r == -ENOENT)
log_err(_("No usable keyslot is available."));
}