Coverage Report

Created: 2026-07-24 06:26

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