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