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