xref: /dragonfly/sbin/tcplay/main.c (revision 7b1e1c8e1e00f6479eba04708b37a13383f7e197)
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
sig_handler(int sig)60 sig_handler(int sig)
61 {
62           if ((sig == SIGUSR1 || sig == SIGINFO) && (summary_fn != NULL))
63                     summary_fn();
64 }
65 
66 static
67 void
usage(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
main(int argc,char * argv[])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