]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
cryptsetup: set the timeout to 0 by default
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <errno.h>
24 #include <sys/mman.h>
25 #include <mntent.h>
26
27 #include <libcryptsetup.h>
28 #include <libudev.h>
29
30 #include "log.h"
31 #include "util.h"
32 #include "path-util.h"
33 #include "strv.h"
34 #include "ask-password-api.h"
35 #include "def.h"
36
37 static const char *opt_type = NULL; /* LUKS1 or PLAIN */
38 static char *opt_cipher = NULL;
39 static unsigned opt_key_size = 0;
40 static unsigned opt_keyfile_size = 0;
41 static unsigned opt_keyfile_offset = 0;
42 static char *opt_hash = NULL;
43 static unsigned opt_tries = 0;
44 static bool opt_readonly = false;
45 static bool opt_verify = false;
46 static bool opt_discards = false;
47 static usec_t opt_timeout = 0;
48
49 /* Options Debian's crypttab knows we don't:
50
51 offset=
52 skip=
53 precheck=
54 check=
55 checkargs=
56 noearly=
57 loud=
58 keyscript=
59 */
60
61 static int parse_one_option(const char *option) {
62 assert(option);
63
64 /* Handled outside of this tool */
65 if (streq(option, "noauto") || streq(option, "nofail"))
66 return 0;
67
68 if (startswith(option, "cipher=")) {
69 char *t;
70
71 t = strdup(option+7);
72 if (!t)
73 return -ENOMEM;
74
75 free(opt_cipher);
76 opt_cipher = t;
77
78 } else if (startswith(option, "size=")) {
79
80 if (safe_atou(option+5, &opt_key_size) < 0) {
81 log_error("size= parse failure, ignoring.");
82 return 0;
83 }
84
85 } else if (startswith(option, "keyfile-size=")) {
86
87 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
88 log_error("keyfile-size= parse failure, ignoring.");
89 return 0;
90 }
91
92 } else if (startswith(option, "keyfile-offset=")) {
93
94 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
95 log_error("keyfile-offset= parse failure, ignoring.");
96 return 0;
97 }
98
99 } else if (startswith(option, "hash=")) {
100 char *t;
101
102 t = strdup(option+5);
103 if (!t)
104 return -ENOMEM;
105
106 free(opt_hash);
107 opt_hash = t;
108
109 } else if (startswith(option, "tries=")) {
110
111 if (safe_atou(option+6, &opt_tries) < 0) {
112 log_error("tries= parse failure, ignoring.");
113 return 0;
114 }
115
116 } else if (streq(option, "readonly") || streq(option, "read-only"))
117 opt_readonly = true;
118 else if (streq(option, "verify"))
119 opt_verify = true;
120 else if (streq(option, "allow-discards"))
121 opt_discards = true;
122 else if (streq(option, "luks"))
123 opt_type = CRYPT_LUKS1;
124 else if (streq(option, "plain") ||
125 streq(option, "swap") ||
126 streq(option, "tmp"))
127 opt_type = CRYPT_PLAIN;
128 else if (startswith(option, "timeout=")) {
129
130 if (parse_sec(option+8, &opt_timeout) < 0) {
131 log_error("timeout= parse failure, ignoring.");
132 return 0;
133 }
134
135 } else if (!streq(option, "none"))
136 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
137
138 return 0;
139 }
140
141 static int parse_options(const char *options) {
142 char *state, *w;
143 size_t l;
144 int r;
145
146 assert(options);
147
148 FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
149 _cleanup_free_ char *o;
150
151 o = strndup(w, l);
152 if (!o)
153 return -ENOMEM;
154 r = parse_one_option(o);
155 if (r < 0)
156 return r;
157 }
158
159 return 0;
160 }
161
162 static void log_glue(int level, const char *msg, void *usrptr) {
163 log_debug("%s", msg);
164 }
165
166 static char *disk_description(const char *path) {
167
168 static const char name_fields[] = {
169 "ID_PART_ENTRY_NAME\0"
170 "DM_NAME\0"
171 "ID_MODEL_FROM_DATABASE\0"
172 "ID_MODEL\0"
173 };
174
175 struct udev *udev = NULL;
176 struct udev_device *device = NULL;
177 struct stat st;
178 char *description = NULL;
179 const char *i;
180
181 assert(path);
182
183 if (stat(path, &st) < 0)
184 return NULL;
185
186 if (!S_ISBLK(st.st_mode))
187 return NULL;
188
189 udev = udev_new();
190 if (!udev)
191 return NULL;
192
193 device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
194 if (!device)
195 goto finish;
196
197 NULSTR_FOREACH(i, name_fields) {
198 const char *name;
199
200 name = udev_device_get_property_value(device, i);
201 if (!isempty(name)) {
202 description = strdup(name);
203 break;
204 }
205 }
206
207 finish:
208 if (device)
209 udev_device_unref(device);
210
211 if (udev)
212 udev_unref(udev);
213
214 return description;
215 }
216
217 static char *disk_mount_point(const char *label) {
218 char *mp = NULL, *device = NULL;
219 FILE *f = NULL;
220 struct mntent *m;
221
222 /* Yeah, we don't support native systemd unit files here for now */
223
224 if (asprintf(&device, "/dev/mapper/%s", label) < 0)
225 goto finish;
226
227 f = setmntent("/etc/fstab", "r");
228 if (!f)
229 goto finish;
230
231 while ((m = getmntent(f)))
232 if (path_equal(m->mnt_fsname, device)) {
233 mp = strdup(m->mnt_dir);
234 break;
235 }
236
237 finish:
238 if (f)
239 endmntent(f);
240
241 free(device);
242
243 return mp;
244 }
245
246 static int help(void) {
247
248 printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
249 "%s detach VOLUME\n\n"
250 "Attaches or detaches an encrypted block device.\n",
251 program_invocation_short_name,
252 program_invocation_short_name);
253
254 return 0;
255 }
256
257 int main(int argc, char *argv[]) {
258 int r = EXIT_FAILURE;
259 struct crypt_device *cd = NULL;
260 char **passwords = NULL, *truncated_cipher = NULL;
261 const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
262 char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
263
264 if (argc <= 1) {
265 help();
266 return EXIT_SUCCESS;
267 }
268
269 if (argc < 3) {
270 log_error("This program requires at least two arguments.");
271 return EXIT_FAILURE;
272 }
273
274 log_set_target(LOG_TARGET_AUTO);
275 log_parse_environment();
276 log_open();
277
278 umask(0022);
279
280 if (streq(argv[1], "attach")) {
281 uint32_t flags = 0;
282 int k;
283 unsigned try;
284 const char *key_file = NULL;
285 usec_t until;
286 crypt_status_info status;
287
288 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
289
290 if (argc < 4) {
291 log_error("attach requires at least two arguments.");
292 goto finish;
293 }
294
295 if (argc >= 5 &&
296 argv[4][0] &&
297 !streq(argv[4], "-") &&
298 !streq(argv[4], "none")) {
299
300 if (!path_is_absolute(argv[4]))
301 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
302 else
303 key_file = argv[4];
304 }
305
306 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
307 if (parse_options(argv[5]) < 0)
308 goto finish;
309 }
310
311 /* A delicious drop of snake oil */
312 mlockall(MCL_FUTURE);
313
314 description = disk_description(argv[3]);
315 mount_point = disk_mount_point(argv[2]);
316
317 if (description && streq(argv[2], description)) {
318 /* If the description string is simply the
319 * volume name, then let's not show this
320 * twice */
321 free(description);
322 description = NULL;
323 }
324
325 if (mount_point && description)
326 asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
327 else if (mount_point)
328 asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
329 else if (description)
330 asprintf(&name_buffer, "%s (%s)", description, argv[2]);
331
332 name = name_buffer ? name_buffer : argv[2];
333
334 k = crypt_init(&cd, argv[3]);
335 if (k) {
336 log_error("crypt_init() failed: %s", strerror(-k));
337 goto finish;
338 }
339
340 crypt_set_log_callback(cd, log_glue, NULL);
341
342 status = crypt_status(cd, argv[2]);
343 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
344 log_info("Volume %s already active.", argv[2]);
345 r = EXIT_SUCCESS;
346 goto finish;
347 }
348
349 if (opt_readonly)
350 flags |= CRYPT_ACTIVATE_READONLY;
351
352 if (opt_discards)
353 flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
354
355 if (opt_timeout > 0)
356 until = now(CLOCK_MONOTONIC) + opt_timeout;
357 else
358 until = 0;
359
360 opt_tries = opt_tries > 0 ? opt_tries : 3;
361 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
362 if (opt_hash) {
363 /* plain isn't a real hash type. it just means "use no hash" */
364 if (!streq(opt_hash, "plain"))
365 hash = opt_hash;
366 } else
367 hash = "ripemd160";
368
369 if (opt_cipher) {
370 size_t l;
371
372 l = strcspn(opt_cipher, "-");
373 truncated_cipher = strndup(opt_cipher, l);
374
375 if (!truncated_cipher) {
376 log_oom();
377 goto finish;
378 }
379
380 cipher = truncated_cipher;
381 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
382 } else {
383 cipher = "aes";
384 cipher_mode = "cbc-essiv:sha256";
385 }
386
387 for (try = 0; try < opt_tries; try++) {
388 bool pass_volume_key = false;
389
390 strv_free(passwords);
391 passwords = NULL;
392
393 if (!key_file) {
394 char *text, **p;
395
396 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
397 log_oom();
398 goto finish;
399 }
400
401 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
402 free(text);
403
404 if (k < 0) {
405 log_error("Failed to query password: %s", strerror(-k));
406 goto finish;
407 }
408
409 if (opt_verify) {
410 char **passwords2 = NULL;
411
412 assert(strv_length(passwords) == 1);
413
414 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
415 log_oom();
416 goto finish;
417 }
418
419 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
420 free(text);
421
422 if (k < 0) {
423 log_error("Failed to query verification password: %s", strerror(-k));
424 goto finish;
425 }
426
427 assert(strv_length(passwords2) == 1);
428
429 if (!streq(passwords[0], passwords2[0])) {
430 log_warning("Passwords did not match, retrying.");
431 strv_free(passwords2);
432 continue;
433 }
434
435 strv_free(passwords2);
436 }
437
438 strv_uniq(passwords);
439
440 STRV_FOREACH(p, passwords) {
441 char *c;
442
443 if (strlen(*p)+1 >= opt_key_size)
444 continue;
445
446 /* Pad password if necessary */
447 if (!(c = new(char, opt_key_size))) {
448 log_oom();
449 goto finish;
450 }
451
452 strncpy(c, *p, opt_key_size);
453 free(*p);
454 *p = c;
455 }
456 }
457
458 k = 0;
459
460 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
461 k = crypt_load(cd, CRYPT_LUKS1, NULL);
462
463 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
464 struct crypt_params_plain params = { .hash = hash };
465
466 /* for CRYPT_PLAIN limit reads
467 * from keyfile to key length, and
468 * ignore keyfile-size */
469 opt_keyfile_size = opt_key_size / 8;
470
471 /* In contrast to what the name
472 * crypt_setup() might suggest this
473 * doesn't actually format anything,
474 * it just configures encryption
475 * parameters when used for plain
476 * mode. */
477 k = crypt_format(cd, CRYPT_PLAIN,
478 cipher,
479 cipher_mode,
480 NULL,
481 NULL,
482 opt_keyfile_size,
483 &params);
484
485 /* hash == NULL implies the user passed "plain" */
486 pass_volume_key = (hash == NULL);
487 }
488
489 if (k < 0) {
490 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
491 goto finish;
492 }
493
494 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
495 crypt_get_cipher(cd),
496 crypt_get_cipher_mode(cd),
497 crypt_get_volume_key_size(cd)*8,
498 argv[3]);
499
500 if (key_file)
501 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
502 opt_keyfile_offset, flags);
503 else {
504 char **p;
505
506 STRV_FOREACH(p, passwords) {
507
508 if (pass_volume_key)
509 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
510 else
511 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
512
513 if (k >= 0)
514 break;
515 }
516 }
517
518 if (k >= 0)
519 break;
520
521 if (k != -EPERM) {
522 log_error("Failed to activate: %s", strerror(-k));
523 goto finish;
524 }
525
526 log_warning("Invalid passphrase.");
527 }
528
529 if (try >= opt_tries) {
530 log_error("Too many attempts.");
531 r = EXIT_FAILURE;
532 goto finish;
533 }
534
535 } else if (streq(argv[1], "detach")) {
536 int k;
537
538 k = crypt_init_by_name(&cd, argv[2]);
539 if (k) {
540 log_error("crypt_init() failed: %s", strerror(-k));
541 goto finish;
542 }
543
544 crypt_set_log_callback(cd, log_glue, NULL);
545
546 k = crypt_deactivate(cd, argv[2]);
547 if (k < 0) {
548 log_error("Failed to deactivate: %s", strerror(-k));
549 goto finish;
550 }
551
552 } else {
553 log_error("Unknown verb %s.", argv[1]);
554 goto finish;
555 }
556
557 r = EXIT_SUCCESS;
558
559 finish:
560
561 if (cd)
562 crypt_free(cd);
563
564 free(opt_cipher);
565 free(opt_hash);
566
567 free(truncated_cipher);
568
569 strv_free(passwords);
570
571 free(description);
572 free(mount_point);
573 free(name_buffer);
574
575 return r;
576 }