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