chiark / gitweb /
cryptsetup: add keyfile-size= support
[elogind.git] / src / cryptsetup / cryptsetup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <errno.h>
24 #include <sys/mman.h>
25 #include <mntent.h>
26
27 #include <libcryptsetup.h>
28 #include <libudev.h>
29
30 #include "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 = DEFAULT_TIMEOUT_USEC;
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"))
66                 return 0;
67
68         if (startswith(option, "cipher=")) {
69                 char *t;
70
71                 if (!(t = strdup(option+7)))
72                         return -ENOMEM;
73
74                 free(opt_cipher);
75                 opt_cipher = t;
76
77         } else if (startswith(option, "size=")) {
78
79                 if (safe_atou(option+5, &opt_key_size) < 0) {
80                         log_error("size= parse failure, ignoring.");
81                         return 0;
82                 }
83
84         } else if (startswith(option, "keyfile-size=")) {
85
86                 if (safe_atou(option+13, &opt_keyfile_size) < 0) {
87                         log_error("keyfile-size= parse failure, ignoring.");
88                         return 0;
89                 }
90
91         } else if (startswith(option, "keyfile-offset=")) {
92
93                 if (safe_atou(option+15, &opt_keyfile_offset) < 0) {
94                         log_error("keyfile-offset= parse failure, ignoring.");
95                         return 0;
96                 }
97
98         } else if (startswith(option, "hash=")) {
99                 char *t;
100
101                 if (!(t = strdup(option+5)))
102                         return -ENOMEM;
103
104                 free(opt_hash);
105                 opt_hash = t;
106
107         } else if (startswith(option, "tries=")) {
108
109                 if (safe_atou(option+6, &opt_tries) < 0) {
110                         log_error("tries= parse failure, ignoring.");
111                         return 0;
112                 }
113
114         } else if (streq(option, "readonly"))
115                 opt_readonly = true;
116         else if (streq(option, "verify"))
117                 opt_verify = true;
118         else if (streq(option, "allow-discards"))
119                 opt_discards = true;
120         else if (streq(option, "luks"))
121                 opt_type = CRYPT_LUKS1;
122         else if (streq(option, "plain") ||
123                  streq(option, "swap") ||
124                  streq(option, "tmp"))
125                 opt_type = CRYPT_PLAIN;
126         else if (startswith(option, "timeout=")) {
127
128                 if (parse_usec(option+8, &opt_timeout) < 0) {
129                         log_error("timeout= parse failure, ignoring.");
130                         return 0;
131                 }
132
133         } else if (!streq(option, "none"))
134                 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
135
136         return 0;
137 }
138
139 static int parse_options(const char *options) {
140         char *state;
141         char *w;
142         size_t l;
143
144         assert(options);
145
146         FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
147                 char *o;
148                 int r;
149
150                 if (!(o = strndup(w, l)))
151                         return -ENOMEM;
152
153                 r = parse_one_option(o);
154                 free(o);
155
156                 if (r < 0)
157                         return r;
158         }
159
160         return 0;
161 }
162
163 static void log_glue(int level, const char *msg, void *usrptr) {
164         log_debug("%s", msg);
165 }
166
167 static char *disk_description(const char *path) {
168         struct udev *udev = NULL;
169         struct udev_device *device = NULL;
170         struct stat st;
171         char *description = NULL;
172         const char *model;
173
174         assert(path);
175
176         if (stat(path, &st) < 0)
177                 return NULL;
178
179         if (!S_ISBLK(st.st_mode))
180                 return NULL;
181
182         if (!(udev = udev_new()))
183                 return NULL;
184
185         if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
186                 goto finish;
187
188         if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
189             (model = udev_device_get_property_value(device, "ID_MODEL")) ||
190             (model = udev_device_get_property_value(device, "DM_NAME")))
191                 description = strdup(model);
192
193 finish:
194         if (device)
195                 udev_device_unref(device);
196
197         if (udev)
198                 udev_unref(udev);
199
200         return description;
201 }
202
203 static char *disk_mount_point(const char *label) {
204         char *mp = NULL, *device = NULL;
205         FILE *f = NULL;
206         struct mntent *m;
207
208         /* Yeah, we don't support native systemd unit files here for now */
209
210         if (asprintf(&device, "/dev/mapper/%s", label) < 0)
211                 goto finish;
212
213         f = setmntent("/etc/fstab", "r");
214         if (!f)
215                 goto finish;
216
217         while ((m = getmntent(f)))
218                 if (path_equal(m->mnt_fsname, device)) {
219                         mp = strdup(m->mnt_dir);
220                         break;
221                 }
222
223 finish:
224         if (f)
225                 endmntent(f);
226
227         free(device);
228
229         return mp;
230 }
231
232 static int help(void) {
233
234         printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n"
235                "%s detach VOLUME\n\n"
236                "Attaches or detaches an encrypted block device.\n",
237                program_invocation_short_name,
238                program_invocation_short_name);
239
240         return 0;
241 }
242
243 int main(int argc, char *argv[]) {
244         int r = EXIT_FAILURE;
245         struct crypt_device *cd = NULL;
246         char **passwords = NULL, *truncated_cipher = NULL;
247         const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
248         char *description = NULL, *name_buffer = NULL, *mount_point = NULL;
249
250         if (argc <= 1) {
251                 help();
252                 return EXIT_SUCCESS;
253         }
254
255         if (argc < 3) {
256                 log_error("This program requires at least two arguments.");
257                 return EXIT_FAILURE;
258         }
259
260         log_set_target(LOG_TARGET_AUTO);
261         log_parse_environment();
262         log_open();
263
264         umask(0022);
265
266         if (streq(argv[1], "attach")) {
267                 uint32_t flags = 0;
268                 int k;
269                 unsigned try;
270                 const char *key_file = NULL;
271                 usec_t until;
272                 crypt_status_info status;
273
274                 /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */
275
276                 if (argc < 4) {
277                         log_error("attach requires at least two arguments.");
278                         goto finish;
279                 }
280
281                 if (argc >= 5 &&
282                     argv[4][0] &&
283                     !streq(argv[4], "-") &&
284                     !streq(argv[4], "none")) {
285
286                         if (!path_is_absolute(argv[4]))
287                                 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
288                         else
289                                 key_file = argv[4];
290                 }
291
292                 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
293                         parse_options(argv[5]);
294
295                 /* A delicious drop of snake oil */
296                 mlockall(MCL_FUTURE);
297
298                 description = disk_description(argv[3]);
299                 mount_point = disk_mount_point(argv[2]);
300
301                 if (description && streq(argv[2], description)) {
302                         /* If the description string is simply the
303                          * volume name, then let's not show this
304                          * twice */
305                         free(description);
306                         description = NULL;
307                 }
308
309                 if (mount_point && description)
310                         asprintf(&name_buffer, "%s (%s) on %s", description, argv[2], mount_point);
311                 else if (mount_point)
312                         asprintf(&name_buffer, "%s on %s", argv[2], mount_point);
313                 else if (description)
314                         asprintf(&name_buffer, "%s (%s)", description, argv[2]);
315
316                 name = name_buffer ? name_buffer : argv[2];
317
318                 if ((k = crypt_init(&cd, argv[3]))) {
319                         log_error("crypt_init() failed: %s", strerror(-k));
320                         goto finish;
321                 }
322
323                 crypt_set_log_callback(cd, log_glue, NULL);
324
325                 status = crypt_status(cd, argv[2]);
326                 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
327                         log_info("Volume %s already active.", argv[2]);
328                         r = EXIT_SUCCESS;
329                         goto finish;
330                 }
331
332                 if (opt_readonly)
333                         flags |= CRYPT_ACTIVATE_READONLY;
334
335                 if (opt_discards)
336                         flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
337
338                 if (opt_timeout > 0)
339                         until = now(CLOCK_MONOTONIC) + opt_timeout;
340                 else
341                         until = 0;
342
343                 opt_tries = opt_tries > 0 ? opt_tries : 3;
344                 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
345                 hash = opt_hash ? opt_hash : "ripemd160";
346
347                 if (opt_cipher) {
348                         size_t l;
349
350                         l = strcspn(opt_cipher, "-");
351
352                         if (!(truncated_cipher = strndup(opt_cipher, l))) {
353                                 log_oom();
354                                 goto finish;
355                         }
356
357                         cipher = truncated_cipher;
358                         cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
359                 } else {
360                         cipher = "aes";
361                         cipher_mode = "cbc-essiv:sha256";
362                 }
363
364                 for (try = 0; try < opt_tries; try++) {
365                         bool pass_volume_key = false;
366
367                         strv_free(passwords);
368                         passwords = NULL;
369
370                         if (!key_file) {
371                                 char *text;
372                                 char **p;
373
374                                 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
375                                         log_oom();
376                                         goto finish;
377                                 }
378
379                                 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
380                                 free(text);
381
382                                 if (k < 0) {
383                                         log_error("Failed to query password: %s", strerror(-k));
384                                         goto finish;
385                                 }
386
387                                 if (opt_verify) {
388                                         char **passwords2 = NULL;
389
390                                         assert(strv_length(passwords) == 1);
391
392                                         if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
393                                                 log_oom();
394                                                 goto finish;
395                                         }
396
397                                         k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
398                                         free(text);
399
400                                         if (k < 0) {
401                                                 log_error("Failed to query verification password: %s", strerror(-k));
402                                                 goto finish;
403                                         }
404
405                                         assert(strv_length(passwords2) == 1);
406
407                                         if (!streq(passwords[0], passwords2[0])) {
408                                                 log_warning("Passwords did not match, retrying.");
409                                                 strv_free(passwords2);
410                                                 continue;
411                                         }
412
413                                         strv_free(passwords2);
414                                 }
415
416                                 strv_uniq(passwords);
417
418                                 STRV_FOREACH(p, passwords) {
419                                         char *c;
420
421                                         if (strlen(*p)+1 >= opt_key_size)
422                                                 continue;
423
424                                         /* Pad password if necessary */
425                                         if (!(c = new(char, opt_key_size))) {
426                                                 log_oom();
427                                                 goto finish;
428                                         }
429
430                                         strncpy(c, *p, opt_key_size);
431                                         free(*p);
432                                         *p = c;
433                                 }
434                         }
435
436                         k = 0;
437
438                         if (!opt_type || streq(opt_type, CRYPT_LUKS1))
439                                 k = crypt_load(cd, CRYPT_LUKS1, NULL);
440
441                         if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
442                                 struct crypt_params_plain params;
443
444                                 zero(params);
445                                 params.hash = hash;
446
447                                 /* for CRYPT_PLAIN limit reads
448                                 * from keyfile to key length, and
449                                 * ignore keyfile-size */
450                                 opt_keyfile_size = opt_key_size / 8;
451
452                                 /* In contrast to what the name
453                                  * crypt_setup() might suggest this
454                                  * doesn't actually format anything,
455                                  * it just configures encryption
456                                  * parameters when used for plain
457                                  * mode. */
458                                 k = crypt_format(cd, CRYPT_PLAIN,
459                                                  cipher,
460                                                  cipher_mode,
461                                                  NULL,
462                                                  NULL,
463                                                  opt_keyfile_size,
464                                                  &params);
465
466                                 pass_volume_key = streq(hash, "plain");
467                         }
468
469                         if (k < 0) {
470                                 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
471                                 goto finish;
472                         }
473
474                         log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
475                                  crypt_get_cipher(cd),
476                                  crypt_get_cipher_mode(cd),
477                                  crypt_get_volume_key_size(cd)*8,
478                                  argv[3]);
479
480                         if (key_file)
481                                 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
482                                             opt_keyfile_offset, flags);
483                         else {
484                                 char **p;
485
486                                 STRV_FOREACH(p, passwords) {
487
488                                         if (pass_volume_key)
489                                                 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
490                                         else
491                                                 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
492
493                                         if (k >= 0)
494                                                 break;
495                                 }
496                         }
497
498                         if (k >= 0)
499                                 break;
500
501                         if (k != -EPERM) {
502                                 log_error("Failed to activate: %s", strerror(-k));
503                                 goto finish;
504                         }
505
506                         log_warning("Invalid passphrase.");
507                 }
508
509                 if (try >= opt_tries) {
510                         log_error("Too many attempts.");
511                         r = EXIT_FAILURE;
512                         goto finish;
513                 }
514
515         } else if (streq(argv[1], "detach")) {
516                 int k;
517
518                 if ((k = crypt_init_by_name(&cd, argv[2]))) {
519                         log_error("crypt_init() failed: %s", strerror(-k));
520                         goto finish;
521                 }
522
523                 crypt_set_log_callback(cd, log_glue, NULL);
524
525                 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
526                         log_error("Failed to deactivate: %s", strerror(-k));
527                         goto finish;
528                 }
529
530         } else {
531                 log_error("Unknown verb %s.", argv[1]);
532                 goto finish;
533         }
534
535         r = EXIT_SUCCESS;
536
537 finish:
538
539         if (cd)
540                 crypt_free(cd);
541
542         free(opt_cipher);
543         free(opt_hash);
544
545         free(truncated_cipher);
546
547         strv_free(passwords);
548
549         free(description);
550         free(mount_point);
551         free(name_buffer);
552
553         return r;
554 }