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