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