Coverage Report

Created: 2025-10-12 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/verity/verity.c
Line
Count
Source
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2
/*
3
 * dm-verity volume handling
4
 *
5
 * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved.
6
 */
7
8
#include <errno.h>
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <string.h>
12
#include <stdint.h>
13
#include <ctype.h>
14
#include <sys/types.h>
15
#include <sys/stat.h>
16
#include <uuid/uuid.h>
17
18
#include "libcryptsetup.h"
19
#include "verity.h"
20
#include "internal.h"
21
22
0
#define VERITY_SIGNATURE "verity\0\0"
23
24
/* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity#verity-superblock-format */
25
struct verity_sb {
26
  uint8_t  signature[8];  /* "verity\0\0" */
27
  uint32_t version; /* superblock version */
28
  uint32_t hash_type; /* 0 - Chrome OS, 1 - normal */
29
  uint8_t  uuid[16];  /* UUID of hash device */
30
  uint8_t  algorithm[32];/* hash algorithm name */
31
  uint32_t data_block_size; /* data block in bytes */
32
  uint32_t hash_block_size; /* hash block in bytes */
33
  uint64_t data_blocks; /* number of data blocks */
34
  uint16_t salt_size; /* salt size */
35
  uint8_t  _pad1[6];
36
  uint8_t  salt[256]; /* salt */
37
  uint8_t  _pad2[168];
38
} __attribute__((packed));
39
40
/* Read verity superblock from disk */
41
int VERITY_read_sb(struct crypt_device *cd,
42
       uint64_t sb_offset,
43
       char **uuid_string,
44
       struct crypt_params_verity *params)
45
0
{
46
0
  struct device *device = crypt_metadata_device(cd);
47
0
  struct verity_sb sb = {};
48
0
  ssize_t hdr_size = sizeof(struct verity_sb);
49
0
  int devfd, sb_version;
50
51
0
  log_dbg(cd, "Reading VERITY header of size %zu on device %s, offset %" PRIu64 ".",
52
0
    sizeof(struct verity_sb), device_path(device), sb_offset);
53
54
0
  if (params->flags & CRYPT_VERITY_NO_HEADER) {
55
0
    log_err(cd, _("Verity device %s does not use on-disk header."),
56
0
      device_path(device));
57
0
    return -EINVAL;
58
0
  }
59
60
0
  if (MISALIGNED_512(sb_offset)) {
61
0
    log_err(cd, _("Unsupported VERITY hash offset."));
62
0
    return -EINVAL;
63
0
  }
64
65
0
  devfd = device_open(cd, device, O_RDONLY);
66
0
  if (devfd < 0) {
67
0
    log_err(cd, _("Cannot open device %s."), device_path(device));
68
0
    return -EINVAL;
69
0
  }
70
71
0
  if (read_lseek_blockwise(devfd, device_block_size(cd, device),
72
0
         device_alignment(device), &sb, hdr_size,
73
0
         sb_offset) < hdr_size)
74
0
    return -EIO;
75
76
0
  if (memcmp(sb.signature, VERITY_SIGNATURE, sizeof(sb.signature))) {
77
0
    log_dbg(cd, "No VERITY signature detected.");
78
0
    return -EINVAL;
79
0
  }
80
81
0
  sb_version = le32_to_cpu(sb.version);
82
0
  if (sb_version != 1) {
83
0
    log_err(cd, _("Unsupported VERITY version %d."), sb_version);
84
0
    return -EINVAL;
85
0
  }
86
0
  params->hash_type = le32_to_cpu(sb.hash_type);
87
0
  if (params->hash_type > VERITY_MAX_HASH_TYPE) {
88
0
    log_err(cd, _("Unsupported VERITY hash type %d."), params->hash_type);
89
0
    return -EINVAL;
90
0
  }
91
92
0
  params->data_block_size = le32_to_cpu(sb.data_block_size);
93
0
  params->hash_block_size = le32_to_cpu(sb.hash_block_size);
94
0
  if (VERITY_BLOCK_SIZE_OK(params->data_block_size) ||
95
0
      VERITY_BLOCK_SIZE_OK(params->hash_block_size)) {
96
0
    log_err(cd, _("Unsupported VERITY block size."));
97
0
    return -EINVAL;
98
0
  }
99
0
  params->data_size = le64_to_cpu(sb.data_blocks);
100
101
  /* Update block size to be used for loop devices */
102
0
  device_set_block_size(crypt_metadata_device(cd), params->hash_block_size);
103
0
  device_set_block_size(crypt_data_device(cd), params->data_block_size);
104
105
0
  params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm));
106
0
  if (!params->hash_name)
107
0
    return -ENOMEM;
108
0
  if (crypt_hash_size(params->hash_name) <= 0) {
109
0
    log_err(cd, _("Hash algorithm %s not supported."),
110
0
      params->hash_name);
111
0
    free(CONST_CAST(char*)params->hash_name);
112
0
    params->hash_name = NULL;
113
0
    return -EINVAL;
114
0
  }
115
116
0
  params->salt_size = le16_to_cpu(sb.salt_size);
117
0
  if (params->salt_size > sizeof(sb.salt)) {
118
0
    log_err(cd, _("VERITY header corrupted."));
119
0
    free(CONST_CAST(char*)params->hash_name);
120
0
    params->hash_name = NULL;
121
0
    return -EINVAL;
122
0
  }
123
0
  params->salt = malloc(params->salt_size);
124
0
  if (!params->salt) {
125
0
    free(CONST_CAST(char*)params->hash_name);
126
0
    params->hash_name = NULL;
127
0
    return -ENOMEM;
128
0
  }
129
0
  memcpy(CONST_CAST(char*)params->salt, sb.salt, params->salt_size);
130
131
0
  if ((*uuid_string = malloc(40)))
132
0
    uuid_unparse(sb.uuid, *uuid_string);
133
134
0
  params->hash_area_offset = sb_offset;
135
0
  return 0;
136
0
}
137
138
static void _to_lower(char *str)
139
0
{
140
0
  for(; *str; str++)
141
0
    if (isupper(*str))
142
0
      *str = tolower(*str);
143
0
}
144
145
/* Write verity superblock to disk */
146
int VERITY_write_sb(struct crypt_device *cd,
147
       uint64_t sb_offset,
148
       const char *uuid_string,
149
       struct crypt_params_verity *params)
150
0
{
151
0
  struct device *device = crypt_metadata_device(cd);
152
0
  struct verity_sb sb = {};
153
0
  ssize_t hdr_size = sizeof(struct verity_sb);
154
0
  size_t block_size;
155
0
  char *algorithm;
156
0
  uuid_t uuid;
157
0
  int r, devfd;
158
159
0
  log_dbg(cd, "Updating VERITY header of size %zu on device %s, offset %" PRIu64 ".",
160
0
    sizeof(struct verity_sb), device_path(device), sb_offset);
161
162
0
  if (!uuid_string || uuid_parse(uuid_string, uuid) == -1) {
163
0
    log_err(cd, _("Wrong VERITY UUID format provided on device %s."),
164
0
      device_path(device));
165
0
    return -EINVAL;
166
0
  }
167
168
0
  if (params->flags & CRYPT_VERITY_NO_HEADER) {
169
0
    log_err(cd, _("Verity device %s does not use on-disk header."),
170
0
      device_path(device));
171
0
    return -EINVAL;
172
0
  }
173
174
  /* Avoid possible increasing of image size - FEC could fail later because of it */
175
0
  block_size = device_block_size(cd, device);
176
0
  if (block_size > params->hash_block_size) {
177
0
    device_disable_direct_io(device);
178
0
    block_size = params->hash_block_size;
179
0
  }
180
181
0
  devfd = device_open(cd, device, O_RDWR);
182
0
  if (devfd < 0) {
183
0
    log_err(cd, _("Cannot open device %s."), device_path(device));
184
0
    return -EINVAL;
185
0
  }
186
187
0
  memcpy(&sb.signature, VERITY_SIGNATURE, sizeof(sb.signature));
188
0
  sb.version         = cpu_to_le32(1);
189
0
  sb.hash_type       = cpu_to_le32(params->hash_type);
190
0
  sb.data_block_size = cpu_to_le32(params->data_block_size);
191
0
  sb.hash_block_size = cpu_to_le32(params->hash_block_size);
192
0
  sb.salt_size       = cpu_to_le16(params->salt_size);
193
0
  sb.data_blocks     = cpu_to_le64(params->data_size);
194
195
  /* Kernel always use lower-case */
196
0
  algorithm = (char *)sb.algorithm;
197
0
  strncpy(algorithm, params->hash_name, sizeof(sb.algorithm)-1);
198
0
  algorithm[sizeof(sb.algorithm)-1] = '\0';
199
0
  _to_lower(algorithm);
200
201
0
  memcpy(sb.salt, params->salt, params->salt_size);
202
0
  memcpy(sb.uuid, uuid, sizeof(sb.uuid));
203
204
0
  r = write_lseek_blockwise(devfd, block_size, device_alignment(device),
205
0
          (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0;
206
0
  if (r)
207
0
    log_err(cd, _("Error during update of verity header on device %s."),
208
0
      device_path(device));
209
210
0
  device_sync(cd, device);
211
212
0
  return r;
213
0
}
214
215
/* Calculate hash offset in hash blocks */
216
uint64_t VERITY_hash_offset_block(struct crypt_params_verity *params)
217
0
{
218
0
  uint64_t hash_offset = params->hash_area_offset;
219
220
0
  if (params->flags & CRYPT_VERITY_NO_HEADER)
221
0
    return hash_offset / params->hash_block_size;
222
223
0
  hash_offset += sizeof(struct verity_sb);
224
0
  hash_offset += params->hash_block_size - 1;
225
226
0
  return hash_offset / params->hash_block_size;
227
0
}
228
229
int VERITY_UUID_generate(char **uuid_string)
230
0
{
231
0
  uuid_t uuid;
232
233
0
  *uuid_string = malloc(40);
234
0
  if (!*uuid_string)
235
0
    return -ENOMEM;
236
0
  uuid_generate(uuid);
237
0
  uuid_unparse(uuid, *uuid_string);
238
0
  return 0;
239
0
}
240
241
int VERITY_verify_params(struct crypt_device *cd,
242
  struct crypt_params_verity *hdr,
243
  bool signed_root_hash,
244
  struct device *fec_device,
245
  struct volume_key *root_hash)
246
0
{
247
0
  bool userspace_verification;
248
0
  int v, r;
249
0
  unsigned int fec_errors = 0;
250
251
0
  assert(cd);
252
0
  assert(hdr);
253
0
  assert(root_hash);
254
255
0
  log_dbg(cd, "Verifying VERITY device using hash %s.",
256
0
    hdr->hash_name);
257
258
0
  userspace_verification = hdr->flags & CRYPT_VERITY_CHECK_HASH;
259
260
0
  if (userspace_verification && signed_root_hash) {
261
0
    log_err(cd, _("Root hash signature verification is not supported."));
262
0
    return -EINVAL;
263
0
  }
264
265
0
  if ((hdr->flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE) && !signed_root_hash) {
266
0
    log_err(cd, _("Root hash signature required."));
267
0
    return -EINVAL;
268
0
  }
269
270
0
  if (!userspace_verification)
271
0
    return 0;
272
273
0
  log_dbg(cd, "Verification of VERITY data in userspace required.");
274
0
  r = VERITY_verify(cd, hdr, crypt_volume_key_get_key(root_hash),
275
0
        crypt_volume_key_length(root_hash));
276
277
0
  if ((r == -EPERM || r == -EFAULT) && fec_device) {
278
0
    v = r;
279
0
    log_dbg(cd, "Verification failed, trying to repair with FEC device.");
280
0
    r = VERITY_FEC_process(cd, hdr, fec_device, 1, &fec_errors);
281
0
    if (r < 0)
282
0
      log_err(cd, _("Errors cannot be repaired with FEC device."));
283
0
    else if (fec_errors) {
284
0
      log_err(cd, _("Found %u repairable errors with FEC device."),
285
0
        fec_errors);
286
      /* If root hash failed, we cannot be sure it was properly repaired */
287
0
    }
288
0
    if (v == -EFAULT)
289
0
      r = -EPERM;
290
0
  }
291
292
0
  return r;
293
0
}
294
295
/* Activate verity device in kernel device-mapper */
296
int VERITY_activate(struct crypt_device *cd,
297
         const char *name,
298
         struct volume_key *root_hash,
299
         struct volume_key *signature,
300
         struct device *fec_device,
301
         struct crypt_params_verity *verity_hdr,
302
         uint32_t activation_flags)
303
0
{
304
0
  uint64_t dmv_flags;
305
0
  int r;
306
0
  key_serial_t kid = 0;
307
0
  char *description = NULL;
308
0
  struct crypt_dm_active_device dmd = { 0 };
309
310
0
  assert(name);
311
0
  assert(root_hash);
312
0
  assert(verity_hdr);
313
314
0
  dmd.size = verity_hdr->data_size * verity_hdr->data_block_size / 512;
315
0
  dmd.flags = activation_flags;
316
0
  dmd.uuid = crypt_get_uuid(cd);
317
318
0
  log_dbg(cd, "Activating VERITY device %s using hash %s.",
319
0
    name, verity_hdr->hash_name);
320
321
0
  if (signature) {
322
0
    r = asprintf(&description, "cryptsetup:%s%s%s",
323
0
       crypt_get_uuid(cd) ?: "", crypt_get_uuid(cd) ? "-" : "", name);
324
0
    if (r < 0)
325
0
      return -EINVAL;
326
327
0
    log_dbg(cd, "Adding signature %s (type user) into thread keyring.", description);
328
0
    kid = keyring_add_key_in_thread_keyring(USER_KEY, description,
329
0
              crypt_volume_key_get_key(signature),
330
0
              crypt_volume_key_length(signature));
331
0
    if (kid < 0) {
332
0
      log_dbg(cd, "keyring_add_key_in_thread_keyring failed with errno %d.", errno);
333
0
      log_err(cd, _("Failed to load key in kernel keyring."));
334
0
      free(description);
335
0
      return -EINVAL;
336
0
    }
337
0
  }
338
339
0
  r = device_block_adjust(cd, crypt_metadata_device(cd), DEV_OK,
340
0
        0, NULL, NULL);
341
0
  if (r)
342
0
    goto out;
343
344
0
  r = device_block_adjust(cd, crypt_data_device(cd),
345
0
        activation_flags & CRYPT_ACTIVATE_SHARED ? DEV_OK : DEV_EXCL,
346
0
        0, &dmd.size, &dmd.flags);
347
0
  if (r)
348
0
    goto out;
349
350
0
  if (fec_device) {
351
0
    r = device_block_adjust(cd, fec_device, DEV_OK,
352
0
          0, NULL, NULL);
353
0
    if (r)
354
0
      goto out;
355
0
  }
356
357
0
  r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
358
0
      crypt_metadata_device(cd), fec_device, crypt_volume_key_get_key(root_hash),
359
0
      crypt_volume_key_length(root_hash), description,
360
0
      VERITY_hash_offset_block(verity_hdr),
361
0
      VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr);
362
363
0
  if (r)
364
0
    goto out;
365
366
0
  r = dm_create_device(cd, name, CRYPT_VERITY, &dmd);
367
0
  if (r < 0 && (dm_flags(cd, DM_VERITY, &dmv_flags) || !(dmv_flags & DM_VERITY_SUPPORTED))) {
368
0
    log_err(cd, _("Kernel does not support dm-verity mapping."));
369
0
    r = -ENOTSUP;
370
0
  }
371
0
  if (r < 0 && signature && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
372
0
    log_err(cd, _("Kernel does not support dm-verity signature option."));
373
0
    r = -ENOTSUP;
374
0
  }
375
0
  if (r < 0)
376
0
    goto out;
377
378
0
  r = dm_status_verity_ok(cd, name);
379
0
  if (r < 0)
380
0
    goto out;
381
382
0
  if (!r)
383
0
    log_err(cd, _("Verity device detected corruption after activation."));
384
385
0
  r = 0;
386
0
out:
387
0
  if (signature) {
388
0
    log_dbg(cd, "Unlinking signature (id: %" PRIi32 ") from thread keyring.", kid);
389
390
0
    if (keyring_unlink_key_from_thread_keyring(kid))
391
0
      log_dbg(cd, "keyring_unlink_key_from_thread_keyring failed with errno %d.", errno);
392
0
  }
393
0
  free(description);
394
0
  dm_targets_free(cd, &dmd);
395
0
  return r;
396
0
}
397
398
int VERITY_dump(struct crypt_device *cd,
399
    struct crypt_params_verity *verity_hdr,
400
    const char *root_hash,
401
    unsigned int root_hash_size,
402
    struct device *fec_device)
403
0
{
404
0
  uint64_t hash_blocks, verity_blocks, fec_blocks = 0, rs_blocks = 0;
405
0
  bool fec_on_hash_device = false;
406
407
0
  hash_blocks  = VERITY_hash_blocks(cd, verity_hdr);
408
0
  verity_blocks = VERITY_hash_offset_block(verity_hdr) + hash_blocks;
409
410
0
  if (fec_device && verity_hdr->fec_roots) {
411
0
    fec_blocks = VERITY_FEC_blocks(cd, fec_device, verity_hdr);
412
0
    rs_blocks  = VERITY_FEC_RS_blocks(fec_blocks, verity_hdr->fec_roots);
413
0
    fec_on_hash_device = device_is_identical(crypt_metadata_device(cd), fec_device) > 0;
414
    /*
415
    * No way to access fec_area_offset directly.
416
    * Assume FEC area starts directly after hash blocks.
417
    */
418
0
    if (fec_on_hash_device)
419
0
      verity_blocks += rs_blocks;
420
0
  }
421
422
0
  log_std(cd, "VERITY header information for %s.\n", device_path(crypt_metadata_device(cd)));
423
0
  log_std(cd, "UUID:            \t%s\n", crypt_get_uuid(cd) ?: "");
424
0
  log_std(cd, "Hash type:       \t%u\n", verity_hdr->hash_type);
425
0
  log_std(cd, "Data blocks:     \t%" PRIu64 "\n", verity_hdr->data_size);
426
0
  log_std(cd, "Data block size: \t%u [bytes]\n", verity_hdr->data_block_size);
427
0
  log_std(cd, "Hash blocks:     \t%" PRIu64 "\n", hash_blocks);
428
0
  log_std(cd, "Hash block size: \t%u [bytes]\n", verity_hdr->hash_block_size);
429
0
  log_std(cd, "Hash algorithm:  \t%s\n", verity_hdr->hash_name);
430
0
  if (fec_device && fec_blocks) {
431
0
    log_std(cd, "FEC RS roots:   \t%" PRIu32 "\n", verity_hdr->fec_roots);
432
0
    log_std(cd, "FEC blocks:     \t%" PRIu64 "\n", rs_blocks);
433
0
  }
434
435
0
  log_std(cd, "Salt:            \t");
436
0
  if (verity_hdr->salt_size)
437
0
    crypt_log_hex(cd, verity_hdr->salt, verity_hdr->salt_size, "", 0, NULL);
438
0
  else
439
0
    log_std(cd, "-");
440
0
  log_std(cd, "\n");
441
442
0
  if (root_hash) {
443
0
    log_std(cd, "Root hash:      \t");
444
0
    crypt_log_hex(cd, root_hash, root_hash_size, "", 0, NULL);
445
0
    log_std(cd, "\n");
446
0
  }
447
448
  /* As dump can take only hash device, we have no idea about offsets here. */
449
0
  if (verity_hdr->hash_area_offset == 0)
450
0
    log_std(cd, "Hash device size: \t%" PRIu64 " [bytes]\n", verity_blocks * verity_hdr->hash_block_size);
451
452
0
  if (fec_device && verity_hdr->fec_area_offset == 0 && fec_blocks && !fec_on_hash_device)
453
0
    log_std(cd, "FEC device size: \t%" PRIu64 " [bytes]\n", rs_blocks * verity_hdr->data_block_size);
454
455
0
  return 0;
456
0
}