]>
Commit | Line | Data |
---|---|---|
1a1f408f VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
dd3f49b1 | 3 | * Copyright (C) 2003,2007,2010,2011,2019 Free Software Foundation, Inc. |
1a1f408f VS |
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 | #include <grub/cryptodisk.h> | |
20 | #include <grub/mm.h> | |
21 | #include <grub/misc.h> | |
22 | #include <grub/dl.h> | |
20a40940 VS |
23 | #include <grub/extcmd.h> |
24 | #include <grub/i18n.h> | |
ce50dbd7 VS |
25 | #include <grub/fs.h> |
26 | #include <grub/file.h> | |
27 | #include <grub/procfs.h> | |
c7f93a20 | 28 | #include <grub/partition.h> |
1a1f408f VS |
29 | |
30 | #ifdef GRUB_UTIL | |
1a1f408f | 31 | #include <grub/emu/hostdisk.h> |
1a1f408f VS |
32 | #endif |
33 | ||
34 | GRUB_MOD_LICENSE ("GPLv3+"); | |
35 | ||
20a40940 VS |
36 | grub_cryptodisk_dev_t grub_cryptodisk_list; |
37 | ||
38 | static const struct grub_arg_option options[] = | |
39 | { | |
40 | {"uuid", 'u', 0, N_("Mount by UUID."), 0, 0}, | |
40211ab8 | 41 | /* TRANSLATORS: It's still restricted to cryptodisks only. */ |
20a40940 | 42 | {"all", 'a', 0, N_("Mount all."), 0, 0}, |
9c4b5c13 | 43 | {"boot", 'b', 0, N_("Mount all volumes with `boot' flag set."), 0, 0}, |
20a40940 VS |
44 | {0, 0, 0, 0, 0, 0} |
45 | }; | |
46 | ||
1a1f408f VS |
47 | /* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ |
48 | #define GF_POLYNOM 0x87 | |
3e90811d VS |
49 | static inline int GF_PER_SECTOR (const struct grub_cryptodisk *dev) |
50 | { | |
51 | return 1U << (dev->log_sector_size - GRUB_CRYPTODISK_GF_LOG_BYTES); | |
52 | } | |
1a1f408f VS |
53 | |
54 | static grub_cryptodisk_t cryptodisk_list = NULL; | |
6f5f3337 | 55 | static grub_uint8_t last_cryptodisk_id = 0; |
1a1f408f VS |
56 | |
57 | static void | |
58 | gf_mul_x (grub_uint8_t *g) | |
59 | { | |
60 | int over = 0, over2 = 0; | |
3e90811d | 61 | unsigned j; |
1a1f408f VS |
62 | |
63 | for (j = 0; j < GRUB_CRYPTODISK_GF_BYTES; j++) | |
64 | { | |
65 | over2 = !!(g[j] & 0x80); | |
66 | g[j] <<= 1; | |
67 | g[j] |= over; | |
68 | over = over2; | |
69 | } | |
70 | if (over) | |
71 | g[0] ^= GF_POLYNOM; | |
72 | } | |
73 | ||
74 | ||
75 | static void | |
76 | gf_mul_x_be (grub_uint8_t *g) | |
77 | { | |
78 | int over = 0, over2 = 0; | |
79 | int j; | |
80 | ||
3e90811d | 81 | for (j = (int) GRUB_CRYPTODISK_GF_BYTES - 1; j >= 0; j--) |
1a1f408f VS |
82 | { |
83 | over2 = !!(g[j] & 0x80); | |
84 | g[j] <<= 1; | |
85 | g[j] |= over; | |
86 | over = over2; | |
87 | } | |
88 | if (over) | |
89 | g[GRUB_CRYPTODISK_GF_BYTES - 1] ^= GF_POLYNOM; | |
90 | } | |
91 | ||
92 | static void | |
93 | gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) | |
94 | { | |
3e90811d | 95 | unsigned i; |
1a1f408f VS |
96 | grub_uint8_t t[GRUB_CRYPTODISK_GF_BYTES]; |
97 | grub_memset (o, 0, GRUB_CRYPTODISK_GF_BYTES); | |
98 | grub_memcpy (t, b, GRUB_CRYPTODISK_GF_BYTES); | |
99 | for (i = 0; i < GRUB_CRYPTODISK_GF_SIZE; i++) | |
100 | { | |
1865baa7 | 101 | if (((a[GRUB_CRYPTODISK_GF_BYTES - i / GRUB_CHAR_BIT - 1] >> (i % GRUB_CHAR_BIT))) & 1) |
1a1f408f VS |
102 | grub_crypto_xor (o, o, t, GRUB_CRYPTODISK_GF_BYTES); |
103 | gf_mul_x_be (t); | |
104 | } | |
105 | } | |
106 | ||
107 | static gcry_err_code_t | |
108 | grub_crypto_pcbc_decrypt (grub_crypto_cipher_handle_t cipher, | |
109 | void *out, void *in, grub_size_t size, | |
110 | void *iv) | |
111 | { | |
112 | grub_uint8_t *inptr, *outptr, *end; | |
c35fcdc0 | 113 | grub_uint8_t ivt[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; |
94f701a8 | 114 | grub_size_t blocksize; |
1a1f408f VS |
115 | if (!cipher->cipher->decrypt) |
116 | return GPG_ERR_NOT_SUPPORTED; | |
94f701a8 VS |
117 | blocksize = cipher->cipher->blocksize; |
118 | if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) | |
119 | || ((size & (blocksize - 1)) != 0)) | |
120 | return GPG_ERR_INV_ARG; | |
121 | if (blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) | |
1a1f408f VS |
122 | return GPG_ERR_INV_ARG; |
123 | end = (grub_uint8_t *) in + size; | |
124 | for (inptr = in, outptr = out; inptr < end; | |
94f701a8 | 125 | inptr += blocksize, outptr += blocksize) |
1a1f408f | 126 | { |
94f701a8 | 127 | grub_memcpy (ivt, inptr, blocksize); |
1a1f408f | 128 | cipher->cipher->decrypt (cipher->ctx, outptr, inptr); |
94f701a8 VS |
129 | grub_crypto_xor (outptr, outptr, iv, blocksize); |
130 | grub_crypto_xor (iv, ivt, outptr, blocksize); | |
1a1f408f VS |
131 | } |
132 | return GPG_ERR_NO_ERROR; | |
133 | } | |
134 | ||
9c6e84b8 VS |
135 | static gcry_err_code_t |
136 | grub_crypto_pcbc_encrypt (grub_crypto_cipher_handle_t cipher, | |
137 | void *out, void *in, grub_size_t size, | |
138 | void *iv) | |
139 | { | |
140 | grub_uint8_t *inptr, *outptr, *end; | |
c35fcdc0 | 141 | grub_uint8_t ivt[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE]; |
94f701a8 VS |
142 | grub_size_t blocksize; |
143 | if (!cipher->cipher->encrypt) | |
9c6e84b8 | 144 | return GPG_ERR_NOT_SUPPORTED; |
94f701a8 VS |
145 | blocksize = cipher->cipher->blocksize; |
146 | if (blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) | |
147 | return GPG_ERR_INV_ARG; | |
148 | if (blocksize == 0 || (((blocksize - 1) & blocksize) != 0) | |
149 | || ((size & (blocksize - 1)) != 0)) | |
9c6e84b8 VS |
150 | return GPG_ERR_INV_ARG; |
151 | end = (grub_uint8_t *) in + size; | |
152 | for (inptr = in, outptr = out; inptr < end; | |
94f701a8 | 153 | inptr += blocksize, outptr += blocksize) |
9c6e84b8 | 154 | { |
94f701a8 VS |
155 | grub_memcpy (ivt, inptr, blocksize); |
156 | grub_crypto_xor (outptr, outptr, iv, blocksize); | |
9c6e84b8 | 157 | cipher->cipher->encrypt (cipher->ctx, outptr, inptr); |
94f701a8 | 158 | grub_crypto_xor (iv, ivt, outptr, blocksize); |
9c6e84b8 VS |
159 | } |
160 | return GPG_ERR_NO_ERROR; | |
161 | } | |
162 | ||
1a1f408f VS |
163 | struct lrw_sector |
164 | { | |
165 | grub_uint8_t low[GRUB_CRYPTODISK_GF_BYTES]; | |
166 | grub_uint8_t high[GRUB_CRYPTODISK_GF_BYTES]; | |
167 | grub_uint8_t low_byte, low_byte_c; | |
168 | }; | |
169 | ||
170 | static void | |
171 | generate_lrw_sector (struct lrw_sector *sec, | |
172 | const struct grub_cryptodisk *dev, | |
173 | const grub_uint8_t *iv) | |
174 | { | |
175 | grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; | |
176 | grub_uint16_t c; | |
177 | int j; | |
178 | grub_memcpy (idx, iv, GRUB_CRYPTODISK_GF_BYTES); | |
3e90811d VS |
179 | sec->low_byte = (idx[GRUB_CRYPTODISK_GF_BYTES - 1] |
180 | & (GF_PER_SECTOR (dev) - 1)); | |
181 | sec->low_byte_c = (((GF_PER_SECTOR (dev) - 1) & ~sec->low_byte) + 1); | |
182 | idx[GRUB_CRYPTODISK_GF_BYTES - 1] &= ~(GF_PER_SECTOR (dev) - 1); | |
1a1f408f VS |
183 | gf_mul_be (sec->low, dev->lrw_key, idx); |
184 | if (!sec->low_byte) | |
185 | return; | |
186 | ||
3e90811d | 187 | c = idx[GRUB_CRYPTODISK_GF_BYTES - 1] + GF_PER_SECTOR (dev); |
1a1f408f VS |
188 | if (c & 0x100) |
189 | { | |
190 | for (j = GRUB_CRYPTODISK_GF_BYTES - 2; j >= 0; j--) | |
191 | { | |
192 | idx[j]++; | |
193 | if (idx[j] != 0) | |
194 | break; | |
195 | } | |
196 | } | |
197 | idx[GRUB_CRYPTODISK_GF_BYTES - 1] = c; | |
198 | gf_mul_be (sec->high, dev->lrw_key, idx); | |
199 | } | |
200 | ||
201 | static void __attribute__ ((unused)) | |
202 | lrw_xor (const struct lrw_sector *sec, | |
203 | const struct grub_cryptodisk *dev, | |
204 | grub_uint8_t *b) | |
205 | { | |
3e90811d | 206 | unsigned i; |
1a1f408f | 207 | |
3e90811d VS |
208 | for (i = 0; i < sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; |
209 | i += GRUB_CRYPTODISK_GF_BYTES) | |
1a1f408f VS |
210 | grub_crypto_xor (b + i, b + i, sec->low, GRUB_CRYPTODISK_GF_BYTES); |
211 | grub_crypto_xor (b, b, dev->lrw_precalc + GRUB_CRYPTODISK_GF_BYTES * sec->low_byte, | |
212 | sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES); | |
213 | if (!sec->low_byte) | |
214 | return; | |
215 | ||
216 | for (i = sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES; | |
3e90811d | 217 | i < (1U << dev->log_sector_size); i += GRUB_CRYPTODISK_GF_BYTES) |
1a1f408f VS |
218 | grub_crypto_xor (b + i, b + i, sec->high, GRUB_CRYPTODISK_GF_BYTES); |
219 | grub_crypto_xor (b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, | |
220 | b + sec->low_byte_c * GRUB_CRYPTODISK_GF_BYTES, | |
221 | dev->lrw_precalc, sec->low_byte * GRUB_CRYPTODISK_GF_BYTES); | |
222 | } | |
223 | ||
9c6e84b8 VS |
224 | static gcry_err_code_t |
225 | grub_cryptodisk_endecrypt (struct grub_cryptodisk *dev, | |
226 | grub_uint8_t * data, grub_size_t len, | |
7b5784d4 | 227 | grub_disk_addr_t sector, int do_encrypt) |
1a1f408f VS |
228 | { |
229 | grub_size_t i; | |
230 | gcry_err_code_t err; | |
231 | ||
c35fcdc0 VS |
232 | if (dev->cipher->cipher->blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE) |
233 | return GPG_ERR_INV_ARG; | |
234 | ||
1a1f408f | 235 | /* The only mode without IV. */ |
88ac3146 | 236 | if (dev->mode == GRUB_CRYPTODISK_MODE_ECB && !dev->rekey) |
7b5784d4 | 237 | return (do_encrypt ? grub_crypto_ecb_encrypt (dev->cipher, data, data, len) |
9c6e84b8 | 238 | : grub_crypto_ecb_decrypt (dev->cipher, data, data, len)); |
1a1f408f | 239 | |
3e90811d | 240 | for (i = 0; i < len; i += (1U << dev->log_sector_size)) |
1a1f408f VS |
241 | { |
242 | grub_size_t sz = ((dev->cipher->cipher->blocksize | |
243 | + sizeof (grub_uint32_t) - 1) | |
244 | / sizeof (grub_uint32_t)); | |
c35fcdc0 | 245 | grub_uint32_t iv[(GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE + 3) / 4]; |
1a1f408f | 246 | |
88ac3146 VS |
247 | if (dev->rekey) |
248 | { | |
249 | grub_uint64_t zone = sector >> dev->rekey_shift; | |
250 | if (zone != dev->last_rekey) | |
251 | { | |
252 | err = dev->rekey (dev, zone); | |
253 | if (err) | |
254 | return err; | |
255 | dev->last_rekey = zone; | |
256 | } | |
257 | } | |
258 | ||
c35fcdc0 | 259 | grub_memset (iv, 0, sizeof (iv)); |
1a1f408f VS |
260 | switch (dev->mode_iv) |
261 | { | |
262 | case GRUB_CRYPTODISK_MODE_IV_NULL: | |
263 | break; | |
264 | case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH: | |
265 | { | |
266 | grub_uint64_t tmp; | |
c35fcdc0 | 267 | void *ctx; |
b44cd9e7 | 268 | |
c35fcdc0 VS |
269 | ctx = grub_zalloc (dev->iv_hash->contextsize); |
270 | if (!ctx) | |
271 | return GPG_ERR_OUT_OF_MEMORY; | |
b44cd9e7 | 272 | |
3e90811d | 273 | tmp = grub_cpu_to_le64 (sector << dev->log_sector_size); |
1a1f408f VS |
274 | dev->iv_hash->init (ctx); |
275 | dev->iv_hash->write (ctx, dev->iv_prefix, dev->iv_prefix_len); | |
276 | dev->iv_hash->write (ctx, &tmp, sizeof (tmp)); | |
277 | dev->iv_hash->final (ctx); | |
278 | ||
279 | grub_memcpy (iv, dev->iv_hash->read (ctx), sizeof (iv)); | |
c35fcdc0 | 280 | grub_free (ctx); |
1a1f408f VS |
281 | } |
282 | break; | |
283 | case GRUB_CRYPTODISK_MODE_IV_PLAIN64: | |
284 | iv[1] = grub_cpu_to_le32 (sector >> 32); | |
4bd4a887 | 285 | /* FALLTHROUGH */ |
1a1f408f VS |
286 | case GRUB_CRYPTODISK_MODE_IV_PLAIN: |
287 | iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); | |
288 | break; | |
171e2be1 VS |
289 | case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64: |
290 | iv[1] = grub_cpu_to_le32 (sector >> (32 - dev->log_sector_size)); | |
291 | iv[0] = grub_cpu_to_le32 ((sector << dev->log_sector_size) | |
292 | & 0xFFFFFFFF); | |
293 | break; | |
1a1f408f VS |
294 | case GRUB_CRYPTODISK_MODE_IV_BENBI: |
295 | { | |
296 | grub_uint64_t num = (sector << dev->benbi_log) + 1; | |
297 | iv[sz - 2] = grub_cpu_to_be32 (num >> 32); | |
298 | iv[sz - 1] = grub_cpu_to_be32 (num & 0xFFFFFFFF); | |
299 | } | |
300 | break; | |
301 | case GRUB_CRYPTODISK_MODE_IV_ESSIV: | |
302 | iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); | |
303 | err = grub_crypto_ecb_encrypt (dev->essiv_cipher, iv, iv, | |
304 | dev->cipher->cipher->blocksize); | |
305 | if (err) | |
306 | return err; | |
307 | } | |
308 | ||
309 | switch (dev->mode) | |
310 | { | |
311 | case GRUB_CRYPTODISK_MODE_CBC: | |
7b5784d4 | 312 | if (do_encrypt) |
9c6e84b8 VS |
313 | err = grub_crypto_cbc_encrypt (dev->cipher, data + i, data + i, |
314 | (1U << dev->log_sector_size), iv); | |
315 | else | |
316 | err = grub_crypto_cbc_decrypt (dev->cipher, data + i, data + i, | |
317 | (1U << dev->log_sector_size), iv); | |
1a1f408f VS |
318 | if (err) |
319 | return err; | |
320 | break; | |
321 | ||
322 | case GRUB_CRYPTODISK_MODE_PCBC: | |
7b5784d4 | 323 | if (do_encrypt) |
9c6e84b8 VS |
324 | err = grub_crypto_pcbc_encrypt (dev->cipher, data + i, data + i, |
325 | (1U << dev->log_sector_size), iv); | |
326 | else | |
327 | err = grub_crypto_pcbc_decrypt (dev->cipher, data + i, data + i, | |
328 | (1U << dev->log_sector_size), iv); | |
1a1f408f VS |
329 | if (err) |
330 | return err; | |
331 | break; | |
332 | case GRUB_CRYPTODISK_MODE_XTS: | |
333 | { | |
3e90811d | 334 | unsigned j; |
1a1f408f VS |
335 | err = grub_crypto_ecb_encrypt (dev->secondary_cipher, iv, iv, |
336 | dev->cipher->cipher->blocksize); | |
337 | if (err) | |
338 | return err; | |
339 | ||
3e90811d | 340 | for (j = 0; j < (1U << dev->log_sector_size); |
1a1f408f VS |
341 | j += dev->cipher->cipher->blocksize) |
342 | { | |
343 | grub_crypto_xor (data + i + j, data + i + j, iv, | |
344 | dev->cipher->cipher->blocksize); | |
7b5784d4 | 345 | if (do_encrypt) |
9c6e84b8 VS |
346 | err = grub_crypto_ecb_encrypt (dev->cipher, data + i + j, |
347 | data + i + j, | |
348 | dev->cipher->cipher->blocksize); | |
349 | else | |
350 | err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, | |
351 | data + i + j, | |
352 | dev->cipher->cipher->blocksize); | |
1a1f408f VS |
353 | if (err) |
354 | return err; | |
355 | grub_crypto_xor (data + i + j, data + i + j, iv, | |
356 | dev->cipher->cipher->blocksize); | |
357 | gf_mul_x ((grub_uint8_t *) iv); | |
358 | } | |
359 | } | |
360 | break; | |
361 | case GRUB_CRYPTODISK_MODE_LRW: | |
362 | { | |
363 | struct lrw_sector sec; | |
364 | ||
365 | generate_lrw_sector (&sec, dev, (grub_uint8_t *) iv); | |
366 | lrw_xor (&sec, dev, data + i); | |
367 | ||
7b5784d4 | 368 | if (do_encrypt) |
9c6e84b8 VS |
369 | err = grub_crypto_ecb_encrypt (dev->cipher, data + i, |
370 | data + i, | |
371 | (1U << dev->log_sector_size)); | |
372 | else | |
373 | err = grub_crypto_ecb_decrypt (dev->cipher, data + i, | |
374 | data + i, | |
375 | (1U << dev->log_sector_size)); | |
1a1f408f VS |
376 | if (err) |
377 | return err; | |
378 | lrw_xor (&sec, dev, data + i); | |
379 | } | |
380 | break; | |
88ac3146 | 381 | case GRUB_CRYPTODISK_MODE_ECB: |
7b5784d4 | 382 | if (do_encrypt) |
4c7337bf VS |
383 | err = grub_crypto_ecb_encrypt (dev->cipher, data + i, data + i, |
384 | (1U << dev->log_sector_size)); | |
9c6e84b8 | 385 | else |
4c7337bf VS |
386 | err = grub_crypto_ecb_decrypt (dev->cipher, data + i, data + i, |
387 | (1U << dev->log_sector_size)); | |
388 | if (err) | |
389 | return err; | |
88ac3146 | 390 | break; |
1a1f408f VS |
391 | default: |
392 | return GPG_ERR_NOT_IMPLEMENTED; | |
393 | } | |
394 | sector++; | |
395 | } | |
396 | return GPG_ERR_NO_ERROR; | |
397 | } | |
398 | ||
9c6e84b8 VS |
399 | gcry_err_code_t |
400 | grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, | |
401 | grub_uint8_t * data, grub_size_t len, | |
402 | grub_disk_addr_t sector) | |
403 | { | |
404 | return grub_cryptodisk_endecrypt (dev, data, len, sector, 0); | |
405 | } | |
406 | ||
dd3f49b1 PS |
407 | grub_err_t |
408 | grub_cryptodisk_setcipher (grub_cryptodisk_t crypt, const char *ciphername, const char *ciphermode) | |
409 | { | |
410 | const char *cipheriv = NULL; | |
411 | grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; | |
412 | grub_crypto_cipher_handle_t essiv_cipher = NULL; | |
413 | const gcry_md_spec_t *essiv_hash = NULL; | |
414 | const struct gcry_cipher_spec *ciph; | |
415 | grub_cryptodisk_mode_t mode; | |
416 | grub_cryptodisk_mode_iv_t mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN64; | |
417 | int benbi_log = 0; | |
418 | grub_err_t ret = GRUB_ERR_NONE; | |
419 | ||
420 | ciph = grub_crypto_lookup_cipher_by_name (ciphername); | |
421 | if (!ciph) | |
422 | { | |
423 | ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", | |
424 | ciphername); | |
425 | goto err; | |
426 | } | |
427 | ||
428 | /* Configure the cipher used for the bulk data. */ | |
429 | cipher = grub_crypto_cipher_open (ciph); | |
430 | if (!cipher) | |
431 | { | |
432 | ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s could not be initialized", | |
433 | ciphername); | |
434 | goto err; | |
435 | } | |
436 | ||
437 | /* Configure the cipher mode. */ | |
438 | if (grub_strcmp (ciphermode, "ecb") == 0) | |
439 | { | |
440 | mode = GRUB_CRYPTODISK_MODE_ECB; | |
441 | mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; | |
442 | cipheriv = NULL; | |
443 | } | |
444 | else if (grub_strcmp (ciphermode, "plain") == 0) | |
445 | { | |
446 | mode = GRUB_CRYPTODISK_MODE_CBC; | |
447 | mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; | |
448 | cipheriv = NULL; | |
449 | } | |
450 | else if (grub_memcmp (ciphermode, "cbc-", sizeof ("cbc-") - 1) == 0) | |
451 | { | |
452 | mode = GRUB_CRYPTODISK_MODE_CBC; | |
453 | cipheriv = ciphermode + sizeof ("cbc-") - 1; | |
454 | } | |
455 | else if (grub_memcmp (ciphermode, "pcbc-", sizeof ("pcbc-") - 1) == 0) | |
456 | { | |
457 | mode = GRUB_CRYPTODISK_MODE_PCBC; | |
458 | cipheriv = ciphermode + sizeof ("pcbc-") - 1; | |
459 | } | |
460 | else if (grub_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0) | |
461 | { | |
462 | mode = GRUB_CRYPTODISK_MODE_XTS; | |
463 | cipheriv = ciphermode + sizeof ("xts-") - 1; | |
464 | secondary_cipher = grub_crypto_cipher_open (ciph); | |
465 | if (!secondary_cipher) | |
466 | { | |
84ff10b1 GW |
467 | ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, |
468 | "Secondary cipher %s isn't available", ciphername); | |
dd3f49b1 PS |
469 | goto err; |
470 | } | |
471 | if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) | |
472 | { | |
473 | ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", | |
474 | cipher->cipher->blocksize); | |
475 | goto err; | |
476 | } | |
477 | if (secondary_cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) | |
478 | { | |
479 | ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", | |
480 | secondary_cipher->cipher->blocksize); | |
481 | goto err; | |
482 | } | |
483 | } | |
484 | else if (grub_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0) | |
485 | { | |
486 | mode = GRUB_CRYPTODISK_MODE_LRW; | |
487 | cipheriv = ciphermode + sizeof ("lrw-") - 1; | |
488 | if (cipher->cipher->blocksize != GRUB_CRYPTODISK_GF_BYTES) | |
489 | { | |
490 | ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d", | |
491 | cipher->cipher->blocksize); | |
492 | goto err; | |
493 | } | |
494 | } | |
495 | else | |
496 | { | |
497 | ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", | |
498 | ciphermode); | |
499 | goto err; | |
500 | } | |
501 | ||
502 | if (cipheriv == NULL) | |
503 | ; | |
dd3f49b1 PS |
504 | else if (grub_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0) |
505 | mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN64; | |
6355ba91 GW |
506 | else if (grub_memcmp (cipheriv, "plain", sizeof ("plain") - 1) == 0) |
507 | mode_iv = GRUB_CRYPTODISK_MODE_IV_PLAIN; | |
dd3f49b1 PS |
508 | else if (grub_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0) |
509 | { | |
510 | if (cipher->cipher->blocksize & (cipher->cipher->blocksize - 1) | |
511 | || cipher->cipher->blocksize == 0) | |
512 | grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported benbi blocksize: %d", | |
513 | cipher->cipher->blocksize); | |
514 | /* FIXME should we return an error here? */ | |
515 | for (benbi_log = 0; | |
516 | (cipher->cipher->blocksize << benbi_log) < GRUB_DISK_SECTOR_SIZE; | |
517 | benbi_log++); | |
518 | mode_iv = GRUB_CRYPTODISK_MODE_IV_BENBI; | |
519 | } | |
520 | else if (grub_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0) | |
521 | mode_iv = GRUB_CRYPTODISK_MODE_IV_NULL; | |
522 | else if (grub_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0) | |
523 | { | |
524 | const char *hash_str = cipheriv + 6; | |
525 | ||
526 | mode_iv = GRUB_CRYPTODISK_MODE_IV_ESSIV; | |
527 | ||
528 | /* Configure the hash and cipher used for ESSIV. */ | |
529 | essiv_hash = grub_crypto_lookup_md_by_name (hash_str); | |
530 | if (!essiv_hash) | |
531 | { | |
532 | ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, | |
533 | "Couldn't load %s hash", hash_str); | |
534 | goto err; | |
535 | } | |
536 | essiv_cipher = grub_crypto_cipher_open (ciph); | |
537 | if (!essiv_cipher) | |
538 | { | |
539 | ret = grub_error (GRUB_ERR_FILE_NOT_FOUND, | |
540 | "Couldn't load %s cipher", ciphername); | |
541 | goto err; | |
542 | } | |
543 | } | |
544 | else | |
545 | { | |
546 | ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Unknown IV mode: %s", | |
547 | cipheriv); | |
548 | goto err; | |
549 | } | |
550 | ||
551 | crypt->cipher = cipher; | |
552 | crypt->benbi_log = benbi_log; | |
553 | crypt->mode = mode; | |
554 | crypt->mode_iv = mode_iv; | |
555 | crypt->secondary_cipher = secondary_cipher; | |
556 | crypt->essiv_cipher = essiv_cipher; | |
557 | crypt->essiv_hash = essiv_hash; | |
558 | ||
559 | err: | |
560 | if (ret) | |
561 | { | |
562 | grub_crypto_cipher_close (cipher); | |
563 | grub_crypto_cipher_close (secondary_cipher); | |
564 | } | |
565 | return ret; | |
566 | } | |
567 | ||
1a1f408f VS |
568 | gcry_err_code_t |
569 | grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize) | |
570 | { | |
571 | gcry_err_code_t err; | |
572 | int real_keysize; | |
573 | ||
574 | real_keysize = keysize; | |
575 | if (dev->mode == GRUB_CRYPTODISK_MODE_XTS) | |
576 | real_keysize /= 2; | |
577 | if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) | |
578 | real_keysize -= dev->cipher->cipher->blocksize; | |
579 | ||
580 | /* Set the PBKDF2 output as the cipher key. */ | |
581 | err = grub_crypto_cipher_set_key (dev->cipher, key, real_keysize); | |
582 | if (err) | |
583 | return err; | |
ce50dbd7 VS |
584 | grub_memcpy (dev->key, key, keysize); |
585 | dev->keysize = keysize; | |
1a1f408f VS |
586 | |
587 | /* Configure ESSIV if necessary. */ | |
588 | if (dev->mode_iv == GRUB_CRYPTODISK_MODE_IV_ESSIV) | |
589 | { | |
590 | grub_size_t essiv_keysize = dev->essiv_hash->mdlen; | |
c35fcdc0 VS |
591 | grub_uint8_t hashed_key[GRUB_CRYPTO_MAX_MDLEN]; |
592 | if (essiv_keysize > GRUB_CRYPTO_MAX_MDLEN) | |
593 | return GPG_ERR_INV_ARG; | |
1a1f408f VS |
594 | |
595 | grub_crypto_hash (dev->essiv_hash, hashed_key, key, keysize); | |
596 | err = grub_crypto_cipher_set_key (dev->essiv_cipher, | |
597 | hashed_key, essiv_keysize); | |
598 | if (err) | |
599 | return err; | |
600 | } | |
601 | if (dev->mode == GRUB_CRYPTODISK_MODE_XTS) | |
602 | { | |
603 | err = grub_crypto_cipher_set_key (dev->secondary_cipher, | |
604 | key + real_keysize, | |
605 | keysize / 2); | |
606 | if (err) | |
607 | return err; | |
608 | } | |
609 | ||
610 | if (dev->mode == GRUB_CRYPTODISK_MODE_LRW) | |
611 | { | |
3e90811d | 612 | unsigned i; |
1a1f408f VS |
613 | grub_uint8_t idx[GRUB_CRYPTODISK_GF_BYTES]; |
614 | ||
615 | grub_free (dev->lrw_precalc); | |
616 | grub_memcpy (dev->lrw_key, key + real_keysize, | |
617 | dev->cipher->cipher->blocksize); | |
3e90811d | 618 | dev->lrw_precalc = grub_malloc ((1U << dev->log_sector_size)); |
1a1f408f VS |
619 | if (!dev->lrw_precalc) |
620 | return GPG_ERR_OUT_OF_MEMORY; | |
621 | grub_memset (idx, 0, GRUB_CRYPTODISK_GF_BYTES); | |
3e90811d | 622 | for (i = 0; i < (1U << dev->log_sector_size); |
1a1f408f VS |
623 | i += GRUB_CRYPTODISK_GF_BYTES) |
624 | { | |
625 | idx[GRUB_CRYPTODISK_GF_BYTES - 1] = i / GRUB_CRYPTODISK_GF_BYTES; | |
626 | gf_mul_be (dev->lrw_precalc + i, idx, dev->lrw_key); | |
627 | } | |
628 | } | |
629 | return GPG_ERR_NO_ERROR; | |
630 | } | |
631 | ||
632 | static int | |
25239370 CW |
633 | grub_cryptodisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, |
634 | grub_disk_pull_t pull) | |
1a1f408f VS |
635 | { |
636 | grub_cryptodisk_t i; | |
637 | ||
638 | if (pull != GRUB_DISK_PULL_NONE) | |
639 | return 0; | |
640 | ||
641 | for (i = cryptodisk_list; i != NULL; i = i->next) | |
642 | { | |
643 | char buf[30]; | |
644 | grub_snprintf (buf, sizeof (buf), "crypto%lu", i->id); | |
25239370 | 645 | if (hook (buf, hook_data)) |
1a1f408f VS |
646 | return 1; |
647 | } | |
648 | ||
649 | return GRUB_ERR_NONE; | |
650 | } | |
651 | ||
652 | static grub_err_t | |
cd8fe79a | 653 | grub_cryptodisk_open (const char *name, grub_disk_t disk) |
1a1f408f VS |
654 | { |
655 | grub_cryptodisk_t dev; | |
656 | ||
657 | if (grub_memcmp (name, "crypto", sizeof ("crypto") - 1) != 0) | |
658 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); | |
659 | ||
660 | if (grub_memcmp (name, "cryptouuid/", sizeof ("cryptouuid/") - 1) == 0) | |
661 | { | |
662 | for (dev = cryptodisk_list; dev != NULL; dev = dev->next) | |
663 | if (grub_strcasecmp (name + sizeof ("cryptouuid/") - 1, dev->uuid) == 0) | |
664 | break; | |
665 | } | |
666 | else | |
667 | { | |
668 | unsigned long id = grub_strtoul (name + sizeof ("crypto") - 1, 0, 0); | |
669 | if (grub_errno) | |
670 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); | |
671 | /* Search for requested device in the list of CRYPTODISK devices. */ | |
672 | for (dev = cryptodisk_list; dev != NULL; dev = dev->next) | |
673 | if (dev->id == id) | |
674 | break; | |
675 | } | |
676 | if (!dev) | |
677 | return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No such device"); | |
678 | ||
3e90811d VS |
679 | disk->log_sector_size = dev->log_sector_size; |
680 | ||
1a1f408f VS |
681 | #ifdef GRUB_UTIL |
682 | if (dev->cheat) | |
683 | { | |
a47a78be | 684 | if (!GRUB_UTIL_FD_IS_VALID (dev->cheat_fd)) |
6de292cb | 685 | dev->cheat_fd = grub_util_fd_open (dev->cheat, GRUB_UTIL_FD_O_RDONLY); |
a47a78be | 686 | if (!GRUB_UTIL_FD_IS_VALID (dev->cheat_fd)) |
9c4b5c13 | 687 | return grub_error (GRUB_ERR_IO, N_("cannot open `%s': %s"), |
20d53541 | 688 | dev->cheat, grub_util_fd_strerror ()); |
1a1f408f VS |
689 | } |
690 | #endif | |
691 | ||
692 | if (!dev->source_disk) | |
693 | { | |
694 | grub_dprintf ("cryptodisk", "Opening device %s\n", name); | |
695 | /* Try to open the source disk and populate the requested disk. */ | |
696 | dev->source_disk = grub_disk_open (dev->source); | |
697 | if (!dev->source_disk) | |
698 | return grub_errno; | |
699 | } | |
700 | ||
701 | disk->data = dev; | |
535998c2 | 702 | disk->total_sectors = dev->total_sectors; |
cb72aa18 | 703 | disk->max_agglomerate = GRUB_DISK_MAX_MAX_AGGLOMERATE; |
1a1f408f VS |
704 | disk->id = dev->id; |
705 | dev->ref++; | |
706 | return GRUB_ERR_NONE; | |
707 | } | |
708 | ||
709 | static void | |
710 | grub_cryptodisk_close (grub_disk_t disk) | |
711 | { | |
712 | grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; | |
713 | grub_dprintf ("cryptodisk", "Closing disk\n"); | |
714 | ||
715 | dev->ref--; | |
716 | ||
717 | if (dev->ref != 0) | |
718 | return; | |
719 | #ifdef GRUB_UTIL | |
720 | if (dev->cheat) | |
721 | { | |
a47a78be VS |
722 | grub_util_fd_close (dev->cheat_fd); |
723 | dev->cheat_fd = GRUB_UTIL_FD_INVALID; | |
1a1f408f VS |
724 | } |
725 | #endif | |
726 | grub_disk_close (dev->source_disk); | |
727 | dev->source_disk = NULL; | |
728 | } | |
729 | ||
730 | static grub_err_t | |
731 | grub_cryptodisk_read (grub_disk_t disk, grub_disk_addr_t sector, | |
20a40940 | 732 | grub_size_t size, char *buf) |
1a1f408f VS |
733 | { |
734 | grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; | |
735 | grub_err_t err; | |
736 | gcry_err_code_t gcry_err; | |
737 | ||
738 | #ifdef GRUB_UTIL | |
739 | if (dev->cheat) | |
740 | { | |
b73249d2 VS |
741 | int r; |
742 | r = grub_util_fd_seek (dev->cheat_fd, sector << disk->log_sector_size); | |
743 | if (r) | |
744 | return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), | |
745 | dev->cheat, grub_util_fd_strerror ()); | |
572e3ea6 VS |
746 | if (grub_util_fd_read (dev->cheat_fd, buf, size << disk->log_sector_size) |
747 | != (ssize_t) (size << disk->log_sector_size)) | |
9c4b5c13 | 748 | return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"), |
20d53541 | 749 | dev->cheat, grub_util_fd_strerror ()); |
1a1f408f VS |
750 | return GRUB_ERR_NONE; |
751 | } | |
752 | #endif | |
753 | ||
754 | grub_dprintf ("cryptodisk", | |
755 | "Reading %" PRIuGRUB_SIZE " sectors from sector 0x%" | |
756 | PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n", | |
757 | size, sector, dev->offset); | |
758 | ||
572e3ea6 | 759 | err = grub_disk_read (dev->source_disk, |
e1b0992a GW |
760 | grub_disk_from_native_sector (disk, sector + dev->offset), |
761 | 0, size << disk->log_sector_size, buf); | |
1a1f408f VS |
762 | if (err) |
763 | { | |
764 | grub_dprintf ("cryptodisk", "grub_disk_read failed with error %d\n", err); | |
765 | return err; | |
766 | } | |
9c6e84b8 VS |
767 | gcry_err = grub_cryptodisk_endecrypt (dev, (grub_uint8_t *) buf, |
768 | size << disk->log_sector_size, | |
769 | sector, 0); | |
1a1f408f VS |
770 | return grub_crypto_gcry_error (gcry_err); |
771 | } | |
772 | ||
773 | static grub_err_t | |
9c6e84b8 VS |
774 | grub_cryptodisk_write (grub_disk_t disk, grub_disk_addr_t sector, |
775 | grub_size_t size, const char *buf) | |
1a1f408f | 776 | { |
9c6e84b8 VS |
777 | grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; |
778 | gcry_err_code_t gcry_err; | |
779 | char *tmp; | |
780 | grub_err_t err; | |
781 | ||
782 | #ifdef GRUB_UTIL | |
783 | if (dev->cheat) | |
784 | { | |
b73249d2 VS |
785 | int r; |
786 | r = grub_util_fd_seek (dev->cheat_fd, sector << disk->log_sector_size); | |
787 | if (r) | |
788 | return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), | |
789 | dev->cheat, grub_util_fd_strerror ()); | |
9c6e84b8 VS |
790 | if (grub_util_fd_write (dev->cheat_fd, buf, size << disk->log_sector_size) |
791 | != (ssize_t) (size << disk->log_sector_size)) | |
9c4b5c13 | 792 | return grub_error (GRUB_ERR_READ_ERROR, N_("cannot read `%s': %s"), |
20d53541 | 793 | dev->cheat, grub_util_fd_strerror ()); |
9c6e84b8 VS |
794 | return GRUB_ERR_NONE; |
795 | } | |
796 | #endif | |
797 | ||
798 | tmp = grub_malloc (size << disk->log_sector_size); | |
799 | if (!tmp) | |
800 | return grub_errno; | |
801 | grub_memcpy (tmp, buf, size << disk->log_sector_size); | |
802 | ||
803 | grub_dprintf ("cryptodisk", | |
804 | "Writing %" PRIuGRUB_SIZE " sectors to sector 0x%" | |
805 | PRIxGRUB_UINT64_T " with offset of %" PRIuGRUB_UINT64_T "\n", | |
806 | size, sector, dev->offset); | |
807 | ||
808 | gcry_err = grub_cryptodisk_endecrypt (dev, (grub_uint8_t *) tmp, | |
809 | size << disk->log_sector_size, | |
810 | sector, 1); | |
811 | if (gcry_err) | |
812 | { | |
813 | grub_free (tmp); | |
814 | return grub_crypto_gcry_error (gcry_err); | |
815 | } | |
816 | ||
442b86de | 817 | /* Since ->write was called so disk.mod is loaded but be paranoid */ |
e1b0992a | 818 | sector = sector + dev->offset; |
442b86de VS |
819 | if (grub_disk_write_weak) |
820 | err = grub_disk_write_weak (dev->source_disk, | |
e1b0992a | 821 | grub_disk_from_native_sector (disk, sector), |
442b86de VS |
822 | 0, size << disk->log_sector_size, tmp); |
823 | else | |
824 | err = grub_error (GRUB_ERR_BUG, "disk.mod not loaded"); | |
9c6e84b8 VS |
825 | grub_free (tmp); |
826 | return err; | |
1a1f408f VS |
827 | } |
828 | ||
829 | #ifdef GRUB_UTIL | |
830 | static grub_disk_memberlist_t | |
831 | grub_cryptodisk_memberlist (grub_disk_t disk) | |
832 | { | |
833 | grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; | |
834 | grub_disk_memberlist_t list = NULL; | |
835 | ||
836 | list = grub_malloc (sizeof (*list)); | |
837 | if (list) | |
838 | { | |
839 | list->disk = dev->source_disk; | |
840 | list->next = NULL; | |
841 | } | |
842 | ||
843 | return list; | |
844 | } | |
845 | #endif | |
846 | ||
847 | static void | |
848 | cryptodisk_cleanup (void) | |
849 | { | |
63fe43f3 | 850 | #if 0 |
1a1f408f VS |
851 | grub_cryptodisk_t dev = cryptodisk_list; |
852 | grub_cryptodisk_t tmp; | |
853 | ||
854 | while (dev != NULL) | |
855 | { | |
856 | grub_free (dev->source); | |
857 | grub_free (dev->cipher); | |
858 | grub_free (dev->secondary_cipher); | |
859 | grub_free (dev->essiv_cipher); | |
860 | tmp = dev->next; | |
861 | grub_free (dev); | |
862 | dev = tmp; | |
863 | } | |
63fe43f3 | 864 | #endif |
1a1f408f VS |
865 | } |
866 | ||
867 | grub_err_t | |
868 | grub_cryptodisk_insert (grub_cryptodisk_t newdev, const char *name, | |
869 | grub_disk_t source) | |
870 | { | |
871 | newdev->source = grub_strdup (name); | |
872 | if (!newdev->source) | |
873 | { | |
874 | grub_free (newdev); | |
875 | return grub_errno; | |
876 | } | |
877 | ||
6f5f3337 | 878 | newdev->id = last_cryptodisk_id++; |
1a1f408f VS |
879 | newdev->source_id = source->id; |
880 | newdev->source_dev_id = source->dev->id; | |
c7f93a20 | 881 | newdev->partition_start = grub_partition_get_start (source->partition); |
1a1f408f VS |
882 | newdev->next = cryptodisk_list; |
883 | cryptodisk_list = newdev; | |
884 | ||
885 | return GRUB_ERR_NONE; | |
886 | } | |
887 | ||
888 | grub_cryptodisk_t | |
889 | grub_cryptodisk_get_by_uuid (const char *uuid) | |
890 | { | |
891 | grub_cryptodisk_t dev; | |
892 | for (dev = cryptodisk_list; dev != NULL; dev = dev->next) | |
893 | if (grub_strcasecmp (dev->uuid, uuid) == 0) | |
894 | return dev; | |
895 | return NULL; | |
896 | } | |
897 | ||
898 | grub_cryptodisk_t | |
899 | grub_cryptodisk_get_by_source_disk (grub_disk_t disk) | |
900 | { | |
901 | grub_cryptodisk_t dev; | |
902 | for (dev = cryptodisk_list; dev != NULL; dev = dev->next) | |
903 | if (dev->source_id == disk->id && dev->source_dev_id == disk->dev->id) | |
c7f93a20 | 904 | if ((disk->partition && grub_partition_get_start (disk->partition) == dev->partition_start) || |
905 | (!disk->partition && dev->partition_start == 0)) | |
906 | return dev; | |
1a1f408f VS |
907 | return NULL; |
908 | } | |
909 | ||
910 | #ifdef GRUB_UTIL | |
911 | grub_err_t | |
912 | grub_cryptodisk_cheat_insert (grub_cryptodisk_t newdev, const char *name, | |
913 | grub_disk_t source, const char *cheat) | |
914 | { | |
915 | newdev->cheat = grub_strdup (cheat); | |
916 | newdev->source = grub_strdup (name); | |
917 | if (!newdev->source || !newdev->cheat) | |
918 | { | |
919 | grub_free (newdev->source); | |
920 | grub_free (newdev->cheat); | |
921 | return grub_errno; | |
922 | } | |
923 | ||
a47a78be | 924 | newdev->cheat_fd = GRUB_UTIL_FD_INVALID; |
1a1f408f VS |
925 | newdev->source_id = source->id; |
926 | newdev->source_dev_id = source->dev->id; | |
c7f93a20 | 927 | newdev->partition_start = grub_partition_get_start (source->partition); |
6f5f3337 | 928 | newdev->id = last_cryptodisk_id++; |
1a1f408f VS |
929 | newdev->next = cryptodisk_list; |
930 | cryptodisk_list = newdev; | |
931 | ||
932 | return GRUB_ERR_NONE; | |
933 | } | |
934 | ||
935 | void | |
bf25f879 | 936 | grub_util_cryptodisk_get_abstraction (grub_disk_t disk, |
24024dac CW |
937 | void (*cb) (const char *val, void *data), |
938 | void *data) | |
1a1f408f VS |
939 | { |
940 | grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; | |
941 | ||
24024dac CW |
942 | cb ("cryptodisk", data); |
943 | cb (dev->modname, data); | |
1a1f408f VS |
944 | |
945 | if (dev->cipher) | |
24024dac | 946 | cb (dev->cipher->cipher->modname, data); |
1a1f408f | 947 | if (dev->secondary_cipher) |
24024dac | 948 | cb (dev->secondary_cipher->cipher->modname, data); |
1a1f408f | 949 | if (dev->essiv_cipher) |
24024dac | 950 | cb (dev->essiv_cipher->cipher->modname, data); |
1a1f408f | 951 | if (dev->hash) |
24024dac | 952 | cb (dev->hash->modname, data); |
1a1f408f | 953 | if (dev->essiv_hash) |
24024dac | 954 | cb (dev->essiv_hash->modname, data); |
1a1f408f | 955 | if (dev->iv_hash) |
24024dac | 956 | cb (dev->iv_hash->modname, data); |
1a1f408f | 957 | } |
20a40940 | 958 | |
bf25f879 VS |
959 | const char * |
960 | grub_util_cryptodisk_get_uuid (grub_disk_t disk) | |
20a40940 VS |
961 | { |
962 | grub_cryptodisk_t dev = (grub_cryptodisk_t) disk->data; | |
bf25f879 | 963 | return dev->uuid; |
20a40940 VS |
964 | } |
965 | ||
1a1f408f VS |
966 | #endif |
967 | ||
20a40940 VS |
968 | static int check_boot, have_it; |
969 | static char *search_uuid; | |
970 | ||
971 | static void | |
972 | cryptodisk_close (grub_cryptodisk_t dev) | |
973 | { | |
974 | grub_crypto_cipher_close (dev->cipher); | |
975 | grub_crypto_cipher_close (dev->secondary_cipher); | |
976 | grub_crypto_cipher_close (dev->essiv_cipher); | |
977 | grub_free (dev); | |
978 | } | |
979 | ||
980 | static grub_err_t | |
981 | grub_cryptodisk_scan_device_real (const char *name, grub_disk_t source) | |
982 | { | |
983 | grub_err_t err; | |
984 | grub_cryptodisk_t dev; | |
985 | grub_cryptodisk_dev_t cr; | |
986 | ||
987 | dev = grub_cryptodisk_get_by_source_disk (source); | |
988 | ||
989 | if (dev) | |
990 | return GRUB_ERR_NONE; | |
991 | ||
992 | FOR_CRYPTODISK_DEVS (cr) | |
993 | { | |
994 | dev = cr->scan (source, search_uuid, check_boot); | |
995 | if (grub_errno) | |
996 | return grub_errno; | |
997 | if (!dev) | |
998 | continue; | |
999 | ||
1000 | err = cr->recover_key (source, dev); | |
1001 | if (err) | |
1002 | { | |
1003 | cryptodisk_close (dev); | |
1004 | return err; | |
1005 | } | |
1006 | ||
1007 | grub_cryptodisk_insert (dev, name, source); | |
1008 | ||
1009 | have_it = 1; | |
1010 | ||
1011 | return GRUB_ERR_NONE; | |
1012 | } | |
1013 | return GRUB_ERR_NONE; | |
1014 | } | |
1015 | ||
1016 | #ifdef GRUB_UTIL | |
1017 | #include <grub/util/misc.h> | |
1018 | grub_err_t | |
1019 | grub_cryptodisk_cheat_mount (const char *sourcedev, const char *cheat) | |
1020 | { | |
1021 | grub_err_t err; | |
1022 | grub_cryptodisk_t dev; | |
1023 | grub_cryptodisk_dev_t cr; | |
1024 | grub_disk_t source; | |
1025 | ||
1026 | /* Try to open disk. */ | |
1027 | source = grub_disk_open (sourcedev); | |
1028 | if (!source) | |
1029 | return grub_errno; | |
1030 | ||
1031 | dev = grub_cryptodisk_get_by_source_disk (source); | |
1032 | ||
1033 | if (dev) | |
1034 | { | |
1035 | grub_disk_close (source); | |
1036 | return GRUB_ERR_NONE; | |
1037 | } | |
1038 | ||
1039 | FOR_CRYPTODISK_DEVS (cr) | |
1040 | { | |
1041 | dev = cr->scan (source, search_uuid, check_boot); | |
1042 | if (grub_errno) | |
1043 | return grub_errno; | |
1044 | if (!dev) | |
1045 | continue; | |
1046 | ||
1047 | grub_util_info ("cheatmounted %s (%s) at %s", sourcedev, dev->modname, | |
1048 | cheat); | |
1049 | err = grub_cryptodisk_cheat_insert (dev, sourcedev, source, cheat); | |
1050 | grub_disk_close (source); | |
1051 | if (err) | |
1052 | grub_free (dev); | |
1053 | ||
1054 | return GRUB_ERR_NONE; | |
1055 | } | |
1056 | ||
1057 | grub_disk_close (source); | |
1058 | ||
1059 | return GRUB_ERR_NONE; | |
1060 | } | |
1061 | #endif | |
1062 | ||
1063 | static int | |
25239370 CW |
1064 | grub_cryptodisk_scan_device (const char *name, |
1065 | void *data __attribute__ ((unused))) | |
20a40940 VS |
1066 | { |
1067 | grub_err_t err; | |
1068 | grub_disk_t source; | |
1069 | ||
1070 | /* Try to open disk. */ | |
1071 | source = grub_disk_open (name); | |
1072 | if (!source) | |
c5dbdc33 VS |
1073 | { |
1074 | grub_print_error (); | |
1075 | return 0; | |
1076 | } | |
20a40940 VS |
1077 | |
1078 | err = grub_cryptodisk_scan_device_real (name, source); | |
1079 | ||
1080 | grub_disk_close (source); | |
1081 | ||
1082 | if (err) | |
1083 | grub_print_error (); | |
1084 | return have_it && search_uuid ? 1 : 0; | |
1085 | } | |
1086 | ||
1087 | static grub_err_t | |
1088 | grub_cmd_cryptomount (grub_extcmd_context_t ctxt, int argc, char **args) | |
1089 | { | |
1090 | struct grub_arg_list *state = ctxt->state; | |
1091 | ||
1092 | if (argc < 1 && !state[1].set && !state[2].set) | |
1093 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); | |
1094 | ||
1095 | have_it = 0; | |
1096 | if (state[0].set) | |
1097 | { | |
1098 | grub_cryptodisk_t dev; | |
1099 | ||
1100 | dev = grub_cryptodisk_get_by_uuid (args[0]); | |
1101 | if (dev) | |
1102 | { | |
1103 | grub_dprintf ("cryptodisk", | |
1104 | "already mounted as crypto%lu\n", dev->id); | |
1105 | return GRUB_ERR_NONE; | |
1106 | } | |
1107 | ||
1108 | check_boot = state[2].set; | |
1109 | search_uuid = args[0]; | |
25239370 | 1110 | grub_device_iterate (&grub_cryptodisk_scan_device, NULL); |
20a40940 VS |
1111 | search_uuid = NULL; |
1112 | ||
1113 | if (!have_it) | |
1114 | return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such cryptodisk found"); | |
1115 | return GRUB_ERR_NONE; | |
1116 | } | |
1117 | else if (state[1].set || (argc == 0 && state[2].set)) | |
1118 | { | |
1119 | search_uuid = NULL; | |
1120 | check_boot = state[2].set; | |
25239370 | 1121 | grub_device_iterate (&grub_cryptodisk_scan_device, NULL); |
20a40940 VS |
1122 | search_uuid = NULL; |
1123 | return GRUB_ERR_NONE; | |
1124 | } | |
1125 | else | |
1126 | { | |
1127 | grub_err_t err; | |
1128 | grub_disk_t disk; | |
1129 | grub_cryptodisk_t dev; | |
418f86c6 | 1130 | char *diskname; |
c93d3e69 AB |
1131 | char *disklast = NULL; |
1132 | grub_size_t len; | |
20a40940 VS |
1133 | |
1134 | search_uuid = NULL; | |
1135 | check_boot = state[2].set; | |
418f86c6 | 1136 | diskname = args[0]; |
c93d3e69 AB |
1137 | len = grub_strlen (diskname); |
1138 | if (len && diskname[0] == '(' && diskname[len - 1] == ')') | |
ce96d01c | 1139 | { |
c93d3e69 | 1140 | disklast = &diskname[len - 1]; |
418f86c6 | 1141 | *disklast = '\0'; |
c93d3e69 | 1142 | diskname++; |
ce96d01c | 1143 | } |
c93d3e69 AB |
1144 | |
1145 | disk = grub_disk_open (diskname); | |
20a40940 | 1146 | if (!disk) |
c93d3e69 AB |
1147 | { |
1148 | if (disklast) | |
1149 | *disklast = ')'; | |
1150 | return grub_errno; | |
1151 | } | |
20a40940 VS |
1152 | |
1153 | dev = grub_cryptodisk_get_by_source_disk (disk); | |
1154 | if (dev) | |
1155 | { | |
1156 | grub_dprintf ("cryptodisk", "already mounted as crypto%lu\n", dev->id); | |
1157 | grub_disk_close (disk); | |
c93d3e69 AB |
1158 | if (disklast) |
1159 | *disklast = ')'; | |
20a40940 VS |
1160 | return GRUB_ERR_NONE; |
1161 | } | |
1162 | ||
c93d3e69 | 1163 | err = grub_cryptodisk_scan_device_real (diskname, disk); |
20a40940 VS |
1164 | |
1165 | grub_disk_close (disk); | |
c93d3e69 AB |
1166 | if (disklast) |
1167 | *disklast = ')'; | |
20a40940 VS |
1168 | |
1169 | return err; | |
1170 | } | |
1171 | } | |
1172 | ||
1a1f408f VS |
1173 | static struct grub_disk_dev grub_cryptodisk_dev = { |
1174 | .name = "cryptodisk", | |
1175 | .id = GRUB_DISK_DEVICE_CRYPTODISK_ID, | |
38409196 VS |
1176 | .disk_iterate = grub_cryptodisk_iterate, |
1177 | .disk_open = grub_cryptodisk_open, | |
1178 | .disk_close = grub_cryptodisk_close, | |
1179 | .disk_read = grub_cryptodisk_read, | |
1180 | .disk_write = grub_cryptodisk_write, | |
1a1f408f | 1181 | #ifdef GRUB_UTIL |
38409196 | 1182 | .disk_memberlist = grub_cryptodisk_memberlist, |
1a1f408f VS |
1183 | #endif |
1184 | .next = 0 | |
1185 | }; | |
1186 | ||
ce50dbd7 VS |
1187 | static char |
1188 | hex (grub_uint8_t val) | |
1189 | { | |
1190 | if (val < 10) | |
1191 | return '0' + val; | |
1192 | return 'a' + val - 10; | |
1193 | } | |
1194 | ||
1195 | /* Open a file named NAME and initialize FILE. */ | |
1196 | static char * | |
431e57a7 | 1197 | luks_script_get (grub_size_t *sz) |
ce50dbd7 VS |
1198 | { |
1199 | grub_cryptodisk_t i; | |
1200 | grub_size_t size = 0; | |
1201 | char *ptr, *ret; | |
1202 | ||
431e57a7 VS |
1203 | *sz = 0; |
1204 | ||
ce50dbd7 VS |
1205 | for (i = cryptodisk_list; i != NULL; i = i->next) |
1206 | if (grub_strcmp (i->modname, "luks") == 0) | |
1207 | { | |
1208 | size += sizeof ("luks_mount "); | |
1209 | size += grub_strlen (i->uuid); | |
1210 | size += grub_strlen (i->cipher->cipher->name); | |
1211 | size += 54; | |
1212 | if (i->essiv_hash) | |
1213 | size += grub_strlen (i->essiv_hash->name); | |
1214 | size += i->keysize * 2; | |
1215 | } | |
1216 | ||
1217 | ret = grub_malloc (size + 1); | |
1218 | if (!ret) | |
1219 | return 0; | |
1220 | ||
1221 | ptr = ret; | |
1222 | ||
1223 | for (i = cryptodisk_list; i != NULL; i = i->next) | |
1224 | if (grub_strcmp (i->modname, "luks") == 0) | |
1225 | { | |
1226 | unsigned j; | |
1227 | const char *iptr; | |
1228 | ptr = grub_stpcpy (ptr, "luks_mount "); | |
1229 | ptr = grub_stpcpy (ptr, i->uuid); | |
1230 | *ptr++ = ' '; | |
1231 | grub_snprintf (ptr, 21, "%" PRIuGRUB_UINT64_T " ", i->offset); | |
1232 | while (*ptr) | |
1233 | ptr++; | |
1234 | for (iptr = i->cipher->cipher->name; *iptr; iptr++) | |
1235 | *ptr++ = grub_tolower (*iptr); | |
1236 | switch (i->mode) | |
1237 | { | |
1238 | case GRUB_CRYPTODISK_MODE_ECB: | |
1239 | ptr = grub_stpcpy (ptr, "-ecb"); | |
1240 | break; | |
1241 | case GRUB_CRYPTODISK_MODE_CBC: | |
1242 | ptr = grub_stpcpy (ptr, "-cbc"); | |
1243 | break; | |
1244 | case GRUB_CRYPTODISK_MODE_PCBC: | |
1245 | ptr = grub_stpcpy (ptr, "-pcbc"); | |
1246 | break; | |
1247 | case GRUB_CRYPTODISK_MODE_XTS: | |
1248 | ptr = grub_stpcpy (ptr, "-xts"); | |
1249 | break; | |
1250 | case GRUB_CRYPTODISK_MODE_LRW: | |
1251 | ptr = grub_stpcpy (ptr, "-lrw"); | |
1252 | break; | |
1253 | } | |
1254 | ||
1255 | switch (i->mode_iv) | |
1256 | { | |
1257 | case GRUB_CRYPTODISK_MODE_IV_NULL: | |
1258 | ptr = grub_stpcpy (ptr, "-null"); | |
1259 | break; | |
1260 | case GRUB_CRYPTODISK_MODE_IV_PLAIN: | |
1261 | ptr = grub_stpcpy (ptr, "-plain"); | |
1262 | break; | |
1263 | case GRUB_CRYPTODISK_MODE_IV_PLAIN64: | |
1264 | ptr = grub_stpcpy (ptr, "-plain64"); | |
1265 | break; | |
1266 | case GRUB_CRYPTODISK_MODE_IV_BENBI: | |
1267 | ptr = grub_stpcpy (ptr, "-benbi"); | |
1268 | break; | |
1269 | case GRUB_CRYPTODISK_MODE_IV_ESSIV: | |
1270 | ptr = grub_stpcpy (ptr, "-essiv:"); | |
1271 | ptr = grub_stpcpy (ptr, i->essiv_hash->name); | |
1272 | break; | |
1273 | case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64: | |
1274 | case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH: | |
1275 | break; | |
1276 | } | |
1277 | *ptr++ = ' '; | |
1278 | for (j = 0; j < i->keysize; j++) | |
1279 | { | |
1280 | *ptr++ = hex (i->key[j] >> 4); | |
1281 | *ptr++ = hex (i->key[j] & 0xf); | |
1282 | } | |
1283 | *ptr++ = '\n'; | |
1284 | } | |
1285 | *ptr = '\0'; | |
431e57a7 | 1286 | *sz = ptr - ret; |
ce50dbd7 VS |
1287 | return ret; |
1288 | } | |
1289 | ||
1290 | struct grub_procfs_entry luks_script = | |
1291 | { | |
1292 | .name = "luks_script", | |
1293 | .get_contents = luks_script_get | |
1294 | }; | |
1295 | ||
20a40940 VS |
1296 | static grub_extcmd_t cmd; |
1297 | ||
1a1f408f VS |
1298 | GRUB_MOD_INIT (cryptodisk) |
1299 | { | |
1300 | grub_disk_dev_register (&grub_cryptodisk_dev); | |
20a40940 VS |
1301 | cmd = grub_register_extcmd ("cryptomount", grub_cmd_cryptomount, 0, |
1302 | N_("SOURCE|-u UUID|-a|-b"), | |
1303 | N_("Mount a crypto device."), options); | |
ce50dbd7 | 1304 | grub_procfs_register ("luks_script", &luks_script); |
1a1f408f VS |
1305 | } |
1306 | ||
1307 | GRUB_MOD_FINI (cryptodisk) | |
1308 | { | |
1309 | grub_disk_dev_unregister (&grub_cryptodisk_dev); | |
1310 | cryptodisk_cleanup (); | |
3b3ac0c9 | 1311 | grub_unregister_extcmd (cmd); |
ce50dbd7 | 1312 | grub_procfs_unregister (&luks_script); |
1a1f408f | 1313 | } |