chiark / gitweb /
22b5eead72d3e804557e602090f86cb781734527
[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 = 3;
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") || streq(option, "discard"))
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_key_size = (opt_key_size > 0 ? opt_key_size : 256);
580
581                 if (key_file) {
582                         struct stat st;
583
584                         /* Ideally we'd do this on the open fd, but since this is just a
585                          * warning it's OK to do this in two steps. */
586                         if (stat(key_file, &st) >= 0 && (st.st_mode & 0005))
587                                 log_warning("Key file %s is world-readable. This is not a good idea!", key_file);
588                 }
589
590                 for (tries = 0; opt_tries == 0 || tries < opt_tries; tries++) {
591                         _cleanup_strv_free_ char **passwords = NULL;
592
593                         if (!key_file) {
594                                 k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
595                                 if (k == -EAGAIN)
596                                         continue;
597                                 else if (k < 0)
598                                         goto finish;
599                         }
600
601                         if (streq_ptr(opt_type, CRYPT_TCRYPT))
602                                 k = attach_tcrypt(cd, argv[2], key_file, passwords, flags);
603                         else
604                                 k = attach_luks_or_plain(cd, argv[2], key_file, passwords, flags);
605                         if (k >= 0)
606                                 break;
607                         else if (k == -EAGAIN) {
608                                 key_file = NULL;
609                                 continue;
610                         } else if (k != -EPERM) {
611                                 log_error("Failed to activate: %s", strerror(-k));
612                                 goto finish;
613                         }
614
615                         log_warning("Invalid passphrase.");
616                 }
617
618                 if (opt_tries != 0 && tries >= opt_tries) {
619                         log_error("Too many attempts; giving up.");
620                         r = EXIT_FAILURE;
621                         goto finish;
622                 }
623
624         } else if (streq(argv[1], "detach")) {
625                 int k;
626
627                 k = crypt_init_by_name(&cd, argv[2]);
628                 if (k) {
629                         log_error("crypt_init() failed: %s", strerror(-k));
630                         goto finish;
631                 }
632
633                 crypt_set_log_callback(cd, log_glue, NULL);
634
635                 k = crypt_deactivate(cd, argv[2]);
636                 if (k < 0) {
637                         log_error("Failed to deactivate: %s", strerror(-k));
638                         goto finish;
639                 }
640
641         } else {
642                 log_error("Unknown verb %s.", argv[1]);
643                 goto finish;
644         }
645
646         r = EXIT_SUCCESS;
647
648 finish:
649
650         if (cd)
651                 crypt_free(cd);
652
653         free(opt_cipher);
654         free(opt_hash);
655         strv_free(opt_tcrypt_keyfiles);
656
657         return r;
658 }