b939467f67494dcfd5f87b4795fd88dcbd3b455b
[platform/upstream/cryptsetup.git] / lib / luks2 / luks2_keyslot_reenc.c
1 /*
2  * LUKS - Linux Unified Key Setup v2, reencryption keyslot handler
3  *
4  * Copyright (C) 2016-2020, Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2016-2020, Ondrej Kozina
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "luks2_internal.h"
23
24 static int reenc_keyslot_open(struct crypt_device *cd,
25         int keyslot,
26         const char *password,
27         size_t password_len,
28         char *volume_key,
29         size_t volume_key_len)
30 {
31         return -ENOENT;
32 }
33
34 int reenc_keyslot_alloc(struct crypt_device *cd,
35         struct luks2_hdr *hdr,
36         int keyslot,
37         const struct crypt_params_reencrypt *params)
38 {
39         int r;
40         json_object *jobj_keyslots, *jobj_keyslot, *jobj_area;
41         uint64_t area_offset, area_length;
42
43         log_dbg(cd, "Allocating reencrypt keyslot %d.", keyslot);
44
45         if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX)
46                 return -ENOMEM;
47
48         if (!json_object_object_get_ex(hdr->jobj, "keyslots", &jobj_keyslots))
49                 return -EINVAL;
50
51         /* encryption doesn't require area (we shift data and backup will be available) */
52         if (!params->data_shift) {
53                 r = LUKS2_find_area_max_gap(cd, hdr, &area_offset, &area_length);
54                 if (r < 0)
55                         return r;
56         } else { /* we can't have keyslot w/o area...bug? */
57                 r = LUKS2_find_area_gap(cd, hdr, 1, &area_offset, &area_length);
58                 if (r < 0)
59                         return r;
60         }
61
62         jobj_keyslot = json_object_new_object();
63         if (!jobj_keyslot)
64                 return -ENOMEM;
65
66         jobj_area = json_object_new_object();
67
68         if (params->data_shift) {
69                 json_object_object_add(jobj_area, "type", json_object_new_string("datashift"));
70                 json_object_object_add(jobj_area, "shift_size", crypt_jobj_new_uint64(params->data_shift << SECTOR_SHIFT));
71         } else
72                 /* except data shift protection, initial setting is irrelevant. Type can be changed during reencryption */
73                 json_object_object_add(jobj_area, "type", json_object_new_string("none"));
74
75         json_object_object_add(jobj_area, "offset", crypt_jobj_new_uint64(area_offset));
76         json_object_object_add(jobj_area, "size", crypt_jobj_new_uint64(area_length));
77
78         json_object_object_add(jobj_keyslot, "type", json_object_new_string("reencrypt"));
79         json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(1)); /* useless but mandatory */
80         json_object_object_add(jobj_keyslot, "mode", json_object_new_string(crypt_reencrypt_mode_to_str(params->mode)));
81         if (params->direction == CRYPT_REENCRYPT_FORWARD)
82                 json_object_object_add(jobj_keyslot, "direction", json_object_new_string("forward"));
83         else if (params->direction == CRYPT_REENCRYPT_BACKWARD)
84                 json_object_object_add(jobj_keyslot, "direction", json_object_new_string("backward"));
85         else
86                 return -EINVAL;
87
88         json_object_object_add(jobj_keyslot, "area", jobj_area);
89
90         json_object_object_add_by_uint(jobj_keyslots, keyslot, jobj_keyslot);
91         if (LUKS2_check_json_size(cd, hdr)) {
92                 log_dbg(cd, "New keyslot too large to fit in free metadata space.");
93                 json_object_object_del_by_uint(jobj_keyslots, keyslot);
94                 return -ENOSPC;
95         }
96
97         JSON_DBG(cd, hdr->jobj, "JSON:");
98
99         return 0;
100 }
101
102 static int reenc_keyslot_store_data(struct crypt_device *cd,
103         json_object *jobj_keyslot,
104         const void *buffer, size_t buffer_len)
105 {
106         int devfd, r;
107         json_object *jobj_area, *jobj_offset, *jobj_length;
108         uint64_t area_offset, area_length;
109         struct device *device = crypt_metadata_device(cd);
110
111         if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
112             !json_object_object_get_ex(jobj_area, "offset", &jobj_offset) ||
113             !json_object_object_get_ex(jobj_area, "size", &jobj_length))
114                 return -EINVAL;
115
116         area_offset = crypt_jobj_get_uint64(jobj_offset);
117         area_length = crypt_jobj_get_uint64(jobj_length);
118
119         if (!area_offset || !area_length || ((uint64_t)buffer_len > area_length))
120                 return -EINVAL;
121
122         devfd = device_open_locked(cd, device, O_RDWR);
123         if (devfd >= 0) {
124                 if (write_lseek_blockwise(devfd, device_block_size(cd, device),
125                                           device_alignment(device), CONST_CAST(void *)buffer,
126                                           buffer_len, area_offset) < 0)
127                         r = -EIO;
128                 else
129                         r = 0;
130         } else
131                 r = -EINVAL;
132
133         if (r)
134                 log_err(cd, _("IO error while encrypting keyslot."));
135
136         return r;
137 }
138
139 static int reenc_keyslot_store(struct crypt_device *cd,
140         int keyslot,
141         const char *password __attribute__((unused)),
142         size_t password_len __attribute__((unused)),
143         const char *buffer,
144         size_t buffer_len)
145 {
146         struct luks2_hdr *hdr;
147         json_object *jobj_keyslot;
148         int r = 0;
149
150         if (!cd || !buffer || !buffer_len)
151                 return -EINVAL;
152
153         if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
154                 return -EINVAL;
155
156         log_dbg(cd, "Reencrypt keyslot %d store.", keyslot);
157
158         jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot);
159         if (!jobj_keyslot)
160                 return -EINVAL;
161
162         r = LUKS2_device_write_lock(cd, hdr, crypt_metadata_device(cd));
163         if (r)
164                 return r;
165
166         r = reenc_keyslot_store_data(cd, jobj_keyslot, buffer, buffer_len);
167         if (r < 0) {
168                 device_write_unlock(cd, crypt_metadata_device(cd));
169                 return r;
170         }
171
172         r = LUKS2_hdr_write(cd, hdr);
173
174         device_write_unlock(cd, crypt_metadata_device(cd));
175
176         return r < 0 ? r : keyslot;
177 }
178
179 int reenc_keyslot_update(struct crypt_device *cd,
180         const struct luks2_reenc_context *rh)
181 {
182         json_object *jobj_keyslot, *jobj_area, *jobj_area_type;
183         struct luks2_hdr *hdr;
184
185         if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2)))
186                 return -EINVAL;
187
188         jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, rh->reenc_keyslot);
189         if (!jobj_keyslot)
190                 return -EINVAL;
191
192         json_object_object_get_ex(jobj_keyslot, "area", &jobj_area);
193         json_object_object_get_ex(jobj_area, "type", &jobj_area_type);
194
195         if (rh->rp.type == REENC_PROTECTION_CHECKSUM) {
196                 log_dbg(cd, "Updating reencrypt keyslot for checksum protection.");
197                 json_object_object_add(jobj_area, "type", json_object_new_string("checksum"));
198                 json_object_object_add(jobj_area, "hash", json_object_new_string(rh->rp.p.csum.hash));
199                 json_object_object_add(jobj_area, "sector_size", json_object_new_int64(rh->alignment));
200         } else if (rh->rp.type == REENC_PROTECTION_NONE) {
201                 log_dbg(cd, "Updating reencrypt keyslot for none protection.");
202                 json_object_object_add(jobj_area, "type", json_object_new_string("none"));
203                 json_object_object_del(jobj_area, "hash");
204         } else if (rh->rp.type == REENC_PROTECTION_JOURNAL) {
205                 log_dbg(cd, "Updating reencrypt keyslot for journal protection.");
206                 json_object_object_add(jobj_area, "type", json_object_new_string("journal"));
207                 json_object_object_del(jobj_area, "hash");
208         } else
209                 log_dbg(cd, "No update of reencrypt keyslot needed.");
210
211         return 0;
212 }
213
214 static int reenc_keyslot_wipe(struct crypt_device *cd, int keyslot)
215 {
216         return 0;
217 }
218
219 static int reenc_keyslot_dump(struct crypt_device *cd, int keyslot)
220 {
221         json_object *jobj_keyslot, *jobj_area, *jobj_direction, *jobj_mode, *jobj_resilience,
222                     *jobj1;
223
224         jobj_keyslot = LUKS2_get_keyslot_jobj(crypt_get_hdr(cd, CRYPT_LUKS2), keyslot);
225         if (!jobj_keyslot)
226                 return -EINVAL;
227
228         if (!json_object_object_get_ex(jobj_keyslot, "direction", &jobj_direction) ||
229             !json_object_object_get_ex(jobj_keyslot, "mode", &jobj_mode) ||
230             !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
231             !json_object_object_get_ex(jobj_area, "type", &jobj_resilience))
232                 return -EINVAL;
233
234         log_std(cd, "\t%-12s%s\n", "Mode:", json_object_get_string(jobj_mode));
235         log_std(cd, "\t%-12s%s\n", "Direction:", json_object_get_string(jobj_direction));
236         log_std(cd, "\t%-12s%s\n", "Resilience:", json_object_get_string(jobj_resilience));
237
238         if (!strcmp(json_object_get_string(jobj_resilience), "checksum")) {
239                 json_object_object_get_ex(jobj_area, "hash", &jobj1);
240                 log_std(cd, "\t%-12s%s\n", "Hash:", json_object_get_string(jobj1));
241                 json_object_object_get_ex(jobj_area, "sector_size", &jobj1);
242                 log_std(cd, "\t%-12s%d [bytes]\n", "Hash data:", json_object_get_int(jobj1));
243         } else if (!strcmp(json_object_get_string(jobj_resilience), "datashift")) {
244                 json_object_object_get_ex(jobj_area, "shift_size", &jobj1);
245                 log_std(cd, "\t%-12s%" PRIu64 "[bytes]\n", "Shift size:", crypt_jobj_get_uint64(jobj1));
246         }
247
248         json_object_object_get_ex(jobj_area, "offset", &jobj1);
249         log_std(cd, "\tArea offset:%" PRIu64 " [bytes]\n", crypt_jobj_get_uint64(jobj1));
250
251         json_object_object_get_ex(jobj_area, "size", &jobj1);
252         log_std(cd, "\tArea length:%" PRIu64 " [bytes]\n", crypt_jobj_get_uint64(jobj1));
253
254         return 0;
255 }
256
257 static int reenc_keyslot_validate(struct crypt_device *cd, json_object *jobj_keyslot)
258 {
259         json_object *jobj_mode, *jobj_area, *jobj_type, *jobj_shift_size, *jobj_hash, *jobj_sector_size, *jobj_direction;
260         const char *mode, *type, *direction;
261         uint32_t sector_size;
262         uint64_t shift_size;
263
264         /* mode (string: encrypt,reencrypt,decrypt)
265          * direction (string:)
266          * area {
267          *   type: (string: datashift, journal, checksum, none)
268          *      hash: (string: checksum only)
269          *      sector_size (uint32: checksum only)
270          *      shift_size (uint64: datashift only)
271          * }
272          */
273
274         /* area and area type are validated in general validation code */
275         if (!jobj_keyslot || !json_object_object_get_ex(jobj_keyslot, "area", &jobj_area) ||
276             !json_object_object_get_ex(jobj_area, "type", &jobj_type))
277                 return -EINVAL;
278
279         jobj_mode = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "mode", json_type_string);
280         jobj_direction = json_contains(cd, jobj_keyslot, "", "reencrypt keyslot", "direction", json_type_string);
281
282         if (!jobj_mode || !jobj_direction)
283                 return -EINVAL;
284
285         mode = json_object_get_string(jobj_mode);
286         type = json_object_get_string(jobj_type);
287         direction = json_object_get_string(jobj_direction);
288
289         if (strcmp(mode, "reencrypt") && strcmp(mode, "encrypt") &&
290             strcmp(mode, "decrypt")) {
291                 log_dbg(cd, "Illegal reencrypt mode %s.", mode);
292                 return -EINVAL;
293         }
294
295         if (strcmp(direction, "forward") && strcmp(direction, "backward")) {
296                 log_dbg(cd, "Illegal reencrypt direction %s.", direction);
297                 return -EINVAL;
298         }
299
300         if (!strcmp(type, "checksum")) {
301                 jobj_hash = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "hash", json_type_string);
302                 jobj_sector_size = json_contains(cd, jobj_area, "type:checksum", "Keyslot area", "sector_size", json_type_int);
303                 if (!jobj_hash || !jobj_sector_size)
304                         return -EINVAL;
305                 if (!validate_json_uint32(jobj_sector_size))
306                         return -EINVAL;
307                 sector_size = crypt_jobj_get_uint32(jobj_sector_size);
308                 if (sector_size < SECTOR_SIZE || NOTPOW2(sector_size)) {
309                         log_dbg(cd, "Invalid sector_size (%" PRIu32 ") for checksum resilience mode.", sector_size);
310                         return -EINVAL;
311                 }
312         } else if (!strcmp(type, "datashift")) {
313                 if (!(jobj_shift_size = json_contains(cd, jobj_area, "type:datashift", "Keyslot area", "shift_size", json_type_string)))
314                         return -EINVAL;
315
316                 shift_size = crypt_jobj_get_uint64(jobj_shift_size);
317                 if (!shift_size)
318                         return -EINVAL;
319
320                 if (MISALIGNED_512(shift_size)) {
321                         log_dbg(cd, "Shift size field has to be aligned to sector size: %" PRIu32, SECTOR_SIZE);
322                         return -EINVAL;
323                 }
324         }
325
326         return 0;
327 }
328
329 const keyslot_handler reenc_keyslot = {
330         .name  = "reencrypt",
331         .open  = reenc_keyslot_open,
332         .store = reenc_keyslot_store, /* initialization only or also per every chunk write */
333         .wipe  = reenc_keyslot_wipe,
334         .dump  = reenc_keyslot_dump,
335         .validate  = reenc_keyslot_validate
336 };