"Fossies" - the Fresh Open Source Software Archive 
Member "cryptsetup-2.4.3/lib/verity/verity.c" (13 Jan 2022, 10405 Bytes) of package /linux/misc/cryptsetup-2.4.3.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "verity.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
2.3.6_vs_2.4.0.
1 /*
2 * dm-verity volume handling
3 *
4 * Copyright (C) 2012-2021 Red Hat, Inc. All rights reserved.
5 *
6 * This file is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This file is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this file; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdint.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <uuid/uuid.h>
30
31 #include "libcryptsetup.h"
32 #include "verity.h"
33 #include "internal.h"
34
35 #define VERITY_SIGNATURE "verity\0\0"
36
37 /* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity#verity-superblock-format */
38 struct verity_sb {
39 uint8_t signature[8]; /* "verity\0\0" */
40 uint32_t version; /* superblock version */
41 uint32_t hash_type; /* 0 - Chrome OS, 1 - normal */
42 uint8_t uuid[16]; /* UUID of hash device */
43 uint8_t algorithm[32];/* hash algorithm name */
44 uint32_t data_block_size; /* data block in bytes */
45 uint32_t hash_block_size; /* hash block in bytes */
46 uint64_t data_blocks; /* number of data blocks */
47 uint16_t salt_size; /* salt size */
48 uint8_t _pad1[6];
49 uint8_t salt[256]; /* salt */
50 uint8_t _pad2[168];
51 } __attribute__((packed));
52
53 /* Read verity superblock from disk */
54 int VERITY_read_sb(struct crypt_device *cd,
55 uint64_t sb_offset,
56 char **uuid_string,
57 struct crypt_params_verity *params)
58 {
59 struct device *device = crypt_metadata_device(cd);
60 struct verity_sb sb = {};
61 ssize_t hdr_size = sizeof(struct verity_sb);
62 int devfd, sb_version;
63
64 log_dbg(cd, "Reading VERITY header of size %zu on device %s, offset %" PRIu64 ".",
65 sizeof(struct verity_sb), device_path(device), sb_offset);
66
67 if (params->flags & CRYPT_VERITY_NO_HEADER) {
68 log_err(cd, _("Verity device %s does not use on-disk header."),
69 device_path(device));
70 return -EINVAL;
71 }
72
73 if (MISALIGNED_512(sb_offset)) {
74 log_err(cd, _("Unsupported VERITY hash offset."));
75 return -EINVAL;
76 }
77
78 devfd = device_open(cd, device, O_RDONLY);
79 if (devfd < 0) {
80 log_err(cd, _("Cannot open device %s."), device_path(device));
81 return -EINVAL;
82 }
83
84 if (read_lseek_blockwise(devfd, device_block_size(cd, device),
85 device_alignment(device), &sb, hdr_size,
86 sb_offset) < hdr_size)
87 return -EIO;
88
89 if (memcmp(sb.signature, VERITY_SIGNATURE, sizeof(sb.signature))) {
90 log_err(cd, _("Device %s is not a valid VERITY device."),
91 device_path(device));
92 return -EINVAL;
93 }
94
95 sb_version = le32_to_cpu(sb.version);
96 if (sb_version != 1) {
97 log_err(cd, _("Unsupported VERITY version %d."), sb_version);
98 return -EINVAL;
99 }
100 params->hash_type = le32_to_cpu(sb.hash_type);
101 if (params->hash_type > VERITY_MAX_HASH_TYPE) {
102 log_err(cd, _("Unsupported VERITY hash type %d."), params->hash_type);
103 return -EINVAL;
104 }
105
106 params->data_block_size = le32_to_cpu(sb.data_block_size);
107 params->hash_block_size = le32_to_cpu(sb.hash_block_size);
108 if (VERITY_BLOCK_SIZE_OK(params->data_block_size) ||
109 VERITY_BLOCK_SIZE_OK(params->hash_block_size)) {
110 log_err(cd, _("Unsupported VERITY block size."));
111 return -EINVAL;
112 }
113 params->data_size = le64_to_cpu(sb.data_blocks);
114
115 params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm));
116 if (!params->hash_name)
117 return -ENOMEM;
118 if (crypt_hash_size(params->hash_name) <= 0) {
119 log_err(cd, _("Hash algorithm %s not supported."),
120 params->hash_name);
121 free(CONST_CAST(char*)params->hash_name);
122 params->hash_name = NULL;
123 return -EINVAL;
124 }
125
126 params->salt_size = le16_to_cpu(sb.salt_size);
127 if (params->salt_size > sizeof(sb.salt)) {
128 log_err(cd, _("VERITY header corrupted."));
129 free(CONST_CAST(char*)params->hash_name);
130 params->hash_name = NULL;
131 return -EINVAL;
132 }
133 params->salt = malloc(params->salt_size);
134 if (!params->salt) {
135 free(CONST_CAST(char*)params->hash_name);
136 params->hash_name = NULL;
137 return -ENOMEM;
138 }
139 memcpy(CONST_CAST(char*)params->salt, sb.salt, params->salt_size);
140
141 if ((*uuid_string = malloc(40)))
142 uuid_unparse(sb.uuid, *uuid_string);
143
144 params->hash_area_offset = sb_offset;
145 return 0;
146 }
147
148 static void _to_lower(char *str)
149 {
150 for(; *str; str++)
151 if (isupper(*str))
152 *str = tolower(*str);
153 }
154
155 /* Write verity superblock to disk */
156 int VERITY_write_sb(struct crypt_device *cd,
157 uint64_t sb_offset,
158 const char *uuid_string,
159 struct crypt_params_verity *params)
160 {
161 struct device *device = crypt_metadata_device(cd);
162 struct verity_sb sb = {};
163 ssize_t hdr_size = sizeof(struct verity_sb);
164 size_t block_size;
165 char *algorithm;
166 uuid_t uuid;
167 int r, devfd;
168
169 log_dbg(cd, "Updating VERITY header of size %zu on device %s, offset %" PRIu64 ".",
170 sizeof(struct verity_sb), device_path(device), sb_offset);
171
172 if (!uuid_string || uuid_parse(uuid_string, uuid) == -1) {
173 log_err(cd, _("Wrong VERITY UUID format provided on device %s."),
174 device_path(device));
175 return -EINVAL;
176 }
177
178 if (params->flags & CRYPT_VERITY_NO_HEADER) {
179 log_err(cd, _("Verity device %s does not use on-disk header."),
180 device_path(device));
181 return -EINVAL;
182 }
183
184 /* Avoid possible increasing of image size - FEC could fail later because of it */
185 block_size = device_block_size(cd, device);
186 if (block_size > params->hash_block_size) {
187 device_disable_direct_io(device);
188 block_size = params->hash_block_size;
189 }
190
191 devfd = device_open(cd, device, O_RDWR);
192 if (devfd < 0) {
193 log_err(cd, _("Cannot open device %s."), device_path(device));
194 return -EINVAL;
195 }
196
197 memcpy(&sb.signature, VERITY_SIGNATURE, sizeof(sb.signature));
198 sb.version = cpu_to_le32(1);
199 sb.hash_type = cpu_to_le32(params->hash_type);
200 sb.data_block_size = cpu_to_le32(params->data_block_size);
201 sb.hash_block_size = cpu_to_le32(params->hash_block_size);
202 sb.salt_size = cpu_to_le16(params->salt_size);
203 sb.data_blocks = cpu_to_le64(params->data_size);
204
205 /* Kernel always use lower-case */
206 algorithm = (char *)sb.algorithm;
207 strncpy(algorithm, params->hash_name, sizeof(sb.algorithm)-1);
208 algorithm[sizeof(sb.algorithm)-1] = '\0';
209 _to_lower(algorithm);
210
211 memcpy(sb.salt, params->salt, params->salt_size);
212 memcpy(sb.uuid, uuid, sizeof(sb.uuid));
213
214 r = write_lseek_blockwise(devfd, block_size, device_alignment(device),
215 (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0;
216 if (r)
217 log_err(cd, _("Error during update of verity header on device %s."),
218 device_path(device));
219
220 device_sync(cd, device);
221
222 return r;
223 }
224
225 /* Calculate hash offset in hash blocks */
226 uint64_t VERITY_hash_offset_block(struct crypt_params_verity *params)
227 {
228 uint64_t hash_offset = params->hash_area_offset;
229
230 if (params->flags & CRYPT_VERITY_NO_HEADER)
231 return hash_offset / params->hash_block_size;
232
233 hash_offset += sizeof(struct verity_sb);
234 hash_offset += params->hash_block_size - 1;
235
236 return hash_offset / params->hash_block_size;
237 }
238
239 int VERITY_UUID_generate(struct crypt_device *cd __attribute__((unused)), char **uuid_string)
240 {
241 uuid_t uuid;
242
243 *uuid_string = malloc(40);
244 if (!*uuid_string)
245 return -ENOMEM;
246 uuid_generate(uuid);
247 uuid_unparse(uuid, *uuid_string);
248 return 0;
249 }
250
251 /* Activate verity device in kernel device-mapper */
252 int VERITY_activate(struct crypt_device *cd,
253 const char *name,
254 const char *root_hash,
255 size_t root_hash_size,
256 const char *signature_description,
257 struct device *fec_device,
258 struct crypt_params_verity *verity_hdr,
259 uint32_t activation_flags)
260 {
261 uint32_t dmv_flags;
262 unsigned int fec_errors = 0;
263 int r, v;
264 struct crypt_dm_active_device dmd = {
265 .size = verity_hdr->data_size * verity_hdr->data_block_size / 512,
266 .flags = activation_flags,
267 .uuid = crypt_get_uuid(cd),
268 };
269
270 log_dbg(cd, "Trying to activate VERITY device %s using hash %s.",
271 name ?: "[none]", verity_hdr->hash_name);
272
273 if (verity_hdr->flags & CRYPT_VERITY_CHECK_HASH) {
274 if (signature_description) {
275 log_err(cd, _("Root hash signature verification is not supported."));
276 return -EINVAL;
277 }
278
279 log_dbg(cd, "Verification of data in userspace required.");
280 r = VERITY_verify(cd, verity_hdr, root_hash, root_hash_size);
281
282 if ((r == -EPERM || r == -EFAULT) && fec_device) {
283 v = r;
284 log_dbg(cd, "Verification failed, trying to repair with FEC device.");
285 r = VERITY_FEC_process(cd, verity_hdr, fec_device, 1, &fec_errors);
286 if (r < 0)
287 log_err(cd, _("Errors cannot be repaired with FEC device."));
288 else if (fec_errors) {
289 log_err(cd, _("Found %u repairable errors with FEC device."),
290 fec_errors);
291 /* If root hash failed, we cannot be sure it was properly repaired */
292 }
293 if (v == -EFAULT)
294 r = -EPERM;
295 }
296
297 if (r < 0)
298 return r;
299 }
300
301 if (!name)
302 return 0;
303
304 r = device_block_adjust(cd, crypt_metadata_device(cd), DEV_OK,
305 0, NULL, NULL);
306 if (r)
307 return r;
308
309 r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL,
310 0, &dmd.size, &dmd.flags);
311 if (r)
312 return r;
313
314 if (fec_device) {
315 r = device_block_adjust(cd, fec_device, DEV_OK,
316 0, NULL, NULL);
317 if (r)
318 return r;
319 }
320
321 r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
322 crypt_metadata_device(cd), fec_device, root_hash,
323 root_hash_size, signature_description,
324 VERITY_hash_offset_block(verity_hdr),
325 VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr);
326
327 if (r)
328 return r;
329
330 r = dm_create_device(cd, name, CRYPT_VERITY, &dmd);
331 if (r < 0 && (dm_flags(cd, DM_VERITY, &dmv_flags) || !(dmv_flags & DM_VERITY_SUPPORTED))) {
332 log_err(cd, _("Kernel does not support dm-verity mapping."));
333 r = -ENOTSUP;
334 }
335 if (r < 0 && signature_description && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
336 log_err(cd, _("Kernel does not support dm-verity signature option."));
337 r = -ENOTSUP;
338 }
339 if (r < 0)
340 goto out;
341
342 r = dm_status_verity_ok(cd, name);
343 if (r < 0)
344 goto out;
345
346 if (!r)
347 log_err(cd, _("Verity device detected corruption after activation."));
348
349 r = 0;
350 out:
351 dm_targets_free(cd, &dmd);
352 return r;
353 }