]> git.proxmox.com Git - grub2.git/blame - grub-core/disk/geli.c
Use hardcoded reference to sha512 and sha256 in geli rather than runtime lookup since...
[grub2.git] / grub-core / disk / geli.c
CommitLineData
1a1f408f
VS
1/*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2003,2007,2010,2011 Free Software Foundation, Inc.
4 *
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/* This file is loosely based on FreeBSD geli implementation
20 (but no code was directly copied). FreeBSD geli is distributed under
21 following terms: */
22/*-
23 * Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
24 * All rights reserved.
25 *
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 *
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
46 */
47
48#include <grub/cryptodisk.h>
49#include <grub/types.h>
50#include <grub/misc.h>
51#include <grub/mm.h>
52#include <grub/dl.h>
53#include <grub/err.h>
54#include <grub/disk.h>
55#include <grub/crypto.h>
56#include <grub/extcmd.h>
57#include <grub/i18n.h>
58
59GRUB_MOD_LICENSE ("GPLv3+");
60
61struct grub_geli_key
62{
63 grub_uint8_t iv_key[64];
64 grub_uint8_t cipher_key[64];
65 grub_uint8_t hmac[64];
66} __attribute__ ((packed));
67
68struct grub_geli_phdr
69{
70 grub_uint8_t magic[16];
71#define GELI_MAGIC "GEOM::ELI"
72 grub_uint32_t version;
73 grub_uint32_t unused1;
74 grub_uint16_t alg;
75 grub_uint16_t keylen;
3e90811d
VS
76 grub_uint16_t unused3[5];
77 grub_uint32_t sector_size;
1a1f408f
VS
78 grub_uint8_t keys_used;
79 grub_uint32_t niter;
80 grub_uint8_t salt[64];
81 struct grub_geli_key keys[2];
82} __attribute__ ((packed));
83
574d2680
VS
84/* FIXME: support big-endian pre-version-4 volumes. */
85/* FIXME: support for keyfiles. */
86/* FIXME: support for HMAC. */
87/* FIXME: support for UUID. */
88/* FIXME: support for mounting all boot volumes. */
1a1f408f 89const char *algorithms[] = {
b6b4ea5f
VS
90 [0x01] = "des",
91 [0x02] = "3des",
92 [0x03] = "blowfish",
93 [0x04] = "cast5",
574d2680 94 /* FIXME: 0x05 is skipjack, but we don't have it. */
1a1f408f 95 [0x0b] = "aes",
574d2680 96 /* FIXME: 0x10 is null. */
b6b4ea5f 97 [0x15] = "camellia128",
171e2be1 98 [0x16] = "aes"
1a1f408f
VS
99};
100
101#define MAX_PASSPHRASE 256
102
103static const struct grub_arg_option options[] =
104 {
105 {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0},
106 {"all", 'a', 0, N_("Mount all."), 0, 0},
107 {0, 0, 0, 0, 0, 0}
108 };
109
110static int check_uuid, have_it;
111static char *search_uuid;
112
88ac3146
VS
113static gcry_err_code_t
114geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno)
115{
116 gcry_err_code_t gcry_err;
117 const struct {
118 char magic[4];
119 grub_uint64_t zone;
120 } __attribute__ ((packed)) tohash
121 = { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) };
122 grub_uint64_t key[(dev->hash->mdlen + 7) / 8];
123
124 grub_dprintf ("geli", "rekeying %" PRIuGRUB_UINT64_T " keysize=%d\n",
125 zoneno, dev->rekey_derived_size);
126 gcry_err = grub_crypto_hmac_buffer (dev->hash, dev->rekey_key, 64,
127 &tohash, sizeof (tohash), key);
128 if (gcry_err)
129 return grub_crypto_gcry_error (gcry_err);
130
131 return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key,
132 dev->rekey_derived_size);
133}
134
1a1f408f
VS
135static grub_cryptodisk_t
136configure_ciphers (const struct grub_geli_phdr *header)
137{
138 grub_cryptodisk_t newdev;
171e2be1 139 grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL;
1a1f408f
VS
140 const struct gcry_cipher_spec *ciph;
141 const char *ciphername = NULL;
1a1f408f
VS
142
143 /* Look for GELI magic sequence. */
144 if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC))
88ac3146
VS
145 || grub_le_to_cpu32 (header->version) > 5
146 || grub_le_to_cpu32 (header->version) < 1)
1a1f408f
VS
147 {
148 grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]);
149 return NULL;
150 }
3e90811d
VS
151 if ((grub_le_to_cpu32 (header->sector_size)
152 & (grub_le_to_cpu32 (header->sector_size) - 1))
153 || grub_le_to_cpu32 (header->sector_size) == 0)
154 {
155 grub_dprintf ("geli", "incorrect sector size %d\n",
156 grub_le_to_cpu32 (header->sector_size));
157 return NULL;
158 }
1a1f408f
VS
159
160#if 0
161 optr = uuid;
162 for (iptr = header->uuid; iptr < &header->uuid[ARRAY_SIZE (header->uuid)];
163 iptr++)
164 {
165 if (*iptr != '-')
166 *optr++ = *iptr;
167 }
168 *optr = 0;
169
170 if (check_uuid && grub_strcasecmp (search_uuid, uuid) != 0)
171 {
172 grub_dprintf ("luks", "%s != %s", uuid, search_uuid);
173 return NULL;
174 }
175#endif
176
177 if (grub_le_to_cpu16 (header->alg) >= ARRAY_SIZE (algorithms)
178 || algorithms[grub_le_to_cpu16 (header->alg)] == NULL)
179 {
180 grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown",
181 grub_le_to_cpu16 (header->alg));
182 return NULL;
183 }
184
185 ciphername = algorithms[grub_le_to_cpu16 (header->alg)];
186 ciph = grub_crypto_lookup_cipher_by_name (ciphername);
187 if (!ciph)
188 {
189 grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available",
190 ciphername);
191 return NULL;
192 }
193
194 /* Configure the cipher used for the bulk data. */
195 cipher = grub_crypto_cipher_open (ciph);
196 if (!cipher)
197 return NULL;
198
171e2be1
VS
199 if (grub_le_to_cpu16 (header->alg) == 0x16)
200 {
201 secondary_cipher = grub_crypto_cipher_open (ciph);
202 if (!secondary_cipher)
203 return NULL;
204 }
205
1a1f408f
VS
206 if (grub_le_to_cpu16 (header->keylen) > 1024)
207 {
208 grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d",
209 grub_le_to_cpu16 (header->keylen));
210 return NULL;
211 }
212
1a1f408f
VS
213 newdev = grub_zalloc (sizeof (struct grub_cryptodisk));
214 if (!newdev)
215 return NULL;
216 newdev->cipher = cipher;
171e2be1 217 newdev->secondary_cipher = secondary_cipher;
1a1f408f
VS
218 newdev->offset = 0;
219 newdev->source_disk = NULL;
220 newdev->benbi_log = 0;
171e2be1
VS
221 if (grub_le_to_cpu16 (header->alg) == 0x16)
222 {
223 newdev->mode = GRUB_CRYPTODISK_MODE_XTS;
224 newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64;
225 }
226 else
227 {
228 newdev->mode = GRUB_CRYPTODISK_MODE_CBC;
229 newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH;
230 }
1a1f408f
VS
231 newdev->essiv_cipher = NULL;
232 newdev->essiv_hash = NULL;
7efb5c9e
VS
233 newdev->hash = GRUB_MD_SHA512;
234 newdev->iv_hash = GRUB_MD_SHA256;
3e90811d
VS
235
236 for (newdev->log_sector_size = 0;
237 (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header->sector_size);
238 newdev->log_sector_size++);
88ac3146
VS
239
240 if (grub_le_to_cpu32 (header->version) >= 5)
241 {
242 newdev->rekey = geli_rekey;
243 newdev->rekey_shift = 20;
244 }
1a1f408f
VS
245#if 0
246 grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid));
247#endif
248 return newdev;
249}
250
251static grub_err_t
252recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header,
253 const char *name, grub_disk_t source __attribute__ ((unused)))
254{
255 grub_size_t keysize = grub_le_to_cpu16 (header->keylen) / 8;
256 grub_uint8_t digest[dev->hash->mdlen];
257 grub_uint8_t geomkey[dev->hash->mdlen];
258 grub_uint8_t verify_key[dev->hash->mdlen];
1a1f408f
VS
259 grub_uint8_t zero[dev->cipher->cipher->blocksize];
260 char passphrase[MAX_PASSPHRASE] = "";
261 unsigned i;
262 gcry_err_code_t gcry_err;
263
264 grub_memset (zero, 0, sizeof (zero));
265
266 grub_printf ("Attempting to decrypt master key...\n");
267
268 /* Get the passphrase from the user. */
269 grub_printf ("Enter passphrase for %s (%s): ", name, dev->uuid);
270 if (!grub_password_get (passphrase, MAX_PASSPHRASE))
271 return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied");
272
7ede8f8b
VS
273 /* Calculate the PBKDF2 of the user supplied passphrase. */
274 if (grub_le_to_cpu32 (header->niter) != 0)
275 {
276 grub_uint8_t pbkdf_key[64];
277 gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase,
278 grub_strlen (passphrase),
279 header->salt,
280 sizeof (header->salt),
281 grub_le_to_cpu32 (header->niter),
282 pbkdf_key, sizeof (pbkdf_key));
1a1f408f 283
7ede8f8b
VS
284 if (gcry_err)
285 return grub_crypto_gcry_error (gcry_err);
1a1f408f 286
7ede8f8b
VS
287 gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key,
288 sizeof (pbkdf_key), geomkey);
289 if (gcry_err)
290 return grub_crypto_gcry_error (gcry_err);
291 }
292 else
293 {
294 struct grub_crypto_hmac_handle *hnd;
295
296 hnd = grub_crypto_hmac_init (dev->hash, NULL, 0);
297 if (!hnd)
298 return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY);
299
300 grub_crypto_hmac_write (hnd, header->salt, sizeof (header->salt));
301 grub_crypto_hmac_write (hnd, passphrase, grub_strlen (passphrase));
302
303 gcry_err = grub_crypto_hmac_fini (hnd, geomkey);
304 if (gcry_err)
305 return grub_crypto_gcry_error (gcry_err);
306 }
1a1f408f
VS
307
308 gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
309 sizeof (geomkey), "\1", 1, digest);
310 if (gcry_err)
311 return grub_crypto_gcry_error (gcry_err);
312
313 gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
314 sizeof (geomkey), "\0", 1, verify_key);
315 if (gcry_err)
316 return grub_crypto_gcry_error (gcry_err);
317
318 grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize);
319
320 /* Try to recover master key from each active keyslot. */
321 for (i = 0; i < ARRAY_SIZE (header->keys); i++)
322 {
323 struct grub_geli_key candidate_key;
324 grub_uint8_t key_hmac[dev->hash->mdlen];
325
326 /* Check if keyslot is enabled. */
327 if (! (header->keys_used & (1 << i)))
328 continue;
329
330 grub_dprintf ("geli", "Trying keyslot %d\n", i);
331
332 gcry_err = grub_crypto_cipher_set_key (dev->cipher,
333 digest, keysize);
334 if (gcry_err)
335 return grub_crypto_gcry_error (gcry_err);
336
337 gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key,
338 &header->keys[i],
339 sizeof (candidate_key),
340 zero);
341 if (gcry_err)
342 return grub_crypto_gcry_error (gcry_err);
343
344 gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key,
345 sizeof (verify_key),
346 &candidate_key,
347 (sizeof (candidate_key)
348 - sizeof (candidate_key.hmac)),
349 key_hmac);
350 if (gcry_err)
351 return grub_crypto_gcry_error (gcry_err);
352
353 if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0)
354 continue;
355 grub_printf ("Slot %d opened\n", i);
356
357 /* Set the master key. */
88ac3146
VS
358 if (!dev->rekey)
359 {
171e2be1
VS
360 grub_size_t real_keysize = keysize;
361 if (grub_le_to_cpu16 (header->alg) == 0x16)
362 real_keysize *= 2;
88ac3146 363 gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key,
171e2be1 364 real_keysize);
88ac3146
VS
365 if (gcry_err)
366 return grub_crypto_gcry_error (gcry_err);
367 }
368 else
369 {
171e2be1
VS
370 grub_size_t real_keysize = keysize;
371 if (grub_le_to_cpu16 (header->alg) == 0x16)
372 real_keysize *= 2;
88ac3146
VS
373 /* For a reason I don't know, the IV key is used in rekeying. */
374 grub_memcpy (dev->rekey_key, candidate_key.iv_key,
375 sizeof (candidate_key.iv_key));
171e2be1 376 dev->rekey_derived_size = real_keysize;
88ac3146
VS
377 dev->last_rekey = -1;
378 COMPILE_TIME_ASSERT (sizeof (dev->rekey_key)
379 >= sizeof (candidate_key.iv_key));
380 }
1a1f408f
VS
381
382 dev->iv_prefix_len = sizeof (candidate_key.iv_key);
383 grub_memcpy (dev->iv_prefix, candidate_key.iv_key,
384 sizeof (candidate_key.iv_key));
385
386 COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key));
387
388 return GRUB_ERR_NONE;
389 }
390
391 return GRUB_ACCESS_DENIED;
392}
393
394static void
395close (grub_cryptodisk_t luks)
396{
397 grub_crypto_cipher_close (luks->cipher);
398 grub_crypto_cipher_close (luks->secondary_cipher);
399 grub_crypto_cipher_close (luks->essiv_cipher);
400 grub_free (luks);
401}
402
403static grub_err_t
404grub_geli_scan_device_real (const char *name, grub_disk_t source)
405{
406 grub_err_t err;
407 struct grub_geli_phdr header;
408 grub_cryptodisk_t newdev, dev;
409 grub_disk_addr_t sector;
410
411 grub_dprintf ("geli", "scanning %s\n", source->name);
412 dev = grub_cryptodisk_get_by_source_disk (source);
413
414 if (dev)
415 return GRUB_ERR_NONE;
416
417 sector = grub_disk_get_size (source);
418 if (sector == GRUB_DISK_SIZE_UNKNOWN)
419 return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli");
420
421 /* Read the LUKS header. */
422 err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
423 if (err)
424 return err;
425
426 newdev = configure_ciphers (&header);
427 if (!newdev)
428 return grub_errno;
429
430 newdev->total_length = grub_disk_get_size (source) - 1;
431
432 err = recover_key (newdev, &header, name, source);
433 if (err)
434 {
435 close (newdev);
436 return err;
437 }
438
439 grub_cryptodisk_insert (newdev, name, source);
440
441 have_it = 1;
442
443 return GRUB_ERR_NONE;
444}
445
446#ifdef GRUB_UTIL
447grub_err_t
448grub_geli_cheat_mount (const char *sourcedev, const char *cheat)
449{
450 grub_err_t err;
451 struct grub_geli_phdr header;
452 grub_cryptodisk_t newdev, dev;
453 grub_disk_t source;
454 grub_disk_addr_t sector;
455
456 /* Try to open disk. */
457 source = grub_disk_open (sourcedev);
458 if (!source)
459 return grub_errno;
460
461 dev = grub_cryptodisk_get_by_source_disk (source);
462
463 if (dev)
464 {
465 grub_disk_close (source);
466 return GRUB_ERR_NONE;
467 }
468
469 sector = grub_disk_get_size (source);
470 if (sector == GRUB_DISK_SIZE_UNKNOWN)
471 return grub_error (GRUB_ERR_OUT_OF_RANGE, "not a geli");
472
473 /* Read the LUKS header. */
474 err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
475 if (err)
476 return err;
477
478 newdev = configure_ciphers (&header);
479 if (!newdev)
480 {
481 grub_disk_close (source);
482 return grub_errno;
483 }
484
485 newdev->total_length = grub_disk_get_size (source) - 1;
486
487 err = grub_cryptodisk_cheat_insert (newdev, sourcedev, source, cheat);
488 grub_disk_close (source);
489 if (err)
490 grub_free (newdev);
491
492 return err;
493}
494#endif
495
496static int
497grub_geli_scan_device (const char *name)
498{
499 grub_err_t err;
500 grub_disk_t source;
501
502 /* Try to open disk. */
503 source = grub_disk_open (name);
504 if (!source)
505 return grub_errno;
506
507 err = grub_geli_scan_device_real (name, source);
508
509 grub_disk_close (source);
510
511 if (err)
512 grub_print_error ();
513 return have_it && check_uuid ? 0 : 1;
514}
515
516#ifdef GRUB_UTIL
517
518void
519grub_util_geli_print_uuid (grub_disk_t disk)
520{
521 grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data;
522 grub_printf ("%s ", dev->uuid);
523}
524#endif
525
526static grub_err_t
527grub_cmd_gelimount (grub_extcmd_context_t ctxt, int argc, char **args)
528{
529 struct grub_arg_list *state = ctxt->state;
530
531 if (argc < 1 && !state[1].set)
532 return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required");
533
534 have_it = 0;
535 if (state[0].set)
536 {
537 grub_cryptodisk_t dev;
538
539 dev = grub_cryptodisk_get_by_uuid (args[0]);
540 if (dev)
541 {
542 grub_dprintf ("luks", "already mounted as crypto%lu\n", dev->id);
543 return GRUB_ERR_NONE;
544 }
545
546 check_uuid = 1;
547 search_uuid = args[0];
548 grub_device_iterate (&grub_geli_scan_device);
549 search_uuid = NULL;
550
551 if (!have_it)
552 return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such luks found");
553 return GRUB_ERR_NONE;
554 }
555 else if (state[1].set)
556 {
557 check_uuid = 0;
558 search_uuid = NULL;
559 grub_device_iterate (&grub_geli_scan_device);
560 search_uuid = NULL;
561 return GRUB_ERR_NONE;
562 }
563 else
564 {
565 grub_err_t err;
566 grub_disk_t disk;
567 grub_cryptodisk_t dev;
568
569 check_uuid = 0;
570 search_uuid = NULL;
571 disk = grub_disk_open (args[0]);
572 if (!disk)
573 return grub_errno;
574
575 dev = grub_cryptodisk_get_by_source_disk (disk);
576 if (dev)
577 {
578 grub_dprintf ("luks", "already mounted as luks%lu\n", dev->id);
579 grub_disk_close (disk);
580 return GRUB_ERR_NONE;
581 }
582
583 err = grub_geli_scan_device_real (args[0], disk);
584
585 grub_disk_close (disk);
586
587 return err;
588 }
589}
590
591static grub_extcmd_t cmd;
592
593GRUB_MOD_INIT (geli)
594{
595 cmd = grub_register_extcmd ("gelimount", grub_cmd_gelimount, 0,
596 N_("SOURCE|-u UUID|-a"),
597 N_("Mount a GELI device."), options);
598}
599
600GRUB_MOD_FINI (geli)
601{
602 grub_unregister_extcmd (cmd);
603}