Coverage Report

Created: 2025-06-13 06:36

/src/cryptsetup/lib/luks2/luks2_segment.c
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * LUKS - Linux Unified Key Setup v2, internal segment handling
4
 *
5
 * Copyright (C) 2018-2025 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2018-2025 Ondrej Kozina
7
 */
8
9
#include "luks2_internal.h"
10
11
/* use only on already validated 'segments' object */
12
uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned blockwise)
13
6.66k
{
14
6.66k
  uint64_t tmp, min = blockwise ? UINT64_MAX >> SECTOR_SHIFT : UINT64_MAX;
15
16
6.66k
  if (!jobj_segments)
17
0
    return 0;
18
19
6.76k
  json_object_object_foreach(jobj_segments, key, val) {
20
6.76k
    UNUSED(key);
21
22
6.76k
    if (json_segment_is_backup(val))
23
61
      continue;
24
25
6.70k
    tmp = json_segment_get_offset(val, blockwise);
26
27
6.70k
    if (!tmp)
28
2.93k
      return tmp;
29
30
3.77k
    if (tmp < min)
31
3.75k
      min = tmp;
32
3.77k
  }
33
34
3.73k
  return min;
35
6.66k
}
36
37
uint64_t json_segment_get_offset(json_object *jobj_segment, unsigned blockwise)
38
13.6k
{
39
13.6k
  json_object *jobj;
40
41
13.6k
  if (!jobj_segment ||
42
13.6k
      !json_object_object_get_ex(jobj_segment, "offset", &jobj))
43
0
    return 0;
44
45
13.6k
  return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
46
13.6k
}
47
48
const char *json_segment_type(json_object *jobj_segment)
49
12
{
50
12
  json_object *jobj;
51
52
12
  if (!jobj_segment ||
53
12
      !json_object_object_get_ex(jobj_segment, "type", &jobj))
54
0
    return NULL;
55
56
12
  return json_object_get_string(jobj);
57
12
}
58
59
uint64_t json_segment_get_iv_offset(json_object *jobj_segment)
60
0
{
61
0
  json_object *jobj;
62
63
0
  if (!jobj_segment ||
64
0
      !json_object_object_get_ex(jobj_segment, "iv_tweak", &jobj))
65
0
    return 0;
66
67
0
  return crypt_jobj_get_uint64(jobj);
68
0
}
69
70
uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise)
71
6.89k
{
72
6.89k
  json_object *jobj;
73
74
6.89k
  if (!jobj_segment ||
75
6.89k
      !json_object_object_get_ex(jobj_segment, "size", &jobj))
76
0
    return 0;
77
78
6.89k
  return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
79
6.89k
}
80
81
static uint64_t json_segment_get_opal_size(json_object *jobj_segment, unsigned blockwise)
82
0
{
83
0
  json_object *jobj;
84
85
0
  if (!jobj_segment ||
86
0
      !json_object_object_get_ex(jobj_segment, "opal_segment_size", &jobj))
87
0
    return 0;
88
89
0
  return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
90
0
}
91
92
static bool json_segment_set_size(json_object *jobj_segment, const uint64_t *size_bytes)
93
0
{
94
0
  json_object *jobj;
95
96
0
  if (!jobj_segment)
97
0
    return false;
98
99
0
  jobj = size_bytes ? crypt_jobj_new_uint64(*size_bytes) : json_object_new_string("dynamic");
100
0
  if (!jobj)
101
0
    return false;
102
103
0
  json_object_object_add(jobj_segment, "size", jobj);
104
105
0
  return true;
106
0
}
107
108
const char *json_segment_get_cipher(json_object *jobj_segment)
109
0
{
110
0
  json_object *jobj;
111
112
  /* FIXME: Pseudo "null" cipher should be handled elsewhere */
113
0
  if (!jobj_segment ||
114
0
      !json_object_object_get_ex(jobj_segment, "encryption", &jobj))
115
0
    return "null";
116
117
0
  return json_object_get_string(jobj);
118
0
}
119
120
uint32_t json_segment_get_sector_size(json_object *jobj_segment)
121
2.29k
{
122
2.29k
  json_object *jobj;
123
2.29k
  int i;
124
125
2.29k
  if (!jobj_segment ||
126
2.29k
            !json_object_object_get_ex(jobj_segment, "sector_size", &jobj))
127
1.98k
    return SECTOR_SIZE;
128
129
310
  i = json_object_get_int(jobj);
130
310
  return i < 0 ? SECTOR_SIZE : i;
131
2.29k
}
132
133
int json_segment_get_opal_segment_id(json_object *jobj_segment, uint32_t *ret_opal_segment_id)
134
0
{
135
0
  json_object *jobj_segment_id;
136
137
0
  assert(ret_opal_segment_id);
138
139
0
  if (!json_object_object_get_ex(jobj_segment, "opal_segment_number", &jobj_segment_id))
140
0
    return -EINVAL;
141
142
0
  *ret_opal_segment_id = json_object_get_int(jobj_segment_id);
143
144
0
  return 0;
145
0
}
146
147
int json_segment_get_opal_key_size(json_object *jobj_segment, size_t *ret_key_size)
148
0
{
149
0
  json_object *jobj_key_size;
150
151
0
  assert(ret_key_size);
152
153
0
  if (!jobj_segment)
154
0
    return -EINVAL;
155
156
0
  if (!json_object_object_get_ex(jobj_segment, "opal_key_size", &jobj_key_size))
157
0
    return -EINVAL;
158
159
0
  *ret_key_size = json_object_get_int(jobj_key_size);
160
161
0
  return 0;
162
0
}
163
164
static json_object *json_segment_get_flags(json_object *jobj_segment)
165
16.6k
{
166
16.6k
  json_object *jobj;
167
168
16.6k
  if (!jobj_segment || !(json_object_object_get_ex(jobj_segment, "flags", &jobj)))
169
1.90k
    return NULL;
170
14.7k
  return jobj;
171
16.6k
}
172
173
bool json_segment_contains_flag(json_object *jobj_segment, const char *flag_str, size_t len)
174
16.6k
{
175
16.6k
  int r, i;
176
16.6k
  json_object *jobj, *jobj_flags = json_segment_get_flags(jobj_segment);
177
178
16.6k
  if (!jobj_flags)
179
1.90k
    return false;
180
181
44.4k
  for (i = 0; i < (int)json_object_array_length(jobj_flags); i++) {
182
30.2k
    jobj = json_object_array_get_idx(jobj_flags, i);
183
30.2k
    if (len)
184
29.3k
      r = strncmp(json_object_get_string(jobj), flag_str, len);
185
858
    else
186
858
      r = strcmp(json_object_get_string(jobj), flag_str);
187
30.2k
    if (!r)
188
539
      return true;
189
30.2k
  }
190
191
14.2k
  return false;
192
14.7k
}
193
194
bool json_segment_is_backup(json_object *jobj_segment)
195
16.6k
{
196
16.6k
  return json_segment_contains_flag(jobj_segment, "backup-", 7);
197
16.6k
}
198
199
json_object *json_segments_get_segment(json_object *jobj_segments, int segment)
200
9.37k
{
201
9.37k
  json_object *jobj;
202
9.37k
  char segment_name[16];
203
204
9.37k
  if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
205
0
    return NULL;
206
207
9.37k
  if (!json_object_object_get_ex(jobj_segments, segment_name, &jobj))
208
30
    return NULL;
209
210
9.34k
  return jobj;
211
9.37k
}
212
213
unsigned json_segments_count(json_object *jobj_segments)
214
2.29k
{
215
2.29k
  unsigned count = 0;
216
217
2.29k
  if (!jobj_segments)
218
0
    return 0;
219
220
2.32k
  json_object_object_foreach(jobj_segments, slot, val) {
221
2.32k
    UNUSED(slot);
222
2.32k
    if (!json_segment_is_backup(val))
223
2.30k
      count++;
224
2.32k
  }
225
226
2.29k
  return count;
227
2.29k
}
228
229
void json_segment_remove_flag(json_object *jobj_segment, const char *flag)
230
0
{
231
0
  json_object *jobj_flags, *jobj_flags_new;
232
233
0
  if (!jobj_segment)
234
0
    return;
235
236
0
  jobj_flags = json_segment_get_flags(jobj_segment);
237
0
  if (!jobj_flags)
238
0
    return;
239
240
0
  jobj_flags_new = LUKS2_array_remove(jobj_flags, flag);
241
0
  if (!jobj_flags_new)
242
0
    return;
243
244
0
  if (json_object_array_length(jobj_flags_new) <= 0) {
245
0
    json_object_put(jobj_flags_new);
246
0
    json_object_object_del(jobj_segment, "flags");
247
0
  } else
248
0
    json_object_object_add(jobj_segment, "flags", jobj_flags_new);
249
0
}
250
251
static json_object *_segment_create_generic(const char *type, uint64_t offset, const uint64_t *length)
252
0
{
253
0
  json_object *jobj = json_object_new_object();
254
0
  if (!jobj)
255
0
    return NULL;
256
257
0
  json_object_object_add(jobj, "type",    json_object_new_string(type));
258
0
  json_object_object_add(jobj, "offset",    crypt_jobj_new_uint64(offset));
259
0
  json_object_object_add(jobj, "size",    length ? crypt_jobj_new_uint64(*length) : json_object_new_string("dynamic"));
260
261
0
  return jobj;
262
0
}
263
264
json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, unsigned reencryption)
265
0
{
266
0
  json_object *jobj = _segment_create_generic("linear", offset, length);
267
0
  if (reencryption)
268
0
    LUKS2_segment_set_flag(jobj, "in-reencryption");
269
0
  return jobj;
270
0
}
271
272
static bool json_add_crypt_fields(json_object *jobj_segment, uint64_t iv_offset,
273
          const char *cipher, const char *integrity, uint32_t integrity_key_size,
274
          uint32_t sector_size, unsigned reencryption)
275
0
{
276
0
  json_object *jobj_integrity;
277
278
0
  assert(cipher);
279
280
0
  json_object_object_add(jobj_segment, "iv_tweak",    crypt_jobj_new_uint64(iv_offset));
281
0
  json_object_object_add(jobj_segment, "encryption",  json_object_new_string(cipher));
282
0
  json_object_object_add(jobj_segment, "sector_size", json_object_new_int(sector_size));
283
284
0
  if (integrity) {
285
0
    jobj_integrity = json_object_new_object();
286
0
    if (!jobj_integrity)
287
0
      return false;
288
289
0
    json_object_object_add(jobj_integrity, "type", json_object_new_string(integrity));
290
0
    json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string("none"));
291
0
    json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string("none"));
292
0
    if (integrity_key_size)
293
0
      json_object_object_add(jobj_integrity, "key_size", json_object_new_int(integrity_key_size));
294
0
    json_object_object_add(jobj_segment,   "integrity", jobj_integrity);
295
0
  }
296
297
0
  if (reencryption)
298
0
    LUKS2_segment_set_flag(jobj_segment, "in-reencryption");
299
300
0
  return true;
301
0
}
302
303
json_object *json_segment_create_crypt(uint64_t offset,
304
          uint64_t iv_offset, const uint64_t *length,
305
          const char *cipher, const char *integrity, uint32_t integrity_key_size,
306
          uint32_t sector_size, unsigned reencryption)
307
0
{
308
0
  json_object *jobj = _segment_create_generic("crypt", offset, length);
309
310
0
  if (!jobj)
311
0
    return NULL;
312
313
0
  if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, integrity_key_size, sector_size, reencryption))
314
0
    return jobj;
315
316
0
  json_object_put(jobj);
317
0
  return NULL;
318
0
}
319
320
static void json_add_opal_fields(json_object *jobj_segment, const uint64_t *length,
321
         uint32_t segment_number, uint32_t key_size)
322
0
{
323
0
  assert(jobj_segment);
324
0
  assert(length);
325
326
0
  json_object_object_add(jobj_segment, "opal_segment_number", json_object_new_int(segment_number));
327
0
  json_object_object_add(jobj_segment, "opal_key_size", json_object_new_int(key_size));
328
0
  json_object_object_add(jobj_segment, "opal_segment_size", crypt_jobj_new_uint64(*length));
329
0
}
330
331
json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
332
              uint32_t segment_number, uint32_t key_size)
333
0
{
334
0
  json_object *jobj = _segment_create_generic("hw-opal", offset, length);
335
0
  if (!jobj)
336
0
    return NULL;
337
338
0
  json_add_opal_fields(jobj, length, segment_number, key_size);
339
340
0
  return jobj;
341
0
}
342
343
json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *length,
344
              uint32_t segment_number, uint32_t key_size,
345
              uint64_t iv_offset, const char *cipher,
346
              const char *integrity, uint32_t sector_size,
347
              unsigned reencryption)
348
0
{
349
0
  json_object *jobj = _segment_create_generic("hw-opal-crypt", offset, length);
350
0
  if (!jobj)
351
0
    return NULL;
352
353
0
  json_add_opal_fields(jobj, length, segment_number, key_size);
354
355
0
  if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, 0, sector_size, reencryption))
356
0
    return jobj;
357
358
0
  json_object_put(jobj);
359
0
  return NULL;
360
0
}
361
362
uint64_t LUKS2_segment_offset(struct luks2_hdr *hdr, int segment, unsigned blockwise)
363
0
{
364
0
  return json_segment_get_offset(LUKS2_get_segment_jobj(hdr, segment), blockwise);
365
0
}
366
367
int json_segments_segment_in_reencrypt(json_object *jobj_segments)
368
0
{
369
0
  json_object *jobj_flags;
370
371
0
  json_object_object_foreach(jobj_segments, slot, val) {
372
0
    if (!json_object_object_get_ex(val, "flags", &jobj_flags) ||
373
0
        !LUKS2_array_jobj(jobj_flags, "in-reencryption"))
374
0
      continue;
375
376
0
    return atoi(slot);
377
0
  }
378
379
0
  return -1;
380
0
}
381
382
uint64_t LUKS2_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise)
383
0
{
384
0
  return json_segment_get_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
385
0
}
386
387
uint64_t LUKS2_opal_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise)
388
0
{
389
0
  return json_segment_get_opal_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
390
0
}
391
392
bool LUKS2_segment_set_size(struct luks2_hdr *hdr, int segment, const uint64_t *segment_size_bytes)
393
0
{
394
0
  return json_segment_set_size(LUKS2_get_segment_jobj(hdr, segment), segment_size_bytes);
395
0
}
396
397
int LUKS2_segment_is_type(struct luks2_hdr *hdr, int segment, const char *type)
398
0
{
399
0
  return !strcmp(json_segment_type(LUKS2_get_segment_jobj(hdr, segment)) ?: "", type);
400
0
}
401
402
static bool json_segment_is_hw_opal_only(json_object *jobj_segment)
403
0
{
404
0
  const char *type = json_segment_type(jobj_segment);
405
406
0
  if (!type)
407
0
    return false;
408
409
0
  return !strcmp(type, "hw-opal");
410
0
}
411
412
static bool json_segment_is_hw_opal_crypt(json_object *jobj_segment)
413
0
{
414
0
  const char *type = json_segment_type(jobj_segment);
415
416
0
  if (!type)
417
0
    return false;
418
419
0
  return !strcmp(type, "hw-opal-crypt");
420
0
}
421
422
static bool json_segment_is_hw_opal(json_object *jobj_segment)
423
0
{
424
0
  return json_segment_is_hw_opal_crypt(jobj_segment) ||
425
0
         json_segment_is_hw_opal_only(jobj_segment);
426
0
}
427
428
bool LUKS2_segment_is_hw_opal_only(struct luks2_hdr *hdr, int segment)
429
0
{
430
0
  return json_segment_is_hw_opal_only(LUKS2_get_segment_jobj(hdr, segment));
431
0
}
432
433
bool LUKS2_segment_is_hw_opal_crypt(struct luks2_hdr *hdr, int segment)
434
0
{
435
0
  return json_segment_is_hw_opal_crypt(LUKS2_get_segment_jobj(hdr, segment));
436
0
}
437
438
bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment)
439
0
{
440
0
  return json_segment_is_hw_opal(LUKS2_get_segment_jobj(hdr, segment));
441
0
}
442
443
int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment, uint32_t *ret_opal_segment_number)
444
0
{
445
0
  json_object *jobj_segment = LUKS2_get_segment_jobj(hdr, segment);
446
447
0
  assert(ret_opal_segment_number);
448
449
0
  if (!json_segment_is_hw_opal(jobj_segment))
450
0
    return -ENOENT;
451
452
0
  return json_segment_get_opal_segment_id(jobj_segment, ret_opal_segment_number);
453
0
}
454
455
int LUKS2_get_opal_key_size(struct luks2_hdr *hdr, int segment)
456
0
{
457
0
  size_t key_size = 0;
458
0
  json_object *jobj_segment = LUKS2_get_segment_jobj(hdr, segment);
459
460
0
  if (json_segment_get_opal_key_size(jobj_segment, &key_size) < 0)
461
0
    return 0;
462
463
0
  return key_size;
464
0
}
465
466
int LUKS2_last_segment_by_type(struct luks2_hdr *hdr, const char *type)
467
0
{
468
0
  json_object *jobj_segments;
469
0
  int last_found = -1;
470
471
0
  if (!type)
472
0
    return -1;
473
474
0
  if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
475
0
    return -1;
476
477
0
  json_object_object_foreach(jobj_segments, slot, val) {
478
0
    if (json_segment_is_backup(val))
479
0
      continue;
480
0
    if (strcmp(type, json_segment_type(val) ?: ""))
481
0
      continue;
482
483
0
    if (atoi(slot) > last_found)
484
0
      last_found = atoi(slot);
485
0
  }
486
487
0
  return last_found;
488
0
}
489
490
int LUKS2_segment_by_type(struct luks2_hdr *hdr, const char *type)
491
0
{
492
0
  json_object *jobj_segments;
493
0
  int first_found = -1;
494
495
0
  if (!type)
496
0
    return -EINVAL;
497
498
0
  if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
499
0
    return -EINVAL;
500
501
0
  json_object_object_foreach(jobj_segments, slot, val) {
502
0
    if (json_segment_is_backup(val))
503
0
      continue;
504
0
    if (strcmp(type, json_segment_type(val) ?: ""))
505
0
      continue;
506
507
0
    if (first_found < 0)
508
0
      first_found = atoi(slot);
509
0
    else if (atoi(slot) < first_found)
510
0
      first_found = atoi(slot);
511
0
  }
512
513
0
  return first_found;
514
0
}
515
516
int LUKS2_segment_first_unused_id(struct luks2_hdr *hdr)
517
0
{
518
0
  json_object *jobj_segments;
519
520
0
  if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
521
0
    return -EINVAL;
522
523
0
  return json_object_object_length(jobj_segments);
524
0
}
525
526
int LUKS2_segment_set_flag(json_object *jobj_segment, const char *flag)
527
0
{
528
0
  json_object *jobj_flags;
529
530
0
  if (!jobj_segment || !flag)
531
0
    return -EINVAL;
532
533
0
  if (!json_object_object_get_ex(jobj_segment, "flags", &jobj_flags)) {
534
0
    jobj_flags = json_object_new_array();
535
0
    if (!jobj_flags)
536
0
      return -ENOMEM;
537
0
    json_object_object_add(jobj_segment, "flags", jobj_flags);
538
0
  }
539
540
0
  if (LUKS2_array_jobj(jobj_flags, flag))
541
0
    return 0;
542
543
0
  json_object_array_add(jobj_flags, json_object_new_string(flag));
544
545
0
  return 0;
546
0
}
547
548
int LUKS2_segments_set(struct crypt_device *cd, struct luks2_hdr *hdr,
549
           json_object *jobj_segments, int commit)
550
0
{
551
0
  json_object_object_add(hdr->jobj, "segments", jobj_segments);
552
553
0
  return commit ? LUKS2_hdr_write(cd, hdr) : 0;
554
0
}
555
556
int LUKS2_get_segment_id_by_flag(struct luks2_hdr *hdr, const char *flag)
557
2.29k
{
558
2.29k
  json_object *jobj_flags, *jobj_segments = LUKS2_get_segments_jobj(hdr);
559
560
2.29k
  if (!flag || !jobj_segments)
561
0
    return -ENOENT;
562
563
2.33k
  json_object_object_foreach(jobj_segments, key, value) {
564
2.33k
    if (!json_object_object_get_ex(value, "flags", &jobj_flags))
565
334
      continue;
566
1.99k
    if (LUKS2_array_jobj(jobj_flags, flag))
567
1
      return atoi(key);
568
1.99k
  }
569
570
2.29k
  return -ENOENT;
571
2.29k
}
572
573
json_object *LUKS2_get_segment_by_flag(struct luks2_hdr *hdr, const char *flag)
574
0
{
575
0
  json_object *jobj_flags, *jobj_segments = LUKS2_get_segments_jobj(hdr);
576
577
0
  if (!flag || !jobj_segments)
578
0
    return NULL;
579
580
0
  json_object_object_foreach(jobj_segments, key, value) {
581
0
    UNUSED(key);
582
0
    if (!json_object_object_get_ex(value, "flags", &jobj_flags))
583
0
      continue;
584
0
    if (LUKS2_array_jobj(jobj_flags, flag))
585
0
      return value;
586
0
  }
587
588
0
  return NULL;
589
0
}
590
591
/* compares key characteristics of both segments */
592
bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2)
593
6
{
594
6
  const char *type = json_segment_type(jobj_segment_1);
595
6
  const char *type2 = json_segment_type(jobj_segment_2);
596
597
6
  if (!type || !type2)
598
0
    return false;
599
600
6
  if (strcmp(type, type2))
601
6
    return false;
602
603
0
  if (!strcmp(type, "crypt"))
604
0
    return (json_segment_get_sector_size(jobj_segment_1) == json_segment_get_sector_size(jobj_segment_2) &&
605
0
      !strcmp(json_segment_get_cipher(jobj_segment_1),
606
0
              json_segment_get_cipher(jobj_segment_2)));
607
608
0
  return true;
609
0
}
610
611
bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr)
612
0
{
613
0
  json_object *jobj_segments, *jobj_size;
614
615
0
  assert(hdr);
616
617
0
  jobj_segments = LUKS2_get_segments_jobj(hdr);
618
0
  if (!jobj_segments)
619
0
    return false;
620
621
0
  json_object_object_foreach(jobj_segments, key, val) {
622
0
    UNUSED(key);
623
624
0
    if (json_segment_is_backup(val))
625
0
      continue;
626
627
0
    if (json_object_object_get_ex(val, "size", &jobj_size) &&
628
0
        !strcmp(json_object_get_string(jobj_size), "dynamic"))
629
0
      return true;
630
0
  }
631
632
0
  return false;
633
0
}