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