/* * Password quality check wrapper * * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved. * Copyright (C) 2012-2023 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 #if defined ENABLE_PWQUALITY #include 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 r = 0; pwquality_free_settings(pwq); return r; } #elif defined ENABLE_PASSWDQC #include 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(¶ms); if (*config && passwdqc_params_load(¶ms, &parse_reason, config)) { log_err(_("Cannot check password quality: %s"), (parse_reason ? parse_reason : "Out of memory")); goto out; } check_reason = passwdqc_check(¶ms.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(¶ms); #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 */ /* coverity[ -taint_source : arg-1 ] */ 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.")); }