xref: /dragonfly/sbin/tcplay/main.c (revision 25a2db75)
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 static
47 void
48 sig_handler(int sig)
49 {
50 	if ((sig == SIGUSR1 || sig == SIGINFO) && (summary_fn != NULL))
51 		summary_fn();
52 }
53 
54 static
55 void
56 usage(void)
57 {
58 	fprintf(stderr,
59 	    "usage: tcplay -c -d device [-g] [-a pbkdb_hash] [-b cipher]\n"
60 	    "              [-f keyfile_hidden] [-k keyfile] [-x pbkdf_hash] [-y cipher]\n"
61 	    "       tcplay -i -d device [-e] [-f keyfile_hidden] [-k keyfile]\n"
62 	    "              [-s system_devcie]\n"
63 	    "       tcplay -m mapping -d device [-e] [-f keyfile_hidden] [-k keyfile]\n"
64 	    "              [-s system_device]\n"
65 	    "       tcplay -h | -v\n"
66 	    "\n"
67 	    "Valid commands are:\n"
68 	    " -c, --create\n"
69 	    "\t Creates a new TC volume on the device specified by -d or --device.\n"
70 	    " -h, --help\n"
71 	    "\t Print help message and exit.\n"
72 	    " -i, --info\n"
73 	    "\t Gives information about the TC volume specified by -d or --device.\n"
74 	    " -m <mapping name>, --map=<mapping name>\n"
75 	    "\t Creates a dm-crypt mapping with the given name for the device\n"
76 	    "\t specified by -d or --device.\n"
77 	    " -v, --version\n"
78 	    "\t Print version message and exit.\n"
79 	    "\n"
80 	    "Valid options for --create are:\n"
81 	    " -a <pbkdf prf algorithm>, --pbkdf-prf=<pbkdf prf algorithm>\n"
82 	    "\t Specifies which hashing function to use for the PBKDF password\n"
83 	    "\t derivation when creating a new volume.\n"
84 	    "\t To see valid options, specify '-a help'.\n"
85 	    " -b <cipher>, --cipher=<cipher>\n"
86 	    "\t Specifies which cipher to use when creating a new TC volume.\n"
87 	    "\t To see valid options, specify '-b help'.\n"
88 	    " -g, --hidden\n"
89 	    "\t Specifies that the newly created volume will contain a hidden volume.\n"
90 	    " -x <pbkdf prf algorithm>, --pbkdf-prf=<pbkdf prf algorithm>\n"
91 	    "\t Specifies which hashing function to use for the PBKDF password\n"
92 	    "\t derivation when creating a new hidden volume.  By default, the\n"
93 	    "\t same as for the outer volume will be used.\n"
94 	    "\t To see valid options, specify '-x help'.\n"
95 	    " -y <cipher>, --cipher=<cipher>\n"
96 	    "\t Specifies which cipher to use when creating a new hidden volume.\n"
97 	    "\t By default, the same as for the outer volume will be used.\n"
98 	    "\t To see valid options, specify '-y help'.\n"
99 	    "\n"
100 	    "Valid options for --info and --map are:\n"
101 	    " -e, --protect-hidden\n"
102 	    "\t Protect a hidden volume when mounting the outer volume.\n"
103 	    " -s <disk path>, --system-encryption=<disk path>\n"
104 	    "\t Specifies that the disk (e.g. /dev/da0) is using system encryption.\n"
105 	    "\n"
106 	    "Valid options common to all commands are:\n"
107 	    " -d <device path>, --device=<device path>\n"
108 	    "\t Specifies the path to the volume to operate on (e.g. /dev/da0s1).\n"
109 	    " -f <key file>, --keyfile-hidden=<key file>\n"
110 	    "\t Specifies a key file to use for the hidden volume password derivation.\n"
111 	    "\t This option is only valid in combination with -e, --protect-hidden\n"
112 	    "\t or -g, --hidden.\n"
113 	    " -k <key file>, --keyfile=<key file>\n"
114 	    "\t Specifies a key file to use for the password derivation, can appear\n"
115 	    "\t multiple times.\n"
116 	    );
117 
118 	exit(1);
119 }
120 
121 static struct option longopts[] = {
122 	{ "create",		no_argument,		NULL, 'c' },
123 	{ "cipher",		required_argument,	NULL, 'b' },
124 	{ "cipher-hidden",	required_argument,	NULL, 'y' },
125 	{ "hidden",		no_argument,		NULL, 'g' },
126 	{ "pbkdf-prf",		required_argument,	NULL, 'a' },
127 	{ "pbkdf-prf-hidden",	required_argument,	NULL, 'x' },
128 	{ "info",		no_argument,		NULL, 'i' },
129 	{ "map",		required_argument,	NULL, 'm' },
130 	{ "keyfile",		required_argument,	NULL, 'k' },
131 	{ "keyfile-hidden",	required_argument,	NULL, 'f' },
132 	{ "protect-hidden",	no_argument,		NULL, 'e' },
133 	{ "device",		required_argument,	NULL, 'd' },
134 	{ "system-encryption",	required_argument,	NULL, 's' },
135 	{ "version",		no_argument,		NULL, 'v' },
136 	{ "help",		no_argument,		NULL, 'h' },
137 	{ NULL,			0,			NULL, 0   },
138 };
139 
140 int
141 main(int argc, char *argv[])
142 {
143 	const char *dev = NULL, *sys_dev = NULL, *map_name = NULL;
144 	const char *keyfiles[MAX_KEYFILES];
145 	const char *h_keyfiles[MAX_KEYFILES];
146 	int nkeyfiles;
147 	int n_hkeyfiles;
148 	int ch, error;
149 	int sflag = 0, info_vol = 0, map_vol = 0, protect_hidden = 0,
150 	    create_vol = 0, contain_hidden = 0;
151 	struct pbkdf_prf_algo *prf = NULL;
152 	struct tc_cipher_chain *cipher_chain = NULL;
153 	struct pbkdf_prf_algo *h_prf = NULL;
154 	struct tc_cipher_chain *h_cipher_chain = NULL;
155 
156 	if ((error = tc_play_init()) != 0) {
157 		fprintf(stderr, "Initialization failed, exiting.");
158 		exit(1);
159 	}
160 
161 	atexit(check_and_purge_safe_mem);
162 	signal(SIGUSR1, sig_handler);
163 	signal(SIGINFO, sig_handler);
164 
165 	nkeyfiles = 0;
166 	n_hkeyfiles = 0;
167 
168 	while ((ch = getopt_long(argc, argv, "a:b:cd:ef:ghik:m:s:vx:y:",
169 	    longopts, NULL)) != -1) {
170 		switch(ch) {
171 		case 'a':
172 			if (prf != NULL)
173 				usage();
174 			if ((prf = check_prf_algo(optarg, 0)) == NULL) {
175 				if (strcmp(optarg, "help") == 0)
176 					exit(0);
177 				else
178 					usage();
179 				/* NOT REACHED */
180 			}
181 			break;
182 		case 'b':
183 			if (cipher_chain != NULL)
184 				usage();
185 			if ((cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
186 				if (strcmp(optarg, "help") == 0)
187 					exit(0);
188 				else
189 					usage();
190 				/* NOT REACHED */
191 			}
192 			break;
193 		case 'c':
194 			create_vol = 1;
195 			break;
196 		case 'd':
197 			dev = optarg;
198 			break;
199 		case 'e':
200 			protect_hidden = 1;
201 			break;
202 		case 'f':
203 			h_keyfiles[n_hkeyfiles++] = optarg;
204 			break;
205 		case 'g':
206 			contain_hidden = 1;
207 			break;
208 		case 'i':
209 			info_vol = 1;
210 			break;
211 		case 'k':
212 			keyfiles[nkeyfiles++] = optarg;
213 			break;
214 		case 'm':
215 			map_vol = 1;
216 			map_name = optarg;
217 			break;
218 		case 's':
219 			sflag = 1;
220 			sys_dev = optarg;
221 			break;
222 		case 'v':
223 			printf("tcplay v%d.%d\n", MAJ_VER, MIN_VER);
224 			exit(0);
225 			/* NOT REACHED */
226 		case 'x':
227 			if (h_prf != NULL)
228 				usage();
229 			if ((h_prf = check_prf_algo(optarg, 0)) == NULL) {
230 				if (strcmp(optarg, "help") == 0)
231 					exit(0);
232 				else
233 					usage();
234 				/* NOT REACHED */
235 			}
236 			break;
237 		case 'y':
238 			if (h_cipher_chain != NULL)
239 				usage();
240 			if ((h_cipher_chain = check_cipher_chain(optarg, 0)) == NULL) {
241 				if (strcmp(optarg, "help") == 0)
242 					exit(0);
243 				else
244 					usage();
245 				/* NOT REACHED */
246 			}
247 			break;
248 		case 'h':
249 		case '?':
250 		default:
251 			usage();
252 			/* NOT REACHED */
253 		}
254 	}
255 
256 	argc -= optind;
257 	argv += optind;
258 
259 	/* Check arguments */
260 	if (!((map_vol || info_vol || create_vol) && dev != NULL) ||
261 	    (map_vol && info_vol) ||
262 	    (map_vol && create_vol) ||
263 	    (create_vol && info_vol) ||
264 	    (contain_hidden && !create_vol) ||
265 	    (sflag && (sys_dev == NULL)) ||
266 	    (map_vol && (map_name == NULL)) ||
267 	    (!(protect_hidden || create_vol) && n_hkeyfiles > 0)) {
268 		usage();
269 		/* NOT REACHED */
270 	}
271 
272 	/* Create a new volume */
273 	if (create_vol) {
274 		error = create_volume(dev, contain_hidden, keyfiles, nkeyfiles,
275 		    h_keyfiles, n_hkeyfiles, prf, cipher_chain, h_prf,
276 		    h_cipher_chain, NULL, NULL,
277 		    0, 1 /* interactive */);
278 		if (error) {
279 			tc_log(1, "could not create new volume on %s\n", dev);
280 		}
281 	} else if (info_vol) {
282 		error = info_volume(dev, sflag, sys_dev, protect_hidden,
283 		    keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL,
284 		    1 /* interactive */, DEFAULT_RETRIES, 0);
285 	} else if (map_vol) {
286 		error = map_volume(map_name,
287 		    dev, sflag, sys_dev, protect_hidden,
288 		    keyfiles, nkeyfiles, h_keyfiles, n_hkeyfiles, NULL, NULL,
289 		    1 /* interactive */, DEFAULT_RETRIES, 0);
290 	}
291 
292 	return error;
293 }
294