Fixed the build error for riscv64 arch using gcc 13
[platform/upstream/cryptsetup.git] / lib / integrity / integrity.c
1 /*
2  * Integrity volume handling
3  *
4  * Copyright (C) 2016-2021 Milan Broz
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 <uuid/uuid.h>
26
27 #include "integrity.h"
28 #include "internal.h"
29
30 static int INTEGRITY_read_superblock(struct crypt_device *cd,
31                                      struct device *device,
32                                      uint64_t offset, struct superblock *sb)
33 {
34         int devfd, r;
35
36         devfd = device_open(cd, device, O_RDONLY);
37         if(devfd < 0)
38                 return -EINVAL;
39
40         if (read_lseek_blockwise(devfd, device_block_size(cd, device),
41                 device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) ||
42             memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) ||
43             sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) {
44                 log_std(cd, "No integrity superblock detected on %s.\n",
45                         device_path(device));
46                 r = -EINVAL;
47         } else {
48                 sb->integrity_tag_size = le16toh(sb->integrity_tag_size);
49                 sb->journal_sections = le32toh(sb->journal_sections);
50                 sb->provided_data_sectors = le64toh(sb->provided_data_sectors);
51                 sb->recalc_sector = le64toh(sb->recalc_sector);
52                 sb->flags = le32toh(sb->flags);
53                 r = 0;
54         }
55
56         return r;
57 }
58
59 int INTEGRITY_read_sb(struct crypt_device *cd,
60                       struct crypt_params_integrity *params,
61                       uint32_t *flags)
62 {
63         struct superblock sb;
64         int r;
65
66         r = INTEGRITY_read_superblock(cd, crypt_metadata_device(cd), 0, &sb);
67         if (r)
68                 return r;
69
70         params->sector_size = SECTOR_SIZE << sb.log2_sectors_per_block;
71         params->tag_size = sb.integrity_tag_size;
72
73         if (flags)
74                 *flags = sb.flags;
75
76         return 0;
77 }
78
79 int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offset)
80 {
81         struct superblock sb;
82         int r;
83
84         r = INTEGRITY_read_superblock(cd, device, offset, &sb);
85         if (r)
86                 return r;
87
88         log_std(cd, "Info for integrity device %s.\n", device_path(device));
89         log_std(cd, "superblock_version %d\n", (unsigned)sb.version);
90         log_std(cd, "log2_interleave_sectors %d\n", sb.log2_interleave_sectors);
91         log_std(cd, "integrity_tag_size %u\n", sb.integrity_tag_size);
92         log_std(cd, "journal_sections %u\n", sb.journal_sections);
93         log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors);
94         log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block);
95         if (sb.version >= SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING))
96                 log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector);
97         log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit);
98         log_std(cd, "flags %s%s%s%s%s\n",
99                 sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "",
100                 sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "",
101                 sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : "",
102                 sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "",
103                 sb.flags & SB_FLAG_FIXED_HMAC ? "fix_hmac " : "");
104
105         return 0;
106 }
107
108 int INTEGRITY_data_sectors(struct crypt_device *cd,
109                            struct device *device, uint64_t offset,
110                            uint64_t *data_sectors)
111 {
112         struct superblock sb;
113         int r;
114
115         r = INTEGRITY_read_superblock(cd, device, offset, &sb);
116         if (r)
117                 return r;
118
119         *data_sectors = sb.provided_data_sectors;
120         return 0;
121 }
122
123 int INTEGRITY_key_size(struct crypt_device *cd, const char *integrity)
124 {
125         if (!integrity)
126                 return 0;
127
128         //FIXME: use crypto backend hash size
129         if (!strcmp(integrity, "aead"))
130                 return 0;
131         else if (!strcmp(integrity, "hmac(sha1)"))
132                 return 20;
133         else if (!strcmp(integrity, "hmac(sha256)"))
134                 return 32;
135         else if (!strcmp(integrity, "hmac(sha512)"))
136                 return 64;
137         else if (!strcmp(integrity, "poly1305"))
138                 return 0;
139         else if (!strcmp(integrity, "none"))
140                 return 0;
141
142         return -EINVAL;
143 }
144
145 /* Return hash or hmac(hash) size, if known */
146 int INTEGRITY_hash_tag_size(const char *integrity)
147 {
148         char hash[MAX_CIPHER_LEN];
149         int r;
150
151         if (!integrity)
152                 return 0;
153
154         if (!strcmp(integrity, "crc32") || !strcmp(integrity, "crc32c"))
155                 return 4;
156
157         r = sscanf(integrity, "hmac(%" MAX_CIPHER_LEN_STR "[^)]s", hash);
158         if (r == 1)
159                 r = crypt_hash_size(hash);
160         else
161                 r = crypt_hash_size(integrity);
162
163         return r < 0 ? 0 : r;
164 }
165
166 int INTEGRITY_tag_size(struct crypt_device *cd,
167                        const char *integrity,
168                        const char *cipher,
169                        const char *cipher_mode)
170 {
171         int iv_tag_size = 0, auth_tag_size = 0;
172
173         if (!cipher_mode)
174                 iv_tag_size = 0;
175         else if (!strcmp(cipher_mode, "xts-random"))
176                 iv_tag_size = 16;
177         else if (!strcmp(cipher_mode, "gcm-random"))
178                 iv_tag_size = 12;
179         else if (!strcmp(cipher_mode, "ccm-random"))
180                 iv_tag_size = 8;
181         else if (!strcmp(cipher_mode, "ctr-random"))
182                 iv_tag_size = 16;
183         else if (!strcmp(cipher, "aegis256") && !strcmp(cipher_mode, "random"))
184                 iv_tag_size = 32;
185         else if (!strcmp(cipher_mode, "random"))
186                 iv_tag_size = 16;
187
188         //FIXME: use crypto backend hash size
189         if (!integrity || !strcmp(integrity, "none"))
190                 auth_tag_size = 0;
191         else if (!strcmp(integrity, "aead"))
192                 auth_tag_size = 16; //FIXME gcm- mode only
193         else if (!strcmp(integrity, "cmac(aes)"))
194                 auth_tag_size = 16;
195         else if (!strcmp(integrity, "hmac(sha1)"))
196                 auth_tag_size = 20;
197         else if (!strcmp(integrity, "hmac(sha256)"))
198                 auth_tag_size = 32;
199         else if (!strcmp(integrity, "hmac(sha512)"))
200                 auth_tag_size = 64;
201         else if (!strcmp(integrity, "poly1305")) {
202                 if (iv_tag_size)
203                         iv_tag_size = 12;
204                 auth_tag_size = 16;
205         }
206
207         return iv_tag_size + auth_tag_size;
208 }
209
210 int INTEGRITY_create_dmd_device(struct crypt_device *cd,
211                        const struct crypt_params_integrity *params,
212                        struct volume_key *vk,
213                        struct volume_key *journal_crypt_key,
214                        struct volume_key *journal_mac_key,
215                        struct crypt_dm_active_device *dmd,
216                        uint32_t flags, uint32_t sb_flags)
217 {
218         int r;
219
220         if (!dmd)
221                 return -EINVAL;
222
223         *dmd = (struct crypt_dm_active_device) {
224                 .flags = flags,
225         };
226
227         /* Workaround for kernel dm-integrity table bug */
228         if (sb_flags & SB_FLAG_RECALCULATING)
229                 dmd->flags |= CRYPT_ACTIVATE_RECALCULATE;
230
231         r = INTEGRITY_data_sectors(cd, crypt_metadata_device(cd),
232                                    crypt_get_data_offset(cd) * SECTOR_SIZE, &dmd->size);
233         if (r < 0)
234                 return r;
235
236         return dm_integrity_target_set(cd, &dmd->segment, 0, dmd->size,
237                         crypt_metadata_device(cd), crypt_data_device(cd),
238                         crypt_get_integrity_tag_size(cd), crypt_get_data_offset(cd),
239                         crypt_get_sector_size(cd), vk, journal_crypt_key,
240                         journal_mac_key, params);
241 }
242
243 int INTEGRITY_activate_dmd_device(struct crypt_device *cd,
244                        const char *name,
245                        const char *type,
246                        struct crypt_dm_active_device *dmd,
247                        uint32_t sb_flags)
248 {
249         int r;
250         uint32_t dmi_flags;
251         struct dm_target *tgt = &dmd->segment;
252
253         if (!single_segment(dmd) || tgt->type != DM_INTEGRITY)
254                 return -EINVAL;
255
256         log_dbg(cd, "Trying to activate INTEGRITY device on top of %s, using name %s, tag size %d, provided sectors %" PRIu64".",
257                 device_path(tgt->data_device), name, tgt->u.integrity.tag_size, dmd->size);
258
259         r = device_block_adjust(cd, tgt->data_device, DEV_EXCL,
260                                 tgt->u.integrity.offset, NULL, &dmd->flags);
261         if (r)
262                 return r;
263
264         if (tgt->u.integrity.meta_device) {
265                 r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
266                 if (r)
267                         return r;
268         }
269
270         r = dm_create_device(cd, name, type, dmd);
271         if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
272                 log_err(cd, _("Kernel does not support dm-integrity mapping."));
273                 return -ENOTSUP;
274         }
275
276         if (r < 0 && (sb_flags & SB_FLAG_FIXED_PADDING) && !dm_flags(cd, DM_INTEGRITY, &dmi_flags) &&
277             !(dmi_flags & DM_INTEGRITY_FIX_PADDING_SUPPORTED)) {
278                 log_err(cd, _("Kernel does not support dm-integrity fixed metadata alignment."));
279                 return -ENOTSUP;
280         }
281
282         if (r < 0 && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) &&
283             !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC) &&
284             ((sb_flags & SB_FLAG_FIXED_HMAC) ?
285             (tgt->u.integrity.vk && !tgt->u.integrity.journal_integrity_key) :
286             (tgt->u.integrity.vk || tgt->u.integrity.journal_integrity_key))) {
287                 log_err(cd, _("Kernel refuses to activate insecure recalculate option (see legacy activation options to override)."));
288                 return -ENOTSUP;
289         }
290
291         return r;
292 }
293
294 int INTEGRITY_activate(struct crypt_device *cd,
295                        const char *name,
296                        const struct crypt_params_integrity *params,
297                        struct volume_key *vk,
298                        struct volume_key *journal_crypt_key,
299                        struct volume_key *journal_mac_key,
300                        uint32_t flags, uint32_t sb_flags)
301 {
302         struct crypt_dm_active_device dmd = {};
303         int r = INTEGRITY_create_dmd_device(cd, params, vk, journal_crypt_key,
304                                             journal_mac_key, &dmd, flags, sb_flags);
305
306         if (r < 0)
307                 return r;
308
309         r = INTEGRITY_activate_dmd_device(cd, name, CRYPT_INTEGRITY, &dmd, sb_flags);
310         dm_targets_free(cd, &dmd);
311         return r;
312 }
313
314 int INTEGRITY_format(struct crypt_device *cd,
315                      const struct crypt_params_integrity *params,
316                      struct volume_key *journal_crypt_key,
317                      struct volume_key *journal_mac_key)
318 {
319         uint32_t dmi_flags;
320         char tmp_name[64], tmp_uuid[40];
321         struct crypt_dm_active_device dmdi = {
322                 .size = 8,
323                 .flags = CRYPT_ACTIVATE_PRIVATE, /* We always create journal but it can be unused later */
324         };
325         struct dm_target *tgt = &dmdi.segment;
326         int r;
327         uuid_t tmp_uuid_bin;
328         struct volume_key *vk = NULL;
329
330         uuid_generate(tmp_uuid_bin);
331         uuid_unparse(tmp_uuid_bin, tmp_uuid);
332
333         r = snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid);
334         if (r < 0 || (size_t)r >= sizeof(tmp_name))
335                 return -EINVAL;
336
337         /* There is no data area, we can actually use fake zeroed key */
338         if (params && params->integrity_key_size)
339                 vk = crypt_alloc_volume_key(params->integrity_key_size, NULL);
340
341         r = dm_integrity_target_set(cd, tgt, 0, dmdi.size, crypt_metadata_device(cd),
342                         crypt_data_device(cd), crypt_get_integrity_tag_size(cd),
343                         crypt_get_data_offset(cd), crypt_get_sector_size(cd), vk,
344                         journal_crypt_key, journal_mac_key, params);
345         if (r < 0) {
346                 crypt_free_volume_key(vk);
347                 return r;
348         }
349
350         log_dbg(cd, "Trying to format INTEGRITY device on top of %s, tmp name %s, tag size %d.",
351                 device_path(tgt->data_device), tmp_name, tgt->u.integrity.tag_size);
352
353         r = device_block_adjust(cd, tgt->data_device, DEV_EXCL, tgt->u.integrity.offset, NULL, NULL);
354         if (r < 0 && (dm_flags(cd, DM_INTEGRITY, &dmi_flags) || !(dmi_flags & DM_INTEGRITY_SUPPORTED))) {
355                 log_err(cd, _("Kernel does not support dm-integrity mapping."));
356                 r = -ENOTSUP;
357         }
358         if (r) {
359                 dm_targets_free(cd, &dmdi);
360                 return r;
361         }
362
363         if (tgt->u.integrity.meta_device) {
364                 r = device_block_adjust(cd, tgt->u.integrity.meta_device, DEV_EXCL, 0, NULL, NULL);
365                 if (r) {
366                         dm_targets_free(cd, &dmdi);
367                         return r;
368                 }
369         }
370
371         r = dm_create_device(cd, tmp_name, CRYPT_INTEGRITY, &dmdi);
372         crypt_free_volume_key(vk);
373         dm_targets_free(cd, &dmdi);
374         if (r)
375                 return r;
376
377         return dm_remove_device(cd, tmp_name, CRYPT_DEACTIVATE_FORCE);
378 }