/src/util-linux/libblkid/src/superblocks/luks.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
3 | | * Copyright (C) 2018-2024 Milan Broz <gmazyland@gmail.com> |
4 | | * |
5 | | * Inspired by libvolume_id by |
6 | | * Kay Sievers <kay.sievers@vrfy.org> |
7 | | * |
8 | | * This file may be redistributed under the terms of the |
9 | | * GNU Lesser General Public License. |
10 | | */ |
11 | | #include <stdio.h> |
12 | | #include <stdlib.h> |
13 | | #include <unistd.h> |
14 | | #include <string.h> |
15 | | #include <errno.h> |
16 | | #include <ctype.h> |
17 | | #include <stdint.h> |
18 | | #include <stdbool.h> |
19 | | |
20 | | #include "superblocks.h" |
21 | | |
22 | | #define LUKS_CIPHERNAME_L 32 |
23 | | #define LUKS_CIPHERMODE_L 32 |
24 | | #define LUKS_HASHSPEC_L 32 |
25 | | #define LUKS_DIGESTSIZE 20 |
26 | | #define LUKS_SALTSIZE 32 |
27 | 0 | #define LUKS_MAGIC_L 6 |
28 | 0 | #define UUID_STRING_L 40 |
29 | 0 | #define LUKS2_LABEL_L 48 |
30 | | #define LUKS2_SALT_L 64 |
31 | | #define LUKS2_CHECKSUM_ALG_L 32 |
32 | | #define LUKS2_CHECKSUM_L 64 |
33 | | |
34 | 0 | #define LUKS_MAGIC "LUKS\xba\xbe" |
35 | 0 | #define LUKS_MAGIC_2 "SKUL\xba\xbe" |
36 | | |
37 | 0 | #define LUKS2_HW_OPAL_SUBSYSTEM "HW-OPAL" |
38 | | |
39 | | /* Offsets for secondary header (for scan if primary header is corrupted). */ |
40 | | #define LUKS2_HDR2_OFFSETS { 0x04000, 0x008000, 0x010000, 0x020000, \ |
41 | | 0x40000, 0x080000, 0x100000, 0x200000, 0x400000 } |
42 | | |
43 | | static const uint64_t secondary_offsets[] = LUKS2_HDR2_OFFSETS; |
44 | | |
45 | | struct luks_phdr { |
46 | | uint8_t magic[LUKS_MAGIC_L]; |
47 | | uint16_t version; |
48 | | uint8_t cipherName[LUKS_CIPHERNAME_L]; |
49 | | uint8_t cipherMode[LUKS_CIPHERMODE_L]; |
50 | | uint8_t hashSpec[LUKS_HASHSPEC_L]; |
51 | | uint32_t payloadOffset; |
52 | | uint32_t keyBytes; |
53 | | uint8_t mkDigest[LUKS_DIGESTSIZE]; |
54 | | uint8_t mkDigestSalt[LUKS_SALTSIZE]; |
55 | | uint32_t mkDigestIterations; |
56 | | uint8_t uuid[UUID_STRING_L]; |
57 | | } __attribute__((packed)); |
58 | | |
59 | | struct luks2_phdr { |
60 | | char magic[LUKS_MAGIC_L]; |
61 | | uint16_t version; |
62 | | uint64_t hdr_size; /* in bytes, including JSON area */ |
63 | | uint64_t seqid; /* increased on every update */ |
64 | | char label[LUKS2_LABEL_L]; |
65 | | char checksum_alg[LUKS2_CHECKSUM_ALG_L]; |
66 | | uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */ |
67 | | char uuid[UUID_STRING_L]; |
68 | | char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */ |
69 | | uint64_t hdr_offset; /* offset from device start in bytes */ |
70 | | char _padding[184]; |
71 | | uint8_t csum[LUKS2_CHECKSUM_L]; |
72 | | /* Padding to 4k, then JSON area */ |
73 | | } __attribute__ ((packed)); |
74 | | |
75 | | static int luks_attributes(blkid_probe pr, struct luks2_phdr *header, uint64_t offset) |
76 | 0 | { |
77 | 0 | int version; |
78 | 0 | struct luks_phdr *header_v1; |
79 | |
|
80 | 0 | if (blkid_probe_set_magic(pr, offset, LUKS_MAGIC_L, (unsigned char *) &header->magic)) |
81 | 0 | return BLKID_PROBE_NONE; |
82 | | |
83 | 0 | version = be16_to_cpu(header->version); |
84 | 0 | blkid_probe_sprintf_version(pr, "%u", version); |
85 | |
|
86 | 0 | if (version == 1) { |
87 | 0 | header_v1 = (struct luks_phdr *)header; |
88 | 0 | blkid_probe_strncpy_uuid(pr, |
89 | 0 | (unsigned char *) header_v1->uuid, UUID_STRING_L); |
90 | 0 | } else if (version == 2) { |
91 | 0 | blkid_probe_strncpy_uuid(pr, |
92 | 0 | (unsigned char *) header->uuid, UUID_STRING_L); |
93 | 0 | blkid_probe_set_label(pr, |
94 | 0 | (unsigned char *) header->label, LUKS2_LABEL_L); |
95 | 0 | blkid_probe_set_id_label(pr, "SUBSYSTEM", |
96 | 0 | (unsigned char *) header->subsystem, LUKS2_LABEL_L); |
97 | 0 | } |
98 | |
|
99 | 0 | return BLKID_PROBE_OK; |
100 | 0 | } |
101 | | |
102 | | static bool luks_valid(struct luks2_phdr *header, const char *magic, uint64_t offset) |
103 | 0 | { |
104 | 0 | if (memcmp(header->magic, magic, LUKS_MAGIC_L)) |
105 | 0 | return false; |
106 | | |
107 | | /* LUKS2 header is not at expected offset */ |
108 | 0 | if (be16_to_cpu(header->version) == 2 && |
109 | 0 | be64_to_cpu(header->hdr_offset) != offset) |
110 | 0 | return false; |
111 | | |
112 | 0 | return true; |
113 | 0 | } |
114 | | |
115 | | static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) |
116 | 0 | { |
117 | 0 | struct luks2_phdr *header; |
118 | 0 | size_t i; |
119 | |
|
120 | 0 | header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, 0, sizeof(struct luks2_phdr)); |
121 | 0 | if (!header) |
122 | 0 | return errno ? -errno : BLKID_PROBE_NONE; |
123 | | |
124 | 0 | if (luks_valid(header, LUKS_MAGIC, 0)) { |
125 | | /* LUKS primary header was found. */ |
126 | 0 | return luks_attributes(pr, header, 0); |
127 | 0 | } |
128 | | |
129 | | /* No primary header, scan for known offsets of LUKS2 secondary header. */ |
130 | 0 | for (i = 0; i < ARRAY_SIZE(secondary_offsets); i++) { |
131 | 0 | header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, |
132 | 0 | secondary_offsets[i], sizeof(struct luks2_phdr)); |
133 | |
|
134 | 0 | if (!header) |
135 | 0 | return errno ? -errno : BLKID_PROBE_NONE; |
136 | | |
137 | 0 | if (luks_valid(header, LUKS_MAGIC_2, secondary_offsets[i])) |
138 | 0 | return luks_attributes(pr, header, secondary_offsets[i]); |
139 | 0 | } |
140 | | |
141 | 0 | return BLKID_PROBE_NONE; |
142 | 0 | } |
143 | | |
144 | | static int probe_luks_opal(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__))) |
145 | 0 | { |
146 | 0 | struct luks2_phdr *header; |
147 | 0 | int version; |
148 | |
|
149 | 0 | header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, 0, sizeof(struct luks2_phdr)); |
150 | 0 | if (!header) |
151 | 0 | return errno ? -errno : BLKID_PROBE_NONE; |
152 | | |
153 | 0 | if (!luks_valid(header, LUKS_MAGIC, 0)) |
154 | 0 | return BLKID_PROBE_NONE; |
155 | | |
156 | 0 | version = be16_to_cpu(header->version); |
157 | |
|
158 | 0 | if (version != 2) |
159 | 0 | return BLKID_PROBE_NONE; |
160 | | |
161 | 0 | if (memcmp(header->subsystem, LUKS2_HW_OPAL_SUBSYSTEM, sizeof(LUKS2_HW_OPAL_SUBSYSTEM)) != 0) |
162 | 0 | return BLKID_PROBE_NONE; |
163 | | |
164 | 0 | if (!blkdid_probe_is_opal_locked(pr)) |
165 | 0 | return BLKID_PROBE_NONE; |
166 | | |
167 | | /* Locked drive with LUKS2 HW OPAL encryption, finish probe now */ |
168 | 0 | return luks_attributes(pr, header, 0); |
169 | 0 | } |
170 | | |
171 | | const struct blkid_idinfo luks_idinfo = |
172 | | { |
173 | | .name = "crypto_LUKS", |
174 | | .usage = BLKID_USAGE_CRYPTO, |
175 | | .probefunc = probe_luks, |
176 | | .magics = BLKID_NONE_MAGIC |
177 | | }; |
178 | | |
179 | | const struct blkid_idinfo luks_opal_idinfo = |
180 | | { |
181 | | .name = "crypto_LUKS", |
182 | | .usage = BLKID_USAGE_CRYPTO, |
183 | | .probefunc = probe_luks_opal, |
184 | | .magics = BLKID_NONE_MAGIC, |
185 | | }; |