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 #define BDE_HDR_SIZE 512
18 #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 {
42 /* 0 */ unsigned char signature[8];
43 /* 8 */ uint16_t size;
44 /* 10 */ uint16_t version;
45 };
46
47 enum {
48 BDE_VERSION_VISTA = 0,
49 BDE_VERSION_WIN7,
50 BDE_VERSION_TOGO
51 };
52
53 #define BDE_MAGIC_VISTA "\xeb\x52\x90-FVE-FS-"
54 #define BDE_MAGIC_WIN7 "\xeb\x58\x90-FVE-FS-"
55 #define BDE_MAGIC_TOGO "\xeb\x58\x90MSWIN4.1"
56
57 #define BDE_MAGIC_FVE "-FVE-FS-"
58
59 static int get_bitlocker_type(const unsigned char *buf)
60 {
61 size_t i;
62 static const char *map[] = {
63 [BDE_VERSION_VISTA] = BDE_MAGIC_VISTA,
64 [BDE_VERSION_WIN7] = BDE_MAGIC_WIN7,
65 [BDE_VERSION_TOGO] = BDE_MAGIC_TOGO
66 };
67
68 for (i = 0; i < ARRAY_SIZE(map); i++) {
69 if (memcmp(buf, map[i], 11) == 0)
70 return (int) i;
71 }
72
73 return -1;
74 }
75
76 /* Returns: < 0 error, 1 nothing, 0 success
77 */
78 static int get_bitlocker_headers(blkid_probe pr,
79 int *type,
80 const unsigned char **buf_hdr,
81 const unsigned char **buf_fve)
82 {
83
84 const unsigned char *buf;
85 const struct bde_fve_metadata *fve;
86 uint64_t off = 0;
87 int kind;
88
89 if (buf_hdr)
90 *buf_hdr = NULL;
91 if (buf_fve)
92 *buf_fve = NULL;
93 if (type)
94 *type = -1;
95
96 buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE);
97 if (!buf)
98 return errno ? -errno : 1;
99
100 kind = get_bitlocker_type(buf);
101
102 /* Check BitLocker header */
103 switch (kind) {
104 case BDE_VERSION_WIN7:
105 off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset);
106 break;
107 case BDE_VERSION_TOGO:
108 off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset);
109 break;
110 case BDE_VERSION_VISTA:
111 goto done;
112 default:
113 goto nothing;
114 }
115
116 if (!off || off % 64)
117 goto nothing;
118 if (buf_hdr)
119 *buf_hdr = buf;
120
121 /* Check Bitlocker FVE metadata header */
122 buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata));
123 if (!buf)
124 return errno ? -errno : 1;
125
126 fve = (const struct bde_fve_metadata *) buf;
127 if (memcmp(fve->signature, BDE_MAGIC_FVE, sizeof(fve->signature)) != 0)
128 goto nothing;
129 if (buf_fve)
130 *buf_fve = buf;
131 done:
132 if (type)
133 *type = kind;
134 return 0;
135 nothing:
136 return 1;
137 }
138
139 /*
140 * This is used by vFAT and NTFS prober to avoid collisions with bitlocker.
141 */
142 int blkid_probe_is_bitlocker(blkid_probe pr)
143 {
144 return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0;
145 }
146
147 static int probe_bitlocker(blkid_probe pr,
148 const struct blkid_idmag *mag __attribute__((__unused__)))
149 {
150 const unsigned char *buf_fve = NULL;
151 const unsigned char *buf_hdr = NULL;
152 int rc, kind;
153
154 rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve);
155 if (rc)
156 return rc;
157
158 if (kind == BDE_VERSION_WIN7) {
159 const struct bde_header_win7 *hdr = (const struct bde_header_win7 *) buf_hdr;
160
161 /* Unfortunately, it seems volume_serial is always zero */
162 blkid_probe_sprintf_uuid(pr,
163 (const unsigned char *) &hdr->volume_serial,
164 sizeof(hdr->volume_serial),
165 "%016d", le32_to_cpu(hdr->volume_serial));
166 }
167
168 if (buf_fve) {
169 const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve;
170
171 blkid_probe_sprintf_version(pr, "%d", fve->version);
172 }
173 return 0;
174 }
175
176 /* See header details:
177 * https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20(BDE)%20format.asciidoc
178 */
179 const struct blkid_idinfo bitlocker_idinfo =
180 {
181 .name = "BitLocker",
182 .usage = BLKID_USAGE_CRYPTO,
183 .probefunc = probe_bitlocker,
184 .magics =
185 {
186 { .magic = BDE_MAGIC_VISTA, .len = 11 },
187 { .magic = BDE_MAGIC_WIN7, .len = 11 },
188 { .magic = BDE_MAGIC_TOGO, .len = 11 },
189 { NULL }
190 }
191 };