Update tcplay from 2.0 to 3.3
[dragonfly.git] / sbin / tcplay / main.c
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