aboutsummaryrefslogtreecommitdiff
path: root/qapi/opts-visitor.c
blob: 324b197495a0c55f533f37965838df3ab41363bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/*
 * Options Visitor
 *
 * Copyright Red Hat, Inc. 2012-2016
 *
 * Author: Laszlo Ersek <lersek@redhat.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 * See the COPYING.LIB file in the top-level directory.
 *
 */

#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qapi/qmp/qerror.h"
#include "qapi/opts-visitor.h"
#include "qemu/queue.h"
#include "qemu/option_int.h"
#include "qapi/visitor-impl.h"


enum ListMode
{
    LM_NONE,             /* not traversing a list of repeated options */

    LM_IN_PROGRESS,      /* opts_next_list() ready to be called.
                          *
                          * Generating the next list link will consume the most
                          * recently parsed QemuOpt instance of the repeated
                          * option.
                          *
                          * Parsing a value into the list link will examine the
                          * next QemuOpt instance of the repeated option, and
                          * possibly enter LM_SIGNED_INTERVAL or
                          * LM_UNSIGNED_INTERVAL.
                          */

    LM_SIGNED_INTERVAL,  /* opts_next_list() has been called.
                          *
                          * Generating the next list link will consume the most
                          * recently stored element from the signed interval,
                          * parsed from the most recent QemuOpt instance of the
                          * repeated option. This may consume QemuOpt itself
                          * and return to LM_IN_PROGRESS.
                          *
                          * Parsing a value into the list link will store the
                          * next element of the signed interval.
                          */

    LM_UNSIGNED_INTERVAL /* Same as above, only for an unsigned interval. */
};

typedef enum ListMode ListMode;

struct OptsVisitor
{
    Visitor visitor;

    /* Ownership remains with opts_visitor_new()'s caller. */
    const QemuOpts *opts_root;

    unsigned depth;

    /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value
     * is a non-empty GQueue, enumerating all QemuOpt occurrences with that
     * name. */
    GHashTable *unprocessed_opts;

    /* The list currently being traversed with opts_start_list() /
     * opts_next_list(). The list must have a struct element type in the
     * schema, with a single mandatory scalar member. */
    ListMode list_mode;
    GQueue *repeated_opts;

    /* When parsing a list of repeating options as integers, values of the form
     * "a-b", representing a closed interval, are allowed. Elements in the
     * range are generated individually.
     */
    union {
        int64_t s;
        uint64_t u;
    } range_next, range_limit;

    /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for
     * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does
     * not survive or escape the OptsVisitor object.
     */
    QemuOpt *fake_id_opt;
};


static OptsVisitor *to_ov(Visitor *v)
{
    return container_of(v, OptsVisitor, visitor);
}


static void
destroy_list(gpointer list)
{
  g_queue_free(list);
}


static void
opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt)
{
    GQueue *list;

    list = g_hash_table_lookup(unprocessed_opts, opt->name);
    if (list == NULL) {
        list = g_queue_new();

        /* GHashTable will never try to free the keys -- we supply NULL as
         * "key_destroy_func" in opts_start_struct(). Thus cast away key
         * const-ness in order to suppress gcc's warning.
         */
        g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list);
    }

    /* Similarly, destroy_list() doesn't call g_queue_free_full(). */
    g_queue_push_tail(list, (gpointer)opt);
}


static void
opts_start_struct(Visitor *v, const char *name, void **obj,
                  size_t size, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    const QemuOpt *opt;

    if (obj) {
        *obj = g_malloc0(size);
    }
    if (ov->depth++ > 0) {
        return;
    }

    ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal,
                                                 NULL, &destroy_list);
    QTAILQ_FOREACH(opt, &ov->opts_root->head, next) {
        /* ensured by qemu-option.c::opts_do_parse() */
        assert(strcmp(opt->name, "id") != 0);

        opts_visitor_insert(ov->unprocessed_opts, opt);
    }

    if (ov->opts_root->id != NULL) {
        ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);

        ov->fake_id_opt->name = g_strdup("id");
        ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
        opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
    }
}


static void
opts_check_struct(Visitor *v, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    GHashTableIter iter;
    GQueue *any;

    if (ov->depth > 1) {
        return;
    }

    /* we should have processed all (distinct) QemuOpt instances */
    g_hash_table_iter_init(&iter, ov->unprocessed_opts);
    if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) {
        const QemuOpt *first;

        first = g_queue_peek_head(any);
        error_setg(errp, QERR_INVALID_PARAMETER, first->name);
    }
}


static void
opts_end_struct(Visitor *v, void **obj)
{
    OptsVisitor *ov = to_ov(v);

    if (--ov->depth > 0) {
        return;
    }

    g_hash_table_destroy(ov->unprocessed_opts);
    ov->unprocessed_opts = NULL;
    if (ov->fake_id_opt) {
        g_free(ov->fake_id_opt->name);
        g_free(ov->fake_id_opt->str);
        g_free(ov->fake_id_opt);
    }
    ov->fake_id_opt = NULL;
}


static GQueue *
lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp)
{
    GQueue *list;

    list = g_hash_table_lookup(ov->unprocessed_opts, name);
    if (!list) {
        error_setg(errp, QERR_MISSING_PARAMETER, name);
    }
    return list;
}


static void
opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size,
                Error **errp)
{
    OptsVisitor *ov = to_ov(v);

    /* we can't traverse a list in a list */
    assert(ov->list_mode == LM_NONE);
    /* we don't support visits without a list */
    assert(list);
    ov->repeated_opts = lookup_distinct(ov, name, errp);
    if (ov->repeated_opts) {
        ov->list_mode = LM_IN_PROGRESS;
        *list = g_malloc0(size);
    } else {
        *list = NULL;
    }
}


static GenericList *
opts_next_list(Visitor *v, GenericList *tail, size_t size)
{
    OptsVisitor *ov = to_ov(v);

    switch (ov->list_mode) {
    case LM_SIGNED_INTERVAL:
    case LM_UNSIGNED_INTERVAL:
        if (ov->list_mode == LM_SIGNED_INTERVAL) {
            if (ov->range_next.s < ov->range_limit.s) {
                ++ov->range_next.s;
                break;
            }
        } else if (ov->range_next.u < ov->range_limit.u) {
            ++ov->range_next.u;
            break;
        }
        ov->list_mode = LM_IN_PROGRESS;
        /* range has been completed, fall through in order to pop option */

    case LM_IN_PROGRESS: {
        const QemuOpt *opt;

        opt = g_queue_pop_head(ov->repeated_opts);
        if (g_queue_is_empty(ov->repeated_opts)) {
            g_hash_table_remove(ov->unprocessed_opts, opt->name);
            return NULL;
        }
        break;
    }

    default:
        abort();
    }

    tail->next = g_malloc0(size);
    return tail->next;
}


static void
opts_check_list(Visitor *v, Error **errp)
{
    /*
     * Unvisited list elements will be reported later when checking
     * whether unvisited struct members remain.
     */
}


static void
opts_end_list(Visitor *v, void **obj)
{
    OptsVisitor *ov = to_ov(v);

    assert(ov->list_mode == LM_IN_PROGRESS ||
           ov->list_mode == LM_SIGNED_INTERVAL ||
           ov->list_mode == LM_UNSIGNED_INTERVAL);
    ov->repeated_opts = NULL;
    ov->list_mode = LM_NONE;
}


static const QemuOpt *
lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp)
{
    if (ov->list_mode == LM_NONE) {
        GQueue *list;

        /* the last occurrence of any QemuOpt takes effect when queried by name
         */
        list = lookup_distinct(ov, name, errp);
        return list ? g_queue_peek_tail(list) : NULL;
    }
    assert(ov->list_mode == LM_IN_PROGRESS);
    return g_queue_peek_head(ov->repeated_opts);
}


static void
processed(OptsVisitor *ov, const char *name)
{
    if (ov->list_mode == LM_NONE) {
        g_hash_table_remove(ov->unprocessed_opts, name);
        return;
    }
    assert(ov->list_mode == LM_IN_PROGRESS);
    /* do nothing */
}


static void
opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    const QemuOpt *opt;

    opt = lookup_scalar(ov, name, errp);
    if (!opt) {
        *obj = NULL;
        return;
    }
    *obj = g_strdup(opt->str ? opt->str : "");
    /* Note that we consume a string even if this is called as part of
     * an enum visit that later fails because the string is not a
     * valid enum value; this is harmless because tracking what gets
     * consumed only matters to visit_end_struct() as the final error
     * check if there were no other failures during the visit.  */
    processed(ov, name);
}


/* mimics qemu-option.c::parse_option_bool() */
static void
opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    const QemuOpt *opt;

    opt = lookup_scalar(ov, name, errp);
    if (!opt) {
        return;
    }

    if (opt->str) {
        if (strcmp(opt->str, "on") == 0 ||
            strcmp(opt->str, "yes") == 0 ||
            strcmp(opt->str, "y") == 0) {
            *obj = true;
        } else if (strcmp(opt->str, "off") == 0 ||
            strcmp(opt->str, "no") == 0 ||
            strcmp(opt->str, "n") == 0) {
            *obj = false;
        } else {
            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                       "on|yes|y|off|no|n");
            return;
        }
    } else {
        *obj = true;
    }

    processed(ov, name);
}


static void
opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    const QemuOpt *opt;
    const char *str;
    long long val;
    char *endptr;

    if (ov->list_mode == LM_SIGNED_INTERVAL) {
        *obj = ov->range_next.s;
        return;
    }

    opt = lookup_scalar(ov, name, errp);
    if (!opt) {
        return;
    }
    str = opt->str ? opt->str : "";

    /* we've gotten past lookup_scalar() */
    assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS);

    errno = 0;
    val = strtoll(str, &endptr, 0);
    if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) {
        if (*endptr == '\0') {
            *obj = val;
            processed(ov, name);
            return;
        }
        if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
            long long val2;

            str = endptr + 1;
            val2 = strtoll(str, &endptr, 0);
            if (errno == 0 && endptr > str && *endptr == '\0' &&
                INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 &&
                (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX ||
                 val2 < val + OPTS_VISITOR_RANGE_MAX)) {
                ov->range_next.s = val;
                ov->range_limit.s = val2;
                ov->list_mode = LM_SIGNED_INTERVAL;

                /* as if entering on the top */
                *obj = ov->range_next.s;
                return;
            }
        }
    }
    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
               (ov->list_mode == LM_NONE) ? "an int64 value" :
                                            "an int64 value or range");
}


static void
opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    const QemuOpt *opt;
    const char *str;
    unsigned long long val;
    char *endptr;

    if (ov->list_mode == LM_UNSIGNED_INTERVAL) {
        *obj = ov->range_next.u;
        return;
    }

    opt = lookup_scalar(ov, name, errp);
    if (!opt) {
        return;
    }
    str = opt->str;

    /* we've gotten past lookup_scalar() */
    assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS);

    if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) {
        if (*endptr == '\0') {
            *obj = val;
            processed(ov, name);
            return;
        }
        if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) {
            unsigned long long val2;

            str = endptr + 1;
            if (parse_uint_full(str, &val2, 0) == 0 &&
                val2 <= UINT64_MAX && val <= val2 &&
                val2 - val < OPTS_VISITOR_RANGE_MAX) {
                ov->range_next.u = val;
                ov->range_limit.u = val2;
                ov->list_mode = LM_UNSIGNED_INTERVAL;

                /* as if entering on the top */
                *obj = ov->range_next.u;
                return;
            }
        }
    }
    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
               (ov->list_mode == LM_NONE) ? "a uint64 value" :
                                            "a uint64 value or range");
}


static void
opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp)
{
    OptsVisitor *ov = to_ov(v);
    const QemuOpt *opt;
    int err;

    opt = lookup_scalar(ov, name, errp);
    if (!opt) {
        return;
    }

    err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj);
    if (err < 0) {
        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
                   "a size value");
        return;
    }

    processed(ov, name);
}


static void
opts_optional(Visitor *v, const char *name, bool *present)
{
    OptsVisitor *ov = to_ov(v);

    /* we only support a single mandatory scalar field in a list node */
    assert(ov->list_mode == LM_NONE);
    *present = (lookup_distinct(ov, name, NULL) != NULL);
}


static void
opts_free(Visitor *v)
{
    OptsVisitor *ov = to_ov(v);

    if (ov->unprocessed_opts != NULL) {
        g_hash_table_destroy(ov->unprocessed_opts);
    }
    g_free(ov->fake_id_opt);
    g_free(ov);
}


Visitor *
opts_visitor_new(const QemuOpts *opts)
{
    OptsVisitor *ov;

    assert(opts);
    ov = g_malloc0(sizeof *ov);

    ov->visitor.type = VISITOR_INPUT;

    ov->visitor.start_struct = &opts_start_struct;
    ov->visitor.check_struct = &opts_check_struct;
    ov->visitor.end_struct   = &opts_end_struct;

    ov->visitor.start_list = &opts_start_list;
    ov->visitor.next_list  = &opts_next_list;
    ov->visitor.check_list = &opts_check_list;
    ov->visitor.end_list   = &opts_end_list;

    ov->visitor.type_int64  = &opts_type_int64;
    ov->visitor.type_uint64 = &opts_type_uint64;
    ov->visitor.type_size   = &opts_type_size;
    ov->visitor.type_bool   = &opts_type_bool;
    ov->visitor.type_str    = &opts_type_str;

    /* type_number() is not filled in, but this is not the first visitor to
     * skip some mandatory methods... */

    ov->visitor.optional = &opts_optional;
    ov->visitor.free = opts_free;

    ov->opts_root = opts;

    return &ov->visitor;
}
ass="hl opt">->version) == QCRYPTO_BLOCK_LUKS_VERSION) { return true; } else { return false; } } /** * Deal with a quirk of dm-crypt usage of ESSIV. * * When calculating ESSIV IVs, the cipher length used by ESSIV * may be different from the cipher length used for the block * encryption, becauses dm-crypt uses the hash digest length * as the key size. ie, if you have AES 128 as the block cipher * and SHA 256 as ESSIV hash, then ESSIV will use AES 256 as * the cipher since that gets a key length matching the digest * size, not AES 128 with truncated digest as might be imagined */ static QCryptoCipherAlgorithm qcrypto_block_luks_essiv_cipher(QCryptoCipherAlgorithm cipher, QCryptoHashAlgorithm hash, Error **errp) { size_t digestlen = qcrypto_hash_digest_len(hash); size_t keylen = qcrypto_cipher_get_key_len(cipher); if (digestlen == keylen) { return cipher; } switch (cipher) { case QCRYPTO_CIPHER_ALG_AES_128: case QCRYPTO_CIPHER_ALG_AES_192: case QCRYPTO_CIPHER_ALG_AES_256: if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_AES_128)) { return QCRYPTO_CIPHER_ALG_AES_128; } else if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_AES_192)) { return QCRYPTO_CIPHER_ALG_AES_192; } else if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_AES_256)) { return QCRYPTO_CIPHER_ALG_AES_256; } else { error_setg(errp, "No AES cipher with key size %zu available", digestlen); return 0; } break; case QCRYPTO_CIPHER_ALG_SERPENT_128: case QCRYPTO_CIPHER_ALG_SERPENT_192: case QCRYPTO_CIPHER_ALG_SERPENT_256: if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_SERPENT_128)) { return QCRYPTO_CIPHER_ALG_SERPENT_128; } else if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_SERPENT_192)) { return QCRYPTO_CIPHER_ALG_SERPENT_192; } else if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_SERPENT_256)) { return QCRYPTO_CIPHER_ALG_SERPENT_256; } else { error_setg(errp, "No Serpent cipher with key size %zu available", digestlen); return 0; } break; case QCRYPTO_CIPHER_ALG_TWOFISH_128: case QCRYPTO_CIPHER_ALG_TWOFISH_192: case QCRYPTO_CIPHER_ALG_TWOFISH_256: if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_TWOFISH_128)) { return QCRYPTO_CIPHER_ALG_TWOFISH_128; } else if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_TWOFISH_192)) { return QCRYPTO_CIPHER_ALG_TWOFISH_192; } else if (digestlen == qcrypto_cipher_get_key_len( QCRYPTO_CIPHER_ALG_TWOFISH_256)) { return QCRYPTO_CIPHER_ALG_TWOFISH_256; } else { error_setg(errp, "No Twofish cipher with key size %zu available", digestlen); return 0; } break; default: error_setg(errp, "Cipher %s not supported with essiv", QCryptoCipherAlgorithm_str(cipher)); return 0; } } /* * Given a key slot, and user password, this will attempt to unlock * the master encryption key from the key slot. * * Returns: * 0 if the key slot is disabled, or key could not be decrypted * with the provided password * 1 if the key slot is enabled, and key decrypted successfully * with the provided password * -1 if a fatal error occurred loading the key */ static int qcrypto_block_luks_load_key(QCryptoBlock *block, QCryptoBlockLUKSKeySlot *slot, const char *password, QCryptoCipherAlgorithm cipheralg, QCryptoCipherMode ciphermode, QCryptoHashAlgorithm hash, QCryptoIVGenAlgorithm ivalg, QCryptoCipherAlgorithm ivcipheralg, QCryptoHashAlgorithm ivhash, uint8_t *masterkey, size_t masterkeylen, QCryptoBlockReadFunc readfunc, void *opaque, Error **errp) { QCryptoBlockLUKS *luks = block->opaque; uint8_t *splitkey; size_t splitkeylen; uint8_t *possiblekey; int ret = -1; ssize_t rv; QCryptoCipher *cipher = NULL; uint8_t keydigest[QCRYPTO_BLOCK_LUKS_DIGEST_LEN]; QCryptoIVGen *ivgen = NULL; size_t niv; if (slot->active != QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED) { return 0; } splitkeylen = masterkeylen * slot->stripes; splitkey = g_new0(uint8_t, splitkeylen); possiblekey = g_new0(uint8_t, masterkeylen); /* * The user password is used to generate a (possible) * decryption key. This may or may not successfully * decrypt the master key - we just blindly assume * the key is correct and validate the results of * decryption later. */ if (qcrypto_pbkdf2(hash, (const uint8_t *)password, strlen(password), slot->salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, slot->iterations, possiblekey, masterkeylen, errp) < 0) { goto cleanup; } /* * We need to read the master key material from the * LUKS key material header. What we're reading is * not the raw master key, but rather the data after * it has been passed through AFSplit and the result * then encrypted. */ rv = readfunc(block, slot->key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, splitkey, splitkeylen, opaque, errp); if (rv < 0) { goto cleanup; } /* Setup the cipher/ivgen that we'll use to try to decrypt * the split master key material */ cipher = qcrypto_cipher_new(cipheralg, ciphermode, possiblekey, masterkeylen, errp); if (!cipher) { goto cleanup; } niv = qcrypto_cipher_get_iv_len(cipheralg, ciphermode); ivgen = qcrypto_ivgen_new(ivalg, ivcipheralg, ivhash, possiblekey, masterkeylen, errp); if (!ivgen) { goto cleanup; } /* * The master key needs to be decrypted in the same * way that the block device payload will be decrypted * later. In particular we'll be using the IV generator * to reset the encryption cipher every time the master * key crosses a sector boundary. */ if (qcrypto_block_decrypt_helper(cipher, niv, ivgen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, 0, splitkey, splitkeylen, errp) < 0) { goto cleanup; } /* * Now we've decrypted the split master key, join * it back together to get the actual master key. */ if (qcrypto_afsplit_decode(hash, masterkeylen, slot->stripes, splitkey, masterkey, errp) < 0) { goto cleanup; } /* * We still don't know that the masterkey we got is valid, * because we just blindly assumed the user's password * was correct. This is where we now verify it. We are * creating a hash of the master key using PBKDF and * then comparing that to the hash stored in the key slot * header */ if (qcrypto_pbkdf2(hash, masterkey, masterkeylen, luks->header.master_key_salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, luks->header.master_key_iterations, keydigest, G_N_ELEMENTS(keydigest), errp) < 0) { goto cleanup; } if (memcmp(keydigest, luks->header.master_key_digest, QCRYPTO_BLOCK_LUKS_DIGEST_LEN) == 0) { /* Success, we got the right master key */ ret = 1; goto cleanup; } /* Fail, user's password was not valid for this key slot, * tell caller to try another slot */ ret = 0; cleanup: qcrypto_ivgen_free(ivgen); qcrypto_cipher_free(cipher); g_free(splitkey); g_free(possiblekey); return ret; } /* * Given a user password, this will iterate over all key * slots and try to unlock each active key slot using the * password until it successfully obtains a master key. * * Returns 0 if a key was loaded, -1 if no keys could be loaded */ static int qcrypto_block_luks_find_key(QCryptoBlock *block, const char *password, QCryptoCipherAlgorithm cipheralg, QCryptoCipherMode ciphermode, QCryptoHashAlgorithm hash, QCryptoIVGenAlgorithm ivalg, QCryptoCipherAlgorithm ivcipheralg, QCryptoHashAlgorithm ivhash, uint8_t **masterkey, size_t *masterkeylen, QCryptoBlockReadFunc readfunc, void *opaque, Error **errp) { QCryptoBlockLUKS *luks = block->opaque; size_t i; int rv; *masterkey = g_new0(uint8_t, luks->header.key_bytes); *masterkeylen = luks->header.key_bytes; for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { rv = qcrypto_block_luks_load_key(block, &luks->header.key_slots[i], password, cipheralg, ciphermode, hash, ivalg, ivcipheralg, ivhash, *masterkey, *masterkeylen, readfunc, opaque, errp); if (rv < 0) { goto error; } if (rv == 1) { return 0; } } error_setg(errp, "Invalid password, cannot unlock any keyslot"); error: g_free(*masterkey); *masterkey = NULL; *masterkeylen = 0; return -1; } static int qcrypto_block_luks_open(QCryptoBlock *block, QCryptoBlockOpenOptions *options, const char *optprefix, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, Error **errp) { QCryptoBlockLUKS *luks; Error *local_err = NULL; int ret = 0; size_t i; ssize_t rv; uint8_t *masterkey = NULL; size_t masterkeylen; char *ivgen_name, *ivhash_name; QCryptoCipherMode ciphermode; QCryptoCipherAlgorithm cipheralg; QCryptoIVGenAlgorithm ivalg; QCryptoCipherAlgorithm ivcipheralg; QCryptoHashAlgorithm hash; QCryptoHashAlgorithm ivhash; char *password = NULL; if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) { if (!options->u.luks.key_secret) { error_setg(errp, "Parameter '%skey-secret' is required for cipher", optprefix ? optprefix : ""); return -1; } password = qcrypto_secret_lookup_as_utf8( options->u.luks.key_secret, errp); if (!password) { return -1; } } luks = g_new0(QCryptoBlockLUKS, 1); block->opaque = luks; /* Read the entire LUKS header, minus the key material from * the underlying device */ rv = readfunc(block, 0, (uint8_t *)&luks->header, sizeof(luks->header), opaque, errp); if (rv < 0) { ret = rv; goto fail; } /* The header is always stored in big-endian format, so * convert everything to native */ be16_to_cpus(&luks->header.version); be32_to_cpus(&luks->header.payload_offset); be32_to_cpus(&luks->header.key_bytes); be32_to_cpus(&luks->header.master_key_iterations); for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { be32_to_cpus(&luks->header.key_slots[i].active); be32_to_cpus(&luks->header.key_slots[i].iterations); be32_to_cpus(&luks->header.key_slots[i].key_offset); be32_to_cpus(&luks->header.key_slots[i].stripes); } if (memcmp(luks->header.magic, qcrypto_block_luks_magic, QCRYPTO_BLOCK_LUKS_MAGIC_LEN) != 0) { error_setg(errp, "Volume is not in LUKS format"); ret = -EINVAL; goto fail; } if (luks->header.version != QCRYPTO_BLOCK_LUKS_VERSION) { error_setg(errp, "LUKS version %" PRIu32 " is not supported", luks->header.version); ret = -ENOTSUP; goto fail; } /* * The cipher_mode header contains a string that we have * to further parse, of the format * * <cipher-mode>-<iv-generator>[:<iv-hash>] * * eg cbc-essiv:sha256, cbc-plain64 */ ivgen_name = strchr(luks->header.cipher_mode, '-'); if (!ivgen_name) { ret = -EINVAL; error_setg(errp, "Unexpected cipher mode string format %s", luks->header.cipher_mode); goto fail; } *ivgen_name = '\0'; ivgen_name++; ivhash_name = strchr(ivgen_name, ':'); if (!ivhash_name) { ivhash = 0; } else { *ivhash_name = '\0'; ivhash_name++; ivhash = qcrypto_block_luks_hash_name_lookup(ivhash_name, &local_err); if (local_err) { ret = -ENOTSUP; error_propagate(errp, local_err); goto fail; } } ciphermode = qcrypto_block_luks_cipher_mode_lookup(luks->header.cipher_mode, &local_err); if (local_err) { ret = -ENOTSUP; error_propagate(errp, local_err); goto fail; } cipheralg = qcrypto_block_luks_cipher_name_lookup(luks->header.cipher_name, ciphermode, luks->header.key_bytes, &local_err); if (local_err) { ret = -ENOTSUP; error_propagate(errp, local_err); goto fail; } hash = qcrypto_block_luks_hash_name_lookup(luks->header.hash_spec, &local_err); if (local_err) { ret = -ENOTSUP; error_propagate(errp, local_err); goto fail; } ivalg = qcrypto_block_luks_ivgen_name_lookup(ivgen_name, &local_err); if (local_err) { ret = -ENOTSUP; error_propagate(errp, local_err); goto fail; } if (ivalg == QCRYPTO_IVGEN_ALG_ESSIV) { if (!ivhash_name) { ret = -EINVAL; error_setg(errp, "Missing IV generator hash specification"); goto fail; } ivcipheralg = qcrypto_block_luks_essiv_cipher(cipheralg, ivhash, &local_err); if (local_err) { ret = -ENOTSUP; error_propagate(errp, local_err); goto fail; } } else { /* Note we parsed the ivhash_name earlier in the cipher_mode * spec string even with plain/plain64 ivgens, but we * will ignore it, since it is irrelevant for these ivgens. * This is for compat with dm-crypt which will silently * ignore hash names with these ivgens rather than report * an error about the invalid usage */ ivcipheralg = cipheralg; } if (!(flags & QCRYPTO_BLOCK_OPEN_NO_IO)) { /* Try to find which key slot our password is valid for * and unlock the master key from that slot. */ if (qcrypto_block_luks_find_key(block, password, cipheralg, ciphermode, hash, ivalg, ivcipheralg, ivhash, &masterkey, &masterkeylen, readfunc, opaque, errp) < 0) { ret = -EACCES; goto fail; } /* We have a valid master key now, so can setup the * block device payload decryption objects */ block->kdfhash = hash; block->niv = qcrypto_cipher_get_iv_len(cipheralg, ciphermode); block->ivgen = qcrypto_ivgen_new(ivalg, ivcipheralg, ivhash, masterkey, masterkeylen, errp); if (!block->ivgen) { ret = -ENOTSUP; goto fail; } block->cipher = qcrypto_cipher_new(cipheralg, ciphermode, masterkey, masterkeylen, errp); if (!block->cipher) { ret = -ENOTSUP; goto fail; } } block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; block->payload_offset = luks->header.payload_offset * block->sector_size; luks->cipher_alg = cipheralg; luks->cipher_mode = ciphermode; luks->ivgen_alg = ivalg; luks->ivgen_hash_alg = ivhash; luks->hash_alg = hash; g_free(masterkey); g_free(password); return 0; fail: g_free(masterkey); qcrypto_cipher_free(block->cipher); qcrypto_ivgen_free(block->ivgen); g_free(luks); g_free(password); return ret; } static void qcrypto_block_luks_uuid_gen(uint8_t *uuidstr) { QemuUUID uuid; qemu_uuid_generate(&uuid); qemu_uuid_unparse(&uuid, (char *)uuidstr); } static int qcrypto_block_luks_create(QCryptoBlock *block, QCryptoBlockCreateOptions *options, const char *optprefix, QCryptoBlockInitFunc initfunc, QCryptoBlockWriteFunc writefunc, void *opaque, Error **errp) { QCryptoBlockLUKS *luks; QCryptoBlockCreateOptionsLUKS luks_opts; Error *local_err = NULL; uint8_t *masterkey = NULL; uint8_t *slotkey = NULL; uint8_t *splitkey = NULL; size_t splitkeylen = 0; size_t i; QCryptoCipher *cipher = NULL; QCryptoIVGen *ivgen = NULL; char *password; const char *cipher_alg; const char *cipher_mode; const char *ivgen_alg; const char *ivgen_hash_alg = NULL; const char *hash_alg; char *cipher_mode_spec = NULL; QCryptoCipherAlgorithm ivcipheralg = 0; uint64_t iters; memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts)); if (!luks_opts.has_iter_time) { luks_opts.iter_time = 2000; } if (!luks_opts.has_cipher_alg) { luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256; } if (!luks_opts.has_cipher_mode) { luks_opts.cipher_mode = QCRYPTO_CIPHER_MODE_XTS; } if (!luks_opts.has_ivgen_alg) { luks_opts.ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64; } if (!luks_opts.has_hash_alg) { luks_opts.hash_alg = QCRYPTO_HASH_ALG_SHA256; } if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { if (!luks_opts.has_ivgen_hash_alg) { luks_opts.ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256; luks_opts.has_ivgen_hash_alg = true; } } /* Note we're allowing ivgen_hash_alg to be set even for * non-essiv iv generators that don't need a hash. It will * be silently ignored, for compatibility with dm-crypt */ if (!options->u.luks.key_secret) { error_setg(errp, "Parameter '%skey-secret' is required for cipher", optprefix ? optprefix : ""); return -1; } password = qcrypto_secret_lookup_as_utf8(luks_opts.key_secret, errp); if (!password) { return -1; } luks = g_new0(QCryptoBlockLUKS, 1); block->opaque = luks; memcpy(luks->header.magic, qcrypto_block_luks_magic, QCRYPTO_BLOCK_LUKS_MAGIC_LEN); /* We populate the header in native endianness initially and * then convert everything to big endian just before writing * it out to disk */ luks->header.version = QCRYPTO_BLOCK_LUKS_VERSION; qcrypto_block_luks_uuid_gen(luks->header.uuid); cipher_alg = qcrypto_block_luks_cipher_alg_lookup(luks_opts.cipher_alg, errp); if (!cipher_alg) { goto error; } cipher_mode = QCryptoCipherMode_str(luks_opts.cipher_mode); ivgen_alg = QCryptoIVGenAlgorithm_str(luks_opts.ivgen_alg); if (luks_opts.has_ivgen_hash_alg) { ivgen_hash_alg = QCryptoHashAlgorithm_str(luks_opts.ivgen_hash_alg); cipher_mode_spec = g_strdup_printf("%s-%s:%s", cipher_mode, ivgen_alg, ivgen_hash_alg); } else { cipher_mode_spec = g_strdup_printf("%s-%s", cipher_mode, ivgen_alg); } hash_alg = QCryptoHashAlgorithm_str(luks_opts.hash_alg); if (strlen(cipher_alg) >= QCRYPTO_BLOCK_LUKS_CIPHER_NAME_LEN) { error_setg(errp, "Cipher name '%s' is too long for LUKS header", cipher_alg); goto error; } if (strlen(cipher_mode_spec) >= QCRYPTO_BLOCK_LUKS_CIPHER_MODE_LEN) { error_setg(errp, "Cipher mode '%s' is too long for LUKS header", cipher_mode_spec); goto error; } if (strlen(hash_alg) >= QCRYPTO_BLOCK_LUKS_HASH_SPEC_LEN) { error_setg(errp, "Hash name '%s' is too long for LUKS header", hash_alg); goto error; } if (luks_opts.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { ivcipheralg = qcrypto_block_luks_essiv_cipher(luks_opts.cipher_alg, luks_opts.ivgen_hash_alg, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; } } else { ivcipheralg = luks_opts.cipher_alg; } strcpy(luks->header.cipher_name, cipher_alg); strcpy(luks->header.cipher_mode, cipher_mode_spec); strcpy(luks->header.hash_spec, hash_alg); luks->header.key_bytes = qcrypto_cipher_get_key_len(luks_opts.cipher_alg); if (luks_opts.cipher_mode == QCRYPTO_CIPHER_MODE_XTS) { luks->header.key_bytes *= 2; } /* Generate the salt used for hashing the master key * with PBKDF later */ if (qcrypto_random_bytes(luks->header.master_key_salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, errp) < 0) { goto error; } /* Generate random master key */ masterkey = g_new0(uint8_t, luks->header.key_bytes); if (qcrypto_random_bytes(masterkey, luks->header.key_bytes, errp) < 0) { goto error; } /* Setup the block device payload encryption objects */ block->cipher = qcrypto_cipher_new(luks_opts.cipher_alg, luks_opts.cipher_mode, masterkey, luks->header.key_bytes, errp); if (!block->cipher) { goto error; } block->kdfhash = luks_opts.hash_alg; block->niv = qcrypto_cipher_get_iv_len(luks_opts.cipher_alg, luks_opts.cipher_mode); block->ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg, ivcipheralg, luks_opts.ivgen_hash_alg, masterkey, luks->header.key_bytes, errp); if (!block->ivgen) { goto error; } /* Determine how many iterations we need to hash the master * key, in order to have 1 second of compute time used */ iters = qcrypto_pbkdf2_count_iters(luks_opts.hash_alg, masterkey, luks->header.key_bytes, luks->header.master_key_salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, QCRYPTO_BLOCK_LUKS_DIGEST_LEN, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; } if (iters > (ULLONG_MAX / luks_opts.iter_time)) { error_setg_errno(errp, ERANGE, "PBKDF iterations %llu too large to scale", (unsigned long long)iters); goto error; } /* iter_time was in millis, but count_iters reported for secs */ iters = iters * luks_opts.iter_time / 1000; /* Why /= 8 ? That matches cryptsetup, but there's no * explanation why they chose /= 8... Probably so that * if all 8 keyslots are active we only spend 1 second * in total time to check all keys */ iters /= 8; if (iters > UINT32_MAX) { error_setg_errno(errp, ERANGE, "PBKDF iterations %llu larger than %u", (unsigned long long)iters, UINT32_MAX); goto error; } iters = MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS); luks->header.master_key_iterations = iters; /* Hash the master key, saving the result in the LUKS * header. This hash is used when opening the encrypted * device to verify that the user password unlocked a * valid master key */ if (qcrypto_pbkdf2(luks_opts.hash_alg, masterkey, luks->header.key_bytes, luks->header.master_key_salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, luks->header.master_key_iterations, luks->header.master_key_digest, QCRYPTO_BLOCK_LUKS_DIGEST_LEN, errp) < 0) { goto error; } /* Although LUKS has multiple key slots, we're just going * to use the first key slot */ splitkeylen = luks->header.key_bytes * QCRYPTO_BLOCK_LUKS_STRIPES; for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { luks->header.key_slots[i].active = i == 0 ? QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED : QCRYPTO_BLOCK_LUKS_KEY_SLOT_DISABLED; luks->header.key_slots[i].stripes = QCRYPTO_BLOCK_LUKS_STRIPES; /* This calculation doesn't match that shown in the spec, * but instead follows the cryptsetup implementation. */ luks->header.key_slots[i].key_offset = (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) + (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE), (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * i); } if (qcrypto_random_bytes(luks->header.key_slots[0].salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, errp) < 0) { goto error; } /* Again we determine how many iterations are required to * hash the user password while consuming 1 second of compute * time */ iters = qcrypto_pbkdf2_count_iters(luks_opts.hash_alg, (uint8_t *)password, strlen(password), luks->header.key_slots[0].salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, luks->header.key_bytes, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; } if (iters > (ULLONG_MAX / luks_opts.iter_time)) { error_setg_errno(errp, ERANGE, "PBKDF iterations %llu too large to scale", (unsigned long long)iters); goto error; } /* iter_time was in millis, but count_iters reported for secs */ iters = iters * luks_opts.iter_time / 1000; if (iters > UINT32_MAX) { error_setg_errno(errp, ERANGE, "PBKDF iterations %llu larger than %u", (unsigned long long)iters, UINT32_MAX); goto error; } luks->header.key_slots[0].iterations = MAX(iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS); /* Generate a key that we'll use to encrypt the master * key, from the user's password */ slotkey = g_new0(uint8_t, luks->header.key_bytes); if (qcrypto_pbkdf2(luks_opts.hash_alg, (uint8_t *)password, strlen(password), luks->header.key_slots[0].salt, QCRYPTO_BLOCK_LUKS_SALT_LEN, luks->header.key_slots[0].iterations, slotkey, luks->header.key_bytes, errp) < 0) { goto error; } /* Setup the encryption objects needed to encrypt the * master key material */ cipher = qcrypto_cipher_new(luks_opts.cipher_alg, luks_opts.cipher_mode, slotkey, luks->header.key_bytes, errp); if (!cipher) { goto error; } ivgen = qcrypto_ivgen_new(luks_opts.ivgen_alg, ivcipheralg, luks_opts.ivgen_hash_alg, slotkey, luks->header.key_bytes, errp); if (!ivgen) { goto error; } /* Before storing the master key, we need to vastly * increase its size, as protection against forensic * disk data recovery */ splitkey = g_new0(uint8_t, splitkeylen); if (qcrypto_afsplit_encode(luks_opts.hash_alg, luks->header.key_bytes, luks->header.key_slots[0].stripes, masterkey, splitkey, errp) < 0) { goto error; } /* Now we encrypt the split master key with the key generated * from the user's password, before storing it */ if (qcrypto_block_encrypt_helper(cipher, block->niv, ivgen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, 0, splitkey, splitkeylen, errp) < 0) { goto error; } /* The total size of the LUKS headers is the partition header + key * slot headers, rounded up to the nearest sector, combined with * the size of each master key material region, also rounded up * to the nearest sector */ luks->header.payload_offset = (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / QCRYPTO_BLOCK_LUKS_SECTOR_SIZE) + (ROUND_UP(DIV_ROUND_UP(splitkeylen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE), (QCRYPTO_BLOCK_LUKS_KEY_SLOT_OFFSET / QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)) * QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS); block->sector_size = QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; block->payload_offset = luks->header.payload_offset * block->sector_size; /* Reserve header space to match payload offset */ initfunc(block, block->payload_offset, opaque, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; } /* Everything on disk uses Big Endian, so flip header fields * before writing them */ cpu_to_be16s(&luks->header.version); cpu_to_be32s(&luks->header.payload_offset); cpu_to_be32s(&luks->header.key_bytes); cpu_to_be32s(&luks->header.master_key_iterations); for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { cpu_to_be32s(&luks->header.key_slots[i].active); cpu_to_be32s(&luks->header.key_slots[i].iterations); cpu_to_be32s(&luks->header.key_slots[i].key_offset); cpu_to_be32s(&luks->header.key_slots[i].stripes); } /* Write out the partition header and key slot headers */ writefunc(block, 0, (const uint8_t *)&luks->header, sizeof(luks->header), opaque, &local_err); /* Delay checking local_err until we've byte-swapped */ /* Byte swap the header back to native, in case we need * to read it again later */ be16_to_cpus(&luks->header.version); be32_to_cpus(&luks->header.payload_offset); be32_to_cpus(&luks->header.key_bytes); be32_to_cpus(&luks->header.master_key_iterations); for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { be32_to_cpus(&luks->header.key_slots[i].active); be32_to_cpus(&luks->header.key_slots[i].iterations); be32_to_cpus(&luks->header.key_slots[i].key_offset); be32_to_cpus(&luks->header.key_slots[i].stripes); } if (local_err) { error_propagate(errp, local_err); goto error; } /* Write out the master key material, starting at the * sector immediately following the partition header. */ if (writefunc(block, luks->header.key_slots[0].key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, splitkey, splitkeylen, opaque, errp) != splitkeylen) { goto error; } luks->cipher_alg = luks_opts.cipher_alg; luks->cipher_mode = luks_opts.cipher_mode; luks->ivgen_alg = luks_opts.ivgen_alg; luks->ivgen_hash_alg = luks_opts.ivgen_hash_alg; luks->hash_alg = luks_opts.hash_alg; memset(masterkey, 0, luks->header.key_bytes); g_free(masterkey); memset(slotkey, 0, luks->header.key_bytes); g_free(slotkey); g_free(splitkey); g_free(password); g_free(cipher_mode_spec); qcrypto_ivgen_free(ivgen); qcrypto_cipher_free(cipher); return 0; error: if (masterkey) { memset(masterkey, 0, luks->header.key_bytes); } g_free(masterkey); if (slotkey) { memset(slotkey, 0, luks->header.key_bytes); } g_free(slotkey); g_free(splitkey); g_free(password); g_free(cipher_mode_spec); qcrypto_ivgen_free(ivgen); qcrypto_cipher_free(cipher); g_free(luks); return -1; } static int qcrypto_block_luks_get_info(QCryptoBlock *block, QCryptoBlockInfo *info, Error **errp) { QCryptoBlockLUKS *luks = block->opaque; QCryptoBlockInfoLUKSSlot *slot; QCryptoBlockInfoLUKSSlotList *slots = NULL, **prev = &info->u.luks.slots; size_t i; info->u.luks.cipher_alg = luks->cipher_alg; info->u.luks.cipher_mode = luks->cipher_mode; info->u.luks.ivgen_alg = luks->ivgen_alg; if (info->u.luks.ivgen_alg == QCRYPTO_IVGEN_ALG_ESSIV) { info->u.luks.has_ivgen_hash_alg = true; info->u.luks.ivgen_hash_alg = luks->ivgen_hash_alg; } info->u.luks.hash_alg = luks->hash_alg; info->u.luks.payload_offset = block->payload_offset; info->u.luks.master_key_iters = luks->header.master_key_iterations; info->u.luks.uuid = g_strndup((const char *)luks->header.uuid, sizeof(luks->header.uuid)); for (i = 0; i < QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS; i++) { slots = g_new0(QCryptoBlockInfoLUKSSlotList, 1); *prev = slots; slots->value = slot = g_new0(QCryptoBlockInfoLUKSSlot, 1); slot->active = luks->header.key_slots[i].active == QCRYPTO_BLOCK_LUKS_KEY_SLOT_ENABLED; slot->key_offset = luks->header.key_slots[i].key_offset * QCRYPTO_BLOCK_LUKS_SECTOR_SIZE; if (slot->active) { slot->has_iters = true; slot->iters = luks->header.key_slots[i].iterations; slot->has_stripes = true; slot->stripes = luks->header.key_slots[i].stripes; } prev = &slots->next; } return 0; } static void qcrypto_block_luks_cleanup(QCryptoBlock *block) { g_free(block->opaque); } static int qcrypto_block_luks_decrypt(QCryptoBlock *block, uint64_t offset, uint8_t *buf, size_t len, Error **errp) { assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); return qcrypto_block_decrypt_helper(block->cipher, block->niv, block->ivgen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, offset, buf, len, errp); } static int qcrypto_block_luks_encrypt(QCryptoBlock *block, uint64_t offset, uint8_t *buf, size_t len, Error **errp) { assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); return qcrypto_block_encrypt_helper(block->cipher, block->niv, block->ivgen, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, offset, buf, len, errp); } const QCryptoBlockDriver qcrypto_block_driver_luks = { .open = qcrypto_block_luks_open, .create = qcrypto_block_luks_create, .get_info = qcrypto_block_luks_get_info, .cleanup = qcrypto_block_luks_cleanup, .decrypt = qcrypto_block_luks_decrypt, .encrypt = qcrypto_block_luks_encrypt, .has_format = qcrypto_block_luks_has_format, };