Imported Upstream version 2.6.1
[platform/upstream/cryptsetup.git] / src / integritysetup.c
1 /*
2  * integritysetup - setup integrity protected volumes for dm-integrity
3  *
4  * Copyright (C) 2017-2023 Red Hat, Inc. All rights reserved.
5  * Copyright (C) 2017-2023 Milan Broz
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 <uuid/uuid.h>
23
24 #define DEFAULT_ALG_NAME "crc32c"
25
26 #include "cryptsetup.h"
27 #include "integritysetup_args.h"
28
29 #define PACKAGE_INTEGRITY "integritysetup"
30
31 static const char **action_argv;
32 static int action_argc;
33 static struct tools_log_params log_parms;
34
35 void tools_cleanup(void)
36 {
37         tools_args_free(tool_core_args, ARRAY_SIZE(tool_core_args));
38 }
39
40 static int _read_keys(char **integrity_key, struct crypt_params_integrity *params)
41 {
42         char *int_key = NULL, *journal_integrity_key = NULL, *journal_crypt_key = NULL;
43         int r;
44
45         if (integrity_key && ARG_SET(OPT_INTEGRITY_KEY_FILE_ID)) {
46                 r = tools_read_vk(ARG_STR(OPT_INTEGRITY_KEY_FILE_ID), &int_key, ARG_UINT32(OPT_INTEGRITY_KEY_SIZE_ID));
47                 if (r < 0)
48                         return r;
49                 params->integrity_key_size = ARG_UINT32(OPT_INTEGRITY_KEY_SIZE_ID);
50         }
51
52         if (ARG_SET(OPT_JOURNAL_INTEGRITY_KEY_FILE_ID)) {
53                 r = tools_read_vk(ARG_STR(OPT_JOURNAL_INTEGRITY_KEY_FILE_ID), &journal_integrity_key, ARG_UINT32(OPT_JOURNAL_INTEGRITY_KEY_SIZE_ID));
54                 if (r < 0) {
55                         crypt_safe_free(int_key);
56                         return r;
57                 }
58                 params->journal_integrity_key = journal_integrity_key;
59                 params->journal_integrity_key_size = ARG_UINT32(OPT_JOURNAL_INTEGRITY_KEY_SIZE_ID);
60         }
61
62         if (ARG_SET(OPT_JOURNAL_CRYPT_KEY_FILE_ID)) {
63                 r = tools_read_vk(ARG_STR(OPT_JOURNAL_CRYPT_KEY_FILE_ID), &journal_crypt_key, ARG_UINT32(OPT_JOURNAL_CRYPT_KEY_SIZE_ID));
64                 if (r < 0) {
65                         crypt_safe_free(int_key);
66                         crypt_safe_free(journal_integrity_key);
67                         return r;
68                 }
69                 params->journal_crypt_key = journal_crypt_key;
70                 params->journal_crypt_key_size = ARG_UINT32(OPT_JOURNAL_CRYPT_KEY_SIZE_ID);
71         }
72
73         if (integrity_key)
74                 *integrity_key = int_key;
75
76         return 0;
77 }
78
79 static int _wipe_data_device(struct crypt_device *cd, const char *integrity_key)
80 {
81         char tmp_name[64], tmp_path[128], tmp_uuid[40];
82         uuid_t tmp_uuid_bin;
83         int r = -EINVAL;
84         char *backing_file = NULL;
85         struct tools_progress_params prog_parms = {
86                 .frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
87                 .batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
88                 .json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
89                 .interrupt_message = _("\nWipe interrupted."),
90                 .device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
91         };
92
93         if (!ARG_SET(OPT_BATCH_MODE_ID))
94                 log_std(_("Wiping device to initialize integrity checksum.\n"
95                         "You can interrupt this by pressing CTRL+c "
96                         "(rest of not wiped device will contain invalid checksum).\n"));
97
98         /* Activate the device a temporary one */
99         uuid_generate(tmp_uuid_bin);
100         uuid_unparse(tmp_uuid_bin, tmp_uuid);
101         if (snprintf(tmp_name, sizeof(tmp_name), "temporary-cryptsetup-%s", tmp_uuid) < 0)
102                 goto out;
103         if (snprintf(tmp_path, sizeof(tmp_path), "%s/%s", crypt_get_dir(), tmp_name) < 0)
104                 goto out;
105
106         r = crypt_activate_by_volume_key(cd, tmp_name, integrity_key,
107                 ARG_UINT32(OPT_INTEGRITY_KEY_SIZE_ID), CRYPT_ACTIVATE_PRIVATE | CRYPT_ACTIVATE_NO_JOURNAL);
108         if (r < 0)
109                 goto out;
110
111         /* Wipe the device */
112         set_int_handler(0);
113         r = crypt_wipe(cd, tmp_path, CRYPT_WIPE_ZERO, 0, 0, DEFAULT_WIPE_BLOCK,
114                        0, &tools_progress, &prog_parms);
115         if (crypt_deactivate(cd, tmp_name))
116                 log_err(_("Cannot deactivate temporary device %s."), tmp_path);
117         set_int_block(0);
118
119 out:
120         free(backing_file);
121         return r;
122 }
123
124 static int action_format(void)
125 {
126         struct crypt_device *cd = NULL;
127         struct crypt_params_integrity params = {
128                 .journal_size = ARG_UINT64(OPT_JOURNAL_SIZE_ID),
129                 .interleave_sectors = ARG_UINT32(OPT_INTERLEAVE_SECTORS_ID),
130                 /* in bitmap mode we have to overload these values... */
131                 .journal_watermark = ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID) ? ARG_UINT32(OPT_BITMAP_SECTORS_PER_BIT_ID) : ARG_UINT32(OPT_JOURNAL_WATERMARK_ID),
132                 .journal_commit_time = ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID) ? ARG_UINT32(OPT_BITMAP_FLUSH_TIME_ID) : ARG_UINT32(OPT_JOURNAL_COMMIT_TIME_ID),
133                 .buffer_sectors = ARG_UINT32(OPT_BUFFER_SECTORS_ID),
134                 .tag_size = ARG_UINT32(OPT_TAG_SIZE_ID),
135                 .sector_size = ARG_UINT32(OPT_SECTOR_SIZE_ID),
136         }, params2;
137         char integrity[MAX_CIPHER_LEN], journal_integrity[MAX_CIPHER_LEN], journal_crypt[MAX_CIPHER_LEN];
138         char *integrity_key = NULL, *msg = NULL;
139         int r;
140         size_t signatures;
141
142         r = crypt_parse_hash_integrity_mode(ARG_STR(OPT_INTEGRITY_ID), integrity);
143         if (r < 0) {
144                 log_err(_("No known integrity specification pattern detected."));
145                 return r;
146         }
147         params.integrity = integrity;
148
149         if (ARG_SET(OPT_JOURNAL_INTEGRITY_ID)) {
150                 r = crypt_parse_hash_integrity_mode(ARG_STR(OPT_JOURNAL_INTEGRITY_ID), journal_integrity);
151                 if (r < 0) {
152                         log_err(_("No known integrity specification pattern detected."));
153                         return r;
154                 }
155                 params.journal_integrity = journal_integrity;
156         }
157
158         if (ARG_SET(OPT_JOURNAL_CRYPT_ID)) {
159                 r = crypt_parse_hash_integrity_mode(ARG_STR(OPT_JOURNAL_CRYPT_ID), journal_crypt);
160                 if (r < 0) {
161                         log_err(_("No known integrity specification pattern detected."));
162                         return r;
163                 }
164                 params.journal_crypt = journal_crypt;
165         }
166
167         r = _read_keys(&integrity_key, &params);
168         if (r)
169                 goto out;
170
171         r = crypt_init_data_device(&cd, action_argv[0], ARG_STR(OPT_DATA_DEVICE_ID));
172         if (r < 0)
173                 goto out;
174
175         if (!ARG_SET(OPT_BATCH_MODE_ID)) {
176                 if (ARG_SET(OPT_DATA_DEVICE_ID) && !ARG_SET(OPT_NO_WIPE_ID))
177                         r = asprintf(&msg, _("This will overwrite data on %s and %s irrevocably.\n"
178                         "To preserve data device use --no-wipe option (and then activate with --integrity-recalculate)."),
179                         action_argv[0], ARG_STR(OPT_DATA_DEVICE_ID));
180                 else
181                         r = asprintf(&msg, _("This will overwrite data on %s irrevocably."), action_argv[0]);
182                 if (r == -1) {
183                         r = -ENOMEM;
184                         goto out;
185                 }
186
187                 r = yesDialog(msg, _("Operation aborted.\n")) ? 0 : -EINVAL;
188                 free(msg);
189                 if (r < 0)
190                         goto out;
191         }
192
193         r = tools_detect_signatures(action_argv[0], PRB_FILTER_NONE, &signatures, ARG_SET(OPT_BATCH_MODE_ID));
194         if (r < 0)
195                 goto out;
196
197         /* Signature candidates found */
198         if (signatures && ((r = tools_wipe_all_signatures(action_argv[0], true, false)) < 0))
199                 goto out;
200
201         if (ARG_SET(OPT_INTEGRITY_LEGACY_PADDING_ID))
202                 crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING);
203
204         if (ARG_SET(OPT_INTEGRITY_LEGACY_HMAC_ID))
205                 crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC);
206
207         r = crypt_format(cd, CRYPT_INTEGRITY, NULL, NULL, NULL, NULL, 0, &params);
208         if (r < 0) /* FIXME: call wipe signatures again */
209                 goto out;
210
211         if (!ARG_SET(OPT_BATCH_MODE_ID) && !crypt_get_integrity_info(cd, &params2))
212                 log_std(_("Formatted with tag size %u, internal integrity %s.\n"),
213                         params2.tag_size, params2.integrity);
214
215         if (!ARG_SET(OPT_NO_WIPE_ID))
216                 r = _wipe_data_device(cd, integrity_key);
217 out:
218         crypt_safe_free(integrity_key);
219         crypt_safe_free(CONST_CAST(void*)params.journal_integrity_key);
220         crypt_safe_free(CONST_CAST(void*)params.journal_crypt_key);
221         crypt_free(cd);
222         return r;
223 }
224
225 static int action_resize(void)
226 {
227         int r;
228         struct crypt_device *cd = NULL;
229         struct crypt_active_device cad;
230         uint64_t new_dev_size = 0;
231         uint64_t old_dev_size;
232         char path[PATH_MAX];
233         char *backing_file = NULL;
234         struct tools_progress_params prog_parms = {
235                 .frequency = ARG_UINT32(OPT_PROGRESS_FREQUENCY_ID),
236                 .batch_mode = ARG_SET(OPT_BATCH_MODE_ID),
237                 .json_output = ARG_SET(OPT_PROGRESS_JSON_ID),
238                 .interrupt_message = _("\nWipe interrupted."),
239                 .device = tools_get_device_name(crypt_get_device_name(cd), &backing_file)
240         };
241
242         if (ARG_SET(OPT_DEVICE_SIZE_ID))
243                 new_dev_size = ARG_UINT64(OPT_DEVICE_SIZE_ID) / SECTOR_SIZE;
244         else if (ARG_SET(OPT_SIZE_ID))
245                 new_dev_size = ARG_UINT64(OPT_SIZE_ID);
246
247         r = crypt_init_by_name_and_header(&cd, action_argv[0], NULL);
248         if (r)
249                 goto out;
250
251         r = crypt_get_active_device(cd, action_argv[0], &cad);
252         if (r)
253                 goto out;
254         old_dev_size = cad.size;
255
256         r = snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), action_argv[0]);
257         if (r < 0)
258                 goto out;
259         r = crypt_resize(cd, action_argv[0], new_dev_size);
260         if (r)
261                 goto out;
262
263         if (!new_dev_size) {
264                 r = crypt_get_active_device(cd, action_argv[0], &cad);
265                 if (r)
266                         goto out;
267                 new_dev_size = cad.size;
268         }
269
270         if (new_dev_size > old_dev_size) {
271                 if (ARG_SET(OPT_WIPE_ID)) {
272                         if (ARG_SET(OPT_BATCH_MODE_ID))
273                                 log_dbg("Wiping the end of the resized device");
274                         else
275                                 log_std(_("Wiping device to initialize integrity checksum.\n"
276                                         "You can interrupt this by pressing CTRL+c "
277                                         "(rest of not wiped device will contain invalid checksum).\n"));
278
279                         set_int_handler(0);
280                         r = crypt_wipe(cd, path, CRYPT_WIPE_ZERO, old_dev_size * SECTOR_SIZE,
281                                       (new_dev_size - old_dev_size) * SECTOR_SIZE, DEFAULT_WIPE_BLOCK,
282                                       0, &tools_progress, &prog_parms);
283                         set_int_block(0);
284                 } else {
285                         log_dbg("Setting recalculate flag");
286                         r = crypt_activate_by_volume_key(cd, action_argv[0], NULL, 0, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_RECALCULATE);
287
288                         if (r == -ENOTSUP)
289                                 log_err(_("Setting recalculate flag is not supported, you may consider using --wipe instead."));
290                 }
291         }
292 out:
293         if (backing_file)
294                 free(backing_file);
295         crypt_free(cd);
296         return r;
297 }
298
299 static int action_open(void)
300 {
301         struct crypt_device *cd = NULL;
302         struct crypt_params_integrity params = {
303                 /* in bitmap mode we have to overload these values... */
304                 .journal_watermark = ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID) ? ARG_UINT32(OPT_BITMAP_SECTORS_PER_BIT_ID) : ARG_UINT32(OPT_JOURNAL_WATERMARK_ID),
305                 .journal_commit_time = ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID) ? ARG_UINT32(OPT_BITMAP_FLUSH_TIME_ID) : ARG_UINT32(OPT_JOURNAL_COMMIT_TIME_ID),
306                 .buffer_sectors = ARG_UINT32(OPT_BUFFER_SECTORS_ID),
307         };
308         uint32_t activate_flags = 0;
309         char integrity[MAX_CIPHER_LEN], journal_integrity[MAX_CIPHER_LEN], journal_crypt[MAX_CIPHER_LEN];
310         char *integrity_key = NULL;
311         int r;
312
313         r = crypt_parse_hash_integrity_mode(ARG_STR(OPT_INTEGRITY_ID), integrity);
314         if (r < 0) {
315                 log_err(_("No known integrity specification pattern detected."));
316                 return r;
317         }
318         params.integrity = integrity;
319
320         if (ARG_SET(OPT_JOURNAL_INTEGRITY_ID)) {
321                 r = crypt_parse_hash_integrity_mode(ARG_STR(OPT_JOURNAL_INTEGRITY_ID), journal_integrity);
322                 if (r < 0) {
323                         log_err(_("No known integrity specification pattern detected."));
324                         return r;
325
326                 }
327                 params.journal_integrity = journal_integrity;
328         }
329
330         if (ARG_SET(OPT_JOURNAL_CRYPT_ID)) {
331                 r = crypt_parse_hash_integrity_mode(ARG_STR(OPT_JOURNAL_CRYPT_ID), journal_crypt);
332                 if (r < 0) {
333                         log_err(_("No known integrity specification pattern detected."));
334                         return r;
335                 }
336                 params.journal_crypt = journal_crypt;
337         }
338
339         if (ARG_SET(OPT_INTEGRITY_NO_JOURNAL_ID) || ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID))
340                 activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL;
341         if (ARG_SET(OPT_INTEGRITY_RECOVERY_MODE_ID))
342                 activate_flags |= CRYPT_ACTIVATE_RECOVERY;
343         if (ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID))
344                 activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP;
345
346         if (ARG_SET(OPT_INTEGRITY_RECALCULATE_ID) || ARG_SET(OPT_INTEGRITY_LEGACY_RECALC_ID))
347                 activate_flags |= CRYPT_ACTIVATE_RECALCULATE;
348
349         if (ARG_SET(OPT_INTEGRITY_RECALCULATE_RESET_ID))
350                 activate_flags |= CRYPT_ACTIVATE_RECALCULATE_RESET;
351
352         if (ARG_SET(OPT_ALLOW_DISCARDS_ID))
353                 activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
354
355         r = _read_keys(&integrity_key, &params);
356         if (r)
357                 goto out;
358
359         if ((r = crypt_init_data_device(&cd, action_argv[0], ARG_STR(OPT_DATA_DEVICE_ID))))
360                 goto out;
361
362         r = crypt_load(cd, CRYPT_INTEGRITY, &params);
363         if (r) {
364                 log_err(_("Device %s is not a valid INTEGRITY device."), action_argv[0]);
365                 goto out;
366         }
367
368         if (ARG_SET(OPT_INTEGRITY_LEGACY_RECALC_ID))
369                 crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC);
370
371         r = crypt_activate_by_volume_key(cd, action_argv[1], integrity_key,
372                                          ARG_UINT32(OPT_INTEGRITY_KEY_SIZE_ID), activate_flags);
373 out:
374         crypt_safe_free(integrity_key);
375         crypt_safe_free(CONST_CAST(void*)params.journal_integrity_key);
376         crypt_safe_free(CONST_CAST(void*)params.journal_crypt_key);
377         crypt_free(cd);
378         return r;
379 }
380
381 static int action_close(void)
382 {
383         struct crypt_device *cd = NULL;
384         crypt_status_info ci;
385         uint32_t flags = 0;
386         int r;
387
388         if (ARG_SET(OPT_DEFERRED_ID))
389                 flags |= CRYPT_DEACTIVATE_DEFERRED;
390         if (ARG_SET(OPT_CANCEL_DEFERRED_ID))
391                 flags |= CRYPT_DEACTIVATE_DEFERRED_CANCEL;
392
393         r = crypt_init_by_name(&cd, action_argv[0]);
394         if (r == 0)
395                 r = crypt_deactivate_by_name(cd, action_argv[0], flags);
396
397         if (!r && ARG_SET(OPT_DEFERRED_ID)) {
398                 ci = crypt_status(cd, action_argv[0]);
399                 if (ci == CRYPT_ACTIVE || ci == CRYPT_BUSY)
400                         log_std(_("Device %s is still active and scheduled for deferred removal.\n"),
401                                   action_argv[0]);
402         }
403
404         crypt_free(cd);
405         return r;
406 }
407
408 static int action_status(void)
409 {
410         crypt_status_info ci;
411         struct crypt_active_device cad;
412         struct crypt_params_integrity ip = {};
413         struct crypt_device *cd = NULL;
414         char *backing_file;
415         const char *device, *metadata_device;
416         int path = 0, r = 0;
417
418         /* perhaps a path, not a dm device name */
419         if (strchr(action_argv[0], '/'))
420                 path = 1;
421
422         ci = crypt_status(NULL, action_argv[0]);
423         switch (ci) {
424         case CRYPT_INVALID:
425                 r = -EINVAL;
426                 break;
427         case CRYPT_INACTIVE:
428                 if (path)
429                         log_std("%s is inactive.\n", action_argv[0]);
430                 else
431                         log_std("%s/%s is inactive.\n", crypt_get_dir(), action_argv[0]);
432                 r = -ENODEV;
433                 break;
434         case CRYPT_ACTIVE:
435         case CRYPT_BUSY:
436                 if (path)
437                         log_std("%s is active%s.\n", action_argv[0],
438                                 ci == CRYPT_BUSY ? " and is in use" : "");
439                 else
440                         log_std("%s/%s is active%s.\n", crypt_get_dir(), action_argv[0],
441                                 ci == CRYPT_BUSY ? " and is in use" : "");
442
443                 r = crypt_init_by_name_and_header(&cd, action_argv[0], NULL);
444                 if (r < 0)
445                         goto out;
446
447                 log_std("  type:    %s\n", crypt_get_type(cd) ?: "n/a");
448
449                 r = crypt_get_active_device(cd, action_argv[0], &cad);
450                 if (r < 0)
451                         goto out;
452
453                 /* Print only INTEGRITY (and LUKS2 with integrity) info */
454                 r = crypt_get_integrity_info(cd, &ip);
455                 if (r < 0)
456                         goto out;
457
458                 log_std("  tag size: %u\n", ip.tag_size);
459                 log_std("  integrity: %s\n", ip.integrity ?: "(none)");
460                 device = crypt_get_device_name(cd);
461                 metadata_device = crypt_get_metadata_device_name(cd);
462                 log_std("  device:  %s%s\n", device, metadata_device ? " (detached)" : "");
463                 if ((backing_file = crypt_loop_backing_file(device))) {
464                         log_std("  loop:    %s\n", backing_file);
465                         free(backing_file);
466                 }
467                 if (metadata_device) {
468                         log_std("  metadata device:  %s\n", metadata_device);
469                         if ((backing_file = crypt_loop_backing_file(metadata_device))) {
470                                 log_std("  loop:    %s\n", backing_file);
471                                 free(backing_file);
472                         }
473                 }
474                 log_std("  sector size:  %u bytes\n", crypt_get_sector_size(cd));
475                 log_std("  interleave sectors: %u\n", ip.interleave_sectors);
476                 log_std("  size:    %" PRIu64 " sectors\n", cad.size);
477                 log_std("  mode:    %s%s\n",
478                         cad.flags & CRYPT_ACTIVATE_READONLY ? "readonly" : "read/write",
479                         cad.flags & CRYPT_ACTIVATE_RECOVERY ? " recovery" : "");
480                 log_std("  failures: %" PRIu64 "\n",
481                         crypt_get_active_integrity_failures(cd, action_argv[0]));
482                 if (cad.flags & CRYPT_ACTIVATE_NO_JOURNAL_BITMAP) {
483                         log_std("  bitmap 512-byte sectors per bit: %u\n", ip.journal_watermark);
484                         log_std("  bitmap flush interval: %u ms\n", ip.journal_commit_time);
485                 } if (cad.flags & CRYPT_ACTIVATE_NO_JOURNAL) {
486                         log_std("  journal: not active\n");
487                 } else {
488                         log_std("  journal size: %" PRIu64 " bytes\n", ip.journal_size);
489                         log_std("  journal watermark: %u%%\n", ip.journal_watermark);
490                         log_std("  journal commit time: %u ms\n", ip.journal_commit_time);
491                         if (ip.journal_integrity)
492                                 log_std("  journal integrity MAC: %s\n", ip.journal_integrity);
493                         if (ip.journal_crypt)
494                                 log_std("  journal encryption: %s\n", ip.journal_crypt);
495                 }
496                 if (cad.flags & (CRYPT_ACTIVATE_ALLOW_DISCARDS))
497                         log_std("  flags: %s\n",
498                                 (cad.flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) ? "discards " : "");
499         }
500 out:
501         crypt_free(cd);
502         if (r == -ENOTSUP)
503                 r = 0;
504         return r;
505         return -EINVAL;
506 }
507
508 static int action_dump(void)
509 {
510         struct crypt_device *cd = NULL;
511         struct crypt_params_integrity params = {};
512         int r;
513
514         if ((r = crypt_init(&cd, action_argv[0])))
515                 return r;
516
517         r = crypt_load(cd, CRYPT_INTEGRITY, &params);
518         if (!r)
519                 crypt_dump(cd);
520         else
521                 log_err(_("Device %s is not a valid INTEGRITY device."), action_argv[0]);
522
523         crypt_free(cd);
524         return r;
525 }
526
527 static struct action_type {
528         const char *type;
529         int (*handler)(void);
530         int required_action_argc;
531         const char *arg_desc;
532         const char *desc;
533 } action_types[] = {
534         { FORMAT_ACTION,action_format, 1, N_("<integrity_device>"),N_("format device") },
535         { OPEN_ACTION,  action_open,   2, N_("<integrity_device> <name>"),N_("open device as <name>") },
536         { CLOSE_ACTION, action_close,  1, N_("<name>"),N_("close device (remove mapping)") },
537         { STATUS_ACTION,action_status, 1, N_("<name>"),N_("show active device status") },
538         { DUMP_ACTION,  action_dump,   1, N_("<integrity_device>"),N_("show on-disk information") },
539         { RESIZE_ACTION,action_resize, 1, N_("<name>"), N_("resize active device") },
540         {}
541 };
542
543 static void help(poptContext popt_context,
544                  enum poptCallbackReason reason __attribute__((unused)),
545                  struct poptOption *key,
546                  const char *arg __attribute__((unused)),
547                  void *data __attribute__((unused)))
548 {
549         struct action_type *action;
550
551         if (key->shortName == '?') {
552                 tools_package_version(PACKAGE_INTEGRITY, false);
553                 poptPrintHelp(popt_context, stdout, 0);
554                 log_std(_("\n"
555                          "<action> is one of:\n"));
556                 for(action = action_types; action->type; action++)
557                         log_std("\t%s %s - %s\n", action->type, _(action->arg_desc), _(action->desc));
558                 log_std(_("\n"
559                          "<name> is the device to create under %s\n"
560                          "<integrity_device> is the device containing data with integrity tags\n"),
561                         crypt_get_dir());
562
563                 log_std(_("\nDefault compiled-in dm-integrity parameters:\n"
564                           "\tChecksum algorithm: %s\n"
565                           "\tMaximum keyfile size: %dkB\n"),
566                           DEFAULT_ALG_NAME, DEFAULT_INTEGRITY_KEYFILE_SIZE_MAXKB);
567                 tools_cleanup();
568                 poptFreeContext(popt_context);
569                 exit(EXIT_SUCCESS);
570         } else if (key->shortName == 'V') {
571                 tools_package_version(PACKAGE_INTEGRITY, false);
572                 tools_cleanup();
573                 poptFreeContext(popt_context);
574                 exit(EXIT_SUCCESS);
575         } else
576                 usage(popt_context, EXIT_SUCCESS, NULL, NULL);
577 }
578
579 static int run_action(struct action_type *action)
580 {
581         int r;
582
583         log_dbg("Running command %s.", action->type);
584
585         r = action->handler();
586
587         show_status(r);
588         return translate_errno(r);
589 }
590
591 static bool needs_size_conversion(unsigned int arg_id)
592 {
593         return (arg_id == OPT_JOURNAL_SIZE_ID || arg_id == OPT_DEVICE_SIZE_ID);
594 }
595
596 static void basic_options_cb(poptContext popt_context,
597                  enum poptCallbackReason reason __attribute__((unused)),
598                  struct poptOption *key,
599                  const char *arg,
600                  void *data __attribute__((unused)))
601 {
602         char msg[256];
603
604         tools_parse_arg_value(popt_context, tool_core_args[key->val].type, tool_core_args + key->val, arg, key->val, needs_size_conversion);
605
606         /* special cases additional handling */
607         switch (key->val) {
608         case OPT_DEBUG_ID:
609                 log_parms.debug = true;
610                 /* fall through */
611         case OPT_VERBOSE_ID:
612                 log_parms.verbose = true;
613                 break;
614         case OPT_INTEGRITY_KEY_SIZE_ID:
615                 /* fall through */
616         case OPT_JOURNAL_INTEGRITY_KEY_SIZE_ID:
617                 /* fall through */
618         case OPT_JOURNAL_CRYPT_KEY_SIZE_ID:
619                 if (ARG_UINT32(key->val) > (DEFAULT_INTEGRITY_KEYFILE_SIZE_MAXKB * 1024)) {
620                         if (snprintf(msg, sizeof(msg), _("Invalid --%s size. Maximum is %u bytes."),
621                             key->longName, DEFAULT_INTEGRITY_KEYFILE_SIZE_MAXKB * 1024) < 0)
622                                 msg[0] = '\0';
623                         usage(popt_context, EXIT_FAILURE, msg,
624                               poptGetInvocationName(popt_context));
625                 }
626         }
627 }
628
629 int main(int argc, const char **argv)
630 {
631         static const char *null_action_argv[] = {NULL};
632         static struct poptOption popt_help_options[] = {
633                 { NULL,    '\0', POPT_ARG_CALLBACK, help, 0, NULL,                         NULL },
634                 { "help",  '?',  POPT_ARG_NONE,     NULL, 0, N_("Show this help message"), NULL },
635                 { "usage", '\0', POPT_ARG_NONE,     NULL, 0, N_("Display brief usage"),    NULL },
636                 { "version",'V', POPT_ARG_NONE,     NULL, 0, N_("Print package version"),  NULL },
637                 POPT_TABLEEND
638         };
639         static struct poptOption popt_basic_options[] = {
640                 { NULL,    '\0', POPT_ARG_CALLBACK, basic_options_cb, 0, NULL, NULL },
641 #define ARG(A, B, C, D, E, F, G, H) { A, B, C, NULL, A ## _ID, D, E },
642 #include "integritysetup_arg_list.h"
643 #undef ARG
644                 POPT_TABLEEND
645         };
646         static struct poptOption popt_options[] = {
647                 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, popt_help_options, 0, N_("Help options:"), NULL },
648                 { NULL, '\0', POPT_ARG_INCLUDE_TABLE, popt_basic_options, 0, NULL, NULL },
649                 POPT_TABLEEND
650         };
651         poptContext popt_context;
652         struct action_type *action;
653         const char *aname;
654         int r;
655
656         crypt_set_log_callback(NULL, tool_log, &log_parms);
657
658         setlocale(LC_ALL, "");
659         bindtextdomain(PACKAGE, LOCALEDIR);
660         textdomain(PACKAGE);
661
662         popt_context = poptGetContext("integrity", argc, argv, popt_options, 0);
663         poptSetOtherOptionHelp(popt_context,
664                                _("[OPTION...] <action> <action-specific>"));
665
666
667         while ((r = poptGetNextOpt(popt_context)) >= 0) {
668         }
669
670         if (r < -1)
671                 usage(popt_context, EXIT_FAILURE, poptStrerror(r),
672                       poptBadOption(popt_context, POPT_BADOPTION_NOALIAS));
673
674         if (!(aname = poptGetArg(popt_context)))
675                 usage(popt_context, EXIT_FAILURE, _("Argument <action> missing."),
676                       poptGetInvocationName(popt_context));
677
678         action_argc = 0;
679         action_argv = poptGetArgs(popt_context);
680         /* Make return values of poptGetArgs more consistent in case of remaining argc = 0 */
681         if (!action_argv)
682                 action_argv = null_action_argv;
683
684         /* Count args, somewhat unnice, change? */
685         while (action_argv[action_argc] != NULL)
686                 action_argc++;
687
688         /* Handle aliases */
689         if (!strcmp(aname, "create") && action_argc > 1) {
690                 /* create command had historically switched arguments */
691                 if (action_argv[0] && action_argv[1]) {
692                         const char *tmp = action_argv[0];
693                         action_argv[0] = action_argv[1];
694                         action_argv[1] = tmp;
695                 }
696                 aname = "open";
697         } else if (!strcmp(aname, "remove")) {
698                 aname = "close";
699         }
700
701         for (action = action_types; action->type; action++)
702                 if (strcmp(action->type, aname) == 0)
703                         break;
704
705         if (!action->type)
706                 usage(popt_context, EXIT_FAILURE, _("Unknown action."),
707                       poptGetInvocationName(popt_context));
708
709         if (action_argc < action->required_action_argc) {
710                 char buf[128];
711                 if (snprintf(buf, 128,_("%s: requires %s as arguments"), action->type, action->arg_desc) < 0)
712                         buf[0] ='\0';
713                 usage(popt_context, EXIT_FAILURE, buf,
714                       poptGetInvocationName(popt_context));
715         }
716
717         tools_check_args(action->type, tool_core_args, ARRAY_SIZE(tool_core_args), popt_context);
718
719         if (ARG_SET(OPT_INTEGRITY_KEY_FILE_ID) != ARG_SET(OPT_INTEGRITY_KEY_SIZE_ID))
720                 usage(popt_context, EXIT_FAILURE, _("Both key file and key size options must be specified."),
721                       poptGetInvocationName(popt_context));
722
723         if (ARG_SET(OPT_JOURNAL_INTEGRITY_KEY_FILE_ID) != ARG_SET(OPT_JOURNAL_INTEGRITY_KEY_SIZE_ID))
724                 usage(popt_context, EXIT_FAILURE, _("Both journal integrity key file and key size options must be specified."),
725                       poptGetInvocationName(popt_context));
726         if (!ARG_SET(OPT_JOURNAL_INTEGRITY_ID) && ARG_SET(OPT_JOURNAL_INTEGRITY_KEY_FILE_ID))
727                 usage(popt_context, EXIT_FAILURE, _("Journal integrity algorithm must be specified if journal integrity key is used."),
728                       poptGetInvocationName(popt_context));
729
730         if (ARG_SET(OPT_JOURNAL_CRYPT_KEY_FILE_ID) != ARG_SET(OPT_JOURNAL_CRYPT_KEY_SIZE_ID))
731                 usage(popt_context, EXIT_FAILURE, _("Both journal encryption key file and key size options must be specified."),
732                       poptGetInvocationName(popt_context));
733         if (!ARG_SET(OPT_JOURNAL_CRYPT_ID) && ARG_SET(OPT_JOURNAL_CRYPT_KEY_FILE_ID))
734                 usage(popt_context, EXIT_FAILURE, _("Journal encryption algorithm must be specified if journal encryption key is used."),
735                       poptGetInvocationName(popt_context));
736
737         if (ARG_SET(OPT_INTEGRITY_RECOVERY_MODE_ID) && ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID))
738                 usage(popt_context, EXIT_FAILURE, _("Recovery and bitmap mode options are mutually exclusive."),
739                       poptGetInvocationName(popt_context));
740
741         if (ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID) &&
742             (ARG_SET(OPT_JOURNAL_INTEGRITY_KEY_FILE_ID) ||
743              ARG_SET(OPT_JOURNAL_CRYPT_ID) || ARG_SET(OPT_JOURNAL_WATERMARK_ID) ||
744              ARG_SET(OPT_JOURNAL_COMMIT_TIME_ID)))
745                 usage(popt_context, EXIT_FAILURE, _("Journal options cannot be used in bitmap mode."),
746                       poptGetInvocationName(popt_context));
747
748         if (!ARG_SET(OPT_INTEGRITY_BITMAP_MODE_ID) &&
749             (ARG_SET(OPT_BITMAP_FLUSH_TIME_ID) || ARG_SET(OPT_BITMAP_SECTORS_PER_BIT_ID)))
750                 usage(popt_context, EXIT_FAILURE, _("Bitmap options can be used only in bitmap mode."),
751                       poptGetInvocationName(popt_context));
752
753         if (ARG_SET(OPT_CANCEL_DEFERRED_ID) && ARG_SET(OPT_DEFERRED_ID))
754                 usage(popt_context, EXIT_FAILURE,
755                       _("Options --cancel-deferred and --deferred cannot be used at the same time."),
756                       poptGetInvocationName(popt_context));
757
758         if (ARG_SET(OPT_DEBUG_ID)) {
759                 crypt_set_debug_level(CRYPT_DEBUG_ALL);
760                 dbg_version_and_cmd(argc, argv);
761         }
762
763         r = run_action(action);
764         tools_cleanup();
765         poptFreeContext(popt_context);
766         return r;
767 }