]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/cryptsetup/cryptsetup.c
cryptsetup: accept both "read-only" and "readonly" spellings
[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") || streq(option, "nofail"))
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") || streq(option, "read-only"))
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 if (opt_hash) {
346 /* plain isn't a real hash type. it just means "use no hash" */
347 if (!streq(opt_hash, "plain"))
348 hash = opt_hash;
349 } else
350 hash = "ripemd160";
351
352 if (opt_cipher) {
353 size_t l;
354
355 l = strcspn(opt_cipher, "-");
356
357 if (!(truncated_cipher = strndup(opt_cipher, l))) {
358 log_oom();
359 goto finish;
360 }
361
362 cipher = truncated_cipher;
363 cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
364 } else {
365 cipher = "aes";
366 cipher_mode = "cbc-essiv:sha256";
367 }
368
369 for (try = 0; try < opt_tries; try++) {
370 bool pass_volume_key = false;
371
372 strv_free(passwords);
373 passwords = NULL;
374
375 if (!key_file) {
376 char *text;
377 char **p;
378
379 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
380 log_oom();
381 goto finish;
382 }
383
384 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
385 free(text);
386
387 if (k < 0) {
388 log_error("Failed to query password: %s", strerror(-k));
389 goto finish;
390 }
391
392 if (opt_verify) {
393 char **passwords2 = NULL;
394
395 assert(strv_length(passwords) == 1);
396
397 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
398 log_oom();
399 goto finish;
400 }
401
402 k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
403 free(text);
404
405 if (k < 0) {
406 log_error("Failed to query verification password: %s", strerror(-k));
407 goto finish;
408 }
409
410 assert(strv_length(passwords2) == 1);
411
412 if (!streq(passwords[0], passwords2[0])) {
413 log_warning("Passwords did not match, retrying.");
414 strv_free(passwords2);
415 continue;
416 }
417
418 strv_free(passwords2);
419 }
420
421 strv_uniq(passwords);
422
423 STRV_FOREACH(p, passwords) {
424 char *c;
425
426 if (strlen(*p)+1 >= opt_key_size)
427 continue;
428
429 /* Pad password if necessary */
430 if (!(c = new(char, opt_key_size))) {
431 log_oom();
432 goto finish;
433 }
434
435 strncpy(c, *p, opt_key_size);
436 free(*p);
437 *p = c;
438 }
439 }
440
441 k = 0;
442
443 if (!opt_type || streq(opt_type, CRYPT_LUKS1))
444 k = crypt_load(cd, CRYPT_LUKS1, NULL);
445
446 if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
447 struct crypt_params_plain params;
448
449 zero(params);
450 params.hash = hash;
451
452 /* for CRYPT_PLAIN limit reads
453 * from keyfile to key length, and
454 * ignore keyfile-size */
455 opt_keyfile_size = opt_key_size / 8;
456
457 /* In contrast to what the name
458 * crypt_setup() might suggest this
459 * doesn't actually format anything,
460 * it just configures encryption
461 * parameters when used for plain
462 * mode. */
463 k = crypt_format(cd, CRYPT_PLAIN,
464 cipher,
465 cipher_mode,
466 NULL,
467 NULL,
468 opt_keyfile_size,
469 &params);
470
471 /* hash == NULL implies the user passed "plain" */
472 pass_volume_key = (hash == NULL);
473 }
474
475 if (k < 0) {
476 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
477 goto finish;
478 }
479
480 log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
481 crypt_get_cipher(cd),
482 crypt_get_cipher_mode(cd),
483 crypt_get_volume_key_size(cd)*8,
484 argv[3]);
485
486 if (key_file)
487 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
488 opt_keyfile_offset, flags);
489 else {
490 char **p;
491
492 STRV_FOREACH(p, passwords) {
493
494 if (pass_volume_key)
495 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
496 else
497 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
498
499 if (k >= 0)
500 break;
501 }
502 }
503
504 if (k >= 0)
505 break;
506
507 if (k != -EPERM) {
508 log_error("Failed to activate: %s", strerror(-k));
509 goto finish;
510 }
511
512 log_warning("Invalid passphrase.");
513 }
514
515 if (try >= opt_tries) {
516 log_error("Too many attempts.");
517 r = EXIT_FAILURE;
518 goto finish;
519 }
520
521 } else if (streq(argv[1], "detach")) {
522 int k;
523
524 if ((k = crypt_init_by_name(&cd, argv[2]))) {
525 log_error("crypt_init() failed: %s", strerror(-k));
526 goto finish;
527 }
528
529 crypt_set_log_callback(cd, log_glue, NULL);
530
531 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
532 log_error("Failed to deactivate: %s", strerror(-k));
533 goto finish;
534 }
535
536 } else {
537 log_error("Unknown verb %s.", argv[1]);
538 goto finish;
539 }
540
541 r = EXIT_SUCCESS;
542
543 finish:
544
545 if (cd)
546 crypt_free(cd);
547
548 free(opt_cipher);
549 free(opt_hash);
550
551 free(truncated_cipher);
552
553 strv_free(passwords);
554
555 free(description);
556 free(mount_point);
557 free(name_buffer);
558
559 return r;
560 }