From 052d7ca8a0e00e096b251c72e205edc895196aa0 Mon Sep 17 00:00:00 2001 From: Aorimn Date: Fri, 15 Aug 2014 22:01:31 +0200 Subject: [PATCH] Big changes Remove disk_op_data global variable in the library Extract file & fuse outputs in two standalone binaries Provide global stub functions for dislocker library Add FIXMEs & TODOs everywhere... --- src/Makefile | 54 +- src/config.c | 5 +- src/config.h | 2 +- src/dislocker-bek.c | 5 +- src/{outputs/file/file.c => dislocker-file.c} | 101 +- src/dislocker-fuse.c | 267 ++++ src/dislocker-metadata.c | 107 +- src/dislocker.c | 1111 +++++++++++------ src/dislocker.h | 92 +- src/encommon.h | 36 +- src/encryption/decrypt.c | 4 +- src/encryption/encrypt.c | 4 +- src/outputs/file/file.h | 38 - src/outputs/fuse/fuse.c | 500 -------- src/outputs/fuse/fuse.h | 43 - src/outputs/fuse/main.c | 99 -- src/outputs/prepare.c | 132 +- src/outputs/prepare.h | 5 +- src/sectors.c | 182 +-- src/sectors.h | 20 +- 20 files changed, 1432 insertions(+), 1375 deletions(-) rename src/{outputs/file/file.c => dislocker-file.c} (51%) create mode 100644 src/dislocker-fuse.c delete mode 100644 src/outputs/file/file.h delete mode 100644 src/outputs/fuse/fuse.c delete mode 100644 src/outputs/fuse/fuse.h delete mode 100644 src/outputs/fuse/main.c diff --git a/src/Makefile b/src/Makefile index 0becabd..4a22721 100644 --- a/src/Makefile +++ b/src/Makefile @@ -27,10 +27,11 @@ CFLAGS = $(WFLAGS) $(DEFINES) $(INC) $(CHARDEN) override LDFLAGS += $(LIB) $(LHARDEN) LIBNAME = lib$(PROGNAME).so -BINS = dislocker dislocker-metadata dislocker-bek +SUFFIXES = fuse file metadata bek +BINS = $(foreach suf, $(SUFFIXES), $(BIN)-$(suf)) BINS_OBJECTS = $(foreach bin, $(BINS), $(bin).o) -SOURCES = common.c config.c sectors.c \ +SOURCES = dislocker.c common.c config.c sectors.c \ xstd/xstdio.c xstd/xstdlib.c \ metadata/datums.c metadata/metadata.c metadata/vmk.c \ metadata/fvek.c metadata/extended_info.c \ @@ -55,8 +56,6 @@ MAN_NUMBER = 1 MAN_PATH = $(DESTDIR)/share/man/man$(MAN_NUMBER)/ MAN_ROOT = ../man/ -FUSE_ROOT := outputs/fuse/fuse -FILE_ROOT := outputs/file/file @@ -85,41 +84,25 @@ endif export -.PHONY : all libs static $(BIN) file fuse common clean +.PHONY : all libs $(BIN) $(SUFFIXES) install uninstall clean travis-install valgrind snapshot release .c.o : $(CC) $(CFLAGS) -c -o $@ $< -all : fuse +all : $(BIN) libs: $(OBJECTS) $(CC) -shared $(CFLAGS) -o $(LIBNAME) $(OBJECTS) # Default rules for final binaries -touch : - touch $(BIN).c - -$(BIN) : touch libs $(BINS_OBJECTS) - for prog in $(BINS); do \ - $(CC) $(CFLAGS) -o $${prog} $${prog}.o $(LDFLAGS); \ - done - - -static : touch libs - $(CC) -static $(CFLAGS) -o $(BIN)-static $(OBJECTS) $(LDFLAGS) -lz -ldl +$(BIN) : libs $(SUFFIXES) # Build the binary for the file output -file: CFLAGS += -D__RUN_FILE -file: SOURCES += $(FILE_ROOT).c -file: $(FILE_ROOT).o - -file: $(BIN) - +file: libs $(BIN)-file # Build the binary for the fuse output -fuse: CFLAGS += -D__RUN_FUSE -DFUSE_USE_VERSION=26 -fuse: SOURCES += $(FUSE_ROOT).c +fuse: CFLAGS += -DFUSE_USE_VERSION=26 # Don't link with the same library on MacOSX ifeq ($(OS), Darwin) @@ -128,17 +111,28 @@ else fuse: LDFLAGS += -lfuse endif # Darwin -fuse: $(FUSE_ROOT).o +fuse: libs $(BIN)-fuse + + +# Build the binary for getting metadata only +metadata: libs $(BIN)-metadata + +# Build the binary for getting bek information only +bek: libs $(BIN)-bek -fuse: $(BIN) # Check if the installed binary is present, make it if it isn't check-install: - @[ -e $(BIN) ] && echo "Found $(BIN) binary, installing it" || make clean fuse + @if [ -e $(BIN)-fuse ] ; then \ + echo "Found $(BIN) binary, installing it"; \ + else \ + make clean all; \ + fi -install: check-install +install: all install -pm755 $(BINS) $(BINARIES_PATH) + cd $(BINARIES_PATH) && ln $(BIN)-fuse $(BIN) && cd - install -pm755 $(LIBNAME) $(LIBRARY_PATH) if [ "$(OS)" = "Darwin" ]; then \ cp $(MAN_ROOT)$(BIN)_osx_man $(MAN_ROOT)$(BIN).$(MAN_NUMBER); \ @@ -162,7 +156,7 @@ uninstall: clean clean: - rm -rf -- $(OBJECTS) $(FILE_ROOT).o $(FUSE_ROOT).o *~ *.swp $(BINS) $(BINS_OBJECTS) $(LIBNAME) + rm -rf -- $(OBJECTS) *~ *.swp $(BINS) $(BINS_OBJECTS) $(LIBNAME) travis-install: if [ "$$TRAVIS_OS_NAME" = "linux" ]; then \ diff --git a/src/config.c b/src/config.c index 6ad2ce0..76226a3 100644 --- a/src/config.c +++ b/src/config.c @@ -54,7 +54,7 @@ PROGNAME " by " AUTHOR ", v"VERSION " (compiled for " __OS "/" __ARCH ")\n" " decrypt volume using the recovery password method\n" " -q, --quiet do NOT display anything\n" " -r, --readonly do not allow to write on the BitLocker volume\n" -" -s, --stateok do not check the volume's state, assume it's ok to mount it" +" -s, --stateok do not check the volume's state, assume it's ok to mount it\n" " -u, --user-password decrypt volume using the user password method\n" " -v, --verbosity increase verbosity (CRITICAL errors are displayed by default)\n" " -V, --volume VOLUME volume to get metadata and keys from\n" @@ -92,7 +92,7 @@ static void hide_opt(char* opt) * @param argv Arguments given to the program * @return Return the number of arguments which are still waiting to be studied */ -int parse_args(dis_config_t* cfg, int argc, char** argv) +int parse_args(dis_config_t* cfg, int argc, char** argv) // TODO change into dis_parge_args() { /** See man getopt_long(3) */ extern int optind; @@ -269,6 +269,7 @@ void print_args(dis_config_t* cfg) xprintf(L_DEBUG, " Verbosity: %d\n", cfg->verbosity); xprintf(L_DEBUG, " Trying to decrypt '%s'\n", cfg->volume_path); + // FIXME decryption_mean is a bitfield switch(cfg->decryption_mean) { case USE_CLEAR_KEY: diff --git a/src/config.h b/src/config.h index 17ab555..8b535c9 100644 --- a/src/config.h +++ b/src/config.h @@ -88,7 +88,7 @@ typedef struct _dis_cfg { * By default, dislocker will check for unstable state that may corrupt data * if mounted using fuse */ - char check_state; + char dont_check_state; } dis_config_t; diff --git a/src/dislocker-bek.c b/src/dislocker-bek.c index c968a2d..f3a5a03 100644 --- a/src/dislocker-bek.c +++ b/src/dislocker-bek.c @@ -74,18 +74,19 @@ int main (int argc, char **argv) { xprintf(L_CRITICAL, "Filename must be provided\n"); usage(argv[0]); + return EXIT_FAILURE; } if(( fd = open(filename, O_RDONLY) ) < 0) { xprintf(L_CRITICAL, "Failed to open file %s\n", filename); - exit(1); + return EXIT_FAILURE; } if(!get_bek_dataset(fd, &bek_dataset)) { xprintf(L_CRITICAL, "Unable to get the dataset from the BEK file\n"); - exit(1); + return EXIT_FAILURE; } close(fd); diff --git a/src/outputs/file/file.c b/src/dislocker-file.c similarity index 51% rename from src/outputs/file/file.c rename to src/dislocker-file.c index 003b131..30a4b51 100644 --- a/src/outputs/file/file.c +++ b/src/dislocker-file.c @@ -21,28 +21,22 @@ * USA. */ - +/* This define is for the O_LARGEFILE definition */ #define _GNU_SOURCE - -#include "encommon.h" #include "dislocker.h" -#include "encryption/decrypt.h" -#include "sectors.h" -#include "metadata/metadata.h" -#include "file.h" - -#ifdef __DARWIN +#if defined(__DARWIN) || defined(__FREEBSD) # define O_LARGEFILE 0 -#endif /* __DARWIN */ +#endif /* __DARWIN || __FREEBSD */ -/** Data used globally for operation on disk (encryption/decryption) */ -extern dis_iodata_t disk_op_data; +/* Number of sectors we're reading at a time */ +#define NB_READ_SECTOR 16 -int file_main(char* ntfs_file) + +int file_main(char* ntfs_file, dis_context_t* dis_ctx) { // Check parameter if(!ntfs_file) @@ -51,11 +45,18 @@ int file_main(char* ntfs_file) return EXIT_FAILURE; } + if(!dis_ctx) + { + xprintf(L_ERROR, "Error, no context given. Abort.\n"); + return EXIT_FAILURE; + } - uint8_t* buffer = xmalloc((size_t)(NB_READ_SECTOR * disk_op_data.sector_size)); + dis_iodata_t io_data = dis_ctx->io_data; + size_t buf_size = (size_t)(NB_READ_SECTOR * io_data.sector_size); + uint8_t* buffer = xmalloc(buf_size); mode_t mode = S_IRUSR|S_IWUSR; - if(disk_op_data.cfg->is_ro & READ_ONLY) + if(dis_ctx->cfg.is_ro & READ_ONLY) mode = S_IRUSR; int fd_ntfs = xopen2(ntfs_file, O_CREAT|O_RDWR|O_LARGEFILE, mode); @@ -64,30 +65,24 @@ int file_main(char* ntfs_file) off_t offset = 0; long long int percent = 0; - xprintf(L_INFO, "File size: %llu bytes\n", disk_op_data.volume_size); + xprintf(L_INFO, "File size: %llu bytes\n", io_data.volume_size); /* Read all sectors and decrypt them if necessary */ xprintf(L_INFO, "\rDecrypting... 0%%"); fflush(stdout); - off_t decrypting_size = (off_t)disk_op_data.volume_size; + off_t decrypting_size = (off_t)io_data.volume_size; while(offset < decrypting_size) { /* Read and decrypt an entire region of the disk */ - disk_op_data.decrypt_region( - disk_op_data.volume_fd, - NB_READ_SECTOR, - disk_op_data.sector_size, - offset, - buffer - ); + dislock(dis_ctx, buffer, offset, buf_size); - offset += NB_READ_SECTOR * disk_op_data.sector_size; + offset += (off_t) buf_size; /* Now copy the required amount of data to the user file */ - xwrite(fd_ntfs, buffer, (size_t)(NB_READ_SECTOR * disk_op_data.sector_size)); + xwrite(fd_ntfs, buffer, buf_size); /* Screen update */ if(percent != (offset*100)/decrypting_size) @@ -105,3 +100,57 @@ int file_main(char* ntfs_file) return EXIT_SUCCESS; } + + + + +/** + * Main function ran initially + */ +int main(int argc, char** argv) +{ + // Check parameters number + if(argc < 2) + { + usage(); + exit(EXIT_FAILURE); + } + + int param_idx = 0; + int ret = 0; + + dis_context_t dis_ctx; + memset(&dis_ctx, 0, sizeof(dis_context_t)); + + + /* Get command line options */ + param_idx = parse_args(&dis_ctx.cfg, argc, argv); + + /* Check that we have the file where to put NTFS data */ + if(param_idx >= argc || param_idx <= 0) + { + fprintf(stderr, "Error, no file given. Abort.\n"); + return EXIT_FAILURE; + } + + /* Initialize dislocker */ + if(dis_initialize(&dis_ctx) == EXIT_FAILURE) + { + xprintf(L_CRITICAL, "Can't initialize dislocker. Abort.\n"); + return EXIT_FAILURE; + } + + /* + * Create a NTFS file which could be mounted using `mount -o loop...` + */ + + char* ntfs_file = argv[param_idx]; + xprintf(L_INFO, "Putting NTFS data into '%s'...\n", ntfs_file); + + /* Run the decryption */ + ret = file_main(ntfs_file, &dis_ctx); + + dis_destroy(&dis_ctx); + + return ret; +} diff --git a/src/dislocker-fuse.c b/src/dislocker-fuse.c new file mode 100644 index 0000000..541cbe7 --- /dev/null +++ b/src/dislocker-fuse.c @@ -0,0 +1,267 @@ +/* -*- coding: utf-8 -*- */ +/* -*- mode: c -*- */ +/* + * Dislocker -- enables to read/write on BitLocker encrypted partitions under + * Linux + * Copyright (C) 2012-2013 Romain Coltel, Hervé Schauer Consultants + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + + +#include +#include + +#include "dislocker.h" + + + +#ifdef __DARWIN +# include +#else +# include +#endif /* __DARWIN */ + + +/** NTFS virtual partition's name */ +#define NTFS_FILENAME "/dislocker-file" + + + +#include "common.h" +#include "encommon.h" +#include "dislocker.h" +#include "metadata/metadata.h" + + +/** + * Data used globally for operation on disk (encryption/decryption) and in the + * dislocker library. + */ +dis_context_t dis_ctx; + + +/** + * Stubs used for FUSE operations. + */ +static int fs_getattr(const char *path, struct stat *stbuf) +{ + int res = 0; + + if(!path || !stbuf) + return -EINVAL; + + memset(stbuf, 0, sizeof(struct stat)); + if(strcmp(path, "/") == 0) + { + stbuf->st_mode = S_IFDIR | 0555; + stbuf->st_nlink = 2; + } + else if(strcmp(path, NTFS_FILENAME) == 0) + { + mode_t m = (dis_ctx.cfg.is_ro & READ_ONLY) ? 0444 : 0666; + stbuf->st_mode = S_IFREG | m; + stbuf->st_nlink = 1; + stbuf->st_size = (off_t)dis_ctx.io_data.volume_size; + } + else + res = -ENOENT; + + return res; +} + +static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + /* Both variables aren't used here */ + (void) offset; + (void) fi; + + if(!path || !buf || !filler) + return -EINVAL; + + if(strcmp(path, "/") != 0) + return -ENOENT; + + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + filler(buf, NTFS_FILENAME + 1, NULL, 0); + + return 0; +} + +static int fs_open(const char *path, struct fuse_file_info *fi) +{ + if(!path || !fi) + return -EINVAL; + + if(strcmp(path, NTFS_FILENAME) != 0) + return -ENOENT; + + + if(dis_ctx.cfg.is_ro & READ_ONLY) + { + if((fi->flags & 3) != O_RDONLY) + return -EACCES; + } + else + { + /* Authorize read/write, readonly and writeonly operations */ + if((fi->flags & 3) != O_RDWR && + (fi->flags & 3) != O_RDONLY && + (fi->flags & 3) != O_WRONLY) + return -EACCES; + } + + return 0; +} + +static int fs_read( + const char *path, + char *buf, + size_t size, + off_t offset, + __attribute__ ((unused)) struct fuse_file_info *fi) +{ + if(!path || !buf) + return -EINVAL; + + /* + * Perform basic checks + */ + if(strcmp(path, NTFS_FILENAME) != 0) + { + xprintf(L_DEBUG, "Unknown entry requested: \"%s\"\n", path); + return -ENOENT; + } + + return dislock(&dis_ctx, (uint8_t*) buf, offset, size); +} + +static int fs_write( + const char *path, + const char *buf, + size_t size, + off_t offset, + __attribute__ ((unused)) struct fuse_file_info *fi) +{ + // Check parameters + if(!path || !buf) + return -EINVAL; + + + if(strcmp(path, NTFS_FILENAME) != 0) + { + xprintf(L_DEBUG, "Unknown entry requested: \"%s\"\n", path); + return -ENOENT; + } + + return enlock(&dis_ctx, (uint8_t*) buf, offset, size); +} + + +/* Structure used by the FUSE driver */ +struct fuse_operations fs_oper = { + .getattr = fs_getattr, + .readdir = fs_readdir, + .open = fs_open, + .read = fs_read, + .write = fs_write, +}; + + +/** + * Main function ran initially + */ +int main(int argc, char** argv) +{ + // Check parameters number + if(argc < 2) + { + usage(); + exit(EXIT_FAILURE); + } + + int param_idx = 0; + int ret = EXIT_SUCCESS; + + + memset(&dis_ctx, 0, sizeof(dis_context_t)); + + + /* Get command line options */ + param_idx = parse_args(&dis_ctx.cfg, argc, argv); + + /* Check we got enough arguments for at least one more, the mount point */ + if(param_idx >= argc || param_idx <= 0) + { + xprintf(L_CRITICAL, "Error, no mount point given. Abort.\n"); + return EXIT_FAILURE; + } + + + /* Initialize dislocker */ + if(dis_initialize(&dis_ctx) == EXIT_FAILURE) + { + xprintf(L_CRITICAL, "Can't initialize dislocker. Abort.\n"); + return EXIT_FAILURE; + } + + + /* + * Create the parameters table needed for FUSE and run it + * This is as we're running argv[0] followed by ARGS (see usage()) + */ + /* Compute the new argc given to FUSE */ + size_t new_argc = (size_t)(argc - param_idx + 1); + xprintf(L_DEBUG, "New value for argc: %d\n", new_argc); + + char** new_argv = xmalloc(new_argc * sizeof(char*)); + + /* Get argv[0] */ + size_t lg = strlen(argv[0]) + 1; + *new_argv = xmalloc(lg); + memcpy(*new_argv, argv[0], lg); + + /* Get all of the parameters from param_idx till the end */ + size_t loop = 0; + for(loop = 1; loop < new_argc; ++loop) + { + lg = strlen(argv[(size_t)param_idx + loop - 1]) + 1; + *(new_argv + loop) = xmalloc(lg); + memcpy(*(new_argv + loop), argv[(size_t)param_idx + loop - 1], lg); + } + + + xprintf(L_INFO, "Running FUSE with these arguments: \n"); + for(loop = 0; loop < new_argc; ++loop) + xprintf(L_INFO, " `--> '%s'\n", *(new_argv + loop)); + + + /* Run FUSE */ + ret = fuse_main((int)new_argc, new_argv, &fs_oper, NULL); + + /* Free FUSE params */ + for(loop = 0; loop < new_argc; ++loop) + xfree(new_argv[loop]); + xfree(new_argv); + + + /* Destroy dislocker structures */ + dis_destroy(&dis_ctx); + + return ret; +} diff --git a/src/dislocker-metadata.c b/src/dislocker-metadata.c index e1f8cc7..86d32b6 100644 --- a/src/dislocker-metadata.c +++ b/src/dislocker-metadata.c @@ -27,9 +27,9 @@ #define _GNU_SOURCE #include - #include +#include "dislocker.h" #include "common.h" #include "config.h" @@ -57,21 +57,13 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - int ret = EXIT_SUCCESS; - int optchar = 0; char *volume_path = NULL; - int fd = 0; - volume_header_t volume_header; - - void* bl_metadata = NULL; - bitlocker_dataset_t* dataset = NULL; datum_vmk_t* vmk_clear_key_datum = NULL; - dis_config_t cfg; - memset(&cfg, 0, sizeof(cfg)); + off_t offset = 0; while((optchar = getopt(argc, argv, "o:V:h")) != -1) { @@ -81,7 +73,7 @@ int main(int argc, char **argv) usage(); return EXIT_SUCCESS; case 'o': - cfg.offset = (off_t) strtoll(optarg, NULL, 10); + offset = (off_t) strtoll(optarg, NULL, 10); break; case 'V': volume_path = strdup(optarg); @@ -94,102 +86,65 @@ int main(int argc, char **argv) } } - xstdio_init(L_INFO, NULL); - if(!volume_path) { usage(); exit(EXIT_FAILURE); } - // Open the volume as a normal file - fd = xopen(volume_path, O_RDONLY|O_LARGEFILE); + dis_context_t dis_ctx; + memset(&dis_ctx, 0, sizeof(dis_context_t)); + /* + * Initialize dislocker's configuration + */ + dis_ctx.cfg.volume_path = volume_path; + dis_ctx.cfg.verbosity = L_INFO; + dis_ctx.cfg.offset = offset; - /* To print UTF-32 strings */ - setlocale(LC_ALL, ""); + /* We don't want to give decryption mean, we only want the metadata */ + dis_ctx.stop_at = AFTER_BITLOCKER_INFORMATION_CHECK; - - // Initialize structures - memset(&volume_header, 0, sizeof(volume_header_t)); - - - // Getting volume infos - if(!get_volume_header(&volume_header, fd, cfg.offset)) + /* Initialize dislocker */ + if(dis_initialize(&dis_ctx) == EXIT_FAILURE) { - xprintf(L_ERROR, "Error during reading the volume: not enough byte read.\n"); - ret = EXIT_FAILURE; - goto error; + xprintf(L_CRITICAL, "Can't initialize dislocker. Abort.\n"); + return EXIT_FAILURE; } - // Printing them - print_volume_header(L_INFO, &volume_header); - // Checking the volume signature - if(memcmp(BITLOCKER_SIGNATURE, volume_header.signature, - BITLOCKER_SIGNATURE_SIZE) != 0) - { - xprintf(L_CRITICAL, - "The signature of the volume (%.8s) doesn't match the " - "BitLocker's one (-FVE-FS-). Abort.\n", - volume_header.signature); - ret = EXIT_FAILURE; - goto error; - } - - // Getting BitLocker metadata and validate them - if(!get_metadata_check_validations(&volume_header, fd, &bl_metadata, &cfg)) - { - xprintf(L_CRITICAL, "A problem occured during the retrieving of metadata. Abort.\n"); - ret = EXIT_FAILURE; - goto error; - } - - if(cfg.force_block == 0 || !bl_metadata) - { - xprintf(L_CRITICAL, "Can't find a valid set of metadata on the disk. Abort.\n"); - ret = EXIT_FAILURE; - goto error; - } - - // Printing BitLocker metadata - print_bl_metadata(L_INFO, bl_metadata); + // Printing volume header + print_volume_header(L_INFO, dis_ctx.io_data.volume_header); xprintf(L_INFO, "\n"); + // Printing BitLocker metadata + print_bl_metadata(L_INFO, dis_ctx.io_data.metadata); + xprintf(L_INFO, "\n"); - // Now we're looking at the data - print_data(L_INFO, bl_metadata); + // Now we're looking at the data themselves + print_data(L_INFO, dis_ctx.io_data.metadata); // Get the metadata's dataset - if(!get_dataset(bl_metadata, &dataset)) + if(!get_dataset(dis_ctx.io_data.metadata, &dataset)) { xprintf(L_CRITICAL, "Can't find a valid dataset. Abort.\n"); - ret = EXIT_FAILURE; - goto error; + dis_destroy(&dis_ctx); + return EXIT_FAILURE; } // Search for a clear key if(has_clear_key(dataset, &vmk_clear_key_datum)) { - xprintf(L_INFO, "\n===== There's a clear key here!\n===== Take a look at it:\n"); + xprintf(L_INFO, "=======[ There's a clear key here ]========\n"); print_one_datum(L_INFO, (void*)vmk_clear_key_datum); - xprintf(L_INFO, "============[ Clear key end ]============\n"); + xprintf(L_INFO, "=============[ Clear key end ]=============\n"); } else xprintf(L_INFO, "No clear key found.\n"); -error: - // Do some cleaning stuff - if(volume_path) - xfree(volume_path); + dis_destroy(&dis_ctx); - if(bl_metadata) - xfree(bl_metadata); - - xclose(fd); - xstdio_end(); - - return ret; + return EXIT_SUCCESS; } diff --git a/src/dislocker.c b/src/dislocker.c index b4d4b45..0ceb306 100644 --- a/src/dislocker.c +++ b/src/dislocker.c @@ -29,7 +29,8 @@ #include "accesses/rp/recovery_password.h" #include "accesses/user_pass/user_pass.h" -#include "encommon.h" + +#include "sectors.h" #include "metadata/datums.h" #include "metadata/metadata.h" #include "metadata/print_metadata.h" @@ -41,9 +42,6 @@ #include "xstd/xstdio.h" #include "config.h" - - - #include "dislocker.h" #include @@ -59,29 +57,8 @@ -/** Data used globally for operation on disk (encryption/decryption) */ -extern dis_iodata_t disk_op_data; - - - -/** - * Main function ran initially - */ -int main(int argc, char** argv) +int dis_initialize(dis_context_t* dis_ctx) { - // Check parameters number - if(argc < 2) - { - usage(); - exit(EXIT_FAILURE); - } - - - int param_idx = 0; - - int fd_volume = 0; - - volume_header_t volume_header; void* bl_metadata = NULL; bitlocker_dataset_t* dataset= NULL; @@ -90,60 +67,68 @@ int main(int argc, char** argv) void* fvek_datum = NULL; datum_key_t* fvek_typed_datum = NULL; - contexts_t ctx; - memset(&ctx, 0, sizeof(ctx)); - int ret = EXIT_SUCCESS; - dis_config_t cfg; - memset(&cfg, 0, sizeof(cfg)); + dis_ctx->io_data.enc_ctx = xmalloc(sizeof(contexts_t)); + memset(dis_ctx->io_data.enc_ctx, 0, sizeof(contexts_t)); - /* Get command line options */ - param_idx = parse_args(&cfg, argc, argv); /* Initialize outputs */ - xstdio_init(cfg.verbosity, cfg.log_file); - - if(cfg.verbosity >= L_INFO) - print_args(&cfg); + xstdio_init(dis_ctx->cfg.verbosity, dis_ctx->cfg.log_file); + if(dis_ctx->cfg.verbosity >= L_DEBUG) + print_args(&dis_ctx->cfg); /* * Check parameters given */ - if(!cfg.volume_path) + if(!dis_ctx->cfg.volume_path) { usage(); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; + dis_destroy(dis_ctx); + return EXIT_FAILURE; } /* Open the volume as a (big) normal file */ - fd_volume = open(cfg.volume_path, O_RDWR|O_LARGEFILE); - if(fd_volume < 0) + xprintf(L_DEBUG, "Trying to open '%s'...\n", dis_ctx->cfg.volume_path); + dis_ctx->io_data.volume_fd = open(dis_ctx->cfg.volume_path, O_RDWR|O_LARGEFILE); + if(dis_ctx->io_data.volume_fd < 0) { /* Trying to open it in read-only if O_RDWR doesn't work */ - fd_volume = xopen(cfg.volume_path, O_RDONLY|O_LARGEFILE); - if(fd_volume < 0) + dis_ctx->io_data.volume_fd = xopen( + dis_ctx->cfg.volume_path, + O_RDONLY|O_LARGEFILE + ); + + if(dis_ctx->io_data.volume_fd < 0) { - xprintf(L_CRITICAL, - "Failed to open %s: %s\n", - cfg.volume_path, strerror(errno)); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; + xprintf( + L_CRITICAL, + "Failed to open %s: %s\n", + dis_ctx->cfg.volume_path, strerror(errno) + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; } - cfg.is_ro |= READ_ONLY; - xprintf(L_WARNING, - "Failed to open %s for writing. Falling back to read-only.\n", - cfg.volume_path); + + dis_ctx->cfg.is_ro |= READ_ONLY; + xprintf( + L_WARNING, + "Failed to open %s for writing. Falling back to read-only.\n", + dis_ctx->cfg.volume_path + ); } - else - xprintf(L_DEBUG, "Opened (fd #%d).\n", fd_volume); + xprintf(L_DEBUG, "Opened (fd #%d).\n", dis_ctx->io_data.volume_fd); + + if(dis_ctx->stop_at == AFTER_OPEN_VOLUME) + { + return EXIT_SUCCESS; + } /* To print UTF-32 strings */ @@ -157,262 +142,66 @@ int main(int argc, char** argv) xprintf(L_INFO, "Looking for BitLocker metadata...\n"); /* Initialize structures */ - memset(&volume_header, 0, sizeof(volume_header_t)); + dis_ctx->io_data.volume_header = xmalloc(sizeof(volume_header_t)); + memset(dis_ctx->io_data.volume_header, 0, sizeof(volume_header_t)); /* Getting volume infos */ - if(!get_volume_header(&volume_header, fd_volume, cfg.offset)) + if(!get_volume_header( + dis_ctx->io_data.volume_header, + dis_ctx->io_data.volume_fd, + dis_ctx->cfg.offset)) { - xprintf(L_CRITICAL, - "Error during reading the volume: not enough byte read.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; + xprintf( + L_CRITICAL, + "Error during reading the volume: not enough byte read.\n" + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; } /* For debug purpose, print the volume header retrieved */ - print_volume_header(L_DEBUG, &volume_header); + print_volume_header(L_DEBUG, dis_ctx->io_data.volume_header); + + if(dis_ctx->stop_at == AFTER_VOLUME_HEADER) + { + return EXIT_SUCCESS; + } /* Checking the signature */ - if(memcmp(BITLOCKER_SIGNATURE, volume_header.signature, + if(memcmp(BITLOCKER_SIGNATURE, dis_ctx->io_data.volume_header->signature, BITLOCKER_SIGNATURE_SIZE) != 0) { - xprintf(L_CRITICAL, - "The signature of the volume (%.8s) doesn't match the " - "BitLocker's one (-FVE-FS-). Abort.\n", - volume_header.signature); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; + xprintf( + L_CRITICAL, + "The signature of the volume (%.8s) doesn't match the " + "BitLocker's one (-FVE-FS-). Abort.\n", + dis_ctx->io_data.volume_header->signature + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; } /* Checking sector size */ - if(volume_header.sector_size == 0) + if(dis_ctx->io_data.volume_header->sector_size == 0) { xprintf(L_CRITICAL, "The sector size found is null. Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; + dis_destroy(dis_ctx); + return EXIT_FAILURE; } - /* Getting BitLocker metadata and validate them */ - if(!get_metadata_check_validations(&volume_header, fd_volume, &bl_metadata, - &cfg)) - { - xprintf(L_CRITICAL, - "A problem occured during the retrieving of metadata. Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - if(cfg.force_block == 0 || !bl_metadata) - { - xprintf(L_CRITICAL, - "Can't find a valid set of metadata on the disk. Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - /* Checking BitLocker version */ - if(((bitlocker_header_t*)bl_metadata)->version > V_SEVEN) - { - xprintf(L_CRITICAL, - "Program designed only for BitLocker version 2 and less, " - "the version here is %hd. Abort.\n", - ((bitlocker_header_t*)bl_metadata)->version); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - - xprintf(L_INFO, "BitLocker metadata found and parsed.\n"); - - /* For debug purpose, print the metadata */ - print_bl_metadata(L_DEBUG, bl_metadata); - print_data(L_DEBUG, bl_metadata); - - /* Now that we have the metadata, get the dataset within it */ - if(get_dataset(bl_metadata, &dataset) != TRUE) - { - xprintf(L_CRITICAL, "Unable to find a valid dataset. Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - /* - * If the state of the volume is currently decrypted, there's no key to grab - */ - if(((bitlocker_header_t*)bl_metadata)->curr_state == DECRYPTED) - goto FIRST_CLEAN; - - /* - * First, get the VMK datum using either a clear key, a recovery password - * or a bek file - */ - while(cfg.decryption_mean) - { - if(cfg.decryption_mean & USE_CLEAR_KEY) - { - if(!get_vmk_from_clearkey(dataset, &vmk_datum)) - { - cfg.decryption_mean &= (unsigned) ~USE_CLEAR_KEY; - } - else - { - xprintf(L_INFO, "Used clear key decryption method\n"); - cfg.decryption_mean = USE_CLEAR_KEY; - break; - } - } - else if(cfg.decryption_mean & USE_USER_PASSWORD) - { - if(!get_vmk_from_user_pass(dataset, &cfg, &vmk_datum)) - { - cfg.decryption_mean &= (unsigned) ~USE_USER_PASSWORD; - } - else - { - xprintf(L_INFO, "Used user password decryption method\n"); - cfg.decryption_mean = USE_USER_PASSWORD; - break; - } - } - else if(cfg.decryption_mean & USE_RECOVERY_PASSWORD) - { - if(!get_vmk_from_rp(dataset, &cfg, &vmk_datum)) - { - cfg.decryption_mean &= (unsigned) ~USE_RECOVERY_PASSWORD; - } - else - { - xprintf(L_INFO, "Used recovery password decryption method\n"); - cfg.decryption_mean = USE_RECOVERY_PASSWORD; - break; - } - } - else if(cfg.decryption_mean & USE_BEKFILE) - { - if(!get_vmk_from_bekfile(dataset, &cfg, &vmk_datum)) - { - cfg.decryption_mean &= (unsigned) ~USE_BEKFILE; - } - else - { - xprintf(L_INFO, "Used bek file decryption method\n"); - cfg.decryption_mean = USE_BEKFILE; - break; - } - } - else if(cfg.decryption_mean & USE_FVEKFILE) - { - if(!build_fvek_from_file(&cfg, &fvek_datum)) - { - cfg.decryption_mean &= (unsigned) ~USE_FVEKFILE; - } - else - { - xprintf(L_INFO, "Used FVEK file decryption method\n"); - cfg.decryption_mean = USE_FVEKFILE; - break; - } - } - else - { - xprintf(L_CRITICAL, "Wtf!? Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - } - - if(!cfg.decryption_mean) - { - xprintf(L_CRITICAL, "None of the provided decryption mean is " - "decrypting the keys. Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - - /* - * NOTE -- We could here validate bl_metadata in a more precise way - * using the VMK and the validations infos after the informations - * - * NOTE -- We could here get all of the other key a user could use - * using the VMK and the reverse encrypted data - */ - - - /* - * And then, use the VMK to decrypt the FVEK - */ - if(cfg.decryption_mean != USE_FVEKFILE) - { - if(!get_fvek(dataset, vmk_datum, &fvek_datum)) - { - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - } - - - /* Just a check of the algo used to crypt data here */ - fvek_typed_datum = (datum_key_t*) fvek_datum; - fvek_typed_datum->algo &= 0xffff; - - if(fvek_typed_datum->algo < AES_128_DIFFUSER || - fvek_typed_datum->algo > AES_256_NO_DIFFUSER) - { - xprintf(L_CRITICAL, - "Can't recognize the encryption algorithm used: %#x. Abort\n", - fvek_typed_datum->algo); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - - - /* - * Init the decrypt keys' contexts - */ - if(!init_keys(dataset, fvek_typed_datum, &ctx)) - { - xprintf(L_CRITICAL, "Can't initialize keys. Abort.\n"); - ret = EXIT_FAILURE; - goto FIRST_CLEAN; - } - - - - /* - * We have everything we need to run in fuse or whatever now - * Let's clean things we don't need here - */ - ret = EXIT_SUCCESS; - -FIRST_CLEAN: - - /* Free them all! */ - if(vmk_datum) - memclean(vmk_datum, - ((datum_generic_type_t*)vmk_datum)->header.datum_size); - - if(fvek_typed_datum) - memclean(fvek_datum, fvek_typed_datum->header.datum_size); - - /* Goto finnishing to clean everything before returning */ - if(ret == EXIT_FAILURE) - goto LAST_CLEAN; - - /* Check if we're running under EOW mode */ - extern guid_t EOW_INFORMATION_OFFSET_GUID; + extern guid_t INFORMATION_OFFSET_GUID, EOW_INFORMATION_OFFSET_GUID; - if(check_match_guid(volume_header.guid, EOW_INFORMATION_OFFSET_GUID)) + if(check_match_guid(dis_ctx->io_data.volume_header->guid, EOW_INFORMATION_OFFSET_GUID)) { xprintf(L_INFO, "Volume has EOW_INFORMATION_OFFSET_GUID.\n"); // First: get the EOW informations no matter what - off_t source = (off_t)volume_header.offset_eow_information[0]; + off_t source = (off_t)dis_ctx->io_data.volume_header->offset_eow_information[0]; void* eow_infos = NULL; - if(get_eow_information(source, &eow_infos, fd_volume)) + if(get_eow_information(source, &eow_infos, dis_ctx->io_data.volume_fd)) { // Second: print them print_eow_infos(L_DEBUG, (bitlocker_eow_infos_t*)eow_infos); @@ -420,7 +209,7 @@ FIRST_CLEAN: xfree(eow_infos); // Thid: check if this struct passes checks - if(get_eow_check_valid(&volume_header, fd_volume, &eow_infos, &cfg)) + if(get_eow_check_valid(dis_ctx->io_data.volume_header, dis_ctx->io_data.volume_fd, &eow_infos, &dis_ctx->cfg)) { xprintf(L_INFO, "EOW information at offset % " F_OFF_T @@ -440,6 +229,247 @@ FIRST_CLEAN: "Getting EOW information at offset % " F_OFF_T " failed\n", source); } + + xprintf(L_CRITICAL, "EOW volume GUID not supported.\n"); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + else if(check_match_guid(dis_ctx->io_data.volume_header->guid, INFORMATION_OFFSET_GUID)) + { + xprintf(L_INFO, "Volume GUID supported\n"); + } + else + { + xprintf(L_CRITICAL, "Unknown volume GUID not supported.\n"); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + if(dis_ctx->stop_at == AFTER_VOLUME_CHECK) + { + return EXIT_SUCCESS; + } + + + /* Getting BitLocker metadata and validate them */ + // TODO give the whole dis_ctx here for stopping at first INFORMATION (for stop_at) + if(!get_metadata_check_validations( + dis_ctx->io_data.volume_header, + dis_ctx->io_data.volume_fd, + &bl_metadata, + &dis_ctx->cfg)) + { + xprintf( + L_CRITICAL, + "A problem occured during the retrieving of metadata. Abort.\n" + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + if(dis_ctx->cfg.force_block == 0 || !bl_metadata) + { + xprintf( + L_CRITICAL, + "Can't find a valid set of metadata on the disk. Abort.\n" + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + /* Checking BitLocker version */ + if(((bitlocker_header_t*)bl_metadata)->version > V_SEVEN) + { + xprintf( + L_CRITICAL, + "Program designed only for BitLocker version 2 and less, " + "the version here is %hd. Abort.\n", + ((bitlocker_header_t*)bl_metadata)->version + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + xprintf(L_INFO, "BitLocker metadata found and parsed.\n"); + + /* For debug purpose, print the metadata */ + print_bl_metadata(L_DEBUG, bl_metadata); + print_data(L_DEBUG, bl_metadata); + + dis_ctx->io_data.metadata = bl_metadata; + + if(dis_ctx->stop_at == AFTER_BITLOCKER_INFORMATION_CHECK) + { + return EXIT_SUCCESS; + } + + + /* Now that we have the metadata, get the dataset within it */ + if(get_dataset(bl_metadata, &dataset) != TRUE) + { + xprintf(L_CRITICAL, "Unable to find a valid dataset. Abort.\n"); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + /* + * If the state of the volume is currently decrypted, there's no key to grab + */ + if(((bitlocker_header_t*)bl_metadata)->curr_state == DECRYPTED) + return EXIT_SUCCESS; + + + /* + * First, get the VMK datum using either any necessary mean + */ + while(dis_ctx->cfg.decryption_mean) + { + if(dis_ctx->cfg.decryption_mean & USE_CLEAR_KEY) + { + if(!get_vmk_from_clearkey(dataset, &vmk_datum)) + { + dis_ctx->cfg.decryption_mean &= (unsigned) ~USE_CLEAR_KEY; + } + else + { + xprintf(L_INFO, "Used clear key decryption method\n"); + dis_ctx->cfg.decryption_mean = USE_CLEAR_KEY; + break; + } + } + else if(dis_ctx->cfg.decryption_mean & USE_USER_PASSWORD) + { + if(!get_vmk_from_user_pass(dataset, &dis_ctx->cfg, &vmk_datum)) + { + dis_ctx->cfg.decryption_mean &= (unsigned) ~USE_USER_PASSWORD; + } + else + { + xprintf(L_INFO, "Used user password decryption method\n"); + dis_ctx->cfg.decryption_mean = USE_USER_PASSWORD; + break; + } + } + else if(dis_ctx->cfg.decryption_mean & USE_RECOVERY_PASSWORD) + { + if(!get_vmk_from_rp(dataset, &dis_ctx->cfg, &vmk_datum)) + { + dis_ctx->cfg.decryption_mean &= (unsigned) ~USE_RECOVERY_PASSWORD; + } + else + { + xprintf(L_INFO, "Used recovery password decryption method\n"); + dis_ctx->cfg.decryption_mean = USE_RECOVERY_PASSWORD; + break; + } + } + else if(dis_ctx->cfg.decryption_mean & USE_BEKFILE) + { + if(!get_vmk_from_bekfile(dataset, &dis_ctx->cfg, &vmk_datum)) + { + dis_ctx->cfg.decryption_mean &= (unsigned) ~USE_BEKFILE; + } + else + { + xprintf(L_INFO, "Used bek file decryption method\n"); + dis_ctx->cfg.decryption_mean = USE_BEKFILE; + break; + } + } + else if(dis_ctx->cfg.decryption_mean & USE_FVEKFILE) + { + if(!build_fvek_from_file(&dis_ctx->cfg, &fvek_datum)) + { + dis_ctx->cfg.decryption_mean &= (unsigned) ~USE_FVEKFILE; + } + else + { + xprintf(L_INFO, "Used FVEK file decryption method\n"); + dis_ctx->cfg.decryption_mean = USE_FVEKFILE; + break; + } + } + else + { + xprintf(L_CRITICAL, "Wtf!? Abort.\n"); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + } + + if(!dis_ctx->cfg.decryption_mean) + { + xprintf( + L_CRITICAL, + "None of the provided decryption mean is " + "decrypting the keys. Abort.\n" + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + dis_ctx->io_data.vmk = vmk_datum; + + if(dis_ctx->stop_at == AFTER_VMK) + { + return EXIT_SUCCESS; + } + + + /* + * NOTE -- We could here validate bl_metadata in a more precise way + * using the VMK and the validations infos after the informations + * + * NOTE -- We could here get all of the other key a user could use + * using the VMK and the reverse encrypted data + */ + + + /* + * And then, use the VMK to decrypt the FVEK + */ + if(dis_ctx->cfg.decryption_mean != USE_FVEKFILE) + { + if(!get_fvek(dataset, vmk_datum, &fvek_datum)) + { + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + } + + + /* Just a check of the algo used to crypt data here */ + fvek_typed_datum = (datum_key_t*) fvek_datum; + fvek_typed_datum->algo &= 0xffff; + + if(fvek_typed_datum->algo < AES_128_DIFFUSER || + fvek_typed_datum->algo > AES_256_NO_DIFFUSER) + { + xprintf( + L_CRITICAL, + "Can't recognize the encryption algorithm used: %#x. Abort\n", + fvek_typed_datum->algo + ); + dis_destroy(dis_ctx); + return EXIT_FAILURE; + } + + dis_ctx->io_data.fvek = fvek_typed_datum; + + if(dis_ctx->stop_at == AFTER_FVEK) + { + return EXIT_SUCCESS; + } + + + /* + * Init the decrypt keys' contexts + */ + if(!init_keys(dataset, fvek_typed_datum, dis_ctx->io_data.enc_ctx)) + { + xprintf(L_CRITICAL, "Can't initialize keys. Abort.\n"); + dis_destroy(dis_ctx); + return EXIT_FAILURE; } @@ -447,113 +477,424 @@ FIRST_CLEAN: * Fill the dis_iodata_t structure which will be used for encryption & * decryption afterward */ - if(!prepare_crypt((bitlocker_header_t*) bl_metadata, &ctx, &cfg, - &volume_header, cfg.offset, fd_volume)) + if(!prepare_crypt(dis_ctx)) { xprintf(L_CRITICAL, "Can't prepare the crypt structure. Abort.\n"); ret = EXIT_FAILURE; - goto LAST_CLEAN; } -#if defined(__RUN_FUSE) - /* Check the state the BitLocker volume is in */ - if(cfg.check_state == TRUE && !check_state((bitlocker_header_t*) bl_metadata)) - { - xprintf(L_CRITICAL, "Invalid state, can't run safely. Abort.\n"); - ret = EXIT_FAILURE; - goto LAST_CLEAN; - } - - /** @see fuse.c */ - extern struct fuse_operations fs_oper; - - /* - * Create the parameters table needed for FUSE and run it - * This is as we're running argv[0] followed by ARGS (see usage()) - */ - - /* Get the new value for argc */ - if(param_idx >= argc || param_idx <= 0) - { - xprintf(L_CRITICAL, "Error, no mount point given. Abort.\n"); - ret = EXIT_FAILURE; - goto LAST_CLEAN; - } - - size_t new_argc = (size_t)(argc - param_idx + 1); - xprintf(L_DEBUG, "New value for argc: %d\n", new_argc); - - char** new_argv = xmalloc(new_argc * sizeof(char*)); + // TODO add the BEFORE_DECRYPTION_CHECKING event here, so add the check here too - /* Get argv[0] */ - size_t lg = strlen(argv[0]) + 1; - *new_argv = xmalloc(lg); - memcpy(*new_argv, argv[0], lg); - - - /* Get all of the parameters from param_idx till the end */ - size_t loop = 0; - for(loop = 1; loop < new_argc; ++loop) - { - lg = strlen(argv[(size_t)param_idx + loop - 1]) + 1; - *(new_argv + loop) = xmalloc(lg); - memcpy(*(new_argv + loop), argv[(size_t)param_idx + loop - 1], lg); - } - - - xprintf(L_INFO, "Running FUSE with these arguments: \n"); - for(loop = 0; loop < new_argc; ++loop) - xprintf(L_INFO, " `--> '%s'\n", *(new_argv + loop)); - - - /* Run FUSE */ - ret = fuse_main((int)new_argc, new_argv, &fs_oper, NULL); - - /* Free FUSE params */ - for(loop = 0; loop < new_argc; ++loop) - xfree(new_argv[loop]); - xfree(new_argv); - -#elif defined(__RUN_FILE) - - /* - * Create a NTFS file which could be mounted using `mount -o loop...` - */ - - /* Check that we have the file where to put NTFS data */ - if(argc <= param_idx) - { - xprintf(L_CRITICAL, "Error, no file given. Abort.\n"); - ret = EXIT_FAILURE; - goto LAST_CLEAN; - } - - char* ntfs_file = argv[param_idx]; - xprintf(L_INFO, "Putting NTFS data into '%s'...\n", ntfs_file); - - /* Run the decryption */ - ret = file_main(ntfs_file); - -#else /* no __RUN_FUSE, nor __RUN_FILE */ - xprintf(L_ERROR, "Neither __RUN_FILE nor __RUN_FUSE was enabled. " - "Nothing to do.\n"); -#endif - -LAST_CLEAN: - /* Finnish cleaning things */ - if(bl_metadata) - xfree(bl_metadata); - - pthread_mutex_destroy(&disk_op_data.mutex_lseek_rw); - - free_args(&cfg); - - xclose(fd_volume); - - xstdio_end(); - + /* Clean everything before returning if there's an error */ + if(ret == EXIT_FAILURE) + dis_destroy(dis_ctx); return ret; } + + + + +int dislock(dis_context_t* dis_ctx, uint8_t* buffer, off_t offset, size_t size) +{ + uint8_t* buf = NULL; + + size_t sector_count; + off_t sector_start; + size_t sector_to_add = 0; + uint16_t sector_size = dis_ctx->io_data.sector_size; + + + /* Check the state the BitLocker volume is in */ + if(dis_ctx->cfg.dont_check_state == FALSE && + !check_state(dis_ctx->io_data.metadata)) + { + xprintf(L_ERROR, "Invalid state, can't run safely. Abort.\n"); + return -EFAULT; + } + + /* Check requested size */ + if(size == 0) + { + xprintf(L_DEBUG, "Received a request with a null size\n"); + return 0; + } + + /* Check requested offset */ + if(offset < 0) + { + xprintf(L_ERROR, "Offset under 0: %#" F_OFF_T "\n", offset); + return -EFAULT; + } + + if(offset >= (off_t)dis_ctx->io_data.volume_size) + { + xprintf( + L_ERROR, + "Offset (%#" F_OFF_T ") exceeds volume's size (%#" F_OFF_T ")\n", + offset, + (off_t)dis_ctx->io_data.volume_size + ); + return -EFAULT; + } + + + /* + * The offset may not be at a sector limit, so we need to decrypt the entire + * sector where it starts. Idem for the end. + * + * + * Example: + * Sector number: 1 2 3 4 5 6 7... + * Data, continuous sectors: |___|___|___|___|___|___|__... + * The data the user want: |__________| + * + * The user don't want all of the data from sectors 2 and 5, but as the data + * are encrypted sector by sector, we have to decrypt them even though we + * won't give him the beginning of the sector 2 and the end of the sector 5. + * + * + * + * Logic to do this is below : + * - count the number of full sectors + * - decode all sectors + * - select and copy the data to user and deallocate all buffers + */ + + /* Do not add sectors if we're at the edge of one already */ + if((offset % sector_size) != 0) + sector_to_add += 1; + if(((offset + (off_t)size) % sector_size) != 0) + sector_to_add += 1; + + sector_count = ( size / sector_size ) + sector_to_add; + sector_start = offset / sector_size; + + xprintf(L_DEBUG, + "--------------------{ Fuse reading }-----------------------\n"); + xprintf(L_DEBUG, " Offset and size needed: %#" F_OFF_T + " and %#" F_SIZE_T "\n", offset, size); + xprintf(L_DEBUG, " Start sector number: %#" F_OFF_T + " || Number of sectors: %#" F_SIZE_T "\n", + sector_start, sector_count); + + + /* + * NOTE: DO NOT use xmalloc() here, we don't want to mess everything up! + * In general, do not use xfunctions() but xprintf() here. + */ + + size_t to_allocate = size + sector_to_add*sector_size; + xprintf(L_DEBUG, " Trying to allocate %#" F_SIZE_T " bytes\n",to_allocate); + buf = malloc(to_allocate); + + /* If buffer could not be allocated, return an error */ + if(!buf) + { + xprintf(L_ERROR, "Cannot allocate buffer for reading, abort.\n"); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + if(errno < 0) + return errno; + else + return -ENOMEM; + } + + + if(!dis_ctx->io_data.decrypt_region( + &dis_ctx->io_data, + sector_count, + sector_size, + sector_start * sector_size, + buf)) + { + free(buf); + xprintf(L_ERROR, "Cannot decrypt sectors, abort.\n"); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + return -EIO; + } + + /* Now copy the required amount of data to the user buffer */ + memcpy(buffer, buf + (offset % sector_size), size); + + free(buf); + + xprintf(L_DEBUG, " Outsize which will be returned: %d\n", (int)size); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + + return (int)size; +} + + + + +int enlock(dis_context_t* dis_ctx, uint8_t* buffer, off_t offset, size_t size) +{ + uint8_t* buf = NULL; + int ret = 0; + + uint16_t sector_size = dis_ctx->io_data.sector_size; + size_t sector_count; + off_t sector_start; + size_t sector_to_add = 0; + + + /* Perform basic checks */ + if(dis_ctx->cfg.is_ro & READ_ONLY) + { + xprintf(L_DEBUG, "Only decrypting (-r or --read-only option passed)\n"); + return -EACCES; + } + + if(size == 0) + { + xprintf(L_DEBUG, "Received a request with a null size\n"); + return 0; + } + + if(offset < 0) + { + xprintf(L_ERROR, "Offset under 0: %#" F_OFF_T "\n", offset); + return -EFAULT; + } + + if(offset >= (off_t)dis_ctx->io_data.volume_size) + { + xprintf(L_ERROR, "Offset (%#" F_OFF_T ") exceeds volume's size (%#" + F_OFF_T ")\n", + offset, (off_t)dis_ctx->io_data.volume_size); + return -EFAULT; + } + + if((size_t)offset + size >= (size_t)dis_ctx->io_data.volume_size) + { + size_t nsize = (size_t)dis_ctx->io_data.volume_size + - (size_t)offset; + xprintf(L_WARNING, "Size modified as exceeding volume's end (offset=%#" + F_SIZE_T " + size=%#" F_SIZE_T " >= volume_size=%#" + F_SIZE_T ") ; new size: %#" F_SIZE_T "\n", + (size_t)offset, size, (size_t)dis_ctx->io_data.volume_size, nsize); + size = nsize; + } + + + /* + * Don't authorize to write on metadata, NTFS firsts sectors and on another + * area we shouldn't write to (don't know its signification yet). + */ + off_t metadata_offset = 0; + off_t metadata_size = 0; + size_t virt_loop = 0; + + for(virt_loop = 0; virt_loop < dis_ctx->io_data.nb_virt_region; virt_loop++) + { + metadata_size = (off_t)dis_ctx->io_data.virt_region[virt_loop].size; + if(metadata_size == 0) + continue; + + metadata_offset = (off_t)dis_ctx->io_data.virt_region[virt_loop].addr; + + if(offset >= metadata_offset && + offset <= metadata_offset + metadata_size) + { + xprintf(L_INFO, "Denying write request on the metadata (1:%#" + F_OFF_T ")\n", offset); + return -EFAULT; + } + + if(offset < metadata_offset && + offset + (off_t)size >= metadata_offset) + { + xprintf(L_INFO, "Denying write request on the metadata (2:%#" + F_OFF_T "+ %#" F_SIZE_T ")\n", offset, size); + return -EFAULT; + } + } + + + /* + * For BitLocker 7's volume, redirect writes to firsts sectors to the backed + * up ones + */ + if(dis_ctx->io_data.metadata->version == V_SEVEN && + offset < dis_ctx->io_data.virtualized_size) + { + xprintf(L_DEBUG, " Entering virtualized area\n"); + if(offset + (off_t)size <= dis_ctx->io_data.virtualized_size) + { + /* + * If all the request is within the virtualized area, just change + * the offset + */ + offset = offset + (off_t)dis_ctx->io_data.metadata->boot_sectors_backup; + xprintf(L_DEBUG, " `-> Just redirecting to %#"F_OFF_T"\n", offset); + } + else + { + /* + * But if the buffer is within the virtualized area and overflow it, + * split the request in two: + * - One for the virtualized area completely (which will be handled + * by "recursing" and entering the case above) + * - One for the rest by changing the offset to the end of the + * virtualized area and the size to the rest to be dec/encrypted + */ + xprintf(L_DEBUG, " `-> Splitting the request in two, recursing\n"); + + size_t nsize = (size_t)(dis_ctx->io_data.virtualized_size - offset); + ret = enlock(dis_ctx, buffer, offset, nsize); + if(ret < 0) + return ret; + + offset = dis_ctx->io_data.virtualized_size; + size -= nsize; + buffer += nsize; + } + } + + + /* + * As in the read function, the offset may not be at a sector limit, so we + * need to decrypt the entire sector where it starts till the entire sector + * where it ends, then push the changes into the sectors at correct offset + * and finally encrypt all of these sectors and write them back to the disk. + * + * + * Example: + * Sector number: 1 2 3 4 5 6 7... + * Data, continuous sectors: |___|___|___|___|___|___|__... + * Where the user want to write: |__________| + * + * The user don't want to write everywhere, just from the middle of sector 2 + * till a part of sector 5. But we're writing sectors by sectors to be able + * to encrypt using AES. So we'll need entire sectors 2 to 5 included. + * + * + * + * Logic to do this is below : + * - read and decrypt all sectors completely (2 to 5 in the example above) + * - replace some data by the user's one + * - encrypt and write the read sectors + */ + + /* Do not add sectors if we're at the edge of one already */ + if((offset % sector_size) != 0) + sector_to_add += 1; + if(((offset + (off_t)size) % sector_size) != 0) + sector_to_add += 1; + + + sector_count = ( size / sector_size ) + sector_to_add; + sector_start = offset / sector_size; + + xprintf(L_DEBUG, + "--------------------{ Fuse writing }-----------------------\n"); + xprintf(L_DEBUG, " Offset and size requested: %#" F_OFF_T " and %#" + F_SIZE_T "\n", offset, size); + xprintf(L_DEBUG, " Start sector number: %#" F_OFF_T + " || Number of sectors: %#" F_SIZE_T "\n", + sector_start, sector_count); + + + /* + * NOTE: DO NOT use xmalloc() here, we don't want to mess everything up! + * In general, do not use xfunctions() but xprintf() here. + */ + + buf = malloc(size + sector_to_add * (size_t)sector_size); + + /* If buffer could not be allocated */ + if(!buf) + { + xprintf(L_ERROR, "Cannot allocate buffer for writing, abort.\n"); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + return -ENOMEM; + } + + + if(!dis_ctx->io_data.decrypt_region( + &dis_ctx->io_data, + sector_count, + sector_size, + sector_start * sector_size, + buf + )) + { + free(buf); + xprintf(L_ERROR, "Cannot decrypt sectors, abort.\n"); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + return -EIO; + } + + + /* Now copy the user's buffer to the received data */ + memcpy(buf + (offset % sector_size), buffer, size); + + + /* Finally, encrypt the buffer and write it to the disk */ + if(!dis_ctx->io_data.encrypt_region( + &dis_ctx->io_data, + sector_count, + sector_size, + sector_start * sector_size, + buf + )) + { + free(buf); + xprintf(L_ERROR, "Cannot encrypt sectors, abort.\n"); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + return -EIO; + } + + + free(buf); + + + /* Note that ret is zero when no recursion occurs */ + int outsize = (int)size + ret; + + xprintf(L_DEBUG, " Outsize which will be returned: %d\n", outsize); + xprintf(L_DEBUG, + "-----------------------------------------------------------\n"); + + return outsize; +} + + + + +int dis_destroy(dis_context_t* dis_ctx) +{ + /* Finish cleaning things */ + if(dis_ctx->io_data.metadata) + xfree(dis_ctx->io_data.metadata); + + if(dis_ctx->io_data.volume_header) + xfree(dis_ctx->io_data.volume_header); + + if(dis_ctx->io_data.vmk) + xfree(dis_ctx->io_data.vmk); + + if(dis_ctx->io_data.fvek) + xfree(dis_ctx->io_data.fvek); + + if(dis_ctx->io_data.enc_ctx) + xfree(dis_ctx->io_data.enc_ctx); + + pthread_mutex_destroy(&dis_ctx->io_data.mutex_lseek_rw); + + free_args(&dis_ctx->cfg); + + xclose(dis_ctx->io_data.volume_fd); + + xstdio_end(); + + return EXIT_SUCCESS; +} diff --git a/src/dislocker.h b/src/dislocker.h index 873b374..291f881 100644 --- a/src/dislocker.h +++ b/src/dislocker.h @@ -23,14 +23,96 @@ #ifndef DISLOCKER_MAIN_H #define DISLOCKER_MAIN_H +#include -#if defined(__RUN_FUSE) -# include "outputs/fuse/fuse.h" -#elif defined(__RUN_FILE) -# include "outputs/file/file.h" -#endif +#include "config.h" +#include "encommon.h" +/** + * dis_initialize() function does a lot of things. So, in order to provide + * flexibility, place some kind of breakpoint after majors steps. + */ +typedef enum { + COMPLETE_EVERYTHING = 0, + AFTER_OPEN_VOLUME, + AFTER_VOLUME_HEADER, + AFTER_VOLUME_CHECK, + AFTER_BITLOCKER_INFORMATION, + AFTER_BITLOCKER_INFORMATION_CHECK, + AFTER_VMK, + AFTER_FVEK, + BEFORE_DECRYPTION_CHECKING, +} dis_stopat_e; + + +/** + * Main structure to pass to dislocker functions. These keeps various + * information in it. + */ +typedef struct _dis_ctx { + + dis_config_t cfg; + + dis_iodata_t io_data; + + dis_stopat_e stop_at; +} dis_context_t; + + + +/** + * Public prototypes + */ + +/** + * Initialize dislocker. As stated above, the initialisation process may be + * stopped at any major step in order to retrieve different information. Note + * that you have to provide an already allocated dis_ctx with an already filled + * dis_ctx->cfg with parameters for dislocker to initialize correctly. + * This function malloc(3)s structures, see dis_destroy() below for free(3)ing + * it. + * dislock() & enlock() function may not be called before executing this + * function. + * + * @param dis_ctx The dislocker context needed for all operations. As stated + * above, this parameter has to be pre-allocated. Furthermore, the dis_ctx->cfg + * structure has to be filled with parameters to properly initialize dislocker. + */ +int dis_initialize(dis_context_t* dis_ctx); + +/** + * Once dis_initialize() has been called, this function is able to decrypt the + * BitLocker-encrypted volume. + * + * @param dis_ctx The same parameter passed to dis_initialize. + * @param offset The offset from where to start decrypting. + * @param buffer The buffer to put decrypted data to. + * @param size The size of a region to decrypt. + */ +int dislock(dis_context_t* dis_ctx, uint8_t* buffer, off_t offset, size_t size); + +/** + * Once dis_initialize() has been called, this function is able to encrypt data + * to the BitLocker-encrypted volume. + * + * @param dis_ctx The same parameter passed to dis_initialize. + * @param offset The offset where to put the data. + * @param buffer The buffer from where to take data to encrypt. + * @param size The size of a region to decrypt. + */ +int enlock(dis_context_t* dis_ctx, uint8_t* buffer, off_t offset, size_t size); + +/** + * Destroy dislocker structures. This is important to call this function after + * dislocker is not needed -- if dis_initialize() has been called -- in order + * for dislocker to free(3) the used memory. + * dislock() & enlock() functions may not be called anymore after executing this + * function. + */ +int dis_destroy(dis_context_t* dis_ctx); + + #endif /* DISLOCKER_MAIN_H */ diff --git a/src/encommon.h b/src/encommon.h index 90fbae4..054ac93 100644 --- a/src/encommon.h +++ b/src/encommon.h @@ -26,6 +26,7 @@ #include "common.h" #include "config.h" +#include "metadata/vmk.h" #include "metadata/metadata.h" #include "metadata/extended_info.h" @@ -59,6 +60,15 @@ typedef struct _data { /* Volume metadata */ bitlocker_header_t* metadata; + /* The volume header, 512 bytes */ + volume_header_t* volume_header; + + /* The VMK */ + datum_key_t* vmk; + + /* The FVEK */ + datum_key_t* fvek; + /* * Virtualized regions are presented as zeroes when queried from the NTFS * layer. In these virtualized regions, we find the 3 BitLocker metadata @@ -88,17 +98,29 @@ typedef struct _data { int volume_fd; /* Contexts used to encrypt or decrypt */ - contexts_t* ctx; - - /* Configuration parameters */ - dis_config_t* cfg; + contexts_t* enc_ctx; /* Function to decrypt a region of the volume */ - int(*decrypt_region)(int fd, size_t nb_read_sector, uint16_t sector_size, off_t sector_start, uint8_t* output); + int(*decrypt_region)( + struct _data* io_data, + size_t nb_read_sector, + uint16_t sector_size, + off_t sector_start, + uint8_t* output + ); /* Function to encrypt a region of the volume */ - int(*encrypt_region)(int fd, size_t nb_write_sector, uint16_t sector_size, off_t sector_start, uint8_t* input); + int(*encrypt_region)( + struct _data* io_data, + size_t nb_write_sector, + uint16_t sector_size, + off_t sector_start, + uint8_t* input + ); - /* FUSE uses threads. We need to protect our "lseek/read" and "lseek/write" sequences */ + /* + * FUSE uses threads. We need to protect our "lseek/read" and "lseek/write" + * sequences + */ pthread_mutex_t mutex_lseek_rw; } dis_iodata_t; diff --git a/src/encryption/decrypt.c b/src/encryption/decrypt.c index 9e37dd1..509f4ee 100644 --- a/src/encryption/decrypt.c +++ b/src/encryption/decrypt.c @@ -402,11 +402,11 @@ int decrypt_sector(dis_iodata_t* global_data, uint8_t* sector, off_t sector_addr { case AES_128_DIFFUSER: case AES_256_DIFFUSER: - decrypt_with_diffuser(global_data->ctx, global_data->sector_size, sector, sector_address, buffer); + decrypt_with_diffuser(global_data->enc_ctx, global_data->sector_size, sector, sector_address, buffer); break; case AES_128_NO_DIFFUSER: case AES_256_NO_DIFFUSER: - decrypt_without_diffuser(global_data->ctx, global_data->sector_size, sector, sector_address, buffer); + decrypt_without_diffuser(global_data->enc_ctx, global_data->sector_size, sector, sector_address, buffer); break; } diff --git a/src/encryption/encrypt.c b/src/encryption/encrypt.c index 6fb9da0..bac752b 100644 --- a/src/encryption/encrypt.c +++ b/src/encryption/encrypt.c @@ -57,11 +57,11 @@ int encrypt_sector(dis_iodata_t* global_data, uint8_t* sector, off_t sector_addr { case AES_128_DIFFUSER: case AES_256_DIFFUSER: - encrypt_with_diffuser(global_data->ctx, global_data->sector_size, sector, sector_address, buffer); + encrypt_with_diffuser(global_data->enc_ctx, global_data->sector_size, sector, sector_address, buffer); break; case AES_128_NO_DIFFUSER: case AES_256_NO_DIFFUSER: - encrypt_without_diffuser(global_data->ctx, global_data->sector_size, sector, sector_address, buffer); + encrypt_without_diffuser(global_data->enc_ctx, global_data->sector_size, sector, sector_address, buffer); break; } diff --git a/src/outputs/file/file.h b/src/outputs/file/file.h deleted file mode 100644 index 74f3926..0000000 --- a/src/outputs/file/file.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* -*- mode: c -*- */ -/* - * Dislocker -- enables to read/write on BitLocker encrypted partitions under - * Linux - * Copyright (C) 2012-2013 Romain Coltel, Hervé Schauer Consultants - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ -#ifndef FILE_H -#define FILE_H - - - -#define NB_READ_SECTOR 16 - - -/* - * Only function others can use - */ -int file_main(char* ntfs_file); - - - -#endif /* FILE_H */ diff --git a/src/outputs/fuse/fuse.c b/src/outputs/fuse/fuse.c deleted file mode 100644 index 9253540..0000000 --- a/src/outputs/fuse/fuse.c +++ /dev/null @@ -1,500 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* -*- mode: c -*- */ -/* - * Dislocker -- enables to read/write on BitLocker encrypted partitions under - * Linux - * Copyright (C) 2012-2013 Romain Coltel, Hervé Schauer Consultants - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - - -/* - * Special thanks to Nitin Kumar and Vipin Kumar for helping me building this - * file - */ - - -#include "common.h" -#include "encommon.h" -#include "dislocker.h" -#include "encryption/decrypt.h" -#include "sectors.h" -#include "metadata/metadata.h" -#include "fuse.h" - - -/** Data used globally for operation on disk (encryption/decryption) */ -extern dis_iodata_t disk_op_data; - - -static int fs_getattr(const char *path, struct stat *stbuf) -{ - int res = 0; - - if(!path || !stbuf) - return -EINVAL; - - memset(stbuf, 0, sizeof(struct stat)); - if(strcmp(path, "/") == 0) - { - stbuf->st_mode = S_IFDIR | 0555; - stbuf->st_nlink = 2; - } - else if(strcmp(path, NTFS_FILENAME) == 0) - { - mode_t m = disk_op_data.cfg->is_ro ? 0444 : 0666; - stbuf->st_mode = S_IFREG | m; - stbuf->st_nlink = 1; - stbuf->st_size = (off_t)disk_op_data.volume_size; - } - else - res = -ENOENT; - - return res; -} - -static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - /* Both variables aren't used here */ - (void) offset; - (void) fi; - - if(!path || !buf || !filler) - return -EINVAL; - - if(strcmp(path, "/") != 0) - return -ENOENT; - - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); - filler(buf, NTFS_FILENAME + 1, NULL, 0); - - return 0; -} - -static int fs_open(const char *path, struct fuse_file_info *fi) -{ - if(!path || !fi) - return -EINVAL; - - if(strcmp(path, NTFS_FILENAME) != 0) - return -ENOENT; - - - if(disk_op_data.cfg->is_ro & READ_ONLY) - { - if((fi->flags & 3) != O_RDONLY) - return -EACCES; - } - else - { - /* Authorize read/write, readonly and writeonly operations */ - if((fi->flags & 3) != O_RDWR && - (fi->flags & 3) != O_RDONLY && - (fi->flags & 3) != O_WRONLY) - return -EACCES; - } - - return 0; -} - -static int fs_read(const char *path, char *buf, size_t size, - off_t offset, UNUSED struct fuse_file_info *fi) -{ - if(!path || !buf) - return -EINVAL; - - uint8_t* buffer = NULL; - - size_t sector_count; - off_t sector_start; - size_t sector_to_add = 0; - - - /* - * Perform basic checks - */ - if(strcmp(path, NTFS_FILENAME) != 0) - { - xprintf(L_DEBUG, "Unknown entry requested: \"%s\"\n", path); - return -ENOENT; - } - - if(size == 0) - { - xprintf(L_DEBUG, "Received a request with a null size\n"); - return 0; - } - - if(offset < 0) - { - xprintf(L_ERROR, "Offset under 0: %#" F_OFF_T "\n", offset); - return -EFAULT; - } - - if(offset >= (off_t)disk_op_data.volume_size) - { - xprintf(L_ERROR, "Offset (%#" F_OFF_T ") exceeds volume's size (%#" - F_OFF_T ")\n", - offset, (off_t)disk_op_data.volume_size); - return -EFAULT; - } - - - /* - * The offset may not be at a sector limit, so we need to decrypt the entire - * sector where it starts. Idem for the end. - * - * - * Example: - * Sector number: 1 2 3 4 5 6 7... - * Data, continuous sectors: |___|___|___|___|___|___|__... - * The data the user want: |__________| - * - * The user don't want all of the data from sectors 2 and 5, but as the data - * are encrypted sector by sector, we have to decrypt them even though we - * won't give him the beginning of the sector 2 and the end of the sector 5. - * - * - * - * Logic to do this is below : - * - count the number of full sectors - * - decode all sectors - * - select and copy the data to user and deallocate all buffers - */ - - /* Do not add sectors if we're at the edge of one already */ - if((offset % disk_op_data.sector_size) != 0) - sector_to_add += 1; - if(((offset + (off_t)size) % disk_op_data.sector_size) != 0) - sector_to_add += 1; - - sector_count = ( size / disk_op_data.sector_size ) + sector_to_add; - sector_start = offset / disk_op_data.sector_size; - - xprintf(L_DEBUG, - "--------------------{ Fuse reading }-----------------------\n"); - xprintf(L_DEBUG, " Offset and size needed: %#" F_OFF_T - " and %#" F_SIZE_T "\n", offset, size); - xprintf(L_DEBUG, " Start sector number: %#" F_OFF_T - " || Number of sectors: %#" F_SIZE_T "\n", - sector_start, sector_count); - - - /* - * NOTE: DO NOT use xmalloc() here, we don't want to mess everything up! - * In general, do not use xfunctions() but xprintf() here. - */ - - size_t to_allocate = size + sector_to_add*(size_t)disk_op_data.sector_size; - xprintf(L_DEBUG, " Trying to allocate %#" F_SIZE_T " bytes\n",to_allocate); - buffer = malloc(to_allocate); - - /* If buffer could not be allocated, return an error */ - if(!buffer) - { - xprintf(L_ERROR, "Cannot allocate buffer for reading, abort.\n"); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - if(errno < 0) - return errno; - else - return -ENOMEM; - } - - - if(!disk_op_data.decrypt_region( - disk_op_data.volume_fd, - sector_count, - disk_op_data.sector_size, - sector_start * disk_op_data.sector_size, - buffer - )) - { - free(buffer); - xprintf(L_ERROR, "Cannot decrypt sectors, abort.\n"); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - return -EIO; - } - - /* Now copy the required amount of data to the user buffer */ - memcpy(buf, buffer + (offset % disk_op_data.sector_size), size); - - free(buffer); - - xprintf(L_DEBUG, " Outsize which will be returned: %d\n", (int)size); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - - return (int)size; -} - -static int fs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *fi) -{ - // Check parameters - if(!path || !buf) - return -EINVAL; - - uint8_t* buffer = NULL; - int ret = 0; - - size_t sector_count; - off_t sector_start; - size_t sector_to_add = 0; - - - /* Perform basic checks */ - if(disk_op_data.cfg->is_ro & READ_ONLY) - { - xprintf(L_DEBUG, "Only decrypting (-r or --read-only option passed)\n"); - return -EACCES; - } - - if(strcmp(path, NTFS_FILENAME) != 0) - { - xprintf(L_DEBUG, "Unknown entry requested: \"%s\"\n", path); - return -ENOENT; - } - - if(size == 0) - { - xprintf(L_DEBUG, "Received a request with a null size\n"); - return 0; - } - - if(offset < 0) - { - xprintf(L_ERROR, "Offset under 0: %#" F_OFF_T "\n", offset); - return -EFAULT; - } - - if(offset >= (off_t)disk_op_data.volume_size) - { - xprintf(L_ERROR, "Offset (%#" F_OFF_T ") exceeds volume's size (%#" - F_OFF_T ")\n", - offset, (off_t)disk_op_data.volume_size); - return -EFAULT; - } - - if((size_t)offset + size >= (size_t)disk_op_data.volume_size) - { - size_t nsize = (size_t)disk_op_data.volume_size - - (size_t)offset; - xprintf(L_WARNING, "Size modified as exceeding volume's end (offset=%#" - F_SIZE_T " + size=%#" F_SIZE_T " >= volume_size=%#" - F_SIZE_T ") ; new size: %#" F_SIZE_T "\n", - (size_t)offset, size, (size_t)disk_op_data.volume_size, nsize); - size = nsize; - } - - - /* - * Don't authorize to write on metadata, NTFS firsts sectors and on another - * area we shouldn't write to (don't know its signification yet). - */ - off_t metadata_offset = 0; - off_t metadata_size = 0; - size_t virt_loop = 0; - - for(virt_loop = 0; virt_loop < disk_op_data.nb_virt_region; virt_loop++) - { - metadata_size = (off_t)disk_op_data.virt_region[virt_loop].size; - if(metadata_size == 0) - continue; - - metadata_offset = (off_t)disk_op_data.virt_region[virt_loop].addr; - - if(offset >= metadata_offset && - offset <= metadata_offset + metadata_size) - { - xprintf(L_INFO, "Denying write request on the metadata (1:%#" - F_OFF_T ")\n", offset); - return -EFAULT; - } - - if(offset < metadata_offset && - offset + (off_t)size >= metadata_offset) - { - xprintf(L_INFO, "Denying write request on the metadata (2:%#" - F_OFF_T "+ %#" F_SIZE_T ")\n", offset, size); - return -EFAULT; - } - } - - - /* - * For BitLocker 7's volume, redirect writes to firsts sectors to the backed - * up ones - */ - if(disk_op_data.metadata->version == V_SEVEN && - offset < disk_op_data.virtualized_size) - { - xprintf(L_DEBUG, " Entering virtualized area\n"); - if(offset + (off_t)size <= disk_op_data.virtualized_size) - { - /* - * If all the request is within the virtualized area, just change - * the offset - */ - offset = offset + (off_t)disk_op_data.metadata->boot_sectors_backup; - xprintf(L_DEBUG, " `-> Just redirecting to %#"F_OFF_T"\n", offset); - } - else - { - /* - * But if the buffer is within the virtualized area and overflow it, - * split the request in two: - * - One for the virtualized area completely (which will be handled - * by "recursing" and entering the case above) - * - One for the rest by changing the offset to the end of the - * virtualized area and the size to the rest to be dec/encrypted - */ - xprintf(L_DEBUG, " `-> Splitting the request in two, recursing\n"); - - size_t nsize = (size_t)(disk_op_data.virtualized_size - offset); - ret = fs_write(path, buf, nsize, offset, fi); - if(ret < 0) - return ret; - - offset = disk_op_data.virtualized_size; - size -= nsize; - buf += nsize; - } - } - - - /* - * As in the read function, the offset may not be at a sector limit, so we - * need to decrypt the entire sector where it starts till the entire sector - * where it ends, then push the changes into the sectors at correct offset - * and finally encrypt all of these sectors and write them back to the disk. - * - * - * Example: - * Sector number: 1 2 3 4 5 6 7... - * Data, continuous sectors: |___|___|___|___|___|___|__... - * Where the user want to write: |__________| - * - * The user don't want to write everywhere, just from the middle of sector 2 - * till a part of sector 5. But we're writing sectors by sectors to be able - * to encrypt using AES. So we'll need entire sectors 2 to 5 included. - * - * - * - * Logic to do this is below : - * - read and decrypt all sectors completely (2 to 5 in the example above) - * - replace some data by the user's one - * - encrypt and write the read sectors - */ - - /* Do not add sectors if we're at the edge of one already */ - if((offset % disk_op_data.sector_size) != 0) - sector_to_add += 1; - if(((offset + (off_t)size) % disk_op_data.sector_size) != 0) - sector_to_add += 1; - - - sector_count = ( size / disk_op_data.sector_size ) + sector_to_add; - sector_start = offset / disk_op_data.sector_size; - - xprintf(L_DEBUG, - "--------------------{ Fuse writing }-----------------------\n"); - xprintf(L_DEBUG, " Offset and size requested: %#" F_OFF_T " and %#" - F_SIZE_T "\n", offset, size); - xprintf(L_DEBUG, " Start sector number: %#" F_OFF_T - " || Number of sectors: %#" F_SIZE_T "\n", - sector_start, sector_count); - - - /* - * NOTE: DO NOT use xmalloc() here, we don't want to mess everything up! - * In general, do not use xfunctions() but xprintf() here. - */ - - buffer = malloc(size + sector_to_add * (size_t)disk_op_data.sector_size); - - /* If buffer could not be allocated */ - if(!buffer) - { - xprintf(L_ERROR, "Cannot allocate buffer for writing, abort.\n"); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - return -ENOMEM; - } - - - if(!disk_op_data.decrypt_region( - disk_op_data.volume_fd, - sector_count, - disk_op_data.sector_size, - sector_start * disk_op_data.sector_size, - buffer - )) - { - free(buffer); - xprintf(L_ERROR, "Cannot decrypt sectors, abort.\n"); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - return -EIO; - } - - - /* Now copy the user's buffer to the received data */ - memcpy(buffer + (offset % disk_op_data.sector_size), buf, size); - - - /* Finally, encrypt the buffer and write it to the disk */ - if(!disk_op_data.encrypt_region( - disk_op_data.volume_fd, - sector_count, - disk_op_data.sector_size, - sector_start * disk_op_data.sector_size, - buffer - )) - { - free(buffer); - xprintf(L_ERROR, "Cannot encrypt sectors, abort.\n"); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - return -EIO; - } - - - free(buffer); - - - /* Note that ret is zero when no recursion occurs */ - int outsize = (int)size + ret; - - xprintf(L_DEBUG, " Outsize which will be returned: %d\n", outsize); - xprintf(L_DEBUG, - "-----------------------------------------------------------\n"); - - return outsize; -} - - -struct fuse_operations fs_oper = { - .getattr = fs_getattr, - .readdir = fs_readdir, - .open = fs_open, - .read = fs_read, - .write = fs_write, -}; diff --git a/src/outputs/fuse/fuse.h b/src/outputs/fuse/fuse.h deleted file mode 100644 index 0c2b024..0000000 --- a/src/outputs/fuse/fuse.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* -*- mode: c -*- */ -/* - * Dislocker -- enables to read/write on BitLocker encrypted partitions under - * Linux - * Copyright (C) 2012-2013 Romain Coltel, Hervé Schauer Consultants - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ -#ifndef FUSE_H -#define FUSE_H - - - - -#ifdef __DARWIN -# include -#else -# include -#endif /* __DARWIN */ - - - - -/** NTFS virtual partition's name */ -#define NTFS_FILENAME "/dislocker-file" - - - -#endif /* FUSE_H */ diff --git a/src/outputs/fuse/main.c b/src/outputs/fuse/main.c deleted file mode 100644 index 5b2a3e1..0000000 --- a/src/outputs/fuse/main.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - FUSE: Filesystem in Userspace - Copyright (C) 2001-2005 Miklos Szeredi - - This program can be distributed under the terms of the GNU GPL. - See the file COPYING. -*/ - -#if defined(__DARWIN) || defined(__FREEBSD) -# include -#else -# include -#endif /* __DARWIN || __FREEBSD */ - -#include -#include -#include -#include - -static const char *hello_str = "Hello World!\n"; -static const char *hello_path = "/hello"; - -static int hello_getattr(const char *path, struct stat *stbuf) -{ - int res = 0; - - memset(stbuf, 0, sizeof(struct stat)); - if(strcmp(path, "/") == 0) { - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 2; - } - else if(strcmp(path, hello_path) == 0) { - stbuf->st_mode = S_IFREG | 0444; - stbuf->st_nlink = 1; - stbuf->st_size = (off_t)strlen(hello_str); - } - else - res = -ENOENT; - - return res; -} - -static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, - off_t offset, struct fuse_file_info *fi) -{ - (void) offset; - (void) fi; - - if(strcmp(path, "/") != 0) - return -ENOENT; - - filler(buf, ".", NULL, 0); - filler(buf, "..", NULL, 0); - filler(buf, hello_path + 1, NULL, 0); - - return 0; -} - -static int hello_open(const char *path, struct fuse_file_info *fi) -{ - if(strcmp(path, hello_path) != 0) - return -ENOENT; - - if((fi->flags & 3) != O_RDONLY) - return -EACCES; - - return 0; -} - -static int hello_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - size_t len; - (void) fi; - if(strcmp(path, hello_path) != 0) - return -ENOENT; - - len = strlen(hello_str); - if (offset < (off_t)len) { - if ((size_t)offset + size > len) - size = len - (size_t)offset; - memcpy(buf, hello_str + offset, size); - } else - size = 0; - - return (int)size; -} - -static struct fuse_operations hello_oper = { - .getattr = hello_getattr, - .readdir = hello_readdir, - .open = hello_open, - .read = hello_read, -}; - -int main(int argc, char *argv[]) -{ - return fuse_main(argc, argv, &hello_oper, NULL); -} diff --git a/src/outputs/prepare.c b/src/outputs/prepare.c index 2aebd4c..4ea622b 100644 --- a/src/outputs/prepare.c +++ b/src/outputs/prepare.c @@ -30,9 +30,7 @@ /** * Getting the real volume size is proving to be quite difficult. */ -static uint64_t get_volume_size(volume_header_t* volume_header, - bitlocker_header_t* metadata, - int fd_volume); +static uint64_t get_volume_size(dis_iodata_t* io_data); @@ -122,40 +120,26 @@ int init_keys(bitlocker_dataset_t* dataset, datum_key_t* fvek_datum, /** * Prepare a structure which hold data used for decryption/encryption * - * @param metadata The BitLocker metadata block - * @param ctx Contexts used to encrypt/decrypt data - * @param cfg Invoked configuration - * @param volume_header First-sector data - * @param offset Where the real volume begins - * @param fd_volume The volume's file descriptor + * @param dis_ctx The dislocker context used everywhere. * @return TRUE if result can be trusted, FALSE otherwise */ -int prepare_crypt(bitlocker_header_t* metadata, contexts_t* ctx, - dis_config_t* cfg, volume_header_t* volume_header, - off_t offset, int fd_volume) +int prepare_crypt(dis_context_t* dis_ctx) { size_t loop = 0; - uint16_t sector_size = volume_header->sector_size; - uint8_t sectors_per_cluster = volume_header->sectors_per_cluster; + dis_iodata_t* io_data = &dis_ctx->io_data; + bitlocker_header_t* metadata = io_data->metadata; + uint16_t sector_size = io_data->volume_header->sector_size; + uint8_t sectors_per_cluster = io_data->volume_header->sectors_per_cluster; uint32_t cluster_size = 0; uint64_t metafiles_size = 0; - /** @see dislocker.c */ - extern dis_iodata_t disk_op_data; + io_data->xinfo = NULL; + io_data->sector_size = sector_size; + io_data->part_off = dis_ctx->cfg.offset; + io_data->decrypt_region = read_decrypt_sectors; + io_data->encrypt_region = encrypt_write_sectors; - memset(&disk_op_data, 0, sizeof(dis_iodata_t)); - - disk_op_data.metadata = metadata; - disk_op_data.xinfo = NULL; - disk_op_data.sector_size = sector_size; - disk_op_data.part_off = offset; - disk_op_data.ctx = ctx; - disk_op_data.cfg = cfg; - disk_op_data.volume_fd = fd_volume; - disk_op_data.decrypt_region = read_decrypt_sectors; - disk_op_data.encrypt_region = encrypt_write_sectors; - - if(pthread_mutex_init(&disk_op_data.mutex_lseek_rw, NULL) != 0) + if(pthread_mutex_init(&io_data->mutex_lseek_rw, NULL) != 0) { xprintf(L_ERROR, "Can't initialize mutex: %s\n", strerror(errno)); return FALSE; @@ -165,16 +149,18 @@ int prepare_crypt(bitlocker_header_t* metadata, contexts_t* ctx, * We need to grab the volume's size from the first sector, so we can * announce it on a getattr call */ - disk_op_data.volume_size = get_volume_size(volume_header, metadata, - fd_volume); - if(disk_op_data.volume_size == 0) + io_data->volume_size = get_volume_size(io_data); + if(io_data->volume_size == 0) { xprintf(L_ERROR, "Can't initialize the volume's size\n"); return FALSE; } - xprintf(L_INFO, "Found volume's size: 0x%1$" F_U64_T " (%1$llu) bytes\n", - disk_op_data.volume_size); + xprintf( + L_INFO, + "Found volume's size: 0x%1$" F_U64_T " (%1$llu) bytes\n", + io_data->volume_size + ); /* * Alignment isn't the same for W$ Vista (size-of-a-cluster aligned on @@ -197,11 +183,11 @@ int prepare_crypt(bitlocker_header_t* metadata, contexts_t* ctx, * Initialize region to report as filled with zeroes, if asked from the NTFS * layer. This is to mimic BitLocker's behaviour. */ - disk_op_data.nb_virt_region = 3; - for(loop = 0; loop < disk_op_data.nb_virt_region; loop++) + io_data->nb_virt_region = 3; + for(loop = 0; loop < io_data->nb_virt_region; loop++) { - disk_op_data.virt_region[loop].addr = metadata->offset_bl_header[loop]; - disk_op_data.virt_region[loop].size = metafiles_size; + io_data->virt_region[loop].addr = metadata->offset_bl_header[loop]; + io_data->virt_region[loop].size = metafiles_size; } if(metadata->version == V_VISTA) @@ -221,38 +207,46 @@ int prepare_crypt(bitlocker_header_t* metadata, contexts_t* ctx, DATUM_VIRTUALIZATION_INFO, NULL, (void**)&datum)) { char* type_str = datumtypestr(DATUM_VIRTUALIZATION_INFO); - xprintf(L_ERROR, "Error looking for the VIRTUALIZATION datum type" - " %hd (%s). Internal failure, abort.\n", - DATUM_VIRTUALIZATION_INFO, type_str); + xprintf( + L_ERROR, + "Error looking for the VIRTUALIZATION datum type" + " %hd (%s). Internal failure, abort.\n", + DATUM_VIRTUALIZATION_INFO, + type_str + ); xfree(type_str); datum = NULL; return FALSE; } - disk_op_data.nb_virt_region++; - disk_op_data.virt_region[3].addr = metadata->boot_sectors_backup; - disk_op_data.virt_region[3].size = datum->nb_bytes; - disk_op_data.virtualized_size = (off_t)datum->nb_bytes; + io_data->nb_virt_region++; + io_data->virt_region[3].addr = metadata->boot_sectors_backup; + io_data->virt_region[3].size = datum->nb_bytes; + io_data->virtualized_size = (off_t)datum->nb_bytes; - xprintf(L_DEBUG, "Virtualized info size: %#" F_OFF_T "\n", - disk_op_data.virtualized_size); + xprintf( + L_DEBUG, + "Virtualized info size: %#" F_OFF_T "\n", + io_data->virtualized_size + ); /* Extended info is new to Windows 8 */ + // TODO add check on datum_types_prop's size against datum->header.datum_type size_t win7_size = datum_types_prop[datum->header.datum_type].size_header; size_t actual_size = ((size_t)datum->header.datum_size) & 0xffff; if(actual_size > win7_size) { - disk_op_data.xinfo = &datum->xinfo; + io_data->xinfo = &datum->xinfo; xprintf(L_DEBUG, "Got extended info\n"); } /* Another area to report as filled with zeroes, new to W8 as well */ if(metadata->curr_state == SWITCHING_ENCRYPTION) { - disk_op_data.nb_virt_region++; - disk_op_data.virt_region[4].addr = metadata->encrypted_volume_size; - disk_op_data.virt_region[4].size = metadata->unknown_size; + io_data->nb_virt_region++; + io_data->virt_region[4].addr = metadata->encrypted_volume_size; + io_data->virt_region[4].size = metadata->unknown_size; } } else @@ -262,17 +256,6 @@ int prepare_crypt(bitlocker_header_t* metadata, contexts_t* ctx, return FALSE; } - /* - * Check we can safely run without leaking information or breaking metadata - */ - extern guid_t INFORMATION_OFFSET_GUID; - - if(!check_match_guid(volume_header->guid, INFORMATION_OFFSET_GUID)) - { - xprintf(L_ERROR, "Unsupported volume GUID.\n"); - return FALSE; - } - return TRUE; } @@ -310,31 +293,32 @@ static uint64_t get_volume_size_from_mbr(volume_header_t* volume_header) /** * Compute the real volume's size. * - * @param volume_header First-sector data - * @param metadata The BitLocker metadata block - * @param fd The volume's file descriptor + * @param io_data The structure holding major information for accessing the + * volume * @return The volume size or 0 if it can't be determined */ -static uint64_t get_volume_size(volume_header_t* volume_header, - bitlocker_header_t* metadata, - int fd_volume) +static uint64_t get_volume_size(dis_iodata_t* io_data) { uint64_t volume_size = 0; - volume_size = get_volume_size_from_mbr(volume_header); + volume_size = get_volume_size_from_mbr(io_data->volume_header); - if(!volume_size && metadata->version == V_SEVEN) + if(!volume_size && io_data->metadata->version == V_SEVEN) { /* * For version V_SEVEN, volumes can be partially encrypted. * Therefore, try to get the real size from the NTFS data */ - uint8_t* input = xmalloc(volume_header->sector_size); - memset(input, 0, volume_header->sector_size); + uint8_t* input = xmalloc(io_data->volume_header->sector_size); + memset(input, 0, io_data->volume_header->sector_size); - if(!read_decrypt_sectors(fd_volume, 1, volume_header->sector_size, 0, - input)) + if(!read_decrypt_sectors( + io_data, + 1, + io_data->volume_header->sector_size, + 0, + input)) { xprintf(L_ERROR, "Unable to read the NTFS header to get the volume's size\n"); diff --git a/src/outputs/prepare.h b/src/outputs/prepare.h index b38ea9d..f7d5ab8 100644 --- a/src/outputs/prepare.h +++ b/src/outputs/prepare.h @@ -26,6 +26,7 @@ #include +#include "dislocker.h" #include "config.h" #include "encommon.h" #include "metadata/datums.h" @@ -42,9 +43,7 @@ int init_keys(bitlocker_dataset_t* dataset, datum_key_t* fvek, contexts_t* ctx); * Function used to prepare a structure which hold data used for * decryption/encryption */ -int prepare_crypt(bitlocker_header_t* metadata, contexts_t* ctx, - dis_config_t* cfg, volume_header_t* volume_header, - off_t offset, int fd_volume); +int prepare_crypt(dis_context_t* dis_ctx); #endif /* PREPARE_H */ \ No newline at end of file diff --git a/src/sectors.c b/src/sectors.c index e37c5e0..342429a 100644 --- a/src/sectors.c +++ b/src/sectors.c @@ -31,19 +31,25 @@ #include "sectors.h" -/** Data used globally for operation on disk (encryption/decryption) */ -dis_iodata_t disk_op_data; - /** Prototype of functions used internally */ static void* thread_decrypt(void* args); static void* thread_encrypt(void* args); -static void fix_read_sector_seven(dis_iodata_t* disk_op_data, - off_t sector_address, uint8_t *output); -static void fix_read_sector_vista(dis_iodata_t* disk_op_data, uint8_t* input, - uint8_t *output); -static void fix_write_sector_vista(dis_iodata_t* disk_op_data, uint8_t* input, - uint8_t *output); +static void fix_read_sector_seven( + dis_iodata_t* io_data, + off_t sector_address, + uint8_t *output +); +static void fix_read_sector_vista( + dis_iodata_t* io_data, + uint8_t* input, + uint8_t *output +); +static void fix_write_sector_vista( + dis_iodata_t* io_data, + uint8_t* input, + uint8_t *output +); @@ -52,7 +58,7 @@ static void fix_write_sector_vista(dis_iodata_t* disk_op_data, uint8_t* input, * Read and decrypt one or more sectors * @warning The sector_start has to be correctly aligned * - * @param fd The file descriptor to the volume + * @param io_data The data structure containing volume's information * @param nb_read_sector The number of sectors to read * @param sector_size The size of one sector * @param sector_start The offset of the first sector to read; See the warning @@ -60,8 +66,12 @@ static void fix_write_sector_vista(dis_iodata_t* disk_op_data, uint8_t* input, * @param output The output buffer where to put decrypted data * @return TRUE if result can be trusted, FALSE otherwise */ -int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, - off_t sector_start, uint8_t* output) +int read_decrypt_sectors( + dis_iodata_t* io_data, + size_t nb_read_sector, + uint16_t sector_size, + off_t sector_start, + uint8_t* output) { // Check parameters if(!output) @@ -77,7 +87,7 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, /* Be sure to lock for lseek/read */ - if(pthread_mutex_lock(&disk_op_data.mutex_lseek_rw) != 0) + if(pthread_mutex_lock(&io_data->mutex_lseek_rw) != 0) { free(input); xprintf(L_ERROR, "Can't lock rw mutex: %s\n", strerror(errno)); @@ -86,8 +96,8 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, /* Go where we need to read data */ - off_t off = sector_start + disk_op_data.part_off; - if(lseek(fd, off, SEEK_SET) < 0) + off_t off = sector_start + io_data->part_off; + if(lseek(io_data->volume_fd, off, SEEK_SET) < 0) { free(input); xprintf(L_ERROR, "Unable to lseek to %#" F_OFF_T "\n", off); @@ -95,7 +105,7 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, } /* Read the sectors we need */ - ssize_t read_size = read(fd, input, size); + ssize_t read_size = read(io_data->volume_fd, input, size); if(read_size <= 0) { @@ -106,7 +116,7 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, } /* Unlock the previously locked mutex */ - if(pthread_mutex_unlock(&disk_op_data.mutex_lseek_rw) != 0) + if(pthread_mutex_unlock(&io_data->mutex_lseek_rw) != 0) { free(input); xprintf(L_ERROR, "Can't unlock rw mutex: %s\n", strerror(errno)); @@ -140,6 +150,8 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, args[loop].modulo = NB_THREAD; args[loop].modulo_result = loop; + args[loop].io_data = io_data; + pthread_create( &thread[loop], NULL, thread_decrypt, (void*) &args[loop] ); } @@ -160,6 +172,8 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, arg.modulo = 0; arg.modulo_result = 42; + args.io_data = io_data; + thread_decrypt(&arg); } #endif @@ -175,7 +189,7 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, * Encrypt and write one or more sectors * @warning The sector_start has to be correctly aligned * - * @param fd The file descriptor to the volume + * @param io_data The data structure containing volume's information * @param nb_write_sector The number of sectors to write * @param sector_size The size of one sector * @param sector_start The offset of the first sector to write; See the warning @@ -183,8 +197,12 @@ int read_decrypt_sectors(int fd, size_t nb_read_sector, uint16_t sector_size, * @param output The input buffer which has to be encrypted and written * @return TRUE if result can be trusted, FALSE otherwise */ -int encrypt_write_sectors(int fd, size_t nb_write_sector, uint16_t sector_size, - off_t sector_start, uint8_t* input) +int encrypt_write_sectors( + dis_iodata_t* io_data, + size_t nb_write_sector, + uint16_t sector_size, + off_t sector_start, + uint8_t* input) { // Check parameter if(!input) @@ -213,6 +231,8 @@ int encrypt_write_sectors(int fd, size_t nb_write_sector, uint16_t sector_size, args[loop].modulo = NB_THREAD; args[loop].modulo_result = loop; + args[loop].io_data = io_data; + pthread_create( &thread[loop], NULL, thread_encrypt, (void*) &args[loop] ); } @@ -233,12 +253,14 @@ int encrypt_write_sectors(int fd, size_t nb_write_sector, uint16_t sector_size, arg.modulo = 0; arg.modulo_result = 42; + args.io_data = io_data; + thread_encrypt(&arg); } #endif /* Be sure to lock for lseek/write */ - if(pthread_mutex_lock(&disk_op_data.mutex_lseek_rw) != 0) + if(pthread_mutex_lock(&io_data->mutex_lseek_rw) != 0) { free(output); xprintf(L_ERROR, "Can't lock rw mutex: %s\n", strerror(errno)); @@ -246,8 +268,8 @@ int encrypt_write_sectors(int fd, size_t nb_write_sector, uint16_t sector_size, } /* Go where we need to write data */ - off_t off = sector_start + disk_op_data.part_off; - if(lseek(fd, off, SEEK_SET) < 0) + off_t off = sector_start + io_data->part_off; + if(lseek(io_data->volume_fd, off, SEEK_SET) < 0) { free(output); xprintf(L_ERROR, "Unable to lseek to %#" F_OFF_T "\n", off); @@ -255,13 +277,17 @@ int encrypt_write_sectors(int fd, size_t nb_write_sector, uint16_t sector_size, } /* Write the sectors we want */ - ssize_t write_size = write(fd, output, nb_write_sector * sector_size); + ssize_t write_size = write( + io_data->volume_fd, + output, + nb_write_sector * sector_size + ); /* Unlock the previously locked mutex */ - if(pthread_mutex_unlock(&disk_op_data.mutex_lseek_rw) != 0) + if(pthread_mutex_unlock(&io_data->mutex_lseek_rw) != 0) { - free(output); xprintf(L_ERROR, "Can't unlock rw mutex: %s\n", strerror(errno)); + free(output); return FALSE; } @@ -283,7 +309,8 @@ static void* thread_decrypt(void* params) if(!params) return NULL; - thread_arg_t* args = (thread_arg_t*)params; + thread_arg_t* args = (thread_arg_t*)params; + dis_iodata_t* io_data = args->io_data; off_t loop = 0; off_t offset = args->sector_start; @@ -293,7 +320,7 @@ static void* thread_decrypt(void* params) size_t virt_loop = 0; off_t metadata_offset = 0; - uint16_t version = disk_op_data.metadata->version; + uint16_t version = io_data->metadata->version; off_t size = 0; @@ -325,13 +352,13 @@ static void* thread_decrypt(void* params) off_t sector_offset = args->sector_start / args->sector_size + loop; /* Check for zero out areas */ - for(virt_loop = 0; virt_loop < disk_op_data.nb_virt_region; virt_loop++) + for(virt_loop = 0; virt_loop < io_data->nb_virt_region; virt_loop++) { - size = (off_t)disk_op_data.virt_region[virt_loop].size; + size = (off_t)io_data->virt_region[virt_loop].size; if(size == 0) continue; - metadata_offset = (off_t)disk_op_data.virt_region[virt_loop].addr; + metadata_offset = (off_t)io_data->virt_region[virt_loop].addr; if(offset >= metadata_offset && offset <= metadata_offset + size) { @@ -348,20 +375,20 @@ static void* thread_decrypt(void* params) /* Check for sectors fixing and non-encrypted sectors */ if(version == V_SEVEN && - (uint64_t)sector_offset < disk_op_data.metadata->nb_backup_sectors) + (uint64_t)sector_offset < io_data->metadata->nb_backup_sectors) { /* * The firsts sectors are encrypted in a different place on a * Windows 7 volume */ fix_read_sector_seven( - &disk_op_data, + io_data, offset, loop_output ); } else if(version == V_SEVEN && - (uint64_t)offset >= disk_op_data.metadata->encrypted_volume_size) + (uint64_t)offset >= io_data->metadata->encrypted_volume_size) { /* Do not decrypt when there's nothing to */ xprintf(L_DEBUG, @@ -378,9 +405,9 @@ static void* thread_decrypt(void* params) */ if(sector_offset < 1) fix_read_sector_vista( - &disk_op_data, - loop_input, - loop_output + io_data, + loop_input, + loop_output ); else { @@ -396,7 +423,7 @@ static void* thread_decrypt(void* params) { /* Decrypt the sector */ if(!decrypt_sector( - &disk_op_data, + io_data, loop_input, offset, loop_output @@ -420,7 +447,8 @@ static void* thread_encrypt(void* params) if(!params) return NULL; - thread_arg_t* args = (thread_arg_t*)params; + thread_arg_t* args = (thread_arg_t*)params; + dis_iodata_t* io_data = args->io_data; off_t loop = 0; off_t offset = args->sector_start; @@ -428,7 +456,7 @@ static void* thread_encrypt(void* params) uint8_t* loop_input = args->input; uint8_t* loop_output = args->output; - uint16_t version = disk_op_data.metadata->version; + uint16_t version = io_data->metadata->version; for(loop = 0; loop < (off_t)args->nb_loop; ++loop, @@ -451,7 +479,7 @@ static void* thread_encrypt(void* params) /* * NOTE: Seven specificities are dealt with earlier in the process - * see fuse.c:fs_write() + * see dislocker.c:enlock() */ if(version == V_VISTA && sector_offset < 16) { @@ -460,22 +488,22 @@ static void* thread_encrypt(void* params) */ if(sector_offset < 1) fix_write_sector_vista( - &disk_op_data, - loop_input, - loop_output + io_data, + loop_input, + loop_output ); else memcpy(loop_output, loop_input, args->sector_size); } else if(version == V_SEVEN && - (uint64_t)offset >= disk_op_data.metadata->encrypted_volume_size) + (uint64_t)offset >= io_data->metadata->encrypted_volume_size) { memcpy(loop_output, loop_input, args->sector_size); } else { if(!encrypt_sector( - &disk_op_data, + io_data, loop_input, offset, loop_output @@ -497,12 +525,11 @@ static void* thread_encrypt(void* params) * "Fix" the firsts sectors of a BitLocker volume encrypted with W$ Seven for * read operation * - * @param disk_op_data Data needed by FUSE and the decryption to deal with - * encrypted data + * @param io_data Data needed by the decryption to deal with encrypted data * @param sector_address Address of the sector to decrypt * @param output The buffer where to put fixed data */ -static void fix_read_sector_seven(dis_iodata_t* disk_op_data, +static void fix_read_sector_seven(dis_iodata_t* io_data, off_t sector_address, uint8_t *output) { // Check parameter @@ -515,19 +542,19 @@ static void fix_read_sector_seven(dis_iodata_t* disk_op_data, * So we can use them here to give a good NTFS partition's beginning. */ off_t from = sector_address; - off_t to = from + (off_t)disk_op_data->metadata->boot_sectors_backup; + off_t to = from + (off_t)io_data->metadata->boot_sectors_backup; xprintf(L_DEBUG, " Fixing sector (7): from %#" F_OFF_T " to %#" F_OFF_T "\n", from, to); - to += disk_op_data->part_off; + to += io_data->part_off; - uint8_t* input = malloc(disk_op_data->sector_size); - memset(input, 0, disk_op_data->sector_size); + uint8_t* input = malloc(io_data->sector_size); + memset(input, 0, io_data->sector_size); /* Be sure to lock for lseek/read */ - if(pthread_mutex_lock(&disk_op_data->mutex_lseek_rw) != 0) + if(pthread_mutex_lock(&io_data->mutex_lseek_rw) != 0) { free(input); xprintf(L_ERROR, "Can't lock rw mutex: %s\n", strerror(errno)); @@ -535,7 +562,7 @@ static void fix_read_sector_seven(dis_iodata_t* disk_op_data, } /* Go where we need to read the new sector */ - if(lseek(disk_op_data->volume_fd, to, SEEK_SET) <0) + if(lseek(io_data->volume_fd, to, SEEK_SET) <0) { free(input); xprintf(L_ERROR, "Unable to lseek to %#" F_OFF_T "\n", to); @@ -543,12 +570,11 @@ static void fix_read_sector_seven(dis_iodata_t* disk_op_data, } /* Read the real sector we need */ - ssize_t read_size = read(disk_op_data->volume_fd, input, - disk_op_data->sector_size); + ssize_t read_size = read(io_data->volume_fd, input, io_data->sector_size); /* Unlock the previously locked mutex */ - if(pthread_mutex_unlock(&disk_op_data->mutex_lseek_rw) != 0) + if(pthread_mutex_unlock(&io_data->mutex_lseek_rw) != 0) { free(input); xprintf(L_ERROR, "Can't unlock rw mutex: %s\n", strerror(errno)); @@ -560,21 +586,21 @@ static void fix_read_sector_seven(dis_iodata_t* disk_op_data, { free(input); xprintf(L_ERROR, "Unable to read %#" F_SIZE_T " bytes from %#" F_OFF_T - "\n", disk_op_data->sector_size, to); + "\n", io_data->sector_size, to); return; } - to -= disk_op_data->part_off; + to -= io_data->part_off; /* If the sector wasn't yet encrypted, don't decrypt it */ - if((uint64_t)to >= disk_op_data->metadata->encrypted_volume_size) + if((uint64_t)to >= io_data->metadata->encrypted_volume_size) { - memcpy(output, input, disk_op_data->sector_size); + memcpy(output, input, io_data->sector_size); } else { decrypt_sector( - disk_op_data, + io_data, input, to, output @@ -589,12 +615,11 @@ static void fix_read_sector_seven(dis_iodata_t* disk_op_data, * "Fix" the firsts sectors of a BitLocker volume encrypted with W$ Vista for * read operation * - * @param disk_op_data Data needed by FUSE and the decryption to deal with - * encrypted data + * @param io_data Data needed by the decryption to deal with encrypted data * @param input The sector which needs a fix * @param output The buffer where to put fixed data */ -static void fix_read_sector_vista(dis_iodata_t* disk_op_data, +static void fix_read_sector_vista(dis_iodata_t* io_data, uint8_t* input, uint8_t *output) { // Check parameter @@ -603,12 +628,12 @@ static void fix_read_sector_vista(dis_iodata_t* disk_op_data, xprintf(L_DEBUG, " Fixing sector (Vista): replacing signature " "and MFTMirror field by: %#llx\n", - disk_op_data->metadata->mftmirror_backup); + io_data->metadata->mftmirror_backup); /* * Only two fields need to be changed: the NTFS signature and the MFT mirror */ - memcpy(output, input, disk_op_data->sector_size); + memcpy(output, input, io_data->sector_size); volume_header_t* formatted_output = (volume_header_t*)output; @@ -616,7 +641,7 @@ static void fix_read_sector_vista(dis_iodata_t* disk_op_data, memcpy(formatted_output->signature, NTFS_SIGNATURE, NTFS_SIGNATURE_SIZE); /* And this is for the MFT Mirror field */ - formatted_output->mft_mirror = disk_op_data->metadata->mftmirror_backup; + formatted_output->mft_mirror = io_data->metadata->mftmirror_backup; } @@ -624,12 +649,11 @@ static void fix_read_sector_vista(dis_iodata_t* disk_op_data, * "Fix" the firsts sectors of a BitLocker volume encrypted with W$ Vista for * write operation * - * @param disk_op_data Data needed by FUSE and the decryption to deal with - * encrypted data + * @param io_data Data needed by the decryption to deal with encrypted data * @param input The sector which needs a fix * @param output The buffer where to put fixed data */ -static void fix_write_sector_vista(dis_iodata_t* disk_op_data, +static void fix_write_sector_vista(dis_iodata_t* io_data, uint8_t* input, uint8_t *output) { // Check parameter @@ -639,7 +663,7 @@ static void fix_write_sector_vista(dis_iodata_t* disk_op_data, /* * Only two fields need to be changed: the NTFS signature and the MFT mirror */ - memcpy(output, input, disk_op_data->sector_size); + memcpy(output, input, io_data->sector_size); volume_header_t* formatted_output = (volume_header_t*)output; @@ -649,15 +673,17 @@ static void fix_write_sector_vista(dis_iodata_t* disk_op_data, /* And this is for the metadata LCN */ formatted_output->metadata_lcn = - disk_op_data->metadata->offset_bl_header[0] / + io_data->metadata->offset_bl_header[0] / (uint64_t)( formatted_output->sectors_per_cluster * formatted_output->sector_size ); - xprintf(L_DEBUG, " Fixing sector (Vista): replacing signature " - "and MFTMirror field by: %#llx\n", - formatted_output->metadata_lcn); - + xprintf( + L_DEBUG, + " Fixing sector (Vista): replacing signature " + "and MFTMirror field by: %#llx\n", + formatted_output->metadata_lcn + ); } diff --git a/src/sectors.h b/src/sectors.h index 21a1f59..1eadc2e 100644 --- a/src/sectors.h +++ b/src/sectors.h @@ -34,6 +34,8 @@ #include +#include "dislocker.h" + /* Struct we pass to a thread for buffer enc/decryption */ typedef struct _thread_arg @@ -48,6 +50,8 @@ typedef struct _thread_arg unsigned int modulo; unsigned int modulo_result; + + dis_iodata_t* io_data; } thread_arg_t; @@ -56,7 +60,19 @@ typedef struct _thread_arg /* * Functions prototypes */ -int read_decrypt_sectors( int fd, size_t nb_read_sector, uint16_t sector_size, off_t sector_start, uint8_t* output); -int encrypt_write_sectors(int fd, size_t nb_write_sector, uint16_t sector_size, off_t sector_start, uint8_t* input); +int read_decrypt_sectors( + dis_iodata_t* io_data, + size_t nb_read_sector, + uint16_t sector_size, + off_t sector_start, + uint8_t* output +); +int encrypt_write_sectors( + dis_iodata_t* io_data, + size_t nb_write_sector, + uint16_t sector_size, + off_t sector_start, + uint8_t* input +); #endif /* SECTORS_H */