Merge branch 'upstream' into tizen
[platform/upstream/cryptsetup.git] / src / utils_password.c
1 /*
2  * Password quality check wrapper
3  *
4  * Copyright (C) 2012-2023 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2012-2023 Milan Broz
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "cryptsetup.h"
23 #include <termios.h>
24
25 #if defined ENABLE_PWQUALITY
26 #include <pwquality.h>
27
28 static int tools_check_pwquality(const char *password)
29 {
30         int r;
31         void *auxerror;
32         pwquality_settings_t *pwq;
33
34         log_dbg("Checking new password using default pwquality settings.");
35         pwq = pwquality_default_settings();
36         if (!pwq)
37                 return -EINVAL;
38
39         r = pwquality_read_config(pwq, NULL, &auxerror);
40         if (r) {
41                 log_err(_("Cannot check password quality: %s"),
42                         pwquality_strerror(NULL, 0, r, auxerror));
43                 pwquality_free_settings(pwq);
44                 return -EINVAL;
45         }
46
47         r = pwquality_check(pwq, password, NULL, NULL, &auxerror);
48         if (r < 0) {
49                 log_err(_("Password quality check failed:\n %s"),
50                         pwquality_strerror(NULL, 0, r, auxerror));
51                 r = -EPERM;
52         } else
53                 r = 0;
54
55         pwquality_free_settings(pwq);
56         return r;
57 }
58 #elif defined ENABLE_PASSWDQC
59 #include <passwdqc.h>
60
61 static int tools_check_passwdqc(const char *password)
62 {
63         passwdqc_params_t params;
64         char *parse_reason = NULL;
65         const char *check_reason;
66         const char *config = PASSWDQC_CONFIG_FILE;
67         int r = -EINVAL;
68
69         passwdqc_params_reset(&params);
70
71         if (*config && passwdqc_params_load(&params, &parse_reason, config)) {
72                 log_err(_("Cannot check password quality: %s"),
73                         (parse_reason ? parse_reason : "Out of memory"));
74                 goto out;
75         }
76
77         check_reason = passwdqc_check(&params.qc, password, NULL, NULL);
78         if (check_reason) {
79                 log_err(_("Password quality check failed: Bad passphrase (%s)"),
80                         check_reason);
81                 r = -EPERM;
82         } else
83                 r = 0;
84 out:
85 #if HAVE_PASSWDQC_PARAMS_FREE
86         passwdqc_params_free(&params);
87 #endif
88         free(parse_reason);
89         return r;
90 }
91 #endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */
92
93 /* coverity[ +tainted_string_sanitize_content : arg-0 ] */
94 static int tools_check_password(const char *password)
95 {
96 #if defined ENABLE_PWQUALITY
97         return tools_check_pwquality(password);
98 #elif defined ENABLE_PASSWDQC
99         return tools_check_passwdqc(password);
100 #else
101         return 0;
102 #endif
103 }
104
105 /* Password reading helpers */
106
107 /* coverity[ -taint_source : arg-1 ] */
108 static ssize_t read_tty_eol(int fd, char *pass, size_t maxlen)
109 {
110         bool eol = false;
111         size_t read_size = 0;
112         ssize_t r;
113
114         do {
115                 r = read(fd, pass, maxlen - read_size);
116                 if ((r == -1 && errno != EINTR) || quit)
117                         return -1;
118                 if (r >= 0) {
119                         if (!r || pass[r-1] == '\n')
120                                 eol = true;
121                         read_size += (size_t)r;
122                         pass = pass + r;
123                 }
124         } while (!eol && read_size != maxlen);
125
126         return (ssize_t)read_size;
127 }
128
129 /* The pass buffer is zeroed and has trailing \0 already " */
130 static int untimed_read(int fd, char *pass, size_t maxlen, size_t *realsize)
131 {
132         ssize_t i;
133
134         i = read_tty_eol(fd, pass, maxlen);
135         if (i > 0) {
136                 if (pass[i-1] == '\n') {
137                         pass[i-1] = '\0';
138                         *realsize = i - 1;
139                 } else
140                         *realsize = i;
141                 i = 0;
142         } else if (i == 0) /* empty input */
143                 i = -1;
144
145         return i;
146 }
147
148 static int timed_read(int fd, char *pass, size_t maxlen, size_t *realsize, long timeout)
149 {
150         struct timeval t;
151         fd_set fds = {}; /* Just to avoid scan-build false report for FD_SET */
152         int failed = -1;
153
154         FD_ZERO(&fds);
155         FD_SET(fd, &fds);
156         t.tv_sec = timeout;
157         t.tv_usec = 0;
158
159         if (select(fd+1, &fds, NULL, NULL, &t) > 0)
160                 failed = untimed_read(fd, pass, maxlen, realsize);
161
162         return failed;
163 }
164
165 static int interactive_pass(const char *prompt, char *pass, size_t maxlen,
166                 long timeout)
167 {
168         struct termios orig, tmp;
169         int failed = -1;
170         int infd, outfd;
171         size_t realsize = 0;
172
173         if (maxlen < 1)
174                 return failed;
175
176         /* Read and write to /dev/tty if available */
177         infd = open("/dev/tty", O_RDWR);
178         if (infd == -1) {
179                 infd = STDIN_FILENO;
180                 outfd = STDERR_FILENO;
181         } else
182                 outfd = infd;
183
184         if (tcgetattr(infd, &orig))
185                 goto out;
186
187         memcpy(&tmp, &orig, sizeof(tmp));
188         tmp.c_lflag &= ~ECHO;
189
190         if (prompt && write(outfd, prompt, strlen(prompt)) < 0)
191                 goto out;
192
193         tcsetattr(infd, TCSAFLUSH, &tmp);
194         if (timeout)
195                 failed = timed_read(infd, pass, maxlen, &realsize, timeout);
196         else
197                 failed = untimed_read(infd, pass, maxlen, &realsize);
198         tcsetattr(infd, TCSAFLUSH, &orig);
199 out:
200         if (!failed && write(outfd, "\n", 1)) {};
201
202         if (realsize == maxlen)
203                 log_dbg("Read stopped at maximal interactive input length, passphrase can be trimmed.");
204
205         if (infd != STDIN_FILENO)
206                 close(infd);
207         return failed;
208 }
209
210 static int crypt_get_key_tty(const char *prompt,
211                              char **key, size_t *key_size,
212                              int timeout, int verify)
213 {
214         int key_size_max = DEFAULT_PASSPHRASE_SIZE_MAX;
215         int r = -EINVAL;
216         char *pass = NULL, *pass_verify = NULL;
217
218         *key = NULL;
219         *key_size = 0;
220
221         log_dbg("Interactive passphrase entry requested.");
222
223         pass = crypt_safe_alloc(key_size_max + 1);
224         if (!pass) {
225                 log_err( _("Out of memory while reading passphrase."));
226                 return -ENOMEM;
227         }
228
229         if (interactive_pass(prompt, pass, key_size_max, timeout)) {
230                 log_err(_("Error reading passphrase from terminal."));
231                 goto out;
232         }
233
234         if (verify) {
235                 pass_verify = crypt_safe_alloc(key_size_max + 1);
236                 if (!pass_verify) {
237                         log_err(_("Out of memory while reading passphrase."));
238                         r = -ENOMEM;
239                         goto out;
240                 }
241
242                 if (interactive_pass(_("Verify passphrase: "),
243                     pass_verify, key_size_max, timeout)) {
244                         log_err(_("Error reading passphrase from terminal."));
245                         goto out;
246                 }
247
248                 if (strncmp(pass, pass_verify, key_size_max)) {
249                         log_err(_("Passphrases do not match."));
250                         r = -EPERM;
251                         goto out;
252                 }
253         }
254
255         *key = pass;
256         /* coverity[string_null] (crypt_safe_alloc wipes string with additional \0) */
257         *key_size = strlen(pass);
258         r = 0;
259 out:
260         crypt_safe_free(pass_verify);
261         if (r)
262                 crypt_safe_free(pass);
263         return r;
264 }
265
266 /*
267  * Note: --key-file=- is interpreted as a read from a binary file (stdin)
268  * key_size_max == 0 means detect maximum according to input type (tty/file)
269  */
270 int tools_get_key(const char *prompt,
271                   char **key, size_t *key_size,
272                   uint64_t keyfile_offset, size_t keyfile_size_max,
273                   const char *key_file,
274                   int timeout, int verify, int pwquality,
275                   struct crypt_device *cd)
276 {
277         char tmp[PATH_MAX], *backing_file;
278         int r = -EINVAL, block;
279
280         block = tools_signals_blocked();
281         if (block)
282                 set_int_block(0);
283
284         if (tools_is_stdin(key_file)) {
285                 if (isatty(STDIN_FILENO)) {
286                         if (keyfile_offset) {
287                                 log_err(_("Cannot use offset with terminal input."));
288                         } else {
289                                 r = 0;
290                                 if (!prompt && !crypt_get_device_name(cd))
291                                         r = snprintf(tmp, sizeof(tmp), _("Enter passphrase: "));
292                                 else if (!prompt) {
293                                         backing_file = crypt_loop_backing_file(crypt_get_device_name(cd));
294                                         r = snprintf(tmp, sizeof(tmp), _("Enter passphrase for %s: "), backing_file ?: crypt_get_device_name(cd));
295                                         free(backing_file);
296                                 }
297                                 if (r >= 0)
298                                         r = crypt_get_key_tty(prompt ?: tmp, key, key_size, timeout, verify);
299                                 else
300                                         r = -EINVAL;
301                         }
302                 } else {
303                         log_dbg("STDIN descriptor passphrase entry requested.");
304                         /* No keyfile means STDIN with EOL handling (\n will end input)). */
305                         r = crypt_keyfile_device_read(cd, NULL, key, key_size,
306                                         keyfile_offset, keyfile_size_max,
307                                         key_file ? 0 : CRYPT_KEYFILE_STOP_EOL);
308                 }
309         } else {
310                 log_dbg("File descriptor passphrase entry requested.");
311                 r = crypt_keyfile_device_read(cd, key_file, key, key_size,
312                                               keyfile_offset, keyfile_size_max, 0);
313         }
314
315         if (block && !quit)
316                 set_int_block(1);
317
318         /* Check pwquality for password (not keyfile) */
319         if (pwquality && !key_file && !r)
320                 r = tools_check_password(*key);
321
322         return r;
323 }
324
325 void tools_passphrase_msg(int r)
326 {
327         if (r == -EPERM)
328                 log_err(_("No key available with this passphrase."));
329         else if (r == -ENOENT)
330                 log_err(_("No usable keyslot is available."));
331 }