]>
Commit | Line | Data |
---|---|---|
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 | ||
59 | GRUB_MOD_LICENSE ("GPLv3+"); | |
60 | ||
61 | struct 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 | ||
68 | struct 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 | 89 | const 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 | ||
103 | static 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 | ||
110 | static int check_uuid, have_it; | |
111 | static char *search_uuid; | |
112 | ||
88ac3146 VS |
113 | static gcry_err_code_t |
114 | geli_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 |
135 | static grub_cryptodisk_t |
136 | configure_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 | ||
251 | static grub_err_t | |
252 | recover_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 | ||
394 | static void | |
395 | close (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 | ||
403 | static grub_err_t | |
404 | grub_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 | |
447 | grub_err_t | |
448 | grub_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 | ||
496 | static int | |
497 | grub_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 | ||
518 | void | |
519 | grub_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 | ||
526 | static grub_err_t | |
527 | grub_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 | ||
591 | static grub_extcmd_t cmd; | |
592 | ||
593 | GRUB_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 | ||
600 | GRUB_MOD_FINI (geli) | |
601 | { | |
602 | grub_unregister_extcmd (cmd); | |
603 | } |