Makefile: Fix order of arguments in linker call
[dmsetup-tc.git] / main.c
blobfa1b48133e7351e2af4d1feecdd5ce1043663836
1 /* main.c: dmsetup-tc main program
3 * Copyright (C) 2008 Jan Krueger <jk@jk.gs>
5 * This file is part of dmsetup-tc.
7 * dmsetup-tc is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * dmsetup-tc is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with dmsetup-tc. If not, see <http://www.gnu.org/licenses/>.
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
32 #ifdef DEBUG
33 void trace(const char *msg, ...)
35 va_list ap; va_start(ap, msg);
36 vfprintf(stderr, msg, ap);
37 va_end(ap);
39 #else
40 void trace(const char *msg, ...) {}
41 #endif
43 #include "dmtc.h"
44 #include "dmtc_version.h"
45 #include "xts.h"
46 #include "pkcs5.h"
47 #include "crc32.h"
48 #include <gcrypt.h>
50 /* Disk interface {{{ ***************************************************** */
52 sdata *read_header(const char *file)
54 unsigned char *buf;
55 sdata *vh;
56 int verr;
57 struct stat sb;
59 if (stat(file, &sb) == -1)
60 return NULL;
62 FILE *f = fopen(file, "r");
63 if (!f) return NULL;
65 switch (sb.st_mode & S_IFMT) {
66 case S_IFBLK:
67 /* Probably a disk device; seek to TC header */
68 if (-1 == fseek(f, 62*512, SEEK_SET)) goto err_nofree;
69 break;
70 case S_IFREG:
71 /* Probably a header file, so don't seek */
72 break;
73 default:
74 fclose(f);
75 return NULL;
78 buf = malloc(512);
79 if (!buf) goto err_nofree;
81 if (1 != fread(buf, 512, 1, f)) goto err;
83 fclose(f);
84 vh = malloc(sizeof (sdata));
85 vh->data = buf;
86 vh->size = 512;
87 return vh;
89 err:
90 free(buf);
91 err_nofree:
92 verr = errno;
93 fclose(f);
94 errno = verr;
95 return NULL;
98 static size_t get_part_info(const char *part, const char *type)
100 char buf[512];
101 char part_name[16];
102 FILE *f;
103 size_t val;
104 int verr;
106 if (1 != sscanf(part, "/dev/%15s", part_name)) return 0;
107 snprintf(buf, 512, "/sys/class/block/%s/%s", part_name, type);
109 f = fopen(buf, "r");
110 if (!f) return 0;
112 if (1 != fscanf(f, "%zu", &val)) {
113 verr = errno;
114 fclose(f);
115 errno = verr;
116 return 0;
119 fclose(f);
120 return val;
123 size_t get_part_start_sector(const char *part)
125 return get_part_info(part, "start");
128 size_t get_part_num_sectors(const char *part)
130 return get_part_info(part, "size");
133 /* }}} ******************************************************************** */
135 /* Data extraction interface {{{ ****************************************** */
137 sdata *get_header_salt(sdata *header)
139 sdata *salt = malloc(sizeof (sdata));
140 salt->data = header->data;
141 salt->size = 64;
142 return salt;
145 unsigned int boswap(int x)
147 unsigned char *c = (unsigned char *) &x;
148 return (c[0] << 24) + (c[1] << 16) + (c[2] << 8) + c[3];
151 int validate_header(sdata *header)
153 unsigned char *data = header->data;
154 int i = header->size - 256;
156 trace(" * Validating header signature\n");
157 if (data[0] != 'T' || data[1] != 'R' || data[2] != 'U' ||
158 data[3] != 'E') return INVALID_SIGNATURE;
160 trace(" * Validating header checksum\n");
161 if (boswap(((uint32_t *) data)[47]) !=
162 crc32(data, 188))
163 return HEADER_CHECKSUM_MISMATCH;
165 trace(" * Validating checksum of master keys\n");
166 if (boswap(((uint32_t *) data)[2]) !=
167 crc32(&data[i], 256))
168 return KEY_CHECKSUM_MISMATCH;
170 trace(" * Making sure we're dealing with system encryption\n");
171 if (!(data[63] & 1)) return NOT_SYSTEM_ENCRYPTION;
173 trace(" * Making sure we're not dealing with partial encryption\n");
174 if (memcmp(&data[36], &data[52], 8)) return PARTIALLY_ENCRYPTED;
176 return VALID_HEADER;
179 sdata *get_header_keys(sdata *header)
181 sdata *res = malloc(sizeof (sdata));
183 res->data = header->data + 192;
184 res->size = 256;
186 return res;
189 /* }}} ******************************************************************** */
191 /* Crypt interface {{{ **************************************************** */
193 void crypt_init()
195 gcry_check_version(NULL);
196 gcry_control(GCRYCTL_INIT_SECMEM, 1048576);
199 sdata *crypt_derive_key(sdata *pass, sdata *salt)
201 sdata *res;
202 unsigned char *vkey;
204 vkey = malloc(64);
205 res = malloc(sizeof (sdata));
207 if (0 != gcry_pbkdf2(GCRY_MD_RMD160,
208 (const char *) (pass->data), pass->size,
209 (const char *) (salt->data), salt->size,
210 1000,
212 (char *) vkey)) {
213 free(vkey);
214 free(res);
215 return NULL;
218 res->data = vkey;
219 res->size = 64;
220 return res;
223 sdata *crypt_decrypt_header(sdata *header, sdata *key)
225 sdata *res;
226 unsigned char *data = header->data + 64;
228 if (0 != aes_xts_decrypt(key->data, NULL, 28, 0, data)) return NULL;
230 res = malloc(sizeof (sdata));
231 res->data = data;
232 res->size = header->size - 64;
233 return res;
236 /* }}} ******************************************************************** */
238 /* Auxiliary CLI functions {{{ ******************************************** */
240 void die_banner()
242 fputs(
243 "dmsetup-tc version " DMTC_VERSION "\n"
244 "Copyright (c) <year range here, who cares> Jan Krueger <jk@jk.gs>\n"
245 "\n"
246 " This program is free software: you can redistribute it and/or modify\n"
247 " it under the terms of the GNU General Public License as published by\n"
248 " the Free Software Foundation, either version 3 of the License, or\n"
249 " (at your option) any later version.\n"
250 "\n"
251 " This program is distributed in the hope that it will be useful,\n"
252 " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
253 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
254 " GNU General Public License for more details.\n"
255 "\n"
256 " You should have received a copy of the GNU General Public License\n"
257 " along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
258 stderr);
259 exit(0);
262 void die_error(char *err)
264 if (!err) {
265 perror("dmsetup-tc: fatal error");
266 } else {
267 fprintf(stderr, "dmsetup-tc: fatal error: %s\n", err);
269 exit(2);
272 void die_root()
274 puts("Fatal: dmsetup-tc needs to run as root");
275 exit(99);
278 void die_syntax()
280 puts(
281 "Syntax: dmsetup-tc [--stdin] <disk|header file> <partition>\n"
282 " --stdin Read passphrase from stdin rather than tty (do not prompt)\n"
283 "\n"
284 "Example: dmsetup-tc /dev/sda /dev/sda3 | dmsetup create myvolume\n"
285 " dmsetup-tc headerfile /dev/sda3 | ...\n"
287 exit(64);
290 /* }}} ******************************************************************** */
292 int main(int argc, char *argv[])
294 int ofs = 1;
295 int from_stdin = 0;
296 char *header_file, *part_dev;
297 sdata pass;
298 sdata *vh;
299 sdata *dh;
300 sdata *vkey;
301 sdata *hkey;
302 size_t size, start;
303 char hkey_hex[129];
304 valid_res val;
305 int i;
307 if (argc > 1 && !strcmp(argv[1], "--version"))
308 die_banner();
309 if (argc == 1 || !strcmp(argv[1], "--help"))
310 die_syntax();
312 if (geteuid())
313 die_root();
315 /* Parse options */
316 while (ofs < argc) {
317 if (!strcmp(argv[ofs], "--stdin")) {
318 from_stdin = 1;
319 ofs++;
320 continue;
322 break;
324 if ((argc - ofs) < 2) die_syntax();
326 header_file = argv[ofs];
327 part_dev = argv[ofs+1];
329 pass.data = getpass(from_stdin ? "" :
330 "Enter passphrase for encrypted volume: ");
331 pass.size = strlen(pass.data);
332 if (0 != mlock(pass.data, pass.size)) die_error(NULL);
334 trace("Loading header from %s...\n", header_file);
335 vh = read_header(header_file);
336 if (!vh) die_error(NULL);
338 crypt_init();
340 trace("Deriving header key...\n");
341 vkey = crypt_derive_key(&pass, get_header_salt(vh));
342 if (!vkey) die_error(NULL);
344 trace("Decrypting header...\n");
345 dh = crypt_decrypt_header(vh, vkey);
346 if (!dh) {
347 char buf[1024];
348 snprintf(buf, 1024, "%s/%s", aes_xts_err, aes_xts_errsrc);
349 die_error(buf);
352 trace("Validating header...\n");
353 val = validate_header(dh);
354 switch (val) {
355 case INVALID_SIGNATURE:
356 fputs(
357 "Could not decrypt the volume. You probably entered a wrong password.\n",
358 stderr);
359 break;
360 case KEY_CHECKSUM_MISMATCH:
361 fputs(
362 "The master key in your volume header seems to be corrupted. I can't\n"
363 "really see this happening on its own, except maybe if your RAM is broken.\n"
364 "Have it checked and try restoring the header from your rescue CD.\n",
365 stderr);
366 break;
367 case HEADER_CHECKSUM_MISMATCH:
368 fputs(
369 "Your volume header is corrupted. I suspect faulty RAM. Have it checked\n"
370 "(and possibly fixed) and restore the header from your rescue CD.\n",
371 stderr);
372 break;
373 case NOT_SYSTEM_ENCRYPTION:
374 fputs(
375 "This volume is not using system drive/partition encryption. This program\n"
376 "only supports volumes within the scope of system encryption. Please use\n"
377 "the official TrueCrypt(R) program for accessing this volume.\n",
378 stderr);
379 break;
380 case PARTIALLY_ENCRYPTED:
381 fputs(
382 "System encryption is still in progress. We can't really support partially\n"
383 "encrypted volumes under Linux without a lot of effort, so please have the\n"
384 "official TrueCrypt(R) program for Windows finish encrypting the volume\n"
385 "before you use this program.\n",
386 stderr);
387 break;
389 if (val != VALID_HEADER) exit(1);
391 hkey = get_header_keys(dh);
392 size = get_part_num_sectors(part_dev);
393 start = get_part_start_sector(part_dev);
395 for (i = 0; i < 16; i++) {
396 sprintf(&hkey_hex[i*8], "%08x", boswap(((unsigned int *) hkey->data)[i]));
399 printf("0 %zu crypt aes-xts-plain %s %zu %s 0", size, hkey_hex,
400 start, part_dev);
401 exit(0);