/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 | | }; |