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