xref: /dragonfly/sbin/tcplay/main.c (revision cfd1aba3)
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #include <getopt.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <inttypes.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <signal.h>
38 #include <time.h>
39 
40 #include "tcplay.h"
41 
42 #ifndef SIGINFO
43 #define SIGINFO SIGUSR1
44 #endif
45 
46 #define FLAG_LONG_FDE		0xff01
47 #define FLAG_LONG_USE_BACKUP	0xff02
48 #define FLAG_LONG_MOD		0xff04
49 #define FLAG_LONG_MOD_KF	0xff08
50 #define FLAG_LONG_MOD_PRF	0xff10
51 #define FLAG_LONG_MOD_NONE	0xff20
52 #define FLAG_LONG_MOD_TO_FILE	0xff40
53 #define FLAG_LONG_USE_HDR_FILE	0xfe01
54 #define FLAG_LONG_USE_HHDR_FILE	0xfe02
55 #define FLAG_LONG_NO_RETRIES	0xfabc
56 
57 
58 static
59 void
60 sig_handler(int sig)
61 {
62 	if ((sig == SIGUSR1 || sig == SIGINFO) && (summary_fn != NULL))
63 		summary_fn();
64 }
65 
66 static
67 void
68 usage(void)
69 {
70 	fprintf(stderr,
71 	    "usage: tcplay -c -d device [-g] [-z] [-w] [-a pbkdf_hash] [-b cipher]\n"
72 	    "              [-f keyfile_hidden] [-k keyfile] [-x pbkdf_hash] [-y cipher]\n"
73 	    "       tcplay -i -d device [-e] [-f keyfile_hidden] [-k keyfile]\n"
74 	    "              [-s system_device] [--fde] [--use-backup]\n"
75 	    "              [--use-hdr-file=hdr_file] [--use-hidden-hdr-file=hdr_file]\n"
76 	    "       tcplay -m mapping -d device [-e] [-f keyfile_hidden] [-k keyfile]\n"
77 	    "              [-s system_device] [--fde] [--use-backup] [--allow-trim]\n"
78 	    "              [--use-hdr-file=hdr_file] [--use-hidden-hdr-file=hdr_file]\n"
79 	    "       tcplay --modify -d device [-k keyfile] [--new-keyfile=keyfile]\n"
80 	    "              [--new-pbkdf-prf=pbkdf_hash] [-s system_device] [--fde]\n"
81 	    "              [--use-backup] [--save-hdr-to-file=hdr_file] [-w]\n"
82 	    "              [--use-hdr-file=hdr_file] [--use-hidden-hdr-file=hdr_file]\n"
83 	    "       tcplay --modify -d device [-k keyfile] --restore-from-backup-hdr [-w]\n"
84 	    "       tcplay -j mapping\n"
85 	    "       tcplay -u mapping\n"
86 	    "       tcplay -h | -v\n"
87 	    "\n"
88 	    "Valid commands are:\n"
89 	    " -c, --create\n"
90 	    "\t Creates a new TC volume on the device specified by -d or --device.\n"
91 	    " -h, --help\n"
92 	    "\t Print help message and exit.\n"
93 	    " -i, --info\n"
94 	    "\t Gives information about the TC volume specified by -d or --device.\n"
95 	    " -j <mapping name>, --info-mapped=<mapping name>\n"
96 	    "\t Gives information about the mapped TC volume under the given mapping.\n"
97 	    " -m <mapping name>, --map=<mapping name>\n"
98 	    "\t Creates a dm-crypt mapping with the given name for the device\n"
99 	    "\t specified by -d or --device.\n"
100 	    " -u <mapping name>, --unmap=<mapping name>\n"
101 	    "\t Removes a dm-crypt mapping with the given name.\n"
102 	    " --modify\n"
103 	    "\t Changes the volume's passphrase, keyfile and optionally the hashing\n"
104 	    "\t function used for the PBKDF password derivation.\n"
105 	    " -v, --version\n"
106 	    "\t Print version message and exit.\n"
107 	    "\n"
108 	    "Valid options for --create are:\n"
109 	    " -a <pbkdf prf algorithm>, --pbkdf-prf=<pbkdf prf algorithm>\n"
110 	    "\t Specifies which hashing function to use for the PBKDF password\n"
111 	    "\t derivation when creating a new volume.\n"
112 	    "\t To see valid options, specify '-a help'.\n"
113 	    " -b <cipher>, --cipher=<cipher>\n"
114 	    "\t Specifies which cipher to use when creating a new TC volume.\n"
115 	    "\t To see valid options, specify '-b help'.\n"
116 	    " -g, --hidden\n"
117 	    "\t Specifies that the newly created volume will contain a hidden volume.\n"
118 	    " -x <pbkdf prf algorithm>, --pbkdf-prf=<pbkdf prf algorithm>\n"
119 	    "\t Specifies which hashing function to use for the PBKDF password\n"
120 	    "\t derivation when creating a new hidden volume.  By default, the\n"
121 	    "\t same as for the outer volume will be used.\n"
122 	    "\t To see valid options, specify '-x help'.\n"
123 	    " -y <cipher>, --cipher=<cipher>\n"
124 	    "\t Specifies which cipher to use when creating a new hidden volume.\n"
125 	    "\t By default, the same as for the outer volume will be used.\n"
126 	    "\t To see valid options, specify '-y help'.\n"
127 	    " -z, --insecure-erase\n"
128 	    "\t Skips the erase of the disk. Possible security hazard.\n"
129 	    " -w, --weak-keys\n"
130 	    "\t Uses a weak source of entropy (urandom) for key material.\n"
131 	    "\t WARNING: This is a REALLY REALLY bad idea for anything but\n"
132 	    "\t testing.\n"
133 	    "\n"
134 	    "Valid options for --modify are:\n"
135 	    " --new-keyfile=<key file>\n"
136 	    "\t Specifies a key file to use for the password derivation, when\n"
137 	    "\t re-encrypting the header, can appear multiple times.\n"
138 	    " --new-pbkdf-prf=<pbkdf prf algorithm>\n"
139 	    "\t Specifies which hashing function to use for the PBKDF password\n"
140 	    "\t derivation when re-encrypting the header.\n"
141 	    "\t To see valid options, specify '-a help'.\n"
142 	    " -s <disk path>, --system-encryption=<disk path>\n"
143 	    "\t Specifies that the disk (e.g. /dev/da0) is using system encryption.\n"
144 	    " --fde\n"
145 	    "\t Specifies that the disk (e.g. /dev/da0) is using full disk encryption.\n"
146 	    " --use-backup\n"
147 	    "\t Uses the backup headers (at the end of the volume) instead of the\n"
148 	    "\t primary headers. Both normal and backup headers will be modified!\n"
149 	    "\t This is useful when your primary headers have been corrupted.\n"
150 	    " --use-hdr-file=<header file>\n"
151 	    "\t Use the header in the specified file instead of the main header on the\n"
152 	    "\t disk as source for the modify operation.\n"
153 	    " --use-hidden-hdr-file=<header file>\n"
154 	    "\t Use the header in the specified file instead of the hidden header on the\n"
155 	    "\t disk as source for the modify operation.\n"
156 	    " --restore-from-backup-hdr\n"
157 	    "\t Implies --use-backup, no new PBKDF hashing function, no new keyfiles\n"
158 	    "\t and no new passphrase.\n"
159 	    "\t In other words, this will simply restore both headers from the backup\n"
160 	    "\t header. This option cannot be used to restore from a backup header file.\n"
161 	    " -w, --weak-keys\n"
162 	    "\t Uses a weak source of entropy (urandom) for salt material. The\n"
163 	    "\t key material is not affected, as the master keys are kept intact.\n"
164 	    "\t WARNING: This is a bad idea for anything but testing.\n"
165 	    " --save-hdr-backup=<header file>\n"
166 	    "\t Saves the modified header in the specified file instead of updating\n"
167 	    "\t the header files on disk.\n"
168 	    "\n"
169 	    "Valid options for --info and --map are:\n"
170 	    " -e, --protect-hidden\n"
171 	    "\t Protect a hidden volume when mounting the outer volume.\n"
172 	    " -s <disk path>, --system-encryption=<disk path>\n"
173 	    "\t Specifies that the disk (e.g. /dev/da0) is using system encryption.\n"
174 	    " -t, --allow-trim\n"
175 	    "\t Allow discards (TRIM command) on mapped volume.\n"
176 	    " --fde\n"
177 	    "\t Specifies that the disk (e.g. /dev/da0) is using full disk encryption.\n"
178 	    " --use-backup\n"
179 	    "\t Uses the backup headers (at the end of the volume) instead of the\n"
180 	    "\t primary headers.\n"
181 	    "\t This is useful when your primary headers have been corrupted.\n"
182 	    " --use-hdr-file=<header file>\n"
183 	    "\t Use the header in the specified file instead of the main header on the\n"
184 	    "\t disk.\n"
185 	    " --use-hidden-hdr-file=<header file>\n"
186 	    "\t Use the header in the specified file instead of the hidden header on the\n"
187 	    "\t disk.\n"
188 	    "\n"
189 	    "Valid options common to all commands are:\n"
190 	    " -d <device path>, --device=<device path>\n"
191 	    "\t Specifies the path to the volume to operate on (e.g. /dev/da0s1).\n"
192 	    " -f <key file>, --keyfile-hidden=<key file>\n"
193 	    "\t Specifies a key file to use for the hidden volume password derivation.\n"
194 	    "\t This option is only valid in combination with -e, --protect-hidden\n"
195 	    "\t or -g, --hidden.\n"
196 	    " -k <key file>, --keyfile=<key file>\n"
197 	    "\t Specifies a key file to use for the password derivation, can appear\n"
198 	    "\t multiple times.\n"
199 	    );
200 
201 	exit(EXIT_FAILURE);
202 }
203 
204 static struct option longopts[] = {
205 	{ "create",		no_argument,		NULL, 'c' },
206 	{ "cipher",		required_argument,	NULL, 'b' },
207 	{ "cipher-hidden",	required_argument,	NULL, 'y' },
208 	{ "hidden",		no_argument,		NULL, 'g' },
209 	{ "pbkdf-prf",		required_argument,	NULL, 'a' },
210 	{ "pbkdf-prf-hidden",	required_argument,	NULL, 'x' },
211 	{ "info",		no_argument,		NULL, 'i' },
212 	{ "info-mapped",	required_argument,	NULL, 'j' },
213 	{ "map",		required_argument,	NULL, 'm' },
214 	{ "keyfile",		required_argument,	NULL, 'k' },
215 	{ "keyfile-hidden",	required_argument,	NULL, 'f' },
216 	{ "protect-hidden",	no_argument,		NULL, 'e' },
217 	{ "device",		required_argument,	NULL, 'd' },
218 	{ "system-encryption",	required_argument,	NULL, 's' },
219 	{ "allow-trim",		no_argument,		NULL, 't' },
220 	{ "fde",		no_argument,		NULL, FLAG_LONG_FDE },
221 	{ "use-backup",		no_argument,		NULL, FLAG_LONG_USE_BACKUP },
222 	{ "use-hdr-file",	required_argument,	NULL, FLAG_LONG_USE_HDR_FILE },
223 	{ "use-hidden-hdr-file",required_argument,	NULL, FLAG_LONG_USE_HHDR_FILE },
224 	{ "modify",		no_argument,		NULL, FLAG_LONG_MOD },
225 	{ "new-keyfile",	required_argument,	NULL, FLAG_LONG_MOD_KF },
226 	{ "new-pbkdf-prf",	required_argument,	NULL, FLAG_LONG_MOD_PRF },
227 	{ "restore-from-backup-hdr", no_argument,	NULL, FLAG_LONG_MOD_NONE },
228 	{ "save-hdr-backup",	required_argument,	NULL, FLAG_LONG_MOD_TO_FILE },
229 	{ "unmap",		required_argument,	NULL, 'u' },
230 	{ "version",		no_argument,		NULL, 'v' },
231 	{ "weak-keys",		no_argument,		NULL, 'w' },
232 	{ "insecure-erase",	no_argument,		NULL, 'z' },
233 	{ "help",		no_argument,		NULL, 'h' },
234 	{ "no-retries",         no_argument,            NULL, FLAG_LONG_NO_RETRIES },
235 	{ NULL,			0,			NULL, 0   },
236 };
237 
238 #define _set_str_opt(opt) \
239 	do {									\
240 		if ((opts->opt = strdup_safe_mem(optarg)) == NULL) {		\
241 			fprintf(stderr, "Could not allocate safe mem.\n");	\
242 			exit(EXIT_FAILURE);					\
243 		}								\
244 	} while(0)
245 
246 int
247 main(int argc, char *argv[])
248 {
249 	struct tcplay_opts *opts;
250 	int ch, error;
251 	int info_vol = 0, map_vol = 0,
252 	    unmap_vol = 0, info_map = 0,
253 	    create_vol = 0, modify_vol = 0;
254 
255 	if ((error = tc_play_init()) != 0) {
256 		fprintf(stderr, "Initialization failed, exiting.");
257 		exit(EXIT_FAILURE);
258 	}
259 
260 	atexit(check_and_purge_safe_mem);
261 	signal(SIGUSR1, sig_handler);
262 	signal(SIGINFO, sig_handler);
263 
264 	if ((opts = opts_init()) == NULL) {
265 		fprintf(stderr, "Initialization failed (opts), exiting.");
266 		exit(EXIT_FAILURE);
267 	}
268 
269 	opts->interactive = 1;
270 
271 	while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:s:tu:vwx:y:z",
272 	    longopts, NULL)) != -1) {
273 		switch(ch) {
274 		case 'a':
275 			if (opts->prf_algo != NULL)
276 				usage();
277 			if ((opts->prf_algo = check_prf_algo(optarg, 0)) == NULL) {
278 				if (strcmp(optarg, "help") == 0)
279 					exit(EXIT_SUCCESS);
280 				else
281 					usage();
282 				/* NOT REACHED */
283 			}
284 			break;
285 		case 'b':
286 			if (opts->cipher_chain != NULL)
287 				usage();
288 			if ((opts->cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
289 				if (strcmp(optarg, "help") == 0)
290 					exit(EXIT_SUCCESS);
291 				else
292 					usage();
293 				/* NOT REACHED */
294 			}
295 			break;
296 		case 'c':
297 			create_vol = 1;
298 			break;
299 		case 'd':
300 			_set_str_opt(dev);
301 			break;
302 		case 'e':
303 			opts->protect_hidden = 1;
304 			break;
305 		case 'f':
306 			if ((error = opts_add_keyfile_hidden(opts, optarg)) != 0) {
307 				fprintf(stderr, "Could not add keyfile: %s\n", optarg);
308 				exit(EXIT_FAILURE);
309 			}
310 			break;
311 		case 'g':
312 			opts->hidden = 1;
313 			break;
314 		case 'i':
315 			info_vol = 1;
316 			break;
317 		case 'j':
318 			info_map = 1;
319 			_set_str_opt(map_name);
320 			break;
321 		case 'k':
322 			if ((error = opts_add_keyfile(opts, optarg)) != 0) {
323 				fprintf(stderr, "Could not add keyfile: %s\n", optarg);
324 				exit(EXIT_FAILURE);
325 			}
326 			break;
327 		case 'm':
328 			map_vol = 1;
329 			_set_str_opt(map_name);
330 			break;
331 		case 's':
332 			opts->flags |= TC_FLAG_SYS;
333 			_set_str_opt(sys_dev);
334 			break;
335 		case 't':
336 			opts->flags |= TC_FLAG_ALLOW_TRIM;
337 			break;
338 		case 'u':
339 			unmap_vol = 1;
340 			_set_str_opt(map_name);
341 			break;
342 		case 'v':
343 			printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER);
344 			exit(EXIT_SUCCESS);
345 			/* NOT REACHED */
346 		case 'w':
347 			fprintf(stderr, "WARNING: Using urandom as source of "
348 			    "entropy for key material is a really bad idea.\n");
349 			opts->weak_keys_and_salt = 1;
350 			break;
351 		case 'x':
352 			if (opts->h_prf_algo != NULL)
353 				usage();
354 			if ((opts->h_prf_algo = check_prf_algo(optarg, 0)) == NULL) {
355 				if (strcmp(optarg, "help") == 0)
356 					exit(EXIT_SUCCESS);
357 				else
358 					usage();
359 				/* NOT REACHED */
360 			}
361 			break;
362 		case 'y':
363 			if (opts->h_cipher_chain != NULL)
364 				usage();
365 			if ((opts->h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
366 				if (strcmp(optarg, "help") == 0)
367 					exit(EXIT_SUCCESS);
368 				else
369 					usage();
370 				/* NOT REACHED */
371 			}
372 			break;
373 		case 'z':
374 			opts->secure_erase = 0;
375 			break;
376 		case FLAG_LONG_FDE:
377 			opts->flags |= TC_FLAG_FDE;
378 			break;
379 		case FLAG_LONG_USE_BACKUP:
380 			opts->flags |= TC_FLAG_BACKUP;
381 			break;
382 		case FLAG_LONG_USE_HDR_FILE:
383 			opts->flags |= TC_FLAG_HDR_FROM_FILE;
384 			_set_str_opt(hdr_file_in);
385 			break;
386 		case FLAG_LONG_USE_HHDR_FILE:
387 			opts->flags |= TC_FLAG_H_HDR_FROM_FILE;
388 			_set_str_opt(h_hdr_file_in);
389 			break;
390 		case FLAG_LONG_MOD:
391 			modify_vol = 1;
392 			break;
393 		case FLAG_LONG_MOD_KF:
394 			if ((error = opts_add_keyfile_new(opts, optarg)) != 0) {
395 				fprintf(stderr, "Could not add keyfile: %s\n", optarg);
396 				exit(EXIT_FAILURE);
397 			}
398 			break;
399 		case FLAG_LONG_MOD_PRF:
400 			if (opts->new_prf_algo != NULL)
401 				usage();
402 			if ((opts->new_prf_algo = check_prf_algo(optarg, 0)) == NULL) {
403 				if (strcmp(optarg, "help") == 0)
404 					exit(EXIT_SUCCESS);
405 				else
406 					usage();
407 				/* NOT REACHED */
408 			}
409 			break;
410 		case FLAG_LONG_MOD_NONE:
411 			opts->new_prf_algo = NULL;
412 			opts->flags |= TC_FLAG_ONLY_RESTORE;
413 			opts->flags |= TC_FLAG_BACKUP;
414 			break;
415 		case FLAG_LONG_MOD_TO_FILE:
416 			opts->flags |= TC_FLAG_SAVE_TO_FILE;
417 			_set_str_opt(hdr_file_out);
418 			break;
419 		case FLAG_LONG_NO_RETRIES:
420 			opts->retries = 1;
421 			break;
422 		case 'h':
423 		case '?':
424 		default:
425 			usage();
426 			/* NOT REACHED */
427 		}
428 	}
429 
430 	argc -= optind;
431 	argv += optind;
432 
433 	/* Check arguments */
434 	if (!(((map_vol || info_vol || create_vol || modify_vol) && opts->dev != NULL) ||
435 	    ((unmap_vol || info_map) && opts->map_name != NULL)) ||
436 	    (TC_FLAG_SET(opts->flags, SYS) && TC_FLAG_SET(opts->flags, FDE)) ||
437 	    (map_vol + info_vol + create_vol + unmap_vol + info_map + modify_vol > 1) ||
438 	    (opts->hidden && !create_vol) ||
439 	    (TC_FLAG_SET(opts->flags, SYS) && (opts->sys_dev == NULL)) ||
440 	    (TC_FLAG_SET(opts->flags, ONLY_RESTORE) && (opts->n_newkeyfiles > 0 || opts->new_prf_algo != NULL)) ||
441 	    (TC_FLAG_SET(opts->flags, BACKUP) && (opts->sys_dev != NULL || TC_FLAG_SET(opts->flags, FDE))) ||
442 	    (map_vol && (opts->map_name == NULL)) ||
443 	    (unmap_vol && (opts->map_name == NULL)) ||
444 	    (!modify_vol && opts->n_newkeyfiles > 0) ||
445 	    (!modify_vol && opts->new_prf_algo != NULL) ||
446 	    (!modify_vol && TC_FLAG_SET(opts->flags, ONLY_RESTORE)) ||
447 	    (!modify_vol && TC_FLAG_SET(opts->flags, SAVE_TO_FILE)) ||
448 	    (!(opts->protect_hidden || create_vol) && opts->n_hkeyfiles > 0)) {
449 		usage();
450 		/* NOT REACHED */
451 	}
452 
453 	/* Create a new volume */
454 	if (create_vol) {
455 		error = create_volume(opts);
456 		if (error) {
457 			tc_log(1, "could not create new volume on %s\n", opts->dev);
458 		}
459 	} else if (info_map) {
460 		error = info_mapped_volume(opts);
461 	} else if (info_vol) {
462 		error = info_volume(opts);
463 	} else if (map_vol) {
464 		error = map_volume(opts);
465 	} else if (unmap_vol) {
466 		error = dm_teardown(opts->map_name, NULL);
467 	} else if (modify_vol) {
468 		error = modify_volume(opts);
469 	}
470 
471 	return error;
472 }
473 
474 #undef _set_str_opt
475