From 1536f1e0287b8281014200ef6911b294272c4773 Mon Sep 17 00:00:00 2001 From: "@syxhe" Date: Wed, 11 Jun 2025 19:39:52 -0500 Subject: Start fixing the encryption scheme --- src/encryption.c | 272 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 216 insertions(+), 56 deletions(-) (limited to 'src/encryption.c') diff --git a/src/encryption.c b/src/encryption.c index fd42929..a3f75d1 100644 --- a/src/encryption.c +++ b/src/encryption.c @@ -16,13 +16,15 @@ #define _GNU_SOURCE -#include "encryption.h" #include "shared.h" +#include "encryption.h" +#include "threadpool.h" #include #include #include +#include #include #include #include @@ -34,19 +36,42 @@ #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 #if ___VXGG___USE_CLS_CALLBACK___ > 0 -void naclfaildefault(void *none) { +/** + * @brief Default sodium init fail callback for use in `checksodiumcb()` + * + * @param none Unused param to fit callback spec + */ +static void naclfaildefault(void *none) { none = none; // Makes gcc happy if(___VXGG___VERBOSE_ERRORS___) error(1, ENOTSUP, " Couldn't initialize sodium for some reason. Quitting..."); exit(EXIT_FAILURE); } -int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { +/** + * @brief Internal function to deal with the `___VXGG___USE_CLS_CALLBACK___` macro + * + * `checksodiumcb()` runs the sodium function `sodium_init()` and on error calls the provided callback with the provided data. The + * callback and data default to `naclfaildefault` and `NULL`, but can be changed when the `set` parameter is non-zero. When `set` + * is zero, the sodium init check is preformed + * + * @note `checksodiumcb()` is ran when these conditions are true - 1: The `___VXGG___ALWAYS_CHECK_LIBSODIUM___` and `___VXGG___USE_CLS_CALLBACK___` + * macros are both greater than 0 when compiling, and 2: a function in `encryption.c` calls a function originating from sodium. This + * function exists as a way to deal with sodium failing yourself, instead of instantly calling `exit()`. If you don't care to handle + * it, or are initializing sodium yourself, this is unnecessary + * + * @param callback A callback to be ran when sodium fails to initialize itself. Ignored if `set` is zero. Must be non-null when `set` is non-zero + * @param data Data to be passed to the callback when it is fired. Ignored if `set` is zero. May be null + * @param set Flag on whether to check sodium or to set a new callback and data pair. Checks sodium when zero, sets callback & data when non-zero + * @retval int + */ +static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { static vxgg_naclfailcb cb = naclfaildefault; static void *usr = NULL; int ret; if(set) { + if(cb == NULL) ERRRET(EINVAL, -1); cb = callback; usr = data; return 2; // libsodium normally returns 1 if the library is already initialized, so this is to signal that the callback has been updated @@ -68,7 +93,15 @@ void vxgg_setsodiumfailcb(vxgg_naclfailcb cb, void *data) { #endif -void checksodium(void) { +/** + * @brief Simple function to check if sodium has been properly initialized + * + * `checksodium()` will run in functions located in `encryption.h` only when the macro `___VXGG___ALWAYS_CHECK_LIBSODIUM___` is greater + * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro. + * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`. + * + */ +static void checksodium(void) { #if ___VXGG___USE_CLS_CALLBACK___ > 0 checksodiumcb(NULL, NULL, 0); #else @@ -103,42 +136,89 @@ int linkto(const char * const target, int tgfd) { return res; } + +static void __ucl_close(void *fd) { + if(!fd) return; + close(*(int*)fd); + *(int*)fd = -1; + return; +} + +static void __ucl_fclose(void *file) { + if(!file) return; + fclose((FILE*)file); + return; +} + int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 - checksodium(); + checksodium(); #endif + cleanup_CREATE(10); int fd = -1, tfd = -1; + FILE *src, *dst; + char *targetdir; // Open the target file if((fd = open(target, O_RDONLY)) < 0) return -1; + cleanup_REGISTER(__ucl_close, (void*)&fd); // Create a temp file for writing - char *targetdir = xdirname(output); - tfd = maketmp(targetdir); - free(targetdir); - if(tfd < 0) { - close(fd); - return -1; - } - - FILE *src, *dst; - if(!(src = fdopen(fd, "rb"))) - ERROR(1, errno, " Couldn't open \"%s\"", , target); - if(!(dst = fdopen(tfd, "wb"))) - ERROR(1, errno, " Couldn't open \"%s\"", , output); - if(encrypttofile(src, dst, key) < 0) - ERROR(1, ENOTRECOVERABLE, " I don't even have a way to cause an error here. How did you do it?",); - - // Link the temp file into the system - if(linkto(output, tfd) < 0) - ERROR(1, errno, " Could not link \"%s\" into system after encryption", , output); - - fclose(dst); - fclose(src); - // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors + targetdir = vxdirname(output); + if(!targetdir) + cleanup_MARK(); + cleanup_CNDREGISTER(free, targetdir); + + // Actually get the file descriptor for the temp file + cleanup_CNDEXEC( + tfd = maketmp(targetdir); + if(tfd < 0) + cleanup_MARK(); + cleanup_CNDREGISTER(__ucl_close, (void*)&tfd); + ); + + // Create a FILE* version of the source fd + cleanup_CNDEXEC( + if(!(src = fdopen(fd, "rb"))) { + WARN(errno, " Couldn't open \"%s\"", , target); + cleanup_MARK(); + } + cleanup_CNDREGISTER(__ucl_fclose, (void*)&src); + ) + + // Create a FILE* version of the target fd + cleanup_CNDEXEC( + if(!(dst = fdopen(tfd, "wb"))) { + WARN(errno, " Couldn't open \"%s\"", , output); + cleanup_MARK(); + } + cleanup_CNDREGISTER(__ucl_fclose, (void*)dst); + ); + + + // Do the encryption now that everything has been set up + cleanup_CNDEXEC( + // Not going to bother changing this, it probably is catastrophic if an error happens when it shouldn't + if(encrypttofile(src, dst, key) < 0) + ERROR(1, ENOTRECOVERABLE, " I don't even have a way to cause an error here. How did you do it?",); + + // Link the temp file into the system + if(linkto(output, tfd) < 0) + WARN(errno, " Could not link \"%s\" into system after encryption", , output); + + free(targetdir); + fclose(dst); + fclose(src); + // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors + ); + + cleanup_CNDFIRE(); + if(cleanup_ERRORFLAGGED) + return -1; + return 0; } @@ -148,31 +228,64 @@ int decryptto(const char * const encrypted, const char * const target, const uns checksodium(); #endif + cleanup_CREATE(10); FILE *src, *dst; - if(!(src = fopen(encrypted, "rb"))) - ERROR(1, errno, " Could not open \"%s\" for decryption", , encrypted); - - int fdst = maketmp(target); - if(!fdst) - ERROR(1, errno, " Could not get temp file for decryption", ); - if(!(dst = fdopen(fdst, "wb"))) - ERROR(1, errno, " Could not open \"%s\" for writing decrypted data", , target); - - if(decrypttofile(src, dst, key) < 0) - ERROR(1, errno, " How did you even cause an error?",); - - // Link temp into system - if(linkto(target, fdst) < 0) - ERROR(1, errno, " Could not link \"%s\" into system", , target); + int fdst; - fclose(dst); - fclose(src); - // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors + // Open the source file + if(!(src = fopen(encrypted, "rb"))) { + WARN(errno, " Could not open \"%s\" for decryption", , encrypted); + return -1; + } + cleanup_REGISTER(__ucl_fclose, src); + + // Get a temp descriptor for the temp file + cleanup_CNDEXEC( + fdst = maketmp(target); + if(!fdst) { + WARN(errno, " Could not get temp file for decryption", ); + cleanup_MARK(); + } + cleanup_CNDREGISTER(__ucl_close, (void*)&fdst); + ); + + // Open a FILE* version of the temp file + cleanup_CNDEXEC( + if(!(dst = fdopen(fdst, "wb"))) { + WARN(errno, " Could not open \"%s\" for writing decrypted data", , target); + cleanup_MARK(); + } + cleanup_CNDREGISTER(__ucl_fclose, dst); + ); + + // Follow through with the rest of the decryption + cleanup_CNDEXEC( + if(decrypttofile(src, dst, key) < 0) + ERROR(1, errno, " How did you even cause an error?",); + + // Link temp into system + if(linkto(target, fdst) < 0) + WARN(errno, " Could not link \"%s\" into system", , target); + + fclose(dst); + fclose(src); + // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors + ); + + cleanup_CNDFIRE(); + if(cleanup_ERRORFLAGGED) + return -1; + // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the + // `dst = fdopen` line, a double close on the temp file descriptor would occur. I've been told that this is not catastrophic, + // and considering how my multithreading works it *should* be fine, but it very well could cause problems. The easy solution is + // to man up and just write another cleanup function to pop the last thing off the stack, but again this is an error I can't + // actually trigger so it's fine for now return 0; } +// TODO: Fix this mess int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { if(!src || !dst || !key) ERRRET(EINVAL, -1); #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 @@ -210,6 +323,7 @@ int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstr return 0; } +// TODO: Fix this as well int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { if(!src || !dst || !key) ERRRET(EINVAL, -1); #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 @@ -283,7 +397,6 @@ int genpassword(char **str, unsigned int words) { return words; } -// sodium_malloc wrapper. Calls `error()` or `abort()` depnding on the value of `___VXGG___XALLOC_EXIT_ON_ERROR___`. Will make sure libsodium is initialized if `___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0` void* xsodium_malloc(size_t size) { #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 checksodium(); @@ -298,15 +411,9 @@ void* xsodium_malloc(size_t size) { -/* - -#include "threadpool.h" +// TODO: Rewrite this to use the threadpool. Each newly scanned file should be pushed onto the threadpool as an encryption task -// #include // #include -// #include -// #include -// #include // dlinkedlist * scandirlist(const char * const dir, int (*selector)(const struct dirent *), int (*cmp)(const struct dirent **, const struct dirent **)) { // if(!dir || selector == NULL || cmp == NULL) ERRRET(EINVAL, NULL); @@ -333,8 +440,61 @@ void* xsodium_malloc(size_t size) { // return list; // } -// TODO: Rewrite this to use the threadpool. Each newly scanned file should be pushed onto the threadpool as an encryption task -*/ + + +struct __ucl_namelist_vals { + struct dirent **namelist; + int entries; +}; + +static void __ucl_namelist(void *namelist) { + if(!namelist) return; + struct __ucl_namelist_vals *real = namelist; + for(int i = 0; i > real->entries; i++) + free(real->namelist[i]); + free(real->namelist); + return; +} + +enum VXGG_FLC { + VXGG_FLC__INVALID, + VXGG_FLC__ENCRYPT, + VXGG_FLC__DECRYPT, + VXGG_FLC__TOOBIG +}; + +ctqueue * getfilelist(enum VXGG_FLC mode, const char * const dir, int threads, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) { + if(mode <= VXGG_FLC__INVALID || mode >= VXGG_FLC__TOOBIG || !dir || threads <= 0 || !filter || !compar) ERRRET(EINVAL, NULL); + + cleanup_CREATE(10); + ctqueue *ctq = NULL; + int files = -1; + + // Create the ctqueue for later + ctq = ctqueue_init(threads); + if(!ctq) + return NULL; + cleanup_REGISTER(ctqueue_free, ctq); + + // Get the scandir list + struct dirent **namelist; + if((files = scandir(dir, &namelist, filter, compar)) < 0) + cleanup_MARK(); + cleanup_CNDREGISTER(__ucl_namelist, (void*)(&(struct __ucl_namelist_vals){.namelist = namelist, .entries = files})); + + // Push everything onto the ctqueue + // TODO: Write task* compatible callbacks for encryption and decryption, then populate the ctqueue based on the specified mode + + cleanup_CNDFIRE(); + if(cleanup_ERRORFLAGGED) + return NULL; + + return ctq; +} + + + + /* int main(void) { -- cgit v1.2.3