chiark / gitweb /
cryptsetup: Add tcrypt support
[elogind.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 "fileio.h"
31 #include "log.h"
32 #include "util.h"
33 #include "path-util.h"
34 #include "strv.h"
35 #include "ask-password-api.h"
36 #include "def.h"
37
38 static const char *opt_type = NULL; /* CRYPT_LUKS1, CRYPT_TCRYPT or CRYPT_PLAIN */
39 static char *opt_cipher = NULL;
40 static unsigned opt_key_size = 0;
41 static unsigned opt_keyfile_size = 0;
42 static unsigned opt_keyfile_offset = 0;
43 static char *opt_hash = NULL;
44 static unsigned opt_tries = 0;
45 static bool opt_readonly = false;
46 static bool opt_verify = false;
47 static bool opt_discards = false;
48 static bool opt_tcrypt_hidden = false;
49 static bool opt_tcrypt_system = false;
50 static char **opt_tcrypt_keyfiles = NULL;
51 static usec_t opt_timeout = 0;
52
53 /* Options Debian's crypttab knows we don't:
54
55     offset=
56     skip=
57     precheck=
58     check=
59     checkargs=
60     noearly=
61     loud=
62     keyscript=
63 */
64
65 static int parse_one_option(const char *option) {
66         assert(option);
67
68         /* Handled outside of this tool */
69         if (streq(option, "noauto") || streq(option, "nofail"))
70                 return 0;
71
72         if (startswith(option, "cipher=")) {
73                 char *t;
74
75                 t = strdup(option+7);
76                 if (!t)
77                         return -ENOMEM;
78
79                 free(opt_cipher);
80                 opt_cipher = t;
81
82         } else if (startswith(option, "size=")) {
83
84                 if (safe_atou(option+5, &opt_key_size) < 0) {
85                         log_error("size= parse failure, ignoring.");
86                         return 0;
87                 }
88
89         } else if (startswith(option, "tcrypt-keyfile=")) {
90
91                 opt_type = CRYPT_TCRYPT;
92                 if (path_is_absolute(option+15))
93                         opt_tcrypt_keyfiles = strv_append(opt_tcrypt_keyfiles, strdup(option+15));
94                 else
95                         log_error("Key file path '%s' is not absolute. Ignoring.", option+15);
96
97         } else if (startswith(option, "keyfile-size=")) {
98
99                 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
100                         log_error("keyfile-size= parse failure, ignoring.");
101                         return 0;
102                 }
103
104         } else if (startswith(option, "keyfile-offset=")) {
105
106                 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
107                         log_error("keyfile-offset= parse failure, ignoring.");
108                         return 0;
109                 }
110
111         } else if (startswith(option, "hash=")) {
112                 char *t;
113
114                 t = strdup(option+5);
115                 if (!t)
116                         return -ENOMEM;
117
118                 free(opt_hash);
119                 opt_hash = t;
120
121         } else if (startswith(option, "tries=")) {
122
123                 if (safe_atou(option+6, &opt_tries) < 0) {
124                         log_error("tries= parse failure, ignoring.");
125                         return 0;
126                 }
127
128         } else if (streq(option, "readonly") || streq(option, "read-only"))
129                 opt_readonly = true;
130         else if (streq(option, "verify"))
131                 opt_verify = true;
132         else if (streq(option, "allow-discards"))
133                 opt_discards = true;
134         else if (streq(option, "luks"))
135                 opt_type = CRYPT_LUKS1;
136         else if (streq(option, "tcrypt"))
137                 opt_type = CRYPT_TCRYPT;
138         else if (streq(option, "tcrypt-hidden")) {
139                 opt_type = CRYPT_TCRYPT;
140                 opt_tcrypt_hidden = true;
141         } else if (streq(option, "tcrypt-system")) {
142                 opt_type = CRYPT_TCRYPT;
143                 opt_tcrypt_system = true;
144         } else if (streq(option, "plain") ||
145                  streq(option, "swap") ||
146                  streq(option, "tmp"))
147                 opt_type = CRYPT_PLAIN;
148         else if (startswith(option, "timeout=")) {
149
150                 if (parse_sec(option+8, &opt_timeout) < 0) {
151                         log_error("timeout= parse failure, ignoring.");
152                         return 0;
153                 }
154
155         } else if (!streq(option, "none"))
156                 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
157
158         return 0;
159 }
160
161 static int parse_options(const char *options) {
162         char *state, *w;
163         size_t l;
164         int r;
165
166         assert(options);
167
168         FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
169                 _cleanup_free_ char *o;
170
171                 o = strndup(w, l);
172                 if (!o)
173                         return -ENOMEM;
174                 r = parse_one_option(o);
175                 if (r < 0)
176                         return r;
177         }
178
179         return 0;
180 }
181
182 static void log_glue(int level, const char *msg, void *usrptr) {
183         log_debug("%s", msg);
184 }
185
186 static char *disk_description(const char *path) {
187
188         static const char name_fields[] = {
189                 "ID_PART_ENTRY_NAME\0"
190                 "DM_NAME\0"
191                 "ID_MODEL_FROM_DATABASE\0"
192                 "ID_MODEL\0"
193         };
194
195         struct udev *udev = NULL;
196         struct udev_device *device = NULL;
197         struct stat st;
198         char *description = NULL;
199         const char *i;
200
201         assert(path);
202
203         if (stat(path, &st) < 0)
204                 return NULL;
205
206         if (!S_ISBLK(st.st_mode))
207                 return NULL;
208
209         udev = udev_new();
210         if (!udev)
211                 return NULL;
212
213         device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
214         if (!device)
215                 goto finish;
216
217         NULSTR_FOREACH(i, name_fields) {
218                 const char *name;
219
220                 name = udev_device_get_property_value(device, i);
221                 if (!isempty(name)) {
222                         description = strdup(name);
223                         break;
224                 }
225         }
226
227 finish:
228         if (device)
229                 udev_device_unref(device);
230
231         if (udev)
232                 udev_unref(udev);
233
234         return description;
235 }
236
237 static char *disk_mount_point(const char *label) {
238         char *mp = NULL;
239         _cleanup_free_ char *device = NULL;
240         FILE *f = NULL;
241         struct mntent *m;
242
243         /* Yeah, we don't support native systemd unit files here for now */
244
245         if (asprintf(&device, "/dev/mapper/%s", label) < 0)
246                 goto finish;
247
248         f = setmntent("/etc/fstab", "r");
249         if (!f)
250                 goto finish;
251
252         while ((m = getmntent(f)))
253                 if (path_equal(m->mnt_fsname, device)) {
254                         mp = strdup(m->mnt_dir);
255                         break;
256                 }
257
258 finish:
259         if (f)
260                 endmntent(f);
261
262         return mp;
263 }
264
265 static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
266         int r;
267         char **p;
268         _cleanup_free_ char *text = NULL;
269
270         assert(name);
271         assert(passwords);
272
273         if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0)
274                 return log_oom();
275
276         r = ask_password_auto(text, "drive-harddisk", until, accept_cached, passwords);
277         if (r < 0) {
278                 log_error("Failed to query password: %s", strerror(-r));
279                 return r;
280         }
281
282         if (opt_verify) {
283                 _cleanup_strv_free_ char **passwords2 = NULL;
284
285                 assert(strv_length(*passwords) == 1);
286
287                 if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
288                         return log_oom();
289
290                 r = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
291                 if (r < 0) {
292                         log_error("Failed to query verification password: %s", strerror(-r));
293                         return r;
294                 }
295
296                 assert(strv_length(passwords2) == 1);
297
298                 if (!streq(*passwords[0], passwords2[0])) {
299                         log_warning("Passwords did not match, retrying.");
300                         return -EAGAIN;
301                 }
302         }
303
304         strv_uniq(*passwords);
305
306         STRV_FOREACH(p, *passwords) {
307                 char *c;
308
309                 if (strlen(*p)+1 >= opt_key_size)
310                         continue;
311
312                 /* Pad password if necessary */
313                 if (!(c = new(char, opt_key_size)))
314                         return log_oom();
315
316                 strncpy(c, *p, opt_key_size);
317                 free(*p);
318                 *p = c;
319         }
320
321         return 0;
322 }
323
324 static int attach_tcrypt(struct crypt_device *cd,
325                                 const char *name,
326                                 const char *key_file,
327                                 char **passwords,
328                                 uint32_t flags) {
329         int r = 0;
330         _cleanup_free_ char *passphrase = NULL;
331         struct crypt_params_tcrypt params = {
332                 .flags = CRYPT_TCRYPT_LEGACY_MODES,
333                 .keyfiles = (const char **)opt_tcrypt_keyfiles,
334                 .keyfiles_count = strv_length(opt_tcrypt_keyfiles)
335         };
336
337         assert(cd);
338         assert(name);
339         assert(key_file || passwords);
340
341         if (opt_tcrypt_hidden)
342                 params.flags |= CRYPT_TCRYPT_HIDDEN_HEADER;
343
344         if (opt_tcrypt_system)
345                 params.flags |= CRYPT_TCRYPT_SYSTEM_HEADER;
346
347         if (key_file) {
348                 r = read_one_line_file(key_file, &passphrase);
349                 if (r < 0) {
350                         log_error("Failed to read password file '%s': %s", key_file, strerror(-r));
351                         return -EAGAIN;
352                 }
353
354                 params.passphrase = passphrase;
355         } else
356                 params.passphrase = passwords[0];
357         params.passphrase_size = strlen(params.passphrase);
358
359         r = crypt_load(cd, CRYPT_TCRYPT, &params);
360         if (r < 0) {
361                 if (key_file && r == -EPERM) {
362                         log_error("Failed to activate using password file '%s'.", key_file);
363                         return -EAGAIN;
364                 }
365                 return r;
366         }
367
368         return crypt_activate_by_volume_key(cd, name, NULL, 0, flags);;
369 }
370
371 static int attach_luks_or_plain(struct crypt_device *cd,
372                                 const char *name,
373                                 const char *key_file,
374                                 char **passwords,
375                                 uint32_t flags) {
376         int r = 0;
377         bool pass_volume_key = false;
378
379         assert(cd);
380         assert(name);
381         assert(key_file || passwords);
382
383         if (!opt_type || streq(opt_type, CRYPT_LUKS1))
384                 r = crypt_load(cd, CRYPT_LUKS1, NULL);
385
386         if ((!opt_type && r < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
387                 struct crypt_params_plain params = {};
388                 const char *cipher, *cipher_mode;
389                 _cleanup_free_ char *truncated_cipher = NULL;
390
391                 if (opt_hash) {
392                         /* plain isn't a real hash type. it just means "use no hash" */
393                         if (!streq(opt_hash, "plain"))
394                                 params.hash = opt_hash;
395                 } else
396                         params.hash = "ripemd160";
397
398                 if (opt_cipher) {
399                         size_t l;
400
401                         l = strcspn(opt_cipher, "-");
402                         truncated_cipher = strndup(opt_cipher, l);
403                         if (!truncated_cipher)
404                                 return log_oom();
405
406                         cipher = truncated_cipher;
407                         cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
408                 } else {
409                         cipher = "aes";
410                         cipher_mode = "cbc-essiv:sha256";
411                 }
412
413                 /* for CRYPT_PLAIN limit reads
414                  * from keyfile to key length, and
415                  * ignore keyfile-size */
416                 opt_keyfile_size = opt_key_size / 8;
417
418                 /* In contrast to what the name
419                  * crypt_setup() might suggest this
420                  * doesn't actually format anything,
421                  * it just configures encryption
422                  * parameters when used for plain
423                  * mode. */
424                 r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode,
425                                  NULL, NULL, opt_keyfile_size, &params);
426
427                 /* hash == NULL implies the user passed "plain" */
428                 pass_volume_key = (params.hash == NULL);
429         }
430
431         if (r < 0) {
432                 log_error("Loading of cryptographic parameters failed: %s", strerror(-r));
433                 return r;
434         }
435
436         log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
437                  crypt_get_cipher(cd),
438                  crypt_get_cipher_mode(cd),
439                  crypt_get_volume_key_size(cd)*8,
440                  crypt_get_device_name(cd));
441
442         if (key_file) {
443                 r = crypt_activate_by_keyfile_offset(cd, name, CRYPT_ANY_SLOT,
444                                                      key_file, opt_keyfile_size,
445                                                      opt_keyfile_offset, flags);
446                 if (r < 0) {
447                         log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r));
448                         return -EAGAIN;
449                 }
450         } else {
451                 char **p;
452
453                 STRV_FOREACH(p, passwords) {
454                         if (pass_volume_key)
455                                 r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
456                         else
457                                 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, *p, strlen(*p), flags);
458
459                         if (r >= 0)
460                                 break;
461                 }
462         }
463
464         return r;
465 }
466
467 static int help(void) {
468
469         printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
470                "%s detach VOLUME\n\n"
471                "Attaches or detaches an encrypted block device.\n",
472                program_invocation_short_name,
473                program_invocation_short_name);
474
475         return 0;
476 }
477
478 int main(int argc, char *argv[]) {
479         int r = EXIT_FAILURE;
480         struct crypt_device *cd = NULL;
481
482         if (argc <= 1) {
483                 help();
484                 return EXIT_SUCCESS;
485         }
486
487         if (argc < 3) {
488                 log_error("This program requires at least two arguments.");
489                 return EXIT_FAILURE;
490         }
491
492         log_set_target(LOG_TARGET_AUTO);
493         log_parse_environment();
494         log_open();
495
496         umask(0022);
497
498         if (streq(argv[1], "attach")) {
499                 uint32_t flags = 0;
500                 int k;
501                 unsigned tries;
502                 usec_t until;
503                 crypt_status_info status;
504                 const char *key_file = NULL, *name = NULL;
505                 _cleanup_free_ char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
506
507                 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
508
509                 if (argc < 4) {
510                         log_error("attach requires at least two arguments.");
511                         goto finish;
512                 }
513
514                 if (argc >= 5 &&
515                     argv[4][0] &&
516                     !streq(argv[4], "-") &&
517                     !streq(argv[4], "none")) {
518
519                         if (!path_is_absolute(argv[4]))
520                                 log_error("Password file path '%s' is not absolute. Ignoring.", argv[4]);
521                         else
522                                 key_file = argv[4];
523                 }
524
525                 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-")) {
526                         if (parse_options(argv[5]) < 0)
527                                 goto finish;
528                 }
529
530                 /* A delicious drop of snake oil */
531                 mlockall(MCL_FUTURE);
532
533                 description = disk_description(argv[3]);
534                 mount_point = disk_mount_point(argv[2]);
535
536                 if (description && streq(argv[2], description)) {
537                         /* If the description string is simply the
538                          * volume name, then let's not show this
539                          * twice */
540                         free(description);
541                         description = NULL;
542                 }
543
544                 if (mount_point && description)
545                         asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
546                 else if (mount_point)
547                         asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
548                 else if (description)
549                         asprintf(&name_buffer, "%s (%s)", description, argv[2]);
550
551                 name = name_buffer ? name_buffer : argv[2];
552
553                 k = crypt_init(&cd, argv[3]);
554                 if (k) {
555                         log_error("crypt_init() failed: %s", strerror(-k));
556                         goto finish;
557                 }
558
559                 crypt_set_log_callback(cd, log_glue, NULL);
560
561                 status = crypt_status(cd, argv[2]);
562                 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
563                         log_info("Volume %s already active.", argv[2]);
564                         r = EXIT_SUCCESS;
565                         goto finish;
566                 }
567
568                 if (opt_readonly)
569                         flags |= CRYPT_ACTIVATE_READONLY;
570
571                 if (opt_discards)
572                         flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
573
574                 if (opt_timeout > 0)
575                         until = now(CLOCK_MONOTONIC) + opt_timeout;
576                 else
577                         until = 0;
578
579                 opt_tries = opt_tries > 0 ? opt_tries : 3;
580                 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
581
582                 if (key_file) {
583                         struct stat st;
584
585                         /* Ideally we'd do this on the open fd, but since this is just a
586                          * warning it's OK to do this in two steps. */
587                         if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
588                                 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
589                 }
590
591                 for (tries = 0; tries < opt_tries; tries++) {
592                         _cleanup_strv_free_ char **passwords = NULL;
593
594                         if (!key_file) {
595                                 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
596                                 if (k == -EAGAIN)
597                                         continue;
598                                 else if (k < 0)
599                                         goto finish;
600                         }
601
602                         if (streq_ptr(opt_type, CRYPT_TCRYPT))
603                                 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
604                         else
605                                 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
606                         if (k >= 0)
607                                 break;
608                         else if (k == -EAGAIN) {
609                                 key_file = NULL;
610                                 continue;
611                         } else if (k != -EPERM) {
612                                 log_error("Failed to activate: %s", strerror(-k));
613                                 goto finish;
614                         }
615
616                         log_warning("Invalid passphrase.");
617                 }
618
619                 if (tries >= opt_tries) {
620                         log_error("Too many attempts; giving up.");
621                         r = EXIT_FAILURE;
622                         goto finish;
623                 }
624
625         } else if (streq(argv[1], "detach")) {
626                 int k;
627
628                 k = crypt_init_by_name(&cd, argv[2]);
629                 if (k) {
630                         log_error("crypt_init() failed: %s", strerror(-k));
631                         goto finish;
632                 }
633
634                 crypt_set_log_callback(cd, log_glue, NULL);
635
636                 k = crypt_deactivate(cd, argv[2]);
637                 if (k < 0) {
638                         log_error("Failed to deactivate: %s", strerror(-k));
639                         goto finish;
640                 }
641
642         } else {
643                 log_error("Unknown verb %s.", argv[1]);
644                 goto finish;
645         }
646
647         r = EXIT_SUCCESS;
648
649 finish:
650
651         if (cd)
652                 crypt_free(cd);
653
654         free(opt_cipher);
655         free(opt_hash);
656         strv_free(opt_tcrypt_keyfiles);
657
658         return r;
659 }