veritysetup.c 15.6 KB
Newer Older
Milan Broz's avatar
Milan Broz committed
1 2 3
/*
 * veritysetup - setup cryptographic volumes for dm-verity
 *
4 5
 * Copyright (C) 2012-2016, Red Hat, Inc. All rights reserved.
 * Copyright (C) 2012-2016, Milan Broz
Milan Broz's avatar
Milan Broz committed
6 7 8
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
9 10
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
Milan Broz's avatar
Milan Broz committed
11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

22
#include "cryptsetup.h"
Milan Broz's avatar
Milan Broz committed
23

24
#define PACKAGE_VERITY "veritysetup"
Milan Broz's avatar
Milan Broz committed
25

Milan Broz's avatar
Milan Broz committed
26
static int use_superblock = 1;
Milan Broz's avatar
Milan Broz committed
27 28

static const char *hash_algorithm = NULL;
29
static int hash_type = 1;
Milan Broz's avatar
Milan Broz committed
30 31
static int data_block_size = DEFAULT_VERITY_DATA_BLOCK;
static int hash_block_size = DEFAULT_VERITY_HASH_BLOCK;
Milan Broz's avatar
Milan Broz committed
32
static uint64_t data_blocks = 0;
Milan Broz's avatar
Milan Broz committed
33
static const char *salt_string = NULL;
34
static uint64_t hash_offset = 0;
35
static const char *opt_uuid = NULL;
36 37 38
static int opt_restart_on_corruption = 0;
static int opt_ignore_corruption = 0;
static int opt_ignore_zero_blocks = 0;
Milan Broz's avatar
Milan Broz committed
39

40
static int opt_version_mode = 0;
Milan Broz's avatar
Milan Broz committed
41

42 43 44
static const char **action_argv;
static int action_argc;

45 46 47 48
static int _prepare_format(struct crypt_params_verity *params,
			   const char *data_device,
			   uint32_t flags)
{
49 50
	char *salt = NULL;
	int len;
51 52 53 54 55 56 57 58

	params->hash_name = hash_algorithm ?: DEFAULT_VERITY_HASH;
	params->data_device = data_device;

	if (salt_string && !strcmp(salt_string, "-")) {
		params->salt_size = 0;
		params->salt = NULL;
	} else if (salt_string) {
59
		len = crypt_hex_to_bytes(salt_string, &salt, 0);
60
		if (len < 0) {
61
			log_err(_("Invalid salt string specified.\n"));
62
			return -EINVAL;
63
		}
64 65 66
		params->salt_size = len;
		params->salt = salt;
	} else {
67
		params->salt_size = DEFAULT_VERITY_SALT_SIZE;
68 69
		params->salt = NULL;
	}
70 71 72 73

	params->data_block_size = data_block_size;
	params->hash_block_size = hash_block_size;
	params->data_size = data_blocks;
74
	params->hash_area_offset = hash_offset;
75
	params->hash_type = hash_type;
76 77 78 79 80
	params->flags = flags;

	return 0;
}

81
static int action_format(int arg)
Milan Broz's avatar
Milan Broz committed
82
{
83 84
	struct crypt_device *cd = NULL;
	struct crypt_params_verity params = {};
85
	uint32_t flags = CRYPT_VERITY_CREATE_HASH;
86
	int r;
Milan Broz's avatar
Milan Broz committed
87

88
	/* Try to create hash image if doesn't exist */
89 90 91 92 93 94
	r = open(action_argv[1], O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
	if (r < 0 && errno != EEXIST) {
		log_err(_("Cannot create hash image %s for writing.\n"), action_argv[1]);
		return -EINVAL;
	} else if (r >= 0) {
		log_dbg("Created hash image %s.", action_argv[1]);
95 96 97
		close(r);
	}

98 99
	if ((r = crypt_init(&cd, action_argv[1])))
		goto out;
Milan Broz's avatar
Milan Broz committed
100

101
	if (!use_superblock)
102 103 104 105 106
		flags |= CRYPT_VERITY_NO_HEADER;

	r = _prepare_format(&params, action_argv[0], flags);
	if (r < 0)
		goto out;
107

108
	r = crypt_format(cd, CRYPT_VERITY, NULL, NULL, opt_uuid, NULL, 0, &params);
109
	if (!r)
110 111
		crypt_dump(cd);
out:
112
	crypt_free(cd);
113
	free(CONST_CAST(char*)params.salt);
114 115 116
	return r;
}

117 118 119 120 121
static int _activate(const char *dm_device,
		      const char *data_device,
		      const char *hash_device,
		      const char *root_hash,
		      uint32_t flags)
Milan Broz's avatar
Milan Broz committed
122
{
123 124 125
	struct crypt_device *cd = NULL;
	struct crypt_params_verity params = {};
	uint32_t activate_flags = CRYPT_ACTIVATE_READONLY;
126
	char *root_hash_bytes = NULL;
127
	ssize_t hash_size;
128
	int r;
Milan Broz's avatar
Milan Broz committed
129

130 131
	if ((r = crypt_init(&cd, hash_device)))
		goto out;
Milan Broz's avatar
Milan Broz committed
132

133 134 135 136 137 138 139
	if (opt_ignore_corruption)
		activate_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION;
	if (opt_restart_on_corruption)
		activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
	if (opt_ignore_zero_blocks)
		activate_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS;

140
	if (use_superblock) {
141
		params.flags = flags;
142
		params.hash_area_offset = hash_offset;
143
		r = crypt_load(cd, CRYPT_VERITY, &params);
144 145 146 147 148
	} else {
		r = _prepare_format(&params, data_device, flags | CRYPT_VERITY_NO_HEADER);
		if (r < 0)
			goto out;
		r = crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params);
149 150 151 152 153 154 155
	}
	if (r < 0)
		goto out;
	r = crypt_set_data_device(cd, data_device);
	if (r < 0)
		goto out;

156
	hash_size = crypt_get_volume_key_size(cd);
157
	if (crypt_hex_to_bytes(root_hash, &root_hash_bytes, 0) != hash_size) {
158
		log_err(_("Invalid root hash string specified.\n"));
159 160 161
		r = -EINVAL;
		goto out;
	}
162 163
	r = crypt_activate_by_volume_key(cd, dm_device,
					 root_hash_bytes,
164
					 hash_size,
165 166 167
					 activate_flags);
out:
	crypt_free(cd);
168
	free(root_hash_bytes);
169
	free(CONST_CAST(char*)params.salt);
170
	return r;
Milan Broz's avatar
Milan Broz committed
171 172
}

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
static int action_create(int arg)
{
	return _activate(action_argv[0],
			 action_argv[1],
			 action_argv[2],
			 action_argv[3], 0);
}

static int action_verify(int arg)
{
	return _activate(NULL,
			 action_argv[0],
			 action_argv[1],
			 action_argv[2],
			 CRYPT_VERITY_CHECK_HASH);
}

static int action_remove(int arg)
Milan Broz's avatar
Milan Broz committed
191
{
192 193
	struct crypt_device *cd = NULL;
	int r;
Milan Broz's avatar
Milan Broz committed
194

195 196 197
	r = crypt_init_by_name(&cd, action_argv[0]);
	if (r == 0)
		r = crypt_deactivate(cd, action_argv[0]);
Milan Broz's avatar
Milan Broz committed
198

199 200 201
	crypt_free(cd);
	return r;
}
Milan Broz's avatar
Milan Broz committed
202

203 204
static int action_status(int arg)
{
205 206 207
	crypt_status_info ci;
	struct crypt_active_device cad;
	struct crypt_params_verity vp = {};
208
	struct crypt_device *cd = NULL;
209 210
	struct stat st;
	char *backing_file;
211 212
	unsigned i, path = 0;
	int r = 0;
213

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
	/* perhaps a path, not a dm device name */
	if (strchr(action_argv[0], '/') && !stat(action_argv[0], &st))
		path = 1;

	ci = crypt_status(NULL, action_argv[0]);
	switch (ci) {
	case CRYPT_INVALID:
		r = -EINVAL;
		break;
	case CRYPT_INACTIVE:
		if (path)
			log_std("%s is inactive.\n", action_argv[0]);
		else
			log_std("%s/%s is inactive.\n", crypt_get_dir(), action_argv[0]);
		r = -ENODEV;
		break;
	case CRYPT_ACTIVE:
	case CRYPT_BUSY:
		if (path)
			log_std("%s is active%s.\n", action_argv[0],
				ci == CRYPT_BUSY ? " and is in use" : "");
		else
			log_std("%s/%s is active%s.\n", crypt_get_dir(), action_argv[0],
				ci == CRYPT_BUSY ? " and is in use" : "");

		r = crypt_init_by_name_and_header(&cd, action_argv[0], NULL);
		if (r < 0 || !crypt_get_type(cd))
			goto out;

		log_std("  type:        %s\n", crypt_get_type(cd));

		r = crypt_get_active_device(cd, action_argv[0], &cad);
		if (r < 0)
			goto out;

		log_std("  status:      %s\n",
			cad.flags & CRYPT_ACTIVATE_CORRUPTED ? "corrupted" : "verified");

		r = crypt_get_verity_info(cd, &vp);
		if (r < 0)
			goto out;

256
		log_std("  hash type:   %u\n", vp.hash_type);
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
		log_std("  data block:  %u\n", vp.data_block_size);
		log_std("  hash block:  %u\n", vp.hash_block_size);
		log_std("  hash name:   %s\n", vp.hash_name);
		log_std("  salt:        ");
		if (vp.salt_size)
			for(i = 0; i < vp.salt_size; i++)
				log_std("%02hhx", (const char)vp.salt[i]);
		else
			log_std("-");
		log_std("\n");

		log_std("  data device: %s\n", vp.data_device);
		if (crypt_loop_device(vp.data_device)) {
			backing_file = crypt_loop_backing_file(vp.data_device);
			log_std("  data loop:   %s\n", backing_file);
			free(backing_file);
		}
		log_std("  size:        %" PRIu64 " sectors\n", cad.size);
		log_std("  mode:        %s\n", cad.flags & CRYPT_ACTIVATE_READONLY ?
					   "readonly" : "read/write");

		log_std("  hash device: %s\n", vp.hash_device);
		if (crypt_loop_device(vp.hash_device)) {
			backing_file = crypt_loop_backing_file(vp.hash_device);
			log_std("  hash loop:   %s\n", backing_file);
			free(backing_file);
		}
		log_std("  hash offset: %" PRIu64 " sectors\n",
			vp.hash_area_offset * vp.hash_block_size / 512);
286 287 288 289 290 291 292 293

		if (cad.flags & (CRYPT_ACTIVATE_IGNORE_CORRUPTION|
				 CRYPT_ACTIVATE_RESTART_ON_CORRUPTION|
				 CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS))
			log_std("  flags:       %s%s%s\n",
				(cad.flags & CRYPT_ACTIVATE_IGNORE_CORRUPTION) ? "ignore_corruption " : "",
				(cad.flags & CRYPT_ACTIVATE_RESTART_ON_CORRUPTION) ? "restart_on_corruption " : "",
				(cad.flags & CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS) ? "ignore_zero_blocks" : "");
294 295
	}
out:
296
	crypt_free(cd);
297 298
	if (r == -ENOTSUP)
		r = 0;
299 300
	return r;
}
301

302 303 304 305 306 307 308 309 310
static int action_dump(int arg)
{
	struct crypt_device *cd = NULL;
	struct crypt_params_verity params = {};
	int r;

	if ((r = crypt_init(&cd, action_argv[0])))
		return r;

311
	params.hash_area_offset = hash_offset;
312
	r = crypt_load(cd, CRYPT_VERITY, &params);
313 314 315 316
	if (!r)
		crypt_dump(cd);
	crypt_free(cd);
	return r;
Milan Broz's avatar
Milan Broz committed
317 318
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
static struct action_type {
	const char *type;
	int (*handler)(int);
	int required_action_argc;
	const char *arg_desc;
	const char *desc;
} action_types[] = {
	{ "format",	action_format, 2, N_("<data_device> <hash_device>"),N_("format device") },
	{ "verify",	action_verify, 3, N_("<data_device> <hash_device> <root_hash>"),N_("verify device") },
	{ "create",	action_create, 4, N_("<name> <data_device> <hash_device> <root_hash>"),N_("create active device") },
	{ "remove",	action_remove, 1, N_("<name>"),N_("remove (deactivate) device") },
	{ "status",	action_status, 1, N_("<name>"),N_("show active device status") },
	{ "dump",	action_dump,   1, N_("<hash_device>"),N_("show on-disk information") },
	{ NULL, NULL, 0, NULL, NULL }
};

335 336 337 338 339
static void help(poptContext popt_context,
		 enum poptCallbackReason reason __attribute__((unused)),
		 struct poptOption *key,
		 const char *arg __attribute__((unused)),
		 void *data __attribute__((unused)))
Milan Broz's avatar
Milan Broz committed
340
{
341 342
	struct action_type *action;

343 344 345
	if (key->shortName == '?') {
		log_std("%s %s\n", PACKAGE_VERITY, PACKAGE_VERSION);
		poptPrintHelp(popt_context, stdout, 0);
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
		log_std(_("\n"
			 "<action> is one of:\n"));
		for(action = action_types; action->type; action++)
			log_std("\t%s %s - %s\n", action->type, _(action->arg_desc), _(action->desc));
		log_std(_("\n"
			 "<name> is the device to create under %s\n"
			 "<data_device> is the data device\n"
			 "<hash_device> is the device containing verification data\n"
			 "<root_hash> hash of the root node on <hash_device>\n"),
			crypt_get_dir());

		log_std(_("\nDefault compiled-in dm-verity parameters:\n"
			 "\tHash: %s, Data block (bytes): %u, "
			 "Hash block (bytes): %u, Salt size: %u, Hash format: %u\n"),
			DEFAULT_VERITY_HASH, DEFAULT_VERITY_DATA_BLOCK,
			DEFAULT_VERITY_HASH_BLOCK, DEFAULT_VERITY_SALT_SIZE,
			1);
363 364 365
		exit(EXIT_SUCCESS);
	} else
		usage(popt_context, EXIT_SUCCESS, NULL, NULL);
Milan Broz's avatar
Milan Broz committed
366 367
}

368 369 370 371 372 373 374 375 376
static int run_action(struct action_type *action)
{
	int r;

	log_dbg("Running command %s.", action->type);

	r = action->handler(0);

	show_status(r);
377
	return translate_errno(r);
Milan Broz's avatar
Milan Broz committed
378 379 380 381
}

int main(int argc, const char **argv)
{
382
	static char *popt_tmp;
Milan Broz's avatar
Milan Broz committed
383
	static const char *null_action_argv[] = {NULL};
384 385 386 387 388 389 390
	static struct poptOption popt_help_options[] = {
		{ NULL,    '\0', POPT_ARG_CALLBACK, help, 0, NULL,                         NULL },
		{ "help",  '?',  POPT_ARG_NONE,     NULL, 0, N_("Show this help message"), NULL },
		{ "usage", '\0', POPT_ARG_NONE,     NULL, 0, N_("Display brief usage"),    NULL },
		POPT_TABLEEND
	};
	static struct poptOption popt_options[] = {
391 392 393 394 395
		{ NULL,              '\0', POPT_ARG_INCLUDE_TABLE, popt_help_options, 0, N_("Help options:"), NULL },
		{ "version",         '\0', POPT_ARG_NONE, &opt_version_mode, 0, N_("Print package version"), NULL },
		{ "verbose",         'v',  POPT_ARG_NONE, &opt_verbose,      0, N_("Shows more detailed error messages"), NULL },
		{ "debug",           '\0', POPT_ARG_NONE, &opt_debug,        0, N_("Show debug messages"), NULL },
		{ "no-superblock",   0,    POPT_ARG_VAL,  &use_superblock,   0, N_("Do not use verity superblock"), NULL },
396
		{ "format",          0,    POPT_ARG_INT,  &hash_type,        0, N_("Format type (1 - normal, 0 - original Chrome OS)"), N_("number") },
397 398 399
		{ "data-block-size", 0,    POPT_ARG_INT,  &data_block_size,  0, N_("Block size on the data device"), N_("bytes") },
		{ "hash-block-size", 0,    POPT_ARG_INT,  &hash_block_size,  0, N_("Block size on the hash device"), N_("bytes") },
		{ "data-blocks",     0,    POPT_ARG_STRING, &popt_tmp,       1, N_("The number of blocks in the data file"), N_("blocks") },
400
		{ "hash-offset",     0,    POPT_ARG_STRING, &popt_tmp,       2, N_("Starting offset on the hash device"), N_("bytes") },
Milan Broz's avatar
Milan Broz committed
401
		{ "hash",            'h',  POPT_ARG_STRING, &hash_algorithm, 0, N_("Hash algorithm"), N_("string") },
402
		{ "salt",            's',  POPT_ARG_STRING, &salt_string,    0, N_("Salt"), N_("hex string") },
403
		{ "uuid",            '\0', POPT_ARG_STRING, &opt_uuid,       0, N_("UUID for device to use."), NULL },
404 405 406
		{ "restart-on-corruption", 0,POPT_ARG_NONE,&opt_restart_on_corruption, 0, N_("Restart kernel if corruption is detected"), NULL },
		{ "ignore-corruption", 0,  POPT_ARG_NONE, &opt_ignore_corruption,  0, N_("Ignore corruption, log it only"), NULL },
		{ "ignore-zero-blocks", 0, POPT_ARG_NONE, &opt_ignore_zero_blocks, 0, N_("Do not verify zeroed blocks"), NULL },
407 408
		POPT_TABLEEND
	};
409

Milan Broz's avatar
Milan Broz committed
410
	poptContext popt_context;
411
	struct action_type *action;
Milan Broz's avatar
Milan Broz committed
412
	const char *aname;
Milan Broz's avatar
Milan Broz committed
413 414
	int r;

415
	crypt_set_log_callback(NULL, tool_log, NULL);
416 417 418 419

	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
Milan Broz's avatar
Milan Broz committed
420 421

	popt_context = poptGetContext("verity", argc, argv, popt_options, 0);
422
	poptSetOtherOptionHelp(popt_context,
423
	                       _("[OPTION...] <action> <action-specific>"));
424 425 426 427 428 429

	while((r = poptGetNextOpt(popt_context)) > 0) {
		unsigned long long ull_value;
		char *endp;

		errno = 0;
430 431
		ull_value = strtoull(popt_tmp, &endp, 10);
		if (*endp || !*popt_tmp || !isdigit(*popt_tmp) ||
432 433 434 435 436 437 438 439 440
		    (errno == ERANGE && ull_value == ULLONG_MAX) ||
		    (errno != 0 && ull_value == 0))
			r = POPT_ERROR_BADNUMBER;

		switch(r) {
			case 1:
				data_blocks = ull_value;
				break;
			case 2:
441
				hash_offset = ull_value;
442 443
				break;
		}
Milan Broz's avatar
Milan Broz committed
444

445 446
		if (r < 0)
			break;
Milan Broz's avatar
Milan Broz committed
447 448 449
	}

	if (r < -1)
450 451
		usage(popt_context, EXIT_FAILURE, poptStrerror(r),
		      poptBadOption(popt_context, POPT_BADOPTION_NOALIAS));
452

453 454 455 456 457
	if (opt_version_mode) {
		log_std("%s %s\n", PACKAGE_VERITY, PACKAGE_VERSION);
		poptFreeContext(popt_context);
		exit(EXIT_SUCCESS);
	}
Milan Broz's avatar
Milan Broz committed
458

459 460 461 462 463 464 465
	if (!(aname = poptGetArg(popt_context)))
		usage(popt_context, EXIT_FAILURE, _("Argument <action> missing."),
		      poptGetInvocationName(popt_context));
	for(action = action_types; action->type; action++)
		if (strcmp(action->type, aname) == 0)
			break;
	if (!action->type)
466 467
		usage(popt_context, EXIT_FAILURE, _("Unknown action."),
		      poptGetInvocationName(popt_context));
Milan Broz's avatar
Milan Broz committed
468

469 470 471 472 473
	action_argc = 0;
	action_argv = poptGetArgs(popt_context);
	/* Make return values of poptGetArgs more consistent in case of remaining argc = 0 */
	if(!action_argv)
		action_argv = null_action_argv;
Milan Broz's avatar
Milan Broz committed
474

475 476 477
	/* Count args, somewhat unnice, change? */
	while(action_argv[action_argc] != NULL)
		action_argc++;
478

479 480 481 482
	if(action_argc < action->required_action_argc) {
		char buf[128];
		snprintf(buf, 128,_("%s: requires %s as arguments"), action->type, action->arg_desc);
		usage(popt_context, EXIT_FAILURE, buf,
483
		      poptGetInvocationName(popt_context));
Milan Broz's avatar
Milan Broz committed
484 485
	}

486 487 488 489 490 491
	if (data_block_size < 0 || hash_block_size < 0 || hash_type < 0) {
		usage(popt_context, EXIT_FAILURE,
		      _("Negative number for option not permitted."),
		      poptGetInvocationName(popt_context));
	}

492 493 494 495 496 497 498 499 500 501
	if ((opt_ignore_corruption || opt_restart_on_corruption || opt_ignore_zero_blocks) && strcmp(aname, "create"))
		usage(popt_context, EXIT_FAILURE,
		_("Option --ignore-corruption, --restart-on-corruption or --ignore-zero-blocks is allowed only for create operation.\n"),
		poptGetInvocationName(popt_context));

	if (opt_ignore_corruption && opt_restart_on_corruption)
		usage(popt_context, EXIT_FAILURE,
		_("Option --ignore-corruption and --restart-on-corruption cannot be used together.\n"),
		poptGetInvocationName(popt_context));

502 503 504
	if (opt_debug) {
		opt_verbose = 1;
		crypt_set_debug_level(-1);
505
		dbg_version_and_cmd(argc, argv);
Milan Broz's avatar
Milan Broz committed
506 507
	}

508
	r = run_action(action);
Milan Broz's avatar
Milan Broz committed
509
	poptFreeContext(popt_context);
510
	return r;
Milan Broz's avatar
Milan Broz committed
511
}