chiark / gitweb /
Merge nss-myhostname
[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") || streq(option, "nofail"))
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                 if (opt_hash) {
346                         /* plain isn't a real hash type. it just means "use no hash" */
347                         if (!streq(opt_hash, "plain"))
348                                 hash = opt_hash;
349                 } else
350                         hash = "ripemd160";
351
352                 if (opt_cipher) {
353                         size_t l;
354
355                         l = strcspn(opt_cipher, "-");
356
357                         if (!(truncated_cipher = strndup(opt_cipher, l))) {
358                                 log_oom();
359                                 goto finish;
360                         }
361
362                         cipher = truncated_cipher;
363                         cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
364                 } else {
365                         cipher = "aes";
366                         cipher_mode = "cbc-essiv:sha256";
367                 }
368
369                 for (try = 0; try < opt_tries; try++) {
370                         bool pass_volume_key = false;
371
372                         strv_free(passwords);
373                         passwords = NULL;
374
375                         if (!key_file) {
376                                 char *text;
377                                 char **p;
378
379                                 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
380                                         log_oom();
381                                         goto finish;
382                                 }
383
384                                 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
385                                 free(text);
386
387                                 if (k < 0) {
388                                         log_error("Failed to query password: %s", strerror(-k));
389                                         goto finish;
390                                 }
391
392                                 if (opt_verify) {
393                                         char **passwords2 = NULL;
394
395                                         assert(strv_length(passwords) == 1);
396
397                                         if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
398                                                 log_oom();
399                                                 goto finish;
400                                         }
401
402                                         k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
403                                         free(text);
404
405                                         if (k < 0) {
406                                                 log_error("Failed to query verification password: %s", strerror(-k));
407                                                 goto finish;
408                                         }
409
410                                         assert(strv_length(passwords2) == 1);
411
412                                         if (!streq(passwords[0], passwords2[0])) {
413                                                 log_warning("Passwords did not match, retrying.");
414                                                 strv_free(passwords2);
415                                                 continue;
416                                         }
417
418                                         strv_free(passwords2);
419                                 }
420
421                                 strv_uniq(passwords);
422
423                                 STRV_FOREACH(p, passwords) {
424                                         char *c;
425
426                                         if (strlen(*p)+1 >= opt_key_size)
427                                                 continue;
428
429                                         /* Pad password if necessary */
430                                         if (!(c = new(char, opt_key_size))) {
431                                                 log_oom();
432                                                 goto finish;
433                                         }
434
435                                         strncpy(c, *p, opt_key_size);
436                                         free(*p);
437                                         *p = c;
438                                 }
439                         }
440
441                         k = 0;
442
443                         if (!opt_type || streq(opt_type, CRYPT_LUKS1))
444                                 k = crypt_load(cd, CRYPT_LUKS1, NULL);
445
446                         if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
447                                 struct crypt_params_plain params;
448
449                                 zero(params);
450                                 params.hash = hash;
451
452                                 /* for CRYPT_PLAIN limit reads
453                                 * from keyfile to key length, and
454                                 * ignore keyfile-size */
455                                 opt_keyfile_size = opt_key_size / 8;
456
457                                 /* In contrast to what the name
458                                  * crypt_setup() might suggest this
459                                  * doesn't actually format anything,
460                                  * it just configures encryption
461                                  * parameters when used for plain
462                                  * mode. */
463                                 k = crypt_format(cd, CRYPT_PLAIN,
464                                                  cipher,
465                                                  cipher_mode,
466                                                  NULL,
467                                                  NULL,
468                                                  opt_keyfile_size,
469                                                  &params);
470
471                                 /* hash == NULL implies the user passed "plain" */
472                                 pass_volume_key = (hash == NULL);
473                         }
474
475                         if (k < 0) {
476                                 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
477                                 goto finish;
478                         }
479
480                         log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
481                                  crypt_get_cipher(cd),
482                                  crypt_get_cipher_mode(cd),
483                                  crypt_get_volume_key_size(cd)*8,
484                                  argv[3]);
485
486                         if (key_file)
487                                 k = crypt_activate_by_keyfile_offset(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_keyfile_size,
488                                             opt_keyfile_offset, flags);
489                         else {
490                                 char **p;
491
492                                 STRV_FOREACH(p, passwords) {
493
494                                         if (pass_volume_key)
495                                                 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
496                                         else
497                                                 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
498
499                                         if (k >= 0)
500                                                 break;
501                                 }
502                         }
503
504                         if (k >= 0)
505                                 break;
506
507                         if (k != -EPERM) {
508                                 log_error("Failed to activate: %s", strerror(-k));
509                                 goto finish;
510                         }
511
512                         log_warning("Invalid passphrase.");
513                 }
514
515                 if (try >= opt_tries) {
516                         log_error("Too many attempts.");
517                         r = EXIT_FAILURE;
518                         goto finish;
519                 }
520
521         } else if (streq(argv[1], "detach")) {
522                 int k;
523
524                 if ((k = crypt_init_by_name(&cd, argv[2]))) {
525                         log_error("crypt_init() failed: %s", strerror(-k));
526                         goto finish;
527                 }
528
529                 crypt_set_log_callback(cd, log_glue, NULL);
530
531                 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
532                         log_error("Failed to deactivate: %s", strerror(-k));
533                         goto finish;
534                 }
535
536         } else {
537                 log_error("Unknown verb %s.", argv[1]);
538                 goto finish;
539         }
540
541         r = EXIT_SUCCESS;
542
543 finish:
544
545         if (cd)
546                 crypt_free(cd);
547
548         free(opt_cipher);
549         free(opt_hash);
550
551         free(truncated_cipher);
552
553         strv_free(passwords);
554
555         free(description);
556         free(mount_point);
557         free(name_buffer);
558
559         return r;
560 }