예제 #1
0
파일: tcplay.c 프로젝트: GDXN/tc-play
static
struct tcplay_info *
new_info(const char *dev, int flags, struct tc_cipher_chain *cipher_chain,
    struct pbkdf_prf_algo *prf, struct tchdr_dec *hdr, off_t start)
{
	struct tc_cipher_chain *chain_start;
	struct tcplay_info *info;
	int i;
	int error;

	chain_start = cipher_chain;

	if ((info = (struct tcplay_info *)alloc_safe_mem(sizeof(*info))) == NULL) {
		tc_log(1, "could not allocate safe info memory\n");
		return NULL;
	}

	strncpy(info->dev, dev, sizeof(info->dev));
	info->cipher_chain = cipher_chain;
	info->pbkdf_prf = prf;
	info->start = start;
	info->hdr = hdr;
	info->blk_sz = hdr->sec_sz;
	info->size = hdr->sz_mk_scope / hdr->sec_sz;	/* volume size */
	info->skip = hdr->off_mk_scope / hdr->sec_sz;	/* iv skip */

	info->volflags = hdr->flags;
	info->flags = flags;

	if (TC_FLAG_SET(flags, SYS))
		info->offset = 0; /* offset is 0 for system volumes */
	else
		info->offset = hdr->off_mk_scope / hdr->sec_sz;	/* block offset */

	/* Associate a key out of the key pool with each cipher in the chain */
	error = tc_cipher_chain_populate_keys(cipher_chain, hdr->keys);
	if (error) {
		tc_log(1, "could not populate keys in cipher chain\n");
		return NULL;
	}

	for (; cipher_chain != NULL; cipher_chain = cipher_chain->next) {
		for (i = 0; i < cipher_chain->cipher->klen; i++)
			sprintf(&cipher_chain->dm_key[i*2], "%02x",
			    cipher_chain->key[i]);
	}

	tc_cipher_chain_free_keys(chain_start);

	return info;
}
예제 #2
0
파일: main.c 프로젝트: tzarskyz/tc-play
int
main(int argc, char *argv[])
{
	const char *dev = NULL, *sys_dev = NULL, *map_name = NULL;
	const char *keyfiles[MAX_KEYFILES];
	const char *h_keyfiles[MAX_KEYFILES];
	int nkeyfiles;
	int n_hkeyfiles;
	int ch, error;
	int flags = 0;
	int info_vol = 0, map_vol = 0, protect_hidden = 0,
	    unmap_vol = 0, info_map = 0,
	    create_vol = 0, contain_hidden = 0, use_secure_erase = 1,
	    use_weak_keys = 0;
	struct pbkdf_prf_algo *prf = NULL;
	struct tc_cipher_chain *cipher_chain = NULL;
	struct pbkdf_prf_algo *h_prf = NULL;
	struct tc_cipher_chain *h_cipher_chain = NULL;

	if ((error = tc_play_init()) != 0) {
		fprintf(stderr, "Initialization failed, exiting.");
		exit(EXIT_FAILURE);
	}

	atexit(check_and_purge_safe_mem);
	signal(SIGUSR1, sig_handler);
	signal(SIGINFO, sig_handler);

	nkeyfiles = 0;
	n_hkeyfiles = 0;

	while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:s:u:vwx:y:z",
	    longopts, NULL)) != -1) {
		switch(ch) {
		case 'a':
			if (prf != NULL)
				usage();
			if ((prf = check_prf_algo(optarg, 0)) == NULL) {
				if (strcmp(optarg, "help") == 0)
					exit(EXIT_SUCCESS);
				else
					usage();
				/* NOT REACHED */
			}
			break;
		case 'b':
			if (cipher_chain != NULL)
				usage();
			if ((cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
				if (strcmp(optarg, "help") == 0)
					exit(EXIT_SUCCESS);
				else
					usage();
				/* NOT REACHED */
			}
			break;
		case 'c':
			create_vol = 1;
			break;
		case 'd':
			dev = optarg;
			break;
		case 'e':
			protect_hidden = 1;
			break;
		case 'f':
			h_keyfiles[n_hkeyfiles++] = optarg;
			break;
		case 'g':
			contain_hidden = 1;
			break;
		case 'i':
			info_vol = 1;
			break;
		case 'j':
			info_map = 1;
			map_name = optarg;
			break;
		case 'k':
			keyfiles[nkeyfiles++] = optarg;
			break;
		case 'm':
			map_vol = 1;
			map_name = optarg;
			break;
		case 's':
			flags |= TC_FLAG_SYS;
			sys_dev = optarg;
			break;
		case 'u':
			unmap_vol = 1;
			map_name = optarg;
			break;
		case 'v':
			printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER);
			exit(EXIT_SUCCESS);
			/* NOT REACHED */
		case 'w':
			fprintf(stderr, "WARNING: Using urandom as source of "
			    "entropy for key material is a really bad idea.\n");
			use_weak_keys = 1;
			break;
		case 'x':
			if (h_prf != NULL)
				usage();
			if ((h_prf = check_prf_algo(optarg, 0)) == NULL) {
				if (strcmp(optarg, "help") == 0)
					exit(EXIT_SUCCESS);
				else
					usage();
				/* NOT REACHED */
			}
			break;
		case 'y':
			if (h_cipher_chain != NULL)
				usage();
			if ((h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
				if (strcmp(optarg, "help") == 0)
					exit(EXIT_SUCCESS);
				else
					usage();
				/* NOT REACHED */
			}
			break;
		case 'z':
			use_secure_erase = 0;
			break;
		case FLAG_LONG_FDE:
			flags |= TC_FLAG_FDE;
			break;
		case FLAG_LONG_USE_BACKUP:
			flags |= TC_FLAG_BACKUP;
			break;
		case 'h':
		case '?':
		default:
			usage();
			/* NOT REACHED */
		}
	}

	argc -= optind;
	argv += optind;

	/* Check arguments */
	if (!(((map_vol || info_vol || create_vol) && dev != NULL) ||
	    ((unmap_vol || info_map) && map_name != NULL)) ||
	    (TC_FLAG_SET(flags, SYS) && TC_FLAG_SET(flags, FDE)) ||
	    (map_vol && info_vol) ||
	    (map_vol && create_vol) ||
	    (unmap_vol && map_vol) ||
	    (unmap_vol && info_vol) ||
	    (unmap_vol && create_vol) ||
	    (create_vol && info_vol) ||
	    (contain_hidden && !create_vol) ||
	    (TC_FLAG_SET(flags, SYS) && (sys_dev == NULL)) ||
	    (map_vol && (map_name == NULL)) ||
	    (unmap_vol && (map_name == NULL)) ||
	    (!(protect_hidden || create_vol) && n_hkeyfiles > 0)) {
		usage();
		/* NOT REACHED */
	}

	/* Create a new volume */
	if (create_vol) {
		error = create_volume(dev, contain_hidden, keyfiles, nkeyfiles,
		    h_keyfiles, n_hkeyfiles, prf, cipher_chain, h_prf,
		    h_cipher_chain, NULL, NULL,
		    0, 1 /* interactive */,
		    use_secure_erase, use_weak_keys);
		if (error) {
			tc_log(1, "could not create new volume on %s\n", dev);
		}
	} else if (info_map) {
		error = info_mapped_volume(map_name, 1 /* interactive */);
	} else if (info_vol) {
		error = info_volume(dev, flags, sys_dev, protect_hidden,
		    keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL,
		    1 /* interactive */, DEFAULT_RETRIES, 0);
	} else if (map_vol) {
		error = map_volume(map_name,
		    dev, flags, sys_dev, protect_hidden,
		    keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL,
		    1 /* interactive */, DEFAULT_RETRIES, 0);
	} else if (unmap_vol) {
		error = dm_teardown(map_name, NULL);
	}

	return error;
}
예제 #3
0
파일: tcplay.c 프로젝트: GDXN/tc-play
struct tcplay_info *
info_map_common(struct tcplay_opts *opts, char *passphrase_out)
{
	struct tchdr_enc *ehdr, *hehdr = NULL;
	struct tcplay_info *info, *hinfo = NULL;
	char *pass;
	char *h_pass;
	int error, error2 = 0;
	size_t sz;
	size_t blksz;
	disksz_t blocks;
	int is_hidden = 0;
	int try_empty = 0;
	int retries;

	if ((error = get_disk_info(opts->dev, &blocks, &blksz)) != 0) {
		tc_log(1, "could not get disk information\n");
		return NULL;
	}

	if (opts->retries < 1)
		retries = 1;
	else
		retries = opts->retries;

	/*
	 * Add one retry so we can do a first try without asking for
	 * a password if keyfiles are passed in.
	 */
	if (opts->interactive && (opts->nkeyfiles > 0)) {
		try_empty = 1;
		++retries;
	}

	info = NULL;

	ehdr = NULL;
	pass = h_pass = NULL;

	while ((info == NULL) && retries-- > 0)
	{
		pass = h_pass = NULL;
		ehdr = hehdr = NULL;
		info = hinfo = NULL;

		if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) {
			tc_log(1, "could not allocate safe passphrase memory\n");
			goto out;
		}

		if (try_empty) {
			pass[0] = '\0';
		} else if (opts->interactive) {
		        if ((error = read_passphrase("Passphrase: ", pass,
			    MAX_PASSSZ, PASS_BUFSZ, opts->timeout))) {
				tc_log(1, "could not read passphrase\n");
				/* XXX: handle timeout differently? */
				goto out;
			}
			pass[MAX_PASSSZ] = '\0';
		} else {
			/* In batch mode, use provided passphrase */
			if (opts->passphrase != NULL) {
				strncpy(pass, opts->passphrase, MAX_PASSSZ);
				pass[MAX_PASSSZ] = '\0';
			}
		}

		if (passphrase_out != NULL) {
			strcpy(passphrase_out, pass);
		}

		if (opts->nkeyfiles > 0) {
			/* Apply keyfiles to 'pass' */
			if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ,
			    opts->keyfiles, opts->nkeyfiles))) {
				tc_log(1, "could not apply keyfiles");
				goto out;
			}
		}

		if (opts->protect_hidden) {
			if ((h_pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) {
				tc_log(1, "could not allocate safe passphrase memory\n");
				goto out;
			}

			if (opts->interactive) {
			        if ((error = read_passphrase(
				    "Passphrase for hidden volume: ", h_pass,
				    MAX_PASSSZ, PASS_BUFSZ, opts->timeout))) {
					tc_log(1, "could not read passphrase\n");
					goto out;
				}
				h_pass[MAX_PASSSZ] = '\0';
			} else {
				/* In batch mode, use provided passphrase */
				if (opts->h_passphrase != NULL) {
					strncpy(h_pass, opts->h_passphrase, MAX_PASSSZ);
					h_pass[MAX_PASSSZ] = '\0';
				}
			}

			if (opts->n_hkeyfiles > 0) {
				/* Apply keyfiles to 'pass' */
				if ((error = apply_keyfiles((unsigned char *)h_pass, PASS_BUFSZ,
				    opts->h_keyfiles, opts->n_hkeyfiles))) {
					tc_log(1, "could not apply keyfiles");
					goto out;
				}
			}
		}

		/* Always read blksz-sized chunks */
		sz = blksz;

		if (TC_FLAG_SET(opts->flags, HDR_FROM_FILE)) {
			ehdr = (struct tchdr_enc *)read_to_safe_mem(
			    opts->hdr_file_in, 0, &sz);
			if (ehdr == NULL) {
				tc_log(1, "error read hdr_enc: %s", opts->hdr_file_in);
				goto out;
			}
		} else {
			ehdr = (struct tchdr_enc *)read_to_safe_mem(
			    (TC_FLAG_SET(opts->flags, SYS)) ? opts->sys_dev : opts->dev,
			    (TC_FLAG_SET(opts->flags, SYS) || TC_FLAG_SET(opts->flags, FDE)) ?
			    HDR_OFFSET_SYS :
			    (!TC_FLAG_SET(opts->flags, BACKUP)) ? 0 : -BACKUP_HDR_OFFSET_END,
			    &sz);
			if (ehdr == NULL) {
				tc_log(1, "error read hdr_enc: %s", opts->dev);
				goto out;
			}
		}

		if (!TC_FLAG_SET(opts->flags, SYS)) {
			/* Always read blksz-sized chunks */
			sz = blksz;

			if (TC_FLAG_SET(opts->flags, H_HDR_FROM_FILE)) {
				hehdr = (struct tchdr_enc *)read_to_safe_mem(
				    opts->h_hdr_file_in, 0, &sz);
				if (hehdr == NULL) {
					tc_log(1, "error read hdr_enc: %s", opts->h_hdr_file_in);
					goto out;
				}
			} else {
				hehdr = (struct tchdr_enc *)read_to_safe_mem(opts->dev,
				    (!TC_FLAG_SET(opts->flags, BACKUP)) ? HDR_OFFSET_HIDDEN :
				    -BACKUP_HDR_HIDDEN_OFFSET_END, &sz);
				if (hehdr == NULL) {
					tc_log(1, "error read hdr_enc: %s", opts->dev);
					goto out;
				}
			}
		} else {
			hehdr = NULL;
		}

		error = process_hdr(opts->dev, opts->flags, (unsigned char *)pass,
		    (opts->nkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
		    ehdr, &info);

		/*
		 * Try to process hidden header if we have to protect the hidden
		 * volume, or the decryption/verification of the main header
		 * failed.
		 */
		if (hehdr && (error || opts->protect_hidden)) {
			if (error) {
				error2 = process_hdr(opts->dev, opts->flags, (unsigned char *)pass,
				    (opts->nkeyfiles > 0)?MAX_PASSSZ:strlen(pass), hehdr,
				    &info);
				is_hidden = !error2;
			} else if (opts->protect_hidden) {
				error2 = process_hdr(opts->dev, opts->flags, (unsigned char *)h_pass,
				    (opts->n_hkeyfiles > 0)?MAX_PASSSZ:strlen(h_pass), hehdr,
				    &hinfo);
			}
		}

		/* We need both to protect a hidden volume */
		if ((opts->protect_hidden && (error || error2)) ||
		    (error && error2)) {
			if (!try_empty)
				tc_log(1, "Incorrect password or not a TrueCrypt volume\n");

			if (info) {
				free_info(info);
				info = NULL;
			}
			if (hinfo) {
				free_info(hinfo);
				hinfo = NULL;
			}

			/* Try again (or finish) */
			free_safe_mem(pass);
			pass = NULL;

			if (h_pass) {
				free_safe_mem(h_pass);
				h_pass = NULL;
			}
			if (ehdr) {
				free_safe_mem(ehdr);
				ehdr = NULL;
			}
			if (hehdr) {
				free_safe_mem(hehdr);
				hehdr = NULL;
			}

			try_empty = 0;
			continue;
		}

		if (opts->protect_hidden) {
			if (adjust_info(info, hinfo) != 0) {
				tc_log(1, "Could not protect hidden volume\n");
				if (info)
					free_info(info);
				info = NULL;

				if (hinfo)
					free_info(hinfo);
				hinfo = NULL;

				goto out;
			}

			if (hinfo) {
				free_info(hinfo);
				hinfo = NULL;
			}
		}
		try_empty = 0;
        }

out:
	if (hinfo)
		free_info(hinfo);
	if (pass)
		free_safe_mem(pass);
	if (h_pass)
		free_safe_mem(h_pass);
	if (ehdr)
		free_safe_mem(ehdr);
	if (hehdr)
		free_safe_mem(hehdr);

	if (info != NULL)
		info->hidden = is_hidden;

	return info;
}
예제 #4
0
파일: tcplay.c 프로젝트: GDXN/tc-play
int
modify_volume(struct tcplay_opts *opts)
{
	struct tcplay_info *info;
	struct tchdr_enc *ehdr, *ehdr_backup;
	const char *new_passphrase = opts->new_passphrase;
	const char **new_keyfiles = opts->new_keyfiles;
	struct pbkdf_prf_algo *new_prf_algo = opts->new_prf_algo;
	int n_newkeyfiles = opts->n_newkeyfiles;
	char *pass, *pass_again;
	int ret = -1;
	off_t offset, offset_backup = 0;
	const char *dev;
	size_t blksz;
	disksz_t blocks;
	int error;

	ehdr = ehdr_backup = NULL;
	pass = pass_again = NULL;
	info = NULL;

	if (TC_FLAG_SET(opts->flags, ONLY_RESTORE)) {
		if (opts->interactive) {
			if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) {
				tc_log(1, "could not allocate safe "
				    "passphrase memory");
				goto out;
			}
		} else {
			new_passphrase = opts->passphrase;
		}
		new_keyfiles = opts->keyfiles;
		n_newkeyfiles = opts->nkeyfiles;
		new_prf_algo = NULL;
	}

	info = info_map_common(opts, pass);
	if (info == NULL)
		goto out;

	if (opts->interactive && !TC_FLAG_SET(opts->flags, ONLY_RESTORE)) {
		if (((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) ||
		   ((pass_again = alloc_safe_mem(PASS_BUFSZ)) == NULL)) {
			tc_log(1, "could not allocate safe passphrase memory\n");
			goto out;
		}

		if ((error = read_passphrase("New passphrase: ", pass, MAX_PASSSZ,
		    PASS_BUFSZ, 0) ||
		    (read_passphrase("Repeat passphrase: ", pass_again,
		    MAX_PASSSZ, PASS_BUFSZ, 0)))) {
			tc_log(1, "could not read passphrase\n");
			goto out;
		}

		if (strcmp(pass, pass_again) != 0) {
			tc_log(1, "Passphrases don't match\n");
			goto out;
		}

		free_safe_mem(pass_again);
		pass_again = NULL;
	} else if (!opts->interactive) {
		/* In batch mode, use provided passphrase */
		if ((pass = alloc_safe_mem(PASS_BUFSZ)) == NULL) {
			tc_log(1, "could not allocate safe "
			    "passphrase memory");
			goto out;
		}

		if (new_passphrase != NULL) {
			strncpy(pass, new_passphrase, MAX_PASSSZ);
			pass[MAX_PASSSZ] = '\0';
		}
	}

	if (n_newkeyfiles > 0) {
		/* Apply keyfiles to 'pass' */
		if ((error = apply_keyfiles((unsigned char *)pass, PASS_BUFSZ,
		    new_keyfiles, n_newkeyfiles))) {
			tc_log(1, "could not apply keyfiles\n");
			goto out;
		}
	}

	ehdr = copy_reencrypt_hdr((unsigned char *)pass,
	    (opts->n_newkeyfiles > 0)?MAX_PASSSZ:strlen(pass),
	    new_prf_algo, opts->weak_keys_and_salt, info, &ehdr_backup);
	if (ehdr == NULL) {
		tc_log(1, "Could not create header\n");
		goto out;
	}

	dev = (TC_FLAG_SET(opts->flags, SYS)) ? opts->sys_dev : opts->dev;
	if (TC_FLAG_SET(opts->flags, SYS) || TC_FLAG_SET(opts->flags, FDE)) {
		/* SYS and FDE don't have backup headers (as far as I understand) */
		if (info->hidden) {
			offset = HDR_OFFSET_HIDDEN;
		} else {
			offset = HDR_OFFSET_SYS;
		}
	} else {
		if (info->hidden) {
			offset = HDR_OFFSET_HIDDEN;
			offset_backup = -BACKUP_HDR_HIDDEN_OFFSET_END;
		} else {
			offset = 0;
			offset_backup = -BACKUP_HDR_OFFSET_END;
		}
	}

	if ((error = get_disk_info(dev, &blocks, &blksz)) != 0) {
		tc_log(1, "could not get disk information\n");
		goto out;
	}

	tc_log(0, "Writing new volume headers to disk/file...\n");

	if (TC_FLAG_SET(opts->flags, SAVE_TO_FILE)) {
		if ((error = write_to_file(opts->hdr_file_out, ehdr, sizeof(*ehdr))) != 0) {
			tc_log(1, "Could not write volume header to file\n");
			goto out;
		}
	} else {
		if ((error = write_to_disk(dev, offset, blksz, ehdr,
		    sizeof(*ehdr))) != 0) {
			tc_log(1, "Could not write volume header to device\n");
			goto out;
		}

		if (!TC_FLAG_SET(opts->flags, SYS) && !TC_FLAG_SET(opts->flags, FDE)) {
			if ((error = write_to_disk(dev, offset_backup, blksz,
			    ehdr_backup, sizeof(*ehdr_backup))) != 0) {
				tc_log(1, "Could not write backup volume header to device\n");
				goto out;
			}
		}
	}

	/* Everything went ok */
	tc_log(0, "All done!\n");

	ret = 0;

out:
	if (pass)
		free_safe_mem(pass);
	if (pass_again)
		free_safe_mem(pass_again);
	if (ehdr)
		free_safe_mem(ehdr);
	if (ehdr_backup)
		free_safe_mem(ehdr_backup);
	if (info)
		free_safe_mem(info);

	return ret;
}
예제 #5
0
파일: main.c 프로젝트: smrt28/tc-play
int
main(int argc, char *argv[])
{
    struct tcplay_opts *opts;
    int ch, error;
    int info_vol = 0, map_vol = 0,
        unmap_vol = 0, info_map = 0,
        create_vol = 0, modify_vol = 0;

    if ((error = tc_play_init()) != 0) {
        fprintf(stderr, "Initialization failed, exiting.");
        exit(EXIT_FAILURE);
    }

    atexit(check_and_purge_safe_mem);
    signal(SIGUSR1, sig_handler);
    signal(SIGINFO, sig_handler);

    if ((opts = opts_init()) == NULL) {
        fprintf(stderr, "Initialization failed (opts), exiting.");
        exit(EXIT_FAILURE);
    }

    opts->interactive = 1;

    while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghij:k:m:s:tu:vwx:y:zC:",
        longopts, NULL)) != -1) {
        switch(ch) {
        case 'a':
            if (opts->prf_algo != NULL)
                usage();
            if ((opts->prf_algo = check_prf_algo(optarg, 0)) == NULL) {
                if (strcmp(optarg, "help") == 0)
                    exit(EXIT_SUCCESS);
                else
                    usage();
                /* NOT REACHED */
            }
            break;
        case 'b':
            if (opts->cipher_chain != NULL)
                usage();
            if ((opts->cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
                if (strcmp(optarg, "help") == 0)
                    exit(EXIT_SUCCESS);
                else
                    usage();
                /* NOT REACHED */
            }
            break;
        case 'c':
            create_vol = 1;
            break;
        case 'C':
            opts->custom_iterations = atoi(optarg);
            break;
        case 'd':
            _set_str_opt(dev);
            break;
        case 'e':
            opts->protect_hidden = 1;
            break;
        case 'f':
            if ((error = opts_add_keyfile_hidden(opts, optarg)) != 0) {
                fprintf(stderr, "Could not add keyfile: %s\n", optarg);
                exit(EXIT_FAILURE);
            }
            break;
        case 'g':
            opts->hidden = 1;
            break;
        case 'i':
            info_vol = 1;
            break;
        case 'j':
            info_map = 1;
            _set_str_opt(map_name);
            break;
        case 'k':
            if ((error = opts_add_keyfile(opts, optarg)) != 0) {
                fprintf(stderr, "Could not add keyfile: %s\n", optarg);
                exit(EXIT_FAILURE);
            }
            break;
        case 'm':
            map_vol = 1;
            _set_str_opt(map_name);
            break;
        case 's':
            opts->flags |= TC_FLAG_SYS;
            _set_str_opt(sys_dev);
            break;
        case 't':
            opts->flags |= TC_FLAG_ALLOW_TRIM;
            break;
        case 'u':
            unmap_vol = 1;
            _set_str_opt(map_name);
            break;
        case 'v':
            printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER);
            exit(EXIT_SUCCESS);
            /* NOT REACHED */
        case 'w':
            fprintf(stderr, "WARNING: Using urandom as source of "
                "entropy for key material is a really bad idea.\n");
            opts->weak_keys_and_salt = 1;
            break;
        case 'x':
            if (opts->h_prf_algo != NULL)
                usage();
            if ((opts->h_prf_algo = check_prf_algo(optarg, 0)) == NULL) {
                if (strcmp(optarg, "help") == 0)
                    exit(EXIT_SUCCESS);
                else
                    usage();
                /* NOT REACHED */
            }
            break;
        case 'y':
            if (opts->h_cipher_chain != NULL)
                usage();
            if ((opts->h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
                if (strcmp(optarg, "help") == 0)
                    exit(EXIT_SUCCESS);
                else
                    usage();
                /* NOT REACHED */
            }
            break;
        case 'z':
            opts->secure_erase = 0;
            break;
        case FLAG_LONG_FDE:
            opts->flags |= TC_FLAG_FDE;
            break;
        case FLAG_LONG_USE_BACKUP:
            opts->flags |= TC_FLAG_BACKUP;
            break;
        case FLAG_LONG_USE_HDR_FILE:
            opts->flags |= TC_FLAG_HDR_FROM_FILE;
            _set_str_opt(hdr_file_in);
            break;
        case FLAG_LONG_USE_HHDR_FILE:
            opts->flags |= TC_FLAG_H_HDR_FROM_FILE;
            _set_str_opt(h_hdr_file_in);
            break;
        case FLAG_LONG_MOD:
            modify_vol = 1;
            break;
        case FLAG_LONG_MOD_KF:
            if ((error = opts_add_keyfile_new(opts, optarg)) != 0) {
                fprintf(stderr, "Could not add keyfile: %s\n", optarg);
                exit(EXIT_FAILURE);
            }
            break;
        case FLAG_LONG_MOD_PRF:
            if (opts->new_prf_algo != NULL)
                usage();
            if ((opts->new_prf_algo = check_prf_algo(optarg, 0)) == NULL) {
                if (strcmp(optarg, "help") == 0)
                    exit(EXIT_SUCCESS);
                else
                    usage();
                /* NOT REACHED */
            }
            break;
        case FLAG_LONG_MOD_NONE:
            opts->new_prf_algo = NULL;
            opts->flags |= TC_FLAG_ONLY_RESTORE;
            opts->flags |= TC_FLAG_BACKUP;
            break;
        case FLAG_LONG_MOD_TO_FILE:
            opts->flags |= TC_FLAG_SAVE_TO_FILE;
            _set_str_opt(hdr_file_out);
            break;
        case FLAG_LONG_NO_RETRIES:
            opts->retries = 1;
            break;
        case 'h':
        case '?':
        default:
            usage();
            /* NOT REACHED */
        }
    }

    argc -= optind;
    argv += optind;

    /* Check arguments */
    if (!(((map_vol || info_vol || create_vol || modify_vol) && opts->dev != NULL) ||
        ((unmap_vol || info_map) && opts->map_name != NULL)) ||
        (TC_FLAG_SET(opts->flags, SYS) && TC_FLAG_SET(opts->flags, FDE)) ||
        (map_vol + info_vol + create_vol + unmap_vol + info_map + modify_vol > 1) ||
        (opts->hidden && !create_vol) ||
        (TC_FLAG_SET(opts->flags, SYS) && (opts->sys_dev == NULL)) ||
        (TC_FLAG_SET(opts->flags, ONLY_RESTORE) && (opts->n_newkeyfiles > 0 || opts->new_prf_algo != NULL)) ||
        (TC_FLAG_SET(opts->flags, BACKUP) && (opts->sys_dev != NULL || TC_FLAG_SET(opts->flags, FDE))) ||
        (map_vol && (opts->map_name == NULL)) ||
        (unmap_vol && (opts->map_name == NULL)) ||
        (!modify_vol && opts->n_newkeyfiles > 0) ||
        (!modify_vol && opts->new_prf_algo != NULL) ||
        (!modify_vol && TC_FLAG_SET(opts->flags, ONLY_RESTORE)) ||
        (!modify_vol && TC_FLAG_SET(opts->flags, SAVE_TO_FILE)) ||
        (!(opts->protect_hidden || create_vol) && opts->n_hkeyfiles > 0)) {
        usage();
        /* NOT REACHED */
    }

    /* Create a new volume */
    if (create_vol) {
        error = create_volume(opts);
        if (error) {
            tc_log(1, "could not create new volume on %s\n", opts->dev);
        }
    } else if (info_map) {
        error = info_mapped_volume(opts);
    } else if (info_vol) {
        error = info_volume(opts);
    } else if (map_vol) {
        error = map_volume(opts);
    } else if (unmap_vol) {
        error = dm_teardown(opts->map_name, NULL);
    } else if (modify_vol) {
        error = modify_volume(opts);
    }

    return error;
}