Coverage Report

Created: 2026-04-11 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/superblocks/bitlocker.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
3
 *
4
 * This file may be redistributed under the terms of the
5
 * GNU Lesser General Public License.
6
 */
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <unistd.h>
10
#include <string.h>
11
#include <errno.h>
12
#include <ctype.h>
13
#include <stdint.h>
14
15
#include "superblocks.h"
16
17
1.00k
#define BDE_HDR_SIZE  512
18
1.00k
#define BDE_HDR_OFFSET  0
19
20
struct bde_header_win7 {
21
/*   0 */ unsigned char boot_entry_point[3];
22
/*   3 */ unsigned char fs_signature[8];
23
/*  11 */ unsigned char __dummy1[67 - 11];
24
/*  67 */ uint32_t      volume_serial;    /* NTFS uses 64bit serial number */
25
/*  71 */ unsigned char volume_label[11]; /* "NO NAME\x20\x20\x20\x20" only */
26
/*  82 */ unsigned char __dummy2[160 - 82];
27
/* 160 */ unsigned char guid[16];   /* BitLocker specific GUID */
28
/* 176 */ uint64_t      fve_metadata_offset;
29
} __attribute__((packed));
30
31
32
struct bde_header_togo {
33
/*   0 */ unsigned char boot_entry_point[3];
34
/*   3 */ unsigned char fs_signature[8];
35
/*  11 */ unsigned char __dummy[424 - 11];
36
/* 424 */ unsigned char guid[16];
37
/* 440 */ uint64_t      fve_metadata_offset;
38
} __attribute__((packed));
39
40
41
struct bde_fve_metadata_block_header {
42
/*   0 */ unsigned char  signature[8];
43
/*   8 */ unsigned char  __dummy1[10 - 8];
44
/*  10 */ uint16_t       version;
45
/*  12 */ unsigned char  __dummy2[64 - 12];
46
} __attribute__((packed));
47
48
struct bde_fve_metadata_header {
49
/*   0 */ uint32_t      size;
50
/*   4 */ uint32_t      version;
51
/*   8 */ uint32_t      header_size;
52
/*  12 */ uint32_t      size_copy;
53
/*  16 */ unsigned char volume_identifier[16];
54
/*  32 */ unsigned char __dummy[48 - 32];
55
} __attribute__((packed));
56
57
struct bde_fve_metadata_entry {
58
/*   0 */ uint16_t      size;
59
/*   2 */ uint16_t      entry_type;
60
/*   4 */ uint16_t      value_type;
61
/*   6 */ uint16_t      version;
62
/*   8 */ unsigned char data[];
63
} __attribute__((packed));
64
65
struct bde_fve_metadata {
66
  struct bde_fve_metadata_block_header block_header;
67
  struct bde_fve_metadata_header header;
68
} __attribute__((packed));
69
70
enum {
71
  BDE_VERSION_VISTA = 0,
72
  BDE_VERSION_WIN7,
73
  BDE_VERSION_TOGO
74
};
75
76
1.00k
#define BDE_MAGIC_VISTA   "\xeb\x52\x90-FVE-FS-"
77
1.00k
#define BDE_MAGIC_WIN7    "\xeb\x58\x90-FVE-FS-"
78
1.00k
#define BDE_MAGIC_TOGO    "\xeb\x58\x90MSWIN4.1"
79
80
109
#define BDE_MAGIC_FVE   "-FVE-FS-"
81
82
1.10k
#define BDE_METADATA_ENTRY_TYPE_DESCRIPTION 0x0007
83
5
#define BDE_METADATA_VALUE_TYPE_STRING      0x0002
84
85
static int get_bitlocker_type(const unsigned char *buf)
86
1.00k
{
87
1.00k
  size_t i;
88
1.00k
  static const char *const map[] = {
89
1.00k
    [BDE_VERSION_VISTA] = BDE_MAGIC_VISTA,
90
1.00k
    [BDE_VERSION_WIN7]  = BDE_MAGIC_WIN7,
91
1.00k
    [BDE_VERSION_TOGO]  = BDE_MAGIC_TOGO
92
1.00k
  };
93
94
3.31k
  for (i = 0; i < ARRAY_SIZE(map); i++) {
95
2.86k
    if (memcmp(buf, map[i], 11) == 0)
96
547
      return (int) i;
97
2.86k
  }
98
99
454
  return -1;
100
1.00k
}
101
102
/* Returns: < 0 error, 1 nothing, 0 success
103
 */
104
static int get_bitlocker_headers(blkid_probe pr,
105
        int *type,
106
        const unsigned char **buf_hdr,
107
        const unsigned char **buf_fve)
108
1.00k
{
109
110
1.00k
  const unsigned char *buf;
111
1.00k
  const struct bde_fve_metadata *fve;
112
1.00k
  uint64_t off = 0;
113
1.00k
  int kind;
114
115
1.00k
  if (buf_hdr)
116
328
    *buf_hdr = NULL;
117
1.00k
  if (buf_fve)
118
328
    *buf_fve = NULL;
119
1.00k
  if (type)
120
328
    *type = -1;
121
122
1.00k
  buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE);
123
1.00k
  if (!buf)
124
0
    return errno ? -errno : 1;
125
126
1.00k
  kind = get_bitlocker_type(buf);
127
128
  /* Check BitLocker header */
129
1.00k
  switch (kind) {
130
133
  case BDE_VERSION_WIN7:
131
133
    off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset);
132
133
    break;
133
409
  case BDE_VERSION_TOGO:
134
409
    off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset);
135
409
    break;
136
5
  case BDE_VERSION_VISTA:
137
5
    goto done;
138
454
  default:
139
454
    goto nothing;
140
1.00k
  }
141
142
542
  if (!off || off % 64)
143
187
    goto nothing;
144
355
  if (buf_hdr)
145
211
    *buf_hdr = buf;
146
147
  /* Check Bitlocker FVE metadata header */
148
355
  buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata));
149
355
  if (!buf)
150
246
    return errno ? -errno : 1;
151
152
109
  fve = (const struct bde_fve_metadata *) buf;
153
109
  if (memcmp(fve->block_header.signature, BDE_MAGIC_FVE, sizeof(fve->block_header.signature)) != 0)
154
63
    goto nothing;
155
156
46
  if (buf_fve) {
157
37
    buf = blkid_probe_get_buffer(pr, off,
158
37
      (uint64_t) sizeof(struct bde_fve_metadata_block_header) + le32_to_cpu(fve->header.size));
159
37
    if (!buf)
160
5
      return errno ? -errno : 1;
161
162
32
    *buf_fve = buf;
163
32
  }
164
46
done:
165
46
  if (type)
166
36
    *type = kind;
167
46
  return 0;
168
704
nothing:
169
704
  return 1;
170
46
}
171
172
/*
173
 * This is used by vFAT and NTFS prober to avoid collisions with bitlocker.
174
 */
175
int blkid_probe_is_bitlocker(blkid_probe pr)
176
673
{
177
673
  return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0;
178
673
}
179
180
static int probe_bitlocker(blkid_probe pr,
181
    const struct blkid_idmag *mag __attribute__((__unused__)))
182
328
{
183
328
  const unsigned char *buf_fve = NULL;
184
328
  const unsigned char *buf_hdr = NULL;
185
328
  const struct bde_fve_metadata_entry *entry;
186
328
  int rc, kind;
187
328
  uint64_t off;
188
189
328
  rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve);
190
328
  if (rc)
191
292
    return rc;
192
193
36
  if (buf_fve) {
194
32
    const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve;
195
196
32
    blkid_probe_sprintf_version(pr, "%d", le16_to_cpu(fve->block_header.version));
197
198
32
    for (off = sizeof(struct bde_fve_metadata_header);
199
584
         off + sizeof(struct bde_fve_metadata_entry) < le32_to_cpu(fve->header.size);
200
582
         off += le16_to_cpu(entry->size)) {
201
582
      entry = (const struct bde_fve_metadata_entry *) ((const char *) &fve->header + off);
202
582
      if (off % 2 ||
203
577
          le16_to_cpu(entry->size) < sizeof(struct bde_fve_metadata_entry) ||
204
569
          off + le16_to_cpu(entry->size) > le32_to_cpu(fve->header.size))
205
29
        return -1;
206
207
553
      if (le16_to_cpu(entry->entry_type) == BDE_METADATA_ENTRY_TYPE_DESCRIPTION &&
208
5
          le16_to_cpu(entry->value_type) == BDE_METADATA_VALUE_TYPE_STRING) {
209
1
        blkid_probe_set_utf8label(pr,
210
1
          entry->data, le16_to_cpu(entry->size) - sizeof(struct bde_fve_metadata_entry),
211
1
          UL_ENCODE_UTF16LE);
212
1
        break;
213
1
      }
214
553
    }
215
216
    /* Microsoft GUID format, interpreted as explained by Raymond Chen:
217
     * https://devblogs.microsoft.com/oldnewthing/20220928-00/?p=107221
218
     */
219
3
    blkid_probe_sprintf_uuid(pr, fve->header.volume_identifier, 16,
220
3
      "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
221
3
      fve->header.volume_identifier[3], fve->header.volume_identifier[2], /* uint32_t Data1 */
222
3
      fve->header.volume_identifier[1], fve->header.volume_identifier[0],
223
3
      fve->header.volume_identifier[5], fve->header.volume_identifier[4], /* uint16_t Data2 */
224
3
      fve->header.volume_identifier[7], fve->header.volume_identifier[6], /* uint16_t Data3 */
225
3
      fve->header.volume_identifier[8], fve->header.volume_identifier[9], /* uint8_t Data4[8] */
226
3
      fve->header.volume_identifier[10], fve->header.volume_identifier[11],
227
3
      fve->header.volume_identifier[12], fve->header.volume_identifier[13],
228
3
      fve->header.volume_identifier[14], fve->header.volume_identifier[15]);
229
3
  }
230
7
  return 0;
231
36
}
232
233
/* See header details:
234
 * https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20(BDE)%20format.asciidoc
235
 */
236
const struct blkid_idinfo bitlocker_idinfo =
237
{
238
  .name   = "BitLocker",
239
  .usage    = BLKID_USAGE_CRYPTO,
240
  .probefunc  = probe_bitlocker,
241
  .magics   =
242
  {
243
    { .magic = BDE_MAGIC_VISTA, .len = 11 },
244
    { .magic = BDE_MAGIC_WIN7,  .len = 11 },
245
    { .magic = BDE_MAGIC_TOGO,  .len = 11 },
246
    { NULL }
247
  }
248
};