Coverage Report

Created: 2026-02-22 06:11

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
902
#define BDE_HDR_SIZE  512
18
902
#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
902
#define BDE_MAGIC_VISTA   "\xeb\x52\x90-FVE-FS-"
77
902
#define BDE_MAGIC_WIN7    "\xeb\x58\x90-FVE-FS-"
78
902
#define BDE_MAGIC_TOGO    "\xeb\x58\x90MSWIN4.1"
79
80
145
#define BDE_MAGIC_FVE   "-FVE-FS-"
81
82
894
#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
902
{
87
902
  size_t i;
88
902
  static const char *const map[] = {
89
902
    [BDE_VERSION_VISTA] = BDE_MAGIC_VISTA,
90
902
    [BDE_VERSION_WIN7]  = BDE_MAGIC_WIN7,
91
902
    [BDE_VERSION_TOGO]  = BDE_MAGIC_TOGO
92
902
  };
93
94
2.97k
  for (i = 0; i < ARRAY_SIZE(map); i++) {
95
2.55k
    if (memcmp(buf, map[i], 11) == 0)
96
475
      return (int) i;
97
2.55k
  }
98
99
427
  return -1;
100
902
}
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
902
{
109
110
902
  const unsigned char *buf;
111
902
  const struct bde_fve_metadata *fve;
112
902
  uint64_t off = 0;
113
902
  int kind;
114
115
902
  if (buf_hdr)
116
287
    *buf_hdr = NULL;
117
902
  if (buf_fve)
118
287
    *buf_fve = NULL;
119
902
  if (type)
120
287
    *type = -1;
121
122
902
  buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE);
123
902
  if (!buf)
124
0
    return errno ? -errno : 1;
125
126
902
  kind = get_bitlocker_type(buf);
127
128
  /* Check BitLocker header */
129
902
  switch (kind) {
130
141
  case BDE_VERSION_WIN7:
131
141
    off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset);
132
141
    break;
133
327
  case BDE_VERSION_TOGO:
134
327
    off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset);
135
327
    break;
136
7
  case BDE_VERSION_VISTA:
137
7
    goto done;
138
427
  default:
139
427
    goto nothing;
140
902
  }
141
142
468
  if (!off || off % 64)
143
141
    goto nothing;
144
327
  if (buf_hdr)
145
197
    *buf_hdr = buf;
146
147
  /* Check Bitlocker FVE metadata header */
148
327
  buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata));
149
327
  if (!buf)
150
182
    return errno ? -errno : 1;
151
152
145
  fve = (const struct bde_fve_metadata *) buf;
153
145
  if (memcmp(fve->block_header.signature, BDE_MAGIC_FVE, sizeof(fve->block_header.signature)) != 0)
154
89
    goto nothing;
155
156
56
  if (buf_fve) {
157
43
    buf = blkid_probe_get_buffer(pr, off,
158
43
      (uint64_t) sizeof(struct bde_fve_metadata_block_header) + le32_to_cpu(fve->header.size));
159
43
    if (!buf)
160
8
      return errno ? -errno : 1;
161
162
35
    *buf_fve = buf;
163
35
  }
164
55
done:
165
55
  if (type)
166
40
    *type = kind;
167
55
  return 0;
168
657
nothing:
169
657
  return 1;
170
56
}
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
615
{
177
615
  return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0;
178
615
}
179
180
static int probe_bitlocker(blkid_probe pr,
181
    const struct blkid_idmag *mag __attribute__((__unused__)))
182
287
{
183
287
  const unsigned char *buf_fve = NULL;
184
287
  const unsigned char *buf_hdr = NULL;
185
287
  const struct bde_fve_metadata_entry *entry;
186
287
  int rc, kind;
187
287
  uint64_t off;
188
189
287
  rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve);
190
287
  if (rc)
191
247
    return rc;
192
193
40
  if (buf_fve) {
194
35
    const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve;
195
196
35
    blkid_probe_sprintf_version(pr, "%d", le16_to_cpu(fve->block_header.version));
197
198
35
    for (off = sizeof(struct bde_fve_metadata_header);
199
481
         off + sizeof(struct bde_fve_metadata_entry) < le32_to_cpu(fve->header.size);
200
476
         off += le16_to_cpu(entry->size)) {
201
476
      entry = (const struct bde_fve_metadata_entry *) ((const char *) &fve->header + off);
202
476
      if (off % 2 ||
203
472
          le16_to_cpu(entry->size) < sizeof(struct bde_fve_metadata_entry) ||
204
461
          off + le16_to_cpu(entry->size) > le32_to_cpu(fve->header.size))
205
29
        return -1;
206
207
447
      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
447
    }
215
216
    /* Microsoft GUID format, interpreted as explained by Raymond Chen:
217
     * https://devblogs.microsoft.com/oldnewthing/20220928-00/?p=107221
218
     */
219
6
    blkid_probe_sprintf_uuid(pr, fve->header.volume_identifier, 16,
220
6
      "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
221
6
      fve->header.volume_identifier[3], fve->header.volume_identifier[2], /* uint32_t Data1 */
222
6
      fve->header.volume_identifier[1], fve->header.volume_identifier[0],
223
6
      fve->header.volume_identifier[5], fve->header.volume_identifier[4], /* uint16_t Data2 */
224
6
      fve->header.volume_identifier[7], fve->header.volume_identifier[6], /* uint16_t Data3 */
225
6
      fve->header.volume_identifier[8], fve->header.volume_identifier[9], /* uint8_t Data4[8] */
226
6
      fve->header.volume_identifier[10], fve->header.volume_identifier[11],
227
6
      fve->header.volume_identifier[12], fve->header.volume_identifier[13],
228
6
      fve->header.volume_identifier[14], fve->header.volume_identifier[15]);
229
6
  }
230
11
  return 0;
231
40
}
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
};