Makefile: Add security compiling option (RELRO, SC, and FORTIFY)
[platform/upstream/cryptsetup.git] / src / utils_password.c
index 55c1343..3374e18 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Password quality check wrapper
  *
- * Copyright (C) 2012-2020 Red Hat, Inc. All rights reserved.
- * Copyright (C) 2012-2020 Milan Broz
+ * 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
@@ -22,8 +22,6 @@
 #include "cryptsetup.h"
 #include <termios.h>
 
-int opt_force_password = 0;
-
 #if defined ENABLE_PWQUALITY
 #include <pwquality.h>
 
@@ -31,7 +29,7 @@ static int tools_check_pwquality(const char *password)
 {
        int r;
        void *auxerror;
-       pwquality_settings_t *pwq; 
+       pwquality_settings_t *pwq;
 
        log_dbg("Checking new password using default pwquality settings.");
        pwq = pwquality_default_settings();
@@ -51,10 +49,8 @@ static int tools_check_pwquality(const char *password)
                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);
+       } else
                r = 0;
-       }
 
        pwquality_free_settings(pwq);
        return r;
@@ -62,55 +58,94 @@ static int tools_check_pwquality(const char *password)
 #elif defined ENABLE_PASSWDQC
 #include <passwdqc.h>
 
-static int tools_check_pwquality(const char *password)
+static int tools_check_passwdqc(const char *password)
 {
        passwdqc_params_t params;
-       char *parse_reason;
+       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"));
-               free(parse_reason);
-               return -EINVAL;
+               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);
-               return -EPERM;
-       }
-
-       return 0;
+               r = -EPERM;
+       } else
+               r = 0;
+out:
+#if HAVE_PASSWDQC_PARAMS_FREE
+       passwdqc_params_free(&params);
+#endif
+       free(parse_reason);
+       return r;
 }
-#else /* !(ENABLE_PWQUALITY || ENABLE_PASSWDQC) */
-static int tools_check_pwquality(const char *password)
+#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
 }
-#endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */
 
 /* Password reading helpers */
-static int untimed_read(int fd, char *pass, size_t maxlen)
+
+/* 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(fd, pass, maxlen);
+       i = read_tty_eol(fd, pass, maxlen);
        if (i > 0) {
-               pass[i-1] = '\0';
+               if (pass[i-1] == '\n') {
+                       pass[i-1] = '\0';
+                       *realsize = i - 1;
+               } else
+                       *realsize = i;
                i = 0;
-       } else if (i == 0) { /* EOF */
-               *pass = 0;
+       } else if (i == 0) /* empty input */
                i = -1;
-       }
+
        return i;
 }
 
-static int timed_read(int fd, char *pass, size_t maxlen, long timeout)
+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 */
@@ -122,7 +157,7 @@ static int timed_read(int fd, char *pass, size_t maxlen, long timeout)
        t.tv_usec = 0;
 
        if (select(fd+1, &fds, NULL, NULL, &t) > 0)
-               failed = untimed_read(fd, pass, maxlen);
+               failed = untimed_read(fd, pass, maxlen, realsize);
 
        return failed;
 }
@@ -133,6 +168,7 @@ static int interactive_pass(const char *prompt, char *pass, size_t maxlen,
        struct termios orig, tmp;
        int failed = -1;
        int infd, outfd;
+       size_t realsize = 0;
 
        if (maxlen < 1)
                return failed;
@@ -146,24 +182,26 @@ static int interactive_pass(const char *prompt, char *pass, size_t maxlen,
                outfd = infd;
 
        if (tcgetattr(infd, &orig))
-               goto out_err;
+               goto out;
 
        memcpy(&tmp, &orig, sizeof(tmp));
        tmp.c_lflag &= ~ECHO;
 
        if (prompt && write(outfd, prompt, strlen(prompt)) < 0)
-               goto out_err;
+               goto out;
 
        tcsetattr(infd, TCSAFLUSH, &tmp);
        if (timeout)
-               failed = timed_read(infd, pass, maxlen, timeout);
+               failed = timed_read(infd, pass, maxlen, &realsize, timeout);
        else
-               failed = untimed_read(infd, pass, maxlen);
+               failed = untimed_read(infd, pass, maxlen, &realsize);
        tcsetattr(infd, TCSAFLUSH, &orig);
-
-out_err:
+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;
@@ -171,8 +209,7 @@ out_err:
 
 static int crypt_get_key_tty(const char *prompt,
                             char **key, size_t *key_size,
-                            int timeout, int verify,
-                            struct crypt_device *cd)
+                            int timeout, int verify)
 {
        int key_size_max = DEFAULT_PASSPHRASE_SIZE_MAX;
        int r = -EINVAL;
@@ -191,35 +228,35 @@ static int crypt_get_key_tty(const char *prompt,
 
        if (interactive_pass(prompt, pass, key_size_max, timeout)) {
                log_err(_("Error reading passphrase from terminal."));
-               goto out_err;
+               goto out;
        }
-       pass[key_size_max] = '\0';
 
        if (verify) {
-               pass_verify = crypt_safe_alloc(key_size_max);
+               pass_verify = crypt_safe_alloc(key_size_max + 1);
                if (!pass_verify) {
                        log_err(_("Out of memory while reading passphrase."));
                        r = -ENOMEM;
-                       goto out_err;
+                       goto out;
                }
 
                if (interactive_pass(_("Verify passphrase: "),
                    pass_verify, key_size_max, timeout)) {
                        log_err(_("Error reading passphrase from terminal."));
-                       goto out_err;
+                       goto out;
                }
 
                if (strncmp(pass, pass_verify, key_size_max)) {
                        log_err(_("Passphrases do not match."));
                        r = -EPERM;
-                       goto out_err;
+                       goto out;
                }
        }
 
        *key = pass;
+       /* coverity[string_null] (crypt_safe_alloc wipes string with additional \0) */
        *key_size = strlen(pass);
        r = 0;
-out_err:
+out:
        crypt_safe_free(pass_verify);
        if (r)
                crypt_safe_free(pass);
@@ -249,14 +286,18 @@ int tools_get_key(const char *prompt,
                        if (keyfile_offset) {
                                log_err(_("Cannot use offset with terminal input."));
                        } else {
+                               r = 0;
                                if (!prompt && !crypt_get_device_name(cd))
-                                       snprintf(tmp, sizeof(tmp), _("Enter passphrase: "));
+                                       r = snprintf(tmp, sizeof(tmp), _("Enter passphrase: "));
                                else if (!prompt) {
                                        backing_file = crypt_loop_backing_file(crypt_get_device_name(cd));
-                                       snprintf(tmp, sizeof(tmp), _("Enter passphrase for %s: "), 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);
                                }
-                               r = crypt_get_key_tty(prompt ?: tmp, key, key_size, timeout, verify, cd);
+                               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.");
@@ -275,8 +316,8 @@ int tools_get_key(const char *prompt,
                set_int_block(1);
 
        /* Check pwquality for password (not keyfile) */
-       if (pwquality && !opt_force_password && !key_file && !r)
-               r = tools_check_pwquality(*key);
+       if (pwquality && !key_file && !r)
+               r = tools_check_password(*key);
 
        return r;
 }
@@ -288,52 +329,3 @@ void tools_passphrase_msg(int r)
        else if (r == -ENOENT)
                log_err(_("No usable keyslot is available."));
 }
-
-int tools_read_mk(const char *file, char **key, int keysize)
-{
-       int fd;
-
-       if (!keysize || !key)
-               return -EINVAL;
-
-       *key = crypt_safe_alloc(keysize);
-       if (!*key)
-               return -ENOMEM;
-
-       fd = open(file, O_RDONLY);
-       if (fd == -1) {
-               log_err(_("Cannot read keyfile %s."), file);
-               goto fail;
-       }
-
-       if (read_buffer(fd, *key, keysize) != keysize) {
-               log_err(_("Cannot read %d bytes from keyfile %s."), keysize, file);
-               close(fd);
-               goto fail;
-       }
-       close(fd);
-       return 0;
-fail:
-       crypt_safe_free(*key);
-       *key = NULL;
-       return -EINVAL;
-}
-
-int tools_write_mk(const char *file, const char *key, int keysize)
-{
-       int fd, r = -EINVAL;
-
-       fd = open(file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR);
-       if (fd < 0) {
-               log_err(_("Cannot open keyfile %s for write."), file);
-               return r;
-       }
-
-       if (write_buffer(fd, key, keysize) == keysize)
-               r = 0;
-       else
-               log_err(_("Cannot write to keyfile %s."), file);
-
-       close(fd);
-       return r;
-}