chiark / gitweb /
f72a14abf617baa199401a3921b1131ee671eae8
[elogind.git] / src / 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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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
26 #include <libcryptsetup.h>
27 #include <libudev.h>
28
29 #include "log.h"
30 #include "util.h"
31 #include "strv.h"
32 #include "ask-password-api.h"
33
34 static const char *opt_type = NULL; /* LUKS1 or PLAIN */
35 static char *opt_cipher = NULL;
36 static unsigned opt_key_size = 0;
37 static char *opt_hash = NULL;
38 static unsigned opt_tries = 0;
39 static bool opt_readonly = false;
40 static bool opt_verify = false;
41 static usec_t opt_timeout = 0;
42
43 /* Options Debian's crypttab knows we don't:
44
45     offset=
46     skip=
47     precheck=
48     check=
49     checkargs=
50     noearly=
51     loud=
52     keyscript=
53 */
54
55 static int parse_one_option(const char *option) {
56         assert(option);
57
58         /* Handled outside of this tool */
59         if (streq(option, "noauto"))
60                 return 0;
61
62         if (startswith(option, "cipher=")) {
63                 char *t;
64
65                 if (!(t = strdup(option+7)))
66                         return -ENOMEM;
67
68                 free(opt_cipher);
69                 opt_cipher = t;
70
71         } else if (startswith(option, "size=")) {
72
73                 if (safe_atou(option+5, &opt_key_size) < 0) {
74                         log_error("size= parse failure, ignoring.");
75                         return 0;
76                 }
77
78         } else if (startswith(option, "hash=")) {
79                 char *t;
80
81                 if (!(t = strdup(option+5)))
82                         return -ENOMEM;
83
84                 free(opt_hash);
85                 opt_hash = t;
86
87         } else if (startswith(option, "tries=")) {
88
89                 if (safe_atou(option+6, &opt_tries) < 0) {
90                         log_error("tries= parse failure, ignoring.");
91                         return 0;
92                 }
93
94         } else if (streq(option, "readonly"))
95                 opt_readonly = true;
96         else if (streq(option, "verify"))
97                 opt_verify = true;
98         else if (streq(option, "luks"))
99                 opt_type = CRYPT_LUKS1;
100         else if (streq(option, "plain") ||
101                  streq(option, "swap") ||
102                  streq(option, "tmp"))
103                 opt_type = CRYPT_PLAIN;
104         else if (startswith(option, "timeout=")) {
105
106                 if (parse_usec(option+8, &opt_timeout) < 0) {
107                         log_error("timeout= parse failure, ignoring.");
108                         return 0;
109                 }
110
111         } else
112                 log_error("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
113
114         return 0;
115 }
116
117 static int parse_options(const char *options) {
118         char *state;
119         char *w;
120         size_t l;
121
122         assert(options);
123
124         FOREACH_WORD_SEPARATOR(w, l, options, ",", state) {
125                 char *o;
126                 int r;
127
128                 if (!(o = strndup(w, l)))
129                         return -ENOMEM;
130
131                 r = parse_one_option(o);
132                 free(o);
133
134                 if (r < 0)
135                         return r;
136         }
137
138         return 0;
139 }
140
141 static void log_glue(int level, const char *msg, void *usrptr) {
142         log_debug("%s", msg);
143 }
144
145 static char *disk_description(const char *path) {
146         struct udev *udev = NULL;
147         struct udev_device *device = NULL;
148         struct stat st;
149         char *description = NULL;
150         const char *model;
151
152         assert(path);
153
154         if (stat(path, &st) < 0)
155                 return NULL;
156
157         if (!S_ISBLK(st.st_mode))
158                 return NULL;
159
160         if (!(udev = udev_new()))
161                 return NULL;
162
163         if (!(device = udev_device_new_from_devnum(udev, 'b', st.st_rdev)))
164                 goto finish;
165
166         if ((model = udev_device_get_property_value(device, "ID_MODEL_FROM_DATABASE")) ||
167             (model = udev_device_get_property_value(device, "ID_MODEL")))
168                 description = strdup(model);
169
170 finish:
171         if (device)
172                 udev_device_unref(device);
173
174         if (udev)
175                 udev_unref(udev);
176
177         return description;
178 }
179
180 int main(int argc, char *argv[]) {
181         int r = EXIT_FAILURE;
182         struct crypt_device *cd = NULL;
183         char **passwords = NULL, *truncated_cipher = NULL;
184         const char *cipher = NULL, *cipher_mode = NULL, *hash = NULL, *name = NULL;
185         char *description = NULL;
186
187         if (argc < 3) {
188                 log_error("This program requires at least two arguments.");
189                 return EXIT_FAILURE;
190         }
191
192         log_set_target(LOG_TARGET_AUTO);
193         log_parse_environment();
194         log_open();
195
196         if (streq(argv[1], "attach")) {
197                 uint32_t flags = 0;
198                 int k;
199                 unsigned try;
200                 const char *key_file = NULL;
201                 usec_t until;
202                 crypt_status_info status;
203
204                 if (argc < 4) {
205                         log_error("attach requires at least two arguments.");
206                         goto finish;
207                 }
208
209                 if (argc >= 5 &&
210                     argv[4][0] &&
211                     !streq(argv[4], "-") &&
212                     !streq(argv[4], "none")) {
213
214                         if (!path_is_absolute(argv[4]))
215                                 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
216                         else
217                                 key_file = argv[4];
218                 }
219
220                 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
221                         parse_options(argv[5]);
222
223                 /* A delicious drop of snake oil */
224                 mlockall(MCL_FUTURE);
225
226                 description = disk_description(argv[3]);
227                 name = description ? description : argv[2];
228
229                 if ((k = crypt_init(&cd, argv[3]))) {
230                         log_error("crypt_init() failed: %s", strerror(-k));
231                         goto finish;
232                 }
233
234                 crypt_set_log_callback(cd, log_glue, NULL);
235
236                 status = crypt_status(cd, argv[2]);
237                 if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) {
238                         log_info("Volume %s already active.", argv[2]);
239                         r = EXIT_SUCCESS;
240                         goto finish;
241                 }
242
243                 if (opt_readonly)
244                         flags |= CRYPT_ACTIVATE_READONLY;
245
246                 until = now(CLOCK_MONOTONIC) + (opt_timeout > 0 ? opt_timeout : 60 * USEC_PER_SEC);
247
248                 opt_tries = opt_tries > 0 ? opt_tries : 3;
249                 opt_key_size = (opt_key_size > 0 ? opt_key_size : 256);
250                 hash = opt_hash ? opt_hash : "ripemd160";
251
252                 if (opt_cipher) {
253                         size_t l;
254
255                         l = strcspn(opt_cipher, "-");
256
257                         if (!(truncated_cipher = strndup(opt_cipher, l))) {
258                                 log_error("Out of memory");
259                                 goto finish;
260                         }
261
262                         cipher = truncated_cipher;
263                         cipher_mode = opt_cipher[l] ? opt_cipher+l+1 : "plain";
264                 } else {
265                         cipher = "aes";
266                         cipher_mode = "cbc-essiv:sha256";
267                 }
268
269                 for (try = 0; try < opt_tries; try++) {
270                         bool pass_volume_key = false;
271
272                         strv_free(passwords);
273                         passwords = NULL;
274
275                         if (!key_file) {
276                                 char *text;
277                                 char **p;
278
279                                 if (asprintf(&text, "Please enter passphrase for disk %s!", name) < 0) {
280                                         log_error("Out of memory");
281                                         goto finish;
282                                 }
283
284                                 k = ask_password_auto(text, "drive-harddisk", until, try == 0 && !opt_verify, &passwords);
285                                 free(text);
286
287                                 if (k < 0) {
288                                         log_error("Failed to query password: %s", strerror(-k));
289                                         goto finish;
290                                 }
291
292                                 if (opt_verify) {
293                                         char **passwords2 = NULL;
294
295                                         assert(strv_length(passwords) == 1);
296
297                                         if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0) {
298                                                 log_error("Out of memory");
299                                                 goto finish;
300                                         }
301
302                                         k = ask_password_auto(text, "drive-harddisk", until, false, &passwords2);
303                                         free(text);
304
305                                         if (k < 0) {
306                                                 log_error("Failed to query verification password: %s", strerror(-k));
307                                                 goto finish;
308                                         }
309
310                                         assert(strv_length(passwords2) == 1);
311
312                                         if (!streq(passwords[0], passwords2[0])) {
313                                                 log_warning("Passwords did not match, retrying.");
314                                                 strv_free(passwords2);
315                                                 continue;
316                                         }
317
318                                         strv_free(passwords2);
319                                 }
320
321                                 STRV_FOREACH(p, passwords) {
322                                         char *c;
323
324                                         if (strlen(*p)+1 >= opt_key_size)
325                                                 continue;
326
327                                         /* Pad password if necessary */
328                                         if (!(c = new(char, opt_key_size))) {
329                                                 log_error("Out of memory.");
330                                                 goto finish;
331                                         }
332
333                                         strncpy(c, *p, opt_key_size);
334                                         free(*p);
335                                         *p = c;
336                                 }
337                         }
338
339                         if (!opt_type || streq(opt_type, CRYPT_LUKS1))
340                                 k = crypt_load(cd, CRYPT_LUKS1, NULL);
341
342                         if ((!opt_type && k < 0) || streq_ptr(opt_type, CRYPT_PLAIN)) {
343                                 struct crypt_params_plain params;
344
345                                 zero(params);
346                                 params.hash = hash;
347
348                                 /* In contrast to what the name
349                                  * crypt_setup() might suggest this
350                                  * doesn't actually format anything,
351                                  * it just configures encryption
352                                  * parameters when used for plain
353                                  * mode. */
354                                 k = crypt_format(cd, CRYPT_PLAIN,
355                                                  cipher,
356                                                  cipher_mode,
357                                                  NULL,
358                                                  NULL,
359                                                  opt_key_size / 8,
360                                                  &params);
361
362                                 pass_volume_key = streq(hash, "plain");
363                         }
364
365                         if (k < 0) {
366                                 log_error("Loading of cryptographic parameters failed: %s", strerror(-k));
367                                 goto finish;
368                         }
369
370                         log_info("Set cipher %s, mode %s, key size %i bits for device %s.",
371                                  crypt_get_cipher(cd),
372                                  crypt_get_cipher_mode(cd),
373                                  crypt_get_volume_key_size(cd)*8,
374                                  argv[3]);
375
376                         if (key_file)
377                                 k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, opt_key_size, flags);
378                         else {
379                                 char **p;
380
381                                 STRV_FOREACH(p, passwords) {
382
383                                         if (pass_volume_key)
384                                                 k = crypt_activate_by_volume_key(cd, argv[2], *p, opt_key_size, flags);
385                                         else
386                                                 k = crypt_activate_by_passphrase(cd, argv[2], CRYPT_ANY_SLOT, *p, strlen(*p), flags);
387
388                                         if (k >= 0)
389                                                 break;
390                                 }
391                         }
392
393                         if (k >= 0)
394                                 break;
395
396                         if (k != -EPERM) {
397                                 log_error("Failed to activate: %s", strerror(-k));
398                                 goto finish;
399                         }
400
401                         log_warning("Invalid passphrase.");
402                 }
403
404                 if (try >= opt_tries) {
405                         log_error("Too many attempts.");
406                         r = EXIT_FAILURE;
407                         goto finish;
408                 }
409
410         } else if (streq(argv[1], "detach")) {
411                 int k;
412
413                 if ((k = crypt_init_by_name(&cd, argv[2]))) {
414                         log_error("crypt_init() failed: %s", strerror(-k));
415                         goto finish;
416                 }
417
418                 crypt_set_log_callback(cd, log_glue, NULL);
419
420                 if ((k = crypt_deactivate(cd, argv[2])) < 0) {
421                         log_error("Failed to deactivate: %s", strerror(-k));
422                         goto finish;
423                 }
424
425         } else {
426                 log_error("Unknown verb %s.", argv[1]);
427                 goto finish;
428         }
429
430         r = EXIT_SUCCESS;
431
432 finish:
433
434         if (cd)
435                 crypt_free(cd);
436
437         free(opt_cipher);
438         free(opt_hash);
439
440         free(truncated_cipher);
441
442         strv_free(passwords);
443
444         free(description);
445
446         return r;
447 }