summaryrefslogtreecommitdiffstats
path: root/libblkid/src/superblocks/bitlocker.c
blob: a9a7bfb8f52f481da3326607377ce4c37be95d28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
 * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
 *
 * This file may be redistributed under the terms of the
 * GNU Lesser General Public License.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdint.h>

#include "superblocks.h"

#define BDE_HDR_SIZE	512
#define BDE_HDR_OFFSET	0

struct bde_header_win7 {
/*   0 */ unsigned char	boot_entry_point[3];
/*   3 */ unsigned char	fs_signature[8];
/*  11 */ unsigned char	__dummy1[67 - 11];
/*  67 */ uint32_t      volume_serial;		/* NTFS uses 64bit serial number */
/*  71 */ unsigned char volume_label[11];	/* "NO NAME\x20\x20\x20\x20" only */
/*  82 */ unsigned char __dummy2[160 - 82];
/* 160 */ unsigned char guid[16];		/* BitLocker specific GUID */
/* 176 */ uint64_t      fve_metadata_offset;
} __attribute__((packed));


struct bde_header_togo {
/*   0 */ unsigned char	boot_entry_point[3];
/*   3 */ unsigned char	fs_signature[8];
/*  11 */ unsigned char	__dummy[424 - 11];
/* 424 */ unsigned char guid[16];
/* 440 */ uint64_t      fve_metadata_offset;
} __attribute__((packed));


struct bde_fve_metadata {
/*   0 */ unsigned char  signature[8];
/*   8 */ uint16_t       size;
/*  10 */ uint16_t       version;
};

enum {
	BDE_VERSION_VISTA = 0,
	BDE_VERSION_WIN7,
	BDE_VERSION_TOGO
};

#define BDE_MAGIC_VISTA		"\xeb\x52\x90-FVE-FS-"
#define BDE_MAGIC_WIN7		"\xeb\x58\x90-FVE-FS-"
#define BDE_MAGIC_TOGO		"\xeb\x58\x90MSWIN4.1"

#define BDE_MAGIC_FVE		"-FVE-FS-"

static int get_bitlocker_type(const unsigned char *buf)
{
	size_t i;
	static const char *map[] = {
		[BDE_VERSION_VISTA] = BDE_MAGIC_VISTA,
		[BDE_VERSION_WIN7]  = BDE_MAGIC_WIN7,
		[BDE_VERSION_TOGO]  = BDE_MAGIC_TOGO
	};

	for (i = 0; i < ARRAY_SIZE(map); i++) {
		if (memcmp(buf, map[i], 11) == 0)
			return (int) i;
	}

	return -1;
}

/* Returns: < 0 error, 1 nothing, 0 success
 */
static int get_bitlocker_headers(blkid_probe pr,
				int *type,
				const unsigned char **buf_hdr,
				const unsigned char **buf_fve)
{

	const unsigned char *buf;
	const struct bde_fve_metadata *fve;
	uint64_t off = 0;
	int kind;

	if (buf_hdr)
		*buf_hdr = NULL;
	if (buf_fve)
		*buf_fve = NULL;
	if (type)
		*type = -1;

	buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE);
	if (!buf)
		return errno ? -errno : 1;

	kind = get_bitlocker_type(buf);

	/* Check BitLocker header */
	switch (kind) {
	case BDE_VERSION_WIN7:
		off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset);
		break;
	case BDE_VERSION_TOGO:
		off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset);
		break;
	case BDE_VERSION_VISTA:
		goto done;
	default:
		goto nothing;
	}

	if (!off || off % 64)
		goto nothing;
	if (buf_hdr)
		*buf_hdr = buf;

	/* Check Bitlocker FVE metadata header */
	buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata));
	if (!buf)
		return errno ? -errno : 1;

	fve = (const struct bde_fve_metadata *) buf;
	if (memcmp(fve->signature, BDE_MAGIC_FVE, sizeof(fve->signature)) != 0)
		goto nothing;
	if (buf_fve)
		*buf_fve = buf;
done:
	if (type)
		*type = kind;
	return 0;
nothing:
	return 1;
}

/*
 * This is used by vFAT and NTFS prober to avoid collisions with bitlocker.
 */
int blkid_probe_is_bitlocker(blkid_probe pr)
{
	return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0;
}

static int probe_bitlocker(blkid_probe pr,
		const struct blkid_idmag *mag __attribute__((__unused__)))
{
	const unsigned char *buf_fve = NULL;
	const unsigned char *buf_hdr = NULL;
	int rc, kind;

	rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve);
	if (rc)
		return rc;

	if (kind == BDE_VERSION_WIN7) {
		const struct bde_header_win7 *hdr = (const struct bde_header_win7 *) buf_hdr;

		/* Unfortunately, it seems volume_serial is always zero */
		blkid_probe_sprintf_uuid(pr,
				(const unsigned char *) &hdr->volume_serial,
				sizeof(hdr->volume_serial),
				"%016d", le32_to_cpu(hdr->volume_serial));
	}

	if (buf_fve) {
		const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve;

		blkid_probe_sprintf_version(pr, "%d", fve->version);
	}
	return 0;
}

/* See header details:
 * https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20(BDE)%20format.asciidoc
 */
const struct blkid_idinfo bitlocker_idinfo =
{
	.name		= "BitLocker",
	.usage		= BLKID_USAGE_CRYPTO,
	.probefunc	= probe_bitlocker,
	.magics		=
	{
		{ .magic = BDE_MAGIC_VISTA, .len = 11 },
		{ .magic = BDE_MAGIC_WIN7,  .len = 11 },
		{ .magic = BDE_MAGIC_TOGO,  .len = 11 },
		{ NULL }
	}
};