diff --git a/service/src/cli.c b/service/src/cli.c index cb3fc28..a08b36e 100644 --- a/service/src/cli.c +++ b/service/src/cli.c @@ -1,3 +1,4 @@ +#define _XOPEN_SOURCE 700 // Ensure both strptime and realpath are declared #include "cli.h" #include "session.h" #include "server.h" @@ -12,12 +13,25 @@ #include #include #include +#include // Define the macro for formatting messages into the session buffer -#define WRITE_TO_BUFFER(session, format, ...) \ - snprintf(session->buffer + strlen(session->buffer), \ - sizeof(session->buffer) - strlen(session->buffer), \ - format, ##__VA_ARGS__) +#define WRITE_TO_BUFFER(session, format, ...) \ + safe_snprintf(session->buffer + strlen(session->buffer), \ + sizeof(session->buffer) - strlen(session->buffer), \ + format, ##__VA_ARGS__) + +// Uses temp buffer to allow for same buffer to be used for input and output +void safe_snprintf(char *dest, size_t dest_size, const char *format, ...) +{ + char temp[dest_size]; + va_list args; + va_start(args, format); + vsnprintf(temp, sizeof(temp), format, args); + va_end(args); + strncpy(dest, temp, dest_size - 1); + dest[dest_size - 1] = '\0'; // Ensure null termination +} void generate_custom_id(char *password, size_t length) { @@ -94,14 +108,14 @@ int interact_cli(session_t *session) // If at root, can't bury treasure if (strcmp(session->full_dir, session->root_dir) == 0) { - WRITE_TO_BUFFER(session, "Can't bury treasure at sea\n"); + WRITE_TO_BUFFER(session, "Can't bury at sea\n"); return 0; } // If file path contains "/", it is not valid if (strchr(file_path, '/') != NULL) { - WRITE_TO_BUFFER(session, "Can only bury treasure where your ship is\n"); + WRITE_TO_BUFFER(session, "Can only bury where your ship is\n"); return 0; } @@ -124,8 +138,8 @@ int interact_cli(session_t *session) // Using provided date and time if (strptime(time_input, "%Y-%m-%d %H:%M", &tm) != NULL) { - sprintf(date, "%04d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); - sprintf(time_str, "%02d:%02d", tm.tm_hour, tm.tm_min); + snprintf(date, sizeof(date), "%04d-%02d-%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + snprintf(time_str, sizeof(time_str), "%02d:%02d", tm.tm_hour, tm.tm_min); } else { @@ -176,9 +190,9 @@ int interact_cli(session_t *session) message_input[read_size - 1] = '\0'; fflush(stdout); - sprintf(content, "Scam Details:\n----------------\nDate: %s\nTime: %s UTC\nScammer: %s %s\nScammer ID: %s\n\nMessage: %s", - date, - time_str, session->pirate_adjective, session->pirate_noun, custom_ID, message_input); + snprintf(content, sizeof(content), "Scam Details:\n----------------\nDate: %s\nTime: %s UTC\nScammer: %s %s\nScammer ID: %s\n\nMessage: %s", + date, + time_str, session->pirate_adjective, session->pirate_noun, custom_ID, message_input); // Create a new file at the current destination char scam_filename[1024]; @@ -209,35 +223,33 @@ int interact_cli(session_t *session) } time_str[j] = '\0'; // Null-terminate the adjusted time string - sprintf(scam_filename, "%s_%s_scam_%s_%s", lower_case_pirate_adjective, lower_case_pirate_noun, date, time_str); + snprintf(scam_filename, sizeof(scam_filename), "%s_%s_scam_%s_%s", lower_case_pirate_adjective, lower_case_pirate_noun, date, time_str); } - printf("Filepath: %s\n", file_path); - // Save as a treasure file with a password if (strncmp(parrot_input, "", 255) != 0) { - sprintf(scam_filename, "%s.treasure", scam_filename); + safe_snprintf(scam_filename, sizeof(scam_filename), "%s.treasure", scam_filename); // add the password to the end of the content - sprintf(content, "%s\n\nProtected with password:\n%s", content, parrot_input); + safe_snprintf(content, sizeof(content), "%s\n\nProtected with password:\n%s", content, parrot_input); } // save as a private file with identity else if (save_with_identity) { - sprintf(scam_filename, "%s.private", scam_filename); + safe_snprintf(scam_filename, sizeof(scam_filename), "%s.private", scam_filename); // hash the identity and add it to the end of the content unsigned char hash[SHA256_DIGEST_LENGTH]; compute_sha256(session->pirate_identity, hash); - sprintf(content, "%s\n\nProtected with identity hash:\n%s", content, hash); + safe_snprintf(content, sizeof(content), "%s\n\nProtected with identity hash:\n%s", content, hash); } else // or normal log file { - sprintf(scam_filename, "%s.log", scam_filename); + safe_snprintf(scam_filename, sizeof(scam_filename), "%s.log", scam_filename); } // Get absolute path char absolute_path[PATH_MAX]; - snprintf(absolute_path, sizeof(absolute_path), "%s/%s", session->full_dir, scam_filename); + safe_snprintf(absolute_path, sizeof(absolute_path), "%s/%s", session->full_dir, scam_filename); // If file already exists, creation fails if (access(absolute_path, F_OK) != -1) { @@ -296,7 +308,7 @@ int interact_cli(session_t *session) strcpy(directory_path, "."); } char absolute_path[PATH_MAX]; - snprintf(absolute_path, sizeof(absolute_path), "%s/%s", session->full_dir, directory_path); + safe_snprintf(absolute_path, sizeof(absolute_path), "%s/%s", session->full_dir, directory_path); char resolved_path[PATH_MAX]; if (realpath(absolute_path, resolved_path) == NULL) { @@ -359,7 +371,7 @@ int interact_cli(session_t *session) int change_directory(char *path, session_t *session) { char absolute_path[PATH_MAX]; - snprintf(absolute_path, sizeof(absolute_path), "%s/%s", session->full_dir, path); + safe_snprintf(absolute_path, sizeof(absolute_path), "%s/%s", session->full_dir, path); char resolved_path[PATH_MAX]; // 1. Resolve the path to an absolute path @@ -394,13 +406,13 @@ int change_directory(char *path, session_t *session) strcpy(session->full_dir, resolved_path); // Calculate the local directory relative to the root directory - snprintf(session->local_dir, sizeof(session->local_dir), "%s", resolved_path + strlen(session->root_dir)); + safe_snprintf(session->local_dir, sizeof(session->local_dir), "%s", resolved_path + strlen(session->root_dir)); // Ensure local_dir starts with a '/' if (session->local_dir[0] != '/') { char temp_dir[PATH_MAX]; - snprintf(temp_dir, sizeof(temp_dir), "/%s", session->local_dir); + safe_snprintf(temp_dir, sizeof(temp_dir), "/%s", session->local_dir); strcpy(session->local_dir, temp_dir); } @@ -414,16 +426,24 @@ void cat_file(char *filename, session_t *session) // check that it is not a directory struct stat path_stat; char file_path[PATH_MAX]; - sprintf(file_path, "%s/%s", session->full_dir, filename); + safe_snprintf(file_path, sizeof(file_path), "%s/%s", session->full_dir, filename); if (stat(file_path, &path_stat) != 0) { - WRITE_TO_BUFFER(session, "No treasure '%s' to loot\n", filename); + WRITE_TO_BUFFER(session, "No '%s' found\n", filename); return; } if (S_ISDIR(path_stat.st_mode)) { - WRITE_TO_BUFFER(session, "'%s' is a destination, not a treasure\n", filename); + WRITE_TO_BUFFER(session, "'%s' is a destination and can't be looted\n", filename); + return; + } + + // Open the file to prepare for reading + FILE *file = fopen(file_path, "r"); + if (file == NULL) + { + WRITE_TO_BUFFER(session, "Unknown error while accessing '%s'\n", filename); return; } @@ -432,18 +452,11 @@ void cat_file(char *filename, session_t *session) { char correct_password[256]; // read the last line of the file, this is the password - FILE *file = fopen(file_path, "r"); - if (file == NULL) - { - WRITE_TO_BUFFER(session, "No treasure '%s' to loot\n", filename); - return; - } char line[256]; while (fgets(line, sizeof(line), file)) { strcpy(correct_password, line); } - fclose(file); WRITE_TO_BUFFER(session, "A parrot is guarding the treasure tightly\n"); WRITE_TO_BUFFER(session, "Speak your words: "); send(session->sock, session->buffer, strlen(session->buffer), 0); @@ -459,8 +472,9 @@ void cat_file(char *filename, session_t *session) { // Parrot feedback char feedback[1024]; - snprintf(feedback, sizeof(feedback), "%s is wrong, %s is wrong, captain!\n", password_input, password_input); + safe_snprintf(feedback, sizeof(feedback), "%s is wrong, %s is wrong, captain!\n", password_input, password_input); WRITE_TO_BUFFER(session, feedback); + fclose(file); return; } } @@ -470,37 +484,23 @@ void cat_file(char *filename, session_t *session) { char correct_hash[65]; // read the last line of the file, this is the identity - FILE *file = fopen(file_path, "r"); - if (file == NULL) - { - WRITE_TO_BUFFER(session, "No treasure '%s' to loot\n", filename); - return; - } char line[256]; while (fgets(line, sizeof(line), file)) { strcpy(correct_hash, line); } - fclose(file); - // Check if the hash of the pirate identity matches the stored identity unsigned char hash[65]; compute_sha256(session->pirate_identity, hash); if (strncmp(correct_hash, (char *)hash, 64) != 0) { WRITE_TO_BUFFER(session, "Arr! This ain't yours matey!\n"); + fclose(file); return; } } - // Open and print the contents of the file - FILE *file = fopen(file_path, "r"); - // print current directory - if (file == NULL) - { - printf("tried opening undefined file: %s\n", filename); - return; - } + // Checks have passed, read the file and send it to the client char line[256]; while (fgets(line, sizeof(line), file)) { @@ -536,7 +536,7 @@ void identity(session_t *session) // Check if the new identity is valid if (strlen(new_identity) != 64) { - WRITE_TO_BUFFER(session, "Length must be 64 (not %d)\n", strlen(new_identity)); + WRITE_TO_BUFFER(session, "Length must be 64 (not %zu)\n", strlen(new_identity)); return; } diff --git a/service/src/cli.h b/service/src/cli.h index 0e3a033..3647c87 100644 --- a/service/src/cli.h +++ b/service/src/cli.h @@ -2,10 +2,13 @@ #ifndef CLI_H #define CLI_H #include "session.h" +#include int interact_cli(session_t *session); int change_directory(char *path, session_t *session); void cat_file(char *filename, session_t *session); void help(); +void identity(session_t *session); +void safe_snprintf(char *dest, size_t dest_size, const char *format, ...); #endif // CLI_H diff --git a/service/src/server.c b/service/src/server.c index 0c91ba6..a031a48 100644 --- a/service/src/server.c +++ b/service/src/server.c @@ -10,6 +10,7 @@ #include #include #include +#include #define PORT 4444 @@ -116,8 +117,6 @@ void trim_whitespace(char *str, size_t buffer_size) // Copy the modified string back to the original buffer, respecting buffer_size strncpy(str, new_str, buffer_size - 1); str[buffer_size - 1] = '\0'; // Ensure null-termination - - printf("Trimmed string: %s\n", str); } void print_terminal_prompt(session_t *session) @@ -126,17 +125,17 @@ void print_terminal_prompt(session_t *session) char dir_message[PATH_MAX]; memset(dir_print, 0, sizeof(dir_print)); strcat(dir_print, session->local_dir); - sprintf(dir_message, "\n%s%s:%s$ ", session->pirate_adjective, session->pirate_noun, dir_print); + safe_snprintf(dir_message, sizeof(dir_message), "\n%s%s:%s$ ", session->pirate_adjective, session->pirate_noun, dir_print); send(session->sock, dir_message, strlen(dir_message), 0); } -void client_session(int *socket_desc, char *pirate_identity) +void client_session(int *socket_desc) { int sock = *socket_desc; session_t session; session.sock = sock; - strcpy(session.pirate_identity, pirate_identity); + generate_random_identity(session.pirate_identity); // Generate a random identity strcpy(session.local_dir, "/"); // Local directory strcpy(session.root_dir, root_dir); // Root directory strcpy(session.full_dir, root_dir); // Full path // Generate a random identity @@ -188,9 +187,6 @@ void client_session(int *socket_desc, char *pirate_identity) int main() { - // Seed the random number generator at startup - srand(time(NULL)); - // get dir and change it to 'data' subfolder if (chdir("../data") != 0) { @@ -257,10 +253,6 @@ int main() { printf("Connection accepted from %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port)); - // generate random identity for new client - char pirate_identity[65]; - generate_random_identity(pirate_identity); - pid_t pid = fork(); if (pid < 0) { @@ -271,8 +263,9 @@ int main() else if (pid == 0) { // This is the child process - close(server_fd); // Child does not need the listening socket - client_session(&client_sock, pirate_identity); // Handle client connection + srand(time(NULL)); // Seed the random number for unique pirate identity + close(server_fd); // Child does not need the listening socket + client_session(&client_sock); // Handle client connection close(client_sock); exit(EXIT_SUCCESS); // End child process } diff --git a/service/src/session.c b/service/src/session.c index 33bfaba..06784b0 100644 --- a/service/src/session.c +++ b/service/src/session.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #define NUM_ADJECTIVES 10 @@ -17,19 +18,39 @@ char *pirate_nouns[NUM_NOUNS] = { // Function to compute SHA-256 hash of a string void compute_sha256(const char *str, char *outputBuffer) { - unsigned char hash[SHA256_DIGEST_LENGTH]; - SHA256_CTX sha256; - SHA256_Init(&sha256); - SHA256_Update(&sha256, str, strlen(str)); - SHA256_Final(hash, &sha256); + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int lengthOfHash = 0; + EVP_MD_CTX *mdctx; + + // Create and initialize the context + if ((mdctx = EVP_MD_CTX_new()) == NULL) + { + // Handle error: Set a generic error message and return + strcpy(outputBuffer, "error"); + return; + } + + // Initialize the Digest operation + if (1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) || + 1 != EVP_DigestUpdate(mdctx, str, strlen(str)) || + 1 != EVP_DigestFinal_ex(mdctx, hash, &lengthOfHash)) + { + // Handle error: Set a generic error message and return + strcpy(outputBuffer, "error"); + EVP_MD_CTX_free(mdctx); + return; + } + + // Clean up + EVP_MD_CTX_free(mdctx); // Convert the binary hash to a hexadecimal string - for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) + for (unsigned int i = 0; i < lengthOfHash; i++) { sprintf(outputBuffer + (i * 2), "%02x", hash[i]); } - outputBuffer[SHA256_DIGEST_LENGTH * 2] = '\0'; // Null-terminate the string + outputBuffer[lengthOfHash * 2] = '\0'; // Null-terminate the string } // Function to generate a random identity string