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