Imported Upstream version 2.6.1
[platform/upstream/cryptsetup.git] / lib / luks2 / luks2_segment.c
1 /*
2  * LUKS - Linux Unified Key Setup v2, internal segment handling
3  *
4  * Copyright (C) 2018-2023 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2018-2023 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 /* use only on already validated 'segments' object */
25 uint64_t json_segments_get_minimal_offset(json_object *jobj_segments, unsigned blockwise)
26 {
27         uint64_t tmp, min = blockwise ? UINT64_MAX >> SECTOR_SHIFT : UINT64_MAX;
28
29         if (!jobj_segments)
30                 return 0;
31
32         json_object_object_foreach(jobj_segments, key, val) {
33                 UNUSED(key);
34
35                 if (json_segment_is_backup(val))
36                         continue;
37
38                 tmp = json_segment_get_offset(val, blockwise);
39
40                 if (!tmp)
41                         return tmp;
42
43                 if (tmp < min)
44                         min = tmp;
45         }
46
47         return min;
48 }
49
50 uint64_t json_segment_get_offset(json_object *jobj_segment, unsigned blockwise)
51 {
52         json_object *jobj;
53
54         if (!jobj_segment ||
55             !json_object_object_get_ex(jobj_segment, "offset", &jobj))
56                 return 0;
57
58         return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
59 }
60
61 const char *json_segment_type(json_object *jobj_segment)
62 {
63         json_object *jobj;
64
65         if (!jobj_segment ||
66             !json_object_object_get_ex(jobj_segment, "type", &jobj))
67                 return NULL;
68
69         return json_object_get_string(jobj);
70 }
71
72 uint64_t json_segment_get_iv_offset(json_object *jobj_segment)
73 {
74         json_object *jobj;
75
76         if (!jobj_segment ||
77             !json_object_object_get_ex(jobj_segment, "iv_tweak", &jobj))
78                 return 0;
79
80         return crypt_jobj_get_uint64(jobj);
81 }
82
83 uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise)
84 {
85         json_object *jobj;
86
87         if (!jobj_segment ||
88             !json_object_object_get_ex(jobj_segment, "size", &jobj))
89                 return 0;
90
91         return blockwise ? crypt_jobj_get_uint64(jobj) >> SECTOR_SHIFT : crypt_jobj_get_uint64(jobj);
92 }
93
94 const char *json_segment_get_cipher(json_object *jobj_segment)
95 {
96         json_object *jobj;
97
98         /* FIXME: Pseudo "null" cipher should be handled elsewhere */
99         if (!jobj_segment ||
100             !json_object_object_get_ex(jobj_segment, "encryption", &jobj))
101                 return "null";
102
103         return json_object_get_string(jobj);
104 }
105
106 uint32_t json_segment_get_sector_size(json_object *jobj_segment)
107 {
108         json_object *jobj;
109         int i;
110
111         if (!jobj_segment ||
112             !json_object_object_get_ex(jobj_segment, "sector_size", &jobj))
113                 return SECTOR_SIZE;
114
115         i = json_object_get_int(jobj);
116         return i < 0 ? SECTOR_SIZE : i;
117 }
118
119 static json_object *json_segment_get_flags(json_object *jobj_segment)
120 {
121         json_object *jobj;
122
123         if (!jobj_segment || !(json_object_object_get_ex(jobj_segment, "flags", &jobj)))
124                 return NULL;
125         return jobj;
126 }
127
128 bool json_segment_contains_flag(json_object *jobj_segment, const char *flag_str, size_t len)
129 {
130         int r, i;
131         json_object *jobj, *jobj_flags = json_segment_get_flags(jobj_segment);
132
133         if (!jobj_flags)
134                 return false;
135
136         for (i = 0; i < (int)json_object_array_length(jobj_flags); i++) {
137                 jobj = json_object_array_get_idx(jobj_flags, i);
138                 if (len)
139                         r = strncmp(json_object_get_string(jobj), flag_str, len);
140                 else
141                         r = strcmp(json_object_get_string(jobj), flag_str);
142                 if (!r)
143                         return true;
144         }
145
146         return false;
147 }
148
149 bool json_segment_is_backup(json_object *jobj_segment)
150 {
151         return json_segment_contains_flag(jobj_segment, "backup-", 7);
152 }
153
154 json_object *json_segments_get_segment(json_object *jobj_segments, int segment)
155 {
156         json_object *jobj;
157         char segment_name[16];
158
159         if (snprintf(segment_name, sizeof(segment_name), "%u", segment) < 1)
160                 return NULL;
161
162         if (!json_object_object_get_ex(jobj_segments, segment_name, &jobj))
163                 return NULL;
164
165         return jobj;
166 }
167
168 unsigned json_segments_count(json_object *jobj_segments)
169 {
170         unsigned count = 0;
171
172         if (!jobj_segments)
173                 return 0;
174
175         json_object_object_foreach(jobj_segments, slot, val) {
176                 UNUSED(slot);
177                 if (!json_segment_is_backup(val))
178                         count++;
179         }
180
181         return count;
182 }
183
184 static void _get_segment_or_id_by_flag(json_object *jobj_segments, const char *flag, unsigned id, void *retval)
185 {
186         json_object *jobj_flags, **jobj_ret = (json_object **)retval;
187         int *ret = (int *)retval;
188
189         if (!flag)
190                 return;
191
192         json_object_object_foreach(jobj_segments, key, value) {
193                 if (!json_object_object_get_ex(value, "flags", &jobj_flags))
194                         continue;
195                 if (LUKS2_array_jobj(jobj_flags, flag)) {
196                         if (id)
197                                 *ret = atoi(key);
198                         else
199                                 *jobj_ret = value;
200                         return;
201                 }
202         }
203 }
204
205 void json_segment_remove_flag(json_object *jobj_segment, const char *flag)
206 {
207         json_object *jobj_flags, *jobj_flags_new;
208
209         if (!jobj_segment)
210                 return;
211
212         jobj_flags = json_segment_get_flags(jobj_segment);
213         if (!jobj_flags)
214                 return;
215
216         jobj_flags_new = LUKS2_array_remove(jobj_flags, flag);
217         if (!jobj_flags_new)
218                 return;
219
220         if (json_object_array_length(jobj_flags_new) <= 0) {
221                 json_object_put(jobj_flags_new);
222                 json_object_object_del(jobj_segment, "flags");
223         } else
224                 json_object_object_add(jobj_segment, "flags", jobj_flags_new);
225 }
226
227 static json_object *_segment_create_generic(const char *type, uint64_t offset, const uint64_t *length)
228 {
229         json_object *jobj = json_object_new_object();
230         if (!jobj)
231                 return NULL;
232
233         json_object_object_add(jobj, "type",            json_object_new_string(type));
234         json_object_object_add(jobj, "offset",          crypt_jobj_new_uint64(offset));
235         json_object_object_add(jobj, "size",            length ? crypt_jobj_new_uint64(*length) : json_object_new_string("dynamic"));
236
237         return jobj;
238 }
239
240 json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length, unsigned reencryption)
241 {
242         json_object *jobj = _segment_create_generic("linear", offset, length);
243         if (reencryption)
244                 LUKS2_segment_set_flag(jobj, "in-reencryption");
245         return jobj;
246 }
247
248 json_object *json_segment_create_crypt(uint64_t offset,
249                                   uint64_t iv_offset, const uint64_t *length,
250                                   const char *cipher, uint32_t sector_size,
251                                   unsigned reencryption)
252 {
253         json_object *jobj = _segment_create_generic("crypt", offset, length);
254         if (!jobj)
255                 return NULL;
256
257         json_object_object_add(jobj, "iv_tweak",        crypt_jobj_new_uint64(iv_offset));
258         json_object_object_add(jobj, "encryption",      json_object_new_string(cipher));
259         json_object_object_add(jobj, "sector_size",     json_object_new_int(sector_size));
260         if (reencryption)
261                 LUKS2_segment_set_flag(jobj, "in-reencryption");
262
263         return jobj;
264 }
265
266 uint64_t LUKS2_segment_offset(struct luks2_hdr *hdr, int segment, unsigned blockwise)
267 {
268         return json_segment_get_offset(LUKS2_get_segment_jobj(hdr, segment), blockwise);
269 }
270
271 int json_segments_segment_in_reencrypt(json_object *jobj_segments)
272 {
273         json_object *jobj_flags;
274
275         json_object_object_foreach(jobj_segments, slot, val) {
276                 if (!json_object_object_get_ex(val, "flags", &jobj_flags) ||
277                     !LUKS2_array_jobj(jobj_flags, "in-reencryption"))
278                         continue;
279
280                 return atoi(slot);
281         }
282
283         return -1;
284 }
285
286 uint64_t LUKS2_segment_size(struct luks2_hdr *hdr, int segment, unsigned blockwise)
287 {
288         return json_segment_get_size(LUKS2_get_segment_jobj(hdr, segment), blockwise);
289 }
290
291 int LUKS2_segment_is_type(struct luks2_hdr *hdr, int segment, const char *type)
292 {
293         return !strcmp(json_segment_type(LUKS2_get_segment_jobj(hdr, segment)) ?: "", type);
294 }
295
296 int LUKS2_last_segment_by_type(struct luks2_hdr *hdr, const char *type)
297 {
298         json_object *jobj_segments;
299         int last_found = -1;
300
301         if (!type)
302                 return -1;
303
304         if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
305                 return -1;
306
307         json_object_object_foreach(jobj_segments, slot, val) {
308                 if (json_segment_is_backup(val))
309                         continue;
310                 if (strcmp(type, json_segment_type(val) ?: ""))
311                         continue;
312
313                 if (atoi(slot) > last_found)
314                         last_found = atoi(slot);
315         }
316
317         return last_found;
318 }
319
320 int LUKS2_segment_by_type(struct luks2_hdr *hdr, const char *type)
321 {
322         json_object *jobj_segments;
323         int first_found = -1;
324
325         if (!type)
326                 return -EINVAL;
327
328         if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
329                 return -EINVAL;
330
331         json_object_object_foreach(jobj_segments, slot, val) {
332                 if (json_segment_is_backup(val))
333                         continue;
334                 if (strcmp(type, json_segment_type(val) ?: ""))
335                         continue;
336
337                 if (first_found < 0)
338                         first_found = atoi(slot);
339                 else if (atoi(slot) < first_found)
340                         first_found = atoi(slot);
341         }
342
343         return first_found;
344 }
345
346 int LUKS2_segment_first_unused_id(struct luks2_hdr *hdr)
347 {
348         json_object *jobj_segments;
349
350         if (!json_object_object_get_ex(hdr->jobj, "segments", &jobj_segments))
351                 return -EINVAL;
352
353         return json_object_object_length(jobj_segments);
354 }
355
356 int LUKS2_segment_set_flag(json_object *jobj_segment, const char *flag)
357 {
358         json_object *jobj_flags;
359
360         if (!jobj_segment || !flag)
361                 return -EINVAL;
362
363         if (!json_object_object_get_ex(jobj_segment, "flags", &jobj_flags)) {
364                 jobj_flags = json_object_new_array();
365                 if (!jobj_flags)
366                         return -ENOMEM;
367                 json_object_object_add(jobj_segment, "flags", jobj_flags);
368         }
369
370         if (LUKS2_array_jobj(jobj_flags, flag))
371                 return 0;
372
373         json_object_array_add(jobj_flags, json_object_new_string(flag));
374
375         return 0;
376 }
377
378 int LUKS2_segments_set(struct crypt_device *cd, struct luks2_hdr *hdr,
379                        json_object *jobj_segments, int commit)
380 {
381         json_object_object_add(hdr->jobj, "segments", jobj_segments);
382
383         return commit ? LUKS2_hdr_write(cd, hdr) : 0;
384 }
385
386 int LUKS2_get_segment_id_by_flag(struct luks2_hdr *hdr, const char *flag)
387 {
388         int ret = -ENOENT;
389         json_object *jobj_segments = LUKS2_get_segments_jobj(hdr);
390
391         if (jobj_segments)
392                 _get_segment_or_id_by_flag(jobj_segments, flag, 1, &ret);
393
394         return ret;
395 }
396
397 json_object *LUKS2_get_segment_by_flag(struct luks2_hdr *hdr, const char *flag)
398 {
399         json_object *jobj_segment = NULL,
400                     *jobj_segments = LUKS2_get_segments_jobj(hdr);
401
402         if (jobj_segments)
403                 _get_segment_or_id_by_flag(jobj_segments, flag, 0, &jobj_segment);
404
405         return jobj_segment;
406 }
407
408 /* compares key characteristics of both segments */
409 bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2)
410 {
411         const char *type = json_segment_type(jobj_segment_1);
412         const char *type2 = json_segment_type(jobj_segment_2);
413
414         if (!type || !type2)
415                 return false;
416
417         if (strcmp(type, type2))
418                 return false;
419
420         if (!strcmp(type, "crypt"))
421                 return (json_segment_get_sector_size(jobj_segment_1) == json_segment_get_sector_size(jobj_segment_2) &&
422                         !strcmp(json_segment_get_cipher(jobj_segment_1),
423                                 json_segment_get_cipher(jobj_segment_2)));
424
425         return true;
426 }