]> git.proxmox.com Git - mirror_qemu.git/blame - block/crypto.c
opts-visitor: Favor new visit_free() function
[mirror_qemu.git] / block / crypto.c
CommitLineData
78368575
DB
1/*
2 * QEMU block full disk encryption
3 *
4 * Copyright (c) 2015-2016 Red Hat, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#include "qemu/osdep.h"
22
23#include "block/block_int.h"
24#include "sysemu/block-backend.h"
25#include "crypto/block.h"
26#include "qapi/opts-visitor.h"
27#include "qapi-visit.h"
28#include "qapi/error.h"
29
30#define BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET "key-secret"
31#define BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG "cipher-alg"
32#define BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE "cipher-mode"
33#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg"
34#define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
35#define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
36
37typedef struct BlockCrypto BlockCrypto;
38
39struct BlockCrypto {
40 QCryptoBlock *block;
41};
42
43
44static int block_crypto_probe_generic(QCryptoBlockFormat format,
45 const uint8_t *buf,
46 int buf_size,
47 const char *filename)
48{
49 if (qcrypto_block_has_format(format, buf, buf_size)) {
50 return 100;
51 } else {
52 return 0;
53 }
54}
55
56
57static ssize_t block_crypto_read_func(QCryptoBlock *block,
58 size_t offset,
59 uint8_t *buf,
60 size_t buflen,
61 Error **errp,
62 void *opaque)
63{
64 BlockDriverState *bs = opaque;
65 ssize_t ret;
66
67 ret = bdrv_pread(bs->file->bs, offset, buf, buflen);
68 if (ret < 0) {
69 error_setg_errno(errp, -ret, "Could not read encryption header");
70 return ret;
71 }
72 return ret;
73}
74
75
76struct BlockCryptoCreateData {
77 const char *filename;
78 QemuOpts *opts;
79 BlockBackend *blk;
80 uint64_t size;
81};
82
83
84static ssize_t block_crypto_write_func(QCryptoBlock *block,
85 size_t offset,
86 const uint8_t *buf,
87 size_t buflen,
88 Error **errp,
89 void *opaque)
90{
91 struct BlockCryptoCreateData *data = opaque;
92 ssize_t ret;
93
8341f00d 94 ret = blk_pwrite(data->blk, offset, buf, buflen, 0);
78368575
DB
95 if (ret < 0) {
96 error_setg_errno(errp, -ret, "Could not write encryption header");
97 return ret;
98 }
99 return ret;
100}
101
102
103static ssize_t block_crypto_init_func(QCryptoBlock *block,
104 size_t headerlen,
105 Error **errp,
106 void *opaque)
107{
108 struct BlockCryptoCreateData *data = opaque;
109 int ret;
110
111 /* User provided size should reflect amount of space made
112 * available to the guest, so we must take account of that
113 * which will be used by the crypto header
114 */
115 data->size += headerlen;
116
117 qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort);
118 ret = bdrv_create_file(data->filename, data->opts, errp);
119 if (ret < 0) {
120 return -1;
121 }
122
123 data->blk = blk_new_open(data->filename, NULL, NULL,
72e775c7 124 BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
78368575
DB
125 if (!data->blk) {
126 return -1;
127 }
128
129 return 0;
130}
131
132
133static QemuOptsList block_crypto_runtime_opts_luks = {
134 .name = "crypto",
135 .head = QTAILQ_HEAD_INITIALIZER(block_crypto_runtime_opts_luks.head),
136 .desc = {
137 {
138 .name = BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
139 .type = QEMU_OPT_STRING,
140 .help = "ID of the secret that provides the encryption key",
141 },
142 { /* end of list */ }
143 },
144};
145
146
147static QemuOptsList block_crypto_create_opts_luks = {
148 .name = "crypto",
149 .head = QTAILQ_HEAD_INITIALIZER(block_crypto_create_opts_luks.head),
150 .desc = {
151 {
152 .name = BLOCK_OPT_SIZE,
153 .type = QEMU_OPT_SIZE,
154 .help = "Virtual disk size"
155 },
156 {
157 .name = BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET,
158 .type = QEMU_OPT_STRING,
159 .help = "ID of the secret that provides the encryption key",
160 },
161 {
162 .name = BLOCK_CRYPTO_OPT_LUKS_CIPHER_ALG,
163 .type = QEMU_OPT_STRING,
164 .help = "Name of encryption cipher algorithm",
165 },
166 {
167 .name = BLOCK_CRYPTO_OPT_LUKS_CIPHER_MODE,
168 .type = QEMU_OPT_STRING,
169 .help = "Name of encryption cipher mode",
170 },
171 {
172 .name = BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG,
173 .type = QEMU_OPT_STRING,
174 .help = "Name of IV generator algorithm",
175 },
176 {
177 .name = BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG,
178 .type = QEMU_OPT_STRING,
179 .help = "Name of IV generator hash algorithm",
180 },
181 {
182 .name = BLOCK_CRYPTO_OPT_LUKS_HASH_ALG,
183 .type = QEMU_OPT_STRING,
184 .help = "Name of encryption hash algorithm",
185 },
186 { /* end of list */ }
187 },
188};
189
190
191static QCryptoBlockOpenOptions *
192block_crypto_open_opts_init(QCryptoBlockFormat format,
193 QemuOpts *opts,
194 Error **errp)
195{
09204eac 196 Visitor *v;
78368575
DB
197 QCryptoBlockOpenOptions *ret = NULL;
198 Error *local_err = NULL;
199
200 ret = g_new0(QCryptoBlockOpenOptions, 1);
201 ret->format = format;
202
09204eac 203 v = opts_visitor_new(opts);
78368575 204
09204eac 205 visit_start_struct(v, NULL, NULL, 0, &local_err);
78368575
DB
206 if (local_err) {
207 goto out;
208 }
209
210 switch (format) {
211 case Q_CRYPTO_BLOCK_FORMAT_LUKS:
212 visit_type_QCryptoBlockOptionsLUKS_members(
09204eac 213 v, &ret->u.luks, &local_err);
78368575
DB
214 break;
215
216 default:
217 error_setg(&local_err, "Unsupported block format %d", format);
218 break;
219 }
15c2f669 220 if (!local_err) {
09204eac 221 visit_check_struct(v, &local_err);
15c2f669 222 }
78368575 223
09204eac 224 visit_end_struct(v, NULL);
78368575
DB
225
226 out:
227 if (local_err) {
228 error_propagate(errp, local_err);
229 qapi_free_QCryptoBlockOpenOptions(ret);
230 ret = NULL;
231 }
09204eac 232 visit_free(v);
78368575
DB
233 return ret;
234}
235
236
237static QCryptoBlockCreateOptions *
238block_crypto_create_opts_init(QCryptoBlockFormat format,
239 QemuOpts *opts,
240 Error **errp)
241{
09204eac 242 Visitor *v;
78368575
DB
243 QCryptoBlockCreateOptions *ret = NULL;
244 Error *local_err = NULL;
245
246 ret = g_new0(QCryptoBlockCreateOptions, 1);
247 ret->format = format;
248
09204eac 249 v = opts_visitor_new(opts);
78368575 250
09204eac 251 visit_start_struct(v, NULL, NULL, 0, &local_err);
78368575
DB
252 if (local_err) {
253 goto out;
254 }
255
256 switch (format) {
257 case Q_CRYPTO_BLOCK_FORMAT_LUKS:
258 visit_type_QCryptoBlockCreateOptionsLUKS_members(
09204eac 259 v, &ret->u.luks, &local_err);
78368575
DB
260 break;
261
262 default:
263 error_setg(&local_err, "Unsupported block format %d", format);
264 break;
265 }
15c2f669 266 if (!local_err) {
09204eac 267 visit_check_struct(v, &local_err);
15c2f669 268 }
78368575 269
09204eac 270 visit_end_struct(v, NULL);
78368575
DB
271
272 out:
273 if (local_err) {
274 error_propagate(errp, local_err);
275 qapi_free_QCryptoBlockCreateOptions(ret);
276 ret = NULL;
277 }
09204eac 278 visit_free(v);
78368575
DB
279 return ret;
280}
281
282
283static int block_crypto_open_generic(QCryptoBlockFormat format,
284 QemuOptsList *opts_spec,
285 BlockDriverState *bs,
286 QDict *options,
287 int flags,
288 Error **errp)
289{
290 BlockCrypto *crypto = bs->opaque;
291 QemuOpts *opts = NULL;
292 Error *local_err = NULL;
293 int ret = -EINVAL;
294 QCryptoBlockOpenOptions *open_opts = NULL;
295 unsigned int cflags = 0;
296
297 opts = qemu_opts_create(opts_spec, NULL, 0, &error_abort);
298 qemu_opts_absorb_qdict(opts, options, &local_err);
299 if (local_err) {
300 error_propagate(errp, local_err);
301 goto cleanup;
302 }
303
304 open_opts = block_crypto_open_opts_init(format, opts, errp);
305 if (!open_opts) {
306 goto cleanup;
307 }
308
309 if (flags & BDRV_O_NO_IO) {
310 cflags |= QCRYPTO_BLOCK_OPEN_NO_IO;
311 }
312 crypto->block = qcrypto_block_open(open_opts,
313 block_crypto_read_func,
314 bs,
315 cflags,
316 errp);
317
318 if (!crypto->block) {
319 ret = -EIO;
320 goto cleanup;
321 }
322
323 bs->encrypted = 1;
324 bs->valid_key = 1;
325
326 ret = 0;
327 cleanup:
328 qapi_free_QCryptoBlockOpenOptions(open_opts);
329 return ret;
330}
331
332
333static int block_crypto_create_generic(QCryptoBlockFormat format,
334 const char *filename,
335 QemuOpts *opts,
336 Error **errp)
337{
338 int ret = -EINVAL;
339 QCryptoBlockCreateOptions *create_opts = NULL;
340 QCryptoBlock *crypto = NULL;
341 struct BlockCryptoCreateData data = {
342 .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
343 BDRV_SECTOR_SIZE),
344 .opts = opts,
345 .filename = filename,
346 };
347
348 create_opts = block_crypto_create_opts_init(format, opts, errp);
349 if (!create_opts) {
350 return -1;
351 }
352
353 crypto = qcrypto_block_create(create_opts,
354 block_crypto_init_func,
355 block_crypto_write_func,
356 &data,
357 errp);
358
359 if (!crypto) {
360 ret = -EIO;
361 goto cleanup;
362 }
363
364 ret = 0;
365 cleanup:
366 qcrypto_block_free(crypto);
367 blk_unref(data.blk);
368 qapi_free_QCryptoBlockCreateOptions(create_opts);
369 return ret;
370}
371
372static int block_crypto_truncate(BlockDriverState *bs, int64_t offset)
373{
374 BlockCrypto *crypto = bs->opaque;
375 size_t payload_offset =
376 qcrypto_block_get_payload_offset(crypto->block);
377
378 offset += payload_offset;
379
380 return bdrv_truncate(bs->file->bs, offset);
381}
382
383static void block_crypto_close(BlockDriverState *bs)
384{
385 BlockCrypto *crypto = bs->opaque;
386 qcrypto_block_free(crypto->block);
387}
388
389
390#define BLOCK_CRYPTO_MAX_SECTORS 32
391
392static coroutine_fn int
393block_crypto_co_readv(BlockDriverState *bs, int64_t sector_num,
394 int remaining_sectors, QEMUIOVector *qiov)
395{
396 BlockCrypto *crypto = bs->opaque;
397 int cur_nr_sectors; /* number of sectors in current iteration */
398 uint64_t bytes_done = 0;
399 uint8_t *cipher_data = NULL;
400 QEMUIOVector hd_qiov;
401 int ret = 0;
402 size_t payload_offset =
403 qcrypto_block_get_payload_offset(crypto->block) / 512;
404
405 qemu_iovec_init(&hd_qiov, qiov->niov);
406
407 /* Bounce buffer so we have a linear mem region for
408 * entire sector. XXX optimize so we avoid bounce
409 * buffer in case that qiov->niov == 1
410 */
411 cipher_data =
412 qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_SECTORS * 512,
413 qiov->size));
414 if (cipher_data == NULL) {
415 ret = -ENOMEM;
416 goto cleanup;
417 }
418
419 while (remaining_sectors) {
420 cur_nr_sectors = remaining_sectors;
421
422 if (cur_nr_sectors > BLOCK_CRYPTO_MAX_SECTORS) {
423 cur_nr_sectors = BLOCK_CRYPTO_MAX_SECTORS;
424 }
425
426 qemu_iovec_reset(&hd_qiov);
427 qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512);
428
429 ret = bdrv_co_readv(bs->file->bs,
430 payload_offset + sector_num,
431 cur_nr_sectors, &hd_qiov);
432 if (ret < 0) {
433 goto cleanup;
434 }
435
436 if (qcrypto_block_decrypt(crypto->block,
437 sector_num,
438 cipher_data, cur_nr_sectors * 512,
439 NULL) < 0) {
440 ret = -EIO;
441 goto cleanup;
442 }
443
444 qemu_iovec_from_buf(qiov, bytes_done,
445 cipher_data, cur_nr_sectors * 512);
446
447 remaining_sectors -= cur_nr_sectors;
448 sector_num += cur_nr_sectors;
449 bytes_done += cur_nr_sectors * 512;
450 }
451
452 cleanup:
453 qemu_iovec_destroy(&hd_qiov);
454 qemu_vfree(cipher_data);
455
456 return ret;
457}
458
459
460static coroutine_fn int
461block_crypto_co_writev(BlockDriverState *bs, int64_t sector_num,
462 int remaining_sectors, QEMUIOVector *qiov)
463{
464 BlockCrypto *crypto = bs->opaque;
465 int cur_nr_sectors; /* number of sectors in current iteration */
466 uint64_t bytes_done = 0;
467 uint8_t *cipher_data = NULL;
468 QEMUIOVector hd_qiov;
469 int ret = 0;
470 size_t payload_offset =
471 qcrypto_block_get_payload_offset(crypto->block) / 512;
472
473 qemu_iovec_init(&hd_qiov, qiov->niov);
474
475 /* Bounce buffer so we have a linear mem region for
476 * entire sector. XXX optimize so we avoid bounce
477 * buffer in case that qiov->niov == 1
478 */
479 cipher_data =
480 qemu_try_blockalign(bs->file->bs, MIN(BLOCK_CRYPTO_MAX_SECTORS * 512,
481 qiov->size));
482 if (cipher_data == NULL) {
483 ret = -ENOMEM;
484 goto cleanup;
485 }
486
487 while (remaining_sectors) {
488 cur_nr_sectors = remaining_sectors;
489
490 if (cur_nr_sectors > BLOCK_CRYPTO_MAX_SECTORS) {
491 cur_nr_sectors = BLOCK_CRYPTO_MAX_SECTORS;
492 }
493
494 qemu_iovec_to_buf(qiov, bytes_done,
495 cipher_data, cur_nr_sectors * 512);
496
497 if (qcrypto_block_encrypt(crypto->block,
498 sector_num,
499 cipher_data, cur_nr_sectors * 512,
500 NULL) < 0) {
501 ret = -EIO;
502 goto cleanup;
503 }
504
505 qemu_iovec_reset(&hd_qiov);
506 qemu_iovec_add(&hd_qiov, cipher_data, cur_nr_sectors * 512);
507
508 ret = bdrv_co_writev(bs->file->bs,
509 payload_offset + sector_num,
510 cur_nr_sectors, &hd_qiov);
511 if (ret < 0) {
512 goto cleanup;
513 }
514
515 remaining_sectors -= cur_nr_sectors;
516 sector_num += cur_nr_sectors;
517 bytes_done += cur_nr_sectors * 512;
518 }
519
520 cleanup:
521 qemu_iovec_destroy(&hd_qiov);
522 qemu_vfree(cipher_data);
523
524 return ret;
525}
526
527
528static int64_t block_crypto_getlength(BlockDriverState *bs)
529{
530 BlockCrypto *crypto = bs->opaque;
531 int64_t len = bdrv_getlength(bs->file->bs);
532
533 ssize_t offset = qcrypto_block_get_payload_offset(crypto->block);
534
535 len -= offset;
536
537 return len;
538}
539
540
541static int block_crypto_probe_luks(const uint8_t *buf,
542 int buf_size,
543 const char *filename) {
544 return block_crypto_probe_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
545 buf, buf_size, filename);
546}
547
548static int block_crypto_open_luks(BlockDriverState *bs,
549 QDict *options,
550 int flags,
551 Error **errp)
552{
553 return block_crypto_open_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
554 &block_crypto_runtime_opts_luks,
555 bs, options, flags, errp);
556}
557
558static int block_crypto_create_luks(const char *filename,
559 QemuOpts *opts,
560 Error **errp)
561{
562 return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
563 filename, opts, errp);
564}
565
566BlockDriver bdrv_crypto_luks = {
567 .format_name = "luks",
568 .instance_size = sizeof(BlockCrypto),
569 .bdrv_probe = block_crypto_probe_luks,
570 .bdrv_open = block_crypto_open_luks,
571 .bdrv_close = block_crypto_close,
572 .bdrv_create = block_crypto_create_luks,
573 .bdrv_truncate = block_crypto_truncate,
574 .create_opts = &block_crypto_create_opts_luks,
575
576 .bdrv_co_readv = block_crypto_co_readv,
577 .bdrv_co_writev = block_crypto_co_writev,
578 .bdrv_getlength = block_crypto_getlength,
579};
580
581static void block_crypto_init(void)
582{
583 bdrv_register(&bdrv_crypto_luks);
584}
585
586block_init(block_crypto_init);