From 0b03b9b95402348a8ab3b5c977e26154c538031e Mon Sep 17 00:00:00 2001 From: Liam Waldron Date: Sat, 22 Mar 2025 11:19:41 -0400 Subject: [PATCH] v0.2.0-rc - implement basic integrity checking framework --- VERSION | 2 +- include/config.h | 82 +++++---- include/data.h | 47 ++++-- include/globals.h | 18 +- include/pkgops.h | 14 +- include/security.h | 60 +++++++ src/libglacier.c | 406 ++++++++++++++++++++++++++++++++++----------- 7 files changed, 473 insertions(+), 156 deletions(-) diff --git a/VERSION b/VERSION index d8cfec1..605a7a1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0-rc +0.2.0-rc diff --git a/include/config.h b/include/config.h index bb38d23..0f0b006 100644 --- a/include/config.h +++ b/include/config.h @@ -1,5 +1,5 @@ /* - * config.h - Function declarations for libglacier + * config.h - Config loading for Glacier * * This file is part of Glacier. * @@ -18,83 +18,75 @@ #ifndef GLACIERCONFIG_H_ #define GLACIERCONFIG_H_ +#include + +/* Constants */ +#ifndef LG_VERBOSE +#define LG_VERBOSE 0 +#endif + /* * init_config - * DESCRIPTION: Init_config initializes the libconfig library, so it can read the required runtime files - * PARAMETERS: - * None. (void) - * RETURN VAUES: - * 0 on success, EXIT_FAILURE on failure - * CAVEATS: - * None. - * EXAMPLE: - * // It is best practice to check for ALL non-zero return values, rather than specific ones, - * // as init_config() returns EXIT_FAILURE * - * if (init_config() != 0) { - * errlog("Failed to initialize libconfig"); - * return(EXIT_FAILURE); // fatal error requiring termination of program execution - * } - * else { - * successlog("Initialized libconfig"); // output automatically if LG_VERBOSE = 1 - * } + * DESCRIPTION: Initialize libconfig with required configs + * PARAMETERS: + * None. + * RETURN VALUES: + * 0 on success, 1 on failure + * CAVEATS: + * This MUST be called before ANY other config function is. + * EXAMPLE: + * init_config(); */ int init_config(void); -/**************************************************************************************************************/ - /* * die_config - * DESCRPTION: Die_config destroys the loaded libconfig library. - * PARAMETERS: - * None. (void) - * RETURN VALUES: - * EXIT_SUCCESS on success - * CAVEATS: - * None. - * EXAMPLE: - * // die_config() is unlikely to fail unless you tried to destroy an invalid object, - * // so checking for non-zero return values is unnecessary * + * DESCRIPTION: Die_config brings down libconfig gracefully. + * PARAMETERS: + * None. + * RETURN VALUES: + * 0 on success, 1 on failure + * CAVEATS: + * This MUST be called after ALL other config functions have completed. + * EXAMPLE: * die_config(); */ int die_config(void); -/**************************************************************************************************************/ - /* * load_all_from_config - * DESCRIPTION: Initialize all settings from glacier.cfg. + * + * DESCRIPTION: load_all_from_config loads all settings from the config file. * PARAMETERS: - * None. (void) + * None. * RETURN VALUES: - * 0 on success, 1 on file does not exist, 2 on library error + * 0 on success, 1 on failure * CAVEATS: * None. * EXAMPLE: * load_all_from_config(); */ -int load_all_from_config(); - -/**************************************************************************************************************/ +int load_all_from_config(void); /* - * [[[ DEPRECATED ]]] - * load_setting_from_config - * DESCRIPTION: Initialize a specified from glacier.cfg. + * load_all_from_profile + * + * DESCRIPTION: load_all_from_profile loads all settings from the profile file. * PARAMETERS: - * char SETTING[] -> The setting to initialize + * None. * RETURN VALUES: - * 0 on success, 1 on setting not found, 2 on file does not exist, 3 on library error + * 0 on success, 1 on failure * CAVEATS: * None. * EXAMPLE: - * load_setting_from_config(); + * load_all_from_profile(); */ -/* int load_setting_from_config(char SETTING[]); */ +int load_all_from_profile(void); #endif diff --git a/include/data.h b/include/data.h index 44e74f0..6b3f7e4 100644 --- a/include/data.h +++ b/include/data.h @@ -20,6 +20,21 @@ #include +/* Maximum number of children a node can have */ +#define MAX_CHILDREN 64 + +/* Maximum recursion depth for tree operations */ +#define MAX_RECURSION_DEPTH 100 + +/* Node structure definition */ +struct node { + char *data; + struct node *children[MAX_CHILDREN]; + int numChildren; + struct node *left; + struct node *right; +}; + /* * create_node * @@ -27,16 +42,30 @@ * PARAMETERS: * char *data -> The name of the node to create * RETURN VALUES: - * None. + * A pointer to the created node on success, NULL on failure * CAVEATS: - * None. + * Caller must free the node using free_node when done * EXAMPLE: - * struct node package = create_node("Package"); + * struct node *package = create_node("Package"); */ struct node *create_node(char *data); -/**************************************************************************************************************/ +/* + * free_node + * + * DESCRIPTION: Free_node recursively frees all memory allocated for a node and its children. + * PARAMETERS: + * struct node *root -> The root node to free + * RETURN VALUES: + * None. + * CAVEATS: + * Will free all child nodes recursively. + * EXAMPLE: + * free_node(package); + */ + +void free_node(struct node *root); /* * add_child @@ -46,16 +75,14 @@ struct node *create_node(char *data); * struct node *parent -> The parent node which the child will be added to * struct node *child -> The child node which will be added to the parent node * RETURN VALUES: - * 1 on maximum children exceeded + * 0 on success, 1-3 for different error conditions * CAVEATS: * None. * EXAMPLE: * add_child(package, dep1); */ -void add_child(struct node *parent, struct node *child); - -/**************************************************************************************************************/ +int add_child(struct node *parent, struct node *child); /* * print_tree @@ -65,13 +92,13 @@ void add_child(struct node *parent, struct node *child); * struct node *root -> The tree to print * int level -> The number of levels to descend * RETURN VALUES: - * None. + * 0 on success, non-zero on error * CAVEATS: * None. * EXAMPLE: * print_tree(package, 0); */ -void print_tree(struct node *root, int level); +int print_tree(struct node *root, int level); #endif diff --git a/include/globals.h b/include/globals.h index 9c48331..a77b87e 100644 --- a/include/globals.h +++ b/include/globals.h @@ -18,22 +18,34 @@ #ifndef GLOBALS_H_ #define GLOBALS_H_ +#include +#include + +/* Constants */ +#define BUFFER_SIZE 1024 +#define MAX_SIZE 256 + +/* libconfig context */ extern config_t cfg; extern config_setting_t *setting; -extern const char str; -extern char GLACIER_ALLOWED_LICENSES; +/* Configuration variables */ +extern int GLACIER_ALLOW_SERVICES; +extern char *GLACIER_ALLOWED_LICENSES; extern int GLACIER_DO_INT_CHECK; extern int GLACIER_VERBOSE; +/* Profile variables */ extern const char *GLACIER_REPO; extern const char *GLACIER_ARCH; extern const char *GLACIER_TARGET; extern const char *GLACIER_LOCALDB; extern const char *GLACIER_SYSTEM_PROFILE; -const char *runtime_files[]; +/* Required runtime files */ +extern const char *runtime_files[]; +/* File pointers for hashing operations */ extern FILE *expected_hash; extern FILE *pkg; diff --git a/include/pkgops.h b/include/pkgops.h index 6498b5c..65dae07 100644 --- a/include/pkgops.h +++ b/include/pkgops.h @@ -18,6 +18,12 @@ #ifndef GLACIERPKGOPS_H_ #define GLACIERPKGOPS_H_ +/* Permission constant for workspace directory creation */ +#define DEFAULT_PERMISSIONS 0750 + +/* Maximum size for path buffers */ +#define PATH_MAX_SIZE 512 + /* * mkworkspace * @@ -25,14 +31,14 @@ * PARAMETERS: * None. * RETURN VAUES: - * 0 on success, 2 on library error + * 0 on workspace already exists, 1 on workspace created, -1 on error * CAVEATS: * None. * EXAMPLE: * mkworkspace(); */ -int mkworkspace(); +int mkworkspace(void); /* * prepare_pkg @@ -41,7 +47,7 @@ int mkworkspace(); * PARAMETERS: * char PACKAGE[] -> The package file to prepare * RETURN VAUES: - * 0 on success, 1 on package does not exist, or error untarring + * 0 on success, 1 on package does not exist or error, other values for specific errors * CAVEATS: * The example presented is bad. You should be calling the system profile variable * rather than manually specifying one. @@ -58,7 +64,7 @@ int prepare_pkg(char PACKAGE[]); * PARAMETERS: * char TASK[] -> The make task to run * RETURN VAUES: - * 0 on success, 1 on failure + * 0 on success, other values for specific errors * CAVEATS: * MUST be run after prepare_pkg(), or else errors will occur * Same caveat as above. Do not manually specify the system profile, use its variable. diff --git a/include/security.h b/include/security.h index 38bc257..c10f9a6 100644 --- a/include/security.h +++ b/include/security.h @@ -18,6 +18,9 @@ #ifndef GLACIERSECURITY_H_ #define GLACIERSECURITY_H_ +typedef unsigned int uint; +typedef unsigned char uchar; + /* * compare_file_hash * @@ -35,4 +38,61 @@ /* int compare_file_hash(char ORIG_HASH[], char FILE[]); */ +/* + * hash_file + * + * DESCRIPTION: Performs a hashing operation on a file and stores the result + * PARAMETERS: + * const char *filename -> The file to hash + * unsigned char *out_hash -> Buffer to store the resulting hash + * unsigned int *out_length -> Will contain the length of the hash + * RETURN VALUES: + * 0 on success, other values for specific errors + * CAVEATS: + * out_hash buffer must be large enough to hold the hash (EVP_MAX_MD_SIZE recommended) + * EXAMPLE: + * unsigned char hash[EVP_MAX_MD_SIZE]; + * unsigned int hash_len; + * hash_file("file.txt", hash, &hash_len); + */ + +int hash_file(const char *filename, unsigned char *out_hash, unsigned int *out_length); + +/* + * print_hash + * + * DESCRIPTION: Prints a specified hash string to stdout + * PARAMETERS: + * unsigned char *hash -> The hash to print + * unsigned int length -> Length of the hash + * RETURN VALUES: + * 0 on success, 1 on error + * CAVEATS: + * None + * EXAMPLE: + * print_hash(hash, hash_len); + */ + +int print_hash(uchar *hash, uint length); + +/* + * stash_hash + * + * DESCRIPTION: Stores a hash inside a string as hexadecimal representation + * PARAMETERS: + * char *stored_hash -> Buffer to store the resulting hash string + * unsigned int stored_hash_size -> Size of the stored_hash buffer + * const uchar *hash -> The hash to convert to string + * uint length -> Length of the hash + * RETURN VALUES: + * 0 on success, 1 on error + * CAVEATS: + * stored_hash buffer must be at least (length*2)+1 bytes in size + * EXAMPLE: + * char hash_str[65]; // 32 bytes SHA-256 = 64 hex chars + null terminator + * stash_hash(hash_str, sizeof(hash_str), hash, hash_len); + */ + +int stash_hash(char *stored_hash, unsigned int stored_hash_size, const uchar *hash, uint length); + #endif diff --git a/src/libglacier.c b/src/libglacier.c index a57ab4c..8070b8d 100644 --- a/src/libglacier.c +++ b/src/libglacier.c @@ -27,14 +27,24 @@ #include #include #include +#include #include #include #include "config.h" +/* Buffer and size constants */ #define BUFFER_SIZE 1024 #define MAX_CHILDREN 64 #define MAX_SIZE 256 +#define PATH_MAX_SIZE 512 +#define MAX_RECURSION_DEPTH 100 +#define DEFAULT_PERMISSIONS 0750 + +/* Define LG_VERBOSE if not defined elsewhere */ +#ifndef LG_VERBOSE +#define LG_VERBOSE 0 +#endif typedef unsigned int uint; typedef unsigned char uchar; @@ -48,19 +58,20 @@ typedef unsigned char uchar; */ config_t cfg; /* Context for libconfig */ -config_setting_t *setting; /* Pointer for setting */ -const char str; /* Unsure what this does, will possibly remove later */ +config_setting_t *setting = NULL; /* Pointer for setting */ -int GLACIER_ALLOW_SERVICES; /* Declaration of GLACIER_ALLOW_SERVICES as given in glacier.cfg */ -char GLACIER_ALLOWED_LICENSES; /* Declaration of GLACIER_ALLOWED_LICENSES as given in glacier.cfg */ -int GLACIER_DO_INT_CHECK; /* Declaration of GLACIER_DO_INT_CHECK as given in glacier.cfg */ -int GLACIER_VERBOSE; /* Declaration of GLACIER_VERBOSE as given in glacier.cfg */ +/* Configuration variables with default values */ +int GLACIER_ALLOW_SERVICES = 0; /* Declaration of GLACIER_ALLOW_SERVICES as given in glacier.cfg */ +char *GLACIER_ALLOWED_LICENSES = NULL; /* Declaration of GLACIER_ALLOWED_LICENSES as given in glacier.cfg */ +int GLACIER_DO_INT_CHECK = 1; /* Declaration of GLACIER_DO_INT_CHECK as given in glacier.cfg */ +int GLACIER_VERBOSE = 0; /* Declaration of GLACIER_VERBOSE as given in glacier.cfg */ -const char *GLACIER_REPO; /* Declaration of GLACIER_REPO as defined in profile.cfg */ -const char *GLACIER_ARCH; /* Declaration of GLACIER_ARCH as defined in profile.cfg */ -const char *GLACIER_TARGET; /* Declaration of GLACIER_TARGET as defined in profile.cfg */ -const char *GLACIER_LOCALDB; /* Declaration of GLACIER_LOCALDB as defined in profile.cfg */ -const char *GLACIER_SYSTEM_PROFILE; /* Declaration of GLACIER_SYSTEM_PROFILE as defined in profile.cfg */ +/* Profile variables */ +const char *GLACIER_REPO = NULL; /* Declaration of GLACIER_REPO as defined in profile.cfg */ +const char *GLACIER_ARCH = NULL; /* Declaration of GLACIER_ARCH as defined in profile.cfg */ +const char *GLACIER_TARGET = NULL; /* Declaration of GLACIER_TARGET as defined in profile.cfg */ +const char *GLACIER_LOCALDB = NULL; /* Declaration of GLACIER_LOCALDB as defined in profile.cfg */ +const char *GLACIER_SYSTEM_PROFILE = NULL; /* Declaration of GLACIER_SYSTEM_PROFILE as defined in profile.cfg */ /* Required runtime files */ const char *runtime_files[] = { @@ -333,12 +344,65 @@ load_setting_from_config(char SETTING[]) struct node *create_node(char *data) { + if (data == NULL) { + if (LG_VERBOSE == 1) { errlog("NULL data passed to create_node()"); } + return NULL; + } + struct node *newNode = (struct node*)malloc(sizeof(struct node)); - newNode->data = strdup(data); + if (newNode == NULL) { + if (LG_VERBOSE == 1) { errlog("Memory allocation failed in create_node()"); } + return NULL; + } + + newNode->data = strdup(data); + if (newNode->data == NULL) { + if (LG_VERBOSE == 1) { errlog("String duplication failed in create_node()"); } + free(newNode); + return NULL; + } + newNode->numChildren = 0; + newNode->left = NULL; + newNode->right = NULL; + + for (int i = 0; i < MAX_CHILDREN; i++) { + newNode->children[i] = NULL; + } + return newNode; } +/* + * free_node + * + * DESCRIPTION: Recursively free a node and all its children. + * PARAMETERS: struct node *root + * DEFINED IN: data.h + * + */ + +void +free_node(struct node *root) +{ + if (root == NULL) { + return; + } + + /* Free all children recursively */ + for (int i = 0; i < root->numChildren; i++) { + free_node(root->children[i]); + } + + /* Free data string */ + if (root->data != NULL) { + free(root->data); + } + + /* Free node itself */ + free(root); +} + /* * add_child * @@ -348,14 +412,31 @@ struct node * */ -void +int add_child(struct node *parent, struct node *child) { + if (parent == NULL || child == NULL) { + if (LG_VERBOSE == 1) { + errlog("NULL pointer passed to add_child()"); + } + return 1; + } + + if (parent->numChildren < 0 || parent->numChildren >= MAX_CHILDREN) { + if (LG_VERBOSE == 1) { + errlog("Invalid numChildren value in parent node"); + } + return 2; + } + if (parent->numChildren < MAX_CHILDREN) { parent->children[parent->numChildren++] = child; + return 0; } else { - if (LG_VERBOSE == 1) { errlog("Maximum number of children exceeded"); } - exit(1); + if (LG_VERBOSE == 1) { + errlog("Maximum number of children exceeded"); + } + return 3; } } @@ -368,22 +449,53 @@ add_child(struct node *parent, struct node *child) * */ -void +int print_tree(struct node *root, int level) { if (root == NULL) { - return; + return 0; + } + + if (level < 0) { + if (LG_VERBOSE == 1) { + errlog("Invalid level value in print_tree()"); + } + return 1; + } + + if (level > MAX_RECURSION_DEPTH) { + /* Safety check to prevent stack overflow from recursive calls */ + if (LG_VERBOSE == 1) { + errlog("Maximum recursion depth exceeded in print_tree()"); + } + return 2; } for (int i = 0; i < level; i++) { printf(" "); } - printf("%s\n", root->data); + if (root->data == NULL) { + printf("(NULL)\n"); + } else { + printf("%s\n", root->data); + } + + if (root->numChildren < 0 || root->numChildren > MAX_CHILDREN) { + if (LG_VERBOSE == 1) { + errlog("Invalid numChildren value in node"); + } + return 3; + } for (int i = 0; i < root->numChildren; i++) { - print_tree(root->children[i], level + 1); + if (print_tree(root->children[i], level + 1) != 0) { + /* Propagate errors up the call stack */ + return 4; + } } + + return 0; } /* @@ -398,18 +510,31 @@ print_tree(struct node *root, int level) int mkworkspace(void) { - DIR* workspace = opendir("/tmp/glacier-workspace"); + const char *workspace_path = "/tmp/glacier-workspace"; + + DIR* workspace = opendir(workspace_path); if (workspace) { + /* Workspace exists */ + closedir(workspace); /* infolog("Not creating new workspace, valid workspace already exists."); */ return 0; } else if (ENOENT == errno) { /* infolog("Creating new Glacier workspace..."); */ - mkdir("/tmp/glacier-workspace", 0777); + if (mkdir(workspace_path, DEFAULT_PERMISSIONS) != 0) { + if (LG_VERBOSE == 1) { + errlog("in mkworkspace()"); + errlog("mkdir() failed to create workspace directory"); + fprintf(stderr, "Error: %s\n", strerror(errno)); + } + return -1; + } return 1; } else { + /* Some other error occurred */ if (LG_VERBOSE == 1) { errlog("in mkworkspace()"); - errlog("mkdir() failed to run"); + errlog("opendir() failed to check workspace"); + fprintf(stderr, "Error: %s\n", strerror(errno)); } return -1; } @@ -433,46 +558,87 @@ prepare_pkg(char PACKAGE[]) errlog("This can be done by running 'glacier-update-pkgdb' as root."); return 1; } else { - char PKG_NEW[512]; - strcat(PKG_NEW, "/tmp/glacier-workspace/"); - strcat(PKG_NEW, PACKAGE); - strcat(PKG_NEW, ".tar"); + char PKG_NEW[PATH_MAX_SIZE]; + PKG_NEW[0] = '\0'; /* Initialize the string buffer */ + snprintf(PKG_NEW, sizeof(PKG_NEW), "/tmp/glacier-workspace/%s.tar", PACKAGE); + FILE *pkg_old, *pkg_new; - char filename[100], contents; + char buffer[BUFFER_SIZE]; + size_t bytes_read; pkg_old = fopen(PACKAGE, "r"); - pkg_new = fopen(PKG_NEW, "a+"); + if (pkg_old == NULL) { + errlog("Failed to open source package file"); + return 1; + } + + pkg_new = fopen(PKG_NEW, "w"); + if (pkg_new == NULL) { + errlog("Failed to create destination package file"); + fclose(pkg_old); + return 1; + } - contents = fgetc(pkg_old); - - while (contents != EOF) { - fputc(contents, pkg_new); - contents = fgetc(pkg_old); + /* Use buffered I/O for better efficiency */ + while ((bytes_read = fread(buffer, 1, sizeof(buffer), pkg_old)) > 0) { + if (fwrite(buffer, 1, bytes_read, pkg_new) != bytes_read) { + errlog("Failed to write to destination file"); + fclose(pkg_old); + fclose(pkg_new); + return 1; + } } fclose(pkg_old); fclose(pkg_new); - char *tar_args[] = { - "/bin/tar", /* This should be changed to /glacier/bin/tar later on */ - "-xvf", - PKG_NEW, - NULL, - }; + + /* Fork before exec to prevent process replacement */ + pid_t pid = fork(); + if (pid < 0) { + errlog("Failed to fork process"); + return 1; + } else if (pid == 0) { + /* Child process */ + char *tar_args[] = { + "/bin/tar", /* This should be changed to /glacier/bin/tar later on */ + "-xvf", + PKG_NEW, + NULL, + }; - execvp( - "/bin/tar", /* Above comment applies here */ - tar_args - ); - if (errno != 0) { - printf(COL_RED "[x] " COL_RESET "Error while unpacking archive for package %s.\n", PACKAGE); - return errno; + execvp("/bin/tar", tar_args); + /* If we get here, execvp failed */ + fprintf(stderr, COL_RED "[x] " COL_RESET "Failed to execute tar: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } else { + /* Parent process */ + int status; + waitpid(pid, &status, 0); + + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + printf(COL_RED "[x] " COL_RESET "Error while unpacking archive for package %s.\n", PACKAGE); + return WEXITSTATUS(status); + } + + if (remove(PKG_NEW) != 0) { + warnlog("Failed to remove temporary package file"); + } + + char pkg_dir[PATH_MAX_SIZE]; + pkg_dir[0] = '\0'; /* Initialize the string buffer */ + snprintf(pkg_dir, sizeof(pkg_dir), "/tmp/glacier-workspace/%s", PACKAGE); + + if (chdir(pkg_dir) != 0) { + errlog("Failed to change directory"); + return 1; + } + + return 0; } - remove(PKG_NEW); - char pkg_dir[512]; - strcat(pkg_dir, "/tmp/glacier-workspace/"); - strcat(pkg_dir, PACKAGE); - chdir(pkg_dir); } + + /* Should never reach here, but added for completeness */ + return 1; } /* @@ -487,20 +653,44 @@ prepare_pkg(char PACKAGE[]) int run_make_task(char TASK[]) { - char *build_args[] = { - "/bin/make", /* This should be changed to /glacier/bin/make later on */ - TASK, - NULL - }; - - execvp( - "/bin/make", /* Above comment applies here */ - build_args - ); - if (errno != 0) { - errlog("An error occurred while running the make task."); - return errno; + if (TASK == NULL) { + errlog("No task specified for make"); + return 1; } + + /* Fork before exec to prevent process replacement */ + pid_t pid = fork(); + if (pid < 0) { + errlog("Failed to fork process"); + return 1; + } else if (pid == 0) { + /* Child process */ + char *build_args[] = { + "/bin/make", /* This should be changed to /glacier/bin/make later on */ + TASK, + NULL + }; + + execvp("/bin/make", build_args); + /* If we get here, execvp failed */ + errlog("Failed to execute make"); + fprintf(stderr, "Error: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } else { + /* Parent process */ + int status; + waitpid(pid, &status, 0); + + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } else { + errlog("Make process terminated abnormally"); + return 1; + } + } + + /* Should never reach here, but added for completeness */ + return 1; } /* @@ -515,6 +705,15 @@ run_make_task(char TASK[]) int print_hash(uchar *hash, uint length) { + if (hash == NULL) { + errlog("NULL hash pointer passed to print_hash()"); + return 1; + } + + if (length == 0) { + warnlog("Zero-length hash passed to print_hash()"); + } + for (uint index = 0; index < length; index++) { printf("%02x", hash[index]); } @@ -533,43 +732,49 @@ print_hash(uchar *hash, uint length) * */ -/*int -stash_hash(uchar *stored_hash, uchar *hash, uint length) +int +stash_hash(char *stored_hash, unsigned int stored_hash_size, const uchar *hash, uint length) { - if (sizeof(stored_hash) != 32 && LG_VERBOSE == 1) { - errlog("in stash_hash()"); - errlog("size of stored hash string must equal 32 bytes"); - return 1; - } - else if (sizeof(stored_hash) != 32) { - return 1; - } - - uint max_size_of_input = 3; - - for (uint index = 0; index < length; index++) { - if (hash[index] > max_size_of_input) { + if (stored_hash == NULL || hash == NULL) { + if (LG_VERBOSE == 1) { errlog("in stash_hash()"); - errlog("size of input exceeds maximum allowed size (hash[index] > 3)"); - return 1; + errlog("NULL pointer provided"); } - snprintf(stored_hash[index], max_size_of_input, "%02x", hash[index]); + return 1; + } + + if (stored_hash_size < (length * 2 + 1)) { + if (LG_VERBOSE == 1) { + errlog("in stash_hash()"); + errlog("Output buffer too small for hash"); + } + return 1; + } + + /* Clear the buffer first */ + stored_hash[0] = '\0'; + + /* Format each byte as a 2-character hex value */ + for (uint index = 0; index < length; index++) { + char hex[3]; + snprintf(hex, sizeof(hex), "%02x", hash[index]); + strncat(stored_hash, hex, stored_hash_size - strlen(stored_hash) - 1); } return 0; -} */ +} /* * hash_file * * DESCRIPTION: Performs a hashing operation on a file and stores the result - * PARAMETERS: unsigned char *h + * PARAMETERS: const char *filename * DEFINED IN: security.h * */ -unsigned char -hash_file(const char *filename) +int +hash_file(const char *filename, unsigned char *out_hash, unsigned int *out_length) { FILE *data = fopen(filename, "rb"); if (! data && LG_VERBOSE == 1) { @@ -585,9 +790,11 @@ hash_file(const char *filename) if (! context && LG_VERBOSE == 1) { errlog("in hash_file()"); errlog("error creating envelope context"); + fclose(data); return 2; } else if (! context) { + fclose(data); return 2; } @@ -601,6 +808,22 @@ hash_file(const char *filename) return 3; } + // Read and hash file content + unsigned char buffer[BUFFER_SIZE]; + size_t bytes_read; + + while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, data)) > 0) { + if (EVP_DigestUpdate(context, buffer, bytes_read) != 1) { + if (LG_VERBOSE == 1) { + errlog("in hash_file()"); + errlog("error updating digest"); + } + EVP_MD_CTX_free(context); + fclose(data); + return 4; + } + } + if (ferror(data)) { if (LG_VERBOSE == 1) { errlog("in hash_file()"); @@ -608,24 +831,21 @@ hash_file(const char *filename) } EVP_MD_CTX_free(context); fclose(data); - return 4; + return 5; } fclose(data); - uchar hash[EVP_MAX_MD_SIZE]; - uint hash_length; - - if (EVP_DigestFinal_ex(context, hash, &hash_length) != 1) { + if (EVP_DigestFinal_ex(context, out_hash, out_length) != 1) { if (LG_VERBOSE == 1) { errlog("in hash_file()"); errlog("error finalizing digest"); } EVP_MD_CTX_free(context); - return 5; + return 6; } EVP_MD_CTX_free(context); - return *hash; + return 0; }