diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index dc76e4c..50a6c7f 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -6,11 +6,25 @@ "${workspaceFolder}/**", "${workspaceFolder}/include" ], - "defines": [], + "defines": ["_XOPEN_SOURCE"], "compilerPath": "/usr/bin/gcc", "cStandard": "gnu17", "cppStandard": "gnu++17", "intelliSenseMode": "linux-gcc-x64" + }, + { + "name": "Linux - GCC GNU", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/include" + ], + "defines": [ + "_ISOC11_SOURCE", + "_XOPEN_SOURCE" + ], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu99", + "intelliSenseMode": "linux-gcc-x64" } ], "version": 4 diff --git a/.vscode/launch.json b/.vscode/launch.json index dfcba76..92c54aa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -51,6 +51,29 @@ ], "preLaunchTask": "make formater" }, + { + "name": "mount", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/mount", + "args": [ + "${input:mountDevice}", + "${input:mountPath}" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/build", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Activer l'impression en mode Pretty pour gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + ], + "preLaunchTask": "make mount" + }, ], "inputs": [ { @@ -58,6 +81,18 @@ "description": "Fichier à formater", "type": "promptString", "default": "test.cfs" + }, + { + "id": "mountDevice", + "description": "Disque à monter", + "type": "promptString", + "default": "test.cfs" + }, + { + "id": "mountPath", + "description": "Point de montage", + "type": "promptString", + "default": "mnt" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index f5c45d5..8f7581d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -35,6 +35,16 @@ "array": "c", "assert.h": "c", "cerrno": "c", - "passphrase.h": "c" + "passphrase.h": "c", + "err.h": "c", + "cryptofuse.h": "c", + "fstream": "c", + "fuse.h": "c", + "stdbool.h": "c", + "core_names.h": "c", + "bitset": "c", + "string_view": "c", + "initializer_list": "c", + "utility": "c" } } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 17b127e..591313d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,6 +12,18 @@ "cwd": "${workspaceFolder}" } }, + { + "label": "make mount", + "type": "shell", + "command": "/usr/bin/make", + "args": [ + "mount", + "-j", + ], + "options": { + "cwd": "${workspaceFolder}" + } + }, { "label": "make test_main", "type": "shell", diff --git a/Makefile b/Makefile index d8e9ece..9b2935c 100644 --- a/Makefile +++ b/Makefile @@ -11,22 +11,30 @@ CC = gcc CFLAGS = -Wall -Wextra -Werror -Iinclude -g -std=gnu99 -D_ISOC11_SOURCE LDFLAGS = -lm -lcrypto $(FSANITIZE) -SRC = $(shell find $(SRC_CORE_DIR) -name '*.c') +SRC = $(shell find $(FS_CORE_DIR) -name '*.c') OBJ = $(subst $(PROJECT_DIR),$(BUILD_DIR),$(SRC:.c=.o)) TESTS_SRC = $(shell find $(TESTS_DIR) -name '*.c') TESTS_OBJ = $(subst $(PROJECT_DIR),$(BUILD_DIR),$(TESTS_SRC:.c=.o)) -FORMAT_SRC = $(SRC_CODE_DIR)/formater.c +FORMAT_SRC = $(SRC_DIR)/formater.c FORMAT_OBJ = $(subst $(PROJECT_DIR),$(BUILD_DIR),$(FORMAT_SRC:.c=.o)) -all : formater +MOUNT_SRC = $(SRC_DIR)/mount_fuse.c +MOUNT_OBJ = $(subst $(PROJECT_DIR),$(BUILD_DIR),$(MOUNT_SRC:.c=.o)) + +all : formater mount formater: $(BUILD_DIR)/formater $(OBJ) + +mount: $(BUILD_DIR)/mount $(OBJ) $(BUILD_DIR)/formater: $(FORMAT_OBJ) $(OBJ) $(CC) $(CFLAGS) $^ -o $(BUILD_DIR)/formater $(LDFLAGS) +$(BUILD_DIR)/mount: $(MOUNT_OBJ) $(OBJ) + $(CC) $(CFLAGS) $^ -o $(BUILD_DIR)/mount $(LDFLAGS) + $(BUILD_DIR)/%.o: $(PROJECT_DIR)/%.c mkdir -p $(dir $@) $(CC) $(CFLAGS) -c $< -o $@ $(LDFLAGS) diff --git a/code/core/security/crypto_disk.c b/code/core/security/crypto_disk.c deleted file mode 100644 index 9adf30a..0000000 --- a/code/core/security/crypto_disk.c +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include - -#include "crypto.h" -#include "print.h" - -EVP_PKEY *load_rsa_keypair_from_disk(const char *public_key_path, - const char *private_key_path, - char *passphrase) -{ - EVP_PKEY *rsa_keypair = NULL; - - FILE *public_key_file = fopen(public_key_path, "r"); - FILE *private_key_file = fopen(private_key_path, "r"); - - if (!public_key_file) - return NULL; - - if (!private_key_file) - { - fclose(public_key_file); - return NULL; - } - - if (PEM_read_PUBKEY(public_key_file, &rsa_keypair, NULL, NULL) == NULL) - internal_error_exit("Failed to load public key\n", EXIT_FAILURE); - if (PEM_read_PrivateKey(private_key_file, &rsa_keypair, NULL, passphrase) - == NULL) - internal_error_exit("Failed to load private key\n", EXIT_FAILURE); - - fclose(public_key_file); - fclose(private_key_file); - - return rsa_keypair; -} diff --git a/code/formater.c b/code/formater.c deleted file mode 100644 index 4d47618..0000000 --- a/code/formater.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include "format.h" -#include "passphrase.h" -#include "print.h" - -int main(int argc, char *argv[]) -{ - char *passphrase = NULL; - char *path = NULL; - switch (argc) - { - case 2: - path = argv[1]; - - // Ask for passphrase - printf("Do you want to secure your key with a passphrase? (y/n): "); - char answer = getchar(); - getchar(); // Consume the newline - if (answer != 'y' && answer != 'Y') - print_warning( - "No passphrase will be used. The key may be exposed.\n"); - else - passphrase = ask_user_passphrase(); - - break; - case 3: - path = argv[1]; - passphrase = argv[2]; - break; - - default: - printf("Usage: %s [RSA key passphrase]\n", argv[0]); - return 1; - } - - if (is_already_formatted(path)) - { - // Ask the user if he wants to overwrite the file - print_warning( - "The file is already formatted. Do you want to overwrite it? " - "(y/n): "); - char answer = getchar(); - getchar(); // Consume the newline - if (answer != 'y' && answer != 'Y') - { - printf("Aborting...\n"); - return 0; - } - else - printf("Overwriting...\n"); - } - - format_fs(path, passphrase); - free(passphrase); - print_success("The file/device `%s` has been formatted successfully!\n", - path); - return 0; -} diff --git a/global.mk b/global.mk index a94fd6a..9f572b7 100644 --- a/global.mk +++ b/global.mk @@ -14,6 +14,7 @@ PROJECT_DIR ?= $(shell false) # MUST BE SET BEFORE INCLUDING THIS FILE BUILD_DIR = $(PROJECT_DIR)/build TESTS_DIR = $(PROJECT_DIR)/tests/criterion -SRC_CODE_DIR = $(PROJECT_DIR)/code -SRC_CORE_DIR = $(SRC_CODE_DIR)/core +SRC_DIR = $(PROJECT_DIR)/src +FS_CORE_DIR = $(SRC_DIR)/fs +FUSE_CORE_DIR = $(SRC_DIR)/fuse diff --git a/include/block.h b/include/block.h index 14fe5b3..36f6bbd 100644 --- a/include/block.h +++ b/include/block.h @@ -4,6 +4,8 @@ #include #include +typedef size_t block_t; + /** * @brief Set the device path global variable. * @@ -42,7 +44,7 @@ uint32_t get_block_size(); * * @return 0 on success, -1 on error. */ -int read_blocks(size_t start_block, size_t nb_blocks, void *buffer); +int read_blocks(block_t start_block, size_t nb_blocks, void *buffer); /** * @brief Write blocks to the device. @@ -53,7 +55,7 @@ int read_blocks(size_t start_block, size_t nb_blocks, void *buffer); * * @return 0 on success, -1 on error. */ -int write_blocks(size_t start_block, size_t nb_blocks, void *buffer); +int write_blocks(block_t start_block, size_t nb_blocks, void *buffer); /** * @brief Read a block from the device and decrypt it. @@ -64,7 +66,7 @@ int write_blocks(size_t start_block, size_t nb_blocks, void *buffer); * @param buffer The buffer to fill with the blocks. * @return int 0 on success, -1 on error. */ -int read_blocks_with_decryption(unsigned char *aes_key, size_t start_block, +int read_blocks_with_decryption(unsigned char *aes_key, block_t start_block, size_t nb_blocks, void *buffer); /** @@ -76,7 +78,7 @@ int read_blocks_with_decryption(unsigned char *aes_key, size_t start_block, * @param buffer The buffer containing the blocks. * @return int 0 on success, -1 on error. */ -int write_blocks_with_encryption(unsigned char *aes_key, size_t start_block, +int write_blocks_with_encryption(unsigned char *aes_key, block_t start_block, size_t nb_blocks, void *buffer); #endif /* BLOCK_H */ diff --git a/include/crypto.h b/include/crypto.h index b2bfe64..bce1c76 100644 --- a/include/crypto.h +++ b/include/crypto.h @@ -122,4 +122,14 @@ EVP_PKEY *load_rsa_keypair_from_disk(const char *public_key_path, const char *private_key_path, char *passphrase); +/** + * @brief Loads the RSA private and public keys from the current user's home + * directory. + * + * @param passphrase Gets filled with the passphrase the user enters if he + * decides to. + * @return EVP_PKEY* The loaded RSA keypair. + */ +EVP_PKEY *load_rsa_keypair_from_home(char **passphrase); + #endif /* CRYPTO_H */ diff --git a/include/cryptofuse.h b/include/cryptofuse.h new file mode 100644 index 0000000..ef1d832 --- /dev/null +++ b/include/cryptofuse.h @@ -0,0 +1,50 @@ +#ifndef CRYPTOFUSE_H + +#define FUSE_USE_VERSION 30 + +#include + +void *cryptfs_init(struct fuse_conn_info *info); +void cryptfs_destroy(void *ptr); +int cryptfs_statfs(const char *path, struct statvfs *stats); + +int cryptfs_mkdir(const char *path, mode_t mode); +int cryptfs_mknod(const char *path, mode_t mode, dev_t num); + +int cryptfs_rmdir(const char *path); + +int cryptfs_getattr(const char *path, struct stat *st); +int cryptfs_open(const char *path, struct fuse_file_info *file); +int cryptfs_read(const char *path, char *buf, size_t sz, off_t offset, + struct fuse_file_info *file); +int cryptfs_write(const char *path, const char *buf, size_t sz, off_t offset, + struct fuse_file_info *file); +int cryptfs_write_buf(const char *path, struct fuse_bufvec *buf, off_t off, + struct fuse_file_info *file); +int cryptfs_read_buf(const char *path, struct fuse_bufvec **bufp, + size_t size, off_t off, struct fuse_file_info *file); +int cryptfs_opendir(const char *path, struct fuse_file_info *file); +int cryptfs_readdir(const char *path, void *data, fuse_fill_dir_t callback, off_t offset, + struct fuse_file_info *file); +int cryptfs_release(const char *path, struct fuse_file_info *file); +int cryptfs_releasedir(const char *path, struct fuse_file_info *file); +int cryptfs_create(const char *path, mode_t mode, struct fuse_file_info *file); +int cryptfs_ftruncate(const char *path, off_t offset, struct fuse_file_info *file); +int cryptfs_access(const char *path, int mode); + +int cryptfs_flush(const char *path, struct fuse_file_info *file); +int cryptfs_fsync(const char *path, int, struct fuse_file_info *file); +int cryptfs_fsyncdir(const char *path, int, struct fuse_file_info *file); + +int cryptfs_chmod(const char *path, mode_t mod); +int cryptfs_chown(const char *path, uid_t uid, gid_t gid); +int cryptfs_rename(const char *path, const char *name); + +int cryptfs_fallocate(const char *path, int, off_t, off_t, + struct fuse_file_info *file); + +static struct fuse_operations cryptfs_ops = { .init = cryptfs_init, + .open = cryptfs_open, + .read = cryptfs_read }; + +#endif /* CRYPTOFUSE_H */ diff --git a/include/format.h b/include/format.h index 34a92a7..db62175 100644 --- a/include/format.h +++ b/include/format.h @@ -1,6 +1,7 @@ #ifndef FORMAT_H #define FORMAT_H +#include #include #include "cryptfs.h" @@ -21,8 +22,10 @@ bool is_already_formatted(const char *file_path); * @param cfs The `struct CryptFS` structure to fill. * @param rsa_passphrase The passphrase used to encrypt the RSA private key. * Set to NULL if no passphrase is needed. + * @param existing_rsa_keypair The RSA keypair to use. */ -void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase); +void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase, + EVP_PKEY *existing_rsa_keypair); /** * @brief Format the given file to a cryptfs file system. @@ -30,7 +33,16 @@ void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase); * @param path The path to the file to format. * @param rsa_passphrase The passphrase used to encrypt the RSA private key. * Set to NULL if no passphrase is needed. + * @param existing_rsa_keypair The existing RSA keypair to use. */ -void format_fs(const char *path, char *rsa_passphrase); +void format_fs(const char *path, char *rsa_passphrase, + EVP_PKEY *existing_rsa_keypair); +/** + * @brief Check if the keys (public and private) are already generated. + * + * @return False if the file does not exist. + * @return True if the file exist. + */ +bool keypair_in_home_exist(void); #endif /* FORMAT_H */ diff --git a/include/fuseblock.h b/include/fuseblock.h new file mode 100644 index 0000000..9c8f5ef --- /dev/null +++ b/include/fuseblock.h @@ -0,0 +1,91 @@ +#ifndef FUSE_BLOCK_H +#define FUSE_BLOCK_H + +#include + +enum block_type { + DATA = 0, + STAT = 1, + DIR = 2 +}; + +/** + * @brief Block type is DATA. + */ +struct datablock { + enum block_type type; + size_t next_block; + size_t sz; + char data[]; +}; + +struct fusestat { + //TODO stat + size_t data_block; + size_t size; +}; + +/** + * @brief Block type is STAT. + * Contains everything relevant about nodes, links, files etc... + */ +struct statblock { + enum block_type type; + size_t elems; + struct fusestat stats[]; +}; + +/** + * @brief This struct should never exceed size 16 + 256 = 272 bytes. + * Ends with a null character. + */ +struct nodeptr { + size_t stat_block; + size_t id; + char name[]; +}; + +/** + * @brief Block type is DIR. + * The data in the filesystem blocks that contain + * everything a saved directory contains. + * To access the next block use the offset + * indicated by next_block or ignore if it is 0. + * Each elements of elems ends with a null character. + * First block of every filesystem is the root's dirblock. + */ +struct dirblock { + enum block_type type; + unsigned int nodes; + size_t next_block; + struct nodeptr elems[]; +}; + +struct dirblock* get_dirblock(size_t block); +struct statblock* get_statblock(size_t block); +struct datablock* get_datablock(size_t block); + +int merge_blocks(size_t starting); + +void swap_blocks(size_t block1, size_t block2); + +/** + * @brief It is the responsibility of the user to free + * the returned pointer (not the contained strings though). + * + * @param block The first block + * @return char** A NULL terminated list of names that the user has to free + */ +char** read_dirblock(size_t block); + +/** + * @brief It is the responsibility of the user to free + * the returned pointer (not the contained strings though). + * + * @return char** A NULL terminated list of names that the user has to free + */ +char** read_rootdir(); + +size_t get_dirblock_by_path(const char* path); + +#endif /* FUSE_BLOCK_H */ \ No newline at end of file diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..7509711 --- /dev/null +++ b/include/io.h @@ -0,0 +1,10 @@ +#ifndef IO_H +#define IO_H + +/** + * @brief Get a char from stdin and skips the next newline + * @return The char from stdin + */ +char get_char_from_stdin(void); + +#endif /* IO_H */ diff --git a/include/passphrase.h b/include/passphrase.h index 1586255..1f52aea 100644 --- a/include/passphrase.h +++ b/include/passphrase.h @@ -5,8 +5,10 @@ /** * @brief Ask user for passphrase (only print '*' for each char on stdout) + * @param confirm if true, ask user to confirm passphrase + * @return char* the chosen passphrase */ -char *ask_user_passphrase(void); +char *ask_user_passphrase(bool confirm); /** * @brief Check if a PEM encoded RSA private key has a passphrase diff --git a/src/formater.c b/src/formater.c new file mode 100644 index 0000000..0109128 --- /dev/null +++ b/src/formater.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +#include "crypto.h" +#include "format.h" +#include "io.h" +#include "passphrase.h" +#include "print.h" +#include "xalloc.h" + +static void ask_new_password(char **passphrase) +{ + printf("Do you want to secure your new key with a passphrase? [y/n]: "); + char answer = getchar(); + getchar(); // Consume the newline + + if (answer != 'y' && answer != 'Y') + { + print_warning("No passphrase will be used. The key may be exposed.\n"); + (void)passphrase; + } + else + *passphrase = ask_user_passphrase(true); +} + +int main(int argc, char *argv[]) +{ + char *passphrase = NULL; + char *path = NULL; + EVP_PKEY *existing_rsa_keypair = NULL; + + switch (argc) + { + case 2: + path = argv[1]; + + break; + case 3: + path = argv[1]; + passphrase = argv[2]; + break; + + default: + printf("Usage: %s [RSA key passphrase]\n", argv[0]); + return EXIT_FAILURE; + } + + // ALREADY FORMATTED + if (is_already_formatted(path)) + { + print_warning( + "The file is already formatted. Do you want to overwrite it? " + "[y/n]: "); + char answer = get_char_from_stdin(); + if (answer != 'y' && answer != 'Y') + { + printf("Aborting...\n"); + return 0; + } + else + printf("Overwriting...\n"); + } + + // KEYS ALREADY PRESENT IN HOME + if (keypair_in_home_exist()) + { + print_info("Keys already generated, do you want to use them? [y/n] "); + char answer = get_char_from_stdin(); + if (answer == 'y' || answer == 'Y') + existing_rsa_keypair = load_rsa_keypair_from_home(&passphrase); + else + ask_new_password(&passphrase); + } + else + ask_new_password(&passphrase); + + format_fs(path, passphrase, existing_rsa_keypair); + print_success("The file/device `%s` has been formatted successfully!\n", + path); + + if (passphrase) + free(passphrase); + + return 0; +} diff --git a/code/core/constraints.c b/src/fs/constraints.c similarity index 100% rename from code/core/constraints.c rename to src/fs/constraints.c diff --git a/code/core/filesystem/block.c b/src/fs/filesystem/block.c similarity index 83% rename from code/core/filesystem/block.c rename to src/fs/filesystem/block.c index 74ff6cc..3b50460 100644 --- a/code/core/filesystem/block.c +++ b/src/fs/filesystem/block.c @@ -8,18 +8,19 @@ #include "crypto.h" #include "xalloc.h" -const char *BLOCK_PATH = NULL; +const char *DEVICE_PATH = NULL; uint32_t BLOCK_SIZE = 0; void set_device_path(const char *path) { - BLOCK_PATH = path; + assert(path != NULL); + DEVICE_PATH = path; } const char *get_device_path() { - assert(BLOCK_PATH != NULL); - return BLOCK_PATH; + assert(DEVICE_PATH != NULL); + return DEVICE_PATH; } void set_block_size(const uint32_t size) @@ -30,13 +31,13 @@ void set_block_size(const uint32_t size) uint32_t get_block_size() { - assert(BLOCK_SIZE != 0); + assert(BLOCK_SIZE >= CRYPTFS_BLOCK_SIZE_BYTES); return BLOCK_SIZE; } -int read_blocks(size_t start_block, size_t nb_blocks, void *buffer) +int read_blocks(block_t start_block, size_t nb_blocks, void *buffer) { - assert(BLOCK_PATH != NULL); + assert(DEVICE_PATH != NULL); assert(BLOCK_SIZE != 0); if (nb_blocks == 0) @@ -44,7 +45,7 @@ int read_blocks(size_t start_block, size_t nb_blocks, void *buffer) if (!buffer) return -1; - FILE *file = fopen(BLOCK_PATH, "r"); + FILE *file = fopen(DEVICE_PATH, "r"); if (!file) return -1; @@ -67,9 +68,9 @@ int read_blocks(size_t start_block, size_t nb_blocks, void *buffer) return 0; } -int write_blocks(size_t start_block, size_t nb_blocks, void *buffer) +int write_blocks(block_t start_block, size_t nb_blocks, void *buffer) { - assert(BLOCK_PATH != NULL); + assert(DEVICE_PATH != NULL); assert(BLOCK_SIZE != 0); if (nb_blocks == 0) @@ -77,7 +78,7 @@ int write_blocks(size_t start_block, size_t nb_blocks, void *buffer) if (buffer == NULL) return -1; - FILE *file = fopen(BLOCK_PATH, "r+"); + FILE *file = fopen(DEVICE_PATH, "r+"); if (file == NULL) return -1; @@ -100,7 +101,7 @@ int write_blocks(size_t start_block, size_t nb_blocks, void *buffer) return 0; } -int read_blocks_with_decryption(unsigned char *aes_key, size_t start_block, +int read_blocks_with_decryption(unsigned char *aes_key, block_t start_block, size_t nb_blocks, void *buffer) { unsigned char *encrypted_buffer = xmalloc(nb_blocks, get_block_size()); diff --git a/code/core/filesystem/fat_parsers.c b/src/fs/filesystem/fat_parsers.c similarity index 100% rename from code/core/filesystem/fat_parsers.c rename to src/fs/filesystem/fat_parsers.c diff --git a/code/core/filesystem/format.c b/src/fs/filesystem/format.c similarity index 73% rename from code/core/filesystem/format.c rename to src/fs/filesystem/format.c index 93577d3..9400348 100644 --- a/code/core/filesystem/format.c +++ b/src/fs/filesystem/format.c @@ -1,6 +1,10 @@ #include "format.h" #include +#include +#include +#include +#include #include "block.h" #include "cryptfs.h" @@ -34,7 +38,8 @@ bool is_already_formatted(const char *file_path) return true; } -void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase) +void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase, + EVP_PKEY *existing_rsa_keypair) { /// ------------------------------------------------------------ /// BLOCK 0 : HEADER @@ -54,7 +59,12 @@ void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase) // Generate AES + RSA keys unsigned char *aes_key = generate_aes_key(); - EVP_PKEY *rsa_key = generate_rsa_keypair(); + + EVP_PKEY *rsa_key = NULL; + if (existing_rsa_keypair != NULL) + rsa_key = existing_rsa_keypair; + else + rsa_key = generate_rsa_keypair(); // Store the RSA modulus and the RSA public exponent in the header store_keys_in_keys_storage(cfs->keys_storage, rsa_key, aes_key); @@ -85,14 +95,15 @@ void format_fill_filesystem_struct(struct CryptFS *cfs, char *rsa_passphrase) EVP_PKEY_free(rsa_key); } -void format_fs(const char *path, char *rsa_passphrase) +void format_fs(const char *path, char *rsa_passphrase, + EVP_PKEY *existing_rsa_keypair) { struct CryptFS *cfs = xcalloc(1, sizeof(struct CryptFS)); set_block_size(CRYPTFS_BLOCK_SIZE_BYTES); set_device_path(path); - format_fill_filesystem_struct(cfs, rsa_passphrase); + format_fill_filesystem_struct(cfs, rsa_passphrase, existing_rsa_keypair); FILE *file = fopen(path, "w+"); if (file == NULL) @@ -108,3 +119,24 @@ void format_fs(const char *path, char *rsa_passphrase) fclose(file); } + +bool keypair_in_home_exist(void) +{ + char *home = getenv("HOME"); + if (!home) + internal_error_exit("Impossible to get the user directory path\n", + EXIT_FAILURE); + + char *private_path = xcalloc(PATH_MAX + 1, sizeof(char)); + char *public_path = xcalloc(PATH_MAX + 1, sizeof(char)); + snprintf(private_path, PATH_MAX, "%s/%s", home, ".cryptfs/private.pem"); + snprintf(public_path, PATH_MAX, "%s/%s", home, ".cryptfs/public.pem"); + + bool exist = + access(private_path, F_OK) == 0 && access(public_path, F_OK) == 0; + + free(private_path); + free(public_path); + + return exist; +} diff --git a/src/fs/misc/io.c b/src/fs/misc/io.c new file mode 100644 index 0000000..b4829c7 --- /dev/null +++ b/src/fs/misc/io.c @@ -0,0 +1,10 @@ +#include + +char get_char_from_stdin(void) +{ + char answer = getchar(); + while (answer == '\n') + answer = getchar(); + getchar(); // Consume the newline + return answer; +} diff --git a/code/core/misc/print.c b/src/fs/misc/print.c similarity index 90% rename from code/core/misc/print.c rename to src/fs/misc/print.c index 2bde921..df8bbb6 100644 --- a/code/core/misc/print.c +++ b/src/fs/misc/print.c @@ -18,10 +18,9 @@ void internal_error_exit(const char *msg, int error_code, ...) va_list args; va_start(args, error_code); - fprintf(stderr, RED_STR("[CRYPTFS INTERNAL ERROR]: ")); + fprintf(stderr, RED_STR("[CRYPTFS INTERNAL ERROR EXIT]: ")); fprintf(stderr, msg, args); -#ifdef DEBUG // Printing backtrace void *array[BACKTRACE_SIZE]; size_t size; @@ -35,7 +34,6 @@ void internal_error_exit(const char *msg, int error_code, ...) for (i = 0; i < size; i++) fprintf(stderr, RED_STR("[CRYPTFS STACK FRAMES]: ") "%s\n", strings[i]); free(strings); -#endif va_end(args); exit(error_code); @@ -46,7 +44,7 @@ void error_exit(const char *msg, int error_code, ...) va_list args; va_start(args, error_code); - fprintf(stderr, RED_STR("[CRYPTFS ERROR]: ")); + fprintf(stderr, RED_STR("[CRYPTFS ERROR EXIT]: ")); fprintf(stderr, msg, args); va_end(args); exit(error_code); @@ -56,7 +54,7 @@ void print_error(const char *msg, ...) { va_list args; va_start(args, msg); - fprintf(stderr, RED_STR("[CRYPTFS ERROR] : ")); + fprintf(stderr, RED_STR("[CRYPTFS ERROR]: ")); fprintf(stderr, msg, args); va_end(args); } @@ -65,7 +63,7 @@ void print_warning(const char *msg, ...) { va_list args; va_start(args, msg); - fprintf(stderr, YELLOW_STR("[CRYPTFS WARNING] : ")); + fprintf(stderr, YELLOW_STR("[CRYPTFS WARNING]: ")); fprintf(stderr, msg, args); va_end(args); } diff --git a/code/core/misc/xalloc.c b/src/fs/misc/xalloc.c similarity index 100% rename from code/core/misc/xalloc.c rename to src/fs/misc/xalloc.c diff --git a/code/core/security/crypto.c b/src/fs/security/crypto.c similarity index 100% rename from code/core/security/crypto.c rename to src/fs/security/crypto.c diff --git a/src/fs/security/crypto_adduser.c b/src/fs/security/crypto_adduser.c new file mode 100644 index 0000000..2788bfd --- /dev/null +++ b/src/fs/security/crypto_adduser.c @@ -0,0 +1,45 @@ + +#include +#include + +#include "cryptfs.h" +#include "print.h" + +int8_t find_rsa_matching_key(EVP_PKEY *rsa_private, + struct CryptFS_Key *keys_storage) +{ + BIGNUM *rsa_private_modulus = NULL; + + if (EVP_PKEY_get_bn_param(rsa_private, OSSL_PKEY_PARAM_RSA_N, + &rsa_private_modulus) + != 1) + internal_error_exit("Failed to get the private RSA modulus\n", + EXIT_FAILURE); + uint8_t i = 0; + for (; i < NB_ENCRYPTION_KEYS; i++) + { + // Compare the exponent and the modulus of the both keys + BIGNUM *key_storage_modulus = + BN_bin2bn(keys_storage[i].rsa_n, RSA_KEY_SIZE_BYTES, NULL); + if (BN_cmp(key_storage_modulus, rsa_private_modulus) == 0) + { + // Init for checking the RSA parameters + EVP_PKEY_CTX *rsa_private_ctx = EVP_PKEY_CTX_new(rsa_private, NULL); + if (rsa_private_ctx == NULL) + internal_error_exit("Failed to create the EVP_PKEY_CTX\n", + EXIT_FAILURE); + + if (EVP_PKEY_pairwise_check(rsa_private_ctx) == 1) + { + BN_free(key_storage_modulus); + EVP_PKEY_CTX_free(rsa_private_ctx); + break; + } + EVP_PKEY_CTX_free(rsa_private_ctx); + } + BN_free(key_storage_modulus); + } + BN_free(rsa_private_modulus); + + return i == NB_ENCRYPTION_KEYS ? -1 : i; +} diff --git a/src/fs/security/crypto_disk.c b/src/fs/security/crypto_disk.c new file mode 100644 index 0000000..410a266 --- /dev/null +++ b/src/fs/security/crypto_disk.c @@ -0,0 +1,59 @@ +#include +#include + +#include "crypto.h" +#include "passphrase.h" +#include "print.h" +#include "xalloc.h" + +EVP_PKEY *load_rsa_keypair_from_disk(const char *public_key_path, + const char *private_key_path, + char *passphrase) +{ + EVP_PKEY *rsa_keypair = NULL; + + FILE *public_key_file = fopen(public_key_path, "r"); + FILE *private_key_file = fopen(private_key_path, "r"); + + if (public_key_file) + { + if (PEM_read_PUBKEY(public_key_file, &rsa_keypair, NULL, NULL) == NULL) + return NULL; + fclose(public_key_file); + } + + if (private_key_file) + { + if (PEM_read_PrivateKey(private_key_file, &rsa_keypair, NULL, + passphrase) + == NULL) + return NULL; + fclose(private_key_file); + } + + return rsa_keypair; +} +EVP_PKEY *load_rsa_keypair_from_home(char **passphrase) +{ + // STEP 2 : Verify if the private key is encrypted + char *home = getenv("HOME"); + if (!home) + internal_error_exit("Impossible to get the user directory path\n", + EXIT_FAILURE); + + char *private_path = xcalloc(PATH_MAX + 1, sizeof(char)); + char *public_path = xcalloc(PATH_MAX + 1, sizeof(char)); + snprintf(private_path, PATH_MAX, "%s/%s", home, ".cryptfs/private.pem"); + snprintf(public_path, PATH_MAX, "%s/%s", home, ".cryptfs/public.pem"); + + EVP_PKEY *rsa_keypair = + load_rsa_keypair_from_disk(public_path, private_path, *passphrase); + + if (rsa_keypair == NULL) + error_exit("Impossible to load the RSA keypair\n", EXIT_FAILURE); + + free(private_path); + free(public_path); + + return rsa_keypair; +} diff --git a/code/core/security/crypto_format.c b/src/fs/security/crypto_format.c similarity index 81% rename from code/core/security/crypto_format.c rename to src/fs/security/crypto_format.c index c96cbe1..ed39398 100644 --- a/code/core/security/crypto_format.c +++ b/src/fs/security/crypto_format.c @@ -108,7 +108,7 @@ void store_keys_in_keys_storage(struct CryptFS_Key *keys_storage, if (i == NB_ENCRYPTION_KEYS) error_exit( - "No more space for any more keys, their is already %lu users " + "No more space for any more keys, there is already %lu users " "in the keys storage\n", EXIT_FAILURE, NB_ENCRYPTION_KEYS); } @@ -179,42 +179,3 @@ void write_rsa_keys_on_disk(EVP_PKEY *rsa_keypair, const char *path_to_write, free(public_pem_path); free(private_pem_path); } - -int8_t find_rsa_matching_key(EVP_PKEY *rsa_private, - struct CryptFS_Key *keys_storage) -{ - BIGNUM *rsa_private_modulus = NULL; - - if (EVP_PKEY_get_bn_param(rsa_private, OSSL_PKEY_PARAM_RSA_N, - &rsa_private_modulus) - != 1) - internal_error_exit("Failed to get the private RSA modulus\n", - EXIT_FAILURE); - uint8_t i = 0; - for (; i < NB_ENCRYPTION_KEYS; i++) - { - // Compare the exponent and the modulus of the both keys - BIGNUM *key_storage_modulus = - BN_bin2bn(keys_storage[i].rsa_n, RSA_KEY_SIZE_BYTES, NULL); - if (BN_cmp(key_storage_modulus, rsa_private_modulus) == 0) - { - // Init for checking the RSA parameters - EVP_PKEY_CTX *rsa_private_ctx = EVP_PKEY_CTX_new(rsa_private, NULL); - if (rsa_private_ctx == NULL) - internal_error_exit("Failed to create the EVP_PKEY_CTX\n", - EXIT_FAILURE); - - if (EVP_PKEY_pairwise_check(rsa_private_ctx) == 1) - { - BN_free(key_storage_modulus); - EVP_PKEY_CTX_free(rsa_private_ctx); - break; - } - EVP_PKEY_CTX_free(rsa_private_ctx); - } - BN_free(key_storage_modulus); - } - BN_free(rsa_private_modulus); - - return i == NB_ENCRYPTION_KEYS ? -1 : i; -} diff --git a/code/core/security/hash.c b/src/fs/security/hash.c similarity index 100% rename from code/core/security/hash.c rename to src/fs/security/hash.c diff --git a/code/core/security/passphrase.c b/src/fs/security/passphrase.c similarity index 53% rename from code/core/security/passphrase.c rename to src/fs/security/passphrase.c index 2908695..5b3bef1 100644 --- a/code/core/security/passphrase.c +++ b/src/fs/security/passphrase.c @@ -11,7 +11,7 @@ #define PASSPHRASE_MAX_LENGTH 50 -char *ask_user_passphrase(void) +char *ask_user_passphrase(bool confirm) { char *passphrase = NULL; char *passphrase_check = NULL; @@ -31,33 +31,44 @@ char *ask_user_passphrase(void) while (true) { - printf("Enter a passphrase (MAX : %u): ", PASSPHRASE_MAX_LENGTH); + confirm + ? printf("Enter a passphrase (MAX : %u): ", PASSPHRASE_MAX_LENGTH) + : printf("Enter passphrase: "); if (scanf("%ms", &passphrase) != 1 || printf("\n") < 0) internal_error_exit("Failed to read passphrase", EXIT_FAILURE); if (strlen(passphrase) <= PASSPHRASE_MAX_LENGTH) { - // Ask again to be sure the user entered the passphrase correctly - printf("Enter the same passphrase again: "); - - if (scanf("%ms", &passphrase_check) != 1 || printf("\n") < 0) - internal_error_exit("Failed to read second passphrase", - EXIT_FAILURE); + if (confirm) + { + // Ask again to be sure the user entered the passphrase + // correctly + printf("Enter the same passphrase again: "); - if (strcmp(passphrase, passphrase_check) == 0) + if (scanf("%ms", &passphrase_check) != 1 || printf("\n") < 0) + internal_error_exit("Failed to read second passphrase", + EXIT_FAILURE); + if (strcmp(passphrase, passphrase_check) == 0) + { + print_success("Passphrase is valid!\n"); + tcsetattr(STDIN_FILENO, TCSANOW, &old_settings); + free(passphrase_check); + getchar(); // Consume the newline + return passphrase; + } + else + { + print_warning("Passphrases do not match\n"); + free(passphrase_check); + free(passphrase); + } + } + else { - print_success("Passphrase is valid!\n"); tcsetattr(STDIN_FILENO, TCSANOW, &old_settings); - free(passphrase_check); getchar(); // Consume the newline return passphrase; } - else - { - print_warning("Passphrases do not match\n"); - free(passphrase_check); - free(passphrase); - } } else @@ -74,19 +85,12 @@ bool rsa_private_is_encrypted(const char *rsa_path) FILE *file = fopen(rsa_path, "r"); if (!file) return false; - char line[256]; - while (fgets(line, sizeof(line), file)) - { - if (strstr(line, "Proc-Type: 4,ENCRYPTED") || strstr(line, "ENCRYPTED")) - { - fclose(file); - return true; - } - { - fclose(file); - return true; - } - } + char line[sizeof( + "-----BEGIN ENCRYPTED PRIVATE KEY-----")]; + bool is_private = false; + if (fgets(line, sizeof(line), file)) + is_private = strstr(line, "ENCRYPTED"); + fclose(file); - return false; + return is_private; } diff --git a/src/fuse/functions.c b/src/fuse/functions.c new file mode 100644 index 0000000..9f9d32a --- /dev/null +++ b/src/fuse/functions.c @@ -0,0 +1,23 @@ +#include "cryptofuse.h" + +#include + +void *cryptfs_init(struct fuse_conn_info *info) +{ + printf("Using FUSE protocol %d.%d\n", info->proto_major, info->proto_minor); + //TODO allocate data + + return NULL; +} + +int cryptfs_open(const char *path, struct fuse_file_info *file) +{ + puts("open"); + if((file->flags & 3) != O_RDONLY) + return -EACCES; + + file->fh = inode_get_idx_by_path(path); + printf("%s is inode %d\n", path, file->fh); + + return 0; +} \ No newline at end of file diff --git a/src/fuse/fuseblock.c b/src/fuse/fuseblock.c new file mode 100644 index 0000000..c606526 --- /dev/null +++ b/src/fuse/fuseblock.c @@ -0,0 +1,12 @@ +#include "fuseblock.h" + +#include "block.h" + +//TODO read multiple blocks at once +struct dirblock* get_dirblock(size_t block) { + void* block = malloc(get_block_size()); + read_blocks(block, 1, ) +} + +struct statblock* get_statblock(size_t block); +struct datablock* get_datablock(size_t block); \ No newline at end of file diff --git a/src/mount_fuse.c b/src/mount_fuse.c new file mode 100644 index 0000000..00ea599 --- /dev/null +++ b/src/mount_fuse.c @@ -0,0 +1,58 @@ +#include + +#include "cryptfs.h" +#include "crypto.h" +#include "print.h" +#include "xalloc.h" + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + printf("Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + const char *device_path = argv[1], *mount_path = argv[2]; + (void)mount_path; + + // Load the RSA keypair from the user's home directory + char *passphrase = NULL; + EVP_PKEY *rsa_key = load_rsa_keypair_from_home(&passphrase); + + // Read the header of the device + FILE *device_file = fopen(device_path, "r"); + if (!device_file) + error_exit("Impossible to open the device file\n", EXIT_FAILURE); + + struct CryptFS_Header header = { 0 }; + if (fread(&header, sizeof(struct CryptFS_Header), 1, device_file) != 1) + error_exit("Impossible to read the header\n", EXIT_FAILURE); + + fclose(device_file); + + // Set the file system global variables + set_device_path(device_path); + set_block_size(header.blocksize); + + // Read the keys storage from the device + struct CryptFS_Key *keys_storage = + xcalloc(NB_ENCRYPTION_KEYS, get_block_size()); + + read_blocks(KEYS_STORAGE_BLOCK, NB_ENCRYPTION_KEYS, keys_storage); + + // Searching for a legitimate key + int8_t matching_rsa_index = find_rsa_matching_key(rsa_key, keys_storage); + if (matching_rsa_index == -1) + { + print_error("You are not allowed to access this encrypted device\n"); + error_exit("No matching RSA key found\n", EXIT_FAILURE); + } + + // Free the memory + EVP_PKEY_free(rsa_key); + free(keys_storage); + free(passphrase); + + // mount the decrypted data using the key + return 0; +} diff --git a/tests/criterion/mount_test.c b/tests/criterion/mount_test.c new file mode 100644 index 0000000..82da76b --- /dev/null +++ b/tests/criterion/mount_test.c @@ -0,0 +1,22 @@ +#include +#include + +#include "mount.h" + +Test(mount_exists, not_exist, .timeout = 10, .init = cr_redirect_stdout) +{ + int result = mount_exists("build/mount_exists.not_exist.test.cfs", "build/mount_exists.not_exist.test.cfs"); + cr_assert_eq(result, -1, "result = %d", result); +} + +Test(mount_exists, not_a_cryptfs, .timeout = 10, .init = cr_redirect_stdout) +{ + int result = mount_exists("build/mount_exists.not_a_cryptfs.test.cfs", "build/mount_exists.not_a_cryptfs.test.cfs"); + cr_assert_eq(result, 1, "result = %d", result); +} + +Test(mount_exists, exist, .timeout = 10, .init = cr_redirect_stdout) +{ + int result = mount_exists("build/mount_exists.exist.test.cfs", "build/mount_exists.exist.test.cfs"); + cr_assert_eq(result, 0, "result = %d", result); +}