xref: /dragonfly/sbin/tcplay/main.c (revision e6d22e9b)
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] [-p] [-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] [-p] [-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 	    " -p, --prompt-passphrase\n"
173 	    "\t Immediately prompt for a passphrase even if a keyfile is supplied.\n"
174 	    " -s <disk path>, --system-encryption=<disk path>\n"
175 	    "\t Specifies that the disk (e.g. /dev/da0) is using system encryption.\n"
176 	    " -t, --allow-trim\n"
177 	    "\t Allow discards (TRIM command) on mapped volume.\n"
178 	    " --fde\n"
179 	    "\t Specifies that the disk (e.g. /dev/da0) is using full disk encryption.\n"
180 	    " --use-backup\n"
181 	    "\t Uses the backup headers (at the end of the volume) instead of the\n"
182 	    "\t primary headers.\n"
183 	    "\t This is useful when your primary headers have been corrupted.\n"
184 	    " --use-hdr-file=<header file>\n"
185 	    "\t Use the header in the specified file instead of the main header on the\n"
186 	    "\t disk.\n"
187 	    " --use-hidden-hdr-file=<header file>\n"
188 	    "\t Use the header in the specified file instead of the hidden header on the\n"
189 	    "\t disk.\n"
190 	    "\n"
191 	    "Valid options common to all commands are:\n"
192 	    " -d <device path>, --device=<device path>\n"
193 	    "\t Specifies the path to the volume to operate on (e.g. /dev/da0s1).\n"
194 	    " -f <key file>, --keyfile-hidden=<key file>\n"
195 	    "\t Specifies a key file to use for the hidden volume password derivation.\n"
196 	    "\t This option is only valid in combination with -e, --protect-hidden\n"
197 	    "\t or -g, --hidden.\n"
198 	    " -k <key file>, --keyfile=<key file>\n"
199 	    "\t Specifies a key file to use for the password derivation, can appear\n"
200 	    "\t multiple times.\n"
201 	    );
202 
203 	exit(EXIT_FAILURE);
204 }
205 
206 static struct option longopts[] = {
207 	{ "create",		no_argument,		NULL, 'c' },
208 	{ "cipher",		required_argument,	NULL, 'b' },
209 	{ "cipher-hidden",	required_argument,	NULL, 'y' },
210 	{ "hidden",		no_argument,		NULL, 'g' },
211 	{ "pbkdf-prf",		required_argument,	NULL, 'a' },
212 	{ "pbkdf-prf-hidden",	required_argument,	NULL, 'x' },
213 	{ "info",		no_argument,		NULL, 'i' },
214 	{ "info-mapped",	required_argument,	NULL, 'j' },
215 	{ "map",		required_argument,	NULL, 'm' },
216 	{ "keyfile",		required_argument,	NULL, 'k' },
217 	{ "keyfile-hidden",	required_argument,	NULL, 'f' },
218 	{ "protect-hidden",	no_argument,		NULL, 'e' },
219 	{ "device",		required_argument,	NULL, 'd' },
220 	{ "prompt-passphrase",	no_argument,		NULL, 'p' },
221 	{ "system-encryption",	required_argument,	NULL, 's' },
222 	{ "allow-trim",		no_argument,		NULL, 't' },
223 	{ "fde",		no_argument,		NULL, FLAG_LONG_FDE },
224 	{ "use-backup",		no_argument,		NULL, FLAG_LONG_USE_BACKUP },
225 	{ "use-hdr-file",	required_argument,	NULL, FLAG_LONG_USE_HDR_FILE },
226 	{ "use-hidden-hdr-file",required_argument,	NULL, FLAG_LONG_USE_HHDR_FILE },
227 	{ "modify",		no_argument,		NULL, FLAG_LONG_MOD },
228 	{ "new-keyfile",	required_argument,	NULL, FLAG_LONG_MOD_KF },
229 	{ "new-pbkdf-prf",	required_argument,	NULL, FLAG_LONG_MOD_PRF },
230 	{ "restore-from-backup-hdr", no_argument,	NULL, FLAG_LONG_MOD_NONE },
231 	{ "save-hdr-backup",	required_argument,	NULL, FLAG_LONG_MOD_TO_FILE },
232 	{ "unmap",		required_argument,	NULL, 'u' },
233 	{ "version",		no_argument,		NULL, 'v' },
234 	{ "weak-keys",		no_argument,		NULL, 'w' },
235 	{ "insecure-erase",	no_argument,		NULL, 'z' },
236 	{ "help",		no_argument,		NULL, 'h' },
237 	{ "no-retries",         no_argument,            NULL, FLAG_LONG_NO_RETRIES },
238 	{ NULL,			0,			NULL, 0   },
239 };
240 
241 #define _set_str_opt(opt) \
242 	do {									\
243 		if ((opts->opt = strdup_safe_mem(optarg)) == NULL) {		\
244 			fprintf(stderr, "Could not allocate safe mem.\n");	\
245 			exit(EXIT_FAILURE);					\
246 		}								\
247 	} while(0)
248 
249 int
250 main(int argc, char *argv[])
251 {
252 	struct tcplay_opts *opts;
253 	int ch, error;
254 	int info_vol = 0, map_vol = 0,
255 	    unmap_vol = 0, info_map = 0,
256 	    create_vol = 0, modify_vol = 0;
257 
258 	if ((error = tc_play_init()) != 0) {
259 		fprintf(stderr, "Initialization failed, exiting.");
260 		exit(EXIT_FAILURE);
261 	}
262 
263 	atexit(check_and_purge_safe_mem);
264 	signal(SIGUSR1, sig_handler);
265 	signal(SIGINFO, sig_handler);
266 
267 	if ((opts = opts_init()) == NULL) {
268 		fprintf(stderr, "Initialization failed (opts), exiting.");
269 		exit(EXIT_FAILURE);
270 	}
271 
272 	opts->interactive = 1;
273 
274 	while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:ps:tu:vwx:y:z",
275 	    longopts, NULL)) != -1) {
276 		switch(ch) {
277 		case 'a':
278 			if (opts->prf_algo != NULL)
279 				usage();
280 			if ((opts->prf_algo = check_prf_algo(optarg, 0, 0)) == NULL) {
281 				if (strcmp(optarg, "help") == 0)
282 					exit(EXIT_SUCCESS);
283 				else
284 					usage();
285 				/* NOT REACHED */
286 			}
287 			break;
288 		case 'b':
289 			if (opts->cipher_chain != NULL)
290 				usage();
291 			if ((opts->cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
292 				if (strcmp(optarg, "help") == 0)
293 					exit(EXIT_SUCCESS);
294 				else
295 					usage();
296 				/* NOT REACHED */
297 			}
298 			break;
299 		case 'c':
300 			create_vol = 1;
301 			break;
302 		case 'd':
303 			_set_str_opt(dev);
304 			break;
305 		case 'e':
306 			opts->protect_hidden = 1;
307 			break;
308 		case 'f':
309 			if ((error = opts_add_keyfile_hidden(opts, optarg)) != 0) {
310 				fprintf(stderr, "Could not add keyfile: %s\n", optarg);
311 				exit(EXIT_FAILURE);
312 			}
313 			break;
314 		case 'g':
315 			opts->hidden = 1;
316 			break;
317 		case 'i':
318 			info_vol = 1;
319 			break;
320 		case 'j':
321 			info_map = 1;
322 			_set_str_opt(map_name);
323 			break;
324 		case 'k':
325 			if ((error = opts_add_keyfile(opts, optarg)) != 0) {
326 				fprintf(stderr, "Could not add keyfile: %s\n", optarg);
327 				exit(EXIT_FAILURE);
328 			}
329 			break;
330 		case 'm':
331 			map_vol = 1;
332 			_set_str_opt(map_name);
333 			break;
334 		case 'p':
335 			opts->prompt_passphrase = 1;
336 			break;
337 		case 's':
338 			opts->flags |= TC_FLAG_SYS;
339 			_set_str_opt(sys_dev);
340 			break;
341 		case 't':
342 			opts->flags |= TC_FLAG_ALLOW_TRIM;
343 			break;
344 		case 'u':
345 			unmap_vol = 1;
346 			_set_str_opt(map_name);
347 			break;
348 		case 'v':
349 			printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER);
350 			exit(EXIT_SUCCESS);
351 			/* NOT REACHED */
352 		case 'w':
353 			fprintf(stderr, "WARNING: Using urandom as source of "
354 			    "entropy for key material is a really bad idea.\n");
355 			opts->weak_keys_and_salt = 1;
356 			break;
357 		case 'x':
358 			if (opts->h_prf_algo != NULL)
359 				usage();
360 			if ((opts->h_prf_algo = check_prf_algo(optarg, 0, 0)) == NULL) {
361 				if (strcmp(optarg, "help") == 0)
362 					exit(EXIT_SUCCESS);
363 				else
364 					usage();
365 				/* NOT REACHED */
366 			}
367 			break;
368 		case 'y':
369 			if (opts->h_cipher_chain != NULL)
370 				usage();
371 			if ((opts->h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
372 				if (strcmp(optarg, "help") == 0)
373 					exit(EXIT_SUCCESS);
374 				else
375 					usage();
376 				/* NOT REACHED */
377 			}
378 			break;
379 		case 'z':
380 			opts->secure_erase = 0;
381 			break;
382 		case FLAG_LONG_FDE:
383 			opts->flags |= TC_FLAG_FDE;
384 			break;
385 		case FLAG_LONG_USE_BACKUP:
386 			opts->flags |= TC_FLAG_BACKUP;
387 			break;
388 		case FLAG_LONG_USE_HDR_FILE:
389 			opts->flags |= TC_FLAG_HDR_FROM_FILE;
390 			_set_str_opt(hdr_file_in);
391 			break;
392 		case FLAG_LONG_USE_HHDR_FILE:
393 			opts->flags |= TC_FLAG_H_HDR_FROM_FILE;
394 			_set_str_opt(h_hdr_file_in);
395 			break;
396 		case FLAG_LONG_MOD:
397 			modify_vol = 1;
398 			break;
399 		case FLAG_LONG_MOD_KF:
400 			if ((error = opts_add_keyfile_new(opts, optarg)) != 0) {
401 				fprintf(stderr, "Could not add keyfile: %s\n", optarg);
402 				exit(EXIT_FAILURE);
403 			}
404 			break;
405 		case FLAG_LONG_MOD_PRF:
406 			if (opts->new_prf_algo != NULL)
407 				usage();
408 			if ((opts->new_prf_algo = check_prf_algo(optarg, 0, 0)) == NULL) {
409 				if (strcmp(optarg, "help") == 0)
410 					exit(EXIT_SUCCESS);
411 				else
412 					usage();
413 				/* NOT REACHED */
414 			}
415 			break;
416 		case FLAG_LONG_MOD_NONE:
417 			opts->new_prf_algo = NULL;
418 			opts->flags |= TC_FLAG_ONLY_RESTORE;
419 			opts->flags |= TC_FLAG_BACKUP;
420 			break;
421 		case FLAG_LONG_MOD_TO_FILE:
422 			opts->flags |= TC_FLAG_SAVE_TO_FILE;
423 			_set_str_opt(hdr_file_out);
424 			break;
425 		case FLAG_LONG_NO_RETRIES:
426 			opts->retries = 1;
427 			break;
428 		case 'h':
429 		case '?':
430 		default:
431 			usage();
432 			/* NOT REACHED */
433 		}
434 	}
435 
436 	argc -= optind;
437 	argv += optind;
438 
439 	/* Check arguments */
440 	if (!(((map_vol || info_vol || create_vol || modify_vol) && opts->dev != NULL) ||
441 	    ((unmap_vol || info_map) && opts->map_name != NULL)) ||
442 	    (TC_FLAG_SET(opts->flags, SYS) && TC_FLAG_SET(opts->flags, FDE)) ||
443 	    (map_vol + info_vol + create_vol + unmap_vol + info_map + modify_vol > 1) ||
444 	    (opts->hidden && !create_vol) ||
445 	    (TC_FLAG_SET(opts->flags, SYS) && (opts->sys_dev == NULL)) ||
446 	    (TC_FLAG_SET(opts->flags, ONLY_RESTORE) && (opts->n_newkeyfiles > 0 || opts->new_prf_algo != NULL)) ||
447 	    (TC_FLAG_SET(opts->flags, BACKUP) && (opts->sys_dev != NULL || TC_FLAG_SET(opts->flags, FDE))) ||
448 	    (map_vol && (opts->map_name == NULL)) ||
449 	    (unmap_vol && (opts->map_name == NULL)) ||
450 	    (!modify_vol && opts->n_newkeyfiles > 0) ||
451 	    (!modify_vol && opts->new_prf_algo != NULL) ||
452 	    (!modify_vol && TC_FLAG_SET(opts->flags, ONLY_RESTORE)) ||
453 	    (!modify_vol && TC_FLAG_SET(opts->flags, SAVE_TO_FILE)) ||
454 	    (!(opts->protect_hidden || create_vol) && opts->n_hkeyfiles > 0)) {
455 		usage();
456 		/* NOT REACHED */
457 	}
458 
459 	/* Create a new volume */
460 	if (create_vol) {
461 		error = create_volume(opts);
462 		if (error) {
463 			tc_log(1, "could not create new volume on %s\n", opts->dev);
464 		}
465 	} else if (info_map) {
466 		error = info_mapped_volume(opts);
467 	} else if (info_vol) {
468 		error = info_volume(opts);
469 	} else if (map_vol) {
470 		error = map_volume(opts);
471 	} else if (unmap_vol) {
472 		error = dm_teardown(opts->map_name, NULL);
473 	} else if (modify_vol) {
474 		error = modify_volume(opts);
475 	}
476 
477 	return error;
478 }
479 
480 #undef _set_str_opt
481